Deep Learningでスケジュール調整してみる、ための自然言語処理をしてみた
GREE Advent Calendar 2015の1日目担当のふじもとです、グリー株式会社でCTOをしてます、もう10年目です。
今年もChristmasに向けてみんなで毎日更新していきますので、ぜひぜひよろしくおねがいします。
わりとどうでもよい序
去年、一昨年は25日担当だったんですが、今年は (なんでかは知らないけど) 1日目書くことになったので、ちょっと趣向を変えて技術的な内容にしてみたいと思います。
なおタイトルに、Deep Learningだの自然言語処理 (以下NLP) だの書いてますが、ぼくは機械学習やNLP、はたまたDeep Learningの専門家でもなくって、たしなむ程度に勉強していたくらいです。ので、この記事はアルゴリズムについて詳しくなろうっていうよりは、いろいろ試してみたっていう方向になってます。
Summary
- わりと単純なCNN + 少ないコーパスでも、タスクによっては実用レベルでNLPができちゃいます (結果はこちら で試せます / コーパス収集兼ねてます、ので、ぜひ遊んでみてください)
- TensorFlow便利です、基本的なつかいかたはもう多くの記事があるので、ここでは他に書いていなかった点だけ補足しています
- 今後は、MV-RNN、RNTNなどの別のDeep Learningのアルゴリズムと、既存の成熟したアルゴリズムとの比較をしてみたかった、がそこはまだできていないので、今度試したらまた記事書いてみたいと思います
Deep Learningで自然言語処理
ということで本題にもどって、Deep Learningで人間とスケジュール調整...をしようと思うと、必然自然言語のやりとりとかも発生し得るので、この記事ではまずはそこを試してみることにします。
Deep Learningのフレームワークは、みなさまご存知Chainer、Caffe、そして先月公開されたTensorFlowなどなどありますが、ここではせっかくなので一番新しいTensorFlowを使ってみます。
TensorFlow概観
TensorFlowのインストールや基本については、Qiitaにもたくさんの日本語記事もありますので、ここでは割愛して、あんまり書いてなさそうなところに絞って少しだけ補足しておきます。
MNIST, MNIST, MNIST
ということで、ぼくを含む「とりあえずTensorFlowっていうの試してみよう」派なかたがたは、MNISTのチュートリアルを試してみるかたが多いと思うんですが、このチュートリアルで構築しているネットワークだけでも、あれやこれやと「どのパラメータが最適なんだろう?」というポイントはあったりします。
patch size
チュートリアルや、見る限りのコードでは、各畳み込み層のpatch sizeを5 x 5に設定していますが、では6 x 6だとどうでしょう?7 x 7だと?とか思ってしまいます。ちなみにこの値は、入力されている配列のサイズより小さければ一応いくつでも動作はするはずです。また、必ずしも正方形(?)である必要もないので、たとえば5 x 4のほうが精度がでるかもしれません、なんとなくダメそうではありますが (ちなみにMNISTの例では、5 x 5以外だと徐々に精度が落ちていきました...のできっと試した限りでは5 x 5が最適っぽいですが、実はぼくはなぜこれが最適かは分かっていません)。
max pooling
プーリング層のアルゴリズムもmax poolingに限らず幾つかの手法が提案されています (そもそも元は平均値が主流だったとかなんとか)。また、これはあまり試す価値がない気もしますが、windowのサイズも2x2以外だとどうなるんだろうとか思ってしまいます (例えば 2 x 1のwindowでも学習させることは可能です、精度落ちるだけな気がするので試していませんが)。
なお、TensorFlowにはデフォルトで、max_pool()の他に、avg_pool()とmax_pool_with_argmax()というメソッドが用意されています。
ネットワーク
ネットワークについても、当然様々な手法が提案されています。TensorFlowのチュートリアルは充実していて、MNISTに続いてCIFAR-10の例があるわけですが、ここでは違ったレイヤ構造を作っています (入力のチャネルも3次元ですしね)。このあたりについては、日々世界中のすごいひとたちが多くのチャレンジをしていて、こちらのpaperは、日本語でILSVRC (ImageNet Large Scale Visual Reconition Challenge / 2012年でDeep Learningを利用したチームが精度を跳ね上げたの有名ですね、というかぼくもそれで知りました) で利用された様々な例が紹介されています。
しかしこうなると、もう手法を組み合わせたり、などなどは自動でやってくれないかなー、とか思ってしまいますね (他力本願)。ただ実際のところ、それをやるにはコンピューティングリソースがまだまだ足りなそうで、あぁどなたか100Gネットワークで繋がったGPUインスタンスを1,000,000インスタンスくらいいただけないものでしょうか、などと思ってしまいます (持て余しそうですが)。
実際なんらかのタスクがあったときに、ではどういうレイヤを作って、どういうパラメータにすればいいのか、そもそもどのアルゴリズムを選択すればいいのか、は、まだまだ人間が考えなければいけないので、その選択も不要になったらさらに身近にさまざまなタスクが試せそうです。
FAQ: tensorflow.Session()が終わらない
テスト用のunbutuでTensorFlowを動かしていると、しばしば処理が終わらないことがあります。traceすると、tensorflow.Session() (あるいはInteractiveSession()) でhangしているのですが、これは (straceするとわかる通り) あまりI/O等のないサーバだと、/dev/randomが十分なエントロピーを得られずに止まっている状態です。
ということで、とりあえずproductionではないことがほとんどだと思うので、エントロピーをエミュレートしてしまえばokです。ubuntuだと一番手軽なのはhavegedあたりのインストールです。
1 |
$ sudo apt-get install haveged |
で、おそらく止まることはなくなるはずです。
FAQ: summaryを出力しようとしたらエラー
TensorBoardでグラフをみるために:
1 2 |
summary_op = tf.merge_all_summaries() summary_writer = tf.train.SummaryWriter("/tmp/tensorboard", sess.graph_def) |
として各stepで
1 2 |
summary_str = sess.run(summary_op, feed_dict={x_pl: batch[0], y_pl: batch[1], keep_prob: 1.0}) summary_writer.add_summary(summary_str, i) |
とするサンプルコードが多くありますが、そこで
1 |
TypeError: Fetch argument None of None has invalid type <type 'NoneType'>, must be a string or Tensor. (Can not convert a NoneType into a Tensor or Operation.) |
というエラーがでる場合は、1つ以上の tf.scalar_summary() を呼び出してください (これがないと、add_summaryがエラーになります)。ぼくはちょっとだけハマりました。
タスクの設計
さて、TensorFlowについては本題ではないので、自然言語処理について考えてみます。とはいえあんまり難しいタスクは大変そうなので、いやなにが大変って多くの場合大量のコーパスが必要になるので、そこが大変だと思うのです。それはそれとして、ここでは状況を:
- 日程調整の依頼メールを出してお返事を受け取りました
と仮定します。例えば
1 2 3 4 5 6 7 8 9 10 11 12 13 |
XXX 様 平素より大変お世話になっております、YYYと申します。 改めて日程を調整させて頂きたく、ご連絡いたしました。大変恐縮ですが、下記候補日よりご都合はいかがでしょうか。 難しい場合はお気軽にお申し付けください。 ・12月 11日 (金) 13:30 ~ 16:00の間 ・12月 14日 (月) 15:00 ~ 18:00の間 ・12月 15日 (火) 15:30 ~ 16:30の間 ご検討のほど、よろしくお願い申しあげます。 |
というとっても丁寧なメールをお送りして、そのお返事がかえって来ました、というようなケースです。
そうするとほぼ全て場合:
- その日程でokです
- いやそこは無理なので別な日で
の2択になるだろうと考えられます。いきなり「そういえばさー」とか話題を逸らし出すような、それ以外のケースについては忘れましょう (実際には「あ、じゃぁあの人も追加で」とかいろいろあるんですが、それは機械での処理を諦める方向で)。という形で単純化すると、今回考えるタスクは:
- (メールの返信であろう)テキストを入力として受け取って
- (1) その日程でOK (2) その日程は無理 (3) なんだかわからない、の3値に分類する
くらいにしてしまえそうです。追加で、日付固有表現の抽出もできればなおよいです。
アルゴリズムの選択
というタスクに対して、どういうアルゴリズム (というかネットワークというか) が適切か、ということを決めなければいけないのですが、とりあえずDeep LearningでNLPといえばRNNがよく取り上げられていて、これはTensorFlowのチュートリアルにもありそうだし簡単に試せそうです...なんですが、意味の抽出、あるいは分類、と考えると直接は利用できなそうで、構築して言語モデルに対してさらにどういう処理を行うか、ってことになってちょっと大変そうです (このあたりはぼくの理解が浅いだけかもしれません)。
で、意味表現の抽出についてはMatrix Vector RNN (MV-RNN) あるいはRecursive Neural Tensor Network (RNTN) という手法も提案されていますが (そしてもちろん他にもたくさんあります)、正直なところ、論文読みながらRNTNをそれっぽく実装してみたんですが、全く精度があがらなくって、これはきっと何か間違えてるな、ということで今回の記事には間に合いませんでしたごめんなさい (いやー、これですごい精度でたぜー、とかだったらかっこよかったんですけどね)。で、今回はCNNをそのまま利用してみました、が、結論から書けば、ほんと何も考えずにまんま適用してみた割には予想より全然精度でたなー、という感じです、Deep Learningスゴイデスネ。
ただ、CNNを利用する際の制約事項として当然固定長、というのが出てきます。ただこれについては、30文節くらいとればだいたいは把握できるかな、ということで、それを超えるものは一旦落としちゃおう、と妥協してます。また、分かち書きにはお約束中のお約束でMecab、入力のvecotr化は、これもTensorFlowでのチュートリアルがありますが、ここではword2vecを利用しました (コーパスが小さいのでこれも30次元くらいでいいやととりあえず、としています)。
これで、入力されたテキストを[単語の30次元のvector] * [30文節] (文節が30未満の場合はzero paddingであとはmax pooling頼みです) という形にして、文ごとにクラスタリング、それぞれのスコアを判定してテキスト全体を3つに分類してみよう、というのが今回の企みです。
Training
まずはコード云々以前にコーパスを作らなければいけない、ので、とりあえず自分が溜め込んでいるメールから20通ほど「スケジュールに関するやりとり」なメールを探し出してきました。で、これを適当に正規化したり、日付表現を抜き出したり、分かち書きして、ラベルをつけていきます。出来上がりとしてはこんな感じです:
1 2 3 4 5 6 7 |
NAME XXX 様 714dab0258e8030257a677d00a0854a8 GREET 平素 より お 世話に なって おり ます 046a513ed3e10cd910642e6bb894ce67 GREET YYY で ございます 1daa392d8f64c446855fd7fe74f714cc GREET 来週 は どうぞ 宜しく お 願い いたし ます 428b941b26c5ecf9888ded62d9dd0a4d ACCEPT 当日 は TIME 迄 に ZZZ 受付 に 伺い ます f2134075c5d9070f55bbff39293d3377 GREET 引き続き どうぞ 宜しく お 願い 申し上げ ます 492c87cc7ed7a7c79ca5238e238bf426 |
末尾のmd5っぽい値は、上書き防止用のタグであまり深い意味はありません。これをたくさん用意しよう...と思ったんですが、あまりに面倒で結局1,000行くらいで止めてしまいました。こういうときこそクラウドソーシングを活用すべきなんだなぁ、と思った次第です。とりあえずコーパスがあまりにも小さいので不安は残るのですが、これでなんとかしてこそ、ですよね、ということで一旦これで試してみることにします。
Trainingは、まずはMNISTのチュートリアルにあるような初歩的なCNNで行いました。その様子は以下のような感じです:
もちょっとstep減らしても良かった気がしますが、Training自体にも結構時間を食うのでまだあまりチューニングはできていません (こういったTrainingの時間を短縮できれば応用の幅も一気に広がりそうですね)。
Test
学習の結果は
で試していただくことができます。なお、手元で試した限りでは「ちゃんと書いたメール風」だと適当なコーパスからは想像もつかないくらい全うにクラスタリングしてくれました。が、そもそも手元のメールのコーパスだし、自分で文章書いて試しても結構似てくるよね、それはそうか、という感じではあります。
ご注意
ということで、とにかくコーパスが足りなくて盛り上がりにかけるので、勝手ながら入力された文章は保存させていただいております。内容はふじもとが責任を持って管理いたしますが、くれぐれも個人情報等入力しないようお願いします。また、あとでふじもとが見るだろうということで、誹謗中傷等入力するのも、できれば避けてください、地味に傷つきます。
入力されたデータは、合間をみて学習してみて精度がどうなっていくか見てみたいと思います (ご報告できる機会があるかどうかはわかりませんが)。
さいごに
論文でも、サービス実装するわけでもないし、ということで今回は「ナイーブにやっても意外に精度でます!」っていうところくらいなんですが、もうちょっとコーパス集めたり、ネットワークの組み方を変えたり、違うアルゴリズムを試したり、そもそもDeep Learningじゃないほうが精度でるんじゃないかとか試したり、などいろいろやってみたいことは広がります (が、調子にのってGPUインスタンス使ってると結構な額になっちゃったりするんですよね...)。素敵なことに、こうやってまず試してみる、っていう敷居がものすごく低くなっているので、ぜひご興味もたれたかたは、あれこれ応用を試してみていただけると楽しいかなと思います。余談ですが、基本Trainingに入ると待ちなので、週末のんびり遊ぶのに最適です。
ということで、いろいろ省略してしまっている部分はありますが、そして実際Deep Learningを使う必要があるかどうかはわかりませんが、コンテキストを限定すれば (そしてコーパスがもうちょっと揃えば...)、スケジュール調整くらいは機械ができてしまいそうなが気がしてきます。年末年始もありますし、機会をみてあれこれチューニングしてみますので、ふじもとにメールおくったら謎な機械がメール返してきても生暖かく見守っていただければ幸いです。
さいごのさいごに
明日は、セキュリティ部な奥村さんによるCSIRTの記事です、お楽しみにー。
それでは、Happy Hacking!
いらなそうなあとがき
(なんか書いてみたものの、いらなそうなのであとがきってことにしてみました...)
昨今、これからはDeep LearningでAI!という記事を多く見かけますし、製品も増えてきているように思います。が、もちろん全てではないですが、なんとなくこの盛り上がりに違和感を覚えたりすることもあります、こんな記事書いておいてなんですが。
このあたりは主観なので様々意見あると思いますが、様々な分野において精度が上がったり応用分野が広がっているのは事実です、が、一方で機械が人間のように成長していく、という夢のようなことが起こるってことも当面ないでしょうと思うわけです。一般論として、現在の人工知能 (あるいはDeep Learningとかで提供されるなんらかのタスクを解くサービス) は、精度が向上するとその応用分野が狭まりますし、1つのモデルで複数のタスクを処理できるってのはまだあんまり聞いたことがないです。と思ったら、全く同じようなことをMiyakeさんも書かれていたのできっと確からしい話なんだと思ってます。つまりはリンク先のMiyakeさんのポストの通りponanzaがそのままチェスに使えるのか、って話ですね (なんかこれはこれで面白そうなテーマですが)。
ので、あるタスクを解きたい、と思ったらそれに特化したかたちでタスクを分解して、入力を調整して、アルゴリズムを選択したり改善して...ということが必要になるわけです。もちろんそれすらも機械で...というと夢はひろがりますが、それはまだまだ先のことでしょう (第4次か第5次AIブームくらいで...)。10-20年先を見て研究開発をするのならまた違った話ですが、目下Deep Learningなりでなにかしよう、と思ったら、アルゴリズムを理解して、タスクを理解して、適用分野を上手に選ぶことが必要だと思うわけです。ただ、上手に分野を選んでがんばれば「おー、これすげー」っていうことができそうなのもまた事実かなと思っていて、確かにスケジュールの調整とかなら機械同士で、あるいは機械と人間とでできちゃいそうだな、ってのが今回の記事のきっかけになってます (あくまできっかけ)。
情報収集
とはいえ、Deep Learningの分野は現在まさに日進月歩、といった状況で日々いろいろな改善が行われています、というか、いるようです。そんな状況に手軽にキャッチアップするにはPFNの岡野原さんのtwitterアカウントをフォローするのが一番です、たぶん。ちなみに完全に蛇足ですが、個人的に命名している「岡野原さん現象」ってのがありまして、勉強会とかでの岡野原さんのおはなしがほんとにわかりやすくて、あまりわかりやすいのでなんかすごい出来る気になって家に帰ってくるんですが、実際実装してみるとあれこれつまづく、ということを指してます。多分100人くらいのひとが「あーわかる」とうなずいてくれるんじゃないかと勝手に思っています...がどうでしょう、ぼくだけか...。
NLPにおけるDeep Learning
Deep LearningのNLPな分野における活用は、他の領域とは異なる難しさがあるのは想像に難くないと思います (画像処理が簡単ってわけもないんですが)。
少し考えて、まず入力が可変長でめんどうそうです。さらに、日本語 (だけではありませんが) だと、分かち書きが必要そうです。いえ、タスクによっては分かち書きをしない、というアプローチもありかもしれませんが、分かち書きをしたほうが精度が上がりそうです (文字単位での処理とどちらが精度上がるか、っていう検証結果も結果は見えてそうな気がしつつ面白そうではありますが)。で、単語単位で処理が可能になったとして、その単語をそのまま入力に使うことはあんまりなくて、なんらかの形で数値なりベクトルなりにする必要があります。さらにさらに「意味」みたいなことを考えると、事前に正規化もしておいたほうが良さそうな気がします、もうなんか「㏾」とかやめてほしいんですけど、とか思うわけです。
多かれすくなかれこういった処理はどんなタスクでも必要ですが、NLPの場合は特にモデルそのものよりも依存する度合いが大きいことが多いです。このあたりの話や、NLPにおけるDeep Learningの適用についての基本的な考え方は、少し前のものにはなりますが自然言語処理のための深層学習という記事が非常によくまとまっています。また、自然言語処理におけるディープラーニングの発展という記事もわかりやすいですし、もうちょっと最近のものでは深層学習時代の自然言語処理というスライド (とそこからのリンク) を読むといろいろと分かった気になれる気がします。が、この辺りがまだまだ入り口だと考えると、やっぱり奥が深いですね、楽しいのは間違いないんですが。
また、少し話はそれますが、NLPにおけるDeep Learningの活用はまだまだこれから、という状況でもあって、下手をすると (タスクによっては) すごい頑張ったDeep Learningのモデルよりも、単純なルールベースのほうが精度が良かったりもします。ありそうなところでは、固有表現抽出...のなかでの日付表現抽出とかは、分かち書きは必須になりますが、もうルールベースのほうが全然精度上げやすいんじゃないかと思っています。で、相変わらずこの分野には特になんの自信もないので、すごいかたがたの発言を引くに:
ルールベースNLPの批判にメンテナンス性の悪さが挙げられることが多いけど、それは当時の貧弱なソフトウェア開発技術の上に構築されたからであって、今時の手法で再構築すればそうでもないのかなと。機械学習であれなんであれ設計が腐ってるとメンテナンス性は悪い。
— Taku Kudo (@taku910) November 13, 2015
ということなので、このコンテキストに意図が100%合ってるかどうかはわかりませんが、(すくなくとも現段階でアプリケーションを書くとするなら)適切なものを選びましょう、ということですね。
なんにしても、やっぱりこういうのは試していてとっても楽しいので、ちょっと時間が空いたら試してみていただけると、夢が広がる感じがするのでオススメです。