1 图像分类任务与语义鸿沟

1.1 图像分类的基本形式

图像分类(Image Classification) 是计算机视觉中的核心任务之一。给定一张输入图像和一组候选类别,例如 dog、cat、truck、plane,分类器需要输出图像所属的类别。这个任务看起来直观,因为人类可以很快认出一张图片里的猫、车或飞机;但对计算机来说,图像本质上只是一个由整数构成的张量。

例如一张 \(800 \times 600\) 的 RGB 图像可以表示为大小为 \(800 \times 600 \times 3\) 的数组,其中每个像素通道通常是 \([0,255]\) 之间的整数。分类器真正看到的是这些数字,而不是“猫的耳朵”“车轮”“飞机机翼”这样的语义对象。

语义鸿沟(Semantic Gap) 指的是低层像素数值和高层语义概念之间的巨大差距。图像分类的困难主要来自:模型必须从原始像素中学习出对类别有用的表示,而这些表示不能简单靠人工规则硬编码出来。

1.2 图像分类的典型挑战

图像分类难,不是因为类别名字复杂,而是因为同一类别的像素外观可能变化极大,不同类别也可能在局部像素上非常相似。

挑战 含义 为什么困难
视角变化(Viewpoint Variation) 相机位置或物体姿态改变 同一个物体的所有像素都会随视角变化而改变
光照变化(Illumination) 光源方向、强度、颜色不同 像素亮度和颜色变化不一定代表类别变化
背景杂乱(Background Clutter) 目标处在复杂背景中 模型需要区分目标和无关背景
遮挡(Occlusion) 目标被其他物体部分挡住 分类器只能看到部分证据
形变(Deformation) 同类物体形状可变 例如动物姿态、人体动作、柔性物体都会改变外观
类内差异(Intraclass Variation) 同一类别内部差异很大 “狗”包含很多品种、颜色和姿态
上下文(Context) 周围场景影响理解 上下文有时有帮助,有时会误导模型

考试重点:图像分类不是把像素和标签做一一对应的记忆问题,而是要学习对上述变化具有鲁棒性的决策规则。

2 数据驱动的图像分类方法

2.1 为什么不能手写规则

如果任务是排序一组数字,可以直接写出明确算法;但“识别猫”没有显而易见的硬编码规则。早期视觉算法尝试先检测边缘、角点等低层结构,再组合成高层对象。边缘和角点确实有用,但很难靠人工规则覆盖现实图像中的所有变化。

现代图像分类通常采用数据驱动方法(Data-Driven Approach)

  1. 收集带标签的数据集,例如许多图像及其类别标签。
  2. 使用机器学习算法从数据中训练分类器。
  3. 在未见过的新图像上评估分类器性能。

这个流程的关键变化是:分类规则不再由人手工完整写出,而是由模型根据训练数据自动学习。

2.2 训练集、验证集与测试集的角色

机器学习分类器通常会接触三类数据:

数据划分 用途 注意事项
训练集(Training Set) 学习模型参数或记忆样本 可以反复使用
验证集(Validation Set) 选择超参数和模型设计 用于开发阶段的模型选择
测试集(Test Set) 最终评估泛化性能 只能在最后使用一次

测试集不能用来调参。如果根据测试集表现反复选择模型,就等价于把测试集信息泄漏进训练流程,最终测试准确率会过于乐观,不能代表模型对新数据的真实表现。

3 最近邻分类器(Nearest Neighbor)

3.1 基本思想

最近邻分类器(Nearest Neighbor Classifier) 是最简单的数据驱动分类器。它的训练阶段几乎什么都不做,只是把所有训练图像和标签存下来;预测时,对于一张测试图像,在训练集中找到最相似的一张图像,然后直接复制它的标签。

形式化地说,给定测试图像 \(x\) 和训练样本 \(\{(x_i,y_i)\}_{i=1}^{N}\),最近邻分类器先计算 \(x\) 与每个 \(x_i\) 的距离,再选择距离最小的样本:

\[ i^* = \arg\min_i d(x,x_i) \]

预测标签为:

\[ \hat{y}=y_{i^*} \]

3.2 训练和预测复杂度

最近邻分类器的时间开销非常不平衡:

阶段 复杂度 原因
训练 \(O(1)\) 或近似 \(O(N)\) 存储 只需要记住训练数据
预测 \(O(N)\) 每个测试样本都要和所有训练样本比较

这和实际需求正好相反。很多应用可以接受较慢的离线训练,但希望上线后的预测非常快。最近邻方法预测时需要扫描大量训练样本,因此不适合作为大规模图像分类的主力方法。

近似最近邻搜索(Approximate Nearest Neighbor)可以缓解这个问题,例如 FAISS 这类系统能做大规模向量检索;但在 CS231n 的图像分类主线中,最近邻主要用于说明数据驱动方法、距离度量和超参数选择。

4 K 近邻、距离度量与超参数选择

4.1 从 1-NN 到 KNN

K 近邻(K-Nearest Neighbors, KNN) 对最近邻方法做了一个自然扩展:预测时不只看最近的一个训练样本,而是找出距离测试图像最近的 \(K\) 个训练样本,并通过多数投票决定标签。

\(K=1\) 时,KNN 就退化为普通最近邻分类器。增大 \(K\) 往往能减少单个异常样本造成的影响,使决策边界更平滑;但 \(K\) 太大也可能把过多无关样本纳入投票,导致分类边界过于粗糙。

4.2 L1 距离与 L2 距离

图像通常被展平成向量后比较距离。常见距离度量包括 L1 距离(Manhattan Distance)L2 距离(Euclidean Distance)

给定两个向量 \(x,y \in \mathbb{R}^D\)

\[ d_1(x,y)=\sum_{k=1}^{D}|x_k-y_k| \]

\[ d_2(x,y)=\sqrt{\sum_{k=1}^{D}(x_k-y_k)^2} \]

L1 距离像在方格城市中沿街道移动,距离由各坐标差的绝对值相加得到。L2 距离是直线距离,更强调较大的单维差异,因为平方会放大大偏差。

例如在二维平面中,\(O=(0,0)\)\(A=(1,0)\)\(B=(\frac{1}{\sqrt{2}},\frac{1}{\sqrt{2}})\)

\[ d_2(O,A)=1,\quad d_2(O,B)=1 \]

但 L1 距离中:

\[ d_1(O,A)=1,\quad d_1(O,B)=\sqrt{2} \]

因此,同一组点在不同距离度量下可能产生不同的最近邻和不同的分类边界

4.3 超参数

KNN 中的 \(K\) 和距离度量都属于超参数(Hyperparameters)。超参数不是通过训练过程直接学出来的模型参数,而是算法设计中的选择,例如:

  • 使用 \(K=1,3,5\) 还是更大的 \(K\)
  • 使用 L1 距离还是 L2 距离。
  • 是否使用某种特征变换或归一化。

超参数通常高度依赖具体数据集,不能只凭直觉决定,必须通过验证集或交叉验证进行选择。

4.4 如何正确选择超参数

选择超参数时有几种常见思路:

方法 是否合理 原因
在训练集上选表现最好的超参数 不合理 例如 \(K=1\) 通常能在训练集上表现完美,但可能过拟合
在测试集上选表现最好的超参数 严重错误 测试集会被用于模型选择,最终评估失真
划分训练集、验证集、测试集 合理 用验证集调参,用测试集做最终评估
交叉验证(Cross-Validation) 小数据集上有用 多次轮换验证折,结果更稳定

交叉验证 的做法是把训练数据分成若干折,例如 5 折。每次用其中一折做验证,其余折做训练,轮换多次后平均验证结果。它在小数据集上很有价值,但在深度学习中不常频繁使用,因为训练成本太高,通常更常见的是固定验证集。

5 CIFAR-10 与 KNN 的局限

5.1 CIFAR-10 数据集

CIFAR-10 是图像分类中的经典数据集,包含 10 个类别、50,000 张训练图像和 10,000 张测试图像,每张图像大小为 \(32 \times 32 \times 3\)

它常用于教学和小规模实验,因为图像尺寸小、类别清晰、训练速度较快。对 CS231n 来说,CIFAR-10 是理解 KNN、线性分类器、损失函数和神经网络的常用例子。

5.2 为什么像素距离上的 KNN 很少用于图像分类

KNN 的关键问题在于:原始像素空间中的距离通常不等于语义相似度。例如一张原图、被遮挡后的图、整体平移 1 个像素的图、颜色轻微偏移的图,在人类看来语义几乎不变,但像素距离可能和另一张完全不同内容的图片一样大。

这说明直接比较像素有严重缺陷:

  • 对平移、旋转、光照变化非常敏感。
  • 不能理解物体结构,只是在比较数字差异。
  • 高维像素空间中,距离度量容易变得不稳定。
  • 预测需要保存并扫描训练集,部署成本高。

因此,KNN 是理解图像分类的好起点,但不是现代视觉分类器的核心方案。

6 线性分类器与参数化方法

6.1 参数化方法的基本思想

