CRPを試す
はじめに
NowいProfessionalなYoungにバカうけなLMを作ってみたいなぁと思ったので、Chinese Restaurant Process(CRP)についてちょっと調べてみた。
CRPとは
- 中華料理店過程(Chinese Restaurant Process)
- 以下のルールによって、集合{1,...,n}の分割Bnを決める離散時間確率過程(時系列)
- t=1のとき、B1 = {1}
- t=n+1のとき、それまで生成された分割Bnに対し、次の確率で追加する
- |ブロックiの要素数|/(n+1)の確率で、そのブロックiに追加する
- 1/(n+1)の確率で、新たなブロックを作りそれに追加する
- Btの例(「|」はブロックの切れ目)
B1 = {1} B2 = {1,2} B3 = {1,2|3} B4 = {1,2,4|3} B5 = {1,2,4|3|5} ...
- で、これは「中華料理店」での"テーブル"と"お客さん"にして考えられるので「中華料理店過程」と呼ばれているみたい(インドのブッフェ版とかもある)
- ブロック = テーブル
- 要素 = お客さん
- ブロックに追加する = お客さんがテーブルを選んで席につく
- 「お店にテーブルがいくつもあり、次々とやってくるお客さんは、テーブルに座っているお客さんの数によって座るテーブルを決める」というイメージ
CRPの一般化
- 上記の考え方はとても有用で、いろんな派生が存在しているみたい
- 確率の式の変形
- 料理の概念の追加(Labeled CRP)
- など?
確率の式の変形
- 2つのパラメータαとθで一般化する
- n+1番目のお客さんが座る席を決める
- 新しいテーブルに座る確率 :
- すでにあるテーブルに座る確率 :
- |B| : 人が座っているテーブルの数
- |b| : そのテーブルに座っているお客さんの数
- α,θ : パラメータ(ただし、確率測度であるために制約がある)
料理の概念の追加
- 各テーブルには一つの料理が置かれ、お客さんは座ったテーブルにおいてある料理を食べる
- この料理が何になるか?は、基底測度G0によって選ばれる
- G0は何らかの確率分布(一様分布とか)
2パラメータなCRPを試してみる
パラメータによる変化を見てみるために下記のコードを試してみた。
コード
#include <fstream> #include <sstream> #include <iostream> #include <vector> #include <algorithm> class ChineseRestaurantProcess { std::vector<int> tables; //テーブルに座っている人数 int N; //人数 //ハイパーパラメータ double alpha; //discount double theta; //strength //乱数 // 注意: longではなくint(32bit)にすべき unsigned long xor128(){ static unsigned long x=123456789,y=362436069,z=521288629,w=88675123; unsigned long t; t=(x^(x<<11));x=y;y=z;z=w; return( w=(w^(w>>19))^(t^(t>>8)) ); } //現在のテーブルの状態から座るテーブルを決定 size_t selectTable(){ double z = (xor128()%1000000001)/1000000000.0; size_t ret = 0; for(size_t i=0; i<tables.size(); i++){ double pi = (tables[i]-alpha)/static_cast<double>(N+theta); z -= pi; if(z < 0.0) return ret; ret++; } return ret; } public: ChineseRestaurantProcess(double alpha, double theta): alpha(alpha),theta(theta) { tables.push_back(1); //最初の客は必ず最初のテーブルに座る N = 1; //最初はぼっち飯 } //お客さんを一人追加 void addCustomer(){ size_t idx = selectTable(); if(idx>=tables.size()){ //new table tables.push_back(1); }else{ //otherwise tables[idx]++; } N++; } //gnuplot用output void gnuplot_output(std::string filename){ std::ofstream ofs(std::string(filename + ".plt").c_str()); std::vector<int> copy_tables(tables); std::sort(copy_tables.begin(), copy_tables.end(), std::greater<int>()); ofs << "set terminal png size 300,300" << std::endl; ofs << "set output \"" << filename << ".png\"" << std::endl; ofs << "set title \"" << filename << "\"" << std::endl; ofs << "set size ratio 1" << std::endl; ofs << "set nokey" << std::endl; ofs << "set xrange [1:100000]" << std::endl; ofs << "set yrange [1:100000]" << std::endl; ofs << "set logscale xy" << std::endl; ofs << "plot \"-\" w lp" << std::endl; for(size_t i=0; i<copy_tables.size(); i++){ ofs << copy_tables[i] << std::endl; } ofs << "e" << std::endl; } }; int main(){ const double alpha = 1.0; const double theta = 10.0; ChineseRestaurantProcess crp(alpha, theta); //1000000人のお客さんに来てもらう for(int i=1; i<1000000; i++){ crp.addCustomer(); } std::stringstream ss; ss << alpha << "_" << theta; crp.gnuplot_output(ss.str()); return 0; }
応用
関連モデル
- Stick Breaking Process
- Polya's urn model
参考ページ
- http://en.wikipedia.org/wiki/Chinese_restaurant_process
- http://cog.brown.edu/~mj/classes/cg168/slides/ChineseRestaurants.pdf
- http://d.hatena.ne.jp/b3s/20081027
- http://www.cs.princeton.edu/courses/archive/fall07/cos597C/scribe/20070921.pdf
- http://www.kecl.ntt.co.jp/as/members/yamada/dpm_ueda_yamada2007.pdf
- http://www.kecl.ntt.co.jp/as/members/ueda/pdf/CVIM-ueda.pdf
- http://d.hatena.ne.jp/nozyh/20110606/1307179168