אופטימיזציה של הסקת LLM: llama.cpp, כימות GGUF, היברידי

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

תחומי פיתוח AI

שאלות נפוצות

העבודות האחרונות

  • פיתוח אפליקציית ווב עבור FEEDME
    פיתוח אפליקציית ווב עבור FEEDME
    1365
  • פיתוח חנות מקוונת לחברת FURNORO
    פיתוח חנות מקוונת לחברת FURNORO
    1330
  • עיצוב לוגו לחברת B2B Advance
    עיצוב לוגו לחברת B2B Advance
    776
  • פיתוח אפליקציית ווב עבור Enviok
    פיתוח אפליקציית ווב עבור Enviok
    1077
  • פיתוח לוגו חברת AIDER
    פיתוח לוגו חברת AIDER
    1022
  • פיתוח CRM עבור Chasseurs
    פיתוח CRM עבור Chasseurs
    1113

llama.cpp: היכן צוואר הבקבוק באינפרנס מקומי

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

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

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

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

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

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

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

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

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

מה בודקים ביציאה:

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

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

השוואת גישות: llama.cpp וסכמות חלופיות

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

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

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

סדר העבודות: מביקורת עומס ועד מסירת סכמות

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

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

מה מוסרים: תצורות, סכמות, נהלי עדכון

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

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

דוגמה להטמעה: פחות צמתים, זמן תגובה יציב

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

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

את האינפרנס עברנו ל-llama.cpp: משקולות ברזולוציה מופחתת של פורמט GGUF, חישוב היברידי — חלק מהשכבות במאיץ, חלק במעבד, שימוש חוזר במטמון מצבים ועיבוד באצווה רציף במקום אצוות קבועות.

מדדים לפני ואחרי

  • מספר צמתים: היה שבעה, הפך לשניים — חמש מכונות התפנו למשימות אחרות;
  • זמן תגובה בשיא: היה 4.2 שניות באחוזון ה-95, הפך ל-1.8 שניות;
  • התנהגות תחת עומס: התור הפסיק לגדול, פיזור זמן התגובה — עשיריות שנייה;
  • איכות התשובות: השווינו על מדגם בקשות מושהה, לא ראינו פערים מול המודל המקורי.

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

במה סכמה היברידית שונה מהרצה מקומית

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

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

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

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

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

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

שאלות טכניות נפוצות על אופטימיזציה של אינפרנס

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

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

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

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

האם החומרה הקיימת תספיק?

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

איך לעדכן מודל בלי לעצור את השירות?

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

עד כמה ארכיטקטורת הפתרון תלויה במודל מסוים?

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

נדון במשימה: מה לשלוח להערכה ראשונית

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

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

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