神经网络相关知识回顾(PyTorch篇)

一、常见概念

1、Batch(批量)
(1)如果是模型训练方法,batch指将所有数据处理完以后一次性更新权重或参数的估计;
(2)如果是模型训练中的数据,batch是一次输入供模型计算用的数据量。

基于批量的模型训练步骤:
a) 初始化参数
b) 重复以下步骤:处理所有数据,更新参数

与其对应的是递增算法,步骤如下:
a) 初始化参数
b) 重复以下步骤:处理一个或者一组数据点,更新参数

(BP算法中,“处理” 的具体操作是计算损失函数的梯度变化曲线。对于批量算法——计算平均或者总体损失函数的梯度变化曲线;而递增算法——计算损失函数仅在对应于该观测值或者数个观测值时的梯度变化曲线。“更新” 是从已有的参数值中减去梯度变化率和学习率的乘积。)

2、Online Learning和Offline Learning
(1)Offline Learning:所有数据都可以被反复获取,例如上面的批量算法。
优点:对于任何固定个数的参数,目标函数都可以直接被计算,因此很容易验证模型训练是否在朝着所需要的方向发展;计算精度可以达到任意合理的程度;可以使用各种不同的算法来避免出现局部最优的情况;可以采用训练、验证、测试三分法对模型的普适度进行验证;可以计算预测值机器置信区间。
(2)Online Learning:每个观测值在处理后会被遗弃,同时更新参数(递增算法的一种)。

3、偏移 / 阈值
采用激活函数的隐藏层或者输出层的神经元通常在计算网络输入时加入一个偏移值(Bias)。对于线性输出神经元,偏移项就是回归中的截距项。
每个隐藏层和输出层的神经元都有自己的偏移项b。但是如果输入数据已经被等比例转换到一个有限值域中,比如[0,1]区间,那么第一个隐藏层的神经元设置了偏移项以后,后面任何层跟这些具备偏移项的神经元有链接的其它神经元就不需要再额外设置偏移项。

4、标准化数据
常见的“标准化”数据处理方法:
(1)重放缩Rescaling:指将一个向量加上或者减去一个常量,再乘以或者除以一个常量。
(2)规范化Normalization:指将一个向量除以其范数(eg:欧式空间距离)。深度学习中,通常采用极差作为范数,即将向量减去最小值,并除以其极差,从而使数值范围在0~1。
常用的Normalization方法(BN、LN、IN、GN)详解见https://cloud.tencent.com/developer/article/1526775
(3)标准化(Standardization):指将一个向量移除其位置和规模的度量。比如:服从正态分布的向量,可以减去其均值,并除以其方差来标准化数据,从而获得一个标准正态分布的向量。

二、torch.nn

torch.nn包中的类涵盖深度神经网络模型在搭建和参数优化过程中的常用内容(卷积层、池化层、全连接层等构造方法,防止过拟合的参数归一化方法、Dropout方法,激活函数部分的线性激活函数、非线性激活函数相关方法…)

1、导包

import torch
# torch.autograd提供了类和函数用来对任意标量函数进行求导。
# 要想使用自动求导,只需要对已有的代码进行微小的改变。只需要将所有的tensor包含进Variable对象中即可
from torch.autograd import Variable

# 批量输入的数据量
batch_n = 100
# 通过隐藏层后输出的特征数
hidden_layer = 100
# 输入数据的特征个数
input_data = 1000
# 最后输出的分类结果数
output_data = 10

x = Variable(torch.randn(batch_n, input_data), requires_grad=False)
y = Variable(torch.randn(batch_n, output_data), requiers_grad=False)

'''
# 这里将权重参数代码删掉,因为之后使用的torch.nn包中的类能够帮助自动生成和初始化对应维度的权重参数
w1 = Variable(torch.randn(input_data, hidden_layer), requires_grad = True)
w2 = Variable(torch.randn(hidden_layer, output_data), requires_grad = True)
'''

2、搭建模型

# ********模型搭建********
model = torch.nn.Sequential(
    # 首先通过其完成从输入层到隐藏层的线性变换
    torch.nn.Linear(input_data, hidden_layer),
    # 经过激活函数
    torch.nn.ReLU(),
    # 最后完成从隐藏层到输出层的线性变换
    torch.nn.Linear(hidden_layer, output_data)
)
print(models)
  • torch.nn.Sequential是一种序列容器,在其中嵌套各种实现神经网络中具体功能相关的类来完成对神经网络模型的搭建。!参数会按照我们所定义的序列自动传递下去。
    我们可以将在容器中的各个部分看作各种不同的模块,模块可以自由组合。模块的加入方式一般有两种:
    (1)使用直接嵌套(即上面代码)
    模型结构打印输出:
Sequential(
  (0): Linear(in_features=1000, out_features=100, bias=True)
  (1): ReLU()
  (2): Linear(in_features=100, out_features=10, bias=True)
)

Process finished with exit code 0

默认使用从零开始的数字序列作为各个模块的名字。

(2)使用orderdict有序字典传入

import torch
from torch.autograd import Variable
from collections import OrderedDict

# 批量输入的数据量
batch_n = 100
# 通过隐藏层后输出的特征数
hidden_layer = 100
# 输入数据的特征个数
input_data = 1000
# 最后输出的分类结果数
output_data = 10

models = torch.nn.Sequential(OrderedDict([
    ("Line1", torch.nn.Linear(input_data, hidden_layer)),
    ("ReLU1", torch.nn.ReLU()),
    ("Line2", torch.nn.Linear(hidden_layer, output_data))
])
)
print(models)

模型结构打印输出:

Sequential(
  (Line1): Linear(in_features=1000, out_features=100, bias=True)
  (ReLU1): ReLU()
  (Line2): Linear(in_features=100, out_features=10, bias=True)
)

Process finished with exit code 0

每个模块分别是我们自定义的名字,比较清楚。

  • torch.nn.Linear类用于实现不同层之间的线性变换。接收三个参数(输入特征数,输出特征数,是否使用偏置)。
    实际使用中,只需将输入特征数和输出特征数传递给torch.nn.Linear类,就会自动生成对应维度的权重参数和偏置。

  • torch.nn.ReLU类属于非线性激活分类,在定义时默认不需要传入参数。还有很多可用(PReLU、LeakyReLU、Tanh、Sigmoid、Softmax…)

3、优化模型

# ********优化模型********
epoch_n = 10000
learning_rate = 1e-4
loss_fn = torch.nn.MSELoss()
  • torch.nn.MSELoss类(均方误差),在定义类对象时不用传入任何参数,但在使用实例是需要输入两个维度一样的参数方(x,y)进行计算:
loss_fn = torch.nn.MSELoss()
x = Variable(torch.randn(100,100))
y = Variable(torch.randn(100,100))
loss = loss_fn(x, y)
print(loss)

结果:

tensor(1.9493)

Process finished with exit code 0
  • torch.nn.L1Loss类(平均绝对误差),方法同上。
  • torch.nn.CrossEntropyLoss类(交叉熵),使用实例时需要输入两个满足交叉熵的计算条件的参数:
    分类交叉熵损失通常用于多类分类集。正确类的概率接近1,其他类的概率接近0
loss_fn = torch.nn.CrossEntropyLoss()
x = Variable(torch.randn(3, 5))
y = Variable(torch.LongTensor(3).random_(5))  # 3个范围为0~4的随机数字
loss = loss_fn(x, y)
print(loss)

结果:

tensor(1.5172)

Process finished with exit code 0

对于交叉熵具体原理,参考https://finisky.github.io/2020/07/09/crossentropyloss/

对建立的模型进行训练并进行参数优化:

# ********训练模型********
for epoch in range(epoch_n):
    y_pred = models(x)
    loss = loss_fn(y_pred, y)
    if epoch%1000 == 0:
        print("Epoch:{}, loss:{:.4f}".format(epoch, loss.item()))
    models.zero_grad()

    loss.backward()
    
    # 访问模型中的全部参数(对models.parameters()进行遍历)
    for param in models.parameters():
        param.data -= param.grad.data*learning_rate

三、torch.optim

之前代码中的神经网络权重的参数优化和更新没有实现自动化。在torch.optim包中提供了很多可实现参数自动优化的类(SGD、AdaGrad、RMSProp、Adam等)。

import torch
# torch.autograd提供了类和函数用来对任意标量函数进行求导。
# 要想使用自动求导,只需要对已有的代码进行微小的改变。只需要将所有的tensor包含进Variable对象中即可
from torch.autograd import Variable

# 批量输入的数据量
batch_n = 100
# 通过隐藏层后输出的特征数
hidden_layer = 100
# 输入数据的特征个数
input_data = 1000
# 最后输出的分类结果数
output_data = 10

