日本語自然言語処理(NLP)学習 GiNZA/spaCy(2)~ユーザー辞書の利用

技術記事

GiNZA/spaCyでユーザ辞書を使って自然言語処理をする際の手順や留意点を示します。
ユーザー辞書を用いることで、分析対象領域のみで利用されている語句でも認識することができるようになります。

Ginzaの標準辞書について

GiNZAはワークス徳島人工知能NLP研究所が開発している形態素解析器Sudachiを用いて日本語処理をしています。そのため、標準辞書はSudachiで利用されているSudachi辞書が用いられています。Sudachi辞書は3種類(Full,Medium,Small)のサイズがあり、用途により使い分けられます。
また、これらは数か月おきに最新語句を取り入れるなどの更新が行われています。
そのため、この標準辞書を更新するだけで、ある程度は新しい語句でも分析することができます。

Sudachi辞書について詳しく説明されている外部サイトの記事がありましたので、参考として以下にリンクを示します。

形態素解析器Sudachiの「辞書」はどのように作られているか
https://zenn.dev/sorami/articles/c9a506000fd1fbd1cf98

ただし、上記の標準辞書には、特殊な領域の語句やローカルな語句は標準辞書には含まれることはまずありません。そのような語句を分析したい場合には、それらを含めたユーザ辞書を作成して追加する必要があります。

ユーザー辞書について

ユーザ辞書とは、利用者独自の語句を追加したい場合に用いる辞書です。
上に示したシステム辞書(Sudachi辞書)に加えて、ユーザ辞書を追加して、日本語を解析することができます。
そのため、専門的な用語、独自の慣用語句、最新語句などを解析したい場合に用います。

ユーザ辞書についての公式ドキュメントは以下となります。
https://github.com/WorksApplications/Sudachi/blob/develop/docs/user_dict.md

上記の公式ドキュメントから抜粋して、ユーザ辞書中の各語句に設定できる属性を以下に示します。なお、表中のNoについては、公式ドキュメントに合わせて、ゼロスタートで記載しています。
また、特に留意すべきと思われる部分を赤字で説明を追記しています。

No項目名 項目内容の説明
0見出し(TRIE用)形態素解析に使用される見出し表記。
留意点としては、Sudachiによる正規化後の文字列を記載すること。
例えば、アルファベットは小文字に正規化されるので、小文字で登録する必要がある。
1左連接ID形態素解析の連接判定(左連接)に使用されるID。
2右連接ID形態素解析の連接判定(右連接)に使用されるID。
3コスト形態素解析に使用される見出し表記のコスト値。 “-32767 ~ 32767” までの整数値で指定できる。 値を小さくするほど、登録した見出し表記が解析結果として出やすくなる。 
もし、この語句を解析結果として必ず残すようにしたい場合には、-32767を指定する。
4見出し(解析結果表示用)「0 見出し (TRIE 用)」と同じものを指定
5品詞1品詞を記述。
6品詞2品詞を記述。
7品詞3品詞を記述。
8品詞4品詞を記述。
9品詞(活用型)品詞(活用型)を記述。
10品詞(活用形)品詞(活用形)を記述。
11読み見出し表記の読みを記述します。 全角カタカナで記述
12正規化表記見出し表記の正規化表記を記述
13辞書形ID活用のある語に対して、その語の辞書形(終止形表記)を指定するための情報
14分割タイプ語の分割単位タイプ (A / B / C) を記述
15A単位分割情報分割単位タイプ B または C の語について、A単位に分割するための情報
16B単位分割情報分割単位タイプ C の語について、B単位に分割するための情報
17※未使用このフィールドは未使用です。 “*”(半角アスタリスク)を記入

ユーザ辞書の追加方法

ユーザ辞書を追加する手順の全体の流れは以下となります。

以下の記事で環境を作成している前提で記載します。

手順1:ユーザ辞書(CSVファイル)を作成

サンプルとして、「ローマの休日」という語句を登録するためのCSVデータを以下に用意しました。
第2~4項目の左連接ID、右連接ID、コストは公式ドキュメントの推奨値に合わせています。
もし、できるだけこの語句を分離せず全体を解析結果に残したい場合は、第4項目のコストを「5000」ではなく、「-32768」にします。

ローマの休日,4786,4786,5000,ローマの休日,名詞,固有名詞,一般,*,*,*,ローマノキュウジツ,ローマの休日,*,*,*,*,*

手順2:コマンド実行により、ユーザ辞書(dicファイル)を作成

手順1で作成したCSVファイルから、ユーザ辞書(dicファイル)を以下のコマンドで作成します。sudachipyというコマンドは、GINZAをインストールした際に同時にインストールされています。

コマンド内容は下記です。
sudachipy ubuild -s [システム辞書のパス] [手順1で作成したcsvファイルのパス]

本環境では、以下のコマンド内容となります。

(ginzatest) D:\mywork>sudachipy ubuild  -s ./ginzatest/Lib/site-packages/sudachidict_core/resources/system.dic ./userdic.csv

上記コマンドを実行後、カレントフォルダに「user.dic」というファイルが作成されています。

手順3:ユーザ辞書(dicファイル)を特定のフォルダに格納

分かりやすいように、手順2で指定したシステム辞書と同じフォルダ内に置きます。
要件により、ユーザーのお好みの場所に置いてもOKです。ただし手順4の設定内容もそれに合わせる必要があります。

(ginzatest) D:\mywork> copy user.dic D:\mywork\ginzatest\Lib\site-packages\sudachidict_core\resources

手順4:sudachiの設定ファイルにユーザ辞書(dicファイル)を追加

sudachiの設定ファイルは、「sudachi.json」という名前です。
本環境の場合、フルパスで記載すると、下記となります。
D:\mywork\ginzatest\Lib\site-packages\sudachipy\resources\sudachi.json

上記ファイルを編集して、下記のように編集します。

{
    "systemDict" : null,
    "characterDefinitionFile" : "char.def",
    "userDict" : ["../../sudachidict_core/resources/user.dic"], # この行を追加します。
    "inputTextPlugin" : [
~ 以下は省略 ~

ユーザ辞書の効果確認

ユーザ辞書を追加していない場合と、追加した場合の分析の変化を以下に示します。
前章でユーザ辞書として登録した語句を含む文章「私はローマの休日を観ました」を用いて、ユーザ辞書登録前後でどのように解析結果が変化するかを確認します。

実行するPythonスクリプトは以下です。

import spacy
nlp = spacy.load('ja_ginza')
doc = nlp('私はローマの休日を観ました。')

for sent in doc.sents:
    for token in sent:
        print(
            token.i,
            token.orth_,
            token.lemma_,
            token.norm_,
            token.morph.get("Reading"),
            token.pos_,
            token.morph.get("Inflection"),
            token.tag_,
            token.dep_,
            token.head.i,
        )
    print('EOS')

ユーザ辞書を追加していない場合の解析結果

(ginzatest) D:\mywork>python ginzatest2.py
0 私 私 私 ['ワタクシ'] PRON [] 代名詞 nsubj 6
1 は は は ['ハ'] ADP [] 助詞-係助詞 case 0
2 ローマ ローマ ローマ ['ローマ'] PROPN [] 名詞-固有名詞-地名-一般 nmod 4
3 の の の ['ノ'] ADP [] 助詞-格助詞 case 2
4 休日 休日 休日 ['キュウジツ'] NOUN [] 名詞-普通名詞-副詞可能 obj 6
5 を を を ['ヲ'] ADP [] 助詞-格助詞 case 4
6 観 観る 見る ['ミ'] VERB ['上一段-マ行;連用形-一般'] 動詞-非自立可能 ROOT 6
7 まし ます ます ['マシ'] AUX ['助動詞-マス;連用形-一般'] 助動詞 aux 6
8 た た た ['タ'] AUX ['助動詞-タ;終止形-一般'] 助動詞 aux 6
9 。 。 。 ['。'] PUNCT [] 補助記号-句点 punct 6
EOS

ユーザ辞書を追加した場合の解析結果

狙い通り、「ローマの休日」という語句が固有名詞として解析されていることに注目です。

(ginzatest) D:\mywork>python ginzatest2.py
0 私 私 私 ['ワタクシ'] PRON [] 代名詞 nsubj 4
1 は は は ['ハ'] ADP [] 助詞-係助詞 case 0
2 ローマの休日 ローマの休日 ローマの休日 ['ローマノキュウジツ'] ADV [] 名詞-固有名詞-一般 obj 4
3 を を を ['ヲ'] ADP [] 助詞-格助詞 case 2
4 観 観る 見る ['ミ'] VERB ['上一段-マ行;連用形-一般'] 動詞-非自立可能 ROOT 4
5 まし ます ます ['マシ'] AUX ['助動詞-マス;連用形-一般'] 助動詞 aux 4
6 た た た ['タ'] AUX ['助動詞-タ;終止形-一般'] 助動詞 aux 4
7 。 。 。 ['。'] PUNCT [] 補助記号-句点 punct 4
EOS

おまけ:ユーザ辞書の正規化表記を利用した語句分類

本来の用途ではないのですが、ユーザ辞書の項目「正規化表記」は「ユーザ辞書の語句が属するグループ名」として利用することができます。
これにより、ユーザ辞書を分類用マスタとして扱って、簡易的なテキスト分類を行うことができます。

たとえば、小説のタイトルだけを含んだいろんな文章があり、そのそれぞれの文章がどの小説家の作品なのかを分析したい場合、各小説名について以下のようなユーザ辞書を作成します。
正規化表記の項目に「小説家名:<小説家の名前>」がセットされています。

人間失格,4786,4786,-32767,人間失格,名詞,固有名詞,一般,*,*,*,ニンゲンシッカク,小説家名:太宰治,*,*,*,*,*
吾輩は猫である,4786,4786,-32767,吾輩は猫である,名詞,固有名詞,一般,*,*,*,ワガハイハネコデアル,小説家名:夏目漱石,*,*,*,*,*

その後、そのユーザ辞書を用いて文章を解析し、正規化表記内容を表示すると、その小説の作者の情報を得ることができます。

下記のように、先ほどのPythonスクリプトに正規化表記出力を追加します。

import spacy
nlp = spacy.load('ja_ginza')
doc = nlp('私は人間失格を読みました。')

for sent in doc.sents:
    for token in sent:
        print(
            token.i,
            token.orth_,
            token.lemma_,
            token.norm_,
            token.morph.get("Reading"),
            token.pos_,
            token.morph.get("Inflection"),
            token.tag_,
            token.dep_,
            token.head.i,
            token.norm_ # 正規化表記を追加
        )
    print('EOS')

結果は、以下となります。
「token.norm_」でユーザ辞書の正規化表記として指定した「小説家名:太宰治」が取得されています。

0 私 私 私 ['ワタクシ'] PRON [] 代名詞 nsubj 4
1 は は は ['ハ'] ADP [] 助詞-係助詞 case 0
2 人間失格 人間失格 小説家名:太宰治 ['ニンゲンシッカク'] NOUN [] 名詞-固有名詞-一般 obj 4
3 を を を ['ヲ'] ADP [] 助詞-格助詞 case 2
4 読み 読む 読む ['ヨミ'] VERB ['五段-マ行;連用形-一般'] 動詞-一般 ROOT 4
5 まし ます ます ['マシ'] AUX ['助動詞-マス;連用形-一般'] 助動詞 aux 4
6 た た た ['タ'] AUX ['助動詞-タ;終止形-一般'] 助動詞 aux 4
7 。 。 。 ['。'] PUNCT [] 補助記号-句点 punct 4
EOS

コメント