studylog/北の雲

chainer/python/nlp

ディープラーニング入門のための入門 その1

このシリーズは何なのか

  • ディープラーニングとやらに興味あるけど何から手を付けていいかわからない
  • そもそも機械学習って何なのかわからない
  • 数式読めない 微分積分わからない
  • 高校卒業してから数学を学んだ事が無い
  • chainerのサンプルを試してみたけれど中身のコードはさっぱりわからない
  • 英語わからないのでchainerのドキュメント読めない

という人向けのchainerを使ったディープラーニング入門するための入門といった位置づけです。

書いている人も似たようなレベルです。微分積分やデルタ記号を見ると頭痛がします。ディープラーニングの数式を一行も理解できません。今英検2級受けたら落ちそうな英語力。アカデミックな情報工学どころか大学は文系でしたしプログラマですらありません。そんな人が書いています。

Mnist(手書き数字を識別する)サンプルを読んで動作が理解できる人や、大学で理系に進んだ人、情報工学を学んだ人には退屈な記事だと思うのでスルーしてください。

最低限pythonが読み書きできること、そしてchainer(記事を書いた時点での1.2系以前のバージョン)がインストールされている環境があることが大前提ですが、この「その1」に関して言えば実際にコードを動かさなくても機械学習が中で何をやっているのかなんとなく雰囲気だけでも掴めるようになると思います。

このシリーズを読破すると何ができるようになるか

  • ブラックボックスだった機械学習が中で何をやってるのか何となくわかるようになるかもしれない
  • 誰かが書いたサンプルを利用するだけでなく、サンプルの中身がなんとなくわかるようになるかもしれない
  • 自分で0からchainerのコードを書けるようになるかもしれない


その1では、まず機械学習とはいったい何なのか、というものをつかんでもらう為のチュートリアル的なものをこなします。


最低限必要な知識「ベクトル」

さて、その前に。
機械学習全般に必要な最低限の知識として「ベクトル」があります。ベクトルが何なのかわからないと一歩も先に進めないと思うので簡単に説明します。

高校の時に習ったベクトルはこんな感じのイメージでした。

f:id:kitanokumo:20150827202945p:plain

でも今から使うベクトルはプログラムでよく使う配列・Arrayみたいなものだと思ってください。

[1,2,3,4,5]

これは数字が5つあるので5次元のベクトルといいます。

世の中の色んな物や事象をベクトルで表現することができます。
例えば一人の人間をベクトル化します。
見た目をベクトル化するなら [ 身長 , 体重 , 年齢 ] のようなベクトルにすることができます。

Aさんは [173 , 60 , 30] (身長170cm , 体重60kg , 30歳)
Bさんは [154 , 46 , 20] (女性っぽい)
Cさんは [140 , 30 , 10] (子供かな?)

と表現できます。
こうやって何でもベクトル化することで、コンピューターがその対象物を理解することが可能になります。ディープラーニングに代表される機械学習の第一歩は、現実世界の物や事象をコンピューターが読めるようにベクトル化する、と言ってもいいと思います。文章・画像・動画などもベクトル化できます。何でもベクトル化は可能です。

ベクトル化の練習

A.あなたの身長体重年齢をベクトル化してください
B.あなたの直近一年の体重の変化を一ヶ月単位でベクトル化してください(推測で構いません)
C.ここ一週間の天気をベクトル化してください

答え

Aは割愛
Bは [65 , 63 , 61 , 62 , 64 , 64 , 65 , 65 , 65 , 67 , 68 , 68]
ダイエット頑張ったけれど途中でリバウンドが始まって逆に体重が増えている様子がわかります

さてCはどうすればいいでしょうか。
例えば実際の天気が晴晴曇雨雨曇晴だったとして、これをどうベクトル化すればいいでしょうか。

[晴 , 晴 , 曇 , 雨 , 雨 , 曇 , 晴 ]

これでは数値じゃないからダメです。ベクトルは数値で表現したい。
なので晴=0 曇り=1 雨=2と適当な数値を振ることでベクトル化できます

[0 , 0 , 1 , 2 , 2 , 1 , 0]

あるいは実際の降水量を元にベクトル化する手もあります

[0 , 0 , 0.1 , 1.3 , 2.5 , 0.2 , 0]

上のベクトルに比べると、同じ雨でも1.3の日よりも2.5の日の方が激しい雨が降った事が表現できています。

つまりCのような問題については明確な答えは存在しません。自分のしたい処理に応じてベクトル化の方法を考えていく必要があります。

