摘要:而產生這種現象的唯一遠遠,僅僅是因為飛行常客里程數遠大于其他特征值。但海倫認為這三種特征是同等重要的,因此作為三個等權重的特征之一,飛行常客里程數并不應該如此嚴重的影響到計算結果。
一、KNN概述
簡單的說,k-近鄰算法采用測量不同特征值之間的距離方法進行分類。
優點:精度高、對異常值不敏感、無數據輸入假定
缺點:計算復雜度高、空間復雜度高
適用數據范圍:數值型和標稱型
1.1 工作原理KNN可以說是最簡單的分類算法之一,同時,它也是最常見的分類算法之一,注意KNN算法是有監督學習的分類算法,它看起來和另外一個機器學習算法Kmeans有點像(Kmeans是無監督學習算法)。
其工作原理是使用一個樣本數據集合,也稱為訓練樣本集,并且樣本集中每個數據都存在標簽,即我們知道樣本集中每一數據與所屬分類的對應關系。輸入沒有標簽的新數據后,將新數據的每個特征與樣本集中的數據對應的特征進行比較,然后算法提取樣本集中特征最相似(最近鄰)的分類標簽。一般來說,我們只選擇樣本集中前k個最相似的數據,這就是KNN中K的出處,最后使用Majority-Voting(多數表決)選擇k個最相似數據中次數出現最多的分類,作為新數據的分類。
1.2 三要素K的取值:可以使用Cross Validation(交叉驗證)來選取合適的值
我們該如何選擇合適的k值呢?通過將樣本數據按照一定比例,拆分出訓練用的數據和驗證用的數據,比如6:4拆出訓練數據和驗證數據,從選取一個較小的k值開始,不斷增加k的值,然后計算驗證集合的方差,最終找到一個比較合適的k值。
距離度量 Metric / Distance Measure:距離度量一般都使用 Euclidean distance(歐氏距離)
二維空間兩個點的歐式距離公式如下
分類決策 Deision rule:分類決策即Majority-Voting ,選取票數最多的標簽,在回歸中通常為k個最鄰近點的標簽的平均值
1.3 在什么時候選擇KNN算法 二、實踐案例(主要重點是可視化) 2.1 實例一 :電影分類電影名稱 打斗鏡頭 接吻鏡頭 電影類型 Californla Man 3 104 愛情片 He`s Not Really into Dudes 2 100 愛情片 Beautiful Woman 1 81 愛情片 Kevin Longblade 101 10 動作片 Robo Slayer 3000 99 5 動作片 Ampedll 98 2 動作片 ? 18 90 未知
首先我們通過 Python 的第三方庫進行數據可視化處理
import matplotlib.pyplot as plt import numpy as np import operator # 已知分類的數據 x1=np.array([3,2,1]) y1=np.array([104,100,81]) x2=np.array([101,99,98]) y2=np.array([10,5,2]) scatter1 = plt.scatter(x1,y1,c="r") scatter2 = plt.scatter(x2,y2,c="b") # 求未知數據 x = np.array([18]) y = np.array([90]) scatter3 = plt.scatter(x,y,c="k") #畫圖例 plt.legend(handles=[scatter1,scatter2,scatter3],labels=["labelA","labelB","X"],loc = "best" ) plt.show()
KNN近鄰算法最核心的部分就是歐式距離的計算,通過計算得到距離最近的k個數的標簽,統計出現次數最多的標簽,將其賦值給新數據。下面這段代碼就是KNN算法最基本的實例。通過傳入數據來預測電影的標簽是什么!
from numpy import * import operator # 電影分類 # 歐式距離求解返回前k個標簽 然后匯總出現次數最多的標簽 def classify0(inX, dataSet, labels, k): dataSetSize = dataSet.shape[0] # 求inX與數據集中各個樣本的歐氏距離 diffMat = tile(inX, (dataSetSize,1)) - dataSet # numpy中的tile函數將inX復制為重復的dataSize個行和重復的1列,功能相當于MATLAB中的repmat sqDiffMat = diffMat**2 sqDistances = sqDiffMat.sum(axis=1) # 按照x軸相加 distances = sqDistances**0.5 # print(distances) sortedDistIndicies = distances.argsort() # 從小到大排序后,返回索引 # 這個返回索引是關鍵 只有理解了這個返回索引才能理解k-nn算法 # print(sortedDistIndicies) # 字典,key存儲第i小的標簽值,value為標簽的次數 classCount = {} for i in range(k): voteIlabel = labels[sortedDistIndicies[i]] # 取第i個小的標簽值 # print(sortedDistIndicies[i]) classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1 # 根據標簽統計標簽次數,如果沒找到返回0。統計前k個候選者中標簽出現的次數 sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True) # operator.itemgetter(1) 按照第2個元素,即標簽出現的次數對classCount從大到小排序 # print(sortedClassCount) # 測試結果 [("B", 2), ("A", 1)] return sortedClassCount[0][0] # 返回最終的類別,即標簽值key # 傳輸數據 def createDataSet(): group = array([[3,104],[2,100],[1,81],[101,10],[99,5],[98,2]]) labels = ["A","A","A","B","B","B"] return group, labels # 測試代碼 group, labels = createDataSet() ans = classify0([18,90], group, labels, 3) print(ans) >>> A2.2 實例二:優化約會匹配效果 1-項目概論
海倫使用約會網站尋求約會對象,經過一段實踐之后,她發現曾交往過三類人:
不喜歡的人
魅力一般的人
極具魅力的人
她希望:
工作日與魅力一般的人約會
周末與極具魅力的人約會
不喜歡的人則直接排除掉
現在她收集到了一些約會網站未曾記錄的數據信息,這更有助于匹配對象的歸類。
2-開發流程1. 收集數據:提供文本文件 2. 準本數據:使用Python解析文本文件 3. 分析數據:使用Matplotlib 畫二維散點圖 4. 訓練算法:此步驟不適用與KNN算法,但是也很重要 5. 測試數據:使用海倫提供的部分數據作為測試樣本 6. 使用算法:產生簡單的命令行程序,然后海倫可以輸入一些特征數據以判斷對方是否為自己喜歡的類型
測試樣本與非測試樣本的區別在于:
測試樣本是以及完成分類的數據,如果測試分類與實際類不同,則標記為一個錯誤
海倫把這些約會對象的數據存放在文本文件 datingTestSet2.txt 中,總共有1000行,海倫約會的對象主要包含以下3種特征:
每年獲得的飛行常客里程數
玩視頻游戲所耗費時間百分比
每周消費的冰激凌公升數
文本文件數據格式如下
40920 8.326976 0.953952 3 14488 7.153469 1.673904 2 26052 1.441871 0.805124 1 75136 13.147394 0.428964 1 38344 1.669788 0.134296 14-準備數據:使用python解析文本
將文本記錄轉換為 NUmPy的解析程序
from numpy import zeros def file2matrix(filename): """ Desc: 導入訓練數據 Parameters: filename:數據文件路徑 return: 數據矩陣 returnMat 和對應的類別 classLabelVector """ fr = open("datingTestSet2.txt") # 獲得文件中的數據行的行數 numberOfLines = len(fr.readlines()) # 生成對應的空矩陣 # 例如:zeros(2,3)就是生成一個 2*3 的矩陣,各個位置上全是0 returnMat = zeros((numberOfLines , 3)) # prepare matrix to return classLabelVector = [] fr = open("datingTestSet2.txt") index =0 for line in fr.readlines(): # str.strip([chars]) -- 返回已移除字符串頭尾指定字符所生成的新字符串 line = line.strip() # 以 切割字符串 listFromLine = line.split(" ") # 每列的屬性數據 returnMat[index , : ] = listFromLine[0:3] # 每列的類別數據,就是 label 標簽數據 classLabelVector.append(int(listFromLine[-1])) index += 1 # 返回數據矩陣 returnMat 和對應的類別 classLabelVector print(returnMat,classLabelVector) return returnMat,classLabelVector if __name__=="__main__": file2matrix(1) >>> [[4.0920000e+04 8.3269760e+00 9.5395200e-01] [1.4488000e+04 7.1534690e+00 1.6739040e+00] [2.6052000e+04 1.4418710e+00 8.0512400e-01] ... [2.6575000e+04 1.0650102e+01 8.6662700e-01] [4.8111000e+04 9.1345280e+00 7.2804500e-01] [4.3757000e+04 7.8826010e+00 1.3324460e+00]] [3, 2, 1, 1, 1, 1, 3, 3, 1, 3, 1, 1, 2, 1, 1, 1, 1, 1, 2, 3, 2, 1, 2, 3, 2, 3, 2, 3, 2, 1, 3, 1, 3, 1, 2, 1, 1, 2, 3, 3, 1, 2, 3, 3, 3, 1, 1, 1, 1, 2, 2, 1, 3, 2, 2, 2, 2, 3, 1, 2, 1, 2, 2, 2, 2, 2, 3, 2, 3, 1, 2, 3, 2, 2, 1, 3, 1, 1, 3, 3, 1, 2, 3, 1, 3, 1, 2, 2, 1, 1, 3, 3, 1, 2, 1, 3, 3, 2, 1, 1, 3, 1, 2, 3, 3, 2, 3, 3, 1, 2, 3, 2, 1, 3, 1, 2, 1, 1, 2, 3, 2, 3, 2, 3, 2, 1, 3, 3, 3, 1, 3, 2, 2, 3, 1, 3, 3, 3, 1, 3, 1, 1, 3, 3, 2, 3, 3, 1, 2, 3, 2, 2, 3, 3, 3, 1, 2, 2, 1, 1, 3, 2, 3, 3, 1, 2, 1, 3, 1, 2, 3, 2, 3, 1, 1, 1, 3, 2, 3, 1, 3, 2, 1, 3, 2, 2, 3, 2, 3, 2, 1, 1, 3, 1, 3, 2, 2, 2, 3, 2, 2, 1, 2, 2, 3, 1, 3, 3, 2, 1, 1, 1, 2, 1, 3, 3, 3, 3, 2, 1, 1, 1, 2, 3, 2, 1, 3, 1, 3, 2, 2, 3, 1, 3, 1, 1, 2, 1, 2, 2, 1, 3, 1, 3, 2, 3, 1, 2, 3, 1, 1, 1, 1, 2, 3, 2, 2, 3, 1, 2, 1, 1, 1, 3, 3, 2, 1, 1, 1, 2, 2, 3, 1, 1, 1, 2, 1, 1, 2, 1, 1, 1, 2, 2, 3, 2, 3, 3, 3, 3, 1, 2, 3, 1, 1, 1, 3, 1, 3, 2, 2, 1, 3, 1, 3, 2, 2, 1, 2, 2, 3, 1, 3, 2, 1, 1, 3, 3, 2, 3, 3, 2, 3, 1, 3, 1, 3, 3, 1, 3, 2, 1, 3, 1, 3, 2, 1, 2, 2, 1, 3, 1, 1, 3, 3, 2, 2, 3, 1, 2, 3, 3, 2, 2, 1, 1, 1, 1, 3, 2, 1, 1, 3, 2, 1, 1, 3, 3, 3, 2, 3, 2, 1, 1, 1, 1, 1, 3, 2, 2, 1, 2, 1, 3, 2, 1, 3, 2, 1, 3, 1, 1, 3, 3, 3, 3, 2, 1, 1, 2, 1, 3, 3, 2, 1, 2, 3, 2, 1, 2, 2, 2, 1, 1, 3, 1, 1, 2, 3, 1, 1, 2, 3, 1, 3, 1, 1, 2, 2, 1, 2, 2, 2, 3, 1, 1, 1, 3, 1, 3, 1, 3, 3, 1, 1, 1, 3, 2, 3, 3, 2, 2, 1, 1, 1, 2, 1, 2, 2, 3, 3, 3, 1, 1, 3, 3, 2, 3, 3, 2, 3, 3, 3, 2, 3, 3, 1, 2, 3, 2, 1, 1, 1, 1, 3, 3, 3, 3, 2, 1, 1, 1, 1, 3, 1, 1, 2, 1, 1, 2, 3, 2, 1, 2, 2, 2, 3, 2, 1, 3, 2, 3, 2, 3, 2, 1, 1, 2, 3, 1, 3, 3, 3, 1, 2, 1, 2, 2, 1, 2, 2, 2, 2, 2, 3, 2, 1, 3, 3, 2, 2, 2, 3, 1, 2, 1, 1, 3, 2, 3, 2, 3, 2, 3, 3, 2, 2, 1, 3, 1, 2, 1, 3, 1, 1, 1, 3, 1, 1, 3, 3, 2, 2, 1, 3, 1, 1, 3, 2, 3, 1, 1, 3, 1, 3, 3, 1, 2, 3, 1, 3, 1, 1, 2, 1, 3, 1, 1, 1, 1, 2, 1, 3, 1, 2, 1, 3, 1, 3, 1, 1, 2, 2, 2, 3, 2, 2, 1, 2, 3, 3, 2, 3, 3, 3, 2, 3, 3, 1, 3, 2, 3, 2, 1, 2, 1, 1, 1, 2, 3, 2, 2, 1, 2, 2, 1, 3, 1, 3, 3, 3, 2, 2, 3, 3, 1, 2, 2, 2, 3, 1, 2, 1, 3, 1, 2, 3, 1, 1, 1, 2, 2, 3, 1, 3, 1, 1, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 2, 2, 2, 3, 1, 3, 1, 2, 3, 2, 2, 3, 1, 2, 3, 2, 3, 1, 2, 2, 3, 1, 1, 1, 2, 2, 1, 1, 2, 1, 2, 1, 2, 3, 2, 1, 3, 3, 3, 1, 1, 3, 1, 2, 3, 3, 2, 2, 2, 1, 2, 3, 2, 2, 3, 2, 2, 2, 3, 3, 2, 1, 3, 2, 1, 3, 3, 1, 2, 3, 2, 1, 3, 3, 3, 1, 2, 2, 2, 3, 2, 3, 3, 1, 2, 1, 1, 2, 1, 3, 1, 2, 2, 1, 3, 2, 1, 3, 3, 2, 2, 2, 1, 2, 2, 1, 3, 1, 3, 1, 3, 3, 1, 1, 2, 3, 2, 2, 3, 1, 1, 1, 1, 3, 2, 2, 1, 3, 1, 2, 3, 1, 3, 1, 3, 1, 1, 3, 2, 3, 1, 1, 3, 3, 3, 3, 1, 3, 2, 2, 1, 1, 3, 3, 2, 2, 2, 1, 2, 1, 2, 1, 3, 2, 1, 2, 2, 3, 1, 2, 2, 2, 3, 2, 1, 2, 1, 2, 3, 3, 2, 3, 1, 1, 3, 3, 1, 2, 2, 2, 2, 2, 2, 1, 3, 3, 3, 3, 3, 1, 1, 3, 2, 1, 2, 1, 2, 2, 3, 2, 2, 2, 3, 1, 2, 1, 2, 2, 1, 1, 2, 3, 3, 1, 1, 1, 1, 3, 3, 3, 3, 3, 3, 1, 3, 3, 2, 3, 2, 3, 3, 2, 2, 1, 1, 1, 3, 3, 1, 1, 1, 3, 3, 2, 1, 2, 1, 1, 2, 2, 1, 1, 1, 3, 1, 1, 2, 3, 2, 2, 1, 3, 1, 2, 3, 1, 2, 2, 2, 2, 3, 2, 3, 3, 1, 2, 1, 2, 3, 1, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 1, 3, 3, 3]5-分析數據:使用Matplotlib畫二維散點圖
下面的代碼里面有三分可視化的結果,但是我們可以在 Notebook中很明顯的看到每年獲取的飛行里程數和玩視頻游戲所消耗的時間百分比所構成的坐標圖非常的清晰的分成三個部分,這就為我們后續的計算距離分類奠定基礎。
from numpy import zeros import numpy as np def file2matrix(filename): """ Desc: 導入訓練數據 Parameters: filename:數據文件路徑 return: 數據矩陣 returnMat 和對應的類別 classLabelVector """ fr = open("datingTestSet2.txt") # 獲得文件中的數據行的行數 numberOfLines = len(fr.readlines()) # 生成對應的空矩陣 # 例如:zeros(2,3)就是生成一個 2*3 的矩陣,各個位置上全是0 returnMat = zeros((numberOfLines , 3)) # prepare matrix to return classLabelVector = [] fr = open("datingTestSet2.txt") index =0 for line in fr.readlines(): # str.strip([chars]) -- 返回已移除字符串頭尾指定字符所生成的新字符串 line = line.strip() # 以 切割字符串 listFromLine = line.split(" ") # 每列的屬性數據 returnMat[index , : ] = listFromLine[0:3] # 每列的類別數據,就是 label 標簽數據 classLabelVector.append(int(listFromLine[-1])) index += 1 # 返回數據矩陣 returnMat 和對應的類別 classLabelVector # print(returnMat,classLabelVector) return returnMat,classLabelVector # 因為 matplotlib 中不可顯示中文,所以使用本機自帶的字體進行替換 # 參考鏈接:https://www.cnblogs.com/pengsky2016/p/8126623.html # 該鏈接是本書的該可視化部分的詳細講解 非常不錯 from matplotlib.font_manager import FontProperties zhfont = FontProperties(fname="C:WindowsFontsmsyh.ttc",size=12) if __name__=="__main__": datingDataMat,datingLabels = file2matrix(1) # print(datingDataMat) # print(datingLabels) import matplotlib import matplotlib.pyplot as plt fig = plt.figure() ax = fig.add_subplot(111) from numpy import * datingLabels = np.array(datingLabels) # 每年獲取的飛行里程數-玩視頻游戲所消耗的事件百分比 # datingDataMat[idx_1,0] 中的參數 0 和 1 以及 2 就是我們所選擇的橫坐標和縱坐標的選取值 # 我們可以根據這三個值的變動來變動我們的坐標軸的選取方法 idx_1 = np.where(datingLabels==1) p1 = ax.scatter(datingDataMat[idx_1,0],datingDataMat[idx_1,1],marker = "*",color = "r",label="1",s=10) idx_2 = np.where(datingLabels==2) p2 = ax.scatter(datingDataMat[idx_2,0],datingDataMat[idx_2,1],marker = "o",color ="g",label="2",s=20) idx_3 = np.where(datingLabels==3) p3 = ax.scatter(datingDataMat[idx_3,0],datingDataMat[idx_3,1],marker = "+",color ="b",label="3",s=30) plt.xlabel(u"每年獲取的飛行里程數", fontproperties=zhfont) plt.ylabel(u"玩視頻游戲所消耗的事件百分比", fontproperties=zhfont) ax.legend((p1, p2, p3), (u"不喜歡", u"魅力一般", u"極具魅力"), loc=2, prop=zhfont) plt.show() # 玩視頻游戲所消耗的事件百分比-每周消耗的冰激凌公升數 # datingLabels = np.array(datingLabels) # idx_1 = np.where(datingLabels==1) # p1 = ax.scatter(datingDataMat[idx_1,1],datingDataMat[idx_1,2],marker = "*",color = "r",label="1",s=10) # idx_2 = np.where(datingLabels==2) # p2 = ax.scatter(datingDataMat[idx_2,1],datingDataMat[idx_2,2],marker = "o",color ="g",label="2",s=20) # idx_3 = np.where(datingLabels==3) # p3 = ax.scatter(datingDataMat[idx_3,1],datingDataMat[idx_3,2],marker = "+",color ="b",label="3",s=30) # plt.xlabel(u"玩視頻游戲所消耗的事件百分比", fontproperties=zhfont) # plt.ylabel(u"", fontproperties=zhfont) # ax.legend((p1, p2, p3), (u"不喜歡", u"魅力一般", u"極具魅力"), loc=2, prop=zhfont) # plt.show() # 飛行常客里程數 - 每周消耗的冰激凌公升數 # datingLabels = np.array(datingLabels) # idx_1 = np.where(datingLabels==1) # p1 = ax.scatter(datingDataMat[idx_1,0],datingDataMat[idx_1,2],marker = "*",color = "r",label="1",s=10) # idx_2 = np.where(datingLabels==2) # p2 = ax.scatter(datingDataMat[idx_2,0],datingDataMat[idx_2,2],marker = "o",color ="g",label="2",s=20) # idx_3 = np.where(datingLabels==3) # p3 = ax.scatter(datingDataMat[idx_3,0],datingDataMat[idx_3,2],marker = "+",color ="b",label="3",s=30) # plt.xlabel(u"每年獲取的飛行里程數", fontproperties=zhfont) # plt.ylabel(u"玩視頻游戲所消耗的事件百分比", fontproperties=zhfont) # ax.legend((p1, p2, p3), (u"不喜歡", u"魅力一般", u"極具魅力"), loc=2, prop=zhfont) # plt.show()6-歸一化數值
歸一化數據是一個讓權重變為統一的過程,比如你要買進10噸鐵礦,用的人民幣和美元肯定不同,那么這10噸鐵礦的價值到底是多少,就需要一個統一的標準來衡量,全世界那么多國家,都要用自己國家的貨幣去買,到底該付多少就很迷茫。這時,規定用美元統一結算,各國按照本國貨幣對比美元的匯率,再加上10噸鐵礦的美元價值,就可以算出自己應付多少本國貨幣。
序號 玩視頻游戲所耗時間百分比 每年獲得的飛行常客里程數 每周消耗的冰激凌公升數 樣本分類 1 0.8 400 0.5 1 2 12 134000 0.9 3 3 0 20000 1.1 2 4 67 32000 0.1 2
表2-2給出了提取的四組數據,如果想要計算樣本3和樣本4之間的距離,可以使用下面的方法:
√((0?67)^2+(20 000 ?32 000)^2+(1.1 ?0.1)^2 )
我們很容易發現,上面方程中數字差值最大的數學對計算結果的影響最大,也就是說,每年獲取的飛行常客里程數對于計算結果的影響遠遠大于表2-
在處理這種不同取值范圍的特征值時,我們通常采用的方法是將數值歸一化,如將取值范圍處理為0到1或-1到1之間。下面的公式可以將任意取值范圍的特征值轉換為0到1區間內的值:
newValue = (oldValue - min ) / (max - min)
其中 min 和 max 分別是數據集中的最小特征值和最大特征值。雖然改變數值取值范圍增加了分類器的復雜度,但為了得到準確結果,我們必須這樣子做。我們需要在文件中增加一個新的函數 autoNorm(),該函數可以自動將數字特征值轉換為0到1的區間。
2中其他兩個特征——玩視頻游戲和每周消費冰激凌公升數——的影響。而產生這種現象的唯一遠遠,僅僅是因為飛行常客里程數遠大于其他特征值。但海倫認為這三種特征是同等重要的,因此作為三個等權重的特征之一,飛行常客里程數并不應該如此嚴重的影響到計算結果。
from numpy import zeros import numpy as np def file2matrix(filename): """ Desc: 導入訓練數據 Parameters: filename:數據文件路徑 return: 數據矩陣 returnMat 和對應的類別 classLabelVector """ fr = open("datingTestSet2.txt") # 獲得文件中的數據行的行數 numberOfLines = len(fr.readlines()) # 生成對應的空矩陣 # 例如:zeros(2,3)就是生成一個 2*3 的矩陣,各個位置上全是0 returnMat = zeros((numberOfLines , 3)) # prepare matrix to return classLabelVector = [] fr = open("datingTestSet2.txt") index =0 for line in fr.readlines(): # str.strip([chars]) -- 返回已移除字符串頭尾指定字符所生成的新字符串 line = line.strip() # 以 切割字符串 listFromLine = line.split(" ") # 每列的屬性數據 returnMat[index , : ] = listFromLine[0:3] # 每列的類別數據,就是 label 標簽數據 classLabelVector.append(int(listFromLine[-1])) index += 1 # 返回數據矩陣 returnMat 和對應的類別 classLabelVector # print(returnMat,classLabelVector) return returnMat,classLabelVector def autoNorm(dataset): """ Desc: 歸一化特征值,消除特征之間量級不同導致的影響 parameter: dataset:數據集 return : 歸一化后的數據集 normDataSet , ranges 和 minVals 即最小值與范圍,并沒有歸一化公式 歸一化公式: Y=(X-Xmin)/(Xmax - Xmin) 其中的 min 和 max 分別是數據集中的最小特征值和最大特征值,該函數可以自動將數字特征值轉化為0到1的區間 """ # 計算每種屬性的最大值、最小值、范圍 minVals = dataset.min(0) maxVals = dataset.max(0) ranges = maxVals - minVals # 極差 normDataSet = zeros(shape(dataset)) m = dataset.shape[0] #生成與最小值之差組成的矩陣 # 因為特征值矩陣有1000 * 3 個值,而 minVals 和 range 的值都為1x3, # 為了解決這個問題,我們使用Numpy庫中的tile()函數將變量內容復制成輸入矩陣同樣大小的矩陣, # 注意這是具體特征值相除,而不是矩陣除法,否則還需要使用函數linalg.solve(matA,matB) normDataSet = dataset - tile(minVals, (m,1)) # 將最小值之差除以范圍組成矩陣 normDataSet = normDataSet / tile(ranges, (m,1)) return normDataSet,range,minVals if __name__=="__main__": datingDataMat,datingLabels = file2matrix(1) normMat,ranges,minVals = autoNorm(datingDataMat) print(normMat) print(range) print(minVals)7-訓練算法
這個是核心內容,即計算各個標記點之間的距離并返回k個標記點內出現次數最多的標簽。
對于每一個在數據集中的數據點: 計算目標的數據點(需要分類的數據點) 與該數據點的距離 將距離排序:從小到大 選取k個最短距離 選取這k個最多的分類類別 返回該類別來作為目標數據點的預測值
整個訓練算法和之前的電影分類的算法是一致的,該算法進化的部分即對數據進行了歸一化處理,導致我們傳入進去的數據也需要進行歸一化處理。
# 訓練算法 # 詳解 k-nn訓練算法 :https://www.cnblogs.com/BaiYiShaoNian/p/4567446.html def classify0(inX,dataSet,labels,k): """ :param inX: 用于分類的輸入向量 :param dataSet: 訓練樣本集合 :param labels: 標簽向量 :param k: K-NN中的k """ # shape 是array的屬性 ,描述一個多維數組的維度 dataSetSize = dataSet.shape[0] # 距離度量 度量公式為歐式距離 """ tile(inX,(dataSetSize,1)) : 把 inX 二維數組化,dataSetSize 表示生成數組后的行數 1 表示列的倍數,整個這一行代表表示前一個二維數組矩陣的每一個元素減去后一個數組對應的元素值 這樣子就實現了矩陣之間的減法,簡單方便 """ diffMat = tile(inX,(dataSetSize,1)) - dataSet sqDiffMat = diffMat ** 2 sqDistances = sqDiffMat.sum(axis=1) # axis=1 表示矩陣中行之間數的求和 axis=0表示列之間數的求和 distances = sqDistances ** 0.5 # 將距離排序:從小到大 # 關鍵是傳回的是位置索引 而這個索引就可以得到在標簽中所對應位置的標簽 sortedDistIndicies = distances.argsort() #選取前k個最短距離,選取這k個中最多的分類類別 classCount = {} # print(labels) for i in range(k): voteIlabel = labels[sortedDistIndicies[i]] # 這一行是python語法 看不懂基礎問題 # get()該方法是訪問字典項的方法,即訪問下標為 voteIlabel的項,如果沒有這一項,那么初始值為0 # 然后把這一項的值加1 classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1 import operator sortedClassCount = sorted(classCount.items(), key = operator.itemgetter(1),reverse = True) return sortedClassCount[0][0]8-測試分類錯誤所占百分比
該函數的代碼是統計該KNN算法的實現效果,抽取一部分的數據作為數據集,進行測試然后統計測試錯誤的百分比
# 測試分類錯誤,錯誤分辨個數 def datingClassTest(): hoRatio = 0.08 # 隨機挖去 10% 的數據作為測試集 datingDataMat,datingLabels = file2matrix("datingTestSet2.txt") # 加載數據文件 normMat, ranges, minVals = autoNorm(datingDataMat) m = normMat.shape[0] numTestVecs = int(m*hoRatio) # 隨機挖去的行數 errorCount = 0.0 for i in range(numTestVecs): # 前numTestVecs條作為測試集(一個一個測試),后面的數據作為訓練樣本,訓練樣本的標簽,3個近鄰 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 number of errr is: %d" % int(errorCount)) print("The total error rate is: %f" % (errorCount / float(numTestVecs))) >>>The number of errr is: 2 >>>The total error rate is: 0.0250009-實踐算法:調用算法接口進行預測
直接在主函數中使用該函數調用數據預處理函數傳入數據、調用歸一化數據處理函數歸一化數據、調用訓練算法傳入數據進行預測,最后輸出算法判斷后的結論。
# 實踐算法 def validateTest(): 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?")) datingDataMat, datingLabels = file2matrix("datingTestSet2.txt") normMat, ranges, minVals = autoNorm(datingDataMat) inArr = array([ffMiles, percentTats, iceCream]) # print(str(inArr),str(minVals),str(ranges),normMat,datingLabels) # 為什么傳入的值需要進行 如下操作呢 (inArry - minVals)/range # 因為我們之前傳入的值都是進行過歸一化的 # 但是如果直接傳入值會過大 所以我們在前面的歸一化操作中將兩個參數傳過來就是為了這里使用 # 但是這里傳入的測試參數也是一個1x3的舉證 所有 minVals 和 ranges 不需要進行 tile化 classifierResult = classify0((inArr - minVals) / ranges, normMat, datingLabels, 3) print("You will probably like this person: ", resultList[classifierResult - 1]) # 主函數 if __name__ == "__main__": # 實踐算法 validateTest() >>>percentage of time spent playing video games ?10 >>>frequent filer miles earned per year?10000 >>>liters of ice cream consumed per year?0.5 >>>You will probably like this person: in small doses10-完整代碼
from numpy import zeros import numpy as np from numpy import * import operator # 數據預處理 def file2matrix(filename): """ Desc: 導入訓練數據 Parameters: filename:數據文件路徑 return: 數據矩陣 returnMat 和對應的類別 classLabelVector """ fr = open("datingTestSet2.txt") # 獲得文件中的數據行的行數 numberOfLines = len(fr.readlines()) # 生成對應的空矩陣 # 例如:zeros(2,3)就是生成一個 2*3 的矩陣,各個位置上全是0 returnMat = zeros((numberOfLines, 3)) # prepare matrix to return classLabelVector = [] fr = open("datingTestSet2.txt") index = 0 for line in fr.readlines(): # str.strip([chars]) -- 返回已移除字符串頭尾指定字符所生成的新字符串 line = line.strip() # 以 切割字符串 listFromLine = line.split(" ") # 每列的屬性數據 returnMat[index, :] = listFromLine[0:3] # 每列的類別數據,就是 label 標簽數據 classLabelVector.append(int(listFromLine[-1])) index += 1 # 返回數據矩陣 returnMat 和對應的類別 classLabelVector # print(returnMat,classLabelVector) return returnMat, classLabelVector # 因為 matplotlib 中不可顯示中文,所以使用本機自帶的字體進行替換 # 參考鏈接:https://www.cnblogs.com/pengsky2016/p/8126623.html # 該鏈接是本書的該可視化部分的詳細講解 非常不錯 from matplotlib.font_manager import FontProperties zhfont = FontProperties(fname="C:WindowsFontsmsyh.ttc", size=12) # 歸一化特征值 def autoNorm(dataset): """ Desc: 歸一化特征值,消除特征之間量級不同導致的影響 parameter: dataset:數據集 return : 歸一化后的數據集 normDataSet , ranges 和 minVals 即最小值與范圍,并沒有歸一化公式 歸一化公式: Y=(X-Xmin)/(Xmax - Xmin) 其中的 min 和 max 分別是數據集中的最小特征值和最大特征值,該函數可以自動將數字特征值轉化為0到1的區間 """ # 計算每種屬性的最大值、最小值、范圍 minVals = dataset.min(0) maxVals = dataset.max(0) ranges = maxVals - minVals # 極差 normDataSet = zeros(shape(dataset)) m = dataset.shape[0] #生成與最小值之差組成的矩陣 # 因為特征值矩陣有1000 * 3 個值,而 minVals 和 range 的值都為1x3, # 為了解決這個問題,我們使用Numpy庫中的tile()函數將變量內容復制成輸入矩陣同樣大小的矩陣, # 注意這是具體特征值相除,而不是矩陣除法,否則還需要使用函數linalg.solve(matA,matB) normDataSet = dataset - tile(minVals, (m,1)) # 將最小值之差除以范圍組成矩陣 normDataSet = normDataSet / tile(ranges, (m,1)) return normDataSet,ranges,minVals # 訓練算法 # 詳解 k-nn訓練算法 :https://www.cnblogs.com/BaiYiShaoNian/p/4567446.html def classify0(inX,dataSet,labels,k): """ :param inX: 用于分類的輸入向量 :param dataSet: 訓練樣本集合 :param labels: 標簽向量 :param k: K-NN中的k """ # shape 是array的屬性 ,描述一個多維數組的維度 dataSetSize = dataSet.shape[0] # 距離度量 度量公式為歐式距離 """ tile(inX,(dataSetSize,1)) : 把 inX 二維數組化,dataSetSize 表示生成數組后的行數 1 表示列的倍數,整個這一行代表表示前一個二維數組矩陣的每一個元素減去后一個數組對應的元素值 這樣子就實現了矩陣之間的減法,簡單方便 """ diffMat = tile(inX,(dataSetSize,1)) - dataSet sqDiffMat = diffMat ** 2 sqDistances = sqDiffMat.sum(axis=1) # axis=1 表示矩陣中行之間數的求和 axis=0表示列之間數的求和 distances = sqDistances ** 0.5 # 將距離排序:從小到大 # 關鍵是傳回的是位置索引 而這個索引就可以得到在標簽中所對應位置的標簽 sortedDistIndicies = distances.argsort() #選取前k個最短距離,選取這k個中最多的分類類別 classCount = {} # print(labels) for i in range(k): voteIlabel = labels[sortedDistIndicies[i]] # 這一行是python語法 看不懂基礎問題 # get()該方法是訪問字典項的方法,即訪問下標為 voteIlabel的項,如果沒有這一項,那么初始值為0 # 然后把這一項的值加1 classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1 import operator sortedClassCount = sorted(classCount.items(), key = operator.itemgetter(1),reverse = True) return sortedClassCount[0][0] # 測試分類錯誤,錯誤分辨個數 def datingClassTest(): hoRatio = 0.08 # 隨機挖去 10% 的數據作為測試集 datingDataMat,datingLabels = file2matrix("datingTestSet2.txt") # 加載數據文件 normMat, ranges, minVals = autoNorm(datingDataMat) m = normMat.shape[0] numTestVecs = int(m*hoRatio) # 隨機挖去的行數 errorCount = 0.0 for i in range(numTestVecs): # 前numTestVecs條作為測試集(一個一個測試),后面的數據作為訓練樣本,訓練樣本的標簽,3個近鄰 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 number of errr is: %d" % int(errorCount)) print("The total error rate is: %f" % (errorCount / float(numTestVecs))) # 實踐算法 def validateTest(): 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?")) datingDataMat, datingLabels = file2matrix("datingTestSet2.txt") normMat, ranges, minVals = autoNorm(datingDataMat) inArr = array([ffMiles, percentTats, iceCream]) # print(str(inArr),str(minVals),str(ranges),normMat,datingLabels) # 為什么傳入的值需要進行 如下操作呢 (inArry - minVals)/range # 因為我們之前傳入的值都是進行過歸一化的 # 但是如果直接傳入值會過大 所以我們在前面的歸一化操作中將兩個參數傳過來就是為了這里使用 # 但是這里傳入的測試參數也是一個1x3的舉證 所有 minVals 和 ranges 不需要進行 tile化 classifierResult = classify0((inArr - minVals) / ranges, normMat, datingLabels, 3) print("You will probably like this person: ", resultList[classifierResult - 1]) # 主函數 if __name__ == "__main__": # 實踐算法 validateTest() # 測試分類錯誤:判斷程序的可行性 print("這是輸出判斷算法優越性的結果:") datingClassTest()
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/45220.html
摘要:簡介源于數據挖掘的一個作業,這里用來實現一下這個機器學習中最簡單的算法之一算法最近鄰分類法。其實這些標簽就對應于機器學習中的特征這一重要概念,而訓練我們識別的過程就對應于泛化這一概念。 1. 簡介 源于數據挖掘的一個作業, 這里用Node.js來實現一下這個機器學習中最簡單的算法之一k-nearest-neighbor算法(k最近鄰分類法)。 k-nearest-neighbor-cl...
摘要:一定義二個人理解其實簡單理解就是通過計算新加入點與附近個點的距離,然后尋找到距離最近的個點,進行占比統計,找到個點中數量占比最高的,那么新加入的樣本,它的就是頻數最高的三實踐語言歐拉距離樣本繪圖計算距離歐拉距離求出和相 一、定義 url:https://en.wikipedia.org/wiki... In pattern recognition, the k-nearest neig...
摘要:在本教程中,我們將學習如何使用八叉樹在點云數據中進行空間分區和鄰居搜索。因此,搜索點和搜索結果之間的距離取決于八叉樹的分辨率參數。此外,先進的內存管理減少了八叉樹構建過程中的內存分配和釋放操作。總結八叉樹實現是空間分區和搜索操作的強大工具。 本教程代碼開源:GitHub 歡迎st...
k近鄰(k-Nearest Neighbor,kNN)算法是經典的帶監督的分類算法,核心思想是如果一個樣本在特征空間中的k個最相鄰的樣本中的大多數屬于某一個類別,則針對該樣本的劃分結果也屬于這個類別。 1. 算法步驟 準備訓練數據和測試數據; 確定參數 k; 計算測試數據與各個訓練數據之間的距離,距離的遞增關系進行排序; 選取距離最小的 k 個點; 確定前 k 個點所在類別的出現頻率; 返回前 ...
閱讀 1893·2021-11-22 09:34
閱讀 3033·2021-09-28 09:35
閱讀 13436·2021-09-09 11:34
閱讀 3600·2019-08-29 16:25
閱讀 2829·2019-08-29 15:23
閱讀 2044·2019-08-28 17:55
閱讀 2434·2019-08-26 17:04
閱讀 3050·2019-08-26 12:21