subword-nmtで語彙を作成

自然言語関連の処理の際、文字を形態素解析で分解し機械学習など実施すると思う。有名な形態素解析ツールはMecabやjumanかと思うが、膨大なテキストデータを形態素解析で分解し、ユニークな単語一覧を作成すると膨大な単語数のvocabができてしまう。当然テキストデータが膨大であれば、色々な言い回しや、低頻度の単語までが含まれてしまう。例えばwikipediaの日本語記事から形態素解析による単語一覧を作成すると100,000語を超える。

機械学習(BERTなど)のインプットレイヤーの次元数が単語数に等しくなるというのはリソース面でも精度面でもよろしくないようだ。多くの事例では30,000~50,000語程度のvocabとして学習させるようだ。

頻度による語彙分解

では膨大なテキストデータの単語数を30,000~50,000語程度に抑えるにはどうすれば良いのか。有名な手法として以下がある。
sentencepiece
https://github.com/google/sentencepiece

subword-nmt
https://github.com/rsennrich/subword-nmt

sentencepieceではテキストから分解する語彙を全体の出現頻度でもって決定し、語彙設定数に分解するようだ。一方subword-nmtは全てのテキストを一旦形態素解析した上で、各語彙をさらに分解して共通パーツを作り全体の語彙数を低減させるようだ。

sentencepieceは手軽だが、日本語や中国語の場合は「character_coverage」を0.9995にするように推奨されている。つまり0.05%の言葉は失われる。これはほとんど使われない超マイナーな漢字まで1単語として含まれてしまうと多くの語彙がほぼ使われない文字で占められてしまうからだ。
BERTなどの場合、これでも問題ないと思われるがテキスト生成などの場合はUNKOWNな語彙を生成してしまいかねない。
また、sentencepieceでは語彙の分解が意味的に不自然なものになりがちだ。
例)
「歩きっぱなしで疲れたぁ」→「歩きっぱな し で疲れたぁ」

sentencepieceによる最適な分類であることは承知だが、このような分解は問題があると考える。
pre-training時に使用したvocabはfine-tunning時にも同一のものを使用することが一般的で、意味的に不自然な語彙分解を今後も使用し続けることになるのは良くないのではと思う。

subword-nmtの方が良いか?

この記事では「subword-nmt」を紹介する。subword-nmtでは以下のように語彙を作成する。(例)
「歩きっぱなしで疲れたぁ」→(mecab)→「歩き っぱなし で 疲れ た ぁ」→(subword-nmt & 文字列置換)→「歩 ##き っぱ ##なし で 疲れ た ぁ」

これで「歩き」以外の「○き」という言葉で「#き」という語彙を共有でき、語彙数をグッと減らすことができる。
sentencepieceと比べて、形態素解析による分解を経ているため、品詞の体系や意味を保持しやすいと考える。またfine-tunning時に使用するvocabとしても汎用性がsentencepieceより高いと思われる。

sentencepieceの方が良いという考えもあると思うが、上記間違っている場合は教えてください。

subword-nmtのPythonでの実装

基本はgithubの通り行えば良い。

インストール

pip install subword-nmt
ALLTXT=形態素解析済みのテキストファイルパス
BPETXT=書き出し先パス
subword-nmt learn-bpe -s 2000 < ${ALLTXT} > codes.txt
subword-nmt apply-bpe -c codes.txt < ${ALLTXT} > ${BPETXT}

これで書き出されたテキストをユニークにして書き出せばvocab.txtが作成できる。

語彙数が適切な数になるように2000という部分は調整する必要がある。