فاز 7 · درس 15

انواع اتنشن در ترنسفورمرها: پنجره‌ی لغزان، تنک و تفاضلی

نوع
ساختنی
زمان
حدود 60 دقیقه
زبان‌ها
Python
پیش‌نیاز
فاز 7 · 02 (خوداتنشن)، فاز 7 · 03 (چندسر)، فاز 7 · 12 (کش KV / فلش‌اتنشن)
شبکه‌ای هندسی از گره‌های نوری که بخش‌هایی از خطوط ارتباطی آن حذف یا کم‌نور شده و فقط اتصالات محلی و کلیدی باقی مانده‌اند
فهرست مطالب
  1. صورت مسئله
  2. مفاهیم پایه
  3. اتنشن پنجره‌ی لغزان (SWA)
  4. اتنشن تنک / بلوکی
  5. اتنشن تفاضلی (DIFF Transformer، سال 2024)
  6. مقایسه‌ی مدل‌های مختلف
  7. پیاده‌سازی از صفر
  8. گام 1: ماسک علیتی کامل (پایه‌ی مقایسه)
  9. گام 2: ماسک علیتی پنجره‌ی لغزان
  10. گام 3: ماسک تنک محلی + استرایددار
  11. گام 4: اتنشن تفاضلی
  12. گام 5: اندازه‌ی کش KV
  13. استفاده از ابزارهای واقعی
  14. خروجی این درس
  15. تمرین‌ها
  16. اصطلاحات کلیدی
  17. برای مطالعه‌ی بیشتر

مکانیزم اتنشن کامل (Full Attention) در طول توالی‌های بالا رفتاری سنگین دارد و هزینه‌ی محاسبات و حافظه‌ی آن با توان دو افزایش پیدا می‌کند. مدل‌های زبانی مدرن برای کار با بافت‌های 32K تا 128K نمی‌توانند در تمام لایه‌ها به هر توکن اجازه‌ی دیدن کل توکن‌های پیشین را بدهند؛ زیرا حجم کش KV و حافظه‌ی فعال‌سازی سرسام‌آور می‌شود.

برای رفع این چالش، مهندسان معماری‌های جایگزینی توسعه داده‌اند: اتنشن پنجره‌ی لغزان (SWA)، اتنشن تنک یا بلوکی (Sparse Attention)، و اتنشن تفاضلی (Differential Attention). این روش‌ها توپولوژی ماتریس امتیازها را دگرگون می‌کنند و بار محاسباتی و اندازه کش KV را تا ده‌ها برابر کاهش می‌دهند.

در شرایطی که سخت‌افزارهای پردازشی پیشرفته محدود و گران‌قیمت هستند، بهینه‌سازی مدل‌ها در سطح معماری اهمیت دوچندان پیدا می‌کند. در این درس با مفاهیم ریاضی، اثرات حافظه‌ای و پیاده‌سازی این ساختارها با FlexAttention در PyTorch آشنا می‌شوید تا بدانید مدل‌هایی مانند Gemma 3 یا DeepSeek چگونه چنین بافت‌های بلندی را مدیریت می‌کنند.

اتنشن کامل (Full attention) مثل یک دایره است: هر توکن تمام توکن‌های دیگر را می‌بیند و حافظه تاوانش را پس می‌دهد. سه مدل جایگزین، شکل این دایره را تغییر می‌دهند و نیمی از هزینه‌ها را برمی‌گردانند.

صورت مسئله

اتنشن کامل از نظر طول توالی، هزینه‌ی حافظه‌ی O(N2)O(N^2) و هزینه‌ی محاسباتی O(N2)O(N^2) دارد. برای مدل Llama 3 70B با طول بافت 128K، این یعنی 16 میلیارد درایه‌ی اتنشن در هر لایه، ضرب در 80 لایه. فلش‌اتنشن (Flash Attention در درس 12) هزینه‌ی حافظه‌ی فعال‌سازی (activation memory) از مرتبه‌ی O(N2)O(N^2) را پنهان می‌کند، اما بار محاسباتی ریاضی را تغییر نمی‌دهد؛ هنوز هم هر توکن به تمام توکن‌های دیگر اتنشن دارد.

سه دسته از مدل‌های جایگزین، توپولوژی خود ماتریس اتنشن را دگرگون می‌کنند:

  1. اتنشن پنجره‌ی لغزان (Sliding window attention یا SWA): هر توکن به‌جای کل پیشوند قبلی، فقط به یک پنجره‌ی ثابت از همسایگانش اتنشن دارد. حافظه و محاسبات به O(N⋅W)O(N \cdot W) کاهش می‌یابد که در آن WW اندازه‌ی پنجره است. نمونه‌ها: Gemma 2/3، لایه‌های اول Mistral 7B، و Phi-3-Long.
  2. اتنشن تنک / بلوکی (Sparse / block attention): فقط زوج‌های انتخابی (i, j) امتیازدهی می‌شوند و وزن بقیه اجباراً صفر می‌شود. نمونه‌ها: Longformer، BigBird، و sparse transformer متعلق به OpenAI.
  3. اتنشن تفاضلی (Differential attention): محاسبه‌ی دو نقشه‌ی اتنشن با تصویر کردن (projection) جداگانه‌ی Q/K و سپس تفریق آن‌ها از یکدیگر. این کار اثر «سینک اتنشن» (attention sink) را که وزن زیادی را بی‌دلیل به اولین توکن‌ها می‌کشاند، خنثی می‌کند. نمونه: مدل DIFF Transformer از مایکروسافت (2024).

این روش‌ها در کنار هم کار می‌کنند. یک مدل پیشروی سال 2026 معمولاً آن‌ها را ترکیب می‌کند: بیشتر لایه‌ها از نوع SWA-1024 هستند، از هر پنج لایه یکی اتنشن کامل سراسری (global) است، و چند سر (head) تفاضلی هم کار بازیابی اطلاعات را دقیق می‌کنند. نسبت 5:1 لایه‌های SWA به سراسری در Gemma 3، اکنون به انتخاب استاندارد و مرجع تبدیل شده است.

مفاهیم پایه

اتنشن پنجره‌ی لغزان (SWA)

هر پرسش (query) در موقعیت ii فقط به موقعیت‌های بازه‌ی [i−W,i][i - W, i] (در SWA علیتی یا causal) یا بازه‌ی [i−W/2,i+W/2][i - W/2, i + W/2] (در حالت دوطرفه) اتنشن دارد. توکن‌های بیرون از این پنجره در ماتریس امتیاز، مقدار −∞-\infty می‌گیرند.

full causal:           sliding window (W=4):
positions 0-7          positions 0-7, W=4
    0 1 2 3 4 5 6 7        0 1 2 3 4 5 6 7
0 | x                0 |  x
1 | x x              1 |  x x
2 | x x x            2 |  x x x
3 | x x x x          3 |  x x x x
4 | x x x x x        4 |    x x x x
5 | x x x x x x      5 |      x x x x
6 | x x x x x x x    6 |        x x x x
7 | x x x x x x x x  7 |          x x x x

به ازای N=8192N = 8192 و W=1024W = 1024، ماتریس امتیازها در حالت میانگین 1024 × 8192 سطر غیرصفر دارد؛ یعنی کاهشی 8 برابری.

کوچک‌تر شدن کش KV با SWA: در هر لایه تنها کافی است آخرین WW توکن از K و V نگه‌داری شوند. برای ساختاری شبیه Gemma 3 (پنجره‌ی 1024 و طول بافت 128K)، حجم کش KV تا 128 برابر کاهش می‌یابد.

افت کیفیت: ترنسفورمرهایی که صرفاً از SWA استفاده می‌کنند، در بازیابی‌های بازه‌ی طولانی دچار مشکل می‌شوند. راه‌حل: چیدمان متناوب لایه‌های SWA در کنار لایه‌های اتنشن کامل. مدل Gemma 3 از نسبت 5 به 1 برای لایه‌های SWA به سراسری بهره می‌برد. مدل Mistral 7B از پشته‌ای از لایه‌های causal-SWA استفاده می‌کرد که در آن اطلاعات از طریق پنجره‌های هم‌پوشان «رو به جلو جریان می‌یافتند»؛ هر لایه میدان دید (receptive field) مؤثر را به اندازه‌ی WW گسترش می‌دهد و بعد از L لایه، مدل می‌تواند تا L × W توکن به عقب اتنشن داشته باشد.

اتنشن تنک / بلوکی

از قبل یک الگوی تنک N × N را مشخص کنید. سه ساختار استاندارد وجود دارد:

  • محلی + استرایددار (OpenAI sparse transformer): به آخرین WW توکن به‌علاوه‌ی توکن‌هایی با فاصله‌ی منظمِ stride از آن نقطه به عقب اتنشن داده می‌شود. این روش هم اطلاعات محلی و هم بلندمدت را با هزینه‌ی محاسباتی O(N⋅N)O(N \cdot \sqrt{N}) پوشش می‌دهد.
  • روش Longformer / BigBird: پنجره‌ی محلی + مجموعه‌ی کوچکی از توکن‌های سراسری (مثل [CLS]) که هم به همه‌ی توکن‌ها اتنشن دارند و هم بقیه به آن‌ها اتنشن می‌ورزند + اتصالات تصادفی و تنک. این الگو از نظر تجربی، طول بافت را با حفظ همان کیفیت تا 2 برابر افزایش می‌دهد.
  • اتنشن تنک نیتیو (Native Sparse Attention از DeepSeek، سال 2025): یادگیری اینکه کدام بلوک‌های (Q, K) اهمیت دارند و سپس رد کردن بلوک‌های صفر در سطح کرنل (kernel). این روش با FlashAttention نیز سازگار است.

اتنشن تنک در واقع دستاوردی در حوزه‌ی مهندسی کرنل است. ریاضیات آن ساده است (ماسک کردن ماتریس امتیاز)؛ برگ برنده‌ی اصلی در این است که درایه‌های صفر اصلاً در حافظه‌ی SRAM لود نمی‌شوند. کتابخانه‌ی FlashAttention-3 و رابط برنامه‌نویسی FlexAttention در سال 2026، اعمال الگوهای تنک سفارشی را در PyTorch کاملاً روان و سازگار کرده‌اند.

اتنشن تفاضلی (DIFF Transformer، سال 2024)

اتنشن معمولی با مشکلی به نام «سینک اتنشن» مواجه است: تابع سافت‌مکس (softmax) جمع هر سطر را مجبور به 1 شدن می‌کند؛ بنابراین توکن‌هایی که نمی‌خواهند به چیز خاصی اتنشن داشته باشند، وزن زیادی را روی اولین توکن (یا چند توکن اول) تخلیه می‌کنند. این اتفاق ظرفیتی را که باید صرف محتوای واقعی می‌شد، تلف می‌کند.

اتنشن تفاضلی این ایراد را با محاسبه‌ی دو نقشه‌ی اتنشن و تفریق آن‌ها برطرف می‌کند:

A1=softmax(Q1K1T/d)A2=softmax(Q2K2T/d)DiffAttn=(A1−λ⋅A2)V\begin{aligned} A_1 &= \mathop{\mathrm{softmax}}(Q_1 K_1^T / \sqrt{d}) \\ A_2 &= \mathop{\mathrm{softmax}}(Q_2 K_2^T / \sqrt{d}) \\ \text{DiffAttn} &= (A_1 - \lambda \cdot A_2) V \end{aligned}

که در آن λ\lambda یک اسکالر یادگیری‌پذیر است (معمولاً بین 0.5 تا 0.8). ماتریس A1 وزن‌های واقعی محتوا را ثبت می‌کند و A2 سینک را می‌گیرد. تفریق این دو، سینک را خنثی کرده و وزن را دوباره به توکن‌های مرتبط اختصاص می‌دهد.

نتایج گزارش‌شده (مایکروسافت 2024): 5 تا 10 درصد پرپلکسیتی (perplexity) کمتر، 1.5 تا 2 برابر بافت مؤثر طولانی‌تر با همان طول آموزش، و بازیابی بسیار دقیق‌تر اطلاعات مدفون در میان انبوه داده‌ها (needle-in-a-haystack).

مقایسه‌ی مدل‌های مختلف

ساختار محاسبات کش KV کیفیت در مقایسه با کامل استفاده در محیط عملیاتی
اتنشن کامل O(N2)O(N^2) O(N)O(N) در هر لایه مبنا (baseline) لایه‌ی پیش‌فرض در تمام مدل‌ها
پنجره‌ی لغزان (پنجره‌ی 1024) O(N⋅W)O(N \cdot W) O(W)O(W) در هر لایه افت 0.1 پرپلکسیتی، عالی در ترکیب با لایه‌های سراسری Gemma 2/3، Phi-3-Long
تنک محلی + استرایددار O(N⋅N)O(N \cdot \sqrt{N}) ترکیبی شبیه به SWA sparse transformer در OpenAI، مدل Longformer
مدل BigBird (محلی + سراسری + تصادفی) حدوداً O(N)O(N) ترکیبی هم‌کیفیت با اتنشن کامل در بافت 2 برابری نسخه‌های اولیه‌ی BERT با طول بافت بالا
تنک نیتیو (DeepSeek-V3.2) O(N⋅active fraction)O(N \cdot \text{active fraction}) O(N)O(N) اختلاف کمتر از 0.05 پرپلکسیتی DeepSeek-V3.2، سال 2025
تفاضلی O(2⋅N2)O(2 \cdot N^2) O(2N)O(2N) 5 تا 10 درصد بهبود پرپلکسیتی DIFF Transformer، مدل‌های اولیه‌ی 2026
اشتراک‌گذاری KV در GQAتعداد سرها را بکشید
GQA KV-SHARINGEvery query head keeps its own projection, but several can share one key-value head. One kv per query is full multi-head attention; one kv for all is multi-query; a few groups in between is grouped-query attention. Fewer kv heads shrink the cache by the ratio of query to kv heads while keeping most of the quality.2 سر kv8 سر query
هر سر query تصویر کردن اختصاصی خودش را نگه می‌دارد، اما چند سر می‌توانند در یک سر key-value مشترک باشند. یک kv به ازای هر query همان اتنشن چندسره‌ی کامل است؛ یک kv برای همه همان multi-query است؛ چند گروه در این بین نیز اتنشن کوئری گروهی (GQA) است. سرهای kv کمتر، کَش را به نسبت سرهای query به kv کاهش می‌دهند و هم‌زمان بخش عمده‌ی کیفیت را حفظ می‌کنند.

پیاده‌سازی از صفر

کد code/main.py را ببینید. در اینجا مقایسه‌گری برای ماسک‌های علیتی می‌سازیم که حالت‌های کامل، SWA، محلی + استرایددار، و تفاضلی را روی یک توالی فرضی و ساده در کنار هم نشان می‌دهد.

گام 1: ماسک علیتی کامل (پایه‌ی مقایسه)

def causal_mask(n):
    return [[0.0 if j <= i else float("-inf") for j in range(n)] for i in range(n)]

ماسک پایه‌ای که از درس 07 به یاد داریم. ماتریس مثلثی پایینی؛ با وزن صفر در بالای قطر اصلی.

گام 2: ماسک علیتی پنجره‌ی لغزان

def swa_mask(n, window):
    M = [[float("-inf")] * n for _ in range(n)]
    for i in range(n):
        lo = max(0, i - window + 1)
        for j in range(lo, i + 1):
            M[i][j] = 0.0
    return M

یک پارامتر دارد: window. اگر window >= n باشد، به همان اتنشن علیتی کامل می‌رسید. اگر window = 1 باشد، هر توکن فقط به خودش اتنشن دارد.

گام 3: ماسک تنک محلی + استرایددار

def strided_mask(n, window, stride):
    M = [[float("-inf")] * n for _ in range(n)]
    for i in range(n):
        lo = max(0, i - window + 1)
        for j in range(lo, i + 1):
            M[i][j] = 0.0
        for j in range(0, i + 1, stride):
            M[i][j] = 0.0
    return M

یک پنجره‌ی محلی پرتراکم به‌علاوه‌ی توکن‌هایی به فواصل ثابت stride به سمت ابتدای توالی. میدان دید با افزودن لایه‌ها به‌صورت لگاریتمی رشد می‌کند.

گام 4: اتنشن تفاضلی

def diff_attention(Q1, K1, Q2, K2, V, lam):
    A1 = softmax_causal(Q1 @ K1.T / sqrt_d)
    A2 = softmax_causal(Q2 @ K2.T / sqrt_d)
    return (A1 - lam * A2) @ V

دو بار محاسبه‌ی اتنشن و تفریق آن‌ها با یک ضریب ترکیب یادگیری‌پذیر. در کد، نقشه‌ی حرارتی سینک اتنشن را در حالت تکی و تفاضلی مقایسه می‌کنیم و محو شدن سینک را می‌بینیم.

گام 5: اندازه‌ی کش KV

میزان حافظه‌ی کش را به ازای هر لایه در طول توالی N=131072N = 131072 برای هر ساختار چاپ کنید. نسخه‌های SWA و تنک، مصرف حافظه را 10 تا 100 برابر کاهش می‌دهند؛ در حالی که حالت تفاضلی آن را دو برابر می‌کند. باید این بهای مصرف حافظه را آگاهانه بپردازید.

استفاده از ابزارهای واقعی

الگوهای عملیاتی سال 2026:

from transformers import AutoModelForCausalLM
# Gemma 3 mixes SWA (window=1024) and global layers at 5:1.
model = AutoModelForCausalLM.from_pretrained("google/gemma-3-27b-it")
# print(model.config.sliding_window, model.config.layer_types)

ابزار FlexAttention در PyTorch 2.5 به بالا یک تابع ماسک دریافت می‌کند:

from torch.nn.attention.flex_attention import flex_attention, create_block_mask

def swa_pattern(b, h, q_idx, kv_idx):
    return (q_idx - kv_idx < 1024) & (q_idx >= kv_idx)

mask = create_block_mask(swa_pattern, B=batch, H=heads, Q_LEN=n, KV_LEN=n)
out = flex_attention(q, k, v, block_mask=mask)

این تابع در قالب یک کرنل سفارشی Triton کامپایل می‌شود. سرعت آن در الگوهای رایج تا 10 درصدی FlashAttention-3 می‌رسد و تابع ماسک آن هم یک تابع ساده‌ی پایتونی است.

چه زمانی از کدام استفاده کنیم:

  • اتنشن کامل خالص: تمام لایه‌ها تا بافت‌های حدود 16K، یا زمان‌هایی که کیفیت بازیابی اطلاعات اولویت اول است.
  • ترکیب SWA و سراسری: بافت‌های طولانی (بیشتر از 32K)، در شرایطی که آموزش و استنتاج با محدودیت حافظه روبه‌رو است؛ انتخاب پیش‌فرض سال 2026 برای بافت‌های بالای 32K.
  • اتنشن تنک بلوکی: کرنل و الگوی سفارشی؛ فقط برای کاربردهای خاص (مثل بازیابی تخصصی یا داده‌های صوتی).
  • اتنشن تفاضلی: هر سناریویی که مشکل سینک اتنشن به آن آسیب می‌زند (سیستم‌های RAG با بافت طولانی، پیدا کردن داده‌های خاص در انبوه اطلاعات).

خروجی این درس

فایل outputs/skill-attention-variant-picker.md را بررسی کنید. این مهارت با توجه به طول بافت هدف، نیازهای بخش بازیابی، و محدودیت‌های پردازشی در آموزش و استنتاج، توپولوژی اتنشن مناسب را برای یک مدل جدید انتخاب می‌کند.

تمرین‌ها

  1. ساده. کد code/main.py را اجرا کنید. مطمئن شوید که حالت SWA با window=4 تمام درایه‌های بیرون از 4 توکن آخر هر سطر را صفر می‌کند. بررسی کنید که با window=n دقیقاً خروجی اتنشن علیتی کامل به‌صورت بیتی بازتولید شود.
  2. متوسط. اتنشن SWA علیتی با window=1024 را روی پروژه‌ی نهایی درس 07 پیاده کنید. آن را 1,000 گام روی مجموعه‌داده‌ی tinyshakespeare آموزش دهید. افت تابع زیان اعتبارسنجی (val loss) و کاهش حداکثر حافظه‌ی مصرفی را در مقایسه با اتنشن کامل بسنجید.
  3. سخت. ساختار ترکیب لایه‌های 5:1 مدل Gemma 3 (پنج لایه SWA و یک لایه سراسری) را در مدل پروژه‌ی نهایی پیاده کنید. با پارامترهای برابر، مقادیر تابع زیان (loss)، حافظه، و کیفیت متن تولیدی را با دو حالت پایه‌ی «کاملاً SWA» و «کاملاً سراسری» مقایسه کنید.
  4. سخت. اتنشن تفاضلی را با یک پارامتر یادگیری‌پذیر λ\lambda برای هر سر (head) بسازید. مدل را روی یک تسک بازیابی ساختگی (شامل یک داده‌ی هدف در میان 2,000 داده‌ی گمراه‌کننده) آموزش دهید. دقت بازیابی را با یک مدل پایه‌ی اتنشن معمولی با تعداد پارامتر برابر مقایسه کنید.

اصطلاحات کلیدی

اصطلاح در گفت‌وگوها معنای دقیق فنی
اتنشن پنجره‌ی لغزان (SWA) «اتنشن محلی» هر پرسش فقط به WW توکن آخر خود اتنشن دارد؛ کش KV تا O(W)O(W) کاهش پیدا می‌کند.
میدان دید مؤثر «مدل تا چند گام قبل را می‌بیند» در یک پشته‌ی LL لایه‌ای از SWA با پنجره‌ی WW، حداکثر تا L×WL \times W توکن است.
مدل‌های Longformer و BigBird «محلی + سراسری + تصادفی» الگوهای تنک همراه با تعداد کمی توکن سراسریِ همیشه فعال؛ روش‌های اولیه‌ی پشتیبانی از بافت طولانی.
اتنشن تنک نیتیو (Native Sparse Attention) «ترفند کرنلِ DeepSeek» یادگیری میزان تنک بودن در سطح بلوک‌ها؛ رد کردن بلوک‌های صفر در سطح کرنل بدون افت کیفیت.
اتنشن تفاضلی «دو نقشه، یکی تفریق می‌شود» در DIFF Transformer: تفریق نقشه‌ی دوم اتنشن (ضرب در ضریب یادگیری‌پذیر λ\lambda) از اولی برای حذف اثر سینک اتنشن.
سینک اتنشن «سرازیر شدن وزن به توکن 0» نرمال‌سازی با سافت‌مکس مجموع سطرها را 1 می‌کند؛ پرسش‌های بدون بار معنایی وزن خود را روی موقعیت 0 خالی می‌کنند.
FlexAttention «تعریف ماسک با توابع پایتونی» رابط برنامه‌نویسی PyTorch 2.5 به بالا که توابع دلخواه ماسک را به کرنل‌هایی با سرعت FlashAttention کامپایل می‌کند.
ترکیب نوع لایه‌ها «نسبت 5:1 پنجره‌ی لغزان به سراسری» چیدمان متناوب لایه‌های تنک و اتنشن کامل در یک پشته برای حفظ کیفیت هم‌زمان با مصرف حافظه‌ی کمتر.

برای مطالعه‌ی بیشتر

سؤالات متداول

اتنشن پنجره‌ی لغزان (SWA) چگونه مصرف حافظه کش KV را کاهش می‌دهد؟

در SWA هر توکن فقط به یک پنجره‌ی ثابت از توکن‌های پیشین خود (مثلاً 1024 توکن) اتنشن دارد؛ بنابراین نیازی به نگه‌داری کل تاریخچه توالی در کش KV نیست و تنها ذخیره‌ی کلیدها و مقادیر همان پنجره در حافظه کفایت می‌کند.

چرا مدل‌ها به جای استفاده‌ی خالص از SWA، آن را با لایه‌های سراسری ترکیب می‌کنند؟

مدل‌هایی که فقط از SWA استفاده می‌کنند در بازیابی اطلاعات در فواصل بسیار طولانی ضعیف می‌شوند. با چیدمان متناوب (مانند نسبت 5 لایه SWA به 1 لایه سراسری در Gemma 3)، ضمن کنترل مصرف حافظه، میدان دید و کیفیت بازیابی در طول متن حفظ می‌شود.

سینک اتنشن چیست و اتنشن تفاضلی چگونه آن را برطرف می‌کند؟

چون سافت‌مکس مجموع سطرها را برابر 1 می‌کند، توکن‌های کم‌اهمیت وزن اضافه را روی اولین توکن‌ها خالی می‌کنند که به آن سینک اتنشن می‌گویند. اتنشن تفاضلی با محاسبه‌ی دو نقشه‌ی اتنشن و تفریق آن‌ها، اثر این تمرکز ناخواسته را حذف کرده و وزن‌ها را به توکن‌های مهم اختصاص می‌دهد.

ما رو توی گوگل بیشتر ببین

با افزودن نکست ادمین به «منابع ترجیحی» گوگل، درس‌ها و مطالب ما را در بخش اخبار برتر جستجو بیشتر می‌بینید.