本篇博客是基於以Kaggle中手寫數字識別實戰爲目標,以KNN算法學習爲驅動導向來進行講解。python
##<a name="a">寫這篇博客的緣由</a>git
寫下這篇博客,很大程度上是但願能記錄和督促本身學習機器學習的過程,同時也在之後的學習生活中,能夠將之前的博客翻來看看,從新回顧知識。github
##<a name="b">什麼是KNN?</a>算法
在模式識別和機器學習中,k-近鄰算法(如下簡稱:KNN)是一種經常使用的監督學習中分類方法。KNN能夠說是機器學習算法中最簡單的一個算法,我但願它能帶領你們走進機器學習,瞭解其中最基本的原理,並應用於實際生活中。KNN的工做機制很是簡單,它是一種處理分類和迴歸問題的無參算法,簡而言之就是經過某種距離度量,計算出測試集與訓練集之間的距離,選取前k個最近距離的訓練樣本,從這k箇中選出訓練樣本中出現最多的類型來做爲測試樣本的類型。數組
k-近鄰算法的通常流程 :app
(1)收集數據:可使用任何方法。 (2)準備數據:格式化數據格式。 (3)分析數據:可使用任何方法。 (4)訓練算法:K-近鄰算法不涉及訓練。 (3)測試算法:計算錯誤率。 (3)使用算法:輸入樣本數據,進行分類。機器學習
###名詞解釋與案例分析:函數
####以手寫數字識別爲例進行說明:學習
訓練集:一組有標籤的數字圖像,即每張圖片,咱們都對它進行了標註,代表這張圖片所顯示的數字是多少。在本案例中,全部的圖片都是以矩陣的形式保存在數據集中。測試
測試集:一組沒有標籤的數字圖像,即給出了一組圖片,可是並無對它進行標註,即它的類型是什麼,咱們也不清楚。
分類:好比手寫數字識別中,給出一張圖片,咱們能夠清楚的分辨,上面所寫的數字,可是計算機,並不能有效的識別出來,所以機器學習的一個應用即是讓計算機從已知分類狀況,推斷未知狀況的類別。
迴歸:拿函數來講,一個函數在圖像上是連續,且有必定規律的時候,咱們能夠經過函數去算出未知的狀況。計算機就是經過已知狀況,而後模擬生成一個函數,去擬合這樣一個模型,從而推斷出未知的狀況。
距離度量:歐式距離、曼哈頓距離、切比雪夫距離。
樣本:在本篇博客中,每一個樣本就是一張數字圖片,測試集中的樣本集,即每一張測試樣本都是沒有分類的。而訓練集中的樣本集,都是有明確的分類。
這裏,博主只是使用了最基本的KNN算法進行手寫數字識別,經過計算歐式距離,達到計算機對手寫數字識別和分類。
##<a name="d">kaggle實戰</a> 在Kaggle中,有一場比賽是knowledge類型的。嗯,就決定是你了!
首先從Kaggle中下載訓練集及測試集。點開訓練集,能夠看見訓練集是由42000張數字圖片組成,咱們能夠將它轉換爲一個420001的標籤矩陣和一個42000784的像素矩陣。(注:normaling函數和toInt函數是對返回的數據進行格式化。後面會對函數進行說明。)
# 讀取Train數據 def loadTrainData(): filename = 'train.csv' with open(filename, 'r') as f_obj: f = [x for x in csv.reader(f_obj)] f.remove(f[0]) f = array(f) labels = f[:,0] datas = f[:,1:] # print(shape(labels)) return normaling(toInt(datas)), toInt(labels)
打開測試集。由於測試集並無分類,所以並無標籤。因此能夠將這個測試集轉換爲28000*784的像素矩陣。
#讀取Test數據 def loadTestData(): filename = 'test.csv' with open(filename, 'r') as f_obj: f = [x for x in csv.reader(f_obj)] f.remove(f[0]) f = array(f) return normaling(toInt(f))
前面提到的normaling函數是爲了將數據集進行歸一化,歸一化的目的是爲了解決數據指標之間的可比性,防止某些數據過大,致使分類結果的誤差較大。
#歸一化數據 def normaling(dataSet): minVals = dataSet.min(0) maxVals = dataSet.max(0) ranges = maxVals - minVals m = dataSet.shape[0] denominator = tile(ranges, (m, 1)) molecular = dataSet - tile(minVals, (m, 1)) normData = molecular / denominator return normData
而toInt函數是由於從csv文件中獲得的數據都是字符串類型,可是測算距離度量是對於數值類型的,所以須要將字符串類型轉換爲數值類型。
#字符串數組轉換整數 def toInt(array): array = mat(array) 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
那麼KNN算法的核心就是經過計算測試集中每個測試樣本與訓練集的距離,選取與測試集最近的k個訓練樣本,再從這k個樣本中,選取出現最多的類型做爲訓練樣本的類別。所以計算測試樣本和訓練集之間的距離以下面代碼所示:
# 核心代碼 def k_NN(inX, dataSet, labels, k): dataSetSize = dataSet.shape[0] diffMat = tile(inX, (dataSetSize, 1)) - dataSet sqDiffMat = diffMat**2 sqDistance = sqDiffMat.sum(axis=1) distances = sqDistance**0.5 sortDisn = argsort(distances) # print("sortDisn shape: ",sortDisn.shape) # print("labels shape:",labels.shape) classCount = {} for i in range(k): # print(sortDisn[i]) # print(type(sortDisn[i])) vote = labels[sortDisn[i]] # print("before :",type(vote)) vote = ''.join(map(str, vote)) # print("after :", type(vote)) classCount[vote] = classCount.get(vote, 0) + 1 sortedD = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True) return sortedD[0][0]
將以上的代碼進行整合,便可把測試集的數據進行分類。
#!/user/bin/python3 # -*- coding:utf-8 -*- #@Date :2018/6/30 19:35 #@Author :Syler import csv from numpy import * import operator # 核心代碼 def k_NN(inX, dataSet, labels, k): dataSetSize = dataSet.shape[0] diffMat = tile(inX, (dataSetSize, 1)) - dataSet sqDiffMat = diffMat**2 sqDistance = sqDiffMat.sum(axis=1) distances = sqDistance**0.5 sortDisn = argsort(distances) # print("sortDisn shape: ",sortDisn.shape) # print("labels shape:",labels.shape) classCount = {} for i in range(k): # print(sortDisn[i]) # print(type(sortDisn[i])) vote = labels[sortDisn[i]] # print("before :",type(vote)) vote = ''.join(map(str, vote)) # print("after :", type(vote)) classCount[vote] = classCount.get(vote, 0) + 1 sortedD = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True) return sortedD[0][0] #讀取Train數據 def loadTrainData(): filename = 'train.csv' with open(filename, 'r') as f_obj: f = [x for x in csv.reader(f_obj)] f.remove(f[0]) f = array(f) labels = f[:,0] datas = f[:,1:] # print(shape(labels)) return normaling(toInt(datas)), toInt(labels) #讀取Test數據 def loadTestData(): filename = 'test.csv' with open(filename, 'r') as f_obj: f = [x for x in csv.reader(f_obj)] f.remove(f[0]) f = array(f) return normaling(toInt(f)) #歸一化數據 def normaling(dataSet): minVals = dataSet.min(0) maxVals = dataSet.max(0) ranges = maxVals - minVals m = dataSet.shape[0] denominator = tile(ranges, (m, 1)) molecular = dataSet - tile(minVals, (m, 1)) normData = molecular / denominator return normData #字符串數組轉換整數 def toInt(array): array = mat(array) 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 #保存結果 def saveResult(res): with open('res.csv', 'w', newline='') as fw: writer = csv.writer(fw) writer.writerows(res) if __name__ == '__main__': dataSet, labels = loadTrainData() testSet = loadTestData() row = testSet.shape[0] # print("dataSet Shape:",dataSet.shape) # print("labels Shape before",shape(labels)) labels = labels.reshape(labels.shape[1],1) # print("labels Shape after reshape ", shape(labels)) # print("testSet Shape",testSet.shape) resList = [] for i in range(row): res = k_NN(testSet[i], dataSet, labels, 4) resList.append(res) print(i) saveResult(resList)
那麼把這個數據結果提交到Kaggle上,結果如何呢?
總的來講,此次結果仍是很滿意的。畢竟KNN算法算是機器學習算法中比較基礎的一個算法,可以達到97.185%的準確率,且有66%的排名已經算是很不錯的啦~
###<a name="e">優勢:</a> 簡單、易於理解,易於實現,無需訓練。 適合對稀有事件進行分類。 特別使用於多分類問題,KNN比SVM的表現更好。 ###<a name="e">缺點:</a> KNN算法是基於實例的學習或者說是一種「懶惰學習」。使用算法的時候,咱們必須有儘可能接近實際數據的訓練樣本數據,這很大程度是由於它並無訓練模型這樣一個步驟,致使它必須保存全部數據集。一旦數據集很大,將致使大量的存儲空間。並且加上每次對樣本的分類或迴歸,都要對數據集中每一個數據計算距離值,實際使用會很是耗時。其次,它受「噪聲」影響很大,尤爲是樣本不平衡的時候,會致使分類的結果誤差很大。加上它的另外一個缺陷是沒法給出任何數據的基礎結構信息,並不能知道測試集與訓練集之間具備什麼特徵。 ###<a name="e">優化方法</a> 如今KNN算法的改進主要分紅分類效率和分類效果兩方面。 一種流行的增長精準率的方法是使用進化算法去優化特徵範圍。 另外一種則是經過各類啓發式算法,去選取一個適合的K值。 不論是分類仍是迴歸,都是根據距離度量來進行加權,使得鄰近值更加平均。 ##<a name="f">總結 </a> KNN算法對於分類數據是最簡單最有效的算法,它能幫助咱們迅速瞭解監督學習中的分類算法的基本模型。 ##<a name="g">參考</a> <a href="https://baike.baidu.com/item/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0%E5%AE%9E%E6%88%98/12344225?fr=aladdin">《機器學習實戰》</a> <a href="https://book.douban.com/subject/26708119/">《機器學習》</a> <a href="https://en.wikipedia.org/wiki/K-nearest_neighbors_algorithm">維基百科</a>
Github地址:<a href="https://github.com/578534869/machine-learning">https://github.com/578534869/machine-learning</a> (歡迎follow,互相學習,共同進步!:-) )