注意确定已经安装了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
我们输出一下损失函数
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
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()
最后这个例子我没实现,照抄的。
这算是基本上的模型构建,主要包括模型、损失函数计算、优化、最后的迭代。