כיצד לייצג מילים כמספרים בעלי משמעות? על הבסיס לאלגוריתמי NLP

מיועד ל- כל אחד (כתבה לא טכנית)

נכתב על ידי Vered Shwartz

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

מספרים במקום מילים

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

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

ייצוג לפי אינדקס (One-hot Vector)

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

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

בייצוג one-hot-vector אנחנו מייצגים כל מילה באמצעות האינדקס שלה ב-V. מילה w באינדקס i מיוצגת ע”י וקטור שאורכו כגודל אוצר המילים – |V|, וכל ערכיו 0 פרט לאינדקס i שערכו 1. ככה ייראו וקטורים של כמה מילים לדוגמה:

nlp

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

בהקשר של משימת הקצה של סיווג טקסטים, מקובל לייצג מסמך ע”י קבוצת כל המילים שהופיעו בו (שאמורות להעיד על הנושא שלו). כלומר, מרחב הפיצ’רים הוא כל המילים הקיימות בשפה, וערך הפיצ’ר של מילה מסוימת w במסמך d יהיה פרופורציונלי למס’ ההופעות של המילה w במסמך d. ייצוג של מסמך ע”י סכימה של כל ווקטורי ה-one-hot של המילים המופיעות בו ייתן לנו בדיוק את זה. עכשיו נניח שבזמן האימון המסווג שלנו ראה הרבה מסמכים מתויגים לנושא “טכנולוגיה” שהכילו את המילה “מחשב”. הוא לומד שהמילה “מחשב” היא פיצ’ר חזק עבור הנושא “טכנולוגיה”. כעת נראה למסווג מסמך שמדבר על מחשבים ניידים והוא יכיל הופעות רבות של המילה “לפטופ”. אם המסווג לא נתקל בזמן האימון (מספיק פעמים) במילה “לפטופ” במסמכים בנושא טכנולוגיה, הוא לא ידע להשלים את המידע החסר הזה מהדמיון למילה “מחשב”. שתי המילים האלה הן פיצ’רים נפרדים לחלוטין עבור המסווג. זה לא מצב טוב.

ייצוג מבוסס שכנים (Distributional Vectors)

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

הרעיון הוא שווקטור של מילה w יהיה כעת עדיין באורך |V|, אבל המשמעות של כל כניסה בווקטור תהיה שונה. הערך של כניסה u בווקטור של w יהיה פרופורציונלי למס’ הפעמים ש-w ו-u הופיעו ביחד בקורפוס האימון. ההגדרה של “הופיעו יחד” גמישה, אבל באופן הפשוט נניח הופעה של u בחלון של k מילים (למשל k=5) סביב w. הערך עצמו יכול להיות מספר ההופעות המשותפות, מספר מנורמל (הסתברות), או מדד כמו PMI, המפחית את ההשפעה של השכיחות של כל מילה בפני עצמה.

שיטות ספירה (Count-based Vectors)

הדבר הפשוט ביותר לעשות יהיה לעבור על קורפוס האימון, ולבנות את מטריצת ההופעות המשותפות (בגודל |V|x|V|) של כל מילה w עם מילה u בחלון בגודל k. השורות של המטריצה משמשות בתור ווקטורי מילים, ואותן אפשר לנרמל (למשל באמצעות PMI). ככה יראו עכשיו הווקטורים של המילים לדוגמה. נראה יותר טוב, לא?

nlp

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

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

שיכוני מילים (Word Embeddings)

אז איך אפשר להשיג ווקטורים דחוסים ממימד נמוך ששומרים על התכונה הטובה של הייצוג מבוסס השכנים? נקפוץ כמה שנים קדימה (ונדלג על פתרון הביניים – הפעלת אלגוריתם להורדת מימדים כגון SVD על מטריצת ההופעות המשותפות). במקום לספור הופעות משותפות, אפשר להחליט מראש על מימד קטן כלשהו D (בדר”כ 50-1000, הכי נפוץ 300) ולחזות ווקטורים באורך D שישמרו על התכונה הזו – כלומר, שלמילים שמופיעות באותם ההקשרים יהיו ווקטורים דומים. למרות שהרעיון הזה כבר הוצע ב-2003 עי בנגיו וקולגות, הוא זכה לפופולריות רק ב-2013 כשמיקולוב וקולגות פרסמו את word2vec (שהמימוש שלו היה יעיל, והתוצאות מרשימות). ככה ייראו הווקטורים, קטני מימדים ודחוסים, ועדיין שומרים על תכונת הדמיון:

nlp

