1 KNN算法python
1.1 KNN算法簡介git
KNN(K-Nearest Neighbor)工做原理:存在一個樣本數據集合,也稱爲訓練樣本集,而且樣本集中每一個數據都存在標籤,即咱們知道樣本集中每一數據與所屬分類對應的關係。輸入沒有標籤的數據後,將新數據中的每一個特徵與樣本集中數據對應的特徵進行比較,提取出樣本集中特徵最類似數據(最近鄰)的分類標籤。通常來講,咱們只選擇樣本數據集中前k個最類似的數據,這就是k近鄰算法中k的出處,一般k是不大於20的整數。最後選擇k個最類似數據中出現次數最多的分類做爲新數據的分類。github
說明:KNN沒有顯示的訓練過程,它是「懶惰學習」的表明,它在訓練階段只是把數據保存下來,訓練時間開銷爲0,等收到測試樣本後進行處理。算法
舉例:以電影分類做爲例子,電影題材可分爲愛情片,動做片等,那麼愛情片有哪些特徵?動做片有哪些特徵呢?也就是說給定一部電影,怎麼進行分類?這裏假定將電影分爲愛情片和動做片兩類,若是一部電影中接吻鏡頭不少,打鬥鏡頭較少,顯然是屬於愛情片,反之爲動做片。有人曾根據電影中打鬥動做和接吻動做數量進行評估,數據以下:windows
電影名稱數組 |
打鬥鏡頭app |
接吻鏡頭機器學習 |
電影類別ide |
Califoria Man函數 |
3 |
104 |
愛情片 |
Beautigul Woman |
1 |
81 |
愛情片 |
Kevin Longblade |
101 |
10 |
動做片 |
Amped II |
98 |
2 |
動做片 |
給定一部電影數據(18,90)打鬥鏡頭18個,接吻鏡頭90個,如何知道它是什麼類型的呢?KNN是這樣作的,首先計算未知電影與樣本集中其餘電影的距離(這裏使用曼哈頓距離),數據以下:
電影名稱 |
與未知分類電影的距離 |
Califoria Man |
20.5 |
Beautigul Woman |
19.2 |
Kevin Longblade |
115.3 |
Amped II |
118.9 |
如今咱們按照距離的遞增順序排序,能夠找到k個距離最近的電影,加入k=3,那麼來看排序的前3個電影的類別,愛情片,愛情片,動做片,下面來進行投票,這部未知的電影愛情片2票,動做片1票,那麼咱們就認爲這部電影屬於愛情片。
1.2 KNN算法優缺點
優勢:精度高,對異常值不敏感、無數據輸入假定
缺點:計算複雜度高、空間複雜度高
1.3 KNN算法python代碼實現
實現步驟:
(1)計算距離
(2)選擇距離最小的k個點
(3)排序
Python 3代碼:
1 import numpy as np 2 import operator 3 4 def classify(intX,dataSet,labels,k): 5 ''' 6 KNN算法 7 ''' 8 #numpy中shape[0]返回數組的行數,shape[1]返回列數 9 dataSetSize = dataSet.shape[0] 10 #將intX在橫向重複dataSetSize次,縱向重複1次 11 #例如intX=([1,2])--->([[1,2],[1,2],[1,2],[1,2]])便於後面計算 12 diffMat = np.tile(intX,(dataSetSize,1))-dataSet 13 #二維特徵相減後乘方 14 sqdifMax = diffMat**2 15 #計算距離 16 seqDistances = sqdifMax.sum(axis=1) 17 distances = seqDistances**0.5 18 print ("distances:",distances) 19 #返回distance中元素從小到大排序後的索引 20 sortDistance = distances.argsort() 21 print ("sortDistance:",sortDistance) 22 classCount = {} 23 for i in range(k): 24 #取出前k個元素的類別 25 voteLabel = labels[sortDistance[i]] 26 print ("第%d個voteLabel=%s",i,voteLabel) 27 classCount[voteLabel] = classCount.get(voteLabel,0)+1 28 #dict.get(key,default=None),字典的get()方法,返回指定鍵的值,若是值不在字典中返回默認值。 29 #計算類別次數 30 31 #key=operator.itemgetter(1)根據字典的值進行排序 32 #key=operator.itemgetter(0)根據字典的鍵進行排序 33 #reverse降序排序字典 34 sortedClassCount = sorted(classCount.items(),key = operator.itemgetter(1),reverse = True) 35 #結果sortedClassCount = [('動做片', 2), ('愛情片', 1)] 36 print ("sortedClassCount:",sortedClassCount) 37 return sortedClassCount[0][0]
2 KNN算法實例
2.1 KNN實現電影分類
1 import numpy as np 2 import operator 3 4 def createDataset(): 5 #四組二維特徵 6 group = np.array([[5,115],[7,106],[56,11],[66,9]]) 7 #四組對應標籤 8 labels = ('動做片','動做片','愛情片','愛情片') 9 return group,labels 10 11 def classify(intX,dataSet,labels,k): 12 ''' 13 KNN算法 14 ''' 15 #numpy中shape[0]返回數組的行數,shape[1]返回列數 16 dataSetSize = dataSet.shape[0] 17 #將intX在橫向重複dataSetSize次,縱向重複1次 18 #例如intX=([1,2])--->([[1,2],[1,2],[1,2],[1,2]])便於後面計算 19 diffMat = np.tile(intX,(dataSetSize,1))-dataSet 20 #二維特徵相減後乘方 21 sqdifMax = diffMat**2 22 #計算距離 23 seqDistances = sqdifMax.sum(axis=1) 24 distances = seqDistances**0.5 25 print ("distances:",distances) 26 #返回distance中元素從小到大排序後的索引 27 sortDistance = distances.argsort() 28 print ("sortDistance:",sortDistance) 29 classCount = {} 30 for i in range(k): 31 #取出前k個元素的類別 32 voteLabel = labels[sortDistance[i]] 33 print ("第%d個voteLabel=%s",i,voteLabel) 34 classCount[voteLabel] = classCount.get(voteLabel,0)+1 35 #dict.get(key,default=None),字典的get()方法,返回指定鍵的值,若是值不在字典中返回默認值。 36 #計算類別次數 37 38 #key=operator.itemgetter(1)根據字典的值進行排序 39 #key=operator.itemgetter(0)根據字典的鍵進行排序 40 #reverse降序排序字典 41 sortedClassCount = sorted(classCount.items(),key = operator.itemgetter(1),reverse = True) 42 #結果sortedClassCount = [('動做片', 2), ('愛情片', 1)] 43 print ("sortedClassCount:",sortedClassCount) 44 return sortedClassCount[0][0] 45 46 47 48 if __name__ == '__main__': 49 group,labels = createDataset() 50 test = [20,101] 51 test_class = classify(test,group,labels,3) 52 print (test_class)
2.2 改進約會網站匹配
這個例子簡單說就是經過KNN找到你喜歡的人,首先數據樣本包含三個特徵,(a)每一年得到的飛行常客里程數(b)玩遊戲消耗的時間(c)每週消耗的冰激淋公升數,樣本數據放在txt中,以下,前三列爲三個特徵值,最後一列爲標籤
首先讀取數據,獲取數據集和標籤
1 def file2matrix(filename): 2 fr = open(filename) 3 arraylines = fr.readlines() 4 #獲取行數 5 numberoflines = len(arraylines) 6 #返回numpy的數據矩陣,目前矩陣數據爲0 7 returnMat = np.zeros([numberoflines,3]) 8 #返回的分類標籤 9 classLabelVector = [] 10 #行的索引 11 index = 0 12 for line in arraylines: 13 #str.strip(rm) 刪除str頭和尾指定的字符 rm爲空時,默認刪除空白符(包括'\n','\r','\t',' ') 14 line = line.strip() 15 #每行數據是\t劃分的,將每行數據按照\t進行切片劃分 16 listFromLine = line.split('\t') 17 #取出前三列數據存放到returnMat 18 returnMat[index,:] = listFromLine[0:3] 19 #根據文本中標記的喜歡程度進行分類 20 if listFromLine[-1] == "didntLike": 21 classLabelVector.append(1) 22 elif listFromLine[-1] == "smallDoses": 23 classLabelVector.append(2) 24 else: 25 classLabelVector.append(3) 26 index += 1 27 return returnMat,classLabelVector
數據和標籤咱們能夠打印一下:
下面用Matplotlib做圖看一下數據信息:
1 from matplotlib.font_manager import FontProperties 2 import numpy as np 3 import matplotlib.pyplot as plt 4 from prepareData_1 import file2matrix 5 import matplotlib.lines as mlines 6 # from matplotlib.font_manage import FontProperties 7 ''' 8 函數說明:數據可視化 9 Parameters: 10 datingDataMat - 特徵矩陣 11 datingLabels - 分類標籤向量 12 Returns: 13 無 14 ''' 15 def showDatas(datingDataMat,datingLabels): 16 #設置漢子格式 17 font = FontProperties(fname=r"c:\windows\fonts\simsun.ttc", size=14) 18 #函數返回一個figure圖像和一個子圖ax的array列表。 19 fig,axs = plt.subplots(nrows=2,ncols=2,sharex=False,sharey=False,figsize=(13,8)) 20 21 numberofLabels = len(datingLabels) 22 LabelColors = [] 23 for i in datingLabels: 24 if i==1: 25 LabelColors.append('black') 26 if i ==2: 27 LabelColors.append('orange') 28 if i==3: 29 LabelColors.append("red") 30 #畫散點圖,以數據矩陣的第一列(飛行常客歷程)、第二列(玩遊戲)數據話散點圖 31 #散點大小爲15 透明度爲0.5 32 axs[0][0].scatter(x=datingDataMat[:,0],y=datingDataMat[:,1],color=LabelColors, 33 s=15,alpha=0.5) 34 axs0_title_text=axs[0][0].set_title(u"每一年得到的飛行里程數與玩視頻遊戲消耗時間佔比", 35 FontProperties=font) 36 axs0_xlabel_text=axs[0][0].set_xlabel("每一年得到的飛行常客里程數",FontProperties=font) 37 axs0_ylabel_text=axs[0][0].set_ylabel("玩遊戲消耗的時間",FontProperties=font) 38 plt.setp(axs0_title_text,size=9,weight='bold',color='red') 39 #畫散點圖,以數據矩陣的第一列(飛行常客歷程)、第三列(冰激淋公斤數)數據話散點圖 40 #散點大小爲15 透明度爲0.5 41 axs[0][1].scatter(x=datingDataMat[:,0],y=datingDataMat[:,2],color=LabelColors, 42 s=15,alpha=0.5) 43 axs0_title_text=axs[0][0].set_title("每一年得到的飛行里程數與冰激淋公斤數佔比", 44 FontProperties=font) 45 axs0_xlabel_text=axs[0][0].set_xlabel("每一年得到的飛行常客里程數",FontProperties=font) 46 axs0_ylabel_text=axs[0][0].set_ylabel("所吃冰激淋公斤數",FontProperties=font) 47 plt.setp(axs0_title_text,size=9,weight='bold',color='red') 48 #畫散點圖,以數據矩陣的第二列(玩遊戲)、第三列(冰激淋公斤數)數據話散點圖 49 #散點大小爲15 透明度爲0.5 50 axs[1][0].scatter(x=datingDataMat[:,1],y=datingDataMat[:,2],color=LabelColors, 51 s=15,alpha=0.5) 52 axs0_title_text=axs[0][0].set_title("玩遊戲時間與冰激淋公斤數佔比", 53 FontProperties=font) 54 axs0_xlabel_text=axs[0][0].set_xlabel("每一年得到的飛行常客里程數",FontProperties=font) 55 axs0_ylabel_text=axs[0][0].set_ylabel("所吃冰激淋公斤數",FontProperties=font) 56 plt.setp(axs0_title_text,size=9,weight='bold',color='red') 57 58 #設置圖例 59 didntLike = mlines.Line2D([],[],color='black',marker='.',markersize=6,label='didntlike') 60 smallDose = mlines.Line2D([],[],color='orange',marker='.',markersize=6,label='smallDose') 61 largeDose = mlines.Line2D([],[],color='red',marker='.',markersize=6,label='largeDose') 62 63 #添加圖例 64 axs[0][0].legend(handles=[didntLike,smallDose,largeDose]) 65 axs[0][1].legend(handles=[didntLike,smallDose,largeDose]) 66 axs[1][0].legend(handles=[didntLike,smallDose,largeDose]) 67 68 plt.show() 69 70 if __name__ == '__main__': 71 filename = "datingTestSet.txt" 72 returnMat,classLabelVector = file2matrix(filename) 73 showDatas(returnMat,classLabelVector) 74 75
這裏我把py文件分開寫了,還要注意txt數據的路徑,高大上的圖:
樣本數據中的到底喜歡什麼樣子的人?本身去分析一下吧。下面要對數據進行歸一化,歸一化的緣由就很少說了,
1 from prepareData_1 import file2matrix 2 import numpy as np 3 ''' 4 函數說明:數據歸一化 5 Parameters: 6 dataSet - 特徵矩陣 7 Returns: 8 normDataSet - 歸一化後的特徵矩陣 9 ranges - 數據範圍 10 minVals - 數據最小值 11 ''' 12 13 def autoNorm(dataSet): 14 #得到數據的最大最小值 15 print (dataSet) 16 print ("**********************") 17 minVals = dataSet.min(0) 18 maxVals = dataSet.max(0) 19 print ("minValues:",minVals) 20 print ("maxValuse:",maxVals) 21 #計算最大最小值的差 22 ranges = maxVals - minVals 23 print () 24 #shape(dataSet)返回dataSet的矩陣行列數 25 normDataSet=np.zeros(np.shape(dataSet)) 26 #返回dataSet的行數 27 m = dataSet.shape[0] 28 #原始值減去最小值 29 normDataSet=dataSet-np.tile(minVals,(m,1)) 30 #除以最大值和最小值的差,獲得的歸一化的數據 31 normDataSet = normDataSet/np.tile(ranges,(m,1)) 32 return normDataSet,ranges,minVals
歸一化後的數據以下:
有了以上步驟,下面就能夠構建完整的約會分類,去找你喜歡的人了:
1 from prepareData_1 import file2matrix 2 from dataNormal_3 import autoNorm 3 import operator 4 import numpy as np 5 ''' 6 函數說明:knn算法,分類器 7 Parameters: 8 inX - 用於分類的數據(測試集) 9 dataset - 用於訓練的數據(訓練集) 10 labes - 分類標籤 11 k - knn算法參數,選擇距離最小的k個點 12 Returns: 13 sortedClassCount[0][0] - 分類結果 14 ''' 15 def classify0(inX,dataset,labes,k): 16 dataSetSize = dataset.shape[0] #返回行數 17 diffMat = np.tile(inX,(dataSetSize,1))-dataset 18 sqDiffMat = diffMat**2 19 sqDistances = sqDiffMat.sum(axis=1) 20 distances = sqDistances**0.5 21 sortedDistIndices =distances.argsort() 22 classCount = {} 23 for i in range(k): 24 voteLabel = labes[sortedDistIndices[i]] 25 classCount[voteLabel] = classCount.get(voteLabel,0)+1 26 sortedClassCount = sorted(classCount.items(),key=operator.itemgetter(1),reverse=True) 27 return sortedClassCount[0][0] 28 def datingClassTest(): 29 #filename="test.txt" 30 filename = "datingTestSet.txt" 31 datingDataMat,datingLabels = file2matrix(filename) 32 #取全部數據的10% 33 hoRatio = 0.1 34 #數據歸一化,返回歸一化後的矩陣,數據範圍,數據最小值 35 normMat,ranges,minVals = autoNorm(datingDataMat) 36 #得到nornMat的行數 37 m = normMat.shape[0] 38 #百分之十的測試數據的個數 39 numTestVecs = int(m*hoRatio) 40 #分類錯誤計數 41 errorCount = 0.0 42 43 for i in range(numTestVecs): 44 #前numTestVecs個數據做爲測試集,後m-numTestVecs個數據做爲訓練集 45 classifierResult = classify0(normMat[i,:],normMat[numTestVecs:m,:], 46 datingLabels[numTestVecs:m],10) 47 print ("分類結果:%d \t真實類別:%d"%(classifierResult,datingLabels[i])) 48 if classifierResult != datingLabels[i]: 49 errorCount += 1.0 50 print ("錯誤率:%f"%(errorCount/float(numTestVecs)*100)) 51 52 if __name__ == '__main__': 53 datingClassTest()
都是上面的步驟,這裏就不解釋了,結果以下所示:
2.3 手寫數字識別
數據能夠樣例能夠打開文本文件進行查看,其中txt文件名的第一個數字爲本txt中的數字,目錄trainingDigits中包含了大約2000個例子,每一個數字大約有200個樣本,testDigits中包含900個測試數據,咱們使用trainingDigits中的數據訓練分類器,testDigits中的數據做爲測試,兩組數據沒有重合。
數據在這裏:https://github.com/Jenny0611/Ml_Learning01
首先咱們要將圖像數據處理爲一個向量,將32*32的二進制圖像信息轉化爲1*1024的向量,再使用前面的分類器,代碼以下:
1 import numpy as np 2 import operator 3 from os import listdir 4 from sklearn.neighbors import KNeighborsClassifier as kNN 5 6 ''' 7 函數說明:將32*32的二進制圖片轉換爲1*1024向量 8 Parameters: 9 filename - 文件名 10 Returns: 11 returnVect - 返回的二進制圖像的1*1024向量 12 ''' 13 def img2vector(filename): 14 #建立1*1024的0向量 15 returnVect = np.zeros((1,1024)) 16 fr = open(filename) 17 #按行讀取 18 for i in range(32): 19 #讀一行數據 20 lineStr=fr.readline() 21 #每一行的前32個數據依次添加到returnVect 22 for j in range(32): 23 returnVect[0,32*i+j]=int(lineStr[j]) 24 return returnVect 25 26 ''' 27 函數說明:手寫數字分類測試 28 Parameters: 29 filename - 無 30 Returns: 31 returnVect - 無 32 ''' 33 def handwritingClassTest(): 34 #測試集的labels 35 hwLabels=[] 36 #返回trainingDigits目錄下的文件名 37 trainingFileList=listdir('trainingDigits') 38 #返回文件夾下文件的個數 39 m=len(trainingFileList) 40 #初始化訓練的Mat矩陣的測試集 41 trainingMat=np.zeros((m,1024)) 42 #從文件名中解析出訓練集的類別 43 for i in range(m): 44 fileNameStr=trainingFileList[i] 45 classNumber = int(fileNameStr.split('_')[0]) 46 #將獲取的類別添加到hwLabels中 47 hwLabels.append(classNumber) 48 #將每個文件的1*1024數據存儲到trainingMat矩陣中 49 trainingMat[i,:]=img2vector('trainingDigits/%s'%(fileNameStr)) 50 #構建KNN分類器 51 neigh = kNN(n_neighbors=3,algorithm='auto') 52 #擬合模型,trainingMat爲測試矩陣,hwLabels爲對應的標籤 53 neigh.fit(trainingMat,hwLabels) 54 #返回testDigits目錄下的文件列表 55 testFileList=listdir('testDigits') 56 errorCount=0.0 57 mTest=len(testFileList) 58 #從文件中解析出測試集的類別並進行分類測試 59 for i in range(mTest): 60 fileNameStr=testFileList[i] 61 classNumber=int(fileNameStr.split('_')[0]) 62 #得到測試集的1*1024向量用於訓練 63 vectorUnderTest=img2vector('testDigits/%s'%(fileNameStr)) 64 #得到預測結果 65 classifierResult=neigh.predict(vectorUnderTest) 66 print ("分類返回結果%d\t真實結果%d"%(classifierResult,classNumber)) 67 if (classNumber != classifierResult): 68 errorCount += 1.0 69 print ("總共錯了%d個\t錯誤率爲%f%%"%(errorCount,errorCount/mTest*100)) 70 71 if __name__ == '__main__': 72 handwritingClassTest()
2.4 小結
KNN是簡單有效的分類數據算法,在使用時必須有訓練樣本數據,還要計算距離,若是數據量很是大會很是消耗空間和時間。它的另外一個缺陷是沒法給出任何數據的基礎結構信息,所以咱們沒法平均實例樣本和典型實例樣本具體特徵,而決策樹將使用機率測量方法處理分類問題,之後章節會介紹。
本文參考:http://blog.csdn.net/c406495762/article/details/75172850
《機器學習實戰》