什么是K近鄰算法
眾所周知,電影可以按照題材分類,然而題材本身是如何定義?同一題材的電影有哪些公共特征?這是電影分類的時候必須要考慮的。而K近鄰算法就是解決這個問題的。K近鄰算法通過分析電影中的特征,比如打斗場景、親吻次數(shù)等來劃分測試的影片跟哪個題材相關(guān)。
K近鄰算法概述
k近鄰算法就是采用測量不同的特征值之間的距離方法進行分類。它的優(yōu)點是精度高、對異常值不敏感、無數(shù)據(jù)輸入假定。它的缺點是計算復(fù)雜度高、空間復(fù)雜度高、僅適用于數(shù)值型和標稱型數(shù)據(jù)。
K近鄰算法的一般流程
(1)收集數(shù)據(jù):數(shù)據(jù)還要數(shù)值型或者是標稱型數(shù)據(jù)
(2)準備數(shù)據(jù):將數(shù)據(jù)轉(zhuǎn)化成距離計算所需要的數(shù)值
(3)分析數(shù)據(jù):使用K近鄰算法對數(shù)據(jù)進行分析
(4)測試算法:計算該算法的錯誤率
(5)使用算法:輸入想要測試的數(shù)據(jù),運行K近鄰算法,得出結(jié)果
K近鄰算法代碼實現(xiàn)
首先導入數(shù)據(jù),添加一個組矩陣和一個標簽矩陣。
from numpy import *
import operator
def createDataSet():
group = array([[1.0,1.1],[1.0,1.0],[0,0],[0,0.1]])
labels = ['A','A','B','B']
return group, labels
這兩個東西有什么用呢?這四組值加上四組標簽一一對應(yīng)在x,y軸上的話就是

這時候輸入一個坐標,例如(1,1)這個點,計算這個點與這四個點的歐氏距離,得出與A標簽的點距離近,所以(1,1)這個點就屬于類型A。這就是K近鄰算法。算法偽代碼如下:
對未知類別屬性的數(shù)據(jù)集中的點依次執(zhí)行以下操作:
1.計算已知類別數(shù)據(jù)集中的點與當前點之間的距離
2.按照距離遞增次序排序
3.選取與當前點距離最小的k個點
4.確定前k個點所在類別的出現(xiàn)頻率
5.返回前k個點出現(xiàn)頻率最高的類別作為當前點的預(yù)測分類
程序代碼如下所示:
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
sortedDistIndicies = distances.argsort()
classCount={}
for i in range(k):
voteIlabel = labels[sortedDistIndicies[i]]
classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1
sortedClassCount = sorted(classCount.items(), key=lambda v: v[1], reverse=True)
return sortedClassCount[0][0]
其中函數(shù)的輸入有四個,inX是輸入向量,也就是要分類的對象。dataSet是訓練樣本集。labels是訓練樣本集對應(yīng)的標簽。最后的參數(shù)k表示選擇最近鄰的數(shù)目。其中計算兩個點之間的距離用的是歐氏距離,公式如下:

如果不是二維空間,而是多維空間,那么計算距離公式為:

計算完所有距離之后,可以對數(shù)據(jù)按照從小到大的次序排列。然后確定前k個距離最小元素所在的分類,返回出現(xiàn)頻率最高的元素標簽。
運行上述程序,輸出的結(jié)果應(yīng)該是B。
測試K近鄰算法
對于測試分類器,我們可以用已知的數(shù)據(jù)來進行測試,首先將已知數(shù)據(jù)使用K近鄰算法進行分類,然后將已知數(shù)據(jù)的標簽與測試結(jié)果作對比,來得到我們的誤差率。我們使用《機器學習實戰(zhàn)》這本書上海倫對約會網(wǎng)站上的哪類人感興趣的數(shù)據(jù)集來進行測試。算法偽代碼如下:
1.準備數(shù)據(jù):使用python解析文件數(shù)據(jù)
2.分析數(shù)據(jù):使用matplotlib畫出二維擴散圖
3.測試數(shù)據(jù):使用文件中部分數(shù)據(jù)集作為測試樣本進行測試
4.得出結(jié)果:將樣本測得的標簽與實際標簽作對比,記錄錯誤標簽的數(shù)目
def file2matrix(filename):#準備數(shù)據(jù)
fr = open(filename,'r',encoding='UTF-8')
numberOfLines = len(fr.readlines()) #get the number of lines in the file
returnMat = zeros((numberOfLines,3)) #prepare matrix to return
classLabelVector = [] #prepare labels return
fr = open(filename,'r',encoding='UTF-8')
index = 0
for line in fr.readlines():
line = line.strip()
listFromLine = line.split('\t')
returnMat[index,:] = listFromLine[0:3]
classLabelVector.append(int(listFromLine[-1]))
index +=1
return returnMat,classLabelVector
#畫出散點圖
fig = plt.figure()
ax = fig.add_subplot(111)
datingDataMat,datingLabels = kNN.file2matrix('EXTRAS/datingTestSet2.txt')
ax.scatter(datingDataMat[:,0], datingDataMat[:,1], 35, 1.0*array(datingLabels))
ax.axis([-2,100000,-0.2,25])
plt.xlabel('Percentage of Time Spent Playing Video Games')
plt.ylabel('Liters of Ice Cream Consumed Per Week')
plt.show()
def autoNorm(dataSet): #數(shù)據(jù)歸一化
minVals = dataSet.min(0)
maxVals = dataSet.max(0)
ranges = maxVals - minVals
normDataSet = zeros(shape(dataSet))
m = dataSet.shape[0]
normDataSet = dataSet - tile(minVals, (m,1))
normDataSet = normDataSet/tile(ranges, (m,1)) #element wise divide
return normDataSet, ranges, minVals
def datingClassTest(): #測試誤差率
hoRatio = 0.5 #hold out 10%
datingDataMat,datingLabels = file2matrix('./datingTestSet2.txt') #load data setfrom file
normMat, ranges, minVals = autoNorm(datingDataMat)
m = normMat.shape[0]
numTestVecs = int(m*hoRatio)
errorCount = 0.0
for i in range(numTestVecs):
classifierResult = classify0(normMat[i,:],normMat[numTestVecs:m,:],datingLabels[numTestVecs:m],3)
print ("the classifier came back with: %d, the real answer is: %d" % (classifierResult, datingLabels[i]))
if (classifierResult != datingLabels[i]): errorCount += 1.0
print( "the total error rate is: %f" % (errorCount/float(numTestVecs)))
print ('errorCount')
其中散點圖如下所示:

圖中清晰地表示了這兩個類別對結(jié)果的影響。這時候從圖中就可以看出,x,y軸的度量單位差異較大,所以對于結(jié)果來說,x軸的數(shù)據(jù)所占的權(quán)重較大,這明顯是不符合常規(guī)的。所以我們在這兒需要將特征值進行歸一化處理,將每個特征都歸一化有助于平均所有特征的權(quán)重。歸一化代碼如上圖所示。最后測試k近鄰算法,隨機抽取前50%數(shù)據(jù)來測試,結(jié)果得出誤差率為6.4%,這個結(jié)果還比較不錯。這個例子表明我們可以正確地預(yù)測分類,現(xiàn)在可以調(diào)用k近鄰算法來對輸入的特征值進行分類,從而確定結(jié)果是屬于哪一類!
預(yù)測函數(shù)代碼如下:
def classifyPersion():
resultlist =['not at all','in small doses','in large doses']
percenttats = float(input("percentage of time spent playing video game:"))
ffmile = float(input('frequent filer miles earned per year:'))
icecream = float(input('liters of ice cream consumed per year:'))
datingDateMat,datingLabels = file2matrix('EXTRAS/datingTestSet2.txt')
normMat,ranges,minVals = autoNorm(datingDateMat)
inArr = array ([ffmile,percenttats,icecream])
classifierResult = classify0((inArr-minVals)/ranges,normMat,datingLabels,3)
print("You will probably like this person: ",resultlist[classifierResult-1])
現(xiàn)在運行這段代碼,并輸入測試人的信息,就能得出對這個人感興趣的程度,如下圖所示:

現(xiàn)在只要輸入某個人的這三項特征,就能得出對他感不感興趣,極大地提高了配對速率。
了解更多請關(guān)注作者微信公眾號:
