鹤啸九天 自律更自由,平凡不平庸 Less is More

【谷歌】机器学习43条军规-The-Rules-of-Machine Learning

2017-10-24
Martin Zinkevich
阅读量

Notes(温馨提示):

  1. ★ 首次阅读建议浏览:导航指南
  2. 右上角工具条搜索文章,右下角二维码关注微信公众号(鹤啸九天),底栏分享、赞赏、评论
  3. ★ 转载请注明文章来源,知识点积累起来不容易,水滴石穿,绳锯木断,谢谢理解
  4. ★ 如有疑问,邮件讨论,欢迎贡献优质资料


机器学习方法论

经验

  • 分类是机器学习经典问题,建分类模型容易,但科学建模,不容易
  • (1)数据集划分:训练集、验证集(模型超参)和测试集(验证模型效果)
    • 三个数据集要满足i.i.d.,即独立同分布,否则评测结果波动大,不足信;这里的分布指多个维度,包括但不限于类别分布、采样时间窗
    • 三者不能混淆、重复

      ,如把测试集和验证集当做一个,就是典型的 扫一眼标准答案再答题 ,评测结果没有意义

    • 数据量上
      • 小规模(约10w以内),传统机器学习方法,三个数据集量级比例 3:1:1
      • 大规模(10w以上),深度学习方法,三个数据集量级比例 98:1:1
  • (2)不均衡问题
    • 机器学习书籍中,假设各类别占比相对平衡(即每个样本同等重要),因此很少提及再平衡
    • 实际项目中,基本不存在均衡的数据集,长尾现象无处不在,我面试别人时,如果候选人在分类项目中只字不提样本均衡,十有八九是没有亲自做过,或者技艺不足
    • 处理方法:过拟合、欠拟合、损失函数等,将样本量均衡到10倍以内(经验值),此处又是一个值得深究的子领域
  • (3)评估指标
    • 一般用precision(精确,注意不是准确)、recall(召回)和F1(或Fβ)来算,二分类问题,还有ROC、AUC指标
    • 对于分类问题,只是宏观计算precision和recall、F1,是不够的,因为观测不到“内部细节”,多分类,这种困惑更加突出
    • 建议同步使用混淆矩阵,清晰看到每个子类的表现,是否错分,分到哪个类别,如何改进?
  • (4)再说测试集
    • 实际项目中,i.i.d.对于模型本身评估没问题,但还是看不到线上真实效果,毕竟均衡后的测试集“失真”
    • 建议,测试集同时准备两份以上
      • 均衡后的测试集:同模型训练集、验证集,用于验证模型真实效果
      • 未均衡的测试集:测试集分布不同于验证集、训练集,纯粹为了观测线上表现
      • 其他均衡策略:用于对比分析,哪种均衡效果更好

机器学习流程

  • 书籍:用tensorflow生态系统搭建机器学习pipeline配套代码
    • 了解构建机器学习管道的步骤
    • 使用TensorFlow Extended中的组件构建管道
    • 使用Apache Beam,Apache Airflow和Kubeflow Pipelines编排您的机器学习管道
    • 使用TensorFlow数据验证和TensorFlow转换处理数据
    • 使用TensorFlow模型分析来详细分析模型
    • 检查模型性能的公平性和偏见
    • 使用TensorFlow Serving或TensorFlow Lite为移动设备部署模型
    • 学习保护隐私的机器学习技术

sklearn


import pandas as pd
from sklearn.cross_validation import train_test_split
from sklearn.preprocessing import LabelEncoder

# 加载数据集
df = pd.read_csv('https://archive.ics.uci.edu/ml/machine-learning-databases/breast-cancer-wisconsin/wdbc.data', header=None)
# Breast Cancer Wisconsin dataset

X, y = df.values[:, 2:], df.values[:, 1]
                                # y为字符型标签
                                # 使用LabelEncoder类将其转换为0开始的数值型
encoder = LabelEncoder()
y = encoder.fit_transform(y)
                    >>> encoder.transform(['M', 'B'])
                    array([1, 0])
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=.2, random_state=0)

# 构建pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
from sklearn.linear_model import LogisticRegression

from sklearn.pipeline import Pipeline

pipe_lr = Pipeline([('sc', StandardScaler()),
                    ('pca', PCA(n_components=2)),
                    ('clf', LogisticRegression(random_state=1))
                    ])
pipe_lr.fit(X_train, y_train)
print('Test accuracy: %.3f' % pipe_lr.score(X_test, y_test))
                # Test accuracy: 0.947


pipeline

  • pipeline这个词,应该来自linux。在linux体系下的各种命令工具的处理,支持pipeline,即管道机制,例如:
cat xxx | awk '{xxxx}' | sort | uniq
  • pipeline是一种良好的接口规范,工具的功能有公共的接口规范,就像流水线一样,一步接着一步。机器学习的处理过程,也可以是pipeline。
  • 实际上scikit-learn开发了整套的pipeline机制,并封装到 sklearn.pipline命名空间下面。

  • PIPELINE
  • sklearn中把机器学习处理过程抽象为estimator,其中estimator都有fit方法,表示“喂”数据进行初始化or训练。
  • estimator有2种:
    • 1、特征变换(transformer)
      • 可以理解为特征工程,即:特征标准化、特征正则化、特征离散化、特征平滑、onehot编码等。该类型统一由一个transform方法,用于fit数据之后,输入新的数据,进行特征变换。
    • 2、预测器(predictor)
      • 即各种模型,所有模型fit进行训练之后,都要经过测试集进行predict所有,有一个predict的公共方法。
  • 上面的抽象的好处即可实现机器学习的pipeline,显然特征变换是可能并行的,通过FeatureUnion实现。特征变换在训练集、测试集之间都需要统一,所以pipeline可以达到模块化的目的。
    • 特征变换往往需要并行化处理,即FeatureUnion所实现的功能
    • pipeline还可以嵌套pipeline,整个机器学习处理流程就像流水工人一样
    • 加上自动调参就完美了,sklearn的调参通过GridSearchCV实现,pipeline+gridsearch简直是绝配
    • Pipeline对象接受二元tuple构成的list,每一个二元 tuple 中的第一个元素为 arbitrary identifier string,我们用以获取(access)Pipeline object 中的 individual elements,二元 tuple 中的第二个元素是 scikit-learn与之相适配的transformer 或者 estimator。
  • 举个NLP处理的例子:
# 生成训练数据、测试数据
X_train, X_test, y_train, y_test = train_test_split(X, y)
# pipeline定义
pipeline = Pipeline([ ('vect', CountVectorizer()), ('tfidf', TfidfTransformer()), ('clf', RandomForestClassifier())])
# FeatureUnion的并行化处理
pipeline = Pipeline([('features', FeatureUnion([ ('text_pipeline', Pipeline([ ('vect', CountVectorizer(tokenizer=tokenize)), ('tfidf', TfidfTransformer()) ])), ('findName', FineNameExtractor())])),('clf', RandomForestClassifier())])
# train classifier
pipeline.fit(X_train, y_train)
# predict on test data
y_pred = pipeline.predict(X_test)

from sklearn.base import BaseEstimator, TransformerMixin

class FineNameExtractor(BaseEstimator, TransformerMixin): 

    def find_name(self, text): 
        return True 

    def fit(self, X, y=None): 
        return self 

    def transform(self, X): 
        X_tagged = pd.Series(X).apply(self.find_name) 
        return pd.DataFrame(X_tagged)

机器学习技术债

系统边界模糊

在传统的软件工程中,一般会进行细致的设计和抽象,对于系统的各个组成部分进行良好的模块划分,这样整个系统的演进和维护都会处于一个比较可控的状态。但机器学习系统天然就与数据存在一定程度的耦合,加上天然的交互式,实验性开发方式,很容易就会把数据清洗,特征工程,模型训练等模块耦合在一起,牵一发而动全身,导致后续添加新特征,做不同的实验验证都会变得越来越慢,越来越困难。

数据依赖难以管理:

传统的软件工程开发中,可以很方便的通过编译器,静态分析等手段获取到代码中的各种依赖关系,快速发现不合理的耦合设计,然后借助于单元测试等手段快速重构改进。在机器学习系统中这类代码耦合分析一样不可或缺,除此之外还多了数据依赖问题。

比如销售预测系统可能会对接终端POS系统数据,也会引入市场部门的营销数据,还有仓储,运输等等多种数据来源。在大多数情况下这些数据源都是不同部门维护的,不受数据算法团队的控制,指不定哪天就悄悄做了一个变更。如果变更很大,可能在做数据处理或者模型训练时会直接抛出错误,但大多数情况下你的系统还是能正常运行,而得到的训练预测结果很可能就有问题了。

在一些复杂业务系统中,输入数据源还会被加工成各种中间数据集,同时被几个数据分析预测任务共享,形成复杂的依赖关系网,进一步加大了数据管理的难度。

机器学习系统的反模式

  • 胶水代码,随着各种开源项目的百花齐放,很多机器学习项目都会调用各种开源库来做数据处理,模型训练,参数调优等环节。于是自然而然在整个项目中大量的代码都是为了把这些不同的开源库粘合在一起的胶水代码,同样会导致之前提到过的边界模糊,与特定的库紧耦合,难以替换模块快速演进等问题。
  • 流水线丛林,在数据处理特征工程与模型调优的迭代演进过程中,稍不注意你的整个系统流水线就会变得无比冗长,各种中间结果的写入和前后依赖极其复杂。这时候想添加一个新特征,或是调试某个执行失败都变得如此困难,逐渐迷失在这混乱的丛林中……如果只具备机器学习知识而缺少工程经验,用这种做实验的方式来开发系统显然是不靠谱的,必须有良好的工程化思维,从总体上把控代码模块结构,才能更好的平衡实验的灵活性与系统开发效率,保证整体的高效运作。
  • 失效的实验性代码路径,这一点也是承接前面,很多时候如果以跑实验的心态来给系统“添砖加瓦”,很可能到后面各种小径交叉的代码库就这么出现了,谁都搞不清楚哪些代码有用哪些是不会执行到的。如何复现别人的实验结果,要用哪些数据集和特征,设置哪些变量做哪些微调都会成为难解之谜。
  • 缺乏好的系统抽象,个人觉得sklearn的各种API设计还算蛮好的,现在很多其它库的高层API都参考了这个准业界标准来实现。文中主要提到在分布式训练中缺乏一个很好的业界标准,比如MapReduce显然是不行的,Parameter Server看起来还算靠谱但也还未成为标准。没有好的抽象标准也就导致了各种库在功能,接口设计时不一致,从而有了以上提到的一系列边界模糊,胶水代码等问题。

配置项技术债

相对于传统软件系统,机器学习系统的配置项往往会更多更复杂,比如要使用哪些特征,各种数据选择的规则,复杂的预处理和后置处理,模型本身的各种参数设置等等。因此除了工程代码外,配置项的精心设计,评审也成了一个不容忽视的点。否则很容易造成系统在实际运行中频繁出错,难以使用。

变化无常的外部世界

机器学习系统很多时候都是直接与外部世界的数据做交互,而外部世界总是变化无常。而且机器学习系统本身的输出也会影响到外部世界,从而进一步回馈到机器学习系统的输入中来。比如推荐系统给用户展示的内容会影响用户的点击行为,而这些点击浏览行为又会成为训练数据输入到推荐系统来。如何获取到外部世界变化的信息,进而及时改进甚至自动更新算法模型就成了一个非常重要的问题。

在谷歌的这篇原始论文中对各种坑都给了一些解决的建议,归纳总结一下总体上来说就是要转变团队整体的文化氛围,强调良好的工程思维和实践。一个设计良好的机器学习项目系统中往往真正跟机器学习相关的代码只占了很小的一部分:

新模型固然酷炫,但是机器学习工程实践的总结与推广与它在整个项目中扮演的角色的重要性显然是不成正比的。所以今天我们要着重来讲一下这个方面:机器学习系统的最佳工程实践是什么样的?

这时候就要请出谷歌的另一篇论文《The ML Test Score》了。前一篇论文在具体实践落地方面缺乏细节,在这篇论文里,谷歌总结了28个非常具体的机器学习系统相关工程实践准则,可谓是干货满满,十分接地气。

文中给出的28个建议都是针对机器学习系统的,没有包含通用软件工程里那些单元测试,发布流程等内容,在实践中这些传统最佳实践也同样非常重要。这些实践在谷歌内部团队广泛使用,但没有一个团队执行的覆盖率超过80%,因此这些测试点都是非常值得关注并有一定的实践难度的。

特征与数据测试

  • 特征期望值编写到schema中:对于很多特征的分布情况或数值期望是有一些先验知识可以去校验的。比如一般人身高都在0-3米的范围内,英语中最常见的词是”the”,整体的词频一般服从幂律分布等。我们可以把这些先验领域知识,或是从训练集中计算出的数学期望值编写在数据schema文件中,后续对于新的输入数据,构建完特征后的模型训练数据以及最终上线使用模型时都能进行自动化的检查,避免因为数据不符合预期而导致的错误预测情况。
  • 确保所有的特征都是有用的:在之前的机器学习技术债论文中也有提到研发人员总是倾向于不断往系统中添加新的特征,尤其在上线时间比较紧迫的情况下,缺少细致的特征选择和有效性验证工作。这会导致特征数量越来越多,构建训练集需要花费的时间也越来越长,后续的维护成本也会更高。所以跟业务代码一样,没有帮助的特征也要及时清理,轻装前行。文中给出的方法基本是常见的特征选择法,比如计算特征相关度,使用单独或小批量特征来跑模型看是否有预测能力等。
  • 去除性价比低的特征:计算添加任何一个特征都需要消耗资源,包括生成和训练模型开销,模型预测开销,甚至还要考虑到对上游数据的依赖,额外的库函数引入,特征本身的不稳定性等等。对于任何一个特征的添加,都要综合考虑这些开销与它能带来的性能提升来决定是否引入。如果只是非常有限的效果提升,我们应该果断放弃那些过于复杂的特征。
  • 特征必须遵循业务规范需求:不同的项目对机器学习系统可以使用的数据可能有不同的规范需求,比如可能有些业务禁止我们使用从用户数据中推演出来的特征。所以我们也需要从代码工程层面把这些规范需求进行实现,以避免训练与线上特征出现不一致或违反了业务规范等问题。
  • 数据流水线必须有完善的隐私控制:与上一条类似,机器学习系统从数据源获取用户相关隐私数据时已经通过了相应的控制校验,后续在系统内部流水线做处理时我们也要时刻注意对隐私数据的访问控制。比如各种中间数据集,数据字典的存放与访问控制,上游系统的用户数据删除能够级联更新到机器学习系统的整个链路中,诸如此类需要特别注意的问题。
  • 能够快速开发新特征:一个新特征从提出到实现,测试,上线的整个流程所需要花费的时间决定了整个机器系统迭代演进,响应外部变化的速度。要实现这一点,良好的工程结构,不同模块的抽象设计都是非常重要的。文中没有给具体的例子,不过我们可以借鉴sklearn中pipeline模块设计的思想,以及类似FeatureHub这样的开源系统的实现来不断优化完善特征工程实践。
  • 为特征工程代码写相应的测试:在实验探索阶段,我们经常会写完一个特征之后,粗略的取样一些数据,大致验证通过后就认为这个特征基本没有问题了。但这其中可能就隐藏了不少bug,而且不像业务代码中的错误,要发现这些bug极其困难。所以必须养成良好的习惯,在特征开发阶段就写好相应的测试代码,确保特征的正确性,后续应对各种系统变更也都能很快通过测试来进行快速验证。

模型开发测试

  • 模型说明必须通过review并记录在案:随着机器学习模型技术的发展,各种复杂模型,大量的参数配置往往让模型训练和执行变得无比复杂。加上在多人协同的项目中,很多时候需要使用不同的数据,或者做一些特定的调整来重新评估模型效果,这时候有详细的模型说明记录就显得尤为重要了。除了简单的文本记录,市面上也有不少开源项目(比如ModelDBMLflow等)专注于帮助开发者管理模型,提高实验的可复现性。
  • 模型优化指标与业务指标一致:很多机器学习的应用业务中,实际的业务指标并不能直接拿来作为目标函数优化,比如业务营收,用户满意度等等。因此大多数模型在优化时都会选择一个“代理指标”,比如用户点击率的logloss之类。因此在建模,评估过程中必须要考虑到这个代理指标与真实业务指标是否有比较强的正相关性。我们可以通过各种A/B测试来进行评估,如果代理指标的改进无法提升真正的业务指标,就需要及时进行调整。
  • 调优模型超参数:这点相信大家都会做,毕竟各种机器学习教程中都会有很大篇幅讲解如何进行调参来提升模型效果。值得注意的是除了暴力的网格搜索,随机搜索同样简单而效果往往更好。另外还有许多更高级的算法例如贝叶斯优化,SMAC等也可以尝试使用。对于同一个数据集,在使用不同的特征组合,数据抽样手段的时候理论上来说都应该进行参数调优以达到最佳效果。这部分的工作也比较容易通过自动化工具来实现,很多AutoML实现都着重于解决这个问题。
  • 对模型时效性有感知:对于很多输入数据快速变化的业务例如推荐系统,金融应用等,模型的时效性就显得极其重要了。如果没有及时训练更新模型的机制,整个系统的运行效果可能会快速下降。我们可以通过保留多个版本的旧模型,使用A/B测试等手段来推演模型效果与时间推移的关系,并以此来制定整体模型的更新策略。
  • 模型应该优于基准测试:对于我们开发的复杂模型,应该时常拿它们与一些简单的基准模型进行测试比较。如果需要花费大量精力调优的模型效果相比简单的线性模型甚至统计预测都没有明显提升的话,我们就要权衡一下使用复杂模型或做进一步研发改进的必要性了。
  • 模型效果在不同数据切片上表现都应达标:在验证模型效果时,我们不能仅依赖于一个总体的验证集或是交叉验证指标。应该在不同维度下对数据进行切分后分别验证,便于我们对模型效果有更细粒度上的理解。否则一些细粒度上的问题很容易被总体统计指标所掩盖,同时这些更细粒度的模型问题也能指导我们做进一步细化模型的调优工作。例如将预测效果根据不同国家的用户,不同使用频率的用户,或者各种类别的电影等方式来分组分析。具体划分方式可以根据业务特点来制定,并同时考察多个重要的维度。我们可以把这些测试固化到发布流程中,确保每次模型更新不会在某些数据子集中有明显的效果下降。
  • 将模型的包容性列入测试范围:近些年来也有不少关于模型包容性,公平性相关问题的研究。例如我们在做NLP相关问题时通常会使用预训练的word embedding表达,如果这些预训练时使用的语料与真实应用的场景有偏差,就会导致类似种族,性别歧视的公平性问题出现。我们可以检查输入特征是否与某些用户类别有强关联性,还可以通过模型输出的切分分析,去判断是否对某些用户组别有明显的差异对待。

对于这个问题的具体处理方式也有很多专门的研究,具体的方法例如:

  • 特征预处理,例如使用embedding映射转换来消除NLP问题中性别维度的差异。
  • 模型开发中的各种后置处理。
  • 收集更多数据以确保模型能学习到少数群体的特性。

机器学习基础设施测试

模型训练是可复现的:在理想情况下,我们对同一份数据以同样的参数进行模型训练应该能获取到相同的模型结果。这样对于我们做特征工程重构验证等都会非常有帮助。但在实际项目中做到稳定复现却非常困难。例如模型中用到的随机数种子,模型各部分的初始化顺序,多线程/分布式训练导致的训练数据使用顺序的不确定性等都会导致无法稳定复现的问题。因此我们在实际工程中对于这些点都要额外注意。比如凡是用到了随机数的地方都应该暴露接口方便调用时进行随机数种子的设置。除了尽量写能确定性运行的代码外,模型融合也能在一定程度上减轻这个问题。

模型说明代码需要相应测试:虽然模型说明代码看起来很像“配置文件”,但其中也可能存在bug,导致模型训练没有按预期方式执行。而且要测试这种模型说明代码也非常困难,因为模型训练往往牵涉到非常多的外部输入数据,而且通常耗时较长。文中提到谷歌将会开源一些相关的框架来帮助做相关的测试。一些具体的测试方法如下:

  • 使用工具生成一些随机数据,在模型中执行一个训练步骤,检验梯度更新是否符合预期。模型需要支持从任意checkpoint中恢复,继续执行训练。
  • 针对算法特性做简单的检验,比如RNN在运行过程中每次应该会接受输入序列中的一个元素来进行处理。
  • 执行少量训练步骤,观察training loss变化,确定loss是按预期呈现不断下降的趋势。
  • 用简单数据集让模型去过拟合,迅速达到在训练集上100%的正确率,证明模型的学习能力。
  • 尽量避免传统的”golden tests”,就是把之前一个模型跑的结果存下来,以此为基准去测试后面新模型是否达到了预期的效果。从长期来看由于输入数据的变化,训练本身的不稳定性都会导致这个方法的维护成本很高。即使发现了性能下降,也难以提供有用的洞察。

机器学习pipeline的集成测试:一个完整的机器学习pipeline一般会包括训练数据的收集,特征生成,模型训练,模型验证,部署和服务发布等环节。这些环节前后都会有一定交互影响,因此需要集成测试来验证整个流程的正确性。这些测试应该包括在持续集成和发布上线环节。为了节约执行时间,对于需要频繁执行的持续集成测试,我们可以选择少量数据进行验证,或者是使用较为简单的模型以便于开发人员快速验证其它环节的修改不会引起问题。而在发布流程中还是需要使用与生产环境尽可能一致的配置来执行整体的集成测试。

模型上线前必须验证其效果:这点应该是大家都会遵守的原则。唯一要注意的是需要同时验证模型的长期效果趋势(是否有缓慢性能下降),以及与最近一个版本对比是否有明显的性能下降。

模型能够对单个样本做debug:这个就有点厉害了,当你发现一个奇怪的模型输出时,你怎么去寻找问题的原因?这时候如果有一个方便的工具能让你把这个样本输入到模型中去,单步执行去看模型训练/预测的过程,那将对排查问题带来极大的便利和效率提升。文中提到TensorFlow自带了一个debugger,在实际工作中我们也会使用eli5LIME之类的工具来做黑盒模型解释,但从方便程度和效果上来说肯定还是比不上框架自带的排查工具。

模型上线前的金丝雀测试:这点在传统软件工程中基本也是标配,尽管我们有测试环境,预发布环境的各种离线测试,模型在正式上线时还是需要在生产环境中跑一下简单的验证测试,确保部署系统能够成功加载模型并产出符合预期的预测结果。在此基础上还可以进一步使用灰度发布的方式,逐渐把流量从旧模型迁移到新模型上来,增加一层保障。

模型能够快速回滚:与其它传统软件服务一样,模型上线后如果发现有问题应该能够快速,安全回滚。要做到这点必须在开发时就确保各种上下游依赖的兼容性。并且回滚演练本身也应该成为常规发布测试的一环,而不是到出了问题才去手毛脚乱的操作,引出更多意料之外的问题。

监控测试

依赖变更推送:机器学习系统一般都会大量依赖于各种外部系统提供的数据,如果这些外部系统的数据格式,字段含义等发生了变化而我们没有感知,很容易就会导致模型训练和预测变得不符合预期。因此我们必须订阅这些依赖系统的变更推送,并确保其它系统的维护者知晓我们在使用他们提供的数据。

训练与线上输入数据分布的一致性:虽然模型内部运作过程极为复杂,难以直接监控其运行时正确性,但是模型的输入数据这块还是比较容易监控的。我们可以用之前定义的特征数据schema文件来对线上输入数据进行检测,当出现较大偏差时自动触发告警,以便及时发现外部数据的变化情况。

训练与线上服务时生成的特征值一致:在机器学习项目中经常会出现同一个特征在训练时和在线上使用时所采用的计算生成方式是不一样的。比如在训练时特征是通过处理之前的历史日志计算出来的,而到了线上的时候同样的特征可能来自于用户实时的反馈。或者是训练时采用的特征生成函数是非常灵活,易于做各种实验尝试的,而到了线上则改用固化的优化计算性能版本以降低服务响应时间。在理想情况下,虽然采用了不同的计算方式,但生成的特征值应该是相同的,否则就会出现训练和预测使用的数据有偏移,导致模型效果变差等问题。所以我们需要通过各种手段来监控线上线下数据的一致性。

例如可以通过对线上数据进行采样打标记录,来与训练集中的对应条目进行直接比较,计算出偏差超出特定阈值的特征数量,及这些特征中相应的有偏差的样本占比数量。另外也可以通过计算线上线下各个特征的统计分布的差别来衡量是否有这类问题的产生,这里同样要注意阈值的设置调整。

  • 模型时效性:这一点与之前的模型测试中的时效性类似,我们可以直接使用其中获取到的模型效果与时间推移关系来推断理想情况下模型训练更新的频率,并以此来对模型持续运行时间进行监控和告警。要注意过长的更新周期会提升模型的维护难度。另外哪怕模型本身更新比较可控,但是模型依赖的数据源也有类似的时效性问题,我们同样需要对其进行监控以免出现数据过期的问题。
  • 数值稳定性:在模型训练中可能会在没有任何报错的情况下出现奇怪的NaN,inf值,导致非预期的参数更新甚至最终得到不可用的模型。因此我们需要在训练中监控任何输入数据中包含NaN或者inf的情况进行适当的处理。同时对于各模型参数的取值范围,ReLU层后输出为0的单元数量占比等指标进行检查,一旦超出预期范围就进行告警,便于及时定位排查相关问题。
  • 模型性能相关监控:机器学习模型的训练速度,预测响应时间,系统吞吐量,以及内存占用等性能指标对于整体系统的可扩展性来说都是至关重要的。尤其是随着数据量的越来越大,越来越复杂模型的引入,更加剧了各种性能问题的出现频率。因此在模型演进,数据变化以及基础架构/计算库的更迭中,需要我们谨慎的评估模型性能变化,进行快速响应。具体在做性能监控时,不但要注意不同代码组件、版本的表现,也要关注数据和模型版本的影响,进行系统的记录与分析。

模型预测质量的回归问题:总体的目标是希望新部署的模型相对于过去的模型在预测性能上没有下降。但验证集相对于线上的真实情况来说总是有所区别的,只能作为一个性能评估的参考。文中列举了几种手段来做监控:

  • 衡量预测的统计偏差,正常情况下模型的预测偏差应该为0,如果不是则说明其中有一定问题。
  • 对于能在作出预测后很快得到正确与否反馈的任务类型,我们可以以此进行实时监控,迅速判断模型效果相比之前是否有下降。
  • 对于在模型提供服务时无法快速获取到正确标签类型的任务类型,我们可以使用人工标记的验证数据来比较模型效果的变化。

最后总结一下前面提到的各种监控,基本上有两个共通点要注意。一是各种告警的阈值要做精心选择,过低的阈值容易出现警报泛滥导致根本没人去管的情况,而过高的阈值又会掩盖系统中已经存在的各种问题。二是除了短时间内明显的指标急剧下降外,同时也要关注长期的缓慢的下降,后者更难以发现,应该引起足够的重视。

文章后续还给出了这28个测试指标的具体评分标准,帮助大家在工程中更好的对照、应用这些最佳实践。对于每个单项,如果是手工执行,并且结果有记录与分发,那么可以得0.5分。如果有专门的系统进行自动化的重复执行,则可以得1分。所以可以看到哪怕有些看起来很简单的测试点大家也并不是都能拿到1分的。如果要计算整体指标,文中建议把四块测试的得分分别加和,然后取这四个组总分中的最小值(因为这四个组别都非常重要)。

根据谷歌内部使用这套评分系统的各种反馈,文中列举了36个业务系统的平均得分情况:

对于这个平均得分情况,作者强力安利了一把谷歌自家的TFX

比如基础架构的集成测试,谷歌内部的得分也很低(平均0.2分)。TFX可以方便的实现整个工程的pipeline,自然也很容易在此基础上完成相应的集成测试了。除了TFX,像Uber的Michelangelo,Facebook的FBLearner Flow也都是类似的机器学习平台,虽然没有开源,但都有比较详细的介绍文章可以学习参考。

这些框架基本可以看作各家公司做机器学习工程的最佳实践总结,总体架构基本都遵循“数据管理->模型训练->模型测试验证->部署上线”这样的流程。不过他们在具体设计实现上各有千秋,例如文中作者认为“线下线上训练数据分布偏差监控”是最为重要但却鲜有实现的一个测试点,在很多项目组都曾经因为缺少这个监控而出现过多次线上故障。因此TFX提供了Data Validation模块来专门解决这个问题。而像Uber的Michelangelo则侧重快速开发特征这个点引入了Feature Store模块。在实际应用中我们可以根据不同的业务特点灵活的选择工程方案来进行实现。

机器学习43条军规——谷歌

梗概介绍

本文把在产品中应用机器学习的过程从浅到深分成了三个大的阶段,又在这三个大的阶段中细分出了一些方面,以此对43条规则进行逻辑分类。简单来说,如果你是从头开始做机器学习系统,那么就可以在不同阶段参考这里面对应的条目,来保证自己走在正确的道路上。

ML工程最佳实践

To make great products:

  • do machine learning like the great engineer you are, not like the great machine learning expert you aren’t.
  • 这句话一定程度上是对整篇文章(叫手册可能更合适)的一个高度概括,ML在实际工作确实更多是工程问题,而不是算法问题。优先从工程效率中要效果,当把这部分榨干后,再考虑算法的升级。

Before Machine Learning

Rule #1: Don’t be afraid to launch a product without machine learning.

  • 规则1:不要害怕上线没有机器学习的产品。
  • 中心思想一句话概括:If you think that machine learning will give you a 100% boost, then a heuristic will get you 50% of the way there.

Rule #2: First, design and implement metrics.

  • 规则2:在动手之前先设计和实现评价指标。
  • 在构建具体的机器学习系统之前,首先在当前系统中记录尽量详细的历史信息,留好特征数据。这样不仅能够留好特征数据,还能够帮助我们随时了解系统的状态,以及做各种改动时系统的变化。

    Rule #3: Choose machine learning over a complex heuristic.

  • 规则3:不要使用过于复杂的规则系统,使用机器学习系统。
  • 简单来讲,复杂的规则系统难以维护,不可扩展,而我们很简单就可以转为ML系统,变得可维护可扩展。

ML Phase I: Your First Pipeline

  • 构建第一个ML系统时,一定要更多关注系统架构的建设。虽然机器学习的算法令人激动,但是基础架构不给力找不到问题时会令人抓狂。

Rule #4: Keep the first model simple and get the infrastructure right.

  • 规则4:第一个模型要简单,但是架构要正确。
  • 第一版模型的核心思想是抓住主要特征、与应用尽量贴合以及快速上线。

Rule #5: Test the infrastructure independently from the machine learning.

  • 规则5:独立于机器学习来测试架构流程。
  • 确保架构是可单独测试的,将系统的训练部分进行封装,以确保其他部分都是可测试的。特别来讲:
    • 测试数据是否正确进入训练算法。检查具体的特征值是否符合预期。
    • 测试实验环境给出的预测结果与线上预测结果是否一致。

Rule #6: Be careful about dropped data when copying pipelines.

  • 规则6:复制pipeline时要注意丢弃的数据。
  • 从一个场景复制数据到另一个场景时,要注意两边对数据的要求是否一致,是否有数据丢失的情况。

Rule #7: Turn heuristics into features, or handle them externally.

  • 规则7:将启发规则转化为特征,或者在外部处理它们。
  • 机器学习系统解决的问题通常都不是新问题,而是对已有问题的进一步优化。这意味着有很多已有的规则或者启发式规则可供使用。这部分信息应该被充分利用(例如基于规则的推荐排序时用到的排序规则)。下面是几种启发式规则可以被使用的方式:
    • 用启发规则进行预处理。如果启发式规则非常有用,可以这么用。例如在垃圾邮件识别中,如果有发件人已经被拉黑了,那么就不要再去学“拉黑”意味着什么,直接拉黑就好了。
    • 制造特征。可以考虑从启发式规则直接制造一个特征。例如,你使用启发式规则来计算query的相关性,那么就可以把这个相关性得分作为特征使用。后面也可以考虑将计算相关性得分的原始数据作为特征,以期获得更多的信息。
    • 挖掘启发式规则的原始输入。如果有一个app的规则启发式规则综合了下载数、标题文字长度等信息,可以考虑将这些原始信息单独作为特征使用。
    • 修改label。当你觉得启发式规则中包含了样本中没有包含的信息时可以这么用。例如,如果你想最大化下载数,同时还想要追求下载内容的质量。一种可行的方法是将label乘以app的平均star数。在电商领域,也常常用类似的方法,例如在点击率预估的项目中,可考虑对最终下单的商品或者高质量的商品对应的样本增加权重。
  • 已有的启发式规则可以帮助机器学习系统更平滑的过渡,但是也要考虑是否有同等效果更简单的实现方式。

Monitoring

  • 概括来讲,要保持好的监控习惯,例如使报警是可应对的,以及建设一个Dashboard页面。

Rule #8: Know the freshness requirements of your system.

  • 规则8:了解你系统对新鲜度的要求。
  • 如果模型延迟一天更新,你的系统会受到多大的效果影响?如果是一周的延迟呢?或者更久?这个信息可以让我们排布监控的优先级。如果模型一天不更新收入就会下降10%,那么可以考虑让一个工程师全天候监控它。了解系统对新鲜度的要求是决定具体监控方案的第一步。

Rule #9: Detect problems before exporting models.

  • 规则9:在模型上线之前检测问题。
  • 模型上线前一定要做完整性、正确性检查,例如AUC、Calibration、NE等指标的计算确认等。如果是模型上线前出了问题,可以邮件通知,如果是用户正在使用的模型出了问题,就需要电话通知了。

Rule #10: Watch for silent failures.

  • 规则10:关注静默失败。
  • 这是一个非常重要,而又经常容易被忽略的问题。所谓的静默失败指的是全部流程都正常完成,但是背后依赖数据出了问题,导致模型效果逐步下降的问题。这种问题在其他系统中并不常出现,但是在机器学习系统中出现几率会比较高。例如训练依赖的某张数据表很久没有更新了,或者表中的数据含义发生了变化等,再或者数据的覆盖度忽然变少,都会对效果产生很大的影响。解决方法是是对关键数据的统计信息进行监控,并且周期性对关键数据进行人工检查。

Rule #11: Give feature column owners and documentation.

  • 规则11:给特征组分配负责人,并记录文档。
  • 这里的feature column指的是一个特征组,例如用户可能属于的国家这组特征就是一个feature column。 如果系统庞大,数据繁多,那么知道每组数据由谁生成就变得非常重要。虽然数据都有简单描述,但是关于特征的具体计算逻辑,数据来源等都需要更详细的记录。

Your Fist Objective

  • objective是模型试图优化的值,而metric指的是任何用来衡量系统的值。

Rule #12: Don’t overthink which objective you choose to directly optimize.

  • 规则12:不要过于纠结该优化哪个目标。
  • 机器学习上线的初期,即使你只优化一个目标,很多指标一般都会一起上涨的。所以不用太纠结究竟该优化哪个。
  • 虽然大佬这么说,但是在我自己的实践经验中,只优化一个目标,系统的整体效果却未必会上涨。典型的如推荐系统的CTR模型,上线之后CTR确实会提升,但是对应的CVR很有可能会下降,这时还需要一个CVR模型,两个模型同时使用才能真正提升系统效果。究其原因,是因为每个目标只关注系统整个过程的一个子过程,贪心地去优化这个子过程,不一定能够得到全局的最优解,通常需要把主要的几个子过程都优化之后,才能取得整体效果的提升。

Rule #13: Choose a simple, observable and attributable metric for your first objective.

  • 规则13:为你的第一个objective选择一个简单可观测可归因的metric。
  • objective应该是简单可衡量的,并且是metric的有效代理。最适合被建模的是可直接观测并被归因的行为,例如:
    • 链接是否被点击?
    • 软件是否被下载?
    • 邮件是否被转发?
    • ……
  • 尽量不要在第一次就建模非直接效果的行为,例如:
    • 用户第二天是否会访问?
    • 用户在网站上停留了多久?
    • 日活用户有多少?
    • 非直接指标是很好的metric,可以用ABTest来进行观测,但不适合用作优化指标。此外,千万不要试图学习以下目标:
    • 用户对产品是否满意?
    • 用户对体验是否满意?
    • ……
  • 这些指标非常重要,但是非常难以学习。应该使用一些代理指标来学习,通过优化代理指标来优化这些非直接指标。为了公司的发展着想,最好有人工来连接机器学习的学习目标和产品业务。

Rule #14: Starting with an interpretable model makes debugging easier.

  • 规则14:使用可解释性强的模型可降低debug难度。
  • 优先选择预测结果有概率含义、预测过程可解释的模型,可以更容易的确认效果,debug问题。例如,如果使用LR做分类,那么预测过程不外乎一些相乘和相加,如果特征都做了离散化,就只有加法了,这样很容易debug一条样本的预测得分是如何被计算出来的。所以出了问题很容易debug。

Rule #15: Separate Spam Filtering and Quality Ranking in a Policy Layer.

  • 规则15:将垃圾过滤和质量排序的工作分离,放到策略层(policy layer)。
  • 排序系统工作的环境中数据分布是相对静态的,大家为了得到更好的排序,会遵守系统制定的规则。但是垃圾过滤更多是个对抗性质的工作,数据分布会经常变动。所以不应该让排序系统去处理垃圾信息的过滤,而是应该有单独的一层去处理垃圾信息。这也是一种可以推广的思想,那就是:排序层只做排序层的事情,职责尽量单一,其他工作让架构上更合适的模块去处理。此外,为了提升模型效果,应该把垃圾信息从训练数据中去除。

ML Phase II: Feature Engineering

  • 前面第一阶段的重点是把数据喂到学习系统中,有了基础的监控指标,有了基础的架构。等这一套系统建立起来后,第二阶段就开始了。 整体来讲,第二阶段的核心工作是将尽量多的有效特征加入到第一版的系统中,一般都可以取得提升。
  • 简单来说,就是要深刻认识到,系统优化永远没有终点,所以系统设计方面要对迭代非常友好。例如增加删除特征是否足够简单,正确性验证是否足够简单,模型迭代是否可以并行运行,等等。
  • 这虽然不是一条具体可行动的(actionable)规则,但是这种思想上的准备对整个系统的开发很有帮助。只有真正深刻意识到了系统持续迭代上线的本质,才会在设计在线和离线架构时为持续迭代最好相应的设计,并做好相应的工具,而不是做一锤子系统。

Rule #17: Start with directly observed and reported features as opposed to learned features.

  • 规则17:优先使用直接观测或收集到的特征,而不是学习出来的特征。
  • 所谓学习出来的特征,指的是用另外的算法学习出来的特征,而非可以直接观测或收集到的简单特征。学习出来的特征由于存在外部依赖,或者计算逻辑复杂,不一定适用于你当前的模型,所以稳定性和有效性会有风险。而直接可观测的特征由于是相对比较客观的,依赖较少的,所以比较稳定。

Rule #18: Explore with features of content that generalize across contexts.

  • 规则18:探索使用可以跨场景的内容特征。
  • 中心思想是在说,要多利用可以在多个场景下使用的特征,例如全局的点击率、浏览量这些特征,可以在多个场景下作为特征使用。这样可以在一些冷启动或者缺乏有效特征的场景下作为特征使用。

Rule #19: Use very specific features when you can.

  • 规则19:尽量使用非常具体的特征。
  • 如果数据量足够大,那么相比少数复杂特征,使用海量简单特征是更简单有效的选择。
  • 所谓非常具体,指的是覆盖样本量比较少的特征,例如文档的ID或者query的ID等。这样的特征虽然每个只覆盖很少一部分特征,但是只要这一组特征整体能够覆盖率比较高,例如90%,那就是OK的。而且还可以通过正则化来消除覆盖率过低或者相关性差的特征。这也是大家都偏爱大规模ID特征的一个原因,现在很多大厂的排序模型特征都大量使用了大规模ID特征。

Rule #20: Combine and modify existing features to create new features in human­-understandable ways.

  • 规则20:用人类可理解的方式对已有特征进行组合、修改来得到新特征。
  • 离散化和交叉是最常用的两种特征使用方式。其本质都是用特征工程的方式,在不改变使用模型本身的情况下增加模型的非线性。这两种方法本身没什么好说的,值得一致的是,在大规模ID类特征的交叉时,例如一段是query里的关键词,另一端是文档里的关键词,那就会产生很大量级的交叉特征,这时有两种处理方法:
    • 点积。其实计算query和文档共同包含的关键词数量。
    • 交集。每一维特征的含义是某个词同时出现在了query和文档中,同时出现则该维特征为1,否则为0。
  • 所谓“人类可理解的方式”,我的理解就是离散化和交叉要基于对业务逻辑的理解,不能乱交叉。

Rule #21: The number of feature weights you can learn in a linear model is roughly proportional to the amount of data you have.

  • 规则21:线性模型中可学到的特征权重数量,与训练数据的数量大体成正比。
  • 这背后有复杂的统计原理做支撑,但你只需要知道结论就可以了。这个原则给我们的启示,是要根据数据量来选择特征的生成方式,例如:
  • 如果你的系统是一个搜索系统,query和文档中有百万级的词,但是你只有千级别的标注样本。那你就别用ID级关键词特征了,而是要考虑点积类特征,把特征数量控制在几十个这个级别。
  • 如果你拥有百万级样本,那么可以将文档和query的关键词进行交叉特征,然后用正则化进行特征选择。这样你会得到百万级特征,但是正则化之后会更少。所以说,千万级样本,十万级特征。
  • 如果你有十亿级或者更高级别的样本,那么你可以使用query和文档的ID级特征,然后加上特征选择和正则化。十亿级样本,千万级特征。
  • 总结起来就是,根据样本决定特征使用方式,样本不够就对特征进行高层次抽象处理,指导和样本量级相匹配。

Rule #22: Clean up features you are no longer using.

  • 规则22:清理不再使用的特征。
  • 如果某个特征已经没有用,并且它与其他特征的交叉也已经没有用,就应该将其清理掉,保持架构的整洁性。
  • 在考虑添加或保留哪些特征时,需要统计一下特征的样本覆盖率,例如一些整体覆盖率很低的个性化feature column,只有很少用户能覆盖到,那么大概率这组特征作用不大。但另一方面,如果某个特征覆盖率很低,例如只有1%,但是其区分度非常大,例如90%取值为1的样本都是正样本,那么 这个特征就值得加入或保留。

Human Analysis of the System

  • 在更进一步之前,我们需要了解一些机器学习课程上不会教你的内容:如何观察分析模型,并改进它。用作者的话说,这更像是一门艺术 ,但仍然有一些规律可循。

Rule #23: You are not a typical end user.

  • 规则23:你不是一个典型的终端用户。
  • 这条规则的中心思想是说,虽然吃自己的狗食是必要的,但也不要总是从工程师的角度来衡量模型的好坏。这不仅可能不值当,而且可能看不出问题。所谓不值当,是因为工程师的时间太贵了,这个大家都懂;而所谓看不出问题,是因为工程师自己看自己开发的模型,容易看不出问题,所谓“不识庐山真面目”。
  • 所以作者认为合理的方法是让真正的终端用户来衡量模型或产品的好坏。要么通过线上ABTest,要么通过众包的方式来做。

Rule #24: Measure the delta between models.

  • 规则24:离线衡量模型之间的差异。
  • 原文没有说是离线,但我通过上下文理解他说的应该是离线。这一条规则说的是新模型在上线之前,需要先和老模型做差异对比。所谓差异对比,指的是对于同样的输入,新旧两个模型给出的结果是否差异足够大。例如对于同一个query,两个排序模型给出的差异是否足够大。如果离线计算发现差异很小,那也没必要上线测试了,因为上线后差异肯定也大不了。如果差异比较大,那么还需要看差异是不是好的差异。通过观察差异可以得知新模型究竟对数据产生了什么影响,这种影响是好是坏。
  • 当然,这一切的前提是你需要有一个稳定的对比系统,起码一个模型和他自己对比的话差异应该非常小,最好是零差异。

Rule #25: When choosing models, utilitarian performance trumps predictive power.

  • 规则25:当选择模型时,实用性指标比预测能力更重要。
  • 这是一条很有用的经验。虽然我们训练模型时objective一般都是logloss,也就是说实在追求模型的预测能力。但是我们在上层应用中却可能有多种用途,例如可能会用来排序,那么这时具体的预测能力就不如排序能力重要;如果用来划定阈值然后跟根据阈值判断垃圾邮件,那么准确率就更重要。当然大多数情况下这几个指标是一致的。
  • 除了作者说的这一点,还有一种情况是需要特别注意的,那就是我们在训练时可能会对样本做采样,导致得到的预测值整体偏高或偏低。如果这个预测值是用来直接排序的,那么这个变化关系不大,但如果有其他用处,例如和另外的值相乘,典型的如广告场景下的CTRbid,或者电商推荐排序下的CTRCVR,在这类场景下,预测值本身的准确性也很重要,就需要对其进行校准(calibrate),使其与采样前的样本点击率对齐。

Rule #26: Look for patterns in the measured errors, and create new features.

  • 规则26:在错误中发现模式,并创建新特征。
  • 这算是一种用来提升模型效果的通用思路。具体来说,指的是观察训练数据中模型预测错误的样本,看看是否能够通过添加额外特征来使得这条样本被模型预测正确。之所以使用训练集中的数据,是因为这部分数据是模型已经试图优化过的,这里面的错误,是模型知道自己搞错了,目前学不出来的,所以如果你给它足够好的其他特征,它或许就能把这条样本学对了。
  • 一旦发现错误的模式,就可以在当前系统之外寻找新的特征。例如,如果你发现当前系统倾向于错误地把长文章排到后面,那么就可以加入文章长度这一特征,让系统去学习文章长度的相关性和重要性。

Rule #27: Try to quantify observed undesirable behavior.

  • 规则27:尽量将观测到的负面行为量化。
  • 如果在系统中观察到了模型没有优化到的问题,典型的例如推荐系统逼格不够这种问题,这时应该努力将这种不满意转化为具体的数字,具体来讲可以通过人工标注等方法标注出不满意的物品,然后进行统计。如果问题可以被量化,后面就可以将其用作特征、objective或者metric。整体原则就是“先量化,再优化”。
  • 多说一句,这里的优化,不一定是用模型来优化,而是指的整体优化。比如推荐系统逼格这种问题,模型可能很难优化,但是只要能量化出来,就可以通过其他方法来尽量减少,例如单独去学习有逼格物品的特征,或者在召回阶段进行一定倾斜。

Rule #28: Be aware that identical short-term behavior does not imply identical long­-term behavior.

  • 规则28:要注意短期内观察到的类似行为不一定会长期存在。
  • 假设你搞了个系统,通过学习每个具体的文档ID和query ID,计算出了每个query下每个文档的点击率。通过离线对比和ABTest,你发现这个系统的行为和当前系统的行为一毛一样,而这个系统又更简单,所以你就把这个系统上线了。后面你会发现这个系统在任何query下都不会给出任何新的文档。奇怪吗?一点都不奇怪,因为你只让它记住了之前的历史数据,它对新数据没有任何信息。
  • 所以唯一能够衡量一个系统是否长期有效的方法,就是让它使用该模型在线上时收集到的真实数据上进行训练。当然这有难度。 往大了说,作者这条规则其实说的是个系统或者模型的泛化能力,如果一个系统或者模型不能对新数据做出很好的预测,那么无论他的离线表现如何,都不能代表它的真正能力。再换个角度来看,一个系统必须具有持续学习适应新数据的能力,才能是一个合格的机器学习系统,否则就只是个学舌的鹦鹉。

Training-Serving Skew

  • 训练和服务之间的差异问题时一个大话题,主要原因包括训练时与服务时数据获取方式不同、训练时与服务时数据分布不同以及模型和算法之间的反馈循环等。作者说这种差异在G家的多条产品线上出现过,都产生了负面影响。但要我说这绝对不仅仅是谷歌的问题,谷歌绝对是做的比较好的了,各种中小厂里面这种问题只多不少,所以这部分的经验是非常宝贵的。解决这类问题的核心是对系统和数据的变化进行监控,确保一切差异都在监控之内,不会悄悄进入系统。

Rule #29: The best way to make sure that you train like you serve is to save the set of features used at serving time, and then pipe those features to a log to use them at training time.

  • 规则29:保证服务与训练一致性的最好方法是将服务时的特征保存下来,然后通过日志将特征喂到训练过程中去。
  • 这句话基本道出了保证差异最小化的核心套路。这种基于特征日志的方法可以极大提升效果,同时能够减少代码复杂度。谷歌的很多团队也正在往这种做法上迁移。

Rule #30: Importance weight sampled data, don’t arbitrarily drop it!

  • 规则30:对采样样本做重要性赋权,不要随意丢弃!
  • 这是作者唯一用了感叹号的一条,可想而知背后的辛酸。当我们有太多训练数据时,我们会只取其中的一部分。但这是错误的。正确的做法是,如果你给某条样本30%的采样权重,那么在训练时就给它10/3的训练权重。通过这样的重要性赋权(importance weight),整个训练结果的校准性(calibration)就还能够保证。
  • 多说一句,这个校准性非常的重要,尤其对于广告系统,或者多个预测值相加或相乘来得到最终结果的系统。如果单个值没有校准,偏低或偏高,那么在相乘或相加之后其含义就会不正确。如果直接使用模型预测值进行排序,校准性就没那么重要,因为校准性不会影响排序,只会影响具体的值。

Rule #31: Beware that if you join data from a table at training and serving time, the data in the table may change.

  • 规则31:如果你在训练时和服务时都在join一张表,那么要注意这张表的数据可能会发生变化。
  • 比如说某张表里存着一些文档的特征,你在离线训练之前要去这个表里取这些特征用来训练,但这里就有个风险,那就是这个表里的数据在你离线取的时候和在线服务的时候数据不一样,发生了变化。最好的解决方式就是在服务端将特征记录在日志中,这样能保证数据的一致性。或者如果这张表的变化频率比较低,也可以考虑对其做小时级或天级备份,以此来减少这种差异。但要记住这种方法并不能彻底解决这个问题。

Rule #32: Re­use code between your training pipeline and your serving pipeline whenever possible.

  • 规则32:尽量在训练pipeline和服务pipeline之间复用代码。
  • 训练一般是离线批量进行的,而服务则是在线流式进行的,这两者之间虽然在处理数据的方式上存在着较大差异,但仍然有很多代码可以共享。这些代码的共享可以从代码层面介绍训练和服务之间的差异。换句话说,日志记录特征是从数据角度消除差异,那么代码复用就是从代码角度消除差异,双管齐下,效果更好。

Rule #33: If you produce a model based on the data until January 5th, test the model on the data from January 6th and after.

  • 规则33:如果训练数据是1月5日之前的,那么测试数据要从1月6日开始。(注:按时间区分训练集和测试集)
  • 这条规则的主要目的是让测试结果与线上结果更加接近,因为我们在使用模型时就是在用服务当天之前的数据训练,然后来预测当天的数据。这样得到的测试结果虽然可能会偏低,但却更加真实。

Rule #34: In binary classification for filtering (such as spam detection or determining interesting e­mails), make small short-­term sacrifices in performance for very clean data.

  • 规则34:在为过滤服务的二分类问题中(例如垃圾邮件过滤),可以为了干净的数据牺牲一些短期效果。
  • 在过滤类的任务中,被标记为负的样本是不会展示给用户的,例如可能会把75%标记为负的样本阻拦住不展现给用户。但如果你只从展示给用户的结果中获取下次训练的样本,显然你的训练样本是有偏的。
  • 更好的做法是使用一定比例的流量(例如1%)专门收集训练数据,在这部分流量中的用户会看到所有的样本。这样显然会影响线上的真实过滤效果,但是会收集到更好的数据,更有利于系统的长远发展。否则系统会越训练越偏,慢慢就不可用了。同时还能保证至少过滤掉74%的负样本,对系统的影响也不是很大。
  • 但是如果你的系统会过滤掉95%或者更多的负样本,这种做法就不那么可行了。即使如此,为了准确衡量模型的效果,你仍然可以通过构造一个更小的数据集(0.1%或者更小)来测试。十万级别的样本足够给出准确的评价指标了。

Rule #35: Beware of the inherent skew in ranking problems.

  • 规则35:注意排序问题中固有的数据偏置。
  • 当新的排序算法对线上排序结果产生了重大改变时,你其实是改变了算法将来会看到的数据。这时这种偏置就会出现。这种问题有以下几种方法来解决,核心思想都是更偏重模型已经看到过的数据。
  • 对覆盖更多query(���类似角色,根据业务不同)的特征给予更强的正则化。这样模型会更偏重只覆盖一部分样本的特征,而不是泛化性特征。这样会阻止爆品出现在不相关query的结果中。
  • 只允许特征取正的权重值。这样任何好特征都会比“未知”特征要好。
  • 不要使用只和文档相关的特征。这是第一条的极端情况,否则会导致类似哈利波特效应的情况出现,也就是一条在任何query下都受欢迎的文档不会到处都出现。去除掉只和文档相关的特征会阻止这种情况发生。

Rule #36: Avoid feedback loops with positional features.

  • 规则36:使用位置特征来避免反馈回路。
  • 大家都知道排序位置本身就会影响用户是否会对物品产生互动,例如点击。所以如果模型中没有位置特征,本来由于位置导致的影响会被算到其他特征头上去,导致模型不够准。可以用加入位置特征的方法来避免这种问题,具体来讲,在训练时加入位置特征,预测时去掉位置特征,或者给所有样本一样的位置特征。这样会让模型更正确地分配特征的权重。
  • 需要注意的是,位置特征要保持相对独立,不要与其他特征发生关联。可以将位置相关的特征用一个函数表达,然后将其他特征用另外的函数表达,然后组合起来。具体应用中,可以通过位置特征不与任何其他特征交叉来实现这个目的。

Rule #37: Measure Training/Serving Skew.

  • 规则37:衡量训练和服务之间的差异。
  • 整体来讲有多种原因会导致这种差异,我们可以将其进行细分为以下几部分:
  • 训练集和测试集之间的差异。这种差异会经常存在,而且不一定是坏事。
  • 测试集和“第二天”数据间的差异。这种差异也会一直存在,而这个“第二天”数据上的表现是我们应该努力优化的,例如通过正则化。这两者之间差异如果过大,可能是因为用到了一些时间敏感的特征,导致模型效果变化明显。
  • “第二天”数据和线上数据间的差异。如果同样一条样本,在训练时给出的结果和线上服务时给出的结果不一致,那么这意味着工程实现中出现了bug。

ML Phase III: Slowed Growth, Optimization Refinement, and Complex Models

  • 一般会有一些明确的信号来标识第二阶段的尾声。首先,每月的提升会逐步降低。你开始在不同指标之间做权衡,有的上升有的下降。嗯,游戏变得有趣了。既然收益不容易获得了,机器学习就得变得更复杂了。
  • 在前两个阶段,大部分团队都可以过得很开心,但到了这个阶段,每个团队都需要找到适合自己的路。

Rule #38: Don’t waste time on new features if unaligned objectives have become the issue.

  • 规则38:如果objective没有达成一致,不要在新特征上浪费时间。
  • 当系统整体达到一个稳定期,大家会开始关注机器学习系统优化目标以外的一些问题。这个时候,目标就不如之前那么清晰,那么如果目标没有确定下来的话,先不要在特征上浪费时间。

Rule #39: Launch decisions are a proxy for long­term product goals.

  • 规则39:上线决策是长期产品目标的代理。
  • 这句话读起来有点别扭,作者举了几个例子来说明,我觉得核心就是在讲一件事情:系统、产品甚至公司的长远发展需要通过多个指标来综合衡量,而新模型是否上线要综合考虑这些指标。所谓代理,指的就是优化这些综合指标就是在优化产品、公司的长远目标。
  • 决策只有在所有指标都在变好的情况下才会变得简单。但常常事情没那么简单,尤其是当不同指标之间无法换算的时候,例如A系统有一百万日活和四百万日收入,B系统有两百万日活和两百万日收入,你会从A切换到B吗?或者反过来?答案是或许都不会,因为你不知道某个指标的提升是否会cover另外一个指标的下降。
  • 关键是,没有任何一个指标能回答:“五年后我的产品在哪里”?
  • 而每个个体,尤其是工程师们,显然更喜欢能够直接优化的目标,而这也是机器学习系统常见的场景 。现在也有一些多目标学习系统在试图解决这种问题。但仍然有很多目标无法建模为机器学习问题,比如用户为什么会来访问你的网站等等。作者说这是个AI-complete问题,也常被称为强AI问题,简单来说就是不能用某个单一算法解决的问题。

Rule #40: Keep ensembles simple.

  • 规则40:ensemble策略保持简单。

  • 什么叫简单的ensemble?作者认为,只接受其他模型的输出作为输入,不附带其他特征的ensemble,叫做简单的ensemble。换句话说,你的模型要么是单纯的ensemble模型,要么是普通的接收大量特征的基模型。 除了保持简单,ensemble模型最好还能具有一些良好的性质。例如,某个基模型的性能提升不能降低组合模型的性能。以及,基模型最好都是可解释的(例如是校准的),这样基模型的变化对上层的组合模型来说也是可解释的。同时,一个基模型预测概率值的提升不会降低组合模型的预测概率值。

Rule #41: When performance plateaus, look for qualitatively new sources of information to add rather than refining existing signals.

  • 规则41:当效果进入稳定期,寻找本质上新的信息源,而不是优化已有的信号。
  • 你加了一些用户的人口统计学特征,你加了一些文档的文字特征,等等,但是关键指标上的提升还不到1%。现在咋整?
  • 这时就应该考虑加一些根本上不同的特征,例如用户再过去一天、一周看过的文档历史,或者另外一个数据源的数据。总之,要加入完全不同的维度的特征。此外也可以尝试使用深度学习,但同时也要调整你对ROI的预期,并且要评估增加的复杂度换来的收益是否值得。

Rule #42: Don’t expect diversity, personalization, or relevance to be as correlated with popularity as you think they are.

  • 规则42:多样性,个性化或者相关性与流行度的相关性关系可能要比你想的弱很多。
  • 多样性意味着内容或者来源的多样性;个性化意味着每个用户得到不一样的东西;相关性意味着一个query的返回结果相比其他query与这个query更相关。所以这三个指标的含义都是与普通不一样。
  • 但问题在于普通的东西很难被打败。
  • 如果你的衡量指标是点击、停留时长、观看数、分享数等等,你本质上是在衡量东西的流行度。有的团队有时会希望学到一个多样化的个性化模型。为此,会加入个性化特征和多样化特征,但是最后会发现这些特征并没有得到预期的权重。
  • 这并不能说明多样性、个性化和相关性不重要。像前文指出,可以通过后续的处理来增加多样性或相关性。如果这时看到长期目标提升了,你就可以确定多样性/相关性是有用的。这时你就可以选择继续使用后续处理的方式,或者根据多样性和相关性直接修改要优化的objective。

Rule #43: Your friends tend to be the same across different products. Your interests tend not to be.

  • 规则43:你在不同产品上的好友一般是一样的,但你的兴趣通常会不一样。
  • 谷歌经常在不同产品上使用同样的好友关系预测模型,并且取得了很好的效果,这证明不同的产品上好友关系是可以迁移的,毕竟他们是固定的同一批人。但他们尝试将一个产品上的个性化特征使用到另外一个产品上时却常常得不到好结果。可行的做法是使用一个数据源上的原始数据来预测另外数据源上的行为,而不是使用加工后的特征。此外,用户在另一个数据源上的行为历史也会有用。

总结

  • 从上面洋洋洒洒43条经验之谈中不难看出,大神作者认为,对于大多数机器学习应用场景来说,我们需要解决的问题大多数都是工程问题,解决这些工程问题需要的并不是复杂的理论,更多是对细节、架构、过程的仔细推敲和精致追求。而这些是我们非大神的普通人可以做到的,如果说大神做的是95分以上的系统,那么我们只要对工程架构、过程和细节做好足够的优化,我们也可以做出至少80分的系统。

结束


支付宝打赏 微信打赏

~ 海内存知已,天涯若比邻 ~

Share

Related Posts

标题:通用人工智能思考 -- Think about AGI

摘要:AGI发展历史, 背后的困境, 及专家(朱松纯/唐杰等) 看法

Comments

--disqus--

    Content
    My Moment ( 微信公众号 )
    欢迎关注鹤啸九天