自然言語処理の分別奮闘記

勉強したこととか

NLP若手の会(YANS)に行ってきた

NLP若手の会、通称YANSに行ってきました。
今回写真を一枚も撮っていなかったのが残念です。部屋にいる時間が少なくて、ほとんど電池が切れてしまっていました。。
Twitterの様子は小町先生がまとめてくださっています。(Togetterまとめ

YANSとは

NLP若手の会 (YANS) は、自然言語処理および関連分野の若手研究者の交流を促進し、若手のアクティビティを高めることを目指したコミュニティです。
(NLP若手の会 第9回シンポジウム)

研究の発表も多々あるのですが、それ以上にNLPの若手な人たちの交流会や親睦会のような役目も大きいようです。特に今年はその色が強かったようで、普段は各々がホテルを取って参加するような形式だったところ、今回は2泊3日の合宿形式でした。様々な方々の協力によって合宿形式の実現ができたそうです。運営委員の方々やスポンサーの方々に感謝です。
特に、ただでさえ安かった参加費が当日に更に安くなっていたことや、交流会の花火代などはスポンサー代によるものだったようです。

なにをしてきたの

正確には公式ページに書いてありますが、大まかには以下のようなことが行われていました。

  • 口頭発表
  • ポスターセッション
  • 招待講演
  • スポンサー発表
  • 国際会議参加報告
  • ハッカソン
  • 交流会企画

この記事では主に

  • 交流会
  • 自分のポスターセッション
  • 印象に残った発表
  • ハッカソン

についてを書きます。(必ずしも時系列順ではないです)

交流会

交流会イベントは数が多く、

  • ビーチバレー
  • 卓球
  • 麻雀
  • カラオケ
  • 花火
  • 懇親会

などなどの企画があり、カラオケ, ハッカソン, 花火, 懇親会に参加しました。ビーチバレーにも参加したかったのですが、ハッカソンに取り組んでいたら参加しそびれてしまいました。フットサルも開催予定だったのですが、ハッカソンと被る時間帯により参加者を集められずやむなく中止に。
何かスポーツもしたかったですが、自然言語処理の方々とカラオケ、花火を楽しみました。(天城越えやばかったです)

今回の交流会企画で一番良かったのが懇親会。懇親会はテレビで見るような宴会場(真ん中に中居さんが通るスペースがあるやつ)で行われました。
席が自由だったので、どこに座ろうかと探していたら丁度社会人参加の二方に挟まれている空席を発見。ここに座るしか無い!と思い座ってみるとビンゴ!以前Twitter上で少し関わったことのある(構造学習について少し教えていただいた)@uchumikさんだったのです!!
ここでお礼をすることもでき、もう一方の隣の@tkngさんとも一緒に企業や経歴の話をしていただいたりしました。なかなか聞くことのできないような話で面白かったです。他にも構造学習の話の流れから他のアルゴリズムの話になったり技術的なお話も聞くことが出来ました。
しばらくお話をしてから席を移動し@unnonounoさんの方へ行き、動的計画法やCFGのtreeが三角形と台形で表せるという、これまた技術的なお話をたくさん聞くことが出来ました。こんな話ばかりで大丈夫かなとおっしゃっていましたがとても面白かったです!
やはりみなさんお酒の席でも技術的な話で盛り上がるのですねw

そんな感じで交流会企画も盛り上がり、個人的には(Twitter上で少し教えていただいただけですが)事前にネットで少し関わった@uchumikさんと実際に会ってお話することができたので良かったです。
後で知ったことなのですが、小町先生曰く@uchumikさんと@unnonounoさんはDPの鬼だそうで、そんなお二方からDPについてのお話を聞けたのは幸運でした。

自分のポスターセッション

YANSでは研究発表も多々あり、自分も 夏休み中に実際に手を動かして作っていたものがあったので、それについて発表しました。
発表内容は「遠距離教師あり学習を用いた複合名詞のアノテーションによる未知語の半自動獲得」です。興味のある方は是非boosterのSlideとポスターのPDFを見ていただけるとありがたいです。(そしてなにかアドバイスをいただけると)
簡単にまとめると、self-trainingのように正解事例を集めるとその集めた正解事例がそのまま語彙として未知語(複合名詞)が獲得できたね。やったぜ。というストーリーです。

ポスター発表をするのは始めてだったのでとても緊張したのですが、若手の会ということなので教えていただく気持ちで発表して気持ちを落ち着かせました。90分のポスターセッションだったのですが必死で説明していたらあっという間に終わってしまっていました。
実際にたくさんの方から意見をいただくことができて、ありがたい限りです。
他にもポスターセッションではどういうところをよく説明するのかわかりましたし、説明してると暑くなってくることも喉が渇くことも体験しましたw思ったよりも体力を使うのですね。
今回の発表内容は実際に手を動かして実験してみたことをまとめていて、Surveyを全くしていなかったので、これからその部分を穴埋めしていこうかなと思います。(distant supervisionって何?self-trainingと何が違うの?と言われても答えられなかった。。)

以下にアドバイスいただいたものをいくつかまとめてみました。ポスターを見ていないとなんのことだかわからないかもしれないです。

  • 全正例を網羅するのが大変ならある程度の量をランダムサンプリングしてアノテーションして実験する方法がある
  • 一回毎の辞書更新に人手介入をするか、人手を介さなくても簡単ないくつかのルールを入れるだけでもノイズはかなり減ることも
  • シード辞書を網羅的なものにするともっと良くなりそう
  • 最終的に形態素解析してみたりして外的評価を試みる(実際に使えるのかどうかわからない)

アドバイス等々を頂き、自分のポスターセッションはとても有益なものになりました。ありがとうございました。

印象に残った発表

印象に残った発表は間違いなく@miorisagaraさんのスポンサー発表なのですが(ぶっちゃけトークがとても面白かったw)、メモに残った(身近に感じた)招待講演の@ta_makinoさんの「研究の現場におけるコードの検証と共有」について箇条書きですがまとめようと思います。
この講演は小町先生もまとめてくださっています。(Togetterまとめ

昔の実験を再現できるか
  • 学生の頃の、あるいはn年前の実験を再現することができますか?
  • できないのであれば、できるようにデータ・コードを管理しましょう
研究室にリポジトリ
  • できるだけ小さな単位で、頻繁にコミットしよう
  • 先生に見てもらうこともできるし、進捗状況もわかる
  • 1ヶ月前の自分は他人、他人にもわかるように共有しておこう
  • 前処理などの小さいスクリプトでも、実験に必要な操作ならばコミット
コードレビューをお願いしよう
  • 一度に大量にお願いするのはやめて
  • 他の人が使う、他の人のを使うときにお願いしたり
  • 下の人に見てもらうのも悪くない、何をやっているのかわかってもらうことができる
  • githubにおいてはpull requestベースでやると自然にコードレビューを組み込める

理想的には論文を査読するように、コードもレビューするような文化になると良い

  • コードレビューをしたことに対する評価があったり
  • コードレビューの経験が実績化したらモチベーションも上がる
  • あいつはコードレビューもいっぱいしてるし、されてるから安心して委託できる
テストコードを書こう
アサーションを出す
  • 仕事では使いづらいが(実際サービス止まったら困る)、研究では積極的に使っていこう
  • 関数・クラスの入り口出口で検査
結合テスト
  • 自明な入力と出力を用意して検査
  • 配布をするなら無効な入力に対してもちゃんとチェックを
  • テストケースをたくさん用意して、まとめて順番にテストする
  • 三角形のプログラムの例のように網羅的にテストケースを用意
  • どういうところをチェックすべきか考えて、必要な部分だけでもチェック
ユニットテスト
  • 関数・クラス単位でチェックする
  • 研究においては特に重要
  • バグが見つかったら再発防止のためにユニットテストをする
  • ユニットテストのライブラリはいっぱいある


  • 上記のようなテストを一発で実行できるようにして、それに通ってからコミットするようにする
  • テストめんどくせー → 既存のコード・ライブラリを使う
  • 良いライブラリはテストコードもちゃんと用意されてる
一般公開リポジトリに登録しよう
  • Readmeに、これをつかうときはこれを引用してねみたいなことを書いておく
  • 人事の人は間違いなく名前で検索する
  • 検索されたときにクオリティーの高いコードが公開されてるとgoodだよね

というようなお話をしていただきました。
これから実践していこうと思います。

ハッカソン

ハッカソンのテーマ

ハッカソンはオープンタスクと共通タスクの2種類があり、共通タスクに参加。 共通タスクではカメリオ*1のログを提供していただきました。
ハッカソンの内容としては、カメリオが表示した記事の中からユーザが実際にクリックする記事を予測して、その精度をチーム同士で競うというものです。具体的には、正解ラベルとして"read"と"show"がついている訓練データから、"read"を正例として学習して、テストデータのラベルを当てにいくというようなタスクです。

自分たちのアプローチ

チーム分けは言語別に、言語によってはバージョン毎に分けれらました。自分はPython2のチーム(4人チーム)になりました。(Python2が多かったのでさらに細分化した)
訓練データ、テストデータを受け取りそれぞれ作業にとりかかりました。きっと素性を考えて分類器を作ることになると思ったので、最初はデータを眺めてどういう傾向があるのかを見ていました。 データを眺めていて疑問に思ったことをカメリオさんに質問していると、showのログはバッチ処理*2で、readのログはリアルタイムにサーバに送っているという情報を得ました。 更に、行動を起こした時間ではなくサーバーに送られてきた時間をログデータにとっているということがわかりました。実際にログデータを見ても、"show"となっている事例はいくつかまとまって同時刻に記録されているのに対して、"read"の事例は時刻が他とずれて記録されていることが確認できました。
これって

で、それなりのPrecision出るんじゃということで、自分はそのコーディングを担当。(実際に運用する上では利用できない素性なので少しずるいですが、、)
他のチームメイトには、

  • 記事のカテゴリがgivenの時のあるユーザーのクリック率を最尤推定で求めるモデル

をデータを見ている間に作ってもらっていました。

最初はテストデータに不備があったとのことで提出できず、他の交流会企画に行くことに。午後は交流会企画のカラオケに参加していたので、ハッカソンの時間がとれず、深夜に同チーム(かつ同研究室)の@Ace12358君と一緒に廊下でハッカソン。昼にやろうと思っていた「時刻ずれてる戦法」を実装して提出しようと思ったら、提出ボタンが行方不明に。。明日提出しようということでその日は寝ることに。
次の日、テストデータの不備を修正したものを受け取ったのですが、テストデータを見てみると気になる点が。なんか正解ラベルのようなものがついているっぽい。しかも全ての事例に"show"のラベルが。。どういうことなのか聞いてみると、テストデータとしては"read"のログは入っていないとのこと。"show"のみのログを見て、最終的にその中から"read"されるものを当ててください、という形式になったとのこと。(readされる記事は必ずその前にshowされている)つまり"read"のログだけ時刻が浮いているという方法はボツに。。。(ここでボツにせずに実行して提出してみればよかった)
2日目から最終日にかけては、当初の予想通りチームメンバー各々が素性を集める作業に。集めた素性は

  • カテゴリID
  • 記事ID
  • はてブ
  • 記事タイトル内の頻出語
  • 記事カテゴリがgivenのときのそのユーザのクリック率
  • カテゴリ毎の人気度

とか色々考えて抽出しました。他にもなんかあったような気がするのですが丁度徹夜して作業してたところなのであんまり覚えてないです。
これらの素性をSVMに突っ込めば分類できるのではというゴリ押し戦法だったのですがSVMが重すぎで学習が終わらず。。最終的には1日目に作ってもらっていた「記事のカテゴリがgivenの時のユーザのクリック率」を適当な閾値で切って閾値以上のものを"read"にするという方法が自分たちのチームのベストスコアになりました。閾値は職人技(提出しては結果を見て考える)で決定してもらいました。
SVMが重すぎたのはどうやらlibsvmというツールを使っていのが原因なようで、liblinearという上位版?を使えば速度はましだったようです。

結果と考察

最終的に結果は4位でした。SVMの方法が提出できていたらどうなっていたのか、ちょっと残念な結果になってしまいました。
しかし、気になる結果が一つ。企業賞を受賞したチームの手法が当初自分たちも考えていた「"read"のログだけ周りと比べて浮いている」というヒューリスティックによるものだったのです。。
ん?なにか気になりますね。だってテストデータにはそもそも"read"のログは存在しないはずなのですから。でも実際に結果の出ているチームがあったのです。恐らく"read"のログが存在しないというのは目眩ましで、本当は"read"のログも存在していて、ラベル名を"show"に書き換えていただけだったのではと推測しています。こっちも提出したらどうなっていたのか、提出すれば良かったです。。

ということで、後悔っぽい終わり方をしてしまいましたが収穫も多かったです。チームメンバーとgithubを使ってコードを共有する経験もできましたし、何より来年から小町研の博士課程に来てくださる@moguranosenshiさんと一緒のチームになって同じタスクに取り組めたことが今回のハッカソンの収穫と言ってもいいですね。

終わりに

色々と書きましたがYANS行って良かったという話です。初めての外部発表もしましたし、NLPな方々とお話できましたし。
ここに書いてあること以外にもたくさん発表がありました。他のブログや公式サイトもチェックしてみてください。

そういえば、写真が一枚もないと思っていたのですが初日に海の見えるマックから撮った写真だけありました。今回三浦海岸で開催されていて海鮮系を食べたかったのですが、どこも混んでいてマックで妥協したのです。。

最後に運営委員の方々、スポンサー企業の方々、本当にありがとうございました。

f:id:uorijuram:20141007033632j:plain

*1:スポンサー企業の白ヤギコーポレーションさんが運営しているキューレーションサービス

*2:ある程度のログが溜まってからサーバに送っている

チュートリアル1: 1-gram言語モデル

とりあえず1回目は、前に言っていたNLP Programing TutorialCapter01(pdf)、1-gram言語モデルの勉強です。
では、スライドに沿って話を進めようと思います。

言語モデルって何?

1-gram言語モデルの前に、そもそも言語モデルって何?

具体例で説明すると。
音声認識システムにて、認識した結果の出力候補としていくつかの単語列があったとしましょう。

  1. W1 = speech recognition system
  2. W2 = speech cognition system
  3. W3 = speck podcast histamine
  4. W4 = スピーチ が 救出 ストン

言語モデルは、これらの単語列に「もっともらしさ」を与えてくれるのです。 一般的には、各単語列に確率を与えてくれます。

上の各単語列は、ある言語モデルに基づくと

  1. P(W1) = 4.021*10-2
  2. P(W2) = 8.932*10-4
  3. P(W3) = 2.432*10-7
  4. P(W4) = 9.124*10-23

のように単語列の生起確率としてとらえることができるようになります。 使う言語モデルによってこの生起確率の値は変わってきます。
この1〜4の単語列の場合、人が見たなら1, 2, 3, 4の順でそれらしい(ありそうな)単語列だと思えるはずです。
つまり、

P(W1) > P(W2) > P(W3) > P(W4)

という確率を出してくれる上の例のような言語モデルは、それっぽい(良い)といえるってことですね。(言語モデルにも善し悪しがあります。)
つまり
言語モデルとは、より自然な、より頻出する文(や単語列)に高い確率を与えてくれるものなのです。

単語列の確率

実際にはどうやって単語列の確率を出してるの?という話

まずは、各単語の同時確率で考えましょう。

P(W=speech recognition system) = P(|W|=3, w1="speech", w2="recoginition", w3="system")

同時確率は条件付き確率で考えられるので

P(|W|=3, w1="speech", w2="recoginition", w3="system") =
   P(w1="speech") | w0="<s>")
   * P(w2="recognition" | w0="<s>", w1="speech")
   * P(w3="system" | w0="<s>", w1="speech" , w2="recognition")
   * P(w4="</s>" | w0="<s>", w1="speech" , w2="recognition", w3="system")

と表せる。*1
(<s>, </s>はそれぞれ文頭記号,文末記号で、1-gramではあまり意味は無いが2-gram以上のN-gramや遷移モデルなどを考えた時には必要になる) ここで文頭,文末記号が入ってるってことは単語列じゃなくて文だったのかよっていう突っ込みは考えないでおこう
これを一般化すると

{ \displaystyle
P(W) = \prod_{i=1}^{|W|+1} P(w_i\ |\ w_0...w_{i-1})
}

これで単語列の確率は表せそうです。 じゃあ、それぞれのP(wi | w0...wi-1)はどうやって求めるのか。
一つの方法として、最尤推定があります。

母集団分布の形が分かっているがその母数が未知であるときに,n個の標本値 x1,x2,...,xnを母集団分布に従う確率変数 X1,X2,...,Xnがとることは最も起こりやすい(maximum likelihood)という条件を用いてその母数を決めようとするものである.
(最尤推定)

起きた現象を、「最も引き起こす確率が高い」場合を考えるのが最尤法です。
(最尤法を言葉で簡潔に説明するには、どのように説明すればいいですか?)

要するに(たぶん)、
起きた事象から、きっとこういう確率だったんだろうなと推測する
ってことだと思います。
で、今回の場合はコーパス*2の単語列を数え上げればいいわけです。(たぶんコーパス事象で、この世の全ての文章などなどが母集団ってことになるんだと思う。たぶん。いやこの世の文章もサンプルにすぎないのかな、、そしたら母集団は文章を生み出してる言語自体なのかな、よくわからんけどニュアンスは伝わったと思う。。)

で、式はどうなるかというと(c(W) : 単語列Wの総数)

{ \displaystyle
P(w_i|w_0\ldots w_{i-1}) = \frac{c(w_0...w_i)}{c(w_0...w_{i-1})}
}

具体的には、

<s> i live in osaka . </s>
<s> i am a graduate student . </s>
<s> my school is in nara . </s>

というコーパスがあった時に<s> i live, <s> i amそれぞれの確率は

P( live | <s> i ) = c(<s> i live) / c(<s> i) = 1/2 = 0.5
P( am | <s> i ) = c(<s> i am) / c(<s> i) = 1/2 = 0.5

となるわけである。

これで、P(wi | w0...wi-1)が求められたわけですが、最尤推定には問題があります。
頻度の低い現象に弱いのです。どういうことかというと、 <s> i live in nara . </s>の文の確率を計算したいときに、

P(nara | <s> i live in) = 0/1 = 0
∴ P(W = <s> i live in nara . </s>) = 0

となってしまうのです。
<s> i live in nara . </s>という文は、ありえない文ではないのですが、 このように最尤推定による言語モデルでは確率が0になってしまいます。
これはコーパス<s> i live in naraという単語列が存在しないのが原因です。
zero frequency problemとかsparseness problemとかゼロ頻度問題とか言われてる問題ですね。読んで字のごとくコーパスに存在しない単語や単語列の頻度・確率が(実際には起こりうるのに)0になってしまうという問題です。

1-gramモデル

今日の本題「1-gramモデル」です。先ほどの低頻度の問題を少し解消してくれます。 1-gram(unigram)モデルは、N-gramモデルのN=1のときのモデルです。(次回N-gramモデルをやります)
1-gramとは1単語ずつの頻度で考えるモデルのことです。*3
このように前後の単語の並びに関係なく(つまり順番を考慮せずに)考えることをbag of wordsと言ったりします。 最尤推定では履歴を考えていましたが、1-gramではその履歴を用いないことで低頻度の単語列を減らしています。
つまり、分子をその単語の頻度、分母を全語彙の頻度の和(コーパスの単語数)

{ \displaystyle
P(w_i | w_1...w_{i-1}) \approx
 \frac {c(w_i)}{\sum_{\tilde{w}\in W}^{} c(\tilde{w})}
}

というように計算することで1単語の頻度として考えています。 あとはこれを掛けあわせて単語列の確率を算出します。
例えば、P(<s>) = 3/22 ≈ 0.14, P(i) = 2/22 ≈ 0.09, P(live) = 1/22 ≈ 0.05, ...
のように各単語の確率を計算すると

P(W = <s> i live in nara . </s>) = 0.14 * 0.09 * 0.05 * 0.09 * 0.05 * 0.14 * 0.14 = 5.556 * 10-8

のように、最尤推定では確率が0になってしまった文の確率が算出できました。

が、まだダメです。まだゼロ頻度問題があります。
これまでは頻度の少ない単語列(や文)に対して、履歴を用いない(単語列として考えずに単語のみで考える)ことによってゼロ頻度問題を解決しようとしました。 これである程度はゼロ頻度問題を解消できたのですが、単語そのものがゼロ頻度の場合、つまり未知語が存在する場合は確率が0になってしまうのです。
例えばP(kyoto)P(kyoto) = 0/22 = 0となってしまいますよね。
なので、P(<s> i live in kyoto . </s>) = 0になってしまいます。

未知語の対応

未知語が含まれると1-gramでさえもゼロ頻度問題が起きることがわかりました。 これを解決するために、未知語の確率を補間します。

{ \displaystyle
P(w_i) = \lambda_1P_{ML}(w_i)+(1-\lambda_1)\frac{1}{N}
}

Nは未知語を含む語彙数として設定します。
PML(wi)はさっきまでの1-gramモデルの考え方で算出する単語wiの確率です。
λ1は補間係数と呼びます、添字の1は1-gramの補間という意味です。

例えば、N=106, λunk=0.05 (λ1=0.95)とすると

P(<s>) = 0.95*0.14 + 0.05*(1/106) = 0.13300005
P(i) = 0.95*0.09 + 0.05*(1/106) = 0.08550005
P(live) = 0.95*0.05 + 0.05*(1/106) = 0.04750005
P(in) = 0.95*0.09 + 0.05*(1/106) = 0.08550005
P(kyoto) = 0.95*0 + 0.05*(1/106) = 0.00000005
P(.) = 0.95*0.14 + 0.05*(1/106) = 0.13300005
P(</s>) = 0.95*0.14 + 0.05*(1/106) = 0.13300005

P(<s> i live in kyoto . </s>) = 0.13300005 * 0.08550005 * 0.04750005 * 0.08550005 * 0.00000005 * 0.13300005 * 0.13300005
  = 4.08 * 10-14

と、未知語 "kyoto" があっても確率が出せました。

これは、常に(1-λ)の割合で未知語の確率を入れているので、Interpolation(線形補間)といいます。
これでゼロ頻度問題を解決できました。
他にも、add-one, good-turing, Kneser-Neyなどのdiscounting*4と呼ばれる方法や、back-offなど様々なスムージングの方法があります。気になる人は、こちらのページにある程度書いてあったので参考までに。(「Ngram言語モデルメモ」)

言語モデルの評価

最尤推定, 1-gramモデル, 未知語の対応とやってきてようやく言語モデルを作ることができました。
次はその言語モデルがどれだけ使えるものなのか、評価方法についてです。
最初のほうで「言語モデルにも良し悪しがあります」と言ったと思いますが、それを定量的に測ることができます。
モデルの評価尺度にはいくつかの方法があり、 ここでは、尤度, 対数尤度, エントロピー, パープレキシティ、カバレージの5つを勉強します。
一般的には、パープレキシティとカバレージが使われるようです。というか、尤度、対数尤度、エントロピーはパープレキシティまでの道のりみたいなものですね。

さて、 言語モデルの構築には、学習データを用いて単語をカウントしました。
言語モデルの評価には、学習データとは別の評価データを用います。
練習問題と期末テストが全く同じだったら、期末テストで高得点が取れるのはあたりまえですよね。それと同じで、学習データと評価データは別に用意する必要があるのです。
ただ、学習データと評価データに同じものを使って、その評価結果が低い場合は学習自体がうまくいっていない(学習にバグがある)という推測をすることができます。

f:id:uorijuram:20140818181509p:plain

尤度

尤度は、モデルMが与えられた時の観測されたデータ(評価データWtest)の確率です。

{ \displaystyle
P(w_{test}|M) = \prod_{w \in W_{test}}P(w|M)
}

つまり、さっきまで計算していた文の確率そのものですね。 テストデータの文章の確率を計算するだけです。

P(w="i live in nara" | M) = 2.52*10-21
P(w="i am a student" | M) = 3.48*10-19
P(w="my classes arre hard" | M) = 2.15*10-34
P(Wtest | M) = 2.52*10-21 * 3.48*10-19 * 2.15*10-34 = 1.89*10-73

確率と尤度は式こそ同じですが、意味は違います。この場合尤度は、評価データの元でのモデルMの尤もらしさを意味しています。 (「尤度とは何者なのか?」)

対数尤度

尤度は確率値を掛けあわせているので、しばしばアンダーフローを起こしてしまいます。尤度に対数をとることでこの問題を解決したのが対数尤度です。対数をとっても大小関係は変わりません。

{ \displaystyle
log(P(w_{test}|M)) = \sum_{w \in W_{test}}log(P(w|M))
}

P(w="i live in nara" | M) = -20.58
P(w="i am a student" | M) = -18.45
P(w="my classes arre hard" | M) = -33.67
P(Wtest | M) = -20.58 -18.45 -33.67 = 72.60

エントロピー

エントロピーHは底2の負の対数尤度を単語数で割った値です。

{ \displaystyle
H(W_{test}|M) = \frac{1}{|W_{test}|}\sum_{w \in W_{test}}-log(P(w|M))
}

情報理論において情報量として利用されています。

エントロピーは3つの意味を持つ:
情報の量 (Amount of Information)
不確実性 (Uncertainty)
圧縮の出来なさ (Incompressibility)
即ち、エントロピーが高い場合、情報量が増え不確実性が増し、圧縮ができにくくなる。例として、表しか出ないコインが持つ不確実性は0である(確実に表になる)。したがって、このコインに最低限の情報しか持たない。
(エントロピーとは)

この場合、エントロピー言語モデルの複雑さを表しています。エントロピーが高いほど複雑で、次へ続く単語の予測が難しいことを表しています。(エントロピーが高い、すなわち確率の低い単語は本当にこの単語が生起するのかが曖昧)つまり、不確実性のことだと思います。

パープレキシティ

パープレキシティは2のエントロピー乗で表します。

{ \displaystyle
PPL = 2^{H}
}

パープレキシティもエントロピーと同じくモデルの複雑さを表していますが、パープレキシティは情報理論においての平均分岐数です。つまり2H個の等出現確率の単語の中から一つの単語を決定することになります。パープレキシティの値が大きいほどモデルが複雑で平均分岐数(次に続く可能性のある単語の数)が多いです。
一般的にはパープレキシティが低い(より複雑でない)ほど良いモデルであると言えますが、単純過ぎても良くない(らしい)ので、どの程度の複雑さが良いのかは場合によるようです。

バレージ

バレージは、評価データに現れたn-gram(今回は1-gramなので単語)の中で、モデルにも含まれている割合のことです。 言語モデルがテストデータ内のどれだけの語彙をカバーできているか、文字通りですね。

{ \displaystyle
\frac{単語数-未知語の数}{単語数}
}

実装

やっと説明部分が終わりました。長かったです。
では、言語モデルの学習と評価を実装してみましょう。私の研究室ではpythonを使っているのでpythonで実装します。 やっていることは単純で単語を数え上げているだけです。学習,評価用のデータ等はNLP Programing Tutorialのページで配布されています。
埋め込みができるのでgistを使っていますが、コードはすべてgithubのRepositoryにあります。gistではなくgithubにあるコードを埋め込む方法ってないのですかね。調べるとgistの埋め込みばかり出てきます。。

まずは言語モデルを学習するためのtrain_lm.py

次に言語モデルを評価するためのtest_unigram.py
Tutorialのスライドにはないですが、せっかくなのでパープレキシティも出力しています。


テスト用のデータで実行してみた結果はこんな感じ。

python train_unigram.py -f 01-train-input.txt -o lm
学習データ(01-train-input.txt)

a b c
a b d

言語モデル(lm)

</s> 0.250000
a 0.250000
b 0.250000
c 0.125000
d 0.125000

python test_unigram.py -f 01-test-input -l lm -o evaluation
評価データ(01-test-input.txt)

a c
e

評価結果(evaluation)

entropy : 6.709899
perplexity : 104.684170
coverage : 0.800000

データが大きいので中身は書きませんが、演習用データ(wiki-en-train.word, wiki-en-test.word)で学習,評価した結果は以下のようになりました。

entropy : 10.526656
perplexity : 1475.160635
coverage : 0.895226

なんかパープレキシティが大きいですけど、どうなんですかね、数字を見てもいまいちよくわかっていないです。平均分岐数だと思うとかなり大きすぎるような気がしますが。。1-gramだからとかそういうことなんですかね。
まぁその辺はいろいろなタスクをやっていく中でわかるようになるでしょう。

最後に少し気になったので調べてみたことを書いてみます。

モデルとは

今回は1-gram言語モデル言語モデルの評価について勉強しました。
しかし言語モデルに限らず、「モデル」とは一体なんなのか。
自然言語処理においてもよく使われている単語ですが、なかなかその意味を掴みにくいです。
私も曖昧な解釈のままなのですが、現時点では

複雑な現象を説明するために用いられる単純化した理論や仮設
(モデルとはなにか?)

という説明が一番しっくりきています。
今回勉強した1-gram言語モデルも、文の生起という複雑な現象を説明するために、「ある単語の生起確率は他の単語に依存しない」という仮説のもとに単純化して考えられています。

この仮説が正しいかどうかはさておき、仮説を立てて捨象することで理論を単純化し現象を捉えやすくしています。

仮設を行い、大勢に影響しない要素を捨象することこそ「モデル」の本質
(いまさら聞けないモデルの話)

抽象的な概念を捉えるのはなかなか難しいですが、これから勉強していく中でも理解を深められたらと思います。

とりあえず、今回のChapter01「1-gram言語モデル」はここまでです。 なにか間違っていることや補足などあれば教えていただけると助かります。

*1: 文の始めには必ずあるので、P(w0="<s>") = 1

*2:自然言語の文章を構造化し大規模に集積したもの。wikipedia

*3:これは単語1-gram, 1文字ずつで考えるものを文字1-gramという

*4:既知語からある割合だけ未知語に割り当てるためdiscountingと呼ばれる

始動します

どうも、marujirouです。
これからブログを書いていこうと思います。

ブログの内容

内容としては、勉強したことを自分なりに書き起こすようなブログを

  • 勉強
  • アウトプット
  • あとで読み返すかも

の3つを主な目的として書いていこうと思います。
他にも、この機会にMarkdownにも慣れちゃおうって目論みもあったりします。

NLP Programing Tutorial

手始めにNLP Programing Tutorialの復習をします。
NLP Programing Tutorialは自然言語処理の基本的なアルゴリズムチュートリアルで、NISTGrahamさんが公開してくださっています。

NLP Tutorialは小町研究室にて勉強会として、ひと通り勉強しました。
のですが、イマイチ理解の浅い部分、ちゃんと実装できていない部分もあったので、その穴埋めをしつつの総復習です。

その他

他に考えているのは、読んだ論文の紹介やその他もろもろ勉強の記事です。
投稿ペースはこの夏の間は1記事/weekくらいで考えています。(考えているだけです)
後期が始まったら予定と相談ですね。

最後に

間違いや勘違いも多々あると思います。
是非、ご指摘やアドバイスなどご教授いただければと思います。