chapter-2-图像分类
图像分类的困难
图像分类是这样的一个任务:你输入一幅图片,计算机根据已有的标签,通过一定的算法,将图片归类。
这对人类来说非常简单,但对计算机来说就很困难了。因为计算机并不能像我们人类直接看到这幅图片,而是看到一堆杂乱的像素,每个像素都用三个数字来描述(RGB)。这里面并没有一个直接的标志来说明图片的类别。
这个问题称为语义鸿沟。即图片所描述的物体与计算机所看到的数字矩阵间的巨大差距。
这个问题的难点不止在于识别图片中的物体,还在于这些物体有时候会处于不同的光照场景;物体有可能会变形(比如猫,众所周知猫是由水构成的);还可能会被遮挡;有时候还会有图像混乱的问题:想要识别的物体和背景很相似,导致识别困难;此外还有类内差异的问题,当一幅图出现不同表现的同一种物体时,如何准确地识别每一个物体。
数据驱动的算法
为了解决以上问题,一个直觉的算法是得到每个确定的物体的边缘,然后得到其特征用以识别。但这种方法的问题是,即使对于同一种物体,当情况发生变化时,它的效果也会变得很差。
于是一种新的方法被提出了:数据驱动的算法。这种算法是搜集大量的标注了种类的图片,设计一定的算法来让计算机进行识别。计算机会搜集所有的数据,并根据算法的思路,来总结每一类图片的特点,并借此用来识别那些未经过标注的图片。
因此,通常程序包含两部分:一部分是训练函数,计算机通过总结已经标注的图片的特点得到分类知识;一类是测试函数,计算机利用已总结的分类知识来识别未标注的图片,并通过分类错误率来判断分类的精确度。
这一思想推动了图像识别技术大幅地发展。
K近邻算法
最近邻是最简单的一类分类算法。它的训练函数很简单,只是单纯地记录所有的训练数据。而在测试阶段,我们拿测试的图片与所有训练数据对比,并选择与它最相似的图片的种类作为它的种类。
如何判断图片的相似程度呢?通常我们使用距离函数。因为图片可以视作一个矩阵,距离函数就是设定特定的函数来计算图片表示的矩阵间的距离。
常用的距离函数有很多,比如曼哈顿距离:
它分别计算两幅图片中对应元素的差的绝对值并求和,将最终的和作为两幅图片的距离。
最近邻算法的训练时间复杂度是O(1),但其预测时间复杂度却是O(n)。这和我们想要的恰好相反,因为训练往往是放在一些计算力很强的机器内运行的,所以时间复杂度即使高一些也没关系;但预测往往是在算力没那么强的场景下运行的,比如手机、网页等。
以下是一个最近邻算法的实例:
从中可以看出最近邻算法的缺点:边缘不够圆滑,没能避免奇异点等。
为了解决这个问题,我们创造了改进的算法:k近邻算法。k近邻算法可以看作一个投票器,它考虑的不仅仅离它最近的一个点的类别,而是k个点的类别,并选择数量最多的那种类别作为该点的类别。其结果如下:
可以看出,k=1时效果和最近邻算法相同。而随着k的增大,边缘会越加平滑,但未分类的空白区域的面积也在增大。所以一个好的k的选择对分类来说非常重要。
上面的距离函数使用了曼哈顿距离,有时候我们也可以使用欧式距离。这两种方法都很直观,因为可以把它们理解为空间距离,但如果把距离函数的范围扩大,比如,计算两句话之间的距离,我们就可以对语句进行分类。
超参数的选择
我们将k和距离函数这样的参数称之为超参数,因为它要在程序运行前指定,它们不一定能在训练数据中获得,而其选取一般是和实际问题有关的。
选取超参数有以下几种思路:
1:找到能完美适合训练集的超参数
这是一种很糟糕的办法。例如在之前的k近邻算法中,若设置k=1,我们就能够完美地分类所有训练数据,但在运行非训练集时效果就不如较大些的k值。
我们编写算法的目的不是为了最好地分类训练集,而是为了在分类未知数据时效果最好。所以不要这样设置超参数。
2、将数据分类为训练集与测试集
另一种想法是将数据分为训练集和已知类别的测试集,然后选择不同的超参数在训练集上训练算法,然后在测试集上运行算法,并选择在测试集上表现最佳的超参数作为选取的超参数。
这看上去很合理,但实际上却很糟糕。因为我们的目的是训练出能够很好地分类未知数据的算法。但这样做的后果是我们得到了能够很好地分类测试集的算法,但因为测试集不一定能够很好地代表现实世界,所以这种方法也不可取。
3、将数据分为训练集、测试集及验证集
我们在训练集上使用不同的超参数来训练算法,然后在验证集上选取表现最好的超参数作为最终的超参数。最后将这个算法在测试集中运行,作为这个算法的评估。
这是一个不错的方法,它最重要的一点是,测试集和训练集及验证集一定要分开,以此来保证评估的真实性。
4、交叉验证
这一方法通常运用在小数据集上,它的理念是首先从数据中抽出一部分作为测试集,然后将其余的部分分为几份,然后每次从每份数据中抽出一份做验证集,其余的做训练集,如此重复直到所有的数据都做过一次验证集。然后我们将所有验证的结果作为该组超参数的表现。
这一方法通常并不能运用到深度学习中,因为在深度学习时我们的数据集往往很大,使用这种方法会消耗太多的算力资源。
线性分类
线性分类器的一个实例如下:
假设我们的图片来自CIFAR-10数据集,那里的图片是32*32的RGB图像,共有10类。
我们将图片作为输入数据x并将其拉伸为一个列向量,另外我们还有参数W。我们的最终目的是知道图片属于哪一类,因此我们还要得到十个数字来表示该图片与这10个类别的归属程度。
在最近邻算法这样的算法中,我们在测试时仍需要保留训练集,这导致了算法变得臃肿。但使用这种方法,我们实际上是把训练集中的知识浓缩到了W中。因此在测试集上运行时,我们不再需要训练集。这使得分类算法的应用场景被大大拓宽了。
我们可以选择各种方法来组合W和x,一个最简单的想法就是乘法,即f(x,W)=W*x,这时分类器被称为线性分类器。
(3072=32323)
以下是一个例子:
假设图片是一个221的图片,分类类别是3,那么x就是一个4行的列向量,w是一个3*4的矩阵,此外还有一个3行的列向量b,我们称之为偏置,最终结果是一个3行的列向量。
整个方程为f(x,W,b) = W * x + b
有趣的是,这里的分类错把猫分成了狗。
下图是线性分类的原理:
它实际是在图像空间中划分了类别数条线,并通过计算判断输入图片属于哪类区域。
但线性分类并非万能,以下是三个线性分类器失效的例子: