0x00 摘要
Alink 是阿里巴巴基于实时计算引擎 Flink 研发的新一代机器学习算法平台,是业界首个同时支持批式算法、流式算法的机器学习平台。本文是漫谈系列的第二篇,将从源码入手,带领大家具体剖析Alink设计思想和架构为何。
因为Alink的公开资料太少,所以均为自行揣测,肯定会有疏漏错误,希望大家指出,我会随时更新。
0x01 Alink设计原则
前文中 [Alink漫谈(一) : 从KMeans算法实现看Alink设计思想] 我们推测总结出Alink部分设计原则
-
算法的归算法,Flink的归Flink,尽量屏蔽AI算法和Flink之间的联系。
-
采用最简单,最常见的开发语言和思维方式。
-
尽量借鉴市面上通用的机器学习设计思路和开发模式,让开发者无缝切换。
-
构建一套战术打法(middleware或adapter),即屏蔽了Flink,又可以利用好Flink,还能让用户快速开发算法。
下面我们就针对这些设计原则,从上至下看看Alink如何设计自己这套战术打法。
为了能让大家更好理解,先整理一个概要图。因为Alink系统主要可以分成三个层面(顶层流水线, 中间层算法组件, 底层迭代计算框架),再加上一个Flink runtime,所以下图就是分别从这四个层面出发来看程序执行流程。
如何看待 pipeline.fit(data).transform(data).print();
// 从顶层流水线角度看
训练流水线 +-----> [VectorAssembler(Transformer)] -----> [KMeans(Estimator)]
| // KMeans.fit之后,会生成一个KMeansModel用来转换
|
转换流水线 +-----> [VectorAssembler(Transformer)] -----> [KMeansModel(Transformer)]
// 从中间层算法组件角度看
训练算法组件 +-----> [MapBatchOp] -----> [KMeansTrainBatchOp]
| // VectorAssemblerMapper in MapBatchOp 是业务逻辑
|
转换算法组件 +-----> [MapBatchOp] -----> [ModelMapBatchOp]
// VectorAssemblerMapper in MapBatchOp 是业务逻辑
// KMeansModelMapper in ModelMapBatchOp 是业务逻辑
// 从底层迭代计算框架角度看
训练by框架 +-----> [VectorAssemblerMapper] -----> [KMeansPreallocateCentroid / KMeansAssignCluster / AllReduce / KMeansUpdateCentroids in IterativeComQueue]
| // 映射到Flink的各种算子进行训练
|
转换(直接) +-----> [VectorAssemblerMapper] -----> [KMeansModelMapper]
// 映射到Flink的各种算子进行转换
// 从Flink runtime角度看
训练 +-----> map, mapPartiiton...
| // VectorAssemblerMapper.map等会被调用
|
转换 +-----> map, mapPartiiton...
// 比如调用 KMeansModelMapper.map 来转换
0x02 Alink实例代码
示例代码还是用之前的KMeans算法部分模块。
算法调用
public class KMeansExample {
public static void main(String[] args) throws Exception {
......
BatchOperator data = new CsvSourceBatchOp().setFilePath(URL).setSchemaStr(SCHEMA_STR);
VectorAssembler va = new VectorAssembler()
.setSelectedCols(new String[]{"sepal_length", "sepal_width", "petal_length", "petal_width"})
.setOutputCol("features");
KMeans kMeans = new KMeans().setVectorCol("features").setK(3)
.setPredictionCol("prediction_result")
.setPredictionDetailCol("prediction_detail")
.setReservedCols("category")
.setMaxIter(100);
Pipeline pipeline = new Pipeline().add(va).add(kMeans);
pipeline.fit(data).transform(data).print();
}
}
算法主函数
public final class KMeansTrainBatchOp extends BatchOperator <KMeansTrainBatchOp>
implements KMeansTrainParams <KMeansTrainBatchOp> {
static DataSet <Row> iterateICQ(...省略...) {
return new IterativeComQueue()
.initWithPartitionedData(TRAIN_DATA, data)
.initWithBroadcastData(INIT_CENTROID, initCentroid)
.initWithBroadcastData(KMEANS_STATISTICS, statistics)
.add(new KMeansPreallocateCentroid())
.add(new KMeansAssignCluster(distance))
.add(new AllReduce(CENTROID_ALL_REDUCE))
.add(new KMeansUpdateCentroids(distance))
.setCompareCriterionOfNode0(new KMeansIterTermination(distance, tol))
.closeWith(new KMeansOutputModel(distanceType, vectorColName, latitudeColName, longitudeColName))
.setMaxIter(maxIter)
.exec();
}
}
算法模块举例
基于点计数和坐标,计算新的聚类中心。
// Update the centroids based on the sum of points and point number belonging to the same cluster.
public class KMeansUpdateCentroids extends ComputeFunction {
@Override
public void calc(ComContext context) {
Integer vectorSize = context.getObj(KMeansTrainBatchOp.VECTOR_SIZE);
Integer k = context.getObj(KMeansTrainBatchOp.K);
double[] sumMatrixData = context.getObj(KMeansTrainBatchOp.CENTROID_ALL_REDUCE);
Tuple2<Integer, FastDistanceMatrixData> stepNumCentroids;
if (context.getStepNo() % 2 == 0) {
stepNumCentroids = context.getObj(KMeansTrainBatchOp.CENTROID2);
} else {
stepNumCentroids = context.getObj(KMeansTrainBatchOp.CENTROID1);
}
stepNumCentroids.f0 = context.getStepNo();
context.putObj(KMeansTrainBatchOp.K,
updateCentroids(stepNumCentroids.f1, k, vectorSize, sumMatrixData, distance));
}
}
0x03 顶层 -- 流水线
本部分实现的设计原则是 :尽量借鉴市面上通用的设计思路和开发模式,让开发者无缝切换。
1. 机器学习重要概念
一个典型的机器学习过程从数据收集开始,要经历多个步骤,才能得到需要的输出。这非常类似于流水线式工作,即通常会包含源数据ETL(抽取、转化、加载),数据预处理,指标提取,模型训练与交叉验证,新数据预测等步骤。
先来说一下几个重要的概念:
-
Transformer:转换器,是一种可以将一个数据转换为另一个数据的算法。比如一个模型就是一个 Transformer。它可以把一个不包含转换标签的测试数据集 打上标签,转化成另一个包含转换标签的特征数据。Transformer可以理解为特征工程,即:特征标准化、特征正则化、特征离散化、特征平滑、onehot编码等。该类型有一个transform方法,用于fit数据之后,输入新的数据,进行特征变换。
-
Estimator:评估器,它是学习算法或在训练数据上的训练方法的概念抽象。所有的机器学习算法模型,都被称为估计器。在 Pipeline 里通常是被用来操作 数据并生产一个 Transformer。从技术上讲,Estimator实现了一个方法fit(),它接受一个特征数据并产生一个转换器。比如一个随机森林算法就是一个 Estimator,它可以调用fit(),通过训练特征数据而得到一个随机森林模型。
-
PipeLine:工作流或者管道。工作流将多个工作流阶段(转换器和估计器)连接在一起,形成机器学习的工作流,并获得结果输出。
-
Parameter:Parameter 被用来设置 Transformer 或者 Estimator 的参数。
2. Alink中概念实现
从 Alink的目录结构中 ,我们可以看出,Alink提供了这些常见概念(其中有些代码借鉴了Flink ML)。
./java/com/alibaba/alink:
common operator params pipeline
./java/com/alibaba/alink/params:
associationrule evaluation nlp regression statistics
classification feature onlinelearning shared tuning
clustering io outlier similarity udf
dataproc mapper recommendation sql validators
./java/com/alibaba/alink/pipeline:
EstimatorBase.java ModelBase.java Trainer.java feature
LocalPredictable.java ModelExporterUtils.java TransformerBase.java nlp
LocalPredictor.java Pipeline.java classification recommendation
MapModel.java PipelineModel.java clustering regression
MapTransformer.java PipelineStageBase.java dataproc tuning
比较基础的是三个接口:PipelineStages,Transformer,Estimator,分别恰好对应了机器学习的两个通用概念 :转换器 ,评估器。PipelineStages是这两个的基础接口。
// Base class for a stage in a pipeline. The interface is only a concept, and does not have any actual functionality. Its subclasses must be either Estimator or Transformer. No other classes should inherit this interface directly.
public interface PipelineStage<T extends PipelineStage<T>> extends WithParams<T>, Serializable
// A transformer is a PipelineStage that transforms an input Table to a result Table.
public interface Transformer<T extends Transformer<T>> extends PipelineStage<T>
// Estimators are PipelineStages responsible for training and generating machine learning models.
public interface Estimator<E extends Estimator<E, M>, M extends Model<M>> extends PipelineStage<E>
其次是三个抽象类定义:PipelineStageBase,EstimatorBase,TransformerBase,分别就对应了以上的三个接口。其中定义了一些基础操作,比如 fit,transform。
// The base class for a stage in a pipeline, either an EstimatorBase or a TransformerBase.
public abstract class PipelineStageBase<S extends PipelineStageBase<S>>
implements WithParams<S>, HasMLEnvironmentId<S>, Cloneable
// The base class for estimator implementations.
public abstract class EstimatorBase<E extends EstimatorBase<E, M>, M extends ModelBase<M>>
extends PipelineStageBase<E> implements Estimator<E, M>
// The base class for transformer implementations.
public abstract class TransformerBase<T extends TransformerBase<T>>
extends PipelineStageBase<T> implements Transformer<T>
然后是Pipeline基础类,这个类就可以把Transformer,Estimator联系起来 。
// A pipeline is a linear workflow which chains EstimatorBases and TransformerBases to execute an algorithm
public class Pipeline extends EstimatorBase<Pipeline, PipelineModel> {
private ArrayList<PipelineStageBase> stages = new ArrayList<>();
public Pipeline add(PipelineStageBase stage) {
this.stages.add(stage);
return this;
}
}
最后是 Parameter 概念相关举例,比如实例中用到的 VectorAssemblerParams。
// Parameters for MISOMapper.
public interface MISOMapperParams<T> extends HasSelectedCols <T>, HasOutputCol <T>,
HasReservedCols <T> {}
// parameters of vector assembler.
public interface VectorAssemblerParams<T> extends MISOMapperParams<T> {
ParamInfo <String> HANDLE_INVALID = ParamInfoFactory
.createParamInfo("handleInvalid", String.class)
.setDescription("parameter for how to handle invalid data (NULL values)")
.setHasDefaultValue("error")
.build();
}
综合来说,因为模型和数据,在Alink运行时候,都统一转化为Table类型,所以可以整理如下:
-
Transformer: 将input table转换为output table。
-
Estimator:将input table转换为模型。
-
模型:将input table转换为output table。
3. 结合实例看流水线
首先是一些基础抽象类,比如:
-
MapTransformer是 flat map 的Transformer。
-
ModelBase是模型定义,也是一个Transformer。
-
Trainer是训练模型定义,是EstimatorBase。
// Abstract class for a flat map TransformerBase. public abstract class MapTransformer<T extends MapTransformer > extends TransformerBase implements LocalPredictable {
// The base class for a machine learning model. public abstract class ModelBase<M extends ModelBase> extends TransformerBase implements Model
// Abstract class for a trainer that train a machine learning model. public abstract class Trainer<T extends Trainer <T, M>, M extends ModelBase> extends EstimatorBase<T, M>
然后就是我们实例用到的两个类型定义。
-
KMeans 是一个Trainer,其实现了EstimatorBase;
-
VectorAssembler 是一个TransformerBase。
// 这是一个 EstimatorBase 类型 public class KMeans extends Trainer <KMeans, KMeansModel> implements KMeansTrainParams , KMeansPredictParams { @Override protected BatchOperator train(BatchOperator in) { return new KMeansTrainBatchOp(this.getParams()).linkFrom(in); } }
// 这是一个 TransformerBase 类型 public class VectorAssembler extends MapTransformer implements VectorAssemblerParams { public VectorAssembler(Params params) { super(VectorAssemblerMapper::new, params); } }
实例中,分别构建了两个流水线阶段,然后这两个实例就被链接到流水线上。
VectorAssembler va = new VectorAssembler()
KMeans kMeans = new KMeans()
Pipeline pipeline = new Pipeline().add(va).add(kMeans);
// 能看出来,流水线上有两个阶段,分别是VectorAssembler和KMeans。
pipeline = {Pipeline@1201}
stages = {ArrayList@2853} size = 2
0 = {VectorAssembler@1199}
mapperBuilder = {VectorAssembler$lambda@2859}
params = {Params@2860} "Params {outputCol="features", selectedCols=["sepal_length","sepal_width","petal_length","petal_width"]}"
1 = {KMeans@1200}
params = {Params@2857} "Params {vectorCol="features", maxIter=100, reservedCols=["category"], k=3, predictionCol="prediction_result", predictionDetailCol="prediction_detail"}"
下文将介绍算法组件。
0xFF 参考
Spark ML简介之Pipeline,DataFrame,Estimator,Transformer
斩获GitHub 2000+ Star,阿里云开源的 Alink 机器学习平台如何跑赢双11数据“博弈”?|AI 技术生态论
★★★★★★关于生活和技术的思考★★★★★★
微信公众账号:罗西的思考