上周没有更新深度学习的文章了,因为周末花了两天时间准备分享的演讲稿。 这次我挺重视的,人家找到我是相信我能做好一次分享,给他们带来一些东西。而且我也是代表公司去的,不能给公司丢人, 所以准备的时间长了一点。 这次也是一个比较好的机会去专注于模型测试的各种场景做讲解,因为这一次我有足够的时间。去年在大会上分享的时间只有 40 分钟,还要穿插着讲一些 docker 和其他类型的测试内容,所以当时我压缩了很多东西,效果并不好。 而这一次分享加上答疑我足足用上了 2 个多小时的时间专注的去讲模型测试的内容。所以效果会好很多。我也打算把这次分享的内容加入到我们新员工的培训视频中。 这些日子有一些大厂的同行会来跟我聊一些对机器学习业务场景的测试方法,看的出来现在对于这种场景的质量保证的需求越来越多。所以 QA 对于这方面的知识储备还是需要积累的。
大家好,我叫孙高飞,目前正在第四范式做机器学习平台的测试工作。 前些天海波他们联系我,希望我来跟大家聊一下机器学习服务的一些测试策略。 我当时就觉得这个话题挺难讲的,因为机器学习这个东西听上去就挺玄学的。虽然自从阿尔法狗出世以后大家都知道机器学习是做人工智能的,但人工智能到底是个什么东西,很难解释的清楚。 因为里面有太多专业术语和领域知识。 我刚入职第四范式的头一个月的时候基本都是懵的,开会的时候我都不知道他们在说什么。 所以这也是我们说测试人员做机器学习相关的测试是比较难的, 因为测试行业发展了这些年,QA 们已经形成了一套比较固定的技术栈和发展路线, 但机器学习并不在其中,同时机器学习也是一个没办法速成的领域。 它不像我们学 python,学 java,学 kubernetes。给我点时间我就能玩起来。 它需要经年累月的学习和实践,这么说吧,机器学习是个比较看经验的领域。我以前跟我们公司的数据科学家聊过,就说你们在之前公司的时候 QA 是怎么测试的,我学习一下,因为我之前没经验么,完全不知道怎么测。 但他们都说没有 QA 测试,招不到合适的。 然后这个问题就尴尬了,我们在完全没有经验可借鉴的情况下开始一个一个的踩坑。 时过两年,我们总算也是总结出了一些东西,希望这些东西能对大家有所帮助。
我们先说说什么是人工智能,毕竟我们想要测试一个东西就要先理解它么。 不严谨的说,现阶段的人工智能是:大数据 + 机器学习。 那为什么是不严谨的说呢,因为人工智能的范畴还挺大的,机器学习是其中的一个子集, 现在大部分人把机器学习和人工智能划等号,因为我们目前在人工智能领域产生了价值的项目,主要是机器学习带来的。 我们又说这是现阶段对人工智能的定义, 因为人工智能还是在迅猛发展的,上个世纪 70 年代的时候人们对人工智能的定义还是专家系统呢, 所以人类的对人工智能的理解也是不停的在进步的,说不定过个几十年人工智能的实现方式就不是机器学习了。
我们举一个信用卡反欺诈的例子, 以前的时候在银行里有一群业务专家, 他们的工作就是根据自己的知识和经验向系统中输入一些规则。例如某一张卡在一个城市有了一笔交易,之后 1 小时内在另一个城市又有了一笔交易。这些专家根据以前的经验判断这种情况是有盗刷的风险的。他们在系统中输入了 1 千多条这样的规则,组成了一个专家系统。 这个专家系统是建立在人类对过往的数据所总结出的经验下建立的。 我们可以把它就看成一个大脑,我们业务是受这个大脑控制的。但这个大脑是有极限的,我们要知道这种规则从 0 条建立到 1 条是很容易的,但是从几千条扩展到几千零一条是很难的。 因为你要保证新的规则有效,要保证它不会跟之前所有的规则冲突,所这很难,因为人的分析能力毕竟是有限的。 我听说过的最大的专家系统是百度凤巢的,好像是在 10 年的时候吧,广告系统里有 1W 条专家规则。但这是极限了,它们已经没办法往里再添加了。 所以说这是人脑的一个极限。 后来呢大家引入机器学习, 给机器学习算法中灌入大量的历史数据进行训练, 它跟人类行为很像的一点就是它可以从历史数据中找到规律,而且它的分析能力更强。 以前人类做分析的时候,可能说在反欺诈的例子里,一个小时之内的跨城市交易记录是一个规则,但如果机器学习来做的话,它可能划分的更细,例如 10 分钟之内有个权重,10 分钟到 20 分钟有个权重,1 小时 10 分钟也有个权重。 也就是说它把这些时间段拆的更细。 也可以跟其他的规则组合,例如虽然我是 1 小时内的交易记录跨越城市了,但是我再哪个城市发生了这类情况也有个权重,发生的时间也有个权重,交易数额也有权重。也就是说机器学习能帮助我们找个更多更细隐藏的更深的规则。 以前银行的专家系统有 1000 多条规则,引入了机器学习后我们生成了 8000W 条规则。 百度在引入机器学习后从 1w 条规则扩展到了几十亿还是几百亿条 (我记不清楚了)。 所以当时百度广告推荐的利润很轻易的提升了 4 倍。 我们可以把专家系统看成是一个比较小的大脑,而机器学习是更大的大脑。 所以说我们叫它机器学习,是因为它像人类一样可以从历史中库刻画出规律,只不过它比人类的分析能力更强。 所以在网上有个段子么,说机器学习就是学出来海量的 if else, 其实想想也有道理,套路都是 if 命中了这条规则,就怎么么样的,else if 命中了那个规则,就怎么怎么样的。 人类的分析能力有限么,专家系统里人类写 1 千,1w 个 if else 就到头了。 但是机器学习给你整出来几百个亿的 if else 出来。 我们就可以把机器学习想的 low 一点么,不要把它想的那么神秘。
那接下来我们就聊聊什么是模型了。 模型这个词大家肯定都听过,但它到底是个什么东西相信很多人都是懵的,因为它太抽象了,好像很多领域里都有这个词, 我刚入职的时候一直都在问模型是什么。 都说机器学习算法训练出来的是模型,我们要针对这个模型做测试,但这玩意到底是个什么东西。 我们继续不严谨的说,模型就是个数据库,里面存储的就是我们之前说到的规则和这些规则对应的权重。 我们应用模型的时候怎么做? 其实就是拿着新数据到模型这个数据库里找规则,把匹配的所有规则和他们的权重做乘法并累加在一起,就是我们最后得到的预测值。 我们外围调用的系统都是根据这个预测值展开的。假如说我要给客户做定价,那把客户的数据传过来,看看这个数据符合哪些规则,然后根据规则的权重算出一个预测的价格回来。 不知道我这么解释机器学习的业务是不是清楚。我觉得解释清楚这些特费劲, 以前在阿尔法狗没出现的时候我们出去跟人家聊,最先要突破的难关就是怎么跟人家解释清楚我们不是骗子,因为以前我们一提我们是做人工智能的,人家都会怀疑你是不是跑来忽悠我的。 所以我特别感谢阿尔法狗的出现,打从那以后我终于不用跟人家解释我是不是骗子的问题了。 有个事还特别有意思,我是第 28 号员工么,看上去好像挺靠前的,但我也是公司成立了 1 年的时候才加入的。 我们的 1 号员工是个技术特别厉害的小伙子,前些天的文渊 (就是我们老板) 还问他,说你当初来我们这的时候是怎么判断出来我们不是骗子的。
刚才讲的东西偏业务,接下来我讲讲更偏原理的东西,以帮助我们理解以后的测试场景, 大家不懂担心听不懂, 我还是尽量说人话。 我们讲讲逻辑回归的原理 ,为什么讲逻辑回归呢,因为它是最简单但也是最常用最重要的机器学习算法。 从逻辑回归可以扩展出很多的应用,使用不同的激活函数它既可以做分类问题,也可以做回归问题, 现在非常火的深度学习是什么? 其实就是个多隐藏层的神经网络, 那神经网络呢?神经网络中的隐藏层每一个神经元都是一个逻辑回归,所以我们说逻辑回归是最重要的一个算法。接下来我们看看它的函数。
假如我们有一个预测房价的场景,为了简化说明假设我们只有面积这一个特征,上面这个函数的坐标图。 公式是:y=wx+b, 其中 y 是预测值,x 是特征 (也就是我们的规则),w 是特征的权重,b 是截距。我们说要预测房价改怎么做呢? 其实它就是求一条直线,让我们根据这条直线算出的预测值跟所有真实的这些点的误差最小。所以我们说它在学习什么? 学习的主要就是 w 和 b 的值。我们要求一个 w 和 b,让预测值跟真实值的误差最小。 所以在模型训练的过程中,我们会根据某种规则不停的改变 w 和 b 的值。 我们训练多少轮,就会改变多少次 w 和 b 的值。直到找到最合适的 w 和 b 的值,让预测值和真实值误差最小。这就是它整个的训练过程。上面说我们只有一个特征,如果有 n 个特征呢? 公式就变成了 y= w1*x1+w2*x2+w3*x3......wn*x* +b, 我们刚才说模型是个数据库,里面存储的是所有的特征和他们的权重,在这里大家就知道了存放的就是 x 和对应的 w, 当有新数据来做预测的时候就是用这个公式来计算预测值的. 当然这是一个线性回归的例子,我们把做这种预测某一个值的算法叫做回归算法。 那我们如何做分类问题呢,假如说人脸识别,要判断我和一张照片是不是一个人,它应该告诉我两个值,0 或者 1. 来告诉我这条数据是正例,还是负例。 所以我们引入了一个叫 sigmod 的激活函数,大家不用知道它的原理,只需要知道它能把我们的预测值转换成 0~1 中的数值。 假如说把我和照片的信息输入到模型中,它返回一个数值是 0.8. 意思是这个人有 80% 的概率跟照片是同一个人。 所以通过这个激活函数我们就把预测值转换了一个概率值。 我们外围系统调用的时候会根据这个概率值做一些处理。 比如说我们设置一个阈值是 0.9。 但凡是预测值大于 0.9 的我们判断为正例,小于 0.9 的我们判断为负例。 这样我们把一个回归问题变成了一个二分类的问题。 这个线性回归模型,也就变成了逻辑回归。 所以说我面试的时候,比较喜欢问候选人逻辑回归为什么是分类算法而不是回归算法。 原因就在这里了, 它基于线性回归,但加入了 sigmod 激活函数后就变成了做 2 分类问题的逻辑回归。 如果你再换个激活函数 softmax,它就变成一个多分类算法了。 所以有些时候,尤其是在做深度学习的时候,其实我们不会去说某个神经元是线性回归,还是逻辑回归的,还是多分类算法,因为造成他们不同的可能就是输出层的激活函数的不同。
OK, 我们之前铺垫了很多原理性的信息,现在我们终于可是开始讲正题了。按照我们传统的软件测试思路, 把整个机器学习服务当做黑盒子,这个黑盒子的实体是模型。 我们把数据输入到模型中,得到预测值,判断预测值的正确性。 这个思路是对的,但是我们对这个预测值的正确性是无法通过单一一条数据就能验证的。 就像在 2 分类问题中我们得到的预测值是一个概率值,我们能验证这个概率是正确的么? 假如模型告诉你这个人和照片有 99.9% 的相似度。 但是他们确实不是一个人,你能说他预测错了么? 明显不能啊,毕竟它没告诉你 100% 一定是这个人,不知道大家看过上一季的最强大脑没有,当时吴恩达先生也在场,人机大战中的人脸识别项目中出乎所有人意料的是计算机输出了两个答案,因为在这个项目的志愿者中有一对双胞胎,计算机计算他们两个人的概率的时候只相差了 0.01%, 当时吴恩达先生也不知道选哪个好了,也不说是说概率高的就一定正确。 所以我们一定输入比较大的数据集到模型中,然后统计一些诸如正确率这样的指标来评估模型的效果。
那首先我们先说分类的问题,先从混淆矩阵说起。对于一个二分类问题来说,将实例分为正类(Positive/+)或负类(Negative/-),但在使用分类器进行分类时会有四种情况
一个实例是正类,并被预测为正类,记为真正类(True Positive TP/T+)
一个实例是正类,但被预测为负类,记为假负类(False Negative FN/F-)
一个实例是负类,但被预测为正类,记为假正类(False Positive FP/F+)
一个实例是负类,但被预测为负类,记为真负类(True Negative TN/F-)
为了全面的表达所有二分问题中的指标参数,下列矩阵叫做混淆矩阵 - Confusion Matrix
这是我们评估一个分类模型的时候第一件要做的事情。我们看这个图,上面有一个正负例判定阈值, 我之前说过做 2 分类问题的时候我们得到的是一个 0~1 的概率值,外围系统通过设定一个阈值来判断数据是正例还是负例。 这个混淆矩阵就是要计算不同的阈值下那 4 中分类情况下的分布。那我们为什么要统计这些东西呢。 因为我们之后要讲的关于分类问题的所有评估指标,几乎都是通过混淆矩阵计算出来的。
根据混淆矩阵,我们会统计如上图一样的评估指标。 当然我们最常用的是召回,精准和 F1 score。 接下来我就着重讲一下这几个指标。 一般来说可能大家的直觉说我们统计模型的正确率就好了,正确率越高说明模型效果越好。 这其实是一个普遍的误区。 举一个例子把,假设我们有一个预测癌症的场景,健康的人有 99 个 (y=0),得癌症的病人有 1 个 (y=1)。我们用一个特别糟糕的模型,永远都输出 y=0,就是让所有的病人都是健康的。这个时候我们的 “准确率” accuracy=99%,判断对了 99 个,判断错了 1 个,但是很明显地这个模型相当糟糕。因此需要一种很好的评测方法,来把这些 “作弊的” 模型给揪出来。
首先是精准率 (precision)。还是拿刚才的癌症的例子说。精准率 (precision) 就是说,所有被查出来得了癌症的人中,有多少个是真的癌症病人。公式是 TP/TP+FP
召回率就是说,所有得了癌症的病人中,有多少个被查出来得癌症。公式是:TP/TP+FN。 意思是真正类在所有正样本中的比率,也就是真正类率 (TPR)。
召回和精准理解起来可能比较绕,我多解释一下,我们说要统计召回率,因为我们要知道所有得了癌症中的人中,我们预测出来多少。因为预测癌症是我们这个模型的主要目的, 我们希望的是所有得了癌症的人都被查出来。不能说得了癌症的我预测说是健康的,这样耽误人家的病情是不行的。 但同时我们也要统计精准率, 为什么呢, 假如我们为了追求召回率,我又输入一个特别糟糕的模型,永远判断你是得了癌症的,这样真正得了癌症的患者肯定不会漏掉了。但明显这也是不行的对吧, 人家明明是健康的你硬说人家得了癌症,结果人家回去悲愤欲绝,生无可恋,自杀了。或者回去以后散尽家财,出家为僧。结果你后来跟人说我们误诊了, 那人家砍死你的心都有。 所以在统计召回的同时我们也要加入精准率, 计算所有被查出来得了癌症的人中,有多少是真的癌症病人。 说到这大家可能已经看出来召回和精准在某称程度下是互斥的, 因为他们追求的是几乎相反的目标。 有些时候召回高了,精准就会低。精准高了召回会变低。 所以这时候就要根据我们的业务重心来选择到底选择召回高的模型还是精准高的模型。 有些业务比较看重召回,有些业务比较看重精准。 当然也有两样都很看重的业务,就例如我们说的这个预测癌症的例子。或者说银行的反欺诈场景。 反欺诈追求高召回率,不能让真正的欺诈场景漏过去,在一定程度上也注重精准率,不能随便三天两头的判断错误把用户的卡给冻结了对吧,来这么几次用户就该换银行了。 所以我们还有一个指标叫 F1 score, 大家可以理解为是召回和精准的平均值,在同时关注这两种指标的场景下作为评估维度。
接下来我们来看一下 2 分类问题最重要的评估指标,ROC 和 AUC
ROC 曲线其实是多个混淆矩阵的结果组合,如果在上述模型中我们没有定好阈值,而是将模型预测结果从高到低排序,将每个概率值依次作为阈值,那么就有多个混淆矩阵。
对于每个混淆矩阵,我们计算两个指标,纵坐标是召回率。横坐标是假正类率,这样就得到了 ROC 曲线。里面的逻辑比较复杂我们不细说,但总结的来讲,ROC 曲线的面积越大,模型的效果越好;ROC 曲线光滑以为着过拟合越少。 但是我们看一个图是难以比较两个模型的效果的。 所以我们把 ROC 的阴影面积计算出来,就是 AUC 了。AUC - Area Under Curve 被定义为 ROC 曲线下的面积。AUC 值越大,证明这个模型越好。 AUC 是我们最重要的评估指标。ROC 不好量化,而 AUC 在 0.5~1 之间。 值越大越好。非常容易量化模型好坏。
OK,我们现在有了 AUC 和一些常用的评估指标。 但这些往往还是不够,因为我们每一个模型都是有业务场景的,业务场景有很复杂的逻辑和不同的侧重点。 以前在测试模型的时候经常的漏测场景就是忽略了业务含义而做的测试。 举个例子,我们拿某视频网站的视频推荐系统来说。 我们使用二分类模型来做推荐问题, 怎么做呢, 我们有一个用户, 同时有 2000 个视频候选集,我们分别用这些视频和用户分别用模型算出 2000 个概率,意思是每个视频用户会点击的概率。然后从高到低排序。把排行前面的视频推荐给用户。 所以我们就把一个推荐系统的问题转换为一个分类问题。只不过这里我们不设置阈值了而已。如果按照我们刚才讲的评估指标,我们计算了 AUC,召回,精准。 就可以了么,大家想一想这样做有没有什么问题? 问题是这些指标的统计都是建立在所有测试集下进行的统计的,粒度是非常粗的。 也就是说我们并没有对测试数据进行分类。 举个例子, 在视频网站中, 新用户和新视频的点击情况可能也是比较看重的指标。 尤其是视频网站上每日新增的视频是很多的,对于新增的热点视频的曝光率也就是被推荐的概率也是很重要的维度。 假如我们有一个模型,我们测试之后发现各项指标都很好,但其实可能它对新用户或者新视频的预测并不好,只是由于新增的用户和视频在整个数据集中的占比太小了,所以从整体的评估指标上来看是比较好的。 所以一般我们测试一个模型的时候,要根据业务引入分组 AUC 的验证
什么是分组 AUC 呢, 是就我们把预测结果按照某一个字段或者某一种维度进行分组,去统计每一个组的 AUC,来评估一下我们的模型在相应的业务场景下是否都有比较好的效果。 比如上图是根据用户性别做的分组,如果我们的业务中性别是个很重要的维度,例如购物网站。我想知道我推荐的东西男性和女性都有不错的点击率,而不是清一色偏向了女性用户。 当然这只是个比喻,实际上的情况要根据自己的业务来定。同时统计分组 AUC 也可以评估测试数据的有效性。 像这个图中除了分组 AUC 之外也会统计每个组中是否有全 0 样本,或者全 1 样本。 如果出现了这种情况,说明我们的测试数据在采集上本身就有问题。
如果我们做回归模型的评估,比如定价,预测抵达时间,预测店铺的出菜时间这类的问题。用到的评估指会相对简单一点。 不用统计混淆矩阵这些东西。 主要是看预测值与真实值的均方误差。
这是一个回归问题的模型评估报告。 当然虽然没有混淆矩阵和 AUC 这些评估指标。 但是做分组验证还是必要的。
接下来我们说说测试数据采集需要注意的地方。 在机器学习中我们说特征是最重要的, 你的模型效果能达到多少取决于在原始数据中抽取特征的质量。 变幻算法和调参只是能尽量提高模型的效果,但是能提高的效果是有限的,如果特征抽的不好,调一辈子参数也是没用的。在网上有个段子,说在中国做机器学习的不是算法工程师,是调参工程师。 因为算法都是开源的,不用自己实现, 工程师就是对着算法的参数调啊调,猜啊猜。 其实这不是对的, 真正的建模工程师是不会大部分时间跑去调参数的, 他们最大的精力是放在理解业务,对业务数据进行统计,分析,尝试并验证特征效果。 我们公司内部举办建模比赛的时候,你看头一个礼拜基本上没几个人提交模型。我们都在对着数据抽特征呢。猜这个字段是干什么的,那个字段是干什么的。哪几个特征组合在一起效果会更好。 这个场景的业务是怎么流转的。 上一次比赛的时候我用 LR 去训练模型, 调了一半天参数 AUC 就只提升了 0.06。后来我就加了个组合特征,AUC 就飚上去 0.08 了。 所以其实我们做机器学习相关的工作,基本上就是跟数据在打交道。 建模工程师整天在数据中统计分析抽特征。 测试工程师也是类似的, 我们刚才讲的所有的模型评估指标都是建立在有一个良好的测试数据集的基础上。 实际上这些评估指标没什么难的,都是死的东西。 你做过一遍,写好工具以后就固定下来了。最难的部分反而是我们能够根据业务的特点,数据的特点,抽取出合适的测试样本。 你们在网上随便搜一下就能看见我之前说的所有评估指标的详细文章,公式,以及如何实现的。那为什么还是有很多地方做的模型测试都没什么效果? 我之前跟一些公司的同行也都聊过一些测试相关的东西,他们有些人也都知道统计 AUC,看召回,精准这些东西。 但为什么测试效果还是不好,模型上线后还是会出问题。 一个主要的问题就是出在了测试数据上。 采集测试数据是要根据业务特点的,也是有一定的套路的。 不是说随便找一份数据或者直接从线上引流就可以的。 那接下里我们就聊一下一些常见的场景。
在模型的生命周期中,我们会有三种数据, 分别是训练集,验证集和测试集。 训练集用来训练模型, 验证集用来快速验证和调参。再参数调的差不多以后才会用测试集出一个完整的评估报告。 在这里验证集和测试集有一个讲究就是这两份数据要处于同一分布。就是说这两份数据的来源要是一致的。为什么呢,我们知道机器学习是一个从历史中学习规律的一个机制。你给它什么样的数据,它就学出什么样的效果。 在这里有一个我们常用的词叫做拟合,我们总能听到类似于说模型在训练集上过拟合了。我这么解释吧。 从现象上来讲,一般如果我们在建模的时候训练集的 AUC 比较高,但是在验证集或者测试集上的 AUC 比较低,就是过拟合。 就是说我们的模型在训练数据上效果特别好,但是一放到测试数据上就跪了。 为什么呢,因为训练数据测试数据是有差别的,他们的数据分布,特征分布都不一样的。世界上就没有两份数据是能抽出相同的特征的。 所以如果建模的时候我们规则划分的特别细,细到了能完美匹配训练数据的所有情况,变成了一个针对训练数据完美定制打造的模型。 所以在训练数据中统计的 AUC 特别高。 但就是因为太细了,其他的数据不吃这一套,他们有不同的特征分布,这么细的规则粒度反而在其他数据上的效果很差。 这就是过拟合。所以我刚才说一个模型的生命周期中应该有 3 个数据。 大家可以理解为训练集和验证集是建模工程师用来训练模型的。 所有规则的建立和验证都是在这两份数据上做的。 也就是说模型拟合的是这两份数据。 而我们要用第三份数据,也就是测试集来验证我们的模型是不是在真实的用户场景下也拟合的不错。 所以我再一开始就说验证集和测试集的来源要一致,验证集是建模过程中用来做自测的, 测试集是最终验证模型效果的。 如果这两份数据任何一个出现了偏差都会出问题。 这两份数据也可以达到互相验证的作用。 当最后模型评估报告出来的时候,如果效果不好,就可以去看一下是验证数据有问题,还是测试数据有问题。 所以作为 QA,我们在采集测试数据的时候一定要非常小心,保证我们的采样是符合真实情况的。 我再举个因为数据出现问题的例子, 阿尔法狗。当初阿尔法狗在对战李世石的时候输过一局, 这个例子就是一个典型的由于数据出现偏差而导致的错误场景。 首先我们看一下阿尔法狗的训练过程。
我们看这个过程,最大的问题是这样的,80w 的人类棋局被淹没在机器下的棋局中,也就是说模型拟合的最多的是战胜阿尔法狗自己的场景。这导致的结果是什么, 是一旦它遇到了逆风局的时候,几乎丧失了胜利的可能性。 为什么呢? 一般下围棋的时候,在一块地盘上碰到了逆风局,人类棋手会争取另开战场,把局势搞复杂,越复杂越好,只要对手犯错自己就有机会。 但是阿尔法狗不会这么想,因为它拟合的是如何战胜自己的数据。它预测出的是自己犯错的概率而不是人犯错的概率, 但他自己几乎是不会犯错的, 所以它觉的即使另开战场对手也不会犯错。根据它的计算,它怎么下都是输。所以我们在看那场比赛的时候,感觉阿尔法狗跟梦游一样。 当然了,这只是行业内大家做出的分析,也并不一定准的。但是当初确实有一种声音是,想要战胜阿尔法狗,就要突然发力一招定乾坤,压制住他。
所以我们可以理解为,在建模,自测时拟合的数据和真实数据的差异导致了这样的结果。我说了这么多就是再强调一件事,对于机器学习服务来说,数据永远是最重要的。
所以根据刚才我们说的,我们说要尽量使用生产环境的数据, 因为这样最符合真实的用户场景。这个相信还是比较好理解的。除了数据真实以外,还有一点就是能够早一点发现一些真实场景下会出现的一些比较鬼畜的事情。 比如说我们以前给客户做 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 上怎么操作了就是理解了。 而是我们要理解用户地域上,时间上,或者是其他隐含模式的分布,理解用户的行为模式,甚至是理解产品的盈利模式。 去找到隐藏在数据中有价值的信息。 再去验证模型是不是能够帮助到我们的产品。 所以我才说在这个领域,相比于技术,可能经验更加重要。大数据处理技术和机器学习的知识储备可能是你能够进行这种工作的基本条件。 但是决定能不能做的好,能不能产生价值,取决的是你再当前业务领域下的经验。 它跟我们以前做的测试不一样, 比如接口测试,给我一份接口文档我很快的就能测试起来。 业务测试,给我一份需求分档我也能很快写出一堆的测试用例来。 但当你刚进入某个领域做机器学习相关的测试的时候, 你基本上是什么都做不了的, 需要花大量的时间去学习业务,分析数据,建立一些基础设施。 之后才能慢慢的输出一些价值出来。 这也是我今天要讲的最主要的内容。 接下来还剩下一点的时间,我来讲一些重要的测试场景。
我们刚才在推荐系统的例子中说到过,某些场景的特征随着时间发生巨变,为了保证模型的时效性 需要引入自学习机制使用每天最新的数据更新模型。 流程如下:
它的原理是比较简单的, 假如我们在线下使用了 N 天的数据进行了模型训练并上线。 那么在 n+1 天的时候数据采集系统会收集到最新一天的数据, 这时候我们会抛弃最老的一天的数据,因为它的已经过了时效性。 之后加入新的数据参与模型训练来保证模型的时效性。 这就是自学习的一个简单的原理说明。 但自学习并不是简单的把数据加入到模型中并更新线上模型的过程。 模型上线是第一个很严肃的事情,中间出现了偏差会导致很大数额的经济损失。也许我们会有一种错觉就是我们的模型算法,参数,和抽取特征的方式都已经在线下固定下来了。 自学习只是重复之前线下的步骤, 已经可以不用作测试了。 这是不对的,因为我们作为源头的数据已经发现了变化, 我们并不能假设这份数据是正确的,并且是符合预定的用户画像的。 所以我们起码要在自学习中有下面这样一个数据闭环的流程。
首先我们要有数据采集系统收集每天最新的数据。 这是数据回流, 然后对数据本身做测试和预警, 我们要对数据进行扫描,统计,分析来保证这份数据是符合建模场景的。 一个是因为数据本身是可能在数据采集中出现错误的, 比如某类商品是有一个价格区间的,但是由于数据采集问题或者上游系统的 bug,可能会出现这个商品的价格的异常波动。 或者另一个是因为某些外界因素导致用户行为的突变。 比如情人节那天,双十一那天。 在某种情况下用户行为跟过去产生了一些颠覆性行为,而且这些行为持续的时间可能很短暂,比如过了双十一那一天以后,可能用户的行为就回归到正常的模式。那么我们还要不要把这一天的数据加入到模型中去预测第二天的行为呢? 这个其实就有待商榷了。 总之不管是什么原因导致的数据本身的分布异常,都要做出预警,大家一起来判断一下要不要使用这份数据,或者说要不要做一些处理后再使用。 所以我们的数据预警机制要每天扫描数据检查异常字段, 要和过去的数据做对比,统计数据是否出现了异常分布。比如前一天我们产品还是女性用户比较多,但突然今天的数据男性用户暴增。 这时候就要看看是什么导致的用户行为的变化。是不是要调整模型训练策略。 经过了数据预警后,我们把数据传递给算法进行模型训练。但有些时候即便模型评估报告中的指标很好,我们心里还是没底。 会不会评估报告不准确? 所以这里我们还会加入 A/B Test 去对比线上模型和新模型的效果。 我们线上的流量导入到线上模型的同时,也会 copy 一份流量到自学习训练出的新模型上, 对比模型之间的效果。 当我们确定了新模型起码不比老模型差的时候,才会切换线上模型。 当然了这里也有一种策略不是一下子切换的,而是根据时间或者效果慢慢的切换,比如先是只把 10% 的流量导到新模型上,90% 的流量还是放在老模型上。 这样一点一点的把流量切到新模型上。 有一个缓冲。 这里怎么做就看具体情况了。 那么我们经过了 A/B Test 以后就将模型上线了。 当然线上也会有监控系统来统计当日的 AUC 等等一些指标做最后的确认。 监控这方面就不详细讲了。 模型上线之后在线上产生价值,产生新数据。 最后通过数据采集系统,数据回流到自学习系统中。 一个数据闭环就这么形成了。 要想做自学习系统就要完成这个数据闭环。 当然我们对这个数据闭环的效率是有一定的要求的, 自学习迭代的快就能尽快的让模型上线,就能尽快的保证模型的时效性, 让模型产生最大的价值。 因为我们说过这类场景的数据特征随时间变化的很快,所以自学习的迭代速度也要快。 这种场景一定程度上就是在抢时间。 当然怎么加快迭代速度和工程化这是我们的产品和系统要解决的问题,我们在这里就不多做讨论了。
最近两年我们测试圈子里出现了一个新的测试方式,就是前后端接口的契约测试。 那么在模型里,其实也有类似的事情。 可能大家觉得模型输出的就是预测值么,就一个字段。 还有什么契约么。
机器学习本身是给系统中其他的模块提供服务的,它并不是直接面向用户的。 所以在外围会有一些调用模型的系统。 对于外围系统来说, 模型的输出是预测值。 如果是分类问题的话,模型本身并不会给你统计混淆矩阵,设定阈值。 它就只管输出预测值,至于外围你怎么设定阈值,怎么统计指标都是你们自己的事。所以预测值,就代表了模型对外围系统的一个契约。 我承诺在阈值设置为多少的时候统计指标可以达到什么样的标准。 这就带来了一个问题, 每次模型更新的时候,它的预测值是否是稳定的。 为什么说要求他是稳定的呢? 难道预测值还会变么? 是的, 预测值可能是会变的。 对于混淆矩阵来说,我们正负例的判定是根据阈值来判定的。所以即便预测值的分布不一样,但是通过调整阈值是完全可以达到同样的评估指标的。 所以外围系统在老模型下调整的阈值可能是固定的,那么在模型更新的时候,我们要保证模型输出的预测值对于与之前的预测值的分布是处于稳定的。 如果阈值不稳定,外围系统的预测基本就瘫痪了,需要及时通知他们改变阈值。 其实除了阈值以外,预测值本身也是有意义的,有些外围系统是不需要设定阈值的。 比如推荐系统, 要知道我们是把推荐系统的问题转换为二分类模型去解决的,虽然我们评估指标中有混淆矩阵有阈值。但当推荐系统真正使用模型的时候是会去管什么阈值,什么正负类的。它的做法是如果给一个用户做推荐,那么有一份用户数据,还有 N 个,例如 2000 个视频的候选集。 我们每一个候选集都和用户去做一次预测。得到预测值。 把 2000 个候选集的预测值都算出来以后,按预测值排序,取预测值 top N 的候选集推荐给用户。 也就是说,对推荐系统来说,预测值就是一个概率,可能不管怎么样我都要向用户推荐一些内容,这时候我不管你预测用户点击的概率有多低,我都要把排行前几的候选集推送出去。 所以这时候什么阈值,正负类对我来说是没意义的。这是一种 2 分类模型转换成推荐系统的方式。 这时候虽然没有了阈值,但是预测值本身就很重要了。 我们在评估模型的时候是按照 AUC,混淆矩阵等去评估的,即便预测值整体变小了但是调整阈值依然可以达到一个比较好的召回和精准。 但对于推荐系统来说预测值减少了就是代表用户的点击率越来越低了。 所以我们还是要去分析一下,为什么我们预测用户会点击的概率越来越小了。 也就有了下面这个评估指标
我们按照预测值进行分组,一般分成 10 组,0.9~1 是一组,0.8~0.9 是一组,以此类推。 我们要对比新旧模型的各段分组的样本占比。也可以统计一些分组指标,例如 AUC,召回,准确率等指标。
我们还有个一模型跟外围系统耦合的场景就是决策引擎。在上个世界 6,7 十年代的时候大家对人工智能的定义是专家规则。 是说某个领域专家向系统中输入各种不同的规则来预测事物, 理论上只要输入的规则足够多,就可以达到人工智能的效果。 后来被证实为不靠谱的,因为人的分析能力的限制,我们无法输入那么多的规则。 但专家系统的使用却是保留了下来, 比如现在的银行中还保留了很多的专家系统,即便他们已经引入了机器学习能力但仍然保留着专家系统的使用。 为什么呢?主要为了防止黑天鹅事件,这些黑天鹅事件发生的概率很小,在庞大的训练样本中只占有了非常小非常小的份额,机器学习是一个偏向历史的预测系统,这么小的数据量很难针对这些黑天鹅事件学习出较好的模型。 但这些黑天鹅事件一旦发生的话会造成难以挽回的损失。 所以在这种情况下需要将人类指定的规则 --- 专家系统和机器学习系统混合使用。 某些情况下使用专家系统,某些情况下使用机器学习模型。 而做这个事情的,就是决策引擎。所以除了对模型本身进行测试以外。我们还要对决策引擎的规则进行测试。 到底什么时候用到了模型,什么时候用到了专家系统。 整个决策引擎的效果如何,都要进行测试。
今天的内容呢我就讲的差不多了, 我们围绕着模型测试说了很多, 并没有接触一些建模过程中的测试。 一个是因为时间有限,再一个是因为如果想做这方面的测试,真是就是要对懂机器学习非常熟悉的。有些人曾经问过我怎么能深入算法进行测试,甚至做一些单测级别的测试。 我不太建议这么做,因为培养这样一个 QA 的时间和成本会很高,但产生的价值可能并不如我们预期的那样高。所以我们刚入行的时候,就在思考。QA 在这个机器学习领域中的定位应该是什么样的,做一些什么事能产生更大的价值。 后来我们觉得,有机器学习的业务测试和工程效率这两条路可以走。 业务测试上,如果像我们公司一样拥有机器学习平台。 那么 QA 除了模型效果的测试之外,还可以模拟建模工程师,模拟建模场景进行测试。 当机器学习平台化以后,它就变成了一个产品,建模工程师是这个产品的用户。 而 QA 要做的,就是让自己无限的接近建模工程师,这个其实挺好理解的,就像让如果让我们去测试一款 c++ 的 IDE,那么我们得让自己无限的接近 C++ 的开发工程师对吧。 另一条路是工程效率, 我们觉得算法底层的测试最好还是交给研发工程师来做,而 QA,要提供好用的测试框架,帮助研发做 CI,CD。所以其实我们团队有两位同事专门在协助算法组的同学进行测试。 但她们主要提供框架和工具,和一些集成测试。 我觉得在机器学习这个领域,工程化和迭代速度非常的重要,就像之前说的,自学习场景就是在抢时间。 但要抢时间的不仅仅是自学习场景。 线下的调研,开发,测试同样都在抢时间。 我们说 AI 就是大数据 + 机器学习。 那么它的特点就是运行的很慢。 即便你用非常小的数据,全程自动化,跑通整个建模过程可能都要 10 来分钟。更何况我们测试的时候基本都要引入很大的数据来测试的,一条流程上的 case 跑个把小时的不奇怪。 所以我们对测试环境的独占性要求很高,因为如果共用一套环境,大家互相踩踏,可能随便一个操作就导致其他人的任务报销了。 也没什么好办法,无非是空间换时间,砸钱换效率。我们用 docker+k8s 搭建了一个容器集群,写了一个很方便的部署软件,让大家可以很方便的搭建自己独占的测试环境。 提测阶段的时候会有 70 套左右的环境,其中还有一些要做 CI,CD。每日上千次的构建行为还是有的。 我们觉能够支持这样一个规模的系统,也是有很大价值的。 所以我们团队的 QA,除了测试工作以外。几乎每个人都会负责一块工程效率的工作。 比如我主要做 k8s 和 docker, 我的一个同事负责搭建和维护各种规模和版本的 hadoop 集群, 另一个同事专门负责 jenkins 和 bamboo 上 job 的配置,集成 CI,CD, 也有专门为底层算法工程师提供测试框架和工具的。 恩, 这就是我们在这两年里对测试人员在这个领域中的一些思考。 那今天的内容我们就说到这把。
我也希望能跟大家一起交流这方面的经验,我再 testerhome 上也在写一些文章来记录我对机器学习的理解和一些测试场景。也欢迎大家留言一起交流心得。
https://testerhome.com/topics/11549