4. 符号微分的基本规则与自动求导机制
符号微分不是“把数代进去算斜率”,而是把求导规则当作变换规则,直接作用在表达式的语法结构上,产出一个新的表达式。理解这一点,是掌握 diff 函数行为的关键:输入是一棵表达式树,输出是另一棵表达式树,中间过程是规则在树上的递归匹配与重写。
以多项式 f(x) = 3x^4 - 2x^2 + 5x - 7 为例,它对应的表达式树根节点是减法,左子树又是减法,叶子分别是系数、变量和幂运算。求导时,系统自顶向下遍历:遇到加减法,就把导数分配到各子树(和差法则);遇到“常数乘子 × 子表达式”,把常数提到导数外面(常数倍法则);遇到 x^n,套用幂法则得到 n·x^(n-1);遇到常数,导数为 0。整棵树遍历完毕,再自底向上把结果拼回新树,得到 f'(x) = 12x^3 - 4x + 5。注意常数项 -7 的导数为 0,所以结果里它消失了——这是符号微分与数值差分最直观的区别之一。
把上述过程抽象出来,符号微分依赖四条最常用的规则:和差法则、常数倍法则、幂法则、常数法则;再加上乘积法则 d(uv) = u′v + uv′、商法则与链式法则 d(f(g(x))) = f′(g(x))·g′(x),就足以覆盖绝大多数初等函数。自动求导机制的本质,就是把这组规则编码成“按节点类型分派”的递归函数:每个节点类型对应一个重写模板,模板里递归调用子节点的导数,最后组合成新节点。因此它不需要知道 x 的具体取值,也不做数值近似,得到的是精确的符号结果。
diff 函数对单变量与多变量表达式的用法
在 SymPy 中,diff 是这套机制的直接入口。对单变量表达式,只需给出变量名;对多变量表达式,则要指明对哪个变量求偏导,或按顺序多次求导。下面用多项式、指数与对数函数做一组可复现的示例(结果为符号推导,非数值实测):
import sympy as sp
x, y = sp.symbols('x y')
# 1) 多项式:一阶导
f1 = 3*x**4 - 2*x**2 + 5*x - 7
print(sp.diff(f1, x)) # 12*x**3 - 4*x + 5
# 2) 指数函数:链式法则
f2 = sp.exp(2*x)
print(sp.diff(f2, x)) # 2*exp(2*x)
# 3) 对数函数:先化简再求导更稳
f3 = sp.log(x**2 + 1)
print(sp.diff(f3, x)) # 2*x/(x**2 + 1)
# 4) 多变量:分别对 x、y 求偏导
f4 = x**2*y + sp.sin(x*y)
print(sp.diff(f4, x)) # 2*x*y + y*cos(x*y)
print(sp.diff(f4, y)) # x**2 + x*cos(x*y)
# 5) 高阶与混合偏导
print(sp.diff(f4, x, 2)) # 2*y - y**2*sin(x*y)
print(sp.diff(f4, x, y)) # 2*x + cos(x*y) - x*y*sin(x*y)
观察输出可以提炼出几条实践要点。第一,diff(f, x) 中的 x 必须是已用 symbols 声明的符号对象,若误传字符串,不同版本行为可能不一致,建议统一用符号变量。第二,多变量表达式默认只对指定变量求偏导,其余变量视为常数,这正是偏导数的定义,不要期待它自动给出全导数。第三,diff(f, x, 2) 等价于连续求两次一阶导,diff(f, x, y) 表示先对 x 再对 y 求混合偏导;对足够光滑的函数,混合偏导与求导次序无关,可用 diff(f, x, y) - diff(f, y, x) 化简为 0 来交叉验证。第四,指数与对数函数常因链式法则产生复合结构,输出未必是“最简形式”,可配合 sp.simplify 或 sp.factor 整理,但要注意化简可能改变表达式的定义域,验证时应以原始表达式为准。
如何验证自动求导结果的正确性
符号结果正确与否,不能只看“长得像”。推荐三条互补的验证路径。其一是规则手算对照:对多项式逐项套幂法则,对 exp(2x) 套链式法则,手算结果与 diff 输出逐项比对,这是最直接的教学验证。其二是数值差分抽查:在若干测试点上用中心差分 (f(x+h) - f(x-h)) / (2h) 近似导数,与符号结果代入同一批点比较,误差应随 h 缩小而收敛;这能发现符号推导中的符号错误或漏项。其三是恒等式检验:例如验证 sp.simplify(sp.diff(f, x, y) - sp.diff(f, y, x)) == 0,或对 log 类表达式检查 diff(log(u), x) 是否等于 diff(u, x)/u 的化简结果。三种方法各有侧重:手算查规则理解,差分查数值一致性,恒等式查结构性质,组合使用比单一手段更可靠。
log(x**2+1) 在全实数域光滑,可放心取点;而含 1/x 或 log(x) 的表达式应避开 x=0 及负半轴,否则数值差分与符号结果都会因定义域问题产生误导性偏差。最后需要区分符号微分与自动微分(AD)这两个容易混淆的概念。符号微分产出的是表达式,适合需要公式本身、需要进一步化简或代入多组参数的场景,但表达式可能随嵌套加深而膨胀;自动微分(前向或反向模式)则在计算图上按链式法则累积数值导数,不生成显式公式,适合大规模数值计算与机器学习训练。二者共享“链式法则 + 递归分解”的思想内核,但产物形态与适用场景不同。掌握 diff 的规则映射与验证方法,既能把符号微分用对,也为理解自动微分的计算图机制打下同一套规则基础。