02. KNN算法

一、KNN算法

  K 近邻算法(K-Nearest Neighbors,KNN)是一种基本的分类与回归方法,属于监督学习算法。KNN 算法的核心思想是通过计算给定样本与数据集中所有样本的距离,找到距离最近的 K 个样本,然后根据这 K 个样本的类型或值来预测当前样板的类别或值。

  我们可以在终端中使用 pip 安装 sklearn 机器学习库和 matplotlib 绘图库。默认是从国外的主站上下载,因此,我们可能会遇到网络不好的情况导致下载失败。我们可以在 pip 指令后通过 -i 指定国内镜像源下载

pip install scikit-learn matplotlib -i https://mirrors.aliyun.com/pypi/simple

  国内常用的 pip 下载源列表:

【1】、分类模型

import numpy as np
import matplotlib.pyplot as plt

from sklearn.neighbors import KNeighborsClassifier

knn = KNeighborsClassifier(n_neighbors=2, weights='distance')                   # KNN分类模型

X = np.array([[2, 1], [3, 1], [1, 4], [2, 6]])                                  # 特征
y = np.array([0, 1, 0, 1])                                                      # 标签

knn.fit(X, y)                                                                   # 模型训练

x = np.array([[4, 9]])
x_class = knn.predict(x)                                                        # 模型预测

fig, ax = plt.subplots()                                                        # 获取子图
ax.axis('equal')                                                                # 使坐标轴比例相等
ax.grid(True)                                                                   # 显示网格

# 使用布尔索引将两类点分开
X1 = X[y == 0]
X2 = X[y == 1]

# 定义不同的颜色,画两组点
colors = ["C0", "C1"]
plt.scatter(X1[:, 0], X1[:, 1], color=colors[0], label='Class 0')
plt.scatter(X2[:, 0], X2[:, 1], color=colors[1], label='Class 1')

x_color = colors[0] if x_class == 0 else colors[1]                              # 新点的颜色
plt.scatter(x[:, 0], x[:, 1], color=x_color, label='New Point')                 # 画新点

plt.show()                                                                      # 显示图像

【2】、回归模型

import numpy as np

from sklearn.neighbors import KNeighborsRegressor

knn = KNeighborsRegressor(n_neighbors=2, weights='distance')                    # KNN回归模型

X = np.array([[2, 1], [3, 1], [1, 4], [2, 6]])                                  # 特征
y = np.array([0.5, 0.33, 4, 3])                                                 # 标签

knn.fit(X, y)                                                                   # 模型训练

x = np.array([[4, 9]])
x_pred = knn.predict(x)                                                         # 模型预测
print(x_pred)

二、心脏病预测

  Heart Disease 数据集:https://www.kaggle.com/datasets/johnsmith88/heart-disease-dataset

字段名
年龄 连续值
性别 0:女;1:男
胸疼类型 0:典型心绞痛;1:非典型心绞痛;2:非心绞痛;3:无症状
静息血压 连续值,单位 mmHg
胆固醇 连续值,单位 mg/dl
空腹血糖 0:小于等于 120mg/dl;1:大于120mg/dl
静息心电图结果 0:正常;1:ST-T 异常;2:可能左心室肥大
最大心率 连续值
运动性心绞疼 0:无;1:有
运动后的 ST 下降 连续值
峰值 ST 段的斜率 0:向上;1:水平;2:向下
主血管数量 0 到 3
地中海贫血 0:正常;1:固定缺陷;2:可逆缺陷
是否患有心脏病(标签) 0:否;1:是
import os
import joblib
import pandas as pd

import sklearn.model_selection

from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.neighbors import KNeighborsClassifier

# 1. 加载数据集
heart_disease_data = pd.read_csv("assets/data/heart_disease.csv")               # 加载数据集
heart_disease_data.dropna(inplace=True)                                         # 删除缺失值

# 2. 划分数据集
X = heart_disease_data.drop("是否患有心脏病", axis=1)                            # 特征矩阵
y = heart_disease_data["是否患有心脏病"]                                         # 目标向量
X_train, X_test, y_train, y_test = sklearn.model_selection.train_test_split(X, y, test_size=0.3, random_state=42)

# 3.特征转换
# 创建列转换器
preprocessor = ColumnTransformer(
    transformers=[
        # 对数值型特征进行标准化
        ("num", StandardScaler(), ["年龄", "静息血压", "胆固醇","最大心率", "运动后的ST下降", "主血管数量"]),
        # 对分类型特征进行独热编码
        ("cat", OneHotEncoder(drop="first"), ["胸痛类型", "静息心电图结果", "峰值ST段的斜率", "地中海贫血"]),
        # 二值特征不做任何转换
        ("binary", "passthrough", ["性别", "空腹血糖", "运动性心绞痛"]),
    ]
)

X_train = preprocessor.fit_transform(X_train)                                   # 对训练集进行拟合和转换
X_test = preprocessor.transform(X_test)                                         # 对测试集进行转换

# 4. 创建模型
knn = KNeighborsClassifier(n_neighbors=3)                                       # 创建KNN分类器

# 5. 训练模型
knn.fit(X_train, y_train)                                                       # 训练KNN分类器

# 6.模型评估,计算准确率
accuracy = knn.score(X_test, y_test)                                            # 计算模型在测试集上的准确率
print(f"模型准确率: {accuracy:.2f}")                                             # 打印模型准确率

# 7.保存模型
if not os.path.exists("assets/models"):
    os.makedirs("assets/models")

joblib.dump(knn, "assets/models/knn_model")                                     # 保存KNN分类器模型

  当我们使用 joblib.dump() 方法保存之前训练好的模型之后,下一次我们可以使用 joblib.load() 方法加载之前训练好的模型直接使用,不需要重复训练。

import joblib
import pandas as pd

import sklearn.model_selection
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.compose import ColumnTransformer

# 1. 加载数据集
heart_disease_data = pd.read_csv("assets/data/heart_disease.csv")               # 加载数据集
heart_disease_data.dropna(inplace=True)                                         # 删除缺失值

# 2. 划分数据集
X = heart_disease_data.drop("是否患有心脏病", axis=1)                            # 特征矩阵
y = heart_disease_data["是否患有心脏病"]                                         # 目标向量
X_train, X_test, y_train, y_test = sklearn.model_selection.train_test_split(X, y, test_size=0.3, random_state=42)

# 3.特征转换
# 创建列转换器
preprocessor = ColumnTransformer(
    transformers=[
        # 对数值型特征进行标准化
        ("num", StandardScaler(), ["年龄", "静息血压", "胆固醇","最大心率", "运动后的ST下降", "主血管数量"]),
        # 对分类型特征进行独热编码
        ("cat", OneHotEncoder(drop="first"), ["胸痛类型", "静息心电图结果", "峰值ST段的斜率", "地中海贫血"]),
        # 二值特征不做任何转换
        ("binary", "passthrough", ["性别", "空腹血糖", "运动性心绞痛"]),
    ]
)

X_train = preprocessor.fit_transform(X_train)                                   # 对训练集进行拟合和转换
X_test = preprocessor.transform(X_test)                                         # 对测试集进行转换

knn = joblib.load("assets/models/knn_model")                                    # 加载KNN分类器模型
y_pred = knn.predict(X_test)                                                    # 对测试集进行预测
print("预测结果:")
print(y_pred)

print("实际结果:")
print(y_test.values)

print(f"预测索引为11的结果:{y_pred[11]}, 实际索引11的结果:{y_test.values[11]}")    

三、模型评估与超参数调优

  之前的模型我们 KNN 模型的 n_neighbors 参数为一个固定值,但这个值可能不是模型最优的超参数值,我们需要不断调整超参数来进行交叉验证,来选择最优的值。之前我们划分数据集时也是指定比例的划分,同样我们需要随机划分数据集。

  在 sklearn 机器学习库中,提供了网格搜索的超参数调优的方法,通过遍历预定义的超参数组合,找到使模型性能达到最优的参数配置。网格搜索通常嵌套交叉验证,与交叉验证结合以提高调参的可靠性。

  • 外层循环:遍历参数网格中的每个参数组合。
  • 内层循环:对每个参数组合使用交叉验证评估模型性能。
import pandas as pd

import sklearn.model_selection

from sklearn.model_selection import GridSearchCV
from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.neighbors import KNeighborsClassifier

# 1. 加载数据集
heart_disease_data = pd.read_csv("assets/data/heart_disease.csv")               # 加载数据集
heart_disease_data.dropna(inplace=True)                                         # 删除缺失值

# 2. 划分数据集
X = heart_disease_data.drop("是否患有心脏病", axis=1)                             # 特征矩阵
y = heart_disease_data["是否患有心脏病"]                                          # 目标向量
X_train, X_test, y_train, y_test = sklearn.model_selection.train_test_split(X, y, test_size=0.3, random_state=42)

# 3.特征转换
# 创建列转换器
preprocessor = ColumnTransformer(
    transformers=[
        # 对数值型特征进行标准化
        ("num", StandardScaler(), ["年龄", "静息血压", "胆固醇","最大心率", "运动后的ST下降", "主血管数量"]),
        # 对分类型特征进行独热编码
        ("cat", OneHotEncoder(drop="first"), ["胸痛类型", "静息心电图结果", "峰值ST段的斜率", "地中海贫血"]),
        # 二值特征不做任何转换
        ("binary", "passthrough", ["性别", "空腹血糖", "运动性心绞痛"]),
    ]
)

X_train = preprocessor.fit_transform(X_train)                                   # 对训练集进行拟合和转换
X_test = preprocessor.transform(X_test)                                         # 对测试集进行转换

# 4. 创建模型
knn = KNeighborsClassifier()                                                    # 创建KNN分类器

# 5.定义KNN分类器的参数网格
param_grid_dict = {
    "n_neighbors": range(1, 11),
    "weights": ["uniform", "distance"],
    }

# 参数cv:交叉验证折数
grid_search_cv = GridSearchCV(knn, param_grid_dict, cv=10)

# 6. 训练模型
grid_search_cv.fit(X_train, y_train)                                            # 训练KNN分类器

# 7. 模型评估,选择最佳模型
results = pd.DataFrame(grid_search_cv.cv_results_)
print("模型评估结果")
print(results.to_string())

# 直接获取最佳模型和最佳得分
knn = grid_search_cv.best_estimator_
print("最佳模型:", knn)
print("最佳模型参数:", grid_search_cv.best_params_)
print("最佳模型得分:", grid_search_cv.best_score_)

# 8.使用最佳模型进行测试
score = knn.score(X_test, y_test)
print("测试集准确率:", score)
posted @ 2026-01-08 23:07  星光映梦  阅读(0)  评论(0)    收藏  举报