Skip to content

导数与微分:变化有多快,局部能不能当成直线

WARNING

🧪 Beta公测版本提示:教程主体已完成,正在优化细节,欢迎大家提Issue反馈问题或建议。

微积分不是一堆求导表。它只讲一件事:光滑的弯曲,在足够小的窗口里看起来像直线。 这条直线的斜率叫导数;用这条直线代替曲线去算「再走一小步会变多少」,叫微分。积分是下一章的累加。领域地图:数学基础。后面的 线性代数 处理的是「已经是直线」的变换;优化 里的梯度,就是本章思想抬到多变量。


一、函数是一台机器,导数问「此刻转多快」

函数 f 把输入 x 变成输出 f(x)。平均变化率只比较两个时刻:

f(a+h)f(a)h

几何上是连接 (a,f(a))(a+h,f(a+h))割线斜率。h 是时间步、是窗口宽度。窗口很大时,割线只是个粗略平均——就像用「这一小时走了 60 公里」说你此刻的车速。

h 缩小,割线会转动。若曲线在 a 附近足够光滑,割线会贴上一根唯一的切线。这根切线的斜率就是导数:

f(a)=limh0f(a+h)f(a)h.

不必先啃 ε-δ。极限在这里的日常含义是:窗口小到再小,斜率不再改主意。 改主意的地方叫不可导(尖角、折点)——对 AI 里常见的 ReLU,尖角处我们约定一个侧导数继续算,思想不变。

割线贴成切线

图解说明:左是宽窗口的平均变化率;中是两点靠近;右是切线,旁边的小三角形就是微分 dy=f(a)dx。底栏:导数是速度,微分是局部直线。

动画说明f(x)=x32x,锚点 x=1.2h 从 0.85 收到 0.04,割线斜率贴近切线 f(1.2)

f(x)=x32xx=2 处,f(2)=10。demo 里三张图用 h=0.80.25、切线把这个贴合过程画出来。

割线怎样贴成切线

图解说明:窗口从宽到窄,割线转到切线。旁边小三角形就是微分 dy=f(a)dx

保姆级数字例。 f(x)=x32xa=2f(2)=84=4

hf(2+h)割线斜率 [f(2+h)4]/h
0.82.835.6=16.35215.44
0.252.2534.5=6.89062511.56
0.052.0534.1=4.51512510.30
010(解析:3x22,代入 2122=10

窗口越小越接近 10,但不会在有限 h 上精确等于 10——这就是极限「再小也不改主意」,不是「某一次已经到达」。

逐步推导:从平均变化率到 f(x)=3x22(点击展开)

f(x)=x32x

f(x+h)f(x)h=(x+h)32(x+h)x3+2xh.

展开 (x+h)3=x3+3x2h+3xh2+h3,于是分子 =3x2h+3xh2+h32h,除以 h

3x22+3xh+h2.

h0,后两项消失,留下 3x22。几何上 3xh+h2 就是「窗口还没缩没时割线比切线多出来的那截」。

不可导:若曲线有尖角,左右两边极限不同(如 |x|0)。ReLU 在 0 处约定右导数 1 或直接令 0,训练仍能走,因为几乎不会精确停在那一个点上。


二、微分:认真对待「局部直线」

导数是一个(斜率)。微分是一次线性替换

f(a+Δx)f(a)+f(a)Δx.

右边是切线。Δx 写成 dx 时,人们把 df=f(x)dx 叫做微分。思想是:

弯曲的世界里,每走一小步,只用乘一次斜率。

这比「背求导公式」更根本。后面几乎所有运算法则,都可以从「两条直线怎么合成」推出来,而不是从极限式里硬算。

  • :两台机器并排,斜率相加。
  • 乘积:矩形的宽 u、高 v。两边各胀一截,面积多出来的是 udv+vdu;角上那一小块 dudv 在「足够小」时扔掉——这就是乘积法则。
  • 复合(链式法则):先 xg(x)gf(g)。第一台把 dx 放大 g 倍,第二台再放大 f 倍,总放大倍数相乘:
ddxf(g(x))=f(g(x))g(x).

神经网络的反向传播,就是这条链从输出往输入逆着乘。本章只要求看见「变化率相乘」;具体下山见 优化与梯度

乘积法则来自矩形

图解说明:宽 uv 的矩形胀一圈:多出来的是 vdu+udv,角上 dudv 扔掉。

逐步推导:乘积法则与链式法则都来自「局部直线」(点击展开)

乘积。 A=uv。增量

ΔA=(u+Δu)(v+Δv)uv=uΔv+vΔu+ΔuΔv.

两边除以 Δx,令 Δx0ΔuΔv/Δx 多了一个趋于 0 的因子,消失。剩下

(uv)=uv+uv.

矩形图里那一小块角就是 ΔuΔv

链式。 y=f(g(x))gx 处局部是直线,斜率 g,所以 ΔggΔxfg(x) 处局部是直线,斜率 f,所以 ΔyfΔgf(g(x))g(x)Δx。相除取极限即

(fg)=f(g)g.

多层神经网络:每一层是一次 g,反向传播从最外层的 f 往回乘,正是把这条链倒过来写。

商法则(备查)。 u/v=uv1,再链式:(v1)=v2v,于是 (u/v)=(uvuv)/v2

法则从线性近似来

图解说明:左:乘积是矩形的两边增量;中:复合是两台齿轮转速相乘;右:多变量时沿一个轴切一刀叫偏导,最陡的箭头叫梯度。

多元只记一句:固定其他变量、只动 xi,就是偏导 f/xi。所有偏导排成向量,叫梯度 f——方向是山坡最陡的上坡。训练走它的反方向。


三、C++ 怎么给「公式」求导

公式进了程序,有三条路。教学代码三条都写了,对照同一条 f(x)=x32xx=2(真值 f=4,f=10)。

你在干什么优点
多项式系数x32x 存成 {0,-2,0,1},逐项 xkkxk1精确(有理系数时)只覆盖多项式
对偶数(自动微分)每个量带一对 (v,d),四则运算按法则重载写一遍公式,值和导数一起出来要给初等函数写导数规则
中心差分[f(x+h)f(xh)]/(2h),把 f 当黑盒不用解析式h 太大不准、太小被浮点噪声咬

1. 多项式:符号的最小可用子集

p(x)=k=0nckxkp(x)=k=1nkckxk1.

C++ 里就是循环:q.c[i-1] = i * p.c[i]。这不是完整的符号计算(不会化简 sin),但把「公式 = 系数数组」说清楚了。积分章会对同一数组做 xkxk+1/(k+1)

2. 对偶数:把微分法则写进类型

引入 x+εε2=0(比任何还小的增量还小,平方扔掉——正是乘积法则里丢掉的那一小块)。于是

f(x+ε)=f(x)+f(x)ε.

代码里用结构体 Dual { double v, d; }。自变量种子是 {x, 1},常数是 {c, 0}。乘法必须写乘积法则,不能只乘 v

cpp
inline Dual operator*(Dual a, Dual b) {
    return Dual{a.v * b.v, a.v * b.d + a.d * b.v};
}

然后你照常写 x*x*x - 2.0*xf.v 是值,f.d 是导数。sin / exp 只要按链式法则给 d 乘上各自的导数即可。这就是正向自动微分;深度学习框架的反向模式是同一套思想换了记账方向。

可选 Eigen 在这里帮不上忙:这不是矩阵库的事。

3. 数值差分:不会公式也能摸斜率

中心差分比单侧 (f(x+h)-f(x))/h 准一阶。demo 的误差曲线会先下降再上升:h101 收到 106 附近最好,再小就开始被 double 的舍入支配。所以「公式微分」能解析就解析(多项式 / 对偶数),差分留给黑盒仿真。

bash
cd docs/math/derivative/code
python demo.py
g++ -std=c++17 demo.cpp -o deriv_demo

割线演示

差分误差随 h

C++ 还会印 sin(x)exp(x)0 处:值 0、导数 1(乘积法则:cose+sine)。


四、小结

概念一句话
平均变化率割线;窗口里的平均速度
导数 f(a)窗口缩没时切线的斜率
微分用切线代替曲线:f(a)+f(a)dx
链式法则复合机器的变化率相乘
对偶数值与导数绑在一起走四则运算
下游积分梯度下降、反向传播

下一章 积分与基本定理:把薄片叠回去,并看到「求导」和「累加」是一对逆运算。

📥 Code

FileViewDownload
demo.pyOpenDownload
dual.hppDownload
demo.cppDownload
exercise.pyOpenDownload

参考

  1. Thompson, Calculus Made Easy(「加一点点」的微分叙事)
  2. 3Blue1Brown, Essence of calculus
  3. Griewank & Walther, Evaluating Derivatives(自动微分)