CS231n第一次作業_問題1

在這裏插入圖片描述

深度學習第一次作業

由於notebook環境的配置較爲麻煩,我直接使用pycharm配置本地的python環境完成了cs231n課堂的第一次作業任務。下面是具體的任務要求:

  • Q1:k-最近鄰分類器

  • Q2:訓練一個SVM

  • Q3:實現Softmax分類器

  • Q4:實現兩層神經網絡

  • Q5:更高層次的表達:圖像特徵

  • Q6:加分:做點其他的

    接下來一個一個問題的分析並完成代碼的編寫,在cs231n課堂上給出了大致的代碼框架,這裏也會沿用框架,但是需要自己去理解和構建整個工程。

    所有代碼都上傳到github中,會不斷更新

Q1 :k-最近鄰分類器

原理簡介

最近鄰分類器

分爲兩步:

  • 記住所有的訓練樣本
  • 將測試樣本和訓練樣本一個個的對比,找到最相似的圖片
    問題:容易受到噪聲的影響
    在這裏插入圖片描述

k-最近鄰分類器

在最近鄰分類器基礎上尋找前k個和測試樣本相似的圖片,然後根據這k個圖片的類別確定測試樣本,可一定程度上避免噪聲的影響。

代碼實現

最近鄰分類器

  1. 樣本的讀取

    這裏採用和原文一樣的數據集,放置的目錄爲\assiment1\datasets\cifar-10-batches-py

    新建文件data_utils.py,在文件中實現讀取數據集的操作。

    import pickle as p
    import numpy as np
    import os
    

讀取單個文件中的圖片

def load_CIFAR_batch(filename):
 with open(filename, 'rb') as f:
     datadict = p.load(f, encoding='latin1')
     X = datadict['data']
     Y = datadict['labels']
     X = X.reshape(10000, 3, 32, 32).transpose(0, 2, 3, 1).astype("float")
     Y = np.array(Y)
     return X, Y

讀取所有文件中的圖片,注意到數據集中共有5個文件

def load_CIFAR10(ROOT):
 xs = []
 ys = []
 for b in range(1, 6):
     f = os.path.join(ROOT, 'data_batch_%d' % (b,))
     X , Y = load_CIFAR_batch(f)
     xs.append(X)         # 將所有batch整合起來
     ys.append(Y)
 Xtr = np.concatenate(xs)  # 使變成行向量,最終Xtr的尺寸爲(50000,32,32,3)
 Ytr = np.concatenate(ys)
 del X, Y
 Xte, Yte = load_CIFAR_batch(os.path.join(ROOT, 'test_batch'))
 return Xtr, Ytr, Xte, Yte
  1. 把所有數據集分類,有的作爲訓練集有的是測試集,有的則是調參使用的驗證集

    新建文件,load_Data.py

    在這裏實現所有有關數據集的操作,從而獲得三類數據集。後面也會用在其他的算法驗證上,避免重複的工作。

    import numpy as np
    import matplotlib.pyplot as plt
    from data_utils import load_CIFAR10
    

    設置好讀取參數然後讀取所有的數據集,所有隨機在各類裏面選擇一些圖片顯示出來,從而驗證自己能成功讀取到數據集數據。

    class load_data(object):
    
        def __init__(self):
            self.X_val = 0
            self.X_train = 0
            self.y_train = 0
            self.X_test = 0
            self.y_test = 0
            self.y_val = 0
            plt.rcParams['figure.figsize'] = (10.0, 8.0)
            plt.rcParams['image.interpolation'] = 'nearest'
            plt.rcParams['image.cmap'] = 'gray'
    
            # 載入CIFAR-10數據集
            cifar10_dir = 'datasets/cifar-10-batches-py'
            self.X_train, self.y_train, self.X_test, self.y_test = load_CIFAR10(cifar10_dir)
    
            # 看看數據集中的一些樣本
            print('Training data shape: ', self.X_train.shape)
            print('Training labels shape: ', self.y_train.shape)
            print('Test data shape: ', self.X_test.shape)
            print('Test labels shape: ', self.y_test.shape)
    
            #  在各類中選擇7個樣本測試數據集是否讀取成功
            classes = ['plane', 'car', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck']
            num_classes = len(classes)
            samples_per_class = 7
            for y, cls in enumerate(classes):
                idxs = np.flatnonzero(self.y_train == y)
                idxs = np.random.choice(idxs, samples_per_class, replace=False)
                for i, idx in enumerate(idxs):
                    plt_idx = i * num_classes + y + 1
                    plt.subplot(samples_per_class, num_classes, plt_idx)
                    plt.imshow(self.X_train[idx].astype('uint8'))
                    plt.axis('off')
                    if i == 0:
                        plt.title(cls)
            plt.show()
            pass
    
        def data_generate(self, train_num, num_validation, num_test):
            #  隨機產生numValidation個樣本作爲驗證數據
            mask = range(train_num, train_num + num_validation)
            self.X_val = self.X_train[mask]
            self.y_val = self.y_train[mask]
            #  隨機產生trainNum個樣本作爲訓練數據
            mask = range(train_num)
            self.X_train = self.X_train[mask]
            self.y_train = self.y_train[mask]
            #  隨機產生numTest個樣本作爲測試數據
            mask = range(num_test)
            self.X_test = self.X_test[mask]
            self.y_test = self.y_test[mask]
    
            # 把圖像信息變成一維的向量
            self.X_train = np.reshape(self.X_train, (self.X_train.shape[0], -1))
            self.X_test = np.reshape(self.X_test, (self.X_test.shape[0], -1))
            self.X_val = np.reshape(self.X_val, (self.X_val.shape[0], -1))
            print(self.X_train.shape, self.X_test.shape, self.X_val.shape)
            return self.X_train, self.y_train, self.X_val, self.y_val, self.X_test, self.y_test
    

    如果成果讀取出數據的話,會顯示出這樣一張圖片
    在這裏插入圖片描述

    調用函數時只需選擇訓練集,測試集等的數量即可

    from load_data import load_data
    test = load_data()
    X_train, y_train, X_val, y_val, X_test, y_test = test.data_generate(4900, 100, 1000)
    
  2. 實現分類器

    新建文件nearest_neighbor.py,實現分類器的算法

    首先是訓練部分,當然很簡單只需要讓代碼記住所有的數據就好了,這裏在構造函數直接就把數據存下來

        def __init__(self, x_train, y_train):
            self.x_train = x_train
            self.y_train = y_train
            pass
    

    然後就是計算距離,這裏採用矩陣的開根號等numpy的內部實現

        def compute_distances(self, test):
            num_test = test.shape[0]
            num_train = self.x_train.shape[0]
            dists = np.zeros((num_test, num_train))
            dists = np.sqrt(-2 * np.dot(test, self.x_train.T) + np.sum(np.square(self.x_train), axis=1) + np.transpose(
                [np.sum(np.square(test), axis=1)]))
            return dists
    

    可以看到基本上就是把下式用矩陣的方式實現了
    testtrain2=test2+train22×testTtrain \left\|test-train\right\|_2=\sqrt{test^{2}+train^{2}-2\times test^{T}\cdot train}
    最後一步就是實現預測部分了,原理上倒是很簡單。找到距離最近的圖片,然後找到那個圖片對應的屬性並預測兩個屬性一樣就好。

        def predict_labels(self, test, k=1):
            dists = self.compute_distances(test)
            num_test = dists.shape[0]
            y_prediction = np.zeros(num_test)
            for i in range(num_test):
                closest_y = self.y_train[np.argsort(dists[i])[:1]]
                y_prediction[i] = closest_y
            return y_prediction
    

    但是可以發現代碼並不是很容易看懂,主要是應用了幾個numpy的函數,下面可以仔細分析一下

        >>> x = np.array([3, 1, 2])
        >>> np.argsort(x)
        array([1, 2, 0])
    

    可以看出argsort函數首先把[3,1,2]從小到大排序,然後返回對應元素的序號。

    排序後[1,2,3],則1的序號是1,2的序號是2,3的序號是0,則返回[1,2,0]

    所以closest_y = self.y_train[np.argsort(dists[i])[:1]] 找到最小距離的圖片的序號。

    最終新建nn.py,運行下面代碼可以得到預測的準確率。

    import numpy as np
    from load_data import load_data
    from nearest_neighbor import nearest_neighbor
    test = load_data()
    X_train, y_train, X_val, y_val, X_test, y_test = test.data_generate(4900, 100, 1000)
    nn = nearest_neighbor(X_train, y_train)
    pre = nn.predict_labels(X_test)
    
    num_correct = np.sum(pre == y_test)
    accuracy = float(num_correct) / 500
    print('Got %d / %d correct => accuracy: %f' % (num_correct, 500, accuracy))
    

    運行得到結果

    Got 269 / 500 correct => accuracy: 0.538000

k-最近鄰分類器

由於噪聲的影響,不一定最近的就是同一種物品,所以提出k-最近鄰分類器。思路就是取前k個較爲近的圖片,然後統計這k個圖片中各類別的數量,認爲和最多數量的類別是同一類。

在代碼上只用改預測部分將其預測的邏輯修改一下便可。這裏新定義一個類,繼承原來的最臨近分類器,重寫預測的函數。

    def predict_labels(self, test, k=1):
        dists = self.compute_distances(test)
        num_test = dists.shape[0]
        y_prediction = np.zeros(num_test)
        for i in range(num_test):
            closest_y = self.y_train[np.argsort(dists[i])[:k]]
            y_prediction[i] = np.argmax(np.bincount(closest_y))
        return y_prediction

可以說結構和nn中一致,不同的是 closest_y = self.y_train[np.argsort(dists[i])[:k]] 取出前k個數據的標籤,然後bincount函數統計各個標籤出現的次數,然後argmax函數找到出現次數最多的標籤,返回給y_prediction[i]。

新建knn.py,運行如下代碼得到結果

import numpy as np
import matplotlib.pyplot as plt

from load_data import load_data
from k_nearest_neighbor import k_nearest_neighbor

test = load_data()
X_train, y_train, X_val, y_val, X_test, y_test = test.data_generate(4900, 100, 1000)
knn = k_nearest_neighbor(X_train, y_train)
accuracy = [11]
for i in range(1, 11):
    pre = knn.predict_labels(X_test, i)
    num_correct = np.sum(pre == y_test)
    print(i)
    print(num_correct)
    accuracy.append(float(float(num_correct)/500.0))
x = np.linspace(1, 1, 11)
print(accuracy)

[0.538, 0.456, 0.522, 0.536, 0.528, 0.54, 0.544, 0.554, 0.556, 0.554]

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章