تصور کنید مدلی با ۷۰ میلیارد پارامتر بهراحتی با دقت ۴ بیتی روی سختافزار شما اجرا میشود، اما درست در لحظه تبدیل و کوانتش، کل سیستم شما کرش میکند. این پارادوکس حافظه رخ میدهد چون کوانتش یک بار کاری کاملاً مجزا با اوج مصرفی بسیار بالاتر از استنتاج است. این موضوع در یک راهنمای کاربردی که در ۱۵ اوت ۲۰۲۶ از طریق وبسایت dev.to منتشر شد، بهطور مفصل بررسی شده است.
شناسایی نقطه شکست
برای اکثر توسعهدهندگان، اولین نشانه شکست، یک کرش ناگهانی است. شکل این شکست بستگی به این دارد که کدام بخش از حافظه تمام شده است. در پردازندههای گرافیکی (GPU)، این اتفاق بهصورت خطای torch.OutOfMemoryError: CUDA out of memory ظاهر میشود. برای مثال، ممکن است سیستمی سعی کند ۳.۰۶ گیگابایت حافظه تخصیص دهد، در حالی که از مجموع ظرفیت ۷۹.۱۵ گیگابایتی، تنها ۱.۲۸ گیگابایت فضای خالی باقی مانده است. این نوع خرابیهای بحرانی گاهی در ابزارهای نظارتی استاندارد بهدرستی نمایش داده نمیشوند، همانطور که در تحلیل ما درباره فریب شاخصهای بهرهوری GPU بررسی کردیم که چگونه معیارهای ظاهری میتوانند وضعیت واقعی سختافزار را بپوشانند.
در میزبانهای لینوکسی، سیستم اغلب هیچ Traceback یا ورودی در لاگهای اپلیکیشن ارائه نمیدهد و صرفاً کلمه «Killed» را چاپ میکند؛ این یعنی OOM Killer (قاتل حافظه) در هسته لینوکس برای جلوگیری از فروپاشی کل سیستم، فرآیند را متوقف کرده است. برای تأیید این موضوع، کاربران باید دستور dmesg -T | tail یا journalctl -k | tail را اجرا کنند تا ثبتهای هسته را ببینند که کدام فرآیند انتخاب شده و در آن لحظه چه مقدار حافظه مصرف میکرده است. در سیستمعامل مکاواس (macOS)، معادل این اتفاق در لاگهای سیستم ثبت میشود و شل گزارش zsh: killed را ارائه میدهد.
همچنین بسیار حیاتی است که بدانید فرآیند در کدام مرحله متوقف شده است. کوانتش در چندین فاز اجرا میشود: ابتدا بارگذاری (Load)، سپس کالیبراسیون (Calibrate)، سپس کوانتش لایه به لایه (Quantize layer by layer) و در نهایت ذخیرهسازی (Save). نام فازی که در آن کرش رخ میدهد، معمولاً نام علت اصلی شکست است.
چرا هزینه کوانتش بیشتر از اجرای مدل است؟
برای درک این موضوع، باید به آنچه در حافظه مقیم (Resident) میماند نگاه کنید. در زمان استنتاج (Inference)، سیستم بهسادگی یک وزن کوانتیده شده را میخواند و از آن استفاده میکند. اما در زمان کوانتش، سیستم باید تصمیم بگیرد که آن وزن «باید» چه مقداری باشد؛ این کار مستلزم داشتن وزنهای اصلی با دقت کامل (Full-precision) و ابزارهای محاسباتی برای یافتن بهینهترین مقادیر است.
طبق گزارش dev.to، سه عامل اصلی باعث جهش حافظه در زمان کوانتش میشوند که هرگز در زمان استنتاج بهطور همزمان وجود ندارند:
- وزنهای منبع (Source Weights): شما معمولاً کوانتش را از دقت ۱۶ بیتی (bf16) شروع میکنید. یک مدل ۷۰ میلیاردی در حالت bf16 حدود ۷۰e9 × ۲ = ۱۴۰e9 بایت، یا تقریباً ۱۳۰ گیگابایت فضا میگیرد، پیش از آنکه هر عملیات دیگری آغاز شود. در واقع ورودی تقریباً چهار برابر بزرگتر از خروجی است.
- فعالسازهای کالیبراسیون (Calibration Activations): متدهای دادهمحور، متنهای نمونه را از مدل عبور میدهند. این کار باعث ایجاد دستهای (Batch) از تانسورهای واقعی میشود که اندازه آنها حاصلضرب طول توالی در اندازه دسته و در بعد پنهان (Hidden Dimension) است.
- آمار متد (Method Statistics): الگوریتم خاصی که استفاده میکنید، اوج مصرف حافظه را تعیین میکند. وقتی خروجی در حال اسمبل شدن است، نقطه اوج مصرف حافظه بهطور قابل توجهی بالاتر از هر دو مدل ورودی و خروجی قرار میگیرد.
GPTQ و مسئله هسین (Hessian)
متد GPTQ بهویژه گران است زیرا وزنهای کوانتیده هر لایه را از طریق به حداقل رساندن خطا در خروجی آن لایه انتخاب میکند. این کار نیازمند یک ماتریس از آمارهای مرتبه دوم روی ورودیهای لایه است که به نام «هسین» شناخته میشود.
برای یک لایه خطی با بعد ورودی (d)، این ماتریس d × d است که در قالب float32 جمعآوری و معکوس (Invert) میشود. محاسبات برای مدلهای بزرگ بسیار سنگین است:
- اگر d = ۸۱۹۲ باشد، ماتریس ۸۱۹۲ × ۸۱۹۲ × ۴ بایت = ۲۶۸,۴۳۵,۴۵۶ بایت (حدود ۲۵۶ مگابایت) است.
- اگر d = ۱۶۳۸۴ باشد، ماتریس ۱۶۳۸۴ × ۱۶۳۸۴ × ۴ بایت = ۱,۰۷۳,۷۴۱,۸۲۴ بایت (۱ گیگابایت) است.
این مقدار برای «هر لایه» است و معکوسسازی چولسکی (Cholesky inversion) به فضای کاری اضافی نیاز دارد. چون این هزینه بهصورت مربعی (Quadratic) با عرض مدل رشد میکند، دو برابر شدن بعد پنهان، آمار را چهار برابر میکند. این توضیح میدهد چرا شکستها بیشتر در مرحله معکوسسازی رخ میدهند تا مرحله بارگذاری، و چرا مدلهایی با ابعاد Feed-forward عریض، سختتر از مدلهایی با تعداد پارامتر کلی بیشتر کوانتیده میشوند.
مسیرهای جایگزین: AWQ و GGUF
متد AWQ متفاوت عمل میکند. این روش بهجای ساختن یک ماتریس معکوس هسین، از بزرگی فعالسازها برای جستجوی مقیاسهای هر کانال استفاده میکند. در نتیجه، اوج مصرف حافظه آن توسط دسته کالیبراسیون هدایت میشود نه یک ماتریس d x d. اگر GPTQ در حافظه شما جا نمیشود و دادههای کالیبراسیون دارید، متدهای آگاه به فعالساز (Activation-aware) مانند AWQ اغلب گزینه ارزانتری روی همان سختافزار هستند، هرچند آنها هم حالتهای شکست خاص خود را دارند (مانند خطای Assertion در اندازه دسته AWQ).
کوانتش به سبک Bitsandbytes (گرد کردن به نزدیکترین عدد) سبکترین گزینه است. این روش اصلاً مرحله کالیبراسیون ندارد و بنابراین هیچ جهش حافظهای ایجاد نمیکند؛ به همین دلیل روی سختافزاری کار میکند که سایرین شکست میخورند، هرچند این موضوع به قیمت کاهش کیفیت تمام میشود. در حوزههای دیگر مانند بازیابی اطلاعات، استفاده از کوانتش باینری برای کاهش حافظه ایندکسهای برداری رویکرد مشابهی در بهینهسازی فضا دارد، هرچند با چالشهای متفاوتی در حفظ کیفیت دست و پنجه نرم میکند.
مسیر GGUF در llama.cpp بهگونهای متفاوت شکست میخورد. این روش از GPU یا کالیبراسیون استفاده نمیکند، به این معنی که در رم سیستم (Host RAM) شکست میخورد. محدودیت اصلی، فایل ورودی است؛ تبدیل یک چکپوینت ۷۰ میلیاردی در دقت ۱۶ بیتی، فایلی با حجم حدود ۱۳۰ گیگابایت تولید میکند و کوانتش آن فایل، نیازمند رمی در همین ابعاد است.
علاوه بر این، مسیر GGUF اغلب شامل سه نقطه اوج حافظه مجزا روی یک ماشین است:
۱. مرحله تبدیل از طریق convert_hf_to_gguf.py.
۲. فرآیند کوانتش از طریق llama-quantize.
۳. مرحله llama-imatrix برای بهبود کیفیت در بیتهای پایین، که مدل کوانتیده نشده را روی متن کالیبراسیون اجرا میکند و به همان مقدار حافظه استنتاج روی مدل کوانتیده نشده نیاز دارد.
استراتژیهایی برای گنجاندن عملیات در حافظه
اگر با دیوارههای حافظه برخورد کردید، راهنما چندین تغییر فنی را بر اساس نوع حافظه تمام شده پیشنهاد میکند:
- رم سیستم (Host RAM): پیش از افزودن سختافزار، فضای Swap روی دیسکهای سریع NVMe اضافه کنید. کوانتش بیشتر محدود به پهنای باند (Throughput-bound) است تا تأخیر (Latency-bound)، بنابراین انتقال داده به دیسک کند است اما تضمین میکند که کار بهجای مرگ در ۱۰ دقیقه، شبانه به پایان برسد.
- حافظه گرافیکی (GPU VRAM): اندازه دسته کالیبراسیون و طول توالی را کاهش دهید. اینها ضربکنندههای مستقیم روی عبارت فعالساز هستند و بهندرت باعث تغییر قابل توجه در وزنهای نهایی میشوند.
- برونسپاری (Offloading): از قابلیت Offloading کتابخانه یا Device Map برای انتقال به CPU استفاده کنید. پردازش لایه به لایه همان چیزی است که کوانتش مدلی بزرگتر از GPU را ممکن میسازد؛ این کار تضمین میکند که فقط لایهای که در حال کوانتش است روی دستگاه مقیم باشد.
در نهایت، بهینهترین راه اغلب اجتناب کامل از این فرآیند است. اکثر مدلهای محبوب پیش از این به صورت چکپوینتهای GGUF یا AWQ منتشر شدهاند و برای کاربر نهایی نیازی به حافظه کوانتش ندارند. انجام این کار را فقط برای مدلهای Fine-tune شده یا مدلهایی که هیچکس پوشش نداده است رزرو کنید.
این تغییر در درک موضوع، کوانتش را از یک شکست «جعبه سیاه» به یک مسئله مهندسی قابل پیشبینی تبدیل میکند. با شناسایی اینکه گلوگاه ماتریس هسین است یا بارگذاری وزنهای منبع، توسعهدهندگان میتوانند متد مناسب را برای بودجه VRAM خود انتخاب کنند.
پیش از ارتقای سختافزار، آخرین یادداشتهای انتشار (Release Notes) کتابخانه کوانتش خود را بررسی کنید، زیرا رفتارهای Streaming و Offloading بهسرعت در حال بهبود هستند. متدی که سال گذشته جا نمیشد، ممکن است اکنون جای بگیرد.
گام بعدی شما
- اگر با خطای Killed در لینوکس مواجه شدید، فوراً
dmesgرا چک کنید تا بفهمید آیا مشکل از رم است یا VRAM. - برای مدلهای عریض، بهجای GPTQ از متد AWQ استفاده کنید تا از محاسبه ماتریس هسین و کرش سیستم جلوگیری کنید.
- پیش از شروع کوانتش مدلهای بزرگ، فضای Swap سیستم خود را افزایش دهید تا از توقف ناگهانی فرآیند در ساعات پایانی جلوگیری شود.
اما داستان سختافزاری این تحول حتی شگفتانگیزتر است — به تحلیل ما درباره تراشههای Blackwell مراجعه کنید.




گفتگو