机器学习必备的数学基础复习向

最近在学习机器学习和深度学习的时候不约而同都会遇到一些数学问题,我发现每一次都要查找一下AI我才能发现自己有什么不懂,所以这一篇文章我主要做复习用。不用每一次在遇到不懂的问题的时候再去问AI。

主要是记录一下AI我觉得给的个人便于理解的答案+自己的整理。

前置知识

初三的数学基础

概念

1 想知道求导,导数。

我想知道数学世界里或者现实世界里为什么会出现求导呢。会出现导数(derivative),本质上是因为人类一直在碰到同一种问题。就是这个世界的东西不是静止的,而是在变化。我们不仅想知道它的值,还想知道。

  • 它现在变化得快不快
  • 它是在变大还是变小
  • 它接下来会往哪个方向变
  • 它在某一瞬间的变化率是多少

所以,导数是为了研究变化而诞生的。 假设你在开车,1小时开了100公里,那速度是多少?你会说的都是平均速度,也就是100/1 =100 这个就是平均变化率,但是你在开车的时候车速并不是一直都是恒定的,有时候你可能起步慢,中间加速,红灯停止,某一瞬间的速度更好是83km。于是人类进一步会在想某一个瞬间,到底有多快? 这个就不是平均能回答的了,于是就出来了一个新的概念,瞬时变化率(instantaneous rate of change)。这就是导数在现实中最核心的来源。

我个人的总结就是当人类从静态的数字,包括长度,面积,角度。后来发现不够,因为现实世界很多问题都和运动有关。于是数学开始研究下面这些概念。

  • 变量(variable)
  • 函数(function)
  • 极限(limit)
  • 导数(derivative)
  • 积分(integral)

所以导数其实是数学从静态走向动态的标志之一。

以后你再学深一点,会发现导数同时还代表:

  • 斜率(slope)
  • 瞬时速度(instantaneous velocity)
  • 局部线性近似(local linear approximation)
  • 最优化中的方向信息(optimization direction)

2 为什么导数一定要靠极限来定义?

为什么导数一定要靠极限(limit)来定义?为什么不能直接说某一点的变化率,非要绕一圈用极限?那么先从函数说,假设函数是

f(x)=x2f(x)=x^2

我们想研究它在 x=2 这个点附近变化有多快。最直接想到的是先看一小段区间,比如从 x=2 到 x=3。这时候平均变化率是

f(3)f(2)32=941=5\frac{f(3)-f(2)}{3-2} = \frac{9-4}{1}=5

这表示在这整段区间里,平均每增加 1 个单位的 x,f(x) 平均增加 5。这没有问题。但我们真正想问的不是这一整段,想知道的是2在这一点的变化率是多少,这和从 2 到 3 这一段平均多快不是一回事。因为曲线不是直线。从 2 到 3 平均变化率是 5。但在 2 附近的即时变化率,不一定正好是 5。

f(x+h)f(x)h\frac{f(x+h)-f(x)}{h}

那为什么不能直接把区间设成 0呢 这里 h 表示你往右挪了一小步。如果你直接让 h=0,就变成⬇️。这是没有意义的。因为除以 0 不合法。

f(x)f(x)0=00\frac{f(x)-f(x)}{0} = \frac{0}{0}

于是就有了极限既然不能直接代 0, 但我们又真的很想知道➡️当 h 无限接近 0 的时候,这个平均变化率会接近什么值?于是数学就引入了极限

limh0f(x+h)f(x)h\lim_{h\to 0}\frac{f(x+h)-f(x)}{h}

这就是导数的定义。你可以把它翻译成中文:当 h 越来越接近 0 时,这个比值越来越接近哪个数。这个数,就是导数。

接下来用 x2x^2 真正算一遍

设:f(x)=x2f(x)=x^2 那么:f(x+h)=(x+h)2=x2+2xh+h2f(x+h)=(x+h)^2=x^2+2xh+h^2

代入定义:

f(x+h)f(x)h=x2+2xh+h2x2h\frac{f(x+h)-f(x)}{h} = \frac{x^2+2xh+h^2-x^2}{h}

化简

=2xh+h2h=\frac{2xh+h^2}{h}

再约掉一个 h

=2x+h=2x+h

现在再让 h→0

limh0(2x+h)=2x\lim_{h\to0}(2x+h)=2x

所以:f(x)=2xf'(x)=2x 这就是为什么 x2x^2 的导数是 2x。

3 偏导数

那么偏导数呢。当现实里只受一个变量影响时,用普通导数就够了。但很多问题不是单变量的。例如房价可能受面积,地段,楼层,年份这些影响。那么你就会想。

  • 面积变一点,价格怎么变
  • 楼层变一点,价格怎么变

这时候就是偏导数(partial derivative)。所以偏导数出现的原因也很自然,现实往往是多因素共同作用的结果,但拉出来一个变量,那么就是偏导数。

我自身的理解就是

偏导数 是不是就是分别对多个变量求导?

