01 K近邻算法
K近邻算法采用测量不同特征值之间的距离方法进行分类
- 优点:精读高,对异常值不敏感,无数据输入假定
- 缺点:计算算杂度高,空间复杂度高。适合数据范围:数值型和标称型
K近邻算法是分类数据最简单最有效的算法。是基于实例的学习,使用算法时我们必须有接近实际数据的训练
样本数据。K近邻算法必须保存全部数据集,如果训练数据集很大,必须使用大量的存储空间。
由于必须对数据集中的每个数据计算距离值,实际使用时可能非常耗时。
它无法给出任何数据的基础结构信息,因此,我们也无法知晓平均实例样本和典型实例样本具体有什么特征
import numpy as np
import pandas as pd
import operator
def createDataSet():
group = np.array([[1.0, 1.1],
[1.0, 1.0],
[0, 0],
[0, 0.1]])
labels = ['A', 'A', 'B', 'B']
return group, labels
def classify1(inX, dataSet: pd.DataFrame, labels: list, k: int) -> str:
"""
k Nearest Neighbors -- 欧式距离 使用pandas的DataFrame
"""
distances = pd.DataFrame({'data': (((dataSet - inX) ** 2).sum(1)) ** 0.5, 'labels': labels})
sortedClassCount = pd.DataFrame(distances).sort_values(by='data')[:k]
re = sortedClassCount.loc[:, 'labels'].value_counts()
return re.index[0]
def classify0(inX, dataSet: np.ndarray, labels: list, k: int) -> str:
"""
k Nearest Neighbors -- 欧式距离
:param inX: 输入向量
:param dataSet: 输入的训练样本集
:param labels: 标签向量
:param k: 选择最近邻居的数目
:return: 最受欢迎的标签
"""
dataSetSize = dataSet.shape[0]
# 距离计算
# 行重复dataSetSize次,列1次
diffMat = np.tile(inX, (dataSetSize, 1)) - dataSet
# 平方和
sqDiffMat = diffMat ** 2
sqDistances = sqDiffMat.sum(axis=1)
# 开方
distances = sqDistances ** 0.5
# 排序索引
sortedDistIndicies = distances.argsort()
classCount = {}
# 选择距离最小的额k个点
for i in range(k):
voteIlabel = labels[sortedDistIndicies[i]]
classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1
# 对第1维的元素进行降序排序
sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True)
return sortedClassCount[0][0]
if __name__ == '__main__':
group, labels = createDataSet()
print(classify0([0, 0], group, labels, 3))
print(classify1([0, 0], pd.DataFrame({'a': group[:, 0], 'b': group[:, 1]}), labels, 3))
sklearn中knn的使用
k-近邻算法的核心思想是未标记样本的类别,由距离其最近的 k个邻居投票来决定。
算法优缺点
- 优点:准确性高,对异常值和噪声有较高的容忍度
- 缺点 :计算量较大,对内存的需求也较大。从算法原理可以看出来,每次对一个未标记样本进行分类时,都需要全部计算一遍距离
算法参数
其算法参数是k,参数选择需要根据数据来决定。
- k值越大,模型的偏差越大,对噪声数据越不敏感,当 k值很大时,可能造成模型欠拟合;
- k值越小,模型的方差就会越大,当k值太小,就会造成模型过拟合。
算法的变种
- 可以增加邻居的权重。默认情况下,在计算距离时,都是使用相同权重。实际上,我们可以针对不同的邻居指定不同的距离权童,如距离越近权重越高。这个可以通过指定算法的 weights参数来实现。
- 使用一定半径内的点取代距离最近的 k 个点。在 scikit-learn 里 , RadiusNeighborsClassifier 类实现了这个算法的变种。当数据采样不均匀时,该算法变种可以取得更好的性能
示例:使用 k-近邻算法进行分类
import numpy as np
from sklearn.datasets._samples_generator import make_blobs
import matplotlib.pyplot as plt
centers = [[-2, 2], [2, 2], [0, 4]]
# cluster_std标准差,用来指明生成的点分布的松散程度
X, y = make_blobs(n_samples=60, centers=centers, random_state=0, cluster_std=0.60)
plt.figure(figsize=(16, 10), dpi=144)
c = np.array(centers)
plt.scatter(X[:, 0], X[:, 1], c=y, s=100, cmap='cool') # 画出样本
plt.scatter(c[:, 0], c[:, 1], s=100, marker='^', c='orange') # 画出中心点
from sklearn.neighbors import KNeighborsClassifier
# 模型训练
k = 5
clf = KNeighborsClassifier(n_neighbors=5)
clf.fit(X, y)
# 进行预测
X_sample = [[0, 2]]
y_sample = clf.predict(X_sample)
print('y_sample', y_sample)
# 把样本周围距离最近的5个点取出来
neighbors = clf.kneighbors(X_sample, return_distance=False)
plt.scatter(X_sample[0][0], X_sample[0][1], marker='x', c=y_sample, s=100, cmap='cool') # 预测点
# 预测点与最近5个样本点连线
for i in neighbors[0]:
plt.plot([X[i][0], X_sample[0][0]], [X[i][1], X_sample[0][1]], 'k--', linewidth=0.6)
plt.show()
示例:使用 k-近邻算法进行回归拟合
import numpy as np
# 生成数据集,它在余弦曲线的基础上加入噪声
n_dots = 40
X = 5 * np.random.rand(n_dots, 1)
y = np.cos(X).ravel()
y += 0.2 * np.random.rand(n_dots) - 0.1
from sklearn.neighbors import KNeighborsRegressor
import matplotlib.pyplot as plt
k = 5
knn = KNeighborsRegressor(k)
knn.fit(X, y)
print(knn.score(X, y))
# 进行回归拟合
# 思路:在X轴上的指定区域内生成足够多的点,使用训练出来的模型进行预测,将所有预测点进行连接,画出拟合曲线
T = np.linspace(0, 5, 500)[:, np.newaxis] # np.newaxis的作用是增加一个维度
print(T.shape)
y_pred = knn.predict(T)
plt.figure(figsize=(16, 10), dpi=144)
plt.scatter(X, y, c='g', label='data', s=100) # 画出训练样本
plt.plot(T, y_pred, c='k', label='prediction', lw=4) # 画出拟合曲线
plt.axis('tight')
plt.title('KNeighborsRegressor (k=%i)' % k)
plt.show()
参考
- 黄永昌等,scikit-learn机器学习——常用算法原理及编程实战,北京:机械工业出版社,2018