本篇我會使用scikit-learn這個開源機器學習庫來對iris數據集進行分類練習。python
我將分別使用兩種不一樣的scikit-learn
內置算法——Decision Tree(決策樹)
和kNN(鄰近算法)
,隨後我也會嘗試本身實現kNN
算法。目前爲止,我仍是在機器學習的入門階段,文章中暫不詳細解釋算法原理,若是想了解細節信息可自行搜索。git
scikit-learn
中預製了不少經典數據集,很是方便咱們本身練習用。使用方式也很容易:github
# 引入datasets from sklearn import datasets # 獲取所需數據集 iris = datasets.load_iris()
load_iris
返回的結果有以下屬性:算法
sepal length (cm)
, sepal width (cm)
, petal length (cm)
和 petal width (cm)
feature_names
中對應的值label
值),其值爲[0,1,2]監督學習
能夠用一個簡單的數學公式來表明:數組
y = f(X)app
按上一篇中的相關術語描述就是已知X(features)
,經過方法f(classifier)
求y(label)
。機器學習
按照這個思路,我將iris
數據分離爲:ide
# X = features X = iris.data # y = label y = iris.target
那如何來使用數據呢?由於只有150行數據,因此爲了驗證算法的正確性,須要將數據分紅兩部分:訓練數據
和測試數據
,很幸運的是scikit-learn
也提供了方便分離數據的方法train_test_split
,我將數據分離成60%
(即90條數據)用於訓練,40%
(即60條數據)用於測試,代碼以下:函數
from sklearn.model_selection import train_test_split X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=.6)
上一篇中已經用過決策樹,使用決策樹的代碼簡單以下:性能
# Decision tree classifier # 生成決策樹 my_classification = tree.DecisionTreeClassifier() # 訓練 my_classification.fit(X_train, y_train) # 預測 predictions = my_classification.predict(X_test)
經過決策樹算法,最終獲得的模型的準確率有多少呢?這個時候可使用scikit-learn
的accuracy_score
方法:
# 得到預測準確率 print(accuracy_score(y_test, predictions))
因爲train_test_split是隨機切分數據,所以最終跑出來的準確率不是一個固定值
kNN算法就是選取k個最近鄰居來歸類樣本值的方法,這是最簡單的一種分類算法,固然缺點也很明顯,必須循環計算測試樣本值和全部的樣本之間的距離,運行效率比較低。
在選用kNN算法的時候,k值最好是奇數
,偶數值會形成沒法歸到惟一類的狀況(屬於不一樣分類的機率正好相等)。
只需在上述分離數據以後,將決策樹算法的代碼替換爲:
# N neighbors classifier # 生成kNN my_classification2 = KNeighborsClassifier(n_neighbors=5) # 訓練 my_classification2.fit(X_train, y_train) # 預測 predictions2 = my_classification2.predict(X_test) # 得到預測準確率 print(accuracy_score(y_test, predictions2))
因爲train_test_split是隨機切分數據,所以最終跑出來的準確率不是一個固定值。並且因爲算法不一樣,即使是相同的數據,跑出來的準確率也和決策樹跑出來的不一樣。
基本思路是沿用上述內置kNN算法的代碼,從新實現KNeighborsClassifier
,稱之爲MyKNN
好了。除了初始化函數以外,還須要fit
和predict
這兩個方法,而且方法簽名和原先的保持一致,因此MyKNN
類的基本結構以下:
class MyKNN: def __init__(self, n_neighbors=5): pass def fit(self, X_train, y_train): pass def predict(self, X_test): pass
初始化方法僅需初始化幾個參數以便後續使用:
def __init__(self, n_neighbors=5): self.n_neighbors = n_neighbors self.X_train = None self.y_train = None
在這裏我簡單處理該方法,因爲原先fit方法包含了X和y兩個參數,所以沿用該方法簽名,這樣就不須要改動其餘代碼了:
def fit(self, X_train, y_train): self.X_train = X_train self.y_train = y_train
我須要在該方法中遍歷計算測試數據和訓練數據之間的距離,兩點之間的距離可使用歐幾里得公式,所以須要先定義一個外部方法my_euclidean
:
def my_euclidean(a, b): return distance.euclidean(a, b)
我須要計算當前測試數據與K個最近距離的訓練數據之間的值,而後看一下這K個數據中,最多的分類是哪一種,則可認爲測試數據也屬於該種分類(機率最高)。所以先定義一個私有方法__closest
:
def __closest(self, row): all_labels = [] for i in range(0, len(self.X_train)): dist = my_euclidean(row, self.X_train[i]) # 獲取k個最近距離的鄰居,格式爲(distance, index)的tuple集合 all_labels = self.__append_neighbors(all_labels, (dist, i)) # 將k個距離最近的鄰居,映射爲label的集合 nearest_ones = np.array([self.y_train[idx] for val, idx in all_labels]) # 使用numpy的unique方法,分組計算label的惟一值及其對應的值第一次出現的index和值的計數 # 例: elements = [1, 2], elements_index = [3,0], elements_count = [1, 4] 這個結合表示: # elements = [1, 2] : 出現了1和2兩種類型的數據 # elements_index = [3,0] : 1第一次出現的index是3, 2第一次出現的index是0 # elements_count = [1, 4] : 1共出現了1次, 2共出現了4次 elements, elements_index, elements_count = np.unique(nearest_ones, return_counts=True, return_index=True) # 返回最大可能性的那種類型的label值 return elements[list(elements_count).index(max(elements_count))]
爲了提高性能,我定義了__append_neighbors
方法,該方法將當前距離-序號
的tuple加入到數組中並按升序排序,最終只截取前k個值,能夠用python的特性很容易實現該邏輯:
def __append_neighbors(self, arr, item): if len(arr) <= self.n_neighbors: arr.append(item) return sorted(arr, key=lambda tup: tup[0])[:self.n_neighbors]
短短几行代碼就實現了本身的kNN
算法,我本地跑下來的準確率在95%以上。
須要完整代碼能夠在個人GitHub上找到。