הרעיון של word2vec הוא ללמוד מטריצה בגודל |V| על D, שכל שורה בה מייצגת מילה. זאת המטריצה של מילות היעד (target). כעזר, מחזיקים מטריצה נוספת באותם המימדים שמייצגת מילה כמילת הקשר (context). שתי המטריצות מאותחלות אקראית. בזמן האימון, האלגוריתם עובר על כל הקורפוס, מילה-מילה, כאשר בכל פעם מילה אחת נחשבת ליעד והמילים המקיפות אותה בחלון נחשבות למילות ההקשר. יש שתי גרסאות לאלגוריתם: באחת (CBOW = continuous bag of words), מנסים לחזות את מילת היעד ממילות ההקשר, ובשנייה (skip-gram), מנסים באמצעות מילת היעד לחזות את מילות ההקשר. כפועל יוצא של החיזוי הזה, הווקטור של מילת היעד (ממטריצת היעד) מתקרב לווקטורים של מילות ההקשר (ממטריצת ההקשר). נוסף על כך, ווקטור היעד צריך להתרחק מווקטורי ההקשר של המילים שלא הופיעו בחלון. בגרסה היעילה יותר (negative sampling), דוגמים k מילים אקראיות כלשהן (שכנראה לא הופיעו בחלון) ומוסיפים לפונקציית המטרה גורם דומה לחיזוי של מילות ההקשר אבל עם המילים האקראיות ומוכפל במינוס 1.

חוץ מ-word2vec יש עוד לא מעט אלגוריתמים שונים ללמידה של word embeddings ע”י חיזוי, הנפוצים מביניהם GloVe של סטנפורד ו-FastText של פייסבוק. בשנים האחרונות הם משמשים כקלט לכל אלגוריתם למידה בתחום עיבוד שפה, בין אם בשימוש בווקטורים המאומנים שפורסמו עם השיטות האלה או באימון מחדש על קורפוס אחר. בהקשר של משימת סיווג הטקסטים שלנו, נוכל עדיין לייצג מסמך ע”י סכום או ממוצע וקטורי המילים. וקטור מסמך יהיה כעת באורך D והפיצ’רים שלו יהיו חבויים, אבל מסמכים עם מילים דומות עדיין יקבלו ייצוג דומה ויסווגו לאותו הנושא, ובמחיר חישובי נמוך יותר. המטרה הושגה!

בעיניי ההצלחה של הווקטורים נמדדת בעיקר בכמה שהם משפרים ביצועים של משימות קצה וכמה קל השימוש בהם. במאמרים המתארים את האלגוריתמים ליצירת הווקטורים, נהוג לדווח על תוצאות על שתי משימות מלאכותיות שבוחנות את איכות הווקטורים: (1) מודל רגרסיה שחוזה דמיון בין מילים בהסתמך על הווקטורים (ומושווה לציוני הדמיון שאנשים נתנו לזוגות מילים), ו-(2) פתרון של בעיות אנלוגיה מהסוג “גבר:מלך ::אישה:?”. הנה דוגמה לאיך שהאנלוגיות האלה נראות במרחב הווקטורי, מתוך המאמר של word2vec:

nlp

תודה ל https://www.kdnuggets.com/2016/05/amazing-power-word-vectors.html

בתור מדד איכותי, נהוג לחשב הטלה של הווקטורים למימד 2 ע”י t-SNE או PCA ולצייר גרף מילים שממנו ניתן לראות שמילים דומות סמנטית ממוקמות זו לצד זו ב(הטלה למימד 2 של ה)מרחב הווקטורי. הנה דוגמת קוד לחישוב הזה, וכך למשל נראה חלק קטן מהגרף המתקבל כשמפעילים t-SNE על GloVe (מאומן מראש, בגודל 50):

nlp

העתיד של ייצוגי מילים

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

ההתפתחות האחרונה בייצוגי מילים היא ההכרה שמשמעות של מילה נגזרת לא רק מהמשמעות הכללית שלה אלא גם במשמעות שלה בהקשר נתון, וזה נכון בעיקר (אבל לא רק) עבור מילים רבות משמעות. למשל, המילה “עכבר” במשפט “קניתי מקלדת ועכבר אלחוטיים” שונה במשמעותה מאשר במשפט “החתול רדף אחרי העכבר”. במקום לייצג את המילה “עכבר” בווקטור יחיד שמאגד את כל ההופעות שלה בקורפוס (בשתי המשמעויות), אפשר לחשב את הווקטור הזה בכל פעם מחדש עבור השימוש הספציפי, ושיהיה תלוי-הקשר. זה מה שעושים בשיטת ELMo שלאחרונה זכתה בפרס המאמר הטוב ביותר בכנס NAACL 2018. לא מעט מאמרים יצאו לאחרונה שהראו ששימוש ב-ELMo משפר ביצועים של משימות קצה על-פני שימוש בווקטורים סטטיים. ההקשר חשוב. החיסרון הברור הוא כמובן שהשימוש בווקטורים דינאמיים הרבה יותר כבד מבחינה חישובית: במקום לשלוף ווקטור מוכן צריך לחשב אותו בכל פעם מחדש ע”י מעבר על כל המשפט… אין מתנות חינם.