数据的重要性:训练集,验证集,测试集
数据集的重要性
在模型的生命周期中,我们会有三种数据, 分别是训练集,验证集和测试集。
- 训练集用来训练模型
- 验证集用来快速验证和调参
- 测试集再参数调的差不多以后才会用测试集出一个完整的评估报告
在这里验证集和测试集有一个讲究就是这两份数据要处于同一分布。就是说这两份数据的来源要是一致的。为什么呢,我们知道机器学习是一个从历史中学习规律的一个机制。你给它什么样的数据,它就学出什么样的效果。
在这里有一个我们常用的词叫做拟合,我们总能听到类似于说模型在训练集上过拟合了。我这么解释吧。 从现象上来讲,一般如果我们在建模的时候训练集的 AUC 比较高,但是在验证集或者测试集上的 AUC 比较低,就是过拟合。
就是说我们的模型在训练数据上效果特别好,但是一放到测试数据上就跪了。 为什么呢,因为训练数据测试数据是有差别的,他们的数据分布,特征分布都不一样的。世界上就没有两份数据是能抽出相同的特征的。 所以如果建模的时候我们规则划分的特别细,细到了能完美匹配训练数据的所有情况,变成了一个针对训练数据完美定制打造的模型。 所以在训练数据中统计的 AUC 特别高。 但就是因为太细了,其他的数据不吃这一套,他们有不同的特征分布,这么细的规则粒度反而在其他数据上的效果很差。 这就是过拟合。所以我刚才说一个模型的生命周期中应该有 3 个数据。 大家可以理解为训练集和验证集是建模工程师用来训练模型的。所有规则的建立和验证都是在这两份数据上做的。 也就是说模型拟合的是这两份数据。 而我们要用第三份数据,也就是测试集来验证我们的模型是不是在真实的用户场景下也拟合的不错。
所以我再一开始就说验证集和测试集的来源要一致,验证集是建模过程中用来做自测的, 测试集是最终验证模型效果的。 如果这两份数据任何一个出现了偏差都会出问题。 这两份数据也可以达到互相验证的作用。 当最后模型评估报告出来的时候,如果效果不好,就可以去看一下是验证数据有问题,还是测试数据有问题。 所以作为 QA,我们在采集测试数据的时候一定要非常小心,保证我们的采样是符合真实情况的。
我再举个因为数据出现问题的例子, 阿尔法狗。当初阿尔法狗在对战李世石的时候输过一局, 这个例子就是一个典型的由于数据出现偏差而导致的错误场景。 首先我们看一下阿尔法狗的训练过程。
- 找到 80w 局人类的棋谱,训练一只阿尔法狗 A
- 想要战胜李世石,柯洁这种世界级选手 80w 的数据是不够的。 所以让阿尔法狗 A 每日自己跟自己下棋,一天一百万局。产生 1 亿 + 的棋谱
- 使用 80w 人类棋谱 +1 亿的机器棋谱,再训练出一只阿尔法狗,就是我们当时看到的对战李世石的阿尔法狗。
我们看这个过程,最大的问题是这样的,80w 的人类棋局被淹没在机器下的棋局中,也就是说模型拟合的最多的是战胜阿尔法狗自己的场景。这导致的结果是什么, 是一旦它遇到了逆风局的时候,几乎丧失了胜利的可能性。
为什么呢? 一般下围棋的时候,在一块地盘上碰到了逆风局,人类棋手会争取另开战场,把局势搞复杂,越复杂越好,只要对手犯错自己就有机会。 但是阿尔法狗不会这么想,因为它拟合的是如何战胜自己的数据。它预测出的是自己犯错的概率而不是人犯错的概率, 但他自己几乎是不会犯错的, 所以它觉的即使另开战场对手也不会犯错。根据它的计算,它怎么下都是输。所以我们在看那场比赛的时候,感觉阿尔法狗跟梦游一样。 当然了,这只是行业内大家做出的分析,也并不一定准的。但是当初确实有一种声音是,想要战胜阿尔法狗,就要突然发力一招定乾坤,压制住他。
所以我们可以理解为,在建模,自测时拟合的数据和真实数据的差异导致了这样的结果。我说了这么多就是再强调一件事,对于机器学习服务来说,数据永远是最重要的。
补充:
AUC(曲线下面积)是一个重要的评估指标,通常用于分类模型的性能评估。它表示ROC曲线下的面积,AUC值越接近1,分类器的性能越好。具体来说,AUC的物理意义是,当随机选择一个正样本和一个负样本时,当前分类算法将正样本排在负样本前面的概率就是AUC值。在机器学习和推荐系统中,AUC被广泛应用于评估模型的效果。
训练集,验证集,测试集

