KNN回归,缺失值处理以及pipeline

上一节我们用knn在鸢尾花数据集上做了分类,现在我们就来用knn做回归预测。

1.1 模拟数据集——knn回归

  1. 首先导入需要用到的包
#Demo来自sklearn官网
import numpy as np
import matplotlib.pyplot as plt
from sklearn.neighbors import KNeighborsRegressor
  1. 创建训练样本,标签与测试集
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],从第一个到最后,步长为5,即:此处只给索引为0,,5,10,15,20,25,30,35的标签值增加噪声)
y[::5] += 1 * (0.5 - np.random.rand(8))
  1. 模型训练、预测及可视化
# #############################################################################
# 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()

在这里插入图片描述
在这里插入图片描述

  1. 结果分析

当k=1时,预测的结果只和最近的一个训练样本相关,从预测曲线中可以看出当k很小时候很容易发生过拟合。

当k=40时,预测的结果和最近的40个样本相关,因为我们只有40个样本,此时是所有样本的平均值,此时所有预测值都是均值,很容易发生欠拟合。

一般情况下,使用knn的时候,根据数据规模我们会从[3, 20]之间进行尝试,选择最好的k,例如上图中的[3, 10]相对1和40都是还不错的选择。

1.2 马绞痛数据–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
  1. 导入我们需要用到的包
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
  1. 数据分析

我们查看马绞痛数据集,发现数据集里面存在“?”,’?‘表示空值,但是在我们使用knn分类器的时候,空值是无法计算的,所以我们先要对数据进行预处理,即:对空值进行填充。

这里我们使用KNNImputer进行空值填充,KNNImputer填充的原来很简单,计算每个样本最近的k个样本,进行空值填充。

下面来看看KNNImputer的运行原理:.

  1. 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)

# array([[1. , 2. , 4. ],
#        [3. , 4. , 3. ],
#        [5.5, 6. , 5. ],
#        [8. , 8. , 7. ]])

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

# 分别计算[np.nan, 6, 5], [3, 4, 3]到点[3, 4, 3], [1, 2, np.nan], [8, 8, 7]的距离
nan_euclidean_distances([[np.nan, 6, 5], [3, 4, 3]], [[3, 4, 3], [1, 2, np.nan], [8, 8, 7]])

# array([[3.46410162, 6.92820323, 3.46410162],
#       [0.        , 3.46410162, 7.54983444]])
  1. 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]

正常的欧式距离
x = [ 3 , 4 , 3 ] , y = [ 8 , 8 , 7 ] ( 3 − 8 ) 2 + ( 4 − 8 ) 2 + ( 3 − 7 ) 2 = 33 = 7.55 x = [3, 4, 3], y = [8, 8, 7] \\ \sqrt{(3-8)^2 + (4-8)^2 + (3-7)^2} = \sqrt{33} = 7.55 x=[3,4,3],y=[8,8,7](38)2+(48)2+(37)2 =33 =7.55

带有空值的欧式聚类
x = [ 1 , 2 , n p . n a n ] , y = [ n p . n a n , 6 , 5 ] 3 1 ( 2 − 6 ) 2 = 48 = 6.928 x = [1, 2, np.nan], y = [np.nan, 6, 5] \\ \sqrt{\frac{3}{1}(2-6)^2} = \sqrt{48} = 6.928 x=[1,2,np.nan],y=[np.nan,6,5]13(26)2 =48 =6.928
只计算所有非空的值,对所有空加权到非空值的计算上,上例中,我们看到一个有3维,只有第二维全部非空,
将第一维和第三维的计算加到第二维上,所有需要乘以3。

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

带有空值的样本最相近的样本1最相近的样本2填充之后的值
[1, 2, np.nan][3, 4, 3]; 3.46[np.nan, 6, 5]; 6.93[1, 2, 4]
[np.nan, 6, 5][3, 4, 3]; 3.46[8, 8, 7]; 3.46[5.5, 6, 5]

下面我们就来对缺失数据做填充:

# load dataset, 将?变成空值
input_file = './horse-colic.csv'
df_data = pd.read_csv(input_file, header=None, na_values='?')

# 得到训练数据和label, 第23列(X对应的列)表示是否发生病变, 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]  # X:特征, y:标签

# 查看所有特征的缺失值个数和缺失率
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()))

# >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

可以看到处理前有1605个缺失值,处理后缺失值个数变为0了。

  1. 基于pipeline模型训练&可视化

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

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()

# >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

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

参考:天池龙珠计划——机器学习训练营

  • 3
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值