[רטרוספקטיבה של אופטימיזציה] מה מודול התרגום של YouDub עשה כל היום, אילו כיוונים יעילים, ואילו כבר הופרכו
שדה הקרב העיקרי של היום הזה בעצם לא היה TTS, אלא מודול התרגום.
הסיבה ישירה: את מודול הדיבוב בהמשך כבר אפשר להחליף לפתרון שקרוב יותר לטימבר היעד, אבל במוצר המוגמר החולשה הבולטת ביותר עדיין היא שתרגום הכתוביות עצמו לא מספיק דומה לתוצר בוגר של B站, במיוחד:
- בסצנות rolling caption, המשפט הקודם “אוכל” מידע מהמשפט הבא.
- הכתוביות המקוריות באנגלית מקוטעות, וברגע שמתרגמים אותן משפט-משפט באופן קשיח לסינית, זה נהיה ארוך ומסורבל.
- אם רודפים יותר מדי אחרי “להשלים משפטים”, חושפים מראש את ה-punchline, שמות עצם ופעולות שמגיעים אחר כך.
- אם רודפים יותר מדי אחרי “לשמור גבולות פרגמנטים”, הסינית נהיית מקוטעת מדי, ולא נראית כמו כתוביות של מוצר מוגמר אמיתי.
לכן בסבב האופטימיזציה הזה העברתי את הפוקוס מ“לתקן סרטון אחד” ל“לבנות סט מסגרת הערכה ואיטרציה לתרגום שניתנת להכללה”.
מה בדיוק נעשה היום
1. קודם בניתי מערכת בסיס (baseline), והפסקתי לכוון לפי תחושה
לא המשכתי לנעוץ מבט בסרטון יחיד ולתקן שורה-שורה, אלא לקחתי ישירות את תשעים ומשהו דגימות ההשוואה B站/Youtube מתוך C:\\Users\\1\\bili_yt_export\\bili_youtube_first100.csv כדי לבנות baseline.
בהתאם עשיתי שני דברים:
- הרחבתי את
scripts/benchmark_translation.py - הוספתי את
scripts/analyze_translation_artifact.py
הראשון אחראי להריץ בכמות את כל השרשרת: תרגום + חיתוך משפטים + שכתוב טקסט לדיבוב, ולהוציא לכל case מדדים ותוצרים ביניים.
השני אחראי לפרק כל case ולבחון אותו, במיוחד בשכבות האלו:
source_rowsprepared_source_rowstranslated_rows_pre_splitpredicted_rowsreference_rows
השלב הזה קריטי מאוד, כי בהמשך התברר שהרבה בעיות לא נגרמות מפוסט-פרוססינג, אלא כבר בשלב translated_rows_pre_split ה-LLM “שאל” תוכן מהשורות הבאות.
2. זיהוי חד של הבעיה המרכזית: rolling-caption “שואל” מההמשך
ההישג הכי גדול של היום לא היה שמדד כלשהו פתאום זינק, אלא שהצלחתי לאתר במדויק את הבעיה העיקרית:
בכתוביות האוטומטיות/הרשמיות של YouTube יש הרבה מבנה rolling-caption; הרבה שורות הן במקור חצי משפט, משפט קטוע, או משפט שממשיך על פני שורות.
אם פשוט אומרים למודל “תרגם טבעי שורה-שורה”, הוא נוטה מאוד לשאול מידע משורה-שתיים קדימה לתוך השורה הנוכחית, כך שהסינית נראית זורמת יותר—אבל בציר הזמן זו הדלפה מוקדמת.
הבעיה הזו בולטת במיוחד בשני ה-hard case: zwIqbrD6JX4 ו-o2V-JJpJH_I.
3. קידמתי את fragment_guard להיות ברירת המחדל הראשית
על בסיס הממצא הזה, הפכתי את fragment_guard לברירת מחדל שמופעלת.
הרעיון המרכזי שלו אינו “להכריח את הסינית להיות קטועה”, אלא להגדיר למודל מגבלה מפורשת:
- ה-id הנוכחי יכול לבטא רק את הסמנטיקה שכבר הופיעה בשורת המקור הנוכחית.
- אם שורת המקור היא בבירור פרגמנט לא-מושלם של rolling-caption, עדיף שהסינית תהיה מעט פתוחה, מאשר להשלים מראש תוכן עתידי.
זה כרגע השינוי היחיד שהוכח באופן יציב כיעיל, ושאני מוכן להכניס כברירת מחדל ב-mainline.
תוצאות שכבר מאושרות כיעילות
תצורת ה-mainline
ה-mainline היציב הנוכחי הוא בערך:
- provider:
openai_context - api base:
http://192.168.10.88:8317/v1 - model:
gpt-5.4-mini - prompt profile:
auto_hybrid - temperature:
0 - fewshot:
8 fragment_guard=on- שאר דגלי הניסוי כבויים כברירת מחדל
נתונים עם שיפור מאושר
fragment_guard מנסיונות מקומיים ועד מדגם בינוני—הוא תמיד רווח חיובי:
- השוואת 4 case:
52.432 -> 53.322 - השוואת 8 case:
55.958 -> 56.058
דוח ה-mainline הנוכחי ל-8 case:
- composite:
56.058 - chrF:
0.3707 - char F1:
0.7729 - density MAE ratio:
0.4272
זה מצביע שלפחות ה-mainline הנוכחי פחות נוטה “לשאול” מההמשך, והקצב הכללי גם קרוב יותר להתפלגות של כתוביות מוצר מוגמר של B站.
אילו כיוונים היום כבר הופרכו אצלי כמעט לחלוטין
1. הפעלה גלובלית של fragment_hints
זה לא חסר תועלת; להפך, בחלק מה-case זה חזק מאוד.
למשל:
zwIqbrD6JX4ב-hard2 עלה מ-54.439ל-57.980VT6rLcVKhzgגם השתפר באופן ברור
אבל הבעיה היא שזה לא יציב.
כשמריצים על 8 case, זה דווקא יורד מ-56.058 ל-55.296 בכללי.
כלומר, זה יותר כמו “תרופה חזקה למבנה ספציפי”, ולא אסטרטגיית mainline שאפשר להדליק כברירת מחדל כרגע.
2. auto_hybrid_v2
עשיתי גרסה אגרסיבית יותר של לוגיקת בחירת profile אוטומטית, כדי שסרטונים שונים יחליפו אוטומטית בין literal_context / bilibili_dub / bilibili_pacing.
התוצאה: ב-8 case זה ירד ישר ל-54.375, פחות מה-mainline הנוכחי 56.058.
המסקנה פשוטה: לוגיקת ה-gating עדיין לא מדויקת מספיק; בשלב הזה זה לא יכול לעלות ל-mainline.
3. להגדיל בכוח את טווח התרגום של full-context
ניסיתי שני כיוונים:
- להעלות את סף ה-full-context כדי שיותר סרטונים יתורגמו “במכה אחת” לכל הטקסט
- להגדיל את chunk מחלון קטן לחלון גדול
זה נראה כאילו זה קרוב יותר ל“להבין את כל המאמר ואז לתרגם”, אבל בפועל זה לא נתן רווח יציב.
הסיבה היא שככל שיש יותר הקשר, המודל גם יותר נוטה לשאול תוכן בין id-ים, וציר הזמן עלול להיות עוד יותר מבולגן.
4. להקטין מאוד את גרעיניות ה-chunk
למשל רעיונות כמו chunk_max_items=2: אינטואיטיבית זה נראה כמו להפחית “הדבקה” בין משפטים, אבל בפועל הרווח גרוע, והמהירות גם יורדת משמעותית.
תוצאות hard2 לא הביאו שיפור איכות, אבל ההשהיה קפצה הרבה—במיוחד o2V-JJpJH_I הופך איטי מאוד.
5. פשוט “למקסם” את “פרומפט בסגנון B站”
בדקתי:
literal_contextbilibili_dubbilibili_pacingauto_hybrid
ב-mixed4, auto_hybrid הוא הטוב ביותר, literal_context שני, ושני ה-profile ה“חזקי סגנון” דווקא גרועים יותר.
זה מראה שכרגע זה לא “ככל שהפרומפט דומה יותר ל-B站 כך טוב יותר”, אלא קודם צריך לפתור גבולות הקשר, משפטים מקוטעים, ויישור timing—ואז לדבר על ניסוח סגנוני.
שינוי התובנה החשוב ביותר היום
חשבתי במקור שהבעיה הגדולה היא “המשפטים לא מתורגמים מספיק טבעי”, ואז הבנתי שלא.
הבעיה היסודית יותר היא בעצם:
- הקלט המוקדם (prior) עצמו מקוטע
- ובין הפרגמנטים יש חפיפה גבוהה
- כדי שהסינית תהיה טבעית, חייבים להשלים במידה מסוימת טון ומבנה
- אבל ברגע שמשלימים יותר מדי, חושפים מראש תוכן עתידי
לכן החלק הקשה באמת במודול התרגום אינו “תרגום” עצמו, אלא:
בתנאי של לא לחצות את גבולות הזמן, לסדר אנגלית מקוטעת לקצב סיני שנראה כמו כתוביות של מוצר מוגמר אמיתי.
זה לא אותו בעיה כמו תרגום מכונה רגיל.
נקודות שעדיין לא נפתרו
אף שה-mainline יציב יותר מקודם, הוא עדיין רחוק מאוד מהיעד שאני רוצה—ובמיוחד עדיין לא מגיע לרמת הגימור של דוגמת B站 שנתת.
נקודות שעדיין בבירור לא נפתרו:
- בחלק מה-hard case עדיין “שואלים” מההמשך
- בחלק מה-case הסינית עדיין נשמעת “תרגומית” (translationese)
- התאמת האורכים אחרי חיתוך משפטים עדיין לא יציבה מספיק
fragment_hintsעדיין לא מצא תנאי gating יציבים- הכמות והבחירה של few-shot עדיין לא כוונו לאופטימום מלא
מה בעיניי הכי שווה להמשיך ממנו הלאה
כרגע הכי שווה לרדוף לא “להוסיף עוד prompt מיסטי”, אלא שלושה דברים:
1. לעשות gating תכונתי ל-fragment_hints, ולא מתג גלובלי
כבר ידוע שהוא חזק מאוד בחלק מה-case.
השלב הבא צריך להיות gating לפי התכונות האלו:
- fragmentary source ratio
- overlap ratio
- punctuated source ratio
- short/tiny line ratio
כלומר להפעיל רק בסרטונים “בפירוק גבוה/rolling-caption גבוה”, ולא לחתוך בגרזן לכלל המקרים.
2. להמשיך לאמת את מספר ה-few-shot
אות קטן שנראה בסוף היום: fewshot=4 ב-hard2 הראה לראשונה תוספת נטו קטנה:
- baseline hard2:
50.454 - fewshot=4 hard2:
50.600
הרווח קטן, אבל הכיוון חיובי.
אם mixed4 ו-mid8 גם ישמרו—זה אומר ש-8 few-shot אולי דווקא מוסיפים יותר מדי רעש.
3. להמשיך לעשות chunk context של “נותנים הקשר בלבד, בלי הרשאת תרגום”
כבר הוספתי גרסה של חלון הקשר לפני/אחרי ה-chunk, אבל כרגע זה עדיין במצב ניסוי.
הערך של הכיוון הזה הוא:
- לתת למודל יכולת להבין מקטע שלם
- ובמקביל עדיין לדרוש ממנו להוציא רק את ה-id של ה-chunk היעד
זה, תיאורטית, מתאים יותר מלהגדיל סתם chunk—לתרגום של “להבין את הכל אבל לא לחצות גבולות”.
מסקנת היום
אם מסכמים במשפט אחד:
ההישג הגדול של היום לא היה “לסיים את מודול התרגום”, אלא לברר בבירור “למה הבעיה קשה, איפה צוואר הבקבוק הראשי עכשיו, אילו כיוונים יעילים, ואילו כבר לא שווה להמשיך לבזבז עליהם זמן”.
עכשיו לפחות ברור:
- בעיית התרגום בפרויקט הזה, במהותה, אינה MT רגיל
- גבולות rolling-caption הם הסתירה המרכזית
fragment_guardהוא כרגע הרווח החיובי היציב היחיד- ל-
fragment_hintsיש פוטנציאל, אבל חייבים gating - few-shot ואסטרטגיית הקשר עדיין שווים חפירה
אם בהמשך רוצים באמת ללטש את הכלים האלה לכיוון “הדיבוב/תרגום הטוב ביותר בעולם לסרטונים בשפה זרה”, אז במודול התרגום אי אפשר יותר להסתמך על כיוונון prompt לפי תחושת בטן; חייבים להמשיך לאורך הקו של baseline-driven, ייחוס לפי case, ואז A/B בצעדים קטנים.
היום לפחות סללתי את הדרך הזו.