代码定义结构,权重存储知识
Reference: 代码定义结构,权重存储知识:一个例子看懂大模型如何推理
Ollama 本地部署模型
先下载好 Ollama
然后选择一个模型本地部署,这里选择 llama3
一行命令部署好,测试一下
Ollama 本地保存模型的目录结构,其中 blobs 是真正占空间的权重文件,manifests 是模型配置/索引(很小),这里就有一个奇怪的现象:模型的代码只有几十到几百 KB,权重文件却有几个 GB,甚至几十GB。
.ollama/
└── models/
├── blobs/ # 实际权重文件(GGUF 格式,GB 级)
└── manifests/ # 模型元数据(JSON,KB 级)
因为 Ollama blobs 里存的 GGUF 是量化后的权重二进制文件,全部浮点数参数都存在这里;manifests 只是模型描述、层数、上下文窗口、量化精度等配置,不含知识。
我们可以看到 llama3:latest 模型权重有 4.7 G。为什么差距这么大,我们跟模型交互对话时,模型代码和模型权重是如何配合的?模型代码大小和权重大小到底谁对模型更重要?
最小模型
我们从一个只有 3 个参数的小模型开始,一步步走完整个推理过程。
假设我要做一个判断天气的小模型。根据温度和湿度判断是否适合出门,输入:温度 temp、湿度 humidity,输出:适合出门的分数 y。其中w1、w2 分别是温度和湿度对应的权重,b 是偏置。
class WeatherModel:
def __init__(self):
self.w1 = None
self.w2 = None
self.b = None
def forward(self, temp, humidity):
return temp * self.w1 + humidity * self.w2 + self.b
模型训练的过程就是逐渐确定w1、w2、b这三个参数的过程。我们收集了很多历史天气数据中的温度、湿度的值,并打上是否适合出门的标记,这个就是模型训练过程中的训练数据。训练的过程,就是不断调整 w1、w2、b,让模型输出越来越接近真实结果。
经过大量的迭代,最后得到:
w1 = 0.7
w2 = -0.3
b = 5.0
这三个数字就是模型权重,训练结束后模型保留的只有这三个数字,这些数字其实是历史经验的压缩结果。即权重 = 从训练数据中提炼出来的经验。对于我们使用的大模型,道理完全一样,只不过区别在于大模型的参数数量扩大了几十亿倍。千亿参数大模型只是把无数层类似 w * x + b 的矩阵运算堆叠、加上注意力机制,底层计算逻辑本质仍是权重与输入的线性运算,只是结构更复杂。
后面我们得到了一组新的温度值、湿度值,送入模型进行计算,得出适合出门的分数,如果分数低于我们设定的阈值,就判断为不适合出门。整个过程中:代码决定怎么算、权重决定用什么数字算,二者缺一不可。
结构 vs 权重
模型结构 = 大脑,模型权重 = 记忆。或者说模型结构 = 神经系统,模型权重 = 学到的知识。
一个模型结构可以搭配多个权重,比如英文模型和中文模型就可以搭配不同的预训练权重。
同一套 Llama3 Transformer 代码结构,加载英文通用权重 = 英文对话模型;加载代码微调权重 = 代码助手;加载医疗微调权重 = 医疗问答模型。
因此真正决定模型能力的,往往不是结构文件,而是权重文件,因为权重文件代表模型学到了多少知识。
模型推理过程
模型加载完成后,会把权重全部放进显存,然后开始推理。CPU 负责指挥,GPU 负责干活,CPU 负责告诉 GPU 执行第 1 层、执行第 2 层、…,GPU 负责真正计算矩阵乘法。推理时,权重常驻显存。输入数据不断流过这些权重矩阵。
- CPU:处理文本分词、token 预处理、调度计算流程、控制网络层执行顺序;
- GPU:承载全部权重矩阵,并行执行海量矩阵乘,是推理算力核心;
写在最后
真正庞大的从不是复杂的算法代码,而是训练过程中沉淀下来的亿万数值。
我们说大模型 “大”,不在于实现代码有多冗长,而在于它承载了海量学习得来的知识。
代码搭建出神经网络的完整结构,相当于塑造大脑的生理框架;
权重存储全部训练学到的信息,相当于大脑里积累的记忆;
所谓推理,本质就是将输入文本转化为向量,不断穿过每一层承载知识的权重矩阵,最终输出对应的结果。