将数据集划分为训练集(Training)和测试集(Testing)是机器学习和统计建模中的重要步骤:
训练集(Training):一般来说 Train 训练集会进一步再分为 Train 训练集与 Validation 验证集两部分,以评价不同参数组合的效果,以确定最终的模型
测试集(Testing):Test 测试集自始至终没有参与到模型的训练过程;它的目的只有一个:在确定一个最终模型后,评价其泛化能力
所以综上,数据集划分有以下原因和好处:
- 评估模型的泛化能力: 训练集用于构建模型,测试集用于评估模型在未见数据上的表现,通过测试集的性能来估计模型的泛化能力,即模型在新数据上的预测准确性;
- 防止过拟合:模型在训练集上表现很好,但在测试集上表现不佳,通常是因为模型过于复杂,捕捉了训练数据中的噪声,使用测试集可以帮助检测过拟合现象,确保模型不仅仅是在记忆训练数据;
- 模型选择和参数调优:通过在测试集上的表现来比较不同模型的优劣,测试集提供了一个客观的标准来选择最佳的超参数组合;
一、简单拆分
1、简单随机拆分
简单随机拆分法是将数据集随机分成训练集和测试集的一种方法,该方法直观,易于实现,不需要复杂的算法或技术;随机拆分可以减少样本选择偏差,确保训练集和测试集的代表性。
缺点:在划分数据集时,不能确保每个类别的样本按照其在总体中的比例被选入训练集和测试集(如下示例中,划分为测试集的两个类别数据有可能出现类别都为 0 或者类别都为 1)
我们一般将数据集按一定比例(如 80:20)随机拆分,可以使用 sklearn 库中的 train_test_split 函数来实现,示例如下
import pandas as pd
from sklearn.model_selection import train_test_split
# 创建一个示例数据集
data = {
'feat1': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
'feat2': [10, 9, 8, 7, 6, 5, 4, 3, 2, 1],
'label': [0, 1, 0, 1, 0, 1, 0, 1, 0, 1]
}
df = pd.DataFrame(data)
# 特征和标签
X = df[['feat1', 'feat2']]
y = df['label']
# 使用简单随机拆分法将数据集分为训练集和测试集
# 使用 stratify 确保每个类别的样本在训练集和测试集中都有代表
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
# 输出结果
print("训练集特征:")
print(X_train)
print("\n测试集特征:")
print(X_test)
print("\n训练集标签:")
print(y_train)
print("\n测试集标签:")
print(y_test)
数据集划分的结果如下
# 训练集特征:
# feat1 feat2
# 4 5 6
# 3 4 7
# 7 8 3
# 5 6 5
# 1 2 9
# 2 3 8
# 9 10 1
# 6 7 4
# 测试集特征:
# feat1 feat2
# 8 9 2
# 0 1 10
# 训练集标签:
# 4 0
# 3 1
# 7 1
# 5 1
# 1 1
# 2 0
# 9 1
# 6 0
# Name: label, dtype: int64
# 测试集标签:
# 8 0
# 0 0
# Name: label, dtype: int64
2、分层抽样
在划分数据集时,确保每个类别的样本按照其在总体中的比例被选入训练集和测试集,通过对每个类别进行单独抽样,确保各类别样本的代表性,其适用于类别不均衡的数据集,优点在于:
- 保证每个类别在训练集和测试集中都有足够的样本,有助于提高模型的泛化能力。
- 减少了由于类别不平衡导致的偏差
我们可以使用 sklearn 库中的 train_test_split 函数,并设置 stratify 参数来实现,示例如下
import pandas as pd
from sklearn.model_selection import train_test_split
# 创建一个示例数据集
data = {
'feat1': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
'feat2': [10, 9, 8, 7, 6, 5, 4, 3, 2, 1],
'label': [0, 1, 0, 1, 0, 1, 0, 1, 0, 1]
}
df = pd.DataFrame(data)
# 特征和标签
X = df[['feat1', 'feat2']]
y = df['label']
# 使用简单随机拆分法将数据集分为训练集和测试集
# 使用 stratify 确保每个类别的样本在训练集和测试集中都有代表
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, stratify=y)
# 输出结果
print("训练集特征:")
print(X_train)
print("\n测试集特征:")
print(X_test)
print("\n训练集标签:")
print(y_train)
print("\n测试集标签:")
print(y_test)
数据集划分的结果如下
# 训练集特征:
# feat1 feat2
# 3 4 7
# 9 10 1
# 4 5 6
# 1 2 9
# 5 6 5
# 0 1 10
# 8 9 2
# 6 7 4
# 测试集特征:
# feat1 feat2
# 7 8 3
# 2 3 8
# 训练集标签:
# 3 1
# 9 1
# 4 0
# 1 1
# 5 1
# 0 0
# 8 0
# 6 0
# Name: label, dtype: int64
# 测试集标签:
# 7 1
# 2 0
# Name: label, dtype: int64
二、交叉验证
1、K折交叉验验
将数据集随机分成 K 个子集(折),每次用 K-1 个子集进行训练,剩余的 1 个子集用于测试,重复 K 次,每次选择不同的子集作为测试集。通过计算所有测试结果的平均值作为模型的最终性能评估指标
优点
- 充分利用数据:每个数据点都可以作为训练集和测试集的一部分,增加了数据的利用率
- 更稳定的评估:提供了对模型性能的更稳定和可靠的估计,因为它考虑了多次划分的结果
- 提高准确性:通过多次训练和测试,减少了因数据划分不同而导致的波动,提高模型的准确性
- 灵活性:可以根据需要调整 K 的大小,以适应不同的数据集和计算资源
缺点
- 计算成本高:需要训练 K 次模型,计算时间和资源消耗较大
- 选择合适的 K 值困难:不同的 K 值可能导致不同的结果,选择不当可能影响模型评估的准确性
- 数据分布问题:如果数据集不平衡,可能需要使用分层 K 折交叉验证来确保各类别的代表性
在 Python 中,可以使用 scikit-learn 库来实现 K 折交叉验证。以下是一个简单的实现示例:
from sklearn.model_selection import KFold, cross_val_score
from sklearn.datasets import load_iris
from sklearn.ensemble import RandomForestClassifier
import numpy as np
# 加载数据集
data = load_iris()
X, y = data.data, data.target
# 定义模型
model = RandomForestClassifier()
# 定义 K 折交叉验证
k = 10
kf = KFold(n_splits=k, shuffle=True, random_state=42)
# 进行交叉验证
scores = cross_val_score(model, X, y, cv=kf)
# 输出结果
print(f"K折交叉验证的平均准确率: {scores.mean():.2f}")
print(f"每折的准确率: {np.round(scores, 2)}")
# -------- 输出 --------
# K折交叉验证的平均准确率: 0.96
# 每折的准确率: [1. 1. 1. 0.93 1. 0.87 0.87 1. 1. 0.93]
2、分层K折交叉验证
其它与 K 折交叉验证相同,但在划分子集时,确保每个子集中各类别的比例与整个数据集中的比例相同。
优点
- 充分利用数据:每个数据点都被用作训练和验证,增加了数据的利用率
- 更稳定的评估:由于每个折的类别分布与整体数据集一致,能提供更稳定的性能评估
- 适用于不平衡数据:确保每个折中包含足够的少数类样本,有助于在不平衡数据集上进行更准确的评估
缺点
- 计算成本高:需要训练模型 K 次,对于大型数据集或复杂模型,计算成本较高
- 不适用于某些回归任务:分层策略主要针对分类任务,对于回归任务,分层的概念不太适用
在 Python 中,可以使用 scikit-learn 库的 StratifiedKFold 来实现分层 K 折交叉验证。以下是一个简单的实现示例,主要和 K 折交叉验证的区别是采用 StratifiedKFold 方法替换了 KFold 方法
from sklearn.model_selection import StratifiedKFold, cross_val_score
from sklearn.datasets import load_iris
from sklearn.ensemble import RandomForestClassifier
import numpy as np
# 加载数据集
data = load_iris()
X, y = data.data, data.target
# 定义模型
model = RandomForestClassifier()
# 定义 K 折交叉验证
k = 10
skf = StratifiedKFold(n_splits=k, shuffle=True, random_state=42)
# 进行交叉验证
scores = cross_val_score(model, X, y, cv=kf)
# 输出结果
print(f"K折交叉验证的平均准确率: {scores.mean():.2f}")
print(f"每折的准确率: {np.round(scores, 2)}")
# -------- 输出 --------
# K折交叉验证的平均准确率: 0.96
# 每折的准确率: [1. 1. 1. 0.93 1. 0.87 0.87 1. 1. 0.93]
3、留一交叉验证
留一交叉验证法(Leave-One-Out Cross-Validation, LOOCV)是一种特殊的交叉验证方法,其中每次迭代只用一个样本作为验证集,其余样本作为训练集。这种方法适用于小型数据集,因为它计算量较大。
优点
- 最大化数据使用:每次训练使用了几乎所有的数据,确保模型训练的充分性
- 无偏估计:由于每个样本都被用作验证集,评估结果通常较为无偏
- 适合小数据集:在样本数较少的情况下,能够充分利用数据进行模型评估
缺点
- 计算成本高:需要训练模型 N 次(N 为样本数),对于大型数据集,计算代价非常高
- 结果方差大:每次只用一个样本进行验证,导致评估结果的方差较大,可能不如 K 折交叉验证稳定
- 不适合大型数据集:由于计算成本和时间要求,通常不适用于大型数据集
- 过拟合风险:在某些情况下,可能导致模型过拟合,因为每次训练几乎使用了所有数据
在 Python 中,可以使用 scikit-learn 库的 LeaveOneOut 方法来实现 留一交叉验证。以下是一个简单的实现示例:
from sklearn.model_selection import LeaveOneOut, cross_val_score
from sklearn.datasets import load_iris
from sklearn.ensemble import RandomForestClassifier
# 加载数据集
data = load_iris()
X, y = data.data, data.target
# 定义模型
model = RandomForestClassifier()
# 定义留一交叉验证
loo = LeaveOneOut()
# 进行交叉验证
scores = cross_val_score(model, X, y, cv=loo)
# 输出结果
print(f"留一交叉验证的平均准确率: {scores.mean():.2f}")
print(f"样本总数: {len(scores)}")
# -------- 输出 --------
# 留一交叉验证的平均准确率: 0.95
# 样本总数: 150
4、Bootstrap 自助法
Bootstrap 自助法是有放回的重复采样:在含有 m 个样本的数据集中,每次随机挑选一个样本, 将其作为训练样本,再将此样本放回到数据集中,这样有放回地抽样 m 次,生成一个与原数据集大小相同的数据集,这个新数据集就是训练集。这样有些样本可能在训练集中出现多次,有些则可能从未出现。原数据集中大概有 36.8% 的样本不会出现在新数据集中。因此,我们把这些未出现在新数据集中的样本作为验证集。
- 优点:训练集的样本总数和原数据集一样都是 m个,并且仍有约 1/3 的数据不出现在训练集中,而可以作为验证集。
- 缺点:这样产生的训练集的数据分布和原数据集的不一样了,会引入估计偏差。
用途:自助法在数据集较小,难以有效划分训练集/验证集时很有用;此外,自助法能从初始数据集中产生多个不同的训练集,这对集成学习等方法有很大的好处。
注意:由于其训练集有重复数据,这会改变数据的分布,因而导致训练结果有估计偏差,因此这种方法不是很常用,除非数据量真的很少。
以下是使用 Python 实现 Bootstrap 自助法的一个简单的实现示例:
import numpy as np
from sklearn.linear_model import LogisticRegression
from sklearn.datasets import load_iris
from sklearn.metrics import accuracy_score
# 加载数据集
data = load_iris()
X = data.data
y = data.target
# 设置参数
num_iterations = 100
n_samples = len(X)
accuracies = []
# Bootstrap 自助法
for _ in range(num_iterations):
# 从原始数据集中有放回地抽样
indices = np.random.choice(n_samples, size=n_samples, replace=True)
X_bootstrap = X[indices]
y_bootstrap = y[indices]
# 训练模型
model = LogisticRegression(max_iter=200)
model.fit(X_bootstrap, y_bootstrap)
# 预测
y_pred = model.predict(X)
# 计算准确率
accuracy = accuracy_score(y, y_pred)
accuracies.append(accuracy)
# 结果汇总
mean_accuracy = np.mean(accuracies)
std_accuracy = np.std(accuracies)
print(f"平均准确率: {mean_accuracy:.2f}")
print(f"准确率标准差: {std_accuracy:.2f}")
# -------- 输出 --------
# 平均准确率: 0.97
# 准确率标准差: 0.01
5、Subsampling 随机子集交叉验证
随机子集交叉验证(Subsampling)是一种模型评估方法,通过多次随机拆分数据集来验证模型的性能。与标准的 k 折交叉验证不同,随机子集交叉验证不一定将数据划分为固定的 k 个子集,而是每次随机选择训练集和测试集。以下是详细的步骤和代码示例
- 优点:能够在不同的训练集和测试集上进行多次评估,提供对模型性能的更加稳健的估计。
- 缺点:计算成本较高,需要多次重复采样和训练模型。
- 使用场景:用于需要更稳健的性能评估的情况,或者对模型性能进行一致性验证。
以下是使用 Python 实现 Subsampling 随机子集交叉验证 的一个简单的实现示例:
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.datasets import load_iris
from sklearn.metrics import accuracy_score
# 加载数据集
data = load_iris()
X = data.data
y = data.target
# 设置参数
num_iterations = 100
test_size = 0.3
accuracies = []
# 随机子集交叉验证
for _ in range(num_iterations):
# 随机拆分数据集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=test_size)
# 训练模型
model = LogisticRegression(max_iter=200)
model.fit(X_train, y_train)
# 预测
y_pred = model.predict(X_test)
# 计算准确率
accuracy = accuracy_score(y_test, y_pred)
accuracies.append(accuracy)
# 结果汇总
mean_accuracy = np.mean(accuracies)
std_accuracy = np.std(accuracies)
print(f"平均准确率: {mean_accuracy:.2f}")
print(f"准确率标准差: {std_accuracy:.2f}")
# -------- 输出 --------
# 平均准确率: 0.96
# 准确率标准差: 0.03
如果你喜欢本文,欢迎点赞,并且关注我们的微信公众号:Python数据挖掘分析,我们会持续更新数据挖掘分析领域的好文章,让大家在数据挖掘分析领域持续精进提升,成为更好的自己!
添加本人微信(coder_0101),或者通过扫描下面二维码添加二维码,拉你进入行业技术交流群,进行技术交流~
扫描以下二维码,加入 Python数据挖掘分析 群,在群内与众多业界大牛互动,了解行业发展前沿~