63
ウェーブレット行列 最速攻略 〜予告編〜 echizen_tm Sep. 16, 2012

Wavelet Matrix

Embed Size (px)

Citation preview

Page 1: Wavelet Matrix

ウェーブレット行列 最速攻略 〜予告編〜

echizen_tm Sep. 16, 2012

Page 2: Wavelet Matrix

はじめに �  今回の発表では

ウェーブレット行列(Wavelet Matrix) というデータ構造の話をします

Page 3: Wavelet Matrix

ウェーブレット行列? �  ウェーブレット行列は

任意のデータ列に対して ある範囲内に 特定のデータがいくつあるかを 定数時間で計算することができます

Page 4: Wavelet Matrix

例えばどういうこと?(1)

4 7 6 5 3 2 1 0 1 4 1 7

この範囲に 1がいくつあるか知りたい!

Page 5: Wavelet Matrix

例えばどういうこと?(2)

4 7 6 5 3 2 1 0 1 4 1 7

この範囲に 3以下の数がいくつあるか知りたい!

Page 6: Wavelet Matrix

でも普通に計算すると

4 7 6 5 3 2 1 0 1 4 1 7

範囲の長さに比例した 時間がかかる・・・

Page 7: Wavelet Matrix

もし範囲内に データが1億件あったら

4 7 6 5 1 0 1 4 1 7

1回の計算で 1億に比例した時間がかかる!

・・・

Page 8: Wavelet Matrix

これは困る �  どんなに長いデータ列に対しても

ある範囲内に 特定のデータがいくつあるかを 同じ時間で計算したい!

Page 9: Wavelet Matrix

それ、 ウェーブレット行列でできるよ

�  ウェーブレット行列は 任意のデータ列に対して ある範囲内に 特定のデータがいくつあるかを 定数時間(=データ数に依存しない時間)で計算 することができます!(再掲)

Page 10: Wavelet Matrix

つまり ウェーブレット行列があると

�  2000万のユーザデータの ID1,000,000からID5,000,000の範囲で 年齢が20歳以上のユーザが何人いるかが 定数時間でわかる

�  5000人の社員データの 社員番号2000から5000の範囲で 年収が500万以上の社員が何人いるかが 定数時間でわかる

�  この中に1人、妹がいる!かどうかが 定数時間でわかる

Page 11: Wavelet Matrix

ウェーブレット行列って デメリットとかないの?

�  定数時間で計算ってことは インデックスとか余計なデータが必要なんじゃないの? →簡潔データ構造を使っているので データ数に対して充分に小さいインデックスで大丈夫 →簡潔データ構造はデータの動的更新は向いてないので 固定された大きなデータに対して参照が多い場合に有効

�  定数時間っていってもデータ数に依存しないだけで そんなに速くないんじゃないの? →ウェーブレット木(既存手法)の2倍高速

Page 12: Wavelet Matrix

ウェーブレット行列の 活用例(1)

�  FM-Index �  接尾辞配列(Suffix Array)型の検索アルゴリズムでは

最も性能が良いと言われている �  データサイズを小さくするために

テキストをBWT(Burrows Wheeler変換)で圧縮している �  検索時にRBWT(逆Burrows Wheeler変換)で

必要な部分だけ解凍

�  RBWTは内部でデータの特定範囲に クエリ内の文字がいくつ含まれるかを計算している

�  ウェーブレット行列で大幅に効率化!

Page 13: Wavelet Matrix

ウェーブレット行列の 活用例(2)

�  gwt �  tb_yasu氏による

大規模グラフ類似度計算ソフトウェア �  当初はウェーブレット木が利用されていたが

最近ウェーブレット行列に置き換わった �  詳しくはブログをチェック!

�  tb_yasuの日記 http://d.hatena.ne.jp/tb_yasu/20120909/1347196146

Page 14: Wavelet Matrix

ウェーブレット行列 最速攻略

echizen_tm Sep. 30, 2012

SPIRE2012より、 ずっとはやい!

Page 15: Wavelet Matrix

アジェンダ �  自己紹介 (1 slide)

�  本発表の目的 (1 slide)

�  簡潔データ構造の復習 (9 slides)

�  ウェーブレット行列の構築 (11 slides)

�  ウェーブレット行列のaccess (6 slides)

