انتقل إلى المحتوى الرئيسي

الوحدة 6 — الاستدعاءات: نقاط الحفظ والتوقّف المبكر وجدولة معدّل التعلّم

الاستدعاء كائن يُنادِيه fit في لحظات محدَّدة: بدء الحقبة ونهايتها، وبدء الحزمة ونهايتها، وبدء التدريب ونهايته. وهو الوسيلة للتصرّف في تدريبٍ جارٍ دون إعادة كتابة حلقته. وثلاثة استدعاءات تكفي لتغطية جُلّ الحاجات، وإعداداتها الافتراضية نادرًا ما تكون الصحيحة.

احفظ أفضل حالة، لا الأخيرة

تدريبٌ يستغرق ساعات ويتوقّف بلا نقطة حفظ هو تدريبٌ ضائع. لكنّ الرهان الأساسي في مكانٍ آخر: فنموذج الحقبة الأخيرة قلّما يكون الأفضل.

from tensorflow import keras

point_de_controle = keras.callbacks.ModelCheckpoint(
filepath="modeles/meilleur.keras",
monitor="val_loss",
mode="min",
save_best_only=True,
save_weights_only=False,
verbose=1,
)

الإعداد الذي يصنع الفرق هو save_best_only=True. فبغيره يُطمَس الملفّ في كلّ حقبة، وتُقابل الكتابة الأخيرة الحالةَ النهائية — وهي غالبًا مُفرِطة في التعلّم. أمّا معه فيحوي الملفّ الحقبةَ التي كانت فيها المتابعة في أفضل مقاييسها.

يسير monitor وmode معًا ويتناقضان بسهولة. فمتابعة val_loss بـmode="max" تحفظ الأسوأ، ولا يُصدر أي تنبيهٍ بذلك. القاعدة: الخسارة تُصغَّر، والدقّة تُكبَّر. وعند الشكّ يستنتج mode="auto" الاتّجاه من اسم المقياس.

أوقف حين تكفّ المتابعة عن التقدّم

يقطع EarlyStopping التدريب حين يستوي المقياس المُتابَع، ممّا يُوفّر وقت الحساب ويحدّ من الإفراط في التعلّم.

arret = keras.callbacks.EarlyStopping(
monitor="val_loss",
patience=10,
min_delta=1e-4,
restore_best_weights=True,
verbose=1,
)

restore_best_weights=True لا غنى عنه وقيمته الافتراضية False. فبغيره يتوقّف التدريب بعد patience حقبةً من الاستواء، ويكون النموذج في الذاكرة نموذجَ آخر حقبة، أي عشر حقبات بعد أفضل نقطة. فأنت اكتشفت لحظة التوقّف الصحيحة واحتفظت بالنموذج السيّئ.

تُضبَط قيمة الصبر تبعًا لضجيج المنحنيات. فقصيرة جدًّا تقطع على تذبذبٍ عاديّ حين كانت الخسارة على وشك النزول. وطويلة جدًّا لا تنفع في شيء. والمقدار المعقول من خمسٍ إلى خمس عشرة حقبة، يُضبَط بعد ملاحظة تباين المتابعة.

يُحدّد min_delta ما يُعدّ تحسُّنًا. فبغيره يُعيد ربحٌ بمقدار 10810^{-8} تصفيرَ عدّاد الصبر ولا يقع التوقّف أبدًا.

استدعاءان يتناقضان

يجب على ModelCheckpoint وEarlyStopping متابعة المقياس نفسه. فمتابعة val_loss في أحدهما وval_accuracy في الآخر تُنتج توقّفًا تحكمه كمّية وحفظًا تحكمه كمّية أخرى: فلا يكون للملفّ المحفوظ صلة باللحظة التي توقّف فيها التدريب.

اخفض معدّل التعلّم في اللحظة المناسبة

معدّل ثابتٌ ينتهي إلى منع التقارب الدقيق: فتبقى الخطوات كبيرة على الاستقرار في الحدّ الأدنى. وهناك استراتيجيتان.

التفاعليّة تُراقب المتابعة وتخفض المعدّل حين تستوي:

reduction = keras.callbacks.ReduceLROnPlateau(
monitor="val_loss",
factor=0.5,
patience=5,
min_lr=1e-6,
verbose=1,
)

