Stimulator

機械学習とか好きな技術話とかエンジニア的な話とかを書く

Rustで扱える機械学習関連のクレート2021

- はじめに -

本記事では、Rustで扱える機械学習関連クレートをまとめる。

普段Python機械学習プロジェクトを遂行する人がRustに移行する事を想定して書くメモ書きになるが、もしかすると長らくRustでMLをやっていた人と視点の違いがあるかもしれない。


追記:2021/02/24

repositoryにしました。こちらを随時更新します
github.com


追記;2021/07/26

GitHub Pagesでウェブサイトにしました
vaaaaanquish.github.io

- 全体感 -

Rustで書かれた(もしくはwrapされた)クレートは、かなり充実しつつある段階。

特に流行しているNeural NetworkないしDeep Learning関連のクレートは更新が盛んである。TensorFlowやPyTorchのRust bindingsもあれば、ゼロからRustで全て書こうというプロジェクトもある。

Numpy、Pandasを目指すプロジェクトも既に存在しているし、元々C/C++で書かれているライブラリであれば、rust-bindgenを使ってRust bindingを簡単に作れるようになってきている。
GitHub - rust-lang/rust-bindgen: Automatically generates Rust FFI bindings to C (and some C++) libraries.

古典的な画像処理やテキスト処理(例えばshift特徴量が欲しいだとかTF-IDFを計算したいだとか)で色々と物足りないクレートを使うことになる場合もありそうだが、画像はimage-rsという大きなクレートが存在し入出力を握っているし、形態素解析器などのクレートも多いので、アルゴリズム部分を自分でガッと書けば良いだけであると言えそう。
 
 

「物足りないクレート」と言ったが、2016~2018年から更新のないクレート、C++ライブラリのリンクが上手くいっていないクレート、Cargo.tomlだけのクレート(crates.ioにアップロードされ名前空間汚染になっている)などがあるという話で、その辺りを踏まえるとまだ(Pythonに比べたら)とっつきにくさがある。

Pythonであれば様々な機械学習モデルへの入力の多くをNumpyを使った行列で記述するが、Rustでの同等のプロジェクトではndarrayがあるものの安定し始めたのはちょうど1年前くらいからで、クレートによって入出力がrustのvectorだったりndarrayだったり、また別のnalgebraというクレートを使っている場合もあるといった状態である。

あとは機械学習の実験時に必要な物事をどうするかという問題も殆ど定まっていない。パラメータ管理はJSONを使うのか、設定時はRustらしくメソッドチェーンを使うのか、どう結果を保存するのか、パイプラインを作っていけるのか、など解決していない(デファクトスタンダードが定まっていない)課題は山積みである。

まあでも似たような問題はJulia langが流行り始めた頃にもあったように記憶していて、参入者が多くなるにつれて、自然とデファクトスタンダードは決まっていくだろう、と感じている。


ここでは、個人的にこれが残っていくんじゃないかと考えているものを優先的に書く。

 

- 機械学習足回り関連のクレート -

jupyterとかnumpy、pandasとか画像、テキスト処理などの機械学習前処理関連のもの
 

Jupyter Notebook

Python機械学習関連の物を作る人の多くがJupyterを使っているだろう(と思っている)。googleプロジェクトの配下にあるEvcxrが便利。
github.com
Notebookカーネル、REPLがあるので、普段Rustの小さな挙動を確認したい時はこれを起動するかシェルから叩いている。私としてはかなり開発速度が上がったツールの1つ。

類似のものとしてはrustdefとかを見てみたが、Evcxrの方が使い勝手がPythonカーネルに近い。


matplotlibやseabornとまではいかないが、vectorであればグラフ描画にplottersのjupyter-integrationが使いやすいなと思って入れてはいるものの、データ分析、EDAにおいてはやはり型にうるさくないPythonがやりやすいので結局Pythonカーネルを起動してる。
GitHub - 38/plotters: A rust drawing library for high quality data plotting for both WASM and native, statically and realtimely 🦀 📈🚀

plotlyのRust bindingsもあって、Pythonくらい柔軟になればあるいはとも思っている。EvcxrをSupportしたのが6ヶ月前にReleaseされた0.6.0(現行最新バージョン)であるのでこれからという感じ。
GitHub - igiagkiozis/plotly: Plotly for Rust

 

Numpy/Scipy

ndarrayというクレートnalgebraというクレートが2大巨頭で争っている。
redditの議論: ndarray vs nalgebra : rust

解決したい課題がnalgebraは線形代数特化な様相もあって、Pythonのように動的によしなに行列をスライスしたり変形させる機構ではなく、コンパイル時に行列の大きさを推定しメモリを確保してそこを使う実装になっている。ピュアRustという事もあるし、ndarrayのような柔軟さが欲しい場合はRustを使う意義があまりないと私は思っている。機械学習で扱う行列演算で曖昧な処理をしてバグを生むことも多いので、機械学習で使う側面から見てもnalgebraが幅をきかせていくと思っているがndarrayという名前に勝てず皆使っているのはndarrayという感じ。

github.com
github.com


(ちなみに私が作っているライブラリでは決めかねてvec![]を使っているごめんなさい)

Pandas

polars一択だと思う。polarsはpythonバインディングもあり、pandasより早い事を謳うライブラリの1つでもある。
github.com
applyやgroupby、aggを使う限りでは、殆どpandasと遜色なく扱える。

参考になる: Rustのデータフレームcrateのpolarsとpandasの比較
 

Queryを扱うという点ではarrowを使う事でデータの処理が行えるが、オンメモリでpythonのDataframeのようにとは少し違ってくる。
https://github.com/apache/arrow/tree/master/rustgithub.com


他にblack-jackというめっちゃかっこいい名前のクレートやrust-dataframeutahといったpandasを意識したクレートがあるが、開発は滞っている。
 

画像処理

image-rs配下のプロジェクトが一番の選択肢になると思う。
github.com
ImageBufferがfrom_vec, to_vecを持っているのでvectorとのやり取りも難しくない。特徴量抽出など、少し複雑な処理を行う場合はimageprocに実装されていってる感じなので、こちらを見ていくと良さそう。
GitHub - image-rs/imageproc: Image processing operations
 

前述のnalgebraを使う前提であれば、cgmathでGPUSIMD最適化などのオプションを付けて多くの線形な処理が行えるので、画像処理のみを目的にするならこちらも選択肢に入る。
github.com
近年の画像処理 * 機械学習観点だとここまで必要になる事は、そう多くはないのかなと思ったりもする。リゾルバを書いて何か解決したい場合とかだろうか。
 

次点でopencv-rsustというクレートがあるのだが、私は結局opencvへのリンクを上手くやってbuildして動かした所で力尽きて終わってしまった。OpenCVインストールバトルやcv::Matの扱いに慣れている場合は選択肢に入るかもしれない。ndarray-imageというndarrayで扱おうというrust-cvなるプロジェクトもあるが、開発が盛んとは言えない(AkazeやBrute forceみたいな古典的な画像処理アルゴリズムを熱心に実装しているのはrust-cv)。

形態素解析/tokenize

lindera になりそう。
github.com
mecabやneologdのような既存の資産も扱えるし、ピュアRustである点も含めて扱いやすい

linderaメンテナのブログ:Rust初心者がRust製の日本語形態素解析器の開発を引き継いでみた - Qiita
私も過去に使ったブログを書きサンプル実装を公開している:Rustによるlindera、neologd、fasttext、XGBoostを用いたテキスト分類 - Stimulator

その他には、sudachi.rsyoinawabi のような実装もあるがメンテは止まっている様子である。

 
英語のTokenzeであればPythonでもおなじみのhuggingfaceのtokenizersがRust実装なのでそのまま扱う事ができる。
github.com

 

- scikit-learn的なやつ -

scikit-learnみたいに色んなアルゴリズムが入ったクレートは有象無象にある。
大体どれも以下のアルゴリズムはサポートしている。

  • Linear Regression
  • Logistic Regression
  • K-Means Clustering
  • Neural Networks
  • Gaussian Process Regression
  • Support Vector Machines
  • Gaussian Mixture Models
  • Naive Bayes Classifiers
  • DBSCAN
  • k-Nearest Neighbor Classifiers
  • Principal Component Analysis
  • Decision Tree
  • Support Vector Machines
  • Naive Bayes
  • Elastic Net

各ライブラリと特徴比較

流石に全部使って見るという事ができていない上に、更新が止まっているものも多いので、最終commit日と一緒にリストアップする

  • rusty-machine (Star: 1.1k, updated: 2020/2/15)
    • 一番よく記事等を見るやつ。最終更新は1年前だが、アルゴリズムよりsrc/analysis配下のConfusion Matrix、Cross Varidation、Accuracy、F1 Score、MSEの実装が参考になるのでよく見に行く。iris等簡易データセットの読み込みもあるがデータだけなら下記のlinfa datasetが良いと思う。
    • Generalized Linear Modelを広くサポートしているのはちょっと特徴的
  • linfra (Star: 658, updated: 2021/1/21)
    • InterfaceもPythonっぽいし、完全にsklearn意識で開発も盛りあがっていてSponsorもいる。特にsklearnと違って各アルゴリズム毎にクレート化されてるのが嬉しい。
    • 上記以外にGaussian Mixture Model ClusteringやAgglomerative Hierarchical Clustering、Elastic Net、ICAをサポートしている
  • SmartCore (Star: 66, updated: 2021/1/22)
  • rustlearn (Star: 470, updated: 2020/6/21)
    • 古いライブラリだがPure Rustでinterfaceもわかりやすいので実装が参考になる
    • レコメンドでよく使うfactorization machinesや多くのmetric、k-fold cross-validation、shuffle splitのようにかなりピンポイントで重要なアルゴリズムを採用している

sckit-learnの立ち位置になっていきそうなのは、Sponsorもついているlinfaか開発者の勢いがあるsmartcoreというイメージ。
github.com
github.com

 

- Gradient Boosting -

主に使われているXGBoost、LightGBM、CatboostにRust bindingsが存在するが、現状modelのtrainまで出来るのはXGBoostとLightGBMのみ。

XGBoost

公式のXGBoostのC++実装をbindgenでbuildしてwrapした実装がある。
github.com
wrapperなので、PythonのXGBoostと同じイメージで使える。開発はほぼ止まっていて、submoduleとして使っているXGBoostのバージョンが少し古かったり、GPU等のSupportがないのがネックではある。

XGBoostで学習したデータを読み込んで推論できる、gbtree実装を使うという手もあるが、まだ上記のwrapperの方がとっつきやすいとは思う。
github.com
 

LightGBM

手前味噌ではあるが私がwrapperを書いている。上記のXGBoostと同様、C++実装をcmakeしてbindgenでbuildしてwrapしたもの。
github.com

parameter configをserde_jsonにしていたり入出力がvectorだったりして本当にこれで良いのか議論したいし、まだサポートしていないc_apiWindowsGPUを作る必要もあるので皆さんのcommitを待っている。

 

CatBoost

Catboostは公式に「一応」Rust bindingsが入っている。
以下のPRを見ての通り、適当なレビューはされておらずドキュメントはない…。
github.com
実装も怪しいが、私が手元で動かしてみた所、ライブラリへのリンクが上手く通っておらずそもそも動かなかった。

XGBoostやLightGBMと同様の方法でbindingsを作れば良いという話でもあるのだが、厄介な所として、Makefileではなくyandexの社内のモノリスで動いていたyamakeというビルドシステムを使ってビルドしている(普通にbuildすると謎のバイナリをダウンロードしてきて動かされる)。
それはやめようよというissueが立ち、catboost/makeディレクトリにMakefileが用意されるようになったが、このMakefileもyandex社内のArcadiaというシステムがジェネレートしたものでかなりヤンチャ(OSごとにファイルが分かれているにも関わらずドキュメントがそれに追い付いてなかったりもする)。
Ya script sources · Issue #131 · catboost/catboost · GitHub

masterが動かなかったのでコードを読む限りだが、現状は学習済みのバイナリを読み込んでpredictする形式しかサポートしていない。これはtrain周りのc_apiがwrapされていないからで、その部分を自前で書く必要もある。

一応私はここ最近Makefileとbuild.rsを作ることに挑戦していて、とりあえずmakeが通ってlibcatboostが生成される所まで来たが、心が折れるかもしれない(折れそうだったのでこの記事を書いて気を紛らわせている)。

 

- Deep Neaural Network -


現状はPytorch、TensorFlowの公式bindingsの2強の状態と言える。

系譜を以下の記事から引用する。

私の知る限りではRust界隈のディープラーニングフレームワーク事情は以下のとおりです。

primitiv-rustでディープラーニングする - Qiita

この記事で紹介されているprimitiv-rustdynet-rsも更新は止まっている。

Tensorflow/PyTorch

公式Bindingsが使える。

github.com

github.com

私はTensorFlow 2.xやKerasが入ってAPIがもう追えなくなったのでPyTorchを使ってるが、tch-rsは不満なく使えている。pretrained modelによる学習もすぐ始められるし、後述するようなBERT、Transformerの実装もあるのでほとんどPythonと遜色ない。GPUにも載る(私は試せていないが)らしい。
 

BERT

最近Transformerの実装の参考に触り始めたtch-rsをベースにしたrust-bertというクレートがあり、難しくなく扱える。
github.com

日本語のPretrainモデルをコンバートしてくる必要があるので、そこが少し面倒だが、みんな大好きHugging Faceのライブラリ群がかなり巻き取ってくれているので、Pythonで書いている時とあまり変わらない印象。
 

- Natural Language Processing -

TF-IDF

ほとんど簡易な実装だが、下記がある。
github.com

sckit-learnのようにTF-IDF Vectorizerみたいな使い方は、issueを見る限りあまりサポートされなさそうなので、その場合は自前で書けば良さそう。

fasttext

公式のFastTextの実装をbindgenでbuildしてwrapしたものがある。

github.com

私も過去に使ったブログを書きサンプル実装を公開している:Rustによるlindera、neologd、fasttext、XGBoostを用いたテキスト分類 - Stimulator
開発としては止まっていて、submoduleとして使っているFastTextのバージョンが遅れているのがネック。
 

- Recommendation -

Collaborative Filtering/Matrix Factorization

協調フィルタリングで多分一番実装がまともなものがquackinになる。もう4年更新がないが、実装自体がシンプルなのでフォークすれば良さそう。
github.com

他にもrecommenderが一応クレートとして見られるけど、ここまでなら自前で作っても良い。
より古典的なVowpalWabbitのRustバインディングも選択肢に入る。
github.com

 

Matrix Factorizationであれば、先述のrustlearnを使うのが良さそう。

non negative matrix factorizationの実装のクレートがあるが、試してみた所精度が出なかったので難しいところ。
GitHub - snd/onmf: fast rust implementation of online nonnegative matrix factorization as laid out in the paper "detect and track latent factors with online nonnegative matrix factorization"

- Information Retrieval -

Apache Solr入門や先の形態素解析のLinderaをメンテしているMinoru OSUKA(@mosuka)さんが作っているgRPCで通信して検索するbayardが全文検索エンジンとしての出来が良い。
github.com
(というよりまともに動くのはこれくらいな気がする)

作者の解説記事:Rust初心者がRustで全文検索サーバを作ってみた - Qiita
初心者が形態素解析器と全文検索エンジンを作っている。謎。
 

フロントでは、WebAssemblyでメモリ効率良く設計されたTinysearchがかなり気になる(使えていない)
github.com
フロントエンド側で検索できて、gzipにすると51KBしかないとか。
公式のブログがかなり参考になる:A Tiny, Static, Full-Text Search Engine using Rust and WebAssembly | Matthias Endler
作者はTrivagoの検索のバックエンドを作っている人らしい。

WebAssemblyでフロントで検索するのは熱く、StripeのengineerからもRustでwasmを介したStrokが出てる。
github.com
 

より一般的なものであれば、Elasticsearchが公式のクライアントとしてElasticsearch-rsを出しているので、裏側にESが既に立っているとかならこれで良さそう。
GitHub - elastic/elasticsearch-rs: Official Elasticsearch Rust Client

 

近傍探索ではFaissのc_apiをwrapしたRust bindingsがある。使い勝手はほぼPythonと同じ。

github.com

他の選択肢としては、HNSWやHNSW graphsをベースにしたgranneがあるので要検討。Pure Rustな所がかなり嬉しいと思う。
github.com

github.com

もうちょっと古典的なものだとVP木をつかったvpsearchがある。kd-treeの実装もあるのでkd-tree版も作れなくはなさそう。

- Reinforcement Learning -

強化学習であれば、rurelというクレートがあるが、流石に私も触りきれていない。
github.com

gymのRust bindings作ってる人もいる。すごい。まだ触れてないけど使えるかも。
github.com

 

おわりに

結局の所「wrapするだけならPython(ないしCython)が便利じゃない?」という所とどう折り合いつけるのかというのはある。

ただ、分散処理するほどではない大規模データ、型のないPythonでは扱いの難しいデータ、wasmによるフロントエンドでの高速処理など、今まで機械学習の社会実装で苦労していたニッチな所に刺さると思うので良いとも思う。
 

他にもこれオススメだよってやつあったら触りに行くので、はてブかツイートしてください。

 f:id:vaaaaaanquish:20210123234057p:plain:w0

【参考】

crates.ioでMachine Learning等で調べた。