尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Burn 贡献者测试指南:张量操作与 Autodiff 反向传播测试的编写规范与实践

Burn 贡献者测试指南:张量操作与 Autodiff 反向传播测试的编写规范与实践 Burn 贡献者测试指南张量操作与 Autodiff 反向传播测试的编写规范与实践【免费下载链接】burnBurn is a next generation tensor library and Deep Learning Framework that doesnt compromise on flexibility, efficiency and portability.项目地址: https://gitcode.com/GitHub_Trending/bu/burn本篇技术指南面向 Burn 项目的贡献者系统讲解 Burn 测试体系的核心组织方式张量Tensor操作测试如何只写一次、处处运行地传播到所有后端自动微分Autodiff反向传播测试应放在哪里、如何设计用例以及浮点/整数断言的正确写法assert_approx_eq、Tolerance、FloatElem/IntElem。读完本文你将能够按照 Burn 的既有规范为新的张量算子或反向传播实现贡献高质量、多精度兼容的测试代码并理解仓库中 crates/burn-backend-tests 与 crates/burn-std 之间的协作关系。测试的整体架构为什么张量操作测试只写一次Burn 是一个多后端深度学习框架同一份算子实现需要运行在 CPUndarray、GPUcubecl/CUDA/ROCm/Metal等多个后端之上。如果每个后端各自维护一份测试不仅重复劳动还会因为各后端数值实现细节的差异而难以维护。Burn 的解决方案是张量操作测试只定义在张量库层通过宏机制传播到所有后端后端本身不重复编写算子测试Autodiff 后端除外。从当前仓库的结构看张量操作与后端的通用测试集中在 crates/burn-backend-tests 中其中 crates/burn-backend-tests/tests/autodiff.rs、crates/burn-backend-tests/tests/tensor.rs 等入口文件通过统一的类型别名如TestTensor与后端无关地编写用例再由各后端测试工程复用。这套单点定义、多点复用的思路贯穿全文理解它有助于你判断一个新测试应该放在哪个 crate、哪个目录。测试用例的形态绝大多数张量操作测试遵循同一形态给定某个输入断言输出等于或近似等于某个预期值即构造输入张量执行待测操作把实际输出转换为TensorData与预期TensorData比较。断言统一在TensorData层面进行而不是直接比较Tensor这正是 crates/burn-std/src/data/compare.rs 中assert_eq/assert_approx_eq的职责所在下文会详细展开。为张量操作编写测试当你新增一个张量算子例如某个新的激活函数、归约或变换时需要同时为它补齐测试。测试应当放在张量库的测试模块中在原文档所描述的上游结构中是crates/burn-tensor/src/tests/ops下的算子测试文件并通过testgen_all宏统一注册而不是写在各后端 crate 里。这样所有后端在运行测试时都会自动获得这份用例无需为每个后端单独复制粘贴。需要特别注意的是Autodiff 后端是例外。由于自动微分测试需要验证梯度它与普通算子测试的断言逻辑不同因此单独组织见下一节。如果你新增的操作带有可导出的反向实现除了算子测试还必须补充 Autodiff 测试。为 Autodiff 反向传播编写测试Autodiff 测试的目标是验证反向传播backward pass的梯度正确性因此不能只断言前向输出还要断言backward()之后每个输入张量的梯度是否符合手工推导的解析结果。二元操作左右两侧都要验证对于二元张量操作如加法、乘法、矩阵乘法、卷积等反向传播时梯度会同时流向左侧与右侧操作数且两侧梯度通常形态不同。因此测试必须同时验证左侧梯度和右侧梯度缺一不可。仓库中 crates/burn-backend-tests/tests/autodiff/add.rs 就是最直接的范例#[test] fn should_diff_add() { let device AutodiffDevice::new(); let tensor_1 TestTensor::1::from_data([2.0, 5.0], device).require_grad(); let tensor_2 TestTensor::from_data([4.0, 1.0], device).require_grad(); let tensor_3 tensor_1.clone() tensor_2.clone(); let grads tensor_3.backward(); let grad_1 tensor_1.grad(grads).unwrap(); let grad_2 tensor_2.grad(grads).unwrap(); grad_1 .to_data() .assert_eq(TensorData::from([1.0, 1.0]), false); grad_2 .to_data() .assert_eq(TensorData::from([1.0, 1.0]), false); tensor_3 .to_data() .assert_eq(TensorData::from([6.0, 6.0]), false); }这个用例清晰地展示了编写 Autodiff 测试的固定套路用AutodiffDevice::new()创建带自动微分能力的设备定义在 crates/burn-backend-tests/tests/common/autodiff.rs内部调用Device::default().autodiff()通过.require_grad()标记需要梯度的输入张量执行前向计算调用.backward()得到grads用.grad(grads)取出每个输入对应的梯度逐一与手工推导的预期梯度比对。同目录下的mul.rs、matmul.rs、conv2d.rs、cat.rs等 80 余个测试文件见模块清单 crates/burn-backend-tests/tests/autodiff/mod.rs都遵循同一模式是学习如何为特定算子写反向测试的最佳参考库。一种省力的预期值推导工作流手工推导梯度容易出错尤其是涉及复合操作、广播或高维张量时。原文档给出了一套非常实用的工作流可以显著降低出错概率使用小型张量、选择简单值例如[2.0, 5.0]、[[1.0, 7.0], [13.0, -3.0]]这类便于心算的数值避免浮点运算带来的误差累积干扰判断。打开终端用ipython导入numpy手工计算把前向输出与反向梯度各算一遍得到标准答案。也可以在 Google Colab 中计算避免在本地系统安装额外依赖。把实际输出与预期输出逐一比对注意同时比对左侧与右侧对二元操作左右两侧的梯度都要分别验证。这套流程的本质是把解析推导转化为数值验证先用独立工具numpy得到可信的预期值再用这些预期值锁定测试断言最后让测试在所有后端持续回归。浮点断言assert_approx_eq与Tolerance浮点运算是非精确的——不同后端CPU 向量化、GPU kernel、融合优化的求和顺序、中间精度都可能不同导致相同计算出现微小数值差异。因此浮点张量测试应使用近似断言而不是精确的assert_eq!。推荐的断言写法actual_output_tensor .into_data() .assert_approx_eq::FloatElemTestBackend(expected_tensor_data, Tolerance::default());这里有两个关键点泛型参数FloatElemTestBackend声明了数据读取与比较所用的浮点元素类型必须与测试后端一致第二个参数是Tolerance容差默认值Tolerance::default()适用于绝大多数场景。Tolerance的源码级解析Tolerance定义在 crates/burn-std/src/data/compare.rs核心判定逻辑在approx_eqcompare.rs|x - y| max(A, R * max(|x|, |y|))其中R是相对容差relativeA是绝对容差absolute。两者取较大者可以同时处理数值很大、相对误差有意义与数值接近 0、绝对误差有意义两类情况。Tolerance提供了多种构造方式compare.rs构造方式相对容差 R绝对容差 A适用场景Tolerance::F::default()等价于balanced()0.0050.5%1e-5常规测试的推荐默认值Tolerance::F::strict()0.064 * F::min_positive_value()对精度要求极高的断言Tolerance::F::permissive()0.011.0%0.01数值实现差异较大的场景Tolerance::F::rel_abs(1e-4, 1e-5)自定义自定义显式指定相对/绝对容差Tolerance::F::relative(r)/absolute(a)仅相对 / 仅绝对—只关心某一类误差此外Tolerance还提供了按精度分级调整的链式方法set_half_precision_relative、set_single_precision_relative、set_double_precision_relative以及对应的*_absolute系列内部通过core::mem::size_of::F()判断浮点类型宽度2/4/8 字节从而让同一套断言在 f16、f32、f64 上使用不同的容差compare.rs。例如Tolerance::F::rel_abs(1e-4, 1e-5).set_half_precision_relative(1e-2)就为半精度单独放宽了相对容差。assert_approx_eq的失败行为assert_approx_eqcompare.rs会先检查两个TensorData的 shape 是否一致然后逐元素比较。它内置了三条人性化规则双方同为NaN视为相等both_nan直接跳过双方同为同符号无穷视为相等both_inf跳过失败时最多打印前 5 个差异位置并在每条差异中同时输出绝对误差、相对误差与容差diff (rel ..., abs ...), tol (rel ..., abs ...)方便快速定位是哪类误差超标。这意味着断言失败时你会立刻知道是相对误差超了还是绝对误差超了从而决定是调整容差还是修复实现。多精度兼容FloatElem与IntElem的正确用法Burn 后端会以多种精度运行测试。以 Autodiff 测试为例仓库同时维护了 crates/burn-backend-tests/tests/autodiff.rsFloatElem f32、IntElem i32与 crates/burn-backend-tests/tests/autodiff_f16.rsFloatElem burn_tensor::f16、IntElem i32。如果你在测试中硬编码f32、i32之类的具体类型那么当测试以半精度f16或其他精度运行时就会编译失败或断言误报。因此原文档提出了两条硬性规范任何断言都必须使用FloatElemTestBackend作为浮点类型参数不要硬编码具体类型任何字面量转换都通过.elem()进行例如1.0_f32.elem()让字面量自动适配测试后端的元素类型。为了方便书写通常会在测试模块顶部做类型别名例如type FT FloatElemTestBackend;之后所有需要标注泛型参数或进行.elem()转换的地方都复用这个别名既简洁又不易出错。在 crates/burn-backend-tests/tests/common/backend.rs 中可以看到类型体系的全貌TestTensorconst D: usize别名到通用TensorD并有TestTensorInt、TestTensorBool分别对应整数与布尔张量文件顶部的#[ctor]初始化函数会根据FloatElem/IntElem的 dtype 配置默认设备从而保证整个测试进程运行在正确的元素类型上。整数测试的注意事项整数张量没有浮点误差问题可以直接用精确断言但同样要遵循类型规范使用IntElemTestBackend而非硬编码的i32/i64测试用到的整数必须落在当前元素类型的可表示范围内若超出max_value或低于min_value应直接提前退出测试return而不是让测试以未定义行为继续原文档约定可以假设最小支持范围是[0..127]即i8可表示的范围。这意味着只要你的测试数值落在0..127之内就可以放心运行在任意整数精度上若要使用更大数值就必须先检查IntElemTestBackend的表示范围超出即跳过。这一约定让同一份整数测试能够在 i8/i16/i32/i64 等不同位宽的后端配置下安全复用。测试基础设施中的辅助工具除了断言体系仓库还为贡献者准备了若干测试辅助设施#[might_panic]过程宏属性定义在 crates/burn-backend-tests/src/lib.rs用于标记允许以特定消息 panic的测试。当测试以预期前缀的消息 panic 时该失败会被忽略否则仍按正常失败处理。注意它基于std::panic::catch_unwind在 no-std 环境下不可用使用时需要按 feature 门控。梯度检查点gradient checkpointing测试变体在 crates/burn-backend-tests/tests/common/autodiff.rs 中通过覆盖AutodiffDevice为Device::default().autodiff().gradient_checkpointing()用include!复用同一套autodiff/mod.rs测试模块从而以零成本让所有 Autodiff 用例在梯度检查点模式下再跑一遍验证内存优化路径的正确性。TensorData::assert_eq(other, strict)精确断言支持strict参数控制是否要求 dtype 完全一致compare.rs。非严格模式下会在当前 dtype 上做比较便于跨 dtype 场景如量化张量使用strict true时 dtype 不同会直接 panic。动手实践新增一个算子测试的检查清单结合全文当你为 Burn 贡献新的算子或反向实现时可以按以下清单逐步落地测试确定测试归属算子测试放入张量库测试模块反向梯度测试放入 Autodiff 测试目录当前仓库为crates/burn-backend-tests/tests/autodiff/并在 crates/burn-backend-tests/tests/autodiff/mod.rs 中注册模块使用TestTensor浮点、TestTensorInt整数等统一类型别名构造输入配合AutodiffDevice::new()与.require_grad()对二元操作分别手工推导并断言左侧与右侧梯度先用小张量、简单值在ipythonnumpy中验证预期值浮点断言一律使用into_data().assert_approx_eq::FloatElemTestBackend(expected, Tolerance::default())必要时根据 f16 特性调整容差如set_half_precision_relative整数断言使用IntElemTestBackend并检查数值是否在可表示范围内超出[0..127]的最小可假设范围时提前退出不硬编码f32/i32字面量统一通过.elem()转换并善用type FT FloatElemTestBackend别名提交前跑一遍测试确认在默认精度与 f16 等替代精度参考autodiff_f16.rs的配置方式下均通过。遵循以上规范你的测试就能在 Burn 的所有后端、所有精度配置下稳定运行真正发挥一处编写、处处执行的威力。【免费下载链接】burnBurn is a next generation tensor library and Deep Learning Framework that doesnt compromise on flexibility, efficiency and portability.项目地址: https://gitcode.com/GitHub_Trending/bu/burn创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表