Die Umgebung verwendet Python3.7 und Tensorflow 2.1.1 mit den folgenden Inhalten.
VOC2012 Schauen wir uns ein Beispiel für die Bildsegmentierung in einem Datensatz an (siehe unten). In diesem Beispiel werden die Pixel im Bild als Fahrrad, Fahrer oder Hintergrund klassifiziert. Diese Art der Klassifizierung von Bildern ** in Pixeleinheiten ** wird als Bildsegmentierung (oder semantische Segmentierung) bezeichnet.
Wenn Sie mit dem Studium neuronaler Netze beginnen, implementieren Sie häufig die handschriftliche Zeichenerkennung von MNIST in Tutorials. In diesem Fall wird das Bild eingegeben und die Wahrscheinlichkeit, dass das Bild eine Zahl von 0 bis 9 ist, wird ausgegeben, so dass die letzte Ausgabeschicht eindimensionale 10 Knoten sind.
Andererseits möchten wir im Fall der Bildsegmentierung jedes Pixel des Bildes klassifizieren, damit die endgültige Ausgabeebene die Abmessungen hat (vertikale Größe des Bildes, horizontale Größe des Bildes, Anzahl der Klassen). U-net, das ich dieses Mal vorstellen werde, kann eine hervorragende Genauigkeit für solche Bildsegmentierungsprobleme liefern.
U-net U-net ist ein Modell mit CNN und Sprungverbindung, das in der Veröffentlichung "U-Net: Faltungsnetzwerke für die Segmentierung biomedizinischer Bilder" veröffentlicht wurde. (Abbildung unten).
Die Grundidee von U-net ist ・ Merkmalsextraktion beim Aufrauen des Bildes mit der Falt- und Poolebene
Schreiben wir zunächst den ersten Teil von U-net (die oberen drei Ebenen in der obigen Abbildung). In der folgenden Implementierung wird die Größe des Eingabebildes auf (256, 256) eingestellt.
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)
Ein Bild der Größe (256, 256, 1) wird mit einem Convolutional Neural Network (CNN) mit 64 Kanälen und einer Filtergröße von 3 gefaltet. Da die Aktivierungsfunktion ein tiefes Modell ist, verwenden wir ReLU. Behalten Sie die Ausgabe mit dem Namen block_1_output für eine spätere Sprungverbindung bei. Die nächste Schicht von U-net wäre wie folgt.
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)
Es empfängt "block_1_output", vergröbert es mit Max Pooling, erhöht die Anzahl der Kanäle mit CNN und faltet es zusammen. Speichern Sie "block_2_output" für das Überspringen der Verbindung wie zuvor. Wenn Sie in diesem Zustand U-net schreiben, sieht das ganze Bild wie folgt aus.
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'])
Es ist lange her, dass U-Net kompliziert ist, aber ich wiederhole ähnliche Dinge. Verwenden Sie tf.concat, um eine Verbindung mit dem Kanalteil als Achse herzustellen. Das Bild des U-Net-Netzwerks enthält nicht die Dropout-Ebene, aber wenn ich den Text des Papiers lese,
Drop-out layers at the end of the contracting path perform further >implicit data augmentation
Da dies der Fall ist, ist die Dropout-Schicht auch im obigen Netzwerk enthalten. Unter der Annahme einer Segmentierung in zwei Klassen wird binary_crossentropy verwendet, wobei die Anzahl der Ausgangskanäle auf 1 gesetzt ist. Sie können sich auch über die Ausgabe informieren, indem Sie die Anzahl der Kanäle auf 2 setzen und sparse_categorical_crossentropy verwenden.
Lassen Sie uns überprüfen, ob das Modell korrekt erstellt wurde.
tf.keras.utils.plot_model(model)
Wenn Sie das Modell mit ausgeben, sieht es wie folgt aus. Es ist sehr lang, aber Sie können sehen, dass die erste halbe Schicht und die zweite halbe Schicht durch Überspringen der Verbindung richtig verbunden sind. Wenn Sie das Modell mit model.summary () ausgeben, sieht es so aus.
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
__________________________________________________________________________________________________
Die Gesamtzahl der Parameter beträgt 34.512.193!, Was eine ziemliche Zahl ist.
Informationen zur Vorbereitung von Eingabedaten, z. B. zum Auffüllen von Daten, finden Sie in Vorheriger Artikel. Schneiden Sie die Bilddaten von ISBI Challenge 2012 (Segmentierung neuronaler Strukturen in EM-Stapeln) in Patches von (256, 256) und führen Sie eine Datenerweiterung mit ImageDataGenerator durch. Es gibt.
Lassen Sie uns nun wie folgt lernen. my_generator ist ein Generator, der Trainingsbilddaten übergibt, und my_val_gen ist ein Generator, der Verifizierungsbilddaten übergibt.
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)
Der Lernzustand ist wie folgt. Die horizontale Achse ist die Anzahl der EPOCHS und die vertikale Achse ist die Genauigkeit oder der Verlust. Der Trainingsverlust und die Trainingsgenauigkeit nehmen monoton ab, was darauf hinweist, dass das Lernen voranschreitet. Die Validierungsgenauigkeit liegt ebenfalls bei über 90%, und es scheint eine prädiktive Leistung zu geben. Wenn Sie genau hinschauen, ist die Validierungsgenauigkeit / der Validierungsverlust das Maximum / Minimum pro 20 bis 30 Schritte. Dies deutet darauf hin, dass es mit zunehmender Anzahl von EPOCHS überanpasst.
Betrachten wir die Segmentierung von Testdaten anhand eines Modells gemäß EPOCHS Nummer 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])
Andererseits war die Segmentierung der Trainingsdaten wie folgt. Sie können sehen, dass die Segmentierung sowohl für die Trainingsdaten als auch für die Testdaten gut funktioniert. Gibt es jedoch im Vergleich zu den Trainingsdaten ein Rauschen in den Segmentierungsergebnissen für die Testdaten? Ich denke. In diesem Zusammenhang kann es erforderlich sein, etwas mehr über das Modell und die Datenerweiterungsmethode zu erfahren.
Wir haben die Implementierung mit Tensorflow für die Bildsegmentierung mit U-Net eingeführt. Ich habe das U-Net-Modell des Originalpapiers so implementiert, wie es ist, aber es gibt verschiedene Techniken, die verwendet werden können, wie z. B. die Chargennormalisierung. Wenn ich eine Chance habe, werde ich erneut eine Einführung schreiben.
Recommended Posts