与 KNN 不同,参数化方法(Parametric Approach) 不在预测时查找整个训练集,而是学习一组参数,把输入图像直接映射为各类别的分数。

对于 CIFAR-10,一张图像展平后是 \(32 \times 32 \times 3=3072\) 维向量。线性分类器输出 10 个类别分数:

\[ f(x,W)=Wx \]

加入偏置项后:

\[ f(x,W,b)=Wx+b \]

其中:

  • \(x \in \mathbb{R}^{3072}\) 是输入图像向量。
  • \(W \in \mathbb{R}^{10 \times 3072}\) 是权重矩阵。
  • \(b \in \mathbb{R}^{10}\) 是偏置向量。
  • 输出 \(s=f(x,W,b)\in \mathbb{R}^{10}\),每个分量是一个类别分数。

预测时选择分数最高的类别:

\[ \hat{y}=\arg\max_j s_j \]

6.2 一个小型线性分类例子

假设输入图像只有 4 个像素,展平后得到向量 \(x\),分类任务有 cat、dog、ship 三类。线性分类器会为每个类别学习一行权重,将输入像素加权求和再加上偏置,得到该类别的分数。

例如:

\[ s_{\text{cat}}=w_{\text{cat}}^Tx+b_{\text{cat}} \]

每一类都有自己的权重向量。某一类的权重可以理解为一个模板:如果输入图像和该模板在像素空间中“对齐”,对应类别分数就会较高。

7 线性分类器的三种理解方式

7.1 代数视角

从代数角度看,线性分类器就是矩阵乘法加偏置:

\[ s=Wx+b \]

它把一个高维输入向量映射为类别分数向量。训练的目标是找到合适的 \(W\)\(b\),让正确类别的分数通常高于错误类别。

7.2 视觉视角

从视觉角度看,权重矩阵 \(W\) 的每一行可以 reshape 回图像形状,成为某个类别的模板。输入图像和模板做内积,越匹配,分数越高。

这种理解很直观,但也暴露了线性分类器的限制:每个类别只有一个线性模板,很难同时表示同一类别的多种模式。例如“汽车”可能有正面、侧面、不同颜色和不同背景,一个单一模板会把这些变化平均在一起。

7.3 几何视角

从几何角度看,线性分类器在输入空间中学习一组线性决策边界。对于两个类别 \(i\)\(j\),分类边界满足:

\[ w_i^Tx+b_i=w_j^Tx+b_j \]

整理后仍是一个超平面:

\[ (w_i-w_j)^Tx+(b_i-b_j)=0 \]

因此线性分类器只能用直线、平面或高维超平面分割空间。对于 XOR、环形区域、多模态类别等结构,单个线性分类器很难正确表示。

8 损失函数:如何评价一个分类器

8.1 为什么需要损失函数

线性分类器的核心问题是:如何找到好的 \(W\)\(b\)。这需要两件事:

  1. 定义一个损失函数(Loss Function),衡量当前分类器在训练数据上有多差。
  2. 使用优化(Optimization) 方法,高效寻找能降低损失的参数。

给定训练集:

\[ \{(x_i,y_i)\}_{i=1}^{N} \]

其中 \(x_i\) 是第 \(i\) 张图像,\(y_i\) 是整数类别标签。单个样本的损失记为 \(L_i\),整个数据集的平均损失为:

\[ L=\frac{1}{N}\sum_{i=1}^{N}L_i \]

损失越小,表示模型给训练数据的分类分数越符合目标;损失越大,表示模型对正确类别的信心不足或把错误类别打得太高。

9 Softmax 分类器与交叉熵损失

9.1 从分数到概率

线性分类器输出的原始分数也叫 logits。这些分数可以是任意实数,不一定非负,也不一定和为 1。Softmax 分类器(Softmax Classifier) 使用 softmax 函数把 logits 转换为概率分布:

\[ p_j=\frac{e^{s_j}}{\sum_{k=1}^{C}e^{s_k}} \]

其中 \(C\) 是类别数,\(s_j\) 是类别 \(j\) 的 logit,\(p_j\) 是模型认为输入属于类别 \(j\) 的概率。

Softmax 有两个重要性质:

  • \(p_j \ge 0\),因为指数函数输出非负。
  • \(\sum_{j=1}^{C}p_j=1\),因为所有指数值被总和归一化。

9.2 交叉熵损失

如果正确类别是 \(y_i\),Softmax 损失定义为:

\[ L_i=-\log p_{y_i} \]

展开为 logits 形式:

