6行代码实现kNN算法
监督学习-分类算法-kNN
- kNN:K最近邻算法,k-Nearest Neighbor
- k个最近的邻居
- 属于:监督学习,分类算法
kNN算法思想
- 衡量未知分类点周围邻居的权重
- 然后把它归类到权重更大的那一类
较适用于类域交叉重叠的样本
kNN算法描述
- 输入k值
- 对未知类别数据集中的每一个点依此执行以下操作
- 计算当前点与已知类别数据集中的点之间的距离
- 按照距离以递增次序排序
- 选取与当前点距离最小的k个点
- 确定前k个点所在的类别出现的频率
- 返回前k个点出现频率最高的类别作为当前点的预测类别
kNN算法优缺点
- 优点:
- 简单,容易理解和实现
- 基于实例预测,不需训练模型
- 适合对象有多个标签的分类
- 缺点:
- 样本不均衡时易失效
- 计算量大,每个待分类对象都需要计算它到 全体已知样本的距离
- 噪声敏感
kNN算法手动实现
特征和标签
类别 | 重量 | 光洁度 |
---|---|---|
苹果 | 150g | 光滑 |
苹果 | 170g | 光滑 |
橘子 | 130g | 粗糙 |
橘子 | 140g | 粗糙 |
import numpy as np
import matplotlib.pyplot as plt
plt.style.use('seaborn')
plt.rcParams['font.family'] = ['Arial Unicode MS', 'Microsoft Yahei', 'SimHei', 'sans-serif']
features = np.array([[150, 0], [170, 0], [130, 1], [140, 1]]) # 特征数据:重量和是否光滑
labels = np.array([0, 0, 1, 1]) # 标签数据:0苹果,1橘子
# 待预测数据,这里为临时的1维数据
w = np.array([180, 0])
features # 训练集特征
array([[150, 0],
[170, 0],
[130, 1],
[140, 1]])
features[:, 0][:2] # 苹果重量
array([150, 170])
features[[0, 1], [0, 0]] # 花式索引(竖着看)
array([150, 170])
features[:, 1][:2]
array([0, 0])
features[[0, 1], [1, 1]]
array([0, 0])
w
array([180, 0])
plt.figure(figsize=(18, 8))
plt.scatter(
features[:, 0][:2],
features[:,1][:2],
color='r',
s=500,
alpha=0.8,
label='苹果',
)
plt.scatter(
features[:, 0][2:],
features[:,1][2:],
color='y',
s=500,
alpha=0.8,
label='橘子',
)
plt.scatter(
w[0],
w[1],
color='#000000',
s=500,
alpha=0.8,
label='未知',
)
plt.legend()
<matplotlib.legend.Legend at 0x54e90f0>
[外链图片转存失败(img-2mvLxkMe-1566634952497)(output_11_1.png)]
1计算未知点到所有已知点的距离
d
12
=
∑
k
=
1
n
(
x
1
k
−
x
2
k
)
2
d_{12}=\sqrt{\sum_{k=1}^{n}\left(x_{1 k}-x_{2 k}\right)^{2}}
d12=∑k=1n(x1k−x2k)2
features
array([[150, 0],
[170, 0],
[130, 1],
[140, 1]])
w
array([180, 0])
((180-140) ** 2 + (0 - 1) ** 2) ** 0.5
40.01249804748511
(w - features)
array([[30, 0],
[10, 0],
[50, -1],
[40, -1]])
distances = np.sum((w - features) ** 2, axis=1) ** 0.5
distances
array([30. , 10. , 50.009999 , 40.01249805])
2 距离从小到大排序
np.sort(distances) # 返回排序后的值,没用
# 原来的distances,索引0,1表示类别苹果,2,3表示类别橘子
# 返回排序后的原索引,索引和类别的关系没变
nearest = np.argsort(distances) # 返回从小到大排序以后的索引
nearest
array([1, 0, 3, 2], dtype=int64)
labels # 类别:0,1,2,3
# 0,1对应0,0(苹果)
# 2,3对应1,1(橘子)
array([0, 0, 1, 1])
3 取前k个值,索引对应的标签值
k = 3
nearest[:k]
array([1, 0, 3], dtype=int64)
labels
labels[[2, 1]] # 查询对应索引的值
topK_y = labels[nearest[:k]] # 查询前k个索引对应的值(标签)
topK_y
array([0, 0, 1])
4 计算前k个值类别出现的频次
a = np.array([0, 1, 2, 1, 3, 0, 0])
np.bincount(a) # 0有3个,1有2个,2有1个,3有1个(索引就是原值)
array([3, 2, 1, 1], dtype=int64)
votes = np.bincount(topK_y) # 2个0,1个1
votes
array([2, 1], dtype=int64)
5 输出出现频次最高的值的索引
np.max(votes)
np.argmax(votes)
0
封装成函数
def knn_classify(k, X_train, y_train, X_test):
"""我手写的机器学习算法:kNN.
attr:
k: 邻居个数,1,2,3.。。
X_train: 训练集特征,[[180, 0], [120, 1]....]
y_train: 训练集标签, [0, 1, 0, 1....]
X_test: 测试集特征,[180, 0]
return: 测试集预测的标签, 0, 1
"""
distances = np.sum((X_test - X_train) ** 2, axis=1) ** 0.5
nearest = np.argsort(distances)
topK_y = y_train[nearest[:k]]
votes = np.bincount(topK_y)
return np.argmax(votes)
knn_classify?
knn_classify(3, features, labels, w)
0
6行代码完成knn算法案例
import numpy as np
X_train =np.array([[150, 0], [170, 0], [130, 1], [140, 1]])
y_train = np.array([0, 0, 1, 1])
X_test = np.array([180, 0])
y_test = np.argmax(np.bincount(y_train[np.argsort(np.sum((X_test - X_train) ** 2, axis=1) ** 0.5)[:k]]))
y_test
0
sklearn的kNN算法
from sklearn.neighbors import KNeighborsClassifier
my_classifier = KNeighborsClassifier(n_neighbors=3) # k值设为3,不能超过训练集数量
my_classifier
KNeighborsClassifier(algorithm='auto', leaf_size=30, metric='minkowski',
metric_params=None, n_jobs=None, n_neighbors=3, p=2,
weights='uniform')
my_classifier.fit(X_train, y_train)
KNeighborsClassifier(algorithm='auto', leaf_size=30, metric='minkowski',
metric_params=None, n_jobs=None, n_neighbors=3, p=2,
weights='uniform')
X_test # 特征维度不够
array([180, 0])
X_test.reshape(1, 2)
array([[180, 0]])
# predict函数需要二维的数组
my_classifier.predict(X_test.reshape(1, 2))
array([0])