上图是整个模型产出一般流程。 训练集和验证集一般是从原始的训练数据中拆分出来的,用于算法人员自测使用, 而测试集则是测试人员使用的。
在传统的划分数据集时,一般采用“7:2:1”或“6:3:1”的比例划分训练集、验证集和测试集,比例因任务而异。 要保证测试集合足够的规模才可以更准确的评估模型的效果。 但在当今行业大数据流行的时代,数据规模是非常大的,动不动就是数以亿计的数据规模,这时候就不需要采用 “7:2:1”或“6:3:1”这个比例了。 可以把更多的数据分给训练集来让模型有更好的效果。 接下来我们需要看这里为什么要单独区分验证集和测试集。
偏差 (欠拟合) 和方差 (过拟合)

我们不论在逻辑回归,线性回归还是在神经网络中应用的都是线性函数。也就是我们一开始的公式:y=wx + b。 也就是我们上图最左边的图片中,是一条直线。
分类算法比较直观一点的理解就是要在坐标空间中找到一个最适合的直线,让每个样本所在的点离这条线最近。但是直线的表达能力是有限的,就像上面最左边的图中一样,一条直线并不能很好的区分圆圈和叉叉,这时候我们会发现在训练集上训练的效果不好,也就是准确率不高,我们称这种情况为高偏差,也叫欠拟合。
我们希望效果能像中间的图一样,是一条曲线,能够有效的增加正确率。 所以这时候激活函数出马了,我们在第一篇帖子中就写了激活函数其实并不是在激活什么,而是为我们的线性方程增加非线性效果。它为我们拟合了更好的效果。但有时候如果激活函数过度拟合就会产生上图中最后边的情况。 它拟合了一个非常复杂的线,这种情况的表现就是它的效果在训练集上非常好,误差很小。 但实际在测试集上表现的就很差。 例如我们在训练集上的误差 1%,而在测试集上额误差达到了 15%。 这是因为我们的线拟合的很复杂,很好的契合了训练集的数据分布,但是到了测试集的时候数据分布就不是这么回事了。那么如果出现了欠拟合或者过拟合该怎么办呢。 通常的做法如下:
- 对于欠拟合: 增加神经网络复杂度,出现欠拟合的原因之一是由于函数的非线性不足,所以用更复杂的网络模型进行训练来加深拟合。
- 对于过拟合:增加数据规模, 出现过拟合的原因之一是数据规模不足而造成的数据分布不均,扩展数据规模能比较好的解决这个问题。 当然另一个做法是加入正则化超参数。
所以对于测试数据的选取是非常重要的,选取不好,对模型评估的结果就会有偏差
选取数据的注意点
尽量使用真实数据
要尽量使用生产环境的数据, 因为这样最符合真实的用户场景。
这个相信还是比较好理解的。除了数据真实以外,还有一点就是能够早一点发现一些真实场景下会出现的一些比较鬼畜的事情。
比如说我们以前给客户做 POC 的时候发现过,刚一测试自学习就跪了。为什么呢,因为客户在生产环境上采集的数据没有做重新分片, 一共几百 M 的数据在 hadoop 上分了 7800 多个片。 一个分片里才百十来 k, 7800 个分片在集群里各种聚合,网络通信。当时集群直接就跪了。 所以说我们使用真实数据是对的。
业务数据呈现时序性
使用真实数据是对的,但是直接从线上引流过来,不做处理在很多情况下也是不行的。
比如如果我们的业务数据呈现时序性的场景。 什么是时序性, 就是我们数据中的特征会随着时间的流动产生巨大的变化。
比如推荐系统, 一般来说在工业界做推荐系统大多都是用逻辑回归来做,而不是另一个常用模型 GBDT。 为什么呢?因为你能够给逻辑回归输入海量的离散特征,这个量能到多少? 数以亿计,给一个大型系统做的模型搞个几百亿的特征都不奇怪。 那为什么会出现这么多的特征呢? 举个例子,我们直接把 ID 当做特征来处理, 也就是说如果我们的系统里有 1000W 个用户,那就是 1000W 个特征。 如果有 1 亿个视频,那就又是 1 亿个特征。 或者我们对所有视频的标题做切词以后再抽取特征,一个词就是一个特征,你想想这么多的视频能切出多少个词来。如果我们对这些特征再做组合特征处理,那特征就更多了。 那我们为什么要搞这么大规模的特征,或者说我们为什么要这么抽取特征。 这样做的好处是我的模型将会是一个集定制化和泛化为一体的模型。我们想想,我们都已经对 user id 和视频 id 做特征处理了, 那他们都已经变成了我模型中的专家规则了, 以后如果这个用户再出现的时候,我就可以为它做定制化的推荐. 同样对视频标题也是一样的,我的模型已经计算出了这些标题中的热点,哪些关键词是点击率高的热门词汇。 点击率预测这个场景本身就是比较难的,因为用户的点击行为是随性的,一般来说 AUC 是不会超过 0.8 的,我们这几个礼拜举办的建模比赛,目前为止第一名是 0.798。
所以建模工程师才会用这种方式把模型作为定制化的热点模型。 那这种场景带来的问题就是我们的特征随着时间产生巨变。 只相差一天的数据可能就会产生巨大的变化。 比如出现了很多新的用户和视频,前两天还是热点的关键词今天可能今天的热度就没那么高 了。 前几天点击率高的视频可能今天就降下去了。所以说我们在这种场景下才会引入高频自学习的策略来用每天最新的数据更新模型。
这里先说一下测试数据的采集。 鉴于这种场景的特性,我们在采集测试数据的时候就要小心的选取合适的时间段的数据进行测试。 首先我们要知道建模用的数据是哪一个时间段内的,然后我们根据业务情况选取一段时间间隔,在这个时间间隔后截取最新的一天的数据作为测试数据。 这个时间间隔是根据我们每次模型训练到上线所需要的时间。 时间段一定要选取好,任何的偏差都可能造成模型的评估效果的偏差。
数据随某字段呈现不均匀分布
再一个常见的场景是数据根据某字段呈现不均匀分布的情况。 还是拿推荐系统举个例子。 一般我们大一点的系统一天的数据量很大,动辄几十个 G 的也不奇怪。 我们不可能把这些数据都当做测试集放进来, 所以要拆。 我见过常见的做法是截取一段时间内的数据或者随机采样。 但这都是有问题的,比如你截哪个时间段内的数据? 晚上 6 点到 8 点之间? 那你把我们这些深夜追剧党放在哪里了。 随机采样的话,点子不好就会造成数据倾斜,比如一天 24 个小时每个时间段的数据量都是不一样的,可能某个时间段的用户比较活跃,数据量多, 随机采样无法控制数据分布,可能就是在某几个时间段上采集了大量的数据,其他时间段的数据很少。或者随机采样的用户大多数是年轻人的,中年人的数据很少。 这些数据倾斜的情况都会影响我们对模型做出正确的评估。 因为之前我们讲过了,评估一个模型的时候要根据字段做分组 AUC 的统计。
所以那我们就要保证我们每一个分组的数据分布是平均的,保证每一个分组都有适当的数据量才能够比较好的评估效果。 所以我们对测试数据要根据业务,也就是分组 AUC 要统计的那个业务字段,做分层拆分采样,什么意思呢, 就是我们根据这个字段分组,然后在每个组中都取适当的百分比作为测试数据。 比如我们的业务对用户的职业特别看重,我们想要知道模型对每种职业都是友好的。 但每个职业的用户数量是不一样的,可能程序员很多,财务人员比较少。 于是我们在每个职业中都采样 20% 做为测试数据。这样保证我们的测试数据在每个职业上都有一个平均的分布。 我们在介绍模型的评估方法时介绍过要进行分组指标的统计, 其目的也是要更全面的评估模型效果。而不至于出现过拟合的情况。
好了,我们花了大量的时间来说数据的事情,因为对于模型测试人员来说,他的大部分时间都在跟数据打交道。这个领域比较看中经验,能不能正确的评估模型的效果,是要根据我们对业务的理解,对建模场景的理解,对所有业务线产生的数据的理解去采集合适的测试数据,验证合适的分组 AUC。 我这里说的理解不是说我们知道 UI 上怎么操作了就是理解了。 而是我们要理解用户地域上,时间上,或者是其他隐含模式的分布,理解用户的行为模式,甚至是理解产品的盈利模式。 去找到隐藏在数据中有价值的信息。 再去验证模型是不是能够帮助到我们的产品。 所以我才说在这个领域,相比于技术,可能经验更加重要。大数据处理技术和机器学习的知识储备可能是你能够进行这种工作的基本条件。 但是决定能不能做的好,能不能产生价值,取决的是你再当前业务领域下的经验。
它跟我们以前做的测试不一样, 比如接口测试,给我一份接口文档我很快的就能测试起来。 业务测试,给我一份需求分档我也能很快写出一堆的测试用例来。 但当你刚进入某个领域做机器学习相关的测试的时候, 你基本上是什么都做不了的, 需要花大量的时间去学习业务,分析数据,建立一些基础设施。 之后才能慢慢的输出一些价值出来。 这也是我今天要讲的最主要的内容。 接下来还剩下一点的时间,我来讲一些重要的测试场景。
详解数据分布带来的性能差异
同样规模的数据(行列相同)为什么性能不同
我们假设有两张表, 他们分别拥有100列100w行。 针对它们进行完全相同的特征工程和模型训练(参数完全一模一样),产出的模型,请问使用 这两个模型针对系统的数据进行模型推理, 他们的性能是否相同? 答案是大部分情况下都是不同的,为什么呢? 我们回顾一下,模型本身是一个 k,v数据库,保存着特征与权重。 而我们的推理公式是:y=wx + b。 理论上在这个公式下特征的数量不会过多的影响模型本身的性能, 因为这里的推理是一个 矩阵乘法,它的计算是很快的,所以特征的数量会影响模型的大小, 但不一定会很大程度的影响推理的性能。
但是我们知道原始数据是不能直接输入给模型的,一定要经过特征工程的计算。 那么特征数量的多少和类型就影响到了这个部分的性能。 虽然我们使用相同的特征工程算法与参数,也使用了系统的规模的数据表。 但是两张数据表之间的数据分布差异可能会造成性能的差异。 比如针对字段X,表A有1000个唯一值(可以抽取1000个特征),而表B有100W个唯一值(可以抽取100w个特征)那么为了计算这些特征所耗费的时间可能就是不一样的了。
所以我们回顾一下模型测试的分层:

