第二篇:基於K-近鄰分類算法的約會對象智能匹配系統


前言

       假如你想到某個在線約會網站尋找約會對象,那么你很可能將該約會網站的所有用戶歸為三類:

       1. 不喜歡的

       2. 有點魅力的

       3. 很有魅力的

       你如何決定某個用戶屬於上述的哪一類呢?想必你會分析用戶的信息來得到結論,比如該用戶 "每年獲得的飛行常客里程數","玩網游所消耗的時間比","每年消耗的冰淇淋公升數"。

       使用機器學習的K-近鄰算法,可以幫助你在獲取到用戶的這三個信息后(或者更多信息 方法同理),自動幫助你對該用戶進行分類,多方便呀!

       本文將告訴你如何具體實現這樣一個自動分類程序。

第一步:收集並准備數據

       首先,請搜集一些約會數據 - 盡可能多。

       然后將自行搜集到的數據存放到一個txt文件中,例如,可以將每個樣本數據各為一行

       前言中提到的那三個分析數據(特征)以及分析結果(整數表示)各為一列,如下所示:

       

       再編寫函數將這些數據從文件中取出並存放到數據結構中:

 1 # 導入numpy數學運算庫
 2 import numpy
 3 
 4 # ==============================================
 5 # 輸入:
 6 #        訓練集文件名(含路徑)
 7 # 輸出:
 8 #        特征矩陣和標簽向量
 9 # ==============================================
10 def file2matrix(filename):
11     '獲取訓練集數據'
12     
13     # 打開訓練集文件
14     fr = open(filename)
15     # 獲取文件行數
16     numberOfLines = len(fr.readlines())
17     # 文件指針歸0
18     fr.seek(0)
19     # 初始化特征矩陣
20     returnMat = numpy.zeros((numberOfLines,3))
21     # 初始化標簽向量
22     classLabelVector = []
23     # 特征矩陣的行號 也即樣本序號
24     index = 0
25     
26     for line in fr:     # 遍歷訓練集文件中的所有行
27         # 去掉行頭行尾的換行符,制表符。
28         line = line.strip()
29         # 以制表符分割行
30         listFromLine = line.split('\t')
31         # 將該行特征部分數據存入特征矩陣
32         returnMat[index,:] = listFromLine[0:3]
33         # 將該行標簽部分數據存入標簽矩陣
34         classLabelVector.append(int(listFromLine[-1]))
35         # 樣本序號+1
36         index += 1
37         
38     return returnMat,classLabelVector

       獲取到數據后就可以print查看獲取到的數據內容了,如下:

      

       很顯然,這樣的顯示非常的不友好,可采用Python的Matplotlib庫來圖像化地展示獲取到的數據

       如果你是在Ubuntu下使用Eclipse插件編譯PyDev的話,安裝Matplotlib是很坑的。

       在獲取到安裝包后,還得在插件設置那里添加新的庫路徑,因為Matplotlib不會自動安裝到Python2.7的庫目錄下,這和NumPy不同。

       下面這個才是正確的庫路徑:

      

       然后就可以編寫以下代碼進行數據的分析了:

1 # 新建一個圖對象
2     fig = plt.figure()
3     # 設置1行1列個圖區域,並選擇其中的第1個區域展示數據。
4     ax = fig.add_subplot(111)
5     # 以訓練集第一列(玩網游所消耗的時間比)為數據分析圖的行,第二列(每年消費的冰淇淋公升數)為數據分析圖的列。
6     ax.scatter(datingDataMat[:,1], datingDataMat[:,2])
7     # 展示數據分析圖
8     plt.show()

       另外在代碼頂部記得包含所需的matplotlib庫:

1 # 導入Matplotlib庫
2 import matplotlib.pyplot as plt
3 import matplotlib

       運行完后,輸出數據分析圖如下:

      

       這里發現一個問題,上面的數據分析圖並沒有顯示分類的結果。

       進一步優化數據分析圖顯示部分代碼:

 1 # 新建一個圖對象
 2     fig = plt.figure()
 3     # 設置1行1列個圖區域,並選擇其中的第1個區域展示數據。
 4     ax = fig.add_subplot(111)
 5     # 以訓練集第一列(玩網游所消耗的時間比)為數據分析圖的行,第二列(每周消費的冰淇淋公升數)為數據分析圖的列。
 6     ax.scatter(datingDataMat[:,1], datingDataMat[:,2], 15.0*numpy.array(datingLabels), 15.0*numpy.array(datingLabels))
 7     # 坐標軸定界
 8     ax.axis([-2,25,-0.2,2.0])
 9     # 坐標軸說明 (matplotlib配置中文顯示有點麻煩 這里直接用英文的好了)
10     plt.xlabel('Percentage of Time Spent Playing Online Games')
11     plt.ylabel('Liters of Ice Cream Consumed Per Week')
12     # 展示數據分析圖
13     plt.show()

       得到如下數據分析圖:

  

       也可以用同樣方法得到 "每年獲得的飛行常客里程數" 和 "玩網游所消耗的時間比" 為軸的圖:

  

