1.KNN回归的直观理解:局部平均
KNN回归的核心思想是,相似的数据点在特征空间中彼此靠近 。当需要对新数据点进行预测时,KNN算法会识别出与该新数据点最接近的“k”个邻居,并利用这些已知邻居的目标值来估计新数据点的值 。这本质上是一种局部插值或平滑技术 。通过这种方式,KNN假设局部区域内的点具有相似的属性,因此可以根据其邻居的平均值来预测新点的值。
2.权重规则
2.1 简单平均(加权平均)
简单平均原则:KNN回归中最常用的方法是计算“k”个最近邻居目标值的简单算术平均值 。
公式:\(\frac{1}{k}∑_{j=1}^ky_{NN(x_0,j)}\)
- k 是最近邻居的数量。
- \(y_{NN(x_0,j)}\)是 \(x_0\) 的第 j 个最近邻居的目标值。
2.2 加权平均
加权平均:通过距离增强预测:为了使更近的邻居对预测产生更大的影响,可以使用加权平均 。一种常见的加权方案是逆距离加权,其中权重与到邻居的距离成反比(例如,\(\frac{1}{d}\)或 \(\frac{1}{d^2}\)) 。
公式:\(\frac{∑_{j=1}^kw_j · y_{NN(x_0,j)}}{∑_{j=1}^kw_j}\)
虽然简单的逆距离加权很常见,但存在一种更深层的数学基础,可以推导出一种最优的加权方案。这种最优方案旨在平衡与权重方差相关的项(\(s_n^2\))和与邻居距离引起的偏差相关的项(\(t_n^2\)),从而最小化整体预测风险 。这意味着加权的选择不仅仅是启发式的,而是基于最小化预测误差的理论基础,将简单的平均方法提升到更复杂的风险最小化方法。这一概念也将KNN与核密度估计器联系起来 。
3.案例1
KNeighborsRegressor
是scikit-learn
中用于KNN回归的类 。
关键参数:
-
n_neighbors
:K值(默认值:5) 。 -
weights
:'uniform'
(默认,所有邻居权重相等)或'distance'
(越近的邻居影响越大) 。也可以是可调用函数。 -
algorithm
:'auto'
、'ball_tree'
、'kd_tree'
、'brute'
。'auto'
会尝试选择最合适的算法。 -
metric
:'minkowski'
(默认,p=2时为欧几里得)、'euclidean'
、'manhattan'
。
注意,在训练之前需要进行特征缩放。
因为KNN完全依赖于距离计算 。如果特征具有不同的尺度(例如,年龄以年为单位,而收入以美元为单位),则范围较大的特征将在距离计算中占据不成比例的主导地位,导致结果出现偏差 。缩放可确保所有特征对结果的贡献均等 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.neighbors import KNeighborsRegressor
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import mean_squared_error
# 示例数据(请替换为您的实际数据)
X = np.random.rand(80) * 10 # 0-10的小数80个
y = np.sin(X)
# 添加噪声
X[::4] += np.random.randn(20)
y[::4] += np.random.randn(20)
# display(X,y)
# 划分数据
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
# 特征缩放(对KNN至关重要)
# StandardScaler 要求输入数据是二维数组(形状为 (n_samples, n_features)),
# 但 X_train 和 X_test 是一维数组,因此需要 reshape(-1, 1)。
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train.reshape(-1, 1))
X_test_scaled = scaler.transform(X_test.reshape(-1, 1))
# 初始化并拟合模型
knn_model = KNeighborsRegressor(n_neighbors=3) # 示例 k=3,默认是5
knn_model.fit(X_train_scaled, y_train)
# 进行预测
y_pred = knn_model.predict(X_test_scaled)
# 评估
rmse = np.sqrt(mean_squared_error(y_test, y_pred))
print(f"Basic KNN RMSE: {rmse}") #Basic KNN RMSE: 1.045599338489518 误差很高,因为实际y的取值范围是-1, 1,
现在为了找到最合适的k值:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
import numpy as np
import matplotlib.pyplot as plt
from sklearn.neighbors import KNeighborsRegressor
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import mean_squared_error
# 设置随机种子以确保结果可复现
np.random.seed(42)
# 生成示例数据
X = np.random.rand(80) * 10 # 0-10 的随机小数,80 个
y = np.sin(X)
# 添加噪声
X[::4] += np.random.randn(20)
y[::4] += np.random.randn(20)
# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
# 特征缩放
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train.reshape(-1, 1))
X_test_scaled = scaler.transform(X_test.reshape(-1, 1))
# 尝试 k 从 1 到 20,计算 RMSE
k_values = range(1, 25)
rmse_values = []
for k in k_values:
# 初始化并拟合 KNN 回归模型
knn_model = KNeighborsRegressor(n_neighbors=k)
knn_model.fit(X_train_scaled, y_train)
# 预测
y_pred = knn_model.predict(X_test_scaled)
# 计算 RMSE
rmse = np.sqrt(mean_squared_error(y_test, y_pred))
rmse_values.append(rmse)
print(f"k={k}, RMSE={rmse:.5f}")
# 找到最小 RMSE 和对应的 k
min_rmse = min(rmse_values)
best_k = k_values[rmse_values.index(min_rmse)]
print(f"\n最佳 k={best_k}, 最小 RMSE={min_rmse:.5f}")
# 绘制 k 与 RMSE 的关系图
plt.figure(figsize=(10, 6))
plt.plot(k_values, rmse_values, marker='o', linestyle='-', color='b', label='RMSE')
plt.axvline(x=best_k, color='r', linestyle='--', label=f'Best k={best_k}')
plt.xlabel('k (Number of Neighbors)')
plt.ylabel('RMSE')
plt.title('RMSE vs. k in KNN Regression')
plt.grid(True)
plt.legend()
plt.show()
也可以进行交叉验证:
1
2
3
4
5
6
7
8
9
10
from sklearn.model_selection import cross_val_score
k_values = range(1, 21)
cv_rmse = []
for k in k_values:
knn_model = KNeighborsRegressor(n_neighbors=k)
scores = cross_val_score(knn_model, X_train_scaled, y_train, cv=5, scoring='neg_mean_squared_error')
cv_rmse.append(np.sqrt(-scores.mean()))
print(f"k={k}, CV RMSE={cv_rmse[-1]:.5f}")
best_k_cv = k_values[np.argmin(cv_rmse)]
print(f"最佳 k (交叉验证)={best_k_cv}, 最小 CV RMSE={min(cv_rmse):.5f}")
其实上面这个案例非常简单,所以可以不需要特征缩放。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.neighbors import KNeighborsRegressor
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import mean_squared_error
# 示例数据(请替换为您的实际数据)
X = np.random.rand(80) * 10 # 0-10的小数80个
y = np.sin(X)
# 添加噪声
X[::4] += np.random.randn(20)
y[::4] += np.random.randn(20)
# display(X,y)
# 划分数据
# X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
# 特征缩放(对KNN至关重要)
# StandardScaler 要求输入数据是二维数组(形状为 (n_samples, n_features)),
# 但 X_train 和 X_test 是一维数组,因此需要 reshape(-1, 1)。
# scaler = StandardScaler()
# X_train_scaled = scaler.fit_transform(X_train.reshape(-1, 1))
# X_test_scaled = scaler.transform(X_test.reshape(-1, 1))
# 初始化并拟合模型
knn_model = KNeighborsRegressor(n_neighbors=3) # 示例 k=3,默认是5
knn_model.fit(X.reshape(-1, 1), y)
# 进行预测
X_test =np.linspace(0, 20, 100).reshape(-1, 1)
y_pred = knn_model.predict(X_test.reshape(-1, 1))
# 评估
rmse = np.sqrt(mean_squared_error(np.sin(X_test), y_pred))
print(f"Basic KNN RMSE: {rmse}") #Basic KNN RMSE: 1.045599338489518 误差很高,因为实际y的取值范围是-1, 1,
# 可视化效果
plt.scatter(X, y)
plt.plot(X_test, y_pred, c="red")
plt.plot(X_test, np.sin(X_test), c="green")
红色的是预测的线,绿色的是正弦线。
注意:回归的评估指标不是准确率. 默认的回归评估指标是R方 R^2
knn.score(X_train.reshape(-1, 1), y_train)
和knn.score(X_test.reshape(-1, 1), y_test)
决定系数R方
R²(R-squared,决定系数)是回归任务中常用的评估指标,用于衡量模型对目标变量变异的解释能力。它的定义为:
\[R^2=1-\frac{SS_{res}}{SS_{tot}}\]其中:
- \(SS_{res}\)(残差平方和,Sum of Squared Residuals):预测值与真实值之间差的平方和,衡量模型的预测误差:
- \(SS_{tot}\)(总平方和,Total Sum of Squares):目标变量与均值的差的平方和,衡量目标变量的总变异:
R² 表示模型解释的目标变量变异的比例。
- 范围:
4.使用GridSearchCV
进行超参数调优
GridSearchCV
系统地搜索超参数的最佳组合 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from sklearn.model_selection import GridSearchCV
# 定义参数网格
parameters = {
"n_neighbors": range(1, 10), # k的示例范围
"weights": ["uniform", "distance"],
"metric": ["euclidean", "manhattan"]
}
# 初始化GridSearchCV
grid_search = GridSearchCV(KNeighborsRegressor(), parameters, cv=5, scoring='neg_mean_squared_error') # cv=5表示5折交叉验证
# 拟合训练数据
grid_search.fit(X_train_scaled, y_train)
# 获取最佳参数和最佳分数
print(f"Best parameters: {grid_search.best_params_}")
best_knn_model = grid_search.best_estimator_
# 评估最佳模型
y_pred_tuned = best_knn_model.predict(X_test_scaled)
rmse_tuned = np.sqrt(mean_squared_error(y_test, y_pred_tuned))
print(f"Tuned KNN RMSE: {rmse_tuned}")
比如还是上面的例子,sin函数的回归:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
from sklearn.model_selection import GridSearchCV
import numpy as np
import matplotlib.pyplot as plt
from sklearn.neighbors import KNeighborsRegressor
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import mean_squared_error
# 设置随机种子以确保结果可复现
np.random.seed(42)
# 生成示例数据
X = np.random.rand(80) * 10 # 0-10 的随机小数,80 个
y = np.sin(X)
# 添加噪声
X[::4] += np.random.randn(20)
y[::4] += np.random.randn(20)
# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
# 特征缩放
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train.reshape(-1, 1))
X_test_scaled = scaler.transform(X_test.reshape(-1, 1))
# 定义参数网格
parameters = {
"n_neighbors": range(1, 10), # k的示例范围
"weights": ["uniform", "distance"],
"metric": ["euclidean", "manhattan"]
}
# 初始化GridSearchCV
grid_search = GridSearchCV(KNeighborsRegressor(), parameters, cv=5, scoring='neg_mean_squared_error') # cv=5表示5折交叉验证
# 拟合训练数据
grid_search.fit(X_train_scaled, y_train)
# 获取最佳参数和最佳分数
print(f"Best parameters: {grid_search.best_params_}")
best_knn_model = grid_search.best_estimator_
# 评估最佳模型
y_pred_tuned = best_knn_model.predict(X_test_scaled)
rmse_tuned = np.sqrt(mean_squared_error(y_test, y_pred_tuned))
print(f"Tuned KNN RMSE: {rmse_tuned}")
运行结果是:
1
2
Best parameters: {'metric': 'euclidean', 'n_neighbors': 9, 'weights': 'distance'}
Tuned KNN RMSE: 0.6704118572445534
5.案例2-人类动作识别
步行,上楼,下楼,坐着,站立和躺着
数据采集每个人在腰部穿着智能手机,进行了六个活动(步行,上楼,下楼,坐着,站立和躺着)。采用嵌入式加速度计和陀螺仪,以50Hz的恒定速度捕获3轴线性加速度和3轴角速度,来获取数据
【1】导入数据与简单分类(默认k = 5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 在 Python 中,.npy 文件是 NumPy 用于存储数组数据的二进制文件格式,广泛用于保存和加载 NumPy 数组。
# 读取 .npy 文件非常简单,主要是使用 NumPy 提供的 np.load 函数。
import numpy as np
import pandas as pd
X_test = np.load("../data/动作分析/x_test.npy")
y_test = np.load("../data/动作分析/y_test.npy")
X_train = np.load("../data/动作分析/x_train.npy")
y_train = np.load("../data/动作分析/y_train.npy")
print(X_train.shape, y_train.shape, X_test.shape, y_test.shape)
display(pd.DataFrame(X_train).head(5), pd.DataFrame(y_train).head(5))
from sklearn.neighbors import KNeighborsClassifier
from sklearn.metrics import accuracy_score
knn = KNeighborsClassifier()
knn.fit(X_train, y_train)
y_pred = knn.predict(X_test)
acc = accuracy_score(y_test, y_pred)
print(acc) # 0.9015948422124194
【2】寻找最佳的k值:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt
# 存储不同K值对应的准确率
k_values = range(1, 21)
accuracies = []
for k in k_values:
# 实例化KNN分类器
knn = KNeighborsClassifier(n_neighbors=k)
# 拟合模型
knn.fit(X_train, y_train)
# 预测并计算准确率
y_pred = knn.predict(X_test)
accuracy = accuracy_score(y_test, y_pred)
accuracies.append(accuracy)
# 绘制K值与准确率的关系图
plt.figure(figsize=(10, 6))
plt.plot(k_values, accuracies, marker='o')
plt.title('K值与准确率')
plt.xlabel('K值')
plt.ylabel('准确率')
plt.xticks(k_values)
plt.grid(True)
plt.show()
# 找到准确率最高的K值
best_k_index = np.argmax(accuracies)
best_k = k_values[best_k_index]
best_accuracy = accuracies[best_k_index]
print(f"最佳K值: {best_k}, 对应的准确率: {best_accuracy:.2f}")
运行三次,得到最佳的k值都是8.
【3】KNN 决策边界可视化:
注意X_train
原先维度是(7352, 561)
,这样的维度不适合绘图(绘图须降至2-3维)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
from sklearn.decomposition import PCA
knn_best=KNeighborsClassifier(n_neighbors=8)
# 使用 PCA 降维到 2 维
pca = PCA(n_components=2)
X_train_2d = pca.fit_transform(X_train) # 将训练数据降维
X_test_2d = pca.transform(X_test) # 将测试数据降维(如果有)
# 2. 使用降维后的数据重新训练 KNN 模型
knn_best = KNeighborsClassifier(n_neighbors=8)
knn_best.fit(X_train_2d, y_train)
# 拼接降维后的数据
X_2d = np.concatenate((X_train_2d, X_test_2d), axis=0)
y = np.concatenate((y_train, y_test), axis=0)
# 重新计算 x_min, x_max, y_min, y_max
x_min, x_max = X_2d[:, 0].min() - 0.5, X_2d[:, 0].max() + 0.5
y_min, y_max = X_2d[:, 1].min() - 0.5, X_2d[:, 1].max() + 0.5
display(x_min, x_max, y_min, y_max)
# 3. 创建密集网格点
# np.meshgrid() 用于生成一个二维网格,就像背景中的像素点
xx, yy = np.meshgrid(np.arange(x_min, x_max, 0.02),
np.arange(y_min, y_max, 0.02))
# 4. 对网格中的所有点进行预测
# np.c_[xx.ravel(), yy.ravel()] 将网格点从二维展平为一维,并组合成(x,y)坐标对
# knn_best.predict() 对这些坐标进行批量预测,得到每个点的类别
Z = knn_best.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape) # 将预测结果的形状恢复成网格的形状
# display(Z)
# 5. 绘制决策边界和原始数据点
plt.figure(figsize=(12, 8))
# plt.pcolormesh() 绘制彩色区域,即决策边界
# 颜色代表了KNN模型对该区域内任何点的预测类别
# plt.pcolormesh(xx, yy, Z, cmap=plt.cm.Spectral, alpha=0.3)
plt.pcolormesh(xx, yy, Z, cmap=plt.cm.Spectral, alpha=0.3)
# plt.scatter() 绘制原始数据点,并用颜色表示其真实类别
# 这样我们可以对比决策边界与实际数据点的关系
# plt.scatter(X[:, 0], X[:, 1], c=y, cmap=plt.cm.Spectral, edgecolors='k')
plt.scatter(X_2d[:, 0], X_2d[:, 1], c=y, cmap=plt.cm.Spectral, edgecolors='k')
plt.title(f'KNN 决策边界 (最佳K值: {best_k})', fontsize=16)
plt.xlabel('特征 1', fontsize=12)
plt.ylabel('特征 2', fontsize=12)
plt.xlim(x_min, x_max)
plt.ylim(y_min, y_max)
plt.show()
6.案例3-预测年收入是否大于50K美元
取全部列作为特征
读取adult.txt
文件,最后一列是年收入,并使用KNN算法训练模型,然后使用模型预测一个人的年收入是否大于50
训练过程中碰到了问题。(需要对非数值型列进行编码:独热编码/标签编码,参考博客:机器学习常见错误:scikit-learn 模型直接处理的非数值型数据)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
import pandas as pd
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import LabelEncoder
# 1. 读取数据
# 使用 header=None 让 Pandas 自动分配整数列名
# 确保 skipinitialspace=True 处理数据中的前导空格
df = pd.read_csv("../data/adults.txt", delimiter=",", header=None, skipinitialspace=True)
# 2. 找出所有非数值列
# 注意:这里我们使用 df.select_dtypes() 来自动识别所有'object'类型的列
# 它们通常是字符串列
categorical_cols = df.select_dtypes(include=['object']).columns
# 3. 对所有非数值特征进行独热编码
# pd.get_dummies 会自动创建新的列
df_processed = pd.get_dummies(df, columns=categorical_cols, dtype=int)
# 4. 划分特征和标签
# 在原始数据中,最后一列是标签。在独热编码后,标签也可能被编码。
# 让我们假设原始的最后一列就是标签,并且它被独热编码了。
# 我们可以找到所有由原始最后一列独热编码而来的列。
# 为了简化,我们直接取最后一列作为标签,但请注意,如果原始标签有多个类别,这可能会有问题。
# 更好的方法是使用 LabelEncoder。
# 假设最后一列是原始标签
y_original = df.iloc[:, -1]
le = LabelEncoder()
y_encoded = le.fit_transform(y_original)
# 移除原始标签列,得到特征矩阵 X
X = df_processed.drop(y_original.name, axis=1, errors='ignore')
# 5. 划分训练集和测试集
# 使用 train_test_split,设置 test_size=0.2,表示20%的数据作为测试集
# random_state=42 可以确保每次划分的结果都是一样的
X_train, X_test, y_train, y_test = train_test_split(X, y_encoded, test_size=0.2, random_state=42)
# 6. 训练模型
knn = KNeighborsClassifier()
knn.fit(X_train, y_train)
print("模型训练成功!")
print(f"训练集大小: {X_train.shape[0]} 条数据")
print(f"测试集大小: {X_test.shape[0]} 条数据")
# 7. (可选) 在测试集上进行预测和评估
from sklearn.metrics import accuracy_score
y_pred = knn.predict(X_test)
accuracy = accuracy_score(y_test, y_pred)
print(f"模型在测试集上的准确率: {accuracy:.2f}")
取几列作为特征
获取年龄、教育程度、职位、每周工作时间作为机器学习数据
获取薪水作为对应结果
1
2
3
4
X = df.loc[1:, [0, 3, 6, 12]]
X = X.rename(columns={0:"age",3:"education", 6:"occupation", 12:"hours_per_week"})
y = df.loc[1:, [14]]
y = y.rename(columns = {14:"salary"})
数据转换,将String类型数据转换为int
【知识点】map方法,进行数据转换
首先看一下X中非数值型列的情况:
1
display(X["education"].value_counts(), X["occupation"].value_counts())
由于 KNN 基于距离计算,它会把数值大小当作距离的一部分。这意味着,如果你的映射是 'HS-grad'
-> 0, 'Bachelors'
-> 1, 'Masters'
-> 2,那么 KNN 就会认为 'HS-grad'
和 'Bachelors'
之间的“距离”是 1,而 'HS-grad'
和 'Masters'
之间的“距离”是 2。这在数据中是有意义的,因为教育水平确实存在一个自然顺序。
值得注意的是,adults.txt
数据集通常包含一个名为 education-num
的列,它就是对 education
列的数值化表示。这个列已经帮我们完成了这个有序映射,可以直接使用。
occupation
(职业)是一个典型的无序分类变量。'Prof-specialty'
和 'Craft-repair'
之间没有一个自然的“谁比谁高”的顺序。直接使用标签编码('Prof-specialty'
-> 0, 'Craft-repair'
-> 1)会引入虚假的距离关系,这可能会损害 KNN 模型的性能。
1
2
3
4
5
6
7
8
9
10
11
# 换成education_num列了
X = df.loc[1:, [0, 4, 6, 12]]
X = X.rename(columns={0:"age",4:"education", 6:"occupation", 12:"hours_per_week"})
y = df.loc[1:, [14]]
y = y.rename(columns = {14:"salary"})
display(X)
# 计算每个职业的频率
occupation_counts = X["occupation"].value_counts(normalize=True)
# 创建频率映射字典
occupation_frequency_mapping = occupation_counts.to_dict()
X['occupation'] = X['occupation'].map(occupation_frequency_mapping)
切片:训练数据和预测数据
1
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
生成算法,训练数据,预测数据:
1
2
3
4
5
6
knn = KNeighborsClassifier() # 默认k=5
knn.fit(X_train, y_train)
y_pred = knn.predict(X_test)
accuracy = accuracy_score(y_test, y_pred)
print(f"模型在测试集上的准确率: {accuracy:.2f}")
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# 存储不同K值对应的准确率
k_values = range(1, 21)
accuracies = []
for k in k_values:
# 实例化KNN分类器,n_neighbors为当前的k值
knn = KNeighborsClassifier(n_neighbors=k)
# 使用训练数据拟合模型
knn.fit(X_train, y_train)
# 使用测试数据进行预测
y_pred = knn.predict(X_test)
# 计算并存储准确率
accuracy = accuracy_score(y_test, y_pred)
print(f"k={k}, 准确率是:{accuracy}")
accuracies.append(accuracy)
# 绘制K值与准确率的关系图
plt.figure(figsize=(10, 6))
plt.plot(k_values, accuracies, marker='o')
plt.title('K值与准确率')
plt.xlabel('K值')
plt.ylabel('准确率')
plt.xticks(k_values)
plt.grid(True)
plt.show()
# 找到准确率最高的K值
best_k_index = np.argmax(accuracies)
best_k = k_values[best_k_index]
best_accuracy = accuracies[best_k_index]
print(f"\n最佳K值: {best_k}, 对应的准确率: {best_accuracy:.2f}")
保存超参数训练之后最佳的模型:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
import joblib
# 如果 y_train 和 y_test 是 Pandas DataFrame 或 Series,
# 使用 .values.ravel() 确保其为一维的numpy数组。
y_train_flat = y_train.values.ravel() if isinstance(y_train, (pd.Series, pd.DataFrame)) else y_train.ravel()
y_test_flat = y_test.values.ravel() if isinstance(y_test, (pd.Series, pd.DataFrame)) else y_test.ravel()
# 存储不同K值对应的准确率
k_values = range(1, 21)
accuracies = []
# 初始化最佳模型和最佳准确率
best_model = None
best_accuracy = 0.0
best_k = 0
for k in k_values:
# 实例化KNN分类器,n_neighbors为当前的k值
knn = KNeighborsClassifier(n_neighbors=k)
# 使用一维的y_train进行拟合,消除警告
knn.fit(X_train, y_train_flat)
# 使用测试数据进行预测
y_pred = knn.predict(X_test)
# 计算并存储准确率
accuracy = accuracy_score(y_test_flat, y_pred) # 评估时也使用一维的y_test
print(f"k={k}, 准确率是:{accuracy}")
accuracies.append(accuracy)
# 检查当前模型是否是最好的模型
if accuracy > best_accuracy:
best_accuracy = accuracy
best_model = knn
best_k = k
# 绘制K值与准确率的关系图
plt.figure(figsize=(10, 6))
plt.plot(k_values, accuracies, marker='o')
plt.title('K值与准确率')
plt.xlabel('K值')
plt.ylabel('准确率')
plt.xticks(k_values)
plt.grid(True)
plt.show()
print(f"\n最佳K值: {best_k}, 对应的准确率: {best_accuracy:.2f}")
# 保存最佳模型
if best_model is not None:
model_filename = f'best_knn_k{best_k}_model.joblib'
joblib.dump(best_model, model_filename)
print(f"最佳模型已保存到文件: {model_filename}")
# 直接打印模型实例,会显示所有超参数
print(best_model)
# 也可以访问具体的超参数
print(f" - 最佳K值 (n_neighbors): {best_model.n_neighbors}")
print(f" - 权重 (weights): {best_model.weights}")
print(f" - 距离度量 (metric): {best_model.metric}")
7.案例4-小麦种类预测
读取seeds.tsv文件,最后一列是小麦品种,其他列是小麦特征
在 Python 中,使用 pandas 读取 TSV(Tab-Separated Values,制表符分隔值)文件非常简单,可以通过 pandas.read_csv()
或 pandas.read_table()
函数实现,因为 TSV 文件本质上是使用制表符(\t
)作为分隔符的文本文件。以下是详细说明和示例代码:
1
df = pd.read_csv("../data/seeds.tsv", delimiter="\t")
除了最后一列是非数值型列,其余都是数值型。
1
2
y = df.iloc[:, -1]
y.value_counts()
标签有3类。
注意到,没有列名,所以读取数据需要制定header是None:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
df = pd.read_csv("../data/seeds.tsv", delimiter="\t", header=None)
display(df)
y = df.iloc[:, -1].values
X = df.iloc[:, :7].values
# 分出测试集和数据集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
# 准备KNN分类器 训练,寻找最佳的k值
# 存储不同K值对应的准确率
k_values = range(1, 21)
accuracies = []
for k in k_values:
# 实例化KNN分类器,n_neighbors为当前的k值
knn = KNeighborsClassifier(n_neighbors=k, weights="distance")
# 使用训练数据拟合模型
knn.fit(X_train, y_train)
# 使用测试数据进行预测
y_pred = knn.predict(X_test)
# 计算并存储准确率
accuracy = accuracy_score(y_test, y_pred)
print(f"k={k}, 准确率是:{accuracy}")
accuracies.append(accuracy)
# 绘制K值与准确率的关系图
plt.figure(figsize=(10, 6))
plt.plot(k_values, accuracies, marker='o')
plt.title('K值与准确率')
plt.xlabel('K值')
plt.ylabel('准确率')
plt.xticks(k_values)
plt.grid(True)
plt.show()
# 找到准确率最高的K值
best_k_index = np.argmax(accuracies)
best_k = k_values[best_k_index]
best_accuracy = accuracies[best_k_index]
print(f"\n最佳K值: {best_k}, 对应的准确率: {best_accuracy:.2f}")
8.案例5-改进约会网站的匹配效果
读取datingTestSet.txt文件,最后一列是喜欢程度。模型:根据前几列的信息,预测喜欢程度
1
2
df = pd.read_csv("../data/datingTestSet.txt", delimiter="\t", header=None)
df
看一下最后一列的取值情况:
1
2
3
X = df.iloc[:, :3]
y = df.iloc[:, -1]
y.value_counts()
创建KNN分类器,进行训练和预测:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
df = pd.read_csv("../data/datingTestSet.txt", delimiter="\t", header=None)
X = df.iloc[:, :3].values
y = df.iloc[:, -1].values
# 分出测试集和数据集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# 准备KNN分类器 训练,寻找最佳的k值
# 存储不同K值对应的准确率
k_values = range(1, 25)
accuracies = []
for k in k_values:
# 实例化KNN分类器,n_neighbors为当前的k值
knn = KNeighborsClassifier(n_neighbors=k, weights="distance")
# 使用训练数据拟合模型
knn.fit(X_train, y_train)
# 使用测试数据进行预测
y_pred = knn.predict(X_test)
# 计算并存储准确率
accuracy = accuracy_score(y_test, y_pred)
print(f"k={k}, 准确率是:{accuracy}")
accuracies.append(accuracy)
# 绘制K值与准确率的关系图
plt.figure(figsize=(10, 6))
plt.plot(k_values, accuracies, marker='o')
plt.title('K值与准确率')
plt.xlabel('K值')
plt.ylabel('准确率')
plt.xticks(k_values)
plt.grid(True)
plt.show()
# 找到准确率最高的K值
best_k_index = np.argmax(accuracies)
best_k = k_values[best_k_index]
best_accuracy = accuracies[best_k_index]
print(f"\n最佳K值: {best_k}, 对应的准确率: {best_accuracy:.2f}")