Kmoon Blog

代码定义结构,权重存储知识

约 1.5k 字 3 min

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 负责真正计算矩阵乘法。推理时,权重常驻显存。输入数据不断流过这些权重矩阵。

写在最后

真正庞大的从不是复杂的算法代码,而是训练过程中沉淀下来的亿万数值。

我们说大模型 “大”,不在于实现代码有多冗长,而在于它承载了海量学习得来的知识。

代码搭建出神经网络的完整结构,相当于塑造大脑的生理框架;

权重存储全部训练学到的信息,相当于大脑里积累的记忆;

所谓推理,本质就是将输入文本转化为向量,不断穿过每一层承载知识的权重矩阵,最终输出对应的结果。

#LLM #Ollama