[PYTHON] Comparaison des performances en incorporant une structure de saut dans SegNet (CaDIS: a Cataract Dataset)

1.Tout d'abord

Cet article est une suite de l'article qui a été segmenté sémantiquement à l'aide de SegNet [^ 2] pour CaDIS: un ensemble de données Cataract [^ 1] qui a été effectué précédemment. est. Cette fois, nous comparerons les performances d'U-Net [^ 3] avec une structure skip intégrée au réseau de previous.

Tout le code

2. Environnement

3. Ensemble de données et fractionnement des données

Veuillez vous référer à Dernière fois.

4. Construction et apprentissage de modèles

Cette fois, nous allons implémenter U-Net [^ 3] avec une structure de saut dans SegNet [^ 2] de précédent et comparer les performances. U-Net connecte la sortie avant le regroupement maximum de l'encodeur avec la sortie après l'échantillonnage ascendant du décodeur et l'entrent dans la couche de convolution du réseau comme indiqué dans la figure ci-dessous. Cela permet de récupérer des informations qui ont été perdues en compressant l'image avec Max Pooling.

image.png

Cette fois, nous n'introduirons que la structure de saut dans la structure de réseau de précédent. Tout d'abord, branchez le réseau comme suit.

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)

Branchement de la sortie en laissant x1 non mis à jour. Ensuite, concaténez les sorties branchées comme suit.

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)

Vous pouvez concaténer des tenseurs d'entrée en utilisant concatenate (). Cette fois, nous combinons x1 = (hauteur, largeur, ch1) et x = (hauteur, largeur, ch2) pour obtenir le tenseur de sortie de(hauteur, largeur, ch1 + ch2). Le réseau auquel cela est appliqué dans chaque regroupement maximum et échantillonnage ascendant est indiqué ci-dessous.

<détails>

Code U-Net </ summary>

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 couches d'encodeur, 8 couches de décodeur)Construire
def cnn(input_shape, classes):
    #La taille de l'image d'entrée doit être un multiple de 32
    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.'

    #encodeur
    ##Couche d'entrée
    inputs = Input(shape=(input_shape[0], input_shape[1], 3))

    ##1ère couche
    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ème couche
    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)

    ##3e couche
    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ème couche
    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ème et 6ème couches
    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ème et 8ème couches
    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)

    #Décodeur
    ##1ère couche
    x = Conv2D(1024, (3, 3), strides=(1, 1), padding='same', kernel_initializer='he_normal')(x)
    x = BatchNormalization()(x)
    x = Activation('relu')(x)

    ##2ème et 3ème couches
    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ème couche
    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ème couche
    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ème couche
    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ème et 8ème couches
    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)


#Construction de réseau
model = cnn(image_size, classes)

De plus, je pense que la description de la partie réseau deviendra redondante en raison de l'approfondissement à l'avenir, je vais donc rendre le codeur Conv + BN + Relu + MaxPool et le décodeur Conv + BN + Relu + Upsampl fonctionnels. Ce qui suit est une version légèrement rafraîchissante de la fonction.

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 couches d'encodeur, 8 couches de décodeur)Construire
@dataclasses.dataclass
class CNN:
    input_shape: tuple #Taille de l'image d'entrée
    classes: int #Nombre de classes de classification

    def __post_init__(self):
        #La taille de l'image d'entrée doit être un multiple de 32
        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.'


    #Bloc codeur
    @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


    #Bloc décodeur
    @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):
        #encodeur
        inputs = Input(shape=(self.input_shape[0], self.input_shape[1], 3)) #Couche d'entrée
        x, x1 = self.encoder(inputs, blocks=1, filters=32, pooling=True) #1ère couche
        x, x2 = self.encoder(x, blocks=1, filters=64, pooling=True) #2ème couche
        x, x3 = self.encoder(x, blocks=1, filters=128, pooling=True) #3e couche
        x, x4 = self.encoder(x, blocks=1, filters=256, pooling=True) #4ème couche
        x, x5 = self.encoder(x, blocks=2, filters=512, pooling=True) #5ème et 6ème couches
        x = self.encoder(x, blocks=2, filters=1024, pooling=False) #7ème et 8ème couches

        #Décodeur
        x = self.encoder(x, blocks=1, filters=1024, pooling=False) #1ère couche
        x = self.decoder(x, x5, blocks=2, filters=512) #2ème et 3ème couches
        x = self.decoder(x, x4, blocks=1, filters=256) #4ème couche
        x = self.decoder(x, x3, blocks=1, filters=128) #5ème couche
        x = self.decoder(x, x2, blocks=1, filters=64) #6ème couche
        ##7ème et 8ème couches
        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)


#Construction de réseau
model = CNN(input_shape=image_size, classes=classes).create()

La méthode d'apprentissage et les paramètres sont les mêmes que Dernière fois. Les résultats d'apprentissage sont les suivants. history.jpg

5. Évaluation

L'évaluation est effectuée par l'IoU moyen pour chaque classe et l'IoU moyen qui est la moyenne d'entre eux, comme dans le précédent.

Voici les résultats de l'évaluation. À partir du tableau, nous pouvons voir que le précédent était de 0% pour de nombreuses classes, qui sont légèrement inférables. De plus, Io U moyen était de 18,5%. Dernière fois SegNet [^ 2] était de 15,0%, nous avons donc pu améliorer la précision de 3,5%.

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

7. Résumé

Dans cet article, nous avons implémenté U-Net [^ 3] qui incorpore une structure de saut dans SegNet [^ 2] de précédent. Pour CaDIS: a Cataract Dataset [^ 1], nous avons comparé les performances à l'IoU moyenne et confirmé une amélioration de 3,5% de la précision. La précision la plus élevée du papier [^ 1] est de 34,16%, jusqu'à 52,66% de PSPNet. Sur la base de ce résultat, nous continuerons à intégrer les dernières méthodes telles que la structure du réseau et la méthode d'extension des données, et viserons des performances identiques ou meilleures.

Recommended Posts

Comparaison des performances en incorporant une structure de saut dans SegNet (CaDIS: a Cataract Dataset)
Segmentation d'image avec CaDIS: un ensemble de données sur la cataracte