\[ L_i=-\log\left(\frac{e^{s_{y_i}}}{\sum_{j=1}^{C}e^{s_j}}\right) \]

也可以写成:

\[ L_i=-s_{y_i}+\log\sum_{j=1}^{C}e^{s_j} \]

这个损失鼓励正确类别概率 \(p_{y_i}\) 尽可能接近 1。若正确类别概率很高,\(-\log p_{y_i}\) 接近 0;若正确类别概率很低,损失会很大。

9.3 最大似然、KL 散度与交叉熵

Softmax 损失也可以从概率建模角度理解:模型给每个类别一个概率,训练时希望最大化观测到的正确标签的似然。最大化正确类别概率等价于最小化负对数似然:

\[ \max p_{y_i} \quad \Longleftrightarrow \quad \min -\log p_{y_i} \]

如果把真实标签表示为 one-hot 分布 \(q\),模型输出为概率分布 \(p\),交叉熵为:

\[ H(q,p)=-\sum_{j=1}^{C}q_j\log p_j \]

由于 one-hot 标签只有正确类别位置为 1,上式就化简为:

\[ H(q,p)=-\log p_{y_i} \]

从分布比较角度看,它也和 KL 散度有关:当真实分布固定时,最小化交叉熵等价于让模型分布更接近真实分布。

9.4 Softmax 损失的常见问答

最小可能值:当正确类别概率 \(p_{y_i}=1\) 时,

\[ L_i=-\log 1=0 \]

因此最小值是 0。

最大可能值:当正确类别概率趋近于 0 时,

\[ -\log p_{y_i}\to \infty \]

因此最大值无上界。

初始化时的损失:如果初始化时所有类别分数近似相等,那么 softmax 输出近似均匀分布:

\[ p_j\approx \frac{1}{C} \]

正确类别损失为:

\[ L_i=-\log\frac{1}{C}=\log C \]

对于 CIFAR-10,\(C=10\),所以初始 softmax loss 大约是:

\[ \log 10 \approx 2.3 \]

10 多类 SVM 损失

10.1 SVM 损失的基本形式

多类 SVM 损失(Multiclass SVM Loss) 又称 hinge loss。它不把分数解释为概率,而是要求正确类别分数比所有错误类别分数至少高出一个间隔(margin)。

给定第 \(i\) 个样本的分数向量 \(s=f(x_i,W)\),正确类别为 \(y_i\),SVM 损失为:

\[ L_i=\sum_{j\ne y_i}\max(0, s_j-s_{y_i}+1) \]

其中 \(1\) 是 margin。对每个错误类别 \(j\),如果:

\[ s_{y_i}\ge s_j+1 \]

说明正确类别至少领先该错误类别 1 分,这一项损失为 0。否则该错误类别会产生正损失。

10.2 SVM 损失例子

假设有 3 个类别 cat、car、frog,某个 cat 样本的分数为:

\[ s_{\text{cat}}=3.2,\quad s_{\text{car}}=5.1,\quad s_{\text{frog}}=-1.7 \]

正确类别是 cat,则损失为:

\[ L_i=\max(0,5.1-3.2+1)+\max(0,-1.7-3.2+1) \]

\[ =\max(0,2.9)+\max(0,-3.9)=2.9 \]

car 的分数比 cat 高,因此产生很大损失;frog 的分数已经足够低,不产生损失。

如果另一个 car 样本分数为:

\[ s_{\text{cat}}=1.3,\quad s_{\text{car}}=4.9,\quad s_{\text{frog}}=2.0 \]

正确类别 car 已经比其他类别至少高 1:

\[ \max(0,1.3-4.9+1)=0 \]

\[ \max(0,2.0-4.9+1)=0 \]

所以该样本损失为 0。

10.3 SVM 损失的性质

SVM 损失关注的是相对分数差距,不是概率。只要正确类别分数比错误类别分数高出 margin,继续提高正确类别分数不会进一步降低该样本损失。

常见问题包括:

  • 最小损失是 0。
  • 最大损失无上界,因为错误类别分数可以比正确类别高任意多。
  • 如果初始化时所有分数接近相等,则每个错误类别项约为 \(\max(0,1)=1\),所以单样本损失约为 \(C-1\)
  • 如果把正确类别 \(j=y_i\) 也放进求和,会额外多出 \(\max(0,s_{y_i}-s_{y_i}+1)=1\),整体损失平移一个常数,不改变相对参数偏好,但会改变损失数值。
  • 如果用 mean 替代 sum,只是按类别数缩放损失和梯度,通常可以通过学习率调整补偿。

10.4 SVM 损失的向量化实现思路

