מבוא
נניח שאתה רוצה שמודל Keras שלך יקבל התנהגות מסוימת במהלך אימון, הערכה או חיזוי. לדוגמה, ייתכן שתרצה לשמור את הדגם שלך בכל עידן אימון. אחת הדרכים לעשות זאת היא באמצעות Callbacks.
באופן כללי, Callbacks הן פונקציות שנקראות כאשר מתרחש אירוע כלשהו, ומועברות כארגומנטים לפונקציות אחרות. במקרה של Keras, הם מהווים כלי להתאמה אישית של ההתנהגות של המודל שלך - בין אם זה במהלך אימון, הערכה או מסקנות. יישומים מסוימים הם רישום, התמדה במודל, עצירה מוקדמת או שינוי קצב הלמידה. זה נעשה על ידי העברת רשימה של Callbacks כארגומנטים עבור keras.Model.fit()
,keras.Model.evaluate()
or keras.Model.predict()
.
כמה מקרי שימוש נפוצים להתקשרות חוזרת הם שינוי קצב הלמידה, רישום, ניטור והפסקה מוקדמת של ההדרכה. ל-Keras יש מספר התקשרויות מובנות, מפורטות
בתיעוד.
עם זאת, כמה יישומים ספציפיים יותר עשויים לדרוש התקשרות חוזרת מותאמת אישית. לדוגמה, יישום חימום קצב למידה עם קוסינוס דיקיי לאחר תקופת החזקה אינו מובנה כרגע, אך נמצא בשימוש נרחב ומאומץ כמתזמן.
מחלקת התקשרות חוזרת ושיטותיה
ל-Keras יש מחלקת התקשרות חוזרת ספציפית, keras.callbacks.Callback
, עם שיטות שניתן לקרוא להן במהלך אימון, בדיקה והסקת מסקנות ברמה גלובלית, אצווה או עידן. כדי ליצור התקשרויות חוזרות מותאמות אישית, עלינו ליצור תת מחלקה ולעקוף את השיטות הללו.
השמיים keras.callbacks.Callback
לכיתה יש שלושה סוגים של שיטות:
- שיטות גלובליות: נקראות בהתחלה או בסוף
fit()
,evaluate()
וpredict()
. - שיטות ברמת אצווה: נקראות בתחילת או בסוף עיבוד אצווה.
- שיטות ברמת עידן: נקראות בתחילת או בסוף קבוצת אימון.
הערה: לכל שיטה יש גישה ל-dict שנקרא logs
. המפתחות והערכים של logs
הם הקשריים - הם תלויים באירוע שקורא לשיטה. יתר על כן, יש לנו גישה למודל בתוך כל שיטה דרך ה self.model
תכונה.
בואו נסתכל על שלוש דוגמאות להתקשרות חוזרת מותאמות אישית - אחת לאימון, אחת להערכה ואחת לחיזוי. כל אחד ידפיס בכל שלב מה המודל שלנו עושה ולאילו יומנים יש לנו גישה. זה מועיל להבנת מה אפשר לעשות עם התקשרויות חוזרות מותאמות אישית בכל שלב.
נתחיל בהגדרת דגם צעצוע:
import tensorflow as tf
from tensorflow import keras
import numpy as np
model = keras.Sequential()
model.add(keras.layers.Dense(10, input_dim = 1, activation='relu'))
model.add(keras.layers.Dense(10, activation='relu'))
model.add(keras.layers.Dense(1))
model.compile(
optimizer=keras.optimizers.RMSprop(learning_rate=0.1),
loss = "mean_squared_error",
metrics = ["mean_absolute_error"]
)
x = np.random.uniform(low = 0, high = 10, size = 1000)
y = x**2
x_train, x_test = (x[:900],x[900:])
y_train, y_test = (y[:900],y[900:])
הדרכה מותאמת אישית
ההתקשרות הראשונה שלנו היא להתקשר במהלך האימון. בוא נעשה תת-סיווג של Callback
מעמד:
class TrainingCallback(keras.callbacks.Callback):
def __init__(self):
self.tabulation = {"train":"", 'batch': " "*8, 'epoch':" "*4}
def on_train_begin(self, logs=None):
tab = self.tabulation['train']
print(f"{tab}Training!")
print(f"{tab}available logs: {logs}")
def on_train_batch_begin(self, batch, logs=None):
tab = self.tabulation['batch']
print(f"{tab}Batch {batch}")
print(f"{tab}available logs: {logs}")
def on_train_batch_end(self, batch, logs=None):
tab = self.tabulation['batch']
print(f"{tab}End of Batch {batch}")
print(f"{tab}available logs: {logs}")
def on_epoch_begin(self, epoch, logs=None):
tab = self.tabulation['epoch']
print(f"{tab}Epoch {epoch} of training")
print(f"{tab}available logs: {logs}")
def on_epoch_end(self, epoch, logs=None):
tab = self.tabulation['epoch']
print(f"{tab}End of Epoch {epoch} of training")
print(f"{tab}available logs: {logs}")
def on_train_end(self, logs=None):
tab = self.tabulation['train']
print(f"{tab}Finishing training!")
print(f"{tab}available logs: {logs}")
אם כל אחת מהשיטות הללו לא תוחלף - התנהגות ברירת המחדל תימשך כפי שהייתה בעבר. בדוגמה שלנו - אנו פשוט מדפיסים את היומנים הזמינים ואת הרמה שבה ה-callback מוחל, עם הזחה מתאימה.
בואו נסתכל על הפלטים:
model.fit(
x_train,
y_train,
batch_size=500,
epochs=2,
verbose=0,
callbacks=[TrainingCallback()],
)
Training!
available logs: {}
Epoch 0 of training
available logs: {}
Batch 0
available logs: {}
End of Batch 0
available logs: {'loss': 2172.373291015625, 'mean_absolute_error': 34.79669952392578}
Batch 1
available logs: {}
End of Batch 1
available logs: {'loss': 2030.1309814453125, 'mean_absolute_error': 33.30256271362305}
End of Epoch 0 of training
available logs: {'loss': 2030.1309814453125, 'mean_absolute_error': 33.30256271362305}
Epoch 1 of training
available logs: {}
Batch 0
available logs: {}
End of Batch 0
available logs: {'loss': 1746.2772216796875, 'mean_absolute_error': 30.268001556396484}
Batch 1
available logs: {}
End of Batch 1
available logs: {'loss': 1467.36376953125, 'mean_absolute_error': 27.10252571105957}
End of Epoch 1 of training
available logs: {'loss': 1467.36376953125, 'mean_absolute_error': 27.10252571105957}
Finishing training!
available logs: {'loss': 1467.36376953125, 'mean_absolute_error': 27.10252571105957}
שימו לב שאנחנו יכולים לעקוב בכל שלב מה המודל עושה, ולאילו מדדים יש לנו גישה. בסוף כל אצווה ותקופה, יש לנו גישה לפונקציית האובדן של המדגם ולמדדים של המודל שלנו.
התקשרות חוזרת להערכה מותאמת אישית
עכשיו, בואו נתקשר ל- Model.evaluate()
שיטה. אנו יכולים לראות שבסוף אצווה יש לנו גישה לפונקציית ההפסד והמדדים באותו זמן, ובסוף ההערכה יש לנו גישה להפסד ולמדדים הכוללים:
class TestingCallback(keras.callbacks.Callback):
def __init__(self):
self.tabulation = {"test":"", 'batch': " "*8}
def on_test_begin(self, logs=None):
tab = self.tabulation['test']
print(f'{tab}Evaluating!')
print(f'{tab}available logs: {logs}')
def on_test_end(self, logs=None):
tab = self.tabulation['test']
print(f'{tab}Finishing evaluation!')
print(f'{tab}available logs: {logs}')
def on_test_batch_begin(self, batch, logs=None):
tab = self.tabulation['batch']
print(f"{tab}Batch {batch}")
print(f"{tab}available logs: {logs}")
def on_test_batch_end(self, batch, logs=None):
tab = self.tabulation['batch']
print(f"{tab}End of batch {batch}")
print(f"{tab}available logs: {logs}")
res = model.evaluate(
x_test, y_test, batch_size=100, verbose=0, callbacks=[TestingCallback()]
)
Evaluating!
available logs: {}
Batch 0
available logs: {}
End of batch 0
available logs: {'loss': 382.2723083496094, 'mean_absolute_error': 14.069927215576172}
Finishing evaluation!
available logs: {'loss': 382.2723083496094, 'mean_absolute_error': 14.069927215576172}
חיזוי מותאם אישית התקשרות חוזרת
לבסוף, בואו נקרא ל Model.predict()
שיטה. שימו לב שבסוף כל אצווה יש לנו גישה לתפוקות החזויות של המודל שלנו:
class PredictionCallback(keras.callbacks.Callback):
def __init__(self):
self.tabulation = {"prediction":"", 'batch': " "*8}
def on_predict_begin(self, logs=None):
tab = self.tabulation['prediction']
print(f"{tab}Predicting!")
print(f"{tab}available logs: {logs}")
def on_predict_end(self, logs=None):
tab = self.tabulation['prediction']
print(f"{tab}End of Prediction!")
print(f"{tab}available logs: {logs}")
def on_predict_batch_begin(self, batch, logs=None):
tab = self.tabulation['batch']
print(f"{tab}batch {batch}")
print(f"{tab}available logs: {logs}")
def on_predict_batch_end(self, batch, logs=None):
tab = self.tabulation['batch']
print(f"{tab}End of batch {batch}")
print(f"{tab}available logs:n {logs}")
res = model.predict(x_test[:10],
verbose = 0,
callbacks=[PredictionCallback()])
עיין במדריך המעשי והמעשי שלנו ללימוד Git, עם שיטות עבודה מומלצות, סטנדרטים מקובלים בתעשייה ודף רמאות כלול. תפסיק לגוגל פקודות Git ולמעשה ללמוד זה!
Predicting!
available logs: {}
batch 0
available logs: {}
End of batch 0
available logs:
{'outputs': array([[ 7.743822],
[27.748264],
[33.082104],
[26.530678],
[27.939169],
[18.414223],
[42.610645],
[36.69335 ],
[13.096557],
[37.120853]], dtype=float32)}
End of Prediction!
available logs: {}
בעזרת אלה - אתה יכול להתאים אישית את ההתנהגות, להגדיר ניטור או לשנות בדרך אחרת את תהליכי ההדרכה, ההערכה או ההסקה. חלופה ל-sublcassing היא להשתמש ב- LambdaCallback
.
שימוש ב- LambaCallback
אחת מההתקשרויות המובנות ב-Keras היא LambdaCallback
מעמד. התקשרות חוזרת זו מקבלת פונקציה המגדירה כיצד היא מתנהגת ומה היא עושה! במובן מסוים, הוא מאפשר לך להשתמש בכל פונקציה שרירותית כהתקשרות חוזרת, ובכך מאפשר לך ליצור התקשרויות מותאמות אישית.
למחלקה יש את הפרמטרים האופציונליים:
-on_epoch_begin
on_epoch_end
on_batch_begin
on_batch_end
on_train_begin
on_train_end
כל פרמטר מקבל תפקוד אשר נקרא באירוע הדגם המתאים. כדוגמה, בואו נבצע התקשרות חוזרת כדי לשלוח דוא"ל כשהדוגמנית תסיים את ההכשרה:
import smtplib
from email.message import EmailMessage
def send_email(logs):
msg = EmailMessage()
content = f"""The model has finished training."""
for key, value in logs.items():
content = content + f"n{key}:{value:.2f}"
msg.set_content(content)
msg['Subject'] = f'Training report'
msg['From'] = '[email protected]'
msg['To'] = 'receiver-email'
s = smtplib.SMTP('smtp.gmail.com', 587)
s.starttls()
s.login("[email protected]", "your-gmail-app-password")
s.send_message(msg)
s.quit()
lambda_send_email = lambda logs : send_email(logs)
email_callback = keras.callbacks.LambdaCallback(on_train_end = lambda_send_email)
model.fit(
x_train,
y_train,
batch_size=100,
epochs=1,
verbose=0,
callbacks=[email_callback],
)
כדי לבצע התקשרות חוזרת מותאמת אישית באמצעות LambdaCallback
, אנחנו רק צריכים ליישם את הפונקציה שאנחנו רוצים שיקראו לה, לעטוף אותה בתור a lambda
לתפקד ולהעביר אותו ל-LambdaCallback
מחלקה כפרמטר.
התקשרות לאחור להדמיית אימון מודל
בחלק זה, ניתן דוגמה להתקשרות חוזרת מותאמת אישית שעושה אנימציה של שיפור הביצועים של המודל שלנו במהלך האימון. על מנת לעשות זאת, אנו מאחסנים את ערכי היומנים בסוף כל אצווה. לאחר מכן, בסוף לולאת האימון, אנו יוצרים אנימציה באמצעות matplotlib
.
על מנת לשפר את ההדמיה, ההפסד והמדדים ישורטו בסולם יומן:
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.animation import FuncAnimation
from IPython import display
class TrainingAnimationCallback(keras.callbacks.Callback):
def __init__(self, duration = 40, fps = 1000/25):
self.duration = duration
self.fps = fps
self.logs_history = []
def set_plot(self):
self.figure = plt.figure()
plt.xticks(
range(0,self.params['steps']*self.params['epochs'], self.params['steps']),
range(0,self.params['epochs']))
plt.xlabel('Epoch')
plt.ylabel('Loss & Metrics ($Log_{10}$ scale)')
self.plot = {}
for metric in self.model.metrics_names:
self.plot[metric], = plt.plot([],[], label = metric)
max_y = [max(log.values()) for log in self.logs_history]
self.title = plt.title(f'batches:0')
plt.xlim(0,len(self.logs_history))
plt.ylim(0,max(max_y))
plt.legend(loc='upper right')
def animation_function(self,frame):
batch = frame % self.params['steps']
self.title.set_text(f'batch:{batch}')
x = list(range(frame))
for metric in self.model.metrics_names:
y = [log[metric] for log in self.logs_history[:frame]]
self.plot[metric].set_data(x,y)
def on_train_batch_end(self, batch, logs=None):
logarithm_transform = lambda item: (item[0], np.log(item[1]))
logs = dict(map(logarithm_transform,logs.items()))
self.logs_history.append(logs)
def on_train_end(self, logs=None):
self.set_plot()
num_frames = int(self.duration*self.fps)
num_batches = self.params['steps']*self.params['epochs']
selected_batches = range(0, num_batches , num_batches//num_frames )
interval = 1000*(1/self.fps)
anim_created = FuncAnimation(self.figure,
self.animation_function,
frames=selected_batches,
interval=interval)
video = anim_created.to_html5_video()
html = display.HTML(video)
display.display(html)
plt.close()
נשתמש באותו מודל כמו קודם, אבל עם דוגמאות אימון נוספות:
import tensorflow as tf
from tensorflow import keras
import numpy as np
model = keras.Sequential()
model.add(keras.layers.Dense(10, input_dim = 1, activation='relu'))
model.add(keras.layers.Dense(10, activation='relu'))
model.add(keras.layers.Dense(1))
model.compile(
optimizer=keras.optimizers.RMSprop(learning_rate=0.1),
loss = "mean_squared_error",
metrics = ["mean_absolute_error"]
)
def create_sample(sample_size, train_test_proportion = 0.9):
x = np.random.uniform(low = 0, high = 10, size = sample_size)
y = x**2
train_test_split = int(sample_size*train_test_proportion)
x_train, x_test = (x[:train_test_split],x[train_test_split:])
y_train, y_test = (y[:train_test_split],y[train_test_split:])
return (x_train,x_test,y_train,y_test)
x_train,x_test,y_train,y_test = create_sample(35200)
model.fit(
x_train,
y_train,
batch_size=32,
epochs=2,
verbose=0,
callbacks=[TrainingAnimationCallback()],
)
הפלט שלנו הוא הנפשה של המדדים ופונקציית האובדן כשהם משתנים בתהליך האימון:
הדפדפן שלך לא תומך בסרטון HTML.
סיכום
במדריך זה, בדקנו את היישום של התקשרויות חוזרות מותאמות אישית ב-Keras.
ישנן שתי אפשרויות ליישום התקשרויות חוזרות מותאמות אישית - באמצעות סיווג משנה של keras.callbacks.Callback
בכיתה, או על ידי שימוש ב- keras.callbacks.LambdaCallback
מעמד.
ראינו דוגמה מעשית אחת לשימוש LambdaCallback
עבור שליחת אימייל בסוף לולאת האימון, ודוגמה אחת לסיווג המשנה של Callback
שיעור שיוצר אנימציה של לולאת האימון.
למרות של-Keras יש הרבה התקשרויות מובנות, לדעת איך ליישם התקשרות חוזרת מותאמת אישית יכולה להיות שימושית עבור יישומים ספציפיים יותר.