计算机视觉笔记(一)
课程介绍
目前为止的参考课程:
斯坦福李飞飞 cs231n 计算机视觉课程:https://www.bilibili.com/video/BV1nJ411z7fe/
可能要用到的编程知识:Python Numpy Tutorial (with Jupyter and Colab) (cs231n.github.io)
作业:
计算机视觉概述
每秒钟 Youtube 上会被上传 5 个小时的视频。所以要理解视频的内容,做标记,然后推广给用户,就不可能依赖员工来完成。
生物学家也在研究视觉的机理,五六十年代休伯尔和威泽尔的电生理学研究启发了计算机视觉的研究。他们将电机插进猫的后脑上的初级视觉皮层,然后观察何种刺激会引起视觉皮层神经的激烈反应。他们发现猫的大脑的初级视觉皮层有各种各样的细胞,其中最重要的细胞是当它们朝着某个特定的方向运动时,对面向边缘产生回应的细胞。此外也有其他复杂的细胞,但人们发现了——视觉处理开始于视觉世界的简单结构。随着视觉处理的过程,信息也逐渐地变化着,大脑建立了复杂的视觉信息,直到它可以识别更为复杂的视觉世界。
当人们认为图像识别太困难,便转向从图像分割开始慢慢做。图像分割,就是把一张图片中的像素点归类到有意义的区域。我们并不知道分割后会得到一个苹果,但我们可以把属于这个苹果的像素抠出来。
面部检测
1999-2000年,机器学习技术,特别是统计机器学习方法开始加速发展,出现了如支持向量机模型,boosting方法,图模型,和第一波神经网络浪潮。有一项特别的工作做出了很大贡献,就是 2001 年使用 AdaBoost 算法进行实时面部检测。在论文发表后的第五年(2006年),出现了第一个能在拍照时检测出人脸的相机。
为了更好地进行目标识别, 在 90 年代末到 2000 年的前十年有一个非常有影响力的思想方法,就是基于特征的目标识别。David Lowe 完成了一个影响深远的工作,叫做 SIFT 特征(SIFT feature,1999 年提出)。思想的核心是,如果一个物体转了个角度,识别起来就很困难,但我们发现变化中有一些具有不变性的特征,只要我们找到这种特征,就会比整张图片的匹配简单很多。
与此同时的另一进展是识别整幅图的场景——是厨房还是山水风景?思想的核心是,一幅图中有很多特征,可以告诉我们是哪种场景。这个算法从图片的各个部分的像素提取特征,并把它们放在一起作为一个特征描述符,然后在特征描述符上做一个支持向量机。
benchmark data set,基准数据集,被标注的数据集 ,用来衡量人们在目标识别方面取得的成果
最有影响力的标注数据集有:PASCAL Visual Object Challenge,有二十个类别。
CIFAR10,有十个类别,50000张训练图像,10000张测试图像
起源
深度学习是什么时候开始的?
在 2012 年,CNN模型或者说深度学习展示了强大的模型容量和能力,在计算机视觉领域和其他领域(如自然语言处理和语音识别领域)取得了巨大进步。里程碑便是 AlexNet(曾经也被叫做 Supervision) 的提出。
2014 年的 ImageNet 挑战赛迎来了 GoogleNet 和来自牛津大学的 VGG 网络。
2015 年微软亚洲研究院发表论文,提出了残差网络,有 152 层。
但是卷积神经网络其实在1998年就提出了
Yann LeCun 和他的合作伙伴在 Bell 实验室完成。1998 年他们使用卷积神经网络来识别数字,希望用来识别手写的支票,帮助邮局识别地址。这个网络可以识别数字和字母。
相关知识
图像分类问题
ImageNet 挑战赛
怎样从输入的一张图片,得到它的类别?
衍生应用:
- object detection 目标检测,需要把物体”框“出来。
- image captioning 图像摘要(给图片添加说明文字)。
- activity recognition 动作识别
- augmented reality 增强现实
- virtual reality 虚拟现实
以上问题可以追溯到一些古老的观点:
- 语义分割
- 知觉分组
这些图片不会被打上标签,我们想要理解每一个像素的含义。比如一个人挥手的样子是想说什么?两个人做了一系列动作,他们是在跳舞还是在打架?
图像分类
语义鸿沟(semantic gap):计算机看到的像素和我们给定的标签之间的巨大差距。
我们无法写具体的分类规则来识别一只猫或者鱼,而是转向采用数据驱动的方法。
最近邻算法(nearest neighbor):也许是最简单的分类器,
曼哈顿距离(L1 距离,L1 distance,又叫曼哈顿距离):衡量两张图片的相似性,对应像素的差的绝对值之和。
欧氏距离(L2 距离,Euclidean distance):用对应像素的差的平方和然后开根号。
🍸如果转动坐标轴,L1 距离会改变,但 L2 距离不会。所以如果你输入的特征向量中的一些值对你的任务有重要的意义,那么 L1 也许更合适。但如果它只是空间中的一个通用向量,你也不知道哪个元素比较特殊,那么 L2 更自然一些。
K-近邻算法
不只是寻找最近的点,而是寻找最近的 K 个点,然后在这些相邻点中进行“投票”,选择票数多的结果。
这个算法不常使用,因为在训练时它很快(只需要把图片“记住”)但在应用它做检测时却很慢(需要逐个和记忆的图片做对比)。另外欧几里得距离或 L1 距离这样的衡量标准用在比较图像上并不合适,因为这种向量化的距离函数,并不适合表示图像之间的视觉的相似度。
另一个问题被称为“维度灾难”,原因是在高维空间中,数据点之间的距离变得相当稀疏,导致常用的距离度量失效。为了实现较好的效果,KNN 需要大量的训练数据。
KNN算法通常受到维度灾难的影响,因为在高维空间中,找到最近的邻居变得更加困难,因为所有的点在高维空间中看起来都很遥远。为了避免维度灾难的影响,可以考虑使用降维技术(如主成分分析)来减少数据的维度,或者选择更适合高维度数据的算法。
🍠因为 KNN 对潜在的各种分布没有做假设,所以能让它较好地工作的唯一情况就是,提供了大量的训练数据。
超参数的选择
选择训练集上效果最好的?不行,例如 K = 1 的 K-近邻分类器,虽然训练集精度 100%,但在未见过的数据上表现不好;
选择测试集上效果最好的?不行,这样往往只是让模型适应了这组测试数据,恐怕仍然和现实世界相差很大。你手动调参,让模型在测试集上“过拟合”了。
相比之下,更合适的做法是再分出一个“验证集”,训练好的模型在验证集上做测试然后调参,选出最好的结果去测试集上测试,将测试结果写进论文里。直到所有工作完成后,才允许接触测试集。
另一个设置超参数的策略是交叉验证(cross validation),在小数据集中更常用,在深度学习中很少用到。先预留好测试集。然后把剩下的数据分成 k 份,每次用 k-1 份做训练,剩下的 1 份做验证,重复 k 次
线性分类
各种组件搭积木组成大型的神经网络,线性分类是其中最基础的
做一个描述图片信息的神经网络,可以考虑用卷积神经网络只关注图片,用循环神经网络只关注语言,把两个网络放在一起。
线性分类器是参数模型(parametric model)中最简单的
K-近邻算法并不会产生“知识”,而线性分类器会把知识存储在权重矩阵中。
🧆如果数据集中猫的数量很多,那么猫所对应的偏置项就会很大
线性分类器只能为每个类别学习一个模板,这就使得当目标类别中有很多“小品种”时,模板会试图对它们取平均,从而导致精度下降。
线性分类器的另一个观点是回归到图像
损失函数和优化
多分类 SVM损失函数
合页损失函数(hinge loss),0 和一个表达式取最大值。
对于线性分类器,可以在预测完某一训练样本后,把所有错误类得分和正确类得分的差经过合页函数后求和,作为损失。之后把所有样本的损失求平均,作为总体的损失。
🍺如果随机初始化权重,那么首轮损失一般会是类别数减一。如果你发现不是这样,那么可能程序存在bug
交叉熵损失
多项逻辑斯蒂回归(multinomial logistic regression,又名 softmax loss) 这个损失函数在深度学习里更常用。
每一个得分取指数,然后做归一化(这一过程就是softmax)。损失函数是正确的类别的概率取对数,添负号。
我们希望经过 softmax 之后,所有的概率集中在正确的类别上。这时我们有很多方法,比如在目标分布和我们计算出的分布之间计算 KL散度 来比较它们的差异,也可以做一个最大似然估计,或者别的方法。
🍋如果正在用 softmax 损失(交叉熵损失,cross-entropy loss)训练模型,那么第一轮迭代之后的损失应该是 Ln(c),c 是类别数。否则可能是程序存在 bug
两种损失函数有一个很大不同——当分类正确时,对 SVM 来说,这代表差值超过安全阈值,所以不会对这组数据有反应。而 softmax 会总是试图在正确分类上积累更多概率质量,将正确类别的分值推向正无穷,把错误类别的分值推向负无穷。
优化
怎样得到梯度?也许可以在每一个分量上做小差分,近似计算梯度?但这并不可行,因为在现实应用中,一个输入可能有上千万个分量,这样的计算是很大的。
所以有时我们会求出解析梯度,而只是用数值梯度来验证写进代码的解析表达式是否正确。
梯度下降 Gradient Descent
老师提到可以考虑先确定学习率,然后调整模型的大小和正则化系数
在实际中,N 可能会非常大,我们难以计算总的损失和梯度,所以我们总是采用随机梯度下降(stochastic gradient descent),它在每一次迭代中,选取一小部分训练样本(称为 minibatch,小批量,批量的大小一般采用 2 的 n 次幂,比如 32,64,128,256)来计算损失和梯度。这本质上是随机的,你可以把它当作是一种蒙特卡洛估计。
图像的特征
之前,我们将整幅图片的像素作为输入传入线性分类器,但由于多模态的问题,这也许不太好(马头朝左和马头朝右)。所以在深度学习被广泛应用之前,人们的图像分类工作一般分两步——首先拿到图片并计算图片的“特征”,将不同的特征向量拼到一起,然后再传入线性分类器。
有一些简单的特征,比如颜色分布直方图,检测边缘的有向梯度直方图,还有词袋。
有向梯度直方图把图片分成一个个小正方形区域,在每个小区域计算主要边缘方向,把这些边缘方向量化到几个组,然后在每一个区域内,计算不同的边缘方向从而得到一个直方图,现在你的全特征向量就是这些不同组的边缘方向直方图。从有向梯度直方图中,我们可以看出有哪些不同类型的边缘,即使他们在图像的不同区域。这种简单的方法很常见,但并没有持续太久。
词袋是从自然语言处理中得到的灵感——如果你得到了一段话,那么用特征向量表示这段话的一个方法就是,计算不同的词在这段话中出现的次数。然而这种灵感应用于计算机视觉并不容易。首先,我们需要建立自己的视觉单词字典。先从获得的图像中进行小的随机块的采样,然后通过 K均值等方法将它们聚合成簇,从而得到不同的簇中心,这些簇中心可能就代表了图像中不同的视觉单词;一旦我们获得了这一系列的视觉单词(也叫做码本,codebook),我们就可以用这些码本对图像进行编码,每张图像中某个视觉单词出现了多少次?这其实也是一种特征表示。
当人们提取完特征(颜色特征、边缘特征、词袋特征)之后,就固定特征提取器(可以把这所有特征拼在一起),使它在训练中不再更新,之后只训练线性分类器。