K近鄰法(K-nearest neighbors,KNN)既可以分類,也可以回歸。
KNN做回歸和分類的區(qū)別在于最后預測時的決策方式。
KNN做分類時,一般用多數(shù)表決法
KNN做回歸時,一般用平均法。
基本概念如下:對待測實例,在訓練數(shù)據(jù)集中找到與該實例最鄰近的K個實例(也就是上面所說的K個鄰居), 這K個實例的多數(shù)屬于某個類,就把該輸入實例分類到這個類中
KNN算法主要考慮:k值的選取,距離度量方式,分類決策規(guī)則。
1) k值的選取。在應用中,k值一般選擇一個比較小的值,一般選用交叉驗證來取最優(yōu)的k值
當K值較小,訓練誤差減小,泛化誤差增大,模型復雜容易過擬合;
當K值較大,泛化誤差減小,訓練誤差增大,模型簡單使預測發(fā)生錯誤(一個極端,K等于樣本數(shù)m,則完全沒有分類,此時無論測試集是什么,結果都屬于訓練集中最多的類)
2)距離度量。Lp距離:誤差絕對值p次方求和再求p次根。歐式距離:p=2的Lp距離。曼哈頓距離:p=1的Lp距離。p為無窮大時,Lp距離為各個維度上距離的最大值
3)分類決策規(guī)則。也就是如何根據(jù)k個最近鄰決定待測對象的分類。k最近鄰的分類決策規(guī)則一般選用多數(shù)表決
1)計算待測對象和訓練集中每個樣本點的歐式距離
2)對上面的所有距離值排序
3)選出k個最小距離的樣本作為“選民”
4)根據(jù)“選民”預測待測樣本的分類或值
1)原理簡單
2)保存模型需要保存所有樣本集
3)訓練過程很快,預測速度很慢
· 優(yōu)點:
精度高、對異常值不敏感
可用于數(shù)值型數(shù)據(jù)和離散型數(shù)據(jù)(既可以用來估值,又可以用來分類)
· 缺點:
時間復雜性高;空間復雜性高;需要大量的內存
樣本不平衡問題(即有些類別的樣本數(shù)量很多,而其它樣本的數(shù)量很少);
一般數(shù)值很大的時候不用這個,計算量太大。但是單個樣本又不能太少,否則容易發(fā)生誤分。
最大的缺點是無法給出數(shù)據(jù)的內在含義。
需要思考的問題:
樣本屬性如何選擇?如何計算兩個對象間距離?當樣本各屬性的類型和尺度不同時如何處理?各屬性不同重要程度如何處理?模型的好壞如何評估?
K近鄰算法的一般流程:準備數(shù)據(jù)- 分析數(shù)據(jù)- 測試算法- 使用算法
關于sklearn的詳細介紹,請見之前的博客 //www.jb51.net/article/204984.htm
5.1.1 sklearn實現(xiàn)k-近鄰算法簡介 官方文檔
5.1.2 KNeighborsClassifier函數(shù)8個參數(shù)
注意:樣本數(shù)據(jù) - 特征數(shù)據(jù) feature 必須是數(shù)字類型,要進行運算的!
5.1.3 實例
(1)對電影進行分類
import pandas as pd import numpy as np from sklearn.neighbors import KNeighborsClassifier # 讀取數(shù)據(jù) df = pd.read_excel(../../myfile.excel) #1、實例模型對象 knn = KNeighborsClassifier(n_neighbors=3) #2、拿到樣本數(shù)據(jù)和分類結果數(shù)據(jù): 截取目標列,樣本數(shù)據(jù)要二維 feature = df[['Action Lean','Love Lean']] target = feature['target'] #3、訓練模型 knn.fit(feature,target) #4、測試結果 movie = np.array([13,21]) res = knn.predict(movie) #5、評分:分數(shù)越高悅準確knn.score(feature,target)
(2)預測年收入是否大于50K美元
# 讀取adult.txt文件,最后一列是年收入,并使用KNN算法訓練模型,然后使用模型預測一個人的年收入是否大于50 # 1. 讀取數(shù)據(jù) data = pd.read_csv('../data/adults.txt') data.head() # 2. 獲取年齡、教育程度、職位、每周工作時間作為機器學習數(shù)據(jù) 獲取薪水作為對應結果 feature = data[['age','education_num','occupation' ,'hours_per_week']] target = data['salary'] # 3. knn中特征數(shù)據(jù)是需要參與運算的,所以要保證特征數(shù)據(jù)必須為數(shù)值型的數(shù)據(jù) # 數(shù)據(jù)轉換,將String類型數(shù)據(jù)轉換為int #### map方法,進行數(shù)據(jù)轉換 dic = {}# unique()方法保證數(shù)據(jù)唯一 occ_arr = feature['occupation'].unique() # 生成 字符對應數(shù)字的 關系表 for i in range(occ_arr.size): dic[occ_arr[i]] = i # 數(shù)值替換字符串 feature['occupation'] = feature['occupation'].map(dic) # 4. 切片:訓練數(shù)據(jù)和預測數(shù)據(jù) # 查看數(shù)據(jù)的形狀 (訓練的數(shù)據(jù)必須是二維數(shù)據(jù)) feature.shape #訓練數(shù)據(jù) x_train = feature[:32500] y_train = target[:32500] #測試數(shù)據(jù) x_test = feature[32500:] y_test = target[32500:] # 5. 生成算法 from sklearn.neighbors import KNeighborsClassifier # 實例化一個 knn對象, # 參數(shù):n_neighbors可調,調到最終預測的是最好的結果. knn = KNeighborsClassifier(n_neighbors=10) # fit() 訓練函數(shù), (訓練數(shù)據(jù),訓練數(shù)據(jù)的結果) knn.fit(x_train,y_train) # 對訓練的模型進行評分 (測試數(shù)據(jù),測試數(shù)據(jù)的結果) knn.score(x_test,y_test) # 6.預測數(shù)據(jù) print('真實的分類結果:',np.array(y_test)) print('模型的分類結果:',knn.predict(x_test))
(3)實例:基于sklearn實現(xiàn)手寫數(shù)字識別系統(tǒng)
pylot 讀取圖片:img_arr.shape 查看形狀
import pandas as pd import numpy as np from sklearn.neighbors import KNeighborsClassifier # 1、樣本數(shù)據(jù)提?。好繌垐D片對應的numpy數(shù)組:0,1,2,3,4,5,6,7,8,9 feature =[] target =[] for i in range(10):#0-9 文件夾名稱 for j in range(1,501): #1-500圖片名稱 imgpath = './data/'+str(i)+'/'+str(i)+'_'+str(j)+'.bmp' #圖片路徑 img_arr = pld.imread(imgpath) feature.append(img_arr) target.append(i) # 2、把列表轉成numpy數(shù)組;feature 必須為二維數(shù)組; feature = np.array(feature) #這個feature 里有多個二維數(shù)組, target = np.array(target) feature.shape (5000,28,28) #里面有5000個28*28的二維數(shù)組 # 擴展:feature是三維數(shù)組;多個二維數(shù)組組成的數(shù)組是三維數(shù)組,多個一維數(shù)組組成的數(shù)組是二維數(shù)組! # 3、feature變形為二維數(shù)組 feature.shape(5000,784) #4、對樣本數(shù)據(jù)和目標數(shù)據(jù)進行同步打亂 np.random.seed(10) np.random.shuffle(feature) np.random.seed(10) np.random.shuffle(target) # 5、對樣本數(shù)據(jù)進行拆分:訓練數(shù)據(jù)和測試數(shù)據(jù) x_train = feature[:4950] y_train = target[:4950] x_test = feature[4950:] y_test = target[4950:] # 6、對模型進行訓練:參數(shù):n_neighbors可調,調到最終預測的評分最好的結果. from sklearn.neighbors import KNeighborsClassifier knn = KNeighborsClassifier(n_neighbors=8) knn.fit(x_train,y_train) # (訓練數(shù)據(jù),訓練數(shù)據(jù)的結果) # 7、對訓練的模型進行評分 (測試數(shù)據(jù),測試數(shù)據(jù)的結果) knn.score(x_test,y_test) # 8、對模型進行測試 print('真實的結果',y_test) print('模型分類的結果',knn.predict(x_test)) #9、保存訓練號的模型 from sklearn.externals import joblib joblib.dump(knn,'./knn.m') #10、讀取訓練好的模型 knn = joblib.load('./knn.m') #------------------------------------------------------------------------------------------------- # 11、將外部圖片帶入模型進行測試 # 注意:外部圖片的樣本數(shù)據(jù)要轉成和訓練模型時候使用的樣本圖片一樣的維度數(shù)組 # ?。?!模型只可以測試類似于測試數(shù)據(jù)中的特征數(shù)據(jù) ?。?! img_arr = plt.imgread('./數(shù)字.jpg') eight_arr = img_arr[170:260,80:70] # 截取圖片的部分 plt.imshow(eight_arr) #查看截取的數(shù)字圖片 # 變形為測試數(shù)據(jù)中的特征數(shù)據(jù):feature.shape(5000,784) 每一行是一個一維的784個元素的數(shù)組;像素要變?yōu)橐粯? # 12、將eight_arr 對應的圖片降維(三維變?yōu)槎S):將(65,50,3)變?yōu)?28,28) eight_arr.mean(axis=2 ) # axis=2 表示去除第三個維度,保留(65,50)保證圖片不能變! # 13、將圖片像素進行等比例壓縮 import scipy.ndimage as ndimage data_pre_test = ndimage.zoom(eight_arr,zoom=(28/65,28/50)) eight_arr.shape #(28,28) # 14、將壓縮好的圖片由二維(28,28)變?yōu)橐痪S(1,784) eight_arr = eight_arr(1,784) # 15、識別外部進行壓縮和降維的圖片 knn.predict(eight_arr) array([8])
# -*- coding: UTF-8 -*- import numpy as np import operator from os import listdir from sklearn.neighbors import KNeighborsClassifier as kNN """ 函數(shù)說明:將32x32的二進制圖像轉換為1x1024向量。 Parameters: filename - 文件名 Returns: returnVect - 返回的二進制圖像的1x1024向量 """ def img2vector(filename): #創(chuàng)建1x1024零向量 returnVect = np.zeros((1, 1024)) #打開文件 fr = open(filename) #按行讀取 for i in range(32): #讀一行數(shù)據(jù) lineStr = fr.readline() #每一行的前32個元素依次添加到returnVect中 for j in range(32): returnVect[0, 32*i+j] = int(lineStr[j]) #返回轉換后的1x1024向量 return returnVect """ 函數(shù)說明:手寫數(shù)字分類測試 Parameters: 無 Returns: 無 """ def handwritingClassTest(): #測試集的Labels hwLabels = [] #返回trainingDigits目錄下的文件名 trainingFileList = listdir('trainingDigits') #返回文件夾下文件的個數(shù) m = len(trainingFileList) #初始化訓練的Mat矩陣,測試集 trainingMat = np.zeros((m, 1024)) #從文件名中解析出訓練集的類別 for i in range(m): #獲得文件的名字 fileNameStr = trainingFileList[i] #獲得分類的數(shù)字 classNumber = int(fileNameStr.split('_')[0]) #將獲得的類別添加到hwLabels中 hwLabels.append(classNumber) #將每一個文件的1x1024數(shù)據(jù)存儲到trainingMat矩陣中 trainingMat[i,:] = img2vector('trainingDigits/%s' % (fileNameStr)) #構建kNN分類器 neigh = kNN(n_neighbors = 3, algorithm = 'auto') #擬合模型, trainingMat為訓練矩陣,hwLabels為對應的標簽 neigh.fit(trainingMat, hwLabels) #返回testDigits目錄下的文件列表 testFileList = listdir('testDigits') #錯誤檢測計數(shù) errorCount = 0.0 #測試數(shù)據(jù)的數(shù)量 mTest = len(testFileList) #從文件中解析出測試集的類別并進行分類測試 for i in range(mTest): #獲得文件的名字 fileNameStr = testFileList[i] #獲得分類的數(shù)字 classNumber = int(fileNameStr.split('_')[0]) #獲得測試集的1x1024向量,用于訓練 vectorUnderTest = img2vector('testDigits/%s' % (fileNameStr)) #獲得預測結果 # classifierResult = classify0(vectorUnderTest, trainingMat, hwLabels, 3) classifierResult = neigh.predict(vectorUnderTest) print("分類返回結果為%d\t真實結果為%d" % (classifierResult, classNumber)) if(classifierResult != classNumber): errorCount += 1.0 print("總共錯了%d個數(shù)據(jù)\n錯誤率為%f%%" % (errorCount, errorCount/mTest * 100)) """ 函數(shù)說明:main函數(shù) Parameters: 無 Returns: 無 """ if __name__ == '__main__': handwritingClassTest()
可以嘗試更改這些參數(shù)的設置,加深對其函數(shù)的理解。
以上就是K近鄰法(KNN)相關知識總結以及如何用python實現(xiàn)的詳細內容,更多關于python實現(xiàn)K近鄰法(KNN)的資料請關注腳本之家其它相關文章!