الوحدة 8 — التعلّم بالنقل مع نماذج مدرَّبة مسبقًا
تدريب شبكة رؤيةٍ من الصفر يستلزم ملايين الصور وأيّامًا من الحساب. أمّا التعلّم بالنقل فيبلغ نتائج أفضل ببضعة آلاف من الصور ودقائق قليلة. تُوضّح هذه الوحدة سبب نجاح ذلك، ثمّ كيفيّة تحقيقه دون الوقوع في المصيدة التي تُفسد نصف المحاولات.
لماذا تُعاد الطبقات الأولى؟
لا تبني شبكةٌ عميقة تمثيلًا واحدًا: بل تُكوّم عدّة تمثيلات، من الأعمّ إلى الأخصّ.
يبقى كاشف الحواف كاشفَ حواف سواء أكانت الصورة قطًّا أم صورةً إشعاعيّة للرئة. وهذا ما يُبرّر الاحتفاظ بأوزان شبكةٍ مدرَّبة على ImageNet واستبدال طرفها فقط.
حمِّل النموذج بلا رأسه
from tensorflow import keras
base = keras.applications.EfficientNetB0(
weights="imagenet",
include_top=False, # يُزيل رأس الأصناف الألف
input_shape=(224, 224, 3),
)
base.trainable = False # يُجمّد القاعدة كاملة
يُزيل include_top=False طبقةَ التصنيف الأصلية، وهي عديمة الفائدة ما دامت أصنافك ليست أصناف ImageNet. ويُجمّد trainable = False الأوزانَ: فلن تستقبل بعد الآن أيّ تدرّج.
ثمّ نُضيف رأسنا الخاصّ، بالواجهة الوظيفية من الوحدة 3:
from tensorflow.keras import layers
entree = keras.Input(shape=(224, 224, 3))
x = keras.applications.efficientnet.preprocess_input(entree)
x = base(x, training=False)
x = layers.GlobalAveragePooling2D()(x)
x = layers.Dropout(0.3)(x)
sortie = layers.Dense(nb_classes, activation="softmax")(x)
modele = keras.Model(entree, sortie)
الـpreprocess_input الخاصّ بكلّ عائلة نماذج ليس اختياريًّا. فكلّ معمارية تنتظر مدخلاتها في مجالٍ محدَّد — بعضها في ، وبعضها في ، وبعضها مُمركَز على متوسّطات ImageNet. وتقديم وحدات بكسلٍ خام من 0 إلى 255 لشبكةٍ تنتظر يُعطي نتائج ضعيفة بلا أيّ رسالة خطأ.
مصيدة BatchNormalization
هذا هو الخطأ الذي يُفسّر أغلبيّة عمليات النقل الفاشلة، وهو خطأ شبه غير مرئيّ.
تحوي طبقة BatchNormalization نوعين من الكمّيات: أوزان مُتعلَّمة، وإحصائيّات جارية — متوسّط وتباين مُتراكمان أثناء التدري ب الأصليّ. فـbase.trainable = False يُجمّد الأوزان، لكنّه لا يكفي لتجميد الإحصائيّات: فإن استُدعيت الطبقة في وضع التدريب استمرّت في تحديثها ببياناتك.
على مجموعةٍ صغيرة تنحرف هذه الإحصائيّات، وتبتعد عن التي جرى تحسين الأوزان المجمَّدة من أجلها، فتُدمّر التمثيل الذي كنّا نسعى إلى حفظه بالذات. والعرَض دقّة تستوي أو تتراجع مع أنّ كلّ شيء يبدو صحيحًا.
المخرج قائم في training=False في استدعاء base(x, training=False) أعلاه. فهو يفرض وضع الاستدلال على القاعدة كاملة، بما فيها الإحصائيّات.
يقرّر base.trainable = False أيّ الأوزان تتلقّى تدرّجًا. أمّا الوسيط training=False فيقرّر كيف تتصرّف الطبقات. وكلتاهما ضرورية ولا تُغني إحداهما عن الأخرى. فعلى قاعدةٍ تحوي تطبيعًا بالحزم — وهذه حال جميع المعماريات الحديثة تقريبًا — يُلغي إغفال الثانية جُلّ فائدة الأولى.
نظامان، بهذا الترتيب
المقاربة الفعّالة تجري على مرحلتين، والترتيب غير قابلٍ للتفاوض.
المرحلة 1، استخراج الخصائص. القاعدة مُجمَّدة، ولا يتدرّب سوى الرأس الجديد، بمعدّل تعلّم عاديّ.
modele.compile(
optimizer=keras.optimizers.Adam(1e-3),
loss="sparse_categorical_crossentropy",
metrics=["accuracy"],
)
modele.fit(jeu, validation_data=jeu_val, epochs=10)
المرحلة 2، الضبط الدقيق. نفكّ تجميد جزءٍ من القاعدة ونستأنف التدريب بمعدّل أضعف بكثير.
base.trainable = True
for couche in base.layers[:-30]: # لا يفكّ إلّا آخر 30 طبقة
couche.trainable = False
modele.compile(
optimizer=keras.optimizers.Adam(1e-5), # مئة مرّة أضعف
loss="sparse_categorical_crossentropy",
metrics=["accuracy"],
)
modele.fit(jeu, validation_data=jeu_val, epochs=10)
إعادة التصريف بعد تعديل trainable إلزاميّة: فبغيرها تبقى قائمة المتغيّرات القابلة للتدريب هي المُثبَّتة عند التصريف السابق، ولا يكون لفكّ التجميد أيّ أثر.
المعدّل المنخفض جدًّا يُفسَّر ببساطة. فالرأس الجديد المُهيَّأ عشوائيًّا يُنتج بدايةً تدرّجات هائلة. وتُطبَّق هذه التدرّجات على قاعدةٍ مدرَّبة مسبقًا بمعدّل عاديّ فتمحو في بضع حزم المعلومةَ المُتراكمة على ملايين الصور. وهذا أيضًا سبب وجوب أن تسبق المرحلة 1 المرحلة 2: فهي تجلب الرأس إلى حالةٍ معقولة قبل أن تتعرّض القاعدة.
كم من الطبقات نفكّ؟
| الحالة | الاستراتيجية |
|---|---|
| بيانات قليلة، مجال قريب | تجميد الكلّ، تدريب الرأس وحده |
| بيانات كثيرة، مجال قريب | فكّ الثلث الأخير |
| بيانات قليلة، مجال بعيد | فكّ الطبقات الوسطى، إبقاء الأولى مجمَّدة |
| بيانات كثيرة، مجال بعيد جدًّا | التفكير في تدريبٍ كامل |
الحالة «بيانات قليلة، مجال بعيد» هي الأدقّ، والتصوير الطبّي مثالها النمطيّ. فالطبقات الأولى تبقى نافعة، والأخيرة أخصّ من أن تُلائم أشياء ImageNet، ولا توجد بيانات كافية لإعادة تعلّم كلّ شيء. وفكّ الوسط هو الحلّ الوسط، ويُصادَق عليه تجريبيًّا.
في الخلاصة
- يعمل النقل لأنّ الطبقات الأولى تتعلّم أنماطًا شاملة — حوافّ ونُسُج — مستقلّة عن المهمّة الأصلية.
- يُزيل
include_top=Falseالرأس الأصلي، و**preprocess_input** الخاصّ بعائلة النماذج لا غنى عنه، إذ تنتظر كلّ معمارية مدخلاتها في مجالٍ محدَّد. - يُجمّد
trainable = Falseالأوزان لكن لا يُجمّد إحصائيّات التطبيع بالحزم: فبلاtraining=Falseعند الاستدعاء تنحرف وتُدمّر التمثيل المحفوظ. - استخراج الخصائص أوّلًا دائمًا، ثمّ الضبط الدقيق بمعدّل أضعف بمئة مرّة، مع إعادة التصريف بعد كلّ تغيير في
trainable.
الوحدة التالية: التدريب الموزّع، حين لا يعود المسرّع الواحد كافيًا.