المُجدوَلة تتّبع منحنى مقرَّرًا سلفًا، مستقلًّا عن النتائج:

planification = keras.optimizers.schedules.CosineDecay(
initial_learning_rate=1e-3,
decay_steps=nb_epoques * pas_par_epoque,
alpha=0.01,
)
modele.compile(optimizer=keras.optimizers.Adam(planification), loss="mse")
المقاربةالميزةالحدّ
التفاعليّةتتكيّف مع السير الفعليتتفاعل بعد الهضبة، بتأخير
المُجدوَلةقابلة للتكرار، بلا فرط ضبط للصبرتشترط معرفة عدد الحقبات

صارت تناقصات جيب التمام هي العُرف في تدريب النماذج الكبيرة، وغالبًا ما تسبقها مرحلة تسخين لبضع مئات من الخطوات. تمنع هذه المرحلة تدميرَ المعلومة من الحزم الأولى بمعدّلٍ كاملٍ يُطبَّق على أوزانٍ حديثة التهيئة.

صبر ReduceLROnPlateau يجب أن يكون أدنى من صبر EarlyStopping

إن كان التوقّف المبكر أقلّ صبرًا من تخفيض المعدّل، انتهى التدريب قبل أوّل تخفيض ولم يخدم الاستدعاء في شيء. ونسبةٌ من اثنين إلى ثلاثة بينهما تعمل جيّدًا: صبر 5 للتخفيض، و12 للتوقّف.

اجمعها، واعرف الترتيب

modele.fit(
jeu_entrainement,
validation_data=jeu_validation,
epochs=200,
callbacks=[point_de_controle, arret, reduction,
keras.callbacks.CSVLogger("journal.csv")],
)

بهذه المجموعة من الاستدعاءات، لم تعد epochs=200 توقُّعًا بل حدًّا أعلى: فـEarlyStopping هو الذي يقرّر النهاية. وهذا هو التفكير السليم، وهو يُلغي فرط ضبطٍ عليك ضبطه.

تُنفَّذ الاستدعاءات بترتيب القائمة في نهاية كلّ حقبة. ووضع ModelCheckpoint أوّلًا يضمن وقوع الحفظ قبل أن يقطع استدعاءٌ آخر التدريب.

اكتب استدعاءك الخاص

الواجهة مفتوحة، واستدعاءٌ منزليّ يقع في أسطر قليلة.

class ArretSurSeuil(keras.callbacks.Callback):
def __init__(self, seuil=0.98):
super().__init__()
self.seuil = seuil

def on_epoch_end(self, epoque, logs=None):
logs = logs or {}
if logs.get("val_accuracy", 0) >= self.seuil:
print(f"\nSeuil atteint a l'epoque {epoque + 1}, arret.")
self.model.stop_training = True

يحوي القاموس logs مقاييس الحقبة، بالأسماء نفسها كما في السجلّ. ويُتيح self.model الوصول إلى النموذج كاملًا، وself.model.stop_training = True هو الآلية التي يستعملها EarlyStopping نفسه.

تمتدّ نقاط الدخول المتاحة من on_train_begin إلى on_predict_batch_end. لكن انتبه إلى الاستدعاءات لكلّ حزمة: فمعالجة مُكلفة في on_train_batch_end تُنفَّذ آلاف المرّات في الحقبة الواحدة، وقد تُهيمن على زمن التدريب.

في الخلاصة

  • لا فائدة من ModelCheckpoint إلّا بـ**save_best_only=True**؛ وإلّا احتوى الملفّ النهائيّ الحقبةَ الأخيرة، ونادرًا ما تكون هي الأفضل.
  • يشترط EarlyStopping restore_best_weights=True، وقيمته الافتراضية False: بغيره نكتشف لحظة التوقّف الصحيحة ونحتفظ بالنموذج السيّئ.
  • يمكن أن يكون تخفيض المعدّل تفاعليًّا بـReduceLROnPlateau أو مُجدوَلًا بتناقص جيب تمام؛ وفي الأولى يجب أن يبقى صبره أدنى من صبر التوقّف المبكر.
  • بهذه الاستدعاءات تصير epochs حدًّا أعلى لا توقُّعًا، ممّا يُلغي فرطَ ضبطٍ من قائمة الضبط.

الوحدة التالية: TensorBoard، لرؤية ما لا تقوله السجلّات.