カテゴリー別アーカイブ: NLP

自作辞書のブートストラップを考える

かな漢字変換や形態素解析といった処理を行うにあたって、辞書は必要不可欠な存在です。これを一から自作することを考えています。

  1. 適切な単語のリストの作成
  2. 単語に対する読み、品詞の追加
  3. 単語に対するコスト値の算出
  4. データソースはWikipediaを使う

こんな感じでおそらくはできるでしょう。時間はかかると思うので、ゆっくり考えていくつもりです。

今はまず教師なし機械学習による単語分割を使って、単語リストを作ることを試みています。これについては階層Pitman-Yor過程に基づく可変長n-gram言語モデル(VPYLM)の実装が複数あり、それを使うことである程度下処理ができます。

Wikipediaの記事はhttps://dumps.wikimedia.org/jawiki/からダウンロードできます。本文のダンプはjawiki-latest-pages-articles.xml.bz2というファイル名です。これをWikipedia ExtractorというPythonスクリプトでプレーンテキストに変換できます。変換時に警告が出ますが、それはたいてい自己参照に関するものなので、放っておいても大丈夫そうです。

さしあたって、展開ディレクトリ/AA/wiki_00を対象にlatticelmをデフォルトパラメータで実施したところ、以下のような感じになりました。


$ head -10 data/AA/wiki_00
< doc i d=" 1 "u rl ="h t tps:/ / ja. wik iped ia. o rg/wi ki?cur id= 1 " ti tl e=" W i kip edi a : アッ プ ロー ドロ グ2 004 年 4 月 ">
W i kip edi a : アッ プ ロー ドロ グ2 004 年 4 月
< u l >
< /d oc >
< doc i d=" 2 " url =" htt ps :// ja. wik iped ia. o rg/wi ki?cur id= 2 " ti tl e=" W i kip edi a : 削 除 記 録 / 過 去 ログ 2 00 2 年 12 月 ">
W i kip edi a : 削 除 記 録 / 過 去 ログ 2 00 2 年 12 月
B e l o w i s a l i s t o f t h e m o s t r e c e n t d e l e t io n s .
A l l t i m e s s h o w n a r eser v er ( U . S . P a ci fi c ) t i m e .
< /d oc >
< doc i d=" 5 " url =" htt ps :// ja. wik iped ia. o rg/wi ki ?c urid= 5 " ti tle =" ア ン パサ ンド ">

ちょっと分割されすぎですね…ハイパーパラメータはいろいろとあるので、チューニングはいろいろと必要そうではあります。他に試された人の解説によると、初期状態がすべて分割されている点が大きいとのことです。

以前の記事で紹介した「続・わかりやすいパターン認識 教師なし学習入門」には通常のPitman-Yor過程までは解説されており、VPYMLに関してはなんとなくでしか理解出来ていません。もう少し自分の理解も深めたいところです。

単語分割自体は系列ラベル問題とみなしてRecurrent Neural Networkベースで処理することも出来るそうですが、教師あり学習なのできちんとしたデータセットを作る必要があります。そこにたどり着くまでのところを、教師なし学習との組み合わせでなんとか実現したいところです。

seq2seq用日本語対話データセット生成スクリプト

以前、Instagramの画像から食べ物の画像のデータセットを作成という記事を書きましたが、その中で「次はseq2seq用の対話データセットをどうにかしたい」とこぼしていました。なんとかそれを形にできました。

元データはDFSG準拠のライセンスでない点に注意してください。このツールによって生成したデータも同様の制限を受けます。

画像データセットに関しては複数種類があって、以前作った画像データセットの方にあまり需要はなかったようですが、今回はそれなりに参考にされていそうです。

できればDFSG互換なライセンスで公開できる形のデータセットが作りたいのですが、さすがに対話を一人でやるのはなかなか無理がありそうです。オープンなslackやIRCチャンネルを立てて、そういうデータを作るための雑談をしよう、という提案をしたら参加してくれる人はいますかね…?

 

Recurrent Convolutional Neural Networks for Text Classificationを実装した

RNN/LSTMでテキスト分類をしたいと思い、まず自分でネットワーク構造を考えてみました。word embeddingとLSTMを組み合わせて、単純な和を文章全体のベクトルとして扱うことを考えてみたのですが、これは思うように動きませんでした。さっぱり学習する気配がありません。

そこで既に提案されている手法を探してみると、word embeddingの配列をx軸固定の畳み込み演算するという手法がひとつありました(CNNによるテキスト分類)。TensorFlowでの実装もあったのですが、chainerでできないかと思いmax_poolingでstride=(0, 1)といった感じで指定してみたのですが、chainerではstrideの値が0より大きいことを想定しているため0除算が発生して動かせませんでした。

仕方がないので、もうひとつの手法「Recurrent Convolutional Neural Networks for Text Classification」を試してみました(参考: 研究開発:RNNで文書分類(Text Classification) )。

構造としては、いわゆる双方向RNNで単語の出現順、その逆順の2つの方向からRNN(実際にはLSTM)を使って、それらを結合し単語ごとに一つのベクトルを生成します。そして、すべての単語についてベクトル各要素の中の最大の値を取り出したベクトルを生成します。最大プーリングに相当する処理ですが、chainerのmax_pooling_2dは今回の用途には使えませんでした。代わりに、function.maximumが使えたのでそちらを使っています。

結果として、おおよそ望むような挙動を得られました。目視で分類したネガティブな文章とポシティブな文章とを分類することができました。

ソースコードは https://github.com/knok/rcnn-text-classification にあります。Qiitaにも解説記事を書きました(Recurrent Convolutional NNでテキスト分類)。

 

自由なデータの重要性

先日、NEologd Casual Talksというイベントに参加してきました。mecab-ipadicに新しいエントリーを追加するmecab-ipadic-neologdに関するイベントでした(Togetterまとめ)。

その中で改めて感じたのは、自由なデータの重要性です。イベントでは「言語資源」という観点でのデータの重要性が語られていました。形態素解析器の辞書として、継続的に更新・リリースがなされているのはNEologd以外ほとんどないという指摘がありました。

自分も2012年にSoftware Freedom Dayの国内イベントで「自由なデータ」という題で発表をしました。

今になって改めてこの資料を見ると、現在は若干状況が変化している部分もあります。NEologdの出現は、まさしくその一つです。

また、日本語の文章チェッカーとしてtextlintという実装の日本語ルールを作成・公開している人がいます。これまでLanguageToolに若干のルールをコミットしてきましたが、textlintのほうがかなり充実している感じです。

最近は機械学習をやってみたりしているのですが、これもまさにデータの質がかなり重要な領域です。変なデータを元に学習すると、そもそも学習が収束しなかったり、期待しない結果を返すモデルができてしまったりします。この領域でも、チュートリアルに使われる定番のデータセットが公開されています(MNIST, CIFAR-10等)。

最近、自分も画像分類タスクのテストのために作ったデータセットはあるので、なんとか公開できる形にできないかと考えています。実データはInstagramにあるもので、個々の画像のライセンスを確認しないまま集めて分類したのですが、画像URLの一覧という形なら問題なく配布できると思います。オライリー「実践 機械学習システム」でも感情分析用のツイートデータを人力で分類したもののID一覧から実データを取得する、という方法が取られていました。

 

実際に自分でデータの分類をやってみると、「どこまでをこの分類に含めるべきか」という根源的な問題を実感できます。実際、単純に検索しただけだと「明らかにおかしい」というものから「これはどうなんだろう」というものも出てきます。

この点について、先日の発表の中で印象的だったのが片山さんの「ファッションが大好きなので無限に洋服の文字列データを眺めるのが楽しかった」という部分でした。特定のドメインのテキストでも、きっと同じような感覚があるのだろうとなんとなく感じています。

ともあれ、公開できる形になったら、改めて告知したいと思います。

Debian上でmecab-ipadicをベースに単語を追加する

今時のMeCabには、新しく追加した単語だけコストを計算させる機能があるので、実際に動かしてみました。

参考となるURLはhttps://taku910.github.io/mecab/dic.htmlです。Web上には古いgoogle code上のページがまだ残っており、Googleで検索するとそちらが上位に引っかかるので注意が必要です。

前提として、mecab-utilsとmecab-ipadic(EUC-JP版、UTF-8は不可)がインストールされている必要があります。

次に、登録する単語のエントリーを用意します。諸事情により、EUC-JPで保存しておきます。以下は実際の例です。foo.csvという名前で保存しておきます。

日生協,,,,名詞,固有名詞,組織,*,*,*,にっせいきょう,ニッセイキョウ,ニッセイキョウ

次にモデルファイルを前述のページから取得します。リンク先はGoogle Driveのようです。この記事を書いている2015年10月の時点では、mecab-ipadic-2.7.0-20070801.modelというファイル名になっています。

必要なファイルが集まったら、以下を実行します。

$ mkdir tmp
$ cp /var/lib/mecab/dic/ipadic/* /usr/share/mecab/dic/ipadic/* tmp/
$ /usr/lib/mecab/mecab-dict-index -m ./mecab-ipadic-2.7.0-20070801.model \
  -d ./tmp -u foo2.csv -f euc-jp -t euc-jp -a foo.csv

すると、foo2.csvに単語のコストが記録された新しい結果が出力されます。

日生協,1292,1292,3062,名詞,固有名詞,組織,*,*,*,にっせいきょう,ニッセイキョウ,ニッセイキョウ

元のmecab-ipadicのソース(/var/lib/mecab/dic/ipadic/*.csv)とモデルmecab-ipadic-2.7.0-20070801.model)がEUC-JPであるため、新しい辞書の文字コードもEUC-JPに合わせる必要があります。

gensimのLdaModelバグ

gensimのLdaModelでtop_topic()を使おうとしたらいろいろバグが残っていたので、とりあえず手元で動くようにしてみました。詳細はMLに報告しましたが、これが正しい解法かは自信がありません。

問題は複数ありますが、まずは名前空間に不備があります。”np”と”ifilter”という2つの関数をglobalで利用しようとして失敗します。これは以下のように無理やり定義してやれば問題を回避できます。

import gensim
gensim.models.ldamodel.np = gensim.models.ldamodel.numpy
import itertools
gensim.models.ldamodel.ifilter = itertools.ifilter # for python2.x
#gensim.models.ldamodel.ifilter = itertools.filter # for python3

これ以外に、LdaModel.top_topics()に与えたcorpusによってはKeyErrorが発生する問題があります。これはMLにも書いたようなパッチで回避できました。

--- ldamodel.py.old     2015-06-01 22:40:03.484170877 +0900
+++ ldamodel.py 2015-06-01 22:42:06.016170862 +0900
@@ -776,9 +776,14 @@
         for topic in xrange(len(topics)):
             topic_coherence_sum = 0.0
             for word_m in topics[topic][1:]:
-                doc_frequency_m = len(doc_word_list[word_m])
-                m_set = set(doc_word_list[word_m])
+               x = doc_word_list.get(word_m, None)
+               if x is None:
+                       continue
+                doc_frequency_m = len(x)
+                m_set = set(x)
                 for word_l in topics[topic][:-1]:
+                   if doc_word_list.get(word_l, None) is None:
+                       continue
                     l_set = set(doc_word_list[word_l])
                     co_doc_frequency = len(m_set.intersection(l_set))
                     topic_coherence_sum += numpy.log(

私の環境では、これでgensim 0.11.1においてtop_topicsが機能するようになりました。