【Pytorch-0】-结构性整理

本节从一个非常简单的小例子开始,说明如何搭建Pytorch的深度学习模型。本节从在Minist数据集上训练最简单的VAE实现开始,讲述如何搭建模型:

1. 模型训练

总的来说,整个训练流程需要以下几个步骤:

  1. 数据准备,超参数管理:数据准备如使用dataset管理,超参数可以通过argparse管理,也可以通过yaml管理,或者暴力输入,但是这不是一个很好的习惯。
  2. 数据预处理:如使用torchvision进行处理,但是实际可能复杂很多。
  3. 数据加载,获得迭代器:例如,Dataloader,DataIter
  4. 搭建模型组件:例如,torch.nn.module,例如线性层等等模块
  5. 设置优化工具:例如,torch.optim.Adam
  6. 设置损失函数:;例如,F.binary_cross_entropy
  7. 进行训练流程管理、训练:这一步其实非常繁琐,例如最原始的GAN,分别利用真实数据集和噪声生成数据训练。如果是复杂的半监督场景等等,那么其实就非常繁琐了。
  8. 进行模型效果测评:在验证集上进行指标的评价,注意,例如我们在翻译时训练的指标是交叉熵,但是评价确实bleu.
  9. 训练日志记录:如利用Tensorboard或者自带的Logger
  10. 模型保存:torch.save(model.state_dict(), path)

2.评价与推理

其实这一步和模型训练没有显著的区别,只不过我们需要考虑不同任务背景下,具体需要做的内容。例如,在生成任务,或者判别任务,有监督或者无监督场景,他们的具体情况都是不一样的。
其中,主要的流程归结如下:

  1. 数据准备:包括有噪声数据和无噪声数据;最主要的区别在于,输入的数据不包含答案
  2. 数据加载:也是获得相应的迭代器,或者是直接获取一个样本。
  3. 模型加载:需要加载模型,并加载已经训练好的参数:model.load_state_dict(torch.load(path))
  4. 模型推理:和训练流程一样,执行模型前馈过程,获得结果
  5. 模型评测:在测试集上进行评测;
  6. 模型效果展示:需要进行case study时,往往需要将结果变为我们能够读懂的形态。注意,在测评的时候,图片和文本往往都被向量化了,在这一步中,我们需要将结果给还原。

3. 项目情况

在这里插入图片描述
一般的,整个项目分为几个部分:

  1. preprocess文件夹:进行模型预处理的文件。注意,很多时候模型预处理和模型训练、推理是非常独立的。例如进行特征提取的过程,完全和模型训练没有关系!以及词表的建立、保存等等。
  2. data文件夹:储存项目中要用到的数据。
  3. utils文件夹:包含日志记录、模型加载等等小内容;以及dataset、dataloader的相关
  4. models文件夹:包含搭建模型的主要模块。
  5. logs文件夹:不同训练中的模型训练过程。
  6. weights文件夹:保存不同参数下的训练模型;特别注意,一次训练中,我们就可能产生很多的模型权重。
  7. img(case study)文件夹:对模型推理的一些结果,做例子结果的保存。
  8. iterManager文件:对整个训练流程的一个epoch内部地train、test、inference进行组织的文件;当然也可以放在utils中,看个人习惯。
  9. main函数文件,将参数配置、数据加载、训练、评价、推理、数据处理、结果处理处理停当地文件。对于训练、测试、推理可能各需要一个。
  10. readme文件,把项目说明仔细地文件。 如果在服务器上,那么往往还有一个把上面所有流程一步走完地shell文件。

如果我们对一个公用的数据集,使用很多模型评测,那么应该设立一个文件夹,其中包含dataset,其中与之并列的是各种model,把数据集放在model项目的子文件夹内是不合理的。每个文件夹下的data,放置各个模型自己独占的数据,比如词典映射表等等。

4. 样例解析

主要讲解训练和推理流程,采取自顶向下的思路:

4.1 main文件

首先是main文件,首先是数据准备,这里由于是minist数据集,所以利用pytorch的api下载,然后利用transformer函数进行处理。
在这里插入图片描述
然后是超参数的设置,其实GPU设置也可以放在这里的,因为之前处理数据不需要知道device。
在这里插入图片描述
然后准备数据迭代器,这里都是通过继承dataloader的方法获得dataiter。
在这里插入图片描述

模型加载。这里我们把model中的各个模块初始化,然后包装。如果是已经训练了一段时间的模型,可以直接加载,继续训练。此外,还需要把模型的优化器、损失函数、日志记录器全部设置停当。

在这里插入图片描述

训练开始,这里调用训练管理的train、test模块,只需要在外围记录好模型训练结果、保存模型就行,因此整体看起来非常的清楚明快。
在这里插入图片描述

4.2 train文件

实际上,很多模型的train模块是相当繁琐的,包括一些数据的采样步骤,正反例子数据的轮流训练等等。这里train函数,只管理一个epoch:
这里VAE部分其实还是比较简单的,其中主要分为如下几步:

  1. 初始化:设置模型为训练模型:model.train();损失函数初始化。
  2. 数据张量形式的变换;这个会在模型的forward之中,反复操作。。还是很繁琐的。
  3. 优化器清空梯度。optimizer.zero_grad()
  4. 模型推理,得到结果:model(x)
  5. 计算损失函数:注意,损失函数可能有很多项目,而且有比例设置,组成一个总体损失。
  6. 反向传播,构建梯度。loss.backward(),注意,loss必须是个标量!
  7. 梯度下降。只需要优化器执行optimizer.step()得到结果。
  8. 返回这个epoch的训练结果。

在这里插入图片描述

4.3 test文件

其实和train文件结构差不多,如下:

  1. 对数据进行初始化。切换模型为测试模式,model.eval(),其中有些模块的train和test行为不一样,例如BN和dropout()模块;设置损失函数为0。
  2. 用torch.no_grad()包装,不记录梯度
  3. 数据张量形式的变换;这个会在模型的forward之中,反复操作。。还是很繁琐的。
  4. 模型推理,得到结果:model(x)
  5. 计算损失函数:注意,损失函数可能有很多项目,而且有比例设置,组成一个总体损失。
  6. 返回损失函数结果。
    在这里插入图片描述
    主要区别:
  7. 不记录梯度;
  8. 不进行反向传播;
  9. 控制模型中一些模块的行为,他们在训练和推理时的行为是不一样的!

4.4 推理、case study

注意,进行case study的时候和测试也不完全一样!
因为模型作为一个整体,而我们实际上可能只需要其中的一部分发挥作用。例如在VAE或者CVAE中,我们并不需要encoder参与其中,我们只需要对隐空间向量进行采样,并输入给decoder解码,获得结果即可。如下:
在这里插入图片描述
大致步骤:

  1. 获得模型,初始化模型结构;
  2. 加载模型参数
  3. 进行数据初始化
  4. 只使用模型的相关部分,完成功能;
  5. 获取结果,并将结果转化为能够读懂的形式;
  6. 保存结果。

4.5 模型搭建

根据上述训练流程,我们清晰的看到,模型中的各个模块尽可能清楚的解耦,并按层次搭建明白,否则如果训练过程和推理、使用过程出现差异,我们无法将他们分开,就很尴尬了。例如VAE中其实有三个部分,其中一部分是encoder,另一部分是decoder,第三个部分是数据处理,即重参数部分。
如下所示,在前馈时结果,各个步骤都比较清晰:
在这里插入图片描述
因此,对于一个大模型,我们倾向于把它分解明白,而不是混杂在一起。其中encoder,decoder、重参数可以分别定义,然后再生成模型的时候逐层包装起来,如下所示,VAE外层,初始化的时候包括两个子部分。

在这里插入图片描述
当然,重参数部分比较简单,可以直接写在VAE内部,当然具体是写成一个函数,还是写成一个模块,就还是看情况而定。
在这里插入图片描述

5. 特别提醒

  • 关于数据和模型的处理:

如果我们对一个公用的数据集,使用很多模型评测,那么应该设立一个文件夹,其中包含dataset,dataset文件夹中包含有各个模型要共同使用的文件;其中与之并列的是各种model,把公用数据集放在model项目的子文件夹内是不合理的。

  • 关于日志记录和模型超参数:

实际上在调试超参数的时候,我们经常会调不好超参数;而且在做实验的时候,经常会有很多不同的想法,进而使得模型训练的结果、权重有很多。这时候,我们一定要按照良好的命名规范命名,否则一段时间以后,自己一定记不住当初到底干了什么,

  • 训练和测试的两个操作:

首先就是model.train()model.eval()的功能:控制特殊模块的工作模式
在PyTorch中进行validation时,会使用model.eval()切换到测试模式,在该模式下,主要用于通知dropout层和BN层(还有其他标准化方法)在train和val模式间切换。在train模式下,dropout网络层会按照设定的参数p设置保留激活单元的概率(保留概率=p); batchnorm层会继续计算数据的mean和var等参数并更新。在val模式下,dropout层会让所有的激活单元都通过,而batchnorm层会停止计算和更新mean和var,直接使用在训练阶段已经学出的mean和var值。该模式不会影响各层的gradient计算行为,即gradient计算和存储与training模式一样,只是不进行反传(backprobagation)。

其次是torch.with.no_grad(),是针对梯度图的构建问题:with torch.no_grad()主要是用于停止autograd模块的工作,以起到加速节省显存的作用,具体行为就是停止gradient计算,从而节省了GPU算力和显存,但是并不会影响特殊模块的行为。测试、推理时,我们显然都不需要这些操作,因此我们一定不要忘记,梯度图是非常占用显存的。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值