关键词:Python、NumPy、标准化、超参数、正向传播、反向传播、小批量、随机梯度下降、MNIST、im2col、col2im、ReLU、softmax、one-hot、权重初始化、卷积神经网络实现


本章概览

本章从理论走向实践,使用 Python 和 NumPy 从零实现神经网络。先实现一个判断图像长宽比的简单全连接神经网络,完成数据标准化、网络结构定义、正向传播、反向传播、参数更新和训练。然后进一步实现卷积神经网络,用于 MNIST 手写数字识别。实现过程中重点介绍了小批量、随机梯度下降、im2col 变换、col2im 变换、ReLU、softmax、权重初始化等关键技术。

一句话总结:理论只有通过实现才能真正理解。本章用 Python 从零实现了全连接神经网络和卷积神经网络,把前四章的数学表达式变成了可运行的代码。


5.1 使用Python实现

核心内容:
绫乃想用 Python 实现神经网络。虽然实现神经网络不限定语言,但 Python 最适合。第一个练手项目是判断图像是否为细长。

要点总结:

  • Python 适合实现神经网络。
  • 实现神经网络不限于 Python,但 Python 更方便。
  • 第一个项目:判断长宽比较小的图像为“细长”。
  • 通过实现可以加深对理论的理解。
  • 亲手运行神经网络很重要。

本节启发:
学习神经网络不能只看公式,必须动手实现。实现过程中会遇到很多理论中不会提到的问题。


5.2 判断长宽比的神经网络

核心内容:
准备训练数据,包含宽、高和标签。对数据进行标准化,使平均值为 0、方差为 1,加快收敛。网络结构、层数、单元数等属于超参数,需要开发者自行决定。

要点总结:

  • 训练数据:宽、高、标签。
  • 标签:1 表示细长,0 表示非细长。
  • 标准化:

x′=x−μσ x'=\frac{x-\mu}{\sigma} x=σxμ

其中 μ\muμ 是平均值,σ\sigmaσ 是标准差。

  • 标准化使平均值为 0、方差为 1。
  • 标准化可以提高参数收敛速度。
  • 层数、单元数等由开发者决定,属于超参数。
  • 超参数没有唯一最佳值,需要不断尝试。
  • 权重和偏置用随机数初始化。
  • 实现时使用数组比矩阵更直观。

本节启发:
数据预处理非常重要。标准化虽然简单,但能显著影响训练速度和效果。

5.2.1 神经网络的结构

核心内容:
确定神经网络结构,准备权重矩阵和偏置。结构选择是超参数问题,没有最佳实践。

要点总结:

  • 需要确定层数、单元数。
  • 权重和偏置是待优化参数。
  • 层数、单元数等是超参数。
  • 超参数如何决定是难题。
  • 先使用之前讨论过的网络结构。
  • 权重和偏置用随机数初始化。

本节启发:
超参数选择没有标准答案,只能通过实验确定。

5.2.2 正向传播

核心内容:
实现正向传播。激活函数选择 sigmoid。计算时保留各层输入 x(l)x^{(l)}x(l) 和加权输入 z(l)z^{(l)}z(l),供反向传播使用。为了能一次性处理多个数据,使用矩阵运算。

要点总结:

  • sigmoid 函数:

σ(x)=11+e−x \sigma(x)=\frac{1}{1+e^{-x}} σ(x)=1+ex1

  • 正向传播需要保留 x(l)x^{(l)}x(l)z(l)z^{(l)}z(l)
  • 使用 np.dot 进行矩阵乘法。
  • 将多个数据纵向排列为矩阵,一次计算。
  • 权重矩阵转置后与输入矩阵相乘。
  • 偏置向量重复排列为矩阵后相加。
  • 矩阵转置不改变数值含义,只是调整形状。

本节启发:
实现时不能只考虑单个数据,要支持批量处理,这样代码更整洁,速度更快。

5.2.3 反向传播

核心内容:
实现反向传播。需要实现 sigmoid 的微分、输出层德尔塔、隐藏层德尔塔。使用矩阵一次性处理多个数据。

