@royvanrijn: 对于好奇的开发者,我构建了《大语言模型的解剖结构》,这是一个交互式解释器,展示了文本如何变成令牌、向量、注意力……

X AI KOLs Timeline 工具

摘要

由 Roy van Rijn 构建的交互式可视化指南,解释了大语言模型的工作原理,从令牌化到注意力机制、Transformer 模块以及文本生成。

对于好奇的开发者 🧠 我构建了《大语言模型的解剖结构》,这是一个交互式解释器,展示了文本如何变成令牌、向量、注意力、Transformer 模块,最终生成文本。 https://t.co/fgCeZuQwJf
查看原文
查看缓存全文

缓存时间: 2026/05/29 14:09

对于好奇的开发者🧠

我构建了“大语言模型解剖”,一个交互式讲解器,展示文本如何变成标记、向量、注意力、Transformer模块,最终生成文本。

https://t.co/fgCeZuQwJf


大语言模型解剖 | 语言模型工作原理交互式可视化指南

来源:https://www.royvanrijn.com/anatomy-of-an-llm/ 引言

引言

大型语言模型感觉像个黑箱。你输入提示,得到一些聪明的回复,中间有数十亿个参数据说在执行“AI”。

本指南将打开这个箱子。

我们将从头到尾追踪一条链路。首先,文本被拆分成标记(tokens)。这些标记变成向量。向量在注意力层和前馈网络层中移动。最后,模型为可能的下一个标记生成分数,解码策略选择输出哪个标记。

目标不是记住每个公式。目标是理解每一步发生了什么变化,以及这一步为何存在。

如果你想知道LLM如何工作、Transformer如何工作,或者注意力、分词、KV缓存和量化是如何结合在一起的,这个页面用一个视觉路径把这些概念串起来。

最后,你应当能够追踪完整路径:

01文本

02标记

03向量

04Transformer模块

05Logits(逻辑值)

06采样

07输出

一旦你能追踪这条路径,黑箱就变得小多了。

你将得到什么

具体的可视化、先从小数字开始、以及交互式控件,使每次变换都可检查。

如何使用

从上往下滚动作为一个连贯叙述,或跳转到特定概念的章节。

谁制作的

Roy van Rijn 在 openvalue(https://www.openvalue.eu/)工作

目录

  1. 01 分词(https://www.royvanrijn.com/anatomy-of-an-llm/#tokenization)
  2. 02 向量嵌入(https://www.royvanrijn.com/anatomy-of-an-llm/#embeddings)
  3. 03 神经元激活(https://www.royvanrijn.com/anatomy-of-an-llm/#neuron-activation)
  4. 04 前馈神经网络(https://www.royvanrijn.com/anatomy-of-an-llm/#feed-forward-network)
  5. 05 Logits 与采样(https://www.royvanrijn.com/anatomy-of-an-llm/#logits)
  6. 06 反向传播(https://www.royvanrijn.com/anatomy-of-an-llm/#backpropagation)
  7. 07 优化器(https://www.royvanrijn.com/anatomy-of-an-llm/#optimizers)
  8. 08 注意力:Q、K与V(https://www.royvanrijn.com/anatomy-of-an-llm/#qkv)
  9. 09 多头注意力(https://www.royvanrijn.com/anatomy-of-an-llm/#multi-head-attention)
  10. 10 RoPE(旋转位置编码)(https://www.royvanrijn.com/anatomy-of-an-llm/#rope)
  11. 11 Transformer模块(https://www.royvanrijn.com/anatomy-of-an-llm/#transformer-block)
  12. 12 训练阶段(https://www.royvanrijn.com/anatomy-of-an-llm/#training-phases)
  13. 13 后训练(https://www.royvanrijn.com/anatomy-of-an-llm/#post-training)
  14. 14 上下文与KV缓存(https://www.royvanrijn.com/anatomy-of-an-llm/#kv-cache)
  15. 15 量化(https://www.royvanrijn.com/anatomy-of-an-llm/#quantization)

第01章

分词

在模型能思考文本之前,文本必须先变成数字。

语言模型不像我们这样阅读单词和句子。它读取一系列标记ID: 由分词器生成的整数。

这使得分词成为模型真正的入口。此后的所有操作都基于数字,而不是原始字符。

一个标记可以是整个单词、单词的一部分、标点、空格,或者是像代码、表情符号或名字这样奇怪的东西的一部分。这就是为什么当你第一次看到分词时,它常常看起来有点奇怪。分词器并不是试图像人类那样分割文本。它试图用固定词表高效地表示文本。

如果每个标记都是一个完整的单词,词表会爆炸。如果每个标记都是一个字符或字节,每个句子会变得非常长。现代分词器处于这两个极端之间。

分割文本

在文本进入语言模型之前,必须将其重写为数字。

分词就是执行此操作的步骤。它将文本分割成小而可复用的片段,称为标记。一个标记可以是整个单词、单词的一部分、标点、数字,甚至是一个空格加上下一个单词的开头。

每个标记在分词器的词表中都有一个条目,并被替换为对应的整数ID。从那时起,模型不再直接处理字符。它看到的是一个有序的标记ID列表。

为什么不只用单词?

整个单词太僵化了。新名字、拼写错误、代码、词形变化、复合词和多语言文本会不断产生模型从未见过的单词。

为什么不只用字母或字节?

这解决了“未知词”问题,但会使每个输入变得更长。更多的片段意味着模型要做更多的工作,并且能放入同一窗口的上下文更少。子词标记是合理的折中:常见文本保持紧凑,而不寻常的文本仍然可以从更小的片段构建出来。

下面你可以用OpenAI的o200k_base分词器进行实验。尝试切换句子,观察边界落在哪里。

在本指南的后面部分,当模型预测下一个标记时,它将在同一个词表上进行预测。

技术说明:下面的例子是使用 tiktoken(https://github.com/openai/tiktoken) 和 o200k_base 编码生成的。

示例句子

原始句子

如果人脑简单到我们能够理解它,那么我们也就会简单到无法理解它。

102个字符

22个标记

5字符/平均每个标记

分词结果

If

#3335

·the

#290

·human

#5396

·brain

#12891

·were

#1504

·so

#813

·simple

#4705

·that

#484

·we

#581

·could

#2023

·understand

#4218

·it

#480

,

#11

·we

#581

·would

#1481

·be

#413

·so

#813

·simple

#4705

·that

#484

·we

#581

·couldn’t

#21149

.

#13

显示标记ID显示空白标记

重要结论

分词不仅仅是预处理。它决定了模型在一个上下文窗口中能看到什么、你的文本花费有多大,以及模型允许预测哪些标记。

一个单词不等于一个标记

不同模型使用不同的分词器。同一句话根据模型不同可能变成不同数量的标记。

第02章

向量嵌入

标记ID只是标签。嵌入将这些标签变成网络可以处理的东西。

分词之后,每个标记用一个整数ID表示。但是一个ID本身没有有用的几何意义。标记15339与标记15340并不“接近”。数字只是标签,就像表格中的行号。

嵌入层通过将每个标记ID转换成一个向量(一个学习到的数字列表)来解决这个问题。从技术上讲,这是一个查找操作。模型有一个嵌入矩阵,每个标记ID选择该矩阵的一行。

从概念上讲,这是离散符号进入连续空间的那一瞬间。一旦标记变成向量,模型就可以比较它们、组合它们、旋转它们、投影它们,并逐渐重塑它们。

这些向量内的值在训练过程中学习。出现在相似上下文中的标记通常最终得到相关的向量,但这并不是一个干净的意义字典。它更像一个混乱的高维坐标系,充满了有用的信号。

初始嵌入大多是上下文无关的。标记“bank”在“river bank”和“investment bank”中从相同的嵌入开始。后续层使用周围的标记将该向量重写为更具体的东西。

从标记ID到嵌入向量

嵌入查找

分词之后,每个标记ID被用作嵌入表的索引。选中的行是一个高维向量,成为模型对该标记的起始表示。

为了可读性,本章使用一个玩具嵌入维度,宽度为24。实际模型的宽度通常要大得多,常见的生产环境宽度包括768、1024、1536、3072,甚至更高。

一个嵌入向量只是一列浮点数:dog=[0.7292, -0.3786, 0.1065, 0.3674, 0.1902, -0.7881, …]

示例句子句子中的标记 If

->

标记ID #3335

->

嵌入行3335

嵌入值(24维)

本指南展示了所有24个玩具向量值。

0.2173

0.5424

0.264

-0.9419

-0.5084

0.0872

-0.6438

0.164

-0.2094

0.6078

0.9056

-0.5944

0.1676

-0.0086

-0.6874

-0.5004

-0.4561

-0.168

0.443

-0.6566

-0.184

-0.4863

0.679

-0.044

相同的标记ID总是映射到相同的嵌入向量。

在真实模型中,这些嵌入值是在训练过程中学习的。出现在相似上下文中的标记会逐渐被移动到向量空间中有用的区域,因此向量最终编码了模型可以依赖的模式。

经常扮演相似角色的标记会被推向相似的方向。例如,标记cat, dog, rabbit经常出现在像“The ___ is sleeping”、“I fed the ___”或“The ___ ran away”这样的句子模板中。由于它们出现在相似的上下文中,它们的向量可能最终彼此靠近。

catcar通常出现在非常不同的上下文中,所以它们的向量往往最终相距更远。

嵌入空间不是人工设计的。没有人告诉模型“把动物放在这里”或“把动词放在那里”。这些模式之所以出现,是因为以这种方式移动向量有助于模型更好地预测文本。

2D类比直觉

向量之间的嵌入距离如果有相似的关系,通常也相似。

puppy(小狗) dog(狗) kitten(小猫) cat(猫)

重要结论

嵌入是标记的起始表示,而不是最终含义。模型的其余部分会在上下文流经网络时不断地重写该向量。

玩具规模

在本指南中,我们使用小向量,因为它们适合屏幕显示。真实模型使用更宽的向量:每个标记几百、几千甚至更多维度。

第03章

神经元激活

加权和还不够。非线性是网络表达能力的来源。

一个神经元接收输入,乘以权重,求和,并输出一个数字。但如果这就是全部,深度学习就不会那么深了。

没有激活函数,堆叠层仍然像一个大线性变换。你可以将矩阵相乘,然后把整个堆叠压缩成一个矩阵。

激活函数打破了那种线性。它决定有多少信号通过。一些值被放大,一些被减弱,一些被推向零。

这使得网络能够构建弯曲的、有条件的、非线性的变换,而不是仅仅缩放和旋转向量。真实模型使用矩阵运算以大规模批处理的方式执行这些操作,同时进行数百万次激活。

单神经元变换

一个神经元接收输入,应用权重,然后将结果通过一个激活函数。这个非线性步骤是让网络能够建模更丰富模式的关键。

z = w1*x1 + w2*x2 + w3*x3

output = activation(z)

神经元示意图

w1=1.10 w2=-0.85 w3=0.55 x1 0.70 x2 -0.25 x3 0.45 Σ z=1.23 GELU out 1.10 输入

x1 0.70 x2 -0.25 x3 0.45 权重

w1 1.10 w2 -0.85 w3 0.55 激活

激活函数根据值的幅度平滑门控,而不是硬裁剪。Transformer模块中常见;计算上比ReLU略重。

神经元输出1.0953

激活曲线

x = 0 y = 0 标记位置随着加权输入z的变化而实时更新。

重要结论

激活函数不是装饰品。它是让堆叠的层变得不仅仅是一个大线性计算的东西。

现代选择

现代Transformer模型可能使用GELU、SiLU或如SwiGLU这样的门控变体。具体选择会改变前向信号以及训练过程中梯度的流动方式。

第04章

前馈神经网络

真实的层不是单个神经元。它是许多简单计算并行运行。

单个神经元是一个有用的教学工具,但模型不会一次处理一个神经元。前馈网络并行应用许多学到的变换。

不是画出每个神经元和每个连接,实现通常用矩阵乘法来表示同一件事。友好的示意图说输入流过神经元。实现说:乘一个矩阵,应用激活,再乘另一个矩阵。

这些是在不同尺度下叙述的同一个故事。

在Transformer模块中,前馈部分通常按位置独立工作。每个标记向量被扩展成更宽的隐藏表示,通过非线性,然后投影回模型宽度。

注意力在位置间移动信息。前馈网络变换每个位置内部的信息。

密集层数学,可视化

这里我们不训练一个完整网络,而是聚焦于一次前向传播。一个密集层简单意味着一个层中的每个节点都连接到下一层的每个节点。

与单个神经元相同的数学现在用矩阵并行完成:

X (1x2) · W1 (2x3) = Z1 (1x3),然后A1 = activation(Z1),然后A1 (1x3) · W2 (3x2) = Z2 (1x2)

矩阵乘法就是一次进行许多加权和。每个输出列是一个神经元,输入中的每一行通过其匹配的权重行做出贡献。

全连接视图

X W1 A1 W2 A2 x1 x2 h1 h2 h3 o1 o2 悬停顶部标签检查矩阵。绿色边框表示激活,红色表示抑制。

x1 0.80 x2 -0.30 激活

矩阵检查器

悬停一个顶部标签 (X, W1, A1, W2, A2) 来检查该矩阵和乘法步骤。

乘法如何映射到连接

W1中的列j包含进入隐藏神经元j的权重。行i对应输入特征i。所以每个隐藏预激活值为: z1_j = x1*w1_1j + x2*w1_2j

第二层以A1作为输入重复该模式: z2_k = a1_1*w2_1k + a1_2*w2_2k + a1_3*w2_3k。这正是图计算,只是向量化了。

用矩阵形式,我们避免单独写每个神经元: [x1 x2] · W1 = [z1_1 z1_2 z1_3],然后激活逐元素应用产生A1。然后该A1行乘以W2,一次产生两个输出神经元。

来自当前滑动块的例子: z1_1 = +0.80*+0.70 + -0.30*+0.10 = +0.53。如果激活函数抑制了此值(例如ReLU对负值),则该路径对下一层贡献较少或为零。

重要结论

前馈网络是每个标记向量被重写的地方。它不是关于在标记之间移动信息;而是在每个标记位置处变换表示。

矩阵视图

矩阵视图不是神经元图的一个更不直观的版本。它是同一计算的可扩展版本。

第05章

Logits 与采样

模型不直接输出一个单词。它输出可能的下一个标记的分数。

在处理完输入后,模型还没有选择一个单词。它产生的是一个原始分数向量:词表中每个标记一个分数。这些分数称为logits。

一个logit不是概率。它仅仅是一个未归一化的分数。较高通常意味着“模型认为这个标记在这里更合适”,但这些数字加起来还不是100%。

为了把logits变成概率,我们应用softmax。然后进入解码:从那个分布中选择下一个标记的策略。

贪心解码总是选择最可能的标记。温度改变分布的形态。Top-k将选择限制在k个最可能的标记内。Top-p(也叫核采样)从总概率超过一个阈值的最小标记组中选择。

模型产生分布。解码器决定我们在从中采样时有多大冒险精神。

相似文章