三 K近邻
1.1 基础
k近邻方法是一种惰性学习算法,可以用于回归和分类,它的主要思想是投票机制,对于一个测试实例x, 我们在有标签的训练数据集上找到和最相近的k个数据,用他们的label进行投票,分类问题则进行表决投票,回归问题使用加权平均或者直接平均的方法。
K近邻算法,即是给定一个训练数据集,对新的输入实例,在训练数据集中找到与该实例最邻近的K个实例,这K个实例的多数属于某个类,就把该输入实例分类到这个类中。(这就类似于现实生活中少数服从多数的思想)
为了保证每个特征同等重要性,需对每个特征进行归一化
- 算法关键: k值的选择 和 距离的计算 >>>
k∈[2,20]
,使用交叉验证得到一个很好的k值
k值还可以表示我们的模型复杂度,当k值越小意味着模型复杂度变大,更容易过拟合,(用极少数的样例来绝对这个预测的结果,很容易产生偏见,这就是过拟合)。我们有这样一句话,k值越多学习的估计误差越小,但是学习的近似误差就会增大。- 样本之间的距离的计算,我们一般使用对于一般使用Lp距离进行计算。当
p=1
时候,称为曼哈顿距离(Manhattan distance),当p=2
时候,称为欧氏距离(Euclidean distance),当p=∞
时候,称为极大距离(infty distance), 表示各个坐标的距离最大值,另外也包含夹角余弦等方法。
一般采用欧式距离较多,但是文本分类则倾向于使用余弦来计算相似度。
- KNN算法优点
- 简单易用,相比其他算法,KNN算是比较简洁明了的算法。即使没有很高的数学基础也能搞清楚它的原理。
- 模型训练时间快,上面说到KNN算法是惰性的,这里也就不再过多讲述。
- 预测效果好。
- 对异常值不敏感
- KNN算法缺点
- 对内存要求较高,因为该算法存储了所有训练数据
- 预测阶段可能很慢
- 对不相关的功能和数据规模敏感
1.2 实例应用
1.2.1 KNN分类
- 数据导入
iris = datasets.load_iris()
x = iris.data[:, :2] #只取前两维数据便于可视化
y = iris.target
- 训练可视化
k_list = [1, 3, 5, 8, 10, 15]
h = 0.02
# 创建不同颜色的画布
cmap_light = ListedColormap(['orange', 'cyan', 'cornflowerblue'])
cmap_bold = ListedColormap(['darkorange', 'c', 'darkblue'])
plt.figure(figsize = (15, 14))
# 根据不同的k值进行可视化
for ind, k in enumerate(k_list):
clf = KNeighborsClassifier(k)
clf.fit(x, y)
# 画出决策边界
x_min, x_max = x[:, 0].min() - 1, x[:, 0].max() + 1
y_min, y_max = x[:, 1].min() - 1, x[:, 1].max() + 1
nx, ny = np.meshgrid(np.arange(x_min, x_max, h),
np.arange(y_min, y_max, h))
z = clf.predict(np.c_[nx.ravel(), ny.ravel()]).reshape(nx.shape)
plt.subplot(321 + ind)
#边界填充颜色
plt.pcolormesh(nx, ny, z, cmap = cmap_light, shading='auto')
#数据点可视化
plt.scatter(x[:, 0], x[:, 1], c = y, cmap = cmap_bold, edgecolor = 'k', s = 20)
plt.xlim(nx.min(), nx.max())
plt.ylim(ny.min(), ny.max())
plt.title("k = %i"%k)
plt.show()
1.2.3 莺尾花KNN分类
- 导入数据
iris = datasets.load_iris()
x = iris.data
y = iris.target
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size = 0.2)
#设置参数k(n_neighbors)=5, 使用欧式距离(metric=minkowski & p=2)
clf = KNeighborsClassifier(n_neighbors=2, p=2, metric='minkowski')
clf.fit(x_train, y_train)
- 训练预测
x_predict = clf.predict(x_test)
acc = sum(x_predict == y_test) / x_predict.shape[0]
print("the accuracy of prediction: %.3f"%acc)
the accuracy of prediction: 0.967
1.2.4 KNN回归
- 载入数据
np.random.seed(0)
# 对40个 (0, 1) * 5 数据进行升序训练 500个(0, 5)等差作为测试
x = np.sort(5 * np.random.rand(40, 1), axis = 0)
T = np.linspace(0, 5, 500)[:, np.newaxis] #np.newaxis -> 一维变二维
#使用sin函数得到y值,并拉伸到一维
y = np.sin(x).ravel()
# Add noise to targets[y值增加噪声]
y[::5] += 1 * (0.5 - np.random.rand(8))
- 预测&可视化
kn = [1, 3, 5, 8, 10, 40]
plt.figure(figsize = (10, 20))
for i, k in enumerate(kn):
# 默认使用加权平均进行计算predictor
clf = KNeighborsRegressor(n_neighbors=k, p = 2, metric = "minkowski")
clf.fit(x, y)
#预测
y_pred = clf.predict(T)
plt.subplot(6, 1, i + 1)
plt.scatter(x, y, color ='red', label = 'data')
plt.plot(T, y_pred, color = 'navy', label = 'prediction')
plt.axis('tight')
plt.legend()
plt.title("KNeighborsRegressor (k = %i)"%(k))
plt.tight_layout()
plt.show()
- 结论: k= 1 >>> 过拟合
当k=40时,预测的结果和最近的40个样本相关,因为我们只有40个样本,此时是所有样本的平均值,此时所有预测值都是均值,很容易发生欠拟合。
一般情况下,使用knn的时候,根据数据规模我们会从[3, 20]之间进行尝试,选择最好的k,例如上图中的[3, 10]相对1和40都是还不错的选择。
1.2.5
参考资料
1.https://zhuanlan.zhihu.com/p/25994179 一文搞懂k近邻(k-NN)算法(一)