要点总结:

  • sigmoid 微分:

σ′(x)=σ(x)(1−σ(x)) \sigma'(x)=\sigma(x)(1-\sigma(x)) σ(x)=σ(x)(1σ(x))

  • 输出层德尔塔:

Δ(L)=(Y−A(L))⊗a′(L)(Z(L)) \Delta^{(L)}=(Y-A^{(L)})\otimes a'^{(L)}(Z^{(L)}) Δ(L)=(YA(L))a(L)(Z(L))

  • 隐藏层德尔塔:

Δ(l)=(Δ(l+1)W(l+1))⊗a′(l)(Z(l)) \Delta^{(l)}=(\Delta^{(l+1)}W^{(l+1)})\otimes a'^{(l)}(Z^{(l)}) Δ(l)=(Δ(l+1)W(l+1))a(l)(Z(l))

  • ⊗\otimes 表示逐元素乘法。
  • NumPy 中:
    • np.dot(A, B) 是矩阵乘法;
    • A * B 是逐元素乘法。
  • 德尔塔用大写 Δ\DeltaΔ 表示多个数据的矩阵。
  • 反向传播需要 Z、下一层德尔塔 Delta、权重 W

本节启发:
反向传播的实现关键是矩阵化。把单个数据的公式扩展为矩阵形式,可以一次性处理所有数据。

5.2.4 训练

核心内容:
实现参数更新和训练流程。学习率 η\etaη 需要尝试,一般设为 0.01 或 0.001。训练流程:正向传播 → 反向传播 → 参数更新。重复多轮。

要点总结:

  • 参数更新:

W(l):=W(l)−η1K∑kΔk(l)TXk(l−1) W^{(l)} := W^{(l)} - \eta \frac{1}{K}\sum_k \Delta_k^{(l)T}X_k^{(l-1)} W(l):=W(l)ηK1kΔk(l)TXk(l1)

b(l):=b(l)−η1K∑kΔk(l) b^{(l)} := b^{(l)} - \eta \frac{1}{K}\sum_k \Delta_k^{(l)} b(l):=b(l)ηK1kΔk(l)

  • 学习率 η\etaη 是超参数。
  • 一般设为 0.01 或 0.001。
  • 训练流程:
    1. 正向传播;
    2. 反向传播;
    3. 参数更新。
  • 重复次数叫轮数,英语 epoch。
  • 轮数也是超参数。
  • 使用目标函数跟踪训练进展。

本节启发:
训练就是不断重复“预测 → 计算误差 → 更新参数”的过程。轮数和学习率都需要实验确定。

5.2.5 小批量

核心内容:
把训练数据分成小批量,每次用一部分数据更新参数。这种方法叫随机梯度下降法或小批量梯度下降法,能更容易收敛到最优解。

要点总结:

  • 小批量:把训练数据分成小份。
  • 每次用一个小批量更新参数。
  • 随机梯度下降法 / 小批量梯度下降法。
  • 二重循环:
    • 内侧:小批量更新;
    • 外侧:轮数循环。
  • 训练日志显示误差逐渐减少。
  • 输出层 sigmoid 输出在 0 到 1 之间,可看作概率。
  • 可设置阈值将概率转为 0 或 1。
  • 测试精度达到 98.4%。

本节启发:
小批量是深度学习训练的标准做法。它兼顾了计算效率和收敛稳定性。


5.3 手写数字的图像识别与卷积神经网络

核心内容:
使用卷积神经网络实现 MNIST 手写数字识别。需要下载数据、准备数据、定义网络结构、实现正向传播、反向传播、参数更新和训练。

要点总结:

  • 使用 MNIST 数据集。
  • MNIST 包含 60000 个训练数据和 10000 个测试数据。
  • 数据已经标注。
  • 手写数字识别适合理解卷积神经网络。
  • 数据收集和标注是机器学习中最难的部分之一。

本节启发:
MNIST 是学习卷积神经网络的经典数据集,适合练习完整流程。

5.3.1 准备数据集

核心内容:
下载 MNIST,读取二进制文件,整理数据形状。图像数据整理为 4 维:索引、通道、高、宽。像素值除以 255,归一化到 0 到 1。标签转为 one-hot 向量。

要点总结:

  • MNIST 文件为 gz 压缩格式。
  • 标签文件前 8 字节为头部,图像文件前 16 字节为头部。
  • 图像数据整理为 4 维:

(索引,通道,高,宽) (\text{索引}, \text{通道}, \text{高}, \text{宽}) (索引,通道,,)

  • 像素值除以 255,归一化到 0 到 1。
  • 标签转为 one-hot 向量。
  • one-hot 也叫 1-of-K 表示。
  • 使用 matplotlibimshow 查看图像。

本节启发:
数据准备是深度学习中最繁琐但最重要的步骤之一。数据形状和数值范围直接影响训练效果。

5.3.2 神经网络的结构

核心内容:
确定卷积神经网络结构,准备权重和偏置。这次使用 ReLU 作为激活函数,权重初始化使用何恺明等人提出的方法。

要点总结:

  • 网络结构参考之前讨论的卷积神经网络。
  • 需要确定过滤器大小、数量、全连接层单元数等超参数。
  • 权重初始化很重要。
  • 使用指定方差的正态分布初始化。
  • 使用 He 初始化方法。
  • 标准差是方差的平方根。
  • 使用 numpy.random.randn 生成标准正态分布随机数,再乘以标准差。
  • 初始化不当可能导致不收敛或发散。

本节启发:
权重初始化不是随便选随机数。合适的初始化方法对训练成功至关重要。

5.3.3 正向传播

核心内容:
实现卷积神经网络正向传播。卷积使用 im2col 变换,把图像转换为矩阵,然后用矩阵乘法计算。ReLU 和池化也使用 im2col。最后连接全连接层,输出层使用 softmax。

要点总结:

  • 直接七重循环计算量太大。
  • 使用 im2col 变换:
    • 从图像中取出单元并排列;
    • 得到 col 形式矩阵;
    • 用矩阵乘法计算卷积。
  • 过滤器权重纵向排列为矩阵。
  • 计算:

Z=XcolTWcol+B Z = X_{col}^T W_{col} + B Z=XcolTWcol+B

  • 多个数据可以纵向排列,一次计算。
  • im2col 有内存浪费,但比多重循环高效。
  • ReLU:

ReLU(x)=max⁡(0,x) \mathrm{ReLU}(x)=\max(0,x) ReLU(x)=max(0,x)

  • 池化:使用 im2col 选择最大值。
  • 保存池化选择的索引,反向传播时使用。
  • softmax 实现要防止溢出:
    • 减去最大值再计算。
  • 池化后的特征图展开为 1 列,连接全连接层。

本节启发:
卷积神经网络的实现难点在于效率。im2col 是连接卷积和矩阵乘法的关键技巧。

5.3.4 反向传播

核心内容:
实现卷积神经网络反向传播。包括 ReLU 微分、输出层德尔塔、全连接隐藏层德尔塔、卷积层德尔塔、池化反向传播。卷积层德尔塔使用 col2im 变换。

要点总结:

  • ReLU 微分:

ReLU′(x)={1,x>00,x≤0 \mathrm{ReLU}'(x)= \begin{cases} 1, & x>0 \\ 0, & x\le 0 \end{cases} ReLU(x)={1,0,x>0x0

  • 输出层德尔塔:

Δ(L)=Y−T \Delta^{(L)}=Y-T Δ(L)=YT

  • 全连接隐藏层德尔塔与之前相同。
  • 与全连接层相连的卷积层德尔塔:展开后与全连接隐藏层形式相同。
  • 与卷积层相连的卷积层德尔塔:
    • 使用 col2im 变换;
    • 把 col 形式恢复为 im 形式;
    • 再乘以 ReLU 微分。
  • 池化反向传播:
    • 未选中单元德尔塔为 0;
    • 在池化选择的索引位置放回德尔塔;
    • 进行 col2im 变换。
  • 反向传播顺序:
    输出层 → 全连接层 → 全连接层 → 池化层 → 卷积层 → 池化层。

本节启发:
col2im 是 im2col 的逆操作。卷积层反向传播的关键是把 col 形式恢复为图像形式。

5.3.5 训练

核心内容:
实现卷积神经网络训练。使用小批量更新参数。全连接层参数更新沿用之前代码,卷积过滤器权重和偏置需要新实现。训练耗时较长,但最终在 MNIST 上效果良好。

要点总结:

  • 卷积过滤器权重更新:

W(l):=W(l)−ηΔcol(l)Xcol(l−1) W^{(l)} := W^{(l)} - \eta \Delta_{col}^{(l)} X_{col}^{(l-1)} W(l):=W(l)ηΔcol(l)Xcol(l1)

  • 卷积过滤器偏置更新:

b(l):=b(l)−η∑Δcol(l) b^{(l)} := b^{(l)} - \eta \sum \Delta_{col}^{(l)} b(l):=b(l)ηΔcol(l)

  • 全连接层权重和偏置更新沿用之前实现。
  • 学习率设为 0.0001。
  • 轮数设为 5。
  • 每 10 次小批量更新输出一次日志。
  • 训练耗时约 1 小时。
  • 测试前 10 个数据,全部分类正确。
  • 输出层 10 维,取概率最高单元索引作为分类结果。

本节启发:
卷积神经网络训练计算量巨大,但效果显著。实现成功后,对理论的理解会大大加深。


专栏 后话

核心内容:
专栏讨论学习机器学习基础知识的价值,以及实践机器学习的价值。掌握基础知识有助于理解框架和新方法,但真正用于实践还需要理解问题、清洗数据、转换问题,并思考模型的价值。

要点总结:

  • 基础知识很重要。
  • 框架虽然方便,但不了解基础很难用好。
  • 学习新东西时,基础能帮助快速入门。
  • 预处理很重要:清洗数据、修补缺失值、平衡采样等。
  • 实践机器学习要思考:
    • 存在什么问题;
    • 需要做什么解决;
    • 机器学习用在哪里;
    • 模型是否有价值。
  • 不能因为流行就强行应用机器学习。
  • 必须理解问题,把问题转换成机器学习可解决的形式。
  • 学习基础知识 + 理解问题 + 创造价值,才是完整的机器学习实践。

本节启发:
机器学习不只是数学和代码,更是解决问题和创造价值。技术只有用在正确的问题上,才有意义。


本章总结

  • 使用 Python 和 NumPy 从零实现神经网络。
  • 数据标准化可以加快收敛。
  • 超参数包括层数、单元数、学习率、轮数等。
  • 正向传播、反向传播、参数更新是训练的三个核心步骤。
  • 小批量梯度下降是标准训练方法。
  • 使用 MNIST 实现手写数字识别。
  • 卷积神经网络实现使用 im2col 和 col2im 变换。
  • ReLU 和 softmax 是卷积神经网络常用函数。
  • 权重初始化很重要,He 初始化适合 ReLU。
  • 卷积神经网络训练计算量大,但效果显著。
  • 基础知识 + 实践 + 价值思考,才是完整的机器学习能力。

公式速查

标准化

x′=x−μσ x'=\frac{x-\mu}{\sigma} x=σxμ

sigmoid

σ(x)=11+e−x \sigma(x)=\frac{1}{1+e^{-x}} σ(x)=1+ex1

sigmoid 微分

σ′(x)=σ(x)(1−σ(x)) \sigma'(x)=\sigma(x)(1-\sigma(x)) σ(x)=σ(x)(1σ(x))

ReLU

ReLU(x)=max⁡(0,x) \mathrm{ReLU}(x)=\max(0,x) ReLU(x)=max(0,x)

ReLU 微分

ReLU′(x)={1,x>00,x≤0 \mathrm{ReLU}'(x)= \begin{cases} 1, & x>0 \\ 0, & x\le 0 \end{cases} ReLU(x)={1,0,x>0x0

softmax

yi=exp⁡(zi)∑jexp⁡(zj) y_i=\frac{\exp(z_i)}{\sum_j \exp(z_j)} yi=jexp(zj)exp(zi)

输出层德尔塔

Δ(L)=Y−T \Delta^{(L)}=Y-T Δ(L)=YT

隐藏层德尔塔

Δ(l)=(Δ(l+1)W(l+1))⊗a′(l)(Z(l)) \Delta^{(l)}=(\Delta^{(l+1)}W^{(l+1)})\otimes a'^{(l)}(Z^{(l)}) Δ(l)=(Δ(l+1)W(l+1))a(l)(Z(l))

参数更新

W(l):=W(l)−η1K∑kΔk(l)TXk(l−1) W^{(l)} := W^{(l)} - \eta \frac{1}{K}\sum_k \Delta_k^{(l)T}X_k^{(l-1)} W(l):=W(l)ηK1kΔk(l)TXk(l1)

b(l):=b(l)−η1K∑kΔk(l) b^{(l)} := b^{(l)} - \eta \frac{1}{K}\sum_k \Delta_k^{(l)} b(l):=b(l)ηK1kΔk(l)

卷积过滤器更新

W(l):=W(l)−ηΔcol(l)Xcol(l−1) W^{(l)} := W^{(l)} - \eta \Delta_{col}^{(l)} X_{col}^{(l-1)} W(l):=W(l)ηΔcol(l)Xcol(l1)

b(l):=b(l)−η∑Δcol(l) b^{(l)} := b^{(l)} - \eta \sum \Delta_{col}^{(l)} b(l):=b(l)ηΔcol(l)


金句摘录

通过实现可以加深对它的理解,所以亲自动手运行神经网络是很重要的。

除了权重和偏置这种需要优化的参数之外,还需由开发者决定层数和单元数,这些参数叫作超参数。

在实现时,最好能一次性处理多个数据,这样代码更整洁,处理速度也更快。

这种随机选择一定数量的数据,并重复训练过程,可以更容易地收敛到最优解。

当从头开始实现复杂的神经网络时,权重的初始化其实是一个相当重要的问题。

如果初始化有误,可能会导致网络不会收敛甚至发散。

卷积神经网络是计算量特别大的算法。

只要用好 im2col 变换,就可以用矩阵来计算。

如果不去思考现在存在什么样的问题,需要做什么来解决这些问题,机器学习可以应用在问题的哪些地方,那就不能想着“先引入机器学习再说”。

如果想要在工作中用到机器学习,就不能只是学习数学部分,还必须理解问题,把问题转换成机器学习可以解决的数学形式之后,再去解决问题。


个人思考

  1. 第 5 章把前四章的理论全部落地。真正写代码时,才会发现很多数学公式中没提到的问题,比如数据标准化、批量处理、权重初始化、数值溢出。
  2. 标准化、初始化、学习率、轮数这些超参数,看似不起眼,但直接影响训练能否成功。
  3. 小批量是训练神经网络的标准做法,它让训练更稳定、更高效。
  4. im2col 和 col2im 是卷积神经网络实现中的关键技巧。没有它们,卷积计算会慢到无法接受。
  5. ReLU 和 softmax 的实现都有工程细节:ReLU 在 0 处不可导,实际实现取 0;softmax 要防止 exp 溢出。
  6. 池化反向传播时,未选中单元德尔塔为 0,这一点实现时必须正确处理。
  7. 卷积神经网络训练非常耗时,但看到模型正常工作时,成就感很强。
  8. 专栏提醒我们:机器学习不只是数学和代码,更是解决问题和创造价值。掌握基础、理解问题、创造价值,三者缺一不可。
  9. 这一章的主线可以概括为:数据准备 → 网络结构 → 正向传播 → 反向传播 → 参数更新 → 小批量训练 → 模型评估。

更多推荐