作者:童蒙
编辑:amethyst
tensor是一种类似于array和matrices的数据结构。PyTorch一般用tensors来表示输入和输出,同时也来表示模型的参数。tensor同Numpy中ndarray类似,然而tensors能够在GPU等其他硬件上运行。
import torch
import numpy as py
1. tensor的初始化
直接从数据转换
data = [[1,2],[3,4]]
x_data = torch.tensor(data)
从numpy转换
- 使用tensor进行转换
np_array = np.array(data)
x_np = torch.tensor(np_array)
- 使用from_numpy进行转换tensor
n = np.ones(5)
t = torch.from_numpy(n)
- 使用numpy将tensor转换成ndarray
t = torch.ones(5)
print(f"t: {t}")
n = t.numpy()
print(f"n: {n}")
- CPU的tensor和Numpy共享内存,修改一个会修改另一个。
从其他tensor转换
新的tensor保留着参数的相关性质,除非被明确的覆盖。
x_ones = torch.one_like(x_data)
x_rands = torch.rand_like(x_data , dtype = torch.float)
指定shape
shape是一个tuple,指定了维度。
shape = (2,3,)
rand_tensor = torch.rand(shape)
ones_tensor = torch.ones(shape)
zeros_tensor = torch.zeros(shape)
2. tensor的属性
tensor的属性有shape,datatype和 device。
tensor = torch.rand(3,4)
print(f"Shape of tensor: {tensor.shape}")
print(f"Datatype of tensor: {tensor.dtype}")
print(f"Device tensor is stored on: {tensor.device}")
3. tensor的操作
tensor的操作有很多,100多种,包括转置、检索、分片、数学计算、抽样、线性代数,以及其他的操作。这些操作也可以用在GPU上,可以用如下操作:
if torch.cuda.is_available():
tensor = tensor.to('cuda')
类numpy的索引和切片
tensor = torch.ones(4,4)
tensor[:,1] = 0
合并tensor
- 使用torch.cat来在某一个维度上进行合并
t1 = torch.cat([tensor, tensor, tensor], dim=1)
- 也可以使用torch.stack进行合并
t2 = torch.stack([tensor, tensor] ,dim =1 )
tensor的计算
- 乘积运算
tensor.matmul(tensor.T)
tensor @ tensor.T
- 点乘运算
tensor.mul(tensor)
tensor * tensor
- 原位计算
使用 “_” 为后缀的进行计算,例如 copy_ , t_ , 会覆盖原始值。例如:
tensor.add_(5)
4.Torch.autograd
“torch.autograd”是PyTorch的自动化微分引擎,从而用来方便进行神经网络训练。下面我们来介绍一下,autograd怎么来帮助神经网络进行训练。
4.1背景信息
Neural networks(NNs)是封装了一系列的函数,来对输入数据进行计算。这些函数有各种参数(weights和biases),都存储在tensor的数据结构中。
训练一个NN有两个步骤:
- Forward propagation:在前向传播中,NN使用各个函数,对输入进行计算,得到最终的结果。
- Backward propagation:在后向传播中,NN通过对前向传播得到的结果计算误差,对参数进行成比例的调整。从最终的output出发,针对每一个函数的参数进行计算error的偏差,并且使用梯度下降来优化参数。
4.2 PyTorch中的用法
首先导入一个模型,然后获得一个随机的输入矩阵,随机生成一个label。
import torch, torchvision
model = torchvision.models.resnet18(pretrained=True)
data = torch.rand(1, 3, 64, 64)
labels = torch.rand(1, 1000)
使用forwardpropagation来计算:
prediction = model(data) # forward pass
对prediction和label来计算loss,然而反向传播loss到整个网络中,这个过程针对loss这个tensor使用backward()。Autograd会计算和存储每个模型的参数的梯度值,并且存在.grad性质中。
loss = (prediction - lables).sum()
loss.backward()
最后,我们加载一个optimizer,以SGD为例,首先定义一个。
optim = torch.optim.SGD(model.parameters(), lr=1e-2, momentum=0.9)
之后,使用step() 来进行梯度下降,优化后的参数会存在.grad中。
optim.step()
这样,一个模型就训练好了。
4.3 Autograd如何做微分
我们来看看autograd如何积累梯度,首先创建两个tensor,required_grad=True,表示会记录每一个操作。
import torch
a = torch.tensor([2.,3.],requires_grad = True)
b = torch.tensor([6.,4.],requires_grad = True)
## 构建一个tensor为Q
Q=3*a**3 - b**2
假设a和b是NN的参数,Q是error值。在NN训练中,我们来获得error反馈在各参数的梯度。
当我们调用.backward(),autograd计算梯度,并且存储在相应的.grad的属性中。当Q是一个vector时候,在使用时候,需要提供一个gradient的参数,长度同Q的shape一致,代表Q的梯度。假设为1 。
external_grad = torch.tensor([1., 1.])
Q.backward(gradient=external_grad)
a.grad
b.grad
也可以把Q变成一个标量,然后直接使用backward()。
Q.sum().backward()
4.4 计算图
autograd会保存data和执行的操作在一个DAG的图形中,构成了Function的对象。在DAG中,输入是叶子,output是root。通过搜索root到leaves,可以计算梯度。
前向传播中,autograd会做两件事:
- 计算得到结果
- 维护DAG的操作函数
当调用backward()时,从root开始,autograd会:
- 计算每一个.grad_fn的梯度
- 将他们累加到.grad中
- 利用DAG的链规则,回溯
设置requires_grad=False可以关闭梯度的计算。
在NN中,不计算梯度的参数称之为 frozen parameters。这个用于冻结某些参数,来调整另一些参数;也可以用于调整一个预先训练好的network。
from torch import nn, optim
model = torchvision.models.resnet18(pretrained=True)
# Freeze all the parameters in the network
for param in model.parameters():
param.requires_grad = False
model.fc = nn.Linear(512, 10)
optimizer = optim.SGD(model.fc.parameters(), lr=1e-2, momentum=0.9)
也可以用装饰器torch.no_grad()来替代。
x = torch.tensor([1], requires_grad=True)
with torch.no_grad():
y = x * 2
y.requires_grad
@torch.no_grad()
def doubler(x):
return x * 2
z = doubler(x)
z.requires_grad
5. 神经网络
可以使用torch.nn 来构建神经网络。nn.autograd定义model和进行微分,nn.Module包含很多层,使用forward进行计算output。
一个神经网络包括以下步骤:
- 定义一个NN,包含一些参数
- 迭代inputs
- 利用input进行计算
- 计算loss
- 反向传播到nn的参数
- 调整nn的权重,例如weight = weight - learning_rate * gradient
5.1 定义网络
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, 3x3 square convolution
# kernel
self.conv1 = nn.Conv2d(1, 6, 3)
self.conv2 = nn.Conv2d(6, 16, 3)
# an affine operation: y = Wx + b
self.fc1 = nn.Linear(16 * 6 * 6, 120) # 6*6 from image dimension
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)
当定义好forward的时候,backward自动也定义好了。
可以使用net.parametes()来调用模型中的参数。
params = list(net.parameters())
print(len(params))
print(params[0].size()) # conv1's .weight
假设输入为32*32,代码如下:
input = torch.randn(1, 1, 32, 32)
out = net(input)
print(out)
首先清零梯度,然后做后向传播。
net.zero_grad()
out.backward(torch.randn(1, 10))
回顾一下调用的模块:
- torch.tensor
- nn.Module()
- nn.parameters()
- autograd.Functions()
5.2 计算损失函数
损失函数为预测值和目标值之间的差异,用来估计预测值同目标的偏离程度。
损失函数有很多,最简单的是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)
当调用loss.backward()时候,整个DAG图都会进行微分,图中的所有tensor,如果requires_grad = True,那么.grad属性会累积梯度。
print(loss.grad_fn) # MSELoss
print(loss.grad_fn.next_functions[0][0]) # Linear
print(loss.grad_fn.next_functions[0][0].next_functions[0][0]) # ReLU
5.3 后向传播
使用loss.backward()来反向传播error。需要注意的是,需要首先清除已经存在的梯度,否则梯度会继续保存。
可以使用loss.backward(),来看前后的变化。
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)
5.4 更新权重
最简单的权重更新方法是SGD,
w = w- learning_rate * gradient
简单的实现如下:
learning_rate = 0.01
for f in net.parameters():
f.data.sub_(f.grad.data * learning_rate)
当用nn的时候,可以使用不同的方法,比如SGD、Adam等,利用torch.optim来实现,使用方法如下:
import torch.optim as optim
# create your optimizer
optimizer = optim.SGD(net.parameters(), lr=0.01)
# in your training loop:
optimizer.zero_grad() # zero the gradient buffers
output = net(input)
loss = criterion(output, target)
loss.backward()
optimizer.step() # Does the update
5.5 其他
保存模型
PATH = './cifar_net.pth'
torch.save(net.state_dict(), PATH)
加载模型
net = Net()
net.load_state_dict(torch.load(PATH))
6.参考资料
https://pytorch.org/tutorials/beginner/deep_learning_60min_blitz.html
网友评论