上一章你在两百行代码里养大了一个字符级小 GPT,看着它从吐乱码进化到吐出像样的句子。那东西的参数量大概几十万到几百万,能背下莎士比亚的腔调,但你没法真的跟它聊天——它太小了,没读过足够多的东西。现在的问题很直接:那些真正能对话、能写代码的开源大模型,动辄七十亿、上百亿个参数,我能在自己这台既没有天价显卡、又不是数据中心的破电脑上跑起来吗?
能。这一章就干这件事,而且不玩虚的——真的下载,真的跑,还真的微调一次。中间只有一个拦路虎需要拆开:一个七十亿参数的模型,凭什么能塞进一张普通显卡?
先算一笔账:模型到底占多大地方
模型的"体重"就是它的参数——上一章讲过,参数就是网络里那些可调的旋钮,训练就是把每个旋钮拧到合适的位置。一个 7B 模型(7 billion parameters,七十亿参数),顾名思义就是有七十亿个这样的旋钮。
每个旋钮是一个数。数占多大地方,取决于你用几个字节存它。默认情况下,训练出来的参数是 FP16(半精度浮点,16-bit floating point)——每个数用 16 个比特、也就是 2 个字节来存,能表示带小数点的、很宽范围的数值。
那么七十亿个旋钮 × 每个 2 字节 = 一百四十亿字节,约 14 GB。这还只是把模型"放进"显存,推理时还要额外的空间存中间结果。一张常见的消费级显卡(比如 8GB 或 12GB 显存的)根本装不下。这就是拦路虎。
把参数想成一屋子的旋钮。旋钮的数量是固定的(7B 就是七十亿个),但每个旋钮的刻度盘可以做得很精细也可以很粗糙。刻度盘越精细,记录一个旋钮位置需要的位数越多,占地越大。量化干的事,就是把精细刻度盘换成粗糙刻度盘。
量化:用更少的比特存同一个旋钮
量化(quantization)就是把每个参数从"高精度存法"换成"低精度存法"。原来用 16 个比特存一个数,现在我改用 8 个比特,甚至 4 个比特。位数少了一半、四分之三,模型体积就跟着掉。
拿 4-bit 举例:16-bit 变 4-bit,每个数只占原来的四分之一,14 GB 直接缩到大约 3.5–4 GB。一张 8GB 显卡轻松装下,还留出余量给推理。这就是为什么你今天能在游戏本上跑起一个像模像样的对话模型。
但天下没有白吃的午餐。4 个比特只能表示 16 个不同的数值(2 的 4 次方),你没法用 16 个档位精确复刻原本连续的刻度。所以量化的本质是四舍五入:把一堆挨得很近的原始数值,都塞进最接近的那个档位里。
好比原来你能报出体重 68.4 公斤,现在只准报整十:70 公斤。信息丢了一点,但"这人大概七十公斤"这个判断还站得住。量化赌的就是:模型有七十亿个旋钮,单个旋钮拧偏一丁点,整体行为几乎没变化——法不责众,误差被海量参数摊薄了。
那"如果不量化会怎样"?你要么买得起几万块的大显存卡,要么把模型拆到内存和硬盘上慢慢挪,推理慢到没法用。量化不是锦上添花,它是让大模型走进普通人电脑的那把钥匙。代价是精度略降——4-bit 下模型偶尔会更笨一点,但对大多数任务,你几乎察觉不到。现在主流的量化格式(比如 GGUF,一种给 CPU/GPU 混合推理设计的模型打包格式)还会做得更聪明:对更敏感的那部分参数留高精度,对不敏感的狠狠压,把损失压到更低。
真的跑起来:两条常见路子
把模型弄到本地跑,现在门槛低得惊人。两条最省心的路:
- Ollama 这类一键工具:装好后一行命令
ollama run llama3,它自动帮你下载已经量化好的模型、加载、开一个聊天界面。你甚至不用知道 GGUF 是什么。适合"我就想赶紧跟它说上话"。 - Hugging Face + Transformers 库:用 Python 代码把模型加载进来,你能看到、能改每一步。适合想动手改造的人——比如接下来我们要做的微调。
用第二条路时,加载一个 4-bit 量化模型的代码骨架大致长这样:
from transformers import AutoModelForCausalLM, BitsAndBytesConfig
bnb = BitsAndBytesConfig(load_in_4bit=True)
model = AutoModelForCausalLM.from_pretrained("模型名", quantization_config=bnb)
关键就是那个 load_in_4bit=True——你在告诉库:别用原始的 16-bit 权重占我 14 GB,加载的时候顺手压成 4-bit。剩下的分词、生成,跟你在第二章、第十章理解的完全一样:切词编号,喂进去,一个词一个词往外掷加权骰子。
LoRA:不动原模型,只加一个小补丁
现在你能把模型跑起来了。但它是个"通才",你想让它变成"你的专才"——比如学会用你公司的话术回邮件,或者模仿某种特定文风。这就要微调(第八章讲过:在预训练底子上,拿小批专门数据再训一训)。
问题来了:全量微调要更新全部七十亿个参数,这需要的显存比推理还夸张——不光要存参数,还要存每个参数的梯度和优化器状态,通常是模型本身的好几倍。你刚用量化省下的地方,一微调又全填回去了,甚至更糟。
解法叫 LoRA(Low-Rank Adaptation,低秩适配)。它的思路极其巧妙,一句话:别动原来的旋钮,在旁边挂一小撮新旋钮,只训练这撮新的。
把原模型想成一台已经调好的巨型调音台,几百万个推子。全量微调是把每个推子都重新推一遍,累死。LoRA 是:原推子焊死不动,我在旁边加一块小小的"微调板",只有几千个推子,最后把两块板的输出加在一起。要改变最终声音,我只需要调那块小板。
为什么"一小撮"就够?这背后有个关键观察:微调想让模型学的那点新东西,其实是个"低维"的调整——不需要撼动整个庞大的权重矩阵,只需要在一个很小的子空间里推一把。低秩(low-rank)这个词说的就是这件事:一个巨大的改动矩阵,可以用两个又瘦又长的小矩阵相乘来近似,而这两个小矩阵的参数量,可能只有原来的千分之一。
效果多夸张?对一个 7B 模型做 LoRA 微调,你真正训练的参数常常只有几百万个——不到总量的 1%。显存需求随之暴跌,配合上面的 4-bit 量化(这套组合有个名字叫 QLoRA,quantized LoRA),你甚至能在一张单卡消费级显卡上,微调一个原本"不可能"在本地碰的模型。训练完,那撮 LoRA 补丁小到只有几十兆,能单独存、单独发,想用的时候贴回原模型上就行。
代码层面你也不用从头写。Hugging Face 的 peft 库把这套封装好了:你指定 LoRA 要挂在哪几层、那撮新旋钮多大(一个叫 r 的秩参数,常取 8 或 16),库就自动帮你冻结原参数、只训练新增的那部分。喂进去几百上千条你自己的例子,跑上十几分钟到几小时,一个带你个人印记的模型就成了。
所以本质上,它就是……
把这一章拆开看,你会发现所谓"在自己电脑上跑大模型"根本没有魔法。量化是把每个旋钮的刻度盘做粗一点,靠海量参数摊薄误差,换来体积暴降;LoRA是承认"你想教它的那点新东西其实很小",于是焊死原旋钮、只挂一小撮新的去训。两招都在做同一件事:承认精度上可以妥协一点点,换来普通人也玩得起的成本。
到这里,大模型对你已经不再是云端一个遥不可及的黑箱——它是一个能下载到硬盘、能塞进显卡、还能被你亲手改造的文件。你拆过它的每一个零件,也在自己的机器上让它转了起来。那么最后一个问题就该问了:当我们已经能亲手造它、跑它、改它,该怎么清醒地看待这波 AI——它到底是神,是骗局,还是别的什么?下一章,我们收尾。