K近鄰算法的分類與回歸應用場景
K近鄰(K-Nearest Neighbors, KNN)算法是一種基礎但強大的機器學習方法,它既可以用于分類問題,也能解決回歸問題。
兩者的核心思想都是基于"近朱者赤,近墨者黑"的原理,但應用場景和輸出形式有所不同。
生活中的應用場景
分類問題應用:
- 疾病診斷:根據患者的癥狀(如體溫、血壓、血液指標等)判斷患者是否患有某種疾病。
- 垃圾郵件識別:根據郵件內容和特征判斷郵件是否為垃圾郵件。
- 手寫數字識別:根據手寫數字的圖像特征判斷其對應的數字。
回歸問題應用:
- 房價預測:根據房屋的面積、臥室數量、地理位置等特征預測房價。
- 股票價格預測:根據歷史價格、交易量等特征預測未來股票價格。
- 電影評分預測:根據用戶的歷史評分和電影特征預測用戶對新電影的評分。
分類與回歸案例的代碼實現
下面我將用Python實現兩個案例,分別展示KNN在分類和回歸問題中的應用。
knn_classification.py
import numpy as np
import matplotlib.pyplot as plt
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score# 生成疾病診斷模擬數據
# 特征:體溫(°C)、血壓(收縮壓)、白細胞計數(10^9/L)
np.random.seed(42) # 設置隨機種子,確保結果可重現# 健康人群數據 (0表示健康)
healthy_samples = 100
healthy_features = np.zeros((healthy_samples, 3))
healthy_features[:, 0] = np.random.normal(36.5, 0.5, healthy_samples) # 體溫
healthy_features[:, 1] = np.random.normal(120, 10, healthy_samples) # 血壓
healthy_features[:, 2] = np.random.normal(6, 1, healthy_samples) # 白細胞計數
healthy_labels = np.zeros(healthy_samples)# 患病者數據 (1表示患病)
sick_samples = 100
sick_features = np.zeros((sick_samples, 3))
sick_features[:, 0] = np.random.normal(38.5, 1.0, sick_samples) # 體溫
sick_features[:, 1] = np.random.normal(140, 15, sick_samples) # 血壓
sick_features[:, 2] = np.random.normal(12, 3, sick_samples) # 白細胞計數
sick_labels = np.ones(sick_samples)# 合并數據
X = np.vstack((healthy_features, sick_features)) # 橫向組合
y = np.hstack((healthy_labels, sick_labels)) # 縱向組合# 劃分訓練集和測試集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)# 存儲不同 n_neighbors 對應的準確率
accuracy_dict = {}
for neighbors in range(1, 11):# 創建KNN分類模型knn_classifier = KNeighborsClassifier(n_neighbors=neighbors) # 設置K值為5# 訓練模型knn_classifier.fit(X_train, y_train)# 預測測試集y_pred = knn_classifier.predict(X_test)# 評估模型accuracy = accuracy_score(y_test, y_pred)accuracy_dict[neighbors] = accuracyprint(f"模型準確率: {accuracy}")# 設置中文字體支持
plt.rcParams['font.sans-serif'] = ['SimHei'] # 設置字體為黑體
plt.rcParams['axes.unicode_minus'] = False # 解決負號顯示問題# 根據鄰居數量和準確率關系繪制折線圖
plt.figure(figsize=(10, 6))
x = accuracy_dict.keys()
y = accuracy_dict.values()
plt.plot(x, y, marker='o', linestyle='-', color='blue', label='準確率')
plt.title('鄰居數量和準確率關系', fontsize=15)
plt.xlabel('鄰居數量', fontsize=12)
plt.ylabel('準確率', fontsize=12)
plt.grid(True, linestyle='--', alpha=0.7)
# 找到最高點
max_y = max(y)
max_index = list(y).index(max_y) # 找到最高點的索引# 高亮顯示最高點
plt.scatter(list(x)[max_index], max_y, color='red', s=100, zorder=10) # 使用scatter添加一個紅色的點,zorder確保它在上面# 添加一些文本標注以明確最高點
plt.text((list(x))[max_index], max_y, f'準確率: {max_y * 100:.2f}%', color='red', fontsize=12, ha='center', va='center')plt.legend()
plt.tight_layout()
plt.show()# 獲取準確率最高的 n_neighbors
n_neighbors = max(accuracy_dict, key=accuracy_dict.get)
print(f"模型準確最高的鄰居數量: {n_neighbors}")
# 創建KNN分類模型
knn_classifier = KNeighborsClassifier(n_neighbors=n_neighbors) # 設置K值為5# 訓練模型
knn_classifier.fit(X_train, y_train)# 預測測試集
y_pred = knn_classifier.predict(X_test)# 評估模型
accuracy = accuracy_score(y_test, y_pred)
accuracy_dict[neighbors] = accuracy
print(f"模型準確率: {accuracy:.2f}")# 可視化體溫和白細胞計數特征
plt.figure(figsize=(10, 6))
plt.scatter(X_test[y_test == 0][:, 0], X_test[y_test == 0][:, 2], c='green', marker='o', label='健康')
plt.scatter(X_test[y_test == 1][:, 0], X_test[y_test == 1][:, 2], c='red', marker='x', label='患病')# 標記預測錯誤的樣本
misclassified = X_test[y_test != y_pred]
plt.scatter(misclassified[:, 0], misclassified[:, 2], c='yellow', marker='*', s=100, label='預測錯誤')plt.xlabel('體溫 (°C)')
plt.ylabel('白細胞計數 (10^9/L)')
plt.title('疾病診斷的KNN分類結果')
plt.legend()
plt.grid(True)
plt.show()# 預測新患者
new_patient = np.array([[37.5, 130, 8.5]]) # 體溫37.5°C,血壓130,白細胞計數8.5
prediction = knn_classifier.predict(new_patient)
print(f"新患者預測結果: {'患病' if prediction[0] == 1 else '健康'}")
模型準確率: 0.9666666666666667
模型準確率: 0.9833333333333333
模型準確率: 0.95
模型準確率: 0.9666666666666667
模型準確率: 0.9666666666666667
模型準確率: 0.9666666666666667
模型準確率: 0.95
模型準確率: 0.95
模型準確率: 0.95
模型準確率: 0.95
模型準確最高的鄰居數量: 2
模型準確率: 0.98
新患者預測結果: 健康
knn_regression.py
import numpy as np
import matplotlib.pyplot as plt
from sklearn.neighbors import KNeighborsRegressor
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error, r2_score# 生成房價預測模擬數據
# 特征:房屋面積(m2)、臥室數量、房齡(年)
np.random.seed(42) # 設置隨機種子,確保結果可重現# 生成100個房屋樣本
samples = 100
X = np.zeros((samples, 3))
X[:, 0] = np.random.randint(50, 200, samples) # 房屋面積
X[:, 1] = np.random.randint(1, 6, samples) # 臥室數量
X[:, 2] = np.random.randint(1, 30, samples) # 房齡# 真實房價計算 (基礎價格 + 面積影響 + 臥室影響 - 房齡影響 + 隨機噪聲)
y_base = 500000 # 基礎價格
y_area = X[:, 0] * 5000 # 面積影響
y_bedrooms = X[:, 1] * 100000 # 臥室影響
y_age = X[:, 2] * 5000 # 房齡影響
y_noise = np.random.normal(0, 50000, samples) # 隨機噪聲y = y_base + y_area + y_bedrooms - y_age + y_noise # 最終房價# 劃分訓練集和測試集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)# 創建KNN回歸模型
knn_regressor = KNeighborsRegressor(n_neighbors=5) # 設置K值為5# 訓練模型
knn_regressor.fit(X_train, y_train)# 預測測試集
y_pred = knn_regressor.predict(X_test)# 評估模型
mse = mean_squared_error(y_test, y_pred)
rmse = np.sqrt(mse)
r2 = r2_score(y_test, y_pred)print(f"均方誤差 (MSE): {mse:.2f}")
print(f"均方根誤差 (RMSE): {rmse:.2f}")
print(f"決定系數 (R2): {r2:.2f}")# 設置中文字體支持
plt.rcParams['font.sans-serif'] = ['SimHei', "serif"] # 設置字體為黑體
plt.rcParams['axes.unicode_minus'] = False # 解決負號顯示問題# 可視化預測結果
plt.figure(figsize=(10, 6))
plt.scatter(y_test, y_pred, c='blue', alpha=0.7)
plt.plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], 'r--', lw=2)
plt.xlabel('實際房價 (元)')
plt.ylabel('預測房價 (元)')
plt.title('KNN回歸預測房價結果')
plt.grid(True)
plt.show()# 配置matplotlib支持中文字體和LaTeX數學符號
plt.rcParams.update({# 優先使用中文字體(SimHei黑體),同時保留serif字體用于LaTeX數學符號"font.family": ["SimHei", "serif"],# 使用Computer Modern字體渲染數學符號"mathtext.fontset": "cm",# 解決負號顯示問題,確保坐標軸中的負號顯示正確"axes.unicode_minus": False,# 不強制使用外部LaTeX,依賴matplotlib內置的數學表達式渲染"text.usetex": False,
})# 可視化房屋面積與房價關系及預測
plt.figure(figsize=(10, 6))
plt.scatter(X_test[:, 0], y_test, c='green', alpha=0.7, label='實際價格')
plt.scatter(X_test[:, 0], y_pred, c='red', alpha=0.7, label='預測價格')
plt.xlabel('房屋面積 (m2)')
plt.ylabel('房價 (元)')
plt.title('房屋面積與房價關系及KNN回歸預測')
plt.legend()
plt.grid(True)
plt.show()# 預測新房屋價格
new_house = np.array([[120, 3, 5]]) # 面積120m2,3個臥室,房齡5年
predicted_price = knn_regressor.predict(new_house)
print(f"預測新房屋價格: {predicted_price[0]:.2f} 元")
均方誤差 (MSE): 29534879082.91
均方根誤差 (RMSE): 171857.15
決定系數 (R2): 0.47
預測新房屋價格: 1318287.37 元
代碼解釋與核心差異
分類案例關鍵點:
- 使用
KNeighborsClassifier
類進行分類任務 - 輸出是離散的類別標簽(0或1,表示健康或患病)
- 評估指標使用準確率(Accuracy)
- 可視化展示了分類邊界和錯誤預測點
回歸案例關鍵點:
- 使用
KNeighborsRegressor
類進行回歸任務 - 輸出是連續的數值(房價)
- 評估指標使用均方誤差(MSE)、均方根誤差(RMSE)和決定系數(R2)
- 可視化展示了預測值與實際值的關系以及特征與目標的關系
核心差異:
- 分類算法預測的是類別,回歸算法預測的是數值
- 分類問題使用投票機制,回歸問題使用平均值或加權平均
- 評估方法完全不同,分類用準確率等,回歸用誤差指標
通過這兩個案例,你可以清晰地看到KNN算法在不同場景下的應用方式和實現差異。
KNN與回歸算法
- KNN回歸算法和線性回歸預測區別 各自適合的應用場景
- KNN分類算法和邏輯回歸分類區別 各自適合的應用場景
-
KNN 回歸算法和線性回歸預測區別
-
原理方面
- KNN 回歸 :KNN(K - Nearest Neighbors)回歸是一種基于實例的學習方法。它的工作原理是,對于一個新的輸入樣本,找到訓練集中與之最相似的 k 個樣本(近鄰),然后根據這 k 個近鄰樣本的目標值來預測新樣本的輸出值。例如,在預測房價時,找出與待預測房屋在面積、位置、房齡等因素上最相似的 k 個已知房價的房屋,將它們的房價進行平均(或其他聚合方式,如加權平均,權重可以是距離的倒數等)來作為待預測房屋的房價。這種方法不假設數據有特定的分布形式,只關心輸入樣本與近鄰樣本之間的相似性。
-
線性回歸 :線性回歸是基于模型的機器學習方法。它假設輸出變量(因變量)與輸入變量(自變量)之間存在線性關系,即目標值可以表示為輸入變量的線性組合加上一個誤差項。例如,對于房價預測,假設房價(因變量)與房屋面積(自變量)之間的關系是線性的,可以通過收集大量房屋的面積和對應房價的數據,擬合出一條直線(一元線性回歸)或一個超平面(多元線性回歸),用這個模型來預測新房屋的房價。
-
模型復雜度方面
- KNN 回歸 :模型復雜度主要取決于數據的分布和 k 值的選擇。當數據分布非常復雜、非線性程度很高時,只要選擇合適的 k 值,KNN 回歸可以很好地擬合數據。但如果 k 值選擇不當,比如 k 值過小,模型可能會過擬合,過于敏感于訓練集中的噪聲點;k 值過大,又可能會導致模型欠擬合,無法捕捉到局部的細節特征。例如,在包含大量噪聲的股票價格預測數據中,如果 k 值選得太小,模型可能被短期波動的噪聲所干擾;如果選得太大,又可能丟失了股票價格隨某些特定因素(如公司業績)變化的局部規律。
-
線性回歸 :模型復雜度相對較低,因為它假設數據符合線性關系。如果數據的實際關系不是線性的,線性回歸可能無法很好地擬合數據,導致預測誤差較大。例如,對于具有復雜曲線關系的化學反應速率與反應物濃度的數據,線性回歸就很難準確建模。
-
訓練與預測時間方面
- KNN 回歸 :訓練過程非常簡單,幾乎不需要訓練,只是將數據存儲起來。但在預測時,對于每個新樣本,都需要計算它與訓練集中所有樣本的距離,然后根據距離找到近鄰樣本并進行聚合計算,這在數據量很大時會導致預測速度較慢。例如,當訓練集包含數百萬個樣本時,預測一個新樣本可能需要大量的計算時間來計算距離。
-
線性回歸 :訓練過程需要通過求解方程(如最小二乘法)來確定模型的參數,這可能涉及到矩陣運算等計算。但這一步在數據量不是特別大的情況下還是比較高效的。一旦模型訓練完成,預測過程就非常簡單快速,只需要將輸入變量代入線性模型進行計算即可。
-
-
各自適合的應用場景
-
KNN 回歸適合的應用場景
- 數據模式不明確或數據關系復雜且非線性程度高。例如在一些小規模的、具有復雜局部特征的數據集上,如預測某種稀有疾病的康復時間,影響康復時間的因素眾多且關系復雜,包括患者的年齡、身體狀況、疾病嚴重程度等多種因素,這些因素與康復時間之間可能沒有明顯的線性關系。
-
線性回歸適合的應用場景
- 數據與目標變量之間存在明顯的線性關系。例如,在研究商品的銷售量與廣告投入之間的關系時,當廣告投入增加,銷售量也大致呈線性增長(在一定范圍內),這時線性回歸可以很好地建立兩者之間的關系模型。
-
-
KNN 分類算法和邏輯回歸分類區別
-
原理方面
- KNN 分類 :和 KNN 回歸類似,KNN 分類也是基于實例的學習方法。對于一個新的輸入樣本,找到訓練集中與之最相似的 k 個樣本,然后根據這 k 個近鄰樣本的類別標簽來進行投票,類別標簽出現次數最多的類別作為新樣本的預測類別。例如,在手寫數字識別中,將待識別數字圖像與訓練集中的數字圖像進行比較,找出最相似的 k 個數字圖像,若其中 “5” 這個類別出現的次數最多,就將待識別數字歸為 “5”。
-
邏輯回歸分類 :邏輯回歸是一種基于模型的分類方法,它使用邏輯函數(通常是 sigmoid 函數)將線性組合的輸入變量映射到 [0,1] 區間,表示樣本屬于某一類別的概率。例如,在預測用戶是否會購買某產品時,根據用戶的年齡、收入等特征構建線性組合,通過 sigmoid 函數轉換為購買的概率,當概率大于 0.5 時,預測為購買(1),否則預測為不購買(0)。
-
輸出方面
- KNN 分類 :直接輸出類別標簽,是基于近鄰樣本的類別投票結果。
-
邏輯回歸分類 :除了可以輸出類別標簽外,還能輸出樣本屬于各類別的概率,這在一些需要概率估計的場景下非常有用,比如在醫學診斷中,不僅可以判斷患者是否患有某種疾病,還可以得到患病的概率,幫助醫生進行決策。
-
模型假設方面
- KNN 分類 :不假設數據分布,只關注樣本之間的相似性。
-
邏輯回歸分類 :假設數據滿足一定的條件,例如因變量的對數幾率(log odds)與自變量之間存在線性關系。
-
-
各自適合的應用場景
-
KNN 分類適合的應用場景
- 樣本的類別分布比較復雜,或者數據量較小且特征空間的局部信息對于分類很重要。例如,在對不同種類的細胞圖像進行分類時,細胞的形態特征可能比較復雜,而且數據量可能有限,此時 KNN 分類可以利用局部相似性來進行分類。
-
邏輯回歸分類適合的應用場景
- 需要輸出概率估計或者特征與類別之間存在近似線性關系的情況。例如,在信用評分中,可以根據客戶的收入、信用歷史等特征,通過邏輯回歸模型來估計客戶違約的概率,從而進行信用風險評估。
-
案例:腫瘤診斷
參與分析的字段及其含義:
字段名 | 含義 |
---|---|
diagnosis | 診斷結果,可能是預測的目標變量 |
radius_mean | 半徑平均值 |
texture_mean | 紋理平均值 |
perimeter_mean | 周長平均值 |
area_mean | 面積平均值 |
smoothness_mean | 平滑度平均值 |
compactness_mean | 緊湊度平均值 |
concavity_mean | 凹陷度平均值 |
concave points_mean | 凹點平均值 |
symmetry_mean | 對稱性平均值 |
fractal_dimension_mean | 分形維度平均值 |
# 預測腫瘤
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
from sklearn.metrics import accuracy_score# 加載數據集
data = pd.read_csv('./data/bc_data.csv')# 提取特征和目標變量
# 選取特征變量,即除了 'id' 和 'diagnosis' 列之外的其他列
X = data.drop(['id', 'diagnosis'], axis=1)
# 選取目標變量 'diagnosis' 列
y = data['diagnosis']# 將數據集劃分為訓練集和測試集,設置測試集大小為 20%,并設置隨機種子以便結果可復現
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)# 存儲不同 n_neighbors 對應的準確率
accuracy_dict = {}# 創建 KNN 分類器,設置鄰居數量為 5
for n_neighbors in range(1, 11):knn = KNeighborsClassifier(n_neighbors=n_neighbors)# knn = KNeighborsClassifier(n_neighbors=5)# 使用訓練集訓練模型knn.fit(X_train, y_train)# 使用訓練好的模型對測試集進行預測y_pred = knn.predict(X_test)# 計算模型的準確率accuracy = accuracy_score(y_test, y_pred)accuracy_dict[n_neighbors] = accuracyprint(f"模型準確率: {accuracy}")x = accuracy_dict.keys()
y = accuracy_dict.values()
max_y = max(y) # 獲取最高準確率
max_index = list(y).index(max_y) # 獲取最高準確率的鄰居數量print(f"模型準確最高的鄰居數量: {list(x)[max_index]}")
print(f"模型最高的準確率: {max_y:.2f}")
模型準確率: 0.9298245614035088
模型準確率: 0.9385964912280702
模型準確率: 0.9298245614035088
模型準確率: 0.9473684210526315
模型準確率: 0.956140350877193
模型準確率: 0.9385964912280702
模型準確率: 0.956140350877193
模型準確率: 0.956140350877193
模型準確率: 0.956140350877193
模型準確率: 0.9649122807017544
模型準確最高的鄰居數量: 10
模型最高的準確率: 0.96