大模型训练的10个调试技巧

几年前,Andrej Karpathy 写了一篇关于训练神经网络的很棒的文章。以下是我在实施过程中遵循的一些额外事项,侧重于调试大型语言模型。

NSDT工具推荐: Three.js AI纹理开发包 - YOLO合成数据生成器 - GLTF/GLB在线编辑 - 3D模型格式在线转换 - 可编程3D场景编辑器 - REVIT导出3D模型插件 - 3D模型语义搜索引擎 - Three.js虚拟轴心开发包 - 3D模型在线减面 - STL模型在线切割 

1、记录任何内容

尽可能广泛地预先设置日志记录。我使用 wandb 进行实验报告。我发现它是目前市场上最好的实验跟踪选择,个人使用量几乎不受限制。

权重:记录训练过程的张量,尤其是层权重的更新。注意趋于零并停留在那里的梯度。有时这只是当前损失景观的副产品,但它通常表明网络已经饱和了它实际上可以进行的学习。

具体来说,

  • 记录每层的梯度幅度
  • 记录每层的梯度分布
  • 记录权重矩阵范数
  • 记录权重矩阵分布

这两个分布日志都是通过 wandb 的内置监视实用程序处理的。

wandb.watch(model, log="all", log_freq=10)

通过迭代当前模块参数,可以轻松完成矩阵范数。两者为你提供了对同一数据的略有不同的视图。

数据点:数据集示例随着时间的推移具有黄金价值。如果损失计算中存在错误,损失指标可能会成功下降,即使网络没有学到任何有价值的东西。错误更难隐藏在人类可理解的数据点中。

  • 在固定的批次设置中记录训练集和推理集示例以及每个示例的预测(可能根据数据集大小以 100 或 1000 为模)。记录多媒体,以便可视化图像、视频或解码的文本。
  • 在任何有可以转换为标记索引的 logit 的地方使用 tokenizer.decode()。这通常是在输入(预嵌入)和输出(后线性投影到词汇空间)处,但也可以是额外的填充生成或掩码等地方。
  • 对于序列或标记嵌入,直接记录它们的投影。

最终层 logits:在单标签、多类别问题中,你可能会使用 softmax 作为损失函数的一部分。由于 softmax 不是硬最大化算法,因此你可以鼓励模型在正确的类别上创建权重分布。如果你在图表上记录最终层 logits,你应该注意到大多数概率质量会随着时间的推移收敛到正确的值(尤其是在过度拟合运行期间)。在过度拟合过程中,你走得越远,这个最大值点应该越明显。但权重应该有一个明显的转变,从均匀分散到接近正确值。

这为过度拟合的进展提供了额外的健全性检查。它确保模型以你期望的方式在统计上发展,并且软最大化值平稳增加。通常,查看随时间的变化是分析训练的一种有用方法。

如有疑问,请始终记录。

2、从简单的架构替代开始

大多数核心 ML 活动都有通用的抽象层。Transformers 可以用 RNN 代替,Resnet 可以代替 CNN。这些更简单的方法无法达到你想要的精度,但可能能够证明整体线束的梯度流是否存在问题。它们的训练速度也更快,如果你尝试对新的过度拟合管道进行快速健全性检查,这将非常有用。

我还注意到,较新的笔记本电脑在矩阵乘法方面变得出奇地快。当然还不足以训练整个网络,但我现在经常发现自己在本地进行初始原型设计。这项工作的重点是简单架构上的过度拟合、数据的健全性检查以及确保矢量化在逻辑上正确。

3、使随机性可重现

现代模型中内置了大量随机性。

  • 数据增强
  • 掩蔽语言建模
  • Dropout 和正则化

正如预期的那样,这些技术有助于通过使网络难以过度拟合来推广模型。但在过度拟合期间,你确实希望它们过度拟合,理想情况下是积极地记住输入以测试模型容量和训练工具。如果输入、输出或损失具有随机性,则很难确定过度拟合期间是否存在问题。

我过去常常在过度拟合时手动禁用模型的所有随机元素:将 dropout 设置为零,禁用数据增强等。这种方法的缺点是有很多 if elif 语句,而且不一定能捕获导入的模块是否在其实现中嵌入了一些随机性。我没有采用这种方法,而是在每个训练和验证步骤中开始用固定种子为模型播种。在 Pytorch-Lightning 中,这看起来像:

CONSTANT_SEED = 60

class MySmartModule:
    def training_step(self, batch):
        if self.trainer.overfit_batches:
            print("Will reset seed for reproducable overfitting")
            pl.seed_everything(CONSTANT_SEED)

    def validation_step(self, batch):
        if self.trainer.overfit_batches:
            print("Will reset seed for reproducable overfitting")
            pl.seed_everything(CONSTANT_SEED)

这不会直接消除随机性,但它应该使随机值在每个训练和验证步骤中保持一致,这实际上是同一件事。这应该允许模型过度拟合以及零随机性实现。通过日志进行双重检查以确认输入值确实相等。

4、过度拟合1,然后 2,然后 5

任何足够大的网络都应该能够在少数数据点上达到 0 损失。我通常从一个示例(1 个批次,批次大小 1)开始。这应该是可以轻易学习的,因为甚至不需要创建判别输出空间。如果成功,则扩展到 2 个不同的示例,然后扩展到 5 个不同的示例。

5、将每个自定义矢量化编写两次

这听起来有点矫枉过正,但它省去了很多麻烦。每当我做前馈传递值以外的任何事情时,我都会将张量转换重构为单独的函数。然后,我使用标准 for 循环和基于单个索引的张量重写此逻辑。然后运行几个示例并确保它们的值匹配。这是验证矢量广播和其他并行操作是否按预期工作的最简单方法。

具体来说,我将两个实现都包装在描述转换的类中。假设我们要编写一个掩盖特定值颜色的函数。我从 for 循环实现开始,逐个索引地进行。调用矢量化管道的尝试失败了。原始类结构如下所示:

class ColorMasking:
    def __init__(self, vectorize):
        self.vectorize = vectorize

    def __call__(self, *args, **kwargs):
        if self.vectorize:
            return self.vectorized(*args, **kwargs)
        else:
            logging.warning("Using greedy implementation of ColorMasking")
            return self.greedy(*args, **kwargs)

    def greedy(self, img):
        for y in range(img.shape[0]):
            for x in range(img.shape[1]):
                ...

    def vectorized(self, img):
        raise NotImplementedError()

这个类可让你轻松地在显式(速度慢但更可能正确)和矢量化(速度快但更可能引入错误)之间切换。它还内置了一个可单元测试的代码块,可以更轻松地检查一段时间内的实现问题。然后,你可以在神经网络模块内选择是否要全面切换到矢量化,或者使用手动矢量化对几个时期进行健全性检查。

class MySmartModule(torch.nn.Module):
    def __init__(self):
        self.vectorize = False

    def forward(img):
        mask = ColorMasking(vectorize=self.vectorize)(img)

这也对实现过程进行了补充,到目前为止您可能只有一个明确的实现:

class MySmartModule(torch.nn.Module):
    def __init__(self):
        self.vectorize = False

    def forward(img):
        mask = ShapeMask(vectorize=self.vectorize)
        mask = ColorMasking(vectorize=False)(img)

有时我会直接用这个非向量化函数运行过度拟合作业,以检查它是否按照我的意愿运行。有时由于速度限制,我会直接编写向量化逻辑。

6、单元测试辅助函数

为向量化、数据加载器和训练管道添加重型单元测试组。

向量化:作为上一节的延续,验证向量化代码是否正常工作。通过几个手写示例定义预期的转换。尝试使用不同的张量大小并记录预期权重或一些预期的转换。

数据加载器:这也适用于数据加载器。如果可能,通过反向转换来保证转换符合预期。获取文本 logits 的 argmax 并检索文本,将图像像素转换为可以与静态工件进行比较的实际 PIL,等等。

训练管道:额外的集成测试可以验证训练管道的某些行为。最大的问题之一通常是梯度流 - 无法正确传播到网络中较早的张量的损失。在最好的情况下,你错过了可验证的学习 - 在最坏的情况下,较早的层将保持随机初始化,而网络的其余部分将猜测随机输入噪声。一种解决方法是进行测试,该测试传递一些合成数据并跨过梯度权重并断言每个范数都非零。网络的每一层都应该有一些学习。

我的训练管道倾向于通过 CLI 训练可执行文件启动。为了确保单元测试在每次训练运行中都令人满意,我在初始化线束之前向此实现添加了 pytest 运行命令。

@click.command()
def train():
    pytest.main()

    # Training block

7、避免使用全局变量

全局变量在常规软件工程中通常是一种不好的形式,在机器学习中也同样糟糕。Jupyter 非常适合原型设计,但当事物被定义为常规单元时,很容易出现错误。即使你将一些逻辑重构为函数,它们仍可能在全局状态下获取变量。

作为一般工作流程,我完全在全局空间中制作原型。传递变量并确保张量大小正确更容易。在这里我通常只处理一个批次。

在开始完整的训练运行之前,我会将所有单元重构为单独的函数。这确保没有全局变量泄漏。它之前已经捕获了一些微不足道的错误,即同一个值被无意中重复使用多次,而不是在更大的列表中进行迭代。

8、确保(静态)批次随时间保持不变

添加自定义数据集、自定义加载器和自定义整理函数时,批次可能会随时间发生细微偏差。当现场操作字典或聚合某些值时,这种情况尤其容易发生。我使用此代码片段来检查随时间推移的相等性。

毋庸置疑,如果你在数据加载器类中引入随机增强,这将不起作用。对于这些情况,我会暂时禁用转换,然后运行此验证。你还可以有选择地将应随时间保持不变的键列入白名单,同时允许随机转换中的键发生变化。

first_sample = next(iter(train_loader))
second_sample = next(iter(train_loader))

print("Will check equality...")
for key in first_sample.keys():
    first_value = first_sample[key]
    second_value = second_sample[key]

    if isinstance(first_value, torch.Tensor):
        if not torch.equal(first_value, second_value):
            print(first_value)
            print(second_value)
            raise ValueError(f"Unequal iterations: {key} (torch tensor)")
    else:
        if first_value != second_value:
            print(first_value)
            print(second_value)
            raise ValueError(f"Unequal iterations: {key}")
print("Success...")

9、合成生成不同大小的数据

在较大的网络中,尤其是通过时间反向传播,梯度可能会在网络中较早消失。我发现一种调试这些问题的有用方法是合成生成不同大小的新数据点。

如果你的数据加载器最终接受磁盘上的文件,那么这是一种自然选择,这在我最终构建的大多数大型架构中都很常见。编写一个函数,以正确的格式将新数据集转储到磁盘。输出值在这里并不重要,因为网络应该只记住过度拟合期间的原始值。

tokenizer = Tokenizer()
labels = ["A", "B", "C"]

@contextmanager
def create_synthetic_datapoint(text_length):
    random.sample(tokenizer.vocab, text_length)
    random.choice(labels)

    with tempfile.TemporaryDirectory() as directory:
        yield directory

with create_synthetic_datapoint(50) as path:
    train_dataset = MyDataset([path])

    trainer.overfit(model, train_dataset)

10、尽可能使用 einops

每当需要张量变换(查看、转置、堆叠等)时,我都会尝试将其放入 einop 中。它们通过引用字符串值来表示轴的含义,从而使这些操作更具描述性。它们还可以假设一些维度,否则你可能需要 .shape 算法。我尝试在这些字符串中使用完整的单词或变量名称,除非某些东西很明显,例如 b 表示批处理。

x = rearrange(x, "b height width embedding -> b (height width) embedding")

我发现,当我离开某个功能几天后,这些 einops 使调试变得容易得多。

11、结束语

