الإفراط و التفريط في تعلّم الآلة

إفتح المحتوى على موقع TensorFlow.org تفاعل مع المحتوى على Google Colab إطّلع على المصدر في Github تنزيل الدّفتر

كالعادة ، سنستخدم في هذا الدّفتر التّفاعليّ واجهة برمجة التطبيقات tf.keras ، والتّي يمكنك معرفة المزيد عنها في دليل Keras الموجود على موقع TensorFlow .

في المثالين السّابقين - تصنيف النّصوص و التنبؤ بكفاءة الوقود - رأينا أن دقة النّموذج على بيانات التحقق (validation set) ستصل إلى ذروتها بعد عدّة حقبات (epochs) من التّدريب ، ومن ثم ستركد أو تبدأ في التناقص.

و بعبارة أخرى، فإنّ نموذجنا سوف يفرط في التعلّم من بيانات التّدريب ، و نقول أيضا يفرط في التخصّص أو يفرط في التّناسب، فتتناسب معلماته (parameters) على هذه البيانات إلى درجة تخلّ بقدرته على العمل بشكل صحيح على بيانات جديدة. تَعَلُّمُ كيفية التعامل مع مشكلة الإفراط في التعلّم أمر في غاية الأهميّة في مجال تعلّم الآلة. على الرغم من أنه غالبًا ما يكون من الممكن تحقيق دقة عالية في مجموعة التّدريب ، ما نريده حقًا هو تطوير نماذج قادرة على أن تُعمّم جيدًا ما تعلّمته على بيانات لم تراها أثناء عمليّة التّدريب كتلك الموجودة في مجموعة الاختبار.

في المقابل، عكس مشكلة الإفراط في التعلّم، هناك مشكلة التفريط في التعلّم. تحصُل مشكلة التفريط في التعلّم عندما لا يزال هناك مجال لتحسين أداء النموذج على بيانات الاختبار، إذا تمّ تدريبه لمدّة أطول. يمكن لهذه المشكلة أن تحدث لعدد من الأسباب: إذا كان النموذج بسيطا إلى حدّ أنّه ليس قادرا على تعلّم كُلّ تعقيدات البيانات و الأنماط الموجودة فيها، أو إذا كان مضبوطا بشكل مفرط (over-regularized)، أو ببساطة إذا لم يُدرّب لوقت كافي. وهذا يعني أن نموذج الشبكة العصبيّة المُدرّب لم يتعلم كُلّ الأنماط ذات الصلة (relevant patterns) بالمُهمّة المطلوبة منه رغم أنّها موجودة في بيانات التدريب.

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

للحيلولة دون الإفراط في التعلّم (أو التخصّص أو التناسب)، فإن أفضل حل هو استخدام بيانات تدريب أكثر اكتمالاً. يجب أن تغطي مجموعة البيانات النطاق الكامل للمُدخلات (inputs) التي من المتوقع أن يتعامل معها النموذج. لذا فإنّ البيانات الإضافية مفيدة فقط إذا كانت تغطي حالات جديدة و مثيرة للاهتمام للمدُخلات.

إنّ النّموذج المُدرّب على بيانات أكثر اكتمالاً سيُعمم بشكل طبيعيّ ما تعلّمه بطريقة أفضل. عندما لا يكون ذلك ممكنًا ، فإن أفضل حلٍّ تالي هو استخدام تقنيات مثل تقنية الضبط (regularization). و التّي تضع قيودا على كميّة و نوع المعلومات التّي يمكن للنموذج تخزينها. و تعتمد هذه الطريقة على المبدأ التّالي: إذا كان نموذج الشبكة العصبية لا يستطيع حفظ سوى عدد صغير من الأنماط (patterns) الموجودة في البيانات، بسبب عمليّة الضبط، فإنّ عمليّة التحسين (optimization) ستجبر النموذج على التركير على أبرز الأنماط و أهمّها، و التّي لديها فرصة أفضل للتعميم الجيّد على بيانات جديدة.

في هذا الدفتر التفاعليّ، سنستكشف العديد من تقنيات الضبط الشائعة ، ونستخدمها لتحسين نموذج تصنيف.

تجهيز بيئة العمل

قبل البدأ، قم باستيراد الحزم الضرورية:

import tensorflow as tf

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

print(tf.__version__)
2.3.0

!pip install -q git+https://github.com/tensorflow/docs

import tensorflow_docs as tfdocs
import tensorflow_docs.modeling
import tensorflow_docs.plots
WARNING: You are using pip version 20.2.2; however, version 20.2.3 is available.
You should consider upgrading via the '/tmpfs/src/tf_docs_env/bin/python -m pip install --upgrade pip' command.

from  IPython import display
from matplotlib import pyplot as plt

import numpy as np

import pathlib
import shutil
import tempfile

logdir = pathlib.Path(tempfile.mkdtemp())/"tensorboard_logs"
shutil.rmtree(logdir, ignore_errors=True)

مجموعة بيانات Higgs

الهدف من هذا البرنامج التعليمي ليس تعلّم فيزياء الجُسيمات، لذلك لا تركّز كثيرا في تفاصيل مجموعة البيانات. تحتوي مجموعة البيانات هذه على 11000000 مثال، يحتوي كل منها على 28 خاصيّة (feature) ، و على تسمية فئة ثنائية (binary class label).

