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