L'environnement utilise python3.7 et Tensorflow 2.1.1 avec le contenu suivant.
VOC2012 Regardons un exemple de segmentation d'image dans un ensemble de données (voir ci-dessous). Dans cet exemple, les pixels de l'image sont classés comme vélo, conducteur ou arrière-plan. Cette façon de classer les images ** en unités de pixels ** est appelée segmentation d'image (ou segmentation sémantique).
Lorsque vous commencez à étudier les réseaux de neurones, vous implémentez souvent la reconnaissance de caractères manuscrits MNIST dans les didacticiels. Dans ce cas, l'image est entrée et la probabilité que l'image soit un nombre de 0 à 9 est sortie, de sorte que la couche de sortie finale est de 10 nœuds unidimensionnels.
Par contre, dans le cas de la segmentation d'image, nous voulons classer chaque pixel de l'image, donc le calque de sortie final a la dimension (taille verticale de l'image, taille horizontale de l'image, nombre de classes). U-net, que je présenterai cette fois, peut fournir une excellente précision pour de tels problèmes de segmentation d'image.
U-net U-net est un modèle utilisant CNN et skip connection publié dans l'article "U-Net: Convolutional Networks for Biomedical Image Segmentation". (Figure ci-dessous).
L'idée de base d'U-net est ・ Extraction de caractéristiques tout en rendant l'image rugueuse avec la couche de pliage et la couche de mise en commun -Restaurer les informations en unités de pixels avec saut de connexion (la partie connectée horizontalement dans la figure ci-dessus) est. En particulier, la connexion de saut est au cœur de ce U-net, et sans cette connexion de saut, il semble que les informations en unités de pixels ne puissent pas être beaucoup retenues en raison de l'effet de grossissement et la précision de la segmentation diminue. U-net est maintenant utilisé dans le cadre de réseaux de neurones plus complexes comme base de la segmentation d'image.
Commençons par écrire la première partie de U-net (les trois couches supérieures gauche dans la figure ci-dessus). Dans l'implémentation suivante, la taille de l'image d'entrée est définie sur (256, 256).
from tensorflow import keras
from tensorflow.keras import layers
inputs = keras.Input(shape=(256, 256, 1), name="img")
x = layers.Conv2D(64, 3, activation="relu", padding="same", kernel_initializer='he_normal')(inputs)
block_1_output = layers.Conv2D(64, 3, activation="relu", padding="same")(x)
Une image de taille (256, 256, 1) est contournée avec un réseau neuronal convolutif (CNN) avec 64 canaux et une taille de filtre de 3. La fonction d'activation étant un modèle profond, nous utilisons ReLU. Conservez la sortie nommée block_1_output pour une connexion ultérieure. La couche suivante de U-net serait la suivante.
x = layers.MaxPooling2D(pool_size=(2, 2), padding="same")(block_1_output)
x = layers.Conv2D(128, 3, activation="relu", padding="same",kernel_initializer='he_normal')(x)
block_2_output = layers.Conv2D(128, 3, activation="relu", padding="same")(x)
Il reçoit "block_1_output", le grossit avec Max Pooling, augmente le nombre de canaux avec CNN et le convolve. Enregistrez "block_2_output" pour ignorer la connexion comme auparavant. Si vous écrivez U-net dans cette condition, l'image entière sera la suivante.
from tensorflow import keras
from tensorflow.keras import layers
inputs = keras.Input(shape=(256, 256, 1), name="img")
x = layers.Conv2D(64, 3, activation="relu", padding="same", kernel_initializer='he_normal')(inputs)
block_1_output = layers.Conv2D(64, 3, activation="relu", padding="same")(x)
x = layers.MaxPooling2D(pool_size=(2, 2), padding="same")(block_1_output)
x = layers.Conv2D(128, 3, activation="relu", padding="same",kernel_initializer='he_normal')(x)
block_2_output = layers.Conv2D(128, 3, activation="relu", padding="same")(x)
x = layers.MaxPooling2D(pool_size=(2, 2), padding="same")(block_2_output)
x = layers.Conv2D(256, 3, activation="relu", padding="same",kernel_initializer='he_normal')(x)
block_3_output = layers.Conv2D(256, 3, activation="relu", padding="same")(x)
x = layers.MaxPooling2D(pool_size=(2, 2), padding="same")(block_3_output)
x = layers.Conv2D(512, 3, activation="relu", padding="same",kernel_initializer='he_normal')(x)
block_4_output = layers.Conv2D(512, 3, activation="relu", padding="same")(x)
x = layers.Dropout(0.5)(block_4_output)
x = layers.MaxPooling2D(pool_size=(2, 2), padding="same")(x)
x = layers.Conv2D(1024, 3, activation="relu", padding="same",kernel_initializer='he_normal')(x)
block_5_output = layers.Conv2D(1024, 3, activation="relu", padding="same")(x)
x = layers.Dropout(0.5)(block_5_output)
x = layers.UpSampling2D(size=(2,2))(x)
x = layers.Conv2D(512, 3, activation="relu", padding="same",kernel_initializer='he_normal')(x)
x = tf.concat([x, block_4_output], axis=3)
x = layers.Conv2D(512, 3, activation="relu", padding="same",kernel_initializer='he_normal')(x)
block_6_output = layers.Conv2D(512, 3, activation="relu", padding="same")(x)
x = layers.UpSampling2D(size=(2,2))(block_6_output)
x = layers.Conv2D(256, 3, activation="relu", padding="same",kernel_initializer='he_normal')(x)
x = tf.concat([x, block_3_output], axis=3)
x = layers.Conv2D(256, 3, activation="relu", padding="same",kernel_initializer='he_normal')(x)
block_7_output = layers.Conv2D(256, 3, activation="relu", padding="same")(x)
x = layers.UpSampling2D(size=(2,2))(block_7_output)
x = layers.Conv2D(128, 3, activation="relu", padding="same",kernel_initializer='he_normal')(x)
x = tf.concat([x, block_2_output], axis=3)
x = layers.Conv2D(128, 3, activation="relu", padding="same",kernel_initializer='he_normal')(x)
block_8_output = layers.Conv2D(128, 3, activation="relu", padding="same")(x)
x = layers.UpSampling2D(size=(2,2))(block_8_output)
x = layers.Conv2D(64, 3, activation="relu", padding="same",kernel_initializer='he_normal')(x)
x = tf.concat([x, block_1_output], axis=3)
x = layers.Conv2D(64, 3, activation="relu", padding="same",kernel_initializer='he_normal')(x)
x = layers.Conv2D(64, 3, activation="relu", padding="same",kernel_initializer='he_normal')(x)
outputs = layers.Conv2D(1, 1, activation="sigmoid", padding="same")(x)
model = keras.Model(inputs, outputs, name="u-net")
model.compile(optimizer='adam',
loss='binary_crossentropy',
metrics=['accuracy'])
Cela fait longtemps que U-net n'est pas compliqué, mais ce que je fais, c'est répéter des choses similaires. Où vous connecter avec sauter la connexion, utilisez tf.concat pour vous connecter avec la partie canal comme axe. L'image du réseau U-net n'inclut pas la couche Dropout, mais quand je lis le texte de l'article,
Drop-out layers at the end of the contracting path perform further >implicit data augmentation
Comme c'est le cas, la couche Dropout est également incluse dans le réseau ci-dessus. De plus, en supposant une segmentation en deux classes, binary_crossentropy est utilisé avec le nombre de canaux de sortie défini sur 1. Vous pouvez également en apprendre davantage sur la sortie en utilisant sparse_categorical_crossentropy avec 2 canaux.
Vérifions si le modèle est fait correctement.
tf.keras.utils.plot_model(model)
Si vous sortez le modèle avec, ce sera comme suit. C'est très long, mais vous pouvez voir que la première demi-couche et la deuxième demi-couche sont correctement connectées par saut de connexion. Lorsque vous sortez le modèle avec model.summary (), cela ressemble à ceci.
model.summary()
Model: "u-net"
__________________________________________________________________________________________________
Layer (type) Output Shape Param # Connected to
==================================================================================================
img (InputLayer) [(None, 256, 256, 1) 0
__________________________________________________________________________________________________
conv2d (Conv2D) (None, 256, 256, 64) 640 img[0][0]
__________________________________________________________________________________________________
conv2d_1 (Conv2D) (None, 256, 256, 64) 36928 conv2d[0][0]
__________________________________________________________________________________________________
max_pooling2d (MaxPooling2D) (None, 128, 128, 64) 0 conv2d_1[0][0]
__________________________________________________________________________________________________
conv2d_2 (Conv2D) (None, 128, 128, 128 73856 max_pooling2d[0][0]
__________________________________________________________________________________________________
conv2d_3 (Conv2D) (None, 128, 128, 128 147584 conv2d_2[0][0]
__________________________________________________________________________________________________
max_pooling2d_1 (MaxPooling2D) (None, 64, 64, 128) 0 conv2d_3[0][0]
__________________________________________________________________________________________________
conv2d_4 (Conv2D) (None, 64, 64, 256) 295168 max_pooling2d_1[0][0]
__________________________________________________________________________________________________
conv2d_5 (Conv2D) (None, 64, 64, 256) 590080 conv2d_4[0][0]
__________________________________________________________________________________________________
max_pooling2d_2 (MaxPooling2D) (None, 32, 32, 256) 0 conv2d_5[0][0]
__________________________________________________________________________________________________
conv2d_6 (Conv2D) (None, 32, 32, 512) 1180160 max_pooling2d_2[0][0]
__________________________________________________________________________________________________
conv2d_7 (Conv2D) (None, 32, 32, 512) 2359808 conv2d_6[0][0]
__________________________________________________________________________________________________
dropout (Dropout) (None, 32, 32, 512) 0 conv2d_7[0][0]
__________________________________________________________________________________________________
max_pooling2d_3 (MaxPooling2D) (None, 16, 16, 512) 0 dropout[0][0]
__________________________________________________________________________________________________
conv2d_8 (Conv2D) (None, 16, 16, 1024) 4719616 max_pooling2d_3[0][0]
__________________________________________________________________________________________________
conv2d_9 (Conv2D) (None, 16, 16, 1024) 9438208 conv2d_8[0][0]
__________________________________________________________________________________________________
dropout_1 (Dropout) (None, 16, 16, 1024) 0 conv2d_9[0][0]
__________________________________________________________________________________________________
up_sampling2d (UpSampling2D) (None, 32, 32, 1024) 0 dropout_1[0][0]
__________________________________________________________________________________________________
conv2d_10 (Conv2D) (None, 32, 32, 512) 4719104 up_sampling2d[0][0]
__________________________________________________________________________________________________
tf_op_layer_concat (TensorFlowO [(None, 32, 32, 1024 0 conv2d_10[0][0]
conv2d_7[0][0]
__________________________________________________________________________________________________
conv2d_11 (Conv2D) (None, 32, 32, 512) 4719104 tf_op_layer_concat[0][0]
__________________________________________________________________________________________________
conv2d_12 (Conv2D) (None, 32, 32, 512) 2359808 conv2d_11[0][0]
__________________________________________________________________________________________________
up_sampling2d_1 (UpSampling2D) (None, 64, 64, 512) 0 conv2d_12[0][0]
__________________________________________________________________________________________________
conv2d_13 (Conv2D) (None, 64, 64, 256) 1179904 up_sampling2d_1[0][0]
__________________________________________________________________________________________________
tf_op_layer_concat_1 (TensorFlo [(None, 64, 64, 512) 0 conv2d_13[0][0]
conv2d_5[0][0]
__________________________________________________________________________________________________
conv2d_14 (Conv2D) (None, 64, 64, 256) 1179904 tf_op_layer_concat_1[0][0]
__________________________________________________________________________________________________
conv2d_15 (Conv2D) (None, 64, 64, 256) 590080 conv2d_14[0][0]
__________________________________________________________________________________________________
up_sampling2d_2 (UpSampling2D) (None, 128, 128, 256 0 conv2d_15[0][0]
__________________________________________________________________________________________________
conv2d_16 (Conv2D) (None, 128, 128, 128 295040 up_sampling2d_2[0][0]
__________________________________________________________________________________________________
tf_op_layer_concat_2 (TensorFlo [(None, 128, 128, 25 0 conv2d_16[0][0]
conv2d_3[0][0]
__________________________________________________________________________________________________
conv2d_17 (Conv2D) (None, 128, 128, 128 295040 tf_op_layer_concat_2[0][0]
__________________________________________________________________________________________________
conv2d_18 (Conv2D) (None, 128, 128, 128 147584 conv2d_17[0][0]
__________________________________________________________________________________________________
up_sampling2d_3 (UpSampling2D) (None, 256, 256, 128 0 conv2d_18[0][0]
__________________________________________________________________________________________________
conv2d_19 (Conv2D) (None, 256, 256, 64) 73792 up_sampling2d_3[0][0]
__________________________________________________________________________________________________
tf_op_layer_concat_3 (TensorFlo [(None, 256, 256, 12 0 conv2d_19[0][0]
conv2d_1[0][0]
__________________________________________________________________________________________________
conv2d_20 (Conv2D) (None, 256, 256, 64) 73792 tf_op_layer_concat_3[0][0]
__________________________________________________________________________________________________
conv2d_21 (Conv2D) (None, 256, 256, 64) 36928 conv2d_20[0][0]
__________________________________________________________________________________________________
conv2d_22 (Conv2D) (None, 256, 256, 1) 65 conv2d_21[0][0]
==================================================================================================
Total params: 34,512,193
Trainable params: 34,512,193
Non-trainable params: 0
__________________________________________________________________________________________________
Le nombre total de paramètres est de 34 512 193!, Ce qui est un nombre considérable.
Veuillez vous référer à Article précédent pour la préparation des données d'entrée telles que le remplissage de données. Découpez les données d'image de ISBI challenge 2012 (Segmentation of neuronal structures in EM stacks) en patchs de (256, 256) et effectuez une augmentation des données avec ImageDataGenerator. Il y a.
Maintenant, apprenons comme suit. my_generator est un générateur qui transmet les données d'image d'entraînement et my_val_gen est un générateur qui transmet les données d'image de vérification.
EPOCHS = 200
STEPS_PER_EPOCH = 300
VALIDATION_STEPS = STEPS_PER_EPOCH//10
model_history = model.fit(my_generator, epochs=EPOCHS,
steps_per_epoch=STEPS_PER_EPOCH,
validation_steps=VALIDATION_STEPS,
validation_data=my_val_gen)
L'état d'apprentissage est le suivant. L'axe horizontal est le nombre d'EPOCHS et l'axe vertical est la précision ou la perte. La perte d'entraînement et la précision de l'entraînement diminuent de manière monotone, ce qui indique que l'apprentissage progresse. La précision de la validation est également supérieure à 90% et il semble y avoir des performances prédictives. Si vous regardez de près, la précision / perte de validation est le maximum / minimum par 20 à 30 étapes. Cela suggère qu'il est surajusté à mesure que le nombre d'EPOCHS augmente encore.
Regardons la segmentation des données de test à l'aide d'un modèle par numéro EPOCHS 30.
def create_mask(pred_img):
pred_img = tf.math.greater(pred_img, 0.5)
return pred_img[0]
plt.figure(dpi=150)
plt.subplot(1, 2, 1)
plt.imshow(test_image[:256, :256], cmap="gray")
plt.subplot(1, 2, 2)
test_image = test_image[:256,:256,tf.newaxis]
test_image = np.expand_dims(test_image, axis=0)
pred_img = model.predict(test_image)
masked_img = create_mask(pred_img)
plt.imshow(masked_img[:,:,0])
En revanche, la segmentation des données de formation était la suivante. Vous pouvez voir que la segmentation fonctionne bien pour les données d'entraînement et les données de test. Cependant, par rapport aux données d'apprentissage, y a-t-il du bruit dans les résultats de segmentation des données de test? Je pense. À cet égard, il peut être nécessaire de concevoir un peu plus sur le modèle et la méthode d'augmentation des données.
Nous avons introduit l'implémentation en utilisant tensorflow pour la segmentation d'image à l'aide de U-net. J'ai implémenté le modèle U-net du papier original tel quel, mais il existe différentes techniques qui peuvent être utilisées telles que la normalisation par lots, donc j'écrirai à nouveau une introduction si j'ai une chance.
Recommended Posts