תחי ישראל - אין לנו ארץ אחרת

תחי ישראל -אין לנו ארץ אחרת

תהליך אופטימיזציה במסגרת למידה בצוותא ensemble learning

מחבר:
בתאריך:

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

אופטימיזציה בשביל ensemble learning למידה בצוותא

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

w = 0.33
preds = w * y_pred_model_0 + w * y_pred_model_1 + w * y_pred_model_2

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

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

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

להורדת הקוד אותו נפתח במדריך

נשתמש באלגוריתמים לאופטימיזציה לצורך מציאת הרכב מודלים ensemble אופטימלי שמטרתו איתור SMS ספאמי בעזרת למידת מכונה. במדריכים קודמים בסדרת המדריכים על למידת מכונה ראינו כיצד להפיק 4 סוגים של מודלים את התוצאות שלהם נשלב על פי ההרכב עליו ימליצו האלגוריתמים שמיד נפעיל. 4 המודלים שתפקידם סיווג סוגי הודעות SMS מבוססים על הטכנולוגיות: bag of words, RNN, word embeddings ו-transformer.

ישנם שיטות רבות של אופטימיזציה. במדריך זה נשתמש ב-3 שיטות:

  • Bayesian optimization אופטימיזציה מבוססת בייס
  • Nelder-Mead algorithm: אלגוריתם Nelder-Mead
  • Dual annealingoptimization חישול כפול

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

 

Bayesian optimization אופטימיזציה מבוססת בייס

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

נתקין את הספרייה שתשתמש אותנו בשביל Bayesian optimization:

!pip install bayesian-optimization

אחרי ההתקנה. נייבא את הספרייה:

from bayes_opt import BayesianOptimization

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

# Bounded region of parameter space
pbounds = {
   'w0': (0.01, 0.99),
   'w1': (0.01, 0.99),
   'w2': (0.01, 0.99),
   'w3': (0.01, 0.99),
}

פונקציית המטרה מחזירה את מידת הדיוק accuracy על סמך המשקלים של המודלים שהיא משקללת:

def objective(w0, w1, w2, w3):
   weighted_avg_pred_logits = [w0*x + w1*y + w2*z + w3*t for x, y, z, t in zip(y_pred_logits_0, y_pred_logits_1, y_pred_logits_2, y_pred_logits_3)]
   weighted_avg_pred = np.argmax(weighted_avg_pred_logits, axis=1)
   return accuracy_score(y_actual, weighted_avg_pred)

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

optimizer = BayesianOptimization(
   f=objective,
   pbounds=pbounds,
   verbose=2,
   random_state=42
)
optimizer.maximize(init_points=10, n_iter=100)

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

print(optimizer.max)

התוצאה:

{'target': 0.9927272727272727, 'params': {'w0': 0.37704931647041523, 'w1': 0.9417000202817178, 'w2': 0.727354062975177, 'w3': 0.5966853145130959}}
  • דיוק של 99.27% עבור משקל המודלים שהתהליך מצא (התוצאות שלך עשויות להיות שונות בגלל הדינמיקה הסטוכסטית של התהליך).

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

# weights
w0 = 0.37704931647041523
w1 = 0.9417000202817178
w2 = 0.727354062975177
w3 = 0.5966853145130959
 
weighted_avg_pred_logits = [w0*x + w1*y + w2*z + w3*t for x, y, z, t in zip(y_pred_logits_0, y_pred_logits_1, y_pred_logits_2, y_pred_logits_3)]
 
weighted_avg_pred = np.argmax(weighted_avg_pred_logits, axis=1)
 
print(accuracy_score(y_actual, weighted_avg_pred))
 
print(confusion_matrix(y_actual, weighted_avg_pred))

התוצאה:

0.9927272727272727
[[477   0]
 [  4  69]]

 

Nelder-Mead optimization

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

result = minimize(objective, pt, method='nelder-mead')

האלגוריתם דורש נקודת התחלה לחיפוש (הפרמטר pt). במקרה שלנו דרושות 4 נקודות כנגד ארבעה משקלים. נגדיר, בהתאם, 4 ערכים אקראיים בטווח שבין 0 ל-1 ונאחסן אותם במערך ששמו גם כן pt:

# define the starting point (between 0 and 1)
pt = rand(4)

פונקציית המטרה זהה כמעט בכל לזו בה השתמשנו בסעיף הקודם:

def objective(x):
   w0, w1, w2, w3 = x
   weighted_avg_pred_logits = [w0*x + w1*y + w2*z + w3*t for x, y, z, t in zip(y_pred_logits_0, y_pred_logits_1, y_pred_logits_2, y_pred_logits_3)]
   weighted_avg_pred = np.argmax(weighted_avg_pred_logits, axis=1)
   return 1 - accuracy_score(y_actual, weighted_avg_pred)

שני דברים אליהם צריך לשים לב:

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

נריץ את הפונקציה:

from scipy.optimize import minimize
from numpy.random import rand
 
def objective(x):
   w0, w1, w2, w3 = x
   weighted_avg_pred_logits = [w0*x + w1*y + w2*z + w3*t for x, y, z, t in zip(y_pred_logits_0, y_pred_logits_1, y_pred_logits_2, y_pred_logits_3)]
   weighted_avg_pred = np.argmax(weighted_avg_pred_logits, axis=1)
   return 1 - accuracy_score(y_actual, weighted_avg_pred)
 
# define the starting point (between 0 and 1)
pt = rand(4)
# perform the search
result = minimize(objective, pt, method='nelder-mead')
print(result)
# summarize the result
print('Status : %s' % result['message'])
print('Total Evaluations: %d' % result['nfev'])
# evaluate solution
solution = result['x']
evaluation = objective(solution)
print('Solution: f(%s) = %.5f' % (solution, evaluation))

התוצאה:

final_simplex: (array([[0.60413815, 0.27673483, 0.34882504, 0.53369009],
       [0.60419715, 0.27673483, 0.34882504, 0.53369009],
       [0.60413815, 0.27676186, 0.34882504, 0.53369009],
       [0.60413815, 0.27673483, 0.34885911, 0.53369009],
       [0.60413815, 0.27673483, 0.34882504, 0.53374221]]), array([0.00727273, 0.00727273, 0.00727273, 0.00727273, 0.00727273]))
           fun: 0.0072727272727273196
       message: 'Optimization terminated successfully.'
          nfev: 59
           nit: 10
        status: 0
       success: True
             x: array([0.60413815, 0.27673483, 0.34882504, 0.53369009])
Status : Optimization terminated successfully.
Total Evaluations: 59
Solution: f([0.60413815 0.27673483 0.34882504 0.53369009]) = 0.00727

ננתח את פלט התוצאה:

  • success - אומר אם החיפוש הצליח
  • message - הודעה אודות הצלחת או כישלון החיפוש
  • nfev - מספר ההערכות של פונקצית המטרה שבוצעו במהלך החיפוש
  • solution - מערך התוצאות (המשקלים) שהביאו לערך המינימלי ומה הערך

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

# weights
w0 = result['x'][0]
w1 = result['x'][1]
w2 = result['x'][2]
w3 = result['x'][3]
 
 
weighted_avg_pred_logits = [w0*x + w1*y + w2*z + w3*t for x, y, z, t in zip(y_pred_logits_0, y_pred_logits_1, y_pred_logits_2, y_pred_logits_3)]
 
weighted_avg_pred = np.argmax(weighted_avg_pred_logits, axis=1)
 
print(accuracy_score(y_actual, weighted_avg_pred))
 
print(confusion_matrix(y_actual, weighted_avg_pred))

התוצאה:

0.9927272727272727
[[477   0]
 [  4  69]]

 

אופטימיזציה בשיטת Dual annealing

Dual annealing היא שיטת אופטימיזציה המשמשת למציאת נקודות קיצון של פונקציות שקשה לאתר בהם נקודות קיצון גלובליות. לדוגמה: פונקציות שאינן לינאריות או שיש להם ריבוי של נקודות קיצון מקומיות. הגישה מכונה כפולה Dual בגלל שהיא מבוססת על שני אלגוריתמים: הראשון הוא simulated annealing - אלגוריתם טיפוס גבעות hill climbing מה שאומר שהוא מתחיל בנקודה רנדומלית ועורך שינויים קטנים בכיוון מסוים. אם השינוי גורם לשיפור התוצאה האלגוריתם ממשיך בכיוון זה. הבעיה עם האלגוריתם שהוא חותר לשיפור מתמיד ואז יש סכנה שהוא יתקע בנקודת קיצון מקומית. פתרון אחד הוא simulated annealing המתיר החלפת הפתרון באופן אקראי עם החלפות רבות יותר בתחילת התהליך והפחתה איטית ככל שהתהליך מתקדם. השליטה בהפחתה נעשית באמצעות פרמטר טמפרטורה שמתחיל גבוה ומסיים נמוך. אלגוריתם שני הוא חיפוש מקומי local search המופעל על התוצאות של ה-simulated annealing בגלל שהוא עדיפותו באיתור נקודות קיצון מקומיות. התהליך בו מופעלים האלגוריתמים simulated annealing ואחר כך local search אחד אחרי השני מאפשר קבלת תוצאות טובות יותר מכל אלגוריתם בנפרד.

