《白话深度学习的数学》读书笔记 | 第5章 实现神经网络
关键词: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+e−x1
- 正向传播需要保留 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)=(Y−A(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(l−1)
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。
- 训练流程:
- 正向传播;
- 反向传播;
- 参数更新。
- 重复次数叫轮数,英语 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 表示。
- 使用
matplotlib的imshow查看图像。
本节启发:
数据准备是深度学习中最繁琐但最重要的步骤之一。数据形状和数值范围直接影响训练效果。
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>0x≤0
- 输出层德尔塔:
Δ(L)=Y−T \Delta^{(L)}=Y-T Δ(L)=Y−T
- 全连接隐藏层德尔塔与之前相同。
- 与全连接层相连的卷积层德尔塔:展开后与全连接隐藏层形式相同。
- 与卷积层相连的卷积层德尔塔:
- 使用 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(l−1)
- 卷积过滤器偏置更新:
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+e−x1
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>0x≤0
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)=Y−T
隐藏层德尔塔
Δ(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(l−1)
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(l−1)
b(l):=b(l)−η∑Δcol(l) b^{(l)} := b^{(l)} - \eta \sum \Delta_{col}^{(l)} b(l):=b(l)−η∑Δcol(l)
金句摘录
通过实现可以加深对它的理解,所以亲自动手运行神经网络是很重要的。
除了权重和偏置这种需要优化的参数之外,还需由开发者决定层数和单元数,这些参数叫作超参数。
在实现时,最好能一次性处理多个数据,这样代码更整洁,处理速度也更快。
这种随机选择一定数量的数据,并重复训练过程,可以更容易地收敛到最优解。
当从头开始实现复杂的神经网络时,权重的初始化其实是一个相当重要的问题。
如果初始化有误,可能会导致网络不会收敛甚至发散。
卷积神经网络是计算量特别大的算法。
只要用好 im2col 变换,就可以用矩阵来计算。
如果不去思考现在存在什么样的问题,需要做什么来解决这些问题,机器学习可以应用在问题的哪些地方,那就不能想着“先引入机器学习再说”。
如果想要在工作中用到机器学习,就不能只是学习数学部分,还必须理解问题,把问题转换成机器学习可以解决的数学形式之后,再去解决问题。
个人思考
- 第 5 章把前四章的理论全部落地。真正写代码时,才会发现很多数学公式中没提到的问题,比如数据标准化、批量处理、权重初始化、数值溢出。
- 标准化、初始化、学习率、轮数这些超参数,看似不起眼,但直接影响训练能否成功。
- 小批量是训练神经网络的标准做法,它让训练更稳定、更高效。
- im2col 和 col2im 是卷积神经网络实现中的关键技巧。没有它们,卷积计算会慢到无法接受。
- ReLU 和 softmax 的实现都有工程细节:ReLU 在 0 处不可导,实际实现取 0;softmax 要防止 exp 溢出。
- 池化反向传播时,未选中单元德尔塔为 0,这一点实现时必须正确处理。
- 卷积神经网络训练非常耗时,但看到模型正常工作时,成就感很强。
- 专栏提醒我们:机器学习不只是数学和代码,更是解决问题和创造价值。掌握基础、理解问题、创造价值,三者缺一不可。
- 这一章的主线可以概括为:数据准备 → 网络结构 → 正向传播 → 反向传播 → 参数更新 → 小批量训练 → 模型评估。
更多推荐



所有评论(0)