Keras2.2.0でchianerぽく書けるようになったみたい：model-subclassing機能


https://github.com/keras-team/keras/releases/tag/2.2.0
他にも変更点ありますが、自分の興味あることだけで申し訳ないです。

今回もcolabで動かして見ました。ちなみに最近colabはgpu確保できないことも良くあるのでお気をつけください。
多分リソースが余ってない時は確保できないです。
https://colab.research.google.com/


```
!pip list | grep Keras
```

>Keras                    2.1.6      


keras最新にします。

```
!pip install -U Keras
```



>Collecting Keras
  Downloading 
...
    Uninstalling Keras-2.1.6:
      Successfully uninstalled Keras-2.1.6
Successfully installed Keras-2.2.0 keras-applications-1.0.2 keras-preprocessing-1.0.1



これまでの書き方も復習します。
必要ない方はその３まで飛ばしてください。

cifar10データを使っています。


# 書き方その１：Sequential ModelにAddで繋ぐ

シーケンシャルの場合　入力１に対して出力1のモデルを想定します。
途中で分岐したり、スキップしたりする googlenet系のinceptionやresnet系に向かないです。
その場合は違う書き方を使います。

```py:
import os
import numpy as np
import warnings
import keras
from keras.datasets import cifar10

from keras.models import Sequential
from keras.models import Model
from keras.layers import Flatten
from keras.layers import Dense
from keras.layers import Input
from keras.layers import Conv2D
from keras.layers import MaxPooling2D
from keras.layers import GlobalMaxPooling2D
from keras.layers import GlobalAveragePooling2D
from keras.preprocessing import image
from keras.utils import layer_utils
from keras.utils.data_utils import get_file
from keras import backend as K
from keras.applications.imagenet_utils import decode_predictions
from keras.applications.imagenet_utils import preprocess_input
from keras.applications.imagenet_utils import _obtain_input_shape
from keras.engine.topology import get_source_inputs

batch_size = 30
num_classes = 10
epochs = 1
num_predictions = 20
save_dir = os.path.join(os.getcwd(), 'saved_models')
model_name = 'keras_cifar10_trained_model.h5'

(x_train, y_train), (x_test, y_test) = cifar10.load_data()
print('x_train shape:', x_train.shape)
print(x_train.shape[1:])
print(x_train.shape[0], 'train samples')
print(x_test.shape[0], 'test samples')

# Convert class vectors to binary class matrices.
y_train = keras.utils.to_categorical(y_train, num_classes)
y_test = keras.utils.to_categorical(y_test, num_classes)

#精度のためにはしない方がいいが、時短のためにデータ減らす
x_train = x_train[:200]
y_train = y_train[:200]
x_test = x_test[:200]
y_test = y_test[:200]


model = Sequential()
# Block 1
model.add(Conv2D(64, (3, 3), activation='relu', padding='same', name='block1_conv1', input_shape=x_train.shape[1:]))
model.add(Conv2D(64, (3, 3), activation='relu', padding='same', name='block1_conv2'))
model.add(MaxPooling2D((2, 2), strides=(2, 2), name='block1_pool'))
# Block 2
model.add(Conv2D(128, (3, 3), activation='relu', padding='same', name='block2_conv1'))
model.add(Conv2D(128, (3, 3), activation='relu', padding='same', name='block2_conv2'))
model.add(MaxPooling2D((2, 2), strides=(2, 2), name='block2_pool'))
# Block 3
model.add(Conv2D(256, (3, 3), activation='relu', padding='same', name='block3_conv1'))
model.add(Conv2D(256, (3, 3), activation='relu', padding='same', name='block3_conv2'))
model.add(MaxPooling2D((2, 2), strides=(2, 2), name='block3_pool'))
# Block 4
model.add(Conv2D(512, (3, 3), activation='relu', padding='same', name='block4_conv1'))
model.add(Conv2D(512, (3, 3), activation='relu', padding='same', name='block4_conv2'))
model.add(MaxPooling2D((2, 2), strides=(2, 2), name='block4_pool'))
# Block 5
model.add(Conv2D(512, (3, 3), activation='relu', padding='same', name='block5_conv1'))
model.add(Conv2D(512, (3, 3), activation='relu', padding='same', name='block5_conv2'))
model.add(Conv2D(512, (3, 3), activation='relu', padding='same', name='block5_conv3'))
model.add(MaxPooling2D((2, 2), strides=(2, 2), name='block5_pool'))
# Classification block
model.add(Flatten(name='flatten'))
model.add(Dense(4096, activation='relu', name='fc1'))
model.add(Dense(4096, activation='relu', name='fc2'))
model.add(Dense(num_classes, activation='softmax', name='predictions'))

opt = keras.optimizers.rmsprop(lr=0.0001, decay=1e-6)
model.compile(loss='categorical_crossentropy',
              optimizer=opt,
              metrics=['accuracy'])
x_train = x_train.astype('float32')
x_test = x_test.astype('float32')
x_train /= 255
x_test /= 255

model.fit(x_train, y_train,
              batch_size=batch_size,
              epochs=epochs,
              validation_data=(x_test, y_test),
              shuffle=True)

scores = model.evaluate(x_test, y_test, verbose=1)
print('Test loss:', scores[0])
print('Test accuracy:', scores[1])


```


# 書き方その２：Functional API Modelで出力を次の入力に繋ぐ

googlenet系のinceptionやresnet系などはFunctinal API Modelで書いています。


```py:
img_input = Input(shape=x_train.shape[1:])
print(img_input.shape)
# Block 1
x = Conv2D(64, (3, 3), activation='relu', padding='same', name='block1_conv1')(img_input)
x = Conv2D(64, (3, 3), activation='relu', padding='same', name='block1_conv2')(x)
x = MaxPooling2D((2, 2), strides=(2, 2), name='block1_pool')(x)
# Block 2
x = Conv2D(128, (3, 3), activation='relu', padding='same', name='block2_conv1')(x)
x = Conv2D(128, (3, 3), activation='relu', padding='same', name='block2_conv2')(x)
x = MaxPooling2D((2, 2), strides=(2, 2), name='block2_pool')(x)
# Block 3
x = Conv2D(256, (3, 3), activation='relu', padding='same', name='block3_conv1')(x)
x = Conv2D(256, (3, 3), activation='relu', padding='same', name='block3_conv2')(x)
x = MaxPooling2D((2, 2), strides=(2, 2), name='block3_pool')(x)
# Block 4
x = Conv2D(512, (3, 3), activation='relu', padding='same', name='block4_conv1')(x)
x = Conv2D(512, (3, 3), activation='relu', padding='same', name='block4_conv2')(x)
x = MaxPooling2D((2, 2), strides=(2, 2), name='block4_pool')(x)
# Block 5
x = Conv2D(512, (3, 3), activation='relu', padding='same', name='block5_conv1')(x)
x = Conv2D(512, (3, 3), activation='relu', padding='same', name='block5_conv2')(x)
x = Conv2D(512, (3, 3), activation='relu', padding='same', name='block5_conv3')(x)
x = MaxPooling2D((2, 2), strides=(2, 2), name='block5_pool')(x)
# Classification block
x = Flatten(name='flatten')(x)
x = Dense(4096, activation='relu', name='fc1')(x)
x = Dense(4096, activation='relu', name='fc2')(x)
x = Dense(num_classes, activation='softmax', name='predictions')(x)

model = Model(img_input, x, name='resnet50')

opt = keras.optimizers.rmsprop(lr=0.0001, decay=1e-6)
model.compile(loss='categorical_crossentropy',
              optimizer=opt,
              metrics=['accuracy'])

x_train = x_train.astype('float32')
x_test = x_test.astype('float32')
x_train /= 255
x_test /= 255

model.fit(x_train, y_train,
              batch_size=batch_size,
              epochs=epochs,
              validation_data=(x_test, y_test),
              shuffle=True)

scores = model.evaluate(x_test, y_test, verbose=1)
print('Test loss:', scores[0])
print('Test accuracy:', scores[1])

```


# 書き方その３：定義して呼び出す model-subclassing


chainerぽく書く([Model subclassing](https://keras.io/models/about-keras-models/#model-subclassing))
↑ご指摘いただきまして追加いたしました！ありがとうございます！


```py:
class SimpleCNN(keras.Model):

    def __init__(self, num_classes=10):
        super(SimpleCNN, self).__init__(name='cnn')
        # Block 1
        self.block1_conv1 = Conv2D(64, (3, 3), activation='relu', padding='same', name='block1_conv1', input_shape=x_train.shape[1:])
        self.block1_conv2 = Conv2D(64, (3, 3), activation='relu', padding='same', name='block1_conv2')
        self.block1_pool = MaxPooling2D((2, 2), strides=(2, 2), name='block1_pool')
        # Block 2
        self.block2_conv1 = Conv2D(128, (3, 3), activation='relu', padding='same', name='block2_conv1')
        self.block2_conv2 = Conv2D(128, (3, 3), activation='relu', padding='same', name='block2_conv2')
        self.block2_pool = MaxPooling2D((2, 2), strides=(2, 2), name='block2_pool')
        # Block 3
        self.block3_conv1 = Conv2D(256, (3, 3), activation='relu', padding='same', name='block3_conv1')
        self.block3_conv2 = Conv2D(256, (3, 3), activation='relu', padding='same', name='block3_conv2')
        self.block3_pool = MaxPooling2D((2, 2), strides=(2, 2), name='block3_pool')
        # Block 4
        self.block4_conv1 = Conv2D(512, (3, 3), activation='relu', padding='same', name='block4_conv1')
        self.block4_conv2 = Conv2D(512, (3, 3), activation='relu', padding='same', name='block4_conv2')
        self.block4_pool = MaxPooling2D((2, 2), strides=(2, 2), name='block4_pool')
        # Block 5
        self.block5_conv1 = Conv2D(512, (3, 3), activation='relu', padding='same', name='block5_conv1')
        self.block5_conv2 = Conv2D(512, (3, 3), activation='relu', padding='same', name='block5_conv2')
        self.block5_conv3 = Conv2D(512, (3, 3), activation='relu', padding='same', name='block5_conv3')
        self.block5_pool = MaxPooling2D((2, 2), strides=(2, 2), name='block5_pool')
        # Classification block
        self.flatten = Flatten(name='flatten')
        self.fc1 = Dense(4096, activation='relu', name='fc1')
        self.fc2 = Dense(4096, activation='relu', name='fc2')
        self.predictions = Dense(num_classes, activation='softmax', name='predictions')

    def call(self, inputs):
        x = self.block1_conv1(inputs)
        x = self.block1_conv2(x)
        x = self.block1_pool(x)
        x = self.block2_conv1(x)
        x = self.block2_conv2(x)
        x = self.block2_pool(x)
        x = self.block3_conv1(x)
        x = self.block3_conv2(x)
        x = self.block3_pool(x)
        x = self.block4_conv1(x)
        x = self.block4_conv2(x)
        x = self.block4_pool(x)
        x = self.block5_conv1(x)
        x = self.block5_conv2(x)
        x = self.block5_conv3(x)
        x = self.block5_pool(x)
        x = self.flatten(x)
        x = self.fc1(x)
        x = self.fc2(x)
        return self.predictions(x)

model = SimpleCNN()
opt = keras.optimizers.rmsprop(lr=0.0001, decay=1e-6)
model.compile(loss='categorical_crossentropy',
              optimizer=opt,
              metrics=['accuracy'])

x_train = x_train.astype('float32')
x_test = x_test.astype('float32')
x_train /= 255
x_test /= 255

model.fit(x_train, y_train,
              batch_size=batch_size,
              epochs=epochs,
              validation_data=(x_test, y_test),
              shuffle=True)

scores = model.evaluate(x_test, y_test, verbose=1)
print('Test loss:', scores[0])
print('Test accuracy:', scores[1])
```


新しい書き方だできるようになったのは嬉しい。

カプセルネットの実装は元からこの記法で書かれてた見たい
https://github.com/keras-team/keras/blob/49f5b931410bc2e56378f20a15e8ac919e0efb88/examples/cifar10_cnn_capsule.py


#Colab使用時のメモリエラーかGPU確保できないエラー

バッチ数下げるか、colab以外を使うかちょっと待つと解決したりする。


```
Hint: If you want to see a list of allocated tensors when OOM happens, add report_tensor_allocations_upon_oom to RunOptions for current allocation info
```

#accuracyとscore

accuracy書く時のメモ。


```py:
# Keras reported accuracy:
score = model.evaluate(x_test, y_test, verbose=0) 
score[1]
# 0.9975801164627075

# Actual accuracy calculated manually:
import numpy as np
y_pred = model.predict(x_test)
acc = sum([np.argmax(y_test[i])==np.argmax(y_pred[i]) for i in range(10000)])/10000
acc
# 0.98780000000000001

score[1]==acc
```