理解完全正确!偏导数本质上就是面对多个变量时,每次只对其中一个求导,把其他所有变量都强行当成死板的数字(常数)。因为现实世界里的事情,几乎都不是由单一因素决定的。导数解决的是单变量问题,而偏导数解决的是多变量协同问题。那么下面有一个例子。

假设你正在写一个计算利润的程序,利润 P 由卖出的苹果 x 和香蕉 y 共同决定,公式是:

P=3x2+5yP = 3x^2 + 5y

现在你想知道:如果我多卖一点苹果 x,利润会增加多少?

这时候你就是在求 P 对 x 的偏导数。为了算出这个数值,数学家使用了一个极其暴力的手段:假设 y 是一个死数字

  • 假装香蕉的销量 y是固定不变的(比如等于 10),那么 5y 就是 50。
  • 公式在你的眼里就变成了单变量函数:P=3x2+50P = 3x^2 + 50
  • 现在对 x 求导就非常简单了:常数 50 的导数是 0,3x23x^2 的导数是 6x。

所以,P 对 x 的偏导数就是 6x。同理,如果你对 y 求偏导,就把 x 当成常数,3x23x^2 整体变成 0,结果就是 5。

偏导数的魔法就在于:无论公式里有多少个字母,只要你锁定一个目标,其他所有字母在你眼里瞬间变成了没有生命的常数。

深度学习里,为什么要用偏导数?

下面有一个调节洗澡水温度的例子,平常你可能只有1个水龙头。但在真实的深度学习中,你的浴室墙上可能密密麻麻装了 100 个水龙头(分别控制红外加热、水压、冷水、热水、花洒角度等),这对应着神经网络里的权重 W1W_1, W2W_2, ..., W100W_100

当水温不合适(Loss 很大)时,你面临着一个极其复杂的局面:这 100 个水龙头,我该调哪个?调多少?

这个时候,偏导数就是你的排查清单。系统会自动执行以下操作:

  1. 冻结第 2 到第 100 个水龙头,只微微转动第 1 个水龙头,算出一个偏导数(比如是 -5)。
  2. 冻结第 1、第 3 到第 100 个水龙头,只微微转动第 2 个水龙头,算出一个偏导数(比如是 10)。
  3. 以此类推,对 100 个水龙头分别执行一次这个操作。

最终,系统会收集到 100 个偏导数。把这 100 个偏导数打包排成一个数组,就组成了 ➡️ 梯度(Gradient)

  • 导数:只有 1 个变量时的变化率。
  • 偏导数:有多个变量时,单独测试其中 1 个变量的变化率(其他当成常数)。
  • 梯度:把所有变量的偏导数打包放进一个张量(数组)里。这个数组完整地告诉了模型:在这几百万个旋钮中,每一个旋钮当前应该往左还是往右拧

4 梯度是什么

其实上面写说了关于偏导数的问题,那么自然也就出现了梯度的计算。

如果一个函数有多个变量。比如f(x,y)。那么它的梯度写作:

f(x,y)\nabla f(x,y)

定义是

f(x,y)=[fxfy]\nabla f(x,y)= \begin{bmatrix} \frac{\partial f}{\partial x} \\ \frac{\partial f}{\partial y} \end{bmatrix}

如果是三个变量

f(x,y,z)=[fxfyfz]\nabla f(x,y,z)= \begin{bmatrix} \frac{\partial f}{\partial x} \\ \frac{\partial f}{\partial y} \\ \frac{\partial f}{\partial z} \end{bmatrix}

所以总结起来,梯度 = 所有偏导数组成的向量(vector)。为什么写成这个样子呢。因为后面在线性代数和机器学习里,这样更方便和向量、矩阵一起运算。不过有些教材也会写成行向量。现在先默认机器学习里常见的是列向量就行。

所以你可以把梯度看成➡️多变量版本的导数。这句话对初学者很有用。

5 概念串联

下面把上面的一些概念串起来的说一些。

f(x)=x2f(x)=x^2

1️⃣然后使用极限定义导数。这里用到了极限

f(x)=limh0(x+h)2x2h=2xf'(x)=\lim_{h\to 0}\frac{(x+h)^2-x^2}{h}=2x

2️⃣从 x^2 得到 2x,这个动作叫求导。3️⃣得到的结果叫导数 f(x)=2xf'(x)=2x。这是导数

4️⃣在某一点,导数等于斜率。比如 x=1:f(1)=2f'(1)=2。表示曲线 y=x2y=x^2 在 x=1 这一点的切线斜率是 2。

5️⃣若 x 发生一个小变化 dx,则⬇️ 这就是微分

dy=f(x)dx=2xdxdy=f'(x)\,dx=2x\,dx

所以它们不是一个东西,但可以串成一条链。

  • 极限(limit):是基础工具
  • 求导(differentiate):是一个过程
  • 导数(derivative):是求导得到的结果
  • 斜率(slope):在函数图像里,导数的几何意义之一
  • 微分(differential):和导数有关,但不等于导数,也不等于求导

极限 → 定义导数 → 通过求导求出导数 → 导数在几何上可理解为斜率 → 微分是导数进一步组织出来的线性近似工具

6 符号问题

接下来在学习的时候,总会遇到各种到符号。但其实不一样的。

  • 普通导数符号 写作正规的拉丁字母小写 dd(读作 d)

专门用于1个变量的一元函数,比如 y=f(x)y = f(x)。 在这个系统里,只有 xx 这一个变量能导致 yy 发生改变。因此计算出的变化率是绝对的、唯一的。这就是为什么它被称为全导数或直接叫导数。

公式表达为 dydx\frac{dy}{dx} ➡️ 说明函数 ff 的世界里只有 xx

  • 偏导数符号 写作一个顶端向后弯曲的特殊符号 \partial(读作 partial 或 del)

专门用于多元函数,比如 z=f(x,y)z = f(x, y)。 在这个系统里,影响 zz 的因素既有 xx 又有 yy。如果只关心单纯改变 xx 会对 zz 产生多大影响,就必须采用控制变量法即在求导的这一瞬间,强行把 yy 冻结成一个常数。 为了在公式上明确告诉读者这是一个冻结了其他变量的局部计算,数学家专门引入了弯曲的 \partial 作为区分。

公式表达为zx\frac{\partial z}{\partial x} ➡️ 说明函数 ff 绝对受多重因素控制,xx 只是此时此刻被单独抽出来分析的那一个

首先我先说一下微分和导数的一些符号的区别。我们经常说的求导,或者是说导数。在数学表示里经常是这样写的。

dydx=f(x), dx, dy\frac{dy}{dx}=f'(x),\ dx,\ dy

但他们不是同一个东西,dy/dxdy/dx这个表示看起来很像很像除法,但这个是完全错误的。

  • dy 除以 dx 等于导数 ❌
  • 好像 dy 和 dx 就是普通分数里的分子分母 ❌

打个比方,学机器学习,也可以这样理解。⬇️损失函数 L(w)L(w) 对参数 ww 的导数。表示参数稍微动一点时,损失变化的快慢。

dLdw\frac{dL}{dw}

如果参数真的动了一小点 dw,那损失的微分近似就是⬇️

dLdLdwdwdL \approx \frac{dL}{dw}dw
  • 算子\nabla

\nabla 是一个极具工程美学的发明。在多元微积分中,如果每次都要把 x\frac{\partial}{\partial x}y\frac{\partial}{\partial y}z\frac{\partial}{\partial z} 分别写出来,公式会极其冗长。\nabla 就是把这些偏导数打包成一个向量

它本身不是一个具体的数值,而是一个操作符(算子),比如下面。计算一个三维空间中的标量场(例如温度分布 T(x,y,z)T(x,y,z))在各方向的变化率。

  • 没有 \nabla 之前(全展开书写)

    必须手动把三个基向量 i,j,k\mathbf{i}, \mathbf{j}, \mathbf{k}(代表 x, y, z 轴)和对应的偏导数拼凑起来。

    gradT=Txi+Tyj+Tzk\text{grad} T = \frac{\partial T}{\partial x}\mathbf{i} + \frac{\partial T}{\partial y}\mathbf{j} + \frac{\partial T}{\partial z}\mathbf{k}

  • 有了 \nabla 之后(打包写法)

    直接把 \nabla 当作一个算子作用在 TT 上。

    T\nabla T

7 导数和微分的区别

这是微积分里最容易混的地方之一。先说结论。

导数回答的是:函数变化得有多快。

微分回答的是:当自变量发生一个很小变化时,函数值大约变化多少。 所以两者关系很近,但不是一个东西。

下面是一个简单的例子。y=f(x) 。设 y=x2y=x^2

导数问的是在某个点,y 相对于x 的局部变化率是多少?

f(x)=x2f(x)=x^2 它的导数是:f(x)=2xf'(x)=2x

这表示:在位置 x 处,这个函数的变化率是 2x。

比如在 x=3 时:f(3)=6f'(3)=6

意思是:在 x=3 附近,x 每变化 1 个单位,y 大约变化 6 个单位。所以导数首先是一个比率速度斜率

微分问的是:如果 x 实际上变动了一点点,那 y 大约变多少?

记 x 的一个很小变化为:dxdx那么对应的 y 的微分定义为:

dy=f(x)dxdy=f'(x)\,dx

这就是微分。注意这里:

  • f(x)f'(x) 是导数
  • dx 是一个很小的输入变化
  • dy 是由导数给出的线性近似变化量

所以 微分 = 导数 × 自变量的微小变化,最核心的区别 你可以这样记

  • 导数 是单位变化率每 1 单位 x 的变化,会带来多少 y 的变化
  • 微分 是实际小变化量这次 x 真的变了 dx,所以 y 大约变了多少

很多人会说。微分就是求导这不准确。更准确是

  • 求导:求出导数
  • 微分:利用导数去描述小变化

所以 求导不是微分,微分也不是导数,但微分依赖导数。

8 关于链式法则

