尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

从零搭建一个 AI Infra 实验室⑦:12GB 显存到底能跑多大模型——模型精度与量化

从零搭建一个 AI Infra 实验室⑦:12GB 显存到底能跑多大模型——模型精度与量化 上一篇我们终于把一次 LLM 推理真正拆开了Prompt ↓ Tokenizer ↓ Input Tokens ↓ Prefill ↓ KV Cache ↓ Decode ↓ Output Tokens同时也知道一次推理真正占用 GPU 显存的并不只有模型本身GPU VRAM │ ├── Model Weights ├── KV Cache ├── Activations ├── Runtime Buffers └── Framework / CUDA Overhead这马上带来了一个对我的实验室非常现实的问题。这台机器使用的是NVIDIA GeForce RTX 3060 12GB VRAM那么12GB 显存到底能跑多大的 LLM前面我们已经算过7B Model × FP16 × 2 Bytes / Parameter ≈ 14GB也就是说一个 7B 模型如果以 FP16 保存光模型权重理论上就已经超过 12GB。更不用说还要给KV Cache Runtime Buffer CUDA留下空间。但是现实中我们又经常看到7B 8B 14B甚至更大的模型运行在消费级显卡上。这是怎么做到的答案就是前面一直提到、但还没有真正展开的Quantization 量化这一篇我们就把FP32 FP16 BF16 INT8 INT4和Parameter Count Model Weight GPU VRAM真正串起来。最后在 RTX 3060 12GB 上用同一个 7B 模型的不同精度与量化版本看看显存到底会发生什么。一、12GB 能跑多大模型这个问题其实少了一个条件第一次接触本地大模型很容易问12GB 显存能跑 7B 吗 12GB 能跑 14B 吗但这个问题其实并不完整。因为7B只告诉我们Parameter Count 参数量并没有告诉我们每一个 Parameter 到底要占多少空间前面已经建立过Parameter Count × Parameter dtype ↓ Weight Size ↓ RAM / VRAM所以更准确的问题应该是12GB 显存能运行多大参数量、什么精度、什么量化方式的模型同样一个7B Model如果分别使用FP32 FP16 INT8 INT4显存需求可以完全不同。二、先把 FP32、FP16、BF16、INT8、INT4 放到一张表里第四篇已经实验过FP32 4 Bytes FP16 2 Bytes BF16 2 Bytes所以这里不再重新展开Sign Exponent Fraction这些浮点格式细节。这一篇更关心的是一个 Parameter 大约需要占多少空间。可以先建立这样一张非常粗略、但很好用的表类型位宽每个 Parameter 理论空间FP3232 bit4 BytesFP1616 bit2 BytesBF1616 bit2 BytesINT88 bit约 1 ByteINT44 bit约 0.5 ByteFP16 和 BF16 虽然都是16 bit但数值表示方式并不相同。它们在数值范围 有效精度之间做了不同取舍。不过对于这一篇关心的Weight Storage来说两者都是2 Bytes / Parameter所以在粗略估算模型 Weight 时可以放在同一个数量级理解。真正让模型容量边界继续发生巨大变化的是INT8 INT4三、参数量乘一下12GB 的边界马上就出来了如果暂时只计算模型 WeightWeight Size ≈ Parameter Count × Bytes Per Parameter就可以得到模型规模FP32FP16 / BF16INT8INT40.5B2GB1GB0.5GB0.25GB1.5B6GB3GB1.5GB0.75GB3B12GB6GB3GB1.5GB7B28GB14GB7GB3.5GB14B56GB28GB14GB7GB32B128GB64GB32GB16GB对于RTX 3060 12GB一下就很直观了。7B FP16≈ 14GB光 Weight 理论上就超过显存。但如果换成 INT87B × 1 Byte ≈ 7GB一下进入了合理范围。继续到 INT47B × 0.5 Byte ≈ 3.5GB显存空间就更加宽裕。甚至14B INT4 ≈ 7GB理论上都开始进入 12GB 的范围。但这里一定要注意这些只是理论 Weight Size不是模型实际运行时的总显存。真实量化比简单的16 bit → 8 bit → 4 bit复杂得多。四、量化到底在做什么假设模型里原来有这样几个 Weight-1.82 -0.47 0.13 0.86 2.34如果简单粗暴地Float ↓ Integer变成-2 0 0 1 2显然会丢掉大量信息。真实量化并不是这样。可以先粗略理解成Original Float Weight ↓ 寻找一个缩放关系 ↓ Scale / Zero Point ↓ Low-bit Integer也就是说模型不再直接保存0.86413这样的高精度 Weight而可能保存一个4-bit Integer再配合Scale Zero Point去近似表达原来的数字。本质上是在做Higher Precision Weight ↓ 允许一定误差 ↓ Lower-bit Weight换来的则是更小的模型 更低的显存 更少的数据搬运 更低的 Memory Bandwidth 压力这就是 Quantization 最核心的思想。Hugging Face 当前的 Transformers 文档也把量化概括为使用更低精度的数据表示 Weight / Activation从而降低内存与计算成本。五、为什么 INT4 只有 16 个状态还能保存几十亿个 Weight4 bit 一共只有2^4 16种状态。第一次看到这里我也很容易产生一个疑问一个 7B 模型里的几十亿个 Weight难道最后只能从 16 个数字里选当然不是这么简单。真实量化通常会把 Weight 划成很多Group例如Weight Tensor │ ├── Group 1 → Scale ├── Group 2 → Scale ├── Group 3 → Scale └── ...每一组都有自己的缩放关系。所以某个4-bit Value表达的是这个 Weight 在当前 Group 的数值范围中大概位于哪里。于是一个真实 4-bit 模型里保存的不只是4-bit Weight还有可能包括Scale Zero Point Group Information Metadata所以以后经常会看到7B × 0.5 Byte ≈ 3.5GB但是下载下来的 4-bit 模型却明显大于 3.5GB。这是正常的。INT4 并不意味着整个模型中的每一个 Byte 都机械地缩小成原来的四分之一。六、AWQ、GPTQ、GGUF 到底是什么继续找量化模型时很快又会遇到AWQ GPTQ GGUF它们很容易和FP16 INT8 INT4混成一类。其实它们并不处在同一层。先用一张表分开名称可以先理解成FP32 / FP16 / BF16数字精度 / dtypeINT8 / INT4低位宽表示AWQWeight Quantization 方法GPTQPost-Training Quantization 方法GGUF模型文件格式 / 生态格式最重要的是INT4 ≠ AWQ ≠ GPTQ ≠ GGUFAWQ 和 GPTQ 都在解决一个核心问题怎样把模型压到 4 bit同时尽可能少损失模型能力AWQActivation-aware Weight Quantization会参考 Activation 信息尽量保护对模型输出更重要的 Weight。GPTQ 则属于Post-Training Quantization也就是模型训练完成以后再寻找量化后的 Weight使量化误差尽可能小。当前 Transformers 文档同样支持 AWQ、GPTQ以及基于 bitsandbytes 的 8-bit / 4-bit 量化。而GGUF更接近一个 Model File Format。它里面可以保存F16 Q8 Q6 Q5 Q4 ...不同类型的 Tensor。GGUF 又经常和llama.cpp一起出现。这已经开始进入Inference Engine的问题了。所以这一篇只把 GGUF 的位置搞清楚不展开。七、现在把整个显存关系画出来到了这里可以把前几篇的知识重新连接起来Model │ ▼ Parameter Count │ ▼ Precision / Quantization │ ┌────────┼────────┐ │ │ │ FP16 INT8 INT4 │ │ │ └────────┼────────┘ ▼ Model Weight Size │ ▼ ┌─────────────────────────────┐ │ GPU VRAM │ │ │ │ Model Weights │ │ KV Cache │ │ Activations │ │ Runtime Buffers │ │ CUDA / Framework │ │ │ └─────────────────────────────┘所以Parameter Count回答的是模型有多少参数而Precision / Quantization回答的是这些 Weight 用多少 bit 保存最终得到Model Weight Size但它仍然只是整个GPU VRAM的一部分。这就是回答12GB 显存到底能跑多大的模型最重要的一张图。八、实验为什么从 0.5B 换成 7B前两篇一直使用Qwen/Qwen2.5-0.5B-Instruct因为那时的目标是研究Model Loading Tokenizer Prefill Decode KV Cache小模型下载快、加载快也不会让显存不足干扰实验。但这一篇研究的恰恰就是VRAM Boundary如果继续用 0.5BFP16 INT8 INT4全部轻松放进 RTX 3060。虽然数字会变化却体会不到量化真正解决了什么。所以这次换成Qwen2.5-7B-Instruct它刚好位于一个很有意思的边界FP16 / BF16 → 12GB 很难完整容纳 INT8 → 开始进入范围 INT4 → 显存明显宽裕这正适合做容量实验。九、实验前先把模型下载好最开始我直接在AutoModelForCausalLM.from_pretrained(...)里让 Transformers 自动下载模型。小模型问题不大。但到了 7B模型权重已经十几 GB网络慢的时候下载可能持续很久。更麻烦的是SSH ↓ 网络断开 ↓ Terminal 退出 ↓ 下载进程也可能被中断于是Model Download和VRAM Experiment两个本来完全不同的问题混到了一起。从实验角度更合理的方式是先下载模型 ↓ 确认本地 Cache 完整 ↓ 关闭网络访问 ↓ 再做 GPU 实验Hugging Face 官方的hf download本身就支持把完整 Repository 下载到本地 CacheTransformers 也支持HF_HUB_OFFLINE1和local_files_onlyTrue做离线加载。先进入环境cd ~/ai-infra-lab source .venv/bin/activate安装或更新pip install -U huggingface_hub检查hf --help1. 用 tmux 避免 SSH 断开影响下载安装sudo apt install tmux创建一个 Sessiontmux new -s hf-download如果网络较慢可以把 Hugging Face 下载超时时间调大export HF_HUB_DOWNLOAD_TIMEOUT120Hugging Face 官方也建议在慢速网络下适当增大这个参数。本机下载时多次整机失联甚至只能强制重启经实验关闭Xet后问题解决。见第十二章第七个坑。export HF_HUB_DISABLE_XET1然后下载原始模型hf download Qwen/Qwen2.5-7B-Instruct如果 SSH 要断开可以CtrlB DDetach 当前 tmux。以后重新 SSH 登录tmux attach -t hf-download下载仍然继续。2. 为什么不用--local-dir前面执行from_pretrained(...)时可能已经下载了一部分模型。默认情况下Hugging Face 会把它们放进~/.cache/huggingface/hub/所以这里继续hf download Qwen/Qwen2.5-7B-Instruct可以直接利用已有 Cache。没必要再复制一份到新的目录。官方当前也默认推荐使用 Cache 系统如果确实希望得到普通目录结构再使用--local-dir。查看 Cachehf cache ls或者hf cache ls | grep Qwen2.5-7B十、AWQ 和 GPTQ 也提前下载前面三组实验BF16 BNB INT8 BNB INT4其实都使用同一个Qwen/Qwen2.5-7B-Instruct只是在加载阶段选择不同方式。所以原始模型只需要下载一次。而 AWQ 和 GPTQ 是已经提前量化好的 Model Repository所以还需要分别下载hf download Qwen/Qwen2.5-7B-Instruct-AWQ以及hf download Qwen/Qwen2.5-7B-Instruct-GPTQ-Int4下载完成后hf cache ls | grep Qwen2.5-7B理想情况下应该能看到Qwen/Qwen2.5-7B-Instruct Qwen/Qwen2.5-7B-Instruct-AWQ Qwen/Qwen2.5-7B-Instruct-GPTQ-Int4Qwen 官方 AWQ Checkpoint 的config.json已经记录bits 4 group_size 128 quant_method awqGPTQ-Int4 则记录bits 4 group_size 128 quant_method gptq所以加载它们时不需要我们再额外用 bitsandbytes 做一次量化。十一、写一个统一的显存测试程序现在终于进入真正的实验。安装基础依赖pip install -U transformers accelerate bitsandbytes safetensorsbitsandbytes 当前可以通过BitsAndBytesConfig直接在 Transformers 加载过程中做 8-bit 或 4-bit 量化。创建nano quant_vram_test.py写入import sys import time import torch from transformers import ( AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig, ) MODEL_IDS { bf16: Qwen/Qwen2.5-7B-Instruct, int8: Qwen/Qwen2.5-7B-Instruct, int4: Qwen/Qwen2.5-7B-Instruct, awq: Qwen/Qwen2.5-7B-Instruct-AWQ, gptq: Qwen/Qwen2.5-7B-Instruct-GPTQ-Int4, } if len(sys.argv) ! 2: print( Usage: python quant_vram_test.py bf16|int8|int4|awq|gptq ) sys.exit(1) MODE sys.argv[1] if MODE not in MODEL_IDS: raise ValueError( fUnknown mode: {MODE} ) MODEL_ID MODEL_IDS[MODE] print( * 60) print(Mode :, MODE) print(Model:, MODEL_ID) print(GPU :, torch.cuda.get_device_name(0)) print( * 60) tokenizer AutoTokenizer.from_pretrained( MODEL_ID, local_files_onlyTrue, ) load_args { # 故意要求整个模型进入 GPU 0 # 避免自动 CPU Offload 干扰实验 device_map: {: 0}, low_cpu_mem_usage: True, # 只读本地 Cache local_files_only: True, } if MODE bf16: load_args[dtype] torch.bfloat16 elif MODE int8: load_args[quantization_config] ( BitsAndBytesConfig( load_in_8bitTrue ) ) elif MODE int4: load_args[quantization_config] ( BitsAndBytesConfig( load_in_4bitTrue, bnb_4bit_quant_typenf4, bnb_4bit_compute_dtypetorch.float16, bnb_4bit_use_double_quantTrue, ) ) elif MODE in (awq, gptq): # Model 自身的 config.json # 已经包含 quantization_config load_args[dtype] auto torch.cuda.empty_cache() torch.cuda.reset_peak_memory_stats() print() print(Loading model from local cache...) start time.perf_counter() model AutoModelForCausalLM.from_pretrained( MODEL_ID, **load_args ) model.eval() torch.cuda.synchronize() load_time ( time.perf_counter() - start ) print( Load Time:, f{load_time:.2f} s ) if hasattr( model, get_memory_footprint ): footprint ( model.get_memory_footprint() / 1024**3 ) print( Model Footprint:, f{footprint:.3f} GiB ) print( Allocated:, f{torch.cuda.memory_allocated() / 1024**3:.3f} GiB ) print( Reserved :, f{torch.cuda.memory_reserved() / 1024**3:.3f} GiB ) messages [ { role: user, content: ( 用三点解释模型量化为什么 能够降低 GPU 显存占用。 ) } ] prompt tokenizer.apply_chat_template( messages, tokenizeFalse, add_generation_promptTrue, ) inputs tokenizer( prompt, return_tensorspt, ) device next( model.parameters() ).device inputs { key: value.to(device) for key, value in inputs.items() } torch.cuda.reset_peak_memory_stats() torch.cuda.synchronize() start time.perf_counter() with torch.inference_mode(): outputs model.generate( **inputs, max_new_tokens128, do_sampleFalse, use_cacheTrue, ) torch.cuda.synchronize() generate_time ( time.perf_counter() - start ) print( Generate Time:, f{generate_time:.3f} s ) print( Peak VRAM:, f{torch.cuda.max_memory_allocated() / 1024**3:.3f} GiB ) input_tokens ( inputs[input_ids].shape[1] ) output_tokens ( outputs.shape[1] - input_tokens ) print( Input Tokens :, input_tokens ) print( Output Tokens:, output_tokens ) input( \nPress Enter to exit... )这里最重要的变化就是local_files_onlyTrue我们已经提前把 Model 下载好了。所以显存实验期间不下载 不访问网络 只加载本地 Model实验变量就干净多了。十二、为什么这里故意不用device_mapauto这是整个实验很重要的一点。日常运行大模型时device_mapauto很好用。如果 GPU 放不下全部模型Accelerate 可以自动把部分 Weight 放到CPU RAM但这一篇研究的是一个模型能不能完整放进 RTX 3060 12GB如果使用 Auto OffloadGPU 装不下 ↓ 部分 Weight 放 CPU ↓ 程序仍然运行成功最后就可能错误得出7B BF16 可以完整放进 12GB。所以这里故意device_map{: 0}要求整个 Model ↓ cuda:0如果显存不够CUDA out of memory反而正是我们想观察的实验结果。十三、开始实验现在完全离线运行先开另一个 SSH Terminalwatch -n 0.5 nvidia-smi然后测试窗口统一使用HF_HUB_OFFLINE1 \ python quant_vram_test.py ...HF_HUB_OFFLINE1会阻止 Transformers 再访问 Hub。1. BF16HF_HUB_OFFLINE1 \ python quant_vram_test.py bf16FP16 和 BF16 对 Weight Storage 来说都是2 Bytes / Parameter所以这里没有必要再把 FP16 单独跑一遍。我们真正要验证的是约 7B × 2 Bytes ≈ 14GB在12GB VRAM上会发生什么。如果看到CUDA out of memory不是实验失败。相反这是一个非常有价值的结果理论容量估算 ↓ 真实 GPU ↓ OOM完全对应起来了。2. INT8继续HF_HUB_OFFLINE1 \ python quant_vram_test.py int8这一次 bitsandbytes 会在加载时进行 8-bit 量化。重点记录Model Footprint Allocated Reserved Peak VRAM nvidia-smi真正要看的不是Model loaded successfully而是BF16 → Model 本身就很难塞进去 INT8 → Model 开始完整进入 GPU这就是 Precision 改变 GPU Capacity Boundary 最直观的一次观察。3. INT4继续HF_HUB_OFFLINE1 \ python quant_vram_test.py int4理论上我们已经从7B BF16 ≈ 14GB降到7B INT8 ≈ 7GB再到7B INT4 ≈ 3.5GB实际数字不会这么整齐。但相对趋势应该非常明显BF16 ↓ OOM / 非常吃紧 INT8 ↓ 可以运行 INT4 ↓ Headroom 明显增加这就是整个实验最重要的现象。十四、再比较 AWQ 和 GPTQAWQ 和 GPTQ 与前面的 BNB INT4 不一样。BNB INT4 是原始 Model ↓ 加载时 Quantization而Qwen2.5-7B-Instruct-AWQ Qwen2.5-7B-Instruct-GPTQ-Int4下载下来的已经是预量化模型。所以程序中elif MODE in (awq, gptq): load_args[dtype] auto不再额外传BitsAndBytesConfig(...)模型自己的config.json已经告诉 Transformers这是 AWQ 4-bit或者这是 GPTQ 4-bit不过这两种预量化模型还需要相应的 Runtime 依赖。在我当前的环境中AWQ 首先需要pip install gptqmodel同时还需要安装与当前 PyTorch 版本匹配的torchvision。我的环境是PyTorch 2.14.0cu132 CUDA 13.2因此安装python -m pip install torchvision0.29.0 \ --index-url https://download.pytorch.org/whl/cu132然后运行HF_HUB_OFFLINE1 \ python quant_vram_test.py awqGPTQ 在此基础上还需要 Hugging Face 的optimumpip install optimum然后运行HF_HUB_OFFLINE1 \ python quant_vram_test.py gptq也就是说在我当前这套环境中AWQ / GPTQ 实验额外用到了gptqmodel torchvision optimum这些只是量化模型对应的 Python Runtime 依赖模型本身已经提前下载到 Hugging Face 本地 Cache 中不需要再次联网下载。这样BF16 INT8 BNB INT4 AWQ INT4 GPTQ INT4五个实验最终都进入同一张结果表。十五、AWQ / GPTQ 这里可能遇到依赖问题这里需要特别提醒一下。下载 Model成功并不代表当前 Python Environment 一定能够运行这种 Quantization Runtime。AWQ 当前 Transformers 官方文档仍然使用 AutoAWQ 作为其中一种加载后端而且特别提醒autoawq可能改变 Transformers 版本。所以如果python quant_vram_test.py awq遇到类似ImportError AWQ backend not installed Version conflict不要先折腾 CUDA。更干净的办法是给 AWQ 单独建环境python -m venv .venv-awq source .venv-awq/bin/activate再按当时官方文档安装相应 Runtime。GPTQ 也类似。目前 Transformers 官方已经把GPT-QModel作为主要 GPTQ Backend并明确说明 AutoGPTQ 已不再是当前支持路线。所以实际跑 AWQ / GPTQ 时以实验当天的 Transformers 官方文档安装 Runtime比把某个旧版本命令永久写死更可靠。这也是 AI Infra 实验里很现实的一点Quantization 原理 变化较慢 Python / Runtime 生态 变化很快十六、实验数据怎么记录最终把结果整理成Variant能否完整 GPU 加载Model FootprintPeak VRAMnvidia-smiBF16否11857 MiBINT8是8.108 GiB8.245 GiB8811 MiBBNB INT4是5.069 GiB5.197 GiB5625 MiBAWQ INT4是5.188 GiB5.207 GiB5559 MiBGPTQ INT4是5.165 GiB5.184 GiB5555 MiB还可以额外记录VariantInput TokensOutput TokensGenerate TimeINT84312816.923 sBNB INT4431286.595 sAWQ INT4431285.482 sGPTQ INT4431285.275 s不过这一篇我不会急着根据Generate Time判断谁快谁慢。因为真正做性能 Benchmark 还要控制Warmup Prompt Length Output Length Kernel Runtime TTFT TPOT等变量。注意本篇实验代码也可通过命令获取git clone https://github.com/mosesyyoung/ai-infra-lab.git这篇真正关注的是同一个模型换成不同 Precision / Quantization 后显存发生了什么十七、为什么几个显存数字对不上实验时很可能发现Model Footprint是一个数字torch.cuda.memory_allocated()又是另一个而nvidia-smi看到的还更大。这是正常的。因为nvidia-smi Used Memory并不等于Model Weight SizeGPU Process 还可能包括CUDA Context PyTorch Allocator Runtime Buffer Temporary Workspace KV Cache所以这一篇不要追求7B INT4 3.500 GiB这种虚假的精确。真正应该建立的是理论 Weight Size ↓ Model Footprint ↓ PyTorch Memory ↓ GPU Process Memory它们是不同层次的数字。十八、最重要的坑INT4 不代表整个推理显存除以 4假设Model Weight BF16 ↓ INT4理论 Weight 显存大幅下降。是不是整个GPU VRAM也会直接÷ 4当然不是。上一篇已经知道GPU VRAM │ ├── Model Weights ├── KV Cache ├── Activations ├── Runtime Buffers └── CUDA / FrameworkWeight Quantization 主要压缩Model Weights而KV Cache完全可能仍然使用FP16 BF16。所以Context Length ↑ 并发请求 ↑以后即使模型已经是 INT4KV Cache仍然会继续消耗大量 VRAM。于是INT4 Model ≠ 整个 LLM 推理系统的显存变成原来的四分之一。这也是为什么Model Fits和Serving Fits是两件不同的事情。十九、所以 12GB 到底能跑多大模型现在终于可以重新回答文章标题。对于RTX 3060 12GB如果希望模型主要甚至全部驻留 GPU可以先建立这样的工程直觉3B FP16 / BF16 → 比较轻松7B / 8B FP16 / BF16 → Weight 已经非常吃紧甚至超过 12GB7B / 8B INT8 → 开始很合理7B / 8B INT4 → 显存宽裕很多14B INT4 → 开始进入值得尝试的范围32B INT4 → 单看理论 Weight 都已经超过 12GB但是14B INT4 ≈ 7GB这个公式并不能推出12GB 显卡可以轻松稳定运行所有 14B 4-bit 模型。真实模型还有Scale Metadata 未量化部分 KV Cache Runtime CUDA所以更准确的说法应该是14B 4-bit 从 FP16 下几乎不可能完整装进 12GB变成了开始进入可以尝试和优化的范围。而不是简单记12GB 稳跑 14B二十、几个真正值得记住的坑第一7B ≠ 7GB7B 描述的是 Parameter Count。第二INT4 ≠ 整个模型严格 0.5 Byte / Parameter那只是第一层理论估算。第三Model File Size ≠ GPU VRAM Usage运行以后还有 KV Cache、CUDA、Runtime。第四device_mapauto可能通过 CPU Offload 让本来放不进 GPU 的模型运行起来。所以Run Successfully并不意味着Full GPU Resident。第五下载失败和模型 OOM完全属于两个不同问题。所以这一篇把Model Download提前剥离出去再进行完全离线的 GPU Benchmark。第六遇到 AWQ / GPTQ 加载失败优先检查Transformers Quantization Backend Python Package Version Compatibility不要第一反应就重装Driver CUDA Toolkit。第七这台 Ubuntu 实验机使用 Hugging Face 默认 Xet 下载大模型时曾多次出现整机失联甚至只能强制重启。日志没有留下 OOM、Wi-Fi Reset 或 Kernel Lockup 等明确错误。设置HF_HUB_DISABLE_XET1改用非 Xet 下载路径后问题解决。因此后续实验统一关闭 Xet。这个现象仅代表本机环境不意味着 Xet 普遍存在稳定性问题。二十一、这一篇真正解决了什么问题回头看前面几篇。第四篇我们知道Tensor │ ├── shape ├── dtype └── device以及FP32 4 Bytes FP16 2 Bytes BF16 2 Bytes。第五篇进一步得到Tensor ↓ Parameter ↓ Model ↓ GPU VRAM并建立Parameter Count × dtype ↓ Weight Size。第六篇又告诉我们真正执行推理以后GPU VRAM里不仅有 Weight还有KV Cache Runtime。到了这一篇这些知识终于真正接到了一起Parameter Count │ ▼ Precision │ ▼ Quantization │ ▼ Model Weight Size │ ▼ GPU VRAM最值得记住的第一点是模型参数量本身不能决定显存需求Parameter Count 必须和 Precision / Quantization 一起看。第二点量化本质上是在允许一定误差的情况下用更低位宽表示模型 Weight。第三点FP16 / BF16 ≈ 2 Bytes / Parameter INT8 ≈ 1 Byte / Parameter INT4 理论约 0.5 Byte / Parameter这就是量化为什么能够大幅扩大消费级 GPU 可运行模型范围。第四点AWQ、GPTQ 是量化方法而 GGUF 更接近模型文件格式它们并不在同一层。最后也是从 Infra 角度最重要的一点模型能塞进显存不代表它就能在长 Context、高并发情况下稳定提供服务。因为 Weight 之外还有KV Cache Runtime CUDA。于是12GB 能跑多大模型已经不再只是一个显卡参数问题。它真正变成了AI Infra Capacity Planning问题。二十二、下一篇为什么自然会出现 llama.cpp 和 Ollama不过这一篇做完又出现了一个新的问题。我们已经知道同一个模型可以有FP16 Safetensors BNB INT4 AWQ GPTQ GGUF Q4_K_M很多不同版本。其中AWQ GPTQ仍然可以放在Transformers ↓ PyTorch ↓ CUDA这条路线里理解。但是看到GGUF以后另一条非常常见的本地推理路线马上就出现了GGUF ↓ llama.cpp ↓ CPU / GPU而平时在本地运行模型时我们又经常看到Ollama。于是问题自然变成模型已经量化好了真正负责把模型跑起来的“推理引擎”到底做了什么为什么 llama.cpp 不依赖完整的 PyTorch也能执行一个 LLM为什么它可以CPU Inference GPU Offload CPU GPU Hybrid为什么某些Model Size GPU VRAM的模型仍然能够运行而 Ollama 又在这套架构上帮我们封装了什么下一篇我们就沿着Model ↓ GGUF ↓ llama.cpp ↓ CPU / GPU Offload ↓ Ollama真正跑一遍。到那时候我们的问题会从模型怎么塞进 GPU继续前进到模型已经有了到底由什么软件负责高效地把它跑起来再往后当我们重新走到vLLM问题又会从单个模型能不能跑升级成多个请求怎么调度 KV Cache 怎么管理 如何做 Batching 一张 GPU 怎样获得更高 Throughput这时LLM才会真正从一个Model逐渐变成Inference System。
返回列表