第15章主要探討近鄰算法(NearestNeighbors)在機器學習領域中的實現與應用。並使用鳶尾花(Iris)資料集作為基礎,展示了近鄰算法的核心概念和基本實作方法。而Exercise則擴展至更大規模的MNIST手寫數字資料集,進行實際效能評估與比較。透過這些練習能夠學會該如何使用NearestNeighbors找出觀測值的最近鄰,以及如何建立K-最近鄰分類器(KNeighborsClassifier)進行預測。後續還有如何通過GridSearchCV找出最佳鄰居大小,優化近鄰模型的效能。另外還有以半徑為基礎的近鄰分類器(RadiusNeighborsClassifier),以及使用FAISS實現近似最近鄰搜索的技術。能夠更加了解在大規模資料集上不同近鄰方法的實際表現,以及時間效率與分類準確率之間的取捨關係。
OS: Windows 11 23H2
CPU: Intel Core i7-13700K
GPU: NVIDIA GeForce RTX 4070 SUPER 12G
RAM: DDR5 64G 5600MT/s
Python Package Manager: uv
Python Version: 3.10.16
Python Package: numpy, pandas, sklearn, rich, seaborn, matplotlib
IDE: Visual Studio Code
為方便查看運行結果,故額外使用rich函式庫建立兩個Function格式化印出指定內容。
15.1 Finding an Observation’s Nearest Neighbors
Code:
Explanation:
這段程式碼使用了sklearn提供的NearestNeighbors方法創建最近鄰居演算法模型處理Iris資料集。首先使用sklearn提供的StandardScaler方法標準化特徵數據,然後建立最近鄰居模型(k=2)。接著建立一個測試觀測值尋找其最近鄰居。另外還有透過設定metric為 "euclidean" 創建了兩個使用歐式距離的模型(k=2和k=3)。最後透過kneighbors_graph方法生成鄰接矩陣並移除自身連接。
Discussion:
最近鄰居法的優點在於簡單直觀且不需假設數據分布。然而,它對噪音敏感,計算成本隨數據量增加而提高,且在高維空間中效能可能下降。參數選擇(如k值)也會影響結果。
Result:
15.2 Creating a K-Nearest Neighbors Classifier
Code:
Explanation:
這段程式碼載入Iris數據集後,將數據使用StandardScaler標準化特徵後,再透過使用sklearn提供的KNeighborsClassifier方法建立K近鄰分類器(K=5)。最後,透過使用predict方法對兩個測試用的新觀測值進行分類預測,並透過predict_proba方法計算屬於各類別的機率。
Discussion:
K最近鄰居分類法實作簡單、不需訓練過程、可捕捉非線性關係且易於理解。然而,其計算成本高、需要大量記憶體儲存訓練數據、對特徵縮放敏感,以及在高維數據時容易受維度影響。K值的選擇也直接影響分類效能和泛化能力。
Result:
15.3 Identifying the Best Neighborhood Size
Code:
Explanation:
這段程式碼使用sklearn提供的Pipeline和GridSearchCV方法尋找Iris資料集分類的最佳K值。首先載入Iris數據集,然後透過Pipeline建立一個包含標準化處理器和K最近鄰居分類器的pipe。接著,透過GridSearchCV和交叉驗證(cv=5)評估不同K值(1到10)的表現。最後,使用best_estimator_.get_params()方法設定 "knn__n_neighbors" 從搜索結果中找出表現最佳的參數配置,並找出最佳的K值。
Discussion:
使用Pipeline和GridSearchCV能夠自動化超參數調整過程,避免人工試錯,同時確保資料預處理和模型評估的一致性。然而,對於較大數據集或較複雜模型,這種方法可能計算成本高昂且耗時。另外,搜索空間的設定仍需手動選擇評估指標。
Result:
15.4 Creating a Radius-Based Nearest Neighbors Classifier
Code:
Explanation:
這段程式碼透過使用sklearn提供的RadiusNeighborsClassifier方法建立半徑鄰居分類器對Iris資料集進行分類。首先載入Iris數據,分為特徵和目標,接著使用StandardScaler將特徵標準化。然後建立半徑鄰居分類器,設定半徑為0.5且使用全部CPU核心(n_jobs=-1),並使用標準化後的特徵和目標訓練模型。最後,使用predict方法對一個測試觀測值進行類別預測。
Discussion:
半徑鄰居分類法可根據數據密度自動調整考慮的鄰居數量,對於非均勻分布的數據特別有效。然而,其半徑選擇困難且敏感;太小可能導致某些樣本找不到鄰居而無法分類,太大則可能包含太多不相關樣本。此外,在高維空間中,合適半徑的選擇更加困難。
Result:
15.5 Finding Approximate Nearest Neighbors
Code:
Explanation:
這段程式碼利用FAISS對Iris數據進行最近鄰居搜索。首先載入並標準化Iris數據,然後透過faiss.IndexIVFFlat方法創建一個IVF索引並使用faiss.IndexFlatIP方法創建量化器。接著訓練並添加數據到索引中,最後使用search方法對一個測試觀測值搜索2個最近鄰居,並將這些鄰居的特徵向量提取出來。
Discussion:
FAISS的主要優點是在大規模數據集上執行高效的相似性搜索,提供顯著的速度提升。且其支援GPU加速和多種索引類型,適合高維向量檢索。然而,FAISS需要較多的記憶體資源,對於小數據集可能效益不明顯,且有一定的精度損失風險。
Result:
15.6 Evaluating Approximate Nearest Neighbors
Code:
Explanation:
這段程式碼主要為比較sklearn提供的最近鄰居搜索方法與FAISS的IVF近似搜索方法。首先載入Iris數據並標準化,然後分別用sklearn提供的NearestNeighbors和FAISS的IndexIVFFlat尋找同一個新觀測值的10個最近鄰居。最後計算這兩種方法所找出的鄰居集合的交集,評估FAISS近似搜索的召回率。
Discussion:
FAISS的IVF索引優點是大幅提升搜索速度,特別適合大規模數據集,並且可通過nprobe參數調節精度與速度的平衡。然而,它會犧牲部分搜索精度,且需要適當設定nlist和nprobe。此方法也有初始訓練成本,且在低維或小型數據集上效能優勢不明顯。
Result:
Settings
這邊使用MNIST作為主要資料集,對第15章中的每一種模型進行訓練與比較差異。
MNIST是機器學習領域中最經典的手寫數字識別資料集,包含60,000張訓練圖片和10,000張測試圖片。每張圖片大小為28×28像素,呈現0至9的手寫數字。因其簡單明確的目標和適中的複雜度,MNIST常作為深度學習入門教學的首選範例,可藉此熟悉基本模型的操作和評估方法。
Explanation:
首先透過fetch_openml方法下載MNIST資料集並存放於專案路徑下的data資料夾中。下載完成後載入資料集並轉換為對應的feature(data)與target(target),接著再對data進行正規化處理壓縮範圍至0 ~ 1之間以便進行訓練。最後再使用sklearn提供的train_test_split方法將MNIST資料即拆分為60000張圖片作為訓練集,10000張圖片作為評估集。
Code:
Explanation:
接下來透過sklearn中的各種不同的方法針對MNIST資料集進行訓練,包含NearstNeighbors、KNeighborsClassifier、RadiusNeighborsClassifier等,其中每一個模型皆使用不同的參數設定。
Code:
Explanation:
對於使用FAISS提供的方法進行準確度評估的部分使用index.search()方法對每一筆測試資料尋找k個最近的鄰居。FAISS搜尋結果會返回兩個陣列:distances包含了每個鄰居與查詢點的距離,而indices則包含了這些鄰居在訓練集中的索引位置。
接下來遍歷每個測試資料點的鄰居索引。對每組鄰居索引,程式碼首先透過列表獲取對應的標籤。這裡的條件idx >= 0是一個防護措施,因為FAISS有時會返回-1表示找不到足夠的鄰居。
當成功獲取鄰居標籤後,使用np.unique()函數同時獲取不重複的標籤值及其出現次數。unique_labels包含不重複的標籤,而counts則記錄了每個標籤出現的頻率。通過np.argmax()方法找出出現最多次的標籤的索引,再使用這個索引從unique_labels中獲取最終預測標籤pred_label。如果沒有有效的鄰居標籤(即neighbor_labels為空),則預設將預測值設為0。
這種多數投票的策略適合多分類問題如MNIST手寫數字識別(0-9共10個類別)。每個預測結果透過append方法添加到predictions列表中,最後轉換為NumPy陣列以便後續使用accuracy_score計算整體的準確度。
Code:
Discussion:
首先,值得注意的是透過GridSearchCV找出的最佳k值為1,理論上,k=1意味著分類僅基於單一個最接近的鄰居,這會非常容易受到雜訊影響。然而使用k值為1的實驗結果顯示模型的準確度為94.48%,略低於使用k=5的基本KNN模型(94.80%)。這種反直覺的結果可能源自MNIST資料集的特性:手寫數字有明顯的特徵區分,使得最近的單一鄰居通常就能提供足夠的分類信息,但也可能因過度擬合導致性能輕微下降。另一種解釋是,網格搜尋使用5折交叉驗證,而最終評估使用不同的測試集,這種差異可能導致輕微的性能波動。
關於RadiusNeighborsClassifier,雖然半徑設定高達18.5,但其準確度僅為75.91%,遠低於KNN的方法。這一個現象可能是因為固定半徑方法的根本限制:不同數字的特徵分布密度不一,固定半徑難以適應所有類別。某些區域可能包含多個類別的樣本,而另一些區域可能樣本稀少,導致投票機制效果不佳或找不到足夠的鄰居來做出準確的預測。
在訓練時間方面,FAISS的搜尋時間為3.32秒,而準確度為93.24%,相比之下KNN的預測時間約為2.1秒,準確度卻高達94.48%。這種效能差異反映了FAISS和傳統KNN的根本設計差異。FAISS採用了近似搜尋策略,通過將數據分割為多個集群來加速大規模數據的搜尋過程。然而,這種近似方法引入了一定的精度損失,因為它可能不會檢查所有潛在的鄰居。更重要的是,FAISS在實驗中設定k=2,意味著每個測試樣本只考慮兩個最近鄰居,而KNN使用k=5或k=1,這也會影響準確度。FAISS的真正優勢在於處理大規模數據集時,而MNIST數據集相對較小(60,000個訓練樣本),因此FAISS的索引架構在此情況下可能帶來了額外的開銷,且沒有顯著的速度優勢。而且,FAISS使用了內積(IndexFlatIP)而非歐氏距離作為相似度度量,這種度量方法的差異也可能對分類性能產生影響。
總體而言,這些結果表示在MNIST這類結構化資料集上,KNN方法是高效的,而FAISS提供了準確度與速度之間的良好平衡。
Result:
Difficulties Encountered and Solutions
1. 在MNIST資料集中使用Radius-Based Nearest Neighbors Classifier出現的半徑問題
Difficulties:在MNIST資料集中使用RadiusNeighborsClassifier時,半徑若設定為和課本中相同的0.5,則評估集中的辨識率會降至0%。
Solution:將RadiusNeighborsClassifier使用的半徑從0.5一步一步進行提升,直到辨識率可超越0%,或是進一步提升,經實驗結果可知,當半徑設定為18.5時,可在評估集中獲得最高的準確度(約75.91%)
2. 在MNIST資料集中使用Radius-Based Nearest Neighbors Classifier評估時出現的問題
Difficulties:在MNIST資料集中使用RadiusNeighborsClassifier時,若未設定outlier_label,則會導致在進行評估時,若該點於指定半徑 中找不到任何鄰居,則會報錯。
Solution:在RadiusNeighborsClassifier中加入參數outlier_label設為-1,即可解決此問題。(會出現以下提示)
e:\Coding\Python class file\ML\venv\lib\site-packages\sklearn\neighbors\_classification.py:864: UserWarning: Outlier label -1 is not in training classes. All class probabilities of outliers will be assigned with 0.
References
[1] Machine Learning with Python Cookbook-2nd, by Kyle Gallatin and Chris Albon, O'Reilly, 2023, ISBN: 9781098135720
[2] 機器學習入門:使用Scikit-Learn與TensorFlow, 黃建庭, 碁峰, 2021, ISBN: 9786263240285
[3] 精通機器學習:使用Scikit-Learn, Keras與TensorFlow(第三版), Aurélien Géron, 歐萊禮, 2024, ISBN: 9786263246676
[4] scikit-learn 機器學習實戰, 鄧立國 郭雅秋 陳子堯 鄧淇文, 清華大學, 2022, ISBN: 9787302604396
[5] Python資料科學學習手冊(第二版), Jake VanderPlas , 歐萊禮, 2023, ISBN: 9786263246843