得益于出色的开源项目和与出版物一起发布代码的日益增长的趋势,成功训练的道路变得越来越容易。但是,当尝试一些新颖的东西(无论是在数据集上还是使用新的模型架构)时,成功之路仍然曲折。一个字符的索引错误可能会导致结果从 SOTA 变为勉强超过基线。

我有一位老同事说“软件中一切皆有可能,你只需要花足够的时间来构建它。” ML 的挑战在于有些事情是不可能的——至少在目前数据和架构的最新水平下是不可能的。 ML 研究是尽可能减少逻辑错误机会的过程。因为失败可能是因为某事根本不可能——或者因为它可能是一个错误。提前勤奋和防御是确保失败是前者而不是后者的最佳方式。对合理的失败感到坦然是让实验真正取得成功的最好方法。


原文链接:大模型训练的调试技巧 - BimAnt

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/698697.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Nature子刊】最争气国人友好“灌水刊”,中科院3区升2区,录用仅1个月,2天见刊!

本周投稿推荐 SSCI • 中科院2区,6.0-7.0(录用友好) EI • 各领域沾边均可(2天录用) CNKI • 7天录用-检索(急录友好) SCI&EI • 4区生物医学类,0.5-1.0(录用…

stm32MP135裸机编程:修改官方GPIO例程在DDR中点亮第一颗LED灯

0 参考资料 轻松使用STM32MP13x - 如MCU般在cortex A核上裸跑应用程序.pdf 正点原子stm32mp135开发板&原理图 STM32Cube_FW_MP13_V1.1.0 STM32CubeIDE v1.151 需要修改那些地方 1.1 修改LED引脚 本例使用开发板的PI3引脚链接的LED作为我们点亮的第一颗LED灯,…

使用uniapp开发app实现后台保活定位能力

在 UniApp 中实现后台保活定位能力通常涉及几个关键步骤,包括获取定位权限、实现定位功能、处理后台定位以及确保应用在后台时能够持续定位。以下是一个基本的指南: 1. 系统定位 IOS系统 首先开启系统定位能力 需要配置后台运行能力 注意:…

神经气体生长算法【GNG】

当德国计算神经学家 Bernd Fritzke 在其 1995 年的开创性论文中提出后来被称为神经气体生长(GNG)的算法时,机器学习还是一个相对较新的领域,并且受到实际神经科学的极大启发。 当时,神经科学正处于一个突破性的时代—…

浅谈word格式:.doc和.docx的优缺点及区别

.doc和.docx是两种最为常见的文档格式,它们在多个方面存在着显著的区别。首先,从版本角度来看,.doc是Microsoft Office Word 2003及之前版本的保存类型,而.docx则是Word 2007及之后版本的保存类型。这一区别直接影响了文档在不同版…

【数据分析】统计学基础及Python具体实现

各位大佬好 ,这里是阿川的博客,祝您变得更强 个人主页:在线OJ的阿川 大佬的支持和鼓励,将是我成长路上最大的动力 阿川水平有限,如有错误,欢迎大佬指正 Python 初阶 Python–语言基础与由来介绍 Python–…

FlowUs息流:新媒体运营者的智能协作解决方案

新媒体和自媒体运营者在日常工作中经常面临信息管理复杂、创意记录不便、团队协作效率低等痛点。FlowUs作为一款多功能的协作工具,能够针对性地解决这些问题。 FlowUs息流是一款专为新媒体从业者设计的协作文档工具,它具备以下功能特色: 中文…

Android 代码打印meminfo

旨在替代adb shell dumpsys meminfo packageName,在log打印meminfo,以便分析内存情况 ActivityManager.MemoryInfo memoryInfo new ActivityManager.MemoryInfo(); activityManager.getMemoryInfo(memoryInfo); long totalMemory Runtime.getRuntime(…

Python从0到100(三十二):lxml模块的学习与应用

学习目标 掌握使用lxml库提取数据的方法。理解lxml在数据处理后提取的数据类型。学习将元素(element)转换为字符串的方法。 1. lxml的安装 通过以下命令安装lxml库: pip install lxml2. lxml的使用 2.1 基础使用 导入lxml的etree库&…

用英语介绍端午节,柯桥零基础英语培训

端午节 Dragon Boat Festival 中国传统节日,农历五月初五。相传古代诗人屈原在五月初五投江自杀,后人把这天作为节日纪念他。有划龙舟比赛、包粽子等风俗。 A traditional Chinese festival on the fifth day of the fifth lunar month. Legend has i…

Doris 2.1 元数据更新

metadata_refresh_interval_sec 20

护理考试搜题软件哪个免费?分享九个搜题直接出答案的软件 #知识分享#微信

培养自己的阅读习惯,并不仅仅限于课外读物,还包括学术期刊、行业报告等,以不断提升自己的知识水平和思考能力。 1.彩虹搜题 这是一个公众号 是一款专门针对于大学生或者是成年自考等学生顺利完成证件考试的应用软件,这款软件涵…

AI巅峰对决:8款大模型边缘作文谁领风骚?

前几天我们预测高考作文题目,然后有朋友说我们预测对了,但是我们认真看,发现和全国卷作文人工智能有点交集,但是不能说预测对。 这次我们不预测了,而是让实力说话——邀请8个国产AI大模型参与一场别开生面的“边缘作文…

人工智能对聊天机器人训练数据的“淘金热”可能会耗尽人类编写的文本

人工智能对聊天机器人训练数据的“淘金热”可能会耗尽人类编写的文本 像ChatGPT这样的人工智能系统可能很快就会耗尽让它们变得更聪明的东西——人们在网上写下和分享的数万亿字。 Epoch AI研究集团发布的一项新研究预计,科技公司将在大约十年之交——2026年至203…

Spring--Bean的作用域,生命周期

Bean的作用域 Bean的作用域有很多种,在Spring Framework中支持6种(其中有四种只有在web环境中才能生效),同时Spring还支持自定义Bean的范围。 Spring Framework中支持的6种范围: 作用域解释singleton每个Spring IoC…

【数学建模】微分方程的数值求解

微分方程的数值求解 一阶差分求解微分方程原理:四阶龙格-库塔方法应用:小船渡河问题: 进阶求二阶微分方程 一阶差分求解微分方程原理: d y d x f ( x n , y n ) \dfrac{dy}{dx}f(x_n,y_n) dxdy​f(xn​,yn​) y n 1 − y n x n 1 − x n f ( x n , y n ) \dfrac{y_{n1}-y_n…

React+TS前台项目实战(一)-- 项目初始化配置及开此系列的初衷

文章目录 前言一、初始化项目二、基础配置1. 项目目录及说明如下2. TS版本使用Craco需注意 总结 前言 前面 后台管理系统实战 系列教程暂时告一段落了,想了解全局各种配置的可自行查看。本次教程将重点介绍React前台项目的实操,关于具体的配置&#xff…

51单片机-数码管显示多个

目录 简介: 一. 简单全亮 二. 控制单个变化 三. 2024 书接上回 51单片机-数码管显示单个 http://t.csdnimg.cn/Ii6x0 简介: 51 单片机作为控制核心,可以与数码管相连接来实现数字的显示。 数码管通常有多个段,通过控制这些段的点亮和熄灭状态&…

弘君资本炒股技巧:银行降准对股票的影响?

银行降准会带动股票市场变得相对活泼起来,假如降准前股价在跌落状态,降准能够起到一定缓冲股价跌落的效果。 什么是降准:降准指的是减少银行在央行的存款准备金率,也便是说银行需求存放于央行的资金份额下降,银行能够…

Tensorflow2.10 完成图像分割任务

前言 图像分割在医学成像、自动驾驶汽车和卫星成像等方面有很多应用,本质其实就是图像像素分类任务,也就是使用深度学习模型为输入图像的每个像素分配一个标签(或类)。 准备 本文的准备如下,使用 pip 安装如下配置&…