gz = tf.keras.utils.get_file('HIGGS.csv.gz', 'http://mlphysics.ics.uci.edu/data/higgs/HIGGS.csv.gz')
Downloading data from http://mlphysics.ics.uci.edu/data/higgs/HIGGS.csv.gz
2816409600/2816407858 [==============================] - 136s 0us/step

FEATURES = 28

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

ds = tf.data.experimental.CsvDataset(gz,[float(),]*(FEATURES+1), compression_type="GZIP")

تقوم فئة قراءة سجلّات csv السابقة بإرجاع قائمة أعداد لكُلّ سِجلّ. و تعيد الوظيفة التّالية إدارج قائمة الأعداد في كُلّ سجلّ إلى زوجٍ مكوَّن من (feature_vector ، label). حيث أنّ feature_vector تحتوى على خصيّات المثال الموجود في السجلّ و يحتوى label على تسميته الحقيقية.

def pack_row(*row):
  label = row[0]
  features = tf.stack(row[1:],1)
  return features, label

صُمِّمَ TensorFlow ليعمل بشكل أكثر فعاليّة على دفعات كبيرة من البيانات.

لذا بدلاً من إستخراج أزواج البيانات باستعمال الوظيفة السابقة على كُلّ سجلّ على حدة، قم بتجهيز وظيفة Dataset أي وظيفة مجموعة بيانات جديدة تأخذ دفعات من البيانات متكوّنة من 10000 مثال، ثمّ تطبّق الوظيفة pack_row على كُلّ هذه الأمثلة في آن واحد، ثمّ تقسّم الدفعات إلى سجلاّت فردية مرّة أخرى. يمكنك القيام بذلك هكذا:

packed_ds = ds.batch(10000).map(pack_row).unbatch()

ألق نظرة على بعض الأمثلة الموجودة في المتغيّر الجديد packed_ds.

كما هو ظاهر، فإن قيم الميزات ليست معيّرة (not normalized) بشكل تامّ، و لكنّ حالتها هذه كافية لما نريد أن نتعلّمه في هذا الدرس.

for features,label in packed_ds.batch(1000).take(1):
  print(features[0])
  plt.hist(features.numpy().flatten(), bins = 101)
tf.Tensor(
[ 0.8692932  -0.6350818   0.22569026  0.32747006 -0.6899932   0.75420225
 -0.24857314 -1.0920639   0.          1.3749921  -0.6536742   0.9303491
  1.1074361   1.1389043  -1.5781983  -1.0469854   0.          0.65792954
 -0.01045457 -0.04576717  3.1019614   1.35376     0.9795631   0.97807616
  0.92000484  0.72165745  0.98875093  0.87667835], shape=(28,), dtype=float32)

png

ليكون وقت تدريب النماذج قصيرا نسبيّا حتّى تتمكّن من اتمام البرنامج التعليمي، استخدم أوّل 1000 عيّنة كمجموعة تحقّق، و ال10000 التّالية للتدريب:

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 للتأكد من أن قارئ البيانات لا يحتاج إلى إعادة قراءتها من الملف في كل حقبة من حقبات عمليّة التّدريب:

validate_ds = packed_ds.take(N_VALIDATION).cache()
train_ds = packed_ds.skip(N_VALIDATION).take(N_TRAIN).cache()
train_ds
<CacheDataset shapes: ((28,), ()), types: (tf.float32, tf.float32)>

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

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 لتقليل معدّل التعلّم بمرور الوقت:

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 حقبة و هكذا دواليك.

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')

png

سيستخدم كل نموذج في هذا الدرس نفس إعدادات التدريب. لذا قم بإعدادها بطريقة قابلة لإعادة الاستخدام ، بدءًا من قائمة الوظائف القابلة للإستدعاء أتوماتكيّا (callbacks).

تمتدّ عمليّة التدريب في هذا الدّرس لعدّة حِقَبٍ قصيرة (epochs). لتقليل الضوضاء الناتجة عن تسجيل (logging) معلومات الإشراف على التدريب، استخدم tfdocs.EpochDots و التّي، ببساطة، ستضع نقطة "." لتمثّل كُلّ حقبة، و مجموعة من المقاييس كل 100 حقبة من التّدريب.

بعد ذلك، قم بتضمين الوظيفة callbacks.EarlyStopping في مجموعة الوظائف القابلة للإستدعاء (callbacks) و التّى ستقوم بإيقاف التّدريب مبكّرا، عند تحقق بعض الشروط، لتجنّب أوقات التّدريب الطويلة و غير الضروريّة. لا حظ أنّ هذه الوظيفة سوف تُراقب قيمة المتغيّر val_binary_crossentropy، و ليس المتغيّر val_loss. سنرى لاحقّا أنّ هذا الاختلاف مهم.

استخدم callbacks.TensorBoard لإنشاء سجلاّت تمكّنك من الإشراف على عمليّة التّدريب و مراقبة تقدّمها باستعمال TensorBoard.

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:

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

نموذج بالغ الصّغر

ابدأ بتدريب النموذج التّالي:

tiny_model = tf.keras.Sequential([
    layers.Dense(16, activation='elu', input_shape=(FEATURES,)),
    layers.Dense(1)
])
size_histories = {}
size_histories['Tiny'] = compile_and_fit(tiny_model, 'sizes/Tiny')
Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense (Dense)                (None, 16)                464       
_________________________________________________________________
dense_1 (Dense)              (None, 1)                 17        
=================================================================
Total params: 481
Trainable params: 481
Non-trainable params: 0
_________________________________________________________________
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/ops/summary_ops_v2.py:1277: stop (from tensorflow.python.eager.profiler) is deprecated and will be removed after 2020-07-01.
Instructions for updating:
use `tf.profiler.experimental.stop` instead.
WARNING:tensorflow:Callbacks method `on_train_batch_end` is slow compared to the batch time (batch time: 0.0035s vs `on_train_batch_end` time: 0.0257s). Check your callbacks.

Epoch: 0, accuracy:0.4825,  binary_crossentropy:0.7767,  loss:0.7767,  val_accuracy:0.4760,  val_binary_crossentropy:0.7360,  val_loss:0.7360,  
....................................................................................................
Epoch: 100, accuracy:0.6002,  binary_crossentropy:0.6255,  loss:0.6255,  val_accuracy:0.5790,  val_binary_crossentropy:0.6260,  val_loss:0.6260,  
....................................................................................................
Epoch: 200, accuracy:0.6240,  binary_crossentropy:0.6140,  loss:0.6140,  val_accuracy:0.6060,  val_binary_crossentropy:0.6157,  val_loss:0.6157,  
....................................................................................................
Epoch: 300, accuracy:0.6378,  binary_crossentropy:0.6043,  loss:0.6043,  val_accuracy:0.6470,  val_binary_crossentropy:0.6061,  val_loss:0.6061,  
....................................................................................................
Epoch: 400, accuracy:0.6505,  binary_crossentropy:0.5962,  loss:0.5962,  val_accuracy:0.6500,  val_binary_crossentropy:0.5991,  val_loss:0.5991,  
....................................................................................................
Epoch: 500, accuracy:0.6628,  binary_crossentropy:0.5907,  loss:0.5907,  val_accuracy:0.6500,  val_binary_crossentropy:0.5945,  val_loss:0.5945,  
....................................................................................................
Epoch: 600, accuracy:0.6694,  binary_crossentropy:0.5869,  loss:0.5869,  val_accuracy:0.6760,  val_binary_crossentropy:0.5918,  val_loss:0.5918,  
....................................................................................................
Epoch: 700, accuracy:0.6740,  binary_crossentropy:0.5825,  loss:0.5825,  val_accuracy:0.6520,  val_binary_crossentropy:0.5917,  val_loss:0.5917,  
....................................................................................................
Epoch: 800, accuracy:0.6799,  binary_crossentropy:0.5801,  loss:0.5801,  val_accuracy:0.6480,  val_binary_crossentropy:0.5899,  val_loss:0.5899,  
....................................................................................................
Epoch: 900, accuracy:0.6747,  binary_crossentropy:0.5778,  loss:0.5778,  val_accuracy:0.6670,  val_binary_crossentropy:0.5882,  val_loss:0.5882,  
....................................................................................................
Epoch: 1000, accuracy:0.6825,  binary_crossentropy:0.5758,  loss:0.5758,  val_accuracy:0.6500,  val_binary_crossentropy:0.5903,  val_loss:0.5903,  
....................................................................................................
Epoch: 1100, accuracy:0.6763,  binary_crossentropy:0.5754,  loss:0.5754,  val_accuracy:0.6730,  val_binary_crossentropy:0.5871,  val_loss:0.5871,  
....................................................................................................
Epoch: 1200, accuracy:0.6832,  binary_crossentropy:0.5731,  loss:0.5731,  val_accuracy:0.6580,  val_binary_crossentropy:0.5880,  val_loss:0.5880,  
....................................................................................................
Epoch: 1300, accuracy:0.6828,  binary_crossentropy:0.5724,  loss:0.5724,  val_accuracy:0.6660,  val_binary_crossentropy:0.5870,  val_loss:0.5870,  
....................................................................................................
Epoch: 1400, accuracy:0.6867,  binary_crossentropy:0.5709,  loss:0.5709,  val_accuracy:0.6660,  val_binary_crossentropy:0.5866,  val_loss:0.5866,  
....................................................................................................
Epoch: 1500, accuracy:0.6865,  binary_crossentropy:0.5702,  loss:0.5702,  val_accuracy:0.6670,  val_binary_crossentropy:0.5860,  val_loss:0.5860,  
....................................................................................................
Epoch: 1600, accuracy:0.6879,  binary_crossentropy:0.5691,  loss:0.5691,  val_accuracy:0.6660,  val_binary_crossentropy:0.5865,  val_loss:0.5865,  
....................................................................................................
Epoch: 1700, accuracy:0.6845,  binary_crossentropy:0.5684,  loss:0.5684,  val_accuracy:0.6630,  val_binary_crossentropy:0.5853,  val_loss:0.5853,  
....................................................................................................
Epoch: 1800, accuracy:0.6876,  binary_crossentropy:0.5675,  loss:0.5675,  val_accuracy:0.6780,  val_binary_crossentropy:0.5842,  val_loss:0.5842,  
....................................................................................................
Epoch: 1900, accuracy:0.6885,  binary_crossentropy:0.5665,  loss:0.5665,  val_accuracy:0.6790,  val_binary_crossentropy:0.5842,  val_loss:0.5842,  
....................................................................................................
Epoch: 2000, accuracy:0.6892,  binary_crossentropy:0.5657,  loss:0.5657,  val_accuracy:0.6600,  val_binary_crossentropy:0.5849,  val_loss:0.5849,  
....................................................................................................
Epoch: 2100, accuracy:0.6895,  binary_crossentropy:0.5652,  loss:0.5652,  val_accuracy:0.6670,  val_binary_crossentropy:0.5838,  val_loss:0.5838,  
....................................................................................................
Epoch: 2200, accuracy:0.6898,  binary_crossentropy:0.5646,  loss:0.5646,  val_accuracy:0.6680,  val_binary_crossentropy:0.5838,  val_loss:0.5838,  
....................................................................................................
Epoch: 2300, accuracy:0.6865,  binary_crossentropy:0.5648,  loss:0.5648,  val_accuracy:0.6840,  val_binary_crossentropy:0.5823,  val_loss:0.5823,  
....................................................................................................
Epoch: 2400, accuracy:0.6913,  binary_crossentropy:0.5637,  loss:0.5637,  val_accuracy:0.6770,  val_binary_crossentropy:0.5828,  val_loss:0.5828,  
....................................................................................................
Epoch: 2500, accuracy:0.6914,  binary_crossentropy:0.5630,  loss:0.5630,  val_accuracy:0.6630,  val_binary_crossentropy:0.5840,  val_loss:0.5840,  
..............................................................................................

تحقق الآن من أداء النموذج:

plotter = tfdocs.plots.HistoryPlotter(metric = 'binary_crossentropy', smoothing_std=10)
plotter.plot(size_histories)
plt.ylim([0.5, 0.7])
(0.5, 0.7)

png

نموذج صغير

لمعرفة ما إذا كان بإمكانك التغلّب على أداء النّموذج بالغ الصّغر السّابق، قم بتدريب بعض النماذج الأكبر تدريجيًا.

جرّب إضافة طبقتين مخفيّتين (hidden layers) تحتوى كُلٌّ منهما على 16 وحدة:

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)
])
size_histories['Small'] = compile_and_fit(small_model, 'sizes/Small')
Model: "sequential_1"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense_2 (Dense)              (None, 16)                464       
_________________________________________________________________
dense_3 (Dense)              (None, 16)                272       
_________________________________________________________________
dense_4 (Dense)              (None, 1)                 17        
=================================================================
Total params: 753
Trainable params: 753
Non-trainable params: 0
_________________________________________________________________
WARNING:tensorflow:Callbacks method `on_train_batch_end` is slow compared to the batch time (batch time: 0.0035s vs `on_train_batch_end` time: 0.0512s). Check your callbacks.

Epoch: 0, accuracy:0.4916,  binary_crossentropy:0.6954,  loss:0.6954,  val_accuracy:0.4910,  val_binary_crossentropy:0.6822,  val_loss:0.6822,  
....................................................................................................
Epoch: 100, accuracy:0.6297,  binary_crossentropy:0.6045,  loss:0.6045,  val_accuracy:0.5760,  val_binary_crossentropy:0.6229,  val_loss:0.6229,  
....................................................................................................
Epoch: 200, accuracy:0.6650,  binary_crossentropy:0.5816,  loss:0.5816,  val_accuracy:0.6520,  val_binary_crossentropy:0.5978,  val_loss:0.5978,  
....................................................................................................
Epoch: 300, accuracy:0.6800,  binary_crossentropy:0.5708,  loss:0.5708,  val_accuracy:0.6670,  val_binary_crossentropy:0.5967,  val_loss:0.5967,  
....................................................................................................
Epoch: 400, accuracy:0.6872,  binary_crossentropy:0.5655,  loss:0.5655,  val_accuracy:0.6640,  val_binary_crossentropy:0.5975,  val_loss:0.5975,  
....................................................................................

نموذج متوسط

جرب الآن إضافة 3 طبقات مخفية مع 64 وحدة لكل منها:

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)
])

وقم بتدريب النموذج باستخدام نفس البيانات:

size_histories['Medium']  = compile_and_fit(medium_model, "sizes/Medium")
Model: "sequential_2"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense_5 (Dense)              (None, 64)                1856      
_________________________________________________________________
dense_6 (Dense)              (None, 64)                4160      
_________________________________________________________________
dense_7 (Dense)              (None, 64)                4160      
_________________________________________________________________
dense_8 (Dense)              (None, 1)                 65        
=================================================================
Total params: 10,241
Trainable params: 10,241
Non-trainable params: 0
_________________________________________________________________
WARNING:tensorflow:Callbacks method `on_train_batch_end` is slow compared to the batch time (batch time: 0.0036s vs `on_train_batch_end` time: 0.0531s). Check your callbacks.

Epoch: 0, accuracy:0.5008,  binary_crossentropy:0.6881,  loss:0.6881,  val_accuracy:0.5100,  val_binary_crossentropy:0.6675,  val_loss:0.6675,  
....................................................................................................
Epoch: 100, accuracy:0.7173,  binary_crossentropy:0.5223,  loss:0.5223,  val_accuracy:0.6620,  val_binary_crossentropy:0.6166,  val_loss:0.6166,  
....................................................................................................
Epoch: 200, accuracy:0.7945,  binary_crossentropy:0.4158,  loss:0.4158,  val_accuracy:0.6300,  val_binary_crossentropy:0.7199,  val_loss:0.7199,  
.........................................................

نموذج كبير

كتمرين ، يمكنك إنشاء نموذج أكبر ، و مراقبته للنظر في سرعة بدءه في الإفراط في التناسب (أي الإفراط في التعلّم أو التخصصّ). بعد ذلك ، دعنا نضيف إلى مقارنة النماذج هذه شبكة ذات سعة أكبر بكثير ، أكثر مما تتطلبه المشكلة:

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)
])

ومرة أخرى ، قم بتدريب النموذج باستخدام نفس البيانات:

size_histories['large'] = compile_and_fit(large_model, "sizes/large")
Model: "sequential_3"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense_9 (Dense)              (None, 512)               14848     
_________________________________________________________________
dense_10 (Dense)             (None, 512)               262656    
_________________________________________________________________
dense_11 (Dense)             (None, 512)               262656    
_________________________________________________________________
dense_12 (Dense)             (None, 512)               262656    
_________________________________________________________________
dense_13 (Dense)             (None, 1)                 513       
=================================================================
Total params: 803,329
Trainable params: 803,329
Non-trainable params: 0
_________________________________________________________________
WARNING:tensorflow:Callbacks method `on_train_batch_end` is slow compared to the batch time (batch time: 0.0041s vs `on_train_batch_end` time: 0.0583s). Check your callbacks.

Epoch: 0, accuracy:0.5068,  binary_crossentropy:0.8395,  loss:0.8395,  val_accuracy:0.4630,  val_binary_crossentropy:0.6880,  val_loss:0.6880,  
....................................................................................................
Epoch: 100, accuracy:1.0000,  binary_crossentropy:0.0023,  loss:0.0023,  val_accuracy:0.6580,  val_binary_crossentropy:1.8327,  val_loss:1.8327,  
....................................................................................................
Epoch: 200, accuracy:1.0000,  binary_crossentropy:0.0001,  loss:0.0001,  val_accuracy:0.6580,  val_binary_crossentropy:2.5090,  val_loss:2.5090,  
..........................

رسم تطوّر قيمة دالّة الخسارة على بيانات التدريب و بيانات التحقّق

تُظهر الخطوط المتواصلة تطوّر الخسارة على بيانات التدريب، وتظهر الخطوط المتقطعة تطوّر الخسارة على بيانات التحقق (تذكير: كُلّما كانت الخسارة أقّل على بيانات التحقّق كُلّما كان النموذج أفضل).

في حين أن بناء نموذج أكبر يعني أنّ هذا النموذج سيكون لديه المزيد من القوة و سعة الإستعاب للتعلّم ، و لكن إذا لم تكن هذه القوة مقيّدة بطريقة ما، فيمكن بسهولة لهذا النموذج أن يُفرط في التعلّم من مجموعة التدريب و تتناسب معلماته (parameters) معها.

في هذا المثال، عادةً، ما يتمكن النموذج "بالغ الصّغر" فقط من تجنب الوقوع في مشكلة الإفراط في التخصّص، وكل نموذج من النماذج الأكبر يقع في هذه المشكلة بسرعة أكبر كُلّما كان النموذج أكبر. تصبح هذه الظّاهرة شديدة التأثير على النموذج الكبير إلى حدّ أنّنا نحتاج إلى استعمال مقياس لوغاريتمي (log-scale) في أيّ رسم بياني حتّى نتمكّن من رؤية و مقارنة تطوّر الخسارة على بيانات التدريب و بيانات التحقّق.

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

  • من الطبيعي أن يكون هناك اختلاف بسيط في قيم مقياس ما بين مجموعتَي البيانات (التدريب و التحقّق).
  • إذا كان كلا المقياسين يتحركان في نفس الاتجاه ، فكل شيء على ما يرام.
  • إذا بدأ مقياس التحقق في الركود بينما يستمر مقياس التدريب في التحسن ، فربما تكون على وشك الإفراط في التعلّم.
  • إذا كان مقياس التحقق يسير في الاتجاه الخاطئ، فمن الواضح أن النموذج مفرط في التخصّص.
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]")
Text(0.5, 0, 'Epochs [Log Scale]')

png

عرض سجلاّت الإشراف على التدريب في TensorBoard

جميع هذه النماذج كتبت سجلات TensorBoard أثناء التدريب.

يمكنك أن تفتح عارض TensorBoard مضمّن داخل هذا الدّفتر التفاعلي هكذا:

%tensorboard --logdir {logdir}/sizes

يمكنك عرض نتائج إحدى دورات التدريب السابقة لهذا الدفتر التّفاعلي على موقع TensorBoard.dev.

يُمكّن موقع TensorBoard.dev من استضافة وتتبع ومشاركة تجارب تعلّم الآلة (ML) مع الجميع.

يتم تضمينه أيضًا في <iframe> لتيسير التّعامل مع TensorBoard في هذا الدّفتر التفاعليّ:

display.IFrame(
    src="https://tensorboard.dev/experiment/vW7jmmF9TmKmy3rbheMQpw/#scalars&amp;_smoothingWeight=0.97",
    width="100%", height="800px")

إذا كنت ترغب في مشاركة نتائج TensorBoard لتجربتك، فيمكنك تحميل السجلات إلى TensorBoard.dev عن طريق نسخ ما يلي في خلية تعليمات برمجية في هذا الدفتر.

