在介绍k-最近邻算法之前,先来点背景知识介绍:由于计算机不能像人脑一样看到一个东西可以自动分辨出这个东西是啥,我们需要一种方法去“训练”它---识别算法。但是对于自然界的物体,不同物种形态不一致,而同一个物种之间也可能有不同的形态,同时拍摄的地点,光线以及物体被遮掩的程度不同,单纯地对每一种物体写具体的分类规则十分费时,且不能涵盖所有可能的情况,最终会导致分辨的失误。基于此,我们采取了“数据驱动方式”---Data Driven approach
-
Data-Driven approach
步骤:
- collect a dataset of images and labels
- use machine learning to train a classifier
- evaluate the classifier on new images
基本框架:
def train(images, labels):
#machine learning
return model
def predict(model, test_images):
#use model to predict labels
return test_labels
训练函数(train)接受我们在网上爬取的照片,以及对应照片的labels,输出模型。预测函数(predict)利用训练好的模型,接受未被分类的新照片,并预测他们对应的labels。
-
k-Nearest Neighbor
基于Data Driven approach,我们讨论第一个classifier,也是最简单的classifier---k-Nearest Neighbor。(文末附代码)
基本思路:
- memorize all data and labels
- predict the label of the most similar training images
就是在分类器记住了所有的图片和对应的labels之后,当我们传入一张照片,根据这张照片和它记住的所有照片的相似度进行对比,找出k张最接近的照片,对新照片进行label的投票。如果k张照片中,大部分的照片都属于同一个label,那么可以预测这张传入的照片也属于这个label。
对于comparing images:
可以使用Distance Metric比较。
L1 distance:
即test image和所有的training image之间单个像素点对点地相减,得到绝对值(这个位置上像素的差别),再求和。L1表现的是两个点在标准坐标系上的绝对轴距总和。
L2 distance:
选择不同的distance方法:L1 distance的计算取决于坐标轴的设置,而改变坐标轴对L2没有任何影响。如果不同的input features对结果有特别的影响,有实际的意义,可以考虑用L1计算距离。如果无法分辨,可以两个都试试,看哪个效果更好。
评估:
假设我们让k-Nearest Neighbor在训练的时候记住了n张照片,在预测的时候,一张test image需要和n张training images进行L1距离检测,这会导致在预测的时候,效率非常地低。
k的取值问题:k过小,鲁棒性不强。(例如:在训练的时候,可能会有噪声或失真信号---孤立点,如果在预测的时候,对应这张test image的最邻近的照片是孤立点,很有可能是误判。)
在图像处理中一般不使用k-Nearest Neighbor,因为单纯的利用距离判断图片之间的视觉相似性并不合适。可能两张有差别的图片,和某一张training image之间的L1距离是一样的。
curse of dimensionality(维度灾难):想要这个分类器达到尽可能好的效果,我们需要采集一定数量的input features来训练。实际上,为了避免过拟合,每增加一种feature(相当于增加一种维度),对应的training data数量随着维度的增加呈指数型增长。这是在机器学习中最不希望看到的结果(并不一定有足够的样本用于训练)。因此,特征数量超过一定的值之后,分类器的效果反而下降。而在k-Nearest Neighbor中,predict时将考虑实例的所有属性,如果目标分辨仅依靠很多属性的其中几个,那么真正视觉上最相似的图片可能相距很远,除非用很多的training images,但是又会导致维度灾难。
-
代码
import numpy as np
class NearestNeighbor:
def __init__(self):
pass
def train(self,X,y):
self.Xtr=X
self.ytr=y
def predict(self,X):
num_test=X.shape[0]
Ypred=np.zeros(num_test,dtype=self.ytr.dtype)
for i in xrange(num_test):
distances=np.sum(np.abs(self.Xtr - X[i,:]),axis=1)
min_index=np.argmin(distances)
Ypred[i]=self.ytr[min_index]
returm Ypred
-
补充
超参数(hyper parameters):没有办法直接从数据中学习到,需要人为地设置(choices about the algorithm that we set rather than learn,very problem-depending),例如这里的k值。
train->validation->test dataset:将所有的数据分为三组(实际中基本不是均分),选择不同的超参数,在training set中进行训练,用validation set进行选择,通过观察有不同超参数的models的结果和对应的labels进行比较来选择效果最好的一组,即完成了所有的调试,最后再用test set进行最终的评估---how your algorithm does well in the unseen dataset。一般我们需要分离validation set和test set。
cross-validation:基本用于小数据集,instead spliting data into training set and validation set, we split data into different folds, try each fold as validation and average the results.
例如:在前四份folds上训练超参数,在第五份fold上验证。(大型模型不适用,消耗算力)