美文网首页阿里云机器学习训练营
Task03-K近邻(k-nearest neighbors)-

Task03-K近邻(k-nearest neighbors)-

作者: 北欧森林 | 来源:发表于2020-12-28 23:45 被阅读0次

    本笔记为参加阿里云“天池龙珠计划 机器学习训练营”所做的学习记录,代码及知识内容均来源于训练营,本人稍作扩充。
    具体活动内容请移步阿里云天池龙珠计划

    2.4.3 模拟数据集--kNN回归

    Step1: 库函数导入

    #Demo来自sklearn官网
    import numpy as np
    import matplotlib.pyplot as plt
    from sklearn.neighbors import KNeighborsRegressor
    

    Step2: 数据导入&分析

    np.random.seed(0)
    # 随机生成40个(0, 1)之前的数,乘以5,再进行升序
    X = np.sort(5 * np.random.rand(40, 1), axis=0)
    # 创建[0, 5]之间的500个数的等差数列, 作为测试数据
    T = np.linspace(0, 5, 500)[:, np.newaxis]
    # 使用sin函数得到y值,并拉伸到一维
    y = np.sin(X).ravel()
    # Add noise to targets[y值增加噪声]
    y[::5] += 1 * (0.5 - np.random.rand(8))
    

    Step3: 模型训练&预测可视化

    # Fit regression model
    # 设置多个k近邻进行比较
    n_neighbors = [1, 3, 5, 8, 10, 40]
    # 设置图片大小
    plt.figure(figsize=(10,20))
    for i, k in enumerate(n_neighbors):
        # 默认使用加权平均进行计算predictor
        clf = KNeighborsRegressor(n_neighbors=k, p=2, metric="minkowski")
        # 训练
        clf.fit(X, y)
        # 预测
        y_ = clf.predict(T)
        plt.subplot(6, 1, i + 1)
        plt.scatter(X, y, color='red', label='data')
        plt.plot(T, y_, color='navy', label='prediction')
        plt.axis('tight')
        plt.legend()
        plt.title("KNeighborsRegressor (k = %i)" % (k))
    
    plt.tight_layout()
    plt.show()
    
    knnfigA.jpg

    Step4:模型分析

    当k=1时,预测的结果只和最近的一个训练样本相关,从预测曲线中可以看出当k很小时候很容易发生过拟合。
    当k=40时,预测的结果和最近的40个样本相关,因为我们只有40个样本,此时是所有样本的平均值,此时所有预测值都是均值,很容易发生欠拟合。
    一般情况下,使用knn的时候,根据数据规模我们会从[3, 20]之间进行尝试,选择最好的k,例如上图中的[3, 10]相对1和40都是还不错的选择。

    2.4.4 马绞痛数据--kNN数据预处理+kNN分类pipeline
    # 下载需要用到的数据集
    !wget https://tianchi-media.oss-cn-beijing.aliyuncs.com/DSW/3K/horse-colic.csv
    
    # 下载数据集介绍
    !wget https://tianchi-media.oss-cn-beijing.aliyuncs.com/DSW/3K/horse-colic.names
    

    Step1: 库函数导入

    import numpy as np
    import pandas as pd
    # kNN分类器
    from sklearn.neighbors import KNeighborsClassifier
    # kNN数据空值填充
    from sklearn.impute import KNNImputer
    # 计算带有空值的欧式距离
    from sklearn.metrics.pairwise import nan_euclidean_distances
    # 交叉验证
    from sklearn.model_selection import cross_val_score
    # KFlod的函数
    from sklearn.model_selection import RepeatedStratifiedKFold
    from sklearn.pipeline import Pipeline
    import matplotlib.pyplot as plt
    from sklearn.model_selection import train_test_split
    

    Step2: 数据导入&分析

    2,1,530101,38.50,66,28,3,3,?,2,5,4,4,?,?,?,3,5,45.00,8.40,?,?,2,2,11300,00000,00000,2
    1,1,534817,39.2,88,20,?,?,4,1,3,4,2,?,?,?,4,2,50,85,2,2,3,2,02208,00000,00000,2
    2,1,530334,38.30,40,24,1,1,3,1,3,3,1,?,?,?,1,1,33.00,6.70,?,?,1,2,00000,00000,00000,1
    1,9,5290409,39.10,164,84,4,1,6,2,2,4,4,1,2,5.00,3,?,48.00,7.20,3,5.30,2,1,02208,00000,00000,1
    2,1,530255,37.30,104,35,?,?,6,2,?,?,?,?,?,?,?,?,74.00,7.40,?,?,2,2,04300,00000,00000,2
    ......

    数据集介绍:horse-colic.names
    数据中的'?'表示空值,如果我们使用KNN分类器,'?'不能数值,不能进行计算,因此我们需要进行数据预处理对空值进行填充。
    这里我们使用KNNImputer进行空值填充,KNNImputer填充的原来很简单,计算每个样本最近的k个样本,进行空值填充。
    我们先来看下KNNImputer的运行原理:

    Step3: KNNImputer空值填充--使用和原理介绍

    X = [[1, 2, np.nan], [3, 4, 3], [np.nan, 6, 5], [8, 8, 7]]
    imputer = KNNImputer(n_neighbors=2, metric='nan_euclidean')
    imputer.fit_transform(X)
    
    # Output:
    # array([[1. , 2. , 4. ],
    #        [3. , 4. , 3. ],
    #        [5.5, 6. , 5. ],
    #        [8. , 8. , 7. ]])
    

    带有空值的欧式距离计算公式:

    nan_euclidean_distances([[np.nan, 6, 5], [3, 4, 3]], [[3, 4, 3], [1, 2, np.nan], [8, 8, 7]])
    
    # Output:
    # array([[3.46410162, 6.92820323, 3.46410162],
    #        [0.        , 3.46410162, 7.54983444]])
    

    Step4: KNNImputer空值填充--欧式距离的计算
    样本[1, 2, np.nan] 最近的2个样本是: [3, 4, 3] [np.nan, 6, 5], 计算距离的时候使用欧式距离,只关注非空样本。 [1, 2, np.nan] 填充之后得到 [1, 2, (3 + 5) / 2] = [1, 2, 4]

    正常的欧式距离:


    knnfigB.jpg

    带有空值的欧式聚类:


    knnfigC.jpg

    只计算所有非空的值,对所有空加权到非空值的计算上,上例中,我们看到一个有3维,只有第二维全部非空, 将第一维和第三维的计算加到第二维上,所有需要乘以3。

    表格中距离度量使用的是带有空值欧式距离计算相似度,使用简单的加权平均进行填充。


    knnfigD.jpg
    # load dataset, 将?变成空值
    input_file = './horse-colic.csv'
    df_data = pd.read_csv(input_file, header=None, na_values='?')
    
    # 得到训练数据和label, 第23列表示是否发生病变, 1: 表示Yes; 2: 表示No. 
    data = df_data.values
    ix = [i for i in range(data.shape[1]) if i != 23]
    X, y = data[:, ix], data[:, 23]
    
    # 查看所有特征的缺失值个数和缺失率
    for i in range(df_data.shape[1]):
        n_miss = df_data[[i]].isnull().sum()
        perc = n_miss / df_data.shape[0] * 100
        if n_miss.values[0] > 0:
            print('>Feat: %d, Missing: %d, Missing ratio: (%.2f%%)' % (i, n_miss, perc))
    
    # 查看总的空值个数
    print('KNNImputer before Missing: %d' % sum(np.isnan(X).flatten()))
    # 定义 knnimputer
    imputer = KNNImputer()
    # 填充数据集中的空值
    imputer.fit(X)
    # 转换数据集
    Xtrans = imputer.transform(X)
    # 打印转化后的数据集的空值
    print('KNNImputer after Missing: %d' % sum(np.isnan(Xtrans).flatten()))
    
    # Output:
    # >Feat: 0, Missing: 1, Missing ratio: (0.33%)
    # >Feat: 3, Missing: 60, Missing ratio: (20.00%)
    # >Feat: 4, Missing: 24, Missing ratio: (8.00%)
    # >Feat: 5, Missing: 58, Missing ratio: (19.33%)
    # >Feat: 6, Missing: 56, Missing ratio: (18.67%)
    # >Feat: 7, Missing: 69, Missing ratio: (23.00%)
    # >Feat: 8, Missing: 47, Missing ratio: (15.67%)
    # >Feat: 9, Missing: 32, Missing ratio: (10.67%)
    # >Feat: 10, Missing: 55, Missing ratio: (18.33%)
    # >Feat: 11, Missing: 44, Missing ratio: (14.67%)
    # >Feat: 12, Missing: 56, Missing ratio: (18.67%)
    # >Feat: 13, Missing: 104, Missing ratio: (34.67%)
    # >Feat: 14, Missing: 106, Missing ratio: (35.33%)
    # >Feat: 15, Missing: 247, Missing ratio: (82.33%)
    # >Feat: 16, Missing: 102, Missing ratio: (34.00%)
    # >Feat: 17, Missing: 118, Missing ratio: (39.33%)
    # >Feat: 18, Missing: 29, Missing ratio: (9.67%)
    # >Feat: 19, Missing: 33, Missing ratio: (11.00%)
    # >Feat: 20, Missing: 165, Missing ratio: (55.00%)
    # >Feat: 21, Missing: 198, Missing ratio: (66.00%)
    # >Feat: 22, Missing: 1, Missing ratio: (0.33%)
    # KNNImputer before Missing: 1605
    # KNNImputer after Missing: 0
    

    Step5: 基于pipeline模型训练&可视化

    什么是Pipeline, 我这里直接翻译成数据管道。任何有序的操作有可以看做pipeline,例如工厂流水线,对于机器学习模型来说,这就是数据流水线。 是指数据通过管道中的每一个节点,结果除了之后,继续流向下游。对于我们这个例子,数据是有空值,我们会有一个KNNImputer节点用来填充空值, 之后继续流向下一个kNN分类节点,最后输出模型。

    knnfigE.jpg
    results = list()
    strategies = [str(i) for i in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 16, 18, 20, 21]]
    for s in strategies:
        # create the modeling pipeline
        pipe = Pipeline(steps=[('imputer', KNNImputer(n_neighbors=int(s))), ('model', KNeighborsClassifier())])
        # 数据多次随机划分取平均得分
        scores = []
        for k in range(20):
            # 得到训练集合和验证集合, 8: 2
            X_train, X_test, y_train, y_test = train_test_split(Xtrans, y, test_size=0.2)
            pipe.fit(X_train, y_train)
            # 验证model
            score = pipe.score(X_test, y_test)
            scores.append(score)
        # 保存results
        results.append(np.array(scores))
        print('>k: %s, Acc Mean: %.3f, Std: %.3f' % (s, np.mean(scores), np.std(scores)))
    # print(results)
    # plot model performance for comparison
    plt.boxplot(results, labels=strategies, showmeans=True)
    plt.show()
    
    # Output:
    # >k: 1, Acc Mean: 0.800, Std: 0.031
    # >k: 2, Acc Mean: 0.821, Std: 0.041
    # >k: 3, Acc Mean: 0.833, Std: 0.053
    # >k: 4, Acc Mean: 0.824, Std: 0.037
    # >k: 5, Acc Mean: 0.802, Std: 0.038
    # >k: 6, Acc Mean: 0.811, Std: 0.030
    # >k: 7, Acc Mean: 0.797, Std: 0.056
    # >k: 8, Acc Mean: 0.819, Std: 0.044
    # >k: 9, Acc Mean: 0.820, Std: 0.032
    # >k: 10, Acc Mean: 0.815, Std: 0.046
    # >k: 15, Acc Mean: 0.818, Std: 0.037
    # >k: 16, Acc Mean: 0.811, Std: 0.048
    # >k: 18, Acc Mean: 0.809, Std: 0.043
    # >k: 20, Acc Mean: 0.810, Std: 0.038
    # >k: 21, Acc Mean: 0.828, Std: 0.038
    
    knnfigF.jpg

    Step 6: 结果分析

    我们的实验是每个k值下,随机切分20次数据, 从上述的图片中, 根据k值的增加,我们的测试准确率会有先上升再下降再上升的过程。 [3, 5]之间是一个很好的取值,上文我们提到,k很小的时候会发生过拟合,k很大时候会发生欠拟合,当遇到第一下降节点,此时我们可以 简单认为不在发生过拟合,取当前的k值即可。

    2.5 KNN原理介绍

    k近邻方法是一种惰性学习算法,可以用于回归和分类,它的主要思想是投票机制,对于一个测试实例x, 我们在有标签的训练数据集上找到和最相近的k个数据,用他们的label进行投票,分类问题则进行表决投票,回归问题使用加权平均或者直接平均的方法。knn算法中我们最需要关注两个问题:k值的选择和距离的计算。 kNN中的k是一个超参数,需要我们进行指定,一般情况下这个k和数据有很大关系,都是交叉验证进行选择,但是建议使用交叉验证的时候,k∈[2,20],使用交叉验证得到一个很好的k值。

    k值还可以表示我们的模型复杂度,当k值越小意味着模型复杂度变大,更容易过拟合,(用极少数的样例来绝对这个预测的结果,很容易产生偏见,这就是过拟合)。我们有这样一句话,k值越多学习的估计误差越小,但是学习的近似误差就会增大。


    距离/相似度的计算:

    样本之间的距离的计算,我们一般使用对于一般使用Lp距离进行计算。当p=1时候,称为曼哈顿距离(Manhattan distance),当p=2时候,称为欧氏距离(Euclidean distance),当p=∞时候,称为极大距离(infty distance), 表示各个坐标的距离最大值,另外也包含夹角余弦等方法。

    一般采用欧式距离较多,但是文本分类则倾向于使用余弦来计算相似度。


    knnfigG.jpg
    学习感悟:

    好难啊!这部分没有学明白~~~

    相关文章

      网友评论

        本文标题:Task03-K近邻(k-nearest neighbors)-

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