总结笔记 | 深度学习之Pytorch入门教程

笔记作者:王博Kings

(本文笔记pdf版本可在【人工智能算法与Python大数据】后台回复8003 )

一、整体学习的建议

1.1 如何成为Pytorch大神?

  • 打好深度学习基础
  • 学习Pytorch的官方tutorial
  • 打开Github,多看看教程
  • 使用 //discuss.pytorch.org, 阅读文档
  • 跑代码,项目,论文代码
  • 复现,实现模型,自己创造

1.2 如何读Github代码?

  1. 首先读简单的代码,比如三四个文件
  2. 然后十个文件
  3. 然后多个文件夹
  4. 整体套路是一样的

1.3 代码能力太弱怎么办?

  • 最简单的办法就行训练
  • 抄代码,抄个100行,然后再写,再抄
  • 刚开始会不理解,后面要思考为什么这么写
  • 写代码的时候不要反复重复!Repeat no!

二、Pytorch与TensorFlow概述

2.1 什么是Pytorch?

  • Facebook开源,2017年,论文《PyTorch 中的自动微分》,地址: //openreview.net/pdf?id=BJJsrmfCZ
  • 类似于Numpy,可以使用GPU,运行在CUDA上
  • PyTorch框架和Python语言的整合更加紧密
  • Pytorch动态图内置,可以定义深度学习模型,可灵活的进行训练和应用
  • 分布式训练并行化时,利用Python对异步执行的本地支持

2.1.1 Pytorch两个核心模块

  1. 计算图,按照需求动态构建,图会随着执行过程而改变
  2. Autograd,执行动态图的自动微分

2.1.2 Pytorch可视化:Visdom

  • 管理整体环境
  • 处理回调
  • 绘制图表细节

2.1.3 Pytorch的优缺点

优点:

  • 类似Python的代码
  • 动态图
  • 轻松编辑
  • 良好的文档和社区支持
  • 开源
  • 很多项目都在使用

缺点:

  • 可视化需要第三方
  • 生产部署需要使用API服务器

2.2 什么是TensorFlow

  • 谷歌开源,2015年,论文《TensorFlow:异构分布式系统上的大规模机器学习》,地址: //download.tensorflow.org/paper/whitepaper2015.pdf
  • 使用GPU增强训练,使用的是内置的GPU进行加速
  • 分布式训练并行化时,必须手动编写代码,并微调每个操作
  • 使用TensorFlow Fold库实现动态图
  • 可实现并行化或依赖驱动式调度,训练更快更有效率

2.2.1 TensorFlow两个核心模块

  1. 一个用于定义计算图以及在各种不同硬件上执行这些图的运行时间的软件库
  2. 一个计算图,计算图是以静态方式定义。与外部通信通过tf.Sessionobject和tf.Placeholder

2.2.2 TensorFlow可视化:TensorBoard

  • 分析程序
  • 展示数据(图像,文本,音频)
  • 可视化计算图的操作和层数信息
  • 跟踪和实时可视化损失和准确度等相关指标
  • 查看权重、偏差、张量等随时间变化的直方图

2.2.3 TensorFlow的优缺点

优点:

  • 简单的内置高级API
  • 使用TensorBoard可视化训练
  • 通过TensorFlow serving容易实现生产部署
  • 很容易的移动平台支持
  • 开源
  • 良好的文档和社区支持

缺点:

  • 静态图
  • 调试方法
  • 难以快速修改

2.3 Pytorch和TensorFlow对比

图片

1.学术界PyTorch已经全面超越TensorFlow,上手快,容易实现,验证想法

2.工业界TensorFlow仍然广泛应用,GPU部署方便

三、Pytorch的一些简单命令

第一步,导入torch:

import torch 

构造一个未被初始化的3×5的矩阵

x = torch.empty(3,5) 
x 

生成一个随机初始化的3×5矩阵

x = torch.rand(3,5) 
x 

生成一个全部为0的矩阵

x = torch.zeros(3,5) 
x 
x.dtype 
torch.float32 

生成一个全部为0的矩阵,long的矩阵

x = torch.zeros(3,5,dtype = torch.long) 
x 
x.dtype 
torch.int64 
# 或者 
x = torch.zeros(3,5).long() 

直接通过数据构建tensor

x = torch.tensor([1.2,4]) 
x 
tensor([1.2000,4.0000]) 

也可以通过已有tensor构建一个新的tensor

x = x.new_ones(3,5) 
# 或者改变类型 
x = x.new_ones(3,5,dtype=torch.double) 

构建一个和上一个形状相同的tensor

x = torch.randn_like(x, dtype=torch.float) 
x 

得到tensor的形状

x.shape 
torch.Size([3,5]) 

加法准则:

x + y 
torch.add(x,y) 
result = torch.empty(3,5) 
torch.add(x,y, out = result) 
result 
# 效果与下面类似 
result = x + y 

in-place加法

注意,任何in-place的运算都会以_结尾,比如x.copy_(y) , x.t_() ,这些都会改变x

y.add_(x) 
y 
# 注意,这里加了下划线,就把y直接进行更改了,效果等价于 y=y+x 

类似于Numpy的索引在Pytorch 的 tensor上面都可以使用,类比matlab

x[1:,1:] 

Resizing,想要resize、reshape一个tensor,可以使用torch.view

x = torch.randn(4,4) 
y = x.view(16) 
y = x.view(2,8)  
# 也可以用-1代表其中一个,它会自动计算 
y = x.view(2,-1)和 x.view(2,8)功能类似 

dir(x)可以看看x包含哪些功能

data 
grad 

怎么使用?

x.data 
x.grad 
x.item 把tensor变成数字 

Numpy和tensor之间的相互转换

a = tensor([1,1,1,1,1]) 
b = a.numpy() 
b 
array([1.,1.,1.,1.,1.], dtype=float32) 

注意,这里a和b是共享空间的,其中一个变,整体都会变
把Numpy array 转换成 Torch Tensor

import numpy as np 
a = np.ones(5) 
b = torch.from_numpy(a) 
b 

如果我们有GPU,可以转换为 CUDA Tensors

if torch.cuda.is_available() 
    device = torch.device("cuda") 
    y = torch.ones_like(x, device=device) 
    x = x.to(device) 
    z = x + y 
    # 注意,这里是GPU的 
    print(z) 
    # 转换成CPU进行输出的话 
    print(z.to("cpu", torch.double)) 

注意,如果是GPU的tensor,是没办法直接用.data这类操作,必须先转换再操作

y.cpu().data.numpy() 

numpy是CPU的库,并没有GPU的库,所以如果想使用numpy,但是你默认GPU产生的是tensor

四、实现两层神经网络

4.1 如何用numpy实现两层神经网络

一个全连接ReLU神经网络,一个隐藏层,没有bias。用来从x预测y,使用L2 Loss。

# 定义一些基本的信息,64个输入,每个输入是1000维,hidden是100维,输出是10维 
N, D_in, H, D_out = 64, 1000, 100, 10 
# 随机创建一些训练数据 
# x是64个1000维,x是64x1000矩阵 
# y是64个10维,y是64x10矩阵 
x = np.random.randn(N, D_in) 
y = np.random.randn(N, D_out) 
# 随机初始化权重 
# w1是1000个100维,w1是1000x100矩阵 
# w2是100个10维,w2是100x10矩阵 
w1 = np.random.randn(D_in, H) 
w2 = np.random.randn(H, D_out) 
learning_rate = 1e-6 
 for t in range(500): 
     # 前向传播,h是64x1000x1000x100=64x100 
     # h_relu是看哪个激活了,还是64x100 
     # y_pred是64x100x100x10=64x10 
     h = x.dot(w1) # N * H 
     h_relu = np.maximum(h, 0) # N * H 
     y_pred = h_relu.dot(w2) # N * D_out 
      
     # compute loss 
     loss = np.square(y_pred - y).sum() 
     print(it, loss) 
      
     # Backward pass 
     # 计算梯度,链式法则,挨个求导相乘 
     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和w2的权重 
     w1 -=learning_rate * grad_w1 
     w2 -=learning_rate * grad_w2  

4.2 如何用torch实现两层神经网络

这里将numpy修改为pytorch做这件事

pytorch用的是tensors

