kNN--近鄰演算法
阿新 • • 發佈:2018-12-08
kNN--近鄰演算法
kNN演算法的核心思想是如果一個樣本在特徵空間中的k個最相鄰的樣本中的大多數屬於某一個類別,則該樣本也屬於這個類別,並具有這個類別上樣本的特性。
在機器學習中常用於分類。
數學內容:
歐氏距離公式,矩陣運算,歸一化數值
python模組:
numpy,operator(用其中的itemgetter做排序),listdir(列出目錄中的檔案),matplotlib.pyplot(視覺化資料分析資料),
PIL(對圖片進行處理)
from numpy import * import operatorfrom os import listdir def createDataSet(): groups=array([[1.0,1.1],[1.0,1.0],[0,0],[0,0.1]]) lables=['A','A','B','B'] return groups,lables #k-近鄰演算法 def classify0(inX, dataset,labels,k): #獲取樣本集中有幾組資料 datasetSize=dataset.shape[0] #歐氏距離公式 計算距離 diffMat=tile(inX, (datasetSize, 1)) - dataset sqDiffMat=diffMat**2 sqDistances=sqDiffMat.sum(axis=1) distances=sqDistances**0.5 #按距離遞增排列,返回樣本集中的index sortedDistances=distances.argsort() classCount={} for i in range(k): #根據距離遞增的順序,獲取與其對應的類別(即目標變數) voteIlabel=labels[sortedDistances[i]] #為k個元素所在的分類計數 classCount[voteIlabel]=classCount.get(voteIlabel,0)+1 #通過對比每個類別出現的次數(即classCount value),以遞減的順序排序 sortedCount=sorted(classCount.items(),key=operator.itemgetter(1),reverse=True) #返回計數最大的那個類別的值 return sortedCount[0][0] #準備資料 def file2matrix(filename): fr=open(filename) arrayOLines=fr.readlines() #獲取檔案行數 numberOflines=len(arrayOLines) #建立一個以檔案行數為行,3列的矩陣 returnMatrix=zeros((numberOflines,3)) #定義一個存放目標變數(類別)的陣列 classLabelVector=[] index=0 #遍歷檔案 for line in arrayOLines: line=line.strip() listFromLine=line.split('\t') #把檔案前三列新增到返回的矩陣中 returnMatrix[index:]=listFromLine[0:3] #檔案最後一列(對應的類別)新增到類別陣列中 classLabelVector.append(int(listFromLine[-1])) index+=1 #返回資料特徵矩陣和類別陣列 return returnMatrix,classLabelVector #通過公式 "newValue=(oldValue-min)/(max-min)" 將任意取值範圍的特徵值轉化為0到1區間內的值 def autoNorm(dataset): #返回每列的最小值 minVals=dataset.min(0) #返回每列的最大值 maxVals=dataset.max(0) #返回最大值與最小值的差 ranges=maxVals-minVals #建立與dataset同行同列的0矩陣 normDataset=zeros(shape(dataset)) #返回dataset的行數 m=dataset.shape[0] #建立一個重複m次的minVals矩陣,並與dataset相減 normDataset=dataset-tile(minVals,(m,1)) #newValue=(oldValue-min)/(max-min) normDataset=normDataset/tile(ranges,(m,1)) return normDataset,ranges,minVals #測試演算法 def datingClassTest(): #設定測試資料比例 hoRatio=0.10 #返回格式化後的資料和其標籤 datingDataMat,datingLabels=file2matrix('datingTestSet2.txt') #歸一化資料值 normMat,ranges,minVals=autoNorm(datingDataMat) #資料的行數 m=normMat.shape[0] #測試資料的行數 numTestVecs=int(m*hoRatio) #設定錯誤預測計數器 errorCount=0.0 #向k-近鄰演算法中傳numTestVecs個測試資料,並把返回的預測資料與真實資料比較返回,若錯誤,計數器加1 for i in range(numTestVecs): """ 呼叫k-近鄰演算法,為其傳入引數, normMat[i]:第i個測試資料, normMat[numTestVecs:m,:]:從numTestVecs到m個樣本資料,(m可以不寫,相當於從numTestVecs索引開始,取剩下所有的normMat資料) datingLabels[numTestVecs:m]:從numTestVecs到m個樣本資料對應的標籤 3:k的值 """ classifierResult=classify0(normMat[i],normMat[numTestVecs:m,:],datingLabels[numTestVecs:m],3) #判斷預測資料與真實資料,如果是錯誤的,則以紅字型輸出,並錯誤預測計數器加1 if (classifierResult!=datingLabels[i]): print("\033[0;31mthe classifier came back with: %d, the real answer is: %d\033[0m" % (classifierResult, datingLabels[i])) errorCount+=1.0 else: print("the classifier came back with: %d, the real answer is: %d" % (classifierResult, datingLabels[i])) print("the total error rate is:%f" %(errorCount/float(numTestVecs))) #約會系統 def classifiyPerson(): #設定分類(標籤)列表 resultList=["not at all", "in small doses", "in large doses"] #提示使用者輸入相應內容 percentTats=float(input("percentage of time spent playing video games?")) ffMiles=float(input("frequent filer miles earned per year?")) iceCream=float(input("liters of ice cream consumed per year?")) #把使用者輸入的三個特徵值格式化成numpy.array資料型別 inArr=array([ffMiles,percentTats,iceCream]) #準備樣本資料及對應標籤 datingDataMat,datingLabels=file2matrix('datingTestSet2.txt') #歸一化樣本資料並返回ranges和minVals,以便歸一化使用者輸入的資料 normMat,ranges,minVals=autoNorm(datingDataMat) #呼叫k-近鄰演算法,並把傳入的預測資料特徵做歸一化 classifierResult=classify0((inArr-minVals)/ranges,normMat,datingLabels,3) #打印出預測出的類別,因為樣本資料中的類別(標籤)為1,2,3,其index是0,1,2,所以要用預測出的分類(1,2,3)減1 print("You will probably like this person: %s" %(resultList[classifierResult-1])) #將32x32的二進位制影象檔案轉換成1x1024的向量 def img2vector(filename): #建立一個1x1024的0矩陣 returnVect=zeros((1,1024)) fr=open(filename) """ 因為已知檔案是32x32,即有檔案中32行內容,通過readline()方法遍歷檔案,得到檔案的每行內容lineStr 再遍歷每行內容lineStr,並把遍歷出的內容新增到returnVect矩陣裡 """ for i in range(32): lineStr=fr.readline() for j in range(32): returnVect[0,32*i+j]=int(lineStr[j]) return returnVect #手寫數字識別系統 def handwritingClassTest(): #建立資料標籤集合 hwLabels=[] #列出目錄衝所有檔案 trainingFileList=listdir('digits/trainingDigits') #得到檔案個數,也就是訓練資料的行數 m=len(trainingFileList) #建立一個m行,1024列的0矩陣 trainingMat=zeros((m,1024)) """ 通過遍歷所有訓練檔案,得到檔名,其對應的數字(eg:0_7.txt),並把數字新增到hwLabels集合, 通過上面的img2vector函式,得到一個與該檔案對應的1x1024矩陣,並新增到trainingMat矩陣中 """ for i in range(m): fileNameStr=trainingFileList[i] fileStr=fileNameStr.split('.')[0] classNumStr=int(fileStr.split('_')[0]) hwLabels.append(classNumStr) trainingMat[i,:]=img2vector('digits/trainingDigits/%s' % fileNameStr) #對測試資料做同樣的操作 testFileList=listdir('digits/testDigits') mTest=len(testFileList) errorCount=0.0 for i in range(mTest): fileNameStr=testFileList[i] fileStr=fileNameStr.split('.')[0] classNumStr=int(fileStr.split('_')[0]) vectorUnderTest=img2vector('digits/testDigits/%s' % fileNameStr) classifierResult=classify0(vectorUnderTest,trainingMat,hwLabels,3) if (classifierResult!=classNumStr): print("\033[0;31mthe classifier came back with: %d, the real answer is: %d\033[0m" % (classifierResult,classNumStr)) errorCount+=1 else: print("the classifier came back with: %d, the real answer is: %d" %(classifierResult,classNumStr)) print("\nthe total number of errors is: %d" % errorCount) print("\nthe total error rate is: %f" %(errorCount/float(mTest))) #在網上找數字圖片做測試 def imgNumClassTest(filename): hwLabels=[] trainingFileList=listdir('digits/trainingDigits') m=len(trainingFileList) trainingMat=zeros((m,1024)) for i in range(m): fileNameStr=trainingFileList[i] fileStr=fileNameStr.split('.')[0] classNumStr=int(fileStr.split('_')[0]) hwLabels.append(classNumStr) trainingMat[i,:]=img2vector('digits/trainingDigits/%s' % fileNameStr) vectorUnderTest=img2vector(filename) classifierResult=classify0(vectorUnderTest,trainingMat,hwLabels,3) print(classifierResult)
約會網站案列資料分析程式碼:
""" 分析資料 """ import kNN from numpy import * import matplotlib import matplotlib.pyplot as plt datingDataMat,datingLabels=kNN.file2matrix('datingTestSet2.txt') #建立一個圖片視窗,預設是1(figure1) fig=plt.figure() #在圖片視窗建立兩行一列的子圖,並使用第一行第一列,即211的含義 ax1=fig.add_subplot(211) """ 建立散點圖,x軸是datingDataMat第一列的資料,y軸是datinDataMat第二列的資料, 後面兩個引數一個代表顏色,一個代表點的大小,兩個引數同時放大15倍,然後這個時候就是同一個label用一種顏色和大小表示出來, 不同的label的點的大小和顏色會不一樣。 """ ax1.scatter(datingDataMat[:,1],datingDataMat[:,2],15*array(datingLabels),15*array(datingLabels)) #設定x軸標籤 plt.xlabel('Play game takes time') #設定y軸標籤 plt.ylabel('Eat ice-cream') #在圖片視窗中使用第一行第二列 ax2=fig.add_subplot(212) #把datingLabels轉成numpy.array型別 datingLabels=array(datingLabels) #取datingLabels中值等於1的index idx_1=where(datingLabels==1) #idx_1即datingTestSet2.txt檔案中第四列值為1的行數,則獲取idx_1行,第一二列的資料建立散點圖,為這些點設定顏色,大小,label p1=ax2.scatter(datingDataMat[idx_1,0],datingDataMat[idx_1,1],color = 'm', label='Hate', s = 50) idx_2=where(datingLabels==2) p2=ax2.scatter(datingDataMat[idx_2,0],datingDataMat[idx_2,1],color = 'c', label='General', s = 30) idx_3=where(datingLabels==3) p3=ax2.scatter(datingDataMat[idx_3,0],datingDataMat[idx_3,1],color = 'r', label='Like', s = 10) plt.xlabel('Flying') plt.ylabel('Play game takes time') #建立圖示放置在左上角 plt.legend(loc='upper left') #顯示圖片 plt.show()
手寫數字識別系統圖片轉文字檔案程式碼:
from PIL import Image import numpy as np import matplotlib.pyplot as plt def img2txt(img_path, txt_name): """ 將影象資料轉換為txt檔案 :param img_path: 影象檔案路徑 :type txt_name: 輸出txt檔案路徑 """ #把圖片轉成二值影象,並設長寬均為32 im = Image.open(img_path).convert('1').resize((32, 32)) # type:Image.Image #plt.imshow(im) #plt.show() #將上面得到的影象轉成array陣列 data = np.asarray(im) #將上面得到的陣列儲存在到文字檔案中,指定儲存資料型別為整型,分隔符 np.savetxt(txt_name, data, fmt='%d', delimiter='')