链式法则(Chain Rule)听起来像是一条高深的微积分定律,但它的本质用两个字就能概括:传导。下面是一个例子。

假设一家公司有三个人:老板 (Z)、经理 (Y)、基层员工 (X)。

他们的情绪是互相影响的

  • 基层员工 x 的工作效率,直接影响经理 y 的业绩。
  • 经理 y 的业绩,直接影响老板 z 的心情。

现在我们来算两笔账(也就是求两个导数/偏导数):

  1. 经理对员工的敏感度:员工每多搬 1 块砖,经理的业绩就会增加 2 分。(数学表达) yx=2\frac{\partial y}{\partial x} = 2
  2. 老板对经理的敏感度:经理的业绩每增加 1 分,老板的心情就会变好 3 度。(数学表达)
zy=3\frac{\partial z}{\partial y} = 3

核心问题 ➡️ 如果基层员工多搬 1 块砖,老板的心情会变好多少度?你的常识会立刻告诉你答案:2 * 3 = 6 度

这就是链式法则的全部奥秘:要想知道 A 对 C 的影响,只需要把 A 对 B 的影响,乘以 B 对 C 的影响。

用公式写出来就是

zx=zy×yx\frac{\partial z}{\partial x} = \frac{\partial z}{\partial y} \times \frac{\partial y}{\partial x}

如果用程序员视角:嵌套函数的逐层求导。在代码里,神经网络其实就是一个串联起来的流水线(或者叫嵌套函数)。

# 经理层 (Layer 1)
def manager_layer(x):
    return 2 * x

# 老板层 (Layer 2)
def boss_layer(y):
    return 3 * y

# 神经网络的前向传播(数据从底层往上汇报)
y = manager_layer(x)
z = boss_layer(y)

# 相当于 z = boss_layer(manager_layer(x))

在深度学习里,最后的 z 就是我们的误差(Loss)。我们想要知道,最底层的输入 x(或者底层的权重 W)需要怎么调,才能让最后的误差 z 变小。

如果没有链式法则,数学家需要把这个嵌套了可能成百上千层的超级大函数拆解开,求出一个极其恐怖的全局导数公式。这在计算上是不可能完成的任务。

  • 偏导数,是弄清楚当前层里,每个单独变量的局部影响(经理对员工的敏感度)。
  • 链式法则,是把这些局部的敏感度乘起来,连成一条线,算出最底层的变量对最顶层结果的全局影响。

深度学习之所以能训练几百亿参数的大模型,靠的就是这两者的结合:局部算偏导,全局靠链式法则乘起来。

9 损失函数

损失函数(Loss Function):定义了优化的目标。它是一个包含已知数据未知模型参数的方程。

首先要明白一点,损失函数是人设计的,不是自然界自己给你的。但它不是随便乱定义的,而是要根据任务目标来定义。比如线性函数里面经常用的就是MSE(均方误差, Mean Squared Error)。分类经常用的就是分类常用交叉熵(cross-entropy)。

其实就是一个评分规则,损失函数是人为设计或选定的,用来量化预测有多差的标准。

为什么需要人类来定义呢?因为机器自己不知道什么叫。例如一个回归任务里

  • 预测房价差 1 万算大错还是小错?
  • 差 10 万该罚多少?
  • 是平方罚,还是绝对值罚?

这些都不是数学自动决定的,而是你要先告诉模型:什么叫错,错多少算严重。

所以损失函数本质上是在回答:你到底希望模型优化什么。

那损失函数是越大越好,还是越小越好呢?因为损失函数是罚分,所以越小越好;训练的目标就是最小化损失。

比如在线性回归里,单个样本常用 ⬇️ 预测 - 真实

L=(y^y)2L=(\hat y-y)^2

如果预测很准

y^y\hat y \approx y (y^y)2(\hat y-y)^2

那么就会很小。如果预测差很多,损失就会很大。所以训练模型的目标通常是:min L。也就是最小化损失(minimize the loss)。下面举例子

这里预测是真实值 y=10

预测1:y=10  L=(10-10)^2=0 很好 损失最小
预测2:y=9   L=(9-10)^2=1 还行,但有误差。
预测3:y=5   L=(5-10)^2=25 更差,损失更大。

所以损失越小,模型越好。

10 关于梯度下降

梯度下降(Gradient Descent):提供了优化的手段。它是一套基于微积分的数值计算算法,专门用来求解上述方程的极小值。

上面只是说了梯度,也就是一连串的导数。

f(x,y,z)=[fxfyfz]\nabla f(x,y,z)= \begin{bmatrix} \frac{\partial f}{\partial x} \\ \frac{\partial f}{\partial y} \\ \frac{\partial f}{\partial z} \end{bmatrix}

其实平时买东西、做决策的时候,大脑里每天都在运行梯度下降

下面是个卖咖啡的例子。假设你开了一家咖啡店,终极目标是把每天的亏损降到最低(在深度学习里,这个亏损就叫 Loss)。你现在唯一能控制的旋钮,就是一杯咖啡的定价(这就相当于模型里的参数 Weight)。

