基於KNN對CIFAR-10數據集分類

基於KNN對CIFAR-10數據集分類

由於本人正在學習機器學習方面的知識,這篇文章記錄了我第一次用公開數據集進行分類的整個過程,所用的語言為python3。由於自己一直喜歡在知乎上查閱資料,在閱讀別人的文章過程中收穫了很多知識,作為一枚喜歡學習編程的小夥子,也想嘗試寫文章一方面記錄自己學到的東西,另一方面也希望能夠幫助到同樣喜歡編程的有需要的小夥伴們。話不多說,獻上第一篇文章!


這篇文章的主要結構如下:

  1. 介紹對CIFAR-10數據集的讀取
  2. 介紹KNN演算法
  3. 提取數據的三種不同的特徵,分別為RGB像素值特徵,RGB像素值經過PCA變換的特徵以及HOG特徵,並用KNN對數據集進行分類,比較三種方法的效果。

1 CIFAR-10數據集的讀取

實驗中用到的數據集為可以到此處下載:

CIFAR-10數據集?

www.cs.toronto.edu

CIFAR-10數據集共包括了10個類別,訓練樣本有50000張圖像,分別存放在5個batch裡面,測試集有10000張圖像,存放在test_batch中。

下面介紹用python3對數據進行讀取:

1.先解壓下載好的數據集到相對路徑中,文件名為cifar-10-batches-py。打開文件夾後可以看到裡面有以下幾個文件。

其中batches.meta記錄了數據集中十個類別的對應信息,data_batch_1到data_batch_5存在了訓練樣本,test_batch存放了測試樣本,具體信息可查看上面的鏈接。

2.數據集官網上提供了python3讀取CIFAR-10的方式,以下函數可以將數據集轉化為字典類型:

def unpickle(file): import pickle with open(file, rb) as fo: dict = pickle.load(fo, encoding=bytes) return dict

由於數據集中每張圖像為32x32,有RGB3個通道,按照RGB通道順序以及每一通道按照行的順序已排列好,一個訓練樣本對應一行有32x32x3=3072個值。用以上的unpickle函數處理後會返回一個字典dict,dict中的key中有數據和標籤,value為對應的值。如dict[bdata]返回一個unit8的10000x3072維numpy的array。dict[blabels]返回一個長度為10000的list,list中的值為0-9,每個數字表示一個類別,具體對應類別可由batchs.meta獲取。

自己寫了一個函數讀取CIFAR-10中的訓練樣本以及測試樣本,函數返回值為4個ndarray。其中traindata為50000x3072,trainlabels為50000x1,testdata為10000x3072,testlabels為10000x1。

import numpy as npimport os#創建訓練樣本和測試樣本def CreatData(): #創建訓練樣本 #依次載入batch_data_i,併合併到x,y x=[] y=[] for i in range(1,6): batch_path=cifar-10-batches-pydata_batch_%d%(i) batch_dict=unpickle(batch_path) train_batch=batch_dict[bdata].astype(float) train_labels=np.array(batch_dict[blabels]) x.append(train_batch) y.append(train_labels) #將5個訓練樣本batch合併為50000x3072,標籤合併為50000x1 #np.concatenate默認axis=0,為縱向連接 traindata=np.concatenate(x) trainlabels=np.concatenate(y) #創建測試樣本 #直接寫cifar-10-batches-py est_batch會報錯,因此把/t當作製表符了,應用\; # test_dict=unpickle("cifar-10-batches-py\test_batch") #建議使用os.path.join()函數 testpath=os.path.join(cifar-10-batches-py,test_batch) test_dict=unpickle(testpath) testdata=test_dict[bdata].astype(float) testlabels=np.array(test_dict[blabels]) return traindata,trainlabels,testdata,testlabels

2 KNN演算法實現

KNN演算法的實現步驟如下:

1.計算待分類的測試數據的特徵向量與已知類別的訓練樣本的特徵向量的歐氏距離。距離公式為:

