如何把数据集划分成训练集和测试集

本文主要内容来自周志华《机器学习》

本文中代码

问题: 对于一个只包含\(m\)个样例的数据集\(D=\{(x_1,y_1),(x_2,y_2),\cdots,(x_m,y_m)\),如何适当处理,从\(D\)中产生训练集\(S\)和测试集\(T\)?

下面介绍三种常见的做法:

  • 留出法
  • 交叉验证法
  • 自助法

留出法(hold-out)

留出法直接将数据集\(D\)划分为两个互斥的集合,其中一个集合作为训练集\(S\),留下的集合作为测试集\(T\),即\(D=S \cup T, S \cap T=\emptyset\)。在\(S\)上训练出模型后,用\(T\)来评估其测试误差,作为对泛化误差的估计。以二分类任务为例,假设\(D\)包含1000个样本,我们采取7/3分样,将其划分为\(S\)包含700个样本,\(T\)包含300个样本。

\(S/T\)的划分要尽可能的保持数据分布的一致性(\(S/T\)中的数据分布跟\(D\)是一样的),才能避免因数据划分过程引入额外的偏差而对最终结果产生影响。例如,在分类任务中,至少要保持样本的类别比例相似。保留类别比例的采样方式,即分层采样(stratified sampling)。例如,\(D\)中含有500个正例,500个反例,当采用分层采样获取70%的样本的训练集\(S\)和30%的赝本的测试集\(T\)时,则\(S\)包含有350个正例和350个反例,\(T\)有150个正例和150个反例。

给定样本比例,有多种划分方式对\(D\)进行分割。如在上面的例子中,我们可以把\(D\)的样本排序,然后把前350个正例放到\(S\)中,也可以把后350个正例放入\(S\)... 这种不同的划分将导致不同的训练/测试集,相应的模型评估也是有差别的。因此,使用留出法时,一般要采用若干次随机划分、重复进行实验评估后取平均值作为留出法的评估结果。例如进行100次随机划分,每次产生一个\(S/T\)进行实验评估,得到100个结果,而留出法返回的则是这100个结果的平均。

常见做法将大约2/3~4/5的样本作为\(S\),剩下的作为\(T\)

通过调用sklearn.model_selection.train_test_split按比例划分训练集和测试集:

import numpy as np
from sklearn.model_selection import train_test_split

X, Y = np.arange(10).reshape((5, 2)), range(5)
print("X=", X)
print("Y=", Y)
X_train, X_test, Y_train, Y_test = train_test_split(
    X, Y, test_size=0.30, random_state=42)
print("X_train=", X_train)
print("X_test=", X_test)
print("Y_train=", Y_train)
print("Y_test=", Y_test)

其中test_size=0.30表示\(T\)占30%, 那么\(S\)占70%。运行结果:

X= [[0 1]
 [2 3]
 [4 5]
 [6 7]
 [8 9]]
Y= range(0, 5)
X_train= [[4 5]
 [0 1]
 [6 7]]
X_test= [[2 3]
 [8 9]]
Y_train= [2, 0, 3]
Y_test= [1, 4]

交叉验证法(cross validation)

交叉验证法将\(D\)划分为\(k\)个大小相似的互斥子集,即\(D=D_1 \cup D_2 \cup \dots \cup D_k, D_i \cap D_j = \emptyset (i \not = j)\)。每个子集\(D_i\)都尽可能保持数据分布的一致性,即从\(D\)中通过分层采样得到。然后,每次用\(k-1\)个子集的并集作为\(S\),剩下的那个子集作为\(T\),这样可获得\(k\)\(S/T\),从而可进行\(k\)次训练和测试,最终返回这\(k\)个测试结果的平均。

交叉验证法评估结果的稳定性和保真性在很大程度上取决于\(k\)的取值、为强调这一点,通常把交叉验证法称为“\(k\)折交叉验证”(\(k\)-fold cross validation)。\(k\)最常用的取值是10,有时也取5和20等。

通过调用sklearn.model_selection.KFold\(k\)折交叉划分训练集和测试集:

import numpy as np
from sklearn.model_selection import KFold

X= np.arange(10).reshape((5, 2))
print("X=", X)
kf = KFold(n_splits=2)
for train_index, test_index in kf.split(X):
    print('X_train:%s ' % X[train_index])
    print('X_test: %s ' % X[test_index])

其中n_splits=2表示\(k=2\)。运行结果:

X= [[0 1]
 [2 3]
 [4 5]
 [6 7]
 [8 9]]
X_train:[[6 7]
 [8 9]] 
X_test: [[0 1]
 [2 3]
 [4 5]] 
X_train:[[0 1]
 [2 3]
 [4 5]] 
X_test: [[6 7]
 [8 9]]  

\(D\)包含\(m\)个样本,令\(k=m\),则得到交叉验证法的一个特例——留一法(Leave-One-Out,简称LOO)。留一法使用的\(S\)只比\(D\)少一个样本,所以在绝大多数情况下,实际评估结果与用\(D\)训练的模型相似。因此,留一法被认为比较准确。但留一法对于大数据集,计算开销太大;另外也不见得永远比其他方法准确。

通过调用sklearn.model_selection.LeaveOneOut按留一法划分训练集和测试集:

import numpy as np
from sklearn.model_selection import LeaveOneOut

X= np.arange(10).reshape((5, 2))
print("X=", X)
loo = LeaveOneOut()
for train_index, test_index in loo.split(X):
    print('X_train:%s ' % X[train_index])
    print('X_test: %s ' % X[test_index])

运行结果:

X= [[0 1]
 [2 3]
 [4 5]
 [6 7]
 [8 9]]
X_train:[[2 3]
 [4 5]
 [6 7]
 [8 9]] 
X_test: [[0 1]] 
X_train:[[0 1]
 [4 5]
 [6 7]
 [8 9]] 
X_test: [[2 3]] 
X_train:[[0 1]
 [2 3]
 [6 7]
 [8 9]] 
X_test: [[4 5]] 
X_train:[[0 1]
 [2 3]
 [4 5]
 [8 9]] 
X_test: [[6 7]] 
X_train:[[0 1]
 [2 3]
 [4 5]
 [6 7]] 
X_test: [[8 9]] 

自助法(bootstrapping)

在前两种方法中都保留部分样本作为\(T\)用于测试,因此实际评估模型使用的训练集\(T\)总是比期望评估模型使用的训练集\(D\)小,这样会引入一些因训练样本规模不同而导致的估计偏差。

自助法,以自助采样(bootstrap sampling)为基础。对\(D\)进行采样产生\(D^{\prime}\):每次随机从\(D\)中挑选一个样本,将其拷贝一份放入\(D^{\prime}\)中,保持\(D\)不变,重复以上过程\(m\)次。显然,\(D\)中有部分样本会多次出现在\(D^{\prime}\)中,而另一部分不会出现。样本在\(m\)次采样中的始终不被采到的概率为\((1-\frac{1}{m})^m\),当\(m \to \infty\)时,\((1-\frac{1}{m})^m \to \frac{1}{e} \approx 0.368\)

通过自助法,\(D\)中有36.8%不会出现\(D^{\prime}\)中。于是我们把\(D^{\prime}\)当作训练集\(S\),把\(D \setminus D^{\prime}\)当作测试集\(T\),这样实际评估模型与期望评估模型都为\(m\)个样本,而仍有数据总量1/3的、没有出现在训练集中的样本用于测试。这样的测试结果为包外估计(out-of-bag estimate)。

自助法在数据集较小、难以划分\(S/T\)时很有用。此外,自助法能从\(D\)中产生不同的\(S\),对集成学习等方法有好吃。自助法产生的\(S\)改变了\(D\)的分布,会引入估计偏差。当数据量足够时,留出法和交叉验证法更常用、

转载于:https://www.cnblogs.com/liupeng-Wang/p/8823371.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值