- 在最下层的时候,测试人员需要针对模型文件进行测试, 一般会由开发人员提供sdk进行模型推理。 在这个阶段,大部分时候SDK是不负责特征工程的工作的(计算机视觉场景可能有例外) 所以此时测试的性能是比较纯粹的模型推理过程,特征数量对性能的影响不会特别大。
- 而当模型发出成服务后,部分情况下会把特征工程的逻辑也包含在模型服务中(把原始的模型服务进行了封装), 当然部分情况下也可能是不带特征工程的。
- 在外围的时候(可能包含了决策引擎),一般都是在做端到端的测试, 输入测试数据就是原始的数据,这时被测对象要负责特征工程的工作。所以这个时候特征情况就会影响性能的表现。
数据质量保证
评估数据质量
不同行业有不同的评估数据质量的标准。但一般可以从完整性、准确性、一致性和及时性共四个角度进行评估。
- 完整性:指数据的记录和信息是否完整,是否存在数据缺失情况。数据缺失主要包括记录的缺失和具体某个字段信息的缺失,两者都会造成统计结果不准确。完整性是数据质量最基础的保障。例如,某个稳定业务的数据量每天约为100万条记录,某天突然下降了1万条,则可能是出现了记录缺失。例如,某科高考成绩表中,每个考卷分数都对应一个准考证号,当准考证号字段的空值数大于0时,则可能是出现了信息缺失。
- 准确性:指数据中记录的信息和数据是否准确、是否存在异常或者错误的信息。例如,成绩单中分数出现负数或订单中出现错误的买家信息等,这些数据都是问题数据。确保记录的准确性也是保证数据质量必不可少的一部分。
- 一致性:通常体现在跨度很大的数据仓库中。 例如,某公司有很多业务数仓分支,对于同一份数据,在不同的数仓分支中必须保证一致性。例如,从在线业务库加工到数据仓库,再到各个数据应用节点,用户ID必须保持同一种类型,且长度也要保持一致。因此,您需要设计数仓的公共层以确保数据的一致性,详情请参见CDM公共维度层设计规范。
- 及时性:保障数据的及时产出才能体现数据的价值。例如,决策分析师通常希望当天就可以看到前一天的数据。若等待时间过长,数据失去了及时性的价值,数据分析工作将失去意义。
在之前提到的数据闭环流程中,到过回流的数据或者说采集的数据需要一套流程来保证质量,否则会影响后续的统计与模型效果。 那一般用什么方式来保证呢。
一种比较常见的方是通过spark脚本来对数据进行扫描来保证数据的完整,准确和一致性: