Skip to content

MIT深度学习课程第三讲:Keras训练实践与图像分类入门

本节课分为两部分:

  1. 使用 KerasColab 实际训练一个心脏病预测模型。
  2. 转入计算机视觉,介绍图像如何表示,并开始搭建图像分类模型。

老师强调,本节课会真正进入 Colab 实操。前半部分承接上一节神经网络训练的理论,后半部分为后续图像分类和彩色图像处理做准备。

一、本节课安排

本节课计划做两个 Colab

  • 第一个 Colab:训练一个用于心脏病预测的 Keras 模型。
  • 第二个 Colab:开始处理图像分类问题。

心脏病预测问题来自前面课程已经介绍过的数据集。图像分类部分会从数字图像的表示讲起,再进入 Fashion-MNIST 数据集。

二、神经网络训练流程回顾

老师先回顾上一节课的核心思想:训练神经网络,本质上和训练其他机器学习模型没有根本区别。

神经网络中有一组参数:

  • weights
  • biases

训练的目标是使用数据找到一组好的参数。

这里的“好”通常指:

模型预测值与真实答案之间的差异尽可能小。

这个差异由 loss function 衡量。

整体训练流程可以概括为:

  1. 定义一个网络结构。
  2. 输入数据经过多层网络。
  3. 网络输出预测值。
  4. 将预测值与真实值一起送入 loss function
  5. 得到一个 loss score
  6. optimizer 计算 loss 对所有参数的梯度。
  7. 使用梯度更新 weightsbiases
  8. 重复这个过程。

因此,训练循环中的核心对象是:

  • network
  • prediction
  • ground truth
  • loss function
  • optimizer
  • gradient
  • parameter update

三、Gradient Descent 与 Stochastic Gradient Descent

上一节课介绍过 gradient descent

在普通 gradient descent 中,每次迭代会使用所有训练样本:

  1. 所有数据点都通过网络。
  2. 对所有数据点计算预测值。
  3. 计算整体 loss。
  4. 计算 loss 对参数的梯度。
  5. 用梯度更新参数。

老师指出,实际训练中更常使用 stochastic gradient descent,简称 SGD

SGD 的思路是:

不一次使用全部数据,而是随机选择一小部分样本,把它们暂时当成当前拥有的全部数据,基于这部分样本计算预测、loss、gradient,并更新参数。

这种小批量样本在技术上叫 minibatch。老师说明,后面会直接把它称为 batch,这也是文献和实践中常见的简化说法。

四、Epoch 的定义

epoch 指的是:

对整个训练集完成一次完整遍历。

也就是说,所有训练样本都至少被送入网络一次。

在普通 gradient descent 中:

  • 一个 epoch 使用所有训练样本。
  • 在这个 epoch 结束时计算一次梯度。
  • 参数只更新一次。

老师提到,梯度可以写成:

text
d loss / dW

也可以用 nabla 符号表示。这些都是表示 loss 关于参数的梯度。

如果数据集有 10 billion 个样本,普通 gradient descent 会让所有样本都通过网络,得到所有输出后,再在 epoch 末尾进行一次参数更新。

五、SGD 中的 Batch 与 Epoch

SGD 中,训练数据会被分成多个 batches

一个 epoch 的过程是:

  1. 将训练集分成 batch 1batch 2,直到最后一个 batch。
  2. batch 1 做一次前向传播,得到预测值。
  3. 计算 loss 和 gradient。
  4. 更新参数。
  5. 用更新后的参数处理 batch 2
  6. 持续处理所有 batches。
  7. 所有训练数据都被处理一遍后,一个 epoch 结束。

因此,在 SGD 中:

一个 epoch 内会发生多次参数更新,更新次数等于 batch 数量。

老师举例:

  • 如果一个 batch 有 32 个样本。
  • 处理完第一个 batch 后,网络参数已经更新。
  • 第二个 batch 使用的已经是更新后的参数。

这也是 SGD 的重要优势:不必等所有数据都处理完才更新,而是边看数据边更新。

六、Batch 数量如何计算

在一个 epoch 中,batch 数量由训练集大小和 batch size 决定。

公式可以理解为:

text
number_of_batches = ceil(training_set_size / batch_size)

老师强调,batch size 是建模者选择的。

常见默认值包括:

  • 32
  • 64

这些值常用的原因之一是它们与并行硬件的计算方式比较匹配。

Keras 会自动计算实际 batch 数量,建模者不需要手动管理每个 batch。但老师强调,仍然应该理解细节,因为只有概念足够清楚,后续遇到新问题时才能真正操作这些概念,而不是只会运行默认代码。

课堂示例:

  • 训练集大约有 194 个患者。
  • batch size = 32
  • 194 / 32 约等于 6.x
  • 向上取整后得到 7 个 batches。

于是:

  • 6 个 batch 各有 32 个样本。
  • 最后一个 batch 只剩下少量样本,例如 2 个。

老师指出,最后一个 batch 不需要和前面的 batch 一样大。

七、关于 Batch 与 Epoch 的课堂问答

7.1 Batch 是取部分变量还是部分数据

学生问:SGD 中只取一部分,是取部分变量,还是取部分数据?

老师回答:

取的是部分 rows,也就是部分 data points。

例如:

  • 1000 个患者样本中取 32 个患者。
  • 1000 张图片中取 32 张图片。

32 个样本构成一个 batch。

不是只取部分特征列,也不是只取网络的一部分。

7.2 每个 Batch 是否都会更新权重

学生问:每次处理 batch 时,是否都会更新当前权重?

老师回答:会。

处理每个 batch 后,都会根据该 batch 计算出的 gradient 更新参数。

因此,训练过程是在不断“边走边修正”。

7.3 Epoch 不是 Loss Function

学生把 epoch 理解成正在求的 loss function。

老师纠正:

epoch 不是 loss function,而是对训练数据完成一次完整遍历。

SGD 中,一个 epoch 内有多个 batches,也就有多次参数更新。

7.4 每个 Batch 是否经过整个网络

学生问:一个 batch 是经过整个网络,还是只经过某一层?

老师回答:

每个 batch 都要经过整个网络。

原因是:

  • 不经过整个网络,就得不到预测值。
  • 没有预测值,就无法计算 loss。
  • 没有 loss,就无法计算 gradient。
  • 没有 gradient,就无法更新 weights。

7.5 普通 Gradient Descent 与 SGD 的更新次数

在普通 gradient descent 中:

  • 一个 epoch 是一次完整遍历。
  • 一个 epoch 只做一次权重更新。

SGD 中:

  • 一个 epoch 仍然是一次完整遍历。
  • 但每个 batch 之后都会更新一次。
  • 更新次数等于 ceil(training_set_size / batch_size)

7.6 SGD 的优势

学生总结:使用 SGD 的优势是,处理完第一个 batch 后,第二个 batch 已经可以使用更好的系数。

老师确认这个理解是正确的。

虽然单个 batch 只是数据的一小部分,梯度估计不如全量数据精确,但通常仍能朝着正确方向更新。这样可以避免等待整个数据集处理完再更新。

学生问:SGD 是得到更好的解,还是节省计算?

老师回答:两者都有。具体原因已在上一讲中讨论。

八、Overfitting、Underfitting 与 Regularization

进入 Colab 前,老师补充了 overfittingregularization

当模型越来越复杂时,训练集误差通常会下降。

例如:

  • 简单回归模型有一定误差。
  • 加入 interaction terms、logarithms 等复杂变换后,训练集误差通常下降。

但模型复杂度继续增加时,验证集或真实泛化表现可能会先变好,再变差。

这对应三个区域:

  • underfitting:模型还太简单,仍有明显改进空间。
  • 合适区域:模型复杂度与数据模式匹配。
  • overfitting:模型开始拟合训练数据中的偶然性和特殊细节,而不是可泛化规律。

目标是找到中间的 sweet spot

九、神经网络为什么容易 Overfit

在神经网络中,模型需要学习输入数据的 representations

为了学习复杂 representation,通常会增加网络层数和参数数量。

老师举例,GPT-3 大约有 96 层。

一般来说:

  • 层数越多,表达能力越强。
  • 层数越多,参数越多。
  • 参数越多,模型复杂度越高。
  • 模型复杂度越高,overfitting 风险越大。

因此,神经网络训练中特别需要考虑 regularization

regularization 的目标是:

降低过拟合风险,让模型既能拟合数据,又不过度贴合训练集中的噪声。

十、Early Stopping

本节课会用到的第一种 regularization 方法是 early stopping

老师提到,Geoff Hintonearly stopping 称为一种 “beautiful free lunch”。Geoff Hinton 是深度学习先驱之一,也获得过图灵奖。

early stopping 的基本方法是:

  1. 将训练数据拆成训练集和验证集。
  2. 持续训练模型。
  3. 训练过程中,训练集 loss 通常会继续下降。
  4. 同时监控验证集表现。
  5. 如果验证集表现开始停滞甚至变差,就停止训练。

关键点是:

验证集只用于评估,不用于计算梯度。

也就是说,验证集不会参与参数更新。

老师说明,Colab 中会先完整跑一遍,观察验证集曲线在哪里变平或变差,再判断应该在哪里停止。实际使用中,Keras 有更高效的方式,不一定要训练到最后再手动回退。

十一、Dropout

另一种 regularization 方法是 dropout

老师说明,dropout 是一种非常巧妙的策略,但本节课不会使用。

本节课主要使用 early stoppingdropout 会在后续课程第一次使用时再详细解释。

十二、训练神经网络的实践清单

老师将训练流程总结为:

  1. 准备数据。
  2. 设计网络结构。
  3. 决定隐藏层数量。
  4. 决定每层神经元数量。
  5. 选择合适的输出层。
  6. 选择与输出类型匹配的 loss function
  7. 选择 optimizer
  8. 选择 regularization 方法,例如 early stoppingdropout
  9. KerasTensorFlow 启动训练。

优化器方面,SGD 有很多变体。

类似隐藏层中常把 ReLU 作为默认激活函数,优化器中常把 Adam 作为默认选择。老师说明,本节课会使用 Adam

十三、什么是 Tensor

进入 TensorFlowKeras 前,老师先解释 tensor

学生最初回答 tensor 是输入数据,可以表示图像、时间序列等。老师指出这个方向接近,但 tensor 的概念更基础。

更准确地说:

tensor 是对 number、vector、table 等对象的统一推广。

不同 rank 的 tensor 可以这样理解:

对象Tensor rank含义
单个数字rank 0没有维度
向量rank 1一维数字列表
表格 / 矩阵rank 2二维数字表
一组表格rank 3可以理解为很多张表叠在一起
视频rank 4一串彩色图片

老师强调,tensor 不一定比矩阵复杂。单个数字本身也是 tensor。

十四、Tensor 的 Rank 与嵌套结构

每个 tensor 都有 rank

可以从嵌套列表角度理解 tensor:

  • rank 1 tensor 是数字列表。
  • rank 2 tensor 是列表的列表,也就是表格。
  • rank 3 tensor 是很多张表构成的列表。
  • rank 4 tensor 可以是很多彩色图片构成的序列。

例如,形状为 (7, 3)rank 2 tensor 可以理解为 7 行、3 列的表格。老师还建议把 tensor 想成由方括号包起来的嵌套数组:外层每向内取一个元素,就进入低一阶的 tensor。

老师给出一个重要理解:

rank n tensor 可以看作一组 rank n-1 tensors

例如:

  • 一个视频是 rank 4 tensor
  • 一组视频就是 rank 5 tensor
  • 一个 rank 10 tensor 可以看作一组 rank 9 tensors

因此,对于任意 tensor,都可以沿第一个维度进行迭代。每取出一个元素,就得到低一阶的 tensor。

老师提到,tensor 概念来自 Einstein,它既直观又容易让人觉得滑。建议阅读教材 2.2 中关于 tensor 的讨论,并通过练习熟悉。

十五、图像与 Tensor 的关系

老师提前指出,后面会看到:

  • 灰度图像通常表示为一张数字表,也就是 rank 2 tensor
  • 彩色图像通常表示为三张数字表,对应颜色通道。
  • 视频可以看作一串彩色图像,因此是更高 rank 的 tensor。

这为后面计算机视觉部分做铺垫。

十六、TensorFlow 的作用

TensorFlow 是一个深度学习库。

名字中的 TensorFlow 可以这样理解:

神经网络中流动的是 tensors,tensors 从输入端进入网络,经过层层计算,再从输出端流出。

TensorFlow 提供几个关键能力:

  1. 自动计算任意复杂 loss function 的 gradients。
  2. 提供各种 optimizers,包括 SGD 及其变体。
  3. 支持将计算分布到多个服务器。
  4. 自动组织计算以利用 GPU

老师强调,手动计算复杂网络的梯度非常痛苦。TensorFlow 可以自动处理链式法则和梯度计算,这是它非常重要的价值。

并行计算方面,老师指出,不是所有计算都容易并行化。TensorFlow 会帮助处理这些复杂性。

GPUGraphics Processing Unit,适合并行计算。即使只有一台有 GPU 的机器,也需要以特定方式组织计算才能充分利用硬件,TensorFlow 会自动完成很多工作。

老师还提到 TPU

TPUTensor Processing Unit,可以理解为 Google 设计的类似 GPU 的硬件。

十七、Keras 与 TensorFlow 的关系

Keras 位于 TensorFlow 之上。

可以理解为:

text
hardware → TensorFlow → Keras

TensorFlow 负责底层计算、梯度、优化和硬件利用。

Keras 提供更方便的高层抽象,例如:

  • layers
  • activation functions
  • 数据预处理工具
  • 模型训练接口
  • 指标报告
  • training loss
  • validation loss
  • validation accuracy
  • 预训练模型库

老师之前用过的 keras.Dense 就是一个 dense layer。

十八、为什么课程选择 TensorFlow/Keras

老师提到,很多学生可能熟悉 PyTorchPyTorch 也是非常优秀的深度学习框架。

本课程选择 TensorFlowKeras 的原因是:

  • 希望课程对编程背景较少的学生更友好。
  • PyTorch 从计算机科学角度要求更高。
  • PyTorch 更依赖对面向对象编程的理解。
  • TensorFlow/Keras 在能力上足够强,同时更容易上手。

老师认为,Keras 很像一个设计良好的 LEGO 积木环境,可以用相对简单的方式搭建复杂模型。

十九、Keras 的三种 API

Keras 有三种主要 API:

  • Sequential API
  • Functional API
  • Subclassing API

本课程几乎都会使用 Functional API

心脏病预测模型也是用 Functional API 搭建的。

老师建议阅读教材 7.2.2,理解 Functional API 的具体工作方式。

老师还提到,相关网站上有许多 KerasColab 示例资源,可以作为课后练习材料。

二十、心脏病预测模型回顾

回到上一节课设计的心脏病预测模型。

模型结构是:

  1. 输入层。
  2. 一个 dense hidden layer。
  3. hidden layer 有 16ReLU neurons。
  4. 输出层使用 sigmoid

因为这是二分类问题,所以输出是一个概率。

训练设置:

  • loss function:binary_crossentropy
  • optimizer:Adam
  • regularization:使用 early stopping 思路监控过拟合

老师提醒:

  • 如果预测任意连续数值,可以用 mean squared error
  • 如果预测二分类概率,并与 0/1 标签比较,应使用 binary_crossentropy

二十一、Colab 使用建议

进入 Colab 前,老师给出实践建议:

  • 不必在课堂上立刻理解每一行代码。
  • 课后应逐行阅读和运行 notebook。
  • 学习新代码时,尽量不要直接复制粘贴。
  • 更推荐手动重打一遍代码,这样更容易理解。

对于课程发布的 Colab

  1. 先使用 Copy to Drive 建立自己的副本。
  2. 如果 notebook 涉及自然语言处理或视觉任务,通常应该使用 GPU runtime。
  3. 之后再次打开同一个 notebook 时,可以直接从 runtime 设置开始。

老师也提到,网上有很多关于 Colab 的短教程,可以自行查看。

二十二、心脏病数据集

心脏病预测 Colab 使用前面介绍过的数据集。

数据集包含:

  • 303 名患者。
  • 多个变量,包括年龄、人口统计信息、生物标志物等。
  • 目标变量是 01,表示是否有心脏病。

老师说明,课堂中不会实时运行每个 cell,因为运行完整 notebook 会占用课堂时间。他已经提前运行过,会逐步解释每部分代码的含义。

二十三、常用 Python 包

每次训练深度学习模型时,通常会加载一组常见包。

本节课中包括:

  • tensorflow
  • keras
  • NumPy
  • Pandas
  • Matplotlib

用途分别是:

  • TensorFlow/Keras:构建和训练深度学习模型。
  • NumPy:处理矩阵、数组和 tensors。
  • Pandas:清洗、整理和转换表格数据。
  • Matplotlib:绘制 loss 曲线和 accuracy 曲线,辅助观察是否需要 early stopping。

老师特别指出,通常会先导入 TensorFlow,再从 TensorFlow 中导入 Keras

二十四、训练中的随机性与 Random Seed

深度学习训练中存在多个随机性来源:

  1. 初始权重通常随机初始化。
  2. SGD 中会随机打乱数据,再形成 batches。
  3. 拆分 trainvalidationtest 时也可能涉及随机抽样。
  4. dropout 本身也带有随机性。

如果希望别人能复现结果,就需要设置 random seed

课堂中使用:

text
set_random_seed(42)

老师说明,42 是对《The Hitchhiker's Guide to the Galaxy》的流行文化引用。

不过,即使设置 seed,也不保证完全得到相同数字。由于硬件、设备驱动等原因,bitwise reproducibility 并不总是有保证。

二十五、读入数据与查看类别比例

老师提到,Francois Chollet 将这个数据整理成了 Pandas dataframe,可以从 CSV 读入。

读入后数据形状是:

text
303 rows, 14 columns

第一步通常查看前几行,理解变量含义和目标列。

对于二分类问题,老师会先检查正负类比例。

本数据中:

  • 72.6% 的患者没有心脏病。
  • 27.4% 的患者有心脏病。

这不是 50/50 的平衡数据集,而是负类明显更多。

二十六、Baseline Model

老师提出问题:如果不使用复杂模型,最简单的 baseline 是什么?

学生回答:

永远预测 0,也就是预测所有人都没有心脏病。

因为 72.6% 的样本属于 0 类,所以这个 baseline 的 accuracy 是:

text
72.6%

任何更复杂的模型都应该超过这个 baseline。否则,就没有使用复杂模型的价值。

二十七、数据预处理:分类变量与数值变量

该数据集同时包含:

  • categorical variables
  • numeric variables

因此,通常先把两类变量分开处理。

27.1 Categorical Variables

神经网络不能直接处理类别输入。

与某些模型不同,例如 decision tree,神经网络需要数值输入。

因此,类别变量必须先数值化。

标准做法是:

text
one-hot encoding

Pandas 中,可以使用:

text
get_dummies

例如变量 thal 原来可能有:

  • fixed
  • normal
  • reversible

经过 one-hot encoding 后,会变成多个列:

  • thal_fixed
  • thal_normal
  • thal_reversible

27.2 Numeric Variables

神经网络通常在输入数值范围较小、尺度相近时训练效果更好。

因此,数值变量需要标准化:

text
standardized_value = (value - mean) / standard_deviation

也就是:

  1. 减去均值。
  2. 除以标准差。

标准化后,数值大多落在 01-1 附近的范围,更适合神经网络训练。

二十八、为什么先划分 Test Set,再标准化

老师强调,标准化之前应该先划分测试集。

流程应是:

  1. 先将数据划分为 training set 和 test set。
  2. 将 test set 锁起来,不参与建模过程。
  3. 只基于 training set 计算 mean 和 standard deviation。
  4. 用 training set 的 mean/std 标准化 training set 和 test set。

原因是:

如果在划分之前用全数据计算 mean/std,标准化过程就已经使用了 test set 的信息。

这样会导致数据泄漏。测试集就不再是完全独立的最终评估数据。

课堂中使用约 80% / 20% 的划分:

  • training set:242 rows
  • test set:61 rows

老师提到,dataframe.shape 可以查看数据维度。

二十九、转换为 NumPy Array

完成 one-hot encoding、标准化和 train/test split 后,数据已经全部是数值。

此时需要将 Pandas dataframe 转成 NumPy array,因为 Keras 可以直接接收 NumPy 数据。

使用方法:

text
to_numpy

老师指出,to_numpy 的名字非常直观。

转换后,traintest 就是 NumPy arrays,保存了同样的信息,但格式更适合送入 Keras

三十、拆分 X 与 Y

原始 dataframe 中包含:

  • 输入特征 columns。
  • 目标变量 column。

训练时必须把两者分开:

  • X:输入特征。
  • Y:真实标签。

原因是:

  1. 模型只接收 X 做预测。
  2. 预测结果再与真实 Y 比较。
  3. 根据差异计算 loss。

课堂中,目标列是左起第 6 列。

处理后:

  • train_x242 x 29
  • test_x61 x 29
  • train_y 是长度为 242 的向量。
  • test_y 是长度为 61 的向量。

这也解释了上一节课中模型为什么有 29 个输入节点。

三十一、搭建心脏病预测模型

数据预处理完成后,开始搭建模型。

模型是上一节课设计过的结构:

  • 输入:29 个 features。
  • hidden layer:16ReLU neurons。
  • output layer:1sigmoid 输出。

这是一个二分类问题,因此输出层用 sigmoid

代码中老师给 layer 加了名字。layer name 是可选的,主要用于后续解释和查看模型结构。

三十二、model.summary() 与参数数量

老师建议,模型建好后应立即运行:

text
model.summary()

model.summary() 会显示:

  • 每一层是什么类型。
  • 输入该层的 tensor shape。
  • 该层输出的 tensor shape。
  • 每层参数数量。

课堂模型总共有 497 个参数。

参数数量计算如下:

hidden layer:

text
29 * 16 + 16

其中:

  • 29 * 16 是输入到 hidden layer 的 weights。
  • 16 是 hidden layer 的 biases。

output layer:

text
16 * 1 + 1

其中:

  • 16 * 1 是 hidden layer 到 output layer 的 weights。
  • 1 是 output bias。

总数:

text
29 * 16 + 16 + 16 * 1 + 1 = 497

老师再次提醒,初学时应手动验证参数数量,确保理解网络结构。

也可以使用 plot_model 可视化网络拓扑。后面网络更大时,图形化结构会更有帮助。

三十三、compile:选择 Loss、Optimizer 与 Metrics

模型训练前需要调用 compile

