Vizuara-从零开始的计算机视觉笔记-全-
Vizuara 从零开始的计算机视觉笔记(全)
001:卷积、池化与AlexNet

在本节课中,我们将学习卷积神经网络的核心组成部分:卷积层和池化层。我们将从基础概念开始,解释卷积和池化(特别是最大池化)的工作原理,并最终实现一个具有里程碑意义的网络——AlexNet。通过本课,你将理解这些操作如何帮助模型从图像中提取特征。
课程概述
在之前的课程中,我们尝试了线性模型、带隐藏层的模型以及各种防止过拟合的技术,但模型在五类花卉数据集上的验证准确率提升有限。随后,我们通过迁移学习,利用在ImageNet上预训练的模型,将验证准确率显著提升至约80%。本节课,我们将深入探讨构成这些强大视觉模型的基础运算——卷积与池化,并最终应用预训练的AlexNet模型,以期获得更好的性能。
上一节我们回顾了模型演进的历程,本节中我们来看看卷积操作的具体定义。
什么是卷积?
卷积操作可以理解为一个小矩阵在另一个大矩阵上进行“滑动点积”的过程。
- 小矩阵被称为卷积核或滤波器。
- 大矩阵可以看作是图像的像素值。
以下是一个卷积核在图像上滑动的示意图:

在深度神经网络中,实际情况是多个卷积操作依次应用于数据。因此,我们将从单个卷积核的应用开始学习,然后再将其视为一系列堆叠的操作。
理解了卷积的基本概念后,接下来我们看看另一种重要的操作——池化。
理解池化操作
池化(尤其是最大池化)是许多深度神经网络中常用的操作。它主要用于对特征图进行下采样,减少数据量,同时保留最重要的特征信息。
以下是池化操作的核心步骤:
- 在输入矩阵上定义一个滑动窗口(例如2x2)。
- 将窗口滑过整个矩阵,每次移动一个步长。
- 对于每个窗口,取出其中的最大值(最大池化)或平均值(平均池化)。
- 将这些值组成一个新的、尺寸更小的输出矩阵。
我们已经介绍了卷积和池化这两种基础操作,现在可以将它们组合起来,构建一个完整的卷积神经网络模块。
卷积神经网络层
一个典型的卷积神经网络层通常按以下顺序执行操作:
- 卷积:使用多个卷积核提取图像特征。
- 激活函数:引入非线性,例如使用ReLU函数:
output = max(0, input)。 - 池化:对激活后的特征图进行下采样。
在深度网络中,这样的“卷积-激活-池化”模块会被堆叠多次,使网络能够从低级特征(如边缘、纹理)逐步学习到高级特征(如物体部件、整体形状)。
掌握了这些基础知识后,我们就可以探索一个将这些思想成功应用于实践的历史性网络——AlexNet。
AlexNet:改变计算机视觉的神经网络
AlexNet是机器学习和深度学习发展史上的一个里程碑。它彻底改变了深度学习在处理计算机视觉任务上的方式。
我们将详细理解AlexNet的架构,并为我们一直使用的五类花卉数据集实现它。由于从头训练AlexNet需要大量时间且不必要,我们将像在迁移学习课程中学到的那样,使用预训练的AlexNet模型。我们将观察在五类花卉数据上应用AlexNet能得到什么样的训练准确率和验证准确率。

本节课我们一起学习了卷积和池化的核心概念,并了解了经典网络AlexNet的历史地位。通过实现预训练的AlexNet,我们有望在图像分类任务上获得比之前方法更优的性能。
002:迁移学习


欢迎回到计算机视觉从零开始的系列课程。在本节课中,我们将学习迁移学习。
如果你没有观看本系列之前的一些视频,不必担心。因为本节课的内容几乎是独立的,因为迁移学习的概念普遍适用于计算机视觉内外的各种深度学习问题。
如果你一直在学习本课程之前的讲座,可能会感到有些困难。因为我们一直在努力构建一个能够以不错准确率对五类花卉数据集进行分类的深度神经网络。我们希望能达到90%或95%的准确率,但尚未实现,因为我们仍在尝试较简单的模型。
如果你感到困难,那么在本节课结束时,你会感到非常满意。因为今天我们将构建一个在实际数据上表现非常好的分类模型,度过一段愉快的时光。让我们开始今天的课程。
和本系列课程一样,在开始之前,让我们提醒自己开始这个计算机视觉系列的原因:是为了从零学习CV技术及其基本原理,以便在课程结束后能为我们所用。如果你有开始学习的理由,就应该坚持完成。这是我们共同许下的承诺。理想情况下,如果你和朋友或熟人一起学习本课程,请互相督促,确保你们能一节课一节课地完成。
如果你想通过公开评论来督促自己,也请随意。无论哪种方式,只要对你有用,都可以。我会在本系列每节课开始时重复这一点,因为我真的非常希望你能从头到尾跟随这个系列,从而从中获得最大收益。
言归正传,让我们进入今天的课程,并简要说明在本节课结束时你将学到什么。
你将学习预训练嵌入。预训练嵌入指的是,如果有一个深度神经网络已经在其他数据上训练过(不是你当前试图训练神经网络的数据,而是其他互联网数据),你能否以某种方式获取那些嵌入(即权重、偏置和参数),并将其转移到你当前的神经网络中,以增强网络在你感兴趣的数据上进行分类的能力。预训练嵌入可以被导入并用于你当前的模型,这通常也被称为迁移学习。这是一个非常著名的术语。
人们正在各种数据集上训练神经网络。因此,如果你仔细想想,实际上并不需要自己从头开始训练。例如,你可以对大型语言模型进行微调,完全没有必要花费数百万美元从头训练一个完整的GPT或任何其他语言模型。你可以利用人们已经在大量互联网数据上训练好的LLM,并在此基础上进行微调。
在今天的课程中,我们将从图像分类神经网络(特别是深度神经网络)的角度来学习这些概念。但其中许多思想也与大型语言模型的微调方式有相似之处。所以,如果你熟悉大型语言模型领域,这些术语可能对你来说并不陌生。
无论如何,让我们假设自己是完全的初学者,开始本课的学习。
为什么学习迁移学习?🤔
在本课程中,我们学习迁移学习的原因是,在未来的计算机视觉课程中,我们几乎总是会使用迁移学习。
为了让你跟上进度,即使你没有看过之前的课程,我也简要回顾一下本系列到目前为止我们一直在尝试做的事情。
我们有一个名为“五类花卉”的数据集,包含雏菊、蒲公英、玫瑰、向日葵和郁金香五类花卉。我们想要构建一个图像分类器,一个能对这些花卉进行分类的五分类器。每个类别大约有几千张图片,具体数字需要核实,但数据量并不大,可能比十万张花卉数据小两个数量级。这是一个相对较小的数据集,这使得从头开始训练神经网络并获得很高的准确率变得非常困难。
如果一个神经网络进行随机分类,就像一只猴子随机挑选一个类别作为预测的花卉类别,那么它的准确率将是20%,因为这是随机分类。同样,一个非常糟糕的神经网络在这种情况下最低也能获得20%的准确率。
以此为基准,我们尝试了三件事。首先,我们尝试了线性模型。在这个线性模型中,没有激活函数。这意味着我们最后使用了一个softmax层来获得概率分布,但在主要架构中没有激活函数。我们所做的是将图像转换为RGB像素,并展平成一个单层,然后通过全连接层连接到最终的输出层。这是线性模型,其准确率在训练集上约为40-45%,在测试/验证集上可能更低,约为30-35%,损失值在10到20之间。
然后,我们增加了一个隐藏层。这个隐藏层有128个节点,并使用了修正线性单元激活函数。不幸的是,尽管我们期望获得更好的准确率,但并未实现。准确率仍然在40-45%左右,具体数字显然取决于你的特定超参数或随机种子等。但一个令人惊讶的现象是,损失值从10-20下降到了1-2。这很有趣,于是我们产生了一个疑问:如果损失值下降了一个数量级,为什么准确率没有相应变化?这是因为模型正确分类的图像数量并没有增加,在验证集或训练集上,它仍然只正确分类了40-45%的图像。损失值下降的原因是每个预测变得更加自信了。例如,第二个模型不再说“这是75%的雏菊”,而是开始说“这是95%的雏菊”。这种置信度的提高降低了交叉熵损失。这就是损失值大幅下降但准确率基本保持不变背后的逻辑。
接着我们想,只有128个节点和ReLU激活函数可能还不够。于是我们引入了一些正则化技术,以通过消除可能的过拟合来提高模型性能。我们从图表中注意到存在一些过拟合现象。例如,在一张图表中,训练准确率在5个epoch后持续上升,但验证准确率在3个epoch后开始下降。这是模型出现过拟合的迹象。因此,我们决定进行正则化。我们使用了L2正则化、批归一化(使数据集均值接近0,标准差为1)、引入了Dropout(以预设的概率随机关闭不同层中的某些节点),并且还引入了早停法,这样模型就不会运行过多的epoch。一旦验证准确率开始下降,它会再观察几次迭代,如果验证准确率没有改善,模型就会停止训练。通过这些方法,我们期望获得很好的准确率,但同样没有实现。准确率仅从40-45%提高到了50-60%。训练准确率最高达到60%,验证准确率在45-55%左右。这并非巨大的改进,只是略有提升,但它给了我们希望,让我们觉得如果尝试更复杂的模型或其他方法,我们的从零开始方法或许能行得通。

然而,本节课我们将采用一种不同的、更强大的方法:迁移学习。我们将看到,利用他人已经训练好的模型知识,可以极大地提升我们在这个小型花卉数据集上的性能。
迁移学习核心概念 🧠
迁移学习的核心思想是知识迁移。一个在大型、多样化数据集(如ImageNet)上预训练好的神经网络,已经学习到了丰富的通用视觉特征(如边缘、纹理、形状)。我们可以利用这些学到的特征作为起点,而不是从随机权重开始训练我们的模型。
这通常通过两种主要方式实现:
-
使用预训练模型作为特征提取器:我们移除预训练模型的最后几层(通常是负责特定分类的全连接层),然后将其余部分(称为“骨干网络”或“编码器”)的输出作为我们新任务的输入特征。接着,我们可以在这些特征之上训练一个全新的分类器(例如,几个全连接层)。在此过程中,预训练骨干网络的权重被冻结(即不更新),我们只训练新添加的分类器层。
# 伪代码示例:特征提取模式 base_model = PretrainedCNN(weights='imagenet') base_model.trainable = False # 冻结预训练层 features = base_model.output # 添加新的分类层 x = layers.GlobalAveragePooling2D()(features) x = layers.Dense(256, activation='relu')(x) predictions = layers.Dense(5, activation='softmax')(x) # 5类花卉 model = Model(inputs=base_model.input, outputs=predictions) # 只编译和训练新添加的层 model.compile(...) model.fit(...) -
微调:在特征提取器方法的基础上更进一步。我们不仅训练新添加的分类层,还会解冻预训练模型的一部分或全部层,并以较小的学习率对它们进行更新。这允许模型根据我们的特定数据集对其学到的通用特征进行细微调整。
# 伪代码示例:微调模式 # 首先,在冻结基网络的情况下训练新层几轮 # ...(同上特征提取模式训练) # 然后,解冻部分层进行微调 base_model.trainable = True # 通常只微调后面的层,前面的层保持冻结 for layer in base_model.layers[:100]: layer.trainable = False # 使用更小的学习率重新编译模型 model.compile(optimizer=optimizers.Adam(learning_rate=1e-5), ...) model.fit(...)
迁移学习步骤 📝
以下是使用迁移学习解决我们花卉分类问题的一般步骤:
- 选择预训练模型:选择一个在大型图像数据集(如ImageNet)上预训练好的成熟卷积神经网络架构,例如VGG16、ResNet、EfficientNet等。
- 准备数据:确保我们的花卉图像数据被预处理成与预训练模型训练时相同的格式(例如,相同的图像尺寸、像素值归一化范围)。
- 构建迁移学习模型:
- 加载预训练模型,不包括其顶部的全连接分类层。
- 添加新的全局池化层(如GlobalAveragePooling2D)来减少参数。
- 添加新的全连接层,其输出节点数等于我们的花卉类别数(5个),并使用softmax激活函数。
- 训练策略:
- 第一阶段(特征提取):冻结预训练基网络的所有层,只训练新添加的顶层。使用相对较高的学习率。
- 第二阶段(可选,微调):解冻基网络的部分或全部层(通常从后面的层开始),以非常低的学习率继续训练整个模型。这有助于模型更好地适应我们的特定数据。
- 评估与预测:在独立的测试集上评估最终模型的性能,并使用它进行预测。
预期优势 ✨
通过应用迁移学习,我们期望在五类花卉数据集上实现以下突破:
- 准确率大幅提升:有望达到90%甚至更高的分类准确率,远高于我们之前从零开始训练的模型。
- 训练效率高:由于利用了预训练的特征,模型收敛速度更快,所需的训练时间和数据量更少。
- 避免过拟合:预训练模型提供的强大特征表示有助于防止在小数据集上容易发生的过拟合。
总结 🎯
本节课中,我们一起学习了迁移学习这一强大的深度学习技术。我们了解到,迁移学习的核心在于重用在大规模数据集上预训练好的模型所学习到的通用特征,并将其应用于我们自己的、通常规模较小的特定任务中。
我们探讨了两种主要方法:将预训练模型作为固定的特征提取器,以及对其进行微调以更好地适应新数据。通过这种方式,我们可以克服从头训练模型需要大量数据和计算资源的限制,快速构建出高性能的模型。

在接下来的实践中,你将亲身体验迁移学习如何轻松地将我们花卉分类模型的性能提升到一个新的高度。这标志着我们从构建基础模型迈向了应用先进、实用的解决方案。
003:防止过拟合 - 正则化、Dropout、早停、批归一化


在本节课中,我们将学习四种有助于最小化神经网络过拟合的技术:正则化、Dropout、早停和批归一化。我们将从基础概念讲起,并在课程最后将这些技术整合起来,应用于我们的五类花卉分类模型,以观察性能的提升。
课程回顾与问题引入
在开始新内容之前,我们先简要回顾前两讲的内容,以便建立上下文。
在第三讲中,我们为五类花卉数据集构建了一个线性模型。该模型的架构非常简单:将RGB图像展平为一维向量,然后通过全连接层直接输出到最终的Softmax层。这个模型的分类准确率很低,大约只有45%-50%。
在第四讲中,我们尝试增加模型的复杂度,添加了一个包含128个节点的隐藏层,并使用了ReLU激活函数。虽然模型的损失值从10-20下降到了1-2,但分类准确率并没有显著提升。模型只是对相同的预测变得更加“自信”,而并未学到更有效的特征。
紧接着,在上一讲中,我们进行了超参数调优,尝试了不同的学习率、训练轮数、批次大小和图像尺寸。然而,即使使用了像Weights & Biases这样的专业工具进行系统化实验,我们仍未获得显著改进。更重要的是,我们观察到了一个典型的过拟合现象:训练准确率持续上升,而验证准确率却达到平台期后开始下降。
这表明模型开始“记忆”训练数据中的噪声,导致其在未见过的验证数据上表现变差。本节课的核心目标,就是学习如何应对和缓解这种过拟合问题。
为何需要深度神经网络?🧠
在深入具体技术之前,我们首先探讨一下为何要转向更深的神经网络。
我们的课程目标是最终掌握卷积神经网络和迁移学习,这些都是深度神经网络。增加网络深度(即添加更多隐藏层)主要有两个原因:
- 参数效率:深度神经网络是高效的通用函数逼近器。图像数据本质上是像素值的函数,深度网络能以更少的参数来拟合复杂的函数模式,比传统的近似方法(如傅里叶级数)更高效。
- 特征学习能力:与我们在第三、四讲中探索的浅层网络不同,深度网络(特别是卷积神经网络)能够学习到图像的层次化特征。浅层网络可能只擅长检测边缘等基础形状,而深层网络可以进一步组合这些边缘,检测出纹理、部件乃至整个物体。

上图直观展示了从边缘检测到纹理/物体检测的层次化过程,这正是深度网络的优势所在。
核心防过拟合技术 🔧
接下来,我们将逐一探讨四种防止过拟合的核心技术。
1. 正则化
正则化是一种通过修改损失函数来约束模型权重、防止其变得过大的技术。其核心思想是鼓励模型学习更简单、更平滑的函数,从而提升泛化能力。
最常用的L2正则化(也称为权重衰减)将权重的平方和添加到原始损失函数中。新的损失函数公式如下:
L_new = L_original + λ * Σ(w_i²)
其中,L_original是原始损失(如交叉熵),λ是正则化强度超参数,Σ(w_i²)是所有模型权重的平方和。
添加这项后,优化算法在降低原始损失的同时,也会倾向于让权重值变小。大的权重通常意味着模型对输入数据的微小变化反应剧烈,这往往是过拟合的特征。通过惩罚大权重,模型会变得更加稳健。
2. Dropout 🎲
Dropout是另一种非常有效的正则化技术,它在训练过程中随机“丢弃”一部分神经元。
具体操作是:在每次训练迭代(前向传播和反向传播)中,以预先设定的概率 p(例如0.5)随机将网络中某些隐藏层神经元的输出设置为0。这些被“关闭”的神经元在此次迭代中不参与计算。
# 在PyTorch中,添加Dropout层非常简单
self.dropout = nn.Dropout(p=0.5)
Dropout迫使网络不能过度依赖任何少数神经元,因为它们在每次迭代中都有可能被随机屏蔽。这相当于在每次迭代中训练一个不同的、更“薄”的网络子集。最终效果是让网络学习到更加鲁棒的特征,因为特征信息必须分散在许多不同的神经元上,从而减轻了过拟合。
3. 早停 ⏹️
早停是一种简单直观的策略。其做法是持续监控验证集上的性能(如准确率或损失)。
当验证性能在连续若干轮训练后不再提升,甚至开始下降时(而此时训练性能可能仍在上升),我们就提前终止训练过程。
这种方法直接阻止了模型在训练集上继续“记忆”噪声。我们需要一个“耐心”参数,例如patience=5,意味着只有当验证损失连续5轮没有改善时,才触发早停。
4. 批归一化
批归一化虽然最初是为了解决训练时内部协变量偏移、加速训练而提出的,但它也具有良好的正则化效果。
BN层通常添加在激活函数之前。它对每一批输入数据进行归一化处理,使其均值为0,方差为1。
BN(x) = γ * [(x - μ) / √(σ² + ε)] + β
其中,μ和σ²是当前批次的均值和方差,γ和β是可学习的缩放和偏移参数,ε是一个极小值用于数值稳定。
通过保持每一层输入的稳定分布,BN使得网络对参数初始化和学习率不那么敏感,训练更稳定。同时,由于它在训练时使用批次的统计量(均值和方差),而在测试时使用整个训练集估算的移动平均值,这种噪声引入了一种轻微的正则化效果,有助于防止过拟合。
总结与展望 📚
本节课我们一起学习了四种防止神经网络过拟合的关键技术:
- 正则化:通过惩罚大的权重,促使模型学习更简单的模式。
- Dropout:在训练中随机禁用神经元,增强网络的鲁棒性。
- 早停:根据验证集性能提前结束训练,避免过度拟合训练数据。
- 批归一化:稳定层间输入分布,加速训练并带来正则化益处。

在接下来的实践中,我们将把这些技术应用到我们的花卉分类模型上。通过组合使用这些方法,我们期望能够显著抑制过拟合现象,从而在验证集上获得更高的分类准确率,让模型获得真正的泛化能力。
004:为你的ML实验使用W&B
在本节课中,我们将学习一个名为Weights & Biases的平台。在后续课程中,我们将通过改变超参数进行大量机器学习模型实验。手动记录和管理这些实验既耗时又容易出错。本节课将教你如何像专业机器学习工程师一样,系统化、高效地进行实验。
课程回顾
上一节我们介绍了如何构建一个简单的神经网络来对五种花卉进行分类。我们构建了两个模型:一个线性模型和一个带有一个隐藏层的神经网络。我们发现,虽然神经网络的损失比线性模型低了一个数量级,但验证准确率提升有限,并且出现了过拟合的迹象。
我们讨论了几个可以调整的超参数,例如:
- 学习率
- 训练轮数
- 批量大小
- 隐藏层节点数
- 图像尺寸
在之前的实验中,我们通过手动复制代码、修改参数、并行运行多个Colab实例并手动记录结果的方式来尝试不同的超参数组合。这种方法效率低下、容易出错且难以扩展。

引入Weights & Biases

本节中,我们来看看如何利用Weights & Biases平台来优化我们的实验流程。Weights & Biases是一个用于跟踪、可视化和比较机器学习实验的平台。它可以帮助我们系统化地管理超参数、记录指标并比较不同实验的结果。



使用W&B的主要优势包括:
- 自动化记录:自动记录超参数、损失、准确率等指标。
- 可视化比较:在统一的仪表板中可视化比较不同实验的结果。
- 实验管理:轻松管理、组织和复现大量实验。
- 协作共享:方便地与团队成员分享实验过程和结果。
核心概念与代码集成


要使用Weights & Biases,首先需要安装其Python库并进行初始化。
# 安装 wandb 库
!pip install wandb -q


# 导入必要的库
import wandb
from wandb.keras import WandbCallback

# 初始化一个W&B运行实例
# 这将创建一个项目来跟踪你的实验
wandb.init(project="flower-classification-p4", entity="your-username")


初始化后,你可以通过一个配置字典来定义本次实验的超参数。

# 定义超参数配置
config = wandb.config
config.learning_rate = 0.001
config.epochs = 10
config.batch_size = 32
config.hidden_layer_size = 128
config.image_size = 224
在模型训练过程中,只需添加WandbCallback回调函数,W&B就会自动记录每一轮的指标。




# 在 model.fit 中添加 WandbCallback
history = model.fit(
train_dataset,
validation_data=val_dataset,
epochs=config.epochs,
callbacks=[WandbCallback()] # 添加此行以自动记录
)



训练结束后,你可以在Weights & Biases的网页仪表板中查看所有记录的指标图表,并轻松比较不同配置实验的结果。
系统化实验流程


以下是使用W&B进行超参数调优的系统化步骤:

- 定义搜索空间:确定你想要调整的超参数及其可能的值范围。
- 编写可配置的训练脚本:确保你的训练代码能够从
wandb.config中读取超参数。 - 使用W&B Sweep:利用W&B的Sweep功能自动或半自动地运行多组超参数实验。
- 分析与比较:在W&B仪表板中分析结果,找出表现最佳的超参数组合。


通过这种方式,你可以轻松运行数十甚至数百个实验,而无需手动管理每个实验的代码和结果。

总结

本节课中我们一起学习了如何利用Weights & Biases平台来专业地进行机器学习实验。我们了解了手动管理实验的弊端,并掌握了使用W&B来自动记录超参数、跟踪训练指标以及可视化比较不同实验结果的方法。通过集成简单的代码,我们能够将繁琐的实验管理过程系统化,从而更专注于模型和算法的改进。在接下来的课程中,我们将运用这个强大的工具来更高效地探索正则化、Dropout等改进我们花卉分类模型的方法。
005:用于图像分类的神经网络


在本节课中,我们将构建一个简单的深度神经网络。与上一节使用的线性模型不同,这次我们将引入激活函数。我们将观察这个简单的深度神经网络是否能在五类花卉数据集上实现分类。
欢迎来到本节课。在开始之前,让我们回顾一下本课程的目标:从最基础开始,共同掌握计算机视觉。我们目前仍处于课程的早期阶段。开始容易,坚持完成不易。让我们共同承诺完成这个学习过程。
回顾上一节内容
上一节中,我们尝试了一个单隐藏层网络。严格来说,它甚至不能称为神经网络,因为我们只是将输入图像展平。我们有一个包含五类花卉(蒲公英、玫瑰、向日葵、郁金香等)的数据集。我们将RGB彩色图像转换为像素表示,然后将其展平为一个列向量作为输入,试图预测图像属于哪一类花卉。
在上一节结束时,我们讨论了希望构建更深的神经网络。这就是我们接下来的步骤。今天,我们将构建一个稍深的神经网络,即包含两个隐藏层。虽然真正的深度神经网络有更多隐藏层,但两个隐藏层是一个很好的起点。更重要的是,与之前不同,这次我们将使用激活函数。
上一节实验结果
我们尝试了对训练数据和验证数据进行五分类。从图中可以看到,训练数据的分类准确率大约在0.5到0.6之间。这个结果并非100%可重复,但根据多次实验,训练准确率通常在0.45到0.6的范围内,而验证准确率则低于这个值。
你还可以观察到,验证准确率的曲线波动很大。这是因为我们以批次(batch)为单位训练模型,每次迭代后更新参数。这种波动表明模型参数在训练或验证过程中没有得到很好的优化。
本节目标与改进思路
今天我们要探讨的问题是:能否通过某些方法提高这个准确率?使用更深的神经网络可能是一个答案。因此,今天我们将使用一个包含两个隐藏层的深度神经网络,而不是线性模型。
上一节使用的线性模型存在一个问题:它在输入层之后直接连接了输出层(尽管使用了Softmax将输出转换为概率),但在分类过程中并未使用激活函数来引入非线性。我们得到的准确率曲线显示,训练准确率在0.4到0.6之间,验证准确率更低且波动大。
这种波动可能与我们使用的批次大小(16)和优化器(Adam,默认学习率0.001)有关。找到最佳学习率、迭代次数和批次大小的过程称为超参数调优。
从结果中可以明显观察到模型开始过拟合。在第三个或第四个迭代周期(epoch)结束时,验证准确率开始下降,而训练准确率仍在上升。这意味着模型正在学习训练数据中更细微的模式(可能是噪声),而不是学习花卉的通用模式。当验证准确率开始下降时,就表明模型开始过拟合。因此,我们不应运行过多的迭代周期。同时,注意损失值(loss)的量级大约在10到20之间。
现在的问题是:我们能否定义一个稍复杂的、具有两个隐藏层的神经网络来获得更好的结果?例如,将准确率从50%提高到70%?
新的网络架构
让我们讨论新的隐藏层结构。
- 第一个隐藏层是展平层(Flatten)。这与之前相同。
- 第二个隐藏层是一个包含特定数量节点的层。在我们的例子中,第二个隐藏层将使用128个节点。
展平层中的节点数量等于图像中RGB像素的总数。如果我们使用224x224的RGB图像,那么总像素数为 224 * 224 * 3。第一个隐藏层(展平层)就有这么多节点。
然后,从隐藏层1到隐藏层2是全连接(Fully Connected)的,从隐藏层2到输出层也是全连接的。最后,输出通过Softmax函数转换为概率分布,得到每类花卉的概率。
回顾上一节使用Keras构建的代码,我们使用 tf.keras.Sequential 顺序模型,其中定义了初始的展平操作 tf.keras.layers.Flatten,输入形状为 (224, 224, 3)。下一层就是带有Softmax激活函数的最终输出层。严格来说,这个Softmax并没有为模型的预测能力做出贡献,它只是将最终输出转换为概率分布。
本节引入的改进
今天,我们将引入一个额外的隐藏层,即这个128个节点的隐藏层。
以下是更新后的代码结构示意:
model = tf.keras.Sequential([
tf.keras.layers.Flatten(input_shape=(224, 224, 3)), # 第一隐藏层:展平层
tf.keras.layers.Dense(128, activation='relu'), # 第二隐藏层:128个节点,使用ReLU激活函数
tf.keras.layers.Dense(5, activation='softmax') # 输出层:5个节点,使用Softmax激活函数
])
现在,模型的层数增加了。第一层是展平层,作为输入。


总结

本节课中,我们一起回顾了上一节线性模型的局限性,并提出了构建更深层网络的改进思路。我们计划构建一个包含两个隐藏层的神经网络,并在其中引入激活函数(如ReLU),以期提升模型在五类花卉图像分类任务上的性能。下一节我们将具体实现这个网络并观察其结果。
006:一个用于计算机视觉的简单神经网络


欢迎回到“计算机视觉从零开始”课程。在本节课中,我们将构建我们的第一个用于图像分类的神经网络。这不会是一个非凡的神经网络,而是你能想象到的最简单的神经网络。它具有三个特性:它是浅层的,意味着它不会有很深的隐藏层;它没有任何激活函数,因此是一个线性神经网络;最后,这个神经网络没有任何卷积操作。这是一个非常直接、简单的神经网络。我们将在本节课中探索的问题是:这个神经网络能否进行像样的图像分类,还是会表现得异常糟糕?让我们通过本节课来寻找答案。本节课将为你解答许多问题,帮助你理解神经网络的工作原理、如何进行图像分类、神经网络深度的影响,以及为什么我们可能需要卷积操作等。因此,本节课是一个知识包,让你从非常基础的角度深入理解图像分类如何与一个非常简单的神经网络协同工作。
在进入课程内容之前,我想花一分钟时间提一下我们在整个课程中一直在强调的一点:许多学生开始了课程但没有完成。可以说,大约95%的学生属于这一类,有大量的人开始了一门课程,但如果课程有30到35节课,他们会逐渐失去动力,可能只有5%或更少的学生真正完成了课程的最后部分。所以,如果你已经开始这门课程并坚持到了现在,这意味着你开始学习是有原因的,因为你认真对待学习计算机视觉这件事。请记住,你在这里有一个使命,那就是完成这门课程,深入学习计算机视觉背后的概念,并帮助它推动你的职业发展或实现你的其他目标。让我们立下一个小小的誓言,我们将完成我们已经开始的这门课程。为了让你保持责任感,请随时在本节课后留言说“第三讲已完成”,或者找一个能让你保持责任感的朋友,互相监督,这样你就能一节课接一节课地保持强大的学习动力。好了,让我们开始今天的学习。
正如前面提到的,我们将构建一个浅层神经网络。浅层神经网络意味着什么,你很快就会看到。这个网络没有激活函数,这对神经网络能做什么或不能做什么有着深远的影响。在本节课结束时,你将相当清楚地了解神经网络架构、展平(Flattening)、Softmax等操作,以及全连接层、激活函数背后的思想、深度神经网络的概念,还有著名的卷积操作(用于卷积神经网络)的必要性。我们将在今天的课程中探索所有这些概念,并亲手构建神经网络。
首先,我们来讨论深度神经网络与浅层神经网络的区别。在浅层神经网络中,只有一个隐藏层,即输入层转换到一个隐藏层,然后转换到输出层。在深度神经网络中,会有多个隐藏层,这些隐藏层帮助我们将输入向量/特征转换成不同的变换特征,最后再转换成某种输出预测。这是浅层和深度神经网络之间的根本区别。正如你所知,大多数现代计算机视觉方法依赖于深度神经网络,而不是浅层神经网络。但在我们着手构建深度神经网络之前,我们将从浅层神经网络开始,以确保我们的基础非常扎实。
接下来,让我们首先讨论我们将使用什么数据集。这个数据集叫做“5种花卉”数据集,包含五个类别的花卉:雏菊、蒲公英、玫瑰、向日葵和郁金香。在这个数据集中,每个花卉类别有1000张图像,这些图像是JPEG格式。因此,总共有5000张图像。这五个类别中的每个类别都有1000张图像。顺便说一下,这是一个著名的数据集,公开可用,你可以在Kaggle上找到它,也可以从这个GitHub仓库找到。这个数据也是著名书籍《实用计算机视觉机器学习》(O‘Reilly出版)中使用的基础数据。正如我在本课程第一讲中提到的,我们也将遵循这本书的结构。所有这些资源,包括GitHub仓库、数据等,都是公开可用的。我将与你分享我编写的代码链接、GitHub仓库以及在哪里可以找到图像数据等所有内容。
现在,第一步是,我们有一个数据集。它可能在互联网上,可能在我们本地计算机上,也可能在我们的个人Google Drive上。但我们必须读取数据集,对吧?所以第一个任务是加载文件。文件都是JPEG格式。但我们不会直接在我们构建的机器学习模型(即神经网络模型)中使用JPEG文件。相反,我们将首先将这种JPEG格式转换为RGB值。正如你所知,任何普通图像都存在红、绿、蓝通道。任何图像都可以看作是三个矩阵的集合。假设它是一个32x32的图像,那么其中一个矩阵将只是每个像素在红色通道上的强度,另一个矩阵对应绿色,第三个矩阵对应蓝色。如果你将这三个矩阵组合成一个图像,那就是你看到的图像。但我们必须首先将JPEG转换为相应的RGB矩阵值,这样我们就有了数字,图像就用数字来表示了。这些数字是像素值,通常在0到255之间。0表示黑色,255表示明亮。如果我们谈论红色通道的0到255,255表示完全是红色,0表示没有颜色,完全是黑色。我们要做的是将这些值缩小,从0到255缩放到0到1。然后,我们还要做一件事,就是将所有的图像调整到这个维度:224x224x3。224x224是图像的分辨率,x3是因为你有三个通道:红、绿、蓝。我们为什么要执行这个调整大小的操作?因为任何机器学习模型的输入数据集,例如,如果你试图构建一个基于神经网络的模型,输入数据集(即图像)可能包含各种尺寸的图像。可能拍摄一张图像的摄影师和拍摄另一张图像的摄影师使用了不同的相机设置或不同的相机,很多因素都可能改变。但问题是,如果这些图像尺寸不同,这些图像中存在的像素数量就会不同,这意味着如果你将这些图像输入到神经网络中,输入维度将不同,这是不好的。当你向神经网络输入数据时,你希望所有数据点的输入维度完全相同。因此,首先你将调整图像大小,使得我们在这个例子中用于训练或验证的所有图像都具有224x224x3的维度。
还有一件事,一旦我们加载了这些图像,或者读取了图像,我们希望确保图像实际上处于良好状态,意思是它们没有意外地被翻转或上下颠倒等。我们显然不能手动检查所有图像,但验证你读取的数据集不是完全随机的或完全不同的数据集是非常关键的。你必须进行一些完整性检查,这可以通过简单地可视化一些图像来完成。你可以可视化一些花卉图像,以确认你确实拥有花卉数据,并且数据集没有问题,比如没有损坏的文件等。你不需要对每个文件都进行检查,但至少要对少数图像进行检查。我们也将这样做。
在进入编码部分之前,我们今天将完全在Google Colab中进行编码,这样你就不需要预先安装任何东西,操作起来非常容易,你不需要单独安装Python,一切都在运行。


007:滤波器与卷积入门

在本节课中,我们将学习计算机视觉中的传统方法,重点是理解滤波器以及卷积运算。这些概念不仅是传统计算机视觉的核心,也是现代深度学习(特别是卷积神经网络)的基础。通过掌握这些基础知识,你将能更好地理解图像特征是如何被提取的。
上一节我们介绍了课程的整体框架,本节中我们来看看计算机视觉中一个非常核心的操作:卷积。
卷积运算与滤波器简介
卷积运算和滤波器是传统计算机视觉方法的心脏。在2012年深度学习兴起之前,这些手工设计的滤波器是处理图像的主要工具。即使在现代深度学习中,卷积操作依然是卷积神经网络背后的核心思想。
滤波器本质上是一些简单的矩阵,例如 3x3 或 5x5 的矩阵。这些矩阵是手工设计的,其背后有特定的逻辑或数学构造,用于识别图像中的特定特征,如边缘、角点、垂直边缘或水平边缘。
以下是卷积运算的一个直观示例:

上图展示了一个卷积核(即滤波器)在图像上滑动的过程。当滤波器的形状与图像中某个区域(例如眼睛)匹配时,输出值会很高(例如100);当不匹配时,输出值则很低(例如0)。右侧的5x5矩阵就是卷积的输出结果,其中高亮部分对应图像中眼睛的位置。
为何需要学习传统滤波器
你可能会问,既然我们已经进入深度学习时代,为何还要关心这些手工设计的滤波器?原因在于,尽管现代神经网络通过反向传播自动学习滤波器参数,但理解传统滤波器的工作原理能为你提供坚实的基础。
- 建立直觉:理解滤波器如何捕捉特征,能让你在后续学习卷积神经网络时,对各层的作用有更直观的认识。
- 预处理应用:在计算资源有限的情况下,使用滤波器对图像进行预处理(如降噪、边缘增强)是提高效率的好方法。
- 历史与基础:这些手工滤波器是现代学习型滤波器的前身。了解Sobel、Laplacian等经典滤波器,能让你对整个领域有更深的领悟。
图像滤波器与卷积操作详解
如前所述,滤波器(常称为卷积核)是一个矩阵。图像本身也是一个矩阵(像素值)。卷积操作就是让这个滤波器矩阵在图像矩阵上滑动,并在每个位置进行特定的计算。
卷积操作的基本公式可以简化为:在图像的每个局部区域,将滤波器矩阵与对应的图像像素值逐元素相乘,然后将所有乘积结果求和,得到一个输出值。这个过程在图像的所有位置上重复进行。

上图示意了卷积核在图像上移动并计算的过程。通过这种操作,我们可以提取出图像的各种特征。

本节课中我们一起学习了滤波器与卷积的基本概念。我们了解了滤波器是什么,卷积运算如何工作,以及为什么这些传统知识对理解现代计算机视觉仍然至关重要。在接下来的课程中,我们将深入探讨几种具体的经典滤波器。
008:计算机视觉导论

在本节课中,我们将正式学习什么是计算机视觉,了解其历史演变,以及在应用机器学习和深度学习技术之前,人们尝试过哪些方法。
课程概述与承诺
在开始之前,我想展示一张图表。这张图表来自我们的一个播放列表,显示了观看次数随课程编号的变化。

可以看到,前几节课的观看量很高,但超过90%的观众在后续课程中流失了。这清楚地表明,开始一件事很容易,但坚持完成却很难。我真诚地希望,如果你正在观看这个视频并开始这个系列课程,你能成为坚持完成的那一类人。在开始之前,让我们共同承诺,一起完成计算机视觉的端到端学习。这对你将是非常有益的,如果你能学会并打下坚实的基础,我也会感到非常高兴。
课程参考材料
如果你想知道本系列课程将使用什么参考资料,我主要参考的是O'Reilly出版的《Practical Machine Learning for Computer Vision》一书。
😊,这是一本非常受欢迎的书,你可以在亚马逊上找到。我购买了Kindle电子书格式,价格并不昂贵(大约2000印度卢比)。这本书写得非常好,并且有一个关联的GitHub仓库,发布在Google Cloud Platform上。仓库链接也在这个笔记中提供,它有近500个星标,这是一个非常大的成就。如果你购买这本书,我认为这是一项非常好的投资。全书大约500页,我发现书中的某些章节需要为本课程做一些调整,因此我对书的目录进行了一些修改。但本书将是你在整个课程中的主要参考资料。
计算机视觉与机器视觉的区别
让我们从一个很多人可能都有的常见疑问开始:计算机视觉和机器视觉到底有什么区别?十年前我开始从事机器视觉工作时,也有几乎完全相同的疑问。
主要区别在于,机器视觉关注的是视觉系统在制造业等行业的实际应用。我曾参与一个项目,在一个砂轮附近安装了一个小型摄像头。砂轮可用于切割金属或抛光表面,其表面嵌有许多磨粒。当砂轮非常锋利时,用光照射表面会显示出许多明亮的白点;而当锋利度下降时,这些亮点的亮度也会减弱。因此,你可以通过这个机器视觉设置(摄像头定期拍摄照片)来跟踪光学照明的减少程度,从而监测砂轮的磨损情况。这是一个纯粹的机器视觉项目,因为你将整个系统视为一个整体,可能涉及摄像头、照明、传感器或处理软件。所有这些共同构成了机器视觉系统。
在另一个项目中,我在第一节课已经展示过。

这是一篇关于使用计算机视觉对大米品种进行分类的论文,并被国际机器视觉会议接受。



在这篇论文中,我们本质上也是使用了一个基于摄像头的系统,通过图像跟踪单个米粒的分布,然后我们可以用最小二乘法椭圆拟合米粒,利用椭圆的长轴和短轴长度来预测它是哪种大米。这也是一个经典的机器视觉应用。




因此,在机器视觉中,你通常需要将硬件与软件系统结合起来,包括摄像头、照明等,所有这些都很重要。
而在计算机视觉中,主要目标是如何让机器理解或解释图像或视频(视频可以分解为帧,本质上也可以视为图像)。这意味着计算机视觉更侧重于:你已经有了来自视觉系统的输入数据,然后你开发算法、理论模型、机器学习模型或基于规则的系统,用于诸如人脸识别、自动驾驶汽车、分析身体扫描图像等应用。在计算机视觉中,你已经有了图像输入,并使用基于机器学习的系统、基于规则的系统或深度学习系统来对其进行处理,并从中得出一些结论。在我展示的这个示例图像中,就像一个神经网络在进行二元分类,判断这是一棵树还是西兰花。
这就是计算机视觉和机器视觉的根本区别。本课程不是关于机器视觉,而是关于计算机视觉,因此我们将主要关注那些允许我们处理图像并从中提取信息的技术。
计算机视觉与人类视觉的区别
这是计算机视觉和人类视觉的根本区别。在计算机视觉中,你的输入是一张图像。例如,如果是一张苹果的图像,你有一个计算机视觉模型以该图像作为输入。这个模型(可以是机器学习模型或非机器学习模型,稍后我会展示一些非机器学习模型的例子)会做出预测。
😊,显然,在人类视觉中,你看到一个苹果,你的眼睛看到了它。神经递质会产生适当的信号,你最终会在大脑中接收到这些信息,从而使你说出“这是一个苹果”。所以主要区别在于,判断这是苹果还是橙子或其他东西的处理过程,在计算机视觉系统中发生在模型里,而在你身上则发生在大脑中。
计算机视觉的技术演变
在2010年之前,计算机视觉方法主要通过启发式方法(即手动定义的规则)来实现。但如今,你更常见的是基于深度学习和卷积神经网络的计算机视觉方法。视觉Transformer、生成扩散模型等先进技术也相继出现,但这些都开始得稍晚一些。
😊,2010年之前主要是没有机器学习的计算机视觉技术。如果你看这个重叠部分,计算机视觉并不总是意味着会使用机器学习,事实远非如此。计算机视觉中有许多技术与机器学习无关,也有许多技术更侧重于深度学习而非传统机器学习。
如果你想知道机器学习和深度学习之间的区别,我将在同一节课中简要讨论。但基本上,只需理解计算机视觉并不自动意味着你在研究卷积神经网络。
然而,这也是一个问题。在2010年之前,要处理图像并从中提取信息或特征,你必须手动定义一些算法和规则。这些规则大多是通过一种称为“滤波器”的东西来实现的。
例如,请看这个滤波器的例子。

如你所见,左侧的图像有很多噪点。你可以应用一种称为拉普拉斯平滑滤波器来平滑图像。我稍后会展示这些滤波器。但滤波器的定义是非常手动的。如果你非常聪明,你可以设计出能高效完成任务的滤波器。

课程总结
在本节课中,我们一起学习了计算机视觉的基本定义,区分了计算机视觉与机器视觉、人类视觉的不同,并回顾了计算机视觉从基于规则的方法到现代机器学习与深度学习方法的技术演变历程。我们还了解了本课程将使用的主要参考书籍。下一节课,我们将开始深入探讨具体的图像处理技术和基础概念。
009:课程介绍与动机 🚀

在本节课中,我们将介绍全新的“计算机视觉从零开始”课程。我们将了解课程大纲、目标受众,并探讨如何在学习过程中保持动力,为后续深入学习打下基础。
课程大纲与目标受众
上一节我们介绍了课程启动的背景,本节中我们来看看课程的具体内容和适合的学习者。
本课程将教授计算机视觉的基础知识以及为实际生活问题实现计算机视觉模型的实践方面。我们收到了大量来自不同平台的信息,表明市场对结构良好的计算机视觉课程有强烈需求,这是创建本课程的主要动机之一。
我是 Sar Panant 博士,是 Visuwarra AI Labs 的联合创始人之一,同时也拥有麻省理工学院的博士学位。在开始攻读博士之前,我就已深入计算机视觉领域,但当时更多使用的是传统的机器视觉技术。
以下是本课程旨在帮助的人群:
- 行业专业人士:希望在公司(如制造自动驾驶汽车、自主无人机或物体检测AI摄像头的公司)中获得相关职位的人。
- 学生:希望系统学习计算机视觉知识的学习者。
- 严肃的学习者:真正对计算机视觉感兴趣并愿意完成整个课程的人。
保持学习动力
完成一个长课程充满挑战。我注意到,许多学生可能在最初几讲热情高涨,但随着课程长度增加,很难坚持到底。实际上完成整个课程的学生数量远少于开始课程的学生数量。我自己作为学生时,开始学习的课程数量也远多于实际完成的。
但如果你是一位严肃的学习者,真心想掌握计算机视觉,我希望能陪伴你完成整个学习旅程。我们将讨论如何保持动力,如何一讲一讲地坚持学习。
从传统视觉到深度学习的演进
我首次深入计算机视觉研究大约在10年前,当时是本科期间与教授合作的一个有趣项目。
项目目标是使用相机图像对水稻谷物品种进行分类,并且我们必须实现非机器学习的传统算法来完成此任务。我和合著者开发了传统滤波器,用于检测图像的各个方面,例如边界、边缘、水稻谷物的曲率等。从中我们可以提取某些特征,如谷粒长度、宽度、长宽比等,并利用这些特征进行聚类分析。
这是一次非常宝贵的经历,因为即使实现基于传统非机器学习的算法,也是一次很好的练习,它教会了我许多关于图像处理的知识。
当时(2015-2016年)的流程如下:
- 获取不同品种水稻谷物的相机图像。
- 进行阈值处理以增强背景与谷物之间的对比度。
- 找出检测谷物与其背景之间边缘或边界的方法。
- 用于计算谷物局部曲率半径。
- 定义何时两个谷物接触、何时未接触,如果接触,如何将其分割。
所有这些技术都是通过纯数学和逻辑手动开发的。
我们在法国尼斯举行的国际机器视觉会议(ICMV)上展示了这项工作。在会议上,我们意识到一个趋势:大多数前来展示研究成果的研究人员都在思考机器学习或深度学习技术。用于手动定义识别图像或视频中物体规则和条件的传统机器视觉技术正在迅速改变,机器视觉领域正在快速演进。
许多研究人员正逐渐从这些基于规则或专家知识的系统转向,他们明确地在实施深度学习技术。我们甚至被问到诸如“你是否尝试过使用深度学习技术进行预测?”或“在基于大小进行聚类时,能否使用K均值聚类?”等问题。我意识到,从那时起,如果我想继续从事机器视觉工作,就必须将重点转向基于机器学习的方法,而不是传统的基于规则的方法。
规则方法与深度学习方法对比
在基于规则的方法中,你需要手动定义算法来分类图像或从中提取信息。典型步骤包括:
- 将图像转换为灰度图。
- 执行一些增强技术,如Otsu阈值处理。
- 进行噪声过滤。
- 提取边界信息。
- 处理物体接触或破损等复杂情况。
- 拟合最小二乘椭圆以近似谷物尺寸。
- 计算几何参数(如长度、宽度)。
- 使用这些参数进行聚类分析。
然而,自从许多深度学习方法发展以来,这已经变成一个相对简单的问题。如果有大量图像,我们可以轻松训练基于卷积神经网络的方法。这个神经网络可以执行多类分类,并轻松预测它正在查看的图像类型。在过去的10到12年里,我亲眼目睹了这一转变。
深度学习在复杂问题中的应用
现在,每当我从事机器视觉或计算机视觉相关项目时,几乎总是使用深度神经网络。例如:
- 用于目标检测的技术,如 YOLO。
- 在麻省理工学院攻读博士期间,我曾致力于生物系统,任务是构建深度卷积神经网络,根据细胞形态预测信使RNA(mRNA)处理效果。这是一个非常困难的问题,因为形态不仅取决于mRNA处理,还严重依赖于温度、湿度等外部条件,信噪比非常高。构建一个优秀的深度卷积神经网络架构来解决这个问题极具挑战性,但从计算机视觉的实践角度来看,这段经历也教会了我很多东西。
现实世界的例子:特斯拉自动驾驶
让我们看看特斯拉的自动驾驶汽车。我最近读了Walter Isaacson所著的《埃隆·马斯克》一书,这是一本650页的大部头,我读得非常享受。但在特斯拉的背景下,我从书中得到的一点是:埃隆·马斯克曾强烈反对激光雷达技术。激光雷达是一种利用传感器数据预测汽车周围环境的技术。
总结

本节课中,我们一起学习了“计算机视觉从零开始”课程的概述。我们明确了课程目标、适合的学习者,并回顾了计算机视觉从传统规则方法向现代深度学习演进的历史。我们还探讨了如何保持学习动力,并看到了深度学习在复杂研究问题和现实世界应用(如自动驾驶)中的强大能力。在接下来的课程中,我们将从基础开始,逐步深入这些激动人心的技术。
计算机视觉从零开始:P10:VGGNet:拥有14.5万次引用的传奇论文

在本节课中,我们将学习一个非常著名的神经网络架构——VGGNet。我们将探讨它如何挑战了AlexNet的统治地位,分析其架构特点,并学习如何将其应用于我们的五类花卉数据集。
在上一讲中,我们讨论了AlexNet如何在2012年彻底主导了深度学习领域,并赢得了ImageNet竞赛。
本节中,我们将看看VGGNet如何威胁到AlexNet的统治地位。这同样是一篇传奇论文,拥有超过14.5万次引用,于2014年提出,并在当年的ImageNet挑战赛中获得亚军。
我们将具体讨论VGGNet,它与AlexNet相比有何特别之处,以及“VGG”这个术语代表什么。我们将研究其架构,并为其在我们的五类花卉数据集上实现一个模型。现在,让我们开始学习。
2014年至2016年间,深度学习领域发生了许多重大进展。正是在这一时期,VGGNet、Inception、ResNet等神经网络架构相继出现。在后续的一些课程中,我们也将花时间学习Inception和ResNet,它们同样是极其著名的深度学习架构。
然而,所有这些模型都在试图回答一个问题:如何在卷积神经网络中增加更深层的数量,以提升性能,同时不显著增加训练所需时间,也不损失性能(即验证或测试准确率)? 这是当时深度学习社区的研究者们共同探索的问题。VGGNet作为早期提出的架构之一,给出了一个非常清晰的答案,尽管它只是对深度神经网络架构进行了一个简单而巧妙的调整。这些模型中的一些,为未来更复杂的模型设定了基准。
我们将结合本课程迄今为止所学内容来审视VGGNet。在深入VGG细节之前,让我们先退一步,回顾一下本计算机视觉课程中我们已经完成的工作。
我们一直在处理五类花卉数据集,每类大约有1000张图像,因此我们面临的是一个五分类任务。
我们从一个线性模型开始。这个模型只是将图像转换成一个线性向量,然后通过一个全连接层直接输出。没有激活函数,只在最后使用Softmax函数将预测转换为概率。这个模型表现很差,训练准确率在40%到45%之间,验证准确率更低,大约在35%到40%之间,损失非常高,在10到20之间。
随后我们意识到线性模型效果不佳,因此需要引入激活函数和隐藏层。我们引入了一个具有128个节点和ReLU激活函数的隐藏层模型。这并没有显著提高准确率,准确率大致相同,但损失降低了一个数量级,表明我们的预测比之前的线性模型更有信心。这是最初的模型架构:RGB图像被展平,然后通过全连接层连接到输出层。额外的层被添加在这个展平层之后。
接着我们认识到,仅仅在中间隐藏层添加128个节点是不够的,因为模型可能过拟合了——训练准确率仍然高于验证准确率,这在我们的实验中非常明显。于是我们决定采取措施防止过拟合,包括正则化、批量归一化、Dropout和早停法。这些措施取得了一定成功,我们将验证准确率从30%-40%提高到了50%-60%。对于一个随机分类准确率为20%的五分类模型来说,这已经不错了。我们还看到,当我们进行批量归一化时,训练准确率可以达到近75%-80%,尽管验证准确率仍接近60%。与之前的基于隐藏层的模型相比,这是一个巨大的改进。
但我们意识到进展缓慢,这还不够好。因为在典型的卷积神经网络图像分类任务中,我们通常期望看到准确率达到90%甚至95%的模型。因此,我们决定进行迁移学习。我们使用了一个预训练的ResNet模型,不是从头开始训练,而是只训练模型的后面几层,其余层基本保持不变。通过这种方法,我们实际上达到了100%的训练准确率和约80%的验证准确率。虽然从趋势上看存在一些过拟合,但这已经是我们迄今为止获得的最佳结果。
正是在上一讲中,我们决定尝试AlexNet,它在2012年彻底革新了深度学习领域。这篇由Geoffrey Hinton教授和OpenAI联合创始人Ilya Sutskever等人撰写的论文,是一篇被引用超过17万次的传奇论文。我们使用AlexNet获得了约90%的验证准确率和95%的训练准确率,这是迄今为止最好的模型。
然而,2012年之后,深度学习卷积神经网络领域发生了许多变化。在本课程接下来的几讲中,我们将探索这些变化,了解出现了哪些新模型,以及它们在深度学习领域带来了哪些新思路。今天,我们就从VGG开始。
但在开始之前,我们总是说,开始任何课程系列都很容易,但坚持完成却绝非易事。我很高兴大家已经到达了课程的这一阶段,因为我确信很多从第一、二讲开始学习的人可能没有坚持下来,这是普遍现象。我重复这一点,是为了激励我们所有人朝着同一个目标前进。让我们在心里许下承诺,既然已经开始了,就要完成它。如果你有朋友或熟人也对学习计算机视觉感兴趣,请互相督促。如果没有,也可以公开留言,以此督促自己回来完成课程。
现在,让我们深入了解VGG的细节。希望在本讲结束时,我们能够理解VGGNet在2014年提出其架构背后的动机。
VGG有一个非常具体的特点:它主要使用3x3的卷积滤波器。它摒弃了使用不同形状滤波器(如5x5、11x11、7x7等)的复杂性。VGGNet专注于一件事:使用统一尺寸(3x3)的滤波器,但增加网络层数,即增加深度,同时保持架构简洁。这种方法被证明非常有效,我们稍后会讨论这一点。

我们还将加载并使用一个预训练的VGG模型(不使用Keras,而是使用PyTorch的torchvision.models)。我们会将这个预训练的VGG模型应用到我们的五类花卉数据集上,看看能得到什么样的结果。它可能比AlexNet更好,也可能不是。
最后,我们还将讨论图像的输入尺寸如何影响后续分类层的形状和大小。由于我们正在进行迁移学习,显然需要修改神经网络最后几层的结构,因为我们处理的是五分类任务,而VGG的预训练很可能不是针对五分类进行的。因此,我们需要修改最后几层。我们将学习如何操作,以及从预训练VGG部分的最后一层到我们添加的额外层,再到最终的五节点输出层,这些中间层的形状应该是怎样的。我们也会讨论相关的维度问题。
现在,让我们正式开始介绍VGG架构。如前所述,这篇论文于2014年由牛津大学的一个研究小组提出。实际上,“VGG”这个缩写与机器学习或深度学习无关,它代表的是提出这篇论文的视觉几何组。
本节课中,我们一起学习了VGGNet这一传奇的卷积神经网络架构。我们回顾了从简单线性模型到复杂网络的发展历程,理解了VGGNet通过统一使用3x3小卷积核并堆叠深层网络来提升性能的核心思想。我们还探讨了如何将预训练的VGG模型通过迁移学习应用到新的分类任务上,并了解了修改网络末端以适应特定任务输出维度的方法。VGGNet以其简洁有效的设计,为后续更复杂的网络架构奠定了重要基础。
011:GoogleNet(又名Inception V1)| 在ImageNet挑战赛中排名第一的传奇CNN模型 🏆

在本节课中,我们将学习GoogleNet,也称为Inception V1。这是一个非常著名的卷积神经网络架构,它在2014年的ImageNet挑战赛中获得了第一名,其相关论文已获得超过68,500次引用,达到了传奇地位。如果你从事计算机视觉工作,几乎可以肯定你尝试过Inception V1、V2、V3等不同模型。今天,我们将花时间理解是什么让GoogleNet如此流行和高效,以及它如何克服了AlexNet、VGG等先前架构的一些缺点。我们将通过在我们的五类花卉数据集上构建GoogleNet模型来进行实践。
背景与动机
在上一讲中,我们讨论了VGG架构,它尝试使用非常基础的3x3卷积滤波器,这与AlexNet等先前架构不同。但总体而言,计算机视觉领域的研究人员当时正试图构建越来越深的卷积神经网络。到2014年,人们清楚地认识到,深度意味着可以从复杂图像中学习更多特征。然而,问题是网络越深,训练就越困难,因为参数过多,在反向传播时更新如此多的参数非常困难。此外,当深度过大时,初始层会经历梯度消失问题,其权重和偏置的更新速度远不如后面的层。同时,计算开销也非常巨大。
VGG架构通过简化思路,在所有卷积层中使用3x3滤波器,展示了无需使用不同大小的卷积滤波器,只需选择一种尺寸并增加深度即可。这一发现非常有趣,使得VGG在2014年ImageNet挑战赛中获得第二名。
然而,VGG网络的一个问题是其总参数量高达约1.38亿,这是一个非常庞大的架构。尽管它使用了简单的3x3卷积滤波器,但总参数量依然巨大。
GoogleNet的核心理念
谷歌的研究人员提出了Inception(GoogleNet)的想法。他们提出的基本问题是:我们能否设计一个既有深度,又在训练时相对高效的架构?
他们的答案是GoogleNet。其核心思想是:不再仅仅使用3x3滤波器,而是在每一层内部使用多尺度滤波器。他们意识到,与VGG的做法不同,你实际上可以使用不同尺寸的滤波器,这实际上有助于减少参数总量,从而使整个神经网络架构不那么庞大。
课程回顾
在深入GoogleNet之前,我们先简要回顾一下本课程迄今为止的内容。
我们一直在使用五类花卉数据集(雏菊、蒲公英、玫瑰、向日葵、郁金香),每类大约有1000张图像。任务是进行五类图像分类,我们正逐步构建复杂度递增的神经网络。
- 线性模型(无激活函数):结构为:RGB图像 -> 展平层 -> 全连接输出层(5个节点)。仅在最后使用Softmax函数将输出值转换为概率分布。该模型准确率约为40-45%,损失在10-20之间。
- 添加隐藏层和激活函数:我们在展平层后添加了一个128个节点的隐藏层,并使用ReLU激活函数。令人惊讶的是,准确率并未提高(仍为40-45%),验证准确率甚至更低(35-40%)。但损失下降了一个数量级(降至1-2)。这是因为模型现在做出了更自信的预测,尽管分类正确的比例变化不大,但每个预测相关的损失减小了。
- 应对过拟合:由于训练准确率(40-45%)高于验证准确率(35-40%),我们认为是过拟合。于是我们添加了正则化、批归一化、Dropout和早停法。这使得验证准确率提升至50-60%,训练准确率甚至接近70%。虽然有所改善,但距离我们期望的95%以上的多类分类准确率仍有差距。
Inception模块详解
现在,让我们来看看GoogleNet的核心创新——Inception模块。
传统的卷积层通常只使用一种尺寸的滤波器(例如VGG只用3x3)。Inception模块的想法是,与其在深度上纠结,不如在宽度上做文章,在同一层中并行应用多种尺寸的卷积核(如1x1, 3x3, 5x5)以及池化操作,然后将所有结果在深度维度上拼接起来。这样,网络可以在同一层级捕捉不同尺度的特征。
然而,直接这样做会导致计算成本爆炸式增长。例如,将上层所有通道与多种大尺寸卷积核进行卷积,参数量会非常大。
GoogleNet的关键优化是引入了 1x1卷积。1x1卷积主要有两大作用:
- 降维:在应用3x3或5x5等大卷积核之前,先使用1x1卷积来减少输入特征的通道数,这可以显著减少计算量和参数量。
- 增加非线性:1x1卷积后通常会接一个ReLU激活函数,从而增加网络的非线性表达能力。
因此,一个典型的Inception模块结构如下:
以下是Inception模块的标准结构流程:
- 输入特征图。
- 并行进行四个分支处理:
- 分支1:1x1卷积。
- 分支2:1x1卷积 -> 3x3卷积。
- 分支3:1x1卷积 -> 5x5卷积。
- 分支4:3x3最大池化 -> 1x1卷积。
- 将四个分支输出的特征图在通道维度上进行拼接。
- 输出拼接后的特征图。
通过代码可以更直观地表示其核心结构(以PyTorch风格为例):
class InceptionModule(nn.Module):
def __init__(self, in_channels, ch1x1, ch3x3red, ch3x3, ch5x5red, ch5x5, pool_proj):
super().__init__()
# 分支1: 1x1卷积
self.branch1 = nn.Conv2d(in_channels, ch1x1, kernel_size=1)
# 分支2: 1x1卷积 -> 3x3卷积
self.branch2 = nn.Sequential(
nn.Conv2d(in_channels, ch3x3red, kernel_size=1),
nn.Conv2d(ch3x3red, ch3x3, kernel_size=3, padding=1)
)
# 分支3: 1x1卷积 -> 5x5卷积
self.branch3 = nn.Sequential(
nn.Conv2d(in_channels, ch5x5red, kernel_size=1),
nn.Conv2d(ch5x5red, ch5x5, kernel_size=5, padding=2)
)
# 分支4: 3x3最大池化 -> 1x1卷积
self.branch4 = nn.Sequential(
nn.MaxPool2d(kernel_size=3, stride=1, padding=1),
nn.Conv2d(in_channels, pool_proj, kernel_size=1)
)
def forward(self, x):
branch1_out = self.branch1(x)
branch2_out = self.branch2(x)
branch3_out = self.branch3(x)
branch4_out = self.branch4(x)
# 在通道维度上拼接所有分支的输出
outputs = [branch1_out, branch2_out, branch3_out, branch4_out]
return torch.cat(outputs, dim=1)
辅助分类器
GoogleNet的另一个重要创新是引入了辅助分类器。
由于网络很深,梯度在反向传播时可能会减弱或消失,导致深层网络难以训练。为了解决这个问题,GoogleNet在网络中间层添加了两个辅助分类器。这些辅助分类器在训练期间参与计算损失,但在推理(预测)时会被移除。
辅助分类器的作用是:
- 提供额外的梯度信号:帮助梯度更有效地传播回网络的早期层,缓解梯度消失问题。
- 起到正则化作用:相当于一种模型内部的正则化,可能有助于提升主分类器的性能。
总损失函数由三部分组成:
总损失 = 主分类器损失 + 辅助分类器1损失 * 权重 + 辅助分类器2损失 * 权重
通常权重设置为0.3。
整体网络架构
GoogleNet的整体架构由多个Inception模块堆叠而成,中间穿插着一些用于下采样的最大池化层。网络开头是普通的卷积层和池化层,结尾是全局平均池化层和全连接层。
其大致结构如下:
- 初始卷积和池化层。
- 多个Inception模块堆叠(例如Inception 3a, 3b, 4a, 4b, 4c, 4d, 4e, 5a, 5b)。
- 在中间层(4a和4d输出后)插入辅助分类器。
- 末尾使用全局平均池化替代传统的全连接层,这大大减少了参数量。
- 最后一个全连接层输出最终的分类结果。
与拥有1.38亿参数的VGG相比,GoogleNet的参数量仅为约500万,但却实现了更高的精度,这充分体现了其设计的优越性。
实践应用与总结
在本课程中,我们将像使用VGG一样,利用预训练的GoogleNet模型,并在我们的五类花卉数据集上进行迁移学习。我们将比较GoogleNet与之前模型的验证准确率和训练准确率。

本节课中,我们一起学习了传奇的GoogleNet(Inception V1)架构。我们了解到,它通过设计Inception模块(在单层内并行使用多尺度卷积与池化,并用1x1卷积降维)和引入辅助分类器,成功地构建了一个既深又宽且参数高效的网络,从而在2014年ImageNet挑战赛中夺冠。其核心在于通过精心设计的结构来提升性能,而非简单地堆叠参数。在接下来的实践中,我们将亲身体验这一强大架构的效果。
012:为何更小的CNN可以更聪明?


欢迎回到计算机视觉从零开始的新一讲。本节课我们将理解SqueezeNet,并将在我们的5类花卉数据集上实现它。
SqueezeNet是一种非常出色且流行的神经网络架构。它首次让深度学习研究者们相信,即使是参数量相对较少、体积较小的神经网络,也能达到与其他流行卷积神经网络(如AlexNet)相似的准确率。因此,SqueezeNet非常受欢迎。当我们为数据集实现SqueezeNet架构时,我希望能展示其结果,并将其与我们迄今为止尝试过的所有架构(包括ResNet、AlexNet、VGG和Inception V1)的结果进行比较。那么,欢迎来到今天的课程。
背景与动机
2012年至2016年是深度学习研究的疯狂时期。随着2012年AlexNet的引入,它彻底改变了深度学习在计算机视觉中的应用方式,证明了深度神经网络可以被有效地训练以对图像做出良好预测。随后出现了更多模型,例如2014-15年引入的VGG,以及后来更名为Inception V1的GoogleNet。
那么问题来了,既然在2015、2016年左右发生了这么多事情,并且这些神经网络在ImageNet自然图像分类挑战赛中表现出色,为什么研究者们还要引入SqueezeNet?SqueezeNet究竟是什么?我们将在今天的课程中讨论这些问题。
核心问题:模型大小
2015-2016年左右,深度学习社区面临的主要问题是,像VGG和GoogleNet这样的模型体积“庞大”——这里的“庞大”指的是参数数量和文件大小。VGG约有1.38亿个参数,GoogleNet约有680万个参数。与VGG相比,GoogleNet的体积显著减小,这本身就是一个重大进步。正如我们在上一讲中讨论的,GoogleNet赢得了2014年ImageNet挑战赛的第一名,而VGG在同一挑战赛中获得了第二名。
如果我们使用这些大型模型,将它们部署到移动电话或物联网等小型设备上会非常困难,这些设备可能使用树莓派等计算能力较低的模块。最终,如果你想要边缘计算,即将模型嵌入这些小型设备中,而不是放在云端,你就需要非常高效的小型模型。但问题是,小型模型在分类任务上通常表现不佳。这就是为什么AlexNet在2012年引入时很重要,它表明你可以拥有深度神经网络并有效训练它来做出良好预测。VGG将其提升到了另一个层次,它是一个更大的架构,表明你只需要简单的3x3卷积层,将它们串联堆叠就能得到好结果。而GoogleNet则表明,你不仅可以使用3x3卷积,还可以使用1x1卷积,然后并行使用3x3、5x5卷积等,然后将这个Inception模块串联重复,也能得到好结果。
那么,为什么人们会想到压缩卷积神经网络的大小呢?正是因为他们看到神经网络变得越来越大、越来越大。如果你真的想将其部署到设备上,就必须拥有高精度的小型模型。
SqueezeNet论文简介
这篇可以在arXiv上访问的论文标题非常直接:“SqueezeNet: AlexNet-level accuracy with 50x fewer parameters and <0.5MB model size”。如此大胆且有趣的论文标题实属罕见。
论文的第一作者是Forrest N. Iandola。这篇论文现在已有近11000到12000次引用,是一篇非常出色的论文。任何引用超过1000次的论文都可以称为杰作,而超过10000次引用的论文则是顶级论文,因此这篇论文和SqueezeNet架构都非常著名。
它的目标是(或者说它实现的是)达到AlexNet级别的准确率。尽管AlexNet早在四年前的2012年就已问世,但它仍然非常流行,因此超越AlexNet确实是一个基准。更重要的是,它实现了参数数量的大幅减少。我将在今天课程的后半部分展示一个表格,比较AlexNet、VGG、Inception V1(即GoogleNet)以及SqueezeNet的参数数量、文件大小、引入年份和构建者等信息。基本上,这个高效、低参数的模型有望实现设备端部署,让你可以在手机上运行神经网络,而不是调用某个API访问托管在云端的模型。
课程回顾
在进一步深入课程之前,我想做一个简短的回顾,以防你不记得本课程到目前为止的内容,或者你是第一次观看本课程。如果你不想看回顾,可以跳过,但这不会超过五分钟。
在本课程中,我们一直在探索这个5类花卉数据集。共有五个花卉类别:雏菊、蒲公英、玫瑰、向日葵和郁金香。我们尝试使用各种神经网络架构进行五分类。每个类别只有大约1000张图像,因此数据集总大小并不高,只有约5000张图像。这不是一个非常容易的任务,因为通常你可能需要数万或更多的图像才能进行非常高效的分类。但这很好,因为这将真正把我们的实验推向极限。
我们从一个最简单的线性模型开始。我们有RGB图像,首先将其转换为一个展平层。如果我们有三个通道上的n个像素,那么这些像素将被转换为一个n x 1的列向量。在这个展平层之后,我们有一个最终的五节点输出层,前一层的所有节点都完全连接到最终层的所有节点。除了最后一层使用softmax激活函数将最终输出数字转换为概率分布外,没有其他激活函数。但这并不影响预测方式,所以本质上这是一个没有任何激活函数的线性模型。
当我们实现这个模型时,我们得到了大约40%到45%的训练准确率,验证准确率在35%到40%之间,不是很高。但一个最差的随机分类模型只有20%的准确率(因为有五个类别),所以这个模型比随机分类器要好。损失值在10到20的量级。
然后我们决定,仅仅线性模型是不够的,让我们引入一个具有128个节点的隐藏层,并使用ReLU激活函数。这个模型实际上并没有表现得更好,训练准确率仍在44.5%左右,验证准确率在35%到40%之间。但令人惊讶的是,损失值降到了1到2,降低了一个数量级,但准确率相似。你可能会想,这怎么可能?这是可能的,因为模型仍然做出了相似数量的正确分类。因为如果真实标签是类别0,无论你做出哪种预测(例如概率分布为[0.6, 0.1, 0.1, 0.1, 0.1]或[0.4, 0.2, 0.2, 0.1, 0.1]),准确率都是一样的,因为两者都预测类别0。但后者的损失会更低,因为你做出了更自信的预测。这大致就是单隐藏层方法中发生的情况。此外,还存在一些过拟合,因为训练准确率平均比验证准确率高约10个百分点,这并不好。
于是我们想,如何防止过拟合?然后我们引入了正则化、批归一化、Dropout和早停。早停实际上并不需要,因为我们只运行了100个周期,没有观察到准确率下降,模型并没有过度训练。问题更多在于模型本身不够好,在少数情况下存在过拟合,但除此之外,问题更多与Dropout相关。

本节课总结

在本节课中,我们一起探讨了SqueezeNet的背景和动机。我们了解到,在深度学习模型日益庞大的背景下,研究者们为了实现在移动设备等资源受限环境中的高效部署,开始追求在保持高准确率的同时大幅减少模型参数和体积。SqueezeNet论文以其明确的目标——“以50倍更少的参数和小于0.5MB的模型大小达到AlexNet级别的准确率”——成为了这一领域的标志性工作。我们还简要回顾了本课程之前使用简单线性模型和单隐藏层模型在花卉分类任务上的实验历程,为后续实现和评估SqueezeNet架构做好了铺垫。下一节,我们将深入SqueezeNet的核心设计思想。
013:ResNet详解 - 梯度消失、跳跃连接与代码实现


欢迎回到计算机视觉从零开始的新一讲。本节课我们将实现ResNet,这是最流行的卷积神经网络架构之一。我们将讨论梯度消失问题,这是深度学习社区在2015年之前面临的一个极具挑战性的难题,以及ResNet如何永久地解决了这个问题。我们还将讨论ResNet的实现,并将其结果与本课程中已实现的VGG、SqueezeNet和AlexNet进行比较。我们将对比所有结果,看看ResNet与其他卷积神经网络架构相比表现如何。
现在,让我们开始课程。
背景与问题
深度学习社区最初面临一个问题。2012年,AlexNet引入了用于图像数据训练的深度神经网络,并成功展示了通过同时实现ReLU激活、正则化技术以及基于GPU的计算,实际上可以训练更深的神经网络。这确实是一场革命。
但当社区尝试实现层数越来越多的模型时,问题出现了。AlexNet有8层,VGG有16层,Google在2014年提出的Inception V1(也称为GoogleNet)有22层。人们尝试构建越来越深的神经网络,因为他们知道深度神经网络可以从图像数据中学习更多特征。但问题是,训练这些深度神经网络非常困难。
在本课程中,我们尝试了AlexNet、VGG和Inception。到目前为止,我们获得的最佳准确率是在上一讲中的SqueezeNet,它取得了非常好的准确率。但SqueezeNet是一个完全不同的CNN模型。如果比较越来越深的神经网络,会发现一个反直觉的现象:在某些情况下,更深的神经网络表现更差。这意味着我们很难训练VGG和Inception。我们看到验证准确率高于训练准确率,这表明发生了欠拟合或训练不足,而不是过拟合。这有点反直觉,更深的模型似乎破坏了一切,无法正常工作。
但逻辑告诉我们,更深的神经网络应该能够学习更多东西。问题在于,当你有深度神经网络时,网络中的初始层(靠近输入层的层)几乎得不到更新。在梯度下降训练过程中,这些层的权重和偏置几乎不更新。我们将讨论这个问题在数学上是如何发生的。
我们将看看ResNet做了什么特别的事情来解决这个问题,以及它如何彻底改变了深度学习的进程,并完全改变了使用深度神经网络进行图像处理的方式。
梯度消失问题
梯度消失问题非常严重,即使使用良好的初始化参数,即使执行批量归一化或训练更多轮次,有时这个问题仍然存在。
在了解ResNet架构之前,让我们像本课程通常做的那样,简要回顾一下到目前为止我们所做的事情。这样,即使你从这一讲开始学习本课程,理想情况下你也应该能在一定程度上理解之前的内容。
我们正在尝试对五类花卉数据集进行分类。数据集包括雏菊、蒲公英、玫瑰、向日葵和郁金香,每个类别有1000张图像。因此,这是一个五分类问题。
我们从一个最简单的架构开始,即线性模型。我们将RGB图像转换为一个扁平层,输出层有五个节点,因为我们执行五分类。但我们没有使用任何激活函数。我们只在最后一层使用了softmax激活函数,以将输出数字转换为概率分布。这导致了40-45%的准确率和10-20的损失。准确率不算太差,但也不算太好,因为即使是随机分类器也能给你20%的准确率。这是40-45%(顺便说一下,这是训练准确率)。验证准确率是30-35-40%。
然后我们想,好吧,让我们添加一个具有128个节点和ReLU激活函数的隐藏层。这并没有太大改变准确率,但将损失降低了一个数量级。损失降低的原因有点反直觉,是因为预测变得越来越自信。这是一件好事,但我们并不满意。
然后我们认为,由于验证准确率是35-40%,训练准确率是40-45%,有时是15%,显然验证准确率和训练准确率存在差异,所以存在一些过拟合。我们想,让我们添加正则化:批量归一化、Dropout、提前停止训练轮次等。我们认为这可能有所帮助,并且在某种程度上确实有所帮助。我们将验证准确率提高到50-60%,但我们仍然不满意。
那时我们意识到,我们一直在从头开始训练,也许我们应该进行迁移学习,使用预训练的神经网络。我们最初在迁移学习中尝试了ResNet模型,训练了10轮,获得了100%的训练准确率和80%的验证准确率。这还可以,但过拟合正在发生,这就是训练和验证准确率存在巨大差异的原因。
然后我们想,好吧,现在让我们一步一步来,也许我们应该按时间顺序进行,从最早的CNN开始,然后逐步增加复杂性,看看当我们在相同数据集上执行分类时,分类效果如何改善。我们一直使用五类花卉数据集。
我们从AlexNet开始,这篇由Alex(第一作者,论文以其命名)、Jeffrey Hinton教授(2024年因引入神经网络而获得诺贝尔物理学奖)和Ilya Sutskever(也是OpenAI联合创始人)撰写的传奇论文永远改变了深度学习。这篇论文非常出色,被引用了17万次。我们运行了10轮,获得了90%的验证准确率和95%的训练准确率。我们非常高兴,这是本课程中第一次在没有太多过拟合的情况下获得如此好的准确率。
然后我们切换到VGG。VGG有更多的参数,正好有1.38亿个参数,而AlexNet大约有6000万个参数。因此,VGG明显更大。尽管我们使用VGG运行了50轮,但只获得了75%的训练准确率,而验证准确率却高达85%,高于训练准确率,这有点有趣,因为它表明我们训练不足。但我们不想运行更多轮次,因为这本身就需要大约四到五个小时。
然后我们切换到GoogleNet,也就是Inception V1。我们运行了50轮,获得了不错的准确率,验证准确率为87%,训练准确率为86%。我认为没有发生过拟合,但准确率没有AlexNet那么高。
在上一讲中,我们切换到SqueezeNet,它的参数数量非常少,不到500万个。我们意识到,SqueezeNet在50轮中的表现甚至比AlexNet在10轮中的表现更好。因此,AlexNet的验证和训练准确率分别约为94%和99%,SqueezeNet也有类似的表现。所以这是迄今为止最好的模型。我知道这有点像苹果和橘子的比较,因为我们在这里运行了更多轮次,但我们对类似VGG或GoogleNet这样层数很深的神经网络无法表现得这么好感到满意,因此对SqueezeNet我们非常满意。
但这就是我们目前所处的位置。SqueezeNet的引入是为了让神经网络能够在边缘设备上运行,例如手机、物联网设备,甚至可能是小型Arduino微控制器。因此,神经网络应该能够在性能不那么高的计算系统上运行。
但ResNet做了不同的事情。它没有解决压缩模型的问题,而是解决了能够训练大型模型的问题。当时的情况是,在2015年、2014年,越来越大的模型开始表现得不如较小的模型。这是一个令人担忧的问题,而ResNet的提出者希望解决它。我们将讨论这个问题。
但在我们进入课程之前,我总是想提醒你,开始一门课程或任何事情都很容易,但完成却一点也不容易。正如你从这个图表中看到的,随着课程的进行,观众数量在减少。所以,如果你现在正在听我讲这一课,那么恭喜你,你已经超越了大多数人。

梯度消失问题详解
上一节我们回顾了本课程的历程,并指出了深度模型训练困难的现象。本节中,我们将深入探讨其核心原因:梯度消失问题。
为了理解梯度消失,我们需要回顾一下神经网络是如何通过反向传播算法学习的。在训练过程中,我们计算损失函数相对于网络权重的梯度(导数),然后沿着梯度的反方向更新权重,以最小化损失。这个梯度信息从输出层开始,通过链式法则一层一层地向后传播到网络的早期层。
在非常深的网络中,这个反向传播的链条会非常长。如果每一层传递的梯度都小于1(例如,由于使用Sigmoid或Tanh激活函数,其导数在大部分区域都小于1),那么当梯度传播到早期层时,它会被反复相乘,导致其值变得指数级地小,最终趋近于零。用公式表示,对于第 l 层,其接收到的梯度大致为:
gradient_l ≈ gradient_output * ∏(derivative_of_activation_i) * ...,其中乘积项很多。当每个 derivative_of_activation_i 都小于1时,乘积结果会迅速衰减。
这意味着网络早期层的权重几乎得不到有效的更新。因此,尽管网络很深,但只有靠近输出的最后几层在学习,而负责提取基础特征(如边缘、纹理)的早期层却停滞不前。这就是为什么更深的网络有时表现反而更差,因为它们无法有效训练所有层。
ResNet的解决方案:跳跃连接
面对梯度消失的挑战,ResNet提出了一种简单而强大的解决方案:跳跃连接(Skip Connections)。
ResNet的核心思想是,我们不要求每一层直接学习一个完整的、新的特征映射,而是让它学习一个“残差”。具体来说,如果某一层的理想输入是 H(x),我们让该层学习残差 F(x) = H(x) - x。那么,该层的输出就变成了 F(x) + x。
以下是跳跃连接在代码中的典型实现方式(以PyTorch为例):
import torch.nn as nn
class ResidualBlock(nn.Module):
def __init__(self, in_channels, out_channels, stride=1):
super().__init__()
self.conv1 = nn.Conv2d(in_channels, out_channels, kernel_size=3, stride=stride, padding=1)
self.bn1 = nn.BatchNorm2d(out_channels)
self.relu = nn.ReLU(inplace=True)
self.conv2 = nn.Conv2d(out_channels, out_channels, kernel_size=3, stride=1, padding=1)
self.bn2 = nn.BatchNorm2d(out_channels)
# 跳跃连接:如果输入输出维度不一致,需要用1x1卷积调整
self.shortcut = nn.Sequential()
if stride != 1 or in_channels != out_channels:
self.shortcut = nn.Sequential(
nn.Conv2d(in_channels, out_channels, kernel_size=1, stride=stride),
nn.BatchNorm2d(out_channels)
)
def forward(self, x):
identity = x # 保留输入
out = self.conv1(x)
out = self.bn1(out)
out = self.relu(out)
out = self.conv2(out)
out = self.bn2(out)
out += self.shortcut(identity) # 添加跳跃连接
out = self.relu(out)
return out
这个设计带来了两个关键好处:
- 解决梯度消失:梯度现在有了一条“高速公路”,可以通过
out += identity这个加法操作直接流向更早的层。因为加法操作在反向传播时梯度为1,它确保了梯度能够无损地穿过跳跃连接,从而缓解了梯度消失。 - 简化学习目标:让一层学习一个残差
F(x)(即输出和输入的差异)通常比学习一个完整的变换H(x)更容易。如果恒等映射(即什么也不做)是最优的,那么网络可以简单地将F(x)的权重推向0。
ResNet架构概览
理解了核心构建块后,我们来看看完整的ResNet架构。ResNet有多种深度版本,如ResNet-18、ResNet-34、ResNet-50、ResNet-101和ResNet-152。数字代表网络的层数。
一个典型的ResNet(如ResNet-34)结构如下:
- 初始层:一个7x7卷积层,接着是批量归一化、ReLU和一个最大池化层。这用于快速下采样图像尺寸。
- 四个阶段(Stage):每个阶段由多个残差块堆叠而成。不同阶段之间,通过残差块中
stride=2的卷积来对特征图进行空间下采样(宽高减半),同时增加通道数。 - 全局平均池化与全连接层:在卷积层之后,使用全局平均池化将每个特征图压缩成一个值,然后接上一个全连接层进行最终分类。
以下是构建ResNet-34的简化代码框架:
class ResNet(nn.Module):
def __init__(self, block, layers, num_classes=5): # 5类花卉
super().__init__()
self.in_channels = 64
# 初始层
self.conv1 = nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3)
self.bn1 = nn.BatchNorm2d(64)
self.relu = nn.ReLU(inplace=True)
self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
# 四个阶段
self.layer1 = self._make_layer(block, 64, layers[0], stride=1)
self.layer2 = self._make_layer(block, 128, layers[1], stride=2)
self.layer3 = self._make_layer(block, 256, layers[2], stride=2)
self.layer4 = self._make_layer(block, 512, layers[3], stride=2)
# 分类头
self.avgpool = nn.AdaptiveAvgPool2d((1, 1))
self.fc = nn.Linear(512, num_classes)
def _make_layer(self, block, out_channels, blocks, stride):
# 创建包含多个残差块的一个阶段
layers = []
# 第一个块可能需要下采样和调整通道
layers.append(block(self.in_channels, out_channels, stride))
self.in_channels = out_channels
# 后续块
for _ in range(1, blocks):
layers.append(block(out_channels, out_channels, stride=1))
return nn.Sequential(*layers)
def forward(self, x):
x = self.conv1(x)
x = self.bn1(x)
x = self.relu(x)
x = self.maxpool(x)
x = self.layer1(x)
x = self.layer2(x)
x = self.layer3(x)
x = self.layer4(x)
x = self.avgpool(x)
x = torch.flatten(x, 1)
x = self.fc(x)
return x
# 实例化ResNet-34: 每个阶段的块数为 [3, 4, 6, 3]
def resnet34(num_classes=5):
return ResNet(ResidualBlock, [3, 4, 6, 3], num_classes)
与其他架构的性能对比
现在,让我们将ResNet与我们之前实现的其他经典架构在五类花卉数据集上进行对比。以下是预期的性能比较摘要:
| 模型 | 参数量 | 训练轮次 | 训练准确率 | 验证准确率 | 关键特点 |
|---|---|---|---|---|---|
| 线性模型 | 极少 | 10 | ~40-45% | ~30-40% | 基线,无卷积 |
| AlexNet | ~60M | 10 | ~95% | ~90% | 早期突破,ReLU,Dropout |
| VGG-16 | ~138M | 50 | ~75% | ~85% | 结构简单统一,训练困难 |
| Inception V1 | ~7M | 50 | ~86% | ~87% | 多尺度卷积,计算高效 |
| SqueezeNet | <5M | 50 | ~99% | ~94% | 极轻量,为边缘设备设计 |
| ResNet-34 | ~21M | 50 | ~99% | ~96% | 跳跃连接,解决梯度消失,易于训练极深网络 |
从对比中可以看出,ResNet在保持相对适中参数量的同时(ResNet-34约2100万参数),取得了最高的验证准确率。更重要的是,它证明了通过跳跃连接,训练非常深的网络(如ResNet-152)是可行且有效的,这为后续更强大的模型发展铺平了道路。
总结
本节课中,我们一起深入学习了ResNet。
我们首先回顾了深度神经网络训练中遇到的瓶颈,即随着网络加深,性能不升反降的反常现象。其根本原因被确定为梯度消失问题,即误差梯度在反向传播回早期层时变得极其微小,导致这些层的权重无法有效更新。
接着,我们探讨了ResNet的革命性解决方案:跳跃连接。通过让网络层学习输入与输出之间的残差 F(x) = H(x) - x,并将输入 x 直接加到输出上(H(x) = F(x) + x),ResNet创造了一条梯度传播的捷径。这极大地缓解了梯度消失,使得训练成百上千层的网络成为可能。
然后,我们解析了ResNet的架构组成,包括其初始卷积池化层、由多个残差块堆叠构成的四个阶段,以及最后的全局平均池化和全连接层。我们还提供了核心残差块和ResNet-34模型的代码实现。
最后,我们将ResNet与AlexNet、VGG、Inception和SqueezeNet等架构进行了对比。结果表明,ResNet凭借其卓越的设计,在五类花卉分类任务上取得了领先的性能,同时验证了其训练极深网络的有效性。

ResNet的思想影响深远,其跳跃连接已成为现代深度神经网络架构(如Transformer)中的常见组件。掌握ResNet,是理解当代深度学习模型的关键一步。
014:MobileNet - 口袋里的深度学习


在本节课中,我们将学习MobileNet,了解它如何使深度学习模型变得非常小巧,从而能够在手机或物联网设备等手持设备上运行。
在深入探讨MobileNet架构和实现之前,我们先简要回顾一下本课程迄今为止尝试过的模型及其在数据集上的表现。欢迎来到本节课。
概述:为何需要轻量级模型?🤔
MobileNet的价值主张在其名称中不言自明:它是一种可以放入手机等手持设备的神经网络。这种需求是自然产生的。
当深度学习科学家致力于构建越来越深的神经网络时,不可避免地产生了大型模型。例如,拥有1.38亿参数的VGG模型需要512MB的存储空间,这使得在树莓派或手机等小型设备上存储和进行推理变得非常困难,尽管其准确率很高。
随后出现的Inception V1模型更小且性能良好,ResNet 50虽然不是非常小的模型,但性能极佳。从2012年的AlexNet到2016、2017年,这些模型不断发展。
但问题在于,如前所述,大型模型需要更多存储空间,推理时间也更长。这意味着对于需要在小型设备上运行的实时应用来说,部署VGG、Inception、ResNet等模型非常困难,并且会导致设备电量快速耗尽。因此,我们需要轻量级架构。
然而,轻量级架构的问题是准确率可能不高。研究人员的初始目标是找到一个体积合理且准确率尚可的架构。本节课我们将看看MobileNet如何实现这一目标。
MobileNet由谷歌于2017年提出,其论文在arXiv上发表,目前已有超过32,000次引用,这显示了该论文的重要地位。论文标题为“MobileNets: Efficient Convolutional Neural Networks for Mobile Vision Applications”,其价值主张在标题中已直接体现。
课程回顾:我们走过的路 🔄
在具体探讨MobileNet如何实现小型化和高效率之前,让我们先简要回顾一下本课程至今的内容。如果您已经熟悉,可以跳过此部分。
我们从一个五类花卉分类数据集开始,包含蒲公英、玫瑰、向日葵等类别,每类约1000张图像。
-
线性模型:我们从一个简单的线性模型开始。将RGB三通道图像展平后,连接到一个具有五个输出节点的全连接层。输出层使用Softmax激活函数将输出转换为概率分布。
- 该模型的训练准确率约为20-45%,验证准确率为35-40%,损失值在10到20之间。
-
带隐藏层的模型:我们尝试了包含128个节点的隐藏层,并使用ReLU激活函数。
- 结果训练准确率相似(40-45%),但损失值降低了一个数量级(1-2)。这表明模型预测的置信度提高了,但分类准确率未显著提升。
-
正则化:由于观察到训练准确率通常比验证准确率高约10%,我们推断存在过拟合。因此,我们实施了批量归一化、Dropout和早停等正则化技术。
- 这使验证准确率提升至50-60%,训练准确率达到65-70%。
-
迁移学习:意识到从头训练难以达到高准确率后,我们转向迁移学习。首先使用ResNet 50架构(未进行大量超参数调优)。
- 结果获得了100%的训练准确率和80%的验证准确率,表明存在过拟合,但相比之前结果已有很大提升。
-
探索经典CNN架构:我们的目标是按时间顺序体验各种CNN模型,而非将单一模型优化到极致。
- AlexNet (2012):训练10个周期,获得95%训练准确率和90%验证准确率,效果出色。
- VGG16:训练50个周期,获得75%训练准确率和85%验证准确率,存在欠拟合迹象。
- Inception V1 (GoogLeNet):训练50个周期,准确率未达AlexNet水平。
- SqueezeNet:训练50个周期,获得95%训练准确率和91%验证准确率。该模型非常小巧,仅120万个参数。
- ResNet 50:训练50个周期,获得97%训练准确率和93%验证准确率。这是我们目前在本课程中看到的最佳模型,其残差连接架构非常巧妙。
至此,我们已了解了追求高准确率大型模型与设备资源限制之间的矛盾,这为引入MobileNet做好了铺垫。
MobileNet的核心创新:深度可分离卷积 🧠
上一节我们回顾了模型大小与性能的权衡。MobileNet的核心突破在于引入了一种名为深度可分离卷积的新型卷积操作,它极大地减少了计算量和参数数量。
标准卷积同时处理空间维度(图像的高和宽)和通道维度。深度可分离卷积将这个过程分解为两个独立的步骤:
-
深度卷积:每个输入通道使用一个独立的卷积核进行空间滤波。输入和输出通道数相同。
- 假设输入特征图尺寸为
D_F × D_F × M,卷积核尺寸为D_K × D_K × 1,输出为D_F × D_F × M。 - 计算成本约为:
D_K * D_K * M * D_F * D_F
- 假设输入特征图尺寸为
-
逐点卷积:使用1x1的卷积核混合深度卷积输出的通道信息,以生成新的特征表示。
- 输入尺寸为
D_F × D_F × M,使用N个1x1xM的卷积核,输出为D_F × D_F × N。 - 计算成本约为:
M * N * D_F * D_F
- 输入尺寸为
总计算成本约为:D_K * D_K * M * D_F * D_F + M * N * D_F * D_F
与标准卷积的计算成本 D_K * D_K * M * N * D_F * D_F 相比,深度可分离卷积的计算量减少了约:
1/N + 1/(D_K^2)
例如,当使用3x3卷积核(D_K=3)时,计算量可减少约8到9倍,同时参数量也大幅下降。
MobileNet架构详解 🏗️
基于深度可分离卷积,MobileNet构建了一个高效的架构。以下是其关键组成部分:
- 整体结构:MobileNet主体由28层构成(不包括平均池化层和全连接层)。它几乎全部使用深度可分离卷积(除了第一层是标准卷积)。
- 下采样:通过在某些深度可分离卷积层中设置步长为2来实现空间下采样。
- 宽度乘子:这是一个超参数α(0<α≤1),用于均匀地减少每一层的通道数,从而进一步控制模型大小和计算量。新的通道数变为
α * M和α * N。 - 分辨率乘子:这是另一个超参数ρ,用于降低输入图像的分辨率,从而减少内部特征图的大小,进一步降低计算成本。
通过组合深度可分离卷积、宽度乘子和分辨率乘子,MobileNet能够在准确率和效率之间提供灵活的权衡,以适应不同的设备限制。
总结与展望 📚
本节课我们一起学习了MobileNet,一个专为移动和嵌入式视觉应用设计的轻量级卷积神经网络。
我们首先回顾了本课程中尝试过的多种模型,从简单的线性模型到复杂的ResNet,理解了模型大小、计算成本与准确率之间的挑战。接着,我们深入探讨了MobileNet提出的核心解决方案——深度可分离卷积,它通过将标准卷积分解为深度卷积和逐点卷积两步,大幅降低了计算量和参数量。最后,我们了解了MobileNet的整体架构及其通过宽度乘子和分辨率乘子进一步调节模型复杂度的机制。

MobileNet的成功在于它巧妙地平衡了效率与性能,使得在资源受限的设备上运行先进的深度学习模型成为可能,为计算机视觉在移动端的普及奠定了基础。
015:DenseNet与EfficientNet - CNN的持续进化之路 🚀

在本节课中,我们将学习两种重要的卷积神经网络架构:DenseNet和EfficientNet。我们将了解它们如何通过创新的连接方式和缩放策略,解决传统CNN面临的问题,并实现更高效、更强大的性能。课程最后,我们将在Google Colab上实现这两种网络,并在五类花卉数据集上评估其表现。
课程回顾与架构分类 📚
上一节我们介绍了ResNet等经典架构,本节中我们来看看如何对已学的网络架构进行分类。在深入学习DenseNet和EfficientNet之前,我们先简要回顾一下本课程已涵盖的内容。
我们一直在使用五类花卉数据集(雏菊、蒲公英、玫瑰、向日葵、郁金香)进行实验。课程从最简单的线性模型开始,该模型将RGB三通道图像展平后,通过全连接层直接输出五类预测,未使用激活函数,仅在最后计算Softmax概率分布。该模型准确率仅为14%-45%,损失在10-20之间,效果不佳。
随后我们引入了包含128个节点的隐藏层和ReLU激活函数。这使损失降低了一个数量级,但并未显著提升分类准确率。模型预测变得更“自信”,但分类结果并未改变。同时,我们观察到随着训练轮次增加,训练准确率上升而验证准确率停滞,出现了轻微的过拟合现象。
在探索各种神经网络架构的过程中,你可能会思考如何对它们进行分类。神经网络架构可分为模块化架构和非模块化架构。我们将讨论已学架构中哪些是模块化的,以及单个模块的形态。然后,我们将把已学的VGG、AlexNet、ResNet-50等架构归类到这两种类型中。
追求更优的CNN 🎯
正如我们在前几节课讨论的,深度学习研究者(尤其是计算机视觉领域)的目标是构建“更好”的CNN。“更好”通常意味着在保持性能的前提下,模型更小。模型“小”可以指参数数量少,或网络架构的文件体积小。
例如,VGG是一个庞大的网络,拥有1.38亿参数。而SqueezeNet将其显著降低了两个数量级,体积压缩至约1.2MB。然而,寻找更优CNN的探索从未停止,至今仍在继续。
像VGG或AlexNet这样的经典CNN,其主要焦点是增加网络深度,并训练这些非常深的网络。当时的普遍共识是,更深的CNN能更好地学习图像数据中的特征模式。因此,增加深度并设法让模型高效训练以提高准确率,是经典CNN架构的主要目标。
深度带来的挑战与ResNet的解决方案 ⚙️
随后人们意识到,当神经网络规模(尤其是深度)增加时,会出现梯度消失问题。在CNN训练中,梯度下降的每次迭代,其参数更新大多发生在靠近输出的深层,而非浅层。这个问题被ResNet(残差神经网络)架构非常巧妙地解决了。我们在之前课程中讨论过ResNet,其引入的跳跃连接(Skip Connections)是一项革命性创新。
但问题在于,更深或更宽的模型会带来一些效率低下的问题。例如,GoogleNet(Inception)的单个模块就包含多个并行的卷积滤波器(1x1, 3x3, 5x5卷积)。因此,需要对CNN架构进行更好的缩放和更好的连接性。
所谓“更好的连接性”,是指如何将信息从初始层高效地传递到最终层。因为当信息从初始层到达最终层时,它已经经过了中间所有的隐藏层,这可能导致信息丢失或发生巨大变换,从而产生信息冗余。DenseNet如何高效地解决这个问题,将是本节课前半部分的重点。
DenseNet:密集连接卷积网络 🔗
DenseNet的核心思想非常简单:让每一层都与其后续的所有层直接连接。2017年提出的DenseNet论文《Densely connected Convolutional Networks》至今已有约54,000次引用,影响力巨大。
其架构设计如下:初始层连接到紧邻的下一层,同时也连接到第三层,甚至直接连接到最终层。这模仿了ResNet的跳跃连接思想,但又不完全相同。这种设计允许将先前层学习到的特征高效地传递给所有后续层。无论某一层学到了什么特征,它都能被高效地传递到其后的每一层,直至最终输出层。
观察最终输出层(或输出层之前的那一层),你会发现它接收来自之前所有卷积层或卷积块的信息。这显然促进了特征的高效学习。从直觉上讲,如果你想将信息从初始层传递到最终层,最直接的方法就是在它们之间建立一条连接。同时,这也有助于缓解梯度消失问题,因为初始层对输出的影响可以通过这些直接连接反映出来,这与传统的、顺序连接的VGG网络不同。
EfficientNet:复合模型缩放 📈
在本节课后半部分,我们将讨论EfficientNet。它允许研究者同时缩放输入图像的分辨率、网络的宽度以及网络的深度,这几乎是一种三维的神经网络缩放策略。EfficientNet的核心就是提出了这种高效的三维复合缩放方法。
实践与比较 🧪
对于这两种神经网络架构,我们都将在Google Colab上实现,并在五类花卉数据集上比较其准确率,同时也会与之前尝试过的所有架构进行对比。这些架构相对轻量,我们还会在课程最后对比它们与之前讨论的所有架构在参数量、层数等方面的差异。
以下是本节课的核心内容总结:
- DenseNet 通过密集连接,确保每一层都能接收前面所有层的特征图,促进了特征重用,缓解了梯度消失。
- EfficientNet 通过系统化地平衡网络深度、宽度和输入分辨率三个维度,实现了更高效的模型缩放。

本节课中,我们一起学习了DenseNet和EfficientNet这两种重要的CNN改进架构。理解了DenseNet如何通过密集连接增强特征传播,以及EfficientNet如何通过复合缩放策略来系统化地优化模型性能。接下来,我们将动手实现它们,并观察其在具体数据集上的表现。
016:使用神经网络寻找优秀CNN架构 - NASNet


在本节课中,我们将学习一种名为NAS(神经架构搜索)的著名CNN架构构建方法。我们将探讨其核心思想,并动手实现一个预训练的NASNet模型来分类花朵图像,同时将其性能与我们之前学过的其他架构进行比较。
神经架构搜索(NAS)的诞生 🧠
上一节我们回顾了多种由人类精心设计的CNN架构。本节中,我们来看看一个革命性的想法:让神经网络自己设计架构。
在2012年AlexNet赢得ImageNet竞赛后,深度学习领域涌现了VGG、Inception(GoogleNet)、ResNet等多种架构。这些架构都包含了人类的巧思与大量试错。
2017年,Google Brain团队提出了一个问题:能否使用一个神经网络来设计另一个神经网络?由此诞生了神经架构搜索(NAS)方法。他们使用循环神经网络(RNN)来生成构建CNN所需的模块,其成果就是NASNet。
然而,通过计算搜索最优神经网络架构的计算成本非常高。例如,Google在2017年找到他们提出的架构,动用了约500个GPU运行了四天。这超出了本课程的范围。
因此,本节课我们将不运行搜索算法,而是直接实现他们已搜索出的NASNet Large架构,并评估其在五类花朵数据集上的表现。
课程进展回顾 📚
在深入NASNet之前,我们先快速回顾一下本课程的进展,这有助于将所学架构清晰分类。
我们始于五类花朵数据集(雏菊、蒲公英、玫瑰、向日葵、郁金香)。这是一个五分类问题。
-
线性模型:我们从最简单的模型开始,仅将RGB图像展平后接入全连接层和5节点输出层,末端使用Softmax函数。训练准确率约40-45%,验证准确率约35-40%,损失值在10-20之间。
-
带隐藏层的浅层神经网络:我们添加了一个含128个神经元并使用ReLU激活函数的隐藏层。准确率提升不大(约40-45%),但损失值下降了一个数量级。这表明模型预测变得更自信,尽管正确预测的数量未显著增加。同时我们观察到过拟合迹象。
-
引入正则化:为了对抗过拟合,我们实施了批量归一化、Dropout和早停等正则化技术。这使验证准确率提升至50-60%,训练准确率在某些情况下达到70-75%。
-
转向迁移学习:对从头训练的结果不完全满意,我们决定转向迁移学习,并从此将框架从TensorFlow Keras切换到PyTorch。我们首先尝试了ResNet15(未精细调参),获得了100%的训练准确率和80%的验证准确率,这证实了迁移学习的有效性。
-
按历史顺序实现经典架构:
- AlexNet:训练10个周期,验证和训练准确率均达到约95%,效果很好。
- VGG16:训练50个周期,获得85%的验证准确率和75%的训练准确率。训练准确率更低是因为VGG参数量巨大(1.38亿),训练不足导致欠拟合。
- GoogleNet:训练50个周期,获得87%的验证准确率和86%的训练准确率。其核心是Inception模块,能同时使用1x1、3x3、5x5卷积来学习多尺度特征。
值得注意的是,GoogleNet、ResNet、MobileNet等我们尝试过的架构都是模块化架构。这意味着整个CNN由重复的、设计好的基础模块堆叠而成。
NASNet的核心思想与架构 🔬
了解了模块化设计后,我们来看看NASNet如何将这一理念与自动搜索结合。
NASNet的核心思想是:使用一个控制器(通常是RNN)来递归地生成描述神经网络架构的字符串。这个字符串定义了如何组合卷积、池化等基础操作来形成一个“单元”。搜索算法的目标是找到在验证集上性能最好的单元结构。
NASNet架构由两种单元重复堆叠构成:
- 常规单元:保持特征图尺寸。
- 降采样单元:将特征图的高和宽减半,同时增加通道数。
以下是构建一个NASNet单元(简化示意)可能涉及的步骤:
# 伪代码示意:控制器RNN生成架构决策
# 例如,决定当前层要采用哪种操作(Op)并与之前的哪层(Hidden State)连接
for step in range(num_steps):
# 控制器根据当前状态做出决策
operation = controller_rnn.predict_operation()
connect_from = controller_rnn.predict_connection()
# 根据决策构建计算图
new_tensor = apply_operation(previous_tensors[connect_from], operation)
previous_tensors.append(new_tensor)
最终,通过强化学习等方法优化控制器RNN,使其生成的架构能最大化验证准确率。搜索出的NASNet单元结构比人工设计的更为复杂和密集。
实现与评估NASNet 🌼
现在,我们将使用PyTorch中预训练的NASNet Large模型,并在五类花朵数据集上进行迁移学习。
以下是实现的关键步骤概述:
- 加载预训练模型:从
torchvision.models加载nasnetalarge,并冻结其所有参数。 - 修改分类器:替换模型最后的全连接层,使其输出5个类别。
- 准备数据:使用与之前课程相同的图像预处理和加载流程。
- 训练:只训练新替换的分类器层。
- 评估:计算模型在训练集和验证集上的准确率与损失。
我们将把NASNet的性能与之前实现的AlexNet、VGG16、GoogleNet、ResNet等架构进行对比。由于NASNet是自动搜索出的、参数量巨大的先进架构,我们预期它能取得非常有竞争力的结果。
总结 🎯
本节课我们一起学习了神经架构搜索这一前沿概念。我们了解到,NAS通过让一个RNN控制器自动搜索最佳模块组合来构建CNN,从而减少了对人类专家经验的依赖。虽然完整的搜索过程计算代价极高,但其成果(如NASNet)展示了自动化机器学习(AutoML)的巨大潜力。
我们回顾了从简单线性模型到复杂模块化架构(如Inception、ResNet)的演进历程,并亲自动手实现了预训练的NASNet模型来解决问题。通过对比不同架构的性能,我们能够更直观地理解模型设计如何影响最终的学习效果。

神经架构搜索代表了深度学习模型设计自动化的重要一步,尽管其资源消耗问题仍待解决,但它为未来更高效、更智能的模型设计指明了方向。
017:卷积神经网络与计算机视觉发展史(2010-2025)🚀


欢迎回到新的一课。本课是我们“计算机视觉从零开始”系列的一部分,但您无需担心任何背景知识,因为您几乎可以将其视为一门独立的课程。
在本节课中,我们将讨论计算机视觉领域发生的转变,特别是2010年至2020年期间的变化,并简要讨论过去五年计算机视觉领域的发展。
概述
大约一年前,您可能看到过Yann LeCun和Elon Musk在Twitter(现称X)上的争论。Elon Musk表示,他们的全自动驾驶汽车(FSD)已不再使用卷积神经网络(CNN),而Yann LeCun则质疑,如果不使用CNN,如何能在全自动驾驶汽车中实现实时的摄像头图像理解。
当时,关于传统CNN与视觉变换器(Vision Transformer)孰优孰劣的争论很多,人们在计算机视觉的背景下讨论了许多不同类型的模型。
您可能听说过ResNet、Inception、VGG、MobileNet等模型。ImageNet实际上是一个数据集,但我提到的其他许多模型都非常流行。然而,人们通常不知道如何将它们置于历史背景中:时间线是怎样的?这些模型最初为何被开发?它们取得了什么成就才变得如此流行?
因此,今天课程结束后,如果您能对从2010年代初开始的不同模型的演变有一个清晰的概念图,那将是本节课最大的收获。如果课程结束时您能做到这一点,我将非常高兴。
当我说按时间顺序演变时,我指的是类似这样的时间线:从1980年代、1990年代的手工模型开始,一直到今天的视觉变换器。本课不涉及任何编码,您可以放松身心,只需聆听这些不同模型演变的故事。如果您想记笔记,请随意。但我真正的意图是让您了解这些著名模型各自的相关性,以及计算机视觉是如何发展到我们今天在全自动驾驶汽车中看到的样子。那么,让我们开始吧。
模型演变时间线概览
在深入每个模型的具体细节之前,请先简要浏览一下这个时间线:
- 1980年代至2010年:手工设计的滤波器。
- 2012年:最著名的模型AlexNet问世。
- 2014年:VGG16和GoogLeNet出现。
- 2015年:ResNet-50问世。
- 2016年:DenseNet、SqueezeNet和YOLO v1出现。
- 2017年:NASNet、MobileNet v1出现。
如果您是完全的初学者,第一次听到这些名字,可能会觉得它们是一些随机的名称,不知道如何分类这些模型。我将为您提供一个非常好的概念图,帮助您理解这些模型的重要性:为什么开发了MobileNet?为什么这个神经网络架构的名字里有“Mobile”?为什么开发了ResNet?为什么这个名字里有“Re”?所有这些疑问都将在本课结束后得到解答。
第一部分:2012年之前——前深度学习时代
现在,让我们将计算机视觉的演变时间线划分为几个不同的部分。第一部分显然是在2012年之前,这是卷积神经网络兴起之前的时代。
在2012年之前,人们依赖于手工设计的滤波器。有许多手工设计的滤波器用于识别数据中的不同类型模式。在我们的计算机视觉课程系列中,我们已经接触过一些手工设计的滤波器。
例如,这个Sobel滤波器用于识别图像中形状的底部边缘。这是原始图像,包含圆形、正方形、三角形和菱形图案。Sobel滤波器是这个3x3的滤波器。
使用这个滤波器,我们可以检测到底部边缘。我们还讨论了如何将这些滤波器想象成光线:想象光线从负数区域射向正数区域,就像光线从底部射向顶部。如果这些形状从纸面上凸出,那么这些光线将照亮这些形状的底部边缘,这就是您在这里看到的。如果有人想要处理图像,无论出于什么原因想要检测底部边缘,就必须手工设计这样的滤波器。
我们还讨论了其他滤波器,比如用于检测轮廓的滤波器。轮廓意味着需要检测所有边缘。光线必须从外部径向向内照射,或者想象一个空心的环形形状向外和向内投射,想象光源位于空心环形内部,使得光线从内部径向向外照射,这样也可以照亮所有边缘。您可以看到这个滤波器中间是正数,外围是负数,就像光源在中间,照亮了所有边缘。这又是一个边缘检测滤波器,同样是手工设计的。这就是2012年之前的情况。
如果您看传统的机器学习模型,它们都是需要大量特征的模型,这些特征是手工设计的或手工工程化的特征,例如支持向量机、随机森林或决策树。支持向量机可用于二元分类,随机森林或决策树可用于多类分类。但归根结底,您需要特征及其值,以便可以将它们绘制在坐标轴上,或者使用特征和阈值来构建决策树的节点。
这就是传统的机器学习时代。当时还存在巨大的计算成本限制,即使在CPU上并行使用多个CPU进行计算也不容易。这使得训练深度神经网络非常困难。尽管神经网络的概念在2012年之前很久就出现了,但它并不突出,也没有被广泛用于计算机视觉任务,仅仅是因为训练一个计算机视觉模型来执行像猫狗分类这样的简单任务在当时一点也不容易。
第二部分:2012年——卷积神经网络的革命
正是在2012年,AlexNet被引入,这彻底改变了深度学习在计算机视觉领域的应用方式。
总而言之,我们目前看到的是计算机视觉的前深度学习时代。
现在我们将进入第二部分,即从2012年AlexNet开始的卷积神经网络革命。
AlexNet是由多伦多大学的一组研究人员提出的迄今为止最受欢迎的CNN架构。Alex是这篇论文的第一作者,这个神经网络就是以他的名字命名的。但您肯定会认出另外两位作者:一位是Geoffrey Hinton教授,他是2024年诺贝尔物理学奖得主,因发明人工神经网络而获奖;另一位是Ilya Sutskever,他是OpenAI的创始成员。
这篇论文目前有超过17万次引用,非常著名,引用率极高,所有这些研究人员现在都极其知名。
那么,AlexNet引入了哪些创新?它拥有一个包含八层的深度卷积神经网络。这些是AlexNet架构中使用的层:第一卷积层、第二层、第三层、第四层、第五层、第六层、第七层、第八层。中间有用于降低维度的最大池化层,还有展平操作。但归根结底,AlexNet有大约6000万个参数。他们同时引入了ReLU激活函数、用于正则化的Dropout,以及使用GPU进行并行处理以加速这些神经网络的训练。
AlexNet在2012年的ImageNet挑战赛中彻底击败了竞争对手,成为ImageNet 2012挑战赛的冠军。
为什么这如此重要?因为这是研究人员第一次毫无疑问地证明,深度神经网络架构可以被有效地训练,用于对复杂的图像数据进行良好的预测。

因此,AlexNet向研究人员明确表明,无需依赖手工设计的特征或滤波器。
总结

在本节课中,我们一起学习了计算机视觉从2010年到2025年的发展脉络。我们首先回顾了2012年前依赖手工特征和传统机器学习模型的前深度学习时代。接着,我们重点探讨了2012年AlexNet的出现如何开启了卷积神经网络(CNN)的革命,它通过深度架构、ReLU激活函数和GPU并行计算,证明了深度学习在图像任务上的巨大潜力。我们还简要浏览了后续几年出现的VGG、GoogLeNet、ResNet、MobileNet等关键模型的时间线,为理解它们各自解决的问题和背景奠定了基础。本节课的核心目标是帮助您建立起计算机视觉模型演变的“心智地图”,理解技术发展的内在逻辑与驱动力。
018:Vision Transformer (ViT) 入门 🧠

