CS231n
upd:
- 2025.12.12 终于做完了 assignment 2,课上到第十课结束(虽然笔记还没更(咕咕咕)),感觉是历史性的一刻,好开心(虽然明天要考四级),是以记之。
Nearest Neighbor Classifier

代码实现:
点击查看代码
import numpy as np
class NearestNeighbor(object):
def __init__(self):
pass
def train(self, X, y):
""" X is N x D where each row is an example. Y is 1-dimension of size N """
# the nearest neighbor classifier simply remembers all the training data
self.Xtr = X
self.ytr = y
def predict(self, X):
""" X is N x D where each row is an example we wish to predict label for """
num_test = X.shape[0]
# lets make sure that the output type matches the input type
Ypred = np.zeros(num_test, dtype = self.ytr.dtype)
# loop over all test rows
for i in range(num_test):
# find the nearest training image to the i'th test image
# using the L1 distance (sum of absolute value differences)
distances = np.sum(np.abs(self.Xtr - X[i,:]), axis = 1)
min_index = np.argmin(distances) # get the index with smallest distance
Ypred[i] = self.ytr[min_index] # predict the label of the nearest example
return Ypred
存在的问题:
- train 是 \(O(1)\) 的,而 \(predict\) 却是 \(O(n)\) 的,每次询问耗时过长,多次询问的情境下效率较低。
- 需存储所有训练数据,空间效率低

二者的区别:\(L1\) 能更准确地保留特征,而 \(L2\) 更支持旋转等变换产生的模糊匹配。
K-Nearest Neighbors:选取距离最小的 \(K\) 个值对应的分类,认定样本最多的那个类为此样本的分类(类似众数)。
存在的问题:\(K \ne 1\) 时,可能存在前 \(K\) 临近分类中有多个众数的情况,而导致无法分类。
那么 \(K\) 的值如何确定?

为了防止验证集选择上存在偏见,采用交叉验证取平均。

注:训练集:验证集:测试集一般 8:1:1
独热编码
独热编码(one-hot vector),一个向量,除了有一位是 \(1\) 外,其余维度均为 \(0\)。
一般用于多分类问题中的真实标签。
线性分类器(Linear Classifier)
Score Function:通过左乘加权矩阵,再与 bias 矩阵加和,将输入图片向量映射到输出向量。
bias 矩阵的作用:使分类的多维向量空间不经过原点,让分类更精确。
存在的问题:可能存在无法线性分类的情况。

优化(Optimization)
梯度下降(Gradient Descent)
解决如何更快地找到更好的 \(W\) 的问题。
即让 Loss 尽可能小。而 Loss 实际上是 \(W\) 的一个函数,为了寻找全局最小值,采用梯度下降的方式。
要求 \(\nabla_W L\)
对正则项 \(R(W)\) 的任意元素 \(W_{a,b}\) 求偏导:
对单个样本 \(i\),交叉熵损失的梯度展开:
令 \(Z = \sum_j e^{s_j}\),则:
其中 \(\nabla_W s_j = x_i^T\)(\(s_j = W_{j,:}x_i + b_j\),对 \(W\) 求导得 \(x_i^T\))。
目标类别项的梯度:
合并得单个样本梯度:
将单个样本梯度平均后与正则项梯度相加:
实现上,可以通过小量计算梯度以验证链式法则所求梯度是否正确。
基本版代码
点击查看代码
while True:
weights_grad = evaluate_gradient(loss_fun, data, weights)
weights += - step_size * weights_grad # perform parameter update
随机梯度下降(Stochastic Gradient Descent(SGD))
解决了梯度下降考虑整个训练集计算量过大的问题,每次随机选择一部分数据训练集计算梯度,通过某些方式循环遍历到整个训练集。
点击查看代码
while True:
data_batch = sample_training_data(data, 256) # sample 256 examples
weights_grad = evaluate_gradient(loss_fun, data_batch, weights)
weights += - step_size * weights_grad # perform parameter update
存在的问题:
-
步长过大可能会直接略过当前的谷;过小而函数较陡时,会在局部震荡而收敛缓慢。
数学上,理解为 Hession 矩阵 中存在异常的最大 or 最小值。 -
可能困在函数的局部最小值或鞍点中。
-
由于二次采样,收敛路线可能过于凌乱。
Momentum
根据物理直觉给 SGD 加上动力。
优点:
-
通过震荡的相互抵消解决了收敛缓慢的问题
-
一定的动力使其有机会突破局部最小值与鞍点的困境(但如果不幸的进入在鞍附近震荡的情况,似乎就陷在循环里了,尽管概率很小,但可以通过更换多个种子与增加扰动的方式解决)
-
一定的动力让路线不会过于随机(凌乱)
两种实现方式:
其中 \(\alpha\) 为学习率,或:
RMSProp
核心思想:通过累计历史梯度的平方并将其置于分母,使得在函数陡峭区步长较少,而在平坦区步长较大。
点击查看代码
grad_squared = 0
while True:
dx = compute_gradient(x)
grad_squared = decay_rate * grad_squared + (1 - decay_rate) * dx * dx
x -= learning_rate * dx / (np.sqrt(grad_squared) + 1e-7)
Adam
目前最流行,结合了 Momentum 与 RMSProp 的优点。
almost:
点击查看代码
first_moment = 0
second_moment = 0
while True:
dx = compute_gradient(x)
first_moment = beta1 * first_moment + (1 - beta1) * dx
second_moment = beta2 * second_moment + (1 - beta2) * dx * dx
x -= learning_rate * first_moment / (np.sqrt(second_moment) + 1e-7)
这段代码存在的问题:beta1 与 beta2 的初始值一般设置为很接近 \(1\) 的数,这导致了最初由于除以了一个极接近 \(0\) 的数导致步长极大。
改进:增加基于时间的 bias correction。
点击查看代码
beta1 = 0.9
beta2 = 0.999
learning_rate = 1e-3 # or 5e-4
#以上是大部分情况下表现优秀的初始值
first_moment = 0
second_moment = 0
while True:
dx = compute_gradient(x)
first_moment = beta1 * first_moment + (1 - beta1) * dx
second_moment = beta2 * second_moment + (1 - beta2) * dx * dx
first_unbias = first_moment / (1 - beta1 ** t)
second_unbias = second_moment / (1 - beta2 ** t)
x -= learning_rate * first_unbias / (np.sqrt(second_unbias) + 1e-7)
各种优化的收敛演示:

标准的 Adam 在计算梯度的时候考虑正则化,而 AdamW(Weight Decay)则选择在最后一行末尾加上正则项,测试表明后者一般略优于前者。
学习率(Learning Rate)
过大无法收敛,过小收敛过慢,比较好的方式是随训练轮数调整学习率。
-
每隔固定轮数将学习率下降一定比例。
-
使用 cos 函数:
- 线性:
- 反平方根:
现在流行先 Linear Warmup,然后采用合理的函数下调学习率:

