Pytorch学习(5) —— 简单模型构建,损失函数,训练方法等

前4个博客,已经介绍了什么是Tensor,构建模型所需的基本类有哪些,这些类有哪些工作。下面,结合这些内容,通过简单的例子进行使用说明。

1 构造模块化的神经网络

下面具体以一个由两个全连接层组成的感知机为例, 介绍如何使用nn.Module构造模块化的神经网络。

import torch
from torch import nn

# 实现了 y = x*w + b 的一个方法,wb是未知参数,x是输入,y是对应的输出
class Linear(nn.Module):
    # 输入维度,in_dim为x的维度,out_dim为y的维度
    def __init__(self, in_dim, out_dim): 
        super(Linear, self).__init__()
        # 定义参数wb,默认是需要求导的
        # 结合公式我们知道w是一个in_dim * out_dim的矩阵
        self.w = nn.Parameter(torch.randn(in_dim, out_dim))
        self.b = nn.Parameter(torch.randn(out_dim))

    # 输入x,计算y = wx + b
    def forward(self, x):
        x = x.matmul(self.w) # 计算wx
        y = x + self.b.expand_as(x) # 再加上b
        return y

class Perception(nn.Module):
    def __init__(self, in_dim, hid_dim, out_dim):
        super(Perception, self).__init__()
        # 这里定义了两层,注意第一层的输出维度要跟第二维的输入维度相同
        self.layer1 = Linear(in_dim, hid_dim)
        self.layer2 = Linear(hid_dim, out_dim)
    def forward(self, x):
        x = self.layer1(x)
        y = torch.sigmoid(x) # 激活函数
        y = self.layer2(y)
        y = torch.sigmoid(y)
        return y

2 损失函数

在深度学习中, 损失反映模型最后预测结果与实际真值之间的差距, 可以用来分析训练过程的好坏、 模型是否收敛等, 例如均方损失、 交叉熵损失等。 在PyTorch中, 损失函数可以看做是网络的某一层而放到模型定义中, 但在实际使用时更偏向于作为功能函数而放到前向传播过程中。

PyTorch在torch.nn及torch.nn.functional中都提供了各种损失函数, 通常来讲, 由于损失函数不含有可学习的参数, 因此这两者在功能上基本没有区别。

在上一行代码的基础上,我们补充下面代码计算损失,损失函数两种输出结果都是一样的。在使用pycharm开发时,输入nn.已经不会弹出functional,所以我觉得未来趋势,functional里面的函数将会被取消,所以安全起见,尽量使用nn自带的函数吧。

model = Perception(100, 1000, 10)
input = torch.randn(100) # 输入一个100维向量
label = torch.randn(10) # 设置输出标签
output = model(input)
# 使用nn.CrossEntropyLoss
criterion = nn.L1Loss()
loss_nn = criterion(output, label)
# 使用nn.functional.l1_loss
loss_functional = nn.functional.l1_loss(output, label)

下面给出nn里面包含的损失函数。

函数定义

torch.nn.L1Loss(size_average=None, reduce=None, reduction='mean')

L1Loss就是输入 x x x和目标 y y y的平均绝对值误差(mean absolute error,MAE)。在深度学习中,一般 x x x就是预测输出的一个向量, y y y是对应的标签,那么绝对值误差就是 ∣ x − y ∣ |x-y| xy,在深度学习中,batchsize一般不为1,定义 L = { l 1 , … , l N } T , l n = ∣ x n − y n ∣ L = \{l_1,\dots,l_N\}^T, l_n = \left| x_n - y_n \right| L={l1,,lN}T,ln=xnyn N N N为对应的batch的个数,那么对应的MAE如下所示,两种误差的区别就是是否求均值。

ℓ ( x , y ) = { m e a n ( L ) r e d u c t i o n = ′ m e a n ′ s u m ( L ) r e d u c t i o n = ′ s u m ′ } \ell(x, y) = \left\{\begin{array}{cc}mean(L) & reduction = 'mean' \\ sum(L) & reduction='sum'\end{array}\right\} (x,y)={mean(L)sum(L)reduction=meanreduction=sum}

如果想返回一个batch中每个输出的误差,设置reduction = 'none',那么返回的就是一个向量 L = { l 1 , … , l N } T , l n = ∣ x n − y n ∣ L = \{l_1,\dots,l_N\}^T, l_n = \left| x_n - y_n \right| L={l1,,lN}T,ln=xnyn

size_average和reduce官方已经不赞成使用了,选择输出误差的格式仅使用reduction配置即可(三个参数,mean,sum,none)

下面是这个函数的简单用法。

loss = nn.L1Loss()
input = torch.randn(3, 5)
target = torch.randn(3, 5)
output = loss(input, target)

3 训练方法

在上述介绍中, nn.Module模块提供了网络骨架,使用了nn.L1Loss计算了输出与标签的差异,也就是损失。这时还缺少一个如何进行模型优化、 加速收敛的模块,nn.optim就是干这事的。

nn.optim中包含了各种常见的优化算法, 包括随机梯度下降算法SGD(Stochastic Gradient Descent, 随机梯度下降) 、Adam(Adaptive Moment Estimation) 、 Adagrad、 RMSProp, 这里仅对常用的SGD与Adam两种算法进行介绍。

3.1 SGD方法

梯度下降(Gradient Descent) 是迭代法中的一种, 是指沿着梯度下降的方向求解极小值, 一般可用于求解最小二乘问题。 在深度学习中, 当前更常用的是SGD算法, 以一个小批次(Mini Batch) 的数据为单位, 计算一个批次的梯度, 然后反向传播优化, 并更新参数。

优点

  • 分担训练压力: 当前数据集通常数量较多, 尺度较大, 使用较大的数据同时训练显然不现实, SGD则提供了小批量训练并优化网络的方法, 有效分担了GPU等计算硬件的压力。
  • 加快收敛: 由于SGD一次只采用少量的数据, 这意味着会有更多次的梯度更新, 在某些数据集中,其收敛速度会更快。

缺点

  • 初始学习率难以确定: SGD算法依赖于一个较好的初始学习率, 但设置初始学习率并不直观, 并且对于不同的任务, 其初始值也不固定。
  • 容易陷入局部最优: SGD虽然采用了小步快走的思想, 但是容易陷入局部的最优解, 难以跳出。

有效解决局部最优的通常做法是增加动量momentum,SGD算法中可以通过设置momentum参数解决局部最优问题(这就是调参)

3.2 Adam方法

Adam利用了梯度的一阶矩与二阶矩动态地估计调整每一个参数的学习率, 是一种学习率自适应算法。Adam的优点在于, 经过调整后, 每一次迭代的学习率都在一个确定范围内, 使得参数更新更加平稳。此外, Adam算法可以使模型更快收敛, 尤其适用于一些深层网络, 或者神经网络较为复杂的场景。

3.3 简单用法

承接之前创建的模型,给出一个简单训练的示例

model = Perception(100, 1000, 10)
optimizer = torch.optim.SGD(params = model.parameters(), lr=0.01)
input = torch.randn(20, 100) # 输入一个10个100维向量
output = model(input) # 输出是一个20*10的矩阵
label = torch.randn(20, 10) # 给出一个对应维度的标签
# 计算loss
criterion = nn.L1Loss()
loss_l1 = criterion(output, label)
optimizer.zero_grad() # 清空梯度, 在每次优化前都需要进行此操作
loss_l1.backward() # 损失的反向传播
optimizer.step() # 利用优化器进行梯度更新

不同参数层分配不同的学习率: 优化器也可以很方便地实现将不同的网络层分配成不同的学习率, 即对于特殊的层单独赋予学习率, 其余的保持默认的整体学习率, 具体示例如下:

# 对于model中需要单独赋予学习率的层, 如special层, 则使用'lr'关键字单独赋予
optimizer = optim.SGD(
    [{'params': model.special.parameters(), 'lr': 0.001},
     {'params': model.base.parameters()}, lr=0.0001)

4 网络模型库 torchvision.models

对于深度学习, torchvision.models库提供了众多经典的网络结构与预训练模型, 例如VGG、 ResNet和Inception等, 利用这些模型可以快速搭建物体检测网络, 不需要逐层手动实现。

以VGG模型为例, 在torchvision.models中, VGG模型的特征层与分类层分别用vgg.features与vgg.classifier来表示, 每个部分是一个nn.Sequential结构, 可以方便地使用与修改。 下面简单进行使用说明。先导入包from torchvision import models

输入vgg = models.vgg16()即可快速的获得一个VGG16模型,VGG16的特征层包括13个卷积、 13个激活函数ReLU、 5个池化, 一共31层,输入print(len(vgg.features))即可发现返回是31。VGG16的分类层包括3个全连接、 2个ReLU、 2个Dropout, 一共7层,输入print(len(vgg.classifier))返回值为7。

模型返回类型也是个pytorch的Module类型,查看源代码发现vgg继承Module类,class VGG(nn.Module),所以在pytorch中直接使用即可。

vgg.features这是一个Sequence,可以通过矩阵角标访问网络结构,比如输入vgg.classifier[-1]直接返回最后一层信息。

Linear(in_features=4096, out_features=1000, bias=True)

5 模型加载与保存

5.1 模型加载

对于计算机视觉的任务, 包括物体检测, 我们通常很难拿到很大的数据集, 在这种情况下重新训练一个新的模型是比较复杂的, 并且不容易调整, 因此, Fine-tune(微调) 是一个常用的选择。 所Finetune是指利用别人在一些数据集上训练好的预训练模型, 在自己的数据集上训练自己的模型。

下面提供两种模型加载方法。

5.1.1 加载自带的预训练模型

直接利用torchvision.models中自带的预训练模型, 只需要在使用时赋予pretrained参数为True即可。

from torchvision import models
vgg = models.vgg16(pretrained=True)

5.1.2 加载本地的训练模型

如果想要使用自己的本地预训练模型, 或者之前训练过的模型, 则可以通过model.load_state_dict()函数操作, 具体如下:

from torchvision import models
vgg = models.vgg16()
state_dict = torch.load("训练模型路径")
vgg.load_state_dict({k:v for k, v in state_dict_items() if k in vgg.state_dict()})

通常来讲, 对于不同的检测任务, 卷积网络的前两三层的作用是非常类似的, 都是提取图像的边缘信息等, 因此为了保证模型训练中能够更加稳定, 一般会固定预训练网络的前两三个卷积层而不进行参数的学习。 例如VGG模型, 可以设置前三个卷积模组不进行参数学习, 设置方式如下:

for layer in range(10):
    for p in vgg[layer].parameters():
        p.requires_grad = False

5.2 模型保存

在PyTorch中, 参数的保存通过torch.save()函数实现, 可保存对象包括网络模型、 优化器等, 而这些对象的当前状态数据可以通过自身的state_dict()函数获取。

torch.save({'model': model.state_dict(), 'optimizer': optimizer.state_dict(), 'model_path.pth')

6 GPU加速

PyTorch为数据在GPU上运行提供了非常便利的操作。 首先可以使用torch.cuda.is_available()来判断当前环境下GPU是否可用, 其次是对于Tensor和模型, 可以直接调用cuda()方法将数据转移到GPU上运行, 并且可以输入数字来指定具体转移到哪块GPU上运行。

import torch
from torchvision import models
a = torch.randn(3,3)
b = models.vgg16()
# 判断当前GPU是否可用
if torch.cuda.is_available():
    a = a.cuda()
# 指定将b转移到编号为1的GPU上
b = b.cuda(1)
# 使用torch.device()来指定使用哪一个GPU
device = torch.device("cuda: 1")
c = torch.randn(3, 3, device = device, requires_grad = True)

在脚本中利用函数指定使用哪一块GPU

import torch
torch.cuda.set_device(1)

在工程应用中, 通常使用torch.nn.DataParallel(module,device_ids)函数来处理多GPU并行计算的问题。 示例如下:

model_gpu = nn.DataParallel(model, device_ids=[0,1])
output = model_gpu(input)

多GPU处理的实现方式是, 首先将模型加载到主GPU上, 然后复制模型到各个指定的GPU上, 将输入数据按batch的维度进行划分,分配到每个GPU上独立进行前向计算, 再将得到的损失求和并反向传播更新单个GPU上的参数, 最后将更新后的参数复制到各个GPU上。

总结

网络基本的模型构建,计算损失,训练方法都说明差不多了,后面可以开展关于网络层、损失函数等具体内容的细致分析,也会穿插介绍一些网络的构建方法。

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
目标检测是计算机视觉领域的重要任务,而Faster R-CNN作为目标检测领域的经典算法之一,具有较高的检测精度和鲁棒性。在睿智的目标检测系列,我们将使用PyTorch搭建一个基于Faster R-CNN的目标检测平台。 首先,我们将使用PyTorch构建一个深度学习模型的基础框架。PyTorch是一个开源的机器学习库,具有灵活的设计和易于使用的特点,因此非常适合用于搭建目标检测平台。我们将利用PyTorch提供的模块和工具,构建一个包含RPN模块、ROI pooling模块和分类回归模块的Faster R-CNN模型。 其次,我们将使用标记好的目标检测数据集,如COCO或PASCAL VOC数据集,对我们搭建的目标检测平台进行训练和验证。我们将利用PyTorch内置的数据加载和预处理工具,以及优化器和损失函数,对Faster R-CNN模型进行端到端的训练,以提高检测精度和泛化能力。 最后,我们将通过在真实场景下的目标检测任务的应用,来验证我们搭建的Faster R-CNN目标检测平台的性能。我们将评估其在目标定位、多目标检测、不同尺寸目标检测等方面的表现,并对可能的改进和优化进行进一步的研究和实验。 通过这样一个基于PyTorch搭建的Faster R-CNN目标检测平台,我们将能够深入理解目标检测算法的原理和实现细节,提高对深度学习框架的应用能力,并为未来的研究和应用奠定基础。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值