K近邻算法的分类与回归应用场景

发布于:2025-07-17 ⋅ 阅读:(17) ⋅ 点赞:(0)

K近邻算法的分类与回归应用场景

K近邻(K-Nearest Neighbors, KNN)算法是一种基础但强大的机器学习方法,它既可以用于分类问题,也能解决回归问题。

两者的核心思想都是基于"近朱者赤,近墨者黑"的原理,但应用场景和输出形式有所不同。

生活中的应用场景

分类问题应用

  • 疾病诊断:根据患者的症状(如体温、血压、血液指标等)判断患者是否患有某种疾病。
  • 垃圾邮件识别:根据邮件内容和特征判断邮件是否为垃圾邮件。
  • 手写数字识别:根据手写数字的图像特征判断其对应的数字。

回归问题应用

  • 房价预测:根据房屋的面积、卧室数量、地理位置等特征预测房价。
  • 股票价格预测:根据历史价格、交易量等特征预测未来股票价格。
  • 电影评分预测:根据用户的历史评分和电影特征预测用户对新电影的评分。

分类与回归案例的代码实现

下面我将用Python实现两个案例,分别展示KNN在分类和回归问题中的应用。

knn_classification.py

KNN分类

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] = accuracy
  print(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

KNN邻居数量和准确率关系

模型准确最高的邻居数量: 2
模型准确率: 0.98

疾病诊断的KNN分类结果

新患者预测结果: 健康
knn_regression.py

KNN预测

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

# 生成房价预测模拟数据
# 特征:房屋面积(m²)、卧室数量、房龄(年)
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"决定系数 (R²): {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('房屋面积 (m²)')
plt.ylabel('房价 (元)')
plt.title('房屋面积与房价关系及KNN回归预测')
plt.legend()
plt.grid(True)
plt.show()

# 预测新房屋价格
new_house = np.array([[120, 3, 5]])  # 面积120m²,3个卧室,房龄5年
predicted_price = knn_regressor.predict(new_house)
print(f"预测新房屋价格: {predicted_price[0]:.2f} 元")
均方误差 (MSE): 29534879082.91
均方根误差 (RMSE): 171857.15
决定系数 (R²): 0.47

KNN回归预测房价
matplotlib

预测新房屋价格: 1318287.37 元

代码解释与核心差异

分类案例关键点

  1. 使用KNeighborsClassifier类进行分类任务
  2. 输出是离散的类别标签(0或1,表示健康或患病)
  3. 评估指标使用准确率(Accuracy)
  4. 可视化展示了分类边界和错误预测点

回归案例关键点

  1. 使用KNeighborsRegressor类进行回归任务
  2. 输出是连续的数值(房价)
  3. 评估指标使用均方误差(MSE)、均方根误差(RMSE)和决定系数(R²)
  4. 可视化展示了预测值与实际值的关系以及特征与目标的关系

核心差异

  • 分类算法预测的是类别,回归算法预测的是数值
  • 分类问题使用投票机制,回归问题使用平均值或加权平均
  • 评估方法完全不同,分类用准确率等,回归用误差指标

通过这两个案例,你可以清晰地看到KNN算法在不同场景下的应用方式和实现差异。

KNN与回归算法

  • KNN回归算法和线性回归预测区别 各自适合的应用场景
  • KNN分类算法和逻辑回归分类区别 各自适合的应用场景
  1. KNN 回归算法和线性回归预测区别

    • 原理方面

      • KNN 回归 :KNN(K - Nearest Neighbors)回归是一种基于实例的学习方法。它的工作原理是,对于一个新的输入样本,找到训练集中与之最相似的 k 个样本(近邻),然后根据这 k 个近邻样本的目标值来预测新样本的输出值。例如,在预测房价时,找出与待预测房屋在面积、位置、房龄等因素上最相似的 k 个已知房价的房屋,将它们的房价进行平均(或其他聚合方式,如加权平均,权重可以是距离的倒数等)来作为待预测房屋的房价。这种方法不假设数据有特定的分布形式,只关心输入样本与近邻样本之间的相似性。
    • 线性回归 :线性回归是基于模型的机器学习方法。它假设输出变量(因变量)与输入变量(自变量)之间存在线性关系,即目标值可以表示为输入变量的线性组合加上一个误差项。例如,对于房价预测,假设房价(因变量)与房屋面积(自变量)之间的关系是线性的,可以通过收集大量房屋的面积和对应房价的数据,拟合出一条直线(一元线性回归)或一个超平面(多元线性回归),用这个模型来预测新房屋的房价。

    • 模型复杂度方面

      • KNN 回归 :模型复杂度主要取决于数据的分布和 k 值的选择。当数据分布非常复杂、非线性程度很高时,只要选择合适的 k 值,KNN 回归可以很好地拟合数据。但如果 k 值选择不当,比如 k 值过小,模型可能会过拟合,过于敏感于训练集中的噪声点;k 值过大,又可能会导致模型欠拟合,无法捕捉到局部的细节特征。例如,在包含大量噪声的股票价格预测数据中,如果 k 值选得太小,模型可能被短期波动的噪声所干扰;如果选得太大,又可能丢失了股票价格随某些特定因素(如公司业绩)变化的局部规律。
    • 线性回归 :模型复杂度相对较低,因为它假设数据符合线性关系。如果数据的实际关系不是线性的,线性回归可能无法很好地拟合数据,导致预测误差较大。例如,对于具有复杂曲线关系的化学反应速率与反应物浓度的数据,线性回归就很难准确建模。

    • 训练与预测时间方面

      • KNN 回归 :训练过程非常简单,几乎不需要训练,只是将数据存储起来。但在预测时,对于每个新样本,都需要计算它与训练集中所有样本的距离,然后根据距离找到近邻样本并进行聚合计算,这在数据量很大时会导致预测速度较慢。例如,当训练集包含数百万个样本时,预测一个新样本可能需要大量的计算时间来计算距离。
    • 线性回归 :训练过程需要通过求解方程(如最小二乘法)来确定模型的参数,这可能涉及到矩阵运算等计算。但这一步在数据量不是特别大的情况下还是比较高效的。一旦模型训练完成,预测过程就非常简单快速,只需要将输入变量代入线性模型进行计算即可。

  2. 各自适合的应用场景

    • KNN 回归适合的应用场景

      • 数据模式不明确或数据关系复杂且非线性程度高。例如在一些小规模的、具有复杂局部特征的数据集上,如预测某种稀有疾病的康复时间,影响康复时间的因素众多且关系复杂,包括患者的年龄、身体状况、疾病严重程度等多种因素,这些因素与康复时间之间可能没有明显的线性关系。
    • 线性回归适合的应用场景

      • 数据与目标变量之间存在明显的线性关系。例如,在研究商品的销售量与广告投入之间的关系时,当广告投入增加,销售量也大致呈线性增长(在一定范围内),这时线性回归可以很好地建立两者之间的关系模型。
  3. KNN 分类算法和逻辑回归分类区别

    • 原理方面

      • KNN 分类 :和 KNN 回归类似,KNN 分类也是基于实例的学习方法。对于一个新的输入样本,找到训练集中与之最相似的 k 个样本,然后根据这 k 个近邻样本的类别标签来进行投票,类别标签出现次数最多的类别作为新样本的预测类别。例如,在手写数字识别中,将待识别数字图像与训练集中的数字图像进行比较,找出最相似的 k 个数字图像,若其中 “5” 这个类别出现的次数最多,就将待识别数字归为 “5”。
    • 逻辑回归分类 :逻辑回归是一种基于模型的分类方法,它使用逻辑函数(通常是 sigmoid 函数)将线性组合的输入变量映射到 [0,1] 区间,表示样本属于某一类别的概率。例如,在预测用户是否会购买某产品时,根据用户的年龄、收入等特征构建线性组合,通过 sigmoid 函数转换为购买的概率,当概率大于 0.5 时,预测为购买(1),否则预测为不购买(0)。

    • 输出方面

      • KNN 分类 :直接输出类别标签,是基于近邻样本的类别投票结果。
    • 逻辑回归分类 :除了可以输出类别标签外,还能输出样本属于各类别的概率,这在一些需要概率估计的场景下非常有用,比如在医学诊断中,不仅可以判断患者是否患有某种疾病,还可以得到患病的概率,帮助医生进行决策。

    • 模型假设方面

      • KNN 分类 :不假设数据分布,只关注样本之间的相似性。
    • 逻辑回归分类 :假设数据满足一定的条件,例如因变量的对数几率(log odds)与自变量之间存在线性关系。

  4. 各自适合的应用场景

    • 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] = accuracy
  print(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

网站公告

今日签到

点亮在社区的每一天
去签到