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, penalty?)

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

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

penalty は "decompose" モードが使う Penalty オブジェクトです。null または undefined(デフォルト)では既定のペナルティを使います。"normal" とペナルティを一緒に渡すと、不正なモードと同じくエラーを投げます。オブジェクトでない値や、範囲内の整数でないフィールドを渡すと、InvalidArg のエラーを投げます。

builder.setMode("normal");
builder.setMode("decompose", { kanjiPenaltyLengthPenalty: 0 });

setDictionary(path)

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

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

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

setUserDictionary(uri)

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

builder.setUserDictionary("/path/to/user_dictionary.csv");

setKeepWhitespace(keep)

出力に空白トークンを含めるかどうかを制御します。残した空白はラティスにも残るため、他のトークンも、MeCab と同様に空白をラティス上で読み飛ばすデフォルトの出力と変わることがあります。空白を出力せずにラティスにだけ残すには、代わりに setSkipWhitespace(false) を使います。

builder.setKeepWhitespace(true);

setSkipWhitespace(skip)

空白をラティス上で読み飛ばすかどうかを設定します。値の意味は設定ファイルの segmenter.skip_whitespace と同じです。

値効果
true空白を読み飛ばし、MeCab と同様に空白の前後の語を直接接続する
false空白を独立したノード(空白の見出し語か SPACE の未知語)としてラティスに残す(v7 より前の動作)。空白は引き続き出力から除外する
null または undefinedデフォルト。辞書の metadata.json の設定に従う(SudachiDict 以外では読み飛ばす)

ここで設定した値は、辞書のデフォルトと、fromFile で読み込んだ skip_whitespace より優先されます。空白トークンを残すとき(setKeepWhitespace(true))は効果がありません。残した空白は常にラティスに残るためです。引数は省略できず、それ以外の値を渡すと Error を投げます。

たとえば IPADIC では、Google が 新しい の が は、デフォルトでは助詞(助詞)、setSkipWhitespace(false) では接続詞(接続詞)と解析されます。詳しくは Segmenter を参照してください。

// v7 より前と同じく、空白をラティスに残す
builder.setSkipWhitespace(false);

// 辞書のデフォルトに戻す
builder.setSkipWhitespace(null);

setSpacePenalty(value)

韓国語向けの左側空白ペナルティ(mecab-ko の left-space-penalty-factor)を設定します。直前に空白があり、先頭品詞タグがルールの一覧にある候補に、そのルールのコストを加算します。値の意味は設定ファイルの segmenter.space_penalty と同じです。

値効果
null または undefinedデフォルト。辞書が metadata.json に同梱するルールがあればそれを使用
falseペナルティをオフにする
true辞書のルールを要求する。辞書がルールを同梱していない場合は build() が例外を投げる
{ rules: [{ pos: [...], cost: n }, ...] }辞書のルールの代わりにこのルールを適用する

それ以外の値を渡すと Error を投げます。

// ペナルティをオフにする
builder.setSpacePenalty(false);

// 明示的なルール(ko-dic が同梱するルールと同じ)
builder.setSpacePenalty({
  rules: [
    { pos: ["EC", "EF", "EP", "ETM", "ETN", "VCP", "XSA", "XSN", "XSV"], cost: 3000 },
    { pos: ["JC", "JKB", "JKC", "JKG", "JKO", "JKQ", "JKS", "JKV", "JX"], cost: 6000 },
  ],
});

// 辞書のデフォルトに戻す
builder.setSpacePenalty(null);

[!NOTE] v6.1.0 以降、ko-dic では左側空白ペナルティがデフォルトで適用されます。ko-dic の metadata.json が mecab-ko-dic のルールを同梱しているためです。これにより韓国語の出力が変わります(例: 검색 이 잘 된다 の 이 は、ペナルティがあると感動詞 IC、なければ主格助詞 JKS になる)。オフにするには setSpacePenalty(false) を呼び出してください。直接作成する Tokenizer では、4 番目の引数に false を渡します(new Tokenizer(dictionary, "normal", null, false))。v6.0.0 リリースからダウンロードした ko-dic はルールを同梱していないため、その辞書ではペナルティはオフのままで、setSpacePenalty(true) を指定すると build() が例外を投げます。詳細は Segmenter を参照してください。

