Lenetを試してみたよ【Keras】
畳み込みニューラルネットワークの元祖として「Lenet」があります。1995年に発表された論文ですね。Kerasで実際に動かしてみた例がありました。
ソースコード
import os import keras from keras.models import Sequential from keras.layers.convolutional import Conv2D from keras.layers.convolutional import MaxPooling2D from keras.layers.core import Activation from keras.layers.core import Flatten from keras.layers.core import Dense from keras.datasets import mnist from keras.optimizers import Adam from keras.callbacks import TensorBoard from pathlib import Path __file__ = "C:\\Users\\admin\\log" print(__file__) def lenet(input_shape, num_classes): model = Sequential() # extract image features by convolution and max pooling layers model.add(Conv2D( 20, kernel_size=5, padding="same", input_shape=input_shape, activation="relu" )) model.add(MaxPooling2D(pool_size=(2, 2))) model.add(Conv2D(50, kernel_size=5, padding="same", activation="relu")) model.add(MaxPooling2D(pool_size=(2, 2))) # classify the class by fully-connected layers model.add(Flatten()) model.add(Dense(500, activation="relu")) model.add(Dense(num_classes)) model.add(Activation("softmax")) return model class MNISTDataset(): def __init__(self): self.image_shape = (28, 28, 1) # image is 28x28x1 (grayscale) self.num_classes = 10 def get_batch(self): (x_train, y_train), (x_test, y_test) = mnist.load_data() x_train, x_test = [self.preprocess(d) for d in [x_train, x_test]] y_train, y_test = [self.preprocess(d, label_data=True) for d in [y_train, y_test]] return x_train, y_train, x_test, y_test def preprocess(self, data, label_data=False): if label_data: # convert class vectors to binary class matrices data = keras.utils.to_categorical(data, self.num_classes) else: data = data.astype("float32") data /= 255 # convert the value to 0~1 scale shape = (data.shape[0],) + self.image_shape # add dataset length data = data.reshape(shape) return data class Trainer(): def __init__(self, model, loss, optimizer): self._target = model self._target.compile( loss=loss, optimizer=optimizer, metrics=["accuracy"] ) self.verbose = 1 logdir = "logdir_lenet" self.log_dir = os.path.join(os.path.dirname(__file__), logdir) def train(self, x_train, y_train, batch_size, epochs, validation_split): if os.path.exists(self.log_dir): import shutil shutil.rmtree(self.log_dir) # remove previous execution os.mkdir(self.log_dir) self._target.fit( x_train, y_train, batch_size=batch_size, epochs=epochs, validation_split=validation_split, callbacks=[TensorBoard(log_dir=self.log_dir)], verbose=self.verbose ) dataset = MNISTDataset() # make model model = lenet(dataset.image_shape, dataset.num_classes) # train the model x_train, y_train, x_test, y_test = dataset.get_batch() trainer = Trainer(model, loss="categorical_crossentropy", optimizer=Adam()) trainer.train( x_train, y_train, batch_size=128, epochs=12, validation_split=0.2 ) # show result score = model.evaluate(x_test, y_test, verbose=0) print("Test loss:", score[0]) print("Test accuracy:", score[1])
まとめ
__file__ = “C:\\Users\\admin\\log" という箇所がありますが、ここは適宜変更してください。割と重要みたいで、これが無いと正常に動作しませんでした。Linux環境だとうまくいくのかもしれません。
Train on 48000 samples, validate on 12000 samples Epoch 1/12 48000/48000 [==============================] - 74s 2ms/step - loss: 0.1838 - acc: 0.9443 - val_loss: 0.0583 - val_acc: 0.9827 Epoch 2/12 48000/48000 [==============================] - 74s 2ms/step - loss: 0.0493 - acc: 0.9848 - val_loss: 0.0472 - val_acc: 0.9839 Epoch 3/12 48000/48000 [==============================] - 74s 2ms/step - loss: 0.0327 - acc: 0.9895 - val_loss: 0.0482 - val_acc: 0.9846 Epoch 4/12 48000/48000 [==============================] - 74s 2ms/step - loss: 0.0236 - acc: 0.9925 - val_loss: 0.0357 - val_acc: 0.9889 Epoch 5/12 48000/48000 [==============================] - 74s 2ms/step - loss: 0.0187 - acc: 0.9939 - val_loss: 0.0415 - val_acc: 0.9866 Epoch 6/12 48000/48000 [==============================] - 74s 2ms/step - loss: 0.0143 - acc: 0.9955 - val_loss: 0.0365 - val_acc: 0.9901 Epoch 7/12 48000/48000 [==============================] - 74s 2ms/step - loss: 0.0119 - acc: 0.9961 - val_loss: 0.0410 - val_acc: 0.9882 Epoch 8/12 48000/48000 [==============================] - 74s 2ms/step - loss: 0.0088 - acc: 0.9970 - val_loss: 0.0301 - val_acc: 0.9913 Epoch 9/12 48000/48000 [==============================] - 74s 2ms/step - loss: 0.0066 - acc: 0.9978 - val_loss: 0.0452 - val_acc: 0.9898 Epoch 10/12 48000/48000 [==============================] - 74s 2ms/step - loss: 0.0084 - acc: 0.9969 - val_loss: 0.0452 - val_acc: 0.9899 Epoch 11/12 48000/48000 [==============================] - 74s 2ms/step - loss: 0.0067 - acc: 0.9977 - val_loss: 0.0353 - val_acc: 0.9912 Epoch 12/12 48000/48000 [==============================] - 74s 2ms/step - loss: 0.0052 - acc: 0.9984 - val_loss: 0.0465 - val_acc: 0.9895 Test loss: 0.04366540142124295 Test accuracy: 0.9888
実行結果はこんな感じです。
脅威の「98.88%」の正答率を誇っていますね!
学習時間が大変なことになっています。なんと1ステップに74秒も掛かっています。2018年のコンピュータでコレなのですから、1995年の技術水準でLenetが発表されたのは奇跡的ですね。その時点で現在に近い水準のコンピュータリソースにアクセスできた人が居たというのはすごいことです。現在でも20年後のコンピュータ環境に近いものがあるのでしょうが、人工知能の研究には資金力も必要になりそうですね。