写深度学习代码的一些心得体会
- 体会1
- 体会2
- 体会3
- 总结
- 内容来源
一般情况下,拿到一批数据之后,首先会根据任务先用领域内经典的Model作为baseline跑通,然后再在这个框架内加入自己设计的Model,微调代码以及修改一些超参数即可。总体流程参考如下:
- 先写dataset部分,包括数据的读取、预处理、增广等操作,将数据集准备好。
- 然后model部分baseline无需修改,proposed是自行设计,定义模型的结构和参数,建立模型架构。
- 最后是train部分,这里调用所有的类实现训练:包括定义模型,模型包裹;获取dataloader;定义loss,优化器,学习率,定义early stoping策略;保存模型权重,保存日志。
当然,文无定法。这个顺序并不是固定不变的,也可以根据具体情况作出相应的调整。例如,当你的数据集已经准备好了,可以直接开始定义模型,然后再定义训练过程;或者在进行模型训练之前,先进行数据集的分析和可视化等操作。
体会1
源自:作者三四但不犹豫
对于图像任务:
- 顺序上,先写dataset部分,检查基本的transform,再搭model,构建head和loss,就可以把一个基础的、可以跑的网络就能跑起来了(这点很重要);
- 可视化很重要,如果是本地开发机,善用
cv.imshow
直观、便捷地可视化处理的结果; - 一个基础的train/inference流程跑通后,分别构建1 张、10 张的数据用于debug,确保任意改动后,可以overfit;
- 调试代码阶段避免随机性、避免数据增强,一定用
tensorboard
之类的工具观察 loss 下降是否合理; - 一般数据集最好处理成coco的格式,我的任务跟传统任务不太一样,但也尽量仿照coco来设计,写dataset的时候可以参考开源实现;
- 善用开源框架,比如Open-MMLab,Detectron2之类的,好处是方便实验,在框架里写不容易出现难以察觉的bug,坏处是开源框架为了适配各种网络,代码复杂程度会高一点,建议从第一版入手了解框架,然后基于最新的一边阅读一边开发。
体会2
源自:捡到一束光
先给结论:以写了两三年pytorch代码的经验而言,比较好的顺序是先写model,再写dataset,最后写train。在讨论码组件的具体顺序前,先分析每一个组件背后的目的和逻辑。
model
构成了整个深度学习训练与推断系统骨架,也确定了整个AI模型的输入和输出格式。- 对于视觉任务,模型架构多为卷积神经网络或是最新的ViT模型;
- 对于NLP任务,模型架构多为Transformer以及Bert;
- 对于时间序列预测,模型架构多为RNN或LSTM。
不同的model对应了不同的数据输入格式,如ResNet一般是输入多通道二维矩阵,而ViT则需要输入带有位置信息的图像patchs。确定了用什么样的model后,数据的输入格式也就确定下来。根据确定的输入格式,我们才能构建对应的dataset。
-
dataset
构建了整个AI模型的输入与输出格式。- 在写作dataset组件时,我们需要考虑数据的存储位置与存储方式,如数据是否是分布式存储的,模型是否要在多机多卡的情况下运行,读写速度是否存在瓶颈,如果机械硬盘带来了读写瓶颈则需要将数据预加载进内存等。
- 在写dataset组件时,我们也要反向微调model组件。例如,确定了分布式训练的数据读写后,需要用
nn.DataParallel
或者nn.DistributedDataParallel
等模块包裹model,使模型能够在多机多卡上运行。 - 此外,dataset组件的写作也会影响训练策略,这也为构建train组件做了铺垫。比如根据显存大小,我们需要确定相应的
BatchSize
,而BatchSize则直接影响学习率的大小。再比如根据数据的分布情况,我们需要选择不同的采样策略进行Feature Balance,而这也会体现在训练策略中。
-
train
构建了模型的训练策略以及评估方法,它是最重要也是最复杂的组件。先构建model与dataset可以添加限制,减少train组件的复杂度。- 在train组件中,我们需要根据训练环境(单机多卡,多机多卡或是联邦学习)确定模型更新的策略,以及确定训练总时长epochs,优化器的类型,学习率的大小与衰减策略,参数的初始化方法,模型损失函数。
- 此外,为了对抗过拟合,提升泛化性,还需要引入合适的正则化方法,如Dropout,BatchNorm,L2-Regularization,Data Augmentation等。
- 有些提升泛化性能的方法可以直接在train组件中实现(如添加L2-Reg,Mixup),有些则需要添加进model中(如Dropout与BatchNorm),还有些需要添加进dataset中(如Data Augmentation)。。
此外,train还需要记录训练过程的一些重要信息,并将这些信息可视化出来,比如在每个epoch上记录训练集的平均损失以及测试集精度,并将这些信息写入tensorboard,然后在网页端实时监控。在构建train组件中,我们需要随时根据模型表现进行参数微调,并根据结果改进model和dataset两个组件。
体会3
源自:芙兰朵露
作为data driven的学科,不同的AI model适合不同的数据类型,选择用哪个模型是基于你的数据长什么样来决定的。初学者知道用CNN处理图片,用RNN处理时间序列/语言,但这些都是最基础的工作,真正体现水平的是根据数据的性质来选择合适的细分模型。比如稀疏图像需要用Sparse CNN,语言Transformer效果比较好,但对某些特殊的时间序列RNN也有奇效。
接下来还有很多技术细节,比如需不需要数据增强?需不需要标签平滑?需不需要残差链接?需不需要多loss,如果需要如何平衡?需不需要解释模型?我甚至没有提到超参数,因为超参数是锦上添花而不是雪中送炭。只要没有明确的信息瓶颈,超参数对模型的影响是很小的。
上面提到的这些问题不需要全想明白,但心里要大致有个谱,至少也要知道这些问题是可能影响你的训练结果的,这其实需要相当的阅读和积累。这样之后出了问题才知道去哪里debug。
然后就可以开始写了。这些问题想明白之后,其实先写哪个part已经不重要了,因为你的心中已经有了一个picture,先把这个picture给sketch下来,然后开始跑,第一遍效果肯定不好,但你要根据输出的结果大致判断哪个部分出了问题,然后针对性地去改进。这一步真的没什么好办法,很多时候其实是直觉,做多了自然就知道了。训练模型-发现问题-修改模型-再训练,就像炼丹一样,经过无数遍的抟炼,才能得到最后的金丹。
其实洋洋洒洒说了这么多,本质不过是几个字:解决问题的能力。making things to work几乎是机器学习中最重要的能力了,而这种能力就是在日常的积累和训练中反复磨练出来的,成功的路上没有捷径。
总结
单纯就个人习惯而言,先写model,确保model的结果没有错误,调试正确。然后写dataset,并调试输出正确。之后写损失函数,并调试正确。最后写train训练代码,推理代码。
内容来源
- 写深度学习代码是先写model还是dataset还是train呢,有个一般化的顺序吗?
- A Recipe for Training Neural Networks