Compressed ngram

23
n-gramを小さくする話を調べてみた 町永圭吾 (machy)

Transcript of Compressed ngram

Page 1: Compressed ngram

n-gramを小さくする話を調べてみた

町永圭吾 (machy)

Page 2: Compressed ngram

n-gramとは • 今日の題材はn-gramインデックスではなく、n-gram確率(言語モデルの一種)です

• 音声認識、手書き文字認識、スペル訂正、かな漢字変換、機械翻訳など様々な場面で利用されている

• ついでに言うと、単語n-gramを題材にします(文字n-gramだと、ちょっと話が違うかも)

P(晴れ | 天気 は) = 0.24 P(きっと | 天気 は) = 0.03 P(まんじゅう | 天気 は) = 0.00001

n-gram確率のイメージ

Page 3: Compressed ngram

n-gramを小さくしたい • クライアントサイドで動作するアプリケーションのためとか • サーバーサイドでも1台のサーバーにオンメモリになると嬉しい

• 注目するのはメモリ使用量 ファイルにしたとき小さいかどうかはあまり気にしない

Page 4: Compressed ngram

まずはデーターを見てみます

• 大規模なn-gramを自分で用意するのは大変

•  google n-gram 4万円するらしい 個人で買えるのかな・・・

•  s_yataさんの日本語ウェブコーパス2010 ありがたい事に利用制限なし http://s-yata.jp/corpus/nwc2010/ngrams/ 単語ngramと文字ngramがある。 文字ngramは頻度10以上、7gramまでで圧縮時12.1GB、展開時75.2GB かなり歯ごたえのあるボリューム

Page 5: Compressed ngram

データの概要

エントリ数

1gram 約4百万エントリ 2gram 約80百万エントリ 3gram 約370百万エントリ 4gram 約607百万エントリ 5gram 約597百万エントリ 6gram 約470百万エントリ 7gram 約347百万エントリ

ユニーク単語数

ユニーク単語の平均Byte数 18.67Byte

頻度最大は”<S>”, “</S>”の約56億回、時点は”の”の約41億回

s_yataさんの日本語ウェブコーパス2010 単語単位 頻度10以上 http://s-yata.jp/corpus/nwc2010/ngrams/

データの例

また 生地 も 129 また 生地 や 14 また 生地 を 443 また 生地 丈 26 また 生地 屋 14 また 生地 自体 22 また 生地 表面 13 また 生垣 に 10 また 生垣 の 16

Page 6: Compressed ngram

普通に作ったn-gramのメモリ使用量を考えてみる

• 単語→単語ID の索引 可変長文字列へのポインタ(8byte)のリスト +可変長文字列(NULL文字終端)の羅列 ソートされていれば二分探索で単語が見つかった行番号がWordID (頻度順にIDを振りたいならば、さらにID情報の付加が必要)

• 末尾だけが異なるエントリグループ 含まれるエントリの数 (8byte) 含まれるエントリのデータ

• エントリ •  単語ID (8Byte) •  頻度(8byte) •  次のエントリグループへのポインタ

(8byte)  IDの二分探索で探す

必要なメモリ

単語のみ 112MB 1gramまで 304MB 2gramまで 2,320MB 3gramまで 11,840MB

Page 7: Compressed ngram

もう少し頑張った場合を考えてみる

• 単語→単語ID の索引 可変長文字列へのオフセット(4byte)のリスト +可変長文字列(NULL文字終端)の羅列 単語の羅列は80MBなので4byteオフセットで十分

• 末尾だけが異なるエントリグループ 含まれるエントリの数 (4byte) 一番多い1gramで含まれるエントリ数は約400万件 含まれるエントリのデータ

• エントリ •  単語ID (1gramは省略、4Byte)

1gramはすべての単語を含むので 単語IDは省略可能

•  頻度(1gramは8byte、以降4byte) •  次のエントリグループへのオフセット

(4byte、末尾以外) n-gramの末尾は次のn-gramへの オフセットは不要

必要なメモリ

単語のみ 96MB 1gramまで 128MB 2gramまで 800MB 3gramまで 4,400MB

Page 8: Compressed ngram

いったん考察1 •  3gramに必要なメモリ量 多少富豪的12GB ~ すこし頑張った4.4GB 二分探索の代わりにハッシュなどを使ったら12GBじゃ済まない

• ポインタのコストは割とでかい。64bitだったらWordID2個分(実装に使う言語によっては、いつのまにかそのコストを払っていたり)

WordID

