KV cache (ذاكرة Key-Value المؤقتة) هي تقنية لتحسين الاستدلال يخزّن فيها نموذج لغوي كبير موترات الانتباه Key وValue المحسوبة في كل طبقة للرموز التي سبق معالجتها. أثناء فك الترميز ذاتي الانحدار، يعيد النموذج استخدام تلك الحالات المخزنة بدلًا من إعادة حساب جميع الرموز السابقة في كل خطوة، مما يقلل الحسابات المتكررة مع نمو التسلسلات.
سواء كنت تستدعي واجهات API مستضافة أو تشغّل نماذج مفتوحة الأوزان على وحدات GPU الخاصة بك، تحتل KV cache موقعًا محوريًا في زمن الاستجابة والضغط على الذاكرة ومعدل المعالجة. ويساعد فهم عملها على تفسير اختلاف الإحساس بالوقت حتى أول رمز عن سرعة البث، وسبب استهلاك السياقات الطويلة قدرًا كبيرًا من VRAM، وضرورة تطابق البادئات تمامًا عند التخزين المؤقت للمطالبات عبر طلبات API.
كيف يعمل الانتباه دون KV cache؟
من دون KV cache، يجب على Transformer ذاتي الانحدار إعادة حساب إسقاطات الانتباه للتسلسل بأكمله من البداية في كل مرة يولّد فيها رمزًا جديدًا في نموذج ذكاء اصطناعي.
في كل طبقة من مفكك ترميز Transformer، يسقط الانتباه الذاتي تمثيل كل رمز إدخال على ثلاثة متجهات:
- Query ($Q$): يمثّل الموضع الحالي الذي يبحث عن السياق ذي الصلة.
- Key ($K$): يمثّل ما يقدمه كل موضع لمطابقته مع Query.
- Value ($V$): يحتوي على المعلومات التي تُجمع بعد حساب أوزان الانتباه من الضرب القياسي بين Query وKey.
لأن النماذج اللغوية التي تستخدم مفكك الترميز فقط تولّد النص رمزًا واحدًا في كل مرة، لا يتطلب توليد الرمز $t+1$ سوى أن ينتبه متجه Query للرمز الأحدث إلى Key وValue للرموز من $1$ إلى $t$. إذا تخلّصت بيئة التشغيل من قيم Key وValue بعد إصدار الرمز $t$، فعلى الخطوة $t+1$ تمرير كل رمز في البادئة عبر جميع الطبقات مرة أخرى لمجرد إعادة إنشاء موترات $K$ و$V$ نفسها تمامًا. ومع ازدياد طول المطالبة والمخرجات، ينمو هذا العمل المتكرر تربيعيًا عبر عملية التوليد الكاملة.
لماذا يتيح الانتباه السببي التخزين المؤقت لـ KV؟
يضمن الحجب السببي اعتماد موترات Key وValue الخاصة بالرمز على الرموز التي سبقته فقط، وليس على الرموز التي تُولّد لاحقًا أبدًا.
في مفكك ترميز سببي يعمل من اليسار إلى اليمين، يُمنع الرمز $i$ من الانتباه إلى أي موضع $j > i$. وبما أن المعلومات لا تتدفق إلا إلى الأمام في التسلسل:
- يثبت تمثيل الرمز $i$ في الطبقة $l$ تمامًا بمجرد معالجة الرموز $1 \dots i$.
- لا تؤدي إضافة الرمز $i+1$ إلى نهاية التسلسل إلى تغيير الحالات المخفية أو Key أو Value للمواضع $1 \dots i$ في أي طبقة.
- تستطيع بيئة التشغيل إضافة المتجهين المحسوبين حديثًا $K_{i+1}$ و$V_{i+1}$ إلى مخزن مؤقت لكل طبقة، هو KV cache، وتمرير الرمز الأحدث وحده إلى خطوة فك الترميز التالية.
كما يوضح دليل KV cache في Hugging Face Transformers ونظرة عامة على بنية الذاكرة المؤقتة في Transformers، يحوّل التخزين المؤقت كل خطوة فك ترميز من معالجة للتسلسل الكامل إلى تحديث تزايدي لرمز واحد يقرأ Key وValue السابقة من الذاكرة.
كيف تختلف طريقة استخدام Prefill وDecode لـ KV cache؟
ينقسم استدلال LLM إلى مرحلتين منفصلتين، Prefill وDecode، تتفاعلان مع KV cache بطرق مختلفة جذريًا وتواجهان اختناقات مختلفة في العتاد.
| مرحلة الاستدلال | ما يحدث لـ KV cache | اختناق العتاد الرئيسي | المقياس المرئي للمستخدم |
|---|---|---|---|
| Prefill (معالجة المطالبة) | تحسب $K$ و$V$ لجميع رموز المطالبة بالتوازي وتملأ الذاكرة المؤقتة الأولية في كل طبقة. | محدود بالقدرة الحسابية، بسبب عمليات ضرب المصفوفات الكبيرة على المطالبة كاملة. | الوقت حتى أول رمز (TTFT). |
| Decode (توليد الرموز) | تحسب $Q$ و$K$ و$V$ لرمز جديد واحد في كل مرة، وتضيف مدخل $K$/$V$ الجديد، وتقرأ السجل المخزن كاملًا. | محدود بعرض نطاق الذاكرة، بسبب تحميل أوزان النموذج وحالات KV المتزايدة في كل خطوة. | زمن الاستجابة بين الرموز / سرعة بث المخرجات. |
كما يشرح المقال التقني من NVIDIA عن Prefill وDecode والتخزين المؤقت لـ KV، تعالج مرحلة Prefill جميع رموز الإدخال في الوقت نفسه. وبما أن المطالبة كاملة معروفة مسبقًا، يمكن لوحدات GPU تجميع الإسقاطات في عمليات مصفوفات كثيفة، مما يشغّل وحدات الحساب بكامل طاقتها ويملأ KV cache في مرور واحد. تزيد المطالبات الطويلة، مثل سياقات المستندات المتعددة في مسارات التوليد المعزز بالاسترجاع (RAG)، عمل Prefill مباشرة وترفع الوقت حتى أول رمز.
بمجرد إصدار أول رمز للمخرجات، يدخل الاستدلال مرحلة Decode. تعالج كل خطوة رمزًا جديدًا واحدًا فقط لكل تسلسل، وتضيف متجهي Key وValue الخاصين به إلى الذاكرة المؤقتة، وتقرأ جميع موترات $K$ و$V$ المخزنة سابقًا من ذاكرة GPU عالية النطاق الترددي (HBM) لحساب الانتباه. ونظرًا إلى انخفاض كثافة العمليات الحسابية في كل خطوة، بينما تزداد قراءات الذاكرة مع طول التسلسل وحجم الدفعة، يصبح فك الترميز سريعًا محدودًا بعرض نطاق الذاكرة وVRAM المتاحة بدلًا من قدرة الحساب الخام بوحدة FLOPs.
ما مقدار الذاكرة التي تستخدمها KV cache؟
يزداد حجم الذاكرة الذي تشغله KV cache خطيًا مع طول التسلسل وحجم الدفعة وعدد الطبقات وعدد رؤوس انتباه Key-Value.
انطلاقًا من أشكال الموترات المستخدمة في ذاكرات Transformer المؤقتة القياسية، حيث تخزّن كل طبقة موتر Key واحدًا وموتر Value واحدًا بأبعاد تشمل الدفعة ورؤوس KV وطول التسلسل وبُعد الرأس، يمكن التعبير عن حجم الذاكرة المؤقتة لكل تسلسل رمزيًا كما يلي:
bytes ≈ 2 × layers × kv_heads × head_dim × seq_len × bytes_per_element
يمثّل كل حد في هذه المعادلة عاملًا يضبطه مصممو النماذج ومهندسو الاستدلال للتحكم في الذاكرة:
- 2: يراعي تخزين موترات Key (K) وValue (V) معًا.
- layers: العدد الإجمالي لكتل مفكك ترميز Transformer في النموذج.
- kv_heads وhead_dim: عدد رؤوس انتباه Key/Value في كل طبقة وبُعد كل رأس.
- seq_len: إجمالي الرموز المخزنة مؤقتًا، أي رموز المطالبة والرموز المولّدة حتى الآن.
- bytes_per_element: الدقة العددية للموترات المخزنة، مثل 2 بايت لـ FP16/BF16، أو 1 بايت لتنسيقات 8 بت، أو أجزاء أصغر مع تكميم KV cache بدقة 4 بت مثل NVFP4.
تقليل kv_heads: MHA وMQA وGQA
في Multi-Head Attention التقليدي (MHA)، يملك كل رأس Query رأس Key ورأس Value مخصصين له (kv_heads = query_heads). ومع اتساع نوافذ السياق وزيادة أحجام الدفعات، أصبح تخزين إسقاطات K وV منفصلة لكل رأس اختناقًا كبيرًا في الذاكرة.
يقلل تغييران معماريان قيمة kv_heads مباشرة في المعادلة أعلاه:
- Multi-Query Attention (MQA): قدّمها Noam Shazeer في Fast Transformer Decoding: One Write-Head is All You Need (arXiv:1911.02150)، وتشارك MQA رأس Key واحدًا ورأس Value واحدًا بين جميع رؤوس Query في الطبقة (
kv_heads = 1). ويقلل ذلك حجم KV cache وحركة البيانات عبر عرض نطاق الذاكرة بشدة أثناء فك الترميز. - Grouped-Query Attention (GQA): قدّمها Ainslie وآخرون في GQA: Training Generalized Multi-Query Transformer Models from Multi-Head Checkpoints (arXiv:2305.13245)، وتقسم GQA رؤوس Query إلى G مجموعات، تشترك كل مجموعة منها في رأس Key ورأس Value واحدين. وتقع GQA بين MHA وMQA، فتحافظ على جودة قريبة من Multi-Head Attention مع خفض مساحة KV cache وعرض النطاق المطلوب بما يتناسب مع تقليل المجموعات.
إلى جانب GQA، تقلل محركات الاستدلال قيمة bytes_per_element أيضًا بتكميم موترات K وV المخزنة إلى تنسيقات أقل دقة، مما يتيح استيعاب سياقات أطول أو دفعات متزامنة أكبر في ذاكرة GPU.
كيف تدير محركات الخدمة ذاكرة KV cache على نطاق واسع؟
تدير محركات الخدمة في بيئات الإنتاج ذاكرة KV cache بتقسيم الموترات المتجاورة إلى كتل ثابتة الحجم، حتى تتمكن وحدات GPU من استيعاب مزيد من الطلبات المتزامنة دون إهدار VRAM.
في التطبيقات البسيطة، تخصص بيئة الخدمة مسبقًا جزءًا متجاورًا من ذاكرة GPU بحجم أقصى طول ممكن للتسلسل في كل طلب. ولأن أطوال المخرجات الفعلية غير معروفة مسبقًا، يسبب التخصيص المسبق تجزئة داخلية وخارجية شديدة للذاكرة: تبقى مساحات محجوزة دون استخدام بينما تُمنع الطلبات الجديدة من التنفيذ.
عالج Kwon وآخرون هذا الاختناق باستخدام PagedAttention في Efficient Memory Management for Large Language Model Serving with PagedAttention (arXiv:2309.06180)، وهي الآلية الأساسية وراء vLLM. وتستند إلى الذاكرة الافتراضية وتقسيم الصفحات في أنظمة التشغيل:
- كتل غير متجاورة ثابتة الحجم: تقسّم PagedAttention ذاكرة KV cache لكل طلب إلى كتل ثابتة الحجم تحتوي على Key وValue لعدد محدد من الرموز، وتُربط عبر جدول يطابق الكتل المنطقية بالكتل الفعلية.
- التخصيص عند الطلب: لا تُخصص كتل فعلية جديدة إلا عند توليد رموز جديدة، مما يزيل التجزئة الداخلية عمليًا.
- مشاركة البادئة على مستوى الكتلة: عندما تشترك عدة طلبات في بادئة مطالبة متطابقة، مثل مطالبة النظام أو أمثلة few-shot مشتركة، يمكن لجداول كتلها المنطقية الإشارة إلى كتل KV الفعلية نفسها في الذاكرة، باستخدام النسخ عند الكتابة فقط عندما تتباعد التسلسلات.
في تقييم ورقة vLLM البحثية (arXiv:2309.06180)، حققت إدارة KV القائمة على الصفحات معدل معالجة أعلى بمقدار 2–4× من FasterTransformer وOrca عند زمن استجابة مماثل، بفضل استيعاب دفعات أكبر بكثير في ذاكرة GPU نفسها. وتعتمد أنظمة الخدمة الحديثة، ومنها vLLM ونظام إعادة استخدام KV cache في NVIDIA TensorRT-LLM، على إدارة KV القائمة على الكتل لإعادة استخدام البادئات المشتركة وتعظيم التزامن.
ما الفرق بين KV cache والتخزين المؤقت للمطالبات؟
KV cache هي آلية الاستدلال الأساسية لكل طلب، وتعيد استخدام حالات الانتباه بين خطوات فك الترميز. أما التخزين المؤقت للمطالبات فهو ميزة للخدمة وAPI عبر الطلبات تحتفظ بكتل KV هذه وتعيد استخدامها بين استدعاءات منفصلة تشترك في بادئة متطابقة.
لأن المطورين يصادفون المصطلحين غالبًا عند تحسين زمن الاستجابة والفواتير، فمن المفيد التمييز بين ما يحدث داخل استدعاء توليد واحد وما يحدث عبر طلبات API متعددة:
| الجانب | KV cache (فك الترميز داخل الطلب) | التخزين المؤقت للمطالبات (إعادة استخدام البادئة بين الطلبات) |
|---|---|---|
| النطاق | طلب واحد عبر خطوات توليد الرموز. | طلبات متعددة خلال فترة احتفاظ قصيرة. |
| العمل الذي تتجنبه | إعادة حساب الرموز السابقة داخل الاستجابة نفسها عند فك ترميز كل رمز جديد. | إعادة تشغيل Prefill على بادئات المطالبات الثابتة المتكررة بين استدعاءات منفصلة. |
| دورة الحياة | تُنشأ أثناء Prefill وتُضاف إليها البيانات أثناء Decode، ثم تُحرر أو تُزال عند اكتمال الطلب. | تُحفظ في تخزين الكتل، سواء في ذاكرة GPU أو المضيف، مع سياسة إزالة وTTL للاستدعاءات المستقبلية. |
| الظهور في الفاتورة | مدمجة في الاستدلال القياسي ولا تُفوتر كبند منفصل. | يقدمها مزودو API غالبًا في صورة أسعار مخفضة لرموز الإدخال المخزنة وTTFT أقل. |
يفرض الانتباه السببي قاعدة صارمة للتخزين المؤقت للمطالبات: يجب أن تتطابق البادئة تمامًا بدءًا من أول رمز. نظرًا إلى اعتماد موترات Key وValue لكل رمز على التسلسل الدقيق للرموز السابقة له، فإن تغيير طابع زمني أو معرّف مستخدم أو تعليمة واحدة في بداية المطالبة يُبطل حالات $K$ و$V$ المخزنة لكل الرموز التالية.
للاطلاع على قواعد الاحتفاظ والحدود الدنيا للرموز وهياكل الخصم الخاصة بكل مزود، راجع دليلنا الكامل عن التخزين المؤقت للمطالبات وكيفية تسعيره لدى المزودين.
إرشادات عملية للمطورين والفرق
يؤدي فهم سلوك KV cache مباشرة إلى بنية أفضل للمطالبات وزمن استجابة أكثر استقرارًا وإنفاق أقل على البنية التحتية:
- ضع المحتوى الثابت في بداية المطالبة تمامًا: ضع تعليمات النظام ومخططات الأدوات والمستندات المرجعية المشتركة قبل أي إدخال ديناميكي للمستخدم أو بيانات وصفية خاصة بالطلب. تتيح البادئة المتطابقة لكل من واجهات API المستضافة والمحركات المحلية مثل vLLM أو TensorRT-LLM إعادة استخدام كتل KV المخزنة بدلًا من تنفيذ عمليات Prefill مكررة.
- راقب تأثير طول السياق في التزامن على وحدات GPU المستضافة ذاتيًا: بما أن حجم KV cache ينمو خطيًا مع
seq_len، فإن مضاعفة متوسط طول السياق تخفض إلى النصف عدد الطلبات المتزامنة التي يمكن لذاكرة GPU استيعابها. عند اختيار نماذج مفتوحة الأوزان للاستضافة الذاتية، فضّل البنى التي تستخدم Grouped-Query Attention (GQA) وبيئات التشغيل التي تدعم تخصيص KV بالصفحات وتكميم KV. - اختصر السياق غير الضروري حتى عندما تكون نوافذ السياق ضخمة: يؤدي إدخال سجلات ضخمة في كل جولة إلى زيادة زمن استجابة Prefill والضغط على ذاكرة KV وفواتير الرموز. اجمع بين التخزين المؤقت للبادئات والاسترجاع الموجّه والتلخيص الدوري؛ وهي أساليب أساسية يتناولها دليلنا عن خفض تكاليف API للذكاء الاصطناعي ومقارنة أسعار API للذكاء الاصطناعي.
الأسئلة الشائعة
هل تغيّر KV cache جودة مخرجات النموذج؟
تُكافئ KV cache القياسية المحفوظة بدقة الاستدلال الأصلية للنموذج، مثل FP16 أو BF16، إعادة حساب البادئة في كل خطوة رياضيًا، لأن الحجب السببي يمنع الرموز المستقبلية من تغيير متجهات Key وValue السابقة. ولا تظهر مفاضلات الجودة إلا عند تطبيق تكميم فاقد لـ KV cache، مثل الضغط بدقة 8 بت أو 4 بت، أو إزالة الرموز القديمة لتوفير الذاكرة.
لماذا لا تستطيع KV cache إعادة استخدام رموز في وسط المطالبة إذا تغيرت بدايتها؟
في Transformer سببي، يتضمن تمثيل كل رمز الانتباه إلى جميع الرموز السابقة له في التسلسل. إذا تغيرت الرموز العشرة الأولى من المطالبة، فستتغير أيضًا الحالات المخفية، وبالتالي إسقاطات Key وValue، بدءًا من الرمز الحادي عشر. ولأن هذه الحالات تعتمد على البادئة الدقيقة التي تسبقها، لا تستطيع محركات الخدمة إعادة استخدام كتل KV المخزنة بين الطلبات إلا عندما تتطابق البادئة من البداية.
هل تحتوي KV cache على أوزان النموذج؟
لا. أوزان النموذج معاملات ثابتة تُكتسب أثناء التدريب وتشترك فيها جميع الطلبات التي تعالجها GPU. أما KV cache فتحتوي على موترات تنشيط ديناميكية خاصة بكل طلب، وهي إسقاطات Key وValue للرموز الموجودة حاليًا في السياق. يجب أن تتسع ذاكرة GPU الإجمالية أثناء الاستدلال لأوزان النموذج الثابتة وتنشيطات بيئة التشغيل المؤقتة وذاكرات KV cache الديناميكية لجميع الطلبات النشطة في الدفعة.
لماذا يتباطأ توليد مزيد من رموز المخرجات عند ارتفاع التزامن؟
أثناء مرحلة Decode، تتطلب كل خطوة لتوليد رمز جديد قراءة موترات Key وValue المخزنة لجميع الرموز السابقة في كل طلب نشط بالدفعة. ومع ازدياد أطوال التسلسلات وعدد الطلبات المتزامنة، يزداد حجم بيانات KV المنقولة من ذاكرة GPU في كل خطوة، مما يستنفد عرض نطاق الذاكرة ويجبر المجدول على الحد من حجم الدفعة أو انتظار قراءات الذاكرة.
المصادر
- Hugging Face Transformers — KV Cache Strategies and Optimization
- Hugging Face Transformers — Cache Explanation and Architecture
- NVIDIA Technical Blog — Optimizing Inference for Long Context and Large Batch Sizes with NVFP4 KV Cache
- Kwon et al. (2023) — Efficient Memory Management for Large Language Model Serving with PagedAttention (arXiv:2309.06180)
- Ainslie et al. (2023) — GQA: Training Generalized Multi-Query Transformer Models from Multi-Head Checkpoints (arXiv:2305.13245)
- Shazeer (2019) — Fast Transformer Decoding: One Write-Head is All You Need (arXiv:1911.02150)
- NVIDIA TensorRT-LLM Documentation — KV Cache System


