(1)确定算法超参数
(2)从训练集中选择离待预测样本A最近的K个样本
(3)根据这K个样本预测A
闵可夫斯基距离(Minkowski distance):scikit-learn中默认使用的距离度量方式,它是衡量数值点之间距离的一种非常常见的方法。
假设数值点P和Q坐标如下:
那么,闵可夫斯基距离定义为:
以鸢尾花数据集为例,使用KNN算法实现分类预测:
from sklearn.neighbors import KNeighborsClassifier
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report
iris = load_iris()
X, y = iris.data[:, :2], iris.target # 只使用两个特征
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, random_state=0)
knn = KNeighborsClassifier(n_neighbors=3, weights="uniform") # KNN算法,自定k值和权重计算方式
"""
n_neighbors:邻居的数量, 即K值
weights:权重计算方式,可选值为uniform与distance。
uniform:统一权重
distance:距离加权权重
"""
knn.fit(X_train, y_train)
y_hat = knn.predict(X_test)
print(classification_report(y_test, y_hat))
------------------------------------------------
precision recall f1-score support
0 1.00 1.00 1.00 13
1 0.78 0.44 0.56 16
2 0.44 0.78 0.56 9
accuracy 0.71 38
macro avg 0.74 0.74 0.71 38
weighted avg 0.77 0.71 0.71 38
不同的超参数值(K值和权重计算方式),会直接影响模型的分类效果:
from matplotlib.colors import ListedColormap
from itertools import product
# 定义决策边界函数,便于可视化
def plot_decision_boundary(model, X, y):
color = ["r", "g", "b"]
marker = ["o", "v", "x"]
class_label = np.unique(y)
cmap = ListedColormap(color[: len(class_label)])
x1_min, x2_min = np.min(X, axis=0)
x1_max, x2_max = np.max(X, axis=0)
x1 = np.arange(x1_min - 1, x1_max + 1, 0.02)
x2 = np.arange(x2_min - 1, x2_max + 1, 0.02)
X1, X2 = np.meshgrid(x1, x2)
Z = model.predict(np.c_[X1.ravel(), X2.ravel()])
Z = Z.reshape(X1.shape)
plt.contourf(X1, X2, Z, cmap=cmap, alpha=0.5)
for i, class_ in enumerate(class_label):
plt.scatter(x=X[y == class_, 0], y=X[y == class_, 1],
c=cmap.colors[i], label=class_, marker=marker[i])
plt.legend()
plt.figure(figsize=(18, 10))
# 使用product计算weights与ks的笛卡尔积组合,这样就可以使用单层循环取代嵌套循环。
weights = ['uniform', 'distance']
ks = [2, 15]
for i, (w, k) in enumerate(product(weights, ks), start=1):
plt.subplot(2, 2, i)
plt.title(f"K值:{k} 权重:{w}")
knn = KNeighborsClassifier(n_neighbors=k, weights=w) # 不同的超参数分别计算
knn.fit(X, y)
plot_decision_boundary(knn, X_train, y_train)
通过上面的结果可以看出:
使用 GridSearchCV 方法找出效果最好的超参数:
from sklearn.model_selection import GridSearchCV
knn = KNeighborsClassifier()
# 定义需要尝试的超参数组合。
grid = {"n_neighbors": range(1, 11, 1), "weights": ['uniform', 'distance']}
gs = GridSearchCV(estimator=knn, param_grid=grid, scoring="accuracy", n_jobs=-1, cv=5, verbose=10, iid=True)
"""
estimator:评估器,即对哪个模型调整超参数。
param_grid:需要检验的超参数组合,从这些组合中,寻找效果最好的超参数组合。
scoring:模型评估标准
n_jobs:并发数量
cv:交叉验证折数
verbose:输出冗余信息,值越大,输出的信息越多。
"""
gs.fit(X_train, y_train)
print(" 最好的分值:", gs.best_score_)
print(" 最好的超参数组合:", gs.best_params_)
print(" 最好的超参数训练好的模型:", gs.best_estimator_)
------------------------------------------------------
最好的分值: 0.8035714285714286
最好的超参数组合: {'n_neighbors': 7, 'weights': 'uniform'}
最好的超参数训练好的模型: KNeighborsClassifier(algorithm='auto', leaf_size=30, metric='minkowski',
metric_params=None, n_jobs=None, n_neighbors=7, p=2,
weights='uniform')
得到最好的超参数之后,就可以使用最好的超参数训练好后的模型进行预测:
estimator = gs.best_estimator_ # 最好的超参数训练好的模型
y_hat = estimator.predict(X_test)
print(classification_report(y_test, y_hat))
---------------------------------------------
precision recall f1-score support
0 1.00 1.00 1.00 13
1 0.80 0.50 0.62 16
2 0.47 0.78 0.58 9
accuracy 0.74 38
macro avg 0.76 0.76 0.73 38
weighted avg 0.79 0.74 0.74 38
以波士顿房价数据集为例,使用KNN算法实现回归预测:
from sklearn.datasets import load_boston
from sklearn.neighbors import KNeighborsRegressor
from sklearn.linear_model import LinearRegression
X, y = load_boston(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, random_state=0)
knn = KNeighborsRegressor(n_neighbors=3, weights="uniform")
"""
没有使用 GridSearchCV 方法找出最佳超参数,因为 gs.fit(X_train, y_train)中 y_train需要为整数,强行将y_train 转为int类型找出的最佳超参数会不准确,此处不适用。
"""
knn.fit(X_train, y_train)
lr = LinearRegression() # 使用线性回归作为对比
lr.fit(X_train, y_train)
print("KNN算法R^2值:", knn.score(X_test, y_test))
print("线性回归算法R^2值:", lr.score(X_test, y_test))
------------------------------------------------------
KNN算法R^2值: 0.509785478689029
线性回归算法R^2值: 0.6354638433202132
从以上结果可以看出,KNN算法的R²值小于线性回归算法的R²值,效果比线性回归要差很多。但这并不能证明KNN算法是不如线性回归的,这是因为:
使用 StandardScaler 进行均值标准差标准化:
from sklearn.preprocessing import StandardScaler
s = StandardScaler()
X_train_scale = s.fit_transform(X_train)
X_test_scale = s.transform(X_test)
knn = KNeighborsRegressor(n_neighbors=3, weights="uniform")
knn.fit(X_train_scale, y_train)
print("均值标准差标准化之后的KNN算法R^2值:", knn.score(X_test_scale, y_test))
-------------------------------------------
均值标准差标准化之后的KNN算法R^2值: 0.6248800677762865
使用 MinMaxScaler 进行最小最大值标准化:
from sklearn.preprocessing import MinMaxScaler
s = MinMaxScaler()
X_train_scale = s.fit_transform(X_train)
X_test_scale = s.transform(X_test)
knn = KNeighborsRegressor(n_neighbors=3, weights="uniform")
knn.fit(X_train_scale, y_train)
print("最小最大值标准化之后的KNN算法R^2值:", knn.score(X_test_scale, y_test))
---------------------------------------
最小最大值标准化之后的KNN算法R^2值: 0.6177749492293981
从以上结果可以看出,进行数据标准化处理之后,KNN算法效果有了进一步提升。
使用 Pipeline 把上面的均值标准差标准化与训练模型两个步骤视为一个整体,一并执行:
from sklearn.pipeline import Pipeline
X, y = load_boston(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, random_state=0)
steps = [("scaler", StandardScaler()), ("knn", KNeighborsRegressor())]
"""
定义流水线的步骤,类型为一个列表,列表中的每个元素是元组类型。
格式为:[(步骤名1,评估器1), (步骤名2, 评估器2),……, (步骤名n, 评估器n)]
"""
p = Pipeline(steps)
p.set_params(knn__n_neighbors=3, knn__weights="uniform") # 设置流水线的参数。
p.fit(X_train, y_train)
------------------------------------
print("流水线之后的R^2值:", p.score(X_test, y_test))
-------------------------------------------
流水线之后的KNN算法R^2值: 0.6248800677762865
拓展:KD树