机器学习-分类之K近邻算法(KNN)原理及实战
k近鄰算法(KNN)
簡介
KNN算法是數據挖掘分類技術中最簡單的方法之一。它通過測量不同特征值之間的距離進行分類的。其基本思路為:如果一個樣本在特征空間中的k個最近鄰樣本中的大多數屬于某一個類別,則該樣本也屬于這個類別。該方法在決定類別上只依據最近的一個或者幾個樣本的類別來決定待分類樣本所屬的類別。
原理
給定一個樣本數據集(稱為訓練集),樣本集內每一個數據都已經存在標簽。(例如:'兔子'這個數據的標簽是'動物')。現在,輸入一個新的數據,含有諸多屬性,將每一個屬性與樣本集中的數據對應的屬性進行比較,通過算法找到樣本集中與它最相似的k個數據, 再獲得這k個樣本數據出現最多的標簽作為這個新數據的標簽。
例如
| data1 | 4 | 0 | 0 | A |
| data2 | 2 | 1 | 1 | B |
| data3 | 0 | 0 | 0 | C |
這時,一個新的數據需要分類。
| datainput | 4 | 0 | 0 |
我們將待標記的數據和樣本集里面的數據比對,發現它的每一個屬性均和data1一致,也只有data1和它完全一致,不難給與標簽為A。
?
在上面這個看似簡單的過程中其實有很多值得思考的東西。
首先我是如何判斷data1和它一致呢?
我是相同屬性去比較的,然后發現每一個屬性值完全相同。可是這是因為屬性少,數據量少,而且一個“等與不等”就能給出結論。但是事實上很多時候(幾乎所有時候)完全一致的兩個數據是幾乎不存在的,那種情況下那么多的屬性,我如何度量兩個數據的接近程度,如何得到最接近的k個數據呢?
那么如何得到最接近的k個數據呢?
這個問題其實不難回答,只要求出待標記數據與已知數據的屬性距離就ok了,至于距離怎么求?這個也不難,如果只有兩個屬性,可以建立二維坐標系,求出新數據的點與其他所有點的距離即可。如果屬性更多,可以從二維上推廣,得到高維兩點之間的距離公式。
這個公式其實就是歐幾里得度量公式。(也可以使用曼哈頓距離公式,當然這兩個都是閔可夫斯基距離公式的特例,但是只有歐式距離保證收斂)
就這樣,我們計算出了待標記數據和所有樣本數據的距離,這是個數值,比較得到最接近的k個數據,統計這k個數據的標簽頻率,選取出現頻率最高的或者最高的中的一個,為新數據打上標簽。
k值的選擇
統計學方法中參數選擇一般是要在偏差(Bias)與方差(Variance)之間取得一個平衡(Tradeoff)。對KNN而言,k值的選擇也要在偏差和方差之間取得平衡。若k取值太小,則分類結果可能因為噪聲點的干擾而出現錯誤,此時方差較大;若k取值太大,假設達到樣本數據總量,那么對所有的測試樣本而言,結果都一樣,分類的結果都是樣本最多的類別,這樣穩定是穩定了,但是預測結果與真實值相差太遠,偏差過大。因此,k值既不能太大也不能太小,通常的做法是,利用交叉驗證(Cross Validation)評估一系列不同的k值,選取結果最好的k值作為訓練參數。
實戰
使用KNN實現約會網站的配對效果
項目描述
假設存在這樣一種情況,海倫使用在線約會網站尋找合適自己的約會對象,盡管網站時常推薦,但是她沒有找到合適的人。經過總結,她發現交往過三類人。
- 不喜歡的(類別1)
- 魅力一般的(類別2)
- 極具魅力的)類別3)
盡管發現了這個規律,但是海倫依然無法將約會網站匹配的對象劃分到恰當類別中,海倫希望分類軟件可以更好地幫助她將匹配對象劃分到正確的分類中。此外,海倫還收集到一些約會網站未曾記錄的信息,她認為這些數據有助于匹配對象的分類。
根據已有數據集,不難發現每個樣本數據有三個特征一個類別,但是發現飛行里程數都是上萬的,而其他兩項的值都不超過10,在計算距離時就會造成這個特征值權重極大,這是不合理的,因為三個特征影響程度應該相當,這里通過歸一化處理數據集的特征值。
代碼實現(注意區別)
自行編寫KNN算法
# -*-coding:utf-8-*- from numpy import * import operatordef classify0(inX, dataSet, labels, k):'''利用距離公式計算出最接近的k個樣本中出現最多的標簽:param inX: 待分類的樣本:param dataSet: 特征矩陣:param labels: 類別矩陣:param k: k值:return: 最接近的標簽'''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# 依照標簽出現的頻率排序sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True)# 選取頻率最大的標簽return sortedClassCount[0][0]def file2matrix(filename):'''數據讀取為矩陣:param filename: 文件名:return: 特征矩陣, 標簽矩陣'''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):'''歸一化數據:param dataSet: 數據集:return: 歸一化后的數據集'''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():'''網站實測:return: 可能標簽'''resultList = ['不喜歡', '一般喜歡', '特別喜歡']percentTats = float(input("玩游戲占的百分比"))ffMiles = float(input("每年坐飛機多少公里"))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("你將有可能對這個人是:", 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):'''數據讀取為矩陣:param filename: 文件名:return: 特征矩陣, 標簽矩陣'''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):'''歸一化數據:param dataSet: 數據集:return: 歸一化后的數據集'''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):'''網站實測:return: 可能標簽'''resultList = ['不喜歡', '一般喜歡', '特別喜歡']percentTats = float(input("玩游戲占的百分比"))ffMiles = float(input("每年坐飛機多少公里"))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("你將有可能對這個人是:", 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)?
補充說明
參考《Python3數據分析與機器學習實戰》,具體的代碼和數據集可以在我的GitHub找到,歡迎star或者fork。
總結
以上是生活随笔為你收集整理的机器学习-分类之K近邻算法(KNN)原理及实战的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 数据分析与挖掘-python常用数据预处
- 下一篇: 数据分析机器学习-分类好坏的评价方式