Перейти к содержимому

מפענחים את סיומות הקוונטיזציה של LLM: מה באמת אומרים Q4_K_M, Q6_K ו-Q8_0

רמה: בינונית
זמן קריאה: 7 דקות
צפיות: 9K
תגיות: Python, בינה מלאכותית, למידת מכונה
מאמר מתורגם מהמקור ברוסית
מאת: [שם המחבר המקורי]

היי!
התלבטתם פעם איזו גרסה מקוונטזת (quantized) של LLM לבחור: Q4_K_M, Q6_K או Q8_0? עד כמה Q6_K פחות טוב במשימות בהשוואה ל-Q8_0? ומה בכלל אומרות כל האותיות האלה בסיומות?

הערה: זהו תרגום מעובד של המאמר המקורי שלי ב-Medium. התרגום נעשה בעזרת המוח, ולא בעזרת רשתות נוירונים או גוגל טרנסלייט.

התלבטות טיפוסית בעת הורדת מודל LLM מקוונטז מ-Hugging Face
התלבטות טיפוסית בעת הורדת מודל LLM מקוונטז מ-Hugging Face

במאמר זה נבין מה משמעותן של סיומות כמו Q4_K_M, מדוע Q4 ≠ int4, ואיך לא להילחץ בראיון עבודה אם ישאלו אתכם על הדקויות של קוונטיזציה.
בואו נתחיל!

איך להבין את הסיומות

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

מה אומרות הסיומות:

  • Q – מציין שהמודל עבר קוונטיזציה. אם נכנסים לעומק, מדובר בשיטת קוונטיזציה ספציפית — GWQ (group-wise quantization), שכן קיימות שיטות אחרות, פחות נפוצות כיום.
  • הספרה אחרי Q (לדוגמה, Q4) – מספר הביטים למשקל (weight) אחד.
  • K – קיבוץ (grouping) של משקולות (לכל קבוצה יש scale ו-zero-point משלה).
  • 0 – ללא קיבוץ (סכמה מיושנת).
  • S – Small, דיוק נמוך, מהירות גבוהה יותר, אך איכות נמוכה יותר.
  • M – Medium, דיוק בינוני.
  • L – Large, דיוק גבוה, השיטה האיטית והמדויקת ביותר.

מה בדיוק אומר הקיבוץ (K ו-0), נסביר בהמשך.

אז בואו נפרק מה בדיוק אומרת הסיומת Q4_K_M:

  • Q4 – מודל מקוונטז עם משקולות של 4 סיביות (ביטים).
  • K – קיבוץ של משקולות (בגדלים של 64 או יותר), עם scale אינדיבידואלי.
  • M – דיוק בינוני (איזון אופטימלי בין איכות לגודל).

משקולות (Weights)

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

הספרה לפני b בשם המודל (למשל, Gemini-12b) מציינת כמה מיליארדי משקולות יש בו. לדוגמה, ב-Gemini-12b יש 12 מיליארד משקולות, ובעת יצירת טוקן אחד, ייתכן שימוש ב-1 עד 12 מיליארד משקולות בשכבות שונות של הרשת. במהלך האימון, המשקולות נשמרות בדרך כלל בפורמט Float32 או Float16 – כלומר, כל אחת מהן תופסת 32 או 16 ביטים.

אופן האחסון של Float32 (מקור: puntoflotante.net)
אופן האחסון של Float32 (מקור: puntoflotante.net)

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

לכאורה, אפשר פשוט לחתוך את Float32 ל-Int4 – אבל זה לא עובד:
int8(0.123456) = 0

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

לכן, במקום עיגול גס, משתמשים בסקיילינג (scale), נקודת אפס (zero point) וקיבוץ (grouping).

דוגמה:

  • משקולות מקוריות: [0.11, 0.09, 0.12, 0.10, ..., 0.15]
  • Scale = 0.005
  • Zero point = 0.08
  • ערכים מקוונטזים: [6, 2, 8, 4, ..., 14]

בזמן ההסקה, המשקולות משוחזרות כך:
משקל = scale * קוונט + zero_point

במהלך דה-קוונטיזציה (המרת ערכי int בחזרה ל-float בזמן ההסקה), הערכים המקוונטזים 6, 2, 8 הופכים ל:
0.005 * 6 + 0.08 = 0.11
0.005 * 2 + 0.08 = 0.09
0.005 * 8 + 0.08 = 0.12

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

💡 מסקנה: בקוונטיזציית Q4 אנחנו לא פשוט דוחסים Float של 32 ו-16 ביט ל-Int4 – אנחנו שומרים מטא-דאטה עבור כל קבוצה, המאפשר לנו לשחזר את המספרים ברמת דיוק גבוהה.

קיבוץ (K ו-0)

קוונטיזציית משקולות מלווה לעתים קרובות בקיבוץ: הערכים מחולקים לבלוקים (קבוצות), ולכל אחת מהן מחושבים בנפרד ה-scale ו-zero point. זה מאפשר לדחוס ביעילות את המשקולות ל-4-6 ביטים עם אובדן דיוק מינימלי.

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

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

טנזור להמחשה – לדוגמה בלבד
טנזור להמחשה – לדוגמה בלבד

זו המשמעות של הסיומת K: קוונטיזציה קבוצתית (group-wise), המאפשרת להתאים את ה-scale/zero point לטווח הערכים המקומי ולהשיג דיוק גבוה יותר.

אם במקום K מופיע 0, כמו ב-Q4_0, זה אומר שנעשה שימוש ב-scale ו-zero point גלובליים, המשותפים לכל הטנזור (או שורת הטנזור). סכמה כזו פשוטה ומהירה יותר, אך פחות מדויקת: הטווח שמכוסה על ידי 4 ביטים צריך «להיכנס» לכל קבוצת הערכים בבת אחת. זה מוביל לשגיאת שחזור גדולה יותר.

ומה לגבי הסיומת 1?
לפעמים נתקלים גם בגרסה עם סיומת 1, למשל Q5_1. זו, כמו 0, שיטה מיושנת, הדומה ל-Q4_0 אך עם שיפורים מינימליים: הקוונטיזציה מיושמת שורה-אחר-שורה על הטנזור, עם scale ו-zero point קבועים לכל שורה. פורמט זה בדרך כלל מעט מדויק יותר מ-0 ומהיר יותר מ-K, אך עדיין נופל משמעותית באיכותו מהסכמות הקבוצתיות המודרניות.

💡 מסקנה: אם יש בחירה בין K, 0 ו-1 – כמעט תמיד כדאי לבחור ב-K: היא נותנת את היחס הטוב ביותר בין דיוק למשקל. אפשר לשקול 0 ו-1 רק במגבלות חומרה קשות, למשל במכשירים ניידים או במערכות זמן-אמת.

בלבול המודל (Perplexity)

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

Perplexity הוא מדד למידת ה»אי-ודאות» של המודל בחיזוי הטוקן הבא. ככל שהוא נמוך יותר – כך המודל «בטוח» יותר בטוקן הבא. בפועל, חשוב במיוחד ה-Perplexity היחסי – עד כמה הערך עלה בהשוואה למודל הלא-מקוונטז (כלומר, המקור).

אם נריץ quantize --help בפרויקט llama.cpp, נוכל לראות טבלה עם כל אפשרויות הקוונטיזציה וה-Perplexity שלהן (המספרים מתייחסים למודל Vicuna 13b):

Allowed quantization types:
   2  or  Q4_0   :  3.50G, +0.2499 ppl @ 7B - small, very high quality loss - legacy, prefer using Q3_K_M
   3  or  Q4_1   :  3.90G, +0.1846 ppl @ 7B - small, substantial quality loss - legacy, prefer using Q3_K_L
   8  or  Q5_0   :  4.30G, +0.0796 ppl @ 7B - medium, balanced quality - legacy, prefer using Q4_K_M
   9  or  Q5_1   :  4.70G, +0.0415 ppl @ 7B - medium, low quality loss - legacy, prefer using Q5_K_M
  10  or  Q2_K   :  2.67G, +0.8698 ppl @ 7B - smallest, extreme quality loss - not recommended
  12  or  Q3_K   : alias for Q3_K_M
  11  or  Q3_K_S :  2.75G, +0.5505 ppl @ 7B - very small, very high quality loss
  12  or  Q3_K_M :  3.06G, +0.2437 ppl @ 7B - very small, very high quality loss
  13  or  Q3_K_L :  3.35G, +0.1803 ppl @ 7B - small, substantial quality loss
  15  or  Q4_K   : alias for Q4_K_M
  14  or  Q4_K_S :  3.56G, +0.1149 ppl @ 7B - small, significant quality loss
  15  or  Q4_K_M :  3.80G, +0.0535 ppl @ 7B - medium, balanced quality - *recommended*
  17  or  Q5_K   : alias for Q5_K_M
  16  or  Q5_K_S :  4.33G, +0.0353 ppl @ 7B - large, low quality loss - *recommended*
  17  or  Q5_K_M :  4.45G, +0.0142 ppl @ 7B - large, very low quality loss - *recommended*
  18  or  Q6_K   :  5.15G, +0.0044 ppl @ 7B - very large, extremely low quality loss
   7  or  Q8_0   :  6.70G, +0.0004 ppl @ 7B - very large, extremely low quality loss - not recommended
   1  or  F16    : 13.00G              @ 7B - extremely large, virtually no quality loss - not recommended
   0  or  F32    : 26.00G              @ 7B - absolutely huge, lossless - not recommended

שימו לב לעמודה החמישית – היא זו שמראה את הסטייה היחסית. Q8_0 כמעט ולא נופל מהמודל המלא בדיוקו, בעוד ש-Q2_K מאבד כבר יותר מדי מידע (ppl = +0.8698 – זה כבר קריטי).

כברירת מחדל, מפתחי llama.cpp ממליצים על האפשרויות הבאות לפרודקשן:

  • Q4_K_M
  • Q5_K_S
  • Q5_K_M

KL Divergence (KLD)

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

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

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

הנה מה שהראה משתמש Reddit בשם kindacognizant בניסוי שלו עם פורמטים מקוונטזים של Mistral 7B על מדגם מוגדר מראש של כ-350 טוקנים מוויקיפדיה:

ממוצע KLD של גרסאות מקוונטזות של Mistral 7B ביחס למקור FP16
ממוצע KLD של גרסאות מקוונטזות של Mistral 7B ביחס למקור FP16

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

KLD עבור הטוקן שנפגע הכי הרבה בכל גרסה
KLD עבור הטוקן שנפגע הכי הרבה בכל גרסה

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

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

KLD עבור 5% מהטוקנים שנפגעו הכי הרבה בכל גרסה
KLD עבור 5% מהטוקנים שנפגעו הכי הרבה בכל גרסה

ההבדל בין ה-KLD הממוצע ל-KLD ב»זנב» די בולט:

  • Q3_K_M: ממוצע +0.037 → +0.263 (ב-5% העליונים) → הבדל של פי 7
  • Q2_K: ממוצע +0.082 → +0.713 (ב-5% העליונים) → הבדל של פי 8.6

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

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

כמה הבהרות לסיום

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

  • QAT (quantization-aware training)
  • ההבדלים בין קוונטיזציה סטטית ודינמית
  • ושיטות מעבר ל-group-wise quantization

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

  • טנזורים של קשב (attention)
  • טנזורים של בלוקי FFN (feedforward)
  • ורכיבים אחרים

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

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

תגיות: ai, ml, llm, llm-models, llm-applications, llm-architecture, quantization, artificial intelligence, ml and ai.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *