性能提升 25 倍:Rust 有望取代 C 和 C++,成為機器學習首選 Python 后端


https://www.infoq.cn/article/dgKDBiPl7KID0dyaE7Wl

在機器學習開發領域,如果我們縱觀全局,撇除所有微小的細節,那么就可以提煉出機器學習開發中的兩大不變步驟:模型訓練和預測(或推斷)。如今,機器學習的首選語言是Python(除非你的工作環境有一些不尋常的約束才會有所不同),而這篇文章會帶你走過一段新的旅程。希望當你看到最后會發現,使用 Rust 作為訓練后端和部署平台的主意並不像聽起來那樣瘋狂或令人困惑(除了標題提到的性能提升外,這種做法的好處其實還有很多)。

性能提升25倍:Rust有望取代C和C++,成為機器學習首選Python后端

為什么選擇 Python?

我們可以花很多時間討論機器學習開發中使用的各種工作流,但如果說我們通常是以一種探索性的方式來訓練模型,這通常是沒有爭議的。你有一組數據,然后把它們切成許多片段從而更好地理解它們,接着嘗試各種方法來解決你所關注的特定問題。(在谷歌街景圖片中識別出小貓?天氣預報?抑或是作物產量優化?做什么你來定!)

這一路上會有很多陷阱,最后你嘗試使用的大多數技術都不是開箱即用的,因此重點在於快速的原型設計和迭代改進。

對於像 Python 這樣的動態編程語言,這是一個理想的使用場景。

更重要的是,你要考慮到大多數機器學習實踐者會有統計學、數學、物理或類似學位的背景,卻不是計算機科學專家,也就是說他們(我也一樣✋)幾乎沒有接受過軟件工程實踐和工具方面的訓練。

雖說 Python 同時支持函數式和面向對象的模式,但你可以使用命令式風格,憑借其腳本功能來快速上手。它的入門門檻很低,隨着你的經驗提升,越來越精於此道,Python 也會與你一同成長。

但是,僅僅易用是遠遠不夠的:訓練機器學習模型需要大量的繁瑣運算,而 Python 絕對不是最快的編程語言。

於是我們看到 NumPy(1995/2006)、SciPy(2001)、Pandas(2008)和 Scikit-learn(2007)魚貫入場。如果沒有這樣一個用於機器學習和科學計算的、高質量且覆蓋全面的工具包,Python 就不會取得今天的地位。

然而,如果你深入背后探究一番,就會發現那里沒有多少 Python 的位置:你正在使用 Python 來編排和利用一個 C 和 C++ 例程的強大內核。

Python 是這些系統的前端,用戶用 Python 這個用戶界面將它們輕松地粘合在一起。C 和 C++ 才是你的后端,是幕后的魔力源泉。

性能提升25倍:Rust有望取代C和C++,成為機器學習首選Python后端

確實,這是 Python 經常被忽略的特性:使用其外函數接口(FFI)與其他編程語言互操作相當容易。特別是,Python 庫可以將需要大量數字運算的程序代碼委派給 C 和 C++,這是 Python 科學生態系統中所有基礎庫都在使用的策略。

當然,技術永遠無法決定一切。社會學因素對於大多數項目的成功(或消亡)都是至關重要的,即使有些人覺得這難以接受。

因此我們應該再補充一些背景:Python 是一個開放源代碼項目(嗨,MATLAB!),它在學術機構中的滲透水平是不可忽略的;而且事實上,當深度學習走進聚光燈下時,與它相關的多數科學生態系統已經建立完畢了。

事后看來,將 Python 視為會在機器學習領域占據統治地位的強大候選者是很自然的事情,結果也並不出人意料。

我們今后還應該繼續使用 Python 嗎?

前面我們簡要地介紹了將 Python 作為機器學習開發首選編程語言的部分原因。

但世界並不是靜止不變的:背景環境的變化可以大大改變人們對哪種工具是“最佳工作工具”的認識。

一些最新趨勢可能會加強 Python 在機器學習領域的地位。

微服務

微服務架構目前在架構設計方法中占主導地位:公司用松散的容器化服務集合來運行他們的業務,這些服務通過網絡相互通信。

運行一個 Polyglot 堆棧從未如此簡單:你的主應用程序和業務邏輯的精華都可以用 Java 編寫——當你想利用機器學習來確定某筆信用卡交易是合法還是欺詐時,你可以發出一個 POST 請求到一個 Python 微服務上。

數據科學家和機器學習工程師用 Python 執行模型探索的日子已經一去不復返了,如今我們將所有內容移交給“生產團隊”,后者會用公司選擇的語言全面重寫邏輯。

DevOps

你構建,你運行——Werner Vogels(亞馬遜 CTO)

既然我們談論的是業務,那就必須強調一點:機器學習模型不是憑空存在的,它們是公司要啟動、優化或改進的產品或過程的一部分。

因此,僅由數據科學家組成的團隊就能取得顯著的成績——是很天真的想法。你需要的東西遠不止這些。

如果要獲得成功的機會,則需要從產品到軟件工程的各種技能的組合。

那么這樣的團隊應該使用哪種編程語言?

記住 JavaScript 的興起歷程:同一個人使用 JavaScript 和 NodeJS,就可以同時處理系統的前端和后端工作(“全棧”)。

作為通用編程語言的 Python 提供了相同的便利。你可以將其科學堆棧用於機器學習開發,並利用其框架(Django、Flask 和 FastAPI 等)進行模型部署,再通過 REST 或 gRPC API 提供預測。

很好,不是嗎?

連鎖效應

  1. Python 擁有一個龐大的機器學習生態系統;
  2. 你希望自己的機器學習算法或機器學習框架能被采納:所以你使用 Python 編寫代碼(或使用 FFI 為它提供 Python 綁定);
  3. Python 生態系統變得更強大了。

循環往復。

答案

明天我們可能還是會用 Python 來編寫機器學習軟件。

我們會永遠使用它嗎?不太可能,這就像在問自己,從現在起 10 年后計算機產業的未來會是什么樣。

但是我不會押注說未來 5 年我們就能看到 Python 的落日。

所以呢?這篇文章不是要談 Rust 的嗎?

性能提升25倍:Rust有望取代C和C++,成為機器學習首選Python后端

沒錯!

但更重要的是,在開始談論正題之前消除所有可能的誤解。

我不相信 Rust 會取代 Python 成為機器學習的首選語言——這事完全沒有任何苗頭,不管是今天還是未來,這都不是什么趨勢。

這兩門語言無法迎合相同的人群,並且它們針對的是不同的約束條件,做了不同的優化工作,解決的是一系列不同的問題。

但是Rust 在機器學習世界中有自己的一席之地。

Rust 具有取代 C 和 C++,成為機器學習負載首選的 Python 后端的巨大潛力。

為什么是 Rust?

沒有比這本書的序言更好的答案了:

例如,“系統級”地處理內存管理、數據表示和並發性的底層細節。傳統上,這種編程領域被視為是神秘的王國,只有少數一些已經花了足夠的時間學習,以避免其臭名昭著陷阱的人們才能踏入其中。即使是實踐它的那些人們也要謹慎行事,以免他們的代碼易受攻擊、容易崩潰或損壞。

Rust 消除了那些舊有的陷阱,並提供了一套友好而精致的工具來幫助你披荊斬棘,打破這些障礙。那些需要“深入”到較底層控制的程序員可以使用 Rust 來做到這一點,而不必承擔崩潰或出現安全漏洞的常見風險,也不必領悟多變的工具鏈的精髓所在。更好的是,這種語言旨在引導你自然地開始使用在性能和內存使用方面效率出色的可靠代碼。

Rust 以徹底領先的信心水平提供了與 C 和 C++ 相當的性能。

你相信編譯器知道你所不知道的內容:換句話說,你從“這到底是什么?”安全地轉到了“讓我們在生產中運行這些代碼!”的這條路線上。

這大大降低了入門的門檻。

讓更多的人(又包括我✋)可以編寫高性能的機器學習算法。

越來越多的人可以為他們每天使用的那些項目的后端做出貢獻。

這會催生一個更大的社區、更多的實驗和更可持續的項目——換句話說,催生一個更健康、更多樣化的生態系統。

回到我之前提到的那些趨勢,你會再次發現全棧帶來的強大力量:負責模型探索的那個人(使用 Python)可以深入研究並使用 Rust 重寫其熱路徑,來優化最終解決方案。。

但在實踐中這樣做的難度如何呢?

性能提升25倍:Rust有望取代C和C++,成為機器學習首選Python后端

用 Rust 實現聚類算法能快多少?

我為 RustFest 2019 准備了一個研討會:我們使用 ndarray(一個 NumPy 的 Rust 等效方案)從零開始實現了 K-Means 聚類算法。

幾周前,我寫了一些關於研討會的筆記相關材料可以在 GitHub 上找到:它由一系列測試驅動的練習構成,每個步驟都為最終解決方案作出了貢獻。

我不能忽視這個問題:與 scikit-learn 相比,Rust 中 K-Means 的范例實現有多快?

我和一群同樣對此問題剛到好奇的人在 RustFest 度過了兩天實現日,最后給出了答案。

如果沒有 @ sitegui 、@ dunnock 和 @ ThomAub ,這個過程會花費更長的時間:非常感謝你們的幫助!

實現

我用 Rust crate 發布了一個清理過的 K-Means 實現:linfa-clustering( https://crates.io/crates/linfa-clustering )。linfa-clustering 是 linfa( https://crates.io/crates/linfa )的一個子集——我們稍后會詳細討論后者。

從源代碼中你可以看出來,重點在於清晰易懂的優化配置:它是 Lloyd 算法的實現范例。

大多數提速機會都沒有得到利用,並且肯定還有進一步調優和打磨的空間——例如,它只將多線程用於分配步驟,而更新步驟還是單線程的。

為了進行正面比較,我為此編寫了 Python 綁定( https://github.com/LukeMathWalker/linfa-python ):linfa is on PyPi( https://pypi.org/project/linfa/ ),作為 Python 庫。

我想重點對比一下:

  • 訓練時間;
  • 推理時間,模型作為一個 gRPC 微服務公開時所測得的時間。

我們測量將模型作為微服務公開來提供預測需要的時間,這更接近在實際生產環境中使用此代碼的表現。

你可以在 GitHub 上獲得重現基准測試的說明、結果和代碼( https://github.com/LukeMathWalker/clustering-benchmarks )。

訓練基准測試

使用 pytest-benchmark )在一個 100 萬點的數據集上訓練 K-Means 模型時,linfa 的訓練速度是 scikit-learn 的 1.3 倍。

平均訓練時間(毫秒)
Linfa(Rust 上的 Python 包裝器) 467.2
Scikit Learn 604.7(慢 1.3 倍)

總體而言,它們的速度比較接近——由於分配步驟是並行的,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 Serving 越來越受歡迎,我的想法得到了驗證。

因此我決定針對三種場景進行基准測試:

  • scikit-learn 的 K-means 運行在 Python 的 gRPC 服務器上;
  • linfa 的 K-means(Python 包裝器)運行在 Python 的 gRPC 服務器上;
  • linfa 的 K-means(Rust)運行在 Rust 的 gRPC 服務器(tonic, https://github.com/hyperium/tonic )上。

我尚未在這些 gRPC Web 服務器上做任何形式的調優:我們要評價的是開箱即用的性能。我再次邀請你查看源代碼(Rust/Python)。

Rust Web 服務器上的 linfa 每秒處理的請求數是 scikit-learn 的 25 倍,是 python gRPC 服務器上的 linfa(Python 包裝器)的 7 倍。

性能提升25倍:Rust有望取代C和C++,成為機器學習首選Python后端

延遲(提供響應需要多長時間)也是如此,其中 Rust Web 服務器上的 linfa 始終比 scikit-learn 快 25 倍,比 Python Web 服務器上的 linfa(Python 包裝器)快 6 倍。

性能提升25倍:Rust有望取代C和C++,成為機器學習首選Python后端

Rust Web 服務器上的 linfa 在重負載下的錯誤率也是最低的。

性能提升25倍:Rust有望取代C和C++,成為機器學習首選Python后端

新的工作流

這項實驗規模太小,無法得出確切的結論,而且我相信你可以找到針對 K-Means 的 Lloyds 算法的更快實現。

但我希望這些結果足以說服你,Rust 確實可以在機器學習開發中發揮重要作用。所有人只要學一些 ndarray 的用法(可以試試研討會提供的材料),就可以寫出這樣的 Rust 實現——可就因為 C 和 C++ 的入門門檻,大批機器學習從業者浪費了多少潛能?

如果這還不夠,我還想告訴你,Rust 不僅可以替換掉 Python 的 C 和 C++ 后端——它還可以利用其不斷發展的異步生態系統來處理部署工作。

做起來很簡單:

  • 使用基於 Rust 的 Python 庫識別候選模型;
  • 序列化最終模型;
  • 提供最終模型的路徑和輸入數據的預期模式作為配置;
  • 收獲果實吧。

性能提升25倍:Rust有望取代C和C++,成為機器學習首選Python后端

這絕對是一個值得在 2020 年探索的想法。

走下去

如前所述,linfa-clustering 是 linfa 的子集,后者是 Rust 中的通用機器學習框架,我計划在 2020 年專注研究這個框架。

甚至在此時將其稱為一個框架還為時過早:linfa-clustering 之外就沒什么東西了😀。

要實現其大膽的使命宣言還有很長的路要走,但在機器學習及其相關領域,對 Rust 生態系統的興趣愈加濃厚: https://github.com/rust- ml/ discussion/issues/1  https://github.com/rust-lang/wg-governance/issues/11  https://github.com/rust-lang/wg-governance/issues/11 

有時你只需點燃星星之火,即可期待它熊熊燎原。

實際上,我堅信只有社區努力推動,才能在 Rust 中扶持、建立和維持一個機器學習生態系統——並沒有捷徑可言。

Rust 生態系統確實包含豐富的機器學習 crates——看看在 crates.io 上搜索 machine learning 會返回多少東西吧。

我們無需從頭開始重寫所有內容:我將 linfa 視為一個元包,一個 Rust 生態系統中精選的算法實現的集合。它是滿足你機器學習需求的第一站,就像是 Python 中的 scikit-learn 一樣。

如果這篇文章引起了你的共鳴,請看一看路線圖——我期待你的貢獻!

非常歡迎你提供關於本文的注釋、建議和反饋:你可以在 Twitter 上 @algo_luca,在 GitHub 上 @LukeMathWalker,或通過電子郵件 rust@lpalmieri.com 與我聯系。

原文鏈接:

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM