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

Post on 11-Jun-2015

6.365 Views

Category:

Documents

4 Downloads

Preview:

Click to see full reader

DESCRIPTION

DS

TRANSCRIPT

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の種類が増える

– メモリが足りなくなる…

• これでいいじゃん!

• 大規模テキストやNを大きくしたら?

– 1GBのデータとかは?

– Ngramの種類が増える

– メモリが足りなくなる…

1.ナイーブな方法 ラピュタのセリフのngramを取ってみると… (全1426文)

2gram(5000種類) ・・ 1632 !! 375 ーー 168 っ! 143 って 134 ああ 134 んだ 120 ー! 111 ない 105 った 103

3gram(10209種類) ・・・ 671 ・・。 101 っ・・ 88 シータ 79 !・・ 76 ーーー 67 ・・あ 67 はっは 64 ー!! 61 っはっ 58

4gram(13066種類) ・・・。 78 っはっは 56 !・・・ 55 ラピュタ 54 。・・・ 47 はっはっ 44 っ・・・ 42 ・・・あ 38 ・・・・ 36 ん・・・ 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を使った方法

• 上からなぞっていけばNgramが数えられる!

いいイカちゃん いイカちゃん かわいいイカちゃん ちゃん ちゃんかわいいイカちゃん ゃん ゃんかわいいイカちゃん わいいイカちゃん ん んかわいいイカちゃん イカちゃん イカちゃんかわいいイカちゃん カちゃん カちゃんかわいいイカちゃん

いい 1 いイ 1 かわ 1 ちゃ 2 ゃん 2 わい 1 んか 1 イカ 2 カち 2

(ドヤッ

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.pdf

補足情報

• ちょっと試してみたい!

– (自己責任で自由に使用・加工してください)

– SuffixArrayを使った頻度カウント

• http://d.hatena.ne.jp/jetbead/20111012/1318422417

– Lossy Countingを使った頻度カウント

• http://d.hatena.ne.jp/jetbead/20111014/1318547950

補足情報

• 参考資料など

–岩波講座ソフトウェア科学15「自然言語処理」

–アルゴリズム・サイエンスシリーズ5 数理技法編

「オンラインアルゴリズムとストリームアルゴリズム」

–入門「自然言語処理」

top related