الوحدة 8 — تكثير البيانات للصور
ننتقل الآن من CIFAR-10 إلى مجموعة بيانات جديدة: صور نفايات لفرزها إلى 6 أصناف (زجاج، كرتون، ورق، بلاستيك، معدن، عضوي). هذه المهمّة تحاكي كاميرا مركّبة في مركز فرز، مع نحو 2500 صورة موسومة لكلّ صنف. هذا قليل جدًّا لتدريب شبكة من الصفر. تُقدّم هذه الوحدة الأداة التي تحوّل 15000 صورة إلى ما يعادل مئات الآلاف: تكثير البيانات.
لماذا يعمل التكثير
تعميم النموذج على بيانات جديدة يعتمد على قدرته على تجاهل الاختلافات غير المهمّة والتركيز على الميزات المهمّة. صورة زجاجة زجاجية تبقى زجاجية سواء صُوّرت من اليسار أو من اليمين، وسواء أنيرت بضوء أصفر أو أبيض. الشبكة التي تتعلّم كلّ هذه ا لأشكال تعمّم أفضل من الشبكة التي رأت صورةً واحدة لكلّ زجاجة.
تكثير البيانات يخلق هذه الأشكال المختلفة صناعيًا من الصور الأصلية. تُنتج كلّ حقبة تدريب صورًا مختلفة قليلًا من كلّ صورة أصلية، فتشعر الشبكة أنّها تتدرّب على مجموعة أوسع بكثير مما هي عليه فعلًا.
الأدوات الأساسية
الانقلاب الأفقي (Horizontal Flip): صورة زجاجة معكوسة أفقيًا لا تزال زجاجة. هذا التكثير أرخص وأكثر فائدة في مهمّات الرؤية العامّة.
from tensorflow.keras import layers
tkathir = tf.keras.Sequential([
layers.RandomFlip("horizontal"),
])
الاقتصاص العشوائي (Random Crop): اقتطع رقعة عشوائية من الصورة ثم أعِد تحجيمها إلى الحجم المستهدف. تُجبَر الشبكة على التعرّف على الغرض من زوايا مختلفة ومقاييس مختلفة.
tkathir = tf.keras.Sequential([
layers.RandomCrop(200, 200),
layers.Resizing(224, 224),
])
التدوير العشوائي (Random Rotation): تدوير بزاوية صغيرة. مناسبة الزاوية تعتمد على المهمّة: 15° للنفايات (لأنّ كاميرا الفرز قد تلتقط الصور بزوايا مختلفة قليلًا)، 45° للمصنّفات الأكثر مرونة، لا شيء للأرقام أو النصوص (حيث التدوير يُغيّر المعنى).
layers.RandomRotation(0.1) # حوالي 36° نصف قوس
الاضطراب اللوني (Color Jitter): تعديلات على السطوع والتباين والتشبّع. يحاكي هذا التنوّع في ظروف الإضاءة والكاميرا.
layers.RandomBrightness(0.2)
layers.RandomContrast(0.2)
تكثيرات متقدّمة: Cutout وMixup
Cutout (DeVries وTaylor 2017) يضع مربّعًا عشوائيًا أسود على الصورة. يبدو مدمّرًا، لكنّه يمنع الشبكة من الاعتماد على منطقة واحدة من الصورة. تظهر آثاره عندما تتعلّم الشبكة النظر إلى الغرض كلّه بدلًا من ركن واحد:
import tensorflow as tf
def cutout(image, taille=32):
h, w = image.shape[0], image.shape[1]
cx = tf.random.uniform([], 0, w, dtype=tf.int32)
cy = tf.random.uniform([], 0, h, dtype=tf.int32)
x1 = tf.maximum(0, cx - taille // 2)
y1 = tf.maximum(0, cy - taille // 2)
x2 = tf.minimum(w, cx + taille // 2)
y2 = tf.minimum(h, cy + taille // 2)
masque = tf.ones_like(image)
zeros = tf.zeros([y2 - y1, x2 - x1, 3])
masque = tf.tensor_scatter_nd_update(
masque,
[[i, j] for i in range(y1, y2) for j in range(x1, x2)],
[[0.0] * 3] * ((y2 - y1) * (x2 - x1)),
)
return image * masque
Mixup (Zhang وآخرون 2017) يخلط صورتَين بمعامل ويخلط أيضًا وسمَيهما بنفس المعامل:
النتيجة صور غريبة (نصف زجاجة نصف كرتون)، لكنّها تُدرِّب الشبكة على قرارات ناعمة بدل قرارات قاطعة. الأثر: تراجع خطأ التقييم بواحد إلى اثنَين في المئة على معظم مهمّات الرؤية.
التكثير في الطيران مقابل التكثير المخزَّن
طريقتان لتطبيق التكثير:
التخزين المسبق (offline): توليد الصور المُكثَّرة مرّة واحدة وحفظها. يتضخّم القرص، وكلّ حقبة ترى نفس التكثيرات.
في الطيران (online / on the fly): توليد التكثيرات في كلّ دفعة من الدفعات. حساب إضافي على GPU، لكنّ كلّ حقبة ترى تكثيرات جديدة. هذا هو المعيار في Keras.
import tensorflow as tf
from tensorflow.keras import layers
# طبقات تكثير مدمجة في النموذج
tkathir = tf.keras.Sequential([
layers.RandomFlip("horizontal"),
layers.RandomRotation(0.1),
layers.RandomZoom(0.1),
layers.RandomContrast(0.2),
layers.RandomBrightness(0.2),
])
entree = tf.keras.Input(shape=(224, 224, 3))
x = tkathir(entree) # يُطبَّق فقط في وضع التدريب
x = tf.keras.applications.resnet50.preprocess_input(x)
# ... باقي النموذج
الميزة الرئيسية لدمج التكثير كطبقات: يُطبَّق تلقائيًا فقط في وضع التدريب، ويُغلَق في وضع التقييم والاستدلال. لا حاجة إلى إدارة يدوية.
ما لا يجب تكثيره
التكثير مفيد فقط إذا حفظ المعنى. هذه الملاحظة تبدو بديهية، لكنّها من أشيع أخطاء المبتدئين. أمثلة:
- قراءة الأرقام أو النصوص: انعكاس أفقي يحوّل «6» إلى شيء يشبه «Ç» بلا معنى، والدوران بـ90° يقلب المعنى كلّيًا.
- الوجوه: انعكاس أفقي مقبول، لكنّ الدوران أكثر من 15° يُنتج وجوهًا غير طبيعية.
- الصور الطبّية: تعديلات الألوان تُدمّر إشارات التشخيص. جرّب انقلاب صورة صدر بالأشعة السينية أفقيًا: قلبت موضع القلب، وهذه معلومة مهمّة سريريًا.
- المنطق المكاني: عندما تحمل الصورة سياقًا مكانيًا مهمًّا (سيارة على الطريق، لا في السماء)، الدوران والانقلابات الرأسية تخرّب المعنى.
للنفايات: انعكاس أفقي مقبول (زجاجة معكوسة تبقى زجاجة)، والدوران بـ15° مقبول، والألوان مقبولة، والانقلاب الرأسي مقبول أيضًا (لا يوجد اتّجاه «صحيح» لزجاجة في مركز فرز). Cutout مفيد أيضًا لأنّه يمنع الشبكة من التعرّف على قطعة صغيرة (كالغطاء وحده).
تكثيرات لا تتلاءم مع مهمّتك تجعل نموذجك يتعلّم شيئًا خاطئًا. تعلّم أوّل أمرٍ: انظر إلى صور مُكثَّرة بعينَيك. إذا كنت لن تتعرّف على الغرض في التكثير، فالشبكة لن تتعرّف عليه أيضًا. اطبع دفعة مكثَّرة قبل التدريب.
عملية على مجموعة النفايات
نبني خطّ تكثير مناسب للنفايات، ونربطه بشبكة صغيرة نبنيها من الصفر لنقيس مساهمة التكثير:
import tensorflow as tf
from tensorflow.keras import layers, models
tkathir = tf.keras.Sequential([
layers.RandomFlip("horizontal_and_vertical"),
layers.RandomRotation(0.1),
layers.RandomZoom(0.15),
layers.RandomBrightness(0.2),
layers.RandomContrast(0.2),
], name="tkathir")
def petit_cnn():
entree = tf.keras.Input(shape=(224, 224, 3))
x = tkathir(entree)
x = layers.Rescaling(1.0 / 255.0)(x)
x = layers.Conv2D(32, 3, padding="same", activation="relu")(x)
x = layers.MaxPooling2D(2)(x)
x = layers.Conv2D(64, 3, padding="same", activation="relu")(x)
x = layers.MaxPooling2D(2)(x)
x = layers.Conv2D(128, 3, padding="same", activation="relu")(x)
x = layers.MaxPooling2D(2)(x)
x = layers.GlobalAveragePooling2D()(x)
sortie = layers.Dense(6, activation="softmax")(x)
return tf.keras.Model(entree, sortie)
modele = petit_cnn()
modele.compile(optimizer="adam",
loss="sparse_categorical_crossentropy",
metrics=["accuracy"])
مع التكثير، ترتفع دقّة التقييم من 62 % (بدونه) إلى 71 % (به)، على نفس بيانات النفايات. تسع نقاط دون جمع صورة إضافية، ودون تغيير المعمارية. هذا هو أثر التكثير الحقيقي.
متى نُبالغ بالتكثير؟
كثرة التكثير تُنتج ظاهرة معاكسة: نقص التخصيص. الشبكة لا تستطيع الوصول إلى دقّة تدريب عالية لأنّ البيانات المدخلة عشوائية جدًّا. علامة ذلك: خسارة التدريب وخسارة التقييم متقاربتان لكنّ الاثنَين عاليتان.
القاعدة العملية: راقب فرق الخسارة بين التدريب والتقييم. فرق كبير = فرط تخصيص، زد التكثير. فرق صفري لكنّ الخسارة عالية = نقص تخصيص، خفّف التكثير أو زد قدرة النموذج.
- ابدأ بتكثير خفيف (انقلاب أفقي + تدوير 10°) وراقب.
- زد التكثير تدريجيًا حتى يُغلق فرط التخصيص.
- خطر: تكثير كبير + بيانات قليلة + نموذج صغير = نقص تخصيص شديد. جرّب النقل بدلًا من التدريب من الصفر (الوحدة القادمة).
- تحقّق دائمًا بصريًا من دفعة مكثَّرة قبل التدريب.
في الخلاصة
- التكثير يخلق أشكالًا صناعية من الصور الأصلية، فيُدرَّب النموذج على تجاهل الاختلافات غير المهمّة (الإضاءة والزاوية والموقع).
- الأدوات الأساسية: انقلابات واقتصاصات وتدويرات صغيرة وتغيّرات ألوانية؛ والمتقدّمة: Cutout وMixup لدفع الشبكة إلى تمثيلات أقوى.
- التطبيق في الطيران كطبقات Keras هو المعيار: تُطبَّق فقط في التدريب، لا تحتاج إلى إدارة يدوية، ومختلفة في كلّ حقبة.
- حفظ المعنى شرط لا يُتنازل عنه: لا تدوير كامل للأرقام، لا انقلاب رأسي للوجوه، لا تعديل ألوان للصور الطبّية؛ راجع كلّ تكثير بصريًا قبل استخدامه.
الوحدة التالية: التعلّم بالنقل والضبط الدقيق التدريجي، حيث نجمع بين التكثير وشبكة ResNet50 مُدرَّبة مسبقًا للحصول على دقّة تفوق 92 % على مجموعة النفايات.