DSIRNLP #2
  phyllo
自己紹介
ID : phyllo
ブログ : http://d.hatena.ne.jp/jetbead

• 某Web企業の新卒
• 入社してから自然言語処理始めました!!

• 最近の興味
  – 自然言語処理を使って、効果的なダイエット法を見つ
    けること
今日の概要
いろんな方法で
 Ngram頻度を
数え上げる!!
Ngram頻度を数える
• 超基礎!
 – 「入門自然言語処理」でいうと18ページ目の内容
 – というか、ただの統計処理


• しかし!
 – 頻度分布
 – Ngram言語モデル
 – その他いろんな応用が考えられる!(はず)
Ngramとは?
• 「隣り合うN個の塊」のこと
 – 単語ngramや文字ngramなどがある


「金持ち喧嘩せず」の文字2gram(bigram)
  →{金持, 持ち, ち喧, 喧嘩, 嘩せ, せず}


「This is apple computer」の単語3gram(trigram)
  →{This-is-apple, is-apple-computer}
方法
方法
1.   ナイーブな方法
2.   Suffix Arrayを使った方法
3.   近似カウント法
4.   分散処理を使う方法
1.ナイーブな方法
1.ナイーブな方法
• やるだけ!
 – Perlだとハッシュに入れて数えたり
  use   strict;
  use   warnings;
  use   utf8;
  use   encoding "utf8";

  my $ngram = shift;
  my %hash;

  while(my $line = <>){                              #一行とってきて
    chomp $line;                                     #最後の改行を削って
    for(my $i=0; $i<length($line)-$ngram+1; $i++){   #ずらしながら
      $hash{substr($line,$i,$ngram)}++;              #ngram文字ずつ数える
    }
  }

  foreach my $key (keys %hash){
    print $key,"¥t",$hash{$key},"¥n";
  }
1.ナイーブな方法
[入力文]
  イカちゃんかわいい
[出力]
  ゃん 1    かわ   1
  ちゃ 1    イカ   1
  カち 1    んか   1
  わい 1    いい   1
1.ナイーブな方法
• これでいいじゃん!

• 大規模テキストやNを大きくしたら?
 – 1GBのデータとかは?
 – Ngramの種類が増える
 – メモリが足りなくなる…
1.ナイーブな方法
ラピュタのセリフのngramを取ってみると…
• これでいいじゃん!      (全1426文)
 2gram(5000種類) 3gram(10209種類)                4gram(13066種類)
• 大規模テキストやNを大きくしたら?
 ・・      1632  ・・・     671                   ・・・。    78
 !!      375   ・・。     101                   っはっは 56
 – 1GBのデータとかは? 88
 ーー      168   っ・・                           !・・・    55
 っ!            シータ                           ラピュタ 54
 – Ngramの種類が増える79
 って
         143
         134   !・・     76                    。・・・    47
 – メモリが足りなくなる… 67
 ああ
 んだ
         134
         120
               ーーー
               ・・あ     67
                                             はっはっ 44
                                             っ・・・    42
 ー!      111   はっは 64                        ・・・あ    38
 ない      105   ー!! 61                        ・・・・    36
 った      103   っはっ 58                        ん・・・    33



                         http://faithrm-zero-zero.blog.so-net.ne.jp/2010-06-24-2
1.ナイーブな方法
• これでいいじゃん!

• 大規模テキストやNを大きくしたら?
 – 1GBのデータとかは?
 – Ngramの種類が増える
 – メモリが足りなくなる…


Ngramを保存しなければいいんだ!!
2.Suffix Array
を使った方法
2.Suffix Arrayを使った方法
• Ngramをメモリに保存しない??
 – ハッシュに保存しない!!
• 入力文のSuffix Arrayを作ってみてみる
入力文:イカちゃんかわいいイカちゃん


  イカちゃんかわいいイカちゃん     いいイカちゃん
  カちゃんかわいいイカちゃん      いイカちゃん
  ちゃんかわいいイカちゃん       かわいいイカちゃん
  ゃんかわいいイカちゃん        ちゃん
  んかわいいイカちゃん         ちゃんかわいいイカちゃん
  かわいいイカちゃん          ゃん
  わいいイカちゃん           ゃんかわいいイカちゃん
  いいイカちゃん            わいいイカちゃん
  ・・・                ・・・
2.Suffix Arrayを使った方法
• Ngramをメモリに保存しない??
 – ハッシュに保存しない!!
• 入力文のSuffix Arrayを作ってみてみる
入力文:イカちゃんかわいいイカちゃん     Bigramが欲しい!


  イカちゃんかわいいイカちゃん     いいイカちゃん
  カちゃんかわいいイカちゃん      いイカちゃん
  ちゃんかわいいイカちゃん       かわいいイカちゃん
  ゃんかわいいイカちゃん        ちゃん
  んかわいいイカちゃん         ちゃんかわいいイカちゃん
  かわいいイカちゃん          ゃん
  わいいイカちゃん           ゃんかわいいイカちゃん
  いいイカちゃん            わいいイカちゃん
  ・・・                ・・・
2.Suffix Arrayを使った方法
いいイカちゃん
いイカちゃん
かわいいイカちゃん
ちゃん               いい   1
ちゃんかわいいイカちゃん      いイ   1
ゃん                かわ   1
ゃんかわいいイカちゃん       ちゃ   2
わいいイカちゃん          ゃん   2
ん                 わい   1
んかわいいイカちゃん        んか   1
イカちゃん             イカ   2
イカちゃんかわいいイカちゃん    カち   2
カちゃん
カちゃんかわいいイカちゃん



• 上からなぞっていけばNgramが数えられる!(ドヤッ
2.Suffix Arrayを使った方法
• メモリの節約になってる?
 UTF8で1文字3byteとすると、
  「(3*N+int4byte) * (Ngramの種類)」
 が
  「(先頭アドレス4byte) * (入力文字数)」
 になっている


• 効率的に実装すればかなり節約になります
2.Suffix Arrayを使った方法
/* 効率的ではないC++のコード */

vector<size_t> ptr; //★先頭位置を保持するポインタ(のかわりのindex)
for(size_t i=0; i<ustr.length()-N+1; i++){ ptr.push_back(i); }

vector<int> cmn_char; //次の文字との共通文字数
Cmp cmp(ustr); //ポインタで文字列を比較するためのファンクタ


//★Suffix Arrayの辞書順ソート
sort(ptr.begin(), ptr.end(), cmp);


//★共通文字数カウント
for(size_t i=0; i<ptr.size(); i++){
  if(i+1<ptr.size()){
    cmn_char.push_back(common_prefix_length(ustr, N, ptr[i], ptr[i+1])); //次の文と何文字共通しているか
  }else{
    cmn_char.push_back(0);
  }
}

//★順番にptrを見ていって、cmn_charがN以上な個数を数える
...
2.Suffix Arrayを使った方法
• これでいいじゃん!

• さらに大規模になったら?
 – 1TBのデータとかは?(twitterのデータとか)
  • 1TBのメモリがあれば…
 – どうしよう…
2.Suffix Arrayを使った方法
• これでいいじゃん!

• さらに大規模になったら?
 – 1TBのデータとかは?(twitterのデータとか)
  • 1TBのメモリがあれば…
 – どうしよう…       メモリが無いと俺はNgramも
                カウントできないのかよ!!
2.Suffix Arrayを使った方法
• これでいいじゃん!

• さらに大規模になったら?
 – 1TBのデータとかは?(twitterのデータとか)
  • 1TBのメモリがあれば…
 – どうしよう…


近似値でいいからできるだけカウント!
3.近似カウント法
3.近似カウント法
• 大まかに2つのアプローチ
 – 全部は無理なので、頻度が多いものに注目しカ
   ウント(Counter-based)

 – Hash関数とかを使って全部を大体正確にカウント
   (Sketches)
3.近似カウント法
• Counter-Based Algorithm
  –   Sticky Sampling
  –   Lossy Counting
  –   Frequent
  –   Space-Saving
• Sketches
  –   Count Sketch
  –   Group Test
  –   CountMin Sketch
  –   hCount
3.近似カウント法
• Counter-Based Algorithm
  –   Sticky Sampling
  –   Lossy Counting
  –   Frequent
  –   Space-Saving
• Sketches
  –   Count Sketch
  –   Group Test
  –   CountMin Sketch
  –   hCount
3.Lossy Counting
• ストリームデータの基本的なカウント法
 – どんどん要素がくるようなデータ


• 何を保持するの?
 – 3つのペア
  • 要素名e, 頻度の推定値f, 最大許容誤差値Δ
 – 定期的に保存しているペアでいらない
                ものを捨てる(整理)
3.Lossy Counting                                                ※Δを要素ごとに保存してない




                                                                     # 新しい要素が来た!
                                                                     # n個目だね
                                                                     # もし保存してたら頻度+1
                                                                     # 新しい要素なら
                                                                     ## 新たに保存
                                                                     ## 頻度はΔ+1
                                                                     # 定期的に
                                                                     ## Δを更新して
                                                                     ## すべての要素を確認
                                                                     ## 頻度がΔより小さければ
                                                                                 削除


Graham Cormode and Marios Hadjieleftheriou, “Methods for finding frequent items in data streams”, The VLDB Journal
3.Lossy Counting
• C++のmapで実装すると…
    /* 効率的でないC++のコード */

    class LossyCounter {
     //★保存している要素リスト
     map<string,pair<int,int> > S;
     //パラメータ
     int N;
     double gamma, epsilon;
     int b_current; //Δ

     //★保存している要素で頻度がΔより小さいものを削除
     void next_backet();
    public:
     //★初期化
     LossyCounter(double gamma, double epsilon);
     //★新しい要素をSに追加
     void add(const string &str);
     //要素strの頻度
     int get(const string &str);
     //要素リストをすべて表示
     void show();
     //要素リストのサイズを返す
     int size();
    };
3.近似カウント法
• これでいいじゃん!

• 確かに、ここまでできれば十分かも
 – Lossy Couting以外の優れた方法もある
  • Space-Savingとか
 – 世の中のほとんどのNgram頻度は数えられる


• でも、近似値だよね、、、
4.分散処理を使う方法
4.分散処理を使う方法
• 近似じゃだめなんだ!
• 正確な頻度が欲しいんだ!!

• Hadoop
  – 個人のPCで試すことができる
  – Amazon EC2上で個人でも利用できる(有料)
    • Amazon Elastic MapReduce


                                 (今回はコード例は略)
まとめ
まとめ
• これでどんなでかいデータが来ても大丈夫!!

• 「やるだけ」だと思ったけど、実際にやってみると
  いろいろ問題があることがわかります
 – 今回は文書量の問題
 – 高速化や効率化も必要

• 「やってみた」からわかったこと
 – これから研究開発したいと思っている人
  • 「なにすればいいかわからん」と思っている人
 – どんどん作ってやってみましょう!!
 – 新しい問題の発見があるかも…! :)
補足情報
• 他のNgram数え上げ方法
 – PrefixSpan
    • http://www.cs.uiuc.edu/~hanj/pdf/span01.pdf
    • http://chasen.org/~taku/publications/nlp2002.pdf
    • http://2boy.org/~yuta/publications/substring_mining.p
      df
補足情報
• ちょっと試してみたい!
 – (自己責任で自由に使用・加工してください)

 – SuffixArrayを使った頻度カウント
   • http://d.hatena.ne.jp/jetbead/20111012/1318422417
 – Lossy Countingを使った頻度カウント
   • http://d.hatena.ne.jp/jetbead/20111014/1318547950
補足情報
• 参考資料など
 – 岩波講座ソフトウェア科学15「自然言語処理」
 – アルゴリズム・サイエンスシリーズ5 数理技法編
   「オンラインアルゴリズムとストリームアルゴリズ
   ム」
 – 入門「自然言語処理」
補足情報
• 参考資料など
 – http://ci.nii.ac.jp/naid/110002934647
 – http://chalow.net/2010-06-21-1.html
 – http://diary.overlasting.net/2010-04-23-3.html
 – http://chalow.net/2010-05-12-1.html
 – http://d.hatena.ne.jp/ny23/20101108/p1

自然言語処理はじめました - Ngramを数え上げまくる