cs231n:assignment1:KNN解答

      本篇博客是对斯坦福大学课程cs231n中第一个作业assignment1中对k-Nearest Neighbor部分的解答,解答过程使得对Python库numpy的应用更加熟悉,以及对train/val/test集有了更深的了解,对交叉验证法有了一定的掌握。下面直接进入正题:

      KNN算法基本思路:通过测试集与训练集的比较,作业中用的衡量标准是欧氏距离。比如,对于作业中的数据集,其中测试集(test)X是一个[500,3072]的矩阵,其中每一行都是一幅32*32*3大小的图片拉伸成的一个向量,共500张图片,训练集(train)X_train是一个大小为[5000,3072]的矩阵,同理有5000张图片。KNN所做的就是对X中的每一行与X_train中的5000行依次计算欧氏距离,并将距离保存在矩阵dists[500,5000],dists[i,j]即表示X[i,:]与X_train[j,:]的距离。若k=1,对于X中第i幅图,则选取所计算的5000个距离中的最小值,该最小值所对应的X_train中那幅图的类别就作为第i幅图的类别。若k=5,则选取距离最小的5个,其中对应的5张训练集的图片按照其出现次数最多的类别则作为第i幅图的类别。

1、也是在ipython notebook直接调试执行的,很方便,建议大家试一试。关于cifar-10的下载就不说了,打开ipython notebook之后再打开knn.ipynb。下面开始完成作业:首先将所下载的cifar-10数据集所在的路径添加到cifar10_dir中,比如我的:cifar10_dir = '/home/xxx/mydata/CS231n/assignment1/cs231n/datasets/cifar-10-batches-py'。

2、完成距离计算部分。

      打开s231n/classifiers/k_nearest_neighbor.py文件

     (1)compute_distances_two_loops()函数

              def compute_distances_two_loops(self, X):

                    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):

                              dists[i,j] = np.sqrt(np.sum(np.square(self.X_train[j,:] - X[i,:])))   #欧式距离的计算,利用两个循环,将距离存入 

                              pass                                                                                           #dists中,该部分比较简单。

                    return dists

      (2)compute_distances_one_loops()函数

          直接写距离计算部分,其他都和两个循环函数中相同,在k_nearest_neighbor.py文件中都可以看到。

           dists[i,:] = np.sqrt(np.sum(np.square(self.X_train[:,:] - np.tile(X[i,:],(self.X_train.shape[0],1))),1).T) 

            #主要是理解取出X中第i行X[i,:],将其扩展为[5000,3072],使其可以与X_train直接相减,具体理解可以看np.tile()函数,至于             #转置什么的,希望可以拿两个小一点的矩阵推导一下,这样可以使得对Python向量化计算更加熟练。本部分:np.tile()的理               #解,向量化计算的学习

       (3)compute_distances_no_loops()函数

 dists=np.sqrt(np.tile(np.sum(self.X_train*self.X_train,1),(X.shape[0],1))+np.tile(np.sum(X*X,1),(self.X_train.shape[0],1)).T-2*np.dot(X,self.X_train.T))

           #无循环的欧式距离的计算花费了好长时间,主要是因为对Python的向量化操作不熟悉。主要思路是:(A-B)^2=A^2+B^2-2*AB。里面还需要一些扩展和转置操作,还是希望可以推导一下,这里也简单解释一下每个部分。self.X_train*self.X_train与X*X即是计算出A^2与B^2。Python中的相乘*符号是元素对应相乘的。np.tile(np.sum(self.X_train*self.X_train,1),(X.shape[0],1)以及np.tile(np.sum(X*X,1),(self.X_train.shape[0],1)).T是对A^2与B^2矩阵的求和、扩展及转置,还是需要推导一下,文字描述不清楚。2*np.dot(X,self.X_train.T),Python中矩阵的相乘需要利用np.dot()函数,此部分是对2*AB的计算。无循环的欧式距离计算:np.dot()的了解,向量化计算。确实挺麻烦的,再三强调,自己推导一下或利用两个小的矩阵调试一下,对向量化计算真的很有帮助。

       (4)predict_labels()函数

            def predict_labels(self, dists, k=1):

                  num_test = dists.shape[0]

                  y_pred = np.zeros(num_test)

                  for i in xrange(num_test):

                       closest_y = []

                       closest_y = self.y_train[np.argsort(dists[i,:])[0 : k]]   #根据dists,按照大小进行排列,取出最小的5个值,np.argsort()                                                                                                           #函数的理解

                       y_pred[i] = Counter(closest_y).keys()[np.argmax(Counter(closest_y).values())   #选取出最小的5个值中出现次数最多                                                                                                                                                       #的类别作为第i幅图的类别

                       return y_pred

           本部分:np.argsort()、np.argmax().value()、Counter函数的理解

           第一部分告一段落,稍后补上交叉验证法的实现。

3、交叉验证法(Cross-validation)的实现

      主要思路:本部分不考虑测试集,将训练集分成5等份,循环将其中的一份拿来做验证集(和之前的测试集作用相同,即用来测试准确率),剩下的四份为新的训练集,对于不同的k值都会计算出5个准确率,并将5个准确率求平均值可得准确率最大时候的k值。

     (1)将训练集以及训练集的label分为5份

              X_train_folds = np.array_split(X_train, num_folds)

              y_train_folds = np.array_split(y_train, num_folds)     #此部分主要是np.array_split()的使用,详细在此不展开

     (2)计算每个k值的5个准确率,并保存在字典中

              直接贴代码:

num_val = 100
for k in k_choices:
    k_to_accuracies[k] = []     #初始化k的5个准确值列表
    for i in range(num_folds):
        X_train_folds = np.array_split(X_train, num_folds)
        y_train_folds = np.array_split(y_train, num_folds)     #由于pop()函数的应用,每次循环都要重新分为5份
        m = 0
        n = 0
        X_val_cross_val = X_train_folds[i]      #取出第i份作为验证集
        X_train_folds.pop(i)                             #将第i份从训练集中删去,剩下的作为新的训练集
        X_train_cross_val = np.zeros((4000,3072))      #初始化新的训练集
        for p in range(4):                                                #以下两个循环都是因为array_split()函数的应用,需要把剩下的四份重新组合成                                                                                   #一个数组,次部分没找到合适的方法,我所运用的方法很麻烦,新建一个数                                                                                        #组,一个一个重新复制进来,希望之后可以找到更好的方法
            for q in range(1000):
                X_train_cross_val[m,:] = X_train_folds[p][q,:]   #将剩下的四份组合成一个数组
                m = m+1
        y_val_cross_val = y_train_folds[i]             #label的划分,同上相同
        y_train_folds.pop(i)
        y_train_cross_val = np.zeros((4000,))
        for u in range(4):
            for v in range(1000):
                y_train_cross_val[n,] = y_train_folds[u][v,]
                n = n + 1
        classifier.train(X_train_cross_val, y_train_cross_val)       #改变新的训练集和验证集
        dists = classifier.compute_distances_no_loops(X_val_cross_val)    #调用距离计算函数
        y_val_pred = classifier.predict_labels(dists, k)                               #调用预测函数
        num_correct = np.sum(y_val_pred == y_val_cross_val)              #统计正确分类的数量
        accuracy = float(num_correct)/num_val                                        #计算正确率
        k_to_accuracies[k].append(accuracy)                                          #保存正确率

结束。语言组织确实不好,希望以后自己还可以看得懂。

你可能感兴趣的:(python)