信用评分卡 DAY5-7

演练:拆分数据集

任务目标

  • 使用多种方法将数据集拆分成训练集、验证集和测试集
  • 确保拆分后的数据集是随机组合的,而不是顺序按比例截断的
  • 使用K折拆分的方法以生成适合交叉验证的数据集
  • 观察拆分后的数据集中,样本标签分类的不均衡性,并找到恰当的方法确保均衡性

任务描述

针对sklearn自带了iris(鸢尾花)数据集,完成下列任务:

  • 熟悉iris数据集:查看iris数据集结构,统计样本标签分类的分布情况
  • 按比例拆分训练集和测试集:分别使用train_test_split和自定义方式,按8:2拆分训练集和测试集
  • 划分K折交叉验证数据集:获取5这拆分数据集,并确保样本标签分类的均衡

工具和方法

  • sklearn.datasets
  • collections.Counter
  • np.random.shuffle、np.random.permutation
  • sklearn.model_selection.train_test_split
  • sklearn.model_selection.KFold、sklearn.model_selection.StratifiedKFold、sklearn.model_selection.StratifiedShuffleSplit

【任务0】 准备工作

本演练准备工作包括:

  • 预设随机数种子以使结果可重现
    • 设置各随机数的种子为固定值(100),以便产生的随机序列可以重现
    • 后续代码中如果涉及到随机种子的设置,应统一设置为random_state
  • 装载iris数据集
    • 使用sklearn.datasets.load_iris可直接装载sklearn内置的iris数据集
from sklearn.datasets import load_iris
iris = load_iris()    # 导入鸢尾花数据集

import random
import numpy as np
random_state = 100
random.seed(random_state)
np.random.seed(random_state)

【任务1】 熟悉iris数据集

【子任务项1-1】 查看iris数据集结构

本任务项用于查看iris数据集的样本数、样本标签分类、特征名称、标签名称等属性,具体包括:

  • 获取特征集和标签集:分别通过data和target属性获取
  • 获取特征和标签的名称:分别通过feature_names和target_names属性获取
  • 输出特征矩阵和标签向量的维度
  • 查看前5个样本的特征
  • 查看各个样本的标签
iris_data = iris.data    # 获取特征数据
iris_target = iris.target    # 获取标签数据
feature_names = iris.feature_names    # 获取特征列的名称
target_names = iris.target_names    # 获取标签的分类名称

print("特征名称:", feature_names)
print("标签分类:", target_names)
print("特征矩阵维度:", iris_data.shape)
print("标签向量维度:", iris_target.shape)
print("特征前5行:\n", iris_data[:5])
print("标签分布:", iris_target)
特征名称: ['sepal length (cm)', 'sepal width (cm)', 'petal length (cm)', 'petal width (cm)']
标签分类: ['setosa' 'versicolor' 'virginica']
特征矩阵维度: (150, 4)
标签向量维度: (150,)
特征前5行:
 [[5.1 3.5 1.4 0.2]
 [4.9 3.  1.4 0.2]
 [4.7 3.2 1.3 0.2]
 [4.6 3.1 1.5 0.2]
 [5.  3.6 1.4 0.2]]
标签分布: [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2
 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
 2 2]
说明
  • 本数据集包括4个特征,3个分类结果,150个样本
  • 标签分类值有0、1、2三种,分别代表setosa、versicolor和virginica
  • 从标签分布可以看到,样本是按照标签分类来排序的。这意味着在拆分数据集时,不能之直接采用顺序方式截断,否则会产生严重的不均衡
【子任务项1-2】 统计样本标签分类的分布情况

本任务项用于统计每种标签的个数。可以通过collections.Counter来对计数。

from collections import Counter

counter = Counter(iris_target)
print("样本中标签分类的个数统计:\n", counter)
样本中标签分类的个数统计:
 Counter({0: 50, 1: 50, 2: 50})
说明

每种标签值的样本数各有50个,说明样本分类分布是比较均衡的

【任务2】 按比例拆分训练集和测试集

【子任务项2-1】 使用train_test_split按8:2拆分训练集和测试集

本任务项将使用sklearn.model_selection.train_test_split,根据指定的测试集比例,自动拆分训练集和测试集。并且查看拆分后的数据子集中样本标签分类的均衡性。

  • 需要同时传入特征集和标签集,以便train_test_split对它们同步进行拆分处理
  • train_test_split将返回4个值,分别表示训练特征集、测试特征集、训练标签集、测试标签集
  • 注意设置random_state为固定值以便能获得可重现的划分结果
from sklearn.model_selection import train_test_split  

test_size = 0.2    # 20%比例为测试集
X_train , X_test, y_train , y_test  = train_test_split(iris_data, iris_target, test_size=test_size, random_state=random_state)
print("训练集特征维度:", X_train.shape)
print("训练集标签维度:", y_train.shape)
print("测试集特征维度:", X_test.shape)
print("测试集标签维度:", y_test.shape)
print("训练集特征前5行:\n", X_train[:5])
print("训练集标签前5个:\n", y_train[:5])

# 计算训练集中的样本分类分布
counter = Counter(y_train)
print("训练集样本标签分类的个数统计:\n", counter)
训练集特征维度: (120, 4)
训练集标签维度: (120,)
测试集特征维度: (30, 4)
测试集标签维度: (30,)
训练集特征前5行:
 [[5.5 2.4 3.7 1. ]
 [5.7 2.8 4.1 1.3]
 [6.  2.2 5.  1.5]
 [4.8 3.  1.4 0.1]
 [5.4 3.9 1.3 0.4]]
训练集标签前5个:
 [1 1 2 0 0]
训练集样本标签分类的个数统计:
 Counter({1: 44, 0: 39, 2: 37})
说明
  • 拆分后训练集样本120个,测试集样本30个。可见测试集占总样本数的20%
  • 训练集前5个样本的标签分别为[1 1 2 0 0],并不是原始数据集中的前5个([0 0 0 0 0]),可见函数在拆分数据集时,进行了随机混洗/乱序处理。这对于样本顺序有规律的数据集来说是非常必要的
  • 从训练集样本标签分类统计结果可以看到,经过拆分后,训练集中各个不同标签类别的样本数(分别为39,44和37个)并不完全相同,但比较接近。这一方面说明乱序的作用,另一方面也说明了该函数不能精确保证样本的标签分布的均衡性。一般来说,这种均衡性的偏差是可以接受的。
  • 因为设置了random_state为固定值,所以重复运行上述代码,将得到相同的结果(可观察前5行输出)
【子任务项2-2】 使用自定义的方式按8:2拆分训练集和测试集

本任务项以自定义代码的方式,模拟上述train_test_split函数来实现数据集的拆分。具体步骤是:

  • 根据样本总数,将每个样本从0开始整数编号,形成一个数组
  • 将所有编号打乱顺序,形成乱序后的数组。这可以通过np.random.shuffle或者np.random.permutation来实现
  • 取编号数组中的前80%元素,作为训练样本编号数组;后20%作为测试样本编号数组
  • 从原始数据集中,根据训练样本编号数组,筛选出训练样本
  • 从原始数据集中,根据测试样本编号数组,筛选出测试样本

(1)生成乱序的样本编号数组

indices = np.arange(len(iris_data))    # 生成0~149整数序列
print("样本编号序列,打乱前:", indices[:10])    # 打印前10个元素
np.random.shuffle(indices)
print("样本编号序列,打乱后:", indices[:10])    # 打印前10个元素

# 也可以用permutation函数,直接将整数序列乱序输出:
# indices = np.random.permutation(len(iris_data)) 
样本编号序列,打乱前: [0 1 2 3 4 5 6 7 8 9]
样本编号序列,打乱后: [128  11 118  15 123 135  32   1 116  45]
说明

乱序后生成的样本编号,已经比较随机。因此,如果从编号数组中取前80%作为训练样本的编号,能够保证随机性

(2)从原始数据集中筛选样本进入训练集和测试集

