《機器學(xué)習(xí)實戰(zhàn)》第二章 k-近鄰算法

算法理解:
K-近鄰算法測量待分類樣本的特征值與已經(jīng)分好類的樣本對應(yīng)的特征值之間的距離,最鄰近的一個或者幾個訓(xùn)練樣本的類別決定了待分類樣本所屬的類別戴而。

工作原理:
存在一個樣本數(shù)據(jù)集合(訓(xùn)練樣本集),并且每個訓(xùn)練樣本都有已知對應(yīng)的所屬分類(標(biāo)簽)捐康。輸入待分類的新樣本后掠剑,將新樣本的每個特征與訓(xùn)練樣本集中數(shù)據(jù)對應(yīng)的特征進(jìn)行比較。然后算法提取樣本集中特征最相似數(shù)據(jù)(最近鄰)的分類標(biāo)簽签钩。一般選前k個最相似的數(shù)據(jù)(一般k不大于20)。最后坏快,選擇k個最相似數(shù)據(jù)中出現(xiàn)次數(shù)最多的分類铅檩,作為新樣本的分類。

距離計算:
在KNN中莽鸿,通過計算待測樣本和訓(xùn)練樣本的特征值之間的差異(距離)作為對象之間的相似性指標(biāo)昧旨,一般使用歐氏距離或曼哈頓距離:

2.jpg

k-近鄰算法優(yōu)點是精度高拾给、對異常值不敏感、無數(shù)據(jù)輸入假定兔沃。缺點是計算復(fù)雜度高蒋得、空間復(fù)雜度高。適用數(shù)據(jù)范圍為數(shù)值型和標(biāo)稱型粘拾。

算法的描述:
1)計算測試數(shù)據(jù)與各個訓(xùn)練數(shù)據(jù)之間的距離窄锅;
2)按照距離的遞增關(guān)系進(jìn)行排序;
3)選取距離最小的K個點缰雇;
4)確定前K個點所在類別的出現(xiàn)頻率入偷;
5)返回前K個點中出現(xiàn)頻率最高的類別作為測試數(shù)據(jù)的預(yù)測分類。

# -*- coding: utf-8 -*-
""" k-Nearest Neighbors
Created on Mon Jan 23 12:56:00 2017
 
@author: xieyuxi
"""
from numpy import *
import operator 
import numpy as np
 
def createDataSet():
    group = array([[1.0, 1.1],[1.0,1.0],[0,0],[0,0.1]])
    lables = ['A','A','B','B']
    return group, lables
    print group, lables
 
 
createDataSet()
 
""" Classify the unknown vector input into defined classes
1械哟、calculate the distance between inX and the current point
2疏之、sort the distances in increasing order
3、take k items with lowest distances to inX
4暇咆、find the majority class among these items
5锋爪、return the majority class as our prediction for the class of inX 
 
Args:
     inX : the input vector to classify 
     dataSet : the full matrix of training examples
     labels : a vector of labels from the training examples
     k : the number of nearest neighbors to use in the voting
 
Attention:
    The labels vector should have as many elements in it 
    as there are rows in the dataSet matrix.
 
Returns:
     the majority class as our prediction for the class of inX
 
Raises:
    None
"""
def classify0(inX, dataSet, labels, k):
    # shape是numpy的數(shù)組的一個屬性,表示數(shù)組的維度
    # 比如一個 n x m的數(shù)組 Y(n 行 m列)爸业,Y.shape表示(n,m)
    # 所以 Y.shape[0]等于 n, Y.shape[1]等于 m
    # dataSet是4X2的數(shù)組其骄,所以此處dataSetSize等于4
    dataSetSize = dataSet.shape[0]      # 4
    # 1、先將一維矩陣擴展和測試矩陣的維度一樣
    # 2扯旷、將新生成的待測矩陣同測試矩陣進(jìn)行矩陣減法拯爽,再將矩陣的差的平方和開根得到距離
    # 3、將距離排序并返回
#==============================================================================
#     1钧忽、 tile(A, reps):reps的數(shù)字從后往前分別對應(yīng)A的第N個維度的重復(fù)次數(shù)毯炮。
#        如(A,2)表示A的列變量重復(fù)2遍,
#           tile(A,(2,3))表示A的行變量重復(fù)2遍耸黑,列變量重復(fù)3遍
#           tile(A,(2,2,3))表示A的第一個維度重復(fù)2遍桃煎,第二個維度重復(fù)2遍,
#           第三個維度重復(fù)3遍大刊。
#           Examples:
#     ----------------------------------------
#     # a = np.array([0, 1, 2])
#     # np.tile(a, 2)
#     array([0, 1, 2, 0, 1, 2])
#     # np.tile(a, (2, 3))
#     array([[0, 1, 2, 0, 1, 2, 0, 1, 2],
#            [0, 1, 2, 0, 1, 2, 0, 1, 2]])
#     ----------------------------------------
#     # b = np.array([[1, 2], [3, 4]])
#     # np.tile(b, 2)
#     array([[1, 2, 1, 2],
#            [3, 4, 3, 4]])
#     # np.tile(b, (2, 1))
#     array([[1, 2],
#            [3, 4],
#            [1, 2],
#            [3, 4]])
# 
#     2为迈、diffMat矩陣:先將輸入的值復(fù)制到和訓(xùn)練樣本同行,再做減法
# 以[0,0]為例:
# [0,0] to array([[0, 0]       [[0, 0]   [[1.0,1.1]           [[-1.  -1.1]
#                 [0, 0]  to   [0, 0] -  [1.0,1.0]    to       [-1.  -1. ]
#                 [0, 0]       [0, 0]    [0,0]                 [ 0.   0. ]
#                 [0, 0]])     [0, 0]]   [0,0.1]]              [ 0.  -0.1]]
#==============================================================================
 
    diffMat = tile(inX, (dataSetSize,1)) - dataSet
    # 其中矩陣的每一個元素都做平方操作
    #    [[ 1.    1.21]
    #     [ 1.    1.  ]
    #     [ 0.    0.  ]
    #     [ 0.    0.01]]
    sqDiffMat = diffMat**2
    # axis=0, 表示列缺菌。 axis=1, 表示行曲尸。
    # example:
    # c = np.array([[0, 2, 1], [3, 5, 6], [0, 1, 1]])
    # c.sum() = 19
    # c.sum(axis=0) =[3 8 8],即將矩陣的行向量想加后得到一個新的一維向量
    # c.sum(axis=1) =[3,14,2], 即每個向量將自己內(nèi)部元素加和作新一維向量里的一個元素 
    # 此處已經(jīng)將每一個行向量中的每一個元素都做了平方操作男翰,并求和得到每一個向量之和
    # 得到一個一維數(shù)組,其中有四個元素分別對應(yīng)了輸入元素與 4個測試樣本的平方距離
    sqDistances = sqDiffMat.sum(axis=1)     # [ 2.21  2.    0.    0.01]
    # 將平方距離開方
    distances = sqDistances**0.5 # [ 1.48660687  1.41421356  0.          0.1 ]
    # argsort函數(shù)返回的是數(shù)組值從小到大的索引值
    # examples:
    #     # x = np.array([3, 1, 2])
    #     # np.argsort(x)
    #         array([1, 2, 0]) 數(shù)值1對應(yīng)下表1纽乱,數(shù)值2對應(yīng)下表2蛾绎,數(shù)值3,對應(yīng)下標(biāo)0
    # [1.48660687  1.41421356  0.0  0.1 ] to [2 3 1 0]
    sortedDistIndex = distances.argsort()   # [2 3 1 0]
    # 建造一個名為classCount的字典,Key是label租冠,value是這個label出現(xiàn)的次數(shù)
    classCount={}
    for i in range(k):
        # 這里很巧妙的地方在于鹏倘,一開始測試數(shù)組和Label的位置是一一對應(yīng)的
        # 所以這里可以通過上面獲得的測試數(shù)組的下標(biāo)index獲得對應(yīng)的分類labels[index]
        voteLabel = labels[sortedDistIndex[i]]
        # 這里將每一個[label : Value]賦值,Value為計算VoteLabel出現(xiàn)的次數(shù)
        # 如果Label不在字典classCount中時顽爹,get()返回 0纤泵,存在則value加 1
        classCount[voteLabel] = classCount.get(voteLabel,0) + 1
 
    # 根據(jù)字典classCount中的Value值對字典進(jìn)行排序,選出出現(xiàn)次數(shù)最多的Label
    #       sorted(iterable, cmp=None, key=None, reverse=False) 
    #       return new sorted list
    #       1镜粤、iterable:是可迭代類型的對象(這里的字典通過iteritems()進(jìn)行迭代)
    #       2捏题、cmp:用于比較的函數(shù),比較什么由key決定肉渴,一般用Lamda函數(shù)
    #       3公荧、key:用列表元素的某個屬性或函數(shù)進(jìn)行作為關(guān)鍵字,迭代集合中的一項; 
    #               operator.itemgetter(1)表示獲得對象的第一個域的值(這里指value)
    #       4同规、reverse:排序規(guī)則循狰。reverse = True 降序 或者 reverse = False 升序。
    #       
    #       return: 一個經(jīng)過排序的可迭代類型對象券勺,與iterable一樣绪钥。
    #               這里返回排好序的[Label:value], 即 [('B', 2), ('A', 1)]
    sortedClassCount = sorted(classCount.iteritems(),
                              key=operator.itemgetter(1), reverse=True)
    # 返回可迭代對象的第一個域的第一值,也就是出現(xiàn)次數(shù)最多的Label
    # sortedClassCount[0][1] 表示第一個域的第二個值关炼,就是出現(xiàn)最多次數(shù)Label出現(xiàn)次數(shù)
    return sortedClassCount[0][0]

本案例的數(shù)據(jù)被置于txt文件中程腹,所以需要我們將數(shù)據(jù)從txt文件中抽出并做處理。所以我們準(zhǔn)備函數(shù)file2matrix()將txt的數(shù)據(jù)轉(zhuǎn)換為矩陣方便我們后面做運算盗扒。

""" Parsing data from a text file
Change the text file data to the format so that the classifier can accept and use it 
 
Args:
     filename string 
 
Returns:
    A matrix of training examples,
    A vector of class labels
 
Raises:
    None
"""
def file2matrix(filename):
    # 根據(jù)文件名打開文件
    fr = open(filename)
    # 讀取每一行的數(shù)據(jù)
    # txt文件中的數(shù)據(jù)類似:"40920      8.326976    0.953952    largeDoses"W
    arrayOlines = fr.readlines()
    # 得到文件行數(shù)
    numberOfLines = len(arrayOlines)
    # 創(chuàng)建返回的Numpy矩陣
    # zeros(a) 僅有一個參數(shù)時跪楞,創(chuàng)建一個一維矩陣,一行 a 列
    # zeros(a, b) 創(chuàng)建一個 a X b 矩陣侣灶, a 行 b 列
    # 這里取了數(shù)據(jù)的行數(shù)和前三個特征值(前三個是特征甸祭,第四個個是類別)創(chuàng)建一個矩陣
    returnMat = zeros((numberOfLines,3)) 
    # 建造一個名為 classLabelVector 的 List 用來存放最后列的Label
    classLabelVector = []
    # 這里的 index 指的是矩陣的第幾行,從 0 開始
    index = 0
    #( 以下三行) 解析文件數(shù)據(jù)到列表
    for line in arrayOlines:
        # 去掉文本中句子中的換行符"/n",但依然保持一行四個元素
        line = line.strip()
        # 以 ‘\t’(tab符號)分個字符串褥影,文本中的數(shù)據(jù)是以tab分開的
        # 另外需要注意 split() 函數(shù)返回一個 List對象
        #       如:['30254', '11.592723', '0.286188', '3']
        listFromLine = line.split('\t')
        # 選取前l(fā)istFromLine的三個元素池户,存儲在特征矩陣中
        # returnMat[index,:]這里需要注意,因為returnMat是一個矩陣
        #       其中的 index指的是矩陣中的第幾個list
        #       例如 listMat = [[list1] 
        #                       [list2] 
        #                       [list3]
        #                       ......
        #                       [listn]]
        #       listMat[0]表示的是矩陣listMat中的第1行凡怎,即 [list1]
        #       listMat[2,:] 表示的是矩陣listMat中的第3行的所有元素
        #       listMat[2,0:2] 表示矩陣listMat中的第3行下標(biāo)為 0和1兩個元素
        #
        # listFromLine[0:3]切片開始于0校焦、停止于3,也就是取了下標(biāo)為0,1,2的元素
        # 將listFromLine[0:3]的第0號到2號元素賦值給特征矩陣returnMat對應(yīng)特征中
        returnMat[index,:] = listFromLine[0:3]
        # listFromLine[-1] 取listFromLine的最后一列標(biāo)簽類(將其強行轉(zhuǎn)換為int類型)
        # 同時將該類別標(biāo)簽按順序加入到標(biāo)簽向量classLabelVector中
        classLabelVector.append(int(listFromLine[-1]))
        # index加 1统倒,矩陣存取下一個list
        index += 1
 
    return returnMat,classLabelVector

在這先根據(jù)特征值寨典,利用Matplotlib繪制類別的散點圖。

import matplotlib
# matplotlib的pyplot子庫提供了和matlab類似的繪圖API房匆,方便用戶快速繪制2D圖表耸成。
import matplotlib.pyplot as plt
# 我們先利用寫好的KNN.py文件中的file2matrix()函數(shù)將數(shù)據(jù)轉(zhuǎn)為矩陣
datingDataMat, datingLabels = KNN.file2matrix(filename)
# 這里創(chuàng)建了一個新的 figure报亩,我們要在上面作圖
fig = plt.figure()
# add_subplot(111)相當(dāng)于在figure上添加子圖
# 其中 111 的意思是,將子圖切割成 1行 1列的圖井氢,并在第 1 塊上作畫
# 所以這里只有一副畫
# 再舉個例子 add_subplot(22x)弦追,將子圖切割成 2行 2列的圖,并在第 X 塊上作畫
ax = fig.add_subplot(111) 
# 利用scatter()函數(shù)分別取已經(jīng)處理好的矩陣datingDataMat的第一列和第二列數(shù)據(jù)
# 并用不同顏色將類別標(biāo)識出來
ax.scatter(datingDataMat[:,0], datingDataMat[:,1],
15.0*array(datingLabels), 15.0*array(datingLabels))
# 展示我們做好的 figure
plt.show()

下圖是很好地解釋add_subplot(22x):


5 (1).png

根據(jù)矩陣第一列和第二列數(shù)據(jù)可以獲得以下帶有顏色類別區(qū)分的散點圖:


3.png

(紅色的點表示非常喜歡花竞,綠色的點表示一般喜歡劲件,藍(lán)色的點表示并不喜歡)

觀察上圖我們不難發(fā)現(xiàn)一個問題,如果當(dāng)某一個特征值與其他的特征值的差異過大的話约急,如果不加權(quán)重或各特征權(quán)重均衡的話零远,很可能會影響到最后的分類,比如例子中的飛行距離烤宙,遠(yuǎn)遠(yuǎn)大于其他兩個特征值遍烦,在這種情況下,特征的距離就會受飛行距離特征值得影響躺枕。所以我們需要將數(shù)據(jù)進(jìn)行歸一化服猪,即將所有數(shù)據(jù)都變?yōu)?0~1 之間的數(shù),目的是讓數(shù)據(jù)的影響都能比較均衡拐云。

""" Normalizing numeric values
Normalize the data to values between 0 and 1.
 
Formula:
    newValue = (oldValue-min)/(max-min)
In the normalization procedure, the variables min and max are the 
smallest and largest values in the dataset. 
 
Args:
     dataSet: the data set to be normalized 
 
Returns:
    A normalized data set
 
Raises:
    None
"""
def autoNorm(dataSet):
    # min(0)取矩陣每一列的最小值
    # min(1)取矩陣每一行的最小值
    # Examples:
    #     array([[1,2]
    #            [3,4]
    #            [5,6]])
    #       min(array,0) = [1,2]
    #       min(array,1) = [1,3,5]
    # 這里同: minVals = np.min(dataSet,0)
    # 返回的是一個一維矩陣
    minVals = dataSet.min(0)    
    # max(0)取矩陣每一列的最大值
    # max(1)取矩陣每一列的最大值
    # 返回的是一個一維矩陣    
    maxVals = dataSet.max(0)
    # ranges也是一個一維矩陣
    ranges = maxVals - minVals
    # shape(dataSet) 返回一個記錄有矩陣維度的tuple
    # 例如:np.shape([[1, 2]]) = (1, 2)
    # zeros(shape)返回一個具有 shape 維度的矩陣罢猪,并且所有元素用 0 填充
    normDataSet = zeros(shape(dataSet))
    # m 表示 dataSet 矩陣的行數(shù)
    # dataSet.shape[1] 表示矩陣的列數(shù)
    m = dataSet.shape[0]
    # tile(minVals, (m,1)) 首先將 minVals 復(fù)制為 m 行的矩陣
    # examples:
    #    if   minVals = [[1,2,3]]
    #         tile(minVals, (3,1))(行數(shù)復(fù)制 3 遍,列保持不變)
    #    return [[1,2,3]
    #            [1,2,3]
    #            [1,2,3]]
    normDataSet = dataSet - tile(minVals, (m,1))
    # 同上叉瘩,將 一維矩陣 ranges 復(fù)制 m行膳帕,再讓矩陣中的元素做商
    normDataSet = normDataSet/tile(ranges, (m,1))
    # 返回歸一化的矩陣,最大與最小值的差值矩陣薇缅,和最小值矩陣
    return normDataSet, ranges, minVals
4.png

(上圖為歸一化后的特征值)

我自己的博客地址為:謝雨熹的學(xué)習(xí)博客
歡迎大家來交流危彩!

Talk is cheap, show me your code!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市泳桦,隨后出現(xiàn)的幾起案子汤徽,更是在濱河造成了極大的恐慌,老刑警劉巖灸撰,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件谒府,死亡現(xiàn)場離奇詭異,居然都是意外死亡浮毯,警方通過查閱死者的電腦和手機完疫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來债蓝,“玉大人壳鹤,你說我怎么就攤上這事∈渭#” “怎么了器虾?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵讯嫂,是天一觀的道長。 經(jīng)常有香客問我兆沙,道長,這世上最難降的妖魔是什么莉掂? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任葛圃,我火速辦了婚禮,結(jié)果婚禮上憎妙,老公的妹妹穿的比我還像新娘库正。我一直安慰自己,他們只是感情好厘唾,可當(dāng)我...
    茶點故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布褥符。 她就那樣靜靜地躺著,像睡著了一般抚垃。 火紅的嫁衣襯著肌膚如雪喷楣。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天,我揣著相機與錄音,去河邊找鬼拂檩。 笑死雳锋,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的谆棱。 我是一名探鬼主播,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼坟募!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起邑狸,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤懈糯,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后推溃,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體昂利,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年铁坎,在試婚紗的時候發(fā)現(xiàn)自己被綠了蜂奸。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡硬萍,死狀恐怖扩所,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情朴乖,我是刑警寧澤祖屏,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布助赞,位于F島的核電站,受9級特大地震影響袁勺,放射性物質(zhì)發(fā)生泄漏雹食。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一期丰、第九天 我趴在偏房一處隱蔽的房頂上張望群叶。 院中可真熱鬧,春花似錦钝荡、人聲如沸街立。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽赎离。三九已至,卻和暖如春端辱,著一層夾襖步出監(jiān)牢的瞬間梁剔,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工掠手, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留憾朴,地道東北人。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓喷鸽,卻偏偏與公主長得像众雷,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子做祝,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,033評論 2 355

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