ちなみにベクトルに使える数値は整数・少数・マイナスと何でも構いません。

以上でベクトルに関する簡単な説明を終わります。


モデル

さて、いよいよ一歩先に進んでみましょう。ここから機械学習における重要な「モデル」という概念が登場します。

モデルとはいったい何なのか。
ベクトルを与えると、それを中でごにょごにょと加工して新しいベクトルを作る機械だと(暫定的に)思ってください。

例えばこれは[1,2,3]というベクトルを与えると、中の数字をそれぞれ2倍にして返すモデルです。
f:id:kitanokumo:20150827211348p:plain

まずはこのモデルを自力で作れるようになること。
これが千里先の彼方にあるディープラーニングへの第一歩です。

今日の目標:ベクトルの中の数字をそれぞれ2倍にして返すモデルを作る

モデルに学習させる

chainerがインストールされた環境で以下のpythonコードを実行してみてください。(python2でも3でも動くはず)

#!/usr/bin/env python
# coding:utf-8
import numpy as np
import chainer.functions as F
from chainer import Variable, FunctionSet, optimizers

#モデルを作る
model = F.Linear(3,3)

#これがモデルに与えるベクトル
data = np.array([[1,2,3]] , dtype=np.float32)

x = Variable(np.array(data))

#ここでモデルにベクトルを投げて、加工させている
y = model(x)

#モデルが加工したベクトルを表示
print(y.data) 

さて、何が表示されました?私の環境ではこうなりました。

[[ 1.89820516  1.43878937  1.29263091]]

人によって表示されたベクトルの値は様々ですので私のと違っても気にしないでください。

さて、この記事の目標は「ベクトルの中の数字をそれぞれ2倍にして返すモデル」を作る事でした。つまり、ここで表示されるベクトルが[2,4,6]になるようにモデルを作りたいのです。しかし今しがた表示されたベクトルは[2,4,6]とはかけ離れていますよね?このお馬鹿なモデルを調教することで賢くしてやりましょう。

どうやってモデルを賢くするのか

手順としては以下の1~3を何度も何度も繰り返します。

  • 1. [1,2,3]をモデルに与えて正解を予想させる
  • 2. モデルが予想したベクトルと、正解ベクトル([2,4,6])との「差」を計算して、「お前が今返してきたベクトルは正解とこれだけかけ離れている!」と教える
  • 3. モデルが少し賢くなる

これをひたすら繰り返す事で、徐々にモデルが「ああこいつは中のベクトルを二倍したのが欲しいんだな」と学習するようになり、[2.4.6]に近いベクトルを返すようになります。

イメージはこんな感じです。
モデル君にベクトルを与える
f:id:kitanokumo:20150827215123j:plain
まだお馬鹿なモデル君はいい加減なベクトルを返して来る
f:id:kitanokumo:20150827215127j:plain
殴る
f:id:kitanokumo:20150827215129j:plain

これを繰り返します。

そうするとモデル君も賢くなって彼女の想いがだんだんとわかってきます。
f:id:kitanokumo:20150827230229j:plain

…何となくイメージは掴めましたでしょうか?


それでは実際にやってみましょう。50回学習させるコードです。

#!/usr/bin/env python
# coding:utf-8
import numpy as np
import chainer.functions as F
from chainer import Variable, FunctionSet, optimizers

#モデル定義
model = F.Linear(3,3)
optimizer = optimizers.SGD()
optimizer.setup(model)


#学習させる回数
times = 50


#与えるベクトル  これが[2,4,6]になって返ってきて欲しい
x = Variable(np.array([[1, 2, 3]], dtype=np.float32))

#正解ベクトルの[2,4,6]
t = Variable(np.array([[2, 4, 6]], dtype=np.float32))

#ここから50回ループ
for i in range(0,times):
    optimizer.zero_grads()

    #ここでモデルに予測させている
    y = model(x)

    #モデルが出した答えを表示
    print(y.data)

    #お馬鹿なモデルが出した答えと、本当の答え([2,4,6])がどのくらい違っているか計算する
    loss = F.mean_squared_error(y, t)

    #その値をモデルに見せて「全然違うじゃねーか!もっと近づけろ!」と学習させる
    loss.backward()
    optimizer.update()
    
    #最初に戻って繰り返す

これを私の環境で実行するとこうなりました。

