Loading

【大模型Wiki】07 · 开源模型与本地部署

开源模型与本地部署

整理自博客园旧笔记《搭建ChatGLM2-6B CPU版本》《使用kaggle白嫖GPU搭建chatglm-6B/Qwen-7B等大模型》(2024-04),2026-09 补充现代部署方案。

一、本地部署的三条路线

把大模型跑在自己机器上,动机通常有三:数据不出内网(隐私/合规)、不想为每次调用付 API 费、要做微调或二次开发。2026 年的开源生态已经把这件事的难度降到了"一条命令"。但不同工具解决的是不同层面的问题,先想清楚自己要什么,再选路线。

路线 工具 适合人群
开箱即用 Ollama、LM Studio 只想跑起来聊天,一条命令
极致性能 vLLM、SGLang、TensorRT-LLM 自己提供 API 服务、高并发
原始 transformers AutoModel.from_pretrained 学习/实验/微调开发

这三条路线本质上是从"上层封装"到"底层原生"的光谱:

  • Ollama / LM Studio 把模型下载、量化、推理引擎全部打包好,你只管选模型名。底层其实也是调用 llama.cpp(GGUF 引擎),只是替你屏蔽了细节。优点是省心,缺点是定制自由度低。
  • vLLM / SGLang 面向"服务化"。它们把模型加载成进程常驻的 HTTP 服务,对外暴露 OpenAI 兼容接口,重点解决吞吐、并发、显存利用率。适合做产品后端。
  • transformers 原生 是最贴近论文/源码的方式,每个张量怎么加载、怎么 forward 你都能改。适合学习原理、改结构、做微调。

显存估算经验值(推理,FP16):

本地部署三条路线

  • 7B ≈ 14GB;14B ≈ 28GB;32B ≈ 64GB;70B ≈ 140GB(多卡或量化)
  • 量化(INT4/Q4_K_M)可以再砍一半以上:7B Q4 约 4-5GB,一台 16G 内存的笔记本也能跑

经验值的来历:一个参数为 FP16(半精度浮点)时占 2 字节。7B 模型 = 70 亿参数 × 2 字节 ≈ 14GB。这个"裸权重"数字还没算 KV Cache(见第四节),所以实际显存占用会更高;下面第四节给出完整公式。

二、路线一:Ollama(2026 年的"默认答案")

当年在 ChatGLM2-6B 上手动改 .cuda()→.float()、克隆 12GB 模型文件的日子,如今被一条命令取代:

# 安装后(官网下载),直接拉模型运行
ollama run qwen3:8b          # 通义千问 8B,自动下载 GGUF 量化版
ollama run deepseek-r1:7b    # DeepSeek-R1 蒸馏 7B(带思考链)
ollama run glm4:9b           # 智谱 GLM-4

# 自带 OpenAI 兼容 API
curl http://localhost:11434/v1/chat/completions -d '{
  "model": "qwen3:8b",
  "messages": [{"role": "user", "content": "你好"}]
}'

要点:模型以 GGUF 格式分发;/v1 端点与 OpenAI API 兼容,所以 Next-Web、Dify 等前端可以直接接。

2.1、Ollama 的工作机制与常用命令

Ollama 本质上是一个"模型运行时 + 守护进程":

  • 安装后 ollama 命令通过本地 11434 端口与一个常驻的 ollama serve 守护进程通信。
  • 模型以 GGUF 文件形式存于 ~/.ollama/models(Windows 在 C:\Users\<你>\.ollama\models)。
  • ollama run <name> 第一次会先 pull 再加载进内存,之后常驻,便于多轮对话复用。
# 查看已拉取的模型
ollama list

# 删除某个模型(释放磁盘)
ollama rm qwen3:8b

# 只拉取不运行(常用于预先准备)
ollama pull deepseek-r1:7b

# 手动指定量化等级 / 上下文长度(通过临时 Modelfile)
ollama run qwen3:8b  # 默认上下文 8192,可在对话中用 /set parameter num_ctx 16384 调整

# 用 Modelfile 固化配置:新建一个文件 myqwen,内容:
#   FROM qwen3:8b
#   PARAMETER num_ctx 16384
#   PARAMETER num_gpu 41          # 用多少层放 GPU(CPU 卸载时为层数)
#   SYSTEM "你是一个严谨的中文助手"
# 然后:
ollama create myqwen -f myqwen
ollama run myqwen

2.2、Ollama 关键参数讲解

