מודל abliterated הוא מודל רגיל עם משקולות פתוחות (open-weight), שהתנהגות הסירוב שלו הוסרה כירורגית באמצעות עריכת המשקולות — לא באימון מחדש, ולא בעטיפה בפרומפט מתוחכם. השם הוא הלחם של ablate (לכרות) ו-obliterate (למחוק כליל), והוא מתאר את המנגנון במדויק: מזהים את הכיוון היחיד במרחב האקטיבציות שנושא את "אני צריך לסרב לזה", ואז דואגים שהרשת לעולם לא תוכל לכתוב את הכיוון הזה שוב.
הטכניקה צמחה מעבודה בתחום הפרשנות (interpretability) שהראתה שסירוב בטרנספורמרים שכוונו לצ'אט מתווך ברובו על ידי כיוון ליניארי אחד. התוצאה הזו היא מה שהופך את כל העניין לזול. אילו הסירוב היה מרוח על פני אלפי פיצ'רים שמקיימים אינטראקציה זה עם זה, הסרתו הייתה דורשת gradient descent. מכיוון שהוא מרוכז, הסרתו דורשת אלגברה ליניארית.
הסירוב חי ב-residual stream
ה-residual stream של טרנספורמר הוא הווקטור המצטבר, אחד לכל מיקום טוקן, שכל בלוק קורא ממנו וכותב בחזרה אליו. ראשי attention ושכבות MLP מוסיפים אליו את הפלטים שלהם; שום דבר לא דורס אותו. בזכות המבנה החיבורי הזה, כיוון ב-residual stream משמעותו בערך אותו דבר בשכבה 10 ובשכבה 40, וזו בדיוק התכונה שמאפשרת לדבר על "כיוון הסירוב" כעל אובייקט יחיד ולא כעל ארבעים אובייקטים לא קשורים.
כדי למצוא אותו צריך שתי קבוצות פרומפטים: אחת שהמודל מסרב לה באופן עקבי, ואחת שהוא עונה עליה באופן עקבי. מריצים את שתיהן, לוכדים את האקטיבציות של ה-residual stream בשכבה ובמיקום טוקן קבועים (הטוקנים האחרונים של ההנחיה עובדים היטב, כי שם המודל סיים לקרוא את הבקשה ועדיין לא התחיל לענות), ולוקחים את ההפרש בין הממוצעים.
import torch
def refusal_direction(harmful: torch.Tensor, harmless: torch.Tensor) -> torch.Tensor:
"""Difference-in-means estimate of the refusal direction, unit-normalised.
Both tensors are [n_prompts, d_model] residual-stream activations captured at
the same layer and token position. Difference-in-means beats a trained probe
here: with a few hundred prompts a probe overfits, a mean shift does not.
"""
r = harmful.mean(dim=0) - harmless.mean(dim=0)
return r / r.norm()עושים את זה בכל שכבה ומיקום מועמדים, מקבלים עשרות כיוונים מועמדים, ואז בוחרים אחד. הבחירה חשובה יותר מהחילוץ: הקריטריון הנכון הוא לבצע ablation על המועמד הזה, ואז למדוד גם כמה הסירוב יורד על פרומפטים מזיקים שנשמרו בצד (held-out), וגם כמה התפלגות הטוקן הבא זזה על פרומפטים תמימים. המספר השני — בדרך כלל דיברגנץ KL מול המודל המקורי, שלא שונה — הוא מד הנזק שלכם. מועמד שמחסל את הסירוב אבל מזיז מאוד את ההתפלגות על הפרומפטים התמימים הוא בחירה גרועה יותר ממועמד שמחסל קצת פחות סירוב תמורת שבריר מהנזק הנלווה.
איך בונים מודל abliterated
ברגע שיש לכם וקטור יחידה r, יש שתי דרכים להשתמש בו. הראשונה היא hook בזמן הסקה: בכל שכבה, מחסרים את הרכיב של האקטיבציה לאורך r לפני שמעבירים אותה הלאה. זה עובד, אבל זה עולה לכם hook בזמן ריצה, כך שהמודל כבר אינו checkpoint רגיל.
השנייה — abliteration של ממש — אופה את זה לתוך המשקולות. כל מטריצה שכותבת אל ה-residual stream מקבלת עדכון מדרגה אחת (rank-one) שמסיר את r ממרחב הפלט שלה: מטריצת ה-embedding, כל ה-output projection של ה-attention, וכל ה-down-projection של ה-MLP.
def orthogonalize_(weight: torch.Tensor, r: torch.Tensor) -> None:
"""Project r out of a matrix that writes into the residual stream, in place.
weight is [d_model, d_in]. After W <- (I - r rT) W, the product W @ x has zero
component along r for every possible x, so no amount of prompting can put the
direction back. Rank-one per matrix; no gradients, no optimiser, no dataset of
answers — only the few hundred prompts used to estimate r.
"""
weight -= torch.outer(r, r @ weight)התוצאה היא checkpoint רגיל לגמרי. אותה ארכיטקטורה, אותו פורמט קובץ, אותה עלות הסקה, נטען ב-vLLM או ב-llama.cpp ללא שינוי. על GPU יחיד כל ההליך רץ בתוך דקות, והחלק היקר הוא סריקת המועמדים, לא העריכה.
במה זה שונה מ-fine-tuning
fine-tuning להסרת סירובים — SFT או DPO על קורפוס של תשובות שנענות לבקשה — הוא פעולה שונה מיסודה. הוא מזיז כל משקולת באמצעות gradient descent, הוא צריך דאטהסט של תשובות ולא רק של פרומפטים, הוא עולה שעות GPU, והוא מלמד התנהגות חדשה במקום למחוק התנהגות קיימת. הוא גם גורר איתו כל סגנון, הטיית אורך ומוזרות עובדתית שחיים בקורפוס ה-fine-tuning, ומסתכן בשכחה קטסטרופלית של יכולות לא קשורות.
abliteration נוגעת בתת-מרחב אחד מדרגה אחת ובשום דבר אחר. הדיוק הזה הוא יתרון המכירה העיקרי שלה וגם מקור מצב הכשל העיקרי שלה: אם במקרה משהו מלבד סירוב חי לאורך r, גם הוא נהרס, ושום זהירות בסריקה לא מונעת את זה לגמרי.
במה זה שונה מפרומפטים של jailbreak
jailbreak משאיר את המשקולות כמות שהן, ובמקום זה מנווט את האקטיבציות לאזור שבו מעגל הסירוב לא נדלק. זה שביר בדרכים שחשובות לאוטומציה. זה שורף טוקני הקשר בכל קריאה. זה לא דטרמיניסטי — אותו פרומפט יכול להיענות בדגימה אחת ולהידחות בדגימה הבאה. זה נשבר בשקט כשהספק מעדכן את המודל או חוסם את הניסוח הספציפי. ומכיוון שמנגנון הסירוב עדיין קיים במלואו, הוא יכול להתעורר מחדש באמצע היצירה, ולתת לכם שלוש פסקאות של פלט שימושי ואחריהן "בעצם, אני לא יכול להמשיך עם זה."
למודל abliterated אין מה להעיר מחדש. ההתנהגות יציבה לאורך כל היצירה ועל פני ניסוחי פרומפט שונים, וזו התכונה שהופכת אותו לשמיש בפייפליין אצווה (batch).
להיות כנים לגבי הפשרות
מודל abliterated אינו מודל טוב יותר. הוא מודל שהפסיק להגיד לא, וזו טענה צרה יותר משהיא נשמעת.
- סחיפה ביכולות. להסרת כיוון מכל מטריצת כתיבה יש עלויות מדידות. checkpoints שעברו abliteration מראים לעיתים קרובות ציות חלש יותר להנחיות במקרי קצה, הסכמה רבה יותר עם הנחות שגויות בפרומפט, ורגרסיות קטנות בבנצ'מרקים של reasoning.
- הסירוב אינו חד-ממדי לחלוטין. כיוון אחד לוכד את רוב ההתנהגות, לא את כולה. צפו שחלק מהסירובים ישרדו, וצפו לנזק נלווה בהתנהגויות שחולקות את אותו כיוון.
- אין ידע חדש. abliteration לא מוסיפה כלום. במקום שבו מודל הבסיס היה מסרב מתוך בורות שהתחפשה לזהירות, עכשיו תקבלו המצאה בטוחה בעצמה — מה שעבור פייפליין הערכה עלול להיות גרוע יותר מסירוב, כי סירוב הוא לפחות אות כן שאפשר לזהות.
- קבוצות הפרומפטים מגדירות את העריכה. קבוצה צרה של פרומפטים מזיקים מניבה כיוון צר. שתי abliterations של אותו מודל בסיס אינן ניתנות להחלפה זו בזו.
הרבה מודלים abliterated שפורסמו עוברים אחר כך fine-tuning קל לתיקון כדי להשיב יכולת שנפגעה — מה שמחזיר בשקט את העלות שהטכניקה נועדה להימנע ממנה.
איפה זה שימושי
מקרה השימוש הכן הוא עבודה שבה סירוב הוא שגיאת מדידה ולא הישג בטיחותי: מערכות red-teaming, קווי בסיס של שיעורי סירוב, הערכת חוסן, וניתוח אבטחה שבו הנושא מפעיל guardrail למרות שהמשימה הגנתית. unbleep מגישה מודלים abliterated מאחורי endpoint תואם OpenAI, כך שאפשר להפנות אליהם קליינט קיים ולקבל קו בסיס יציב. מה שתבנו עם זה — באחריותכם; ראו את מדיניות השימוש המקובל לגבי הקווים שאנחנו לא חוצים.
קבלו מפתח API ומדדו את ההבדל בעצמכם.