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 下载源列表:
- 阿里云 https://mirrors.aliyun.com/pypi/simple
- 中国科技大学 https://pypi.mirrors.ustc.edu.cn/simple
- 清华大学 https://pypi.tuna.tsinghua.edu.cn/simple
- 中国科学技术大学 http://pypi.mirrors.ustc.edu.cn/simple
【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)

浙公网安备 33010602011771号