第一次在本地跑ChatGLM-6B那天,我天真地以为装个显卡驱动就能直接跑起来,结果整个周末都耗在报错排查里。后来才发现,本地部署大模型这件事,真正拦住新手的不是模型本身,而是一连串环境配置问题——CUDA版本对不上、PyTorch装成CPU版、显存爆了、临时文件把磁盘塞满,每一个都能让人卡在原地。这篇攻略就是我走完整条链路之后的完整复盘,从CUDA配置讲到ChatGLM-6B真正跑出第一句对话,每一步都会说清楚为什么这么做,以及哪些坑可以提前避开。
文章面向的是完全没接触过本地模型部署的初学者,但也适合那些之前照着网上教程装到一半就放弃的人。不管你是 AI 方向的学生、想离线使用大模型的开发者,还是单纯对数据隐私有要求、希望模型留在自己机器上的爱好者,这套最小闭环流程都能让你少走不少弯路。
1. 先别急着装CUDA:评估你的机器到底带不带动6B参数模型
1.1 先看显存,再看内存,最后看硬盘
我见过不少读者一上来就问“我这台电脑能跑ChatGLM-6B吗”,然后贴一张截图。判断这事其实不复杂,主要看四个硬件维度:显卡型号、显存大小、内存容量、硬盘类型。
显卡型号决定了你用的加速架构和能安装的CUDA能力上限,但真正卡脖子的往往是显存。显存是GPU自己的“办公桌”,模型权重、推理过程中的中间激活值、注意力机制的KV缓存都要临时摆在这张桌子上。桌面不够大,东西自然放不下,于是就会出现最常见的CUDA out of memory报错。
内存则是“库房”。加载模型时,系统需要先把磁盘上的权重文件读进内存,再搬到显存里去。如果你加载 FP16 精度的ChatGLM-6B,光权重部分就有大约12GB,加载过程内存峰值往往是权重的两倍左右,所以16GB内存会非常紧张,32GB比较舒服。
硬盘主要影响加载速度和模型文件的存放空间。FP16 原始权重文件大概13GB,你至少要预留四五十GB的剩余空间,还要考虑下载过程中的临时文件。如果条件允许,优先把模型放在SSD上——我第一次跑的时候模型放在机械硬盘里,光是加载模型就等了五六分钟,换成NVMe固态后时间缩短到不到一分钟,体感差距极大。
1.2 不同显存档位对应什么样的使用预期
网络上关于“最低配置”的说法很多,但我在实际测试中发现,显存大小对应的合理预期可以按下面这个表来估算:
| 显存大小 | 推荐精度 | 显存占用估算 | 使用体验 |
|---|---|---|---|
| 6GB | INT4 量化 | 权重约3.5GB,总占用约5-6GB | 可以跑通,但上下文长度要缩短 |
| 8GB | INT4 或 INT8 | 权重约3.5-6GB,总占用6-8GB | 日常对话足够,长文本会紧张 |
| 12GB | INT8 或 FP16 | 权重约6-12GB,总占用11-13GB | FP16 勉强,INT8 很舒服 |
| 16GB以上 | FP16 | 权重约12GB,总占用13-16GB | 基本无压力,可拉长上下文 |
这里要说明一点,6B参数这个规模,FP16 权重就是12GB左右,这是硬算出来的。8GB显存用户最理想的方案是INT4量化,6GB显存则需要把量化后的上下文长度进一步压缩,否则仍然会爆显存。
顺便提一句,如果你没有NVIDIA显卡,是AMD显卡或者Apple Silicon芯片的机器,也有各自的方案,比如AMD的ROCm、苹果的MPS后端,但这两条路在配置上都有额外成本。CUP纯CPU跑也不是不行,只是慢得比较感人,生成一句话可能要半分钟,跟GPU比体验差太多。所以我后续的内容都默认NVIDIA显卡,这也是目前本地部署大模型最成熟、资料最多的路线。
1.3 一个很容易被忽略的细节:显卡驱动的新旧
我之前帮一位朋友排查了一下午,跑什么模型都报CUDA初始化失败,最后发现是显卡驱动还停留在三年前的版本。显卡驱动就像是操作系统和GPU之间的翻译官,太老的翻译官根本听不懂新版CUDA运行时说的话。所以不管你打算用哪个版本的CUDA工具包,第一步都应该先把NVIDIA驱动更新到官方最新版本。
判断驱动是否正常也很简单,在命令行窗口输入nvidia-smi,能看到显卡型号、驱动版本、显存占用等信息就基本没问题。
2. CUDA配置的真正难点:驱动、Toolkit与PyTorch之间的版本三角关系
2.1 先把几个名词讲清楚,不然你会被教程绕晕
很多小白卡死在CUDA这一关,本质是被网上的教程搞晕了。CUDA配置涉及三个东西:NVIDIA显卡驱动、CUDA Toolkit、cuDNN,外加一个PyTorch。
它们之间的关系可以这样理解:显卡驱动是系统级别的程序,它负责让操作系统知道你有一块NVIDIA显卡,并且能用它做通用计算;CUDA Toolkit是给开发者准备的编译工具和运行库,写CUDA代码、编译CUDA程序都会用到它;cuDNN则是NVIDIA专门为深度学习优化的加速函数库,卷积、矩阵运算这些操作经过它会有很好的加速效果。
关键点来了:如果你只是用PyTorch跑ChatGLM-6B,真正需要关心的是显卡驱动和PyTorch自带的CUDA运行库。PyTorch在安装包内部已经集成了CUDA运行库和cuDNN,不需要你额外安装CUDA Toolkit。很多人被老教程误导,非要单独装一个CUDA Toolkit,结果反而制造了版本冲突。
2.2 nvidia-smi显示的版本号和nvcc的版本号为什么不一样
这个现象几乎让每个新手困惑:nvidia-smi显示 CUDA Version 是 12.4,但nvcc -V显示的却是 11.8,到底哪个是真的?
答案是两个都是真的。nvidia-smi显示的CUDA版本是当前驱动最高支持的CUDA版本,它更像一个“上限”;nvcc -V显示的是你本地安装的CUDA Toolkit的版本,是开发工具自己带的。你实际跑PyTorch时,用的又完全不是这两者,而是PyTorch轮子里内置的CUDA运行时。
所以别再被“版本不一致”吓到了。你真正要保证的匹配关系就两层:第一,显卡驱动足够新,能覆盖PyTorch要求的CUDA版本;第二,你安装的PyTorch的CUDA版本和你预期的一致。
2.3 我的推荐做法:不装CUDA Toolkit,只更新驱动
这是我给绝大多数小白的建议:跳过单独安装CUDA Toolkit这一步,直接更新驱动,然后安装对应CUDA版本的PyTorch。
具体操作分三步:
第一步,去NVIDIA官网下载最新驱动并安装,重启电脑后确认nvidia-smi能正常显示。
第二步,进入PyTorch官网的安装命令生成页面,选择你的操作系统、包管理器和CUDA版本。以CUDA 12.1为例,会生成类似这样的命令:
pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121第三步,在命令行里验证PyTorch是否能识别GPU:
python -c "import torch; print(torch.__version__); print(torch.cuda.is_available())"如果输出True,恭喜你,CUDA配置这关就算过了。如果输出False,大概率是安装成了CPU版本的PyTorch,需要卸载重装。
我为什么推荐这个做法而不是装CUDA Toolkit?因为对跑模型这件事来说,CUDA Toolkit的大部分功能你用不上,它反而会引入PATH环境变量、版本冲突这些额外变量。少装一个东西,就少一个翻车点。
2.4 什么情况下才需要真的装CUDA Toolkit
有一种情况你还是需要单独装CUDA Toolkit:你需要从源码编译某些自定义算子,或者你在Windows下遇到某些依赖需要本地编译的库。
比如ChatGLM-6B依赖的cpm-kernels,在某些时候需要本地编译,这时如果没有C++编译环境,就会卡在安装环节。这种情况下你需要安装Visual Studio Build Tools,并设置好CUDA_HOME环境变量。不过我建议新手尽量绕开这条路径,优先找预编译好的依赖,能pip install就绝不本地编译。
3. 用conda造一个干净环境:Python版本和PyTorch依赖的首次握手
3.1 为什么非要用conda,不能直接用系统Python吗
很多初学者习惯直接往系统Python里装包,装到后面才发现灾难性后果:今天给这个项目装了一个新版本transformers,明天另一个项目因为版本不兼容就崩了。本地跑大模型往往不是一次性操作,你后面还会试其他模型、换不同版本,环境隔离是非常值得一开始就做好的习惯。
conda的作用就是给你创造一个又一个互相隔离的“独立房间”,每个房间有自己的Python版本和依赖,互不干扰。我推荐用Miniconda,体积小,操作简单。
3.2 创建环境的完整命令
安装好Miniconda后,打开命令行终端,执行:
conda create -n llm-env python=3.10 -y conda activate llm-env这里选Python 3.10是经验之谈。ChatGLM-6B发布时对应的生态链在3.8到3.10之间都比较稳定,3.10兼容性最好,后面的第三方依赖基本都能找到预编译版本。如果你用Python 3.12,某些老一点的库可能还没有适配,容易平地起惊雷。
激活环境后,可以顺手把 pip 的下载源设置成国内镜像,这一步对国内网络环境非常友好,能明显加快后续安装依赖的速度:
pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple这个操作只是换了个下载源,速度和稳定性都会有明显改善。
3.3 安装PyTorch时的“CPU版陷阱”
这是我想单独拎出来讲的一个坑。很多人装PyTorch的时候会习惯性直接pip install torch,这样默认装到的是CPU版本,装完之后CUDA完全不能用,排查半天才发现问题出在这里。
正确的做法是回到第2.3节说的,使用PyTorch官方推荐的带--index-url参数的安装命令,让它明确从CUDA版本的源里下载。装完以后一定要用torch.cuda.is_available()验证一次,确认是真的能识别显卡。这一步多花30秒,能帮你省掉后面一整天的困惑。
另外有个Windows下的细节:国内很多教程建议从清华源安装PyTorch,但清华源的torch版本往往不区分CUDA和CPU,容易装成纯CPU版。如果你想省事,建议直接走PyTorch官方源。如果下载速度太慢,也可以考虑先下载whl文件到本地,再pip install 本地文件,实测这个方案对速度不稳定的人很管用。
4. 下载ChatGLM-6B并跑出第一句话:模型文件、最小代码与第一次推理
4.1 模型文件到底长什么样,每个文件是干什么的
ChatGLM-6B是开源模型,可以从模型托管平台(比如Hugging Face平台或它的国内镜像站)下载。常见的下载方式有三种:网页端逐个下载、Git LFS克隆整个仓库、以及调用huggingface_hub库的API下载。
不管你用哪种方式,最终你会得到一个包含以下文件的文件夹:
config.json:模型结构配置,包括层数、隐藏层维数、注意力头数量,这些参数决定模型的架构规模。modeling_chatglm.py:ChatGLM的模型定义代码,这也是为什么加载时要加trust_remote_code=True——模型仓库里自带了官方自定义的网络结构代码,需要用这个参数允许执行仓库内的Python文件。tokenizer.model:分词器词表文件,负责把中文句子切成模型能理解的token序列。pytorch_model.bin(或.safetensors后缀的权重文件):模型真正的权重参数,6B参数规模的FP16权重大概12GB。quantization.py:量化辅助代码,做INT8、INT4量化时会用到。
下载的时候千万注意别只下载了权重文件就把其他文件扔了,尤其是config.json和modeling_chatglm.py缺失时,加载模型会直接报错。
4.2 安装模型配套依赖,版本别随便升
ChatGLM-6B的官方仓库里通常会附一个requirements.txt,里面列了依赖库及版本要求。我反复提醒新手的一件事就是:先按照官方给定的版本装,不要手贱升级到最新版。
以我长期测试下来的稳定组合为例:
pip install transformers==4.27.1 pip install accelerate pip install sentencepiece pip install cpm-kernelstransformers是非常关键的库,版本太新时某些模型自定义代码里的接口已经失效,会报各种属性找不到的错误;版本太老又会缺新接口。等整个流程跑通了,你再慢慢折腾新版不迟。
cpm-kernels是ChatGLM的加速算子库,第一次安装时如果没有匹配的预编译版本,就会被触发本地C++编译,这时候Windows用户会要求安装Visual Studio Build Tools,很容易卡住。遇到这种情况,优先搜索该库是否有对应Python版本的预编译wheel文件,不折腾本地编译。
4.3 写一个最小的推理脚本,验证模型能说话
环境准备齐了,模型文件也下载好了,接下来就是见证奇迹的时刻。建一个test_chatglm.py,内容如下:
from transformers import AutoTokenizer, AutoModel # 替换成你实际保存模型的本地路径 MODEL_PATH = "D:/models/chatglm-6b" tokenizer = AutoTokenizer.from_pretrained(MODEL_PATH, trust_remote_code=True) model = AutoModel.from_pretrained(MODEL_PATH, trust_remote_code=True).half().cuda() model = model.eval() prompt = "你好" response, history = model.chat(tokenizer, prompt, history=[]) print("问:", prompt) print("答:", response)这里做几个解释:
.half()是把模型参数从FP32转为半精度FP16,显存占用直接减半,推理速度也会更快,这是NVIDIA显卡跑这个模型的标准操作。
.cuda()是把模型加载到GPU上,如果你的机器支持,也可以先cuda:0指定设备。如果显存不够,这一行就会暴露问题,后面第五节我会讲对应的量化替代方案。
model.chat(tokenizer, prompt, history=[])是模型自定义的对话方法,封装好了拼prompt和生成回复的逻辑,新手不需要手动处理上下文历史。
运行python test_chatglm.py,如果顺利,你会看到终端打印出 “你好” 开头的一句问候语。第一次跑通的那一刻,我心里确实有一种说不出的成就感。
4.4 第一次推理时的正常现象与异常现象
第一次加载模型,现象和你的预期可能不太一样。加载FP16权重时,刚开始一两分钟CPU占用会飙升,这是权重文件在从磁盘读进内存、再搬运到显存;显存占用会逐渐上涨,最终停在11GB到13GB左右。GPU利用率并不会一直满,生成回复时才会跳动。
如果程序卡在下载阶段不动,检查是不是模型路径写错了,或者下载没有完成。如果提示缺少某个依赖包,回到4.2按顺序安装。如果显存溢出,别慌,这是配置档位没匹配好,我下一节专门讲这个。
5. 实测最容易翻车的四个场景:显存崩溃、内存不足、依赖报错与排查完整复盘
5.1 CUDA out of memory:先确认是谁占用了显存
这是本地部署大模型遇到频率最高的报错。很多人看到CUDA out of memory就以为是模型太大、机器太菜,实际上有时候只是其他进程把显存占了。
我的排查顺序是固定的:
第一步,运行nvidia-smi -l 1,动态查看当前显卡占用情况,确认是不是有别的程序在占显存。如果显存已经被占了一大半,把无关程序关掉再说。
第二步,确认模型本身是否超过了显存容量。比如8GB显存跑FP16的ChatGLM-6B,12GB权重肯定放不下,这时候就别硬撑,切到INT8或者INT4量化。
第三步,检查是不是推理时的临时缓存爆了。即使权重放得下,如果上下文长度设置太长,KV cache叠加起来也可能把显存涨满。把max_length从默认的2048调小到1024或512,显存占用会下降不少。
这里有一个量化估算思路可以帮助判断:ChatGLM-6B的KV cache每生成一个token大约占用448KB显存(具体公式涉及层数、注意力维度等参数),2048个token的上下文意味着接近900MB的额外开销。所以长对话场景的显存需求远远不止权重文件的大小。
5.2 Windows虚拟内存不足:模型加载阶段卡死或直接报MemoryError
这个问题多发生在内存只有16GB、却强行加载FP16模型的机器上。加载时权重文件13GB,加上系统本身占用,内存瞬间打满,Windows就会出现假死或报MemoryError。
如果你的内存只有16GB,又实在想跑FP16,可以手动把Windows虚拟内存的最大值调大到32GB左右,让系统在内存吃紧时用一部分磁盘空间顶上去。操作路径是:系统属性 → 高级系统设置 → 性能设置 → 高级 → 虚拟内存更改 → 自定义大小。
但虚拟内存毕竟是“磁盘当内存”,速度和稳定性都会打折。我更建议16GB内存用户直接跑量化版本,INT4量化权重3.5GB左右,加载过程内存峰值也就八九GB,会舒服得多。
5.3 transformers版本过新导致的属性报错:看报错行,比看论坛更有效
新手上路遇到AttributeError: 'ChatGLMModel' object has no attribute 'xxx'这类报错时,第一反应往往是去搜索引擎复制报错内容,但很多旧教程已经过时了。
我的建议是:先看完整堆栈信息。报错如果出在modeling_chatglm.py文件里,大概率是版本接口不匹配。这时最快速的解决方式是把transformers降级到官方要求的版本。我见过太多人一上来就装最新版transformers,然后被各种奇怪的兼容问题折磨,折腾几天后降级就好了。
在AI开源项目里,“最新版”不等于“最好版”,官方requirements.txt里的版本号才是这个模型当时测试验证过的组合,先相信它。
5.4 下载中断和文件不完整:加载时奇怪的乱码或构造参数警告
模型文件很大,下载过程容易中断。Git LFS支持断点续传,但如果你是在网页端手动点的下载,中断后重新下载时可能会覆盖掉不完整的文件,极难发现。
一个隐蔽的问题是,某个权重文件没下载完整时,加载模型不一定直接报错,而是可能出现乱码回答,或者模型的输出整体变得很奇怪。遇到这种灵异问题,我建议立刻回头核对文件完整性。
如果模型文件通过Git LFS克隆,可以直接git lfs pull检查缺失的LFS文件。如果是手动下载的,对比一下文件大小是否和托管平台页面标注的一致,不一致就重新下载。磁盘空间不足时也容易出现半截文件,安装前记得预留空间。
另外,Windows自带的安全扫描有时会在模型加载时扫描大文件,导致加载速度异常慢。把模型目录加入安全软件的排除列表,加载速度会有肉眼可见的提升。
6. 跑起来之后怎么调:量化方案、上下文参数与长期使用经验
6.1 量化方案怎么选:FP16、INT8还是INT4
模型跑通之后,你大概率会想追求更好的体验,比如更低的显存占用、更快的生成速度。量化就是最直接的思路。简单说,量化是让模型用更少的比特数表示参数,比如FP16用16位表示一个权重值,INT8用8位,INT4用4位,占用和计算量都显著下降,代价是精度损失。
| 精度 | 权重显存 | 中文效果 | 适合场景 |
|---|---|---|---|
| FP16 | 约12GB | 最稳定,几乎无损 | 显存充足的机器 |
| INT8 | 约6GB | 效果接近FP16,略微下降 | 8-12GB显存的日常使用 |
| INT4 | 约3.5GB | 中文有一定可感知损失 | 6-8GB显存的小机器 |
我在8GB显存机器上实测过多个量化方案,INT8和FP16在普通问答场景里几乎听不出区别,但INT4在长文本、文学性表达上偶尔会指代不清,或者给出略微奇怪的措辞。如果你的显存刚好卡在8GB,INT8是一个不错的平衡点。
代码层面的量子化加载方式,以INT8和INT4为例,一般思路是在from_pretrained时传入量化参数:
from transformers import AutoModel, AutoTokenizer MODEL_PATH = "D:/models/chatglm-6b" tokenizer = AutoTokenizer.from_pretrained(MODEL_PATH, trust_remote_code=True) model = AutoModel.from_pretrained( MODEL_PATH, trust_remote_code=True, load_in_8bit=True )实测下来,量化和半精度选择的本质是你愿意用多少效果换多少显存,没有绝对答案,按机器配置来就好。
6.2 上下文长度:默认2048不是必须的
很多人不知道max_length或者上下文长度这个参数对显存的影响有多大。对话的每次生成都要把之前所有的历史token重新跑一遍注意力计算,上下文越长,显存开销越大。
如果只是日常闲聊,把上下文限制在512到1024已经非常够用了,显存占用会明显下降。使用ChatGLM-6B默认的model.chat方法时,官方已经帮你维护了history,你不需要自己拼接全部历史文本,但内部仍然会把整段历史作为输入计算,所以控制历史轮数也能间接控制上下文长度。
6.3 日常使用的三种推荐形态
跑通最小脚本后,可以按需求选择更顺手的形态:
第一种,命令行交互脚本,适合在服务器或开发环境里快速测试。写一个循环接收输入,传给model.chat,打印回复即可。
第二种,借用社区里现成的Web演示脚本,在本地启动一个网页聊天界面,适合日常体验和给同事演示。常见的做法是基于gradio或类似框架写一个Web UI,模型封装在接口层,浏览器只管显示。
第三种,封装成本地API服务,让其他程序也可以调用本地模型。这种思路适合后续想接入其他应用、做自动化任务,或者把模型能力分享给局域网内其他设备的场景。
无论哪种形态,我建议日常使用中开一个终端窗口,时不时运行watch -n 1 nvidia-smi,观察显存和温度。
把模型目录放到SSD上,加载速度真的会快很多;模型第一次加载完之后,再次加载会因为系统缓存的存在变快不少,所以别急着频繁重启电脑。最后提醒一句,本地部署模型最大的价值是数据不出机器、完全离线可用,这也是我后来一直把6B模型留在本地的原因。先把最小闭环跑通,再谈优化。你现在离那句“你好”只差一步配置的距离了。