[[ 0.14060879  0.60941315  1.49593627]]
[[ 0.84717733  1.89783609  3.20748067]]
[[ 1.28525007  2.69665813  4.26863813]]
[[ 1.55685508  3.19192839  4.92655516]]
[[ 1.72525036  3.4989953   5.33446455]]
[[ 1.82965517  3.68937707  5.58736801]]
[[ 1.89438593  3.80741429  5.74416828]]
[[ 1.93451917  3.88059664  5.84138393]]
[[ 1.95940173  3.9259696   5.90165806]]
[[ 1.9748292   3.95410109  5.93902826]]
[[ 1.98439395  3.97154307  5.9621973 ]]
[[ 1.9903245   3.98235655  5.97656202]]
[[ 1.99400055  3.98906112  5.98546886]]
[[ 1.99628079  3.99321771  5.99099064]]
[[ 1.99769413  3.99579525  5.99441433]]
[[ 1.99856997  3.99739289  5.99653673]]
[[ 1.99911356  3.99838376  5.9978528 ]]
[[ 1.99945033  3.99899769  5.99866819]]
[[ 1.9996593   3.99937868  5.99917507]]
[[ 1.99978912  3.99961495  5.99948788]]
[[ 1.99986923  3.9997611   5.99968243]]
[[ 1.99991894  3.99985194  5.99980354]]
[[ 1.99995005  3.99990797  5.99987841]]
[[ 1.99996889  3.99994326  5.99992466]]
[[ 1.99998081  3.99996471  5.99995327]]
[[ 1.99998832  3.99997807  5.99997091]]
[[ 1.99999273  3.99998641  5.99998188]]
[[ 1.99999559  3.99999166  5.99998903]]
[[ 1.99999726  3.99999475  5.99999332]]
[[ 1.99999809  3.99999714  5.99999523]]
[[ 1.99999881  3.99999809  5.99999762]]
[[ 1.99999952  3.99999857  5.99999809]]
[[ 1.99999952  3.99999928  5.99999905]]
[[ 1.99999952  3.99999952  5.99999952]]
[[ 1.99999952  3.99999976  5.99999952]]
[[ 1.99999952  4.          6.        ]]
[[ 1.99999952  4.          6.        ]]
[[ 1.99999952  4.          6.        ]]
[[ 1.99999952  4.          6.        ]]
[[ 1.99999952  4.          6.        ]]
[[ 1.99999952  4.          6.        ]]
[[ 1.99999952  4.          6.        ]]
[[ 1.99999952  4.          6.        ]]
[[ 1.99999952  4.          6.        ]]
[[ 1.99999952  4.          6.        ]]
[[ 1.99999952  4.          6.        ]]
[[ 1.99999952  4.          6.        ]]
[[ 1.99999952  4.          6.        ]]
[[ 1.99999952  4.          6.        ]]
[[ 1.99999952  4.          6.        ]]

最初は 0.14060879 0.60941315 1.49593627という正解とはかけ離れたベクトルを返していますが、間違って殴られる度に徐々に正解の[2,4,6]に近づいているのがわかるでしょうか。
40回目当たりからはほぼ正解を返してきて変化しなくなりました。学習が完了したのです。

これが機械学習と呼ばれる手法の基礎中の基礎です。
1.モデルにデータを与えて
2.予測させて
3.正解との差をモデルに教えて学習
4.1に戻って繰り返し

ディープラーニングの元となっているニューラルネットワークでも、ちょっと前まで機械学習の主流だったSVM(サポートベクターマシン)でもこの原理は変わりません。ちなみにこれを「教師あり学習」と呼びます。正解データを逐一モデルに与えるので、教師あり、なんですね。その逆の教師無し学習というものもあります。

おわり

ディープラーニングというよりは、機械学習の基礎中の基礎を、かなりざっくばらんに解説してみました。厳密な数学・情報工学を学んだ人から見れば突っ込みどころが満載かもしれませんが、大目に見てください。

モデルにデータを与えてまず予測させて、その後正解を教えて学習させる。この繰り返しで色んなことを自動的にこなせるモデルを作る事ができます。

例えば空の写真を見せて、「晴れ・くもり・雨・雪」のいずれかの天気を答えられるモデルを作りたいとします。まず写真をベクトル化します(どうやるかはいつか解説します)。そのベクトルをモデルに与えて晴れ・くもり・雨・雪の4つから予測させて、その後、晴れ・雨という正解を与えます。この繰り返しで天気を正確に答えられるモデルを作る事ができます。

いかがだったでしょうか。
まだまだディープラーニングとはほど遠い内容でしたが、ほんの少しだけ機械学習がいったい何者なのかわかっていただけたのではないでしょうか。

あまりマメな人間ではないので、その2がいつになるやら…。