# 按照8:2的比例,计算训练样本和测试样本数量分配
split_ratio = 0.8
train_nums = int(split_ratio * len(iris_data))
train_indices = indices[:train_nums]    # indices的前train_nums个元素作为训练样本的索引编号
test_indices = indices[train_nums:]    # indices的train_nums个之后的元素作为测试样本的索引编号


X_train = iris_data[train_indices]    # 从iris_data中根据指定的训练样本的索引号,取出所有训练样本
y_train = iris_target[train_indices]    
X_test = iris_data[test_indices]     # 从iris_data中根据指定的测试样本的索引号,取出所有测试样本
y_test = iris_target[test_indices]    

print("训练集特征维度:", X_train.shape)
print("训练集标签维度:", y_train.shape)
print("测试集特征维度:", X_test.shape)
print("测试集标签维度:", y_test.shape)
print("训练集特征前5行:\n", X_train[:5])
训练集特征维度: (120, 4)
训练集标签维度: (120,)
测试集特征维度: (30, 4)
测试集标签维度: (30,)
训练集特征前5行:
 [[6.4 2.8 5.6 2.1]
 [4.8 3.4 1.6 0.2]
 [7.7 2.6 6.9 2.3]
 [5.7 4.4 1.5 0.4]
 [6.3 2.7 4.9 1.8]]

(3)查看训练集样本的均衡性

# 计算训练集中的样本分类分布
counter = Counter(y_train)
print("训练集样本标签分类的个数统计:\n", counter)
训练集样本标签分类的个数统计:
 Counter({2: 42, 0: 42, 1: 36})
说明

可见,采用自定义的拆分方式后,训练集的样本分布基本上是均衡的

【任务3】 划分K折交叉验证数据集

【子任务项3-1】 使用KFold获取5折拆分数据集

本任务项采用KFold函数来创建5折拆分的数据集。

  • 通过n_splits参数指定 K K K
  • 使用split方法对指定数据集进行拆分,拆分的结果是 K K K个划分结果,每个划分结果中包括训练集的样本编号数组和测试集的样本编号数组

(1)打印了每个划分结果中训练样本(前5个)和测试样本的编号数组

from sklearn.model_selection import KFold

kf = KFold(n_splits=5)    # 指定做5折拆分(也就是生成5个分组的数据集)
fold_index = 1
for train_indices, test_indices in kf.split(iris_data):
    print("第%d个划分结果,训练样本编号(前5个):%s, 测试样本编号:%s" % (fold_index, train_indices[:5], test_indices))
    fold_index += 1
第1个划分结果,训练样本编号(前5个):[30 31 32 33 34], 测试样本编号:[ 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
 24 25 26 27 28 29]
第2个划分结果,训练样本编号(前5个):[0 1 2 3 4], 测试样本编号:[30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
 54 55 56 57 58 59]
第3个划分结果,训练样本编号(前5个):[0 1 2 3 4], 测试样本编号:[60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
 84 85 86 87 88 89]
第4个划分结果,训练样本编号(前5个):[0 1 2 3 4], 测试样本编号:[ 90  91  92  93  94  95  96  97  98  99 100 101 102 103 104 105 106 107
 108 109 110 111 112 113 114 115 116 117 118 119]
第5个划分结果,训练样本编号(前5个):[0 1 2 3 4], 测试样本编号:[120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
 138 139 140 141 142 143 144 145 146 147 148 149]
说明

从测试集样本的编号可以看出:

  • 指定 K K K折(本例 K = 5 K=5 K=5)拆分后,将原始数据集按顺序平均分成 K K K个部分。
  • 一共生成 K K K组数据。对于其中的第 i i i组数据,使用第 i i i个部分的数据作为测试集,剩余部分数据作为作为训练集
  • 在本例第1个划分结果中,编号为0~29的样本被划入了测试集,其余样本划入训练集;第2个划分结果中,编号为30~59的样本被划入了测试集。依次类推

(2)获取每个Fold的训练特征集、训练标签集、测试特征集、测试标签集,并统计训练集样本标签的分类情况

fold_index = 1
for train_indices, test_indices in kf.split(iris_data):
    X_train_fold_data = iris_data[train_indices]    # 训练特征集
    y_train_fold_data = iris_target[train_indices]    # 训练标签集
    X_test_fold_data = iris_data[test_indices]    # 测试特征集
    y_test_fold_data = iris_target[test_indices]    # 测试标签集
    # 计算训练集中的样本分类分布
    counter = Counter(y_train_fold_data)
    print("第%d个划分结果,训练样本中标签分类的个数统计%s" % (fold_index, counter))
    fold_index += 1
第1个划分结果,训练样本中标签分类的个数统计Counter({1: 50, 2: 50, 0: 20})
第2个划分结果,训练样本中标签分类的个数统计Counter({2: 50, 1: 40, 0: 30})
第3个划分结果,训练样本中标签分类的个数统计Counter({0: 50, 2: 50, 1: 20})
第4个划分结果,训练样本中标签分类的个数统计Counter({0: 50, 1: 40, 2: 30})
第5个划分结果,训练样本中标签分类的个数统计Counter({0: 50, 1: 50, 2: 20})
说明
  • 在上面的训练样本标签分类个数统计中,每个划分结果中,训练集分类标签为0,1,2的样本数差别很大,样本分类很不均衡
  • 这是因为KFold默认情况下不会进行乱序处理,它将按原始样本的顺序进行截断
  • 如果必须进行乱序处理,应在构造KFold对象时传入:shuffle=True,如下面的代码所示。

(3)KFold的乱序+N折拆分

kf = KFold(n_splits=5, shuffle=True)    # 指定做5折拆分,并且采取乱序处理
fold_index = 1
for train_indices, test_indices in kf.split(iris_data):
    print("第%d个划分结果训练样本编号(前5个):%s, 测试样本编号:%s" % (fold_index, train_indices[:5], test_indices))
    y_train_fold_data = iris_target[train_indices]    # 训练标签集
    counter = Counter(y_train_fold_data)
    print("第%d个划分结果训练样本中标签分类的个数统计%s" % (fold_index, counter))
    fold_index += 1
第1组训练样本编号(前5个):[0 1 2 3 4], 测试样本编号:[  5   6   7   8  18  20  21  24  27  33  43  46  60  72  75  79  85  89
  90  94 103 106 110 111 120 126 129 136 142 146]
第1组训练样本中标签分类的个数统计Counter({1: 42, 2: 40, 0: 38})
第2组训练样本编号(前5个):[0 2 3 5 6], 测试样本编号:[  1   4  10  12  15  19  23  35  39  45  50  56  58  64  69  76  77  84
  88  91  97 104 112 119 123 127 132 143 145 149]
第2组训练样本中标签分类的个数统计Counter({2: 41, 0: 40, 1: 39})
第3组训练样本编号(前5个):[1 2 3 4 5], 测试样本编号:[  0  13  14  26  29  31  32  36  40  41  52  59  66  68  71  73  78  86
  92  95 102 107 109 131 133 137 140 141 144 148]
第3组训练样本中标签分类的个数统计Counter({0: 40, 1: 40, 2: 40})
第4组训练样本编号(前5个):[0 1 2 4 5], 测试样本编号:[  3   9  16  34  37  42  44  47  49  54  61  62  65  70  80  83  93  96
  98  99 100 105 115 118 121 124 125 128 139 147]
第4组训练样本中标签分类的个数统计Counter({0: 41, 2: 40, 1: 39})
第5组训练样本编号(前5个):[0 1 3 4 5], 测试样本编号:[  2  11  17  22  25  28  30  38  48  51  53  55  57  63  67  74  81  82
  87 101 108 113 114 116 117 122 130 134 135 138]
第5组训练样本中标签分类的个数统计Counter({0: 41, 1: 40, 2: 39})
说明
  • 此时可以看到,每个Fold中,样本标签类型的数量比较接近,均衡性变得更好
  • 后面介绍StratifiedKFold方法可以直接实现这一功能
【子任务项3-2】 使用StratifiedKFold获得的5折数据集

本任务项使用StratifiedKFold达到与上述KFold乱序K折拆分的效果。

  • StratifiedKFold将自动计算原始样本分类标签的比例,较为均衡的将不同分类的样本拆分到训练集和测试集中
  • 需要分别传入原始特征集和标签集,以便StratifiedKFold将统计不同标签结果
  • 还可以在构造函数中传入shuffle=True,先对数据进行乱序。这将会进一步增强数据的随机性
from sklearn.model_selection import StratifiedKFold
from collections import Counter

skf = StratifiedKFold(n_splits=5)
fold_index = 1
for train_indices, test_indices in skf.split(iris_data, iris_target):
    y_train_fold_data = iris_target[train_indices]    # 训练标签集
    counter = Counter(y_train_fold_data)
    print("第%d个划分结果训练样本中标签分类的个数统计%s" % (fold_index, counter))
    fold_index += 1
第1组训练样本中标签分类的个数统计Counter({0: 40, 1: 40, 2: 40})
第2组训练样本中标签分类的个数统计Counter({0: 40, 1: 40, 2: 40})
第3组训练样本中标签分类的个数统计Counter({0: 40, 1: 40, 2: 40})
第4组训练样本中标签分类的个数统计Counter({0: 40, 1: 40, 2: 40})
第5组训练样本中标签分类的个数统计Counter({0: 40, 1: 40, 2: 40})
说明

从上可见,各个分类的样本时比较均衡的

【子任务项3-3】 使用StratifiedShuffleSplit获得指定训练测试样本数比例的K折数据集

前述KFold和StratifiedKFold,会根据 K K K的值,将 K − 1 K-1 K1份数据作为训练集,剩下1份数据作为测试集。也就是说,无法设置训练集和测试集的样本数比例。
本例使用StratifiedShuffleSplit,通过指定test_size来设定测试集占总样本数的比例,同时完成K折拆分,并考虑了样本的均衡性。
下面的例子,通过test_size设置测试集占比为0.3。

from sklearn.model_selection import StratifiedShuffleSplit

skf = StratifiedShuffleSplit(n_splits=5, test_size=0.3, random_state=random_state)
fold_index = 1
for train_indices, test_indices in skf.split(iris_data, iris_target):
    y_train_fold_data = iris_target[train_indices]    # 训练标签集
    counter = Counter(y_train_fold_data)
    print("第%d个划分结果训练样本中标签分类的个数统计%s" % (fold_index, counter))
    y_test_fold_data = iris_target[test_indices]    # 训练标签集
    counter = Counter(y_test_fold_data)
    print("第%d个划分结果测试样本中标签分类的个数统计%s" % (fold_index, counter))
    fold_index += 1
第1组训练样本中标签分类的个数统计Counter({1: 35, 0: 35, 2: 35})
第1组测试样本中标签分类的个数统计Counter({0: 15, 1: 15, 2: 15})
第2组训练样本中标签分类的个数统计Counter({2: 35, 0: 35, 1: 35})
第2组测试样本中标签分类的个数统计Counter({2: 15, 0: 15, 1: 15})
第3组训练样本中标签分类的个数统计Counter({2: 35, 0: 35, 1: 35})
第3组测试样本中标签分类的个数统计Counter({2: 15, 0: 15, 1: 15})
第4组训练样本中标签分类的个数统计Counter({0: 35, 2: 35, 1: 35})
第4组测试样本中标签分类的个数统计Counter({2: 15, 1: 15, 0: 15})
第5组训练样本中标签分类的个数统计Counter({0: 35, 1: 35, 2: 35})
第5组测试样本中标签分类的个数统计Counter({1: 15, 0: 15, 2: 15})
说明

上可以看到,在每个Folde中,每个训练集样本总数为 35 ∗ 3 = 105 35*3=105 353=105,测试集样本总数为 15 ∗ 3 = 45 15*3=45 153=45,测试集占总样本数的30%。同时,每个分类的样本数量也时均衡的。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值