ゼロから作るDeep Learning ―Pythonで学ぶディープラーニングの理論と実装 を買った

書店をぶらついていると、真っ赤なタイトルの本が目に飛び込んできた。

ページをめくってみた。すごい。勉強になる。数式読めない人けど、なんとなくDeep Learningが理解できるかも知れないきざしのきざしの兆候を感じる。すごい。うじゃひょぇえええええ!!!!!期待にドキドキ胸が鼓動を早める。そう、これは…買っただけで勉強しない時の予感。

ふと、ページをめくる指が震える。
おかしい。何かがおかしい。かすかに感じるこの違和感はなんだろう。もしかして、この素晴らしい本には、ひょっとして一点だけ欠陥と言わざるをえないところがあるのではないか。すべて整っているのに肝心のところで、どこか画竜点睛を欠くのではないか。

その原因は、最初はぼんやりとしていたが、次第に誰の目にもわかるほど明瞭になっていった。

どういうわけか、本文中のコードに Ruby + NArray が一行も使われてないのである。 そのかわり Python + numpy という一風変わった言事とライブラリが使用されている。Python? パイソン? 聞いたことがない新鮮な響きだ。Rubyはどこへいったのだろう? 公園だと思ったら行ってみたら墓地だった。そんな衝撃を想像してほしい。

これでは作者の美的センスを疑わざるをえない状況である。それだけではない。近代文明社会への冒涜、民主主義への挑戦というものではなかろうか。久々に怒りで額に汗が流れた。グルグルと景色が回りはじめ、書棚に並ぶ本たちの笑い声がドッと湧いてこだました。

※ 本文はフィクションです。謹んで勉強させていただきます。

無知をさらす
・損失関数についてあまり意識してなかった
 ・交差エントロピー誤差というものを知らなかった
 ・なるほどsoftmaxと交差エントロピー誤差はセットなのね
・数値微分という言葉を知らなかった
・勾配によるニューラルネットワークの更新がよく理解できない
・chainerって連鎖率(chain rule)から名前がつけられたのだろうか
 ・合成関数の微分は合成関数を構成するそれぞれの関数の微分の積によって表すことができる
・ハイパーパラメータという言葉はよく聞くが、なぜハイパーというのか知らなかった
・ミニバッチはランダムにサンプルを取得するものだと知らなかった(順番にサンプル取得するものだとばかり思っていた)
・加算、乗算の計算グラフによる逆伝播を理解した
・Adamが理解できないぞ?
・Xavierの初期値は、sigmoidやtanhに有効だが、ReLuには「Heの初期化」がある。へー。
・Batch Normalization 今度やってみよう
・ハイパーパラメータの最適化は結局総当り式?
・特徴マップという言葉を知らなかった
・im2colねえ…。これRubyだけで実装するとやっぱりfor(each)使わざるを得ない気がするが。

自信をもったところ
なんだかんだですでに実装したことがあるような内容も多い。①パーセプトロン、②2層ニューラルネットワーク、③計算グラフ ④バッチ処理 ⑤CNNみたいな順番で勉強してきてよかったなと。経験的に好きな事はつまみ食い的に勉強した方が、かえって効率的に勉強できる気がする。むしろこの本だけでDeep Learning勉強しようとしていたらどこまで理解出来てたか疑問だ。

この本のおかげでchain ruleと計算グラフについて少し理解した。
加算ノードの逆伝播と、乗算ノードの逆伝播理解したから。Numo::NArrayでLSTMにもう一回挑戦してみたいな。

RubyでKaggleのMnistを試してみた

はじめてのKaggle

 先のエントリーで医用画像認識ソフト開発の米EnliticがKaggleでエンジニアを集めているということを知って、技術者でもなんでもないけどKaggleをちょっと試してみたくなった。

Mnist

 チュートリアルでMnist手書き文字認識があるそうなので、以前作成したRubyでニューラルネットでMnist認識するコードを使って投稿してみた。使用したニューラルネットワークはこんな感じ。隠れ層が大きくなると実行に時間がかかるので200程度にとどめておいた。

 3層のニューラルネットワークなので、少し無理をすればディープラーニングの定義に当てはまるかも知れない…いや、やっぱり無理かな。

バックプロパゲーションだけで多層ニューラルネットワークを学習させてみる

 なぜ単純なバックプロパゲーションだけで多層ニューラルネットワークが学習できてしまうのかというと(Mnist手書き文字認識が簡単な問題だからかも知れないが)10点中10点満点で正解した場合でも「おまえはまだまだ出来るはずだ。次から100点を取らないと。まだ90点足りないよ!」いう感じ(ニューラルネットがつらそうですね)で大げさに誤差を逆伝播させる仕組みにしているからだと思う。多分。以前試した時は4層〜6層ぐらいの先細りの多層パーセプトロンなら、denoising autoencoder等の方法を使わなくても学習できていた。(softmaxがよくわかってなかった頃に思いつきで実装しただけなので、何という手法なのかは知らない。そしてこの方法で作られた重みはdenoising autoencoderで学習させた重みと比較して可視化した時にあまり綺麗とは言えない。)

結果

結果は正解率 0.97657 で1218チーム中470番ぐらいでした。(でもこれ画像の並行移動してなかったら、Mnistの出典データを参照するだけで正解率100%簡単に出せるんじゃ…)

学習時間は手元のパソコンで2時間弱かかった。

改善できそうなところ

 これまでにもParallelsによる並列計算による改良を何回か試みたけどうまくいってない。順伝播(評価時)はうまくいく。パイプで返却されるデータが少ないためかThreadの待ちがうまく動く。CPUのファンがぶんぶん回って、htopコマンドで4つのCPUの使用率100%になるのを見るのは爽快だ。しかし逆伝播では、パイプで返却される重みのデータ量が多すぎるせいか、Threadの待ち合わせがうまく動いてくれない。STDOUT.sync = true すればいいとかそういう問題でもないみたいだ。ここを上手くやって複数のCPUで動いてくれれば、もうすこし高速化できると思うけど難しい。

 あとは旧NArray + opencl_ruby_ffiを使えばGPUによる高速計算もある程度できると思う。しかしC言語も読み書きできない私のような素人さんにはちょっと敷居が高すぎるというのが正直なところだ。NarrayからGPUのメモリに移動して、GPUのメモリからNArrayに移動して、NArrayからGPUのメモリに移動して、を繰り返す時間的ロスってどのぐらいなのか気になる。OpenCLは手元のパソコンで試していてPCがフリーズしたりした時に萎えるけど、Amazon EC2 のGPUインスタンスでもopencl_ruby_ffiが動くことを確認したので今度試してみたいところ。

 ひと通りKaggle体験を楽しんだあと、日本にもKaggleのようなサイトがないか探してみた。いくつかみつかったけど、どこもKaggleほどの活気はなく、なんだか閑散としていてほんの少し寂しい気持ちになったのでした。

Amazon EC2 試してみた

 chainerでより気軽に遊ぶためにAmazon EC2を試してみた。そして思った。凄いなあと。ディープラーニング向けのレンタルサーバーはこれからの分野のようで、IDCフロンティアは、近日サービス開始となっている。アリババにはGPUクラウドがあるみたいだけど日本語の情報が少なすぎて敷居が高い。手元のパソコンでDeep Learningを試したい人は初期費用20万円ぐらい計上してGPU付きのPCを準備する必要があったけど、もうそんな時代ではないみたいだ。でも、いい事ずくめで興奮しているのに、どうしてもクラウドコンピューティング万歳という気分にはなれない。なんでだろう。それはともかく、はやくRubyでNArray使って簡単にGPU計算できる時代になりますように。

犬がかわいい

ソウル大学のgliese581ggさんのYOLO_tensorflowのtiny版をNumo::NArrayに直訳してみました。
cutedog
何が言いたいかというと、犬がとてもかわいい。

アルゴリズム:You only look once (YOLO)
言語:Ruby
ライブラリ:Numo::NArray, oily_png
重み:YOLO_tensorflow

※ Deep Learning はできていません

(この画像には本来著作権があるのかも知れないが確かめられなかった。しかしインターネット上では壁紙用画像として広く配布されているようだから許してほしい。)

データセット、データセット

 VGGによる画像分類は面白い。もしデータセットがあれば医用画像で人間の認識を超える精度を出すこともできる気がする。特にCTでは、あっという間に人間を超える高精度を出してくれそうでこわい。もしもCTの読影をDeep Learningでやらせようとすると、いくつかの課題があるように感じられる。第1に、個人情報保護の問題、第2に、完備されているデータセットを作ることが難しい問題、第3に、自動運転のような安全性に関する問題がある。
 このうちで最も問題になるのは、データセットを作るところだと思う。もちろんまず個人情報保護をきちんと解消しないとデータセットは作れなが、安全性に対する不安は、一旦高品質な分類器が作られ始めると、世間の反応は変わっていくのではないかと思う。今現在でも、レポート文章とレポート添付画像を使ってCNNとLSTM組み合わせて学習させれば相当なところまでやれるのかも知れない。国内でこういう事をこっそり研究してそうなところは富士フィルムさんかな。放射線科医はこの問題についてどう考えているのだろう。

 雑多な資料をながめていて思ったのは、ImageNetという団体が主催するコンテストによって画像認識がどんどん進化していったということ。そのようなコンテストが色々な領域で生まれて、やがて医療をとりまく環境も少しずつ変わっていくのでしょう。あるいは、オープンなデータセットが作られないまま謎の方向へ進化していく道も薄っすらと想像されるが、そちらの方向はあまり望ましくないと個人的には思います。

Ruby で畳み込みニューラルネットワーク(VGG-16)

RubyでVGG16による画像分類を書いてみました。

行列計算はいつも通りNumo::NArrayを使いました。
学習済みの重みとバイアスは、こちらのchainerモデル(元々はCaffe)から取り出しました。

convの部分は、Rubyのループと、NArrayの行列計算が同居しています。遅いですがメモリをあまり食いません。
まだまだ改良の余地はありそうです。しかし、Pythonを使って計算したモデルを使っていますので「Ruby で Deep Learning」はできていませんね。

人間と社会環境の相互作用としての病気

 以下、与太話が延々と続くので真面目な人はあまり真に受けないでほしいです。

 強化学習をやっていてふと考えた。病気とは何だろう。

 病気とは何か。そこにはいろいろな視点がある。例えば「正常であるものと正常でないものの境界は人間が決めるから、健康と病気も人間が決める」という考え方がある。なるほど、そう思う時もある。

 「未だかつて存在せず、これからも決して存在しないが、成立してもおかしくなかった病気。」というのがある。これは何かというと、人類には潜在的にそのような病気にかかるのに十分な構造があるけれども、その病気は頻度として極めて稀であるため、結果的に人類の誕生から滅亡まで、誰1人その病気に罹患しなかった(だからこの病気は存在しない)。というものである。この「存在しない病気」を私ちが知ることはないけれども、きっと存在する。ではこの存在しない状態は「病気」と言えるのだろうか?
 そこまで抽象的じゃなくて、ある病気と別の病気、あるいは健康と病気の境界のグレーゾーンについて考えてみる。病態をいくつに分割するかは、虹の色をいくつに分割するかと同じで恣意的なものだ。時代が新しくなるとともに、それまで一つの症候群としてまとまっていた病態が、複数の病気へと分類されていく。私は病気の分類の複雑さは、その病気が本来持つ性質ではなく、人間が今まで経験した症例数と比例しているのではないかと疑っている。人間分類器は似たような状態を一定数経験すると、それを新たに分類したがる性質を持っているからだ。けれども、あまりそういう話は聞いたことがない。やっぱり病気は病気で、最初から存在する、という考え方の方が人気がある。

 「果たしてチンパンジーは病気なのか?」という疑問を考えたことはあるだろうか。これは結構難しい問題で、チンパンジーを人間の病院で検査すれば多くの異常が発見されるだろう。健康診断なら間違いなく赤点だ。しかし、人間の標準からかけ離れているからといって、チンパンジーを「先天的な遺伝子疾患」であると見なすのは、あまりにも可哀想じゃないですか? チンパンジーが人間の病院でいくら病気の診断をもらっても、森林では元気に生活することができる。チンパンジーという状態は森林という環境によく適合しているのである。とすると環境に適合していることを「健康」と言ってよいのだろうか? それも、少し違う気がする。

 ここで、最初の話題に立ち返えろう。病気とは何だろうか。

 結論からいうと、現代社会で問題となっている「病気」は、病気が単独で存在しているのではなく、環境の変化に起因するものが少なくないのではないかと感じる。もう少し丁寧に書くと、太古の時代の環境に最適化されている人間の身体が、現在の環境に対応できていない状態のことを私たちは「病気」として認識しているのではないだろうか。(もちろん全ての病気が…というわけではなくて、現代社会でメジャーな病気が…という意味です。私達は何万世代とチューニングされているのですから、現代においてなお残っている「病気」の意味を考える必要があります。)

 例えば「糖尿病」は飢餓と低血糖に苦しんでいた人類が、飽食の時代に対応できずにいる状態とみることができるかもしれない。長い年月を慢性的な飢餓と低血糖に悩まされていた人類は、高血糖に対応して血糖値を下げるホルモンを1種類しか用意していなかった。そこで現在のような飽食の時代になって高血糖の状態が当たり前になると、簡単に不具合が発生してしまう。人類はインスリンを注射で補充するようになった。食欲も適度に抑えられ、結果的に肥満にならない人が健康的だと見なされるようになった。しかし、これは「飢餓はもう問題ではない」という社会環境が前提にあってはじめて成立する疾患ではないだろうか。

 誤解を恐れずに書くと、やはり糖尿病であることは生存に不利である。だから長い年月の後には糖尿病の人は少しずつ減少していくだろう。それは見方を変えれば、人々の飢餓に対する適応力が失われていくことにもつながるのではないだろうか。そうすると、一体どちらの状態が「健康」なのだろうか。飢饉に強い性質か? それとも飽食に強い性質か? その答えは人間だけ見ていても決まらず、社会環境によって左右される。つまり病気が独立して存在しているのではなく、社会との相互関係のなかで病気の範囲が定義されいる、ということにならないだろうか。

 心筋梗塞や脳梗塞など、動脈硬化と血栓に起因する病気には、血液をサラサラにする抗凝固療法が行われている。血液をサラサラにする薬を飲むことで血液が固まって血管が詰まることを防いでいる。ところが、血液がサラサラにして血を止まりにくいということは、怪我などで出血した際にも血が止まりにくくなるということを意味する。狩猟採集の時代には、恐らく現代とは比較にならないほど怪我が多かっただろう。そのような環境では出血が止まりにくいということは、それだけで生存に不利になると推測される。つまり「怪我による出血はもはや問題ではない」という社会環境のあって、はじめて血液ドロドロという性質が是正されるべき「不健康」な状態として立ちはだかるようになったと考えられるかも知れない。

 人は不老不死を求めてきたけれども、老化に介入する有効な手段は未だにみつからない。そういう意味では、現在行われている治療は、乱暴にまとめればすべて「延命治療」と言えなくもない。そこで私達は何をしているかと言えば、太古の時代に最適化され、新しい環境についていけない生体の性質を、薬剤を投与することで、より生存に有利な方向、あるいは勾配に傾けているのである。そうすることで生体をより現代環境に適した状態に変えて、結果として寿命を伸ばしているのである。そしてその生存に有利な方向・性質というものは、必ずしも人間の身体単体だけで決まる独立した定数ではなく、身体をとりまく社会環境によって大きく変化しうるものらしい。つまり、病気や疾患とは、私達が思っている以上に社会環境に依存する存在であり、環境と人間との相互作用の中に立ち現れてくるものだということになる。おまけに現代社会では社会保障制度が発達して、どの程度まで生体の性質を変化させられるかは、投入できる社会的な医療資源の量にも大きく左右されるようになった。

だとすると病気を治療するとは本当はどういうことなのだろう?

 強化学習を眺めていて、ふと、そんなことを考えた。なんだかもう予想以上に風呂敷を広げすぎたようで、頭が痛くなってきたのでこのへんで。

落書き Ruby/Tkでリアルタイムプロット

使うもの
・Ruby/Tk + Plotchart
・dRuby

Ruby/Tk はThreadとの相性が非常に悪く、非同期処理やメインループを2つ回すような作業をやろうとすると上手くいかないことが多いです。
そこで真ん中にdRubyサーバーを挟みます。これで大抵の処理はRuby/Tkでリアルタイムに表示することができます。

drb.rb

env.rb

plot.rb

あとはdrb.rb、env.rb、plot.rbの順番に実行します。
終了するときはゾンビプロセスを残さないように気をつけます。

学習中や、順伝播中のニューラルネットワークの重みを表示するみたいなよくあるネタをやってみようかしらん。でもそれだけじゃあまりにも面白くないからもうひと工夫したいところ。。

雑記 人工知能やプログラミングを勉強する素人について考える

 この日進月歩、一日千秋、不老長寿の時代、IT業界と直接縁がなくても「人工知能ってどんなものだろう?」と興味をもって、何となく暇な時間を使ってプログラミングを勉強したり、ニューラルネットワークについて調べたりしている人は決して少なくない。

 そういう人の一人として、別に使いこなせるわけでもなく、使いこなす必要すらないChainerの資料とかをボーッと眺めて週末を過ごしているわけだが、「ソフトウェア開発が本職ではない学生や研究者が多く参加している」というフレーズが頭に飛び込んできた。

 誤解を恐れずに言えば、Python や NumPy が普及したのは、プログラミングの事は考えずに計算や論理に集中すればよくて、雑多なプログラミング技法に興味がない研究者でも使いこなすことができたという点に強みがあったのではないかと思う。神様のような人はたくさんいるけれども、多くの分野の物事が何でもできる人は未だに見たことがない。徒競走が得意だからといって、上手に目玉焼きを作れるとは限らない。そこから類推すると、おそらく仕事プログラミングができる人は、機械学習のアルゴリズムに疎く、機械学習のアルゴリズムに詳しい人は仕事プログラミングの流儀には疎いのだろう。無知を無知のままに温存するという強い需要に応えるためにどの言語でも「ライブラリ」が発達しているのを見ても、人間は一つ以上のことを器用にこなしたりできないことがわかる。

 ここで一つの疑問が浮かぶ。仕事で、プログラミングや人工知能を全く使わない人が、それらを勉強することに何かメリットがあるのか?という問題だ。

 結論から言うと、現時点では、よほど特殊な職業をのぞいて何らメリットはないと思う。メリットはないけど、デメリットはある。頭のメモリーや暇な時間を人工知能やプログラミングなどの生きていくのに不必要な知識の習得に費やすため、何であれ本業に対する悪影響が懸念される。プログラミングを勉強すればするほど、本業の勉強がおろそかになり、本業における競争力を喪失する危険性が出てくる。
 しかし悪影響はそれにどどまらない。学校や職場では、人工知能とは頭がいい人がやる事で、普通の人には無関係だという誤解がある。そのため、おばかさん(と認識されている人間)が人工知能の話を少しでもすると不信の目を向けられることがある。オタクへの偏見もまだまだ根強い。ターミナルを起動しただけで迫害されたという話もよく聞く。100歩譲って「機械学習」という概念について理解してもらったとしても「おぉん? つまりYouは機械を手なづけて、俺達の仕事を奪おうとしているのかね?!?」などの余計な誤解を招き、白眼視されるかも知れない。(人間の介在しない状態を作り出すことで効率化を進めるという発想は興味深い問題を含むがそれは別の話)もしくは哀れみの目線で「悪いことは言わないから、君はもっと時間と能力を有効に使った方がいい。」と優しく諭されるかもしれない。もちろん彼は100%心から心配してくれているのである。どこの空間でも強烈な競争原理が働くため、いずれ人工知能やプログラミングを学ぶことをやめてしまう。もしも、常に賢くて合理的な選択をしなければならないのなら。

 ところで話は切り替わって、仕事で機械学習をしなければならないとしたら、おそらく、RubyやNArrayは選ばれない。Chainerすら選ぶかどうか微妙で、こそこそとRuby本を封印し、TensorFlow や Scikit-learn を勉強し始める自分の姿が目にうかぶ。その理由は簡単で、ユーザーが多くて、その結果として先行事例と動作確認が十分にあり、なおかつ知名度があって世界的にスタンダードになりそうで、顧客や上司や同僚から評価されそう、そして何より最後まで行けそうな道具を選びたい。そんな保守的な思考回路がピュンピュン回るからだ。妄想のなかでもそうなるのだから、実際にエンジニアをやってる人の中では確実にそうなるのに違いない。とすると、永久に日曜プログラマがRubyを使って機械学習で遊べる日は来ないことになる。そんな未来では私が困る。

 ところが、趣味でプログラミングをしているだけなら話は変わってくる。numpyで機械学習なんて世界中の人がやってるけれど、これをRubyに書きなおすだけで何か新しいことをやった気がしてくる。一日一善。早寝早起。七転び八起き。まあ、気がしてくるだけなのだが、焼肉定食、強化学習における「報酬」が簡単に手に入る。何かにコミットした気持ちになる。これ重要。ここでノンエンジニアであることがアウトプットに有利に働いてくる。プロのエンジニアやIT関係者は職業柄正しいことをしなければならないという防衛的なインセンティブがどうしてもある。しかし、ノンエンジニア素人なら失うものがない。間違いだらけのポンコツあっても、特に気にもせず、むしろポンコツを笑いの種に本職の人が来てくれたら儲けもの。という気持ちで適当に遊んでればいい。

 人工知能について考えるとき、人工知能に関わる人間が気になる。単純なニューラルネットワークCNNと異なり、強化学習DQNのインパクトをどう社会的に位置づけるかが難しいように、人工知能の未来については、人工知能そのものの評価よりも、人工知能をとりまくノンエンジニアの人間様の「報酬」や「コスト」について観察して、可能ならばその状況そのものに介入するのがよいと思うけど、これ、自分には手に余る難しさのようだ。