ここまでSequential()
でDLモデルを構築してきましたが、この方法では学習スピードが辛くなってきましたし、軽量高速なモデルも作ってみたいと感じます。ですが、Sequential()
ではモデルの複雑さと学習スピードの遅さがほぼイコール関係なので、扱うデータが大きく多量になるにつれて試行錯誤も時間がかかるようになります。そこで気になってくるのが主流となっているModel()
クラスを使ったモデル構築です。
これまでSequential()
でもそこそこいろいろなことを試してきましたし、そろそろ太刀打ちできるかな?ということでModel()
クラスを使ったDLモデル構築に挑戦してみようと思います。
そもそもModel()クラスって何?
Kerasには機械学習モデル構築の方法として、2種類のクラスが用意されています。今まで使ってきたSequential()
クラスと、より高度なモデル構築が可能なModel()
クラス(functional API)です。
Sequential()
クラスでは、ノード同士の入出力は上から順番に勝手にやってくれていました。ところが、Model()
クラスでは、ノードがどのノードからデータを受け取るのか等を自分で決めることができます。
例えば、全結合層一層のMNIST学習用モデルをそれぞれの記法で書くと以下のようになります。
Sequential()クラスの場合
from keras.layers.core import Dense, Activation
from keras.models import Sequential
model = Sequential()
model.add(Dense(512, input_dim=(784)))
model.add(Activation("relu"))
model.add(Dense(10))
model.add(Activation("softmax"))
Model()クラスの場合
from keras.layers import Input, Dense
from keras.models import Model
inputs = Input(shape=(784,))
dense1 = Dense(512, activation='relu')(inputs)
predictions = Dense(10, activation='softmax')(dense1)
model = Model(inputs=inputs, outputs=predictions)
一番の違いは各レイヤーの最後に入力がどのレイヤーかを指定している部分。これによってひとつの出力を複数のノードに渡す枝分かれするモデルも構築可能だし、Model()
は複数の入力、出力を指定することも可能なので、例えばR、G、Bのチャンネルを別々に学習させて最後に合流させる、みたいなモデルを作ることもできるようになります。
実際にMNISTを学習させてみる
ここまでだとちょっとピンとこないところもあるので、CNN版MNISTモデルをModel()
で作ってみます。
コーディング
見やすいように小分けにしますが、一連で書いていただいてだいじょうぶです。
前処理
import numpy as np
from keras.datasets import mnist
from keras.utils import np_utils
#MNISTデータセット呼び出し
(X_train, y_train), (X_test, y_test) = mnist.load_data()
#データの正規化
X_train = np.array(X_train)/255.
X_test = np.array(X_test)/255.
#CNNに入れるために次元を追加
X_train = np.expand_dims(X_train, axis=-1)
X_test = np.expand_dims(X_test, axis=-1)
#ラベルのバイナリ化
y_train = np_utils.to_categorical(y_train)
y_test = np_utils.to_categorical(y_test)
以前はモデルの中にReshape()
レイヤーでチャンネル追加をしていましたがこの方法はMNIST以外だと何故かうまくいかなかったので、今回は前処理段階で次元を追加する方法をとってみました。
モデル構築
from keras.layers import Input, Dense, Conv2D, MaxPooling2D
from keras.models import Model
from keras.layers.core import Flatten
inputs = Input(shape=(28,28,1)) #入力層
conv1 = Conv2D(16, (3, 3), padding='same', activation='relu')(inputs) #畳込み層1
pool1 = MaxPooling2D((2, 2))(conv1)
conv2 = Conv2D(32, (3, 3), padding='same', activation='relu')(pool1) #畳込み層2
pool2 = MaxPooling2D((2, 2))(conv2)
flat = Flatten()(pool2) #全結合層に渡すため配列を一次元化
dense1 = Dense(784, activation='relu')(flat) #全結合層
predictions = Dense(10, activation='softmax')(dense1) #出力層
model = Model(inputs=inputs, outputs=predictions) #モデルの宣言(入力と出力を指定)
コンパイル・実行
ここはいつもどおり。
model.compile(optimizer="sgd", loss="categorical_crossentropy", metrics=["accuracy"])
hist = model.fit(X_train, y_train, batch_size=16, verbose=1, epochs=30, validation_split=0.3)
評価
score = model.evaluate(X_test, y_test, verbose=1)
print("正解率(acc):", score[1])
ぼくの環境では正解率(acc):0.9894となりました。正常に学習できているようです。
まとめ
記述のルールがちょっと変わり、可読性が少し下がりますがそこまでとっつきにくい感じではなさそうです。これまでSequential()
でいろいろ試してきた効果もあったと思います。
次回以降で、もう少し高度なモデル構築も試してみたいです。