Prima di concludere questo post già lungo, volevo evidenziare alcune delle altre funzionalità che abbiamo integrato nel modello e fornire alcuni esempi di codice di formazione per coloro che sono interessati a sviluppare il proprio modello di inpainting.
Abbandono di Montecarlo
A differenza dei tradizionali metodi bayesiani, non produciamo direttamente una stima dell’incertezza su base fisica utilizzando U-Net. Per avere un'idea approssimativa della confidenza e della stabilità del modello, abbiamo deciso di introdurre un dropout a livello di inferenza nel modello basato sul lavoro di Gal e Ghahramani, 2016, che ci consente di generare una distribuzione di previsioni dipinte per ciascun caso di test. Queste distribuzioni ci consentono di produrre intervalli di confidenza per ciascun pixel verniciato e di affinare ulteriormente le nostre stime per le regioni di cui il modello è più certo durante la verniciatura. Un esempio di ciò è mostrato di seguito in Fig. 17.
Solitamente utilizziamo N=50 iterazioni per caso e, come possiamo vedere sopra, le aree con la massima incertezza sono tipicamente i bordi e gli spazi vuoti delle nuvole, poiché il modello spesso ha allucinazioni quando posiziona queste caratteristiche.
Statistiche di formazione
La formazione del modello per questo progetto è stata completata su due set di hardware, tra cui un cluster di elaborazione GPU basato su Linux su Microsoft Azure e un desktop ad alte prestazioni con Windows 11 (ulteriori dettagli del sistema nella Tabella 1). Nel corso di 2 giorni è stata eseguita anche un'ampia scansione degli iperparametri bayesiani. Inoltre, viene applicata la normalizzazione batch insieme all'arresto anticipato (n=20), all'abbandono e alla regolarizzazione di L2 (regressione della cresta) per contribuire a mitigare l'overfitting durante il processo di addestramento. Il decadimento del tasso di apprendimento viene applicato anche a due epoche (450 e 475), consentendo al modello di stabilizzarsi più facilmente su minimi di perdita locali verso la fine della fase di addestramento. Tutte le esecuzioni di training e le scansioni degli iperparametri vengono salvate online utilizzando il file Opzione di archiviazione cloud Weights & Biasesper monitorare i tassi di apprendimento e la stabilità del modello nel tempo.
Codice di esempio
Un collegamento a GitHub è qui: https://github.com/frasertheking/blindzone_inpainting
Tuttavia, desidero fornire di seguito una panoramica dell'effettiva implementazione di 3Net+ (con profondità variabile) in Tensorflow per coloro che sono interessati a giocarci.
def conv_block(x, kernels, kernel_size=(3, 3), strides=(1, 1), padding='same', is_bn=True, is_relu=True, n=2, l2_reg=1e-4):
for _ in range(1, n+1):
x = k.layers.Conv2D(filters=kernels, kernel_size=kernel_size,
padding=padding, strides=strides,
kernel_regularizer=tf.keras.regularizers.l2(l2_reg),
kernel_initializer=k.initializers.he_normal(seed=42))(x)
if is_bn:
x = k.layers.BatchNormalization()(x)
if is_relu:
x = k.activations.relu(x)
return xdef unet3plus(input_shape, output_channels, config, depth=4, training=False, clm=False):
""" Prep """
interp = config('interpolation')
input_layer = k.layers.Input(shape=input_shape, name="input_layer")
xpre = preprocess(input_layer, output_channels)
""" Encoder """
encoders = ()
for i in range(depth+1):
if i == 0:
e = conv_block(xpre, config('filters')*(2**i), kernel_size=(config('kernel_size'), config('kernel_size')), l2_reg=config('l2_reg'))
else:
e = k.layers.MaxPool2D(pool_size=(2, 2))(encoders(i-1))
e = k.layers.Dropout(config('dropout'))(e, training=True)
e = conv_block(e, config('filters')*(2**i), kernel_size=(config('kernel_size'), config('kernel_size')), l2_reg=config('l2_reg'))
encoders.append(e)
""" Middle """
cat_channels = config('filters')
cat_blocks = depth+1
upsample_channels = cat_blocks * cat_channels
""" Decoder """
decoders = ()
for d in reversed(range(depth+1)):
if d == 0 :
continue
loc_dec = ()
decoder_pos = len(decoders)
for e in range(len(encoders)):
if d > e+1:
e_d = k.layers.MaxPool2D(pool_size=(2**(d-e-1), 2**(d-e-1)))(encoders(e))
e_d = k.layers.Dropout(config('dropout'))(e_d, training=True)
e_d = conv_block(e_d, cat_channels, kernel_size=(config('kernel_size'), config('kernel_size')), n=1, l2_reg=config('l2_reg'))
elif d == e+1:
e_d = conv_block(encoders(e), cat_channels, kernel_size=(config('kernel_size'), config('kernel_size')), n=1, l2_reg=config('l2_reg'))
elif e+1 == len(encoders):
e_d = k.layers.UpSampling2D(size=(2**(e+1-d), 2**(e+1-d)), interpolation=interp)(encoders(e))
e_d = k.layers.Dropout(config('dropout'))(e_d, training=True)
e_d = conv_block(e_d, cat_channels, kernel_size=(config('kernel_size'), config('kernel_size')), n=1, l2_reg=config('l2_reg'))
else:
e_d = k.layers.UpSampling2D(size=(2**(e+1-d), 2**(e+1-d)), interpolation=interp)(decoders(decoder_pos-1))
e_d = k.layers.Dropout(config('dropout'))(e_d, training=True)
e_d = conv_block(e_d, cat_channels, kernel_size=(config('kernel_size'), config('kernel_size')), n=1, l2_reg=config('l2_reg'))
decoder_pos -= 1
loc_dec.append(e_d)
de = k.layers.concatenate(loc_dec)
de = conv_block(de, upsample_channels, kernel_size=(config('kernel_size'), config('kernel_size')), n=1, l2_reg=config('l2_reg'))
decoders.append(de)
""" Final """
d1 = decoders(len(decoders)-1)
d1 = conv_block(d1, output_channels, kernel_size=(config('kernel_size'), config('kernel_size')), n=1, is_bn=False, is_relu=False, l2_reg=config('l2_reg'))
outputs = (d1)
""" Deep Supervision """
if training:
for i in reversed(range(len(decoders))):
if i == 0:
e = conv_block(encoders(len(encoders)-1), output_channels, kernel_size=(config('kernel_size'), config('kernel_size')), n=1, is_bn=False, is_relu=False, l2_reg=config('l2_reg'))
e = k.layers.UpSampling2D(size=(2**(len(decoders)-i), 2**(len(decoders)-i)), interpolation=interp)(e)
outputs.append(e)
else:
d = conv_block(decoders(i - 1), output_channels, kernel_size=(config('kernel_size'), config('kernel_size')), n=1, is_bn=False, is_relu=False, l2_reg=config('l2_reg'))
d = k.layers.UpSampling2D(size=(2**(len(decoders)-i), 2**(len(decoders)-i)), interpolation=interp)(d)
outputs.append(d)
if training:
for i in range(len(outputs)):
if i == 0:
continue
d_e = outputs(i)
d_e = k.layers.concatenate((out1, out2, out3))
outputs(i) = merge_output(input_layer, k.activations.linear(d_e), output_channels)
return tf.keras.Model(inputs=input_layer, outputs=outputs, name='UNet3Plus')
Fonte: towardsdatascience.com