09_分类算法--k近邻算法(KNN)、案例、欧氏距离、k-近邻算法API、KNeighborsClassifier、及其里面的案例(网络资料+学习资料整理笔记)
1 分類算法–k近鄰算法(KNN)
定義:如果一個樣本在特征空間中**k個最相似(即特征空間中最鄰近)**的樣本中的大多數屬于某一個類別,則該樣本也屬于這個類別,則該樣本也屬于這個類別。
k-近鄰算法采用測量不同特征值之間的距離來進行分類
優點:精度高、對異常值不敏感、無數據輸入假定。 缺點:計算復雜度高、空間復雜度高。 使用數據范圍:數值型和標稱型。以下是《機器原理》中的關于KNN的介紹
K最近鄰(k-Nearest Neighbor,KNN)分類算法可以說是最簡單的機器學習算法了。它采用測量不同特征值之間的距離方法進行分類。它的思想很簡單:如果一個樣本在特征空間中的k個最相似(即特征空間中最鄰近)的樣本中的大多數屬于某一個類別,則該樣本也屬于這個類別。
比如上面這個圖,我們有兩類數據,分別是藍色方塊和紅色三角形,他們分布在一個上圖的二維中間中。那么假如我們有一個綠色圓圈這個數據,需要判斷這個數據是屬于藍色方塊這一類,還是與紅色三角形同類。怎么做呢?我們先把離這個綠色圓圈最近的幾個點找到,因為我們覺得離綠色圓圈最近的才對它的類別有判斷的幫助。那到底要用多少個來判斷呢?這個個數就是k了。如果k=3,就表示我們選擇離綠色圓圈最近的3個點來判斷,由于紅色三角形所占比例為2/3,所以我們認為綠色圓是和紅色三角形同類。如果k=5,由于藍色四方形比例為3/5,因此綠色圓被賦予藍色四方形類。從這里可以看到,k的值還是很重要的。
該算法在分類時有個主要的不足是,當樣本不平衡時,如一個類的樣本容量很大,而其他類樣本容量很小時,有可能導致當輸入一個新樣本時,該樣本的K個鄰居中大容量類的樣本占多數。因此可以采用權值的方法(和該樣本距離小的鄰居權值大)來改進。該方法的另一個不足之處是計算量較大,因為對每一個待分類的文本都要計算它到全體已知樣本的距離,才能求得它的K個最近鄰點。目前常用的解決方法是事先對已知樣本點進行剪輯,事先去除對分類作用不大的樣本。該算法比較適用于樣本容量比較大的類域的自動分類,而那些樣本容量較小的類域采用這種算法比較容易產生誤分[參考機器學習十大算法]。
總的來說就是我們已經存在了一個帶標簽的數據庫,然后輸入沒有標簽的新數據后,將新數據的每個特征與樣本集中數據對應的特征進行比較,然后算法提取樣本集中特征最相似(最近鄰)的分類標簽。一般來說,只選擇樣本數據庫中前k個最相似的數據。最后,選擇k個最相似數據中出現次數最多的分類。其算法描述如下:
1)計算已知類別數據集中的點與當前點之間的距離;
2)按照距離遞增次序排序;
3)選取與當前點距離最小的k個點;
4)確定前k個點所在類別的出現頻率;
5)返回前k個點出現頻率最高的類別作為當前點的預測分類。
KNN做回歸和分類的主要區別在于最后做預測時候的決策方式不同。KNN做分類預測時,一般是選擇多數表決法,即訓練集里和預測的樣本特征最近的K個樣本,預測為里面有最多類別數的類別。而KNN做回歸時,一般是選擇平均法,即最近的K個樣本的樣本輸出的平均值作為回歸預測值。
1.1 一個例子弄懂k-近鄰
電影可以按照題材分類,每個題材又是如何定義的呢?那么假如兩種類型的電影,動作片和愛情片。動作片有哪些公共的特征?那么愛情片又存在哪些明顯的差別呢?我們發現動作片中打斗鏡頭的次數較多,而愛情片中接吻鏡頭相對更多。當然動作片中也有一些接吻鏡頭,愛情片中也會有一些打斗鏡頭。所以不能單純通過是否存在打斗鏡頭或者接吻鏡頭來判斷影片的類別。那么現在我們有6部影片已經明確了類別,也有打斗鏡頭和接吻鏡頭的次數,還有一部電影類型未知。
那么我們使用K-近鄰算法來分類愛情片和動作片:存在一個樣本數據集合,也叫訓練樣本集,樣本個數M個,知道每一個數據特征與類別對應關系,然后存在未知類型數據集合1個,那么我們要選擇一個測試樣本數據中與訓練樣本中M個的距離,排序過后選出最近的K個,這個取值一般不大于20個。選擇K個最相近數據中次數最多的分類。那么我們根據這個原則去判斷未知電影的分類
我們假設K為3,那么排名前三個電影的類型都是愛情片,所以我們判定這個未知電影也是一個愛情片。那么計算距離是怎樣計算的呢?
1.2 計算距離公式
兩個樣本的距離可以通過如下公式計算,又叫歐氏距離,比如說,a(a1,a2,a3),b(b1,b2,b3)
1.3 sklearn k-近鄰算法API
參考:https://www.jianshu.com/p/871884bb4a75
sklearn.neighbors.KNeighborsClassifier(n_neighbors=5, weights=’uniform’, algorithm=’auto’, leaf_size=30, p=2, metric=’minkowski’, metric_params=None,n_jobs=None, **kwargs)[source]1、n_neighbors:int,可選(默認=5),k_neighbors查詢默認使用的鄰居數,選擇最鄰近的數目k。2、weights:鄰近點的計算權重值,uniform代表各個點權重值相等。用于預測的權重函數。可選參數如下:A:‘uniform’:統一的權重. 在每一個鄰居區域里的點的權重都是一樣的。B:'distance':權重點等于他們距離的倒數。使用此函數,更近的鄰居對于所預測的點的影響更大。C:[callable]:一個用戶自定義的方法,此方法接收一個距離的數組,然后返回一個相同形狀并且包含權重的數組。。3、algorithm:{‘auto’,‘ball_tree’,‘kd_tree’,‘brute’},可選用于計算最近鄰居的算法:‘ball_tree’將會使用 BallTree,‘kd_tree’將使用 KDTree。‘auto’將嘗試根據傳遞給fit方法的值來決定最合適的算法。 (不同實現方式影響效率)4、leaf_size(葉子數量):int,可選參數(默認為30)、傳遞給BallTree或kTree的葉子大小,這會影響構造和查詢的速度,以及存儲樹所需的內存。5、p:Minkowski度量的指數參數。p = 1 代表使用曼哈頓距離 (l1),p = 2 代表使用歐幾里得距離(l2)6、metric:距離度量,點之間距離的計算方法。7、metric_params:額外的關鍵字度量函數。8、n_jobs:為鄰近點搜索運行的并行作業數。方法:
| fit(X,y) | 使用X作為訓練數據,y作為目標值(類似于標簽)來擬合模型 |
| get_params([deep]) | 獲取估值器的參數。 |
| kneighbors([X, n_neighbors, return_distance]) | 查找一個或幾個點的K個鄰居。返回每個點的下標和到鄰居的距離。 |
| kneighbors_graph([X,n_neighbors,mode]) | 計算在X數組中每個點的k鄰居的(權重)圖 |
| predit(X) | 給提供的數據預測對應的標簽。 |
| predict_proba(X) | 返回測試數據X的概率估值。 |
| score(X, y[, sample_weight]) | 返回給定測試數據和標簽的平均準確值。 |
| set_params(**params) | 設置估值器的參數。 |
案例:
from sklearn.neighbors import KNeighborsClassifierX = [[0],[1],[2],[3]] y = [0,0,1,1]neigh = KNeighborsClassifier(n_neighbors=3) neigh.fit(X,y)print(neigh.predict([[1.1]])) print(neigh.predict_proba([[0.9]]))輸出結果:
[0] [[0.66666667 0.33333333]] fit(X,y) 使用X作為訓練數據擬合模型,y作為X的類別值。X,y為數組或者矩陣參數:X: {類似數組, 稀疏矩陣, BallTree, KDTree}待訓練數據。如果是數組或者矩陣,形狀為 [n_samples, n_features],如果矩陣為’precomputed', 則形狀為[n_samples, n_samples]。y: {類似數組, 稀疏矩陣}形狀為[n_samples] 或者 [n_samples, n_outputs]的目標值。案例:
import numpy as np from sklearn.neighbors import KNeighborsClassifierneigh = KNeighborsClassifier(n_neighbors=3)X = np.array([[1, 1], [1, 1.1], [0, 0], [0, 0.1]]) y = np.array([1,1,0,0])print(neigh.fit(X,y))輸出結果為:
KNeighborsClassifier(algorithm='auto', leaf_size=30, metric='minkowski',metric_params=None, n_jobs=None, n_neighbors=3, p=2,weights='uniform') get_params(deep=True)獲取估值器的參數。參數:deep:boolean,可選參數如果為True,則返回估值器的參數,以包含子目標的估值器。 返回值:params:Mapping string to any返回Map變量,內容為[參數值:值,參數值:值,......] kneighbors(X=None,n_neighbors=None,return_distance=True)查詢一個或幾個點的K個鄰居, 返回每個點的下標和到鄰居的距離。參數:X: 類似數組, 形狀(n_query, n_features)或者(n_query, n_indexed) 。如果矩陣是‘precomputed’,形狀為(n_query, n_indexed)帶查詢的一個或幾個點。如果沒有提供,則返回每個有下標的點的鄰居們。n_neighbors: int鄰居數量 (默認為調用構造器時設定的n_neighboes的值).return_distance: boolean, 可選參數. 默認為 True.如果為 False,則不會返回距離返回值:dist: array當return_distance =True時,返回到每個點的長度。ind: array鄰居區域里最近的幾個點的下標。例子:
from sklearn.neighbors import NearestNeighbors samples = [[0., 0., 0.], [0., .5, 0.], [1., 1., .5]]# n_neighbors = 1故只得到1個最近的點 neigh = NearestNeighbors(n_neighbors=1) neigh.fit(samples) # 找到的是samples離[1.,1.,1.]最近的點,第一個值是距離值,第二個值是samples中的坐標值 print(neigh.kneighbors([[1.,1.,1.]])) """ 輸出的是: (array([[0.5]]), array([[2]], dtype=int64)) """如你所見返回值為[[0.5]]和[[2]]。意思是此點是距離為0.5并且是樣本中的第三個元素(下標從0開始)。你可以嘗試查詢多個點。
from sklearn.neighbors import NearestNeighbors samples = [[0., 0., 0.], [0., .5, 0.], [1., 1., .5]]# n_neighbors = 1故只得到1個最近的點 neigh = NearestNeighbors(n_neighbors=1) neigh.fit(samples)# 以下是查詢多個點的情況 X = [[0., 1., 0.], [1., 0., 1.]] print(neigh.kneighbors(X,return_distance=False)) """ 輸出的是: [[1][2]] """ print(neigh.kneighbors(X,return_distance=True)) """ 輸出結果: (array([[0.5 ],[1.11803399]]), array([[1],[2]], dtype=int64))其中第一個array表示分別離[0., 1., 0.]、 [1., 0., 1.]的距離 array([[1],[2]], dtype=int64) 表示samples的下標值 """ kneighbors_graph(X=None,n_neighbors=None,mode=’connectivity’) 計算在X數組中每個點的k鄰居的(權重)圖 參數:X: 類似數組, 形狀(n_query, n_features)。如果矩陣是‘precomputed’,形狀為(n_query, n_indexed)一個或多個待查詢點。如果沒有提供,則返回每個有下標的點的鄰居們。n_neighbors: int鄰居數量。(默認為調用構造器時設定的n_neighboes的值)。mode: {‘connectivity’, ‘distance’}, 可選參數返回矩陣數據類型: ‘connectivity’ 會返回1和0組成的矩陣。 in ‘distance’ 會返回點之間的歐幾里得距離。A:CSR格式的稀疏矩陣,形狀為 [n_samples, n_samples_fit]n_samples_fit 是擬合過的數據中樣例的數量,其中 A[i, j] 代表i到j的邊的權重。例子:
from sklearn.neighbors import NearestNeighbors neigh = NearestNeighbors(n_neighbors=2) X=[[0],[3],[1]] neigh.fit(X) A = neigh.kneighbors_graph(X) print(A.toarray())輸出結果:
[[1. 0. 1.][0. 1. 1.][1. 0. 1.]] predict(X) 給提供的數據預測相應的類別標簽 參數:X:類似數組, 形狀(n_query, n_features)。 如果矩陣是‘precomputed’,形狀為(n_query, n_indexed)待測試樣例。 返回值:y: 形狀為 [n_samples] 或者 [n_samples, n_outputs]的數組返回每個待測試樣例的類別標簽。 predict_proba(X) 返回測試數據X的概率估值。 參數:X:類似數組, 形狀(n_query, n_features)。 如果矩陣是‘precomputed’,形狀為(n_query, n_indexed) 的待測樣例。 返回值:p:形狀為[n_samples,n_classes]的數組,或者是n_outputs列表。輸入樣例的類別概率估值。其中類別根據詞典順序排序。 score(X,y,sample_weight=None) 返回給定測試數據和標簽的平均準確度。在多標簽分類中,返回的是各個子集的準確度。 參數:X:類似數組,形狀為(n_samples,n_features)待測試樣例。y:類似數組,形狀為(n_samples)或者(n_samples,n_outputs)X對于的正確標簽sample_weight:類似數組,形狀為[n_samples],可選參數。待測試的權重。返回值:score:floatself.predict(X)關于y的平均準確率。 set_params(**params) 設置估值器的參數。此方法在單個估值器和嵌套對象(例如管道)中有效。返回值:self使用到sklearn.neighbors.KNeighborsClassifier的案例
Classifier comparison : https://scikit-learn.org/stable/auto_examples/classification/plot_classifier_comparison.html#sphx-glr-auto-examples-classification-plot-classifier-comparison-py
Plot the decision boundaries of a VotingClassifier:https://scikit-learn.org/stable/auto_examples/ensemble/plot_voting_decision_regions.html#sphx-glr-auto-examples-ensemble-plot-voting-decision-regions-py
Digits Classification Exercise : https://scikit-learn.org/stable/auto_examples/exercises/plot_digits_classification_exercise.html#sphx-glr-auto-examples-exercises-plot-digits-classification-exercise-py
Nearest Neighbors Classification: https://scikit-learn.org/stable/auto_examples/neighbors/plot_classification.html#sphx-glr-auto-examples-neighbors-plot-classification-py
1.4 k-近鄰算法案例分析
本案例使用最著名的"鳶尾"數據集,該數據集曾經被Fisher用在經典論文中,目前作為教科書般的數據樣本預存在Scikit-learn的工具包中。
from sklearn.datasets import load_iris #使用加載器讀取數據并存入變量iris iris = load_iris()#查驗數據規模 print(iris.data.shape)#查看數據說明(這是一個好習慣) print(iris.DESCR)通過上述代碼對數據的查驗以及數據本身的描述,我們了解到Iris數據集共有150朵鳶尾數據樣本,并且均勻分布在3個不同的亞種;每個數據樣本有總共4個不同的關于花瓣、花萼的形狀特征所描述。由于沒有制定的測試集合,因此按照慣例,我們需要對數據進行隨即分割,25%的樣本用于測試,其余75%的樣本用于模型的訓練。
由于不清楚數據集的排列是否隨機,可能會有按照類別去進行依次排列,這樣訓練樣本的不均衡的,所以我們需要分割數據,已經默認有隨機采樣的功能。
對Iris數據集進行分割
# 用于獲取鳶尾花的數據集 from sklearn.datasets import load_iris #數據切分 from sklearn.model_selection import train_test_split # 標準化的庫 from sklearn.preprocessing import StandardScaler#使用加載器讀取數據并存入變量iris iris = load_iris()#查驗數據規模 print(iris.data.shape)#查看數據說明(這是一個好習慣) print(iris.DESCR)# 對iris數據集進行分割 X_train,X_test,y_train,y_test = train_test_split(iris.data,iris.target,test_size=0.25,random_state=42)# 對特征數據進行標準化 ss = StandardScaler() X_train = ss.fit_transform(X_train) X_test = ss.fit_transform(X_test)K近鄰算法是非常直觀的機器學習模型,我們可以發現K近鄰算法沒有參數訓練過程,也就是說,我們沒有通過任何學習算法分析訓練數據,而只是根據測試樣本訓練數據的分布直接作出分類決策。因此,K近鄰屬于無參數模型中非常簡單一種。
# 用于獲取鳶尾花的數據集 from sklearn.datasets import load_iris #數據切分 from sklearn.model_selection import train_test_split # 標準化的庫 from sklearn.preprocessing import StandardScaler # K近鄰算法 from sklearn.neighbors import KNeighborsClassifier from sklearn.metrics import classification_report from sklearn.model_selection import GridSearchCVdef knniris():"""鳶尾花分類:return: None"""# 1、數據集獲取和分割lr = load_iris()x_train, x_test, y_train, y_test = train_test_split(lr.data, lr.target, test_size=0.25)# 2、進行標準化std = StandardScaler()x_train = std.fit_transform(x_train)x_test = std.transform(x_test)# 3、estimator流程knn = KNeighborsClassifier()# 4、得出模型knn.fit(x_train,y_train)# 5、進行預測或得出精度(得出x_test所屬的分類)y_predict = knn.predict(x_test)print(y_predict)"""結果:[1 0 1 2 2 2 2 2 0 1 1 0 0 2 2 0 1 2 2 1 2 2 1 0 1 0 0 0 2 1 1 2 0 2 0 0 2 2]"""score = knn.score(x_test,y_test)print(score)"""結果為:0.9473684210526315"""#===============================================================print("===============================================================")# 通過網格搜索,n_neighbors為參數列表param = {"n_neighbors": [3, 5, 7]}gs = GridSearchCV(knn, param_grid=param, cv=10)# 建立模型gs.fit(x_train, y_train)# print(gs)# 預測數據print(gs.score(x_test, y_test))# 分類模型的精確率和召回率# print("每個類別的精確率與召回率:",classification_report(y_test, y_predict,target_names=lr.target_names))return Noneif __name__ == "__main__":knniris()輸出結果:
[1 0 0 1 2 2 1 0 0 1 1 1 0 1 2 0 1 2 1 1 0 2 2 1 0 0 0 0 0 2 2 0 2 1 2 1 1 2] 0.9736842105263158 =============================================================== D:\installed\Anaconda3\lib\site-packages\sklearn\model_selection\_search.py:814: DeprecationWarning: The default of the `iid` parameter will change from True to False in version 0.22 and will be removed in 0.24. This will change numeric results when test-set sizes are unequal.DeprecationWarning) 0.9736842105263158案例2:Facebook-K-近鄰預測用戶簽到位置案例
數據格式:
輸出結果為:
600 1970-01-01 18:09:40 957 1970-01-10 02:11:10 4345 1970-01-05 15:08:02 4735 1970-01-06 23:03:03 5580 1970-01-09 11:26:50... 29100203 1970-01-01 10:33:56 29108443 1970-01-07 23:22:04 29109993 1970-01-08 15:03:14 29111539 1970-01-04 00:53:41 29112154 1970-01-08 23:01:07 Name: time, Length: 17710, dtype: datetime64[ns] E:/workspace/numpy/sklearn/03_KNeighborsClassifier.py:165: SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame. Try using .loc[row_indexer,col_indexer] = value instead See the caveats in the documentation: http://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copydata['day'] = time_value.day E:/workspace/numpy/sklearn/03_KNeighborsClassifier.py:166: SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame. Try using .loc[row_indexer,col_indexer] = value instead See the caveats in the documentation: http://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copydata['hour'] = time_value.hour E:/workspace/numpy/sklearn/03_KNeighborsClassifier.py:167: SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame. Try using .loc[row_indexer,col_indexer] = value instead See the caveats in the documentation: http://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copydata['weekday'] = time_value.weekdayrow_id x y accuracy place_id day hour weekday 600 600 1.2214 2.7023 17 6683426742 1 18 3 957 957 1.1832 2.6891 58 6683426742 10 2 5 4345 4345 1.1935 2.6550 11 6889790653 5 15 0 4735 4735 1.1452 2.6074 49 6822359752 6 23 1 5580 5580 1.0089 2.7287 19 1527921905 9 11 4... ... ... ... ... ... ... ... 29100203 29100203 1.0129 2.6775 12 3312463746 1 10 3 29108443 29108443 1.1474 2.6840 36 3533177779 7 23 2 29109993 29109993 1.0240 2.7238 62 6424972551 8 15 3 29111539 29111539 1.2032 2.6796 87 3533177779 4 0 6 29112154 29112154 1.1070 2.5419 178 4932578245 8 23 3 [17710 rows x 8 columns] D:\installed\Anaconda3\lib\site-packages\sklearn\model_selection\_split.py:657: Warning: The least populated class in y has only 1 members, which is too few. The minimum number of members in any class cannot be less than n_splits=2.% (min_groups, self.n_splits)), Warning) 在測試集上準確率: 0.4189125295508274 在交叉驗證當中最好的結果: 0.39344262295081966 選擇最好的模型是: KNeighborsClassifier(algorithm='auto', leaf_size=30, metric='minkowski',metric_params=None, n_jobs=None, n_neighbors=10, p=2,weights='uniform') 每個超參數每次交叉驗證的結果: {'mean_fit_time': array([0.01545823, 0.00547051, 0.00598311]), 'std_fit_time': array([1.04693174e-02, 4.82320786e-04, 2.78949738e-05]), 'mean_score_time': array([0.36103368, 0.38176703, 0.47072983]), 'std_score_time': array([0.02695775, 0.01324654, 0.02192879]), 'param_n_neighbors': masked_array(data=[3, 5, 10],mask=[False, False, False],fill_value='?',dtype=object), 'params': [{'n_neighbors': 3}, {'n_neighbors': 5}, {'n_neighbors': 10}], 'split0_test_score': array([0.34967218, 0.37308773, 0.39541055]), 'split1_test_score': array([0.35561923, 0.37695002, 0.39143585]), 'mean_test_score': array([0.35261665, 0.375 , 0.39344262]), 'std_test_score': array([0.00297338, 0.00193105, 0.00198726]), 'rank_test_score': array([3, 2, 1])}1.5 實例流程
1、數據集的處理 2、分割數據集 3、對數據集進行標準化 4、estimator流程進行分類預測1.6 問題
1、k值取多大?有什么影響 k值取很小:容易受異常點影響 k值取很大:容易受最近數據太多導致比例變化1.7 k-近鄰算法優缺點
1、優點1.1 簡單,易于理解,易于實現,無需估計參數,無需訓練 2、缺點2.1 懶惰算法,對測試樣本分類時的計算量大,內存開銷大。2.2 必須指定K值,K值選擇不當則分類精度不能保證 3、使用場景:小數據場景,幾千~幾萬樣本,具體場景具體業務去測試1.8 k-近鄰算法實現
加快搜索速度——基于算法的改進KDTree,API接口里面有實現。
1.9 k近鄰算法作業:
1、通過k-近鄰算法對生物物種進行分類——鳶尾花(load_iris)
總結
以上是生活随笔為你收集整理的09_分类算法--k近邻算法(KNN)、案例、欧氏距离、k-近邻算法API、KNeighborsClassifier、及其里面的案例(网络资料+学习资料整理笔记)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 春节吃什么 中国传统春节饮食文化解析?
- 下一篇: 忘记手机银行登录密码怎么办