系列回顾:前三篇我们搞定了符号系统、指数对数、以及函数的本质。这一篇我们来讲导数——它是整个深度学习优化过程的数学基础。没有导数,就没有梯度下降,就没有神经网络的训练,就没有 DeepSeek。
"神经网络是怎么训练的?"
如果你去问人,十个人里有九个会说"梯度下降"。
再追问"梯度下降是什么?",大多数人会说"沿着梯度的反方向更新参数"。
再追问"梯度是什么?",这时候很多人就开始卡壳了。
梯度是导数在多维情况下的推广。所以要真正理解梯度下降,必须先把导数这件事想清楚。
导数这个概念,不是一个复杂的计算技巧,它背后有一个非常干净的物理直觉:变化率。理解了这个直觉,导数就不再是一堆需要死记的公式,而是一个帮助你"感知函数形状"的工具。
这篇文章,我们从变化率的直觉出发,一步一步走到梯度下降,最后在 DeepSeek 的训练过程里找到导数的身影。
假设你开车,行驶距离是时间的函数:$s(t)$。
某一时刻你看了一眼速度表,显示 80 km/h。这个速度是怎么来的?
直觉上,速度就是"一小段时间内走了多远"除以"那段时间":
$$v \approx \frac{s(t + \Delta t) - s(t)}{\Delta t}$$
$\Delta t$(读作"delta t")表示一小段时间,$\Delta$ 在数学里专门用来表示"变化量"。
当 $\Delta t$ 越来越小,趋近于零,这个比值趋近于某个确定的数,就是那一刻的瞬时速度——这就是导数的定义。
$$v(t) = s'(t) = \lim_{\Delta t \to 0} \frac{s(t + \Delta t) - s(t)}{\Delta t}$$
导数就是瞬时变化率:当自变量变化极小一点点时,函数值变化了多少。
导数还有一个等价的几何解释:函数在某点的切线斜率。
想象函数 $f(x) = x^2$ 的图像,是一条抛物线。
在 $x = 1$ 这个点上,抛物线的切线斜率是多少?
$$f'(x) = 2x \implies f'(1) = 2 \times 1 = 2$$
斜率为 2,意味着在 $x = 1$ 附近,$x$ 每增加 1 个单位,$f(x)$ 大约增加 2 个单位。
斜率为正:函数在这里是上坡,往右走,值变大。 斜率为负:函数在这里是下坡,往右走,值变小。 斜率为零:函数在这里是平坡,处于极值点(极大或极小)。
这是梯度下降最核心的直觉:斜率告诉你"这里是上坡还是下坡",下坡的方向就是损失减小的方向。
计算 $f(x) = x^2$ 在 $x = 3$ 处的导数:
用定义:
$$f'(3) = \lim_{h \to 0} \frac{f(3+h) - f(3)}{h} = \lim_{h \to 0} \frac{(3+h)^2 - 9}{h}$$
展开分子:
$$= \lim_{h \to 0} \frac{9 + 6h + h^2 - 9}{h} = \lim_{h \to 0} \frac{6h + h^2}{h} = \lim_{h \to 0} (6 + h) = 6$$
所以 $f'(3) = 6$,和公式 $f'(x) = 2x$ 在 $x=3$ 的结果一致。
意思是:在 $x = 3$ 这个点,$x$ 增加一点点,$f(x)$ 会以 6 倍的速率增加。
实际使用中,不需要每次都用定义来算,有一套现成的求导公式:
最重要的是前四个,在深度学习里反复出现。
特别注意 $e^x$ 的导数:求导之后还是 $e^x$ 本身。这是自然常数 $e$ 最神奇的性质,也是它在深度学习里大量使用的根本原因——计算梯度时,$e^x$ 带来的项不会产生新的复杂形式。
现实中的神经网络函数都很复杂,是多个简单函数组合而成的。我们需要几条法则来处理这种情况。
$$(f + g)' = f' + g'$$
两个函数之和的导数,等于各自导数之和。
例子:$(x^2 + \ln x)' = 2x + \frac{1}{x}$
$$(f \cdot g)' = f' \cdot g + f \cdot g'$$
两个函数乘积的导数,有一个容易记住的口诀:前导后不导,加上前不导后导。
例子:$(x^2 \cdot e^x)' = 2x \cdot e^x + x^2 \cdot e^x = (2x + x^2)e^x$
链式法则(Chain Rule)是反向传播算法的数学核心,没有它就没有现代深度学习。
对于复合函数 $h(x) = f(g(x))$,它的导数是:
$$h'(x) = f'(g(x)) \cdot g'(x)$$
用另一种记号写,更直观:
$$\frac{dh}{dx} = \frac{df}{dg} \cdot \frac{dg}{dx}$$
这个记号有一个很好的直觉:就像分数的约分,$dg$ 在分子分母"抵消"了,得到 $\frac{dh}{dx}$。虽然这不是严格的数学推导,但作为记忆技巧很好用。
一个具体例子:
计算 $h(x) = e^{x^2}$ 的导数。
这是一个复合函数:外层 $f(u) = e^u$,内层 $g(x) = x^2$。
$$h'(x) = f'(g(x)) \cdot g'(x) = e^{x^2} \cdot 2x = 2xe^{x^2}$$
链式法则可以多层嵌套:
对于 $y = f(g(h(x)))$:
$$\frac{dy}{dx} = \frac{df}{dg} \cdot \frac{dg}{dh} \cdot \frac{dh}{dx}$$
这正是神经网络反向传播的数学结构——从输出层开始,一层一层往回乘导数,最终算出每个参数的梯度。
假设网络有三层:
$$\text{输出} = f_3(f_2(f_1(x; \theta_1); \theta_2); \theta_3)$$
对第一层参数 $\theta_1$ 求梯度:
$$\frac{\partial L}{\partial \theta_1} = \frac{\partial L}{\partial f_3} \cdot \frac{\partial f_3}{\partial f_2} \cdot \frac{\partial f_2}{\partial f_1} \cdot \frac{\partial f_1}{\partial \theta_1}$$
这就是反向传播——链式法则的直接应用。我们在第5篇讲偏导数和链式法则时会完整推导一遍。
导数不只是一个计算结果,它是对函数"地形"的描述。
理解这个对于理解梯度下降至关重要,所以我们多花点时间在这里。
对于损失函数,我们想找它的最小值点,也就是 $f'(x) = 0$ 的地方(极小值)。
但是,$f'(x) = 0$ 的点可能是极大值、极小值或鞍点,需要进一步判断。在高维情况下(神经网络参数有几千亿维),鞍点问题是一个重要的优化挑战,我们后面讲优化器时会聊到。
导数还可以再求一次导,叫做二阶导数 $f''(x)$,它描述的是"斜率的变化率",也就是曲率。
在深度学习里,二阶导数(Hessian 矩阵)的信息可以用来加速优化,但因为参数量太大,计算 Hessian 代价极高,大多数时候我们只用一阶导数(梯度)。
前面讲的都是单变量函数 $f(x)$。但神经网络有几千亿个参数,损失函数是一个多变量函数 $L(\theta_1, \theta_2, \ldots, \theta_n)$。
这时候,"导数"需要推广成"梯度"。
对多变量函数,我们可以固定其他所有变量,只对其中一个变量求导,这叫偏导数。
对于 $f(x, y) = x^2 + 3xy + y^2$:
对 $x$ 的偏导(把 $y$ 当常数):
$$\frac{\partial f}{\partial x} = 2x + 3y$$
对 $y$ 的偏导(把 $x$ 当常数):
$$\frac{\partial f}{\partial y} = 3x + 2y$$
注意:偏导数用 $\partial$(偏导符号,读作"partial"),不是 $d$,用来区分多变量和单变量的情况。
梯度(Gradient)就是把所有偏导数收集在一起,组成一个向量:
$$\nabla f = \left(\frac{\partial f}{\partial x}, \frac{\partial f}{\partial y}\right)$$
对上面的例子:
$$\nabla f = (2x + 3y, \quad 3x + 2y)$$
梯度是一个向量,它的方向是函数值增加最快的方向,它的大小是函数值增加的速率。
梯度的反方向,就是函数值减小最快的方向——这正是梯度下降要走的路。
想象一座山,你站在山坡上,想下山(找损失函数的最小值)。
梯度就是"当前位置最陡的上山方向"。往梯度的反方向走,就是沿着最陡的下坡走,下山最快。
用数学语言写出来:
$$\theta_{\text{new}} = \theta_{\text{old}} - \alpha \nabla L(\theta_{\text{old}})$$
这就是梯度下降的更新公式:
每走一步,参数就更新一次,损失函数就减小一点,模型就"更好"了一点。重复这个过程成千上万次,最终找到损失函数的最小值——这就是神经网络的训练过程。
梯度下降公式里有一个 $\alpha$,叫做学习率(Learning Rate),它控制每次更新的步长。
这个参数看起来简单,但其实非常关键,深度学习里有大量的经验和技巧都围绕它展开。
如果步子太大,你可能直接跨过了山谷,从左边坡跳到了右边坡,来回振荡,永远找不到谷底。
在数学上,参数更新过大会导致损失函数不降反升,训练发散。
步子太小,每次只挪一点点,要走非常非常久才能收敛,训练效率极低。
而且在实际的损失函数地形里,存在很多局部平坦区域(梯度接近零),步子太小的话很容易"卡在平原里出不去"。
DeepSeek V3 的论文里提到,训练使用 AdamW 优化器,最大学习率设置为 $2.2 \times 10^{-4}$,然后采用余弦退火(cosine annealing)策略让学习率逐渐减小。
这不是随便拍的数字,是大量实验调出来的。学习率调参(Learning Rate Scheduling)是深度学习工程里最重要的技能之一,我们在优化器那篇会专门讲这些策略。
让我们从头到尾做一遍梯度下降,彻底理清楚它在做什么。
任务:找到函数 $L(\theta) = (\theta - 3)^2$ 的最小值。
(这是一个极其简化的"损失函数",真正的答案当然是 $\theta = 3$,我们用它来演示梯度下降的过程。)
第一步:计算梯度
$$\frac{dL}{d\theta} = 2(\theta - 3)$$
第二步:设置初始参数和学习率
令 $\theta_0 = 0$,学习率 $\alpha = 0.3$。
第三步:迭代更新
第1次迭代:
$$\nabla L(\theta_0) = 2(0 - 3) = -6$$ $$\theta_1 = \theta_0 - \alpha \cdot \nabla L(\theta_0) = 0 - 0.3 \times (-6) = 1.8$$
第2次迭代:
$$\nabla L(\theta_1) = 2(1.8 - 3) = -2.4$$ $$\theta_2 = 1.8 - 0.3 \times (-2.4) = 2.52$$
第3次迭代:
$$\nabla L(\theta_2) = 2(2.52 - 3) = -0.96$$ $$\theta_3 = 2.52 - 0.3 \times (-0.96) = 2.808$$
迭代过程:$0 \to 1.8 \to 2.52 \to 2.808 \to 2.923 \to \cdots \to 3$
参数从 $0$ 开始,一步一步逼近真正的最小值 $\theta = 3$。
注意每一步梯度都在缩小(从 -6 到 -2.4 到 -0.96):这是因为越接近最小值,坡度越平,步子自然越小,这个机制让梯度下降能自然地"减速"收敛,不会在最小值附近无限振荡。
前面讲的都是单变量的情况,现在来说说神经网络里的实际情况。
一个最简单的神经元计算:
$$z = wx + b$$ $$a = \sigma(z) = \frac{1}{1 + e^{-z}} \quad \text{(Sigmoid 激活函数)}$$ $$L = (a - y)^2 \quad \text{(均方误差损失)}$$
我们想知道参数 $w$ 应该怎么更新,需要计算 $\frac{\partial L}{\partial w}$。
用链式法则,一步一步往回算:
$$\frac{\partial L}{\partial w} = \frac{\partial L}{\partial a} \cdot \frac{\partial a}{\partial z} \cdot \frac{\partial z}{\partial w}$$
逐项计算:
$$\frac{\partial L}{\partial a} = 2(a - y)$$
$$\frac{\partial a}{\partial z} = \sigma(z)(1 - \sigma(z)) \quad \text{(Sigmoid 的导数有这个优美的形式)}$$
$$\frac{\partial z}{\partial w} = x$$
合并:
$$\frac{\partial L}{\partial w} = 2(a - y) \cdot \sigma(z)(1 - \sigma(z)) \cdot x$$
这就是对 $w$ 的梯度,然后按照梯度下降公式更新:
$$w \leftarrow w - \alpha \cdot \frac{\partial L}{\partial w}$$
看起来复杂,但每一步都只是基础的求导法则。这就是反向传播的本质——对整个网络逐层应用链式法则,从输出到输入,逐层计算梯度。
DeepSeek V3 有大约 6710 亿个参数,也就是说每次前向传播之后,反向传播需要计算 6710 亿个梯度值。
这在数学上没什么特别的,就是链式法则应用 6710 亿次;但在工程上,这需要极其高效的并行计算,需要数千块 GPU 同时工作,这也是大模型训练成本高昂的根本原因。
DeepSeek V3 的论文里提到,整个训练使用了 2048 块 H800 GPU,运行了大约 2.788 million GPU-hours。这 278 万 GPU 小时,本质上就是在重复做一件事:计算梯度,然后更新参数。
理解了链式法则,就能理解深度学习历史上最大的难题之一:梯度消失(Vanishing Gradient)。
回想链式法则,对于一个 $L$ 层的网络:
$$\frac{\partial L}{\partial \theta_1} = \frac{\partial L}{\partial h_L} \cdot \frac{\partial h_L}{\partial h_{L-1}} \cdots \frac{\partial h_2}{\partial h_1} \cdot \frac{\partial h_1}{\partial \theta_1}$$
这是 $L$ 个导数项相乘。
如果每一层的导数都略小于 1,比如都是 0.5,那乘了 100 层之后:
$$0.5^{100} \approx 10^{-30}$$
这个数字小到可以忽略不计。靠近输入层的参数,它的梯度几乎为零,参数根本无法更新——网络前面几层完全"学不动"了。
这就是梯度消失。早期的深度网络(超过 10 层)很难训练,根本原因就在这里。
Sigmoid 函数的导数是 $\sigma(z)(1-\sigma(z))$,它的最大值只有 0.25(在 $z=0$ 时),绝大多数情况下都小于 0.25。
这意味着,用 Sigmoid 作为激活函数,每过一层梯度至少缩小 4 倍,层数一多,梯度就彻底消失了。
现代深度学习用了几种方法解决这个问题:
ReLU 激活函数:$\text{ReLU}(x) = \max(0, x)$,导数是 1(正区间)或 0(负区间),正区间内不会压缩梯度。
残差连接:第三篇提到的 $h' = h + f(h)$,对 $h$ 求导得到 $1 + f'(h)$,那个"$+1$"保证了即使 $f'(h)$ 很小,梯度也有一条"高速公路"(那个 1)直接传回来,不会消失。
归一化层(BatchNorm/LayerNorm):稳定每层输出的数值范围,防止激活值过大或过小导致梯度异常。
这三个技术,是让深层神经网络(几十层乃至几百层)变得可以训练的关键。DeepSeek V3 这样有 61 层的 Transformer,就是站在这些技术的肩膀上。
现在来看一段 DeepSeek V3 论文里与梯度相关的内容,把今天学的东西落地。
DeepSeek V3 论文里提到了梯度裁剪(Gradient Clipping):
训练时对梯度的 L2 范数做裁剪,阈值设置为 1.0。
用数学语言写就是:
$$\text{如果 } \|\nabla L\|_2 > 1.0,\text{ 则 } \nabla L \leftarrow \frac{\nabla L}{\|\nabla L\|_2}$$
意思是:如果梯度的长度超过了 1.0,就把它等比例缩小,让它的长度恰好等于 1.0(方向不变)。
这是在解决另一个问题:梯度爆炸(Gradient Explosion)。
和梯度消失相反,如果每层导数大于 1,乘了 L 层之后梯度会变得极大,参数更新幅度失控,损失函数直接发散。梯度裁剪就是在梯度变得太大之前,强行把它压回合理范围。
你现在看到"梯度裁剪、阈值 1.0"这样的描述,知道它背后的数学了——这是在限制所有参数的梯度向量的 L2 范数不超过 1。
这篇文章我们把导数从直觉到应用完整走了一遍。
第一,导数是瞬时变化率,几何上就是切线斜率。正导数意味着上坡,负导数意味着下坡,零导数可能是极值点。
第二,链式法则是求复合函数导数的工具,也是反向传播算法的数学基础。对每一层的参数求梯度,本质上就是从输出到输入逐层应用链式法则。
第三,梯度是导数在多维情况下的推广,方向是函数值增加最快的方向。梯度下降沿梯度的反方向更新参数,是神经网络训练的核心算法。
第四,梯度消失和梯度爆炸是深度网络的经典难题。ReLU、残差连接、归一化层和梯度裁剪是现代解决方案。
第五,学习率是梯度下降的步长,太大会振荡发散,太小会收敛缓慢,是深度学习里最重要的超参数之一。
下一篇预告:
第5篇,我们讲偏导数与链式法则。
今天我们初步接触了偏导数和链式法则,但只是皮毛。下一篇我们会完整推导反向传播算法,用一个真实的两层神经网络,从损失函数出发,一步一步把每个参数的梯度算出来。这是理解深度学习训练过程最重要的一篇,不要错过。
导数告诉你山势,梯度告诉你方向,学习率决定你的步伐。三者配合,才能找到谷底。
还没有评论,来抢沙发吧!
博客管理员
40 篇文章
还没有评论,来抢沙发吧!