自然言語処理向けの日本語のデータって思ったよりも少ない気がします。 特に研究用途でのみ許可されているコーパスが多く、趣味で試したい時にどのデータを使うかで結構時間を使ってしまいます。
「日本語ウェブコーパス 2010 」はテキストデータに関しては研究目的の利用に限られていますが、N-gramコーパスは利用条件について「特にありません.二次配布も自由です.」と書かれています。
N-gramコーパス
データは形態素単位と文字単位があります。
形態素は Google のngram コーパス、GSK2007-C Web日本語Nグラム第1版 にもとづいているとのこと。
Google Japan Blog: 大規模日本語 n-gram データの公開 の下記引用部分を読む限り、Mecabによる分割と考えてよさそうです。
単語分割: 日本語は英語のようにスペースで単語が分割されていません。そこで、工藤が開発に携わっているオープンソースの形態素解析エンジンMeCabを使用して単語に分割しました。
filelistを活用しつつ頻度1000以上のデータを展開すると以下のようになりました。charに文字単位、wordに形態素単位のngramが入っています。
├── char │ └── over999 │ ├── 1gms │ │ └── 1gm-0000.xz │ ├── 2gms │ │ └── 2gm-0000.xz │ ├── 3gms │ │ └── 3gm-0000.xz │ ├── 4gms │ │ ├── 4gm-0000.xz │ │ └── 4gm-0001.xz │ ├── 5gms │ │ ├── 5gm-0000.xz │ │ └── 5gm-0001.xz │ ├── 6gms │ │ └── 6gm-0000.xz │ ├── 7gms │ │ └── 7gm-0000.xz │ └── filelist └── word └── over999 ├── 1gms │ └── 1gm-0000.xz ├── 2gms │ └── 2gm-0000.xz ├── 3gms │ └── 3gm-0000.xz ├── 4gms │ └── 4gm-0000.xz ├── 5gms │ └── 5gm-0000.xz ├── 6gms │ └── 6gm-0000.xz ├── 7gms │ └── 7gm-0000.xz └── filelist
pythonでxzファイルの中身を確認する
中身を見てみます。
xzファイルなのでpythonの場合 lzma
を使って読み込みます。試しに100行出力するコードを書いてみました。
(Python3.8で実行を確認しています)
import lzma import sys from itertools import islice def print_xz_ngram_file(file_path): with lzma.open(file_path, "rt") as f: for line in islice(f.readlines(), 100): print(line.strip()) if __name__ == '__main__': argv = sys.argv[1:] if argv: print_xz_ngram_file(argv[0])
3gramデータ ./word/over999/3gms
を指定して実行すると以下のようになりました。
空白区切りで単語、そのあとタブ区切りで頻度という構成です。 一番上の行だと「"」「"」「東京」の並びは20,194回という意味になります。
" " 東京 20194 " ( 9 1101 " ( ソーシャルブックマーク 27598 " ( ダブル 7507 " - レビュー 1119 " 0 " 4284 " 0 m 2247 " 1 " 3569 " 2 " 1472 " 2 ちゃん 2192 " 3 " 1124 ………
ちなみに、頻度1000以上でも十分大きいファイルなので、読み込みに時間がかかります。
頻繁にアクセスするのであればデータベースに保存したほうが良さそうです。
ngramを使って文生成してみる
ngramの出現頻度を使って簡単な文生成プログラムを作ります。 gistに上げています
- あるn-1単語の単語列
words_0
を入力したら、words_0
からはじまるngramで最も頻度が高い単語w_1
を出力 - その後、queueのように
words_0
の先頭を押し出して、w_1
を末尾に追加、 n-1単語の単語列words_1
をつくる words_1
からはじまる頻度の高い単語を出力し、、を繰り返す
以上のように単語のリストを生成していきます。
データを読み込む時、最も頻度の高いデータだけ残すように上書きしていきます。文末記号 </S>
が含まれている場合、文が終了してしまうので除外しました。
個人的に気になったので、カッコ 「
と 【
もノイズとして除外していますが、どこまでノイズとするかはデータを見つつ決めるのがいいと思います。
2gramデータの場合は最初の1単語、3gramデータを使う場合は最初に2単語入力し、ngramデータがある限り次の単語を取得していきます。
実行してみます。まず2gram。
適当な名詞・動詞・形容詞を入れてみます。(名詞はいろいろ試している時に出力された単語なので選出にバイアスがかかっています)
Enter 1 word: いちご いちご の は 、 その 時 に は 、 その 時 Enter 1 word: 著作 著作 権 は 、 その 時 に は 、 その 時 Enter 1 word: レビュー レビュー を し て いる の は 、 その 時 に Enter 1 word: 動く 動く の は 、 その 時 に は 、 その 時 Enter 1 word: 美しい 美しい もの で は 、 その 時 に は 、 その Enter 1 word: 来 来 て いる の は 、 その 時 に は 、
いずれも 「は 、」を起点に出力が同じになっているのがわかります。助詞は頻度が高いので仕方がないですね。
3gram・4gramに増やします。それぞれ2gram・3gramの出力を参考に入力しました。
2gram | 3gram | 4gram | |
---|---|---|---|
いちご | いちご の は 、 その 時 に は 、 その 時 | いちご の パウンドケーキ ◇ LED 衝立 と 木彫り 兜 の 緒 が | いちご の パウンドケーキ ◇ LED 衝立 と 木彫り 兜 の セット (※) |
著作 | 著作 権 は 、 その 時 に は 、 その 時 | 著作 権 は 、 この よう な 気 が し ます が | 著作 権 は 、 ( 株 ) の 企業 情報 を 登録 する |
レビュー | レビュー を し て いる の は 、 その 時 に | レビュー を 書く こと が でき ます か 、 と 思っ て | レビュー を 書く 場合 は ログイン し て ください ね ♪ (※) |
動く | 動く の は 、 その 時 に は 、 その 時 | 動く の か な と 思っ て い ます が 、 その | 動く の か 、 それとも 、 (※) |
美しい | 美しい もの で は 、 その 時 に は 、 その | 美しい もの を 、 さらに は 、 この よう な 気 が | 美しい もの を 見 て 、 その 中 で 、 この ブログ の |
来 | 来 て いる の は 、 その 時 に は 、 | 来 て くれ た の は 、 この よう な 気 が | 来 て くれ た の は 、 この よう な こと を し |
(※) は出力が途中で途切れた文
3gramではまだ「 は 、 この よう な 気 が」という同じ出力になってしまっていますが、4gramだとそれぞれ完全に違う出力になりました。
ですが、4gramの場合、該当する単語がないことで途中で出力が終了しています。
以上の結果から、多様な出力をするにはデータの扱いに工夫が必要なことがわかります。
いわゆるngram言語モデルでは生起確率 P(w_0, w_1, ..., w_n)
を複数のngramデータを使って求めます。
ngram言語モデルについては各所に資料があるので読んでみてください。
せっかくなのでこのデータを使ってngram言語モデルを実装してみようかなと思いますが、すでに誰かがやっているかもしれません……