实现多类 SVM 损失时,一般步骤是:

  1. 计算所有样本的类别分数矩阵 \(S=XW+b\)
  2. 取出每个样本正确类别的分数 \(s_{y_i}\)
  3. 计算所有类别的 margin:

\[ \text{margin}_{ij}=s_j-s_{y_i}+1 \]

  1. 对正确类别位置设置为 0,因为 \(j=y_i\) 不参与求和。
  2. 对所有 margin 应用 \(\max(0,\cdot)\)
  3. 对类别求和,再对样本求平均。

这类实现体现了深度学习代码中的重要风格:尽量使用矩阵运算和向量化操作,而不是对样本和类别写大量 Python 循环。

11 Softmax 与 SVM 的比较

Softmax 和 SVM 都可以训练线性分类器,但它们对分数的解释不同。

维度 Softmax Multiclass SVM
分数解释 logits 经过 softmax 后变成概率 分数只用于比较相对大小
损失形式 \(-\log p_{y_i}\) \(\sum_{j\ne y_i}\max(0,s_j-s_{y_i}+1)\)
优化目标 提高正确类别概率 让正确类别比分错类别高出 margin
对很自信样本的处理 继续提高正确概率仍会减少损失,但收益变小 一旦满足 margin,损失就是 0
输出可解释性 有概率解释 没有直接概率解释

考虑正确类别分数已经很高的情况,例如分数为 \([10,-2,3]\),若正确类别是第一类,SVM 可能已经满足 margin,损失为 0;Softmax 损失也很小,但仍不是严格 0。若把正确类别分数从 10 提高到 20,SVM 损失仍然是 0,而 Softmax 损失会继续变得更接近 0。

核心区别:SVM 关心“是否已经领先足够多”,Softmax 关心“正确类别概率是否尽可能接近 1”。

12 复习重点与易混点

12.1 必须掌握的概念

  • 语义鸿沟:图像是像素张量,但任务目标是高层语义类别。
  • KNN:训练时记忆数据,测试时找最近邻或 \(K\) 个近邻投票。
  • 距离度量:L1 和 L2 会导致不同的邻居关系和决策边界。
  • 超参数:例如 \(K\) 和距离度量,需要用验证集选择。
  • 测试集只在最后使用一次:否则会造成测试集泄漏。
  • 线性分类器\(f(x,W,b)=Wx+b\),输出类别分数。
  • 损失函数:把“分类器表现好坏”转化为可优化的数值目标。
  • Softmax 损失:把 logits 变为概率,最小化正确类别的负对数概率。
  • SVM 损失:要求正确类别分数比错误类别分数至少高出 margin。

12.2 常见易混点

易混点 正确理解
KNN 训练快,所以整体好用 KNN 预测慢,而且像素距离不符合语义相似度
训练集表现最好就是模型最好 训练集表现可能来自过拟合,超参数应看验证集
测试集可以用来调参 测试集只用于最终评估,不能参与选择模型
Softmax 的输入就是概率 Softmax 输入是 logits,输出才是概率
SVM 和 Softmax 都只看最大分数类别 训练时两者都利用所有类别分数,但损失定义不同
线性分类器可以解决所有分类边界 线性模型只能表示超平面边界,复杂模式需要非线性模型

12.3 公式速查

KNN 最近邻:

\[ i^*=\arg\min_i d(x,x_i),\quad \hat{y}=y_{i^*} \]

L1 距离:

\[ d_1(x,y)=\sum_{k=1}^{D}|x_k-y_k| \]

L2 距离:

\[ d_2(x,y)=\sqrt{\sum_{k=1}^{D}(x_k-y_k)^2} \]

线性分类器:

\[ s=f(x,W,b)=Wx+b \]

数据集平均损失:

\[ L=\frac{1}{N}\sum_{i=1}^{N}L_i \]

Softmax 概率:

\[ p_j=\frac{e^{s_j}}{\sum_{k=1}^{C}e^{s_k}} \]

Softmax 损失:

\[ L_i=-\log p_{y_i} \]

多类 SVM 损失:

\[ L_i=\sum_{j\ne y_i}\max(0,s_j-s_{y_i}+1) \]

12.4 做题时的检查习惯

计算 Softmax loss 时,先确认正确类别概率是否合理;若所有 logits 相等,答案应为 \(\log C\)。计算 SVM loss 时,逐个错误类别检查 \(s_j-s_{y_i}+1\) 是否大于 0,只有正 margin 才会贡献损失。调参题中,看到“在 test set 上选择最优模型”应立即判断为错误做法。