导论


课程介绍

CS231n 是斯坦福大学开设的计算机视觉与深度学习经典课程‌,全称 CS231n: Convolutional Neural Networks for Visual Recognition,主要教授学生如何用卷积神经网络让计算机看懂图片,并执行一些简单的任务。

此课程的前半部分从线性分类器开始,以图像处理的角度串联了Deep learning系列的绝大部分模型和算法。课程的后半部分开始着重于图像分类、目标检测、语义分割、3D视觉、生成式模型等应用型模型算法。为了更好地衔接本课程,本篇日志将综合 CS231n-2025 课程的前八讲,介绍深度学习基础模型在CV领域的算法和应用。

为更好食用本系列学习日志,建议读者拥有一定的深度学习知识基础。本篇日志更侧重于深度学习篇的模型原理回顾,以及在图像处理中的使用方法,帮助读者更好的过渡。

计算机视觉

计算机视觉(Computer Vision, CV)是使用计算机及相关设备对生物视觉的一种模拟。它的主要任务就是通过对采集的图片或视频进行处理以获得相应场景的三维信息。目前为止,计算机视觉已经被广泛应用于人们的日常生活中,如人脸识别技术、自动驾驶技术、医疗自动化等。

早期的计算机视觉主要围绕以下问题展开

  • 图像分类(Image Classification):找出图中包含着哪些类别的目标

  • 目标检测(Object Detection):找到某个目标具体在哪个位置

  • 语义分割(Semantic Segmentation):对目标物体进行一个像素级分割,即对图像中的每一个像素点都进行分类,但是同一物体的不同实例不需要单独分割出来

基础模型


本部分对应 Letcure 1-4,以图像分类任务为切入点。

图像分类的形式

图像分类是计算机视觉领域中的一个重要任务,其目标是将输入的图像分配到预定义的类别中。这个任务通常涉及使用机器学习和深度学习技术来训练模型,使其能够自动识别和分类图像。

一般的,图像分类希望通过某种算法实现以下函数

1
2
3
def classify_image(image):
# Some algorithms
return class_label

MNIST数据集

机器学习算法是一种较好的实现方式。对于监督学习算法,我们采用数据驱动的模式。

  1. 收集大量图片和对应标签作为数据集
  2. 使用机器学习算法训练分类器(Classifer)
  3. 在新图片上评估分类器的表现
1
2
3
4
5
6
7
def train(images, labels):
# Machine Learning
return model

def predict(model, test_igames):
# Use model to predict
return test_labels

为了让机器良好的读入一张图片,我们需要获取照片的分辨率,并对每一个像素的RGB值等数据进行结构化存储。因此,机器学习的数据实际上依赖于图片像素的RGB值等数字特征,而非图片本身。

举例而言,某张分辨率为 512×512512\times 512 的彩色图片携带有 512×512×3512\times 512\times 3 个数值信息。

最近邻分类器

一种最简单的形式是利用KNN算法,即最近邻算法。考虑某两张图片,若判断他们为同一类别,那么他们在图像上应该有相似之处,比如颜色值相近等。假设某图片的RGB值组成数值矩阵 I\boldsymbol{I},那么对剩下所有训练集图片的数值矩阵进行遍历,可以找出距离(一般是 LpL_p 范数)最小的图片

minImtrainIImp\min_{\boldsymbol{I}_m\in train} \|\boldsymbol{I}-\boldsymbol{I}_m\|_p

上述算法只考虑最近邻元素,这容易导致孤立样本点的存在。为此,我们可以考虑某个图片的 KK 个最近邻元素,断定相邻个数最多的图片类别为目标图片的类别。这就是最近邻分类器的基本原理,其中 KK 是一个超参数,距离函数的选择(L1,L2L1,L2 范数等)也是一个变量。

为选取合适的超参数 KK,一般进行多折交叉验证(Cross-Validation),选取具有最高准确率的超参数值。更多有关交叉验证的内容详见机器学习篇。

线性分类器

假设某输入图片的尺寸为 l×w×3l\times w\times 3,那么其输入向量表示为 xR3lw\boldsymbol{x}\in\mathbb{R}^{3lw}。考虑一个线性映射

f(x,W)=Wx+bRmf(\boldsymbol{x},\boldsymbol{W})=\boldsymbol{Wx}+\boldsymbol{b}\in\mathbb{R}^{m}

其中权重矩阵 WRm×3lw\boldsymbol{W}\in\mathbb{R}^{m\times 3lw},偏置向量 bRm\boldsymbol{b}\in\mathbb{R}^{m}。上述映射把该图片映射为一个 mm 维列向量,其中每一行的元素对应某种分类标签的得分(Score)。

一张照片通过得分评估与某个类别的相似度,因此 mm 的大小取决于分类标签的个数。我们希望把得分定义为 目标图片是此类别的概率,这就要求我们归一化处理分数。一个好的解决方法是采用 Softmax\mathrm{Softmax} 操作,即对于得分向量 s=fRm\boldsymbol{s}=f\in\mathbb{R}^msi\boldsymbol{s}_i 是第 ii 类的得分,有操作

softmax(si)=exp(si)jexp(sj)(0,1)\mathrm{softmax}(\boldsymbol{s}_i)=\frac{\exp(\boldsymbol{s}_i)}{\sum_j\exp(\boldsymbol{s}_j)}\in (0,1)

根据最大似然估计法,一个好的权重 W\boldsymbol{W} 应当使得下述交叉熵损失函数最小

L=i=1mlogP(Y=yiX=xi)+λR(W)L=-\sum_{i=1}^m \log P(Y=y_i|X=x_i)+\lambda R(\boldsymbol{W})

其中 λR(W)\lambda R(\boldsymbol{W})正则项。为了优化参数,我们可以使用小批量SGD法、动量法、RMSProp法或Adam法等梯度法逼近最小值。梯度法中的学习率选取同样重要,比如考虑余弦衰减形式

ηt=12η0(1+cosπtT)\eta_t=\frac12\eta_0\left(1+\cos\frac{\pi t}{T}\right)

除此之外还可以添加线性Warm-up,使得学习率通过线性预热再衰减。

神经网络架构

线性分类器只能引入线性关系,而现实中很多问题都是非线性关系。一种解决方法是通过引入多层的神经网络架构。通过引入隐藏层和复杂的激活函数(如Softmax,ReLU,Tanh等),神经网络能够处理复杂的非线性关系。

神经网络模型中有若干个权重矩阵,而优化方法仍然遵循梯度法。神经网络的训练过程中,前向传播反向传播交替进行。前向传播通过训练数据和权重参数计算输出结果;反向传播通过导数链式法则计算损失函数对各参数的梯度,并根据梯度进行参数的更新。

除此之外还有Xavier初始化和Dropout等优化技巧,更多内容可以参考机器学习(五)深度学习(三)

CNN架构


本部分对应 Letcure 5-6,以特征提取为切入点。

卷积层与特征提取

在神经网络架构的基础上,卷积神经网络(CNN)通过加入卷积层池化层以获取图像更复杂的局部特征。卷积神经网络自最初的 LeNet,再到后来的 AlexNet, GoogLeNet, ResNet 等,不断突破了图像分类任务的准确度,更多详情可以参考深度学习(四)深度学习(五)

考虑一个对手写字符进行分类的神经网络模型,如果我们能够利用某些架构,提取该字符的一些特征,那么就有可能利用这些特征进行更精确的分类。为了实现这一点,我们需要引入卷积层。

常规的神经网络架构中使用的是全连接层,即通过线性映射和激活函数实现不同二维向量的全连接转化。在卷积层中,我们保留原始输入的多维性,并利用一个小的在原始输入上滚动计算,得到一个新的层。核的参数被视为可训练的参数,一般通过某些方法进行初始化。

维度统一

假设某个图像作为原始输入,其维度可以表示为 3×32×323\times 32\times 32,其中 33 作为RGB通道称为通道数(Channels),我们采用 3×5×53\times 5\times 5 大小的核就可以得到一个 1×28×281\times 28\times 28 的输出。其中的通道数必须与卷积层输入的通道数保持一致。

上述例子中的输出通道数被压缩为了 11,如果我们想要增加输出的通道数,可以在核上增加一个新的维度。在上例中使用 6×3×5×56\times 3\times 5\times 5 大小的核就可以得到一个 6×28×286\times 28\times 28 的输出。

然而,这就使得输入、输出和核的维度出现了不统一,因为核是一个四维张量,而输入和输出是三维张量。为了解决这个问题,我们通过小批量的输入,得到统一的四维张量架构,方便计算。

一般情况下,设输入为批量 NN 的图片,卷积层可以由下图描述

  • 输入层:N×Cin×H×WN\times C_{in}\times H\times W
  • 卷积核:Cout×Cin×Kw×HhC_{out}\times C_{in}\times K_w\times H_h
  • 偏置向量:Cout×13C_{out}\times 1^3
  • 输出层:N×Cout×H×WN\times C_{out}\times H'\times W'

填充与步幅

从上图可以看出,卷积层操作可能带来两个问题

  1. 卷积操作会不断收缩原输入,可能压缩模型的复杂性
  2. 卷积操作可能使得感受野收缩的过慢

为了解决这两个问题,可以分别采用填充步幅的方法。前者通过在原输入的长宽维度边缘添加多余的 00 ,以增加卷积层输出大小;后者通过设置卷积核采样的步幅增加感受野的收敛速度。对于填充 Ph,PwP_h,P_w 和步幅 Sh,SwS_h,S_w,一个输入 N×Cin×H×WN\times C_{in}\times H\times W 在核 Cout×Cin×Kw×HhC_{out}\times C_{in}\times K_w\times H_h 操作下的输出维度为

N×Cout×HKh+PhSh+1×WKw+PwSw+1N\times C_{out}\times \lfloor\frac{H-K_h+P_h}{S_h}+1\rfloor\times\lfloor\frac{W-K_w+P_w}{S_w}+1\rfloor

池化

卷积核对原图的操作被视为二维互相关操作,而池化层则采用了其他的采样操作,比如最大池化或平均池化。池化层的主要优点之一是减轻卷积层对位置的过度敏感,通过对特征进行统计处理(最大值、平均值等)从而形成新的特征图,可以有效降低模型的复杂度,防止过拟合

全连接转化

问题在于,我们如何将四维张量形式的卷积操作输出转化为最终的全连接输出。考虑一个空间尺度为 1×11\times 1 的卷积核,如果其输出维度为 11,那么就可以实现输入通道的融合,并且这种融合只考虑其自身 1×11\times 1 的局部范围,有效的压缩了数据的维度。

规一化

为了防止数值大小不统一而导致的梯度计算爆炸,或梯度计算消失问题,我们可以预处理数据,保证他们在归一化后拥有相近的数值大小,以优化计算。归一化就是把原数据转化为均值为0,方差为1的形式,有很多种处理方法,常见形式包括

  • 批量归一化BatchNorm:对每批样本的通道 N×H×WN\times H\times W 归一化
  • 层归一化LayerNorm:对每个样本的所有通道 C×H×WC\times H\times W 归一化
  • 单归一化InstanceNorm:对每个空间尺度 H×WH\times W 归一化
  • 群归一化GroupNorm:对某些通道的空间尺度 Cp×H×WC_{p}\times H\times W 归一化

其他技巧

基于之前深度学习篇中的CNN架构和技巧,我们在下面罗列出一些具有启发性的优化技巧

  1. Dropout暂退法:设置超参数 pp 随机丢弃一些节点,不参与前向传播
  2. ReLU激活函数:采用简单的ReLU激活函数可能带来更好的效果
  3. VGGNet:小尺寸的核搭配更深的网络架构可能带来更好的效果
  4. ResNet:采用残差连接方法能够保证优化效果不变差
  5. Kaiming/Xavier初始化:提供更好的初始化效果
  6. 训练数据增强:通过反转、剪切、拼接训练数据加强训练效果

RNN架构


本部分对应 Letcure 7-8

图像与序列处理

在CNN架构中,我们总是认定输入图片的尺寸必须一致,因为CNN架构的可运行性极度依赖于输入和输出尺寸是否合法。实际运用中,我们可能会遇到如下图像处理问题

  • 一对多:对于一张输入照片,如何输出几个相应的关键词?
  • 多对一:对于一个动态视频,如何对这个动态照片序列分类?
  • 多对多:对于一个动态视频,如何输出几个相应的关键词?

循环神经网络(RNN)通过某种循环机制能够实现序列的处理。具体而言,RNN通过定义隐变量,在序列的输入过程中不断更新隐变量,并根据当前隐变量和输入给定输出。对于输入 {xt}\{x_t\} 和隐变量 hth_t,RNN的核心架构可以由下式描述

ht=fh(ht1,xt,Wxh,Whh),yt=fy(ht,Why)h_t=f_h(h_{t-1},x_t,W_{xh},W_{hh}),\quad y_t=f_y(h_t,W_{hy})

有关RNN的更多数学细节可参考深度学习(八)。RNN的强大之处在于,其拥有预测下一步未知输出的能力,即使输入中没有提供。当RNN给定全新的输出时,我们将RNN自身生成的输出作为下一步的输入,这样就可以让其不断实现循环输出。即便如此,过多步的预测输出可能会不理想。

当输入为图像时,我们可以通过CNN与RNN的结合形式得到一个图像序列模型

  • 第一层模型为CNN架构,又称图像编码器(Image Encoder),通过CNN卷积架构获取图像核心信息,最终输出一个全连接层向量
  • 第二层模型为RNN架构,通过图像编码器的输出执行序列任务

RNN改良架构

RNN模型的一个问题在于:由于其模型变量拥有时间特性,在梯度计算时需要考虑时间变化对不同变量的影响,即时间截断的梯度下降。可以数学上证明,普通的RNN模型很容易出现梯度计算问题,因此需要对其架构进行改良。在深度学习(九)中,我们简要介绍了几种形式

  1. 深层循环神经网络
  2. 门控循环单元GRU
  3. 长短期记忆模型LSTM
  4. 双向循环神经网络
  5. 编码器-解码器架构(Encoder-Decoder)

我们主要介绍最后一个架构,其经常被应用于序列到序列问题(seq2seq)。对于给定的输入序列 {xtt=1,,T}\{x_t| t=1,\cdots,T\},我们按照时间步依次计算编码器隐状态 ht=f(ht1,xt)h_t=f(h_{t-1},x_t) 直到 hTh_T,此时的 hTh_T 理应涵盖了输入序列的所有信息,称之为上下文向量(Context Vector) cc

接下来,我们希望所有的输出都依赖于这个上下文向量。定义输出序列为 {ytt=1,,T}\{y_t|t=1,\cdots,T'\},其解码器隐状态 sts_t 的更新应该同时依赖于当前输出、上一步的解码器隐状态和上下文向量,即有

st=g(st1,yt,c),s0 is initializeds_t=g(s_{t-1},y_t,c),\quad s_0 \text{ is initialized}

其中,函数 gg 可以被视为门控单元GRU或长短期记忆单元LSTM。

注意力

在编码器-解码器架构中,上下文向量 cc 这个想法很有启发性,但当输入序列长度过大时,上下文向量是不容易总结整个输入序列的,这就带来了序列转换瓶颈。为了解决此问题,我们需要赋予模型能够回看输入序列的能力,也就是注意力机制。这一创新首先由Bahdanau等人提出,参考深度学习(十)

在编码器-解码器架构基础上,我们初始化解码器隐状态 s0s_0。定义注意力函数

et,i=fatt(st1,hi)e_{t,i}=f_{att}(s_{t-1},h_i)

其中 fattf_{att} 是一个线性层,其结果 et,ie_{t,i} 表示 st1s_{t-1} 和某个编码器隐状态 hih_i相似度。为了归一化这些 et,ie_{t,i},可以采用Softmax操作得到注意力评分

at,i=softmax(et,i)(0,1)a_{t,i}=\mathrm{softmax}(e_{t,i})\in (0,1)

此时的上下文向量应当根据这些注意力评分而变化,因此 cc 变成了一个关于时间 tt 的变量

ct=i=1Tat,ihic_t=\sum_{i=1}^Ta_{t,i}h_i

因此,解码器架构被重写为

st=g(st1,yt,ct)s_t=g(s_{t-1},y_t,c_t)

根据这个思想,我们可以定义神经网络中的注意力层(Attention Layer)。对于查询矩阵 QRNQ×DQ\boldsymbol{Q}\in\mathbb{R}^{N_Q\times D_Q} 和数据矩阵 XRNX×DX\boldsymbol{X}\in\mathbb{R}^{N_X\times D_X},我们通过线性映射得到键、值矩阵 KRNX×DQ,VRNX×DV\boldsymbol{K}\in\mathbb{R}^{N_X\times D_Q},\boldsymbol{V}\in\mathbb{R}^{N_X\times D_V}

K=XWK,V=XWV\boldsymbol{K}=\boldsymbol{XW}_K,\quad \boldsymbol{V}=\boldsymbol{XW}_V

键值矩阵可以被视为数据矩阵在不同特征方向上的投影,因此他们具有输入数据的某些特征,矩阵 WK,WV\boldsymbol{W}_K,\boldsymbol{W}_V 是可学习参数。为了通过注意力机制获取相似度,通过缩放点积注意力得到矩阵

E=QKTdRNQ×NX,Eij=qiTkjd\boldsymbol{E}=\frac{\boldsymbol{QK}^T}{\sqrt{d}}\in\mathbb{R}^{N_Q\times N_X},\quad E_{ij}=\frac{\boldsymbol{q}_i^T\boldsymbol{k}_j}{\sqrt{d}}

其中 ddqiTkj\boldsymbol{q}_i^T\boldsymbol{k}_j 的方差以保证方差归一化,因此注意力评分为Softmax后的结果

A=softmax(E),Y=AVRNQ×DX\boldsymbol{A}=\mathrm{softmax}(\boldsymbol{E}),\quad \boldsymbol{Y}=\boldsymbol{AV}\in\mathbb{R}^{N_Q\times D_X}

如果我们将查询也视为从输入得到的线性投影 Q=XWQ\boldsymbol{Q}=\boldsymbol{XW}_Q,此时的注意力称为自注意力,也就是说模型完全按照输入执行注意力层操作,得到输出。自注意力层满足置换不变性(Permutation Equivariance),即输入顺序只影响输出顺序而不影响输出结果,这将导致两个结果

  1. 好处:自注意力层可以执行并行计算,更高效
  2. 不足:自注意力机制无法获取输入的位置信息

对于后者,我们可以引入两个机制来解决这一问题

  1. 遮掩操作:每个输入 xtx_t 只能看到其自身和之前的输入,否则标记为 -\infty
  2. 位置编码:通过三角编码形式为输入向量注入唯一对应的位置信息

如果我们想要得到更复杂的自注意力效果,可以施加多头注意力获取不同维度的特征。

Transformers

Transformer架构旨在解决循环神经网络在处理长序列数据时存在的计算效率问题和难以并行化等问题。该架构的核心创新是使用自注意力机制替代传统的循环结构,以实现序列的并行处理,并取得了更好的表现。更多有关Transformer架构的数学原理见深度学习(十)

近期的绝大多数模型都将以Transformer架构为蓝本。