news 2026/10/22 11:37:08

一元函数微分学与机器学习:从导数到梯度下降实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
一元函数微分学与机器学习:从导数到梯度下降实战

参加过开源社区组队学习的人应该都有体会:一帮人按任务打卡、互相答疑、一起啃同一个主题,比自己闷头看书容易坚持得多。我最近刚跟完“机器学习数学基础”系列里的一元函数微分学这个Task 03,趁着热乎劲儿,把这一路的思考、推导和踩坑记录整理出来,希望能帮到正在补数学基础的朋友。这篇文章不打算写成一章教材,而是把一个一个知识点拉回“机器学习到底哪里用得上”这条主线上去讲,适合数学底子薄、但想认真搞懂模型原理的初学者参考。

1. 先摆正坐标:一元函数微分学在机器学习里到底解决什么问题

1.1 机器学习为什么会用到导数

很多人学机器学习,一上来就看模型代码,结果看到“梯度下降”“反向传播”这些词就发懵。其实剥开外壳,大部分机器学习工作的核心就一件事:调整参数,让损失变小。而“怎么调整才合理”这个问题,落到数学上恰好就是微分学要回答的。

拿最简单的线性回归举例。假设我们想拟合数据,模型是 y = wx + b,损失函数用均方误差 L(w, b) = (1/n) Σ (wx_i + b - y_i)²。训练的目标是找到一组 (w, b),让 L 尽可能小。这个函数是一元还是二元?它其实是关于 w 和 b 的二元函数,但如果固定其中一个变量,单独看 w 或单独看 b,就回到了“一元函数求极值”的问题。

一元函数微分学的价值就在这里:它能告诉我们函数在某一点的变化趋势。导数大于0,说明函数在增加;导数小于0,说明函数在减小;导数等于0的地方,就是可能的极值点。机器学习里的“学习”,说白了就是把导数当成指南针,反复朝着让损失函数下降的方向移动参数。

1.2 从“刻画变化”到“指引方向”

我特别喜欢用“下山”来比喻梯度下降。想象你站在山坡上,周围一片漆黑,要想走到山谷最低处,最朴素的办法就是踩一踩脚下的地面,感觉哪个方向是往下走的,就往那边迈一步。这里“脚下的坡度”就是导数,或者说是一元情境下损失函数的斜率。

在多元情况下你会听到“梯度”这个词,它本质上是各个方向偏导数组成的向量。但很多人忽略了一点:多元梯度的许多直觉,都来自一元导数。你如果连 f(w) = (w - 1)² 在 w = 3 处导数为正、函数在增大都算不清楚,那后面理解多维参数的更新公式,只会更难。

所以我在学习这个Task时一直提醒自己:别看一元函数“简单”,它是整个优化理论的地基。地基不牢,后面无论看支持向量机的对偶问题还是神经网络的链式求导,都会觉得像看天书。

2. 导数的定义别糊弄过去:它决定了你会不会在细节上踩坑

2.1 极限定义拆开看

教科书上导数的定义长这样:
f'(x₀) = lim(Δx→0) [f(x₀ + Δx) - f(x₀)] / Δx

很多初学者看到这个式子,第一反应是:这不就是把 Δx 取得特别小,算两点间的斜率吗?对,物理直觉就是这么回事。但为什么必须用极限,而不能直接取 Δx = 0?因为直接把 Δx 代零,分母就是0,计算没有意义。极限告诉我们的是“趋势”,而不是“代入”。

我在组队学习群里看到有同学问:既然极限那么抽象,我写代码算导数的时候,是不是只要把 Δx 设成一个很小的数,比如 1e-8,就算是导数了?这正好引出一个实操中的经典问题——数值微分。

用程序实现导数近似,通常写成:

def numerical_derivative(f, x, eps=1e-6): return (f(x + eps) - f(x - eps)) / (2 * eps)

注意这里用的是中心差分 (f(x+ε) - f(x-ε)) / (2ε),而不是单侧差分 (f(x+ε) - f(x)) / ε。理由很简单:中心差分的误差是 O(ε²),单侧差分只有 O(ε)。同样取 ε = 1e-6,中心差分结果会精确得多。

但 ε 也不是越小越好。如果 ε 取到 1e-16,已经接近浮点数精度极限,分子在做减法时会产生灾难性抵消,算出来的导数反而全是噪声。我在实际计算中一般取 ε = 1e-6 或 1e-7,这是精度和数值稳定性之间一个比较务实的平衡点。

