V předchozích dvou článcích jsem vytvářel modely pro sémantickou segmentaci obrázků takzvaně „z čistého stolu“. Dělal jsem to proto, abych si lépe ověřil, jak modely fungují. Popravdě řečeno, takto se to dnes asi běžně nedělá. Obvykle autoři vychází z již existujících modelů, které pak přizpůsobují konkrétnímu úkolu. A to tom bude můj dnešní příspěvek.
Základem každého segmentačního modelu založeného na konvolučních vrstvách je klasický klasifikační model. Ten tvoří základ pro kontrakční fázi modelu, obvykle se označuje jako „backbone“. Nabízí se tedy možnost využít některý z již existujících modelů, a případně také váhy vrstev, které byly zjištěny při trénování na nějaké obecné datové sadě. Jinak řečeno, použít přístup tzv. „transfer learning“, a expanzní fázi doplnit vlastníma rukama. To je tedy náplň následujícího povídání.
In [1]:
import sys import os import shutil import warnings import glob import pathlib import numpy as np import pandas as pd import matplotlib.pyplot as plt import seaborn as sns from sklearn.model_selection import train_test_split import tensorflow as tf import tensorflow.keras as keras from keras.models import Sequential from keras import Input, Model from keras import layers from keras.utils import plot_model from IPython.display import Image import cv2 sns.set_style('darkgrid') warnings.simplefilter(action='ignore', category=FutureWarning)
In [2]:
def seed_all():
import random
random.seed(42)
np.random.seed(42)
tf.random.set_seed(42)
os.environ['PYTHONHASHSEED'] = str(42)
os.environ['TF_DETERMINISTIC_OPS'] = '1'
seed_all()
if not os.path.exists('/kaggle/working/model'):
os.makedirs('/kaggle/working/model')
In [3]:
DATA_ROOT = "/kaggle/input/lgg-mri-segmentation/kaggle_3m/"
IMAGE_SIZE = (128, 128)
Dnes budu pro jednoduchost opět vycházet z již dříve použité datové sady Brain MRI segmentation. Pokud by vás zajímaly bližší informace o tom, jak jsem data načítal, odkážu vás na předchozí článek. Tam to je trochu více popsáno. Na tomto místě si pouze data načtu do dvou polí, X – zdrojové obrázky a Y – segmentační masky jako cíl.
In [4]:
image_paths = [] for path in glob.glob(DATA_ROOT + "**/*_mask.tif"): def strip_base(p): parts = pathlib.Path(p).parts return os.path.join(*parts[-2:]) image = path.replace("_mask", "") if os.path.isfile(image): image_paths.append((strip_base(image), strip_base(path))) else: print("MISSING: ", image, "==>", path)
In [5]:
def get_image_data(image_paths):
x, y = list(), list()
for image_path, mask_path in image_paths:
image = cv2.imread(os.path.join(DATA_ROOT, image_path), flags=cv2.IMREAD_COLOR)
image = cv2.resize(image, IMAGE_SIZE)
mask = cv2.imread(os.path.join(DATA_ROOT, mask_path), flags=cv2.IMREAD_GRAYSCALE)
mask = cv2.resize(mask, IMAGE_SIZE)
x.append(image)
y.append(mask)
return np.array(x) / 255, np.expand_dims(np.array(y) / 255, -1)
X, Y = get_image_data(image_paths)
print(f"X: {X.shape}")
print(f"Y: {Y.shape}")
X: (3929, 128, 128, 3)
Y: (3929, 128, 128, 1)
Z posledního výpisu je zřejmé, že jsem načetl 3929 vzorků v rozlišení 128×128 pixelů. Zdrojové obrázky jsou vytvořeny jako RGB snímky se třemi kanály, cílové masky pak jako odstíny šedi (tedy jeden kanál).
Klasifikační model VGG16 jsem si vybral záměrně, neboť se jedná o poměrně jednoduchý a přímočarý model, který bude pro mé pokusy plně vyhovovat. Navíc vzhledem k mému omezenému rozlišení zdrojových obrázků ani nebudu využívat výstupy všech konvolučních bloků. Tento model pro mne bude představovat kontrakční fázi segmentačního modelu U-Net. Dále budu potřebovat ještě expanzní fázi. Tu si napíšu vlastníma rukama s využitím Transpose Convolution a konvolučních bloků.
Nejdříve ale potřebuji instanci klasifikačního modelu VGG16. Nejjednodušší způsob je načtení rovnou z distribuce Keras včetně vah zjištěných při trénování modelu jeho autory:
In [6]:
from keras.applications.vgg16 import VGG16, preprocess_input
In [7]:
vgg16 = VGG16(include_top=False, weights="imagenet", input_shape=X.shape[1:]) vgg16.summary() Downloading data from https://storage.googleapis.com/tensorflow/keras-applications/vgg16/vgg16_weights_tf_dim_ordering_tf_kernels_notop.h5 58889256/58889256 [==============================] - 2s 0us/step Model: "vgg16" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= input_1 (InputLayer) [(None, 128, 128, 3)] 0 block1_conv1 (Conv2D) (None, 128, 128, 64) 1792 block1_conv2 (Conv2D) (None, 128, 128, 64) 36928 block1_pool (MaxPooling2D) (None, 64, 64, 64) 0 block2_conv1 (Conv2D) (None, 64, 64, 128) 73856 block2_conv2 (Conv2D) (None, 64, 64, 128) 147584 block2_pool (MaxPooling2D) (None, 32, 32, 128) 0 block3_conv1 (Conv2D) (None, 32, 32, 256) 295168 block3_conv2 (Conv2D) (None, 32, 32, 256) 590080 block3_conv3 (Conv2D) (None, 32, 32, 256) 590080 block3_pool (MaxPooling2D) (None, 16, 16, 256) 0 block4_conv1 (Conv2D) (None, 16, 16, 512) 1180160 block4_conv2 (Conv2D) (None, 16, 16, 512) 2359808 block4_conv3 (Conv2D) (None, 16, 16, 512) 2359808 block4_pool (MaxPooling2D) (None, 8, 8, 512) 0 block5_conv1 (Conv2D) (None, 8, 8, 512) 2359808 block5_conv2 (Conv2D) (None, 8, 8, 512) 2359808 block5_conv3 (Conv2D) (None, 8, 8, 512) 2359808 block5_pool (MaxPooling2D) (None, 4, 4, 512) 0 ================================================================= Total params: 14714688 (56.13 MB) Trainable params: 14714688 (56.13 MB) Non-trainable params: 0 (0.00 Byte) _________________________________________________________________
Výpis modelu uvádím záměrně. Vzhledem k tomu, že budu vytvářet expanzní fázi U-Net modelu, budu potřebovat napojení přes tzv. skip connections z různých úrovní klasifikačního modelu.
Obvykle se využívá výstup poslední konvoluční vrstvy před vrstvou MaxPooling2D. Budu se tohoto doporučení držet, proto budu potřebovat výstupy z vrstev block1_conv2, block2_conv2 , block3_conv3 a block4_conv3 (pro ověření je výše právě ten výpis).
A nyní již mám vše potřebné pro vytvoření U-Net modelu založeného na VGG16 backbone:
In [8]:
def create_model_UNet_VGG16Backbone(X_shape, classes=1, name="UNet_VGG16Backbone"):
def conv_block(x, *, filters, kernel_size=(3, 3), strides=(1, 1), padding='same', activation='relu', name=""):
x = layers.Conv2D(filters=filters, kernel_size=kernel_size, strides=strides, padding=padding, kernel_initializer="he_normal", name=f"{name}_conv")(x)
x = layers.BatchNormalization(name=f"{name}_norm")(x)
if activation:
x = layers.Activation(activation, name=f"{name}_acti")(x)
return x
def decoder_block(x, s, *, filters, name=""):
x = layers.Conv2DTranspose(filters, (2, 2), strides=2, padding='same', kernel_initializer="he_normal", name=f"{name}_trans")(x)
x = layers.Concatenate(name=f"{name}_concat")([x, s])
x = conv_block(x, filters=filters, name=f"{name}_conv1")
x = conv_block(x, filters=filters, name=f"{name}_conv2")
return x
# Contracting Path
base_model = VGG16(include_top=False, input_shape=X_shape[-3:])
base_model.trainable = False
# Bottleneck
x = conv_block(base_model.get_layer("block4_conv3").output, filters=512, name="bot1")
x = conv_block(x, filters=512, name="bot2")
# Expansive Path
x = decoder_block(x, base_model.get_layer("block3_conv3").output, filters=256, name="dec2")
x = decoder_block(x, base_model.get_layer("block2_conv2").output, filters=128, name="dec3")
x = decoder_block(x, base_model.get_layer("block1_conv2").output, filters=64, name="dec4")
# Output
outputs = conv_block(x, filters=classes, kernel_size=(1, 1), activation='sigmoid', name="outputs")
return Model(inputs=base_model.input, outputs=outputs, name=name)
Kontrakční fázi modelu mně představuje instance VGG16. Vzhledem k tomu, že jsem si model načetl včetně vah, nebudu tuto část modelu trénovat, a proto je nastaven příznak trainable=False.
Vstupem pro úzké hrdlo modelu je výstup vrstvy block4_conv3 kontrakční fáze (jen pro připomenutí, poslední konvoluce před pooling). V tomto místě mám rozlišení 16×16 s 256 vlastnostmi. Hrdlo je tvořeno dvěma konvolučními bloky se zachováním rozlišení obrázku, ale se zvětšením počtu vlastností na dvojnásobek.
Expanzní fáze je pak tvořena třemi bloky komplementárními ke kontrakční fázi. Jedná se tedy o bloky s roztažením obrázku na dvojnásobnou velikost vrstvou Conv2DTranspose s krokem konvoluce 2. Následuje zřetězení výstupu vrstvy s tenzorem skip connection. A vše je završeno dvěma konvolučními bloky s redukcí počtu sledovaných vlastností.
Výstupem celého modelu je opět konvoluční blok s rozlišením původního obrázku a aktivační funkcí Sigmoid pro zjištění pravděpodobnosti zařazení pixelu do třídy.
Opět jsem si zde doplnil implementace pro ztrátovou funkci a metriky. Jako ztrátovou funkci používám kombinaci Binary CrossEntropy a Dice Coefficient. Metriky jsem si v tomto případě vybral Dice Coefficient a Jaccard Index (i když by mně asi stačila jenom jedna, neboť jejich výsledky se dost kryjí).
In [9]:
import keras.backend as K from keras.losses import binary_crossentropy def dice_coefficient(y_true, y_pred): smooth = 1. y_true_f = K.flatten(y_true) y_pred_f = K.flatten(y_pred) intersection = K.sum(y_true_f * y_pred_f) score = (2. * intersection + smooth) / (K.sum(y_true_f) + K.sum(y_pred_f) + smooth) return score def dice_loss(y_true, y_pred): loss = 1 - dice_coefficient(y_true, y_pred) return loss def bce_dice_loss(y_true, y_pred): loss = binary_crossentropy(y_true, y_pred) + dice_loss(y_true, y_pred) return loss def jaccard_index(y_true, y_pred): smooth = 1. y_true_f = K.flatten(y_true) y_pred_f = K.flatten(y_pred) intersection = K.sum(y_true_f * y_pred_f) + smooth union = K.sum((y_true_f + y_pred_f) - (y_true_f * y_pred_f)) + smooth return intersection / union def jaccard_loss(y_true, y_pred): return 1 - jaccard_index(y_true, y_pred)
Ještě si musím rozdělit celou datovou sadu na část pro trénování a pro testování výkonu modelu (poměr 80:20):
In [10]:
x_train, x_test, y_train, y_test = train_test_split(X, Y, test_size=0.2) print(f"x_train: {x_train.shape}, y_train: {y_train.shape}") print(f"x_test: {x_test.shape}, y_test: {y_test.shape}") x_train: (3143, 128, 128, 3), y_train: (3143, 128, 128, 1) x_test: (786, 128, 128, 3), y_test: (786, 128, 128, 1)
Vytvořím si instanci modelu a přeložím jej:
In [11]:
model = create_model_UNet_VGG16Backbone(x_test.shape, 1) model.compile(optimizer="adam", loss=bce_dice_loss, metrics=[dice_coefficient, jaccard_index]) plot_model(model, to_file=f'/kaggle/working/model/{model.name}.png',show_shapes=True, show_layer_names=True) Image(retina=True, filename=f'/kaggle/working/model/{model.name}.png')
Použiji pro mne obvyklý postup s callback funkcemi pro úschovu nejlepší verze modelu a dřívější zastavení trénování v případě, že se výsledky začnou zhoršovat při validaci (což se ale nestalo a využil jsem celou sadu 100 epoch).
In [12]:
MODEL_CHECKPOINT = f"/kaggle/working/model/{model.name}.ckpt"
EPOCHS = 100
callbacks_list = [
keras.callbacks.EarlyStopping(monitor='val_dice_coefficient', mode='max', patience=20),
keras.callbacks.ModelCheckpoint(filepath=MODEL_CHECKPOINT, monitor='val_dice_coefficient', save_best_only=True, mode='max', verbose=1)
]
history = model.fit(
x=x_train,
y=y_train,
epochs=EPOCHS,
callbacks=callbacks_list,
validation_split=0.2,
verbose=1)
Epoch 1/100
I0000 00:00:1709056488.748993 67 device_compiler.h:186] Compiled cluster using XLA! This line is logged at most once for the lifetime of the process.
79/79 [==============================] - ETA: 0s - loss: 1.6200 - dice_coefficient: 0.0358 - jaccard_index: 0.0183
Epoch 1: val_dice_coefficient improved from -inf to 0.03375, saving model to /kaggle/working/model/UNet_VGG16Backbone.ckpt
79/79 [==============================] - 31s 267ms/step - loss: 1.6200 - dice_coefficient: 0.0358 - jaccard_index: 0.0183 - val_loss: 2.6663 - val_dice_coefficient: 0.0337 - val_jaccard_index: 0.0172
Epoch 2/100
79/79 [==============================] - ETA: 0s - loss: 1.5630 - dice_coefficient: 0.0393 - jaccard_index: 0.0201
Epoch 2: val_dice_coefficient improved from 0.03375 to 0.04521, saving model to /kaggle/working/model/UNet_VGG16Backbone.ckpt
79/79 [==============================] - 19s 244ms/step - loss: 1.5630 - dice_coefficient: 0.0393 - jaccard_index: 0.0201 - val_loss: 1.6512 - val_dice_coefficient: 0.0452 - val_jaccard_index: 0.0232
Epoch 3/100
79/79 [==============================] - ETA: 0s - loss: 1.5218 - dice_coefficient: 0.0421 - jaccard_index: 0.0216
Epoch 3: val_dice_coefficient improved from 0.04521 to 0.04943, saving model to /kaggle/working/model/UNet_VGG16Backbone.ckpt
79/79 [==============================] - 20s 249ms/step - loss: 1.5218 - dice_coefficient: 0.0421 - jaccard_index: 0.0216 - val_loss: 1.4917 - val_dice_coefficient: 0.0494 - val_jaccard_index: 0.0254
Epoch 4/100
79/79 [==============================] - ETA: 0s - loss: 1.4834 - dice_coefficient: 0.0448 - jaccard_index: 0.0229
Epoch 4: val_dice_coefficient improved from 0.04943 to 0.05024, saving model to /kaggle/working/model/UNet_VGG16Backbone.ckpt
79/79 [==============================] - 20s 248ms/step - loss: 1.4834 - dice_coefficient: 0.0448 - jaccard_index: 0.0229 - val_loss: 1.4353 - val_dice_coefficient: 0.0502 - val_jaccard_index: 0.0259
Epoch 5/100
79/79 [==============================] - ETA: 0s - loss: 1.4490 - dice_coefficient: 0.0469 - jaccard_index: 0.0241
Epoch 5: val_dice_coefficient improved from 0.05024 to 0.05573, saving model to /kaggle/working/model/UNet_VGG16Backbone.ckpt
79/79 [==============================] - 20s 248ms/step - loss: 1.4490 - dice_coefficient: 0.0469 - jaccard_index: 0.0241 - val_loss: 1.4291 - val_dice_coefficient: 0.0557 - val_jaccard_index: 0.0288
Epoch 6/100
79/79 [==============================] - ETA: 0s - loss: 1.4185 - dice_coefficient: 0.0492 - jaccard_index: 0.0253
Epoch 6: val_dice_coefficient did not improve from 0.05573
79/79 [==============================] - 14s 176ms/step - loss: 1.4185 - dice_coefficient: 0.0492 - jaccard_index: 0.0253 - val_loss: 1.7762 - val_dice_coefficient: 0.0477 - val_jaccard_index: 0.0246
Epoch 7/100
79/79 [==============================] - ETA: 0s - loss: 1.3861 - dice_coefficient: 0.0520 - jaccard_index: 0.0268
Epoch 7: val_dice_coefficient improved from 0.05573 to 0.06047, saving model to /kaggle/working/model/UNet_VGG16Backbone.ckpt
79/79 [==============================] - 19s 243ms/step - loss: 1.3861 - dice_coefficient: 0.0520 - jaccard_index: 0.0268 - val_loss: 1.3667 - val_dice_coefficient: 0.0605 - val_jaccard_index: 0.0313
Epoch 8/100
79/79 [==============================] - ETA: 0s - loss: 1.3565 - dice_coefficient: 0.0547 - jaccard_index: 0.0282
Epoch 8: val_dice_coefficient improved from 0.06047 to 0.06562, saving model to /kaggle/working/model/UNet_VGG16Backbone.ckpt
79/79 [==============================] - 20s 250ms/step - loss: 1.3565 - dice_coefficient: 0.0547 - jaccard_index: 0.0282 - val_loss: 1.3166 - val_dice_coefficient: 0.0656 - val_jaccard_index: 0.0341
Epoch 9/100
79/79 [==============================] - ETA: 0s - loss: 1.3301 - dice_coefficient: 0.0572 - jaccard_index: 0.0296
Epoch 9: val_dice_coefficient did not improve from 0.06562
79/79 [==============================] - 14s 176ms/step - loss: 1.3301 - dice_coefficient: 0.0572 - jaccard_index: 0.0296 - val_loss: 1.3041 - val_dice_coefficient: 0.0646 - val_jaccard_index: 0.0335
Epoch 10/100
79/79 [==============================] - ETA: 0s - loss: 1.3042 - dice_coefficient: 0.0605 - jaccard_index: 0.0313
Epoch 10: val_dice_coefficient improved from 0.06562 to 0.06736, saving model to /kaggle/working/model/UNet_VGG16Backbone.ckpt
79/79 [==============================] - 19s 248ms/step - loss: 1.3042 - dice_coefficient: 0.0605 - jaccard_index: 0.0313 - val_loss: 1.2948 - val_dice_coefficient: 0.0674 - val_jaccard_index: 0.0350
...
Epoch 90/100
79/79 [==============================] - ETA: 0s - loss: 0.1222 - dice_coefficient: 0.8841 - jaccard_index: 0.7946
Epoch 90: val_dice_coefficient improved from 0.82025 to 0.82255, saving model to /kaggle/working/model/UNet_VGG16Backbone.ckpt
79/79 [==============================] - 20s 251ms/step - loss: 0.1222 - dice_coefficient: 0.8841 - jaccard_index: 0.7946 - val_loss: 0.2062 - val_dice_coefficient: 0.8226 - val_jaccard_index: 0.7001
Epoch 91/100
79/79 [==============================] - ETA: 0s - loss: 0.1175 - dice_coefficient: 0.8885 - jaccard_index: 0.8013
Epoch 91: val_dice_coefficient improved from 0.82255 to 0.82593, saving model to /kaggle/working/model/UNet_VGG16Backbone.ckpt
79/79 [==============================] - 20s 249ms/step - loss: 0.1175 - dice_coefficient: 0.8885 - jaccard_index: 0.8013 - val_loss: 0.2047 - val_dice_coefficient: 0.8259 - val_jaccard_index: 0.7049
Epoch 92/100
79/79 [==============================] - ETA: 0s - loss: 0.1202 - dice_coefficient: 0.8863 - jaccard_index: 0.7982
Epoch 92: val_dice_coefficient improved from 0.82593 to 0.83059, saving model to /kaggle/working/model/UNet_VGG16Backbone.ckpt
79/79 [==============================] - 20s 249ms/step - loss: 0.1202 - dice_coefficient: 0.8863 - jaccard_index: 0.7982 - val_loss: 0.1978 - val_dice_coefficient: 0.8306 - val_jaccard_index: 0.7115
Epoch 93/100
79/79 [==============================] - ETA: 0s - loss: 0.1188 - dice_coefficient: 0.8877 - jaccard_index: 0.8009
Epoch 93: val_dice_coefficient did not improve from 0.83059
79/79 [==============================] - 14s 176ms/step - loss: 0.1188 - dice_coefficient: 0.8877 - jaccard_index: 0.8009 - val_loss: 0.2024 - val_dice_coefficient: 0.8245 - val_jaccard_index: 0.7026
Epoch 94/100
79/79 [==============================] - ETA: 0s - loss: 0.1095 - dice_coefficient: 0.8965 - jaccard_index: 0.8139
Epoch 94: val_dice_coefficient did not improve from 0.83059
79/79 [==============================] - 14s 176ms/step - loss: 0.1095 - dice_coefficient: 0.8965 - jaccard_index: 0.8139 - val_loss: 0.2007 - val_dice_coefficient: 0.8264 - val_jaccard_index: 0.7057
Epoch 95/100
79/79 [==============================] - ETA: 0s - loss: 0.1102 - dice_coefficient: 0.8958 - jaccard_index: 0.8135
Epoch 95: val_dice_coefficient improved from 0.83059 to 0.83182, saving model to /kaggle/working/model/UNet_VGG16Backbone.ckpt
79/79 [==============================] - 20s 250ms/step - loss: 0.1102 - dice_coefficient: 0.8958 - jaccard_index: 0.8135 - val_loss: 0.1987 - val_dice_coefficient: 0.8318 - val_jaccard_index: 0.7133
Epoch 96/100
79/79 [==============================] - ETA: 0s - loss: 0.1051 - dice_coefficient: 0.9003 - jaccard_index: 0.8206
Epoch 96: val_dice_coefficient improved from 0.83182 to 0.83433, saving model to /kaggle/working/model/UNet_VGG16Backbone.ckpt
79/79 [==============================] - 19s 244ms/step - loss: 0.1051 - dice_coefficient: 0.9003 - jaccard_index: 0.8206 - val_loss: 0.1952 - val_dice_coefficient: 0.8343 - val_jaccard_index: 0.7170
Epoch 97/100
79/79 [==============================] - ETA: 0s - loss: 0.1092 - dice_coefficient: 0.8957 - jaccard_index: 0.8136
Epoch 97: val_dice_coefficient did not improve from 0.83433
79/79 [==============================] - 14s 176ms/step - loss: 0.1092 - dice_coefficient: 0.8957 - jaccard_index: 0.8136 - val_loss: 0.1974 - val_dice_coefficient: 0.8312 - val_jaccard_index: 0.7124
Epoch 98/100
79/79 [==============================] - ETA: 0s - loss: 0.1062 - dice_coefficient: 0.8998 - jaccard_index: 0.8203
Epoch 98: val_dice_coefficient did not improve from 0.83433
79/79 [==============================] - 14s 176ms/step - loss: 0.1062 - dice_coefficient: 0.8998 - jaccard_index: 0.8203 - val_loss: 0.1964 - val_dice_coefficient: 0.8307 - val_jaccard_index: 0.7119
Epoch 99/100
79/79 [==============================] - ETA: 0s - loss: 0.0996 - dice_coefficient: 0.9061 - jaccard_index: 0.8303
Epoch 99: val_dice_coefficient improved from 0.83433 to 0.83926, saving model to /kaggle/working/model/UNet_VGG16Backbone.ckpt
79/79 [==============================] - 20s 251ms/step - loss: 0.0996 - dice_coefficient: 0.9061 - jaccard_index: 0.8303 - val_loss: 0.1908 - val_dice_coefficient: 0.8393 - val_jaccard_index: 0.7243
Epoch 100/100
79/79 [==============================] - ETA: 0s - loss: 0.0943 - dice_coefficient: 0.9111 - jaccard_index: 0.8381
Epoch 100: val_dice_coefficient improved from 0.83926 to 0.84058, saving model to /kaggle/working/model/UNet_VGG16Backbone.ckpt
79/79 [==============================] - 20s 250ms/step - loss: 0.0943 - dice_coefficient: 0.9111 - jaccard_index: 0.8381 - val_loss: 0.1873 - val_dice_coefficient: 0.8406 - val_jaccard_index: 0.7261
A takto vypadal průběh ztrátové funkce a metrik při trénování modelu:
In [13]:
fig, ax = plt.subplots(1, 2, figsize=(16, 4)) sns.lineplot(data={k: history.history[k] for k in ('loss', 'val_loss')}, ax=ax[0]) sns.lineplot(data={k: history.history[k] for k in history.history.keys() if k not in ('loss', 'val_loss')}, ax=ax[1]) plt.show()
Načtu si váhy modelu, který vyšel při validaci nejlépe. Budu doufat, že bude mít také nejlepší výsledky při testování:
In [14]:
model.load_weights(f"/kaggle/working/model/{model.name}.ckpt")
Out[14]:
<tensorflow.python.checkpoint.checkpoint.CheckpointLoadStatus at 0x79015c4e3a90>
Udělám tedy predikci pro celou testovací sadu. Výsledek pak převedu z pravděpodobnosti na hodnoty ano/ne (dělící hladinou v mém případě je pravděpodobnost 50%):
In [15]:
y_pred = model.predict(x_test) y_pred = (y_pred > 0.5).astype(np.float64) 25/25 [==============================] - 2s 79ms/step
Následuje ukázka několika náhodně vybraných vzorků, neboť obrázkem nikdy neurazíš:
In [16]:
for _ in range(20):
i = np.random.randint(len(y_test))
if y_test[i].sum() > 0:
plt.figure(figsize=(8, 8))
plt.subplot(1,3,1)
plt.imshow(x_test[i])
plt.title('Original Image')
plt.subplot(1,3,2)
plt.imshow(y_test[i])
plt.title('Original Mask')
plt.subplot(1,3,3)
plt.imshow(y_pred[i])
plt.title('Prediction')
plt.show()
Pro dokreslení výsledků modelu ještě doplňuji dva histogramy pro metriky Dice Coefficient a Jaccard Index stejně, jak tomu bylo v předchozích dvou článcích:
In [17]:
pred_dice_metric = np.array([dice_coefficient(y_test[i], y_pred[i]).numpy() for i in range(len(y_test))])
In [18]:
fig=plt.figure(figsize=(8, 3)) sns.histplot(pred_dice_metric, stat="probability", bins=50) plt.xlabel("Dice metric") plt.show()
In [19]:
pred_jaccard_metric = np.array([jaccard_index(y_test[i], y_pred[i]).numpy() for i in range(len(y_test))])
In [20]:
fig=plt.figure(figsize=(8, 3)) sns.histplot(pred_jaccard_metric, stat="probability", bins=50) plt.xlabel("Jaccard (IoU) metric") plt.show()
pracuje na pozici IT architekta. Poslední roky se zaměřuje na integrační a komunikační projekty ve zdravotnictví. Mezi jeho koníčky patří také paragliding a jízda na horském kole.
Přečteno 33 524×
Přečteno 29 927×
Přečteno 27 156×
Přečteno 25 070×
Přečteno 20 657×