参数 含义 默认值/建议
num_ctx 上下文窗口 token 数 2048/4096;长文档问答调到 8192~32768
num_gpu 加载到 GPU 的层数 自动;CPU 跑时设 0
num_thread CPU 推理线程数 物理核数
temperature / top_p 采样参数(见 API 篇) 0.7 / 0.9
repeat_penalty 重复惩罚(类似 frequency_penalty) 1.1

踩坑:把 num_ctx 调得很大但显存不够时,Ollama 会默默降级或 OOM。先 ollama ps 看常驻占用,再决定能开多大上下文。

2.3、Ollama 的边界

Ollama 适合"单用户 / 小团队自托管聊天"。它默认是单并发顺序推理(一次只算一个请求),高并发场景吞吐远不如 vLLM。如果你要做对外服务、要连续 batching、要多卡张量并行,请直接看第三节的 vLLM。

三、路线二:vLLM(自建服务)

pip install vllm
vllm serve Qwen/Qwen3-8B --max-model-len 32768 \
  --gpu-memory-utilization 0.9 --port 8000
  • PagedAttention 管理 KV Cache(显存利用率提升数倍)
  • Continuous Batching 动态拼批,吞吐远高于 transformers 原生
  • 原生 OpenAI 兼容 API,可配 tensor-parallel-size 多卡跑大模型

生产部署事实标准;单机高并发场景同样可用 SGLang(结构化生成/多轮更快)。

3.1、vLLM 为什么快:PagedAttention 与 Continuous Batching

传统 transformers 推理每个请求都要预留一整块连续 KV Cache(最坏情况长度),导致显存碎片严重、利用率低。vLLM 借鉴操作系统"分页"思想:

  • KV Cache 被切成固定大小的 block 存到非连续显存,像虚拟内存一样按需分配,碎片大幅减少——同样显存能多塞 2~4 倍的并发请求。
  • Continuous Batching(连续批处理):不等一个 batch 里所有请求都生成完,而是哪个请求生成完就立刻把新请求补进来,GPU 利用率接近打满。这是吞吐翻倍的真正原因。

3.2、vLLM 启动参数详解

vllm serve Qwen/Qwen3-8B \
  --host 0.0.0.0 --port 8000 \
  --max-model-len 32768 \          # 最大上下文长度(token),影响 KV Cache 显存
  --gpu-memory-utilization 0.90 \  # GPU 显存占用上限比例,留点余量给碎片
  --tensor-parallel-size 2 \       # 多卡张量并行(模型切到 2 张卡),70B 必备
  --dtype auto \                   # 权重精度 auto/half/bfloat16
  --quantization awq \             # 若加载 AWQ 量化权重,需显式声明
  --enable-prefix-caching \        # 复用相同 system prompt 的 KV(多用户同提示词省显存)
  --max-num-seqs 256               # 最大并发序列数

启动成功后访问 http://localhost:8000/docs 能看到 Swagger 文档;OpenAI 兼容端点就是 /v1/chat/completions。

# 用 OpenAI SDK 直接对接 vLLM 起的服务(改 base_url 即可)
from openai import OpenAI
client = OpenAI(base_url="http://localhost:8000/v1", api_key="EMPTY")

resp = client.chat.completions.create(
    model="Qwen/Qwen3-8B",
    messages=[{"role": "user", "content": "用一句话解释 PagedAttention"}],
    max_tokens=256,
)
print(resp.choices[0].message.content)

3.3、SGLang 简介(何时选它)

SGLang 在"结构化输出 / 多轮 agent"场景更快:它用 RadixAttention 缓存可复用前缀,并通过 DSL 让"先抽取再判断"这类多步调用减少重复计算。若你的业务是大量 JSON 抽取、工具调用链路,可优先试 SGLang;纯聊天高并发两者接近。

四、显存到底怎么算:完整估算公式

只记"7B≈14GB"不够用。真实部署要算两部分:权重本体 + 推理时的 KV Cache。

4.1、权重显存公式

权重显存(GB) ≈ 参数量(B) × 每参数字节数
精度 每参数字节 7B 权重 说明
FP32 4 28 GB 基本不用,除非 CPU int 推理
FP16 / BF16 2 14 GB transformers 默认
INT8 1 7 GB 权重量化
INT4 (Q4_K_M) 0.5 ~3.5–4.5 GB GGUF 常用,含开销

之所以 INT4 实际略高于 0.5×参数量,是因为量化权重还附带少量缩放因子(scales/zeros)元数据,约占 5%~10%。

