深度学习_pytorch_郭郭专属

深度学习+pytorch

0、机器学习的步骤

  1. 数据预处理
    • 数据格式的统一、异常数据的消除和必要的数据变换
    • 划分训练集、验证集、测试集
      • 按比例随机选取
      • KFold方法
      • 使用sklearn带的test_train_split函数、kfold来实现
  2. 选择模型、设定损失函数和优化方法、超参数
  3. 用模型去拟合训练集数据,并在验证集/测试集上计算模型表现

1、深度学习的步骤

  1. 深度学习和机器学习在流程上类似,但在代码实现上有较大的差异
  2. 差异
    • 由于深度学习所需的样本量很大,一次加载全部数据运行可能会超出内存容量而无法实现
      • 批(batch)训练等提高模型表现的策略,需要每次训练读取固定数量的样本送入模型中训练
    • 模型实现:深度神经网络层数往往较多,同时会有一些用于实现特定功能的层(如卷积层、池化层、批正则化层、LSTM层等),因此深度神经网络往往需要“逐层”搭建,或者预先定义好可以实现特定功能的模块,再把这些模块组装起来。这种“定制化”的模型构建方式能够充分保证模型的灵活性,也对代码实现提出了新的要求。
    • 损失函数和优化器的设定: 由于模型设定的灵活性,因此损失函数和优化器要能够保证反向传播能够在用户自行定义的模型结构上实现。
  3. 深度学习中训练和验证过程最大的特点在于读入数据是按批的,每次读入一个批次的数据,放入GPU中训练,然后将损失函数反向传播回网络最前面的层,同时使用优化器调整网络参数。这里会涉及到各个模块配合的问题。训练/验证后还需要根据设定好的指标计算模型表现。

2、常用的包

  • pandas:表格信息的读入
  • cv2
  • matplotlib:可视化
  • seaborn:可视化
  • sklearn:下游分析和指标计算

3、GPU的设置

  • 我们的数据和模型如果没有经过显式指明设备,默认会存储在CPU上,为了加速模型的训练,我们需要显式调用GPU,一般情况下GPU的设置有两种常见的方式:
# 方案一:使用os.environ,这种情况如果使用GPU不需要设置
import os
os.environ['CUDA_VISIBLE_DEVICES'] = '0,1' # 指明调用的GPU为0,1号

# 方案二:使用“device”,后续对要使用GPU的变量用.to(device)即可
device = torch.device("cuda:1" if torch.cuda.is_available() else "cpu") # 指明调用的GPU为1号

4、数据读入

PyTorch数据读入是通过Dataset+DataLoader的方式完成的,Dataset定义好数据的格式和数据变换形式DataLoader用iterative的方式不断读入批次数据。

(1)、Dataset

我们可以定义自己的Dataset类来实现灵活的数据读取,定义的类需要继承PyTorch自身的Dataset类(torch.utils.data.Dataset)。
主要包含三个函数:

  • init: 用于向类中传入外部参数,同时定义样本集
  • getitem: 用于逐个读取样本集合中的元素,可以进行一定的变换,并将返回训练/验证所需的数据
  • len: 用于返回数据集的样本数

例子:简单的例子

import torch  
from torch.utils.data import Dataset  
  
class my_dataset(Dataset):  
   """  
  继承Dataset, 重载了__init__(), __getitem__(), __len__()  
  实现将一组Tensor数据对 封装成 Tensor数据集  
 能够通过index得到数据集的数据,能够通过len,得到数据集大小  
 """  def __init__(self, data_tensor, target_tensor):  
      self.data_tensor = data_tensor  
      self.target_tensor = target_tensor  
   """  
 __getitem__():实现索引数据集中的某一个数据。  
 我们知道,序列可以通过索引的方法获取序列中的任意元素,__getitem__()则实现了能够通过索引的方法获取对象中的任意元素。  
 此外,我们可以在__getitem__()中实现数据预处理。  
 """  def __getitem__(self, index):  
      return self.data_tensor[index], self.target_tensor[index]  
  
   def __len__(self):  
      return self.data_tensor.size(0) # torch.size(0)中的0表示第0维度的数据数量  
  """  
 a = torch.tensor([[1,2,3], [4,5,6]]) print(a.size(0))    # 2 第0维有[1,2,3]和[4,5,6]两个数据  
 print(a.size(1))    # 3 第1维有1,2,3(或4,5,6)三个数据  
 """  
# 随机生成数据  
data = torch.randn(4,3)  
data_target = torch.rand(4)  
"""  
|rand(size)|返回一个或一组服从“0~1”均匀分布的随机样本值。随机样本取值范围是[0,1),不包括1|t.rand(5)>>>tensor([0.0444, 0.5292, 0.8150, 0.6439, 0.5347])  
|randn(size)|返回一个或一组服从N(0,1)标准正态分布的随机样本值|t.rand(5)>>>tensor([-0.6638,  1.3208, -1.6684,  1.3031, -0.1184])  
"""  
  
# 将数据封装成Dataset  
my_dataset_1 = my_dataset(data, data_target)  
  
# 调用数据  
print(f'导出数据集中的第1行数据:{my_dataset_1[1]}')  
  
# 获取数据集大小  
print(f'数据集的大小是:{len(my_dataset_1)}')
导出数据集中的第1行数据:(tensor([ 1.2165, -1.7230,  0.2734]), tensor(0.1911))
数据集的大小是:4

(2)、DataLoader

  • Dataset 只负责抽象数据,并且一次调用 getitem 只返回一个样本
  • 在训练神经网络的时候,是对一个 batch 的数据进行操作,同时还需要对数据进行 shuffle 和并行加速等,为此,torch 提供了 DataLoader 去实现这些功能
  • DataLoader 的函数定义如下:
    DataLoader(dataset,
               batch_size=1,
               shuffle=False,
               sampler=None,
               num_workers=0,
               collate_fn=default_collate,
               pin_memory=False,
               drop_last=False)
    
    • dataset:加载的数据集(Dataset 对象)
    • batch_size:batch size(批大小)
    • shuffle:是否把数据打乱
    • sampler:样本抽样,后面会详细解释
    • num_workers:使用多进程加载的进程数,0 表示不使用多进程,Windows下该参数设置为0,Linux下常见的为4或者8,根据自己的电脑配置来设置
    • collate_fn:如何把多个数据拼接成一个 batch,一般使用默认的方式就可以了
    • pin_memory:是否将数据保存在 pin memory 区,pin memory 中的数据转到 GPU 中速度会快一些
    • drop_last:dataset 中的数据个数可能不是 batch_size 的整数倍,drop_last 为 True,会把多出来不足一个 Batch 的数据丢弃
  • 构建好Dataset后,就可以使用DataLoader来按批次读入数据了,实现代码如下:
    from torch.utils.data import DataLoader
    
    dataloader = DataLoader(dataset,
                            batch_size=3,
                            shuffle=True,
                            num_workers=0,
                            drop_last=False)
    
    dataiter = iter(dataloader)  # dataloader是一个可迭代对象,通过 iter 把 dataloader 变成一个迭代器
    imgs, labels = next(dataiter)
    imgs.size()  # batch_size,channel,height,weight
    
    • 迭代器:

    迭代器的两种使用方法
    第一种直接获取所有数据,数据量大不建议使用
    for batch_datas, batch_labels in dataloader:
    train()

    第二种只生成一个迭代器,用一个取一个数据
    dataiter = iter(dataloader)
    imgs, labels = next(dataiter)

5、PyTorch中神经网络的构造

  • PyTorch中神经网络构造一般是基于nn.Module类的模型。
  • 下面继承 Module 类构造多层感知机MLP
  • MLP 类重载了 Module 类的 init 函数forward 函数
    • 创建模型参数
    • 定义前向计算(正向传播)
  • 构造MLP类:
    import torch
    from torch import nn
    
    class MLP(nn.Module):
      # 声明带有模型参数的层,这里声明了两个全连接层
      def __init__(self, **kwargs):
        # 调用MLP父类Block的构造函数来进行必要的初始化。这样在构造实例时还可以指定其他函数
        super(MLP, self).__init__(**kwargs)
        self.hidden = nn.Linear(784, 256)
        self.act = nn.ReLU()
        self.output = nn.Linear(256,10)
        
       # 定义模型的前向计算,即如何根据输入x计算返回所需要的模型输出
      def forward(self, x):
        o = self.act(self.hidden(x))
        return self.output(o) 
    
  • MLP 类中⽆须定义反向传播函数。系统将通过⾃动求梯度⽽自动⽣成反向传播所需的 backward 函数。
  • 实例化MLP类
    X = torch.rand(2,784) # 设置一个随机的输入张量
    net = MLP() # 实例化模型
    print(net) # 打印模型
    net(X) # 前向计算,net(X) 会调用 MLP 继承⾃自 Module 类的 __call__ 函数,这个函数将调⽤用 MLP 类定义的forward 函数来完成前向计算。
    
    	MLP(
    	  (hidden): Linear(in_features=784, out_features=256, bias=True)
    	  (act): ReLU()
    	  (output): Linear(in_features=256, out_features=10, bias=True)
    	)
    	tensor([[ 0.0149, -0.2641, -0.0040,  0.0945, -0.1277, -0.0092,  0.0343,  0.0627,
    	         -0.1742,  0.1866],
    	        [ 0.0738, -0.1409,  0.0790,  0.0597, -0.1572,  0.0479, -0.0519,  0.0211,
    	         -0.1435,  0.1958]], grad_fn=<AddmmBackward>)
    

一些理论知识

训练集、验证集、测试集

  • train、validation、test
  • (1) 训练集相当于课后的练习题,用于日常的知识巩固。
    (2) 验证集相当于周考,用来纠正和强化学到的知识。
    (3) 测试集相当于期末考试,用来最终评估学习效果

  • 训练集在建模过程中会被大量经常使用,验证集用于对模型少量偶尔的调整,而测试集只作为最终模型的评价出现,因此训练集,验证集和测试集所需的数据量也是不一致的,在数据量不是特别大的情况下一般遵循6:2:2的划分比例
  • 为了使模型“训练”效果能合理泛化至“测试”效果,从而推广应用至现实世界中,因此一般要求训练集,验证集和测试集数据分布近似。但需要注意,三个数据集所用数据是不同的。
  • 训练集和测试集的区别
    • 使用验证集是为了快速调参,也就是用验证集选择超参数(网络层数,网络节点数,迭代次数,学习率这些)。另外用验证集还可以监控模型是否异常(过拟合啦什么的),然后决定是不是要提前停止训练。
    • 验证集的关键在于选择超参数,我们手动调参是为了让模型在验证集上的表现越来越好,如果把测试集作为验证集,调参去拟合测试集,就有点像作弊了。
    • 测试集不参与参数的学习过程,也不参与参数的选择过程,仅仅用于模型评价

Shuffle

是什么?

shuffle(中文意思:洗牌,混乱)。shuffle在机器学习与深度学习中代表的意思是,将训练模型的数据集进行打乱的操作
原始的数据,在样本均衡的情况下可能是按照某种顺序进行排列,如前半部分为某一类别的数据,后半部分为另一类别的数据。但经过打乱之后数据的排列就会拥有一定的随机性,在顺序读取的时候下一次得到的样本为任何一类型的数据的可能性相同。

为什么(要使用Shuffle)?

因为机器学习其假设和对数据的要求就是要满足独立同分布。所以任何样本的出现都需要满足“随机性”。所以在数据有较强的 “人为” 次序特征的情况下,Shuffle显得至关重要。
but: 模型本身就为序列模型,则数据集的次序特征为数据的主要特征,并且模型需要学到这种次序规律时,则不可以使用Shuffle。否则会将数据集中的特征破坏。
所以在序列预测的时候要把Shaffle设为None

Shuffle为什么重要?

  • Shuffle可以防止训练过程中的模型抖动,有利于模型的健壮性
    • 假设训练数据分为两类,在未经过Shuffle的训练时,首先模型的参数会去拟合第一类数据,当大量的连续数据(第一类)输入训练时,会造成模型在第一类数据上的过拟合。当第一类数据学习结束后模型又开始对大量的第二类数据进行学习,这样会使模型尽力去逼近第二类数据,造成新的过拟合现象。这样反复的训练模型会在两种过拟合之间徘徊,造成模型的抖动,也不利于模型的收敛和训练的快速收敛
  • Shuffle可以防止过拟合,并且使得模型学到更加正确的特征
    • NN网络的学习能力很强,如果数据未经过打乱,则模型反复依次序学习数据的特征,很快就会达到过拟合状态,并且有可能学会的只是数据的次序特征模型的缺乏泛化能力
    • 举个栗子 :如:100条数据中前50条为A类剩余50条为B类,模型在很短的学习过程中就学会了50位分界点,且前半部分为A后半部分为B。则并没有学会真正的类别特征。
  • 为使得训练集,验证集,测试集中数据分布类似
    • 可以混合所有类别为一个数据集进行shuffle,但是我觉得,单个标签下先分train_data和test_data,然后组合所有标签下的train_data再进行shuffle最为妥当

参考链接:shuffle

