Overfit and underfit

Xem trên TensorFlow.org Chạy trong Google Colab Xem nguồn trên GitHub Tải xuống sổ ghi chép

Như mọi khi, mã trong ví dụ này sẽ sử dụng API tf.keras mà bạn có thể tìm hiểu thêm trong hướng dẫn TensorFlow Keras .

Trong cả hai ví dụ trước - phân loại văn bảndự đoán hiệu suất nhiên liệu - chúng tôi thấy rằng độ chính xác của mô hình của chúng tôi đối với dữ liệu xác thực sẽ đạt mức cao nhất sau khi đào tạo trong một số kỷ nguyên và sau đó sẽ trì trệ hoặc bắt đầu giảm.

Nói cách khác, mô hình của chúng tôi sẽ quá phù hợp với dữ liệu đào tạo. Học cách đối phó với việc mặc trang phục quá sức là quan trọng. Mặc dù thường có thể đạt được độ chính xác cao trên tập huấn luyện , nhưng điều chúng tôi thực sự muốn là phát triển các mô hình tổng quát hóa tốt cho tập thử nghiệm (hoặc dữ liệu mà họ chưa từng thấy trước đây).

Ngược lại của overfitting là underfitting . Việc trang bị thiếu xảy ra khi dữ liệu tàu vẫn còn chỗ để cải thiện. Điều này có thể xảy ra vì một số lý do: Nếu mô hình không đủ mạnh, được quy định quá mức hoặc đơn giản là chưa được đào tạo đủ lâu. Điều này có nghĩa là mạng chưa học được các mẫu liên quan trong dữ liệu đào tạo.

Tuy nhiên, nếu bạn đào tạo quá lâu, mô hình sẽ bắt đầu quá tải và học các mẫu từ dữ liệu đào tạo không tổng quát hóa thành dữ liệu thử nghiệm. Chúng ta cần phải cân bằng. Hiểu cách huấn luyện cho một số kỷ nguyên thích hợp như chúng ta sẽ khám phá bên dưới là một kỹ năng hữu ích.

Để ngăn chặn việc trang bị quá nhiều, giải pháp tốt nhất là sử dụng dữ liệu đào tạo đầy đủ hơn. Tập dữ liệu phải bao gồm đầy đủ các đầu vào mà mô hình dự kiến ​​sẽ xử lý. Dữ liệu bổ sung có thể chỉ hữu ích nếu nó bao gồm các trường hợp mới và thú vị.

Một mô hình được đào tạo trên dữ liệu đầy đủ hơn sẽ tổng quát hóa tốt hơn một cách tự nhiên. Khi điều đó không còn nữa, giải pháp tốt nhất tiếp theo là sử dụng các kỹ thuật như chính quy hóa. Những ràng buộc này đặt ra những hạn chế về số lượng và loại thông tin mà mô hình của bạn có thể lưu trữ. Nếu một mạng chỉ đủ khả năng ghi nhớ một số lượng nhỏ các mẫu, thì quá trình tối ưu hóa sẽ buộc nó phải tập trung vào các mẫu nổi bật nhất, có cơ hội tổng quát hóa tốt hơn.

Trong sổ tay này, chúng ta sẽ khám phá một số kỹ thuật chính quy hóa phổ biến và sử dụng chúng để cải thiện mô hình phân loại.

Thành lập

Trước khi bắt đầu, hãy nhập các gói cần thiết:

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)

Tập dữ liệu Higgs

Mục tiêu của hướng dẫn này không phải để làm vật lý hạt, vì vậy đừng tập trung vào các chi tiết của tập dữ liệu. Nó chứa 11 000 000 ví dụ, mỗi ví dụ có 28 tính năng và nhãn lớp nhị phân.

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

Lớp tf.data.experimental.CsvDataset có thể được sử dụng để đọc bản ghi csv trực tiếp từ tệp gzip mà không cần bước giải nén trung gian.

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

Lớp trình đọc csv đó trả về một danh sách các đại lượng vô hướng cho mỗi bản ghi. Hàm sau sẽ đóng gói lại danh sách các đại lượng vô hướng đó thành một cặp (feature_vector, label).

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

TensorFlow hiệu quả nhất khi hoạt động trên một loạt dữ liệu lớn.

Vì vậy, thay vì đóng gói lại từng hàng riêng lẻ, hãy tạo Tập dữ liệu mới có các lô 10000 ví dụ, hãy áp dụng pack_row Dataset từng lô, rồi chia các lô sao lưu thành các bản ghi riêng lẻ:

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

Hãy xem một số bản ghi từ packed_ds mới này.

Các tính năng không được chuẩn hóa hoàn toàn, nhưng điều này là đủ cho hướng dẫn này.

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

Để giữ cho hướng dẫn này tương đối ngắn, chỉ sử dụng 1000 mẫu đầu tiên để xác nhận và 10000 mẫu tiếp theo để đào tạo:

N_VALIDATION = int(1e3)
N_TRAIN = int(1e4)
BUFFER_SIZE = int(1e4)
BATCH_SIZE = 500
STEPS_PER_EPOCH = N_TRAIN//BATCH_SIZE

Các phương thức Dataset.skipDataset.take làm cho việc này trở nên dễ dàng.

Đồng thời, sử dụng phương thức Dataset.cache để đảm bảo rằng trình tải không cần đọc lại dữ liệu từ tệp trên mỗi kỷ nguyên:

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

Các tập dữ liệu này trả về các ví dụ riêng lẻ. Sử dụng phương thức .batch để tạo các lô có kích thước thích hợp cho việc đào tạo. Trước khi chia lô cũng nhớ .shuffle.repeat tập huấn luyện.

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

Thể hiện trang phục quá mức

Cách đơn giản nhất để ngăn chặn việc trang bị quá mức là bắt đầu với một mô hình nhỏ: Một mô hình có một số lượng nhỏ các tham số có thể học được (được xác định bởi số lớp và số đơn vị trên mỗi lớp). Trong học tập sâu, số lượng các tham số có thể học được trong một mô hình thường được gọi là "năng lực" của mô hình.

Theo trực giác, một mô hình có nhiều tham số hơn sẽ có nhiều "khả năng ghi nhớ" hơn và do đó sẽ có thể dễ dàng học một ánh xạ giống như từ điển hoàn hảo giữa các mẫu đào tạo và mục tiêu của chúng, một ánh xạ không có bất kỳ sức mạnh tổng quát nào, nhưng điều này sẽ vô ích khi đưa ra dự đoán trên dữ liệu chưa từng thấy trước đây.

Luôn ghi nhớ điều này: các mô hình học sâu có xu hướng phù hợp tốt với dữ liệu đào tạo, nhưng thách thức thực sự là tổng quát hóa chứ không phải phù hợp.

Mặt khác, nếu mạng có tài nguyên ghi nhớ hạn chế, nó sẽ không thể học ánh xạ một cách dễ dàng. Để giảm thiểu sự mất mát của nó, nó sẽ phải học các biểu diễn nén có nhiều khả năng dự đoán hơn. Đồng thời, nếu bạn làm cho mô hình của mình quá nhỏ, nó sẽ khó phù hợp với dữ liệu đào tạo. Có sự cân bằng giữa “quá nhiều dung lượng” và “không đủ dung lượng”.

Thật không may, không có công thức kỳ diệu nào để xác định kích thước hoặc kiến ​​trúc phù hợp của mô hình của bạn (về số lớp hoặc kích thước phù hợp cho mỗi lớp). Bạn sẽ phải thử nghiệm bằng cách sử dụng một loạt các kiến ​​trúc khác nhau.

Để tìm kích thước mô hình thích hợp, tốt nhất nên bắt đầu với tương đối ít lớp và tham số, sau đó bắt đầu tăng kích thước của các lớp hoặc thêm các lớp mới cho đến khi bạn thấy lợi nhuận giảm dần khi mất xác thực.

Bắt đầu với một mô hình đơn giản chỉ sử dụng các lớp, layers.Dense làm đường cơ sở, sau đó tạo các phiên bản lớn hơn và so sánh chúng.

Quy trình đào tạo

Nhiều mô hình đào tạo tốt hơn nếu bạn giảm dần tỷ lệ học tập trong quá trình đào tạo. Sử dụng optimizers.schedules để giảm tốc độ học tập theo thời gian:

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)

Đoạn mã trên đặt một schedules.InverseTimeDecay .InverseTimeDecay giảm tốc độ học theo phương pháp hyperbol xuống 1/2 tốc độ cơ bản ở 1000 kỷ nguyên, 1/3 ở 2000 kỷ nguyên, v.v.

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

Mỗi mô hình trong hướng dẫn này sẽ sử dụng cùng một cấu hình đào tạo. Vì vậy, hãy thiết lập những thứ này theo cách có thể tái sử dụng, bắt đầu với danh sách các lệnh gọi lại.

Việc đào tạo cho hướng dẫn này kéo dài trong nhiều thời kỳ ngắn. Để giảm tiếng ồn ghi nhật ký, hãy sử dụng tfdocs.EpochDots mà chỉ cần in a . cho mỗi kỷ nguyên và một tập hợp đầy đủ các chỉ số cứ sau 100 kỷ nguyên.

Tiếp theo bao gồm callbacks.EarlyStopping để tránh thời gian đào tạo dài và không cần thiết. Lưu ý rằng lệnh gọi lại này được đặt để giám sát val_binary_crossentropy , không phải val_loss . Sự khác biệt này sẽ là quan trọng sau này.

Sử dụng callbacks.TensorBoard để tạo nhật ký TensorBoard cho quá trình đào tạo.

def get_callbacks(name):
  return [
    tfdocs.modeling.EpochDots(),
    tf.keras.callbacks.EarlyStopping(monitor='val_binary_crossentropy', patience=200),
    tf.keras.callbacks.TensorBoard(logdir/name),
  ]

Tương tự, mỗi mô hình sẽ sử dụng cùng một cài đặt Model.compileModel.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

Mô hình tí hon

Bắt đầu bằng cách đào tạo một người mẫu:

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

Bây giờ hãy kiểm tra xem mô hình đã hoạt động như thế nào:

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

Mô hình nhỏ

Để xem bạn có thể đánh bại hiệu suất của mô hình nhỏ hay không, hãy đào tạo dần dần một số mô hình lớn hơn.

Hãy thử hai lớp ẩn với 16 đơn vị mỗi lớp:

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

Mô hình trung bình

Bây giờ hãy thử 3 lớp ẩn với 64 đơn vị mỗi lớp:

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

Và đào tạo mô hình bằng cách sử dụng cùng một dữ liệu:

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

Mô hình lớn

Như một bài tập, bạn có thể tạo một mô hình thậm chí còn lớn hơn và xem nó bắt đầu quá mức nhanh như thế nào. Tiếp theo, hãy thêm vào điểm chuẩn này một mạng có dung lượng lớn hơn nhiều, nhiều hơn những gì vấn đề sẽ xảy ra:

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

Và, một lần nữa, đào tạo mô hình bằng cách sử dụng cùng một dữ liệu:

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

Lập kế hoạch đào tạo và đánh giá tổn thất

Các đường liền nét hiển thị mất mát trong quá trình huấn luyện và các đường đứt nét hiển thị mất xác thực (hãy nhớ: mất xác thực thấp hơn cho thấy một mô hình tốt hơn).

Trong khi xây dựng một mô hình lớn hơn mang lại cho nó nhiều sức mạnh hơn, nếu sức mạnh này không bị hạn chế bằng cách nào đó, nó có thể dễ dàng quá mức cho phép tập luyện.

Trong ví dụ này, thông thường, chỉ có mô hình "Tiny" quản lý để tránh hoàn toàn trang bị quá mức và mỗi mô hình lớn hơn trang bị dữ liệu nhanh hơn. Điều này trở nên nghiêm trọng đối với mô hình "large" đến mức bạn cần chuyển cốt truyện sang quy mô nhật ký để thực sự xem điều gì đang xảy ra.

Điều này rõ ràng nếu bạn vẽ và so sánh các chỉ số xác thực với các chỉ số đào tạo.

  • Có một sự khác biệt nhỏ là điều bình thường.
  • Nếu cả hai chỉ số đều di chuyển theo cùng một hướng, mọi thứ đều ổn.
  • Nếu chỉ số xác thực bắt đầu đình trệ trong khi chỉ số đào tạo tiếp tục được cải thiện, có thể bạn đã gần đến việc trang bị quá mức.
  • Nếu chỉ số xác thực đang đi sai hướng, thì rõ ràng là mô hình đang được trang bị quá mức.
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

Xem trong TensorBoard

Các mô hình này đều viết nhật ký TensorBoard trong quá trình đào tạo.

Mở trình xem TensorBoard được nhúng bên trong sổ ghi chép:

#docs_infra: no_execute

# Load the TensorBoard notebook extension
%load_ext tensorboard

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

Bạn có thể xem kết quả của lần chạy trước đó của sổ ghi chép này trên TensorBoard.dev .

TensorBoard.dev là một trải nghiệm được quản lý để lưu trữ, theo dõi và chia sẻ các thử nghiệm ML với mọi người.

Nó cũng được bao gồm trong một <iframe> để thuận tiện:

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

Nếu bạn muốn chia sẻ kết quả TensorBoard, bạn có thể tải nhật ký lên TensorBoard.dev bằng cách sao chép phần sau vào một ô mã.

tensorboard dev upload --logdir  {logdir}/sizes

Các chiến lược để ngăn chặn việc trang bị quá nhiều

Trước khi đi vào nội dung của phần này, hãy sao chép nhật ký huấn luyện từ mô hình "Tiny" ở trên, để làm cơ sở so sánh.

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

Thêm quy định trọng lượng

Có thể bạn đã quen thuộc với nguyên lý Occam's Razor: đưa ra hai cách giải thích cho một điều gì đó, giải thích có nhiều khả năng đúng nhất là giải thích "đơn giản nhất", giải thích tạo ra ít giả định nhất. Điều này cũng áp dụng cho các mô hình được học bởi mạng nơ-ron: với một số dữ liệu huấn luyện và kiến ​​trúc mạng, có nhiều bộ giá trị trọng số (nhiều mô hình) có thể giải thích dữ liệu và các mô hình đơn giản hơn ít có khả năng được trang bị quá mức so với các mô hình phức tạp.

Một "mô hình đơn giản" trong ngữ cảnh này là một mô hình mà sự phân bố của các giá trị tham số có ít entropy hơn (hoặc một mô hình có ít tham số hơn hoàn toàn, như chúng ta đã thấy trong phần trên). Do đó, một cách phổ biến để giảm thiểu việc trang bị thừa là đặt các hạn chế về độ phức tạp của mạng bằng cách buộc các trọng số của nó chỉ nhận các giá trị nhỏ, điều này làm cho việc phân phối các giá trị trọng số trở nên "đều đặn" hơn. Điều này được gọi là "điều hòa trọng lượng", và nó được thực hiện bằng cách thêm vào chức năng tổn thất của mạng một chi phí liên quan đến việc có trọng số lớn. Chi phí này có hai loại:

  • Chính quy hóa L1 , trong đó chi phí tăng thêm tỷ lệ với giá trị tuyệt đối của các hệ số trọng số (tức là với cái được gọi là "định mức L1" của các trọng số).

  • Chính quy hóa L2 , trong đó chi phí tăng thêm tỷ lệ với bình phương giá trị của các hệ số trọng số (tức là với cái được gọi là "định mức L2" bình phương của các trọng số). Sự chính quy hóa L2 còn được gọi là sự phân rã trọng lượng trong ngữ cảnh của mạng nơ-ron. Đừng để cái tên khác khiến bạn nhầm lẫn: sự phân rã trọng lượng về mặt toán học giống hệt như sự chính quy L2.

Chính quy hóa L1 đẩy các trọng số về phía chính xác bằng không, khuyến khích một mô hình thưa thớt. Việc chính quy hóa L2 sẽ phạt các tham số trọng số mà không làm cho chúng trở nên thưa thớt vì hình phạt về 0 vì trọng số nhỏ - một lý do tại sao L2 phổ biến hơn.

Trong tf.keras , điều chỉnh trọng số được thêm vào bằng cách chuyển các thể hiện của bộ điều chỉnh trọng số cho các lớp dưới dạng đối số từ khóa. Bây giờ chúng ta hãy thêm quy định trọng lượng 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) có nghĩa là mọi hệ số trong ma trận trọng số của lớp sẽ cộng 0.001 * weight_coefficient_value**2 vào tổng tổn thất của mạng.

Đó là lý do tại sao chúng tôi đang theo dõi trực tiếp binary_crossentropy . Bởi vì nó không có thành phần chính quy hóa này trộn lẫn vào.

Vì vậy, cùng một mô hình "Large" với hình phạt chính quy L2 hoạt động tốt hơn nhiều:

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

png

Như bạn có thể thấy, mô hình chính quy "L2" hiện cạnh tranh hơn nhiều với mô hình "Tiny" . Mô hình "L2" này cũng có khả năng chống overfitting tốt hơn nhiều so với mô hình "Large" mà nó dựa trên mặc dù có cùng số lượng thông số.

Thêm thông tin

Có hai điều quan trọng cần lưu ý về loại chính quy này.

Đầu tiên: nếu bạn đang viết vòng lặp đào tạo của riêng mình, thì bạn cần chắc chắn hỏi mô hình về những tổn thất chính quy của nó.

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

Thứ hai: Việc triển khai này hoạt động bằng cách thêm các hình phạt trọng lượng vào sự mất mát của mô hình, và sau đó áp dụng quy trình tối ưu hóa tiêu chuẩn sau đó.

Có một cách tiếp cận thứ hai mà thay vào đó, chỉ chạy trình tối ưu hóa trên mức hao hụt thô và sau đó trong khi áp dụng bước đã tính toán, trình tối ưu hóa cũng áp dụng một số giảm trọng lượng. "Sự sụt giảm trọng lượng theo từng giai đoạn" này được nhìn thấy trong các trình tối optimizers.FTRL hóa như Optimizers.FTRL và optimizers.AdamW .

Thêm học sinh bỏ học

Bỏ học là một trong những kỹ thuật chính quy hóa hiệu quả nhất và được sử dụng phổ biến nhất cho mạng nơ-ron, được phát triển bởi Hinton và các sinh viên của ông tại Đại học Toronto.

Giải thích trực quan cho việc bỏ mạng là bởi vì các nút riêng lẻ trong mạng không thể dựa vào đầu ra của các nút khác, mỗi nút phải tự xuất ra các tính năng hữu ích.

Bỏ học, áp dụng cho một lớp, bao gồm việc "bỏ học" ngẫu nhiên (tức là được đặt thành 0) một số tính năng đầu ra của lớp trong quá trình đào tạo. Giả sử một lớp nhất định thông thường sẽ trả về một vectơ [0,2, 0,5, 1,3, 0,8, 1,1] cho một mẫu đầu vào nhất định trong quá trình đào tạo; sau khi áp dụng loại bỏ, vectơ này sẽ có một vài mục không phân phối ngẫu nhiên, ví dụ: [0, 0.5, 1.3, 0, 1.1].

"Tỷ lệ bỏ học" là phần nhỏ của các đối tượng địa lý đang bị loại bỏ; nó thường được đặt trong khoảng từ 0,2 đến 0,5. Tại thời điểm kiểm tra, không có đơn vị nào bị loại bỏ, và thay vào đó, giá trị đầu ra của lớp được thu nhỏ theo hệ số bằng với tỷ lệ bỏ học, để cân bằng thực tế là nhiều đơn vị đang hoạt động hơn so với thời điểm đào tạo.

Trong tf.keras , bạn có thể giới thiệu tính năng bỏ qua trong mạng thông qua lớp Bỏ qua, lớp này được áp dụng cho đầu ra của lớp ngay trước đó.

Hãy thêm hai lớp Dropout trong mạng của chúng tôi để xem chúng hoạt động như thế nào trong việc giảm trang bị quá mức:

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

Rõ ràng từ âm mưu này rằng cả hai cách tiếp cận chính quy này đều cải thiện hành vi của mô hình "Large" . Nhưng điều này vẫn không đánh bại ngay cả đường cơ sở của "Tiny" .

Tiếp theo, hãy thử cả hai cùng nhau và xem liệu điều đó có tốt hơn không.

Kết hợp L2 + bỏ học

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

Mô hình này với sự chính quy hóa "Combined" rõ ràng là mô hình tốt nhất cho đến nay.

Xem trong TensorBoard

Các mô hình này cũng ghi lại nhật ký TensorBoard.

Để mở trình xem tensorboard được nhúng bên trong máy tính xách tay, hãy sao chép thông tin sau vào một ô mã:

%tensorboard --logdir {logdir}/regularizers

Bạn có thể xem kết quả của lần chạy trước đó của sổ ghi chép này trên TensorDoard.dev .

Nó cũng được bao gồm trong một <iframe> để thuận tiện:

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

Cái này đã được tải lên với:

tensorboard dev upload --logdir  {logdir}/regularizers

Kết luận

Tóm lại: đây là những cách phổ biến nhất để ngăn chặn việc trang bị quá mức trong mạng nơ-ron:

  • Nhận thêm dữ liệu đào tạo.
  • Giảm dung lượng của mạng.
  • Thêm quy định trọng lượng.
  • Thêm học sinh bỏ học.

Hai cách tiếp cận quan trọng không được đề cập trong hướng dẫn này là:

  • tăng dữ liệu
  • bình thường hóa hàng loạt

Hãy nhớ rằng mỗi phương pháp có thể giúp ích cho riêng mình, nhưng thường xuyên kết hợp chúng có thể thậm chí còn hiệu quả hơn.

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