WordID

freq

freq

next

next

WordID freq next

WordID freq next

Entry* Entry* Entry* Entry*

Count

WordID WordID

freq freq

next next

WordID freq next WordID freq next

Count

ハッシュ? ポインターのリスト?

Page 9: Compressed ngram

いったん考察2

• 単語辞書に必要なメモリ量は112MBと多くない この部分にハッシュを使ったとしてもせいぜい400MB

•  3gramまでだと、3gramの部分の情報量が多い(4.4GB中の3.6GB) コーパスの大きさとカットオフの頻度のバランスにもよるだろう

•  n-gramのライブラリは実用上、backoff係数などを計算して((n-1)-gramまでの各エントリに)保持するため、さらにいくらかのメモリを使用する 今回紹介するテクニックは格納する値の性質に強くは依存しないため、簡単のため省略して考える

• 固定長のデータはアクセシビリティが高い。考えなしに圧縮するわけにはいかない

Page 10: Compressed ngram

紹介する論文

• Quantization-based Language Model Compression (2001) Edward Whittakker, Bhiksha Raj

• Compressing trigram language model using golomb coding (2007) Ken Church, Ted Hart, Jianfeng Gao

• A Succinct N-gram Language Model (2009) Taro Watanabe, Hajime Tsukada, Hideki Isozaki

• 注)紹介するテクニックはそれぞれの論文が初出だとは限りません

Page 11: Compressed ngram

Quantization-based Language Model Compression (2001) •  Lloyd’sアルゴリズムによる頻度情報の量子化(頻度

4byte→2byte) • 単語IDがソートされていることを利用したIDの圧縮(4byte→およそ2byte?)

• エントリグループごとのエントリ数は不要

Page 12: Compressed ngram

•  Lloyd’sアルゴリズムによる頻度情報の量子化(頻度4byte→2byte)

頻度の頻度

初期化 収束

頻度

代表値

•  4byteで表せる頻度(約43億まで)を2byte、65,536種類の値で代表させる。代表値のIDから頻度を引くためのテーブルが必要

•  Lloyd’sのアルゴリズムは、EMアルゴリズムと考えられる(代表値に代表される範囲から最適な代表値を選ぶ、代表値と代表値の境界を決める、を繰り返す)誤差の合計が少なくなるように学習される

•  誤差が生じるが性能への影響は小さい。

10 9 8 7 6 5 4 3 2 1

Page 13: Compressed ngram

• 単語IDがソートされていることを利用したIDの圧縮(4byte→およそ2byte?)

•  look-up tableのオフセットの差分が探索すべき範囲 •  CMU-Cambridge Toolkitでも使われている方法だと書かれている •  末尾のn-gramエントリではWordIDがスパースなので効果が薄いと考えられるが、詳しくは述べられていない。

0002 A018

0002 B9F1

0002 F022

0003 0EE5

0003 310B

0003 41BA

WordID(4byte) ソートされていることに注意

A018

B9F1

F022

0EE5

310B

41BA

WordID lower(2byte)

*last

*last

*last

*last

0000

0001

0002

0003

look-up table

Page 14: Compressed ngram

• エントリグループごとのエントリ数は不要

WordID WordID

freq freq

next next

WordID freq next WordID freq next

WordID WordID

freq freq

WordID freq WordID freq WordID WordID

freq freq

WordID freq WordID freq

2gram 3gram

• 次のエントリグループのオフセットまでが、そのグループの範囲

Page 15: Compressed ngram

Compressing trigram language model using golomb coding (2007) •  2gramと3gramをごちゃまぜにしてハッシュ値でインデキシング。ハッシュ値をソートして隣接要素の差分をGolomb符号で圧縮する。 このままでは逐次検索するしかないので、スキップリストを併用する。 頻度、backoff重みも含めて、1エントリあたり3bytesとのこと。

• Stolcke pruningという、頻度ではない基準でエントリの削除を行なっている。

Page 16: Compressed ngram

• ハッシュ値でインデキシング。ハッシュ値をソートして隣接要素の差分をGolomb符号で圧縮する。

P(w1| w3 w2)=?

hash = w3*V3+w2*V2+w1*V1 mod P

2014755

2040193

2077690

2100147

2121572

2129982

2160917

2178113

衝突が起きにくいよう、 Pはエントリ総数の2万倍あたりの素数を使う

ハッシュ値をソートしてリストアップ

2014755

2100147

2160917

look-up table 29438 37497

8400

21425

17196

*data

*data

