Macで形態素解析ライブラリ「Vibrato」を動かす

初めに

より速い形態素解析ライブラリを探していて,Mecab(および高速化)や jaggerよりも速いと言われている vibratoを触ってみます。

Demo

本とカレーの街神保町へようこそ。形態素解析した場合,以下のようになります 。

プロジェクトは以下で公開しています

GitHub - ayutaz/hello-Vibrato-rust: VibratoのRust版を動かすテスト

開発環境

Rust ver

$ rustc --version
cargo --version
rustc 1.80.0 (051478957 2024-07-21)
cargo 1.80.0 (376290515 2024-07-16)

MacにRustが入っていない場合は,以下の記事で導入する方法を記載しているため参考にしてください

ayousanz.hatenadiary.jp

Vibrato専用の辞書ファイルをダウンロード

まずはVibrato専用の辞書ファイルをダウンロードします。今回は一番サイズが小さいipadic-mecab-2_7_0を使用します。

ダウンロードしたら,以下で解凍をします。

tar xvf ipadic-mecab-2_7_0.tar.xz

Rustのプロジェクトの作成

次にRustのプロジェクトの作成をします

cargo new hello-rust

解凍した辞書もこのフォルダ内に移動します.

このプロジェクトの Cargo.toml は以下のように記載します。

[package]
name = "hello-rust"
version = "0.1.0"
edition = "2021"

[dependencies]
vibrato = "0.5.0"
zstd = "0.12.3"

Vibratoを動かす

Vibratoを動かしていきます。

main.rsを以下のように記載します。

use std::fs::File;
use std::env;
use std::io::{self, Read};
use vibrato::{Dictionary, Tokenizer};

pub fn mecab(dict_path: &str) {
    // 辞書ファイルのロード
    let reader = zstd::Decoder::new(File::open(dict_path).unwrap()).unwrap();
    let dict = Dictionary::read(reader).unwrap();

    // トークナイザーの生成
    let tokenizer = Tokenizer::new(dict)
        .ignore_space(true).unwrap()
        .max_grouping_len(24);

    // ワーカーの生成。mutableです。
    let mut worker = tokenizer.new_worker();

    // 標準入力から文章を読み込む
    let mut text = String::new();
    io::stdin().read_to_string(&mut text).unwrap();

    // 文章をセット。繰り返したい場合は、これを再度呼び出し、ワーカーを使い回す。
    worker.reset_sentence(&text);
    worker.tokenize(); // 形態素解析の実行。mutable self

    println!("num_tokens: {}", worker.num_tokens());

    // 抽出したトークンをループで表示する
    worker.token_iter()
        .filter(|t| { // 絞り込み
            let words: Vec<&str> = t.feature().split(',').collect();
            let subwords: Vec<&str> = words[0].split('-').collect();
            subwords[0] == "名詞" || subwords[0] == "カスタム名詞"
        })
        .for_each(|t| { // 出力
            println!("{}: {}", t.surface(), t.feature());
        });
}

fn main() {
    let args: Vec<String> = env::args().collect();
    
    if args.len() < 3 || args[1] != "-i" {
        eprintln!("Usage: {} -i <dictionary_path>", args[0]);
        std::process::exit(1);
    }

    let dict_path = &args[2];
    
    // mecab関数を呼び出す
    mecab(dict_path);
}

以下のコマンドを実行することで処理を実行することができます

echo '本とカレーの街神保町へようこそ。' | cargo run --release -p hello-rust -- -i ipadic-mecab-2_7_0/system.dic.zst

結果は以下のようになります。

num_tokens: 10
本: 名詞,一般,*,*,*,*,本,ホン,ホン
カレー: 名詞,固有名詞,地域,一般,*,*,カレー,カレー,カレー
街: 名詞,一般,*,*,*,*,街,マチ,マチ
神保: 名詞,固有名詞,地域,一般,*,*,神保,ジンボウ,ジンボー
町: 名詞,接尾,地域,*,*,*,町,マチ,マチ