tensorflow == 2.2.0 keras == 2.3.1 (Standardversion von Google Colab ab 202.6.10)
Sie finden den gesamten Code auf github. https://github.com/milky1210/Segnet Der Code im Artikel ist ein Auszug. Wenn Sie ihn also tatsächlich bearbeiten möchten, laden Sie den Code herunter.
Bei dem Problem, die Beschriftung dessen, was für jedes Pixel des Bildes reflektiert wird, als SEMANTIC-Segmentierung durch Deep Learning abzuleiten, ist es genau, die Feature-Map wiederherzustellen, deren Auflösung durch Pooling usw. auf die ursprüngliche Dimension verringert wurde. Wir schlagen ein Modell vor, das der Grenzlinie zugeordnet ist.
SegNet führt UpSampling durch, nachdem die Auflösung in der Faltungsschicht und der Pooling-Schicht wie bei einem normalen FCN verringert wurde. Beim Erhöhen der Auflösung wird jedoch eine als Pooling-Index bezeichnete Technik verwendet, um zu verhindern, dass die Grenze unscharf wird. Es gibt. Hier erben Encode und Decode die Form des VGG16-Modells (ein Modell, das für seine Bildklassifizierung bekannt ist). Pooling indices Denken Sie, wie in dieser Abbildung gezeigt, daran, wo sich Max befand, als Max Pooling durchgeführt wurde, und übertragen Sie jede Feature-Map während des UpSampling an diese Position.
Es handelt sich um einen Datensatz, der Probleme wie Bilderkennung, Bilderkennung und Segmentierung unterstützt, die auch in den Papieren von SegNet zur Leistungsüberprüfung verwendet werden. Sie können es von [hier] herunterladen (http://host.robots.ox.ac.uk/pascal/VOC/voc2012/).
Beim Herunterladen sind JPEGImages / und SegmentationObject / in VOCdevkit / VOC2012 / enthalten, und Training und Überprüfung werden mit JPEGImage als Eingabebild und SegmentationObject als Ausgabebild durchgeführt.
JPEGImages / ~ .jpg und Segmentation Object / ~ .png werden in jedem Verzeichnis unterstützt. 22 Klassen werden klassifiziert, einschließlich Hintergrund und Grenzen.
In diesem Artikel werden nur die Definition des Modells, die Definition der Verlustfunktion und das Training behandelt. Darüber hinaus werden Schulungen und Überprüfungen mit einer Auflösung von 64 x 64 durchgeführt.
Zunächst wird SegNet (Encoder-Decoder) ohne Pooling-Index als Vergleichsziel wie folgt als VGG16 modelliert.
def build_FCN():
ffc = 32
inputs = layers.Input(shape=(64,64,3))
for i in range(2):
x = layers.Conv2D(ffc,kernel_size=3,padding="same")(inputs)
x = layers.BatchNormalization()(x)
x = layers.ReLU()(x)
x = layers.MaxPooling2D((2,2))(x)
for i in range(2):
x = layers.Conv2D(ffc*2,kernel_size=3,padding="same")(x)
x = layers.BatchNormalization()(x)
x = layers.ReLU()(x)
x = layers.MaxPooling2D((2,2))(x)
for i in range(3):
x = layers.Conv2D(ffc*4,kernel_size=3,padding="same")(x)
x = layers.BatchNormalization()(x)
x = layers.ReLU()(x)
x = layers.MaxPooling2D((2,2))(x)
for i in range(3):
x = layers.Conv2D(ffc*8,kernel_size=3,padding="same")(x)
x = layers.BatchNormalization()(x)
x = layers.ReLU()(x)
x = layers.MaxPooling2D((2,2))(x)
for i in range(3):
x = layers.Conv2D(ffc*8,kernel_size=3,padding="same")(x)
x = layers.BatchNormalization()(x)
x = layers.ReLU()(x)
x = layers.UpSampling2D((2,2))(x)
for i in range(3):
x = layers.Conv2D(ffc*4,kernel_size=3,padding="same")(x)
x = layers.BatchNormalization()(x)
x = layers.ReLU()(x)
x = layers.UpSampling2D((2,2))(x)
for i in range(3):
x = layers.Conv2D(ffc*2,kernel_size=3,padding="same")(x)
x = layers.BatchNormalization()(x)
x = layers.ReLU()(x)
x = layers.UpSampling2D((2,2))(x)
for i in range(2):
x = layers.Conv2D(ffc*2,kernel_size=3,padding="same")(x)
x = layers.BatchNormalization()(x)
x = layers.ReLU()(x)
x = layers.UpSampling2D((2,2))(x)
for i in range(2):
x = layers.Conv2D(ffc,kernel_size=3,padding="same")(x)
x = layers.BatchNormalization()(x)
x = layers.ReLU()(x)
x = layers.Conv2D(22,kernel_size=3,padding="same",activation="softmax")(x)
return models.Model(inputs,x)
Wenn das vgg16 nachgeahmt wird, hat es eine solche Struktur und wird zu einem Netzwerk mit 24 Faltungsschichten. Beachten Sie, dass MaxPooling2D verwendet wird, um das Bild zu verkleinern, und UpSampling2D, um das Bild zu vergrößern. Schauen wir uns als nächstes den Unterschied zwischen Segnet und diesem Modell an. Zunächst enthält Segnet die Informationen, die ArgMaxPooling2D entsprechen, in dieser Ebene wie folgt, bevor MaxPooling2D ausgeführt wird. Diese Funktion ist nicht in Keras und verwendet die im Tensorflow. Daher muss der ursprüngliche Keras-Layer erstellt werden. Wenn Sie die Funktion wie folgt definieren, handelt es sich um eine Ebene, die auf Keras ausgeführt wird.
class MaxPoolingWithArgmax2D(Layer):
def __init__(self):
super(MaxPoolingWithArgmax2D,self).__init__()
def call(self,inputs):
output,argmax = tf.nn.max_pool_with_argmax(inputs,ksize=[1,2,2,1],strides=[1,2,2,1],padding='SAME')
argmax = K.cast(argmax,K.floatx())
return [output,argmax]
def compute_output_shape(self,input_shape):
ratio = (1,2,2,1)
output_shape = [dim//ratio[idx] if dim is not None else None for idx, dim in enumerate(input_shape)]
output_shape = tuple(output_shape)
return [output_shape,output_shape]
Definieren Sie eine Ebene, um beim nächsten Up Sampling an den Ort zurückzukehren, an dem argmax war (dies ist ziemlich lang).
class MaxUnpooling2D(Layer):
def __init__(self):
super(MaxUnpooling2D,self).__init__()
def call(self,inputs,output_shape = None):
updates, mask = inputs[0],inputs[1]
with tf.variable_scope(self.name):
mask = K.cast(mask, 'int32')
input_shape = tf.shape(updates, out_type='int32')
# calculation new shape
if output_shape is None:
output_shape = (input_shape[0],input_shape[1]*2,input_shape[2]*2,input_shape[3])
self.output_shape1 = output_shape
# calculation indices for batch, height, width and feature maps
one_like_mask = K.ones_like(mask, dtype='int32')
batch_shape = K.concatenate([[input_shape[0]], [1 ], [1], [1]],axis=0)
batch_range = K.reshape(tf.range(output_shape[0], dtype='int32'),shape=batch_shape)
b = one_like_mask * batch_range
y = mask // (output_shape[2] * output_shape[3])
x = (mask // output_shape[3]) % output_shape[2]
feature_range = tf.range(output_shape[3], dtype='int32')
f = one_like_mask * feature_range
# transpose indices & reshape update values to one dimension
updates_size = tf.size(updates)
indices = K.transpose(K.reshape(
K.stack([b, y, x, f]),
[4, updates_size]))
values = K.reshape(updates, [updates_size])
ret = tf.scatter_nd(indices, values, output_shape)
return ret
def compute_output_shape(self,input_shape):
shape = input_shape[1]
return (shape[0],shape[1]*2,shape[2]*2,shape[3])
Wenn Segnet unter Verwendung der durch diese definierten Ebene definiert wird, ist dies wie folgt.
def build_Segnet():
ffc = 32
inputs = layers.Input(shape=(64,64,3))
for i in range(2):
x = layers.Conv2D(ffc,kernel_size=3,padding="same")(inputs)
x = layers.BatchNormalization()(x)
x = layers.ReLU()(x)
x,x1 = MaxPoolingWithArgmax2D()(x)
for i in range(2):
x = layers.Conv2D(ffc*2,kernel_size=3,padding="same")(x)
x = layers.BatchNormalization()(x)
x = layers.ReLU()(x)
x,x2 = MaxPoolingWithArgmax2D()(x)
for i in range(3):
x = layers.Conv2D(ffc*4,kernel_size=3,padding="same")(x)
x = layers.BatchNormalization()(x)
x = layers.ReLU()(x)
x,x3 = MaxPoolingWithArgmax2D()(x)
for i in range(3):
x = layers.Conv2D(ffc*8,kernel_size=3,padding="same")(x)
x = layers.BatchNormalization()(x)
x = layers.ReLU()(x)
x,x4 = MaxPoolingWithArgmax2D()(x)
for i in range(3):
x = layers.Conv2D(ffc*8,kernel_size=3,padding="same")(x)
x = layers.BatchNormalization()(x)
x = layers.ReLU()(x)
x = layers.Dropout(rate = 0.5)(x)
x = MaxUnpooling2D()([x,x4])
for i in range(3):
x = layers.Conv2D(ffc*4,kernel_size=3,padding="same")(x)
x = layers.BatchNormalization()(x)
x = layers.ReLU()(x)
x = MaxUnpooling2D()([x,x3])
for i in range(3):
x = layers.Conv2D(ffc*2,kernel_size=3,padding="same")(x)
x = layers.BatchNormalization()(x)
x = layers.ReLU()(x)
x = MaxUnpooling2D()([x,x2])
for i in range(2):
x = layers.Conv2D(ffc,kernel_size=3,padding="same")(x)
x = layers.BatchNormalization()(x)
x = layers.ReLU()(x)
x = MaxUnpooling2D()([x,x1])
for i in range(2):
x = layers.Conv2D(ffc,kernel_size=3,padding="same")(x)
x = layers.BatchNormalization()(x)
x = layers.ReLU()(x)
x = layers.Conv2D(22,kernel_size=3,padding="same",activation="softmax")(x)
return models.Model(inputs,x)
Diesmal verwendete die Verlustfunktion die Kreuzentropie jedes Pixels. Adam (lr = 0,001, beta_1 = 0,9, beta_2 = 0,999) wurde zur Optimierung verwendet.
Wir haben bestätigt, wie stark sich das Ergebnis je nach Vorhandensein oder Nichtvorhandensein von Würfelpools ändert. Der Trainingsverlust und der Durchschnitt der korrekten Antwortrate bei jedem Pixel wurden grafisch dargestellt. Erstens das Ergebnis des Modells ohne Pooling-Index
Die Verifizierungsdaten hatten eine korrekte Antwortrate von ca. 78%. Als nächstes werde ich die Ergebnisse von SegNet veröffentlichen.
Es war stabil mit einer korrekten Antwortrate von etwa 82%, und wir konnten das Verhalten gemäß dem Papier sehen.
Eingabe von links, kein Pooling-Index, alle Testdaten mit SegNet, GT
Es wurde festgestellt, dass das Halten des Pooling-Index die Genauigkeit signifikant verbessern kann.