Python 3实现k-邻近算法以及 iris 数据集分类应用
前言
這個周基本在琢磨這個算法以及自己利用Python3 實現自主編程實現該算法。持續時間比較長,主要是Pyhton可能還不是很熟練,走了很多路,基本是一邊寫一邊學。不過,總算是基本搞出來了。不多說,進入正題。
1. K-鄰近算法
1.1 基本原理
k近鄰法(k-nearest neighbor, k-NN)是1967年由Cover T和Hart P提出的一種基本分類與回歸方法。它的工作原理是:存
在一個樣本數據集合,也稱作為訓練樣本集,并且樣本集中每個數據都存在標簽,即我們知道樣本集中每一個數據與所屬分
類的對應關系。輸入沒有標簽的新數據后,將新的數據的每個特征與樣本集中數據對應的特征進行比較,然后算法提取樣本
最相似數據(最近鄰)的分類標簽。一般來說,我們只選擇樣本數據集中前k個最相似的數據,這就是k-近鄰算法中k的出處,
通常k是不大于20的整數。最后,選擇k個最相似數據中出現次數最多的分類,作為新數據的分類。
1.2 一個例子
先來看一個圖:
在這個圖里面,我們可以看到有第三種顏色標記的標簽,藍色正方形,紅色三角形以及一個未知類型的綠色原點。那么如何判斷這
個綠色點是屬于藍色家族的還是紅色家族的呢?
鄰近的思想就是計算這個綠色的點分別到它附近的點的距離,距離近就判定屬于這個類型,那么K-鄰近就是讓待分類的這個點與所
有的已經分類點的距離,然后選取K個點,統計待分類的這個綠色點屬于哪個類別的數量比較多,就最終判定這個點屬于哪一個。
再回到圖,首先是K=3,可以看到實線里面有兩個紅點,一個藍點,那么判定這個綠家伙屬于紅色的三角形類型。接著,選取了近
距離綠色點最近的5個點,這時,會發現,藍色系占得更多,所以,判定這個綠家伙是屬于藍色正方形的類型。
從這個例子可以看出來,K-鄰近的幾個基本關鍵點有:
點之間的距離計算
d12=∑i=1n(x1i?x2i)???????????√
曼哈頓距離:
兩個向量a(x11,xx12,?,x1n)與b(x21,xx22,?,x2n)的曼哈頓距離為:
其他
參考http://www.cnblogs.com/xbinworld/archive/2012/09/24/2700572.html
里面有著更加詳細的關于距離的介紹。
距離排序
在這個計算的過程中,需要將最終的計算進行一個排序的。為下一步操作做好準備。
K的選擇
很明顯,對于這算法,K的選取決定了整個算法分類預測的準確性,可以說是其核心參數。從上面的例子也可以看出來,K=3和K=5得到的決然不同的結果的。
1.3 算法步驟
(1)初始化距離
(2)計算待分類樣本和每個訓練集的距離
(3)排序
(4)選取K個最鄰近的數據
(5)計算K個訓練樣本的每類標簽出現的概率
(6)將待分類樣本歸為出現概率最大的標簽,分類結束。
2. Python實現K-鄰近算法
2.1 K-鄰近函數
def mykNN(testData, trainData, label, K):# testData 待分類的數據集# trainData 已經分類好的數據集# label trainData數據集里面的分類標簽# K是knn算法中的K# testData=[101,20]# testData=np.array(testData)import numpy as nparraySize = trainData.shapetrainingSampleNumber = arraySize[0] # 樣本大小trainFeatureNumber = arraySize[1] # 樣本特征個數# 將待測試樣本拓展為和訓練集一樣大小矩陣testDataTemp = np.tile(testData, (trainingSampleNumber, 1))distanceMatrixTemp = (testDataTemp - trainData)**2distanceMatrix = np.sum(distanceMatrixTemp, axis=1)distanceMatrix = np.sqrt(distanceMatrix)# print('測試集與訓練集之間的歐式距離值為:\n')# print(distanceMatrix)# print()# np.argsort()得到矩陣排序后的對應的索引值sortedDistanceIndex = np.argsort(distanceMatrix)# print(sortedDistanceIndex)# 定義一個統計類別的字典labelClassCount = {}for i in range(K):labelTemp = label[sortedDistanceIndex[i]] # 獲取排名前K的距離對應的類別值# print(labelTemp)labelClassCount[labelTemp] = labelClassCount.get(labelTemp, 0) + 1 # 統計前K中每個類別出現的次數# print(labelClassCount)sortedLabelClassCount = sorted(labelClassCount.items(), key=lambda item: item[1], reverse=True) # 對字典進行降序排序# lambda item:item[1] 匿名函數,將利用dict.items()獲取的字典的key-value作為該匿名函數的變量輸入。# reverse=True 降序排列# print(sortedLabelClassCount)return sortedLabelClassCount[0][0] # 返回最終的分類標簽值2.2 牛刀小試-電影分類
舉個簡單的例子,我們可以使用k-近鄰算法分類一個電影是愛情片還是動作片。
| 電影1 | 1 | 101 | Romance |
| 電影2 | 5 | 89 | Romance |
| 電影3 | 108 | 5 | action |
| 電影4 | 115 | 8 | action |
以上是已知的訓練樣本,我們需預測的是(101, 20)這個樣本,我們大致可以知道,打斗鏡頭多則應該是動作片
數據集函數
def creatDataSet(): #定義數據集函數group = np.array([[1, 101, 5], [5, 89, 6], [108, 5, 100], [115, 8, 120]])label = ['romance Movie', 'romance Movie', 'action Movie', 'action Movie']# label=['r','r','a','a']return group, label# print(group)# print(label)'''主函數
if __name__=='__main__': #主函數finalIdentifyingResult=[]group,label=creatDataSet()print()print('Identifying ......')print()print('The identified result is :\n')testData=[101,20]testData=np.array(testData)finalIdentifyingLabel=mykNN(testData,group,label,3)print('the test data is identified as: ',finalIdentifyingLabel,'\n')可以看出來,分類結果和我們預測的是一致的,動作電影。
完整代碼
#!/usr/bin/env python # -*- coding: utf-8 -*- # @Date : 2017-08-28 16:04:25 # @Author : AiYong (aiyong_stu@163.com) # @Link : http://blog.csdn.net/sjtuai # @Version : $Id$import numpy as npdef creatDataSet():group = np.array([[1, 101], [5, 89], [108, 5], [115, 8]])label = ['romance Movie', 'romance Movie', 'action Movie', 'action Movie']return group, labelprint(group)print(label)def mykNN(testData, trainData, label, K):arraySize = trainData.shapetrainingSampleNumber = arraySize[0]testDataTemp = np.tile(testData, (trainingSampleNumber, 1))distanceMatrixTemp = (testDataTemp - trainData)**2distanceMatrix = np.sum(distanceMatrixTemp, axis=1)distanceMatrix = np.sqrt(distanceMatrix)sortedDistanceIndex = np.argsort(distanceMatrix)labelClassCount = {}for i in range(K):labelTemp = label[sortedDistanceIndex[i]] labelClassCount[labelTemp] = labelClassCount.get(labelTemp, 0) + 1 sortedLabelClassCount = sorted(labelClassCount.items(), key=lambda item: item[1], reverse=True) return sortedLabelClassCount[0][0] if __name__=='__main__':finalIdentifyingResult=[]group,label=creatDataSet()print()print('Identifying ......')print()print('The identified result is :\n')testData=[101,20]testData=np.array(testData)finalIdentifyingLabel=mykNN(testData,group,label,3)print('the test data is identified as: ',finalIdentifyingLabel,'\n')2.3 考驗階段-鳶尾花數據集應用-分類預測
鳶尾花數據集
U can get description of ‘iris.csv’ at ‘http://aima.cs.berkeley.edu/data/iris.txt‘####
Definiation of COLs:
#1. sepal length in cm (花萼長) #
#2. sepal width in cm(花萼寬)#
#3. petal length in cm (花瓣長)
#4. petal width in cm(花瓣寬) #
#5. class: #
#– Iris Setosa #
#– Iris Versicolour #
#– Iris Virginica #
#Missing Attribute Values: None
數據集整理函數
def creatDataSet(fileName, test_size_ratio):# fileName is the data file whose type is string# test_size whose type is float is the ratio of test data in the whole# data setirisData = np.loadtxt(fileName, dtype=float,delimiter=',', usecols=(0, 1, 2, 3))dataSize = irisData.shapeirisLabel = np.loadtxt(fileName, dtype=str, delimiter=',', usecols=4)irisLabel = irisLabel.reshape(dataSize[0], 1)#這里使用的一個函數是機器學習庫中的一個可以用來隨機選取訓練集和測試集的一個函數iristrainData, iristestData, iristrainDataLabel, iristestDataLabel = cross_validation.train_test_split(irisData, irisLabel, test_size=test_size_ratio, random_state=0)return iristrainData, iristestData, iristrainDataLabel, iristestDataLabel矩陣轉化為列表函數
def ndarray2List(label):#這個函數的目的是為了后的數據服務的。label = label.tolist()finalLabel = []for i in range(label.__len__()):finalLabel.append('\n'.join(list(label[i])))return finalLabel自定義混淆矩陣計算函數
def computingConfusionMatrix(trueResultA, modelPredictResultB):# trueResultA 正確的分類結果,numpy矩陣類型# modelPredictResultB 模型預測結果,numpy矩陣類型# labelType 分類標簽值,list列表類型#返回,confusionMatrix,混淆矩陣,numpy矩陣類型#返回,labelType,分類標簽,list列表類型#返回,Accuracy,分類爭取率,float浮點數據import numpy as nplabelType = []for i in trueResultA:if i not in labelType:labelType.append(i)print(labelType)labelTypeNumber = labelType.__len__()confusionMatrix = np.zeros([labelTypeNumber, labelTypeNumber], dtype='int64')finalCount = 0for i in range(labelTypeNumber):for j in range(trueResultA.__len__()):if modelPredictResultB[j] == labelType[i] and trueResultA[j] == labelType[i]:confusionMatrix[i][i] += 1else:for k in range(labelTypeNumber):if k == i:breakif modelPredictResultB[j] == labelType[k]:confusionMatrix[i][k] += 1breakcount = 0for i in range(labelTypeNumber - 1, -1, -1):if i == 0:breakfor j in range(labelTypeNumber - 1 - count):confusionMatrix[i][j] = confusionMatrix[i][j] - confusionMatrix[i - 1][j]count += 1totalTrueResult = 0for k in range(labelTypeNumber):totalTrueResult += confusionMatrix[k][k]Accuracy = float(totalTrueResult / modelPredictResultB.__len__()) * 100return confusionMatrix, labelType, Accuracy定義圖里面的橫縱坐標軸標簽值的旋轉
def labelsRotation(labels, rotatingAngle):#labels 獲取的x,y軸的標簽值#rotatingAngle 想要旋轉的角度# 定義x,y軸標簽旋轉函數for t in labels:t.set_rotation(rotatingAngle)定義混淆矩陣可視化函數
def plotConfusionMatrix(confusionMatrix,labelType):import matplotlib.pyplot as plt # 設置圖片的大小以及圖片分辨率fig = plt.figure(figsize=(10, 8), dpi=120)plt.clf() # 繪制圖,colormap是coolwarmplt.imshow(confusionMatrix, cmap=plt.cm.coolwarm, interpolation='nearest')plt.colorbar() # 設置x,y的橫縱軸的標簽plt.xlabel('Predicted Result', fontsize=11)plt.ylabel('True Result', fontsize=11)cmSize = confusionMatrix.shapewidth = cmSize[0]height = cmSize[1]plt.xticks(fontsize=11)plt.yticks(fontsize=11) # 設置橫縱坐標的刻度標簽,顯示為分類標簽值x_locs, x_labels = plt.xticks(range(width), labelType[:width])y_locs, y_labels = plt.yticks(range(height), labelType[:height]) # 設置x,y軸的標簽是否旋轉labelsRotation(x_labels, 0)labelsRotation(y_labels, 0)# 在圖里面添加數據標簽confusionMatrix = confusionMatrix.Tfor x in range(width): # 數據標簽for y in range(height):plt.annotate(confusionMatrix[x][y], xy=(x, y), horizontalalignment='center', verticalalignment='center')plt.grid(True, which='minor', linestyle='-') # plt.rc('font',family='Times New Roman',size=15)font = {'family': 'monospace', 'weight': 'bold', 'size': 15}plt.rc('font', **font)plt.show()主函數
if __name__ == '__main__':finalIdentifyingResult = []iriskNNResult = []iristrainData, iristestData, iristrainDataLabel, iristestDataLabel = creatDataSet('iris.txt', 0.8)testGroup = iristestDatatrainGroup = iristrainDatatrainLabel = iristrainDataLabeltestSize = testGroup.shapetestSampleNumber = testSize[0]print()print('Identifying ......')print()print('The identified result is :\n')for i in range(testSampleNumber):testData = testGroup[i]finalIdentifyingLabel = mykNN(testData, trainGroup, trainLabel, 10)finalIdentifyingResult.append(finalIdentifyingLabel)iriskNNResult = np.array(finalIdentifyingResult).reshape(testSampleNumber, 1)print(finalIdentifyingResult)trueResultA = ndarray2List(iristestDataLabel)modelPredictResultB = finalIdentifyingResultconfusionMatrix, labelType, Accuracy = computingConfusionMatrix(trueResultA, modelPredictResultB)print('The accuracy is :{a:5.3f}%'.format(a=Accuracy))plotConfusionMatrix(confusionMatrix, labelType)結果
混淆矩陣
從上面的結果可以看到這個準確率在90%以上,說明還是不錯的!
完整的程序結構
#!/usr/bin/env python # -*- coding: utf-8 -*- # @Date : 2017-08-08 16:04:25 # @Author : AiYong (aiyong_stu@163.com) # @Link : http://blog.csdn.net/sjtuai # @Version : $Id$import numpy as np from sklearn import cross_validation import matplotlib.pyplot as pltdef mykNN(testData, trainData, label, K):# testData 待分類的數據集# trainData 已經分類好的數據集# label trainData數據集里面的分類標簽# K是knn算法中的K# testData=[101,20]# testData=np.array(testData)import numpy as nparraySize = trainData.shapetrainingSampleNumber = arraySize[0] # 樣本大小trainFeatureNumber = arraySize[1] # 樣本特征個數# 將待測試樣本拓展為和訓練集一樣大小矩陣testDataTemp = np.tile(testData, (trainingSampleNumber, 1))distanceMatrixTemp = (testDataTemp - trainData)**2distanceMatrix = np.sum(distanceMatrixTemp, axis=1)distanceMatrix = np.sqrt(distanceMatrix)# print('測試集與訓練集之間的歐式距離值為:\n')# print(distanceMatrix)# print()# np.argsort()得到矩陣排序后的對應的索引值sortedDistanceIndex = np.argsort(distanceMatrix)# print(sortedDistanceIndex)# 定義一個統計類別的字典labelClassCount = {}for i in range(K):labelTemp = label[sortedDistanceIndex[i]] # 獲取排名前K的距離對應的類別值# print(labelTemp)labelClassCount[labelTemp] = labelClassCount.get(labelTemp, 0) + 1 # 統計前K中每個類別出現的次數# print(labelClassCount)sortedLabelClassCount = sorted(labelClassCount.items(), key=lambda item: item[1], reverse=True) # 對字典進行降序排序# lambda item:item[1] 匿名函數,將利用dict.items()獲取的字典的key-value作為該匿名函數的變量輸入。# reverse=True 降序排列# print(sortedLabelClassCount)return sortedLabelClassCount[0][0] # 返回最終的分類標簽值def creatDataSet(fileName, test_size_ratio):# fileName is the data file whose type is string# test_size whose type is float is the ratio of test data in the whole# data setirisData = np.loadtxt(fileName, dtype=float,delimiter=',', usecols=(0, 1, 2, 3))dataSize = irisData.shapeirisLabel = np.loadtxt(fileName, dtype=str, delimiter=',', usecols=4)irisLabel = irisLabel.reshape(dataSize[0], 1)#這里使用的一個函數是機器學習庫中的一個可以用來隨機選取訓練集和測試集的一個函數iristrainData, iristestData, iristrainDataLabel, iristestDataLabel = cross_validation.train_test_split(irisData, irisLabel, test_size=test_size_ratio, random_state=0)return iristrainData, iristestData, iristrainDataLabel, iristestDataLabeldef ndarray2List(label):#這個函數的目的是為了后的數據服務的。label = label.tolist()finalLabel = []for i in range(label.__len__()):finalLabel.append('\n'.join(list(label[i])))return finalLabeldef computingConfusionMatrix(trueResultA, modelPredictResultB):# trueResultA 正確的分類結果,numpy矩陣類型# modelPredictResultB 模型預測結果,numpy矩陣類型# labelType 分類標簽值,list列表類型#返回,confusionMatrix,混淆矩陣,numpy矩陣類型#返回,labelType,分類標簽,list列表類型#返回,Accuracy,分類爭取率,float浮點數據import numpy as nplabelType = []for i in trueResultA:if i not in labelType:labelType.append(i)print(labelType)labelTypeNumber = labelType.__len__()confusionMatrix = np.zeros([labelTypeNumber, labelTypeNumber], dtype='int64')finalCount = 0for i in range(labelTypeNumber):for j in range(trueResultA.__len__()):if modelPredictResultB[j] == labelType[i] and trueResultA[j] == labelType[i]:confusionMatrix[i][i] += 1else:for k in range(labelTypeNumber):if k == i:breakif modelPredictResultB[j] == labelType[k]:confusionMatrix[i][k] += 1breakcount = 0for i in range(labelTypeNumber - 1, -1, -1):if i == 0:breakfor j in range(labelTypeNumber - 1 - count):confusionMatrix[i][j] = confusionMatrix[i][j] - confusionMatrix[i - 1][j]count += 1totalTrueResult = 0for k in range(labelTypeNumber):totalTrueResult += confusionMatrix[k][k]Accuracy = float(totalTrueResult / modelPredictResultB.__len__()) * 100return confusionMatrix, labelType, Accuracydef labelsRotation(labels, rotatingAngle):#labels 獲取的x,y軸的標簽值#rotatingAngle 想要旋轉的角度# 定義x,y軸標簽旋轉函數for t in labels:t.set_rotation(rotatingAngle)def plotConfusionMatrix(confusionMatrix,labelType):import matplotlib.pyplot as pltfig = plt.figure(figsize=(10, 8), dpi=120)plt.clf()plt.imshow(confusionMatrix, cmap=plt.cm.coolwarm, interpolation='nearest')plt.colorbar()plt.xlabel('Predicted Result', fontsize=11)plt.ylabel('True Result', fontsize=11)cmSize = confusionMatrix.shapewidth = cmSize[0]height = cmSize[1]plt.xticks(fontsize=11)plt.yticks(fontsize=11)x_locs, x_labels = plt.xticks(range(width), labelType[:width])y_locs, y_labels = plt.yticks(range(height), labelType[:height])labelsRotation(x_labels, 0)labelsRotation(y_labels, 0)confusionMatrix = confusionMatrix.Tfor x in range(width): # 數據標簽for y in range(height):plt.annotate(confusionMatrix[x][y], xy=(x, y), horizontalalignment='center', verticalalignment='center')plt.grid(True, which='minor', linestyle='-')font = {'family': 'monospace', 'weight': 'bold', 'size': 15}plt.rc('font', **font)plt.show()if __name__ == '__main__':finalIdentifyingResult = []iriskNNResult = []iristrainData, iristestData, iristrainDataLabel, iristestDataLabel = creatDataSet('iris.txt', 0.8)testGroup = iristestDatatrainGroup = iristrainDatatrainLabel = iristrainDataLabeltestSize = testGroup.shapetestSampleNumber = testSize[0]print()print('Identifying ......')print()print('The identified result is :\n')for i in range(testSampleNumber):testData = testGroup[i]finalIdentifyingLabel = mykNN(testData, trainGroup, trainLabel, 10)finalIdentifyingResult.append(finalIdentifyingLabel)iriskNNResult = np.array(finalIdentifyingResult).reshape(testSampleNumber, 1)print(finalIdentifyingResult)trueResultA = ndarray2List(iristestDataLabel)modelPredictResultB = finalIdentifyingResultconfusionMatrix, labelType, Accuracy = computingConfusionMatrix(trueResultA, modelPredictResultB)print('The accuracy is :{a:5.3f}%'.format(a=Accuracy))plotConfusionMatrix(confusionMatrix, labelType)3. 總結
這里有非常好的關于K-鄰近分類的理論以及一些比較好的圖理:
https://en.wikipedia.org/wiki/K-nearest_neighbors_algorithm
3.1 優缺點
優點
簡單,易于理解,易于實現,無需估計參數,無需訓練
適合對稀有事件進行分類(例如當流失率很低時,比如低于0.5%,構造流失預測模型)
特別適合于多分類問題(multi-modal,對象具有多個類別標簽),例如根據基因特征來判斷其功能分類,kNN比SVM的表現要好
缺點
懶惰算法,對測試樣本分類時的計算量大,內存開銷大,評分慢
可解釋性較差,無法給出決策樹那樣的規則。
3.2 一些問題集錦
1、k值設定為多大?
k太小,分類結果易受噪聲點影響;k太大,近鄰中又可能包含太多的其它類別的點。(對距離加權,可以降低k值設定的影響)
k值通常是采用交叉檢驗來確定(以k=1為基準)
經驗規則:k一般低于訓練樣本數的平方根
2、類別如何判定最合適?
投票法沒有考慮近鄰的距離的遠近,距離更近的近鄰也許更應該決定最終的分類,所以加權投票法更恰當一些。
3、如何選擇合適的距離衡量?
高維度對距離衡量的影響:眾所周知當變量數越多,歐式距離的區分能力就越差。
變量值域對距離的影響:值域越大的變量常常會在距離計算中占據主導作用,因此應先對變量進行標準化。
4、訓練樣本是否要一視同仁?
在訓練集中,有些樣本可能是更值得依賴的。
可以給不同的樣本施加不同的權重,加強依賴樣本的權重,降低不可信賴樣本的影響。
5、性能問題?
kNN是一種懶惰算法,平時不好好學習,考試(對測試樣本分類)時才臨陣磨槍(臨時去找k個近鄰)。
懶惰的后果:構造模型很簡單,但在對測試樣本分類地的系統開銷大,因為要掃描全部訓練樣本并計算距離。
參考鏈接:
https://en.wikipedia.org/wiki/K-nearest_neighbors_algorithm
http://blog.csdn.net/jmydream/article/details/8644004
http://wuguangbin1230.blog.163.com/blog/static/61529835201522905624494/
https://docs.scipy.org/doc/numpy-dev/user/quickstart.html
http://matplotlib.org/api/pyplot_summary.html
總結
以上是生活随笔為你收集整理的Python 3实现k-邻近算法以及 iris 数据集分类应用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 安卓屏幕尺寸修改器(安卓屏幕尺寸)
- 下一篇: c语言对中文字符串编码_Python |