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, penalty: nil)
トークナイズモードを設定します。
"normal"-- 標準的なトークナイズ(デフォルト)"decompose"-- 複合語をより小さな単位に分解
キーワード引数 penalty: には、"decompose" モードが使う Lindera::Penalty を渡します。nil(デフォルト)では既定のペナルティを使います。"normal" とペナルティを一緒に渡すと、不正なモードと同じく RuntimeError が、Lindera::Penalty でない値を渡すと TypeError が発生します。
builder.set_mode('normal')
builder.set_mode('decompose', penalty: Lindera::Penalty.new(nil, 0, nil, nil))
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.csv')
set_keep_whitespace(keep)
出力に空白トークンを含めるかどうかを制御します。残した空白はラティスにも残るため、他のトークンも、MeCab と同様に空白をラティス上で読み飛ばすデフォルトの出力と変わることがあります。空白を出力せずにラティスにだけ残すには、代わりに set_skip_whitespace(false) を使います。
builder.set_keep_whitespace(true)
set_skip_whitespace(skip)
空白をラティス上で読み飛ばすかどうかを設定します。skip の意味は設定ファイルの segmenter.skip_whitespace と同じです:
true-- 空白を読み飛ばし、MeCab と同様に空白の前後の語を直接接続します。false-- 空白を独立したノード(空白の見出し語かSPACEの未知語)としてラティスに残します(v7 より前の動作)。空白は引き続き出力から除外されます。nil-- 辞書のmetadata.jsonのデフォルトです。SudachiDict 以外では読み飛ばします。以前の呼び出しを取り消すときに使います。
ここで設定した値は、辞書のデフォルトと、TokenizerBuilder.from_file で読み込んだ skip_whitespace より優先されます。空白トークンを残すとき(set_keep_whitespace(true))は効果がありません。残した空白は常にラティスに残るためです。それ以外の値は、set_keep_whitespace と同じく Ruby の真偽値の規則で扱われ、false と nil 以外は読み飛ばしを有効にします。
たとえば IPADIC では、Google が 新しい の が は、デフォルトでは助詞(助詞)、set_skip_whitespace(false) では接続詞(接続詞)と解析されます。詳しくは Segmenter を参照してください。
# v7 より前と同じく、空白をラティスに残す
builder.set_skip_whitespace(false)
set_space_penalty(value)
韓国語向けの左側空白ペナルティ(left-space penalty)を設定します。直前に空白がある候補のうち、本来は前の語に空白なしで付く品詞(助詞、語尾など)の候補にコストを加算します。value の意味は設定ファイルの segmenter.space_penalty と同じです:
nil-- デフォルト。辞書がmetadata.jsonに同梱するルールがあればそれを使います(ko-dic は mecab-ko-dic のルールを同梱し、他の同梱辞書はルールを持ちません)。以前の呼び出しを取り消すときにも使います。false-- ペナルティをオフにします。true-- 辞書のルールを要求します。ルールを同梱しない辞書ではbuildがRuntimeErrorを発生させます。- 文字列キーのハッシュ
{ 'rules' => [{ 'pos' => [...], 'cost' => n }, ...] }-- 辞書のルールの代わりにこのルールを適用します。先頭品詞タグがposに含まれる候補にcostを加算し、最初に一致したルールが使われます。
これらの形式に当てはまらない値(1、'false'、{ 'rules' => 'JKS' } など)では RuntimeError が発生します。変換できない値では TypeError が発生します。該当するのは Symbol(ハッシュのキーや要素に含まれる場合も含むため、%i[JKS] ではなく %w[JKS] と書きます)、有限でない Float(NaN や Infinity)、64 ビットの範囲を超える Integer、および nil・true・false・Integer・Float・String・Array・Hash 以外のオブジェクトです。
# ペナルティをオフにする
builder.set_space_penalty(false)
# 明示的なルール
builder.set_space_penalty({
'rules' => [
{ 'pos' => %w[EC EF EP ETM ETN VCP XSA XSN XSV], 'cost' => 3000 },
{ 'pos' => %w[JC JKB JKC JKG JKO JKQ JKS JKV JX], 'cost' => 6000 }
]
})
[!NOTE] v6.1.0 から、ko-dic では mecab-ko と同じく左側空白ペナルティがデフォルトで適用されます。たとえば
검색 이 잘 된다の이は感動詞ICと解析されます(ペナルティがなければ主格助詞JKS)。オフにするにはbuilder.set_space_penalty(false)を呼び出してください。Lindera::Tokenizer.new(dictionary, ...)で直接作成したトークナイザーは常にデフォルトを使うため、オフにするにはTokenizerBuilderを使ってください。ルールを同梱しているのは Lindera 6.1.0 以降でビルドした ko-dic だけです。v6.0.0 リリースの ko-dic ではペナルティはオフのままで、辞書を再ビルドするまでtrueはエラーになります。詳しくは Segmenter を参照してください。
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)
set_space_penalty、append_character_filter、append_token_filter の引数は、配列と hash の入れ子が 128 段までです。それより深い値や、自分自身を含む値を渡すと ArgumentError が発生します。
ビルド
build
設定された内容で Tokenizer をビルドして返します。
tokenizer = builder.build
Tokenizer
Lindera::Tokenizer はテキストに対して形態素解析を行います。
Tokenizer の作成
Lindera::Tokenizer.new(dictionary, mode = nil, user_dictionary = nil, space_penalty: nil, penalty: nil)
読み込み済みの辞書から直接トークナイザーを作成します。mode のデフォルトは 'normal' で、user_dictionary は省略できます。
キーワード引数 space_penalty: には TokenizerBuilder#set_space_penalty と同じ値を渡せます。nil(デフォルト)では辞書が同梱する左側空白ペナルティのルールを使うため、ko-dic ではペナルティがオンになります。オフにするには false を渡します。不正な設定では、set_space_penalty や build と同じエラーが発生します。
キーワード引数 penalty: には、TokenizerBuilder#set_mode と同じく "decompose" モードの Lindera::Penalty を渡せます。エラーも set_mode と同じです。
require 'lindera'
dictionary = Lindera.load_dictionary('embedded://ipadic')
tokenizer = Lindera::Tokenizer.new(dictionary)
# 左側空白ペナルティなしの ko-dic
ko_tokenizer = Lindera::Tokenizer.new(Lindera.load_dictionary('embedded://ko-dic'), space_penalty: false)
ユーザー辞書を使用する場合:
dictionary = Lindera.load_dictionary('embedded://ipadic')
user_dict = Lindera.load_user_dictionary_for('/path/to/user_dictionary.csv', dictionary)
tokenizer = Lindera::Tokenizer.new(dictionary, 'normal', user_dict)
Tokenizer メソッド
tokenize(text)
入力テキストをトークナイズし、Token オブジェクトの配列を返します。
tokens = tokenizer.tokenize('形態素解析')
パラメータ:
| 名前 | 型 | 説明 |
|---|---|---|
text | String | トークナイズするテキスト |
戻り値: Array<Token>
tokenize_surfaces(text)
入力テキストをトークナイズし、トークンの surface のみを文字列の配列として返します。分かち書き用途の高速パスです。Token オブジェクトを生成せず、形態素の詳細情報もロードしないため、surface 文字列だけが必要な場合は tokenize より大幅に高速です。結果は tokenizer.tokenize(text).map(&:surface) と一致します。
surfaces = tokenizer.tokenize_surfaces('形態素解析')
# ["形態素", "解析"]
パラメータ:
| 名前 | 型 | 説明 |
|---|---|---|
text | String | トークナイズするテキスト |
戻り値: 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
パラメータ:
| 名前 | 型 | 説明 |
|---|---|---|
text | String | トークナイズするテキスト |
n | Integer | 返す結果の数 |
unique | Boolean | 結果の重複を排除(false で無効) |
cost_threshold | Integer または nil | 最良パスからの最大コスト差(nil で無制限) |
戻り値: Array<[Array<Token>, Integer]>
Mode
Lindera::Mode はトークナイズモードを表します。モードの確認・比較のためのスタンドアロンヘルパーとして提供されています。TokenizerBuilder#set_mode と Tokenizer.new は、Mode インスタンスではなく単純なモード文字列("normal" または "decompose")を受け取ります。decompose モードのペナルティは、それぞれのキーワード引数 penalty: で渡します(下記の Penalty を参照してください)。
Mode の作成
Lindera::Mode.new(mode_str)
Mode を作成します。引数は必須ですが nil を渡すこともできます。"normal" / "Normal"(mode_str が nil の場合に使用)または "decompose" / "Decompose" を受け付けます。それ以外の値を渡すと ArgumentError が発生します。
require 'lindera'
mode = Lindera::Mode.new('normal')
mode = Lindera::Mode.new('decompose')
mode = Lindera::Mode.new(nil) # デフォルトの "normal" になる
Mode メソッド
| メソッド | 戻り値 | 説明 |
|---|---|---|
to_s | String | "normal" または "decompose" |
name | String | to_s と同じ |
inspect | String | 例: "#<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_threshold | Integer | 2 | ペナルティが適用される漢字のみの表層形の長さのしきい値 |
kanji_penalty_length_penalty | Integer | 3000 | しきい値を超える漢字のみの表層形に加算されるコストペナルティ |
other_penalty_length_threshold | Integer | 7 | 漢字のみでない表層形にペナルティが適用される長さのしきい値 |
other_penalty_length_penalty | Integer | 1700 | しきい値を超える漢字のみでない表層形に加算されるコストペナルティ |
penalty = Lindera::Penalty.new(nil, nil, nil, nil)
penalty.kanji_penalty_length_threshold # 2
Penalty の使い方
Penalty は "decompose" と一緒に、TokenizerBuilder#set_mode か Tokenizer.new のキーワード引数 penalty: で渡します。長さのペナルティをなくすと、decompose モードでも長い語が分割されずに残ります。
require 'lindera'
text = '関西国際空港限定トートバッグ'
penalty = Lindera::Penalty.new(nil, 0, nil, 0)
builder = Lindera::TokenizerBuilder.new
builder.set_dictionary('embedded://ipadic')
builder.set_mode('decompose')
p builder.build.tokenize(text).map(&:surface)
# => ["関西", "国際", "空港", "限定", "トートバッグ"]
builder.set_mode('decompose', penalty: penalty)
p builder.build.tokenize(text).map(&:surface)
# => ["関西国際空港", "限定", "トートバッグ"]
dictionary = Lindera.load_dictionary('embedded://ipadic')
tokenizer = Lindera::Tokenizer.new(dictionary, 'decompose', penalty: penalty)
p tokenizer.tokenize(text).map(&:surface)
# => ["関西国際空港", "限定", "トートバッグ"]
"normal" モードと一緒に Penalty を渡すと RuntimeError が発生します。
Token
Token は単一の形態素トークンを表します。
プロパティ
| プロパティ | 型 | 説明 |
|---|---|---|
surface | String | トークンの表層形 |
byte_start | Integer | 元テキストでの開始バイト位置 |
byte_end | Integer | 元テキストでの終了バイト位置 |
position | Integer | トークンの位置インデックス |
word_id | Integer | 辞書の単語 ID |
details | Array<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) # 例: "トウキョウ"
パラメータ:
| 名前 | 型 | 説明 |
|---|---|---|
index | Integer | details 配列へのゼロベースインデックス |
戻り値: String または nil
to_h / to_hash
トークンを Symbol をキーとするプレーンな Hash として返します。各フィールドは
Ruby の自然な型を保つため、独自のエンコーダなしでシリアライズできます。
to_h と to_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_schema と Metadata#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フィールド(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 = Lindera::Schema.create_default
Schema メソッド
| メソッド | 戻り値 | 説明 |
|---|---|---|
fields | Array<String> | すべてのフィールド名(順序どおり) |
get_all_fields | Array<String> | fields と同じ |
field_count | Integer | フィールドの総数 |
get_field_index(name) | Integer または nil | name という名前のフィールドのインデックス |
get_field_name(index) | String または nil | index にあるフィールド名 |
get_custom_fields | Array<String> | 固定の4フィールド(surface、left_context_id、right_context_id、cost)以降のフィールド名 |
get_field_by_name(name) | FieldDefinition または nil | name の完全なフィールド定義 |
validate_record(record) | nil | record がスキーマと一致しない場合 ArgumentError を発生 |
to_s | String | 例: "Schema(fields=13)" |
inspect | String | フィールドの全リスト |
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::FieldDefinition は Schema 内の単一フィールドを表します。インスタンスは Schema#get_field_by_name からのみ取得でき、公開コンストラクタはありません(Lindera::FieldDefinition.new は TypeError を発生させます)。
FieldDefinition プロパティ
| プロパティ | 型 | 説明 |
|---|---|---|
index | Integer | スキーマ内でのフィールドの位置(ゼロベース) |
name | String | フィールド名 |
field_type | FieldType | フィールドタイプ |
description | String または 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 と同様、インスタンスは Schema(FieldDefinition#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"