使用 Rust 将机器学习生产速率提升25倍!

本文经作者授权翻译,原作者Luca Palmieri



原文链接:



https://www.lpalmieri.com/posts/2019-12-01-taking-ml-to-production-with-rust-a-25x-speedup/

TDU导读:
随着Rust语言的强劲增长,也有许多数据科学家在探索Rust在数据科学、机器学习方面的应用,本文作者是一名机器学习工程师,他使用Rust和Python分别实现了K-Means算法,并进行了对比,并给出了自己的代码和材料,值得探索。

正文

如果我们抛弃所有小细节,纵观全局,那么机器学习开发中将只剩两个部分:

  • 模型训练
  • 做出预测(推论)

如今,机器学习的首选语言是

Python

(除非您的工作环境有一些不同寻常的限制)。

下面,我将带你踏上一段旅程。希望这段旅行结束后,将

Rust


用于 后端训练和平台部署
不会像听起来那样疯狂或者令人困惑了。


为什么是Python?

我们可以花很多时间讨论机器学习开发的不同工作流,但毫无争议的是,模型训练通常是以一种

探索性的方式

进行的。你拥有一组数据,为了更好地理解,你将其切割成小块,然后尝试各种方法来解决某个你所关注的特定问题(例如在Google Street View 图片中识别小猫,天气预报,作物产量优化等等)。

一路上会有很多陷阱,你尝试的大多数技术最后可能都无法开箱即用。因此,重点是

快速设计出原型

然后

迭代改进

。所以像Python这样的动态编程语言是一个理想的选择。


动态编程语言:
https://en.wikipedia.org/wiki/Dynamic_programming_language

更进一步,如果你了解到大部分机器学习的从业人员是统计学,数学,物理或类似的专业出身,而不是计算机科学的背景。就会知道他们几乎没有经过任何软件工程实践和工具的训练。

尽管Python同时支持函数性和面向对象模式,你可以使用命令式的脚本语言开始使用它。它的入门门槛很低,而且你会发现Python会随着你的经验和精通程度一起提高。

但是,仅仅易于使用不会帮你走得很远:训练机器学习模型需要进行大量的繁琐运算,而Python绝对不是最快的语言。所以,NumPy(1995/2006),SciPy(2001),Pandas(2008)和Scikit-learn(2007)出现了。如果没有这些用于机器学习和科学计算的高质量且全面的工具包,Python不会是今天的样子。

但是,如果你透过表象研究,会发现你只是使用Python来协调和利用了

C和C++ 的强大核心



Python是这些系统的前端

,用户利用python轻松将各种东西缝合在一起;而

C和C ++是您的后端

,是幕后神奇的魔法。

事实上,这往往是Python容易被人忽略的特点:利用外部功能接口(
Foreign Function Interfa
ce ),它很轻易可以与其他编程语言进行交互操作。特别是,Python库可以将需要大量数字运算的程序委派给C和C ++,这是Python科学生态系统中所有基础库所使用的策略。

当然,技术永远无法说明一切。即使有些人觉得难以接受,社会学因素对于大多数项目的成功(或消亡)都是至关重要的。因此,我们应该增加一种说法,即Python是一个开源项目,它在学术机构中的渗透水平不可忽略;当深度学习成为人们关注的焦点时,它的大多数科学生态系统早已建立。事后看来,将Python视为最终在机器学习领域占据统治地位的强大候选人并不奇怪。

未来还应该使用Python吗?

我们已经简要地介绍了将Python作为机器学习开发的首选编程语言的原因。但是,世界并不会保持静止:环境的改变可以大大改变人们对哪种工具是“最佳工作工具”的认识。

最新的趋势可能增强了Python在该领域的地位。

微服务

微服务架构目前在Design Space 中占主导地位:公司将其业务打包为服务集合来运行,这些服务通过网络相互通信。这使得运行多语言堆栈变得简单:您的主应用程序可以用Java编写-当您想利用机器学习来确定某笔信用卡交易是合法还是欺诈时,可以向Python的微服务提出POST请求。

数据科学家和机器学习工程师用Python执行模型探索后,将所有内容移交给“生产团队”以完全改写公司选择的语言,这样的日子已经一去不复返了

DevOps


“you build it, you run it” -Werner Vogels(Amazon CTO)

如果是在业务层面,需要强调的是机器学习模型并不是凭空存在的:它们是公司要启动,优化或改进某个产品或过程中的一部分。因此,

仅由

数据科学家组成的团队并不一定会具有出色的表现。如果要获得成功,则需要从产品到软件工程的各种技能组混合发挥效用。

那么这样的团队应该使用哪种编程语言?使用JavaScript和NodeJS可以使同一个人能够做前端和后端的工作(“全栈”)。

Python作为通用编程语言也提供了这样的便利。你可以将其用于机器学习开发,并利用其框架(Django,Flask,FastAPI)将模型部署为REST或gRPC API来进行预测。


网络效应

  1. Python具有庞大的机器学习生态系统;
  2. 如果你希望你的机器学习算法或框架被采用,那么请使用Python编写代码(或使用FFI绑定Python);
  3. Python生态系统越来越庞大

答案

未来我们可能仍将使用Python编写机器学习软件。我们会永远使用它吗?这不太可能,这个问题就像在问十年后计算机的未来是什么样的。但我推测未来5年我们将迎来Python的衰落。



那又怎样?


这不是一篇关于Rust的文章吗?

确实是的!但在开始之前,消除任何可能产生的误解是非常重要的。 

从机器学习的语言选择来说,我不认为Rust会代替Python

–事实上也没有发生,不管现在还是未来,这都不是个问题。

它们满足的是不同人群的需求,经过优化后,它们可以在不同的约束条件下解决不同的问题集。

但是Rust在机器学习的世界里也起了作用!

Rust有着巨大的潜力,

来代替C和C++优先成为Python在机器学习工作负荷的后端。

为什么是Rust?

没有比《Rust编程语言》这本书前言中所说的更好的答案了


「举个例子,“系统级别”工作就是处理内存管理、数据抽象和并发等底层细节。传统来说,这类编程领域被认为很神秘,只有少数人花了必要的时间学习,以避免一些臭名昭著的隐患。即使是那些实践它的人也要谨慎行事,以免他们的代码暴露于漏洞、崩溃或灾难之中。


Rust通过消除这些问题,提供了友好的、优雅的工具打破了这些障碍。那些需要“下潜”到底层控制的程序员可以使用Rust这样做,既不用承担日常的崩溃或安全漏洞风险,也不必学习易变的工具链。更好的是,这门语言旨在引导你自然而然地获得可靠的代码,并且执行效率高,内存使用充分.」

Rust在完全不同的置信水平上提供了可与C及C++媲美的性能。



你要相信编译器知道你所不知道的:


换句话说,你可以安全的从“这他妈的是什么?”转化到“让我们在生产中运行此代码!”的路线上.

这大大降低了门槛。

更多的人(又是我~)可以尝试编写高性能的机器学习算法。

越来越多的人有机会为他们日常使用的项目的后端做出贡献。

这会带来一个更大的社区、更多的实验以及更可持续的项目——换句话说,是一个更健康、更多样化的生态系统。

回到我前面提到的那些趋势,你会再次发现全栈的强大能量:同一个人既可以负责模型探索(使用Python),也可以使用Rust重写热路径,深入优化最终的解决方案。

那么这很容易实践么?


做个小实验:聚类

我为RustFest2019大会准备了一次研讨会:我们使用ndarray(一个与numpy等价的Rust的库)从头实现K-Means聚类。


workshop介绍地址:


https://barcelona.rustfest.eu/sessions/machine-learning-ndarray


RustFest2019大会介绍:


https://barcelona.rustfest.eu/

几周前,我在研讨会上写了一些笔记,这些材料可以在GitHub上找到。它是由一系列测试驱动的练习构成的,每一步都有助于最终的解决方案。


研讨会笔记:


https://www.lpalmieri.com/posts/2019-11-14-rustfest-2019-a-retrospective/


ndarray介绍:


https://github.com/LukeMathWalker/ndarray-koans

一个我不能回避的问题是:与scikit-learn提供的K-Means相比,Rust的教学实施速度有多快??

我和其他一些好奇的人们花掉了“implentation days”的时间给出了答案。如果没有 @sitegui, @dunnock and @ThomAub ,将花费更长时间,感谢你们的帮助!


实现

我已经发布了一个干净的K-Means算法实现,是名为linfa-clustering的Rust类库。linfa-clustering是linfa的一个子类-我们稍后将详细讨论它。

linfa-clustering:

https://crates.io/crates/linfa-clustering

你可以清楚的看到源代码着重在清晰的优化,而不是模糊的:这是 Lloyds’ 算法的教科书实现。

https://github.com/LukeMathWalker/linfa/blob/master/linfa-clustering/src/k_means/algorithm.rs

大多数提速机会都没有利用起来,绝对还存在分析和优化的空间——例如,它在分配阶段仅使用了多线程,而更新阶段使用了单线程。

为了进行比较,我编写了Python:在Python库–PyPi上使用linfa。

我着重想比较一下:

  • 训练时间

  • 预测时间

    ,当该模型作为 gRPC微服务
    公开时进行测量。

通过测量将模型作为微服务公开的预测所需时间,我们可以更接近在实际生产环境中使用此代码的实际效果。

GitHub上提供了重现基准测试的说明、结果和代码。

https://github.com/LukeMathWalker/clustering-benchmarks



训练基准

使用pytest-benchmark,在1百万点的数据集上,使用linfa(Python)

训练

一个K-Means模型,要比scikit-learn快

1.3倍

pytest-benchmark:https://pypi.org/project/pytest-benchmark/

总的来说,他们的速度具有可比性–由于并行的分配步骤,linfa可能稍微快一些

如果您发现这个现象,请再次思考:我们正在比较的双方,一方是为

两天的教学研讨会准备的实现

,另一方是

最成熟机器学习框架使用的实现

从基准代码中可以看到,linfa 的 K-Means实现提供了一个类似scikit-learn接口。

from sklearn.datasets import make_blobs 

import pytest 

from linfa import KMeans 

from sklearn.cluster import KMeans as sk_KMeans


@pytest.fixture(scope="session", autouse=True) def make_data(): return make_blobs(n_samples=1000000)
def test_k_means_rust(benchmark, make_data): dataset, cluster_index = make_data model = KMeans(3, max_iter=100, tol=1e-4) labels = benchmark(model.fit_predict, dataset) assert len(labels) == len(cluster_index) def test_k_means_python(benchmark, make_data): dataset, cluster_index = make_data # Using the same algorithm model = sk_KMeans(3, init="random", algorithm="full", max_iter=100, tol=1e-4, n_init=1) labels = benchmark(model.fit_predict, dataset) assert len(labels) == len(cluster_index)

我也想鼓励你尝试一下Rust的版本——交互看起来有点不同(原因我可能会在另一篇博客文章中讨论),但相信你可以很容易地识别出相同的步骤:

use linfa::clustering::{generate_blobs, KMeans, KMeansHyperParams}; 

use ndarray::array; 

use ndarray_rand::rand::SeedableRng; 

use rand_isaac::Isaac64Rng;


fn main() { // Our random number generator, seeded for reproducibility let mut rng = Isaac64Rng::seed_from_u64(42); // For each our expected centroids, generate 1000 data points around it (a "blob") let expected_centroids = array![[10., 10.], [1., 12.], [20., 30.], [-20., 30.]]; let dataset = generate_blobs(10000, &expected_centroids, &mut rng); // Configure our training algorithm let n_clusters = 4; let hyperparams = KMeansHyperParams::new(n_clusters) .max_n_iterations(200) .tolerance(1e-5) .build(); // Infer an optimal set of centroids based on the training data distribution let model = KMeans::fit(hyperparams, &dataset, &mut rng); // Assign each point to a cluster using the set of centroids found using fit let labels = model.predict(&dataset); }

预测基准


如前所述,使用专用的微服务为机器学习模型提供服务是业界公认的模式。
不过,在这些微服务中,通常很少甚至没有业务逻辑:它只不过是一个


远程函数调用。

给定一个序列化的机器学习模型,我们能完全自动化/抽象掉API生成吗?我认为这是一定的,TensorFlow的流行已经证实了这一点。

因此,我决定对三个场景进行基准测试:

  • 基于Python的gRPC服务器的scikit learn的K-means;
  • 基于Python的gRPC服务器的linfa的K-means(Python包装器);
  • 基于Rust的gRPC服务器(tonic)的linfa的K-means(Rust)。

我没有在这些gRPC web服务器上执行任何微调:我在寻找开箱即用的性能。你可以查看源代码:


Rust:


https://github.com/LukeMathWalker/clustering-benchmarks/blob/master/rust-grpc/src/main.rs


Python:


https://github.com/LukeMathWalker/clustering-benchmarks/blob/master/python-grpc/src/main.py

Rust web服务器上的linfa每秒处理的请求比scikit learn多25倍
,比Python gRPC服务器上的linfa(Python wrapper)多7倍。



延迟(提供响应需要多长时间)也是如此,


其中Rust Web服务器上的linfa始终比scikit-learn

快26倍

,比Python Web服务器上的linfa(Python包装器)快7倍。

Rust web服务器上的linfa在重载情况下的错误率也是最低的。


一个新的工作流

这是一个太小的实验,不能得出彻底的结论,我相信你可以为K-Means找到更快的Lloyds’ 算法实现。

但我希望这足以让你相信Rust可以在机器学习开发中发挥作用。每个人都可以通过对ndarray工作原理的一些训练来编写Rust实现(试试workshop的材料!),有多少机器学习实践者由于使用C和C++而被抑制了潜力?

如果这还不够,Rust不仅可以替代C和C++作为Python后端,Rust可以利用其日益增长的异步生态系统来处理部署。

这可能像下面这些工作一样简单:

  • 使用Rust驱动的Python库识别您的候选模型;
  • 将最终模型序列化;
  • 提供最终模型的路径和输入数据的预期模式作为配置;;
  • 盈利。

这绝对是2020年值得探索的方向!


继续前进

如前所述,linfa-clustering是linfa的一个子类,linfa是Rust中的一个通用机器学习框架,我计划在2020年重点关注它。

尽管称之为框架,但它目前并不成熟:linfa-clustering确实是唯一的一部分。

完成其大胆的使命宣言任重道远,但当涉及到机器学习及其环境时,在Rust生态系统中意义重大。有时,星星之火可以燎原。


Rust的宣言:


https://github.com/rust-ml/discussion/issues/1

事实上,我坚信只有社区才能在Rust中培育、构建,并维持一个机器学习生态系统-没有其他的出路。


https://www.youtube.com/watch?v=odI_LY8AIqo&t=8s

Rust生态系统中确实有大量的机器学习类库—只需在crates.io上搜索机器学习便可得到。


crates.io:


https://crates.io/search?q=machine%20learning

你不需要从头开始重写所有东西:我把linfa想象成一个元包,一个来自Rust生态系统的精确算法实现的集合。正如Python的scikit-learn一样,是您的ML需要的第一站。

如果少了什么,我们会写下来。

如果某些东西已经可用并且可以通过兼容的接口公开,我们将采用它。

提出一系列特征确实是最重要的贡献之一——但首先我们必须探索,然后我们将抽象。

如果这一点引起了你的共鸣,请看一看路线图-我期待着你的贡献!


路线图:

https://github.com/LukeMathWalker/linfa/issues

非常欢迎与我沟通这篇文章的笔记、建议和反馈



Twitter at @algo_luca

GitHub at @LukeMathWalker
email rust@lpalmieri.com
正文完


本文经作者授权翻译,原作者Luca Palmieri

原文链接:
https://www.lpalmieri.com/posts/2019-12-01-taking-ml-to-production-with-rust-a-25x-speedup/
文中图片、超链接来自原文