4.2、KV Cache 显存公式(决定你能开多长上下文)

Transformer 每生成一个 token,都要把历史所有 token 的 Key/Value 缓存下来。KV Cache 随上下文长度和并发数线性增长:

KV Cache(GB) ≈
  2                         # K 和 V 两份
  × 层数 L
  × 隐藏维度 H
  × 序列长 S(token)
  × 批大小 B(并发)
  × 每元素字节(精度)
  / (1024^3)

简化写法(以 FP16 为例,2 字节/元素):

KV Cache(GB) ≈ 2 × L × H × S × B × 2 / 1e9

以 Qwen3-8B 为例(约 36 层、隐藏维 4096):

单层每 token KV = 2 × 4096 × 2 字节 ≈ 16 KB
一个 8192 长、并发 1 的请求 ≈ 16KB × 8192 ≈ 131 MB
并发 32、上下文 8192 ≈ 4.2 GB

关键结论:

  • 上下文从 4k 翻倍到 32k,KV Cache 翻 8 倍;这是 --max-model-len 一调大显存就爆的根因。
  • 并发 B 直接线性放大 KV Cache,所以"高并发 + 长上下文"最吃显存。
  • vLLM 的 PagedAttention 不改变 KV Cache 的"总量公式",但把"预留浪费"压到最低,等效提升了可承载并发。

4.3、一条机器能不能跑的总判据

总显存需求 ≈ 权重显存 + KV Cache + 约 1~2GB 运行时开销

决策例子:

  • 笔记本 16GB 内存、无独显 → 只能 CPU + INT4(GGUF),num_ctx 控制在 4096 内。
  • 单卡 24GB(RTX 4090)→ 可 FP16 跑 7B~14B,或 INT4 跑 32B。
  • 单卡 80GB(A100)→ FP16 跑 70B,或 INT4 跑更大的 MoE。

五、量化格式对比:GGUF / GPTQ / AWQ

量化是把高精度的权重"压缩",用更少比特表示,从而省显存、提速,代价是少量精度损失。三种主流格式针对的引擎和场景不同:

维度 GGUF (GGML) GPTQ AWQ
提出/代表 llama.cpp / Georgi Gerganov GPTQ 论文 MIT Han Lab (AWQ)
典型比特 Q4_K_M / Q5_K_M 等 4-bit / 3-bit 4-bit / 2-bit
主要推理引擎 llama.cpp、Ollama、LM Studio(CPU/GPU 通吃) AutoGPTQ、vLLM、TGI AutoAWQ、vLLM、LM Studio
是否依赖校准集 否(训练后直接量化) 是(用 128~256 条样本校准) 是(用少量样本估算"重要权重")
精度保持 好(k-quant 混合量化优秀) 好 优(保护 1% 显著权重)
CPU 跑 原生支持,最快 基本不支持 基本不支持
适用场景 个人本地/边缘/CPU GPU 服务化 GPU 服务化、保精度优先

5.1、三者的原理差异(一句话版)

  • GGUF:llama.cpp 的容器格式,内部是 k-quant 混合量化——对"重要的"权重用高精度、不重要的用低精度,且把模型结构/分词器/元数据打包进一个文件,便于分发。它不依赖校准集,开箱即用。
  • GPTQ:基于"近似二阶信息"的训练后量化,用校准数据做逐层补偿,使 4-bit 下重建误差最小。需要校准集,量化过程较慢。
  • AWQ(Activation-aware Weight Quantization):观察到"只有约 1% 的权重对激活输出影响巨大",量化时不碰这些重要权重(只做缩放保留),其余正常低位量化。因此对精度更友好,常作为 vLLM 上保精度首选。

5.2、怎么选

  • 想在笔记本/树莓派/纯 CPU 跑 → 只选 GGUF(Ollama 默认)。
  • 有 GPU、要部署成服务且最省显存 → GPTQ 或 AWQ 皆可,vLLM 都支持;追求极限保精度选 AWQ。
  • 想一个文件到处跑 → GGUF 是单文件,GPTQ/AWQ 通常是一组 safetensors + 配置。
# 在 HuggingFace 上常见命名规律
Qwen3-8B-GGUF/Qwen3-8B-Q4_K_M.gguf   # GGUF 量化版(llama.cpp/Ollama)
Qwen/Qwen3-8B-GPTQ-Int4              # GPTQ 版
Qwen/Qwen3-8B-AWQ                    # AWQ 版