但你是个毫无经验的新手,你不知道市场行情,不知道成本线在哪。你该怎么找到那个完美的定价?你只能靠试错。

梯度下降的 4 个关键步骤

1️⃣ 瞎猜一个起点(初始化)

第一天,你脑门一拍,定价 100元/杯。

结果一天下来一杯都没卖出去,房租水电全亏。你的亏损(Loss)达到了最高点。

2️⃣ 感受反馈(计算梯度)

第二天,你试探性地降了一点价,定为 90元/杯。

结果卖出去了两杯。你一算账,虽然还是亏,但亏损比昨天少了一点点。

这个时候,你得到了一个极其宝贵的商业直觉:只要价格往下降,亏损就会减少

这个直觉,在深度学习里就叫做梯度。梯度根本不是什么高深的东西,它就是一个趋势指南针,告诉你:调整这个旋钮(价格),结果(亏损)是变好还是变坏了。

3️⃣ 顺着好趋势走(下降)

既然指南针告诉你降价能减少亏损,那你就顺着这个方向继续走。这就是梯度下降里的下降——永远朝着让亏损减少的方向去调整参数。于是你一路把价格降到了 50元、30元、20元……生意越来越好,亏损越来越少,慢慢变成了盈利(亏损达到谷底)。

4️⃣ 防止走过头(遇到反向梯度)

如果你觉得降价就是好,一路把价格降到了 2元/杯。这时候,虽然门口排起了长队,但你卖得越多,亏得越多(因为连咖啡豆的成本都收不回来)。你的亏损突然又暴涨了!

这时候,反馈(梯度)变了,它告诉你现在价格太低了,必须涨价才能减少亏损。 于是你赶紧往回调,从 2元 涨到 5元、10元……最后,你发现停在15元杯的时候,亏损是最小的(赚得最多)。这就叫模型收敛了,你找到了最优解。

核心概念对照表

如果把上面这个做生意的过程翻译成 AI 黑话,就是下面这样

咖啡店老板(你)深度学习术语它的本质是什么
每天到底亏了多少钱损失函数 (Loss)衡量当前模型有多糟糕的指标。目标是让它越小越好。
咖啡的定价参数/权重 (Weights)模型里唯一可以去调整、扭动的旋钮。
降价能少亏涨价能少亏的趋势梯度 (Gradient)参数微调后的反馈方向。它永远指着能让错误变小的路。
每次调价是降 10元 还是降 0.5元学习率 (Learning Rate)每次调整步伐的大小。步子太大容易错过完美价格,步子太小则需要摸索很久。(超参 需要自己写)

所以,什么是梯度下降?它就是一个摸着石头过河的自动试错策略

因为 AI 模型里有几百万个像咖啡定价这样的参数,没有任何人能用数学公式一步算出它们最完美的组合。AI 只能先瞎猜一个值,然后看看亏损,再根据梯度(趋势)一点点地往完美的值去逼近。这就是梯度下降的全部真相,没有任何玄乎的魔法,全是朴素的常识。

11 损失函数和梯度下降配合

用一个最基础的一元线性回归作为数学例子。

  1. 设定数学目标(构建损失函数)

假设我们有一个极其简单的模型,只有一个需要计算的参数 w(权重)

y^=wx\hat{y} = w \cdot x

现在我们有一条真实的训练数据:输入 x = 2,标准答案 y = 8。

显然,肉眼能看出完美的 ww 应该是 4。但在计算机中,我们需要通过损失函数让机器自己算出来。

我们使用最常见的均方误差(MSE)作为损失函数 L(为了后续求导方便,我们在前面乘上一个 1/2 ,这不影响求极值)

L(w)=12(y^y)2L(w) = \frac{1}{2}(\hat{y} - y)^2

将我们的模型和数据代入损失函数中

L(w)=12(w28)2L(w) = \frac{1}{2}(w \cdot 2 - 8)^2

到这里,损失函数的作用结束了。它成功地把模型预测有多准这个问题,转化成了一个关于变量 ww 的一元二次方程。

我们的目标变成了:求出让 L(w) 最小的 w

  1. 求解过程(启动梯度下降)

因为这是一元二次方程,计算机通过求导数(梯度)来寻找极小值。首先,对损失函数关于参数 ww 求导(使用链式法则):

dLdw=122(2w8)2\frac{dL}{dw} = \frac{1}{2} \cdot 2(2w - 8) \cdot 2 dLdw=4w16\frac{dL}{dw} = 4w - 16

这就得到了梯度公式。接下来,我们设定学习率 α=0.1\alpha = 0.1,并让机器随便猜一个初始的参数,比如 w0=0w_0 = 0

梯度下降的核心更新公式是

wnew=woldαdLdww_{new} = w_{old} - \alpha \cdot \frac{dL}{dw}

然后计算机开始执行迭代计算

第一次迭代:

  • 计算当前梯度:当 w=0w = 0 时,dLdw=4(0)16=16\frac{dL}{dw} = 4(0) - 16 = -16
  • 更新参数:w1=00.1(16)=1.6w_1 = 0 - 0.1 \cdot (-16) = 1.6

第二次迭代:

  • 计算当前梯度:当 w=1.6w = 1.6 时,dLdw=4(1.6)16=6.416=9.6\frac{dL}{dw} = 4(1.6) - 16 = 6.4 - 16 = -9.6
  • 更新参数:w2=1.60.1(9.6)=1.6+0.96=2.56w_2 = 1.6 - 0.1 \cdot (-9.6) = 1.6 + 0.96 = 2.56

第三次迭代:

  • 计算当前梯度:当 w=2.56w = 2.56 时,dLdw=4(2.56)16=10.2416=5.76\frac{dL}{dw} = 4(2.56) - 16 = 10.24 - 16 = -5.76
  • 更新参数:w3=2.560.1(5.76)=2.56+0.576=3.136w_3 = 2.56 - 0.1 \cdot (-5.76) = 2.56 + 0.576 = 3.136
  1. 数学关系的总结

观察上面的计算过程,参数 ww 正在严格按照数学逻辑,从 01.62.563.1360 \rightarrow 1.6 \rightarrow 2.56 \rightarrow 3.136,一步步向着完美解 w=4w = 4 逼近。在这个纯计算的过程中:

  1. 损失函数给出了代数表达式 L(w)=12(2w8)2L(w) = \frac{1}{2}(2w - 8)^2
  2. 梯度下降对这个表达式求导,并利用导数的数值大小,强行将参数 ww 往导数为 00(极小值)的地方推。

深度学习中无论多复杂的网络,无非就是把这个一元方程,换成了包含几百亿个变量 ww 的多元方程,然后求偏导数进行矩阵运算而已,底层的数学逻辑与这个例子完全一致。

也就是说模型和损失函数都是我们自己指定的 最后的答案就是求未知数w是吗

没错,模型和损失函数是我们人类设定的游戏规则,而整个训练过程的终极目的,就是让机器自己去解出那个(或那一堆)未知数 ww。我们可以把人类和机器的分工拆解得更清晰一些

  1. 人类负责搭框架

在敲代码之前,人类必须要替机器做两个核心决定

  • 指定模型(Model):这是在假设数据的规律长什么样。比如你指定模型是 y^=wx\hat{y} = w \cdot x,这就等于你认定数据是一条直线。如果你用了几十层的残差网络(ResNet),这就等于你认定数据是一个极其复杂的非线性高维曲面。
  • 指定损失函数(Loss Function):这是在给机器定义什么是好,什么是坏。你是关心绝对误差,还是平方误差?你只有定义了什么是,机器才知道怎么去
  1. 机器负责填数字(找未知数)

一旦框架搭好,人类就退居幕后,剩下的脏活累活全交给了梯度下降

  • 你刚才例子里的 ww,在专业术语里叫做权重(Weights)。通常还会搭配一个偏置(Bias) bb,公式变成 y^=wx+b\hat{y} = w \cdot x + b
  • 机器的任务,就是像一个不知疲倦的计算器,通过计算损失 \rightarrow 求导数(梯度) \rightarrow 走一步更新数字的死循环,一点点逼近完美的 wwbb
  1. 从 1 个未知数,到 1750 亿个未知数

你刚才理解的,是一个最简单的一元线性回归。而如今那些极其强大的人工智能,比如帮你画图的 AI、能和你流畅聊天的 ChatGPT,在底层的数学逻辑上,和你刚刚理解的这个过程没有任何本质区别。唯一的区别仅仅在于规模

  • 你的例子里,模型里只有 1 个未知数 ww
  • 早期的图像识别网络(如 ResNet),模型里大约有 2000 万个未知数 ww
  • GPT-3 大模型,它的那个超复杂的深度学习公式里,包含了 1750 亿个未知数 ww

但不管未知数有多少,人类依然只是搭建好模型和损失函数,然后按下运行键,让装配了自动微分功能的计算机,在满是未知数的海洋里,用梯度下降法去寻找那座名为最小损失的谷底。

我这样理解对不对,首先你要有一个模型的函数,然后给出一个损失函数,对着损失函数求导得到的就是梯度,然后使用梯度下降的公式就求出了w

或者去看当前最顶尖的 AI 工程师写的训练代码,无论模型有多么庞大(哪怕是 ChatGPT),底层的绝对核心就是你刚才说的这 4 句话。唯一需要稍微补充的一点点细节是:这个过程不是一步到位算出来的,而是一个死循环,需要重复成千上万次。翻译成 AI 界的标准训练循环(Training Loop)⬇️

先有模型函数(model)→ 再定义损失函数(loss)→ 对损失函数对参数求导/求偏导,得到梯度(gradient)→ 用梯度下降更新参数 w → 重复很多次,逐步逼近更好的 w

AI 炼丹的四大标准动作

  1. 你要有一个模型的函数 👉 专业术语:前向传播 (Forward Pass)

    你给定一个框架(比如 y^=wx\hat{y} = w \cdot x),把数据输入进去,让模型用当前瞎猜的 ww 算出一个预测值。

  2. 给出一个损失函数👉 专业术语:计算损失 (Compute Loss)

    把模型的预测值和真实答案放进损失函数里,算出一个具体的误差分数(比如考了 20 分,距离 100 分差很远)。

  3. 对着损失函数求导得到梯度👉 专业术语:反向传播 (Backward Pass / Autograd)

    框架底层的自动微分开始工作,利用链式法则,沿着计算图往回推,算出损失函数对每一个未知数 ww 的导数(梯度),也就是找出错题原因

  4. 使用梯度下降公式求出 ww 👉 专业术语:优化器更新 (Optimizer Step)

    拿到梯度后,把旧的 ww 按照学习率的步伐,往正确的方向挪动一点点,变成新的 ww

【循环往复】:拿着更新后的新 ww,回到第 1 步重新算预测值、重新算损失、重新求导……不停地转圈圈,直到损失函数降到了谷底(或者降到了你满意的程度),此时此刻停留在手里的 ww,就是你最终训练出来的 AI 模型

那么接下来用线性回归完整的串联一起。

  • 写模型
y^=wx+b\hat y=wx+b
  • 写损失函数
L(prediction,target)L(\text{prediction},\text{target}) L=(wx+by)2L=(wx+b-y)^2
  • 对参数求偏导
Lw=2(wx+by)x\frac{\partial L}{\partial w}=2(wx+b-y)x Lb=2(wx+by)\frac{\partial L}{\partial b}=2(wx+b-y)
  • 梯度
L=[2(wx+by)x2(wx+by)]\nabla L=\begin{bmatrix}2(wx+b-y)x\\2(wx+b-y)\end{bmatrix}
  • 然后更新
wwηLww \leftarrow w-\eta \frac{\partial L}{\partial w} bbηLbb \leftarrow b-\eta \frac{\partial L}{\partial b}

重复很多次后,w,b 会越来越合适。

最后的数学题

下面有2道数学题,如果上面的概念你都理解了,那么下面的数学题肯定是没问题的。

第一题

我们用最简单的一元线性回归,只保留一个参数 ww,先不加 bb

Qy^=wx\hat y = wx

这里只有一个训练样本:x=2,y=6x=2,\quad y=6

损失函数定义为平方误差:L=(y^y)2L=(\hat y-y)^2

初始参数设为:w=1w=1

学习率(learning rate)设为:η=0.1\eta=0.1

然后求下面的

y^=?\hat y = ? 求预测值

L=?L = ? 求损失函数下的误差

L(w)=?L(w)=? 把损失写成参数的函数

dLdw=?\frac{dL}{dw}=? 对参数求导

当前梯度(当 w=1 时):dLdww=1=?\frac{dL}{dw}\bigg|_{w=1}=? 在当前算梯度

更新后:wnew=?w_{\text{new}}=? 梯度下降更新参数

最后的新损失:Lnew=?L_{\text{new}}=?

答案

Q1 y^=wx=12=2\hat y = wx = 1 \cdot 2 = 2

Q2 L=(y^y)2=(26)2=(4)2=16L = (\hat y - y)^2 = (2-6)^2 = (-4)^2 = 16

Q3 L(w)=(wxy)2L(w) = (wx-y)^2 代入 x=2, y=6,然后答案 L(w)=(2w6)2L(w) = (2w-6)^2

Q4 L(w)=(2w6)2L(w)=(2w-6)^2 用链式法则 dLdw=2(2w6)2=4(2w6)=8w24\frac{dL}{dw}=2(2w-6)\cdot 2 = 4(2w-6)=8w-24

Q5 这里我第一次做错了 dLdww=1=8124=16\frac{dL}{dw}\bigg|_{w=1} = 8\cdot 1 - 24 = -16

Q6 wnew=wηdLdww_{\text{new}} = w - \eta \frac{dL}{dw} 然后代入 wnew=10.1×(16)w_{\text{new}} = 1 - 0.1 \times (-16) 所以答案 wnew=2.6w_{\text{new}} = 2.6

Q7 用新参数 w=2.6 y^new=wx=2.62=5.2\hat y_{\text{new}} = wx = 2.6 \cdot 2 = 5.2 然后新损失 Lnew=(5.26)2=(0.8)2=0.64L_{\text{new}} = (5.2-6)^2 = (-0.8)^2 = 0.64

基本会了,唯一混掉的是:梯度更新后的参数 不是一个东西。

  1. 模型 y^=wx\hat y = wx

  2. 当前参数做预测 y^=12=2\hat y = 1 \cdot 2 = 2

  3. 用损失函数衡量误差 L=(26)2=16L=(2-6)^2=16

  4. 把损失写成参数的函数 L(w)=(2w6)2L(w)=(2w-6)^2

  5. 对参数求导 dLdw=8w24\frac{dL}{dw}=8w-24

  6. 在当前点算梯度 dLdww=1=16\frac{dL}{dw}\bigg|_{w=1}=-16

  7. 用梯度下降更新参数 wnew=10.1(16)=2.6w_{\text{new}}=1-0.1(-16)=2.6

  8. 新参数带来更小损失 Lnew=0.64L_{\text{new}}=0.64

