שיטות מומלצות לאופטימיזציה של הסקת מסקנות של מודלים גדולים של שפה (LLM) באמצעות GPU ב-Google Kubernetes Engine‏ (GKE)

‫Google Kubernetes Engine‏ (GKE) מספק שליטה מדויקת בהסקת מסקנות של מודלים גדולים של שפה (LLM) עם ביצועים ועלויות אופטימליים. במדריך הזה מתוארות שיטות מומלצות לאופטימיזציה של הסקת מסקנות והצגה של מודלים גדולים של שפה (LLM) בקוד פתוח באמצעות יחידות עיבוד גרפי (GPU) ב-GKE, באמצעות מסגרות ההצגה vLLM ו-Text Generation Inference (TGI).

סיכום רשימת המשימות

מטרות

המדריך הזה מיועד ללקוחות של AI גנרטיבי, למשתמשי GKE חדשים או קיימים, למהנדסי ML ולמהנדסי LLMOps (DevOps) שרוצים לבצע אופטימיזציה של עומסי עבודה של LLM באמצעות מעבדי GPU עם Kubernetes.

בסוף המדריך הזה תוכלו:

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

סקירה כללית של שיטות לאופטימיזציה של מודלים גדולים של שפה (LLM)

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

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

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

לפני שמתחילים

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

  1. כדי לקבל גישה למודל Gemma, להכין את הסביבה וליצור ולהגדיר משאבים, פועלים לפי ההוראות במדריכים הבאים: Google Cloud

    חשוב לשמור את טוקן הגישה של Hugging Face בסוד של Kubernetes.

  2. משכפלים את מאגר הדוגמאות https://github.com/GoogleCloudPlatform/kubernetes-engine-samples/ לסביבת הפיתוח המקומית.

  3. משנים את ספריית העבודה ל-/kubernetes-engine-samples/ai-ml/llm-serving-gemma/.

שיטה מומלצת: קוונטיזציה

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

קיימות שיטות שונות לקוונטיזציה, ולכל אחת מהן יש יתרונות וחסרונות ייחודיים. חלק מהשיטות, כמו AWQ ו-GPTQ, דורשות קוונטיזציה מראש וזמינות בפלטפורמות כמו Hugging Face או Kaggle. לדוגמה, אם מפעילים GPTQ על מודל Llama-2 13B ו-AWQ על מודל Gemma 7B, אפשר להפעיל את המודלים על GPU L4 יחיד במקום על שני GPU L4 ללא קוונטיזציה.

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

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

בוחרים אחת מהכרטיסיות האלה כדי לראות דוגמה להחלת קוונטיזציה באמצעות מסגרות העבודה TGI או vLLM:

TGI

‫GKE תומך באפשרויות הכימות הבאות עם TGI:

  • awq
  • gptq
  • eetq
  • bitsandbytes
  • bitsandbytes-nf4
  • bitsandbytes-fp4

שיטות הכימות AWQ ו-GPTQ דורשות מודלים שעברו כימות מראש, בעוד שכימות EETQ ו-bitsandbytes אפשר להחיל על כל מודל. מידע נוסף על האפשרויות האלה מפורט במאמר הזה ב-Hugging Face.

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

בקטע הקוד הבא מוצג איך לבצע אופטימיזציה של Gemma 7B באמצעות כימות bitsandbytes של TGI ב-GKE.

args:
- --model-id=$(MODEL_ID)
- --num-shard=2
- --quantize=bitsandbytes

כדי להחיל את ההגדרה הזו, משתמשים בפקודה הבאה:

kubectl apply -f tgi/tgi-7b-bitsandbytes.yaml

vLLM

‫GKE תומך באפשרויות הכימות הבאות באמצעות vLLM:

כדי להשתמש בכימות מודלים עם vLLM, המודלים צריכים להיות מכומתים מראש. כשמפעילים את זמן הריצה, מגדירים את הפרמטר –quantization.

בקטע הקוד הבא מוצג איך לבצע אופטימיזציה של מודל Gemma 7B באמצעות awq קוונטיזציה באמצעות vLLM ב-GKE:

args:
- --model=$(MODEL_ID)
- --tensor-parallel-size=1
- --quantization=awq
env:
- name: MODEL_ID
  value: google/gemma-7b-AWQ
