图像分类
1 数据驱动方法
人眼和计算机看到的图片不同,计算机看到的图片是由很多代表像素点的数字表示的数组,所以人眼和计算机的视觉识别存在着Semantic Gap(语义鸿沟)。
同时,让计算机能够有效地识别图片中的物体之前,还存在很多挑战:比如
- 一些像素的简单偏移或者变化,会使整张图片有改变;
- 物体所在的环境,如光线,位置会有变化;
- 物体本身有不同的姿态;
- 可能有障碍物遮挡物体;
等等。
以前的硬编码方式不适合用在物体识别,因为我们很难去为这件事情设定一个死的规则。
我们希望有一个方法,可以有效地识别物体,且对不同情境下的同一物体的识别具有鲁棒性,对于不同物体的识别具有可拓展性。
这里就引出了机器学习的数据驱动方法,该方法首先要收集很多图片和对应标签作为数据,然后训练一个数据分类器,使用该分类器在新的图像集上进行评估。
接下来老师介绍了第一个分类器:Nearest Neighbor
- 该方法的train步骤只是单纯地记录下所有的图片和标签。
- 该方法的predict步骤对每个新的图像,找出train中和它最相似的图像,将它的图像标签赋给新图像。
如何去判定图像的相似呢?这里介绍曼哈顿距离。将两个图像中每个像素点的差异相加,就是两个点的距离。
class NearestNeighbor:
def __init__(self):
pass
def train(self,X,y):
'''X is N*D where each row in a dimension, y is a one-dimension '''
#the nearesr neighrbor algorithm simply remember all training data
self.X_tr = X
self.y_tr = y
def test(self,X):
'''X is M*D where each row is an example we wish to predict label for'''
num_test = X.shape[0]
Ypred = np.zeros(num_test,dtype = self.y_tr.dtype)
for i in range(num_test):
distances = np.sum(np.abs(self.X_tr-X[i,:]),xis=1)
min_index = np.argmin(distances)
Ypred[i] = self.y_tr[min_index]
return Ypred
Nearest Neighbor训练时的时间复杂度为 O ( 1 ) O(1) O(1),预测的时间复杂度为 O ( n ) O(n) O(n),这是不好的。因为我们构件模型的目的在于希望它能快速地预测,反而训练的时间就算长一些也可以接受。
2 KNN算法
上节描述的NN算法是寻找与待预测图像最相近的点,但是实际上,最相近的点不一定与待测图像标签一致;反而是从相邻的K个点中进行投票,票选出来的标签会更加鲁棒和精确。这种加上投票的算法叫做KNN。
除了上节提到的曼哈顿距离,还有一种度量向量间距离的方法,欧式距离。
这两种距离的区别在于,L1距离随着坐标轴的旋转各点到原点的距离会发生变化,L2则不会。如果输入的特征中,有些特征是具有明显含义的,使用L1效果可能会好一些。
像刚刚提到的K, distance的选择,都是超参数(hyperparameters),所谓超参数,就是在训练模型之前人为选择的而非训练得到的参数。超参数非常依赖问题情境,需要不断地调参来使训练效果最优。
划分数据集的有效方法是将数据集分成三份:训练集、验证集和测试集。选择在验证集上效果最好的超参数,然后在测试集上测试。在训练时应该不接触测试集,直到最后测试模型效果的时候才应该去接触测试集。
还有一种划分数据集的方法叫做“K-折划分法”,这种方法一般适用于小数据集,不常在深度学习中使用。它是将训练集分成几份,每次选择一份作为验证集进行训练。
实际上,我们不会在图片域使用KNN进行预测。因为KNN在预测时很慢,而且在像素点之间的距离度量没什么实际意义。
还有一个原因,就是维度灾难。
因为KNN并不会在训练前假设一个分布函数,所以它需要训练集的标签数据较为densely地分布。但是,随着维度的提升,获取densely数据需要的数据量越来越多,这会引发维度灾难。
3 线性分类
线性分类(linear classifier)可以说是神经网络中最基本、最简单的一个组件。
线性分类方法是一种参数方法。
考虑CIFAR-10数据集,共有50,000张训练图像,每张的维度是 32 × 32 × 3 32 \times 32 \times 3 32×32×3;10,000涨测试图像。
考虑一个参数模型,把图像,我们通常称为 x x x作为输入, W W W就是模型的参数,将 x x x和 W W W以某种或复杂或简单的方式组合起来,就得到了我们的参数模型,输出类别。
将 x x x与 W W W相乘,是一种直观和简单的方法,线性分类器也是这么做的。
将 32 × 32 × 3 32 \times 32 \times 3 32×32×3的图片拉伸成 3072 × 1 3072 \times 1 3072×1的向量 x x x,输出 f ( w , X ) f(w,X) f(w,X)是 10 × 1 10 \times 1 10×1的向量,所以 W W W的形状就是一个 10 × 3072 10 \times 3072 10×3072的矩阵。这里我们可以加一个偏置常数向量 b b b,表示给每一个类加偏置,比如训练数据中猫的图片远远多于狗的图片,那么猫的偏置值会大一些。
刚刚是从代数的角度解读线性分类,我们还可以从视觉的角度解读线性分类。训练好模型得到 W W W后,由于 W W W的每一行代表一个类,我们可以将每个类的 W r W_r Wr抽出来,形成模板图片。通过观察模板图片我们可以大概推测出这个 W W W学了个什么东西出来。
或者从几何学的角度解读线性分类。我们把每个图像都视为高维空间的一个点,那么线性分类器就是在高维空间去找一些平面,来分割这些不同的类。
也存在着一些问题是线性分类无法解决的,比如说上图class1的异或问题,有些情景没办法使用线性分割,这就需要我们采用其他的方法去做预测分类。