לוגו זפירנט

הבנת התאמה יתר ב-ConvNets

תאריך:

מבוא

התאמת יתר ב-ConvNets היא אתגר בלמידה עמוקה וברשתות עצביות, שבהן מודל לומד יותר מדי מנתוני אימון, מה שמוביל לביצועים גרועים בנתונים חדשים. תופעה זו נפוצה במיוחד בארכיטקטורות עצביות מורכבות, שיכולות לדמות מערכות יחסים מורכבות. טיפול בהתאמת יתר ב-convnet הוא חיוני לבניית מודלים אמינים של רשתות עצבים. מאמר זה מספק מדריך להבנה והפחתת התאמת יתר, בחינת סיבות שורש כמו מורכבות המודל, נתוני אימון מוגבלים ותכונות רועשות. הוא גם דן בטכניקות למניעת התאמת יתר, כגון אסטרטגיות הגדלת נתונים ושיטות רגולציה.

הייתי ממליץ לקרוא מאמרים אלה להבנה בסיסית ב התאמת יתר, התאמת יתר ו פשרה בשונות הטיה.

למד יעדים

  • הבן את הסיבות, ההשלכות והתרחישים של התאמת יתר ConvNets.
  • פרש עקומות למידה כדי לזהות התאמה יתר וחוסר התאמה במודלים של רשתות עצביות.
  • למד טכניקות שונות להפחתת התאמת יתר, כגון עצירה מוקדמת, נשירה, נורמליזציה של אצווה, רגולציה והגדלת נתונים.
  • יישם טכניקות אלה באמצעות TensorFlow ו-Keras לאמן ConvNets על מערך הנתונים של CIFAR-10.
  • נתח את ההשפעה של טכניקות שונות על ביצועי המודל והכללה.

תוכן העניינים

תרחישים נפוצים להתאמת יתר ב-ConvNet

הבה נבחן כמה תרחישים נפוצים של התאמת יתר ב-ConvNet:

תרחיש 1: מודל מורכב מאוד עם נתונים לא מספיקים

שימוש במודל מורכב מאוד, כגון רשת עצבית עמוקה, על מערך נתונים קטן עלול להוביל להתאמת יתר. המודל עשוי לשנן את דוגמאות האימון במקום ללמוד את הדפוס הכללי. לדוגמה, אימון רשת עצבית עמוקה עם כמה מאות תמונות בלבד למשימה מורכבת כמו זיהוי תמונות עלול להוביל להתאמת יתר.

תוצאה

המודל עשוי לבצע ביצועים טובים מאוד בנתוני האימון אך לא מצליח להכליל לנתונים חדשים, בלתי נראים, וכתוצאה מכך ביצועים גרועים ביישומים בעולם האמיתי.

כיצד לפתור בעיה זו?

קבל יותר נתוני אימון, בצע הגדלת תמונה כדי להכליל את מערך הנתונים שלנו. התחל עם מודל פחות מורכב ואם הקיבולת קטנה אז הגדל את המורכבות. 

תרחיש 2: אימון מוגזם

אימון רציף של דגם לתקופות רבות מדי עלול להוביל להתאמת יתר. כאשר המודל רואה את נתוני האימון שוב ושוב, הוא עשוי להתחיל לשנן אותם במקום ללמוד את הדפוסים הבסיסיים.

תוצאה

הביצועים של המודל עשויים להגיע לרמה או אפילו להתדרדר בנתונים בלתי נראים ככל שהוא מתמחה יותר ויותר למערך האימונים.

כיצד לפתור בעיה זו?

השתמש בעצירה מוקדמת כדי להימנע מהדגם כדי להתאים יותר ולשמור את הדגם הטוב ביותר. 

תרחיש 3: התעלמות מהסדרה

טכניקות רגוליזציה, כגון רגוליזציה L1 או L2, משמשות למניעת התאמת יתר על ידי ענישה של מודלים מורכבים. התעלמות או כוונון לא נכון של פרמטרי רגולציה עלולים להוביל להתאמת יתר.

תוצאה

המודל עלול להפוך למורכב מדי ולא להכליל היטב לנתונים חדשים, וכתוצאה מכך ביצועים גרועים מחוץ למערך האימונים.

כיצד לפתור בעיה זו?

יישום רגוליזציה, אימות צולב, כוונון היפר פרמטרים. 

מהי הקיבולת של הדגם?

היכולת של מודל מתייחסת לגודל ולמורכבות של הדפוסים שהוא מסוגל ללמוד. עבור רשתות עצביות, זה ייקבע במידה רבה על ידי כמה נוירונים יש לה וכיצד הם מחוברים זה לזה. אם נראה שהרשת שלך לא מתאימה לנתונים, עליך לנסות להגדיל את הקיבולת שלה.

אתה יכול להגדיל את הקיבולת של רשת על ידי הפיכתה לרחבה יותר (יותר יחידות לשכבות קיימות) או על ידי הפיכתה לעומק יותר (הוספת שכבות נוספות). לרשתות רחבות יותר קל יותר ללמוד מערכות יחסים ליניאריות יותר, בעוד שרשתות עמוקות יותר מעדיפות מערכות לא ליניאריות יותר. מה עדיף תלוי רק במערך הנתונים.

פירוש עקומות למידה

Keras מספקת את היכולת לרשום התקשרויות חוזרות בעת אימון א מודל למידה עמוקה. אחת מקריאת ברירת המחדל שנרשמת בעת אימון כל מודלים של למידה עמוקה היא ה-Callback בהיסטוריה. הוא רושם מדדי אימון עבור כל תקופה. זה כולל את האובדן והדיוק (עבור בעיות סיווג) ואת האובדן והדיוק עבור מערך האימות אם מוגדר כזה.

אובייקט ההיסטוריה מוחזר מקריאות לפונקציה fit() המשמשת לאימון המודל. מדדים מאוחסנים במילון באיבר ההיסטוריה של האובייקט המוחזר.

לדוגמה, אתה יכול לרשום את המדדים שנאספו באובייקט היסטוריה באמצעות קטע הקוד הבא לאחר אימון המודל:

# list all data in history
print(history.history.keys())

פלט:

['accuracy', 'loss', 'val_accuracy', 'val_loss']

סוג מידע

אתה עשוי לחשוב על המידע בנתוני האימון כאל שני סוגים:

  • אות: האות הוא החלק שמכליל, החלק שיכול לעזור למודל שלנו לבצע תחזיות מנתונים חדשים.
  • רַעַשׁ: הרעש הוא אותו חלק שנכון רק לנתוני האימון; הרעש הוא כל התנודות האקראיות שמגיעות מנתונים בעולם האמיתי או כל הדפוסים המקריים, הלא אינפורמטיביים, שאינם יכולים למעשה לעזור למודל לבצע תחזיות. הרעש הוא שהחלק עשוי להיראות שימושי אבל ממש לא.

כאשר אנו מאמנים מודל, אנו מתווים את ההפסד על מערכת האימונים תקופה אחר תקופה. לכך נוסיף גם עלילה של נתוני האימות. עלילות אלו אנו מכנים עקומות הלמידה. כדי לאמן מודלים של למידה עמוקה ביעילות, עלינו להיות מסוגלים לפרש אותם.

עקומות למידה

באיור לעיל אנו יכולים לראות שאובדן האימון פוחת ככל שהתקופות גדלות, אך אובדן האימות פוחת בהתחלה ועולה כאשר המודל מתחיל ללכוד רעש הקיים במערך הנתונים. כעת אנו הולכים לראות כיצד להימנע מהתאמה יתר של ConvNets באמצעות טכניקות שונות. 

שיטות להימנע מהתאמה יתר

כעת, לאחר שראינו כמה תרחישים וכיצד לפרש עקומות למידה כדי לזהות התאמת יתר. בואו נבדוק כמה שיטות כדי למנוע התאמת יתר ברשת עצבית:

שיטה 1: השתמש בנתונים נוספים

הגדלת גודל מערך הנתונים שלך יכולה לעזור למודל להכליל טוב יותר מכיוון שיש לו דוגמאות מגוונות יותר ללמוד מהן. המודל ימצא דפוסים חשובים הקיימים במערך הנתונים ויתעלם מרעש מכיוון שהמודל מבין שהדפוסים הספציפיים (רעש) אלה אינם קיימים בכל מערך הנתונים.

שיטה 2: עצירה מוקדמת

עצירה מוקדמת היא טכניקה המשמשת למניעת התאמת יתר על ידי ניטור ביצועי המודל על ערכת אימות במהלך האימון. האימון מופסק כאשר הביצועים בערכת האימות מתחילים להתדרדר, מה שמצביע על כך שהדגם מתחיל להתאים יותר מדי. בדרך כלל, ערכת אימות נפרדת משמשת לניטור ביצועים, והאימון מופסק כאשר הביצועים לא השתפרו במשך מספר מוגדר של תקופות.

התאמה והתאמת יתר

שיטה 3: נשירה

אנו יודעים שהתאמת יתר נגרמת על ידי הרשת לומדת דפוסים מזויפים (רעש) בנתוני האימון. כדי לזהות את הדפוסים המזויפים הללו, רשת תסתמך לעתים קרובות על שילובים ספציפיים מאוד של משקל, מעין "קונספירציה" של משקלים. בהיותם כל כך ספציפיים, הם נוטים להיות שבירים: הסר אחד והקונספירציה מתפרקת.

זה הרעיון מאחורי הנשירה. כדי לשבור את הקונספירציות הללו, אנו נושרים באופן אקראי חלק מיחידות הקלט של שכבה בכל שלב באימון, מה שמקשה הרבה יותר על הרשת ללמוד את הדפוסים המזויפים האלה בנתוני האימון. במקום זאת, עליו לחפש דפוסים רחבים וכלליים, שדפוסי המשקל שלהם נוטים להיות חזקים יותר. 

אפשר גם לחשוב על נשירה כיצירת סוג של אנסמבל של רשתות. התחזיות לא ייעשו עוד על ידי רשת אחת גדולה, אלא על ידי ועדה של רשתות קטנות יותר. אנשים בוועדה נוטים לעשות טעויות מסוגים שונים, אך להיות צודקים בו זמנית, מה שהופך את הוועדה בכללותה לטובה יותר מכל אדם. (אם אתה מכיר יערות אקראיים כמכלול של עצי החלטה, זה אותו רעיון.)

התאמת יתר ב-ConvNets

שיטה 4: נורמליזציה של אצווה

השיטה המיוחדת הבאה שנסתכל עליה מבצעת "נורמליזציה אצווה" (או "נורמה אצווה"), מה שיכול לעזור לתקן אימון שהוא איטי או לא יציב.

עם רשתות עצביות, זה בדרך כלל רעיון טוב לשים את כל הנתונים שלך בקנה מידה נפוץ, אולי עם משהו כמו StandardScaler של sikit-learn או MinMaxScaler. הסיבה היא ש-SGD יעביר את משקלי הרשת ביחס לגודל ההפעלה שהנתונים מייצרים. תכונות שנוטות לייצר הפעלות בגדלים שונים מאוד יכולות לגרום להתנהגות אימון לא יציבה.

עכשיו, אם זה טוב לנרמל את הנתונים לפני שהם נכנסים לרשת, אולי גם נרמול בתוך הרשת יהיה טוב יותר! למעשה, יש לנו סוג מיוחד של שכבה שיכולה לעשות זאת, שכבת הנורמליזציה של אצווה. שכבת נורמליזציה של אצווה בוחנת כל אצווה כפי שהיא נכנסת, תחילה מנרמלת את האצווה עם ממוצע וסטיית תקן משלה, ולאחר מכן גם מעמידה את הנתונים על קנה מידה חדש עם שני פרמטרים לשינוי קנה מידה שניתנים לאימון. Batchnorm, למעשה, מבצעת סוג של שינוי קנה מידה מתואם של התשומות שלה.

לרוב, batchnorm מתווספת כסיוע לתהליך האופטימיזציה (אם כי זה יכול לפעמים גם לסייע בחיזוי ביצועים). דגמים עם נורמה אצווה נוטים להזדקק לפחות עידנים כדי להשלים הכשרה. יתרה מכך, batchnorm יכולה גם לתקן בעיות שונות שעלולות לגרום לאימון "להיתקע". שקול להוסיף נורמליזציה אצווה לדגמים שלך, במיוחד אם אתה נתקל בבעיות במהלך האימון.

שיטה 5: רגוליזציה L1 ו-L2

הסדרת L1 ו-L2 הן טכניקות המשמשות למניעת התאמת יתר על ידי ענישה של משקלים גדולים ברשת העצבית. הסדרת L1 מוסיפה מונח עונש לפונקציית ההפסד פרופורציונלית לערך המוחלט של המשקולות. זה מעודד דלילות במשקלים ויכול להוביל לבחירת תכונה. הסדרת L2, הידועה גם בשם דעיכה במשקל, מוסיפה מונח עונש פרופורציונלי לריבוע המשקולות לפונקציית הירידה. זה מונע מהמשקולות להפוך לגדולות מדי ומעודד את פיזור המשקולות בצורה שווה יותר.

הבחירה בין הסדרת L1 ו-L2 תלויה לרוב בבעיה הספציפית ובמאפיינים הרצויים של המודל.

קיום ערכים גדולים להסדרת L1/L2 יגרום למודל לא ללמוד מהר ולהגיע לרמה בלמידה מה שיגרום למודל לא להתאים. 

שיטה 6: הגדלת נתונים

הדרך הטובה ביותר לשפר את הביצועים של מודל למידת מכונה היא לאמן אותו על יותר נתונים. ככל שהמודל צריך ללמוד מהן יותר דוגמאות, כך הוא יוכל לזהות טוב יותר אילו הבדלים בתמונות חשובים ואילו לא. יותר נתונים עוזרים למודל להכליל טוב יותר.

דרך קלה אחת להשיג יותר נתונים היא להשתמש בנתונים שכבר יש לך. אם נוכל לשנות את התמונות במערך הנתונים שלנו בדרכים המשמרות את המחלקה (לדוגמה: סיווג ספרות MNIST אם ננסה להגדיל את 6 יהיה קשה להבחין בין 6 ל-9), נוכל ללמד את המסווג שלנו להתעלם מסוגים אלה של טרנספורמציות. לדוגמה, אם מכונית פונה שמאלה או ימינה בתמונה, לא משנה את העובדה שהיא מכונית ולא משאית. לכן, אם אנו מגדילים את נתוני האימון שלנו עם תמונות מתהפכות, המסווג שלנו ילמד ש"שמאל או ימין" הוא הבדל שהוא צריך להתעלם ממנו.

וזה כל הרעיון מאחורי הגדלת נתונים: הוסף עוד כמה נתונים מזויפים שנראים באופן סביר כמו הנתונים האמיתיים והמסווג שלך ישתפר. 

זכור, המפתח להימנעות מהתאמה יתרה הוא לוודא שהדגם שלך מכליל היטב. בדוק תמיד את הביצועים של הדגם שלך על ערכת אימות, לא רק על ערכת ההדרכה.

יישום של שיטות לעיל עם נתונים

הבה נבחן את שלבי היישום עבור השיטות הנ"ל:

שלב 1: טעינת ספריות נחוצות

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import tensorflow as tf
from tensorflow.keras import datasets, layers, models
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from tensorflow.keras.callbacks import ModelCheckpoint
import keras
from keras.preprocessing import image
from keras import models, layers, regularizers
from tqdm import tqdm
import warnings
warnings.filterwarnings(action='ignore')

שלב 2: טעינת ערכת נתונים ועיבוד מקדים

#Here all the images are in the form of a numpy array
cifar10 = tf.keras.datasets.cifar10
(x_train, y_train), (x_test, y_test) = cifar10.load_data()
x_train = x_train / 255.0
x_test = x_test / 255.0

שלב 3: לימוד מערך נתונים

x_train.shape, y_train.shape, x_test.shape, y_test.shape 

פלט:

תְפוּקָה
np.unique(y_train)

פלט:

תְפוּקָה
#These labels are in the order and taken from the documentaion
class_names = ['airplane', 'automobile', 'bird', 'cat', 'deer',
               'dog', 'frog', 'horse', 'ship', 'truck']

שלב 4: הדמיית תמונה מתוך מערך נתונים

def show_image(IMG_INDEX):
    plt.imshow(x_train[20] ,cmap=plt.cm.binary)
    plt.xlabel(class_names[y_train[IMG_INDEX][0]])
    plt.show()
show_image(20)
התאמת יתר ב-ConvNets
model = models.Sequential()
model.add(layers.Conv2D(32, (3, 3), activation='relu', input_shape=(32, 32, 3)))
model.add(layers.AveragePooling2D((2, 2)))
model.add(layers.Conv2D(64, (3, 3), activation='relu'))
model.add(layers.AveragePooling2D((2, 2)))
model.add(layers.Conv2D(64, (3, 3), activation='relu'))
model.add(layers.Flatten())
model.add(layers.Dense(64, activation='relu'))
model.add(layers.Dense(10))
model.summary()

תנו לנו כעת לאתחל פרמטרי היפר ומודל קומפילציה עם אופטימיזציה, פונקציית אובדן ומדד הערכה.


train_hyperparameters_config={'optim':keras.optimizers.Adam(learning_rate=0.001),
                             'epochs':20,
                              'batch_size':16
                             }
model.compile(optimizer=train_hyperparameters_config['optim'],
                  loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
                  metrics=['accuracy'])

שלב 6: מודל הדרכה

history = model.fit(x_train, y_train, 
                        epochs=train_hyperparameters_config['epochs'], 
                        batch_size=train_hyperparameters_config['batch_size'], 
                        verbose=1,
                        validation_data=(x_test, y_test))

שלב 7: הערכת המודל

אלה יספרו לנו את המידע הכלול באובייקט ההיסטוריה ואנו משתמשים בהם כדי ליצור את עקומות המידע שלנו.

print(history.history.keys()) 
def learning_curves(history):
# Plotting Accuracy
    plt.figure(figsize=(14, 5))  # Adjust the figure size as needed
    plt.subplot(1, 2, 1)  # Subplot with 1 row, 2 columns, and index 1
    plt.plot(history.history['accuracy'], label='train_accuracy', marker='s', markersize=4)
    plt.plot(history.history['val_accuracy'], label='val_accuracy', marker='*', markersize=4)
    plt.xlabel('Epoch')
    plt.ylabel('Accuracy')
    plt.legend(loc='lower right')

    # Plotting Loss
    plt.subplot(1, 2, 2)  # Subplot with 1 row, 2 columns, and index 2
    plt.plot(history.history['loss'], label='train_loss', marker='s', markersize=4)
    plt.plot(history.history['val_loss'], label='val_loss', marker='*', markersize=4)
    plt.xlabel('Epoch')
    plt.ylabel('Loss')
    plt.legend(loc='lower right')

    plt.show()
learning_curves(history)
התאמת יתר ב-ConvNets

מהעקומות אנו יכולים לראות שדיוק האימות מגיע לרמה לאחר העידן הרביעי והמודל מתחיל ללכוד רעש. לפיכך ניישם עצירה מוקדמת כדי למנוע התאמה יתר של הדגם ונחזיר את המשקולות הטובות ביותר על בסיס val_loss. אנו נשתמש ב-val_loss כדי לנטר עצירה מוקדמת כאשר הרשת העצבית שלנו מנסה לצמצם אובדן באמצעות כלי אופטימיזציה. דיוק ודיוק אימות תלויים בסף (הסתברות להפרדת מחלקות - בדרך כלל 4 לסיווג בינארי), כך שאם מערך הנתונים שלנו אינו מאוזן זה יהיה הפסד שעלינו לדאוג לגביו ברוב המקרים. 

שלב 8: יישום עצירה מוקדמת

מכיוון שאנו לא מודאגים שהדגם שלנו יתאים יתר על המידה שכן עצירה מוקדמת תמנע מהדגם שלנו לקרות. זוהי בחירה טובה לבחור מספר גבוה יותר של עידנים וסבלנות מתאימה. כעת נשתמש באותה ארכיטקטורת מודל ונתרגל עם עצירה מוקדמת של התקשרות חוזרת. 

model = models.Sequential()
model.add(layers.Conv2D(32, (3, 3), activation='relu', input_shape=(32, 32, 3)))
model.add(layers.AveragePooling2D((2, 2)))
model.add(layers.Conv2D(64, (3, 3), activation='relu'))
model.add(layers.AveragePooling2D((2, 2)))
model.add(layers.Conv2D(64, (3, 3), activation='relu'))
model.add(layers.Flatten())
model.add(layers.Dense(128, activation='relu'))
model.add(layers.Dense(64, activation='relu'))
model.add(layers.Dense(10))
model.summary()

# Here we have used more epochs than needed since we use patience parameter which we stop the model from overfitting
train_hyperparameters_config = {
    'optim': keras.optimizers.Adam(learning_rate=0.001),
    'patience': 5,
    'epochs': 50,
    'batch_size': 32, 
}
print('Setting the callback and early stopping configurations...')
callback = tf.keras.callbacks.EarlyStopping(
    monitor='val_loss', 
    min_delta=0.001, # minimium amount of change to count as an improvement
    patience=train_hyperparameters_config['patience'], 
    restore_best_weights=True)

def model_train(model, x_train, y_train, x_test, y_test, train_hyperparameters_config):
    model.compile(optimizer=train_hyperparameters_config['optim'],
                      loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
                      metrics=['accuracy'])
    ht = model.fit(x_train, y_train, 
                            epochs=train_hyperparameters_config['epochs'], 
                            batch_size=train_hyperparameters_config['batch_size'],
                            callbacks=[callback],
                            verbose=1,
                            validation_data=(x_test, y_test))
    return ht

ht=model_train(model, x_train, y_train, x_test, y_test, train_hyperparameters_config)
learning_curves(ht)
התאמת יתר ב-ConvNets

לדעת את המשקולות הטובות ביותר שלנו שהדגם לקח. 

print('Testing ..................')
test_loss, test_acc = model.evaluate(x_test,  y_test, verbose=2)
print('test_loss : ', test_loss, 'test_accuracy : ', test_acc)

שלב 9: הגדלת מורכבות הדגם

מכיוון שהמודל שלנו אינו מתפקד טוב ואינו מתאים מכיוון שהוא אינו מסוגל ללכוד מספיק נתונים. עלינו להגביר את מורכבות המודל שלנו ולהעריך. 

model = models.Sequential()

model.add(layers.Conv2D(128, (3, 3), activation='relu', input_shape=(32, 32, 3)))
model.add(layers.MaxPooling2D((2, 2)))

model.add(layers.Conv2D(256, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))

model.add(layers.Conv2D(256, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))

model.add(layers.Conv2D(512, (3, 3), activation='relu', padding='same'))
model.add(layers.MaxPooling2D((2, 2)))

model.add(layers.Flatten())
model.add(layers.Dense(256, activation='relu'))
model.add(layers.Dense(128, activation='relu'))
model.add(layers.Dense(10, activation='softmax'))
model.summary()
תְפוּקָה

אנחנו יכולים לראות שיש עלייה בסך הפרמטרים. זה יעזור למצוא קשרים מורכבים יותר במודל שלנו. הערה: מערך הנתונים שלנו הוא של תמונות בגודל 32X32; אלו תמונות קטנות יחסית. מכאן ששימוש במודלים מורכבים יותר בהתחלה בוודאי יתאים יותר למודל ולכן אנו נוטים להגדיל את מורכבות המודל שלנו לאט.

# Here we have used more epochs than needed since we use patience parameter which we stop the model from overfitting
train_hyperparameters_config = {
    'optim': keras.optimizers.Adam(learning_rate=0.001),
    'patience': 5,
    'epochs': 50,
    'batch_size': 32, 
}
print('Setting the callback and early stopping configurations...')
callback = tf.keras.callbacks.EarlyStopping(
    monitor='val_loss', 
    min_delta=0.001, # minimium amount of change to count as an improvement
    patience=train_hyperparameters_config['patience'], 
    restore_best_weights=True)
ht=model_train(model, x_train, y_train, x_test, y_test, train_hyperparameters_config)

learning_curves(ht)
התאמת יתר ב-ConvNets
print('Testing ..................')
test_loss, test_acc = model.evaluate(x_test,  y_test, verbose=2)
print('test_loss : ', test_loss, 'test_accuracy : ', test_acc)

מהגרפים שלמעלה נוכל לומר בבירור שהמודל מתאים יתר על המידה, ולכן נשתמש בשיטה אחרת שנקראת Drop out normalization ו- Batch normalization.

שלב 10: שימוש בשכבות נשירה ושכבות נורמליזציה של אצווה

model = models.Sequential()

model.add(layers.Conv2D(128, (3, 3), activation='relu', input_shape=(32, 32, 3)))
model.add(layers.BatchNormalization())
model.add(layers.MaxPooling2D((2, 2)))

model.add(layers.Conv2D(256, (3, 3), activation='relu'))
model.add(layers.BatchNormalization())
model.add(layers.MaxPooling2D((2, 2)))

model.add(layers.Conv2D(256, (3, 3), activation='relu'))
model.add(layers.BatchNormalization())
model.add(layers.MaxPooling2D((2, 2)))

model.add(layers.Conv2D(512, (3, 3), activation='relu', padding='same'))
model.add(layers.BatchNormalization())
model.add(layers.MaxPooling2D((2, 2)))

model.add(layers.Flatten())
model.add(layers.Dense(256, activation='relu'))
model.add(layers.BatchNormalization())
model.add(layers.Dropout(0.3))

model.add(layers.Dense(128, activation='relu'))
model.add(layers.BatchNormalization())
model.add(layers.Dropout(0.3))

model.add(layers.Dense(10, activation='softmax'))
model.summary()
תְפוּקָה
# Here we have used more epochs than needed since we use patience parameter which we stop the model from overfitting
train_hyperparameters_config = {
    'optim': keras.optimizers.Adam(learning_rate=0.001),
    'patience': 5,
    'epochs': 50,
    'batch_size': 32, 
}
print('Setting the callback and early stopping configurations...')
callback = tf.keras.callbacks.EarlyStopping(
    monitor='val_loss', 
    min_delta=0.001, # minimium amount of change to count as an improvement
    patience=train_hyperparameters_config['patience'], 
    restore_best_weights=True)
ht=model_train(model, x_train, y_train, x_test, y_test, train_hyperparameters_config)
learning_curves(ht)
התאמת יתר ב-ConvNets
print('Testing ..................')
test_loss, test_acc = model.evaluate(x_test,  y_test, verbose=2)
print('test_loss : ', test_loss, 'test_accuracy : ', test_acc)

מגרפי הלמידה אנו יכולים לראות שהמודל מתאים יותר מדי אפילו עם נורמליזציה אצווה ושכבות נשירה. מכאן שבמקום להגדיל את המורכבות אלא להגדיל את מספר המסננים. היינו מוסיפים עוד שכבות קונבולציה כדי לחלץ עוד תכונות.

שלב 11: הגדלת שכבות קונבולוציה

הקטן את הפרמטר הניתן לאימון אך הגדל את שכבות הקונבולציה כדי לחלץ תכונות נוספות.

model = models.Sequential()

model.add(layers.Conv2D(32, (3, 3), activation='relu', padding='same', input_shape=(32, 32, 3)))
model.add(layers.BatchNormalization())
model.add(layers.Conv2D(32, (3, 3), activation='relu', padding='same'))
model.add(layers.BatchNormalization())
model.add(layers.MaxPool2D((2, 2)))
model.add(layers.Dropout(0.2))

model.add(layers.Conv2D(64, (3, 3), activation='relu', padding='same'))
model.add(layers.BatchNormalization())
model.add(layers.Conv2D(64, (3, 3), activation='relu', padding='same'))
model.add(layers.BatchNormalization())
model.add(layers.MaxPool2D((2, 2)))
model.add(layers.Dropout(0.3))

model.add(layers.Conv2D(128, (3, 3), activation='relu', padding='same'))
model.add(layers.BatchNormalization())
model.add(layers.Conv2D(128, (3, 3), activation='relu', padding='same'))
model.add(layers.BatchNormalization())
model.add(layers.MaxPool2D((2, 2)))
model.add(layers.Dropout(0.4))

model.add(layers.Flatten())
model.add(layers.Dense(128, activation='relu'))
model.add(layers.BatchNormalization())
model.add(layers.Dropout(0.5))

model.add(layers.Dense(10, activation='softmax'))

model.summary()
# Here we have used more epochs than needed since we use patience parameter which we stop the model from overfitting
train_hyperparameters_config = {
    'optim': keras.optimizers.Adam(learning_rate=0.001),
    'patience': 5,
    'epochs': 50,
    'batch_size': 32, 
}
print('Setting the callback and early stopping configurations...')
callback = tf.keras.callbacks.EarlyStopping(
    monitor='val_loss', 
    min_delta=0.001, # minimium amount of change to count as an improvement
    patience=train_hyperparameters_config['patience'], 
    restore_best_weights=True)
ht=model_train(model, x_train, y_train, x_test, y_test, train_hyperparameters_config)
תפוקה
learning_curves(ht)
התאמת יתר ב-ConvNets
print('Testing ..................')
test_loss, test_acc = model.evaluate(x_test,  y_test, verbose=2)
print('test_loss : ', test_loss, 'test_accuracy : ', test_acc)

מהתפוקה ועקומת הלמידה שלעיל אנו יכולים להסיק שהדגם הצליח מאוד ונמנע מהתאמה יתר. דיוק האימון ודיוק האימות קרובים מאוד. בתרחיש זה לא נזדקק לשיטות נוספות להפחתת התאמת יתר. עם זאת, נחקור את הסדרת L1/L2. 

שלב 12: שימוש ב-L1/L2 Regularization

from tensorflow.keras import regularizers

model = models.Sequential()

model.add(layers.Conv2D(32, (3, 3), activation='relu', padding='same', input_shape=(32, 32, 3)))
model.add(layers.BatchNormalization())
model.add(layers.Conv2D(32, (3, 3), activation='relu', padding='same', kernel_regularizer=regularizers.l1(0.0005)))
model.add(layers.BatchNormalization())
model.add(layers.MaxPool2D((2, 2)))
model.add(layers.Dropout(0.2))

model.add(layers.Conv2D(64, (3, 3), activation='relu', padding='same'))
model.add(layers.BatchNormalization())
model.add(layers.Conv2D(64, (3, 3), activation='relu', padding='same', kernel_regularizer=regularizers.l2(0.0005)))
model.add(layers.BatchNormalization())
model.add(layers.MaxPool2D((2, 2)))
model.add(layers.Dropout(0.3))

model.add(layers.Conv2D(128, (3, 3), activation='relu', padding='same'))
model.add(layers.BatchNormalization())
model.add(layers.Conv2D(128, (3, 3), activation='relu', padding='same'))
model.add(layers.BatchNormalization())
model.add(layers.MaxPool2D((2, 2)))
model.add(layers.Dropout(0.4))

model.add(layers.Flatten())
model.add(layers.Dense(128, activation='relu', kernel_regularizer=regularizers.l1_l2(0.0005, 0.0005)))
model.add(layers.BatchNormalization())
model.add(layers.Dropout(0.5))
model.add(layers.Dense(10, activation='softmax'))

model.summary()
# Here we have used more epochs than needed since we use patience parameter which we stop the model from overfitting
train_hyperparameters_config = {
    'optim': keras.optimizers.Adam(learning_rate=0.001),
    'patience': 7,
    'epochs': 70,
    'batch_size': 32, 
}
print('Setting the callback and early stopping configurations...')
callback = tf.keras.callbacks.EarlyStopping(
    monitor='val_loss', 
    min_delta=0.001, # minimium amount of change to count as an improvement
    patience=train_hyperparameters_config['patience'], 
    restore_best_weights=True)
ht=model_train(model, x_train, y_train, x_test, y_test, train_hyperparameters_config)
learning_curves(ht)
התאמת יתר ב-ConvNets
print('Testing ..................')
test_loss, test_acc = model.evaluate(x_test,  y_test, verbose=2)
print('test_loss : ', test_loss, 'test_accuracy : ', test_acc)

כעת אנו יכולים לראות שהסדרת L1/L2 גם לאחר שימוש בציון ענישה נמוך של 0.0001, הפכה את הדגם שלנו לחוסר כושר ב-4%. לפיכך מומלץ להשתמש בזהירות בכל השיטות יחד. מכיוון שנורמליזציה ורגוליזציה של אצווה משפיעים על המודל באופן דומה, לא נצטרך להסדיר L1/L2. 

שלב 13: הגדלת נתונים

אנו נשתמש ב- ImageDataGenerator מ- tensorflow keras.

# creates a data generator object that transforms images
datagen = ImageDataGenerator(
rotation_range=40,
width_shift_range=0.2,
height_shift_range=0.2,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True,
fill_mode='nearest')

# pick an image to transform
test_img = x_train[20]
img = image.img_to_array(test_img)  # convert image to numpy arry
img = img.reshape((1,) + img.shape)  # reshape image

i = 0

for batch in datagen.flow(img, save_prefix='test', save_format='jpeg'):  # this loops runs forever until we break, saving images to current directory with specified prefix
    plt.figure(i)
    plot = plt.imshow(image.img_to_array(batch[0]))
    i += 1
    if i > 4:  # show 4 images
        break

plt.show()
התאמת יתר ב-ConvNets

אלו הן ארבע תמונות מוגדלות ותמונה מקורית אחת.

# Create an instance of the ImageDataGenerator
datagen = ImageDataGenerator(
    rotation_range=40,
    width_shift_range=0.2,
    height_shift_range=0.2,
    shear_range=0.2,
    zoom_range=0.2,
    horizontal_flip=True,
    fill_mode='nearest'
)

# Create an iterator for the data generator
data_generator = datagen.flow(x_train, y_train, batch_size=32)

# Create empty lists to store the augmented images and labels
augmented_images = []
augmented_labels = []

# Loop over the data generator and append the augmented data to the lists
num_batches = len(x_train) // 32
progress_bar = tqdm(total=num_batches, desc="Augmenting data", unit="batch")

for i in range(num_batches):
    batch_images, batch_labels = next(data_generator)
    augmented_images.append(batch_images)
    augmented_labels.append(batch_labels)
    progress_bar.update(1)

progress_bar.close()

# Convert the lists to NumPy arrays
augmented_images = np.concatenate(augmented_images, axis=0)
augmented_labels = np.concatenate(augmented_labels, axis=0)

# Combine the original and augmented data
x_train_augmented = np.concatenate((x_train, augmented_images), axis=0)
y_train_augmented = np.concatenate((y_train, augmented_labels), axis=0)
תפוקה

השתמשנו בספריית tqdm כדי לדעת את התקדמות ההגדלה שלנו.

x_train_augmented.shape, y_train_augmented.shape
תְפוּקָה

זהו מערך הנתונים שלנו לאחר הגדלה. כעת נשתמש במערך הנתונים הזה ונאמן את המודל שלנו.

model = models.Sequential()

model.add(layers.Conv2D(32, (3, 3), activation='relu', padding='same', input_shape=(32, 32, 3)))
model.add(layers.BatchNormalization())
model.add(layers.Conv2D(32, (3, 3), activation='relu', padding='same'))
model.add(layers.BatchNormalization())
model.add(layers.MaxPool2D((2, 2)))
model.add(layers.Dropout(0.2))

model.add(layers.Conv2D(64, (3, 3), activation='relu', padding='same'))
model.add(layers.BatchNormalization())
model.add(layers.Conv2D(64, (3, 3), activation='relu', padding='same'))
model.add(layers.BatchNormalization())
model.add(layers.MaxPool2D((2, 2)))
model.add(layers.Dropout(0.3))

model.add(layers.Conv2D(128, (3, 3), activation='relu', padding='same'))
model.add(layers.BatchNormalization())
model.add(layers.Conv2D(128, (3, 3), activation='relu', padding='same'))
model.add(layers.BatchNormalization())
model.add(layers.MaxPool2D((2, 2)))
model.add(layers.Dropout(0.4))

model.add(layers.Flatten())
model.add(layers.Dense(128, activation='relu'))
model.add(layers.BatchNormalization())
model.add(layers.Dropout(0.5))

model.add(layers.Dense(10, activation='softmax'))

model.summary()

# Here we have used more epochs than needed since we use patience parameter which we stop the model from overfitting
train_hyperparameters_config = {
    'optim': keras.optimizers.Adam(learning_rate=0.001),
    'patience': 10,
    'epochs': 70,
    'batch_size': 32, 
}
print('Setting the callback and early stopping configurations...')
callback = tf.keras.callbacks.EarlyStopping(
    monitor='val_loss', 
    min_delta=0.001, # minimium amount of change to count as an improvement
    patience=train_hyperparameters_config['patience'], 
    restore_best_weights=True)

ht=model_train(model, x_train_augmented, y_train_augmented, x_test, y_test, train_hyperparameters_config)


learning_curves(ht)
התאמת יתר ב-ConvNets
print('Testing ..................')
test_loss, test_acc = model.evaluate(x_test,  y_test, verbose=2)
print('test_loss : ', test_loss, 'test_accuracy : ', test_acc)

אנו יכולים לראות שהמודל יותר כללי וירידה בהפסד. יש לנו גם דיוק אימות טוב יותר. מכאן שהגדלת נתונים הגדילה את דיוק המודל שלנו. 

סיכום

התאמת יתר היא בעיה שכיחה בלמידה עמוקה, במיוחד עם ארכיטקטורות רשתות עצביות מורכבות כמו ConvNets. מתרגלים יכולים למנוע התאמת יתר ב-ConvNets על ידי הבנת הסיבות השורשיות שלה וזיהוי תרחישים שבהם היא מתרחשת. טכניקות כמו עצירה מוקדמת, נשירה, נורמליזציה של אצווה, רגוליזציה והגדלת נתונים יכולות לסייע בהפחתת בעיה זו. יישום טכניקות אלו על מערך הנתונים של CIFAR-10 הראה שיפורים משמעותיים בהכללת המודל ובביצועים. שליטה בטכניקות אלו והבנת העקרונות שלהן יכולים להוביל למודלים חזקים ואמינים של רשתות עצבים.

שאלות נפוצות

שאלה 1. מהי התאמה יתר, ומדוע היא בעיה בלמידה עמוקה? 

א. התאמת יתר מתרחשת כאשר מודל לומד את נתוני האימון טוב מדי, כולל הרעש והדפוסים הלא רלוונטיים שלו, וכתוצאה מכך ביצועים גרועים בנתונים חדשים, בלתי נראים. זו בעיה מכיוון שמודלים מאובזרים מדי אינם מצליחים להכליל ביעילות, מה שמגביל את התועלת המעשית שלהם.

שאלה 2. כיצד אוכל לזהות התאמת יתר במודל הרשת העצבית שלי?

ת. אתה יכול לזהות התאמת יתר ב-ConvNets על ידי פירוש עקומות הלמידה, אשר מתווים את מדדי האימון והאימות (למשל, אובדן, דיוק) על פני תקופות. אם מדדי האימות מפסיקים להשתפר או מתחילים להשפיל בעוד מדדי האימון ממשיכים להשתפר, זה סימן להתאמת יתר.

שאלה 3. מהי עצירה מוקדמת וכיצד היא מסייעת במניעת התאמת יתר?

א. עצירה מוקדמת היא טכניקה המנטרת את ביצועי המודל על סט אימות במהלך האימון ועוצרת את תהליך האימון כאשר הביצועים על ערכת האימות מתחילים להתדרדר, דבר המעיד על התאמה יתר. זה עוזר למנוע מהדגם להתאים יתר על המידה על ידי הפסקת האימון בזמן הנכון.

שאלה 4. כיצד הגדלת נתונים מסייעת בהפחתת התאמת יתר? 

א. הגדלת נתונים היא תהליך של יצירת נתוני אימון חדשים סינתטיים על ידי החלת טרנספורמציות (למשל, היפוך, סיבוב, קנה מידה) על הנתונים הקיימים. זה עוזר למודל להכליל טוב יותר על ידי חשיפתו לדוגמאות מגוונות יותר, ומפחית את הסיכון להתאמת יתר ב-ConvNets לנתוני ההדרכה המוגבלים.

ספוט_ימג

המודיעין האחרון

ספוט_ימג