פרק 3 מתוך 5 | Skill-Building
הכנת Dataset — מטקסט גולמי ל-JSONL מוכן לאימון
אם בפרקים 1-2 החלטנו מתי Fine-Tuning הוא הכלי הנכון ומה נכנס על GPU חינמי, בפרק הזה אנחנו בונים את הדבר שבאמת יקבע אם האימון יעבוד: ה-Dataset. לא hyperparameters נוצצים, לא notebook מסתורי, ולא עוד prompt ארוך. Dataset נקי, עקבי, מגוון ומותאם ל-chat template הוא ההבדל בין מודל קטן שמרגיש כמו עוזר אישי לבין מודל שמוציא טקסט מקושקש בביטחון מלא.
מה יהיה לך ביד בסוף הפרק
- קובץ seeds-50.jsonl עם 50 דוגמאות ידניות איכותיות למשימה שלך.
- תבנית system/user/assistant קבועה שמגדירה למודל את התפקיד, הגבולות והסגנון.
- טבלת בחירת פורמט: ChatML, ShareGPT או Alpaca, עם החלטה מנומקת למשימה שלך.
- Prompt מוכן ל-Claude שמרחיב 50 seeds ל-200 ואז ל-1,000 דוגמאות בלי לשבור את הפורמט.
- קובץ clean-200.jsonl אחרי Deduplication, סינון איכות וזיהוי teacher artifacts.
- קבצי train.jsonl, validation.jsonl ו-heldout.jsonl מוכנים לפרק 4.
- קובץ dpo-pairs.jsonl ראשוני עם זוגות chosen/rejected לקראת פרק 5.
מה תוכל/י לעשות אחרי הפרק
- תוכל/י לבנות Dataset של 200+ זוגות איכותיים במבנה Instruction נכון, כולל system/user/assistant.
- תוכל/י לבחור Chat Template מתאים למודל ולוודא שהוא תואם ל-tokenizer לפני אימון.
- תוכל/י להרחיב 50 seed examples ל-1,000 דוגמאות עם Claude כ-Teacher Model בלי להוריד איכות.
- תוכל/י לבנות זוגות DPO בסיסיים, chosen/rejected, לקראת Preference Tuning בפרק 5.
- תוכל/י להריץ Quality Gate על Dataset: תקינות JSONL, כפילויות, התפלגות אורכים, גיוון instructions ו-teacher-model artifacts.
לפני שמתחילים
- פרקי חובה: פרק 1 על ההחלטה בין Prompt, RAG ו-Fine-Tuning; פרק 2 על LoRA/QLoRA ותקציב VRAM.
- כלים: עורך טקסט פשוט, Python או notebook בסיסי, חשבון Claude או מודל Teacher חזק אחר, ויכולת לשמור קובצי JSONL.
- זמן לפרק: כ-95 דקות קריאה ותרגול, או 2-3 שעות אם באמת בונים Dataset ראשון תוך כדי.
- דאטה: 20-50 דוגמאות מקוריות של טקסט, שיחות, תשובות תמיכה, פוסטים, קטעי קוד או outputs שאתה רוצה שהמודל ילמד לחקות.
הפרויקט שלך
בפרק 2 בנית אינטואיציה על LoRA/QLoRA, חישבת מה נכנס על T4 חינמי, והבנת למה rank נמוך יכול לשמור על מודל קטן ויציב.
בפרק הזה תוסיף לפרויקט את הדלק האמיתי: Dataset נקי, בפורמט נכון, עם דוגמאות שמלמדות את ההתנהגות שאתה רוצה ולא רק את הנושא הכללי.
בפרק 4 תשתמש בקבצים שנכין כאן כדי להריץ אימון Unsloth אמיתי על Colab או Kaggle, למזג adapter, להמיר ל-GGUF ולהריץ ב-Ollama.
מונחים שתפגוש/י בפרק
| Term | עברית | הגדרה קצרה |
|---|---|---|
| JSONL | JSON לפי שורות | קובץ שבו כל שורה היא אובייקט JSON עצמאי, כלומר דוגמת אימון אחת. |
| Instruction Format | מבנה הוראה | הצורה שבה מציגים למודל בקשה ותשובה רצויה כדי שילמד התנהגות. |
| Chat Template | תבנית שיחה | החוזה של ה-tokenizer: איך role/content הופכים לטוקנים שהמודל מזהה. |
| ChatML | פורמט messages | פורמט נפוץ עם רשימת messages, וכל הודעה כוללת role ו-content. |
| ShareGPT | פורמט שיחות ישן יותר | פורמט עם conversations, from ו-value; שימושי כשמקור הדאטה הגיע משיחות. |
| Alpaca | פורמט instruction/input/output | פורמט פשוט למשימות single-turn שבהן יש הוראה, קלט אופציונלי ופלט. |
| Seed Examples | דוגמאות זריעה | דוגמאות ידניות איכותיות שמגדירות ל-Teacher Model איך להרחיב את הדאטה. |
| Teacher Model | מודל מורה | מודל חזק כמו Claude שמייצר או משפר דוגמאות אימון למודל קטן יותר. |
| Judge LLM | מודל שופט | מודל שמדרג דוגמאות לפי rubric, כדי לסנן שורות חלשות לפני האימון. |
| Deduplication | הסרת כפילויות | זיהוי דוגמאות זהות או כמעט זהות שמלמדות את המודל לחזור על עצמו. |
| Held-Out Set | סט בדיקה חיצוני | דוגמאות שלא נכנסות לאימון ומשמשות למדידה הוגנת מול ה-base. |
| DPO | אימון העדפות | שיטת Preference Tuning שמלמדת מודל להעדיף תשובה chosen על פני rejected. |
למה ה-Dataset הוא 80% מה-Fine-Tuning
Dataset הוא לא "החומר שמכניסים לאימון". זו ההגדרה המעשית של ההתנהגות שאתה רוצה שהמודל ילמד. אם הדוגמאות שלך קצרות מדי, מעורבבות מדי, או כתובות בסגנון שלא דומה לתוצר הסופי, המודל ילמד בדיוק את הבלגן הזה. Fine-Tuning לא קורא את הכוונה שלך בין השורות. הוא רואה רצפים של טוקנים, מזהה דפוסים, ומנסה להמשיך אותם.
זו הסיבה שהרבה ניסיונות Fine-Tuning נראים כמו כישלון של LoRA או כישלון של ה-hyperparameters, אבל בפועל הם כישלון של דאטה. מישהו מאמן מודל על 60 תשובות תמיכה אמיתיות, מתלהב שהאימון הסתיים בלי שגיאה, ואז מגלה שהמודל עונה רק על שלושה סוגי שאלות וחוזר על אותם ניסוחים. או שהוא בונה Dataset של brand voice מפוסטים ישנים, אבל חצי מהפוסטים הם מכירות אגרסיביות וחצי הם הודעות שירות יבשות, ואז מתפלא שהמודל לא "נשמע כמו המותג". המודל לא רואה מותג. הוא רואה ממוצע מבולגן.
בפרק 1 אמרנו ש-Fine-Tuning טוב לשינוי התנהגות עמיד: tone, format, reasoning pattern, schema compliance. בפרק הזה אנחנו מתרגמים את זה לשאלה יותר חדה: איזו התנהגות צריך להופיע שוב ושוב בדוגמאות, מספיק פעמים, מספיק מגוון, ובלי סתירות? אם המטרה היא מודל קטן שכותב תקצירי שיחות שירות בעברית עסקית, ה-Dataset צריך לכלול שאלות קצרות, שאלות כועסות, שאלות עם שגיאות כתיב, בקשות לא הגיוניות, בקשות שדורשות סירוב, ותשובות בסגנון המדויק שאתה רוצה. לא מספיק להכניס "טקסטים יפים".
גודל ה-Dataset חשוב, אבל איכות חשובה יותר. מתחת לכ-100 דוגמאות, בהרבה משימות המודל יזכור תבניות במקום להכליל, ולעיתים יהיה גרוע מה-base על קלט חדש. סביב 200 דוגמאות איכותיות יש רצפה מעשית לריצה ראשונה. סביב 1,000-2,000 דוגמאות איכותיות יש sweet spot לרוב המשימות הצרות של Vibe Coder: מספיק גיוון כדי שהמודל ילמד pattern, לא כל כך הרבה דאטה שהסינון הופך לפרויקט מחקר.
חשוב גם להבין מה Dataset לא עושה. הוא לא מחליף RAG כשאתה צריך ידע שמתעדכן כל יום. הוא לא קסם שמתקן מודל חלש מדי למשימה. והוא לא דרך להכניס מאגר ענק של עובדות ואז לצפות ל-citations. Dataset טוב מלמד איך לענות, באיזה מבנה לענות, מתי לסרב, באיזה טון להשתמש, ואילו צעדים לבצע. ידע דינמי שייך ל-RAG. הוראות קצרות שייכות ל-prompt. התנהגות חוזרת ששווה לאכוף שייכת ל-Fine-Tuning.
דרך טובה לבדוק אם אתה באמת עובד על Dataset התנהגותי היא לשאול: האם אפשר להסיר את כל שמות הכלים והעובדות ועדיין לראות את הדפוס? למשל, אם כל הדוגמאות שלך עוסקות ב-Google Ads אבל ההתנהגות הרצויה היא "לענות ללקוח בזהירות, עם אבחון לפני המלצה", הדפוס אמור להיות גלוי גם בדוגמה על Meta Ads, Shopify או מערכת CRM. אם הדפוס נעלם ברגע שהחלפת את שם הפלטפורמה, כנראה שה-Dataset שלך הוא אוסף ידע ולא אוסף התנהגות.
עוד בדיקה: האם אפשר לכתוב מבחן קטן שמבדיל בין תשובה טובה לתשובה גרועה? אם לא, המודל לא יוכל ללמוד את ההבדל. "תשובה מקצועית" היא לא מבחן. "תשובה שמתחילה באמפתיה קצרה, לא מבטיחה תוצאה, מבקשת שלושה נתונים, ומסיימת ב-next action" היא מבחן. בפרויקט קוד, "קוד נקי" הוא לא מבחן. "פונקציה שמחזירה object עם keys קבועים, לא כותבת side effects, וכוללת טיפול שגיאות" הוא מבחן.
לכן בפרק הזה נחשוב על Dataset כמו על מערכת בדיקות. כל שורה היא test case שמראה למודל מה עובר ומה לא. ה-seeds הם unit tests להתנהגות המרכזית. ה-synthetic rows הם הרחבת coverage. ה-quality gate הוא CI קטן שמונע מדאטה שבור להיכנס. ה-held-out set הוא מבחן regression. כשחושבים כך, הרבה החלטות נהיות פשוטות: לא מכניסים שורה שלא יודעים למה היא קיימת, לא מאמנים על output שלא היינו מאשרים ללקוח, ולא מגדילים כמות לפני שהבדיקות הירוקות.
עשו עכשיו 3 דקות
בחר/י משימה אחת צרה למודל המאומן וכתוב/י משפט הצלחה אחד שניתן לבדוק. למשל: "המודל יקבל שיחת תמיכה בעברית ויחזיר תקציר ב-5 bullets, כולל next action ו-tone רגוע". אם המשפט כולל יותר ממשימה אחת, צמצם/י אותו עכשיו.
טעות נפוצה: לבנות Dataset סביב נושא במקום סביב התנהגות
הטעות: לאסוף הרבה טקסטים על התחום, למשל "כל הפוסטים של החברה" או "כל תיעוד המוצר", ולחשוב שהמודל ילמד מה לעשות.
למה זה מפתה: זה מרגיש כמו הרבה ידע, וזה קל יותר מאשר לכתוב דוגמאות מדויקות.
מה לעשות במקום: להגדיר פעולה חוזרת: לסכם, לנסח, לסווג, להחזיר JSON, לענות בטון מסוים, או לתקן קוד בסגנון מסוים. כל שורה ב-Dataset צריכה להראות input והתנהגות רצויה, לא רק ידע רקע.
הגדרת משימה וגבולות לפני שאוספים שורה אחת
לפני שכותבים דוגמת JSONL אחת, צריך לענות על ארבע שאלות: מה המודל עושה, למי הוא עוזר, איך נראית תשובה טובה, ומה הוא לא אמור לעשות. זה נשמע בסיסי, אבל זה המקום שבו Dataset טוב נולד. אם אין גבולות, גם Claude כ-Teacher Model ייצור דוגמאות מפוזרות, והמודל הקטן ילמד להיות כללי מדי.
ניקח דוגמה ישראלית פשוטה: סוכנות דיגיטל קטנה רוצה מודל קטן שרץ מקומית וכותב תשובות ראשונות ללקוחות ששואלים על קמפיינים. זו לא משימה של "לדעת שיווק". זו משימה של סגנון, מבנה, וריסון. תשובה טובה צריכה לפתוח באמפתיה קצרה, להסביר את המצב בלי להבטיח תוצאה, לתת next step, ולשמור על עברית טבעית עם מושגים מקצועיים באנגלית כשצריך. תשובה גרועה מבטיחה "נשפר לכם את ה-ROAS ב-30%", מאשימה את הלקוח, או מוסיפה נתונים שלא הופיעו בשאלה.
לכן הגדרת המשימה לא יכולה להיות "מודל לתמיכת לקוחות". היא צריכה להיות: "המודל מקבל הודעת לקוח בעברית על קמפיין Google/Meta ומחזיר טיוטת תשובה קצרה, מקצועית, עם הסבר, פעולה הבאה, וללא הבטחות תוצאה". עכשיו אפשר לבנות דוגמאות. עכשיו אפשר גם לבנות negative examples: שאלות שהמודל לא אמור לענות עליהן, כמו בקשות לייעוץ משפטי, חשיפת נתונים פרטיים, או התחייבות למחיר.
גבולות חשובים במיוחד כשמשתמשים בדאטה אמיתי מחברות ישראליות. צ'אט שירות יכול להכיל שמות, מספרי טלפון, מספרי הזמנה, כתובות, emails, פרטי תשלום או תיאור אירוע אישי. גם אם אתה עובד על מודל פנימי, אל תכניס פרטים מזהים ל-Dataset. החלף אותם ב-placeholders עקביים כמו [שם_לקוח], [מספר_הזמנה], [עיר]. המודל צריך ללמוד את המבנה ואת הטון, לא לשנן פרטים על אנשים.
הגדרת גבולות גם עוזרת ל-Held-Out Set. כבר עכשיו שמור 20 prompts שלא ייכנסו לאימון. הם יהיו מבחן הפרק הבא: האם המודל המאומן באמת השתפר מול ה-base? אל תבחר רק דוגמאות קלות. בחר prompts שמייצגים את המקרים שבהם ה-base נכשל היום: לקוח כועס, שאלה חלקית, דרישה לא סבירה, input עם typo, או בקשה ל-output בפורמט קבוע.
בפרויקטים אמיתיים, גבולות טובים חוסכים גם כסף. אם אתה לא מגדיר מראש שהמודל לא אמור לפתור שאלות משפטיות, Claude ייצור דוגמאות שבהן assistant עונה בביטחון על דברים שהוא לא אמור לענות עליהם. אם אתה לא מגדיר שהמודל לא מבטיח תוצאות שיווקיות, synthetic data יכלול משפטים שנשמעים שיווקיים מדי. כל שורה כזו היא לא רק רעש; היא הוראה למודל לחזור על התנהגות מסוכנת.
כדאי לכתוב מסמך קצר בשם task-card.md לפני ה-Dataset. הוא לא חייב להיות יפה. הוא צריך לכלול חמישה סעיפים: שם המשימה, משתמש היעד, output רצוי, out-of-scope, וסימני כישלון. סימני כישלון הם קריטיים: "ממציא נתונים", "כותב יותר מ-120 מילים", "לא מבקש הבהרה כשחסר מידע", "מבטיח תוצאה", "מערבב עברית ואנגלית בצורה לא טבעית". אחר כך, כשמסננים synthetic data, קל יותר להחליט מה דוחים.
אם יש כמה אנשים בצוות, השתמשו ב-task-card כדי למנוע Dataset עם כמה קולות. מצב קלאסי: מנהל מוצר כותב דוגמאות ענייניות, איש מכירות כותב דוגמאות אגרסיביות, ונציג שירות כותב דוגמאות רכות מאוד. כל אחת יכולה להיות טובה לבד, אבל ביחד הן מלמדות מודל עם אישיות מתפצלת. לפני שמאחדים, קובעים קול אחד. אם צריך כמה קולות, זה כבר שדה מפורש בדאטה או מודלים שונים, לא ערבוב שקט.
למשימה עסקית בעברית, כדאי גם להחליט מראש על רמת הפורמליות. האם המודל אומר "שלום דנה" או "היי דנה"? האם הוא כותב "נבדוק" או "אני אבדוק"? האם הוא משתמש ב"את/ה" או פונה בלשון רבים? הדברים האלה נראים קטנים, אבל Fine-Tuning לומד אותם במהירות. אם חצי מהדוגמאות פונות בלשון יחיד וחצי בלשון רבים, המודל יחליף ביניהן בלי להבין שיש כאן החלטת מותג. לכן task-card צריך לכלול גם voice rules קטנים.
דבר נוסף שכדאי להגדיר הוא רמת יוזמה. יש מודלים שאתה רוצה שיהיו זהירים מאוד ויבקשו הבהרה כשחסר מידע. יש מודלים שאתה רוצה שיציעו פעולה סבירה גם כשיש חוסר חלקי. אין תשובה אוניברסלית. סוכנות שמטפלת בתקציבי פרסום עשויה להעדיף זהירות, כי הבטחה לא מבוססת עולה אמון. כלי פנימי לסיכום notes יכול להיות יוזם יותר. אם לא תגדיר את זה, synthetic data יחליט בשבילך.
עשו עכשיו 4 דקות
כתוב/י שלושה inputs שהמודל חייב לדעת לפתור ושלושה inputs שאינם חלק מהמשימה. ליד כל input מחוץ לגבולות, כתוב/י מה התגובה הרצויה: סירוב, בקשת הבהרה, או הפניה לאדם.
| רכיב Scope | דוגמה טובה | דוגמה חלשה |
|---|---|---|
| פעולה | להחזיר תקציר שיחת שירות ב-5 bullets | להבין שירות לקוחות |
| קהל יעד | נציג תמיכה בחברת SaaS ישראלית | כל עסק |
| Output | JSON עם summary, sentiment, next_action | תשובה טובה |
| גבול | לא לתת ייעוץ משפטי ולא לחשוף מידע אישי | לא לעשות טעויות |
JSONL ופורמטי Instruction: ChatML, ShareGPT ו-Alpaca
JSONL הוא קובץ שבו כל שורה היא JSON עצמאי. לא מערך גדול עם פסיקים בין אובייקטים, לא טבלה, ולא Markdown. שורה אחת שווה דוגמת אימון אחת. זה פורמט נוח כי אפשר לקרוא אותו שורה-שורה, לסנן שורות בעייתיות, לחבר קבצים, ולבדוק תקינות בלי לפתוח את כל הקובץ בזיכרון.
הפורמט המרכזי שנשתמש בו בפרק הוא ChatML-style messages: רשימה של הודעות, וכל הודעה כוללת role ו-content. זה מתאים לרוב משימות השיחה וה-instruction tuning, ו-TRL SFTTrainer יודע לעבוד עם conversational datasets כאלה. בדרך כלל יהיו שלושה תפקידים: system שמגדיר מי המודל ומה הגבולות, user שמייצג את הבקשה, ו-assistant שמייצג את התשובה הרצויה.
{"messages":[{"role":"system","content":"אתה עוזר תמיכה בעברית. ענה בקצרה, בלי הבטחות תוצאה, ותמיד סיים ב-next action."},{"role":"user","content":"הלקוח אומר שהקמפיין לא מביא לידים כבר שבועיים. מה לענות לו?"},{"role":"assistant","content":"מבינה את הדאגה. נבדוק קודם אם הבעיה היא בכמות חשיפות, איכות קהל או טופס נחיתה. אעדכן את הלקוח שאנחנו בודקים את שלושת הנתונים האלה היום, ולא נבטיח שיפור לפני שיש מספרים. Next action: לשלוף spend, CTR, CVR ולידים מהשבועיים האחרונים."}]}
ShareGPT נראה דומה אבל משתמש במפתחות אחרים: conversations, from, value, ובדרך כלל הערכים הם human ו-gpt. אם הדאטה שלך כבר מגיע בפורמט הזה, לא חייבים לזרוק אותו. אבל צריך להחליט: או שמאמנים בנתיב שיודע לקבל ShareGPT, או שממירים ל-role/content לפני אימון. Unsloth מספק פונקציה כמו standardize_sharegpt בדיוק למקרים כאלה.
Alpaca הוא פורמט פשוט יותר: instruction, input, output. הוא טוב למשימות single-turn כמו "סכם את הטקסט", "סווג את הפנייה", או "המר את הנתונים ל-JSON". הוא פחות טבעי למשימות multi-turn שבהן רוצים שהמודל ילמד שיחה, הבהרה, או הקשר מתמשך. אם אתה בונה מודל שנועד לדבר כמו assistant, ChatML או ShareGPT בדרך כלל יהיו בחירה טובה יותר.
החוק החשוב: אל תערבב פורמטים באותו train split. לא חצי ChatML, רבע Alpaca ורבע ShareGPT כי "מצאתי דוגמאות טובות". מבחינתך אלה אולי רק צורות JSON שונות. מבחינת תהליך tokenization, אלה עלולים להפוך לטקסטים שונים לגמרי. תבחר פורמט אחד, תכתוב אליו converter קטן אם צריך, ותבדוק דוגמה אחת אחרי ההמרה.
ל-JSONL יש עוד יתרון קטן אבל חשוב: קל לבנות סביבו הרגלי עבודה. אפשר לשמור קובץ raw, קובץ clean וקובץ rejected בלי לשנות תשתית. אפשר להריץ בדיקת תקינות על כל שורה. אפשר להשתמש ב-head או בעורך טקסט כדי לראות דוגמאות. אפשר לחבר קבצים עם פעולה פשוטה. בפרויקט קטן, הפשטות הזו שווה יותר ממערכת דאטה כבדה מדי.
כאשר כותבים תוכן בעברית, שים לב לקידוד ולמרכאות. שמור את הקובץ ב-UTF-8. אל תשתמש במרכאות חכמות בתוך JSON אם הן נשברות בעורך. אם assistant צריך להחזיר JSON בתוך content, צריך לברוח מרכאות פנימיות או להחליט שהפלט הוא טקסט שמתאר JSON. למתחילים, עדיף להתחיל עם תשובות טקסט רגילות ורק אחר כך להוסיף output מובנה. אם המשימה עצמה דורשת JSON schema, בנה bucket נפרד לדוגמאות כאלה.
עוד החלטה היא האם לכלול system בכל שורה. בהרבה workflows זה כן עדיף, כי כל דוגמה עומדת לבד וברור מה תפקיד המודל. אם הכלי שלך מוסיף system prompt בזמן אימון מבחוץ, אפשר להחזיק אותו בנפרד, אבל אל תערבב בין שתי הגישות בלי להבין. בפרק הזה נשתמש ב-system בכל שורה כדי שה-Dataset יהיה self-contained וקל לבדיקה.
מסגרת החלטה: איזה פורמט Dataset לבחור?
| אם... | בחר/י... | למה |
|---|---|---|
| המודל צריך assistant שיחה או outputs עם system prompt קבוע | ChatML messages | הכי ברור ל-TRL ול-Hugging Face, קל לבדיקה עם role/content. |
| הדאטה כבר הגיע משיחות ShareGPT עם from/value | ShareGPT ואז standardize | לא מאבדים מבנה שיחה, אבל ממירים לפני training אם המודל מצפה ל-ChatML. |
| המשימה היא single-turn פשוטה | Alpaca | instruction/input/output קריא ופשוט, במיוחד לסיווג, סיכום והמרה. |
| יש לך דוגמאות מכמה מקורות | פורמט אחד אחרי conversion | גיוון בתוכן טוב; גיוון בפורמט בלי שליטה מסוכן. |
עשו עכשיו 5 דקות
פתח/י קובץ טקסט בשם seeds-50.jsonl, הדבק/י דוגמה אחת בפורמט messages, ודאג/י שהיא נמצאת בשורה אחת בלבד. אם העורך שלך שובר שורות לתצוגה, זה בסדר; מה שחשוב הוא שאין Enter באמצע האובייקט עצמו.
טעות נפוצה: לערבב פורמטים כי "כולם JSON"
הטעות: להכניס לקובץ אחד גם rows של messages, גם conversations, וגם instruction/output.
למה זה מפתה: כל הדוגמאות נראות קריאות, והרבה tutorials משתמשים בפורמטים שונים בלי להסביר את ההבדל.
מה לעשות במקום: להחזיק קובצי מקור נפרדים, להמיר לפורמט קנוני אחד, ואז לאמן רק על הקובץ המאוחד. אם יש ספק, בחר/י ChatML-style messages לפרויקט הזה.
Chat Template: החוזה בין ה-Dataset ל-Tokenizer
Chat Template הוא השכבה שהרבה מתחילים מדלגים עליה ואז מאשימים את האימון. מודל chat לא באמת מקבל "role" ו-"content" בצורה קסומה. מתחת לממשק, ה-tokenizer הופך את רשימת ההודעות לרצף טקסט עם control tokens: סימונים שמראים איפה user מתחיל, איפה assistant מתחיל, איפה הודעה נגמרת, ואיפה המודל אמור להמשיך. לכל משפחת מודלים יש ציפיות שונות.
ב-Hugging Face יש מתודה בשם apply_chat_template. היא מקבלת messages ומחזירה את הרצף שהמודל באמת רואה. בזמן inference, לפעמים מוסיפים add_generation_prompt=true כדי להגיד למודל "עכשיו תור assistant". בזמן training, ברוב המקרים רוצים add_generation_prompt=false, כי דוגמת האימון כבר כוללת את תשובת assistant. אם תוסיף generation prompt במקום לא נכון, או אם תכניס special tokens כפולים, המודל יכול ללמוד גבולות הודעה עקומים.
הדרך הבטוחה היא לא לנחש. בחר base model, טען את tokenizer, קח דוגמה אחת מה-Dataset, והדפס את הפלט אחרי apply_chat_template. אתה מחפש שלושה דברים: תפקידי user/assistant מופיעים בסימונים שהמודל מצפה להם, אין tokens כפולים כמו שני EOS ברצף, והתשובה הרצויה של assistant נמצאת במקום שבו loss אמור ללמד את המודל. אם הפלט נראה כמו סלט, האימון ילמד סלט.
הטעות השקטה ביותר היא להסתכל רק על ה-JSON ולא על הטקסט שהמודל רואה. JSON יכול להיות מושלם ועדיין להפוך לרצף שגוי. לדוגמה, מודל אחד מצפה לסימונים בסגנון <|im_start|>user, מודל אחר מצפה ל-[INST], ומודל אחר משתמש בתבנית משפחתית משלו. אם לימדת אותו עם סימונים שלא שייכים אליו, הוא עלול ללמוד לכתוב את הסימונים עצמם, לעצור מוקדם מדי, או להמשיך את הודעת user במקום לענות כ-assistant.
בשלב ראשון, אל תנסה להיות חכם עם assistant-only loss, packing, או templates מותאמים אישית. אלה כלים טובים, אבל הם מוסיפים נקודות כשל. עבור Dataset ראשון, המטרה היא לראות דוגמה אחת נכנסת, דוגמה אחת מתפרמטת, ואימון קטן שמוציא תשובה הגיונית. אחרי שזה עובד, אפשר לשפר יעילות. יעילות לפני ודאות היא דרך מעולה לבזבז session של GPU על בעיה שלא תראה עד הסוף.
אם אתה משתמש ב-Unsloth, היתרון הוא שיש helper functions שמטפלים בחלק מה-template mapping. עדיין אל תסמוך על זה בעיניים עצומות. הדפס את CHAT_TEMPLATES, בחר template מתאים למשפחת המודל, ואם הדאטה שלך ShareGPT, המר אותו עם standardize_sharegpt או mapping ברור. ברגע שאתה עובר לפרק 4, ה-Dataset כבר צריך להיות בפורמט האימון הסופי. אל תשאיר "נסדר את זה ב-notebook".
from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("Qwen/Qwen2.5-1.5B-Instruct")
example = {
"messages": [
{"role": "system", "content": "ענה בעברית עסקית קצרה."},
{"role": "user", "content": "סכם את הפנייה של הלקוח."},
{"role": "assistant", "content": "הלקוח מבקש עדכון על סטטוס ההזמנה."}
]
}
formatted = tokenizer.apply_chat_template(
example["messages"],
tokenize=False,
add_generation_prompt=False,
)
print(formatted)
עשו עכשיו 5 דקות
מצא/י את שם ה-base model שלך וכתוב/י לידו את ה-template הצפוי. אם אתה עובד עם Unsloth, בדוק/י האם יש template בשם מתאים ב-CHAT_TEMPLATES. אם אתה עובד ישירות עם Transformers, הדפס/י דוגמה אחת עם apply_chat_template.
טעות נפוצה: Chat Template Mismatch
הטעות: לאמן על ShareGPT כשה-tokenizer מצפה ל-ChatML, או להמיר רק חלק מהשורות.
למה זה מפתה: האימון לא בהכרח ייפול. הוא יכול לרוץ עד הסוף ולהפיק loss שנראה סביר.
מה לעשות במקום: לפני אימון מלא, הדפס/י שלוש דוגמאות אחרי formatting: דוגמה רגילה, edge case, וסירוב. אם הטקסט שהמודל רואה לא ברור לך, אל תתחיל/י אימון.
בניית 50 Seed Examples ידניים
Seed Examples הם דוגמאות שאתה כותב ביד כדי ללמד את ה-Teacher Model מה נחשב טוב. אל תחשוב עליהם כעל "התחלה קטנה". תחשוב עליהם כעל מפרט התנהגות. אם ה-seeds שלך חדים, Claude יכול להרחיב אותם בצורה טובה. אם הם מעורפלים, הוא ייצר הרבה טקסט יפה ולא שימושי.
החלוקה המומלצת ל-50 seeds ראשונים היא לא 50 דוגמאות אקראיות. בנה buckets. לדוגמה עבור מודל brand voice לסוכנות דיגיטל: 10 דוגמאות קלות שבהן הלקוח שואל שאלה ברורה; 15 דוגמאות רגילות עם הקשר חסר; 10 edge cases שבהם יש typo, כעס או דרישה לא סבירה; 5 דוגמאות של סירוב מנומס; 5 דוגמאות שבהן ה-output חייב להיות JSON; ו-5 דוגמאות שמדגימות סגנון מובהק של החברה. זה מכריח אותך ללמד כיסוי, לא רק ממוצע.
כל seed צריך להיות קטן מספיק כדי לבדוק אותו, אבל עשיר מספיק כדי ללמד. Input של "כתוב תשובה ללקוח" לא מלמד. Input שמכיל מצב, אילוץ, tone ו-output רצוי כן מלמד. גם assistant response צריך להיות התשובה שהיית באמת רוצה לקבל מהמודל המאומן. אל תכתוב תשובה חצי טובה בתקווה שהמודל "יבין את הרעיון". Fine-Tuning מחקה דוגמאות. הוא לא מתקן אותך.
בשלב הזה כדאי להחזיק קובץ נוסף בשם seed-notes.md. ליד כל bucket כתוב מה הוא מייצג ולמה הוא קיים. זה יעזור כשנבקש מ-Claude להרחיב. במקום prompt כללי כמו "תייצר עוד 200 דוגמאות", ניתן לו את הכוונה: שמור על אותו system, שמור על אותם גבולות, צור וריאציות של intents, אל תוסיף עובדות שלא הופיעו, וסמן סירובים כשצריך.
אם אתה עובד עם דאטה אמיתי, אל תדביק raw chats כמו שהם. נקה פרטים אישיים לפני ה-seed. בישראל, גם הודעה פשוטה יכולה לכלול שם, טלפון, כתובת, מספר הזמנה או מידע רגיש. השתמש ב-placeholders: [שם_לקוח], [טלפון], [מספר_הזמנה]. אם יש ספק משפטי, אל תכניס את השורה. המודל לא צריך לשנן אף אדם אמיתי.
Seed טוב כולל גם דברים שלא נראים מרשימים. דוגמה שבה user שואל שאלה קצרה מדי וה-assistant מבקש הבהרה היא דוגמה חזקה. דוגמה שבה user כועס וה-assistant לא מתגונן היא דוגמה חזקה. דוגמה שבה חסר נתון וה-assistant מסרב להסיק מסקנה היא דוגמה חזקה. הרבה מתחילים כותבים רק דוגמאות שבהן הכול ברור, ואז המודל נראה טוב בדמו אבל נכשל בשימוש אמיתי.
כדי לשמור על קצב, כתוב seeds במצב טיוטה ואז עבור עליהם בסבב שני. בסבב הראשון המטרה היא coverage: שיהיו מספיק סוגי מקרים. בסבב השני המטרה היא עקביות: אותו tone, אותה רמת פירוט, אותו מבנה תשובה, אותם גבולות. אל תנסה לכתוב מושלם מהשורה הראשונה. מה שכן אסור לעשות הוא לתת ל-Claude לכתוב את ה-seeds הראשונים בלי שאתה מגדיר איכות. אם ה-Teacher מגדיר את הסטנדרט במקומך, אתה כבר לא יודע מה המודל אמור ללמוד.
הקפד לכתוב גם assistant responses שאתה לא מתבייש לפרסם. אם אתה אומר לעצמך "זה רק לאימון, המודל יבין", עצור. זו בדיוק הדוגמה שהמודל יחקה. עדיף 30 seeds מצוינים מ-80 seeds בינוניים. אפשר להשלים כמות בהמשך. קשה יותר למחוק התנהגות גרועה אחרי שהיא נכנסה לאימון הראשון.
כשאתה כותב 50 seeds, אל תעבוד ברצף אחד ארוך בלי review. כתוב 15, עצור, וקרא רק את תשובות assistant ברצף. האם הן נשמעות כמו אותו אדם? האם הן משתמשות באותו מבנה? האם הן שונות מספיק כדי לא להישמע כמו template מכני? אחר כך קרא רק את user prompts. האם יש מגוון אמיתי בקלטים? אם הקריאה הזאת משעממת אותך כי הכול נראה אותו דבר, היא תשעמם גם את המודל במובן הגרוע: היא תלמד אותו לחזור על אותה תבנית.
עבור משימות קוד, seed טוב צריך לכלול גם failure mode. למשל user מבקש "תכתוב פונקציה" אבל לא אומר מה קורה כש-input ריק; assistant הרצוי שואל הבהרה או מוסיף guard ברור. עבור משימות תוכן, seed טוב צריך לכלול בקשה עם טון בעייתי; assistant הרצוי מרכך בלי לשנות משמעות. עבור משימות JSON, seed טוב צריך לכלול בדיוק את schema ולתת output תקין. אל תסמוך על המודל שילמד schema מדוגמה אחת.
תרגיל 1: לבנות 50 seed examples ראשונים 30 דקות
- בחר/י משימה צרה אחת מה-do-now הראשון וכתוב/י לה system message קבוע באורך 2-4 משפטים.
- צור/י טבלה עם שישה buckets: קל, רגיל, edge case, סירוב, output מובנה, וסגנון.
- כתוב/י לפחות 5 דוגמאות בכל bucket, ואז השלם/י ל-50 לפי המקומות שהכי חשובים למשימה שלך.
- העבר/י כל דוגמה לשורת JSONL בפורמט messages. בכל שורה חייבים להיות system, user ו-assistant.
- בדוק/י ידנית 10 שורות אקראיות: האם assistant באמת מייצג את הפלט שרוצים ללמד?
- שמור/י את הקובץ בשם
seeds-50.jsonlואת ההסברים בשםseed-notes.md.
תוצר צפוי: קובץ seeds-50.jsonl תקין, עם 50 שורות, ו-seed-notes.md שמסביר את buckets והגבולות.
עשו עכשיו 5 דקות
כתוב/י ארבעה seeds ראשונים: אחד קל, אחד רגיל, אחד edge case, ואחד שבו המודל צריך לסרב בנימוס או לבקש הבהרה. אל תתקדם/י להרחבה לפני שיש לך לפחות ארבעה סוגים כאלה.
הרחבה ל-Synthetic Data עם Claude כ-Teacher Model
Synthetic Data הוא דאטה שהמודל המורה מייצר עבורך. זה לא אומר דאטה מזויף במובן רע. זה אומר שאתה משתמש במודל חזק כדי ליצור וריאציות של משימות, ניסוחים, inputs וקצוות שלא קל לאסוף ידנית. בפרויקט קטן, זה מה שמאפשר לעבור מ-50 seeds ל-200 דוגמאות תוך שעה, ובהמשך ל-1,000 דוגמאות בלי לכתוב הכול לבד.
אבל יש כאן מלכודת: synthetic data לא משפר את ה-seeds. הוא מגדיל אותם. אם ה-seeds משעממים, הוא ייצור הרבה שעמום. אם ה-seeds כוללים סתירות, הוא ייצור עוד סתירות. אם ה-seeds מלאים באנגלית מלאכותית, הוא יפיץ אותה. לכן ההרחבה הראשונה היא לא ל-1,000. ההרחבה הראשונה היא ל-200, ואז Quality Gate. רק אחרי שה-rejection rate נמוך, עוברים ל-1,000.
ה-prompt ל-Claude צריך להיות קשוח לגבי הפורמט. בקש JSONL בלבד. בקש שכל שורה תהיה אובייקט אחד. הגדר schema. הגדר buckets. בקש גיוון ב-intents, באורך, ברמת כעס, ובחוסר בהירות. בקש לא להוסיף נתונים מספריים או הבטחות שלא מופיעים בקלט. בקש להימנע ממשפטים כמו "כמודל AI" או פתיחות כלליות שחוזרות על עצמן. וכמעט תמיד, בקש batches קטנים: 25-50 דוגמאות בכל פעם. קל יותר לבדוק batch קטן מאשר להציל 800 שורות חלשות.
אתה Teacher Model שמייצר דוגמאות אימון למודל קטן.
המשימה: [תאר/י את המשימה הצרה]
פורמט חובה: JSONL. כל שורה היא אובייקט אחד עם messages.
אסור להחזיר Markdown, הסברים, או מערך JSON.
השתמש ב-system message הזה בדיוק:
[הדבק/י system]
צור 25 דוגמאות חדשות שמבוססות על ה-seeds המצורפים.
דרישות:
1. שמור על אותו סגנון assistant.
2. אל תעתיק seed קיים.
3. כלול 5 edge cases.
4. כלול 3 סירובים מנומסים או בקשות הבהרה.
5. אל תוסיף פרטים אישיים אמיתיים.
6. אל תשתמש בביטויים כמו "כמודל AI".
7. ודא שכל שורה היא JSON תקין בשורה אחת.
Seeds:
[הדבק/י 10-15 seeds מייצגים]
אחרי ש-Claude מחזיר batch, אל תדביק אותו ישר ל-train. קודם שמור אותו כ-synthetic-batch-01.raw.jsonl. אם יש שורה שבורה, אל תתקן ידנית 200 שורות. החזר ל-Claude את השגיאה ובקש regenerate של השורות השבורות בלבד. אם יש בעיית סגנון, אל תמשיך batch נוסף. שפר את prompt ההרחבה ואת ה-seeds. Synthetic pipeline טוב הוא איטרטיבי: seed, generate, inspect, reject, improve, generate again.
כשמבקשים הרחבה, חשוב לבקש מגוון מכוון ולא מגוון אקראי. "תן עוד דוגמאות" יוצר הרבה שורות דומות. "צור 5 דוגמאות עם לקוח כועס, 5 עם שאלה חלקית, 5 עם טעות כתיב, 5 עם בקשה לא לגיטימית, ו-5 עם דרישה ל-output מובנה" מייצר coverage. אפשר גם לבקש מ-Claude להחזיר field פנימי זמני כמו bucket בקובץ עבודה, ואז להסיר אותו לפני training אם trainer לא צריך אותו. כך קל לבדוק שההרחבה באמת מאוזנת.
עוד טריק שימושי הוא batch review. אחרי batch אחד, אל תבקש מיד עוד. בקש מ-Claude עצמו לכתוב ביקורת על ה-batch לפי rubric, אבל אל תקבל את הביקורת כאמת. השתמש בה כדי למצוא תבניות. אם הוא אומר ש-12 שורות דומות מדי, בדוק אותן. אם הוא מפספס artifact ברור, עדכן את rubric. המטרה היא לשפר את מערכת הייצור, לא רק את הקובץ הנוכחי.
בדאטה עברי, בקש במפורש עברית טבעית ולא "עברית מתורגמת". בקש לשמור מושגים מקצועיים באנגלית כשכך מדברים בישראל: ROAS, CTR, JSON, lead, brief, checkout. אל תכריח תרגום של כל מושג. מודל טוב לשוק ישראלי צריך לדעת לערבב עברית ואנגלית כמו אנשי מקצוע אמיתיים, אבל לא לברוח לאנגלית שלמה ללא צורך.
אם אתה מתכנן להגיע ל-1,000 שורות, שמור versioning פשוט: v0-seeds, v1-raw-200, v2-clean-200, v3-clean-1000. כשאימון נכשל, היסטוריית גרסאות תחסוך שעות. בלי גרסאות, קשה לדעת אם הבעיה נכנסה בשלב ה-seeds, בשלב ההרחבה, או בשלב הסינון.
בהרחבה גדולה, השתמש גם ב-negative instructions ל-Teacher. כלומר, אל תכתוב רק מה ליצור; כתוב מה אסור ליצור. אסור להמציא נתוני מחיר, אסור להשתמש בשמות חברות אמיתיות, אסור להבטיח תוצאה, אסור לכתוב "כמומחה", אסור לשנות את system message, אסור להחזיר מערך JSON. Teacher Model חזק עדיין מנסה להיות מועיל, ולפעמים מועילות יתר היא בדיוק מה שהורס Dataset. הגבלות ברורות יוצרות פחות ניקוי אחר כך.
אם batch יוצא טוב, שמור גם את prompt ההרחבה שעבד. הרבה אנשים שומרים רק את הפלט ושוכחים לשמור את המתכון. בפרק 4 או אחרי אימון ראשון, אולי תרצה להוסיף עוד 500 דוגמאות באותו סגנון. בלי prompt שמור, תנסה לשחזר מהזיכרון ותקבל Dataset עם אופי אחר. קובץ קטן בשם teacher-prompts.md עם גרסאות prompt ותוצאות review הוא חלק מהפרויקט.
מסגרת החלטה: האם להרחיב עכשיו ל-1,000 דוגמאות?
- אם אין עדיין 50 seeds ידניים שמכסים buckets שונים, לא מרחיבים. כותבים seeds.
- אם יש 50 seeds אבל אין system message קבוע, לא מרחיבים. מייצבים את התפקיד והגבולות.
- אם 200 הדוגמאות הראשונות מקבלות rejection rate מעל 25%, לא מרחיבים. משפרים prompt ו-seeds.
- אם ה-200 נקיות, מגוונות ותקינות JSONL, מרחיבים ב-batches של 100-200 עד היעד.
תרגיל 2: להרחיב 50 seeds ל-200 דוגמאות עם Claude 25 דקות
- בחר/י 10-15 seeds מייצגים מתוך
seeds-50.jsonl. - הדבק/י אותם לתבנית ה-Teacher Prompt שלמעלה והחלף/י את תיאור המשימה.
- בקש/י batch של 25 דוגמאות בלבד. אל תבקש/י 200 בבת אחת.
- שמור/י את הפלט כ-
synthetic-batch-01.raw.jsonlוהריץ/י בדיקת JSON בסיסית או בדיקה ידנית של 5 שורות. - חזור/י על הפעולה עד שיש בערך 200 דוגמאות raw.
- אחד/י אותן לקובץ
synthetic-200-raw.jsonlבלי למחוק את קבצי batch המקוריים.
תוצר צפוי: קובץ synthetic-200-raw.jsonl עם 200 שורות מועמדות, עדיין לפני סינון איכות.
עשו עכשיו 4 דקות
קח/י seed אחד וכתוב/י שלוש וריאציות שונות של אותו intent. שנה ניסוח, רמת בהירות וטון של user, אבל שמור/י על אותה התנהגות assistant. זה האימון הקטן שלך לפני שנותנים ל-Claude לעשות scale.
טעות נפוצה: להשתמש במודל חלש כ-Teacher
הטעות: לייצר synthetic data עם מודל קטן וזול, ואז לאמן מודל קטן אחר על הפלט שלו.
למה זה מפתה: זה חוסך כסף ונראה "מקומי" או "עצמאי".
מה לעשות במקום: השתמש/י במודל החזק ביותר הזמין לך לייצור הדאטה, למשל Claude או GPT-5, ואז סנן/י. מודל Teacher חלש מעביר לתלמיד את אותן טעויות, רק בכמות גדולה יותר.
סינון איכות: Deduplication, Judge LLM והתפלגות אורכים
Quality Gate הוא השלב שבו אתה מרוויח את כל האיכות. אחרי synthetic expansion, הקובץ נראה מרשים כי יש הרבה שורות. אבל המטרה היא לא הרבה שורות. המטרה היא שורות שמלמדות התנהגות נכונה, מגוונת ויציבה. איכות Dataset נופלת בדרך כלל בארבעה מקומות: JSON לא תקין, כפילויות, שורות עם סגנון Teacher מלאכותי, והתפלגות אורכים מוזרה.
הבדיקה הראשונה היא מכנית: כל שורה JSON תקין, כל שורה כוללת messages, כל messages כולל user ו-assistant, ואין שורות ריקות. אחר כך בודקים כפילויות. לא רק כפילות זהה, אלא near duplicates: אותו input עם מילה אחת שונה, אותה תשובה עם פתיחה זהה, או 30 שורות שמתחילות "בהחלט, הנה...". כפילויות מלמדות את המודל להעדיף ניסוח אחד ולחזור עליו.
הבדיקה השלישית היא התפלגות אורכים. אם כל ה-user prompts הם 12-18 מילים, המודל יתקשה עם שאלה ארוכה. אם כל התשובות הן 120 מילים, הוא ילמד לנפח גם כשהתשובה צריכה להיות קצרה. צור מדידה פשוטה: כמה מילים ב-user, כמה מילים ב-assistant, ומה המינימום/מקסימום. אתה לא צריך סטטיסטיקה מתקדמת. אתה צריך לראות אם יש טווח סביר.
הבדיקה הרביעית היא teacher artifacts. אלה סימנים שהדאטה נשמע כמו Claude ולא כמו המודל שאתה רוצה: פתיחות חוזרות, disclaimer כללי, עברית מתורגמת, משפטים כמו "חשוב לציין", או נטייה להסביר יותר מדי. בדאטה עברי, עוד artifact נפוץ הוא עברית רשמית מדי עם מילים שאף נציג שירות ישראלי לא היה כותב. אם אתה מאמן brand voice, artifacts כאלה הם רעש ישיר.
Judge LLM יכול לעזור, אבל הוא לא מחליף אותך. תן לו rubric ברור: תקינות פורמט, נאמנות למשימה, גיוון, סגנון, בטיחות, והאם השורה מוסיפה משהו חדש. בקש ממנו להחזיר ציון 1-5 וסיבת דחייה קצרה. אל תבקש ממנו "לשפר את הכול" מיד. קודם למד מה נדחה. אם 40% מהדחיות הן בגלל סגנון, הבעיה ב-prompt ההרחבה. אם 40% בגלל גבולות, הבעיה ב-system message. אם 40% בגלל כפילויות, הבעיה בבקשה לגיוון.
לבסוף, ערבב 10-20% דוגמאות כלליות או רחבות יותר באותו סגנון כדי לצמצם catastrophic forgetting. אם המשימה שלך צרה מאוד, למשל "ענה רק על שאלות משלוחים", המודל עלול להיפגע במשימות כלליות כמו ניסוח, הבהרה וסיכום. דוגמאות general mix יכולות להיות בקשות שירות כלליות, סיכום פנייה, בקשת הבהרה, או ניסוח תשובה ללא נתונים חסרים. הן לא צריכות להפוך את המודל לכללי. הן צריכות לשמור אותו לא שביר.
Quality Gate טוב כולל גם בדיקה של סתירות. אם בחלק מהשורות assistant מסיים ב-next action ובחלק לא, האם זו החלטה מודעת? אם בחלק מהשורות הוא משתמש בגוף ראשון רבים ובחלק בגוף ראשון יחיד, האם זה מתאים למותג? אם בשורה אחת הוא מסרב לתת הבטחות ובשורה אחרת מבטיח שיפור, המודל ילמד ששתי ההתנהגויות לגיטימיות. סתירה אחת חוזרת מסוכנת יותר מעשר שגיאות כתיב.
כדאי גם לבדוק coverage מול ה-task-card. סמן בטבלה כמה דוגמאות יש לכל bucket. אם יש 120 דוגמאות רגילות ורק 3 סירובים, המודל לא ילמד לסרב. אם יש 80 edge cases ורק 20 מקרים רגילים, המודל עלול להיות חשדן מדי. Dataset טוב לא חייב להיות מאוזן מתמטית, אבל הוא צריך לשקף את השימוש הצפוי ואת המקומות שבהם base נכשל.
בדיקת מידע אישי צריכה להיות מפורשת. חפש תבניות של טלפון ישראלי, emails, מספרי הזמנה, שמות מלאים, כתובות, שמות חברות לקוח, תעודות זהות או פרטי תשלום. גם אם אתה לא כותב סקריפט מושלם, מעבר ידני עם חיפוש בסיסי עדיף מכלום. שורה אחת עם פרט אישי יכולה להיכנס ל-train, להופיע ב-output לא צפוי, ולהפוך ניסוי פנימי לסיכון אמיתי.
כשאתה יוצר quality-report.md, אל תכתוב רק "עבר". כתוב החלטה. למשל: "מתוך 220 שורות raw, נדחו 37: 18 כפילויות, 9 teacher artifacts, 6 format errors, 4 wrong task. נשארו 183, נוספו 25 general mix, סה"כ 208 clean. החלטה: מספיק לריצת SFT ראשונה, לא מספיק להרחבה ל-1,000 עד שנטפל ב-artifacts". דו"ח כזה הופך אותך למפעיל מערכת, לא לקורא תחושות.
ה-Quality Gate צריך להסתכל גם על outputs קצרים מדי. בדאטה synthetic יש נטייה לייצר תשובות "יפות" אבל ריקות: משפט פתיחה, משפט כללי, וסיום. תשובה כזו יכולה לקבל ציון גבוה אם ה-rubric כללי מדי, אבל היא לא מלמדת פעולה. לכן אחד המדדים צריך להיות "האם assistant מוסיף החלטה, צעד או מבנה שהמודל צריך ללמוד?". אם התשובה רק נשמעת נעימה, דחה או שכתב.
מצד שני, Outputs ארוכים מדי הם בעיה הפוכה. אם כל assistant response הוא מדריך קטן, המודל ילמד להאריך גם כשצריך לענות בשתי שורות. עבור משימת תמיכה, כדאי להחזיק טווחי אורך: תשובה קצרה, תשובה בינונית, תשובה עם הסבר. עבור משימת JSON, האורך פחות חשוב מהשלמות. עבור משימת קוד, אולי צריך comments רק כשיש מורכבות. המדידה לא צריכה להיות מושלמת, רק מודעת.
בדיקה אחרונה לפני split היא בדיקת "האם הייתי משלם על זה?". קח 10 דוגמאות אקראיות והסתכל על assistant בלבד, בלי user. האם זה נראה כמו output שמוצר אמיתי היה גאה להציג? אחר כך הסתכל על user בלבד. האם הבקשות נשמעות כמו משתמשים אמיתיים? אם אחת מהתשובות היא לא, הדאטה עדיין לא מוכן. מודל קטן לא יעלה מעל התקרה של הדוגמאות שלו.
מסגרת החלטה: Quality Gate לפני אימון
| בדיקה | אם נכשל | אם עובר |
|---|---|---|
| JSONL תקין | עוצרים ומתקנים או מחדשים batch | עוברים לבדוק שדות חובה |
| פורמט אחיד | ממירים לפורמט אחד | מדפיסים דוגמה אחרי chat template |
| כפילויות | מסירים near duplicates ומבקשים גיוון חדש | בודקים התפלגות אורכים |
| Teacher artifacts | דוחים שורות ומעדכנים prompt | מכניסים ל-clean set |
| Rejection rate | מעל 25%: לא מרחיבים | מתחת 15%: אפשר להגדיל batch |
תרגיל 3: להריץ Quality Gate ידני ו-Judge LLM 25 דקות
- פתח/י את
synthetic-200-raw.jsonlודגום/י 30 שורות: 10 מההתחלה, 10 מהאמצע, 10 מהסוף. - דרג/י כל שורה 1-5 לפי ארבעה מדדים: תקינות, נאמנות למשימה, גיוון, וסגנון.
- סמן/י שורות עם פתיחות חוזרות, disclaimers, עברית מוזרה, או output שלא מתאים ל-system.
- העבר/י batch של 20 שורות ל-Judge Prompt ובקש/י ציון וסיבת דחייה לכל שורה.
- צור/י
rejected-rows.mdעם שלוש קטגוריות דחייה: duplicate, artifact, wrong_task. - שמור/י רק שורות שעברו ל-
clean-200.jsonl, וכתוב/יquality-report.mdעם מספר שורות, rejection rate והחלטה אם להרחיב.
תוצר צפוי: clean-200.jsonl נקי מספיק לריצת אימון ראשונה, ו-quality-report.md שמסביר למה השורות שנשארו ראויות לאימון.
עשו עכשיו 5 דקות
בחר/י חמש שורות synthetic ודרג/י אותן בעצמך. אם אינך מצליח/ה להסביר למה שורה קיבלה 5/5, היא לא באמת שורה חזקה. כתוב/י סיבת דחייה אחת גם לשורה שנראית "כמעט בסדר".
Dataset ל-DPO: Chosen ו-Rejected בזול
בפרק 5 נלמד Preference Tuning, ובעיקר DPO/ORPO. לא נאמן DPO עכשיו, אבל נכין starter dataset קטן. הרעיון פשוט: עבור אותו prompt, יש תשובה טובה (chosen) ותשובה חלשה (rejected). המודל לומד להעדיף את chosen. זה מתאים במיוחד כש-SFT כבר לימד את המודל את המשימה, אבל הוא עדיין בוחר לפעמים תשובה פחות טובה: יותר מדי ארוכה, פחות בטוחה, פחות בסגנון, או פחות מדויקת.
אפשר לבנות זוגות DPO בזול מתוך הדחיות שכבר מצאת. אם יש לך שורה שנדחתה בגלל teacher artifact, שמור אותה כ-rejected וכתוב גרסה טובה כ-chosen. אם יש תשובת לקוח שמבטיחה יותר מדי, שמור אותה כ-rejected וכתוב תשובה זהירה כ-chosen. אם יש JSON לא עקבי, שמור אותו כ-rejected וכתוב JSON תקין כ-chosen. כך הדחיות לא מתבזבזות; הן הופכות לדוגמאות העדפה.
פורמט DPO משתנה בין כלים, אבל המבנה הלוגי קבוע: prompt, chosen, rejected. גם כאן צריך לשמור על אותו Chat Template. אל תכתוב chosen בפורמט אחד ו-rejected בפורמט אחר. ואל תיצור rejected גרוע בצורה ילדותית מדי. אם rejected הוא שטויות מוחלטות, המודל לא לומד tradeoff אמיתי. rejected טוב הוא תשובה שנראית סבירה במבט ראשון אבל נכשלת בקריטריון חשוב.
בדוגמה של סוכנות דיגיטל, rejected איכותי יכול להיות תשובה שמנוסחת יפה אבל מבטיחה תוצאה. בתחום קוד, rejected איכותי יכול להיות פתרון שעובד למקרה הפשוט אבל מתעלם משגיאה. בתחום brand voice, rejected יכול להיות מדויק עובדתית אבל לא בסגנון המותג. כך המודל לומד את ההעדפה העדינה, לא רק להבדיל בין טוב לגרוע בצורה ברורה מדי.
אל תכניס יותר מדי DPO לפרק הזה. המטרה היא להכין חומר לפרק 5, לא לפתוח עוד חזית. 5-10 זוגות מצוינים מספיקים כ-starter. אם בזמן Quality Gate מצאת דחיות מעניינות, שמור אותן. אם לא, צור כמה ידנית. העיקר שתגיע לפרק 5 עם הבנה של המבנה ועם דוגמאות שמייצגות tradeoffs אמיתיים.
{"prompt":[{"role":"system","content":"ענה בעברית עסקית קצרה וללא הבטחות תוצאה."},{"role":"user","content":"לקוח כועס שאין לידים כבר שבוע. כתוב תשובה ראשונית."}],"chosen":[{"role":"assistant","content":"מבינה את התסכול. נבדוק היום האם הירידה מגיעה מחשיפות, הקלקות או המרה בדף הנחיתה, ונעדכן עם מספרים לפני שנמליץ על שינוי תקציב. Next action: לשלוף spend, CTR, CVR ולידים לשבעת הימים האחרונים."}],"rejected":[{"role":"assistant","content":"אין מה לדאוג, נשפר את הלידים ב-30% בשבוע הבא ונעלה תקציב כדי לפתור את זה."}]}
עשו עכשיו 5 דקות
כתוב/י prompt אחד ושתי תשובות: chosen שמייצגת את הסגנון הרצוי, ו-rejected שנראית סבירה אבל נכשלת בקריטריון אחד. ליד ה-rejected כתוב/י למה היא נכשלת: הבטחה מוגזמת, פורמט שגוי, tone לא מתאים, או מידע מומצא.
אריזה סופית: Train, Validation ו-Held-Out לפרק הבא
בסוף הפרק אתה צריך להחזיק תיקייה נקייה, לא אוסף קבצים שהצטברו. המבנה המומלץ הוא פשוט: raw/ לכל מקורות ודאטה לא מסונן, work/ לקבצי ביניים, ו-final/ לקבצים שנכנסים לפרק 4. בתוך final/ יהיו train.jsonl, validation.jsonl, heldout.jsonl, dpo-pairs.jsonl ו-quality-report.md.
הפיצול הראשון יכול להיות 80/10/10: 80% train, 10% validation, 10% held-out. validation משמש במהלך האימון או אחרי epoch כדי לראות אם המודל מתחיל overfit. held-out הוא מבחן נקי שלא נוגעים בו עד ההשוואה מול ה-base. אם יש לך רק 200 דוגמאות, 20 held-out זה מספיק להתחלה. אל תכניס את ה-held-out ל-Claude להרחבה, אל תתקן אותו לפי output של המודל המאומן, ואל תבחר אותו רק מתוך הדוגמאות הקלות.
כדאי גם לשמור metadata קטן. לא בתוך כל שורה אם זה מסבך את trainer, אלא בדו"ח נפרד: כמה שורות, אילו buckets, אחוז דחייה, תאריך יצירה, שם teacher model, פורמט, template, האם יש general mix, וכמה דוגמאות DPO. הדו"ח הזה יעזור כשמשהו ייכשל בפרק 4. אם המודל מוציא output ארוך מדי, נחזור לדו"ח ונראה אם כל assistant examples היו ארוכים. אם המודל מתבלבל ב-JSON, נראה האם היו מספיק examples עם output מובנה.
לפני שאתה מכריז שה-Dataset מוכן, הרץ בדיקת smoke ידנית: פתח שלוש שורות מ-train, שתי שורות מ-validation, שתי שורות מ-held-out, ודוגמת DPO אחת. האם כולן באותו פורמט? האם כולן בלי מידע אישי? האם assistant נראה כמו output שאתה באמת רוצה? האם אין Markdown מסביב ל-JSON? אם כן, הפרק עשה את שלו. עכשיו יש לך לא רק קובץ לאימון, אלא מערכת קטנה לייצור דאטה איכותי.
עוד דבר קטן: שמור את הפקודה או notebook cell שבו השתמשת לפיצול. גם אם הפיצול נעשה ידנית, כתוב מה עשית. בפרק 4, אם התוצאות יהיו טובות, תרצה לשחזר. אם הן יהיו גרועות, תרצה להבין. Reproducibility בפרויקט קטן לא דורשת MLOps מלא. היא דורשת שמות קבצים ברורים, תאריך, seed אקראי אם היה, והסבר קצר.
אם אתה עובד לבד, עצור כאן ל-review של 10 דקות לפני אימון. אם יש אדם נוסף בצוות, תן לו לקרוא רק 20 שורות: 10 train, 5 held-out, 5 rejected. אל תסביר לו מה הוא אמור לראות. אם הוא מזהה את המשימה, הטון והגבולות, ה-Dataset ברור. אם הוא שואל "מה המודל אמור לעשות פה?", גם המודל ישאל את אותה שאלה בדרך היחידה שהוא יודע: output לא עקבי.
ההישג של הפרק אינו קובץ גדול. ההישג הוא שאתה יודע למה כל קובץ קיים. train.jsonl מלמד. validation.jsonl בודק בזמן עבודה. heldout.jsonl מוכיח שיפור. dpo-pairs.jsonl שומר העדפות לעתיד. quality-report.md מסביר את ההחלטות. זה בדיוק ההבדל בין ניסוי מזל לבין pipeline קטן שאפשר לסמוך עליו.
לפני מעבר לפרק 4, מומלץ לעשות גם "dry run" בלי אימון: קח שלושה prompts מה-held-out, כתוב בעצמך את התשובה האידיאלית, ואז השווה אותה לשלוש דוגמאות דומות ב-train. אם אין ב-train שום דבר שמלמד את ההתנהגות שאתה מצפה לראות ב-held-out, המבחן לא הוגן או ה-Dataset חסר. המטרה היא לא להכניס את ה-held-out לאימון, אלא לוודא שהאימון מקבל מספיק דפוסים כדי להצליח על מקרים דומים. זו בדיקה קטנה שמונעת אכזבה גדולה: הרבה פעמים אנשים בונים held-out קשה מאוד, אבל ה-train לא כולל אף דוגמה שמלמדת את הכלל הרלוונטי. מצד שני, אם held-out כמעט זהה ל-train, אין הוכחת הכללה. אתה מחפש מרחק בריא: דומה בהתנהגות, שונה בניסוח.
אם יש לך ספק בין עוד 100 שורות לבין עוד שעה של review, בחר/י ב-review. בפרויקטים הראשונים, שורת אימון חלשה עולה יותר משורה חסרה. תמיד אפשר להרחיב Dataset נקי; קשה יותר לבנות אמון במודל שכבר למד הרגלים עקומים.
הכלל המעשי: אל תשלח/י לאימון שורה שלא היית מוכן/ה להראות כדוגמת זהב למישהו שלומד את העבודה שלך. אם הדוגמה מביכה, עמומה או חצי נכונה, היא לא חומר אימון; היא חומר דחייה. זה מבחן קטן, אבל הוא מציל אימונים.
בפרק 4, כשנריץ אימון, כל בעיה שנראה תתורגם חזרה לשאלה על ה-Dataset. אם loss יורד אבל הפלט נשאר כללי, נבדוק האם הדוגמאות מספיק מובחנות. אם המודל מוציא פורמט לא יציב, נבדוק דוגמאות JSON ו-chat template. אם הוא נשמע כמו Claude, נבדוק teacher artifacts. אם הוא שוכח יכולות כלליות, נבדוק general mix. העבודה שעשית כאן היא מערכת האבחון של האימון, לא רק הכנה טכנית.
זו גם הסיבה שלא כדאי למחוק את קבצי הביניים. raw batches, rejected rows ו-quality report הם ראיות. כשאימון לא מצליח, קל להתפתות לשנות learning rate או rank. לפעמים זה נכון, אבל ברוב הפרויקטים הראשונים הבעיה נמצאת בשורה שהיית צריך לדחות. אם אתה שומר היסטוריה, תוכל לבדוק. אם מחקת הכול והשארת רק train.jsonl, אתה מנחש.
תרגיל 4: להכין train/validation/held-out לפרק 4 30 דקות
- ערבב/י את
clean-200.jsonlאחרי הסינון. אל תשאיר/י את הסדר לפי batch של Claude. - פצל/י בערך 80% ל-
train.jsonl, 10% ל-validation.jsonl, ו-10% ל-heldout.jsonl. - ודא/י שה-held-out כולל לפחות 20 prompts שמייצגים edge cases ולא רק שאלות קלות.
- שמור/י 10 זוגות DPO ראשונים ב-
dpo-pairs.jsonl. אם אין 10, צור/י לפחות 5 איכותיים. - כתוב/י
quality-report.mdעם: מספר שורות, פורמט, chat template, rejection rate, buckets, general mix, וסיכונים שנותרו. - פתח/י 7 שורות אקראיות מכל הקבצים הסופיים ובדוק/י שאין מידע אישי ואין פורמטים מעורבבים.
תוצר צפוי: תיקיית final עם train.jsonl, validation.jsonl, heldout.jsonl, dpo-pairs.jsonl ו-quality-report.md.
עשו עכשיו 3 דקות
צור/י שלושה שמות קבצים סופיים וכתוב/י ליד כל אחד מה נכנס אליו: train.jsonl, validation.jsonl, heldout.jsonl. אם אינך יודע/ת להסביר את ההבדל ביניהם, אל תתחיל/י אימון בפרק 4.
שגרת עבודה אחרי הפרק
| תדירות | פעולה | למה זה חשוב |
|---|---|---|
| יומי | להוסיף 3-5 דוגמאות אמיתיות חדשות ל-raw backlog, אחרי אנונימיזציה. | ה-Dataset נשאר מחובר למציאות ולא רק ל-seeds הראשונים. |
| יומי | לסמן שורה אחת שבה ה-base נכשל ולשמור אותה כמועמדת held-out. | מבחן ההשוואה בפרק 4 יהיה חד ולא קל מדי. |
| שבועי | להריץ deduplication ולמחוק near duplicates. | מונע מהמודל ללמוד חזרתיות. |
| שבועי | לבדוק 20 שורות synthetic מול rubric איכות. | תופס teacher artifacts לפני שהם גדלים. |
| שבועי | לעדכן את quality-report.md עם row counts ו-rejection rate. | שומר היסטוריה כשמשווים ריצות אימון. |
| שבועי | להוסיף 5 דוגמאות general mix אם המשימה נהיית צרה מדי. | מצמצם catastrophic forgetting. |
| חודשי | לעבור על system message ולוודא שהוא עדיין משקף את המוצר והגבולות. | מותג, מדיניות ותהליכים משתנים. |
| חודשי | לרענן held-out set עם inputs חדשים שהמודל עדיין מתקשה בהם. | מונע מבחן שמפסיק לאתגר את המודל. |
| חודשי | לבדוק מחדש את chat template מול גרסת tokenizer חדשה אם החלפת base model. | מונע template mismatch שקט. |
אם אתם עושים רק דבר אחד מהפרק הזה 5 דקות
כתבו 10 seed examples ידניים בפורמט ChatML-style messages, ואז הדפיסו דוגמה אחת אחרי apply_chat_template. אם השלב הזה לא ברור או לא נקי, אל תעברו ל-synthetic data ואל תתחילו אימון.
בדקו את עצמכם
- למה Dataset שמוגדר סביב "נושא" חלש יותר מ-Dataset שמוגדר סביב "התנהגות"? (רמז: מה המודל באמת מחקה?)
- איך Chat Template mismatch יכול לגרום למודל להוציא פלט מקושקש גם אם האימון הסתיים בלי שגיאה? (רמז: control tokens)
- למה לא כדאי להרחיב מיד מ-50 seeds ל-1,000 דוגמאות? (רמז: rejection rate)
- איך Judge LLM עוזר בסינון איכות, ולמה הוא לא מחליף בדיקה אנושית? (רמז: rubric מול טעם מקצועי)
- למה Held-Out Set חייב להישאר מחוץ לאימון ולתהליך השיפור? (רמז: השוואה הוגנת מול base)
סיכום הפרק
הנקודה המרכזית בפרק היא ש-Fine-Tuning לא מתחיל ב-GPU; הוא מתחיל בדוגמאות. Dataset טוב מגדיר התנהגות מדויקת, שומר על פורמט אחד, מכבד את chat template של המודל, ומתרחב בזהירות מ-seeds ידניים ל-synthetic data מסונן. אם עשית את העבודה כאן, פרק 4 יהיה הרבה פחות מפחיד: במקום לחפש למה האימון נכשל, תגיע/י עם קבצים נקיים, דו"ח איכות, ו-held-out set שמאפשר למדוד שיפור אמיתי. בפרק הבא נעבור ל-Unsloth על Colab ו-Kaggle ונשתמש ב-Dataset הזה כדי לאמן מודל שרץ ב-Ollama.
Checklist לפני שעוברים לפרק 4
- ☐ הגדרתי משימה צרה אחת למודל המאומן.
- ☐ כתבתי system message קבוע שמגדיר תפקיד, טון וגבולות.
- ☐ בחרתי פורמט Dataset אחד: ChatML, ShareGPT שעבר standardize, או Alpaca.
- ☐ יצרתי לפחות 50 seed examples ידניים.
- ☐ בדקתי שכל seed הוא JSONL תקין בשורה אחת.
- ☐ הדפסתי דוגמה אחת אחרי
apply_chat_template. - ☐ הרחבתי ל-200 דוגמאות synthetic בבאצ'ים קטנים.
- ☐ סיננתי כפילויות ו-teacher artifacts.
- ☐ יצרתי
clean-200.jsonlאחרי Quality Gate. - ☐ הוספתי 10-20% general mix אם המשימה צרה מאוד.
- ☐ פיצלתי ל-
train.jsonl,validation.jsonlו-heldout.jsonl. - ☐ שמרתי לפחות 20 held-out prompts שלא נכנסים לאימון.
- ☐ יצרתי לפחות 5-10 זוגות
dpo-pairs.jsonl. - ☐ כתבתי
quality-report.mdעם row counts, rejection rate, פורמט ו-template. - ☐ הסרתי פרטים אישיים מכל דוגמה שמגיעה מדאטה אמיתי.