美文网首页机器学习
【机器学习】k近邻分类与回归实战

【机器学习】k近邻分类与回归实战

作者: 东南有大树 | 来源:发表于2019-01-16 14:35 被阅读17次

    在这一节中,可以了解到K近邻算法,并应用于分类与回归的例子。

    k近邻又称作k-NN算法,是最简单的机器学习算法。非常的适合小白入门了解机器学习原理。k-NN会保存训练的数据,在面对新的数据时,算法会在训练数据集中找到最近的数据点,这也就是为什么被叫作“最近邻”的原因。

    本节的数据将基于上一章节的数据,如有疑问,请查看上一章节。

    备注:mgleran中的数据是用来展示的,不与机器学习的sklern模块相重叠。

    安装mgleran请使用pip install mglearn命令。

    # 在学习之前,先导入这些常用的模块
    import numpy as np
    import pandas as pd
    import matplotlib.pyplot as plt
    import mglearn
    

    k近邻分类

    k-NN 算法最简单的版本是只考虑一个最近邻,即被预测的新的数据点离训练的数据集中的哪个点最近,它将被归类为哪个类别。

    在mglearn的forge中,内置了此种情况,如下图为单一最近邻模型对forge数据集的预测结果,五角星为被预测的数据点,根据其离得最近的训练数据集,通过设置参数n_neighbors=1来设定“最近邻”的个数。

    mglearn.plots.plot_knn_classification(n_neighbors=1)
    
    C:\Users\Administrator\Anaconda3\lib\site-packages\sklearn\utils\deprecation.py:77: DeprecationWarning: Function make_blobs is deprecated; Please import make_blobs directly from scikit-learn
      warnings.warn(msg, category=DeprecationWarning)
    

    从上图我们就能明白,新数据离得谁最近,他就会被归类为哪一类。

    左上角的五角星其实应该属于三角星一类,为了提高准确率,这里可以提高“最近邻”个数,即n_neighbors值。再来看一下当n_neighbors=3时的情况:

    mglearn.plots.plot_knn_classification(n_neighbors=3)
    
    C:\Users\Administrator\Anaconda3\lib\site-packages\sklearn\utils\deprecation.py:77: DeprecationWarning: Function make_blobs is deprecated; Please import make_blobs directly from scikit-learn
      warnings.warn(msg, category=DeprecationWarning)
    

    当提高“最近邻”的个数后,可以看到,对于新数据的预测更准确了,左上角的五角星近相近的是一个圆和两个三角星,因此它被归类于三角一类。

    下面来看看如何通过 scikit-learn 来应用 k 近邻算法。

    # 导入 train_test_split
    from sklearn.model_selection import train_test_split
    # 导入二分类数据集
    X, y = mglearn.datasets.make_forge()
    
    # 将数据打乱,并分为训练集与测试集
    X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)
    
    C:\Users\Administrator\Anaconda3\lib\site-packages\sklearn\utils\deprecation.py:77: DeprecationWarning: Function make_blobs is deprecated; Please import make_blobs directly from scikit-learn
      warnings.warn(msg, category=DeprecationWarning)
    

    上面的代码提供了数据集,接下来,需要实例化k-NN对象。

    # 导入k-NN模型
    from sklearn.neighbors import KNeighborsClassifier
    # 将k-NN模型实例为对象,并指定最近邻个数
    clf = KNeighborsClassifier(n_neighbors=3)
    

    现在,我们就有了最近邻算法模型实例化的对象了,该对象的引用被保存到了变量clf中。

    接下来,我们使用该对象对训练集数据进行训练,以得出一个模型结果。

    clf.fit(X_train, y_train)
    
    KNeighborsClassifier(algorithm='auto', leaf_size=30, metric='minkowski',
               metric_params=None, n_jobs=1, n_neighbors=3, p=2,
               weights='uniform')
    

    对于训练集训练的结果,会被保存在k-NN对象中,所以,该对象会对新数据在此基础上进行预测。

    接下来,对测试集进行预测,并查看预测结果。

    # 预测测试集数据
    clf.predict(X_test)
    
    array([1, 0, 1, 0, 1, 0, 0])
    
    # 评估模型的泛化能力
    clf.score(X_test, y_test)
    
    0.8571428571428571
    

    可以看到,模型的精度为86%,也就是说,该模型对于测试数据的预测,有86%的结果是正确的。

    接下来要做的,是在多张图表中展示不同“最近邻”参数下,模型对于数据的分类能力的体现。而这种分类,采用决策边界来展示。

    接下来将分析“最近邻”分另为1、3、9的情况。

    # 通过subplots创建一个幕布,在幕布上创建三个绘图区,幕布大小为10*3
    fig, axes = plt.subplots(1, 3, figsize=(10,3))
    
    # 通过zip函数,将两个集体打包并成对的返回
    for n_neighbors, ax in zip([1, 3, 9], axes):
        # fit方法返回对象本身,所以我们可以将实例化和拟合放在一行代码中
        clf = KNeighborsClassifier(n_neighbors=n_neighbors).fit(X, y)
        # 展示模型的决策边界
        mglearn.plots.plot_2d_separator(clf, X, fill=True, eps=0.5, ax=ax, alpha=.4)
        # 将原始数据的位置在图中展示 
        mglearn.discrete_scatter(X[:, 0], X[:, 1], y, ax=ax)
        # 设置标题
        ax.set_title('n_neighbors{}'.format(n_neighbors))
        # 设置横坐标标签
        ax.set_xlabel('feature 0')
        # 设置纵坐标标签
        ax.set_ylabel('feature 1')
    
    # 在第一张图上显示图例
    axes[0].legend(loc=3)
    plt.show()
    

    从上图可以发现,“最近邻”的个数变大后,决策边界将变得更加的平缓,曲线也变得更加的简单。这种更简单的情形,比较适合于大多数的数据。

    这种结论也能说明,更简单的模型,泛化能力更好。

    接下来认证一下模型复杂度与泛化能力之间的关系

    这次将使用乳腺癌数据集,对该数据用不同的“最邻近”个数进行训练与测试,然后对于评估结果展示一个线性的结果。

    # 导入乳腺癌数据集模块
    from sklearn.datasets import load_breast_cancer
    # 加载数据
    cancer = load_breast_cancer()
    
    # 将数据分为训练集与测试集
    X_train, X_test, y_train, y_test = train_test_split(cancer.data, cancer.target, stratify=cancer.target, random_state=66)
    
    # 创建两个列表,分别用来记录不同“最近邻”参数下模型的训练集精度和测试集精度
    training_accuracy = []
    test_accuracy = []
    
    # n_neighbors 取值从1到10
    neighbors_settings = range(1, 11)
    
    # 循环测试不同“最近邻”参数
    for n_neighbors in neighbors_settings:
        # 构建模型 
        clf = KNeighborsClassifier(n_neighbors=n_neighbors)
        # 训练
        clf.fit(X_train, y_train)
        # 记录训练集精度
        training_accuracy.append(clf.score(X_train, y_train))
        # 记录测试集精度
        test_accuracy.append(clf.score(X_test, y_test))
        
    # 画出训练集数据的曲线图
    plt.plot(neighbors_settings, training_accuracy, label="training accuracy")
    # 画出测试集的曲线图
    plt.plot(neighbors_settings, test_accuracy, label="test accuracy")
    # 设置x与y轴标签
    plt.xlabel('n_neighbors')
    plt.ylabel('Accuracy')
    # 展示图例
    plt.legend()
    plt.show()
    

    能过上图的展示发现,训练集的泛化精度随着“最近邻”个数的增加,精度越来越低。而测试集的泛化精度随着“最近邻”个数的增加,先上长后下降。两者的最佳取值大概在n_neighbors=6这个位置。

    也就是说,当“最近邻”个数过小的时候,模型过于复杂,不适用于新数据。而“最近邻”个数过大,则模型会变得过于简单,性能也会变差。

    k近邻回归

    k近邻算法还适用于回归。在mglearn的wave中内置了一个回归数据集。现在,在该数据集上再添加三个测试点,利用单一“最近邻”参数来预测目标的结果值。如下:

    mglearn.plots.plot_knn_regression(n_neighbors=1)
    

    再来看下“最近邻”个数为3时的预测值。

    mglearn.plots.plot_knn_regression(n_neighbors=3)
    

    上面两个图例是现成的模型,在 scikit-learn 中的 KNeighborsRegressor 类用于回归算法的实现,它的用法与 KNeighborsClassifier 的用法相似。

    # 导入 KNeighborsRegressor 模块
    from sklearn.neighbors import KNeighborsRegressor
    
    # 加载40个回归数据集
    X, y = mglearn.datasets.make_wave(n_samples=40)
    
    # 将wave数据集分为训练集和测试集
    X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)
    
    # 模型实例化,并将邻居个数设置为3
    reg = KNeighborsRegressor(n_neighbors=3)
    # 利用训练数据和训练目标值为拟合模型
    reg.fit(X_train, y_train)
    
    KNeighborsRegressor(algorithm='auto', leaf_size=30, metric='minkowski',
              metric_params=None, n_jobs=1, n_neighbors=3, p=2,
              weights='uniform')
    

    然后对测试集进行预测:

    reg.predict(X_test)
    
    array([-0.05396539,  0.35686046,  1.13671923, -1.89415682, -1.13881398,
           -1.63113382,  0.35686046,  0.91241374, -0.44680446, -1.13881398])
    

    评估一下模型的泛化精度:

    reg.score(X_test, y_test)
    
    0.8344172446249604
    

    对于回归模型,scroe方法返回的是R的平方数,也叫做决定系统,是回归模型预测的优度度量,位于0到1之间。R的平方等于1对应完美预测,R的平方等于0对应常数模型,即总是预测训练集响应(y_train)的平均值。

    分析 KNeighborsRegressor

    同样,这里来展示“最近邻”个数分别为1、3、9下的泛化能力。

    fig, axes = plt.subplots(1, 3, figsize=(15, 4))
    # 创建1000个数据点,在-3和3之间均匀分布
    line = np.linspace(-3, 3, 1000).reshape(-1, 1)
    
    for n_neighbors, ax in zip([1, 3, 9], axes):
        # 利用1、3、9个邻居分别进行预测
        reg = KNeighborsRegressor(n_neighbors=n_neighbors)
        reg.fit(X_train, y_train)
        # 展示预测曲线与训练集和测试集的关系
        ax.plot(line, reg.predict(line))
        ax.plot(X_train, y_train, '^', c=mglearn.cm2(0), markersize=8)
        ax.plot(X_test, y_test, 'v', c=mglearn.cm2(1), markersize=8)
        
        ax.set_title('{} neighbor(s)\n train score: {:.2f} test score: {:.2f}'.format(n_neighbors, reg.score(X_train, y_train), reg.score(X_test, y_test)))
        ax.set_xlabel('Feature')
        ax.set_ylabel('Target')
    axes[0].legend(['Model predictions', 'Training data/target', 'Test data/target'], loc='best')
    plt.show()
    

    从图中可以看出,仅使用单一“最近邻”参数,训练集中的第个点都对预测结果有显著影响,非常不稳定。

    而更大的“最近邻”个数所对应的预测结果也更平滑,但对训练数据的拟合不好。

    总结:

    一般来说,KNeighbors分类器有2个重要参数:邻居的个数与数据点之间距离的度量方法,在实践中,使用较小的邻居个数(比如3或5)往往可以得到比较好的结果。

    k-NN的优点之一是比较容易理解,比较适合机器学习小白入门,但如果数据量特别大,该模型的表现就会很差。

    
    

    相关文章

      网友评论

        本文标题:【机器学习】k近邻分类与回归实战

        本文链接:https://www.haomeiwen.com/subject/xfdmdqtx.html