�  ウェーブレット行列のrank (9 slides)

�  まとめ (1 slide)

�  おまけ(1 slide)

�  参考資料 (2 slides)

�  【個人研究】ウェーブレット行列のrankを2倍高速化する(7 slides)

Page 16: Wavelet Matrix

自己紹介 �  ID: echizen_tm

�  ブログ: EchizenBlog-Zwei

�  職業: webエンジニア

�  興味: 自然言語処理を支える技術 (簡潔データ構造、機械学習)

�  お仕事: レコメンドとか

�  近況: 転職しました

Page 17: Wavelet Matrix

本発表の目的 �  ウェーブレット行列を紹介する

�  2012年10月21日〜25日の SPIREという国際会議で発表されるデータ構造

�  ウェーブレット行列とは? →ウェーブレット木と同じ機能を持つデータ構造 (しかもウェーブレット木より簡単で効率的)

�  なんで紹介するの? →ウェーブレット行列の圧倒的な性能に心奪われたから

�  ウェーブレット木とか知らないんだけど・・・ →ウェーブレット木の知識は不要

Page 18: Wavelet Matrix

簡潔データ構造の復習(1/9) �  “簡潔データ構造”というデータ構造はない

�  既存のデータ構造(木、配列)に対して簡潔版がある

�  簡潔データ構造は既存のデータ構造を大幅に圧縮する

�  圧縮したまま操作は効率的にできる(解凍しなくてOK)

Page 19: Wavelet Matrix

簡潔データ構造の復習(2/9) �  主な簡潔データ構造は以下の3つ

�  簡潔ビットベクトル �  すべての基礎となる簡潔データ構造 �  他の簡潔データ構造を実装するときに必要

�  LOUDS �  木の簡潔データ構造 �  詳しくは“日本語入力を支える技術”を読みましょう

�  ウェーブレット木 �  配列の簡潔データ構造 �  ウェーブレット行列に取って代わられる予定

Page 20: Wavelet Matrix

簡潔データ構造の復習(3/9) �  簡潔ビットベクトルは以下の操作を提供する

�  access(i): i番目の要素を返す

�  rank(b, i): ビット列のi番目より前にいくつbが出現するかを返す

�  select(b, i): ビット列でi番目にbが出現する位置を返す (今回はふれないので忘れましょう)

Page 21: Wavelet Matrix

簡潔データ構造の復習(4/9) �  access(b, i)の例

�  ビット列0110に対して �  access(0) = 0, access(1) = 1

access(2) = 1, access(3) = 0

Page 22: Wavelet Matrix

簡潔データ構造の復習(5/9) �  rank(b, i)の例

�  ビット列0110に対して �  rank(0, 1) = 1, rank(0, 2) = 1

rank(0, 3) = 1, rank(0, 4) = 2

�  rank(1, 1) = 0, rank(1, 2) = 1 rank(1, 3) = 2, rank(1, 4) = 2

Page 23: Wavelet Matrix

簡潔データ構造の復習(6/9) �  ウェーブレット木は以下の操作を提供する

�  access(i): i番目の要素を返す

�  rank(c, i): 配列のi番目より前にいくつcが出現するかを返す

�  select(c, i): 配列でi番目にcが出現する位置を返す (今回はふれないので忘れましょう)

Page 24: Wavelet Matrix

簡潔データ構造の復習(7/9) �  access(c, i)の例

�  配列abbcに対して �  access(0) = a, access(1) = b

access(2) = b, access(3) = c

Page 25: Wavelet Matrix

簡潔データ構造の復習(8/9) �  rank(c, i)の例

�  配列abbcに対して �  rank(a, 1) = 1, rank(a, 2) = 1

rank(a, 3) = 1, rank(a, 4) = 1

�  rank(b, 1) = 0, rank(b, 2) = 1 rank(b, 3) = 2, rank(b, 4) = 2

�  rank(c, 1) = 0, rank(c, 2) = 0 rank(c, 3) = 0, rank(c, 4) = 1

Page 26: Wavelet Matrix

簡潔データ構造の復習(9/9) �  簡潔ビットベクトルの実装については

DSIRNLP#2”作ろう!簡潔ビットベクトル”で解説した �  以降、ビット列に対しては

access, rankができるものとして扱う

�  ウェーブレット木は ウェーブレット行列で同じことがより簡単にできる (のでウェーブレット木の実装方法は気にしなくてOK)

�  復習はここまで!

Page 27: Wavelet Matrix

ウェーブレット行列の構築(1/11)

�  ウェーブレット行列の目的

�  配列に対するaccessは普通にできる

�  配列に対するrankができるようにしたい (でもaccessができる状態は維持する)

�  rankとは「ある範囲内」に 「ある数」がいくつあるかを数え上げる操作

Page 28: Wavelet Matrix

ウェーブレット行列の構築(2/11)

�  ウェーブレット行列の基本的な発想 �  データを2つのグループに分ける操作を繰り返す

�  次第に値が近いデータが近くに寄ってくるようにする �  同じ値が一箇所に集まったところで個数を数え上げる

Page 29: Wavelet Matrix

ウェーブレット行列の構築(3/11)

�  まずは以下の配列を考える

�  このデータ列を2進表記すると以下のようになる

4 7 6 5 3 2 1 0 1 4 1 7

Page 30: Wavelet Matrix

ウェーブレット行列の構築(4/11)

�  最初のビット列(オレンジ)はそのまま ウェーブレット行列の1行目となる

4 7 6 5 3 2 1 0 1 4 1 7

1 1 1 1 0 0 0 0 0 1 0 1

Page 31: Wavelet Matrix

ウェーブレット行列の構築(5/11)

�  2番目のビット列(赤)の前半に 1番目のビット列(オレンジ)で値が0だったものを置く

4 7 6 5 3 2 1 0 1 4 1 7

1 1 0 0 0 0

3 2 1 0 1 1

Page 32: Wavelet Matrix

ウェーブレット行列の構築(6/11)

�  2番目のビット列(赤)の後半に 1番目のビット列(オレンジ)で値が1だったものを置く

4 7 6 5 3 2 1 0 1 4 1 7

1 1 0 0 0 0

3 2 1 0 1 1 4 7 6 5 4 7

0 1 1 0 0 1

Page 33: Wavelet Matrix

ウェーブレット行列の構築(7/11)

�  この時点で前半に[0, 3]のデータが 後半に[4, 7]のデータが集まった

1 1 0 0 0 0

3 2 1 0 1 1 4 7 6 5 4 7

0 1 1 0 0 1

0,1,2,3 4,5,6,7

Page 34: Wavelet Matrix

ウェーブレット行列の構築(8/11)

�  3番目のビット列(青)の前半に 2番目のビット列(赤)で値が0だったものを置く

1 1 0 0 0 0 0 1 1 0 0 1

1 0 1 1 0 1

1 0 1 1 4 5

1 0 1 0 1 1 0 1 0 1 0 1

3 2 1 0 1 1 4 7 6 5 4 7

Page 35: Wavelet Matrix

ウェーブレット行列の構築(9/11)

�  3番目のビット列(青)の後半に 2番目のビット列(赤)で値が1だったものを置く

1 1 0 0 0 0 0 1 1 0 0 1

1 0 1 1 0 1

1 0 1 1 4 5

1 0 1 0 1 1 0 1 0 1 0 1

3 2 1 0 1 1 4 7 6 5 4 7

1 0 1 0 1

3 2 7 6 7

Page 36: Wavelet Matrix

ウェーブレット行列の構築(10/11)

�  今度はデータが [0,1][2,3][4,5][6,7]の4つの範囲に分割された

1 0 1 1 0 1

1 0 1 1 4 5

1 0 1 0 1

3 2 7 6 7

0,1 4,5 2,3 6,7

Page 37: Wavelet Matrix

ウェーブレット行列の構築(11/11)

�  まとめるとこうなる

1 0 1 1 0 1

1 0 1 1 4 5

1 0 1 0 1

3 2 7 6 7

4 7 6 5 3 2 1 0 1 4 1 7

1 1 1 1 0 0 0 0 0 1 0 1

1 1 0 0 0 0

3 2 1 0 1 1 4 7 6 5 4 7

0 1 1 0 0 1

Page 38: Wavelet Matrix

ウェーブレット行列のaccess(1/6)

�  access(0)の場合

�  1本目のビット列(オレンジ)の0番目は1なので 2本目のビット列(赤)の後半のどこかにある

�  rank(1, 0) = 0 なので 2本目後半の0番目(先頭から6番目)に移動しているはず

1 1 1 1 0 0 0 0 0 1 0 1

1 1 0 0 0 0 0 1 1 0 0 1

Page 39: Wavelet Matrix

ウェーブレット行列のaccess(2/6)

�  access(0)の場合

�  2本目のビット列(赤)の6番目は0なので 3本目のビット列(青)の前半のどこかにある

�  rank(0, 6) = 4 なので 3本目前半の4番目に移動しているはず

1 1 0 0 0 0 0 1 1 0 0 1

1 0 1 1 0 1 0 1 0 1 0 1

Page 40: Wavelet Matrix

ウェーブレット行列のaccess(3/6)

�  access(0)の場合

�  これらを繋ぎ合わせると100(2進数)となる

�  10進数で4なのでaccess(0) = 4 が得られた

1 1 0 0 0 0 0 1 1 0 0 1

1 0 1 1 0 1 0 1 0 1 0 1

1 1 1 1 0 0 0 0 0 1 0 1

Page 41: Wavelet Matrix

ウェーブレット行列のaccess(4/6)

�  access(5)の場合

�  1本目のビット列(オレンジ)の5番目は0なので 2本目のビット列(赤)の前半のどこかにある

�  rank(0, 5) = 1 なので 2本目前半の1番目に移動しているはず

1 1 1 1 0 0 0 0 0 1 0 1

1 1 0 0 0 0 0 1 1 0 0 1

Page 42: Wavelet Matrix

ウェーブレット行列のaccess(5/6)

�  access(5)の場合

�  2本目のビット列(赤)の1番目は1なので 3本目のビット列(青)の後半のどこかにある

�  rank(1, 1) = 1 なので 3本目後半の1番目(先頭から8番目)に移動しているはず

1 1 0 0 0 0 0 1 1 0 0 1

1 0 1 1 0 1 0 1 0 1 0 1

Page 43: Wavelet Matrix

ウェーブレット行列のaccess(6/6)

�  access(5)の場合

�  これらを繋ぎ合わせると010(2進数)となる

�  10進数で2なのでaccess(5) = 2 が得られた

1 1 0 0 0 0 0 1 1 0 0 1

1 0 1 1 0 1 0 1 0 1 0 1

1 1 1 1 0 0 0 0 0 1 0 1

Page 44: Wavelet Matrix

ウェーブレット行列のrank(1/9) �  rank(4, 10)の場合

�  つまり以下の紫の枠にいくつ4があるかを計算したい

4 7 6 5 3 2 1 0 1 4 1 7

0 10

Page 45: Wavelet Matrix

ウェーブレット行列のrank(2/9) �  rank(4, 10)の場合

�  なので1本目のビット列(オレンジ)では 1となっている部分に注目すればよい

1 1 1 1 0 0 0 0 0 1 0 1

4 1 0 0

0 10

4 7 6 5 3 2 1 0 1 4 1 7

Page 46: Wavelet Matrix

ウェーブレット行列のrank(3/9) �  rank(4, 10)の場合

�  1本目のビット列(オレンジ)で1となる数は 2本目のビット列(赤)で後半に移動している

�  1本目の枠の先頭の0は 2本目ではそのまま後半の先頭(6番目)に移動する

1 1 1 1 0 0 0 0 0 1 0 1

1 1 0 0 0 0 0 1 1 0 0 1

0 10

Page 47: Wavelet Matrix

ウェーブレット行列のrank(4/9) �  rank(4, 10)の場合

�  1本目のビット列(オレンジ)で1となる数は 2本目のビット列(赤)で後半に移動している

�  1本目の枠の終端の10は rank(1, 10) = 5 なので 2本目では後半の5番目(先頭から11番目)に移動する

1 1 1 1 0 0 0 0 0 1 0 1

1 1 0 0 0 0 0 1 1 0 0 1

0 10

6 11

Page 48: Wavelet Matrix

ウェーブレット行列のrank(5/9) �  rank(4, 10)の場合

�  なので2本目のビット列(赤)では 0となっている部分に注目すればよい

4 1 0 0

1 1 0 0 0 0 0 1 1 0 0 1

11

3 2 1 0 1 1 4 7 6 5 4 7

Page 49: Wavelet Matrix

ウェーブレット行列のrank(6/9) �  rank(4, 10)の場合

�  2本目のビット列(赤)で0となる数は 3本目のビット列(青)で前半に移動している

�  2本目の枠の先頭の6は rank(0, 6) = 4 なので 3本目では前半の4番目に移動する

1 1 0 0 0 0 0 1 1 0 0 1

6 11

1 0 1 1 0 1 0 1 0 1 0 1

Page 50: Wavelet Matrix

ウェーブレット行列のrank(7/9) �  rank(4, 10)の場合

�  2本目のビット列(赤)で0となる数は 3本目のビット列(青)で前半に移動している

�  2本目の枠の終端の11は rank(0, 11) = 7 なので 3本目では前半の7番目に移動する

1 1 0 0 0 0 0 1 1 0 0 1

6 11

1 0 1 1 0 1 0 1 0 1 0 1

4 7

Page 51: Wavelet Matrix

ウェーブレット行列のrank(8/9) �  rank(4, 10)の場合

�  なので3本目のビット列(青)では 0となっている部分に注目すればよい

4 1 0 0

1 0 1 1 0 1 0 1 0 1 0 1

1 0 1 1 4 5 4 3 2 7 6 7

Page 52: Wavelet Matrix

ウェーブレット行列のrank(9/9) �  rank(4, 10)の場合

�  3本目のビット列(青)の枠で囲まれている範囲では 元の配列で4となる数が0に、5となる数が1となっている

�  rank(0, 4) = 1 なので枠の先頭までに0が1つある rank(0, 7) = 3 なので枠の終端までに0が3つある

�  3 − 1 = 2 なので枠の中に4は2つあるとわかる

1 0 1 1 0 1 0 1 0 1 0 1

1 0 1 1 4 5 4 3 2 7 6 7

元のデータに対してrank(4, 10) = 2 となる

Page 53: Wavelet Matrix

まとめ �  ウェーブレット行列は

任意の要素を持つ配列に対してaccess, rank, select の機能を提供する

�  これはウェーブレット木が提供する機能と同等

�  ウェーブレット行列の機能は ビット列のaccess, rank, selectによって実現される

�  つまり簡潔ビットベクトルの性能が ウェーブレット行列に大きく影響する

Page 54: Wavelet Matrix

おまけ �  資料[1] “The Wavelet Matrix”

より転載

�  WT:普通のウェーブレット木

�  WTNP: Levelwiseな ウェーブレット木の実装

�  WM: ウェーブレット行列

�  RG: R.Gonzalezらによる 簡潔BVの実装

�  RRR: R.Raman, V.Raman, S.S.Rao による簡潔BVの実装

Page 55: Wavelet Matrix

参考資料(1/2) �  [1] “The Wavelet Matrix”

Claude & Navarro; SPIRE2012 http://www.dcc.uchile.cl/~gnavarro/ps/spire12.4.pdf

�  [2] アスペ日記(takeda25さんのブログ) http://d.hatena.ne.jp/takeda25/

�  [3] EchizenBlog-Zwei(echizen_tmのブログ) http://d.hatena.ne.jp/echizen_tm/

Page 56: Wavelet Matrix

参考資料(2/2) �  ウェーブレット行列のライブラリには以下のものがある

�  libcds: 論文著者(F. Claude)の実装 様々な簡潔データ構造が実装されたすごいライブラリ https://github.com/fclaude/libcds

�  wavelet-matrix-cpp: takeda25さんの実装 wat-array互換。rank高速化(後述)あり。他にも工夫がたくさん https://github.com/hiroshi-manabe/wavelet-matrix-cpp

�  shellinford: echizen_tmの実装 FM-Indexライブラリ。rank高速化したウェーブレット行列を含む https://code.google.com/p/shellinford/

Page 57: Wavelet Matrix

【個人研究】ウェーブレット行列のrankを2倍高速化する(1/7) �  ウェーブレット行列の論文”The Wavelet Matrix”

に書かれているrank計算のアルゴリズムは 内部で簡潔ビットベクトルのbv.rankを 2 * log(文字種)回呼び出している �  文字列の場合2 * log(256) = 16回

�  文字種 * log(データ長)のデータ領域を 追加で持たせることで 簡潔ビットベクトルのbv.rank呼び出し回数を log(256)回に削減することができる �  文字列の場合、256*64bit=2KBの追加データ領域で

bv.rank呼び出しを8回に減らすことができる

Page 58: Wavelet Matrix

【個人研究】ウェーブレット行列のrankを2倍高速化する(2/7) �  ウェーブレット行列のrankは

データの範囲を絞り込んでいって 最後に2種類のデータしか含まれなくなったら bv.rankでビットの数を計算するというもの

�  範囲の更新は以下のように行われる(rank(c, i)) �  depth = 0, begin = 0, end = i �  while (depth < log(文字種)) {

bit = cのdepth番目のbit begin = bv[depth].rank(bit, begin) end = bv[depth].rank(bit, end) depth++; }

Page 59: Wavelet Matrix

【個人研究】ウェーブレット行列のrankを2倍高速化する(3/7) �  範囲の更新は以下のように行われる(rank(c, i))

�  depth = 0, begin = 0, end = i �  while (depth < log(文字種)) {

bit = cのdepth番目のbit begin = bv[depth].rank(bit, begin) end = bv[depth].rank(bit, end) depth++; }

�  このアルゴリズムには重要な2つの特徴がある �  beginとendの更新は互いに独立に行われる �  beginが最終的に取りうる値は高々”文字種”の数と同じ

(文字列の場合は256)

Page 60: Wavelet Matrix

【個人研究】ウェーブレット行列のrankを2倍高速化する(4/7)

�  ウェーブレット行列のrankの更新アルゴリズムには 重要な2つの特徴がある �  beginとendの更新は互いに独立に行われる �  beginが取りうる値は高々”文字種”の数と同じ

(文字列の場合は256)

�  この2つの特徴から以下のことが言える �  beginが取りうる値を事前に計算して持っておくことで

bv.rankの呼び出し回数を半分にすることが出来る �  beginの取りうる値は高々”文字種”の数なので

文字種*log(データ長)の予備領域で済む �  beginとendは互いに独立なので

beginの途中の値がなくてもendの更新には影響がない

Page 61: Wavelet Matrix

【個人研究】ウェーブレット行列のrankを2倍高速化する(5/7)

�  改良前のrank(c, i) �  depth = 0, begin = 0, end = i �  while (depth < log(文字種)) {

bit = cのdepth番目のbit begin = bv[depth].rank(bit, begin) end = bv[depth].rank(bit, end) depth++; }

�  改良後のrank(c, i) �  depth = 0, begin = 事前計算したcに対するbegin, end = i �  while (depth < log(文字種)) {

bit = cのdepth番目のbit end = bv[depth].rank(bit, end) depth++; }

Page 62: Wavelet Matrix

【個人研究】ウェーブレット行列のrankを2倍高速化する(6/7) �  なぜbeginは高々”文字種”の数の値しか取らないのか?

�  ウェーブレット行列の最終行は”2種類の値”ごとのブロック

�  beginは最終的にこのブロックのうちのどれかの先頭に来る �  それぞれのブロックの先頭位置begin’に対して

bv.rank(0, begin’), bv.rank(1, begin’)を持っておけば良い

1 0 1 1 4 5 4 3 2 7 6 7

0,1 4,5 2,3 6,7

rank(0, i) rank(1, i)

rank(4, i) rank(5, i)

rank(2, i) rank(3, i)

rank(6, i) rank(7, i)

Page 63: Wavelet Matrix

【個人研究】ウェーブレット行列のrankを2倍高速化する(7/7) �  で、実際性能はどうなの?

�  takeda25さんがwat-arrayとの比較をしてくださっています �  データ長100,000,000、文字種1,000に対して

ウェーブレット行列(高速版): 1.49 micro sec (53%) ウェーブレット木(wat-array): 2.51 micro sec (100%)

�  echizen_tm作のFM-Index (内部でウェーブレット木/行列を使用)での比較 �  データ長4,000,000、文字種256に対して

ウェーブレット行列(高速版): 144 micro sec (67%) ウェーブレット行列: 190 misco sec (89%) ウェーブレット木: 214 micro sec (100%)