本例设置:

  • loss function:binary_crossentropy
  • optimizer:Adam
  • metrics:accuracy

老师强调,metrics 只用于报告,不用于优化。

例如 accuracy 会显示出来,方便观察模型表现,但不会直接参与梯度计算。

compile 的作用是:

将模型、loss、optimizer、metrics 等信息组织起来,使模型适合后续训练、并行计算和分布式计算。

这也是为什么在 Keras 中训练前需要一个 compilation step。

三十四、model.fit():开始训练

真正训练模型使用:

text
model.fit(...)

需要传入:

  • x tensor。
  • y tensor。
  • epochs
  • batch_size
  • verbose
  • validation_split

本例选择:

  • batch_size = 32
  • epochs = 300
  • validation_split = 0.2
  • verbose = 1

其中,verbose = 1 表示训练时输出较多描述性日志,方便观察训练过程。

老师说明,32 是一个很常见的 batch size 默认值。实践中可以尝试 3264128 等。

有研究者的经验说法是:

使用机器内存能承受的最大 batch size 往往是一个可以尝试的方向。

但本例数据很小,从 32 开始即可。

关于 epoch 数,老师说:

  • 2030 个 epochs 通常是不错的起点。
  • 本例为了观察是否会过拟合,特意跑 300 个 epochs。

validation_split = 0.2 表示:

从 training data 中拿出 20% 作为 validation set,不用于训练,只用于观察 validation loss 和 validation accuracy。

三十五、训练输出如何阅读

model.fit 运行后会输出很多行。

每个 epoch 的输出中包含:

  • 当前 epoch 使用了多少 batches。
  • 每个 epoch 运行时间。
  • training loss。
  • training accuracy。
  • validation loss。
  • validation accuracy。

本例中,老师用第 300 个 epoch 的一行输出做解释:它显示 7/7 batches,表示该 epoch 使用了全部 7 个 batches;同一行还会显示 training loss、training accuracy,以及 epoch 结束后在 validation set 上计算出的 validation loss 和 validation accuracy。

课堂示例中,该 batch 的 training accuracy 显示为 98.5%。老师强调,这些数字都只是日志输出中的不同字段,直接盯着一大段数字不容易理解,因此通常会画成曲线。

这与前面的 batch 计算一致:

  • 从 training set 中再拿出 20% 做 validation 后,剩余训练样本约为 193194
  • batch_size = 32
  • 向上取整后是 7 个 batches。

老师解释,前 6 个 batches 大约各有 32 个样本,最后一个 batch 只剩很少样本。

三十六、history 对象与绘图

老师将训练结果保存为:

text
history = model.fit(...)

history 对象包含训练过程中的诊断信息。

其中:

text
history.history

是一个 dictionary,保存了各个 epoch 的:

  • loss
  • accuracy
  • validation loss
  • validation accuracy

这些值可以用 Matplotlib 绘制曲线,用于判断训练趋势和过拟合。

三十七、Validation Set 与 Test Set 的区别

学生问:已经划分过 test set,为什么 model.fit 还要留出 validation set?

老师解释:

三类数据集的角色不同:

  • training set:用于训练和更新参数。
  • validation set:用于 early stopping 和 hyperparameter tuning。
  • test set:最终模型确定后,只使用一次,用于估计真实部署表现。

validation set 用来回答:

  • 是否应该 early stop。
  • 哪组 hyperparameters 更好。
  • 模型是否开始过拟合。

test set 则要一直锁起来,直到最终模型确定。

最后评估 test set 的目的不是改模型,而是了解模型部署到真实世界后的可能表现。

三十八、关于 Metrics 的问答

学生问:能否不用 accuracy,而使用其他指标,例如 confusion matrix 相关指标?

老师回答:可以。

可用指标包括:

  • error rate
  • accuracy
  • F1
  • F-beta
  • 其他基于 confusion matrix 的指标

关键点是:

metrics 只是报告给建模者看的,不参与训练优化。

Keras 提供了很多可选 metrics。

三十九、Validation Split 的具体含义

学生问:validation_split = 0.2 是从训练集中拿出 20%,还是从每个 batch 中拿出 20%

老师回答:

从 training set 中拿出 20%

拿出 validation set 后,剩下的数据才被分成 batches 进行训练。

因此,可用于计算 batch 数量的训练数据会减少。

老师进一步说明:

  • validation set 在训练开始前固定下来。
  • 每个 epoch 使用同一个 validation set。
  • 这不是 cross-validation。
  • 每个 epoch 不会重新抽一个新的 validation set。

另一个学生用具体数值确认:如果 training loss 已经到 0.0860,而 validation loss 是 0.660,是否说明已经越过较合适的停止点,最终部署时应使用前面那个更好的模型。老师确认这个判断方向,并说明后面会讲如何拿到对应的模型,例如是否需要重新从头训练到某个 epoch,或用更智能的方法保存最佳模型。

学生问:每个 epoch 有什么不同?

老师回答:

不同的是 weights 已经改变。

在一个 epoch 内,每个 minibatch 后 weights 都会更新。一个 epoch 结束后,模型已经经历了所有训练样本,然后再从头开始下一轮。

四十、观察 Loss 曲线

老师用 Matplotlib 绘制:

  • training loss
  • validation loss

横轴是 epochs。

观察结果:

  • training loss 稳定下降。
  • validation loss 一开始下降。
  • 到某个阶段后变平。
  • 后面可能轻微上升。

这说明模型可能出现了一定程度的 overfitting

不过,老师提醒:

binary_crossentropy 是一个数学上方便的 proxy,它能衡量预测与真实标签的差异,但实际部署时可能更关心 accuracy 或 error rate。

因此,不能只看 loss,也要看 accuracy。

四十一、观察 Accuracy 曲线

老师继续绘制:

  • training accuracy
  • validation accuracy

观察结果:

  • training accuracy 随 epoch 增加而上升。
  • training accuracy 可以达到低到中 90% 的水平。
  • validation accuracy 大约在 50 个 epochs 附近变平。
  • 后面 validation accuracy 甚至又有所上升。

因此,虽然 validation loss 显示出一些 overfitting,但 validation accuracy 最后仍然变好。

老师的判断是:

这种 overfitting 可能可以接受,不一定需要立刻停止。

可以选择:

  • 严格按照 validation loss,在约 50 个 epochs 处停止。
  • 或者接受当前结果,因为 accuracy 后续仍有改善。

老师更倾向于关注与真实部署目标更接近的指标。如果真实世界中关心的是错误率或准确率,那么 accuracy 的信号也很重要。

四十二、Epoch 数量如何决定

学生问:老师前面说 2030 个 epochs 是起点,但这里跑了 300 个。如果只跑 2030,可能还看不到过拟合,怎么办?

老师回答:

2030 是起点,不是固定规则。

做法是:

  1. 先训练一段,例如 2030 个 epochs。
  2. 查看 validation loss 是否仍在下降。
  3. 如果还在下降,就继续训练。
  4. 可以再运行 fit 继续训练 10 个 epochs。
  5. 如果 validation loss 开始上升,再停止或回退。

Keras 的特点是:

再次调用 fit 时,会从当前权重继续训练,而不是从头开始。

老师说明,手动观察曲线是为了建立直觉。实际中可以使用 callbacks

callbacks 可以设置:

  • 当 validation loss 不再改善时自动停止。
  • 当 validation loss 不再改善时保存最佳模型。

这样就不必手动回到某个 epoch 重新训练。

四十三、为什么 Loss 变差时 Accuracy 还可能变好

学生问:为什么 validation loss 变高时,accuracy 还能变好?

老师回答:

accuracy 和 loss 相关,但不是同一个东西。

accuracy 是离散指标。

例如二分类中:

  • 预测概率 0.49 可能被判为 0
  • 预测概率 0.51 可能被判为 1

0.490.51

  • binary_crossentropy 变化可能很小。
  • 但 accuracy 会从错变成对,发生跳跃式变化。

因此,accuracy 是一个比较跳跃、离散、粗糙的指标,而 loss 是连续的优化目标。

老师还提到,可以尝试取某个 epoch 的权重,例如 epoch 50,并比较其验证表现。最终仍要看哪个指标更接近真实部署目标。

四十四、最终 Test Set 评估

模型训练并完成判断后,使用:

text
model.evaluate(test_x, test_y)

此时才使用最开始保留的 test set。

老师运行结果显示,模型在 test set 上达到:

text
83.6% accuracy

前面的 baseline 是:

text
72.6% accuracy

因此,这个小型神经网络明显超过了简单 baseline。

老师提醒,许多 preprocessing 步骤也可以放进 Keras 内部完成,Keras 有相应的 preprocessing layers。

四十五、课后如何学习这个 Colab

老师建议课后:

  • 逐行阅读 notebook。
  • 修改每一行。
  • 重新运行。
  • 观察输出如何变化。

这种方式有助于建立训练模型的直觉。

四十六、关于 False Positive 与 False Negative 的问答

学生问:是否可以构建一个模型,让它更少产生 false positives 或 false negatives?

老师回答:可以。

可以做两件事:

  1. 报告这些指标,例如 false positive rate、false negative rate。
  2. 使用更复杂的 loss functions,将 false positive 和 false negative 的不对称代价纳入训练。

老师没有在本节课展开细节,只说明这是可行的。

四十七、灰度图像如何表示

进入计算机视觉部分后,老师先讲数字图像表示。

灰度图像,也就是黑白图像,可以表示为一张数字表。

图像中的每个位置叫 pixel

每个 pixel 有一个光强值:

  • 0 表示没有光,对应黑色。
  • 255 表示非常亮,对应白色。
  • 中间数值表示不同灰度。

老师用数字 5 的图像举例:

  • 黑色区域通常是 0
  • 白色笔画区域是更大的数。
  • 数值越接近 255,越亮。

如果离远一点看这些数字表,甚至可以看出数字 5 的形状。

四十八、像素光强的单位

学生问:光强的单位是什么?

老师回答,本课程中只需要把它理解为一个归一化尺度。

真实世界中的模拟图像会经过数字化过程,被映射到 0255 的范围。

这里不需要关心具体物理单位或 lumens 到数值的精确映射。不同设备和处理方式可能有很多变化。

对本课程来说,理解为:

每个 pixel 的光强被映射为 0255 的相对数值。

即可。

四十九、彩色图像如何表示

彩色图像中,每个 pixel 不再只有一个数字,而是有三个数字。

这三个数字表示颜色通道的强度:

  • red
  • green
  • blue

每个通道仍然是 0255 之间的数。

因此,一张彩色图像可以表示为三张数字表:

  • red channel table
  • green channel table
  • blue channel table

在深度学习中,这些颜色维度通常称为 channels

老师以 Killian Court 图片为例,说明一张彩色图片对应三张表,因此可以看作一个 rank 3 tensor

五十、计算机视觉中的主要任务

老师介绍了几类常见图像处理任务。

50.1 Image Classification

image classification 是最基础的任务。

问题形式是:

给定一张图片,判断它属于哪个类别。

经典例子是:

  • dog vs cat

也可以是多类别分类。

本节课后面会处理图像分类问题。

50.2 Localization

localization 不只判断图片中有什么,还要判断目标在图片中的位置。

例如:

  • 判断图片中有 sheep。
  • 同时输出 sheep 的位置。

通常用一个 bounding box 表示位置。

