Lindera

License: MIT Crates.io

Rust製の形態素解析ライブラリです。Linderaは kuromoji-rs からフォークされ、複数言語のテキストトークナイズに対して、簡単なインストールと簡潔なAPIの提供を目指しています。

主な機能

機能説明
形態素解析Viterbiベースの分割と品詞タグ付け
多言語サポート日本語(IPADIC、IPADIC NEologd、UniDic)、韓国語(ko-dic)、中国語(CC-CEDICT、Jieba)
辞書システムビルド済み辞書、ユーザー辞書、カスタム辞書学習
テキスト処理パイプライン柔軟なテキスト正規化のための組み合わせ可能なキャラクターフィルターとトークンフィルター
CRF学習辞書コスト推定のためのカスタムCRFモデルの学習
PythonバインディングPyO3を介してPythonからLinderaを利用可能
WebAssemblywasm-bindgenを介してブラウザでLinderaを実行可能
Pure RustC/C++依存なし。Rustがサポートするあらゆるプラットフォームで動作

トークナイズの流れ

graph LR
    subgraph Your Application
        T["Text"]
    end
    subgraph Lindera
        CF["Character Filters"]
        SEG["Segmenter\n(Dictionary + Viterbi)"]
        TF["Token Filters"]
    end
    T --> CF --> SEG --> TF --> R["Tokens"]

ドキュメントマップ

セクション説明
はじめにインストール、クイックスタート、サンプル
辞書利用可能な辞書とその使い方
設定YAMLベースのトークナイザー設定
ユーザー辞書カスタムユーザー辞書の作成と利用
フィルターキャラクターフィルターとトークンフィルターのリファレンス
CRF学習辞書コスト推定モデルのカスタム学習
CLIコマンドラインインターフェースリファレンス
アーキテクチャクレート構成と設計の概要
APIリファレンスRust APIドキュメント
コントリビュートLinderaへの貢献方法

パフォーマンス

Linderaは日本語テキスト(IPADIC)を、シングルスレッドで概ね10〜20 MB/sの速度でトークナイズします(速度はハードウェアや、Viterbiラティスを呼び出しをまたいで再利用するかどうかに依存します)。ベンチマークスイート(lindera/benches/)を自分のマシンで再現するにはmake benchを実行してください。詳細な計測方法と比較データは#875を参照してください。

クイック例

use lindera::dictionary::load_dictionary;
use lindera::mode::Mode;
use lindera::segmenter::Segmenter;
use lindera_analysis::tokenizer::Tokenizer;
use lindera::LinderaResult;

fn main() -> LinderaResult<()> {
    let dictionary = load_dictionary("embedded://ipadic")?;
    let segmenter = Segmenter::new(Mode::Normal, dictionary, None);
    let tokenizer = Tokenizer::new(segmenter);

    let text = "関西国際空港限定トートバッグ";
    let mut tokens = tokenizer.tokenize(text)?;
    println!("text:\t{}", text);
    for token in tokens.iter_mut() {
        let details = token.details().join(",");
        println!("token:\t{}\t{}", token.surface.as_ref(), details);
    }

    Ok(())
}

上記の例は以下のように実行できます:

cargo run -p lindera-analysis --features=embed-ipadic --example=tokenize

実行結果は以下のようになります:

text:   関西国際空港限定トートバッグ
token:  関西国際空港    名詞,固有名詞,組織,*,*,*,関西国際空港,カンサイコクサイクウコウ,カンサイコクサイクーコー
token:  限定    名詞,サ変接続,*,*,*,*,限定,ゲンテイ,ゲンテイ
token:  トートバッグ    名詞,一般,*,*,*,*,*,*,*

ライセンス

Linderaは MITライセンス の下で公開されています。

アーキテクチャ

Linderaは複数のクレートで構成されるCargo workspaceとして構成されています。各クレートは、低レベルのCRF計算から高レベルのCLIや言語バインディングまで、それぞれ明確な責務を持っています。

クレート依存関係図

graph TB
    CRF["lindera-crf\n(CRF Engine)"]
    DICT["lindera-dictionary\n(Dictionary Base)"]
    TRAINER["lindera-trainer\n(CRF Training)"]
    IPADIC["lindera-ipadic"]
    UNIDIC["lindera-unidic"]
    SUDACHIDICT["lindera-sudachidict"]
    KODIC["lindera-ko-dic"]
    CCCEDICT["lindera-cc-cedict"]
    JIEBA["lindera-jieba"]
    NEOLOGD["lindera-ipadic-neologd"]
    LIB["lindera\n(Segmenter)"]
    ANALYSIS["lindera-analysis\n(Analysis Chain)"]
    CLI["lindera-cli\n(CLI)"]
    BINDINGCORE["lindera-binding-core"]
    PY["lindera-python"]
    NODEJS["lindera-nodejs"]
    RUBY["lindera-ruby"]
    PHP["lindera-php"]
    WASM["lindera-wasm"]

    CRF --> TRAINER
    DICT --> TRAINER
    TRAINER -.->|"train feature"| LIB
    DICT --> IPADIC
    DICT --> UNIDIC
    DICT --> SUDACHIDICT
    DICT --> KODIC
    DICT --> CCCEDICT
    DICT --> JIEBA
    DICT --> NEOLOGD
    DICT --> LIB
    DICT --> ANALYSIS
    DICT --> WASM
    IPADIC --> LIB
    UNIDIC --> LIB
    SUDACHIDICT --> LIB
    KODIC --> LIB
    CCCEDICT --> LIB
    JIEBA --> LIB
    NEOLOGD --> LIB
    LIB --> ANALYSIS
    LIB --> CLI
    ANALYSIS --> CLI
    LIB --> BINDINGCORE
    ANALYSIS --> BINDINGCORE
    BINDINGCORE --> PY
    BINDINGCORE --> NODEJS
    BINDINGCORE --> RUBY
    BINDINGCORE --> PHP
    BINDINGCORE --> WASM

クレート一覧

クレート種類説明
lindera-crfコアPure RustによるCRF(条件付き確率場)実装。no_stdサポート。シリアライゼーションにrkyvを使用。
lindera-dictionaryコア辞書ベースライブラリ。辞書の読み込みとビルドを提供。
lindera-trainerコアCRFベースの辞書学習パイプライン。lindera-crflindera-dictionaryの上に構築され、直接、またはlinderafacadeのtrain feature経由で利用される。
linderaコア純粋な形態素セグメンター。辞書クレートを統合し、Segmenter APIを提供。
lindera-analysisコアlinderaの上に構築されたLucene風の分析チェーン。文字フィルタ、トークンフィルタ、およびそれらをSegmenterの周りで組み合わせるTokenizerを提供。
lindera-cliアプリケーショントークナイズ、辞書ビルド、CRF学習のためのコマンドラインインターフェース。
lindera-binding-coreコア以下の5つの言語バインディングが共有するFFI非依存のヘルパー。
lindera-ipadic辞書IPADICベースの日本語辞書。
lindera-ipadic-neologd辞書IPADIC NEologdベースの日本語辞書(新語対応)。
lindera-unidic辞書UniDicベースの日本語辞書。
lindera-sudachidict辞書SudachiDictベースの日本語辞書。
lindera-ko-dic辞書ko-dicベースの韓国語辞書。
lindera-cc-cedict辞書CC-CEDICTベースの中国語辞書。
lindera-jieba辞書Jiebaベースの中国語辞書。
lindera-pythonバインディングPyO3を利用したPythonバインディング。
lindera-nodejsバインディングNAPI-RSを利用したNode.jsバインディング。
lindera-rubyバインディングMagnus + rb-sysを利用したRubyバインディング。
lindera-phpバインディングext-php-rsを利用したPHPバインディング。
lindera-wasmバインディングwasm-bindgenを利用したWebAssemblyバインディング。

トークナイズパイプライン

Linderaは複数段階のパイプラインでテキストを処理します:

Input Text
  |
  v
Character Filters    -- Normalize characters (e.g., Unicode normalization, mapping)
  |
  v
Segmenter            -- Segment text into tokens using a dictionary and the Viterbi algorithm
  |
  v
Token Filters        -- Transform tokens (e.g., POS filtering, stop words, stemming)
  |
  v
Output Tokens

Segmenterがコアコンポーネントです。辞書から候補トークンのラティスを構築し、Viterbiアルゴリズムを適用して最小コストのパスを見つけ、最も適切な分割結果を生成します。辞書検索は、所有権を持つ構造体へのデシリアライズを行わず、dict.trieのシリアライズ済みバイト列上を直接走査する文字単位のダブル配列トライ(crawdadでビルド)を使用します。繰り返しトークナイズを行う場合は、Segmenter::new_worker()が返すSegmentWorkerがラティスとスクラッチバッファの割り当てを呼び出しをまたいで再利用し、保持メモリを自動収縮ポリシーで制限します。

Featureフラグ

Feature説明デフォルト
mmapファイルシステム辞書読み込みのためのメモリマップドファイルサポート(--mmap/use_mmapでオプトイン。トライ・単語リストファイル・接続コスト行列はいずれもマップしたバイト列上で直接参照され、遅延読み込みされる)有効
trainCRFベースの辞書学習機能(lindera-crfに依存)CLI + Python/Node.js/Ruby/PHPバインディング(デフォルト有効)/linderaコアではデフォルト無効(オプトイン)/lindera-wasmでは利用不可
embed-ipadicIPADIC辞書をバイナリに埋め込み無効
embed-ipadic-neologdIPADIC NEologd辞書をバイナリに埋め込み無効
embed-unidicUniDic辞書をバイナリに埋め込み無効
embed-sudachidictSudachiDict辞書をバイナリに埋め込み無効
embed-ko-dicko-dic辞書をバイナリに埋め込み無効
embed-cc-cedictCC-CEDICT辞書をバイナリに埋め込み無効
embed-jiebaJieba辞書をバイナリに埋め込み無効
embed-cjkIPADIC + ko-dic + Jieba辞書を埋め込み無効
embed-cjk2UniDic + ko-dic + Jieba辞書を埋め込み無効
embed-cjk3IPADIC NEologd + ko-dic + Jieba辞書を埋め込み無効
embed-cjk4SudachiDict + ko-dic + Jieba辞書を埋め込み無効

詳細

はじめに

このセクションでは、Linderaのインストールから最初の形態素解析の実行までをガイドします。

  • インストール -- プロジェクトへのLinderaの追加と環境変数の設定
  • クイックスタート -- わずか数行のコードで最初のテキストをトークナイズ
  • サンプル -- 一般的なユースケースのサンプルプログラムを探索

インストール

動作要件

Rust 1.88 以降。ワークスペースが rust-version として宣言しているため、 古いツールチェインではコンパイルエラーではなく cargo のエラーとして報告されます。

依存関係の追加

Cargo.tomlに以下を追加してください:

[dependencies]
lindera = "5"

[!NOTE] v4 からアップグレードする場合はv4からv5への移行を参照してください。

辞書のセットアップ

Linderaの実行にはビルド済み辞書が必要です。GitHub Releases から辞書をダウンロードし、読み込み時にそのパスを指定してください:

#![allow(unused)]
fn main() {
let dictionary = load_dictionary("/path/to/ipadic")?;
}

[!TIP] 辞書をバイナリに直接埋め込みたい場合(上級者向け)は、対応する embed-* feature フラグを有効にしてビルドし、embedded:// スキームでロードしてください:

#![allow(unused)]
fn main() {
// Cargo.toml: lindera = { version = "5", features = ["embed-ipadic"] }
let dictionary = load_dictionary("embedded://ipadic")?;
}

詳細は Feature フラグ を参照してください。

環境変数

LINDERA_BUILD_DICTIONARY_CACHE_DIR

LINDERA_BUILD_DICTIONARY_CACHE_DIR 環境変数は、埋め込み辞書ビルドパイプラインのビルド時キャッシュディレクトリを指定します。辞書クレートの build script のみが読み取り、実行時の動作には影響しません。

設定すると、各ビルドは $LINDERA_BUILD_DICTIONARY_CACHE_DIR/<version>-fmt<format>/<version> は辞書クレートのバージョン、<format> は辞書フォーマットバージョン)配下に 2 種類のファイルを保存します:

  • ダウンロードした配布アーカイブ(MD5 で検証。無効なファイルは自動的に再ダウンロード)
  • クレートに埋め込まれるビルド済みバイナリ辞書

フォーマットバージョンをパスに含めているのは、オンディスクレイアウトが異なるビルドが書いたキャッシュを「古いまま再利用」ではなく「キャッシュミス」にするためです。再利用の直前にはキャッシュディレクトリ内の記録済みフォーマットバージョンも再確認するので、中断されたビルドが残した不完全なディレクトリも弾かれます。古いフォーマットバージョンのディレクトリは自動削除されないため、キャッシュが肥大化する場合は手動で削除してください。

これにより以下のメリットがあります:

  • オフラインビルド: 一度キャッシュされれば、以降のビルドにネットワークアクセスは不要です
  • ビルドの高速化: 有効なキャッシュがあればダウンロードと辞書ビルドがスキップされます
  • 再現可能なビルド: ビルド間での辞書バージョンの一貫性を保ちます

使用方法:

export LINDERA_BUILD_DICTIONARY_CACHE_DIR=/path/to/cache
cargo build --features=embed-ipadic

注意点:

  • このディレクトリは自動管理されており、削除しても安全です(必要に応じて再ダウンロード・再ビルドされます)
  • バージョンごとのサブディレクトリはアップグレードのたびに蓄積され、自動削除されません。古いものは自由に削除できます
  • この変数を設定すると、embed-* feature が無効でも辞書クレートはダウンロードとビルドを実行します(キャッシュの事前準備に便利です)

非推奨: 旧名 LINDERA_DICTIONARIES_PATH はフォールバックとして引き続き動作しますが(両方設定時は新名が優先)、v6.0.0 で削除されます。

LINDERA_CONFIG_PATH

LINDERA_CONFIG_PATH 環境変数は、トークナイザーの設定ファイル(YAML形式)へのパスを指定します。これにより、Rustコードを変更せずにトークナイザーの動作を設定できます。

export LINDERA_CONFIG_PATH=./resources/config/lindera.yml

設定フォーマットの詳細は 設定 セクションを参照してください。

DOCS_RS

DOCS_RS 環境変数は、docs.rsでドキュメントをビルドする際に自動的に設定されます。この変数が検出されると、Linderaは実際の辞書データをダウンロードする代わりにダミーの辞書ファイルを作成します。これにより、ネットワークアクセスや大容量ファイルのダウンロードなしでドキュメントをビルドできます。

これは主にdocs.rs内部で使用されるものであり、通常ユーザーが設定する必要はありません。

LINDERA_WORKDIR

LINDERA_WORKDIR 環境変数は、ビルドプロセス中に lindera-dictionary クレートによって自動的に設定されます。これはビルドされた辞書データファイルを含むディレクトリを指し、辞書クレートがデータファイルの場所を特定するために内部で使用されます。

この変数は自動的に設定されるため、ユーザーが変更する必要はありません。

クイックスタート

この例では、Linderaの基本的な使い方を説明します。

以下の処理を行います:

  • Normalモードでセグメンターを作成
  • 入力テキストを分割(形態素解析)
  • トークンを出力

この例では embed-ipadic feature を使用します。この feature はビルド時にIPADIC辞書を自動的にダウンロードしてバイナリに埋め込むため、辞書を手動でダウンロードする必要はありません。

use std::borrow::Cow;

use lindera::dictionary::load_dictionary;
use lindera::mode::Mode;
use lindera::segmenter::Segmenter;
use lindera::LinderaResult;

fn main() -> LinderaResult<()> {
    let dictionary = load_dictionary("embedded://ipadic")?;
    let segmenter = Segmenter::new(Mode::Normal, dictionary, None);

    let text = "関西国際空港限定トートバッグ";
    let mut tokens = segmenter.segment(Cow::Borrowed(text))?;
    println!("text:\t{}", text);
    for token in tokens.iter_mut() {
        let details = token.details().join(",");
        println!("token:\t{}\t{}", token.surface.as_ref(), details);
    }

    Ok(())
}

上記の例は以下のように実行できます:

% cargo run --features embed-ipadic --example=segment

[!TIP] 辞書をバイナリに埋め込みたくない場合は、GitHub Releases からビルド済みIPADIC辞書をダウンロードしてローカルディレクトリ(例: /path/to/ipadic)に展開し、代わりに load_dictionary("/path/to/ipadic") を呼び出してください。この場合、embed-ipadic feature は不要です。詳細は Feature フラグ を参照してください。

実行結果は以下のようになります:

text:   関西国際空港限定トートバッグ
token:  関西国際空港    名詞,固有名詞,組織,*,*,*,関西国際空港,カンサイコクサイクウコウ,カンサイコクサイクーコー
token:  限定    名詞,サ変接続,*,*,*,*,限定,ゲンテイ,ゲンテイ
token:  トートバッグ    名詞,一般,*,*,*,*,*,*,*

[!NOTE] character filter・token filter・Tokenizer API は独立クレート lindera-analysis が提供します(v5.0 以降)。分析チェーンが必要な場合は 依存定義に lindera-analysis = "5" を追加してください。

サンプル

Linderaには、一般的なユースケースを示すいくつかのサンプルプログラムが含まれています。ソースコードはGitHubの examplesディレクトリ で確認できます。

以下のサンプルはすべて embed-ipadic feature を有効にして実行します。この feature はビルド時にIPADIC辞書を自動的にダウンロードしてバイナリに埋め込むため、辞書を手動でダウンロードする必要はありません。

利用可能なサンプル

segment

Segmenter API による基本的な形態素分割です。lindera クレート単体で動作します。

cargo run -p lindera --features=embed-ipadic --example=segment

tokenize

外部IPADIC辞書を使用した基本的なトークナイズです。入力テキストを分割し、各トークンの品詞情報を表示します。

cargo run -p lindera-analysis --features=embed-ipadic --example=tokenize

tokenize_with_user_dict

ユーザー辞書を使用したトークナイズです。ドメイン固有の用語のために、辞書をカスタムエントリで補完する方法を示します。

cargo run -p lindera-analysis --features=embed-ipadic --example=tokenize_with_user_dict

tokenize_with_filters

キャラクターフィルターとトークンフィルターを使用したトークナイズです。Unicode正規化、品詞フィルタリングなどの変換を含むテキスト処理パイプラインを実演します。

cargo run -p lindera-analysis --features=embed-ipadic --example=tokenize_with_filters

tokenize_with_config

YAML設定ファイルを使用したトークナイズです。プログラムではなく宣言的にトークナイザーを設定する方法を示します。

cargo run -p lindera-analysis --features=embed-ipadic --example=tokenize_with_config

基本概念

このセクションでは、Linderaの形態素解析システムの基本的な概念について説明します。

  • 形態素解析 - Linderaがテキストを分割・解析する仕組み。
  • 辞書 - Linderaがサポートする辞書フォーマット。
  • トークナイズ - トークナイズモードとN-Best解析。
  • ユーザー辞書 - ユーザー辞書によるカスタム単語の追加。
  • フィルタ - 文字フィルタによるテキスト前処理とトークンフィルタによるトークン後処理。

形態素解析

形態素解析とは

形態素解析とは、テキストを最小の意味単位(形態素)に分割し、その文法的特性を同定する処理です。日本語、中国語、韓国語のように単語がスペースで区切られない言語では、形態素解析は検索インデックス作成、テキスト分類、機械翻訳などの自然言語処理タスクにおいて不可欠な最初のステップです。

Linderaの仕組み

Linderaは辞書ベースの形態素解析器です。既知の単語とそのコストを含む事前コンパイル済みのシステム辞書を使用し、Viterbiアルゴリズムを適用して入力テキストの最適な分割を見つけます。

解析処理は以下のように動作します:

  1. 文分割: ラティスを構築する前に、Linderaは入力テキストを区切り文字(\n\t)で文単位に分割し、1文ずつ処理します。文の先頭からおよそ 32 KiB 以内に区切り文字が見つからない場合、Linderaはその位置で強制的に文の境界を区切り、警告をログに出力します。これはラティスのメモリ・CPU コストを制限するためです。区切り文字を含まない病的な入力(minify されたテキストや base64 エンコードされたデータなど)に対しては、この人為的な分割位置でトークナイズ結果に影響が出ることがあります。
  2. ラティス構築: Linderaは各文をスキャンし、各位置で辞書内のすべての候補単語を検索して、候補分割の有向非巡回グラフ(ラティス)を構築します。
  3. コスト割り当て: 各候補単語には(辞書からの)単語コストが関連付けられており、隣接する単語のペアには(連接コスト行列からの)連接コストが関連付けられています。
  4. 最適パス探索: Viterbiアルゴリズムがラティスを通る最小総コストのパスを見つけ、最適な分割結果を生成します。

主な用語

用語説明
表層形入力テキストに実際に現れるテキスト(例:"食べ")。
品詞(POS)単語の文法的カテゴリ(例:名詞、動詞、助詞)。Linderaの辞書は最大4階層のサブカテゴリを持つ階層的な品詞タグを提供します。
読み単語の発音。日本語辞書では通常カタカナで表記されます。
原形単語の非活用形(辞書形)(例:表層形"食べ"に対して"食べる")。
活用活用する単語の語形変化情報。活用型と活用形で構成されます。

コストベースの分割

Viterbiアルゴリズムは総コストが最小となる分割パスを選択します。パスの総コストは以下の合計です:

  • 単語コスト: 辞書内の各単語に関連付けられたコスト。コストが低いほど、その単語が出現する可能性が高いことを意味します。一般的な単語はコストが低く、まれな単語はコストが高い傾向があります。
  • 連接コスト: 隣接する2つの単語を接続するコスト。左側の単語の右文脈IDと右側の単語の左文脈IDによって決定されます。

アルゴリズムは以下を計算します:

Total cost = sum of word costs + sum of connection costs

この総コストを最小化することで、Linderaは入力テキストの最も自然な分割を見つけます。

連接コスト行列

連接コスト行列は、ある単語から別の単語への遷移コストを格納します。以下でインデックスされる2次元行列です:

  • 先行する単語の右文脈ID
  • 後続する単語の左文脈ID

これらの文脈IDは、単語境界に関する文法的情報をエンコードしています。例えば、名詞と助詞の間の連接コストは通常低く(自然な並び)、2つの動詞の基本形の間の連接コストは高くなります(不自然な並び)。

連接コスト行列は辞書ビルドプロセスの一部としてバイナリ形式にコンパイルされ、実行時に効率的な参照のために読み込まれます。

辞書

Linderaは、日本語・韓国語・中国語の形態素解析のための様々な辞書をサポートしています。各辞書は個別のクレートとして提供されます。

辞書言語クレート説明
IPADIC日本語lindera-ipadic日本語で最も一般的な辞書
IPADIC NEologd日本語lindera-ipadic-neologd新語に対応したIPADIC
UniDic日本語lindera-unidic均一な単語単位定義を持つ辞書
SudachiDict日本語lindera-sudachidict活発にメンテナンスされている語彙(上流で年数回更新)
ko-dic韓国語lindera-ko-dic韓国語の形態素解析
CC-CEDICT中国語lindera-cc-cedict中英辞書
Jieba中国語lindera-jiebaJiebaベースの中国語辞書

辞書の入手方法

ビルド済み辞書は GitHub Releases からダウンロードできます。対象言語の辞書アーカイブをダウンロードし、ローカルディレクトリに展開してください。

#![allow(unused)]
fn main() {
// ローカルパスから外部辞書を読み込む
let dictionary = load_dictionary("/path/to/ipadic")?;
}

[!TIP] 外部辞書ファイルなしの自己完結型バイナリが必要な場合は、embed-* feature フラグを使って辞書を埋め込み、embedded:// スキームでロードできます:

#![allow(unused)]
fn main() {
let dictionary = load_dictionary("embedded://ipadic")?;
}

詳細は Feature フラグ を参照してください。

各辞書クレートのドキュメントで、フォーマット詳細、ビルド手順、使用例を参照してください。

トークナイズ

Linderaは複数のトークナイズモードを提供し、代替の分割候補を列挙するためのN-Best解析をサポートしています。

トークナイズモード

Normalモード

Normalモードは辞書エントリに基づく標準的なトークナイズを実行します。辞書に単一エントリとして存在する複合語はそのまま保持されます。

-- "関西国際空港限定トートバッグ" をNormalモードでトークナイズ:

関西国際空港 | 限定 | トートバッグ

複合名詞 "関西国際空港"(Kansai International Airport)は辞書に1つのエントリとして存在するため、単一トークンとして保持されます。

Decomposeモード

Decomposeモードは、複合語が辞書エントリとして存在する場合でも、さらに構成要素に分解します。

-- "関西国際空港限定トートバッグ" をDecomposeモードでトークナイズ:

関西 | 国際 | 空港 | 限定 | トートバッグ

複合語 "関西国際空港" は "関西"、"国際"、"空港" に分解されます。

モードの選択

Rustでは、Segmenterの作成時にモードを指定します:

#![allow(unused)]
fn main() {
use lindera::mode::Mode;
use lindera::segmenter::Segmenter;
use lindera::dictionary::load_dictionary;

let dictionary = load_dictionary("embedded://ipadic")?;

// Normal mode
let segmenter = Segmenter::new(Mode::Normal, dictionary, None);

// Decompose mode
let segmenter = Segmenter::new(Mode::Decompose(Default::default()), dictionary, None);
}

CLIでは、--modeフラグを使用します:

echo "関西国際空港限定トートバッグ" | lindera tokenize --dict embedded://ipadic --mode normal
echo "関西国際空港限定トートバッグ" | lindera tokenize --dict embedded://ipadic --mode decompose

[!NOTE] embedded:// スキームを使用するには、lindera-cli を対応する embed-* feature 付きでビルドする必要があります(例: cargo install lindera-cli --features=embed-ipadic)。デフォルトビルドの lindera-cli はどの embed-* feature も有効化していないため、 embed-* feature なしで --dict embedded://ipadic を指定すると Invalid dictionary scheme: embedded エラーになります。

N-Bestトークナイズ

N-Bestトークナイズは、総パスコスト順(低コスト = より良い分割)に上位N件のトークナイズ候補を列挙します。最良の結果が曖昧な場合や、入力テキストの代替解釈を探索したい場合に有用です。

アルゴリズム

N-Bestトークナイズは**Forward-DP Backward-A***アルゴリズムに基づいており、MeCabのN-Best実装と互換性があります。フォワードパスは動的計画法で最適コストを計算し、バックワードパスはA*探索を使用して総コストの昇順にパスを列挙します。

パラメータ

tokenize_nbestメソッドは以下のパラメータを受け付けます:

パラメータ説明
text&strトークナイズするテキスト。
nusize返すN-best結果の数。
uniquebooltrueの場合、同じ単語境界位置を生成する結果を重複排除します。
cost_thresholdOption<i64>Some(threshold)の場合、best_cost + threshold以内のコストのパスのみを返します。

Rust APIの例

use lindera::dictionary::load_dictionary;
use lindera::mode::Mode;
use lindera::segmenter::Segmenter;
use lindera_analysis::tokenizer::Tokenizer;
use lindera::LinderaResult;

fn main() -> LinderaResult<()> {
    let dictionary = load_dictionary("embedded://ipadic")?;
    let segmenter = Segmenter::new(Mode::Normal, dictionary, None);
    let tokenizer = Tokenizer::new(segmenter);

    let text = "すもももももももものうち";

    // Get top 3 tokenization results
    let results = tokenizer.tokenize_nbest(text, 3, false, None)?;

    for (rank, (tokens, cost)) in results.iter().enumerate() {
        println!("--- NBEST {} (cost={}) ---", rank + 1, cost);
        for token in tokens {
            let details = token.details().join(",");
            println!("{}\t{}", token.surface.as_ref(), details);
        }
    }

    Ok(())
}

実行結果は以下のようになります:

--- NBEST 1 (cost=21245) ---
すもも  名詞,一般,*,*,*,*,すもも,スモモ,スモモ
も      助詞,係助詞,*,*,*,*,も,モ,モ
もも    名詞,一般,*,*,*,*,もも,モモ,モモ
も      助詞,係助詞,*,*,*,*,も,モ,モ
もも    名詞,一般,*,*,*,*,もも,モモ,モモ
の      助詞,連体化,*,*,*,*,の,ノ,ノ
うち    名詞,非自立,副詞可能,*,*,*,うち,ウチ,ウチ
--- NBEST 2 (cost=24541) ---
...

CLIの例

echo "すもももももももものうち" | lindera tokenize --dict embedded://ipadic -N 3

Latticeの再利用

繰り返しトークナイズを行う場合、Latticeを再利用してメモリ割り当てを削減できます:

#![allow(unused)]
fn main() {
use lindera_dictionary::viterbi::Lattice;

let mut lattice = Lattice::default();
let results = tokenizer.tokenize_nbest_with_lattice(text, &mut lattice, 3, false, None)?;
}

セグメンターのレベルでは、SegmentWorkerSegmenter::new_worker で作成)がこのパターンをラティスとスクラッチバッファを所有するセッションオブジェクトとしてまとめており、さらに自動縮小ポリシーで保持メモリも制限します。詳細は Segmenter のページを参照してください。

ユーザー辞書

ユーザー辞書は、システム辞書と併用してカスタム単語を登録できる補助辞書です。ドメイン固有の用語、ブランド名、固有名詞、またはデフォルトのシステム辞書に含まれていない単語を登録する場合に有用です。

CSVフォーマット

最もシンプルなユーザー辞書フォーマットは、3つのカラムを持つCSVファイルです:

<surface>,<part_of_speech>,<reading>

CSV内容の例

東京スカイツリー,カスタム名詞,トウキョウスカイツリー
東武スカイツリーライン,カスタム名詞,トウブスカイツリーライン
とうきょうスカイツリー駅,カスタム名詞,トウキョウスカイツリーエキ

各辞書タイプ(IPADIC、UniDic、ko-dicなど)は、文脈ID、コスト、すべての素性フィールドを完全に制御できる詳細CSVフォーマットもサポートしています。各辞書タイプの詳細フォーマットについては 辞書 セクションを参照してください。

Rust APIの例

use std::fs::File;
use std::path::PathBuf;

use lindera::dictionary::{Metadata, load_dictionary, load_user_dictionary};
use lindera::error::LinderaErrorKind;
use lindera::mode::Mode;
use lindera::segmenter::Segmenter;
use lindera_analysis::tokenizer::Tokenizer;
use lindera::LinderaResult;

fn main() -> LinderaResult<()> {
    let user_dict_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
        .join("../resources")
        .join("user_dict")
        .join("ipadic_simple_userdic.csv");

    let metadata_file = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
        .join("../lindera-ipadic")
        .join("metadata.json");
    let metadata: Metadata = serde_json::from_reader(
        File::open(metadata_file)
            .map_err(|err| LinderaErrorKind::Io.with_error(anyhow::anyhow!(err)))
            .unwrap(),
    )
    .map_err(|err| LinderaErrorKind::Io.with_error(anyhow::anyhow!(err)))
    .unwrap();

    let dictionary = load_dictionary("embedded://ipadic")?;
    let user_dictionary = load_user_dictionary(user_dict_path.to_str().unwrap(), &metadata)?;
    let segmenter = Segmenter::new(
        Mode::Normal,
        dictionary,
        Some(user_dictionary), // Using the loaded user dictionary
    );

    // Create a tokenizer.
    let tokenizer = Tokenizer::new(segmenter);

    // Tokenize a text.
    let text = "東京スカイツリーの最寄り駅はとうきょうスカイツリー駅です";
    let mut tokens = tokenizer.tokenize(text)?;

    // Print the text and tokens.
    println!("text:\t{}", text);
    for token in tokens.iter_mut() {
        let details = token.details().join(",");
        println!("token:\t{}\t{}", token.surface.as_ref(), details);
    }

    Ok(())
}

実行結果は以下のようになります:

text:   東京スカイツリーの最寄り駅はとうきょうスカイツリー駅です
token:  東京スカイツリー        カスタム名詞,*,*,*,*,*,*,トウキョウスカイツリー,*
token:  の      助詞,連体化,*,*,*,*,の,ノ,ノ
token:  最寄り駅        名詞,一般,*,*,*,*,最寄り駅,モヨリエキ,モヨリエキ
token:  は      助詞,係助詞,*,*,*,*,は,ハ,ワ
token:  とうきょうスカイツリー駅        カスタム名詞,*,*,*,*,*,*,トウキョウスカイツリーエキ,*
token:  です    助動詞,*,*,*,特殊・デス,基本形,です,デス,デス

base_formフィールド(CSV/詳細情報の7番目のフィールド)が表層形ではなく*になっている点に注意してください。 シンプルな3カラムのユーザー辞書スキーマでは surfacepart_of_speechreading の3つしか指定できず、 base_form を含むそれ以外のフィールドはすべて辞書の metadata.default_field_value (IPADICの場合は*)で埋められます。

CLIでのユーザー辞書ビルド

CLIを使用して、CSVからバイナリ形式のユーザー辞書をビルドできます:

lindera build --src <source_dir> --dest <dest_dir> --metadata <metadata.json> --user

バイナリとCSVのユーザー辞書

  • CSVフォーマット: 実行時に読み込み・パースされます。開発時や小規模な辞書に便利です。
  • バイナリフォーマット: 高速な読み込みのために事前コンパイルされます。大規模なユーザー辞書を本番環境で使用する場合に推奨されます。

どちらのフォーマットもSegmenterの作成時に指定できます。バイナリフォーマットはCSVのパースステップをスキップするため、起動時間が短縮されます。

フィルタ

Linderaの解析パイプライン(lindera-analysisクレートが提供するTokenizer)には、Segmenterの前後に2つの拡張ポイントがあります。トークナイズの生テキストを変換する文字フィルタと、トークナイズのトークン列を変換するトークンフィルタです。

Input Text
  --> Character Filters (preprocessing)
  --> Tokenization
  --> Token Filters (postprocessing)
  --> Output Tokens

文字フィルタ

文字フィルタは、入力テキストがSegmenterに渡される前に前処理を行います。全角文字を半角に変換する、Unicode表現を正準化する、日本語の踊り字を展開形に変換するなど、トークナイズの一貫性を高めるためのテキスト正規化に主に使われます。文字フィルタはテキストの長さを変えることがあるため、Linderaはすべての変換を記録し、各トークンのバイトオフセットを元の(フィルタ適用前の)テキストにおける位置へ補正します。

トークンフィルタ

トークンフィルタは、Segmenterが生成したトークン列に対して後処理を行います。トークンを基本形(辞書形)に置き換える、トークンの表層形をひらがな・カタカナ間で変換する、品詞タグでトークンを除去する、ストップワードを除去するなど、検索や解析のためにトークン列を正規化・削減する目的で主に使われます。

フィルタの設定方法

文字フィルタ・トークンフィルタのいずれも、kind文字列で識別され、フィルタ固有のパラメータを持つJSONのargsオブジェクトで設定します。フィルタは追加された順序で実行され、追加方法は2通りあります。

  • YAML設定ファイル: character_filtersキーとtoken_filtersキーの下にフィルタを列挙する。

    character_filters:
      - kind: unicode_normalize
        args:
          kind: nfkc
    
    token_filters:
      - kind: japanese_base_form
    
  • Rust API: append_character_filterappend_token_filterTokenizerに順番にフィルタを追加する。

    #![allow(unused)]
    fn main() {
    // Character filters run first, transforming the raw input text;
    // token filters run last, transforming the resulting token list.
    tokenizer
        .append_character_filter(BoxCharacterFilter::from(unicode_normalize_char_filter))
        .append_token_filter(BoxTokenFilter::from(japanese_base_form_filter));
    }

利用可能なフィルタ

Linderaは4種類の文字フィルタと18種類のトークンフィルタを提供しており、日本語・韓国語・汎用的なテキスト正規化をカバーしています。

カテゴリフィルタ
文字フィルタunicode_normalize, japanese_iteration_mark, mapping, regex
トークンフィルタ -- 正規化japanese_base_form, japanese_reading_form, korean_reading_form, japanese_kana, japanese_katakana_stem, japanese_number, mapping, remove_diacritical_mark, lowercase, uppercase
トークンフィルタ -- 品詞によるフィルタリングjapanese_keep_tags, japanese_stop_tags, korean_keep_tags, korean_stop_tags
トークンフィルタ -- 単語によるフィルタリングkeep_words, stop_words
トークンフィルタ -- 構造の変換japanese_compound_word, length

各フィルタの説明・パラメータ・実行可能なYAML/Rust APIの例はフィルターリファレンスを参照してください。

Lindera CRF

Lindera CRFは、rucrfからフォークされたConditional Random Fields(CRF)のpure Rust実装です。ラティス構造をサポートしたCRFの学習器と推定器を提供します。

主な特徴

  • 可変長エッジを持つラティス構造
  • L1、L2、およびElastic Net正則化
  • マルチスレッド学習
  • rkyvによるゼロコピーデシリアライゼーション
  • --no-default-features --features allocによるno_stdサポート

目次

rucrfからの変更点

  • シリアライゼーションバックエンド: ゼロコピーデシリアライゼーションのため、bincodeからrkyvに変更
  • Elastic Net正則化: L1とL2のペナルティを組み合わせたRegularization::ElasticNetを追加
  • Rust 2024 edition: Rust 2024 editionに更新
  • 依存クレートの更新: argminargmin-mathhashbrownなどを更新

アーキテクチャ

モジュール構成

lindera-crf/src/
├── lib.rs                # パブリックAPIの再エクスポート
├── feature.rs            # FeatureSet, FeatureProvider
├── lattice.rs            # Edge, Node, Lattice
├── model.rs              # RawModel, MergedModel, Model trait
├── trainer.rs            # Trainer, Regularization enum
├── errors.rs             # エラー型
├── forward_backward.rs   # 前向き・後向きアルゴリズム
├── math.rs               # 数学ユーティリティ (logsumexp)
├── optimizers.rs         # optimizersモジュールの宣言
├── optimizers/
│   └── lbfgs.rs          # L-BFGS最適化
└── utils.rs              # ユーティリティtrait

主要コンポーネント

FeatureProvider / FeatureSet

ラベルごとの素性セットを管理します。各FeatureSetは、指定されたラベルのユニグラム素性と左右のバイグラム素性を保持します(保持するのは素性IDのみで、重みは持ちません)。FeatureProviderはラベルIDをFeatureSetインスタンスにマッピングします。重みはRawModel側で別途保持されます(weightsunigram_weight_indicesbigram_weight_indices)。

Lattice / Edge / Node

系列ラベリングのための可変長エッジを持つラティス構造です。Edgeはラベル付きの候補スパンを表し、Nodeは特定の位置にあるエッジを集約します。Latticeは入力データから構築され、モデルが最適パスを探索するために使用されます。

Trainer

設定可能な正則化を用いたL-BFGS最適化によりCRFモデルを学習します。Trainerはラベル付きラティスの例を受け取り、前向き・後向きアルゴリズムで勾配を計算し、反復的にモデルの重みを更新します。

Regularization

設定可能な正則化戦略:

  • L1: L1ペナルティによるスパースモデル
  • L2: L2ペナルティによる滑らかなモデル
  • ElasticNet: L1とL2を設定可能なl1_ratioで組み合わせ

Model (trait)

ラティスを通じて最適パスを探索するためのインターフェースです。2つの実装が提供されています:

  • RawModel: 素性IDでインデックスされたフラットベクトルに重みを格納
  • MergedModel: 推論に最適化され、素性の重みをrkyvでシリアライズ可能なコンパクトな表現にマージ。ラベルごとの要素型としてMergedFeatureSetを使用

MergedFeatureSetは、ラベルの事前合計済みユニグラムweightと、MergedModelのバイグラム重み行列を参照するleft_id/right_id接続IDを保持します。

前向き・後向きアルゴリズム

ラティス上でアルファ(前向き)とベータ(後向き)の値を計算します。学習時に期待素性カウントと勾配の計算に使用されます。

Feature フラグ

Feature説明デフォルト
allocno_std向けのallocサポートNo*
std標準ライブラリサポート(allocを含む)No*
train学習機能(L-BFGS、マルチスレッド、ログ出力)Yes

* defaultに直接列挙されているわけではありませんが、デフォルトで有効なtrainstdを有効化し、stdがさらにallocを有効化するため、実際にはデフォルトビルドでallocstdは有効になっています。

APIリファレンス

APIリファレンスは以下で公開されています:

Lindera Dictionary

Lindera Dictionaryは、形態素解析辞書のベースライブラリです。辞書の読み込み、ビルド、Viterbiベースのセグメンテーションを提供します。CRFベースの辞書学習機能は、別クレートのlindera-trainerが提供します。

主な特徴

  • ファイルシステムまたは埋め込みデータからの辞書読み込み
  • MeCab形式のCSVソースファイルからの辞書ビルド
  • 最適なセグメンテーションのためのViterbiアルゴリズム
  • N-bestパス生成(Forward-DP Backward-A*)
  • メモリマップドファイルサポート

目次

アーキテクチャ

モジュール構成

lindera-dictionary/src/
├── lib.rs               # パブリックAPI
├── dictionary.rs        # Dictionary, UserDictionary
├── builder.rs           # DictionaryBuilder
├── loader.rs            # DictionaryLoader trait, FSDictionaryLoader
├── viterbi.rs           # Lattice, Edge, Viterbiセグメンテーション
├── nbest.rs             # NBestGenerator (Forward-DP Backward-A*)
├── mode.rs              # Mode (Normal/Decompose), Penalty
├── error.rs             # LinderaError, LinderaErrorKind
├── assets.rs            # ダウンロードとファイル管理
├── macros.rs            # 辞書クレート共通の embedded_dictionary! マクロ
├── dictionary/
│   ├── character_definition.rs    # 文字種定義
│   ├── connection_cost_matrix.rs  # 連接コスト行列
│   ├── context_id_map.rs          # ContextIdMap(連接コストのコンテキストID再割り当て)
│   ├── prefix_dictionary.rs       # 前方一致辞書(crawdad トライをバイト列上で直接走査)
│   ├── unknown_dictionary.rs      # 未知語処理
│   ├── metadata.rs                # 辞書メタデータ
│   └── schema.rs                  # スキーマ定義

主要コンポーネント

Dictionary / UserDictionary

コンパイル済み辞書データを保持する主要データ構造です。Dictionaryは文字種定義、連接コスト行列、前方一致辞書(文字単位のダブル配列トライ)、および未知語辞書を含みます。システム辞書のトライはビルド時にcrawdadで構築され、実行時はdict.trieのシリアライズ済みバイト列上を直接走査します(デシリアライズなしのゼロコピー。ロードは O(1) のヘッダ検査のみ)。UserDictionaryを使用すると、システム辞書の上にカスタム語彙を追加できます。ユーザー辞書の前方一致検索は従来どおり rkyv アーカイブ内のdaachorseオートマトンを使用します。

DictionaryBuilder

ソースCSVファイルから辞書をビルドするためのFluent APIです。MeCab形式の辞書ソースを、実行時に使用されるバイナリ形式にコンパイルします。4つのビルドステージ(メタデータ、未知語辞書、前方一致辞書、連接コスト行列)は、wasm以外のターゲットではスコープ付きスレッド上で並行実行され、OSスレッドを持たないwasmでは逐次ビルドにフォールバックします。並行実行パスでは4ステージすべての作業データを同時に保持するため、ピークメモリ使用量が大きくなります。

DictionaryLoader / FSDictionaryLoader

DictionaryLoaderはコンパイル済み辞書を読み込むためのtraitです。FSDictionaryLoaderはファイルシステムベースの実装で、ディレクトリから辞書ファイルを読み込みます。オプションでメモリマップドファイルをサポートします。

埋め込み辞書マクロ

embedded_dictionary!マクロ(lindera-dictionary/src/macros.rs#[macro_export])は、各辞書クレートがコンパイル済み辞書をバイナリに埋め込むために必要なボイラープレートを生成します。具体的には、include_bytes!経由で辞書コンポーネントを読み込むload()関数と、DictionaryLoaderを実装するローダー構造体です。各辞書クレート(lindera-ipadiclindera-ipadic-neologdlindera-unidiclindera-ko-diclindera-cc-cedictlindera-jieba)のembedded.rsは、読み込みロジックを重複実装する代わりにこのマクロを呼び出します。

Viterbi (Lattice, Edge)

入力テキストから候補トークンのラティスを構築し、Viterbiアルゴリズムを使用して最適なセグメンテーションパスを探索します。ラティス内の各Edgeは、関連するコスト(単語コスト + 連接コスト)を持つ候補トークンを表します。

NBestGenerator

Forward-DP Backward-A*アルゴリズムを使用してN-bestセグメンテーションパスを生成します。これにより、アプリケーションは単一の最適パスを超えた代替セグメンテーションを検討できます。

Mode

トークナイゼーションの動作を制御します:

  • Normal: 最適なViterbiパスを使用した標準的なトークナイゼーション
  • Decompose: 設定可能なPenalty閾値に基づいて複合名詞をさらに分割

辞書フォーマットバージョン

ビルド済み辞書ディレクトリは、自身が書かれたオンディスクレイアウトを metadata.jsonformat_version として記録します。値は DictionaryBuilder::build_metadataDICTIONARY_FORMAT_VERSIONlindera-dictionary/src/dictionary/metadata.rs)から刻印します。いま何を書いたのかを正しく主張できるのはビルダだけなので、ソース metadata.json 側の値は無視されます。辞書ディレクトリのロード時には記録されたバージョンを検証し、一致しなければ対処方法を含むエラーを返します。

この検証が重要なのは、ほとんどの成果物が自前のヘッダを持たないためです。matrix.mtxdict.valsdict.words は生の配列なので、古いレイアウトの辞書でも長さが辻褄の合う範囲ならエラーにならず異常な値として解釈されます。各辞書クレートに手書きでチェックインされているソース metadata.json はビルドの入力を記述するもので、format_version を持たず、フォーマット検証の対象にもなりません。

ビルド済み成果物のバイト列が変わる変更では必ず DICTIONARY_FORMAT_VERSION を上げてください。シリアライズ形式をそのまま書き出している依存クレートの更新(dict.triecrawdadchar_def.bin / unk.binrkyv、ユーザー辞書 .bin 内の daachorse)も対象で、本クレートのコードが 1 行も変わらないため見落としやすい箇所です。ビルドキャッシュがフォーマットバージョンをキーに含めているのは、まさにこのためです。

現在のフォーマットバージョンは 2 です。バージョン 2 では、システム前方一致辞書のバイト単位 daachorse Aho-Corasick オートマトン(dict.da)が、文字単位のダブル配列トライ(dict.trie)と u32 の累積和インデックス(dict.valsidx)に置き換えられました。バージョン 1 以前の辞書はロード時に対処方法を含むエラーで拒否されます。ユーザー辞書の .binこの変更の影響を受けませんが、daachorse によって書き出されているため daachorse の更新では無効化されます。上記のリストに daachorse が含まれているのはそのためです。

コンテキストIDリマッピング

辞書メタデータ(lindera-dictionary/src/dictionary/metadata.rs)は、connection_id_mapping: boolフラグとオプションのcontext_id_map: Option<ContextIdMap>を持ちます。辞書クレート(例: lindera-unidic)がconnection_id_mappingを有効にすると、DictionaryBuilderはビルド時にアクセス頻度に基づいて連接行列の左右コンテキストIDを再割り当てし、頻繁に使用される連接コストのセルが近くに集まるようにしてキャッシュ局所性を高めます。DictionaryBuilder::with_context_id_freqは、IDのランク付けに使用するバンドル済み頻度ヒストグラムを(任意で)アタッチするためのメソッドで、再割り当て自体はlindera-dictionary/src/builder/context_id_remap.rsで計算されます。ContextIdMaplindera-dictionary/src/dictionary/context_id_map.rs)は結果として得られるleft/rightの置換を保持し、同じマッピングを後から再適用できるようにビルド済みのmetadata.jsonに永続化されます。ユーザー辞書は常に元の(再割り当てされていない)ID空間でコンパイルされるため、UserDictionary::remap_context_idslindera-dictionary/src/dictionary.rs)は永続化されたContextIdMapを使って、ユーザー辞書のコンテキストIDを、それが紐付くシステム辞書と同じ空間に再割り当てします。この再割り当ては全単射(bijective)な付け替えであるため、トークナイゼーションの出力は変わらず、ルックアップの局所性のみが変化します。

学習

CRFベースの辞書学習パイプラインは、本クレートのランタイム型の上に構築された独立クレート lindera-trainer に含まれます。詳細は学習パイプラインのドキュメントを参照してください。

Featureフラグ

Feature説明デフォルト
mmapファイルシステム辞書読み込みのためのメモリマップドファイルサポート(トライ・単語リストファイル・接続コスト行列はいずれもマップしたバイト列上で直接参照され、遅延読み込みされる。ロード時に全体展開されるコンポーネントはない)Yes
build_rs辞書ソースのHTTPダウンロードNo
ctxfreq実験的機能: 連接行列のアクセス頻度プロファイリングを計測し、コンテキストID頻度リマップの構築に使用No

APIリファレンス

APIリファレンスは以下で公開されています:

Lindera Trainer

Lindera Trainerは、CRFベースの辞書学習パイプライン(lindera train)を実装するクレートです。アノテーション付きコーパスと種辞書から学習済みモデルを生成し、そのモデルをMeCab形式の辞書ソースファイルにエクスポートしてバイナリ辞書としてビルドできるようにします。内部ではlindera-dictionaryのランタイム型とlindera-crfのCRFコアを利用しています。このクレートは直接利用することも、linderaファサードのtrain feature経由(lindera::dictionary::trainerとして再エクスポート)で利用することもできます。

主な特徴

  • lindera-crfによるCRFベースの重み学習(L1、L2、Elastic Net正則化に対応)
  • MeCab互換の素性テンプレート解析(feature.def: %F[n]%L[n]%R[n]%w%u%l%r、およびそれぞれの?付きオプション形式)
  • MeCab互換の3セクション形式による素性書き換え(rewrite.def: ユニグラム/左文脈/右文脈の書き換えルール)
  • 辞書形式に依存しない設計: surface,left_id,right_id,cost,feature...の列構成に従う辞書であれば任意のもの(IPADIC、UniDic、ko-dic、CC-CEDICTなど)を扱える
  • char.defの文字カテゴリに基づく未知語の自動カテゴリ分類
  • 学習されたCRFの重みから連接コスト行列を生成し、MeCab互換のコスト変換(tocost)を適用
  • ゼロコピーのrkyvバイナリ形式によるモデルのシリアライズ(読み込み時はレガシーJSON形式へのフォールバックにも対応)
  • Lindera/MeCab辞書ソースファイル(lex.csvmatrix.defunk.defchar.deffeature.defrewrite.defleft-id.defright-id.def)の直接エクスポート

目次

アーキテクチャ

モジュール構成

lindera-trainer/src/
├── lib.rs                # Trainer: CRF学習の実行制御。パブリックAPIの再エクスポート
├── config.rs             # TrainerConfig: 種辞書・char.def・feature.def・rewrite.defのパース
├── corpus.rs              # Corpus, Example, Word: 学習データの表現
├── feature_extractor.rs  # FeatureExtractor: 素性テンプレート解析と素性ID管理
├── feature_rewriter.rs   # DictionaryRewriter: MeCab互換の3セクション書き換え
└── model.rs               # Model, SerializableModel: 学習済みモデルの保持・シリアライズ・辞書出力

主要コンポーネント

TrainerConfig

学習に必要な5つの入力ファイル――種辞書(lex.csv)、文字定義(char.def)、未知語定義(unk.def)、素性テンプレート(feature.def)、書き換えルール(rewrite.def)――をパースし、Trainerが利用する設定情報にまとめます。TrainerConfig::from_readers(またはファイルパスを直接渡せるラッパーのfrom_paths)は、種辞書から表層形と素性の語彙を抽出し、パースした素性テンプレートからFeatureExtractorを構築し、書き換えルールからDictionaryRewriterを構築し、さらにchar.defから実際にパースした文字定義をもとに最小限のインメモリlindera_dictionary::dictionary::Dictionaryを組み立てます。注意: このDictionaryprefix_dictionary(システム辞書)フィールドとunknown_dictionaryフィールドは、空のスタブとして構築されるだけです――学習時に実際に使われる種辞書の語彙や未知語カテゴリの素性は、このDictionaryオブジェクトではなく、TrainerConfig自身のsurfaces / features / unk_categories / unk_costsフィールドに別途保持されています。また、surfaces()surface_features()get_features()に加え、user_lexicon() / add_user_lexicon_entry() / load_user_lexicon_from_content()によるユーザー辞書へのアクセス、metadata()も提供します。

Corpus / Example / Word

アノテーション付き学習データを表現します。Wordは表層形とその素性文字列(カンマ区切り)のペアです。Exampleは1つの学習用の文であり、Vec<Word>から構築され、各語の表層形を連結して元の文を復元します。CorpusExampleの集合です。Corpus::from_readerはタブ区切りのsurface<TAB>features形式の行をパースし、空行またはEOSのみの行を文の区切りとして扱います。

FeatureExtractor

MeCab互換の素性テンプレートを解析し、生成された素性文字列から内部で採番されるNonZeroU32型の素性IDへのマッピングを管理します。サポートされるテンプレートのプレースホルダは、%F[n] / %F?[n](インデックスnの素性フィールド。?付きは値が*の場合にスキップ)、%t(文字カテゴリ)、%w(表層形、ユニグラムのみ)、%u / %l / %r(書き換え後のユニグラム/左文脈/右文脈の素性文字列全体)、およびバイグラムの左右文脈フィールド用の%L[n] / %L?[n] / %R[n] / %R?[n]です。extract_unigram_feature_ids[_with_ctx]extract_left_feature_ids[_with_ctx]extract_right_feature_ids[_with_ctx]は、パース済みテンプレートを素性の配列(バイグラム抽出の場合は表層形/ufeature/lfeature/rfeatureを保持するオプションのTemplateContextも併せて)に適用し、対応する素性IDを返します。初回利用時には新しいIDが割り当てられます。

DictionaryRewriter

MeCabの3セクション形式rewrite.def[unigram rewrite][left rewrite][right rewrite])を実装します。各セクションはpattern<TAB>replacement形式のルールの並びを持ち、内部のFeatureRewriterBuilderによってFeatureRewriterというprefix trie(前方一致木)に構築されます。DictionaryRewriter::from_readerは3セクションすべてをパースしますが、セクションヘッダのないファイルは後方互換性のため(右文脈書き換えのみのレガシー形式として)扱います。rewrite()は素性文字列に3つの書き換え器すべてを適用し、(ufeature, lfeature, rfeature)を返します。マッチするルールがないセクションは入力をそのまま通過させます。(かつてのrewrite_cached()は素性文字列全体をキーにメモ化していましたが、実辞書の素性は行ごとにほぼ一意でキャッシュが一度もヒットしないため削除されました — #975。)

Model / SerializableModel

ModelTrainer::trainが生成する学習済みモデルです。学習されたlindera_crf::RawModel、学習に用いたTrainerConfig、抽出済みの素性の重みとラベル、そして学習後にread_user_lexiconで追加されたユーザー辞書エントリを保持します。学習されたCRFの重みは、MeCab互換の整数コストに変換されます(tocost(weight, cost_factor) = clamp(round(-weight * cost_factor), i16::MIN, i16::MAX)、すなわち[-32768, 32767]の範囲にクランプされます)。この際のコストファクターは、i16の値域を最大限活用できるよう計算されます(calculate_cost_factor)。Modelは自身をシリアライズでき(write_model)、辞書ソースファイルを直接エクスポートすることもできます。

SerializableModelは、Model::read_modelがリーダーから以前学習したモデルを読み込んだ際に返される、rkyvでシリアライズ可能な素のデータ型です(lindera export CLIコマンドで使用されます)。Modelと同じ学習結果の情報――素性の重み、ラベル、品詞情報、連接コスト行列、未知語カテゴリ、保存されているchar.def / feature.def / rewrite.defの内容、コストファクター、左右文脈IDマッピング――をTrainerConfigを持たない所有データとして保持し、辞書エクスポートファイルを生成するための独自のライターメソッド群を提供します。

Modelのパブリックメソッド

メソッド説明
read_user_lexicon<R: Read>(&mut self, rdr: R) -> Result<()>ユーザー定義辞書(種辞書と同じsurface,left_id,right_id,cost,feature...のCSV形式)をモデルに読み込みます。これにより、後続のエクスポート処理で推定された連接IDとコストを割り当てられるようになります。ユーザー辞書が必要な場合は、辞書を書き出す前に呼び出す必要があります。
write_model<W: Write>(&self, writer: &mut W) -> Result<()>学習済みモデル全体(素性の重み、ラベル、品詞情報、連接行列、未知語カテゴリ、保存された定義ファイルの内容、コストファクター、左右IDマッピング)をrkyvバイナリ形式でシリアライズします。出力はバイト単位で再現可能です。連接行列と未知語カテゴリが順序付きマップであるため、バイト列はモデルの内容だけで決まります。
read_model<R: Read>(reader: R) -> Result<SerializableModel>関連関数。write_modelが書き出したデータからSerializableModelをデシリアライズします。まずrkyv形式を試み、ペイロードが実際にJSONオブジェクトで始まる場合にのみレガシーのJSON形式へフォールバックします。いずれでもない場合は、形式の不一致として再学習を促すエラーを返します。feature_setsフィールドが存在しない旧形式のモデルについては、feature_weightsから補完します。
write_dictionary<W1, W2, W3, W4>(&self, lexicon_wtr, connector_wtr, unk_handler_wtr, user_lexicon_wtr) -> Result<()>レキシコン、連接コスト行列、未知語辞書、ユーザー辞書を一度にまとめて書き出す便利メソッドです。ユーザー辞書はread_user_lexiconで読み込んだエントリを入力順で書き出します。パラメータが0,0,0のエントリには推定した連接IDと学習済みコストを割り当て、それ以外のエントリは入力のまま再出力します。
write_lexicon<W: Write>(&self, writer: &mut W) -> Result<()>マージされたCRFモデルから得た連接IDとコストを用いて、種辞書の各語彙エントリについてlex.csv形式のエントリ(surface,left_id,right_id,cost,features...)を書き出します。
write_connection_costs<W: Write>(&self, writer: &mut W) -> Result<()>すべての(right_id, left_id)の組み合わせ(BOS/EOSを含む)を網羅する密なmatrix.def形式の連接コスト行列を書き出します。学習中に一度も出現しなかった組み合わせには最大のペナルティコスト(i16::MAX)が設定され、Viterbi探索が未学習の遷移を避けるようにします。
write_unknown_dictionary<W: Write>(&self, writer: &mut W) -> Result<()>各未知語文字カテゴリについて、学習された連接ID・コストとunk.def由来の素性文字列を用いてunk.def形式のエントリを書き出します。
get_unknown_word_cost(&self, category: usize) -> i32指定した未知語カテゴリのインデックスに対して設定されているコスト(unk.defのコスト列由来)を返します。設定がない場合はデフォルト値2000を返します。
num_features(&self) -> usize学習済みモデルが保持する素性の重みの数を返します。
num_labels(&self) -> usize学習済みモデルにおけるラベル(語彙の表層形と未知語カテゴリの合計)の数を返します。
raw_model(&self) -> &lindera_crf::RawModel高度な操作や低レベルの処理のために、内部のlindera-crfのraw modelへアクセスします。
write_bigram_details<L: Write, R: Write, C: Write>(&self, left_wtr, right_wtr, cost_wtr) -> Result<()>バイグラム素性とコストを記述した3つの診断用ファイル(左側素性一覧、右側素性一覧、生のバイグラム素性ペアごとのコスト行。id 0 はBOS/EOS、名前は素性抽出器由来)を書き出します。3ファイルとも同じモデルに対してバイト単位で再現可能です。
evaluate(&self, test_lattices: &[lindera_crf::Lattice]) -> f64raw modelの素性の重みの絶対値の平均を簡易的な評価スコアとして返します。引数test_latticesは現時点では未使用であり、保留データに対するモデルの評価はまだ行われません。
write_dictionary_buffers(&self, lexicon, connector, unk_handler, user_lexicon: &mut Vec<u8>) -> Result<()>ラベル、素性の重み、ユーザーエントリ数、表層形を、それぞれ生のrkyvバイトバッファへシリアライズします。CSVベースのwrite_dictionaryに対する、より低レベルな代替エクスポート手段です。
write_left_id_def<W: Write>(&self, writer: &mut W) -> Result<()>学習された左文脈IDをその素性文字列にマッピングしたleft-id.defを書き出します。先頭行は0 BOS/EOSです。
write_right_id_def<W: Write>(&self, writer: &mut W) -> Result<()>学習された右文脈IDをその素性文字列にマッピングしたright-id.defを書き出します。先頭行は0 BOS/EOSです。

SerializableModelのパブリックメソッド

Model::read_modelが返す値に対して利用できるメソッド群で、以前に学習・シリアライズされたモデルから辞書ソースファイルを再エクスポートするために使用します(lindera export CLIコマンドで利用されます)。

メソッド説明
write_lexicon<W: Write>(&self, writer: &mut W) -> Result<()>保存されているfeature_setslabelspos_infoから、末尾の未知語カテゴリのラベルを除いてlex.csv形式のエントリを書き出します。
write_connection_costs<W: Write>(&self, writer: &mut W) -> Result<()>保存されているconnection_matrixmax_left_idmax_right_idから、密なmatrix.def形式の連接コスト行列を書き出します。
write_unknown_dictionary<W: Write>(&self, writer: &mut W) -> Result<()>保存されている各未知語カテゴリについてunk.def形式のエントリを書き出します。
write_char_def<W: Write>(&self, writer: &mut W) -> Result<()>元の学習入力からそのまま保存されているchar.defの内容を書き出します。
write_feature_def<W: Write>(&self, writer: &mut W) -> Result<()>元の学習入力からそのまま保存されているfeature.defの内容を書き出します。
write_rewrite_def<W: Write>(&self, writer: &mut W) -> Result<()>元の学習入力からそのまま保存されているrewrite.defの内容を書き出します。
write_left_id_def<W: Write>(&self, writer: &mut W) -> Result<()>保存されているleft_id_mapからleft-id.defを書き出します。先頭行は0 BOS/EOSです。
write_right_id_def<W: Write>(&self, writer: &mut W) -> Result<()>保存されているright_id_mapからright-id.defを書き出します。先頭行は0 BOS/EOSです。
update_metadata_json<W: Write>(&self, base_metadata_path: &Path, writer: &mut W) -> Result<()>ベースとなるmetadata.jsonを読み込み、学習結果に基づく値(素性の重みの中央値から推定したdefault_word_cost、および素性・ラベル数や文脈IDの範囲・学習メタデータを含むmodel_infoセクション)で更新した結果を書き出します。タイムスタンプは含まれず、同じモデルに対してバイト単位で再現可能です。

ModelMetadataFeatureSetInfo

ModelMetadataは学習実行に関する要約情報を保持します: versionregularization(使用した正則化コスト係数)、iterations(設定された最大イテレーション数)、feature_countlabel_countです。SerializableModel::metadataに格納されます。

FeatureSetInfoは、学習後にマージされたCRFモデルから抽出された、ラベルごとの連接情報を保持します: left_idright_idweightです。SerializableModel::feature_setsには、labelsと同じ順序で各ラベルに対応する1エントリが格納されます。

APIリファレンス

APIリファレンスは以下で公開されています:

Lindera ライブラリ

lindera クレートは純粋な形態素セグメンターです。辞書クレートを統合し、Segmenter API を提供します。デフォルトでは lindera-analysislindera-crflindera-trainer に依存しません。このセクションでは、セグメンテーション、エラーハンドリング、APIリファレンスについて説明します。

Tokenizer や文字フィルタ・トークンフィルタ(Segmenter の上に構築されたLucene風の分析チェーン)が必要な場合は、別クレートのLindera Analysis設定フィルタページを含む)を参照してください。

Segmenter

Segmenter は形態素解析を実行するコアコンポーネントです。辞書とコストモデルに基づいて、入力テキストの最適な分割を Viterbi アルゴリズムで探索します。

Segmenter の作成

Segmenter には以下の3つのコンポーネントが必要です:

  • Mode - トークナイズ戦略(Normal または Decompose
  • Dictionary - 形態素解析用のシステム辞書
  • UserDictionary(オプション) - カスタム単語用の補助辞書
#![allow(unused)]
fn main() {
use lindera::dictionary::load_dictionary;
use lindera::mode::Mode;
use lindera::segmenter::Segmenter;

let dictionary = load_dictionary("embedded://ipadic")?;
let segmenter = Segmenter::new(Mode::Normal, dictionary, None);
}

トークナイズモード

Mode::Normal

辞書に登録されたエントリに基づく標準的なトークナイズです。辞書に登録された単語に忠実に分割します。

#![allow(unused)]
fn main() {
use lindera::mode::Mode;

let mode = Mode::Normal;
}

Mode::Decompose

複合名詞を構成要素に分解します。このモードでは、長い複合語にペナルティを適用し、Segmenter がより短い構成要素に分割するよう促します。

例えば、「関西国際空港限定トートバッグ」という文中の複合語「関西国際空港」は、Mode::Normal では1つのトークンの一部のままですが、Mode::Decompose では「関西」「国際」「空港」に分割されます(分割されるかどうかは前後の文脈にも依存し、同じ文字列単独では同じ結果にならない場合があります)。

#![allow(unused)]
fn main() {
use lindera::mode::Mode;

let mode = Mode::Decompose(Default::default());
}

辞書の読み込み

Lindera は様々なソースから辞書を読み込むための load_dictionary 関数を提供しています。

埋め込み辞書

適切な Feature フラグ(例: embed-ipadic)を指定してビルドすると、バイナリから直接辞書を読み込むことができます:

#![allow(unused)]
fn main() {
use lindera::dictionary::load_dictionary;

let dictionary = load_dictionary("embedded://ipadic")?;
}

利用可能な埋め込み辞書URI:

  • embedded://ipadic - IPADIC(日本語)
  • embedded://ipadic-neologd - IPADIC NEologd(日本語)
  • embedded://unidic - UniDic(日本語)
  • embedded://ko-dic - ko-dic(韓国語)
  • embedded://cc-cedict - CC-CEDICT(中国語)
  • embedded://jieba - Jieba(中国語)

外部辞書

ビルド済みの辞書ディレクトリをファイルシステムから読み込むことができます:

#![allow(unused)]
fn main() {
use lindera::dictionary::load_dictionary;

let dictionary = load_dictionary("/path/to/dictionary")?;
}

Tokenizer との連携

Segmenter は通常、Character Filter と Token Filter のサポートを追加する Tokenizer を通じて使用されます:

use lindera::dictionary::load_dictionary;
use lindera::mode::Mode;
use lindera::segmenter::Segmenter;
use lindera_analysis::tokenizer::Tokenizer;
use lindera::LinderaResult;

fn main() -> LinderaResult<()> {
    let dictionary = load_dictionary("embedded://ipadic")?;
    let segmenter = Segmenter::new(Mode::Normal, dictionary, None);
    let tokenizer = Tokenizer::new(segmenter);

    let text = "日本語の形態素解析を行うことができます。";
    let tokens = tokenizer.tokenize(text)?;

    for mut token in tokens {
        let details = token.details().join(",");
        println!("{}\t{}", token.surface.as_ref(), details);
    }

    Ok(())
}

tokenmut で束縛している点に注意してください。Token::details&mut self を取るため、単純な for token in tokens ではコンパイルエラー(E0596: 可変として借用できません)になります。

Config からの構築

Segmenter::from_config は、Tokenizer/TokenizerBuilder と同じ設定フォーマット(設定を参照)のうち segmenter: セクション相当を受け取り、SegmenterConfigserde_json::Value)から Segmenter を構築します:

#![allow(unused)]
fn main() {
use serde_json::json;
use lindera::segmenter::{Segmenter, SegmenterConfig};

let config: SegmenterConfig = json!({
    "mode": "normal",
    "dictionary": "embedded://ipadic",
    "keep_whitespace": false,
    "use_mmap": false
});
let segmenter = Segmenter::from_config(&config)?;
}

注: ここでは use_mmap を明示的に示すためにあえて false を指定していますが、省略した場合も後述のデフォルト(true)と同じ挙動になります。

メモリマップド読み込み

ファイルシステム辞書(embedded:// ではない辞書)に対しては、mmap cargo feature がコンパイルに含まれている場合(デフォルトで含まれます)、 use_mmap はデフォルトで true になり、メモリマップド読み込みが自動的に 使用されます。単純なファイル読み込み(非メモリマップド)を強制したい場合は false を指定してください。 辞書フォーマットバージョン 2 以降、トライ・単語リストファイル・接続コスト 行列はいずれもマップしたバイト列上で直接参照されるため、大きなコンポーネント はすべて遅延読み込みされ、ロード時に所有メモリへ全体展開されるものは ありません。 use_mmapembedded:// 辞書に対しては無視されます(埋め込みデータは 既に静的なゼロコピーバイトスライスであるため)。mmap cargo feature (デフォルトで有効)が必要です。

空白文字の扱い

デフォルトでは、MeCab互換のため空白のみのトークンは出力から除外されます。Segmenter に対して keep_whitespace(true) を呼び出すと、これらを保持できます:

#![allow(unused)]
fn main() {
let segmenter = Segmenter::new(Mode::Normal, dictionary, None).keep_whitespace(true);
}

未知語のグルーピング

未知語のグルーピングはデフォルトでは無制限です。max_grouping_len(Some(n)) で MeCab の max-grouping-size と同じ意味論(先頭を除いた文字数で数え、MeCab のデフォルトは 24)の上限を設定できます。上限を超えるランは 1 文字ずつの未知語になります:

#![allow(unused)]
fn main() {
let segmenter = Segmenter::new(Mode::Normal, dictionary, None).max_grouping_len(Some(24));
}

グルーピングとは独立に、Lindera はデフォルトで MeCab/Vibrato 由来の 「候補ラダー(length ladder)」も生成します。各カテゴリの char.defLENGTH フィールドまでの短い未知語候補を段階的に生成し、Viterbi 探索が 最もコストの低い長さを選べるようにします。v6 以前と同一の出力にするには unknown_word_ladder(false) で無効化してください:

#![allow(unused)]
fn main() {
let segmenter = Segmenter::new(Mode::Normal, dictionary, None).unknown_word_ladder(false);
}

N-Best セグメンテーション

segment_nbest は、コストの合計で並べた上位 n 件の分割結果を、それぞれのコストと共に返します。unique を指定すると、単語境界は同じで品詞タグのみ異なる結果を重複排除できます。cost_threshold を指定すると、best_cost + threshold を超えるコストのパスを除外できます:

#![allow(unused)]
fn main() {
let results = segmenter.segment_nbest(Cow::Borrowed("すもももももももものうち"), 3, false, None)?;
for (tokens, cost) in results {
    println!("cost={cost}");
    for token in tokens {
        println!("  {}", token.surface.as_ref());
    }
}
}

segment_nbest_with_lattice は同じ処理を行いますが、呼び出しごとの Lattice バッファの再確保を避けるために、再利用可能な Lattice を自分で渡すことができます。

文分割

ラティスを構築する前に、Linderaは入力テキストを区切り文字(\n\t)で文単位に分割し、1文ずつ処理します。文の先頭からおよそ 32 KiB 以内に区切り文字が見つからない場合、Linderaはその位置で強制的に文の境界を区切り、警告をログに出力します。これは、その文に対して構築される Viterbi ラティスのサイズ(ひいてはメモリ・CPU コスト)を制限するためです。通常のテキストに対してはこの挙動は意識する必要がありませんが、区切り文字を含まない病的な入力(例えば minify されたテキストや base64 エンコードされたデータなど)に対しては、この人為的な分割位置でトークナイズ結果に影響が出ることがあります。

再利用可能な Worker

SegmentWorker は、Viterbi ラティスとバックトレース用スクラッチバッファを所有する再利用可能なセグメンテーションセッションです。呼び出しのたびに segment が支払うアロケーションを回避できます。new_worker(セグメンターを clone)または into_worker(セグメンターを消費し、ユーザー辞書のコピーを回避)で作成し、segment/segment_nbest を呼び出します:

#![allow(unused)]
fn main() {
let mut worker = segmenter.new_worker();
for line in lines {
    let tokens = worker.segment(line)?;
    for token in &tokens {
        println!("{}", token.surface.as_ref());
    }
}
}

返されるトークンは worker を借用するため、次の呼び出しの前に消費する必要があります(上記のような行単位のループはそのままコンパイルできます)。set_modeset_keep_whitespace で呼び出しごとに設定を切り替えられます。segmenter() は内部の Segmenter への共有参照を返します。&mut でのアクセサは意図的に提供されていません。再利用中のラティスの下で辞書を差し替えてしまうと、この worker が保証する辞書とラティスの対応関係が壊れてしまうためです。

Worker は保持メモリも制限します。区切り文字のない最大長の文を 1 回処理するとラティスは数 MB まで成長し(32 KiB の ASCII 文で約 18 MB、32 KiB の CJK 文では文字索引ラティスのスロット数が 1/3 のため約 6 MB)、素の Lattice はそれを保持し続けます。Worker は一定回数の呼び出し窓で容量が過大と判明した場合に自動でラティスを縮小し、shrink_to(text_len_hint) で即時に縮小することもできます。reset() は内部バッファを破棄して新しいものに置き換えます。これは、例えばパニックによって worker を保持する Mutex がポイズニングされた場合など、バッファが不整合な中間状態を保持している可能性がある回復経路のために用意されています。Segmenter の設定(mode や空白の扱いなど)自体は保持されます。

Worker は作成元セグメンターの辞書に恒久的に紐付けられ、稼働中の worker の辞書を差し替える手段はありません。これにより、ラティス再利用に起因するバグの一群を構造的に排除しています。マルチスレッドで使う場合は、共有の Segmenter からスレッドごとに worker を 1 つ作成してください。

エラーハンドリング

Lindera はライブラリ全体で使いやすいエラーハンドリングを実現するため、anyhowthiserror に基づく構造化されたエラーシステムを使用しています。

LinderaResult

LinderaResult<T> 型エイリアスは、Lindera における失敗する可能性のある操作の標準的な戻り値型です:

#![allow(unused)]
fn main() {
pub type LinderaResult<T> = Result<T, LinderaError>;
}

LinderaError

LinderaError はメインのエラー型で、エラー種別と完全なコンテキストを持つソースエラーを含みます:

#![allow(unused)]
fn main() {
pub struct LinderaError {
    pub kind: LinderaErrorKind,
    source: anyhow::Error,
}
}

add_context メソッドを使用して、エラーに追加のコンテキストを付与できます:

#![allow(unused)]
fn main() {
let error = error.add_context("failed to load dictionary from /path/to/dict");
}

LinderaErrorKind

LinderaErrorKind はエラーを分類する列挙型です:

Kind説明
IoI/Oエラー(ファイルの読み書き、ネットワーク)
Parseパースエラー(無効な入力形式)
Serializeシリアライズエラー
Deserializeデシリアライズエラー
Content無効なコンテンツまたはデータのエラー
Args無効な引数のエラー
Decodeデコードエラー
NotFoundリソースが見つからない(例: 辞書ファイルの欠落)
Build辞書ビルドエラー
Dictionary辞書関連のエラー
Mode無効なトークナイズモードのエラー
FeatureDisabled有効化されていない機能を使用しようとした

エラーの作成

LinderaErrorKind::with_error を使用して、種別とソースからエラーを作成します:

#![allow(unused)]
fn main() {
use lindera::error::LinderaErrorKind;

let error = LinderaErrorKind::Io.with_error(anyhow::anyhow!("file not found: config.yml"));
}

? 演算子の使用

Lindera の関数は LinderaResult を返すため、? 演算子で自然にエラーを伝播できます:

#![allow(unused)]
fn main() {
use lindera::dictionary::load_dictionary;
use lindera::mode::Mode;
use lindera::segmenter::Segmenter;
use lindera_analysis::tokenizer::Tokenizer;
use lindera::LinderaResult;

fn analyze(text: &str) -> LinderaResult<Vec<String>> {
    let dictionary = load_dictionary("embedded://ipadic")?;
    let segmenter = Segmenter::new(Mode::Normal, dictionary, None);
    let tokenizer = Tokenizer::new(segmenter);

    let tokens = tokenizer.tokenize(text)?;
    Ok(tokens.iter().map(|t| t.surface.as_ref().to_string()).collect())
}
}

エラーハンドリングパターン

エラー種別によるマッチング

#![allow(unused)]
fn main() {
use lindera::dictionary::load_dictionary;
use lindera::error::LinderaErrorKind;

match load_dictionary("/path/to/dictionary") {
    Ok(dict) => { /* 辞書を使用 */ }
    Err(e) if e.kind() == LinderaErrorKind::NotFound => {
        eprintln!("Dictionary not found: {}", e);
    }
    Err(e) if e.kind() == LinderaErrorKind::Io => {
        eprintln!("I/O error loading dictionary: {}", e);
    }
    Err(e) => {
        eprintln!("Unexpected error: {}", e);
    }
}
}

外部エラーからの変換

#![allow(unused)]
fn main() {
use lindera::error::LinderaErrorKind;

let content = std::fs::read_to_string("config.yml")
    .map_err(|err| LinderaErrorKind::Io.with_error(anyhow::anyhow!(err)))?;
}

APIリファレンス

APIリファレンスは以下で公開されています:

Lindera Analysis

Lindera Analysisは、linderaクレートが提供する純粋な形態素解析器(Segmenter)の上に、Lucene流のテキスト解析チェーンを重ねるクレートです。文字フィルタ、Segmenter、トークンフィルタを1つのTokenizerパイプラインとして組み合わせ、Rustコードから組み立てることも、YAML設定ファイルだけで組み立てることもできます。

主な特徴

  • 文字フィルタ: セグメンテーション前に入力テキストを変換し、バイトオフセットは元のテキストに対して自動的に補正される
  • トークンフィルタ: Segmenterが生成したトークンの変換・結合・除去・並べ替えを行う
  • Tokenizer / TokenizerBuilder: Rustコード、またはYAMLファイル(LINDERA_CONFIG_PATH)から解析パイプライン全体を組み立てる
  • 日本語・韓国語・汎用のテキスト正規化をカバーする豊富な組み込みフィルタ

目次

設定

LinderaはYAML形式の設定ファイルを読み込むことができます。 環境変数 LINDERA_CONFIG_PATH にファイルのパスを指定してください。Rustコードでトークナイザーの動作をコーディングすることなく、簡単に利用できます。

segmenter:
  mode: "normal"
  dictionary: "embedded://ipadic"
  # user_dictionary: "./resources/user_dict/ipadic_simple_userdic.csv"
  # keep_whitespace: false
  # use_mmap: false # ファイルシステム辞書(embedded:// ではない辞書)にのみ意味がある
  # max_grouping_len: 24 # 未知語のグルーピング長の上限。省略または 0 で無制限
  # unknown_word_ladder: true # 短い未知語候補も生成する(デフォルト: true)

character_filters:
  - kind: "unicode_normalize"
    args:
      kind: "nfkc"
  - kind: "japanese_iteration_mark"
    args:
      normalize_kanji: true
      normalize_kana: true
  - kind: mapping
    args:
       mapping:
         リンデラ: Lindera

token_filters:
  - kind: "japanese_compound_word"
    args:
      tags:
        - "名詞,数"
        - "名詞,接尾,助数詞"
      new_tag: "名詞,数"
  - kind: "japanese_number"
    args:
      tags:
        - "名詞,数"
  - kind: "japanese_stop_tags"
    args:
      tags:
        - "接続詞"
        - "助詞"
        - "助詞,格助詞"
        - "助詞,格助詞,一般"
        - "助詞,格助詞,引用"
        - "助詞,格助詞,連語"
        - "助詞,係助詞"
        - "助詞,副助詞"
        - "助詞,間投助詞"
        - "助詞,並立助詞"
        - "助詞,終助詞"
        - "助詞,副助詞/並立助詞/終助詞"
        - "助詞,連体化"
        - "助詞,副詞化"
        - "助詞,特殊"
        - "助動詞"
        - "記号"
        - "記号,一般"
        - "記号,読点"
        - "記号,句点"
        - "記号,空白"
        - "記号,括弧閉"
        - "その他,間投"
        - "フィラー"
        - "非言語音"
  - kind: "japanese_katakana_stem"
    args:
      min: 3
  - kind: "remove_diacritical_mark"
    args:
      japanese: false

Segmenter のオプション

キーデフォルト説明
modestring"normal"分割モード。"normal" または "decompose"
dictionarystring(必須)辞書 URI。例: "embedded://ipadic"
user_dictionarystring(なし)ユーザー辞書のパス
keep_whitespaceboolfalse空白トークンを無視せずに出力する(MeCab は無視する)
use_mmapboolmmap feature が有効なとき on(デフォルト)辞書をメモリマップする。ファイルシステム辞書(embedded:// ではない辞書)にのみ意味がある
max_grouping_leninteger(無制限)未知語のグルーピングが先頭の 1 文字を超えてspan できる最大文字数。MeCab の max-grouping-size に相当する(MeCab のデフォルトは 24)。これを超えるグルーピング候補は生成されず、代わりに 1 文字の未知語が出力される。キーを省略するか 0 を指定すると無制限になる
unknown_word_ladderbooltrueMeCab や Vibrato と同様に、char.def の各カテゴリの LENGTH フィールドまでの短い未知語候補も生成する。v6 より前の出力を正確に再現するには false を指定する
% export LINDERA_CONFIG_PATH=./resources/config/lindera.yml
use std::path::PathBuf;

use lindera_analysis::tokenizer::TokenizerBuilder;
use lindera::LinderaResult;

fn main() -> LinderaResult<()> {
    // 設定ファイルからトークナイザーの設定を読み込む
    let path = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
        .join("../resources")
        .join("config")
        .join("lindera.yml");

    let builder = TokenizerBuilder::from_file(&path)?;

    let tokenizer = builder.build()?;

    let text = "Linderaは形態素解析エンジンです。ユーザー辞書も利用可能です。".to_string();
    println!("text: {text}");

    let tokens = tokenizer.tokenize(&text)?;

    for token in tokens {
        println!(
            "token: {:?}, start: {:?}, end: {:?}, details: {:?}",
            token.surface, token.byte_start, token.byte_end, token.details
        );
    }

    Ok(())
}

フィルタ

文字フィルタ(Character Filter)とトークンフィルタ(Token Filter)は、lindera-analysisTokenizerパイプラインにおける前処理・後処理の2つの段階です。

  • 文字フィルタはセグメンテーションの前に入力テキストを変換します。バイトオフセットは自動的に補正されるため、変換後のテキストに対して生成されたトークンでも、元のフィルタ前のテキストにおける位置が正しく報告されます。
  • トークンフィルタはSegmenterが生成したトークンのリストをセグメンテーションの後に変換します。

どちらのフィルタも設定方法は共通で、kind文字列(CLIの--character-filter / --token-filterフラグでもkind:{"json": "args"}という形式で使用されます)と、フィルタ固有のパラメータを持つJSONのargsオブジェクトで構成されます。

文字フィルタ

文字フィルタはYAML設定ファイルのcharacter_filtersキーで設定します。各エントリは順番に適用され、あるフィルタの出力が次のフィルタの入力になります。

unicode_normalize

4種類の標準的なUnicode正規化形式のいずれかを使って入力テキストを正規化します。

パラメータ:

パラメータ必須説明
kindstringはいnfcnfdnfkcnfkdのいずれか

例:

{
  "kind": "unicode_normalize",
  "args": {
    "kind": "nfkc"
  }
}

japanese_iteration_mark

日本語の踊り字(繰り返し記号)であるを、それぞれが繰り返す文字に置き換えて正規化します。ひらがな・カタカナの繰り返し記号については、必要に応じて濁点の付与・除去も行います。

パラメータ:

パラメータ必須デフォルト説明
normalize_kanjiboolいいえfalse漢字の踊り字を正規化する
normalize_kanaboolいいえfalseひらがな・カタカナの踊り字を正規化する

例:

{
  "kind": "japanese_iteration_mark",
  "args": {
    "normalize_kanji": true,
    "normalize_kana": true
  }
}

mapping(文字フィルタ)

mappingのキーに一致する部分を対応する値に置き換えます。入力テキスト全体に対して、Aho-Corasickオートマトンによる最長一致検索を行います。

パラメータ:

パラメータ必須説明
mappingobject(string to string)はい置換対象の部分文字列と、その置換先の対応表

例:

{
  "kind": "mapping",
  "args": {
    "mapping": {
      "リンデラ": "Lindera"
    }
  }
}

regex

正規表現にマッチした箇所をすべて、リテラルな置換文字列で置き換えます。キャプチャグループの内容は置換文字列に展開されません。

パラメータ:

パラメータ必須説明
patternstringはい正規表現(regexクレートの構文)
replacementstringはいpatternにマッチした箇所すべてを置き換えるリテラル文字列

例:

{
  "kind": "regex",
  "args": {
    "pattern": "\\s{2,}",
    "replacement": " "
  }
}

トークンフィルタ

トークンフィルタはYAML設定ファイルのtoken_filtersキーで設定します。各フィルタは、Segmenterが生成したトークンリストに対して順番に適用されます。

japanese_base_form

トークンの表層形を、辞書のbase_form(またはorthographic_base_form)フィールドに登録された原形(辞書形)に置き換えます。動詞・形容詞のレンマ化(見出し語化)として機能します。未知語処理によって生成されたトークン(token.word_id.is_unknown())は変更されません。

このフィルタに設定パラメータはありません。

例:

{
  "kind": "japanese_base_form"
}

japanese_compound_word

品詞タグがtagsのいずれかに一致する連続したトークンを、1つの複合語トークンに結合します。

パラメータ:

パラメータ必須説明
tagsarray<string>はい結合対象となるトークンを示す品詞タグ(カンマ区切りで最大4階層)
new_tagstringいいえ結合後のトークンに付与する品詞タグ。省略した場合は複合語が付与される

例:

{
  "kind": "japanese_compound_word",
  "args": {
    "tags": [
      "名詞,数",
      "名詞,接尾,助数詞"
    ],
    "new_tag": "名詞,数"
  }
}

japanese_kana

トークンテキストをひらがなとカタカナの間で相互変換します。

パラメータ:

パラメータ必須説明
kindstringはい"hiragana"はカタカナをひらがなに、"katakana"はひらがなをカタカナに変換する

例:

{
  "kind": "japanese_kana",
  "args": {
    "kind": "hiragana"
  }
}

japanese_katakana_stem

カタカナのトークンの末尾にある長音記号(、U+30FC)を除去します。ただし、トークンの文字数がminより大きい場合のみ除去されます。

パラメータ:

パラメータ必須説明
min正の整数はい末尾の長音記号をステミングする対象となる、カタカナトークンの最小文字数

例:

{
  "kind": "japanese_katakana_stem",
  "args": {
    "min": 3
  }
}

japanese_keep_tags

品詞タグがtagsのいずれかに一致するトークンのみを保持し、それ以外を除去します。

タグは 4 階層のカンマ区切りに正規化され(不足分は * で補完)、各トークンの先頭 4 つの品詞詳細と完全一致で比較されます。IPADIC の助詞トークンは必ず 助詞,係助詞 のようにサブカテゴリを持つため、助詞 単独では一致しません。一方、助動詞はサブカテゴリを持たないため(助動詞,*,*,*)、助動詞 単独で一致します。この動作は japanese_stop_tags も同様です。

パラメータ:

パラメータ必須説明
tagsarray<string>はい保持する品詞タグ(カンマ区切りで最大4階層)

例:

{
  "kind": "japanese_keep_tags",
  "args": {
    "tags": [
      "名詞,一般"
    ]
  }
}

japanese_number

トークンの表層テキストに含まれる日本語の数値表現(漢数字、大字、全角数字)をアラビア数字に変換します。

パラメータ:

パラメータ必須説明
tagsarray<string>またはnullいいえ変換対象を限定する品詞タグ(カンマ区切りで最大4階層)。省略またはnullの場合はすべてのトークンが変換対象になる

例:

{
  "kind": "japanese_number",
  "args": {
    "tags": [
      "名詞,数"
    ]
  }
}

japanese_reading_form

トークンの表層テキストを、辞書のreadingフィールドに登録された読み(カタカナ)に置き換えます。未知語処理によって生成されたトークン(token.word_id.is_unknown())は変更されません。

このフィルタに設定パラメータはありません。

例:

{
  "kind": "japanese_reading_form"
}

japanese_stop_tags

品詞タグがtagsのいずれかに一致するトークンを除去します。

パラメータ:

パラメータ必須説明
tagsarray<string>はい除去する品詞タグ(カンマ区切りで最大4階層)

例:

{
  "kind": "japanese_stop_tags",
  "args": {
    "tags": [
      "助詞,格助詞,一般",
      "助詞,係助詞",
      "助詞,連体化",
      "助動詞"
    ]
  }
}

keep_words

表層テキストがwordsのいずれかに完全一致するトークンのみを保持します。

パラメータ:

パラメータ必須説明
wordsarray<string>はい保持する表層形の一覧

例:

{
  "kind": "keep_words",
  "args": {
    "words": [
      "すもも",
      "もも"
    ]
  }
}

korean_keep_tags

最初の品詞タグがtagsのいずれかに一致する韓国語トークンのみを保持します。

パラメータ:

パラメータ必須説明
tagsarray<string>はい保持する品詞タグ

例:

{
  "kind": "korean_keep_tags",
  "args": {
    "tags": [
      "NNG"
    ]
  }
}

korean_reading_form

トークンの表層テキストを、辞書のreadingフィールドに登録された読みに置き換えます。未知語処理によって生成されたトークン(token.word_id.is_unknown())は変更されません。

このフィルタに設定パラメータはありません。

例:

{
  "kind": "korean_reading_form"
}

korean_stop_tags

最初の品詞タグがtagsのいずれかに一致する韓国語トークンを除去します。

パラメータ:

パラメータ必須説明
tagsarray<string>はい除去する品詞タグ

例:

{
  "kind": "korean_stop_tags",
  "args": {
    "tags": [
      "EP",
      "EF",
      "JKG"
    ]
  }
}

length

表層テキストの文字数が[min, max]の範囲に収まるトークンのみを保持します。

パラメータ:

パラメータ必須説明
min符号なし整数いいえ最小文字数(この値を含む)
max符号なし整数いいえ最大文字数(この値を含む)

例:

{
  "kind": "length",
  "args": {
    "min": 2,
    "max": 3
  }
}

lowercase

トークンの表層テキストを小文字に変換します。

このフィルタに設定パラメータはありません。

例:

{
  "kind": "lowercase"
}

mapping(トークンフィルタ)

mappingのキーに一致する部分を、各トークンの表層テキスト内で対応する値に置き換えます。Aho-Corasickオートマトンによる最長一致検索を使用します。文字フィルタのmappingのトークン版に相当します。

パラメータ:

パラメータ必須説明
mappingobject(string to string)はい置換対象の部分文字列と、その置換先の対応表

例:

{
  "kind": "mapping",
  "args": {
    "mapping": {
      "籠": "篭"
    }
  }
}

remove_diacritical_mark

トークンの表層テキストからダイアクリティカルマーク(発音区別符号)を除去し、その後テキストの元のUnicode正規化形式を再適用します。

パラメータ:

パラメータ必須デフォルト説明
japaneseboolいいえfalse日本語の濁点・半濁点の結合文字(分解済みの濁音・半濁音仮名に含まれるものなど)も除去する

例:

{
  "kind": "remove_diacritical_mark",
  "args": {
    "japanese": false
  }
}

stop_words

表層テキストがwordsのいずれかに完全一致するトークンを除去します。

パラメータ:

パラメータ必須説明
wordsarray<string>はい除去する表層形の一覧

例:

{
  "kind": "stop_words",
  "args": {
    "words": [
      "も",
      "の"
    ]
  }
}

uppercase

トークンの表層テキストを大文字に変換します。

このフィルタに設定パラメータはありません。

例:

{
  "kind": "uppercase"
}

YAML設定

文字フィルタとトークンフィルタは、Segmenterと一緒に1つのYAMLファイルで設定します。ファイル全体の形式は設定を参照してください。関連する部分だけを抜粋すると次のようになります。

character_filters:
  - kind: "unicode_normalize"
    args:
      kind: "nfkc"
  - kind: "japanese_iteration_mark"
    args:
      normalize_kanji: true
      normalize_kana: true

token_filters:
  - kind: "japanese_stop_tags"
    args:
      tags:
        - "助詞,格助詞,一般"
        - "助詞,係助詞"
        - "助詞,連体化"
        - "助動詞"
  - kind: "japanese_katakana_stem"
    args:
      min: 3
  - kind: "lowercase"
  - kind: "length"
    args:
      min: 2

Rust API

文字フィルタとトークンフィルタは、プログラムから作成・適用することもできます。

use lindera::dictionary::load_dictionary;
use lindera::mode::Mode;
use lindera::segmenter::Segmenter;
use lindera_analysis::character_filter::BoxCharacterFilter;
use lindera_analysis::character_filter::unicode_normalize::{
    UnicodeNormalizeCharacterFilter, UnicodeNormalizeKind,
};
use lindera_analysis::token_filter::BoxTokenFilter;
use lindera_analysis::token_filter::japanese_stop_tags::JapaneseStopTagsTokenFilter;
use lindera_analysis::token_filter::japanese_katakana_stem::JapaneseKatakanaStemTokenFilter;
use lindera_analysis::tokenizer::Tokenizer;
use lindera::LinderaResult;

fn main() -> LinderaResult<()> {
    let dictionary = load_dictionary("embedded://ipadic")?;
    let segmenter = Segmenter::new(Mode::Normal, dictionary, None);

    let mut tokenizer = Tokenizer::new(segmenter);

    // 文字フィルタを追加
    let normalize_filter = UnicodeNormalizeCharacterFilter::new(UnicodeNormalizeKind::NFKC);
    tokenizer.append_character_filter(BoxCharacterFilter::from(normalize_filter));

    // トークンフィルタを追加
    let stop_tags_filter = JapaneseStopTagsTokenFilter::new(
        vec![
            "助詞,格助詞,一般".to_string(),
            "助詞,係助詞".to_string(),
            "助詞,連体化".to_string(),
            "助動詞".to_string(),
        ]
        .into_iter()
        .collect(),
    );
    tokenizer.append_token_filter(BoxTokenFilter::from(stop_tags_filter));

    let katakana_stem_filter =
        JapaneseKatakanaStemTokenFilter::new(std::num::NonZeroUsize::new(3).unwrap());
    tokenizer.append_token_filter(BoxTokenFilter::from(katakana_stem_filter));

    // フィルタを適用してトークナイズ
    let tokens = tokenizer.tokenize("Linderaは形態素解析エンジンです。")?;

    for token in tokens {
        println!(
            "token: {:?}, details: {:?}",
            token.surface, token.details
        );
    }

    Ok(())
}

append_character_filterappend_token_filterメソッドは、フィルタを追加した順番で登録します。文字フィルタはセグメンテーション前のテキストに対して順次適用され、トークンフィルタはセグメンテーション後のトークンリストに対して順次適用されます。

アーキテクチャ

モジュール構成

lindera-analysis/src/
├── lib.rs                          # パブリックAPIの再エクスポート、CLIフラグ解析ヘルパー
├── character_filter.rs              # CharacterFilter trait、OffsetMapping、CharacterFilterLoader
├── character_filter/
│   ├── unicode_normalize.rs         # Unicode正規化(NFC/NFD/NFKC/NFKD)
│   ├── japanese_iteration_mark.rs   # 日本語の踊り字(繰り返し記号)正規化
│   ├── mapping.rs                   # マッピングによるテキスト置換
│   └── regex.rs                     # 正規表現によるテキスト置換
├── token_filter.rs                  # TokenFilter trait、TokenFilterLoader
├── token_filter/
│   ├── japanese_base_form.rs
│   ├── japanese_compound_word.rs
│   ├── japanese_kana.rs
│   ├── japanese_katakana_stem.rs
│   ├── japanese_keep_tags.rs
│   ├── japanese_number.rs
│   ├── japanese_reading_form.rs
│   ├── japanese_stop_tags.rs
│   ├── keep_words.rs
│   ├── korean_keep_tags.rs
│   ├── korean_reading_form.rs
│   ├── korean_stop_tags.rs
│   ├── length.rs
│   ├── lowercase.rs
│   ├── mapping.rs
│   ├── remove_diacritical_mark.rs
│   ├── stop_words.rs
│   ├── tags.rs                      # keep/stop系タグフィルタが共有するヘルパー(非公開)
│   └── uppercase.rs
└── tokenizer.rs                      # Tokenizer、TokenizerBuilder

主要コンポーネント

CharacterFilter

セグメンテーション前にテキストを前処理するフィルタのtraitです。各実装はname()と、textをその場で書き換えて実施した変換内容をOffsetMappingとして返すapply(&self, text: &mut String) -> LinderaResult<OffsetMapping>を提供します。

OffsetMappingTransformationレコードのリストから構築される)により、複数のフィルタを順に適用した後でも、Tokenizerはフィルタ後のテキストに対して計算されたトークンのバイトオフセットを、元の入力テキストにおけるバイトオフセットへ変換できます。BoxCharacterFilterは任意のCharacterFilter実装をボックス化・クローン可能なトレイトオブジェクトとしてラップし、CharacterFilterLoaderkind文字列とserde_json::Valueの引数からフィルタを構築します(YAML設定の読み込みとCLIフラグ解析の両方で利用されます)。

TokenFilter

Segmenterが生成したトークンを後処理するフィルタのtraitです。各実装はname()と、トークンをその場で変換・結合・並べ替え・除去するapply(&self, tokens: &mut Vec<Token<'_>>) -> LinderaResult<()>を提供します。BoxTokenFilterは任意のTokenFilter実装をボックス化・クローン可能なトレイトオブジェクトとしてラップし、TokenFilterLoaderCharacterFilterLoaderと同様に、kind文字列とserde_json::Valueの引数からフィルタを構築します。

Tokenizer / TokenizerBuilder

Tokenizerは、文字フィルタ、lindera::segmenter::Segmenter、トークンフィルタを1つの解析パイプラインとして組み合わせます。tokenizeを呼び出すと、入力テキストに文字フィルタを適用し、フィルタ後のテキストをセグメンテーションし、得られたトークンにトークンフィルタを適用したうえで、記録済みのOffsetMappingを使って各トークンのバイトオフセットを元のテキストに対する値へ補正します。

TokenizerBuilderTokenizerConfigserde_json::Value)からTokenizerを組み立てます。この設定はプログラムから直接構築することも、YAMLファイルから読み込むこと(TokenizerBuilder::from_file、または環境変数LINDERA_CONFIG_PATH経由で自動的に読み込むTokenizerBuilder::new)も、set_segmenter_modeset_segmenter_dictionaryappend_character_filterappend_token_filterで段階的に組み立てることもできます。YAMLファイルの形式は設定を、フィルタの完全なリファレンスはフィルタを参照してください。

AnalysisWorker

AnalysisWorkerTokenizer::new_workerまたはTokenizer::into_workerで作成)は、解析チェーン全体に対する再利用可能なセッションです。呼び出しごとのバッファ — Viterbiラティスとバックトレース用スクラッチ(SegmentWorker経由)、文字フィルタが操作する正規化テキストバッファ、オフセットマッピング用スクラッチ — をすべて所有するため、tokenizeを繰り返し呼び出してもTokenizer::tokenizeが支払う呼び出しごとのアロケーションを回避できます。文字フィルタが設定されている場合、トークンのsurfaceはトークンごとのStringにコピーされる代わりにワーカーのバッファを借用します。返されるトークンはワーカーを借用するため、次の呼び出しの前に消費する必要があります。マルチスレッドで使う場合はスレッドごとにワーカーを作成してください(あるいはlindera-binding-coreのようにMutexで保護します)。基盤となるSegmentWorkerと自動メモリ縮小ポリシーについてはSegmenterのページを参照してください。

AnalysisWorkerの主なpublicメソッド:

  • tokenize(&mut self, text: &str) — ワーカーの内部バッファを再利用しながら、解析チェーン全体を通してtextをトークナイズします。同じ入力・設定であればTokenizer::tokenizeとまったく同じトークンを返します。
  • tokenize_nbest(&mut self, text: &str, n, unique, cost_threshold) — ワーカーの内部バッファを再利用しながら、コスト付きの上位N件の結果をトークナイズして返します。Tokenizer::tokenize_nbestとまったく同じ結果を返します。
  • set_mode(&mut self, mode: Mode) — 以降の呼び出しで使用するセグメンテーションモードを設定します。
  • set_keep_whitespace(&mut self, keep: bool) — 以降の呼び出しで空白トークンを出力に残すかどうかを設定します。
  • shrink_to(&mut self, text_len_hint: usize) — ワーカーの内部バッファを、text_len_hintバイトの入力に必要なサイズまで直ちに縮小します。
  • reset(&mut self) — すべての内部バッファを破棄し、新しいバッファに置き換えます。(例えばワーカーを保持するMutexがパニックでpoisonedになった場合の)リカバリ用途を想定しており、設定(辞書・フィルタ・モード)は保持されます。

Feature フラグ

Feature説明デフォルト
embed-ipadicIPADIC辞書をバイナリに埋め込む(lindera/embed-ipadicへ委譲)No
embed-ipadic-neologdIPADIC-NEologd辞書をバイナリに埋め込む(lindera/embed-ipadic-neologdへ委譲)No
embed-unidicUniDic辞書をバイナリに埋め込む(lindera/embed-unidicへ委譲)No
embed-sudachidictSudachiDict辞書をバイナリに埋め込む(lindera/embed-sudachidictへ委譲)No
embed-ko-dicko-dic辞書をバイナリに埋め込む(lindera/embed-ko-dicへ委譲)No
embed-cc-cedictCC-CEDICT辞書をバイナリに埋め込む(lindera/embed-cc-cedictへ委譲)No
embed-jiebaJieba辞書をバイナリに埋め込む(lindera/embed-jiebaへ委譲)No

APIリファレンス

APIリファレンスは以下で公開されています:

Lindera CLI

Lindera のための形態素解析コマンドラインインターフェースです。

インストール

cargo経由でインストール

cargo経由でバイナリをインストールできます:

% cargo install lindera-cli

GitHub Releasesからダウンロード

または、以下のリリースページからビルド済みバイナリをダウンロードすることもできます:

辞書の入手

Lindera はバイナリに辞書を同梱していません。最も簡単な入手方法は download サブコマンドで、CLI と同じバージョンのビルド済み辞書を取得して OS 標準のアプリケーションデータディレクトリにインストールします:

% lindera download ipadic

ダウンロード後は辞書名で参照できます:

% echo "関西国際空港限定トートバッグ" | lindera tokenize --dict ipadic

利用可能な辞書名と保存場所についてはコマンドを参照してください。

または、GitHub Releases ページからビルド済み辞書を手動でダウンロードすることもできます:

# 例: IPADIC 辞書のダウンロードと展開
% curl -LO https://github.com/lindera/lindera/releases/download/<version>/lindera-ipadic-<version>.zip
% unzip lindera-ipadic-<version>.zip -d /path/to/ipadic

CLI 使用時に展開した辞書パスを指定します(アーカイブには lindera-ipadic ディレクトリが含まれる点に注意):

% echo "関西国際空港限定トートバッグ" | lindera tokenize --dict /path/to/ipadic/lindera-ipadic

ソースからビルド

辞書なしでビルド(デフォルト)

辞書を埋め込まず、トークナイザーとトレーナーのみを含むバイナリをビルドします:

% cargo build --release

全機能を含めてビルド

% cargo build --release --all-features

辞書埋め込みビルド(上級者向け)

辞書をバイナリに直接埋め込みたい上級者向けに、embed-* feature フラグを使用できます。実行時の外部辞書ファイルが不要になりますが、バイナリサイズが増加します。

IPADIC(日本語辞書)

% cargo build --release --features=embed-ipadic

IPADIC NEologd(日本語辞書)

% cargo build --release --features=embed-ipadic-neologd

UniDic(日本語辞書)

% cargo build --release --features=embed-unidic

SudachiDict(日本語辞書)

% cargo build --release --features=embed-sudachidict

ko-dic(韓国語辞書)

% cargo build --release --features=embed-ko-dic

CC-CEDICT(中国語辞書)

% cargo build --release --features=embed-cc-cedict

Jieba(中国語辞書)

% cargo build --release --features=embed-jieba

[!TIP] embed-* feature フラグ付きでビルドした後、embedded:// スキームで埋め込み辞書をロードできます:

% echo "関西国際空港限定トートバッグ" | lindera tokenize --dict embedded://ipadic

詳細は Feature フラグ を参照してください。

コマンド

Lindera CLI は6つのメインコマンドを提供します:

  • list - 形態素解析辞書の一覧と状態を表示
  • tokenize - テキストに対して形態素解析を実行
  • build - ソースCSVファイルから辞書をビルド
  • download - GitHub リリースページから学習済み辞書をダウンロード
  • train - 注釈付きコーパスデータからCRFモデルを学習
  • export - 学習済みモデルを辞書フォーマットにエクスポート

list

既知の形態素解析辞書の一覧を表示し、各辞書について(embed-* feature フラグ経由で)バイナリに埋め込まれているか、および lindera download で学習済み辞書がローカルにダウンロード済みかを表示します。

list パラメータ

このコマンドは引数を取りません。環境変数 LINDERA_DATA_DIR を設定すると、ダウンロード済み辞書を探すアプリケーションデータディレクトリを上書きできます(lindera download と同じ挙動です)。

list の使用方法

% lindera list
NAME            EMBEDDED  DOWNLOADED  PATH
ipadic          yes       yes         /home/user/.local/share/lindera/dictionaries/5.1.0/lindera-ipadic
ipadic-neologd  no        no          -
unidic          no        incomplete  /home/user/.local/share/lindera/dictionaries/5.1.0/lindera-unidic
sudachidict     no        no          -
ko-dic          no        no          -
cc-cedict       no        no          -
jieba           no        no          -

出力は1行につき1辞書です:

  • EMBEDDED は、ビルド時に辞書が埋め込まれている場合(例: --features=embed-ipadic)に yes になります。
  • DOWNLOADED は、実行中の CLI バージョンに対応する lindera download でインストールされた完全な辞書が存在する場合に yes、インストールディレクトリは存在するが必要なファイルが欠けている場合に incomplete になります(lindera download <name> --force で再ダウンロードしてください)。
  • PATH は、インストールディレクトリが存在する場合はそのパス、存在しない場合は - を表示します。

tokenize

様々な辞書を使用して、日本語、中国語、または韓国語のテキストに対して形態素解析(トークナイズ)を行います。

パラメータ

  • --dict / -d: 辞書のパス、URI、またはダウンロード済み辞書名(必須)
    • ファイルパス: /path/to/dictionary
    • 埋め込み: embedded://ipadic, embedded://unidic, etc.
    • ダウンロード済み辞書名: ipadic, unidic など。lindera download でインストールした辞書に解決されます。同名のファイルパスが実在する場合はパスが優先されます。
  • --output / -o: 出力形式 (デフォルト: mecab)
    • mecab: 品詞情報を含むMeCab互換形式
    • wakati: スペース区切りのトークンのみ
    • json: すべてのトークン情報を含む詳細なJSON形式
  • --user-dict / -u: ユーザー辞書のパス(オプション)
  • --mode / -m: トークナイズモード (デフォルト: normal)
    • normal: 標準的なトークナイズ
    • decompose: 複合語を分解する
  • --char-filter / -c: 文字フィルタ設定 (JSON)
  • --token-filter / -t: トークンフィルタ設定 (JSON)
  • --keep-whitespace: 空白文字のトークンを出力に含める(デフォルトでは MeCab 互換のため空白は除去されます)
  • --max-grouping-len: 未知語グルーピングの上限(先頭を除いた文字数。MeCab の max-grouping-size に相当し、MeCab のデフォルトは 24)。上限を超えるランは 1 文字ずつの未知語になります。デフォルト: 無制限
  • --disable-unknown-word-ladder: MeCab/Vibrato 由来の未知語候補ラダー(char.defLENGTH フィールド)を無効化する。デフォルトで有効。v6 以前の Lindera と同一の出力にするには無効化する
  • --mmap: 辞書ディレクトリの単語リストにメモリマップドファイル読み込みを使用する。embedded:// 辞書、および mmap feature が無効な場合は無視される。プロセスがマップを保持している間に辞書ディレクトリを再ビルド・切り詰めると、次回のルックアップで SIGBUS を引き起こす可能性がある。
  • --nbest / -N: 返す N-best 結果の数(デフォルト: 1)。2以上に設定すると N-best 出力が有効になります。
  • --nbest-unique: 同じ分割を生成するパスの重複を排除します。
  • --nbest-cost-threshold: 最良パスからの最大コスト差。best_cost + threshold 以内のコストを持つパスのみが返されます。
  • 入力ファイル: オプションのファイルパス (デフォルト: 標準入力)

基本的な使用方法

# 辞書ディレクトリを指定してトークナイズ
echo "日本語の形態素解析を行うことができます。" | lindera tokenize \
  --dict /path/to/dictionary

# 埋め込み辞書を指定してトークナイズ
echo "日本語の形態素解析を行うことができます。" | lindera tokenize \
  --dict embedded://ipadic

# 出力形式を指定してトークナイズ
echo "日本語の形態素解析を行うことができます。" | lindera tokenize \
  --dict embedded://ipadic \
  --output json

# ファイルからテキストを読み込んでトークナイズ
lindera tokenize \
  --dict /path/to/dictionary \
  --output wakati \
  input.txt

外部辞書を使用した例

外部IPADIC(日本語辞書)を使用したトークナイズ

% echo "日本語の形態素解析を行うことができます。" | lindera tokenize \
  --dict /tmp/lindera-ipadic-2.7.0-20250920
日本語  名詞,一般,*,*,*,*,日本語,ニホンゴ,ニホンゴ
の      助詞,連体化,*,*,*,*,の,ノ,ノ
形態素  名詞,一般,*,*,*,*,形態素,ケイタイソ,ケイタイソ
解析    名詞,サ変接続,*,*,*,*,解析,カイセキ,カイセキ
を      助詞,格助詞,一般,*,*,*,を,ヲ,ヲ
行う    動詞,自立,*,*,五段・ワ行促音便,基本形,行う,オコナウ,オコナウ
こと    名詞,非自立,一般,*,*,*,こと,コト,コト
が      助詞,格助詞,一般,*,*,*,が,ガ,ガ
でき    動詞,自立,*,*,一段,連用形,できる,デキ,デキ
ます    助動詞,*,*,*,特殊・マス,基本形,ます,マス,マス
。      記号,句点,*,*,*,*,。,。,。
EOS

外部IPADIC NEologd(日本語辞書)を使用したトークナイズ

% echo "日本語の形態素解析を行うことができます。" | lindera tokenize \
  --dict /tmp/lindera-ipadic-neologd-0.0.7-20200820
日本語  名詞,一般,*,*,*,*,日本語,ニホンゴ,ニホンゴ
の      助詞,連体化,*,*,*,*,の,ノ,ノ
形態素解析      名詞,固有名詞,一般,*,*,*,形態素解析,ケイタイソカイセキ,ケイタイソカイセキ
を      助詞,格助詞,一般,*,*,*,を,ヲ,ヲ
行う    動詞,自立,*,*,五段・ワ行促音便,基本形,行う,オコナウ,オコナウ
こと    名詞,非自立,一般,*,*,*,こと,コト,コト
が      助詞,格助詞,一般,*,*,*,が,ガ,ガ
でき    動詞,自立,*,*,一段,連用形,できる,デキ,デキ
ます    助動詞,*,*,*,特殊・マス,基本形,ます,マス,マス
。      記号,句点,*,*,*,*,。,。,。
EOS

外部UniDic(日本語辞書)を使用したトークナイズ

% echo "日本語の形態素解析を行うことができます。" | lindera tokenize \
  --dict /tmp/lindera-unidic-2.1.2
日本    名詞,固有名詞,地名,国,*,*,ニッポン,日本,日本,ニッポン,日本,ニッポン,固,*,*,*,*
語      名詞,普通名詞,一般,*,*,*,ゴ,語,語,ゴ,語,ゴ,漢,*,*,*,*
の      助詞,格助詞,*,*,*,*,ノ,の,の,ノ,の,ノ,和,*,*,*,*
形態    名詞,普通名詞,一般,*,*,*,ケイタイ,形態,形態,ケータイ,形態,ケータイ,漢,*,*,*,*
素      接尾辞,名詞的,一般,*,*,*,ソ,素,素,ソ,素,ソ,漢,*,*,*,*
解析    名詞,普通名詞,サ変可能,*,*,*,カイセキ,解析,解析,カイセキ,解析,カイセキ,漢,*,*,*,*
を      助詞,格助詞,*,*,*,*,ヲ,を,を,オ,を,オ,和,*,*,*,*
行う    動詞,一般,*,*,五段-ワア行,連体形-一般,オコナウ,行う,行う,オコナウ,行う,オコナウ,和,*,*,*,*
こと    名詞,普通名詞,一般,*,*,*,コト,事,こと,コト,こと,コト,和,コ濁,基本形,*,*
が      助詞,格助詞,*,*,*,*,ガ,が,が,ガ,が,ガ,和,*,*,*,*
でき    動詞,非自立可能,*,*,上一段-カ行,連用形-一般,デキル,出来る,でき,デキ,できる,デキル,和,*,*,*,*
ます    助動詞,*,*,*,助動詞-マス,終止形-一般,マス,ます,ます,マス,ます,マス,和,*,*,*,*
。      補助記号,句点,*,*,*,*,,。,。,,。,,記号,*,*,*,*
EOS

外部ko-dic(韓国語辞書)を使用したトークナイズ

% echo "한국어의형태해석을실시할수있습니다." | lindera tokenize \
  --dict /tmp/lindera-ko-dic-2.1.1-20180720
한국어  NNG,*,F,한국어,Compound,*,*,한국/NNG/*+어/NNG/*
의      JKG,*,F,의,*,*,*,*
형태    NNG,*,F,형태,*,*,*,*
해석    NNG,행위,T,해석,*,*,*,*
을      JKO,*,T,을,*,*,*,*
실시    NNG,행위,F,실시,*,*,*,*
할      XSV+ETM,*,T,할,Inflect,XSV,ETM,하/XSV/*+ᆯ/ETM/*
수      NNB,*,F,수,*,*,*,*
있      VV,*,T,있,*,*,*,*
습니다  EF,*,F,습니다,*,*,*,*
.       SF,*,*,*,*,*,*,*
EOS

外部CC-CEDICT(中国語辞書)を使用したトークナイズ

% echo "可以进行中文形态学分析。" | lindera tokenize \
  --dict /tmp/lindera-cc-cedict-0.1.0-20200409
可以    *,*,*,*,ke3 yi3,可以,可以,can/may/possible/able to/not bad/pretty good/
进行    *,*,*,*,jin4 xing2,進行,进行,to advance/to conduct/underway/in progress/to do/to carry out/to carry on/to execute/
中文    *,*,*,*,Zhong1 wen2,中文,中文,Chinese language/
形态学  *,*,*,*,xing2 tai4 xue2,形態學,形态学,morphology (in biology or linguistics)/
分析    *,*,*,*,fen1 xi1,分析,分析,to analyze/analysis/CL:個|个[ge4]/
。      *,*,*,*,*,*,*,*
EOS

外部Jieba(中国語辞書)を使用したトークナイズ

% echo "可以进行中文形态学分析。" | lindera tokenize \
  --dict /tmp/lindera-jieba-0.1.1

埋め込み辞書を使用した例

Linderaは、特定の機能フラグを指定してビルドすることで、バイナリに辞書を直接含めることができます。これにより、外部辞書ファイルなしでトークナイズが可能になります。

埋め込みIPADIC(日本語辞書)を使用したトークナイズ

% echo "日本語の形態素解析を行うことができます。" | lindera tokenize \
  --dict embedded://ipadic
日本語  名詞,一般,*,*,*,*,日本語,ニホンゴ,ニホンゴ
の      助詞,連体化,*,*,*,*,の,ノ,ノ
形態素  名詞,一般,*,*,*,*,形態素,ケイタイソ,ケイタイソ
解析    名詞,サ変接続,*,*,*,*,解析,カイセキ,カイセキ
を      助詞,格助詞,一般,*,*,*,を,ヲ,ヲ
行う    動詞,自立,*,*,五段・ワ行促音便,基本形,行う,オコナウ,オコナウ
こと    名詞,非自立,一般,*,*,*,こと,コト,コト
が      助詞,格助詞,一般,*,*,*,が,ガ,ガ
でき    動詞,自立,*,*,一段,連用形,できる,デキ,デキ
ます    助動詞,*,*,*,特殊・マス,基本形,ます,マス,マス
。      記号,句点,*,*,*,*,。,。,。
EOS

注意: IPADIC辞書をバイナリに含めるには、--features=embed-ipadic オプションを使用してビルドする必要があります。

埋め込みUniDic(日本語辞書)を使用したトークナイズ

% echo "日本語の形態素解析を行うことができます。" | lindera tokenize \
  --dict embedded://unidic
日本    名詞,固有名詞,地名,国,*,*,ニッポン,日本,日本,ニッポン,日本,ニッポン,固,*,*,*,*
語      名詞,普通名詞,一般,*,*,*,ゴ,語,語,ゴ,語,ゴ,漢,*,*,*,*
の      助詞,格助詞,*,*,*,*,ノ,の,の,ノ,の,ノ,和,*,*,*,*
形態    名詞,普通名詞,一般,*,*,*,ケイタイ,形態,形態,ケータイ,形態,ケータイ,漢,*,*,*,*
素      接尾辞,名詞的,一般,*,*,*,ソ,素,素,ソ,素,ソ,漢,*,*,*,*
解析    名詞,普通名詞,サ変可能,*,*,*,カイセキ,解析,解析,カイセキ,解析,カイセキ,漢,*,*,*,*
を      助詞,格助詞,*,*,*,*,ヲ,を,を,オ,を,オ,和,*,*,*,*
行う    動詞,一般,*,*,五段-ワア行,連体形-一般,オコナウ,行う,行う,オコナウ,行う,オコナウ,和,*,*,*,*
こと    名詞,普通名詞,一般,*,*,*,コト,事,こと,コト,こと,コト,和,コ濁,基本形,*,*
が      助詞,格助詞,*,*,*,*,ガ,が,が,ガ,が,ガ,和,*,*,*,*
でき    動詞,非自立可能,*,*,上一段-カ行,連用形-一般,デキル,出来る,でき,デキ,できる,デキル,和,*,*,*,*
ます    助動詞,*,*,*,助動詞-マス,終止形-一般,マス,ます,ます,マス,ます,マス,和,*,*,*,*
。      補助記号,句点,*,*,*,*,,。,。,,。,,記号,*,*,*,*
EOS

注意: UniDic辞書をバイナリに含めるには、--features=embed-unidic オプションを使用してビルドする必要があります。

埋め込みIPADIC NEologd(日本語辞書)を使用したトークナイズ

% echo "日本語の形態素解析を行うことができます。" | lindera tokenize \
  --dict embedded://ipadic-neologd
日本語  名詞,一般,*,*,*,*,日本語,ニホンゴ,ニホンゴ
の      助詞,連体化,*,*,*,*,の,ノ,ノ
形態素解析      名詞,固有名詞,一般,*,*,*,形態素解析,ケイタイソカイセキ,ケイタイソカイセキ
を      助詞,格助詞,一般,*,*,*,を,ヲ,ヲ
行う    動詞,自立,*,*,五段・ワ行促音便,基本形,行う,オコナウ,オコナウ
こと    名詞,非自立,一般,*,*,*,こと,コト,コト
が      助詞,格助詞,一般,*,*,*,が,ガ,ガ
でき    動詞,自立,*,*,一段,連用形,できる,デキ,デキ
ます    助動詞,*,*,*,特殊・マス,基本形,ます,マス,マス
。      記号,句点,*,*,*,*,。,。,。
EOS

注意: IPADIC NEologd辞書をバイナリに含めるには、--features=embed-ipadic-neologd オプションを使用してビルドする必要があります。

埋め込みko-dic(韓国語辞書)を使用したトークナイズ

% echo "한국어의형태해석을실시할수있습니다." | lindera tokenize \
  --dict embedded://ko-dic
한국어  NNG,*,F,한국어,Compound,*,*,한국/NNG/*+어/NNG/*
의      JKG,*,F,의,*,*,*,*
형태    NNG,*,F,형태,*,*,*,*
해석    NNG,행위,T,해석,*,*,*,*
을      JKO,*,T,을,*,*,*,*
실시    NNG,행위,F,실시,*,*,*,*
할      XSV+ETM,*,T,할,Inflect,XSV,ETM,하/XSV/*+ᆯ/ETM/*
수      NNB,*,F,수,*,*,*,*
있      VV,*,T,있,*,*,*,*
습니다  EF,*,F,습니다,*,*,*,*
.       SF,*,*,*,*,*,*,*
EOS

注意: ko-dic辞書をバイナリに含めるには、--features=embed-ko-dic オプションを使用してビルドする必要があります。

埋め込みCC-CEDICT(中国語辞書)を使用したトークナイズ

% echo "可以进行中文形态学分析。" | lindera tokenize \
  --dict embedded://cc-cedict
可以    *,*,*,*,ke3 yi3,可以,可以,can/may/possible/able to/not bad/pretty good/
进行    *,*,*,*,jin4 xing2,進行,进行,to advance/to conduct/underway/in progress/to do/to carry out/to carry on/to execute/
中文    *,*,*,*,Zhong1 wen2,中文,中文,Chinese language/
形态学  *,*,*,*,xing2 tai4 xue2,形態學,形态学,morphology (in biology or linguistics)/
分析    *,*,*,*,fen1 xi1,分析,分析,to analyze/analysis/CL:個|个[ge4]/
。      *,*,*,*,*,*,*,*
EOS

注意: CC-CEDICT辞書をバイナリに含めるには、--features=embed-cc-cedict オプションを使用してビルドする必要があります。

埋め込みJieba(中国語辞書)を使用したトークナイズ

% echo "可以进行中文形态学分析。" | lindera tokenize \
  --dict embedded://jieba

注意: Jieba辞書をバイナリに含めるには、--features=embed-jieba オプションを使用してビルドする必要があります。

ユーザー辞書の例

Linderaは、システム辞書と一緒にカスタム単語を追加するためのユーザー辞書をサポートしています。ユーザー辞書はCSVまたはバイナリ形式にすることができます。

ユーザー辞書の使用(CSV形式)

% echo "東京スカイツリーの最寄り駅はとうきょうスカイツリー駅です" | lindera tokenize \
  --dict embedded://ipadic \
  --user-dict ./resources/user_dict/ipadic_simple_userdic.csv
東京スカイツリー        カスタム名詞,*,*,*,*,*,東京スカイツリー,トウキョウスカイツリー,*
の      助詞,連体化,*,*,*,*,の,ノ,ノ
最寄り駅        名詞,一般,*,*,*,*,最寄り駅,モヨリエキ,モヨリエキ
は      助詞,係助詞,*,*,*,*,は,ハ,ワ
とうきょうスカイツリー駅        カスタム名詞,*,*,*,*,*,とうきょうスカイツリー駅,トウキョウスカイツリーエキ,*
です    助動詞,*,*,*,特殊・デス,基本形,です,デス,デス
EOS

ユーザー辞書の使用(バイナリ形式)

% echo "東京スカイツリーの最寄り駅はとうきょうスカイツリー駅です" | lindera tokenize \
  --dict /tmp/lindera-ipadic-2.7.0-20250920 \
  --user-dict ./resources/user_dict/ipadic_simple_userdic.bin
東京スカイツリー        カスタム名詞,*,*,*,*,*,東京スカイツリー,トウキョウスカイツリー,*
の      助詞,連体化,*,*,*,*,の,ノ,ノ
最寄り駅        名詞,一般,*,*,*,*,最寄り駅,モヨリエキ,モヨリエキ
は      助詞,係助詞,*,*,*,*,は,ハ,ワ
とうきょうスカイツリー駅        カスタム名詞,*,*,*,*,*,とうきょうスカイツリー駅,トウキョウスカイツリーエキ,*
です    助動詞,*,*,*,特殊・デス,基本形,です,デス,デス
EOS

トークナイズモード

Linderaは2つのトークナイズモードを提供します:normaldecompose です。

Normal モード(デフォルト)

辞書に登録された単語に基づいて忠実にトークナイズします:

% echo "関西国際空港限定トートバッグ" | lindera tokenize \
  --dict embedded://ipadic \
  --mode normal
関西国際空港    名詞,固有名詞,組織,*,*,*,関西国際空港,カンサイコクサイクウコウ,カンサイコクサイクーコー
限定    名詞,サ変接続,*,*,*,*,限定,ゲンテイ,ゲンテイ
トートバッグ    名詞,一般,*,*,*,*,*,*,*
EOS

Decompose モード

複合語をさらに分解してトークナイズします:

% echo "関西国際空港限定トートバッグ" | lindera tokenize \
  --dict embedded://ipadic \
  --mode decompose
関西    名詞,固有名詞,地域,一般,*,*,関西,カンサイ,カンサイ
国際    名詞,一般,*,*,*,*,国際,コクサイ,コクサイ
空港    名詞,一般,*,*,*,*,空港,クウコウ,クーコー
限定    名詞,サ変接続,*,*,*,*,限定,ゲンテイ,ゲンテイ
トートバッグ    名詞,一般,*,*,*,*,*,*,*
EOS

出力形式

Linderaは3つの出力形式を提供します:mecab, wakati, json

MeCab 形式(デフォルト)

品詞情報を含むMeCab互換形式で結果を出力します:

% echo "お待ちしております。" | lindera tokenize \
  --dict embedded://ipadic \
  --output mecab
お待ち  名詞,サ変接続,*,*,*,*,お待ち,オマチ,オマチ
し  動詞,自立,*,*,サ変・スル,連用形,する,シ,シ
て  助詞,接続助詞,*,*,*,*,て,テ,テ
おり  動詞,非自立,*,*,五段・ラ行,連用形,おる,オリ,オリ
ます  助動詞,*,*,*,特殊・マス,基本形,ます,マス,マス
。  記号,句点,*,*,*,*,。,。,。
EOS

Wakati 形式

トークンテキストのみをスペース区切りで出力します:

% echo "お待ちしております。" | lindera tokenize \
  --dict embedded://ipadic \
  --output wakati
お待ち し て おり ます 。

JSON 形式

すべてのトークン情報を含む詳細なJSON形式で出力します:

% echo "お待ちしております。" | lindera tokenize \
  --dict embedded://ipadic \
  --output json
[
  {
    "base_form": "お待ち",
    "byte_end": 9,
    "byte_start": 0,
    "conjugation_form": "*",
    "conjugation_type": "*",
    "part_of_speech": "名詞",
    "part_of_speech_subcategory_1": "サ変接続",
    "part_of_speech_subcategory_2": "*",
    "part_of_speech_subcategory_3": "*",
    "pronunciation": "オマチ",
    "reading": "オマチ",
    "surface": "お待ち",
    "word_id": 14698
  },
  ...
]

N-Best トークナイズ

Linderaは N-Best トークナイズをサポートしており、コスト順(低コスト=高精度)に上位 N 件のトークナイズ候補を返します。これは MeCab の N-Best 実装と互換性のある Forward-DP Backward-A* アルゴリズムに基づいています。

基本的な N-Best の例

% echo "すもももももももものうち" | lindera tokenize \
  --dict embedded://ipadic \
  -N 3
NBEST 1 (cost=7546)
すもも  名詞,一般,*,*,*,*,すもも,スモモ,スモモ
も      助詞,係助詞,*,*,*,*,も,モ,モ
もも    名詞,一般,*,*,*,*,もも,モモ,モモ
も      助詞,係助詞,*,*,*,*,も,モ,モ
もも    名詞,一般,*,*,*,*,もも,モモ,モモ
の      助詞,連体化,*,*,*,*,の,ノ,ノ
うち    名詞,非自立,副詞可能,*,*,*,うち,ウチ,ウチ
EOS
NBEST 2 (cost=7914)
すもも  名詞,一般,*,*,*,*,すもも,スモモ,スモモ
も      助詞,係助詞,*,*,*,*,も,モ,モ
もも    名詞,一般,*,*,*,*,もも,モモ,モモ
もも    名詞,一般,*,*,*,*,もも,モモ,モモ
の      助詞,連体化,*,*,*,*,の,ノ,ノ
うち    名詞,非自立,副詞可能,*,*,*,うち,ウチ,ウチ
EOS
NBEST 3 (cost=10060)
すもも  名詞,一般,*,*,*,*,すもも,スモモ,スモモ
も      助詞,係助詞,*,*,*,*,も,モ,モ
もも    名詞,一般,*,*,*,*,もも,モモ,モモ
も      助詞,係助詞,*,*,*,*,も,モ,モ
も      助詞,係助詞,*,*,*,*,も,モ,モ
の      助詞,連体化,*,*,*,*,の,ノ,ノ
うち    名詞,非自立,副詞可能,*,*,*,うち,ウチ,ウチ
EOS

ユニーク結果を使用した N-Best

同じ分割が複数のパスに現れる場合(内部的な Viterbi 状態のみが異なる場合)、--nbest-unique を使用して重複を排除します:

% echo "営業部長谷川です" | lindera tokenize \
  --dict embedded://ipadic \
  -N 5 --nbest-unique -o wakati
NBEST 1 (cost=15760)
営業 部長 谷川 です
NBEST 2 (cost=17758)
営業 部長 谷 川 です
NBEST 3 (cost=18816)
営業 部 長谷川 です
NBEST 4 (cost=19320)
営業 部長 谷川 で す
NBEST 5 (cost=20814)
営業 部 長谷 川 です

コスト閾値を使用した N-Best

--nbest-cost-threshold を使用して、最良パスから一定のコスト範囲内の結果に制限します:

% echo "営業部長谷川です" | lindera tokenize \
  --dict embedded://ipadic \
  -N 10 --nbest-unique --nbest-cost-threshold 5000 -o wakati
NBEST 1 (cost=15760)
営業 部長 谷川 です
NBEST 2 (cost=17758)
営業 部長 谷 川 です
NBEST 3 (cost=18816)
営業 部 長谷川 です

残りの候補は 15760 + 5000 = 20760 を超えるため、3件の結果のみが返されます。

フィルタを使用した高度なトークナイズ

Linderaは、文字フィルタ、トークナイザー、トークンフィルタを組み合わせた分析フレームワークを提供します。フィルタはJSONを使用して構成します。

% echo "すもももももももものうち" | lindera tokenize \
  --dict embedded://ipadic \
  --char-filter 'unicode_normalize:{"kind":"nfkc"}' \
  --token-filter 'japanese_keep_tags:{"tags":["名詞,一般"]}'
すもも  名詞,一般,*,*,*,*,すもも,スモモ,スモモ
もも    名詞,一般,*,*,*,*,もも,モモ,モモ
もも    名詞,一般,*,*,*,*,もも,モモ,モモ
EOS

build

Linderaで使用するための形態素解析辞書をCSVソースファイルからビルド(コンパイル)します。

ビルドパラメータ

  • --src / -s: 辞書CSVファイルを含むソースディレクトリ(ユーザー辞書の場合は単一CSVファイル)
  • --dest / -d: コンパイルされた辞書の出力先ディレクトリ
  • --metadata / -m: 辞書構造を定義するメタデータ設定ファイル (metadata.json)
  • --user / -u: システム辞書の代わりにユーザー辞書をビルドする(オプションフラグ)
  • --context-id-freq / -f: 接続コストIDの並び替えに使用するコンテキストID アクセス頻度ファイル(オプション。辞書の metadata.jsonconnection_id_mapping: true が設定されている場合にのみ意味を持ちます)

辞書の種類

システム辞書 (System dictionary)

以下を含む完全な形態素解析辞書です:

  • 語彙エントリ(単語定義)
  • 接続コスト行列
  • 未知語処理ルール
  • 文字種定義

ユーザー辞書 (User dictionary)

システム辞書と一緒に動作する、カスタム単語のための補助辞書です。

IPADIC(日本語辞書)のビルド

# IPADICソースファイルのダウンロードと展開
% curl -L -o /tmp/mecab-ipadic-2.7.0-20250920.tar.gz "https://Lindera.dev/mecab-ipadic-2.7.0-20250920.tar.gz"
% tar zxvf /tmp/mecab-ipadic-2.7.0-20250920.tar.gz -C /tmp

# 辞書のビルド
% lindera build \
  --src /tmp/mecab-ipadic-2.7.0-20250920 \
  --dest /tmp/lindera-ipadic-2.7.0-20250920 \
  --metadata ./lindera-ipadic/metadata.json

IPADIC NEologd(日本語辞書)のビルド

% curl -L -o /tmp/mecab-ipadic-neologd-0.0.7-20200820.tar.gz "https://lindera.dev/mecab-ipadic-neologd-0.0.7-20200820.tar.gz"
% tar zxvf /tmp/mecab-ipadic-neologd-0.0.7-20200820.tar.gz -C /tmp

% lindera build \
  --src /tmp/mecab-ipadic-neologd-0.0.7-20200820 \
  --dest /tmp/lindera-ipadic-neologd-0.0.7-20200820 \
  --metadata ./lindera-ipadic-neologd/metadata.json

UniDic(日本語辞書)のビルド

% curl -L -o /tmp/unidic-mecab-2.1.2.tar.gz "https://Lindera.dev/unidic-mecab-2.1.2.tar.gz"
% tar zxvf /tmp/unidic-mecab-2.1.2.tar.gz -C /tmp

% lindera build \
  --src /tmp/unidic-mecab-2.1.2 \
  --dest /tmp/lindera-unidic-2.1.2 \
  --metadata ./lindera-unidic/metadata.json

CC-CEDICT(中国語辞書)のビルド

% curl -L -o /tmp/CC-CEDICT-MeCab-0.1.0-20200409.tar.gz "https://lindera.dev/CC-CEDICT-MeCab-0.1.0-20200409.tar.gz"
% tar zxvf /tmp/CC-CEDICT-MeCab-0.1.0-20200409.tar.gz -C /tmp

% lindera build \
  --src /tmp/CC-CEDICT-MeCab-0.1.0-20200409 \
  --dest /tmp/lindera-cc-cedict-0.1.0-20200409 \
  --metadata ./lindera-cc-cedict/metadata.json

Jieba(中国語辞書)のビルド

% curl -L -o /tmp/mecab-jieba-0.1.1.tar.gz "https://lindera.dev/mecab-jieba-0.1.1.tar.gz"
% tar zxvf /tmp/mecab-jieba-0.1.1.tar.gz -C /tmp

% lindera build \
  --src /tmp/mecab-jieba-0.1.1/dict-src \
  --dest /tmp/lindera-jieba-0.1.1 \
  --metadata ./lindera-jieba/metadata.json

ko-dic(韓国語辞書)のビルド

% curl -L -o /tmp/mecab-ko-dic-2.1.1-20180720.tar.gz "https://Lindera.dev/mecab-ko-dic-2.1.1-20180720.tar.gz"
% tar zxvf /tmp/mecab-ko-dic-2.1.1-20180720.tar.gz -C /tmp

% lindera build \
  --src /tmp/mecab-ko-dic-2.1.1-20180720 \
  --dest /tmp/lindera-ko-dic-2.1.1-20180720 \
  --metadata ./lindera-ko-dic/metadata.json

ユーザー辞書のビルド

IPADICユーザー辞書(日本語)のビルド

ユーザー辞書フォーマットの詳細については、以下のURLを参照してください:

% lindera build \
  --src ./resources/user_dict/ipadic_simple_userdic.csv \
  --dest ./resources/user_dict \
  --metadata ./lindera-ipadic/metadata.json \
  --user

UniDicユーザー辞書(日本語)のビルド

ユーザー辞書フォーマットの詳細については、以下のURLを参照してください:

% lindera build \
  --src ./resources/user_dict/unidic_simple_userdic.csv \
  --dest ./resources/user_dict \
  --metadata ./lindera-unidic/metadata.json \
  --user

SudachiDictユーザー辞書(日本語)のビルド

ユーザー辞書フォーマットの詳細については、以下のURLを参照してください:

% lindera build \
  --src ./resources/user_dict/sudachidict_simple_userdic.csv \
  --dest ./resources/user_dict \
  --metadata ./lindera-sudachidict/metadata.json \
  --user

CC-CEDICTユーザー辞書(中国語)のビルド

ユーザー辞書フォーマットの詳細については、以下のURLを参照してください:

% lindera build \
  --src ./resources/user_dict/cc-cedict_simple_userdic.csv \
  --dest ./resources/user_dict \
  --metadata ./lindera-cc-cedict/metadata.json \
  --user

Jiebaユーザー辞書(中国語)のビルド

ユーザー辞書フォーマットの詳細については、以下のURLを参照してください:

% lindera build \
  --src ./resources/user_dict/jieba_simple_userdic.csv \
  --dest ./resources/user_dict \
  --metadata ./lindera-jieba/metadata.json \
  --user

ko-dicユーザー辞書(韓国語)のビルド

ユーザー辞書フォーマットの詳細については、以下のURLを参照してください:

% lindera build \
  --src ./resources/user_dict/ko-dic_simple_userdic.csv \
  --dest ./resources/user_dict \
  --metadata ./lindera-ko-dic/metadata.json \
  --user

download

CLI と同じバージョンの学習済み辞書アーカイブを GitHub リリースページからダウンロードし、OS 標準のアプリケーションデータディレクトリにインストールします。

download パラメータ

  • 辞書名(必須): ipadic, ipadic-neologd, unidic, sudachidict, ko-dic, cc-cedict, jieba のいずれか
  • --force: 既存の辞書を再ダウンロードして置き換える

保存場所

辞書はバージョン付きレイアウト <データディレクトリ>/dictionaries/<バージョン>/lindera-<辞書名>/ にインストールされます。OS ごとのデフォルトのベースディレクトリ:

OSデフォルトの場所
Linux~/.local/share/lindera
macOS~/Library/Application Support/lindera
Windows%LOCALAPPDATA%\lindera

環境変数 LINDERA_DATA_DIR を設定するとベースディレクトリを上書きできます。この変数はビルド時のソースキャッシュ用変数 LINDERA_BUILD_DICTIONARY_CACHE_DIR とは無関係です。

download の使用方法

% lindera download ipadic
Downloading https://github.com/lindera/lindera/releases/download/v5.1.0/lindera-ipadic-5.1.0.zip
100% (15.1/15.1 MiB)
Downloaded dictionary 'ipadic'
/home/user/.local/share/lindera/dictionaries/5.1.0/lindera-ipadic

インストール先のパスは標準出力に出力されるため(進捗や通知は標準エラー出力)、スクリプトで取得できます:

% DICT_DIR=$(lindera download ipadic)

ダウンロード後は tokenize で辞書名を指定して参照できます:

% echo "日本語の形態素解析を行うことができます。" | lindera tokenize --dict ipadic

注意事項:

  • アーカイブのバージョンは常に CLI のバージョンと一致します。リリースが公開されていない開発版のバージョンで実行すると HTTP 404 で失敗します。
  • 整合性は HTTPS トランスポートと ZIP アーカイブ内蔵のファイルごとの CRC32 チェックサムで検証されます。
  • 最大の辞書(ipadic-neologd)は約 305 MB のダウンロードで、展開時にはその約 2 倍の空きディスク容量が必要です。

train

注釈付きコーパスデータから新しい形態素解析モデルを学習します。この機能を使用するには、train 機能フラグを有効にしてビルドする必要があります。(train 機能フラグはデフォルトで有効になっています。)

学習パラメータ

  • --seed / -s: 重み付けを行うシード語彙ファイル(CSV形式)
  • --corpus / -c: 学習用コーパス(注釈付きテキスト)
  • --char-def / -C: 文字定義ファイル (char.def)
  • --unk-def / -u: 未知語定義ファイル (unk.def) - 重み付けの対象
  • --feature-def / -f: 素性定義ファイル (feature.def)
  • --rewrite-def / -r: 書換えルール定義ファイル (rewrite.def)
  • --output / -o: 出力モデルファイル
  • --lambda / -l: 正則化係数 (0.0-1.0) (デフォルト: 0.01)
  • --regularization / -R: 正則化の種類: l1, l2, または elasticnet (デフォルト: l1)
  • --elastic-net-l1-ratio: Elastic Net 正則化時のL1比率 (0.0-1.0)。--regularization elasticnet を指定した場合のみ使用されます (デフォルト: 0.5)
  • --max-iterations / -i: 学習の最大反復回数 (デフォルト: 100)
  • --max-threads / -t: 最大スレッド数 (デフォルトはCPUコア数。速度のみに影響し、学習結果は変わりません)

基本的なワークフロー

1. 学習用ファイルの準備

シード語彙ファイル (seed.csv):

シード語彙ファイルは、CRFモデルの学習に使用される初期辞書エントリを含みます。各行はカンマ区切りのフィールドを持つ単語エントリを表します:

  • 表層形
  • 左文脈ID
  • 右文脈ID
  • 単語コスト
  • 品詞タグ(複数のフィールド)
  • 原形
  • 読み(カタカナ)
  • 発音

注意: 正確なフィールド定義は辞書フォーマット(IPADIC, UniDic, ko-dic, CC-CEDICT)によって異なります。詳細は各辞書のフォーマット仕様を参照してください。

外国,0,0,0,名詞,一般,*,*,*,*,外国,ガイコク,ガイコク
人,0,0,0,名詞,接尾,一般,*,*,*,人,ジン,ジン

学習用コーパス (corpus.txt):

学習用コーパスファイルは、CRFモデルの学習に使用される注釈付きテキストデータを含みます。各行は以下で構成されます:

  • 表層形(単語)とそれに続くタブ文字
  • カンマ区切りの形態素素性(品詞タグ、原形、読み、発音)
  • 文は "EOS" (End Of Sentence) マーカーで区切られます
外国	名詞,一般,*,*,*,*,外国,ガイコク,ガイコク
人	名詞,接尾,一般,*,*,*,人,ジン,ジン
参政	名詞,サ変接続,*,*,*,*,参政,サンセイ,サンセイ
権	名詞,接尾,一般,*,*,*,権,ケン,ケン
EOS

ファイルフォーマットや高度な機能の詳細については、学習パイプライン を参照してください。

2. モデルの学習

lindera train \
  --seed ./resources/training/seed.csv \
  --corpus ./resources/training/corpus.txt \
  --unk-def ./resources/training/unk.def \
  --char-def ./resources/training/char.def \
  --feature-def ./resources/training/feature.def \
  --rewrite-def ./resources/training/rewrite.def \
  --output /tmp/lindera/training/model.dat \
  --lambda 0.01 \
  --max-iterations 100

3. 学習結果

学習済みモデルには以下が含まれます:

  • 既存単語: 新しく学習された重みを持つすべてのシード辞書レコード
  • 新語: シード辞書にはないがコーパスに含まれる単語(適切な重み付きで追加)

export

学習済みモデルファイルをLindera辞書フォーマットのファイルにエクスポートします。この機能を使用するには、train 機能フラグを有効にしてビルドする必要があります。

エクスポートパラメータ

  • --model / -m: 学習済みモデルファイル(.dat形式)のパス
  • --output / -o: 辞書ファイルの出力先ディレクトリ
  • --metadata: オプションの metadata.json ファイル(学習済みモデル情報で更新されます)
  • --cost-factor: 重みからコストへの変換係数を上書き(デフォルト: 学習済みモデルの値、通常は700)

出力ファイル

エクスポートコマンドは出力ディレクトリに以下の辞書ファイルを作成します:

  • lex.csv: 学習された重みを持つ語彙ファイル(MeCab互換の tocost() によるコスト変換)
  • matrix.def: 全 (right_id, left_id) ペアを網羅する密な接続コスト行列
  • unk.def: 未知語定義
  • char.def: 文字種定義
  • feature.def: 素性テンプレート定義(学習済みモデルからコピー)
  • rewrite.def: 素性リライトルール(学習済みモデルからコピー)
  • left-id.def: 左文脈IDから素性文字列へのマッピング
  • right-id.def: 右文脈IDから素性文字列へのマッピング
  • metadata.json: 更新されたメタデータファイル(--metadata オプションが指定された場合)

完全なワークフロー例

1. モデルの学習

lindera train \
  --seed ./resources/training/seed.csv \
  --corpus ./resources/training/corpus.txt \
  --unk-def ./resources/training/unk.def \
  --char-def ./resources/training/char.def \
  --feature-def ./resources/training/feature.def \
  --rewrite-def ./resources/training/rewrite.def \
  --output /tmp/lindera/training/model.dat \
  --lambda 0.01 \
  --max-iterations 100

2. 辞書フォーマットへのエクスポート

lindera export \
  --model /tmp/lindera/training/model.dat \
  --metadata ./resources/training/metadata.json \
  --output /tmp/lindera/training/dictionary

3. 辞書のビルド

lindera build \
  --src /tmp/lindera/training/dictionary \
  --dest /tmp/lindera/training/compiled_dictionary \
  --metadata /tmp/lindera/training/dictionary/metadata.json

4. 学習済み辞書の使用

echo "これは外国人参政権です。" | lindera tokenize \
  -d /tmp/lindera/training/compiled_dictionary

メタデータ更新機能

--metadata オプションが指定されると、エクスポートコマンドは以下の処理を行います:

  1. ベースとなる metadata.json ファイルを読み込み、既存の設定を保持します
  2. 特定のフィールドを学習済みモデルの値で更新します:
    • default_word_cost: 素性重みの中央値から計算された値
    • model_info: 素性数、ラベル数、行列サイズ、反復回数、正則化、バージョンを含む学習統計情報。タイムスタンプは書き込まれないため、同じモデルを 2 回エクスポートすると同一の出力になります
  3. 既存の設定を保持します(辞書名、文字エンコード設定、スキーマ定義、その他のユーザー定義設定など)

チュートリアル

このチュートリアルでは、Lindera CLIの基本的な使い方を、インストールから高度なテキスト処理まで順を追って説明します。

1. CLIのインストール

Lindera CLIをインストールします:

% cargo install lindera-cli

インストールの確認:

% lindera --help

2. 辞書のダウンロード

GitHub リリースページからビルド済みの IPADIC 辞書をダウンロードします。辞書は OS 標準のアプリケーションデータディレクトリにインストールされます:

% lindera download ipadic

利用可能な辞書名と保存場所についてはコマンドを参照してください。

3. 基本的なトークナイズ

ダウンロードした IPADIC 辞書を辞書名で参照して、日本語テキストをトークナイズします:

% echo "東京は日本の首都です。" | lindera tokenize \
  --dict ipadic

期待される出力:

東京    名詞,固有名詞,地域,一般,*,*,東京,トウキョウ,トーキョー
は      助詞,係助詞,*,*,*,*,は,ハ,ワ
日本    名詞,固有名詞,地域,国,*,*,日本,ニホン,ニホン
の      助詞,連体化,*,*,*,*,の,ノ,ノ
首都    名詞,一般,*,*,*,*,首都,シュト,シュト
です    助動詞,*,*,*,特殊・デス,基本形,です,デス,デス
。      記号,句点,*,*,*,*,。,。,。
EOS

4. 異なる出力形式を試す

Wakati 形式(分かち書きのみ)

% echo "東京は日本の首都です。" | lindera tokenize \
  --dict ipadic \
  --output wakati

期待される出力:

東京 は 日本 の 首都 です 。

JSON 形式(詳細情報)

% echo "東京は日本の首都です。" | lindera tokenize \
  --dict ipadic \
  --output json

バイトオフセット、品詞タグ、読みなどの詳細なトークン情報を含むJSON配列が出力されます。

5. Decompose モードの使用

Decompose モードは複合名詞を構成要素に分解します:

% echo "関西国際空港限定トートバッグ" | lindera tokenize \
  --dict ipadic \
  --mode decompose

期待される出力:

関西    名詞,固有名詞,地域,一般,*,*,関西,カンサイ,カンサイ
国際    名詞,一般,*,*,*,*,国際,コクサイ,コクサイ
空港    名詞,一般,*,*,*,*,空港,クウコウ,クーコー
限定    名詞,サ変接続,*,*,*,*,限定,ゲンテイ,ゲンテイ
トートバッグ    名詞,一般,*,*,*,*,*,*,*
EOS

Normal モードと比較すると、「関西国際空港」が1つのトークンのままになる点が異なります。

6. 文字フィルタとトークンフィルタの適用

Unicode正規化を行い、一般名詞のみを保持します:

% echo "Linderaは形態素解析エンジンです。" | lindera tokenize \
  --dict ipadic \
  --char-filter 'unicode_normalize:{"kind":"nfkc"}' \
  --token-filter 'japanese_keep_tags:{"tags":["名詞,一般","名詞,サ変接続","名詞,固有名詞,組織"]}'

期待される出力:

Lindera 名詞,固有名詞,組織,*,*,*,*,*,*
形態素  名詞,一般,*,*,*,*,形態素,ケイタイソ,ケイタイソ
解析    名詞,サ変接続,*,*,*,*,解析,カイセキ,カイセキ
エンジン        名詞,一般,*,*,*,*,エンジン,エンジン,エンジン
EOS

Unicode正規化により全角文字が半角に変換され、Token Filter により指定した品詞タグに一致するトークンのみが保持されます。

複数のフィルタを組み合わせることもできます:

% echo "すもももももももものうち" | lindera tokenize \
  --dict ipadic \
  --token-filter 'japanese_stop_tags:{"tags":["助詞,格助詞,一般","助詞,係助詞","助詞,連体化","助動詞"]}'

7. ユーザー辞書の使用

カスタム単語エントリを含むCSVファイル(例: my_dict.csv)を作成します:

東京スカイツリー,カスタム名詞,トウキョウスカイツリー

ユーザー辞書を使用してトークナイズします:

% echo "東京スカイツリーの最寄り駅はとうきょうスカイツリー駅です" | lindera tokenize \
  --dict ipadic \
  --user-dict ./my_dict.csv

ユーザー辞書がない場合、「東京スカイツリー」は複数のトークンに分割されます。ユーザー辞書を使用すると、1つのトークンとして認識されます。

ビルド済みのユーザー辞書の例については、以下を参照してください:

% echo "東京スカイツリーの最寄り駅はとうきょうスカイツリー駅です" | lindera tokenize \
  --dict ipadic \
  --user-dict ./resources/user_dict/ipadic_simple_userdic.csv

期待される出力:

東京スカイツリー        カスタム名詞,*,*,*,*,*,東京スカイツリー,トウキョウスカイツリー,*
の      助詞,連体化,*,*,*,*,の,ノ,ノ
最寄り駅        名詞,一般,*,*,*,*,最寄り駅,モヨリエキ,モヨリエキ
は      助詞,係助詞,*,*,*,*,は,ハ,ワ
とうきょうスカイツリー駅        カスタム名詞,*,*,*,*,*,とうきょうスカイツリー駅,トウキョウスカイツリーエキ,*
です    助動詞,*,*,*,特殊・デス,基本形,です,デス,デス
EOS

Lindera Python

Lindera Python は、PyO3 を使用して構築された Lindera 形態素解析エンジンの Python バインディングです。Python 3.10 以降をサポートし、Lindera の高性能なトークナイズ機能を Python エコシステムに提供します。

特徴

  • 多言語対応: 日本語(IPADIC、IPADIC NEologd、UniDic)、韓国語(ko-dic)、中国語(CC-CEDICT、Jieba)のテキストをトークナイズ
  • テキスト処理パイプライン: 文字フィルタとトークンフィルタを組み合わせて、柔軟な前処理・後処理が可能
  • CRF ベースの辞書学習: アノテーション付きコーパスからカスタム形態素解析モデルを学習(train feature が必要)
  • 複数のトークナイズモード: 解析粒度に応じた Normal モードと Decompose モード
  • N-best トークナイズ: コスト順にランク付けされた複数のトークナイズ候補を取得
  • ユーザー辞書: システム辞書をカスタム語彙で拡張

ドキュメント

インストール

PyPI からのインストール

ビルド済みホイールが PyPI で公開されています:

pip install lindera

[!NOTE] PyPI パッケージには辞書が含まれていません。下記の辞書の入手を参照してください。

辞書の入手

Lindera はパッケージに辞書を同梱していません。ビルド済み辞書を別途入手する必要があります。

GitHub Releases からのダウンロード

ビルド済み辞書は GitHub Releases ページから入手できます。辞書アーカイブをダウンロードしてローカルディレクトリに展開してください:

# 例: IPADIC 辞書のダウンロードと展開
curl -LO https://github.com/lindera/lindera/releases/download/<version>/lindera-ipadic-<version>.zip
unzip lindera-ipadic-<version>.zip -d /path/to/ipadic

ソースからのビルド

特定の feature フラグを有効にする必要がある場合など、ソースからビルドするには以下の前提条件が必要です:

  • Python 3.10 以降(3.14 まで)
  • Rust ツールチェーン -- rustup 経由でインストール
  • maturin -- Rust ベースの Python 拡張をビルドするための Python パッケージ

maturin を pip でインストールします:

pip install maturin

開発ビルド

lindera-python を開発モードでビルドしてインストールします:

cd lindera-python
maturin develop

または、プロジェクトの Makefile を使用します:

make python-develop

学習機能付きビルド

train feature を有効にすると、CRF ベースの辞書学習機能が利用可能になります。デフォルトで有効になっています:

maturin develop --features train

Feature フラグ

Feature説明デフォルト
trainCRF 学習機能有効
embed-ipadic日本語辞書(IPADIC)をバイナリに埋め込み無効
embed-unidic日本語辞書(UniDic)をバイナリに埋め込み無効
embed-sudachidict日本語辞書(SudachiDict)をバイナリに埋め込み無効
embed-ipadic-neologd日本語辞書(IPADIC NEologd)をバイナリに埋め込み無効
embed-ko-dic韓国語辞書(ko-dic)をバイナリに埋め込み無効
embed-cc-cedict中国語辞書(CC-CEDICT)をバイナリに埋め込み無効
embed-jieba中国語辞書(Jieba)をバイナリに埋め込み無効
embed-cjk全 CJK 辞書をバイナリに埋め込み(IPADIC、ko-dic、Jieba)無効

複数の feature を組み合わせることができます:

maturin develop --features "train,embed-ipadic,embed-ko-dic"

[!TIP] 辞書をバイナリに直接埋め込みたい場合(上級者向け)は、対応する embed-* feature フラグを有効にしてビルドし、embedded:// スキームでロードしてください:

dictionary = load_dictionary("embedded://ipadic")

詳細は Feature フラグ を参照してください。

インストールの確認

インストール後、Python で lindera が利用可能であることを確認します:

import lindera

print(lindera.version())

クイックスタート

このガイドでは、lindera-python を使用してテキストをトークナイズする方法を紹介します。

基本的なトークナイズ

トークナイザーの作成には TokenizerBuilder の使用を推奨します:

from lindera import TokenizerBuilder

builder = TokenizerBuilder()
builder.set_mode("normal")
builder.set_dictionary("/path/to/ipadic")
tokenizer = builder.build()

tokens = tokenizer.tokenize("関西国際空港限定トートバッグ")
for token in tokens:
    print(f"{token.surface}\t{','.join(token.details)}")

注意: ビルド済み辞書を GitHub Releases からダウンロードし、展開したディレクトリのパスを指定してください。

期待される出力:

関西国際空港    名詞,固有名詞,組織,*,*,*,関西国際空港,カンサイコクサイクウコウ,カンサイコクサイクーコー
限定    名詞,サ変接続,*,*,*,*,限定,ゲンテイ,ゲンテイ
トートバッグ    UNK

メソッドチェーン

TokenizerBuilder は簡潔な設定のためにメソッドチェーンをサポートしています:

from lindera import TokenizerBuilder

tokenizer = (
    TokenizerBuilder()
    .set_mode("normal")
    .set_dictionary("/path/to/ipadic")
    .build()
)

tokens = tokenizer.tokenize("すもももももももものうち")
for token in tokens:
    print(f"{token.surface}\t{token.get_detail(0)}")

トークンプロパティへのアクセス

各トークンは以下のプロパティを公開しています:

from lindera import TokenizerBuilder

tokenizer = TokenizerBuilder().set_dictionary("/path/to/ipadic").build()
tokens = tokenizer.tokenize("東京タワー")

for token in tokens:
    print(f"Surface: {token.surface}")
    print(f"Byte range: {token.byte_start}..{token.byte_end}")
    print(f"Position: {token.position}")
    print(f"Word ID: {token.word_id}")
    print(f"Unknown: {token.is_unknown}")
    print(f"Details: {token.details}")
    print()

N-best トークナイズ

コスト順にランク付けされた複数のトークナイズ候補を取得します:

from lindera import TokenizerBuilder

tokenizer = TokenizerBuilder().set_dictionary("/path/to/ipadic").build()
results = tokenizer.tokenize_nbest("すもももももももものうち", n=3)

for tokens, cost in results:
    surfaces = [t.surface for t in tokens]
    print(f"Cost {cost}: {' / '.join(surfaces)}")

Tokenizer API

TokenizerBuilder

TokenizerBuilder はビルダーパターンを使用して Tokenizer インスタンスを設定・構築します。

コンストラクタ

TokenizerBuilder()

デフォルト設定で新しいビルダーを作成します。

from lindera import TokenizerBuilder

builder = TokenizerBuilder()

TokenizerBuilder().from_file(file_path)

YAML ファイルから設定を読み込み、新しいビルダーを返します。segmentercharacter_filterstoken_filters を含む完全な例は lindera-python/resources/lindera.yml を参照してください。

builder = TokenizerBuilder().from_file("lindera.yml")

設定メソッド

すべてのセッターメソッドはメソッドチェーンのために self を返します。

set_mode(mode)

トークナイズモードを設定します。

  • "normal" -- 標準的なトークナイズ(デフォルト)
  • "decompose" -- 複合語をより小さな単位に分解
builder.set_mode("normal")

set_dictionary(path)

システム辞書のパスまたは URI を設定します。

# 埋め込み辞書を使用
builder.set_dictionary("embedded://ipadic")

# 外部辞書を使用
builder.set_dictionary("/path/to/dictionary")

set_user_dictionary(uri)

ユーザー辞書の URI を設定します。

builder.set_user_dictionary("/path/to/user_dictionary")

set_keep_whitespace(keep)

出力に空白トークンを含めるかどうかを制御します。

builder.set_keep_whitespace(True)

append_character_filter(kind, args=None)

前処理パイプラインに文字フィルタを追加します。

builder.append_character_filter("unicode_normalize", {"kind": "nfkc"})

append_token_filter(kind, args=None)

後処理パイプラインにトークンフィルタを追加します。

builder.append_token_filter("lowercase", {})

ビルド

build()

設定された内容で Tokenizer をビルドして返します。

tokenizer = builder.build()

Tokenizer

Tokenizer はテキストに対して形態素解析を行います。

Tokenizer の作成

Tokenizer(dictionary, mode="normal", user_dictionary=None)

読み込み済みの辞書から直接トークナイザーを作成します。

from lindera import Tokenizer, load_dictionary

dictionary = load_dictionary("embedded://ipadic")
tokenizer = Tokenizer(dictionary, mode="normal")

Tokenizer メソッド

tokenize(text)

入力テキストをトークナイズし、Token オブジェクトのリストを返します。

tokens = tokenizer.tokenize("形態素解析")

パラメータ:

名前説明
textstrトークナイズするテキスト

戻り値: list[Token]

tokenize_surfaces(text)

入力テキストをトークナイズし、トークンの surface のみを文字列のリストとして返します。分かち書き用途の高速パスです。Token オブジェクトを生成せず、形態素の詳細情報もロードしないため、surface 文字列だけが必要な場合は tokenize より大幅に高速です。結果は [t.surface for t in tokenizer.tokenize(text)] と一致します。

surfaces = tokenizer.tokenize_surfaces("形態素解析")
# ["形態素", "解析"]

パラメータ:

名前説明
textstrトークナイズするテキスト

戻り値: list[str]

tokenize_nbest(text, n, unique=False, cost_threshold=None)

N-best トークナイズ結果を返します。各結果はトータルパスコストとペアになっています。

results = tokenizer.tokenize_nbest("すもももももももものうち", n=3)
for tokens, cost in results:
    print(cost, [t.surface for t in tokens])

パラメータ:

名前説明
textstrトークナイズするテキスト
nint返す結果の数
uniquebool結果の重複を排除(デフォルト: False
cost_thresholdint または None最良パスからの最大コスト差(デフォルト: None

戻り値: list[tuple[list[Token], int]]

Mode

Mode はトークナイズモードを表します。モードの確認や比較のためのスタンドアロンな ヘルパーとして提供されています。TokenizerBuilder.set_mode()Tokenizer の コンストラクタは、現在は Mode インスタンスではなく単純なモード文字列 ("normal" または "decompose")のみを受け付けます(下記 Penalty の制限事項 も参照)。

Mode の作成

Mode(mode_str=None)

Mode を作成します。"normal" / "Normal"(省略時のデフォルト)または "decompose" / "Decompose" を受け付けます。それ以外の値を指定すると ValueError が発生します。

from lindera import Mode

mode = Mode("normal")
mode = Mode("decompose")
mode = Mode()  # デフォルトは "normal"

メソッド

メソッド戻り値説明
__str__()str"normal" または "decompose"
__repr__()str例: "Mode.Normal"
is_normal()boolモードが "normal" の場合 True
is_decompose()boolモードが "decompose" の場合 True
mode = Mode("decompose")
str(mode)            # "decompose"
repr(mode)           # "Mode.Decompose"
mode.is_normal()      # False
mode.is_decompose()   # True

Penalty

Penalty"decompose" モードのセグメンテーションで使用される、長さに基づく ペナルティの閾値を設定します。

Penalty の作成

Penalty(kanji_penalty_length_threshold=2, kanji_penalty_length_penalty=3000, other_penalty_length_threshold=7, other_penalty_length_penalty=1700)

すべての引数は省略可能で、上記の値がデフォルトとして使用されます。

from lindera import Penalty

penalty = Penalty(
    kanji_penalty_length_threshold=2,
    kanji_penalty_length_penalty=3000,
    other_penalty_length_threshold=7,
    other_penalty_length_penalty=1700,
)

Penalty のプロパティ

4 つのフィールドはすべて取得・設定の両方をサポートしています:

プロパティデフォルト説明
kanji_penalty_length_thresholdint2ペナルティが適用される、漢字のみの表層形の長さの閾値
kanji_penalty_length_penaltyint3000閾値を超える漢字のみの表層形に加算されるコストペナルティ
other_penalty_length_thresholdint7漢字のみでない表層形にペナルティが適用される長さの閾値
other_penalty_length_penaltyint1700閾値を超える漢字のみでない表層形に加算されるコストペナルティ
penalty.kanji_penalty_length_threshold = 3
print(penalty.kanji_penalty_length_threshold)  # 3

現在の制限事項: 現時点では PenaltyTokenizerTokenizerBuilder に 渡す方法はありません。set_mode()Tokenizer のコンストラクタは単純な モード文字列のみを受け付け、内部的に "decompose" モードは常に Penalty の デフォルト値を使用します -- カスタムの Penalty インスタンスを作成しても、 トークナイズには反映されません。

Token

Token は単一の形態素トークンを表します。

プロパティ

プロパティ説明
surfacestrトークンの表層形
byte_startint元テキストでの開始バイト位置
byte_endint元テキストでの終了バイト位置
positionintトークンの位置インデックス
word_idint辞書の単語 ID
is_unknownbool辞書に登録されていない単語の場合 True
detailslist[str] または None形態素の詳細情報(品詞、読みなど)

Token メソッド

get_detail(index)

指定されたインデックスの詳細文字列を返します。インデックスが範囲外の場合は None を返します。

token = tokenizer.tokenize("東京")[0]
pos = token.get_detail(0)        # 例: "名詞"
subpos = token.get_detail(1)     # 例: "固有名詞"
reading = token.get_detail(7)    # 例: "トウキョウ"

パラメータ:

名前説明
indexintdetails リストへのゼロベースインデックス

戻り値: str または None

to_dict()

トークンをプレーンな dict として返します。各フィールドは Python の自然な型を 保つため、独自のエンコーダなしでシリアライズできます。

import json

data = tokenizer.tokenize("東京")[0].to_dict()
# {'surface': '東京', 'byte_start': 0, 'byte_end': 6, 'position': 0,
#  'word_id': 12345, 'is_unknown': False, 'details': [...]}

json.dumps([token.to_dict() for token in tokens])

戻り値: surfacebyte_startbyte_endpositionword_idis_unknowndetails をキーに持つ dict

details の構造は辞書によって異なります:

  • IPADIC: [品詞, 品詞細分類1, 品詞細分類2, 品詞細分類3, 活用型, 活用形, 原形, 読み, 発音]
  • UniDic: UniDic 仕様に準拠した詳細な形態素情報
  • ko-dic / CC-CEDICT / Jieba: 各辞書固有の詳細フォーマット

エラーハンドリング

Lindera Python の関数は、カスタムの例外型ではなく標準的な Python の例外を送出します:

  • IOErrorOSError のエイリアス) -- ファイルが存在しない、読み込めないなどの I/O 関連の失敗
  • ValueError -- それ以外のすべてのケース(不正な設定、パースエラー、 トークナイズの失敗など)
from lindera import load_dictionary

try:
    dictionary = load_dictionary("/path/that/does/not/exist")
except ValueError as e:
    print(f"Failed to load dictionary: {e}")

lindera.LinderaError クラスも登録されていますが、このクレート内のどの関数からも 送出されることはありません -- 手動で構築・送出する場合にのみ使用できます。この ライブラリのエラーを処理する際は、LinderaError ではなく IOError / ValueError(または一般的な Exception)をキャッチしてください。

辞書管理

Lindera Python は、形態素解析で使用する辞書の読み込み、ビルド、管理のための関数を提供します。

辞書の読み込み

システム辞書

load_dictionary(uri) を使用してシステム辞書を読み込みます。GitHub Releases からビルド済み辞書をダウンロードし、展開したディレクトリのパスを指定してください:

from lindera import load_dictionary

dictionary = load_dictionary("/path/to/ipadic")

埋め込み辞書(上級者向け) -- embed-* feature フラグ付きでビルドした場合、埋め込み辞書を使用できます:

dictionary = load_dictionary("embedded://ipadic")

読み込んだ Dictionary は、自身のメタデータも公開しています:

print(dictionary.metadata_name())      # 例: "ipadic"
print(dictionary.metadata_encoding())  # 例: "UTF-8"
metadata = dictionary.metadata()       # Metadata オブジェクト全体

これは、システム辞書と同じメタデータ(スキーマ、エンコーディングなど)を 共有する必要があるユーザー辞書を読み込む際に便利です。 lindera-python/examples/tokenize_with_userdict.py を参照してください:

from lindera import Tokenizer, load_dictionary, load_user_dictionary

dictionary = load_dictionary("embedded://ipadic")
metadata = dictionary.metadata()
user_dictionary = load_user_dictionary("/path/to/user_dictionary.csv", metadata)

tokenizer = Tokenizer(dictionary, mode="normal", user_dictionary=user_dictionary)

ユーザー辞書

ユーザー辞書はシステム辞書にカスタム語彙を追加します。

from lindera import load_user_dictionary, Metadata

metadata = Metadata()
user_dict = load_user_dictionary("/path/to/user_dictionary", metadata)

トークナイザーのビルド時にユーザー辞書を渡します:

from lindera import Tokenizer, load_dictionary, load_user_dictionary, Metadata

dictionary = load_dictionary("/path/to/ipadic")
metadata = Metadata()
user_dict = load_user_dictionary("/path/to/user_dictionary", metadata)

tokenizer = Tokenizer(dictionary, mode="normal", user_dictionary=user_dict)

または、ビルダー経由で設定します:

from lindera import TokenizerBuilder

tokenizer = (
    TokenizerBuilder()
    .set_dictionary("/path/to/ipadic")
    .set_user_dictionary("/path/to/user_dictionary")
    .build()
)

辞書のビルド

システム辞書のビルド

ソースファイルからシステム辞書をビルドします:

from lindera import build_dictionary, Metadata

metadata = Metadata(name="custom", encoding="UTF-8")
build_dictionary("/path/to/input_dir", "/path/to/output_dir", metadata)

入力ディレクトリには辞書のソースファイル(CSV レキシコン、matrix.def など)が含まれている必要があります。

ユーザー辞書のビルド

CSV ファイルからユーザー辞書をビルドします:

from lindera import build_user_dictionary, Metadata

metadata = Metadata()
build_user_dictionary("ipadic", "user_words.csv", "/path/to/output_dir", metadata)

metadata パラメータは省略可能です。省略した場合はデフォルトのメタデータ値が使用されます:

build_user_dictionary("ipadic", "user_words.csv", "/path/to/output_dir")

注意: 最初の引数(上記の "ipadic")は現在未使用で、将来のために予約されて いるものです -- ビルドの選択や設定には一切影響しません。ビルドの挙動は metadata(特に metadata.user_dictionary_schema)によってのみ制御されます。 現時点では任意の文字列を渡すことができます。

Metadata

Metadata クラスは辞書のパラメータを設定します。

Metadata の作成

from lindera import Metadata

# デフォルトのメタデータ
metadata = Metadata()

# カスタムメタデータ
metadata = Metadata(
    name="my_dictionary",
    encoding="UTF-8",
    default_word_cost=-10000,
)

JSON からの読み込み

metadata = Metadata.from_json_file("metadata.json")

プロパティ

プロパティデフォルト説明
namestr"default"辞書名
encodingstr"UTF-8"文字エンコーディング
default_word_costint-10000未知語のデフォルトコスト
default_left_context_idint1288デフォルトの左文脈 ID
default_right_context_idint1288デフォルトの右文脈 ID
default_field_valuestr"*"欠損フィールドのデフォルト値
flexible_csvboolFalse柔軟な CSV パースを許可
skip_invalid_cost_or_idboolFalse無効なコストまたは ID のエントリーをスキップ
normalize_detailsboolFalse形態素の詳細情報を正規化
dictionary_schemaSchemaIPADIC スキーマメイン辞書のスキーマ
user_dictionary_schemaSchema最小スキーマユーザー辞書のスキーマ

すべてのプロパティは取得と設定の両方をサポートしています:

metadata = Metadata()
metadata.name = "custom_dict"
metadata.encoding = "EUC-JP"
print(metadata.name)  # "custom_dict"

to_dict()

メタデータの辞書表現を返します:

metadata = Metadata(name="test")
print(metadata.to_dict())

Schema

SchemaFieldDefinitionFieldType は、辞書エントリーのフィールド構成を 表します。スキーマは Metadata.dictionary_schemaMetadata.user_dictionary_schema(上記の表を参照)で使用されます。

FieldType

FieldType は単一フィールドの種別を列挙します:

  • FieldType.Surface -- 表層形(単語のテキスト)
  • FieldType.LeftContextId -- 左文脈 ID
  • FieldType.RightContextId -- 右文脈 ID
  • FieldType.Cost -- 単語コスト
  • FieldType.Custom -- その他の辞書固有フィールド

FieldDefinition

FieldDefinition はスキーマ内の単一フィールドを表します。

FieldDefinition(index, name, field_type, description=None)

from lindera import FieldDefinition, FieldType

field = FieldDefinition(0, "surface", FieldType.Surface, "Surface form")

プロパティ(読み取り専用):

プロパティ説明
indexintスキーマ内でのフィールドの位置(0 始まり)
namestrフィールド名
field_typeFieldTypeフィールドの種別
descriptionstr または None任意の説明文

Schema の作成

Schema はフィールド名の順序付きリストを保持し、フィールド名とインデックス間の 相互参照を提供します。

Schema(fields)

フィールド名のリストからスキーマを作成します。

from lindera import Schema

schema = Schema([
    "surface",
    "left_context_id",
    "right_context_id",
    "cost",
    "major_pos",
    "reading",
])

Schema.create_default()

組み込みのデフォルトスキーマを返す静的メソッドです。13 個のフィールドから成り、 IPADIC 形式のレイアウトに対応します(surface, left_context_id, right_context_id, cost, major_pos, pos_detail_1, pos_detail_2, pos_detail_3, conjugation_type, conjugation_form, base_form, reading, pronunciation)。

schema = Schema.create_default()

Schema のメソッドとプロパティ

メンバー戻り値説明
fields(プロパティ)list[str]すべてのフィールド名(順序どおり)
field_count()intフィールドの総数
get_field_index(name)int または Nonename という名前のフィールドのインデックス
get_field_name(index)str または Noneindex のフィールド名
get_custom_fields()list[str]4 つの固定フィールド(surface, left_context_id, right_context_id, cost)以降のフィールド名
get_field_by_name(name)FieldDefinition または Nonename の完全なフィールド定義
validate_record(record)Nonerecord がスキーマと一致しない場合 ValueError を送出
__len__()intfield_count() と同じ
schema = Schema.create_default()

schema.field_count()               # 13
schema.get_field_index("cost")     # 3
schema.get_field_name(0)           # "surface"
schema.get_custom_fields()         # ["major_pos", "pos_detail_1", ..., "pronunciation"]
len(schema)                        # 13

field = schema.get_field_by_name("surface")
print(field.index, field.name, field.field_type)  # 0 surface FieldType.Surface

schema.validate_record([
    "東京", "1288", "1288", "100",
    "名詞", "固有名詞", "地域", "一般", "*", "*",
    "東京", "トウキョウ", "トーキョー",
])

テキスト処理パイプライン

Lindera Python は、トークナイズ前に文字フィルタを適用し、トークナイズ後にトークンフィルタを適用する、組み合わせ可能なテキスト処理パイプラインをサポートしています。フィルタは TokenizerBuilder に追加され、追加された順序で実行されます。

Input Text
  --> Character Filters (preprocessing)
  --> Tokenization
  --> Token Filters (postprocessing)
  --> Output Tokens

文字フィルタ

文字フィルタはトークナイズ前に入力テキストを変換します。

unicode_normalize

入力テキストに Unicode 正規化を適用します。

from lindera import TokenizerBuilder

tokenizer = (
    TokenizerBuilder()
    .set_dictionary("embedded://ipadic")
    .append_character_filter("unicode_normalize", {"kind": "nfkc"})
    .build()
)

サポートされる正規化形式: "nfc""nfkc""nfd""nfkd"

mapping

マッピングテーブルに従って文字や文字列を置換します。

tokenizer = (
    TokenizerBuilder()
    .set_dictionary("embedded://ipadic")
    .append_character_filter("mapping", {
        "mapping": {
            "\u30fc": "-",
            "\uff5e": "~",
        }
    })
    .build()
)

japanese_iteration_mark

日本語の踊り字(繰り返し記号)を完全な形に展開します。

tokenizer = (
    TokenizerBuilder()
    .set_dictionary("embedded://ipadic")
    .append_character_filter("japanese_iteration_mark", {
        "normalize_kanji": True,
        "normalize_kana": True,
    })
    .build()
)

トークンフィルタ

トークンフィルタはトークナイズ後にトークンを変換または除去します。

lowercase

トークンの表層形を小文字に変換します。

tokenizer = (
    TokenizerBuilder()
    .set_dictionary("embedded://ipadic")
    .append_token_filter("lowercase", {})
    .build()
)

japanese_base_form

辞書の形態素情報を使用して、活用形を基本形(辞書形)に置換します。

tokenizer = (
    TokenizerBuilder()
    .set_dictionary("embedded://ipadic")
    .append_token_filter("japanese_base_form", {})
    .build()
)

japanese_stop_tags

指定されたタグに一致する品詞のトークンを除去します。

tokenizer = (
    TokenizerBuilder()
    .set_dictionary("embedded://ipadic")
    .append_token_filter("japanese_stop_tags", {
        "tags": ["助詞,格助詞,一般", "助詞,係助詞", "助詞,連体化", "助動詞"],
    })
    .build()
)

タグは 4 階層のカンマ区切りに正規化され(不足分は * で補完)、各トークンの先頭 4 つの品詞詳細と完全一致で比較されます。IPADIC の助詞トークンは必ず 助詞,係助詞 のようにサブカテゴリを持つため、助詞 単独では一致しません。一方、助動詞はサブカテゴリを持たないため(助動詞,*,*,*)、助動詞 単独で一致します。

japanese_keep_tags

指定されたタグに一致する品詞のトークンのみを保持します。その他のトークンはすべて除去されます。

tokenizer = (
    TokenizerBuilder()
    .set_dictionary("embedded://ipadic")
    .append_token_filter("japanese_keep_tags", {
        "tags": ["名詞,一般"],
    })
    .build()
)

パイプラインの完全な例

以下の例では、複数の文字フィルタとトークンフィルタを1つのパイプラインに組み合わせています:

from lindera import TokenizerBuilder

tokenizer = (
    TokenizerBuilder()
    .set_mode("normal")
    .set_dictionary("embedded://ipadic")
    # Preprocessing
    .append_character_filter("unicode_normalize", {"kind": "nfkc"})
    .append_character_filter("japanese_iteration_mark", {
        "normalize_kanji": True,
        "normalize_kana": True,
    })
    # Postprocessing
    .append_token_filter("japanese_base_form", {})
    .append_token_filter("japanese_stop_tags", {
        "tags": ["助詞,格助詞,一般", "助詞,係助詞", "助詞,連体化", "助動詞", "記号,句点", "記号,読点"],
    })
    .append_token_filter("lowercase", {})
    .build()
)

tokens = tokenizer.tokenize("Linderaは形態素解析を行うライブラリです。")
for token in tokens:
    print(f"{token.surface}\t{','.join(token.details)}")

このパイプラインでは:

  1. unicode_normalize が全角文字を半角に変換(NFKC 正規化)
  2. japanese_iteration_mark が踊り字を展開
  3. japanese_base_form が活用形のトークンを基本形に変換
  4. japanese_stop_tags が助詞(格助詞・係助詞・連体化)・助動詞・句読点を除去
  5. lowercase がアルファベットを小文字に正規化

学習

Lindera Python は、アノテーション付きコーパスからカスタム CRF ベースの形態素解析モデルを学習する機能をサポートしています。この機能には train feature が必要です。

前提条件

train feature を有効にして lindera-python をビルドします(デフォルトで有効):

maturin develop --features train

モデルの学習

lindera.train() を使用して、種辞書とアノテーション付きコーパスから CRF モデルを学習します:

import lindera

lindera.train(
    seed="resources/training/seed.csv",
    corpus="resources/training/corpus.txt",
    char_def="resources/training/char.def",
    unk_def="resources/training/unk.def",
    feature_def="resources/training/feature.def",
    rewrite_def="resources/training/rewrite.def",
    output="/tmp/model.dat",
    lambda_=0.01,
    max_iter=100,
    max_threads=4,
)

学習パラメータ

パラメータデフォルト説明
seedstr必須種辞書ファイルのパス(CSV 形式)
corpusstr必須アノテーション付き学習コーパスのパス
char_defstr必須文字定義ファイルのパス(char.def)
unk_defstr必須未知語定義ファイルのパス(unk.def)
feature_defstr必須素性定義ファイルのパス(feature.def)
rewrite_defstr必須書き換えルール定義ファイルのパス(rewrite.def)
outputstr必須学習済みモデルファイルの出力パス
lambda_float0.01L1 正則化コスト(0.0--1.0)
max_iterint100最大学習イテレーション数
max_threadsint または NoneNoneスレッド数(None = CPU コア数を自動検出)

学習済みモデルのエクスポート

学習後、lindera.export() を使用してモデルを辞書ソースファイルにエクスポートします:

import lindera

lindera.export(
    model="/tmp/model.dat",
    output="/tmp/dictionary_source",
    metadata="resources/training/metadata.json",
)

エクスポートパラメータ

パラメータデフォルト説明
modelstr必須学習済みモデルファイルのパス(.dat)
outputstr必須辞書ソースファイルの出力ディレクトリ
metadatastr または NoneNoneベースとなる metadata.json ファイルのパス

エクスポートにより、出力ディレクトリに以下のファイルが作成されます:

  • lex.csv -- 学習済みコスト付きのレキシコンエントリー
  • matrix.def -- 連接コスト行列
  • unk.def -- 未知語定義
  • char.def -- 文字カテゴリ定義
  • metadata.json -- 更新されたメタデータ(metadata パラメータ指定時)

完全なワークフロー

カスタム辞書の学習と使用の完全なワークフロー:

import lindera

# Step 1: Train the CRF model
lindera.train(
    seed="resources/training/seed.csv",
    corpus="resources/training/corpus.txt",
    char_def="resources/training/char.def",
    unk_def="resources/training/unk.def",
    feature_def="resources/training/feature.def",
    rewrite_def="resources/training/rewrite.def",
    output="/tmp/model.dat",
    lambda_=0.01,
    max_iter=100,
)

# Step 2: Export to dictionary source files
lindera.export(
    model="/tmp/model.dat",
    output="/tmp/dictionary_source",
    metadata="resources/training/metadata.json",
)

# Step 3: Build the dictionary from exported source files
metadata = lindera.Metadata.from_json_file("/tmp/dictionary_source/metadata.json")
lindera.build_dictionary("/tmp/dictionary_source", "/tmp/dictionary", metadata)

# Step 4: Use the trained dictionary
tokenizer = (
    lindera.TokenizerBuilder()
    .set_dictionary("/tmp/dictionary")
    .set_mode("normal")
    .build()
)

tokens = tokenizer.tokenize("形態素解析のテスト")
for token in tokens:
    print(f"{token.surface}\t{','.join(token.details)}")

Lindera Node.js

Lindera Node.js は、NAPI-RS を使用して構築された Lindera 形態素解析エンジンの Node.js バインディングです。Node.js 18 以降をサポートし、Lindera の高性能なトークナイズ機能を Node.js エコシステムに提供します。

特徴

  • 多言語対応: 日本語(IPADIC、IPADIC NEologd、UniDic)、韓国語(ko-dic)、中国語(CC-CEDICT、Jieba)のテキストをトークナイズ
  • テキスト処理パイプライン: 文字フィルタとトークンフィルタを組み合わせて、柔軟な前処理・後処理が可能
  • CRF ベースの辞書学習: アノテーション付きコーパスからカスタム形態素解析モデルを学習(train feature が必要)
  • 複数のトークナイズモード: 解析粒度に応じた Normal モードと Decompose モード
  • N-best トークナイズ: コスト順にランク付けされた複数のトークナイズ候補を取得
  • ユーザー辞書: システム辞書をカスタム語彙で拡張
  • TypeScript サポート: 完全な型定義を同梱

ドキュメント

インストール

npm からのインストール

ビルド済みパッケージが npm で公開予定です:

npm install lindera

[!NOTE] npm パッケージには辞書が含まれていません。下記の辞書の入手を参照してください。 ブラウザ/WASM での利用には lindera-wasm を参照してください。

ソースからのビルド

前提条件

  • Node.js 18 以降(LTS バージョン推奨)
  • Rust ツールチェーン -- rustup 経由でインストール
  • NAPI-RS CLI -- Rust で Node.js ネイティブアドオンをビルドするための CLI ツール

NAPI-RS CLI をグローバルにインストールします:

npm install -g @napi-rs/cli

辞書の入手

Lindera はパッケージに辞書を同梱していません。ビルド済み辞書を別途入手する必要があります。

GitHub Releases からのダウンロード

ビルド済み辞書は GitHub Releases ページから入手できます。辞書アーカイブをダウンロードしてローカルディレクトリに展開してください:

# 例: IPADIC 辞書のダウンロードと展開
curl -LO https://github.com/lindera/lindera/releases/download/<version>/lindera-ipadic-<version>.zip
unzip lindera-ipadic-<version>.zip -d /path/to/ipadic

開発ビルド

lindera-nodejs を開発モードでビルドします:

cd lindera-nodejs
npm install
npm run build

または、プロジェクトの Makefile を使用します:

make nodejs-develop

学習機能付きビルド

train feature を有効にすると、CRF ベースの辞書学習機能が利用可能になります。デフォルトで有効になっています:

npm run build -- --features train

Feature フラグ

Feature説明デフォルト
trainCRF 学習機能有効
embed-ipadic日本語辞書(IPADIC)をバイナリに埋め込み無効
embed-unidic日本語辞書(UniDic)をバイナリに埋め込み無効
embed-sudachidict日本語辞書(SudachiDict)をバイナリに埋め込み無効
embed-ipadic-neologd日本語辞書(IPADIC NEologd)をバイナリに埋め込み無効
embed-ko-dic韓国語辞書(ko-dic)をバイナリに埋め込み無効
embed-cc-cedict中国語辞書(CC-CEDICT)をバイナリに埋め込み無効
embed-jieba中国語辞書(Jieba)をバイナリに埋め込み無効
embed-cjk全 CJK 辞書をバイナリに埋め込み(IPADIC、ko-dic、Jieba)無効

複数の feature を組み合わせることができます:

npm run build -- --features "train,embed-ipadic,embed-ko-dic"

[!TIP] 辞書をバイナリに直接埋め込みたい場合(上級者向け)は、対応する embed-* feature フラグを有効にしてビルドし、embedded:// スキームでロードしてください:

const dictionary = loadDictionary("embedded://ipadic");

詳細は Feature フラグ を参照してください。

インストールの確認

インストール後、Node.js で lindera が利用可能であることを確認します:

const lindera = require("lindera");

console.log(lindera.version());

[!NOTE] npm パッケージ linderaexports マップは types / import / require の各条件を宣言しているため、 CommonJS(require("lindera"))からも ES モジュール(import { TokenizerBuilder } from "lindera")からも そのまま読み込めます。

最小の ESM の例:

import { TokenizerBuilder } from "lindera";

console.log(typeof TokenizerBuilder); // "function"

クイックスタート

このガイドでは、lindera-nodejs を使用してテキストをトークナイズする方法を紹介します。

基本的なトークナイズ

トークナイザーの作成には TokenizerBuilder の使用を推奨します:

const { TokenizerBuilder } = require("lindera");

const builder = new TokenizerBuilder();
builder.setMode("normal");
builder.setDictionary("/path/to/ipadic");
const tokenizer = builder.build();

const tokens = tokenizer.tokenize("関西国際空港限定トートバッグ");
for (const token of tokens) {
  console.log(`${token.surface}\t${token.details.join(",")}`);
}

注意: ビルド済み辞書を GitHub Releases からダウンロードし、展開したディレクトリのパスを指定してください。

期待される出力:

関西国際空港    名詞,固有名詞,組織,*,*,*,関西国際空港,カンサイコクサイクウコウ,カンサイコクサイクーコー
限定    名詞,サ変接続,*,*,*,*,限定,ゲンテイ,ゲンテイ
トートバッグ    名詞,一般,*,*,*,*,*,*,*

メソッドチェーン

すべてのセッターはビルダー自身(this)を返すため、設定をチェーンして記述できます:

const { TokenizerBuilder } = require("lindera");

const tokenizer = new TokenizerBuilder()
  .setMode("normal")
  .setDictionary("/path/to/ipadic")
  .build();

const tokens = tokenizer.tokenize("すもももももももものうち");
for (const token of tokens) {
  console.log(`${token.surface}\t${token.details[0]}`);
}

1 文ずつセッターを呼び出すスタイルもそのまま使えます。どちらのスタイルでも同じビルダーを設定します。

トークンプロパティへのアクセス

各トークンは以下のプロパティを公開しています:

const { TokenizerBuilder } = require("lindera");

const builder = new TokenizerBuilder();
builder.setDictionary("/path/to/ipadic");
const tokenizer = builder.build();

const tokens = tokenizer.tokenize("東京タワー");
for (const token of tokens) {
  console.log(`Surface: ${token.surface}`);
  console.log(`Byte range: ${token.byteStart}..${token.byteEnd}`);
  console.log(`Position: ${token.position}`);
  console.log(`Word ID: ${token.wordId}`);
  console.log(`Unknown: ${token.isUnknown}`);
  console.log(`Details: ${token.details}`);
  console.log();
}

N-best トークナイズ

コスト順にランク付けされた複数のトークナイズ候補を取得します:

const { TokenizerBuilder } = require("lindera");

const builder = new TokenizerBuilder();
builder.setDictionary("/path/to/ipadic");
const tokenizer = builder.build();

const results = tokenizer.tokenizeNbest("すもももももももものうち", 3);
for (const { tokens, cost } of results) {
  const surfaces = tokens.map((t) => t.surface);
  console.log(`Cost ${cost}: ${surfaces.join(" / ")}`);
}

TypeScript

Lindera Node.js には TypeScript の型定義が同梱されています。すべてのクラスと関数に完全な型が付いています。npm パッケージ linderaexports マップは require / import の両方の条件を宣言しているため、CommonJS からも ES モジュールからもそのまま読み込めます。以下のサンプルは moduleResolution: node16 + strict でコンパイルできることを確認済みです:

import type { Token } from "lindera";
import { TokenizerBuilder } from "lindera";

const builder = new TokenizerBuilder();
builder.setMode("normal");
builder.setDictionary("/path/to/ipadic");
const tokenizer = builder.build();

const tokens: Token[] = tokenizer.tokenize("形態素解析");
for (const token of tokens) {
  console.log(`${token.surface}: ${token.details.join(",")}`);
}

Tokenizer API

TokenizerBuilder

TokenizerBuilder はビルダーパターンを使用して Tokenizer インスタンスを設定・構築します。

コンストラクタ

new TokenizerBuilder()

デフォルト設定で新しいビルダーを作成します。

const { TokenizerBuilder } = require("lindera");

const builder = new TokenizerBuilder();

new TokenizerBuilder().fromFile(filePath)

YAML ファイルから設定を読み込み、新しいビルダーを返します。

const builder = new TokenizerBuilder().fromFile("lindera.yml");

辞書、文字フィルタ、トークンフィルタの設定を含む完全な設定ファイルの例は lindera-nodejs/resources/lindera.yml を参照してください。

設定メソッド

すべてのセッターメソッドはビルダー自身(this)を返すため、メソッドチェーンでも 1 文ずつの呼び出しでも設定できます。

setMode(mode)

トークナイズモードを設定します。

  • "normal" -- 標準的なトークナイズ(デフォルト)
  • "decompose" -- 複合語をより小さな単位に分解
builder.setMode("normal");

setDictionary(path)

システム辞書のパスまたは URI を設定します。

// 埋め込み辞書を使用
builder.setDictionary("embedded://ipadic");

// 外部辞書を使用
builder.setDictionary("/path/to/dictionary");

setUserDictionary(uri)

ユーザー辞書の URI を設定します。

builder.setUserDictionary("/path/to/user_dictionary");

setKeepWhitespace(keep)

出力に空白トークンを含めるかどうかを制御します。

builder.setKeepWhitespace(true);

appendCharacterFilter(kind, args?)

前処理パイプラインに文字フィルタを追加します。

builder.appendCharacterFilter("unicode_normalize", { kind: "nfkc" });

appendTokenFilter(kind, args?)

後処理パイプラインにトークンフィルタを追加します。

builder.appendTokenFilter("lowercase", {});

ビルド

build()

設定された内容で Tokenizer をビルドして返します。

const tokenizer = builder.build();

Tokenizer

Tokenizer はテキストに対して形態素解析を行います。

Tokenizer の作成

new Tokenizer(dictionary, mode?, userDictionary?)

読み込み済みの辞書から直接トークナイザーを作成します。

const { Tokenizer, loadDictionary } = require("lindera");

const dictionary = loadDictionary("embedded://ipadic");
const tokenizer = new Tokenizer(dictionary, "normal");

Tokenizer メソッド

tokenize(text)

入力テキストをトークナイズし、プレーンなトークンオブジェクトの配列を返します。

const tokens = tokenizer.tokenize("形態素解析");

パラメータ:

名前説明
textstringトークナイズするテキスト

戻り値: Token[]

トークンはクラスのインスタンスではなくプレーンな JavaScript オブジェクトです。そのため通常の GC で回収され、JSON.stringifystructuredClone、worker への転送を変換なしで通過します。詳細は Token を参照してください。

tokenizeSurfaces(text)

入力テキストをトークナイズし、トークンの surface のみを文字列の配列として返します。分かち書き用途の高速パスです。トークンオブジェクトを構築せず、形態素の詳細情報もロードしないため、surface 文字列だけが必要な場合は tokenize より大幅に高速です。結果は tokenizer.tokenize(text).map((t) => t.surface) と一致します。

const surfaces = tokenizer.tokenizeSurfaces("形態素解析");
// ["形態素", "解析"]

パラメータ:

名前説明
textstringトークナイズするテキスト

戻り値: string[]

tokenizeNbest(text, n, unique?, costThreshold?)

N-best トークナイズ結果を返します。各結果はトークン配列とトータルパスコストを含みます。

const results = tokenizer.tokenizeNbest("すもももももももものうち", 3);
for (const { tokens, cost } of results) {
  console.log(cost, tokens.map((t) => t.surface));
}

パラメータ:

名前説明
textstringトークナイズするテキスト
nnumber返す結果の数
uniqueboolean結果の重複を排除(デフォルト: false
costThresholdnumber | undefined最良パスからの最大コスト差(デフォルト: undefined

戻り値: NbestResult[]。各 NbestResult{ tokens: Token[], cost: number } です。

Token

Token は単一の形態素トークンを表すプレーンオブジェクトです。クラスではなく TypeScript の interface であり、メソッドもプロトタイプも持ちません。 各フィールドは直接読み出します。

プロパティ

プロパティ説明
surfacestringトークンの表層形
byteStartnumber元テキストでの開始バイト位置
byteEndnumber元テキストでの終了バイト位置
positionnumberトークンの位置インデックス
wordIdnumber辞書の単語 ID
isUnknownboolean辞書に登録されていない単語の場合 true
detailsstring[]形態素の詳細情報(品詞、読みなど)

詳細情報の読み出し

details を直接インデックスします。範囲外のインデックスは undefined になります。

const token = tokenizer.tokenize("東京")[0];
const pos = token.details[0];      // 例: "名詞"
const subpos = token.details[1];   // 例: "固有名詞"
const reading = token.details[7];  // 例: "トウキョウ"

details の構造は辞書によって異なります:

  • IPADIC: [品詞, 品詞細分類1, 品詞細分類2, 品詞細分類3, 活用型, 活用形, 原形, 読み, 発音]
  • UniDic: UniDic 仕様に準拠した詳細な形態素情報
  • ko-dic / CC-CEDICT / Jieba: 各辞書固有の詳細フォーマット

Mode と Penalty

ModePenalty は npm パッケージ lindera からエクスポートされていますが、現状どの公開 API にも接続されていませんTokenizerBuilder.setMode() / Tokenizer のコンストラクタは 単純なモード文字列("normal" または "decompose")のみを受け取り、decompose モードは 内部的に常にデフォルトのペナルティ設定を使用します。これらの型は完全性のためにここで 説明していますが、JavaScript からペナルティの挙動をカスタマイズする用途にはまだ使用できません。

Mode

2つの値を持つ文字列列挙型です:

  • Mode.Normal -- 辞書コストに基づく標準的なトークナイズ
  • Mode.Decompose -- ペナルティベースの複合語分解

Penalty

decompose モードで使用されるペナルティパラメータを表すオブジェクトです:

プロパティ説明
kanjiPenaltyLengthThresholdnumberペナルティを適用する前の漢字列の長さの閾値(デフォルト: 2
kanjiPenaltyLengthPenaltynumber長い漢字列に対するペナルティ値(デフォルト: 3000
otherPenaltyLengthThresholdnumberペナルティを適用する前のその他の文字列の長さの閾値(デフォルト: 7
otherPenaltyLengthPenaltynumber長いその他の文字列に対するペナルティ値(デフォルト: 1700

辞書管理

Lindera Node.js は、形態素解析で使用する辞書の読み込み、ビルド、管理のための関数を提供します。

辞書の読み込み

システム辞書

loadDictionary(uri) を使用してシステム辞書を読み込みます。GitHub Releases からビルド済み辞書をダウンロードし、展開したディレクトリのパスを指定してください:

const { loadDictionary } = require("lindera");

const dictionary = loadDictionary("/path/to/ipadic");

埋め込み辞書(上級者向け) -- embed-* feature フラグ付きでビルドした場合、埋め込み辞書を使用できます:

const dictionary = loadDictionary("embedded://ipadic");

Dictionary には、読み込んだ辞書のメタデータを参照するための読み取り専用アクセサがいくつか用意されています:

console.log(dictionary.metadataName());     // 例: "ipadic"
console.log(dictionary.metadataEncoding()); // 例: "UTF-8"

const metadata = dictionary.metadata(); // Metadata オブジェクト全体
console.log(metadata.defaultWordCost);

ユーザー辞書

ユーザー辞書はシステム辞書にカスタム語彙を追加します。

const { loadUserDictionary, Metadata } = require("lindera");

const metadata = new Metadata();
const userDict = loadUserDictionary("/path/to/user_dictionary", metadata);

トークナイザーのビルド時にユーザー辞書を渡します:

const { Tokenizer, loadDictionary, loadUserDictionary, Metadata } = require("lindera");

const dictionary = loadDictionary("/path/to/ipadic");
const metadata = new Metadata();
const userDict = loadUserDictionary("/path/to/user_dictionary", metadata);

const tokenizer = new Tokenizer(dictionary, "normal", userDict);

または、ビルダー経由で設定します:

const { TokenizerBuilder } = require("lindera");

const builder = new TokenizerBuilder();
builder.setDictionary("/path/to/ipadic");
builder.setUserDictionary("/path/to/user_dictionary");
const tokenizer = builder.build();

辞書のビルド

システム辞書のビルド

ソースファイルからシステム辞書をビルドします:

const { buildDictionary, Metadata } = require("lindera");

const metadata = new Metadata({ name: "custom", encoding: "UTF-8" });
buildDictionary("/path/to/input_dir", "/path/to/output_dir", metadata);

入力ディレクトリには辞書のソースファイル(CSV レキシコン、matrix.def など)が含まれている必要があります。

ユーザー辞書のビルド

CSV ファイルからユーザー辞書をビルドします:

const { buildUserDictionary, Metadata } = require("lindera");

const metadata = new Metadata();
buildUserDictionary("ipadic", "user_words.csv", "/path/to/output_dir", metadata);

metadata パラメータは省略可能です。省略した場合はデフォルトのメタデータ値が使用されます:

buildUserDictionary("ipadic", "user_words.csv", "/path/to/output_dir");

[!NOTE] 第一引数(kind)は現状未使用です -- 将来の拡張のために予約されており、ビルド結果には 影響しません。現時点では任意の文字列を渡しても問題ありません。

Metadata

Metadata クラスは辞書のパラメータを設定します。

Metadata の作成

const { Metadata } = require("lindera");

// デフォルトのメタデータ
const metadata = new Metadata();

// カスタムメタデータ
const metadata = new Metadata({
  name: "my_dictionary",
  encoding: "UTF-8",
  defaultWordCost: -10000,
});

JSON からの読み込み

const metadata = Metadata.fromJsonFile("metadata.json");

プロパティ

プロパティデフォルト説明
namestring"default"辞書名
encodingstring"UTF-8"文字エンコーディング
defaultWordCostnumber-10000未知語のデフォルトコスト
defaultLeftContextIdnumber1288デフォルトの左文脈 ID
defaultRightContextIdnumber1288デフォルトの右文脈 ID
defaultFieldValuestring"*"欠損フィールドのデフォルト値
flexibleCsvbooleanfalse柔軟な CSV パースを許可
skipInvalidCostOrIdbooleanfalse無効なコストまたは ID のエントリーをスキップ
normalizeDetailsbooleanfalse形態素の詳細情報を正規化

[!NOTE] このバインディングの Metadata オブジェクトには、スキーマ情報(辞書/ユーザー辞書の フィールド構成)は公開されていません -- dictionarySchema / userDictionarySchema プロパティは存在しません。代わりに独立した Schema クラスを使用してください。

すべてのプロパティは取得と設定の両方をサポートしています:

const metadata = new Metadata();
metadata.name = "custom_dict";
metadata.encoding = "EUC-JP";
console.log(metadata.name); // "custom_dict"

toObject()

メタデータのオブジェクト表現を返します:

const metadata = new Metadata({ name: "test" });
console.log(metadata.toObject());

Schema

Schema クラスは辞書エントリーのフィールド構造を定義します。

Schema の作成

const { Schema } = require("lindera");

// デフォルトの IPADIC 互換スキーマ
const schema = Schema.createDefault();

// カスタムスキーマ
const custom = new Schema(["surface", "left_id", "right_id", "cost", "pos", "reading"]);

Schema メソッド

メソッド戻り値説明
getFieldIndex(name)number | nullフィールド名からインデックスを取得
fieldCount()numberフィールドの総数
getFieldName(index)string | nullインデックスからフィールド名を取得
getCustomFields()string[]インデックス 4 以降のフィールド(形態素素性)
getAllFields()string[]すべてのフィールド名
getFieldByName(name)FieldDefinition | nullフィールド定義の完全な情報を取得
validateRecord(record)voidCSV レコードをスキーマに対して検証
const schema = Schema.createDefault();

console.log(schema.fieldCount());           // 13(IPADIC フォーマット)
console.log(schema.getFieldIndex("pos1"));  // 例: 4
console.log(schema.getAllFields());          // ["surface", "left_id", ...]
console.log(schema.getCustomFields());      // インデックス 4 以降のフィールド

FieldDefinition

プロパティ説明
indexnumberフィールドの位置インデックス
namestringフィールド名
fieldTypeFieldTypeフィールド型の列挙値
descriptionstring | undefined任意の説明

FieldType

説明
FieldType.Surface単語の表層形
FieldType.LeftContextId左文脈 ID
FieldType.RightContextId右文脈 ID
FieldType.Cost単語コスト
FieldType.Custom形態素素性フィールド

テキスト処理パイプライン

Lindera Node.js は、トークナイズ前に文字フィルタを適用し、トークナイズ後にトークンフィルタを適用する、組み合わせ可能なテキスト処理パイプラインをサポートしています。フィルタは TokenizerBuilder に追加され、追加された順序で実行されます。

Input Text
  --> Character Filters (preprocessing)
  --> Tokenization
  --> Token Filters (postprocessing)
  --> Output Tokens

[!NOTE] このページではよく使われる一部のフィルタのみを例として紹介しています。これが全リストではありません。 lindera-analysis は合計4種類の文字フィルタと18種類のトークンフィルタを提供しています。全フィルタ(パラメータ・使用例を含む)の 正式なカタログについてはフィルタを参照してください。

文字フィルタ

文字フィルタはトークナイズ前に入力テキストを変換します。

unicode_normalize

入力テキストに Unicode 正規化を適用します。

const { TokenizerBuilder } = require("lindera");

const builder = new TokenizerBuilder();
builder.setDictionary("embedded://ipadic");
builder.appendCharacterFilter("unicode_normalize", { kind: "nfkc" });
const tokenizer = builder.build();

サポートされる正規化形式: "nfc""nfkc""nfd""nfkd"

mapping

マッピングテーブルに従って文字や文字列を置換します。

const builder = new TokenizerBuilder();
builder.setDictionary("embedded://ipadic");
builder.appendCharacterFilter("mapping", {
  mapping: {
    "\u30fc": "-",
    "\uff5e": "~",
  },
});
const tokenizer = builder.build();

japanese_iteration_mark

日本語の踊り字(繰り返し記号)を完全な形に展開します。

const builder = new TokenizerBuilder();
builder.setDictionary("embedded://ipadic");
builder.appendCharacterFilter("japanese_iteration_mark", {
  normalize_kanji: true,
  normalize_kana: true,
});
const tokenizer = builder.build();

トークンフィルタ

トークンフィルタはトークナイズ後にトークンを変換または除去します。

lowercase

トークンの表層形を小文字に変換します。

const builder = new TokenizerBuilder();
builder.setDictionary("embedded://ipadic");
builder.appendTokenFilter("lowercase", {});
const tokenizer = builder.build();

japanese_base_form

辞書の形態素情報を使用して、活用形を基本形(辞書形)に置換します。

const builder = new TokenizerBuilder();
builder.setDictionary("embedded://ipadic");
builder.appendTokenFilter("japanese_base_form", {});
const tokenizer = builder.build();

japanese_stop_tags

指定されたタグに一致する品詞のトークンを除去します。

const builder = new TokenizerBuilder();
builder.setDictionary("embedded://ipadic");
builder.appendTokenFilter("japanese_stop_tags", {
  tags: ["助詞,格助詞,一般", "助詞,係助詞", "助詞,連体化", "助動詞"],
});
const tokenizer = builder.build();

[!NOTE] タグは 4 階層のカンマ区切りに正規化され(不足分は * で補完)、各トークンの先頭 4 つの品詞詳細と完全一致で比較されます。 IPADIC の助詞トークンは必ず 助詞,係助詞 のようにサブカテゴリを持つため、助詞 単独では一致しません。 一方、助動詞はサブカテゴリを持たないため(助動詞,*,*,*)、助動詞 単独で一致します。

japanese_keep_tags

指定されたタグに一致する品詞のトークンのみを保持します。その他のトークンはすべて除去されます。

const builder = new TokenizerBuilder();
builder.setDictionary("embedded://ipadic");
builder.appendTokenFilter("japanese_keep_tags", {
  tags: ["名詞,一般"],
});
const tokenizer = builder.build();

パイプラインの完全な例

以下の例では、複数の文字フィルタとトークンフィルタを1つのパイプラインに組み合わせています:

const { TokenizerBuilder } = require("lindera");

const builder = new TokenizerBuilder();
builder.setMode("normal");
builder.setDictionary("embedded://ipadic");
// Preprocessing
builder.appendCharacterFilter("unicode_normalize", { kind: "nfkc" });
builder.appendCharacterFilter("japanese_iteration_mark", {
  normalize_kanji: true,
  normalize_kana: true,
});
// Postprocessing
builder.appendTokenFilter("japanese_base_form", {});
builder.appendTokenFilter("japanese_stop_tags", {
  tags: ["助詞,格助詞,一般", "助詞,係助詞", "助詞,連体化", "助動詞", "記号,句点", "記号,読点"],
});
builder.appendTokenFilter("lowercase", {});
const tokenizer = builder.build();

const tokens = tokenizer.tokenize("Linderaは形態素解析を行うライブラリです。");
for (const token of tokens) {
  console.log(`${token.surface}\t${token.details.join(",")}`);
}

このパイプラインでは:

  1. unicode_normalize が全角文字を半角に変換(NFKC 正規化)
  2. japanese_iteration_mark が踊り字を展開
  3. japanese_base_form が活用形のトークンを基本形に変換
  4. japanese_stop_tags が助詞(格助詞・係助詞・連体化)・助動詞・句読点を除去
  5. lowercase がアルファベットを小文字に正規化

学習

Lindera Node.js は、アノテーション付きコーパスからカスタム CRF ベースの形態素解析モデルを学習する機能をサポートしています。この機能には train feature が必要です。

前提条件

train feature を有効にして lindera-nodejs をビルドします(デフォルトで有効):

npm run build -- --features train

モデルの学習

[!NOTE] 以下で示すファイルパス(resources/training/*.csv*.def)はあくまで例示用のプレースホルダーであり、 このリポジトリにこれらのファイルが実際に同梱されているわけではありません。種辞書・コーパス・各種定義ファイルを その場で生成し、実際に学習・エクスポート・辞書ビルドまで一気通貫で行う完全な実行可能サンプルは lindera-nodejs/examples/train_and_export.js を参照してください。

train() を使用して、種辞書とアノテーション付きコーパスから CRF モデルを学習します:

const { train } = require("lindera");

train({
  seed: "resources/training/seed.csv",
  corpus: "resources/training/corpus.txt",
  charDef: "resources/training/char.def",
  unkDef: "resources/training/unk.def",
  featureDef: "resources/training/feature.def",
  rewriteDef: "resources/training/rewrite.def",
  output: "/tmp/model.dat",
  lambda: 0.01,
  maxIter: 100,
  maxThreads: 4,
});

学習パラメータ

パラメータデフォルト説明
seedstring必須種辞書ファイルのパス(CSV 形式)
corpusstring必須アノテーション付き学習コーパスのパス
charDefstring必須文字定義ファイルのパス(char.def)
unkDefstring必須未知語定義ファイルのパス(unk.def)
featureDefstring必須素性定義ファイルのパス(feature.def)
rewriteDefstring必須書き換えルール定義ファイルのパス(rewrite.def)
outputstring必須学習済みモデルファイルの出力パス
lambdanumber0.01L1 正則化コスト(0.0--1.0)
maxIternumber100最大学習イテレーション数
maxThreadsnumber | undefinedundefinedスレッド数(undefined = CPU コア数を自動検出)

学習済みモデルのエクスポート

学習後、exportModel() を使用してモデルを辞書ソースファイルにエクスポートします:

const { exportModel } = require("lindera");

exportModel({
  model: "/tmp/model.dat",
  output: "/tmp/dictionary_source",
  metadata: "resources/training/metadata.json",
});

エクスポートパラメータ

パラメータデフォルト説明
modelstring必須学習済みモデルファイルのパス(.dat)
outputstring必須辞書ソースファイルの出力ディレクトリ
metadatastring | undefinedundefinedベースとなる metadata.json ファイルのパス

エクスポートにより、出力ディレクトリに以下のファイルが作成されます:

  • lex.csv -- 学習済みコスト付きのレキシコンエントリー
  • matrix.def -- 連接コスト行列
  • unk.def -- 未知語定義
  • char.def -- 文字カテゴリ定義
  • metadata.json -- 更新されたメタデータ(metadata パラメータ指定時)

完全なワークフロー

カスタム辞書の学習と使用の完全なワークフロー:

const {
  train,
  exportModel,
  buildDictionary,
  Metadata,
  TokenizerBuilder,
} = require("lindera");

// Step 1: Train the CRF model
train({
  seed: "resources/training/seed.csv",
  corpus: "resources/training/corpus.txt",
  charDef: "resources/training/char.def",
  unkDef: "resources/training/unk.def",
  featureDef: "resources/training/feature.def",
  rewriteDef: "resources/training/rewrite.def",
  output: "/tmp/model.dat",
  lambda: 0.01,
  maxIter: 100,
});

// Step 2: Export to dictionary source files
exportModel({
  model: "/tmp/model.dat",
  output: "/tmp/dictionary_source",
  metadata: "resources/training/metadata.json",
});

// Step 3: Build the dictionary from exported source files
const metadata = Metadata.fromJsonFile("/tmp/dictionary_source/metadata.json");
buildDictionary("/tmp/dictionary_source", "/tmp/dictionary", metadata);

// Step 4: Use the trained dictionary
const builder = new TokenizerBuilder();
builder.setDictionary("/tmp/dictionary");
builder.setMode("normal");
const tokenizer = builder.build();

const tokens = tokenizer.tokenize("形態素解析のテスト");
for (const token of tokens) {
  console.log(`${token.surface}\t${token.details.join(",")}`);
}

Lindera Ruby

Lindera Ruby は、Magnusrb-sys を使用して構築された Lindera 形態素解析エンジンの Ruby バインディングです。Ruby 3.1 以降をサポートし、Lindera の高性能なトークナイズ機能を Ruby エコシステムに提供します。

特徴

  • 多言語対応: 日本語(IPADIC、IPADIC NEologd、UniDic)、韓国語(ko-dic)、中国語(CC-CEDICT、Jieba)のテキストをトークナイズ
  • テキスト処理パイプライン: 文字フィルタとトークンフィルタを組み合わせて、柔軟な前処理・後処理が可能
  • CRF ベースの辞書学習: アノテーション付きコーパスからカスタム形態素解析モデルを学習(train feature が必要)
  • 複数のトークナイズモード: 解析粒度に応じた Normal モードと Decompose モード
  • N-best トークナイズ: コスト順にランク付けされた複数のトークナイズ候補を取得
  • ユーザー辞書: システム辞書をカスタム語彙で拡張

ドキュメント

インストール

[!NOTE] lindera-ruby はまだ RubyGems に公開されていません。ソースからビルドする必要があります。

前提条件

  • Ruby 3.1 以降
  • Rust ツールチェーン -- rustup 経由でインストール
  • Bundler -- Ruby の依存関係管理ツール(gem install bundler

辞書の入手

Lindera はパッケージに辞書を同梱していません。ビルド済み辞書を別途入手する必要があります。

GitHub Releases からのダウンロード

ビルド済み辞書は GitHub Releases ページから入手できます。辞書アーカイブをダウンロードしてローカルディレクトリに展開してください:

# 例: IPADIC 辞書のダウンロードと展開
curl -LO https://github.com/lindera/lindera/releases/download/<version>/lindera-ipadic-<version>.zip
unzip lindera-ipadic-<version>.zip -d /path/to/ipadic

開発ビルド

lindera-ruby を開発モードでビルドしてインストールします:

cd lindera-ruby
bundle install
bundle exec rake compile

または、プロジェクトの Makefile を使用します:

make build-lindera-ruby

make test-lindera-ruby を実行すると、Rust のユニットテストと Ruby の minitest スイートの両方が実行されます。

学習機能付きビルド

train feature は、CRF ベースの辞書学習機能を有効にします。デフォルトで有効です:

LINDERA_FEATURES="train" bundle exec rake compile

Feature フラグ

Feature は環境変数 LINDERA_FEATURES にカンマ区切りリストで指定します。

Feature説明デフォルト
trainCRF 学習機能有効
embed-ipadic日本語辞書(IPADIC)をバイナリに埋め込み無効
embed-unidic日本語辞書(UniDic)をバイナリに埋め込み無効
embed-sudachidict日本語辞書(SudachiDict)をバイナリに埋め込み無効
embed-ipadic-neologd日本語辞書(IPADIC NEologd)をバイナリに埋め込み無効
embed-ko-dic韓国語辞書(ko-dic)をバイナリに埋め込み無効
embed-cc-cedict中国語辞書(CC-CEDICT)をバイナリに埋め込み無効
embed-jieba中国語辞書(Jieba)をバイナリに埋め込み無効
embed-cjk全 CJK 辞書をバイナリに埋め込み(IPADIC、ko-dic、Jieba)無効

複数の feature を組み合わせることができます:

LINDERA_FEATURES="train,embed-ipadic,embed-ko-dic" bundle exec rake compile

[!TIP] 辞書をバイナリに直接埋め込みたい場合(上級者向け)は、対応する embed-* feature フラグを有効にしてビルドし、embedded:// スキームでロードしてください:

dictionary = Lindera.load_dictionary("embedded://ipadic")

詳細は Feature フラグ を参照してください。

インストールの確認

インストール後、Ruby で lindera が利用可能であることを確認します:

require 'lindera'

puts Lindera.version

クイックスタート

このガイドでは、lindera-ruby を使用してテキストをトークナイズする方法を紹介します。

基本的なトークナイズ

トークナイザーの作成には Lindera::TokenizerBuilder の使用を推奨します:

require 'lindera'

builder = Lindera::TokenizerBuilder.new
builder.set_mode('normal')
builder.set_dictionary('/path/to/ipadic')
tokenizer = builder.build

tokens = tokenizer.tokenize('関西国際空港限定トートバッグ')
tokens.each do |token|
  puts "#{token.surface}\t#{token.details.join(',')}"
end

注意: ビルド済み辞書を GitHub Releases からダウンロードし、展開したディレクトリのパスを指定してください。

期待される出力:

関西国際空港    名詞,固有名詞,組織,*,*,*,関西国際空港,カンサイコクサイクウコウ,カンサイコクサイクーコー
限定    名詞,サ変接続,*,*,*,*,限定,ゲンテイ,ゲンテイ
トートバッグ    名詞,一般,*,*,*,*,*,*,*

逐次的な設定

TokenizerBuilder は逐次的なメソッド呼び出しで設定します:

require 'lindera'

builder = Lindera::TokenizerBuilder.new
builder.set_mode('normal')
builder.set_dictionary('/path/to/ipadic')
tokenizer = builder.build

tokens = tokenizer.tokenize('すもももももももものうち')
tokens.each do |token|
  puts "#{token.surface}\t#{token.get_detail(0)}"
end

トークンプロパティへのアクセス

各トークンは以下のプロパティを公開しています:

require 'lindera'

builder = Lindera::TokenizerBuilder.new
builder.set_dictionary('/path/to/ipadic')
tokenizer = builder.build

tokens = tokenizer.tokenize('東京タワー')

tokens.each do |token|
  puts "Surface: #{token.surface}"
  puts "Byte range: #{token.byte_start}..#{token.byte_end}"
  puts "Position: #{token.position}"
  puts "Word ID: #{token.word_id}"
  puts "Unknown: #{token.unknown?}"
  puts "Details: #{token.details}"
  puts
end

N-best トークナイズ

コスト順にランク付けされた複数のトークナイズ候補を取得します:

require 'lindera'

builder = Lindera::TokenizerBuilder.new
builder.set_dictionary('/path/to/ipadic')
tokenizer = builder.build

results = tokenizer.tokenize_nbest('すもももももももものうち', 3, false, nil)

results.each do |tokens, cost|
  surfaces = tokens.map(&:surface)
  puts "Cost #{cost}: #{surfaces.join(' / ')}"
end

Tokenizer API

TokenizerBuilder

Lindera::TokenizerBuilder はビルダーパターンを使用して Tokenizer インスタンスを設定・構築します。

コンストラクタ

Lindera::TokenizerBuilder.new

デフォルト設定で新しいビルダーを作成します。

require 'lindera'

builder = Lindera::TokenizerBuilder.new

Lindera::TokenizerBuilder.from_file(file_path)

JSON ファイルから設定を読み込み、新しいビルダーを返します。これはクラスメソッドであり、既存のインスタンスに対してチェーンするものではありません。

builder = Lindera::TokenizerBuilder.from_file('config.json')

設定メソッド

set_mode(mode)

トークナイズモードを設定します。

  • "normal" -- 標準的なトークナイズ(デフォルト)
  • "decompose" -- 複合語をより小さな単位に分解
builder.set_mode('normal')

set_dictionary(path)

システム辞書のパスまたは URI を設定します。

# 埋め込み辞書を使用
builder.set_dictionary('embedded://ipadic')

# 外部辞書を使用
builder.set_dictionary('/path/to/dictionary')

set_user_dictionary(uri)

ユーザー辞書の URI を設定します。

builder.set_user_dictionary('/path/to/user_dictionary')

set_keep_whitespace(keep)

出力に空白トークンを含めるかどうかを制御します。

builder.set_keep_whitespace(true)

append_character_filter(kind, args)

前処理パイプラインに文字フィルタを追加します。

builder.append_character_filter('unicode_normalize', { 'kind' => 'nfkc' })

append_token_filter(kind, args)

後処理パイプラインにトークンフィルタを追加します。args が不要な場合は nil を渡します。

builder.append_token_filter('lowercase', nil)

ビルド

build

設定された内容で Tokenizer をビルドして返します。

tokenizer = builder.build

Tokenizer

Lindera::Tokenizer はテキストに対して形態素解析を行います。

Tokenizer の作成

Lindera::Tokenizer.new(dictionary, mode, user_dictionary)

読み込み済みの辞書から直接トークナイザーを作成します。user_dictionary が不要な場合は nil を渡します。

require 'lindera'

dictionary = Lindera.load_dictionary('embedded://ipadic')
tokenizer = Lindera::Tokenizer.new(dictionary, 'normal', nil)

ユーザー辞書を使用する場合:

dictionary = Lindera.load_dictionary('embedded://ipadic')
metadata = dictionary.metadata
user_dict = Lindera.load_user_dictionary('/path/to/user_dictionary', metadata)
tokenizer = Lindera::Tokenizer.new(dictionary, 'normal', user_dict)

Tokenizer メソッド

tokenize(text)

入力テキストをトークナイズし、Token オブジェクトの配列を返します。

tokens = tokenizer.tokenize('形態素解析')

パラメータ:

名前説明
textStringトークナイズするテキスト

戻り値: Array<Token>

tokenize_surfaces(text)

入力テキストをトークナイズし、トークンの surface のみを文字列の配列として返します。分かち書き用途の高速パスです。Token オブジェクトを生成せず、形態素の詳細情報もロードしないため、surface 文字列だけが必要な場合は tokenize より大幅に高速です。結果は tokenizer.tokenize(text).map(&:surface) と一致します。

surfaces = tokenizer.tokenize_surfaces('形態素解析')
# ["形態素", "解析"]

パラメータ:

名前説明
textStringトークナイズするテキスト

戻り値: Array<String>

tokenize_nbest(text, n, unique, cost_threshold)

N-best トークナイズ結果を返します。各結果はトータルパスコストとペアになっています。

results = tokenizer.tokenize_nbest('すもももももももものうち', 3, false, nil)
results.each do |tokens, cost|
  puts "#{cost}: #{tokens.map(&:surface).join(' / ')}"
end

パラメータ:

名前説明
textStringトークナイズするテキスト
nInteger返す結果の数
uniqueBoolean結果の重複を排除(false で無効)
cost_thresholdInteger または nil最良パスからの最大コスト差(nil で無制限)

戻り値: Array<[Array<Token>, Integer]>

Mode

Lindera::Mode はトークナイズモードを表します。モードの確認・比較のためのスタンドアロンヘルパーとして提供されています。TokenizerBuilder#set_modeTokenizer.new は現在、Mode インスタンスではなく単純なモード文字列("normal" または "decompose")のみを受け付けます(下記の Penalty の制限事項も参照してください)。

Mode の作成

Lindera::Mode.new(mode_str)

Mode を作成します。引数は必須ですが nil を渡すこともできます。"normal" / "Normal"mode_strnil の場合に使用)または "decompose" / "Decompose" を受け付けます。それ以外の値を渡すと ArgumentError が発生します。

require 'lindera'

mode = Lindera::Mode.new('normal')
mode = Lindera::Mode.new('decompose')
mode = Lindera::Mode.new(nil)  # デフォルトの "normal" になる

Mode メソッド

メソッド戻り値説明
to_sString"normal" または "decompose"
nameStringto_s と同じ
inspectString例: "#<Lindera::Mode: decompose>"
normal?Booleanモードが "normal" の場合 true
decompose?Booleanモードが "decompose" の場合 true
mode = Lindera::Mode.new('decompose')
mode.to_s        # "decompose"
mode.normal?      # false
mode.decompose?   # true

Penalty

Lindera::Penalty"decompose" モードのセグメンテーションで使用される、長さに基づくペナルティのしきい値を設定します。

Penalty の作成

Lindera::Penalty.new(kanji_penalty_length_threshold, kanji_penalty_length_penalty, other_penalty_length_threshold, other_penalty_length_penalty)

4つの位置引数はすべて必須ですが、それぞれ nil を渡すとデフォルト値(下記参照)にフォールバックします。

require 'lindera'

penalty = Lindera::Penalty.new(2, 3000, 7, 1700)
penalty = Lindera::Penalty.new(nil, nil, nil, nil)  # すべてデフォルト値を使用

Penalty プロパティ

すべてのプロパティは読み取り専用です(setter メソッドはありません):

プロパティデフォルト説明
kanji_penalty_length_thresholdInteger2ペナルティが適用される漢字のみの表層形の長さのしきい値
kanji_penalty_length_penaltyInteger3000しきい値を超える漢字のみの表層形に加算されるコストペナルティ
other_penalty_length_thresholdInteger7漢字のみでない表層形にペナルティが適用される長さのしきい値
other_penalty_length_penaltyInteger1700しきい値を超える漢字のみでない表層形に加算されるコストペナルティ
penalty = Lindera::Penalty.new(nil, nil, nil, nil)
penalty.kanji_penalty_length_threshold  # 2

現在の制限: 現時点では PenaltyTokenizerTokenizerBuilder に渡す方法はありません。set_modeTokenizer.new は単純なモード文字列のみを受け付け、内部的に "decompose" モードは常に Penalty のデフォルト値を使用します -- カスタムの Penalty インスタンスを作成しても、現時点ではトークナイズには反映されません。

Token

Token は単一の形態素トークンを表します。

プロパティ

プロパティ説明
surfaceStringトークンの表層形
byte_startInteger元テキストでの開始バイト位置
byte_endInteger元テキストでの終了バイト位置
positionIntegerトークンの位置インデックス
word_idInteger辞書の単語 ID
detailsArray<String>形態素の詳細情報(品詞、読みなど)

さらに、述語メソッド unknown? は辞書に登録されていない単語の場合 true を返します:

token.unknown?  # => false

Token メソッド

get_detail(index)

指定されたインデックスの詳細文字列を返します。インデックスが範囲外の場合は nil を返します。

token = tokenizer.tokenize('東京')[0]
pos = token.get_detail(0)        # 例: "名詞"
subpos = token.get_detail(1)     # 例: "固有名詞"
reading = token.get_detail(7)    # 例: "トウキョウ"

パラメータ:

名前説明
indexIntegerdetails 配列へのゼロベースインデックス

戻り値: String または nil

to_h / to_hash

トークンを Symbol をキーとするプレーンな Hash として返します。各フィールドは Ruby の自然な型を保つため、独自のエンコーダなしでシリアライズできます。 to_hto_hash は同一のメソッドです。

require 'json'

data = tokenizer.tokenize('東京')[0].to_h
# {surface: "東京", byte_start: 0, byte_end: 6, position: 0,
#  word_id: 12345, is_unknown: false, details: [...]}

JSON.generate(tokens.map(&:to_h))

戻り値: :surface:byte_start:byte_end:position:word_id:is_unknown:details をキーに持つ Hash

details の構造は辞書によって異なります:

  • IPADIC: [品詞, 品詞細分類1, 品詞細分類2, 品詞細分類3, 活用型, 活用形, 原形, 読み, 発音]
  • UniDic: UniDic 仕様に準拠した詳細な形態素情報
  • ko-dic / CC-CEDICT / Jieba: 各辞書固有の詳細フォーマット

Schema

Lindera::Schema はフィールド名の順序付きリストを保持し、フィールド名とインデックスの間の相互変換を提供します。Metadata#dictionary_schemaMetadata#user_dictionary_schema で使用されます(辞書管理 を参照)。

Schema の作成

Lindera::Schema.new(fields)

フィールド名の配列からスキーマを作成します。

require 'lindera'

schema = Lindera::Schema.new(%w[
  surface
  left_context_id
  right_context_id
  cost
  major_pos
  reading
])

Lindera::Schema.create_default

組み込みのデフォルトスキーマを返します。IPADIC 形式に対応する13フィールド(surfaceleft_context_idright_context_idcostmajor_pospos_detail_1pos_detail_2pos_detail_3conjugation_typeconjugation_formbase_formreadingpronunciation)です。

schema = Lindera::Schema.create_default

Schema メソッド

メソッド戻り値説明
fieldsArray<String>すべてのフィールド名(順序どおり)
get_all_fieldsArray<String>fields と同じ
field_countIntegerフィールドの総数
get_field_index(name)Integer または nilname という名前のフィールドのインデックス
get_field_name(index)String または nilindex にあるフィールド名
get_custom_fieldsArray<String>固定の4フィールド(surfaceleft_context_idright_context_idcost)以降のフィールド名
get_field_by_name(name)FieldDefinition または nilname の完全なフィールド定義
validate_record(record)nilrecord がスキーマと一致しない場合 ArgumentError を発生
to_sString例: "Schema(fields=13)"
inspectStringフィールドの全リスト
schema = Lindera::Schema.create_default

schema.field_count                  # 13
schema.get_field_index('cost')      # 3
schema.get_field_name(0)            # "surface"
schema.get_custom_fields            # ["major_pos", "pos_detail_1", ..., "pronunciation"]

field = schema.get_field_by_name('surface')
puts "#{field.index} #{field.name} #{field.field_type}"  # 0 surface surface

schema.validate_record([
  '東京', '1288', '1288', '100',
  '名詞', '固有名詞', '地域', '一般', '*', '*',
  '東京', 'トウキョウ', 'トーキョー'
])

FieldDefinition

Lindera::FieldDefinitionSchema 内の単一フィールドを表します。インスタンスは Schema#get_field_by_name からのみ取得でき、公開コンストラクタはありません(Lindera::FieldDefinition.newTypeError を発生させます)。

FieldDefinition プロパティ

プロパティ説明
indexIntegerスキーマ内でのフィールドの位置(ゼロベース)
nameStringフィールド名
field_typeFieldTypeフィールドタイプ
descriptionString または nil説明(任意)
schema = Lindera::Schema.create_default
field = schema.get_field_by_name('surface')

field.index         # 0
field.name          # "surface"
field.field_type    # #<Lindera::FieldType: surface>
field.description    # nil(デフォルトスキーマでは説明は設定されていない)

FieldType

Lindera::FieldType はフィールドの種別を列挙します。FieldDefinition と同様、インスタンスは SchemaFieldDefinition#field_type 経由)からのみ取得でき、公開コンストラクタはありません。

to_s(および inspect)は次のいずれかを返します:

  • "surface" -- 表層形(単語のテキスト)
  • "left_context_id" -- 左文脈 ID
  • "right_context_id" -- 右文脈 ID
  • "cost" -- 単語コスト
  • "custom" -- その他の辞書固有フィールド
field = Lindera::Schema.create_default.get_field_by_name('surface')
field.field_type.to_s  # "surface"

辞書管理

Lindera Ruby は、形態素解析で使用する辞書の読み込み、ビルド、管理のためのメソッドを提供します。

辞書の読み込み

システム辞書

Lindera.load_dictionary(uri) を使用してシステム辞書を読み込みます。GitHub Releases からビルド済み辞書をダウンロードし、展開したディレクトリのパスを指定してください:

require 'lindera'

dictionary = Lindera.load_dictionary('/path/to/ipadic')

埋め込み辞書(上級者向け) -- embed-* feature フラグ付きでビルドした場合、埋め込み辞書を使用できます:

dictionary = Lindera.load_dictionary('embedded://ipadic')

ユーザー辞書

ユーザー辞書はシステム辞書にカスタム語彙を追加します。

require 'lindera'

dictionary = Lindera.load_dictionary('/path/to/ipadic')
metadata = dictionary.metadata
user_dict = Lindera.load_user_dictionary('/path/to/user_dictionary', metadata)

トークナイザーの作成時にユーザー辞書を渡します:

require 'lindera'

dictionary = Lindera.load_dictionary('/path/to/ipadic')
metadata = dictionary.metadata
user_dict = Lindera.load_user_dictionary('/path/to/user_dictionary', metadata)

tokenizer = Lindera::Tokenizer.new(dictionary, 'normal', user_dict)

または、ビルダー経由で設定します:

require 'lindera'

builder = Lindera::TokenizerBuilder.new
builder.set_dictionary('/path/to/ipadic')
builder.set_user_dictionary('/path/to/user_dictionary')
tokenizer = builder.build

辞書のビルド

システム辞書のビルド

ソースファイルからシステム辞書をビルドします:

require 'lindera'

metadata = Lindera::Metadata.from_json_file('metadata.json')
Lindera.build_dictionary('/path/to/input_dir', '/path/to/output_dir', metadata)

入力ディレクトリには辞書のソースファイル(CSV レキシコン、matrix.def など)が含まれている必要があります。

ユーザー辞書のビルド

CSV ファイルからユーザー辞書をビルドします:

require 'lindera'

metadata = Lindera::Metadata.from_json_file('metadata.json')
Lindera.build_user_dictionary('ipadic', 'user_words.csv', '/path/to/output_dir', metadata)

metadata パラメータは省略可能です。省略した場合はデフォルトのメタデータ値が使用されます:

Lindera.build_user_dictionary('ipadic', 'user_words.csv', '/path/to/output_dir', nil)

[!NOTE] 第一引数(kind、上の例では 'ipadic')は現時点では未使用です -- 将来の利用のために予約 されているだけで、ビルドには影響しません。現時点では任意の文字列を渡すことができます。

Metadata

Lindera::Metadata クラスは辞書のパラメータを設定します。

Metadata の作成

require 'lindera'

# 標準設定でデフォルトのメタデータを作成
metadata = Lindera::Metadata.create_default

Lindera::Metadata.new は9つのプロパティすべてを必須の位置引数として受け取ります (それぞれ nil を渡すとデフォルト値にフォールバックします)。特定の値を上書きしたい 場合にのみ使用してください:

metadata = Lindera::Metadata.new(
  'my_dict', # name
  'UTF-8',   # encoding
  -10_000,   # default_word_cost
  1288,      # default_left_context_id
  1288,      # default_right_context_id
  '*',       # default_field_value
  false,     # flexible_csv
  false,     # skip_invalid_cost_or_id
  false      # normalize_details
)

JSON ファイルからの読み込み

metadata = Lindera::Metadata.from_json_file('metadata.json')

辞書からのメタデータ取得

読み込み済みの辞書からメタデータを取得できます:

dictionary = Lindera.load_dictionary('/path/to/ipadic')
metadata = dictionary.metadata

プロパティ

プロパティデフォルト説明
nameString"default"辞書名
encodingString"UTF-8"文字エンコーディング
default_word_costInteger-10000未知語のデフォルトコスト
default_left_context_idInteger1288デフォルトの左文脈 ID
default_right_context_idInteger1288デフォルトの右文脈 ID
default_field_valueString"*"欠損フィールドのデフォルト値
flexible_csvBooleanfalse柔軟な CSV パースを許可
skip_invalid_cost_or_idBooleanfalse無効なコストまたは ID のエントリーをスキップ
normalize_detailsBooleanfalse形態素の詳細情報を正規化

テキスト処理パイプライン

Lindera Ruby は、トークナイズ前に文字フィルタを適用し、トークナイズ後にトークンフィルタを適用する、組み合わせ可能なテキスト処理パイプラインをサポートしています。フィルタは Lindera::TokenizerBuilder に追加され、追加された順序で実行されます。

Input Text
  --> Character Filters (preprocessing)
  --> Tokenization
  --> Token Filters (postprocessing)
  --> Output Tokens

[!NOTE] このページではよく使われる一部のフィルタのみを例として紹介しています。これが全リストではありません。 lindera-analysis は合計4種類の文字フィルタと18種類のトークンフィルタを提供しています。全フィルタ(パラメータ・使用例を含む)の 正式なカタログについてはフィルタを参照してください。

文字フィルタ

文字フィルタはトークナイズ前に入力テキストを変換します。

unicode_normalize

入力テキストに Unicode 正規化を適用します。

require 'lindera'

builder = Lindera::TokenizerBuilder.new
builder.set_dictionary('embedded://ipadic')
builder.append_character_filter('unicode_normalize', { 'kind' => 'nfkc' })
tokenizer = builder.build

サポートされる正規化形式: "nfc""nfkc""nfd""nfkd"

mapping

マッピングテーブルに従って文字や文字列を置換します。

builder = Lindera::TokenizerBuilder.new
builder.set_dictionary('embedded://ipadic')
builder.append_character_filter('mapping', {
  'mapping' => {
    "\u30fc" => '-',
    "\uff5e" => '~'
  }
})
tokenizer = builder.build

japanese_iteration_mark

日本語の踊り字(繰り返し記号)を完全な形に展開します。

builder = Lindera::TokenizerBuilder.new
builder.set_dictionary('embedded://ipadic')
builder.append_character_filter('japanese_iteration_mark', {
  'normalize_kanji' => true,
  'normalize_kana' => true
})
tokenizer = builder.build

トークンフィルタ

トークンフィルタはトークナイズ後にトークンを変換または除去します。

lowercase

トークンの表層形を小文字に変換します。

builder = Lindera::TokenizerBuilder.new
builder.set_dictionary('embedded://ipadic')
builder.append_token_filter('lowercase', nil)
tokenizer = builder.build

japanese_base_form

辞書の形態素情報を使用して、活用形を基本形(辞書形)に置換します。

builder = Lindera::TokenizerBuilder.new
builder.set_dictionary('embedded://ipadic')
builder.append_token_filter('japanese_base_form', nil)
tokenizer = builder.build

japanese_stop_tags

指定されたタグに一致する品詞のトークンを除去します。

builder = Lindera::TokenizerBuilder.new
builder.set_dictionary('embedded://ipadic')
builder.append_token_filter('japanese_stop_tags', {
  'tags' => ['助詞,格助詞,一般', '助詞,係助詞', '助詞,連体化', '助動詞']
})
tokenizer = builder.build

[!NOTE] タグは 4 階層のカンマ区切りに正規化され(不足分は * で補完)、各トークンの先頭 4 つの品詞詳細と完全一致で比較されます。 IPADIC の助詞トークンは必ず 助詞,係助詞 のようにサブカテゴリを持つため、助詞 単独では一致しません。 一方、助動詞はサブカテゴリを持たないため(助動詞,*,*,*)、助動詞 単独で一致します。

japanese_keep_tags

指定されたタグに一致する品詞のトークンのみを保持します。その他のトークンはすべて除去されます。

builder = Lindera::TokenizerBuilder.new
builder.set_dictionary('embedded://ipadic')
builder.append_token_filter('japanese_keep_tags', {
  'tags' => ['名詞,一般']
})
tokenizer = builder.build

japanese_katakana_stem

カタカナ語の末尾にある長音記号を除去します。最小文字数を指定できます。

builder = Lindera::TokenizerBuilder.new
builder.set_dictionary('embedded://ipadic')
builder.append_token_filter('japanese_katakana_stem', { 'min' => 3 })
tokenizer = builder.build

パイプラインの完全な例

以下の例では、複数の文字フィルタとトークンフィルタを1つのパイプラインに組み合わせています:

require 'lindera'

builder = Lindera::TokenizerBuilder.new
builder.set_mode('normal')
builder.set_dictionary('embedded://ipadic')

# Preprocessing
builder.append_character_filter('unicode_normalize', { 'kind' => 'nfkc' })
builder.append_character_filter('japanese_iteration_mark', {
  'normalize_kanji' => true,
  'normalize_kana' => true
})

# Postprocessing
builder.append_token_filter('japanese_base_form', nil)
builder.append_token_filter('japanese_stop_tags', {
  'tags' => ['助詞,格助詞,一般', '助詞,係助詞', '助詞,連体化', '助動詞', '記号,句点', '記号,読点']
})
builder.append_token_filter('lowercase', nil)

tokenizer = builder.build

tokens = tokenizer.tokenize('Linderaは形態素解析を行うライブラリです。')
tokens.each do |token|
  puts "#{token.surface}\t#{token.details.join(',')}"
end

このパイプラインでは:

  1. unicode_normalize が全角文字を半角に変換(NFKC 正規化)
  2. japanese_iteration_mark が踊り字を展開
  3. japanese_base_form が活用形のトークンを基本形に変換
  4. japanese_stop_tags が助詞(格助詞・係助詞・連体化)・助動詞・句読点を除去
  5. lowercase がアルファベットを小文字に正規化

学習

Lindera Ruby は、アノテーション付きコーパスからカスタム CRF ベースの形態素解析モデルを学習する機能をサポートしています。この機能には train feature が必要です。

前提条件

train feature を有効にして lindera-ruby をビルドします:

LINDERA_FEATURES="embed-ipadic,train" bundle exec rake compile

モデルの学習

Lindera.train を使用して、種辞書とアノテーション付きコーパスから CRF モデルを学習します:

require 'lindera'

Lindera.train(
  'resources/training/seed.csv',
  'resources/training/corpus.txt',
  'resources/training/char.def',
  'resources/training/unk.def',
  'resources/training/feature.def',
  'resources/training/rewrite.def',
  '/tmp/model.dat',
  0.01,   # lambda (L1 regularization)
  100,    # max_iter
  nil     # max_threads (nil = auto-detect)
)

学習パラメータ

Lindera.train の引数は位置引数として順番に渡します:

順番パラメータ説明
1seedString種辞書ファイルのパス(CSV 形式)
2corpusStringアノテーション付き学習コーパスのパス
3char_defString文字定義ファイルのパス(char.def)
4unk_defString未知語定義ファイルのパス(unk.def)
5feature_defString素性定義ファイルのパス(feature.def)
6rewrite_defString書き換えルール定義ファイルのパス(rewrite.def)
7outputString学習済みモデルファイルの出力パス
8lambdaFloatL1 正則化コスト(0.0--1.0)
9max_iterInteger最大学習イテレーション数
10max_threadsInteger または nilスレッド数(nil = CPU コア数を自動検出)

学習済みモデルのエクスポート

学習後、Lindera.export を使用してモデルを辞書ソースファイルにエクスポートします:

require 'lindera'

Lindera.export('/tmp/model.dat', '/tmp/dictionary_source', 'resources/training/metadata.json')

エクスポートパラメータ

順番パラメータ説明
1modelString学習済みモデルファイルのパス(.dat)
2outputString辞書ソースファイルの出力ディレクトリ
3metadataString または nilベースとなる metadata.json ファイルのパス

エクスポートにより、出力ディレクトリに以下のファイルが作成されます:

  • lex.csv -- 学習済みコスト付きのレキシコンエントリー
  • matrix.def -- 連接コスト行列
  • unk.def -- 未知語定義
  • char.def -- 文字カテゴリ定義
  • metadata.json -- 更新されたメタデータ(metadata パラメータ指定時)

完全なワークフロー

カスタム辞書の学習と使用の完全なワークフロー:

require 'lindera'

# Step 1: Train the CRF model
Lindera.train(
  'resources/training/seed.csv',
  'resources/training/corpus.txt',
  'resources/training/char.def',
  'resources/training/unk.def',
  'resources/training/feature.def',
  'resources/training/rewrite.def',
  '/tmp/model.dat',
  0.01,  # lambda
  100,   # max_iter
  nil    # max_threads
)

# Step 2: Export to dictionary source files
Lindera.export('/tmp/model.dat', '/tmp/dictionary_source', 'resources/training/metadata.json')

# Step 3: Build the dictionary from exported source files
metadata = Lindera::Metadata.from_json_file('/tmp/dictionary_source/metadata.json')
Lindera.build_dictionary('/tmp/dictionary_source', '/tmp/dictionary', metadata)

# Step 4: Use the trained dictionary
builder = Lindera::TokenizerBuilder.new
builder.set_dictionary('/tmp/dictionary')
builder.set_mode('normal')
tokenizer = builder.build

tokens = tokenizer.tokenize('形態素解析のテスト')
tokens.each do |token|
  puts "#{token.surface}\t#{token.details.join(',')}"
end

Lindera PHP

Lindera PHP は、ext-php-rs を使用して構築された Lindera 形態素解析エンジンの PHP バインディングです。PHP 8.1 以降をサポートし、Lindera の高性能なトークナイズ機能を PHP エコシステムに提供します。

特徴

  • 多言語対応: 日本語(IPADIC、IPADIC NEologd、UniDic)、韓国語(ko-dic)、中国語(CC-CEDICT、Jieba)のテキストをトークナイズ
  • テキスト処理パイプライン: 文字フィルタとトークンフィルタを組み合わせて、柔軟な前処理・後処理が可能
  • CRF ベースの辞書学習: アノテーション付きコーパスからカスタム形態素解析モデルを学習(train feature が必要)
  • 複数のトークナイズモード: 解析粒度に応じた Normal モードと Decompose モード
  • N-best トークナイズ: コスト順にランク付けされた複数のトークナイズ候補を取得
  • ユーザー辞書: システム辞書をカスタム語彙で拡張

ドキュメント

インストール

[!NOTE] lindera-php はまだ Packagist に公開されていません。ソースからビルドする必要があります。

前提条件

  • PHP 8.1 以降
  • Rust ツールチェーン -- rustup 経由でインストール
  • Composer -- PHP の依存関係管理(テスト実行時に必要)

辞書の入手

Lindera はパッケージに辞書を同梱していません。ビルド済み辞書を別途入手する必要があります。

GitHub Releases からのダウンロード

ビルド済み辞書は GitHub Releases ページから入手できます。辞書アーカイブをダウンロードしてローカルディレクトリに展開してください:

# 例: IPADIC 辞書のダウンロードと展開
curl -LO https://github.com/lindera/lindera/releases/download/<version>/lindera-ipadic-<version>.zip
unzip lindera-ipadic-<version>.zip -d /path/to/ipadic

ビルド

lindera-php をビルドします:

cargo build -p lindera-php

または、プロジェクトの Makefile を使用します:

make build-lindera-php

学習機能付きビルド

train feature を有効にすると、CRF ベースの辞書学習機能が利用可能になります。デフォルトで有効になっています:

cargo build -p lindera-php --features train

PHP 拡張の読み込み

ビルド後、-d extension= オプションでビルドされた共有ライブラリを指定して PHP を実行します:

php -d extension=target/debug/liblindera_php.so script.php

リリースビルドの場合:

cargo build -p lindera-php --release
php -d extension=target/release/liblindera_php.so script.php

Feature フラグ

Feature説明デフォルト
trainCRF 学習機能有効
embed-ipadic日本語辞書(IPADIC)をバイナリに埋め込み無効
embed-unidic日本語辞書(UniDic)をバイナリに埋め込み無効
embed-sudachidict日本語辞書(SudachiDict)をバイナリに埋め込み無効
embed-ipadic-neologd日本語辞書(IPADIC NEologd)をバイナリに埋め込み無効
embed-ko-dic韓国語辞書(ko-dic)をバイナリに埋め込み無効
embed-cc-cedict中国語辞書(CC-CEDICT)をバイナリに埋め込み無効
embed-jieba中国語辞書(Jieba)をバイナリに埋め込み無効
embed-cjk全 CJK 辞書をバイナリに埋め込み(IPADIC、ko-dic、Jieba)無効

複数の feature を組み合わせることができます:

cargo build -p lindera-php --features "train,embed-ipadic,embed-ko-dic"

[!TIP] 辞書をバイナリに直接埋め込みたい場合(上級者向け)は、対応する embed-* feature フラグを有効にしてビルドし、embedded:// スキームでロードしてください:

$dictionary = Lindera\Dictionary::load('embedded://ipadic');

詳細は Feature フラグ を参照してください。

インストールの確認

インストール後、PHP で lindera が利用可能であることを確認します:

<?php

$version = Lindera\Dictionary::version();
echo "Lindera version: {$version}\n";

実行方法:

php -d extension=target/debug/liblindera_php.so script.php

クイックスタート

このガイドでは、lindera-php を使用してテキストをトークナイズする方法を紹介します。

基本的なトークナイズ

辞書を読み込み、トークナイザーを作成してテキストをトークナイズします:

<?php

// Load the dictionary
$dictionary = Lindera\Dictionary::load('/path/to/ipadic');

// Create a tokenizer
$tokenizer = new Lindera\Tokenizer($dictionary, 'normal');

// Tokenize the text
$tokens = $tokenizer->tokenize('関西国際空港限定トートバッグ');
foreach ($tokens as $token) {
    echo $token->surface . "\t" . implode(',', $token->details) . "\n";
}

注意: ビルド済み辞書を GitHub Releases からダウンロードし、展開したディレクトリのパスを指定してください。

期待される出力:

関西国際空港    名詞,固有名詞,組織,*,*,*,関西国際空港,カンサイコクサイクウコウ,カンサイコクサイクーコー
限定    名詞,サ変接続,*,*,*,*,限定,ゲンテイ,ゲンテイ
トートバッグ    名詞,一般,*,*,*,*,*,*,*

TokenizerBuilder の使用

TokenizerBuilder を使用すると、より柔軟にトークナイザーを設定できます:

<?php

$builder = new Lindera\TokenizerBuilder();
$builder->setMode('normal');
$builder->setDictionary('/path/to/ipadic');
$tokenizer = $builder->build();

$tokens = $tokenizer->tokenize('すもももももももものうち');
foreach ($tokens as $token) {
    echo $token->surface . "\t" . $token->getDetail(0) . "\n";
}

トークンプロパティへのアクセス

各トークンは以下のプロパティを公開しています:

<?php

$dictionary = Lindera\Dictionary::load('/path/to/ipadic');
$tokenizer = new Lindera\Tokenizer($dictionary, 'normal');
$tokens = $tokenizer->tokenize('東京タワー');

foreach ($tokens as $token) {
    echo "Surface: {$token->surface}\n";
    echo "Byte range: {$token->byte_start}..{$token->byte_end}\n";
    echo "Position: {$token->position}\n";
    echo "Word ID: {$token->word_id}\n";
    echo "Unknown: " . ($token->is_unknown ? 'true' : 'false') . "\n";
    echo "Details: " . implode(',', $token->details) . "\n";
    echo "\n";
}

N-best トークナイズ

コスト順にランク付けされた複数のトークナイズ候補を取得します:

<?php

$builder = new Lindera\TokenizerBuilder();
$builder->setDictionary('/path/to/ipadic');
$tokenizer = $builder->build();

$results = $tokenizer->tokenizeNbest('東京都', 3);
foreach ($results as $result) {
    $surfaces = array_map(fn($t) => $t->surface, $result->tokens);
    echo "Cost {$result->cost}: " . implode(' / ', $surfaces) . "\n";
}

Decompose モード

Decompose モードでは、複合語をより小さな単位に分解します:

<?php

$dictionary = Lindera\Dictionary::load('/path/to/ipadic');
$tokenizer = new Lindera\Tokenizer($dictionary, 'decompose');

$tokens = $tokenizer->tokenize('関西国際空港限定トートバッグ');
foreach ($tokens as $token) {
    echo $token->surface . "\n";
}

トークナイザー API

TokenizerBuilder

TokenizerBuilder はビルダーパターンを使用して Tokenizer インスタンスを設定・構築します。

コンストラクタ

new Lindera\TokenizerBuilder()

デフォルト設定で新しいビルダーを作成します。

<?php

$builder = new Lindera\TokenizerBuilder();

設定メソッド

setMode($mode)

トークナイズモードを設定します。

  • "normal" -- 標準的なトークナイズ(デフォルト)
  • "decompose" -- 複合語をより小さな単位に分解
$builder->setMode('normal');

setDictionary($uri)

システム辞書のパスまたは URI を設定します。

// 埋め込み辞書を使用
$builder->setDictionary('embedded://ipadic');

// 外部辞書を使用
$builder->setDictionary('/path/to/dictionary');

setUserDictionary($uri)

ユーザー辞書の URI を設定します。

$builder->setUserDictionary('/path/to/user_dictionary.csv');

setKeepWhitespace($keep)

出力に空白トークンを含めるかどうかを制御します。

$builder->setKeepWhitespace(true);

appendCharacterFilter($kind, $args)

前処理パイプラインに文字フィルタを追加します。

$builder->appendCharacterFilter('unicode_normalize', ['kind' => 'nfkc']);

appendTokenFilter($kind, $args)

後処理パイプラインにトークンフィルタを追加します。

$builder->appendTokenFilter('lowercase');
$builder->appendTokenFilter('japanese_stop_tags', [
    'tags' => ['助詞,格助詞,一般', '助詞,係助詞', '助詞,連体化', '助動詞'],
]);

ビルド

build()

設定された内容で Tokenizer をビルドして返します。

$tokenizer = $builder->build();

Tokenizer

Tokenizer はテキストに対して形態素解析を行います。

Tokenizer の作成

new Lindera\Tokenizer($dictionary, $mode, $userDictionary)

読み込み済みの辞書から直接トークナイザーを作成します。

<?php

$dictionary = Lindera\Dictionary::load('embedded://ipadic');
$tokenizer = new Lindera\Tokenizer($dictionary, 'normal');

ユーザー辞書を指定する場合:

<?php

$dictionary = Lindera\Dictionary::load('embedded://ipadic');
$metadata = $dictionary->metadata();
$userDictionary = Lindera\Dictionary::loadUser('/path/to/user_dictionary.csv', $metadata);

$tokenizer = new Lindera\Tokenizer($dictionary, 'normal', $userDictionary);

Tokenizer メソッド

tokenize($text)

入力テキストをトークナイズし、Token オブジェクトの配列を返します。

$tokens = $tokenizer->tokenize('形態素解析');

パラメータ:

名前説明
$textstringトークナイズするテキスト

戻り値: array<Token>

tokenizeSurfaces($text)

入力テキストをトークナイズし、トークンの surface のみを文字列の配列として返します。分かち書き用途の高速パスです。Token オブジェクトを生成せず、形態素の詳細情報もロードしないため、surface 文字列だけが必要な場合は tokenize より大幅に高速です。結果は array_map(fn ($t) => $t->surface, $tokenizer->tokenize($text)) と一致します。

<?php

$surfaces = $tokenizer->tokenizeSurfaces('形態素解析');
// ["形態素", "解析"]

パラメータ:

名前説明
$textstringトークナイズするテキスト

戻り値: array<string>

tokenizeNbest($text, $n)

N-best トークナイズ結果を返します。各結果は NbestResult オブジェクトで、トークン配列とトータルパスコストを含みます。

$results = $tokenizer->tokenizeNbest('すもももももももものうち', 3);
foreach ($results as $result) {
    echo "Cost: {$result->cost}\n";
    foreach ($result->tokens as $token) {
        echo "  {$token->surface}\n";
    }
}

パラメータ:

名前説明
$textstringトークナイズするテキスト
$nint返す結果の数

戻り値: array<NbestResult>

NbestResult

NbestResult は N-best トークナイズの個別の結果を表します。

NbestResult プロパティ

プロパティ説明
tokensarray<Token>トークンの配列
costintトータルパスコスト

Token

Token は単一の形態素トークンを表します。

Token プロパティ

プロパティ説明
surfacestringトークンの表層形
byte_startint元テキストでの開始バイト位置
byte_endint元テキストでの終了バイト位置
positionintトークンの位置インデックス
word_idint辞書の単語 ID
is_unknownbool辞書に登録されていない単語の場合 true
detailsarray<string>形態素の詳細情報(品詞、読みなど)

Token メソッド

getDetail($index)

指定されたインデックスの詳細文字列を返します。インデックスが範囲外の場合は null を返します。

$token = $tokenizer->tokenize('東京')[0];
$pos = $token->getDetail(0);        // 例: "名詞"
$subpos = $token->getDetail(1);     // 例: "固有名詞"
$reading = $token->getDetail(7);    // 例: "トウキョウ"

パラメータ:

名前説明
$indexintdetails 配列へのゼロベースインデックス

戻り値: string または null

toArray()

トークンを連想配列として返します。各フィールドは PHP の自然な型を保つため、 独自のエンコーダなしでシリアライズできます。

$data = $tokenizer->tokenize('東京')[0]->toArray();
// ['surface' => '東京', 'byte_start' => 0, 'byte_end' => 6, 'position' => 0,
//  'word_id' => 12345, 'is_unknown' => false, 'details' => [...]]

echo json_encode(array_map(fn($t) => $t->toArray(), $tokens));

戻り値: surfacebyte_startbyte_endpositionword_idis_unknowndetails をキーに持つ array

[!NOTE] Lindera\TokenJsonSerializable を実装していないため、トークンオブジェクトを そのまま json_encode に渡してもこれらのフィールドは出力されません。先に toArray() を呼んでください。この拡張は ext-php-rs でビルドされており、その #[php_class] はまだ実装インターフェースを宣言できません(上流の ext-php-rs#326)。

details の構造は辞書によって異なります:

  • IPADIC: [品詞, 品詞細分類1, 品詞細分類2, 品詞細分類3, 活用型, 活用形, 原形, 読み, 発音]
  • UniDic: UniDic 仕様に準拠した詳細な形態素情報
  • ko-dic / CC-CEDICT / Jieba: 各辞書固有の詳細フォーマット

Mode

Mode はトークナイズの動作モードを表します。

Mode の作成

new Lindera\Mode($name)

$mode = new Lindera\Mode('normal');
$mode = new Lindera\Mode('decompose');
$mode = new Lindera\Mode();  // デフォルト: 'normal'

Mode プロパティ

プロパティ説明
namestringモード名("normal" または "decompose"

Mode メソッド

メソッド戻り値説明
isNormal()boolNormal モードの場合 true
isDecompose()boolDecompose モードの場合 true

Penalty

Penalty は、文字種と長さのしきい値に基づいて、decompose モードが複合語をどの程度積極的に分割するかを設定します。

注意: Penalty は現時点で TokenizerBuilderTokenizer のコンストラクタには接続されていません。これを受け取るセッターは存在しないため、インスタンスを作成してもトークナイズには影響しません。decompose モードは常に以下のデフォルト値を使用します。

<?php

// すべてのパラメータは省略可能で、decompose モードが使用するデフォルト値になります
$penalty = new Lindera\Penalty(
    kanji_penalty_length_threshold: 2,
    kanji_penalty_length_penalty: 3000,
    other_penalty_length_threshold: 7,
    other_penalty_length_penalty: 1700,
);

Penalty プロパティ

プロパティデフォルト説明
$kanji_penalty_length_thresholdint2漢字連続のしきい値
$kanji_penalty_length_penaltyint3000しきい値を超えた漢字連続に適用されるペナルティ
$other_penalty_length_thresholdint7その他の文字連続のしきい値
$other_penalty_length_penaltyint1700しきい値を超えたその他の文字連続に適用されるペナルティ

辞書管理

Lindera PHP は、形態素解析で使用する辞書の読み込み、ビルド、管理のためのクラスを提供します。

辞書の読み込み

システム辞書

Lindera\Dictionary::load($uri) を使用してシステム辞書を読み込みます。GitHub Releases からビルド済み辞書をダウンロードし、展開したディレクトリのパスを指定してください:

<?php

$dictionary = Lindera\Dictionary::load('/path/to/ipadic');

埋め込み辞書(上級者向け) -- embed-* feature フラグ付きでビルドした場合、埋め込み辞書を使用できます:

<?php

$dictionary = Lindera\Dictionary::load('embedded://ipadic');

ユーザー辞書

ユーザー辞書はシステム辞書にカスタム語彙を追加します。Lindera\Dictionary::loadUser() を使用して読み込みます。

<?php

$dictionary = Lindera\Dictionary::load('/path/to/ipadic');
$metadata = $dictionary->metadata();
$userDictionary = Lindera\Dictionary::loadUser('/path/to/user_dictionary.csv', $metadata);

トークナイザーの作成時にユーザー辞書を渡します:

<?php

$dictionary = Lindera\Dictionary::load('/path/to/ipadic');
$metadata = $dictionary->metadata();
$userDictionary = Lindera\Dictionary::loadUser('/path/to/user_dictionary.csv', $metadata);

$tokenizer = new Lindera\Tokenizer($dictionary, 'normal', $userDictionary);

または、ビルダー経由で設定します:

<?php

$builder = new Lindera\TokenizerBuilder();
$builder->setDictionary('/path/to/ipadic');
$builder->setUserDictionary('/path/to/user_dictionary.csv');
$tokenizer = $builder->build();

Dictionary メソッド

メソッド戻り値説明
Dictionary::load($uri)Dictionaryシステム辞書を読み込む
Dictionary::loadUser($path, $metadata)UserDictionaryユーザー辞書を読み込む
Dictionary::version()stringLindera のバージョン文字列を返す
Dictionary::build($source, $dest, $metadata)void辞書をビルドする
$dictionary->metadata()Metadata辞書のメタデータを返す
$dictionary->metadataName()string辞書名を返す
$dictionary->metadataEncoding()string辞書のエンコーディングを返す

辞書のビルド

システム辞書のビルド

ソースファイルからシステム辞書をビルドします:

<?php

$metadata = Lindera\Metadata::fromJsonFile('/path/to/metadata.json');
Lindera\Dictionary::build('/path/to/input_dir', '/path/to/output_dir', $metadata);

入力ディレクトリには辞書のソースファイル(CSV レキシコン、matrix.def など)が含まれている必要があります。

以下は IPADIC 辞書をダウンロードしてビルドする例です:

<?php

$url = 'https://lindera.dev/mecab-ipadic-2.7.0-20070801.tar.gz';
$filename = '/tmp/mecab-ipadic-2.7.0-20070801.tar.gz';

// Download and extract dictionary source
file_put_contents($filename, file_get_contents($url));
$phar = new PharData($filename);
$phar->extractTo('/tmp/', null, true);

// Load metadata and build
$metadata = Lindera\Metadata::fromJsonFile('resources/ipadic_metadata.json');
Lindera\Dictionary::build(
    '/tmp/mecab-ipadic-2.7.0-20070801',
    '/tmp/lindera-ipadic',
    $metadata
);

Metadata

Metadata クラスは辞書のパラメータを設定します。

Metadata の作成

<?php

// デフォルトのメタデータ
$metadata = Lindera\Metadata::createDefault();

// カスタムメタデータ
$metadata = new Lindera\Metadata('my_dictionary', 'UTF-8', -10000);

JSON からの読み込み

$metadata = Lindera\Metadata::fromJsonFile('metadata.json');

プロパティ

プロパティデフォルト説明
namestring"default"辞書名
encodingstring"UTF-8"文字エンコーディング
default_word_costint-10000未知語のデフォルトコスト

Schema

Schema クラスは辞書のフィールド構造を定義します。

Schema の作成

<?php

// デフォルトスキーマ(IPADIC 互換)
$schema = Lindera\Schema::createDefault();

// カスタムスキーマ
$schema = new Lindera\Schema(['surface', 'pos']);

メソッド

メソッド戻り値説明
fieldCount()intフィールド数を返す
getFieldIndex($name)intフィールドのインデックスを返す(見つからない場合は -1
getFieldByName($name)FieldDefinition または nullフィールド情報を返す
getCustomFields()array<string>カスタムフィールド名の配列を返す
validateRecord($record)voidレコードがスキーマに適合するか検証する

Schema プロパティ

プロパティ説明
fieldsarray<string>フィールド名の配列

テキスト処理パイプライン

Lindera PHP は、トークナイズ前に文字フィルタを適用し、トークナイズ後にトークンフィルタを適用する、組み合わせ可能なテキスト処理パイプラインをサポートしています。フィルタは TokenizerBuilder に追加され、追加された順序で実行されます。

Input Text
  --> Character Filters (preprocessing)
  --> Tokenization
  --> Token Filters (postprocessing)
  --> Output Tokens

文字フィルタ

文字フィルタはトークナイズ前に入力テキストを変換します。

unicode_normalize

入力テキストに Unicode 正規化を適用します。

<?php

$builder = new Lindera\TokenizerBuilder();
$builder->setDictionary('embedded://ipadic');
$builder->appendCharacterFilter('unicode_normalize', ['kind' => 'nfkc']);
$tokenizer = $builder->build();

サポートされる正規化形式: "nfc""nfkc""nfd""nfkd"

mapping

マッピングテーブルに従って文字や文字列を置換します。

<?php

$builder = new Lindera\TokenizerBuilder();
$builder->setDictionary('embedded://ipadic');
$builder->appendCharacterFilter('mapping', [
    'mapping' => [
        'リンデラ' => 'lindera',
    ],
]);
$tokenizer = $builder->build();

japanese_iteration_mark

日本語の踊り字(繰り返し記号)を完全な形に展開します。

<?php

$builder = new Lindera\TokenizerBuilder();
$builder->setDictionary('embedded://ipadic');
$builder->appendCharacterFilter('japanese_iteration_mark', [
    'normalize_kanji' => 'true',
    'normalize_kana' => 'true',
]);
$tokenizer = $builder->build();

トークンフィルタ

トークンフィルタはトークナイズ後にトークンを変換または除去します。

lowercase

トークンの表層形を小文字に変換します。

<?php

$builder = new Lindera\TokenizerBuilder();
$builder->setDictionary('embedded://ipadic');
$builder->appendTokenFilter('lowercase');
$tokenizer = $builder->build();

japanese_base_form

辞書の形態素情報を使用して、活用形を基本形(辞書形)に置換します。

<?php

$builder = new Lindera\TokenizerBuilder();
$builder->setDictionary('embedded://ipadic');
$builder->appendTokenFilter('japanese_base_form', []);
$tokenizer = $builder->build();

japanese_katakana_stem

カタカナ語の語尾の長音記号を除去してステミングを行います。

<?php

$builder = new Lindera\TokenizerBuilder();
$builder->setDictionary('embedded://ipadic');
$builder->appendTokenFilter('japanese_katakana_stem', ['min' => 3]);
$tokenizer = $builder->build();

japanese_stop_tags

指定されたタグに一致する品詞のトークンを除去します。

<?php

$builder = new Lindera\TokenizerBuilder();
$builder->setDictionary('embedded://ipadic');
$builder->appendTokenFilter('japanese_stop_tags', [
    'tags' => ['助詞,格助詞,一般', '助詞,係助詞', '助詞,連体化', '助動詞'],
]);
$tokenizer = $builder->build();

タグは 4 階層のカンマ区切りに正規化され(不足分は * で補完)、各トークンの先頭 4 つの品詞詳細と完全一致で比較されます。IPADIC の助詞トークンは必ず 助詞,係助詞 のようにサブカテゴリを持つため、助詞 単独では一致しません。一方、助動詞はサブカテゴリを持たないため(助動詞,*,*,*)、助動詞 単独で一致します。

japanese_keep_tags

指定されたタグに一致する品詞のトークンのみを保持します。その他のトークンはすべて除去されます。

<?php

$builder = new Lindera\TokenizerBuilder();
$builder->setDictionary('embedded://ipadic');
$builder->appendTokenFilter('japanese_keep_tags', [
    'tags' => ['名詞,一般'],
]);
$tokenizer = $builder->build();

パイプラインの完全な例

以下の例では、複数の文字フィルタとトークンフィルタを1つのパイプラインに組み合わせています:

<?php

$builder = new Lindera\TokenizerBuilder();

// Set mode and dictionary
$builder->setMode('normal');
$builder->setDictionary('embedded://ipadic');

// Preprocessing
$builder->appendCharacterFilter('unicode_normalize', ['kind' => 'nfkc']);
$builder->appendCharacterFilter(
    'japanese_iteration_mark',
    ['normalize_kanji' => 'true', 'normalize_kana' => 'true']
);
$builder->appendCharacterFilter('mapping', ['mapping' => ['リンデラ' => 'lindera']]);

// Postprocessing
$builder->appendTokenFilter('japanese_katakana_stem', ['min' => 3]);
$builder->appendTokenFilter('japanese_stop_tags', [
    'tags' => [
        '接続詞',
        '助詞,格助詞,一般',
        '助詞,格助詞,一般',
        '助詞,係助詞',
        '助詞,副助詞',
        '助詞,終助詞',
        '助詞,連体化',
        '助動詞',
        '記号',
        '記号,一般',
        '記号,読点',
        '記号,句点',
        '記号,空白',
    ],
]);
$builder->appendTokenFilter('lowercase');

// Build the tokenizer
$tokenizer = $builder->build();

// Tokenize
$text = 'Linderaは形態素解析エンジンです。';
$tokens = $tokenizer->tokenize($text);

foreach ($tokens as $token) {
    echo $token->surface . "\t" . implode(',', $token->details) . "\n";
}

このパイプラインでは:

  1. unicode_normalize が全角文字を半角に変換(NFKC 正規化)
  2. japanese_iteration_mark が踊り字を展開
  3. mapping が指定された文字列を置換
  4. japanese_katakana_stem がカタカナ語をステミング
  5. japanese_stop_tags が助詞、助動詞、記号を除去
  6. lowercase がアルファベットを小文字に正規化

学習

Lindera PHP は、アノテーション付きコーパスからカスタム CRF ベースの形態素解析モデルを学習する機能をサポートしています。この機能には train feature が必要です。

前提条件

train feature を有効にして lindera-php をビルドします(デフォルトで有効):

cargo build -p lindera-php --features train

モデルの学習

Lindera\Trainer::train() を使用して、種辞書とアノテーション付きコーパスから CRF モデルを学習します:

<?php

Lindera\Trainer::train(
    '/path/to/seed.csv',         // seed: 種辞書ファイル
    '/path/to/corpus.txt',       // corpus: アノテーション付きコーパス
    '/path/to/char.def',         // char_def: 文字定義ファイル
    '/path/to/unk.def',          // unk_def: 未知語定義ファイル
    '/path/to/feature.def',      // feature_def: 素性定義ファイル
    '/path/to/rewrite.def',      // rewrite_def: 書き換えルール定義ファイル
    '/tmp/model.dat',            // output: 学習済みモデルの出力パス
    0.01,                        // lambda: L1 正則化コスト
    100,                         // max_iter: 最大イテレーション数
    null                         // max_threads: スレッド数(null = 自動検出)
);

学習パラメータ

パラメータデフォルト説明
$seedstring必須種辞書ファイルのパス(CSV 形式)
$corpusstring必須アノテーション付き学習コーパスのパス
$charDefstring必須文字定義ファイルのパス(char.def)
$unkDefstring必須未知語定義ファイルのパス(unk.def)
$featureDefstring必須素性定義ファイルのパス(feature.def)
$rewriteDefstring必須書き換えルール定義ファイルのパス(rewrite.def)
$outputstring必須学習済みモデルファイルの出力パス
$lambdafloat0.01L1 正則化コスト(0.0--1.0)
$maxIterint100最大学習イテレーション数
$maxThreadsint または nullnullスレッド数(null = CPU コア数を自動検出)

学習済みモデルのエクスポート

学習後、Lindera\Trainer::export() を使用してモデルを辞書ソースファイルにエクスポートします:

<?php

Lindera\Trainer::export(
    '/tmp/model.dat',                    // model: 学習済みモデルファイル
    '/tmp/dictionary_source',            // output: 出力ディレクトリ
    '/path/to/metadata.json'             // metadata: メタデータファイル(省略可)
);

エクスポートパラメータ

パラメータデフォルト説明
$modelstring必須学習済みモデルファイルのパス(.dat)
$outputstring必須辞書ソースファイルの出力ディレクトリ
$metadatastring または nullnullベースとなる metadata.json ファイルのパス

エクスポートにより、出力ディレクトリに以下のファイルが作成されます:

  • lex.csv -- 学習済みコスト付きのレキシコンエントリー
  • matrix.def -- 連接コスト行列
  • unk.def -- 未知語定義
  • char.def -- 文字カテゴリ定義
  • metadata.json -- 更新されたメタデータ($metadata パラメータ指定時)

完全なワークフロー

カスタム辞書の学習と使用の完全なワークフロー:

<?php

// Step 1: Train the CRF model
Lindera\Trainer::train(
    'resources/training/seed.csv',
    'resources/training/corpus.txt',
    'resources/training/char.def',
    'resources/training/unk.def',
    'resources/training/feature.def',
    'resources/training/rewrite.def',
    '/tmp/model.dat',
    0.01,   // lambda
    100,    // max_iter
    null    // max_threads
);

// Step 2: Export to dictionary source files
Lindera\Trainer::export(
    '/tmp/model.dat',
    '/tmp/dictionary_source',
    'resources/training/metadata.json'
);

// Step 3: Build the dictionary from exported source files
$metadata = Lindera\Metadata::fromJsonFile('/tmp/dictionary_source/metadata.json');
Lindera\Dictionary::build('/tmp/dictionary_source', '/tmp/dictionary', $metadata);

// Step 4: Use the trained dictionary
$dictionary = Lindera\Dictionary::load('/tmp/dictionary');
$tokenizer = new Lindera\Tokenizer($dictionary, 'normal');

$tokens = $tokenizer->tokenize('形態素解析のテスト');
foreach ($tokens as $token) {
    echo $token->surface . "\t" . implode(',', $token->details) . "\n";
}

Lindera WASM

Lindera WASM は、wasm-bindgen を使用して構築された Lindera 形態素解析エンジンの WebAssembly バインディングです。Web ブラウザ、Node.js、バンドラー環境で日本語、韓国語、中国語のテキストトークナイズを直接実行できます。

配布フォーマット

Lindera WASM は wasm-pack を通じて複数の配布フォーマットをサポートしています:

ターゲット用途モジュールシステム
webブラウザ ESMES Modules
bundlerWebpack、Vite、RollupES Modules(バンドラー解決)

辞書パッケージ

各パッケージはオフライン使用のために特定の辞書を埋め込みます:

Feature フラグ辞書言語
(なし)埋め込み辞書なし--
embed-ipadicIPADIC日本語
embed-unidicUniDic日本語
embed-ko-dicko-dic韓国語
embed-cc-cedictCC-CEDICT中国語
embed-jiebaJieba中国語
embed-cjkIPADIC + ko-dic + JiebaCJK

セクション

インストール

前提条件

  • Rust(stable ツールチェーン)
  • wasm-pack(v0.10 以降)

辞書の入手

Lindera WASM はデフォルトで辞書を同梱しません。ブラウザ環境では、OPFS(Origin Private File System)API を使用して辞書を実行時にダウンロードする方法を推奨します。

GitHub Releases からのダウンロード

ビルド済み辞書は GitHub Releases ページから入手できます。ブラウザ環境では、OPFS ヘルパーを使用して辞書をダウンロードしてキャッシュします:

import { downloadDictionary, hasDictionary } from 'lindera-wasm/opfs';

if (!await hasDictionary("ipadic")) {
    await downloadDictionary(
        "https://github.com/lindera/lindera/releases/download/<version>/lindera-ipadic-<version>.zip",
        "ipadic",
    );
}

詳細は OPFS 辞書ストレージ を参照してください。

wasm-pack によるビルド

公開されている npm パッケージと同じ構成でビルドするには、web ターゲットを使用します:

wasm-pack build --target web

出力は lindera-wasm クレート内の pkg/ ディレクトリに書き込まれます。

web ターゲットのビルドは、ブラウザからネイティブ ES モジュールとして直接利用できるほか、モダンなバンドラー(Vite、Webpack 5 の asyncWebAssembly など)からもそのまま利用できます。バンドラー専用のビルドを別途用意する必要はありません。

なぜ web ターゲットのみなのか(bundler ではなく)

wasm-pack のデフォルトの --targetbundler ですが、公開されている lindera-wasm パッケージは意図的に --target web でビルドしています。ターゲット名はやや誤解を招きやすいため、それぞれの実態を説明します:

  • --target bundler(wasm-pack のデフォルト)は、.wasm ファイルを ES モジュールとして直接 import する JavaScript を生成します。これはまだ標準化が完了していない WebAssembly ESM integration 提案に依存しており、実際にこの出力を解釈できるのは事実上 asyncWebAssembly experiment を有効にした Webpack だけです。このデフォルトは Webpack が支配的だった時代の名残であり、「bundler 汎用」という名前に反して実態は Webpack 専用に近い出力です。たとえば Vite で利用するには追加プラグイン(vite-plugin-wasm と top-level-await 対応)が必要です。
  • --target web は標準技術のみで構成されたコードを生成します。.wasm ファイルは new URL('lindera_wasm_bg.wasm', import.meta.url) で解決され、明示的な非同期 init 関数の中で fetch + WebAssembly.instantiateStreaming により読み込まれます。ビルドステップなしでブラウザのネイティブ ES モジュールとして動作するほか、モダンなバンドラー(Vite、Webpack 5、Rollup)は new URL(..., import.meta.url) パターンを認識して .wasm ファイルをアセットとして配置します。唯一のコストは、API を使う前に一度 await __wbg_init() を呼ぶ必要があることです。

まとめると、bundler ターゲットは Webpack に限ればゼロコンフィグですが、web ターゲットは明示的な init 呼び出し 1 回のコストでどこでも動きます。v5 までは両方のビルド(lindera-wasm-weblindera-wasm-bundler)を公開していましたが、v6 からは可搬性の高い単一ビルドに統合しました。lindera-wasm-bundler からの移行は v5 から v6 への移行ガイドを参照してください。

利用可能な Feature フラグ(上級者向け)

辞書を WASM バイナリに直接埋め込みたい上級者向けに、以下の feature フラグが利用できます。バイナリサイズが大幅に増加しますが、実行時の辞書ダウンロードが不要になります。

Feature辞書言語
embed-ipadicIPADIC日本語
embed-unidicUniDic日本語
embed-sudachidictSudachiDict日本語(注意: 約570MB、WASMでは通常非現実的)
embed-ko-dicko-dic韓国語
embed-cc-cedictCC-CEDICT中国語
embed-jiebaJieba中国語
embed-cjkIPADIC + ko-dic + JiebaCJK(全言語)

複数の feature フラグを有効にして複数の辞書を組み合わせることができます:

wasm-pack build --target web --features embed-ipadic,embed-ko-dic

npm パッケージの命名規則

スコープなしの lindera-wasm-* プレフィックスは Lindera プロジェクトの名前空間です。辞書を埋め込んだ独自ビルドを npm に公開する場合は、公式パッケージとの誤認を避けるため、必ず自分のスコープ配下の名前で公開してください:

@your-scope/lindera-wasm-{dict}

例:

  • @your-scope/lindera-wasm-ipadic
  • @your-scope/lindera-wasm-unidic
  • @your-scope/lindera-wasm-cjk

公開前にパッケージ名を設定するには、生成された pkg/package.jsonname フィールドを編集します。なお、辞書を埋め込んだパッケージは辞書の再配布にあたるため、各辞書のライセンス条件(帰属表示など)にも従ってください。

[!NOTE] 本プロジェクトのリリースワークフロー(.github/workflows/release.yml)が実際にビルドして npm に公開しているのは、embed-* feature を一切使わずに web ターゲットでビルドした lindera-wasm という汎用パッケージのみです。lindera-wasm-ipadic のようなスコープなしの辞書名付きパッケージ名は、v3 以前のリリースの名残としてプロジェクトが確保しているもので、現在は更新されていません。第三者がこれらの名前で公開することはできません(しないでください)。本ドキュメント内でのこれらの名前は、対応する embed-* feature(上記の利用可能な Feature フラグを参照)でローカルビルドした場合の説明用の例に過ぎません。

npm からのインストール

ビルド済みパッケージが npm で公開されています:

npm install lindera-wasm

または yarn で:

yarn add lindera-wasm

[!NOTE] npm パッケージには辞書が含まれていません。OPFS ヘルパーを使用して辞書を実行時にダウンロードしてください。詳細は OPFS 辞書ストレージ を参照してください。

クイックスタート

Web(ブラウザ) -- OPFS 辞書ロード

推奨される方法は、OPFS ヘルパーを使用して辞書を実行時にダウンロードすることです:

import __wbg_init, { TokenizerBuilder, loadDictionaryFromBytes } from 'lindera-wasm';
import { downloadDictionary, loadDictionaryFiles, hasDictionary } from 'lindera-wasm/opfs';

async function main() {
    await __wbg_init();

    // キャッシュされていない場合は辞書をダウンロード
    if (!await hasDictionary("ipadic")) {
        await downloadDictionary(
            "https://github.com/lindera/lindera/releases/download/<version>/lindera-ipadic-<version>.zip",
            "ipadic",
        );
    }

    // OPFS から辞書を読み込み
    const files = await loadDictionaryFiles("ipadic");
    const dictionary = loadDictionaryFromBytes(
        files.metadata, files.dictTrie, files.dictValsIdx, files.dictVals,
        files.dictWordsIdx, files.dictWords, files.matrixMtx, files.charDef,
        files.unk,
    );

    // トークナイザーを構築
    const builder = new TokenizerBuilder();
    builder.setDictionaryInstance(dictionary);
    builder.setMode("normal");
    const tokenizer = builder.build();

    const tokens = tokenizer.tokenize("関西国際空港限定トートバッグ");
    tokens.forEach(token => {
        console.log(`${token.surface}\t${token.details.join(',')}`);
    });
}

main();

注意: ビルド済み辞書を GitHub Releases からダウンロードしてください。完全なワークフローは OPFS 辞書ストレージ を参照してください。

期待される出力:

関西国際空港    名詞,固有名詞,一般,*,*,*,関西国際空港,カンサイコクサイクウコウ,カンサイコクサイクーコー
限定    名詞,サ変接続,*,*,*,*,限定,ゲンテイ,ゲンテイ
トートバッグ    名詞,一般,*,*,*,*,*,*,*

埋め込み辞書の使用(上級者向け)

embed-* feature フラグ付きでビルドした場合、埋め込み辞書を使用できます:

[!NOTE] ここでの lindera-wasm-ipadic は説明用のパッケージ名であり、npm に公開されているものではありません。実際に公開されているのは lindera-wasm のみです。このようなパッケージを自分でビルド・命名する方法は npm パッケージの命名規則 を参照してください。

import __wbg_init, { TokenizerBuilder } from 'lindera-wasm-ipadic';

async function main() {
    await __wbg_init();

    const builder = new TokenizerBuilder();
    builder.setDictionary("embedded://ipadic");
    builder.setMode("normal");
    const tokenizer = builder.build();

    const tokens = tokenizer.tokenize("関西国際空港限定トートバッグ");
    tokens.forEach(token => {
        console.log(`${token.surface}\t${token.details.join(',')}`);
    });
}

main();

フィルタの使用

トークナイズパイプラインに文字フィルタやトークンフィルタを追加できます:

import __wbg_init, { TokenizerBuilder, loadDictionaryFromBytes } from 'lindera-wasm';
import { loadDictionaryFiles } from 'lindera-wasm/opfs';

async function main() {
    await __wbg_init();

    // OPFS にキャッシュ済みの辞書を読み込み
    const files = await loadDictionaryFiles("ipadic");
    const dictionary = loadDictionaryFromBytes(
        files.metadata, files.dictTrie, files.dictValsIdx, files.dictVals,
        files.dictWordsIdx, files.dictWords, files.matrixMtx, files.charDef,
        files.unk,
    );

    const builder = new TokenizerBuilder();
    builder.setDictionaryInstance(dictionary);
    builder.setMode("normal");

    // Add Unicode NFKC normalization
    builder.appendCharacterFilter("unicode_normalize", { kind: "nfkc" });

    // Add a stop-tags filter to remove particles and auxiliary verbs
    builder.appendTokenFilter("japanese_stop_tags", {
        tags: ["助詞,格助詞,一般", "助詞,係助詞", "助詞,連体化", "助動詞"]
    });

    const tokenizer = builder.build();
    const tokens = tokenizer.tokenize("Linderaは形態素解析エンジンです");
    tokens.forEach(token => {
        console.log(`${token.surface}\t${token.details.join(',')}`);
    });
}

main();

N-Best トークナイズ

コスト順にランク付けされた複数のトークナイズ候補を取得します:

const results = tokenizer.tokenizeNbest("すもももももももものうち", 3);
results.forEach((result, rank) => {
    console.log(`--- NBEST ${rank + 1} (cost=${result.cost}) ---`);
    result.tokens.forEach(token => {
        console.log(`${token.surface}\t${token.details.join(',')}`);
    });
});

Tokenizer API

このページでは、lindera-wasm が公開する JavaScript/TypeScript API について説明します。

TokenizerBuilder

設定済みの Tokenizer インスタンスを作成するためのビルダークラスです。

コンストラクタ

const builder = new TokenizerBuilder();

デフォルト設定で新しいビルダーを作成します。

メソッド

すべてのセッターは同じ設定を共有するビルダーハンドルを返すため、 builder.setMode("normal").setDictionary(...) のようにチェーンでも、1 文ずつでも記述できます。 返されるハンドルは新しいオブジェクトですが、設定先は同一のビルダーです。

setMode(mode)

トークナイズモードを設定します。

  • パラメータ: mode (string) -- "normal" または "decompose"
  • 戻り値: void
builder.setMode("normal");

setDictionary(uri)

トークナイズに使用する辞書を設定します。

  • パラメータ: uri (string) -- 辞書の URI(例: "embedded://ipadic"
  • 戻り値: void
builder.setDictionary("embedded://ipadic");

setDictionaryInstance(dictionary)

読み込み済みの辞書インスタンスをトークナイズに設定します。 URI の代わりにバイトデータから読み込んだ辞書(例: loadDictionaryFromBytes() 経由)を使用する場合に使います。

  • パラメータ: dictionary (Dictionary) -- 読み込み済みの辞書オブジェクト
  • 戻り値: void
import { loadDictionaryFromBytes } from 'lindera-wasm';
import { loadDictionaryFiles } from 'lindera-wasm/opfs';

const files = await loadDictionaryFiles("ipadic");
const dictionary = loadDictionaryFromBytes(
    files.metadata, files.dictTrie, files.dictValsIdx, files.dictVals,
    files.dictWordsIdx, files.dictWords, files.matrixMtx, files.charDef,
    files.unk,
);

builder.setDictionaryInstance(dictionary);

setUserDictionaryInstance(userDictionary)

読み込み済みのユーザー辞書インスタンスを設定します。loadUserDictionaryFromBytes()(CSV)または loadUserDictionaryBinFromBytes()(ビルド済み .bin)でバイト列から読み込んでください。WebAssembly では URI ベースのユーザー辞書は使用できません。

  • パラメータ: userDictionary (UserDictionary) -- 読み込み済みのユーザー辞書オブジェクト
  • 戻り値: void

setKeepWhitespace(keep)

出力に空白トークンを保持するかどうかを設定します。

  • パラメータ: keep (boolean) -- true で空白トークンを保持
  • 戻り値: void
builder.setKeepWhitespace(true);

appendCharacterFilter(name, args)

前処理パイプラインに文字フィルタを追加します。

  • パラメータ:
    • name (string) -- フィルタ名(例: "unicode_normalize""japanese_iteration_mark"
    • args (object, 省略可) -- フィルタの設定
  • 戻り値: void
builder.appendCharacterFilter("unicode_normalize", { kind: "nfkc" });

appendTokenFilter(name, args)

後処理パイプラインにトークンフィルタを追加します。

  • パラメータ:
    • name (string) -- フィルタ名(例: "japanese_stop_tags""lowercase"
    • args (object, 省略可) -- フィルタの設定
  • 戻り値: void
builder.appendTokenFilter("japanese_stop_tags", {
    tags: ["助詞,格助詞,一般", "助詞,係助詞", "助詞,連体化", "助動詞", "記号,句点", "記号,読点"]
});

build()

設定済みの Tokenizer インスタンスをビルドして返します。ビルド後もビルダーはそのまま使えるため、同じ設定から複数のトークナイザーをビルドできます。

  • 戻り値: Tokenizer
const tokenizer = builder.build();

Tokenizer

メインのトークナイザークラスです。TokenizerBuilder.build() またはコンストラクタ経由で作成できます。

Tokenizer コンストラクタ

const tokenizer = new Tokenizer(dictionary, mode, userDictionary);
  • パラメータ:
    • dictionary (Dictionary) -- 読み込み済みの辞書オブジェクト
    • mode (string, 省略可) -- トークナイズモード("normal" または "decompose"、デフォルト: "normal"
    • userDictionary (UserDictionary, 省略可) -- 読み込み済みのユーザー辞書

Tokenizer メソッド

tokenize(text)

入力テキストをトークナイズします。

  • パラメータ: text (string) -- トークナイズするテキスト
  • 戻り値: Token[] -- トークンオブジェクトの配列
const tokens = tokenizer.tokenize("関西国際空港");

tokenizeSurfaces(text)

入力テキストをトークナイズし、トークンの surface のみを返します。分かち書き用途の高速パスです。トークンオブジェクトを生成せず、形態素の詳細情報もロードしないため、surface 文字列だけが必要な場合は tokenize より大幅に高速です。結果は tokenizer.tokenize(text).map((t) => t.surface) と一致します。(Web Worker とは無関係です。)

  • パラメータ: text (string) -- トークナイズするテキスト
  • 戻り値: string[] -- surface 文字列の配列
const surfaces = tokenizer.tokenizeSurfaces("関西国際空港");
// ["関西国際空港"]

tokenizeNbest(text, n, unique?, costThreshold?)

トータルパスコスト順に N-best トークナイズ結果を返します。

  • パラメータ:
    • text (string) -- トークナイズするテキスト
    • n (number) -- 返す結果の数
    • unique (boolean, 省略可) -- 同一のセグメンテーション結果を重複排除(デフォルト: false
    • costThreshold (bigint, 省略可) -- bestCost + threshold 以内のパスのみ返す
  • 戻り値: { tokens: Token[], cost: number } の配列
const results = tokenizer.tokenizeNbest("すもももももももものうち", 3);

// コスト閾値を指定する場合 -- bigint リテラルとして渡す必要がある点に注意
const resultsWithThreshold = tokenizer.tokenizeNbest("すもももももももものうち", 3, false, 100n);

Token

トークナイザーが生成する単一のトークンで、プレーンな JavaScript オブジェクトです。

プロパティ

プロパティ説明
surfacestringトークンの表層形
byteStartnumber元テキストでの開始バイトオフセット
byteEndnumber元テキストでの終了バイトオフセット
positionnumberトークンの位置インデックス
wordIdnumber辞書内の単語 ID
isUnknownboolean未知語かどうか
detailsstring[]形態素の詳細フィールド

[!NOTE] トークンは wasm_bindgen のクラスインスタンスではなくプレーンオブジェクトです。クラスインスタンスは JavaScript 側が解放するまでデータを Rust ヒープ上に保持するため、yield しない同期ループでトークナイズするとメモリが蓄積します。プレーンオブジェクトなら Rust 側に確保されるものが無く、結果は JSON.stringifystructuredClone、worker への転送を変換なしで通過します。フィールド名は lindera-nodejs バインディングと同じ camelCase です。

詳細情報の読み出し

details を直接インデックスします。範囲外のインデックスは undefined になります。

const pos = token.details[0];     // 例: "名詞"
const reading = token.details[7]; // 例: "トウキョウ"

トークンのシリアライズ

トークンは既にプレーンオブジェクトなので、そのままシリアライズできます。

console.log(JSON.stringify(token, null, 2));

ヘルパー関数

[!NOTE] 以下の例は lindera-wasm-ipadic からインポートしていますが、これは embed-ipadic feature を使ってローカルビルドした場合の説明用パッケージ名であり、npm に公開されているものではありません。実際に公開されているのは lindera-wasm のみです。詳細は npm パッケージの命名規則 を参照してください。

loadDictionary(uri)

指定された URI から辞書を読み込みます。

  • パラメータ: uri (string) -- 辞書の URI(例: "embedded://ipadic"
  • 戻り値: Dictionary
import { loadDictionary } from 'lindera-wasm-ipadic';

const dict = loadDictionary("embedded://ipadic");

loadUserDictionaryFromBytes(csv, metadata)

CSV バイト列(UTF-8)からユーザー辞書をビルドします。バイト列は fetch・ファイル入力・OPFS から取得します。

  • パラメータ:
    • csv (Uint8Array) -- ユーザー辞書 CSV の内容
    • metadata (Metadata) -- 組み合わせるシステム辞書のメタデータ(例: dictionary.metadata
  • 戻り値: UserDictionary

loadUserDictionaryBinFromBytes(bytes)

ビルド済みユーザー辞書(lindera build --user の出力)をバイト列から読み込みます。

  • パラメータ: bytes (Uint8Array) -- .bin の内容
  • 戻り値: UserDictionary

version() / getVersion()

lindera-wasm パッケージのバージョン文字列を返します。

  • 戻り値: string
import { version } from 'lindera-wasm-ipadic';

console.log(version()); // 例: "6.0.0"

列挙型とユーティリティクラス

Mode

トークナイズモードの列挙型です。

説明
Mode.Normal辞書コストに基づく標準的なトークナイズ
Mode.Decomposeペナルティベースのセグメンテーションによる複合語分解

Penalty

decompose モードの設定です。複合語をどの程度積極的に分解するかを制御します。

const penalty = new Penalty(
    kanjiThreshold?,     // 漢字の長さ閾値(デフォルト: 2)
    kanjiPenalty?,       // 漢字の長さペナルティ(デフォルト: 3000)
    otherThreshold?,     // その他の文字の長さ閾値(デフォルト: 7)
    otherPenalty?,       // その他の文字の長さペナルティ(デフォルト: 1700)
);
プロパティデフォルト説明
kanji_penalty_length_thresholdnumber2漢字複合語分割の長さ閾値
kanji_penalty_length_penaltynumber3000閾値を超える漢字複合語のペナルティコスト
other_penalty_length_thresholdnumber7非漢字複合語分割の長さ閾値
other_penalty_length_penaltynumber1700閾値を超える非漢字複合語のペナルティコスト

LinderaError

Lindera 操作のエラー型です。

const error = new LinderaError("message");
console.log(error.message);    // "message"
console.log(error.toString()); // "message"
プロパティ / メソッド説明
messagestringエラーメッセージ
toString()stringエラーメッセージを返す

[!NOTE] LinderaError はユーティリティクラスとしてエクスポートされていますが、TokenizerBuilderTokenizer・辞書読み込み関数(lindera-wasm/src/tokenizer.rslindera-wasm/src/dictionary.rs)の実際のエラーパスはすべて JsValue::from_str(...) で reject しており、JsLinderaError/LinderaError のインスタンスではありません。そのため、これらの API が投げるエラーは JavaScript 側では単なる文字列として現れます。catch (e) { ... } で捕捉する際は、eLinderaError インスタンスではなく string として扱ってください。

snake_case エイリアス

Python API との一貫性のため、すべてのメソッドは snake_case 形式でも利用可能です:

camelCasesnake_case
setMode()set_mode()
setDictionary()set_dictionary()
setDictionaryInstance()set_dictionary_instance()
setUserDictionaryInstance()set_user_dictionary_instance()
setKeepWhitespace()set_keep_whitespace()
appendCharacterFilter()append_character_filter()
appendTokenFilter()append_token_filter()
tokenizeSurfaces()tokenize_surfaces()
tokenizeNbest()tokenize_nbest()
loadDictionary()load_dictionary()
loadDictionaryFromBytes()load_dictionary_from_bytes()
loadUserDictionaryFromBytes()load_user_dictionary_from_bytes()
loadUserDictionaryBinFromBytes()load_user_dictionary_bin_from_bytes()

辞書管理

OPFS からの辞書読み込み

WASM で辞書を使用する推奨方法は、GitHub Releases からダウンロードし、OPFS 経由で読み込むことです。これにより、WASM バイナリに大きな辞書を埋め込む必要がなくなります。

バイトデータからの読み込み

OPFS やその他のブラウザストレージに保存された辞書を loadDictionaryFromBytes() で読み込みます。

loadDictionaryFromBytes(metadata, dictTrie, dictValsIdx, dictVals, dictWordsIdx, dictWords, matrixMtx, charDef, unk)

  • パラメータ:
    • metadata (Uint8Array) -- metadata.json の内容
    • dictTrie (Uint8Array) -- dict.trie の内容(文字単位のダブル配列トライ)
    • dictValsIdx (Uint8Array) -- dict.valsidx の内容(単語値インデックス)
    • dictVals (Uint8Array) -- dict.vals の内容(単語値データ)
    • dictWordsIdx (Uint8Array) -- dict.wordsidx の内容(単語詳細インデックス)
    • dictWords (Uint8Array) -- dict.words の内容(単語詳細)
    • matrixMtx (Uint8Array) -- matrix.mtx の内容(連接コスト行列)
    • charDef (Uint8Array) -- char_def.bin の内容(文字定義)
    • unk (Uint8Array) -- unk.bin の内容(未知語辞書)
  • 戻り値: Dictionary
import { loadDictionaryFromBytes, TokenizerBuilder } from 'lindera-wasm';
import { loadDictionaryFiles } from 'lindera-wasm/opfs';

// OPFS から辞書ファイルを読み込む
const files = await loadDictionaryFiles("ipadic");

// バイトデータから Dictionary を作成
const dictionary = loadDictionaryFromBytes(
    files.metadata,
    files.dictTrie,
    files.dictValsIdx,
    files.dictVals,
    files.dictWordsIdx,
    files.dictWords,
    files.matrixMtx,
    files.charDef,
    files.unk,
);

// TokenizerBuilder で使用
const builder = new TokenizerBuilder();
builder.setDictionaryInstance(dictionary);
builder.setMode("normal");
const tokenizer = builder.build();

ダウンロードとキャッシュを含む完全なワークフローは OPFS 辞書ストレージ を参照してください。

埋め込み辞書(上級者向け)

embed-* feature フラグ付きでビルドした場合、embedded:// URI スキームで埋め込み辞書を読み込めます。WASM バイナリのサイズが大幅に増加します。

[!NOTE] 以下の例の lindera-wasm-ipadicembed-ipadic feature でローカルビルドした場合の説明用パッケージ名であり、npm に公開されているものではありません。実際に公開されているのは lindera-wasm のみです。詳細は npm パッケージの命名規則 を参照してください。

埋め込み辞書の読み込み

import { loadDictionary } from 'lindera-wasm-ipadic';

const dictionary = loadDictionary("embedded://ipadic");

利用可能な埋め込み辞書の URI(ビルド時に有効にした feature に依存):

URIFeature フラグ
embedded://ipadicembed-ipadic
embedded://unidicembed-unidic
embedded://ko-dicembed-ko-dic
embedded://cc-cedictembed-cc-cedict
embedded://jiebaembed-jieba

TokenizerBuilder での使用

const builder = new TokenizerBuilder();
builder.setDictionary("embedded://ipadic");
builder.setMode("normal");
const tokenizer = builder.build();

Tokenizer コンストラクタでの使用

import { loadDictionary, Tokenizer } from 'lindera-wasm-ipadic';

const dictionary = loadDictionary("embedded://ipadic");
const tokenizer = new Tokenizer(dictionary, "normal");

Dictionary クラス

Dictionary クラスは、読み込み済みの形態素解析辞書を表します。

プロパティ

プロパティ説明
namestring辞書名(例: "ipadic"
encodingstring辞書の文字エンコーディング
metadataMetadata完全なメタデータオブジェクト
console.log(dictionary.name);     // "ipadic"
console.log(dictionary.encoding); // "utf-8"

ユーザー辞書

ユーザー辞書を使用すると、システム辞書にないカスタム語彙を追加できます。

WebAssembly にはファイルシステムが無いため、ユーザー辞書は JavaScript 側で 取得したバイト列fetch<input type="file">・OPFS)から読み込みます。

CSV バイト列からのユーザー辞書の読み込み

ユーザー辞書を組み合わせるシステム辞書のメタデータ(例: dictionary.metadata)を渡してください。CSV の内容は UTF-8 である必要が あります。

import { loadUserDictionaryFromBytes } from 'lindera-wasm';

const response = await fetch('/dictionaries/user_dict.csv');
const csvBytes = new Uint8Array(await response.arrayBuffer());
const userDict = loadUserDictionaryFromBytes(csvBytes, dictionary.metadata);

OPFS 上のファイルも同じ形で使えます:

const root = await navigator.storage.getDirectory();
const handle = await root.getFileHandle('user_dict.csv');
const csvBytes = new Uint8Array(await (await handle.getFile()).arrayBuffer());
const userDict = loadUserDictionaryFromBytes(csvBytes, dictionary.metadata);

ビルド済みユーザー辞書(.bin)の読み込み

lindera build --user でコンパイルしたユーザー辞書はそのまま読み込めます:

import { loadUserDictionaryBinFromBytes } from 'lindera-wasm';

const response = await fetch('/dictionaries/user_dict.bin');
const binBytes = new Uint8Array(await response.arrayBuffer());
const userDict = loadUserDictionaryBinFromBytes(binBytes);

Tokenizer でのユーザー辞書の使用

import { loadDictionaryFromBytes, loadUserDictionaryFromBytes, Tokenizer } from 'lindera-wasm';
import { loadDictionaryFiles } from 'lindera-wasm/opfs';

const files = await loadDictionaryFiles("ipadic");
const dictionary = loadDictionaryFromBytes(
    files.metadata, files.dictTrie, files.dictValsIdx, files.dictVals,
    files.dictWordsIdx, files.dictWords, files.matrixMtx, files.charDef,
    files.unk,
);
const response = await fetch('/dictionaries/user_dict.csv');
const csvBytes = new Uint8Array(await response.arrayBuffer());
const userDict = loadUserDictionaryFromBytes(csvBytes, dictionary.metadata);
const tokenizer = new Tokenizer(dictionary, "normal", userDict);

ユーザー辞書の CSV フォーマット

ユーザー辞書の CSV は Lindera ユーザー辞書と同じフォーマットに準拠します。 IPADIC のシンプル形式は以下のとおりです:

東京スカイツリー,カスタム名詞,トウキョウスカイツリー
東武スカイツリーライン,カスタム名詞,トウブスカイツリーライン

各行の構成: surface,part_of_speech,reading。辞書のフル形式 (IPADIC では 13 フィールド以上)の行も併用できます。内容は UTF-8 で ある必要があります。

辞書のビルド

WebAssembly では辞書のビルドはできません。ビルドはソースディレクトリを 読み出力ディレクトリへ書き込みますが、wasm32-unknown-unknown には ファイルシステムが無いためです。辞書は lindera CLI(またはネイティブ バインディング)でビルドし、結果をバイト列としてここに読み込んでください。 ビルド済み辞書のダウンロードは OPFS 辞書管理 を参照してください。

Metadata

Metadata クラスは辞書のパラメータを設定します。

コンストラクタ

const metadata = new Metadata(name?, encoding?);
  • パラメータ:
    • name (string, 省略可) -- 辞書名(デフォルト: "default"
    • encoding (string, 省略可) -- 文字エンコーディング(デフォルト: "UTF-8"

静的メソッド

Metadata.createDefault()

デフォルト値で Metadata インスタンスを作成します。

const metadata = Metadata.createDefault();

Metadata プロパティ

プロパティデフォルト説明
namestring"default"辞書名
encodingstring"UTF-8"文字エンコーディング
dictionary_schemaSchemaIPADIC スキーマメイン辞書のスキーマ
user_dictionary_schemaSchema最小スキーマユーザー辞書のスキーマ

すべてのプロパティは取得と設定の両方に対応しています:

const metadata = Metadata.createDefault();
metadata.name = "custom_dict";
metadata.encoding = "EUC-JP";
console.log(metadata.name); // "custom_dict"

[!NOTE] Python・Node.js・Ruby・PHP の各バインディングと異なり、WASM の Metadata クラスは default_word_costdefault_left_context_iddefault_right_context_iddefault_field_valueflexible_csvskip_invalid_cost_or_idnormalize_details を取得・設定可能なプロパティとして公開していません(lindera-wasm/src/metadata.rs 参照)。これらは常にバインディング共通のデフォルト値(コスト -10000、文脈 ID 1288、フィールド値 "*"、フラグはすべて false)にフォールバックし、JavaScript から変更することはできません。

読み込み済み辞書のメタデータには dictionary.metadata からアクセスできます。

Schema

Schema クラスは辞書エントリのフィールド構造を定義します。

Schema コンストラクタ

const schema = new Schema(["surface", "left_id", "right_id", "cost", "pos", "reading"]);

Schema 静的メソッド

  • Schema.create_default() -- IPADIC のレイアウトを緩やかに踏襲した組み込みの 13 フィールドスキーマを作成する。内訳は 4 つのシステムフィールド(surfaceleft_context_idright_context_idcost)に続く 9 つの汎用素性フィールド(major_pospos_detail_1pos_detail_3conjugation_typeconjugation_formbase_formreadingpronunciation)。これらのフィールド名(および conjugation_type/conjugation_form の順序)は、実際の lindera-ipadic 辞書スキーマ(part_of_speechpart_of_speech_subcategory_1_3conjugation_formconjugation_type、...)とは異なる。実際の IPADIC 辞書のスキーマに合わせたい場合は、読み込み済み辞書の dictionary.metadata.dictionary_schema を使用すること

Schema メソッド

メソッド戻り値説明
get_field_index(name)number | undefinedフィールド名からインデックスを取得
field_count()numberフィールドの総数
get_field_name(index)string | undefinedインデックスからフィールド名を取得
get_custom_fields()string[]インデックス 3 以降のフィールド(形態素素性)
get_all_fields()string[]すべてのフィールド名
get_field_by_name(name)FieldDefinition | undefinedフィールド定義の完全な情報を取得

FieldDefinition

プロパティ説明
indexnumberフィールドの位置インデックス
namestringフィールド名
field_typeFieldTypeフィールド型の列挙値
descriptionstring | undefined説明(省略可)

FieldType

説明
FieldType.Surface単語の表層形テキスト
FieldType.LeftContextId左文脈 ID
FieldType.RightContextId右文脈 ID
FieldType.Cost単語コスト
FieldType.Custom形態素素性フィールド

ブラウザでの使用

ES Module インポート

ブラウザ環境では、Lindera の関数を使用する前に WASM モジュールを初期化する必要があります。デフォルトエクスポートの __wbg_init がこの初期化を処理します。

推奨される方法は、辞書を WASM バイナリに埋め込むのではなく、OPFS から読み込むことです:

import __wbg_init, { TokenizerBuilder, loadDictionaryFromBytes } from 'lindera-wasm';
import { downloadDictionary, loadDictionaryFiles, hasDictionary } from 'lindera-wasm/opfs';

async function main() {
    // WASM モジュールを初期化する(いずれかの API を使用する前に一度だけ呼び出す必要がある)
    await __wbg_init();

    // キャッシュされていない場合は辞書をダウンロード
    if (!await hasDictionary("ipadic")) {
        await downloadDictionary(
            "https://github.com/lindera/lindera/releases/download/<version>/lindera-ipadic-<version>.zip",
            "ipadic",
        );
    }

    // OPFS から辞書を読み込み
    const files = await loadDictionaryFiles("ipadic");
    const dictionary = loadDictionaryFromBytes(
        files.metadata, files.dictTrie, files.dictValsIdx, files.dictVals,
        files.dictWordsIdx, files.dictWords, files.matrixMtx, files.charDef,
        files.unk,
    );

    const builder = new TokenizerBuilder();
    builder.setDictionaryInstance(dictionary);
    builder.setMode("normal");
    const tokenizer = builder.build();

    const tokens = tokenizer.tokenize("形態素解析を行います");
    tokens.forEach(token => {
        console.log(`${token.surface}: ${token.details.join(',')}`);
    });
}

main();

埋め込み辞書の使用(上級者向け)

embed-* feature フラグ付きでビルドした場合、OPFS の代わりに埋め込み辞書を使用できます:

[!NOTE] ここでの lindera-wasm-ipadic は説明用のパッケージ名であり、npm に公開されているものではありません。実際に公開されているのは lindera-wasm のみです。このようなパッケージを自分でビルド・命名する方法は npm パッケージの命名規則 を参照してください。

import __wbg_init, { TokenizerBuilder } from 'lindera-wasm-ipadic';

async function main() {
    await __wbg_init();

    const builder = new TokenizerBuilder();
    builder.setDictionary("embedded://ipadic");
    builder.setMode("normal");
    const tokenizer = builder.build();

    const tokens = tokenizer.tokenize("形態素解析を行います");
    tokens.forEach(token => {
        console.log(`${token.surface}: ${token.details.join(',')}`);
    });
}

main();

HTML の例

OPFS 辞書ロードを使用した lindera-wasm の最小限の HTML ページ:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Lindera WASM Demo</title>
</head>
<body>
    <textarea id="input" rows="4" cols="50">関西国際空港限定トートバッグ</textarea>
    <br>
    <button id="tokenize" disabled>Tokenize</button>
    <pre id="output">Loading dictionary...</pre>

    <script type="module">
        import __wbg_init, { TokenizerBuilder, loadDictionaryFromBytes } from './pkg/lindera_wasm.js';
        import { downloadDictionary, loadDictionaryFiles, hasDictionary } from './pkg/opfs.js';

        let tokenizer;

        async function init() {
            await __wbg_init();

            // キャッシュされていない場合は辞書をダウンロード
            if (!await hasDictionary("ipadic")) {
                document.getElementById('output').textContent = 'Downloading dictionary...';
                await downloadDictionary(
                    "https://github.com/lindera/lindera/releases/download/<version>/lindera-ipadic-<version>.zip",
                    "ipadic",
                );
            }

            // OPFS から辞書を読み込み
            const files = await loadDictionaryFiles("ipadic");
            const dictionary = loadDictionaryFromBytes(
                files.metadata, files.dictTrie, files.dictValsIdx, files.dictVals,
                files.dictWordsIdx, files.dictWords, files.matrixMtx, files.charDef,
                files.unk,
            );

            const builder = new TokenizerBuilder();
            builder.setDictionaryInstance(dictionary);
            builder.setMode("normal");
            tokenizer = builder.build();

            document.getElementById('tokenize').disabled = false;
            document.getElementById('output').textContent = 'Ready!';
        }

        document.getElementById('tokenize').addEventListener('click', () => {
            const text = document.getElementById('input').value;
            const tokens = tokenizer.tokenize(text);
            const output = tokens.map(t =>
                `${t.surface}\t${t.details.join(',')}`
            ).join('\n');
            document.getElementById('output').textContent = output;
        });

        init();
    </script>
</body>
</html>

Webpack の設定

Webpack 5 を使用する場合は、asyncWebAssembly experiment を有効にします:

// webpack.config.js
module.exports = {
    experiments: {
        asyncWebAssembly: true,
    },
    module: {
        rules: [
            {
                test: /\.wasm$/,
                type: "webassembly/async",
            },
        ],
    },
};

次に、lindera-wasm パッケージをインポートします。web ターゲットのビルドでは、使用前にデフォルトエクスポートの __wbg_init() を呼び出す必要があります:

import __wbg_init, { TokenizerBuilder, loadDictionaryFromBytes } from 'lindera-wasm';
import { loadDictionaryFiles } from 'lindera-wasm/opfs';

// WASM モジュールを初期化
await __wbg_init();

// OPFS から辞書を読み込み(セットアップは OPFS 辞書ストレージを参照)
const files = await loadDictionaryFiles("ipadic");
const dictionary = loadDictionaryFromBytes(
    files.metadata, files.dictTrie, files.dictValsIdx, files.dictVals,
    files.dictWordsIdx, files.dictWords, files.matrixMtx, files.charDef,
    files.unk,
);

const builder = new TokenizerBuilder();
builder.setDictionaryInstance(dictionary);
builder.setMode("normal");
const tokenizer = builder.build();

Vite / Rollup のセットアップ

Vite は web ターゲットの WASM をそのままサポートしています。npm からインストールした lindera-wasm を直接インポートできます:

import __wbg_init, { TokenizerBuilder, loadDictionaryFromBytes } from 'lindera-wasm';
import { loadDictionaryFiles } from 'lindera-wasm/opfs';

await __wbg_init();
// OPFS から辞書を読み込み、上記のように TokenizerBuilder を使用

Vite では optimizeDeps でこのパッケージを除外してください:

// vite.config.js
import { defineConfig } from 'vite';

export default defineConfig({
    optimizeDeps: {
        exclude: ["lindera-wasm"],
    },
});

ローカルでビルドした場合は、生成された pkg/ ディレクトリをプロジェクトに配置し、./pkg/lindera_wasm.js を直接インポートすることもできます。

Chrome 拡張機能に関する注意事項

Manifest V3 を使用する Chrome 拡張機能では、デフォルトで WebAssembly.compileWebAssembly.instantiate が制限されています。拡張機能で lindera-wasm を使用するには、Content Security Policy に wasm-unsafe-eval を追加する必要があります:

{
    "content_security_policy": {
        "extension_pages": "script-src 'self' 'wasm-unsafe-eval'; object-src 'self'"
    }
}

wasm-unsafe-eval は WebAssembly の実行のみを許可し、任意の JavaScript eval() は許可しません。

パフォーマンスのヒント

  • 初期化は一度だけ: __wbg_init() はアプリケーション起動時に一度だけ呼び出し、トークナイズリクエストごとには呼び出さないでください。
  • トークナイザーの再利用: Tokenizer インスタンスは一度作成し、複数の tokenize() 呼び出しで再利用してください。
  • Web Workers: 大量のトークナイズ処理を行う場合は、メインスレッドのブロックを避けるため、Web Worker での Lindera の実行を検討してください。

OPFS 辞書ストレージ

Lindera WASM は、Web ブラウザでの辞書の永続キャッシュのために OPFS(Origin Private File System)ヘルパーユーティリティを提供します。辞書を一度ダウンロードすれば、WASM バイナリに埋め込むことなく、セッションをまたいで再利用できます。

概要

OPFS ヘルパーは、WASM パッケージとともに別の JavaScript モジュール(opfs.js)として配布されます。ブラウザの Origin Private File System を使用して、辞書のダウンロード、保存、読み込み、管理を行う関数を提供します。

辞書は OPFS パス lindera/dictionaries/<name>/ に保存されます。

インポート

import { downloadDictionary, loadDictionaryFiles, removeDictionary,
         listDictionaries, hasDictionary } from 'lindera-wasm/opfs';

関数

downloadDictionary(url, name, options?)

辞書の zip アーカイブをダウンロードし、展開して、ファイルを OPFS に保存します。

アーカイブは、必要な 9 つの辞書ファイルを含む zip ファイルである必要があります。サブディレクトリにネストされていても構いません。

  • 引数:
    • url (string) -- 辞書 zip アーカイブの URL
    • name (string) -- 辞書の保存名(例: "ipadic"
    • options (object, 省略可):
      • onProgress (function) -- 進捗コールバック
      • fetchInit (RequestInit, 省略可) -- fetch() にそのまま渡す追加オプション(カスタムヘッダー、認証情報、AbortSignal など)
  • 戻り値: Promise<void>
await downloadDictionary(
    "https://example.com/ipadic.zip",
    "ipadic",
    {
        onProgress: (progress) => {
            switch (progress.phase) {
                case "downloading":
                    console.log(`Downloading: ${progress.loaded}/${progress.total} bytes`);
                    break;
                case "extracting":
                    console.log("Extracting archive...");
                    break;
                case "storing":
                    console.log("Storing in OPFS...");
                    break;
                case "complete":
                    console.log("Done!");
                    break;
            }
        },
    },
);

進捗コールバック

onProgress コールバックは以下の形式のオブジェクトを受け取ります:

プロパティ説明
phasestring"downloading""extracting""storing"、または "complete"
loadednumber | undefinedダウンロード済みバイト数("downloading" フェーズのみ)
totalnumber | undefined合計バイト数(判明している場合、"downloading" フェーズのみ)

loadDictionaryFiles(name)

OPFS から辞書ファイルを Uint8Array 値のオブジェクトとして読み込みます。

返されたオブジェクトは loadDictionaryFromBytes() にそのまま渡すことができます。

  • 引数: name (string) -- 辞書名(例: "ipadic"
  • 戻り値: Promise<DictionaryFiles>
const files = await loadDictionaryFiles("ipadic");

DictionaryFiles

プロパティソースファイル
metadataUint8Arraymetadata.json
dictTrieUint8Arraydict.trie(文字単位のダブル配列トライ)
dictValsIdxUint8Arraydict.valsidx(単語値インデックス)
dictValsUint8Arraydict.vals(単語値データ)
dictWordsIdxUint8Arraydict.wordsidx(単語詳細インデックス)
dictWordsUint8Arraydict.words(単語詳細)
matrixMtxUint8Arraymatrix.mtx(連接コスト行列)
charDefUint8Arraychar_def.bin(文字定義)
unkUint8Arrayunk.bin(未知語辞書)

removeDictionary(name)

OPFS から辞書を削除します。

  • 引数: name (string) -- 削除する辞書名
  • 戻り値: Promise<void>
await removeDictionary("ipadic");

listDictionaries()

OPFS に保存されているすべての辞書を一覧表示します。

  • 戻り値: Promise<string[]> -- 辞書名の配列
const names = await listDictionaries();
console.log(names); // 例: ["ipadic", "unidic"]

hasDictionary(name)

OPFS に辞書が存在するかどうかを確認します。

  • 引数: name (string) -- 確認する辞書名
  • 戻り値: Promise<boolean>
if (await hasDictionary("ipadic")) {
    console.log("Dictionary is cached");
}

完全なワークフロー

OPFS ベースの辞書を使用する一般的なワークフロー:

import __wbg_init, { TokenizerBuilder, loadDictionaryFromBytes } from 'lindera-wasm';
import { downloadDictionary, loadDictionaryFiles, hasDictionary } from 'lindera-wasm/opfs';

async function main() {
    await __wbg_init();

    const DICT_NAME = "ipadic";
    const DICT_URL = "https://github.com/lindera/lindera/releases/download/<version>/lindera-ipadic-<version>.zip";

    // キャッシュされていない場合は辞書をダウンロード
    if (!await hasDictionary(DICT_NAME)) {
        await downloadDictionary(DICT_URL, DICT_NAME, {
            onProgress: ({ phase, loaded, total }) => {
                if (phase === "downloading" && total) {
                    console.log(`${(loaded / total * 100).toFixed(1)}%`);
                }
            },
        });
    }

    // OPFS から辞書を読み込み
    const files = await loadDictionaryFiles(DICT_NAME);
    const dictionary = loadDictionaryFromBytes(
        files.metadata, files.dictTrie, files.dictValsIdx, files.dictVals,
        files.dictWordsIdx, files.dictWords, files.matrixMtx, files.charDef,
        files.unk,
    );

    // トークナイザーを構築
    const builder = new TokenizerBuilder();
    builder.setDictionaryInstance(dictionary);
    builder.setMode("normal");
    const tokenizer = builder.build();

    // トークナイズ
    const tokens = tokenizer.tokenize("形態素解析を行います");
    tokens.forEach(token => {
        console.log(`${token.surface}\t${token.details.join(',')}`);
    });
}

main();

必要な辞書ファイル

有効な辞書アーカイブには以下の 9 ファイルが含まれている必要があります:

ファイル説明
metadata.json辞書メタデータ(名前、エンコーディング、スキーマなど)
dict.trie文字単位のダブル配列トライ構造
dict.valsidx単語値インデックス
dict.vals単語値データ
dict.wordsidx単語詳細インデックス
dict.words単語詳細(形態素素性)
matrix.mtx連接コスト行列
char_def.bin文字カテゴリ定義
unk.bin未知語辞書

ブラウザ互換性

OPFS は安全なコンテキスト(HTTPS または localhost)が必要で、以下のブラウザでサポートされています:

  • Chrome 86+
  • Edge 86+
  • Firefox 111+
  • Safari 15.2+

zip の展開には DecompressionStream API を使用しており、以下が必要です:

  • Chrome 80+
  • Edge 80+
  • Firefox 113+
  • Safari 16.4+

Lindera IPADIC

Lindera IPADIC は、IPADIC に基づく日本語辞書クレートです。IPADIC は、日本語の形態素解析で最も広く使われている辞書です。

目次

API リファレンス

Lindera IPADIC

辞書バージョン

このリポジトリには mecab-ipadic が含まれています。

辞書フォーマット

IPADIC の辞書フォーマットおよび品詞タグの詳細については、マニュアルを参照してください。

IndexName (Japanese)Name (English)Notes
0表層形Surface
1左文脈IDLeft context ID
2右文脈IDRight context ID
3コストCost
4品詞Part-of-speech
5品詞細分類1Part-of-speech subcategory 1
6品詞細分類2Part-of-speech subcategory 2
7品詞細分類3Part-of-speech subcategory 3
8活用形Conjugation form
9活用型Conjugation type
10原形Base form
11読みReading
12発音Pronunciation

ユーザー辞書フォーマット (CSV)

簡易版

IndexName (Japanese)Name (English)Notes
0表層形Surface
1品詞Part-of-speech
2読みReading

この簡易スキーマに含まれないフィールド (base_formpronunciation など) には、辞書の default_field_value (IPADIC の場合は *) が設定されます。

詳細版

IndexName (Japanese)Name (English)Notes
0表層形Surface
1左文脈IDLeft context ID
2右文脈IDRight context ID
3コストCost
4品詞Part-of-speech
5品詞細分類1Part-of-speech subcategory 1
6品詞細分類2Part-of-speech subcategory 2
7品詞細分類3Part-of-speech subcategory 3
8活用形Conjugation form
9活用型Conjugation type
10原形Base form
11読みReading
12発音Pronunciation
13--13 以降は自由に拡張可能です。

API リファレンス

API リファレンスは以下の URL から参照できます:

ビルド

このページでは、ソースファイルから IPADIC 辞書をビルドする方法を説明します。

システム辞書のビルド

IPADIC のソースファイルをダウンロードし、辞書をビルドします:

# IPADIC ソースファイルのダウンロードと展開
% curl -L -o /tmp/mecab-ipadic-2.7.0-20250920.tar.gz "https://Lindera.dev/mecab-ipadic-2.7.0-20250920.tar.gz"
% tar zxvf /tmp/mecab-ipadic-2.7.0-20250920.tar.gz -C /tmp

# 辞書のビルド
% lindera build \
  --src /tmp/mecab-ipadic-2.7.0-20250920 \
  --dest /tmp/lindera-ipadic-2.7.0-20250920 \
  --metadata ./lindera-ipadic/metadata.json

ユーザー辞書のビルド

CSV ファイルからユーザー辞書をビルドします:

% lindera build \
  --src ./resources/user_dict/ipadic_simple_userdic.csv \
  --dest ./resources/user_dict \
  --metadata ./lindera-ipadic/metadata.json \
  --user

ユーザー辞書フォーマットの詳細については、辞書フォーマットを参照してください。

バイナリへの埋め込み

IPADIC 辞書をバイナリに直接埋め込むには、以下のようにビルドします:

cargo build --features=embed-ipadic

これにより、外部辞書ファイルなしで embedded://ipadic を辞書パスとして使用できるようになります。

使用例

このページでは、IPADIC 辞書を使用したトークナイズの例を示します。

外部 IPADIC でトークナイズ

% echo "日本語の形態素解析を行うことができます。" | lindera tokenize \
  --dict /tmp/lindera-ipadic-2.7.0-20250920
日本語  名詞,一般,*,*,*,*,日本語,ニホンゴ,ニホンゴ
の      助詞,連体化,*,*,*,*,の,ノ,ノ
形態素  名詞,一般,*,*,*,*,形態素,ケイタイソ,ケイタイソ
解析    名詞,サ変接続,*,*,*,*,解析,カイセキ,カイセキ
を      助詞,格助詞,一般,*,*,*,を,ヲ,ヲ
行う    動詞,自立,*,*,五段・ワ行促音便,基本形,行う,オコナウ,オコナウ
こと    名詞,非自立,一般,*,*,*,こと,コト,コト
が      助詞,格助詞,一般,*,*,*,が,ガ,ガ
でき    動詞,自立,*,*,一段,連用形,できる,デキ,デキ
ます    助動詞,*,*,*,特殊・マス,基本形,ます,マス,マス
。      記号,句点,*,*,*,*,。,。,。
EOS

埋め込み IPADIC でトークナイズ

% echo "日本語の形態素解析を行うことができます。" | lindera tokenize \
  --dict embedded://ipadic
日本語  名詞,一般,*,*,*,*,日本語,ニホンゴ,ニホンゴ
の      助詞,連体化,*,*,*,*,の,ノ,ノ
形態素  名詞,一般,*,*,*,*,形態素,ケイタイソ,ケイタイソ
解析    名詞,サ変接続,*,*,*,*,解析,カイセキ,カイセキ
を      助詞,格助詞,一般,*,*,*,を,ヲ,ヲ
行う    動詞,自立,*,*,五段・ワ行促音便,基本形,行う,オコナウ,オコナウ
こと    名詞,非自立,一般,*,*,*,こと,コト,コト
が      助詞,格助詞,一般,*,*,*,が,ガ,ガ
でき    動詞,自立,*,*,一段,連用形,できる,デキ,デキ
ます    助動詞,*,*,*,特殊・マス,基本形,ます,マス,マス
。      記号,句点,*,*,*,*,。,。,。
EOS

注意: IPADIC 辞書をバイナリに含めるには、--features=embed-ipadic オプションを付けてビルドする必要があります。

ユーザー辞書を使用したトークナイズ (CSV フォーマット)

% echo "東京スカイツリーの最寄り駅はとうきょうスカイツリー駅です" | lindera tokenize \
  --dict embedded://ipadic \
  --user-dict ./resources/user_dict/ipadic_simple_userdic.csv
東京スカイツリー        カスタム名詞,*,*,*,*,*,*,トウキョウスカイツリー,*
の      助詞,連体化,*,*,*,*,の,ノ,ノ
最寄り駅        名詞,一般,*,*,*,*,最寄り駅,モヨリエキ,モヨリエキ
は      助詞,係助詞,*,*,*,*,は,ハ,ワ
とうきょうスカイツリー駅        カスタム名詞,*,*,*,*,*,*,トウキョウスカイツリーエキ,*
です    助動詞,*,*,*,特殊・デス,基本形,です,デス,デス
EOS

ユーザー辞書を使用したトークナイズ (バイナリフォーマット)

% echo "東京スカイツリーの最寄り駅はとうきょうスカイツリー駅です" | lindera tokenize \
  --dict /tmp/lindera-ipadic-2.7.0-20250920 \
  --user-dict ./resources/user_dict/ipadic_simple_userdic.bin
東京スカイツリー        カスタム名詞,*,*,*,*,*,*,トウキョウスカイツリー,*
の      助詞,連体化,*,*,*,*,の,ノ,ノ
最寄り駅        名詞,一般,*,*,*,*,最寄り駅,モヨリエキ,モヨリエキ
は      助詞,係助詞,*,*,*,*,は,ハ,ワ
とうきょうスカイツリー駅        カスタム名詞,*,*,*,*,*,*,トウキョウスカイツリーエキ,*
です    助動詞,*,*,*,特殊・デス,基本形,です,デス,デス
EOS

Rust API の使用例

use lindera::dictionary::load_dictionary;
use lindera::mode::Mode;
use lindera::segmenter::Segmenter;
use lindera_analysis::tokenizer::Tokenizer;
use lindera::LinderaResult;

fn main() -> LinderaResult<()> {
    let dictionary = load_dictionary("embedded://ipadic")?;
    let segmenter = Segmenter::new(Mode::Normal, dictionary, None);
    let tokenizer = Tokenizer::new(segmenter);

    let text = "日本語の形態素解析を行うことができます。";
    let mut tokens = tokenizer.tokenize(text)?;
    for token in tokens.iter_mut() {
        let details = token.details().join(",");
        println!("{}\t{}", token.surface.as_ref(), details);
    }
    Ok(())
}

Lindera IPADIC NEologd

Lindera IPADIC NEologd は、新語を含む IPADIC NEologd に基づく日本語辞書クレートです。標準の IPADIC 辞書を拡張し、最新の用語や固有名詞を追加した語彙を提供します。

目次

API リファレンス

Lindera IPADIC NEologd

辞書バージョン

このリポジトリには mecab-ipadic-neologd が含まれています。

辞書フォーマット

IPADIC の辞書フォーマットおよび品詞タグの詳細については、マニュアルを参照してください。

IndexName (Japanese)Name (English)Notes
0表層形Surface
1左文脈IDLeft context ID
2右文脈IDRight context ID
3コストCost
4品詞Part-of-speech
5品詞細分類1Part-of-speech subcategory 1
6品詞細分類2Part-of-speech subcategory 2
7品詞細分類3Part-of-speech subcategory 3
8活用形Conjugation form
9活用型Conjugation type
10原形Base form
11読みReading
12発音Pronunciation

ユーザー辞書フォーマット (CSV)

簡易版

IndexName (Japanese)Name (English)Notes
0表層形Surface
1品詞Part-of-speech
2読みReading

詳細版

IndexName (Japanese)Name (English)Notes
0表層形Surface
1左文脈IDLeft context ID
2右文脈IDRight context ID
3コストCost
4品詞Part-of-speech
5品詞細分類1Part-of-speech subcategory 1
6品詞細分類2Part-of-speech subcategory 2
7品詞細分類3Part-of-speech subcategory 3
8活用形Conjugation form
9活用型Conjugation type
10原形Base form
11読みReading
12発音Pronunciation
13--13 以降は自由に拡張可能です。

API リファレンス

API リファレンスは以下の URL から参照できます:

ビルド

このページでは、ソースファイルから IPADIC NEologd 辞書をビルドする方法を説明します。

システム辞書のビルド

IPADIC NEologd のソースファイルをダウンロードし、辞書をビルドします:

% curl -L -o /tmp/mecab-ipadic-neologd-0.0.7-20200820.tar.gz "https://lindera.dev/mecab-ipadic-neologd-0.0.7-20200820.tar.gz"
% tar zxvf /tmp/mecab-ipadic-neologd-0.0.7-20200820.tar.gz -C /tmp

% lindera build \
  --src /tmp/mecab-ipadic-neologd-0.0.7-20200820 \
  --dest /tmp/lindera-ipadic-neologd-0.0.7-20200820 \
  --metadata ./lindera-ipadic-neologd/metadata.json

ユーザー辞書のビルド

CSV ファイルからユーザー辞書をビルドします:

% lindera build \
  --src ./resources/user_dict/ipadic_simple_userdic.csv \
  --dest ./resources/user_dict \
  --metadata ./lindera-ipadic-neologd/metadata.json \
  --user

ユーザー辞書フォーマットの詳細については、辞書フォーマットを参照してください。

バイナリへの埋め込み

IPADIC NEologd 辞書をバイナリに直接埋め込むには、以下のようにビルドします:

cargo build --features=embed-ipadic-neologd

これにより、外部辞書ファイルなしで embedded://ipadic-neologd を辞書パスとして使用できるようになります。

使用例

このページでは、IPADIC NEologd 辞書を使用したトークナイズの例を示します。

外部 IPADIC NEologd でトークナイズ

% echo "日本語の形態素解析を行うことができます。" | lindera tokenize \
  --dict /tmp/lindera-ipadic-neologd-0.0.7-20200820
日本語  名詞,一般,*,*,*,*,日本語,ニホンゴ,ニホンゴ
の      助詞,連体化,*,*,*,*,の,ノ,ノ
形態素解析      名詞,固有名詞,一般,*,*,*,形態素解析,ケイタイソカイセキ,ケイタイソカイセキ
を      助詞,格助詞,一般,*,*,*,を,ヲ,ヲ
行う    動詞,自立,*,*,五段・ワ行促音便,基本形,行う,オコナウ,オコナウ
こと    名詞,非自立,一般,*,*,*,こと,コト,コト
が      助詞,格助詞,一般,*,*,*,が,ガ,ガ
でき    動詞,自立,*,*,一段,連用形,できる,デキ,デキ
ます    助動詞,*,*,*,特殊・マス,基本形,ます,マス,マス
。      記号,句点,*,*,*,*,。,。,。
EOS

NEologd では「形態素解析」が単一の複合名詞として扱われますが、標準の IPADIC では「形態素」と「解析」に分割されます。

埋め込み IPADIC NEologd でトークナイズ

% echo "日本語の形態素解析を行うことができます。" | lindera tokenize \
  --dict embedded://ipadic-neologd
日本語  名詞,一般,*,*,*,*,日本語,ニホンゴ,ニホンゴ
の      助詞,連体化,*,*,*,*,の,ノ,ノ
形態素解析      名詞,固有名詞,一般,*,*,*,形態素解析,ケイタイソカイセキ,ケイタイソカイセキ
を      助詞,格助詞,一般,*,*,*,を,ヲ,ヲ
行う    動詞,自立,*,*,五段・ワ行促音便,基本形,行う,オコナウ,オコナウ
こと    名詞,非自立,一般,*,*,*,こと,コト,コト
が      助詞,格助詞,一般,*,*,*,が,ガ,ガ
でき    動詞,自立,*,*,一段,連用形,できる,デキ,デキ
ます    助動詞,*,*,*,特殊・マス,基本形,ます,マス,マス
。      記号,句点,*,*,*,*,。,。,。
EOS

注意: IPADIC NEologd 辞書をバイナリに含めるには、--features=embed-ipadic-neologd オプションを付けてビルドする必要があります。

ユーザー辞書を使用したトークナイズ (CSV フォーマット)

% echo "東京スカイツリーの最寄り駅はとうきょうスカイツリー駅です" | lindera tokenize \
  --dict embedded://ipadic-neologd \
  --user-dict ./resources/user_dict/ipadic_simple_userdic.csv
東京スカイツリー        カスタム名詞,*,*,*,*,*,*,トウキョウスカイツリー,*
の      助詞,連体化,*,*,*,*,の,ノ,ノ
最寄り駅        名詞,一般,*,*,*,*,最寄り駅,モヨリエキ,モヨリエキ
は      助詞,係助詞,*,*,*,*,は,ハ,ワ
とうきょうスカイツリー駅        カスタム名詞,*,*,*,*,*,*,トウキョウスカイツリーエキ,*
です    助動詞,*,*,*,特殊・デス,基本形,です,デス,デス
EOS

ユーザー辞書を使用したトークナイズ (バイナリフォーマット)

% echo "東京スカイツリーの最寄り駅はとうきょうスカイツリー駅です" | lindera tokenize \
  --dict /tmp/lindera-ipadic-neologd-0.0.7-20200820 \
  --user-dict ./resources/user_dict/ipadic_simple_userdic.bin
東京スカイツリー        カスタム名詞,*,*,*,*,*,*,トウキョウスカイツリー,*
の      助詞,連体化,*,*,*,*,の,ノ,ノ
最寄り駅        名詞,一般,*,*,*,*,最寄り駅,モヨリエキ,モヨリエキ
は      助詞,係助詞,*,*,*,*,は,ハ,ワ
とうきょうスカイツリー駅        カスタム名詞,*,*,*,*,*,*,トウキョウスカイツリーエキ,*
です    助動詞,*,*,*,特殊・デス,基本形,です,デス,デス
EOS

Rust API の使用例

use lindera::dictionary::load_dictionary;
use lindera::mode::Mode;
use lindera::segmenter::Segmenter;
use lindera_analysis::tokenizer::Tokenizer;
use lindera::LinderaResult;

fn main() -> LinderaResult<()> {
    let dictionary = load_dictionary("embedded://ipadic-neologd")?;
    let segmenter = Segmenter::new(Mode::Normal, dictionary, None);
    let tokenizer = Tokenizer::new(segmenter);

    let text = "日本語の形態素解析を行うことができます。";
    let mut tokens = tokenizer.tokenize(text)?;
    for token in tokens.iter_mut() {
        let details = token.details().join(",");
        println!("{}\t{}", token.surface.as_ref(), details);
    }
    Ok(())
}

Lindera UniDic

Lindera UniDic は、統一的な単語単位の定義を使用する UniDic に基づく日本語辞書クレートです。UniDic は IPADIC よりも詳細な形態素情報を提供し、1エントリあたり 21 フィールドを持ちます。

目次

API リファレンス

Lindera UniDic

辞書バージョン

このリポジトリには unidic-mecab が含まれています。

辞書フォーマット

unidic-mecab の辞書フォーマットおよび品詞タグの詳細については、マニュアルを参照してください。

IndexName (Japanese)Name (English)Notes
0表層形Surface
1左文脈IDLeft context ID
2右文脈IDRight context ID
3コストCost
4品詞大分類Part-of-speech
5品詞中分類Part-of-speech subcategory 1
6品詞小分類Part-of-speech subcategory 2
7品詞細分類Part-of-speech subcategory 3
8活用型Conjugation type
9活用形Conjugation form
10語彙素読みReading
11語彙素(語彙素表記 + 語彙素細分類)Lexeme
12書字形出現形Orthographic surface form
13発音形出現形Phonological surface form
14書字形基本形Orthographic base form
15発音形基本形Phonological base form
16語種Word type
17語頭変化型Initial mutation type
18語頭変化形Initial mutation form
19語末変化型Final mutation type
20語末変化形Final mutation form

ユーザー辞書フォーマット (CSV)

簡易版

IndexName (Japanese)Name (English)Notes
0表層形Surface
1品詞大分類Part-of-speech
2語彙素読みReading

詳細版

IndexName (Japanese)Name (English)Notes
0表層形Surface
1左文脈IDLeft context ID
2右文脈IDRight context ID
3コストCost
4品詞大分類Part-of-speech
5品詞中分類Part-of-speech subcategory 1
6品詞小分類Part-of-speech subcategory 2
7品詞細分類Part-of-speech subcategory 3
8活用型Conjugation type
9活用形Conjugation form
10語彙素読みReading
11語彙素(語彙素表記 + 語彙素細分類)Lexeme
12書字形出現形Orthographic surface form
13発音形出現形Phonological surface form
14書字形基本形Orthographic base form
15発音形基本形Phonological base form
16語種Word type
17語頭変化型Initial mutation type
18語頭変化形Initial mutation form
19語末変化型Final mutation type
20語末変化形Final mutation form
21--21 以降は自由に拡張可能です。

API リファレンス

API リファレンスは以下の URL から参照できます:

ビルド

このページでは、ソースファイルから UniDic 辞書をビルドする方法を説明します。

システム辞書のビルド

UniDic のソースファイルをダウンロードし、辞書をビルドします:

% curl -L -o /tmp/unidic-mecab-2.1.2.tar.gz "https://Lindera.dev/unidic-mecab-2.1.2.tar.gz"
% tar zxvf /tmp/unidic-mecab-2.1.2.tar.gz -C /tmp

% lindera build \
  --src /tmp/unidic-mecab-2.1.2 \
  --dest /tmp/lindera-unidic-2.1.2 \
  --metadata ./lindera-unidic/metadata.json \
  --context-id-freq ./lindera-unidic/context_id_freq.txt

[!TIP] lindera-unidic/metadata.jsonconnection_id_mapping: true を設定しているため、ビルダーは 連接コスト行列の文脈 ID を使用頻度順に付け替え、連接コスト参照時のキャッシュ局所性を改善します。 --context-id-freq / -f に同梱の context_id_freq.txt ヒストグラムを渡すことで、この付け替えに 実際のコーパス頻度データを与えて ID をランク付けできます。このフラグを省略してもビルドは失敗せず、 精度の低いエントリ数ベースのフォールバックに黙って切り替わるだけです。いずれの場合もトークン化の 結果には影響しません -- この付け替えはコストを保つ全単射な再ラベル付けであり、ビルド時の最適化のみに 関わるものであって正確性には影響しません。

ユーザー辞書のビルド

CSV ファイルからユーザー辞書をビルドします:

% lindera build \
  --src ./resources/user_dict/unidic_simple_userdic.csv \
  --dest ./resources/user_dict \
  --metadata ./lindera-unidic/metadata.json \
  --user

ユーザー辞書フォーマットの詳細については、辞書フォーマットを参照してください。

バイナリへの埋め込み

UniDic 辞書をバイナリに直接埋め込むには、以下のようにビルドします:

cargo build --features=embed-unidic

これにより、外部辞書ファイルなしで embedded://unidic を辞書パスとして使用できるようになります。

使用例

このページでは、UniDic 辞書を使用したトークナイズの例を示します。

外部 UniDic でトークナイズ

% echo "日本語の形態素解析を行うことができます。" | lindera tokenize \
  --dict /tmp/lindera-unidic-2.1.2
日本    名詞,固有名詞,地名,国,*,*,ニッポン,日本,日本,ニッポン,日本,ニッポン,固,*,*,*,*
語      名詞,普通名詞,一般,*,*,*,ゴ,語,語,ゴ,語,ゴ,漢,*,*,*,*
の      助詞,格助詞,*,*,*,*,ノ,の,の,ノ,の,ノ,和,*,*,*,*
形態    名詞,普通名詞,一般,*,*,*,ケイタイ,形態,形態,ケータイ,形態,ケータイ,漢,*,*,*,*
素      接尾辞,名詞的,一般,*,*,*,ソ,素,素,ソ,素,ソ,漢,*,*,*,*
解析    名詞,普通名詞,サ変可能,*,*,*,カイセキ,解析,解析,カイセキ,解析,カイセキ,漢,*,*,*,*
を      助詞,格助詞,*,*,*,*,ヲ,を,を,オ,を,オ,和,*,*,*,*
行う    動詞,一般,*,*,五段-ワア行,連体形-一般,オコナウ,行う,行う,オコナウ,行う,オコナウ,和,*,*,*,*
こと    名詞,普通名詞,一般,*,*,*,コト,事,こと,コト,こと,コト,和,コ濁,基本形,*,*
が      助詞,格助詞,*,*,*,*,ガ,が,が,ガ,が,ガ,和,*,*,*,*
でき    動詞,非自立可能,*,*,上一段-カ行,連用形-一般,デキル,出来る,でき,デキ,できる,デキル,和,*,*,*,*
ます    助動詞,*,*,*,助動詞-マス,終止形-一般,マス,ます,ます,マス,ます,マス,和,*,*,*,*
。      補助記号,句点,*,*,*,*,,。,。,,。,,記号,*,*,*,*
EOS

UniDic では「日本語」が「日本」と「語」に、「形態素」が「形態」と「素」に分割されます。これは UniDic の統一的な単語単位の定義を反映しています。

埋め込み UniDic でトークナイズ

% echo "日本語の形態素解析を行うことができます。" | lindera tokenize \
  --dict embedded://unidic
日本    名詞,固有名詞,地名,国,*,*,ニッポン,日本,日本,ニッポン,日本,ニッポン,固,*,*,*,*
語      名詞,普通名詞,一般,*,*,*,ゴ,語,語,ゴ,語,ゴ,漢,*,*,*,*
の      助詞,格助詞,*,*,*,*,ノ,の,の,ノ,の,ノ,和,*,*,*,*
形態    名詞,普通名詞,一般,*,*,*,ケイタイ,形態,形態,ケータイ,形態,ケータイ,漢,*,*,*,*
素      接尾辞,名詞的,一般,*,*,*,ソ,素,素,ソ,素,ソ,漢,*,*,*,*
解析    名詞,普通名詞,サ変可能,*,*,*,カイセキ,解析,解析,カイセキ,解析,カイセキ,漢,*,*,*,*
を      助詞,格助詞,*,*,*,*,ヲ,を,を,オ,を,オ,和,*,*,*,*
行う    動詞,一般,*,*,五段-ワア行,連体形-一般,オコナウ,行う,行う,オコナウ,行う,オコナウ,和,*,*,*,*
こと    名詞,普通名詞,一般,*,*,*,コト,事,こと,コト,こと,コト,和,コ濁,基本形,*,*
が      助詞,格助詞,*,*,*,*,ガ,が,が,ガ,が,ガ,和,*,*,*,*
でき    動詞,非自立可能,*,*,上一段-カ行,連用形-一般,デキル,出来る,でき,デキ,できる,デキル,和,*,*,*,*
ます    助動詞,*,*,*,助動詞-マス,終止形-一般,マス,ます,ます,マス,ます,マス,和,*,*,*,*
。      補助記号,句点,*,*,*,*,,。,。,,。,,記号,*,*,*,*
EOS

注意: UniDic 辞書をバイナリに含めるには、--features=embed-unidic オプションを付けてビルドする必要があります。

Rust API の使用例

use lindera::dictionary::load_dictionary;
use lindera::mode::Mode;
use lindera::segmenter::Segmenter;
use lindera_analysis::tokenizer::Tokenizer;
use lindera::LinderaResult;

fn main() -> LinderaResult<()> {
    let dictionary = load_dictionary("embedded://unidic")?;
    let segmenter = Segmenter::new(Mode::Normal, dictionary, None);
    let tokenizer = Tokenizer::new(segmenter);

    let text = "日本語の形態素解析を行うことができます。";
    let mut tokens = tokenizer.tokenize(text)?;
    for token in tokens.iter_mut() {
        let details = token.details().join(",");
        println!("{}\t{}", token.surface.as_ref(), details);
    }
    Ok(())
}

Lindera SudachiDict

Lindera SudachiDict は、Sudachi 形態素解析器が使用する、活発にメンテナンスされている辞書 SudachiDict に基づく日本語辞書クレートです。上流で年に数回更新されているため、最近の語彙(令和スマホ推し活、...)が語彙に含まれます。各エントリは 19 フィールドを持ち、正規化表記・分割情報・同義語グループ ID といった SudachiDict 固有のカラムを含みます。

辞書のソースファイルは lindera/sudachidict にミラーされています。同梱バージョンは 20260723(small + core + notcore レキシコン)です。

[!NOTE] この辞書は SudachiDict の語彙とラティスの挙動を再現しますが、Sudachi のエンジンプラグイン(カタカナ・数詞の連結、入力正規化、A/B/C 分割モード)は再現しません。詳細は Sudachi との挙動の違いを参照してください。

目次

API リファレンス

Lindera SudachiDict

辞書バージョン

このリポジトリには SudachiDict 20260723(small + core + notcore レキシコン)が含まれています。

辞書フォーマット

辞書フォーマットおよび品詞タグの詳細については、SudachiDict のドキュメントを参照してください。

IndexName (Japanese)Name (English)Notes
0表層形Surface
1左文脈IDLeft context ID
2右文脈IDRight context ID
3コストCost
4見出し(解析結果表示用)Display surface
5品詞大分類Part-of-speech
6品詞中分類Part-of-speech subcategory 1
7品詞小分類Part-of-speech subcategory 2
8品詞細分類Part-of-speech subcategory 3
9活用型Conjugation type
10活用形Conjugation form
11読みReading
12正規化表記Normalized form
13辞書形IDDictionary form word ID
14分割タイプSplit modeA/B/C
15A単位分割情報Split references (A)
16B単位分割情報Split references (B)
17語構成Word structure
18同義語グループIDSynonym group IDs

[!NOTE] 見出し(解析結果表示用)が品詞カラムより前のインデックス 4 に位置するため、 トークン詳細(details)の先頭は、IPADIC や UniDic のような品詞ではなく見出しに なります。先頭の詳細を品詞タグとして位置ベースで読み取るトークンフィルタ (japanese_stop_tagsjapanese_keep_tagsjapanese_compound_word)は、 この辞書では期待どおりにマッチしません。token.get("part_of_speech") のような スキーマを認識するアクセスは正しく動作します。詳細は lindera/lindera#997 を参照してください。

ユーザー辞書フォーマット (CSV)

簡易版

IndexName (Japanese)Name (English)Notes
0表層形Surface
1品詞大分類Part-of-speech
2読みReading

詳細版

詳細版は上記の辞書フォーマット(19 カラム)に従います。

IndexName (Japanese)Name (English)Notes
0-18-Same as the dictionary format
19--19 以降は自由に拡張可能です。

API リファレンス

API リファレンスは以下の URL から参照できます:

ビルド

このページでは、ソースファイルから SudachiDict 辞書をビルドする方法を説明します。

システム辞書のビルド

SudachiDict のソースアーカイブをダウンロードし、辞書をビルドします:

% curl -L -o /tmp/sudachidict-20260723.tar.gz "https://lindera.dev/sudachidict-20260723.tar.gz"
% tar zxvf /tmp/sudachidict-20260723.tar.gz -C /tmp

% lindera build \
  --src /tmp/sudachidict-20260723 \
  --dest /tmp/lindera-sudachidict \
  --metadata ./lindera-sudachidict/metadata.json

アーカイブには raw レキシコン(small + core + notcore)、連接コスト行列、および位置合わせ済みの char.def / unk.def が同梱されているため、手動の前処理は不要です。ビルドされた辞書のサイズは約 570MB です。

[!TIP] 同梱バージョンより新しい上流の SudachiDict リリースからビルドするには、 上流の raw 配布物から直接ビルドする SudachiDict(カスタムビルド)を参照してください。

ユーザー辞書のビルド

CSV ファイルからユーザー辞書をビルドします:

% lindera build \
  --src ./resources/user_dict/sudachidict_simple_userdic.csv \
  --dest ./resources/user_dict \
  --metadata ./lindera-sudachidict/metadata.json \
  --user

ユーザー辞書フォーマットの詳細については、辞書フォーマットを参照してください。

バイナリへの埋め込み

SudachiDict 辞書をバイナリに直接埋め込むには、以下のようにビルドします:

cargo build --features=embed-sudachidict

これにより、外部辞書ファイルなしで embedded://sudachidict を辞書パスとして使用できるようになります。

[!NOTE] 埋め込み辞書はバイナリサイズを約 570MB 増加させます。バイナリサイズが重要な場合は、 上記のように辞書をディレクトリにビルドし、パス指定で読み込むことを推奨します。

使用例

このページでは、SudachiDict 辞書を使用したトークナイズの例を示します。

外部 SudachiDict でトークナイズ

% echo "令和五年に始まった。推し活が楽しい。" | lindera tokenize \
  --dict /tmp/lindera-sudachidict
令和	令和,名詞,固有名詞,一般,*,*,*,レイワ,令和,*,A,*,*,*,*
五	五,名詞,数詞,*,*,*,*,ゴ,五,*,A,*,*,*,017040
年	年,名詞,普通名詞,助数詞可能,*,*,*,ネン,年,*,A,*,*,*,*
に	に,助詞,格助詞,*,*,*,*,ニ,に,*,A,*,*,*,*
始まっ	始まっ,動詞,一般,*,*,五段-ラ行,連用形-促音便,ハジマッ,始まる,380965,A,*,*,*,000378
た	た,助動詞,*,*,*,助動詞-タ,終止形-一般,タ,た,83558,A,*,*,*,*
。	。,補助記号,句点,*,*,*,*,。,。,*,A,*,*,*,*
推し活	推し活,名詞,普通名詞,一般,*,*,*,オシカツ,推し活,*,A,*,*,*,*
が	が,助詞,格助詞,*,*,*,*,ガ,が,*,A,*,*,*,*
楽しい	楽しい,形容詞,一般,*,*,形容詞,終止形-一般,タノシイ,楽しい,519727,A,*,*,*,*
。	。,補助記号,句点,*,*,*,*,。,。,*,A,*,*,*,*
EOS

令和 が単一の固有名詞になり(IPADIC と UniDic 2.1.2 は に分割します)、推し活 が語彙に含まれている点に注目してください。詳細(details)の先頭カラムは見出し(解析結果表示用)で、その後に品詞カラムと SudachiDict 固有のカラム(正規化表記、分割タイプ、同義語グループID)が続きます。

埋め込み SudachiDict でトークナイズ

% echo "令和五年に始まった。推し活が楽しい。" | lindera tokenize \
  --dict embedded://sudachidict

出力は上記と同じです。

注意: SudachiDict 辞書をバイナリに含めるには、--features=embed-sudachidict オプションを付けてビルドする必要があります。

Rust API の使用例

use lindera::dictionary::load_dictionary;
use lindera::mode::Mode;
use lindera::segmenter::Segmenter;
use lindera_analysis::tokenizer::Tokenizer;
use lindera::LinderaResult;

fn main() -> LinderaResult<()> {
    let dictionary = load_dictionary("embedded://sudachidict")?;
    let segmenter = Segmenter::new(Mode::Normal, dictionary, None);
    let tokenizer = Tokenizer::new(segmenter);

    let text = "令和五年に始まった。推し活が楽しい。";
    let mut tokens = tokenizer.tokenize(text)?;
    for token in tokens.iter_mut() {
        // Schema-aware access: works regardless of column positions
        let pos = token.get("part_of_speech").unwrap_or_default().to_string();
        let details = token.details().join(",");
        println!("{}\t{}\t{}", token.surface.as_ref(), pos, details);
    }
    Ok(())
}

Lindera ko-dic

Lindera ko-dic は、mecab-ko-dic に基づく韓国語辞書クレートです。

目次

API リファレンス

Lindera ko-dic

辞書バージョン

このリポジトリには mecab-ko-dic が含まれています。

辞書フォーマット

mecab-ko-dic で使用される辞書フォーマットおよび品詞タグの情報は、mecab-ko-dic のリポジトリ README からリンクされているこの Google スプレッドシートに記載されています。

ko-dic は NAIST JDIC よりもフィールド列が 1 つ少なく、全体的に異なる情報セットを持っています(例: 単語の「原形」を提供しません)。

タグは世宗(Sejong)で規定されたものを若干修正したものです。世宗から mecab-ko-dic のタグ名へのマッピングは、上記スプレッドシートの 태그 v2.0 タブに記載されています。

辞書フォーマットの完全な仕様は(韓国語で)スプレッドシートの 사전 형식 v2.0 タブに記載されています。空の値はデフォルトで * になります。

IndexName (Korean)Name (English)Notes
0표면Surface
1왼쪽 문맥 IDLeft context ID
2오른쪽 문맥 IDRight context ID
3비용Cost
4품사 태그Part-of-speech tagスプレッドシートの 태그 v2.0 タブを参照
5의미 부류Meaning(確信するには例が少なすぎます)
6종성 유무Presence or absenceT は true、F は false、それ以外は *
7읽기Reading通常は表層形と一致しますが、外来語(例: 漢字語)では異なる場合があります
8타입TypeInflect(活用)、Compound(複合名詞)、Preanalysis(基分析)のいずれか
9첫번째 품사First part-of-speech例: 品詞タグが "VV+EM+VX+EP" の場合、VV を返します
10마지막 품사Last part-of-speech例: 品詞タグが "VV+EM+VX+EP" の場合、EP を返します
11표현Expression활용, 복합명사, 기분석이 어떻게 구성되는지 알려주는 필드 -- 活用、複合名詞、基分析がどのように構成されるかを示すフィールド

ユーザー辞書フォーマット (CSV)

簡易版

IndexName (Korean)Name (English)Notes
0표면Surface
1품사 태그part-of-speech tagスプレッドシートの 태그 v2.0 タブを参照
2읽기reading通常は表層形と一致しますが、外来語(例: 漢字語)では異なる場合があります

詳細版

IndexName (Korean)Name (English)Notes
0표면Surface
1왼쪽 문맥 IDLeft context ID
2오른쪽 문맥 IDRight context ID
3비용Cost
4품사 태그part-of-speech tagスプレッドシートの 태그 v2.0 タブを参照
5의미 부류meaning(確信するには例が少なすぎます)
6종성 유무presence or absenceT は true、F は false、それ以外は *
7읽기reading通常は表層形と一致しますが、外来語(例: 漢字語)では異なる場合があります
8타입typeInflect(活用)、Compound(複合名詞)、Preanalysis(基分析)のいずれか
9첫번째 품사first part-of-speech例: 品詞タグが "VV+EM+VX+EP" の場合、VV を返します
10마지막 품사last part-of-speech例: 品詞タグが "VV+EM+VX+EP" の場合、EP を返します
11표현expression활용, 복합명사, 기분석이 어떻게 구성되는지 알려주는 필드 -- 活用、複合名詞、基分析がどのように構成されるかを示すフィールド
12--12 以降は自由に拡張可能です。

API リファレンス

API リファレンスは以下の URL から参照できます:

ビルド

システム辞書のビルド

mecab-ko-dic のソースファイルをダウンロード・展開し、辞書をビルドします:

% curl -L -o /tmp/mecab-ko-dic-2.1.1-20180720.tar.gz "https://Lindera.dev/mecab-ko-dic-2.1.1-20180720.tar.gz"
% tar zxvf /tmp/mecab-ko-dic-2.1.1-20180720.tar.gz -C /tmp
% lindera build \
  --src /tmp/mecab-ko-dic-2.1.1-20180720 \
  --dest /tmp/lindera-ko-dic-2.1.1-20180720 \
  --metadata ./lindera-ko-dic/metadata.json \
  --context-id-freq ./lindera-ko-dic/context_id_freq.txt

[!TIP] lindera-ko-dic/metadata.jsonconnection_id_mapping: true を設定しているため、ビルダーは 連接コスト行列の文脈 ID を使用頻度順に付け替え、連接コスト参照時のキャッシュ局所性を改善します。 --context-id-freq / -f に同梱の context_id_freq.txt ヒストグラムを渡すことで、この付け替えに 実際のコーパス頻度データを与えて ID をランク付けできます。このフラグを省略してもビルドは失敗せず、 精度の低いエントリ数ベースのフォールバックに黙って切り替わるだけです。いずれの場合もトークン化の 結果には影響しません -- この付け替えはコストを保つ全単射な再ラベル付けであり、ビルド時の最適化のみに 関わるものであって正確性には影響しません。

ユーザー辞書のビルド

% lindera build \
  --src ./resources/user_dict/ko-dic_simple_userdic.csv \
  --dest ./resources/user_dict \
  --metadata ./lindera-ko-dic/metadata.json \
  --user

辞書の埋め込み

ko-dic 辞書をバイナリに直接埋め込むには、以下の feature フラグを付けてビルドします:

% cargo build --features=embed-ko-dic

使用例

外部 ko-dic でトークナイズ

% echo "한국어의형태해석을실시할수있습니다." | lindera tokenize \
  --dict /tmp/lindera-ko-dic-2.1.1-20180720
한국어  NNG,*,F,한국어,Compound,*,*,한국/NNG/*+어/NNG/*
의      JKG,*,F,의,*,*,*,*
형태    NNG,*,F,형태,*,*,*,*
해석    NNG,행위,T,해석,*,*,*,*
을      JKO,*,T,을,*,*,*,*
실시    NNG,행위,F,실시,*,*,*,*
할      XSV+ETM,*,T,할,Inflect,XSV,ETM,하/XSV/*+ᆯ/ETM/*
수      NNB,*,F,수,*,*,*,*
있      VV,*,T,있,*,*,*,*
습니다  EF,*,F,습니다,*,*,*,*
.       SF,*,*,*,*,*,*,*
EOS

埋め込み ko-dic でトークナイズ

% echo "한국어의형태해석을실시할수있습니다." | lindera tokenize \
  --dict embedded://ko-dic
한국어  NNG,*,F,한국어,Compound,*,*,한국/NNG/*+어/NNG/*
의      JKG,*,F,의,*,*,*,*
형태    NNG,*,F,형태,*,*,*,*
해석    NNG,행위,T,해석,*,*,*,*
을      JKO,*,T,을,*,*,*,*
실시    NNG,행위,F,실시,*,*,*,*
할      XSV+ETM,*,T,할,Inflect,XSV,ETM,하/XSV/*+ᆯ/ETM/*
수      NNB,*,F,수,*,*,*,*
있      VV,*,T,있,*,*,*,*
습니다  EF,*,F,습니다,*,*,*,*
.       SF,*,*,*,*,*,*,*
EOS

注意: ko-dic 辞書をバイナリに含めるには、--features=embed-ko-dic オプションを付けてビルドする必要があります。

Rust API の使用例

use lindera::dictionary::load_dictionary;
use lindera::mode::Mode;
use lindera::segmenter::Segmenter;
use lindera_analysis::tokenizer::Tokenizer;
use lindera::LinderaResult;

fn main() -> LinderaResult<()> {
    let dictionary = load_dictionary("embedded://ko-dic")?;
    let segmenter = Segmenter::new(Mode::Normal, dictionary, None);
    let tokenizer = Tokenizer::new(segmenter);

    let text = "한국어의형태해석을실시할수있습니다.";
    let mut tokens = tokenizer.tokenize(text)?;
    for token in tokens.iter_mut() {
        let details = token.details().join(",");
        println!("{}\t{}", token.surface.as_ref(), details);
    }
    Ok(())
}

Lindera CC-CEDICT

Lindera CC-CEDICT は、CC-CEDICT-MeCab に基づく中国語辞書クレートです。

目次

API リファレンス

Lindera CC-CE-DICT

辞書バージョン

このリポジトリには CC-CEDICT-MeCab が含まれています。

辞書フォーマット

IndexName (Chinese)Name (English)Notes
0表面形式Surface
1左语境IDLeft context ID
2右语境IDRight context ID
3成本Cost
4词类Part-of-speech
5词类1Part-of-speech subcategory 1
6词类2Part-of-speech subcategory 2
7词类3Part-of-speech subcategory 3
8併音Pinyin
9繁体字Traditional
10簡体字Simplified
11定义Definition

ユーザー辞書フォーマット (CSV)

簡易版

IndexName (Chinese)Name (English)Notes
0表面形式Surface
1词类Part-of-speech
2併音Pinyin

詳細版

IndexName (Chinese)Name (English)Notes
0表面形式Surface
1左语境IDLeft context ID
2右语境IDRight context ID
3成本Cost
4词类Part-of-speech
5词类1Part-of-speech subcategory 1
6词类2Part-of-speech subcategory 2
7词类3Part-of-speech subcategory 3
8併音Pinyin
9繁体字Traditional
10簡体字Simplified
11定义Definition
12--12 以降は自由に拡張可能です。

API リファレンス

API リファレンスは以下の URL から参照できます:

ビルド

システム辞書のビルド

CC-CEDICT-MeCab のソースファイルをダウンロード・展開し、辞書をビルドします:

% curl -L -o /tmp/CC-CEDICT-MeCab-0.1.0-20200409.tar.gz "https://lindera.dev/CC-CEDICT-MeCab-0.1.0-20200409.tar.gz"
% tar zxvf /tmp/CC-CEDICT-MeCab-0.1.0-20200409.tar.gz -C /tmp
% lindera build \
  --src /tmp/CC-CEDICT-MeCab-0.1.0-20200409 \
  --dest /tmp/lindera-cc-cedict-0.1.0-20200409 \
  --metadata ./lindera-cc-cedict/metadata.json

ユーザー辞書のビルド

% lindera build \
  --src ./resources/user_dict/cc-cedict_simple_userdic.csv \
  --dest ./resources/user_dict \
  --metadata ./lindera-cc-cedict/metadata.json \
  --user

辞書の埋め込み

CC-CEDICT 辞書をバイナリに直接埋め込むには、以下の feature フラグを付けてビルドします:

% cargo build --features=embed-cc-cedict

使用例

外部 CC-CEDICT でトークナイズ

% echo "可以进行中文形态学分析。" | lindera tokenize \
  --dict /tmp/lindera-cc-cedict-0.1.0-20200409
可以    *,*,*,*,ke3 yi3,可以,可以,can/may/possible/able to/not bad/pretty good/
进行    *,*,*,*,jin4 xing2,進行,进行,to advance/to conduct/underway/in progress/to do/to carry out/to carry on/to execute/
中文    *,*,*,*,Zhong1 wen2,中文,中文,Chinese language/
形态学  *,*,*,*,xing2 tai4 xue2,形態學,形态学,morphology (in biology or linguistics)/
分析    *,*,*,*,fen1 xi1,分析,分析,to analyze/analysis/CL:個|个[ge4]/
。      *,*,*,*,*,*,*,*
EOS

埋め込み CC-CEDICT でトークナイズ

% echo "可以进行中文形态学分析。" | lindera tokenize \
  --dict embedded://cc-cedict
可以    *,*,*,*,ke3 yi3,可以,可以,can/may/possible/able to/not bad/pretty good/
进行    *,*,*,*,jin4 xing2,進行,进行,to advance/to conduct/underway/in progress/to do/to carry out/to carry on/to execute/
中文    *,*,*,*,Zhong1 wen2,中文,中文,Chinese language/
形态学  *,*,*,*,xing2 tai4 xue2,形態學,形态学,morphology (in biology or linguistics)/
分析    *,*,*,*,fen1 xi1,分析,分析,to analyze/analysis/CL:個|个[ge4]/
。      *,*,*,*,*,*,*,*
EOS

注意: CC-CEDICT 辞書をバイナリに含めるには、--features=embed-cc-cedict オプションを付けてビルドする必要があります。

Rust API の使用例

use lindera::dictionary::load_dictionary;
use lindera::mode::Mode;
use lindera::segmenter::Segmenter;
use lindera_analysis::tokenizer::Tokenizer;
use lindera::LinderaResult;

fn main() -> LinderaResult<()> {
    let dictionary = load_dictionary("embedded://cc-cedict")?;
    let segmenter = Segmenter::new(Mode::Normal, dictionary, None);
    let tokenizer = Tokenizer::new(segmenter);

    let text = "可以进行中文形态学分析。";
    let mut tokens = tokenizer.tokenize(text)?;
    for token in tokens.iter_mut() {
        let details = token.details().join(",");
        println!("{}\t{}", token.surface.as_ref(), details);
    }
    Ok(())
}

Lindera Jieba

Lindera Jieba は、mecab-jieba に基づく中国語辞書クレートです。

目次

API リファレンス

Lindera Jieba

辞書バージョン

このリポジトリには mecab-jieba が含まれています。

辞書フォーマット

IndexName (Chinese)Name (English)Notes
0表面形式Surface
1左语境IDLeft context ID
2右语境IDRight context ID
3成本Cost
4词类Part-of-speech
5字符类型Character type
6併音Pinyin
7繁体字Traditional
8簡体字Simplified
9定义Definition
10字符数Character count
11首字符First character
12末字符Last character
13频率等级Frequency band

ユーザー辞書フォーマット (CSV)

簡易版

IndexName (Chinese)Name (English)Notes
0表面形式Surface
1词类Part-of-speech
2併音Pinyin

詳細版

IndexName (Chinese)Name (English)Notes
0表面形式Surface
1左语境IDLeft context ID
2右语境IDRight context ID
3成本Cost
4词类Part-of-speech
5字符类型Character type
6併音Pinyin
7繁体字Traditional
8簡体字Simplified
9定义Definition
10字符数Character count
11首字符First character
12末字符Last character
13频率等级Frequency band
14--14 以降は自由に拡張可能です。

API リファレンス

API リファレンスは以下の URL から参照できます:

ビルド

システム辞書のビルド

mecab-jieba のソースファイルをダウンロード・展開し、辞書をビルドします:

% curl -L -o /tmp/mecab-jieba-0.1.1.tar.gz "https://lindera.dev/mecab-jieba-0.1.1.tar.gz"
% tar zxvf /tmp/mecab-jieba-0.1.1.tar.gz -C /tmp
% lindera build \
  --src /tmp/mecab-jieba-0.1.1/dict-src \
  --dest /tmp/lindera-jieba-0.1.1 \
  --metadata ./lindera-jieba/metadata.json

ユーザー辞書のビルド

% lindera build \
  --src ./resources/user_dict/jieba_simple_userdic.csv \
  --dest ./resources/user_dict \
  --metadata ./lindera-jieba/metadata.json \
  --user

辞書の埋め込み

Jieba 辞書をバイナリに直接埋め込むには、以下の feature フラグを付けてビルドします:

% cargo build --features=embed-jieba

使用例

外部 Jieba でトークナイズ

% echo "可以进行中文形态学分析。" | lindera tokenize \
  --dict /tmp/lindera-jieba-0.1.1
可以    c,CHINESE,ke3 yi3,可以,可以,can/may/possible/able to/not bad/pretty good,2,可,以,high
进行    v,CHINESE,jin4 xing2,進行,进行,(of a process etc) to proceed; to be in progress; to be underway/(of people) to carry out; to conduct (an investigation or discussion etc)/(of an army etc) to be on the march; to advance,2,进,行,high
中文    nz,CHINESE,Zhong1 wen2,中文,中文,Chinese language,2,中,文,high
形态    n,CHINESE,xing2 tai4,形態,形态,shape/form/pattern/morphology,2,形,态,high
学      n,CHINESE,xue2,學,学,to learn/to study/to imitate/science/-ology,1,学,学,high
分析    vn,CHINESE,fen1 xi1,分析,分析,to analyze/analysis/CL:個|个[ge4],2,分,析,high
。      w,*,*,*,*,*,*,*,*,*
EOS

埋め込み Jieba でトークナイズ

% echo "可以进行中文形态学分析。" | lindera tokenize \
  --dict embedded://jieba
可以    c,CHINESE,ke3 yi3,可以,可以,can/may/possible/able to/not bad/pretty good,2,可,以,high
进行    v,CHINESE,jin4 xing2,進行,进行,(of a process etc) to proceed; to be in progress; to be underway/(of people) to carry out; to conduct (an investigation or discussion etc)/(of an army etc) to be on the march; to advance,2,进,行,high
中文    nz,CHINESE,Zhong1 wen2,中文,中文,Chinese language,2,中,文,high
形态    n,CHINESE,xing2 tai4,形態,形态,shape/form/pattern/morphology,2,形,态,high
学      n,CHINESE,xue2,學,学,to learn/to study/to imitate/science/-ology,1,学,学,high
分析    vn,CHINESE,fen1 xi1,分析,分析,to analyze/analysis/CL:個|个[ge4],2,分,析,high
。      w,*,*,*,*,*,*,*,*,*
EOS

注意: Jieba 辞書をバイナリに含めるには、--features=embed-jieba オプションを付けてビルドする必要があります。

Rust API の使用例

use lindera::dictionary::load_dictionary;
use lindera::mode::Mode;
use lindera::segmenter::Segmenter;
use lindera_analysis::tokenizer::Tokenizer;
use lindera::LinderaResult;

fn main() -> LinderaResult<()> {
    let dictionary = load_dictionary("embedded://jieba")?;
    let segmenter = Segmenter::new(Mode::Normal, dictionary, None);
    let tokenizer = Tokenizer::new(segmenter);

    let text = "可以进行中文形态学分析。";
    let mut tokens = tokenizer.tokenize(text)?;
    for token in tokens.iter_mut() {
        let details = token.details().join(",");
        println!("{}\t{}", token.surface.as_ref(), details);
    }
    Ok(())
}

SudachiDict(カスタムビルド)

このページでは、SudachiDict — Sudachi が使用する、活発にメンテナンスされている日本語辞書 — を、上流の raw 配布物から、汎用辞書ビルダーとメタデータファイルのみを使って Lindera 辞書として 直接ビルドする方法を説明します。エンジン側の変更は不要です。

[!TIP] SudachiDict は公式辞書クレート lindera-sudachidict としても利用できます。通常の用途では クレートの利用を推奨します(--features embed-sudachidictlindera download sudachidict、 またはビルド済みリリースアーカイブ)。同梱バージョンより新しい上流リリースを取り込みたい 場合や、カスタムバリアント(例: small + core のみ)をビルドしたい場合に、このページを 利用してください。

関連 Issue: lindera/lindera#487

[!NOTE] このレシピで生成される辞書は語彙とラティスの挙動が Sudachi と一致しますが、 Sudachi のエンジンプラグインは再現しません。利用する前に、下記の Sudachi との挙動の違い を確認してください。

なぜ SudachiDict か

従来の MeCab フォーマットの辞書(IPADIC、UniDic 2.1.2)は、語彙の更新が 何年も前に止まっています。SudachiDict は年に数回更新されており、MeCab 互換の raw CSV フォーマットで配布されているため、汎用ビルダーでそのままコンパイルできます。 公式の lindera-sudachidict クレートは 20260723 リリースを同梱しています。この レシピを使って上流の raw 配布物からビルドすれば、より新しいリリースを自分で 取り込むことができます。20260723 リリースでは:

  • 令和 が単一の固有名詞になります(IPADIC と UniDic 2.1.2 は 令|和 に分割します)
  • スマホテレワーク推し活コロナ禍 が語彙に含まれます
  • 1905 年の 321K 文字の小説では、未知語トークンの割合が 2.00%(IPADIC)から 0.51% に下がります

ソースファイル

SudachiDict の raw 辞書ファイル(最新の日付は 一覧 を確認してください):

% mkdir -p /tmp/sudachidict-src
% cd /tmp/sudachidict-src
% for f in small_lex core_lex notcore_lex; do
    curl -LO "http://sudachi.s3-website-ap-northeast-1.amazonaws.com/sudachidict-raw/20260723/${f}.zip"
    unzip -o "${f}.zip" && rm "${f}.zip"
  done

連接コスト行列。SudachiDict 自身のビルドがダウンロードするファイルで、UniDic 2.1.2 の 行列と同一です(SudachiDict のエントリは UniDic の文脈 ID を使用しています):

% curl -LO "https://d2ej7fkh96fzlu.cloudfront.net/sudachidict-raw/matrix.def.zip"
% unzip -o matrix.def.zip && rm matrix.def.zip

文字種定義と未知語定義は UniDic 2.1.2 のソース(lindera-unidic がビルドに使う アーカイブと同じもの)から取得します:

% curl -L -o /tmp/unidic-mecab-2.1.2.tar.gz "https://Lindera.dev/unidic-mecab-2.1.2.tar.gz"
% tar zxf /tmp/unidic-mecab-2.1.2.tar.gz -C /tmp
% cp /tmp/unidic-mecab-2.1.2/char.def /tmp/unidic-mecab-2.1.2/unk.def .

unk.def を SudachiDict のカラムレイアウトに揃える

SudachiDict の辞書行は 19 カラムで、カラム 4(0 始まり)は表示用の表層形(display surface)です。そのため形態素の詳細情報はカラム 5 から始まります。一方 UniDic の unk.def は品詞がカラム 4 にあります。プレースホルダの表示カラムを挿入して、 未知語の詳細情報が辞書語と同じインデックスに来るようにします:

% awk -F, 'BEGIN{OFS=","} {out=$1 OFS $2 OFS $3 OFS $4 OFS "*"; for(i=5;i<=NF;i++) out=out OFS $i; print out}' unk.def > unk.def.tmp && mv unk.def.tmp unk.def

メタデータ

sudachidict-metadata.json として保存します。先頭 4 フィールドはビルダーの予約 カラムで、残りは SudachiDict のカラムを順に列挙したものです:

{
  "name": "sudachidict",
  "encoding": "UTF-8",
  "default_word_cost": -10000,
  "default_left_context_id": 0,
  "default_right_context_id": 0,
  "default_field_value": "*",
  "flexible_csv": true,
  "skip_invalid_cost_or_id": true,
  "normalize_details": false,
  "dictionary_schema": {
    "fields": [
      "surface",
      "left_context_id",
      "right_context_id",
      "cost",
      "display_surface",
      "part_of_speech",
      "part_of_speech_subcategory_1",
      "part_of_speech_subcategory_2",
      "part_of_speech_subcategory_3",
      "conjugation_type",
      "conjugation_form",
      "reading",
      "normalized_form",
      "dictionary_form_id",
      "split_mode",
      "split_a",
      "split_b",
      "word_structure",
      "synonym_group_ids"
    ]
  },
  "user_dictionary_schema": { "fields": ["surface", "part_of_speech", "reading"] }
}

ビルドと確認

% lindera build \
  --src /tmp/sudachidict-src \
  --dest /tmp/lindera-sudachidict \
  --metadata ./sudachidict-metadata.json

small + core + notcore(2026-07-23)のビルドは Lindera v6 で約 10 秒、生成される辞書は 約 570MB です。なお、Lindera のリリース間でオンディスク辞書フォーマットのバージョンが 変わった場合、ビルド済み辞書は再ビルドが必要です (v5 から v6 への移行 を参照)。

% echo "令和五年に始まった。推し活が楽しい。" | lindera tokenize --dict /tmp/lindera-sudachidict
令和	令和,名詞,固有名詞,一般,*,*,*,レイワ,令和,*,A,*,*,*,*
五	五,名詞,数詞,*,*,*,*,ゴ,五,*,A,*,*,*,017040
年	年,名詞,普通名詞,助数詞可能,*,*,*,ネン,年,*,A,*,*,*,*
...
推し活	推し活,名詞,普通名詞,一般,*,*,*,オシカツ,推し活,*,A,*,*,*,*

詳細情報には SudachiDict の追加カラム(正規化形、分割参照、同義語グループ ID)が 含まれるため、後段の処理から利用できます。

[!NOTE] display_surface が品詞カラムの前にあるため、details[0] は IPADIC や UniDic の ような品詞ではなく表示用表層形になります。details の先頭を品詞タグとして位置ベースで 読むトークンフィルタ(japanese_stop_tagsjapanese_keep_tagsjapanese_compound_word)は、この辞書では期待どおりにマッチしません。 token.get("part_of_speech") のようなスキーマ経由のアクセスは正しく動作します。

Sudachi との挙動の違い

ラティス層は等価であることを検証済みです: 行列ファイルは SudachiDict の公式ビルドが 使用するものと byte 単位で同一(sha256)であり、Sudachi のパス書き換え・入力正規化 プラグインを無効にすると、Sudachi エンジンはこの辞書を使う Lindera と同じ最小コスト パスを生成します。残る違いは辞書データではなく Sudachi のエンジンプラグインです:

Sudachi の機能効果Lindera での状況
JoinKatakanaOovPluginカタカナ連続を結合するパス書き換え(例: よりコストの低い サブ+スク のパスではなく サブスク未対応
JoinNumericPlugin数値列の結合未対応
DefaultInputTextPlugin検索前の NFKC + 小文字化による正規化(例: 半角の AI が正規化済みエントリにヒット)Lindera の character filter(unicode_normalize)で部分的に代替可能
A/B/C 分割モードsplit_a / split_b 参照による辞書エントリの分割未適用。エントリは分割せずそのまま使用されます。分割参照は詳細情報に保持されるため、後処理で適用できます

ライセンス

SudachiDict は Apache-2.0 です。matrix.defchar.defunk.def は UniDic の一部です (BSD/GPL/LGPL のトリプルライセンス — SudachiDict の LEGAL notice を参照)。このレシピでビルドした辞書は両方を含みます。再配布の前にライセンスを 確認してください。

v3 から v4 への移行

Lindera v4.0.0 は、v3 系で意図的に先送りしていた破壊的変更をまとめて取り込んだメジャー リリースです。個々の変更は小さいものですが、安心してアップグレードできるよう、すべての変更を 本ガイドに列挙します。

破壊的変更は、v3.0.7 と v4 の公開サーフェスを cargo public-api で機械的に差分比較して検証 しています。

概要

変更対象対応
デフォルトスキーマのフィールド名が pos_detail_* に統一Python, Node.js, Ruby, PHPmiddle_pos / small_pos / fine_pospos_detail_1 / pos_detail_2 / pos_detail_3 に変更
Token.details が常にリストPython, Node.js, Rubynull / None / nil の処理を削除
バインディングの Segmenter を削除Python, WASM代わりに tokenizer を使用
LINDERA_CACHE 環境変数を削除Rust ビルド, CLILINDERA_DICTIONARIES_PATH を使用
ユーザー辞書のバイナリ形式が変更すべて(ビルド済み .binユーザー辞書を CSV から再ビルド
lindera-dictionary の viterbi 内部をカプセル化Rust クレート利用者新しいアクセサを使用

最上位の lindera クレートの公開 Rust API は v3.0.7 と v4 で変わりません。

デフォルト辞書スキーマのフィールド名

Schema.create_default()(およびデフォルト辞書スキーマ)は、3 つの品詞詳細フィールドを middle_pos / small_pos / fine_pos ではなく pos_detail_1 / pos_detail_2 / pos_detail_3(インデックス 5, 6, 7)と命名するようになりました。これにより、すべての バインディングが、すでに pos_detail_* を使用していたコアの lindera::dictionary::Schema::default() と一致します。

対象は Python・Node.js・Ruby・PHP バインディングです。WASM バインディングはすでに pos_detail_* を使用していたため変更ありません。

Python の例:

schema = Schema.create_default()
# v3: schema.fields[5] == "middle_pos"
# v4: schema.fields[5] == "pos_detail_1"

# v3
index = schema.get_field_index("middle_pos")
# v4
index = schema.get_field_index("pos_detail_1")

これらのフィールド名を文字列で参照している箇所(ルックアップ、カスタムスキーマ、シリアライズ された設定など)があれば、pos_detail_* 形式に更新してください。

Token.details は常にリスト

Token.details は常に文字列のリストになり、null / None / nil を返さなくなりました。 詳細を持たないトークンは空リストで表現されます。以前は Python・Node.js・Ruby バインディングが (実際には常に値が入っているにもかかわらず)nullable 型でラップしていました。PHP と WASM バインディングはもともと非 nullable でした。

Python では型が list[str] | None から list[str] に変わります:

# v3 — 型の都合で null チェックが必要だった
if token.details is not None:
    pos = token.details[0]

# v4 — details は常にリスト
pos = token.details[0]

Node.js では Array<string> | null から Array<string> に、Ruby では Array | nil から Array に変わります。null / nil チェックを削除してください。

バインディングの Segmenter を削除

使われていなかった Segmenter ラッパーをバインディングから削除しました。コンストラクタが無く 使用できないもので、形態素分割は常に tokenizer から利用できます。

  • Python: lindera.segmenter サブモジュールと lindera.segmenter.Segmenter が無くなりました。
  • WASM: Segmenter クラスのエクスポートが無くなりました。

代わりに tokenizer でトークン化してください:

from lindera import Tokenizer, TokenizerBuilder

tokenizer = TokenizerBuilder().build()
tokens = tokenizer.tokenize("関西国際空港")

LINDERA_CACHE 環境変数を削除

非推奨だったビルド時環境変数 LINDERA_CACHE を削除しました。数リリース前から正式に サポートされている LINDERA_DICTIONARIES_PATH を使用してください:

# v3(非推奨)
export LINDERA_CACHE=/path/to/dicts

# v4
export LINDERA_DICTIONARIES_PATH=/path/to/dicts

ユーザー辞書のバイナリ形式が変更

ユーザー辞書は、システム辞書と同じ 8-bit のバリアント数エンコーディングを使うようになりました (1 表層あたり最大 255 バリアント、以前は 31)。そのため、v3 でビルドしたユーザー辞書の .bin ファイルは v4 では誤ってデコードされます。形式バージョンのガードが無いため、失敗はサイレントで す(トークンは生成されますが、誤った詳細になります)。

v4 でユーザー辞書を CSV から再ビルドしてください:

lindera build --user \
  --src user_dict.csv \
  --dest ./build \
  --metadata lindera-ipadic/metadata.json

ユーザー辞書をビルド済み .bin ではなく .csv から読み込んでいる場合は、読み込み時に再ビルド されるため対応は不要です。

Rust ライブラリ: lindera-dictionary の viterbi 内部

これは lindera-dictionary クレートを直接利用している場合にのみ影響します。lindera クレートの API は変わりません。

内部の viterbi 構造体は公開フィールドを持たなくなりました。代わりにアクセサを使用してください:

#![allow(unused)]
fn main() {
// v3 — フィールドへ直接アクセス
let id = word_id.id;
let cost = word_entry.word_cost;

// v4 — アクセサ
let id = word_id.id();
let cost = word_entry.word_cost();
}

lindera_dictionary::viterbi のその他の変更:

  • EdgeType を削除しました。
  • WordEntrynew() / word_cost() / word_id() を、WordIdid() を追加しました。
  • WordEntry::serialize / WordEntry::deserialize / WordEntry::SERIALIZED_LEN を非公開に しました。
  • util::read_aligned_fileembedded_dictionary! マクロを追加しました。

この一覧は、lindera-dictionary クレートの v3.0.7 と v4 リリース間で機械生成された 完全な cargo public-api 差分から作成しています。

アップグレードチェックリスト

  • middle_pos / small_pos / fine_pospos_detail_1 / pos_detail_2 / pos_detail_3 に置き換える(Python, Node.js, Ruby, PHP)。
  • Token.detailsnull / None / nil チェックを削除する(Python, Node.js, Ruby)。
  • バインディングの Segmenter の使用を tokenizer に置き換える(Python, WASM)。
  • LINDERA_CACHELINDERA_DICTIONARIES_PATH に置き換える。
  • ビルド済みユーザー辞書 .bin を CSV から再ビルドする。
  • lindera-dictionary の viterbi フィールド直接アクセスを新しいアクセサに切り替える(Rust)。

v4 から v5 への移行

Lindera v5.0.0 では、ワークスペースをリーンなコアを中心に再構成しました。 lindera クレートのデフォルトビルドは純粋な形態素分割器(セグメンター)となり、 辞書学習パイプラインは独立したクレートに移動しました。このガイドでは、 すべての破壊的変更とその対処方法を説明します。

概要

変更影響範囲対処方法
分析チェーンが新クレート lindera-analysis へ移動Tokenizer・character filter・token filter を使う Rust ユーザーlindera-analysis に依存し import パスを更新
lindera-dictionarytrain feature 廃止lindera-dictionary --features train を直接使うユーザーlindera-trainer(または lindera facade の train feature)に依存
ビルドキャッシュ変数の改名LINDERA_DICTIONARIES_PATH を設定しているユーザーLINDERA_BUILD_DICTIONARY_CACHE_DIR に改名(旧名は v6.0.0 まで動作)

言語バインディング(Python・Node.js・Ruby・PHP・WASM)と CLI は影響を 受けません。必要な feature はそれぞれが有効化しており、API と出力も 変わりません。トークナイズ結果も不変です — 同じ入力と辞書に対して、 v5 は v4 とバイト単位で同一のトークンを出力します。

分析チェーンは lindera-analysis クレートへ

v5.0 では lindera クレートは純粋な形態素分割器になりました。 character_filtertoken_filtertokenizer モジュールは新クレート lindera-analysis に移動しています (Lucene のトークナイザーコアとアナライザーモジュールの分離と同じ構成です)。

Tokenizer やフィルタを使用している場合は、新クレートに依存し import パスを 更新してください。API 自体は変更ありません:

# v4
[dependencies]
lindera = "4.0"

# v5
[dependencies]
lindera = "5.0"
lindera-analysis = "5.0"
#![allow(unused)]
fn main() {
// v4
use lindera::tokenizer::Tokenizer;
use lindera::token_filter::japanese_stop_tags::JapaneseStopTagsTokenFilter;

// v5
use lindera_analysis::tokenizer::Tokenizer;
use lindera_analysis::token_filter::japanese_stop_tags::JapaneseStopTagsTokenFilter;
}

テキストの分割だけを行う場合、コードの変更は不要です。むしろ依存ツリーが 軽量化されます(kanaria・unicode-normalization・unicode-segmentation・ unicode-blocks・serde_yaml_ng がビルドされなくなります):

#![allow(unused)]
fn main() {
use std::borrow::Cow;

use lindera::dictionary::load_dictionary;
use lindera::mode::Mode;
use lindera::segmenter::Segmenter;

let dictionary = load_dictionary("/path/to/ipadic")?;
let segmenter = Segmenter::new(Mode::Normal, dictionary, None);
let tokens = segmenter.segment(Cow::Borrowed("関西国際空港限定トートバッグ"))?;
}

学習機能は lindera-trainer クレートへ

CRF 学習パイプライン(TrainerConfigTrainerCorpusModelSerializableModel)は、lindera-dictionarytrain feature 配下の trainer モジュールから、新しい lindera-trainer クレートに移動しました。 これにより lindera-dictionarylindera-crfregex に依存しなく なりました。

lindera facade 経由の利用は変更不要ですtrain feature が lindera-trainer を取り込み、同じパスで再エクスポートします:

#![allow(unused)]
fn main() {
// v4 でも v5 でも動作します(train feature 有効時):
use lindera::dictionary::trainer::{Corpus, Trainer, TrainerConfig};
}

lindera-dictionary --features train を直接使っていた場合のみ、 切り替えが必要です:

# v4
[dependencies]
lindera-dictionary = { version = "4.0", features = ["train"] }

# v5
[dependencies]
lindera-dictionary = "5.0"
lindera-trainer = "5.0"
#![allow(unused)]
fn main() {
// v4
use lindera_dictionary::trainer::{Corpus, Trainer, TrainerConfig};

// v5
use lindera_trainer::{Corpus, Trainer, TrainerConfig};
}

CLI の lindera trainlindera exportlindera build ワークフローに 変更はありません。

ビルドキャッシュ環境変数の改名

ビルド時の辞書キャッシュ変数 LINDERA_DICTIONARIES_PATHLINDERA_BUILD_DICTIONARY_CACHE_DIR に改名されました。この変数は辞書クレートの build script だけがビルド時に読み取るもので、ダウンロードした辞書アーカイブと ビルド済みバイナリ辞書を保持する自動管理のキャッシュを指定します。新しい名前は その契約(ビルド時専用・辞書・キャッシュ)を明示します。

旧名は v5.x の間は非推奨のフォールバックとして動作し(両方設定時は新名が優先)、 v6.0.0 で削除されます。

# v4
export LINDERA_DICTIONARIES_PATH=/path/to/cache

# v5
export LINDERA_BUILD_DICTIONARY_CACHE_DIR=/path/to/cache

v5 から v6 への移行

Lindera v6.0.0 では、ビルド済みユーザー辞書の再ビルドが必要になり、 トークナイズ結果が 2 点(Decompose モードの精度修正と、新しいデフォルト 有効な未知語機能)で変わり、JavaScript バインディングのトークン型が変わり、 lindera-dictionary の一部 Rust API が改名されます。ほとんどのユーザーは 辞書の再ビルドだけで済みますが、lindera_dictionary::viterbi/mode の型を 直接使っている場合、JavaScript を使っている場合、未知語(辞書外の文字列)に 対する厳密な旧出力に依存している場合は、追加で確認すべき点があります。

v5.2 以前からアップグレードする場合は、後述のシステム辞書フォーマット 変更も対象になります。この変更は v5.3.0 で既にリリース済みのため、v5.3.0 からのアップグレードではシステム辞書の再ビルドは不要です。ただしユーザー 辞書の再ビルドは、これとは別の理由で必要です。

概要

変更影響範囲対処方法
ビルド済みユーザー辞書 .bin が読み込めなくなった.bin からユーザー辞書を読み込むすべてのユーザーCSV から lindera build --user で再ビルド
学習済み model.dat が読み込めなくなった以前のビルドで生成した model.dat を再利用しているユーザーlindera train を再実行
Decompose モードの長さペナルティが非 3 バイト文字に対して正確になった1・2・4 バイトの UTF-8 文字を含むテキストに対する Mode::Decompose の出力対応不要(正確性修正)。出力を厳密に固定している場合は Decompose 出力を再確認
未知語の候補ラダー(length ladder)がデフォルトで有効に辞書外テキストに対する Normal・Decompose 両モードの出力v5 と同一の出力が必要な場合は unknown_word_ladder(false) / --disable-unknown-word-ladder を設定
max_grouping_len オプションの新設(--max-grouping-len、config キー、builder/worker setter)MeCab の max-grouping-size 相当の上限を使いたいユーザー対応不要(デフォルトは従来どおり無制限)
JS バインディングがプレーンオブジェクトを返すようになり Token クラスが廃止Node.js・WASM ユーザーtoken.getDetail(i)token.details[i] に置換。WASM ユーザーはフィールド名の camelCase 化にも対応
npm・PyPI のパッケージ名が変更lindera-nodejslinderalindera-pythonlindera、WASM は lindera-wasm に統合)npm・PyPI からバインディングをインストールしているユーザー依存関係と import のパッケージ名を更新(下記参照)
lindera_dictionary::viterbi/mode の一部項目が改名・削除、Lattice::set_text/set_text_nbest に引数 2 つ追加Lattice/Edge/Penalty/Mode を直接利用するコード(lindera クレートの Segmenter/Tokenizer/Worker API 利用者は対象外)下記の表に従い呼び出し箇所を更新
ビルド済み辞書フォーマットがバージョン 2(dict.dadict.trie + dict.valsidx)— v5.3.0 でリリース済みv5.2 以前で作成した自前ビルドのシステム辞書lindera build で再ビルド、または lindera download で再取得
loadDictionaryFromBytes() が 9 引数 — v5.3.0 でリリース済みv5.2 以前からアップグレードする、バイトデータから辞書を読み込む WASM ユーザーdictDa の代わりに dictTriedictValsIdx を渡す
OPFS の DictionaryFilesdictDadictTrie + dictValsIdx に置き換え — v5.3.0 でリリース済みv5.2 以前からアップグレードする、opfs ヘルパーを使う WASM ユーザーOPFS にキャッシュ済みの辞書を再ダウンロード

npm・PyPI・crates.io のパッケージ名変更

v6.0.0 から、公開パッケージ名がサフィックス付きの名前から素の名前 (bare name)に統一されます:

レジストリ旧パッケージ名新パッケージ名
npm(Node.js 本体)lindera-nodejslindera
npm(プラットフォーム別)lindera-nodejs-<platform>(例: lindera-nodejs-darwin-arm64lindera-<platform>(例: lindera-darwin-arm64
npm(WASM)lindera-wasm-web / lindera-wasm-bundlerlindera-wasm(単一パッケージ)
PyPIlindera-pythonlindera

RubyGems の lindera と、crates.io のコアクレート(linderalindera-dictionary など)は変更ありません。

Node.js(npm)

package.json の依存関係と require/import のパッケージ名を更新してください:

// v5
const { TokenizerBuilder } = require("lindera-nodejs");

// v6
const { TokenizerBuilder } = require("lindera");

プラットフォーム別パッケージ(optionalDependencies 経由で自動的に 解決されます)も lindera-nodejs-<platform> から lindera-<platform> に 変わりますが、通常は明示的に依存指定していないため対応は不要です。

lindera-nodejs 系パッケージは npm 上で deprecated としてマークされます。 公開済みの旧バージョンはそのまま残ります。

Python(PyPI)

インストールコマンドと依存関係を更新してください:

# v5
pip install lindera-python

# v6
pip install lindera

Python の import 名は変わりません — 以前から import lindera であり、 コードの変更は不要です。変わるのは PyPI 上の配布名だけです。

移行を助けるため、lindera-python の最終リリースとして、新しい lindera パッケージに依存するだけの移行スタブ(transition stub)が PyPI に公開されます。 pip install lindera-python は当面動作しますが、依存関係は lindera に 切り替えてください。

WASM(npm)

lindera-wasm-weblindera-wasm-bundler の 2 パッケージは、 wasm-pack --target web でビルドされた単一の lindera-wasm パッケージに 統合されます。

lindera-wasm-web を使っていた場合は、パッケージ名の置き換えのみです:

// v5
import __wbg_init, { TokenizerBuilder } from "lindera-wasm-web";
await __wbg_init();

// v6
import init, { TokenizerBuilder } from "lindera-wasm";
await init();

lindera-wasm-bundler を使っていた場合も、同じ lindera-wasm パッケージを インストールします。ただし web ターゲットのビルドでは初期化が自動では 行われないため、使用前にデフォルトエクスポートの非同期初期化関数を必ず 呼び出してください:

// v5(bundler ターゲット: 初期化はバンドラーが処理)
import { TokenizerBuilder } from "lindera-wasm-bundler";

// v6(web ターゲット: 明示的な初期化が必要)
import init, { TokenizerBuilder } from "lindera-wasm";
await init();

モダンなバンドラー(Vite、Webpack 5 の asyncWebAssembly など)は web ターゲットのビルドをそのまま扱えます。設定例は ブラウザでの使用 を参照してください。

crates.io

バインディングクレート(lindera-pythonlindera-nodejslindera-rubylindera-wasm)は crates.io への公開を終了します。公開済みの 5.3.0 までの バージョンはそのまま残ります。これらは各言語のレジストリ(PyPI・npm・ RubyGems)経由で利用するものであり、Rust クレートとして依存する用途は 想定されていません。コアクレートは引き続き crates.io に公開されます。

ビルド済みユーザー辞書の再ビルドが必要

Lindera v6 では daachorse を 4.x から 5.0 に更新しており、ユーザー辞書が 内部に持つ Aho-Corasick オートマトンのシリアライズ形式が変わりました。 そのため、v5 系でビルドした .bin ファイルはロードに失敗します:

LinderaError(kind=Deserialize, source=InvalidAutomatonError: invalid serialized automaton)

システム辞書と異なり、ユーザー辞書の .bin には format_version が無いため バージョン検査ができず、より親切なメッセージを出せません。上記の デシリアライズエラーとして表面化します。

CSV から v6 で再ビルドしてください:

lindera build --user \
  --src ./user_dict.csv \
  --dest ./build \
  --metadata ./lindera-ipadic/metadata.json

.bin ではなく .csv からユーザー辞書を読み込んでいる場合は、ロード時に コンパイルされるため対応は不要です。

学習済みモデルファイルの再生成が必要

lindera train が書き出す model.dat がバイト単位で再現可能になりました。 同じ入力・同じフラグで 2 回学習するとバイト列が一致するため、 チェックサムの取得・キャッシュ・差分比較ができます。

これを実現するため、連接行列と未知語カテゴリをハッシュマップではなく 順序付きマップとして保持するようにしました。rkyv は archived HashMap を ソース走査順にレイアウトし、その順序はインスタンスごとにシードされるためです。 ディスク上のレイアウトが変わるので、以前のビルドが書き出した model.dat は 読み込みに失敗します。

failed to deserialize model: ... re-run `lindera train` to regenerate it.

model.datlindera trainlindera export の間の中間ファイルであり、 配布物ではありません。lindera train を再実行して生成し直してください。 旧モデルからすでにエクスポート済みの辞書には影響しません。また、 学習された重みはどちらでも同一です。変わったのは直列化の順序だけです。

関連する注意点が 2 つあります。

  • SerializableModel::connection_matrixSerializableModel::unk_categories の型が HashMap から BTreeMap に変わりました。これらの公開フィールドを直接読んでいる 場合にのみ影響します。ライターメソッド群は変更ありません。
  • 再現性は --max-threads の値によらず成り立ちます。勾配と損失は学習データの 固定分割を固定順で加算するため、スレッド数は所要時間だけを変え、 学習結果は変えません。
  • lindera export の出力もバイト単位で再現可能になりました。metadata.jsonupdated_at タイムスタンプを持たなくなり、各エクスポートライターは決定的な 順序でエントリを書き出します。これに伴い Rust API から ModelInfo.updated_at フィールドを削除しました(lindera-dictionary)。 このキーを含む既存の metadata.json は引き続き読み込めます。
  • DictionaryRewriter::rewrite_cachedclear_cache を削除しました。 キャッシュは素性文字列全体をキーにしており、実辞書では行ごとにほぼ一意で 一度もヒットせず、メモリを保持するだけだったためです。代わりに rewrite を呼んでください(&mut も不要になりました)。
  • FeatureExtractor::extract_* 6 メソッドの features 引数を &[String] から &[&str] に変更しました。呼び出し側がフィールドごとに String を確保する必要がなくなります。&[String] を持っている場合は &v.iter().map(|s| s.as_str()).collect::<Vec<_>>() で渡せます。
  • lindera-wasm からファイルシステム依存のエクスポート loadUserDictionary / buildDictionary / buildUserDictionary / TokenizerBuilder.setUserDictionary(uri)(と snake_case エイリアス)を 削除しました。wasm32-unknown-unknown にはファイルシステムが無く、 これらは常に実行時に失敗していました。システム辞書は loadDictionaryFromBytes()(または embedded://)で、ユーザー辞書は 新設の loadUserDictionaryFromBytes() / loadUserDictionaryBinFromBytes()setUserDictionaryInstance() で 読み込んでください。loadDictionary() はファイル URI・パスを bytes API を案内するエラーで即座に拒否するようになりました。
  • 固定分割の導入で加算順が変わったため、この変更に学習した重みと 変更後に学習した重みはバイト単位では一致しません(--max-threads 1 を 含むすべてのスレッド数で)。以後の成果物を比較可能にするには lindera train を再実行してください。

辞書フォーマットバージョン 2(v5.3.0 でリリース済み)

この変更は v6.0.0 ではなく v5.3.0 でリリース済みです。v5.3.0 から アップグレードする場合、システム辞書は既にフォーマットバージョン 2 で あり、そのままロードできます。この節は v5.2 以前からアップグレード する場合にのみ従ってください。

システム前方一致辞書は、シリアライズされた daachorse Aho-Corasick オートマトン (dict.da)として保存されなくなりました。ビルダはビルド時に crawdad で文字単位の ダブル配列トライを構築して dict.trie として書き出し、単語値への u32 累積和 インデックス(dict.valsidx)を併せて出力します。実行時にはトライをシリアライズ済み バイト列上で直接走査するため、ロードはデシリアライズではなく O(1) のヘッダ検査で済み、 mmap 下では他の大きなコンポーネントと同様に遅延ページングされたままになります。

ビルド済み辞書ディレクトリは以下の 9 ファイルで構成されます:

ファイル説明
metadata.json辞書メタデータ(format_version を含むようになった)
dict.trie文字単位のダブル配列トライ構造(新規)
dict.valsidx単語値インデックス(新規)
dict.vals単語値データ
dict.wordsidx単語詳細インデックス
dict.words単語詳細(形態素素性)
matrix.mtx連接コスト行列
char_def.bin文字カテゴリ定義
unk.bin未知語辞書

dict.triedict.valsidx 以外のファイルは v5 から変更ありません。dict.da は 存在しなくなりました。

古い辞書のロードは明確なエラーで失敗する

ビルド済み辞書の metadata.jsonformat_version: 2 を記録し、ローダーがこれを検証 します。v5.2 以前でビルドした辞書のロードは、ヘッダを持たないバイナリファイルを 誤読する代わりに、対処方法を含むエラーで失敗します:

Dictionary 'ipadic' has format version 1, but this build of Lindera reads format version 2. To fix this, rebuild it with `lindera build`, or download a matching prebuilt dictionary with `lindera download`.

トークナイズ結果の変更

上記のフォーマット変更とは異なり、以下の 2 点は同じ入力・同じ辞書でも Lindera が出力するトークン自体を変える可能性があります。

Decompose モードのペナルティ精度修正

Mode::Decompose の長さペナルティは、スパンの文字数を (終了バイト位置 - 開始バイト位置) / 3 で近似していました。これは全体が 3 バイト UTF-8 文字(一般的な漢字・かな)で構成されたテキストに対してのみ 正確です。Lindera の Viterbi ラティスは内部的に文字単位で索引されるように なったため、ペナルティは実際の文字数を使うようになりました。この変更が 影響するのは 1 バイト(ASCII)・2 バイト・4 バイト(絵文字や一部の CJK 拡張文字など)の UTF-8 文字を含むスパンの Decompose 出力のみで、純粋な 日本語の Decompose 出力には影響しません。これは挙動選択ではなくバグ修正の ため、旧来の(不正確な)挙動に戻すオプションはありません。

未知語の候補ラダー(新規・デフォルト有効)

辞書に無い文字が連続する箇所に対して、MeCab や Vibrato は候補となる未知語の 長さを 1..=LENGTHLENGTH は辞書の char.def にあるカテゴリごとの フィールド)まで段階的に(梯子=ladder のように)生成し、Viterbi 探索に コスト最小の長さを選ばせます。Lindera は char.def から LENGTH を パースして保存はしていましたが、実行時には一切参照しておらず、常に 1 文字の候補のみ(グルーピング対象カテゴリの場合はラン全体を覆う 1 候補のみ) しか生成していませんでした。

v6.0.0 からは、このラダーがデフォルトで生成されるようになります。IPADIC で 影響を受けるのは KANJIHIRAGANAKATAKANALENGTH=2)カテゴリで、 それ以外の大半のカテゴリは LENGTH=0 のため影響を受けません。実際の効果と しては、辞書に無い漢字・かなが連続する箇所で、1 文字ずつに分割するより 複数文字でまとめたほうがコストが低い場合に、複数文字の未知語としてまとめて トークナイズされるようになります — 例えば、未知の 2 字熟語が従来は 2 つの 未知語トークンに分かれていたのが、1 つの未知語トークンになります。

これは後述の新オプション max_grouping_len(ラン全体のグルーピング上限) とは独立した、加算的な機能です。

辞書外の漢字・かな連続を含むテキストで v5 と同一の出力を再現するには、 ラダーを無効化してください:

let segmenter = Segmenter::new(mode, dictionary, None)
    .unknown_word_ladder(false);
lindera tokenize -d ipadic --disable-unknown-word-ladder input.txt

AnalysisWorker/SegmentWorkerset_unknown_word_ladder(bool)TokenizerBuilderset_segmenter_unknown_word_ladder(bool) で同じ 切り替えができます。

新オプション: max_grouping_len(デフォルトは従来どおり)

v6 では MeCab の max-grouping-size と同じ意味論を追加しました。辞書外 文字のグルーピング可能なランが、先頭を除いて max_grouping_len 文字を 超える場合、グループ候補を出さずに 1 文字ずつの未知語を使います。 デフォルトは無制限で、これは v5 と同じ挙動です。つまり 明示的に設定しない限り何も変わりません:

let segmenter = Segmenter::new(mode, dictionary, None)
    .max_grouping_len(Some(24)); // MeCab 自体のデフォルト値
lindera tokenize -d ipadic --max-grouping-len 24 input.txt

SegmentWorker/AnalysisWorkerset_max_grouping_len(Option<usize>)TokenizerBuilderset_segmenter_max_grouping_len(usize)、および segmenter 設定の max_grouping_len キーでも指定できます。

lindera_dictionary の Rust API 変更

以下は lindera_dictionary::viterbi::{Lattice, Edge}lindera_dictionary::mode::{Mode, Penalty} を直接利用するコード(独自の ラティス検査や代替トークナイザーフロントエンドなど)にのみ影響します。 lindera クレートの SegmenterTokenizerSegmentWorkerAnalysisWorker API は影響を受けません — 上記の unknown_word_ladder/max_grouping_len オプションが追加されただけで、 シグネチャの変更はありません。

v5v6理由
Lattice::text_len()Lattice::char_len()ラティスがバイト索引から文字索引に変わったため。改名により、バイト前提のコードは黙って誤動作せずコンパイルエラーになる
Lattice::edges_at(pos)Lattice::edges_at_char(pos)同上(pos が文字位置になった)
Lattice::paths_at(pos)Lattice::paths_at_char(pos)同上
Lattice::capacity() / shrink_to(n)名前は同じだが単位がバイトから文字(スロット)にバイト長は依然として shrink_to の有効な(安全側に倒れる)引数として使える(文はバイト数より文字数の方が少ないため)
Edge::num_chars()削除パック済み Edge は終了位置を保持しなくなった(常にラティススロットの添字と一致するため)ので、エッジ単体からは計算できなくなった
Penalty::penalty(&Edge)Penalty::penalty(&Edge, num_chars: usize)呼び出し側が正確な文字数スパンを渡すようになった(前述の Decompose 精度修正を参照)
Mode::penalty_cost(&Edge)削除コードベース内に呼び出し元が無かった。必要であれば Penalty::penalty で再実装可能
Lattice::set_text(..., search_mode)Lattice::set_text(..., search_mode, max_grouping_len, unknown_word_ladder)新オプション用に引数 2 つが末尾に追加。v6 のデフォルトに合わせるなら Nonetrue、v5 の挙動にするなら Nonefalse を渡す。set_text_nbest も同様に変更
Lattice::take_max_char_len()(新規)加算的な追加。処理した最大の文長を返してリセットする(worker の縮小判定用)

set_textset_text_nbest には、文が u16::MAX 文字未満であることを 検査する panic 契約も追加されました(エッジが開始位置を u16 で保持する ようになったため)。Segmenter を経由する経路は MAX_SENTENCE_BYTES で 文を分割するためすべて安全です。Lattice を直接駆動するコードだけが、 自分で入力を分割する必要があります。

JavaScript バインディング: トークンがプレーンオブジェクトに

これまで両 JS バインディングは Token クラスのインスタンスを返していました。 これらのインスタンスは JavaScript ヒープの外にメモリを保持し、ホストが ファイナライザを実行して初めて解放されます。napi はそのファイナライザを イベントループへ遅延させ、wasm-bindgen は解放を呼び出し側に委ねるため、 大きな入力を yield せずに同期ループでトークナイズすると、強制 GC を 挟んでもメモリが蓄積していました。v6 では両バインディングともプレーン オブジェクトを返すようになり、メモリは JavaScript の GC が完全に管理します。

実用上の利点: バッチループでメモリがフラットに保たれ、結果を JSON.stringifystructuredClone・worker への転送にそのまま使えます。

getDetail(i) の廃止(Node.js・WASM 共通)

プレーンオブジェクトにメソッドは無いため、配列を直接参照してください:

// v5
const pos = token.getDetail(0);

// v6
const pos = token.details[0];

範囲外の読み出しは null ではなく undefined になります。

Node.js: tokenizeObjects の廃止

tokenizeObjects は上記のメモリ問題に対する v5 限定の回避策としてのみ 存在していました。tokenize が同じプレーンオブジェクトを返すように なったため、呼び出しを置き換えてください:

// v5
const tokens = tokenizer.tokenizeObjects(text);

// v6
const tokens = tokenizer.tokenize(text);

tokenizeNbest もプレーンオブジェクトを返すようになったため、v5 で 既知の制約として記載していた蓄積の問題は解消されています。NbestResultToken と同様にクラスではなくなり、同じ tokenscost プロパティを 持つプレーンオブジェクトになりました。

どちらもクラスではなくなったため、パッケージから export されなくなりました。 TokenJsTokenNbestResultJsNbestResult はすべて index.js から 削除され、require("lindera").Tokenundefined になります。 instanceof による判定もできません:

// v5
const { Token } = require("lindera-nodejs");
if (tokens[0] instanceof Token) { /* ... */ }

// v6 — プレーンオブジェクトなので、プロパティで判定する
if (typeof tokens[0].surface === "string") { /* ... */ }

TypeScript 利用者へ: トークンを表すインターフェース名は Token になりました (Token がクラスに使われていた間は JsTokenData という名前でした)。

WASM: フィールド名が camelCase に

廃止された Token クラスは snake_case のフィールドを公開する一方、 同じクラスの toJSON() は既に camelCase を出力しており不整合がありました。 v6 では Node.js バインディングに揃えて camelCase に統一します:

// v5
token.byte_start, token.byte_end, token.word_id, token.is_unknown

// v6
token.byteStart, token.byteEnd, token.wordId, token.isUnknown

surfacepositiondetails は変更ありません。既に toJSON() を 呼んでその結果を読んでいたコードは、これらの名前を使っていたため そのまま動作します。ただし toJSON() 自体は、トークンが既にプレーン オブジェクトであるため廃止されました:

// v5
const data = token.toJSON();

// v6
const data = token; // 既にプレーンオブジェクト

Token はモジュールから export されなくなったため、 import { Token } from 'lindera-wasm-...' は失敗します。代わりに import すべきものはありません — tokenize が直接プレーンオブジェクトを返します。

WASM の tokenizeNbest は v5 の時点で既にプレーンオブジェクトを 返していたため、変更ありません。

対応が不要なケース

  • Python・Ruby・PHP バインディング: 上記の JavaScript の変更の影響を 受けません。これらは解放が決定的であり、JS 側の作り直しの動機となった メモリ問題が元々無かったため、Token クラスをそのまま維持しています。 それぞれプレーンデータへの変換メソッド(to_dict()to_hto_hash)、 toArray())が追加されましたが、削除・改名されたものはありません。
  • .csv から読み込むユーザー辞書: ロード時にコンパイルされるため、 新しいフォーマットが自動的に反映されます。(ビルド済みの .bin は 再ビルドが必要です — 冒頭の節を参照してください。)
  • lindera クレートの公開 API: SegmenterTokenizerSegmentWorkerAnalysisWorker のシグネチャは変わっていません(加算的な unknown_word_ladder/max_grouping_len オプションを除く)。パスや URI から 辞書をロードするコードは、辞書自体を再ビルドまたは再ダウンロードすればそのまま コンパイル・動作します。embed-* の埋め込み辞書は各辞書クレートのビルド スクリプトがコンパイルするため、常に実行中のバージョンと一致します。

アップグレードチェックリスト

全員:

  • ビルド済みユーザー辞書 .bin を CSV から再ビルドするlindera build --user)。どの v5 からのアップグレードでも必要です。
  • 辞書外・非日本語テキストに対するトークナイズ結果を厳密に固定している場合は 再確認する — Decompose ペナルティ修正と未知語ラダー(デフォルト有効)の 両方が結果を変える可能性がある。v5 と同一の未知語出力が必要なら unknown_word_ladder(false) を設定する。

JavaScript(Node.js・WASM):

  • Node.js: package.json の依存を lindera-nodejs から lindera に変更し、 require/import のパッケージ名を更新する。
  • WASM: 依存を lindera-wasm-web / lindera-wasm-bundler から lindera-wasm に変更する。lindera-wasm-bundler を使っていた場合は、使用前に デフォルトエクスポートの初期化関数(await init())を呼び出すコードを 追加する。
  • token.getDetail(i)token.details[i] に置き換える。
  • Node.js: tokenizeObjects(text)tokenize(text) に置き換え、 Token/NbestResult の import をやめる(export されなくなったため)。 TypeScript 利用者は型名 JsTokenDataToken に変更する。
  • WASM: トークンのフィールド参照を camelCase に変更し(byteStartbyteEndwordIdisUnknown)、toJSON() の呼び出しを削除し、 Token の import をやめる。

Python:

  • 依存を lindera-python から lindera に変更する(pip install lindera)。 import lindera はそのままで、コードの変更は不要。

lindera_dictionary を直接利用している場合:

  • 上記の Rust API 表に従い呼び出し箇所を更新する(set_text/set_text_nbest の追加引数 2 つを含む)。

v5.2 以前からアップグレードする場合(以下は v5.3.0 でリリース済み):

  • 自前ビルドのシステム辞書を lindera build で再ビルドするか、lindera download でビルド済み辞書を再取得する。
  • WASM: loadDictionaryFromBytes() の呼び出しを 9 引数のシグネチャに更新する (dictDa の代わりに dictTriedictValsIdx)。
  • WASM: v5 世代の辞書を OPFS から削除し、v6 のアーカイブをダウンロードする。

開発ガイド

このセクションでは、Lindera のビルド、テスト、貢献に関する情報を提供します。

ビルドとテスト

ビルド

デフォルトビルド

デフォルトの feature(mmap)でワークスペースをビルドします:

cargo build

学習機能付きビルド

CRF ベースの辞書学習機能を含めてビルドします:

cargo build --features train

CLI のみビルド

cargo build -p lindera-cli

CLI ではデフォルトで train feature が有効になっています。

テスト

単一テスト

クレート内の特定のテストを実行します(開発時はこちらを推奨):

cargo test -p <crate> <test_name>

学習機能のテスト

cargo test -p lindera-trainer

クレート単位の全機能テスト

単一クレートの全テストスイートを実行します:

cargo test -p <crate> --all-features

注意: CI では --all-features は使用されません。各クレートに対してキュレートされた個別の feature の組み合わせでテストを実行します(.github/workflows/regression.yml を参照)。Makefile のクレート別パターンターゲット(make test-<crate>make lint-<crate>)は CI と同じ feature の組み合わせを適用するため、ローカルでの実行方法としては最も近い代替手段です。

ワークスペース全体のテスト

cargo test

品質チェック

フォーマットチェック

コードのフォーマットがプロジェクトのスタイルに一致しているか確認します:

cargo fmt --all -- --check

フォーマットを自動修正するには:

cargo fmt --all

リント

Clippy を警告をエラーとして扱うモードで実行します:

cargo clippy -- -D warnings

注意: CI で強制されているのは cargo fmt --all -- --check のみです。cargo clippy は現時点で CI では実行されませんが、PR を開く前にローカルで(例えば make lint 経由で)実行することを推奨します。

ドキュメント

API ドキュメント

Rust の API ドキュメントを生成して開きます:

cargo doc --no-deps --open

mdBook ドキュメント

ユーザー向けドキュメントをビルドします:

mdbook build docs

http://localhost:3000 でローカルプレビュー:

mdbook serve docs

Markdown リント

ドキュメントの Markdown スタイルの問題をチェックします:

markdownlint-cli2 "docs/src/**/*.md"

ルールはリポジトリルートの .markdownlint.json で設定されています。

Feature フラグ

Lindera は Cargo の feature フラグを使用して、オプション機能と辞書の埋め込みを制御します。

コア Feature

Feature説明デフォルト
mmapメモリマップドファイルサポート有効
trainCRF ベースの辞書学習(lindera-trainer に依存)下記参照
  • mmap はメインの lindera クレートでデフォルトで有効です。
  • 分析チェーン(character filter・token filter・Tokenizer)は本クレートの feature ではありません。v5.0 以降は独立クレート lindera-analysis が提供します。 lindera クレート自体は Segmenter API を中心とした純粋な形態素分割器です。
  • trainlindera-clilindera-pythonlindera-nodejslindera-rubylindera-php ではデフォルトで有効です。コアライブラリである lindera クレートではデフォルトで無効なので、ライブラリとして使用する場合は features = ["train"] で明示的に有効にしてください。lindera-wasm では利用できません。

外部辞書の使用(推奨)

推奨される方法は、ビルド済み辞書を外部ファイルとして使用することです。GitHub Releases から辞書をダウンロードし、実行時にそのパスを指定してください:

#![allow(unused)]
fn main() {
let dictionary = load_dictionary("/path/to/ipadic")?;
}

この使用方法では、追加の feature フラグは不要です。

辞書埋め込み Feature(上級者向け)

これらの feature はビルド済み辞書をバイナリに直接埋め込み、実行時に外部辞書ファイルを不要にします。自己完結型バイナリが必要な上級者向けの機能です。

Feature辞書言語
embed-ipadicIPADIC日本語
embed-ipadic-neologdIPADIC NEologd日本語
embed-unidicUniDic日本語
embed-sudachidictSudachiDict日本語
embed-ko-dicko-dic韓国語
embed-cc-cedictCC-CEDICT中国語
embed-jiebaJieba中国語

いずれもデフォルトでは無効です。必要に応じて有効にしてください:

[dependencies]
lindera = { version = "5", features = ["embed-ipadic"] }

埋め込みを有効にした場合、以下のように辞書を読み込めます:

#![allow(unused)]
fn main() {
let dictionary = load_dictionary("embedded://ipadic")?;
}

[!NOTE] 辞書データには Lindera 本体とは別のライセンスが適用されます。辞書を埋め込んだ バイナリを配布する場合(またはビルド済み辞書を再配布する場合)は、対応する 辞書クレートの NOTICE.txt(例: lindera-unidic/NOTICE.txt)に記載された 帰属表示を、配布物に付属するドキュメントや資料に転載してください。

組み合わせ Feature

多言語アプリケーション向けに、複数の辞書を一度に有効にするメタ Feature です。

Feature含まれる辞書
embed-cjkIPADIC + ko-dic + Jieba
embed-cjk2UniDic + ko-dic + Jieba
embed-cjk3IPADIC NEologd + ko-dic + Jieba
embed-cjk4SudachiDict + ko-dic + Jieba

Feature フラグの組み合わせ

複数の feature フラグを組み合わせることができます。例えば、日本語と韓国語の辞書を両方埋め込む場合:

[dependencies]
lindera = { version = "5", features = ["embed-ipadic", "embed-ko-dic"] }

またはコマンドラインから:

cargo build --features embed-ipadic,embed-ko-dic

注意事項

  • 辞書の埋め込みはバイナリサイズを大幅に増加させます。実際に必要な辞書のみを埋め込んでください。
  • train feature は lindera-crf への依存を追加し、コンパイル時間が増加します。トークナイズのみのユースケースでは不要です。
  • mmap feature はファイルシステム辞書に対するメモリマップド読み込みを有効にします(CLIの--mmapまたはsegmenter設定のuse_mmapキーで要求)。辞書フォーマットバージョン 2 以降、トライ(dict.trie/dict.valsidx)もシリアライズ済みバイト列上を直接走査するため、単語リストファイル(dict.vals/dict.wordsidx/dict.words)・接続コスト行列(matrix.mtx)と合わせて、大きなコンポーネントはすべて遅延読み込みされ、匿名メモリを消費しません。ロード時に全体がメモリに展開されるコンポーネントはありません。埋め込み辞書には影響しませんが、埋め込み辞書のトライと接続コスト行列もバイナリ内から直接参照されます。

プロジェクト構成

Lindera は複数のクレートで構成される Cargo ワークスペースとして組織されています。

ディレクトリ構成

lindera/
├── lindera-crf/            # CRF engine (pure Rust, no_std)
├── lindera-dictionary/     # Dictionary base library
├── lindera-trainer/        # CRF ベースの辞書学習
├── lindera/                # Core morphological analysis library
├── lindera-analysis/       # 分析チェーン(character/token filter・tokenizer)
├── lindera-cli/            # CLI tool
├── lindera-binding-core/   # 言語バインディングが共有するFFI非依存のヘルパー
├── lindera-ipadic/         # IPADIC dictionary (Japanese)
├── lindera-ipadic-neologd/ # IPADIC NEologd dictionary (Japanese)
├── lindera-unidic/         # UniDic dictionary (Japanese)
├── lindera-sudachidict/    # SudachiDict dictionary (Japanese)
├── lindera-ko-dic/         # ko-dic dictionary (Korean)
├── lindera-cc-cedict/      # CC-CEDICT dictionary (Chinese)
├── lindera-jieba/          # Jieba dictionary (Chinese)
├── lindera-python/         # Python bindings (PyO3)
├── lindera-nodejs/         # Node.js bindings (NAPI-RS)
├── lindera-ruby/           # Ruby bindings (Magnus + rb-sys)
├── lindera-php/            # PHP bindings (ext-php-rs)
├── lindera-wasm/           # WebAssembly bindings (wasm-bindgen)
├── resources/              # Test resources and sample data
├── docs/                   # Documentation (mdBook)
└── examples/               # Example code

クレートの説明

コアクレート

lindera-crf

条件付き確率場(CRF)の pure Rust 実装です。no_std 環境をサポートします。高速なゼロコピーシリアライゼーションに rkyv を使用します。辞書学習で使用される統計学習エンジンを提供します。

lindera-dictionary

辞書のベースライブラリです。辞書の読み込み、ビルド、クエリ機能を提供します。

lindera-trainer

カスタム辞書作成のための CRF 学習パイプラインです。lindera-dictionary のランタイム型と lindera-crf エンジンの上に構築されています。通常は lindera facade の train feature 経由で利用します(lindera::dictionary::trainer として再エクスポート)。

モジュール役割
config.rs設定管理(種辞書、char.def、feature.def、rewrite.def)
corpus.rs学習コーパスの処理
feature_extractor.rs素性テンプレートの解析と素性 ID 管理
feature_rewriter.rsMeCab 互換の素性書き換え(3セクション形式)
model.rs学習済みモデルの保存、シリアライゼーション、辞書出力

lindera

純粋な形態素セグメンターです。辞書クレートを統合し、Segmenter API を提供します。

lindera-analysis

lindera の上に構築されたLucene風の分析チェーンです。文字フィルタ、トークンフィルタ、およびそれらを Segmenter の周りで組み合わせる Tokenizer を提供します。

lindera-cli

トークナイズ、辞書学習、エクスポート、ビルドのためのコマンドラインインターフェースです。デフォルトで train feature が有効です。

lindera-binding-core

5つの言語バインディング(lindera-pythonlindera-nodejslindera-rubylindera-phplindera-wasm)すべてが共有するFFI非依存のヘルパーです。各バインディングがそれぞれの言語のネイティブAPIでラップするコアのトークナイザー・スキーマ・メタデータ層を提供します。

辞書クレート

各辞書クレートには、特定の言語と辞書ソースのビルド済み辞書データが含まれます。

クレート言語辞書ソース
lindera-ipadic日本語IPADIC
lindera-ipadic-neologd日本語IPADIC NEologd(拡張語彙)
lindera-unidic日本語UniDic
lindera-sudachidict日本語SudachiDict
lindera-ko-dic韓国語ko-dic
lindera-cc-cedict中国語CC-CEDICT
lindera-jieba中国語Jieba

バインディング

lindera-python

PyO3 で構築された Python バインディングです。Lindera のトークナイザー API を Python アプリケーションに公開します。

lindera-nodejs

NAPI-RS で構築された Node.js バインディングです。Lindera のトークナイザー API を Node.js アプリケーションに公開します。

lindera-ruby

Magnusrb-sys で構築された Ruby バインディングです。Lindera のトークナイザー API を Ruby gem として公開します。

lindera-php

ext-php-rs で構築された PHP バインディングです。Lindera のトークナイザー API を PHP 拡張として公開します。

lindera-wasm

wasm-bindgen で構築された WebAssembly バインディングです。ブラウザと Node.js でのトークナイズを可能にします。

その他のディレクトリ

resources/

テストスイートで使用されるサンプル辞書、ユーザー辞書、テストコーパスなどのテストリソースです。

docs/

mdBook で構築されたユーザー向けドキュメントです。目次は docs/src/SUMMARY.md で定義されています。日本語翻訳は docs/ja/ 配下にあります。

examples/

一般的な使用パターンを示す実行可能なサンプルプログラムです。以下のコマンドで実行できます:

cargo run --features=embed-ipadic --example=<example_name>

学習パイプライン

Lindera は、カスタム形態素解析モデルを作成するための CRF ベースの辞書学習機能を提供します。この機能には train feature フラグが必要です。

概要

学習パイプラインは3つのステージで構成されます:

lindera train --> model.dat --> lindera export --> dictionary files --> lindera build --> compiled dictionary
  1. Train: アノテーション付きコーパスと種辞書から CRF の重みを学習し、バイナリモデルファイルを生成します。
  2. Export: 学習済みモデルを Lindera 辞書ソースファイルに変換します。
  3. Build: ソースファイルを Lindera が実行時に読み込めるバイナリ辞書にコンパイルします。

必要な入力ファイル

1. 種辞書 (seed.csv)

MeCab CSV 形式のベース語彙辞書です。

外国,0,0,0,名詞,一般,*,*,*,*,外国,ガイコク,ガイコク
人,0,0,0,名詞,接尾,一般,*,*,*,人,ジン,ジン
参政,0,0,0,名詞,サ変接続,*,*,*,*,参政,サンセイ,サンセイ

各行の構成: surface,left_id,right_id,cost,pos,pos_detail1,pos_detail2,pos_detail3,inflection_type,inflection_form,base_form,reading,pronunciation

種辞書では left_idright_idcost フィールドは 0 に設定されます。学習器が CRF モデルから適切な値を計算します。

2. 学習コーパス (corpus.txt)

タブ区切り形式のアノテーション付きテキストデータです。各行は surface<TAB>pos_info で、文は EOS で区切られます。

外国	名詞,一般,*,*,*,*,外国,ガイコク,ガイコク
人	名詞,接尾,一般,*,*,*,人,ジン,ジン
参政	名詞,サ変接続,*,*,*,*,参政,サンセイ,サンセイ
権	名詞,接尾,一般,*,*,*,権,ケン,ケン
EOS

これ	連体詞,*,*,*,*,*,これ,コレ,コレ
は	助詞,係助詞,*,*,*,*,は,ハ,ワ
テスト	名詞,サ変接続,*,*,*,*,テスト,テスト,テスト
EOS

学習の品質はこのコーパスの量と質に大きく依存します。

3. 文字定義 (char.def)

文字タイプのカテゴリと Unicode コードポイント範囲を定義します。

# Category definition: category_name compatibility_flag continuity_flag length
DEFAULT 0 1 0
HIRAGANA 1 1 0
KATAKANA 1 1 0
KANJI 0 0 2
ALPHA 1 1 0
NUMERIC 1 1 0

# Character range mapping
0x3041..0x3096 HIRAGANA  # Hiragana
0x30A1..0x30F6 KATAKANA  # Katakana
0x4E00..0x9FAF KANJI     # Kanji
0x0030..0x0039 NUMERIC   # Numbers
0x0041..0x005A ALPHA     # Uppercase letters
0x0061..0x007A ALPHA     # Lowercase letters

パラメータは各文字タイプの未知語がどのように分割されるかを制御します。隣接文字との互換性、同一タイプの連続が1つのトークンとして続くかどうか、デフォルトのトークン長を指定します。

4. 未知語定義 (unk.def)

文字タイプごとに未知語の処理方法を定義します。

DEFAULT,0,0,0,名詞,一般,*,*,*,*,*,*,*
HIRAGANA,0,0,0,名詞,一般,*,*,*,*,*,*,*
KATAKANA,0,0,0,名詞,一般,*,*,*,*,*,*,*
KANJI,0,0,0,名詞,一般,*,*,*,*,*,*,*
ALPHA,0,0,0,名詞,固有名詞,一般,*,*,*,*,*,*
NUMERIC,0,0,0,名詞,数,*,*,*,*,*,*,*

5. 素性テンプレート (feature.def)

CRF モデルが学習に使用する情報を定義する MeCab 互換の素性抽出パターンです。

# Unigram features (word-level)
UNIGRAM U00:%F[0]           # POS
UNIGRAM U01:%F[0],%F?[1]    # POS + POS detail (%F?[n] = optional, skipped if *)
UNIGRAM U02:%F[6]           # Base form
UNIGRAM U03:%w              # Surface form

# Bigram features (context combination)
BIGRAM B00:%L[0]/%R[0]      # Left POS / Right POS
BIGRAM B01:%L[0],%L[1]/%R[0],%R[1]  # Left POS detail / Right POS detail

テンプレート変数:

変数説明
%F[n] / %F?[n]インデックス n の素性フィールド(? = オプション、値が * の場合はスキップ)
%L[n]左文脈の素性フィールド(rewrite.def の左セクションから)
%R[n]右文脈の素性フィールド(rewrite.def の右セクションから)
%w単語の表層形
%uユニグラム書き換え後の素性文字列
%l左書き換え後の素性文字列
%r右書き換え後の素性文字列

6. 素性書き換えルール (rewrite.def)

MeCab 互換の3セクション形式による素性正規化ルールです。セクションは空行で区切られます。

# Section 1: Unigram rewrite rules
名詞,固有名詞,*  名詞,固有名詞
助動詞,*,*,*,特殊・デス  助動詞
*  *

# Section 2: Left context rewrite rules
名詞,固有名詞,*  名詞,固有名詞
助詞,*  助詞
*  *

# Section 3: Right context rewrite rules
名詞,固有名詞,*  名詞,固有名詞
助詞,*  助詞
*  *

各行は pattern<TAB>replacement です。パターンはワイルドカードとして * を使用し、前方一致で照合されます。各セクションで最初に一致したルールが適用されます。ユニグラム、左文脈、右文脈に対して異なるルールを独立して適用できるため、スパース性を低減するきめ細かい素性正規化が可能です。

学習パラメータ

パラメータ説明デフォルト
lambda正則化係数(過学習の制御)0.01
regularization正則化の種類: l1l2elasticnet のいずれかl1
elastic-net-l1-ratioElastic Net 正則化の L1 比率(0.0-1.0、--regularization elasticnet の場合のみ使用)0.5
max-iterations最大学習イテレーション数100
max-threads並列処理スレッド数CPU コア数

CLI の使用

Train

lindera train \
    --seed seed.csv \
    --corpus corpus.txt \
    --char-def char.def \
    --unk-def unk.def \
    --feature-def feature.def \
    --rewrite-def rewrite.def \
    --lambda 0.01 \
    --max-iterations 100 \
    --max-threads 4 \
    --output model.dat

Export

学習済みモデルを辞書ソースファイルに変換します:

lindera export \
    --model model.dat \
    --metadata metadata.json \
    --output ./dict-source

以下のファイルが生成されます:

ファイル説明
lex.csv学習済みコスト付きレキシコン
matrix.def連接コスト行列
unk.def未知語定義
char.def文字定義
feature.def素性テンプレート
rewrite.def素性書き換えルール
left-id.def左文脈 ID マッピング
right-id.def右文脈 ID マッピング
metadata.json辞書メタデータ

Build

エクスポートされたソースファイルをバイナリ辞書にコンパイルします:

lindera build \
    --src ./dict-source \
    --dest ./dict-compiled \
    --metadata ./dict-source/metadata.json

出力モデルの形式

学習済みモデルは高速読み込みのために rkyv バイナリ形式でシリアライズされます。以下を含みます:

  • CRF で学習された素性の重み
  • ラベルセット(語彙エントリー)
  • 品詞情報
  • 素性テンプレート
  • 学習メタデータ(正則化、イテレーション数、素性・ラベル数)

出力はバイト単位で再現可能です。同じ入力・同じフラグで 2 回学習すると model.dat のバイト列が一致するため、チェックサムの取得や差分比較ができます。 これは --max-threads の値によらず成り立ちます。勾配と損失は学習データの 固定分割を固定順で加算するため、スレッド数は所要時間だけを変え、 結果は変えません。

API の使用

lindera-trainer の完全な API については、Lindera Trainer アーキテクチャAPIリファレンスを参照してください。

#![allow(unused)]
fn main() {
use std::fs::File;
use lindera_trainer::{Corpus, Trainer, TrainerConfig};

// Load configuration from files
let seed_file = File::open("resources/training/seed.csv")?;
let char_file = File::open("resources/training/char.def")?;
let unk_file = File::open("resources/training/unk.def")?;
let feature_file = File::open("resources/training/feature.def")?;
let rewrite_file = File::open("resources/training/rewrite.def")?;

let config = TrainerConfig::from_readers(
    seed_file,
    char_file,
    unk_file,
    feature_file,
    rewrite_file
)?;

// Initialize and configure trainer
let trainer = Trainer::new(config)?
    .regularization_cost(0.01)
    .max_iter(100)
    .num_threads(4);

// Load corpus
let corpus_file = File::open("resources/training/corpus.txt")?;
let corpus = Corpus::from_reader(corpus_file)?;

// Execute training
let model = trainer.train(corpus)?;

// Save model (binary format)
let mut output = File::create("trained_model.dat")?;
model.write_model(&mut output)?;

// Output in Lindera dictionary format. output_user.csv には read_user_lexicon で
// 読み込んだエントリが入力順で書かれる(呼ばなければ空になる)。
let mut lex_out = File::create("output_lex.csv")?;
let mut conn_out = File::create("output_conn.dat")?;
let mut unk_out = File::create("output_unk.def")?;
let mut user_out = File::create("output_user.csv")?;
model.write_dictionary(&mut lex_out, &mut conn_out, &mut unk_out, &mut user_out)?;

Ok::<(), Box<dyn std::error::Error>>(())
}

推奨コーパス仕様

実用的なアプリケーション向けの辞書を生成するための推奨事項:

コーパスサイズ

レベル文数用途
最小100以上基本的な動作確認
推奨1,000以上実用的なアプリケーション
理想10,000以上商用品質

品質ガイドライン

  • 語彙の多様性: さまざまな品詞のバランスの取れた分布、活用形・接尾語のカバー、専門用語・固有名詞の適切な含有。
  • 一貫性: コーパス全体で分析基準を一貫して適用すること。
  • 検証: 形態素解析結果を手動で検証すること。エラー率を5%以下に維持すること。

貢献ガイド

Lindera への貢献に興味をお持ちいただきありがとうございます。このページでは、貢献を始めるためのガイドラインを紹介します。

はじめに

  1. GitHub でリポジトリをフォークします。

  2. フォークをローカルにクローンします:

    git clone https://github.com/<your-username>/lindera.git
    cd lindera
    
  3. feature ブランチを作成します:

    git checkout -b feature/my-feature
    
  4. 変更を行い、すべてのチェックに通ることを確認します:

    cargo fmt --all -- --check
    cargo clippy -- -D warnings
    cargo test
    

    注意: CI で強制されているのは cargo fmt --all -- --check のみです。cargo clippy は現時点で CI では実行されませんが、PR を開く前にローカルで(例えば make lint 経由で)実行することを推奨します。

  5. 変更をコミットしてプッシュし、プルリクエストを開きます。

コードスタイル

  • リポジトリの既存のコードスタイルに従ってください。
  • コミット前に cargo fmt を実行してください。
  • すべての public および private アイテム(型、関数、モジュール、フィールド、定数、型エイリアス)にドキュメントコメント(///)を記述してください。
  • trait 実装メソッドにも、実装固有の振る舞いを説明するドキュメントコメントを記述してください。
  • 関数・メソッドのドキュメントには、該当する場合 # Arguments# Returns セクションを含めてください。
  • コードコメント、ドキュメントコメント、コミットメッセージ、ログメッセージ、エラーメッセージは英語で記述してください。
  • 本番コードでは unwrap()expect() を避けてください(テストコードでは使用可)。
  • unsafe ブロックは必要な場合にのみ使用し、必ず // SAFETY: ... コメントを付けてください。
  • モジュールは mod.rs スタイルではなく、ファイルベースのスタイル(src/tokenizer.rs)を使用してください。

テスト

  • すべての新機能にユニットテストを作成してください。

  • 開発中は迅速なフィードバックのために関連するテストのみを実行してください:

    cargo test -p <crate> <test_name>
    
  • 学習パイプライン機能に関連する作業では、lindera-trainer クレートのテストを実行してください:

    cargo test -p lindera-trainer
    

コミットメッセージ

Conventional Commits の仕様に従ってください。コミットメッセージは英語で記述してください。

例:

  • feat: add Korean dictionary support
  • fix: correct character category ID in trainer
  • docs: update installation instructions
  • refactor: split large training method into smaller functions

ドキュメント

  • 変更がユーザー向けドキュメントに影響する場合は、docs/src/ 配下の関連ファイルを更新してください。

  • Markdown ファイルの編集後は、リントエラーがないことを確認してください:

    markdownlint-cli2 "docs/src/**/*.md"
    
  • ルールはリポジトリルートの .markdownlint.json で設定されています。

依存関係

新しい依存関係を追加する際は、ライセンスの互換性を確認してください。Lindera は MIT / Apache-2.0 デュアルライセンスを使用しています。

Feature フラグ

学習関連コードの条件コンパイルには #[cfg(feature = "train")] を使用してください。完全なリストは Feature フラグ を参照してください。

問題の報告

バグを報告する際は、以下の情報を含めてください:

  • Lindera のバージョン(lindera --version または Cargo.toml を確認)
  • Rust のバージョン(rustc --version
  • オペレーティングシステム
  • 問題の再現手順
  • 期待される動作と実際の動作