算法原理
模型
k近邻算法是一个多分类的机器学习算法。它的实现过程很容易理解,简单来说,就是通过计算待分类样本与所有训练样本的“距离”,然后取出离待分类样本最近的k个训练样本,最后将这k个训练样本中类别最多的一种作为该待分类样本的预测类别。
图解
如图所示,假设有若干个训练样本,不同颜色代表不同的类别。
现在放入一个待分类样本。
定义k值(假设为3),定义距离(假设是欧式距离),计算该待分类样本与所有训练样本的距离。
选取距离最小的k个训练样本。
将这k个训练样本中出现最多的那个类别作为该待分类样本的预测类别。此时图上待分类样本的预测类别是红色。
具体步骤
1、已知训练集 { x i , y i } i = 1 n \{x_i,y_i\}_ {i=1}^n {xi,yi}i=1n,训练集的每个样本包含特征 x i x_i xi和类别 y i y_i yi,设定k值,定义样本间的距离计算公式(这里的距离需要自己定义,可以是曼哈顿距离,欧式距离等等)。
2、对于每个特征为
a
a
a的待分类样本
A
A
A,计算其与所有训练样本的距离
d
=
d
(
a
,
x
i
)
,
i
=
1
,
2
,
.
.
.
,
n
d=d(a,x_i),i=1,2,...,n
d=d(a,xi),i=1,2,...,n
3、取出距离
d
d
d最近的k个训练样本
(
x
i
1
,
y
i
1
)
,
(
x
i
2
,
y
i
2
)
,
.
.
.
,
(
x
i
k
,
y
i
k
)
(x_{i1},y_{i1}),(x_{i2},y_{i2}),...,(x_{ik},y_{ik})
(xi1,yi1),(xi2,yi2),...,(xik,yik)
将
{
y
i
1
,
y
i
2
,
.
.
.
,
y
i
k
}
\{y_{i1},y_{i2},...,y_{ik}\}
{yi1,yi2,...,yik}中出现次数最多的类别作为待分类样本
A
A
A的预测类别。
4、重复执行2、3步直至所有待分类样本预测完毕。
程序实现
初始化函数
def __init__(self, k=10):
"""
k 最近邻个数
"""
self.k = k
训练函数
def fit(self, X, y):
self.X = X
self.y = y
训练函数只需存储样本的特征和类别,不需要进行计算,这是knn的一个优点。
预测函数
def predict(self, X):
res = []
for x in X:
# 计算平方和
sum = np.sum((x-self.X)**2, axis=1)
num = np.argsort(sum)[:self.k]
pred_y = np.argmax(np.bincount(self.y[num]))
res.append(pred_y)
return np.array(res)
因为需要计算每个待分类样本与每个训练样本的距离,所以虽然实现过程简单,但计算比较耗时。
整合全部代码
class KNeighborsClassifier():
def __init__(self, k=10):
"""
k 最近邻个数
"""
self.k = k
def fit(self, X, y):
self.X = X
self.y = y
def predict(self, X):
res = []
for x in X:
# 计算平方和
sum = np.sum((x-self.X)**2, axis=1)
num = np.argsort(sum)[:self.k]
pred_y = np.argmax(np.bincount(self.y[num]))
res.append(pred_y)
return np.array(res)
实例化演示
导入相关库并查看数据分布。
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import load_iris
from sklearn.metrics import accuracy_score
from sklearn.model_selection import train_test_split
iris = load_iris()
X = iris.data[:100]
y = iris.target[:100]
train_x,test_x,train_y,test_y = train_test_split(X, y, test_size=0.3, random_state=1)
color = ['red','yellow']
plt.rcParams['font.sans-serif'] = ['SimHei']
for i in range(2):
plt.scatter(train_x[train_y==i,0], train_x[train_y==i,1], color=color[i])
plt.scatter(test_x[:,0], test_x[:,1],color='silver')
plt.legend([1,2,'待分类样本'])
实例化函数并预测样本类别。
clf = KNeighborsClassifier(k=20)
clf.fit(train_x, train_y)
pred_y = clf.predict(test_x)
accuracy_score(test_y, pred_y)
进一步
k值是一个超参数,k值的大小决定了模型的好坏,可以通过k折交叉验证来选取适合的k值,注意k折交叉的k值和k近邻的k值不是同一个意思,只是刚好都这么叫而已。这里采用5折交叉验证,主要步骤是将训练集均分为5份小训练集,取出其中的4份用于knn分类器的训练,剩余的1份用来验证模型的效果,可以得到模型的准确率。不同的取法会得到不一样的结果,对于每个k值都将计算得到5个模型的准确率,我们将在重新生成的二分类样本集上进行操作。
from sklearn.model_selection import KFold
from sklearn.datasets import make_classification
X,y = make_classification(n_samples=300, n_features=5, shuffle=True, random_state=6, n_classes=2)
color = ['red','yellow']
for i in range(2):
plt.scatter(X[y==i,0], X[y==i,1], color=color[i])
进行5折交叉验证,选出最优的k值。
kf = KFold(n_splits=5)
accuracy = []
for i in range(1,40):
acc = []
for train_index, test_index in kf.split(X):
train_x,train_y = X[train_index],y[train_index]
test_x,test_y = X[test_index],y[test_index]
clf = KNeighborsClassifier(k=i)
clf.fit(train_x,train_y)
pred_y = clf.predict(test_x)
acc.append(sum(pred_y==test_y)/len(pred_y))
accuracy.append(acc)
accuracy = np.array(accuracy)
m,n = accuracy.shape
for i in range(n):
plt.scatter(range(1,m+1),accuracy[:,i])
plt.errorbar(range(1,m+1),accuracy.mean(axis=1),accuracy.std(axis=1))
从图上可以看出,当参数k取5的时候,模型的效果最好。
在数据量还不是很大的情况下,可以使用上述的k折交叉验证或网格搜索来寻找最优的k值;但当数据量很大时,采用这些方法,虽然可以实现,但是同时计算时间也会很长,要慎重使用。
以上便是k近邻算法的代码实现,如有不对之处,欢迎交流。