📉

マラリア細胞検知を題材に、TensorFlowの3つのモデル構築方法を解説

2023/04/22に公開

はじめに

TensorFlow には、モデルの定義方法が3つ存在します。

  1. Sequential モデル
  2. Functional API
  3. モデルクラス

それぞれの方法を用いて、マラリア細胞の検知する LeNet ライクなモデルを構築する。

完全なコードは以下にアップロードした。この記事では重要な部分だけ説明する。

データセット

データセットは以下を用いる。

https://www.tensorflow.org/datasets/catalog/malaria

画像のサイズを揃えて正規化を行う。

import tensorflow_datasets as tfds
import tensorflow as tf

IM_SIZE = 224

@tf.function
def resize(image, label):
  return tf.image.resize(image, (IM_SIZE, IM_SIZE))/255.0, label

dataset, dataset_info = tfds.load('malaria', with_info=True, as_supervised=True, shuffle_files = True, split=['train'])
dataset = dataset[0].map(resize, num_parallel_calls=tf.data.experimental.AUTOTUNE)

Sequential モデル

Sequential は、各レイヤーに一つの入力と一つの出力があるニューラルネットワークのモデルを簡単に構築出来る。
コードを以下に示す。

lenet_model = tf.keras.Sequential([
    InputLayer(input_shape = (IM_SIZE, IM_SIZE, 3)),

    Conv2D(filters = N_FILTERS , kernel_size = KERNEL_SIZE, strides = N_STRIDES , padding='valid',
          activation = 'relu',kernel_regularizer = L2(REGULARIZATION_RATE)),
    BatchNormalization(),
    MaxPool2D (pool_size = POOL_SIZE, strides= N_STRIDES*2),
    Dropout(rate = DROPOUT_RATE ),

    Conv2D(filters = N_FILTERS*2 + 4, kernel_size = KERNEL_SIZE, strides=N_STRIDES, padding='valid',
          activation = 'relu', kernel_regularizer = L2(REGULARIZATION_RATE)),
    BatchNormalization(),
    MaxPool2D (pool_size = POOL_SIZE, strides= N_STRIDES*2),

    Flatten(),

    Dense( CONFIGURATION['N_DENSE_1'], activation = "relu"),
    BatchNormalization(),
    Dropout(rate = DROPOUT_RATE),

    Dense( CONFIGURATION['N_DENSE_2'], activation = "relu"),
    BatchNormalization(),

    Dense(1, activation = "sigmoid"),
])

lenet_model.summary()

画像を CNN で変換し全結合層で推論を行う。このような基本的な画像検知モデルを作成するのに Sequential は適している。ただ、入力や出力が複数あったり途中で分岐したりするモデルを Sequential で作成することは出来ない。このような複雑なモデル定義するには、次に説明する Functional API を用いる。

Functional API

Functional API はより複雑なモデルを定義するのに使用出来る。今回は Sequential で構築出来るモデルであるため、Functional API を使用するメリットはない。
コードを以下に示す。

func_input = Input(shape=(IM_SIZE, IM_SIZE, 3), name='input')

x = Conv2D(filters = N_FILTERS , kernel_size = KERNEL_SIZE, strides = N_STRIDES , padding='valid',
        activation = 'relu',kernel_regularizer = L2(REGULARIZATION_RATE))(func_input)
x = BatchNormalization()(x)
x = MaxPool2D (pool_size = POOL_SIZE, strides= N_STRIDES*2)(x)
x = Dropout(rate = DROPOUT_RATE )(x)

x = Conv2D(filters = N_FILTERS*2 + 4, kernel_size = KERNEL_SIZE, strides=N_STRIDES, padding='valid',
        activation = 'relu', kernel_regularizer = L2(REGULARIZATION_RATE))(x)
x = BatchNormalization()(x)
x = MaxPool2D (pool_size = POOL_SIZE, strides= N_STRIDES*2)(x)

x = Flatten()(x)

x = Dense( CONFIGURATION['N_DENSE_1'], activation = "relu")(x)
x = BatchNormalization()(x)
x = Dropout(rate = DROPOUT_RATE)(x)

x = Dense( CONFIGURATION['N_DENSE_2'], activation = "relu")(x)
x = BatchNormalization()(x)

output = Dense(1, activation = "sigmoid")(x)

functional_lenet_model = Model(func_input, output, name = "Feature_Extractor")
functional_lenet_model.summary()

出力を順番に入力に加えてくことで、非巡回有向グラフを作成出来る。ただ、RNN のような巡回を持つモデルは Functional API を用いて作成出来ない。より複雑なモデルを定義したい場合は、次に説明するクラスを用いる。

モデルクラス

クラスはより複雑なモデルを定義するのに使用出来る。Layer を継承して再帰的なレイヤーを定義することで、Functional API より複雑なモデルを作成可能である。今回は Sequential で構築出来るモデルであるため、クラス使用するメリットはない。
コードを以下に示す。

class Lenet(Model):
    def __init__(self):
        super(Lenet, self).__init__()

        self.conv1 = Conv2D(filters = N_FILTERS , kernel_size = KERNEL_SIZE, strides = N_STRIDES , padding='valid',
            activation = 'relu',kernel_regularizer = L2(REGULARIZATION_RATE))
        self.batch_norm1 = BatchNormalization()
        self.max_pool1 = MaxPool2D (pool_size = POOL_SIZE, strides= N_STRIDES*2)

        self.conv2 = Conv2D(filters = N_FILTERS*2 + 4, kernel_size = KERNEL_SIZE, strides=N_STRIDES, padding='valid',
            activation = 'relu', kernel_regularizer = L2(REGULARIZATION_RATE))
        self.batch_norm2 = BatchNormalization()
        self.max_pool2 = MaxPool2D (pool_size = POOL_SIZE, strides= N_STRIDES*2)

        self.flatten = Flatten()

        self.dense1 = Dense( CONFIGURATION['N_DENSE_1'], activation = "relu")
        self.batch_norm3 = BatchNormalization()
        self.dropout1 = Dropout(rate = DROPOUT_RATE)

        self.dense2 = Dense( CONFIGURATION['N_DENSE_2'], activation = "relu")
        self.batch_norm4 = BatchNormalization()

        self.dense3 = Dense(1, activation = "sigmoid")

    def call(self, inputs):

        x = self.conv1(inputs)
        x = self.batch_norm1(x)
        x = self.max_pool1(x)
        x = self.dropout1(x)

        x = self.conv2(x)
        x = self.batch_norm2(x)
        x = self.max_pool2(x)

        x = self.flatten(x)

        x = self.dense1(x)
        x = self.batch_norm3(x)
        x = self.dropout1(x)

        x = self.dense2(x)
        x = self.batch_norm4(x)

        x = self.dense3(x)
        return x


class_lenet_model = Lenet()
class_lenet_model.build(input_shape=(None, IM_SIZE, IM_SIZE, 3))
class_lenet_model.summary()

__init__でレイヤーを定義して、callでモデルの処理を定義する。call内で Sequential を用いてモデルを構築することも可能である。

おわりに

Sequential と Functional API とクラスを用いて TensorFlow のモデルを作成する方法について解説した。

Discussion