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 27 301×
Přečteno 26 967×
Přečteno 25 845×
Přečteno 23 717×
Přečteno 19 459×