x = Variable(torch.randn(batch_n, input_data), requires_grad=False)
y = Variable(torch.randn(batch_n, output_data), requires_grad=False)

'''
# 这里将权重参数代码删掉,因为之后使用的torch.nn包中的类能够帮助自动生成和初始化对应维度的权重参数
w1 = Variable(torch.randn(input_data, hidden_layer), requires_grad = True)
w2 = Variable(torch.randn(hidden_layer, output_data), requires_grad = True)
'''

# ********模型搭建********
models = torch.nn.Sequential(
    # 首先通过其完成从输入层到隐藏层的线性变换
    torch.nn.Linear(input_data, hidden_layer),
    # 经过激活函数
    torch.nn.ReLU(),
    # 最后完成从隐藏层到输出层的线性变换
    torch.nn.Linear(hidden_layer, output_data)
)
# print(models)

# ********优化模型********
epoch_n = 20
learning_rate = 1e-4
loss_fn = torch.nn.MSELoss()

optimzer = torch.optim.Adam(models.parameters(), lr=learning_rate)

# ********模型训练********
for epoch in range(epoch_n):
    y_pred = models(x)
    loss = loss_fn(y_pred, y)
    print("Epoch:{}, loss:{:.4f}".format(epoch, loss.item()))
    optimzer.zero_grad()  # 因为上面引入了优化算法,直接调用optimzer.zero_grad()实现模型参数梯度的归零

    loss.backward()

    # 使用计算得到的梯度值对各个节点的参数进行梯度更新
    optimzer.step()

打印结果:

Epoch:0, loss:1.1384
Epoch:1, loss:1.1160
Epoch:2, loss:1.0941
Epoch:3, loss:1.0727
Epoch:4, loss:1.0517
Epoch:5, loss:1.0311
Epoch:6, loss:1.0109
Epoch:7, loss:0.9913
Epoch:8, loss:0.9720
Epoch:9, loss:0.9532
Epoch:10, loss:0.9349
Epoch:11, loss:0.9171
Epoch:12, loss:0.8996
Epoch:13, loss:0.8827
Epoch:14, loss:0.8663
Epoch:15, loss:0.8503
Epoch:16, loss:0.8347
Epoch:17, loss:0.8194
Epoch:18, loss:0.8045
Epoch:19, loss:0.7900

Process finished with exit code 0

上面使用了Adam作为优化函数,在此类中输入的是被优化的参数和学习速率的初始值,如果没有输入学习率的初始值,默认0.001。!!Adam优化函数可以对梯度更新使用到的学习率进行自适应调节。

四、torch和torchvision

torchvision包的主要功能是实现数据的处理、导入和预览等:

import torch
import torchvision
from torchvision import datasets
from torchvision import transforms
from torch.autograd import Variable

1、torch.transforms
torch.transformers中提供了丰富的类对数据进行变换(eg:CV数据集是图片;NLP数据集是文本)。其中有很大一部分可以用作实现数据增强。

transform = transforms.Compose([transforms.ToTensor(),
                                transforms.Normalize(mean=[0.5, 0.5, 0.5],
                                                     std=[0.5, 0.5, 0.5])])

transforms.Compose类看作是一种容器,可以同时对多种数据变换进行组合,传入的参数是一个列表,列表中的元素是对载入数据进行的各种变换操作(上面代码只用了类型转换ToTensor和标准化变换Normalize)。
标准差变换法:
神经网络相关知识回顾(PyTorch篇)_第1张图片
经过此变换后,数据全部符合均值为0,标准差为1的标准正态分布。
其他的数据变换操作:

  • torchvision.transforms.Resize 按需求大小进行缩放,传递的参数可以是一个整型数据,或者是(h,w)这样的序列——(高度,宽度)。
  • torchvision.transforms.Scale 同上。
  • torchvision.transforms.CenterCrop 对载入的图片以图片中心为参考点,按所需大小进行裁剪。传递参数同上。
  • torchvision.transforms.RandomCrop 字面意思,随机裁剪。
  • torchvision.transforms.RandomHorizontalFlip 对图片按随机概率进行水平翻转,可以传递参数自定义随机概率,如果没有定义就默认0.5。
  • torchvision.transforms.RandomVerticalFlip 垂直翻转,参数同上。
  • torchvision.transforms.ToTensor 对图片数据进行类型转换——Tensor数据类型。
  • torchvision.transforms.ToPILImage 将Tensor变量的数据——PIL图片数据,显示图片内容。

2、torch.nn
神经网络的典型处理如下:
(1)定义可学习参数的网络结构(堆叠各层和层的设计);
(2)数据集输入;
(3)对输入进行处理(由定义的网络层进行处理),主要体现在网络的前向传播;
(4)计算loss,由loss层计算;
(5)反向传播求梯度;
(6)根据梯度改变参数值(SGD),最简单的实现方式为:
w = w - lr * gradient
CNN模型搭建:

# *******模型搭建和参数优化*******
class Model(torch.nn.Module):
    # 两个卷积层:一个池化层和两个全连接层
    def __init__(self):
        super(Model, self).__init__()
        self.conv1 = torch.nn.Sequential(
            torch.nn.Conv2d(1, 64, kernel_size=3, stride=1, padding=1),
            torch.nn.ReLU(),
            torch.nn.Conv2d(64, 128, kernel_size=3, stride=1, padding=1),
            torch.nn.ReLU(),
            torch.nn.MaxPool2d(stride=2, kernel_size=2)
        )

        self.dense = torch.nn.Sequential(
            torch.nn.Linear(14*14*128, 1024),
            torch.nn.ReLU(),
            torch.nn.Dropout(p=0.5),
            torch.nn.Linear(1024, 10)
        )

    def forward(self, x):
        x = self.conv1(x)
        x = x.view(-1, 14*14*128)
        x = self.demse(x)
        return x
  1. torch.nn.Conv2d 卷积层, 主要输入参数:输入通道数、输出通道数、卷积核大小、卷积核移动步长、padding值。
  2. torch.nn.MaxPool2d 最大池化层,主要参数:池化窗口大小、池化窗口移动步长大小、padding值。
  3. torch.nn.Dropout 防止卷积神经网络在训练过程中发生过拟合(工作原理:以一定的随机概率将卷积神经网络模型的部分参数归零,达到减少相邻两层神经连接的目的。)
  4. 前向传播forward函数
    使用流程:
    (1)调用module的call方法;
    (2)module的call里面调用module的forward方法;
    (3)forward里面如果调用Module的子类,回到第一步,如果碰到Function的子类,继续往下;
    (4)调用Function的call方法;
    (5)Function的call方法调用了Function的forward方法;
    (6)Function的forward返回值;
    (7)module的forward返回值;
    (8)在module的call进行forward_hook操作,然后返回值。

其他重要知识

准备数据—>选择模型框架,监督训练中还有两个重要部分:损失函数、优化算法

  1. 损失函数选择:
    对于模型输出为概率的情况,损失函数应该是基于交叉熵的损失;
  2. 优化算法选择:
    优化算法使用误差信号更新模型的权重。最简单的——超参数控制优化器,这个超参数是学习率(lr),在训练过程中,应该尝试几种不同的学习率并进行比较。
    方法:经典的随机梯度下降(SGD),但对于复杂的优化问题,存在收敛性问题。可替代方案是自适应优化算法(Adagrad或Adam),对于Adam,见上面的三。

监督循环训练:
是一个嵌套循环:在数据集或一组批次上的内部循环,以及在固定数量的周期其他终止条件上重复内部循环的外部循环。
经过许多批处理之后,训练循环就完成了一个周期(指一个完整的训练迭代)。模型是为一定数量的周期而训练的,要训练的epoch数量不是可以选择的,但是有一些方法可以决定何时停止。

核心思想:1、定义模型;2、计算输出;3、使用损失函数计算梯度;4、应用优化算法根据梯度更新模型参数。

分割数据集:
划分训练、验证、测试数据集 或 进行k折交叉验证 (较小数据集可)
确保这三个数据集保持相同的分布,应该采取一些预防措施:将按类标签设置的数据聚合起来,然后随机地将每个按类标签划分的数据集拆分为训练、验证和测试数据集。(常见:训练70%,验证15%,测试15%)。

何时停止训练:
“及时停止”的启发式方法。跟踪验证数据集上的性能,并注意何时不再提高性能。如果性能持续没有得到改善,训练终止。
训练结束之前的epoch的数量称为容忍性。

合适的超参数:
损失函数、优化算法、学习率、层的大小、及时停止的容忍性、各种规范化决策。

你可能感兴趣的:(深度学习,神经网络,pytorch,深度学习)