熱線電話:13121318867

登錄
首頁精彩閱讀機器學習的敲門磚:kNN算法(上)
機器學習的敲門磚:kNN算法(上)
2019-10-15
收藏
<a href='/map/jiqixuexi/' style='color:#000;font-size:inherit;'>機器學習</a>的敲門磚:kNN算法(上)

作者 | Japson

來源 | 木東居士

0x00 前言

天下苦數學久矣!

對于很多想要入門機器學習的工程師來說,數學是通往AI道路上的第一支攔路虎。一些已經工作的同學不得不撿起早已還給老師的數學知識,勉強拿起《統計學習方法》、《西瓜書》等入門書籍鉆研?;虮灰粋€個復雜的機公式勸退,或記下一堆公式定理之后卻不知道和代碼有什么關系,茫然不知所措。

其實對于工程師來說,最直接的入門方法就是coding。

本系列從最簡單的機器學習算法“K-近鄰算法”開始,通過代碼走進機器學習的大門,搞定傳統機器學習算法。

首先會介紹算法的基本原理,然后依據原理手動實現算法,最后使用sklearn中提供的機器學習庫完成一些小demo。不用擔心,相關的機器學習概念以及算法原理也會穿插其中,幫助你以“代碼->原理->代碼”這種迭代的方式完成學習。

需要:

掌握Python語言,能夠使用Numpy、Pandas等工具庫。

安裝Anaconda

不要求對機器學習算法以及相關概念有很深刻的了解,因為在文章中會對首次出現的概念進行介紹。

子曰:“先行其言而后從之”。行動永遠是引發改變的第一步,話不多說,先讓我們碼起來吧!


0x01 初探kNN算法

為什么選擇kNN

為什么說KNN算法是機器學習的敲門磚?

首先KNN算法思想簡單樸素,容易理解,幾乎不需要任何數學知識。這一點使得KNN算法非常適合入門。

其次,KNN算法也很好用,理論成熟,簡單粗暴,既可以用來做分類(天然支持多分類),也可以用來做回歸。并且與樸素貝葉斯之類的算法相比,由于其對數據沒有假設,因此準確度高,對異常點不敏感。

最后,kNN算法簡單,但是可以解釋機器學習算法過程中的很多細節問題,能夠完整的刻畫機器學習應用的流程。

當然KNN算法也有缺點,我們會在最后進行總結。

kNN思想簡介

魯迅曾經說過:“想要了解一個人,就去看看他的朋友”。因此,KNN算法是魯迅發明的。

kNN(k-NearestNeighbor),也就是k最近鄰算法。顧名思義,所謂K最近鄰,就是k個最近的鄰居的意思。也就是在數據集中,認為每個樣本可以用離他最距離近的k個鄰居來代表。

貼出一張從百度百科上找的一張圖,我們可以直觀地感受到這樸素的思想:我們要判斷Xu 是什么顏色的,找到與其距離最近的5個點,有4個是紅色的,有1個是綠色的。因此我們認為Xu是屬于紅色的集合

<a href='/map/jiqixuexi/' style='color:#000;font-size:inherit;'>機器學習</a>的敲門磚:kNN算法(上)

因此我們說:

在一個給定的類別已知的訓練樣本集中,已知樣本集中每一個數據與所屬分類的對應關系(標簽)。在輸入不含有標簽的新樣本后,將新的數據的每個特征與樣本集中數據對應的特征進行比較,然后算法提取樣本最相似的k個數據(最近鄰)的分類標簽。通過多數表決等方式進行預測。即選擇k個最相似數據中出現次數最多的分類,作為新數據的分類。

K近鄰法不具有顯式的學習過程,而是利用訓練數據集對特征向量空間進行劃分,并作為其分類的“模型”。

kNN算法流程

通過理解算法思想,可以將其簡化為“找鄰居+投票”。K近鄰法使用的模型,實際上是特征空間的劃分。模型由三個基本要素決定:

  • 距離度量
  • k值
  • 分類決策規則

其中兩個實例點之間的距離反映了相似程度。一般來說使用歐氏距離來計算。

梳理kNN算法流程如下:

  1. 計算測試對象到訓練集中每個對象的距離
  2. 按照距離的遠近排序
  3. 選取與當前測試對象最近的k的訓練對象,作為該測試對象的鄰居
  4. 統計這k個鄰居的類別頻率
  5. k個鄰居里頻率最高的類別,即為測試對象的類別

<a href='/map/jiqixuexi/' style='color:#000;font-size:inherit;'>機器學習</a>的敲門磚:kNN算法(上)


0x02 算法實現

kNN算法自實現

打開Jupyter Notebook,創建Python3文件。

準備數據

首先我們準備一組數據:

import numpy as npimport matplotlib.pyplot as plt# raw_data_x是特征,raw_data_y是標簽,0為良性,1為惡性raw_data_X = [[3.393533211, 2.331273381],
 [3.110073483, 1.781539638],
 [1.343853454, 3.368312451],
 [3.582294121, 4.679917921],
 [2.280362211, 2.866990212],
 [7.423436752, 4.685324231],
 [5.745231231, 3.532131321],
 [9.172112222, 2.511113104],
 [7.927841231, 3.421455345],
 [7.939831414, 0.791631213]
 ]
raw_data_y = [0, 0, 0, 0, 0, 1, 1, 1, 1, 1]# 設置訓練組X_train = np.array(raw_data_X)
y_train = np.array(raw_data_y)# 將數據可視化plt.scatter(X_train[y_train==0,0],X_train[y_train==0,1], color='g', label = 'Tumor Size')
plt.scatter(X_train[y_train==1,0],X_train[y_train==1,1], color='r', label = 'Time')
plt.xlabel('Tumor Size')
plt.ylabel('Time')
plt.axis([0,10,0,5])
plt.show()

數據可視化后生成的圖片如下圖所示。其中橫軸是腫塊大小,縱軸是發現時間。每個病人的腫塊大小和發病時間構成了二維平面特征中的一個點。對于每個點,我們通過label明確是惡性腫瘤(綠色)、良性腫瘤(紅色)。

<a href='/map/jiqixuexi/' style='color:#000;font-size:inherit;'>機器學習</a>的敲門磚:kNN算法(上)

那么現在給出一個腫瘤患者的數據(樣本點)x:[8.90933607318, 3.365731514],是良性腫瘤還是惡性腫瘤

求距離

我們要做的是:求點x到數據集中每個點的距離,首先計算距離,使用歐氏距離

<a href='/map/jiqixuexi/' style='color:#000;font-size:inherit;'>機器學習</a>的敲門磚:kNN算法(上)

下面寫代碼:

from math import sqrt
distances = [] # 用來記錄x到樣本數據集中每個點的距離for x_train in X_train:
 d = sqrt(np.sum((x_train - x) ** 2))
 distances.append(d)# 使用列表生成器,一行就能搞定,對于X_train中的每一個元素x_train都進行前面的運算,把結果生成一個列表distances = [sqrt(np.sum((x_train - x) ** 2)) for x_train in X_train]
distances
輸出:[5.611968000921151, 6.011747706769277, 7.565483059418645, 5.486753308891268, 6.647709180746875, 1.9872648870854204, 3.168477291709152, 0.8941051007010301, 0.9830754144862234, 2.7506238644678445]

在求出距離列表之后,我們要找到最小的距離,需要進行一次排序操作。其實不是簡單的排序,因為我們把只將距離排大小是沒有意義的,我們要知道距離最小的k個點是在樣本集中的位置。

這里我們使用函數:np.argsort(array) 對一個數組進行排序,返回的是相應的排序后結果的索引

nearest = np.argsort(distances)
nearest
輸出:array([7, 8, 5, 9, 6, 3, 0, 1, 4, 2])
結果的含義是:距離最小的點在distances數組中的索引是7,第二小的點索引是8... 近到遠是哪些點


選k值

然后我們選擇k值,這里暫定為6,那就找出最近的6個點(top 6),并記錄他們的標簽值(y)

k = 6topK_y = [y_train[i] for i in nearest[:k]]
topK_y
輸出:[1, 1, 1, 1, 1, 0]
<a href='/map/jiqixuexi/' style='color:#000;font-size:inherit;'>機器學習</a>的敲門磚:kNN算法(上)


決策規則

下面進入投票環節。找到與測試樣本點最近的6個訓練樣本點的標簽y是什么??梢圆椴煌悇e的點有多少個。

將數組中的元素和元素出現的頻次進行統計

from collections import Counter
votes = Counter(topK_y)
votes
輸出:一個字典,原數組中值為0的個數為1,值為1的個數有為5Counter({0:1, 1:5})
# Counter.most_common(n) 找出票數最多的n個元素,返回的是一個列表,列表中的每個元素是一個元組,元組中第一個元素是對應的元素是誰,第二個元素是頻次votes.most_common(1)
輸出:[(1,5)]
predict_y = votes.most_common(1)[0][0]
predict_y
輸出:1

得到預測的y值是1


自實現完整工程代碼

我們已經在jupyter notebook中寫好了kNN算法,下面我們在外部進行封裝。

相關代碼可以在 https://github.com/japsonzbz/ML_Algorithms 中看到

import numpy as npimport math as sqrtfrom collections import Counterclass kNNClassifier:
 def __init__(self, k):
 """初始化分類器"""
 assert k >= 1, "k must be valid"
 self.k = k
 self._X_train = None
 self._y_train = None
 def fit(self, X_train, y_train):
 """根據訓練數據集X_train和y_train訓練kNN分類器"""
 assert X_train.shape[0] == y_train.shape[0], \ "the size of X_train must be equal to the size of y_train"
 assert self.k <= X_train.shape[0], \ "the size of X_train must be at least k"
 self._X_train = X_train
 self._y_train = y_train return self def predict(self,X_predict):
 """給定待預測數據集X_predict,返回表示X_predict結果的向量"""
 assert self._X_train is not None and self._y_train is not None, \ "must fit before predict!"
 assert X_predict.shape[1] == self._X_train.shape[1], \ "the feature number of X_predict must be equal to X_train"
 y_predict = [self._predict(x) for x in X_predict] return np.array(y_predict) def _predict(self, x):
 distances = [sqrt(np.sum((x_train - x) ** 2)) for x_train in self._X_train]
 nearest = np.argsort(distances)
 topK_y = [self._y_train[i] for i in nearest]
 votes = Counter(topK_y) return votes.most_common(1)[0][0] def __repr__(self):
 return "kNN(k=%d)" % self.k

當我們寫完定義好自己的kNN代碼之后,可以在jupyter notebook中使用魔法命令進行調用:

%run myAlgorithm/kNN.py
knn_clf = kNNClassifier(k=6)
knn_clf.fit(X_train, y_train)
X_predict = x.reshape(1,-1)
y_predict = knn_clf.predict(X_predict)
y_predict
輸出:array([1])

現在我們就完成了一個sklearn風格的kNN算法,但是實際上,sklearn封裝的算法比我們實現的要復雜得多。

<a href='/map/jiqixuexi/' style='color:#000;font-size:inherit;'>機器學習</a>的敲門磚:kNN算法(上)


sklearn中的kNN

代碼

對于機器學習來說,其流程是:訓練數據集 -> 機器學習算法 -fit-> 模型 輸入樣例 -> 模型 -predict-> 輸出結果

我們之前說過,kNN算法沒有模型,模型其實就是訓練數據集,predict的過程就是求k近鄰的過程。

我們使用sklearn中已經封裝好的kNN庫。你可以看到使用有多么簡單。

from sklearn.neighbors import KNeighborsClassifier# 創建kNN_classifier實例kNN_classifier = KNeighborsClassifier(n_neighbors=6)# kNN_classifier做一遍fit(擬合)的過程,沒有返回值,模型就存儲在kNN_classifier實例中kNN_classifier.fit(X_train, y_train)# kNN進行預測predict,需要傳入一個矩陣,而不能是一個數組。reshape()成一個二維數組,第一個參數是1表示只有一個數據,第二個參數-1,numpy自動決定第二維度有多少y_predict = kNN_classifier.predict(x.reshape(1,-1))
y_predict
輸出:array([1])

在kNN_classifier.fit(X_train, y_train)這行代碼后其實會有一個輸出:

KNeighborsClassifier(algorithm='auto', leaf_size=30, metric='minkowski',
 metric_params=None, n_jobs=1, n_neighbors=6, p=2,
 weights='uniform')

參數

class
sklearn.neighbors.KNeighborsClassifier(n_neighbors=5, weights=’uniform’, algorithm=’auto’, leaf_size=30, p=2, metric=’minkowski’, metric_params=None, n_jobs=None, **kwargs)

我們研究一下參數:

  • n_neighbors: int, 可選參數(默認為 5)。用于kneighbors查詢的默認鄰居的數量
  • weights(權重): str or callable(自定義類型), 可選參數(默認為 ‘uniform’)。用于預測的權重參數,可選參數如下:
  • uniform : 統一的權重. 在每一個鄰居區域里的點的權重都是一樣的。
  • distance : 權重點等于他們距離的倒數。
  • 使用此函數,更近的鄰居對于所預測的點的影響更大。
  • [callable] : 一個用戶自定義的方法,此方法接收一個距離的數組,然后返回一個相同形狀并且包含權重的數組。
  • algorithm(算法): {‘auto’, ‘ball_tree’, ‘kd_tree’, ‘brute’}, 可選參數(默認為 ‘auto’)。計算最近鄰居用的算法:
  • ball_tree 使用算法BallTree
  • kd_tree 使用算法KDTree
  • brute 使用暴力搜索
  • auto 會基于傳入fit方法的內容,選擇最合適的算法。
  • 注意 : 如果傳入fit方法的輸入是稀疏的,將會重載參數設置,直接使用暴力搜索。
  • leaf_size(葉子數量): int, 可選參數(默認為 30)。傳入BallTree或者KDTree算法的葉子數量。此參數會影響構建、查詢BallTree或者KDTree的速度,以及存儲BallTree或者KDTree所需要的內存大小。此可選參數根據是否是問題所需選擇性使用。
  • p: integer, 可選參數(默認為 2)。用于Minkowski metric(閔可夫斯基空間)的超參數。p = 1, 相當于使用曼哈頓距離,p = 2, 相當于使用歐幾里得距離],對于任何 p ,使用的是閔可夫斯基空間。
  • metric(矩陣): string or callable, 默認為 ‘minkowski’。用于樹的距離矩陣。默認為閔可夫斯基空間,如果和p=2一塊使用相當于使用標準歐幾里得矩陣. 所有可用的矩陣列表請查詢 DistanceMetric 的文檔。
  • metric_params(矩陣參數): dict, 可選參數(默認為 None)。給矩陣方法使用的其他的關鍵詞參數。
  • n_jobs: int, 可選參數(默認為 1)。用于搜索鄰居的,可并行運行的任務數量。如果為-1, 任務數量設置為CPU核的數量。不會影響fit

方法

對于KNeighborsClassifier的方法:

方法名含義fit(X, y)使用X作為訓練數據,y作為目標值(類似于標簽)來擬合模型。get_params([deep])獲取估值器的參數。neighbors([X, n_neighbors, return_distance])查找一個或幾個點的K個鄰居。kneighbors_graph([X, n_neighbors, mode])計算在X數組中每個點的k鄰居的(權重)圖。predict(X)給提供的數據預測對應的標簽。predict_proba(X)返回測試數據X的概率估值。score(X, y[, sample_weight])返回給定測試數據和標簽的平均準確值。set_params(**params)設置估值器的參數。


0xFF 總結

在本文中我們了解了第一個ML算法kNN,kNN憑借著自己樸素成熟的特點成為機器學習的敲門磚。

然后我們學習了kNN算法的流程,并且在jupyter notebook上手動實現了代碼,并且在外部也進行了封裝。最后我們學習了sklearn中的kNN算法。

雖然我們自己實現了一個機器學習算法,但是它的效果怎樣樣?預測準確率高不高?我們在機器學習過程中還有哪些需要注意的問題呢?

且聽下回分解。

數據分析咨詢請掃描二維碼

若不方便掃碼,搜微信號:CDAshujufenxi

數據分析師資訊
更多

OK
客服在線
立即咨詢
日韩人妻系列无码专区视频,先锋高清无码,无码免费视欧非,国精产品一区一区三区无码
客服在線
立即咨詢