tag:blogger.com,1999:blog-85372047967532075212024-03-06T13:51:57.406+09:00uchiumi log日々の日記や自然言語処理関連の話題について書いていこうかなと思います。K.Uchiumihttp://www.blogger.com/profile/00353844307088495181noreply@blogger.comBlogger23125tag:blogger.com,1999:blog-8537204796753207521.post-55027174792758238072013-07-19T20:21:00.000+09:002013-07-19T20:21:11.222+09:00Denso IT Laboratory に技術ブログができました。本日より Denso IT Laboratory に<a href="http://tech.d-itlab.co.jp/">技術ブログ</a>ができました。
僕も早速初投稿です。
といっても勉強がてら自分用にメモした言語モデルのまとめスライドへの slideshare のリンクを張っただけですが。
K.Uchiumihttp://www.blogger.com/profile/00353844307088495181noreply@blogger.com0tag:blogger.com,1999:blog-8537204796753207521.post-37214749744819334642013-04-07T03:21:00.004+09:002013-04-07T03:38:36.552+09:00google sparse_hashmap を使ってみる自作のハッシュにバグがあるのだけど、作り直している余裕も時間もないため、google sparse_hash_map を使うことにした。
決め手は自分でシリアライザを書けばポインタを持つ構造体やクラスも保存/読み込みが可能なこと。<br />
<br />
とりあえずやってみたコードを以下に書いておく。<br />
公式のドキュメントにあるシリアライザのコードが動かないので結構苦戦した。<br />
<br />
<script type='syntaxhighlighter' class='brush: c++'><![CDATA[
# include <google/sparse_hash_map>
# include <iostream>
# include <cstdio>
# include <ext/hash_map>
using namespace std;
using google::sparse_hash_map;
typedef tr1::hash<string> StrHashFunc;
typedef tr1::hash<int> IntHashFunc;
struct eqstr
{
bool operator() (const string& a, const string& b) const
{
return (a == b);
}
};
struct eqint
{
bool operator() (const int a, const int b) const
{
return (a == b);
}
};
struct StrIntSerializer
{
bool operator() (FILE*& fp, const pair<const string, int>& value)
{
const unsigned char size = value.first.length();
if (fwrite(&size, 1, 1, fp) != 1)
return false;
if (fwrite(value.first.data(), size, 1, fp) != 1)
return false;
if (fwrite(&value.second, sizeof(value.second), 1, fp) != 1)
return false;
return true;
}
bool operator() (FILE*& fp, const pair<const string, int>* value)
{
unsigned char size;
if (fread(&size, 1, 1, fp) != 1)
return false;
char* buf = new char[size];
if (fread(buf, size, 1, fp) != 1)
{
delete[] buf;
return false;
}
new(const_cast<string*>(&value->first)) string(buf, size);
delete[] buf;
if (fread(const_cast<int*>(&value->second), sizeof(value->second), 1, fp) != 1)
return false;
return true;
}
};
typedef sparse_hash_map<string, int, StrHashFunc, eqstr> month_t;
typedef sparse_hash_map<int, int, IntHashFunc, eqint> imap_t;
int main(int argc, char **argv)
{
month_t months;
months["january"] = 31;
months["february"] = 28;
months["march"] = 31;
months["april"] = 30;
months["may"] = 31;
months["june"] = 30;
months["july"] = 31;
months["august"] = 31;
months["september"] = 30;
months["october"] = 31;
months["november"] = 30;
months["december"] = 31;
cout << "september -> " << months["september"] << endl;
cout << "april -> " << months["april"] << endl;
cout << "june -> " << months["june"] << endl;
cout << "november -> " << months["november"] << endl;
imap_t imap;
imap[1] = 10;
imap[10] = 20;
imap[11] = 30;
cout << "imap 1 -> " << imap[1] << endl;
cout << "imap 10 -> " << imap[10] << endl;
cout << "imap 11 -> " << imap[11] << endl;
FILE *fp = fopen("hashtable.data", "wb");
months.serialize(StrIntSerializer(), fp);
imap.write_metadata(fp);
imap.write_nopointer_data(fp);
fclose(fp);
month_t months2;
imap_t imap2;
FILE *fp_in = fopen("hashtable.data","rb");
months2.unserialize(StrIntSerializer(), fp_in);
imap2.read_metadata(fp_in);
imap2.read_nopointer_data(fp_in);
fclose(fp_in);
cout << "september -> " << months2["september"] << endl;
cout << "april -> " << months2["april"] << endl;
cout << "june -> " << months2["june"] << endl;
cout << "november -> " << months2["november"] << endl;
cout << "imap2 1 -> " << imap2[1] << endl;
cout << "imap2 10 -> " << imap2[10] << endl;
cout << "imap2 11 -> " << imap2[11] << endl;
return 0;
}
]]></script>
<script type='syntaxhighlighter' class='brush: c++'><![CDATA[
% ./a.out
september -> 30
april -> 30
june -> 30
november -> 30
imap 1 -> 10
imap 10 -> 20
imap 11 -> 30
september -> 30
april -> 30
june -> 30
november -> 30
imap2 1 -> 10
imap2 10 -> 20
imap2 11 -> 30
]]></script>
K.Uchiumihttp://www.blogger.com/profile/00353844307088495181noreply@blogger.com0tag:blogger.com,1999:blog-8537204796753207521.post-36713298539935450802012-10-20T02:06:00.001+09:002020-07-22T22:50:42.691+09:00SHDCRF の実装をアップしておきました公開するかどうか悩んだのですが、公開することにしました。<br />
<br />
<a href="http://code.google.com/p/uchumik/">http://code.google.com/p/uchumik/</a><br />
<br />
説明のスライドも slideshare にアップしています。
ただいまコンバート中なのでまたそちらは後ほど。<br />
<br />
コンバート終わったようなので埋め込み。<br />
何故か17ページ目の周辺化の式でΣが出なくなっている様子。<br />
コンバートのエラーかな。<br />
<iframe allowfullscreen="allowfullscreen" frameborder="0" height="356" marginheight="0" marginwidth="0" scrolling="no" src="https://www.slideshare.net/slideshow/embed_code/14804008" style="border-width: 1px 1px 0; border: 1px solid #CCC; margin-bottom: 5px;" width="427"> </iframe> <br />
<div style="margin-bottom: 5px;">
<strong> <a href="http://www.slideshare.net/uchumik/shdcrf" target="_blank" title="Shdcrf">Shdcrf</a> </strong> from <strong><a href="http://www.slideshare.net/uchumik" target="_blank">uchumik</a></strong> </div>
<br />
<br />
スライドは元々 DSIRNLP#3 での 20 分の発表用に作ったため、CRF や Forward-Backward アルゴリズム、 Viterbi アルゴリズムは聴衆者の方々が理解していることを前提としています。<br />
なので、いきなり読んでも非常に分かりにくいものとなっています。<br />
先に TokyoNLP で発表した CRF の資料を見てもらうと多少分かりやすくなるかと思います。<br />
<br />
<a href="http://uchiumi.blogspot.jp/2011/06/crf_25.html">http://uchiumi.blogspot.jp/2011/06/crf_25.html</a><br />
<br />
今日は眠いので説明はまた今度。K.Uchiumihttp://www.blogger.com/profile/00353844307088495181noreply@blogger.com0tag:blogger.com,1999:blog-8537204796753207521.post-7571223030820395592011-12-10T04:24:00.004+09:002011-12-10T11:10:04.586+09:00Ragnarok Online の AI に機械学習を入れてみたGit hub を使えと言われたのだけど、使うのに登録やら設定やらしなければいけないのが面倒なので止めました。<br />一発ネタの Lua スクリプトを公開するためだけだしなぁ。<br /><br />ということで久しぶりにブログをアップしてファイル添付をしようと思ったのですが、何と Blogger ではファイルの添付は出来ない模様。<br /><br />短いとは言えコメント入れたら800行とか行くので、とりあえず Lua で書いた ML の部分だけ。<br /><br /><pre class="prettyprint"><br />function dotproduct(v1,v2)<br /> local val = 0<br /> for k,v in pairs(v1) do<br /> if (v2[k]) then<br /> val = val + v * v2[k]<br /> end<br /> end<br /> return val<br />end<br /><br />function logsumexp(x,y)<br /> if (not x) then<br /> return y<br /> end<br /> if (x == y) then<br /> return x + 0.69314718055<br /> end<br /> local min = math.min(x,y)<br /> local max = math.max(x,y)<br /> if (max > min + 50) then<br /> return max<br /> else<br /> return max + math.log(math.exp(min-max)+1)<br /> end<br /><br />end<br /><br />MLR = {} -- multiclass logistic regression<br />BLR = {} -- binary class logistic regression<br /><br />MLR.new = function(num,r,c)<br /> local obj = {}<br /> obj.class = num<br /> obj.eta = r<br /> obj.pcache = {}<br /> obj.cc = c*obj.eta<br /> for i = 1, num, 1 do<br /> obj[i] = {} -- parameters<br /> obj.pcache[i] = {} -- penalties<br /> end<br /><br /> obj.predict = function(self, fv)<br /> local max = nil<br /> local pc = 1<br /> for i = 1, self.class, 1 do<br /> local v = dotproduct(fv,self[i])<br /> max = max or v<br /> if (v > max) then<br /> max = v<br /> pc = i<br /> end<br /> end<br /> return pc<br /> end<br /><br /> obj.update = function(self,cc,fv)<br /> local values = {}<br /> local z = nil<br /> for i = 1, self.class, 1 do<br /> values[i] = dotproduct(fv,self[i])<br /> z = logsumexp(z,values[i])<br /> end<br /> for i = 1, self.class, 1 do<br /> local grad = -math.exp(values[i]-z)<br /> if (i == cc) then<br /> grad = grad + 1<br /> end<br /> for k,v in pairs(fv) do<br /> if (type(v) == "number") then<br /> if (self[i][k]) then<br /> self[i][k] = self[i][k] + self.eta * grad * v<br /> else<br /> self[i][k] = self.eta * grad * v<br /> end<br /> if (not self.pcache[i][k]) then<br /> self.pcache[i][k] = 0<br /> end<br /> local p = self.cc - self.pcache[i][k]<br /> self.pcache[i][k] = self.cc<br /> self[i][k] = self[i][k]/(1+p) -- 本当は正則化のペナルティも減衰させるんだけどとりあえず定数でやっておく<br /> end<br /> end<br /> end<br /> self.cc = self.cc + self.eta<br /> end<br /><br /> return obj<br />end<br /><br />BLR.new = function(r)<br /> local obj = {}<br /> obj.eta = r<br /><br /> obj.predict = function(self,fv)<br /> local v = dotproduct(fv,self)<br /> return 1/(1+math.exp(-v))<br /> end<br /><br /> obj.update = function(self,cc,fv) -- cc:1(correct) or 0(incorrect)<br /> local grad = cc - self:predict(fv)<br /> for k,v in pairs(fv) do<br /> if (type(v) == "number") then<br /> if(self[k]) then<br /> self[k] = self[k] + self.eta * grad * v<br /> else<br /> self[k] = self.eta * grad * v<br /> end<br /> end<br /> end<br /> end<br /><br /> return obj<br />end<br /><br />----<br />-- A learning to rank Algorithm<br />-- Top 1 ListNet<br />----<br />ListNet = {}<br />ListNet.new = function(r,c)<br /> local obj = {}<br /> obj.eta = r<br /> obj.pcache = {}<br /> obj.cc = c*obj.eta<br /><br /> obj.predict = function(self,instances)<br /> local values = {}<br /> local max = nil<br /> local id = nil<br /> for k,v in pairs(instances) do<br /> values[k] = dotproduct(v,self)<br /> max = max or values[k]<br /> id = id or k<br /> if (values[k] > max) then<br /> max = values[k]<br /> id = k<br /> end<br /> end<br /> if (not max) then<br /> return<br /> end<br /> return id<br /> end<br /><br /> obj.update = function(self,instances,c)<br /> local values = {}<br /> local z = nil<br /> for k,v in pairs(instances) do<br /> values[k] = dotproduct(v,self)<br /> z = logsumexp(z,values[k])<br /> end<br /> for k,v in pairs(instances) do<br /> local grad = -math.exp(values[k] - z)<br /> if (c == k) then<br /> grad = grad + 1<br /> end<br /> for i,w in pairs(v) do<br /> if (self[i]) then<br /> self[i] = self[i] + self.eta * grad * w<br /> else<br /> self[i] = self.eta * grad * w<br /> end<br /> if (not self.pcache[i]) then<br /> self.pcache[i] = 0<br /> end<br /> local p = self.cc - self.pcache[i]<br /> self.pcache[i] = self.cc<br /> self[i] = self[i]/(1+p) -- 本当は正則化のペナルティも減衰させるんだけどとりあえず定数でやっておく<br /> end<br /> end<br /> self.cc = self.cc + self.eta<br /> end<br /> return obj<br />end<br /></pre><br /><br />学習データがストリームとしてやってくるので、学習率のスケジューリングなどは今回はやっていません。<br />また、FOBOSによるL2正則化の累積ペナルティが追加学習を行う時にはリセットされています。<br />何でこんなことになっているのかというと、面倒くさかったというのももちろんある(というか大半はそれ)のですが、Ragnarok Online ではマップごとで出現する敵のタイプが異なるため、敵に関するアクティブになる素性は明らかにマップごとに分かれます。<br />そのため、ペナルティの累積を常に残しておくと、各マップで学習を行って以前行ったことのあるマップに行くと、AI の学習をさせた途端その事例に含まれる素性のパラメータが全て0になってしまい最初からやり直し・・・ということが想定されるので、そういう風にならないようにしています。<br />これはどういうことかというと、行動やスキルのモデルファイルは1つに見えますが、実際にはマップごとに別々にモデルを学習しているのと同じ事になっています。<br /><br />Ragnarok Online の AI 部分については使ってみたいという希望者が居るようなら改めて公開考えてみます。K.Uchiumihttp://www.blogger.com/profile/00353844307088495181noreply@blogger.com0tag:blogger.com,1999:blog-8537204796753207521.post-61335319620081572392011-06-25T07:24:00.003+09:002011-06-25T07:28:48.028+09:00間違ってるかもしれないCRFの説明<div style="width:425px" id="__ss_8416551"> <strong style="display:block;margin:12px 0 4px"><a href="http://www.slideshare.net/uchumik/crf-8416551" title="Crfと素性テンプレート">Crfと素性テンプレート</a></strong> <iframe src="http://www.slideshare.net/slideshow/embed_code/8416551" width="425" height="355" frameborder="0" marginwidth="0" marginheight="0" scrolling="no"></iframe> <div style="padding:5px 0 12px"> View more <a href="http://www.slideshare.net/">presentations</a> from <a href="http://www.slideshare.net/uchumik">uchumik</a> </div> </div><br /><br />TokyoNLP で話す内容を貼っておきます。<br />きっと後で訂正が入るんだろうな。。K.Uchiumihttp://www.blogger.com/profile/00353844307088495181noreply@blogger.com0tag:blogger.com,1999:blog-8537204796753207521.post-82829076386553253842011-06-12T22:12:00.004+09:002011-06-12T23:15:44.550+09:00実数素性テンプレートの使える CRFこの間記事で書いた実数素性テンプレートを使える CRF を作ってみました。<br /><a href="https://code.google.com/p/cnf/source/browse/#hg%2Ftoys%2Frtcrf%2Fsrc">google code</a><br /><br />面倒くさかったのでだいぶコピペの目立つコードになってますが、その辺は勘弁。<br /><br /><ul><li>テンプレートが文字列を扱うバイナリな値を取る場合(%x マクロを使うもの)ならば、素性関数 f_k(y_{i-1},y_i,X) は 1 or 0 を取る。</li><li>テンプレートが実数を扱う場合(%r や SUM, MAX, Value, etc. )ならば、素性関数 f_k(y_{i-1},y_i,X) はテンプレートに書かれた演算結果の値 or 0 を取る。</li></ul>と、実数素性を使わない CRF からの変更点はそれだけです。<div><br /><br />以下は動作確認のためだけのコード。<br /><pre class="prettyprint"><br /># include "rtcrflearn.hpp"<br /># include "rtcrftagger.hpp"<br /><br />using namespace RtCrf;<br />using namespace std;<br /><br />int main(int argc, char **argv)<br />{<br /> Crflearn learner(*(argv+1),*(argv+2),1000000);<br /> learner.init();<br /><br /> learner.learn(5,0);<br /> learner.save("test.model");<br /><br /> Crftagger tagger(*(argv+1),1000000);<br /> tagger.read("test.model");<br /> tagger.tagging(*(argv+2));<br /> return 0;<br />}<br /></pre><br /><br />下が動かしたときの出力。<br /><pre class="prettyprint"><br /># ./test template ../data/train.txt<br />labels: 2<br />bound: 3<br />ufeatures: 17<br />bfeatures: 1<br />instance: 1<br />uparameters: 34<br />bparameters: 8<br />model parameters: 42<br />epoch: 0 err:0.500000(2/4)<br />epoch: 1 err:0.000000(0/4)<br />epoch: 2 err:0.000000(0/4)<br />epoch: 3 err:0.000000(0/4)<br />epoch: 4 err:0.000000(0/4)<br />a b c 1.0 -1.0 L_A L_A<br />d e f -1.0 1.0 L_B L_B<br />p e f -0.5 0.1 L_B L_B<br />g b c 0.3 -0.1 L_A L_A<br /></pre><br />実数素性を使えそうな公開データとかあればそれで試してみたいですけど、とりあえず今日の所はここまで。<br /><br /></div><div>Tagger クラスの出力に予測時のスコアを出すようにすれば、1段目の CRF の出力を2段目の CRF の学習素性に使うというようなスタッキングも出来るようになるかなと思う次第。</div><div>そのうち CNF とかと一緒にまとめて API を変えとこうかな。</div>K.Uchiumihttp://www.blogger.com/profile/00353844307088495181noreply@blogger.com0tag:blogger.com,1999:blog-8537204796753207521.post-49054319805645832012011-06-08T02:27:00.007+09:002011-06-08T03:47:00.533+09:00実数素性テンプレートを作ろう。まだ生きてます。<br /><br />ここしばらくはちと研究のまねごとなどを会社でしている事もあって、あんまりブログに書ける事も無い日々です。<br />とりあえず対外的に問題ない範囲だと、去年の11月にACMLという会議でポスター発表をしてみました。<br />ポスターはアブストの提出だけだったのですが、参考文献が後で見直したら壊れていたという大失敗。<br /><br />後、同じ11月にNL研でクエリの訂正手法についての発表をしてきました。<br />広島のお好み焼きはおいしかったです。<br /><br />あと、 NetWalker を活用するべくいくつか実装したものもあるのですが、そちらはまだしばらく非公開。<br /><br />とりあえず生存報告はこんな所。<br />最近忙しいですが何となく以前よりは楽しく過ごせている気がします。<br /><br />で、久しぶりに書いた記事がこれで終わりというのもなんなので、近々 TokyoNLP で発表しようかなと思っている CRF の素性テンプレート周りの話に関連して少々追記。<br /><br />素性テンプレートが使える CRF というと、CRF++ や crfsgd がありますが、基本的にこれらの素性テンプレートは文字列を素性とするタイプで、素性関数は f_k(y,x) = 1 or 0 というようにバイナリの値を取るようになっています。<br />これはある素性関数は特定の y と x のペアが観測された時に 1 を返して、それ以外のときは 0 を取るという意味で、素性がテキストの時は特に問題になる事はありません。<br />が、最近はクエリログとかを扱う機会も多くなってきた都合上、出来れば実数値も扱いたいなーと思う事がしばしばです。<br />例えば、クエリに含まれるトークンにタグ付けをする際には、トークンの尤度を言語モデルで求めて、それを素性として扱いたいとかがあると思うのですが、それは現状だと数値の範囲を表す様なテキストに変更して素性として扱うしかなさそうです。<br />ですが、この方法だとスコアの範囲ごとに素性が分かれるので、やっぱり元々の言語モデルで求めた尤度を直接素性として扱うのとは違いそうです。<br /><br />なので、実数の扱える素性テンプレートを作ってみました。<br /><a href="http://code.google.com/p/cnf/source/browse/#hg%2Ftoys%2Frtcrf%2Fsrc">http://code.google.com/p/cnf/source/browse/#hg%2Ftoys%2Frtcrf%2Fsrc</a><br />動作としては、 f_k(y,テキスト素性) = 1 or 0、 f_k(y,実数素性) = 素性の実数値 or 0 となります。<br /><br />もちろん、素性テンプレートというからには観測された素性の組み合わせを扱えないと格好が悪いので、実数素性でも組み合わせ(素性どうしの各種演算等)が扱えるようにしました。<br /><br />で、ついでに素性テンプレートが今まで学習器とセットで独立性が低かった点も見直してみました。<br /><br />使える演算としては、<br /><ul><li>( x )</li><li>x + x</li><li>x - x</li><li>x * x</li><li>x / x</li><li>x % x</li><li>-x</li><li>log x</li><li>sqrt x</li><li>exp x</li><li>max ( x,... )</li><li>min ( x,... )</li><li>sum ( x,... )</li><li>prod ( x,... )</li><li>x > x</li><li>x >= x</li><li>x < x</li><li>x <= x</li></ul><br />をひとまず用意。 <br />上の例だと x は変数に見えますが、別の演算を x の所に入れる事も出来るので、<br /><pre class="prettyprint"><br />(x_1+x_2)*x_3*max(max(...),min(...),x_4,log(x_5),...)<br /></pre><br />みたいな事も出来ます。<br /><br />とりあえず実際に適当に書いてみたテンプレートの例が以下です。<br /><br /><pre class="prettyprint"># Unigram<br />U00:%x[-2,0]<br />U01:%x[-1,0]_%x[0,0]<br />U02:%x[-1,0]_%x[0,0]_%x[1,0]<br />U03:1.0-%r[0,3]<br />U04:%r[0,3]+%r[0,4]<br />U05:%r[0,3]-%r[0,4]<br />U06:%r[0,3]*-%r[0,4]<br />U07:%r[0,3]/-%r[0,4]<br />U08:%r[0,3]%-%r[0,4]<br />U09:SUM(%r[.,3])<br />U10:PROD(%r[.,4])<br />U11:MIN(%r[0,3],%r[0,4])<br />U12:MAX(%r[0,3],%r[0,4])<br />U13:MIN(%r[.,3])<br />U14:MAX(%r[.,4])<br />U15:MIN(%r[0,.])<br />U16:MAX(%r[0,.])<br />U17:MIN(MAX(%r[0,.],%r[.,3]),%r[.,4])<br />U18:%r[0,3]/MAX(%r[.,3])<br />U19:%r[0,3]/SUM(%r[.,3])<br />U20:MIN(%r[0,3]+%r[0,4],%r[0,3])<br /><br /># Bigram<br />B<br /></pre><br />で、入力に使ったこれまた適当データが次。<br /><pre class="prettyprint">a b c 1.0 -1.0 L_A <br />d e f -1.0 1.0 L_B <br />p e f -0.5 0.1 L_B <br />g b c 0.3 -0.1 L_A<br /></pre><br />これに上のテンプレートを使って素性を展開すると、次のようになります。<br /><pre class="prettyprint">U00:_B-2:1 U01:_B-1_a:1 U02:_B-1_a_d:1 U05:2 U06:1 U07:1 U09:-0.2 U10:0.01 U11:-1 U12:1 U13:-1 U14:1 U15:-1 U16:1 U17:-1 U18:1 U19:-5 B:1 <br />U00:_B-1:1 U01:a_d:1 U02:a_d_p:1 U03:2 U05:-2 U06:1 U07:1 U09:-0.2 U10:0.01 U11:-1 U12:1 U13:-1 U14:1 U15:-1 U16:1 U17:-1 U18:-1 U19:5 U20:-1 B:1 <br />U00:a:1 U01:d_p:1 U02:d_p_g:1 U03:1.5 U04:-0.4 U05:-0.6 U06:0.05 U07:5 U08:-0.1 U09:-0.2 U10:0.01 U11:-0.5 U12:0.1 U13:-1 U14:1 U15:-0.5 U16:0.1 U17:-1 U18:-0.5 U19:2.5 U20:-0.5 B:1 <br />U00:d:1 U01:p_g:1 U02:p_g__E+1:1 U03:0.7 U04:0.2 U05:0.4 U06:0.03 U07:3 U08:0.1 U09:-0.2 U10:0.01 U11:-0.1 U12:0.3 U13:-1 U14:1 U15:-0.1 U16:0.3 U17:-1 U18:0.3 U19:-1.5 U20:0.2 B:1 <br /></pre><br />実装 は c++ で、 bison、 flex を使って作っています。<br />tsubosaka さんからはこのくらいの仕様なら bison、 flex なんて使わないでいいんじゃないの?というありがたいお言葉を頂いたのですが、残念ながら僕は 1 からパーザを書いた経験が無く、正直自信が無いためこれらを使っています。<br />ちなみに flex のバージョンが 2.5.35 、bison が 2.3 の環境で作っています。<br />bison は 2.1 以降なら c++ に対応しているので問題ないと思いますが、 flex の方は 2.5.35 だと yyFlexLexer::yywrap の実装がデフォルトで無いため、yywrap の実装を適当にソースコードの中に書いています。<div>これが 2.5.4 以降だと標準で実装されているので、多分コンパイル時に多重定義で怒られるんじゃないかなぁという気がします。<br /><br />今週はきっとこれ使った CRF 実装してます。</div>K.Uchiumihttp://www.blogger.com/profile/00353844307088495181noreply@blogger.com0tag:blogger.com,1999:blog-8537204796753207521.post-8195073755219120602010-08-05T00:29:00.007+09:002010-08-05T02:31:01.696+09:00bigram feature について。twitter で @takeda25 さんが指摘されていたのですが, CRF で f(y_{i-1},y_{i},x_{i}) という観測素性とラベル bigram の組みまで考慮した素性関数があまり使われないのは, 素性数が増えるからというよりは計算に時間が掛かるためだろうか? という事を私なりに考えてみました.<br /><br />CRF の1事例に対するパラメータ推定に掛かるオーダーはラベル数L, 系列長Tの時に, O(L^2T) です. <br />forward-backward でラティス中の位置 i, ラベル j のノードの alpha を計算する際には以下の logsumexp の計算を行います.<br /><pre class="prettyprint"><br />for (k = 0; k < L; ++k)<br /> alpha_{i,j} = logsumexp(alpha_{i,j}, alpha_{i-1,k}+cost)<br /></pre><br />この計算を全ての i, j について行います.<br /><br />この時足し込んでいる cost が 観測素性+遷移素性のコストで, 観測素性 x に対してラベル bigram を考慮する場合, 遷移素性のコスト計算をする時に一緒に足す事になるため, 単純に観測された bigram 素性の数だけ加算回数が増えます. <br />ですが, logsumexp の呼び出し回数自体は変わりません. (最初この部分も定数倍になると勘違いしていました)<br /><br />cost の加算部分だけが定数倍される程度なので, 計算時間にはそんなに影響が出ないのではないかと考えたのですが, 実は CNF だと結構影響が出ました...<br /><br />CNF では cost の計算時に, logstic 関数による非線形な重み付けが必要になるため, exp の計算が入ります. exp の計算が素性数だけ増えるため, 試してみたら結構遅くなりました..<br /><br />試してみたのは CoNLL2000 の学習データで, 使用したテンプレートは以下の通りです. 見事に全部 bigram テンプレートです.<br /><pre class="prettyprint"><br /># Bigram<br />B00:%x[-2,0,0]<br />B01:%x[-1,0,0]<br />B02:%x[0,0,0]<br />B03:%x[1,0,0]<br />B04:%x[2,0,0]<br />B05:%x[-1,0,0]/%x[0,0,0]<br />B06:%x[0,0,0]/%x[1,0,0]<br /><br />B10:%x[-2,1,0]<br />B11:%x[-1,1,0]<br />B12:%x[0,1,0]<br />B13:%x[1,1,0]<br />B14:%x[2,1,0]<br />B15:%x[-2,1,0]/%x[-1,1,0]<br />B16:%x[-1,1,0]/%x[0,1,0]<br />B17:%x[0,1,0]/%x[1,1,0]<br />B18:%x[1,1,0]/%x[2,1,0]<br /><br />B20:%x[-2,1,0]/%x[-1,1,0]/%x[0,1,0]<br />B21:%x[-1,1,0]/%x[0,1,0]/%x[1,1,0]<br />B22:%x[0,1,0]/%x[1,1,0]/%x[2,1,0]<br /><br />B<br /></pre><br /><br />これで学習を行ってみたところ, 次のようになりました.<br /><br />* iter = 1<br /><pre class="prettyprint"><br />time ./src/learner --learner=cnf -t src/template -c data/conll2000/train.txt -s bigramtest.save --l2 --pool=1000000 --block=24 --lambda=2 --iter=1 <br />labels: 22<br />features: 338552<br />bound: 3<br />ufeatures: 0<br />bfeatures: 76329<br />instance: 8936<br />gate functions: 20<br />parameters of gate functions: 31<br />uparameters: 0<br />bparameters: 40301712<br />model parameters: 40301743<br />epoch: 0 err:0.069070(14624/211727)<br />./src/learner --learner=cnf -t src/template -c data/conll2000/train.txt -s 1323.90s user 41.84s system 96% cpu 23:40.72 total<br /></pre><br /><br />ちなみに, テンプレートを元々の<br /><pre class="prettyprint"><br />U00:%x[-2,0,0]<br />U01:%x[-1,0,0]<br />U02:%x[0,0,0]<br />U03:%x[1,0,0]<br />U04:%x[2,0,0]<br />U05:%x[-1,0,0]/%x[0,0,0]<br />U06:%x[0,0,0]/%x[1,0,0]<br /><br />U10:%x[-2,1,0]<br />U11:%x[-1,1,0]<br />U12:%x[0,1,0]<br />U13:%x[1,1,0]<br />U14:%x[2,1,0]<br />U15:%x[-2,1,0]/%x[-1,1,0]<br />U16:%x[-1,1,0]/%x[0,1,0]<br />U17:%x[0,1,0]/%x[1,1,0]<br />U18:%x[1,1,0]/%x[2,1,0]<br /><br />U20:%x[-2,1,0]/%x[-1,1,0]/%x[0,1,0]<br />U21:%x[-1,1,0]/%x[0,1,0]/%x[1,1,0]<br />U22:%x[0,1,0]/%x[1,1,0]/%x[2,1,0]<br /><br /># Bigram<br />B<br /></pre><br /><br />にすると, iter = 1 の学習時間は以下の通り.<br /><br /><pre class="prettyprint"><br />time ./src/learner --learner=cnf -t src/template -c data/conll2000/train.txt -s bigramtest.save --l2 --pool=1000000 --block=24 --lambda=2 --iter=1<br />labels: 22<br />features: 338552<br />bound: 3<br />ufeatures: 76328<br />bfeatures: 1<br />instance: 8936<br />gate functions: 20<br />parameters of gate functions: 31<br />uparameters: 1679216<br />bparameters: 528<br />model parameters: 1679775<br />epoch: 0 err:0.057314(12135/211727)<br />./src/learner --learner=cnf -t src/template -c data/conll2000/train.txt -s 67.23s user 5.61s system 92% cpu 1:18.67 total<br /></pre><br /><br />だいたい bigram テンプレート数倍だけ遅くなりました..<br />exp の計算が bigram テンプレート数倍必要になるので, そこが影響したようです.<br /><br />CRF の場合は logistic 関数の計算無しで加算のみになるため, ここまでの影響は出ないと考えられるので, bigram 素性を使ったとしてもそこまで遅くなるという事は無いのではないかと思います.<br /><br />ちなみにオール bigram テンプレートを使った時の学習に必要なメモリは約 450 MB でした.<br /><br />bigram 素性を使うとタグ付け時にトークンの文脈依存性を上手く学習できたりしそうですし, 意味の無い素性ではないと思うのですが, 何であまり使われていないんでしょうね.<br /><br />ちなみに, CRFSuite では高速化の為にいろいろな工夫がされているそうで, その工夫の中にラベル bigram と観測素性の組み合わせは考慮しないという前提に基づいた手法があるのか, bigram 素性を扱うと高速化手法のどれかが使えなくなってしまうっぽい?<br />ソースコードを落としてきてちら見してみたのですが, 人のコードを読むのが苦手な私ではすぐには問題箇所が分からず, 「おー! コード超キレイ!」とそんな感想しか言えませんでした...<br /><br />時間のある時にじっくり読んで勉強させてもらいます!K.Uchiumihttp://www.blogger.com/profile/00353844307088495181noreply@blogger.com2tag:blogger.com,1999:blog-8537204796753207521.post-8539280308469875022010-07-09T22:44:00.006+09:002010-07-22T00:01:01.619+09:00a hybrid markov/semi-markov cnf名前だけ知りつつもどんな物なのか知らなかったので、5月に <a href="http://www.cs.cmu.edu/~wcohen/postscript/semiCRF.pdf">semi-markov crf の論文</a> を読んでみました。<br /><br />semi-crf は入力系列に対する最適なセグメンテーションを学習する学習器です。<br />crf が P(Y|X, W) を求めていたのに対して、semi-crf では P(S|X, W) を推定します。<br /><br />文章で書くと、2つの違いは<br /><br />* crf では入力系列 X に対して生成されうるすべてのラベル系列 Y' に対して、正解系列とそれ以外の系列を弁別するよう学習する<br />* semi-crf では入力系列 X に対して生成されうるすべてのセグメンテーション結果 S' に対して、正解のセグメンテーションとそれ以外のセグメンテーションを弁別するよう学習する<br /><br />ということになります。<br /><br />semi-crf では入力系列が与えられた際に、t_j , ... , u_j までを1つのセグメント s_j として、1つのラベルを割り当てます。<br />この時、t_j 、 u_j は常に以下を満たす必要があります。<br /><br />1 <= t_j <= u_j <= |S| , t_{j+1} = u_j + 1<br /><br />これは、セグメントの終了位置と次のセグメントの開始位置は一致するよということです。<br />で、crf で使われていた素性関数 f の代わりに segment feature function g ( g^k(y_j,y_{j-1},X,t_j,u_j) )を使います。<br />といっても、あまり大きく変わっているという事は無くて、crf の素性関数がトークン x_i とラベル y_i のペアに対して 1 or 0 を返していたのが、セグメント s_i とラベル y_i のペアに変わった程度です。<br /><br />大きな違いは以上なのですが、パラメータ推定をする際のラティスの形が違います。<br /><br />crf ではラティスはきれいな格子でしたが、semi-crf では各位置 i について、i で始まるセグメントと i で終了するセグメントのみ接続を許すため、結構複雑な形をしています。<br />ただ、これは形態素解析の物と同じなので <a href="http://amzn.to/9zIsEp">自然言語処理-基礎と応用-</a> の1章、形態素解析の 1.1.5 節あたりを読むと分かりやすいと思います。<br /><br />パラメータ推定は crf と同じ方法が使えます。<br /><br />とりあえずここまで理解したところで、cnf で作ってみよう! と考えて作り始めたのですが、いざ作ってみて失敗に気づきました。<br /><br />semi-crf ではセグメント単位で同じラベルをふるため、ラベル付けは IOB とかじゃ無く、トークン列に対して AAA、BBB のようにつければ良いやと思っていたのですが、いざ動かす所で、正解の与え方ではまりました。<br />というのも、AA, AA, AA というラベル列を正解コーパスでつけたつもりでも、AAA, AAA、AAAAAA のどれとも区別が出来なくなってました。。<br /><br />それで、方向を修正し、結局ラベル付けは IOB タグに限定する事にして(cnf ではそんな事はないのですが...セグメントの開始位置を区別しつつ、cnf と同じフォーマットのコーパスを入力に取れるようにする方法が他に思いつきませんでした。)、せっかく IOB を使うのだから、従来使えていた cnf と同じ素性も使えるようにしてみました。<br /><br />IOB タグを使い、cnf で使っていたトークン素性を使えるようにするという事は、あるトークン x がセグメントの開始 B になりやすいだとか、また逆に B にはなりにくいという様な情報を学習できます。<br />さらに、 cnf では素性テンプレートを使い、カレントの位置 i の n 個前のトークンが何であったか等も素性として扱えるため、セグメントの外にあるトークンの情報も扱えるという利点があります。<br /><br />まさにこれと同じ事をやっているっぽい論文<br /><br /><a href="http://aclweb.org/anthology/W/W06/W06-1655.pdf">A Hybrid Markov/Semi-Markov Conditional Random Field for Sequence Segmentation</a><br /><br />があったので、名前をお借りして a hybrid markov/semi-markov cnf と名付けてみました。<br />上の論文自体はまだちゃんと読んで理解したわけではないので、自信を持って同じとも違うとも言えないのですが、ちらっと読んだところ、私と同じくセグメント素性にトークン素性を追加したのではなく、セグメント素性をトークン素性の和で表しているように見えます。<br />そうするとコストの計算時に、トークン素性のコストを予め計算しておいて、各セグメントで共通する部分についてはキャッシュを使うという事が出来るようになります。<br />ちゃんと読んでからでないと断言はできませんが、あってたらそこが私の作った学習器との違いになりそうです。<br /><br />とりあえず、コードは以前 cnf をアップした所にそのまま追加しました。<br /><br /><a href="http://code.google.com/p/cnf/">http://code.google.com/p/cnf/</a><br /><br />semi* が追加部分です。<br /><br />素性テンプレートは以下のようになっています。<br /><pre class="prettyprint"><br /># Unigram Segment<br />S:%s[15,0]<br /><br /># Bigram Segment<br />T<br /><br /># Unigram<br />U00:%x[-2,0,0]<br />U01:%x[-1,0,0]<br />U02:%x[0,0,0]<br />U03:%x[1,0,0]<br />U04:%x[2,0,0]<br />U05:%x[-1,0,0]/%x[0,0,0]<br />U06:%x[0,0,0]/%x[1,0,0]<br /><br />U10:%x[-2,1,0]<br />U11:%x[-1,1,0]<br />U12:%x[0,1,0]<br />U13:%x[1,1,0]<br />U14:%x[2,1,0]<br />U15:%x[-2,1,0]/%x[-1,1,0]<br />U16:%x[-1,1,0]/%x[0,1,0]<br />U17:%x[0,1,0]/%x[1,1,0]<br />U18:%x[1,1,0]/%x[2,1,0]<br /><br />U20:%x[-2,1,0]/%x[-1,1,0]/%x[0,1,0]<br />U21:%x[-1,1,0]/%x[0,1,0]/%x[1,1,0]<br />U22:%x[0,1,0]/%x[1,1,0]/%x[2,1,0]<br /><br /># Bigram<br />B<br /></pre><br /><br />トークン素性については以前と変わりありません。<br /><pre class="prettyprint"><br /># Unigram Segment<br />S:%s[15,0]<br /><br /># Bigram Segment<br />T<br /></pre><br />の部分が追加したテンプレートの仕様で、<br /><br />S:%s[ length , gate function に与える重みの初期値 ]<br /><br />が unigram segment のテンプレート、<br /><br /> T:%s[ length , gate function に与える重みの初期値 ] <br /><br />が bigram segment のテンプレートになります。<br />length は考慮するセグメントの最大長です。<br /><br />S はセグメントの S ですが、 T は良い頭文字が思いつかなかったので、とりあえず T にしました。<br />トークン素性の bigram の B 同様、セグメントのラベル遷移だけを素性にする場合は T だけ書いておけば OK です。<br /><br />ソースを落としてきたら、<br /><pre><br /># make<br /># ./src/semicnflearn src/semitemplate data/conll2000/train.txt semicnf.save<br /># ./src/semicnftagger src/semitemplate semicnf.save data/conll2000/test.txt<br /></pre><br />みたいにして使います。<br /><br />コーパスは従来の系列ラベリングで使えた物は大抵使えるんじゃないかと思いますが、セグメンテーションに適した手法なのでどうせならちゃんとしたタスクのコーパスで評価したいところですね。<br /><br />調べてみた所、公開されている semi-crf の実装もあまり無いようですし、素性テンプレートの使える semi-markov cnf はそこそこ貴重なのではないかと自分で思ってみたりしています。<br /><br />今回から、学習器の学習率のスケジューリングとL1 正則化は<br /><a href="http://www.aclweb.org/anthology/P/P09/P09-1054.pdf">Stochastic Gradient Descent Training for L1-regularized Log-linear Models with Cumulative Penalty</a><br />の手法に変更しました。<br /><br />@unnonouno さんのブログ <br /><a href="http://unnonouno.blogspot.com/2010/02/l1-sgd.html">http://unnonouno.blogspot.com/2010/02/l1-sgd.html </a><br />で紹介されていた手法で、説明が秀逸ですw<br /><br />[2010.7.21 追記]<br />template の仕様を変更しました。<br />以前の仕様では、unigram segment と bigram segment で各1つずつしかテンプレートを記述できなかったのですが、複数のテンプレートを記述できるようにしました。<br />それに伴って、<br /><br />S:%s[ length , gate function に与える重みの初期値 ]<br /><br />から、<br /><br />S:%s[ length, col, 重みの初期値]<br /><br />と、col を指定できるようにテンプレートのマクロも変更しています。<br /><br />おまけで、新しい template の例を以下に。<br /><pre class="prettyprint"><br /># Unigram Segment<br />S01:%s[15,0,0]<br />S02:%s[15,1,0]<br /><br /># Bigram Segment<br />T<br /><br /># Unigram<br />U00:%x[-2,0,0]<br />U01:%x[-1,0,0]<br />U02:%x[0,0,0]<br />U03:%x[1,0,0]<br />U04:%x[2,0,0]<br />U05:%x[-1,0,0]/%x[0,0,0]<br />U06:%x[0,0,0]/%x[1,0,0]<br /><br />U10:%x[-2,1,0]<br />U11:%x[-1,1,0]<br />U12:%x[0,1,0]<br />U13:%x[1,1,0]<br />U14:%x[2,1,0]<br />U15:%x[-2,1,0]/%x[-1,1,0]<br />U16:%x[-1,1,0]/%x[0,1,0]<br />U17:%x[0,1,0]/%x[1,1,0]<br />U18:%x[1,1,0]/%x[2,1,0]<br /><br />U20:%x[-2,1,0]/%x[-1,1,0]/%x[0,1,0]<br />U21:%x[-1,1,0]/%x[0,1,0]/%x[1,1,0]<br />U22:%x[0,1,0]/%x[1,1,0]/%x[2,1,0]<br /><br /># Bigram<br />B<br /></pre>K.Uchiumihttp://www.blogger.com/profile/00353844307088495181noreply@blogger.com0tag:blogger.com,1999:blog-8537204796753207521.post-19062616855702081122010-06-13T17:09:00.003+09:002010-06-13T18:11:15.362+09:00グラフラプラシアンで推薦以前縁あって小町さんと一緒に仕事をさせてもらい論文に名前を載せてもらったのですが、会社だけでなく自宅でもちょっと使いたいなーということもあり、実装してみることにしました。<br /><br />参考にしたのは以下の論文です。<br /><a href="http://cl.naist.jp/~komachi/papers/jsai2010-click.pdf">ラプラシアンラベル伝播による検索クリックスルーログからの意味カテゴリ獲得</a><br /><br />元論文と違うのは、インスタンス-パターン行列の要素を単純な頻度から別の尺度に変えている点です。<br />元々そのまんま実装してみたところ、非常にレアな場合なのですが、ジェネリックパターン1つのみと共起するようなインスタンスがあった場合に、これが上位に出やすくなるという問題が発生し、どうにかできないかなと模索していたところ、小町さんからアドバイスを頂き、それを基に手を加えています。<br /><br />とりあえず動作検証のために<a href="http://www.grouplens.org/node/73">MovieLens Data Sets</a>を使って実験してみました。<br /><br />最初にデータのフォーマットをツールの入力形式へ変更。<br />perl swap.pl < ../ml-data/u.data | LC_ALL='C' sort > mlens.data<br /><br /><pre class="prettyprint"><br />#!/opt/local/bin/perl<br /><br /># user id | item id | rating | timestamp<br />use warnings;<br />use strict;<br /><br />my %dic;<br />my $file = "../ml-data/u.item";<br />open (F, $file) or die;<br />while (<F>)<br />{<br /> chomp;<br /> my @t = split(/\|/);<br /><br /> unless (defined $dic{$t[0]})<br /> {<br /> $dic{$t[0]} = $t[1];<br /> }<br />}<br />close (F);<br />while (<>)<br />{<br /> chomp;<br /> my @tokens = split(/\t/);<br /> print $dic{$tokens[1]}, "\t$tokens[0]\t$tokens[2]\n";<br />}<br /></pre><br /><br />入力ファイルのフォーマットは次のようにTSV形式で<インスタンス-パターン-評価値>として、インスタンスでソートしておきます。<br /><br /><pre><br />'Til There Was You (1997) 152 4<br />'Til There Was You (1997) 178 3<br />'Til There Was You (1997) 223 1<br />'Til There Was You (1997) 299 2<br />'Til There Was You (1997) 342 1<br />'Til There Was You (1997) 416 3<br />'Til There Was You (1997) 530 2<br />'Til There Was You (1997) 532 3<br />'Til There Was You (1997) 782 2<br />...<br /></pre><br /><br />で、実行。<br /><br />./quetchup -f mlens.data -o test.save <br /><br />デフォルトでの実行は alpha = 0.0001, iteration = 10, エッジのカットに使う閾値 0.1 としています。<br /><br />./quetchup -c sample.txt -r test.save | head -50 <br /><br />sample.txt<br /><pre><br />Snow White and the Seven Dwarfs (1937)<br /></pre><br /><br />白雪姫がお気に入りとすると、以下のようにディズニー系の映画を上位にレコメンドすることができます。<br /><br /><pre><br />[Instance]<br />Snow White and the Seven Dwarfs (1937):0.999801<br />Cinderella (1950):5.19753e-07<br />Fantasia (1940):5.06323e-07<br />Dumbo (1941):4.17243e-07<br />Beauty and the Beast (1991):3.94127e-07<br />Aladdin (1992):3.88318e-07<br />Mary Poppins (1964):3.71035e-07<br />Jungle Book, The (1994):3.67852e-07<br />Sword in the Stone, The (1963):3.67118e-07<br />Lion King, The (1994):3.64219e-07<br />Pinocchio (1940):3.60772e-07<br />Robin Hood: Prince of Thieves (1991):3.5665e-07<br />Aristocats, The (1970):3.51889e-07<br />Fox and the Hound, The (1981):3.34974e-07<br />Star Trek VI: The Undiscovered Country (1991):3.24104e-07<br />Alice in Wonderland (1951):3.19468e-07<br />Homeward Bound: The Incredible Journey (1993):2.97177e-07<br />Sound of Music, The (1965):2.97109e-07<br />Walk in the Sun, A (1945):2.96338e-07<br />Very Natural Thing, A (1974):2.96338e-07<br />Bedknobs and Broomsticks (1971):2.93205e-07<br />Star Trek IV: The Voyage Home (1986):2.90837e-07<br />Gone with the Wind (1939):2.89412e-07<br />Nightmare Before Christmas, The (1993):2.85079e-07<br />Star Trek: Generations (1994):2.8431e-07<br />Winnie the Pooh and the Blustery Day (1968):2.82242e-07<br />101 Dalmatians (1996):2.81954e-07<br />Swiss Family Robinson (1960):2.6257e-07<br />Aladdin and the King of Thieves (1996):2.54459e-07<br />Grease (1978):2.53118e-07<br />African Queen, The (1951):2.52688e-07<br />Ghost (1990):2.52399e-07<br />Hunt for Red October, The (1990):2.4501e-07<br />Miracle on 34th Street (1994):2.42372e-07<br />Home Alone (1990):2.4004e-07<br />Top Gun (1986):2.35881e-07<br />Abyss, The (1989):2.35302e-07<br />Little Women (1994):2.33329e-07<br />Old Yeller (1957):2.32874e-07<br />Pocahontas (1995):2.31783e-07<br />Mrs. Doubtfire (1993):2.31762e-07<br />Jumanji (1995):2.31231e-07<br />Pete's Dragon (1977):2.29198e-07<br />Christmas Carol, A (1938):2.28481e-07<br />New York Cop (1996):2.28269e-07<br />Maltese Falcon, The (1941):2.26072e-07<br />Goofy Movie, A (1995):2.25177e-07<br />James and the Giant Peach (1996):2.23962e-07<br />Star Trek: The Motion Picture (1979):2.23878e-07<br /></pre><br /><br />ですが、よく見るとスタートレックとかが入り込んでいます。<br />そこで、次のように嫌いなものリストを用意します。<br /><br />sample2.txt<br /><pre><br />Walk in the Sun, A (1945)<br />Star Trek: Generations (1994)<br /></pre><br /><br />./quetchup -c sample.txt -n sample2.txt -r test.save | head -50 <br /><br /><pre><br />[Instance]<br />Snow White and the Seven Dwarfs (1937):0.9998<br />Cinderella (1950):2.96839e-07<br />Fantasia (1940):2.81548e-07<br />Dumbo (1941):2.54658e-07<br />Aladdin (1992):2.4008e-07<br />Lion King, The (1994):2.30137e-07<br />Beauty and the Beast (1991):2.10186e-07<br />I Don't Want to Talk About It (De eso no se habla) (1993):2.01313e-07<br />Jurassic Park (1993):1.96396e-07<br />Coldblooded (1995):1.95535e-07<br />Robin Hood: Prince of Thieves (1991):1.74025e-07<br />Fox and the Hound, The (1981):1.71627e-07<br />African Queen, The (1951):1.69855e-07<br />Sound of Music, The (1965):1.69781e-07<br />Mary Poppins (1964):1.64425e-07<br />Wizard of Oz, The (1939):1.6242e-07<br />Swan Princess, The (1994):1.56013e-07<br />Homeward Bound: The Incredible Journey (1993):1.53814e-07<br />Mamma Roma (1962):1.52174e-07<br />Good Man in Africa, A (1994):1.50378e-07<br />Some Like It Hot (1959):1.46221e-07<br />Citizen Kane (1941):1.42743e-07<br />Anne Frank Remembered (1995):1.40035e-07<br />Dances with Wolves (1990):1.36046e-07<br />It's a Wonderful Life (1946):1.3514e-07<br />Hunt for Red October, The (1990):1.3501e-07<br />Young Frankenstein (1974):1.34608e-07<br />Rebecca (1940):1.32806e-07<br />Winnie the Pooh and the Blustery Day (1968):1.30989e-07<br />It Happened One Night (1934):1.30592e-07<br />M (1931):1.30189e-07<br />Maltese Falcon, The (1941):1.29532e-07<br />Lassie (1994):1.28929e-07<br />Foreign Correspondent (1940):1.28287e-07<br />Grease (1978):1.2729e-07<br />Alice in Wonderland (1951):1.23873e-07<br />Quiz Show (1994):1.22688e-07<br />Singin' in the Rain (1952):1.22222e-07<br />Sword in the Stone, The (1963):1.18912e-07<br />Lawrence of Arabia (1962):1.15236e-07<br />To Kill a Mockingbird (1962):1.14732e-07<br />Swiss Family Robinson (1960):1.14698e-07<br />Drop Dead Fred (1991):1.13925e-07<br />Vertigo (1958):1.13817e-07<br />Top Gun (1986):1.12622e-07<br />Bonnie and Clyde (1967):1.12262e-07<br />Blue Angel, The (Blaue Engel, Der) (1930):1.10351e-07<br />Annie Hall (1977):1.09473e-07<br />Turning, The (1992):1.07052e-07<br /></pre><br /><br />と、無事スタートレックとか消えてくれました(スタートレック好きな人ごめんなさい!)。<br />ちなみに、head で出力を絞っているので見えていませんが、出力にはスコア付きのパターンリストも含まれます。<br />これで、どんなパターンで各インスタンスがつながっているのかも分かりやすいです。<br /><br />お気に入りリストが1つしかなくても十分な精度が出そうだし、複数あっても無問題。<br />嫌いなものあればそれに近いものを推薦候補から外しやすい。<br /><br />この手法で推薦とか悪くないなと思う今日この頃な訳です。<br /><br />ソースコードを公開したいのですが、Quetchup アルゴリズムは一応特許が取られているはずなので、それがどんな内容か分からないと不安だったりします。。K.Uchiumihttp://www.blogger.com/profile/00353844307088495181noreply@blogger.com0tag:blogger.com,1999:blog-8537204796753207521.post-12299408026541006092010-03-29T23:21:00.005+09:002010-04-01T22:46:48.021+09:00gibbs sampling3月に入って netwalker を購入しました。<br /><br />電車通勤の時間を利用してコードを書きたいなと思っていたのですが、なかなかよろしい感じです。<br />といってもキーボードは使いやすいとは言い難く、長いコードは書きたくないですが...<br /><br />ということで、netwalker で作成したプログラム第一号を公開します。<br />第一号は gibbs sampling を使ったモチーフ抽出アルゴリズムの実装です。<br /><br />前々から gibbs sampling について調べてはいたのですが、適度な練習問題が無く実装はしていませんでした。<br />今月になって購入した<a href="http://www.kyoritsu-pub.co.jp/shinkan/shin0702_04.html">バイオインフォマティクスの数理とアルゴリズム</a>にちょうど良い例があったので、それを実装してみました。<br /><br />そもそもモチーフって何という話ですが、一言で言うとタンパク質の配列パターンの事を指すそうです。<br />私は専門ではないので詳しい事は分かりませんが、ここでは与えられた複数のタンパク質配列から、特徴的な配列パターンを見つけてくる事をモチーフ抽出と言ってよさそうです。<br /><br />具体的にどうモチーフを抽出するかですが、載っている例では、各タンパク質A、C、T、Gの出現確率をP(A)、P(C)、P(T)、P(G)、部分配列中の位置 j に各タンパク質が現れる確率を f_j(A)、f_j(C)、f_j(T)、f_j(G)とした時に、相対エントロピーを最大化するように部分配列を選んでくる事でモチーフを抽出します。<br /><br />これをローカルマルチプルアラインメントと呼ぶそうですが、この問題はまともにやると NP 困難なので、gibbs sampling を使って効率的に解くよ、というお話しになっています。<br /><br />という事で、アルゴリズムは以下の通り。<br /><br />1. 各入力配列 s_k から, 長さ L の部分配列 t_k を一様ランダムに選択 (k = 1,...,n)<br />2. 入力配列から, s_i を一様ランダムに選択<br />3. f'_j(a) を t_i 意外の t_k から計算したモチーフ領域の j 番目の列に置ける文字 a の頻度とする ( f'_j(a) = (|{k|t_k[j] = a, k != i}|) / (n-1))<br />4. s_i から長さ L の部分配列 t'_i を, Πj f'_j(t'_i[j])/p(t'_i[j]) に比例する確率でランダムに選択<br />5. t_i を t'_i で置き換える<br />6. 2-5 を十分な回数繰り返す<br /><br />ステップ 3. の頻度は確率の間違えかな?と思うのですが、一応そのまま書いておきます。<br />ステップ 4. では求めた事後分布に従って新しくモチーフ候補を選択し直すわけですが、ここはランダムで選択しないと初期値から動かなくなるので、まず計算したスコアの配列を確率分布として使えるように総和が1となるように正規化してから、<a href="http://ibisforest.org/index.php?PRML">PRML</a>の下巻に紹介されている棄却サンプリングを使いました。<br />提案分布には一様分布を使っています。<br /><br />コードと実行例は以下。<br /><br /><pre class="prettyprint"><br />#!/usr/local/bin/perl<br /><br />use warnings;<br />use strict;<br /><br />my $L = 5;<br />my @dataset;<br />my %wordset;<br />my $words = 0;<br /><br />while(<>)<br />{<br /> chomp;<br /> my $line = $_;<br /> my @sequence = split(//,$line);<br /> push @dataset,\@sequence;<br />}<br /><br /># init words and probs<br />$words = &setwords(\%wordset, \@dataset);<br />&initwordprobs($words,\%wordset);<br />my $datasize = @dataset;<br /><br />print "dataset\n";<br />foreach my $ar (@dataset)<br />{<br /> print "@$ar\n";<br />}<br /><br /># init motif set<br />my @pos;<br />foreach my $ar (@dataset)<br />{<br /> my $p = int rand(@$ar-$L);<br /> push @pos, $p;<br /> #print $p,"\n";<br />}<br /><br /># output first<br />print "init\n";<br />for (my $i = 0; $i < @dataset; $i++)<br />{<br /> my $ar = $dataset[$i];<br /> my $p = $pos[$i];<br /> for (my $j = $p; $j < $p+$L; $j++)<br /> {<br /> print "$ar->[$j] ";<br /> }<br /> print "\n";<br />}<br /><br /># iterration<br />for (my $iter = 0; $iter < 50; $iter++)<br />{<br /> #print "epoch: $iter\n";<br /> my $id = int rand($datasize);<br /> my %wordsfreq;<br /># wordset * position<br /># +---+-------------+<br /># | |a|b|c|d|e|f|g|<br /># +---+-------------+<br /># | 0 | |<br /># | 1 | |<br /># | 2 | frequency |<br /># | . | |<br /># | L | |<br /># +---+-------------+<br /> &initwordsfreq(\%wordset,$L,\%wordsfreq);<br /># count word frequency<br /> &countwordfreq($id,\@dataset,\%wordsfreq,\@pos);<br /># pick up new motif i<br /> my $ar = $dataset[$id];<br /> $pos[$id] = &pickup($id,$ar,$L,\%wordsfreq, \%wordset);<br />}<br /><br /># output<br />print "result\n";<br />for (my $i = 0; $i < @dataset; $i++)<br />{<br /> my $ar = $dataset[$i];<br /> my $p = $pos[$i];<br /> for (my $j = $p; $j < $p+$L; $j++)<br /> {<br /> print "$ar->[$j] ";<br /> }<br /> print "\n";<br />}<br /><br /># subroutine<br />sub pickup<br />{<br /> my $id = shift;<br /> my $ar = shift;<br /> my $L = shift;<br /> my $wordsfreqhr = shift;<br /> my $wordsethr = shift;<br /><br /> my @scores;<br /> my $total = 0;<br /> for (my $p = 0; $p <= @$ar-$L; $p++)<br /> {<br /> my $score = 1;<br /> my $index = 0;<br /> for (my $j = $p; $j < $p+$L; $j++)<br /> {<br /> my $w = $ar->[$j];<br /> $score *= $wordsfreqhr->{$index}->{$w}/$wordsethr->{$w};<br /> $index++;<br /> }<br /> push @scores, $score;<br /> $total += $score;<br /> }<br /> return int rand(@scores) if $total == 0;<br /> for(my $i = 0; $i < @scores; $i++)<br /> {<br /> $scores[$i] /= $total;<br /> }<br /> #print "@scores\n";<br /> my $flg = 1;<br /> while($flg)<br /> {<br /> my $npos = int rand(@scores);<br /> my $s = rand(1);<br /> if ($s <= $scores[$npos])<br /> {<br /> $flg = 0;<br /> return $npos;<br /> }<br /> }<br />}<br />sub countwordfreq<br />{<br /> my $id = shift;<br /> my $datasetar = shift;<br /> my $wordsfreqhr = shift;<br /> my $posar = shift;<br /><br /> for (my $i = 0; $i < @$datasetar; $i++)<br /> {<br /> next if ($i == $id);<br /> my $ar = $datasetar->[$i];<br /> for (my $j = $posar->[$i]; $j < $L+$posar->[$i]; $j++)<br /> {<br /> my $p = $j-$posar->[$i];<br /> my $w = $ar->[$j];<br /> $wordsfreqhr->{$p}->{$w}++;<br /> }<br /> }<br /> for (my $l = 0; $l < $L; $l++)<br /> {<br /> foreach my $k (keys %{$wordsfreqhr->{$l}})<br /> {<br /> $wordsfreqhr->{$l}->{$k} /= (@$datasetar-1);<br /> }<br /> }<br />}<br /><br />sub initwordsfreq<br />{<br /> my $wordsethr = shift;<br /> my $L = shift;<br /> my $wordfhr = shift;<br /> for (my $i = 0; $i < $L; $i++)<br /> {<br /> my %freqonpos;<br /> foreach my $k (keys %$wordsethr)<br /> {<br /> $freqonpos{$k} = 0;<br /> }<br /> $wordfhr->{$i} = \%freqonpos;<br /> }<br />}<br />sub initwordprobs<br />{<br /> my $words = shift;<br /> my $wordsethr = shift;<br /><br /> foreach my $k (keys %$wordsethr)<br /> {<br /> $wordsethr->{$k} = 1/$words;<br /> }<br />}<br /><br />sub setwords<br />{<br /> my $wordshr = shift;<br /> my $dataar = shift;<br /> my $words = 0;<br /><br /> foreach my $ar (@$dataar)<br /> {<br /> foreach my $w (@$ar)<br /> {<br /> unless (defined $wordshr->{$w})<br /> {<br /> $wordshr->{$w} = defined;<br /> $words++;<br /> }<br /> }<br /> }<br /> return $words;<br />}<br /></pre><br /><br />サンプルデータ1<br /><pre><br />c a t c g a a a a a a<br />a a a a c a t c g a a<br />a a c a t c g a a a a<br />a a a a a a c a t c g<br /></pre><br /><br />サンプルデータ2<br /><pre><br />t c a g a g a t t c g t a g<br />t t c a t t c g g g c g t<br />c g a t t c g c g a c t c<br />t g a a t t c g g a a<br /></pre><br /><br />実行例1<br /><pre><br /># perl gibbs.pl < sample1.txt<br />dataset<br />c a t c g a a a a a a<br />a a a a c a t c g a a<br />a a c a t c g a a a a<br />a a a a a a c a t c g<br />init<br />g a a a a <br />a a a c a <br />a t c g a <br />a a a a a <br />result<br />c a t c g <br />c a t c g <br />c a t c g <br />c a t c g <br /></pre><br /><br />実行例2<br /><pre><br /># perl gibbs.pl < sample2.txt<br />dataset<br />t c a g a g a t t c g t a g<br />t t c a t t c g g g c g t<br />c g a t t c g c g a c t c<br />t g a a t t c g g a a<br />init<br />t c a g a <br />c a t t c <br />t c g c g <br />a a t t c <br />result<br />a t t c g <br />a t t c g <br />a t t c g <br />a t t c g <br /></pre><br /><br />上手い事特徴的な文字列を抽出できる事が確認できました。K.Uchiumihttp://www.blogger.com/profile/00353844307088495181noreply@blogger.com0tag:blogger.com,1999:blog-8537204796753207521.post-88203995309280320882010-02-28T06:21:00.005+09:002010-03-25T23:28:20.727+09:00Conditional Neural Fields on Google Code<div>CNF の著者の Jian Peng 氏に特許について質問をしてみたところ、問題ないということでしたので Google Code にプロジェクトを作成してコードを公開しました。</div><div><br /></div><a href="http://code.google.com/p/cnf">http://code.google.com/p/cnf/</a><div><br /></div><div>あまりちゃんとした実装ではないので、使用は自己責任でお願いします。</div><div>間違ってるかもしれないので、間違いがあれば教えてくれると嬉しいです。</div><div><br /></div><div>mercurial で管理しているので、以下のコマンドで落としてきて使用できます。</div><div><br /></div><div>$ hg clone https://cnf.googlecode.com/hg/ cnf </div><div>$ cd cnf</div><div>$ make</div><div>$ ./src/cnflearn src/template data/conll2000/train.txt test.save</div><div>$ ./src/cnftagger src/template test.save data/conll2000/test.txt</div><div><br /></div><div>cnflearn_main.cpp と cnftagger_main.cpp を見てもらえば分かる通り、実行用のプログラムはむちゃくちゃ手抜です。</div><div><br /></div><div>logsumexp の計算部分には、NAIST の浅原先生の資料にあるコードを使わせて頂きました。</div><div><a href="http://cl.aist-nara.ac.jp/index.php?plugin=attach&refer=DMLA%2F2005%C7%AF%C5%D9&openfile=2005-06-07.pdf">Sequential Tagging メモ</a></div><div><br /></div>K.Uchiumihttp://www.blogger.com/profile/00353844307088495181noreply@blogger.com0tag:blogger.com,1999:blog-8537204796753207521.post-53258527377264361622010-02-16T23:10:00.004+09:002010-02-17T01:18:39.777+09:00Conditional Neural Fields年越ししてから既に2ヶ月が過ぎ、2月も終わりが見えかけてきた今日この頃です。<br />生存報告をかねて、少しだけ最近やっていた事を書いておきます。<br /><br />去年の年末くらいに、面白そうな論文を見つけたのでそれを読みつつ、実装していました。<br />NIPS2009 で発表された論文です。<br /><br />その名も Conditional Neural Fields 。<div><a href="http://books.nips.cc/papers/files/nips22/NIPS2009_0935.pdf">http://books.nips.cc/papers/files/nips22/NIPS2009_0935.pdf</a><br /><br />名前から何か感じるところの有る人もいそうですが、これはCRFに隠れ層を加えて、非線形にした物になります。<br /><br />自分のメモ用に、先に簡単に CRF についておきます。<br />--<br />CRF の説明はNLP2006のチュートリアル資料が割と分かりやすいです。<br /><a href="http://nlp.dse.ibaraki.ac.jp/~shinnou/lecture/nl/NLP2006.pdf">http://nlp.dse.ibaraki.ac.jp/~shinnou/lecture/nl/NLP2006.pdf</a><br /><br />私はオンライン学習器でしかCRFを作った事が無いので、ここではそのつもりで書きます。<br />CRF では素性関数に対応するパラメータを学習するわけですが、その式は大雑把に書くと次のようになります。<br /><br />Λ_t+1 = Λ_t + η * (正解の素性関数ベクトル - 期待素性関数ベクトル)<br /><br />η は学習率、Λ は素性関数に対応するパラメータベクトルです。<br />正解の素性関数ベクトルは入力系列が与えられれば素性と正解のラベルのペアを取り出すだけで作れます。<br />要はどの素性とラベルのペアが何個有ったか、どのラベルとラベルの遷移が何回あったか、をカウントしてベクトルにしています。<br /><br />期待素性関数ベクトルはちょっと計算が面倒ですが、現在のパラメータΛのもとで、入力系列から生起しうる全てのラベル系列を考えます。<br />各ラベル系列が生起した際の、各素性関数の期待値を計算し足し合わせた物が期待素性関数ベクトルです。<br />まともに全てのラベル系列を生成して試すともの凄い計算量になるので、これは Forward-Backward アルゴリズムを用いて計算するのが一般的です。<br />--<br /><br />前置きが長くなりましたが、 CNF について書きます。<div>ちなみに間違ってるかもしれません。あまり期待しては行けません。</div><div>この記事は後でこっそり修正されてる可能性もあります。</div><div><br />CRF との違いは、観測素性の扱いになります。<br />CRF では、素性関数 f(x,y) は素性xとラベルy のペアを観測した際に1になるような関数です。<br /><br />これが CNF では、f(h(X,t),y) になります。<br /><br />数式は元論文のままだと比較しにくいので変えてます。<br />関数h はロジスティック関数で、X に対して非線形な値を取ります。<br />t は系列のカレントの位置を表しています。<br /><br />f(h(X,t),y) は組み合わせ素性xとラベルy のペアを観測した際に、h(X,t)を返す関数と思ってください。<br />X が大文字になっている理由は、CNF では入力系列に対して隠れ層で組み合わせ素性を取り出すためです。<br />素性関数 f(h(X),y) は入力系列 X に対して、位置tに置ける組み合わせ素性 x とラベル y のペアに対して、h(X,t) を返します。<br /><br />h(X,t) は中で何をしているのかと言うと、入力系列 X とパラメータθベクトルの内積を計算して、その値をロジスティック関数に乗せて非線形な値にしています。<br /><br />こうする事で、単純に組み合わせ素性が観測されたというだけではなく、その素性に対して非線形な重みを与えています。<br /><br />次に、パラメータの最適化ですが、実は素性関数が非線形になっているという事を無視すると、観測素性についてのパラメータと、遷移素性についてのパラメータの更新の式は一緒になります。<br /><br />ただし、正解の素性関数ベクトルはどの素性とラベルのペアが何個有ったか、どのラベルとラベルの遷移が何回あったか、ではなく、素性に対して非線形な重みを計算し足し合わせたものになります。ラベルとラベルの遷移は相変わらずただのカウントです。<br />要は、カウントの仕方が1ずつ足していたのが、実数値ずつ足すようになった物です。<br /><br />期待素性関数ベクトルも同じ要領です。 入力系列から生成されうる全てのラベル系列を考えて、各ラベル系列が生起した際の各素性関数の期待値を計算して足し合わせます。 ただし期待値を計算する際には素性に対して、これまで有った/無かったで済ませていたところにh関数の値が入ります。<br /><br />パラメータ推定での1つ大きな違いは、これに加えて隠れ層のパラメータθについても更新することです。<br /><div><br /></div><div>θの更新は、次の式で行われます。</div><div>θt+1 = θt + Σt w_yt,g * ∂h/∂θ - E (Σt w_yt^,g * ∂h/∂θ)</div><div><br /></div><div>wは素性関数に対応した重みパラメータです。</div><div>重みパラメータΛ = (W, U, θ) と思ってください。</div><div>Wは観測素性に対応する重みパラメータ</div><div>Uは遷移素性に対応する重みパラメータ</div><div>θは隠れ層のパラメータ</div><div>E は期待値です。</div><div><br /></div><div>かなりやっつけな式なので詳しくは元論文を読んだ方が良いです。</div><div>要は、正解系列中の観測素性の重みベクトルと、入力系列から生成されうる全ラベル系列を考えた時の各観測素性の期待値ベクトルの差をなくすようにθを更新しています。</div><div><br /></div><div>という事で、実装してみました。</div><div>実装は sgd + FOLOS です。</div><div><br /></div><div>コード公開したいんだけど、権利関係やら何やら良く分からない。</div><div>論文の著者にメールして特許についてとか聞いといた方が良いと言われているのですが、そこで停止中。</div><div><br /></div><div>--</div><div>ちなみに、CNF は組み合わせ素性、CRF は組み合わせ無し、のような印象を受けますが、CRF++やCrfSgdを使った事が有る方は分かると思いますが、CRF でも素性テンプレートを使って組み合わせ素性を扱えます。</div><div><br /></div><div>素性テンプレートを使った組み合わせ素性の抽出はまさに CNF で言うところの中間層だと思っています。</div><div>なので、CNF と CRF の違いは実際のところロジスティック関数による非線形な値を観測素性に与えているところと、θの学習が入っているところだというのが私の理解です。</div><div><br /></div><div>私の実装もそのまんまで、「ロジスティック関数による非線形な値を観測素性に与えているところと、θの学習を入れた」素性テンプレートを使える CRF です。</div><div><br /></div><div>--</div><div>conll2000 のチャンキングで比較をしてみたので一応載せておきます。</div><div>一応素性テンプレートは揃えてありますが、内部での処理が微妙に違うため素性数は厳密に一緒ではありません。</div><div><br /></div><div>まあ、見ての通りだと思いますが、恐らくロジスティック関数による非線形な値云々よりも、組み合わせ素性を使えるかどうかの方が重要で、CRF でもそれが出来れば問題ないんだろうなという感じでした。</div><div><br /></div><div>元々Kernelなどを用いた非線形な分類の実体は、素性の組み合わせを考慮してより高次元空間へ写像して、その空間で線形分離しているわけで、素性テンプレートを使って組み合わせ素性を扱っている以上、あんま変わんない結果が出たとしても別段驚く事じゃない気もしますね。</div><div><br /></div><div>* CrfSgd</div><div><div>[Epoch 50] -- wnorm: 9273.81 total time: 1664.69 seconds</div><div>Training perf: sentences: 8936 loss: 4473.67 objective*n: 9110.58</div><div> misclassifications: 794(0.375011%)</div><div>accuracy: 99.62%; precision: 99.32%; recall: 99.18%; FB1: 99.25</div><div>Testing perf: sentences: 2012 loss: 4429.27 objective*n: 5473.3</div><div> misclassifications: 1893(3.99561%)</div><div>accuracy: 96.00%; precision: 93.84%; recall: 93.62%; FB1: 93.73</div><div>Saving model file model.gz.</div><div>Done! 1664.69 seconds.</div><div><br /></div><div>* CNF</div><div><div>epoch: 49 err:0.002985(632/211727)</div><div>./cnflearn template data/conll2000/train.txt testbb.save 3728.58s user 200.56s system 69% cpu 1:34:54.39 total</div><div><br /></div><div>./cnftagger template testbb.save data/conll2000/test.txt | ./conlleval </div><div><div>processed 47377 tokens with 23852 phrases; found: 23718 phrases; correct: 22293.</div><div>accuracy: 96.02%; precision: 93.99%; recall: 93.46%; FB1: 93.73</div><div> ADJP: precision: 81.08%; recall: 75.34%; FB1: 78.11 407</div><div> ADVP: precision: 83.71%; recall: 80.72%; FB1: 82.19 835</div><div> CONJP: precision: 55.56%; recall: 55.56%; FB1: 55.56 9</div><div> INTJ: precision: 100.00%; recall: 50.00%; FB1: 66.67 1</div><div> LST: precision: 0.00%; recall: 0.00%; FB1: 0.00 0</div><div> NP: precision: 94.39%; recall: 93.88%; FB1: 94.14 12355</div><div> PP: precision: 96.81%; recall: 97.78%; FB1: 97.29 4859</div><div> PRT: precision: 83.15%; recall: 69.81%; FB1: 75.90 89</div><div> SBAR: precision: 87.72%; recall: 85.42%; FB1: 86.55 521</div><div> VP: precision: 93.95%; recall: 93.62%; FB1: 93.78 4642</div><div><br /></div></div></div></div></div></div>K.Uchiumihttp://www.blogger.com/profile/00353844307088495181noreply@blogger.com0tag:blogger.com,1999:blog-8537204796753207521.post-2272281221192219122009-12-30T01:29:00.007+09:002010-03-20T00:45:49.530+09:00PLSV以前書いた plsv の perl script を晒しておく。<br />勾配法には sgd を使用した。<br />行数は320行。<br /><br /><pre class="prettyprint"><br />#!/usr/bin/perl<br /># Probabilistic Latent Semantic Visualization<br /># Copyright (c) 2009, Kei Uchiumi<br /><br />use warnings;<br />use strict;<br /><br /># Usage<br /># perl plsv.pl corpus<br /><br />our $dimension = 2;<br />our $topicsize = 2;<br />our $alpha = 0.01;<br />our $beta = 0.0001;<br />our $ganma = 0.0001 * $topicsize;<br />our $docnum = 0; # document size N<br />our $iteration = 50;<br /><br /># for sgd parameters<br />our $rate = 0.1; # learning rate<br />our $input = shift;<br /><br />my %W;<br /><br />open(F, "$input") or die "Couldn't open $input $!";<br />while (<F>)<br />{<br /> chomp;<br /> my $line = $_;<br /> my @tokens = split(/\s/,$line);<br /> &storeword(\%W, \@tokens);<br /> $docnum++;<br />}<br />close(F);<br /><br />our $wordsize = (keys %W);<br />$beta = $beta * $docnum;<br /><br /># init parameters<br />our %theta;<br />our %phi;<br />our %xai;<br /><br /># init phi<br />for (my $i = 0; $i < $topicsize; $i++)<br />{<br /> my @position;<br /> for (my $d = 0; $d < $dimension; $d++)<br /> {<br /> #$position[$d] = rand;<br /> $position[$d] = 1;<br /> }<br /> $phi{$i} = \@position;<br />}<br /># init xai<br />for (my $i = 0; $i < $docnum; $i++)<br />{<br /> my @position;<br /> for (my $d = 0; $d < $dimension; $d++)<br /> {<br /> $position[$d] = 0;<br /> }<br /> $xai{$i} = \@position;<br />}<br /># init theta<br />for (my $i = 0; $i < $topicsize; $i++)<br />{<br /> my @words;<br /> my $denominator = 0;<br /> for (my $j = 0; $j < $wordsize; $j++)<br /> {<br /> $words[$j] = -log(1.0 - rand);<br /> $denominator += $words[$j];<br /> }<br /> for (my $j = 0; $j < $wordsize; $j++)<br /> {<br /> $words[$j] = $words[$j] / $denominator;<br /> }<br /> $theta{$i} = \@words;<br />}<br /><br />our %prob_zpx;<br />our %prob_zpnm;<br /><br /># learning start<br />for (my $i = 0; $i < $iteration; $i++)<br />{<br /> &expectation($input);<br /> &maximization($input);<br />}<br /><br /># output<br />use Data::Dumper;<br />print "Result\n";<br />print "Phi\n";<br />print Dumper(%phi);<br />print "Xai\n";<br />print Dumper(%xai);<br />print "Theta\n";<br />print Dumper(%theta);<br /><br /># functions<br />sub xaiupdate<br />{<br /> my $docid = shift;<br /> my $topic = shift;<br /> my $grad = shift;<br /><br /> my $x = $xai{$docid};<br /> my $p = $phi{$topic};<br /><br /> for (my $i = 0; $i < $dimension; $i++)<br /> {<br /> my $diff = $grad * ($x->[$i] - $p->[$i]) - $ganma * $x->[$i];<br /> $x->[$i] += $rate * $diff;<br /> }<br /> return;<br />}<br /><br />sub phiupdate<br />{<br /> my $docid = shift;<br /> my $topic = shift;<br /> my $grad = shift;<br /><br /> my $x = $xai{$docid};<br /> my $p = $phi{$topic};<br /><br /> for (my $i = 0; $i < $dimension; $i++)<br /> {<br /> my $diff = $grad * ($p->[$i] - $x->[$i]) - $beta * $p->[$i];<br /> $p->[$i] += $rate * $diff;<br /> }<br /> return;<br />}<br /><br />sub update<br />{<br /> my $input = shift;<br /><br /> my $docid = 0;<br /> open(F,"$input") or die "Couldn't open $input $!";<br /> while (<F>)<br /> {<br /> chomp;<br /> my $line = $_;<br /> my @tokens = split(/\s/,$line);<br /><br /> my $p_zpnm = $prob_zpnm{$docid};<br /> for (my $i = 0; $i < @tokens; $i++)<br /> {<br /> my $p_znm = $p_zpnm->{$i};<br /> for (my $j = 0; $j < $topicsize; $j++)<br /> {<br /> my $p_zpx = $prob_zpx{$docid}->[$j];<br /> my $p_z = $p_znm->[$j];<br /> my $grad = $p_zpx - $p_z;<br /> &xaiupdate($docid,$j,$grad);<br /> &phiupdate($docid,$j,$grad);<br /> }<br /> }<br /> $docid++;<br /> }<br /> close(F);<br />}<br /><br />sub thetaupdate<br />{<br /> my $input = shift;<br /> my $topic = shift;<br /> my $word = shift;<br /><br /> my $numerator = 0;<br /> my $denominator = 0;<br /> my $docid = 0;<br /> open(F,"$input") or die "Couldn't open $input $!";<br /> while (<F>)<br /> {<br /> chomp;<br /> my $line = $_;<br /> my @tokens = split(/\s/,$line);<br /><br /> my $p_zpnm = $prob_zpnm{$docid};<br /> for (my $i = 0; $i < @tokens; $i++)<br /> {<br /> my $p_znm = $p_zpnm->{$i};<br /> if ($tokens[$i] eq $word)<br /> {<br /> $numerator += $p_znm->[$topic];<br /> }<br /> $denominator += $p_znm->[$topic];<br /> }<br /> $docid++;<br /> }<br /> close(F);<br /><br /> return ($numerator+$alpha)/($denominator+$alpha*$wordsize);<br />}<br /><br />sub maximization<br />{<br /> my $input = shift;<br /># theta update<br /> for (my $i = 0; $i < $topicsize; $i++)<br /> {<br /> for (my $j = 0; $j < $wordsize; $j++)<br /> {<br /> $theta{$i}->[$j] = &thetaupdate($input,$i,$j);<br /> }<br /> }<br /># xai, phi update<br /> &update($input);<br /><br /> return;<br />}<br /><br />sub euclid<br />{<br /> my $topic = shift;<br /> my $docid = shift;<br /><br /> my $docpositions = $xai{$docid};<br /> my $topicpositions = $phi{$topic};<br /><br /> my $d = 0;<br /> for (my $i = 0; $i < $dimension; $i++)<br /> {<br /> my $diff = $docpositions->[$i] - $topicpositions->[$i];<br /> $d += $diff * $diff;<br /> }<br /><br /> return $d;<br />}<br /><br />sub dist<br />{<br /> my $topic = shift;<br /> my $docid = shift;<br /><br /> my $denominator = 0;<br /> for (my $i = 0; $i < $topicsize; $i++)<br /> {<br /> $denominator += exp(-1/2 * &euclid($i, $docid));<br /> }<br /> my $numerator = exp(-1/2 * &euclid($topic, $docid));<br /><br /> return $numerator/$denominator;<br />}<br /><br />sub posterior<br />{<br /> my $docid = shift;<br /> my $topic = shift;<br /> my $word = shift;<br /><br /> my $p_zpx = $prob_zpx{$docid};<br /> my $denominator = 0;<br /> for (my $i = 0; $i < $topicsize; $i++)<br /> {<br /> $denominator += $p_zpx->[$i] * $theta{$i}->[$word];<br /> }<br /> my $numerator = $p_zpx->[$topic] * $theta{$topic}->[$word];<br /><br /> return $numerator/$denominator;<br />}<br /><br />sub expectation<br />{<br /> my $input = shift;<br /> for (my $i = 0; $i < $docnum; $i++)<br /> {<br /> my @probs;<br /> for (my $j = 0; $j < $topicsize; $j++)<br /> {<br /> my $prob = &dist($j,$i);<br /> $probs[$j] = $prob;<br /> }<br /> $prob_zpx{$i} = \@probs;<br /> }<br /><br /> my $docid = 0;<br /> open(F,"$input") or die "Couldn't open $input $!";<br /> while (<F>)<br /> {<br /> chomp;<br /> my $line = $_;<br /> my @tokens = split(/\s/,$line);<br /><br /> my %probs_znm;<br /> for (my $i = 0; $i < @tokens; $i++)<br /> {<br /> my @probs;<br /> for (my $j = 0; $j < $topicsize; $j++)<br /> {<br /> my $p = &posterior($docid, $j, $tokens[$i]);<br /> $probs[$j] = $p;<br /> }<br /> $probs_znm{$i} = \@probs;<br /> }<br /><br /> $prob_zpnm{$docid} = \%probs_znm;<br /> $docid++;<br /> }<br /> close(F);<br /> return;<br />}<br /><br />sub storeword<br />{<br /> my $wh = shift;<br /> my $ta = shift;<br /><br /> foreach my $w (@$ta)<br /> {<br /> unless (defined $wh->{$w})<br /> {<br /> $wh->{$w} = 1;<br /> }<br /> }<br /> return;<br />}<br /><br /></f></f></f></f></pre><br /><br />入力ファイルの例は以下の通り。<br />1行が1文書に相当。<br />数値は単語ID。<br /><pre><br />0 1 2 3<br />0 1 2 3<br />4 5 6 7<br />4 5 6 7<br /></pre><br /><br />使用例<br /><pre><br /># perl plsv.pl sample.txt<br />Result<br />Phi<br />$VAR1 = '1';<br />$VAR2 = [<br /> '-0.581827405837318',<br /> '-0.581827405837318'<br /> ];<br />$VAR3 = '0';<br />$VAR4 = [<br /> '1.50610033709651',<br /> '1.50610033709651'<br /> ];<br />Xai<br />$VAR1 = '1';<br />$VAR2 = [<br /> '-0.42461538023816',<br /> '-0.42461538023816'<br /> ];<br />$VAR3 = '3';<br />$VAR4 = [<br /> '1.29019177243395',<br /> '1.29019177243395'<br /> ];<br />$VAR5 = '0';<br />$VAR6 = [<br /> '-0.393904728506928',<br /> '-0.393904728506928'<br /> ];<br />$VAR7 = '2';<br />$VAR8 = [<br /> '1.29484945144959',<br /> '1.29484945144959'<br /> ];<br />Theta<br />$VAR1 = '1';<br />$VAR2 = [<br /> '0.248699888255414',<br /> '0.24869988511089',<br /> '0.248699888504977',<br /> '0.248699890098641',<br /> '0.00130761840347527',<br /> '0.00129605716166152',<br /> '0.00129717365378877',<br /> '0.00129959881115254'<br /> ];<br />$VAR3 = '0';<br />$VAR4 = [<br /> '0.00128080308998399',<br /> '0.00128080623499826',<br /> '0.00128080284038186',<br /> '0.00128080124646881',<br /> '0.248711689079392',<br /> '0.248723252125832',<br /> '0.248722135459428',<br /> '0.248719709923515'<br /> ];<br /></pre>K.Uchiumihttp://www.blogger.com/profile/00353844307088495181noreply@blogger.com0tag:blogger.com,1999:blog-8537204796753207521.post-46509427432335734232009-11-01T21:53:00.001+09:002009-11-01T21:55:48.179+09:00ためして寒天<a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgwPi9paSmPA2h7WTO3P5A9c6nwSvxBrfv3PiWGVFo5jZBuBbbXNqvUMAJ0lOEMNgHYPp2cSZpaoTMl506Ilebnq3EzSn_3bTVUzFKyah-YNjyx4gBbZy2PSUcZjhyphenhyphenhbM4phCkfcMgoZLnk/s1600-h/kanten.jpg"><img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 150px; height: 200px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgwPi9paSmPA2h7WTO3P5A9c6nwSvxBrfv3PiWGVFo5jZBuBbbXNqvUMAJ0lOEMNgHYPp2cSZpaoTMl506Ilebnq3EzSn_3bTVUzFKyah-YNjyx4gBbZy2PSUcZjhyphenhyphenhbM4phCkfcMgoZLnk/s200/kanten.jpg" border="0" alt="" id="BLOGGER_PHOTO_ID_5399117876391297938" /></a>買い物中に何とも言えない名前のドリンクを発見。<br /><div>このネーミングってどうなんだろう。</div>K.Uchiumihttp://www.blogger.com/profile/00353844307088495181noreply@blogger.com0tag:blogger.com,1999:blog-8537204796753207521.post-68346935156756216362009-10-07T01:37:00.002+09:002009-10-07T01:38:52.488+09:00twitter皆やってるし、勧められてもいたので twitter を始めてみた。<div>使い方が良く分からない。。</div><div><br /></div><div>なんか最初からフレンドが20人とかになってるし、どうなってるんだろう。</div><div><br /></div><div>私がそんなに大勢の友達を持っているはずが無いじゃないか。</div>K.Uchiumihttp://www.blogger.com/profile/00353844307088495181noreply@blogger.com0tag:blogger.com,1999:blog-8537204796753207521.post-36663705017719981342009-09-27T13:25:00.002+09:002009-09-27T13:45:02.026+09:00入院+iPhone9/16-9/25 まで入院していたのですが、その直前に iPhone を購入しました。<div><br /></div><div>予約したのが10日で、申し込んだのが12日、3営業日で到着予定だったのですが、15日の午後3時過ぎに発送したようで16日の入院初日に手に入れられないという事態になりました。</div><div><br /></div><div>佐川急便さんにお電話したところ、ソフトバンクに確認して、許可をもらった後に病院へ転送してくれると言ってくれたので安心していたのですが、その後落とし穴が待ってました。</div><div><br /></div><div>16日に佐川急便さんからお電話が着て、ソフトバンクが許可をくれないので直接ソフトバンクと話をしてくださいと言われ、ソフトバンクにこちらから電話をかけてみるとなんと…</div><div><br /></div><div>以下だいたいの流れ。</div><div><br /></div><div>SBオペレータ:こちら一度発送したものの届け先住所の変更は出来ません。</div><div>SBオペレータ:違う住所へ届けるためには今の分をこちらでキャンセル致しますので、その後最初からやり直して頂く必要があります。</div><div>私:その場合新しく予約からになるんですか?</div><div>SBオペレータ:はい。予約からになります。</div><div>私:今発送されている iPhone はどうなるんでしょう。</div><div>SBオペレータ:キャンセルとなります。</div><div>私:予約の場合、クーポンとかどうなるんでしょうか。会社の特典クーポンが期限過ぎてしまうのですが。</div><div>SBオペレータ:申し訳ないのですがキャンセルとなります。</div><div>私:免許証とか画像でアップロードしたのですが、それはどうなりますか。</div><div>SBオペレータ:もう一度アップロードして頂くことになります。</div><div>私:入院中でそれが出来ないのですが…</div><div>SBオペレータ:お時間のある時に再度やり直してください。</div><div>SBオペレータ:キャンセル致しますか?</div><div>私:キャンセルしなかった場合、私は25日の退院後まで受け取れないのですが、取り置きなどは出来ますか?</div><div>SBオペレータ:出来ません。受取人不在で戻ってきた場合はキャンセルとなります。</div><div>私:それでは、今ここでキャンセルするか、受取人不在で戻ってきてキャンセルするかのどちらかしかないんでしょうか。</div><div>SBオペレータ:そうです。</div><div>私:...分かりました。どうもありがとうございました。</div><div><br /></div><div>この後病院に外出許可を頂いて自宅に戻り、佐川急便さんに電話をして、電話後3時間以内に届けてとお願いをしました。</div><div>佐川急便さん、ほんと届けてくれてありがとうございます。</div><div><br /></div><div>iPhone は入院生活中のネット閲覧に凄く役に立ってくれましたし、サービスそのものは悪くないと思うのですが、ソフトバンクはマニュアル体制が徹底しすぎていて、融通が効かない気がする。</div><div><br /></div><div>送り先の住所を変更するだけで最初の予約からやり直さないと行けないし、受取人側で転送しようとしても転送不可だし、オペレータはキャンセル -> 最初からやり直し以外言わないし。。</div><div><br /></div><div>iPhone のキャリア選択でソフトバンク以外に docomo が出ていたので、もし万が一キャリアフリーになったら速攻移ろうと思います。</div>K.Uchiumihttp://www.blogger.com/profile/00353844307088495181noreply@blogger.com0tag:blogger.com,1999:blog-8537204796753207521.post-5266368287538638372009-08-16T20:39:00.008+09:002009-08-16T21:18:26.973+09:00学生時代の友人に仕事を紹介された。<div><div><span class="Apple-style-span" style="font-family:'times new roman';"><span class="Apple-style-span" style="font-size:small;">大学の同級生から電話が有って、一緒に食事でもどうと誘われてこの間一緒に飲んできました。</span></span></div><div><span class="Apple-style-span" style="font-family:'times new roman';"><span class="Apple-style-span" style="font-size:small;"><br /></span></span></div><div><span class="Apple-style-span" style="font-family:'times new roman';"><span class="Apple-style-span" style="font-size:small;">飲み屋で、友人は今の職を辞めて副業の方に専念したいと言っていて、副業でお世話になっている方々を紹介したいと言われ、ちょうど今日セミナーがあるということで行ってきました。</span></span></div><div><span class="Apple-style-span" style="font-family:'times new roman';"><span class="Apple-style-span" style="font-size:small;"><br /></span></span></div><div><span class="Apple-style-span" style="font-family:'times new roman';"><span class="Apple-style-span" style="font-size:small;">セミナーの開催者は相当儲かっているようでしたが、仕事の内容はネットワークビジネスと呼ばれるもので、参加そのものは簡単ですが収益化するには自分の下に顧客の勧誘と商品の販売をする人たちのツリーを作る必要があります。</span></span></div><div><span class="Apple-style-span" style="font-family:'times new roman';"><span class="Apple-style-span" style="font-size:small;"><br /></span></span></div><div><span class="Apple-style-span" style="font-family:'times new roman';"><span class="Apple-style-span" style="font-size:small;">友人は既にそれをやってある程度収益を出しているとのことでした。</span></span></div><div><span class="Apple-style-span" style="font-family:'times new roman';"><span class="Apple-style-span" style="font-size:small;"><br /></span></span></div><div><span class="Apple-style-span" style="font-family:'times new roman';"><span class="Apple-style-span" style="font-size:small;">あくまで販売している商品は無価値なものではないので、無限連鎖でも無いようなので、ねずみ講や悪徳マルチとは別物のようでした。</span></span></div><div><span class="Apple-style-span" style="font-family:'times new roman';"><span class="Apple-style-span" style="font-size:small;"><br /></span></span></div><div><span class="Apple-style-span" style="font-family:'times new roman';"><span class="Apple-style-span" style="font-size:small;">薬事法や特定商取引法などにも気を使っているようで、法に遵守しているかどうかは不明ですが法を守ろうという意識はあるようでした。</span></span></div><div><span class="Apple-style-span" style="font-family:'times new roman';"><span class="Apple-style-span" style="font-size:small;"><br /></span></span></div><div><span class="Apple-style-span" style="font-family:'times new roman';"><span class="Apple-style-span" style="font-size:small;">私にもやってみないか?ということで、私が人を勧誘して販売をする人を育成するのは苦手という話をすると、友人やその上の人たちが協力してくれるとまで言ってくれました。</span></span></div><div><span class="Apple-style-span" style="font-family:'times new roman';"><span class="Apple-style-span" style="font-size:small;"><br /></span></span></div><div><span class="Apple-style-span" style="font-family:'times new roman';"><span class="Apple-style-span" style="font-size:small;">とりあえず前向きに考えてみるということで、一度その会社について調べてから返事をするということにしたのですが、調べてみるとかなり先行き不安でした。</span></span></div><div><span class="Apple-style-span" style="font-family:'times new roman';"><span class="Apple-style-span" style="font-size:small;"><br /></span></span></div><div><span class="Apple-style-span" style="font-family:'times new roman';"><span class="Apple-style-span" style="font-size:small;">もちろん MLM という販売方法に多く問題があるのは知っていますが、法に遵守する限りは一応合法なので、副業としていいかなぁとも思ったのですが、、</span></span></div><div><span class="Apple-style-span" style="font-family:'times new roman';"><span class="Apple-style-span" style="font-size:small;"><br /></span></span></div><div><span class="Apple-style-span" style="font-family:'times new roman';"><span class="Apple-style-span" style="font-size:small;">中にはノルマを果たすために借金をする人までいて</span></span><span class="Apple-style-span" style="font-family:'times new roman';"><span class="Apple-style-span" style="font-size:small;">消費者保護センターへの相談が増えているそうです。</span></span></div><div><span class="Apple-style-span" style="font-family:'times new roman';"><span class="Apple-style-span" style="font-size:small;"><br /></span></span></div><div><span class="Apple-style-span" style="font-family:'times new roman';"><span class="Apple-style-span" style="font-size:small;">そして、彼らが借金して買った商品をさらに別業者が買い取り、小売りの入荷原価よりも安く売っているという…。</span></span></div><div><span class="Apple-style-span" style="font-family:'times new roman';"><span class="Apple-style-span" style="font-size:small;"><br /></span></span></div><div><span class="Apple-style-span" style="font-family: 'times new roman', fantasy; ">これでは馬鹿正直に親会社と契約して、入荷額と販売額の差額でも儲けるというのはまず難しそうです。</span></div><div><span class="Apple-style-span" style="font-family:'times new roman';"><span class="Apple-style-span" style="font-size:small;"><br /></span></span></div><div><span class="Apple-style-span" style="font-family:'times new roman', fantasy;"><span class="Apple-style-span" style="font-family: Georgia, fantasy; "><div><span class="Apple-style-span" style="font-family:'times new roman';"><span class="Apple-style-span" style="font-size:small;">さらには、</span></span><span class="Apple-style-span" style="font-family:'times new roman';"><span class="Apple-style-span" style="font-size:small;">消費者保護センターの1つからアメリカ本社への警告書も着ているらしく、日本の法人が改善しないようなら本社に事業損害を与える可能性が有ると本社から報告書が書かれている様子。</span></span></div></span></span></div><div><span class="Apple-style-span" style="font-family:'times new roman', fantasy;"><br /></span></div><div><span class="Apple-style-span" style="font-family:'times new roman';"><span class="Apple-style-span" style="font-size:small;">売り上げ自体も、日本では減収続きの様で、参加してもメリットはなさそうだなということでお断りしました。</span></span></div><div><span class="Apple-style-span" style="font-family:'times new roman';"><span class="Apple-style-span" style="font-size:small;"><br /></span></span></div><div><span class="Apple-style-span" style="font-family:'times new roman';"><span class="Apple-style-span" style="font-size:small;">うーん、今の会社にいてもあまり儲からないのは確かなので、儲け話は大歓迎なのですが、今回は縁がなかったです。残念。</span></span></div></div>K.Uchiumihttp://www.blogger.com/profile/00353844307088495181noreply@blogger.com3tag:blogger.com,1999:blog-8537204796753207521.post-43008157603709816822009-07-08T00:40:00.003+09:002009-07-08T01:00:02.215+09:00会社について最近私の会社では人件費削減に注力しているらしく、社員のやる気を上司たちが物凄い勢いで削いでくれている。<div><br /></div><div>似た感想を持った人がいないかなーと探していたら、なぜか自分が学生時代に出した論文がリファーされているのを見つけてびっくりした。</div><div>似た手法はたくさんあるし、私がオリジナルというわけではないなかでリファーしてもらえたのは素直に嬉しいです。</div><div><br /></div><div>きっと今ならもっとうまくやれるんだろうなー。</div><div><br /></div><div>最近は文章のトピックセグメンテーションに構造学習を使用している研究もあるそうです。</div><div>NAISTでは、SVMs を使って論文の章立てを予測するなどもしている?らしく、学生時代の自分がその場にいたらきっと圧倒されるんだろうなと思っていたり。</div><div><br /></div><div>今作っている学習器の作成が終わったら、また挑戦してみようかなぁ。</div><div><br /></div><div>会社で研究的なことを仕事にするのはかなり難しくなってきているので、やるとしたら自宅作業になりそうですが・・・。</div>K.Uchiumihttp://www.blogger.com/profile/00353844307088495181noreply@blogger.com0tag:blogger.com,1999:blog-8537204796753207521.post-44598545731468204002009-07-05T01:17:00.004+09:002009-07-05T02:29:48.431+09:00プログラミングのための確率統計(仮)<div>最近は参加をするのを止めているのだけど、ちょっと前までWebで公開されている(<span class="Apple-style-span" style=" white-space: pre; font-family:メイリオ;font-size:12px;">http://www10.plala.or.jp/xyzt/pscs/)資料を使った勉強会に出席していました。</span></div><div><span class="Apple-style-span" style="font-family:メイリオ;font-size:100%;"><span class="Apple-style-span" style=" white-space: pre;font-size:12px;"><br /></span></span></div><div><span class="Apple-style-span" style="font-family:メイリオ;font-size:100%;"><span class="Apple-style-span" style=" white-space: pre;font-size:12px;">この資料、章ごとの冒頭部分が凄く楽しいんですね。</span></span></div><div><span class="Apple-style-span" style="font-family:メイリオ;font-size:100%;"><span class="Apple-style-span" style=" white-space: pre;font-size:12px;">2章の冒頭を例に挙げてみます。</span></span></div><div><span class="Apple-style-span" style="font-family:メイリオ;font-size:100%;"><span class="Apple-style-span" style=" white-space: pre;font-size:12px;"><div><br /></div><div>A:私の調査によると、ゲーム機所持者の犯罪率は50%以上です。何らかの規制をすべきでしょう。</div><div>B:何そのやたら高い数字?</div><div>A:最近の少年犯罪では、犯人の半数以上がゲーム機を所持していました。</div><div>B:ええと、つっこみ所がありすぎて困るんだけど。とりあえず犯罪関係なしで最近の少年のゲーム機所持率から調べ直してくれない?</div><div><br /></div><div>これ、条件付確率と同時確率、周辺確率の話をする前のコラムなんですけど、面白いです。</div><div><div>Aさんは条件付確率 P(犯罪者|ゲーム機所持者) のつもりで話をしているわけですけど、実際にはそうはなっていないんですね。</div></div><div><br /></div><div>で、Aさんが最初に言っている数字が実は何を表しているのかというと、それはP(ゲーム機所持者|犯罪者)。</div><div>とりあえず世界には少年が100人しかいないとして適当に次みたいな表を作ってみます。</div><div><br /></div><div> | ゲーム機持ってる | ゲーム機持ってない</div><div>---------+------------------------+---------------------------</div><div>犯罪者 | 10 | 10</div><div>善良 | 40 | 40</div><div><br /></div><div>P(犯罪者,ゲーム機所持者) = 1/10</div><div>P(善良,ゲーム機所持者) = 2/5</div><div>P(犯罪者, ゲーム機持ってない人) = 1/10</div><div>P(善良, ゲーム機持ってない人) = 2/5</div><div><br /></div><div>さてこの例だと、ゲーム機を持っているという条件が与えられた時の条件付確率P(犯罪者|ゲーム機所持者)は、</div><div> P(犯罪者|ゲーム機所持者) = 1/5</div><div>です。</div><div><br /></div><div>Aさんはどういうことをしたのかな、というと・・・この世界から善良な少年をなくしちゃったんですね。</div><div>つまり、条件付確率P(ゲーム機所持者|犯罪者)を求めてます。</div><div>さて、この世界で確率を計算するときに、善良な少年の存在を無視すると、少年は世界で20人しかいなくなります。</div><div>その上で、ゲーム機所持者の割合を調べてみると 1/2 、つまり50%になります。</div><div><br /></div><div>あらら、P(犯罪者|ゲーム機所持者)が50%以上といってゲーム機を規制しようと言っていたのに、P(ゲーム機所持者|犯罪者)を求めちゃってたよ。</div><div>ということでした。</div><div><br /></div><div>まぁ、こんな話は著者がジョークで書いただけだろうと思っていたら、現実でもあるみたい。</div><div><br /></div><div>・ 公明新聞 http://www.komei.or.jp/news/2008/0219/10816.html</div><div>昨今流行の児ポ法問題の奴ですね。</div><div>[一部抜粋]</div><div><span class="Apple-style-span" style=" white-space: normal; color: rgb(75, 73, 72); line-height: 18px; font-family:Arial;font-size:13px;">性犯罪者の4割は、子どもの写真やアニメを収集していたという調査もある。</span></div><div><span class="Apple-style-span" style="font-family:Arial;font-size:100%;color:#4B4948;"><span class="Apple-style-span" style=" line-height: 18px; white-space: normal;font-size:13px;">(中略)</span></span></div><div><span class="Apple-style-span" style="font-family:Arial;font-size:100%;color:#4B4948;"><span class="Apple-style-span" style=" line-height: 18px; white-space: normal;font-size:13px;">アニメなどを児童ポルノの対象とすることには、「実在する被害者がいない」「表現の自由を保障すべき」との理由から反対論が多いが、犯罪誘発防止の観点から、アニメ大国の責任において積極的に議論する必要がある。</span></span></div><div><span class="Apple-style-span" style="font-family:Arial;font-size:100%;color:#4B4948;"><span class="Apple-style-span" style=" line-height: 18px; white-space: normal;font-size:13px;"><br /></span></span></div><div><span class="Apple-style-span" style="font-family:Arial;font-size:100%;color:#4B4948;"><span class="Apple-style-span" style=" line-height: 18px; white-space: normal;font-size:13px;">上記の例でいうと、Aさんが公明党になりますね。</span></span></div><div><span class="Apple-style-span" style="font-family:Arial;font-size:100%;color:#4B4948;"><span class="Apple-style-span" style=" line-height: 18px; white-space: normal;font-size:13px;"><br /></span></span></div><div><span class="Apple-style-span" style="font-family:Arial;font-size:100%;color:#4B4948;"><span class="Apple-style-span" style=" line-height: 18px; white-space: normal;font-size:13px;">公明党: 子供の写真やアニメを収集している人の性犯罪率は4割です。子供の写真やアニメを規制しましょう。</span></span></div><div><span class="Apple-style-span" style="font-family:Arial;font-size:100%;color:#4B4948;"><span class="Apple-style-span" style=" line-height: 18px; white-space: normal;font-size:13px;"><span class="Apple-style-span" style="color: rgb(0, 0, 0); line-height: normal; white-space: pre; font-family:メイリオ;font-size:12px;"><div>B: 何そのやたら高い数字?</div><div>公明党: <span class="Apple-style-span" style=" white-space: normal; color: rgb(75, 73, 72); line-height: 18px; font-family:Arial;font-size:13px;">性犯罪者の4割は、子どもの写真やアニメを収集していました。</span></div><div>B: ええと、つっこみ所がありすぎて困るんだけど。とりあえず性犯罪関係なしで子どもの写真やアニメの所持率から調べ直してくれない?</div><div><br /></div><div>確率は分かりにくい概念ですけど、一応今の国会で議席過半数を占める連立与党の一党なんだし、さらに実施されるとやり直しとか難しそうなんだから、もう少しまともに調査とか出来る人も議論に参加してもらったらどうなんだろう。</div></span></span></span></div></span></span></div>K.Uchiumihttp://www.blogger.com/profile/00353844307088495181noreply@blogger.com0tag:blogger.com,1999:blog-8537204796753207521.post-81059247377891346232009-07-05T00:49:00.008+09:002009-07-08T00:58:15.163+09:00まだ残ってた。筆不精なので全然書いてなかった。<div>というか、このブログの存在すら忘れてました。</div><div><br /></div><div>機械学習の勉強中とか言いつつ、何も書いてないですね。</div><div>ブログ開設から2年が経過していますが、その間の作業としては</div><div><br /></div><div>[ブログ開設当初]</div><div>・ Averaged Perceptron (Perl モジュール)作成</div><div>2値、多値分類、双対形式版では polynomial kernel が使える。</div><div>キャッシュを用いて高速な学習が可能。</div><div>主形式版はとりあえず早い。</div><div><br /></div><div>[その後]</div><div>・ オンライン学習で Logistic regression 作成</div><div>系列ラベリングをやってみたくて、CRFの勉強として作ってみた。</div><div>単純なGDで実装。</div><div><br /></div><div>・ <a href="http://people.csail.mit.edu/mcollins/papers/icml07.pdf">Exponentiated Gradient Algorithms for Log-Linear Structured Prediction</a></div><div>これを読んでお勉強。</div><div><br /></div><div>・ EG で Logistic regression を作ってみる</div><div>参考にしたのは以下</div><div><a href="http://www.soe.ucsc.edu/~manfred/pubs/J36.pdf" style="text-decoration: none;">Exponentiated Gradient versus Gradient Descent for Linear Predictors</a></div><div>EG+ ならちゃんと収束もしたし、学習も出来たが、EG± だとうまく行かなかった。</div><div>正の値か負の値のどちらかしかパラメータが学習できない。</div><div><br /></div><div>・ EG で CRF を作る。</div><div>一応動くものが出来たのだが、後に Forward-Backward の実装が間違っていることに気づく。</div><div>何でちゃんと動いたのか疑問。</div><div>これもEG+かEG-のみ。</div><div><br /></div><div>・ CRF で N-gram テンプレートを使えるようにしてみる</div><div>CRF++ は便利な素性テンプレートが使えてるけど、実は意外と制限がある。</div><div>扱えるトークンはカレントの位置から ±4 までで、ラベル遷移は bigram までしか扱えない。</div><div>というわけでとりあえず作る。</div><div><br /></div><div>・ 小町さんから紹介された Folos で Logistic regression を実装</div><div>論文をちゃんと読んでいないので証明とかは分からないのだけど、岡野原氏のPPTを参考に実装すると、1時間くらいで出来た。</div><div>かなり分かりやすい資料でした。</div><div><br /></div><div>L1正則化が使えるようになったのは素直に嬉しい。</div><div><br /></div><div>・ Folos で CRF を作成</div><div>N-gram テンプレートが使える CRF で、かつ L1 正則化も使えちゃうというもの。</div><div>勾配法は EG をやめて GD にした。理由はパラメータで正負両方の値を学習したかったから。</div><div>EG でもちゃんと作れればいけるんだろうけど、なんかうまくいかないんだよなぁ。</div><div><br /></div><div>遷移素性や Forward-Backward の接続チェックとかの実装で、簡単のために STL を使いまくっていたらむちゃくちゃ遅くなった。</div><div>後、コンパクトなモデルを作るんだ!と思って抽出される素性を蜜な素性から、コーパス中に出てきた素性しか扱わない疎なものにしたら、思ったほど精度が出ず、CoNLL 2000 の Shared Task で9位程度の成績しか出なかった。</div><div>なので現在高速化&蜜な素性を扱うようにして作成中。</div><div>crfsgd が F-measure(F1)で 93.75 とか出てるみたいだし、同じくらい出したいなぁ。</div><div><br /></div><div>[途中経過]</div><div>メモリの使用量がオンライン学習のくせしてめちゃくちゃ多いのは私の実装が悪いから・・・。</div><div><br /></div><div>[追記20090707]</div><div>自前アロケータに与えているプールサイズの指定で 0 が多かっただけだった。。</div><div>メモリ節約できてたし、素性を登録しておく辞書(自作ハッシュ)のテーブルサイズを同期に指摘されて調整したら160万強の素性の生成と登録が30秒を切る程度の時間で終わるようになりました。</div>K.Uchiumihttp://www.blogger.com/profile/00353844307088495181noreply@blogger.com1tag:blogger.com,1999:blog-8537204796753207521.post-52302379564086809292007-10-21T02:08:00.001+09:002007-10-21T02:10:03.298+09:00だるい昨日飲み会でお酒を飲んだのだが、それが多少残ってるんだろうか。<br />とにかくだるい。<br /><br />今日はドラムの練習をするつもりだったけど、バンドの人たちから連絡がなかったからそのまま放置。<br />夜8時を過ぎて連絡が来たので、明日にしてもらった。<br /><br />明日コートをクリーニングに出したらそのまま行こうと思う。K.Uchiumihttp://www.blogger.com/profile/00353844307088495181noreply@blogger.com0tag:blogger.com,1999:blog-8537204796753207521.post-37409440661510894112007-10-20T00:56:00.000+09:002007-10-20T01:06:10.309+09:00作成* log<br /><br />自宅PCの環境がWindowsとMacのデュアルブートになったため、<br />moinXでつけてるlogではWindowsから更新が見れなくなった。<br /><br />Googleアカウントでブログも作れるようなので作成。<br />今後個人のログはこちらに書こうかなと考え中。<br /><br />ついでにこのアカウントでCGI設置&比較的自由なPerlモジュールのインストール可能なWebスペースとか貸してくれないかな。<br /><br />記憶力が悪いせいか、いろんなところにアカウントを作るとIDとPASSをよく忘れてしまう。<br />以前ライブドアにアカウントを作って、ウェブマネーを使って本を買ったのですが、<br />アカウントを忘れたため入ることもできません。<br />まだ1000円以上お財布に入れたままだったと思うのだけど、どうしたものやら。K.Uchiumihttp://www.blogger.com/profile/00353844307088495181noreply@blogger.com0