机器学习–K近邻算法
基本概念
k k k 近邻法 ( k k k-nearest neighbor, k k k-NN) 是一种基于分类与回归的方法。
原理:对于一个实例,根据其 k k k 个最近邻的训练实例的类别,通过多数表决等方式来决定这个实例的类别。
输入:实例的特征向量
输出:实例的类别
三个基本要素: k k k 值的选择、距离度量以及分类决策规划
k k k 近邻算法
输入:训练数据集
T
=
{
(
x
1
,
y
1
)
,
(
x
2
,
y
2
)
,
⋯
,
(
x
n
,
y
n
)
}
T = \{(x_1,y_1),(x_2,y_2), \cdots,(x_n,y_n)\}
T={(x1,y1),(x2,y2),⋯,(xn,yn)}
其中,
x
i
x_i
xi是实例特征向量,
y
i
y_i
yi 是实例的类别。
输出:实例
x
x
x 所属的类别
y
y
y。
- 根据指定的度量,在训练集 T T T 中找出与 x x x 最邻近的 k k k 个点,包含在 N k ( x ) N_k(x) Nk(x) 中。
- 在 N k ( x ) N_k(x) Nk(x) 中根据分类决策规则(如多数表决)决定 x x x 的类别 y y y。
即:
y
=
a
r
g
m
a
x
x
j
∑
x
i
∈
N
k
(
x
)
I
(
y
i
=
c
j
)
,
i
=
1
,
2
,
⋯
,
N
;
j
=
1
,
2
,
⋯
,
K
y=arg\ \mathop{max}\limits_{x_j} \sum_{x_i \in N_k(x)}I(y_i=c_j),i=1,2,\cdots,N;j=1,2,\cdots,K
y=arg xjmaxxi∈Nk(x)∑I(yi=cj),i=1,2,⋯,N;j=1,2,⋯,K
其中,
I
I
I 表示指示函数,当
y
i
=
c
j
y_i=c_j
yi=cj 时
I
=
1
I=1
I=1,否则为0。
k k k 近邻模型
模型由三个基本要素----距离度量、 k k k 值选择和分类决策规则决定。
距离度量
特征空间中两个实例之间的距离可以反映两个实例之间的相似程度。在 k k k 近邻中,一般使用过欧式距离,也可以使用曼哈顿距离或者更一般的 L p L_p Lp 距离。
对于实例
x
i
,
x
j
R
n
x_i, x_j \ R^n
xi,xj Rn,
L
p
L_p
Lp 距离可以定义为:
L
p
(
x
i
,
x
j
)
=
(
∑
l
=
1
n
∣
x
i
(
l
)
−
x
j
(
l
)
∣
p
)
1
p
L_p(x_i,x_j)=(\sum_{l=1}^n\vert x_i^{(l)}-x_j^{(l)}\vert^p)^{\frac{1}{p}}
Lp(xi,xj)=(l=1∑n∣xi(l)−xj(l)∣p)p1
当 p = 2 时称为欧式距离,当 p = 1 时称为曼哈顿距离。
k k k 值的选择
k
k
k 值的选择会对
k
k
k 近邻算法的结果产生巨大的影响。
如果
k
k
k 值过小,训练误差会减小,但测试误差会增大。意味着整体模型变得复杂,容易发生过拟合。
如果
k
k
k 值过大,模型变得更加简单,测试误差会减小,但训练误差会增加。
通常我们使用交叉验证法来选取最优的
k
k
k 值。
分类决策规则
k k k 近邻法中的分类决策规则往往是多数表决,即由周围 k k k 个实例中的类别最多的点来决定输入实例的类别。
实现案例
本章节使用 sklearn 库内置函数实现了基于 k k k 近邻算法的分类(鸢尾花分类和手写数字识别)与回归(波斯顿房价预测)。
k k k 近邻算法实现鸢尾花数据集分类
# coding=utf-8
from sklearn import datasets
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.neighbors import KNeighborsClassifier
if __name__ == '__main__':
# 导入数据
iris = datasets.load_iris()
# 划分数据集
x_train, x_test, y_train, y_test = train_test_split(iris.data, iris.target, test_size=0.3)
# 标准化处理
std = StandardScaler()
x_train = std.fit_transform(x_train)
x_test = std.transform(x_test)
# 创建KNN模型
knn = KNeighborsClassifier(n_neighbors=5, weights="uniform")
# 进行训练
knn.fit(x_train, y_train)
# 进行预测
y_predict = knn.predict(x_test)
print(f"predict accuracy = {knn.score(x_test, y_test)}")
# predict accuracy = 0.9333333333333333
k k k 近邻算法实现手写数字识别
# coding=utf-8
from sklearn import datasets
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.neighbors import KNeighborsClassifier
if __name__ == '__main__':
# 导入数据
digits = datasets.load_digits()
# 数据集划分
x_train, x_test, y_train, y_test = train_test_split(digits.data, digits.target, test_size=0.3)
# 标准化处理
std = StandardScaler()
x_train = std.fit_transform(x_train)
x_test = std.transform(x_test)
# 创建模型
knn = KNeighborsClassifier(n_neighbors=5)
# 训练模型
knn.fit(x_train, y_train)
# 测试模型
knn.predict(x_test)
print(f"prediction accuracy = {knn.score(x_test, y_test)}")
# predict accuracy = 0.9888888888888889
k k k 近邻算法实现波斯顿房价预测
# coding=utf-8
from sklearn import datasets
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.neighbors import KNeighborsClassifier
from sklearn.metrics import r2_score, mean_squared_error, mean_absolute_error
if __name__ == '__main__':
# 导入数据
boston = datasets.load_boston()
# # 数据集划分
x_train, x_test, y_train, y_test = train_test_split(boston.data, boston.target, test_size=0.3)
# 输入要为二维数组
y_train = y_train.reshape(-1, 1)
y_test = y_test.reshape(-1, 1)
# 标准化处理
std = StandardScaler()
x_train = std.fit_transform(x_train)
x_test = std.transform(x_test)
y_train = std.fit_transform(y_train)
y_test = std.transform(y_test)
# 创建模型
knn = KNeighborsClassifier(n_neighbors=3)
# 训练模型
knn.fit(x_train, y_train.astype(int).astype(float))
# Tips: y_train.astype(int).astype(float) 如果不这样会出现:
# ValueError: Unknown label type: 'continuous'
# 测试模型
prediction = knn.predict(x_test)
# 模型评估
print(f"default evolution value = {knn.score(x_test, y_test.astype(int).astype(float))}")
print(f"R_squared value = {r2_score(std.inverse_transform(y_test), std.inverse_transform(prediction))}")
print(f"mean_squared_error value = {mean_squared_error(std.inverse_transform(y_test), std.inverse_transform(prediction))}")
print(f"mean_absolute_error value = {mean_absolute_error(std.inverse_transform(y_test), std.inverse_transform(prediction))}")
# default evolution value = 0.756578947368421
# R_squared value = 0.3205080076834249
# mean_squared_error value = 74.66965266743814
# mean_absolute_error value = 5.753533302051314