In [ ]:
#@title Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

In [ ]:
#@title MIT License
#
# Copyright (c) 2017 François Chollet
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
# الإفراط و التفريط في تعلّم الآلة
Note: قامت مجموعة المتطوعين في مشروع Tensorflow بترجمة هذا المحتوى. نظرًا لأن هذه الترجمات تعتمد على قاعدة أقصى الجهد (best-effort) ، فلا نضمن أنها انعكاس دقيق وحديث [للمحتوى الرسمي باللغة الإنجليزية](https://www.tensorflow.org/?hl=en). إذا كانت لديك اقتراحات لتحسين هذه الترجمة ، يرجى إرسال "Pull request" إلى مشروع [tensorflow/docs-l10n](https://github.com/tensorflow/docs-l10n). للتطوع أو مراجعة ترجمات المجموعة يرجى ارسال ايميل إلى docs@tensorflow.org.
كالعادة ، سنستخدم في هذا الدّفتر التّفاعليّ واجهة برمجة التطبيقات `tf.keras` ، والتّي يمكنك معرفة المزيد عنها في [دليل Keras](https://www.tensorflow.org/guide/keras?hl=ar) الموجود على موقع TensorFlow . في المثالين السّابقين - [تصنيف النّصوص](https://www.tensorflow.org/tutorials/keras/text_classification_with_hub?hl=ar) و [التنبؤ بكفاءة الوقود](https://www.tensorflow.org/tutorials/keras/regression?hl=ar) - رأينا أن دقة النّموذج على بيانات التحقق (validation set) ستصل إلى ذروتها بعد عدّة حقبات (epochs) من التّدريب ، ومن ثم ستركد أو تبدأ في التناقص. و بعبارة أخرى، فإنّ نموذجنا سوف *يفرط في التعلّم* من بيانات التّدريب ، و نقول أيضا يفرط في التخصّص أو يفرط في التّناسب، فتتناسب معلماته (parameters) على هذه البيانات إلى درجة تخلّ بقدرته على العمل بشكل صحيح على بيانات جديدة. تَعَلُّمُ كيفية التعامل مع مشكلة الإفراط في التعلّم أمر في غاية الأهميّة في مجال تعلّم الآلة. على الرغم من أنه غالبًا ما يكون من الممكن تحقيق دقة عالية في *مجموعة التّدريب* ، ما نريده حقًا هو تطوير نماذج قادرة على أن تُعمّم جيدًا ما تعلّمته على بيانات لم تراها أثناء عمليّة التّدريب كتلك الموجودة في *مجموعة الاختبار*. في المقابل، عكس مشكلة الإفراط في التعلّم، هناك مشكلة *التفريط في التعلّم*. تحصُل مشكلة التفريط في التعلّم عندما لا يزال هناك مجال لتحسين أداء النموذج على بيانات الاختبار، إذا تمّ تدريبه لمدّة أطول. يمكن لهذه المشكلة أن تحدث لعدد من الأسباب: إذا كان النموذج بسيطا إلى حدّ أنّه ليس قادرا على تعلّم كُلّ تعقيدات البيانات و الأنماط الموجودة فيها، أو إذا كان مضبوطا بشكل مفرط (over-regularized)، أو ببساطة إذا لم يُدرّب لوقت كافي. وهذا يعني أن نموذج الشبكة العصبيّة المُدرّب لم يتعلم كُلّ الأنماط ذات الصلة (relevant patterns) بالمُهمّة المطلوبة منه رغم أنّها موجودة في بيانات التدريب. Note: يسمّى مفهوم ال overfitting بالعربيّة الإفراط في التعلّم أو الإفراط في التّناسب أو الإفراط في التخصّص. و يسمّى مفهوم ال underfitting التفريط في التعلّم أو التفريط في التناسب أو التفريط في التخصّص. إذا قمت بالتدريب لفترة طويلة جدًا ، فسيبدأ النموذج في ملاءمة وتعلم الأنماط من بيانات التدريب التي لا تُعمّم على بيانات الاختبار أو على بيانات جديدة. نحن بحاجة إلى تحقيق التوازن بين الحاجة إلى تدريب النموذج إلى فترة طويلة بما يكفي لتفادي مشكلة التفريط في التعلّم و لكن ليست طويلة بما يؤدي للوقوع في مشكلة الإفراط في التعلّم. لذا يُعدّ فهم كيفية تحديد العدد المناسب من حقبات التدريب، كما سنستكشف أدناه، مهارة مفيدة. للحيلولة دون الإفراط في التعلّم (أو التخصّص أو التناسب)، فإن أفضل حل هو استخدام بيانات تدريب أكثر اكتمالاً. يجب أن تغطي مجموعة البيانات النطاق الكامل للمُدخلات (inputs) التي من المتوقع أن يتعامل معها النموذج. لذا فإنّ البيانات الإضافية مفيدة فقط إذا كانت تغطي حالات جديدة و مثيرة للاهتمام للمدُخلات. إنّ النّموذج المُدرّب على بيانات أكثر اكتمالاً سيُعمم بشكل طبيعيّ ما تعلّمه بطريقة أفضل. عندما لا يكون ذلك ممكنًا ، فإن أفضل حلٍّ تالي هو استخدام تقنيات مثل تقنية الضبط (regularization). و التّي تضع قيودا على كميّة و نوع المعلومات التّي يمكن للنموذج تخزينها. و تعتمد هذه الطريقة على المبدأ التّالي: إذا كان نموذج الشبكة العصبية لا يستطيع حفظ سوى عدد صغير من الأنماط (patterns) الموجودة في البيانات، بسبب عمليّة الضبط، فإنّ عمليّة التحسين (optimization) ستجبر النموذج على التركير على أبرز الأنماط و أهمّها، و التّي لديها فرصة أفضل للتعميم الجيّد على بيانات جديدة. في هذا الدفتر التفاعليّ، سنستكشف العديد من تقنيات الضبط الشائعة ، ونستخدمها لتحسين نموذج تصنيف. Note: تسمّى عمليّة ال optimization بالعربيّة التّحسين أو الإستمثال. يُمكنك قراءة المزيد عنها في [هذا المقال](https://ar.wikipedia.org/wiki/استمثال_(رياضيات)).
## تجهيز بيئة العمل قبل البدأ، قم باستيراد الحزم الضرورية:

In [ ]:
import tensorflow as tf

from tensorflow.keras import layers
from tensorflow.keras import regularizers

print(tf.__version__)

In [ ]:
!pip install git+https://github.com/tensorflow/docs

import tensorflow_docs as tfdocs
import tensorflow_docs.modeling
import tensorflow_docs.plots

In [ ]:
from  IPython import display
from matplotlib import pyplot as plt

import numpy as np

import pathlib
import shutil
import tempfile

In [ ]:
logdir = pathlib.Path(tempfile.mkdtemp())/"tensorboard_logs"
shutil.rmtree(logdir, ignore_errors=True)
## مجموعة بيانات Higgs الهدف من هذا البرنامج التعليمي ليس تعلّم فيزياء الجُسيمات، لذلك لا تركّز كثيرا في تفاصيل مجموعة البيانات. تحتوي مجموعة البيانات هذه على 11000000 مثال، يحتوي كل منها على 28 خاصيّة (feature) ، و على تسمية فئة ثنائية (binary class label). Note: نستعمل في العربيّة كلمة خاصيّة أو ميزة للإشارة إلى مفهوم Feature المستعمل في تعلّم الآلة.

In [ ]:
gz = tf.keras.utils.get_file('HIGGS.csv.gz', 'http://mlphysics.ics.uci.edu/data/higgs/HIGGS.csv.gz')

In [ ]:
FEATURES = 28
يمكن استخدام فئة `tf.data.experimental.CsvDataset` لقراءة سجلات csv مباشرة من ملفّ مضغوط بتقنية gzip دون القيام بخطوة فكّ الضغط (decompression) بالطريقة التّالية.

In [ ]:
ds = tf.data.experimental.CsvDataset(gz,[float(),]*(FEATURES+1), compression_type="GZIP")
تقوم فئة قراءة سجلّات csv السابقة بإرجاع قائمة أعداد لكُلّ سِجلّ. و تعيد الوظيفة التّالية إدارج قائمة الأعداد في كُلّ سجلّ إلى زوجٍ مكوَّن من (feature_vector ، label). حيث أنّ feature_vector تحتوى على خصيّات المثال الموجود في السجلّ و يحتوى label على تسميته الحقيقية.

In [ ]:
def pack_row(*row):
  label = row[0]
  features = tf.stack(row[1:],1)
  return features, label
صُمِّمَ TensorFlow ليعمل بشكل أكثر فعاليّة على دفعات كبيرة من البيانات. لذا بدلاً من إستخراج أزواج البيانات باستعمال الوظيفة السابقة على كُلّ سجلّ على حدة، قم بتجهيز وظيفة `Dataset` أي وظيفة `مجموعة بيانات` جديدة تأخذ دفعات من البيانات متكوّنة من 10000 مثال، ثمّ تطبّق الوظيفة `pack_row` على كُلّ هذه الأمثلة في آن واحد، ثمّ تقسّم الدفعات إلى سجلاّت فردية مرّة أخرى. يمكنك القيام بذلك هكذا:

In [ ]:
packed_ds = ds.batch(10000).map(pack_row).unbatch()
ألق نظرة على بعض الأمثلة الموجودة في المتغيّر الجديد `packed_ds`. كما هو ظاهر، فإن قيم الميزات ليست معيّرة (not normalized) بشكل تامّ، و لكنّ حالتها هذه كافية لما نريد أن نتعلّمه في هذا الدرس.

In [ ]:
for features,label in packed_ds.batch(1000).take(1):
  print(features[0])
  plt.hist(features.numpy().flatten(), bins = 101)
ليكون وقت تدريب النماذج قصيرا نسبيّا حتّى تتمكّن من اتمام البرنامج التعليمي، استخدم أوّل 1000 عيّنة كمجموعة تحقّق، و ال10000 التّالية للتدريب:

In [ ]:
N_VALIDATION = int(1e3)
N_TRAIN = int(1e4)
BUFFER_SIZE = int(1e4)
BATCH_SIZE = 500
STEPS_PER_EPOCH = N_TRAIN//BATCH_SIZE
تسهّل طريقتا `Dataset.skip` و` Dataset.take` ذلك. في الوقت نفسه ، استخدم طريقة `Dataset.cache` للتأكد من أن قارئ البيانات لا يحتاج إلى إعادة قراءتها من الملف في كل حقبة من حقبات عمليّة التّدريب:

In [ ]:
validate_ds = packed_ds.take(N_VALIDATION).cache()
train_ds = packed_ds.skip(N_VALIDATION).take(N_TRAIN).cache()

In [ ]:
train_ds
تُرجع الطرق السابقة مجموعة بيانات متكوّنة من أمثلة فرديّة. استخدم طريقة `batch` لتحويل مجموعة البيانات إلى دفعات ذات حجم مناسب للتدريب. قبل إنشاء الدفعات، تذكّر بأن تخلط البيانات باستعمال `shuffle` و أن تسمح بتكرار السجلات لاتمام الدفعات باستعمال `repeat`.

In [ ]:
validate_ds = validate_ds.batch(BATCH_SIZE)
train_ds = train_ds.shuffle(BUFFER_SIZE).repeat().batch(BATCH_SIZE)
## برهنة الإفراط في التعلّم إن أبسط طريقة لمنع الإفراط في التعلّم هي البدء بنموذج صغير: أي نموذج به عدد صغير من المعلمات (parameters) التي يجب تعلّمها (و عددها الجمليّ يحدده عدد الطبقات و عدد الوحدات في كل طبقة). في التعلّم العميق، غالبًا ما يُشار إلى عدد المعلمات القابلة للتعلّم في النموذج باسم "سَعَةِ" النموذج (model's capacity). بديهيّا ، النموذج الذي يحتوي على المزيد من المعلمات سيكون له "سعة حفظ" أكبر ، وبالتالي سيكون قادرًا على استنتاج و حفظ ما يشبه قاموسا يَقرِنُ فيه بين عيّنات التدريب و أهدافها (تسمياتها). و لكن هذا القاموس لن تكون له أيّ قدرة على التعميم (generalization power). و هذا لن يكون مفيدا عند القيام بتنبؤات على بيانات لم يراها النموذج من قبل. ضع التّالي دائمًا في الاعتبار: تميل نماذج التعلّم العميق إلى ملاءمة بيانات التدريب بشكل جيّد ، ولكن التحدي الحقيقي هو قدرة النموذج على التعميم ، وليس قدرته على ملاءمة بيانات التدريب. من ناحية أخرى ، إذا كانت قدرة الشبكة على الحفظ محدودة، فلن تتمكن من تعلّم أو استنتاج قاموس الإقتران بين بيانات التدريب و تسميتها بسهولة. لذا، لتقليل خسارتها، سيتعين عليها تعلُّمُ تمثيلات مضغوطة (compressed representations) لها قوة تنبؤية أكبر. ولكن، في الوقت نفسه، إذا جعلت النموذج صغيرًا جدًا، فسيجد صعوبة في ملاءمة بيانات التدريب و التعلّم منها. لذا يجب إيجاد التوازن بين "السعة الزائدة" و "السعة غير الكافية" للنموذج. لسوء الحظ، لا توجد صيغة سحرية لتحديد الحجم أو البنية الصحيحة لنموذجك (من حيث عدد الطبقات ، أو الحجم المناسب لكل طبقة). سيكون عليك تجربة عددٍ من بِنَى الشبكات العصبيّةالمختلفة. للعثور على حجم نموذج مناسب، من الأفضل أن تبدأ بطبقات ومعلمات قليلة نسبيًا، ثم تبدأ في زيادة حجم الطبقات أو إضافة طبقات جديدة حتى ترى تناقصًا في عائدات هذه الزيادات على أداء النموذج في بيانات التحقّق. و يكون ذلك عن طريق مراقبة تطوّر قيمة دالّة الخسارة على بيانات التحقّق. ابدأ بنموذج بسيط باستخدام طبقات `layers.Dense` فقط كنموذجٍ مبدئي (baseline)، ثمّ أنشئ إصدارات جديدة من النموذج بزيادة عدد الطبقات أو عدد المعلمات فيها و قارن أداء كُلّ إصدار للنموذج مع أداء الإصدار السّابق.
### طريقة التدريب
تتدرّب العديد من النماذج بشكل أفضل إذا قمت بتقليل معدّل التعلّم (learning rate) بشكل تدريجي أثناء التدريب. استخدم `optimizers.schedules` لتقليل معدّل التعلّم بمرور الوقت:

In [ ]:
lr_schedule = tf.keras.optimizers.schedules.InverseTimeDecay(
  0.001,
  decay_steps=STEPS_PER_EPOCH*1000,
  decay_rate=1,
  staircase=False)

def get_optimizer():
  return tf.keras.optimizers.Adam(lr_schedule)
يحدد الكود أعلاه "جدولا زمنيا" باستعمال الوظيفة `schedules.InverseTimeDecay` لخفض معدل التعلّم بشكل زائدي (hyperbolically) إلى 1/2 (نصف) القيمة الأولى لمعدّل التعلّم عند بلوغ 1000 حقبة، و إلى 1/3 (ثلث) القيمة الأولى عند بلوغ 2000 حقبة و هكذا دواليك.

In [ ]:
step = np.linspace(0,100000)
lr = lr_schedule(step)
plt.figure(figsize = (8,6))
plt.plot(step/STEPS_PER_EPOCH, lr)
plt.ylim([0,max(plt.ylim())])
plt.xlabel('Epoch')
_ = plt.ylabel('Learning Rate')
سيستخدم كل نموذج في هذا الدرس نفس إعدادات التدريب. لذا قم بإعدادها بطريقة قابلة لإعادة الاستخدام ، بدءًا من قائمة الوظائف القابلة للإستدعاء أتوماتكيّا (callbacks). تمتدّ عمليّة التدريب في هذا الدّرس لعدّة حِقَبٍ قصيرة (epochs). لتقليل الضوضاء الناتجة عن تسجيل (logging) معلومات الإشراف على التدريب، استخدم `tfdocs.EpochDots` و التّي، ببساطة، ستضع نقطة "." لتمثّل كُلّ حقبة، و مجموعة من المقاييس كل 100 حقبة من التّدريب. بعد ذلك، قم بتضمين الوظيفة `callbacks.EarlyStopping` في مجموعة الوظائف القابلة للإستدعاء (callbacks) و التّى ستقوم بإيقاف التّدريب مبكّرا، عند تحقق بعض الشروط، لتجنّب أوقات التّدريب الطويلة و غير الضروريّة. لا حظ أنّ هذه الوظيفة سوف تُراقب قيمة المتغيّر `val_binary_crossentropy`، و ليس المتغيّر `val_loss`. سنرى لاحقّا أنّ هذا الاختلاف مهم. استخدم `callbacks.TensorBoard` لإنشاء سجلاّت تمكّنك من الإشراف على عمليّة التّدريب و مراقبة تقدّمها باستعمال TensorBoard.

In [ ]:
def get_callbacks(name):
  return [
    tfdocs.modeling.EpochDots(),
    tf.keras.callbacks.EarlyStopping(monitor='val_binary_crossentropy', patience=200),
    tf.keras.callbacks.TensorBoard(logdir/name),
  ]
وبالمثل ، سيستخدم كل نموذج نفس الإعدادات في الوظيفتان `Model.compile` و` Model.fit`:

In [ ]:
def compile_and_fit(model, name, optimizer=None, max_epochs=10000):
  if optimizer is None:
    optimizer = get_optimizer()
  model.compile(optimizer=optimizer,
                loss=tf.keras.losses.BinaryCrossentropy(from_logits=True),
                metrics=[
                  tf.keras.losses.BinaryCrossentropy(
                      from_logits=True, name='binary_crossentropy'),
                  'accuracy'])

  model.summary()

  history = model.fit(
    train_ds,
    steps_per_epoch = STEPS_PER_EPOCH,
    epochs=max_epochs,
    validation_data=validate_ds,
    callbacks=get_callbacks(name),
    verbose=0)
  return history
### نموذج بالغ الصّغر
ابدأ بتدريب النموذج التّالي:

In [ ]:
tiny_model = tf.keras.Sequential([
    layers.Dense(16, activation='elu', input_shape=(FEATURES,)),
    layers.Dense(1)
])

In [ ]:
size_histories = {}

In [ ]:
size_histories['Tiny'] = compile_and_fit(tiny_model, 'sizes/Tiny')
تحقق الآن من أداء النموذج:

In [ ]:
plotter = tfdocs.plots.HistoryPlotter(metric = 'binary_crossentropy', smoothing_std=10)
plotter.plot(size_histories)
plt.ylim([0.5, 0.7])
### نموذج صغير
لمعرفة ما إذا كان بإمكانك التغلّب على أداء النّموذج بالغ الصّغر السّابق، قم بتدريب بعض النماذج الأكبر تدريجيًا. جرّب إضافة طبقتين مخفيّتين (hidden layers) تحتوى كُلٌّ منهما على 16 وحدة:

In [ ]:
small_model = tf.keras.Sequential([
    # `input_shape` is only required here so that `.summary` works.
    layers.Dense(16, activation='elu', input_shape=(FEATURES,)),
    layers.Dense(16, activation='elu'),
    layers.Dense(1)
])

In [ ]:
size_histories['Small'] = compile_and_fit(small_model, 'sizes/Small')
### نموذج متوسط
جرب الآن إضافة 3 طبقات مخفية مع 64 وحدة لكل منها:

In [ ]:
medium_model = tf.keras.Sequential([
    layers.Dense(64, activation='elu', input_shape=(FEATURES,)),
    layers.Dense(64, activation='elu'),
    layers.Dense(64, activation='elu'),
    layers.Dense(1)
])
وقم بتدريب النموذج باستخدام نفس البيانات:

In [ ]:
size_histories['Medium']  = compile_and_fit(medium_model, "sizes/Medium")
###نموذج كبير كتمرين ، يمكنك إنشاء نموذج أكبر ، و مراقبته للنظر في سرعة بدءه في الإفراط في التناسب (أي الإفراط في التعلّم أو التخصصّ). بعد ذلك ، دعنا نضيف إلى مقارنة النماذج هذه شبكة ذات سعة أكبر بكثير ، أكثر مما تتطلبه المشكلة:

In [ ]:
large_model = tf.keras.Sequential([
    layers.Dense(512, activation='elu', input_shape=(FEATURES,)),
    layers.Dense(512, activation='elu'),
    layers.Dense(512, activation='elu'),
    layers.Dense(512, activation='elu'),
    layers.Dense(1)
])
ومرة أخرى ، قم بتدريب النموذج باستخدام نفس البيانات:

In [ ]:
size_histories['large'] = compile_and_fit(large_model, "sizes/large")
### رسم تطوّر قيمة دالّة الخسارة على بيانات التدريب و بيانات التحقّق
تُظهر الخطوط المتواصلة تطوّر الخسارة على بيانات التدريب، وتظهر الخطوط المتقطعة تطوّر الخسارة على بيانات التحقق (تذكير: كُلّما كانت الخسارة أقّل على بيانات التحقّق كُلّما كان النموذج أفضل).
في حين أن بناء نموذج أكبر يعني أنّ هذا النموذج سيكون لديه المزيد من القوة و سعة الإستعاب للتعلّم ، و لكن إذا لم تكن هذه القوة مقيّدة بطريقة ما، فيمكن بسهولة لهذا النموذج أن يُفرط في التعلّم من مجموعة التدريب و تتناسب معلماته (parameters) معها. في هذا المثال، عادةً، ما يتمكن النموذج `"بالغ الصّغر"` فقط من تجنب الوقوع في مشكلة الإفراط في التخصّص، وكل نموذج من النماذج الأكبر يقع في هذه المشكلة بسرعة أكبر كُلّما كان النموذج أكبر. تصبح هذه الظّاهرة شديدة التأثير على النموذج الكبير إلى حدّ أنّنا نحتاج إلى استعمال مقياس لوغاريتمي (log-scale) في أيّ رسم بياني حتّى نتمكّن من رؤية و مقارنة تطوّر الخسارة على بيانات التدريب و بيانات التحقّق. هذا واضح إذا قمت برسم ومقارنة مقاييس التحقق بمقاييس التدريب. و عند المقارنة يجب أخذ النقاط التّالية في عين الإعتبار: * من الطبيعي أن يكون هناك اختلاف بسيط في قيم مقياس ما بين مجموعتَي البيانات (التدريب و التحقّق). * إذا كان كلا المقياسين يتحركان في نفس الاتجاه ، فكل شيء على ما يرام. * إذا بدأ مقياس التحقق في الركود بينما يستمر مقياس التدريب في التحسن ، فربما تكون على وشك الإفراط في التعلّم. * إذا كان مقياس التحقق يسير في الاتجاه الخاطئ، فمن الواضح أن النموذج مفرط في التخصّص.

In [ ]:
plotter.plot(size_histories)
a = plt.xscale('log')
plt.xlim([5, max(plt.xlim())])
plt.ylim([0.5, 0.7])
plt.xlabel("Epochs [Log Scale]")
Note: اِستخدمت جميع دورات التدريب المذكورة أعلاه الوظيفة القابلة للاستدعاء `callbacks.EarlyStopping` و التّي تمكّن من الإيقاف المبكّر للتدريب. و هي تقوم بإنهاء عمليّة التدريب بمجرّد أن يتضّح أنّ النّموذج لم يعد يحرز أي تقّدم بمواصلة عمليّة التدريب.
### عرض سجلاّت الإشراف على التدريب في TensorBoard جميع هذه النماذج كتبت سجلات TensorBoard أثناء التدريب. يمكنك أن تفتح عارض TensorBoard مضمّن داخل هذا الدّفتر التفاعلي هكذا:

In [ ]:
#docs_infra: no_execute
%tensorboard --logdir {logdir}/sizes
يمكنك عرض [نتائج إحدى دورات التدريب السابقة](https://tensorboard.dev/experiment/vW7jmmF9TmKmy3rbheMQpw/#scalars&_smoothingWeight=0.97) لهذا الدفتر التّفاعلي على موقع [TensorBoard.dev](https://tensorboard.dev/). يُمكّن موقع TensorBoard.dev من استضافة وتتبع ومشاركة تجارب تعلّم الآلة (ML) مع الجميع. يتم تضمينه أيضًا في `` لتيسير التّعامل مع TensorBoard في هذا الدّفتر التفاعليّ:

In [ ]:
display.IFrame(
    src="https://tensorboard.dev/experiment/vW7jmmF9TmKmy3rbheMQpw/#scalars&_smoothingWeight=0.97",
    width="100%", height="800px")
إذا كنت ترغب في مشاركة نتائج TensorBoard لتجربتك، فيمكنك تحميل السجلات إلى [TensorBoard.dev](https://tensorboard.dev/) عن طريق نسخ ما يلي في خلية تعليمات برمجية في هذا الدفتر. Note: تتطلب هذه الخطوة حسابًا على Google.
!tensorboard dev upload --logdir  {logdir}/sizes
Caution: تنفيذ الأمر البرمجي السّابق لن ينتهي من تلقاء نفسه. فقد تم تصميمه لتحميل نتائج التجارب التّي يمكن أن تستمرّ لوقت طويل. لذا، و بمجرد تحميل بياناتك، تحتاج إلى إيقافه باستخدام خيار "interrupt execution" (إيقاف التنفيذ) الموجود في الدّفتر التفاعليّ.
## استراتيجيات منع الإفراط في التعلّم
قبل الدخول في محتوى هذا القسم، انسخ سجلات التدريب من النموذج `"الصغير جدّا"` أعلاه، لاستخدامه كخط مرجعي للمقارنة.

In [ ]:
shutil.rmtree(logdir/'regularizers/Tiny', ignore_errors=True)
shutil.copytree(logdir/'sizes/Tiny', logdir/'regularizers/Tiny')

In [ ]:
regularizer_histories = {}
regularizer_histories['Tiny'] = size_histories['Tiny']
### إضافة ضبط الأوزان (weight regularization)
قد تكون على دراية بمبدأ Occam's Razor و الذّي يقول: باعتبار شرحين لشيء ما، من المرجّح أن يكون التفسير "الأبسط" هو الأصحّ، أي التفسير الذي يَستعمل أقلّ قدر من الافتراضات المسبقة (assumptions). ينطبق هذا أيضًا على النماذج التي تتعلمها الشبكات العصبية و يكمُن وجه المقارنة في التّالي: إذا أخذنا بعض بيانات التدريب و تصميم شبكة عصبيّة ما، فهناك مجموعات متعددة من قيم الأوزان (أي نماذج متعددة) التّي يُمكنها أن تفسّر البيانات، والنماذج الأبسط أقل عرضة لمشكلة الإفراط في التناسب من النماذج المعقدة. في هذا السياق، "النموذج البسيط" هو نموذج يكون توزيع قيم المعلمات (parameters) فيه إنتروبيا أقل - has less entropy - (أو إذا احتوى النموذج بأسره على معلمات أقلّ ، كما رأينا في القسم السّابق من هذا الدّرس). وبالتالي فإن الطريقة الشائعة لتخفيف الإفراط في التعلّم هي وضع قيود على تعقيد نموذج الشبكة العصبيّة عن طريق إجبار أوزانها على أخذ قيم صغيرة فقط، مما يجعل توزيع قيم الأوزان "أكثر انتظامًا". وهذا ما يسمى "ضبط الوزن" (weight regularization)، ويتم ذلك عن طريق إضافة تكلفة مرتبطة بالأوزان الكبيرة إلى دالّة الخسارة في نموذج الشبكة. تأتي هذه التكلفة في صيغتين: * [الضبط بطريقة L1](https://developers.google.com/machine-learning/glossary/#L1_regularization)، حيث تتناسب التكلفة المضافة مع القيمة المطلقة للأوزان ( ولذلك تسمّى أيضا "معيار L1" للأوزان). * [الضبط بطريقة L2](https://developers.google.com/machine-learning/glossary/#L2_regularization)، حيث تتناسب التكلفة المضافة مع مربع قيمة الأوزان (أي ما يسمى "معيار L2" التربيعي للأوزان). و تسمّى هذه الطريقة أيضا "اضمحلال الوزن" (weight decay) في سياق مجال الشبكات العصبيّة. لا تدع إختلاف الأسماء يربكك: فمن منطلق رياضيّ اضمحلال الوزن يساوي تماما طريقة الضبط باستعمال معيار L2. يؤدي الضبط بطريقة L1 إلى دفع الأوزان إلى الصفر حرفيّا و هو ما يُنتج نموذجًا قليل الكثافة - و يسمّى أيضا نموذجا متناثرًا (sparse model). وأمّا طريقة الضبط باستعمال L2 ستُعاقب الأوزان دون جعلها متناثرة أو قليلة الكثافة و ذلك لأنّ قيمة هذه "التكلفة الزائدة" أو العقوبة ستكون قريبة إلى الصفر للأوزان الصغيرة. و هذا أحد الأسباب التّي تجعل طريقة L2 أكثر شيوعا. في `tf.keras`، ضبط الأوزان يتمّ عن طريق تمرير وظائف الضبط إلى طبقات شبكة عصبيّة ما كإعداد من إعداداتها. فلنضف ضبط الأوزان بطريقة L2 لنموذجنا ليصير كالتّالي:

In [ ]:
l2_model = tf.keras.Sequential([
    layers.Dense(512, activation='elu',
                 kernel_regularizer=regularizers.l2(0.001),
                 input_shape=(FEATURES,)),
    layers.Dense(512, activation='elu',
                 kernel_regularizer=regularizers.l2(0.001)),
    layers.Dense(512, activation='elu',
                 kernel_regularizer=regularizers.l2(0.001)),
    layers.Dense(512, activation='elu',
                 kernel_regularizer=regularizers.l2(0.001)),
    layers.Dense(1)
])

regularizer_histories['l2'] = compile_and_fit(l2_model, "regularizers/l2")
استعمال الدّالة `l2` بهذا الشكل `l2(0.001)` يعني أن كل معامل (coefficient) في مصفوفة أوزان طبقة ما، سيضيف قيمة خسارة إضافيّة إلي إجماليّ خسارة الشبكة باستعمال المعادلة التّالية: `0.001 * weight_coefficient_value**2`. هذا هو السبب في أننا نراقب `binary_crossentropy` مباشرة، لأنه لا يحتوي على مكون الضبط (regularization component). لذا ، فإن النموذج `"الكبير"` سيكون أداءه أفضل باستعمال الضبط بمعيار L2 كعقوبة إضافيّة مع إجماليّ قيمة دالّة الخسارة:

In [ ]:
plotter.plot(regularizer_histories)
plt.ylim([0.5, 0.7])
كما ترى ، فإن النموذج المضبوط (regularized model) باستعمال معيار "L2" أصبح الآن أكثر قدرة على المنافسة مع النموذج الأوّل `"بالغ الصّغر"`. هذا النموذج `"الكبير"` المضبوط ب "L2" هو أيضا أكثر مقاومة و صمودا أمام مشلكة الإفراط في التعلّم مقارنة بالنموذج `"الكبير"` السّابق الذّي لم يحتوي على ضبط "L2" و ذلك على الرغم من احتواء كلا النموذجان على نفس عدد .المعلمات
#### معلومات إضافية هناك شيئان مهمان يجب ملاحظتهما حول هذا النوع من الضبط (regularization). **أولاً:** إذا كنت بصدد كتابة حلقة التدريب (training loop) الخاصة بك ، فأنت بحاجة إلى أن تستخرج من النموذج خسائره المتعلّقة بالضبط (regularization losses). و يمكن القيام بذلك هكذا:

In [ ]:
result = l2_model(features)
regularization_loss=tf.add_n(l2_model.losses)
**ثانيًا:** طريقة استعمال الضبط السابقة تعتمد على إضافة عقوبات للأوزان (weight penalties) إلى القيمة الجمليّة للخسارة، ثمّ استعمال أي إجراء من إجراءات التحسين المعهودة (مثل gradient descent) بعد ذلك. هناك نهجٌ ثانٍ لاستعمال الضبط يقوم على تشغيل المحسّن فقط على الخسارة الأولية (من دون القيمة المضافة من عمليّة الضبط)، وبعد ذلك أثناء تطبيق خطوة إعادة حساب قيم الأوزان، يقوم المحسّن بحسابات إضافية تؤدّي إلى اضمحلال الأوزان إلى درجة ما. هذه الطريقة التّي تؤدّي إلى "إضمحلال الأوزان بشكل منفصل" ("Decoupled Weight Decay") يمكن إيجادُها في بعض المحسّنات (optimizers) مثل `optimizers.FTRL` و` Optizers.AdamW`.
### الإسقاط العشوائي (Dropout) تقنية الإسقاط العشوائي أو Dropout هي واحدة من أكثر تقنيات الضبط فاعليّة و هي شائعة الاستعمال خاصّة لضبط نماذج الشبكات العصبيّة. وقع تطويرها من قبل Geoffrey Hinton و طلبته في جامعة تُورنتو الكنديّة. التفسير البديهي لطريقة عمل تقنيّة الإسقاط العشوائي (dropout) هي أنّه باستعمال هذه التقنيّة فإنّ كُلّ خليّة عصبيّة ، أو عقدة، من الشبكة العصبيّة ستستنتج بأنّها لا تستطيع أن تعتمد على مُخرجات (outputs) الخلايا الأخرى، فتضطرّ إلى أن تستخرج بنفسها أهمّ الميزات و الخصائص الموجودة في البيانات التّى تصل إليها و التّى تمكّنها من العمل بشكل أصحّ دون الإعتماد على غيرها. يتمثّل الإسقاط العشوائي، المطبّق على طبقة شبكة عصبيّة ما، في تجاهل بعض الخاصّيات المُخرجة (output features) من طبقة ما بطريقة عشوائية، و نعنى بتجاهلها أو إسقاطها تحويل قيمتها إلى صفر. لنفترض كمثال أنّ لدينا طبقة تخرج عادة متوجّهًا (vector) كالتّالي `[0.2, 0.5, 1.3, 0.8, 1.1]` بعد إدخال مثال من البيانات أثناء عمليّة التدريب؛ بعض تطبيق تقنية الإسقاط العشوائي، سيحتوى المتوجّه السابق على عدّة أصفار موزعة بشكل عشوائي، مثال: `[0, 0.5, 1.3, 0, 1.1]`. نسمّي "معدّل الإسقاط" الجزء من الميزات، في طبقة ما، الذّي يتمّ تجاهله أو استبعاده (بتحويله إلى أصفار)؛ و تكون قيمة هذا المعدّل عادة بين 0.2 و 0.5. لا يتمّ استخدام تقنية الإسقاط العشوائي عند اختبار النموذج، أي لا يتمّ استبعاد أيٍّ من مُخرجات خلايا أي طبقة من الشبكة، بدلاً من ذلك يتمّ تصغير قيم المُخرجات بعامل يساوي قيمة "معدّل الإسقاط" السّابق حتّى نأخذ في عين الإعتبار أنّ عدد خلايا الشبكة النشطة عند الاختبار أكبر من عددها عند التدريب. في `tf.keras` يمكنك استعمال تقنية الإسقاط العشوائي (dropout) عبر إضافة طبقة Dropout و التّى يتمّ تطبيقها على إخراج الطبقة السّابقة لها مباشرة. لنضف طبقتي Dropout لشبكتنا لنرى مدى نجاحهما في الحدّ من مشكلة الإفراط في التعلّم:

In [ ]:
dropout_model = tf.keras.Sequential([
    layers.Dense(512, activation='elu', input_shape=(FEATURES,)),
    layers.Dropout(0.5),
    layers.Dense(512, activation='elu'),
    layers.Dropout(0.5),
    layers.Dense(512, activation='elu'),
    layers.Dropout(0.5),
    layers.Dense(512, activation='elu'),
    layers.Dropout(0.5),
    layers.Dense(1)
])

regularizer_histories['dropout'] = compile_and_fit(dropout_model, "regularizers/dropout")

In [ ]:
plotter.plot(regularizer_histories)
plt.ylim([0.5, 0.7])
من الواضح من الرسم البياني أنّ تقنيتي الضبط المدروستان سابقا يحسّنان أداء النموذج `"الكبير"`. و لكن، رغم ذلك، لا يتخطّى أداء النموذج الكبير أداء النموذج بالغ الصّغر الذّي نستعمله كخطّ مرجعي. في التّالي، سنجرّب تقنيتي الضبط معًا لنرى إن كان ذلك سيؤدّي إلي تحسين أداء النموذج.
### تجربة تقنتي الضبط L2 و Dropout معًا

In [ ]:
combined_model = tf.keras.Sequential([
    layers.Dense(512, kernel_regularizer=regularizers.l2(0.0001),
                 activation='elu', input_shape=(FEATURES,)),
    layers.Dropout(0.5),
    layers.Dense(512, kernel_regularizer=regularizers.l2(0.0001),
                 activation='elu'),
    layers.Dropout(0.5),
    layers.Dense(512, kernel_regularizer=regularizers.l2(0.0001),
                 activation='elu'),
    layers.Dropout(0.5),
    layers.Dense(512, kernel_regularizer=regularizers.l2(0.0001),
                 activation='elu'),
    layers.Dropout(0.5),
    layers.Dense(1)
])

regularizer_histories['combined'] = compile_and_fit(combined_model, "regularizers/combined")

In [ ]:
plotter.plot(regularizer_histories)
plt.ylim([0.5, 0.7])
من الواضح أنّ النموذج الحالي الذّي يستعمل تقنيتي الضبط هو الأفضل حتّى الآن.
### عرض سجلاّت الإشراف على التدريب في TensorBoard جميع هذه النماذج كتبت سجلات TensorBoard أثناء التدريب. يمكنك أن تفتح عارض TensorBoard مضمّن داخل هذا الدّفتر التفاعلي هكذا:
%tensorboard --logdir {logdir}/regularizers
يمكنك عرض [نتائج إحدى تجارب التدريب السابقة](https://tensorboard.dev/experiment/fGInKDo8TXes1z7HQku9mw/#scalars&_smoothingWeight=0.97) لهذا الدفتر التّفاعلي على موقع [TensorBoard.dev](https://tensorboard.dev/). يتم تضمينه أيضًا في `` لتيسير التّعامل مع TensorBoard في هذا الدّفتر التفاعليّ:

In [ ]:
display.IFrame(
    src="https://tensorboard.dev/experiment/fGInKDo8TXes1z7HQku9mw/#scalars&_smoothingWeight=0.97",
    width = "100%",
    height="800px")
تم تحميل هذا باستعمال الأمر التّالي:
!tensorboard dev upload --logdir  {logdir}/regularizers
## الاستنتاجات
للتلخيص: نجمع فيما يلي أكثر الطرق شيوعًا لمقاومة مشكلة الإفراط في التعلّم التّي قد تتعرّض إليها نماذج تعلّم الآلة في العموم و نماذج الشبكات العصبيّة بالخصوص: * احصل على المزيد من بيانات التدريب لجعل البيانات أكثر إكتمالاً. * تقليل سعة الشبكة عن طريق تقليل عدد طبقاتها و عدد الخلايا في كُلّ طبقة. * استعمال تقنية ضبط الأوزان (weight regularization). * استعمال تقنية الإسقاط العشوائي (Dropout). هناك طريقتان مهمتان لم يتمّ تناولهما في هذا الدليل وهما: * تكثير البيانات (data augmentation) * تعيير دُفعات البيانات (batch normalization) تذكّر أن كل طريقةٍ يمكنُ أن تساعد وحدها في تحسين أداء النّموذج ، ولكن غالبًا ما يكون الجمع بين طرق متعدّدة أكثر فعالية.