ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

PyTorch C++ 二阶优化器指南:使用 torch::optim::LBFGS 进行全批量训练

PyTorch C++ 二阶优化器指南:使用 torch::optim::LBFGS 进行全批量训练 PyTorch C 二阶优化器指南使用 torch::optim::LBFGS 进行全批量训练【免费下载链接】pytorchTensors and Dynamic neural networks in Python with strong GPU acceleration项目地址: https://gitcode.com/GitHub_Trending/py/pytorch在 PyTorch 的 C 前端即torch::optim命名空间中优化器家族除了一阶方法SGD、Adam、RMSprop 等还提供了基于曲率信息的二阶方法。本文档聚焦于其中唯一代表性的二阶优化器LBFGSLimited-memory Broyden–Fletcher–Goldfarb–Shanno从适用场景、完整参数说明、closure 机制、底层实现原理到源码级验证帮助你在小模型微调、凸或近凸优化、全批量full-batch训练等场景中正确选型与高效使用。读完本文你将能写出可直接运行、可调参、可断点续训的 LBFGS 训练代码。什么是二阶优化方法二阶方法使用目标函数的曲率信息Hessian 或其近似来决定优化步。与仅利用一阶梯度的 SGD/Adam 相比二阶方法通常能以更少的迭代步数收敛但单步的计算成本与内存占用更高。本文与同目录下的 一阶优化文档、自适应优化文档 共同构成 PyTorch C 优化器 API 的完整参考整体入口见 Optimizers (torch::optim)。LBFGS 属于拟牛顿法它并不显式构造完整的逆 Hessian而是利用最近若干步的梯度差曲率历史在每次迭代中递归地近似“逆 Hessian × 梯度”这一搜索方向。这种方式规避了牛顿法对二阶导数的显式求解对光滑、近凸的损失曲面往往收敛速度显著快于一阶方法代价是必须记录并不断滚动更新一小段历史向量。LBFGS 何时该用、何时不该用根据 second_order.md 及 PyTorch 的实现约定以下场景适合选用 LBFGS小模型历史向量随参数量线性增长参数规模越大内存开销越高微调预训练模型损失曲面相对平滑曲率信息能带来高效的收敛凸或近凸优化问题LBFGS 的理论收敛保证依赖目标的光滑性与近似凸性全批量训练而非 mini-batch这是 LBFGS 使用的硬性前提——它内部用确定性方式沿搜索方向反复调用损失函数与梯度含可选线搜索对随机梯度噪声非常敏感。相应地大模型、大批量随机训练场景应优先考虑一阶方法文档中提到的 Memory-intensive 特性也提醒用户每一次step()都可能执行多次前向/反向时间开销不可忽视。核心参数LBFGSOptions 完整字段与默认值文档中重点介绍了lr、max_iter、history_size三个关键参数而实际可配置项远比这三个丰富。完整的默认值与含义定义在头文件 torch/csrc/api/include/torch/optim/lbfgs.h参数默认值含义与建议lr1.0学习率。LBFGS 通常直接设为1.0实际步长由内部迭代与线搜索决定需要缩放时再调低max_iter20每次step()内最多执行的优化迭代次数max_evalstd::nullopt每次step()内损失函数最大求值次数为nullopt时由构造函数自动取max_iter * 5 / 4tolerance_grad1e-7一阶最优条件阈值当扁平梯度绝对值的最大值不超过该值时提前终止tolerance_change1e-9步长与损失变化停止阈值用于判断“缺乏进展”history_size100保存的过去梯度/步长对数内存历史长度。文档示例中使用10以节省内存line_search_fnstd::nullopt线搜索函数名当前仅支持strong_wolfe强 Wolfe 条件关于max_eval的自动推导可参见 lbfgs.h当max_eval未显式设置时构造器会将max_iter与5/4相乘写入参数组选项与全局默认选项保证在迭代预算之外留出线搜索函数求值的余量。构造 LBFGS 优化器与 SGD/Adam 直接传参不同LBFGS 通过选项对象链式设置auto optimizer torch::optim::LBFGS( model-parameters(), torch::optim::LBFGSOptions(1.0) // 学习率 lr .max_iter(20) // 每步内部迭代上限 .history_size(10)); // 历史梯度记忆长度若采用默认选项可简写为torch::optim::LBFGS(model-parameters())或torch::optim::LBFGS(model-parameters(), torch::optim::LBFGSOptions())。LBFGS 的两大硬性约束1. 必须提供 closure闭包LBFGS 要求一个每次调用都重新计算损失的 closure 函数——这是它与一阶优化器最显著的差异。原因在于 LBFGS 的非线性共轭风格迭代需要在搜索方向上对损失做多次“沿方向求值”每次都必须基于当前被临时更新的参数重新前向/反向。在基类 optimizer.h 中LossClosure被定义为std::functionTensor()而step()的默认实参为nullptr。实现文件 torch/csrc/api/src/optim/lbfgs.cpp 的第一步就是校验TORCH_CHECK(closure ! nullptr, LBFGS requires a closure function);即不传 closure 调用step()会直接抛错。标准训练循环写法如下// LBFGS requires a closure that recomputes the model for (int epoch 0; epoch num_epochs; epoch) { auto closure []() { optimizer.zero_grad(); auto output model-forward(data); auto loss loss_fn(output, target); loss.backward(); return loss; }; optimizer.step(closure); }closure 内的职责固定为三步zero_grad()清空旧梯度 → 前向计算损失 →loss.backward()回填梯度 → 返回损失张量。step(closure)返回值即首次求值得到的原始损失可作日志监控。2. 只支持单一参数组LBFGS 不允许按参数组分别设置选项。构造器中有显式断言lbfgs.hTORCH_CHECK( param_groups_.size() 1, LBFGS doesnt support per-parameter options (parameter groups));同时step()内部也以TORCH_INTERNAL_ASSERT(param_groups_.size() 1)再次确认所有参数作为一个整体参与扁平化梯度计算见下文原理部分。进阶用法开启 strong_wolfe 线搜索若希望每步自动确定最佳步长可启用内置线搜索auto optimizer torch::optim::LBFGS( model-parameters(), torch::optim::LBFGSOptions(1.0) .line_search_fn(strong_wolfe));实现中仅接受字符串strong_wolfe其余取值会触发only strong_wolfe is supported的检查lbfgs.cpp。启用后每步迭代都会调用_strong_wolfe完成“bracket zoom”两阶段搜索先通过沿方向求值扩大区间再用三次插值缩小区间直到满足 Armijo 条件与曲率条件相关实现见 lbfgs.cpp其中 Wolfe 参数取默认c11e-4、c20.9最多 25 次线搜索迭代。由于线搜索会增加多次前向/反向max_eval会相应消耗需保证其预算充足。底层原理从源码看 LBFGS 如何工作LBFGS 的 C 实现整体“移植”自 Python 版torch.optim.LBFGS两者代码逻辑高度一致Python 参考见 torch/optim/lbfgs.py插值与 Wolfe 过程注释均标明源自早期torch/optim项目。核心步骤可从 lbfgs.cpp 梳理如下扁平化梯度_gather_flat_grad()把所有参数的梯度view(-1)后torch::cat拼成一维向量稀疏梯度先to_dense()未定义梯度用零填充。参数总数由_numel()缓存。两步历史更新L-BFGS 记忆计算y grad_new - grad_prev与s d * t。当y·s 1e-10时若历史队列长度已达history_size则pop_front滚动淘汰最旧记录再压入新的y、s与ro 1/(y·s)并以H_diag y·s/(y·y)更新对角近似初值lbfgs.cpp。两遍递归求方向反向循环用old_dirs计算系数α并修正q随后正向循环用old_stps组合出最终搜索方向d经典 L-BFGS two-loop recursionlbfgs.cpp。全部历史都存放在LBFGSParamState的std::dequeTensor中见 lbfgs.h。步长与迭代控制t的初值在首轮取min(1, 1/‖flat_grad‖₁)·lr后续为lrstep()的主循环会按顺序检查四个退出条件——达到max_iter、函数求值次数达到max_eval、满足梯度容忍度tolerance_grad、方向导数或损失变化小于tolerance_changelbfgs.cpp。沿方向的临时求值无论是否线搜索每轮都可能通过_add_grad(t, d)把参数暂时移动到x t·d处重新closure()求损失并取梯度随后_set_param(x)恢复原参数_directional_evaluatelbfgs.cpp。这解释了为什么 closure 必须“可重复、确定性”地重建同一份损失。另外值得注意LBFGS 是全局状态优化器状态只挂在第一个参数上以兼容load_state_dict的映射方式lbfgs.cpp。序列化与旧格式兼容LBFGS支持save/load存档与序列化选项字段与参数状态func_evals、n_iter、t、prev_loss、d、H_diag、prev_flat_grad、三组历史队列等均可写入serialize::OutputArchivelbfgs.cpp。加载时若检测不到pytorch_version字段则按 1.5.0 之前旧格式解析并打印警告提示重新保存lbfgs.cpp。这意味着你可以把优化器状态与模型一起存档实现断点续训。源码级正确性验证仓库自带两套机制验证 C LBFGS 与 Python 行为一致C 单元测试test/cpp/api/optim.cppXORConvergence_LBFGSL262-L266用 XOR 数据集同时验证默认配置与line_search_fn(strong_wolfe)配置均可收敛ProducesPyTorchValues_LBFGSL396-L404逐参数比对该 C 实现与 Pythontorch.optim.LBFGS的输出值MergeWithDefaultOptions_LBFGSL812 起验证选项合并与单参数组限制。基线生成脚本test/cpp/api/optim_baseline.py 用torch.optim.LBFGS(p, 1.0)与torch.optim.LBFGS(p, 1.0, line_search_fnstrong_wolfe)生成期望参数配合check_exact_values断言 C 与 Python 在数值上完全对齐。如果你希望在自己工程中做类似冒烟验证最简单的方式就是复制上文完整训练循环在小型回归问题上对比默认模式与strong_wolfe模式下的损失下降曲线。小结与选型建议二阶方法以曲率信息换取更少迭代步数但计算与内存更昂贵PyTorch C 前端中二阶优化以LBFGS为代表接口为torch::optim::LBFGSLBFGSOptions适用于小模型微调、凸/近凸优化与全批量训练使用 LBFGS 必须牢记两条铁律step()必须接收可重复计算损失的 closure只允许单一参数组七个配置项lr、max_iter、max_eval、tolerance_grad、tolerance_change、history_size、line_search_fn可覆盖从默认快速迭代到强 Wolfe 线搜索的多种精度需求更广泛的一阶方法SGD、Adam 等选择对比可回看 Optimizers (torch::optim) 入口文档。实际工程中建议先用默认参数跑通正确性再逐步开启line_search_fn(strong_wolfe)与调小tolerance_change以逼近更优解同时结合损失曲线确认全批量前提未被破坏。【免费下载链接】pytorchTensors and Dynamic neural networks in Python with strong GPU acceleration项目地址: https://gitcode.com/GitHub_Trending/py/pytorch创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表