模型输出不仅包括类别,还包括 box 的坐标,例如:

  • top-left coordinate
  • bottom-right coordinate

50.3 Object Detection

object detection 处理更复杂的情况:

一张图片中可能有多个对象,需要识别每个对象并定位它们。

例如图片中有多只 sheep,需要分别框出:

  • sheep 1
  • sheep 2
  • sheep 3

老师提到,自动驾驶系统会不断进行 object detection。

摄像头系统会每秒多次识别和定位:

  • pedestrian
  • zebra crossing
  • dog
  • stroller
  • 其他道路对象

50.4 Semantic Segmentation

semantic segmentation 是对每个 pixel 进行分类。

它不是给整张图片一个类别,而是给每个像素一个类别。

例如:

  • 这些 pixels 是 road。
  • 这些 pixels 是 sheep。
  • 这些 pixels 是 grass。

因此,semantic segmentation 可以理解为:

对每个 pixel 做 multi-class classification。

50.5 Instance Segmentation

instance segmentation 更进一步。

它不仅要给每个 pixel 分类,还要区分同一类别中的不同实例。

例如:

  • 这些 pixels 属于 sheep 1。
  • 那些 pixels 属于 sheep 2。
  • 另一些 pixels 属于 sheep 3。

老师指出,这些都是非常常见且有用的计算机视觉任务,都适合用 deep learning 系统处理。

五十一、Fashion-MNIST 数据集

接下来,老师进入图像分类示例:Fashion-MNIST

Fashion-MNIST 包含:

  • 70,000 张服装图片。
  • 10 个类别。
  • 类别包括 boots、sweaters、T-shirts 等。

这是一个平衡数据集:

  • 每个类别大约占 10%

因此,最简单的 naive baseline 是随机或固定预测某一类,其 accuracy 约为:

text
10%

老师说明,一个简单神经网络可以在这个任务上接近:

text
90% accuracy

五十二、图像输入为什么需要 Flatten

Fashion-MNIST 中每张图片是:

text
28 x 28

也就是一张 rank 2 tensor,一张数字表。

而前面构建的普通 dense neural network 接收的是向量。

因此,需要把 28 x 28 的表格转换成一个长向量。

这个过程叫:

text
flattening

具体做法可以理解为:

  1. 取第一行像素值。
  2. 接在向量开头。
  3. 取第二行像素值。
  4. 接在第一行后面。
  5. 持续把所有行串起来。

结果是:

text
28 * 28 = 784

因此,每张图片会变成长度为 784 的向量。

老师强调,flattening 本身没有做复杂学习,只是重新组织数据形状。

五十三、Fashion-MNIST 的简单网络结构

flatten 后,就回到了熟悉的向量输入场景。

本节课中的简单网络结构是:

  1. 输入:长度为 784 的向量。
  2. hidden layer:256ReLU neurons。
  3. output layer:处理 10 个类别。

老师说明,他尝试过几个不同 hidden layer 大小,256 个 neurons 表现不错,因此示例中使用 256

五十四、多类别输出问题

心脏病预测是二分类:

  • 输出 01
  • 用一个 sigmoid 输出概率。

Fashion-MNIST10 分类:

  • 类别包括 T-shirttrouserankle boot 等服装类别。
  • 具体编号到类别名称的映射可以查看 Fashion-MNIST 的说明页面。

一张图片只能属于其中一个类别。

因此,模型输出应该是 10 个概率,并且这些概率需要满足:

text
sum(probabilities) = 1

五十五、为什么不能用 10 个 Sigmoid

学生回答了关键原因:

10 个独立 sigmoid 的输出不一定加起来等于 1

sigmoid 可以把每个数压到 01,但如果独立使用 10 个 sigmoid,它们之间没有约束。

在 Fashion-MNIST 这种互斥分类中:

  • 只有一个类别是正确答案。
  • 模型需要输出一组彼此竞争的概率。
  • 所有类别概率应加起来为 1

因此,需要使用 softmax

五十六、Softmax Function

softmax 的作用是:

将任意一组数转换成一组非负概率,并保证它们加起来等于 1

对于 10 分类:

  1. 网络最后先输出 10 个线性数值。
  2. 对每个数取指数,例如 e^a
  3. 用每个指数值除以全部指数值之和。

形式可以理解为:

text
softmax(a_i) = exp(a_i) / sum(exp(a_j))

这样会带来两个结果:

  • exp(a_i) 一定为正。
  • 每个值都除以总和,所以所有输出加起来等于 1

因此,softmax 可以把网络输出的任意 10 个数变成 10 个类别概率。

五十七、Softmax 与 GPT-4 的关系

老师特别提醒,softmax 很重要,因为大型语言模型也使用类似思想。

他用 GPT-4 做类比:

  • 当模型生成输出时,每次发出的严格来说不是 word,而是 token
  • 可以暂时把它想象成 word。
  • 每一步生成时,模型需要在大量可能 tokens 中选择下一个。
  • 老师举例说,这是一个大约 52,000 维的 softmax

也就是说:

可以把语言模型的下一 token 预测理解为一个非常大的多分类问题。

每个 token 都是可能输出,模型通过 softmax 得到概率分布,再选择合适的输出。

五十八、输出层与 Loss Function 的对应关系

老师总结了输出层和 loss function 的匹配关系。

任务输出输出层形式常用 loss
任意单个数值linear outputmean squared error
单个概率sigmoidbinary_crossentropy
多个任意数值多个 linear outputsmean squared error
多个互斥类别概率softmaxcategorical_crossentropysparse_categorical_crossentropy

选择 loss function 时,要看标签数据如何编码。

五十九、为什么输出概率而不是直接输出 0 或 1

学生问:既然最终只有一个正确类别,为什么要输出概率,而不是直接输出 01

老师回答:

不能强迫网络精确输出 01

网络会产生它能产生的数值。建模者能做的是使用合适的输出层,把这些数值约束到需要的范围。

例如:

  • sigmoid 将输出约束在 01
  • softmax 将多个输出约束为总和为 1 的概率分布。

六十、标签编码方式与 Cross-Entropy Loss

多分类标签可以有不同编码方式。

60.1 Binary Classification

心脏病预测中,标签是:

text
0 or 1

对应 loss:

text
binary_crossentropy

60.2 Sparse Encoding

Fashion-MNIST 中,标签可以是整数:

text
0, 1, 2, ..., 9

这种形式称为:

text
sparse encoded

对应 loss:

text
sparse_categorical_crossentropy

60.3 One-Hot Encoding

同样的 10 分类标签也可以写成 one-hot vector。

例如某一类对应:

text
[0, 0, 0, 0, 0, 0, 0, 0, 0, 1]

对应 loss:

text
categorical_crossentropy

老师说明,这些方式表达的是同一个分类任务,只是标签编码不同。应根据数据实际编码选择正确的 Keras loss function。

categorical_crossentropy 可以看作 binary_crossentropy 的多类别推广。老师没有展开数学细节,但直觉类似。

六十一、Fashion-MNIST Colab:加载数据

进入第二个 Colab 后,老师沿用类似设置:

  • 导入 TensorFlowKeras
  • 导入常用的 NumPyPandasMatplotlib
  • 设置 random_seed 保证尽量可复现。

Fashion-MNISTKeras 内置标准数据集,因此不需要另找数据文件。

使用:

text
load_data

即可加载。

六十二、为什么数据集自带 Train/Test Split

Keras 提供的 Fashion-MNIST 已经预先划分好了 training set 和 test set。

原因是:

这样不同研究者或学习者可以在同一个 test set 上比较模型表现。

如果每个人自己随机划分测试集,模型准确率之间就不一定可比。

六十三、Fashion-MNIST 的 Tensor Shape

训练输入数据是一个 rank 3 tensor

可以理解为:

一组 rank 2 tensors

具体形状是:

  • 60,000 张训练图片。
  • 每张图片是 28 x 28 的数字表。

因此:

text
train_x: 60000 images, each 28 x 28

输出标签是一个长度为 60,000 的向量:

text
train_y: 60000 labels

测试集是:

  • 10,000 张测试图片。
  • 每张同样是 28 x 28
  • 对应 10,000 个标签。

六十四、类别编号与类别名称

如果查看 train_y 的前几个值,会看到类似:

text
9, 0, 3, ...

这些数字是类别编号。

Fashion-MNIST 的类别映射可以从 GitHub 页面查看,例如:

  • 0:T-shirt
  • 1:trouser
  • 9:ankle boot

老师通常会创建一个类别名称列表,把数字标签映射成人类可读的类别名。

这样查看结果时,不必反复记忆数字和类别之间的对应关系。

六十五、查看原始图像数据

老师查看第一张图片:

  • 它的标签对应 ankle boot
  • 原始图像数据是一个 28 x 28 数字表。
  • 表中可以看到很多 0,也能看到 250233 等较大的像素值。

这些数字就是灰度像素强度。

老师使用 Matplotlib 可视化前 25 张图片。

图片看起来比较小、模糊,但仍能看出服装轮廓。例如第一张是 ankle boot

老师在本节课末尾暂停,说明下一次课会继续这个图像分类流程,并进一步进入彩色图像。

六十六、本节课核心总结

本节课的知识链条可以概括为:

  • 神经网络训练通过 loss function 衡量预测与真实值差异。
  • optimizer 根据 gradient 更新 weightsbiases
  • epoch 是对训练集的一次完整遍历。
  • 普通 gradient descent 每个 epoch 更新一次。
  • SGD 将训练集分成 batches,每个 batch 后更新一次。
  • batch size 是建模者选择的,常见起点是 32
  • 复杂神经网络容易 overfit,因此需要 regularization。
  • early stopping 通过 validation set 判断何时停止训练。
  • dropout 是另一种 regularization 方法,本节课只预告。
  • tensor 是 number、vector、matrix/table 及更高维数组的统一表示。
  • TensorFlow 负责 tensors 的计算流、自动梯度、优化器、分布式计算和 GPU 利用。
  • KerasTensorFlow 之上提供 layers、training、metrics、preprocessing 和预训练模型等高层接口。
  • 心脏病预测模型使用 29 个输入、16ReLU hidden neurons、sigmoid 输出,共 497 个参数。
  • 二分类任务使用 binary_crossentropy,优化器使用 Adam
  • validation set 用于调参和 early stopping,test set 只在最终评估时使用。
  • 本例神经网络 test accuracy 为 83.6%,超过 72.6% baseline。
  • 灰度图像是 0255 的像素强度表。
  • 彩色图像由 RGB 三个 channels 构成,可视为 rank 3 tensor
  • 计算机视觉任务包括 classification、localization、object detection、semantic segmentation、instance segmentation。
  • Fashion-MNIST70,000 张服装图片的 10 分类任务,baseline 为 10%
  • 28 x 28 图像可以 flatten 成长度为 784 的向量。
  • 多类别互斥分类需要 softmax,而不是多个独立 sigmoid
  • softmax 将任意一组数转换成总和为 1 的概率分布。
  • 根据标签编码不同,多分类任务可使用 sparse_categorical_crossentropycategorical_crossentropy

本节课最重要的实践结论是:

训练神经网络不仅是搭建网络结构,还包括数据预处理、batch/epoch 设置、validation/test 划分、loss 与 metrics 的选择,以及对训练曲线的诊断。

理解这些细节后,Keras 训练流程就不只是运行几行代码,而是一个可以被分析、调试和改进的建模过程。

最后更新于: