hatunina’s blog

メモと日記です

NLTKでIOBタグ付けと頻出単語描画とストップワード除去とシノニムを探す

色々試しました。

Tokenize ~ Pos tagging ~ chunking ~ IOB tagging

いじくる文章はiPhonewikiを使います。

import nltk

sent = """iPhone is a line of smartphones designed and marketed by Apple Inc. 
The iPhone line of products use Apple's iOS mobile operating system software. 
The first-generation iPhone was released on June 29, 2007, 
and multiple new hardware iterations with new iOS releases have been released since."""


まず、Tokenizeはこう

tokens= nltk.word_tokenize(sent)
print(tokens)


実行結果

['iPhone', 'is', 'a', 'line', 'of', 'smartphones', 'designed', 'and', 'marketed', 'by', 'Apple', 'Inc', '.', 'The', 'iPhone', 'line', 'of', 'products', 'use', 'Apple', "'s", 'iOS', 'mobile', 'operating', 'system', 'software', '.', 'The', 'first-generation', 'iPhone', 'was', 'released', 'on', 'June', '29', ',', '2007', ',', 'and', 'multiple', 'new', 'hardware', 'iterations', 'with', 'new', 'iOS', 'releases', 'have', 'been', 'released', 'since', '.']


単語で分けるだけですね。英語なのでわかりやすくて良い。
次は品詞タグ付けです。

pos_tags = nltk.pos_tag(tokens)
print(pos_tags)


実行結果

[('iPhone', 'NN'), ('is', 'VBZ'), ('a', 'DT'), ('line', 'NN'), ('of', 'IN'), ('smartphones', 'NNS'), ('designed', 'VBN'), ('and', 'CC'), ('marketed', 'VBN'), ('by', 'IN'), ('Apple', 'NNP'), ('Inc', 'NNP'), ('.', '.'), ('The', 'DT'), ('iPhone', 'JJ'), ('line', 'NN'), ('of', 'IN'), ('products', 'NNS'), ('use', 'VBP'), ('Apple', 'NNP'), ("'s", 'POS'), ('iOS', 'NN'), ('mobile', 'NN'), ('operating', 'VBG'), ('system', 'NN'), ('software', 'NN'), ('.', '.'), ('The', 'DT'), ('first-generation', 'NN'), ('iPhone', 'NN'), ('was', 'VBD'), ('released', 'VBN'), ('on', 'IN'), ('June', 'NNP'), ('29', 'CD'), (',', ','), ('2007', 'CD'), (',', ','), ('and', 'CC'), ('multiple', 'JJ'), ('new', 'JJ'), ('hardware', 'NN'), ('iterations', 'NNS'), ('with', 'IN'), ('new', 'JJ'), ('iOS', 'NN'), ('releases', 'NNS'), ('have', 'VBP'), ('been', 'VBN'), ('released', 'VBN'), ('since', 'IN'), ('.', '.')]


はい。
そしてチャンキング

tree = nltk.ne_chunk(pos_tags)
print(tree)


実行結果

(S
  (GPE iPhone/NN)
  is/VBZ
  a/DT
  line/NN
  of/IN
  smartphones/NNS
  designed/VBN
  and/CC
  marketed/VBN
  by/IN
  (PERSON Apple/NNP Inc/NNP)
  ./.
  The/DT
・
・
・


ちょっとこれよくわからず。。

なんだろ。treeって描画用?

まあ、気にせずIOBタグ付けはこう

iob = nltk.tree2conlltags(tree)
print(iob)


実行結果

[('iPhone', 'NN', 'B-GPE'), ('is', 'VBZ', 'O'), ('a', 'DT', 'O'), ('line', 'NN', 'O'), ('of', 'IN', 'O'), ('smartphones', 'NNS', 'O'), ('designed', 'VBN', 'O'), ('and', 'CC', 'O'), ('marketed', 'VBN', 'O'), ('by', 'IN', 'O'), ('Apple', 'NNP', 'B-PERSON'), ('Inc', 'NNP', 'I-PERSON'), ('.', '.', 'O'), ('The', 'DT', 'O'), ('iPhone', 'JJ', 'B-ORGANIZATION'), ('line', 'NN', 'O'), ('of', 'IN', 'O'), ('products', 'NNS', 'O'), ('use', 'VBP', 'O'), ('Apple', 'NNP', 'B-PERSON'), ("'s", 'POS', 'O'), ('iOS', 'NN', 'B-ORGANIZATION'), ('mobile', 'NN', 'O'), ('operating', 'VBG', 'O'), ('system', 'NN', 'O'), ('software', 'NN', 'O'), ('.', '.', 'O'), ('The', 'DT', 'O'), ('first-generation', 'NN', 'O'), ('iPhone', 'NN', 'B-ORGANIZATION'), ('was', 'VBD', 'O'), ('released', 'VBN', 'O'), ('on', 'IN', 'O'), ('June', 'NNP', 'O'), ('29', 'CD', 'O'), (',', ',', 'O'), ('2007', 'CD', 'O'), (',', ',', 'O'), ('and', 'CC', 'O'), ('multiple', 'JJ', 'O'), ('new', 'JJ', 'O'), ('hardware', 'NN', 'O'), ('iterations', 'NNS', 'O'), ('with', 'IN', 'O'), ('new', 'JJ', 'O'), ('iOS', 'NN', 'B-ORGANIZATION'), ('releases', 'NNS', 'O'), ('have', 'VBP', 'O'), ('been', 'VBN', 'O'), ('released', 'VBN', 'O'), ('since', 'IN', 'O'), ('.', '.', 'O')]


めっちゃ簡単やんけ〜
英語ずるくない?
チャンキングとIOBタグ付けこれだけでできるのってすごくないですか?
日本語だったら品詞タグ付けまではMeCabでやって、チャンキングとかIOBタグ付け、、、どうすんの?目視?

頻出単語描画

他にも便利な関数がたくさんあったのでついでに試します。
文章は引き続き同じものを使います。
FreqDistという関数を使うと計算できるっぽい。

freq = nltk.FreqDist(tokens)
print(freq.items())


実行結果

dict_items([('first-generation', 1), ('software', 1), ('June', 1), ('have', 1), ('been', 1), ('and', 2), ('is', 1), ('mobile', 1), ("'s", 1), ('new', 2), ('iPhone', 3), ('The', 2), ('.', 3), ('Apple', 2), ('released', 2), ('multiple', 1), ('marketed', 1), (',', 2), ('operating', 1), ('2007', 1), ('use', 1), ('29', 1), ('designed', 1), ('system', 1), ('iOS', 2), ('a', 1), ('line', 2), ('iterations', 1), ('was', 1), ('Inc', 1), ('on', 1), ('of', 2), ('products', 1), ('releases', 1), ('since', 1), ('hardware', 1), ('smartphones', 1), ('by', 1), ('with', 1)])


数字部分は各単語の登場回数です。
これをグラフで描画することができます。

freq.plot(20, cumulative=False)


実行結果

f:id:hatunina:20180414222329p:plain
便利〜、何かに使えそう〜

ストップワードでクリーニング

上のグラフを見ると真ん中ぐらいに「of」があります。
こういう文章によく登場するけどあんま意味ない、みたいなやつは除去してあげる必要があります。
nltkには各言語のストップワードが用意されています。

from nltk.corpus import stopwords
stopwords = stopwords.words('english')

print(len(stopwords))
print(stopwords)


実行結果

179
['i', 'me', 'my', 'myself', 'we', 'our', 'ours', 'ourselves', 'you', "you're", "you've", "you'll", "you'd", 'your', 'yours', 'yourself', 'yourselves', 'he', 'him', 'his', 'himself', 'she', "she's", 'her', 'hers', 'herself', 'it', "it's", 'its', 'itself', 'they', 'them', 'their', 'theirs', 'themselves', 'what', 'which', 'who', 'whom', 'this', 'that', "that'll", 'these', 'those', 'am', 'is', 'are', 'was', 'were', 'be', 'been', 'being', 'have', 'has', 'had', 'having', 'do', 'does', 'did', 'doing', 'a', 'an', 'the', 'and', 'but', 'if', 'or', 'because', 'as', 'until', 'while', 'of', 'at', 'by', 'for', 'with', 'about', 'against', 'between', 'into', 'through', 'during', 'before', 'after', 'above', 'below', 'to', 'from', 'up', 'down', 'in', 'out', 'on', 'off', 'over', 'under', 'again', 'further', 'then', 'once', 'here', 'there', 'when', 'where', 'why', 'how', 'all', 'any', 'both', 'each', 'few', 'more', 'most', 'other', 'some', 'such', 'no', 'nor', 'not', 'only', 'own', 'same', 'so', 'than', 'too', 'very', 's', 't', 'can', 'will', 'just', 'don', "don't", 'should', "should've", 'now', 'd', 'll', 'm', 'o', 're', 've', 'y', 'ain', 'aren', "aren't", 'couldn', "couldn't", 'didn', "didn't", 'doesn', "doesn't", 'hadn', "hadn't", 'hasn', "hasn't", 'haven', "haven't", 'isn', "isn't", 'ma', 'mightn', "mightn't", 'mustn', "mustn't", 'needn', "needn't", 'shan', "shan't", 'shouldn', "shouldn't", 'wasn', "wasn't", 'weren', "weren't", 'won', "won't", 'wouldn', "wouldn't"]


そして、これらを元の文章から取り除き、もう一度頻出単語をチェックしてみます。

clean_tokens = tokens[:]
 
for token in tokens:
    if token in stopwords:
        clean_tokens.remove(token)

clean_freq = nltk.FreqDist(clean_tokens)
clean_freq.plot(20, cumulative=False)


f:id:hatunina:20180414223303p:plain
真ん中ぐらいにあった「of」が無くなってますね。

シノニムを探す

シノニム(類義語)も辞書が用意されているらしい。

from nltk.corpus import wordnet
 
syn = wordnet.synsets('Japan')

print(syn[0].definition())
print(syn[0].examples())


実行結果

a string of more than 3,000 islands to the east of Asia extending 1,300 miles between the Sea of Japan and the western Pacific Ocean
[]


wordnet.synsets('Japan')でシノニムを探す単語を指定します。
そして、syn[0].definition()とするとwordnetに登録されている定義を取得できます。
上ではJapanの定義を取得してますが、島3,000もないでしょ、おかしくね?って思ってググったら、

海上保安庁は、1987年(昭和62年)に『海上保安の現況』において日本を構成する島の数を「6,852」としている。

日本の島の一覧 - Wikipedia

めっちゃあるやん、島


で、シノニムはこう

for syn in wordnet.synsets('Japan'): 
    for lemma in syn.lemmas(): 
        print(lemma.name())


実行結果

Japan
Japanese_Islands
Japanese_Archipelago
Japan
Nippon
Nihon
japan
japan
japan


ちゃんと「Nihon」とかもあるのがいいすね。

参考

参考はこの辺です。

Natural Language Toolkit — NLTK 3.2.5 documentation

便利機能系はこちらです。

likegeeks.com

他にも対義語を探すこと等もできるみたいです。

「パーフェクトPython」を読みました

読みました。

gihyo.jp

めちゃくちゃ良かった。
最近読んだ中で一番良かった。
やはり良書と呼ばれるものはハズレがない

Python以外の言語経験がある方ならこれ一冊で十分
「みんなのPython」→「パーフェクトPython」の順番でもいいかもしれない。
初めてのプログラミングならこの間に何かもう一冊欲しいかも。

内容は2015年発売なのでそれなりに古いです。
でも古いだけあって機械学習の話がないのが逆に良い。

そもそもPythonってこんなことできるんだってことが知れて良い。

目次はこんな感じです。

Part1 Python ~overview
1章 Pythonの概要
Part2 言語仕様
2章 Pythonの基本
3章 型とリテラル
4章 制御構文
5章 関数
6章 クラス
7章 モジュールとパッケージ
8章 拡張モジュールと組み込み
9章 標準ライブラリ
Part3 実践的な開発
10章 コマンドラインユーティリティ
11章 チャットアプリケーション
12章 アプリケーション/ライブラリの配布
13章 テスト
14章 Webプログラミング
Part4 適用範囲
15章 学術/分析系ライブラリ
16章 マルチメディア
17章 ネットワーク
18章 データストア
19章 運用/監視
Appendix
A 環境構築
B 標準ライブラリ

パーフェクトPython:書籍案内|技術評論社

Part1 ~ 2は「みんなのPython」とあまり変わらず。
ソースコードが具体的になっていたり簡単な内容は端折っているので知ってる人ならサクサク読めます。

Part3以降はタイトル通りの業務で使うような話です。
この辺りの基礎を知らずにフレームワークを使うのは抵抗があるので、すごく良かった。

特に14章Webプログラミングがとても勉強になりました。
この辺はまたまとめていこうと思います。

とりあえずPythonの本を読むのはこれで一旦終了かな〜
Webに特化したPython本があれば読みたいけど。。。
でもこれ以上のものを読むとしたらオライリーになるのかなあ?

crfsuiteとpycrfsuiteについて

CRFについて勉強中です。
色々調べつつサンプルを動かして見ました。

こちらはコマンドラインで実行できるcrfsuiteのチュートリアル記事です。

CRFsuite - Tutorial on Chunking Task -

英語ですが、難しい単語は出てこないですしざっくり大まかな概要は把握できると思います。特にfeature generationがわかりやすくてよかったです。固有表現抽出タスクについて勉強を始めた際に特徴量の形状が全く掴めなかったのですが、下図のように一般化されているとスッと頭に入って来ます。

・w[t-2], w[t-1], w[t], w[t+1], w[t+2],
・w[t-1]|w[t], w[t]|w[t+1],
・pos[t-2], pos[t-1], pos[t], pos[t+1], pos[t+2],
・pos[t-2]|pos[t-1], pos[t-1]|pos[t], pos[t]|pos[t+1], pos[t+1]|pos[t+2],
・pos[t-2]|pos[t-1]|pos[t], pos[t-1]|pos[t]|pos[t+1], pos[t]|pos[t+1]|pos[t+2]

tというインデックスが対象となっている単語です。
tを基準にして前後2単語と前後1単語との組み合わせ、また同様に品詞を特徴量と使用するわけです。

例えば、「4月は代々木公園で花見をします。」という文章で特徴量を考えてみます。

import MeCab
m = MeCab.Tagger()
print(m.parse("4月は代々木公園で花見をします。"))

実行結果

4月   名詞,副詞可能,*,*,*,*,4月,シガツ,シガツ
は 助詞,係助詞,*,*,*,*,は,ハ,ワ
代々木公園 名詞,固有名詞,地域,一般,*,*,代々木公園,ヨヨギコウエン,ヨヨギコーエン
で 助詞,格助詞,一般,*,*,*,で,デ,デ
花見  名詞,サ変接続,*,*,*,*,花見,ハナミ,ハナミ
を 助詞,格助詞,一般,*,*,*,を,ヲ,ヲ
し 動詞,自立,*,*,サ変・スル,連用形,する,シ,シ
ます  助動詞,*,*,*,特殊・マス,基本形,ます,マス,マス
。 記号,句点,*,*,*,*,。,。,。
EOS

ここでtを「代々木公園」とすると各特徴は下記のようになります。

w[t-2], w[t-1], w[t], w[t+1], w[t+2] = 4月, は, 代々木公園, で, 花見
w[t-1]|w[t], w[t]|w[t+1] = は|代々木公園, 代々木公園|で
pos[t-2], pos[t-1], pos[t], pos[t+1], pos[t+2], = 名詞, 助詞, 名詞, 助詞, 名詞
pos[t-2]|pos[t-1], pos[t-1]|pos[t], pos[t]|pos[t+1], pos[t+1]|pos[t+2] = 名詞|助詞, 助詞|名詞, 名詞|助詞, 助詞|名詞
pos[t-2]|pos[t-1]|pos[t], pos[t-1]|pos[t]|pos[t+1], pos[t]|pos[t+1]|pos[t+2] = 名詞|助詞|名詞, 助詞|名詞|助詞, 名詞|助詞|名詞

で、「代々木公園」に対するラベルは固有表現かつ名詞なので「B-NP」とかになるわけですね。
IOB2といったタグ付け方法が有名っぽいですが他にも色々あるようです(まだ調べてない)。


イマイチ理解仕切れていないのですが、IOB2タグ付けはみなさんどうやっているのでしょうか?
ひたすら目視でタグ付け?尋常ではないくらい大変な作業だと思うのですが、、、
ちなみに、僕が今お世話になっている会社では、データがちょっと特殊なため目視ではなくPythonで自動化できています。


話はそれましたが、これで一つの単語に対し正解データとなるラベルと各特徴量が準備できました。
この処理を各単語について行うことになるわけですが、実際に文章を読み込んで特徴量を作ってみると、元のデータ量はそんなに多くないのに生成してみるとめちゃくちゃデータ量増えるやんけ。。。ってなります。学習よりもfeture generateの方が時間がかかったりします。


次は、crfsuiteをPythonで使えるようにしたpycrfsuiteのサンプルを動かします。

github.com

NLTKに含まれているコーパスを利用しています。
だがしかし、スペイン語

試しにコーパスの一部を翻訳にかけてみると、、、

原文

ElAbogadoGeneraldelEstado,DarylWilliams,subrayóhoylanecesidaddetomarmedidasparaprotegeralsistemajudicialaustralianofrenteaunapáginadeinternetqueimposibilitaelcumplimientodelosprincipiosbásicosdelaLey.

翻訳

国家評議会(Daryl Williams)は、法律の基本原則を遵守することを可能にするインターネットページの観点から司法制度を保護するための措置を講じる必要があることを強調した。

めっちゃ真面目な内容でした。

こんな感じの文章に対して学習と予測をさせるのですが、先ほどのように特徴量を生成しないといけません。
今回のサンプルではword2featuresという関数で実装されています。
おそらく、この部分は分析した文章の性質によっては独自で作ることはできそうです。
ただ、細かくなりすぎるとメンテしづらいんだろうなあ。。。

データの中身を見ながら動かした形跡はgitへあげたので興味のある方はこちらへどうぞ

github.com
ちなみに、NLTKのコーパスを初めて使う際は下記コードを先に実行してコーパスをダウンロードする必要があるので注意

import nltk
nltk.download()



こんな感じでCRFについて勉強中です。
アルゴリズムについてはまた近々まとめるかもしれません。
今のところ、このスライドがわかりやすい気がしてます。

http://2boy.org/~yuta/publications/nlp2006-slides.pdf