cs231n assigment1 KNN部分程式碼執行結果及分析
這是cs231n的課程作業,通過在網上尋找的各個程式碼解析以及自己執行程式碼後,對於KNN特徵提取有了更加深刻的瞭解,也對python語言有了初步的認識。
在之前的課程也對KNN演算法簡單介紹過,KNN演算法比較簡單:在最近的K個樣本中選擇概率最大的作為測試的值(需要進行距離判斷,可以用歐氏距離、曼哈頓距離等距離度量方法)。更具體的可參考http://blog.csdn.net/zhyh1435589631/article/details/53875182
下面進行具體的knn 呼叫程式 程式碼分析:
1、data_utils 載入資料集
這裡選用的資料集是 cifar-10 資料集 http://www.cs.toronto.edu/~kriz/cifar.html
簡單介紹下cifar-10 資料集:
①cifar-10 資料集包含60000個32*32的彩色影象,共有10類,每類有6000個影象。其中包含50000個訓練影象和10000個測試影象。 資料集分為5個訓練塊和1個測試塊,每個塊有10000個影象。測試塊是從每類隨機選出的1000個影象組成的。而訓練塊以隨機的順序由剩下的這些影象組成,但對於同一類,某一些訓練塊可能包含比其它塊更多的影象,但總體裡說訓練塊從每一類中都取出了5000個影象(也就是說訓練塊取影象的時候是不均勻的)。
②data——1個10000*3072大小的uint8陣列。陣列的每行儲存1張32*32的影象。第1個1024包含紅色通道值,下1個包含綠色,最後的1024包含藍色。 我覺得[1]中的程式碼:X = X.reshape(10000, 3, 32, 32)括號裡的數字就是這個意思
labels——1個10000數的範圍為0~9的列表。索引i的數值表示陣列data中第i個影象的標籤。
③資料集中包含另外1個叫batches.meta的檔案。它也包含1個Python字典物件。有如下列元素:
label_names——1個10元素的列表,給labels中的數值標籤以有意義的名稱。例如,label_names[0] == “airplane”, label_names[1] == “automobile”等。
輸出相應的訓練集和測試集資料 Xtr, Ytr, Xte, Yte(training,test)
2、 載入資料集的呼叫
方框中的是程式碼,下面的是執行結果
這一部分程式主要就是呼叫資料,設定影象的大小等。輸出訓練集和測試集資料的大小性質,data shape的第一個數字是訓練集/測試集中影象數量,第二和第三個表示影象的畫素大小,第四個數字表示通道值(我覺得是這樣)
3、顯示資料集的一部分資訊
第一行程式碼定義了各類的名稱,第二行是類的數量,第三行設定了每類顯示多少樣本(這裡是每類顯示7個樣本),然後for迴圈是為了完成顯示選中樣本的功能,enumerate()是列舉函式,返回索引和值,比如for y, cls in enumerate(classes)執行後y=1,2……;cls='plane', 'car'……;numpy.flatnonzero()函式可以用來返回某個特定元素的位置
4、調整資料集大小
5、使用KNN
KNN訓練需要用到KNearestNeighbor 類。可以將訓練分成兩個步驟(1)計算所有訓練樣本和測試樣本之間的距離(2)對每個測試樣本找k個最近的樣本,從中選出投票最多的那個類別。
6、knn 本質實現部分的程式碼
這部分程式碼是kNN訓練重點
訓練分類器,KNN分類器計算的是歐氏距離,輸入X和y,X是包含num_train個D維訓練資料的陣列,y是包含訓練標籤的陣列
預測資料,輸入測試資料陣列X,k的數值(選擇用來投票的最近鄰居的個數),迴圈階數num_loops,返回包含測試資料對應的預測標籤的陣列y,如果迴圈階數是0,就呼叫compute_distances_no_loops函式來計算距離,如果迴圈階數是1,就呼叫compute_distances_one_loops函式,迴圈階數是2,就呼叫compute_distances_two_loops函式,否則就輸出錯誤資訊。
這個函式主要通過兩層 for 迴圈對計算測試集與訓練集資料之間的歐式距離。這裡計算歐氏距離用到了dot()函式。dot(a, b),對於二維陣列,它相當於矩陣乘法;而一維陣列是向量的內積(無複共軛); 對於N維,它是a的最後一個維度和b的倒數第二維的乘積。dot(a, b)[i,j,k,m] = sum(a[i,j,:] * b[k,:,m])
參考https://docs.scipy.org/doc/numpy/reference/generated/numpy.dot.html
所以上面的dists[i,j]等價於np.sqrt(np.sum(np.square(self.X_train[j,:] - X[i,:])))
本質上這裡填入的程式碼和 上一節中的是一致的, 只是多了一個 axis = 1 指定方向
這部分程式碼有一定難度,參考http://blog.csdn.net/geekmanong/article/details/51524402和http://blog.csdn.net/zhyh1435589631/article/details/54236643
所以dists = np.sqrt(self.getNormMatrix(X, num_train).T + self.getNormMatrix(self.X_train, num_test) - 2 * np.dot(X, self.X_train.T))
這部分實現的功能是根據計算得到的距離關係, 挑選 K 個數據組成選民, 進行黨派選舉。
其中argsort函式返回的是陣列值從小到大的索引值,即對於一維陣列來說,第一個元素是原陣列中數值最小的元素的索引(從0開始排列),二維陣列可以根據自己的意願確定排列方向。
如 >>> x = np.array([3, 1, 2])
>>> np.argsort(x)
array([1, 2, 0])
7、測試兩層迴圈的距離演算法
測試的圖片為500張,訓練集為5000
8、用圖片的形式顯示距離
9、設定k=1時使用KNN進行訓練
得到正確率為0.274,500個影象對了137
10、修改k引數後使用KNN進行訓練
k=5時,正確率為0.278
11、用一層迴圈的距離演算法進行訓練
呼叫之前一層迴圈的距離演算法進行KNN訓練,發現與兩層迴圈得到的歐氏距離矩陣一致
12、用無迴圈的距離演算法進行訓練
呼叫之前無迴圈的距離演算法進行KNN訓練,發現與兩層迴圈得到的歐氏距離矩陣一致
13、檢視三種距離計算方法的使用時間
比較三種距離計算方法(無迴圈、一層迴圈和兩層迴圈)所需要的時間,發現無迴圈的計算時間最短,其次是一層迴圈的,兩層迴圈計算時間最長。說明迴圈非常耗費時間。
14、cross validation交叉驗證
交叉驗證實際上是將資料的訓練集進行拆分, 分成多個組, 構成多個訓練和測試集, 來篩選較好的超引數
比如說將訓練集分為 5組資料(依次將 fold 1, 2 .. 5 作為訓練集, 將剩餘的資料作為驗證集, 訓練得到超引數)
先篩選不同的K
numpy.array_split函式用於把陣列分成幾份,預設axis=0(豎直),用法參考https://docs.scipy.org/doc/numpy/reference/generated/numpy.array_split.html
合併陣列函式:numpy.vstack(垂直方向)和hstack(水平方向)
15、圖形化顯示
16、選取最好的k 進行訓練
可以發現, 即使是最好情況下, KNN演算法的識別準確率也很低,只有27.4%