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
posted @ 2020-12-24 12:45  fly_bk  阅读(193)  评论(0编辑  收藏  举报