آموزش الگوریتم U-Net با کراس
در این آموزش، نحوه ایجاد U-Net، که یک مدل تقسیمبندی تصویر (image segmentation) است، در TensorFlow 2 / Keras را خواهید آموخت، ابتدا مختصری در مورد تقسیمبندی تصویر، معماری U-Net ارائه میکنیم و سپس پیادهسازی کد را با یک نوت بوک Colab مرور میکنیم.
بخش بندی تصویر U-Net در کراس
تقسیم بندی تصویر یک کار بینایی کامپیوتری است که با اختصاص یک برچسب به هر پیکسل تصویر، یک تصویر را به چندین ناحیه تقسیم می کند.
اطلاعات بسیار بیشتری در مورد یک تصویر نسبت به تشخیص شی، که یک کادر محدود در اطراف شی شناسایی شده ترسیم می کند، یا طبقه بندی تصویر، که یک برچسب به شی اختصاص می دهد، ارائه می دهد.
انواع تقسیم بندی تصویر
تقسیم بندی تصویر می تواند در برنامه های واقعی مانند تصویربرداری پزشکی، تقسیم بندی لباس ها، نقشه های سیل، ماشین های خودران و غیره استفاده شود.
دو نوع تقسیم بندی تصویر وجود دارد:
1. Semantic segmentation (تقسیم بندی معنایی)
هر پیکسل را با یک برچسب طبقه بندی می کنیم.
2. Instance segmentation (تقسیمبندی نمونه)
هر پیکسل را طبقهبندی می کنیم و هر نمونه شی را متمایز میکنیم.
برای درک بهتر تفاوت Semantic segmentation و Instance segmentation تصویر زیر را نگاه کنید:
U-Net یک تکنیک تقسیم بندی معنایی (Semantic segmentation) است که در ابتدا برای تقسیم بندی تصویربرداری پزشکی پیشنهاد شده است.
این یکی از مدلهای تقسیمبندی یادگیری عمیق قبلی (یک شبکه کانولوشنال پنجره کشویی) است و معماری U-Net نیز در بسیاری از انواع GAN مانند ژنراتور Pix2Pix استفاده میشود.
معماری U-Net
U-Net در مقاله U-Net: Convolutional Networks for Biomedical Image Segmentation در سال 2015 معرفی شد.
معماری مدل نسبتاً ساده است: که شامل یک encoder (برای نمونه برداری پایین) و یک decoder (برای نمونه برداری بالا) با اتصالات پرش می باشد.
همانطور که شکل پایین نشان می دهد، شکل آن مانند حرف U است و از این رو U-Net نامیده می شود.
- فلشهای خاکستری نشاندهنده اتصالات پرش است که نقشه ویژگی encoder را با decoder پیوند میدهد، که به جریان برگشتی گرادیانها برای بهبود آموزش کمک میکند.
- معماری U-Net دارای یک مسیر گسترشسازی در سمت راست و یک مسیر فشردهسازی در سمت چپ میباشد.
- مسیر فشردهسازی متشکل از ۲ لایه سه به سه پیچشی است.
- هر یک از این لایههای پیچشی، یک تابع فعالسازی Relu و یک الگوریتم max-pooling دو در دو برای کاهش نمونهگیری کاهش نمونهگیری نگاشت ویژگی دارد.
اکنون که درک اولیه ای از Semantic segmentation و معماری U-Net داریم، اجازه دهید U-Net را با TensorFlow 2 / Keras پیاده سازی کنیم.
لطفا آموزش زیر را با نوت بوک Colab دنبال کنید.
شروع
ما از Google Colab برای آموزش مدل استفاده خواهیم کرد، بنابراین مطمئن شوید که “Hardware accelerator” را روی “GPU under Runtime / تغییر نوع زمان اجرا” تنظیم کرده اید.
سپس کتابخانه ها و بسته هایی که این پروژه به آنها بستگی دارد را وارد کنید:
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
import tensorflow_datasets as tfds
import matplotlib.pyplot as plt
import numpy as np
دیتاست
ما از مجموعه داده های حیوانات خانگی Oxford-IIIT استفاده خواهیم کرد که به عنوان بخشی از مجموعه داده های TensorFlow (TFDS) در دسترس است.
می توان آن را به راحتی با TFDS بارگیری کرد، و سپس با کمی پیش پردازش داده، آماده برای آموزش مدل های تقسیم بندی میشود.
فقط با یک خط کد، میتوانیم tfds
با تعیین نام مجموعه داده، مجموعه داده را بارگیری کنیم و با تنظیم with_info=True
:
dataset, info = tfds.load('oxford_iiit_pet:3.*.*', with_info=True)
تغییر در داده های دانلود شده
خب حالا قبل از شروع آموزش U-Net، چند تغییر در داده های دانلود شده ایجاد می کنیم:
1-تغییر در اندازه ی تصاویر و ماسک ها
ابتدا اندازه تصاویر و ماسک ها را به 128x128
تغییر می دهیم .
def resize(input_image, input_mask):
input_image = tf.image.resize(input_image, (128, 128), method="nearest")
input_mask = tf.image.resize(input_mask, (128, 128), method="nearest")
return input_image, input_mask
2- ایجاد یک تابع برای تقویت مجموعه داده
حالا یک تابع برای تقویت مجموعه داده با چرخاندن افقی آنها (تولید دیتای جدید از روی دیتاهای دانلود شده (augment)) ایجادمیکنیم.
def augment(input_image, input_mask):
if tf.random.uniform(()) > 0.5:
# Random flipping of the image and mask
input_image = tf.image.flip_left_right(input_image)
input_mask = tf.image.flip_left_right(input_mask)
return input_image, input_mask
3- ایجاد یک تابع برای نرمالیزه کردن مجموعه داده با مقیاس بندی تصاویر به محدوده [-1, 1]
و کاهش ماسک تصویر به وسیله 1
def normalize(input_image, input_mask):
input_image = tf.cast(input_image, tf.float32) / 255.0
input_mask -= 1
return input_image, input_mask
ما دو تابع را برای پیش پردازش مجموعه داده های آموزشی (train) و آزمایشی (test) ایجاد می کنیم که تفاوت جزئی بین این دو وجود دارد.
ما فقط روی مجموعه داده آموزشی تقویت تصویر (image augmentation) را انجام می دهیم.
def load_image_train(datapoint):
input_image = datapoint["image"]
input_mask = datapoint["segmentation_mask"]
input_image, input_mask = resize(input_image, input_mask)
input_image, input_mask = augment(input_image, input_mask)
input_image, input_mask = normalize(input_image, input_mask)
return input_image, input_mask
def load_image_test(datapoint):
input_image = datapoint["image"]
input_mask = datapoint["segmentation_mask"]
input_image, input_mask = resize(input_image, input_mask)
input_image, input_mask = normalize(input_image, input_mask)
return input_image, input_mask
tf.data
اکنون با استفاده از تابع map
آماده ساختن یک خط لوله ورودی هستیم :
train_dataset = dataset["train"].map(load_image_train, num_parallel_calls=tf.data.AUTOTUNE)
test_dataset = dataset["test"].map(load_image_test, num_parallel_calls=tf.data.AUTOTUNE)
اگرprint(train_dataset)
اجرا کنیم متوجه می شویم که تصویر به شکل 128x128x3 در tf.float32
است در حالی که ماسک تصویر به شکل 128x128x1
با نوع داده tf.uint8
هستش.
ما یک اندازه دسته ای 64
و یک اندازه بافر را 1000
برای ایجاد دسته ای از مجموعه داده های آموزشی و آزمایشی تعریف می کنیم.
با مجموعه داده اصلی TFDS، 3680 نمونه آموزشی و 3669 نمونه آزمایشی وجود دارد که بیشتر به مجموعههای اعتبارسنجی/آزمون تقسیم میشوند.
ما از train_batches
and the validation_batches
برای آموزش مدل U-Net استفاده خواهیم کرد.
پس از پایان آموزش، از علامت test_batches
برای آزمایش پیشبینیهای مدل استفاده میکنیم.
BATCH_SIZE = 64
BUFFER_SIZE = 1000
train_batches = train_dataset.cache().shuffle(BUFFER_SIZE).batch(BATCH_SIZE).repeat()
train_batches = train_batches.prefetch(buffer_size=tf.data.experimental.AUTOTUNE)
validation_batches = test_dataset.take(3000).batch(BATCH_SIZE)
test_batches = test_dataset.skip(3000).take(669).batch(BATCH_SIZE)
اکنون مجموعه داده ها برای آموزش آماده هستند.
بیایید یک تصویر نمونه تصادفی و ماسک آن را از مجموعه داده آموزشی تجسم کنیم تا ایده ای از نحوه به نظر رسیدن داده ها داشته باشیم.
def display(display_list):
plt.figure(figsize=(15, 15))
title = ["Input Image", "True Mask", "Predicted Mask"]
for i in range(len(display_list)):
plt.subplot(1, len(display_list), i+1)
plt.title(title[i])
plt.imshow(tf.keras.utils.array_to_img(display_list[i]))
plt.axis("off")
plt.show()
sample_batch = next(iter(train_batches))
random_index = np.random.choice(sample_batch[0].shape[0])
sample_image, sample_mask = sample_batch[0][random_index], sample_batch[1][random_index]
display([sample_image, sample_mask])
نمونه تصویر ورودی یک گربه به شکل 128x128x3
.
ماسک واقعی سه بخش دارد
پسزمینه سبز.
پیش زمینه بنفش، در این مورد، یک گربه.
و طرح کلی زرد.
تصویر زیر هم تصویر ورودی اصلی و هم تصویر ماسک واقعی را نشان می دهد.
معماری مدل
اکنون که داده ها را برای آموزش آماده کرده ایم، بیایید معماری مدل U-Net را تعریف کنیم.
همانطور که قبلا ذکر شد، U-Net به شکل یک حرف U با یک رمزگذار، رمزگشا و اتصالات پرش بین آنها است.
بنابراین ما چند بلوک ساختمانی برای ساخت مدل U-Net ایجاد خواهیم کرد.
بلوک های ساختمان
1- تابع double_conv_block
با لایه Conv2D-ReLU-Conv2D-ReLU
ابتدا یک تابع double_conv_block
با لایه Conv2D-ReLU-Conv2D-ReLU
ها ایجاد می کنیم که هم در رمزگذار (یا مسیر انقباض) و هم در گلوگاه U-Net استفاده می کنیم/
def double_conv_block(x, n_filters):
# Conv2D then ReLU activation
x = layers.Conv2D(n_filters, 3, padding = "same", activation = "relu", kernel_initializer = "he_normal")(x)
# Conv2D then ReLU activation
x = layers.Conv2D(n_filters, 3, padding = "same", activation = "relu", kernel_initializer = "he_normal")(x)
return x
2- تابع downsample_block
سپس یک تابع downsample_block
برای نمونه برداری پایین یا استخراج ویژگی تعریف می کنیم تا در رمزگذار (encoder) استفاده شود.
def downsample_block(x, n_filters):
f = double_conv_block(x, n_filters)
p = layers.MaxPool2D(2)(f)
p = layers.Dropout(0.3)(p)
return f, p
3- تابع upsample_block
در نهایت، ما یک تابع upsample_block برای رمزگشا decoder (یا مسیر گسترش) U-Net تعریف می کنیم.
def upsample_block(x, conv_features, n_filters):
# upsample
x = layers.Conv2DTranspose(n_filters, 3, 2, padding="same")(x)
# concatenate
x = layers.concatenate([x, conv_features])
# dropout
x = layers.Dropout(0.3)(x)
# Conv2D twice with ReLU activation
x = double_conv_block(x, n_filters)
return x
مدل U-Net
سه گزینه برای ساخت مدل Keras وجود دارد:
1. Sequential API
آسانترین و مبتدیپسندترین، چیدن لایهها به ترتیب.
2. Functional API
انعطافپذیرتر است و توپولوژی غیر خطی، لایههای مشترک و ورودیها یا خروجیهای متعدد را امکانپذیر میکند.
3. Model subclassing
منعطف ترین و بهترین مدل، برای مدل های پیچیده که به حلقه های آموزشی سفارشی نیاز دارند.
U-Net یک معماری نسبتاً ساده دارد.
با این حال، برای ایجاد اتصالات پرش بین رمزگذار و رمزگشا، باید چند لایه را به هم متصل کنیم.
بنابراین Keras Functional API برای این منظور مناسب ترین است.
1- ابتدا یک build_unet_model
تابع ایجاد می کنیم، ورودی ها، لایه های رمزگذار، گلوگاه، لایه های رمزگشا و در نهایت لایه خروجی را Conv2D
با فعال سازی softmax
مشخص می کنیم.
توجه داشته باشید که 128x128x3
شکل تصویر ورودی است .
خروجی دارای سه کانال مربوط به سه کلاس است که مدل هر پیکسل را برای آنها طبقه بندی می کندوشامل پس زمینه، شی پیش زمینه و طرح کلی شی می باشد.
# inputs
inputs = layers.Input(shape=(128,128,3))
# encoder: contracting path - downsample
# 1 - downsample
f1, p1 = downsample_block(inputs, 64)
# 2 - downsample
f2, p2 = downsample_block(p1, 128)
# 3 - downsample
f3, p3 = downsample_block(p2, 256)
# 4 - downsample
f4, p4 = downsample_block(p3, 512)
# 5 - bottleneck
bottleneck = double_conv_block(p4, 1024)
# decoder: expanding path - upsample
# 6 - upsample
u6 = upsample_block(bottleneck, f4, 512)
# 7 - upsample
u7 = upsample_block(u6, f3, 256)
# 8 - upsample
u8 = upsample_block(u7, f2, 128)
# 9 - upsample
u9 = upsample_block(u8, f1, 64)
# outputs
outputs = layers.Conv2D(3, 1, padding="same", activation = "softmax")(u9)
# unet model with Keras Functional API
unet_model = tf.keras.Model(inputs, outputs, name="U-Net")
return unet_model
میتوانیم معماری مدل model.summary()
را برای دیدن هر جزئیات مدل تجسم کنیم.
و ما میتوانیم از تابع Keras Utils به نام plot_model
برای تولید یک نمودار بصریتر، از جمله اتصالات پرش استفاده کنیم.
تصویر تولید شده در Colab نود درجه چرخانده شده است تا بتوانید معماری U شکل را در شکل پایین ببینید (جزئیات را بهتر در نوت بوک Colab ببینید):
U-Net را کامپایل و آموزش دهید
برای کامپایل unet_model
، بهینهساز، تابع ضرر و معیارهای دقت را برای ردیابی در طول آموزش مشخص میکنیم:
unet_model.compile(optimizer=tf.keras.optimizers.Adam(),
loss="sparse_categorical_crossentropy",
metrics="accuracy")
We train the unet_model by calling model.fit() and training it for 20 epochs.
NUM_EPOCHS = 20
TRAIN_LENGTH = info.splits["train"].num_examples
STEPS_PER_EPOCH = TRAIN_LENGTH // BATCH_SIZE
VAL_SUBSPLITS = 5
TEST_LENTH = info.splits["test"].num_examples
VALIDATION_STEPS = TEST_LENTH // BATCH_SIZE // VAL_SUBSPLITS
model_history = unet_model.fit(train_batches,
epochs=NUM_EPOCHS,
steps_per_epoch=STEPS_PER_EPOCH,
validation_steps=VALIDATION_STEPS,
validation_data=test_batches)
پس از آموزش برای 20 دوره، یک دقت آموزشی (accuracy) و یک دقت اعتبار سنجی (validation accuracy) را دریافت می کنیم ~0.88
. منحنی یادگیری در طول آموزش نشان میدهد که مدل در مجموعه دادههای آموزشی و مجموعه اعتبارسنجی به خوبی عمل میکند، که نشان میدهد مدل به خوبی تعمیم مییابد بدون اینکه overfitting داشته باشد ( شکل پایین).
پیش بینی (Prediction)
اکنون که آموزش را به پایان رساندیم unet_model
، بیایید از آن برای پیش بینی چند تصویر نمونه از مجموعه داده آزمایشی استفاده کنیم
def create_mask(pred_mask):
pred_mask = tf.argmax(pred_mask, axis=-1)
pred_mask = pred_mask[..., tf.newaxis]
return pred_mask[0]
def show_predictions(dataset=None, num=1):
if dataset:
for image, mask in dataset.take(num):
pred_mask = unet_model.predict(image)
display([image[0], mask[0], create_mask(pred_mask)])
else:
display([sample_image, sample_mask,
create_mask(model.predict(sample_image[tf.newaxis, ...]))])
count = 0
for i in test_batches:
count +=1
print("number of batches:", count)
برای تصاویر ورودی، ماسک های واقعی، و ماسک های پیش بینی شده توسط مدل U-Net که آموزش دادیم، به شکل پایین مراجعه کنید .
خلاصه که:
در این پست، نحوه بارگیری داده های حیوانات خانگی Oxford-IIIT با مجموعه داده TensorFlow و نحوه آموزش یک مدل U-Net تقسیم بندی تصویر را از ابتدا یاد گرفتید. U-Net را با Keras Functional API ایجاد کردیم و معماری U شکل را با اتصالات پرش تجسم کردیم. این پست از آموزش رسمی تقسیم بندی تصویر TensorFlow.org و آموزش U-Net در Keras.io الهام گرفته شده است که keras.utils.Sequence
برای بارگذاری داده ها استفاده می کند و دارای معماری U-Net به سبک Xception است. U-Net یک شروع عالی برای یادگیری تقسیم بندی معنایی در تصاویر است. برای کسب اطلاعات بیشتر در مورد این موضوع، مقالات تقسیم بندی مدل های مدرن مانند DeepLab V3 ، HRNet ، U2-Net و غیره را در میان بسیاری از مقالات دیگر بخوانید.
دیدگاهتان را بنویسید