2.2 几何意义与可导性

导数的几何意义是曲线在某点处切线的斜率。这句话很重要,但要结合图像去理解:函数曲线在某个点附近如果变得越来越陡,说明导数的绝对值在变大;如果曲线趋于平滑,导数绝对值就在变小。当曲线出现尖点的时候,左导数和右导数不相等,该点就不可导。

最经典的例子是 f(x) = |x|,也就是绝对值函数,在 x = 0 处,左边切线斜率是 -1,右边是 +1,左右导数对不上,所以不可导。这个例子和机器学习有什么关系?关系很大。ReLU 激活函数在原点处就存在同样的问题,它的导数在 x > 0 时为 1,在 x < 0 时为 0,精确地说在 x = 0 处没有定义。工程实践中,大家通常约定 ReLU'(0) = 0 或 1,甚至直接不关注这一点,反正大部分框架的实现都默认了某个取值。

还有一条重要结论:可导一定连续,连续不一定可导。也就是说,可导是比连续更强的一个条件。判断一个函数能不能放心求导,先看它是否连续,再看它的左右导数是否一致。

3. 求导法则吃透,机器学习公式才不会推错

3.1 四则运算与线性组合求导

运算规则本身不复杂:加法法则就是“和的导数等于导数之和”:

(f(x) + g(x))' = f'(x) + g'(x)

常数倍法则:

(c · f(x))' = c · f'(x)

但掌握这两条规则,在机器学习里已经能处理一大类问题了。比如前面说的均方误差损失,假设只有一个样本,L = (wx + b - y)²,关于 w 求导时,先把 wx + b - y 看成一个整体 u,外层是 u² ,这里面其实同时用到了复合函数求导和线性组合求导,一步步拆开:

dL/dw = 2(wx + b - y) · x

这个式子里的 2 来自外层平方求导,x 来自内层对 w 的导数。成型之后你会发现,这个结果其实特别直观:梯度的方向由误差 (wx + b - y) 乘以特征 x 决定。误差越大,对 w 的调整步子就应该越大。

乘法和除法法则也应该熟悉,虽然它们在基础机器学习公式里出现频率没有加法高,但在推导某些概率模型的似然函数时经常会用到:

(f · g)' = f'g + fg'
(f / g)' = (f'g - fg') / g²

3.2 链式法则:反向传播的数学底座

链式法则是整个Task里含金量最高的部分,也是最需要花时间“想明白”的地方。它的公式很简单:

(f(g(x)))' = f'(g(x)) · g'(x)

但这个简单的式子,扩展出去就是神经网络反向传播的全部数学原理。神经网络为什么叫“深度”?因为它是一层套一层的复合函数。前一层的输出是后一层的输入,数据从输入到输出,就像俄罗斯套娃一样。

我们看一个三层复合的例子:

y = f(g(h(x)))

按链式法则,dy/dx = f'(g(h(x))) · g'(h(x)) · h'(x)

这个结构如果画成计算图,从最外层一路乘到最内层。反向传播其实就是链式法则的反向应用:先算输出层对损失的影响,再一层一层往输入方向回传梯度。

我在做这个Task时,曾经花了一整个晚上拿一个两层网络手工推梯度。网络结构是:

z = w₂ · σ(w₁ · x + b₁) + b₂

其中 σ 是 sigmoid 激活函数。目标是推导损失 L = (z - y)² 对 w₁ 的偏导。拆开就是:

∂L/∂w₁ = ∂L/∂z · ∂z/∂a · ∂a/∂z₁ · ∂z₁/∂w₁

这里 a = σ(z₁),z₁ = w₁x + b₁。一步一步算下来,你会发现链式法则就像流水线一样,把误差信号从末端传回前端。只要一个环节的导数算错,整个梯度就偏了。

3.3 一个实操案例:sigmoid 函数的导数为什么那么漂亮

sigmoid 函数定义为:

σ(x) = 1 / (1 + e⁻ˣ)

它在机器学习里非常常见,尤其在老式神经网络和逻辑回归中。这个函数的导数特别有名,结果是:

σ'(x) = σ(x) · (1 - σ(x))

为什么?实操推一遍最有说服力。先把 σ(x) 看成 (1 + e⁻ˣ)⁻¹,用链式法则:

σ'(x) = -(1 + e⁻ˣ)⁻² · (-e⁻ˣ) = e⁻ˣ / (1 + e⁻ˣ)²

然后做个变形:σ(x)(1 - σ(x)) = (1/(1+e⁻ˣ)) · (1 - 1/(1+e⁻ˣ)) = (1/(1+e⁻ˣ)) · (e⁻ˣ/(1+e⁻ˣ)) = e⁻ˣ/(1+e⁻ˣ)²

两边结果完全一样。这个推导过程如果自己动手写一遍,会对“复合函数+代数恒等变形”有非常具体的感受。更重要的是,σ'(x) = σ(x)(1 - σ(x)) 这个形式在反向传播里极其好用:只要知道 sigmoid 的输出值,就能直接算出它的梯度,连 x 都不用再往前找。

4. 常见函数求导速查与记忆技巧

4.1 一张表解决基本功

组队学习的好处在于,群里的讨论会把琐碎的知识点体系化。我把Task 03里出现频率最高的求导公式整理成了表格,随时翻出来看:

函数导数备注
C(常数)0不随变量变化
xⁿn·xⁿ⁻¹幂函数
eˣeˣ自己等于自己,指数函数里最特殊
aˣaˣ·ln(a)通则
ln(x)1/xx > 0
logₐ(x)1/(x·ln(a))以 a 为底的对数
sin(x)cos(x)三角函数
cos(x)-sin(x)注意负号
tan(x)sec²(x)较少直接用

这些公式不能死记,关键是理解它们之间的联系。比如 aˣ = e^{x·ln(a)},所以 (aˣ)' = e^{x·ln(a)} · ln(a) = aˣ · ln(a)。只要记住了 eˣ 的导数,其他指数函数的导数都能推出来。

4.2 机器学习里高频出现的几个特殊函数

除了基本初等函数,机器学习里还有几个高频函数,它们的导数形态值得单独拉出来说说。

第一个是tanh 函数:tanh(x) = (eˣ - e⁻ˣ) / (eˣ + e⁻ˣ)。它的导数是 1 - tanh²(x)。可以看出,这个导数的计算同样只需要输出值,不需要原始输入,所以在中间层做激活函数时效率很高。tanh 输出范围在 (-1, 1),梯度范围也在 [-1, 1],和 sigmoid 相比中心对称性更好,实际使用中收敛往往也更平滑。

第二个是ReLU 函数:ReLU(x) = max(0, x)。它的导数是一个分段函数:

  • x > 0 时,导数为 1
  • x < 0 时,导数为 0
  • x = 0 时,导数没有统一定义,实践中一般取 0

ReLU 的导数不连续,但它在深度网络里反而比 sigmoid 更常用,主要原因是在正区间梯度恒为 1,不像 sigmoid 在两端饱和(梯度趋近于 0),不容易发生梯度消失。

第三个是softmax 函数。它主要用于多分类输出的概率化,严格来说它的求导涉及雅可比矩阵,比一元函数复杂不少。我一开始看到 softmax 求导就头大,后来才意识到,如果能把一元函数的链式法则啃透,再看 softmax 的导数也能顺着“分情况处理 i 和 j 是否相同”的思路推出来。Task 03 不要求细究 softmax,但它会作为后面章节的引子,所以把求导基本功打牢很重要。

5. 梯度下降手推一遍:从导数到参数更新

5.1 完整的推导流程

理论知识扯再多,都不如手推一遍来得实在。我挑一个最简单的例子:最小化函数

f(w) = (w - 1)² + 2

这个函数在 w = 1 时取到最小值 2。我们来看梯度下降怎么一步步“找到”这个点。

第一步,求导:

f'(w) = 2(w - 1)

第二步,假设初始点 w₀ = 3,学习率 η = 0.1。参数更新公式是:

w_new = w_old - η · f'(w_old)

代入算几次:

  • 第1轮:w₀ = 3,f'(3) = 2(3-1) = 4,w₁ = 3 - 0.1 × 4 = 2.6
  • 第2轮:w₁ = 2.6,f'(2.6) = 2(2.6-1) = 3.2,w₂ = 2.6 - 0.1 × 3.2 = 2.28
  • 第3轮:w₂ = 2.28,f'(2.28) = 2.56,w₃ = 2.28 - 0.1 × 2.56 = 2.024
  • 第4轮:w₃ = 2.024,f'(2.024) = 2.048,w₄ = 2.024 - 0.1 × 2.048 = 1.8192

