LoginSignup
0
0

More than 5 years have passed since last update.

JubatusのRustクライアントを書きました

Posted at

msgpack-rustmsgpack-rpc-rustを使ったライブラリの試作として、Jubatusクライアントを書いてみました。

Rustのバージョンは1.13.0です。

現時点ではClassifierとRecommenderとにしか対応していません。

リポジトリ

jubatus-rust-client

使い方

JubatusのドキュメントにあるClassifierの例をRust実装で試してみます。

他のクライアントの例と同様、事前にshogun.jsonを作成し、jubaclassifierでサーバを起動しておきます。

まずは cargo new でプロジェクト作成します。

$ cargo new --bin jubaclassify
$ cd jubaclassify

Cargo.tomlに以下を追加。rand crateはトレーニングデータをシャッフルするために使います。

Cargo.toml
[dependencies]
jubatus = { git = "https://github.com/hhatto/jubatus-rust-client.git" }
rand = "0.3"

少し長いですが、 src/main.rs は以下です。

main.rs
extern crate jubatus;
extern crate rand;

use jubatus::classifier::client::ClassifierClient;
use jubatus::classifier::types::LabeledDatum;
use jubatus::common::datum::Datum;
use rand::{thread_rng, Rng};


fn train(client: &mut ClassifierClient) {
    let mut train_data = vec![];
    for n in vec!["家康", "秀忠", "家光", "家綱", "綱吉", "家宣", "家継", "吉宗", "家重", "家治", "家斉", "家慶", "家定", "家茂"] {
        let mut d = Datum::new();
        d.add_string("name", n);
        train_data.push(LabeledDatum {
            label: "徳川".to_string(),
            data: d,
        });
    }
    for n in vec!["尊氏", "義詮", "義満", "義持", "義量", "義教", "義勝", "義政", "義尚", "義稙", "義澄", "義稙", "義晴", "義輝", "義栄"] {
        let mut d = Datum::new();
        d.add_string("name", n);
        train_data.push(LabeledDatum {
            label: "足利".to_string(),
            data: d,
        });
    }
    for n in vec!["時政", "義時", "泰時", "経時", "時頼", "長時", "政村", "時宗", "貞時", "師時", "宗宣", "煕時", "基時", "高時", "貞顕"] {
        let mut d = Datum::new();
        d.add_string("name", n);
        train_data.push(LabeledDatum {
            label: "北条".to_string(),
            data: d,
        });
    }

    // training data must be shuffled on online learning!
    let mut rng = thread_rng();
    rng.shuffle(&mut train_data);

    // run train
    client.train(train_data);
}

fn predict(client: &mut ClassifierClient) {
    // predict the last shogun
    for n in vec!["慶喜", "義昭", "守時"] {
        let mut d = Datum::new();
        d.add_string("name", n);
        let mut res = client.classify(vec![d]);
        // get the predicted shogun name
        let ref shogun_name = res[0].iter_mut().max_by_key(|x| (x.score * 100.) as i64).unwrap().label;
        println!("{} {}", shogun_name, n);
    }
}

fn main() {
    let host = "127.0.0.1:9199";
    let name = "test";
    let mut client = ClassifierClient::new(host, name);
    train(&mut client);
    predict(&mut client);
}

ビルドして実行してみます。

$ cargo build
$ cargo run
徳川 慶喜
足利 義昭
北条 守時

無事、例の通りに実行できました。

コード生成処理について

Jubatusクライアントはjeneratorと呼ばれるJubatusが提供するコードジェネレータを使用して生成されています。
jubatus-rust-clientはjubatus/jubatusをフォークしたhhatto/jubatusのsupport-rust-client-for-jeneratorブランチを使ってコード生成しています。

現状はjubatus-rust-client向けに生成するコードは100%自動生成では無く、
一部手で修正している箇所があります。今後100%自動生成にすべく改修する予定です。
(そもそも私がOCamlをほとんど書いたことないので、かなりゆっくりな対応になりそうですが。)

使用したcrateについて

RustでMessagePackを扱うcrateとしてはmneumann/rust-msgpackも存在しているのですが、1年近く変更が無くメンテナンスされてなさそうなのと、msgpack-rpc用のシリアライザ/デシリアライザが用意されてそうなのですが、通信部分が提供されていないので簡単に使えなさそうだったので、msgpack-rustmsgpack-rpc-rustの組み合わせを選びました。

その他改善点のまとめ

  • rmp::value::Value <-> Rust構造体 それぞれの変換をもう少しエレガントにできないか?
    • 今のところ対象の構造体に from_msgpack_valueto_msgpack_valueを生やしてそれを使って変換している
    • serde crateあたりでなんとかできそうなできなさそうな...
  • jenerator のコード(rust.ml)をもう少しエレガントにできないか?
    • 力技で書いたところが多い
    • HashMapや多段のVector(Vec<Vec<Struct>>みたいなの)をうまく処理できるようなコードを生成できていない
    • Struct.from_msgpack_value() 内でrmp::Value::Array.as_array()使って値を順番に取り出すのだが、インデックス番号が0固定になっている
  • テスト書く

最後に

jubatus-rust-client作ってみましたの共有と使い方の紹介でした。
Jubatusユーザのみなさん、Rustユーザのみなさん、是非とも使ってみてください。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0