文章目录
张量
Tensor是pytorch中最基本的数据类型, 相当于numpy中的ndarray, 是具有追踪运算, 自动求导功能的多维数组.
1. 创建
x = torch.rand(5, 3) # 随机二维数组
x = torch.empty(5, 3) # 创建二维数组但不进行初始化
x = torch.zeros(5, 3) # 创建全零的二维数组
x = torch.tensor([1, 2, 3]) # 从list创建tensor
x = torch.from_numpy(np.array([1, 2, 3])) # 使用ndarray创建张量
# 从已有张量创建新张量, 新张量复用原有张量的未指定参数
x = x.new_ones(5, 3, dtype=torch.long)
x = torch.randn_like(x, dtype = torch.double) # reuse the size and override the type
2. 计算
张量加法:
x + y
torch.add(x, y)
y.add_(x) # 所有带_后缀的方法都是在原地修改张量
切片: 语法和numpy一致
修改shape: 使用x.view(dim1, dim2, ..) 使用-1表示按照其他维度自适应
矩阵乘: 使用torch.mm(tensor1, tensor2)计算矩阵乘
矩阵转置: 二维矩阵的转置torch.Tensor.t()
高维矩阵的转置: torch.Tensor.transpose(dim1, dim2, dim3, ...), dim是原矩阵的维度index
元素点乘: 使用troch.mul(tensor1, tensor2)计算元素范围的乘法
numpy桥梁: 对张量使用x.numpy()获得张量对应的np.ndarray, 二者共享底层的数据, 即修改该array能够直接修改tensor的数据
获取值: 对于只含有一个元素的tensor, 可以使用x.item()获得数值, 对于含有多个元素的tensor, 使用x. tolist().
3. 使用gpu计算
在使用GPU计算前, 需要将训练集, 标签, 模型都转入到GPU中, 对于tensor, 使用x.to(device)转移到对应的设备中.
4. 其他常用方法
torch.Tensor.expand(*size):
expand函数对tensor按照size进行扩展, 这种扩展不会增加内存, 也就是说, 他们是指向的同一对象, 修改其中任意一个会修改全部. size与原tensor.size()的关系, size能够在原tensor的size为1的维扩展, 其他维保持不变, 也可以使用-1代替其他维的shape, 但是不能够不一致. 可以直接扩展为更高的维, 更高的维一定在现有维之前.
*size是解序列, 这里例如传入一个list, *list将list解析为它的所有元素
In [42]: a = torch.tensor([1, 2, 3, 4])
In [43]: a.unsqueeze(1).expand(4, 2) # (4,1)->(4,2)
Out[43]:
tensor([[1, 1],
[2, 2],
[3, 3],
[4, 4]])
In [45]: a.unsqueeze(1).expand(-1, 2) #(4,1) - (-1, 2) -> (4, 2)
Out[45]:
tensor([[1, 1],
[2, 2],
[3, 3],
[4, 4]])
a.unsqueeze(1).expand(5, -1, 2) #(4,1) -> (5, 4, 2)
a.unsqueeze(1).expand(-1, 2, 5) # 报错, 高维在前
a.unsqueeze(1).expand(3, 2) # 报错, size不为1的dim的size, 4和3不能够对应
torch.Tensor.repeat(*size)
和expand的功能类似, 但是是对原tensor的复制, 而不是引用. 参数是每个维度的重复次数.
In [5]: a = torch.tensor([1, 2, 3, 4])
In [6]: a.repeat(2, 2)
Out[6]:
tensor([[1, 2, 3, 4, 1, 2, 3, 4],
[1, 2, 3, 4, 1, 2, 3, 4]])
torch.Tensor.gather(dim, index):
对tensor在dim维进行索引, 例如对于4*3的tensor, 对dim1进行索引, 则index的所有元素都应该在0-2. 此外, 被索引的tensor和index的维度, 在除了dim维都需要保持一致, 上例中, index的shape应当是(4, *).
In [25]: a=torch.tensor([[.1,.2,.3],
...: [1.1,1.2,1.3],
...: [2.1,2.2,2.3],
...: [3.1,3.2,3.3]])
In [40]: a.gather(1, torch.tensor([[0, 1, 2, 1]]).t())
Out[40]:
tensor([[0.1000],
[1.2000],
[2.3000],
[3.2000]])
In [41]: a.gather(1, torch.tensor([[0, 1, 2, 1],[2, 1, 0, 1]]).t())
Out[41]:
tensor([[0.1000, 0.3000],
[1.2000, 1.2000],
[2.3000, 2.1000],
[3.2000, 3.2000]])
a.gather(1, torch.tensor([[0, 1, 2, 1]])) # 报错tensor(4, 3), index(1, 4), dim1, 对不上
a.gather(1, torch.tensor([0, 1, 2, 1])) # 报错tensor(4, 3), index(4,) dim1, 对不上
a.gather(1, torch.tensor([[0, 1, 2, 3]]).t()) # 报错, dim1范围0-2, 出现3
自动求导
autograd包是pytorch的核心包, 它提供了对于在tensor上的所有操作自动计算微分的功能, 使用者只需要关心网络的正向传播结构, 而不用处理反向传播. 对于tensor来说, 当设置其requires_grad参数为True之后, tensor开始追踪在其上的所有操作, 当调用.backward()方法时, 会计算从开始记录到当前tensor的操作的导数, 并留在沿计算流程的每个tensor的.grad属性中, 当不希望tensor记录历史时, 使用.detach()方法停止它记录计算历史, 来避免未来的计算被记录. 除了使用该方法, 也可以对代码块添加with torch.no_grad():. 当对模型进行评估时, 这个功能十分有用, 能够屏蔽requires_grad =True的参数发生改变.
要获得任意层的权重的导数 ∂ L o s s ∂ W i \frac{\partial Loss}{\partial W_i} ∂Wi∂Loss, 包括两步,
loss.backward() # loss -- tensor
print(w_i.grad) # loss对w_i的偏导值
grad属性最初为None, 当使用backward方法时, 该属性会成为一个tensor, 多次使用backward, 同一tensor的grad会累加, 因此需要对其重新置0, 或直接调用nn.Module.zero_grad()方法.
默认情况下, backward被调用后会释放已经完成反向传播的这部分计算图的内存, 当一次迭代的后面部分还需要用到前面的计算图时, 例如A3C的情况, 就可以对backward方法加上retain_graph参数.
神经网络
一个典型的神经网络训练过程如下:
- 定义具有可学习参数(权重)的神经网络
- 在输入的数据集上多次迭代
- 计算输入数据的正向传播结果
- 计算loss
- 将loss的梯度反向传播到网络的参数中
- 更新网络的参数, 最基本的方式是使用
weight = weight - learning_rate * gradient
定义网络
- 所有网络类都是
nn.Module的子类 - 所有网络类需要实现
__init__,forward(self, input)方法, 当调用该类的实例时, 其实是调用的forward方法.构造函数必须先调用父类的构造函数,super(Linear, self).__init__()或nn.Module.__init__(), 前者更加普遍. - 网络的结构主要在
forward方法中实现
容器
nn.Module是最基本的容器类, 其他网络模块都是其子类, module可以相互嵌套. 自己写的model也应该是其子类.
nn.Sequential是比较简单的容器, 能够构建线性网络的结构.
# Example of using Sequential
model = nn.Sequential(
nn.Conv2d(1,20,5),
nn.ReLU(),
nn.Conv2d(20,64,5),
nn.ReLU()
)
层
pytorch提供了大量的层供使用, 具体参考documentation
nn.Module和nn.functional的区别
大部分nn中的层class都有nn.function对应,其区别是
- nn.Module实现的layer是由class Layer(nn.Module)定义的特殊类,会自动提取可学习参数nn.Parameter
- nn.functional中的函数更像是纯函数,由def function(input)定义。
由于两者性能差异不大,所以具体使用取决于个人喜好。对于激活函数和池化层,由于没有可学习参数,一般使用nn.functional完成,其他的有学习参数的部分则使用类。但是Droupout由于在训练和测试时操作不同,所以建议使用nn.Module实现,它能够通过model.eval加以区分。
nn.functional不需要放入__init__进行构造,所以不具有可学习参数的部分可以使用nn.functional进行代替。
两者主要的区别就是对于可学习参数nn.Parameter的识别能力,所以可以使用nn.functional提供的函数构造nn.Module, 构造时, 将参数tensor注册成Parameter类即可.
class Linear(nn.Module):
def __init__(self, in_features, out_features):
# nn.Module.__init__(self)
super(Linear, self).__init__()
self.w = nn.Parameter(t.randn(out_features, in_features)) # nn.Parameter是Tensor的子类
self.b = nn.Parameter(t.randn(out_features))
def forward(self, x):
# wx+b
return F.linear(x, self.w, self.b)
layer = Linear(4, 3)
input = V(t.randn(2, 4))
output = layer(input)
print(output)
定义网络和训练
- 按照
class Net(nn.Module)的方式定义网络的__init__和forward, forward返回的应当是网络的输出, 即预测. - 定义loss函数, 可以通过
nn.MSELoss()等类定义loss函数, loss函数实例的输入是网络输出和标签, 输出是loss值. - 对
net的梯度清零后, 从loss进行反向传播 - 实例化优化器类, 对网络权重进行更新
import torch.optim as optim
import torch.nn as nn
net = Net(params)
critierion = nn.MSELoss()
optimizer = optim.SGD(net.parameters(), lr)
optimizer.zero_grad()
output = net(input)
loss = criterion(output, target)
loss.backward()
optimizer.step()
以下是一个预测图片分类的完整例子
import torch
import torchvision
import torchvision.transforms as transforms
# The output of torchvision datasets are PILImage images of range [0, 1]. We transform them to Tensors of normalized range [-1, 1].
transform = transforms.Compose(
[transforms.ToTensor(),
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])
trainset = torchvision.datasets.CIFAR10(root='./data', train=True,
download=True, transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=4,
shuffle=True, num_workers=2)
testset = torchvision.datasets.CIFAR10(root='./data', train=False,
download=True, transform=transform)
testloader = torch.utils.data.DataLoader(testset, batch_size=4,
shuffle=False, num_workers=2)
classes = ('plane', 'car', 'bird', 'cat',
'deer', 'dog', 'frog', 'horse', 'ship', 'truck')
# build network
import torch.nn as nn
import torch.nn.functional as F
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.conv1 = nn.Conv2d(3, 6, 5)
self.pool = nn.MaxPool2d(2, 2)
self.conv2 = nn.Conv2d(6, 16, 5)
self.fc1 = nn.Linear(16 * 5 * 5, 120)
self.fc2 = nn.Linear(120, 84)
self.fc3 = nn.Linear(84, 10)
def forward(self, x):
x = self.pool(F.relu(self.conv1(x)))
x = self.pool(F.relu(self.conv2(x)))
x = x.view(-1, 16 * 5 * 5)
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
x = self.fc3(x)
return x
net = Net()
# define loss
import torch.optim as optim
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)
# train the network
for epoch in range(2): # loop over the dataset multiple times
running_loss = 0.0
for i, data in enumerate(trainloader, 0):
# get the inputs; data is a list of [inputs, labels]
inputs, labels = data
# zero the parameter gradients
optimizer.zero_grad()
# forward + backward + optimize
outputs = net(inputs)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
# print statistics
running_loss += loss.item()
if i % 2000 == 1999: # print every 2000 mini-batches
print('[%d, %5d] loss: %.3f' %
(epoch + 1, i + 1, running_loss / 2000))
running_loss = 0.0
print('Finished Training')
# Test the network
dataiter = iter(testloader)
images, labels = dataiter.next()
outputs = net(images)
_, predicted = torch.max(outputs, 1)
print('Predicted: ', ' '.join('%5s' % classes[predicted[j]]
for j in range(4)))
correct = 0
total = 0
with torch.no_grad():
for data in testloader:
images, labels = data
outputs = net(images)
_, predicted = torch.max(outputs.data, 1)
total += labels.size(0)
correct += (predicted == labels).sum().item()
print('Accuracy of the network on the 10000 test images: %d %%' % (
100 * correct / total))
数据导入与预处理
pytorch提供了Dataset类用于描述一个抽象的数据集, 使用时自定义类重写__getitems__()方法和__len__()方法实现一个映射型的数据集, 可以通过datasetclass[idx]获得第idx个数据. 也可以重写__iter__()方法实现一个iterableDataset, 这主要适合随机访问开销很大的情形和batch size与数据集大小有关的情形.
在Dataset的基础上, pytorch提供了DataLoader类对dataset进行进一步的处理, 这个类实现了
- 对数据进行分批batching
- 对数据进行打乱shuffle
- 对数据进行并行的读取, num_workers
几个功能, 建议数据导入阶段都将数据包装成Dataset并交给DataLoader来管理.
优化
参考
本文介绍了PyTorch中的张量操作,包括创建、计算、GPU计算,以及张量的expand、repeat和gather方法。此外,还讲解了自动求导的概念,如何在神经网络中定义、训练模型,以及数据导入和预处理的基本步骤。

258

被折叠的 条评论
为什么被折叠?



