代码定义结构,权重存储知识:一个例子看懂大模型如何推理
转载
https://mp.weixin.qq.com/s/TM9lv6b-9AH8O9ZiApgTBA
下载一个开源模型时,经常会看到一种奇怪现象:模型代码只有几十 KB,权重文件却有几个 GB,甚至几十 GB。
为什么差距这么大?模型的知识到底存放在哪里?当我们输入一句话时,代码和权重又是如何配合,最终生成结果的?
要理解这些问题,不需要先研究 Transformer 或注意力机制。我们从一个只有 3 个参数的小模型开始,一步步走完整个推理过程。
先搭一个最小模型
假设我要做一个判断天气的小模型。
输入两个数:
-
温度
-
湿度
输出一个数:
-
适合出门的分数
分数越高,越适合出门。
模型结构写出来只有几行:
class WeatherModel:def __init__(self):self.w1 = Noneself.w2 = Noneself.b = Nonedef forward(self, temp, humidity):return temp * self.w1 + humidity * self.w2 + self.b
代码定义了这样一个公式:
分数 = 温度 × w1 + 湿度 × w2 + b
这里有一个很重要的事实:
代码只规定了:
这里需要三个数字。
但它并没有规定:
这三个数字具体是多少。
此时:
w1 = ?w2 = ?b = ?
都还是空的。
所以模型结构本质上是一张蓝图。
它描述了:
-
有哪些变量
-
怎么连接
-
应该执行什么计算
但它本身并不包含知识。
训练之后,权重来了
假设我们收集了很多历史天气数据:
温度 湿度 是否适合出门25 80 否23 40 是30 35 是18 95 否...
训练的过程,就是不断调整 w1、w2、b,让模型输出越来越接近真实结果。
经过大量迭代后,得到:
w1 = 0.7w2 = -0.3b = 5.0
这三个数字就是模型权重。
很多人会觉得:
不就是三个数字吗?
但这里其实发生了一件更重要的事情。
训练结束后,原始天气数据并不会跟着模型一起保存。
保存下来的只有:
0.7-0.35.0
这些数字其实是历史经验的压缩结果。
换句话说:
权重 ≠ 普通数字权重 = 从训练数据中提炼出来的经验
模型学到的知识,被编码进了这些数字里。
对于 GPT 来说,道理完全一样。
区别只是:
天气模型3 个参数GPT-31750 亿个参数
参数数量扩大了几十亿倍,但本质没有变化。
跑一遍:数字的旅程
假设今天:
温度 = 25湿度 = 80
输入模型。
首先把权重代入公式:
temp × 0.7 + humidity × (-0.3) + 5.0
再把输入代进去:
25 × 0.7 + 80 × (-0.3) + 5.0
开始计算:
25 × 0.7 = 17.580 × (-0.3) = -24.0
最后:
17.5 + (-24.0) + 5.0= -1.5
输出:
-1.5
模型认为今天不太适合出门。
这就是一次完整推理。
整个过程中:
代码决定怎么算权重决定用什么数字算
两者缺一不可。
加载权重,本质就是填空
很多人每天都会写:
model.load_state_dict(torch.load("weights.pth"))
但很少去想这句话到底干了什么。
在我们的例子里:
weights.pth
里面存的其实就是:
w1 = 0.7w2 = -0.3b = 5.0
加载时做的事情,本质等价于:
model.w1 = 0.7model.w2 = -0.3model.b = 5.0
就这么简单。
没有神秘计算。
没有自动推理。
没有重新训练。
只是把数字按照名字对应起来:
w1 → w1w2 → w2b → b
然后复制进去。
加载完成后:
空蓝图+具体数字=完整模型
结构和权重,到底谁更重要?
一个容易被忽视的事实是:
同一个模型结构,可以搭配很多不同权重。
例如:
同一个 Transformer 架构↓预训练权重 A→ 英文模型预训练权重 B→ 中文模型预训练权重 C→ 代码模型
结构没变。
变化的是权重。
可以把它理解成:
模型结构 = 大脑模型权重 = 记忆
或者:
模型结构 = 神经系统模型权重 = 学到的知识
因此真正决定模型能力的,往往不是结构文件,而是权重文件。
参数为什么会突然爆炸?
在天气模型里:
输入数量 = 2参数数量 = 2
很容易理解。
但神经网络规模一大,情况立刻变化。
假设:
1000 个输入↓1000 个输出
如果每个输出都需要看到所有输入:
1000 × 1000 = 1,000,000
就需要 100 万个权重。
再进一步:
8192 个输入↓8192 个输出
对应:
8192 × 8192 = 67,108,864
单个矩阵就有 6700 多万个参数。
而 GPT 的一层里不止一个矩阵。
几十层甚至上百层叠起来之后,参数数量很快达到:
70 亿700 亿1750 亿
级别。
所以权重文件越来越大,不是因为代码变复杂了,而是因为需要保存的数字越来越多。
为什么代码只有几百行,权重却有几十 GB?
看一个真实例子。
假设代码里写着:
nn.Linear(768, 768)
这一行代码只有十几个字符。
但它对应的权重矩阵大小是:
768 × 768 = 589,824
接近 59 万个浮点数。
代码描述的是:
这里有个 768×768 的矩阵
权重保存的是:
矩阵第 1 个格子是多少矩阵第 2 个格子是多少...矩阵第 589824 个格子是多少
所以:
代码负责描述规格权重负责填满内容
规格不会变得特别大。
内容可以无限增长。
这就是为什么:
模型结构几 KB ~ 几 MB模型权重几 GB ~ 几百 GB
进入 GPU 之后发生了什么?
加载完成后,权重会被放进显存。
然后开始推理。
CPU 和 GPU 的分工可以简单理解为:
CPU = 指挥GPU = 干活
CPU 负责告诉 GPU:
执行第 1 层执行第 2 层执行第 3 层...
GPU 负责真正计算矩阵乘法。
例如:
768×768 矩阵乘768×1 向量
最终得到:
768 个输出
这些输出之间互不依赖。
于是 GPU 可以把任务拆给大量计算单元同时执行。
GPU 之所以快,不是因为单个计算特别强,而是因为能同时做大量相同计算。
为什么推理前必须先把权重搬进显存?
很多人会问:
权重不是放在 SSD 里吗?为什么还要占显存?
原因很简单:
推理时会频繁访问权重。
假设一个模型有:
70B 参数
使用 FP16 存储:
70B × 2 Byte≈ 140GB
生成一个词时,几乎所有层都会访问自己的权重。
如果每次都去 SSD 读取:
SSD 带宽≈ 7GB/s
那么读完整个模型就需要十几到二十秒。
而用户希望看到的是:
每秒几十个 Token
显然做不到。
所以实际流程是:
SSD↓内存↓显存↓GPU计算
推理时,权重常驻显存。
输入数据不断流过这些权重矩阵。
这样 GPU 才能高速完成计算。
最后,把整个过程串起来
从头到尾,其实只有四步:
1. 定义模型结构2. 加载训练好的权重3. 把权重放进显存4. 输入数据逐层经过计算
最终得到结果。
整个链路如下:
模型结构(蓝图)+模型权重(经验和知识)↓加载到模型↓搬到GPU显存↓输入逐层流过↓输出结果
很多人第一次看到 GPT-3 有 1750 亿参数时,以为里面藏着某种全新的数学。
实际上并没有。
模型结构描述的事情非常简单:
乘法加法结果传给下一层
真正庞大的,不是算法本身,而是那些训练过程中学出来的数字。
大模型之所以“大”,不是因为代码有多复杂,而是因为它记住了太多东西。
代码负责定义大脑的结构。
权重负责保存大脑的记忆。
而推理,本质上就是让输入数据穿过这些记忆,最终得到答案。
更多推荐

所有评论(0)