部署机器学习数据管道和算法不应该是一项耗时或困难的任务。MLeap允许数据科学家和工程师将Spark和Scikit-learn的机器学习管道部署到可移植的格式和执行引擎中。
文档
文档可在https://combust.github.io/mleap-docs/获取。
阅读序列化Spark ML管道并使用MLeap进行评分以全面了解可能性。
简介
使用MLeap执行引擎和序列化格式,我们为机器学习数据管道和算法提供了高性能、可移植且易于集成的生产库。
为了实现可移植性,我们在JVM上构建软件,并仅使用广泛采用的序列化格式。
我们还提供与现有技术的高度集成。
我们对这个项目的目标是:
- 允许研究人员/数据科学家和工程师继续使用Spark和Scikit-Learn构建数据管道和训练算法
- 通过提供ML管道序列化/反序列化到/从一个通用框架(Bundle.ML)来扩展Spark/Scikit/TensorFlow
- 使用MLeap运行时执行您的管道和算法,而无需依赖Spark或Scikit(numpy、pandas等)
概述
- 用Scala实现的核心执行引擎
- 支持Spark、PySpark和Scikit-Learn
- 使用Scikit-learn或Spark导出模型,并使用MLeap运行时执行它(无需依赖Spark上下文或sklearn/numpy/pandas等)
- 从2种可移植序列化格式中选择(JSON、Protobuf)
- 实现自定义数据类型和转换器,用于MLeap数据帧和转换器管道
- 广泛的测试覆盖,包括Spark和MLeap管道的完全对等测试
- 可选的Spark转换器扩展,以扩展Spark的默认转换器产品
依赖兼容性矩阵
除了下面列出的版本外,其他版本也可能工作(特别是JRE的更新Java版本), 但这些是由mleap测试的配置。
MLeap版本 | Spark版本 | Scala版本 | Java版本 | Python版本 | XGBoost版本 | Tensorflow版本 |
---|---|---|---|---|---|---|
0.23.1 | 3.4.0 | 2.12.18 | 11 | 3.7, 3.8 | 1.7.6 | 2.10.1 |
0.23.0 | 3.4.0 | 2.12.13 | 11 | 3.7, 3.8 | 1.7.3 | 2.10.1 |
0.22.0 | 3.3.0 | 2.12.13 | 11 | 3.7, 3.8 | 1.6.1 | 2.7.0 |
0.21.1 | 3.2.0 | 2.12.13 | 11 | 3.7 | 1.6.1 | 2.7.0 |
0.21.0 | 3.2.0 | 2.12.13 | 11 | 3.6, 3.7 | 1.6.1 | 2.7.0 |
0.20.0 | 3.2.0 | 2.12.13 | 8 | 3.6, 3.7 | 1.5.2 | 2.7.0 |
0.19.0 | 3.0.2 | 2.12.13 | 8 | 3.6, 3.7 | 1.3.1 | 2.4.1 |
0.18.1 | 3.0.2 | 2.12.13 | 8 | 3.6, 3.7 | 1.0.0 | 2.4.1 |
0.18.0 | 3.0.2 | 2.12.13 | 8 | 3.6, 3.7 | 1.0.0 | 2.4.1 |
0.17.0 | 2.4.5 | 2.11.12, 2.12.10 | 8 | 3.6, 3.7 | 1.0.0 | 1.11.0 |
设置
使用Maven或SBT链接
SBT
libraryDependencies += "ml.combust.mleap" %% "mleap-runtime" % "0.23.1"
Maven
<dependency>
<groupId>ml.combust.mleap</groupId>
<artifactId>mleap-runtime_2.12</artifactId>
<version>0.23.1</version>
</dependency>
Spark集成
SBT
libraryDependencies += "ml.combust.mleap" %% "mleap-spark" % "0.23.1"
Maven
<dependency>
<groupId>ml.combust.mleap</groupId>
<artifactId>mleap-spark_2.12</artifactId>
<version>0.23.1</version>
</dependency>
PySpark集成
从PyPI安装MLeap
$ pip install mleap
使用库
有关更完整的示例,请参阅我们的另一个Git存储库:MLeap演示
创建并导出Spark管道
第一步是在Spark中创建我们的管道。在我们的示例中,我们将手动构建一个简单的Spark ML管道。
import ml.combust.bundle.BundleFile
import ml.combust.mleap.spark.SparkSupport._
import org.apache.spark.ml.Pipeline
import org.apache.spark.ml.bundle.SparkBundleContext
import org.apache.spark.ml.feature.{Binarizer, StringIndexer}
import org.apache.spark.sql._
import org.apache.spark.sql.functions._
import scala.util.Using
val datasetName = "./examples/spark-demo.csv"
val dataframe: DataFrame = spark.sqlContext.read.format("csv")
.option("header", true)
.load(datasetName)
.withColumn("test_double", col("test_double").cast("double"))
// 像平常一样使用Spark内置的转换器
val stringIndexer = new StringIndexer().
setInputCol("test_string").
setOutputCol("test_index")
val binarizer = new Binarizer().
setThreshold(0.5).
setInputCol("test_double").
setOutputCol("test_bin")
val pipelineEstimator = new Pipeline()
.setStages(Array(stringIndexer, binarizer))
val pipeline = pipelineEstimator.fit(dataframe)
// 然后序列化管道
val sbc = SparkBundleContext().withDataset(pipeline.transform(dataframe))
Using(BundleFile("jar:file:/tmp/simple-spark-pipeline.zip")) { bf =>
pipeline.writeBundle.save(bf)(sbc).get
}
用于训练的数据集可以在这里找到
Spark管道不是为了在Spark之外运行而设计的。它们需要一个DataFrame,因此需要一个SparkContext来运行。这些是昂贵的数据结构和库,不适合包含在项目中。使用MLeap,执行管道不需要依赖Spark。MLeap的依赖很轻量,我们使用快速的数据结构来执行您的ML管道。
PySpark集成
在PySpark作业中导入MLeap库
import mleap.pyspark
from mleap.pyspark.spark_support import SimpleSparkSerializer
更多内容请参见python/README.md中的PySpark集成。
创建和导出Scikit-Learn管道
import pandas as pd
from mleap.sklearn.pipeline import Pipeline
from mleap.sklearn.preprocessing.data import FeatureExtractor, LabelEncoder, ReshapeArrayToN1
from sklearn.preprocessing import OneHotEncoder
data = pd.DataFrame(['a', 'b', 'c'], columns=['col_a'])
categorical_features = ['col_a']
feature_extractor_tf = FeatureExtractor(input_scalars=categorical_features,
output_vector='imputed_features',
output_vector_items=categorical_features)
# x1标签的标签编码器
label_encoder_tf = LabelEncoder(input_features=feature_extractor_tf.output_vector_items,
output_features='{}_label_le'.format(categorical_features[0]))
# 将LabelEncoder的输出重塑为N×1数组
reshape_le_tf = ReshapeArrayToN1()
# x1独热编码的向量组装器
one_hot_encoder_tf = OneHotEncoder(sparse=False)
one_hot_encoder_tf.mlinit(prior_tf = label_encoder_tf,
output_features = '{}_label_one_hot_encoded'.format(categorical_features[0]))
one_hot_encoder_pipeline_x0 = Pipeline([
(feature_extractor_tf.name, feature_extractor_tf),
(label_encoder_tf.name, label_encoder_tf),
(reshape_le_tf.name, reshape_le_tf),
(one_hot_encoder_tf.name, one_hot_encoder_tf)
])
one_hot_encoder_pipeline_x0.mlinit()
one_hot_encoder_pipeline_x0.fit_transform(data)
one_hot_encoder_pipeline_x0.serialize_to_bundle('/tmp', 'mleap-scikit-test-pipeline', init=True)
# array([[ 1., 0., 0.],
# [ 0., 1., 0.],
# [ 0., 0., 1.]])
使用MLeap加载和转换
由于我们将Spark和Scikit-learn管道导出为标准格式,我们可以使用之前步骤中的Spark训练管道或Scikit-learn管道来演示本节中MLeap的使用。选择权在您!
import ml.combust.bundle.BundleFile
import ml.combust.mleap.runtime.MleapSupport._
import scala.util.Using
// 加载我们在上一节保存的Spark管道
val bundle = Using(BundleFile("jar:file:/tmp/simple-spark-pipeline.zip"))) { bundleFile =>
bundleFile.loadMleapBundle().get
}).opt.get
// 创建一个简单的LeapFrame进行转换
import ml.combust.mleap.runtime.frame.{DefaultLeapFrame, Row}
import ml.combust.mleap.core.types._
// MLeap大量使用了Try等单子类型
val schema = StructType(StructField("test_string", ScalarType.String),
StructField("test_double", ScalarType.Double)).get
val data = Seq(Row("hello", 0.6), Row("MLeap", 0.2))
val frame = DefaultLeapFrame(schema, data)
// 使用我们的管道转换数据帧
val mleapPipeline = bundle.root
val frame2 = mleapPipeline.transform(frame).get
val data2 = frame2.dataset
// 从转换后的行获取数据并进行一些断言
assert(data2(0).getDouble(2) == 1.0) // 字符串索引器输出
assert(data2(0).getDouble(3) == 1.0) // 二值化器输出
// 第二行
assert(data2(1).getDouble(2) == 2.0)
assert(data2(1).getDouble(3) == 0.0)
文档
更多文档,请参阅我们的文档,您可以在其中学习:
- 实现可与Spark、MLeap和Scikit-learn一起使用的自定义转换器
- 实现自定义数据类型以使用Spark和MLeap管道进行转换
- 使用经过优化的基于行的转换器以极快的速度进行转换
- 将MLeap数据帧序列化为各种格式,如avro、json和自定义二进制格式
- 为MLeap数据帧实现新的序列化格式
- 通过几个使用真实世界数据创建预测管道的演示管道进行学习
- 支持的Spark转换器
- 支持的Scikit-learn转换器
- MLeap提供的自定义转换器
贡献
- 编写文档。
- 为有趣的ML问题编写教程/演练
- 贡献Spark中的Estimator/Transformer
- 在您的公司使用MLeap并告诉我们您的想法
- 在github上提出功能请求或报告错误
- 为现有的功能请求或错误报告提出拉取请求
- 加入关于如何将MLeap作为依赖项引入Spark的讨论。在Gitter上与我们交谈(见README.md顶部的链接)
构建
请确保您有sbt 1.9.3、java 11、scala 2.12.18
- 初始化git子模块
git submodule update --init --recursive
- 运行
sbt compile
感谢
感谢Swoop支持XGboost集成。
贡献者信息
- Jason Sleight (jsleight)
- Talal Riaz (talalryz)
- Weichen Xu (WeichenXu123)
过去的贡献者
- Hollin Wilkins (hollin@combust.ml)
- Mikhail Semeniuk (mikhail@combust.ml)
- Anca Sarb (sarb.anca@gmail.com)
- Ryan Vogan (rvogan@yelp.com)
许可证
请参阅本仓库中的LICENSE和NOTICE文件。
版权所有 20 Combust, Inc.
根据Apache许可证2.0版("许可证")获得许可; 除非遵守许可证,否则不得使用此文件。 您可以在以下位置获取许可证副本
http://www.apache.org/licenses/LICENSE-2.0
除非适用法律要求或书面同意,否则根据许可证分发的软件 是按"原样"分发的,没有任何明示或暗示的保证或条件。 有关许可证下的特定语言管理权限和 限制,请参阅许可证。