可以清楚看到,参数在逐渐往 w = 1 靠近。每走一步的幅度也在变小,因为越接近极值点,导数的绝对值越小,“坡度”越平缓。

这里有一个重要的工程心得:真正在跑的机器学习模型,损失函数比这个复杂得多,但参数更新的核心机制一模一样。看懂这个循环,梯度下降就不再是黑盒了。

5.2 学习率怎么定:大则震荡,小则磨蹭

同样是上面的例子,如果把学习率改到 η = 1.1,会发生什么?

  • 第1轮:w₁ = 3 - 1.1 × 4 = -1.4
  • 第2轮:f'(-1.4) = 2(-1.4-1) = -4.8,w₂ = -1.4 - 1.1 × (-4.8) = 3.88

w 不仅没靠近 1,反而在 3 和 -1 之间来回蹦,这就是“发散”。如果把学习率改成 η = 0.001:

  • 第1轮:w₁ = 3 - 0.001 × 4 = 2.996
  • 第10轮:也只是到了 2.93 左右

理论上最终还是会收敛到 1,但这个速度在真实训练里是不能接受的。所以我一直觉得,学习率是梯度下降里面最需要“手感”的超参数,而手感从哪来?就是多做类似上面这样的手工计算,亲自感受一下同样一个函数,在不同学习率下的行为差异。

5.3 用数值微分验证解析梯度

在组队学习任务里,有个练习要求自己算某个复合函数的解析梯度,再和数值微分的结果对比。这个做法非常值得推荐,因为它能最快暴露你推导中的错误。

比如要验证 f(w) = (1 + e⁻ʷ)⁻¹ 这个 sigmoid 在 w = 2 处的导数。解析导数是:

f'(2) = σ(2)(1 - σ(2))

你可以顺手算一下:

1 + e⁻² ≈ 1.1353,σ(2) ≈ 0.8808,1 - σ(2) ≈ 0.1192,所以 f'(2) ≈ 0.1050

再用数值微分验证:

import numpy as np def sigmoid(x): return 1 / (1 + np.exp(-x)) def numerical_derivative(f, x, eps=1e-6): return (f(x + eps) - f(x - eps)) / (2 * eps) print(numerical_derivative(sigmoid, 2.0)) # 输出约 0.10499

两个结果几乎完全一致,这就说明解析推导没推错。以后不管接触多复杂的模型,都可以用这个“双通道检查法”给自己兜底。

6. 我在这个Task 03踩过的坑与避坑指南

6.1 别把 d、∂、Δ 混着用

组队学习群里有个很普遍的乱象:微分符号到处乱写。严格来说,d 表示一元函数的微分,∂ 表示多元函数对某个变量的偏导,Δ 表示变化量。虽然很多时候大家口头混着说,但自己写推导时一定要分清。

尤其是在从“一元函数微分学”迈向“多元函数微分学”的节点上,符号的准确性不是吹毛求疵,而是直接关系到你对“对谁求导”这个问题的理解。比如损失函数 L(w, b) 关于 w 的偏导写作 ∂L/∂w,你如果写成 dL/dw,变量就不清楚了。

6.2 导数常见错误速查表

错误类型错误示例正确做法
复合函数漏链对 (x²+1)³ 求导写成 3(x²+1)²再乘内层导数 2x,结果是 6x(x²+1)²
搞反幂函数和指数函数把 eˣ 的导数当成 x·eˣ⁻¹eˣ 导数还是 eˣ;只有 xⁿ 用幂函数规则
忽略分段点对 ReLU 在 x=0 处直接求导分段讨论,工程上取约定值
认为导数为0就是极小值f(w) = w³ 在 w=0 处导数为0但不是极值需要看二阶导数或两边符号变化
数值微分 ε 取太小ε=1e-16,结果全是噪声取 1e-6 或 1e-7

6.3 一个更隐蔽的坑:极值点与拐点混淆

导数为0的点叫驻点,驻点可能是极大值,可能是极小值,也可能什么都不是。f(w) = w³ 在 w = 0 处导数为0,但它既不是极大也不是极小,而是一个拐点。怎么判断?看二阶导数的符号:大于0是极小值,小于0是极大值,等于0要再讨论。

这个知识点在机器学习里的意义在于:梯度下降只能保证找到局部极值点,而且如果初始点选在某个特殊位置,比如高维空间的鞍点附近,训练就可能卡住。虽然现在有很多优化器(如Adam)通过动量机制缓解了这类问题,但理解“导数为0 ≠ 极值”依然是优化理论的基石。

6.4 学习建议:先手推,再编码

跟完这个Task之后,我最大的感触是:数学基础类的学习,一定不能只看不动手。单纯看视频、看文档,当时觉得都懂了,合上电脑两天后再看同一个公式,又好像不认识。

所以我给刚开始接触机器学习数学基础的人一个实操建议:每学完一个求导法则,立刻给自己出三道题。第一道是纯计算题,比如对某个复合函数求导;第二道是应用场景题,比如写一段小程序用数值微分验证手推结果;第三道是推导题,比如自己把 sigmoid 的导数从头推一遍。三道题都过,这个知识点才算真正粘住了。

组队学习这种形式,最大的优势是有一群人陪你一起做题、互相检查推导过程。哪怕只是把自己卡住的地方发到群里,听别人怎么绕过去的,也比一个人死磕有效率得多。希望这篇笔记也能帮你减少一点摸索过程中的挫败感。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/22 11:37:08

C++ vector深度指南:动态数组、内存管理与性能优化实战

用C写了这么多年&#xff0c;如果让我只推荐一个标准库容器&#xff0c;我会毫不犹豫选std::vector。裸数组太死板&#xff0c;list对缓存不友好&#xff0c;map用起来又偏重——vector以连续内存、动态扩容、随机访问这三个特性&#xff0c;几乎覆盖了日常开发八成的数据结构需…

作者头像 李华
网站建设 2026/10/22 11:35:59

从手动命令到自动化:WEF无线安全测试框架完全实战指南

WiFi测试这行&#xff0c;很多人一开始都栽在“工具接力”上。你airodump-ng扫完信道&#xff0c;要切换aireplay-ng去踢客户端&#xff0c;然后还得回来抓包&#xff0c;中间的终端窗口切换、参数记忆、cap包格式转换&#xff0c;能把人绕晕。整套流程我跑了上百次之后&#x…

作者头像 李华
网站建设 2026/10/22 11:35:25

AI代码视频生成全流程:从模型输出到MP4的工程化实践

1. 先搞清楚一件事&#xff1a;模型不生成视频&#xff0c;它生成的是“代码” 1.1 一个被反复误读的标题 “Claude Opus 5.5 生成视频了”——这句话我第一次看到的时候&#xff0c;第一反应不是兴奋&#xff0c;而是皱眉。因为从技术路径上讲&#xff0c;一个以文本和代码为…

作者头像 李华
网站建设 2026/10/22 11:32:59

稠密检索技术全解析:从倒排索引到向量召回的工程实践

讲检索&#xff0c;大部分人脑子里冒出来的第一个东西是“关键词匹配”。我把搜索从倒排索引做到向量召回&#xff0c;踩了不少坑&#xff0c;今天专门把稠密检索技术掰开揉碎讲一遍。这篇不只讲“模型怎么把句子变成向量”&#xff0c;还会讲为什么我们需要从字面匹配走向语义…

作者头像 李华
网站建设 2026/10/22 11:32:42

OpenMontage实测:5.4万Star开源视频工具的能力边界与争议

1. 一个5.4万Star的项目&#xff0c;为什么评论区吵成了两派OpenMontage这个名字&#xff0c;最近在开发者圈子里出现的频率有点高。5.4万Star的成绩放在任何一个开源项目上都算得上耀眼&#xff0c;但有意思的是&#xff0c;围绕它的评价呈现出极其割裂的状态&#xff1a;一派…

作者头像 李华
网站建设 2026/10/22 11:32:06

Linux服务器MySQL多实例部署与systemd开机自启全指南

同一台Linux服务器上跑两个甚至三个MySQL实例&#xff0c;这活乍一听有点自找麻烦&#xff1a;端口要错开、数据目录要隔离、日志要各写各的&#xff0c;最后还得让它们在开机时自己起来。但等你真的遇到“多项目要独立数据库、资源又不够再开一台机器”这种需求时&#xff0c;…

作者头像 李华