2.將距離從小到大排序。

3.去前k個值並統計k個值中每個類別出現的頻數。

4.頻數最大的訓練樣本的類別即為測試樣本的與預測類別。

實現代碼如下,該代碼存放於KNN.py中,後面用到KNN時,需要把KNN.py放在相對路徑,輸入from KNN.py import KNN 即可使用該函數:

import numpy as np#knn分類函數,輸入訓練樣本,訓練樣本標籤,測試樣本(為ndarray),k值(k默認為3);返回預測標籤def KNN(traindata,trainlabels,testdata,k=3): #計算距離,存放在dist中,每一行表示測試樣本與所有訓練樣本的距離 num_train=traindata.shape[0] num_test=testdata.shape[0] dist=np.zeros((num_test,num_train)) for i in range(num_test): dist[i]=np.reshape(np.sqrt(np.sum(np.square(testdata[i]-traindata),axis=1)),[1,num_train]) #找到每一行中從小到大排序後前k個值所對應的原來的索引,對應的標籤給close_k #統計這些索引中出現次數最多的那個數為預測樣本類別 predictlabels=np.zeros((num_test,1)) for i in range(num_test): close_k=trainlabels[np.argsort(dist[i])[:k]] predictlabels[i]=np.argmax(np.bincount(close_k)) return predictlabels

3 提取圖像三種不同特徵並用KNN分類比較三種方法的效果

為了節省時間,3組實驗中用了前10000個訓練樣本和前5000個測試樣本。實驗中設置了不同的K值進行,每個K個得到一個分類精度,最後將精度可視化,並求得最大精度。

1.提取數據集的RGB像素值特徵並用KNN進行分類。

實現程序如下所示:

運行該程序前需要將前一步中的KNN.py文件存放於當前路徑(相對路徑),才能調用KNN函數。

#從當前路徑下的KNN.py載入KNN函數from KNN import KNNimport numpy as npimport osimport timeimport matplotlib.pyplot as pltstart_time=time.clock()#將cifar-10中的數據解壓成字典類型def unpickle(file): import pickle with open(file, rb) as fo: dict = pickle.load(fo, encoding=bytes) return dict#創建訓練樣本和測試樣本def CreatData(): #創建訓練樣本 #依次載入batch_data_i,併合併到x,y x=[] y=[] for i in range(1,6): batch_path=cifar-10-batches-pydata_batch_%d%(i) batch_dict=unpickle(batch_path) train_batch=batch_dict[bdata].astype(float) train_labels=np.array(batch_dict[blabels]) x.append(train_batch) y.append(train_labels) #將5個訓練樣本batch合併為50000x3072,標籤合併為50000x1 #np.concatenate默認axis=0,為縱向連接 traindata=np.concatenate(x) trainlabels=np.concatenate(y) #創建測試樣本 #直接寫cifar-10-batches-py est_batch會報錯,因此把/t當作製表符了,應用\; # test_dict=unpickle("cifar-10-batches-py\test_batch") #建議使用os.path.join()函數 testpath=os.path.join(cifar-10-batches-py,test_batch) test_dict=unpickle(testpath) testdata=test_dict[bdata].astype(float) testlabels=np.array(test_dict[blabels]) return traindata,trainlabels,testdata,testlabels#創建訓練樣本和測試樣本traindata1,trainlabels1,testdata1,testlabels1=CreatData()#print(traindata:,traindata.shape)#print(trainlabels:,trainlabels.shape)#print(testdata:,testdata.shape)#print(testlabels:,testlabels.shape)num_train=10000num_test=5000traindata=traindata1[:num_train]trainlabels=trainlabels1[:num_train]testdata=testdata1[:num_test]testlabels=testlabels1[:num_test]num_test=testdata.shape[0]#K的取值k_choice=[5,10,15,20,40,100]#k_choice=[10]k_accuracy=[]for k_c in k_choice: #預測標籤 predictlabels=KNN(traindata,trainlabels,testdata,k=k_c) testlabels=np.reshape(testlabels,[num_test,1]) #計算精度 num_right=np.sum((predictlabels==testlabels).astype(float)) accuracy=(num_right/num_test)*100 k_accuracy.append(accuracy) print(k=%d:accuracy=%.2f%%%(k_c,accuracy)) #將結果可視化plt.figure(figsize=(10,6))plt.plot(k_choice,k_accuracy,r-)plt.plot(k_choice,k_accuracy,go)plt.xlabel(k)plt.ylabel(accuracy(%))plt.title(k choice)plt.show()#記錄最大精度以及對應K值max_id=np.argmax(k_accuracy)max_acc=k_accuracy[max_id]max_acc_k=k_choice[max_id]print(

max_acc=%.2f%%,max_acc_k=%d%(max_acc,max_acc_k))end_time=time.clock()print(

運行時間:%ss%(str(end_time-start_time)))

2.提取數據集的RGB像素值並通過PCA變換獲取特徵並用KNN進行分類。實驗中設置了不同的主成分數以及不同的K值。

實現程序如下所示:

運行該程序前需要將前一步中的KNN.py文件存放於當前路徑(相對路徑),才能調用KNN函數。

#從當前路徑下的KNN.py載入KNN函數from KNN import KNNimport numpy as npimport osimport timeimport matplotlib.pyplot as pltfrom sklearn.decomposition import PCAstart=time.clock()#將cifar-10中的數據解壓成字典類型def unpickle(file): import pickle with open(file, rb) as fo: dict = pickle.load(fo, encoding=bytes) return dict#創建訓練樣本和測試樣本def CreatData(): #創建訓練樣本 #依次載入batch_data_i,併合併到x,y x=[] y=[] for i in range(1,6): batch_path=cifar-10-batches-pydata_batch_%d%(i) batch_dict=unpickle(batch_path) train_batch=batch_dict[bdata].astype(float) train_labels=np.array(batch_dict[blabels]) x.append(train_batch) y.append(train_labels) #將5個訓練樣本batch合併為50000x3072,標籤合併為50000x1 #np.concatenate默認axis=0,為縱向連接 traindata=np.concatenate(x) trainlabels=np.concatenate(y) #創建測試樣本 #直接寫cifar-10-batches-py est_batch會報錯,因此把/t當作製表符了,應用\; # test_dict=unpickle("cifar-10-batches-py\test_batch") #建議使用os.path.join()函數 testpath=os.path.join(cifar-10-batches-py,test_batch) test_dict=unpickle(testpath) testdata=test_dict[bdata].astype(float) testlabels=np.array(test_dict[blabels]) return traindata,trainlabels,testdata,testlabels#創建訓練樣本和測試樣本traindata1,trainlabels1,testdata1,testlabels1=CreatData()num_train=10000num_test=5000traindata=traindata1[:num_train]trainlabels=trainlabels1[:num_train]testdata=testdata1[:num_test]testlabels=testlabels1[:num_test]#用PCA變換#pca=PCA(n_components=307,copy=True)#num_pca=pca.n_components#pca_traindata=pca.fit_transform(traindata)#print(num_pca)#print(pca_traindata.shape)#記錄最高精度max_acc,以及對應的HOG尺寸max_acc_cellsize和K值max_acc_Kmax_acc=0max_acc_n_components=0max_acc_k=0#設置不同的kk_choice=[5,10,15,20,40,100]#k_choice=[10] for k_c in k_choice: print(

