注:本文的部分内容来源于作者之前的线上分享[1]和现场分享[2],在此基础上做了更为深入、更为系统化的阐述。
机器学习系统现在已经成为各种大数据应用中的标配,系统中没有机器学习环节都不好意思跟人打招呼。教你如何做机器学习的资料也有很多,但是本文有所不同,本文的重点在于告诉你不要如何去做,也可以说是机器学习中的“反模式”。
机器学习系统就如同热带雨林,极其容易迷失其中,踩到各种大坑,让你遍体鳞伤。本文摘选了我们团队在实践中踩过的一些坑,以及总结出来的经验教训,希望能够充当一份指南,减少大家迷失其中的几率。
0号坑:只见模型,不见系统。
机器学习系统的核心是各种机器学习模型,但并不能说模型是系统的全部,甚至都不一定是系统中最重要的部分。如果把一个完整的机器学习系统比喻成一辆汽车,那么模型可以算作是汽车的引擎,但是我们知道除了引擎以外,影响汽车最终性能的因素还有非常的多,从国外进口一台高级引擎并不代表就能造出世界一流的车来。
在机器学习系统中也是一样,要想模型充分发挥作用,需要在系统构建时具有充分的大局观意识,把模型当做系统的一部分来看待。在这里我们强调其中一点,就是在注意优化模型的同时,更要注意模型的提升是否对系统整体最终效果产生了提升,如果没有,那么要从系统中模型以外的部分找问题。
下面举一个广告系统中的例子来说明问题。我们知道广告展示的决策过程可以简单分为物料召回和排序两个部分,而机器学习经常被用来做排序部分的工作。但是最终的广告点击效果,不仅仅和排序模型的质量有关,同时和召回质量也有很大关系,如果召回的相关性不够好,那么无论怎么排序,最终效果都不会好。这个时候如果眼睛只看到模型效果,是很难找到真正问题所在的。
所以在开发系统的过程中,不能只关注模型本身的好坏,例如AUC之类的评测指标,更重要的是要关注模型对系统最终影响,以调优系统为目标,而不是仅仅调优模型为目标。如果只看到模型而看不到系统,很可能会做出指标漂亮但是没有实效的“花瓶系统”来。
1号坑:忽视模型过程和细节
很多人觉得机器学习模型是个神奇的黑盒子,只需要把样本和特征喂进去,就会有好用的模型参数生产出来,就像马三立相声中的牛肉罐头机器一样。这种黑盒子思维会让人习惯性地忽略模型的细节——例如某个参数为何是这个取值,这个取值是否合理,这个取值对应的样本数据是什么样子,等等——而是把精力都花在调一些外部参数之类的工作上。
这样做的后果是,如果模型效果不好,不一定能够通过调整外部参数来达到调优效果。例如,在样本收集处理过程中,掺入了一些噪音数据没有去除,那么这些噪音数据会影响最终的模型参数,进而影响模型效果。这种问题通过调一些诸如正则化参数之类的参数是无法解决的,真正有效的解决方法是深入的具体参数中,找到表现异常的参数,然后深入到该参数对应的正负样本及其特征,这样逐层渗透地查找问题。典型的LR模型作为当今最流行的模型,很多人只看到了训练速度和扩展性这些优点,而没有充分利用模型简洁性这一特点。LR简洁的参数形式非常适合使用上面描述的问题查找方法来定位问题。
当然,上面这个颇为复杂的查找流程,如果没有趁手的工具帮助,是很难通畅执行的。所以需要构建一套可视化工具,来辅助这个过程。具体地说,以LR模型为例,这个系统要能够支持:对于每个预测的case,能看到起作用的参数及其取值,能看到参与训练这个参数的样本以及特征细节,等等。有了这样一套系统,可能只需要点几下鼠标就能够定位到问题,降低了查找问题的难度,提高了使用的积极性,从而有利于系统的持续提升。
所以,对于机器学习模型,要敢于破除“黑盒迷信”,对其进行“解剖”,有针对性地优化和查找问题,通过把控训练过程细节来把控最终模型。
2号坑:不注重样本精细化处理
大家都知道样本是机器学习模型的“食物”,直接关系到模型效果的好坏。但是很多情况下,我们对待样本的态度并没有足够认真,没有像在乎特征那样在乎样本的质量。以二分类问题为例具体来讲,有两种情况会比较常见。
第一种情况,是对负样本的界定不够细致。负样本的含义一般来说是曝光但是未点击的样本,但是“曝光”是一件需要仔细琢磨的事情。最粗暴的方式是用服务器后台日志中的数据作为曝光,但是这样做会带来一个显然的问题,就是日志中的item不一定全部真正“曝光”,也就是不一定真的被用户看到了。更好的方式是通过页面埋点来记录真正曝光的东西,但是这种方法也会有问题,那就是即使页面上曝光了,用户也不一定真正看到了,或者说用户的眼睛不一定扫到了曝光的区域,毕竟页面那么大,用户的注意力不一定在哪里。针对这种情况一种解决方法是把最后一个被点击的商品以上的作为“真正曝光”的,因为用户既然点击了这一个,那么可以认为这个以上的用户都是看到了的。
第二种情况,也是更底层的一个问题,就是对样本这个概念的理解不到位。统计机器学习的根本思路是根据历史行为学习模式,从而预测未来。所以“样本代表历史”是很容易被接受的定义,但是在实际工作中,更好的样本代表的应该是“我们希望的历史”,而不一定是“真实的历史”。那么“我们希望的历史”和“真实的历史”之间差异在哪里呢?
举一个电商系统的例子。在电商系统的访问记录中,有这么三种类型的数据:
1)有明确购买意图用户的数据
2)随便逛逛用户的数据
3)各种作弊用户或爬虫的数据
这三种用户的行为模式是不一样的,第一种目标明确,浏览的商品关联性强;第二种目标不明确,看的东西相对发散;第三种在规律上明显与正常人不同。这三种类型的数据夹杂在一起,格式完全相同,不做专门的分析是无法区分开的。三种数据混在一起进入模型训练,当然也会得出一个模型,这个根据“真实数据”训练出来的模型,其中的参数都在尽量拟合这三种数据的混合体。但是让我们现在停下来想一想,我们训练模型出来,真正希望服务的用户是哪种用户?第三种肯定不是,第二种可能是,第一种一定是。如果我们真正希望服务的用户只是第一种用户,但是训练数据中包含了另外两种,那么第一种用户的体验一定会受到影响。而如果我们只用第一种用户的数据训练模型出来,那么这种“量身定做”的模型对目标用户的效果一定是更好的。根据我们的实践经验证明,无论是AUC指标还是上线效果,都是有明显提升的。
举这两个例子,目的是提醒大家,除了特征工程,样本工程也同样重要,在某些情况下甚至会更重要。所以在进行训练之前,以及模型调优的过程中,都要仔细思考样本是否真正反映了你的需求,有必要时要对样本做针对性选择。
3号坑:过于依赖算法
机器学习系统的核心是模型和算法,基于模型和算法的可扩展性也是机器学习系统的核心竞争力之一。但是这并不代表系统中的每个环节都一定要用算法来处理,完全摒弃非算法的、甚至手工的方法。很多机器学习系统中都会有一些核心的基础数据,这些数据的数据量谈不上海量,但是纯手工处理也是有一定工作量的,这个时候人们第一反应往往是用算法去处理这些数据,但是有的时候简单粗暴的方法才是真正有效的方法。
我们在构建当当的机器学习系统时,需要对图书文本做一个主体模型聚类,在此之前需要得到一份“干净”的原始文本数据。所谓“干净”数据指的是去除了诸如SEO词、商家乱填写的内容等等之后的一个类似词表的数据。为了达到去除噪音词的目的,我们尝试过很多方法,简单的高级的都试过,都有效果,但都达不到我们要的效果。经过ROI衡量,我们决定人工来处理这些数据,大概花了三个人一周左右的时间,人工处理之后,效果确实非常好。
这个例子并不是在宣传反算法,而是说要根据具体的问题选择合适的方法。这个问题为什么可以用人工方法去做呢?原因有很多,其中之一就是这个数据的变化幅度非常小,人工处理一遍之后可以用很久,如果是一份每天在变的数据显然就不适合用人工来做了。这个问题或许存在更高级的算法可以解决,但是考虑到数据的不变性,以及整体的ROI,从工程角度来讲还是手工比较合算。
所以说,即使是在机器学习系统这种整体比较高大上的系统中,也要具体问题具体分析,需要撸起袖子搬砖的时候,该搬就得搬。
4号坑:核心数据缺乏控制
从数据流的角度来看,机器学习系统中的数据要经过样本收集、特征生成、模型训练、数据评测等等这样一个流程,在这样一个比较长的流程中,不一定每个环节都是自己可控的,那么在那些不可控的环节,就有可能出现风险,而更可怕的是,由于数据控制在别人手里,出现了问题自己还不知道。
以样本收集为例,在大公司里,这样的工作很可能是由统一负责日志收集的平台部门来做的,而算法团队只要拿来用就可以了。这种做法是把双刃剑,好处很明显,就是减轻了算法团队的负担,但是也会带来隐患,就是你拿到的数据不一定真的是你要的数据。
正确的数据只有一种,但是错误的数据却有很多种错误方法。在样本收集方面,前台发送过来的曝光数据也存在着多种可能性,例如可能是缓存起来的数据,也有可能是用来做SEO的数据,等等。这些数据在发送方来看,都是合理的数据,但对于算法模型来看,都不是用户真正看到的数据,而用户真正看到的数据才是我们真正想要的数据。那么作为这份数据的使用方,算法模型很有可能就会受到这种错误数据的影响。最可怕的是,这种错误并不是那种能让程序崩溃的错误,让我们能在第一时间发现,而是完全隐藏在正常数据中,只有你栽了跟头返回来找问题时或许才能发现。
这种错误数据出现的原因是什么呢?并不是一定日志收集团队不负责任,关键在于收集日志的团队不使用日志,或者说出数据的人不用数据,那么就很难要求他们来保证数据的质量。这种分离的状态对于模型算法这种高度依赖数据的应用是有风险的,所以最好能够加强这部分数据的控制能力,如果不能完全自己来做,那么就要有对应的监控机制,做到有问题能及时发现、及时处理,而不是完全交给别人,自己只管拿来用。
5号坑:团队不够“全栈”
全栈工程师是近年来很火爆的一个概念,在机器学习这样一个复杂系统中,每个人都做到全栈未必现实,但是有一条基本要求应该努力做到,就是团队级别的全栈。
机器学习系统的团队一般主要由算法工程师和系统工程师组成,往往会忽略其他角色,比较典型的就是掌握前端技能的工程师。前端技能在机器学习系统中有很重要的作用,例如在效果评测时的可视化展示方面,但是更重要的是,能够一定程度上加强对数据流程的控制能力。例如在上面那个坑的例子中,说到前端曝光收集的问题,如果团队中没有熟悉前端技术的人,是很难找到这种数据发送的隐藏问题的。
当然,技术全栈只是解决问题的手段,更重要的是能关注全部系统的全局性思维。
6号坑:系统边界模糊/巨型系统
机器学习系统与其他软件系统相比,有一个显著的 特点,就是它是建立在实验性、探索性开发的基础上的。尤其是在初次搭建系统的时候,很难做到在完整设计的指导下开发,而大多是一边探索尝试一边开发,到最后达到上线要求的时候,系统也就随之成型了。
但是这样构建出的系统,有个很大的问题,就是很容易做成一个边界模糊、模块耦合、结构复杂的“巨型系统”,这种系统的典型特征包括:
1)模块间不可拆分,样本、特征、训练等步骤都偶合在一起。
2)很多实验性、探索性代码遍布其中,搞不清楚哪些在用,哪些已失效。
3)pipeline特别长,其中包括一些可能已经无用的流程。
为什么会出现这样的系统呢?重要原因之一就是前面提到过的,机器学习系统的探索式的本质。在刚开始做系统的时候,可能样本处理、特征处理这些都比较简单,所以就都写在了一起。随着各个流程处理的精细化、复杂化,每个步骤都在变复杂,但是由于这种变化是在慢慢发生的,很容易形成“温水煮青蛙”的效应,导致系统慢慢变得不可控,从技术债的角度来讲,系统就是在慢慢地、不知不觉地欠债,而且是利息很高的债。
这样的系统必然是难以维护、难以优化的,解决的方法就是逐步的重构,或者借鉴一些成熟的系统架构,然后再做适合自己的本地化。但这其中最关键的,还是要有一颗架构师的心,能够时刻审视自己的系统,评估其健康状况,并适时做出改变。关于机器学习系统技术债的更多详细讨论,可参见这篇文章[3]或这篇文章[4]。
7号坑:不重视基础数据架构建设
数据是机器学习系统的血液,这里面包括各种样本数据,原始特征数据,处理后的特征数据,支撑数据等等,那么提供这些数据的系统和架构就好比循环系统,循环系统的循环、代谢能力直接决定了这个系统的健康程度。
机器学习系统在构建初期,对待各种数据的态度往往是辅助性质的,认为这些数据只是为了模型服务的”燃料“,而没有把它们本身作为严肃的子系统来对待,所以这些数据的架构往往缺乏设计,大多比较随意,可能会有很多难以复用“一次性”代码。这在初期冲模型效果的时候似乎是没有问题的,但是到了系统搭建起来之后,需要频繁地对各种数据进行调整、尝试的时候,如果没有经过精心设计的数据架构支持,每次数据调整都会非常耗费精力,工作量可能不亚于重新出一遍数据。尤其是在系统上线之后,修改数据变得不能那么随意之后,问题就更加严重。
这是一个严肃而复杂的问题,也不是一两个简单方法就可以解决的,而是需要从数据源开始做仔细的设计,设计时充分考虑数据可能的用法,并留有一定扩展性,保证数据的可用性和可探索性。具体实践起来可以考虑微服务的做法,微服务是个性化推荐领先的Netflix公司最早提出并实践的概念,它们实践的场景也是围绕个性化推荐展开的。其中一个比较有趣的例子,是他们构建了一套“特征时光机”系统[5],以微服务的形式把不同时间维度的数据呈献给算法和研究人员,就好像拥有时光机一样,能够在不同时间中容易穿梭,获取数据。此外,在通用的数据基础架构方面,可以参考Stitchfix的做法[6],其核心是将数据流程根据职责进行划分,以求达到最高的团队协作效率。
总结
机器学习系统的坑远不止这几个,但是希望通过这几个代表性的坑,能够让大家意识到,从课本上的模型到实践中的系统,中间有非常多的工作需要做。做出一个成功的机器学习系统,也不仅仅是调调模型,跑跑算法这么简单。对这方面有兴趣的同学欢迎一起讨论。
参考资料:
[1] http://www.csdn.net/article/2015-10-16/2825925
[2] http://www.top100summit.com/think/10785
[3] http://research.google.com/pubs/pub43146.html
[4] https://papers.nips.cc/paper/5656-hidden-technical-debt-in-machine-learning-systems.pdf
[5] http://www.tuicool.com/articles/2qu6Bbf
[6] http://multithreaded.stitchfix.com/blog/2016/03/16/engineers-shouldnt-write-etl/
本文作者:
张相於(@HelloSpirit),微信公众号【ResysChina】特约作者。现任当当网推荐系统开发经理,负责当当网的推荐系统、NLP算法等工作。多年来主要从事推荐系统以及机器学习相关工作,也做过反垃圾、反作弊相关工作,并热衷于探索大数据技术&机器学习技术在其他领域的应用实践。