epoch、eposide、batch、batchsize、iterations

  • epoch:
    • one forward pass and one backward pass of all the training examples, in the neural network terminology
    • 深度学习的术语:所有的数据送入网络中, 完成了一次前向计算 + 反向传播的过程
    • 跑完整个训练数据集,叫做一个epoch
    • 举个栗子 :假设有6400个样本,在训练过程中,这6400个样本都跑完了才算一个epoch。一般实验需要训练很多个epoch,取平均值作为最后的结果,在一定程度上,相当于集成,避免局部极值。
  • episode
    • one a sequence of states, actions and rewards, which ends with terminal state.
    • 强化学习:agent与environment完成了一次完整的交互
    • 深度学习:在神经网络中很少见到,主要是在few-shot或者zero-shot这样的小样本学习中。一个episode,就是一次选择support set和query set类别的过程,即用选择的某几个类训练一次模型,下一个episode,再选择其他几个类训练模型。
    • 一个epoch中存在多个eposide。
  • batch
    • 当一个Epoch的样本(也就是所有的训练样本)数量可能太过庞大(对于计算机而言),就需要把它分成多个小块,也就是就是分成多个Batch 来进行训练。
  • batchsize
    • 每个batch 中: 训练样本的数量。
    • 举个栗子 : 之前的6400个样本,如果送一个样本进去,就更新一次网络的权重,也就是在线学习。与之对应的,我们可以把一个epoch的数据,分批送进网络,可以加快训练时间。每次送多少进去,就是batch_size,假设把6400个样本,分成50次送进去,那么每一次就要送128个样本进去,即batch_size=128。
  • iteration
    • 把一个epoch的所有数据分成了很多批,批数就是iteration
    • i t e r a t i o n s = 数据总数 b a t c h s i z e iterations = \frac{数据总数}{batchsize} iterations=batchsize数据总数

参考:1234

overfitting 过拟合

  • 只能拟合训练数据, 但不能很好地拟合不包含在训练数据中的其他数据的状态
  • 解决过拟合问题有两个方向:
    • 降低参数空间的维度或者降低每个维度上的有效规模(effective size)。降低每个参数维度的有效规模的方法主要是正则化,如权重衰变(weight decay)和早停法(early stopping)等。
    • 降低参数数量的方法包括greedy constructive learning剪枝和**权重共享(卷积)**等。

early stopping 早停止

  • callback: 指定在每个epoch开始和结束的时候进行哪种特定操作。Callbacks中有一些设置好的接口,可以直接使用,如’acc’, 'val_acc’, ’loss’ 和 ’val_loss’等等。

  • EarlyStopping是Callbacks的一种,EarlyStopping则是用于提前停止训练的callbacks。

  • 定义:当测试集上的loss不再减小(即减小的程度小于某个阈值)的时候停止继续训练,认为模型已经收敛,结束模型训练,保存现有模型的一种手段

  • 解决的问题:

    • 如果epoch数量太少,网络有可能发生欠拟合(即对于定型数据的学习不够充分)
    • 如果epoch数量太多,则有可能发生过拟合(即网络对定型数据中的“噪声”而非信号拟合)
    • 早停法旨在解决epoch数量需要手动设置的问题
  • 过程:

    • 开始时,将训练的数据分为训练集验证集
    • 每个epoch结束后(或每N个epoch后): 在验证集上获取测试结果,记录目前为止最好的验证集精度, 而随着epoch的增加,如果在验证集上发现测试误差上升,则停止训练;
    • 将之前处理测试集时准确率最高时的权重作为网络的最终参数。
      在这里插入图片描述
  • 存在疑问:(怎样才认为验证集精度不再提高了) 并不是说验证集精度一降下来便认为不再提高了,因为可能经过这个Epoch后,精度降低了,但是随后的Epoch又让精度又上去了,所以不能根据一两次的连续降低就判断不再提高。在现实中,模型在验证集上的误差不会像上图那样平滑,而是像下图一样:
    在这里插入图片描述

  • 也就是说,模型在验证集上的表现可能从短暂的变差之后继续变好.总之,我们是不能提前知道它的走势,就像预测股票走势一样, 那到底什么时候喊停,所以我们需要一些停止标准来帮助我们去寻找更好的训练时间和泛化错误之间的权衡.

  • patience:能够容忍多少个epoch内都没有改善。patience的大小和learning rate直接相关。在learning rate设定的情况下,前期先训练几次观察抖动的epoch number,patience设置的值应当稍大于epoch number。在learning rate变化的情况下,建议要略小于最大的抖动epoch number。

参考链接:111222333444

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

郭小儒

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值