Ensure that inputs to a model are in the correct shape

Il n’y a pas de tutoriel spécifique sur le sujet sur le site de TensorFlow. Celui qui s’en rapproche le plus est : The Keras functional API.

Pour ne pas changer les habitudes, utilisons encore une fois MNIST pour expliquer la shape du modèle.

Pour rappel, un tenseur a un nom (optionnel), un type (e.g. int16), un rang (le nombre de dimensions du tenseur) et un(e) shape (le nombre d’éléments dans chaque dimension).

Lorsqu’on définit un modèle, on doit préciser la shape de l’input qui s’impose par la forme de nos données.

Si on a une image, ce sera la dimension de celle-ci, par exemple (28,28,3), pour une image en couleur(s) ou (28,28) pour une image en niveaux de gris.

import numpy as np
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers

tf.keras.backend.clear_session()  # For easy reset of notebook state.
mnist = tf.keras.datasets.mnist

(x_train, y_train), (x_test, y_test) = mnist.load_data()
x_train, x_test = x_train / 255.0, x_test / 255.0

Ici, on lit un tableau de 60 000 images (28,28) en niveaux de gris pour x_train et 10 000 images pour x_test.

Si notre modèle est le suivant :

model = tf.keras.models.Sequential([
  tf.keras.layers.Flatten(input_shape=(28, 28), name="MyInput"),
  tf.keras.layers.Dense(128, activation='relu'),
  tf.keras.layers.Dropout(0.2),
  tf.keras.layers.Dense(10)
])
loss_fn = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)
model.compile(optimizer='adam',
              loss=loss_fn,
              metrics=['accuracy'])

Alors, l’input shape est :

model.input_shape
(None, 28, 28)
model.summary()
Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
flatten (Flatten)            (None, 784)               0         
_________________________________________________________________
dense (Dense)                (None, 128)               100480    
_________________________________________________________________
dropout (Dropout)            (None, 128)               0         
_________________________________________________________________
dense_1 (Dense)              (None, 10)                1290      
=================================================================
Total params: 101,770
Trainable params: 101,770
Non-trainable params: 0

On pourrait lire les données différemment, non pas en tant que matrices mais comme des vecteurs :

(x_train, y_train), (x_test, y_test) = keras.datasets.mnist.load_data()

x_train = x_train.reshape(60000, 784).astype('float32') / 255
x_test = x_test.reshape(10000, 784).astype('float32') / 255

et dans ce cas ne pas avoir à faire un flatten

model = tf.keras.models.Sequential([
  tf.keras.layers.Input(784,),
  tf.keras.layers.Dense(128, activation='relu'),
  tf.keras.layers.Dropout(0.2),
  tf.keras.layers.Dense(10)
])

Le résultat est identique.

Une autre façon d’écrire le modèle, avec la functional API .

inputs = keras.Input(shape=(784,))
dense = layers.Dense(128, activation='relu')
x = dense(inputs)
dro = layers.Dropout(0.2)
x = dro(x)
outputs = layers.Dense(10)(x)
model = keras.Model(inputs=inputs, outputs=outputs, name='mnist_model')