构建机器学习系统

256 阅读8分钟
原文链接: renxiawang.com

最近看完了Machine Learning Systems - Designs that scale。这本书以反应式系统reactive systems)为指导方法论,讲述了如何构建一个可扩展的机器学习系统。这本书专注在工程上的问题,而非科学上的问题,即关注点在如何构建一个端到端的工程系统,而非某种机器学习算法的开发、优化之类的问题。

这篇文章总结了我从书中学习到的主要内容。因为反应式系统所强调的各种特质均属于工程师都应该知道的构建健壮、灵活的、可扩展的系统最佳实践类知识,所以这方面的内容被我刻意略过。书中大部分代码基于Spark MLlib和Scala实现,这部分也不是我阅读这本书的主要目的,因此也略过。下面有些例子也参考了著名的西瓜书。

一个机器学习系统可以抽象成5个组件,每个组件的输出是下一个组件的输入:

  1. 数据收集组件:将数据从机器学习系统外的地方收集、注入到系统中来。这个组件的输入是所有可能有用的数据,输出是按照系统需求持久化的数据。
  2. 数据转换组件:从原始数据中转换、抽取出有用的数据表示。这个组件的输入是原始数据,输出是可用于模型学习的数据集。
    • 特征(features):从原始数据派生出来的对做预测或分类有用的数据点。比如判断一个西瓜是不是好瓜时,西瓜的色泽、根蒂和敲声都可以是特征。
    • 概念(concepts):是机器学习模型要预测或分类的目标值,可以是离散的或连续的值。如西瓜是不是好瓜的例子中,好瓜非好瓜即是概念,因为是离散值,也通常称为标记。如果是预测房价的模型,则房价即是概念。
  3. 模型学习组件:从特征和概念学习机器学习模型。这个组件的输入是有特征和概念组成的数据集,输出是有效的机器学习模型。
  4. 模型发布组件:将模型持久化以供使用。这个组件的额输入是机器学习模型,输出是持久化的机器学习模型。
  5. 模型服务组件:将模型用于处理预测请求。这个组件的输入的用户预测请求,输出是机器学习模型针对用户输入预测的结果。

数据收集

为了使收集到数据保持最大的真实有效性,收集的过程不要做数据装换。比如在一片森林里安装一些传感器,收集动物迁徙的数量规模,能得到的原始数据是:在某个时间点A,动物C1从这里经过9只,动物C2从这里经过14只…。如果收集的过程中将数据转换为:在某个时间点A,动物C1从这里经过少于10只,动物C2从这里经过大于10只…。那样就丢失了一些原始数据的细节了。

收集到的数据应该是不可变的数据,即不能去修改。如果一个关于一个主体的数据,在不同时间有不同的变化,那就以多条数据记录每个时间点这个主体的数据,而不是一直去更新这个主体的数据。

为了使数据收集组件可以扩展,需要避免存储共享的可修改状态。这是可扩展系统的一个常见特质,share-nothing。数据也应该可以以任何顺序写入原始数据数据库。

数据转换

由于数据的不确定性及机器学习过程的实验特质,特征抽取的代码很容易变得庞大的和复杂。为了应对未知的对特征的需求,特征抽取和转换的代码应该好好组织,并且没有side-effect,以达到可以灵活复用和组合使用的目的。这一部分的挑战在于如何设计灵活的数据转换器以满足不确定的数据及多变的实验需求。

模型学习

书中的大部分示例实现都基于Spark,然而目前Spark对深度学习的支持还不算完善。因此目前需要用到深度学习的地方,很可能都需要借助对Scala支持不完善的框架如Tensorflow。为了使基于Scala的系统可以与基于Python的Tensorflow模型通讯,可以使用外观模式(Facade Pattern)。本质上是使用如Pyro4的工具,让基于Tensorflow构建的深度学习模型可以从Scala通过(本机)网络接口访问。

学习得到的模型需要评估,评估通过后才能上线进入生产环境。量化评估的指标有准确率(accuracy)精确率(precision)召回率(recall)F-measureAUC

为帮助理解,我们还是用西瓜的例子。假设有100个西瓜,80个是不好的,20个是好的。目标是找出所有好瓜。模型准确地预测了20个好瓜,50个坏瓜,也错误地将30个坏瓜分类为好瓜。

准确率指的是给定一个测试数据集,预测正确的样本占总样本数之比。上面的西瓜例子中,模型正确判断了70个瓜的好坏,那么这时准确率就是70%。但这个值并不是总是能用来判断一个模型是否有效。如果给的测试数据是1000个瓜,其中只有1个好瓜,当模型把所有瓜都预测为坏瓜时,这个指标给出的准确率是(1000-1)/1000=99.9%,这显然并不是一个有效地指标。

下面来看看精确率、召回率、F-measure和AUC。这几个指标均依赖于对true positive、false negative这些分类的定义。以前经常对这些名词感到懵逼。使用混淆矩阵(confusion matrix)可以帮助理解:

预测结果
true false
真实情况 true true positive 真阳性 TP false negative 伪阴性 FN 漏报
false false positive 伪阳性 FP 误报 true negative 真阴性 TN

我们可以得到西瓜例子中TP、FN、FP、TN的值:

  • TP: 20
  • FN: 0
  • FP: 30
  • TN: 50

精确率针对的是预测结果。其公式是

P = T P T P + F P " role="presentation">P=TPTP+FP P = T P T P + F P

即预测为正的样本中有多少是真正为正的正样本,即在我们的例子中,预测为好瓜中有多少是真正的好瓜。

召回率针对的是总样本。其公式是

R = T P T P + F N " role="presentation">R=TPTP+FN R = T P T P + F N

即样本中有多少为正的样本被正确地预测了,即在我们的例子中,有多少真正的好瓜被预测为好瓜。

F-measure是精确率和召回率的调和均值,公式是

2 F 1 = 1 P + 1 R " role="presentation">2F1=1P+1R 2 F 1 = 1 P + 1 R

F 1 = 2 T P 2 T P + F P + F N " role="presentation">F1=2TP2TP+FP+FN F 1 = 2 T P 2 T P + F P + F N

F-measure认为精确率和召回率的权重是一样的,然而这并不适用所有场景。

召回率有另一个名字,叫真阳性率(true positive rate, TPR),相应地也有伪阳性率(false positive rate, FPR)

T P R = T P T P + F N " role="presentation">TPR=TPTP+FN T P R = T P T P + F N
F P R = F P F P + T N " role="presentation">FPR=FPFP+TN F P R = F P F P + T N

TPR即样本中的正样本被正确地预测到的概率。FPR即样本中的负样本被错误地预测为正样本的概率。这两个指标可以可视化成一个称之为ROC(receiver-operating characteristic)曲线的图。

我们要的是,给定一个正样本和一个负样本,模型能正确预测正样本的概率高于错误预测正样本的概率。其中曲线下面的面积称为AUC(area under the curve),简单来说AUC的值越大,模型越好。当AUX=0.5时说明模型的效果跟随机猜没什么区别。

基于以上的一些指标,模型学习组件可以自动化判断学习得到的模型是不是好的模型。甚至可以跟上一次迭代生成的模型进行比较以得出这次生成的模型是不是更优。

每一次学习一个新模型,应该讲这个模型学习的context记录下来,如时间、参数、评估结果等,跟模型一起存起来,以便组件本身下次迭代使用,或者供其他组件使用。

模型发布和服务

通过评估后决定可以上线的模型需要打包,以供系统使用到生产环境中服务用户请求。这一部分与其他大部分 web service 工程类似。