resources:
  requests:
    nvidia.com/gpu: 1
  limits:
    nvidia.com/gpu: 1

כדי להחיל את ההגדרה הזו, משתמשים בפקודה הבאה:

kubectl apply -f vllm/vllm-7b-awq.yaml

שיפור זמן האחזור באמצעות כימות של מטמון KV

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

כדי להפעיל את הכמותיזציה של מטמון FP8 E5M2 KV, מגדירים את הפרמטר --kv-cache-dtype fp8_e5m2:

args:
- --model=$(MODEL_ID)
- --tensor-parallel-size=1
- --kv-cache-dtype=fp8_e5m2
- --max-model-len=1200
resources:
  requests:
    cpu: "2"
    memory: "25Gi"
    ephemeral-storage: "25Gi"
    nvidia.com/gpu: 1
  limits:
    cpu: "2"
    memory: "25Gi"
    ephemeral-storage: "25Gi"
    nvidia.com/gpu: 1

כדי להחיל את ההגדרה הזו, משתמשים בפקודה הבאה:

kubectl apply -f vllm/vllm-7b-kvcache.yaml

שיטה מומלצת: Tensor parallelism

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

מידע נוסף על הטכניקה הזו זמין במדריך Tensor Parallelism של Hugging Face.

בוחרים אחת מהכרטיסיות הבאות כדי לראות דוגמה להחלת מקביליות טנסור באמצעות מסגרות TGI או vLLM:

TGI

עם TGI, סביבת זמן הריצה של ההגשה תשתמש בכל ה-GPU שזמינים ל-Pod כברירת מחדל. כדי להגדיר את מספר ה-GPU שבהם רוצים להשתמש, צריך לציין את הפרמטר --num-shard עם מספר ה-GPU כערך.

ברשימת המודלים במסמכי התיעוד של Hugging Face מפורטים המודלים שנתמכים על ידי tensor parallelism.

בקטע הקוד הבא מוצג איך לבצע אופטימיזציה של מודל Gemma 7B שעבר כוונון להוראות באמצעות מקביליות טנסור ושני מעבדי GPU מסוג L4:

args:
- --model-id=$(MODEL_ID)
- --num-shard=2

כדי להחיל את ההגדרה הזו, משתמשים בפקודה הבאה:

kubectl apply -f tgi/tgi-7b-it-tensorparallelism.yaml

באשכולות GKE Autopilot, הפעלת הפקודה הזו יוצרת Pod עם דרישות מינימליות למשאבים של 21 vCPU ו-78GiB זיכרון.

vLLM

‫vLLM תומך בהסקת מסקנות מקבילית של טנסורים מבוזרים. התכונה הזו מופעלת ב-vLLM כברירת מחדל אם יש יותר מ-GPU אחד.

בקטע הקוד הבא אפשר לראות איך אפשר לבצע אופטימיזציה של מודל Gemma 7B שעבר כוונון להוראות באמצעות מקביליות טנסור ושני מעבדי GPU מסוג L4:

args:
- --model=$(MODEL_ID)
- --tensor-parallel-size=2

כדי להחיל את ההגדרה הזו, משתמשים בפקודה הבאה:

kubectl apply -f vllm/vllm-7b-it-tensorparallelism.yaml

באשכולות GKE Autopilot, הפעלת הפקודה הזו יוצרת Pod עם דרישות מינימליות למשאבים של 21 vCPU ו-78GiB זיכרון.

שיטה מומלצת: אופטימיזציה של זיכרון המודל

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

אופטימיזציה של שכבת תשומת הלב

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

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

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

  • Paged attention: Paged attention משפר את ניהול הזיכרון במודלים גדולים וברצפים ארוכים של קלט באמצעות טכניקות החלפה, בדומה לזיכרון וירטואלי של מערכת ההפעלה. כך מצמצמים את הפיצול והכפילות במטמון KV, ומאפשרים רצפים ארוכים יותר של קלט בלי שייגמר הזיכרון של ה-GPU.
  • Flash attention: Flash attention מצמצם את צווארי הבקבוק בזיכרון ה-GPU על ידי מזעור העברות הנתונים בין זיכרון ה-RAM של ה-GPU לבין מטמון L1 במהלך יצירת הטוקנים. כך נמנע זמן בלי פעילות של ליבות המחשוב, ומשפרים באופן משמעותי את הביצועים של היקש ואימון של GPU.

