Autograd 虽然提供了自动微分,但是如果用 autograd 自行搭建神经网络,也是比较麻烦的;

 

本教程环境 pytorch 1.3以上

pytorch 提供了更高级的 API,即 torch.nn 模块,该模块构建于 Autograd 上,其中 nn.Module 是最重要的类,如下

class Module(object):
    r"""Base class for all neural network modules.
    Your models should also subclass this class.

        import torch.nn as nn
        import torch.nn.functional as F

        class Model(nn.Module):
            def __init__(self):
                super(Model, self).__init__()
                self.conv1 = nn.Conv2d(1, 20, 5)
                self.conv2 = nn.Conv2d(20, 20, 5)

            def forward(self, x):
                x = F.relu(self.conv1(x))
                return F.relu(self.conv2(x))
    """

    dump_patches = False
    _version = 1
    def __init__(self):
        torch._C._log_api_usage_once("python.nn_module")def forward(self, *input):
        raise NotImplementedError
    def register_buffer(self, name, tensor):def register_parameter(self, name, param):

从中可以看到构建网络的大致步骤

 

搭建网络流程 

在 PyTorch 中搭建神经网络的基本步骤如下:

1. 网络需要继承自 nn.Module

2. 在构造函数中声明可学习参数  

  // 不需要学习的参数可声明,可不声明

3. 重写 forward 方法,记录前向计算过程

  // 只要在 nn.Module 子类中构造了 forward 方法,backward 方法就会基于 Autograd 自动实现

 

以 LeNet 为例

代码如下

import torch as t
import torch.nn as nn
import torch.nn.functional as F


class Net(nn.Module):
    ### 必须1:构建网络需要继承 nn.Module
    def __init__(self):
        super(Net, self).__init__()

        ### 必须2:需要学习的参数必须放在 构造函数 中
        # 不需要学习的参数可放可不放
        ###输入 32x32 单通道
        self.conv1 = nn.Conv2d(1, 6, 5)         ###卷积层1: 1,输入通道数, 6,输出通道数,5,卷积核 size 5*5    输出 28x28 6通道
                                                ###后面接了2x2的池化,步长 2                                     输出 14x14 6通道
        self.conv2 = nn.Conv2d(6, 16, 5)        ###卷积层2:                                                    输出 10x10 16通道
                                                ###后面接了2x2的池化,步长 2                                     输出 5x5 16通道

        ### 全连接层  wx+b
        self.fc1 = nn.Linear(16*5*5, 120)       ###第一层全连接  120 自定义
        self.fc2 = nn.Linear(120, 84)           ### 第二层     84 自定义
        self.fc3 = nn.Linear(84, 10)            ### 第三层     10 个分类

    def forward(self, x):
        ### 必须3:构造 forward 前向计算函数
        ### 只要在 nn.Module 的子类中定义了 forward,backward 函数就会被自动实现(利用 Autograd)
        ### 卷积--激活--池化
        x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2)) ###池化层1:
        x = F.max_pool2d(F.relu(self.conv2(x)), 2)
        print(x.size()[0])      # 1
        x = x.view(x.size()[0], -1)     # -1 表示自适应,拉伸成 1 维
        x = F.relu(self.fc1(x))         # relu(wx+b)
        x = F.relu(self.fc2(x))
        x = self.fc3(x)
        return x

net = Net()
print(net)      ### 打印可学习参数名称列表
# Net(
#   (conv1): Conv2d(1, 6, kernel_size=(5, 5), stride=(1, 1))
#   (conv2): Conv2d(6, 16, kernel_size=(5, 5), stride=(1, 1))
#   (fc1): Linear(in_features=400, out_features=120, bias=True)
#   (fc2): Linear(in_features=120, out_features=84, bias=True)
#   (fc3): Linear(in_features=84, out_features=10, bias=True)
# )

 

网络的可学习参数通过 net.parameters() 获取,也可以通过 net.named_parameters 同时获取参数名称和参数值

params = list(net.parameters())     ### 可学习参数
# print(params)
print(net.named_parameters)         ### 可学习参数及其名称
for name, parameter in net.named_parameters():
    print(name, parameter.size())

 

API

nn 提供了构建神经网络的各种方法

nn. 的方法都是一个类

 

loss function

均方差 nn.MSELoss

用法大致如下,其他函数也类似

predict = net(input)
criterion = nn.MSELoss()
loss = criterion(predict, label)

交叉熵 nn.CrossEntropyLoss

 

优化器

torch.optim 中实现了大多数的优化方法,如 SGD,Adam,RMSProp

用法如下

from torch.optim import SGD
criterion = nn.CrossEntropyLoss()
optimizer = SGD(net.parameters(), lr=0.001, momentum=0.9)
for epoch in range(2):
    running_loss = 0.
    for i, data in enumerate(trainloader, 0):
        (inputs, labels) = data

        ### 梯度清零,与 net.zero_grad() 效果一样
        optimizer.zero_grad()
        outputs = net(inputs)
        loss = criterion(outputs, labels)   ### 计算损失
        loss.backward()         ### 反向传播
        optimizer.step()        ### 参数更新
        
        ### 打印 loss,可有可无
        running_loss += loss[0]
        if i % 2000 == 1999:
            print(epoch+1, i+1, running_loss/2000)
        running_loss = 0.

 

激活函数

ReLu:有两个 API

nn.ReLu:这是一个类,用法如下

import torch
import torch.nn as nn
m = nn.ReLU()
input = torch.randn(2)
output = m(input)
print(output)

relu:这是一个函数,用法如下

import torch.nn.functional as F
x = F.relu(input)

relu 的底层应该是 nn.ReLu

 

Sigmoid:也是两个 API

用法完全同 ReLu

 

其他激活函数同理

 

卷积与池化

nn.Conv2d :一个类

class Conv2d(_ConvNd):
    def __init__(self, in_channels, out_channels, kernel_size, stride=1,
                 padding=0, dilation=1, groups=1,
                 bias=True, padding_mode='zeros'):

 

nn.functional.max_pool2d:一个函数

def _max_pool2d(input, kernel_size, stride=None, padding=0, dilation=1,
                ceil_mode=False, return_indices=False)

nn.MaxPool2d:一个类

CLASS torch.nn.MaxPool2d(kernel_size, stride=None, padding=0, dilation=1, return_indices=False, ceil_mode=False)

 

其他

nn.Linear:线性函数,他是一个类

class Linear(Module):
    __constants__ = ['bias', 'in_features', 'out_features']

    def __init__(self, in_features, out_features, bias=True):

 

 

 

参考资料: