Hatena::ブログ(Diary)

Mi manca qualche giovedi`? このページをアンテナに追加 RSSフィード Twitter

2012-02-01 内容的にはむしろ英語の資料がいるよなあ

極大部分文字列を使った twitter 言語判定(中谷 NLP2012)

来たる 3/13〜16 に広島で行われる言語処理学会年次大会(NLP2012)にて発表する「極大部分文字列を使った twitter 言語判定」の論文を公開。


中谷 秀洋, 極大部分文字列を使った twitter 言語判定, 言語処理学会第18年次大会, 2012
(一旦公開取り下げ中)

【注】

言語処理学会の公開規定が「大会での発表後」であったため、一旦公開を取りやめました。3/15 の発表終了後、再公開します。

【/注】



第8回 TokyoNLP で発表した「∞-gram を使った短文言語判定」と基本線は同じ。ただしその発表の時にお約束していたとおり、17言語の判定精度で 99.1% を達成している。99% 越えは作り始める前からの目標だったので素直に嬉しい。

twitter の言語判定を研究されている方達*1が、もちろん独立に作成された6言語のデータセットでも 99% 以上の精度で判定でき(詳細は論文を参照)、一定の客観性が得られたことも嬉しい。


ちなみに、きれいな訓練データさえ増やせば精度はまだ上がる手応えはある。できれば 99% 後半を狙いたいところ。なあに発表まであと1ヶ月半……さすがに無理か(元になるデータがその期間では十分集まらない)。


実用への課題は、論文の最後にも書いたとおり、メモリに載っけるにはモデルがちょっとでかめなこと(99.1% を出したモデルだと 160MBくらい)。

いやまあそれ単独ならたいしたことないけど、検索とか何とか他のメインアプリケーションが動いている環境で、そのサブ機能でしかない言語判定が占めるにはでかすぎるかな(いくら必要不可欠な機能とは言え)。もう少し精度とリソースを天秤に掛けられるようにしたい。


コードは MIT ライセンスで公開している。ただし、極大部分文字列の抽出モジュールは某社内ライブラリの公開準備待ち。

学習済みのモデルも同リポジトリで公開しているが、まだちょっと古くて小さい奴になってしまっている。最新のモデルはもうちょっと小さくできないかなあと少し試行錯誤しているところ。もうしばらくしたら公開できると思う。

データセットは、TokyoNLP #8 以降に作成したものは twitter の ID 付きで作っているので、「ID と言語ラベル」という組で公開が可能になっているのではあるが、そのようなデータをどういったライセンスで公開するのがいいのかちょっと悩んでいることもあり、公開には至っていない。

なお、ID付きで作ったデータは主にマイナーめの言語(チェコ語デンマーク語、フィンランド語、ノルウェー語、ポーランド語、ルーマニア語など)である。英語とかフランス語とかもそのうちきっと作るんだろうけれど、現時点ではモチベーションの高いもの(それを作ることで判定精度が上がる)優先ってことで、もっぱらマイナー言語のデータばかり作っている。喜ぶ人少なそう(笑)。


論文に書ききれなかった小ネタはいっぱいいっぱいあるんだけど、一つだけ。

この ldig(Language Detection with Infinity-Gram) はノルウェー語、デンマーク語に次いでオランダ語が苦手。オランダ語には is とか goes とか、英語と同じ、あるいは英語の綴りだとしてもおかしくない頻出語がままあるので結構難しい、と最近まで思っていた。

が、先に出てきた twitter の言語判定を研究されている Tromp さんらが作成された6言語のデータセットに対しては、なんとオランダ語の精度が一番良い。実は Tromp さんらはオランダの大学(Eindhoven University of Technology)の方達、つまりきっとオランダ人。なので、オランダ語のデータが「一番きれい」なのだろう。

ということは裏返せば、ldig がオランダ語を苦手としているのはデータが汚いからということでもある。うーん頑張り甲斐がある……。


最後に、論文のチェックをしてくれたサイボウズ・ラボの同僚と id:nokuno さんに多謝。

*1:[Tromp+ Benelearn2011] Graph-Based N-gram Language Identification on Short Texts

2011-12-24 正直読みにくい論文だったけどおもしろかった。

EMNLP 読み会で Extreme Extraction - Machine Reading in a Week(Freedman+) を読みました

nokuno さん主催の EMNLP 読み会にのこのこ参加。

広く浅くがモットーなので、論文読み会ではできるだけあんまり知らない領域の論文を選ぶことにしている。ということで今回は "Extreme Extraction - Machine Reading in a Week"(Freedman+) という論文を選んでみた。

Information Extraction 系のお話は興味あるんだけど、そのお話をするのにほぼ必須となる NER(Named Entity Recognition) まわりに毎回跳ね返されてた。まあでも何回かチャレンジしてきた中でそこそこ蓄積があったみたいで、今回はなんとか読めそうだな〜という感触。

どうも NER まわりは独自の文化というかコンテキストとがある感じがするよなあと思ってたのだけど、sleepy_yoshi さんも同じようなことを言われていたので、心強かったりw


というわけでこちらが発表資料。



論文をちらっと眺めると、やたら "2 hours" とか "43 hours" とかいった記述が目に付く。「わずか 50時間で、既存の relation extraction システムを新しい relation に対応させた」という内容なので、この作業にはこれだけかかった、などの主張がいちいち入っているのだ。まあそれはそれでいいんだけど、そのためどうも実際に作業を行った時系列に書かかれる構成になっているようで、理解のための構成にはなっていない。

また「既存の relation extraction システム」についての詳細は不明。参照先論文としてすら触れられていない。


一方、参照論文 [Kozareva+ 08] Semantic Class Learning from the Web with Hyponym Pattern Linkage の DAP(Double-Anchored Pattern) という手法がおもしろかった。"disease such as cold and" といった形のクエリーを検索エンジンに投げると、"and" の後に disease クラスの新しいメンバーが出てくるので、それをまた seed に使ってどんどん探していく、というもの。

そんな狭い表現だとヒット件数少なくてうまくいかないんじゃあ、と思ったが、試しにやってみたら意外といい感じ。そのためした様子は資料に書いてあるので、興味あれば。


次は NIPS 読み会。

2011-11-25 質疑応答が終わらなくて nokuno さんがやきもきしてたw

#TokyoNLP で「∞-gram を使った短文言語判定」を発表しました

TokyoNLP 第8回に のこのこ参加。主催者の id:nokuno さん、発表者&参加者のみなさん、そして会場を提供してくださった EC ナビさん改め VOYAGE GROUP さん& @ajiyoshi さん、お疲れ様でした&ありがとうございました。


今回は「∞-gram を使った短文言語判定」というネタを発表。「短文言語判定」って、要は「このツイートは何語?」ってこと。

こちらが資料。


そして実装したプロトタイプ ldig (Language Detection with Infinity-Gram) とモデル(小)はこちらで公開。


言語判定とは「文章が何語で書かれているか」を当てるタスクで、以前一度 TokyoNLP #2 で language-detection (以降 langdetect)という言語判定 Java ライブラリを発表した。

でも langdetect は twitter のような短い文章が苦手で、またナイーブベイズを使っていたのでメタデータなどの非独立性の高い(というか独立性の読めない)素性を加えにくいという難点があった。

そこで今回は短い文書でも高い精度(99%以上)で言語判定するための方式とそのプロトタイプ実装を作ってみた、という内容になっている。


といっても、なにやら難しげなタイトルに反して機械学習の難しい話は少なめ(ほとんど全部はしょったのでw)。実際、数式も一つも出てこない。特に極大部分文字列のところはなかなかわかりやすく書けたんじゃあないか、とか自画自賛してみるw

また後半はいろんな言語をコンピュータで扱う場合の雑学集みたいなものなので、こちらは本当に何の知識もなくても楽しんでもらえると思う。


今回の ldig では、いくつか飛び道具は使っているものの、多クラスロジスティック回帰という非常にポピュラーで枯れつつある手法がベースになっている。

それでも十分高い精度が出るのは、そこそこの大きさのコーパス(地道にツイートに言語ラベルを振り続けて作った)と、対象言語それぞれに特有な事情を考慮した正規化をきめ細かくやっているから。逆にそれがなければ、どんなに複雑で最新な機械学習アルゴリズムを持ってきても、99% の精度は目指すことも出来ないと思う。

ちょうど同じ TokyoNLP #8 で @zzzelch さんが「機械学習は1割、データが9割」とおっしゃられていたが、その点はまさに同感(機械学習の割合は、もうちょびっとくらい増やしてもいいかもしれないけどw)。


あとは質疑応答や休み時間や懇親会でいただいた質問を、憶えている範囲で……といっても、あんまり憶えてなかったので(すいませんんん!)、nokuno さんのメモを参考にさせていただきつつ。


複数言語を含むツイートを判定すると?

現状は与えられた文章全体で確率を計算している。分割が必要なら ldig を呼ぶ側で行う。

ldig で勝手に「ここまでは○○語、ここからは××語、……」とできたらもちろんいいが(願望)、今のところノーアイデア。

仮に素性の傾向を部分的に見て分割するべき可能性を判定するなどのアプローチを考えてみると、例えば you に出てくる -ou という綴りは「英語らしくない」など、単語単位で見たら「その言語らしさ」はかなりのバラツキが出てしまうことがわかっており、おそらくもう1アイデア必要と感じている。


スペルミスがあったら?

実際スペルミスは非常に多いが、1箇所間違っているくらいなら特に問題ない(さすがに絶対間違わないという意味ではなく、99% という精度を目指すレベルで考えても誤差の範囲、ということ)。

それより you => u, for => 4 や末尾の母音を省略するような、ショートメッセージ特有の短縮表現の方がはるかに頻度が高くて問題になっている。現状ではそういった表現ごと学習することで、できるだけ誤判定を減らそうとしているが、それでも間違えてしまう極端な表現(人間が見ても「………………わかるか!」と叫んでしまうくらいw)も少なからずはある。


クエリの言語判定はできる?

手元にデータがないので試してはいないが、クエリはもともと短い上に、固有名詞が多くて機能語が少ない(という印象)ので、正直難しいと思う。いいとこ 90% ってところかと。


現実の言語分布を反映させると精度は上がる? 下がる?

やってみないとわからないが、なんの工夫もしないでそのまま適用したら下がるかもしれない。

というのも、現実の分布では英語がダントツに多いわけだが、実は ldig は、デンマーク語・ノルウェー語ほどではないにしても、英語が少し苦手。原因はおそらく英語の語彙は他のほとんど全ての言語にも出てくるので、英語に与えられる確率が微妙に下がっているせいではないか、と推測している。

もちろん、もともと「英語が多い」といった分布を勘案できるモデルにすれば、精度は上げられると思う(当然だけどね)。そこらへん含め、おもしろい&現実に即した観点だと思うので、できれば確認してみたいと思う。


メタデータを使ったら精度もっと上がるのでは?

メタデータアプリケーションドメイン固有のものなので、最初からそれに頼ってしまうと他の分野で使えなくなってしまう。というわけで、今回はあくまで素のテキストからどこまで目指せるか、という点にしぼっている。

今回はロジスティック回帰をベースとしており、そういった非独立性の高い素性を入れたモデルを考えることもできるはずなので、現実の利用シーンではそういったことも考えていくと思う。有用メタデータを捨てるなんてもったいなさすぎるしね!w


実際の素性の数は? 全部を使うとさすがに多すぎるのでは?

その場では曖昧な記憶の数字を答えてたが、ちゃんと確認したものを記す。

訓練データ(小+大)を食わせたときの「頻度2以上の極大部分文字列」は約1000万。

そこから「頻度が8以上」という足切りをしつつ(この8という数字はいくつか試行錯誤した中から選んだ現時点での閾値)、ラテン文字を含む素性に絞って 90万。

正則化なしの SGD を8周回して、パラメータが有効な範囲(絶対値>10e-7)に入ったのが 70万。

L1 正則化を 2周回して、つぶれなかった残りが 50万。


素性を頻度で足切りしているのは、もちろん素性が多すぎると学習に時間がかかるという理由もあるが、頻度の少ない素性も入れてしまうと配布したモデルから元のツイートが容易に再現できてしまう(というか、まんま入ってしまう)という懸念があるからでもある。


ラベル付きコーパスは配布しないの?

権利問題が……と答えたら、ラベルと ID を配布して、ツイートの取得はそれぞれが行うという手があるよ、と。あーそういえばそうやって配布しているコーパスあったあった。自分もそうすればいいという頭がなかった……。

今まで作ったコーパスはそこら辺の情報捨ててしまってたのでもう無理だけど、今後作る分は ID を保持して「ラベル+ID」で配布できるようにしようと思う。


skip n-gram とかは?

ありだと思う。ただ、有効な素性間の距離がかなり長いことが予想されるので、PMI とか見て有効な素性の組みを探すとかできないかという方向で構想中。


アノテーションってどうやってるの? どんなツール使ってる?

秀丸で。

2011-11-18 前回急用でお休みして皆勤がとぎれてしまった(涙

#TokyoNLP で twitter で言語判定してみたというネタを話します

第8回自然言語処理勉強会 #TokyoNLP
http://atnd.org/events/22199

主催の id:nokuno さんを除けば、TokyoNLP できっと最多発表だと思うが、来週の第8回でもまたまたのこのこ発表。


今回は「∞-gram を使った短文言語判定」というタイトルで、かっこつけているが、要は twitterツイートの言語判定をしてみました、というお話の予定。

以前、53言語を 99% 以上の精度で判定する language-detection という Java の言語判定ライブラリを開発し、Apache Solr にも組み込んでもらえる(見込み)と、一定の評価はいただいたのだが、いかんせん「短文の言語判定が苦手」という弱点があり。

53言語分あわせて 1MB ちょっとしかないプロファイル(JSON ファイル換算)でそんな短い文章の言語判定なんか土台無理に決まってて、しかもツイッターなんてもうデタラメな表現の宝庫なわけでムリムリ、各言語の膨大な辞書をプロファイルとして持って単語単位で判定すればそりゃできるだろうけど現実的ではないよなあ、とかとかネガティブな方向まっしぐらだったりしてた。一時期は。

でも、ん? 意外とできるかも? と、なんかふと思っちゃったら、ついつい作ってみたくなって作ってみた、という感じ。


language-detection と同じく、今回も目標は 99% 以上の精度。目標通りにいったのかは発表でのお楽しみ、ということで。

というか、今現在も必死にあれこれ作り込んでて、例によってギリギリまでいじっているんじゃあないかな(苦笑)。

2011-11-17 ずいぶん久しぶりの連載ですが、そこは突っ込まないでねw

gihyo.jp の連載「機械学習 はじめよう」の第11回「線形回帰を実装してみよう」が公開されました

gihyo.jp の連載「機械学習 はじめよう」の第11回「線形回帰を実装してみよう」が公開されました。


機械学習 はじめよう 第11回「線形回帰を実装してみよう」
http://gihyo.jp/dev/serial/01/machine-learning/0011

今回は、第8回と9回で紹介した線形回帰をおなじみの Python / numpy / matplotlib で実装する内容となっています。

実践編を担当するのは今回が初めてなので、どういう書き方にしようかいろいろ迷ってしまいました。

ノリが以前と変わっているから調子が狂っちゃうかもしれませんが、ご容赦ください(笑)。


見せ方は変えてありますが、今回の連載記事の内容は、先日 Tokyo.SciPy #2 で発表した内容と一部被っています。

Tokyo.SciPy #2 でフルボッコされたコード*1はさらにパワーアップして、心ある numpy 使いなら目を背けるようなコードになっています(苦笑)。ほんとすいません。

恐らく「numpy を使う以上はループを極限まで使わないコードを書くべきだ。こんな numpy の流儀に従っていないコードをサンプルとして、しかも gihyo.jp のような影響力の大きいサイトに掲載するなど言語道断、けしからん!」と怒られるのでしょう(目に浮かびます)。


「見通しの良い、書き換えやすいコード」であることを優先したため、と書くとこの前ブログで書いたことの繰り返しでしかないので、もう少し具体的に。

例えばサンプルコードの phi() を(numpy で言うところの)「ユニバーサル」に書けば、numpy らしい、より高速なコードに近づきます。

しかし記事の中では、応用問題的に特徴関数 phi() を書き換えてみるススメなどもしています。そのとき phi() がユニバーサルであることを仮定するコードだったら、どうでしょう。正しく書き換えるには、numpy についての十分な知識が必要になってしまいます。

想定している読者は「機械学習を学んでみたい人」なので(言わずもがな?)、numpy をよく知っていることを前提にはできません。せめて間違った場合に「 phi をユニバーサルにしてください」のようなエラーメッセージが出てくれれば良かったのですが、実際に出るのは間接的で謎めいたエラー。numpy 初心者にはなにがなにやらさっぱり。


というわけで、今後も numpy コミュニティの方々の気に触るコードを生産し続けるかもしれませんが、あしからず……。

そうだ! gihyo.jp に numpy 講座の連載があればいいんだ! お客様の中にどなたかが numpy に詳しい方〜 (笑)


ここからおまけ。

記事の冒頭の線形回帰の復習*2の中で、連立方程式を整理すると行列の式になる、というくだりがありました。ちなみに「パターン認識機械学習」(PRML) では、式 (3.13) から (3.15) あたりに相当します(表記などは少し違っています)。


  • ¥frac{¥partial E(¥bf{w})}{¥partial w_m}=¥sum_{n=1}^N ¥phi_m(x_n)¥left(¥sum_{j=1}^M w_j¥phi_j(x_n)-t_n¥right)=0¥hspace{20} (m=1,¥;¥cdots¥;,M)   (式 A)
  • ¥bf{w}=(¥bf{¥Phi}^T¥bf{¥Phi})^{-1}¥bf{¥Phi}^T¥bf{t}   (式 B)

この「整理」の部分は記事の本筋ではないので省略しましたが、こういった計算を苦手にしている人もきっと多いでしょう。

教科書を読んでいる時なら、逆に (式B) から始めて、(式A) をひねくり出すという裏技(?)が使えるかもしれませんが、現実の問題では答えがあらかじめわかっているなあんてことはありません。


しかしこのような変形も、上の「数式」の中で紹介したテクニックを使えばかんたんに(つまり、ひらめきや職人技がなくても)できちゃいます。


f:id:n_shuyo:20111104145227p:image


要は (式A) をこの形に合うように変形していくだけです。

といっても、この中で (式A) に合うのは2番目の行列積しかありません。

Φ=(φ_nm), φ_nm=φ_m(x_n) に書き換えつつ、Σの中の引き算をバラして、それぞれ「真ん中の添え字」が組みになるように並び替えていきます。添え字の順番を変えるのは転置を使います。


  • ¥sum_{n=1}^N ¥phi_{nm}¥left(¥sum_{j=1}^M w_j¥phi_{nj}¥right)-¥sum_{n=1}^N ¥phi_{nm}t_n=0
  • ¥sum_{n=1}^N ¥phi_{nm}¥left(¥sum_{j=1}^M ¥phi_{nj}w_j¥right)-¥sum_{n=1}^N (¥Phi^T)_{mn}t_n=0

ここまででまず上の行列積の対応から ¥sum_{j=1}^M ¥phi_{nj}w_j=(¥Phi¥bf{w})_n, (¥Phi^T)_{mn}t_n=(¥Phi^T¥bf{t})_m とわかります。


  • ¥sum_{n=1}^N ¥phi_{nm}(¥Phi¥bf{w})_n-(¥Phi^T¥bf{t})_m=0
  • ¥sum_{n=1}^N (¥Phi^T)_{mn}(¥Phi¥bf{w})_n-(¥Phi^T¥bf{t})_m=0

おなじく ¥sum_{n=1}^N (¥Phi^T)_{mn}(¥Phi¥bf{w})_n=(¥Phi^T¥Phi¥bf{w})_m となりました。というわけで、


  • (¥Phi^T¥Phi¥bf{w})_m-(¥Phi^T¥bf{t})_m=0¥hspace{20} (m=1,¥;¥cdots¥;,M)
  • ¥Phi^T¥Phi¥bf{w}-¥Phi^T¥bf{t}=¥bf{0}

出ましたね。慣れるともっとさくさく出来るようになりますよ。

*1:でも負けませんでしたけどね!(えっへん)

*2:といいつつ、初出の式がちらほらw