4/14(日) 足・靴・木型研究会「第2回研究集会」を開催します☆彡

1-2. 世界最短(*要出典)の深層学習コード

やること

物事を理解するには、やっぱり最小構成がいちばんです。しかし、ネットで深層学習のサンプルコードを検索しても、「MNISTの画像分類」といった、そこそこ難しい入門教材しか見つかりません。コピペで模倣はできても、その先に進むことは難しいです。今日は、世界最短の深層学習コードとして、XORゲートの学習を行います。

環境とコード

WinPython3.6をおすすめしています。

WinPython - Browse /WinPython_3.6/3.6.7.0 at SourceForge.net
Portable Scientific Python 2/3 32/64bit Distribution for Windows
AIワークショップ | 初心者だけどAIできちゃった(昼編) (2018/08/28 14:00〜)
# 同日の「夜編」と同じ内容です。夜編は混雑が予想されますので、可能な方はこちらにご参加くださいますと幸いです。 ## 概要 ノートPCを持参して、深層学習でケーキを10種類に分類しましょう。 「プログラム環境のインストール」や「教師データの集め方」からやりますので、初心者におすすめです。 ## コンセプト ...

XORゲート(排他的論理和)とは

XORゲート(排他的論理和)は、2つの入力を受け取って1つの出力を出す変換器で、次の変換ルールを持ちます。

  • (0, 0) → 0
  • (0, 1) → 1
  • (1, 0) → 1
  • (1, 1) → 0

どうでしょうか、2つの入力値を単純に足したり掛けたりするだけでは実現できない感じがします。実はXORゲートは「深層」な学習でないと表現できないことが分かっています。

(追記)高校生から、「左ひく右の絶対値」でできますよというコメントをいただきました。そのとおりです笑

なぜXORゲートは「深層」でないと表現できないのか

入力の一方をx軸に、もう一方をy軸にとって、座標上に出力値を置いてみました。単純パーセプトロン(深層学習の深層でないやつ)は、1本の線を引くようなイメージで0と1を分けることができます。ANDゲートやORゲートについては、4種の出力を1本の線で分けられますね。しかしXORゲートは、1本の直線ではうまく分けることができません。そこで、単純パーセプトロンを層状に重ねて「深層」にすることで、いわば座標がぐねっと曲がり、最終的に1本の直線で0と1を分けることができるようになるのです。

ニューラルネットモデル

XORゲートですので、入力が2ユニット、出力が1ユニットです。その間に、中間層として8ユニットを全結合で置きました。

コード大公開

全然短くないように見えますが、説明を除くと実質12行です。

# -*- coding: utf-8 -*-
"""
え!?実質たったの12行で深層学習を!?
2018/11/06 @ Yasuda
"""
#===================
#ライブラリのインポート
#===================
import numpy as np 
from keras.models import Sequential
from keras.layers.core import Dense


#===================
#教師データの作成
#===================
#今回はXORゲート(排他的論理和)を学習させます
x = np.array([[0,0],[0,1],[1,0],[1,1]])#入力セット(4種類しかないけど)
y = np.array([[0],[1],[1],[0]])#正解セット、順番は入力に対応させること


#===================
#ニューラルネットモデルの作成
#===================
#中間層:「Dense()」でノード数を指定する。何層でも追加できるよ
#活性化関数:とりあえず「tanh」でOK。最終層は場合によるが、今回は「sigmoid」(0か1に寄る)が最適
model = Sequential()
model.add(Dense(8, input_dim=2, activation='tanh'))#入力セットを受け取る層は「input_dim」を必ず指定
model.add(Dense(1, activation='sigmoid'))#最後は出力層。出力の要素数に合わせること


#===================
#モデルのコンパイル
#===================
#損失関数(目的関数):出力が1個で0または1のときは「binary_crossentropy」を使う
#最適化関数:とりあえず「Adam」でOK
model.compile(loss='binary_crossentropy', optimizer='Adam')


#===================
#学習開始
#===================
#エポック数:「nb_epoch」は教師データ(=ドリル)を何周学習させるか。繰り返し学習させよう
model.fit(x, y, nb_epoch=1000)


#===================
#学習できたか確認
#===================
#「model.predict()」に「入力のセット」を入れると「答えのセット」が返る
ans = model.predict(x)#学習済みのモデルにもう一度教師データを入力して答えを出す
print(ans)#答えの表示

コードの実行

配布されたプログラムをcodeフォルダに入れ、Spyderの画面にドラッグ&ドロップすると開きます。

実行ボタン(またはF5キー)を押すとプログラムが実行されます。右下に実行結果が表示されます。

結果

実行すると、いかにも学習が進んでいる、といった結果が表示されます。最後に、学習に使用した入力をもう一度入力して、どれくらい学習できたかを確認しています。なんとなく0,1,1,0に近いですね。

重みとバイアスを取り出して手計算してみる

おまけですが、中間層を3ユニットに減らしてしっかりと学習させ、学習後の重みとバイアスを取り出してみました。紙とペンを用意して、入力層に(0, 0), (0, 1), (1, 0), (1, 1)の4種類を当てはめて必死に掛け算と足し算をしてみると、たしかに出力が0, 1, 1, 0に近い値になりました。さっきは足したり掛けたりしてもどうにもなりませんでしたが、このように計算すれば、XORゲートができるんですね。

タイトルとURLをコピーしました