Tensorflow2系 モデルの保存と読み込み

1. Tensorflow 2系のモデルの保存と読み込み

1.1. 目次

1.2. 概要

Tensorflow2 でのモデルの保存方法についてメモしておく。
参考 https://www.tensorflow.org/guide/keras/save_and_serialize?hl=ja#sequential_%E3%83%A2%E3%83%87%E3%83%AB%E3%81%BE%E3%81%9F%E3%81%AF_functional_api_%E3%83%A2%E3%83%87%E3%83%AB%E3%81%AE%E6%A7%8B%E6%88%90

Tensorflow2 ではモデル全体を保存する方法とモデルの重みを保存する方法がある。
また、それらの保存における保存形式として2つの保存形式がある。

2つの保存形式はそれぞれKeras H5形式とTensorflow SavedModel形式と呼ばれる。

まずはモデル全体の保存する方法をメモしてから、次にモデルの重みを保存する方法を見ていく。

1.3. 開発環境

Ubuntu 18.04 LTS

!cat $VIRTUAL_ENV/../pyproject.toml
[tool.poetry]
name = "tips"
version = "0.1.0"
description = ""
authors = ["Your Name <you@example.com>"]

[tool.poetry.dependencies]
python = "^3.8"
numpy = "1.19.3"
tensorflow-cpu = "2.6.2"
jupyter = "^1.0.0"
nbconvert = "^6.3.0"
Pillow = "^8.4.0"

[tool.poetry.dev-dependencies]
pytest = "^5.2"

[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"
import os
from pathlib import Path

import tensorflow as tf
import tensorflow.keras as keras 

FILE_DIR = Path(os.path.abspath(os.path.curdir))

1.4. モデル準備

def make_model():
    wsize, hsize, csize = 28, 28, 3
    x0 = keras.layers.Input(shape=(wsize, hsize, csize))
    x = keras.layers.Conv2D(32, 3, activation='relu')(x0)
    x = keras.layers.Conv2D(64, 3, activation='relu')(x)
    x = keras.layers.Flatten()(x)
    x = keras.layers.Dense(128, activation='relu')(x)
    x = keras.layers.Dense(10, activation='softmax')(x)
    model = keras.Model(inputs=(x0), outputs=(x))
    return model
model = make_model()

model.summary()
model.compile(optimizer=keras.optimizers.Adam(),
              loss=keras.losses.SparseCategoricalCrossentropy())
Model: "model"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
input_1 (InputLayer)         [(None, 28, 28, 3)]       0         
_________________________________________________________________
conv2d (Conv2D)              (None, 26, 26, 32)        896       
_________________________________________________________________
conv2d_1 (Conv2D)            (None, 24, 24, 64)        18496     
_________________________________________________________________
flatten (Flatten)            (None, 36864)             0         
_________________________________________________________________
dense (Dense)                (None, 128)               4718720   
_________________________________________________________________
dense_1 (Dense)              (None, 10)                1290      
=================================================================
Total params: 4,739,402
Trainable params: 4,739,402
Non-trainable params: 0
_________________________________________________________________


2021-12-18 16:12:32.573350: I tensorflow/core/platform/cpu_feature_guard.cc:142] This TensorFlow binary is optimized with oneAPI Deep Neural Network Library (oneDNN) to use the following CPU instructions in performance-critical operations:  AVX2 FMA
To enable them in other operations, rebuild TensorFlow with the appropriate compiler flags.

1.5. モデル全体の保存と読み込み

モデル全体の保存はkeras.Model.saveで行うことができる。
Keras h5形式での保存の場合、拡張子を.h5にしたファイル名を指定する。
それ以外の場合はSavedModel形式で保存される。

モデル全体の保存は以下のようにすると保存することができる。

keras_model_file = Path(f"{FILE_DIR}/data/models/keras_model.h5")
saved_model_file = Path(f"{FILE_DIR}/data/models/saved_model")
# モデル全体のセーブ
print("# start save h5")
model.save(keras_model_file)
print("# start save SavedModel")                  
model.save(saved_model_file)
# start save h5
# start save SavedModel

この際にKeras h5形式の場合はひとつのファイルにモデルの構造や重みが保存されるが、SavedModel形式ではディレクトリに保存される。

!ls data/models/*
data/models/keras_model.h5

data/models/saved_model:
assets  keras_metadata.pb  saved_model.pb  variables

saved_model.pb は saved_model形式でのモデルの構造等が保存してある。
keras_metadata.pb はtf2.5から導入されたモノで、おそらくSavedModel形式からKeras.Modelを復元するためのモノである。
この点がSavedModel形式で保存するほうがよい理由となる。
variablesはモデルの重みが保存してある。

読み込みは以下の方法で行う。

print("load keras model")
# keras model インスタンスとしてロード
# keras model load from h5 model
keras_model1 :keras.Model = keras.models.load_model(keras_model_file)
keras_model1.summary()
del keras_model1
load keras model
Model: "model"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
input_1 (InputLayer)         [(None, 28, 28, 3)]       0         
_________________________________________________________________
conv2d (Conv2D)              (None, 26, 26, 32)        896       
_________________________________________________________________
conv2d_1 (Conv2D)            (None, 24, 24, 64)        18496     
_________________________________________________________________
flatten (Flatten)            (None, 36864)             0         
_________________________________________________________________
dense (Dense)                (None, 128)               4718720   
_________________________________________________________________
dense_1 (Dense)              (None, 10)                1290      
=================================================================
Total params: 4,739,402
Trainable params: 4,739,402
Non-trainable params: 0
_________________________________________________________________
Model: "model"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
input_1 (InputLayer)         [(None, 28, 28, 3)]       0         
_________________________________________________________________
conv2d (Conv2D)              (None, 26, 26, 32)        896       
_________________________________________________________________
conv2d_1 (Conv2D)            (None, 24, 24, 64)        18496     
_________________________________________________________________
flatten (Flatten)            (None, 36864)             0         
_________________________________________________________________
dense (Dense)                (None, 128)               4718720   
_________________________________________________________________
dense_1 (Dense)              (None, 10)                1290      
=================================================================
Total params: 4,739,402
Trainable params: 4,739,402
Non-trainable params: 0
_________________________________________________________________
# keras model load from tf model
keras_model2 :keras.Model = keras.models.load_model(saved_model_file)
keras_model2.summary()
del keras_model2
Model: "model"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
input_1 (InputLayer)         [(None, 28, 28, 3)]       0         
_________________________________________________________________
conv2d (Conv2D)              (None, 26, 26, 32)        896       
_________________________________________________________________
conv2d_1 (Conv2D)            (None, 24, 24, 64)        18496     
_________________________________________________________________
flatten (Flatten)            (None, 36864)             0         
_________________________________________________________________
dense (Dense)                (None, 128)               4718720   
_________________________________________________________________
dense_1 (Dense)              (None, 10)                1290      
=================================================================
Total params: 4,739,402
Trainable params: 4,739,402
Non-trainable params: 0
_________________________________________________________________
# tf形式としてロード
print("load_tf_model")
# tf model load from keras model
# Error
try:
    tf_model1 = tf.saved_model.load(keras_model_file)
except Exception as e:
    print("error")
else:
    print(tf_model1)
    del tf_model1
load_tf_model
error
# tf model load from tf model
# Success
tf_model2 = tf.saved_model.load(str(saved_model_file))
print(tf_model2)
del tf_model2
<tensorflow.python.saved_model.load.Loader._recreate_base_user_object.<locals>._UserObject object at 0x7f0a0437da00>

1.6. 重みの保存と読み込み

モデルの全体だけでなく、重みだけを保存する方法も存在する。
重みだけの保存は転移学習等で使用するときに使うために用いられる。

重みの保存にもkeras h5形式とSavedModel形式がある。
どちらもKeras.Modelインスタンスで読み込みことができるのでどちらが良いのかは分からない。

重みの保存は以下のコードで行うことができる。

# 重みだけ保存
weights_file = f"{FILE_DIR}/data/weights/weights.h5"
checkpoint_file = f"{FILE_DIR}/data/weights/ckpt"
print("save weights")
# hdf5 形式
model.save_weights(weights_file)
# tf形式
model.save_weights(checkpoint_file)
save weights

これらのデータは以下のように保存されている weights.h5weights_fileを引数に渡した時に作成されるファイルである。 それ以外のファイルはcheckpoint_fileを引数に渡した時に作成されるファイルである。

これはsaved_model形式でモデル全体を保存した時に作成される重みと同じである。

checkpointファイルに関しては分からないので要調査が必要。

!ls data/weights/
!echo "---"
!ls data/models/saved_model/variables
checkpoint  ckpt.data-00000-of-00001  ckpt.index  weights.h5
---
variables.data-00000-of-00001  variables.index

重みの読み込みは以下の方法で行う。 SavedModel形式のファイルはドットの前までの名前を使えば読み込むことができる。

モデル全体を保存したときに保存されたファイルも同じように読み込むことができる。

# 重みの読み込み
print("load weights")
model.load_weights(weights_file)
model.load_weights(checkpoint_file)

model.load_weights(f"{FILE_DIR}/data/models/saved_model/variables/variables")
load weights





<tensorflow.python.training.tracking.util.CheckpointLoadStatus at 0x7f0a044b6a60>

Tensorflow2 モデル構築

1. Tutorial2 モデルの作成

1.1. 目次

1.2. 結論

Functional API 作成方法でモデルを作ろう。

1.3. 概要

Tensorflow2系でのモデルの作成方法は3つの方法がある。
それらの方法について解説していく。

3つ方法は以下の名前で呼ばれる。

1.4. 開発環境

Ubuntu 18.04 LTS

!cat $VIRTUAL_ENV/../pyproject.toml
[tool.poetry]
name = "tips"
version = "0.1.0"
description = ""
authors = ["Your Name <you@example.com>"]

[tool.poetry.dependencies]
python = "^3.8"
numpy = "1.19.3"
tensorflow-cpu = "2.6.2"
jupyter = "^1.0.0"
nbconvert = "^6.3.0"
Pillow = "^8.4.0"

[tool.poetry.dev-dependencies]
pytest = "^5.2"

[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"
import numpy as np
import tensorflow as tf
import tensorflow.keras as keras

1.5. データ

今回はMNISTデータセットを用いる

train, test = keras.datasets.mnist.load_data()
train_x, train_y = train

train_x = train_x / 255 # normalize
train_x = np.expand_dims(train_x, axis=-1) # 28x28 -> 28x28x1
print(f"[shape] : {np.shape(train_x)}")
_, hsize, wsize, csize = np.shape(train_x)

unique_labels = np.unique(train_y)
num_class = len(unique_labels)
print(f"[Unique label] : {unique_labels}")
print(f"[num class] : {num_class}")
[shape] : (60000, 28, 28, 1)
[Unique label] : [0 1 2 3 4 5 6 7 8 9]
[num class] : 10

1.6. Subclass API

Subclass API はモデルのひな型を継承して、その内部にレイヤーなどを定義する方法である。
この方法はpytorchを使っている人には馴染み深い方法である。
pytorchの時はtorch.nn.Moduleを継承してモデルを定義していたが、Tensorflow2系ではtensorflow.keras.Modelを継承してモデルを定義する。 今回はMNISTの分類問題におけるモデルを作成してみる。

class MyModel(keras.Model):
    def __init__(self, num_class):
        super().__init__()
        self.conv1 = keras.layers.Conv2D(32, 3, activation='relu') # 畳み込み層
        self.conv2 = keras.layers.Conv2D(64, 3, activation='relu') # 畳み込み層
        self.flatten = keras.layers.Flatten() # バッチサイズ以外を平坦化
        self.dense1 = keras.layers.Dense(128, activation='relu') # 全結合層 torch.nn.flattenと同一
        self.dense2 = keras.layers.Dense(num_class, activation='softmax')

    def call(self, x):
        x = self.conv1(x)
        x = self.conv2(x)
        x = self.flatten(x)
        x = self.dense1(x)
        x = self.dense2(x)
        return x

model = MyModel(num_class)
model.compile(optimizer=keras.optimizers.Adam(),
              loss=keras.losses.SparseCategoricalCrossentropy(),
              metrics=keras.metrics.Accuracy())
2021-12-16 22:29:28.038684: I tensorflow/core/platform/cpu_feature_guard.cc:142] This TensorFlow binary is optimized with oneAPI Deep Neural Network Library (oneDNN) to use the following CPU instructions in performance-critical operations:  AVX2 FMA
To enable them in other operations, rebuild TensorFlow with the appropriate compiler flags.

Subclass API を用いて作成されたモデルは define by run 形式で作成されるのでデータが渡されるまでmodel.summary()で構造を確認することができない。

model.summary()
---------------------------------------------------------------------------

ValueError                                Traceback (most recent call last)

/tmp/ipykernel_800/3470139634.py in <module>
----> 1 model.summary()


~/workspace/mytensorflow/tips/.venv/lib/python3.8/site-packages/keras/engine/training.py in summary(self, line_length, positions, print_fn)
   2519     """
   2520     if not self.built:
-> 2521       raise ValueError('This model has not yet been built. '
   2522                        'Build the model first by calling `build()` or calling '
   2523                        '`fit()` with some data, or specify '


ValueError: This model has not yet been built. Build the model first by calling `build()` or calling `fit()` with some data, or specify an `input_shape` argument in the first layer(s) for automatic build.

一回データを通すとmodel.summary()を用いてモデルの構造を表示することができるが、Output Shape等の詳細な情報を表示されない。

result = model.predict(train_x[:1])
model.summary()
Model: "my_model"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv2d (Conv2D)              multiple                  320       
_________________________________________________________________
conv2d_1 (Conv2D)            multiple                  18496     
_________________________________________________________________
flatten (Flatten)            multiple                  0         
_________________________________________________________________
dense (Dense)                multiple                  4718720   
_________________________________________________________________
dense_1 (Dense)              multiple                  1290      
=================================================================
Total params: 4,738,826
Trainable params: 4,738,826
Non-trainable params: 0
_________________________________________________________________


2021-12-16 22:29:30.558040: I tensorflow/compiler/mlir/mlir_graph_optimization_pass.cc:185] None of the MLIR Optimization Passes are enabled (registered 2)

これらの問題からあまり推奨できない。

1.7. Sequential API

Sequentaial モデルは一番簡単な作成方法である。
この方法はレイヤーをリストに入れていき、最終的にkeras.Sequetial()に引数として渡すだけで定義できる。

mylayers = [keras.layers.Input(shape=(28, 28, 3)),
            keras.layers.Conv2D(32, 3, activation='relu'), 
            keras.layers.Conv2D(64, 3, activation='relu'), 
            keras.layers.Flatten(), 
            keras.layers.Dense(128, activation='relu'),
            keras.layers.Dense(num_class, activation='softmax')]
            
model = keras.Sequential(mylayers)
model.compile(optimizer=keras.optimizers.Adam(),
              loss=keras.losses.SparseCategoricalCrossentropy(),
              metrics=keras.metrics.Accuracy())
model.summary()
Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv2d_2 (Conv2D)            (None, 26, 26, 32)        896       
_________________________________________________________________
conv2d_3 (Conv2D)            (None, 24, 24, 64)        18496     
_________________________________________________________________
flatten_1 (Flatten)          (None, 36864)             0         
_________________________________________________________________
dense_2 (Dense)              (None, 128)               4718720   
_________________________________________________________________
dense_3 (Dense)              (None, 10)                1290      
=================================================================
Total params: 4,739,402
Trainable params: 4,739,402
Non-trainable params: 0
_________________________________________________________________

もしくは最初にSequentialクラスをインスタンス化しておいて、addコマンドでレイヤーを追加していく方式も取れる。

model = keras.Sequential()
model.add(keras.layers.Input(shape=(28, 28, 3)))
model.add(keras.layers.Conv2D(32, 3, activation='relu')) # 畳み込み層
model.add(keras.layers.Conv2D(64, 3, activation='relu')) # 畳み込み層
model.add(keras.layers.Flatten()) # バッチサイズ以外を平坦化
model.add(keras.layers.Dense(128, activation='relu')) # 全結合層 torch.nn.flattenと同一
model.add(keras.layers.Dense(num_class, activation='softmax'))

model.compile(optimizer=keras.optimizers.Adam(),
              loss=keras.losses.SparseCategoricalCrossentropy(),
              metrics=keras.metrics.Accuracy())
model.summary()
Model: "sequential_1"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv2d_4 (Conv2D)            (None, 26, 26, 32)        896       
_________________________________________________________________
conv2d_5 (Conv2D)            (None, 24, 24, 64)        18496     
_________________________________________________________________
flatten_2 (Flatten)          (None, 36864)             0         
_________________________________________________________________
dense_4 (Dense)              (None, 128)               4718720   
_________________________________________________________________
dense_5 (Dense)              (None, 10)                1290      
=================================================================
Total params: 4,739,402
Trainable params: 4,739,402
Non-trainable params: 0
_________________________________________________________________

しかしコノ方法ではレイヤーが直列したモデルしか書けない。
なので Functional API の手法を使うのが一番良いだろう。

1.8. Functional API

Functional API では入力がレイヤーを通過していくように記述していく。
最後に入力と最終出力を使ってモデルを作成する。

x0 = keras.layers.Input(shape=(28, 28, 3))
x = keras.layers.Conv2D(32, 3, activation='relu')(x0)
x = keras.layers.Conv2D(64, 3, activation='relu')(x) 
x = keras.layers.Flatten()(x)
x = keras.layers.Dense(128, activation='relu')(x)
x = keras.layers.Dense(num_class, activation='softmax')(x)
model = keras.Model(inputs=[x0], outputs=[x])

model.compile(optimizer=keras.optimizers.Adam(),
              loss=keras.losses.SparseCategoricalCrossentropy(),
              metrics=keras.metrics.Accuracy())
model.summary()
Model: "model"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
input_3 (InputLayer)         [(None, 28, 28, 3)]       0         
_________________________________________________________________
conv2d_6 (Conv2D)            (None, 26, 26, 32)        896       
_________________________________________________________________
conv2d_7 (Conv2D)            (None, 24, 24, 64)        18496     
_________________________________________________________________
flatten_3 (Flatten)          (None, 36864)             0         
_________________________________________________________________
dense_6 (Dense)              (None, 128)               4718720   
_________________________________________________________________
dense_7 (Dense)              (None, 10)                1290      
=================================================================
Total params: 4,739,402
Trainable params: 4,739,402
Non-trainable params: 0
_________________________________________________________________

並列なレイヤーを持つモデルを作成してみると以下のように2出力のモデルなどが作れる。

x0 = keras.layers.Input(shape=(28, 28, 3))
x = keras.layers.Conv2D(32, 3, activation='relu')(x0)
x = keras.layers.Conv2D(64, 3, activation='relu')(x) 
x = keras.layers.Flatten()(x)
x = keras.layers.Dense(128, activation='relu')(x)
x1 = keras.layers.Dense(num_class, activation='softmax')(x)
x2 = keras.layers.Dense(100, activation='sigmoid')(x)
model = keras.Model(inputs=[x0], outputs=[x1, x2])

model.compile(optimizer=keras.optimizers.Adam(),
              loss=keras.losses.SparseCategoricalCrossentropy(),
              metrics=keras.metrics.Accuracy())
model.summary()
Model: "model_1"
__________________________________________________________________________________________________
Layer (type)                    Output Shape         Param #     Connected to                     
==================================================================================================
input_4 (InputLayer)            [(None, 28, 28, 3)]  0                                            
__________________________________________________________________________________________________
conv2d_8 (Conv2D)               (None, 26, 26, 32)   896         input_4[0][0]                    
__________________________________________________________________________________________________
conv2d_9 (Conv2D)               (None, 24, 24, 64)   18496       conv2d_8[0][0]                   
__________________________________________________________________________________________________
flatten_4 (Flatten)             (None, 36864)        0           conv2d_9[0][0]                   
__________________________________________________________________________________________________
dense_8 (Dense)                 (None, 128)          4718720     flatten_4[0][0]                  
__________________________________________________________________________________________________
dense_9 (Dense)                 (None, 10)           1290        dense_8[0][0]                    
__________________________________________________________________________________________________
dense_10 (Dense)                (None, 100)          12900       dense_8[0][0]                    
==================================================================================================
Total params: 4,752,302
Trainable params: 4,752,302
Non-trainable params: 0
__________________________________________________________________________________________________

この方式はKerasTensorという仮想Tensorをレイヤーに通してモデルを作成していく過程が意識しやすい。
例えばx0変数は以下の形式である。
この方式に慣れておいたほうが後々自作レイヤーを作る際に糧となるだろう。

print(x0)
KerasTensor(type_spec=TensorSpec(shape=(None, 28, 28, 3), dtype=tf.float32, name='input_4'), name='input_4', description="created by layer 'input_4'")

1.9. 最後に

以上3つのモデル作成方法 SubClassAPI, SequentialAPI, Functional APIを紹介した。

カスタマイズ性の高さ等を考えると Functional APIを使うのがよいだろう。

Tensorflow チュートリアル Mnistでの画像分類

1. Tensorflow Tutorials

TensorflowのMNISTを使ったチュートリアルについて記載する

  • 最終更新 2021/12/14

1.1. 目次

1.2. 開発環境

Ubuntu 18.04 LTS

# 開発環境
!cat $VIRTUAL_ENV/../pyproject.toml
[tool.poetry]
name = "tips"
version = "0.1.0"
description = ""
authors = ["Your Name <you@example.com>"]

[tool.poetry.dependencies]
python = "^3.8"
numpy = "1.19.3"
tensorflow-cpu = "2.6.2"
jupyter = "^1.0.0"
nbconvert = "^6.3.0"
Pillow = "^8.4.0"

[tool.poetry.dev-dependencies]
pytest = "^5.2"

[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"
import tensorflow as tf
import tensorflow.keras as keras

import numpy as np
from PIL import Image
import IPython.display as display

1.3. 数字判定データセットで学習させる

数字判定のAIモデルを作ってみる。

1.3.1. 学習データ準備

数字と数字ラベルがついたデータをダウンロードする。
データは60000枚の28x28のサイズの画像(train_x)とそれに対する正解ラベル(train_y)である。
データの種類は0~9までの10個の数字に分類されている。

# データ準備
train_data, test_data = keras.datasets.mnist.load_data()
train_x, train_y = train_data
print("train_x")
print(f"[ type  ] : {type(train_x)}")
print(f"[ shape ] : {np.shape(train_x)}")
print(f"[ dtype ] : {train_x.dtype}")
print("train_y")
print(f"[ type  ] : {type(train_y)}")
print(f"[ shape ] : {np.shape(train_y)}")
print(f"[ dtype ] : {train_y.dtype}")
train_x
[ type  ] : <class 'numpy.ndarray'>
[ shape ] : (60000, 28, 28)
[ dtype ] : uint8
train_y
[ type  ] : <class 'numpy.ndarray'>
[ shape ] : (60000,)
[ dtype ] : uint8

変形を行い、データをモデルに合うようにする。 1. 画措値は0~255の値である。モデルの学習を早くするために0~1の範囲に圧縮する
2. データの次元を拡張する。グレイスケール画像なのでチャンネルサイズを1とする

# データ変形
train_x = train_x / 255 # normalize
train_x = np.expand_dims(train_x, axis=-1) # 28x28 -> 28x28x1
print(f"[shape] : {np.shape(train_x)}")
_, hsize, wsize, csize = np.shape(train_x)
[shape] : (60000, 28, 28, 1)

正解データのクラス数を求めておく。
求めたクラス数はモデルの出力の数を決めるために用いられる。

# 正解データのクラス数(種類)
unique_labels = np.unique(train_y)
num_class = len(unique_labels)
print(f"[Unique label] : {unique_labels}")
print(f"[num class] : {num_class}")
[Unique label] : [0 1 2 3 4 5 6 7 8 9]
[num class] : 10

ちなみにココで使用したMNISTデータセットUbuntuの場合~/.keras/datasetsにダウンロードされる。

!ls ~/.keras/datasets/
mnist.npz

このnpzファイルはnumpyのデータが保存されたモノである。そのためnumpy.loadでロードできる。

import os
_temp = np.load(f"{os.environ['HOME']}/.keras/datasets/mnist.npz")
print(_temp.files)
print(_temp['x_train'].shape)
print(_temp['y_train'].shape)
['x_test', 'x_train', 'y_train', 'y_test']
(60000, 28, 28)
(60000,)

1.3.2. モデル作成

入力データと出力データに沿ったモデルを作成する。

モデルは Functional API 形式で作成する。

モデルを作成したら compile を行う。 compile関数はoptimizerと損失関数loss、そしてmetricsを決定する。
optimizerにはモデルを更新する勾配を適用する最適化関数である。Adamが使われることが多いのでAdamを用いる。
lossはモデルの出力と正解データの比較をとる損失関数である。モデルやデータの種類によって変わるが、今回は多クラス分類なのでSparseCategoricalCrossentropyを用いる。
metricsはモデルを評価する関数である。今回はモデルの全結果が何%正解しているかを表すaccuuracy(精度)を用いる。

x0 = keras.layers.Input((hsize, wsize, csize))
x = keras.layers.Conv2D(32, 3, activation='relu')(x0)
x = keras.layers.Conv2D(64, 3, activation='relu')(x)
x = keras.layers.Flatten()(x)
x = keras.layers.Dense(128, activation='relu')(x)
x = keras.layers.Dense(num_class, activation='softmax')(x)
model = keras.Model(inputs=[x0], outputs=[x])
model.summary()
Model: "model"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
input_1 (InputLayer)         [(None, 28, 28, 1)]       0         
_________________________________________________________________
conv2d (Conv2D)              (None, 26, 26, 32)        320       
_________________________________________________________________
conv2d_1 (Conv2D)            (None, 24, 24, 64)        18496     
_________________________________________________________________
flatten (Flatten)            (None, 36864)             0         
_________________________________________________________________
dense (Dense)                (None, 128)               4718720   
_________________________________________________________________
dense_1 (Dense)              (None, 10)                1290      
=================================================================
Total params: 4,738,826
Trainable params: 4,738,826
Non-trainable params: 0
_________________________________________________________________


2021-12-14 22:19:58.873474: I tensorflow/core/platform/cpu_feature_guard.cc:142] This TensorFlow binary is optimized with oneAPI Deep Neural Network Library (oneDNN) to use the following CPU instructions in performance-critical operations:  AVX2 FMA
To enable them in other operations, rebuild TensorFlow with the appropriate compiler flags.

1.3.3. 学習

学習方法はいくつかあるが今回はfit関数を用いて学習を行う

fit関数は入力データtrain_xと正解データtrain_yを渡すことで学習を行うことができる。

この際にエポック数とバッチサイズを決めることができる。

学習データ全体を用いて1回モデルを学習することを1エポックと呼ぶ。1エポックではモデルの性能が満足のいくレベルに到達しないので、ふつうは複数回エポックを繰り返す。この回数をエポック数と呼ぶ。 今回は適当に 3エポック 学習を行うこととする。

バッチサイズは一回に学習するデータの数である。
バッチサイズを設定する理由はいくつかあるがここでは記載しない 慣例として$2n$が使われる。 今回は適当に 32 バッチサイズで行うこととする。

model.fit(train_x, train_y, batch_size=32, epochs=3)
Epoch 1/3
1875/1875 [==============================] - 275s 147ms/step - loss: 0.1501 - accuracy: 0.9561
Epoch 2/3
1875/1875 [==============================] - 276s 147ms/step - loss: 0.0616 - accuracy: 0.9807
Epoch 3/3
1875/1875 [==============================] - 276s 147ms/step - loss: 0.0451 - accuracy: 0.9856



<keras.callbacks.History at 0x7f86fc1feb20>

精度を示すaccuracyが上昇しているのが分かる。

tf.Tensorとnp.Tensorの要素抽出の違い

TensorflowとnumpyでのTensorの要素の抽出方法が異なったのでメモしておく。 例として2次元テンソルから偶数の要素を抽出する

import tensorflow as tf
import numpy as np

x_np = np.array(
[[ 1,  2,  3,  4,  5],
 [ 6,  7,  8,  9, 10],
 [11, 12, 13, 14, 15],
 [16, 17, 18, 19, 20],
 [21, 22, 23, 24, 25]]
)

numpyの場合

even_idxes = np.where(x_np % 2 == 0)
y_np = x_np[even_idxes] #ファンシーインデックス機能を利用

結果

even_idxes
(array([0, 0, 1, 1, 1, 2, 2, 3, 3, 3, 4, 4]), array([1, 3, 0, 2, 4, 1, 3, 0, 2, 4, 1, 3]))
----------
y_np
[ 2  4  6  8 10 12 14 16 18 20 22 24]

tensorflowの場合

x_tf = tf.convet_to_tensor(x_np)
even_idxes = tf.where(x_tf % 2 == 0)
y_tf = tf.gather_nd(x_tf, even_idxes)

結果

even_idxes
tf.Tensor(
[[0 1]
 [0 3]
 [1 0]
 [1 2]
 [1 4]
 [2 1]
 [2 3]
 [3 0]
 [3 2]
 [3 4]
 [4 1]
 [4 3]], shape=(12, 2), dtype=int64)
----------
y_tf
tf.Tensor([ 2  4  6  8 10 12 14 16 18 20 22 24], shape=(12,), dtype=int64)