tensorboard dev upload --logdir  {logdir}/sizes

استراتيجيات منع الإفراط في التعلّم

قبل الدخول في محتوى هذا القسم، انسخ سجلات التدريب من النموذج "الصغير جدّا" أعلاه، لاستخدامه كخط مرجعي للمقارنة.

shutil.rmtree(logdir/'regularizers/Tiny', ignore_errors=True)
shutil.copytree(logdir/'sizes/Tiny', logdir/'regularizers/Tiny')
PosixPath('/tmp/tmpndjoke92/tensorboard_logs/regularizers/Tiny')
regularizer_histories = {}
regularizer_histories['Tiny'] = size_histories['Tiny']

إضافة ضبط الأوزان (weight regularization)

قد تكون على دراية بمبدأ Occam's Razor و الذّي يقول: باعتبار شرحين لشيء ما، من المرجّح أن يكون التفسير "الأبسط" هو الأصحّ، أي التفسير الذي يَستعمل أقلّ قدر من الافتراضات المسبقة (assumptions). ينطبق هذا أيضًا على النماذج التي تتعلمها الشبكات العصبية و يكمُن وجه المقارنة في التّالي: إذا أخذنا بعض بيانات التدريب و تصميم شبكة عصبيّة ما، فهناك مجموعات متعددة من قيم الأوزان (أي نماذج متعددة) التّي يُمكنها أن تفسّر البيانات، والنماذج الأبسط أقل عرضة لمشكلة الإفراط في التناسب من النماذج المعقدة.

في هذا السياق، "النموذج البسيط" هو نموذج يكون توزيع قيم المعلمات (parameters) فيه إنتروبيا أقل - has less entropy - (أو إذا احتوى النموذج بأسره على معلمات أقلّ ، كما رأينا في القسم السّابق من هذا الدّرس). وبالتالي فإن الطريقة الشائعة لتخفيف الإفراط في التعلّم هي وضع قيود على تعقيد نموذج الشبكة العصبيّة عن طريق إجبار أوزانها على أخذ قيم صغيرة فقط، مما يجعل توزيع قيم الأوزان "أكثر انتظامًا". وهذا ما يسمى "ضبط الوزن" (weight regularization)، ويتم ذلك عن طريق إضافة تكلفة مرتبطة بالأوزان الكبيرة إلى دالّة الخسارة في نموذج الشبكة. تأتي هذه التكلفة في صيغتين:

  • الضبط بطريقة L1، حيث تتناسب التكلفة المضافة مع القيمة المطلقة للأوزان ( ولذلك تسمّى أيضا "معيار L1" للأوزان).

  • الضبط بطريقة L2، حيث تتناسب التكلفة المضافة مع مربع قيمة الأوزان (أي ما يسمى "معيار L2" التربيعي للأوزان). و تسمّى هذه الطريقة أيضا "اضمحلال الوزن" (weight decay) في سياق مجال الشبكات العصبيّة. لا تدع إختلاف الأسماء يربكك: فمن منطلق رياضيّ اضمحلال الوزن يساوي تماما طريقة الضبط باستعمال معيار L2.

يؤدي الضبط بطريقة L1 إلى دفع الأوزان إلى الصفر حرفيّا و هو ما يُنتج نموذجًا قليل الكثافة - و يسمّى أيضا نموذجا متناثرًا (sparse model). وأمّا طريقة الضبط باستعمال L2 ستُعاقب الأوزان دون جعلها متناثرة أو قليلة الكثافة و ذلك لأنّ قيمة هذه "التكلفة الزائدة" أو العقوبة ستكون قريبة إلى الصفر للأوزان الصغيرة. و هذا أحد الأسباب التّي تجعل طريقة L2 أكثر شيوعا.

في tf.keras، ضبط الأوزان يتمّ عن طريق تمرير وظائف الضبط إلى طبقات شبكة عصبيّة ما كإعداد من إعداداتها. فلنضف ضبط الأوزان بطريقة L2 لنموذجنا ليصير كالتّالي:

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")
Model: "sequential_4"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense_14 (Dense)             (None, 512)               14848     
_________________________________________________________________
dense_15 (Dense)             (None, 512)               262656    
_________________________________________________________________
dense_16 (Dense)             (None, 512)               262656    
_________________________________________________________________
dense_17 (Dense)             (None, 512)               262656    
_________________________________________________________________
dense_18 (Dense)             (None, 1)                 513       
=================================================================
Total params: 803,329
Trainable params: 803,329
Non-trainable params: 0
_________________________________________________________________
WARNING:tensorflow:Callbacks method `on_train_batch_end` is slow compared to the batch time (batch time: 0.0039s vs `on_train_batch_end` time: 0.0575s). Check your callbacks.

Epoch: 0, accuracy:0.4958,  binary_crossentropy:0.8107,  loss:2.3262,  val_accuracy:0.5580,  val_binary_crossentropy:0.6951,  val_loss:2.1357,  
....................................................................................................
Epoch: 100, accuracy:0.6593,  binary_crossentropy:0.5954,  loss:0.6191,  val_accuracy:0.6430,  val_binary_crossentropy:0.5893,  val_loss:0.6128,  
....................................................................................................
Epoch: 200, accuracy:0.6659,  binary_crossentropy:0.5816,  loss:0.6033,  val_accuracy:0.7000,  val_binary_crossentropy:0.5810,  val_loss:0.6028,  
....................................................................................................
Epoch: 300, accuracy:0.6822,  binary_crossentropy:0.5725,  loss:0.5948,  val_accuracy:0.6740,  val_binary_crossentropy:0.5883,  val_loss:0.6102,  
....................................................................................................
Epoch: 400, accuracy:0.6862,  binary_crossentropy:0.5665,  loss:0.5903,  val_accuracy:0.7040,  val_binary_crossentropy:0.5862,  val_loss:0.6101,  
.........................................

استعمال الدّالة l2 بهذا الشكل l2(0.001) يعني أن كل معامل (coefficient) في مصفوفة أوزان طبقة ما، سيضيف قيمة خسارة إضافيّة إلي إجماليّ خسارة الشبكة باستعمال المعادلة التّالية: 0.001 * weight_coefficient_value**2.

هذا هو السبب في أننا نراقب binary_crossentropy مباشرة، لأنه لا يحتوي على مكون الضبط (regularization component).

لذا ، فإن النموذج "الكبير" سيكون أداءه أفضل باستعمال الضبط بمعيار L2 كعقوبة إضافيّة مع إجماليّ قيمة دالّة الخسارة:

plotter.plot(regularizer_histories)
plt.ylim([0.5, 0.7])
(0.5, 0.7)

png

كما ترى ، فإن النموذج المضبوط (regularized model) باستعمال معيار "L2" أصبح الآن أكثر قدرة على المنافسة مع النموذج الأوّل "بالغ الصّغر". هذا النموذج "الكبير" المضبوط ب "L2" هو أيضا أكثر مقاومة و صمودا أمام مشلكة الإفراط في التعلّم مقارنة بالنموذج "الكبير" السّابق الذّي لم يحتوي على ضبط "L2" و ذلك على الرغم من احتواء كلا النموذجان على نفس عدد .المعلمات

معلومات إضافية

هناك شيئان مهمان يجب ملاحظتهما حول هذا النوع من الضبط (regularization).

أولاً: إذا كنت بصدد كتابة حلقة التدريب (training loop) الخاصة بك ، فأنت بحاجة إلى أن تستخرج من النموذج خسائره المتعلّقة بالضبط (regularization losses). و يمكن القيام بذلك هكذا:

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 لشبكتنا لنرى مدى نجاحهما في الحدّ من مشكلة الإفراط في التعلّم:

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")
Model: "sequential_5"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense_19 (Dense)             (None, 512)               14848     
_________________________________________________________________
dropout (Dropout)            (None, 512)               0         
_________________________________________________________________
dense_20 (Dense)             (None, 512)               262656    
_________________________________________________________________
dropout_1 (Dropout)          (None, 512)               0         
_________________________________________________________________
dense_21 (Dense)             (None, 512)               262656    
_________________________________________________________________
dropout_2 (Dropout)          (None, 512)               0         
_________________________________________________________________
dense_22 (Dense)             (None, 512)               262656    
_________________________________________________________________
dropout_3 (Dropout)          (None, 512)               0         
_________________________________________________________________
dense_23 (Dense)             (None, 1)                 513       
=================================================================
Total params: 803,329
Trainable params: 803,329
Non-trainable params: 0
_________________________________________________________________
WARNING:tensorflow:Callbacks method `on_train_batch_end` is slow compared to the batch time (batch time: 0.0044s vs `on_train_batch_end` time: 0.0649s). Check your callbacks.

Epoch: 0, accuracy:0.4937,  binary_crossentropy:0.8282,  loss:0.8282,  val_accuracy:0.5020,  val_binary_crossentropy:0.6776,  val_loss:0.6776,  
....................................................................................................
Epoch: 100, accuracy:0.6544,  binary_crossentropy:0.5976,  loss:0.5976,  val_accuracy:0.6870,  val_binary_crossentropy:0.5828,  val_loss:0.5828,  
....................................................................................................
Epoch: 200, accuracy:0.6892,  binary_crossentropy:0.5574,  loss:0.5574,  val_accuracy:0.6710,  val_binary_crossentropy:0.5806,  val_loss:0.5806,  
....................................................................................................
Epoch: 300, accuracy:0.7264,  binary_crossentropy:0.5077,  loss:0.5077,  val_accuracy:0.6690,  val_binary_crossentropy:0.6013,  val_loss:0.6013,  
...................
plotter.plot(regularizer_histories)
plt.ylim([0.5, 0.7])
(0.5, 0.7)

png

من الواضح من الرسم البياني أنّ تقنيتي الضبط المدروستان سابقا يحسّنان أداء النموذج "الكبير". و لكن، رغم ذلك، لا يتخطّى أداء النموذج الكبير أداء النموذج بالغ الصّغر الذّي نستعمله كخطّ مرجعي.

في التّالي، سنجرّب تقنيتي الضبط معًا لنرى إن كان ذلك سيؤدّي إلي تحسين أداء النموذج.

تجربة تقنتي الضبط L2 و Dropout معًا

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")
Model: "sequential_6"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense_24 (Dense)             (None, 512)               14848     
_________________________________________________________________
dropout_4 (Dropout)          (None, 512)               0         
_________________________________________________________________
dense_25 (Dense)             (None, 512)               262656    
_________________________________________________________________
dropout_5 (Dropout)          (None, 512)               0         
_________________________________________________________________
dense_26 (Dense)             (None, 512)               262656    
_________________________________________________________________
dropout_6 (Dropout)          (None, 512)               0         
_________________________________________________________________
dense_27 (Dense)             (None, 512)               262656    
_________________________________________________________________
dropout_7 (Dropout)          (None, 512)               0         
_________________________________________________________________
dense_28 (Dense)             (None, 1)                 513       
=================================================================
Total params: 803,329
Trainable params: 803,329
Non-trainable params: 0
_________________________________________________________________
WARNING:tensorflow:Callbacks method `on_train_batch_end` is slow compared to the batch time (batch time: 0.0044s vs `on_train_batch_end` time: 0.0628s). Check your callbacks.

Epoch: 0, accuracy:0.4991,  binary_crossentropy:0.8005,  loss:0.9586,  val_accuracy:0.5610,  val_binary_crossentropy:0.7304,  val_loss:0.8879,  
....................................................................................................
Epoch: 100, accuracy:0.6372,  binary_crossentropy:0.6057,  loss:0.6356,  val_accuracy:0.6410,  val_binary_crossentropy:0.5841,  val_loss:0.6139,  
....................................................................................................
Epoch: 200, accuracy:0.6658,  binary_crossentropy:0.5909,  loss:0.6163,  val_accuracy:0.6540,  val_binary_crossentropy:0.5770,  val_loss:0.6025,  
....................................................................................................
Epoch: 300, accuracy:0.6698,  binary_crossentropy:0.5797,  loss:0.6085,  val_accuracy:0.6860,  val_binary_crossentropy:0.5625,  val_loss:0.5914,  
....................................................................................................
Epoch: 400, accuracy:0.6776,  binary_crossentropy:0.5776,  loss:0.6080,  val_accuracy:0.6590,  val_binary_crossentropy:0.5709,  val_loss:0.6013,  
....................................................................................................
Epoch: 500, accuracy:0.6814,  binary_crossentropy:0.5706,  loss:0.6032,  val_accuracy:0.6660,  val_binary_crossentropy:0.5601,  val_loss:0.5927,  
....................................................................................................
Epoch: 600, accuracy:0.6861,  binary_crossentropy:0.5667,  loss:0.6018,  val_accuracy:0.6940,  val_binary_crossentropy:0.5441,  val_loss:0.5791,  
....................................................................................................
Epoch: 700, accuracy:0.6873,  binary_crossentropy:0.5637,  loss:0.6009,  val_accuracy:0.6850,  val_binary_crossentropy:0.5534,  val_loss:0.5905,  
....................................................................................................
Epoch: 800, accuracy:0.6960,  binary_crossentropy:0.5533,  loss:0.5925,  val_accuracy:0.7000,  val_binary_crossentropy:0.5504,  val_loss:0.5895,  
....................................................................................................
Epoch: 900, accuracy:0.6962,  binary_crossentropy:0.5523,  loss:0.5928,  val_accuracy:0.7000,  val_binary_crossentropy:0.5439,  val_loss:0.5844,  
....................................
plotter.plot(regularizer_histories)
plt.ylim([0.5, 0.7])
(0.5, 0.7)

png

من الواضح أنّ النموذج الحالي الذّي يستعمل تقنيتي الضبط هو الأفضل حتّى الآن.

عرض سجلاّت الإشراف على التدريب في TensorBoard

جميع هذه النماذج كتبت سجلات TensorBoard أثناء التدريب.

يمكنك أن تفتح عارض TensorBoard مضمّن داخل هذا الدّفتر التفاعلي هكذا:

%tensorboard --logdir {logdir}/regularizers

يمكنك عرض نتائج إحدى تجارب التدريب السابقة لهذا الدفتر التّفاعلي على موقع TensorBoard.dev.

يتم تضمينه أيضًا في <iframe> لتيسير التّعامل مع TensorBoard في هذا الدّفتر التفاعليّ:

display.IFrame(
    src="https://tensorboard.dev/experiment/fGInKDo8TXes1z7HQku9mw/#scalars&amp;_smoothingWeight=0.97",
    width = "100%",
    height="800px")

تم تحميل هذا باستعمال الأمر التّالي:

tensorboard dev upload --logdir  {logdir}/regularizers

الاستنتاجات

للتلخيص: نجمع فيما يلي أكثر الطرق شيوعًا لمقاومة مشكلة الإفراط في التعلّم التّي قد تتعرّض إليها نماذج تعلّم الآلة في العموم و نماذج الشبكات العصبيّة بالخصوص:

  • احصل على المزيد من بيانات التدريب لجعل البيانات أكثر إكتمالاً.
  • تقليل سعة الشبكة عن طريق تقليل عدد طبقاتها و عدد الخلايا في كُلّ طبقة.
  • استعمال تقنية ضبط الأوزان (weight regularization).
  • استعمال تقنية الإسقاط العشوائي (Dropout).

هناك طريقتان مهمتان لم يتمّ تناولهما في هذا الدليل وهما:

  • تكثير البيانات (data augmentation)
  • تعيير دُفعات البيانات (batch normalization)

تذكّر أن كل طريقةٍ يمكنُ أن تساعد وحدها في تحسين أداء النّموذج ، ولكن غالبًا ما يكون الجمع بين طرق متعدّدة أكثر فعالية.

# 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.