sudachiの分割モードを複数使ってword2vecの精度を上げる
tl;dr
日本語でword2vecするには、学習用コーパスの分かち書きに大きく依存するけど、sudachiを使って複数の分割粒度を同時に使って学習したらいい感じになるよ。っていう論文があったので、pythonでやってみた。
論文紹介
「複数粒度の分割結果に基づく日本語単語分散表現」
http://www.anlp.jp/proceedings/annual_meeting/2019/pdf_dir/P8-5.pdf
ワークスアプリケーションズと国立国語研究所の共同研究です。
提案手法
固有表現のように長い語への分割と、その中に含まれるより短い分割を学習コーパスとして同時に用いることで、固有表現と複合語を幅広くカバーしようというものです。 具体的には、ワークスアプリケーションズが開発している形態素解析器「sudachi」の3つの分割モード(分割単位)を使って、それぞれの分割単位で分かち書きしたも結果をひとつの学習コーパスとします。
複数の分割単位で分けたあとに一つのコーパスとすることで、単語-文脈語のペアが複数分割される部分のみバリエーションを持てるので単語の長さを超えて従来よりも単語観の類似性を捉えた分散表現ができるとのことです。
(論文より引用)
sudachiの分割モード
sudachiはテキストの分割粒度としてA,B,Cの3つのモードが指定できます。
それぞれ以下のような単位になります。
sudachi mode | 単位 | 分割例 |
---|---|---|
A | 短単位 | 選挙/管理/委員/会 |
B | 中単位 | 選挙/管理/委員会 |
C | 長単位 | 選挙管理委員会 |
sudachiの準備
pythonでsudachiを使う場合は、SudachiPyを使うと便利です。
※SudachiPyはまだ開発中ですので、利用は自己責任でおねがいします。
SudachiPy インストール
SudachiPyはPyPIに登録されていないのでgitからインストールします。
pip install -e git+git://github.com/WorksApplications/SudachiPy@develop#egg=SudachiPy
辞書のインストール
SudachiPyのリポジトリには辞書が含まれていないため、辞書をダウンロードします。
https://github.com/WorksApplications/SudachiDict
辞書はcoreとfullの2種類あります。 coreは基本的な語彙を収録したもので、fullは雑多な固有名詞まで収録したものです。目的によって使用するものを選んでください。とりあえず使いたい場合はcoreを使うといいと思います。
辞書をダウンロードしたら解凍後、system.dicに名称を変更してSudachiPy/resources/
以下に置いてください。
unzip sudachi-x.y.z-dictionary-core.zip mv xxxx.dic system.dic
pip lsit | grep sudachipy
ファイル移動
sudachipy -m A -a
で起動できたらOK
word2vecの準備
gensimのインストール
pip install gensim
学習データ
今回はここのデータを使います。
1000記事程度の本文をテキストデータとして保存します。
コード
学習データを作成
import csv import json from sudachipy import tokenizer, dictionary, config with open("articles.txt", "r") as f: texts = [l.strip() for l in f] # 設定の読み込み with open(config.SETTINGFILE, "r", encoding="utf-8") as f: settings = json.load(f) # tokenizerの辞書設定 tokenizer_obj = dictionary.Dictionary(settings).create() # 各単位のtokenizer作成 modeA = tokenizer.Tokenizer.SplitMode.A # 短単位 modeB = tokenizer.Tokenizer.SplitMode.B # 中単位 modeC = tokenizer.Tokenizer.SplitMode.C # 長単位 # 使う品詞かどうかチェックする def is_stop(token): allow_list = ["名詞", "動詞", "形容詞"] pos = token.part_of_speech() if pos[0] in allow_list: return False return True # わかち書きの作成 def generate_wakati(texts, mode): split_mode = modeA if mode == "A": slpit_mode = modeA elif mode == "B": split_mode = modeB elif mode == "C": split_mode = modeC else: print("error") return [ [ m.surface() for m in tokenizer_obj.tokenize(split_mode, t) if not is_stop(m) ] for t in texts ] wakati_A = generate_wakati(texts, "A") wakati_B = generate_wakati(texts, "B") wakati_C = generate_wakati(texts, "C") wakati_ABC = wakati_A + wakati_B + wakati_C
テキストを3単位でそれぞれわかち書きした結果を単純に足し合わせます。 わかち書き時に今回は名詞, 動詞, 形容詞だけを使いました。
word2vecで学習
from gensim.models import word2vec model = word2vec.Word2Vec(wakati_ABC, size=300, window=8, min_count=3, negative=5, iter=15, workers=4)
word2vecはgensimのword2vecを使います。 メソッドを呼び出してさっき作成したモデルを使うだけです。 学習時のパラメータは論文で使われていたパラメータをそのまま使いました。
結果
model.most_similar(positive="三菱UFJ") > 'UFJ', 0.8853795528411865 '三菱', 0.8742923736572266 'UFJ銀行', 0.8564153909683228 'スタンレー', 0.7913519144058228 '傘下', 0.7682371139526367 '三井', 0.7614598274230957 'みずほフィナンシャルグループ', 0.7539564371109009 '商事', 0.7529445886611938 'ソフトバンク', 0.7498173713684082 '三菱UFJフィナンシャル・グループ', 0.7467410564422607
model.most_similar(positive="自民") > '自民党', 0.8419219255447388, '公明', 0.7904272675514221, '県議会', 0.7845382690429688, '分裂', 0.7752047181129456, '選挙戦', 0.7693485617637634, '与野党', 0.7693350911140442, '支持層', 0.7673546075820923, '島根', 0.7669612169265747, '戦い', 0.7654584050178528, '県知事選', 0.760123074054718
model.most_similar(positive="アメリカ") > 'オブ', 0.8347927331924438 'エンタープライズ', 0.8062992691993713 'ブライアン', 0.7762320041656494 'モイニハン', 0.7745112180709839 'フェロー', 0.7728168964385986 '本拠', 0.7711036205291748 'ロサンゼルス', 0.769803524017334 'アメリカン', 0.7447160482406616 'クーパー', 0.7413920164108276 'サックス', 0.7402298450469971
そもそもデータ量が少ないので微妙な感じになっていますし、比較をしてないのでなんともいえないですが、 複数粒度に分割できる用語はそれぞれのトークンが似た単語として出てくるのでいい感じなのではという気がします。 当初はこの結果を使って検索エンジンの類義語辞書を生成できないかなと思っていましたが、その用途ではそもそもelasticsearchのsudachi tokenizerを使えば済みそう...
次はmecabや中単位など1単位のわかち書きでやった結果と比較したい思います。
おまけ
model.most_similar(positive="令和") > '新元号', 0.7717349529266357 '元号', 0.7474104762077332 '国書', 0.7238500118255615 '出典', 0.7032668590545654 '典拠', 0.7001941800117493 '万葉集', 0.6888912916183472 '万葉', 0.6675821542739868 '引用', 0.665723443031311 '広至', 0.6630961894989014 '考案', 0.6610066890716553