修正一行代码,PyTorch磨炼三倍提速,这些「低级技术」是关键

时间:2024-11-16 19:54:52 编辑: 来源:

机械之心编译

作者 :Sebastian Raschka

编纂  :Panda W

用对于了措施 ,修正行代减速 PyTorch 磨炼,码P磨炼无意也不是倍提那末重大。

克日,速低术关深度学习规模驰名钻研者、技键Lightning AI 的修正行代首席家养智能教育者 Sebastian Raschka 在 CVPR 2023 上宣告了主题演讲「Scaling PyTorch Model Training With Minimal Code Changes」。

为了能与更多人分享钻研下场 ,码P磨炼Sebastian Raschka 将演讲整理成一篇文章。倍提文章品评辩说了若何在最小代码变更的速低术关情景下扩展 PyTorch 模子磨炼,并表明重点是技键运用混合精度(mixed-precision)措施以及多 GPU 磨炼方式,而不是修正行代低级机械优化 。

文章运用视觉 Transformer(ViT)作为根基模子  ,码P磨炼ViT 模子在一个根基数据集上重新开始 ,倍提经由约 60 分钟的速低术关磨炼 ,在测试集上取患了 62% 的技键精确率。

GitHub 地址 :https://github.com/rasbt/cvpr2023

如下是文章原文:

构建基准

在接下来的部份中 ,Sebastian 将品评辩说若何在不断止大批代码重构的情景下改善磨炼光阴以及精确率。

想要留意的是,模子以及数据集的详细信息并非这里的主要关注点(它们只是为了尽可能重大 ,以便读者可能在自己的机械上复现 ,而不需要下载以及装置太多的依赖) 。所有在这里分享的示例都可能在 GitHub 找到 ,读者可能探究以及重用残缺的代码。

剧本 00_pytorch-vit-random-init.py 的输入  。

不要重新开始磨炼

现如今  ,重新开始磨炼文本或者图像的深度学习模子艰深为低效的。咱们个别会运用预磨炼模子 ,并对于模子妨碍微调,以节约光阴以及合计资源 ,同时取患上更好的建模下场。

假如思考下面运用的相同 ViT 架构,在另一个数据集(ImageNet)上妨碍预磨炼 ,并对于其妨碍微调,就能在更短的光阴内实现更好的预料功能:20 分钟(3 个磨炼 epoch)内抵达 95% 的测试精确率。

00_pytorch-vit-random-init.py 以及 01_pytorch-vit.py 的比力 。

提升合计功能

咱们可能看到 ,相对于从零开始磨炼,微调可能大大提升模子功能 。下面的柱状图总结了这一点。

00_pytorch-vit-random-init.py 以及 01_pytorch-vit.py 的比力柱状图 。

尽管 ,模子下场可能因数据集或者使命的差距而有所差距 。但对于良多文本以及图像使命来说,从一个在通用公共数据集上预磨炼的模子开始是值患上的 。

接下来的部份将探究种种能耐 ,以减速磨炼光阴 ,同时又不舍身预料精确性。

开源库 Fabric

在 PyTorch 中以最小代码变更来高效扩展磨炼的一种措施是运用开源 Fabric 库,它可能看做是 PyTorch 的一个轻量级包装库 / 接口 。经由 pip 装置。

pip install lightning

下面探究的所有技术也可能在纯 PyTorch 中实现。Fabric 的目的是使这一历程愈加利便。

在探究「减速代码的低级技术」以前 ,先介绍一下将 Fabric 集成到 PyTorch 代码中需要妨碍的小修正。一旦实现这些修正 ,惟独要修正一行代码 ,就能轻松地运用低级 PyTorch 功能 。

PyTorch 代码以及更正后运用 Fabric 的代码之间的差距是重大的,只波及到一些细微的更正 ,如下面的代码所示 :

艰深 PyTorch 代码(左)以及运用 Fabric 的 PyTorch 代码

总结一下上图,就能患上到艰深的 PyTorch 代码转换为 PyTorch+Fabric 的三个步骤 :

  • 导入 Fabric 并实例化一个 Fabric 工具。

  • 运用 Fabric 配置模子、优化器以及 data loader。

  • 损失函数运用 fabric.backward () ,而不是 loss.backward () 。

这些重大的修正提供了一种运用 PyTorch 低级特色的道路 ,而无需对于现有代码妨碍进一步重构。

深入品评辩说下面的「低级特色」以前 ,要确保模子的磨炼运行光阴  、预料功能与以前相同 。

01_pytorch-vit.py 以及 03_fabric-vit.py 的比力服从 。

正如前面柱状图中所看到的 ,磨炼运行光阴 、精确率与以前残缺相同,正如预期的那样 。其中 ,任何晃动都可能归因于随机性 。

在前面的部份中 ,咱们运用 Fabric 更正了 PyTorch 代码。为甚么要费这么大的劲呢 ?接下来将试验低级技术 ,好比混合精度以及扩散式磨炼,惟独变更一行代码 ,把下面的代码

fabric = Fabric(accelerator="cuda")

改为

fabric = Fabric(accelerator="cuda", precision="bf16-mixed")

04_fabric-vit-mixed-precision.py 剧本的比力服从。脚当地址 :https://github.com/rasbt/cvpr2023/blob/main/04_fabric-vit-mixed-precision.py

经由混合精度磨炼,咱们将磨炼光阴从 18 分钟摆布延早退 6 分钟 ,同时坚持相同的预料功能。这种磨炼光阴的延迟惟独在实例化 Fabric 工具时削减参数「precision="bf16-mixed"」即可实现。

清晰混合精度机制

混合精度磨炼本性上运用了 16 位以及 32 位精度,以确保不会损失精确性 。16 位展现中的合计梯度比 32 位格式快良多,而且还节约了大批内存。这种策略在内存或者合计受限的情景下颇为有利 。

之以是称为「混合」而不是「低」精度磨炼 ,是由于不是将所有参数以及操作转换为 16 位浮点数。相同,在磨炼历程中 32 位以及 16 位操作之间切换,因此称为「混合」精度 。

如下图所示 ,混合精度磨炼波及步骤如下:

  • 将权重转换为较低精度(FP16)以减速合计速率;

  • 合计梯度;

  • 将梯度转换回较高精度(FP32)以坚持数值晃动性;

  • 运用缩放后的梯度更新原始权重。

这种措施在坚持神经收集精确性以及晃动性的同时  ,实现为了高效的磨炼。

更详细的步骤如下 :

  • 将权重转换为 FP16  :在这一步中,神经收集的权重(或者参数)初始时用 FP32 格式展现,将其转换为较低精度的 FP16 格式 。这样可能削减内存占用,而且由于 FP16 操作所需的内存较少,可能更快地被硬件处置。

  • 合计梯度 :运用较低精度的 FP16 权重妨碍神经收集的前向转达以及反向转达 。这一步合计损失函数相对于收集权重的梯度(偏导数),这些梯度用于在优化历程中更新权重。

  • 将梯度转换回 FP32  :在合计患上到 FP16 格式的梯度后,将其转换回较高精度的 FP32 格式 。这种转换对于坚持数值晃动性颇为紧张 ,防止运用较低精度算术时可能泛起的梯度消逝或者梯度爆炸等下场 。

  • 乘学习率并更新权重 :以 FP32 格式展现的梯度乘以学习率将用于更新权重(标量值,用于判断优化历程中的步长)。

步骤 4 中的乘积用于更新原始的 FP32 神经收集权重。学习率有助于操作优化历程的收敛性,对于实现精采的功能颇为紧张。

Brain Float 16

前面谈到了「float 16-bit」精度磨炼 。需要留意的是 ,在以前的代码中 ,指定了 precision="bf16-mixed" ,而不是 precision="16-mixed"。这两个都是实用的选项。

在这里 ,"bf16-mixed" 中的「bf16」展现 Brain Floating Point(bfloat16) 。google开拓了这莳格式,用于机械学习以及深度学习运用 ,特意是在张量处置单元(TPU)中。Bfloat16 比照传统的 float16 格式扩展了动态规模 ,但舍身了确定的精度。

扩展的动态规模使患上 bfloat16 可能展现颇为大以及颇为小的数字 ,使其更适用于深度学习运用中可能碰着的数值规模 。可是,较低的精度可能会影响某些合计的精确性  ,或者在某些情景下导致舍入倾向 。但在大少数深度学习运用中,这种飞腾的精度对于建模功能的影响很小。

尽管 bfloat16 最后是为 TPU 开拓的,但从 NVIDIA Ampere 架构的 A100 Tensor Core GPU 开始 ,已经有多少种 NVIDIA GPU 开始反对于 bfloat16。

咱们可能运用下面的代码魔难 GPU 是否反对于 bfloat16:

>>> torch.cuda.is_bf16_supported()True

假如你的 GPU 不反对于 bfloat16,可能将 precision="bf16-mixed" 变更为 precision="16-mixed"。

多 GPU 磨炼以及残缺分片数据并行

接下来要试验更正多 GPU 磨炼 。假如咱们有多个 GPU 可供运用 ,这会带来短处,由于它可能让咱们的模子磨炼速率更快。

这里介绍一种更先进的技术 — 残缺分片数据并行(Fully Sharded Data Parallelism (FSDP)) ,它同时运用了数据并行性以及张量并行性。

在 Fabric 中 ,咱们可能经由下面的方式运用 FSDP 削减配置装备部署数目以及多 GPU 磨炼策略:

fabric = Fabric(accelerator="cuda", precision="bf16-mixed",devices=4, strategy="FSDP"  # new!)

06_fabric-vit-mixed-fsdp.py 剧本的输入。

如今运用 4 个 GPU,咱们的代码运行光阴约莫为 2 分钟,是以前仅运用混合精度磨炼时的近 3 倍 。

清晰数据并行以及张量并行

在数据并行中 ,小批量数据被分割,而且每一个 GPU 上都有模子的本来。这个历程经由多个 GPU 的并行使命来减速模子的磨炼速率。

如下扼要概述了数据并行的使命道理:

统一个模子被复制到所有的 GPU 上 。

每一个 GPU 分说接管差距的输入数据子集(差距的小批量数据)。

所有的 GPU 独即将对于模子妨碍前向转达以及反向转达 ,合计各自的部份梯度 。

群集并对于所有 GPU 的梯度求平均值。

平均梯度被用于更新模子的参数 。

每一个 GPU 都在并行地处置差距的数据子集 ,经由梯度的平均化以及参数的更新 ,全部模子的磨炼历程患上以减速。

这种措施的主要优势是速率 。由于每一个 GPU 同时处置差距的小批量数据 ,模子可能在更短的光阴内处置更多的数据 。这可能清晰削减磨炼模子所需的光阴,特意是在处置大型数据集时 。

可是 ,数据并行也有一些限度。最紧张的是,每一个 GPU 必需具备残缺的模子以及参数本来。这限度了可能磨炼的模子巨细,由于模子必需顺应单个 GPU 的内存 。这对于今世的 ViTs 或者 LLMs 来说这是不可行的 。

与数据并行差距,张量并即将模子自己散漫到多个 GPU 上 。而且在数据并行中 ,每一个 GPU 都需要适 应全部模子,这在磨炼较大的模子时可能成为一个限度。而张量并行应承磨炼那些对于单个 GPU 而言可能过大的模子,经由将模子分解并扩散到多个配置装备部署上妨碍磨炼 。

张量并行是若何使命的呢?想象一下矩阵乘法,有两种方式可能妨碍扩散合计 —— 按行或者按列。为了重大起见,思考按列妨碍扩散合计 。好比,咱们可能将一个大型矩阵乘法操作分解为多个自力的合计,每一个合计可能在差距的 GPU 上妨碍,如下图所示 。而后将服从衔接起来以取患上服从,这实用地摊派了合计负载。

原文链接:https://magazine.sebastianraschka.com/p/accelerating-pytorch-model-training