📕 第1章 深度学习与机器学习
📄 一句话概括

这一节要的数学不多,但每一样都得真会:向量与内积、求和记号、范数、偏微分、 合成函数的微分(链锁律)、指数与对数。 全节的验证方式只有一个:手推一遍,再用数值微分对拍。偏导数解析 64 与数值 64 差 4.19e-10, 链锁律解析 0.578060 与数值差 1.89e-11——差在这个量级,说明推对了。 这些不是考前复习,是第 4 章反向传播的全部零件。

📝 本节和前面几节的关系

前三节都是「是什么」,这一节是「拿什么算」。 第 4 章的梯度法、反向传播,第 5 章的卷积求和,全靠这里的四样东西。 如果微积分忘得差不多了,只要记住一句话: 导数就是「挪一点点,看变多少」,数值微分就是照这句话直接写出来的。

一、数值、变量与常数

书里从最朴素的讲起:数、变量、常数、等式、代入式。换成机器学习的语境,这三样各有各的角色。 x 是数据,是常数——训练时它不动;w 是参数,是变量——训练就是动它;y 是标签,也是常数。 所以「学习」这个词翻译成数学,就是「固定 x 与 y,改动 w,让某个式子变小」。 「代入式」就是前向传播:把 x 代进 y = wx + b,算出一个预测值。 第 4 章那一层层往前算的网络,本质就是连续代入好几次。

二、向量、上下标与求和记号

向量就是把几个数并排放在一起:a = (a₁, a₂, a₃)。 下标是分量序号,上标在机器学习里常表示「第几个样本」,x⁽²⁾ 是第二个样本。 两个向量的内积是对应分量相乘再求和:a·b = Σ aᵢbᵢ。 a=(3,-1,2)、b=(1,4,-2) 时,内积 = 3×1 + (-1)×4 + 2×(-2) = -5。 一个神经元的加权和 z = w₁x₁ + w₂x₂ + b,写成向量就是 z = w·x + b。 书里所有的 Σ 最后都变成一次矩阵乘,这也是为什么显卡有用——它擅长的就是这件事。

三、绝对值、最大值、距离与范数

绝对值是把负号去掉,范数是把「长度」这个概念推广到向量上。同一个向量有三种量法:

范数 怎么算 a=(3,-1,2) 的值 几何意义
L1 各分量绝对值相加 6.0 走街区的距离
L2 平方和开根号 3.7417 直线距离
L∞ 绝对值里最大的那个 3.0 只看最远的一维

距离就是差的范数:‖a - b‖₂ 是两点的直线距离。k 近邻、k-means、词向量相似度,底层都是这一句。 L1 和 L2 还有一个实际区别:L1 更容易把某些分量压到正好 0(稀疏),L2 则倾向把大家均匀压小。 所以想做特征筛选时常用 L1。

四、微分、偏微分与链锁律

导数衡量「输入挪一点点,输出变多少」。多元函数里只挪一个变量、其余按住不动,得到的就是偏导数。 以 f(x, y) = (xy + x)² 在 (2, 3) 为例。令 u = xy + x,则 f = u²。 对 x 求偏导要把 y 当常数:∂f/∂x = 2u·(y+1) = 2×8×4 = 64;对 y 求偏导则 ∂f/∂y = 2u·x = 32。 合成函数的微分就是链锁律:外层导数乘内层导数。 z = sin(wx + b) 对 w 求导,外层是 cos(wx+b),内层 wx+b 对 w 的导数是 x,所以 ∂z/∂w = cos(wx+b)·x。 数值 0.578060,解析也 0.578060。 反向传播就是把这个乘法一层层往回做。一层一层乘下去,乘的链条越长, 梯度越容易趋近 0——这就是深层网络难训的数学原因。

五、指数与对数

指数函数 eˣ 单调递增、永远为正;对数 log 是它的反函数,能把乘法变加法。 机器学习里它出现在两个地方,都很关键。 一是 softmax:把一排实数变成加起来等于 1 的概率。 做法是先取指数(保证为正、拉开差距),再除以总和。 [2, 1, 0.1] 变出来是 0.659001 / 0.242433 / 0.098566。 二是交叉熵:-log(正确那一类的概率)。概率 1 时它是 0,概率 0.1 时它是 2.3。 它比平方误差更狠地惩罚「自信地答错」,所以分类任务几乎都用它。 还有一个工程细节:直接算 exp(700) 会溢出。 所以实现里统一先减掉最大值再取指数,数学上完全等价,数值上救命。

六、动手 Demo:把书上跳过的步骤算出来

四段:向量四量、偏微分对拍、链锁律对拍、softmax 归一化。 对拍的意思是同一个量用两种办法算——手推的解析式和「挪一点点」的数值式,两个结果对上,才算推对了。

import numpy as np

a = np.array([3.0, -1.0, 2.0])
b = np.array([1.0, 4.0, -2.0])
print(a @ b, np.abs(a).sum(), np.sqrt(a @ a), np.abs(a).max())

def f(x, y):                       # f = (xy + x)²
    return np.square(x * y + x)

x0, y0, h = 2.0, 3.0, 1e-5
# 解析:f = u²,u = xy + x,∂f/∂x = 2u·(y+1),∂f/∂y = 2u·x
ax = 2 * (x0 * y0 + x0) * (y0 + 1.0)
ay = 2 * (x0 * y0 + x0) * x0
nx = (f(x0 + h, y0) - f(x0 - h, y0)) / (2 * h)
ny = (f(x0, y0 + h) - f(x0, y0 - h)) / (2 * h)

# 链锁律:z = sin(wx + b),∂z/∂w = cos(wx + b)·x
w, x, b = 0.7, 1.3, 0.2
dz = np.cos(w * x + b) * x
num = (np.sin((w + h) * x + b) - np.sin((w - h) * x + b)) / (2 * h)

# softmax:先减最大值,再取指数,最后归一化
s = np.array([2.0, 1.0, 0.1])
e = np.exp(s - s.max())
p = e / e.sum()

实跑输出:

--------------------------------------------------------------------
机器学习所需的数学 —— 把书上跳过的步骤算出来
--------------------------------------------------------------------

  量                    值
  --------------------------------
  内积 a·b              -5.0
  L1 范数 |a|₁          6.0
  L2 范数 |a|₂          3.7417
  L∞ 范数 |a|∞          3.0

  三种范数是三种距离的口味:L1 走街区,L2 走直线,L∞ 只看最远的那个分量。

  偏导数            解析解          数值解          误差
  ------------------------------------------------------------
  ∂f/∂x             64.0000         64.0000         4.19e-10
  ∂f/∂y             32.0000         32.0000         8.56e-10

  解析解是手推的:f = u²,u = xy + x,所以 ∂f/∂x = 2u·(y+1)。
  **数值解是「把 x 挪一点点、看 f 变多少」,两者差在 1e-9 量级,说明手推对了。**

  链锁律:z = sin(wx + b),要 ∂z/∂w,就是外层的 cos 乘内层对 w 的导数 x。
  解析 0.578060,数值 0.578060,差 1.89e-11——一行乘法,不用展开。

  类            概率
  --------------------------
  第 1 类       0.659001
  第 2 类       0.242433
  第 3 类       0.098566
  合计          1.000000

  减掉最大值再取指数,是为了不让 exp(700) 溢出——数学上等价,数值上救命。
  **交叉熵就是 -log(正确那一类的概率);概率越接近 1,这个数越接近 0。**

内积 -5.0、L1 6.0、L2 3.7417、L∞ 3.0。同一个向量三种长度,数值差得不小, 用哪个取决于你想惩罚什么样的误差。 ∂f/∂x 解析 64.0000、数值 64.0000,差 4.19e-10;∂f/∂y 差 8.56e-10。这个量级的差不是错, 是数值微分本身的截断误差——中心差分的误差跟 h² 同阶,h=1e-5 时理论误差约 1e-10,正好对上。 链锁律那一行差 1.89e-11。 这一行是整本书最重要的一行代码:它证明了「外层导数 × 内层导数」这个乘法就是梯度的算法, 第 4 章的反向传播不过是把它在一百层上重复一遍。 softmax 三个数 0.659001、0.242433、0.098566,加起来正好 1.000000。 注意原始分数 2、1、0.1 只差 1.9,概率却差到 6.7 倍——指数会把差距放大。

30 秒实验

  1. 把 h 从 1e-5 改成 1e-2 和 1e-8,数值微分的误差各变成多少?为什么不是越小越好?
  2. 把 f 换成 f(x, y) = xy + sin(x),重新手推两个偏导,再用数值对拍。
  3. 把 softmax 的输入从 [2, 1, 0.1] 改成 [20, 10, 1],概率分布会更平还是更尖?

常见误区

  1. 以为做深度学习可以不会微积分。 调包确实能跑, 但模型不收敛时你要判断的是「梯度消失了还是学习率太大」,这需要看得懂梯度。
  2. 以为偏导数就是「分别求导再拼起来」。 形式上对,但要牢记其他变量是按住不动的; 把 y 也当变量一起求,算出来的是全导数,不是偏导。
  3. 以为 log 只是化简用的。 它最大的用处是数值稳定:一百个数连乘会下溢成 0, 取对数变成一百个数相加,就不会。
自测三问(点开对答案) 1. 为什么数值微分不能把 h 取到极小(比如 1e-15)? —— 因为浮点数只有约 16 位有效数字,h 太小时 f(x+h) 与 f(x-h) 的差会被舍入误差淹掉, 分子变成噪声。中心差分的最佳 h 一般在 1e-5 附近。 2. L1 与 L2 范数在实用上最大的区别是什么? —— L1 的等值线是菱形,最优点容易落在坐标轴上,所以会把某些分量压成正好 0(稀疏); L2 的等值线是圆,倾向于把各分量均匀缩小,很少正好为 0。 3. 链锁律和反向传播是什么关系? —— 反向传播就是链锁律的机械化执行:从输出层开始, 把「外层导数 × 内层导数」一层层往输入方向乘回去,每一层顺手把自己需要的梯度存下来。

第一章到此结束:深度学习是什么、怎么来的、在机器学习的哪一格、要用哪些数学。 下一章开始动手——先把 Python 装上,把环境弄干净。

相关笔记