10 分钟理解 PyTorch 代码

来源:互联网 发布:淘宝论坛app官方下载 编辑:程序博客网 时间:2024/05/22 00:49

本文译自: Understand PyTorch code in 10 minutes

PyTorch 是一个新的深度学习框架. 本文的内容基于 Justin Johnson 的 教程, 如果想要有更多了解或有更多时间的话建议仔细研究一下.

PyTorch 主要包含 4 个包 (package):

  1. torch: 一个通用性的数组库, 与 Numpy 类似, 当 tensor 类型被转化(torch.cuda.TensorFloat)的时候可以在 GPU 上进行计算.

  2. torch.autograd: 一个用来构建计算图来自动求梯度的包.

  3. torch.nn: 一个包含常见 layer 和 cost function 的 神经网络库.

  4. torch.optim: 包含像 SGD, Adam 这样常见优化算法的一个优化包.

导入工作

你可以像下面这样导入 PyTorch:

import torch # arrays on GPUimport torch.autograd as autograd #build a computational graphimport torch.nn as nn ## neural net libraryimport torch.nn.functional as F ## most non-linearities are hereimport torch.optim as optim # optimization package

使用 torch array 代替 numpy ndarray -> 提供在 GPU 上的线性代数运算支持

PyTorch 提供了类似 Numpy array 的多维数组, 当数据类型可以被转化为 (torch.cuda.TensorFloat) 时就可以放到 GPU 上进行处理. 多维数组和它相关的一些函数都是通用的科学计算工具.

Torch for numpy users 可以查看它是如何与 numpy 相关联的:

# 2 matrices of size 2x3 into a 3d tensor 2x2x3d=[[[1., 2.,3.],[4.,5.,6.]],[[7.,8.,9.],[11.,12.,13.]]]d=torch.Tensor(d) # array from python listprint "shape of the tensor:",d.size()# the first index is the depthz=d[0]+d[1]print "adding up the two matrices of the 3d tensor:",z
shape of the tensor: torch.Size([2, 2, 3])adding up the two matrices of the 3d tensor:  8  10  12 15  17  19[torch.FloatTensor of size 2x3]
# a heavily used operation is reshaping of tensors using .view()print d.view(2,-1) #-1 makes torch infer the second dim
  1   2   3   4   5   6  7   8   9  11  12  13[torch.FloatTensor of size 2x6]

torch.autograd -> 创建一个计算图并自动计算梯度

第二个特性是 autograd 包, 它能够定义一个计算图以便于我们能够自动计算梯度. 在计算图中, 一个节点就是一个数组, 一条边就是在数组上的一个操作. 为了创建一个计算图, 我们需要在函数里面封装一个数组来创建一个节点 (torch.autograd.Variable()). 然后在该节点上的所有操作将会被定义为边, 操作的结果将会成为计算图中新的节点. 计算图中的每个节点都有一个 node.data 属性, 它是一个多维数组. 还有一个 node.grad 属性, 它是某个标量的梯度 (node.grad 同时也是一个 .Variable()). 在定义好图以后, 只需一个命令 (loss.backward()) 就可以计算图中所有节点的 loss 梯度.

  • 使用 torch.autograd.Variable() 可以将一个 Tensor 转化成为计算图中一个节点.
    • 通过 x.data 来获取它的值
    • 通过 x.grad 来获取的梯度
  • 在 .Variable() 上施加操作来生成图中的边
# d is a tensor not a node, to create a node based on it:x= autograd.Variable(d, requires_grad=True)print "the node's data is the tensor:", x.data.size()print "the node's gradient is empty at creation:", x.grad # the grad is empty right now
the node's data is the tensor: torch.Size([2, 2, 3])the node's gradient is empty at creation: None
# do operation on the node to make a computational graphy= x+1z=x+ys=z.sum()print s.creator
<torch.autograd._functions.reduce.Sum object at 0x7f1e59988790>
# calculate gradientss.backward()print "the variable now has gradients:",x.grad
the variable now has gradients: Variable containing:(0 ,.,.) =  2  2  2  2  2  2(1 ,.,.) =  2  2  2  2  2  2[torch.FloatTensor of size 2x2x3]

torch.nn 包含了各种神经网络的 layer (对一个 tensor 行的线性映射) + (非线性) -> 无须手动控制 tensor 和参数即可构建一个神经网络计算图

第三个特性是一个高层次的神经网络库 (torch.nn), 它抽象出了神经网络的 layer 中所有的参数处理, 使得能够几个命令就可以定义一个神经网络 (比如, torch.nn.conv). 这个包同样也带有常用的 loss function (比如, torch.nn.MSEloss). 我们以定义一个模型容器开始, 比如使用 (torch.nn.Sequential) 有一系列层的模型, 并且按顺序列出我们想要的层. 这个库处理其他的所有事情; 我们可以通过 model.parameters() 来获取参数 (Variables()).

# linear transformation of a 2x5 matrix into a 2x3 matrixlinear_map=nn.Linear(5,3)print "using randomly initialized params:", linear_map.parameters
using randomly initialized params: <bound method Linear.parameters of Linear (5 -> 3)>
# data has 2 examples with 5 features and 3 targetdata=torch.randn(2,5) # trainingy=autograd.Variable(torch.randn(2,3)) # target# make a nodex=autograd.Variable(data, requires_grad=True)# apply transformation to a node creates a computational grapha=linear_map(x)z=F.relu(a)o=F.softmax(z)print "output of softmax as a probability distribution:", o.data.view(1,-1)# loss functionloss_func=nn.MSELoss() #instantiate loss functionL=loss_func(z,y) # calculateMSE loss between output and targetprint "Loss:", L
output of softmax as a probability distribution: 0.2092  0.1979  0.5929  0.4343  0.3038  0.2619[torch.FloatTensor of size 1x6]Loss: Variable containing: 2.9838[torch.FloatTensor of size 1]

我们也可以通过子集 torch.nn.Module 自定义 layer, 实现一个接受一个 Variable() 作为输入, 并输出一个 Variable()forward() 函数. 我们也可以定义一个随时间变化的 layer 来创建一个动态网络!

  • 当自定义一个 layer, 需要实现 2 个函数:
    • 首先需要继承 init 函数, 然后 layer 中的所有参数必须被定义为类变量 (self.x)
    • 在 forward 函数里面, 我们传递输入, 在输入上施加操作并进行输出. 输入需要时一个 autograd.Variable() 以便于 pytorch 能够构建 layer 的计算图.
class Log_reg_classifier(nn.Module):    def __init__(self, in_size,out_size):        super(Log_reg_classifier,self).__init__() #always call parent's init        self.linear=nn.Linear(in_size, out_size) #layer parameters    def forward(self,vect):        return F.log_softmax(self.linear(vect)) #

torch.optim 可以进行优化 -> 我们通过 torch.nn 构建一个计算图, 通过 torch.autograd 来计算梯度, 然后输入到 torch.optim 来更新网络参数

第四个特性是一个配合 NN 库使用的优化包 (torch.optim). 这个库包含了一些像 Adam, RMSprop 的 optimizer. 我们定义一个 optimizer 并传入网络参数和学习率 (opt = torch.optim.Adam(model.parameters(), lr=learning_rate), 然后我们可以调用 opt.step() 对于我们的参数做一步更新.

optimizer=optim.SGD(linear_map.parameters(),lr=1e-2) # instantiate optimizer with model params + learning rate# epoch loop: we run following until convergenceoptimizer.zero_grad() # make gradients zeroL.backward(retain_variables=True)optimizer.step()print L
Variable containing: 2.9838[torch.FloatTensor of size 1]

构建一个神经网络十分容易, 下面是一个完整示例:

# define modelmodel = Log_reg_classifier(10,2)# define loss functionloss_func=nn.MSELoss()# define optimizeroptimizer=optim.SGD(model.parameters(),lr=1e-1)# send data through model in minibatches for 10 epochsfor epoch in range(10):    for minibatch, target in data:        model.zero_grad() # pytorch accumulates gradients, making them zero for each minibatch        #forward pass        out=model(autograd.Variable(minibatch))        #backward pass        L=loss_func(out,target) #calculate loss        L.backward() # calculate gradients        optimizer.step() # make an update step