הממשק בו נשתמש הוא של ספריית SciPy המצויידת במתודה dual_annealing() הפרמטרים אותם נעביר למתודה הם: פונקצית המטרה ו-bounds - טווח קביל של פרמטרים:

# perform the dual annealing search
result = dual_annealing(objective, bounds)

נפעיל את המתודה על פונקצית המטרה:

from scipy.optimize import dual_annealing
from numpy.random import rand
 
def objective(x):
   w0, w1, w2, w3 = x
   weighted_avg_pred_logits = [w0*x + w1*y + w2*z + w3*t for x, y, z, t in zip(y_pred_logits_0, y_pred_logits_1, y_pred_logits_2, y_pred_logits_3)]
   weighted_avg_pred = np.argmax(weighted_avg_pred_logits, axis=1)
   return 1 - accuracy_score(y_actual, weighted_avg_pred)
 
# define the starting point (between 0 and 1)
# define range for input
r_min, r_max = 0, 1
# define the bounds on the search
bounds = np.array([[r_min, r_max], [r_min, r_max], [r_min, r_max], [r_min, r_max]])
# perform the dual annealing search
result = dual_annealing(objective, bounds)
print(result)
# summarize the result
print('Status : %s' % result['message'])
print('Total Evaluations: %d' % result['nfev'])
# evaluate solution
solution = result['x']
evaluation = objective(solution)
print('Solution: f(%s) = %.5f' % (solution, evaluation))

התוצאה:

fun: 0.0072727272727273196
 message: ['Maximum number of iteration reached']
    nfev: 8011
    nhev: 0
     nit: 1000
    njev: 2
  status: 0
 success: True
x: array([0.37322158, 0.82959575, 0.4800364 , 0.63658886])
Status : ['Maximum number of iteration reached']
Total Evaluations: 8011
Solution: f([0.37322158 0.82959575 0.4800364  0.63658886]) = 0.00727
  • הערך של success מעיד על הצלחת הניסוי
  • ה-message הוא 'Maximum number of iteration reached' מראה על סיום החיפוש בגלל שעבר את המגבלה שמוקצה לו על ידי הספרייה. ניתן לשנות את ערך זה על ידי שינוי הפרמטר maxiter שערכו הדיפולטי 1000)

הפונקציה מחפשת מינימום גלובלי והתוצאה fun היא הנמוכה ביותר שמצא התהליך 0.00727 כיוון שאותנו מעניין ערך המקסימום בגלל שאנחנו מעוניינים ב-accuracy נפחית ערך זה מ-1:

1 - 0.00727 = 0.99273

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

array([0.37322158, 0.82959575, 0.4800364 , 0.63658886])

נציב את ערכי המשקולות שמצא התהליך בפונקציית המטרה ונעריך את התוצאה:

# weights
w0 = result['x'][0]
w1 = result['x'][1]
w2 = result['x'][2]
w3 = result['x'][3]
 
def objective(w0, w1, w2, w3):
   weighted_avg_pred_logits = [w0*x + w1*y + w2*z + w3*t for x, y, z, t in zip(y_pred_logits_0, y_pred_logits_1, y_pred_logits_2, y_pred_logits_3)]
   weighted_avg_pred = np.argmax(weighted_avg_pred_logits, axis=1)
   return accuracy_score(y_actual, weighted_avg_pred)
 
objective(w0, w1, w2, w3)
 
 
weighted_avg_pred_logits = [w0*x + w1*y + w2*z + w3*t for x, y, z, t in zip(y_pred_logits_0, y_pred_logits_1, y_pred_logits_2, y_pred_logits_3)]
 
weighted_avg_pred = np.argmax(weighted_avg_pred_logits, axis=1)
 
print(accuracy_score(y_actual, weighted_avg_pred))
 
print(confusion_matrix(y_actual, weighted_avg_pred))

התוצאה:

0.9927272727272727
[[477   0]
 [  4  69]]

 

להורדת הקוד אותו פיתחנו במדריך

 

אולי גם זה יעניין אותך

ensemble learning לשיפור ביצועי מודלים של למידת מכונה

איתור SMS ספאמי באמצעות טכנולוגית Transformer

זיהוי SMS ספאמי בעזרת RNN ומרחב הטמעה שאומן מראש

 

לכל המדריכים בנושא של למידת מכונה

 

אהבתם? לא אהבתם? דרגו!

0 הצבעות, ממוצע 0 מתוך 5 כוכבים

 

 

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

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

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

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

השימוש שלך באתר מהווה ראייה להסכמתך עם הכללים והתקנות שנוסחו בהסכם תנאי השימוש.

הוסף תגובה חדשה

 

 

ענה על השאלה הפשוטה הבאה כתנאי להוספת תגובה:

איך קוראים בעברית לצ`ופצ`יק של הקומקום?