התאמה של גודל הקלט והפלט של המודל

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

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

TGI

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

‫GKE תומך בפרמטרים הבאים של TGI לאופטימיזציה של דרישות הזיכרון של המודל:

בקטע הקוד הבא אפשר לראות איך מפעילים מודל Gemma 7B שעבר כוונון להוראות באמצעות GPU L4 יחיד, עם הגדרות הפרמטרים --max-total-tokens=3072, --max-batch-prefill-tokens=512, --max-input-length=512:

args:
- --model-id=$(MODEL_ID)
- --num-shard=1
- --max-total-tokens=3072 
- --max-batch-prefill-tokens=512
- --max-input-length=512
env:
- name: MODEL_ID
  value: google/gemma-7b

כדי להחיל את ההגדרה הזו, משתמשים בפקודה הבאה:

kubectl apply -f tgi/tgi-7b-token.yaml

vLLM

ב-vLLM, מגדירים את חלון ההקשר של המודל, שמשפיע ישירות על גודל מטמון ה-KV ועל דרישות ה-RAM של ה-GPU. אורכי הקשר הקצרים יותר מאפשרים שימוש במעבדי GPU במחירים נוחים יותר. ערך ברירת המחדל הוא מספר האסימונים המקסימלי שהמודל מקבל. אם צריך, מגבילים את האורך המקסימלי של חלון ההקשר באמצעות --max-model-len MAX_MODEL_LEN.

לדוגמה, מודל Gemma 7B שעבר כוונון להוראות, עם אורך ההקשר שמוגדר כברירת מחדל של 8,192, חורג מקיבולת הזיכרון של מעבד NVIDIA L4 GPU יחיד. כדי לפרוס ב-L4, מגבילים את האורך המשולב של ההנחיות והפלט על ידי הגדרת --max-model-len לערך מתחת ל-640. השינוי הזה מאפשר להריץ את המודל במעבד גרפי L4 יחיד, למרות חלון ההקשר הגדול שלו כברירת מחדל.

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

args:
- --model=$(MODEL_ID)
- --tensor-parallel-size=1
- --max-model-len=600

כדי להחיל את ההגדרה הזו, משתמשים בפקודה הבאה:

kubectl apply -f vllm/vllm-7b-token.yaml

סיכום רשימת המשימות

יעד אופטימיזציה תרגל
זמן אחזור
  • תעדוף של מעבדי GPU עוצמתיים: כדאי לשקול שדרוג למעבדי GPU עם יכולות חישוביות גבוהות יותר וזיכרון קלט/פלט עם קצב העברת נתונים גבוה.
  • כדאי לבדוק את הכמותיזציה: טכניקות כמו AWQ יכולות לשפר את זמן האחזור, אבל חשוב לשים לב לפשרות פוטנציאליות בדיוק.
תפוקה
  • Scale horizontally: Increase the number of serving replicas (Pods) to distribute the workload.
  • שימוש במקביליות של טנסורים: במודלים גדולים שחורגים מהקיבולת של GPU יחיד, אפשר להשתמש במקביליות של טנסורים כדי לפזר את החישובים בין כמה מעבדי GPU. במודלים קטנים יותר, כדאי להשתמש בכמה רפליקות עם מקביליות טנסור של '1' כדי להימנע מתקורה.
  • בקשות אצווה וכימות: שילוב בקשות ובדיקת טכניקות כימות ששומרות על רמת דיוק מקובלת.
עלות-תועלת
  • בחירת מודלים קטנים יותר: בוחרים מודלים בתוך משפחה שמתאימים למגבלות המשאבים ולתקציב.
  • Apply quantization: Use quantization to reduce memory requirements, particularly when dealing with larger models.
  • הגבלת חלון ההקשר: הגבלת חלון ההקשר כדי להקטין עוד יותר את שימוש בזיכרון ולאפשר הרצה ביחידות GPU קטנות יותר וחסכוניות יותר.

המאמרים הבאים