CS231n- 第二讲 图像分类
What
图像分类为计算机视觉领域的核心问题之一,并且有着各种各样的实际应用。
以下图为例,图像分类模型读取该图片,并生成该图片属于集合 {cat, dog, hat, mug}中各个标签的概率。
需注意的是,计算机中的RGB图像存储为一个由数字构成的三维数组,0表示全黑,255表示全白。
因此,图像分类的任务可表述为:对于一个给定的图像,预测它属于的那个分类标签(或者给出属于一系列不同标签的可能性)
Challenges
- 视角变化(Viewpoint variation):同一个物体,摄像机可以从多个角度来展现。
- 光照条件(Illumination conditions):在像素层面上,光照的影响非常大。
- 形变(Deformation):很多东西的形状并非一成不变,会有很大变化。
- 大小变化(Scale variation):物体可视的大小通常是会变化的(不仅是在图片中,在真实世界中大小也是变化的)。
- 遮挡(Occlusion):目标物体可能被挡住。有时候有物体的一小部分(可以小到几个像素)是可见的。
- 背景干扰(Background clutter):物体可能混入背景之中,使之难以被辨认。
- 类内差异(Intra-class variation):一类物体的个体之间的外形差异很大,比如椅子。这一类物体有许多不同的对象,每个都有自己的外形。
因此,面对以上所有变化及其组合,好的图像分类模型能够在维持分类结论稳定的同时,保持对类间差异足够敏感。
HOW
1. 数据驱动方法(Data-Driven Approach)
- 收集图片和标签集
- 使用机器学习训练一个分类器
- 在新的图片上进行测试
2. Nearest Neighbor 分类器
假设现在我们有CIFAR-10的50000张图片(每种分类5000张)作为训练集,我们希望将余下的10000作为测试集并给他们打上标签。
Nearest Neighbor算法将会拿着测试图片和训练集中每一张图片去比较,然后将它认为最相似的那个训练集图片的标签赋给这张测试图片。
那么具体如何比较两张图片呢?最简单的方法就是逐个像素比较,最后将差异值全部加起来。换句话说,就是将两张图片先转化为两个向量I_1和$I_2$,然后计算他们的L1距离:$$d_1(I_1,I_2)=\sum_{p}|I_1^p-I_2^p|$$
- 优点:易于理解,实现简单;算法的训练过程只需要将训练集数据存储起来,训练耗费时间短
- 缺点:测试要花费大量时间计算,因为每个测试图像都需要和所有存储的训练图像进行比较;而卷积神经网络虽然训练花费很多时间,但是一旦训练完成,对新的测试数据进行分类非常快。这样的模式就符合实际使用需求。
代码实现为:
1 # 将CIFAR-10的数据加载到内存中,并分成4个数组:训练数据和标签,测试数据和标签 2 Xtr, Ytr, Xte, Yte = load_CIFAR10('data/cifar10/') # a magic function we provide 3 # flatten out all images to be one-dimensional 4 Xtr_rows = Xtr.reshape(Xtr.shape[0], 32 * 32 * 3) # Xtr_rows becomes 50000 x 3072 5 Xte_rows = Xte.reshape(Xte.shape[0], 32 * 32 * 3) # Xte_rows becomes 10000 x 3072 6 7 nn = NearestNeighbor() # create a Nearest Neighbor classifier class 8 nn.train(Xtr_rows, Ytr) # train the classifier on the training images and labels 9 Yte_predict = nn.predict(Xte_rows) # predict labels on the test images 10 # and now print the classification accuracy, which is the average number 11 # of examples that are correctly predicted (i.e. label matches) 12 print 'accuracy: %f' % ( np.mean(Yte_predict == Yte) ) 13 14 import numpy as np 15 16 class NearestNeighbor(object): 17 def __init__(self): 18 pass 19 20 def train(self, X, y): 21 """ X is N x D where each row is an example. Y is 1-dimension of size N """ 22 # the nearest neighbor classifier simply remembers all the training data 23 self.Xtr = X 24 self.ytr = y 25 26 def predict(self, X): 27 """ X is N x D where each row is an example we wish to predict label for """ 28 num_test = X.shape[0] 29 # lets make sure that the output type matches the input type 30 Ypred = np.zeros(num_test, dtype = self.ytr.dtype) 31 32 # loop over all test rows 33 for i in xrange(num_test): 34 # find the nearest training image to the i'th test image 35 # using the L1 distance (sum of absolute value differences) 36 distances = np.sum(np.abs(self.Xtr - X[i,:]), axis = 1) 37 min_index = np.argmin(distances) # get the index with smallest distance 38 Ypred[i] = self.ytr[min_index] # predict the label of the nearest example 39 40 return Ypred
3.K-Nearest Neighbors(KNN)
(1)实现思路:与其只找最相近的那1个图片的标签,我们找最相似的k个图片的标签,然后让他们针对测试图片进行投票,最后取票数最高的标签 作为对测试图片的预测。
(2)联系:当k=1的时候,k-Nearest Neighbor分类器就是Nearest Neighbor分类器;从直观感受上就可以看到,k越大时,分类的效果更平滑,使得分类器对于异常值更有抵抗力。
(3)距离度量(Distance Metric)
- L1(Manhattan)距离
各像素差的绝对值的和,受限于坐标轴,表示为$d_1(I_1,I_2)=\sum_{p}|I_1^p-I_2^p|$
- L2(Euclidean)距离
各像素差的平方的和的开方,不受坐标轴限制,表示为$d_1(I_1,I_2)=\sqrt{\sum_{p}(I_1^p-I_2^p)^2}$
(4) 超参数(Hyperparameter)
-
找到最合适的 K 和 distance
-
将数据集分为训练集、验证集和测试集:在训练集上训练模型,在验证集上设置超参数,最终在测试集上评估性能。
测试数据集只使用一次,即在训练完成后评价最终的模型时使用。如果你使用测试集来调优,而且算法看起来效果不错,那么真正的危险在于:算法实际部署后,性能可能会远低于预期,造成过拟合问题。因此,从训练集中取出一部分数据用来调优,称之为验证集(validation set)。验证集其实就是作为假的测试集来调优。程序结束后,我们会作图分析出哪个k值表现最好,然后用这个k值来跑真正的测试集,并作出对算法的评价。
1 # assume we have Xtr_rows, Ytr, Xte_rows, Yte as before 2 # recall Xtr_rows is 50,000 x 3072 matrix 3 Xval_rows = Xtr_rows[:1000, :] # take first 1000 for validation 4 Yval = Ytr[:1000] 5 Xtr_rows = Xtr_rows[1000:, :] # keep last 49,000 for train 6 Ytr = Ytr[1000:] 7 8 # find hyperparameters that work best on the validation set 9 validation_accuracies = [] 10 for k in [1, 3, 5, 10, 20, 50, 100]: 11 12 # use a particular value of k and evaluation on validation data 13 nn = NearestNeighbor() 14 nn.train(Xtr_rows, Ytr) 15 # here we assume a modified NearestNeighbor class that can take a k as input 16 Yval_predict = nn.predict(Xval_rows, k = k) 17 acc = np.mean(Yval_predict == Yval) 18 print 'accuracy: %f' % (acc,) 19 20 # keep track of what works on the validation set 21 validation_accuracies.append((k, acc))
- 交叉验证(Cross-Validation):有时候,训练集数量较小(因此验证集的数量更小),人们会使用一种被称为交叉验证的方法,这种方法更加复杂些。还是用刚才的例子,如果是交叉验证集,我们就不是取1000个图像,而是将训练集平均分成5份,其中4份用来训练,1份用来验证。然后其他4份 各份轮流作为验证集来验证,最后取所有5次验证结果的平均值作为算法验证结果。
(如上图所示:针对每个k值,得到5个准确率结果,取其平均值,然后对不同k值的平均表现画线连接。本例中,当k=7的时算法表现最好(对应图中的准确率峰值)。如果我们将训练集分成更多份数,直线一般会更加平滑(噪音更少))
(5)仅仅使用L1和L2范数来进行像素比较是不够的,图像更多的是按照背景和颜色被分类,而不是语义主体分身
上图使用t-SNE的可视化技术将CIFAR-10的图片进行了二维排列,排列相近的图片L2距离小
参考链接:
[1] https://zhuanlan.zhihu.com/p/20894041?refer=intelligentunit
转载于:https://www.cnblogs.com/jngwl/articles/10263288.html