1. 程式人生 > >機器學習實戰筆記-K近鄰演算法2(改進約會網站的配對效果)

機器學習實戰筆記-K近鄰演算法2(改進約會網站的配對效果)

案例二.:使用K-近鄰演算法改進約會網站的配對效果
案例分析:
海倫收集的資料集有三類特徵,分別是每年獲得的飛行常客里程數、玩視訊遊戲所耗時間百分比、 每週消費的冰淇淋公升數。我們需要將新資料的每個新資料的每個特徵與樣本集中資料對應的特徵進行比較,然後演算法提取樣本集中特徵最相似資料(最近鄰)的分類標籤。一般來說,我們只選擇樣本資料集中前k個最相似的資料,這就是k-近鄰演算法中k的出處,通常k是不大於20的整數。最後,選擇k個最相似資料中出現次數最多的分類,作為新資料的分類。

流程:在約會網站上使用K近鄰演算法
(1)收集資料:提供文字檔案。
(2)_準備資料: 使用Python解析文字檔案。
(3 )分析資料:使用Matplotlib畫二維擴散圖。
(4)訓練演算法:此步驟不適用於k近鄰演算法。
(5)測試演算法:使用海倫提供的部分資料作為測試樣本。測試樣本和非測試樣本的區別在於:測試樣本是已經完成分類的資料,如果預測分類與實際類別不同,則標記為一個錯誤。
(6)使用演算法:產生簡單的命令列程式,然後海倫可以輸入一些特徵資料以判斷對方是否為自己喜歡的型別。

資料集樣式
這裡寫圖片描述
從檔案讀取資料集轉換為二維陣列和類別向量
程式碼:

def file2matrix(filename): 
fr = open(filename)
arrayOLines = fr.readlines()
numberOfLines = len(arrayOLines)
returnMat = zeros((numberOfLines,3))
classLabelVector = []
index = 0
for line in arrayOLines:
line = line.strip()
listFromLine = line.split('\t')
returnMat[index,:] = listFromLine[0:3]
classLabelVector.append(int(listFromLine[-1]))
index+=1
return returnMat,classLabelVector

讀取資料檔案後顯示截圖:
這裡寫圖片描述

繪製散點圖,散點圖使用datingMat矩陣的第二、第三列資料,分別表示特徵
值 “玩視訊遊戲所耗時間百分比”和 “每週所消費的冰淇淋公升數” 。
程式碼:

from numpy import * 
from imp import reload
import matplotlib
import matplotlib.pyplot as plt
import kNN
datingMat,labelVector = kNN.file2matrix('datingTestSet2.txt')
plt.figure(figsize = (8,5), dpi = 80)
axes = plt.subplot(111)
type1_x = []
type1_y = []
type2_x = []
type2_y = []
type3_x = []
type3_y = []
for i in range(len(labelVector)):
#不喜歡
if labelVector[i] == 1:
type1_x.append(datingMat[i][0])
type1_y.append(datingMat[i][1])
#魅力一般
elif labelVector[i] == 2:
type2_x.append(datingMat[i][0])
type2_y.append(datingMat[i][1])
#魅力超群
else:
type3_x.append(datingMat[i][0])
type3_y.append(datingMat[i][1])
typeFirst = axes.scatter(type1_x, type1_y, s=20, c='red')
typeSecond = axes.scatter(type2_x, type2_y, s=40, c='green')
typeThird = axes.scatter(type3_x, type3_y, s=60, c='blue')
plt.xlabel(u'每年獲取的飛行里程數')
plt.ylabel(u'玩視訊遊戲所消耗的事件百分比')
axes.legend((typeFirst,typeSecond,typeThird),(u'不喜歡',u'魅力一般',u'魅力超群'),loc = 2)
plt.show()

顯示圖片:
這裡寫圖片描述

歸一化資料
如果我們仔細觀察資料集會發現,每年獲取的飛行常客里程數對於計算結果的影響將遠遠大於資料集中其他兩個特徵— 玩視訊遊戲的和每週消費冰洪淋公升數— 的影響。而產生這種現象的唯一原因,僅僅是因為飛行常客里程數遠大於其他特徵值。但海倫認為這三種特徵是同等重要的,因此作為三個等權重的特徵之一,飛行常客里程數並不應該如此嚴重地影響到計算結果。
在處理這種不同取值範圍的特徵值時,我們通常採用的方法是將數值歸一化,如將取值範圍處理為0到1或者-1到1之間。下面的公式可以將任意取值範圍的特徵值轉化為0到1區間內的值:
n e w V a l u e = { o l d V a l u e - m i n ) / (max-min)
其中min和max分別是資料集中的最小特徵值和最大特徵值。雖然改變數值取值範圍增加了
分類器的複雜度,但為了得到準確結果,我們必須這樣做。我們需要在檔案kNN.py中增加一個新函式autoNorm() , 該函式可以自動將數字特徵值轉化為0到1的區間。
程式碼:

 def autoNorm(dataSet): 
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))
return normDataSet,ranges,minVals

歸一化效果截圖:
這裡寫圖片描述

測試演算法
機器學習演算法一個很重要的工作就是評估演算法的正確率,通常我們只提供已有資料的90%作為訓練樣本來訓練分類器 ,而使用其餘的10%資料去測試分類器,檢測分類器的正確率。需要注意的是,10%的測試資料應該是隨機選擇的,由於海倫提供的資料並沒有按照特定目的來排序,所以我們可以隨意選擇10%資料而不影響其隨機性。
對於分類器來說,錯誤率就是分類器給出錯誤結果的次數除以測試資料的總數,完美分類器的錯誤率為0 ,而錯誤率為1.0的分類器不會給出任何正確的分類結果。程式碼裡我們定義一個計數器變數,每次分類器錯誤地分類資料,計數器就加1, 程式執行完成之後計數器的結果除以資料點總數即是錯誤率。
程式碼:

def datingClassTest(): 
hoRatio = 0.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):
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)))

測試截圖:
這裡寫圖片描述

約會網站預測函式

def classifyPerson():
    resultList = ['not at all','in small doses','in large doses']
    percentTats = float(input("percentage of time spent playing video games?"))
    ffMiles = float(input("frequent flier 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])
    classifierResult = classify0((inArr-minvals)/ranges, normMat,datingLabels,3)
    print("you will probably like this person: %s" % resultList[classifierResult-1])

效果截圖:
這裡寫圖片描述