k=,k_c) #設置不同的pca成分數 num_components=[100,700,1000,2000,3000,3072]# num_components=[100,300,700,1000] #對應精度 size_accuracy=[] for num_c in num_components: #提取訓練樣本的PCA特徵 pca=PCA(n_components=num_c,copy=True) pca_traindata=pca.fit_transform(traindata) #提取測試樣本的PCA特徵 pca_testdata=pca.fit_transform(testdata) #預測標籤 predictlabels=KNN(pca_traindata,trainlabels,pca_testdata,k=k_c) testlabels=np.reshape(testlabels,[num_test,1]) #計算精度 num_right=np.sum((predictlabels==testlabels).astype(float)) accuracy=(num_right/num_test)*100 print(n_components=%d:accuracy=%.2f%%%(num_c,accuracy)) size_accuracy.append(accuracy) max_id=np.argmax(size_accuracy) if size_accuracy[max_id]>max_acc: max_acc=size_accuracy[max_id] max_acc_n_components=num_components[max_id] max_acc_k=k_c #將結果可視化 plt.figure(figsize=(12,8)) plt.plot(num_components,size_accuracy,r-) plt.plot(num_components,size_accuracy,go) plt.xlabel(n_components) plt.ylabel(accuracy(%)) plt.title(k=%d%(k_c)) plt.show()print(

The result:max_acc=%.2f%%,max_acc_n_components=%d,max_acc_k=%d%(max_acc,max_acc_n_components,max_acc_k))end=time.clock()print(運行時間為:%ss%(str(end-start)))

3.提取數據集的HOG特徵並用KNN進行分類。實驗中設置了不同的cellsize以及不同的K值。

實現程序如下所示:

運行該程序前需要將前一步中的KNN.py文件存放於當前路徑(相對路徑),才能調用KNN函數。

# -*- coding: utf-8 -*-"""Created on Sat May 5 22:25:12 2018@author: tph"""#從當前路徑下的KNN.py載入KNN函數from KNN import KNNimport numpy as npimport osimport timeimport matplotlib.pyplot as pltfrom PIL import Imagefrom skimage import feature as ftstart=time.clock()#將cifar-10中的數據解壓成字典類型def unpickle(file): import pickle with open(file, rb) as fo: dict = pickle.load(fo, encoding=bytes) return dict#創建訓練樣本和測試樣本def CreatData(): #創建訓練樣本 #依次載入batch_data_i,併合併到x,y x=[] y=[] for i in range(1,6): batch_path=cifar-10-batches-pydata_batch_%d%(i) batch_dict=unpickle(batch_path) train_batch=batch_dict[bdata] train_labels=np.array(batch_dict[blabels]) x.append(train_batch) y.append(train_labels) #將5個訓練樣本batch合併為50000x3072,標籤合併為50000x1 #np.concatenate默認axis=0,為縱向連接 traindata=np.concatenate(x) trainlabels=np.concatenate(y) #創建測試樣本 #直接寫cifar-10-batches-py est_batch會報錯,因此把/t當作製表符了,應用\; # test_dict=unpickle("cifar-10-batches-py\test_batch") #建議使用os.path.join()函數 testpath=os.path.join(cifar-10-batches-py,test_batch) test_dict=unpickle(testpath) testdata=test_dict[bdata] testlabels=np.array(test_dict[blabels]) return traindata,trainlabels,testdata,testlabels#創建訓練樣本和測試樣本traindata1,trainlabels1,testdata1,testlabels1=CreatData()num_train=10000num_test=5000traindata=np.reshape(traindata1[:num_train],[num_train,3,32,32])trainlabels=trainlabels1[:num_train]testdata=np.reshape(testdata1[:num_test],[num_test,3,32,32])testlabels=testlabels1[:num_test]#提取HOG特徵函數,輸入data為[num,3,32,32]格式,size為cell尺寸;返回data_hogfeature為[num,dim]def hog_extraction(data,size): num=data.shape[0] #提取訓練樣本的HOG特徵 data1_hogfeature=[] for i in range(num): x=data[i] r=Image.fromarray(x[0]) g=Image.fromarray(x[1]) b=Image.fromarray(x[2]) #合併三通道 img=Image.merge("RGB",(r,g,b)) #轉為灰度圖 gray=img.convert(L)# out=gray.resize((100,100),Image.ANTIALIAS) #轉化為array gray_array=np.array(gray) #提取HOG特徵 hogfeature=ft.hog(gray_array,pixels_per_cell=(size,size)) data1_hogfeature.append(hogfeature) #把data1_hogfeature中的特徵按行堆疊 data_hogfeature=np.reshape(np.concatenate(data1_hogfeature),[num,-1]) return data_hogfeature#記錄最高精度max_acc,以及對應的HOG尺寸max_acc_cellsize和K值max_acc_Kmax_acc=0max_acc_cellsize=0max_acc_k=0#設置不同的kk_choice=[5,10,15,20,40,100]#k_choice=[15]for k_c in k_choice: print(

k=,k_c) #設置不同的HOG cellsize cellsize=[4,6,8,9,10]# cellsize=[10] size_accuracy=[] for size in cellsize: #提取訓練樣本的HOG特徵 train_hogfeature=hog_extraction(traindata,size) #提取測試樣本的HOG特徵 test_hogfeature=hog_extraction(testdata,size) num_hogfeature=test_hogfeature.shape[1] #預測標籤 predictlabels=KNN(train_hogfeature,trainlabels,test_hogfeature,k=k_c) testlabels=np.reshape(testlabels,[num_test,1]) #計算精度 num_right=np.sum((predictlabels==testlabels).astype(float)) accuracy=(num_right/num_test)*100 print(cellsize=(%d,%d),num_hogfeature=%d:accuracy=%.2f%%%(size,size,num_hogfeature,accuracy)) size_accuracy.append(accuracy) max_id=np.argmax(size_accuracy) if size_accuracy[max_id]>max_acc: max_acc=size_accuracy[max_id] max_acc_cellsize=cellsize[max_id] max_acc_k=k_c #將結果可視化 plt.figure(figsize=(12,8)) plt.plot(cellsize,size_accuracy,r-) plt.plot(cellsize,size_accuracy,go) plt.xlabel(cellsize) plt.ylabel(accuracy(%)) plt.title(k=%d%(k_c)) plt.show()print(

The result:max_acc=%.2f%%,max_acc_cellsize=(%d,%d),max_acc_k=%d%(max_acc,max_acc_cellsize,max_acc_cellsize,max_acc_k))end=time.clock()print(運行時間為:%ss%(str(end-start)))

提取數據集的RGB像素值特徵並用KNN進行分類的實驗結果如下:

當k=20時,取得最高精度為29.70%。

提取數據集的RGB像素值並通過PCA變換獲取特徵並用KNN進行分類的實驗結果如下,這裡只列出了k=5的不同主成分數的精度:

當k=5,n_components=700時,取得最高精度為7.74%。

提取數據集的HOG特徵並用KNN進行分類的實驗結果如下圖所示,這裡只列出了k=10時的不同cellsize的精度:

當k=10,cellsize=(10,10)時,取得最高精度為41.46%。

結果分析:

從以上的結果可知,三種方法中提取HOG特徵用於分類的精度最高,為41.46%;提取RGB特徵用於分類的精度次之,為29.70%;而提取圖像RGB再用PCA變換後的特徵用於分類的精度最低,為7.74%,該精度比我們去猜的精度10%還低,有點尷尬。之後會繼續推出使用其它機器學習方法或者深度學習方法對該數據集分類的實驗文章。


一個喜歡學習編程的小夥子的第一篇文章,有什麼錯誤請指正,另外有什麼好的建議或者意見也歡迎在評論區提,以便寫出更高質量的文章和大家相互交流~~

推薦閱讀:

深度學習筆記-Part3
簡單理解機器學習:正則化
那麼多人都在進行大數據培訓?你到底還在猶豫什麼?
Kaggle Titanic 生存預測(Top1.4%)完整代碼分享

TAG:機器學習 | 數據挖掘 | 深度學習DeepLearning |