フィルタ
文字フィルタ(Character Filter)とトークンフィルタ(Token Filter)は、lindera-analysisのTokenizerパイプラインにおける前処理・後処理の2つの段階です。
- 文字フィルタはセグメンテーションの前に入力テキストを変換します。バイトオフセットは自動的に補正されるため、変換後のテキストに対して生成されたトークンでも、元のフィルタ前のテキストにおける位置が正しく報告されます。
- トークンフィルタはSegmenterが生成したトークンのリストをセグメンテーションの後に変換します。
どちらのフィルタも設定方法は共通で、kind文字列(CLIの--character-filter / --token-filterフラグでもkind:{"json": "args"}という形式で使用されます)と、フィルタ固有のパラメータを持つJSONのargsオブジェクトで構成されます。
文字フィルタ
文字フィルタはYAML設定ファイルのcharacter_filtersキーで設定します。各エントリは順番に適用され、あるフィルタの出力が次のフィルタの入力になります。
unicode_normalize
4種類の標準的なUnicode正規化形式のいずれかを使って入力テキストを正規化します。
パラメータ:
| パラメータ | 型 | 必須 | 説明 |
|---|---|---|---|
kind | string | はい | nfc、nfd、nfkc、nfkdのいずれか |
例:
{
"kind": "unicode_normalize",
"args": {
"kind": "nfkc"
}
}
japanese_iteration_mark
日本語の踊り字(繰り返し記号)である々、ゝ、ゞ、ヽ、ヾを、それぞれが繰り返す文字に置き換えて正規化します。ひらがな・カタカナの繰り返し記号については、必要に応じて濁点の付与・除去も行います。
パラメータ:
| パラメータ | 型 | 必須 | デフォルト | 説明 |
|---|---|---|---|---|
normalize_kanji | bool | いいえ | false | 漢字の踊り字々を正規化する |
normalize_kana | bool | いいえ | false | ひらがな・カタカナの踊り字ゝ、ゞ、ヽ、ヾを正規化する |
例:
{
"kind": "japanese_iteration_mark",
"args": {
"normalize_kanji": true,
"normalize_kana": true
}
}
mapping(文字フィルタ)
mappingのキーに一致する部分を対応する値に置き換えます。入力テキスト全体に対して、Aho-Corasickオートマトンによる最長一致検索を行います。
パラメータ:
| パラメータ | 型 | 必須 | 説明 |
|---|---|---|---|
mapping | object(string to string) | はい | 置換対象の部分文字列と、その置換先の対応表 |
例:
{
"kind": "mapping",
"args": {
"mapping": {
"リンデラ": "Lindera"
}
}
}
regex
正規表現にマッチした箇所をすべて、リテラルな置換文字列で置き換えます。キャプチャグループの内容は置換文字列に展開されません。
パラメータ:
| パラメータ | 型 | 必須 | 説明 |
|---|---|---|---|
pattern | string | はい | 正規表現(regexクレートの構文) |
replacement | string | はい | 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つの複合語トークンに結合します。
結合後のトークンの詳細には、辞書スキーマから解決した品詞の位置に new_tag が入ります。それより前の品詞以外のフィールド(SudachiDict の display_surface)と残りのフィールドはすべて * になります。
パラメータ:
| パラメータ | 型 | 必須 | 説明 |
|---|---|---|---|
tags | array<string> | はい | 結合対象となるトークンを示す品詞タグ(カンマ区切りで最大4階層) |
new_tag | string | いいえ | 結合後のトークンに付与する品詞タグ。省略した場合は複合語が付与される |
例:
{
"kind": "japanese_compound_word",
"args": {
"tags": [
"名詞,数",
"名詞,接尾,助数詞"
],
"new_tag": "名詞,数"
}
}
japanese_kana
トークンテキストをひらがなとカタカナの間で相互変換します。
パラメータ:
| パラメータ | 型 | 必須 | 説明 |
|---|---|---|---|
kind | string | はい | "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 つ(辞書スキーマで part_of_speech という名前のフィールドから始まる 4 つ。スキーマにそのフィールドが無い場合は先頭の詳細から)と完全一致で比較されます。IPADIC の助詞トークンは必ず 助詞,係助詞 のようにサブカテゴリを持つため、助詞 単独では一致しません。一方、助動詞はサブカテゴリを持たないため(助動詞,*,*,*)、助動詞 単独で一致します。この動作は japanese_stop_tags、japanese_compound_word、および japanese_number の tags も同様です。位置はスキーマ経由で解決されるため、SudachiDict の display_surface のように品詞の前に列があるスキーマでも同じ規則で一致します。
パラメータ:
| パラメータ | 型 | 必須 | 説明 |
|---|---|---|---|
tags | array<string> | はい | 保持する品詞タグ(カンマ区切りで最大4階層) |
例:
{
"kind": "japanese_keep_tags",
"args": {
"tags": [
"名詞,一般"
]
}
}
japanese_number
トークンの表層テキストに含まれる日本語の数値表現(漢数字、大字、全角数字)をアラビア数字に変換します。
トークンは、その全ての文字が数詞である場合にのみ変換されます(Lucene の JapaneseNumberFilter と同じ規則)。そのため、数詞で始まるだけの通常の語は変換されません。一部は1部にならず一部のままです。
japanese_compound_wordの後段で使う場合は、名詞,数のトークンだけを結合してください。助数詞(名詞,接尾,助数詞)を数値トークンに結合すると、そのトークンは数詞ではなくなるため、一万円は変換されずそのまま残ります。
パラメータ:
| パラメータ | 型 | 必須 | 説明 |
|---|---|---|---|
tags | array<string>またはnull | いいえ | 変換対象を限定する品詞タグ(カンマ区切りで最大4階層)。japanese_keep_tags と同じ規則で一致判定される。省略またはnullの場合はすべてのトークンが変換対象になる |
例:
{
"kind": "japanese_number",
"args": {
"tags": [
"名詞,数"
]
}
}
japanese_reading_form
トークンの表層テキストを、辞書のreadingフィールドに登録された読み(カタカナ)に置き換えます。未知語処理によって生成されたトークン(token.word_id.is_unknown())は変更されません。
このフィルタに設定パラメータはありません。
例:
{
"kind": "japanese_reading_form"
}
japanese_stop_tags
品詞タグがtagsのいずれかに一致するトークンを除去します。
パラメータ:
| パラメータ | 型 | 必須 | 説明 |
|---|---|---|---|
tags | array<string> | はい | 除去する品詞タグ(カンマ区切りで最大4階層) |
例:
{
"kind": "japanese_stop_tags",
"args": {
"tags": [
"助詞,格助詞,一般",
"助詞,係助詞",
"助詞,連体化",
"助動詞"
]
}
}
keep_words
表層テキストがwordsのいずれかに完全一致するトークンのみを保持します。
パラメータ:
| パラメータ | 型 | 必須 | 説明 |
|---|---|---|---|
words | array<string> | はい | 保持する表層形の一覧 |
例:
{
"kind": "keep_words",
"args": {
"words": [
"すもも",
"もも"
]
}
}
korean_compound_word
最初の品詞タグがtagsのいずれかに一致する連続した韓国語トークンを、1つの複合語トークンに結合します。結合後のトークンは先頭トークンのbyte_startとposition、末尾トークンのbyte_endを持ち、position_lengthは結合したトークン数になります。隣接トークンが一致しない単独のトークンはそのままです。
ko-dicは漢数詞を形態素単位に分割するため、이/NR 천/NR 이/NR 십/NR 육/NRをkorean_numberで変換する前に1トークンへ戻すのがこのフィルタの役割です。
| 入力 | ko-dicのトークン | korean_compound_word適用後(tags: ["SN", "NR"]、new_tag: "NR") | korean_number適用後 |
|---|---|---|---|
이천이십육년 | 이/NR 천/NR 이/NR 십/NR 육/NR 년/NNBC | 이천이십육/NR 년/NNBC | 2026 년 |
10만 | 10/SN 만/NR | 10만/NR | 100000 |
2천26 | 2/SN 천/NR 26/SN | 2천26/NR | 2026 |
パラメータ:
| パラメータ | 型 | 必須 | 説明 |
|---|---|---|---|
tags | array<string> | はい | 結合対象となるトークンを示す品詞タグ(NRやSNなどko-dicの単一タグ) |
new_tag | string | いいえ | 結合後のトークンに付与する品詞タグ。他のdetailは*になる。省略した場合は복합어が付与されるが、これはko-dicのタグではないため、後続のkorean_numberやkorean_keep_tagsに結合後のトークンを渡す場合はNRなどのタグを指定する |
例:
{
"kind": "korean_compound_word",
"args": {
"tags": [
"SN",
"NR"
],
"new_tag": "NR"
}
}
既定の設定では、ko-dicは만 원や십만 원の만を数詞NRと判定するため、십만 원は십만/NR 원/NNBCになります。空白の読み飛ばしを無効にする(skip_whitespace: false)と、만の後ろの語は空白のノードに接続するため、ko-dicはそこでの만を助詞JXと判定し(만/JX 원/NNG)、["SN", "NR"]では結合されません。
korean_decompound
ko-dicの複合語・活用形・事前解析済み(Preanalysis)トークンを、expressionフィールドが示す形態素に分割します。Lucene noriのDecompoundModeと同じ動作で、隣接トークンを結合するkorean_compound_wordの逆操作にあたります。LinderaのMode::Decomposeは日本語の漢字向けに調整された長さペナルティであり、これらのフィールドを読まないため、このフィルタなしでは무궁화はどのモードでも分割されません。
| 入力 | ko-dicのトークン | mode: "discard" | mode: "mixed" |
|---|---|---|---|
무궁화꽃이 | 무궁화/NNG 꽃/NNG 이/JKS | 무궁 화 꽃 이 | 무궁화 무궁 화 꽃 이 |
가곡역 | 가곡역/NNP | 가곡 역 | 가곡역 가곡 역 |
断片のオフセットはnoriに合わせています。自身のバイト範囲を持つのはCompoundの断片だけで、各断片は自身が表すテキストの範囲を持ちます。InflectとPreanalysisの断片はトークン全体の範囲を共有します。活用形の断片は元の表層形の部分文字列とは限らないためです(갔は가 + 았に分解されます)。
Compoundトークンを分割するのは、断片をつなげると表層形とちょうど一致する場合だけで、それ以外は範囲を共有します。これには、断片が表層形そのものの文字列ではない同梱ko-dicの12項目(안지랑역 = 안지 + 역、고춧값 = 고추 + 값)が含まれます。これらではnoriのオフセットが一部の断片を別の文字列に向けてしまいます。프린터3D = 프린터 + 쓰리디のようなユーザー辞書の項目や、先行フィルタが表層形を書き換えた場合も同様です。同梱辞書のそれ以外の項目ではオフセットはnoriと一致し、どのオフセットでテキストを切り出しても安全です。
discardでは断片が複合語自身の位置から連番で並び、mixedでは複合語がその位置にposition_lengthを断片数として残り、断片が同じ位置から続きます。展開後のトークンは増えた位置の分だけ後ろにずれ、それ以外はずれないため、korean_stop_tagsなど先行フィルタが残した位置の隙間は保たれます。
展開するのは位置を1つだけ占めるトークンに限るため、mixedが残した複合語が再び分割されることはありません。それでもフィルタはチェーンに一度だけ入れてください。mixedが残した断片1つの項目は位置を1つしか占めないためです。korean_compound_wordと組み合わせる場合は結合を先に置いてください。結合はバイト範囲の重なるトークンを繋がないため逆順でも壊れませんが、結合を先にするとセグメンターが出したトークンそのものを対象にできます。
断片は自身の品詞タグと意味分類を持ち、複合語自体を説明するフィールド(type、expression、first_part_of_speech、last_part_of_speech、reading、presence_absence)は空になります。読みを空にするのはnoriと同じ挙動で、noriのDecompoundToken.getReading()はnullを返します。korean_reading_formは*をスキップするため、断片は自身の表層形を保ちます。
スキーマにtype/expressionフィールドがない辞書に適用した場合、フィルタは設定されたフィルタごとに一度だけ警告を出し、すべてのトークンをそのまま通過させます。
パラメータ:
| パラメータ | 型 | 必須 | 説明 |
|---|---|---|---|
mode | string | いいえ | "discard"(デフォルト、noriと同じ)は複合語を断片で置き換え、"mixed"は複合語を残したうえで断片も出力します |
types | array<string> | いいえ | 分解対象のtype値(大文字と小文字を区別)。デフォルトは["Compound", "Inflect", "Preanalysis"]。空のリストやそれ以外の値はエラーになります |
例:
{
"kind": "korean_decompound",
"args": {
"mode": "discard",
"types": [
"Compound",
"Inflect",
"Preanalysis"
]
}
}
korean_keep_tags
最初の品詞タグがtagsのいずれかに一致する韓国語トークンのみを保持します。
パラメータ:
| パラメータ | 型 | 必須 | 説明 |
|---|---|---|---|
tags | array<string> | はい | 保持する品詞タグ |
例:
{
"kind": "korean_keep_tags",
"args": {
"tags": [
"NNG"
]
}
}
korean_number
トークンの表層テキストに含まれる漢数詞由来の韓国語の数値表現(ハングル数字、漢数字、全角数字)をアラビア数字に変換します。固有語数詞(하나、둘、열、스물など)は位取りの構造を持たないため変換しません。
変換はトークンごとに独立して行われます。ko-dicは漢数詞を形態素単位に分割するため、1つの数を構成するトークン群が1つのアラビア数字にまとまるわけではありません。
| 入力 | ko-dicのトークン | korean_number適用後 |
|---|---|---|
이천이십육년 | 이/NR 천/NR 이/NR 십/NR 육/NR 년/NNBC | 2 1000 2 10 6 년 |
10만 | 10/SN 만/NR | 10 10000 |
IPADICに対するjapanese_compound_wordのように、変換前にトークンを結合するにはkorean_compound_wordをこのフィルタの前に置きます。結合後のトークンを変換対象にするため、new_tagにはこのフィルタのtagsに含まれるタグを指定します。
[
{"kind": "korean_compound_word", "args": {"tags": ["SN", "NR"], "new_tag": "NR"}},
{"kind": "korean_number", "args": {"tags": ["SN", "NR"]}}
]
このチェーンで이천이십육년は2026 년、10만は100000、2천26は2026になります。漢字表記の二千二十六/SHのように数値全体が1トークンの場合は、このフィルタ単体でそのまま変換されます。
パラメータ:
| パラメータ | 型 | 必須 | 説明 |
|---|---|---|---|
tags | array<string>またはnull | いいえ | 変換対象を限定する品詞タグ。省略した場合は["SN", "NR"]、nullの場合はすべてのトークンが変換対象になる |
トークンは、その全ての文字が数詞である場合にのみ変換されます(Lucene の KoreanNumberFilter と同じ規則)。これにより固有語数詞が保護されます。일곱(7)は漢数詞の일で始まるため、1文字ずつ変換すると1곱になってしまいます。
ハングルの数詞は他の形態素とも同形で(이は主格助詞、만は補助詞)、これらは1文字なので全文字一致の規則では区別できません。そのため既定では数詞の品詞タグに限定しています。
漢数字も変換する場合はSHを追加してください。対応は2つの意味で部分的です。ko-dicは漢字語全体をNNGと判定するため參加や萬歲は変換されず、一方で単語に結び付かない単独の漢字はSHとなるため、十字架は10 字 架、百貨店は100 貨 店になります。
例:
{
"kind": "korean_number",
"args": {
"tags": [
"SN",
"NR"
]
}
}
korean_reading_form
トークンの表層テキストを、辞書のreadingフィールドに登録された読みに置き換えます。未知語処理によって生成されたトークン(token.word_id.is_unknown())は変更されません。
このフィルタに設定パラメータはありません。
例:
{
"kind": "korean_reading_form"
}
korean_stop_tags
最初の品詞タグがtagsのいずれかに一致する韓国語トークンを除去します。
パラメータ:
| パラメータ | 型 | 必須 | 説明 |
|---|---|---|---|
tags | array<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のトークン版に相当します。
パラメータ:
| パラメータ | 型 | 必須 | 説明 |
|---|---|---|---|
mapping | object(string to string) | はい | 置換対象の部分文字列と、その置換先の対応表 |
例:
{
"kind": "mapping",
"args": {
"mapping": {
"籠": "篭"
}
}
}
remove_diacritical_mark
トークンの表層テキストからダイアクリティカルマーク(発音区別符号)を除去し、その後テキストの元のUnicode正規化形式を再適用します。
パラメータ:
| パラメータ | 型 | 必須 | デフォルト | 説明 |
|---|---|---|---|---|
japanese | bool | いいえ | false | 日本語の濁点・半濁点の結合文字(分解済みの濁音・半濁音仮名に含まれるものなど)も除去する |
例:
{
"kind": "remove_diacritical_mark",
"args": {
"japanese": false
}
}
stop_words
表層テキストがwordsのいずれかに完全一致するトークンを除去します。
パラメータ:
| パラメータ | 型 | 必須 | 説明 |
|---|---|---|---|
words | array<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_filterとappend_token_filterメソッドは、フィルタを追加した順番で登録します。文字フィルタはセグメンテーション前のテキストに対して順次適用され、トークンフィルタはセグメンテーション後のトークンリストに対して順次適用されます。