日本語Wikipediaのデータで言語モデル(KenLM, RNNLM)を学習させる手順メモ
日本語Wikipediaのデータで言語モデル(KenLM, RNNLM)を学習させる手順のメモです。
kenlm | GitHub
https://github.com/kpu/kenlm
Faster RNNLM (HS/NCE) toolkit | GitHub
https://github.com/yandex/faster-rnnlm
動作確認環境は、次のとおり。
- Ubuntu Linux 20.04 / WSL2
- Python 3.10.11
Wikipediaデータの準備
日本語WikipediaのデータとWikiExtractorをダウンロードし、展開します.
mkdir -p data/wikipedia && cd $_
wget https://dumps.wikimedia.org/jawiki/latest/jawiki-latest-pages-articles.xml.bz2
wget https://raw.githubusercontent.com/apertium/WikiExtractor/master/WikiExtractor.py
python WikiExtractor.py --infn jawiki-latest-pages-articles.xml.bz2
cd ../..
data/wikipedia
配下に、wiki.txt
が出来ていることを確認します.
入手したテキストデータを、文字単位に分かち書きしたファイルに変換します。
ここでは、次のスクリプトで変換処理を行います。
prepare_wikidata.py
import sys
def scan_wikitext(wikitext_path):
def print_entry(counter, title, body):
for ln in body.split('\n'):
if len(ln) > 0 and ln.endswith("。"):
ln = " ".join(ln.strip())
print(ln)
print(f"{counter + 1}: {entry_title} processed", file=sys.stderr)
with open(wikitext_path) as f:
entry_counter = 0
entry_title = None
entry_body = ''
for ln in f:
if len(ln) == 1:
if entry_title is None:
continue
print_entry(entry_counter, entry_title, entry_body)
entry_counter += 1
entry_title = None
elif entry_title is None:
entry_title = ln.strip()[:-1]
entry_body = ''
else:
entry_body += ln
if entry_title is not None:
print_entry(entry_counter, entry_title, entry_body)
if __name__ == '__main__':
if len(sys.argv) == 0:
print("usage: python prepare_wikidata.py [path of wiki.txt]")
scan_wikitext(sys.argv[1])
スクリプトは次のように実行します。
python prepare_wikidata.py data/wikipedia/wiki.txt > data/wikipedia/wiki_character_split.txt
data/wikipedia
配下に、wiki_character_split.txt
が出来ていることを確認します.
KenLMでの学習と予測
次に、KenLMで言語モデルの学習と予測を行います。
kenlmのビルド
まずは、KenLMをビルドします。 次のREADMEのとおり実施します。
kenlm | GitHub
https://github.com/kpu/kenlm
以下のコマンドで依存Packageをインストールします。
このコマンドはDebian/Ubuntu向けです、他の環境は公式のREADMEを参照してください。
sudo apt install build-essential cmake libboost-system-dev libboost-thread-dev libboost-program-options-dev libboost-test-dev libeigen3-dev zlib1g-dev libbz2-dev liblzma-dev
ソースコードをcloneし、ビルドします。
git clone git@github.com:kpu/kenlm.git
cd kenlm
mkdir -p build
cd build
cmake ..
make -j 4
次のコマンドを実行して、ヘルプが出力されていればビルドは完了です。
bin/lmplz --help
bin/build_binary --help
cd ../../
モデルの学習
入力データをシャッフルします。
shuf data/wikipedia/wiki_character_split.txt > wiki_shuffled.txt
モデルを学習させます。
mkdir -p models/wikipedia/kenlm
kenlm/build/bin/lmplz --order 4 --discount_fallback < wiki_shuffled.txt > models/wikipedia/kenlm/kenlm_model.arpa
kenlm/build/bin/build_binary models/wikipedia/kenlm/kenlm_model.arpa models/wikipedia/kenlm/kenlm_model.bin
予測(perplexityの出力)
モデルを使って、テキストのperplexityを出力します。
ここでは、次のスクリプトで予測処理を行います。
predict_kenlm.py
import kenlm
import os
MODEL_BIN='models/wikipedia/kenlm/kenlm_model.bin'
if __name__ == '__main__':
if not os.path.exists(MODEL_BIN):
raise Exception("model file not found: {}".format(MODEL_BIN))
# モデルのロード
model = kenlm.LanguageModel(MODEL_BIN)
# 予測
for txt in [
"脱字が存在する文章です。",
"脱字が存在する文章す。",
]:
sentence = " ".join(txt.strip())
prob = model.score(sentence, bos=True, eos=True)
perplexity = model.perplexity(sentence)
print(perplexity, prob, txt)
cnt = 0
for prob, _, _ in model.full_scores(sentence):
chara = sentence.split(' ')[cnt] if cnt < len(txt) else ''
print(" ", prob, chara)
cnt += 1
kenlmのpythonライブラリをインストールします。
pip install https://github.com/kpu/kenlm/archive/master.zip
予測処理を実行して、Perplexityを出力します。
python predict_kenlm.py
以下のような出力になり、誤った文の方がPerplexityが高くなっていることが見えます。
43.4621701753186 -21.295448303222656 脱字が存在する文章です。
-4.088757514953613 脱
-3.4269466400146484 字
-0.9862440228462219 が
-2.4829885959625244 存
-0.0017122072167694569 在
-0.27959761023521423 す
-0.001823164988309145 る
-3.210365056991577 文
-1.1511752605438232 章
-0.8413480520248413 で
-2.7702455520629883 す
-1.0101044178009033 。
-1.0441397428512573
96.10513676993703 -23.792959213256836 脱字が存在する文章す。
-4.088757514953613 脱
-3.4269466400146484 字
-0.9862440228462219 が
-2.4829885959625244 存
-0.0017122072167694569 在
-0.27959761023521423 す
-0.001823164988309145 る
-3.210365056991577 文
-1.1511752605438232 章
-3.458188056945801 す
-2.787339448928833 。
-1.9178202152252197
RNNLMでの学習と予測
次に、RNNLMで言語モデルの学習と予測を行います。
Faster RNNLMのビルド
まずは、Faster RNNLMをビルドします。
次のREADMEのとおり実施します。
Faster RNNLM (HS/NCE) toolkit | GitHub
https://github.com/yandex/faster-rnnlm
ソースコードをcloneします。
git clone git@github.com:yandex/faster-rnnlm.git
cd faster-rnnlm
eigen3をダウンロードします。
(build.shにまかせるとエラーになるため、手動でダウンロードしています)
wget https://gitlab.com/libeigen/eigen/-/archive/3.2.10/eigen-3.2.10.tar.gz
tar zxf eigen-3.2.10.tar.gz
mv eigen-3.2.10 eigen3
ビルドします。
./build.sh
次のコマンドを実行して、ヘルプが出力されていればビルドは完了です。
faster-rnnlm/rnnlm
cd ..
モデルの学習
入力データをシャッフルし、教師データと検証データに分割します。
shuf data/wikipedia/wiki_character_split.txt > wiki_shuffled.txt
split -n l/1/5 wiki_shuffled.txt > wiki_train.txt
split -n l/2/5 wiki_shuffled.txt >> wiki_train.txt
split -n l/3/5 wiki_shuffled.txt >> wiki_train.txt
split -n l/4/5 wiki_shuffled.txt >> wiki_train.txt
split -n l/5/5 wiki_shuffled.txt > wiki_valid.txt
モデルを学習させます。
mkdir -p models/wikipedia/rnnlm
faster-rnnlm/faster-rnnlm/rnnlm -train wiki_train.txt -valid wiki_valid.txt -rnnlm models/wikipedia/rnnlm/rnnlm_model -hidden 160 -rand-seed 1 -debug 1 -bptt 4 -bptt-block 10
予測(対数確率・エントロピーの出力)
モデルを使って、テキストのperplexityを出力します。
対象の文章が書かれたテキストファイルを用意します。
test1.txt
脱 字 が 存 在 す る 文 章 で す 。
test2.txt
脱 字 が 存 在 す る 文 章 す 。
以下のコマンドで予測を実行します。
faster-rnnlm/faster-rnnlm/rnnlm -rnnlm models/wikipedia/rnnlm/rnnlm_model -test test1.txt
faster-rnnlm/faster-rnnlm/rnnlm -rnnlm models/wikipedia/rnnlm/rnnlm_model -test test2.txt
以下のような出力になり、正しい文の方が対数確率が高くなっていることが見えます。
$ faster-rnnlm/faster-rnnlm/rnnlm -rnnlm models/wikipedia/rnnlm/rnnlm_model -test test1.txt
Read the vocabulary: 16093 words
Restoring existing nnet
Constructing RNN: layer_size=160, layer_type=sigmoid, layer_count=1, maxent_hash_size=0, maxent_order=0, vocab_size=16093, use_nce=0
Contructed HS: arity=2, height=31
-24.181795
Test entropy 6.179245
$ faster-rnnlm/faster-rnnlm/rnnlm -rnnlm models/wikipedia/rnnlm/rnnlm_model -test test2.txt
Read the vocabulary: 16093 words
Restoring existing nnet
Constructing RNN: layer_size=160, layer_type=sigmoid, layer_count=1, maxent_hash_size=0, maxent_order=0, vocab_size=16093, use_nce=0
Contructed HS: arity=2, height=31
-24.415205
Test entropy 6.758796
参考サイト
以下の記事を参考にさせていただきました。
はじめての自然言語処理 第10回 QuartzNet による音声認識の検証 | オブジェクトの広場
https://www.ogis-ri.co.jp/otc/hiroba/technical/similar-document-search/part10.html
以上。