目录)
PyTorch 入门笔记总结
1、Tensor张量
创建张量
Torch构造张量有如下几种形式:
import torch
#直接构造类似于numpy中ndarray的随机张量
x = troch.rand(5,3)
print(x)
#利用已有数据构造张量
x = torch.tensor([[[5,3,1],[6,4,2]])
print(x)
#根据已经存在的tensor创建
x = x.new_ones(5, 3, dtype=torch.double)
print(x)
x = torch.randn_like(x, dtype=torch.float32)
print(x)
#输出
tensor([[0.4041, 0.4289, 0.2413],
[0.8269, 0.2149, 0.6988],
[0.3254, 0.6165, 0.4514],
[0.9963, 0.1263, 0.7608],
[0.6543, 0.1745, 0.6938]])
tensor([[5, 3, 1],
[6, 4, 2]])
tensor([[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.]], dtype=torch.float64)
tensor([[-0.4634, 0.2839, -0.3666],
[-0.5293, 0.2354, 0.6911],
[-1.6419, 0.5335, 0.7720],
[ 0.5266, -0.9511, 0.0539],
[-2.4723, 0.4116, -0.3734]])
张量的运算
以加法运算为例,介绍几种张量的运算方法:
#+重载
y = torch.rand(5,3)
print(x+y)
#torch.add /可提供一个输出张量存储结果
print(torch.add(x,y))
result = torch.empty(5,3)
torch.add(x, y, out = result)
print(result)
#in-place方式运算 改变y值
y.add_(x)
print(y)
#输出
tensor([[1.1657, 0.7392, 1.5607],
[1.8393, 0.6036, 0.5653],
[0.6798, 0.8985, 1.0473],
[0.0417, 0.6784, 0.3095],
[0.2775, 0.4266, 0.7707]])
切片与变形
张量可以跟numpy矩阵一样实现切片操作:
print(y[4,:])
#输出
tensor([0.2775, 0.4266, 0.7707])
若要改变tensor的形状,使用view函数,会将tensor按照索引顺序重新排列:
y.view(3,-1)
print(y)
#输出
tensor([[1.1657, 0.7392, 1.5607, 1.8393, 0.6036],
[0.5653, 0.6798, 0.8985, 1.0473, 0.0417],
[0.6784, 0.3095, 0.2775, 0.4266, 0.7707]])
2、Pytorch的自动微分
requires_grad
torch.Tensor 是包的核心类。如果将其属性 .requires_grad 设置为 True,则会开始跟踪针对 tensor 的所有操作。完成计算后,您可以调用 .backward() 来自动计算所有梯度。该张量的梯度将累积到 .grad 属性中。这意味着一个张量的requires_grad为True的情况下,接下来使用该张量进行计算的时候会追踪全部的计算过程,形成一张计算图。之后对输出进行反向传播计算的时候会自动的计算所有的梯度。如下所示:
import torch
x = torch.ones(2,2, requires_grad = True)
print(x)
y = x+2
print(y)
print(y.requires_grad)
z = y*y*3
out = z.mean()
print(z,out)
#输出
#x
tensor([[1., 1.],
[1., 1.]], requires_grad=True)
#y and requires_grad
tensor([[3., 3.],
[3., 3.]], grad_fn=<AddBackward0>)
True
#z,out
tensor([[27., 27.],
[27., 27.]], grad_fn=<MulBackward0>)
tensor(27., grad_fn=<MeanBackward0>)
同时可以通过requires_grad_()改变张量的可导性:
x = torch.ones(2,2)
print(x.requires_grad)
x.requires_grad_(True)
print(x.requires_grad)
#输出
False
True
反向传播
在获取到requires_grad张量计算后的输出out之后,我们可以采用.backward()进行自动的反向传播,计算出tensor图中涉及的变量导数:
out.backward()
print(x.grad)
#输出
tensor([[4.5000, 4.5000],
[4.5000, 4.5000]])
需要注意的是,当输出张量不再是一个标量的时候,自动反向求导不能正常进行,我们需要传递简单向量给backward()作为参数。如下所示:
x = torch.randn(3, requires_grad=True)
print(x)
y = x * 2
while y.data.norm() < 1000:
y = y * 2
print(y)
#输出
tensor([-0.6820, -1.6825, 1.3975], requires_grad=True)
tensor([-349.1966, -861.4306, 715.5014], grad_fn=<MulBackward0>)
可以看到当前的输出y不是一个标量,如果对其使用backward的时候会出现如下报错:
RuntimeError: grad can be implicitly created only for scalar outputs
因此需要给反向传播输入传递向量作为参数:
v = torch.tensor([1.0, 1.0, 0.1], dtype=torch.float)
y.backward(v)
print(x.grad)
#输出
tensor([512.0000, 512.0000, 51.2000])
停止跟踪
在评估模型时,停止跟踪特别有用,因为模型在训练阶段具有 requires_grad = True 的可训练参数有利于调参,但在评估阶段我们不需要梯度。您可以将代码块使用 with torch.no_grad(): 包装起来。如下所示:
x = torch.rand(5,3, requires_grad = True)
y = x*x
with torch.no_grad():
z = x*2
print(x.requires_grad)
print(y.requires_grad)
print(z.requires_grad)
#输出
True
True
False
3、神经网络的构建
在了解PyTorch基本内容之后,我们开始着手搭建自己的神经网络。神经网络是基于自动梯度 (autograd)来定义一些模型,一个 nn.Module 包括层和一个方法 forward(input) 它会返回输出(output)。
定义神经网络
import torch
import torch.nn as nn
import torch.nn.functional as F
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
# 1 input image channel, 6 output channels, 5x5 square convolution
# kernel
self.conv1 = nn.Conv2d(1, 6, 5)
self.conv2 = nn.Conv2d(6, 16, 5)
# an affine operation: y = Wx + b
self.fc1 = nn.Linear(16 * 5 * 5, 120)
self.fc2 = nn.Linear(120, 84)
self.fc3 = nn.Linear(84, 10)
#前向传播过程
def forward(self, x):
# Max pooling over a (2, 2) window
x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2))
# If the size is a square you can only specify a single number
x = F.max_pool2d(F.relu(self.conv2(x)), 2)
x = x.view(-1, self.num_flat_features(x))
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
x = self.fc3(x)
return x
#输入全连接层总数
def num_flat_features(self, x):
size = x.size()[1:] # all dimensions except the batch dimension
num_features = 1
for s in size:
num_features *= s
return num_features
net = Net()
print(net)
#输出网络架构
Net(
(conv1): Conv2d(1, 6, kernel_size=(5, 5), stride=(1, 1))
(conv2): Conv2d(6, 16, kernel_size=(5, 5), stride=(1, 1))
(fc1): Linear(in_features=400, out_features=120, bias=True)
(fc2): Linear(in_features=120, out_features=84, bias=True)
(fc3): Linear(in_features=84, out_features=10, bias=True)
)
对于网络当中可以训练的参数可以通过.parameters()获取,从输出中看到公有十组可以训练的网络参数(对应于五层结构每层的权重W和偏置b):
params = list(net.parameters())
print(len(params))
print(params[0].size()) # conv1's .weight
#输出
10
torch.Size([6, 1, 5, 5]) #第一层卷积层参数6个5*5*1卷积核
让我们尝试随机生成一个 32x32 的输入图片通过网络获取输出。
input = torch.randn(1, 1, 32, 32)
out = net(input)
print(out)
#输出
tensor([[-0.0233, 0.0159, -0.0249, 0.1413, 0.0663, 0.0297, -0.0940, -0.0135,
0.1003, -0.0559]], grad_fn=<AddmmBackward>)
在此,我们完成了:
1.定义一个神经网络
2.处理输入
还剩下:
1.计算损失值
2.调用反向传播
3.更新网络中的权重
损失函数
一个损失函数需要一对输入:模型输出和目标,然后计算一个值来评估输出距离目标有多远。
有一些不同的损失函数在 torch.nn 包中。一个简单的损失函数就是 nn.MSELoss ,这计算了均方误差。
output = net(input)
target = torch.randn(10) # a dummy target, for example
target = target.view(1, -1) # make it the same shape as output
criterion = nn.MSELoss()
loss = criterion(output, target)
print(loss)
#输出
tensor(1.3389, grad_fn=<MseLossBackward>)
反向传播
为了实现反向传播损失,我们所有需要做的事情仅仅是使用 loss.backward()。你需要清空现存的梯度,要不然梯度将会和现存的梯度累计到一起。
现在我们调用 loss.backward() ,然后看一下 con1 的偏置项在反向传播之前和之后的变化。
net.zero_grad() # zeroes the gradient buffers of all parameters
print('conv1.bias.grad before backward')
print(net.conv1.bias.grad)
loss.backward()
print('conv1.bias.grad after backward')
print(net.conv1.bias.grad)
#输出
conv1.bias.grad before backward
tensor([0., 0., 0., 0., 0., 0.])
conv1.bias.grad after backward
tensor([-0.0054, 0.0011, 0.0012, 0.0148, -0.0186, 0.0087])
参数更新
在torch.optim 中实现了许多的参数更新方法,例如SGD, Nesterov-SGD, Adam, RMSProp等。使用方法如下:
import torch.optim as optim
# 创建SGD来进行参数更新
optimizer = optim.SGD(net.parameters(), lr=0.01)
# 在训练的循环当中
optimizer.zero_grad() # zero the gradient buffers
output = net(input)
loss = criterion(output, target)
loss.backward()
optimizer.step() # 参数更新