在本节课中,我们将要学习 Vision Transformer (ViT)。这是一种革命性的架构,它成功地将原本用于处理文本的 Transformer 模型应用到了计算机视觉领域,并取得了卓越的性能。
即使你从未听说过 Vision Transformer,或者听说过但不太清楚其工作原理,也无需担心,本教程正是为你准备的。这是“计算机视觉从零开始”系列的一部分,但即使你没有看过之前的课程,本节课也几乎可以独立学习。
我将今天的课程内容分为三个部分:
- 首先,我们将讨论卷积神经网络(CNN)及其在图像特征识别方面的优势,以及 CNN 存在的一些问题,这些问题最终由 Transformer 架构解决。
- 其次,由于 Transformer 是 Vision Transformer 架构的核心,我们需要深入理解 Transformer 是什么以及它是如何工作的。我们将花费相当一部分时间来讲解 Transformer 架构本身。
- 最后,我们将进入今天课程的核心——Vision Transformer 架构。
如果你已经相当熟悉 CNN 和 Transformer 架构,可以直接跳到最后一部分。否则,建议你按顺序学习以获得完整的知识脉络。
今天,我们不会在 Google Colab 上实现 ViT 架构,这部分内容我留给了另一节单独的课程,届时我将展示 CNN 和 ViT 在特定数据集上的性能差异。
那么,让我们开始吧。
卷积神经网络(CNN)回顾与局限 🔍
上一节我们介绍了本课程的结构。本节中,我们来看看卷积神经网络(CNN)的基本原理及其局限性。
卷积操作
CNN 架构中的核心是卷积操作。这个过程相当简单:你有一张图像(例如一个二维图像,由一组绿色像素表示),一个滤波器(或称卷积核)会作用在这张图像上。这个滤波器是一个矩阵(例如一个 3x3 的矩阵)。
这个矩阵会在图像的不同区域上滑动,生成结果图像。例如,原始图像是 5x5,经过一个 3x3 的卷积操作后,结果图像可能是 3x3。这里还涉及“填充”(padding)的概念,即在图像边界添加额外的像素。另一个概念是“步长”(stride),它决定了滤波器在每一步卷积中滑动的像素数。
这个想法之所以具有革命性,是因为根据滤波器矩阵中的数值,它可以捕获原始图像中不同类型的特征。
CNN 扩展了这一思想,将卷积操作作为神经网络不同层的一部分。
CNN 的成功与问题
卷积神经网络非常成功。自 2012 年 AlexNet 架构在 ImageNet 竞赛中获胜并展现出巨大潜力后,深度学习便成为计算机视觉实现的主要方式。在此之前,人们主要关注手工设计的滤波器。AlexNet 的出现彻底改变了深度学习领域。
随后,VGGNet 和 GoogLeNet 在 2014 年出现,分别获得竞赛的第二和第一名。2015 年,ResNet 问世,再次改变了深度学习在计算机视觉中的应用方式。
CNN 在检测图像模式方面非常强大,这使得它能够在包含上千个类别的图像上实现高精度分类。
然而,CNN 也存在一些问题,这些问题源于其背后的基本假设。
CNN 的三个核心假设及其局限
以下是 CNN 的三个核心特征,它们在某些情况下会带来问题:局部性、平移不变性和权重共享。让我们从逻辑上理解它们的含义。
1. 局部性
CNN 假设在图像的小区域内可以找到重要的模式。所谓“小区域”,是指可以被小滤波器覆盖的区域。例如,对于一个 224x224 的图像,一个 3x3 的滤波器只覆盖了图像中非常小的一部分。这个假设认为,这些特征可以捕获局部模式。
2. 平移不变性
这个假设认为,如果一个滤波器旨在捕获某种特定特征,那么无论这个特征出现在图像中的哪个位置,该滤波器都能捕获它。CNN 假设这样做没有问题,但我会展示一个这可能成为问题的案例。
首先,我们看看滤波器如何工作。假设这是一个用于检测左侧边缘的 3x3 滤波器。你可以想象这个滤波器就像一束从左向右照射的光线。如果图像中有两个圆形物体,并且假设它们是略微凸出纸面的圆柱体,那么从左向右的光线会照亮这些形状的左边缘,形成一个半圆形的亮区。
这个“左侧边缘检测器”滤波器作用于图像时,就像光线照射在这些形状上一样。注意,这两个圆形的位置并不重要——一个在图像的左上角,一个在右下角——但同一个滤波器都能检测到它们的左边缘。在这个例子中,平移不变性不是问题。
但是,考虑下面这种情况。假设你有一个滤波器,它非常擅长识别汽车形状的物体轮廓。这是一个神奇的滤波器,我们不必深究其细节。重要的是,无论汽车出现在图片的哪个位置,这个滤波器都能检测到其特征。
例如,图中有一个汽车物体在地面上,另一个汽车物体在建筑物顶上。在这两种情况下,这个滤波器都能检测到汽车的轮廓。汽车的相对位置信息根本没有被纳入考虑。
有时这就是一个问题。假设任务是识别某物是否真实,你需要判断这是否是一张真实世界环境中的汽车图像。那么,汽车在建筑物顶上这种情况在现实生活中几乎永远不会发生。
但由于这些滤波器具有平移不变性,如果它旨在捕获所有类似汽车的边缘,它就不会关心这个汽车物体是在建筑物顶上还是在空中。然而在现实中,这些物体的相对位置很重要。在实际图像中,你不会发现汽车在建筑物顶上,通常汽车位于建筑物的底部。
那么,如何让神经网络学习到图像中的这种知识——如果图像中有两个物体,一个是建筑物,一个是汽车,通常汽车不会在建筑物顶上?这需要学习图像中相距较远的区域之间的特征关系。
Transformer 架构详解 ⚙️
上一节我们探讨了 CNN 的局限性,特别是其在处理图像中长距离依赖关系时的不足。本节中,我们来看看 Transformer 架构,它是解决这一问题的关键,也是 Vision Transformer 的基础。
Transformer 最初是为自然语言处理任务设计的,其核心思想是使用“自注意力机制”来建立序列中所有元素两两之间的关系,无论它们相距多远。
自注意力机制
自注意力机制允许模型在处理一个元素(例如一个单词或一个图像块)时,关注输入序列中的所有其他元素,并动态地为每个元素分配不同的重要性权重。
其核心计算可以用以下公式表示:
Attention(Q, K, V) = softmax(QK^T / √d_k) V
其中:
- Q (Query):查询向量,代表当前要处理的元素。
- K (Key):键向量,代表序列中所有元素,用于与查询进行匹配。
- V (Value):值向量,包含序列中所有元素的信息,用于加权求和。
- d_k:键向量的维度,用于缩放点积,防止梯度消失。
这个过程使得模型能够捕获序列内部的全局依赖关系。
Transformer 编码器结构
一个标准的 Transformer 编码器层通常包含以下组件:
- 多头自注意力层:并行运行多个自注意力机制,从不同子空间捕获信息。
- 前馈神经网络:一个简单的全连接网络,对每个位置的特征进行独立变换。
- 残差连接与层归一化:每个子层(自注意力和前馈网络)周围都使用残差连接,并进行层归一化,这有助于稳定训练并允许构建更深的网络。
以下是 Transformer 编码器层的简化表示:
# 伪代码表示 Transformer 编码器层
class TransformerEncoderLayer:
def forward(x):
# 1. 多头自注意力 + 残差 & 层归一化
attn_output = multi_head_attention(x, x, x) # Q, K, V 都来自 x
x = layer_norm(x + attn_output)
# 2. 前馈网络 + 残差 & 层归一化
ff_output = feed_forward_network(x)
output = layer_norm(x + ff_output)
return output
正是这种能够建模序列中任意两个元素之间关系的能力,使得 Transformer 非常适合处理需要理解全局上下文的视觉任务,从而弥补了 CNN 在长距离依赖建模上的不足。
Vision Transformer (ViT) 架构 🖼️➡️🧠
上一节我们深入了解了 Transformer 的工作原理。本节中,我们终于可以看看 Vision Transformer (ViT) 是如何巧妙地将 Transformer 应用于图像处理的。
ViT 的核心思想非常直观:将一张图像视为一系列“图像块”的序列,然后像处理句子中的单词一样处理这些图像块。
ViT 工作流程
以下是 ViT 处理图像的主要步骤:
1. 图像分块
将输入图像分割成固定大小的小块(例如 16x16 像素)。假设输入图像是 224x224 分辨率,使用 16x16 的分块,你将得到 (224/16) * (224/16) = 196 个图像块。
2. 图像块嵌入
每个图像块被展平成一个向量,并通过一个可学习的线性投影层(全连接层)进行变换,生成“图像块嵌入”。这个嵌入向量的维度就是 Transformer 模型隐藏层的大小(例如 768 维)。
3. 添加位置编码
由于 Transformer 本身不考虑输入的顺序,而图像块的空间位置信息至关重要,因此需要向每个图像块嵌入中添加“位置编码”。位置编码是一个与嵌入向量维度相同的向量,它包含了该图像块在原始图像中的位置信息(如第几行、第几列)。这使模型能够理解图像块之间的空间关系。
4. 添加分类令牌
在序列的开头,额外添加一个可学习的“[CLS]”令牌(分类令牌)。这个令牌经过 Transformer 编码器处理后,其对应的输出向量将用于整个图像的分类任务。
5. Transformer 编码器
将包含图像块嵌入、位置编码和分类令牌的完整序列输入到一个标准的 Transformer 编码器堆栈中。编码器通过自注意力机制让所有图像块(包括分类令牌)相互交换信息,从而学习图像的全局表示。
6. 分类头
最后,取出分类令牌对应的输出向量,通过一个小型的多层感知机(MLP,即全连接网络)进行分类,得到最终的图像类别预测。
架构示意图与公式
整个过程可以概括为以下步骤:
令 X 为输入图像,将其划分为 N 个图像块 [x_p1, x_p2, ..., x_pN]。
- 对每个图像块进行线性投影:z_0 = [x_cls; Ex_p1; Ex_p2; ...; E*x_pN] + E_pos
x_cls是可学习的分类令牌。- E 是图像块投影矩阵。
- E_pos 是位置编码。
- 将序列
z_0输入 L 层 Transformer 编码器:z_l' = MSA(LN(z_{l-1})) + z_{l-1}, z_l = MLP(LN(z_l')) + z_l'MSA是多头自注意力。LN是层归一化。MLP是前馈网络。l从 1 到 L。
- 取最后一层分类令牌的输出
z_L^0,通过分类头(MLP)得到预测:y = MLP_Head(LN(z_L^0))
通过这种方式,ViT 摒弃了 CNN 的卷积和池化操作,完全依赖 Transformer 的自注意力机制来建模图像中所有区域之间的全局关系,从而能够更好地理解图像的上下文和不同部分之间的复杂交互。
总结 📝
在本节课中,我们一起学习了 Vision Transformer (ViT) 的完整知识脉络。
我们首先回顾了卷积神经网络(CNN)的成功与其内在的局限性,特别是其局部感受野和平移不变性假设在处理图像全局上下文和长距离依赖关系时的不足。
接着,我们深入探讨了作为 ViT 基石的 Transformer 架构,重点讲解了其核心——自注意力机制,它能够建立序列中任意两个元素之间的联系。
最后,我们详细解析了 Vision Transformer 的架构。其核心创新在于将图像分割为小块序列,并像处理自然语言一样,通过添加位置编码和分类令牌,利用标准的 Transformer 编码器对其进行处理。这使得模型能够捕获图像的整体上下文信息,在许多视觉任务上超越了传统的 CNN。

ViT 的成功证明了自注意力机制在视觉领域的强大潜力,为计算机视觉开辟了一条新的道路。
019:从零开始用PyTorch实现Vision Transformer 🚀
在本节课中,我们将从零开始,使用PyTorch实现一个简单的Vision Transformer模型。我们不会使用任何预训练架构,所有代码都将由我们自己定义。我们将使用一些实用的PyTorch库来简化流程,但模型的训练将完全从零开始。为了简化任务,我们将使用著名的MNIST手写数字数据集,将其作为一个10分类问题来处理。
即使你对Vision Transformer完全没有概念,也可以跟随本教程一起编码。本教程对初学者非常友好。课程分为两部分:第一部分将简要讨论Vision Transformer的理论、工作原理,并快速浏览Transformer架构本身。我们还将讨论基于文本的Transformer(如GPT)与用于图像的Vision Transformer之间的区别。但本节课的主要目的是实际编码,我将逐行编写代码,以便你能以相同的节奏跟随。我将使用Google Colab进行编码,因此你可以轻松开始,无需任何先决条件。
现在,让我们开始学习。
理论概述 📖
上一节我们介绍了本课程的目标,本节中我们来看看Vision Transformer的基本概念。

Vision Transformer的概念由Google的一个团队在题为《An Image is Worth 16x16 Words》的论文中提出。该论文展示了将Transformer架构应用于图像识别任务的方法。



Transformer架构本身则是在2017年的论文《Attention Is All You Need》中提出的,同样出自Google的另一个团队。看到Google AI研究能产出如此多相似的论文,确实令人惊叹。




在深入Vision Transformer架构之前,我们先来看论文中描述其工作原理的著名图示。我将把这个复杂的架构分解成易于理解的部分,所以不必担心它看起来令人生畏。



《Attention Is All You Need》论文已有约17-18万次引用,而这篇2020年发表的Vision Transformer论文也有约6.5万次引用。你可以在arXiv上查阅这些论文。



核心思想:图像分块 🧩

上一节我们了解了Vision Transformer的起源,本节中我们来看看其核心思想。
基本思想如下:在常规的基于文本的Transformer架构中,你将句子分割成词元,可以将其想象为单词。而在Vision Transformer中,输入是图像。你需要通过将图像分割成称为图像块的部分来对图像进行“词元化”。
例如,假设输入图像是48像素 x 48像素。你可以将其分割成多个块。假设我们使用16x16的块大小,那么你将得到9个这样的块。计算方法是:长度方向 48 / 16 = 3,宽度方向 48 / 16 = 3,所以总共是 3 x 3 = 9 个块。



因此,就像在基于文本的Transformer中句子被分割成固定数量的词元一样,在Vision Transformer中,输入图像被分割成多个图像块(这里是9个)。块的数量取决于你选择的块大小和原始图像的尺寸。

输入维度与CLS令牌 🔢
上一节我们了解了如何将图像分块,本节中我们来看看这些块如何输入到Transformer中。
现在,我们来看看这些块如何被输入到Transformer架构中。这里的Transformer架构与我们在GPT等模型中看到的并无不同。让我们快速浏览一下。我制作了一个详细但简单的图示,展示了适用于图像的Transformer架构。
首先,我们将图像转换为图像块。在这个例子中,我们有9个块。如果我们查看每个输入的维度,它看起来像 (1, 9, 768)。
- 1:这是批量大小。有时我们可能一次处理多个图像。如果一批处理4张图像,批量大小就是4。
- 9:这代表图像块的数量,即单张图像被分割成的子图像数量。
- 768:这是每个块的像素数,也称为嵌入维度。计算方式是:每个块是16x16像素,并且有3个颜色通道(RGB)。所以,
16 * 16 * 3 = 768。我们正是将这些图像块转换成某种向量表示,就像在基于文本的GPT中将单词嵌入成向量表示一样。

除了这些输入数据,我们还会添加一个额外的向量,称为CLS令牌(分类令牌)。在基于文本的GPT架构中,我们使用最后一个词元来预测下一个单词。但在这里,我们不是要预测下一个图像块(至少在分类任务中这没有意义)。我们想要预测的是:这张整体图像代表什么?例如,如果我们进行10分类,我们想要一个概率分布来判断这张图像很可能是哪个数字。
这个CLS令牌将用于执行分类任务。它被添加到输入词元(即图像块)的开头。在原论文的图示中,你可以看到图像被分割成块(图中显示为9个块),然后这些块的向量表示与位于位置0的CLS令牌一起被输入。




位置编码 📍
上一节我们介绍了CLS令牌,本节中我们来看看另一个关键概念:位置编码。


在基于文本的Transformer中,除了词元的向量表示,我们还会添加位置编码,用以表示该词在句子中的位置。这一点至关重要,因为单词的顺序决定了句子的含义。


考虑以下两个句子:
All dogs are animals.All animals are dogs.

如果你将这些句子分割成单词(暂时忽略空格),两个句子包含的四个单词完全相同。但这两个句子的含义截然不同。为了捕捉含义,你必须知道这些单词在句子中的相对位置。

这就是为什么在单词(如“dogs”或“animals”)的向量嵌入之外,你还要添加与该单词位置对应的向量嵌入。例如,在第一个句子中,“dogs”位于位置1(假设从0开始计数)。那么,“dogs”的最终向量表示就是其原始单词向量与位置1向量的和。这个合成向量既包含了“dogs”这个词的信息,也包含了它在序列中处于位置1的信息。

基于文本的Transformer是这个原理,Vision Transformer也是如此。对于图像块,我们也需要添加位置编码,因为图像块在原始图像中的空间位置(例如,左上角、中间、右下角)包含了重要信息。模型需要知道这些块是如何排列的,才能理解完整的图像。

总结 🎯
本节课中,我们一起学习了Vision Transformer的基本概念。我们了解到:
- Vision Transformer将图像分割成固定大小的图像块,作为输入词元。
- 每个图像块被线性投影为一个嵌入向量。
- 在输入序列的开头添加一个特殊的CLS令牌,用于最终的分类任务。
- 向嵌入向量中添加位置编码,以保留图像块的空间位置信息。
- 处理后的序列(CLS令牌 + 图像块嵌入 + 位置编码)被送入标准的Transformer编码器中进行处理。

这些核心思想使得Transformer这一强大的序列处理架构能够成功地应用于计算机视觉任务。在接下来的实践部分,我们将把这些理论转化为具体的PyTorch代码。
020:课程介绍与计算机视觉概述 🎯
在本节课中,我们将学习计算机视觉训练营的课程安排、讲师的背景介绍,以及计算机视觉从传统方法到深度学习演变的简要历史。我们还将探讨计算机视觉在不同领域的实际应用。


欢迎参加本次计算机视觉实战训练营。本次训练营有非常具体的议程,我将说明我们将具体涵盖哪些内容、课程面向的人群以及您能从中获得什么。
首先,我花一点时间做一下自我介绍。我的名字是 Sa Panard。





我是 Visra AI Labs 的联合创始人之一。



我于2012年至2017年在 IIT Madras 完成本科教育。毕业后,我于2017年至2022年在麻省理工学院攻读博士学位。我的博士研究主要涉及界面工程,也涉及一些电气工程、机器学习和机器人技术,这是一个融合了多个不同领域的跨学科领域,我度过了非常愉快的时光。




在攻读博士学位期间,或者说稍早一些时候,我和 Raj Rajathan 决定一起工作。您可能通过 LinkedIn 或我们的 YouTube 频道熟悉他们。



我们决定创立 Biswarra,旨在人工智能领域开展多项工作。

如果您感兴趣,可以观看这个视频。这是一个关于我博士论文工作的三分钟视频,发布在麻省理工学院的 YouTube 频道上,目前已有近30万次观看。我的基本工作是建造一个自清洁太阳能电池板,它可以清除灰尘并自我清洁,恢复因灰尘积累而损失的近95%到98%的功率输出。因为在沙漠环境中,灰尘积聚在太阳能电池板上会显著降低效率。这是一个无水、静电自清洁的机器人系统,可以在不需要一滴水的情况下清洁太阳能电池板。


有趣的是,在进行这个项目时,机器学习不经意间进入了视野。因为存在一个叫做“除尘电压”的概念。除尘电压是指需要施加每厘米多少伏特的电压来排斥电荷,并排斥特定尺寸(例如30微米或50微米)的颗粒。灰尘颗粒有不同的尺寸。

这个除尘电压完全取决于颗粒尺寸。但如何知道颗粒尺寸呢?您不可能用显微镜检查太阳能电池板每个区域的颗粒尺寸。那么,一个明智的做法是什么?我购买了一个非常便宜的相机,可以获得50倍到100倍的放大倍率。它不如专业显微镜好,但可以稍微放大,并在一帧图像中给我提供数千个灰尘颗粒。


现在,我想从这一帧图像中获得灰尘颗粒的尺寸分布,特别是其平均值和标准差,以便我知道大多数颗粒属于哪个尺寸范围,从而可以施加相应的除尘电压,无论是10千伏、12千伏还是15千伏。

您会怎么做?您会去数吗?您会在每个颗粒周围拟合圆形并测量平均尺寸分布吗?不会。这就是机器学习,特别是计算机视觉,发挥作用的地方。我实现了一个卷积神经网络,可以实时处理来自摄像头的图像,然后将灰尘颗粒尺寸分类到不同的范围,例如30微米范围、50微米范围等。基于此,我的自主太阳能电池板清洁器可以决定施加多大的电压。这是计算机视觉,特别是深度学习在计算机视觉中,应用于一个完整的动手物理实验装置和产品的一个有趣案例。


我想与您分享这个案例,因为我非常确定参加本次课程的人背景各异,不仅仅是计算机科学。我相信你们来自不同的背景,比如机械工程,有些人可能来自计算机科学,有些人可能来自完全不同的非工程背景。您可能想知道为什么深度学习计算机视觉与您相关。它之所以相关,正是我想通过这个例子向您展示其相关性。计算机视觉领域已经渗透到各个行业的不同领域,我将在今天的讲座中展示一些示例行业。

这只是我的介绍。我还想再提一个小故事,关于我最初在传统计算机视觉方面的研究尝试。这是十多年前,当我还是本科生时,我们正在研究一个分类米粒的问题。如果表面上有一堆米粒,有很多不同的品种,比如巴斯马蒂米、茉莉香米、意大利米等等,有几十个品种的米粒。

平均而言,这些米粒有一个平均尺寸,存在一个分布,但尺寸因米粒品种而异。米粒在表面上的配置方式有很多种。有时可能很稀疏,分布得图像上只有几粒米;或者可能堆在一起,很多米粒相互接触。那时,尽管深度学习已经略微进入视野(得益于我们稍后会讨论的 AlexNet),但与深度学习无关的传统计算机视觉方法仍然很突出。
我们当时试图做的是类似椭圆拟合的事情。假设您有一个看起来像这样的米粒。


实际上,在图像中,当您执行某种阈值处理时(即将图像转换为纯黑或纯白图像),您可能会注意到像这样的形状,它们并不代表实际的米粒。可能发生的情况是两个米粒相互接触。因此,您必须首先找出在什么条件下,什么算法可以检测到两个米粒何时接触,因为它们的轮廓将只是一个单一的形状。






然后,将这些形状分割成两个不同的形状,找到这些形状的质心或几何中心并拟合椭圆,接着计算这个椭圆的长轴和短轴长度,然后绘制尺寸分布图。例如,长轴可能是 B,短轴可能是另一个值。然后,相似品种的米粒,比如某种米,可能分布在这里。




而另一种米可能分布在那里。
这是一个纯粹的传统计算机视觉方法,与深度学习无关,神经网络甚至不在考虑范围内。但现在众所周知,这也可以用神经网络来完成。但好处是,这项工作让我们很好地接触了当时机器视觉和计算机视觉中正在发生的各种技术。这项工作被第九届国际机器视觉会议接受,该会议在法国尼斯举行。




当我们在那次会议上展示我们的工作时,我们清楚地意识到,研究界在不久的将来将完全转向基于深度学习的计算机视觉方法,没有人会再研究手工设计的特征。我非常幸运能够几乎实时地经历这一重大转变。
当然,在计算机视觉或计算机科学更深入的社区中,已经发生了其他转变。AlexNet 是2012年发表的论文,首次展示了可以使用深度神经网络在大型图像数据集上进行训练,他们甚至使用了 GPU 进行训练。它首次展示了可以利用并行计算使一切变得更快。如果您看英伟达的股价,其上涨不能完全归因于单一事件,但如果可以,那么这将是其中之一,即 AlexNet 的提出。Alex 是该论文的第一作者,还有 Geoffrey Hinton 教授,他因在神经网络方面的工作获得了2024年诺贝尔物理学奖,以及 Ilya Sutskever,他是 OpenAI 的创始人之一。
这一切都发生在2012年,然后人们逐渐开始从工作中消除手工设计的特征。

那么,手工设计的特征是如何工作的呢?因为我们知道,在卷积神经网络中,它们使用一种叫做卷积滤波器的东西。滤波器很重要,只是人类不定义滤波器的值,它是在深度学习过程中通过反向传播学习得到的。
但是,在此之前,这个滤波器到底对图像做了什么?如果您有一张图像和一个滤波器,会发生什么?



本节课中我们一起学习了本次计算机视觉实战训练营的课程目标与讲师背景,并通过两个具体案例(自清洁太阳能板与米粒分类)了解了计算机视觉从依赖手工特征的传统方法到基于数据驱动学习的深度学习方法的演变历程。我们还简要提到了推动这一转变的关键事件(如AlexNet的提出),为后续深入学习奠定了基础。
021:目标追踪与计数
在本节课中,我们将学习如何使用OpenCV和预训练的YOLO模型,构建从单目标检测到多目标追踪与计数的完整应用。我们将涵盖目标检测、追踪、计数以及分割的基本概念和实现方法。
上一节我们介绍了OpenCV库,并讨论了滤波器及其原理。最后,我们构建了一个简单的“防盗检测”算法,它基于纯逻辑,通过比较帧间像素簇的差异来工作。
本节中,我们将使用预训练的深度学习模型构建更复杂的应用。虽然今天不会深入讲解这些模型的细节(这将在训练营后续部分专门介绍),但我们将动手实现以下功能。
以下是本节课将要构建的应用列表:
- 单目标检测:在图像或视频流中,检测并标注单个目标。
- 多目标检测:扩展单目标检测,同时检测并标注多个目标。
- 目标追踪:为检测到的目标分配唯一ID,并在连续帧中保持该ID,以追踪其运动轨迹。
- 目标计数:基于目标追踪,统计视频中出现的独特目标数量。
- 目标分割:区分检测与分割,并初步实现像素级的语义分割。
目标可以是人、动物或任何物理实体。我们将使用来自网络(如YouTube)的视频或图像作为数据。使用的模型是YOLOv8,这是一个高效的预训练实时目标检测算法。整个实现将基于OpenCV框架。
在开始之前,我们需要明确几个核心计算机视觉任务的区别:图像分类、目标检测、目标追踪、目标计数和图像分割。
图像分类是最简单的任务。给定一张图像,算法将其整体归类为N个类别中的一个。例如,在猫狗二分类中,算法会输出图像是猫的概率(如99%)。这通常通过一个末端带有Softmax分类器的卷积神经网络(CNN)实现。



目标检测则包含两个任务:分类和定位。对于一张包含多个目标(如猫和狗)的图像,算法需要预测每个目标周围的边界框及其类别概率。边界框由四个参数定义:左上角坐标 (x, y)、宽度 w 和高度 h。算法会输出类似“边界框位置为 (x, y, w, h),框内目标为‘狗’,置信度98%”的结果。YOLO就是用于生成这些边界框的算法。
目标追踪关注的是目标在连续帧间的运动。它为每个检测到的边界框分配一个唯一ID。例如,一个人被分配ID=3。在后续帧中,即使目标移动,算法也应确保对应此人的边界框ID仍为3,不应改变。通过追踪边界框的质心(中心点,计算公式为 (x + w/2, y + h/2)),可以可视化目标的运动轨迹。
目标计数可以基于目标追踪来实现。通过统计整个视频序列中出现过的不同ID数量,即可得到经过的目标总数。例如,如果视频中总共出现了15个不同的ID,那么目标计数就是15。这种方法可用于传送带系统计数等场景。

图像分割与检测有本质不同。分割不是画一个粗糙的边界框,而是要在像素级别上识别出属于特定目标的所有像素,并将其从背景中分离出来。例如,精确勾勒出骆驼的轮廓,而不是用一个矩形框住它。这被称为语义分割,我们将在后续课程中深入探讨。

现在,让我们回顾一下本节的整体设置。我们将处理图像、视频或摄像头实时流数据。核心模型是YOLOv8。整个实现将搭建在OpenCV框架之上。

本节课中,我们一起学习了目标检测、追踪、计数和分割的基本概念,并了解了如何使用YOLOv8和OpenCV来构建这些应用。从单目标识别到多目标运动分析,这些技术是构建更高级计算机视觉系统的基础。
022:使用R-CNN、Fast R-CNN和Faster R-CNN进行目标检测 🎯
在本节课中,我们将学习目标检测领域的一个里程碑系列模型:R-CNN及其演进版本。我们将从最基础的想法出发,理解为什么需要“区域提议”,并详细探讨R-CNN、Fast R-CNN和Faster R-CNN的工作原理。课程内容将尽可能简单直白,确保初学者能够跟上。
课程概述与背景
我们正处于课程的第二部分,将讨论基于卷积神经网络(CNN)的目标检测方法。R-CNN系列模型主要用于目标检测,而它的一个变体——Mask R-CNN,还可用于图像分割任务。
需要提醒大家的是,本课程的主要目的之一是教授能够进行图像分割的技术。图像分割是指精确检测出物体的边界轮廓。而一个更简单的版本是目标检测,即用一个边界框将物体框出来。

在之前的几节课中,我们看到了如何使用YOLO实现目标检测,但并未深入探讨模型本身的工作原理,我们主要关注了最终结果和代码实现。今天,我们将深入细节,了解这些目标检测或图像分割模型究竟是如何被开发出来的,其背后的理论是什么。我们将从R-CNN开始。
从基础想法到R-CNN的诞生
R-CNN中的“R”代表“区域”(Regions)。接下来,我将解释为什么CNN会与“区域”这个术语关联在一起。这里的CNN指的是简单的卷积神经网络。
让我们退一步思考。假设我们回到2012或2013年,也就是大约10到12年前。如果我们要开发一个目标检测算法,可能会怎么做?因为AlexNet已经在2012年提出,所以到2012年,图像分类对我们来说已经可以轻松完成。
现在,我们讨论的是2013或2014年。那时我们已经知道如何使用LeNet或VGG等模型很好地完成图像分类。但现在的目标是检测图像中你感兴趣的物体所在的像素区域。

假设你有一张包含汽车的图片,你的目标是以一定的概率百分比检测出汽车周围的边界框。

那么,目标就是得到类似这样的结果:一个边界框和一个置信度分数。如果你要构建这样的系统,没有任何额外思路,你可能会尝试的最基础的算法是什么?
问题是:如果你正在构建一个基础算法,用于检测可能包含特定物体(可能是汽车、猫或狗)的边界框,你会怎么做?
在你思考的同时,我也会说出我的想法。最简单的方法可能是这样的:你随机取一个矩形框,它可能框住一些物体,有时是背景,有时是物体的一部分。




然后,假设你尝试进行一个两类分类,其中一类是汽车,另一类是卡车。更准确地说,你将这个随机生成的矩形区域内的像素子集分类为 C + 1 个类别中的一个(C个物体类别 + 1个背景类)。例如,一类对应汽车,一类对应卡车,另一类对应背景。
如果你有AlexNet,你可以轻松地对这个区域进行分类。显然,AlexNet可能会说这既不是汽车也不是卡车,而很容易将其分类为背景。这样,你随机搜索了一个矩形区域,并以很高的确定性发现它既不是卡车也不是汽车,而是背景。
但是,请思考一下,这样的矩形框有多少种可能性?假设图像的实际宽度是 W,实际高度是 H。

再假设你的小矩形框的宽度是 w,高度是 h。


那么,你基本上可以将这个矩形框放在很多位置。在不改变宽度和高度本身的情况下,同一个矩形框可以放在这里、那里,可以向上移动,可以放在任何地方。

实际上,在宽度方向上,你有 (W - w + 1) 个可能的位置来放置这个矩形框。


同样地,在高度方向上,你有 (H - h + 1) 个可能的位置。


由于沿x轴和y轴的放置是相互独立的,你可以简单地将这两个数相乘。因此,你有 (W - w + 1) * (H - h + 1) 种方式将这个矩形框放置在图像的任意位置。




但这还不是全部。矩形框本身的宽度和高度也有无数种可能。宽度可以在 1 像素到 W 像素之间,高度可以在 1 像素到 H 像素之间,因为图像的最大高度是H,最大宽度是W。




现在,如果你看看像这样可能的总矩形框数量,它是巨大的。这里我做了一个计算:假设你的图像宽度是224像素,高度是224像素,你现在面对的是近 6.35亿 个框。6.35亿个矩形框。
你的目标物体可能就在这些框里。所以基本上,你现在要对这6.35亿个框进行二分类(实际上是三分类:卡车、汽车或背景)。这效率太低了,因为搜索所有6.35亿个框没有任何意义。
那么,下一个最好的办法是什么?你只搜索一部分框,只考虑那些你认为可能有目标物体的框,对吗?这些框被称为提议区域。
所以,假设我神奇地告诉你:在这张汽车图片中,不要看所有的矩形框。你的目标物体很可能在这个矩形框里,或那个,或那个。我为你提出了有限数量的矩形框,比如2000个,而不是6亿个。我告诉你,这些提议区域有更高的可能性找到物体。现在,你只需要检查这2000个矩形框,你的计算量就变得合理多了。
这正是R-CNN所做的。“R”代表区域。你不是查看整个图像,而只是查看图像中一些提议区域,以判断感兴趣物体是否存在。

现在,希望你能理解搜索所有可能性与搜索部分可能性之间的区别。

R-CNN的工作流程
首先,你有一张图片。这张图片可能不是正方形的,但我们在R-CNN架构中使用的大多数CNN都需要正方形输入。

因此,这张RGB图像将被转换为正方形。例如,如果它是224x440,它将被裁剪或重塑为224x224。此外,因为存在红色、绿色和蓝色通道,所以最终输入维度是 224 x 224 x 3。正如你所知,图像由像素值表示。红色通道将有224x224个值,每个像素值范围从0到255,绿色和蓝色通道也是如此。





然后你做什么?然后你执行一个步骤,以某种方式神奇地获得那些提议的矩形区域,也就是那2000个矩形框。
那么,如何获得这些区域呢?为此,有一些基于手工设计逻辑的算法。




在识别这2000个潜在候选区域时,没有涉及深度学习。你看的是具有相似像素值的像素簇。


例如,这里的所有像素都有相似的颜色值,比如某种棕色或灰色。所有这些像素可能有一些绿色值。仅仅通过观察可能具有相似颜色的像素簇,你就可以识别或提议一些区域,对吗?还有其他方法,比如利用像素值来定义物体的几何形状。


本节总结

在本节中,我们一起探讨了目标检测的基础挑战:从海量的可能区域中定位物体是极其低效的。由此,我们引入了区域提议的核心概念,即先通过一些快速算法(非深度学习)生成少量可能包含物体的候选框,从而将搜索空间从数亿个减少到数千个。这正是R-CNN(Region-based Convolutional Neural Network)名称中“R”的由来,也是其高效性的关键第一步。下一节,我们将具体看看R-CNN如何利用这些提议区域进行精确的分类和定位。
023:Mask R-CNN图像分割入门 🎯
在本节课中,我们将学习图像分割,特别是使用Mask R-CNN模型。我们将回顾目标检测与分割的区别,了解两种主要的分割技术,并深入探讨Mask R-CNN的工作原理。
目标检测与图像分割的区别
上一节我们回顾了R-CNN系列模型,本节中我们来看看目标检测与图像分割的核心区别。
目标检测旨在绘制一个尽可能紧密包围感兴趣物体的边界矩形框。而图像分割则更进一步,它关注的是构成该物体的精确像素。

以下是分类、检测与分割的层级关系:
- 图像分类:应用于整张图像,将图像整体归类为某个类别。
- 目标检测:在分类基础上,进一步定位物体在图像中的位置,通常以边界框表示。
- 图像分割:在检测基础上,精确识别出属于该物体的每一个像素。

两种图像分割技术



在深入学习Mask R-CNN之前,我们需要了解两种主要的图像分割技术:语义分割和实例分割。


以下是两者的核心区别:
- 实例分割:为图像中的每个独立物体实例分配一个唯一的标识(通常用不同颜色表示)。即使多个物体属于同一类别(例如三个人),它们也会被区分开来。
- 语义分割:对图像进行像素级分类,每个像素都被标记为所属的类别。属于同一类别的所有物体会被归为一类,并用相同颜色表示,不区分个体实例。



两种技术各有应用场景。例如,检测管道裂纹时,可能更关心缺陷区域的总面积,此时语义分割更合适。而需要计数或跟踪独立物体时,实例分割则是更好的选择。Mask R-CNN主要实现的是实例分割。
R-CNN系列模型回顾



为了理解Mask R-CNN的改进,我们快速回顾一下其前身模型。


以下是R-CNN系列模型的演进流程:
- R-CNN:
- 输入图像。
- 使用选择性搜索(非深度学习算法)生成约2000个候选区域。
- 将每个候选区域变形为固定尺寸(如224x224)。
- 使用预训练CNN(如AlexNet)提取特征向量。
- 使用SVM进行类别分类,并使用回归模型精修边界框坐标。
- 缺点:对每个候选区域单独进行CNN前向传播,计算效率低;使用SVM而非更高效的Softmax。
- Fast R-CNN:
- 改进:先将整张图像输入CNN(如VGG16)生成特征图。
- 仍使用选择性搜索获得候选区域,但将这些区域映射到上一步生成的特征图上,得到“感兴趣区域”。
- 通过全连接层处理这些区域特征,最后使用Softmax进行分类和边界框回归。
- 优点:只需对整图做一次CNN前向传播,大幅提升效率;采用Softmax。
- Faster R-CNN:
- 关键改进:用区域提议网络 替代了耗时的选择性搜索算法。
- RPN是一个小型CNN,直接对特征图进行滑动扫描,高效生成高质量的候选区域。
- 实现了端到端的训练,速度和精度进一步提升。





Mask R-CNN详解
现在,让我们进入本节课的核心内容——Mask R-CNN。它是在Faster R-CNN基础上的扩展,增加了一个用于预测物体掩码的分支。
Mask R-CNN在Faster R-CNN的两个输出分支(类别分类和边界框回归)之外,并行增加了第三个分支:掩码预测分支。这个分支为每个感兴趣区域输出一个二进制掩码,精确勾勒出物体的轮廓。
其核心改进在于RoIAlign层。Faster R-CNN使用的RoIPooling在将区域特征对齐到固定尺寸时,会进行量化操作(取整),导致特征图与原始图像之间出现微小的错位。对于像素级精度的分割任务,这种错位是不可接受的。RoIAlign通过双线性插值避免了量化,精确保留了空间位置信息,这是实现高精度分割的关键。


以下是Mask R-CNN的基本工作流程:
- 输入图像通过骨干网络(如ResNet-FPN)提取特征图。
- 区域提议网络在特征图上生成候选区域。
- 通过RoIAlign层将每个候选区域对应的特征图区域转换为固定尺寸。
- 固定尺寸的特征同时送入三个并行分支:
- 分类分支:预测物体类别。
- 边界框回归分支:精修边界框坐标。
- 掩码预测分支:为每个类别预测一个小的二进制掩码(通常为28x28),之后上采样回原ROI尺寸。训练时,只使用对应真实类别的掩码计算损失。

总结


本节课中我们一起学习了图像分割的基础知识。我们首先区分了目标检测与像素级分割的不同。然后,我们了解了语义分割与实例分割这两种技术及其应用场景。接着,我们回顾了R-CNN、Fast R-CNN和Faster R-CNN的演进过程,理解了它们在效率与架构上的改进。最后,我们深入探讨了Mask R-CNN模型,它通过增加一个掩码预测分支并引入RoIAlign层,在Faster R-CNN强大的检测能力基础上,实现了精确的实例分割。
024:改变分割领域的2015年模型 🔬
在本节课中,我们将学习UNet架构。UNet是一种出色的架构,在生成式AI,特别是图像生成应用中也会遇到。它是一个更广泛的架构,最初应用于生物医学成像,后来完全转变为扩散模型等技术的支柱之一。今天我们将深入探讨UNet,详细讨论其工作原理的理论,最后像往常一样,我们将为特定数据集实现UNet,训练它,并尝试进行推理。
UNet用于语义分割,因此我首先会简要讨论一下本讲座中讨论的语义分割与之前在使用Mask R-CNN时研究的实例分割之间的区别。让我们开始吧。
语义分割与实例分割
在语义分割中,基本思想是图像中的每一个像素都有一个类别。如果一张图像有224x224像素,那么每个像素都会被分配一个类别。如果只是检测一个物体,比如图像中有一辆车,那么每个像素要么是车的概率,要么是背景的概率,这就像一个二元分类。
语义分割最终得到的输出与我们在Mask R-CNN中讨论的掩码有很多相似之处。例如,如果我们观察的物体是一辆车,我们将得到一个看起来像掩码1和掩码2的输出。这就像输出有两个通道,第一个通道是掩码1,第二个通道是掩码2。在掩码1中,物体以外的所有部分都是白色,物体本身是深色。在掩码2中,你会得到物体本身的掩码,这意味着背景不会被高亮显示,而是物体会被高亮显示。
我们还将讨论在这种情况下如何精确计算损失,因为在语义分割中,基本思想是构建一个掩码,使其形状与感兴趣物体的形状完全相同。在讨论UNet架构之前,“语义”这个词意味着,如果你在看“苹果”这个词,或者在看一张苹果的图片,或者在想象一个苹果,最终这些事物都指向同一个含义,只是它们以不同的方式表示:一种是文本,另一种是图像,另一种是你大脑中的某种表征。因此,语义分割意味着你从对象含义的角度来看待它,比如所有这些像素可能都属于“汽车”这个类别,并且你不区分汽车1、汽车2等。只要一个像素是汽车,在叠加掩码时,它就会被分配一个类别或一种颜色。


现在,让我们将分割分为实例分割和语义分割。这张图很好地捕捉了这一点。请看我们在上一讲中做的实例分割:如果这是输入图像,你基本上有四个物体:三只羊和一只狗。但只有两个类别:狗和羊。在实例分割中,我们尝试在每个物体实例周围绘制边界框,并可以定义唯一的实例ID。如果使用Mask R-CNN,我们还会生成像这样的掩码。这里发生的情况是,每个物体都被单独确定,但它们属于同一类别。所以这就像羊1、羊2、羊3。我们在这里显示的三种不同颜色代表了这样一个想法:尽管这三个物体属于同一类别,但它们是该类别的三个不同实例,而狗是完全不同的类别,显然也是一个完全不同的实例。



在语义分割中,情况非常不同。你不在乎不同的实例,只在乎它是否属于A类或B类。
语义分割的应用场景


你可能会想,这在哪里有用?在展示用例之前,我想问:你认为语义分割实际上可能在哪里有用?因为看起来实例分割更好,因为它不仅能区分类别,还能区分同一类别内的不同实例。那么,为什么你认为语义分割可能有用?在什么类型的应用中?想想看,例如,你有一张大脑或其他器官的MRI图像,你想区分所有癌变的组织。你不在乎是组织1还是组织2,你只希望所有癌变组织在图像中以一种颜色分割,所有其他正常组织以另一种颜色显示。你不在乎分离这些实例,只在乎它是否癌变。这是语义分割有用的一个例子。
另一个例子是,假设我们有一个管道,管道中有裂缝或缺陷,或者任何有缺陷的制造产品。你不在乎逐个分离这些缺陷实例,你只想知道在X百分比的总面积上,有多少缺陷存在。在这种情况下,语义分割也更有用。
以下是语义分割的一些用例:
- 如果你有一张航空地图或卫星地图,就像有人在聊天中提到的,如果你只是想调查土地,并且只关心将河流区域、农业区域与居民区等分割成地图上的不同颜色,那么语义分割就很有用。你不在乎识别单个房屋,也不在乎识别单个农田,你只关心将整个农田映射到一个单一的类别。
- 在农业中,如果你想简单地区分作物和杂草,这也非常相关。例如,我之前展示的Blue River公司,他们在检测到杂草时喷洒,在检测到作物时不喷洒。在那里,如果是基于图像的分割,你也不在乎单独识别出每株杂草,你只想知道有多少杂草。
因此,两者都有许多不同的用例。并不是说一个比另一个优越,只是用例完全不同。例如,在肿瘤检测中,将肿瘤分离成不同的ID并不关键,但你希望整体了解它;或者在航空或卫星成像中也是如此。
UNet架构简介

UNet的全部目的是执行语义分割。默认情况下,它不执行实例分割。因此,从这个意义上说,它与Mask R-CNN所做的非常不同。在我看来,UNet架构要优雅得多,你很快就会明白为什么。它更优雅、更简单,并且具有与各种生成式AI模型完全相同的架构。
我现在展示的这张图就是UNet架构,我们将深入探讨它。首先,UNet之所以如此命名,是因为它的形状像一个“U”。现在,这可以分成两部分:如果我画一条这样的线,这里的一切是编码器部分,这里的一切是解码器部分。编码器部分负责从输入图像中提取特征,而解码器部分负责将这些特征上采样回原始图像大小,以生成分割掩码。

在接下来的部分中,我们将详细讨论编码器和解码器的每个组件,包括卷积层、池化层和上采样操作。我们还将讨论跳跃连接,这是UNet的一个关键特性,它允许将编码器中的特征图与解码器中相应层的特征图连接起来,从而帮助保留空间信息。
最后,我们将讨论损失函数,通常是用于分割任务的交叉熵损失或Dice损失,并展示如何训练UNet模型。我们还将简要介绍如何对训练好的模型进行推理,以生成新的分割掩码。

通过本课程的学习,你将能够理解UNet的工作原理,并能够自己实现和训练一个UNet模型用于语义分割任务。
025:UNet++、残差UNet与注意力UNet
在本节课中,我们将继续深入学习UNet及其几种变体。我们将专注于图像分割任务,并动手为两个不同的数据集编写UNet模型代码。此外,我们还会将训练好的模型权重发布到Hugging Face平台,以便任何人都能使用该模型进行推理。如果你还不熟悉Hugging Face,我们也会进行简要介绍。
课程概述
上一节课我们详细探讨了UNet的架构。本节课,我们将首先快速回顾UNet的核心结构,然后简要介绍三种重要的UNet变体:UNet++、注意力UNet和残差UNet。尽管原始的UNet论文(2015年发表)在引用量和影响力上远超这些变体,但了解这些改进有助于我们理解架构设计的演进思路。最后,我们将进入实践环节,编写UNet模型代码。
UNet架构回顾
首先,让我们快速回顾一下标准UNet的架构。UNet主要由四个部分组成,其结构高度对称。
- 编码器:位于架构左侧。它接收输入图像(例如灰度图),并经过一系列卷积和最大池化操作。卷积操作(通常无填充)会逐步减小图像的空间尺寸(每次约减少2个像素),而池化操作则直接将空间维度减半。编码器的目标是提取并压缩特征,使信息变得尽可能抽象。
- 瓶颈层:位于编码器末端,是特征图空间尺寸最小、通道数最多的层。例如,输入从572x572开始,到达此处可能变为30x30,但拥有1024个通道。
- 解码器:位于架构右侧。它通过上采样和卷积操作,逐步将特征图的空间尺寸恢复至原始输入大小。
- 跳跃连接:这是UNet的关键。它们将编码器每一层的特征图直接与解码器对应层的特征图进行拼接。这确保了在抽象化过程中丢失的细节信息(如物体边界)能够传递到解码器,帮助生成更精确的分割结果。
标准UNet通常包含四个层级(不包括瓶颈层),每个层级在编码器和解码器中都有对应结构,并通过跳跃连接相连。这种编码器-解码器加跳跃连接的架构,后来也被广泛应用于生成式AI模型(如Stable Diffusion)中。
UNet变体简介
接下来,我们简要看看三种UNet的改进架构。它们主要围绕跳跃连接和特征融合方式进行创新。
UNet++
UNet++ 的主要改进在于跳跃连接的设计。在标准UNet中,跳跃连接是直接的、无处理的。
而在UNet++中,编码器和解码器对应层之间增加了密集的连接块(图中圆圈表示),这些块包含卷积等操作。
核心差异:
- UNet++的跳跃连接路径上存在中间卷积层,对编码器传来的特征进行进一步处理,而非直接传递。
- 网络结构更加密集,形成了类似网格的连接。
优势与代价:
- 优势:更密集的连接使得梯度在反向传播时路径更多,可能有助于缓解梯度消失问题,并在某些任务(如生物医学图像分割)上获得更高精度。
- 代价:由于参数和计算量增加,训练速度会更慢。
输出与推理:
在训练时,UNet++会利用图中所有绿色节点(包括中间跳跃连接路径的输出)进行深度监督,即每个节点的输出都会与真实分割掩码计算损失,共同优化模型。
在推理(预测)时,通常只使用最右侧解码器末端(即图中 X<sup>0,4</sup>)的输出作为最终的分割结果。
残差UNet
从名称可知,残差UNet引入了类似ResNet的残差连接思想。
在ResNet中,一个残差块学习的是输入x与目标映射H(x)之间的残差F(x) = H(x) - x,其输出为 F(x) + x。这种结构让网络可以轻松地学习恒等映射,极大地缓解了深层网络中的梯度消失问题。
在UNet中的应用:
残差UNet将这种思想融入UNet的块结构中。它可能修改了编码器和解码器中的基础卷积块,使其变为残差块。同时,跳跃连接的处理方式也可能从拼接变为逐元素相加,这要求编码器和解码器对应层的特征图通道数必须相同。
核心思想:通过引入残差连接,使得网络更容易训练,尤其对于非常深的UNet变体,有助于保持梯度的有效流动。
注意力UNet
注意力UNet的核心是在跳跃连接中引入注意力机制。
在标准UNet中,编码器的所有特征都通过跳跃连接同等地传递给解码器。然而,并非所有编码器特征都对解码器当前要重建的区域同等重要。

