Overfit و Underfit

مشاهده در TensorFlow.org در Google Colab اجرا شود مشاهده منبع در GitHub دانلود دفترچه یادداشت

مثل همیشه، کد موجود در این مثال از tf.keras API استفاده می‌کند که می‌توانید در راهنمای TensorFlow Keras درباره آن اطلاعات بیشتری کسب کنید.

در هر دو مثال قبلی - طبقه‌بندی متن و پیش‌بینی بهره‌وری سوخت - دیدیم که دقت مدل ما در داده‌های اعتبارسنجی پس از آموزش برای چند دوره به اوج خود می‌رسد و سپس راکد می‌شود یا شروع به کاهش می‌کند.

به عبارت دیگر، مدل ما بیش از حد با داده های آموزشی مطابقت دارد. یادگیری نحوه برخورد با بیش از حد مناسب بسیار مهم است. اگرچه اغلب دستیابی به دقت بالایی در مجموعه آموزشی امکان پذیر است، آنچه ما واقعاً می خواهیم این است که مدل هایی را توسعه دهیم که به خوبی به یک مجموعه آزمایشی تعمیم دهند (یا داده هایی که قبلاً ندیده اند).

نقطه مقابل overfitting، underfitting است. عدم تناسب زمانی اتفاق می افتد که هنوز فضایی برای بهبود داده های قطار وجود دارد. این ممکن است به دلایل مختلفی اتفاق بیفتد: اگر مدل به اندازه کافی قدرتمند نباشد، بیش از حد منظم باشد، یا به سادگی به اندازه کافی آموزش ندیده باشد. این بدان معناست که شبکه الگوهای مربوطه را در داده های آموزشی نیاموخته است.

اگر برای مدت طولانی تمرین کنید، مدل شروع به بیش از حد برازش می کند و الگوهایی را از داده های آموزشی یاد می گیرد که به داده های تست تعمیم نمی دهند. ما باید تعادل ایجاد کنیم. درک چگونگی آموزش برای تعداد مناسبی از دوره ها همانطور که در زیر بررسی خواهیم کرد یک مهارت مفید است.

برای جلوگیری از برازش بیش از حد، بهترین راه حل استفاده از داده های آموزشی کامل تر است. مجموعه داده باید طیف کاملی از ورودی هایی را که مدل انتظار می رود مدیریت کند را پوشش دهد. داده های اضافی تنها زمانی مفید است که موارد جدید و جالب را پوشش دهد.

مدلی که بر روی داده های کامل تر آموزش داده شود، طبیعتاً بهتر تعمیم می یابد. هنگامی که دیگر امکان پذیر نیست، بهترین راه حل بعدی استفاده از تکنیک هایی مانند منظم سازی است. اینها محدودیت هایی را بر روی کمیت و نوع اطلاعاتی که مدل شما می تواند ذخیره کند ایجاد می کند. اگر یک شبکه فقط توانایی حفظ تعداد کمی از الگوها را داشته باشد، فرآیند بهینه سازی آن را مجبور می کند تا بر برجسته ترین الگوها تمرکز کند، که شانس بیشتری برای تعمیم خوب دارند.

در این نوت بوک، چندین تکنیک متداول منظم سازی را بررسی خواهیم کرد و از آنها برای بهبود مدل طبقه بندی استفاده خواهیم کرد.

برپایی

قبل از شروع، بسته های لازم را وارد کنید:

import tensorflow as tf

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

print(tf.__version__)
2.8.0-rc1
!pip install git+https://github.com/tensorflow/docs

import tensorflow_docs as tfdocs
import tensorflow_docs.modeling
import tensorflow_docs.plots
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)

مجموعه داده هیگز

هدف این آموزش انجام فیزیک ذرات نیست، بنابراین روی جزئیات مجموعه داده تمرکز نکنید. این شامل 11 000 000 مثال است که هر کدام دارای 28 ویژگی و یک برچسب کلاس باینری است.

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 [==============================] - 123s 0us/step
2816417792/2816407858 [==============================] - 123s 0us/step
FEATURES = 28

کلاس tf.data.experimental.CsvDataset را می توان برای خواندن رکوردهای csv مستقیماً از یک فایل gzip بدون مرحله فشرده سازی میانی استفاده کرد.

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

آن کلاس csv reader لیستی از اسکالرها را برای هر رکورد برمی گرداند. تابع زیر آن لیست اسکالرها را در یک جفت (ویژگی_بردار، برچسب) دوباره بسته بندی می کند.

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 جدید بیندازید.

ویژگی ها کاملاً عادی نیستند، اما این برای این آموزش کافی است.

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 element_spec=(TensorSpec(shape=(28,), dtype=tf.float32, name=None), TensorSpec(shape=(), dtype=tf.float32, name=None))>

این مجموعه داده ها نمونه های فردی را برمی گرداند. از روش .batch برای ایجاد دسته هایی با اندازه مناسب برای آموزش استفاده کنید. قبل از .shuffle نیز به یاد داشته باشید که مجموعه آموزشی را مخلوط کرده و .repeat کنید.

validate_ds = validate_ds.batch(BATCH_SIZE)
train_ds = train_ds.shuffle(BUFFER_SIZE).repeat().batch(BATCH_SIZE)

بیش از حد مناسب بودن را نشان دهید

ساده ترین راه برای جلوگیری از برازش بیش از حد، شروع با یک مدل کوچک است: مدلی با تعداد کمی پارامتر قابل یادگیری (که با تعداد لایه ها و تعداد واحدها در هر لایه تعیین می شود). در یادگیری عمیق، تعداد پارامترهای قابل یادگیری در یک مدل اغلب به عنوان "ظرفیت" مدل نامیده می شود.

به طور شهودی، مدلی با پارامترهای بیشتر «ظرفیت به خاطر سپردن» بیشتری خواهد داشت و بنابراین می‌تواند به راحتی یک نقشه‌برداری کامل شبیه به فرهنگ لغت را بین نمونه‌های آموزشی و اهداف آنها بیاموزد، نقشه‌برداری بدون هیچ قدرت تعمیم، اما این در هنگام پیش‌بینی بی‌فایده خواهد بود. بر روی داده های دیده نشده قبلی

همیشه این را در نظر داشته باشید: مدل‌های یادگیری عمیق معمولاً در تناسب با داده‌های آموزشی خوب هستند، اما چالش واقعی تعمیم است، نه برازش.

از طرف دیگر، اگر شبکه منابع حافظه محدودی داشته باشد، نمی تواند به راحتی نقشه برداری را یاد بگیرد. برای به حداقل رساندن از دست دادن آن، باید بازنمایی های فشرده ای را یاد بگیرد که قدرت پیش بینی بیشتری دارند. در عین حال، اگر مدل خود را خیلی کوچک کنید، در تطبیق با داده های آموزشی مشکل خواهد داشت. بین «ظرفیت بیش از حد» و «ظرفیت ناکافی» تعادل وجود دارد.

متأسفانه، هیچ فرمول جادویی برای تعیین اندازه یا معماری مناسب مدل شما (از نظر تعداد لایه ها یا اندازه مناسب برای هر لایه) وجود ندارد. شما باید با استفاده از یک سری معماری های مختلف آزمایش کنید.

برای یافتن اندازه مدل مناسب، بهتر است با لایه‌ها و پارامترهای نسبتاً کمی شروع کنید، سپس شروع به افزایش اندازه لایه‌ها یا اضافه کردن لایه‌های جدید کنید تا زمانی که کاهش بازدهی از دست دادن اعتبارسنجی را مشاهده کنید.

با یک مدل ساده شروع کنید که فقط از لایه ها استفاده می layers.Dense به عنوان خط پایه، سپس نسخه های بزرگتر ایجاد کنید و آنها را مقایسه کنید.

رویه آموزشی

اگر در طول تمرین به تدریج میزان یادگیری را کاهش دهید، بسیاری از مدل ها بهتر تمرین می کنند. از 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 تنظیم می‌کند. InverseTimeDecay به صورت هذلولی نرخ یادگیری را به 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

هر مدل در این آموزش از تنظیمات آموزشی مشابهی استفاده می کند. بنابراین اینها را به روشی قابل استفاده مجدد تنظیم کنید، با لیستی از تماس‌های برگشتی شروع کنید.

آموزش این آموزش برای بسیاری از دوره های کوتاه اجرا می شود. برای کاهش نویز ورود به سیستم از tfdocs.EpochDots استفاده کنید که به سادگی یک را چاپ می کند . برای هر دوره، و یک مجموعه کامل از معیارها در هر 100 دوره.

در مرحله بعد شامل callbacks.EarlyStopping برای جلوگیری از زمان‌های طولانی و غیر ضروری تمرین می‌شود. توجه داشته باشید که این callback برای نظارت بر 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.fit و Model.compile یکسان استفاده خواهد کرد:

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
_________________________________________________________________

Epoch: 0, accuracy:0.4961,  binary_crossentropy:0.7294,  loss:0.7294,  val_accuracy:0.4840,  val_binary_crossentropy:0.7200,  val_loss:0.7200,  
....................................................................................................
Epoch: 100, accuracy:0.5931,  binary_crossentropy:0.6279,  loss:0.6279,  val_accuracy:0.5860,  val_binary_crossentropy:0.6288,  val_loss:0.6288,  
....................................................................................................
Epoch: 200, accuracy:0.6157,  binary_crossentropy:0.6178,  loss:0.6178,  val_accuracy:0.6200,  val_binary_crossentropy:0.6134,  val_loss:0.6134,  
....................................................................................................
Epoch: 300, accuracy:0.6370,  binary_crossentropy:0.6086,  loss:0.6086,  val_accuracy:0.6220,  val_binary_crossentropy:0.6055,  val_loss:0.6055,  
....................................................................................................
Epoch: 400, accuracy:0.6522,  binary_crossentropy:0.6008,  loss:0.6008,  val_accuracy:0.6260,  val_binary_crossentropy:0.5997,  val_loss:0.5997,  
....................................................................................................
Epoch: 500, accuracy:0.6513,  binary_crossentropy:0.5946,  loss:0.5946,  val_accuracy:0.6480,  val_binary_crossentropy:0.5911,  val_loss:0.5911,  
....................................................................................................
Epoch: 600, accuracy:0.6636,  binary_crossentropy:0.5894,  loss:0.5894,  val_accuracy:0.6390,  val_binary_crossentropy:0.5898,  val_loss:0.5898,  
....................................................................................................
Epoch: 700, accuracy:0.6696,  binary_crossentropy:0.5852,  loss:0.5852,  val_accuracy:0.6530,  val_binary_crossentropy:0.5870,  val_loss:0.5870,  
....................................................................................................
Epoch: 800, accuracy:0.6706,  binary_crossentropy:0.5824,  loss:0.5824,  val_accuracy:0.6590,  val_binary_crossentropy:0.5850,  val_loss:0.5850,  
....................................................................................................
Epoch: 900, accuracy:0.6709,  binary_crossentropy:0.5796,  loss:0.5796,  val_accuracy:0.6680,  val_binary_crossentropy:0.5831,  val_loss:0.5831,  
....................................................................................................
Epoch: 1000, accuracy:0.6780,  binary_crossentropy:0.5769,  loss:0.5769,  val_accuracy:0.6530,  val_binary_crossentropy:0.5851,  val_loss:0.5851,  
....................................................................................................
Epoch: 1100, accuracy:0.6735,  binary_crossentropy:0.5752,  loss:0.5752,  val_accuracy:0.6620,  val_binary_crossentropy:0.5807,  val_loss:0.5807,  
....................................................................................................
Epoch: 1200, accuracy:0.6759,  binary_crossentropy:0.5729,  loss:0.5729,  val_accuracy:0.6620,  val_binary_crossentropy:0.5792,  val_loss:0.5792,  
....................................................................................................
Epoch: 1300, accuracy:0.6849,  binary_crossentropy:0.5716,  loss:0.5716,  val_accuracy:0.6450,  val_binary_crossentropy:0.5859,  val_loss:0.5859,  
....................................................................................................
Epoch: 1400, accuracy:0.6790,  binary_crossentropy:0.5695,  loss:0.5695,  val_accuracy:0.6700,  val_binary_crossentropy:0.5776,  val_loss:0.5776,  
....................................................................................................
Epoch: 1500, accuracy:0.6824,  binary_crossentropy:0.5681,  loss:0.5681,  val_accuracy:0.6730,  val_binary_crossentropy:0.5761,  val_loss:0.5761,  
....................................................................................................
Epoch: 1600, accuracy:0.6828,  binary_crossentropy:0.5669,  loss:0.5669,  val_accuracy:0.6690,  val_binary_crossentropy:0.5766,  val_loss:0.5766,  
....................................................................................................
Epoch: 1700, accuracy:0.6874,  binary_crossentropy:0.5657,  loss:0.5657,  val_accuracy:0.6600,  val_binary_crossentropy:0.5774,  val_loss:0.5774,  
....................................................................................................
Epoch: 1800, accuracy:0.6845,  binary_crossentropy:0.5655,  loss:0.5655,  val_accuracy:0.6780,  val_binary_crossentropy:0.5752,  val_loss:0.5752,  
....................................................................................................
Epoch: 1900, accuracy:0.6837,  binary_crossentropy:0.5644,  loss:0.5644,  val_accuracy:0.6790,  val_binary_crossentropy:0.5753,  val_loss:0.5753,  
....................................................................................................
Epoch: 2000, accuracy:0.6853,  binary_crossentropy:0.5632,  loss:0.5632,  val_accuracy:0.6780,  val_binary_crossentropy:0.5753,  val_loss:0.5753,  
....................................................................................................
Epoch: 2100, accuracy:0.6871,  binary_crossentropy:0.5625,  loss:0.5625,  val_accuracy:0.6670,  val_binary_crossentropy:0.5769,  val_loss:0.5769,  
...................................

حال بررسی کنید که مدل چگونه عمل کرد:

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

مدل کوچک

برای اینکه ببینید آیا می توانید عملکرد مدل کوچک را شکست دهید، به تدریج چند مدل بزرگتر را آموزش دهید.

دو لایه مخفی را با 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
_________________________________________________________________

Epoch: 0, accuracy:0.4864,  binary_crossentropy:0.7769,  loss:0.7769,  val_accuracy:0.4930,  val_binary_crossentropy:0.7211,  val_loss:0.7211,  
....................................................................................................
Epoch: 100, accuracy:0.6386,  binary_crossentropy:0.6052,  loss:0.6052,  val_accuracy:0.6020,  val_binary_crossentropy:0.6177,  val_loss:0.6177,  
....................................................................................................
Epoch: 200, accuracy:0.6697,  binary_crossentropy:0.5829,  loss:0.5829,  val_accuracy:0.6310,  val_binary_crossentropy:0.6018,  val_loss:0.6018,  
....................................................................................................
Epoch: 300, accuracy:0.6838,  binary_crossentropy:0.5721,  loss:0.5721,  val_accuracy:0.6490,  val_binary_crossentropy:0.5940,  val_loss:0.5940,  
....................................................................................................
Epoch: 400, accuracy:0.6911,  binary_crossentropy:0.5656,  loss:0.5656,  val_accuracy:0.6430,  val_binary_crossentropy:0.5985,  val_loss:0.5985,  
....................................................................................................
Epoch: 500, accuracy:0.6930,  binary_crossentropy:0.5607,  loss:0.5607,  val_accuracy:0.6430,  val_binary_crossentropy:0.6028,  val_loss:0.6028,  
.........................

مدل متوسط

اکنون 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
_________________________________________________________________

Epoch: 0, accuracy:0.5017,  binary_crossentropy:0.6840,  loss:0.6840,  val_accuracy:0.4790,  val_binary_crossentropy:0.6723,  val_loss:0.6723,  
....................................................................................................
Epoch: 100, accuracy:0.7173,  binary_crossentropy:0.5221,  loss:0.5221,  val_accuracy:0.6470,  val_binary_crossentropy:0.6111,  val_loss:0.6111,  
....................................................................................................
Epoch: 200, accuracy:0.7884,  binary_crossentropy:0.4270,  loss:0.4270,  val_accuracy:0.6390,  val_binary_crossentropy:0.7045,  val_loss:0.7045,  
..............................................................

مدل بزرگ

به عنوان یک تمرین، می توانید یک مدل حتی بزرگتر ایجاد کنید و ببینید که چقدر سریع شروع به نصب بیش از حد می کند. در مرحله بعد، بیایید شبکه ای را به این معیار اضافه کنیم که ظرفیت بسیار بیشتری دارد، بسیار بیشتر از آن چیزی که مشکل ایجاب می کند:

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
_________________________________________________________________

Epoch: 0, accuracy:0.5145,  binary_crossentropy:0.7740,  loss:0.7740,  val_accuracy:0.4980,  val_binary_crossentropy:0.6793,  val_loss:0.6793,  
....................................................................................................
Epoch: 100, accuracy:1.0000,  binary_crossentropy:0.0020,  loss:0.0020,  val_accuracy:0.6600,  val_binary_crossentropy:1.8540,  val_loss:1.8540,  
....................................................................................................
Epoch: 200, accuracy:1.0000,  binary_crossentropy:0.0001,  loss:0.0001,  val_accuracy:0.6560,  val_binary_crossentropy:2.5293,  val_loss:2.5293,  
..........................

تلفات آموزشی و اعتبارسنجی را ترسیم کنید

خطوط توپر افت تمرین را نشان می‌دهند و خطوط نقطه چین افت اعتبار را نشان می‌دهند (به یاد داشته باشید: کاهش اعتبار کمتر نشان‌دهنده مدل بهتر است).

در حالی که ساخت یک مدل بزرگتر به آن قدرت بیشتری می دهد، اگر این قدرت به نحوی محدود نشود، می تواند به راحتی به مجموعه آموزشی اضافه شود.

در این مثال، به طور معمول، تنها مدل "Tiny" می‌تواند به طور کلی از بیش‌تنظیم شدن جلوگیری کند، و هر یک از مدل‌های بزرگ‌تر با سرعت بیشتری به داده‌ها اضافه می‌شوند. این برای مدل "large" آنقدر شدید می شود که باید طرح را به یک مقیاس گزارش تغییر دهید تا واقعاً ببینید چه اتفاقی می افتد.

اگر معیارهای اعتبار سنجی را با معیارهای آموزشی ترسیم و مقایسه کنید، این امر آشکار است.

  • طبیعی است که یک تفاوت کوچک وجود داشته باشد.
  • اگر هر دو معیار در یک جهت حرکت کنند، همه چیز خوب است.
  • اگر معیار اعتبار سنجی شروع به راکد شدن کند در حالی که متریک آموزشی به بهبود ادامه می دهد، احتمالاً به بیش از حد برازش نزدیک شده اید.
  • اگر معیار اعتبار سنجی در جهت اشتباه باشد، مدل به وضوح بیش از حد مناسب است.
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 تعبیه شده را در داخل یک نوت بوک باز کنید:

#docs_infra: no_execute

# Load the TensorBoard notebook extension
%load_ext tensorboard

# Open an embedded TensorBoard viewer
%tensorboard --logdir {logdir}/sizes

می توانید نتایج اجرای قبلی این نوت بوک را در TensorBoard.dev مشاهده کنید.

TensorBoard.dev یک تجربه مدیریت شده برای میزبانی، ردیابی و به اشتراک گذاری آزمایش های ML با همه است.

همچنین برای راحتی در <iframe> گنجانده شده است:

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

اگر می‌خواهید نتایج TensorBoard را به اشتراک بگذارید، می‌توانید با کپی کردن موارد زیر در یک سلول کد، گزارش‌ها را در TensorBoard.dev آپلود کنید.

tensorboard dev upload --logdir  {logdir}/sizes

استراتژی های جلوگیری از بیش از حد مناسب

قبل از ورود به محتوای این بخش، گزارش‌های آموزشی را از مدل "Tiny" بالا کپی کنید تا به‌عنوان پایه مقایسه استفاده کنید.

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

تنظیم وزن را اضافه کنید

ممکن است با اصل تیغ اوکام آشنا باشید: با ارائه دو توضیح برای چیزی، توضیحی که به احتمال زیاد درست است، «ساده‌ترین» است، توضیحی که کمترین فرض را دارد. این همچنین در مورد مدل‌های آموخته‌شده توسط شبکه‌های عصبی صدق می‌کند: با توجه به برخی داده‌های آموزشی و معماری شبکه، مجموعه‌های متعددی از مقادیر وزن (مدل‌های متعدد) وجود دارد که می‌توانند داده‌ها را توضیح دهند، و مدل‌های ساده‌تر نسبت به مدل‌های پیچیده کمتر برازش می‌کنند.

یک "مدل ساده" در این زمینه مدلی است که در آن توزیع مقادیر پارامتر آنتروپی کمتری دارد (یا مدلی با پارامترهای کمتر، همانطور که در بخش بالا دیدیم). بنابراین یک راه متداول برای کاهش بیش از حد برازش این است که برای پیچیدگی یک شبکه محدودیت‌هایی ایجاد کنیم که وزن‌های آن را فقط مقادیر کوچکی در نظر بگیرند، که توزیع مقادیر وزن را «منظم‌تر» می‌کند. به این کار "تنظیم وزن" می گویند و با اضافه کردن هزینه ای که با داشتن وزنه های بزرگ به عملکرد کاهش شبکه اضافه می شود، انجام می شود. این هزینه در دو نوع است:

  • تنظیم L1 ، که در آن هزینه اضافه شده متناسب با مقدار مطلق ضرایب وزن است (یعنی با آنچه "هنجار L1" وزن ها نامیده می شود).

  • تنظیم L2 ، که در آن هزینه اضافه شده متناسب با مجذور مقدار ضرایب وزن است (یعنی با چیزی که مجذور "هنجار L2" وزن ها نامیده می شود). تنظیم L2 در زمینه شبکه های عصبی، کاهش وزن نیز نامیده می شود. اجازه ندهید نام متفاوت شما را گیج کند: کاهش وزن از نظر ریاضی دقیقاً مشابه تنظیم L2 است.

منظم‌سازی L1 وزن‌ها را دقیقاً به سمت صفر می‌برد و یک مدل پراکنده را تشویق می‌کند. منظم‌سازی 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
_________________________________________________________________

Epoch: 0, accuracy:0.5126,  binary_crossentropy:0.7481,  loss:2.2415,  val_accuracy:0.4950,  val_binary_crossentropy:0.6707,  val_loss:2.0653,  
....................................................................................................
Epoch: 100, accuracy:0.6625,  binary_crossentropy:0.5945,  loss:0.6173,  val_accuracy:0.6400,  val_binary_crossentropy:0.5871,  val_loss:0.6100,  
....................................................................................................
Epoch: 200, accuracy:0.6690,  binary_crossentropy:0.5864,  loss:0.6079,  val_accuracy:0.6650,  val_binary_crossentropy:0.5856,  val_loss:0.6076,  
....................................................................................................
Epoch: 300, accuracy:0.6790,  binary_crossentropy:0.5762,  loss:0.5976,  val_accuracy:0.6550,  val_binary_crossentropy:0.5881,  val_loss:0.6095,  
....................................................................................................
Epoch: 400, accuracy:0.6843,  binary_crossentropy:0.5697,  loss:0.5920,  val_accuracy:0.6650,  val_binary_crossentropy:0.5878,  val_loss:0.6101,  
....................................................................................................
Epoch: 500, accuracy:0.6897,  binary_crossentropy:0.5651,  loss:0.5907,  val_accuracy:0.6890,  val_binary_crossentropy:0.5798,  val_loss:0.6055,  
....................................................................................................
Epoch: 600, accuracy:0.6945,  binary_crossentropy:0.5610,  loss:0.5864,  val_accuracy:0.6820,  val_binary_crossentropy:0.5772,  val_loss:0.6026,  
..........................................................

l2(0.001) به این معنی است که هر ضریب در ماتریس وزن لایه 0.001 * weight_coefficient_value**2 به کل از دست دادن شبکه اضافه می کند.

به همین دلیل است که ما مستقیماً binary_crossentropy را زیر نظر داریم. زیرا این مؤلفه منظم سازی را با هم مخلوط نکرده است.

بنابراین، همان مدل "Large" با جریمه تنظیم L2 بسیار بهتر عمل می کند:

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

png

همانطور که می بینید، مدل منظم "L2" اکنون با مدل "Tiny" بسیار رقابتی تر است. این مدل "L2" نیز با وجود داشتن تعداد پارامترهای یکسان، نسبت به مدل "Large" که بر اساس آن ساخته شده بود، در برابر بیش از حد مقاوم‌تر است.

اطلاعات بیشتر

دو نکته مهم در مورد این نوع تنظیم وجود دارد.

اول: اگر حلقه آموزشی خود را می نویسید، باید مطمئن شوید که از مدل ضررهای منظم سازی آن را بخواهید.

result = l2_model(features)
regularization_loss=tf.add_n(l2_model.losses)

دوم: این پیاده‌سازی با اضافه کردن جریمه‌های وزنی به از دست دادن مدل، و سپس اعمال یک روش بهینه‌سازی استاندارد پس از آن کار می‌کند.

رویکرد دومی وجود دارد که در عوض فقط بهینه‌ساز را روی از دست دادن خام اجرا می‌کند، و سپس هنگام اعمال مرحله محاسبه‌شده، بهینه‌ساز مقداری کاهش وزن را نیز اعمال می‌کند. این "کاهش وزن جدا شده" در بهینه سازهایی مانند optimizers.FTRL و optimizers.AdamW دیده می شود.

اضافه کردن ترک تحصیل

ترک تحصیل یکی از موثرترین و رایج ترین تکنیک های منظم سازی برای شبکه های عصبی است که توسط هینتون و دانشجویانش در دانشگاه تورنتو توسعه یافته است.

توضیح شهودی برای انصراف این است که چون گره های منفرد در شبکه نمی توانند به خروجی سایرین تکیه کنند، هر گره باید ویژگی هایی را ارائه دهد که به تنهایی مفید هستند.

Dropout که بر روی یک لایه اعمال می‌شود، شامل حذف تصادفی تعدادی از ویژگی‌های خروجی لایه در طول آموزش است. فرض کنید یک لایه داده شده به طور معمول یک بردار [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
_________________________________________________________________

Epoch: 0, accuracy:0.4961,  binary_crossentropy:0.8110,  loss:0.8110,  val_accuracy:0.5330,  val_binary_crossentropy:0.6900,  val_loss:0.6900,  
....................................................................................................
Epoch: 100, accuracy:0.6557,  binary_crossentropy:0.5961,  loss:0.5961,  val_accuracy:0.6710,  val_binary_crossentropy:0.5788,  val_loss:0.5788,  
....................................................................................................
Epoch: 200, accuracy:0.6871,  binary_crossentropy:0.5622,  loss:0.5622,  val_accuracy:0.6860,  val_binary_crossentropy:0.5856,  val_loss:0.5856,  
....................................................................................................
Epoch: 300, accuracy:0.7246,  binary_crossentropy:0.5121,  loss:0.5121,  val_accuracy:0.6820,  val_binary_crossentropy:0.5927,  val_loss:0.5927,  
............
plotter.plot(regularizer_histories)
plt.ylim([0.5, 0.7])
(0.5, 0.7)

png

از این طرح مشخص است که هر دوی این رویکردهای منظم‌سازی، رفتار مدل "Large" را بهبود می‌بخشند. اما این هنوز حتی از خط پایه "Tiny" هم نمی گذرد.

سپس هر دو را با هم امتحان کنید و ببینید که آیا این کار بهتر است یا خیر.

L2 ترکیبی + ترک تحصیل

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
_________________________________________________________________

Epoch: 0, accuracy:0.5090,  binary_crossentropy:0.8064,  loss:0.9648,  val_accuracy:0.4660,  val_binary_crossentropy:0.6877,  val_loss:0.8454,  
....................................................................................................
Epoch: 100, accuracy:0.6445,  binary_crossentropy:0.6050,  loss:0.6350,  val_accuracy:0.6630,  val_binary_crossentropy:0.5871,  val_loss:0.6169,  
....................................................................................................
Epoch: 200, accuracy:0.6660,  binary_crossentropy:0.5932,  loss:0.6186,  val_accuracy:0.6880,  val_binary_crossentropy:0.5722,  val_loss:0.5975,  
....................................................................................................
Epoch: 300, accuracy:0.6697,  binary_crossentropy:0.5818,  loss:0.6100,  val_accuracy:0.6900,  val_binary_crossentropy:0.5614,  val_loss:0.5895,  
....................................................................................................
Epoch: 400, accuracy:0.6749,  binary_crossentropy:0.5742,  loss:0.6046,  val_accuracy:0.6870,  val_binary_crossentropy:0.5576,  val_loss:0.5881,  
....................................................................................................
Epoch: 500, accuracy:0.6854,  binary_crossentropy:0.5703,  loss:0.6029,  val_accuracy:0.6970,  val_binary_crossentropy:0.5458,  val_loss:0.5784,  
....................................................................................................
Epoch: 600, accuracy:0.6806,  binary_crossentropy:0.5673,  loss:0.6015,  val_accuracy:0.6980,  val_binary_crossentropy:0.5453,  val_loss:0.5795,  
....................................................................................................
Epoch: 700, accuracy:0.6937,  binary_crossentropy:0.5583,  loss:0.5938,  val_accuracy:0.6870,  val_binary_crossentropy:0.5477,  val_loss:0.5832,  
....................................................................................................
Epoch: 800, accuracy:0.6911,  binary_crossentropy:0.5576,  loss:0.5947,  val_accuracy:0.7000,  val_binary_crossentropy:0.5446,  val_loss:0.5817,  
.......................
plotter.plot(regularizer_histories)
plt.ylim([0.5, 0.7])
(0.5, 0.7)

png

این مدل با تنظیم "Combined" بدیهی است که بهترین مدل تا کنون است.

مشاهده در TensorBoard

این مدل ها لاگ های TensorBoard را نیز ثبت کردند.

برای باز کردن نمایشگر تانسوربورد تعبیه شده در یک نوت بوک، موارد زیر را در یک سلول کد کپی کنید:

%tensorboard --logdir {logdir}/regularizers

می توانید نتایج اجرای قبلی این نوت بوک را در TensorDoard.dev مشاهده کنید.

همچنین برای راحتی در <iframe> گنجانده شده است:

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

این با آپلود شد:

tensorboard dev upload --logdir  {logdir}/regularizers

نتیجه گیری

برای جمع‌بندی: در اینجا رایج‌ترین راه‌های جلوگیری از برازش بیش از حد در شبکه‌های عصبی وجود دارد:

  • داده های آموزشی بیشتری دریافت کنید.
  • کاهش ظرفیت شبکه
  • تنظیم وزن را اضافه کنید.
  • اضافه کردن ترک تحصیل

دو رویکرد مهم که در این راهنما پوشش داده نشده اند عبارتند از:

  • افزایش داده ها
  • عادی سازی دسته ای

به یاد داشته باشید که هر روش می تواند به تنهایی کمک کند، اما اغلب ترکیب آنها می تواند حتی موثرتر باشد.

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