0%

在数据平台中实现机器学习工程化

随着AI技术的使用日益广泛,在数据平台中进行机器学习建模分析成为了越来越常见的场景。

提到AI技术,不少人会直接联系到近几年特别火的基于人工神经网络的深度学习技术。其实,在企业业务中使用最广泛的还并不是深度学习,这是因为深度学习模型的应用领域常常是图像、音视频、自然语言处理等,而企业期望的应用领域多是销售、营销、客户关系管理等。另一方面,深度学习模型的可解释性比较差,难以从业务角度分析其合理性,这也限制了深度学习的应用。

一些常见的企业AI技术的应用场景示例如下:

  • 给从线上渠道过来的大量销售线索分级,以便销售人员可以针对性的进行营销
  • 预测客户的生命周期价值,识别潜在中等价值客户,期望用营销手段将其转化为高价值客户
  • 预测客户的流失,找出将要流失的客户,期望用活动留住客户
  • 识别“羊毛党”顾客,在做活动时,将这些客户排除在外

上述模型大都可以使用简单数据统计结合使用一些传统的机器学习算法(如线性回归、决策树、SVM等)来实现。

一个例子

举个例子,假设有一个在线超市,希望预测其顾客的生命周期价值,以便可以针对性的进行营销。如何用机器学习的方法来解决这个问题呢?

首先,应该可以明确的是,我们可以用回归方法来预测顾客的生命周期价值。有了这个预测值,就可以根据预测值的分布情况将客户分层,从而针对每一层的客户制定营销策略。

典型的线性回归模型是基于一组有效的特征进行预测的有监督模型。其基本思想是期望找到一组权重值,这些权重值与特征值相乘然后加和得到预测值。

对应到业务上理解,可以认为:

生命周期价值 = 权重1 x 日均消费 + 权重2 x 月均消费 + 权重3 x 消费间隔 + ...

对于特征值,一般会使用数学手段进行一些预处理,比如归一化。权重值的计算也会涉及一些数据方法,比如使用最小二乘法、梯度下降法等。然而这些看上去比较复杂的数学方法却不是影响模型效果的核心。

真正决定模型效果的是特征的选择!这也是整个模型不确定性最大,探索性最强的地方。数据分析师或数据建模人员常常在这里做大量的数据调研与分析。

机器学习模型应用过程

从上面的示例中,我们可以大致了解到机器学习模型的应用过程。实际工作中,有没有什么成熟的流程可以参考呢?

早在1999年,欧盟相关机构就起草了一个关于机器学习模型应用的标准流程,即CRISP-DM(cross-industry standard process for data mining)模型,中文翻译为“跨行业数据挖掘标准流程”模型。这个模型将整个过程分成六个阶段,如下图所示:

CRISP DM, 图片来自https://www.ibm.com/docs/zh/spss-modeler/saas?topic=dm-crisp-help-overview

从图中可以看出,机器学习模型应用将依次经历商业理解、数据理解、数据准备、建模、模型评估、产品化部署这六个阶段。这六个阶段的详细定义可以参考这里

CRISP DM模型中,前三个步骤均属于特征探索的步骤,而且存在一个循环。从中可以看出特征探索过程通常是复杂的,而且要经常回到起点重新开始。

经过多年的实践,CRISP DM模型现在已被广泛用于在企业中开发机器学习模型。

CRISP DM模型类似的还有KDD模型(定义了数据筛选、数据预处理、数据转换、数据挖掘、解释评估几个步骤)、SEMMA模型(定义了抽样、探索、修改、建模、评估几个步骤)、DMAIC方法(来自于六西格玛管理,包括定义、测量、分析、改进、控制几个步骤)等。这些模型具备一定的相似性,其中CRISP DM模型是应用最广泛的。

工程化考虑

机器学习模型的探索及构建需要兼备较强的业务经验和统计学知识,通常由数据分析师或者数据科学家完成(下文统称数据分析师)。(可参考文章《那些数据工作中的角色》。)

作为数据工程师,则需要考虑如何进行机器学习模型的工程化应用。

在数据平台中进行机器学习模型的工程化应用一般需要考虑这样一些问题:

  • 如何应对大数据量?
  • 如何支持特征探索?
  • 如何训练模型?
  • 如何部署模型?
  • 如何执行预测?
  • 如何管理模型版本?
  • 如何更新模型?

数据平台中的数据量通常很大,这是在做技术选择时主要需要考虑的问题。这会带来很多限制,比如数据分析师可能更喜欢用pandas进行数据分析,但是pandas处理的是内存中的数据,无法应对大量数据的场景。

此时通常有几种选择:

  • 在训练模型时,如果只需要在小规模的抽样数据集上训练,则可以提供一种方式让数据分析师导出数据用于训练,然后,在模型预测时分批进行数据预测。
  • 如果需要基于大规模数据进行模型训练,则需要基于某种分布式计算引擎进行支持。比如,可以选择Spark,让数据分析师编写Spark代码实现模型。
  • 从统一开发语言的角度考虑,可以让数据分析师编写SQL实现特征处理,这样就可以和指标开发统一起来。算法模型部分则用Spark等分布式计算引擎实现,不做任何特征处理。

数据分析师在进行特征探索时,常常会试验多组特征,从中找到比较有效的特征。探索的过程一般需要进行一些记录,以便了解尝试过哪些特征,哪些被丢弃了,哪些表现好可以保留下来。

数据分析师在训练模型之后通常可以确定模型的一个版本用于部署,但是由于数据经常更新,这个模型常常需要重新训练。所以,需要提供一种方式进行模型重新训练集版本管理。

根据不同的数据消费需求,模型预测可以通过运行批处理任务实现(无实时访问数据的需求,比如根据客户生命周期价值分层进行营销的场景),也可以部署为一个在线的API进行实时预测(有实时访问最新数据的需求,比如产品推荐场景)。

在有的企业中,数据分析师的职责只限于模型开发,开发完模型后,他们就将模型交给数据工程师进行工程化实现。事实上,如果模型输出的数据量小且仅需要运行一次,则可能无需工程化,数据分析师可以用自己熟悉的技术实现,只要能把最后的计算结果导出就行。反之,则需要进行工程化实现。不过,此时并不太建议将模型转交给另一位数据工程师进行实现。因为,重写代码极容易引入一些细微的BUG。比较好的方式是提供一个易用的数据工具,让数据分析师可以自助完成模型工程化。

在数据平台中进行实现

关于如何在数据平台中实现一个机器学习模型开发工具,下面分享一个案例。

语言选择

前面的文章《数据应用开发语言和环境》中讨论了数据开发语言选择问题,提到了自定义的以SQL为基础的数据开发语言。这里我们可以沿用这样的数据开发语言实现机器学习模型的特征处理。

对于不熟悉SQL语言的分析函数的数据分析师可能会抵触用SQL来进行特征处理,他们会认为很多功能无法实现。事实上,这里主要的限制来自于数据量限制,不使用 SQL将难以利用分布式计算引擎的快速计算的优势。当然,用通用编程语言编写代码,也更容易导致代码不易理解。

现在有大量常用的SQL分析函数支持,比如Spark SQL有大量的聚合函数窗口函数实现,可以实现绝大部分pandas库提供的数据转换功能。如果还不满足需求,则可以考虑自定义实现UDFUDAF来扩展功能。

至于模型构建及训练的代码,这里选择采用Spark框架进行实现,使用PySpark库提供的对于数据分析师友好的Python语言编写。编写一段Python代码构建并训练模型是比较简单的,一个示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
def train_model(spark: SparkSession)
data = spark.sql('select * from feature_table')
# Configure an ML pipeline, which consists of three stages: tokenizer, hashingTF, and lr.
tokenizer = Tokenizer(inputCol="text_feature", outputCol="words")
hashingTF = HashingTF(inputCol=tokenizer.getOutputCol(), outputCol="features")
lr = LogisticRegression(maxIter=10, regParam=0.001)
pipeline = Pipeline(stages=[tokenizer, hashingTF, lr])

# Fit the pipeline to training documents.
model = pipeline.fit(data)

model.write().overwrite().save('path/to/save')

特征探索

特征探索是数据分析师工作的重点,如果可以提供一套好用的工具,将能有效提高效率。

从便于工程化的角度考虑,这个工具应当可以帮助数据分析师编写工程化的代码,同时它应该具备足够的灵活性,以便可以记录数据分析和探索的过程。

从工具开发的角度,我们可以设计一套这样的模型来支持特征探索:

  • 通过一段SQL代码来读入数据,它可以将所有需要关联的数据连接起来形成一个宽表
  • 将特征构建过程拆分为多个步骤
    • 下级步骤可以是一个普通的数据转换步骤,此时,可以继承上级步骤中产生的所有变量
    • 下级步骤可以是一个分组步骤,此时,可以通过聚合函数来聚合生成新的变量
  • 每一个步骤可以拆分为多个过程
    • 下级过程可以使用上级过程产生的变量来计算新的变量

采用最简单的实现方式,可以用电子表格作为工具的应用接口。以上模型可以用电子表格模板表示如下:

Feature Development

该电子表格模板解释如下:

  • 标记1中,添加一个名为source的表格记录数据读取的SQL。
  • 标记2中,添加一个名为features的表格记录第一次特征开发步骤。
    • 标记2.1中,开发了三个字段作为此特征开发步骤中开发出的特征。
    • 标记2.2中,为这三个字段设置过程ID。
    • 标记2.3中,编写SQL表达式,为这三个字段设置转换逻辑。
  • 标记3中,添加一个名为features1的表格记录第二次特征开发步骤。
    • 标记3.1中,开发三个字段作为此步骤中开发出的特征,并设置好过程ID及转换表达式。
    • 标记3.2中,为开发出的三个特征添加属性,标记是否需要作为输出特征及原因。
    • 标记3.3中,指定输出数据表。

此工具具备这样一些灵活性:

  • 数据分析师可以灵活的记录特征探索过程中的一些分析结果。
  • 数据分析师可以在输出表中忽略某些无效特征。
  • 数据分析师可以用步骤及过程来组合整个特征转换的逻辑。
  • 数据分析师可以充分利用电子表格的过滤功能,快速找到想要关注的特征。
  • 特征表格中的第二列t_col_attr被设计为扩展属性,可以根据情况进行扩展,比如可以定义v_if_train: a > 1表示训练阶段要过滤的数据

ETL任务设计

特征探索和开发只是整个机器学习模型工程化的一部分。有了特征,如何工程化的组织模型训练、模型预测呢?

模型训练时,为了构建训练数据,通常有特征还不够,还需要目标变量。目标变量一般用y表示(特征一般用x表示)。

在模型预测之前,需要将模型训练得到的模型保存到模型库。

同时,模型训练一般是不常做的,因此可以采用手动运行ETL的方式执行。而模型预测则常常需要周期性的执行,因为特征常常会随着时间改变。

总结起来,可以按照下图来设计机器学习模型对应的ETL任务:

ETL Task Design

上述训练数据构建及预测数据构建过程中的特征计算代码几乎相同,可以通过读取电子表格中的信息来自动生成这些代码。这不仅可以避免代码重复,还可以保证训练过程和预测过程使用一致的方式构造特征。

模型训练与预测

前面提到模型训练需要目标变量y,那么,如何提取y的值呢?对于上面例子中的生命周期价值,其值常常是已有的流失客户的消费总额,通过执行一个SQL查询即可取出这里的y值。

有了y值,还需要想办法和前面构造的特征进行关联,这通常可以通过相同的ID值实现。比如生命周期价值模型,特征值和y值都是基于客户来计算的,客户ID就可以作为关联特征值和y值的数据列。

为了支持y值的获取,在设计数据工具时,我们需要:

  • 设计一个地方记录y的提取过程。
  • 设计一个可配置的ID字段用于将特征数据和y变量提取出的数据进行关联。
  • 确保特征数据和y值数据均包含配置的ID字段。

对于模型预测过程,y值是不需要的,但是,一般可定义一些额外的可配置项,如:模型名、模型版本、特征列、ID列、其他参考列。

有了这些配置,模型预测的ETL代码就可以根据模板自动生成。一个自动生成的代码示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
-- target=variables
select
, 'file:///some/path/model/ltv/v1' as model_save_path
, 'user_id,r,f,m' as feature_cols
, 'user_id' as id_col
, '' as output_ref_cols

-- target=temp.result
select * from dwb_sales.ltv_model_feature

-- target=func.model_predict(${model_save_path}, result, ${feature_cols}, ${id_col}, ${output_ref_cols})

-- target=output.dwb_sales.ltv_model_feature_predict
select * from result

可以用电子表格模板表示模型训练和预测如下:

Model Train & Predict

上述两个表格分别用于定义训练和预测过程中可用的配置。其中:

  • 模型训练表格的配置: y_sql表示提取y值的SQL代码,vars_sqly_sql提供变量支持,x_id_coly_id_col表示特征数据和y值数据中的ID列的列名,y_target_col表示y值数据中的y值列名。
  • 模型预测表格的配置:model_name表示模型名,model_version表示模型版本,feature_cols表示需要进入模型的特征列,id_col表示预测结果中的ID列,output_ref_cols表示预测结果中的其他参考列。

模型发布及版本管理

机器学习模型工程化还有一个重要环节,那就是模型发布及版本管理。

当数据分析师训练好一个新的模型之后,如何将此模型部署到生产环境呢?模型发布及版本管理环节主要回答这个问题。

如何实现模型发布?这个还需要基于数据分析师的使用场景来看。

大多数数据分析师喜爱用Jupyter Notebook这类工具进行特征探索与模型开发。一个简单的想法是,是不是可以直接让他们在不离开工作环境就可以进行模型发布?

为了实现这个功能,一个可行的办法是,提供一个Python程序库,公布出来一些API用于发布模型。

在设计API时需要支持版本机制。版本是很有用的功能。当模型需要更新时,通常需要生成一个新的版本,这样就可以轻松的实现回滚;当我们想比对多个模型的效果时,可以每个版本都运行一次,然后监控不同版本模型的真实效果。

应用TDD的思想,站在数据分析师使用这个库的角度,可以这样设计这个程序库的API:

1
2
3
4
5
6
7
8
9
10
11
...
mm = ModelManager()
ver = mm.deploy_model('some_model', '/path/to/saved/model/')
print('deployed model version:', ver)

models = mm.list_models()
print(models)

versions = mm.list_model_versions('some_model')
print(versions)
...

部署模型之后,将会得到一个版本号,将此版本号填入电子表格中,然后重新生成预测代码并部署即可完成整个模型的线上更新了。

总结

本文首先讨论了机器学习模型的一般实现过程。
结合此过程,进一步分析了机器学习模型工程化的一些考虑。
最后,以数据平台下的机器学习模型开发为背景,结合一个实例,分析并设计了一个基于电子表格的机器学习模型工程化工具。此工具可以作为在机器学习工程化起步阶段的一个基本的轻量级工具使用,可帮助数据分析师实现自助式的机器学习模型开发及部署。

机器学习工程化是大规模机器学习应用的前提,随着机器学习应用越来越广泛,机器学习工程化显得越来越重要。本文中介绍的内容可作为一个不错起点,可以扩展的内容还非常多,希望与大家一起探索。

欢迎关注我的其它发布渠道