Dieser Artikel ist eine Fortsetzung des Artikels, der mit SegNet [^ 2] für CaDIS semantisch segmentiert wurde: ein Katarakt-Datensatz [^ 1], der [zuvor] durchgeführt wurde (https://qiita.com/burokoron/items/c730e607607c925c6fd1). ist. Dieses Mal werden wir die Leistung von U-Net [^ 3] mit einer in das Netzwerk von [previous] integrierten Sprungstruktur vergleichen (https://qiita.com/burokoron/items/c730e607607c925c6fd1).
Weitere Informationen finden Sie unter Letztes Mal.
Dieses Mal werden wir U-Net [^ 3] mit einer Sprungstruktur in SegNet [^ 2] von previous implementieren und die Leistung vergleichen. U-Net verbindet den Ausgang vor dem Encoder Max Pooling mit dem Ausgang nach dem Up Sampling des Decoders und gibt ihn in die Convolution-Schicht im Netzwerk ein, wie in der folgenden Abbildung gezeigt. Auf diese Weise können verlorene Informationen wiederhergestellt werden, indem das Bild mit Max Pooling komprimiert wird.
Dieses Mal werden wir nur die Sprungstruktur in die Netzwerkstruktur von [vorherige] einführen (https://qiita.com/burokoron/items/c730e607607c925c6fd1). Verzweigen Sie zunächst das Netzwerk wie folgt.
x = Conv2D(32, (3, 3), padding='same', kernel_initializer='he_normal')(inputs)
x = BatchNormalization()(x)
x1 = Activation('relu')(x)
x = MaxPool2D(pool_size=(2, 2))(x1)
Verzweigen Sie die Ausgabe, indem Sie "x1" nicht aktualisieren. Verketten Sie anschließend die verzweigten Ausgaben wie folgt.
x = UpSampling2D(size=(2, 2))(x)
x = concatenate([x, x1], axis=-1)
x = Conv2D(64, (3, 3), strides=(1, 1), padding='same', kernel_initializer='he_normal')(x)
Sie können Eingangstensoren mit concatenate ()
verketten. Dieses Mal kombinieren wir "x1 = (Höhe, Breite, ch1)" und "x = (Höhe, Breite, ch2)", um den Ausgangstensor von "(Höhe, Breite, ch1 + ch2)" zu erhalten. Das Netzwerk, auf das dies in jedem Max Pooling und Up Sampling angewendet wird, ist unten dargestellt.
from tensorflow.keras.models import Model
from tensorflow.keras.layers import Input, MaxPool2D, UpSampling2D, concatenate
from tensorflow.keras.layers import Conv2D, BatchNormalization, Activation
# U-Net(8 Schichten Encoder, 8 Schichten Decoder)Bauen
def cnn(input_shape, classes):
#Die Eingabebildgröße muss ein Vielfaches von 32 sein
assert input_shape[0]%32 == 0, 'Input size must be a multiple of 32.'
assert input_shape[1]%32 == 0, 'Input size must be a multiple of 32.'
#Encoder
##Eingabeebene
inputs = Input(shape=(input_shape[0], input_shape[1], 3))
##1. Schicht
x = Conv2D(32, (3, 3), padding='same', kernel_initializer='he_normal')(inputs)
x = BatchNormalization()(x)
x1 = Activation('relu')(x)
x = MaxPool2D(pool_size=(2, 2))(x1)
##2. Schicht
x = Conv2D(64, (3, 3), strides=(1, 1), padding='same', kernel_initializer='he_normal')(x)
x = BatchNormalization()(x)
x2 = Activation('relu')(x)
x = MaxPool2D(pool_size=(2, 2))(x2)
##3. Schicht
x = Conv2D(128, (3, 3), strides=(1, 1), padding='same', kernel_initializer='he_normal')(x)
x = BatchNormalization()(x)
x3 = Activation('relu')(x)
x = MaxPool2D(pool_size=(2, 2))(x3)
##4. Schicht
x = Conv2D(256, (3, 3), strides=(1, 1), padding='same', kernel_initializer='he_normal')(x)
x = BatchNormalization()(x)
x4 = Activation('relu')(x)
x = MaxPool2D(pool_size=(2, 2))(x4)
##5. und 6. Schicht
x = Conv2D(512, (3, 3), strides=(1, 1), padding='same', kernel_initializer='he_normal')(x)
x = BatchNormalization()(x)
x = Activation('relu')(x)
x = Conv2D(512, (3, 3), strides=(1, 1), padding='same', kernel_initializer='he_normal')(x)
x = BatchNormalization()(x)
x5 = Activation('relu')(x)
x = MaxPool2D(pool_size=(2, 2))(x5)
##7. und 8. Schicht
x = Conv2D(1024, (3, 3), strides=(1, 1), padding='same', kernel_initializer='he_normal')(x)
x = BatchNormalization()(x)
x = Activation('relu')(x)
x = Conv2D(1024, (3, 3), strides=(1, 1), padding='same', kernel_initializer='he_normal')(x)
x = BatchNormalization()(x)
x = Activation('relu')(x)
#Decoder
##1. Schicht
x = Conv2D(1024, (3, 3), strides=(1, 1), padding='same', kernel_initializer='he_normal')(x)
x = BatchNormalization()(x)
x = Activation('relu')(x)
##2. und 3. Schicht
x = UpSampling2D(size=(2, 2))(x)
x = concatenate([x, x5], axis=-1)
x = Conv2D(512, (3, 3), strides=(1, 1), padding='same', kernel_initializer='he_normal')(x)
x = BatchNormalization()(x)
x = Activation('relu')(x)
x = Conv2D(512, (3, 3), strides=(1, 1), padding='same', kernel_initializer='he_normal')(x)
x = BatchNormalization()(x)
x = Activation('relu')(x)
##4. Schicht
x = UpSampling2D(size=(2, 2))(x)
x = concatenate([x, x4], axis=-1)
x = Conv2D(256, (3, 3), strides=(1, 1), padding='same', kernel_initializer='he_normal')(x)
x = BatchNormalization()(x)
x = Activation('relu')(x)
##5. Schicht
x = UpSampling2D(size=(2, 2))(x)
x = concatenate([x, x3], axis=-1)
x = Conv2D(128, (3, 3), strides=(1, 1), padding='same', kernel_initializer='he_normal')(x)
x = BatchNormalization()(x)
x = Activation('relu')(x)
##6. Schicht
x = UpSampling2D(size=(2, 2))(x)
x = concatenate([x, x2], axis=-1)
x = Conv2D(64, (3, 3), strides=(1, 1), padding='same', kernel_initializer='he_normal')(x)
x = BatchNormalization()(x)
x = Activation('relu')(x)
##7. und 8. Schicht
x = UpSampling2D(size=(2, 2))(x)
x = concatenate([x, x1], axis=-1)
x = Conv2D(64, (3, 3), strides=(1, 1), padding='same', kernel_initializer='he_normal')(x)
x = Conv2D(classes, (1, 1), strides=(1, 1), padding='same', kernel_initializer='he_normal')(x)
outputs = Activation('softmax')(x)
return Model(inputs=inputs, outputs=outputs)
#Netzwerkaufbau
model = cnn(image_size, classes)
Darüber hinaus denke ich, dass die Beschreibung des Netzwerkteils aufgrund der Vertiefung in der Zukunft überflüssig wird, sodass ich den Encoder "Conv + BN + Relu + MaxPool" und den Decoder "Conv + BN + Relu + Upsampl" funktionsfähig machen werde. Das Folgende ist eine leicht erfrischende Version der Funktion.
import dataclasses
from tensorflow.keras.models import Model
from tensorflow.keras.layers import Input, MaxPool2D, UpSampling2D, concatenate
from tensorflow.keras.layers import Conv2D, BatchNormalization, Activation
# U-Net(8 Schichten Encoder, 8 Schichten Decoder)Bauen
@dataclasses.dataclass
class CNN:
input_shape: tuple #Bildgröße eingeben
classes: int #Anzahl der Klassifizierungsklassen
def __post_init__(self):
#Die Eingabebildgröße muss ein Vielfaches von 32 sein
assert self.input_shape[0]%32 == 0, 'Input size must be a multiple of 32.'
assert self.input_shape[1]%32 == 0, 'Input size must be a multiple of 32.'
#Encoderblock
@staticmethod
def encoder(x, blocks, filters, pooling):
for i in range(blocks):
x = Conv2D(filters, (3, 3), padding='same', kernel_initializer='he_normal')(x)
x = BatchNormalization()(x)
x = Activation('relu')(x)
if pooling:
return MaxPool2D(pool_size=(2, 2))(x), x
else:
return x
#Decoderblock
@staticmethod
def decoder(x1, x2, blocks, filters):
x = UpSampling2D(size=(2, 2))(x1)
x = concatenate([x, x2], axis=-1)
for i in range(blocks):
x = Conv2D(filters, (3, 3), padding='same', kernel_initializer='he_normal')(x)
x = BatchNormalization()(x)
x = Activation('relu')(x)
return x
def create(self):
#Encoder
inputs = Input(shape=(self.input_shape[0], self.input_shape[1], 3)) #Eingabeebene
x, x1 = self.encoder(inputs, blocks=1, filters=32, pooling=True) #1. Schicht
x, x2 = self.encoder(x, blocks=1, filters=64, pooling=True) #2. Schicht
x, x3 = self.encoder(x, blocks=1, filters=128, pooling=True) #3. Schicht
x, x4 = self.encoder(x, blocks=1, filters=256, pooling=True) #4. Schicht
x, x5 = self.encoder(x, blocks=2, filters=512, pooling=True) #5. und 6. Schicht
x = self.encoder(x, blocks=2, filters=1024, pooling=False) #7. und 8. Schicht
#Decoder
x = self.encoder(x, blocks=1, filters=1024, pooling=False) #1. Schicht
x = self.decoder(x, x5, blocks=2, filters=512) #2. und 3. Schicht
x = self.decoder(x, x4, blocks=1, filters=256) #4. Schicht
x = self.decoder(x, x3, blocks=1, filters=128) #5. Schicht
x = self.decoder(x, x2, blocks=1, filters=64) #6. Schicht
##7. und 8. Schicht
x = UpSampling2D(size=(2, 2))(x)
x = concatenate([x, x1], axis=-1)
x = Conv2D(64, (3, 3), strides=(1, 1), padding='same', kernel_initializer='he_normal')(x)
x = Conv2D(self.classes, (1, 1), strides=(1, 1), padding='same', kernel_initializer='he_normal')(x)
outputs = Activation('softmax')(x)
return Model(inputs=inputs, outputs=outputs)
#Netzwerkaufbau
model = CNN(input_shape=image_size, classes=classes).create()
Die Lernmethode und die Parameter sind dieselben wie bei Letztes Mal. Die Lernergebnisse sind wie folgt.
Die Bewertung wird durch die durchschnittliche IoU für jede Klasse und die mittlere IoU durchgeführt, die der Durchschnitt von ihnen ist, wie im vorherigen.
Nachfolgend sind die Bewertungsergebnisse aufgeführt. Aus der Tabelle können wir ersehen, dass der [vorherige] Wert (https://qiita.com/burokoron/items/c730e607607c925c6fd1) für viele Klassen, die leicht ableitbar sind, 0% betrug. Zusätzlich betrug das mittlere Io U 18,5%. Letztes Mal SegNet [^ 2] betrug 15,0%, sodass wir die Genauigkeit um 3,5% verbessern konnten.
Index | Class | average IoU(SegNet)[%] | average IoU(U-Net)[%] |
---|---|---|---|
0 | Pupil | 85.3 | 86.5 |
1 | Surgical Tape | 53.3 | 57.1 |
2 | Hand | 6.57 | 6.96 |
3 | Eye Retractors | 21.9 | 53.6 |
4 | Iris | 74.4 | 76.0 |
5 | Eyelid | 0 | 0 |
6 | Skin | 49.7 | 48.4 |
7 | Cornea | 88.0 | 88.5 |
8 | Hydro. Cannula | 0 | 31.8 |
9 | Visco. Cannula | 0 | 4.36 |
10 | Cap. Cystotome | 0 | 3.71 |
11 | Rycroft Cannula | 0 | 4.37 |
12 | Bonn Forceps | 3.58 | 7.94 |
13 | Primary Knife | 5.35 | 10.3 |
14 | Phaco. Handpiece | 0.0781 | 12.3 |
15 | Lens Injector | 16.4 | 15.8 |
16 | A/I Handpiece | 16.4 | 20.5 |
17 | Secondary Knife | 6.08 | 11.8 |
18 | Micromanipulator | 0 | 8.99 |
19 | A/I Handpiece Handle | 6.49 | 8.16 |
20 | Cap. Forceps | 0 | 0.337 |
21 | Rycroft Cannula Handle | 0 | 0.00863 |
22 | Phaco. Handpiece Handle | 0 | 4.26 |
23 | Cap. Cystotome Handle | 0 | 0.407 |
24 | Secondary Knife Handle | 2.49 | 3.82 |
25 | Lens Injector Handle | 0 | 0 |
26 | Water Sprayer | ─ | ─ |
27 | Suture Needle | 0 | 0 |
28 | Needle Holder | ─ | ─ |
29 | Charleux Cannula | 0 | 0 |
30 | Vannas Scissors | ─ | ─ |
31 | Primary Knife Handle | 0 | 0 |
32 | Viter. Handpiece | 0 | 0 |
33 | Mendez Ring | ─ | ─ |
34 | Biomarker | ─ | ─ |
35 | Marker | ─ | ─ |
In diesem Artikel haben wir U-Net [^ 3] implementiert, das eine Sprungstruktur in SegNet [^ 2] von [previous] enthält (https://qiita.com/burokoron/items/c730e607607c925c6fd1). Für CaDIS: einen Katarakt-Datensatz [^ 1] haben wir die Leistung mit dem mittleren IoU verglichen und eine Verbesserung der Genauigkeit um 3,5% bestätigt. Die höchste Genauigkeit des Papiers [^ 1] beträgt 34,16%, bis zu 52,66% von PSPNet. Basierend auf diesem Ergebnis werde ich weiterhin die neuesten Methoden wie Netzwerkstruktur und Datenerweiterungsmethode einbeziehen und die gleiche oder eine bessere Leistung anstreben.