指南 2:使用 PyTorch 研究项目
根据我得到的一些反馈,我们将尝试总结有关如何在 PyTorch 中设置和构建大型研究项目的提示和技巧,例如您的硕士论文
如果你有好的想法,请随意贡献自己
设置
框架
选择正确的框架至关重要。 如果您采用标准的循环优化单个前向传递及其损失,请考虑使用 PyTorch Lightning。 它大大减少了代码开销,并允许在需要时轻松地将模型扩展到多个 GPU 和/或节点。 不过,如果您预计需要对默认训练过程进行相当大的更改,请考虑使用普通 PyTorch 并编写您自己的框架。 最初可能需要更多时间,但可以更轻松地在优化过程中进行编辑。
对于自己的框架,以下可用作示例设置:
general/
│ train.py
│ task.py
│ mutils.py
layers/
experiments/
│ task1/
│ train.py
│ task.py
│ eval.py
│ dataset.py
│ task2/
│ train.py
│ task.py
│ eval.py
│ dataset.py
general/train.py文件涵盖了每个模型需要的默认操作(训练循环、加载/保存模型、设置模型等)。 如果您使用 PyTorch Lightning,这会减少每个任务的训练代码文件,并且只需要设计训练对象。general/task.py文件涵盖了您必须为任务执行的特定部分(训练步骤、验证步骤等)的模板。 如果您使用 PyTorch Lightning,这将会在 Lightning 模块的定义。layers/models文件夹包含用于指定用于设置模型的nn.Modules的代码。experiments文件夹包含特定于任务的代码。 每个任务都有自己的 train.py 用于指定参数解析器、设置模型等,而 task.py 会覆盖 general/task.py 中的模板。eval.py文件应该有一个训练模型的检查点目录作为输入,并且应该在测试数据集上评估这个模型。 最后,文件“dataset.py”包含设置数据集所需的所有部分。请注意,此模板假定您可能有多个不同的任务和多个不同的模型。 如果您有更简单的设置,则可以固有地将模板缩小到一起。
参数解析器
通过参数解析器指定超参数是一种很好的做法。 参数解析器允许您调用类似
python train.py --learning ... --seed ... --hidden_size ...等的方式进行训练。如果您有多个模型可供选择,您将拥有多组超参数。 可以在 PyTorch Lightning 文档 中找到一个很好的总结,而无需使用 Lightning。 本质上,您可以为每个模型定义一个静态方法,该方法返回一个针对其特定超参数的解析器。 这使您的代码更清晰,更容易定义新任务,而无需复制整个参数解析器。
为确保可重复性(下面有更多详细信息),建议将参数保存为 json 文件或检查点文件夹中的类似文件。
超参数搜索
一般来说,超参数搜索都是靠经验。 一旦你训练了很多模型,你就会更容易选择合理的初始超参数。
第一种方法是查看与你的模型类似的工作,看看其他人使用了什么模型超参数。 这将帮助您获得一组合理的超参数。
超参数搜索可能很昂贵。 因此,在扩大范围之前,先尝试在浅层模型上进行搜索。
虽然从模型中获得最佳效果是大型网格搜索的最佳方式,但运行起来通常是不合理的。 通常情况会对超参数进行分组,并逐个优化。
工具包
PyTorch Lightning 提供了很多关于超参数搜索的有用技巧和工具包,例如:
学习率查找器 绘制了几个初始批次的学习率与损失的关系图,并帮助您选择合理的学习率。
Autoscaling batch sizes 找到给定 GPU 的最大可能的批处理大小(如果有帮助 你有非常深的大模型,很明显你需要尽可能大的批量)。
为了比较多个超参数配置,您可以将它们添加到 TensorBoard。 这是比较多次运行的一种简洁方法。 如果有兴趣,可以在 此处 找到关于此的博客。
有多个库支持您进行自动超参数搜索。 可以在 此处 找到 PyTorch 中的一个很好的概述。
可重复性
一切都与可重复性有关。 确保您可以使用相同的随机值、批次等重现您所做的任何训练。您会遇到这样一种情况,您已经尝试了很多不同的方法,但没有一种方法能够改进您之前的一次运行。 当您尝试使用最佳超参数再次运行模型时,您不希望有一个糟糕的惊喜(相信我,有足够多的人遇到这个问题,而且它也可能发生在您身上)。 因此,在开始任何网格搜索之前,请确保您能够重现运行。 使用相同的超参数、种子等在 Lisa 上并行运行两个作业,如果您没有得到完全相同的结果,请先停止并尝试修复它。
关于再现性的另一个事实是保存和加载模型没有任何问题。 在长时间训练之前,请确保您能够从磁盘加载保存的模型,并获得与训练期间完全相同的测试分数。
将您的超参数打印到 SLURM 输出文件中(python 中的简单打印语句)。 这将帮助您识别运行,并且您可以轻松检查 Lisa 是否执行了您打算执行的作业。 此外,超参数应存储在检查点目录中的单独文件中,无论是由 PyTorch Lightning 还是您自己保存。
运行作业时,自动将作业文件复制到您的检查点文件夹。 这通过确保您准备好准确的运行评论来提高可重复性。
除了 slurm 输出文件之外,创建一个输出文件,您可以在其中存储最佳训练、验证和测试分数。 当您想要快速比较多个模型或创建结果统计信息时,这会有所帮助。
如果您想安全起见并使用 git,您甚至可以打印/保存您当前所在的 git 提交的哈希值,以及您对文件所做的任何更改。 可以在 此处 找到如何执行此操作的示例。
种子
DL 模型本质上是随机嘈杂的,如果您不进行确定性执行,则没有两次运行是相同的。 在运行网格搜索之前,请尝试了解您的实验可能有多嘈杂。 与结果比例相比,您期望的噪声越多,您需要运行的模型版本越多,才能在设置之间获得统计上的显着差异。
完成网格搜索后,使用新种子运行另一个最佳配置模型。 如果分数仍然是最好的,就采用该模型。 如果没有,请考虑为网格搜索中的前 \(k\) 个模型运行更多种子。 否则,您可能会采用了次优模型,而该模型很幸运是特定种子的最佳模型。
学习率
学习率是一个重要参数,它取决于优化器、模型和许多其他超参数。
通常好的起点是 SGD 的 0.1 和 Adam 的 1e-3。
模型越深,学习率通常应该越低。 例如,Transformer 模型通常对 Adam 应用 1e-5 到 1e-4 的学习率。
你的 batch 越低,学习率应该越低。 如果批量大小太小,请考虑使用梯度累积(PyTorch Lightning 支持此功能,请参阅[此处]( https://pytorch-lightning.readthedocs.io/en/latest/training_tricks.html#accumulate-gradients))。
考虑使用 PyTorch Lightning 学习率查找器 工具包进行初始猜测。
LR调度器
与学习率类似,再次应用的调度器取决于分类器和模型。
对于图像分类器和 SGD 作为优化器,多步 LR 调度器已证明是不错的选择。
使用 Adam 训练的模型通常使用平滑的学习率指数衰减或类似余弦的调度程序。
对于变形金刚:记得使用学习率预热。 余弦调度器通常用于事后衰减学习率,但也可以用指数衰减代替。
正则化
如果您看到训练性能明显高于测试性能,则正则化在网络中很重要。
正则化参数相互影响,因此必须一起调整。 最常用的正则化技术是:
权重衰减
退出
增强
Dropout 通常是一个好主意,因为它适用于大多数架构,并已证明可以有效减少过度拟合。
如果您想在 Adam 中使用权重衰减,请记住使用
torch.optim.AdamW而不是torch.optim.Adam。
领域特定正则化
有几种正则化技术取决于您的输入数据/域。 最常见的包括:
计算机视觉:图像增强,如水平翻转、旋转、缩放和裁剪、颜色失真、高斯噪声等。
NLP:整个单词的输入丢失。
Graphs:丢弃边,节点,或者所有节点的部分特征。
使用 SLURM 进行网格搜索
作业数组允许您并行启动 N 个作业,每个作业的设置略有不同。
它实际上与创建 N 个作业文件并调用 N 次
sbatch ...相同,但这在某些时候可能会变得烦人且混乱。
PyTorch Lightning
编写任务数组会很繁琐,因此建议编写一个可以自动生成超参数文件的脚本,如果你必须经常这样做的话(例如,通过将种子参数添加到每个超参数配置中 4 次) . 但是,如果您使用的是 PyTorch Lightning,则可以直接创建作业数组文件。 可在此处 找到相关文档。