第三步:數據歸一化

       想必你會發現,我們分析的這三個特征,在距離計算公式中所占的權重是不同的:飛機歷程肯定要比吃冰淇淋的公升數大多了。

       因此,需要將它們轉為同樣的一個數量區間,再進行距離計算。 --- 這個步驟就叫做數據歸一化

       可以使用如下公式對數據進行歸一化:

              newValue = (oldValue - min) / (max - min)

       即用舊的特征值去減它取到的最小的值,然后再除以它的取值范圍。

       很顯然,所有得到的新值取值均在 0 -1 。

       這部分代碼如下:

 1 # ==============================================
 2 # 輸入:
 3 #        訓練集
 4 # 輸出:
 5 #        歸一化后的訓練集
 6 # ==============================================
 7 def autoNorm(dataSet):
 8     '數據歸一化'
 9     
10     # 獲得每列最小值
11     minVals = dataSet.min(0)
12     # 獲得每列最大值
13     maxVals = dataSet.max(0)
14     # 獲得每列特征的取值范圍
15     ranges = maxVals - minVals
16     # 構建初始矩陣(模型同dataSet)
17     normDataSet = numpy.zeros(numpy.shape(dataSet))
18     
19     # 數據歸一化矩陣運算
20     m = dataSet.shape[0]
21     normDataSet = dataSet - numpy.tile(minVals, (m,1))
22     # 注意/是特征值相除法。/在別的函數庫也許是矩陣除法的意思。
23     normDataSet = normDataSet/numpy.tile(ranges, (m,1))
24     
25     return normDataSet

第四步:測試算法

       測試的策略是隨機取10%的數據進行分析,再判斷分類准確率如何。

       這部分代碼如下:

 1 # ================================================
 2 # 輸入:
 3 #
 4 # 輸出:
 5 #        對指定訓練集文件進行K近鄰算法測試並打印測試結果
 6 # ================================================
 7 def datingClassTest():
 8     '分類算法測試'
 9     
10     # 設置要測試的數據比重
11     hoRatio = 0.10
12     # 獲取訓練集
13     datingDataMat,datingLabels = file2matrix('datingTestSet.txt')
14     # 數據歸一化
15     normMat, ranges, minVals = autoNorm(datingDataMat)
16     # 計算實際要測試的樣本數
17     m = normMat.shape[0]
18     numTestVecs = int(m*hoRatio)
19     # 存放錯誤數
20     errorCount = 0.0
21     
22     # 對測試集樣本一一進行分類並分析打印結果
23     print "錯誤的分類結果如下:"
24     for i in range(numTestVecs):
25         classifierResult = classify0(normMat[i,:],normMat[numTestVecs:m,:],datingLabels[numTestVecs:m],3)
26         if (classifierResult != datingLabels[i]): 
27             print "分類結果: %d, 實際結果: %d" % (classifierResult, datingLabels[i])
28             errorCount += 1.0
29     print "總錯誤率: %.2f"  % (errorCount/float(numTestVecs))
30     print "總錯誤數:%.2f" % errorCount

       其中,classify0 函數在文章K-近鄰分類算法原理分析與代碼實現中有具體實現。

       打印出如下結果:

      

       錯誤率為5%左右,這是應該算是比較理想的狀況了吧。

第五步:使用算法構建完整可用系統

       下面,可以在這個訓練集和分類器之上構建一個完整的可用系統了。

       系統功能很簡單:用戶輸入要判斷對象三個特征 - "每年獲得的飛行常客里程數","玩網游所消耗的時間比","每年消耗的冰淇淋公升數"。

       PS:在真實系統中,這部分輸入可不由用戶來輸入,而從網站直接下載數據。

       程序幫你判斷你是不喜歡還是有點喜歡,抑或是很喜歡。

       這部分代碼如下:

 1 # ===========================================================
 2 # 輸入:
 3 #
 4 # 輸出:
 5 #        對用戶指定的對象以指定的訓練集文件進行K近鄰分類並打印結果信息
 6 # ===========================================================    
 7 def classifyPerson():
 8     '約會對象分析系統'
 9     
10     # 分析結果集合
11     resultList = ['不喜歡', '有點喜歡', '很喜歡']
12     
13     # 獲取用戶輸入的目標分析對象的特征值
14     percentTats = float(raw_input("玩網游所消耗的時間比:"))
15     ffMiles = float(raw_input("每年獲得的飛行常客里程數:"))
16     iceCream = float(raw_input("每年消費的冰淇淋公升數:"))
17     
18     # 獲取訓練集
19     datingDataMat, datingLabels = file2matrix('datingTestSet.txt')
20     # 數據歸一化
21     normMat, ranges, minVals = autoNorm(datingDataMat)
22     # 獲取分類結果
23     inArr = numpy.array([ffMiles, percentTats, iceCream])
24     classifierResult = classify0((inArr-minVals)/ranges, normMat, datingLabels, 3)
25     
26     print "分析結果:", resultList[classifierResult-1]

       運行結果:

      

       至此,該系統編寫完畢。

小結

1. KNN算法其實並沒有一個實際的 "訓練" 過程。取得了數據就當作是訓練過了的。在下下篇文章將講解決策樹,它就有詳細的訓練,或者說知識學習的過程。

2. 可采用從網站自動下載數據的方式,讓這個系統的決策更為科學,再加上良好的界面,就能投入實際使用了。

3. 下篇文章將講解KNN算法一個更為高級的應用 - 手寫識別系統

4. 這個程序也看出,處理文本/字符串方面,Python比C++好用多了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM