در مدلهای زبانی امروزی، پشتیبانی از پنجرههای بافت طولانی (مانند 64k یا 128k توکن) به یک ضرورت در پردازش اسناد و سامانههای RAG تبدیل شده است. با این حال، مکانیسم استاندارد اتنشن سافتمکس با یک محدودیت ساختاری مواجه است: این تابع هیچگاه خروجی دقیقاً صفر تولید نمیکند. وقتی طول توالی از دهها هزار توکن عبور میکند، تجمع احتمالهای ناچیز روی توکنهای نامرتبط پدیدهای به نام «کف نویز» میسازد که منجر به پدیدهی گم شدن در میانه (Lost in the Middle) و افت دقت در ارجاعات میشود.
معماری ترنسفورمر تفاضلی (Differential Transformer) با بهرهگیری از ایدهی حذف نویز دیفرانسیلی، اتنشن را از تفاضل دو شاخهی سافتمکس محاسبه میکند تا نویز مشترک پسزمینه خنثی شود. نسخهی بهینهشدهی آن، یعنی DIFF V2 که توسط مایکروسافت معرفی شد، مشکلات سرعت و ناپایداری نسخه اول را حل کرده و بدون نیاز به کرنلهای سفارشی، با زیرساختهای استاندارد مانند FlashAttention هماهنگ شده است.
در این درس، ابتدا مبانی ریاضی نویز سافتمکس و چگونگی حذف آن را بررسی میکنیم. سپس تفاوتهای معماری V1 و V2 را واکاوی کرده و در نهایت یک پیادهسازی گامبهگام از عملگر تفاضلی را با ابزارهای استاندارد پایتون اجرا میکنیم تا اثربخشی آن را بهصورت تجربی ببینید.
اتنشنِ سافتمکس (Softmax attention) مقدار کمی از احتمال را روی تکتک توکنهای نامرتبط پخش میکند. در طول 100k توکن، این نویزها روی هم جمع میشوند و سیگنال اصلی را در خود غرق میکنند. مدل ترنسفورمر تفاضلی (Differential Transformer؛ Ye و همکاران، ICLR 2025) با محاسبهی اتنشن بهصورت تفاضل دو سافتمکس، این مشکل را حل و کف نویز مشترک را کم میکند. نسخهی DIFF V2 (مایکروسافت، ژانویهی 2026) بازنویسیِ مناسب برای استک پروداکشن است: تاخیر دیکود دقیقاً برابر با ترنسفورمر خط مبنا، بدون نیاز به کرنلهای سفارشی، و سازگار با FlashAttention. در این درس، مسیر V1 تا V2 را سرتاسر بررسی میکنیم و یک پیادهسازی آزمایشیِ آمادهی اجرا از عملگر تفاضل را با کتابخانهی استاندارد Python میسازیم.
اهداف یادگیری
- دقیق بیان کنید چرا اتنشن سافتمکس کف نویز دارد و چرا این نویز با افزایش طول بافت رشد میکند.
- فرمول اتنشن تفاضلی را استخراج کنید و توضیح دهید چرا تفریق، بخش نویز مشترک را حذف میکند اما سیگنال اصلی را دستنخورده نگه میدارد.
- تفاوتهای V1 با V2 را موشکافی کنید: چه چیزی سریعتر، سادهتر و پایدارتر شد، و چرا هر یک از این تغییرات برای پیشآموزش در مقیاس پروداکشن ضروری بود.
- اتنشن تفاضلی را از صفر در Python خالص پیادهسازی کنید و ویژگی حذف نویز را بهصورت تجربی روی یک کوئری ساختگی شامل «سیگنال بهعلاوهی نویز» بسنجید.
صورت مسئله
اتنشنِ سافتمکس استاندارد یک ویژگی ریاضی دارد که در مقیاسهای بزرگ به دردسری عملیاتی تبدیل میشود. برای یک کوئری q، وزنهای اتنشن برابرند با softmax(qK^T / sqrt(d)). تابع سافتمکس هرگز نمیتواند مقدار دقیقاً صفر تولید کند؛ بنابراین به هر توکن نامرتبط مقداری وزن مثبت اختصاص مییابد. این وزن باقیمانده همان نویز است و متناسب با طول بافت افزایش مییابد. در بافت 128k توکنی، حتی اگر به هر توکن نامرتبط فقط 0.0001% از احتمال برسد، مجموع وزن 127,999 توکن نامرتبط رویهم حدود 13% از کل وزن را تشکیل میدهد. مدل باید یاد بگیرد چطور مسیر خود را از میان کف نویزی پیدا کند که مدام با افزایش طول بافت بزرگتر میشود.
در عمل، این پدیده خود را بهشکل تداخل سرهای اتنشن نشان میدهد: ارجاعهای توهمی در RAG با بافت طولانی، شکست در سناریوهای گم شدن در میانه (Lost in the Middle) در تسکهای بازیابی با 100k توکن، و افت نامحسوس دقت در بنچمارکهای «سوزن در انبار کاه» (needle in a haystack) فراتر از 32k توکن. مقالهی ترنسفورمر تفاضلی (arXiv:2410.05258، ICLR 2025) این شکاف را اندازه گرفت: مدلهای DIFF نسبت به خطوط مبنای هماندازهی خود به پرپلکسیتی پایینتر، دقت بالاتر در بافتهای طولانی و توهمات کمتری رسیدند.
با این حال، DIFF V1 سه مشکل اساسی داشت که مانع از ورودش به پایپلاینهای پیشآموزش پیشرو (frontier) میشد: کش مقدار (value cache) آن باید در هر گام دیکود دو بار لود میشد، به کرنلهای سفارشی CUDA نیاز داشت که سازگاری با FlashAttention را از بین میبردند، و لایهی RMSNorm به ازای هر سر آموزشهای طولانیمدت را در مقیاس بالای 70B ناپایدار میکرد. نسخهی DIFF V2 (وبلاگ unilm مایکروسافت، 20 ژانویهی 2026) هر سه مورد را برطرف کرد. در این درس هر دو نسخه را بررسی میکنیم، عملگر تفاضل را میسازیم و حذف نویز را روی یک کوئری ساده بنچمارک میکنیم.
مفاهیم پایه
کف نویز در سافتمکس
برای یک کوئری q و کلیدهای K = [k_1, ..., k_N]، وزنهای اتنشن به این صورت هستند:
هیچ w_iای هرگز صفر نمیشود. اگر k_i کاملاً به q بیارتباط باشد، امتیاز q . k_i صفر نخواهد بود، بلکه حول صفر با واریانس ||q||^2 / d نوسان میکند. پس از نرمالسازی سافتمکس، هر توکن نامرتبط همچنان سهمی معادل O(1/N) در مجموع وزندار دارد. مجموع سهم همهی توکنهای نامرتبط برابر است با O((N-1)/N) = O(1)؛ مقداری که اصلاً ناچیز نیست.
آنچه مدل واقعاً نیاز دارد، سازوکاری شبیه به top-k قاطع است: وزن بالا برای توکنهای مرتبط و وزنی نزدیک به صفر برای بقیه. سافتمکس بیش از حد هموار است و نمیتواند مستقیماً این کار را انجام دهد.
ایدهی تفاضلی
تصویر کردن ماتریسهای Q و K را برای هر سر به دو نیمه تقسیم میکنیم: و . سپس دو نقشهی اتنشن به دست میآوریم:
خروجی:
این تفریق، هر توزیع نویزی را که بین دو نقشه مشترک باشد حذف میکند. اگر هر دو نقشه روی 127k توکن نامرتبط وزنی تقریباً یکنواخت داشته باشند (که در مقداردهی اولیهی تصادفی همینطور است)، آن وزنها اثر یکدیگر را خنثی میکنند. اما سیگنال اصلی، یعنی وزن برجسته روی چند توکنِ واقعاً مرتبط، تنها در صورتی خنثی میشود که در هر دو نقشه دقیقاً با یک شدت ظاهر شود؛ پدیدهای که بعد از آموزش مدل رخ نخواهد داد.
پارامتر lambda یک اسکالر قابلیادگیری به ازای هر سر است که بهصورت lambda = exp(lambda_q1 dot lambda_k1) - exp(lambda_q2 dot lambda_k2) + lambda_init پارامتریزه میشود و حتی میتواند منفی باشد. مقدار lambda_init بهطور پیشفرض یک عدد مثبت کوچک مثل 0.8 است.
شباهت به حذف نویز در سیستمهای چندمیکروفونی
تصور کنید دو میکروفون نویزدار صدای یک گوینده را ضبط میکنند. هر دو میکروفون صدای گوینده را بههمراه نویز پسزمینهی همبسته دریافت میکنند. اگر سیگنال یکی را از دیگری کم کنید، نویز مشترک حذف میشود. صدای گوینده باقی میماند چون فاز یا دامنهی آن در دو سیگنال به اندازهای تفاوت دارد که جلوی خنثی شدن کامل را بگیرد. ضریب lambda به ازای هر سر دقیقاً همین تعادل را یاد میگیرد.
مقایسهی V1 و V2
نسخهی V1 تعداد پارامترها را برابر با ترنسفورمر خط مبنا نگه میداشت. برای ایجاد دو کوئری در هر سر، بُعد سر (head dimension) را نصف میکرد. این کار باعث کاهش قدرت بیان سرها و از آن بدتر، نصف شدن کش مقدار در هر سر میشد. در فاز دیکود، کش مقدار باید در هر گام دو بار لود میشد (یکبار برای هر branch سافتمکس). در نتیجه، سرعت دیکود با وجود برابریِ تعداد پارامترها، از خط مبنا کمتر بود.
نسخهی V2 تعداد سرهای کوئری را دو برابر میکند اما سرهای KV را ثابت نگه میدارد (پارامترهای موردنیاز را از تصویر به بالا یا up-projection قرض میگیرد). بعد سرها درست مثل خط مبنا دستنخورده میماند. پس از انجام تفریق، این بعد اضافی با عمل تصویر کردن دوباره کوچک میشود تا با تصویرِ در ترنسفورمر خط مبنا مطابقت پیدا کند. با این کار سه اتفاق مهم همزمان میافتد:
- سرعت دیکود دقیقاً برابر با خط مبنا میشود (کش کلید-مقدار فقط یک بار لود میشود).
- لایهی FlashAttention بدون هیچ تغییری اجرا میشود (بدون نیاز به کرنل سفارشی).
- شدت محاسباتی (Arithmetic intensity) در مرحلهی دیکود بالا میرود (محاسبات بیشتری به ازای هر بایتِ لودشده از HBM انجام میشود).
علاوه بر این، V2 لایهی RMSNorm به ازای هر سر را که در V1 برای پایدارسازی تفریق به کار میرفت حذف میکند. در مقیاسهای پیشآموزش ردهی 70B، این RMSNorm در مراحل پایانی آموزش باعث ناپایداری میشد. V2 آن را با یک طرح مقداردهی اولیهی سادهتر جایگزین میکند که بدون نیاز به ماژول اضافی، پایداری آموزش را حفظ میکند.
چه زمانی باید از آن استفاده کرد؟
| تسک یا بار کاری | مزیت |
|---|---|
| سامانههای RAG با بافت طولانی (64k+) | نقشههای اتنشن تمیزتر، ارجاعات توهمی کمتر |
| بنچمارکهای سوزن در انبار کاه | افزایش چشمگیر دقت فراتر از 32k توکن |
| پرسش و پاسخ چندسندی | تداخل کمتر میان اسناد مختلف |
| تکمیل کد در بافتهای 8k | تغییرات ناچیز، ارزش تغییر معماری را ندارد |
| چتهای کوتاه (< 4k) | تقریباً غیرقابلتشخیص نسبت به خط مبنا |
ارزش این روش با طول بافت نسبت مستقیم دارد. در 4k توکن کف نویز آنقدر کوچک است که اتنشن استاندارد مشکلی ایجاد نمیکند؛ اما در 128k به مدل آسیب میزند.
سازگاری با سایر قابلیتهای فنی سال 2026
| قابلیت | سازگار با DIFF V2؟ |
|---|---|
| GQA | بله (V2 سرهای Q را افزایش میدهد، نه سرهای KV را) |
| MLA (در DeepSeek) | در تئوری بله، اما هنوز مقالهای منتشر نشده که این دو را ترکیب کرده باشد |
| MoE | بله (اتنشن مستقل از بلوک MLP است) |
| RoPE | بله (دستنخورده) |
| YaRN / مقیاسپذیری بافت طولانی | بله (دقیقاً جایی است که DIFF بیشترین کمک را میکند) |
| FlashAttention | در V2 بله (در V1 خیر) |
| دیکودینگ حدسی | بله (تغییر اتنشن برای چرخهی دیکودینگ حدسی نامرئی است) |
پیادهسازی از صفر
فایل code/main.py اتنشن تفاضلی را در Python خالص پیادهسازی میکند. با تعریف یک کوئری ساختگی که ساختار سیگنال و نویز آن مشخص است، میتوانید نسبت حذف نویز را مستقیماً بسنجید.
گام 1: اتنشن سافتمکس استاندارد
عملیات ماتریسی با کتابخانهی استاندارد: لیستهای تو در تو، ضرب ماتریسی دستی، و سافتمکس با تفریق مقدار ماکزیمم جهت پایداری عددی.
def softmax(row):
m = max(row)
exps = [math.exp(x - m) for x in row]
s = sum(exps)
return [e / s for e in exps]
گام 2: تقسیم Q و K به دو نیمه
سبک V1: نصف کردن بعد سر. سبک V2: حفظ بعد سر و دو برابر کردن تعداد سرها. پیادهسازی آزمایشی ما برای سادگی آموزش از رویکرد V1 استفاده میکند؛ منطق ریاضی کاملاً یکسان است و فقط نحوهی سازماندهی ابعاد تفاوت دارد.
گام 3: دو شاخهی سافتمکس + تفریق
A1 = [softmax([dot(q1, k) / scale for k in K1]) for q1 in Q1]
A2 = [softmax([dot(q2, k) / scale for k in K2]) for q2 in Q2]
diff_weights = [[a1 - lam * a2 for a1, a2 in zip(r1, r2)] for r1, r2 in zip(A1, A2)]
out = [[sum(w * v[j] for w, v in zip(row, V)) for j in range(d_v)] for row in diff_weights]
نکته: وزنهای خروجی میتوانند منفی باشند. این مسئله مشکلی ندارد؛ کش مقدار میتواند سهمهای علامتدار را مدیریت کند و لایهی تصویرِ بعدیِ V علامتها را جذب خواهد کرد.
گام 4: سنجش حذف نویز
یک توالی ساختگی به طول 1024 بسازید. توکن سیگنال را در موقعیتی مشخص قرار دهید و بقیهی توالی را با نویز پر کنید. سپس دو مقدار را حساب کنید: (الف) وزن اتنشن سافتمکس استاندارد روی موقعیت سیگنال، و (ب) وزن اتنشن تفاضلی روی همان موقعیت. نسبت سیگنال به نویز را در هر دو حالت اندازه بگیرید. اتنشن تفاضلی، بسته به اینکه دو شاخه چقدر یاد گرفته باشند با هم تفاوت داشته باشند، نسبت سیگنال به نویز را 3 تا 10 برابر بهتر میکند.
گام 5: محاسبهی تعداد پارامترها در V1 در مقایسه با V2
با فرض پیکربندی (hidden=4096, heads=32, d_head=128)، این مقادیر چاپ میشوند:
- ترنسفورمر خط مبنا: ماتریسهای Q، K و V هر کدام با ابعاد
hidden * hidden، و بلوک MLP با ابعاد4 * hidden. - نسخهی DIFF V1: ماتریسهای Q و K هر کدام با ابعاد
hidden * hidden، ماتریس V با ابعادhidden * hidden(بدون تغییر)، بعد هر سر در محاسبات داخلی نصف میشود. پارامترهایlambdaبه ازای هر سر اضافه میشوند: . - نسخهی DIFF V2: ماتریس Q با ابعاد
2 * hidden * hidden، ماتریسهای K و V با ابعادhidden * hidden. بعد اضافی قبل از دوباره با تصویر کردن کاهش مییابد. پارامترهایlambdaنیز اضافه میشوند.
کد آزمایشی هزینهی پارامتر اضافی در V2 (تقریباً hidden * hidden پارامتر بیشتر به ازای هر بلوک اتنشن) را محاسبه کرده و در خروجی نمایش میدهد.
استفاده از ابزارهای واقعی
تا آوریل 2026، معماری DIFF V2 هنوز به همهی سرورهای استنتاج پروداکشن اضافه نشده است، اما فرایند یکپارچهسازی آن در vLLM و SGLang جریان دارد. در حال حاضر این الگو را در بخشهای زیر میبینیم:
- مدلهای پروداکشن داخلی مایکروسافت برای بافتهای طولانی.
- پیادهسازیهای پژوهشی در چند مرحلهی آموزش مدلهای باز با هدف بافتهای 256k به بالا.
- معماریهای هیبریدی که اتنشن تفاضلی را با اتنشن پنجرهی لغزان (sliding-window attention) در لایههای متناوب ترکیب میکنند.
چه زمانی در سال 2026 سراغ این معماری بروید:
- وقتی میخواهید مدلی را از صفر برای طول بافت موثر 64k به بالا آموزش دهید. اتنشن تفاضلی را از همان ابتدا اضافه کنید؛ آموزش مجدد در مراحل بعدی بسیار گران تمام میشود.
- در فاینتیونینگ مدلهای بافت طولانی، وقتی که خطاهای ناشی از گم شدن در میانه نتایج ارزیابی شما را خراب میکنند. با یک LoRA روی لایههای تصویرِ Q میتوانید ساختار DIFF را تقریب بزنید.
چه زمانی نباید از آن استفاده کنید:
- وقتی در حال سرو یک مدل متراکم (dense) ازپیشآموزشدیده هستید که عملکرد پایدار و خوبی در بافتهای طولانی دارد. هزینهی آموزش مجدد بهندرت روی وزنهای موجود توجیه اقتصادی پیدا میکند.
- وقتی طول بافت شما همیشه کمتر از 16k توکن است. در این حالت کف نویز کاملاً ناچیز است.
خروجی این درس
خروجی این درس فایل outputs/skill-diff-attention-integrator.md است. این سند بر اساس معماری مدل، طول بافت هدف، وضعیت تولید توهم و بودجهی آموزشی، یک برنامهی یکپارچهسازی برای اضافه کردن اتنشن تفاضلی به پیشآموزش جدید یا فاینتیونینگ با LoRA ارائه میدهد.
تمرینها
-
اسکریپت
code/main.pyرا اجرا کنید. مطمئن شوید نسبت سیگنال به نویز گزارششده برای اتنشن تفاضلی روی کوئری ساختگی بیشتر از اتنشن سافتمکس استاندارد است. دامنهی نویز را تغییر دهید و نقطهی تلاقی را پیدا کنید؛ یعنی جایی که اتنشن استاندارد دیگر عملاً بیاستفاده میشود. -
تفاوت تعداد پارامترها را بین مدل خط مبنا با DIFF V1 و با DIFF V2 برای مدلی در کلاس 7B محاسبه کنید (hidden=4096, heads=32, d_head=128, 32 layers). نشان دهید کدام بخشها پارامتر اضافه کردهاند و کدام بخشها ثابت ماندهاند.
-
بخش 3 مقالهی DIFF V1 (سند arXiv:2410.05258) و بخش 2 وبلاگ هاگینگفیس دربارهی DIFF V2 را بخوانید. در دو جمله توضیح دهید چرا لایهی RMSNorm به ازای هر سر در V1 ضروری بود و چرا V2 توانست بدون واگرایی در آموزش آن را حذف کند.
-
یک ارزیابی ابلیشن (ablation) بسازید: اتنشن تفاضلی را یکبار با
lambda = 0(فقط سافتمکس اول) و یکبار باlambda = 1(تفریق کامل) محاسبه کنید. روی کوئری ساختگی بررسی کنید نسبت سیگنال به نویز در طول این بازه چه تغییری میکند. مقداری ازlambdaرا که بیشترین نسبت سیگنال به نویز را میدهد مشخص کنید. -
پیادهسازی آزمایشی را توسعه دهید تا از ترکیب GQA و DIFF V2 پشتیبانی کند. پیکربندی را روی 8 سر KV و 32 سر Q بگذارید. نشان دهید حجم کش کلید-مقدار دقیقاً با یک مدل خط مبنای GQA با همین پیکربندی (8, 32) برابر است.
اصطلاحات کلیدی
| اصطلاح | در گفتوگوها | تعریف دقیق فنی |
|---|---|---|
| اتنشن تفاضلی | «کم کردن دو تا سافتمکس از هم» | تقسیم ماتریسهای Q و K به دو نیمه، محاسبهی دو نقشهی سافتمکس، تفریق نقشهی دوم (ضرب در ) از اولی، و در نهایت ضرب نتیجه در V |
| کف نویز | «دنبالهی غیرصفر سافتمکس» | وزنِ که تابع سافتمکس به هر توکن نامرتبط اختصاص میدهد و مجموع آن در بافتهای طولانی به میرسد |
| lambda | «ضریب تفریق» | اسکالر قابلیادگیری به ازای هر سر که بهصورت exp(lq1.lk1) - exp(lq2.lk2) + lambda_init فرمولبندی میشود و میتواند مقداری منفی هم داشته باشد |
| DIFF V1 | «نسخهی کنفرانس ICLR 2025» | معماری اولیهی Differential Transformer؛ بعد سر را برای حفظ تعداد پارامترها نصف میکند، به کرنل اختصاصی نیاز دارد و سرعت دیکود پایینی دارد |
| DIFF V2 | «اصلاحیهی ژانویهی 2026» | سرهای Q را دو برابر میکند اما سرهای KV را ثابت نگه میدارد؛ تاخیر دیکود آن با خط مبنا برابری میکند و با FlashAttention سازگاری کامل دارد |
| لایهی RMSNorm به ازای هر سر | «تثبیتکنندهی V1» | یک لایهی نرمالسازی اضافه در V1 بعد از عمل تفریق؛ در V2 حذف شد تا از ناپایداری در مراحل پایانی آموزش جلوگیری شود |
| نسبت سیگنال به نویز | «میزان هدررفت اتنشن» | نسبت وزن اختصاصیافته به موقعیت سیگنال واقعی به میانگین وزنهای دادهشده به موقعیتهای نامرتبط |
| گم شدن در میانه | «نقطهضعف بافت طولانی» | پدیدهای تجربی که در آن دقت بازیابی برای اسناد قرارگرفته در میانهی یک بافت طولانی افت میکند؛ اتنشن تفاضلی این مشکل را کاهش میدهد |
| شدت محاسباتی | «تعداد FLOP به ازای هر بایت لودشده» | نسبتی که در V2 با دو برابر کردن کوئریها به ازای هر بار خواندن KV در فاز دیکود افزایش یافت؛ معیاری حیاتی برای سناریوهای دیکودِ وابسته به حافظه (memory-bound) |
برای مطالعهی بیشتر
- Ye et al. — Differential Transformer (arXiv:2410.05258, ICLR 2025) — مقالهی اصلی، شامل مبانی تئوری حذف نویز و تستهای ابلیشن روی بافت طولانی.
- Microsoft unilm — Differential Transformer V2 (Hugging Face blog, January 2026) — بازنویسی معماری برای استکهای عملیاتی، دستیابی به تاخیر دیکود برابر با خط مبنا و سازگار با FlashAttention.
- Understanding Differential Transformer Unchains Pretrained Self-Attentions (arXiv:2505.16333) — تحلیل نظری دربارهی چرایی بازیابی ساختار اتنشن پیشآموزشدیده از طریق تفریق.
- Shared DIFF Transformer (arXiv:2501.17900) — نسخهای با اشتراکگذاری پارامترها.
- Vaswani et al. — Attention Is All You Need (arXiv:1706.03762) — ترنسفورمر خط مبنا که DIFF اتنشن خود را نسبت به آن تفریق میکند.
- Liu et al. — Lost in the Middle (arXiv:2307.03172) — مقالهی مرجع بنچمارک بافتهای طولانی که اتنشن تفاضلی برای حل چالشهای آن طراحی شده است.
