« June 2009 | Main | August 2009 »

2009.07.15

夏のインターンのお知らせ

今年の夏、Preferred Infrastructure(PFI)では学生インターンを募集しています。詳細はこちら
 (その他詳細など、代表西川のブログ
興味がある方はぜひ応募してください。第一期の締切はすぐです。

インターンプログラムは私が担当する予定です。基本的には期間内に終わるようなプロジェクトを決めて、それを実現するアイディアを一緒に考えてやっていくという形ですが、内容や勤務体系は相談してきめたいと思います。ISの人には演習3に近いと思っていただければいいでしょうか。
(ちなみに、弊社最初のインターンはおーくらさんが来てくれました。このときもプロジェクトを適当に決めて、いろいろ試行錯誤して進めました。)

みなさんの応募をお待ちしております!

| | Comments (1155) | TrackBack (0)

2009.07.04

netflix prize is over, 時間経過による嗜好性の変化

米国のオンラインDVDレンタルサービス「Netflix」が、現在利用しているレコメンデーションシステムの性能をはじめに10%改善したチームに100万ドルの賞金を与えるという触れ込みで始まったnetflix prizeは当初の予想よりも時間がかかったが、つい最近最初からトップを走り続けていたbellkorと、上位陣のコラボレーションのチームが10%の壁を破った(leaderboard)。

彼らの手法は「非常に多くの様々な種類のレコメンデーションシステムの結果を混ぜ合わせる」という愚直だがいかにも精度が出そうだという方法を採用している(、と昨年度の結果からは思われる。近々詳細は出るだろう。)

実際に使ってとどめになったかどうかは分からないが、彼らのチームの主要メンバーがKDDで新しい手法を発表しており、単一の手法による最高精度を達成している。ちなみに今年のKDD(データマイニング系の学会の最高峰の一つ)のBest Research Paperである

Yehuda Koren, "Collaborative Filtering with Temporal Dynamics ", KDD 2009 pdf

これは時間経過による嗜好性の変化をモデルに組み込んだものである。

協調フィルタリングによるレコメンデーションを行おうと思うと時間経過による様々な変化がある。例えば、ある人の嗜好性が連続的に変わる、もしくはある商品に対する評価が連続的に変わるということもあるし、ちょっと面白い例で、一つのアカウントを家族で使う状況だと、ある日はお父さんが使っていて、次の日は娘が使う場合があり、この場合だと、アカウントだけをベースにみていると嗜好の傾向が非連続的に変わることになる。

この嗜好の時間変化についてはずっと昔から多くの人が取り組み、利用してきたがどうもうまくいかなかったものだが(グラフの変化、テンソルでとらえるような研究も多い)、この研究では、まず単純に時間経過情報を入れただけではうまくいかないというところからスタートして、様々な工夫を入れている。最終的に高い精度を達成した手法では、人毎、商品毎のbias項、人と商品の隠れ項の部分に時間変化の影響を入れており、緩やかな場合の変化をスプライン関数で捉え、さらに連続的ではない急激な変化を1日毎のログを別に加えることで捉えている。

この論文で特に面白かったのが6章から始まる結果の分析であり、様々な時間経過による指向性の変化を仮説を立てて調べている。2004年頃に全体のレーティングが非連続的に上がるという現象がみられ、さらに古い映画の評価が新しい映画の評価よりも高くなるという現象がみられている。

分析の手法は実際に論文を読んでもらうとして、結果、分かったのは、2004年頃にnetflix自身が提供しているレコメンデーションシステムのcinematchの精度が向上し、GUIもよくなったおかげでユーザーがより自分の嗜好にあった映画を見られるようになったということである。さらに、新しい映画はなんとなく流行りだから見ている場合が多いのに対し古い映画はレコメンデーションシステムによってお勧めされたものが多いので古い映画の評価が相対的に高いことが起きたらしい。

10%の性能改善という絶妙な目標設定とともに、最終的にnetflix社のcinematchがユーザーの満足度を上げているのに成功したということが証明された形でnetflix prizeが終わりを迎えることで、netflixすごいなとおもいました。

| | Comments (375) | TrackBack (0)

2009.07.03

Burrows-Wheeler変換の線形時間アルゴリズム

研究紹介です。今夏のSPIRE 2009という学会で

"A Linear-Time Burrows-Wheeler Transform using Induced Sorting", D. Okanohara, K. Sadakane, SPIRE 2009 pdf(draft)

というのを発表します。これは与えられた文字列に対し接尾辞配列を経ないでBurrows-Wheeler変換を直接行うというもので、アルファベットサイズによらず入力長に対して線形時間で行えます。基本的なアイディアは昨年のInduced Sortingによる接尾辞配列の線形時間構築アルゴリズム(いわゆるSAIS)を接尾辞配列を使わないでシミュレートするものです。pushとpop操作だけからなり、そのまま外部記憶上での構築とかにも対応できるようになっています。

Burrows-Wheeler変換(BWT, Block Sortingとも呼ばれるen-wikipedia)は、94年(発見自体は80年代)にBurrows, Wheelerらによって提案された文字列の可逆変換であり、データ圧縮、全文検索、データマイニングなど広い分野で使われる変換手法です。なにか大規模な文字列処理をするなら、とりあえずBWTをかけとけば大抵楽に処理できます。

--
今回の論文の位置づけを示すために、文字列処理用のデータ構造の歴史的背景とかを書いてみます。

まず、接尾辞木(Suffix Tree, ja-wikipedia)が1973年にWeinerによって提唱されます。接尾辞木は次のように作られます。まず長さnの入力文字列T[1, n]に対し、i文字目からj文字目までの部分文字列をT[i, j]で表すとします。この時、Si = T[i, n]をTのi番目の接尾辞と呼びます。入力Tには全部でn個の接尾辞があるのですが、この接尾辞それぞれをキーとしたtrieを作ります。次に、子が一つしかない節点を取り除き、更に文字列を枝上でそのまま持つ代わりに、葉に何番目の接尾辞に対応するかの情報を格納すると共に、各節点には深さ情報のみを記録するとします。するとT中に出現する全ての部分文字列はO(n^2)個あるのですが、節点の数は高々n個で、さらに葉の数もn個しかないようなコンパクトなデータ構造ができます。これが接尾辞木です。接尾辞木を利用することで非常に多くの様々な文字列処理を高速に行えることが知られています
(suffix treeを利用した様々なアルゴリズムについては有名なGusfield本にいろいろ書いてありますAlgorithms on Strings, Trees and Sequences: Computer Science and Computational Biology

こうしたデータ構造は、大量のデータ(30億文字かならるヒトゲノムとか)を扱うことが主な目的なので、高速に構築できることが重要になります。接尾辞木も提案されてから様々な手法が提案されてきました。様々な改良が重ねられた後に、95年 Ukkonenによってアルファベットサイズが定数の場合の現実的な線形時間アルゴリズムが、97年にFarachによって任意のアルファベットサイズに対する線形時間アルゴリズムが提案されています。

接尾辞木は非常に強力なツールなのですが大きな問題がありました。それは接尾辞木を保存するのに必要な作業領域量、つまりメモリを非常にたくさん必要とすることです。入力サイズに対し(ポインタのサイズが定数の場合)線形のサイズとはいえ効率的な実装でも入力長に対し数十倍のメモリが必要とされてました。

こうした問題を解決したのが91年にManberらによって提案された接尾辞配列(Suffix Array)です。これは接尾辞木の葉の部分のみを使ったもので作業領域量を入力長*ポインタサイズ(普通は4バイト)と大幅に減らすことに成功しました。接尾辞配列を正確にいうと、入力Tに対し、接尾辞配列SA[1, n]をT[SA[i], n] < T[SA[i+1], n]が全ての1 <= i < n-1に対し成り立つような配列と定義されます(文字列間の<は辞書式順序とする)。SAを使えば、接尾辞が辞書式順序に並んでいるので、ある文字列qを検索したい場合にはT[SA[l], n] <= q <= T[SA[r], n]が成り立つようなl, rを二分探索で求めることでqの出現場所を全て発見することができ、SA[l, r]がqの出現場所であり、r-l+1がqの出現回数となります。任意の文字列に対しO(log n)時間で検索できるデータ構造が約5n byte(入力文字列n + 接尾辞配列4nバイト)で保持できるということになります。

大幅に作業領域量を減らすことに成功した接尾辞配列でしたが、構築時間が大きくなるという問題がありました。接尾辞木を最初に作り葉の部分だけを回収すれば線形時間で構築できるのですが、結局作業領域量が大きくかかってしまいます。接尾辞木を経ないで接尾辞配列を直接構築するには、接尾辞を辞書式順序にソートする必要があり、最悪計算量がO(n^2 log n)かかるという問題がありました(入力にT = aaaaaa...a$というものを考えた場合、毎回の比較にO(n), 比較回数にO(n log n)かかるため)。この構築時間は97年に定兼先生によってO(n log n)に改善され、2003年には接尾辞配列を直接線形時間で構築するアルゴリズムが3つ同時に提案されます。その後もさらに高速な構築アルゴリズムが提案されていき、現在の構築時間では1秒あたり4~8MB程度構築できるようになっています(ベンチマーク@white page

接尾辞配列と大体同時期にBurrows Wheeler変換というものが提案されました。これは入力T[1, n]をB[i] := T[SA[i]-1](SA[i]=0の時はB[i]=T[n])で定義される文字列B[1, n]に変換するというものです。
この変換は一見すると直感的ではありませんが、非常に重要な性質を持っていることが分かっています。代表的なものを三つあげると、

(1)可逆変換である

BW変換後のB[1, n]のみから入力文字列T[1, n]に戻すことがO(n)時間で可能です。これはLF-mappingと呼ばれるものを利用します。B[i]=cに対応する接尾辞はrank(B, i, c) + C(c)番目にあるというものです、但しrank(B, i, c)はB[1, i]中のcの出現回数、C(c)はB中のcより小さい文字の出現回数です(つまりT中と言い換えても同じ)。(実際の逆変換のコードはC++20行弱程度で例えばpdfの20枚目あたりにあります)

(2)元の入力が圧縮しやすい場合、BW変換後は同じ文字が連続して出現しやすい

Bは定義より、各文字をそれに後続する文字列をキーとしてソートした結果となっています。よって似た文脈の直前に同じ文字が出現しやすいのであれば、Bでは連続して同じ文字が出現することになります。例えば英語では"the"が頻出するので"he .."の文脈の直前には"t"が出現しやすくB中でも"t"が連続して出現するようになります。

この直感は様々な形で解析されています。まず圧縮しやすいというのは(k-1)文字を見たら次の文字は非常に予測しやすいということでいえて、これはHk(k次経験エントロピー)という数値で測ることができます。そして、BW変換後は文脈情報を全く無視して先頭移動法などローカルな情報だけを使って符号化してもその後のサイズがHkに漸近することが知られています("A simpler analysis of Burrows-Wheeler-based compression, H. Kaplan., and et. al.pdf

(3)(圧縮)全文検索に利用できる

元々データ圧縮用に作られたBW変換は、2001年にFerraginaらによるFM-indexと呼ばれるデータ構造によって全文検索に利用できるということが分かりました。BW変換の定義には接尾辞配列が使われているので接尾辞の情報が入っているのですが実際にこれを、LF-mappingの性質を使うことで全文検索ができることが示されました。
このFM-indexではLF-mapping、つまりはrank(B, i, c)を求める必要があり、FM-indexが最初に提案された時には、この部分の効率的かつ高速な計算の実現が困難でした。それが03年にGrossiらによって提案されたwavelet treeを使うことで高速に行うことができ、現在ではFM-indexはテキスト長に依存せずクエリ長に比例する時間で全文検索が行えます。しかも必要な作業領域量はBと同じだけのスペースなので約nバイトで済みます。

詳細を追うことはやめておきますが、最長共通接頭辞配列や、木を表すテーブルと組み合わせることで接尾辞配列(拡張接尾辞配列、Enhanced Suffix Arrays)やBWTは接尾辞木と同じように複雑な文字列検索を行えることがわかっています。

ここまでの流れを追うと、文字列処理用のデータ構造は、効率的に操作できる能力を変えずにどんどんコンパクトになってきていました。

- 接尾辞木 1973年 O(n log n)、元文書の約20倍でヒトゲノムの場合は約50GB
- 接尾辞配列 1991年 n log n 、元文書の約5倍でヒトゲノムの場合は約13GB
- BW変換 1994年(2001年)、nHk、元文書の等倍以下でヒトゲノムの場合は約750MB

また、それらのデータ構造を直接高速に線形時間で構築するようなアルゴリズムも提案されてきました。今回の発表はこの流れに沿うものになってます。

線形時間の構築
- 接尾辞木 Ukkonen 1995年、Farach 1997年
- 接尾辞配列 Aluruなど 2003年
- BW変換 今回の 2009年

さて、今後もこの流れは続くかですが、コンパクトになっていく流れは、このままでは難しいといえます。すでにBW変換は元の文書を圧縮したサイズに近いサイズまできています。全文検索において、任意の部分文字列に対し正しい結果が得られるということは、元の文書を復元できるということを意味しているので、データ圧縮とやっていることは同じです。ですので、元の文書が持っている情報を捨てない限りはこれ以上小さくするのはかなり難しいと思われます(もしできたらデータ圧縮においても革命)。結果の正確さを100%ではなく、何らかの形で保障しながらサイズを劇的に減らす方向については十分ありうると思います。

それに対し、構築時間については、理論上は線形時間よりは速くするのはちょっと大変そうですが、実際には改善が続けられています。今回提案した手法も実装に落すところでは更に工夫が必要です(その実験/論文はそのうち)。実装以外にも例えば接尾辞配列において並列に計算するような方法や、検索時間を犠牲にして構築時間を減らすような方法もでています。秒間数MBを処理できるとは数年前から比べたら各段の進歩ですが、今流行りのマイクロブログのリアルタイム検索などを実現しようと考えたり、もうすぐ実用化されると噂される次々世代シーケンサは秒間1GB超という速さでゲノム配列を読めるということで、まだまだ改良しなければならなさそうです。

| | Comments (114) | TrackBack (0)

« June 2009 | Main | August 2009 »