دليل شامل لاستخدام نموذج U-Net لتحليل الصور باستخدام Keras

Amine
05/09/2024

المقدمة

نموذج U-Net هو نوع من الشبكات العصبية التلافيفية (Convolutional Neural Networks – CNN) المُصمم خصيصًا لأغراض تقسيم الصور (Image Segmentation). يتميز النموذج ببنية متناسقة على شكل حرف “U”، مما يجعله مثاليًا لتحديد ملامح دقيقة داخل الصور مثل الصور الطبية. سنقوم في هذا الدليل بشرح تفاصيل نموذج U-Net خطوة بخطوة باستخدام مكتبة Keras.

1. بنية نموذج U-Net

نموذج U-Net يتكون من جزئين رئيسيين:

  • المسار التنزلي (Downsampling Path): في هذا المسار، يتم استخدام طبقات تلافيفية (Convolutional Layers) وطبقات تجميع (Pooling Layers) لاستخراج الميزات الأساسية وتقليل أبعاد الصور.
  • المسار التصاعدي (Upsampling Path): هذا الجزء يقوم باستعادة الأبعاد الأصلية للصور مع إضافة التفاصيل باستخدام طبقات توسع (Upsampling Layers) ودمج الميزات المستخلصة من المسار التنزلي عبر روابط التخطّي (Skip Connections).

يعمل نموذج U-Net على تقسيم كل بكسل في الصورة إلى فئة معينة بناءً على الميزات التي تم تعلمها خلال مراحل التلافيف والتوسيع.

2. المسار التنزلي (Downsampling Path)

يتكون المسار التنزلي من سلسلة من الطبقات التلافيفية (Convolutional Layers) وطبقات التجميع (Pooling Layers). كل طبقة تلافيفية تحتوي على مجموعة من الفلاتر التي تعمل على استخراج الميزات من الصورة، مثل الحواف أو القوام. بعد كل مجموعة من الطبقات التلافيفية، نستخدم طبقة تجميع (MaxPooling) لتقليل حجم الصورة بشكل تدريجي مع الاحتفاظ بالميزات الهامة.

على سبيل المثال، الطبقة التلافيفية الأولى تأخذ المدخلات الأصلية (مثل صورة 128×128) وتطبق فلاتر متعددة لاستخراج الميزات الأساسية.

from keras import layers, models

def unet_model(input_size=(128, 128, 3)):
    inputs = layers.Input(input_size)
    
    # الطبقة التلافيفية الأولى
    conv1 = layers.Conv2D(64, (3, 3), activation='relu', padding='same')(inputs)
    conv1 = layers.Conv2D(64, (3, 3), activation='relu', padding='same')(conv1)
    pool1 = layers.MaxPooling2D(pool_size=(2, 2))(conv1)

    # الطبقة التلافيفية الثانية
    conv2 = layers.Conv2D(128, (3, 3), activation='relu', padding='same')(pool1)
    conv2 = layers.Conv2D(128, (3, 3), activation='relu', padding='same')(conv2)
    pool2 = layers.MaxPooling2D(pool_size=(2, 2))(conv2)
    
    # الطبقة التلافيفية الثالثة
    conv3 = layers.Conv2D(256, (3, 3), activation='relu', padding='same')(pool2)
    conv3 = layers.Conv2D(256, (3, 3), activation='relu', padding='same')(conv3)
    pool3 = layers.MaxPooling2D(pool_size=(2, 2))(conv3)
    
    # الطبقة التلافيفية الرابعة
    conv4 = layers.Conv2D(512, (3, 3), activation='relu', padding='same')(pool3)
    conv4 = layers.Conv2D(512, (3, 3), activation='relu', padding='same')(conv4)
    pool4 = layers.MaxPooling2D(pool_size=(2, 2))(conv4)
    
    # الطبقة التلافيفية الخامسة (أسفل U)
    conv5 = layers.Conv2D(1024, (3, 3), activation='relu', padding='same')(pool4)
    conv5 = layers.Conv2D(1024, (3, 3), activation='relu', padding='same')(conv5)

    return inputs, conv1, conv2, conv3, conv4, conv5

في هذا الجزء، قمنا ببناء أربع طبقات تلافيفية متتالية مع MaxPooling لتقليل أبعاد الصور تدريجيًا، مما يسمح للشبكة باستخلاص الميزات الهامة من الصورة.

3. المسار التصاعدي (Upsampling Path)

في هذا المسار، نقوم بتوسيع الصورة تدريجيًا باستخدام طبقات التوسيع (Upsampling Layers) لإعادة الصورة إلى حجمها الأصلي. ندمج الميزات التي تم استخراجها في المسار التنزلي باستخدام روابط التخطّي (Skip Connections) لتعزيز الدقة.

إليك كيف نقوم ببناء المسار التصاعدي:

def unet_model(input_size=(128, 128, 3)):
    # نفس المسار التنزلي السابق
    inputs, conv1, conv2, conv3, conv4, conv5 = build_downsampling_path(input_size)

    # المسار التصاعدي (Upsampling Path)
    up6 = layers.Conv2D(512, (2, 2), activation='relu', padding='same')(layers.UpSampling2D(size=(2, 2))(conv5))
    merge6 = layers.concatenate([conv4, up6], axis=3)
    conv6 = layers.Conv2D(512, (3, 3), activation='relu', padding='same')(merge6)
    conv6 = layers.Conv2D(512, (3, 3), activation='relu', padding='same')(conv6)
    
    up7 = layers.Conv2D(256, (2, 2), activation='relu', padding='same')(layers.UpSampling2D(size=(2, 2))(conv6))
    merge7 = layers.concatenate([conv3, up7], axis=3)
    conv7 = layers.Conv2D(256, (3, 3), activation='relu', padding='same')(merge7)
    conv7 = layers.Conv2D(256, (3, 3), activation='relu', padding='same')(conv7)
    
    up8 = layers.Conv2D(128, (2, 2), activation='relu', padding='same')(layers.UpSampling2D(size=(2, 2))(conv7))
    merge8 = layers.concatenate([conv2, up8], axis=3)
    conv8 = layers.Conv2D(128, (3, 3), activation='relu', padding='same')(merge8)
    conv8 = layers.Conv2D(128, (3, 3), activation='relu', padding='same')(conv8)
    
    up9 = layers.Conv2D(64, (2, 2), activation='relu', padding='same')(layers.UpSampling2D(size=(2, 2))(conv8))
    merge9 = layers.concatenate([conv1, up9], axis=3)
    conv9 = layers.Conv2D(64, (3, 3), activation='relu', padding='same')(merge9)
    conv9 = layers.Conv2D(64, (3, 3), activation='relu', padding='same')(conv9)
    
    conv10 = layers.Conv2D(1, (1, 1), activation='sigmoid')(conv9)

    model = models.Model(inputs, conv10)
    
    return model

في المسار التصاعدي، يتم توسيع الصورة باستخدام UpSampling2D، حيث نضاعف حجم الصورة. ندمج كل طبقة مع الطبقات المقابلة من المسار التنزلي باستخدام concatenate لتشكيل روابط التخطّي (Skip Connections)، مما يسمح بتقليل فقدان المعلومات المكانية.

4. تدريب النموذج

بعد بناء النموذج، نقوم بتجميعه (Compile) باستخدام وظيفة الخسارة binary_crossentropy المناسبة لتقسيم الصور الثنائية (مثل تحديد الخلفية من المقدمة)، مع استخدام المحسّن Adam.

model = unet_model(input_size=(128, 128, 3))
model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])

# تدريب النموذج
history = model.fit(train_images, train_masks, epochs=50, batch_size=32, validation_split=0.2)

5. تقييم النموذج

بمجرد انتهاء التدريب، نقوم بتقييم النموذج على مجموعة البيانات الاختبارية لقياس الدقة (Accuracy) وفهم أداء النموذج.

test_loss, test_acc = model.evaluate(test_images, test_masks)
print(f"Test accuracy: {test_acc}")

6. التنبؤ بالتقسيمات

بعد التدريب والتقييم، يمكننا استخدام النموذج للتنبؤ بالتقسيمات على صور جديدة. نستخدم model.predict ثم نعرض النتائج باستخدام مكتبة Matplotlib.

predicted_mask = model.predict(new_image)

import matplotlib.pyplot as plt

plt.figure(figsize=(10, 5))
plt.subplot(1, 2, 1)
plt.title("Original Image")
plt.imshow(new_image.squeeze())

plt.subplot(1, 2, 2)
plt.title("Predicted Mask")
plt.imshow(predicted_mask.squeeze(), cmap='gray')
plt.show()

الخاتمة

في هذا الدليل، قمنا ببناء وتدريب نموذج U-Net باستخدام Keras لتحليل الصور وتقسيمها. نموذج U-Net أثبت فعاليته في المهام التي تتطلب دقة عالية في تحديد ملامح الصور، خاصة في المجالات الطبية. يمكنك تحسين أداء النموذج باستخدام تقنيات مثل تحسين البيانات (Data Augmentation) وزيادة حجم البيانات التدريبية.

التعليقات

اترك تعليقاً