appendCharacterFilter(kind, args?)

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

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

appendTokenFilter(kind, args?)

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

builder.appendTokenFilter("lowercase", {});

setSpacePenalty、appendCharacterFilter、appendTokenFilter の引数は、配列とオブジェクトの入れ子が 128 段までです。それより深い値や、自分自身を含む値を渡すと Error を投げます。

ビルド

build()

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

const tokenizer = builder.build();

Tokenizer

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

Tokenizer の作成

new Tokenizer(dictionary, mode?, userDictionary?, spacePenalty?, penalty?)

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

spacePenalty には setSpacePenalty と同じ値を渡せます。null または undefined(デフォルト)では、辞書が同梱する左側空白ペナルティのルールを使います。不正な設定では、setSpacePenalty や build() と同じくエラーを投げます。

penalty には、setMode と同じく "decompose" モードの Penalty を渡せます。エラーも setMode と同じです。

const { Tokenizer, loadDictionary } = require("lindera");

const dictionary = loadDictionary("embedded://ipadic");
const tokenizer = new Tokenizer(dictionary, "normal");

// 左側空白ペナルティなしの ko-dic
const koTokenizer = new Tokenizer(loadDictionary("embedded://ko-dic"), "normal", null, false);

Tokenizer メソッド

tokenize(text)

入力テキストをトークナイズし、プレーンなトークンオブジェクトの配列を返します。

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

パラメータ:

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

戻り値: Token[]

トークンはクラスのインスタンスではなくプレーンな JavaScript オブジェクトです。そのため通常の GC で回収され、JSON.stringify、structuredClone、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返す結果の数。0 以上の整数
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

TokenizerBuilder.setMode() と Tokenizer のコンストラクタは、モードを単純な文字列 ("normal" または "decompose")で、decompose モードのペナルティを Penalty オブジェクトで受け取ります。

Mode

2つの値を持つ文字列列挙型です:

  • Mode.Normal -- 辞書コストに基づく標準的なトークナイズ
  • Mode.Decompose -- ペナルティベースの複合語分解

Penalty

decompose モードのペナルティパラメータを持つプレーンなオブジェクト(TypeScript の型は Penalty)です。フィールドはすべて省略でき、省略したフィールドはデフォルト値になります:

プロパティ型説明
kanjiPenaltyLengthThresholdnumberペナルティを適用する前の漢字列の長さの閾値。0 以上の整数(デフォルト: 2)
kanjiPenaltyLengthPenaltynumber長い漢字列に対するペナルティ値。32 ビット符号付き整数(デフォルト: 3000)
otherPenaltyLengthThresholdnumberペナルティを適用する前のその他の文字列の長さの閾値。0 以上の整数(デフォルト: 7)
otherPenaltyLengthPenaltynumber長いその他の文字列に対するペナルティ値。32 ビット符号付き整数(デフォルト: 1700)

"decompose" と一緒に setMode() か Tokenizer のコンストラクタ に渡します。長さのペナルティをなくすと、decompose モードでも長い語が分割されずに残ります:

const { Tokenizer, TokenizerBuilder, loadDictionary } = require("lindera");

const text = "関西国際空港限定トートバッグ";
const penalty = { kanjiPenaltyLengthPenalty: 0, otherPenaltyLengthPenalty: 0 };
const surfaces = (tokenizer) => tokenizer.tokenize(text).map((token) => token.surface);

const builder = new TokenizerBuilder().setDictionary("embedded://ipadic");
console.log(surfaces(builder.setMode("decompose").build()));
// [ '関西', '国際', '空港', '限定', 'トートバッグ' ]

console.log(surfaces(builder.setMode("decompose", penalty).build()));
// [ '関西国際空港', '限定', 'トートバッグ' ]

const tokenizer = new Tokenizer(loadDictionary("embedded://ipadic"), "decompose", null, null, penalty);
console.log(surfaces(tokenizer));
// [ '関西国際空港', '限定', 'トートバッグ' ]

"normal" モードと一緒にペナルティを渡すとエラーを投げます。