首页.jpg
注意确定已经安装了torch和torchvision
使用案例学习Pytorch
在完成60分钟入门之后,接下来有六节tutorials和五节关于文本处理的tutorials。争取一天一节。不过重点是关注神经网络构建和数据处理部分。
教程通过下面的几个例子介绍了Pytorch的基本概念。Pytorch的核心是以下两个特性:
- n维张量,和numpy类似但可以在GPU上运行
- 构建和训练神经网络的自动微分功能
接下来教程会使用全连接Relu网络作为运行案例,这个网络有一个隐藏层hy,使用梯度下降进行训练拟合随即数据。
下面是教程目录。
1.Tensors(张量)
1.1 Warm-up:numpy
1.2 PyTorch:Tensors
2.Autograd(自动梯度)
2.1 PyTorch:Variables and autograd (变量和自动梯度)
2.2 PyTorch : Defining new autograd functions(定义新的自动梯度函数)
2.3 TensorFlow: Static Graphs (静态图)
3.nn module
3.1 PyTorch: nn
3.2 PyTorch: optim
3.3 PyTorch: Custom nn Modules (定制nn模块)
3.4 PyTorch: Control Flow + Weight Sharing (控制流+权重分享)
1. Tensor
1.1 热身
介绍Pytorch之前使用numpy实现一个网络。numpy提供的是一个n维数组对象,以及操作这些数组的函数。但是numpy不是为计算图、深度学习、梯度计算而生,在这里生成一个两层的网络,然后使用随即数据拟合,前向、后向传播均使用numpy操作。
# -*- coding:utf-8 -*- import numpy as np import matplotlib.pyplot as plt N, D_in, H, D_out = 64, 1000, 100, 10 x = np.random.randn(N, D_in) y = np.random.randn(N, D_out) w1 = np.random.randn(D_in, H) w2 = np.random.randn(H, D_out) learning_rate = 1e-6 for t in range(500): h = x.dot(w1) h_relu = np.maximum(h, 0) y_pred = h_relu.dot(w2) loss = np.square(y_pred - y).sum() print(t, loss) grad_y_pred = 2.0 * (y_pred - y) grad_w2 = h_relu.T.dot(grad_y_pred) grad_h_relu = grad_y_pred.dot(w2.T) grad_h = grad_h_relu.copy() grad_h[h < 0] = 0 grad_w1 = x.T.dot(grad_h) w1 -= learning_rate * grad_w1 w2 -= learning_rate * grad_w2
我们输出一下损失函数
image.png
1.2 PyTorch:Tensors
Pytorch的基本概念Tensor,Tensor与numpy的array类似,是一个n维的array。
Pytorch提供多种进行Tensor运算,教程中还提到,Pytorch同样也不是专门进行深度学习、计算图、计算梯度的。但是优势在于能在GPU上运行。
在这里使用Pytorch Tensor来拟合一个2层的网络,需要手动执行前向和反向操作。
import torch import matplotlib.pyplot as plt dtype = torch.float device = torch.device('cpu') N, D_in, H, D_out = 64, 1000, 100, 10 x = torch.randn(N, D_in, device=device, dtype=dtype) y = torch.randn(N, D_out, device=device, dtype=dtype) w1 = torch.randn(D_in, H, device=device, dtype=dtype) w2 = torch.randn(H, D_out, device=device, dtype=dtype) learning_rate = 1e-6 loss_list = [] t_list = [] for t in range(500): h = x.mm(w1) h_relu = h.clamp(min=0) y_pred = h_relu.mm(w2) loss = (y_pred - y).pow(2).sum().item() print(t, loss) loss_list.append(loss) t_list.append(t) grad_y_pred = 2.0 * (y_pred - y) grad_w2 = h_relu.t().mm(grad_y_pred) grad_h_relu = grad_y_pred.mm(w2.t()) grad_h = grad_h_relu.clone() grad_h[h < 0] = 0 grad_w1=x.t().mm(grad_h) w1-=learning_rate*grad_w1 w2-=learning_rate*grad_w2
image.png
2.Autograd(自动梯度)
2.1 PyTorch:Tensors and autograd
实际操作中肯定不能像上面那样手动操作,这样,就需要有一个自动求梯度,自动反向传递。
在之前60分钟已经做了介绍,那就是autograd包,在使用autograd时,前向通道定义一个计算图(computational graph),图中的节点是Tensor,边是依据输入Tensor产生的输出Tensro的函数,这样图的反向传播就可以计算梯度了。
操作历程如下
- 将Tensors打包到Variable对象中(一个Variable代表一个计算图中的节点。如果x是一个Variable,那么x. data 就是一个Tensor),x.grad是另一个Tensor,这个Tensor通过对某个标量保持x的梯度
- 这里使用Tensor和autograd来实现上面的那个两层网络,不再需要手动实现反向传播了!
import torch dtype = torch.float device = torch.device('cpu') N, D_in, H, D_out = 64, 1000, 100, 10 x = torch.randn(N, D_in, device=device, dtype=dtype) y = torch.randn(N, D_out, device=device, dtype=dtype) # 在这里设置requires_grad为True,就可以在接下来进行反向传播计算 w1 = torch.randn(D_in, H, device=device, dtype=dtype, requires_grad=True) w2 = torch.randn(H, D_out, device=device, dtype=dtype, requires_grad=True) learning_rate = 1e-6 for t in range(500): y_pred = x.mm(w1).clamp(min=0).mm(w2) #计算损失,这时损失已经是一个Tensor,shape为(1,),loss.item()获取的就是损失的标量值 loss=(y_pred-y).pow(2).sum() print(t,loss.item()) loss.backward() with torch.no_grad(): w1-=learning_rate*w1.grad w2-=learning_rate*w2.grad #在使用完之后将梯度归零 w1.grad.zero_() w2.grad.zero_()
对比之前的操作,会发现Pytorch大大降低了手工操作的负担,只需要在设定的时候增加requires_grad=True,在最后对权重进行归零即可。
2.2 PyTorch : Defining new autograd functions(定义新的自动梯度函数)
在底层,每次原始的autograd操作都是对Tensor的两个方法的操作。
- forward方法用于计算输入Tensor
- backward方法获取输出的梯度,并且计算输入Tensors相对于该相同标量值的梯度
- 在Pytorch中,可以容易定义自己的autograd操作,通过定义子类torch.autograd.Function来实现forward和backward函数,然后就可以通过构建实例并进行调用来使用新的autograd运算符。传递包含输入数据的Variables。
这个例子就是定制一个autograd函数来执行Relu非线性,进一步执行两层神经网络。
import torch class MyRelu(torch.autograd.Function): @staticmethod def forward(ctx, input): ''' ctx是一个上下文对象,可用于存储信息以进行反向计算。 :param ctx: :param input: :return: ''' ctx.save_for_backward(input) return input.clamp(min=0) @staticmethod def backward(ctx, grad_output): ''' 在backwward中,获取一个含有损失的梯度的Tensor,并且计算相对于输入的损失的梯度。 :param ctx: :param grad_output: :return: ''' input, = ctx.saved_tensors grad_input = grad_output.clone() grad_input[input < 0] = 0 return grad_input dtype = torch.float device = torch.device("cpu") N, D_in, H, D_out = 64, 1000, 100, 10 x = torch.randn(N, D_in, device=device, dtype=dtype) y = torch.randn(N, D_out, device=device, dtype=dtype) w1 = torch.randn(D_in, H, device=device, dtype=dtype, requires_grad=True) w2 = torch.randn(H, D_out, device=device, dtype=dtype, requires_grad=True) learning_rate = 1e-6 for t in range(500): relu = MyRelu.apply y_pred = relu(x.mm(w1)).mm(w2) loss = (y_pred - y).pow(2).sum() print(t, loss.item()) loss.backward() with torch.no_grad(): w1 -= learning_rate * w1.grad w2 -= learning_rate * w2.grad w1.grad.zero_() w2.grad.zero_()
2.3 TensorFlow: Static Graphs (静态图)
这一节空出来,主要内容是为了对比Tensorflow和Pytorch在动态图和静态图之间的区别,不用说,肯定Pytorch更好一些。
3.nn module(神经网络模块)
3.1 PyTorch: nn
计算图和autograd时很有用的范式,可以用来定义复杂运算符和自动求导。这时对于大型网络而言,原始的autograd就有些低级。
在构建神经网络时,会经常需要把计算安排在层(layers)中。某些层有可学习的参数,将会在学习中进行优化。
Pytorch提供的nn包就是类似Keras的高级抽象,其中定义了一系列Modules,相当于神经网络中的层,一个Module接收输入Variables,计算输出Variables,但是也可以保持一个内部状态,例如包含了可学习参数的Variables。nn 包还定义了一系列在训练神经网络时常用的损失函数。
接下来就是使用nn包来实现上面的两层神经网络
import torch N, D_in, H, D_out = 64, 1000, 100, 10 x = torch.randn(N, D_in) y = torch.randn(N, D_out) model = torch.nn.Sequential( torch.nn.Linear(D_in, H), torch.nn.ReLU(), torch.nn.Linear(H, D_out), ) loss_fn = torch.nn.MSELoss(size_average=False) learning_rate = 1e-6 for t in range(500): y_pred = model(x) loss = loss_fn(y_pred, y) print(t, loss.data[0]) model.zero_grad() loss.backward() with torch.no_grad(): for param in model.parameters(): param -= learning_rate * param.grad
3.2 PyTorch: optim(优化)
在前面更新权重,使用的是手动改变学习参数,这对于使用简单的优化算法(SGD)是可以的,但是在使用更加复杂的优化算法(ADAGrad,RMSProp,Adam)训练神经网络。
Pytorch提供了optim包对优化算法进行抽象。
同样是刚才的那个2层神经网络,这里使用optim来进行优化。
import torch N, D_in, H, D_out = 64, 1000, 100, 10 x = torch.randn(N, D_in) y = torch.randn(N, D_out) model=torch.nn.Sequential( torch.nn.Linear(D_in,H), torch.nn.ReLU(), torch.nn.Linear(H,D_out), ) loss_fn=torch.nn.MSELoss(size_average=False) learning_rate=1e-4 optimizer=torch.optim.Adam(model.parameters(), lr=learning_rate) for t in range(500): y_pred=model(x) loss=loss_fn(y_pred,y) print(t,loss.item()) optimizer.zero_grad() loss.backward() optimizer.step()
对比前面的,你会发现越来越简化,就能生成一个神经网络。
3.3 PyTorch: Custom nn Modules (定制nn模块)
有时候,需要设定比现有模块序列更加复杂的模型。这时,你可以通过生成一个nn.Module的子类来定义一个forward。该forward可以使用其他的modules或者其他的自动梯度运算来接收输入Variables,产生输出Variables。
在这个例子中,我们实现两层神经网络作为一个定制的Module子类。
import torch class TwoLayerNet(torch.nn.Module): def __init__(self, D_in, H, D_out): super(TwoLayerNet, self).__init__() self.linear1 = torch.nn.Linear(D_in, H) self.linear2 = torch.nn.Linear(H, D_out) def forward(self, x): h_relu = self.linear1(x).clamp(min=0) y_pred = self.linear2(h_relu) return y_pred N, D_in, H, D_out = 64, 1000, 100, 10 x = torch.randn(N, D_in) y = torch.randn(N, D_out) model = TwoLayerNet(D_in, H, D_out) criterion=torch.nn.MSELoss(size_average=False) optimizer=torch.optim.SGD(model.parameters(),lr=1e-4) for t in range(500): y_pred=model(x) loss=criterion(y_pred,y) print(t,loss.item()) optimizer.zero_grad() loss.backward() optimizer.step()
3.4 PyTorch: Control Flow + Weight Sharing (控制流+权重分享)
我们实现一个非常奇怪的模型来作为动态图和权重分享的例子。这个模型是一个全连接的ReLU网络。每一个前向通道选择一个1至4之间的随机数,在很多隐含层中使用。多次使用相同的权重来计算最内层的隐含层。
这个模型我们使用正常的Python流控制来实现循环。在定义前向通道时,通过多次重复使用相同的Module来实现权重分享。
我们实现这个模型作为一个Module的子类。
# -*- coding: utf-8 -*- import random import torch class DynamicNet(torch.nn.Module): def __init__(self, D_in, H, D_out): super(DynamicNet, self).__init__() self.input_linear = torch.nn.Linear(D_in, H) self.middle_linear = torch.nn.Linear(H, H) self.output_linear = torch.nn.Linear(H, D_out) def forward(self, x): h_relu = self.input_linear(x).clamp(min=0) for _ in range(random.randint(0, 3)): h_relu = self.middle_linear(h_relu).clamp(min=0) y_pred = self.output_linear(h_relu) return y_pred # N is batch size; D_in is input dimension; # H is hidden dimension; D_out is output dimension. N, D_in, H, D_out = 64, 1000, 100, 10 # Create random Tensors to hold inputs and outputs x = torch.randn(N, D_in) y = torch.randn(N, D_out) # Construct our model by instantiating the class defined above model = DynamicNet(D_in, H, D_out) # Construct our loss function and an Optimizer. Training this strange model with # vanilla stochastic gradient descent is tough, so we use momentum criterion = torch.nn.MSELoss(reduction='sum') optimizer = torch.optim.SGD(model.parameters(), lr=1e-4, momentum=0.9) for t in range(500): # Forward pass: Compute predicted y by passing x to the model y_pred = model(x) # Compute and print loss loss = criterion(y_pred, y) print(t, loss.item()) # Zero gradients, perform a backward pass, and update the weights. optimizer.zero_grad() loss.backward() optimizer.step()
最后这个例子我没实现,照抄的。
这算是基本上的模型构建,主要包括模型、损失函数计算、优化、最后的迭代。