דילוג לתוכן
New

DeepSeek V4.1 Flash free for 7 days: 10M tokens a day after a top-up of $10 or more. Until Nov 30.

How it works

מהו מודל שפה גדול (LLM)?

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

מתחילים10 דקות קריאהמאת NotHoly

מהו מודל שפה גדול

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

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

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

למה זה חשוב לעבודת אבטחה

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

הבנת המנגנון משנה שלושה הרגלים. מפסיקים לצפות מהמודל לדעת דברים שמעולם לא ראה. מפסיקים לקרוא שטף כראיה. ומתחילים לשאול איפה אפשר לבדוק כל טענה. NIST Generative AI Profile, שפורסם כ-NIST AI 600-1, מונה confabulation בין הסיכונים של מערכות AI גנרטיביות. Confabulation היא הפקה של תוכן שגוי שמוצג בביטחון. הפרופיל מבקש מארגונים להיערך לזה ולא להניח שזה לא יקרה.

איך זה עובד, שלב אחר שלב

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

שלב 1: טקסט הופך לטוקנים

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

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

כדי להמחיש את הרעיון, הסקריפט הבא בונה BPE זעיר מכמה מילים. זה צעצוע ולא ה-tokenizer של מודל אמיתי, אבל המנגנון זהה. שומרים אותו בשם toy_bpe.py ומריצים עם Python 3:

python
from collections import Counter

corpus = "low low low lower lowest newer newer newer wider wider new new"
words = Counter(tuple(w) + ("</w>",) for w in corpus.split())

def pair_counts(vocab):
    pairs = Counter()
    for word, freq in vocab.items():
        for a, b in zip(word, word[1:]):
            pairs[(a, b)] += freq
    return pairs

def merge(vocab, pair):
    out = Counter()
    for word, freq in vocab.items():
        new, i = [], 0
        while i < len(word):
            if i < len(word) - 1 and (word[i], word[i + 1]) == pair:
                new.append(word[i] + word[i + 1]); i += 2
            else:
                new.append(word[i]); i += 1
        out[tuple(new)] += freq
    return out

merges = []
vocab = words
for step in range(6):
    pairs = pair_counts(vocab)
    if not pairs:
        break
    best = max(pairs, key=lambda p: (pairs[p], p))
    merges.append(best)
    vocab = merge(vocab, best)
    print(f"merge {step + 1}: {best[0]!r} + {best[1]!r} -> {best[0] + best[1]!r} (count {pairs[best]})")

def tokenize(word):
    toks = list(word) + ["</w>"]
    for a, b in merges:
        i = 0
        while i < len(toks) - 1:
            if toks[i] == a and toks[i + 1] == b:
                toks[i:i + 2] = [a + b]
            else:
                i += 1
    return toks

for w in ["lowest", "newest", "widen"]:
    print(w, "->", tokenize(w))

הפלט הבא מגיע מהרצת הסקריפט ב-Windows 11 עם Python 3.12.10. הפלט אצלך אמור להתאים שורה בשורה, כי הסקריפט לא משתמש באקראיות:

text
merge 1: 'r' + '</w>' -> 'r</w>' (count 6)
merge 2: 'e' + 'r</w>' -> 'er</w>' (count 6)
merge 3: 'w' + '</w>' -> 'w</w>' (count 5)
merge 4: 'n' + 'e' -> 'ne' (count 5)
merge 5: 'l' + 'o' -> 'lo' (count 5)
merge 6: 'w' + 'er</w>' -> 'wer</w>' (count 4)
lowest -> ['lo', 'w', 'e', 's', 't', '</w>']
newest -> ['ne', 'w', 'e', 's', 't', '</w>']
widen -> ['w', 'i', 'd', 'e', 'n', '</w>']

קוראים קודם את המיזוגים. הזוג השכיח ביותר בקורפוס הקטן הוא סמן סוף מילה ליד r, ולכן המיזוג הזה בא ראשון. מיזוגים מאוחרים יותר בונים חלקים כמו er</w> וגם lo, שמשקפים איותים חוזרים בקורפוס. אחר כך מסתכלים על הפיצולים. "lowest" מתפרק לחלקים lo, w, e, s, t ולסמן הסוף, כי אוצר המילים של הצעצוע לא למד חלק עבור est או owe. "widen" חוזר כמעט לתווים בודדים. אוצר המילים של ה-tokenizer מכסה את מה שראה באימון שלו, וכל דבר אחר עולה יותר חלקים.

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

שלב 2: טוקנים הופכים לווקטורים

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

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

שלב 3: Attention וה-transformer

רוב ה-LLMs הנוכחיים בנויים על ארכיטקטורת ה-transformer, שתוארה ב-"Attention Is All You Need" של Vaswani ועמיתיו, שפורסם ב-2017 ‏(arXiv:1706.03762). מודלי שפה קודמים קראו לעיתים קרובות טקסט טוקן אחרי טוקן ונשאו קדימה סיכום מתמשך. ה-transformer מאפשר לכל מיקום להסתכל ישירות על מיקומים אחרים דרך מנגנון שנקרא attention.

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

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

שלב 4: ניבוי הטוקן הבא

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

ניקח את הביטוי "The capital of France is". כמעט כל ההסתברות הולכת לטוקן שממשיך ב-"Paris". המודל לא בדק במפה. הוא למד שההמשך הזה נפוץ מאוד בטקסט שראה. אותו מנגנון מטפל בבקשות קשות יותר: הערת קוד ואחריה חתימת פונקציה, הודעת שגיאה ואחריה סיבה סבירה, שאלה ואחריה הסבר שנשמע הגיוני. כל אחד מהם הוא המשך, וההצלחה של המודל תלויה בעד כמה הדפוסים שלמד מתאימים לטקסט החדש.

שלב 5: אימון

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

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

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

שלב 6: הסקה

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

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

חלון הקשר

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

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

Temperature ודגימה

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

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

למה מודלים נשמעים בטוחים ועדיין טועים

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

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

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

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

טעויות נפוצות

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

מה זה אומר לעבודת אבטחה

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

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

איך ממשיכים

קוראים את הסעיף הראשון ואת תרשים הארכיטקטורה של מאמר ה-transformer. לא צריך כל משוואה כדי לראות את צורת הרעיון. אחר כך מריצים את ה-tokenizer הצעצוע על מילים שלך ובודקים איפה הוא מפצל אותן. המאמר של Sennrich ועמיתיו הציג יחידות תת־מילה שנבנו ב-byte-pair encoding, והפרק בקורס של Hugging Face עובר על האלגוריתם עם קוד. לאחר מכן קוראים את הסעיף על confabulation בפרופיל של NIST כדי לראות איך הסיכון מנוסח עבור ארגונים.

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

מקורות

מקורות

  1. Attention Is All You Need (arXiv:1706.03762)
  2. Neural Machine Translation of Rare Words with Subword Units (arXiv:1508.07909)
  3. NIST AI 600-1: Generative AI Profile
  4. Byte-Pair Encoding tokenization, LLM Course

עוד בנושא מודלי LLM ו-API

מודלי LLM ו-API

מעבר מ-Verboo ל-NotHoly: מה משתנה ומה נשאר

איך מעבירים זרימת עבודה תואמת OpenAI מ-Verboo, מ-Venice AI או מ-Drael ל-NotHoly: base URL, מפתח, מזהי מודל, חיוב, רישיון וכלים.

ביניים5 דקות קריאה