# Ollama 拉的就是 GGUF;vLLM 拉 GPTQ/AWQ 时需加 --quantization
vllm serve Qwen/Qwen3-8B-AWQ --quantization awq

六、路线三:transformers 原始方式(学习用)

保留当年笔记的核心代码,这是所有框架底下最原生的样子:

from transformers import AutoModelForCausalLM, AutoTokenizer

model = AutoModelForCausalLM.from_pretrained(
    "Qwen/Qwen3-8B", torch_dtype="auto", device_map="auto",
)
tokenizer = AutoTokenizer.from_pretrained("Qwen/Qwen3-8B")

messages = [{"role": "user", "content": "你是谁?"}]
text = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
model_inputs = tokenizer([text], return_tensors="pt").to(model.device)
generated_ids = model.generate(**model_inputs, max_new_tokens=512)
response = tokenizer.batch_decode(generated_ids, skip_special_tokens=True)[0]
print(response)

这段原生代码每一步的含义:

  1. AutoModelForCausalLM —— 因果语言模型(Causal LM,只能"从左往右"生成),适配 Qwen/Llama 等。
  2. torch_dtype="auto" —— 用模型卡里声明的精度(通常 BF16)加载,省显存且数值稳。
  3. device_map="auto" —— 自动把层分布到可用 GPU,多卡时自动切分;无 GPU 则全 CPU。
  4. apply_chat_template —— 把 messages 按模型规定的聊天模板拼成带特殊标记的纯文本(这一步最容易漏,漏了模型就不"聊天"而像续写)。
  5. model.generate —— 自回归生成,max_new_tokens 控制最多新生成多少 token(注意是"新增"而非总长度)。

6.1、当年的 CPU 方案回顾(ChatGLM2-6B)

无 GPU 时把 .cuda() 改为 .float()(FP32 加载进内存),32G 内存可跑,启动慢、占用约 22G。三种入口脚本:

  • cli_demo.py:命令行对话
  • web_demo.py:Gradio Web UI(launch(server_name='0.0.0.0', auth=fn) 可对外)
  • api.py:uvicorn 暴露 POST API

这套"改代码适配硬件"的思路如今由量化(GGUF/GPTQ/AWQ)+ 推理框架(llama.cpp/Ollama)接管——CPU 跑 7B 模型在 2026 年已是普通操作。

6.2、模型下载

# HuggingFace(国内镜像)
export HF_ENDPOINT=https://hf-mirror.com
huggingface-cli download Qwen/Qwen3-8B

# ModelScope(魔搭,国内直连)
modelscope download --model deepseek-ai/DeepSeek-R1-0528

下载量注意:FP16 的 7B 约 14GB;GGUF Q4 约 4-5GB;70B FP16 约 140GB(建议直接拉量化版)。

七、白嫖 GPU:Kaggle / Colab / AutoDL

当年用 Kaggle 免费 GPU(P100/T4×2,每周 30h)跑 7B 模型的方法依然有效:

  1. 登录 Kaggle → 手机验证(不验证不能用 GPU)
  2. Notebook 设置里切换 Accelerator 为 GPU
  3. Settings → Internet 打开(下载模型需要联网)
# Kaggle/Colab 里跑一个 7B
!pip install -q transformers accelerate
from transformers import AutoModelForCausalLM, AutoTokenizer
tok = AutoTokenizer.from_pretrained("Qwen/Qwen3-8B")
model = AutoModelForCausalLM.from_pretrained(
    "Qwen/Qwen3-8B", torch_dtype="auto", device_map="auto")

2026 年的补充选项:

平台 免费额度 说明
Kaggle T4×2,30h/周 最稳的免费双卡
Google Colab T4(免费版) 挂代理可用
AutoDL / 仙宫云 按小时计费(约 ¥1-3/h) 国内租 4090/A100,跑 70B 微调性价比高

7.1、Kaggle 跑模型的完整最小示例

# 1) 安装依赖(Kaggle 镜像已含 torch,但 transformers 版本可能旧)
!pip install -q transformers accelerate sentencepiece

# 2) 用镜像加速下载(Kaggle 机房连 HF 有时慢)
import os
os.environ["HF_ENDPOINT"] = "https://hf-mirror.com"

# 3) 加载(device_map="auto" 自动用免费 GPU)
from transformers import AutoModelForCausalLM, AutoTokenizer
tok = AutoTokenizer.from_pretrained("Qwen/Qwen3-8B")
model = AutoModelForCausalLM.from_pretrained(
    "Qwen/Qwen3-8B", torch_dtype="auto", device_map="auto").eval()

# 4) 推理
prompt = tok.apply_chat_template(
    [{"role": "user", "content": "用一句话介绍你自己"}],
    tokenize=False, add_generation_prompt=True)
out = model.generate(**tok([prompt], return_tensors="pt").to(model.device),
                     max_new_tokens=256)
print(tok.decode(out[0], skip_special_tokens=True))

Kaggle 注意:免费 GPU 单次会话有断连风险(闲置 60 分钟或总时长到 30h 会被回收);不要把微调后的大模型权重只存在 /kaggle/working 之外,关掉就没了。要持久化请挂 Kaggle Dataset 或传 ModelScope。

八、常见报错排查

本地部署 90% 的坑集中在显存、依赖版本、编码、网络四类。

8.1、显存不足(CUDA out of memory)

RuntimeError: CUDA out of memory. Tried to allocate ...
  • 换更小量化:FP16→INT4(GGUF Q4_K_M / vLLM --quantization awq)。
  • 减小 --max-model-len / num_ctx,KV Cache 立刻下降。
  • 降并发:单用户先跑通再谈性能。
  • 多卡:vllm serve ... --tensor-parallel-size 2;或 transformers device_map="auto" 自动拆分。
  • CPU 兜底:Ollama num_gpu 0 或 transformers 不指定 cuda。

8.2、依赖版本冲突

ImportError: cannot import name 'xxx' from 'transformers'
  • 锁定版本:pip install transformers==4.44.0 accelerate==0.33.0。
  • 新模型往往要求新版 transformers,旧模型用新库可能报错——以模型卡 requirements 为准。
  • torch 与 CUDA 版本要匹配(如 CUDA 12.1 配 torch 2.3+),否则报 CUDA error。

8.3、trust_remote_code 相关

ValueError: Loading THUDM/chatglm2-6b requires you to execute the modeling file...
  • 老模型(ChatGLM2/早期 Qwen)需要 trust_remote_code=True,新版 Qwen2+ 已无需。
  • 远程代码有安全风险,只对自己信任的仓库开启。

8.4、网络/下载失败

  • HF 连不上:设 HF_ENDPOINT=https://hf-mirror.com 或用 modelscope download。
  • GGUF 下载中断:用 huggingface-cli download 支持断点续传,比浏览器稳。

8.5、CPU 推理极慢 / 卡死

  • 确认不是误把 14GB FP16 模型放 CPU 了(会swap到磁盘,慢到像死机)。应换 GGUF Q4。
  • num_thread 设成物理核数,别超线程翻倍导致争抢。
  • 关闭其他吃内存程序,避免 22G+ 占用触发系统 OOM Killer。

8.6、中文乱码 / 输出退格

  • Windows 终端编码设为 UTF-8:chcp 65001;或在 Python 文件头加 # -*- coding: utf-8 -*-。
  • 确认 tokenizer 加载成功,没退化成按字切分。

九、2026 年开源模型选型速查

需求 推荐 理由
个人电脑聊天 Qwen3-8B、GLM-4-9B Ollama 一条命令,中文好
推理/数学 DeepSeek-R1-Distill、Qwen3-Thinking 思考链蒸馏小模型
代码 Qwen3-Coder、DeepSeek-V3 补全与 Agent 式改码
长上下文/知识库 Qwen3-32B、GLM-4.5 128k 窗口
边缘设备 Qwen3-1.7B/4B、Llama-3.2-3B 手机/树莓派可跑

选型原则:先看显存预算,再看中文/代码/推理的侧重点;中文场景 Qwen/GLM/DeepSeek 基本全面优于 Llama 同级。

十、小结

  • 想"立刻用"选 Ollama;想"对外服务"选 vLLM/SGLang;想"学原理/微调"用 transformers 原生。
  • 量化是本地部署的命门:CPU 只能跑 GGUF,GPU 服务化 GPTQ/AWQ。
  • 显存 = 权重 + KV Cache,上下文和并发是显存黑洞,调参前先套第四节公式算一遍。
  • 报错先怀疑显存和版本,九成问题能在这两处定位。

下一篇(08)会展开 OpenAI 兼容 API 的全参数与函数调用,本地起的服务也能直接对接这些代码。

posted @ 2026-09-19 05:42  Stellan7  阅读(51)  评论(0)    收藏  举报