اگر یک مدل زبانی را روی GPU اجرا کرده باشید، احتمالاً دیدهاید که با طولانی شدن متن یا افزایش کاربران همزمان، حافظه کارت گرافیک بهسرعت پر میشود، حتی وقتی خود مدل بهراحتی در آن جا شده است. مقصر اصلی KV Cache است. در این مقاله توضیح میدهیم KV Cache چیست، چرا وجود دارد، چطور حجم آن را حساب کنیم و با چه تکنیکهایی مصرف آن را کم کنیم.
فهرست مطالب
تولید متن در LLM چگونه انجام میشود؟
مدلهای زبانی متن را توکن به توکن تولید میکنند. برای تولید هر توکن جدید، مدل باید به همه توکنهای قبلی، چه متن ورودی و چه توکنهایی که خودش تولید کرده، «توجه» (Attention) کند. در مکانیزم Attention، برای هر توکن در هر لایه دو بردار به نام Key و Value محاسبه میشود و توکن جدید با مقایسه خود با Keyهای قبلی، تصمیم میگیرد از Valueهای کدام توکنها چقدر استفاده کند.
KV Cache چیست؟
بردارهای Key و Value هر توکن، بعد از محاسبه تغییر نمیکنند. پس منطقی است که آنها را یک بار حساب کنیم و در حافظه نگه داریم، بهجای اینکه برای تولید هر توکن جدید، دوباره برای همه توکنهای قبلی محاسبه شوند. این حافظه نگهداری همان KV Cache است.
بدون KV Cache، هزینه محاسبه هر توکن جدید با طول متن رشد میکند و تولید پاسخهای طولانی بهشدت کند میشود. با KV Cache، محاسبات تکراری حذف میشوند، اما در ازای آن حافظه GPU مصرف میشود. این همان معامله کلاسیک «حافظه در برابر سرعت» است.
تولید متن دو مرحله دارد:
- Prefill: کل متن ورودی یکجا پردازش و KV Cache آن ساخته میشود. این مرحله محاسباتی است و زمان تا اولین توکن (TTFT) را تعیین میکند.
- Decode: توکنها یکییکی تولید میشوند و در هر مرحله، Key و Value توکن جدید به کش اضافه میشود. این مرحله به پهنای باند حافظه وابسته است.
فرمول محاسبه حجم KV Cache
حجم KV Cache بهازای هر توکن:
۲ × تعداد لایهها × تعداد هدهای KV × ابعاد هر هد × بایتهای دقت
و حجم کل:
حجم بهازای هر توکن × طول متن (Context) × تعداد درخواستهای همزمان
عدد ۲ برای این است که هم Key و هم Value ذخیره میشوند. در دقت FP16 هر عدد ۲ بایت است. نکته مهم این است که در فرمول، تعداد هدهای KV میآید، نه تعداد کل هدهای Attention؛ این تفاوت در مدلهای جدید بسیار تعیینکننده است.
مثال عددی
مدل Llama ۳.۱ 8B این مشخصات را دارد: ۳۲ لایه، ۸ هد KV، ابعاد هر هد ۱۲۸.
۲ × ۳۲ × ۸ × ۱۲۸ × ۲ بایت = ۱۳۱٬۰۷۲ بایت ≈ ۱۲۸ کیلوبایت بهازای هر توکن
| طول متن | یک کاربر | ۱۰ کاربر همزمان |
|---|---|---|
| ۴٬۰۰۰ توکن | حدود ۰٫۵ گیگابایت | حدود ۵ گیگابایت |
| ۳۲٬۰۰۰ توکن | حدود ۴ گیگابایت | حدود ۴۰ گیگابایت |
| ۱۲۸٬۰۰۰ توکن | حدود ۱۶ گیگابایت | حدود ۱۶۰ گیگابایت |
برای مقایسه، وزنهای همین مدل در FP16 حدود ۱۶ گیگابایت هستند. یعنی یک کاربر با متن ۱۲۸ هزار توکنی، به اندازه خود مدل حافظه برای KV Cache مصرف میکند. به همین دلیل در محاسبه VRAM مورد نیاز، فقط اندازه مدل کافی نیست.
GQA؛ چرا مدلهای جدید کش کوچکتری دارند؟
در Attention کلاسیک (Multi-Head Attention)، هر هد Attention، Key و Value جداگانه دارد. مدلهای جدید مثل Llama ۳، Qwen ۲.۵ و Mistral از Grouped-Query Attention استفاده میکنند: چند هد Query یک جفت Key و Value مشترک دارند. در Llama ۳.۱ 8B، ۳۲ هد Query فقط ۸ هد KV دارند؛ یعنی KV Cache چهار برابر کوچکتر از حالتی است که هر هد KV جداگانه داشت. همین طراحی است که پشتیبانی از متنهای طولانی را عملی کرده است.
روشهای کاهش مصرف KV Cache
- محدود کردن طول متن: اگر کاربرد شما به ۱۲۸ هزار توکن نیاز ندارد، طول متن را محدود کنید. سادهترین و مؤثرترین روش است.
- کوانتایز کش: ذخیره KV Cache در دقت ۸ بیتی (FP8 یا Q8) حجم آن را تقریباً نصف میکند، معمولاً با افت کیفیت ناچیز. این کار با کوانتایز وزنهای مدل متفاوت است و میتوان هر دو را همزمان انجام داد.
- PagedAttention: روش سنتی، برای هر درخواست یک بلوک پیوسته به اندازه حداکثر طول متن رزرو میکرد و بخش زیادی از حافظه هدر میرفت. vLLM با الهام از صفحهبندی حافظه در سیستمعاملها، کش را در بلوکهای کوچک و به اندازه نیاز واقعی تخصیص میدهد و هدررفت را تقریباً به صفر میرساند. نتیجه، جا دادن درخواستهای همزمان بسیار بیشتر روی همان GPU است.
- Prefix Caching: اگر بسیاری از درخواستها با یک پیام سیستمی یا سند مشترک شروع میشوند، KV Cache آن بخش یک بار ساخته و بین درخواستها به اشتراک گذاشته میشود.
KV Cache در عمل: Ollama و vLLM
در Ollama، طول متن با پارامتر num_ctx و تعداد درخواست همزمان با OLLAMA_NUM_PARALLEL تعیین میشود و حافظه کش متناسب با حاصلضرب این دو رزرو میشود. کوانتایز کش با متغیر OLLAMA_KV_CACHE_TYPE=q8_0 فعال میشود که به فعال بودن Flash Attention (OLLAMA_FLASH_ATTENTION=1) نیاز دارد.
در vLLM، پارامتر --gpu-memory-utilization تعیین میکند چه سهمی از VRAM استفاده شود و هرچه بعد از بارگذاری مدل باقی بماند، به KV Cache اختصاص مییابد. vLLM در لاگ شروع، ظرفیت کش را به توکن اعلام میکند. با --max-model-len طول متن و با --kv-cache-dtype fp8 دقت کش را تنظیم کنید.
مقایسه کامل این دو موتور را در Ollama یا vLLM و نکات سرویسدهی به چند کاربر را در اجرای LLM برای چند کاربر همزمان بخوانید.
سوالات متداول
آیا افزایش طول پرامپت مصرف KV Cache را بالا میبرد؟
بله. حجم KV Cache با مجموع طول ورودی و خروجی بهصورت خطی رشد میکند. دو برابر شدن طول متن، دو برابر شدن حافظه کش را به همراه دارد.
چرا مدل در VRAM جا میشود اما با خطای کمبود حافظه مواجه میشوم؟
چون حافظه لازم برای KV Cache، بهخصوص با طول متن بالا یا چند درخواست همزمان، به حجم مدل اضافه میشود. طول متن یا تعداد درخواست همزمان را کاهش دهید یا کش را کوانتایز کنید.
آیا KV Cache بین درخواستها حفظ میشود؟
بهطور پیشفرض نه، اما با Prefix Caching بخشهای مشترک ابتدای درخواستها، مثل پیام سیستمی، دوباره استفاده میشوند و زمان پاسخ کاهش مییابد.
آیا کوانتایز KV Cache کیفیت پاسخ را کم میکند؟
در دقت ۸ بیتی افت کیفیت معمولاً قابل توجه نیست. دقتهای پایینتر مثل ۴ بیتی ممکن است در متنهای طولانی روی دقت اثر بگذارند.
VRAM کافی برای متنهای طولانی و کاربران همزمان
با سرورهای GPU تراست هاست و کارتهایی با ۴۸ و ۸۰ گیگابایت حافظه، اسناد طولانی را پردازش کنید و به کاربران بیشتری همزمان پاسخ دهید.