Task09基于模拟数据集的KNN回归、基于马绞痛数据集的KNN数据预处理+KNN分类pipeline

Task09 基于模拟数据集的KNN回归、基于马绞痛数据集的KNN数据预处理+KNN分类pipeline

一、学习内容概括

学习资料:

1.阿里云天池:https://tianchi.aliyun.com/specials/promotion/aicampml?invite_channel=1

2.sklearnAPI:https://scikit-learn.org/stable/modules/classes.html

3.matplotlib.pyplot:https://matplotlib.org/api/pyplot_summary.html

4.pandasAPI:https://pandas.pydata.org/pandas-docs/version/0.21/index.html

二、具体学习内容

  1. 模拟数据集--kNN回归

    • Step1: 库函数导入
    • Step2: 数据导入&分析
    • Step3: 模型训练&可视化
  2. 马绞痛数据--kNN数据预处理+kNN分类pipeline

    • Step1: 库函数导入
    • Step2: 数据导入&分析
    • Step3: KNNImputer空值填充--使用和原理介绍
    • Step4: KNNImputer空值填充--欧式距离的计算
    • Step5: 基于pipeline模型预测&可视化

1 模拟数据集---KNN回归

step1:库函数导入

# step1:库函数导入
#Demo来自sklearn官网
import numpy as np
import matplotlib.pyplot as plt
from sklearn.neighbors import KNeighborsRegressor

step2:数据导入&分析 

# Step2: 数据导入&分析
np.random.seed(0)
# 随机生成40个(0, 1)之前的数,乘以5,再进行升序
## numpy.random.rand(d0, d1, …, dn)的随机样本位于[0, 1)中。 
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:模型训练&预测可视化 

# Step3: 模型训练&预测可视化
# 设置多个k近邻进行比较
n_neighbors = [1, 3, 5, 8, 10, 40]
# 创建一个新图形,宽度为10英寸,高度为20英寸
plt.figure(figsize=(10,20))
for i, k in enumerate(n_neighbors):
    # 默认使用加权平均进行计算predictor
    # 默认度量标准为minkowski,p = 2等效于标准欧几里德度量标准。
    clf = KNeighborsRegressor(n_neighbors=k, p=2, metric="minkowski")
    # 训练
    clf.fit(X, y)
    # 预测
    y_ = clf.predict(T)
    # subplot:在当前图形上添加一个子图。三个整数值6,1,i+1描述子图位置。
    plt.subplot(6, 1, i + 1)
    # 绘制y与X的散点图
    plt.scatter(X, y, color='red', label='data')
    # 绘制y_对T的线条
    plt.plot(T, y_, color='navy', label='prediction')
    # 设置轴属性:将限制设置得足够大以显示所有数据,然后禁用进一步的自动缩放。
    plt.axis('tight')
    # 在轴上放置图例。不设置参数则自动检测要在图例中显示的元素
    plt.legend()
    plt.title("KNeighborsRegressor (k = %i)" % (k))

# 调整子图之间及其周围的填充
plt.tight_layout()
plt.show()

参考资料:

matplotlib.pyplot.figure:创建一个新图形,或激活一个现有图形。https://matplotlib.org/api/_as_gen/matplotlib.pyplot.figure.html#matplotlib.pyplot.figure

sklearn.neighbors.KNeighborsRegressor:基于k近邻的回归。https://scikit-learn.org/stable/modules/generated/sklearn.neighbors.KNeighborsRegressor.html#sklearn.neighbors.KNeighborsRegressor

matplotlib.pyplot.subplot:在当前图形上添加一个子图。https://matplotlib.org/api/_as_gen/matplotlib.pyplot.subplot.html#matplotlib.pyplot.subplot

matplotlib.pyplot.scatteryx的散点图,其中标记大小和/或颜色不同。https://matplotlib.org/api/_as_gen/matplotlib.pyplot.scatter.html#matplotlib.pyplot.scatter

matplotlib.pyplot.plot:将y对x绘制为线条和/或标记。https://matplotlib.org/api/_as_gen/matplotlib.pyplot.plot.html#matplotlib.pyplot.plot

matplotlib.pyplot.axis:获取或设置一些轴属性的便捷方法。https://matplotlib.org/api/_as_gen/matplotlib.pyplot.axis.html#matplotlib.pyplot.axis

matplotlib.pyplot.legend:在轴上放置图例。https://matplotlib.org/api/_as_gen/matplotlib.pyplot.legend.html#matplotlib.pyplot.legend

模型分析:

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

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

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

 

2 马绞痛数据--kNN数据预处理+kNN分类pipeline

下载数据集:

# 下载需要用到的数据集
!wget https://tianchi-media.oss-cn-beijing.aliyuncs.com/DSW/3K/horse-colic.csv
--2020-12-23 20:09:28--  https://tianchi-media.oss-cn-beijing.aliyuncs.com/DSW/3K/horse-colic.csv
Resolving tianchi-media.oss-cn-beijing.aliyuncs.com (tianchi-media.oss-cn-beijing.aliyuncs.com)... 47.95.85.21
Connecting to tianchi-media.oss-cn-beijing.aliyuncs.com (tianchi-media.oss-cn-beijing.aliyuncs.com)|47.95.85.21|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 25151 (25K) [text/csv]
Saving to: ‘horse-colic.csv’

100%[======================================>] 25,151      --.-K/s   in 0.03s   

2020-12-23 20:09:28 (732 KB/s) - ‘horse-colic.csv’ saved [25151/25151]

得到一个文件:horse-colic.csv

下载数据集介绍:

# 下载数据集介绍
!wget https://tianchi-media.oss-cn-beijing.aliyuncs.com/DSW/3K/horse-colic.names
--2020-12-23 20:09:49--  https://tianchi-media.oss-cn-beijing.aliyuncs.com/DSW/3K/horse-colic.names
Resolving tianchi-media.oss-cn-beijing.aliyuncs.com (tianchi-media.oss-cn-beijing.aliyuncs.com)... 47.95.85.21
Connecting to tianchi-media.oss-cn-beijing.aliyuncs.com (tianchi-media.oss-cn-beijing.aliyuncs.com)|47.95.85.21|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 9011 (8.8K) [application/octet-stream]
Saving to: ‘horse-colic.names’

100%[======================================>] 9,011       --.-K/s   in 0s      

2020-12-23 20:09:49 (93.3 MB/s) - ‘horse-colic.names’ saved [9011/9011]

得到一个文件:horse-colic.names

step1:库函数导入 

# 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:数据导入&分析

数据集horse-colic.csv:

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空值填充---使用和原理介绍

# Step3: KNNImputer空值填充---使用和原理介绍
# nan=非数字。np.nan是非数字(NaN)的IEEE 754浮点表示。
X = [[1, 2, np.nan], [3, 4, 3], [np.nan, 6, 5], [8, 8, 7]]
# n_neighbors:用于插补的相邻样本数。metric:用于搜索邻居的距离度量。
imputer = KNNImputer(n_neighbors=2, metric='nan_euclidean')
# fit_transform:适合数据,然后对其进行转换。
imputer.fit_transform(X)

运行结果:

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

参考资料:

numpy.nan:非数字(NaN)的IEEE 754浮点表示。https://numpy.org/doc/stable/reference/constants.html?highlight=nan#numpy.nan

sklearn.impute.KNNImputer:使用k近邻来完成缺失值的估算。每个样本的缺失值都是使用n_neighbors训练集中找到的最近邻的平均值估算的 。如果两个都不缺失的特征都接近,则两个样本接近。https://scikit-learn.org/stable/modules/generated/sklearn.impute.KNNImputer.html#sklearn.impute.KNNImputer

# 带有空值的欧式距离计算公式
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, 3.46410162]])

结果分析:

3.46410162是[np.nan, 6, 5]与[3, 4, 3]之间的欧式距离;6.92820323是[np.nan, 6, 5]与[1, 2, np.nan]之间的欧式距离;3.46410162是[np.nan, 6, 5]与[8, 8, 7]之间的欧式距离。

0. 是[3, 4, 3]与[3, 4, 3]之间的欧式距离;3.46410162是[3, 4, 3]与[1, 2, np.nan]之间的欧式距离;3.46410162是[3, 4, 3]与[8, 8, 7]之间的欧式距离。

参考资料:

sklearn.metrics.pairwise.nan_euclidean_distances(X,Y = None,*,squared = False,missing_values = nan,copy = True):在缺少值的情况下计算欧几里得距离。https://scikit-learn.org/stable/modules/generated/sklearn.metrics.pairwise.nan_euclidean_distances.html#sklearn.metrics.pairwise.nan_euclidean_distances 

计算X和Y中每对样本之间的欧式距离,如果Y = None,则假定Y = X。当计算一对样本之间的距离时,此公式将忽略两个样本中均缺少值的要素坐标,并按比例增加其余坐标的权重:dict(x,y)=sqrt(权重*离当前坐标的平方距离),其中,权重=总坐标数/当前坐标数。

例子:[3, na, na, 6][1, na, 4, 5]之间的距离和是:\sqrt{\frac{4}{2}((3-1)^{2}+(6-5)^{2})}

如果缺少所有坐标或没有共同的当前坐标,则为该对返回NaN。

Step4:KNNImputer空值填充---欧式距离的计算

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

带有空值的欧式距离举例:x=[1,2,np.nan],y=[np.nan,6,5] \sqrt{\frac{3}{1}(2-6)^{2}}=\sqrt{48}=6.928 

在计算带有空值的欧式距离时,忽略x和y中缺值的维度,只计算有值的共同维度。如x忽略第三维、y忽略第一维,只计算第二维,x拿出一个2,y拿出一个6。计算有值的共同维度时,要乘以一个权重,这个权重的分子代表y的总维度数3个,权重的分母代表当前用到的维度数1个(第二维)。再看前面的例子,[3, na, na, 6][1, na, 4, 5]都是4维,x忽略第二维、第三维,y忽略第二维,只计算有值的第一维和第四维。x拿出第一维的3,y拿出第一维的1,二者求差的平方;x拿出第四维的6,y拿出第四维的5,二者求差的平方。然后这两个平方求和,再乘以一个权重,这个权重的分子是y的总维度数4个,权重的分母是当前用到的维度数2个。

# Step4:KNNImputer空值填充
# load dataset, 将?变成空值
input_file = './horse-colic.csv'
# read_csv将CSV文件读取到DataFrame中。df_data.shape(300,28)
df_data = pd.read_csv(input_file, header=None, na_values='?')

# 得到训练数据和label, 第23列表示是否发生病变, 1: 表示Yes; 2: 表示No. 
data = df_data.values #data.shape(300,28)
ix = [i for i in range(data.shape[1]) if i != 23] #x是从0到27(除去23)的一维数组,共27个数
X, y = data[:, ix], data[:, 23] #X.shape(300, 27);y.shape(300,)

# 查看所有特征的缺失值个数和缺失率
for i in range(df_data.shape[1]):
    #当前行的所有特质的缺失值个数n_miss
    n_miss = df_data[[i]].isnull().sum()
    #当前行的所有特质的缺失率perc
    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)
# 转换数据集
Xtrains = imputer.transform(X)
# 打印转化后的数据集的空值
print('KNNImputer after Missing: %d' % sum(np.isnan(Xtrains).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

参考资料:

pandas.read_csv:将CSV(逗号分隔)文件读取到DataFrame中。https://pandas.pydata.org/pandas-docs/version/0.21/generated/pandas.read_csv.html?highlight=read_csv#pandas.read_csv

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

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

# Step5:基于pipeline模型训练&可视化
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
    # steps是元祖列表,实现拟合变换
    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(Xtrains, 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

sklearn.pipeline.Pipeline:带有最终估算器的变换数据管道。https://scikit-learn.org/stable/modules/generated/sklearn.pipeline.Pipeline.html#sklearn.pipeline.Pipeline 

结果分析:

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

 

 

 

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值