למידה של התחממות קצב עם דעיכת קוסינוס ב-Keras/TensorFlow PlatoBlockchain Data Intelligence. חיפוש אנכי. איי.

למידה של התחממות קצב עם דעיכת קוסינוס ב-Keras/TensorFlow

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

$$
weight_{t+1} = weight_t – lr *frac{derror}{dweight_t}
$$

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

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

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

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

חימום קצב למידה

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

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

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

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

במדריך זה, ניישם חימום קצב למידה ב-Keras/TensorFlow בתור א keras.optimizers.schedules.LearningRateSchedule תת-מעמד ו keras.callbacks.Callback התקשר חזרה. קצב הלמידה יוגדל מ 0 ל target_lr ולהחיל ריקבון קוסינוס, מכיוון שזהו לוח זמנים משני נפוץ מאוד. כרגיל, Keras מקל על הטמעת פתרונות גמישים בדרכים שונות ולשלוח אותם עם הרשת שלך.

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

קצב למידה עם התקשרויות חוזרות של Keras

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

עכשיו, tf.keras.callbacks.LearningRateScheduler() מעביר את מספר העידן לפונקציה שבה הוא משתמש כדי לחשב את קצב הלמידה, שהוא די גס. LR Warmup צריך להיעשות על כל אחד צעד (אצווה), לא תקופה, אז נצטרך לגזור א global_step (בכל התקופות) כדי לחשב את קצב הלמידה במקום, ותת-סיווג את Callback class ליצור התקשרות חוזרת מותאמת אישית במקום להעביר את הפונקציה, מכיוון שנצטרך להעביר ארגומנטים בכל קריאה, וזה בלתי אפשרי רק כאשר מעבירים את הפונקציה:

def func():
    return ...
    
keras.callbacks.LearningRateScheduler(func)

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

def lr_warmup_cosine_decay(global_step,
                           warmup_steps,
                           hold = 0,
                           total_steps=0,
                           start_lr=0.0,
                           target_lr=1e-3):
    
    learning_rate = 0.5 * target_lr * (1 + np.cos(np.pi * (global_step - warmup_steps - hold) / float(total_steps - warmup_steps - hold)))

    
    warmup_lr = target_lr * (global_step / warmup_steps)

    
    
    if hold > 0:
        learning_rate = np.where(global_step > warmup_steps + hold,
                                 learning_rate, target_lr)
    
    learning_rate = np.where(global_step < warmup_steps, warmup_lr, learning_rate)
    return learning_rate

בכל שלב, אנו מחשבים את קצב הלמידה ואת קצב הלמידה בחימום (שני המרכיבים של לוח הזמנים), ביחס ל- start_lr ו target_lr. start_lr יתחיל בדרך כלל ב 0.0, בזמן ש target_lr תלוי ברשת ובאופטימיזציה שלך - 1e-3 ייתכן שאינה ברירת מחדל טובה, אז הקפד להגדיר את היעד שלך מתחיל LR בעת קריאה לשיטה.

אם global_step בהכשרה גבוהה יותר מה warmup_steps קבענו - אנו משתמשים בלוח הזמנים של ריקבון הקוסינוס LR. אם לא, זה אומר שאנחנו עדיין מתחממים, אז נעשה שימוש בחימום LR. אם ה hold הטיעון מוגדר, נחזיק את target_lr למספר זה של צעדים לאחר החימום ולפני דעיכת הקוסינוס. np.where() מספק תחביר מצוין עבור זה:

np.where(condition, value_if_true, value_if_false)

אתה יכול לדמיין את הפונקציה באמצעות:

steps = np.arange(0, 1000, 1)
lrs = []

for step in steps:
  lrs.append(lr_warmup_cosine_decay(step, total_steps=len(steps), warmup_steps=100, hold=10))
plt.plot(lrs)

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

from keras import backend as K

class WarmupCosineDecay(keras.callbacks.Callback):
    def __init__(self, total_steps=0, warmup_steps=0, start_lr=0.0, target_lr=1e-3, hold=0):

        super(WarmupCosineDecay, self).__init__()
        self.start_lr = start_lr
        self.hold = hold
        self.total_steps = total_steps
        self.global_step = 0
        self.target_lr = target_lr
        self.warmup_steps = warmup_steps
        self.lrs = []

    def on_batch_end(self, batch, logs=None):
        self.global_step = self.global_step + 1
        lr = model.optimizer.lr.numpy()
        self.lrs.append(lr)

    def on_batch_begin(self, batch, logs=None):
        lr = lr_warmup_cosine_decay(global_step=self.global_step,
                                    total_steps=self.total_steps,
                                    warmup_steps=self.warmup_steps,
                                    start_lr=self.start_lr,
                                    target_lr=self.target_lr,
                                    hold=self.hold)
        K.set_value(self.model.optimizer.lr, lr)

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

ראשית, אנו מגדירים את הבנאי עבור המחלקה ועוקבים אחר השדות שלה. בכל אצווה שהסתיימה, נגדיל את הצעד הגלובלי, נשים לב ל-LR הנוכחי ונוסיף אותו לרשימת ה-LRs עד כה. בתחילת כל אצווה - נחשב את ה-LR באמצעות ה- lr_warmup_cosine_decay() פונקציה והגדר את ה-LR הזה כ-LR הנוכחי של המייעל. זה נעשה עם הקצה האחורי set_value().

כשזה נעשה - פשוט חשב את סך השלבים (אורך/גודל_אצווה*תקופות) וקח חלק מהמספר הזה עבורך warmup_steps:


total_steps = len(train_set)*config['EPOCHS']



warmup_steps = int(0.05*total_steps)

callback = WarmupCosineDecay(total_steps=total_steps, 
                             warmup_steps=warmup_steps,
                             hold=int(warmup_steps/2), 
                             start_lr=0.0, 
                             target_lr=1e-3)

לבסוף, בנה את המודל שלך וספק את ההתקשרות חזרה ב- fit() להתקשר:

model = keras.applications.EfficientNetV2B0(weights=None, 
                                            classes=n_classes, 
                                            input_shape=[224, 224, 3])
  
model.compile(loss="sparse_categorical_crossentropy",
                  optimizer='adam',
                  jit_compile=True,
                  metrics=['accuracy'])

בסוף האימון, אתה יכול להשיג ולהמחיש את ה-LRs שהשתנו באמצעות:

lrs = callback.lrs 
plt.plot(lrs)

למידה של התחממות קצב עם דעיכת קוסינוס ב-Keras/TensorFlow PlatoBlockchain Data Intelligence. חיפוש אנכי. איי.

אם אתה מתווה את ההיסטוריה של דגם מאומן עם ובלי חימום LR - אתה תראה הבדל מובהק ביציבות האימון:

למידה של התחממות קצב עם דעיכת קוסינוס ב-Keras/TensorFlow PlatoBlockchain Data Intelligence. חיפוש אנכי. איי.

קצב למידה עם תת כיתת LearningRateSchedule

חלופה ליצירת התקשרות חוזרת היא ליצור א LearningRateSchedule תת-מחלקה, שאינה מתמרנת את ה-LR - היא מחליפה אותו. גישה זו מאפשרת לך לדחוף קצת יותר אל הקצה האחורי של Keras/TensorFlow, אך כאשר משתמשים בה, לא ניתן לשלב אותה עם התקשרויות אחרות הקשורות ל-LR, כגון ReduceLROnPlateau(), העוסק ב-LRs כמספרי נקודה צפה.

בנוסף, השימוש בתת המחלקה ידרוש ממך להפוך אותה לניתנת לסידרה (עומס יתר get_config()) כפי שהוא הופך לחלק מהדגם, אם ברצונך לשמור את משקלי הדגם. דבר נוסף שיש לציין הוא שהכיתה תצפה לעבוד רק עם tf.Tensorס. למרבה המזל, ההבדל היחיד באופן שבו אנו עובדים יהיה שיחות tf.func() במקום np.func() מכיוון שממשקי ה-API של TensorFlow ו-NumPy דומים להפליא ותואמים.

בואו נכתוב מחדש את הנוחות lr_warmup_cosine_decay() פונקציה כדי להשתמש בפעולות TensorFlow במקום זאת:

def lr_warmup_cosine_decay(global_step,
                           warmup_steps,
                           hold = 0,
                           total_steps=0,
                           start_lr=0.0,
                           target_lr=1e-3):
    
    
    learning_rate = 0.5 * target_lr * (1 + tf.cos(tf.constant(np.pi) * (global_step - warmup_steps - hold) / float(total_steps - warmup_steps - hold)))

    
    warmup_lr = target_lr * (global_step / warmup_steps)

    
    
    if hold > 0:
        learning_rate = tf.where(global_step > warmup_steps + hold,
                                 learning_rate, target_lr)
    
    learning_rate = tf.where(global_step < warmup_steps, warmup_lr, learning_rate)
    return learning_rate

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

התחביר נקי יותר מה- Callback sublcass, בעיקר בגלל שאנו מקבלים גישה ל- step שדה, במקום לעקוב אחריו בעצמנו, אבל גם מקשה במקצת על העבודה עם מאפייני הכיתה - במיוחד, זה מקשה על חילוץ lr מ tf.Tensor() לכל סוג אחר כדי לעקוב אחריו ברשימה. ניתן לעקוף זאת מבחינה טכנית על ידי הפעלה במצב להוט, אך מהווה מטרד למעקב אחר ה-LR למטרות איתור באגים, ועדיף להימנע מכך:

class WarmUpCosineDecay(keras.optimizers.schedules.LearningRateSchedule):
    def __init__(self, start_lr, target_lr, warmup_steps, total_steps, hold):
        super().__init__()
        self.start_lr = start_lr
        self.target_lr = target_lr
        self.warmup_steps = warmup_steps
        self.total_steps = total_steps
        self.hold = hold

    def __call__(self, step):
        lr = lr_warmup_cosine_decay(global_step=step,
                                    total_steps=self.total_steps,
                                    warmup_steps=self.warmup_steps,
                                    start_lr=self.start_lr,
                                    target_lr=self.target_lr,
                                    hold=self.hold)

        return tf.where(
            step > self.total_steps, 0.0, lr, name="learning_rate"
        )

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


total_steps = len(train_set)*config['EPOCHS']



warmup_steps = int(0.05*total_steps)

schedule = WarmUpCosineDecay(start_lr=0.0, target_lr=1e-3, warmup_steps=warmup_steps, total_steps=total_steps, hold=warmup_steps)

וצינור האימון שונה רק בכך שאנו מגדירים את ה-LR של האופטימיזר ל- schedule:

model = keras.applications.EfficientNetV2B0(weights=None, 
                                            classes=n_classes, 
                                            input_shape=[224, 224, 3])
  
model.compile(loss="sparse_categorical_crossentropy",
                  optimizer=tf.keras.optimizers.Adam(learning_rate=schedule),
                  jit_compile=True,
                  metrics=['accuracy'])

history3 = model.fit(train_set,
                    epochs = config['EPOCHS'],
                    validation_data=valid_set)

אם ברצונך לשמור את הדגם, ה WarmupCosineDecay לוח הזמנים יצטרך לעקוף את get_config() שיטה:

    def get_config(self):
        config = {
          'start_lr': self.start_lr,
          'target_lr': self.target_lr,
          'warmup_steps': self.warmup_steps,
          'total_steps': self.total_steps,
          'hold': self.hold
        }
        return config

לבסוף, בעת טעינת הדגם, תצטרך לעבור א WarmupCosineDecay כאובייקט מותאם אישית:

model = keras.models.load_model('weights.h5', 
                                custom_objects={'WarmupCosineDecay', WarmupCosineDecay})

סיכום

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

יישמנו חימום לקצב למידה עם דעיכת קוסינוס, הסוג הנפוץ ביותר של הפחתת LR בשילוב עם חימום. אתה יכול ליישם כל פונקציה אחרת להפחתה, או לא להפחית את קצב הלמידה כלל - להשאיר אותו לשיחות חוזרות אחרות כגון ReduceLROnPlateau(). יישמנו את חימום קצב הלמידה כ-Keras Callback, כמו גם כ-Keras Optimizer Schedule ותכננו את קצב הלמידה לאורך התקופות.

בול זמן:

עוד מ Stackabuse