📑

[自然言語処理] NEologdを自前で改造して使っている話

2022/10/28に公開約6,300字5件のコメント

https://github.com/neologd/mecab-ipadic-neologd

NEologdは新語や固有名詞に強い形態素解析用辞書として有名ですが、2020年の9月を最後に更新がありません。実用上の問題は大きく分けて2つあります。

  1. 新語・固有名詞が供給されない
  2. 不具合が修正されない

1つ目も重要なのですが、今回フォーカスするのは2つ目の点です。

私は個人開発でazooKeyという日本語入力アプリケーションを開発しています。かな漢字変換用辞書の生成の目的でNEologdを使っているのですが、この用途では品詞と読みの正確性がクリティカルになってきます。しかしNEologdの辞書では「ヤバい」が固有名詞になっていたり、「光GENJI」に「イチ」と読みがついていたりします。これは回り回ってかな漢字変換の性能に影響を与えるので、可能な限り修正したい不具合です。

しかし、更新がない以上、自前でどうにかするしかありません。

不具合への対処

当初この手の問題は実行時に動的に適用する独自フォーマットのパッチファイルを用意し、誤りに気付くたびにこれを更新して解決していました。動的に適用、というのは形態素解析を実行したあとでフィルターの条件を満たすノードに修正をかけるということです。

実際のファイルの一部です。最初の「CHANGE」が「修正せよ」という命令を表し、続く「word:光GENJI ruby:イチ」が修正対象のAND条件を表します。続く「ruby:ヒカルゲンジ」が修正内容です。

CHANGE	word:光GENJI ruby:イチ	ruby:ヒカルゲンジ
CHANGE	word:クラシック競走 ruby:クラシック	ruby:クラシックキョウソウ
CHANGE	word:アーケード街 ruby:アーケード	ruby:アーケードガイ
CHANGE	word:爆売れ ruby:ウレ	ruby:バクウレ
CHANGE	word:ダートマス大学 ruby:ダートマス	ruby:ダートマスダイガク
CHANGE	word:LGBT ruby:エルジイビイティイ	ruby:エルジービーティー

当初はこのように比較的シンプルな修正が多かったため、手動でパッチのエントリを追加していけば事足りました。

ヨネン・ヨンネン問題

しかし使っていくにつれより多くの不具合が見つかります。例えば「4年」が「ヨンネン」と読まれる不具合です。

CHANGE	word:4年 ruby:ヨンネン	ruby:ヨネン
CHANGE	word:4年ぶり ruby:ヨンネンブリ	ruby:ヨネンブリ
CHANGE	word:14年 ruby:ジュウヨンネン	ruby:ジュウヨネン
CHANGE	word:14年ぶり ruby:ジュウヨンネンブリ	ruby:ジュウヨネンブリ
CHANGE	word:24年 ruby:ニジュウヨンネン	ruby:ニジュウヨネン
CHANGE	word:24年ぶり ruby:ニジュウヨンネンブリ	ruby:ニジュウヨネンブリ
CHANGE	word:34年 ruby:サンジュウヨンネン	ruby:サンジュウヨネン
CHANGE	word:34年ぶり ruby:サンジュウヨンネンブリ	ruby:サンジュウヨネンブリ
CHANGE	word:44年 ruby:ヨンジュウヨンネン	ruby:ヨンジュウヨネン
CHANGE	word:44年ぶり ruby:ヨンジュウヨンネンブリ	ruby:ヨンジュウヨネンブリ
……
CHANGE	word:1824年 ruby:センハッピャクニジュウヨンネン	ruby:センハッピャクニジュウヨネン
CHANGE	word:94年ぶり ruby:キュウジュウヨンネンブリ	ruby:キュウジュウヨネンブリ

「4年」「14年」……「1824年」まで「ヨンネン」と読まれてしまうので、パッチファイル方式では180ほどのエントリを追加しなければいけません。また「4年」の派生語にも同様の不具合があり、「4年ぶり」から「94年ぶり」まで間違っています。さらに、実は「4年前」でも同様の問題が発生するのですが、手動でチェックしているうちは気づきませんでした。

実行時にパッチを動的適用するのはパフォーマンスの面でも悪手です。エントリが少ないうちは良かったのですが、数量表現の修正が増えてくるとこのパッチの適用が律速となりました。テキスト解析にかかる時間が数倍単位で変わるので、動的なパッチの適用をうまく実装する工夫が必要になります。パッチファイル方式はこのような点で辛くなります。

すごい・すっごい・すげえ・すっげえ問題

別の問題として、「すっごい」「すげえ」「すっげえ」というような形容詞の口語形(?)を正しく解析できない問題がありました。先ほどの例はルビのみが問題でしたが、こちらの場合分かち書きなどでも問題が発生します。動的なパッチでこれに対応するのは非常に困難です。

ユーザ辞書を用いれば「すげえ」そのものを追加することは容易です。また、形容詞に対して口語形を生成することも可能です。そこで全ての形容詞に対して「すげえ」のようなエントリを生成してユーザ辞書を作れば、一応は「すげえ」「高え」「やべえ」「汚ねえ」の解析が可能になります。

しかし、全ての形容詞に対して「すげえ」のようなエントリを生成しようとすると、形態素解析の辞書に入っている全ての形容詞のエントリを舐める必要があります。そうしてできたユーザ辞書はそこそこ大きなファイルで、パフォーマンスの観点では最適ではありません。

どうせ全てのエントリを舐めるなら、もうシステム辞書そのものを置き換えても大差ないのではないでしょうか。

ということで、不具合を修正するため、ついにNEologdの辞書を改造することにしました

辞書の改造

まずは元データであるcsvファイルを入手する必要があります[1]。ipadicのcsvは以下で入手します。

https://github.com/taku910/mecab/tree/master/mecab-ipadic

このファイルはeuc-jpなので、まずはutf8に書き換える必要があります。

find ./ -name "*.csv" | xargs nkf --overwrite --oc=UTF-8

それから、NEologdのcsvファイルを以下で入手します。

https://github.com/neologd/mecab-ipadic-neologd/tree/master/seed

seed配下のデータはipadicのデータとの差分のみなので、エントリの重複はありません。また、デフォルト状態のNEologdはファイルのうち一部のみを使っています。必要であればoffのファイルも処理対象に入れましょう。

on:
    mecab-user-dict-seed.20200910.csv
    neologd-adjective-std-dict-seed.20151126.csv
    neologd-adjective-verb-dict-seed.20160324.csv
    neologd-adverb-dict-seed.20150623.csv
    neologd-common-noun-ortho-variant-dict-seed.20170228.csv
    neologd-ill-formed-words-dict-seed.20170127.csv
    neologd-interjection-dict-seed.20170216.csv
    neologd-noun-sahen-conn-ortho-variant-dict-seed.20160323.csv
    neologd-proper-noun-ortho-variant-dict-seed.20161110.csv
off:
    neologd-adjective-exp-dict-seed.20151126.csv
    neologd-date-time-infreq-dict-seed.20190415.csv
    neologd-quantity-infreq-dict-seed.20190415.csv

流れとしては、

  1. ipadicのcsvファイルとNEologdのcsvファイルそれぞれを1行1行チェックして修正を加えたcsvを出力する
  2. 出力したcsvファイルから辞書ファイル(sys.dic)を書き出す
  3. sys.dicを適切なディレクトリに移動して辞書ディレクトリを作る

となります。

フィルターの適用

1つ目の手順は適当な言語でスクリプトを書いて好きなようにフィルターを掛けてください。私はPythonで以下のような処理を行うスクリプトを書きました。

  • 形容詞に対して次の形を生成する

    • 2文字目に「っ」を挿入して強調する形式 (すごい→すっごい, 明るい→あっかるい)
    • 口語で語尾を伸ばす形式 (すごい→すげえ, 明るい→明りい)
    • 上の2つを組み合わせた形式 (すごい→すっげえ, 明るい→あっかりい)
  • 単語に「4年」を含み、かつ読みに「ヨンネン」を含む単語において、読みの「ヨンネン」を「ヨネン」に置換する

  • 単語がひらがな・カタカナのみからなる場合、読みを単語をカタカナ化したもので置換する (読みの修正)

  • これまで用意していたパッチのうち、他の機械的処理で修正できないものを適用する (例)

    CHANGE	word:微分積分学 ruby:ビブンセキブン	ruby:ビブンセキブンガク
    CHANGE	word:光GENJI ruby:イチ	ruby:ヒカルゲンジ
    CHANGE	word:讃美歌 ruby:イチ	ruby:サンビカ
    CHANGE	word:夢女子 ruby:ユメコ	ruby:ユメジョシ
    CHANGE	word:明倫養賢堂 ruby:ヨウケンドウ	ruby:メイリンヨウケンドウ
    

sys.dicの生成

2つ目の手順ではsys.dicを生成します。フィルターを行った結果を1つにまとめた辞書ファイルout.csvがあることにします。

mecab-ipadicの配下で以下を実行してsys.dicを作ります。混ざらないようにするため、out.csv以外のcsvファイルはよけてください。

/usr/local/Cellar/mecab/0.996/libexec/mecab/mecab-dict-index -f utf8 -t utf8

辞書ディレクトリの作成

3つ目の手順ではsys.dicを適切な辞書ディレクトリに移動します。まずはNEologdがどこにあるのか確認します。私の環境(macOS)では以下のような配置になっていました。

- usr
 - local
  - lib
   - mecab
    - dic 
     - ipadic
     - mecab-ipadic-neologd

そこでdic/配下に新たにmy-neologdを作ります。中身は一旦mecab-ipadic-neologdのコピペにしました。

- usr
 - local
  - lib
   - mecab
    - dic 
     - ipadic
     - mecab-ipadic-neologd
     - my-neologd

それから、先ほど生成したsys.dicをこのmy-neologdに突っ込みます。これで新しい辞書ディレクトリが準備できました。必要な場合はdicrcも書き換えてください。

使ってみる

最後に、新しく作った辞書を使ってみましょう。先ほどの辞書のディレクトリを-dオプションで指定してmecabを実行します。うまく行っていれば変更が反映されています。

mecab -d /usr/local/lib/mecab/dic/my-ipadic

以上で、完全なカスタム辞書が準備できました。今後は実行時のパフォーマンスに影響を与えることなく、形態素解析用の辞書をカスタマイズすることができます。

辞書の評価

ある程度のミスを許せば、csvファイルを走査することで簡易的な評価を行うことができます。例えば「単語の側に現れる仮名の集合から、ルビに現れる仮名の集合を引いたものが、空集合でない」という条件でエントリをフィルターすると、読みが間違っている可能性のあるエントリを一覧することができます。

この方法でipadicとNEologdで以下のようなエラーが見つかりました。

CHANGE	word:役どころ ruby:ヤクドトロ	ruby:ヤクドコロ
CHANGE	word:身体のどこ ruby:シンタイ	ruby:シンタイノドコ
CHANGE	word:河原ぶろ ruby:カワラ	ruby:カワラブロ
CHANGE	word:肉はさみ ruby:ニク	ruby:ニクハサミ
CHANGE	word:軽焼きせんべい ruby:カルヤキ	ruby:カルヤキセンベイ
CHANGE	word:萌え〜 ruby:モエナントカ	ruby:モエ〜

この手の評価をいくつか実行していくことで解析を多少改善することができました。

課題

最初に「新語・固有名詞が供給されない」という問題を指摘しましたが、こちらは不具合の修正以上に悩ましい問題になっています。2020年9月以降の新語がほとんど入っていないので、「ちいかわ」とか「オミクロン株」とか「夏油」とか全部ダメです。

また、新語・固有名詞だけではなく、一般的な語彙であっても欠けていることはしばしばです。「鬱」「混色」「諫言」「拒食」「咥える」「失くす」「茶葉」などはデフォルト状態で欠けています。

現状、気付いた範囲で手動で新語や固有名詞を集めたり、Webテキストから頻度の高い複合語を抽出したりして自動でsys.dicに統合するようにしています。ただ、本家が更新されない以上、なかなか本質的な解決にはなりません。

NEologdの更新停止に伴い、日本中で同様の困難が起きているんじゃないかと思っているんですが、皆さん、どうやって解決されているんでしょうか……。「うちはこうしてる」というのがあれば、ぜひ教えていただきたいです。

脚注
  1. 辞書ファイルであるsys.dicを逆コンパイルすればcsvと同じデータが入っているのではないかと思いますが、逆コンパイルのツールは見つけられませんでした。今回はcsvが手に入るのでこちらを使います。 ↩︎

Discussion

「新語・固有名詞が供給されない」については、
以下が廃止されたからじゃないかなと・・・

https://developer.hatena.ne.jp/ja/documents/keyword/

メインの情報資源が消失したことで、
更新するモチベが消失してしまったんじゃないかって想像してみる

https://github.com/neologd/mecab-ipadic-neologd/blob/master/README.ja.md#利点

※だからって、何の問題解決にもなってないですけど・・・

はてなキーワードの廃止、惜しいです……

https://github.com/WorksApplications/SudachiDict

そのままensan様の用途に使えるかはわからないのですが、WorkApplicationsさんが主導して開発している形態素解析器sudachiに使われている辞書を使ってみるのはいかがでしょうか。

現在進行形で意欲的に開発されているため、辞書も頻繁に更新されているようです。
辞書自体の確認はできていないのですが、手元でsudachiを使って「オミクロン株」を解析したところ、一つの単語として解析ができました。
ただ、一方で、コンテンツには強くないようで「ちいかわ」や「夏油」は残念ながら1単語と認識はできませんでしたね。

また、本辞書を使うのであればMeCabの代わりにSudachiを使ってみるというのもありかもしれません。
https://github.com/WorksApplications/sudachi.rs

ありがとうございます!

SudachiDictは素晴らしいプロジェクトで、とても魅力的な選択肢だと思います。更新が頻繁なのがありがたいです。

ただ、ipadicとSudachiDictは品詞体系が違います。ipadicの品詞体系に依拠したコードが多く、これをこれからSudachiDictの品詞体系に移行させるのは残念ながら難しい状況です。SudachiDictの品詞体系からipadic品詞体系に変換するという手は以前少しだけ検討したのですが、まだSudachiDict側の品詞体系への理解が浅く、現実的に可能か自信が持てていません……。

返信、ありがとうございます。
確かに、sudachiの品詞体系はunidicでしたね…。
頭からすっぽりと抜け落ちていました。

unidicが短単位でとても細かいことを考えると、unidicをipadicに対応させて変換するのは、単純なルールで変換するのは難しいかもしれませんね…。

ログインするとコメントできます