K近鄰:從理論到實踐

K近鄰:從理論到實踐

文章目錄

  • K近鄰:從理論到實踐
    • 1. 核心思想
    • 2. 距離度量
    • 3. k的選擇與誤差分析
      • 3.1 近似誤差
      • 3.2 估計誤差
      • 3.3 總誤差
    • 4. kd樹的構造與搜索
      • 4.1 kd樹的構造
      • 4.2 kd樹的搜索
    • 5. 總結
    • 6. K近鄰用于iris數據集分類
      • 6.1加載數據
      • 6.2加載模型并可視化

1. 核心思想

K近鄰(KNN)是一種基于實例的監督學習方法。其基本思想是:
對于一個待分類樣本,根據訓練集中與其“距離”最近的 kk 個鄰居的類別,通過投票或加權投票的方式決定該樣本的類別。

數學表達:
設訓練集為

D={(x1,y1),(x2,y2),…,(xn,yn)},xi∈Rd,yi∈{1,2,…,C}{D} = \{ (x_1,y_1), (x_2,y_2), \dots, (x_n,y_n) \}, \quad x_i \in \mathbb{R}^d, \; y_i \in \{1,2,\dots,C\}D={(x1?,y1?),(x2?,y2?),,(xn?,yn?)},xi?Rd,yi?{1,2,,C}

給定測試樣本x,找到其最近的 kk 個鄰居集合Nk(x){N}_k(x)Nk?(x)
預測類別為:

y^(x)=arg?max?c∈{1,…,C}∑(xi,yi)∈Nk(x)1(yi=c)\hat{y}(x) = \arg\max_{c \in \{1,\dots,C\}} \sum_{(x_i,y_i) \in \mathcal{N}_k(x)} \mathbf{1}(y_i = c)y^?(x)=argc{1,,C}max?(xi?,yi?)Nk?(x)?1(yi?=c)

其中,1(?){1}(\cdot)1(?) 是指示函數。

如果采用加權投票(考慮距離遠近),則為:

y^(x)=arg?max?c∈{1,…,C}∑(xi,yi)∈Nk(x)1∥x?xi∥?1(yi=c)\hat{y}(x) = \arg\max_{c \in \{1,\dots,C\}} \sum_{(x_i,y_i) \in \mathcal{N}_k(x)} \frac{1}{\|x - x_i\|} \cdot \mathbf{1}(y_i = c)y^?(x)=argc{1,,C}max?(xi?,yi?)Nk?(x)?x?xi?1??1(yi?=c)


2. 距離度量

KNN 依賴距離來衡量樣本相似度。常見的度量方式有:

  • 歐氏距離:

d(xi,xj)=∑l=1d(xi(l)?xj(l))2d(x_i, x_j) = \sqrt{\sum_{l=1}^d (x_i^{(l)} - x_j^{(l)})^2}d(xi?,xj?)=l=1d?(xi(l)??xj(l)?)2?

  • 曼哈頓距離:

d(xi,xj)=∑l=1d∣xi(l)?xj(l)∣d(x_i, x_j) = \sum_{l=1}^d |x_i^{(l)} - x_j^{(l)}|d(xi?,xj?)=l=1d?xi(l)??xj(l)?

  • 閔可夫斯基距離(推廣形式):

d(xi,xj)=(∑l=1d∣xi(l)?xj(l)∣p)1/pd(x_i, x_j) = \left( \sum_{l=1}^d |x_i^{(l)} - x_j^{(l)}|^p \right)^{1/p}d(xi?,xj?)=(l=1d?xi(l)??xj(l)?p)1/p


3. k的選擇與誤差分析

KNN 的性能對 k 值選擇敏感,體現了 近似誤差估計誤差 的權衡。

3.1 近似誤差

  • 定義:模型表達能力不足,導致預測結果無法逼近真實分布。
  • k 較大時:決策邊界過于平滑,難以捕捉復雜模式 → 近似誤差大
  • k 較小時:決策邊界靈活,可以更好地擬合真實模式 → 近似誤差小

數學上,假設真實函數為 f(x),KNN 的期望預測為:

f^(x)=ED[y^(x)]\hat{f}(x) = \mathbb{E}_{\mathcal{D}}[\hat{y}(x)]f^?(x)=ED?[y^?(x)]

則近似誤差為:

Bias2(x)=(ED[y^(x)]?f(x))2\text{Bias}^2(x) = \big( \mathbb{E}_{\mathcal{D}}[\hat{y}(x)] - f(x) \big)^2Bias2(x)=(ED?[y^?(x)]?f(x))2

3.2 估計誤差

  • 定義:模型對有限訓練數據過于依賴,泛化性差,導致預測不穩定。
  • k 較小時:極易受噪聲點影響,估計誤差大。
  • k 較大時:結果受單個點波動影響小,估計誤差小。

其數學形式為:

Var(x)=ED[(y^(x)?ED[y^(x)])2]\text{Var}(x) = \mathbb{E}_{\mathcal{D}}\big[(\hat{y}(x) - \mathbb{E}_{\mathcal{D}}[\hat{y}(x)])^2\big]Var(x)=ED?[(y^?(x)?ED?[y^?(x)])2]

3.3 總誤差

textMSE(x)=Bias2(x)+Var(x)+σ2text{MSE}(x) = \text{Bias}^2(x) + \text{Var}(x) + \sigma^2textMSE(x)=Bias2(x)+Var(x)+σ2

其中,σ2\sigma^2σ2 是不可約誤差。
因此,選擇合適的 k 值非常重要。


4. kd樹的構造與搜索

由于 KNN 需要計算測試點與所有訓練點的距離,時間復雜度為O(n)。為了加速,可以用 kd樹進行近鄰搜索。

4.1 kd樹的構造

  • kd樹是一種對數據進行遞歸二分的空間劃分結構。
  • 每次選擇一個維度(通常是方差最大的維度),按照該維度的中位數劃分數據。
  • 構造過程:
    1. 從根節點開始,選擇一個維度作為切分軸;
    2. 找到該維度的中位數,作為節點存儲值;
    3. 左子樹存儲小于該值的樣本,右子樹存儲大于該值的樣本;
    4. 遞歸進行直到樣本數過少或樹深度達到限制。

偽代碼:

function build_kd_tree(points, depth):
if points is empty:
return None
axis = depth mod d
sort points by axis
median = len(points) // 2
node = new Node(points[median])
node.left = build_kd_tree(points[:median], depth+1)
node.right = build_kd_tree(points[median+1:], depth+1)
return node


4.2 kd樹的搜索

kd樹搜索遵循“回溯+剪枝”原則:

  1. 從根節點開始,遞歸到葉子節點,找到測試點所屬的區域;
  2. 以該葉子節點為“當前最近鄰”;
  3. 回溯檢查父節點和另一子樹,若另一子樹中可能存在更近鄰,則遞歸進入;
  4. 維護一個大小為 kk 的優先隊列,存儲當前最近的 kk 個鄰居;
  5. 搜索結束時隊列中的點即為近鄰結果。

偽代碼:

function knn_search(node, target, k, depth):
if node is None:
return
axis = depth mod d
if target[axis] < node.point[axis]:
next = node.left
other = node.right
else:
next = node.right
other = node.left

function knn_search(node, target, k, depth):if node is None:returnaxis = depth mod dif target[axis] < node.point[axis]:next = node.leftother = node.rightelse:next = node.rightother = node.leftknn_search(next, target, k, depth+1)update priority queue with node.pointif |target[axis] - node.point[axis]| < current_max_distance_in_queue:knn_search(other, target, k, depth+1)

5. 總結

  • 核心思想:KNN 通過尋找最近的 kk 個鄰居來分類或回歸。
  • k 的選擇:小 kk → 近似誤差小、估計誤差大(過擬合);大 kk → 近似誤差大、估計誤差小(欠擬合)。
  • kd樹:通過空間劃分加速近鄰搜索,提升算法效率。

最終,KNN 的關鍵在于 合適的 k 值選擇高效的搜索結構


6. K近鄰用于iris數據集分類

6.1加載數據

from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_splitiris = load_iris(as_frame=True)
X = iris.data[["sepal length (cm)", "sepal width (cm)"]]
y = iris.target
X_train, X_test, y_train, y_test = train_test_split(X, y, stratify=y, random_state=0)

鳶尾花數據集,as_frame=True 表示返回 pandas DataFrame 而不是 numpy 數組,方便做列選擇。

這個數據集有 150 條樣本4 個特征sepal length, sepal width, petal length, petal width。目標變量 target 有三類 (0=setosa, 1=versicolor, 2=virginica)。

6.2加載模型并可視化

import matplotlib.pyplot as plt
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.inspection import DecisionBoundaryDisplay
import pandas as pd
import time# 1. 載入數據
iris = load_iris(as_frame=True)
X = iris.data[["sepal length (cm)", "sepal width (cm)"]]
y = iris.target
X_train, X_test, y_train, y_test = train_test_split(X, y, stratify=y, random_state=0
)# 2. 構建 pipeline:標準化 + KNN
clf = Pipeline(steps=[("scaler", StandardScaler()),("knn", KNeighborsClassifier(n_neighbors=11))]
)# 3. 不同的 weights 和 algorithm 組合
weights_list = ["uniform", "distance"]
algorithms = ["auto", "ball_tree", "kd_tree"]# 定義結果存儲表
results = []# 4. 畫圖:每行一個 weights,每列一個 algorithm
fig, axs = plt.subplots(nrows=len(weights_list), ncols=len(algorithms), figsize=(18, 10)
)for i, weights in enumerate(weights_list):for j, algo in enumerate(algorithms):ax = axs[i, j]# 設置參數并擬合start_train = time.time()clf.set_params(knn__weights=weights, knn__algorithm=algo).fit(X_train, y_train)end_train = time.time()start_pred = time.time()clf.predict(X_test)end_pred = time.time()acc = clf.score(X_test, y_test)results.append({"weights": weights,"algorithm": algo,"accuracy": acc,"train_time (s)": end_train - start_train,"predict_time (s)": end_pred - start_pred})# 決策邊界disp = DecisionBoundaryDisplay.from_estimator(clf,X_test,response_method="predict",plot_method="pcolormesh",xlabel=iris.feature_names[0],ylabel=iris.feature_names[1],shading="auto",alpha=0.5,ax=ax,)# 訓練樣本點scatter = disp.ax_.scatter(X.iloc[:, 0], X.iloc[:, 1], c=y, edgecolors="k")# 圖例disp.ax_.legend(scatter.legend_elements()[0],iris.target_names,loc="lower left",title="Classes",)# 子圖標題ax.set_title(f"k={clf[-1].n_neighbors}, weights={weights}, algo={algo}")plt.tight_layout()
plt.show()
df_results = pd.DataFrame(results)
print(df_results)

image-20250917183120303

 weights  algorithm  accuracy  train_time (s)  predict_time (s)
0   uniform       auto  0.710526        0.003293          0.004401
1   uniform  ball_tree  0.710526        0.004864          0.006618
2   uniform    kd_tree  0.710526        0.003537          0.004044
3  distance       auto  0.631579        0.003269          0.001961
4  distance  ball_tree  0.631579        0.003211          0.001694
5  distance    kd_tree  0.631579        0.003055          0.001578

不同 algorithm 的表現

  • autoball_treekd_tree 在相同權重下的 準確率完全一致,訓練預測速度不同,這說明 搜索算法僅影響計算效率,不會改變最終分類結果
  • 這和理論一致:算法只是用不同的數據結構加速鄰居查找,不會影響鄰居集合本身。

不同 weights 的表現

  • uniform 權重下,測試集準確率為 71.05%
  • distance 權重下,測試集準確率為 63.16%
  • 在本實驗中,uniform 明顯優于 distance
  • 這表明在鳶尾花數據的 前兩個特征(花萼長、寬) 上,等權投票比加權投票更適合。可能原因是:
    • 特征維度少,距離加權放大了噪聲點或邊界點的影響;
    • 類別邊界本身不完全線性,用距離權重反而削弱了多數鄰居的穩定性。

結合可視化

  • 從決策邊界圖上可以看到:

    • uniform 的邊界相對平滑,更符合數據整體分布;
      eights 的表現**
  • uniform 權重下,測試集準確率為 71.05%

  • distance 權重下,測試集準確率為 63.16%

  • 在本實驗中,uniform 明顯優于 distance

  • 這表明在鳶尾花數據的 前兩個特征(花萼長、寬) 上,等權投票比加權投票更適合。可能原因是:

    • 特征維度少,距離加權放大了噪聲點或邊界點的影響;
    • 類別邊界本身不完全線性,用距離權重反而削弱了多數鄰居的穩定性。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/98079.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/98079.shtml
英文地址,請注明出處:http://en.pswp.cn/web/98079.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Dokcer的安裝(ubuntu-20.04.6):

Dokcer的安裝(ubuntu-20.04.6)&#xff1a; 1.添加Docker倉庫 #更新本地軟件包索引&#xff0c;獲取最新的軟件包信息 sudo apt-get update #安裝依賴包 sudo apt-get install -y \ ca-certificates \ curl \ gnupg \ lsb-release #創建密鑰存儲目錄 sudo mkdir -p /etc/apt/…

CT圖像重建原理

一、CT到底測了什么&#xff1f;硬件動作X 射線源與探測器陣列對置&#xff0c;圍著物體旋轉。每轉到一個角度 θ&#xff08;也叫一個視角 / view&#xff09;&#xff0c;源發射扇形/平行的射線束&#xff0c;探測器陣列上有很多“通道/像素/bin”&#xff08;記作索引 n&…

【pycharm】 ubuntu24.04 搭建uv環境

通過uv配置python環境 一直是conda環境 現在有個開源項目說用uv更快更好 所以在pycharm搞起。 一開始在在一個conda項目的里面某個項目里搞 發現會被conda 環境影響。 導致deepseed 安裝不了。 python 環境不對 # NOTE: We must explicitly request them as `dependencies` abo…

從軟件工程角度談企業管理

從軟件工程角度談企業管理企業管理&#xff0c;本質上是人與人之間的博弈。 管理的最大難題&#xff0c;不是定目標、不是寫流程&#xff0c;而是&#xff1a;如何讓個體的利益最大化路徑&#xff0c;與組織的整體目標一致&#xff1f; 這就是經濟學里的“激勵相容”。 在互聯網…

vue3 實現前端生成水印效果

vue3 實現前端生成水印效果首先一點哈&#xff0c;就是單純web前端生成水印只能作為警示使用&#xff0c;如果享徹底防住幾乎是不可能的&#xff0c;有無數種方式去掉web前端生成的水印&#xff0c;所以這種方式只當是一個君子協議吧。編寫水印組件 首先直接把這部分封裝成一個…

Armonia Mall超級數字生態WEB3商城的引領者

Armonia Mall是一個基于Web3技術的超級數字生態商城&#xff0c;旨在打造全球首家Web3數字普惠商城&#xff0c;幫助千萬行銷人實現數字生態創業&#xff0c;讓全球一億家庭共享數字經濟紅利。 Armonia Mall商城創始人&#xff1a;石玉華Armonia Mall七大超級機制&#xff08;模…

Axios與Java Spring構建RESTful API服務集成指南

1 前后端分離時代的技術選擇 現在的Web開發&#xff0c;前后端分離已經不是什么新鮮事了。前端用什么&#xff1f;很多團隊選擇Axios。后端呢&#xff1f;Java Spring依然是企業級應用的首選。 Axios這個JavaScript庫確實好用&#xff0c;Promise-based的設計讓異步請求變得簡單…

Django ORM多對多關系實戰指南

一、Django 多對多關系的原理 在關系型數據庫中&#xff0c;多對多關系通常需要 第三張中間表 來維護兩張表之間的對應關系。 在 Django 中&#xff0c;你只需要定義 ManyToManyField&#xff0c;Django 會自動幫你創建這張中間表。 特點&#xff1a; 可以雙向查詢&#xff08;…

STM32 單片機開發 - TIM 定時器(PWM)

一、硬件定時器高級控制定時器 Advanced Control Timers (TIM1/TIM8)通用定時器 General Purpose Timers (TIM2/TIM3/TIM4/TIM5)通用定時器 General Purpose Timers (TIM15/TIM16/TIM17)基本定時器 Basic Timers (TIM6/TIM7)表 1 定時器種類二、TIM 中 PWM 概念PWM 的基本原理就…

OpenCV內置分類器實現簡單的人臉識別

引言 人臉檢測是計算機視覺領域的基礎任務之一&#xff0c;廣泛應用于安防監控、人機交互、圖像美化等場景。今天我們將通過一段簡潔的Python代碼&#xff0c;使用OpenCV庫實現實時攝像頭人臉檢測功能。無論你是計算機視覺新手還是有經驗的開發者&#xff0c;這篇文章都能幫你理…

Tomcat 性能優化與高并發調優

Tomcat 性能優化與高并發調優1. 引言 經過前幾篇文章的學習&#xff0c;我們已經掌握了 Tomcat 的核心原理&#xff1a; Connector 連接器容器體系&#xff08;Engine → Host → Context → Wrapper&#xff09;Servlet 執行鏈路線程模型&#xff08;Executor Worker&#xf…

MacOS M1安裝face_recognition

MacOS M1安裝face_recognition一致失敗&#xff0c;嘗試網上各種方法還是失敗&#xff0c;遂分享自己安裝成功的經歷。 conda虛擬環境python版本&#xff1a;3.9.23準備工作確保 Homebrew 已安裝 Homebrew 是 macOS 的包管理器&#xff0c;用于安裝依賴項。如果尚未安裝&#x…

動態庫和靜態庫的鏈接加載

靜態庫的鏈接與加載靜態庫&#xff08;如.a或.lib文件&#xff09;在編譯時直接鏈接到可執行文件中。編譯器會將靜態庫中實際用到的代碼復制到最終的可執行文件&#xff0c;生成獨立的二進制文件。優點是不依賴外部庫文件&#xff0c;但會導致可執行文件體積較大。生成靜態庫的…

如何處理在pytorch環境中已經安裝的matplotlib無法使用的問題

1 問題已經安裝好的matplotlib包無法在pytorch環境中使用。2 方法方法一&#xff1a;用命令安裝matplotlib &#xff1a;方法二&#xff1a;打開cmd&#xff0c;使用conda install matplotlib命令安裝matplotlib庫#輸入以下代碼段&#xff0c;查詢當前執行路徑import osos.sys.…

Linux基礎命令匯總

系統基礎指令 ls:列出目錄內容 ls -a:顯示所有文件(包括隱藏文件) ls -l:顯示詳細文件信息 ls /etc:列出 /etc 目錄內容 示例: cat:查看文件內容 cat /etc/os-release:查看系統版本信息 cat file1:顯示文件內容 cat file1 file2 > merged.txt:合并文件并輸出到新…

一場史詩級的冒險——Docker命令大航海!

各位親愛的開發者、運維勇士、以及所有對現代化軟件部署充滿好奇的小伙伴們&#xff01;今天&#xff0c;我們將開啟一場史詩級的冒險——Docker命令大航海&#xff01;我們將乘坐“Docker號”巨輪&#xff0c;駛向容器化技術的星辰大海。 這不是一篇枯燥的說明書&#xff0c;而…

告別依賴混亂:Spring IoC 容器與 DI 依賴注入入門精講

目錄 什么是 IoC IoC 介紹 傳統開發思路 解決方法 IoC 優勢 DI IoC & DI 使用 IoC 詳解 Bean 的存儲 Controller&#xff08;控制器存儲&#xff09; 獲取 bean 對象的其他方法 bean 命名 面試題之 ApplicationContext pk BeanFactory Service&#xff08;服…

視頻理解學習筆記

目錄 VideoRefer VideoPrism 核心解密&#xff1a;通用視頻編碼器的力量 VideoRefer VideoRefer 是由浙江大學和阿里達摩院聯合推出的視頻對象感知與推理技術&#xff0c;增強視頻大型語言模型&#xff08;Video LLMs&#xff09;的空間-時間理解能力。簡單一點來說就是可以…

P1198題解

題目鏈接 開題第一件事看數據范圍.這里的范圍是二十萬,支持O(nlogn). 這是一個RMQ問題,同時要加點,我們因此考慮ST表或者線段樹.這里用線段樹是核彈打蚊子,沒有意義,我們因此考慮ST表.我們注意到如果加點操作需要改動ST表原來的東西ST表就會炸掉,我們就要考慮更高級的數據結構…

使用yolov8對視頻進行目標檢測

使用 Ultralytics 的 YOLO 模型對視頻進行逐幀目標檢測非常簡單&#xff0c;以下是完整的實現方法&#xff1a; 我們的輸入視頻是這樣的 視頻目標檢測輸入視頻這里是天津市和平區天津大學附近&#xff0c;感興趣的小伙伴來天津玩哈&#xff01;&#xff01; 1. 安裝依賴 確保已…