*data

残りは差分をとってGolomb符号化 0102 0103 0104 0105 0106 0107 0108 0109

0102 0105 0108

0103 0104

0106 0107

0109 •  WordIDを格納する必要がない。ハッシュ値の衝突の危険性がある •  3gramの組み合わせを総当りにすれば衝突は多いかもしれないが、入力が3gram確率に応じて起こるとすれば、衝突は比較的少ないのかも

•  n-gram末尾では、直前までが一致するグループ内のエントリ数が2,3個しかない場合も多い。グループ内で差分圧縮しても効果が薄いが、この手法だと全体を差分圧縮できるのがポイントか。

Page 17: Compressed ngram

A Succinct N-gram Language Model (2009) Taro Watanabe, Hajime Tsukada, Hideki Isozaki • 木構造を表すのにLOUDSを使用、さらにちょっと工夫(1ノードあたり2.4bit)

• WordIDと頻度をVariable Byte風に圧縮する。 Succinct Bit Vectorを使ってランダムアクセスできるようにする。(WordID 15.2bit, 頻度10.96bit)

• さらに8KBブロックごとにgzip圧縮した場合は、20%くらい節約できた。実用的な処理速度は出る。

Page 18: Compressed ngram

A Succinct N-gram Language Model (2009) Taro Watanabe, Hajime Tsukada, Hideki Isozaki • 木構造を表すのにLOUDSを使用、さらにちょっと工夫(1エントリあたり2.4bit)

• 簡単のため、工夫なしバージョンを引用したのが上図 •  node idは便宜的なもので、node id自体を記録する必要はない。node idで親ノード、子ノードを辿ることができる。node idをキーとして、実データ(WordID、頻度等、別途用意)を引く。

• 普通のn-gramで考えた方式だと、末尾以外は32bit、末尾は0bit消費する。想定した3gramだと1エントリあたり約5.3bit (コーパスが異なるので単純には比較できない)

Page 19: Compressed ngram

A Succinct N-gram Language Model (2009) Taro Watanabe, Hajime Tsukada, Hideki Isozaki • WordIDと頻度をVariable Byte風に圧縮する。 Succinct Bit

Vectorを使ってランダムアクセスできるようにする。(WordID 15.2bit, 頻度10.96bit)

WordID

• Variable Byteと異なり、境界情報は別途保持している • 境界情報をsuccinct bit vectorで保持する。select(x)操作(x番目の1を問い合わせる)により、高速に「インデックス番号→データの先頭までのオフセット」を知ることができる。 次のインデックス番号のオフセットから終端もわかる。

boundary

1Byte

1 1 0 0 1 0 1

3Byte 2Byte 1Byte

Page 20: Compressed ngram

A Succinct N-gram Language Model (2009) Taro Watanabe, Hajime Tsukada, Hideki Isozaki • さらに8KBブロックごとにgzip圧縮した場合は、20%くらい節約できた。実用的な処理速度は出た、と書いてある。

• 実装方法の細かい説明はなし。 • 節約が20%程度ということは、ブロック圧縮しなくても、かなりいい線いってると言える?

Page 21: Compressed ngram

テクニックのまとめ

• サイズ情報をオフセットの差分から求めて情報の重複を避ける(3回出てきた)

• 性能への影響が少ない部分の情報を切り捨てる •  頻度情報の量子化 •  頻度が小さいエントリ、削除してもスムージングで予測される確率が近いエントリの削除

•  ときどき間違えることを許容してハッシュを使う

• 探索性能を確保しつつ圧縮する •  線形探索しかできない形式をブロックに分けてインデックスする •  succinct bit vectorでオフセット情報をもつ

• まとめてみると、汎用性をみいだせるような、そうでもないような・・・

Page 22: Compressed ngram

推計してみる

ちょっと 富豪的 ※

ふつう バイナリ ※

最後に紹介した論文 ※

3gram まで 11,738MB 4,304MB 1,613MB

※すべて推計値 WordIDの索引を除く部分

生テキスト形式

xz圧縮テキスト

3gram まで 10,196MB 1,854MB

参考 文字列のエントリ表記を含むので 比較には注意

•  bits/entry の情報から推計してみました。実際には試していないので(すいません)勘違いがあるかも 論文では5gramまで。推計したのは3gramであるなど、条件は異なります

Page 23: Compressed ngram

おしまい

• まだまだ改善の余地はあるか? →ある…きがする

• いけてるライブラリはどれ? →知らないので、あったら教えてください