kNN算法實例之約會網(wǎng)站

好久沒更新了客蹋,最近在忙著寫論文敞咧,昨天我的新書機器實戰(zhàn)到了,于是就連夜學(xué)了第一個實例偶翅。

1他去、k-近鄰法簡介

k近鄰法(k-nearest neighbor, k-NN)是1967年由Cover T和Hart P提出的一種基本分類與回歸方法。它的工作原理是:存在一個樣本數(shù)據(jù)集合倒堕,也稱作為訓(xùn)練樣本集,并且樣本集中每個數(shù)據(jù)都存在標(biāo)簽爆价,即我們知道樣本集中每一個數(shù)據(jù)與所屬分類的對應(yīng)關(guān)系垦巴。輸入沒有標(biāo)簽的新數(shù)據(jù)后媳搪,將新的數(shù)據(jù)的每個特征與樣本集中數(shù)據(jù)對應(yīng)的特征進(jìn)行比較,然后算法提取樣本最相似數(shù)據(jù)(最近鄰)的分類標(biāo)簽骤宣。一般來說秦爆,我們只選擇樣本數(shù)據(jù)集中前k個最相似的數(shù)據(jù),這就是k-近鄰算法中k的出處憔披,通常k是不大于20的整數(shù)等限。最后,選擇k個最相似數(shù)據(jù)中出現(xiàn)次數(shù)最多的分類芬膝,作為新數(shù)據(jù)的分類望门。

舉個簡單的例子,我們可以使用k-近鄰算法分類一個電影是愛情片還是動作片锰霜。

表1.1 每部電影的打斗鏡頭數(shù)筹误、接吻鏡頭數(shù)以及電影類型

表1.1 就是我們已有的數(shù)據(jù)集合,也就是訓(xùn)練樣本集癣缅。這個數(shù)據(jù)集有兩個特征厨剪,即打斗鏡頭數(shù)和接吻鏡頭數(shù)。除此之外友存,我們也知道每個電影的所屬類型祷膳,即分類標(biāo)簽。用肉眼粗略地觀察屡立,接吻鏡頭多的直晨,是愛情片。打斗鏡頭多的侠驯,是動作片抡秆。以我們多年的看片經(jīng)驗,這個分類還算合理吟策。如果現(xiàn)在給我一部電影儒士,你告訴我這個電影打斗鏡頭數(shù)和接吻鏡頭數(shù)。不告訴我這個電影類型檩坚,我可以根據(jù)你給我的信息進(jìn)行判斷着撩,這個電影是屬于愛情片還是動作片。而k-近鄰算法也可以像我們?nèi)艘粯幼龅竭@一點匾委,不同的地方在于拖叙,我們的經(jīng)驗更"牛逼",而k-近鄰算法是靠已有的數(shù)據(jù)赂乐。比如薯鳍,你告訴我這個電影打斗鏡頭數(shù)為2,接吻鏡頭數(shù)為102挨措,我的經(jīng)驗會告訴你這個是愛情片挖滤,k-近鄰算法也會告訴你這個是愛情片崩溪。你又告訴我另一個電影打斗鏡頭數(shù)為49,接吻鏡頭數(shù)為51斩松,我"邪惡"的經(jīng)驗可能會告訴你伶唯,這有可能是個"愛情動作片",畫面太美惧盹,我不敢想象乳幸。 (如果說,你不知道"愛情動作片"是什么钧椰?請評論留言與我聯(lián)系粹断,我需要你這樣像我一樣純潔的朋友。) 但是k-近鄰算法不會告訴你這些演侯,因為在它的眼里姿染,電影類型只有愛情片和動作片,它會提取樣本集中特征最相似數(shù)據(jù)(最鄰近)的分類標(biāo)簽秒际,得到的結(jié)果可能是愛情片悬赏,也可能是動作片,但絕不會是"愛情動作片"娄徊。當(dāng)然闽颇,這些取決于數(shù)據(jù)集的大小以及最近鄰的判斷標(biāo)準(zhǔn)等因素。

2寄锐、距離度量

我們已經(jīng)知道k-近鄰算法根據(jù)特征比較兵多,然后提取樣本集中特征最相似數(shù)據(jù)(最鄰近)的分類標(biāo)簽。那么橄仆,如何進(jìn)行比較呢剩膘?比如,我們還是以表1.1為例盆顾,怎么判斷紅色圓點標(biāo)記的電影所屬的類別呢怠褐? 如下圖所示。

我們可以從散點圖大致推斷您宪,這個紅色圓點標(biāo)記的電影可能屬于動作片奈懒,因為距離已知的那兩個動作片的圓點更近。k-近鄰算法用什么方法進(jìn)行判斷呢宪巨?沒錯磷杏,就是距離度量。這個電影分類的例子有2個特征捏卓,也就是在2維實數(shù)向量空間极祸,可以使用我們高中學(xué)過的兩點距離公式計算距離,如圖1.2所示。

通過計算遥金,我們可以得到如下結(jié)果:

  • (101,20)->動作片(108,5)的距離約為16.55
  • (101,20)->動作片(115,8)的距離約為18.44
  • (101,20)->愛情片(5,89)的距離約為118.22
  • (101,20)->愛情片(1,101)的距離約為128.69

通過計算可知峦椰,紅色圓點標(biāo)記的電影到動作片 (108,5)的距離最近,為16.55汰规。如果算法直接根據(jù)這個結(jié)果,判斷該紅色圓點標(biāo)記的電影為動作片物邑,這個算法就是最近鄰算法溜哮,而非k-近鄰算法坎匿。那么k-近鄰算法是什么呢驼唱?k-近鄰算法步驟如下:

  1. 計算已知類別數(shù)據(jù)集中的點與當(dāng)前點之間的距離信卡;
  2. 按照距離遞增次序排序休吠;
  3. 選取與當(dāng)前點距離最小的k個點潮梯;
  4. 確定前k個點所在類別的出現(xiàn)頻率碗淌;
  5. 返回前k個點所出現(xiàn)頻率最高的類別作為當(dāng)前點的預(yù)測分類喷橙。

比如扼劈,現(xiàn)在我這個k值取3锣笨,那么在電影例子中蝌矛,按距離依次排序的三個點分別是動作片(108,5)、動作片(115,8)错英、愛情片(5,89)入撒。在這三個點中,動作片出現(xiàn)的頻率為三分之二椭岩,愛情片出現(xiàn)的頻率為三分之一茅逮,所以該紅色圓點標(biāo)記的電影為動作片。這個判別過程就是k-近鄰算法判哥。

代碼實現(xiàn)步驟

準(zhǔn)備數(shù)據(jù):從文本文件中解析數(shù)據(jù)
分析數(shù)據(jù):使用Matplotlib創(chuàng)建散點圖
準(zhǔn)備數(shù)據(jù):歸一化數(shù)值
測試算法:作為完整程序驗證分類器
使用算法:構(gòu)建完整可用系統(tǒng)

from numpy import *
from matplotlib.font_manager import FontProperties
import matplotlib.lines as mlines
import  matplotlib.pyplot as plt
import operator
def createDataSet():
    group = array([[1.0,1.1],[1.0,1.0],[0,0],[0,0.1]])
    labels=["A","A","B","B"]
    return group,labels
''''
函數(shù)說明:knn算法献雅,分類器,inx是輸入的向量塌计,dataset是數(shù)據(jù)集挺身,labels是標(biāo)簽
'''''
def classify0(inX, dataSet, labels, k):
    dataSetSize = dataSet.shape[0]
    diffMat = tile(inX, (dataSetSize,1))-dataSet
    sqDiffMat = diffMat**2
    sqDistance = sqDiffMat.sum(axis=1)
    distances = sqDistance**0.5
    sortedDistIndicies = 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]
'''
函數(shù)說明:打開解析文件
filename:文件名
returnmat:特征矩陣
classlabelvector;標(biāo)簽列表
'''
def file2matrix(filename):
    fr = open(filename)
    arrayOLines = fr.readlines()
    numberOfLines = len(arrayOLines)
    returnMat = zeros((numberOfLines,3))#創(chuàng)建一個行*3的0矩陣
    classLabelVector = []
    index = 0
    for line in arrayOLines:
        line = line.strip() #去除每一行的空格或換行符
        listFromLine = line.split("\t") #按照回車分割每一行為每一個數(shù)組
        returnMat[index, :] = listFromLine[0:3]#把每一行數(shù)據(jù)添加到之前創(chuàng)建的0矩陣之中
        #print(listFromLine)
        #classLabelVector.append(str(listFromLine[-1]))
        """"
        if listFromLine[-1] == "didntLike":
            classLabelVector.append(1)
        elif listFromLine[-1] == "smallDoses":
            classLabelVector.append(2)
        elif listFromLine[-1] == "largeDoses":
            classLabelVector.append(3)
            """
        classLabelVector.append(int(listFromLine[-1]))
        index += 1
    return returnMat, classLabelVector
"""
函數(shù)說明:分析數(shù)據(jù),數(shù)據(jù)可視化

"""
def showdatas(datingDataMat,datingLabels):
    # 設(shè)置漢字格式
    font = FontProperties(fname=r"c:\windows\fonts\simsun.ttc", size=14)
    # 將fig畫布分隔成1行1列,不共享x軸和y軸,fig畫布的大小為(13,8)
    # 當(dāng)nrow=2,nclos=2時,代表fig畫布被分為四個區(qū)域,axs[0][0]表示第一行第一個區(qū)域
    fig, axs = plt.subplots(nrows=2, ncols=2, sharex=False, sharey=False, figsize=(13, 8))

    numberOfLabels = len(datingLabels)
    LabelsColors = []
    for i in datingLabels:
        if i == 1:
            LabelsColors.append('black')
        if i == 2:
            LabelsColors.append('orange')
        if i == 3:
            LabelsColors.append('red')
    # 畫出散點圖,以datingDataMat矩陣的第一(飛行扯峄模客例程)瞒渠、第二列(玩游戲)數(shù)據(jù)畫散點數(shù)據(jù),散點大小為15,透明度為0.5
    axs[0][0].scatter(x=datingDataMat[:, 0], y=datingDataMat[:, 1], color=LabelsColors, s=15, alpha=.5)
    # 設(shè)置標(biāo)題,x軸label,y軸label
    axs0_title_text = axs[0][0].set_title(u'每年獲得的飛行常客里程數(shù)與玩視頻游戲所消耗時間占比', FontProperties=font)
    axs0_xlabel_text = axs[0][0].set_xlabel(u'每年獲得的飛行臣级螅客里程數(shù)', FontProperties=font)
    axs0_ylabel_text = axs[0][0].set_ylabel(u'玩視頻游戲所消耗時間占', FontProperties=font)
    plt.setp(axs0_title_text, size=9, weight='bold', color='red')
    plt.setp(axs0_xlabel_text, size=7, weight='bold', color='black')
    plt.setp(axs0_ylabel_text, size=7, weight='bold', color='black')

    # 畫出散點圖,以datingDataMat矩陣的第一(飛行澄榫粒客例程)、第三列(冰激凌)數(shù)據(jù)畫散點數(shù)據(jù),散點大小為15,透明度為0.5
    axs[0][1].scatter(x=datingDataMat[:, 0], y=datingDataMat[:, 2], color=LabelsColors, s=15, alpha=.5)
    # 設(shè)置標(biāo)題,x軸label,y軸label
    axs1_title_text = axs[0][1].set_title(u'每年獲得的飛行辰宋牵客里程數(shù)與每周消費的冰激淋公升數(shù)', FontProperties=font)
    axs1_xlabel_text = axs[0][1].set_xlabel(u'每年獲得的飛行城瞎浚客里程數(shù)', FontProperties=font)
    axs1_ylabel_text = axs[0][1].set_ylabel(u'每周消費的冰激淋公升數(shù)', FontProperties=font)
    plt.setp(axs1_title_text, size=9, weight='bold', color='red')
    plt.setp(axs1_xlabel_text, size=7, weight='bold', color='black')
    plt.setp(axs1_ylabel_text, size=7, weight='bold', color='black')

    # 畫出散點圖,以datingDataMat矩陣的第二(玩游戲)、第三列(冰激凌)數(shù)據(jù)畫散點數(shù)據(jù),散點大小為15,透明度為0.5
    axs[1][0].scatter(x=datingDataMat[:, 1], y=datingDataMat[:, 2], color=LabelsColors, s=15, alpha=.5)
    # 設(shè)置標(biāo)題,x軸label,y軸label
    axs2_title_text = axs[1][0].set_title(u'玩視頻游戲所消耗時間占比與每周消費的冰激淋公升數(shù)', FontProperties=font)
    axs2_xlabel_text = axs[1][0].set_xlabel(u'玩視頻游戲所消耗時間占比', FontProperties=font)
    axs2_ylabel_text = axs[1][0].set_ylabel(u'每周消費的冰激淋公升數(shù)', FontProperties=font)
    plt.setp(axs2_title_text, size=9, weight='bold', color='red')
    plt.setp(axs2_xlabel_text, size=7, weight='bold', color='black')
    plt.setp(axs2_ylabel_text, size=7, weight='bold', color='black')
    # 設(shè)置圖例
    didntLike = mlines.Line2D([], [], color='black', marker='.',
                              markersize=6, label='didntLike')
    smallDoses = mlines.Line2D([], [], color='orange', marker='.',
                               markersize=6, label='smallDoses')
    largeDoses = mlines.Line2D([], [], color='red', marker='.',
                               markersize=6, label='largeDoses')
    # 添加圖例
    axs[0][0].legend(handles=[didntLike, smallDoses, largeDoses])
    axs[0][1].legend(handles=[didntLike, smallDoses, largeDoses])
    axs[1][0].legend(handles=[didntLike, smallDoses, largeDoses])
    # 顯示圖片
    plt.show()
    """""
    函數(shù)說明:dataSet是數(shù)據(jù)集,將數(shù)據(jù)集轉(zhuǎn)化為0-1之間的數(shù)椰棘,公式為 
    newvalue = (oldvalue - min)/(max - min)
    """
def autoNorm(dataSet):
    #min(0)是取當(dāng)前列最小值纺棺,min(1)是當(dāng)前列最大值,max同理
    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
def datingClassTest():
    hoRatio = 0.10
    datingDataMat, datingLabels = file2matrix("datingTestSet.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( "分類器結(jié)果:%d, 真實結(jié)果:%d" %(classifierResult,datingLabels[i]))
        if(classifierResult!=datingLabels[i]) :
            errorCount += 1.0
    print( "分類器的錯誤率是%f" %(errorCount/float(numTestVecs)))
def classifyPerson():
    resultList = ["討厭", "有點喜歡", "非常喜歡"]
    percentTats = float(input("玩視頻游戲所占百分比:"))
    ffMiles = float(input("每年飛行里程數(shù):"))
    iceCream = float(input("每周吃的冰淇淋公升數(shù):"))
    datingDataMat, datingLabels = file2matrix("datingTestSet2.txt")
    normMat, ranges, minVals = autoNorm(datingDataMat)
    inArr = array([ffMiles, percentTats, iceCream])
    norminArr = (inArr - minVals) / ranges
    classifierResult = classify0(norminArr, normMat, datingLabels, 3)
    print("這個人你應(yīng)該%s"%(resultList[classifierResult-1]))
if __name__ == '__main__':
    classifyPerson()
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末邪狞,一起剝皮案震驚了整個濱河市祷蝌,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌帆卓,老刑警劉巖巨朦,帶你破解...
    沈念sama閱讀 207,248評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異剑令,居然都是意外死亡糊啡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評論 2 381
  • 文/潘曉璐 我一進(jìn)店門吁津,熙熙樓的掌柜王于貴愁眉苦臉地迎上來棚蓄,“玉大人,你說我怎么就攤上這事碍脏∷笠溃” “怎么了?”我有些...
    開封第一講書人閱讀 153,443評論 0 344
  • 文/不壞的土叔 我叫張陵潮酒,是天一觀的道長睛挚。 經(jīng)常有香客問我,道長急黎,這世上最難降的妖魔是什么扎狱? 我笑而不...
    開封第一講書人閱讀 55,475評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮勃教,結(jié)果婚禮上淤击,老公的妹妹穿的比我還像新娘。我一直安慰自己故源,他們只是感情好污抬,可當(dāng)我...
    茶點故事閱讀 64,458評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著绳军,像睡著了一般印机。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上门驾,一...
    開封第一講書人閱讀 49,185評論 1 284
  • 那天射赛,我揣著相機與錄音,去河邊找鬼奶是。 笑死楣责,一個胖子當(dāng)著我的面吹牛竣灌,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播秆麸,決...
    沈念sama閱讀 38,451評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼初嘹,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了沮趣?” 一聲冷哼從身側(cè)響起屯烦,我...
    開封第一講書人閱讀 37,112評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎房铭,沒想到半個月后漫贞,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,609評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡育叁,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,083評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了芍殖。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片豪嗽。...
    茶點故事閱讀 38,163評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖豌骏,靈堂內(nèi)的尸體忽然破棺而出龟梦,到底是詐尸還是另有隱情,我是刑警寧澤窃躲,帶...
    沈念sama閱讀 33,803評論 4 323
  • 正文 年R本政府宣布计贰,位于F島的核電站,受9級特大地震影響蒂窒,放射性物質(zhì)發(fā)生泄漏躁倒。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,357評論 3 307
  • 文/蒙蒙 一洒琢、第九天 我趴在偏房一處隱蔽的房頂上張望秧秉。 院中可真熱鬧,春花似錦衰抑、人聲如沸象迎。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽砾淌。三九已至,卻和暖如春谭网,著一層夾襖步出監(jiān)牢的瞬間汪厨,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評論 1 261
  • 我被黑心中介騙來泰國打工蜻底, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留骄崩,地道東北人聘鳞。 一個月前我還...
    沈念sama閱讀 45,636評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像要拂,于是被迫代替她去往敵國和親抠璃。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,925評論 2 344

推薦閱讀更多精彩內(nèi)容