PyTorch-专知-链路化知识-1、一文带你入门优雅的 PyTorch

1、一文带你入门优雅的PyTorch

第一章、PyTorch基础

一、PyTorch介绍

1、说明

  • PyTorchTorchPython上的衍生(Torch是一个使用Lua语言的神经网络库)
  • tensorflow比较
    • PyTorch建立的神经网络是动态的
    • Tensorflow是建立静态图
    • Tensorflow高度工业化,它的底层代码很难看懂。
    • PyTorch好那么一点点,如果你深入API,你至少能比看Tensorflow多看懂一点点Pytorch的底层在干嘛。

建议:

  • 如果你是学生, 随便选一个学, 或者稍稍偏向 PyTorch, 因为写代码的时候应该更好理解. 懂了一个模块, 转换 Tensorflow 或者其他的模块都好说.
  • 如果是上班了, 跟着你公司来, 公司用什么, 你就用什么, 不要脱群. #### 2、安装PyTorch
  • 官网:http://pytorch.org/
  • 进入官网之后可以选择对应的安装选项
    • 目前只支持LinuxMacOS版本
    • 执行下面对应的安装命令即可 image
  • 安装PyTorch会安装两个模块
    • 一个是 torch, 一个 torchvision, torch主模块, 用来搭建神经网络的。
    • torchvision辅模块,有数据库,还有一些已经训练好的神经网络等着你直接用, 比如 (VGG, AlexNet, ResNet).

二、基础知识

1、和Numpy交互

(1)数据转换

import torch
import numpy as np

# 创建一个np array
a = np.array([[1,2], [3,4]])
b = torch.from_numpy(a)      # 根据np array创建torch 张量
c = b.numpy()                # 根据张量, 导出np array

(2)Torch中的运算

  • API
  • torchtensor 的运算和 numpy array运算很相似,比如
    • np.abs() --> torch.abs()
    • np.sin() --> torch.sin()等
  • 矩阵相乘:
    • data = [[1,2], [3,4]]
    • tensor = torch.FloatTensor(data) # 转换成32位浮点 tensor
    • torch.mm(tensor, tensor)

2、变量Variable

Variable的组成

(1)什么是Variable

  • Torch 中的 Variable由三部分组成,data部分是TorchTensor, grad部分是这个Variable的梯度缓存区, creator部分是这个Variable的创造节点. 如果用一个 Variable 进行计算, 那返回的也是一个同类型的Variable.

(2)使用

对于式子: y= wx+b

  • 导入包 import torch from torch.autograd import Variable # torch 中 Variable 模块
  • 定义tensor并放入Variable:

requires_grad 是参不参与误差反向传播, 要不要计算梯度

x = Variable(torch.Tensor([1]), requires_grad=True)
w = Variable(torch.Tensor([2]), requires_grad=True)
b = Variable(torch.Tensor([3]), requires_grad=True)

注意VariableTensor的区别:

Variable 计算时, 它在后台默默地搭建着一个庞大的系统, 叫做计算图, computational graph. 将所有的计算步骤 (节点) 都连接起来, 最后进行误差反向传递的时候, 一次性将所有 variable 里面的修改幅度 (梯度) 都计算出来, 而 tensor 只是一个数据结构.

  • 构建计算图
    y = w * x + b # y = 2 * x + 3

(3)计算梯度

# 对y求梯度
y.backward()

# 打印一下各个变量的梯度
print(x.grad)    # y对x的梯度: x.grad = 2 
print(w.grad)    # y对w的梯度: w.grad = 1 
print(b.grad)    # y对b的梯度: b.grad = 1

(4) Variable里面的数据

  • 直接print(variable)只会输出 Variable形式的数据,在很多时候是用不了的(比如想要用 plt 画图), 所以我们要转换一下, 将它变成 tensor形式.
  • 获取 tensor 数据:print(variable.data) # tensor 形式, 然后也可以转而numpy数据: print(variable.data.numpy()) # numpy 形式

复杂一点引入矩阵: 创建一个 $ y = w * x + b $

import torch 
from torch.autograd import Variable
import torch.nn as nn

# 创建张量(x, y)
x = Variable(torch.randn(5, 3))
y = Variable(torch.randn(5, 2))

# 构建一个线性层
linear = nn.Linear(3, 2)
# 打印一个w 和 b
print ('w: ', linear.weight)
print ('b: ', linear.bias)

# 选择损失函数
loss_func = nn.MSELoss()
# 选择优化方法, 设定学习率
optimizer = torch.optim.SGD(linear.parameters(), lr=0.01)

# 计算前向过程
pred = linear(x)

# 计算损失
loss = loss_func(pred, y)
print('loss: ', loss.data[0])

# 反向传播
loss.backward()

# 打印一下各个变量的梯度
print ('dL/dw: ', linear.weight.grad) 
print ('dL/db: ', linear.bias.grad)

# 优化一步
optimizer.step()

# 用优化了一步的参数,再算一遍前向过程,并计算损失
pred = linear(x)
loss = criterion(pred, y)
print('优化了一步之后, 现在的损失是: ', loss.data[0])

3、PyTorch中的激活函数


  • 导入包:import torch.nn.functional as F # 激活函数都在这
  • 平时要用到的就这几个.relu, sigmoid, tanh, softplus
  • 激活函数
    • 激活函数的输入输出都是variable
x = torch.linspace(-5, 5, 200)  # x data (tensor), shape=(100, 1)
x = Variable(x)
x_np = x.data.numpy()   # 换成 numpy array, 出图时用
y_relu = F.relu(x).data.numpy()
y_sigmoid = F.sigmoid(x).data.numpy()
y_tanh = F.tanh(x).data.numpy()
y_softplus = F.softplus(x).data.numpy()
# y_softmax = F.softmax(x)  

4、PyTorch中的数据加载器和batch

(1) 生成数据生成并构建Dataset子类

import torch
import torch.utils.data as Data

torch.manual_seed(1)    

BATCH_SIZE = 5

x = torch.linspace(1, 10, 10)       # 输入数据
y = torch.linspace(10, 1, 10)       # 输出数据

# 打包成TensorDataset对象,成为标准数据集
torch_dataset = Data.TensorDataset(data_tensor=x, target_tensor=y)

(2) 生成batch数据

PyTorch用类torch.utils.data.DataLoader加载数据,并对数据进行采样,生成batch迭代器。

# 创建数据加载器
loader = Data.DataLoader(
    dataset=torch_dataset,      # TensorDataset类型数据集
    batch_size=BATCH_SIZE,      # mini batch size
    shuffle=True,               # 设置随机洗牌
    num_workers=2,              # 加载数据的进程个数
)

for epoch in range(3):   # 训练3轮
    for step, (batch_x, batch_y) in enumerate(loader):  # 每一步
        # 在这里写训练代码...
        print('Epoch: ', epoch, '| Step: ', step, '| batch x: ', batch_x.numpy(), '| batch y: ', batch_y.numpy())

5、PyTorch中的完整案例和CUDA

  • PyTorch中使用GPU计算很简单,通过调用.cuda()方法,很容易实现GPU支持.
  • torch.cuda会记录当前选择的GPU,并且分配的所有CUDA张量将在上面创建.
  • 可以使用torch.cuda.device上下文管理器更改所选设备.

下面的例子是对上文中的数据进行训练的完整案例,支持GPU计算.

import torch
from torch.autograd import Variable
import torch.utils.data as Data

'''
超参
'''
torch.manual_seed(1)    

BATCH_SIZE = 5
LEARNING_RATE = 0.01


'''
准备数据
'''
train_data_x = torch.linspace(1, 10, 10)       # 输入数据
train_data_y = torch.linspace(10, 1, 10)       # 输出数据

# 打包成TensorDataset对象,成为标准数据集
torch_dataset = Data.TensorDataset(data_tensor=train_data_x, target_tensor=train_data_y)

# 创建数据加载器
loader = Data.DataLoader(
    dataset=torch_dataset,      # TensorDataset类型数据集
    batch_size=BATCH_SIZE,      # mini batch size
    shuffle=True,               # 设置随机洗牌
    num_workers=2,              # 加载数据的进程个数
)

'''
构建网络结构
'''
# 构建一个线性层
linear = nn.Linear(3, 2)
print ('w: ', linear.weight)
print ('b: ', linear.bias)

'''
如果支持GPU
'''
if torch.cuda.is_available():
    linear = linear.cuda()      # 将网络中的参数和缓存移到GPU显存中


# 选择损失函数
loss_func = nn.MSELoss()
# 选择优化方法, 设定学习率
optimizer = torch.optim.SGD(linear.parameters(), lr=LEARNING_RATE)

'''
训练
'''
for epoch in range(3):   # 训练3轮
    for step, (x, y) in enumerate(loader):  # 每一步

        # 如果支持GPU,将训练数据移植GPU显存
        if torch.cuda.is_available():
            x = Variable(x).cuda()
            y = Variable(y).cuda()

        output = linear(x)                  # 预测一下
        loss = loss_func(output, b_y)       # 计算损失
        optimizer.zero_grad()               # 清空上一步的梯度缓存
        loss.backward()                     # 计算新梯度, 反向传播
        optimizer.step()                    # 优化一步


        print('Epoch: ', epoch, '| Step: ', step, '| batch x: ', batch_x.numpy(), '| batch y: ', batch_y.numpy())

6、保存和加载模型

  • 保存和加载整个网络
# 保存和加载整个模型, 包括: 网络结构, 模型参数等
torch.save(resnet, 'model.pkl')
model = torch.load('model.pkl')
  • 保存和加载网络中的参数
torch.save(resnet.state_dict(), 'params.pkl')
resnet.load_state_dict(torch.load('params.pkl'))

7、加载预训练模型

import torchvision

# 下载并加载resnet.
resnet = torchvision.models.resnet18(pretrained=True)

# 如果你只想要finetune模型最顶层的参数
for param in resnet.parameters():
    # 将resent的参数设置成不更新
    param.requires_grad = False

# 把resnet的全连接层fc 替换成自己设置的线性层nn.Linear
# 比如说,输入维度是resnet.fc.in_features, 输出是100维
resnet.fc = nn.Linear(resnet.fc.in_features, 100) 

# 测试一下
images = Variable(torch.randn(10, 3, 256, 256))
outputs = resnet(images)
print (outputs.size())   # (10, 100)

8、简单回归

这一节我们来见证神经网络是如何通过简单的形式将一群数据用一条线条来表示. 或者说, 是如何在数据当中找到他们的关系, 然后用神经网络模型来建立一个可以代表他们关系的线条. image

  • 建立数据集

我们创建一些假数据来模拟真实的情况. 比如一个一元二次函数: y = a * x2 + b, 我们给 y 数据加上一点噪声来更加真实的展示它.

import torch
from torch.autograd import Variable
import matplotlib.pyplot as plt

torch.manual_seed(1)

x = torch.unsqueeze(torch.linspace(-1, 1, 100), dim=1)  # 张量 x: (100, 1)
y = x.pow(2) + 0.2*torch.rand(x.size())                 # 加入噪声的张量 y: (100, 1)

# 将张量转为 Variable
x, y = Variable(x), Variable(y)

# 画一下
plt.scatter(x.data.numpy(), y.data.numpy())
plt.show()
  • 建立神经网络

    class Net(torch.nn.Module):
    def __init__(self, n_feature, n_hidden, n_output):
        super(Net, self).__init__()
        self.hidden = torch.nn.Linear(n_feature, n_hidden)   # 隐层
        self.relu = torch.nn.ReLU()                          # 选择激活层
        self.predict = torch.nn.Linear(n_hidden, n_output)   # 输出层
    
    def forward(self, x):
        x = self.hidden(x)                                   # 计算隐层
        x = self.relu(x)                                     # 计算激活层
        x = self.predict(x)                                  # 输出层
        return x
    net = Net(n_feature=1, n_hidden=10, n_output=1)              # 定义网络
    print(net) 
                                                  #打印网络结构
    
  • 训练网络

    # 选择损失函数和优化方法
    loss_func = torch.nn.MSELoss()
    optimizer = torch.optim.SGD(net.parameters(), lr=0.5)
    plt.ion()   # hold住图
    for t in range(100):
    prediction = net(x)                                      # 用网络预测一下
    loss = loss_func(prediction, y)                          # 计算损失
    optimizer.zero_grad()                                    # 清除上一步的梯度
    loss.backward()                                          # 反向传播, 计算梯度
    optimizer.step()                                         # 优化一步
    if t % 5 == 0:
        plt.cla()
        plt.scatter(x.data.numpy(), y.data.numpy())
        plt.plot(x.data.numpy(), prediction.data.numpy(), 'r-', lw=5)
        plt.text(0.5, 0, 'Loss=%.4f' % loss.data[0], fontdict={'size': 20, 'color':  'red'})
        plt.pause(0.1)
    plt.ioff()
    plt.show()
    

9、快速构建序列网络

torch.nn.Sequential是一个Sequential容器,模块将按照构造函数中传递的顺序添加到模块中。另外,也可以传入一个有序模块。 为了更容易理解,官方给出了一些案例:

# Sequential使用实例

model = nn.Sequential(
          nn.Conv2d(1,20,5),
          nn.ReLU(),
          nn.Conv2d(20,64,5),
          nn.ReLU()
        )

# Sequential with OrderedDict使用实例
model = nn.Sequential(OrderedDict([
          ('conv1', nn.Conv2d(1,20,5)),
          ('relu1', nn.ReLU()),
          ('conv2', nn.Conv2d(20,64,5)),
          ('relu2', nn.ReLU())
        ]))

为了方便比较,我们先用普通方法搭建一个神经网络。

import torch


# 继承方式实现, 能够自定义forward
class ModuleNet(torch.nn.Module):
    def __init__(self, n_feature, n_hidden, n_output):
        super(ModuleNet, self).__init__()
        self.hidden = torch.nn.Linear(n_feature, n_hidden)   # 隐层
        self.relu = torch.nn.ReLU()                          # 选择激活层
        self.predict = torch.nn.Linear(n_hidden, n_output)   # 输出层

    def forward(self, x):
        x = self.hidden(x)                                   # 计算隐层
        x = self.relu(x)                                     # 计算激活层
        x = self.predict(x)                                  # 输出层
        return x

module_net = ModuleNet(1, 10, 1)
  • 上面ModuleNet继承了一个torch.nn.Module中的神经网络结构, 然后对其进行了修改;接下来我们来使用torch.nn.Sequential来快速搭建一个神经网络。
#用序列化工具, 给予Pytorch 内部集成的网络层 快速搭建
seq_net = torch.nn.Sequential(
    torch.nn.Linear(1, 10),
    torch.nn.ReLU(),
    torch.nn.Linear(10, 1)
)

我们来打印一下2个神经网络的数据,查看区别:

print(module_net)     # 打印网络结构
"""
ModuleNet (
  (hidden): Linear (1 -> 10)
  (relu): ReLU ()
  (predict): Linear (10 -> 1)
)
"""

print(seq_net)        # 打印网络结构
"""
Sequential (
  (0): Linear (1 -> 10)
  (1): ReLU ()
  (2): Linear (10 -> 1)
)
"""

reference:

完整系列搜索查看,请PC登录 www.zhuanzhi.ai, 搜索“PyTorch”即可得。

对PyTorch教程感兴趣的同学,欢迎进入我们的专知PyTorch主题群一起交流、学习、讨论,扫一扫如下群二维码即可进入(先加微信小助手weixinhao: Rancho_Fang,注明PyTorch)。

展开全文
相关主题
Top