shine-Notes

ゆるふわ思考ダンプ

Rust×WASMに入門する(Linderaでブラウザから形態素解析)

サマリ

  • 前から名前だけしか知らなかったWeb AssemblyをRustで動かしてみた
  • ついでにRust×NLP(ML)を覗き見したかったので、形態素解析エンジンを動かしてみた

背景

まとまった時間が有るときは(業務で全く使わない)Rustを少しずつ触っていこう、という活動を偶にやっている。 前はWebフレームワーク(Rocket)を触ってみたが、今回はRustの可能性を語る上でよく名前が出てくるWebAssemblyことWASMについて、実際どんなもんなのかを知りたかったので動かしてみた。かつ、普段はデータサイエンス周りを生息地にしているので、Rust×MLを色々探ってみて、今回は形態素解析エンジンを動かすことにした。

感想は最後に述べるとして、ざっとやったことの記録をまず載せる。

やったこと

WASMでHello World

まずはWebAssemblyを動かしてみる。これはMozilla公式ドキュメントが結構丁寧なチュートリアルを要ししてくれていた。ふだんRust慣れしていない人向けに書いているらしく、Rust自体の登場人物についても逐一説明してくれている。最終的に、index.htmlをロードするとアラートを表示するようなページが作成できる。

developer.mozilla.org

日本語でのやってみた記事も参考に。

dev.classmethod.jp

細かい手順については本エントリでは今更明記しない。ざっくり建て付けとしては、

  • wasm-packというcrateをcargo installしておけば、WASM用のバイナリをビルドできる。
  • WASMそのものはJavaScriptから呼び出せるバイナリーコード環境
  • Rustのお作法としては#[wasm_bindgen]内の関数を経由してJavaScript側の処理を呼び出したり渡したりする
  • $ wasm-pack build --target webでビルドすることで、.wasmファイルとそれを呼び出す.jsファイルを生成してくれる

こんな感じと理解した(厳密な所や細かな選択肢はいずれ理解していきたい)

Hello Lindera

次にWASMで動かすコードだが、Hello worldだけでは面白みがない。自分が多少は見知っている領域にしたかったので、形態素解析エンジン、具体的にはLinderaを動かすことにした。

github.com

PythonistaであればMecabとかJanomeが思い浮かぶと思うが(Mecabは厳密にはC++実装というのはさておき)、Rustにおける選択肢の一つがLinderaになる。構造や立ち位置の説明などはメンテナの方の記事がわかりやすい。

qiita.com

Linderaそのものを動かしたい場合は、上記にも記載の通りcargo intall lindera-cliで操作感を確認することが出来る。 (個人的にはSudachi.rsもTryしてみたかったのだが、今回は上手にWASMで呼び出すコードが書けなかったので、見送った)

LinderaをJavaScriptから呼ぶ

最後に、Hello worldで使ったコードを上手いこと書き換えながら、WASMでLinderaを呼び出すようなRust側のコードとJavaScriptを書いていく。出来上がりとしては↓のような感じで、入力した文章を形態素解析して画面に返してくれる。

f:id:shinebalance:20210815210119p:plain
Hello Lindera on WASM

大した内容はないが、コードは下記。

github.com

個人的なRust力&JavaScript力不足で時間は食ったが、やり方自体はわかってみれば簡単。

  • cargo.tomlにlinderaとlindera-coreを追加する
[dependencies]
lindera = "0.7.1"
lindera-core = "0.7.1"
wasm-bindgen = "0.2"
  • lib.rsTokenizerを使ってtokenizeした結果をJSValueで返す
use lindera::tokenizer::Tokenizer;
use lindera_core::core::viterbi::Mode;
use wasm_bindgen::prelude::*;


#[wasm_bindgen]
pub fn greet(text: &str) -> JsValue {

    // create tokenizer
    let mut tokenizer = Tokenizer::new(Mode::Normal, "");

    // tokenize the text
    let tokens = tokenizer.tokenize(&text);

    // JSに対応した戻り値を返すので
    JsValue::from_serde(&tokens).unwrap()

}
  • 1つポイントが有るとすると、#[wasm_bindgen]内で戻り値を返すときにJsValueを使うところだろうか。最初は前にRocketでJsonを返した時のノリでVec<String>unwrap()して返そうとしたりしていたのだがJS側で何も受け取れず、確かによくよく考えたらJavaScriptとのやり取りになるんだから専用のStructがあるような…と思ってググったらズバリの記事があった。感謝。

note.com

  • もろもろ完成したらindex.htmlで参照できる場所にバイナリとjsを置いてScriptで呼び出す。詳細はリポジトリを参照。

所感(考察)

  • という訳でやったこととしては以上なのだが、結構ここに至るまで色々調べたりして思ったことも有るのでざっと書いておく。

  • WebAssembly自体は幅広くマジョリティに、というよりは特定用途でポツポツいずれガッツリ、みたいに使われそうに感じた。

  • Rust × 形態素解析については、本稿の途中でも触れたがSudachi.rsにも注目してる。

  • ちなみに本記事に至るまでRust×MLについて調べてたが、実質は皆大好きばんくし氏がのblogをほーんほんほんと読むだけになった。RustってMLにどんくらい使えるのかなー、という話は氏のBlogと紹介されているエムスリーテックブログを読めば大体わかるのでおすすめ。 vaaaaaanquish.hatenablog.com

  • というかWASM×Rust×NLPに興味持ったのは氏の↓に大きく影響を受けてる。

WebAssemblyで機械学習Webアプリ「俺か俺以外か」をつくった - Stimulator

というわけでやったこととしては小さいが色々と勉強になった。そして今回Rustの事をかなり忘れていることも自覚したので、ちょくちょく関連書籍を読みつつまとまった時間が有れば何かまた作ってみたい。

以上