機(jī)器學(xué)習(xí)之kNN算法(純python實(shí)現(xiàn))

前面文章分別簡(jiǎn)單介紹了線性回歸,邏輯回歸紫岩,貝葉斯分類(lèi),并且用python簡(jiǎn)單實(shí)現(xiàn)睬塌。這篇文章介紹更簡(jiǎn)單的 knn泉蝌, k-近鄰算法(kNN,k-NearestNeighbor)揩晴。
k-近鄰算法(kNN勋陪,k-NearestNeighbor),是最簡(jiǎn)單的機(jī)器學(xué)習(xí)分類(lèi)算法之一硫兰,其核心思想在于用距離目標(biāo)最近的k個(gè)樣本數(shù)據(jù)的分類(lèi)來(lái)代表目標(biāo)的分類(lèi)(這k個(gè)樣本數(shù)據(jù)和目標(biāo)數(shù)據(jù)最為相似)诅愚。

原理

kNN算法的核心思想是用距離最近(多種衡量距離的方式)的k個(gè)樣本數(shù)據(jù)來(lái)代表目標(biāo)數(shù)據(jù)的分類(lèi)。

具體講瞄崇,存在訓(xùn)練樣本集呻粹, 每個(gè)樣本都包含數(shù)據(jù)特征和所屬分類(lèi)值。
輸入新的數(shù)據(jù)苏研,將該數(shù)據(jù)和訓(xùn)練樣本集匯中每一個(gè)樣本比較等浊,找到距離最近的k個(gè),在k個(gè)數(shù)據(jù)中摹蘑,出現(xiàn)次數(shù)做多的那個(gè)分類(lèi)筹燕,即可作為新數(shù)據(jù)的分類(lèi)。

image.png

如上圖:
需要判斷綠色是什么形狀衅鹿。當(dāng)k等于3時(shí)撒踪,屬于三角。當(dāng)k等于5是大渤,屬于方形制妄。
因此該方法具有一下特點(diǎn):

  • 監(jiān)督學(xué)習(xí):訓(xùn)練樣本集中含有分類(lèi)信息
  • 算法簡(jiǎn)單, 易于理解實(shí)現(xiàn)
  • 結(jié)果收到k值的影響泵三,k一般不超過(guò)20.
  • 計(jì)算量大耕捞,需要計(jì)算與樣本集中每個(gè)樣本的距離衔掸。
  • 訓(xùn)練樣本集不平衡導(dǎo)致結(jié)果不準(zhǔn)確問(wèn)題

接下來(lái)用oython 做個(gè)簡(jiǎn)單實(shí)現(xiàn), 并且嘗試用于約會(huì)網(wǎng)站配對(duì)俺抽。

python簡(jiǎn)單實(shí)現(xiàn)

def classify(inX, dataSet, labels, k):
    """
    定義knn算法分類(lèi)器函數(shù)
    :param inX: 測(cè)試數(shù)據(jù)
    :param dataSet: 訓(xùn)練數(shù)據(jù)
    :param labels: 分類(lèi)類(lèi)別
    :param k: k值
    :return: 所屬分類(lèi)
    """

    dataSetSize = dataSet.shape[0]  #shape(m, n)m列n個(gè)特征
    diffMat = np.tile(inX, (dataSetSize, 1)) - dataSet
    sqDiffMat = diffMat ** 2
    sqDistances = sqDiffMat.sum(axis=1)
    distances = sqDistances ** 0.5  #歐式距離
    sortedDistIndicies = distances.argsort()  #排序并返回index

    classCount = {}
    for i in range(k):
        voteIlabel = labels[sortedDistIndicies[i]]
        classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1 #default 0

    sortedClassCount = sorted(classCount.items(), key=lambda d:d[1], reverse=True)
    return sortedClassCount[0][0]

算法的步驟上面有詳細(xì)的介紹敞映,上面的計(jì)算是矩陣運(yùn)算,下面一個(gè)函數(shù)是代數(shù)運(yùn)算磷斧,做個(gè)比較理解振愿。

def classify_two(inX, dataSet, labels, k):
    m, n = dataSet.shape   # shape(m, n)m列n個(gè)特征
    # 計(jì)算測(cè)試數(shù)據(jù)到每個(gè)點(diǎn)的歐式距離
    distances = []
    for i in range(m):
        sum = 0
        for j in range(n):
            sum += (inX[j] - dataSet[i][j]) ** 2
        distances.append(sum ** 0.5)

    sortDist = sorted(distances)

    # k 個(gè)最近的值所屬的類(lèi)別
    classCount = {}
    for i in range(k):
        voteLabel = labels[ distances.index(sortDist[i])]
        classCount[voteLabel] = classCount.get(voteLabel, 0) + 1 # 0:map default
    sortedClass = sorted(classCount.items(), key=lambda d:d[1], reverse=True)
    return sortedClass[0][0]

有了上面的分類(lèi)器,下面進(jìn)行最簡(jiǎn)單的實(shí)驗(yàn)來(lái)預(yù)測(cè)一下:

def createDataSet():
    group = np.array([[1, 1.1], [1, 1], [0, 0], [0, 0.1]])
    labels = ['A', 'A', 'B', 'B']
    return group, labels

上面是一個(gè)簡(jiǎn)單的訓(xùn)練樣本集弛饭。

if __name__ == '__main__':
    dataSet, labels = createDataSet()
    r = classify_two([0, 0.2], dataSet, labels, 3)
    print(r)

執(zhí)行上述函數(shù):可以看到輸出B冕末, [0 ,0.2]應(yīng)該歸入b類(lèi)。

上面就是一個(gè)最簡(jiǎn)單的kNN分類(lèi)器侣颂,下面有個(gè)例子栓霜。

kNN用于判斷婚戀網(wǎng)站中人的受歡迎程度

訓(xùn)練樣本集中部分?jǐn)?shù)據(jù)如下:

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    1

第一列表示每年獲得的飛行常客里程數(shù)横蜒, 第二列表示玩視頻游戲所耗時(shí)間百分比, 第三類(lèi)表示每周消費(fèi)的冰淇淋公升數(shù)销凑。第四列表示分類(lèi)結(jié)果丛晌,1, 2斗幼, 3 分別是 不喜歡澎蛛,魅力一般,極具魅力蜕窿。

  1. 將數(shù)據(jù)轉(zhuǎn)換成numpy谋逻。
# 文本轉(zhuǎn)換成numpy
def file2matrix(filepath="datingSet.csv"):
    dataSet = np.loadtxt(filepath)
    returnMat = dataSet[:, 0:-1]
    classlabelVector = dataSet[:, -1:]
    return returnMat, classlabelVector
  1. 首先對(duì)數(shù)據(jù)有個(gè)感知,知道是哪些特征影響分類(lèi)桐经,進(jìn)行可視化數(shù)據(jù)分析毁兆。
# 2, 3列數(shù)據(jù)進(jìn)行分析
def show_2_3_fig():
    data, cls = file2matrix()
    fig = plt.figure()
    ax = fig.add_subplot(111)
    ax.scatter(data[:, 1], data[: ,2], c=cls)
    plt.xlabel("playing game")
    plt.ylabel("Icm Cream")
    plt.show()

image.png

如上圖可以看到并無(wú)明顯的分類(lèi)阴挣。
image.png

image.png

可以看到不同的人根據(jù)特征有明顯的區(qū)分气堕。因此可以使用kNN算法來(lái)進(jìn)行分類(lèi)和預(yù)測(cè)。

  1. 由于后面要用到距離比較畔咧,因此數(shù)據(jù)之前的影響較大茎芭, 比如飛機(jī)里程和冰淇淋數(shù)目之間的差距太大。因此需要對(duì)數(shù)據(jù)進(jìn)行歸一化處理誓沸。
# 數(shù)據(jù)歸一化
def autoNorm(dataSet):
    minVal = dataSet.min(0)
    maxVal = dataSet.max(0)
    ranges = maxVal - minVal

    normDataSet = np.zeros(dataSet.shape)
    m, n = dataSet.shape  # 行梅桩, 特征
    normDataSet = dataSet - minVal
    normDataSet = normDataSet / ranges
    return normDataSet, ranges, minVal
  1. 衡量算法的準(zhǔn)確性
    knn算法可以用正確率或者錯(cuò)誤率來(lái)衡量。錯(cuò)誤率為0拜隧,表示分類(lèi)很好宿百。
    因此可以將訓(xùn)練樣本中的10%用于測(cè)試趁仙,90%用于訓(xùn)練。
# 定義測(cè)試算法的函數(shù)
def datingClassTest(h=0.1):
    hoRatio = h
    datingDataMat, datingLabels = file2matrix()
    normMat, ranges, minVals = autoNorm(datingDataMat)
    m, n = normMat.shape
    numTestVecs = int(m * hoRatio)  #測(cè)試數(shù)據(jù)行數(shù)
    errorCount = 0  # 錯(cuò)誤分類(lèi)數(shù)


    # 用前10%的數(shù)據(jù)做測(cè)試
    for i in range(numTestVecs):
        classifierResult = classify(normMat[i, :], normMat[numTestVecs:m, :], datingLabels[numTestVecs:m],  3)
        # print('the classifier came back with: %d,the real answer is: %d' % (int(classifierResult), int(datingLabels[i])))
        if classifierResult != datingLabels[i]:
            errorCount += 1
    print("the total error rate is: %f" % (errorCount / float(numTestVecs)))

調(diào)整不同的測(cè)試比例犀呼,對(duì)比結(jié)果幸撕。

  1. 使用knn進(jìn)行預(yù)測(cè)。
    有了訓(xùn)練樣本和分類(lèi)器外臂,對(duì)新數(shù)據(jù)可以進(jìn)行預(yù)測(cè)坐儿。模擬數(shù)據(jù)并進(jìn)行預(yù)測(cè)如下:
# 簡(jiǎn)單進(jìn)行預(yù)測(cè)
def classifypersion():
    resultList = ["none", 'not at all','in small doses','in large doses']
    # 模擬數(shù)據(jù)
    ffmiles = 15360
    playing_game = 8.545204
    ice_name = 1.340429

    datingDataMat, datingLabels = file2matrix()
    normMat, ranges, minVals = autoNorm(datingDataMat)
    inArr = np.array([ffmiles, playing_game, ice_name])
    # 預(yù)測(cè)數(shù)據(jù)歸一化
    inArr = (inArr - minVals) / ranges
    classifierResult = classify(inArr, normMat, datingLabels, 3)
    print(resultList[int(classifierResult)])

可以看到基本的得到所屬的分類(lèi)。

完成代碼和數(shù)據(jù)請(qǐng)參考:
github:kNN

總結(jié)

  • kNN
  • 監(jiān)督學(xué)習(xí)
  • 數(shù)據(jù)可視化
  • 數(shù)據(jù)歸一化宋光,不影響計(jì)算
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末貌矿,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子罪佳,更是在濱河造成了極大的恐慌逛漫,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件赘艳,死亡現(xiàn)場(chǎng)離奇詭異酌毡,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)蕾管,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)枷踏,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人掰曾,你說(shuō)我怎么就攤上這事旭蠕。” “怎么了旷坦?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵掏熬,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我秒梅,道長(zhǎng)旗芬,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任番电,我火速辦了婚禮岗屏,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘漱办。我一直安慰自己这刷,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布娩井。 她就那樣靜靜地躺著暇屋,像睡著了一般。 火紅的嫁衣襯著肌膚如雪洞辣。 梳的紋絲不亂的頭發(fā)上咐刨,一...
    開(kāi)封第一講書(shū)人閱讀 51,125評(píng)論 1 297
  • 那天昙衅,我揣著相機(jī)與錄音,去河邊找鬼定鸟。 笑死而涉,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的联予。 我是一名探鬼主播啼县,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼沸久!你這毒婦竟也來(lái)了季眷?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤卷胯,失蹤者是張志新(化名)和其女友劉穎子刮,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體窑睁,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡挺峡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了担钮。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片沙郭。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖裳朋,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情吓著,我是刑警寧澤鲤嫡,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站绑莺,受9級(jí)特大地震影響暖眼,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜纺裁,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一诫肠、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧欺缘,春花似錦栋豫、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至嫩絮,卻和暖如春丛肢,著一層夾襖步出監(jiān)牢的瞬間围肥,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來(lái)泰國(guó)打工蜂怎, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留穆刻,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓杠步,卻偏偏與公主長(zhǎng)得像氢伟,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子篮愉,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353

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