כיצד אנו עובדים על איכות ומהירות בחירת ההמלצות

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

כיצד אנו עובדים על איכות ומהירות בחירת ההמלצות

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

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

אני אגיד לך איך פתרנו את הבעיות האלה.

בחירת מועמדים

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

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

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

כיצד אנו עובדים על איכות ומהירות בחירת ההמלצות

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

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

שלב ALS בזמן ריצה

כיצד לקחת בחשבון את משוב המשתמש מיד לאחר לחיצה?

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

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

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

בואו נפרק את המטריצה ​​לשניים: P (mxd) ו-Q (dxn), כאשר d הוא הממד של הייצוג הווקטורי (בדרך כלל מספר קטן). אז כל אובייקט יתאים לוקטור d-ממדי (למשתמש - שורה במטריצה ​​P, למסמך - עמודה במטריצה ​​Q). וקטורים אלה יהיו ההטבעות של האובייקטים המתאימים. כדי לחזות אם משתמש יאהב מסמך, אתה יכול פשוט להכפיל את ההטמעות שלו.

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

כיצד אנו עובדים על איכות ומהירות בחירת ההמלצות

כאן rui הוא האינטראקציה של המשתמש u עם מסמך i, qi הוא הווקטור של מסמך i, pu הוא הווקטור של המשתמש u.

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

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

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

סינון שיתופי מבוזר

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

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

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

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

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

כיצד אנו עובדים על איכות ומהירות בחירת ההמלצות

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

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

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

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

העבר לאזור דומיין אחר

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

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

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

מסקנה

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

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

מקור: www.habr.com

הוספת תגובה