Hugging Face: 构建神经网络的简单注意事项
构建和训练神经网络通常是一个令人沮丧的过程,性能提升可能会被误认为是 bug,而微小的实现错误可能会在不导致模型崩溃的情况下持续存在。为了缓解这些问题,Hugging Face 建议采用一种严谨的思维过程,优先考虑数据理解和简单的基准测试,而不是立即使用复杂的架构。
优先于模型选择进行数据分析
构建神经网络的第一步应该是放下机器学习,完全专注于数据。对数据集进行定性和定量的理解,可以让开发者识别出模型最终可以捕捉的高层模式。
数据分析的关键问题包括:
- 标签平衡: 各个类别之间的标签是否平衡?
- 数据完整性: 是否存在错误的或与事实不符的金标准标签(gold-labels)?
- 噪声来源: 数据是如何获取的,以及该过程中潜在的噪声来源是什么?
- 预处理: 哪些预处理步骤(例如,tokenization、移除 URL 或 hashtags)天然适合该数据?
- 多样性: 数据集中的样本有多大程度的多样性?
- 基于规则的潜力: 什么样的基于规则的算法在该问题上会有不错的表现?
建立简单的基准测试
在部署复杂模型之前,开发者应该实现简单的基准测试,以建立对任务难度的感知并提供一个比较基准。对于文本分类,这可能包括在 word2vec 或 fastText embeddings 上训练的逻辑回归。
为了合理地证明使用复杂模型的必要性,开发者应该回答以下问题:
- 随机预测: 随机预测器的表现如何,其损失函数(loss)看起来是什么样的?
- 指标选择: 衡量进展的最佳指标是什么,这些指标的局限性是什么?
- 差距分析: 简单方法中缺失了什么,导致它们无法达到完美分数?
- 归纳偏置: 神经网络工具箱中的哪些架构最适合模拟数据的归纳偏置(inductive bias)?
严谨的实现与调试
由于神经网络即使包含 bug,也往往可以训练并提供不错的性能,因此严谨的调试是至关重要的。一个主要的建议是在移除正则化的情况下,对一小批样本进行过拟合(例如,16 个样本)。如果模型无法在小批量样本上实现零损失,那么很可能存在实现错误或模型容量不足。
常见的实现错误
- 索引问题: 沿着错误的维度收集张量(tensors)。
- 状态管理: 在评估期间忘记调用
model.eval(),或者在 PyTorch 中忘记调用model.zero_grad()来清除梯度。 - 预处理: 输入预处理流水线中的错误。
- 损失函数参数: 在损失函数需要 logits 时传递了概率。
- 对称性破缺: 使用单一的常数值初始化权重矩阵,从而阻止了对称性破缺。
- 梯度流: 参数在正向传播过程中从未被调用,因此无法接收梯度。
- 学习率: 学习率始终为零或取了意想不到的值。
- Tokenization: 不理想的截断或 tokenizer 输出中的错误。
监控训练动态
开发者应该使用 Tensorboard 等工具来绘制损失、参数和梯度的演变过程。此外,在训练期间打印一些模型输出——例如翻译模型中生成的文本——可以为模型是否随着时间的推移变得越来越有说服力提供定性的见解。监控训练损失与评估损失之间的差距对于检测过拟合至关重要。
策略性超参数调优
超参数调优应该是一个有针对性的过程,而不是盲目的搜索。虽然与贝叶斯优化相比,随机网格搜索通常是一个难以被超越的基准,但目标应该是理解哪些超参数具有最高的影力。
不建议盲目启动数千次运行。如果一个模型需要极端的超参数值(例如,学习率 4e2)才能表现良好,这通常表明神经网络内部存在根本性问题,需要被识别和理解,而不是简单地进行调优。
最终,目标是倾向于对系统每个组件进行深入理解,而不是进行无法合理证明的魔法架构微调。