הצג באתר TensorFlow.org | הפעל בגוגל קולאב | צפה במקור ב-GitHub | הורד מחברת |
כמו תמיד, הקוד בדוגמה זו ישתמש ב-API של tf.keras
, עליו תוכל ללמוד עוד במדריך TensorFlow Keras .
בשתי הדוגמאות הקודמות - סיווג טקסט וחיזוי יעילות הדלק - ראינו שהדיוק של המודל שלנו בנתוני האימות יגיע לשיא לאחר אימון במשך מספר תקופות, ולאחר מכן יעמוד או יתחיל לרדת.
במילים אחרות, המודל שלנו יתאים יותר מדי לנתוני האימון. חשוב ללמוד כיצד להתמודד עם התאמה יתר. למרות שלעיתים קרובות ניתן להשיג דיוק גבוה במערך האימונים , מה שאנחנו באמת רוצים זה לפתח מודלים שמכללים היטב את מערך הבדיקות (או נתונים שהם לא ראו בעבר).
ההיפך מהתאמה יתר היא התאמה לא קטנה. תת-התאמה מתרחשת כאשר עדיין יש מקום לשיפור בנתוני הרכבת. זה יכול לקרות מכמה סיבות: אם הדגם אינו חזק מספיק, הוא מוסדר יתר על המידה, או פשוט לא עבר הכשרה מספיק זמן. המשמעות היא שהרשת לא למדה את הדפוסים הרלוונטיים בנתוני האימון.
אם אתה מתאמן יותר מדי זמן, המודל יתחיל להתאים יתר על המידה וללמוד דפוסים מנתוני האימון שאינם מכלילים את נתוני המבחן. אנחנו צריכים למצוא איזון. ההבנה כיצד להתאמן למספר מתאים של תקופות כפי שנחקור להלן היא מיומנות שימושית.
כדי למנוע התאמת יתר, הפתרון הטוב ביותר הוא להשתמש בנתוני אימון מלאים יותר. מערך הנתונים צריך לכסות את כל מגוון התשומות שהמודל צפוי לטפל בהם. נתונים נוספים עשויים להיות שימושיים רק אם הם מכסים מקרים חדשים ומעניינים.
מודל שאומן על נתונים מלאים יותר יעשה הכללה באופן טבעי טוב יותר. כאשר זה כבר לא אפשרי, הפתרון הבא הטוב ביותר הוא להשתמש בטכניקות כמו רגוליזציה. אלה מציבים מגבלות על כמות וסוג המידע שהמודל שלך יכול לאחסן. אם רשת יכולה להרשות לעצמה רק לשנן מספר קטן של דפוסים, תהליך האופטימיזציה יאלץ אותה להתמקד בדפוסים הבולטים ביותר, שיש להם סיכוי טוב יותר להכליל היטב.
במחברת זו, נחקור מספר טכניקות רגוליזציה נפוצות, ונשתמש בהן כדי לשפר מודל סיווג.
להכין
לפני שתתחיל, ייבא את החבילות הדרושות:
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 מחזירה רשימה של סקלרים עבור כל רשומה. הפונקציה הבאה אורזת מחדש את רשימת הסקלרים לזוג (feature_vector, label).
def pack_row(*row):
label = row[0]
features = tf.stack(row[1:],1)
return features, label
TensorFlow הוא היעיל ביותר כאשר הוא פועל על קבוצות גדולות של נתונים.
אז במקום לארוז מחדש כל שורה בנפרד, צור Dataset
חדש שלוקח אצוות של 10,000 דוגמאות, החל את הפונקציה 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)
כדי לשמור על הדרכה קצרה יחסית, השתמש רק ב-1000 הדוגמאות הראשונות לאימות, וב-10,000 הבאות להדרכה:
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')
כל דגם במדריך זה ישתמש באותה תצורת אימון. אז הגדר את אלה בצורה ניתנת לשימוש חוזר, החל מרשימת ההתקשרות חזרה.
ההדרכה להדרכה זו נמשכת תקופות קצרות רבות. כדי להפחית את רעש הרישום השתמש ב- tfdocs.EpochDots
שפשוט מדפיס .
עבור כל תקופה, ומערכת מלאה של מדדים כל 100 עידנים.
בשלב הבא callbacks.EarlyStopping
. עצירה מוקדמת כדי למנוע זמני אימון ארוכים ומיותרים. שים לב שהקריאה חוזרת זו מוגדרת לנטר את 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 _________________________________________________________________ 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)
דגם קטן
כדי לראות אם אתה יכול לנצח את הביצועים של הדגם הקטן, אמן בהדרגה כמה דגמים גדולים יותר.
נסה שתי שכבות נסתרות עם 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"
שאתה צריך להעביר את העלילה ל-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]')
הצג ב- 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 regularization , כאשר העלות הנוספת היא פרופורציונלית לערך המוחלט של מקדמי המשקולות (כלומר למה שנקרא "נורמת L1" של המשקולות).
L2 regularization , כאשר העלות הנוספת היא פרופורציונלית לריבוע ערך מקדמי המשקולות (כלומר למה שנקרא "נורמת 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)
כפי שאתה יכול לראות, הדגם המוסדר "L2"
הוא כעת הרבה יותר תחרותי עם הדגם "Tiny"
. דגם "L2"
זה גם עמיד הרבה יותר בפני התאמה יתר מהדגם "Large"
שעליו התבסס למרות שיש לו אותו מספר פרמטרים.
עוד מידע
יש שני דברים חשובים לשים לב לסוג זה של הסדרה.
ראשית: אם אתה כותב לולאת אימון משלך, עליך להיות בטוח לבקש מהמודל את הפסדי ההסדרה שלו.
result = l2_model(features)
regularization_loss=tf.add_n(l2_model.losses)
שנית: יישום זה עובד על ידי הוספת עונשי המשקל לאובדן המודל, ולאחר מכן יישום נוהל אופטימיזציה סטנדרטי לאחר מכן.
ישנה גישה שניה שבמקום זאת מפעילה את האופטימיזר רק על ההפסד הגולמי, ולאחר מכן תוך יישום הצעד המחושב האופטימיזר מחיל גם דעיכה מסוימת במשקל. "Decoupled Weight Decay" זה נראה באופטימייזרים כמו optimizers.FTRL
ו- optimizers.AdamW
.
הוסף נשירה
נשירה היא אחת מטכניקות הרגולציה היעילות והנפוצות ביותר עבור רשתות עצביות, שפותחה על ידי הינטון ותלמידיו באוניברסיטת טורונטו.
ההסבר האינטואיטיבי לנשירה הוא שמכיוון שצמתים בודדים ברשת אינם יכולים להסתמך על הפלט של האחרים, כל צומת חייב להוציא תכונות שימושיות בפני עצמן.
נשירה, המוחלת על שכבה, מורכבת מ"נשירה" אקראית (כלומר הגדרה לאפס) של מספר תכונות פלט של השכבה במהלך האימון. נניח ששכבה נתונה בדרך כלל הייתה מחזירה וקטור [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_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)
ברור מהעלילה הזו ששתי גישות ההסדרה הללו משפרות את התנהגות המודל "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)
המודל הזה עם ההסדרה "Combined"
הוא ללא ספק הטוב ביותר עד כה.
הצג ב- TensorBoard
מודלים אלה גם רשמו יומני 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.