1. 程式人生 > >Kaggle KNN實現Digit Recognizer

Kaggle KNN實現Digit Recognizer

本文參考了https://blog.csdn.net/u012162613/article/details/41929171,然後總結一下自己的理解。主要從資料準備、資料分析、核心演算法三個方面介紹。

資料準備

Kaggle官網中搜索Digit Recognizer,從‘Data’中下載csv檔案:train.csv,test.csv,sample_submission.csv。

從github下載knn_benchmark.csv(官方給出的參考結果,可與自己訓練的結果對比)https://github.com/clytwynec/digit_recognition/blob/master/data/knn_benchmark.csv

資料分析

訓練集和測試集是包含0-9共10個手寫數字的灰度影象,每張圖的畫素是28*28=784,每個畫素有一個單一的畫素值,畫素值是0-255的整數。

train.csv

train.csv是訓練樣本集,大小42001*785,第一行是文字描述,所以實際的樣本資料大小是42000*785。其中第一列的每一個數字是它對應行的label,可以將第一列單獨取出來,得到42000*1的向量trainLabel,剩下的就是42000*784的特徵向量集trainData。程式碼如下:

def loadTrainData():
    l=[]
    with open('train.csv') as file:   #open()返回了一個檔案物件file
         lines=csv.reader(file)   #reader()返回reader()物件lines,lines是一個列表
         for line in lines:    #按行加入到l列表中
             l.append(line)    #42001*785
    l.remove(l[0])    #第一行是文字描述,刪除第一行
    l=array(l)     #列表轉換為陣列
    label=l[:,0]    #取l中所有行的第0個數據
    data=l[:,1:]    #取l中所有行,第一列到最後一列的資料
    return nomalizing(toInt(data)),toInt(label)  #label 1*42000  data 42000*784

其中有兩個函式需要說明一下,toInt()函式,是將字串轉換為整數,因為從csv檔案讀取出來的,是字串型別的,而運算過程中需要整數型別的,因此需要轉換。toInt()函式如下:

def toInt(array):   
    array=mat(array)  #用mat()轉換為矩陣之後可以進行一些線性代數的操作
    m,n=shape(array)
    newArray=zeros((m,n))
    for i in range(m):
        for j in range(n):
                newArray[i,j]=int(array[i,j])
    return newArray

nomalizing()函式做的工作是歸一化,因為train.csv裡面提供的表示影象的資料是0~255的,為了簡化運算,我們可以將其轉化為二值影象,因此將所有非0的數字,即1~255都歸一化為1。nomalizing()函式如下:

def nomalizing(array):
    m,n=shape(array)
    for i in range(m):
        for j in range(n):
            if array[i,j]!=0:
                array[i,j]=1
    return array

test.csv

test.csv裡的資料大小是28001*784,第一行是文字描述,因此實際的測試資料樣本是28000*784,與train.csv不同,沒有label,28000*784即28000個測試樣本,需要做的工作就是為這28000個測試樣本找出正確的label。

knn_benchmark.csv

knn_benchmark.csv裡的資料是28001*2,第一行是文字說明,可以去掉,第一列表示圖片序號1~28000,第二列是圖片對應的數字。

核心演算法

採用KNN演算法來分類。K最近鄰分類演算法是資料探勘分類技術中最簡單的方法之一。kNN演算法的核心思想是如果一個樣本在特徵空間中的k個最相鄰的樣本中的大多數屬於某一個類別,則該樣本也屬於這個類別,並具有這個類別上樣本的特性。本文衡量樣本間的距離使用的是歐式距離。核心程式碼:

def classify(inX, dataSet, labels, k):
    inX=mat(inX)   #inX是輸入的單個樣本
    dataSet=mat(dataSet)   #訓練樣本集
    labels=mat(labels)    #對應的標籤向量
    dataSetSize = dataSet.shape[0]    #得出dataSet的行數,即樣本個數              
    diffMat = tile(inX, (dataSetSize,1)) - dataSet   #tile(A,(m,n))將陣列A作為元素構造m行n列的陣列
    sqDiffMat = array(diffMat)**2
    sqDistances = sqDiffMat.sum(axis=1)    #array.sum(axis=1)按行累加,axis=0為按列累加              
    distances = sqDistances**0.5
    sortedDistIndicies = distances.argsort()   #array.argsort()返回陣列值從小到大的索引值         
    classCount={}                                                               
    for i in range(k):
        voteIlabel = labels[sortedDistIndicies[i],0]  
        classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1   #get(key,x)從字典中獲取key對應的value,沒有key的話返回0
    sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True)   #sorted()函式,按照第二個元素即次序逆向(reverse=True)排序
    return sortedClassCount[0][0]

簡單說明一下,inX就是輸入的單個樣本,是一個特徵向量。dataSet是訓練樣本,對應上面的trainData,labels對應trainLabel,k是knn演算法選定的k,一般選擇0~20之間的數字。這個函式將返回inX的label,即圖片inX對應的數字。

對於測試集裡28000個樣本,呼叫28000次這個函式即可。

執行結果

以上使用了20000個訓練樣本進行訓練,最後預測的錯誤率為3.5%。