分割数据集是数据科学和机器学习的基本步骤。这对于以现实且公正的方式评估模型的性能至关重要。目标是确保模型不仅能够很好地拟合现有数据,而且能够推广到新的、未见过的数据。本文讨论了分割数据集的各种方法,每种方法都有其优点和注意事项。
在数据科学错综复杂的处理步骤中,第一步通常是最关键的——划分数据集。就像大厨仔细分离原料以增强菜肴的风味一样,数据科学家必须明智地分割他们的数据集,确保每个子集(训练、验证和测试)都是整体的代表性缩影。这种细致的划分不仅是一项初步任务,而且是一项基础行为,为模型从朴素学习到富有洞察力的预测奠定了基础,呼应了永恒的真理:数据的划分蕴藏着知识的统一。
随机分割
最常见的方法是随机分割,其中数据点被随机分配给训练集、验证集和测试集。通常,数据会被分成 70-15-15 的比例,分别用于训练集、验证集和测试集。该方法可确保每组数据点的良好混合,并且易于实施。但是,它可能不适合具有不平衡类别或时间序列数据的数据集。
分层分割
分层分割用于维持每个子集中类的比例。这种方法对于类别分布不平衡的数据集特别有用。通过确保每个类别在训练集、验证集和测试集中按比例表示,我们可以防止模型偏向多数类别。
基于时间的分割
对于时间序列数据,基于时间的分割至关重要。数据按照时间进行分割,保证训练集包含前期数据,测试集包含后期数据。这种方法对于需要根据过去的数据预测未来事件的模型至关重要,因为它模拟了模型训练期间无法获得未来数据的现实场景。
交叉验证
交叉验证涉及将数据集划分为 k 个子集或折叠。该模型在 k-1 次折叠上进行训练,并在剩余的折叠上进行验证。这个过程重复 k 次,每次折叠作为验证集一次。交叉验证提供了对模型性能的全面评估,在处理小型数据集时特别有用。
特定领域的拆分
在某些领域,分割数据可能需要特定的方法。例如,在医学成像中,确保来自同一患者的图像不会同时出现在训练集和测试集中至关重要。这种方法被称为按患者拆分,可以避免数据泄漏并确保模型能够在不同患者之间进行推广。
留一法和留P法
留一法 (Leave-One-Out,LOO) 和留一法 (Leave-P-Out,LPO) 是详尽的交叉验证方法。在 LOO 中,模型在除用于测试的一个数据点之外的所有数据点上进行训练。对每个数据点重复此操作。LPO 通过省略 p 个数据点来扩展这一点。虽然这些方法很全面,但它们的计算量很大,并且对于大型数据集可能不实用。
在 Python 中创建合成数据集并演示各种数据集分割方法既具有教育意义又具有实用性。
接下来的代码将生成一个合成数据集,使用各种方法对其进行分割,然后绘制每个分割中类的分布。这种视觉表示将帮助你了解每种拆分方法的差异和含义。
第 1 步:创建综合数据集
我们将使用 scikit-learn 生成合成数据集。该数据集适用于分类问题。
from sklearn.datasets import make_classification
import pandas as pd
# 创建合成数据集
X, y = make_classification(n_samples=1000, n_features=20, n_classes=2, random_state=42)
# 转成 DataFrame,方便操作
df = pd.DataFrame(X)
df['target'] = y
第 2 步:使用不同的方法分割数据集
2.1 随机分割
from sklearn.model_selection import train_test_split
train_set, test_set = train_test_split(df, test_size=0.2, random_state=42)
2.2 分层分割
strat_train_set, strat_test_set = train_test_split(df, test_size=0.2, random_state=42, stratify=df['target'])
2.3 基于时间的分割(模拟)
假设数据集具有类似时间的特征,我们将对此进行模拟。
# 添加一个时间特征
df['time'] = range(len(df))
time_threshold = int(len(df) * 0.8)
time_train_set = df[df['time'] < time_threshold]
time_test_set = df[df['time'] >= time_threshold]
第 3 步:可视化分割
为了可视化分割,我们将使用 matplotlib 绘制类别分布图。该图将帮助我们了解每个分组中的类是如何分布的。
import matplotlib.pyplot as plt
def plot_class_distribution(sets, labels):
plt.figure(figsize=(10, 6))
for i, dataset in enumerate(sets):
plt.subplot(1, len(sets), i+1)
plt.title(labels[i])
plt.hist(dataset['target'])
plt.xlabel('Class')
plt.ylabel('Frequency')
plt.tight_layout()
plt.show()
# Plotting
plot_class_distribution([train_set, strat_train_set, time_train_set],
['Random Split', 'Stratified Split', 'Time-based Split'])
结论
选择正确的数据集分割方法取决于数据的性质、当前的问题和可用的资源。对于平衡的数据集,随机分割可能就足够了,但对于不平衡或时间序列数据,分层或基于时间的分割更合适。交叉验证技术可以对模型进行彻底的评估,但需要更多的计算资源。最终,所选择的方法应该旨在最大限度地提高模型泛化到新数据的能力,同时最大限度地减少偏差或过拟合。