机器学习-分类之K近邻算法(KNN)原理及实战
k近鄰算法(KNN)
簡(jiǎn)介
KNN算法是數(shù)據(jù)挖掘分類技術(shù)中最簡(jiǎn)單的方法之一。它通過測(cè)量不同特征值之間的距離進(jìn)行分類的。其基本思路為:如果一個(gè)樣本在特征空間中的k個(gè)最近鄰樣本中的大多數(shù)屬于某一個(gè)類別,則該樣本也屬于這個(gè)類別。該方法在決定類別上只依據(jù)最近的一個(gè)或者幾個(gè)樣本的類別來(lái)決定待分類樣本所屬的類別。
原理
給定一個(gè)樣本數(shù)據(jù)集(稱為訓(xùn)練集),樣本集內(nèi)每一個(gè)數(shù)據(jù)都已經(jīng)存在標(biāo)簽。(例如:'兔子'這個(gè)數(shù)據(jù)的標(biāo)簽是'動(dòng)物')。現(xiàn)在,輸入一個(gè)新的數(shù)據(jù),含有諸多屬性,將每一個(gè)屬性與樣本集中的數(shù)據(jù)對(duì)應(yīng)的屬性進(jìn)行比較,通過算法找到樣本集中與它最相似的k個(gè)數(shù)據(jù), 再獲得這k個(gè)樣本數(shù)據(jù)出現(xiàn)最多的標(biāo)簽作為這個(gè)新數(shù)據(jù)的標(biāo)簽。
例如
| data1 | 4 | 0 | 0 | A |
| data2 | 2 | 1 | 1 | B |
| data3 | 0 | 0 | 0 | C |
這時(shí),一個(gè)新的數(shù)據(jù)需要分類。
| datainput | 4 | 0 | 0 |
我們將待標(biāo)記的數(shù)據(jù)和樣本集里面的數(shù)據(jù)比對(duì),發(fā)現(xiàn)它的每一個(gè)屬性均和data1一致,也只有data1和它完全一致,不難給與標(biāo)簽為A。
?
在上面這個(gè)看似簡(jiǎn)單的過程中其實(shí)有很多值得思考的東西。
首先我是如何判斷data1和它一致呢?
我是相同屬性去比較的,然后發(fā)現(xiàn)每一個(gè)屬性值完全相同。可是這是因?yàn)閷傩陨?#xff0c;數(shù)據(jù)量少,而且一個(gè)“等與不等”就能給出結(jié)論。但是事實(shí)上很多時(shí)候(幾乎所有時(shí)候)完全一致的兩個(gè)數(shù)據(jù)是幾乎不存在的,那種情況下那么多的屬性,我如何度量?jī)蓚€(gè)數(shù)據(jù)的接近程度,如何得到最接近的k個(gè)數(shù)據(jù)呢?
那么如何得到最接近的k個(gè)數(shù)據(jù)呢?
這個(gè)問題其實(shí)不難回答,只要求出待標(biāo)記數(shù)據(jù)與已知數(shù)據(jù)的屬性距離就ok了,至于距離怎么求?這個(gè)也不難,如果只有兩個(gè)屬性,可以建立二維坐標(biāo)系,求出新數(shù)據(jù)的點(diǎn)與其他所有點(diǎn)的距離即可。如果屬性更多,可以從二維上推廣,得到高維兩點(diǎn)之間的距離公式。
這個(gè)公式其實(shí)就是歐幾里得度量公式。(也可以使用曼哈頓距離公式,當(dāng)然這兩個(gè)都是閔可夫斯基距離公式的特例,但是只有歐式距離保證收斂)
就這樣,我們計(jì)算出了待標(biāo)記數(shù)據(jù)和所有樣本數(shù)據(jù)的距離,這是個(gè)數(shù)值,比較得到最接近的k個(gè)數(shù)據(jù),統(tǒng)計(jì)這k個(gè)數(shù)據(jù)的標(biāo)簽頻率,選取出現(xiàn)頻率最高的或者最高的中的一個(gè),為新數(shù)據(jù)打上標(biāo)簽。
k值的選擇
統(tǒng)計(jì)學(xué)方法中參數(shù)選擇一般是要在偏差(Bias)與方差(Variance)之間取得一個(gè)平衡(Tradeoff)。對(duì)KNN而言,k值的選擇也要在偏差和方差之間取得平衡。若k取值太小,則分類結(jié)果可能因?yàn)樵肼朁c(diǎn)的干擾而出現(xiàn)錯(cuò)誤,此時(shí)方差較大;若k取值太大,假設(shè)達(dá)到樣本數(shù)據(jù)總量,那么對(duì)所有的測(cè)試樣本而言,結(jié)果都一樣,分類的結(jié)果都是樣本最多的類別,這樣穩(wěn)定是穩(wěn)定了,但是預(yù)測(cè)結(jié)果與真實(shí)值相差太遠(yuǎn),偏差過大。因此,k值既不能太大也不能太小,通常的做法是,利用交叉驗(yàn)證(Cross Validation)評(píng)估一系列不同的k值,選取結(jié)果最好的k值作為訓(xùn)練參數(shù)。
實(shí)戰(zhàn)
使用KNN實(shí)現(xiàn)約會(huì)網(wǎng)站的配對(duì)效果
項(xiàng)目描述
假設(shè)存在這樣一種情況,海倫使用在線約會(huì)網(wǎng)站尋找合適自己的約會(huì)對(duì)象,盡管網(wǎng)站時(shí)常推薦,但是她沒有找到合適的人。經(jīng)過總結(jié),她發(fā)現(xiàn)交往過三類人。
- 不喜歡的(類別1)
- 魅力一般的(類別2)
- 極具魅力的)類別3)
盡管發(fā)現(xiàn)了這個(gè)規(guī)律,但是海倫依然無(wú)法將約會(huì)網(wǎng)站匹配的對(duì)象劃分到恰當(dāng)類別中,海倫希望分類軟件可以更好地幫助她將匹配對(duì)象劃分到正確的分類中。此外,海倫還收集到一些約會(huì)網(wǎng)站未曾記錄的信息,她認(rèn)為這些數(shù)據(jù)有助于匹配對(duì)象的分類。
根據(jù)已有數(shù)據(jù)集,不難發(fā)現(xiàn)每個(gè)樣本數(shù)據(jù)有三個(gè)特征一個(gè)類別,但是發(fā)現(xiàn)飛行里程數(shù)都是上萬(wàn)的,而其他兩項(xiàng)的值都不超過10,在計(jì)算距離時(shí)就會(huì)造成這個(gè)特征值權(quán)重極大,這是不合理的,因?yàn)槿齻€(gè)特征影響程度應(yīng)該相當(dāng),這里通過歸一化處理數(shù)據(jù)集的特征值。
代碼實(shí)現(xiàn)(注意區(qū)別)
自行編寫KNN算法
# -*-coding:utf-8-*- from numpy import * import operatordef classify0(inX, dataSet, labels, k):'''利用距離公式計(jì)算出最接近的k個(gè)樣本中出現(xiàn)最多的標(biāo)簽:param inX: 待分類的樣本:param dataSet: 特征矩陣:param labels: 類別矩陣:param k: k值:return: 最接近的標(biāo)簽'''print(inX)dataSetSize = dataSet.shape[0]diffMat = tile(inX, (dataSetSize, 1)) - dataSetsqDiffMat = diffMat ** 2sqDisttances = sqDiffMat.sum(axis=1)distances = sqDisttances ** 0.5sortedDistIndicies = distances.argsort()classCount = {}for i in range(k):voteIlabel = labels[sortedDistIndicies[i]]classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1# 依照標(biāo)簽出現(xiàn)的頻率排序sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True)# 選取頻率最大的標(biāo)簽return sortedClassCount[0][0]def file2matrix(filename):'''數(shù)據(jù)讀取為矩陣:param filename: 文件名:return: 特征矩陣, 標(biāo)簽矩陣'''fr = open(filename)arrayOLines = fr.readlines()numberOfLines = len(arrayOLines)returnMat = zeros((numberOfLines, 3))classLabelVector = []index = 0for line in arrayOLines:line = line.strip()listFromLine = line.split('\t')returnMat[index, :] = listFromLine[0:3]classLabelVector.append(int(listFromLine[-1]))index += 1return returnMat, classLabelVectordef autoNorm(dataSet):'''歸一化數(shù)據(jù):param dataSet: 數(shù)據(jù)集:return: 歸一化后的數(shù)據(jù)集'''minVals = dataSet.min(0)maxVals = dataSet.max(0)ranges = maxVals - minValsm = dataSet.shape[0]normDataSet = dataSet - tile(minVals, (m, 1))normDataSet = normDataSet / tile(ranges, (m, 1))print(normDataSet)return normDataSet, ranges, minValsdef classifyPerson():'''網(wǎng)站實(shí)測(cè):return: 可能標(biāo)簽'''resultList = ['不喜歡', '一般喜歡', '特別喜歡']percentTats = float(input("玩游戲占的百分比"))ffMiles = float(input("每年坐飛機(jī)多少公里"))iceCream = float(input("每年吃多少公升的冰淇淋"))datingDataMat, datingLabels = file2matrix('data/datingTestSet2.txt')normMat, ranges, minVals = autoNorm(datingDataMat)inArr = array([ffMiles, percentTats, iceCream])print((inArr - minVals) / ranges)classifierResult = classify0((inArr - minVals) / ranges, normMat, datingLabels, 3)print("你將有可能對(duì)這個(gè)人是:", resultList[int(classifierResult) - 1])if __name__ == '__main__':classifyPerson()?
使用sklearn的KNN模塊
# -*-coding:UTF-8 -*- from sklearn.neighbors import KNeighborsClassifier from sklearn.preprocessing import MinMaxScaler from numpy import *def file2matrix(filename):'''數(shù)據(jù)讀取為矩陣:param filename: 文件名:return: 特征矩陣, 標(biāo)簽矩陣'''fr = open(filename)arrayOLines = fr.readlines()numberOfLines = len(arrayOLines)returnMat = zeros((numberOfLines, 3))classLabelVector = []index = 0for line in arrayOLines:line = line.strip()listFromLine = line.split('\t')returnMat[index, :] = listFromLine[0:3]classLabelVector.append(int(listFromLine[-1]))index += 1return returnMat, classLabelVectordef autoNorm(dataSet):'''歸一化數(shù)據(jù):param dataSet: 數(shù)據(jù)集:return: 歸一化后的數(shù)據(jù)集'''minVals = dataSet.min(0)maxVals = dataSet.max(0)ranges = maxVals - minValsmin_max_scaler = MinMaxScaler()normDataSet = min_max_scaler.fit_transform(dataSet)return normDataSet, ranges, minValsdef classifyPerson(model):'''網(wǎng)站實(shí)測(cè):return: 可能標(biāo)簽'''resultList = ['不喜歡', '一般喜歡', '特別喜歡']percentTats = float(input("玩游戲占的百分比"))ffMiles = float(input("每年坐飛機(jī)多少公里"))iceCream = float(input("每年吃多少公升的冰淇淋"))datingDataMat, datingLabels = file2matrix('data/datingTestSet2.txt')normMat, ranges, minVals = autoNorm(datingDataMat)inArr = array([[ffMiles, percentTats, iceCream]])model.fit(normMat, datingLabels)print((inArr - minVals) / ranges)rst = model.predict((inArr - minVals) / ranges)print("你將有可能對(duì)這個(gè)人是:", resultList[int(rst) - 1])if __name__ == '__main__':model = KNeighborsClassifier(n_neighbors=3, weights='uniform', algorithm='auto', leaf_size=30,p=2, metric='minkowski', metric_params=None, n_jobs=1)classifyPerson(model)?
補(bǔ)充說明
參考《Python3數(shù)據(jù)分析與機(jī)器學(xué)習(xí)實(shí)戰(zhàn)》,具體的代碼和數(shù)據(jù)集可以在我的GitHub找到,歡迎star或者fork。
總結(jié)
以上是生活随笔為你收集整理的机器学习-分类之K近邻算法(KNN)原理及实战的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 数据分析与挖掘-python常用数据预处
- 下一篇: 数据分析机器学习-分类好坏的评价方式