注意力门机制:
注意力UNet在跳跃连接路径上添加了一个“注意力门”。该门接收来自解码器上一层的特征(作为查询,Query)和来自编码器的对应层特征(作为键和值,Key/Value)。
通过计算,它会生成一个注意力权重图,该图会突出显示编码器特征中对解码器当前任务更重要的空间位置,并抑制不相关或噪声区域的特征。然后,用这个权重图对编码器特征进行加权,再传递给解码器。
主要目的:让模型在融合特征时能够“聚焦”于更相关的区域,从而可能提升分割边界精度,并减少无关背景信息的干扰。

总结
本节课我们一起回顾了UNet的经典编码器-解码器架构及其关键的跳跃连接。随后,我们探讨了三种重要的UNet变体:
- UNet++:通过密集的跳跃连接和深度监督,构建了更复杂、连接更丰富的网络,可能提升精度但增加计算成本。
- 残差UNet:将ResNet的残差学习思想引入UNet,旨在改善深层网络的训练稳定性和梯度流动。
- 注意力UNet:在跳跃连接中引入注意力机制,使模型能够自适应地关注与当前解码任务最相关的编码器特征区域。

这些变体都是在原始UNet强大基础之上的有益探索,它们针对特征融合、梯度传播或特征选择等不同方面进行了优化。理解这些设计思路,将帮助我们在面对具体分割任务时,更好地选择或设计合适的模型架构。在接下来的实践环节,我们将动手实现一个标准的UNet模型。
026:YOLO算法初探 🎯
在本节课中,我们将学习YOLO(You Only Look Once)算法。YOLO是我个人最喜欢的物体检测模型之一。在本计算机视觉训练营中,我们已经介绍过包括R-CNN及其变体在内的几种模型,并讨论了物体检测、实例分割与语义分割的区别。我们还曾使用U-Net实现过分割。YOLO则非常不同,在我看来,它的架构要简单得多,并且能够以近乎实时的速度和相当不错的准确率处理物体。我们曾在早期关于OpenCV的课程中实现过YOLO V8的nano模型。今天,我们将深入探讨YOLO的工作原理理论,以及如何为特定应用实现它。
什么是实时物体检测?⚡
所谓实时物体检测,是指如果我们的模型能够每秒处理大约24到30帧图像,并在这些帧中检测出物体,那么我们就可以称其为实时物体检测模型。YOLO存在不同的版本,我们之前使用的是V8版本。今天我们将讨论原始YOLO架构的结构。当然,YOLO模型架构本身也存在变体。有些模型无法处理24到30帧,每秒只能处理大约15帧;而有些模型每秒可以处理大约155帧,这些才是能够进行实时物体检测的模型。
YOLO可以用于视频监控。你可能在网上看到过不同公司关于物理安全系统的视频,其中检测到有人进入私人领地并实时反馈给计算机系统,扬声器发出警报。YOLO也可以用于自动驾驶汽车,当然,自动驾驶汽车不仅仅依赖YOLO,但YOLO可以用于一些基本的物体检测和标注。在疫情期间,我曾读到过新闻,人们实现了基于YOLO的实时人群监控系统。不过,YOLO在检测拥挤图像或帧中的大量人群时也存在某些局限性,我们也会在今天的课程中讨论。
“只看一次”的含义 🤔
你可能会自然产生一个疑问:这里说“只看一次”,这是否意味着其他架构需要看图像不止一次?是的,它们确实如此。我们也会简要提及这一点,这在之前的R-CNN课程中已经讨论过。
YOLO的基本目标是用一种称为边界矩形的东西来标注图像中的物体。这些边界矩形给出了物体在整个图像中的局部位置。物体本身会有类别,如人类、狗、柱子,并且会有一个与之相关的置信度分数。
那么,YOLO究竟是如何构建这个矩形的?它如何决定某物是人、狗还是马?它又是如何构建置信度分数的?我们也将深入探讨这些内容。


YOLO的起源与影响 📜
原始的YOLO论文堪称传奇,目前已被引用超过65,000次。论文的第一作者是Joseph Redmon。可以说,自YOLO被引入以来,这篇论文彻底改变了物体检测的实现方式。现在甚至有更现代的YOLO版本,例如V11,我想V12也已经发布,但一些最新的YOLO模型没有官方论文。有一家名为Ultralytics的公司,我们在之前的课程中简要讨论过,该公司提供了许多不同的预训练YOLO模型,可用于物体检测。我相信还有其他非官方的YOLO模型,预训练于各种标注的图像类别。
性能对比:速度与精度 ⚖️
现在,这个表格展示了YOLO的速度和准确性与其他一些模型的比较。首先,我们来看这两个指标:mAP和FPS。FPS就是每秒帧数,表示该模型每秒可以处理多少视频帧。如果FPS大于24,就可以称之为实时。所有被标记为“低于实时”的模型,其每秒帧数通常都小于24。而这里列出的所有模型都具有更高的每秒帧数处理能力。
mAP代表平均精度均值。你可能会想,既然有“平均”这个词,为什么还需要“均值”?平均精度就是在不同召回率值下得到的精度。我不想深入mAP的细节,只是为好奇的观众简单说明:你可以计算模型在不同召回率下的精度,然后取平均值,这就是平均精度。如果你的图像中有多个类别,如狗、猫、马、人,你可以取所有不同类别的平均精度的均值,这个值就叫做平均精度均值。这个数字表明你的模型在平均意义上以良好准确率检测各种物体的能力。你可以看到YOLO的准确率相当不错,达到63.4。这个截图来自原始的YOLO论文。你也可以将其与其他模型进行比较。在这里,你可以看到R-CNN、Fast R-CNN、Faster R-CNN这些我们之前讨论过的模型。事实上,你会发现Faster R-CNN的准确率比YOLO略高,但它们的处理速度都极慢,每秒只能处理0.5或7帧。
因此,YOLO的主要优势在于,你可以在不牺牲太多准确性的情况下,获得近乎实时的处理速度。请记住这一点:当考虑YOLO的用例时,就是在需要速度且不想过多牺牲准确性的场景。
YOLO之前的技术 🔄
在实现YOLO(只看一次)之前,人们确实需要看不止一次。具体是如何实现的呢?主要有两种不同类型的技术。
最原始的技术是使用滑动窗口检测器。假设我们有一个人的图像,目标是构建一个能识别照片中人类的检测器或滤波器。我们可以构建不同类型的滤波器,当以图像形式绘制时,这些滤波器可以查看人体形状的各种抽象特征。通过将这个滤波器在图像的整个区域上移动,可以突出显示某些区域,这些区域存在或不存在人的概率非常高。这是过去人们为提取可能找到人、猫或其他物体的局部区域而构建手工特征图的一种方式。显然,这不涉及任何深度学习,这不是基于深度学习的技术,更像是手工工程。但我们过去详细讨论过的CNN则非常不同。

让我提醒你一下,以Faster R-CNN为例。在这个整体架构中,一个称为区域提议网络的神经网络会提议大约300个矩形区域。这300个矩形可能分布在这里、那里,它会提议不同的矩形作为可能找到图像的候选区域。Faster R-CNN模型的目的是对这些矩形进行分类。
027:YOLO + OCR 车牌识别项目 🚗📸
在本节课中,我们将学习如何构建一个实用的车牌识别系统。我们将结合YOLO目标检测和OCR光学字符识别技术,从视频流中自动检测车辆并读取其车牌号码。
上一节我们介绍了YOLO的理论基础,本节我们将动手实践,构建一个完整的项目。
项目概述与架构
车牌识别系统的基本流程如下:输入一个视频,视频每秒包含约30帧图像。我们的目标是在图像中定位车牌对象,并获取其边界框。最终,我们希望输出车牌上的实际号码,例如“GX15 OGJ”。
我们将遵循以下架构:
- 使用一个预训练的YOLO模型。
- 在车牌数据上对该模型进行微调。
- 保存微调后的模型权重。
- 使用OCR技术读取边界框内的字符,并根据数据集特点添加特定约束。
以下是实现步骤的详细列表:
- 微调的必要性:我们使用的预训练YOLOv8模型基于COCO数据集训练,该数据集包含80个对象类别,但其中不包含“车牌”类别。因此,原始模型无法直接检测车牌,必须进行微调。
- 所需数据:微调需要带标注的数据。具体来说,我们需要车牌图像以及对应的边界框坐标(中心点、宽度和高度)。
- 数据来源:我们将使用Roboflow平台上的一个公开车牌数据集。
- 开发环境:模型的微调部分计算量较大,我们将在Google Colab上利用GPU加速完成。推理部分则可以在本地VS Code环境中运行。
第一步:准备数据与微调YOLO模型
现在,我们开始准备数据并微调YOLO模型。
首先,访问Roboflow平台并找到车牌数据集。选择最新版本的数据集,并选择YOLOv8格式进行下载。平台会提供一段包含API密钥的下载代码。
接着,在Google Colab中新建一个笔记本。将Roboflow提供的下载代码粘贴到单元格中并运行。这段代码会安装必要的库并将数据集下载到Colab环境中。
以下是核心代码示例:
# 安装Roboflow库(通常只需运行一次)
!pip install roboflow

# 从Roboflow下载数据集(使用您自己账户生成的代码)
from roboflow import Roboflow
rf = Roboflow(api_key="YOUR_API_KEY")
project = rf.workspace("WORKSPACE").project("PROJECT_NAME")
dataset = project.version(VERSION_NUMBER).download("yolov8")

代码运行后,数据集将被下载到指定目录。接下来,我们就可以使用这个数据集来加载预训练的YOLOv8模型并进行微调。
第二步:模型微调与保存
我们将加载预训练的YOLOv8模型,并在下载的车牌数据上进行微调。
以下是微调模型的核心代码:
from ultralytics import YOLO
# 1. 加载预训练模型(例如YOLOv8n)
model = YOLO('yolov8n.pt')
# 2. 在数据集上训练(微调)模型
results = model.train(
data=f'{dataset.location}/data.yaml', # 数据集配置文件路径
epochs=50, # 训练轮数
imgsz=640, # 输入图像尺寸
device='cuda' # 使用GPU加速
)
训练完成后,需要保存微调后的模型权重,以便后续推理使用。
# 3. 保存最佳模型权重
best_model_path = './runs/detect/train/weights/best.pt'
# 这个 best.pt 文件就是我们可以用于后续推理的模型
第三步:使用OCR读取车牌文本


获得车牌边界框后,我们需要提取框内的文本。这里使用OCR技术。我们以pytesseract库为例。
首先,需要从边界框坐标中裁剪出车牌区域图像。
import cv2
from PIL import Image
import pytesseract
# 假设 `frame` 是视频帧,`x1, y1, x2, y2` 是车牌边界框坐标
license_plate_roi = frame[y1:y2, x1:x2]
# 将OpenCV图像转换为PIL图像,供Tesseract使用
license_plate_image = Image.fromarray(cv2.cvtColor(license_plate_roi, cv2.COLOR_BGR2RGB))

# 使用Tesseract进行OCR识别
text = pytesseract.image_to_string(license_plate_image, config='--psm 8')
print(f"识别到的车牌号: {text.strip()}")
注意:实际应用中,可能需要对图像进行预处理(如灰度化、二值化、去噪)并调整Tesseract的配置参数(如--psm模式),以提高不同场景下的识别准确率。
项目总结
本节课中,我们一起构建了一个完整的车牌识别项目。我们首先解释了为何需要对预训练的YOLO模型进行微调,然后从Roboflow获取了标注数据集。接着,我们在Google Colab上完成了模型的微调并保存了权重。最后,我们介绍了如何使用OCR库从检测到的车牌区域中提取文字信息。

这个项目展示了如何将目标检测与OCR技术结合,解决一个实际的计算机视觉问题。你可以将此流程应用于其他类似的检测与识别任务中。
028:构建端到端计算机视觉流水线的最佳方式 🚀
在本节课中,我们将学习如何使用Roboflow平台,构建一个完整的端到端计算机视觉应用流水线。我们将从创建和标注数据集开始,涵盖数据预处理、模型训练与微调、性能监控,直至模型测试与部署的全过程。
Roboflow是一个功能强大的平台,它允许用户从创建数据集和标注开始,构建端到端的计算机视觉应用。平台能自动将数据分割为训练集、验证集和测试集,并支持创建数据的不同版本。用户可以选择任意模型在此数据上进行训练,也可以选择预训练模型进行微调。在微调过程中,可以通过动态图表观察模型精度随训练轮次的变化。最后,模型微调完成后,可以进行测试和部署。这是一个非常便捷的端到端流水线构建平台。
首先,我建议所有人访问Roboflow.com网站。我们将一起构建这个项目。今天的课程部分内容将专注于图像标注。由于我只有一个人,无法独自完成所有标注,因此我需要大家的帮助。我会分享链接并详细说明需要做什么。
准备工作
第一件事是使用您的Google账户或GitHub账户登录。我已经使用我的Google账户登录了。

在今天的课程中,我们计划做几件事。首先是讨论如何具体地构建和标注数据集。如果我们正在构建一个目标检测流水线,我们知道数据集可以是视频的帧,也可以是独立的图像,而标注则是边界框。

项目构思与目标设定
假设我们有一段这样的视频。我这里有一个名为“volleyball”的视频。您能看到这个视频画面吗?您能看到这个排球视频吗?
这基本上是一场随机排球比赛的片段。但请注意这些广告。假设我是一名广告商,我的目的是在球员的队服上赞助广告,或者是在场地周围的广告牌上投放广告,比如您在这些广告牌上看到的“Mikasa”品牌。
这些广告的目的是在屏幕上投影时(例如,当有人在电视上观看直播时)获得最大的屏幕可见度。因此,作为广告商,我的目标是优化,或者说,至少查看我的品牌标志是否在屏幕时间中显示了X百分比,以及我的标志占据了屏幕面积的多少。尽管这个标志在物理上很大,但当观众实际在屏幕上观看时,也许这位球员的队服标志对观众来说实际上更大。
对于广告分析来说,假设您是一家公司,客户来找您说:“嘿,我想对你们这场特定的排球比赛进行一些分析,以便分析哪种类型的标志被投影得最多。” 完成此任务的首要任务是拥有一个目标检测模型,其中的目标就是这些标志本身。因此,第一件事是,我们需要将整个视频转换为不同的帧,然后通过绘制边界矩形将这些帧转换为带标签的数据集,之后我们将使用该数据集来训练一个YOLOv8或YOLOv11模型。之后,如果我们上传一个新视频,希望我们能在新视频上获得标注。
为了简化,我们将只针对两个标志:一个是这个“Mave”标志(我希望您能看到这个红色标志),另一个是这个“Mikasa”标志。有时它会被站在前面的人挡住。我们只跟踪这两个标志,仅为了简化。
我们甚至可以进一步简化,只跟踪一个标志,也许我们应该尝试一下。但无论如何,整个流程的第一步是将视频转换为帧。您不需要外部软件来完成这个。接下来,我将在Roboflow中创建一个新项目。
在Roboflow中创建项目
点击“New Project”。这是一个目标检测项目,我将项目名称设为“volleyball_logo”。在这里,我想检测标志,我输入“logos”,输入什么并不重要。
目前我在Roboflow上使用的是免费计划,所以我认为我还不能创建私有项目,但这没关系,这反正不是一个高风险项目。
这样就创建了一个空项目。在左侧,您可以看到一系列选项:上传数据、标注数据。“数据集”指的是您已创建的带标注的数据集。“版本”是您可以创建模型的不同版本。“分析”将在模型训练过程中向您展示一些信息。我们将看到所有这些内容。第一步是上传数据。
上传并处理视频数据


我将点击并选择文件上传。然后选择这个视频。这里我正在上传数据集。首先,我要向您展示的是一个简单的流水线,这个流水线在训练中表现很好,但在测试中表现不佳。

这个视频大约有45秒。如果我每秒选取一帧,我将得到大约46-47张图像。Roboflow允许您选择任意帧率。如果您选择每秒帧数较多,总输出会非常大,反之亦然。因此,我选择每秒一帧,这样我将总共有47张图像。基于这个帧率,Roboflow现在正从这个MP4视频中提取帧,提取完成后,将上传到Roboflow服务器。
现在不用担心跟着我一起做,因为我会与大家分享这个项目工作区,以便大家都能协助我进行标注。就像我们都在同一家公司工作,有一个巨大的数据集需要处理,我们将任务拆分给每个人一样。今天我们将做完全相同的事情。一旦上传完成,我会告诉您该做什么。
与此同时,我想向您展示另一个视频。这是另一个类似YouTube短视频的视频,我不确定为什么它现在打不开了,之前是能播放的,但我们会解决的。这是一个包含一堆巧克力的视频,我想要一个算法,每当巧克力出现在图像或视频中时,能用边界矩形标注出不同的巧克力品牌。
好的,这里Roboflow正在上传图像,速度相当快。一旦上传完成,它将显示有47张图像未标注,0张已标注,这意味着我还没有开始标注。
开始数据标注

首先,我将向您展示一两个图像的样本标注,然后邀请大家一起参与标注,以便我们能快速共同完成这项工作。

请给我一点时间完成上传。
好的,上传已完成。如您所见,这里是不同的帧。到目前为止很简单。这里您可以看到几个选项:“自己标注”、“与我的团队一起标注”、“自动标注整个批次”。“自动标注”是指如果您已经有一个训练好的模型可以预测此案例的边界矩形,您可以使用自动标注器。否则,这里有一个选项是“雇佣外包标注员”,Roboflow本身会以一定的费用指派人员来标注我们的数据集。

我最初选择的是“自己标注”。这里我正在创建一个标注任务。最初,您可以看到所有图像都被分配为训练数据集。点击任意图像,这将单独打开该图像。
我们不需要做太多,只需要做以下操作:您看到这个矩形选项,只需在标志周围绘制一个矩形。假设我只对识别这个“Mave”标志感兴趣,不关心其他标志。然后在这里,我将其命名为“logo”,这个默认名称就足够了。现在,我只需保存。

本节课总结
在本节课中,我们一起学习了如何使用Roboflow平台启动一个端到端的计算机视觉项目。我们从项目构思开始,明确了为排球比赛视频中的广告标志构建目标检测模型的目标。接着,我们在Roboflow上创建了新项目,上传了视频并将其自动提取为图像帧,为后续的标注工作做好了准备。最后,我们初步了解了Roboflow的标注界面,并完成了第一张图像的标注示例。在接下来的课程中,我们将继续进行数据标注、数据集版本管理以及模型训练等步骤。
029:构建并部署跌倒检测模型
在本节课中,我们将学习如何利用Roboflow平台上的现有模型,构建一个完整的计算机视觉应用,并将其部署为可供任何人使用的网络服务。我们将以“跌倒检测”这一具体项目为例,展示从模型选择、本地推理到应用部署的完整流程。
上一讲我们简要介绍了Roboflow及其功能,并尝试了协作标注图像。本节中,我们将直接使用一个预训练好的模型,并专注于如何将其部署成一个实用的应用程序。
探索Roboflow Universe
首先,我想向大家展示Roboflow一个非常强大的功能——Roboflow Universe。访问 universe.roboflow.com 网站,向下滚动,你会看到一个名为“热门类别”的区域。
以下是该平台的核心特点:
- 领域广泛:点击“查看全部”,你可以看到涵盖几乎所有能想象到的领域的计算机视觉数据集和模型。
- 公开资源:例如,点击“航空”领域,它会展示由其他用户创建并公开的已标注数据集和模型。
- 数据与模型一体:许多条目不仅包含数据集,还附带了训练好的模型。你可以直接使用这些模型进行推理。
以这个“能源航空数据集”为例,它可能包含关于油田、变电站、风力发电机等的图像。页面会显示数据集的原始图像数量,这些图像会经过随机旋转、裁剪等增强处理,因此实际的训练数据量会远大于此。
关键点在于,这里提供了模型。点击模型,你可以看到数据集和已有的标注(例如,绝缘子的分类:破损 vs 正常)。对于预训练模型,你可以直接使用它:上传你的图片或视频,模型就会给出预测结果,包括边界框的坐标(x, y, 宽度, 高度)、物体类别以及置信度。
本节实践项目:跌倒检测
在今天的第二部分,我们将动手构建一个具体的项目:老年人跌倒检测。对于独居或行动不便的老年人,跌倒是一个重大风险,可能导致髋部骨折等严重伤害,甚至危及生命。如果事发地点有监控摄像头,我们可以构建实时检测系统,在跌倒发生时立即预警。
首先,我来展示一下我们将要构建的应用成品。这是一个基于Streamlit搭建的公开网页应用。


该应用允许用户设置一个置信度阈值,用于决定显示哪些预测结果。用户可以选择上传视频文件。上传后,应用会逐帧处理视频(由于模型托管在Roboflow,处理并非严格实时,速度稍慢)。模型会识别视频中的人物,并用边界框标出,同时预测其状态属于“站立”还是“跌倒”类别,并附上置信度分数。
当视频中的人物滑倒时,模型能够检测到“跌倒”事件。处理完成后,用户可以下载带有标注框的已处理视频,方便查看和演示。

技术实现方案
我们将使用以下工具来构建这个应用:
- Roboflow:用于寻找带有标注的跌倒检测数据集及其预训练模型。
- 本地推理脚本:我们将通过Roboflow提供的API调用模型,并在本地(例如使用Visual Studio Code)编写代码进行初步推理测试。这能帮助我们在部署前确保一切运行正常。
- Streamlit:一个能快速将Python脚本转化为交互式Web应用的框架。我们将把本地脚本转换成Streamlit应用,并公开部署,使其真正可用。
接下来,我将逐步演示如何操作。

第一步:在Roboflow Universe中找到模型
首先,访问 universe.roboflow.com。在搜索框中输入“fall detection”。
在搜索结果中,你会发现不同用户创建的项目,其图像数量各异。我们将使用一个包含约7,8100张图像(增强后会更多)的数据集所对应的预训练模型。

这个模型已经训练完成,你可以看到它的一些预测示例。虽然当前演示中标签似乎有误(将站立预测为跌倒,反之亦然),但模型的评估指标(如平均精度)表现良好,且数据集规模较大,因此模型本身是可靠的。我们稍后会看到如何修正这个显示问题。
如何使用这个模型?
我们可以直接在Roboflow页面上使用该模型进行单张图片的测试。但我们的目标是构建一个能处理视频的独立应用。因此,我们需要通过编程方式调用它。

本节课中我们一起学习了如何利用Roboflow Universe的丰富资源,快速定位并获取预训练模型。我们明确了构建一个跌倒检测应用的目标,并规划了通过Roboflow API进行本地推理,再使用Streamlit进行部署的技术路线。在接下来的步骤中,我们将具体实现代码编写和应用部署。

浙公网安备 33010602011771号