准备工作
网上已经有很多关于cs231n作业的讲解和code,所以这里的大多东西都是借鉴他人的成果,但对于个人,一步步的去实现程序比只看代码更能提升能力。本文将会关注KNN分类器在编程实现中的种种问题。
在这篇知乎专栏可以查看cs231n课程的所有笔记资料(中文版)https://zhuanlan.zhihu.com/p/21930884,找到assignment1,下载相应的代码和数据集,我使用的是windows系统,IDE:Pycharm,根据作业建议安装了anaconda。pip安装相应的第三方库。
assignment1中的.ipynb文件能很好的引导你完成作业,同时.ipynb交互式编程也方便重复调试,pip install jupyter后,pycharm就能运行.ipynb文件。
KNN_Classifier
找到knn.ipynb, jupyter 将每段代码分为一个cell,这样有利于单独调试函数。一路run下来,本次作业重点是实现KNN算法,不关注诸如CIFAR10数据集的读入、图形的绘制等问题。
使用两层循环来计算x_test与x_train的距离是容易的,注意求和函数是np.sum
Compute the distance between each test point in X and each training point in self.X_train using a nested loop over both the training data and the test data. Inputs: - X: A numpy array of shape (num_test, D) containing test data. Returns: - dists: A numpy array of shape (num_test, num_train) where dists[i, j] is the Euclidean distance between the ith test point and the jth training point. """ num_test = X.shape[0] num_train = self.X_train.shape[0] dists = np.zeros((num_test, num_train)) for i in xrange(num_test): for j in xrange(num_train): ##################################################################### # TODO: # # Compute the l2 distance between the ith test point and the jth # # training point, and store the result in dists[i, j]. You should # # not use a loop over dimension. # ##################################################################### dists[i][j] = np.sqrt(np.sum((X[i] - self.X_train[j]) ** 2)) ##################################################################### # # END OF YOUR CODE # ##################################################################
predict_labels,分为两步,第一使用包含距离的矩阵找到k个近邻,再标识出k个紧邻的label;第二,将K个label中出现次数最多的标记为当前x_test的label。
第一步,使用np.argsort()函数对每行距离排序并返回索引值,取前K个值,根据索引值找到对应的lable。
第二步,使用np.bincount()和np.argmax()函数,其中
# bincount(x, weights=None, minlength=0) # Count number of occurrences of each value in array of non-negative ints. 按顺序返回count number #a=[1,2,3,3,3,4,5,6] #np.bincount(a) # array([0, 1, 1, 3, 1, 1, 1], dtype=int64) #argmax(a, axis=None, out=None) #Returns the indices of the maximum values along an axis.
one_loop的编写使用到numpy中broadcast的性质,向量[i,:]的写法,np.sum(axis)
for i in xrange(num_test):
dists[i,:] = np.sqrt(np.sum((X[i] - self.X_train) **2, axis=1) )
no_loop的编写主要将距离公式中的平方换成 (a2-2ab+b2)的形式,同样使用到broadcast性质,reshape也可用np.sum(keepdims=Ture)代替
dists += np.sum(X ** 2 ,axis=1).reshape(num_test,1)
dists += np.sum(self.X_train ** 2 ,axis=1)
dists -= 2 * np.dot(X,self.X_train.T)
dists = np.sqrt(dists)
KNN核心代码就此完成,两层循环,一层循环,无循环都是使用numpy的矩阵操作,使用循环利于理解图像距离的计算过程,无循环则大大提升计算速度。
PS:作业中使用交叉检验来验证KNN识别率,其中使用到 numpy.array_split(X,n)方法,其将长度为L的数组X分为n个子数组,包括L%n个长度为(L//n)+1和剩余的长度为L//n的数组