基本思想
近朱者赤 近墨者黑
如果一个样本的K个最相近的样本中,大多数都属于某一类,那这个样本也属于这一类
KNN原理
- K-Nearest Neighbors,它是有监督,分类算法,也可用于回归,思想简单,理论成熟
- 训练模型时,没有实际的训练过程,保存的模型只是将所有的训练样本集保存起来。
- 在预测未知样本时,计算其到训练样本集中每个样本的距离,然后对所有的距离从小到大排序,取距离最近的K个训练集样本,由这K个最近邻样本投票决定该未知样本的所属类别,解决回归问题时,则取这K个最近邻的标记值的平均值/加权平均值
KNN算法预测圆形样本的类别:
K=3
K=5
K值不同,预测结果不同,对K值敏感
小过拟合,大欠拟合
KNN的三要素
- K值选择,分类问题一般取奇数,且 K ≤ 20 或者 K ≤ m , m 为训练样本数 K\le20 或者 K\le \sqrt{m},m为训练样本数 K≤20或者K≤m,m为训练样本数
- 距离的度量,一般采用欧式距离 (注意归一化)
- 近邻的决策方式
分类问题–投票
回归问题–>均值
KNN的复杂度
KNN分类的复杂度与训练样本数成正比–> O(m)
KNN问题点
训练集类别不平衡时,预测结果偏向于类别多的一方
例如:
类别为0 的样本数95
类别为1 的样本数5
取K=11,预测类别为1 的一个样本, 11个近邻中最多有
5个属于类别1,
6个属于类别0,
投票决策预测结果为0,显然这是不正确的
解决方案:不同的样本给予不同的权重 1 d 或者 1 d 2 \frac {1} {d}或者\frac {1} {d^2} d1或者d21
KNN优点
- 思想简单,理论成熟,既可以分类,也可以回归
- 可用于 非线性 非线性 非线性 的分类
- 训练时间复杂度较低,仅O(m)
- 和朴素贝叶斯相比,对数据没有假设,准确率高,对噪声数据不敏感
KNN 缺点
- 计算量大,尤其训练样本数多,维度大时
- 样本不平衡时,预测结果偏向于类别多的一类
- 大数据时,建立KD树,球树模型,需要大内存
- 训练模型,基本不学习,可解释性不强
sklearn库中KNN
#分类
from sklearn.neighbors import KNeighborsClassifier
#回归
from sklearn.neighbors import KNeighborsRegressor
#knn 扩展
from sklearn.neighbors import RadiusNeighborsClassifier
from sklearn.neighbors import RadiusNeighborsRegressor
以KNN分类为例,介绍常用的参数:
- n_neighbors, K值,最近邻数,默认5, 一般取奇数,小于等于20
- weights, K个近邻各自的权重,
- ‘uniform’ ,权重相同
- ‘distance’ ,权重跟距离成反比
- 自定义函数,根据距离计算权重
- algorithm,计算近邻的算法
- metric,距离度量方式,默认欧式距离
KNN小试牛刀
- 生成分类数据
from sklearn.datasets import make_classification
x,y = make_classification(n_samples=500,n_features=2,n_classes=3,n_redundant=0,n_clusters_per_class=1,class_sep=1.2)
#可视化样本点
from matplotlib import pyplot as plt
plt.figure()
plt.title("Sample points")
plt.scatter(x[y==0][:,0],x[y==0][:,1],s=100,c="r",marker="s",edgecolors=None,label="label 0")
plt.scatter(x[y==1][:,0],x[y==1][:,1],s=100,c="g",marker="^",edgecolors=None,label="label 1")
plt.scatter(x[y==2][:,0],x[y==2][:,1],s=100,c="b",marker="o",edgecolors=None,label="label 2")
plt.grid()
plt.legend(loc="best",frameon=True,framealpha=0.3)
plt.show()
- 训练模型
from sklearn.neighbors import KNeighborsClassifier
clf = KNeighborsClassifier(n_neighbors=15,weights="distance")
clf.fit(x,y)
- 画出KNN的分类边界
#分类边界
x_min,y_min = x.min(axis=0)
x_max,y_max = x.max(axis=0)
x_range = np.linspace(x_min-1,x_max+1,200)
y_range = np.linspace(y_min-1,y_max+1,200)
xx,yy = np.meshgrid(x_range,y_range)
#预测
z = clf.predict(np.c_[xx.ravel(),yy.ravel()])
#画等高线
plt.contour(xx,yy,z.reshape(xx.shape),cmap="cool")
plt.show()
如下的分类边界