第二题

已知模型:y^=wx+b\hat y = wx + b

训练样本:x=2,y=7x=2,\quad y=7

损失函数:L=(y^y)2L=(\hat y-y)^2

初始参数:w=1,b=1w=1,\quad b=1

学习率:η=0.1\eta=0.1

求下面

1 y^=?\hat y = ? 求预测值

2 L=?L = ? 求损失函数下的损失

3 L(w,b)=?L(w,b)=? 把损失函数写成带有参数的导数

4 Lw=?\frac{\partial L}{\partial w}=? 对带有参数的损失函数求偏导 w

Lb=?\frac{\partial L}{\partial b}=? 对带有参数的损失函数求偏导 b

5 Lw(1,1)=?\frac{\partial L}{\partial w}\Big|_{(1,1)}=? 在当前点求梯度 w

Lb(1,1)=?\frac{\partial L}{\partial b}\Big|_{(1,1)}=? 在当前点求梯度 b

6 L(1,1)=?\nabla L(1,1)=? 在当前点求梯度

7 wnew=?w_{\text{new}}=? 梯度下降求最新参数 w

bnew=?b_{\text{new}}=? 梯度下降求最新参数 b

8 $$L_{\text{new}}=?$ 新参数下的损失

答案

Q1: 测试值 y^=wx+b=12+1=3\hat y = wx+b = 1\cdot 2 + 1 = 3

Q2: 当前损失 L=(y^y)2=(37)2=16L=(\hat y-y)^2=(3-7)^2=16

Q3: 写成wb函数

L(w,b)=(wx+by)2L(w,b)=(wx+b-y)^2 代入 x=2, y=7x=2,\ y=7 最后答案 L(w,b)=(2w+b7)2L(w,b)=(2w+b-7)^2

Q4: 对w求偏导

L(w,b)=(2w+b7)2L(w,b)=(2w+b-7)^2u=2w+b7u=2w+b-7 所以 L=u2L=u^2

对 w 求偏导时:Lw=2uuw\frac{\partial L}{\partial w}=2u\cdot \frac{\partial u}{\partial w} 而:uw=2\frac{\partial u}{\partial w}=2 所以:Lw=2(2w+b7)2\frac{\partial L}{\partial w}=2(2w+b-7)\cdot 2

最后答案 Lw=4(2w+b7)=8w+4b28\frac{\partial L}{\partial w}=4(2w+b-7)=8w+4b-28

对b求偏导

Lb=2(2w+b7)b(2w+b7)\frac{\partial L}{\partial b}=2(2w+b-7)\cdot \frac{\partial}{\partial b}(2w+b-7) 因为 b(2w+b7)=1\frac{\partial}{\partial b}(2w+b-7)=1

所以 Lb=2(2w+b7)\frac{\partial L}{\partial b}=2(2w+b-7) 结果就是 Lb=2(2w+b7)=4w+2b14\frac{\partial L}{\partial b}=2(2w+b-7)=4w+2b-14

Q5: 直接代入就好

Lw(1,1)=4(21+17)=4(4)=16\frac{\partial L}{\partial w}\Big|_{(1,1)}=4(2\cdot1+1-7)=4(-4)=-16

Lb(1,1)=2(21+17)=2(4)=8\frac{\partial L}{\partial b}\Big|_{(1,1)} =2(2\cdot1+1-7) =2(-4) =-8

Q6:求梯度 梯度是向量,不是一个数。

L(w,b)=[LwLb]=[4(2w+b7)2(2w+b7)]\nabla L(w,b)= \begin{bmatrix} \frac{\partial L}{\partial w}\\ \frac{\partial L}{\partial b} \end{bmatrix} = \begin{bmatrix} 4(2w+b-7)\\ 2(2w+b-7) \end{bmatrix}

所以结果就是 L(1,1)=[168]\nabla L(1,1)=\begin{bmatrix}-16\\-8\end{bmatrix}

Q7: 更新参数

wnew=wηLwbnew=bηLbw_{\text{new}}=w-\eta \frac{\partial L}{\partial w} b_{\text{new}}=b-\eta \frac{\partial L}{\partial b}

所以答案就是 wnew=10.1(16)=1+1.6=2.6w_{\text{new}}=1-0.1(-16)=1+1.6=2.6 bnew=10.1(8)=1+0.8=1.8b_{\text{new}}=1-0.1(-8)=1+0.8=1.8

Q8: 那么最后重新计算损失

y^new=wnewx+bnew=2.62+1.8=5.2+1.8=7\hat y_{\text{new}}=w_{\text{new}}x+b_{\text{new}}=2.6\cdot 2+1.8=5.2+1.8=7

新损失 Lnew=(77)2=0L_{\text{new}}=(7-7)^2=0