简介
KNN(K Nearest Neighbors)
- 可用于分类问题也可用于回归问题
- 分类问题和回归问题又划分为是否带权重
举例
介绍
- 已经有的类别红色的三角形和蓝色的正方形
- 新输入的这个绿色点应该属于什么类型(红色的三角形还是蓝色的正方形)
- 当K=3时,找到1个蓝色的正方形和2个红色的三角形,少数服从多数,认为新输入的绿色点是红色三角形类别
- 极端情况,当K=1时,距离新输入的绿色点最近的那个图形是什么形状,新输入的绿色点就是什么形状
- 极端情况,当K=训练集样本数时,整个训练集样本那个类别的图形数量最多,新输入的绿色点就属于这个形状
原理
KNN(K Nearest Neighbors)
- K个最近的邻居算法
- 先找到K个最近的邻居,再少数服从多数
- 新的输入实例,在训练数据集中找到与该实例最相似(邻近)的K个实例,这K个实例的多数属于某个类别,就把该输入实例分类到这个类别
相似度衡量
similarity
- 相似度通过距离衡量
- 越相似,代表输入实例与训练实例之间的距离越近
距离定义
设特征空间
X
X
X是m维实数向量空间
R
n
R^n
Rn,
x
i
,
x
j
∈
X
x_i,x_j\in X
xi,xj∈X,
x
i
=
(
x
i
(
1
)
,
x
i
(
2
)
,
.
.
.
,
x
i
(
m
)
)
T
x_i=(x_i^{(1)},x_i^{(2)},...,x_i^{(m)})^T
xi=(xi(1),xi(2),...,xi(m))T,
x
j
=
(
x
j
(
1
)
,
x
j
(
2
)
,
.
.
.
,
x
j
(
m
)
)
T
x_j=(x_j^{(1)},x_j^{(2)},...,x_j^{(m)})^T
xj=(xj(1),xj(2),...,xj(m))T
- x i , x j x_i,x_j xi,xj的 L p L_p Lp距离: L p ( x i , x j ) = ( ∑ l = 1 m ∣ x i ( l ) − x j ( l ) ∣ p ) 1 p L_p(x_i,x_j)=\Big(\sum\limits_{l=1}^m|x_i^{(l)}-x_j^{(l)}|^p\Big)^{\frac{1}{p}} Lp(xi,xj)=(l=1∑m∣xi(l)−xj(l)∣p)p1
- p = 2 p=2 p=2欧式距离: L 2 ( x i , x j ) = ( ∑ l = 1 m ∣ x i ( l ) − x j ( l ) ∣ 2 ) 1 2 L_2(x_i,x_j)=\Big(\sum\limits_{l=1}^m|x_i^{(l)}-x_j^{(l)}|^2\Big)^{\frac{1}{2}} L2(xi,xj)=(l=1∑m∣xi(l)−xj(l)∣2)21
K值确定
- 根据举例可知,K=1和K=训练集样本数时都不是合适的K值,那么如何确定1个合适大小的K值
- 在应用中,K值一般取一个比较小的数值,通常采用交叉验证法来选取最优的K值。
代码
介绍
- 使用sklearn提供的数字数据集,它由1797幅手写数字图像组成,每个数字由8x8的像素值向量表示。
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import load_digits
from sklearn.model_selection import train_test_split
np.random.seed(1)
def get_data_set():
# 数字数据集,每个数字由8x8的像素点组成
digits = load_digits()
# data:8x8像素的图像;target:图像代表的数字
X, y = digits.data, digits.target
# 样例展示 0~9的图片数字
# figure width=10, height=8》在figure上添加子图,10个axes,每个横轴跨度=2,纵轴跨度=5
# 可知pyplot的使用:figure》figure上axes(可以画多个)》在axes上画图
fig = plt.figure(figsize=(10, 8))
for i in range(10):
# 将坐标轴添加到fig中 rows=2,column=5,index=i+1:表示从左到右边从上到下第几个ax
ax = fig.add_subplot(2, 5, i + 1)
# 在axes上展示图片,imshow=imageshow,cmap=Colormap
plt.imshow(X[i].reshape((8, 8)), cmap='gray')
plt.show()
# 划分数据集为训练集和测试集
# 例如:X_train.shape=(1347,64); y_train.shape=(1347,); X_test.shape=(450,64); y_test.shape=(450,)
X_train, X_test, y_train, y_test = train_test_split(X, y)
print('X_train.shape=', X_train.shape)
print('y_train.shape=', y_train.shape)
print('X_test.shape=', X_test.shape)
print('y_test.shape=', y_test.shape)
data_set = DataSet(X_train, y_train, X_test, y_test)
return data_set
class DataSet(object):
"""
X_train 训练集样本
y_train 训练集样本值
X_test 测试集样本
y_test 测试集样本值
"""
def __init__(self, X_train, y_train, X_test, y_test):
self.X_train = X_train
self.y_train = y_train
self.X_test = X_test
self.y_test = y_test
class K_NN():
"""
k-nearest-neighbor 类
"""
def __init__(self, X, y):
"""
:param X: X_train
:param y: y_train
"""
self.X = X
self.y = y
def euclidean_distance(self, X):
"""
X和X_train的欧式距离
"""
# X.shape结果(n,64)即n_samples=n
print("X.shape=", X.shape)
m, _ = X.shape
# axis=1,行求和;axis=0,列求和
# L2是(n,1)的矩阵
L2 = [np.sqrt(np.sum((self.X - X[i]) ** 2, axis=1)) for i in range(m)]
# array转ndarray
return np.array(L2)
def hypothesis(self, X, k=1):
"""
X:待预测数据,矩阵
k:距离X最近的k个对象
"""
# step1:计算欧式距离
dists = self.euclidean_distance(X)
# step 2: 找到k个最近的邻居和这些邻居所属的类别
# 每列从小到达排序,再取每列的k个元素的下标
idxk = np.argsort(dists)[:, :k]
print("idxk.shape=", idxk.shape)
# y_idxk是矩阵(n,k)
y_idxk = self.y[idxk]
print("y_idxk.shape=", y_idxk.shape)
if k == 1:
# 切换成行向量,便于展示
return y_idxk.T
else:
m, _ = X.shape
# y_idxk是数组(n,k)》max_votes是数组(n,1)
# 投票 key是1个匿名函数,参数为y_idxk》统计y_idxk[i]中每个元素出现的次数》max则返回出现次数最多的元素(整个过程就是少数服从多数)
max_votes = [max(y_idxk[i], key=list(y_idxk[i]).count) for i in range(m)]
return max_votes
def evaluate_model(knn, X_test, y_test):
y_p_test1 = knn.hypothesis(X_test, k=1)
test_acc1 = np.sum(y_p_test1[0] == y_test) / len(y_p_test1[0]) * 100
print("k=1时,测试精度:", test_acc1)
print("---------------------")
y_p_test3 = knn.hypothesis(X_test, k=3)
test_acc3 = np.sum(y_p_test3 == y_test) / len(y_p_test3) * 100
print("k=3时,测试精度:", test_acc3)
print("---------------------")
y_p_test5 = knn.hypothesis(X_test, k=5)
test_acc5 = np.sum(y_p_test5 == y_test) / len(y_p_test5) * 100
print("k=5时,测试精度:", test_acc5)
print("---------------------")
def show_result(knn, data_set):
"""
展示训练结果
"""
print("k=1,1个最近邻居")
# data_set.X_test[0]是tuple类型
n = data_set.X_test[0].shape[0]
# data_set.X_test[0].reshape(-1,n)将(64,)转为(1,64)矩阵
print("预测类别:", knn.hypothesis(data_set.X_test[0].reshape(-1, n), k=1))
print("真实类别:", data_set.y_test[0])
print("---------------------")
print("k=5,5个最近邻居")
n = data_set.X_test[20].shape[0]
print("预测类别:", knn.hypothesis(data_set.X_test[20].reshape(-1, n), k=5))
print("真实类别:", data_set.y_test[20])
print("---------------------")
print("测试10行数据x5~x14;k=1,1个最近邻居")
print("预测类别们:", knn.hypothesis(data_set.X_test[5:15], k=1))
print("真实类别们:", data_set.y_test[5:15])
print("---------------------")
print("测试10行数据x5~x14;k=4,4个最近邻居")
print("预测类别们:", knn.hypothesis(data_set.X_test[5:15], k=4))
print("真实类别们:", data_set.y_test[5:15])
print("---------------------")
def main():
# 获取数据集》划分数据集》展示数据集
data_set = get_data_set()
# 构造KNN
knn = K_NN(data_set.X_train, data_set.y_train)
# 使用测试集评估模型
evaluate_model(knn, data_set.X_test, data_set.y_test)
# 展示结果
show_result(knn, data_set)
if __name__ == "__main__":
main();