# 定义一些基本的信息,64个输入,每个输入是1000维,hidden是100维,输出是10维 
N, D_in, H, D_out = 64, 1000, 100, 10 
# 随机创建一些训练数据 
# x是64个1000维,x是64x1000矩阵 
# y是64个10维,y是64x10矩阵 
# np.random要改为torch 
x = torch.randn(N, D_in) 
y = torch.randn(N, D_out) 
# 随机初始化权重 
# w1是1000个100维,w1是1000x100矩阵 
# w2是100个10维,w2是100x10矩阵 
w1 = torch.randn(D_in, H) 
w2 = torch.randn(H, D_out) 
learning_rate = 1e-6 
 for t in range(500): 
     # 前向传播,h是64x1000x1000x100=64x100 
     # h_relu是看哪个激活了,还是64x100 
     # y_pred是64x100x100x10=64x10 
     # .dot改为mm 
     h = x.mm(w1) # N * H 
     # maximum改为clamp,夹住 
     h_relu = h.clamp(min=0) # N * H 
     y_pred = h_relu.mm(w2) # N * D_out 
      
     # compute loss 
     # 求平方在这里也要改 
     # loss = np.square(y_pred - y).sum() 
     loss = (y_pred - y).pow(2).sum().item() 
     print(it, loss) 
      
     # Backward pass 
     # 计算梯度,链式法则,挨个求导相乘 
     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和w2的权重 
     w1 -=learning_rate * grad_w1 
     w2 -=learning_rate * grad_w2  

4.3 简单的autograd

x = torch.tensor(1., requires_grad=True) 
w = torch.tensor(2., requires_grad=True) 
b = torch.tensor(3., requires_grad=True) 
y = w*x + b  # y = 2*1 + 3 
y.backward() 
# dy/dw=x 
print(w.grad) 
print(x.grad) 
print(b.grad) 
# 结果 
tensor(1.) 
tensor(2.) 
tensor(1 . ) 

如何使用autograd简化上面的两层神经网络?

# 定义一些基本的信息,64个输入,每个输入是1000维,hidden是100维,输出是10维 
N, D_in, H, D_out = 64, 1000, 100, 10 
# 随机创建一些训练数据 
# x是64个1000维,x是64x1000矩阵 
# y是64个10维,y是64x10矩阵 
# np.random要改为torch 
x = torch.randn(N, D_in) 
y = torch.randn(N, D_out) 
# 随机初始化权重 
# w1是1000个100维,w1是1000x100矩阵 
# w2是100个10维,w2是100x10矩阵 
w1 = torch.randn(D_in, H, requires_grad=True) 
w2 = torch.randn(H, D_out, requires_grad=True) 
learning_rate = 1e-6 
 for t in range(500): 
     # 前向传播,h是64x1000x1000x100=64x100 
     # h_relu是看哪个激活了,还是64x100 
     # y_pred是64x100x100x10=64x10 
     # .dot改为mm 
     # h = x.mm(w1) # N * H 
     # h_relu = h.clamp(min=0) # N * H 
     # y_pred = h_relu.mm(w2) # N * D_out 
     # 把上面三行合并为1行 
     y_pred =x.mm(w1).clamp(min=0).mm(w2) 
      
     # compute loss 
     # 求平方在这里也要改 
     loss = (y_pred - y).pow(2).sum()  # 这是计算图 
     print(it, loss.item()) 
      
     # Backward pass 
     # 计算梯度,链式法则,挨个求导相乘 
     # 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) 
     # 这些求梯度的全部不要 
     loss.backward() 
      
     # 更新w1和w2的权重 
     # 为了减少内存,取消计算图的储存 
     with torch.no_grad(): 
         w1 -=learning_rate * w1.grad 
         w2 -=learning_rate * w2.grad  
         # 每次清零 
         w1.grad.zero_() 
         w2.grad.zero_() 

最后pytorch的简单代码

# 定义一些基本的信息,64个输入,每个输入是1000维,hidden是100维,输出是10维 
N, D_in, H, D_out = 64, 1000, 100, 10 
x = torch.randn(N, D_in) 
y = torch.randn(N, D_out) 
w1 = torch.randn(D_in, H, requires_grad=True) 
w2 = torch.randn(H, D_out, requires_grad=True) 
learning_rate = 1e-6 
 for t in range(500): 
     y_pred =x.mm(w1).clamp(min=0).mm(w2) 
      
     loss = (y_pred - y).pow(2).sum()  # 这是计算图 
     print(it, loss.item()) 
      
     loss.backward() 
      
     # 更新w1和w2的权重 
     with torch.no_grad(): 
         w1 -=learning_rate * w1.grad 
         w2 -=learning_rate * w2.grad  
         # 每次清零 
         w1.grad.zero_() 
         w2.grad.zero_() 

五、Pytorch常用库

5.1 Pytorch:nn

使用pytorch:nn库来构建网络

使用Pytorch autograd 来构建计算图和计算梯度

import torch.nn as nn 
# 定义一些基本的信息,64个输入,每个输入是1000维,hidden是100维,输出是10维 
N, D_in, H, D_out = 64, 1000, 100, 10 
x = torch.randn(N, D_in) 
y = torch.randn(N, D_out) 
# 不用这么繁琐顶定义这个w1,w2 
# w1 = torch.randn(D_in, H, requires_grad=True) 
# w2 = torch.randn(H, D_out, requires_grad=True) 
model = torch.nn.Sequential( 
    torch.nn.Linear(D_in,H),  #w_1 * x +b_1 
    torch.nn.ReLU(); 
    torch.nn.Linear(H, D_out) 
) 
# model = model.cuda() 
loss_fn = nn.MSELoss(reduction='sum') 
learning_rate = 1e-6 
 for t in range(500): 
     # y_pred =x.mm(w1).clamp(min=0).mm(w2) 
     y_pred = model(x)  # model.forward()  
      
     loss = loss_fn(y_pred,y) 
     print(it, loss.item()) 
      
     model.zero_grad 
     loss.backward() 
      
     # 更新w1和w2的权重 
     with torch.no_grad(): 
         for param in model.parameters(): 
             param -= learning_rate * param.grad 

初始化数据,可能影响训练效果 ,这里可对weight进行初始化

model 
model[0].weight 
可能初始化问题会影响训练效果 
可在model和loss_fn中间加: 
torch.nn.init.normal_(model[0].weight) #第一层 
torch.nn.init.normal_(model[2].weight) #第二层 

5.2 Pytorch:optim

不用手动更新模型的weights,而是使用optim这个包来帮助我们更新参数

optim这个包提供了各种不同的模型优化方法,包括SGD+momentum,RMSProp,Adam等等

import torch.nn as nn 
# 定义一些基本的信息,64个输入,每个输入是1000维,hidden是100维,输出是10维 
N, D_in, H, D_out = 64, 1000, 100, 10 
x = torch.randn(N, D_in) 
y = torch.randn(N, D_out) 
# 不用这么繁琐顶定义这个w1,w2 
# w1 = torch.randn(D_in, H, requires_grad=True) 
# w2 = torch.randn(H, D_out, requires_grad=True) 
model = torch.nn.Sequential( 
    torch.nn.Linear(D_in,H),  #w_1 * x +b_1 
    torch.nn.ReLU(); 
    torch.nn.Linear(H, D_out) 
) 
# model = model.cuda() 
loss_fn = nn.MSELoss(reduction='sum') 
learning_rate = 1e-4 
optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate) 
# 或者优化方法 
# learning_rate = 1e-6 
# optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate) 
learning_rate = 1e-6 
 for t in range(500): 
     
     y_pred = model(x)  # model.forward()  
      
     loss = loss_fn(y_pred,y) 
     print(it, loss.item()) 
     # 求导之前把数据清空 
     optimizer.zero_grad() 
     loss.backward() 
      
     # 更新w1和w2的权重 
     optimizer.step() 

5.3 Pytorch:nn Module

import torch.nn as nn 
# 定义一些基本的信息,64个输入,每个输入是1000维,hidden是100维,输出是10维 
N, D_in, H, D_out = 64, 1000, 100, 10 
x = torch.randn(N, D_in) 
y = torch.randn(N, D_out) 
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, bias=False) 
        self.linear2 = torch.nn.Linear(H, D_out, bias=False) 
     
    def forward(self, x) 
         # 定义前向传播 
        y_pred = self.linear2(self.linear1(x).clamp(min=0)) 
        return y_pred 
         
model = TwoLayerNet(D_in, H, D_out)) 
loss_fn = nn.MSELoss(reduction='sum') 
learning_rate = 1e-4 
optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate) 
learning_rate = 1e-6 
 for t in range(500): 
     
     y_pred = model(x)  # model.forward()  
      
     loss = loss_fn(y_pred,y) 
     print(it, loss.item()) 
      
     # 求导之前把数据清空 
     optimizer.zero_grad() 
     loss.backward() 
      
     # 更新w1和w2的权重 
     optimizer.step()