.agents/skills/paddle-op-dev/references/unit-test.md
新增算子需要在 test/legacy_test 目录下添加单元测试,使用 OpTest 框架验证算子前向计算和反向梯度的正确性。
test/legacy_test/test_xxx_op.py
继承 OpTest 基类来编写算子测试。
from paddle.base import core
from paddle.base.tests.unittests.op_test import OpTest
import unittest
import numpy as np
class TestXxxOp(OpTest):
def setUp(self):
self.op_type = "xxx" # 算子名
self.python_api = paddle.xxx # 对应的 Python API
self.init_config()
# 设置输入
self.inputs = {
'X': np.random.random((3, 4)).astype("float64"),
}
# 设置属性
self.attrs = {
'attr1': value1,
}
# 设置期望输出
self.outputs = {
'Out': expected_output,
}
def test_check_output(self):
self.check_output(check_pir=True)
def test_check_grad(self):
self.check_grad(['X'], 'Out', check_pir=True)
check_output() 验证 C++ 算子前向计算结果与 Python/NumPy 参考实现的一致性。
def test_check_output(self):
self.check_output(check_pir=True)
check_pir=True:同时检验 PIR 模式下的输出正确性check_grad() 验证反向梯度计算的正确性(通过数值微分法)。
def test_check_grad(self):
self.check_grad(
['X'], # 需要检查梯度的输入列表
'Out', # 对应的输出名
check_pir=True
)
float64 类型以保证数值微分精度no_grad_set 参数def test_check_grad_no_x(self):
self.check_grad(
['Y'], 'Out',
no_grad_set=set("X"),
check_pir=True
)
以 trace 算子为例(test/legacy_test/test_trace_op.py):
import unittest
import numpy as np
import paddle
from paddle.base import core
from paddle.base.tests.unittests.op_test import OpTest
class TestTraceOp(OpTest):
def setUp(self):
self.op_type = "trace"
self.python_api = paddle.trace
self.init_config()
def init_config(self):
self.case = np.random.randn(20, 6).astype('float64')
self.inputs = {'Input': self.case}
self.attrs = {'offset': 0, 'axis1': 0, 'axis2': 1}
self.outputs = {
'Out': np.trace(self.inputs['Input'],
offset=self.attrs['offset'],
axis1=self.attrs['axis1'],
axis2=self.attrs['axis2'])
}
def test_check_output(self):
self.check_output(check_pir=True)
def test_check_grad(self):
self.check_grad(['Input'], 'Out', check_pir=True)
class TestTraceOpCase1(TestTraceOp):
"""测试不同参数配置"""
def init_config(self):
self.case = np.random.randn(20, 6).astype('float64')
self.inputs = {'Input': self.case}
self.attrs = {'offset': 1, 'axis1': 0, 'axis2': 1}
self.outputs = {
'Out': np.trace(self.inputs['Input'],
offset=self.attrs['offset'],
axis1=self.attrs['axis1'],
axis2=self.attrs['axis2'])
}
class TestTraceOpCase2(TestTraceOp):
"""测试高维输入"""
def init_config(self):
self.case = np.random.randn(2, 20, 2, 3).astype('float64')
self.inputs = {'Input': self.case}
self.attrs = {'offset': 1, 'axis1': 1, 'axis2': 2}
self.outputs = {
'Out': np.trace(self.inputs['Input'],
offset=self.attrs['offset'],
axis1=self.attrs['axis1'],
axis2=self.attrs['axis2'])
}
class TestTraceAPICase(unittest.TestCase):
"""测试 Python API 调用"""
def test_case1(self):
x = paddle.to_tensor(np.random.randn(20, 6).astype('float64'))
result = paddle.trace(x)
expected = np.trace(x.numpy())
np.testing.assert_allclose(result.numpy(), expected, rtol=1e-05)
if __name__ == "__main__":
unittest.main()
# 单个测试文件
python -m pytest test/legacy_test/test_xxx_op.py -v
# 或者
python test/legacy_test/test_xxx_op.py
float64 确保数值微分精度init_config() 测试不同参数组合