数据组织

当机器学习的模型建立之后,就需要为机器学习准备数据。在一般习惯上,会将所有数据划分为训练数据、验证数据和测试数据。如果在训练集上进行模型评估,所观察到的学习结果参数是不正确的,因为随着学习的进行,模型在训练集上的性能始终在提高,而在未知数据上的性能则会达到一个瓶颈甚至出现衰退。

机器学习的目的是得到一个泛化的模型,这个模型需要在未知数据上也能有很好的性能。这就需要对模型的泛化能力进行衡量,也就是对机器学习模型进行评估。

将所有数据划分为三个集合的用途分别是,在训练数据上训练模型,在验证数据上评估模型,找到最佳参数后在测试数据上测试模型。如果仅划分为两个数据集合,会将验证数据上的信息泄露于模型中,使验证数据也变成了训练数据。这是因为模型的调节过程需要模型在验证数据上的性能作为反馈信号,这个过程也是学习的一部分。当多次重复验证过程时,验证数据上的信息就会被模型学习进去。所以当训练数据和验证数据都能够得到较好的性能时,模型在未知数据上的性能未必是理想的。

在选择训练数据时,通常要注意以下三个问题。

  1. 数据代表性。训练集和测试集的数据都应该能代表当前数据,在进行数据划分之前,通常应该先随机打乱数据。
  2. 时间。如果需要根据过去预测未来,那么在划分数据前就不应该打乱数据,否则会因为打乱数据而造成时间泄露。在这种情况下应该始终保证测试集的数据始终晚于训练集的数据。
  3. 数据冗余。如果数据中的某些数据重复出现了,那么打乱数据再划分训练集和验证集就会导致两个数据集合之间存在数据冗余,这将对模型的评估造成影响。所以一定要确保训练集和验证集之间没有交集。

根据我们能够拥有的数据量的多少,可以有多种方法来完成数据的组织工作。即便是可用数据非常少,也还有几种高级方法可以对数据进行扩增。以下介绍几种常用的数据组织方式。

留出验证

留出验证是最简单的数据划分方式,仅仅是预留处一定比例的数据作为测试集,然后在剩余的数据上训练模型,用预留出的数据评估模型。但是为了避免前文提到的信息泄露问题,一般还需要一个额外验证集。

这里给出一个简单的留出验证的示例。

num_validations = 10000

# 打乱全部数据
np.random.shuffle(data)

# 定义验证集
validation_data = data[:num_validations]

# 定义训练集
data = data[num_validations:]
training_data = data[:]

# 进行第一轮模型训练
model = get_model()
model.train(training_data)
# 训练评估
validation_score = model.evaluate(validation_data)

# 测试模型
model = get_model()
model.train(np.concatenate([training_data, validation_data]))
test_score = model.evaluate(test_data)

留出验证的最大问题就是,当可用数据较少时,验证集和测试集包含的样本就会更少,无法在统计学上代表数据。

K折验证

K折验证弥补了留出验证中的一些不足。K折验证将数据划分为大小相同的K个分区,对于每个分区\(\alpha\),在剩余的K-1个分区上训练模型,然后在分区\(\alpha\)上评估模型。模型最终的评估分数等于K个分数的平均值。对于针对不同训练集和测试集敏感的模型,K折验证很适合使用。与留出验证一样,K折验证也需要一个额外验证集。

这里不再给出K折验证的示例程序,读者可参考上面的留出验证尝试编写K折验证的数据分区方法。

重复K折验证

当数据更加少,而又需要尽可能精确的评估模型的时候,可以选择带有打乱数据的重复K折验证。这种验证的具体做法是多次使用K折验证,但是每次在将数据划分为K个分区前都先将数据打乱。带有打乱数据的重复K折验证的模型最终分数是每次K折验证分数的平均值。

需要注意的是,重复K折验证需要训练和评估\(N \times K\)个模型,其中\(N\)代表重复次数,计算成本很高。