深度学习总结与回顾

深度学习笔记(十九)

Posted by Nino Lau on June 30, 2019

本周是深度学习实验课的最后一次课,让我们回顾一下在这个学期中大家都学习到了哪些内容。在前面的课程中我们首先了解了神经网络的结构,如何去搭建神经网络,怎样去训练神经网络,以及神经网络的优化、微调。紧接着我们又学习了深度学习领域当前比较流行的几个大方向,例如分类、语义分割、目标检测、对抗神经网络、自然语言处理等等。接下来的内容是对我们所学的知识的一个总结和升华。

1.神经网络的基础内容

1.框架

当前训练神经网络的框架有许多,例如tensorflow, caffe, pytorch等等,由于pytorch上手简单,使用方便,所以本门课程选择的框架是pytorch.

2.神经网络的基础单元

在pytorch框架中,神经网络的最基本的单元是张量(tensor), 由于框架本身已经实现了反向传播的机制,所以我们在搭建网络的过程中只需将大部分精力放在正向传播上,不太需要关心反向传播(梯度消失,梯度爆炸问题除外)。

3.如何去搭建一个简单的神经网络

import torch
import torch.nn as nn
import torch.optim as optim

class Net(nn.Module):
    def __init__(self, *args):
        self.layer1 = nn.Conv2d(3,64,kernel_size=3, stride=1,padding=0, bias=True)
        self.avg_pool = nn.AvgPool2d(4)
        self.layer2 = nn.Linear(64,10)
        
    
    def forward(self, x):
        out = self.layer1(x)
        out = out.view(x.size(0), -1)
        out = self.layer2(out)
        return out

4.如何去训练一个神经网络

LR = 0.01
BATCH_SIZE = 32
EPOCH = 10

net = Net()

optimizer = optim.SGD(net.parameters(), lr=LR, momentum=0.9, weight_decay=5e-4)
criterion = nn.CrossEntropyLoss()

for i in range(EPOCH):
    for data in enumerate(dataloader):
        x, y = data
        x, y = x.cuda(), y.cuda()
        # 模型预测结果
        y_hat = net(x)
        # 计算loss值
        loss = criterion(y, y_hat)
        # 清空优化器,必须在反向传播前进行
        optimizer.zero_grad()
        # 反向传播
        loss[0].backward()
        # 更新网络参数
        optimizer.step()
        
    '''
    这部分根据个人需求添加自己的代码,可以实现打印当前epoch的loss值,
    在训练集上的准确率,在验证集上的准确率,绘图等等操作
    '''

在以上训练过程中,值得大家注意的是:
建议大家同时打印出模型在训练集和测试集上的准确率,有些同学只关注在测试集上的准确率,忽略了在训练集上的准确率,导致可能出现一种情况,在测试集上的准确率不够理想,就怀疑是模型或者是其他什么地方出了问题。这个时候我们应该看看在训练集上的准确率如何,如果模型在训练集上表现的结果都不是很好,说明模型根本就没有训练好,又怎么能期望它能够在测试集上表现得好呢?这个时候我们首先应该想办法让模型在训练集的准确率达到99%以上(分类网络一般都可以达到近乎100%)。这是初学者很容易犯的一个低级错误,希望能够引起大家的重视。

5.模型训练的基本技巧

  • 设置随机种子,确保可以复现结果
  • k折交叉验证,验证模型的泛化能力
  • 数据预处理中的均值和方差的设置(目前有两种较常用的做法,一种是使用pretrain_model数据集的均值和方差,一种是使用自己数据集的均值和方差,哪种效果更好,实践出真知)
  • 数据增强,关于transforms里面众多函数的使用,也是根据数据集而定,建议大家多多尝试,
  • 模型的初始化,如果使用的模型提供了pretrain_model建议大家充分利用,可以加速收敛,如果想重新训练就要考虑torch.nn.init里面的众多初始化方法
  • 模型修改,根据自己的需求结合理论知识修改网络,这部分内容需要大家进行多次实验,找到适合自己数据集的较好的模型
  • 优化器,一般来说分类网络我们常用sgd和adam两种优化器,没有绝对的定论哪种更好。sgd的缺点是收敛可能要慢一些,如果调整得当可以得到比较好的结果,adam收敛速度快一些,但是结果可能略逊于sgd
  • batch_size,较大的batch_size才能充分发挥网络中bn层的作用,但是消耗的gpu资源也越多
  • learning_rate,学习率是网络训练过程中至关重要的参数,面对不同的batch_size,不同的优化方式,不同的数据集其最合适的值都是不确定的,我们无法仅凭经验来准确地得出lr值,能做的就是多做实验(可以参考的做法:观察训练过程中loss值和accuracy值,如果两者都上升到一定高度后趋于平缓,这个时候可以考虑调低lr,经过多次实验后,大致可得出lr在何时可能需要衰减)
  • lr与bs的关系,一般越大的bs使用越大的lr,因为越大的bs意味着我们学习的时候,收敛方向的confidence越大,我们前进的方向更加坚定,而较小的bs则显得比较杂乱,无规律性,因此bs较小的时候需要小的学习率保证不至于出错
  • 模型保存,模型的保存分为两种,一种是保存整个模型,另一种是保存模型的参数,一般建议保存模型的参数,因为模型的参数更便于我们灵活地去加载(比如之前提到的只加载部分模型参数),另外,模型的参数实际上是以字典的形式保存下来的。

以上内容基本上在14周课件中都有实现

6.训练的高级技巧

  • 过拟合,过拟合典型的表现为训练集损失远远小于验证集损失,而欠拟合则表现为训练集损失大于验证集损失,记住是远远大于,而不是说训练集损失稍微大于验证集损失就判断为过拟合。举个例子,如果遇到训练集损失为0.8,验证集损失为2.0,则可以判断为过拟合
  • dropout,dropout可以减轻过拟合现象,但请不要无脑使用,dropout一般适合全连接层部分,而卷积层由于参数不是很多,所以不太需要dropout,加上的话对模型的泛化能力没有太大影响。
  • 难例挖掘,在深度学习任务中,我们可能会遇到一些比较’棘手‘的数据,这些数据相比较于其他数据更难识别,他们称为hard-negative。我们先使用初始的正负样本训练分类器,然后再用训练出的分类器对样本进行分类,把其中负样本错误分类的那些样本(hard-negative)放入负样本集合,再继续训练分类器,如此反复,直到达到到停止条件(比如分类器性能不再提升)
  • 尝试过拟合一个小数据集,关闭正则化、随机失活、数据扩充,使用训练集的一小部分,让神经网络训练几个周期。确保出现零损失,如果没有,那么很可能什么地方出错了(经典的小trick)

一个很具体的问题

第12次作业,数据预处理和decoder输出内容转换成文本的过程
网络输出是词向量,通过查询字典(embedding)可以转换成对应的index(整数),再查询预先定义的index和词的对应关系,可以找到原始对应的单词