
【斯坦福CS231n 学习笔记1】从线性分类器到自适应优化算法
主要是Lecture1-Lecture3部分的笔记。
因为最近要学习的去烟/雾项目是关于计算机视觉的,这方面还算是一个小白,所以选择了 Stanford 的 CS231n 来学习一下,给自己建立一个比较系统的框架。
课程总体大纲(Lecture 1)
Lecture 1 主要是这门课的课程概览,介绍了课程的总体框架,大概分为以下四个部分:
1. 深度学习基础
2. 视觉世界感知与理解
3. 生成式与交互式的视觉智能
4. 以人为中心的应用与影响
其实也分别对应了现代视觉智能的四个层次:基础的方法论、经典的判别任务、生成与交互以及社会技术系统视角。
在 Lecture 1 的基础模块中首先强调了图像分类是计算机视觉的核心任务,因为其是很多复杂任务的“原型”:检测、分割、视频理解等都可以视为在分类之上增加结构约束或输出维度。
所谓的图像分类,其实也就是学习一个函数:
- 输入:图像(本质上是像素构成的张量)。
- 输出:类别(或者类别概率)。
- 模型:,参数为 。
课程在基础模块中会用图像分类依次引出 Linear classifier、Regularization&Optimization 以及 Neural Networks,实际上就是用最简单的模型建立从输入到输出的一个数学框架,在发现模型会过拟合/训练困难后引入正则化与优化的方法,在发现线性表达能力的局限性以后引入神经网络。
真实世界的视觉远不止分类,除了分类以外还有语义分割(每个像素输出语义类别(只分“类”不分“实例”))、目标检测(输出多个目标的类别+边界框)、实例分割(像素级输出,并区分同类不同个体)。
而在模型方面,模型不仅仅是 MLP,在视觉中更常见的是 CNN、RNN、Transformers。因为图像不是一维向量,而是具有局部相关性与空间结构的信号。
- CNN 使用“局部感受野+权重共享”引入归纳偏置,更加符合图像的统计规律。
- Transformer 使用注意力机制建模更长程依赖,并与大规模预训练相结合,成为了现代视觉/多模态的关键骨干。
Lecture 1 还提到,现代深度学习离不开“规模化训练”,现代模型与数据集都很大,单 GPU 往往不够,需要并行训练。而并行训练包括了- 数据并行:复制模型,切分数据。
- 模型并行:切分模型到不同设备。
- 同步 vs 异步梯度更新。
除了 2D 图像识别以外,当前计算机视觉领域比较前沿的部分还包括:
- 自监督学习:数据不完全依赖人工标注,通过设计预训练目标,让模型从数据本身学习表示。
- 生成式建模:风格迁移与“文生图/扩散模型”,模型不仅能够“判别”,还能够生成。
- 视觉-语言模型(VLM):VLM、对比学习与检索增强等方向表明,视觉与语言的融合越来越重要。
- 3D 视觉:强调 2D 图像走向三维理解与重建,是视觉智能的重要扩展。
- 具身智能:智能体需要在环境中感知并且行动,视觉已经成为“闭环决策系统”的一部分。
图像分类任务与数据驱动方法(Lecture 2)
图像分类任务
给定:
- 一张图像(本质上是一个张量,如 的 RGB 像素)。
- 一个标签集合(dog/cat/truck/...)
目标就是学习一个函数 ,将图像映射到类别。
显然这个任务揭示了,计算机看到的和人类看到的并不一样,课程使用了一个案例:
人看到的是猫,但是计算机看到的是一堆 0-255 的数字,也就是人类语义与像素表示之间存在语义鸿沟(semantic gap)。
该任务的挑战就在于“语义等价≠像素等价”,即在人类语义上是同一个东西,但是在像素表示上却可能相差非常远,这直接导致了像素距离是不可靠的。
例如以下几点:
- 视角变化:同一个物体,从不同的角度看,外观的差异极大。在像素层面上几乎所有的像素都会发生改变,物体的轮廓、比例、遮挡关系都会发生变化。
- 光照变化:同一个物理在不同光照条件下也不同。在像素层面上的体现就是像素值直接依赖光照强度、阴影区域的细节丢失、颜色分布的整体偏移等。
- 背景杂乱:物体并不总是在“干净背景”中,背景可能会与物体的颜色/纹理相似(伪装)。在像素层面上的体现是背景像素往往数量多于物体像素,且背景对特征的影响更大。
- 遮挡:物体被部分遮挡。在像素层面上就导致了关键结构缺失、轮廓不完整以及训练中看到的“整体模式”在测试时不存在。
- 形变:非刚性物体的姿态变化,在像素层面上同一物体的几何结构不固定、局部模式位置也发生变化。
- 类内差异:同一类别内部差异巨大,如品种、型号、风格不同。这导致了像素和语义的错位,类内的差异也许可能会大于类间的差异,“看起来更像别的类”的同类样本大量存在。
- 上下文依赖:物体的合理性依赖于上下文,在像素层面上就导致了一些歧义,局部外观可能并不足以区分类别,模型会利用上下文来作为“捷径”。
为什么需要机器学习
因为图像分类不像排序数字一样能够写出明确的规则,历史上使用过边缘、角点之类的手工流程,但是实际上很难覆盖现实情况的复杂性。
所以采用了数据驱动的机器学习:
- 收集带标签的数据。
- 使用算法训练分类器。
- 在新图像上评估泛化性能。
Nearest Neighbor/KNN(非参数化模型)
Nearest Neighbor
最简单的分类算法,不涉及参数学习。
- 训练阶段:简单地记忆所有的训练数据和标签,时间复杂度为 。
- 预测阶段:计算测试图像与所有训练图像的距离,将最近的训练图像的标签作为预测结果。时间复杂度为 ,其中 是训练样本数。
该算法的问题是,测试时间随数据量线性增长,这在实际应用中是不可接受的(我们希望训练慢,测试快)。
至于如何计算距离,此处主要有两种距离:
- L1 (Manhattan) distance:,即将两张图像所有对应像素的差的绝对值求和。L1 距离依赖于坐标系的旋转,对坐标轴敏感。
- L2(Euclidean) distance:,即欧几里得距离。L2 距离具有旋转不变性,通常对坐标轴不敏感。
KNN
为了解决最近邻算法对噪声敏感的问题(例如,一个异常点可能导致决策边界出现孤岛),引入了 K 值。
- 机制:找到距离最近的 K 个样本,通过多数投票决定类别。
- 效果:当 K > 1 时,决策边界会变得更加平滑,抗噪能力增强。
超参数选择
K 的取值和距离度量的选择(L1 或者 L2)被称为超参数,不是通过训练得到的,而是需要人为设定。
Lecture 2 中提到了实验的一些方法论:
- 不能在测试集上调整超参数,这会导致“数据泄露”,使得评估结果无法反映模型的泛化能力。
- 正确的做法是将数据划分为训练集、验证集和测试集:需要在训练集上训练;在验证集上评估不同超参数的表现,选择最佳组合;仅在最后一次使用测试集来报告性能。
此外还有交叉验证:在数据量较少的时候,将训练的数据分为 M 折,轮流作为验证集,但是在实际的深度学习中,因为计算成本较高,通常不常用,更多地采用单一训练集划分。
线性分类器
线性分类器采用参数化方法的思想,不需要记住所有的数据,而是去学习一个带有参数的函数。
- 输入:图像 。
- 输出:每个类别一个分数,所谓的分数即模型认为这张图属于某一类的证据强度。
- 参数:权重矩阵 和偏置 。
具体的形式是:
这个评分函数有两种解释视角:
- 代数/视觉:矩阵 的每一行可以看成是一个类别的模板,线性分类器本质上是在计算输入图像与各个类别模板的内积(相似度)。从这个视角看,每个类别就只能够学习一个模板,若一类物体有多种形态,则线性分类器只能学到一个平均后的模板。
- 几何视角:线性分类器在高维空间中学习超平面来切割空间。 这个公式定义了决策的边界,即一个相比于给定数据维数空间少 1 的切面将空间一分为二(只能切分一次)。局限性在于其无法解决非线性可分问题(例如 XOR 问题,或者环形分布的数据结构)。
Loss Function + Optimization
要选择一个好的 ,需要定义一个 Loss Function 来衡量当前的 有多差,以及用 optimization 来找到最小 loss 的参数。
Softmax
最终希望将分数转化为概率,而概率必须≥0 且概率的总和必须=1。由此引出了 Softmax 函数:
其中对每个分数取指数(保证非负),然后处以所有指数之和(归一化到 1)。
Softmax Loss
如果正确类别的概率越大,我们越满意;因此损失可以取负对数:
正确类的概率接近 1→loss 接近 0。
正确类的概率很小→ loss 很大。
而优化的目标就是最小化损失函数,由此我们就能够得到这一个新的 Softmax 损失函数。
初始化时的 loss
Lecture 2 中还给到了一个很实用的 tips:如果在刚开始是各类的概率近似等于 ,那么 , 如当 C =10 时,即有十个类,那么一开始的 loss 就约为 。以后在训练模型的时候,看到初始 loss 在 附近,则通常是正常的。
SVM loss
SVM loss 的定义如下:
SVM loss 不关心概率,只关心是否满足间隔,即关注 correct 和 incorrect 的分数差,如果正确类分数比所有错误类都至少高出 1,则样本的损失直接变为 0。
SVM 和 Softmax 的区别是 SVM 有“够了就停”的硬阈值,只要满足 loss=0 ,梯度=0,样本就不会再推动参数进行更新;而 Softmax 则没有“硬停止”, 但是会收益递减,只要正确类的概率还不是 1,就还有 loss,继续把正确类的分数太高,正确类的概率会更接近 1,loss 会继续下降——只是下降幅度会越来越小,收益递减。
Regularization & Optimization(Lecture 3)
Regularization
一个完整的损失函数 L 由两部分构成:
- Data Loss:迫使模型拟合训练数据。
- Regularization Loss:迫使模型保持“简单”,防止过拟合。
- λ:调节两者之间的权衡。
根据奥卡姆剃刀原则,最简单的就是最好的,所以要通过正则化来迫使模型,往简单的方向贴近,使其能够拥有更好的泛化能力。
常见的正则化项
- L2 正则化:
其具有惩罚大的权重值的特性(每个权重值一旦变大,代价值会“二次增长”),且更加倾向于让权重向量 更加分散,例如其认为权重向量 要优于 。如果输入的数据受到噪声干扰,其分散的权重能够利用所有输入特征的微小贡献,而不是过度依赖某一特征,从而提高泛化能力。
- L1 正则化:
其更加倾向于产生稀疏的权重矩阵,即许多元素变为 0,用一个最简单的一维目标来看:
其最优解是 ,只要 ,其最优解就是 。
Optimization
Gradient
优化的目标是寻找参数 ,使得损失函数 最小化。Lecture 3 提到在参数空间采用随机搜索的效果很差,因此应当使用梯度。
在一维上有:
而在多维,梯度(Gradient,)的定义就是对每个变量求偏导数所组成的向量,其几何意义为指向函数增长最快的方向,而梯度的负方向即为最速下降方向,也就是我们所要寻找的优化方法。
梯度的计算有两种:
- 数值梯度:使用有限差分近似 ,其计算较慢,主要用于梯度检查以调试代码。
- 解析梯度:使用微积分的链式法则推导公式,例如设输入为 ,权重矩阵为,先通过线性层得到分数(logits),再用 softmax 得到概率向量 ,其中 ,最后用交叉熵定义损失 ( 为真实类别)。因为 对 的依赖路径是 ,所以求 的核心就是沿着这条“复合函数链”应用链式法则:。其中第一项 是 softmax 与 cross-entropy 组合后的经典结果:令 one-hot 标签向量为 ,则有 ;第二项来自线性层 ,其局部导数体现为“每个 logit 对对应行权重的梯度等于输入”,把上游梯度链回来即可得到 。将所有分量写成矩阵形式,最终得到非常紧凑的解析梯度公式:。这就是所谓“解析梯度是链式法则推导出来的”:把整体损失看成一连串算子(计算图)的复合,每一步只算局部梯度,再把上游梯度按链式法则传回去,最终拼出对所有参数的全局梯度。
一般而言,使用解析梯度训练,使用数值梯度检查代码是否写错。
Gradient Descent
最基础的参数更新公式如下所示:
其中 是学习率,深度学习中最重要的超参数之一,其如果太小,则收敛的速度会极慢,容易陷入局部最优;如果其学习过快,会导致其不收敛或震荡、损失值爆炸以及模型不稳定。
Stochastic Gradient Descent
由于在整个训练集上计算梯度计算量过大,实际应用中采用 Mini-batch 来近似真实梯度,这样做的计算效率比较高,更新频率也比较快。
但是还是要说说其存在的问题:
- Poor Conditioning:如果 loss 在某个方向变化得很快,另一个方向很慢,SGD 会在陡峭方向抖动,在平缓方向进展缓慢,方向呈现“之”字型。
从数学本质上看,是由于其 Hessian(二阶偏导数矩阵)决定,设 H 为损失函数的 Hessian 矩阵,其特征值 描述了局部的曲率,其中为最大特征值,对应曲率最大的方向, 为最小特征值,对应曲率最小的方向,条件数定义为最大绝对特征值与最小绝对特征值之比:,当 很大时(即病态条件),损失函数在一个方向极其拉伸,在另一个方向极其压缩,SGD 的收敛速度与条件数直接相关,条件数越大,收敛所需的迭代次数越多,优化越困难。
- Local minima/Saddle points:此处探讨的是梯度为零的临界点对优化的阻碍。早期的优化理论比较担心局部极小值,但是在高维深度学习中,鞍点比局部极小值要更加常见:
鞍点是指在该点处梯度为零,但是在某些方向上是局部极小值,在另一些方向上是局部极大值。从数学概率上看,要形成一个局部极小值,Hessian 矩阵的所有 个特征值都必须为正,假设每个特征值的符号是随机的,在一个深度神经网络的参数空间中, 往往是数百万甚至数亿,全正的概率无限接近 0,因此绝大多数梯度为 0 的点,都是一部分特征值为正,一部分为负的鞍点。鞍点附近的梯度极其微小,且 SGD 依赖于梯度更新,若梯度趋近于 0, 参数更新几乎停止,模型会长时间卡在鞍点附近导致假死。
- Gradient Noise:由于将全量梯度下降简化为了小批量随机梯度下降,从期望上看,只要时间足够,下降的方向大体是对的;从方差的角度看,由于采样的随机性,导致近似梯度含有噪声,这导致即使在真正的极小值点,小批量的梯度也不一定是 0。实际影响的表现是参数更新的轨迹不再是平滑的曲线,而是充满了随机抖动,且收敛的精度下降,会在最优点的附近游走。此外还有双刃剑效应:如果不随着训练逐渐减小学习率,噪声会阻止模型的收敛;而好处在于这种噪声实际上又引入了某种退火机制,当卡在浅层的局部极小值或鞍点附近时,梯度的随机波动可能恰好把参数带出这个区域,从而有机会找到更好的解。
SGD+Momentum
为了解决这个问题,引入了物理学中的“速度”的概念,利用梯度的移动平均来对参数进行更新:
其中 为摩擦系数,通常设置为 0.9 或 0.99,其作用为在震荡方向上正负梯度相互抵消,减少震荡;在平坦方向上梯度累积,加速收敛;同时利用惯性冲过梯度为 0 的区域。
自适应学习率算法
在之前的算法中,对所有的参数使用同一个全局学习率 ,自适应学习率的核心思想就是为每个参数 维护一个特定的学习率,若某个参数的梯度一直很大,就降低它的学习率,如果某个参数的梯度一直很小,就增加它的学习率,本质上是在对梯度进行归一化。
RMSProp
在 Momentum 的基础上,加上“按维度缩放”的思想:根据历史平方梯度(带衰减)做元素级的缩放,即对每个参数除以其历史梯度的均方根。
- Cache 更新:。
- 参数更新:。
其效果是在梯度大的方向减小补偿,在梯度小的方向增大补偿,从而自动调整轨迹,于此同时由于衰减率的加入,可以防止由于梯度平方恒为正时 cache 变量单调递增而导致学习率在模型未收敛时就因为过小而停止学习的问题。
Adam
这是深度学习中最常用的默认优化器,其结合了 Momentum 与 RMSProp。其主要维护两个状态变量:
- 一阶矩():梯度的移动平均≈均值。
- 二阶矩():梯度平方的移动平均≈未中心化的方差。
初始算法如下:
偏差修正:由于 和 初始化为 0 向量,且 和 接近 1(衰减很慢),在训练初期, 和 的值会严重偏向于 0。为了修正此初始化偏差,Adam 引入了修正项:
- 当 很大时,,修正项不起作用。
- 当 很小时,接近 1,分母很小,从而放大了 和 , 抵消了初始化的零偏差。
最终的 Adam 更新公式如下:
超参数调优策略
Lecture 3 最后讨论了在训练的过程中应当如何动态调整学习率:
- 学习率衰减:可以采用Step Decay,即每隔几轮 epoch 将学习率减半或变为 0.1 倍;或者 Cosine Decay,即学习率随着时间按照余弦函数曲线下降,最后趋近于 0。
- Linear Warmup:在训练初期现先将学习率从 0 线性增加到预设值,用于稳定训练初期的剧烈梯度变化。
- 二阶优化:牛顿法和 Hessian 矩阵,虽然收敛极快,但是需要计算 的 Hessian 矩阵极其逆矩阵,对于深度神经网络而言,其显存和计算成本不可接受,因此主要还是使用一阶方法。
参考如下:
https://cs231n.stanford.edu/index.html