一条经验结论:当增加 batch size 时,需同比例增加学习率。
一点想法:当数据集较小时,可以尝试用二次或更高次的函数拟合,或许会有更好的效果。
代价函数
衡量神经网络的好坏:
SVM Loss
全称 Multiclass Support Vector Machine loss,公式如下:
其中 $s_j = f(x_i, W, b)_j $
二次代价函数
均方误差损失函数,衡量了神经网络输出值(预测值) 与真实值(目标值) 之间差异的平方和。
其中 \(y\) 是预测值,\(t\) 是目标值。
对权重 \(w\) 求导
其中 \(z=wx\)
缺陷:误差较大时调整速率反而可能较慢。
MSEloss
计算均方误差的损失函数(即二次代价函数)。
import torch
import torch.nn as nn
loss = nn.MSELoss(outputs, targets)
交叉熵(Cross-Entropy)
信息量
一个事件所包含的能够减少或消除“不确定性”的东西。
香农的数学定义:
一个发生概率记为 \(p_i\) 的事件 \(x\),其信息量定义为 \(I(x)\),其值为:
熵
熵衡量的是整个随机系统的平均不确定性或平均信息量。熵是信息量的期望值。
KL散度
全称 Kullback-Leibler 散度,衡量两个概率分布之间差异的一种方法。
对于离散随机变量上的两个概率分布 \(P\) 和 \(Q\),KL散度定义为:
式子右半部分是 \(P\) 分布的熵,而左半部分为交叉熵。
由吉布斯不等式 KL 散度始终大于等于 \(0\)。
交叉熵
交叉熵直观理解是"用错误策略编码所需的平均成本"
对于两个离散概率分布 \(P\) 和 \(Q\),交叉熵定义为:
若代入神经网络输出值(预测值 \(y\)) 与真实值(目标值 \(t\)):
底数其实可以任取,但通常取 \(2,e,10\),且最常用 \(2\) 与 \(e\)。
\(t\) 与 \(y\) 由输出得分通过 softmax 转换得到。
与 KL 散度的关系:
正则化(Regularization)
防止模型在训练集上过拟合。
思想:类似 Occam's Razor,对一种现象的多种解释中,经验上一般越简单的越符合事实。
其中 \(\lambda\) 是超参数,为正则化强度,用于控制对模型复杂度的惩罚力度。
常见的正则化方式:
-
L2 Regularization(最常用): \(\sum_{k}^{} \sum_{l}^{} W_{k,l}^2\)
偏好值更分散更平均的 \(W\) -
L1 Regularization: $\sum_{k}^{} \sum_{l}^{} \left | W_{k,l} \right | $
偏好值更稀疏的 \(W\),即 \(W\) 中会有更多的 \(0\) -
Elastic net (L1 + L2): $\sum_{k}^{} \sum_{l}^{} \beta W_{k,l}^2 + \left | W_{k,l} \right | $
兼顾稀疏性与均衡性,使输出更稳定
正则化的好处:
-
则取我们所偏好的 \(W\)
-
让模型更简化,防止过拟合,使其在测试集上表现更好
-
助于优化(加速)模型训练
激活函数
Sigmoid 函数
逻辑斯谛函数,数学表达式为:
优良的性质:Sigmoid 的局部梯度只与其自身有关。
用于神经网络中间层时的问题:多层激活函数以后梯度快速趋于一个极小的值。
Softmax 函数
多分类问题中,网络输出层的激活函数,对输出值进行归一化操作,将其全部转化为概率(和为1)。
Tanh(双曲正切函数)
ReLU 函数
问题:若进入 \(x < 0\) 的部分会造成神经元死亡。
Leaky ReLU
解决 ReLU 死亡神经元的问题。
GELU
其中 \(\Phi(x)\) 是标准高斯分布的累积分布函数。
问题:相比 ReLU 计算量更大,且在很负的情况下仍趋于 \(0\)。
SiLU
问题:与 GELU 类似

(全连接层)神经网络
fully-connected networks 或 multi-layer perceptrons(MLP)
多层线性分类,中间有隐藏节点(神经元),层间有激活函数。
对神经元工作的理解:每一层按照部分特征分类,越分越具体直至达到目标分类结果。
激活函数的作用:使全连接层拥有非线性分类的能力,若去掉激活函数,不同层的加权矩阵 \(W\) 则可以相乘合并成一个加权矩阵,使全连接层退化为线性分类器。
为了防止过拟合,一般不调中间层大小,而是调正则化参数 \(\lambda\)。
全连接层神经网络的设计很大程度上依赖数据集。
反向传播(Backpropagation)
对全连接层手动求梯度在用之于程序很蠢,况且 Loss 函数可能会变,网络结构也可能改变,不可能每次手动重新求,所以需要更智慧的方法。
通过链式法则拆分求梯度过程,通过反向传递逐个求出每一层的梯度,最终层(计算起点)梯度为 \(1\)。

不同运算门的反向传播逻辑:

若节点的值为向量,可借助 Jacobian 矩阵(不用真的去构建)实现反向传播。
若节点的值为矩阵,也可借助类似的思想实现。但若直接存储所有信息内存就要爆炸了。借助乘法门的交换性质,可以简化存储与运算。

卷积神经网络(CNN)
Convolutional Neural Networks
Convolution Layer
直接运用全连接层神经网络消耗大量内存,破坏了图像本身的空间结构,而卷积神经网络很好地解决了这些。

通过卷积核提取特征。
小的卷积核比大的更有效(参数少且多非线性层)
卷积核与 bias 的值初始随机(使其有机会演变出识别不同特征的 filters),通过梯度下降优化确定。
存在的问题:单纯的卷积是线性的。解决:增加非线性激活函数。
卷积层使得数据的二维平面缩小,解决方法是增加 padding:在图像周围加上若干圈 \(0\)。
越后面的卷积层中每一格 receptive field 越大(包含原始图像信息的范围(视域)越大)。但若卷积核对原图像进行完全采样,receptive field 的增长是线性的。
想法:引入 stride,控制步长使得 receptive field 呈指数型增长。

当然,也可以将 2D 的 convolution 变为 1D 或 3D。
Pooling Layer
池化操作本质也是一种下采样(卷积层保持空间尺寸,而池化缩小宽高)。缓解 convolution 计算量大的问题。
Pooling 将原始图像划分成若干不相交区域,对每个区域进行一定的函数操作将其缩成一个数,通常我们用 Max Pooling(天然地引入非线性,如果是 Average Pooling 则需引入非线性激活函数)。
A question:如果数据集图像大小不一样怎么办?
-
强制压缩调整成相同大小
-
使用 padding 填充 \(0\)
-
按长宽比分类分别处理

关于卷积与池化一个有趣的性质:Translation Equivariance
即平移等价性,无论是先经过平移变换再卷积池化,还是先卷积池化再平移,神经网络输出结果是一样的。这让神经网络更关心特征而非位置。
实现上的瓶颈:GPU 的内存限制问题。内存消耗主要来自中间激活值、参数及其梯度。
优化方法:减少 batch size(最有效)、简化架构或使用更小过滤器。
Normalization Layer
标准化层干两件事:
-
通过某种方式计算平均值与标准差,标准化数据集样本
-
对数据进行放缩(scale)与平移(shift)
计算方式:

其中 scale 与 shift 的参数是可学习参数。
计算平均值与标准差的不同方式:

Dropout Layer
在训练时按固定比例 \(p\) 随机丢掉一些神经元节点(正则化的一种形式),而在测试时不这样做。
\(p\) 一般取 \(0.5\),有时也取 \(0.25\)。
形象理解:使神经网络具备通过部分特征(而非所有特征)就能对样本进行分类的能力。
另一种理解:训练的是一堆共享参数的模型的集合,其对样本的适应性更强。
这会使训练时 Loss 变大,而让模型在测试时表现更好。
note:在测试时应乘上 \(p\),否则会导致数据量显著多于训练时而导致模型混乱。
点击查看代码
def predit(X):
H1 = np.maximum(0, W1 @ X + b1) * p # scale the activations
H2 = np.maximum(0, W2 @ H1 + b2) * p # scale the activations
scores = W3 @ H2 + b3
卷积神经网络架构
总体思路:更小的卷积核,更深的网络。

更小的卷积核:用更高效的方式获得更大的视野域。
更深的网络:在一定范围内,网络越深模型性能越好。
但是,达到一定深度后,模型效果饱和且之后迅速下降。(梯度消失/爆炸 and 模型难以收敛(不能通过延长续训练时间来解决))
解决方法:ResNet(残差网络)
直觉的想法:如果将多层网络中的某些层简化成恒等函数,那较深模型至少会拥有较浅模型的性能。
类比得到残差网络:如果能直接学习恒等映射,就没必要学复杂的特征变换:

注意:\(F(x)\) 与 \(X\) 需要有相同的 shape。

另一个经验性的技巧:在网络最开头加上较大的卷积核构成的卷积层。
对 \(W\) 的初始化也很重要,如果初始化的值过小,多层后激活值趋向于 \(0\),若过大,多层后激活值变得很大。
Solution: Kaiming/MSRA Initialization
点击查看代码
dims = [4096] * 7
hs = []
x = np.random.randn(16, dims[0])
for Din, Dout in zip(dims[:-1], dims[1:]):
W = np.random.randn(Din, Dout) * np.sqrt(2.0 / Din)
x = np.maximum(0, x.dot(W))
hs.append(x)
推导:He et al, “Delving Deep into Rectifiers: Surpassing Human-Level Performance on ImageNet Classification”, ICCV 2015
模型训练
数据初始化 标准化数据
数据增强
- 随机增加噪点

-
Flips(注意是否仍然符合逻辑)
-
随机伸缩裁剪

-
对比度、亮度……(原则:人还能轻易认出来)
-
遮挡
Transfer Learning
借用已经训练好的卷积网络,只训练最后一层映射到分类的全连接层;
或以已经训练好的模型为初始化参数。
要求:数据集特征相似。

超参数(Hyperparameter)的选择

注意:与其在几组固定的超参数集合中寻找最优的搭配组合,不如随机多组超参数。这能避免在某个超参数较劣的情况下浪费过多时间。

循环神经网络(RNN)
Recurrent Neural Network
区别于 CNN 只能理解静态的单个样本,RNN 可以处理序列输入。按输入输出类型可分为:单对多、多对单、多对多(不匹配)、多对多(匹配)。
大致思想:通过让前一个时刻的输出参与到当前时刻的输入,使模型具有基于时间的记忆理解能力。
数学表示:
其中:\(h_t\) 表示第 \(t\) 个时刻的隐藏层,\(x_t\) 表示第 \(t\) 个时刻的输入,\(y_t\) 表示第 \(t\) 个时刻的输出,三个 \(W\) 分别为三种不同的权值矩阵(为了方便训练一般全局用一样的这三个 \(W\))
图示:

优点:
-
可以处理任意长的输入
-
当前结果结合了历史所有输入
-
模型不会因输入增长而变大
-
共用 \(W\) 使其具有良好的对称性
变体:Multiple RNN
梯度计算
仍然采用梯度下降和反向传播,但要遍历整个序列。
缺点:计算量巨大+更新延迟
优化方案:时间截断反向传播(TBPTT),只选取一个时间窗口计算 Loss 与进行反向传播。forward 与 backward 的步长一般一致,但也可以不一致。(由于当前片段的第一个时刻的 \(h_t\) 仍然采用上一个片段最后一个时刻的 \(h_{t-1}\),所以前向的信息传递是连续的)
基本步骤:forward -> backward -> 更新 W -> 传递 \(h_t\) -> forward -> ……
致命问题:梯度的消失与爆炸。
考虑 \(W_{hh}^{T-1}\),若 \(W\) 中的最大值大于一,走向梯度爆炸,小于一,走向梯度消失。
梯度爆炸可以通过梯度截断解决,但梯度消失难以解决。
LSTM
试图解决 RNN 的诸多问题。

四个门:input gate、forget gate、output gate、gate gate(?)
Transformer
在标准的编码器-解码器 RNN 架构中(例如用于机器翻译),编码器需要将整个输入序列的信息压缩成一个固定维度的上下文向量(通常是最后一个隐藏状态)。然后,解码器只依靠这一个向量来生成整个输出序列。显然,信息会饱和或被稀释。
Attention


1.关于为什么要除 \(\sqrt{D_Q}\)
- 因为如果不除,若两个极其相似的值做点积,随着维度增大,\(E\) 会变得非常大,计算 softmax 时会除以一个非常大的数,导致梯度消失。
2.关于为什么要区分 \(Q\), \(K\) 与 \(V\)
-
区分 \(Q\) 与 \(K\):本质在找与问题最相关的答案,如果两者不区别,那么问题最相关的答案就是问题自身(
好哲学) -
区分 \(K\) 和 \(V\):\(K\) 与 \(Q\) 做匹配时要尽可能的精简特征,而 \(V\) 与 \(A\) 结合产生输出时则需要保留原来的丰富语义,二者冲突。
3.实操时,一般把 \(W_Q\), \(W_K\), \(W_V\) 拼接成一个大矩阵。
4.有趣的性质:置换等变性(permutation equivariant),即任意排列输入的 token 顺序,输出也仅是按输入打乱方式打乱。
如果想要让模型知道位置信息,在输入时加入位置编码(对于索引的固定函数)


5.Flash Attention 将内存从 \(O(N^2)\) 降到 \(O(N)\)。

MLP
Multi-Layer Perceptron
区别于 Attention 让多个 token 互相交流,MLP 对每一个 token 独立操作。
Transformer 架构

应用与变体:
LLM 需要 mask 一些值,使其只能基于前文推测。
ViT 图像处理(需告知二维位置信息)
Pre-Norm Transformer 将标准化层移到残差连接内(Attention 与 MLP 之前),使训练更稳定。
RMSNorm (Root-Mean-Square)在 Pre-Norm Transformer 的基础上,将标准化层替换成 RMS 层,使训练进一步稳定。
其中 \(x\) 是输入,\(y\) 是输出,\(\gamma\) 是权值。
SwiGLU MLP

上帝的仁慈
MoE 有多组(假设为 \(E\))独立的 MLP(理解为每一组都储存特定领域的信息),每个 token 只路由到 \(A < E\) 个 MLP(最相关的 \(A\) 个),保证效果的同时大大提升效率。
语义分割
Semantic Segmentation
直观想法,给每个像素赋予一个语义标签。从原始图像的 RGB 矩阵,到输出的语义矩阵。
U-Net
解决保留矩阵大小计算量超标的问题,先缩小再放大。
需要 pooling 的反操作 unpooling,几种策略:
- Nearest Neighbor:直接将每个像素的值扩大一圈(给周围的空白像素)。
- Bed of Nails:扩大的空白像素用 \(0\) 填充。
- Max Unpooling:与 Bed of Nails 类似,但记录 Max Pooling 时最大值的位置,并在 Unpooling 时将值放到对应位置。
- Learnable Upsampling:采用卷积核的反向逻辑,用一个 filter 将一个像素映射到一个像素矩阵(重叠部分相加)。(filters 都是可通过训练学习的)
由于下采样会丢失一些信息,所以模仿残差层的策略,将原始图像复制到输出端相加,保证图像的结构信息。
实例分割
语义分割只能区分出不同类别的物体,但不能分割实例(比如两只贴在一起的猫会变成一坨)
目标检测
要分割实例得先检测出目标。
一个思路:用 Softmax 计算分类损失,用 L2 Loss 计算位置损失,然后求和,变成多任务损失。
问题:图像中不同的物体数量会导致不同数量的输出,算法本身扩展困难。
改进:不输入整个图像,而是截取一部分分析类别。
问题:可选的图像框大小位置太多了。
Selective Search
快速定位到可能有物体的图像框。
方法:采用某种方式分割图像,计算每个小区域与其相邻区域的相似度,合并相似度最高的,重复上述步骤,直到整个图像合并成一个区域。这个过程会生成一个区域合并的层次结构。在合并过程的每一层,所有存在的区域的外接矩形都会被收集起来,作为候选区域提议。
R-CNN
目标检测的里程碑式算法。
流程:
- 使用 Selective Search 算法生成约 2000个 可能包含物体的候选区域。
- 由于CNN(如AlexNet)需要固定尺寸的输入,将每个形状不一的候选区域强行缩放到固定大小尺寸。这个步骤会破坏物体的原始纵横比。
- 将每个变形后的区域输入一个预训练好的 CNN 模型(如在 ImageNet 上训练的分类网络)。
- 取CNN最后一个全连接层的输出,得到一个固定长度的特征向量。这样,一张图片就得到了约2000个特征向量。
- 为每个类别单独训练一个二元线性支持向量机分类器。将每个候选区域的特征向量,分别输入所有类别的 SVM 中。每个 SVM 判断该区域是否属于它对应的类别(例如,“是狗”还是“不是狗”),并给出一个置信度分数。
- 为每个类别单独训练一个线性回归模型。输入同样是候选区域的特征向量。回归模型学习的是如何将原始的候选框(由 Selective Search 产生)微调得更贴合真实物体边界。它会输出四个值,对应边界框中心点的偏移量和宽高的缩放量。这让检测框定位更精确。
- 非极大值抑制:对于每个类别,根据分类置信度分数排序,剔除那些与最高分框重叠度(IoU)过高的冗余框。这能确保同一个物体只被一个最准的框检测出来。
- 输出:最终得到图片中所有检测到的物体,每个物体包含类别标签和精确的边界框坐标。
问题:对于每个候选图像框单独跑 CNN 还是太慢了。
解决:只对整个图像进行一次特征提取。
Fast R-CNN
流程:
- 用一次 CNN 生成整张图的共享特征图。
- 用 Selective Search 在原图像上生成候选框,再映射到特征图。
- 采用 RoI Pooling(Region of Interest Pooling),将其池化成固定大小的特征向量。
- 再将特征向量送入 Per-Region Network,这是一个全连接层网络,执行两个并行的任务 (也称为多任务损失):物体分类(Object category)与边框微调(Box offset)。

RoI Pooling 的问题:映射时的强制取整与池化分割时的强制取整会导致一定的误差。
Faster R-CNN
引入区域提案网络(RPN)代替原来的传统区域提案方法(如 Selective Search)。
原理: RPN 在共享特征图上滑动一个小型网络,为特征图上的每个位置生成锚点 (Anchors)。
-
锚点: 预先定义的、具有不同尺度(Scale)和长宽比(Aspect Ratio)的一组边界框。
-
输出: RPN 对每个锚点输出两个结果:
- 前景/背景分数 (Classification): 判断该锚点内是否包含目标物体。
- 边界框回归 (Regression): 微调锚点的坐标以更好地拟合真实的目标边界。
Faster R-CNN 最大的突破在于它将区域提案和目标检测整合到同一个深度学习框架中,并使用一个联合损失函数进行端到端的训练。
实例分割
Mask R-CNN
在目标检测的基础上完成实例分割,可预测目标的像素级掩码 (Pixel-to-Pixel Mask)。
关键在于将 RoI Pooling 替换成了 RoI Align,并在预测头部分添加了掩码分支 (Mask Branch)(一个小型的全卷积网络 (FCN),用于预测 RoI 中每个像素的二值掩码(即属于前景还是背景(类无关)))。
RoI Align

单阶段目标检测器 (Single-Stage Detectors)
核心思想是将所有检测任务合并到一个卷积网络的前向传播中完成。
代表:
SSD (Single Shot MultiBox Detector),YOLO (You Only Look Once) 系列,RetinaNet (Focal Loss)

DETR
Transformer 的一个很好的应用。

object queries 是可学习参数。
可视化
Visual Viewpoint 线性分类器的平均模板
Visualize Filters 可视化卷积核观察网络在学习的特征
利用引导反向传播可以获得更清晰的特征图。

即正向激活为正且反向梯度为正时梯度才允许通过。
Saliency Maps 像素的重要程度:更改哪些像素对分类结果影响最大
CAM(Class Activation Mapping )

问题:只能追溯到最后一个卷积层。
解决:
Grad-CAM (Gradient-Weighted Class Activation Mapping)

Visualizing ViT features 可视化 Attention
视频理解
视频分类
视频一般很大,一般先按一定方式采样,降低帧率。
Single-Frame CNN
一个直观的想法,对视频的每一帧跑 CNN 进行图像分类,采用平均分类结果作为视频分类结果。
Late Fusion (with FC layers)
直觉:视频应该有一个联系所有帧的宏观理解。
方法:将每个 CNN 跑出来的特征向量整合成一个大的特征向量,再扔进全连接层训练。
问题:在后期整合再映射到低维向量会导致全连接层参数较多,训练效率低。
Late Fusion (with pooling)
整合向量这一步替换成做平均池化,消去时间维度。
问题:late fusion 可能因为 CNN 而已经丢失了一部分细节信息。
Early Fusion
尝试解决细节信息丢失问题,在最开始按时间维度拼接所有帧,并用一个 CNN 完成时间维度的降维映射。
问题:似乎有点过于暴力了。
3D CNN
介于 Late Fusion 与 Early Fusion 之间,将卷积核变成三维的。

如果卷积核的时间维度和输入视频的时间维度对其,卷积核学习到的东西就没有时间平移不变性了,因此,让卷积核在时间维度上也进行滑动。
C3D: The VGG of 3D CNNs
模仿二维的 VGG 架构

但这与 2D 相比计算量翻了很多倍,效率较低。
Two-Stream Networks
仿照人类捕捉动作的直觉,将动作与外观分开训练。

C3D 只能处理较短的视频,对于较长的视频,可以采用 RNN 的思路。利用 CNN 提取特征并按时序传递。
CNN 用预训练的,只作为特征提取器,减少内存消耗。

RCN(Recurrent Convolutional Network)
进一步的,结合 CNN 与 RNN,得到 RCN。

问题:RCN 计算量大,不能并行计算。
Spatio-Temporal Self-Attention (Nonlocal Block)
可并行

将这个 Nonlocal Block 加入 3D CNN 架构。
Inflating 2D Networks to 3D (I3D)
直观的想法:借用 2D 架构。将卷积核与池化替换成三维的,用 2D 的 W 初始化 3D 的 W。具体地,复制 T 遍再整体除以 T,这样当视频是一个不变的图像时,3D CNN 架构能给出与图像分类器一样的结果。
关于如何定位有效帧,可以仿照 Faster R-CNN 的思路进行基于时间的区域提案,分类检测有效动作帧。
音频
视频一个很重要的部分是声音。
一些主要的工作:不同音源的检测和分离,基于声音的视频理解。

浙公网安备 33010602011771号