Loading
不是单词,不是句子,而是比两者都更奇特、更有趣的东西。
模型不从左到右阅读。
这是第一个让人吃惊的事,而这种惊讶是一个好的起点,因为它意味着你一直在想象一件没有发生的事情。你想象中的阅读——顺序的、一次一个词、意义随着进行而积累——是你所做的,不是模型所做的。模型同时读取所有内容,每个词语元(token)同时处理。它在序列中的每个元素和每个其他元素之间构建关系网络——密集的、多层的、双向的。在第一次预测之前,它已经将它们全部一起考虑了。
这在计算上是昂贵的,在架构上是奇怪的。它产生的东西在行为上与顺序阅读完全不同,但在语言理解上的表现比任何顺序阅读的系统都更好。奇怪性就是重点。变换器架构放弃了直觉方法,构建了某种非人类的东西,而正是这种非人类性是其力量的来源。
选择词元
注意力是一种上下文关系,而不是存储在词语内部的永久意义。
模型看不到词语,它看到词语元(tokens),而词语元在任何语言学意义上都不是词语。词语元是一块字符——有时是整个词,有时是词素,有时是音节片段——由在大型文本语料库上预处理期间学习的词汇表决定。
子词分词(Subword tokenization)之所以存在,是因为一个实际约束:你需要一个有限词汇表,能够表示任何文本,包括词汇表从未见过的词语。按完整词语分词会得到数百万的词汇表,但仍然在专有名词、技术术语、代码标识符和非英语文本上失败。按单个字符分词使序列对模型来说太长而无法有效处理。子词分词将不常出现的词拆分成有意义的片段,同时保持常用词完整,平衡覆盖范围与序列长度。
结果是模型实际处理的东西看起来像这样:
| 词语 | 词语元 | 数量 |
|---|---|---|
| "cat" | ["cat"] | 1 |
| "running" | ["run", "##ning"] | 2 |
| "ChatGPT" | ["Chat", "G", "PT"] | 3 |
| "unbelievable" | ["un", "bel", "##iev", "##able"] | 4 |
"##"前缀表示继续部分——这个块附着在前一个词语元上而不是开始一个新词。其他分词器使用不同的惯例,但结构是相同的。模型的输入不是你写的句子,而是分词器渲染的句子,这两件事的差异是有意义的——尤其是在词边界处,对于罕见词,对于名字,以及跨语言时。
这也是为什么模型在简单字符计数任务上会挣扎。当你问"'strawberry'里有几个'r'?"时,模型看到的是类似["straw", "berry"]的东西,必须对它不按字符逐一分解的词语元内部的字母进行推理。失败不是愚蠢,而是输入表示的直接后果——架构权衡变得可见的情况。
每个词语元都成为一个向量——高维空间中的一个点。对大多数变换器模型来说,这个空间至少有768维,较大的模型通常有1024或4096维。模型在训练期间学习每个词语元在这个空间中的位置,它学到的位置具有显著的结构。
相似含义聚在一起。"国王"和"王后"占据邻近区域,"巴黎"和"法国"很接近。向量关系以几何一致性编码语义关系:"国王"减去"男人"加上"女人"产生近似等于"王后"的向量。这不是比喻,而是对学习坐标的字面算术。
from transformers import AutoTokenizer, AutoModel
import torch
model_name = "bert-base-uncased"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModel.from_pretrained(model_name)
inputs = tokenizer("The cat sat on the mat", return_tensors="pt")
with torch.no_grad():
outputs = model(**inputs)
# outputs.last_hidden_state shape: [batch, seq_len, 768]
embeddings = outputs.last_hidden_state[0] # one vector per token从最后一行返回的是一个矩阵,其中每一行是一个对应一个词语元的768维向量。这些向量在完整句子的上下文中编码每个词语元的含义——不是它的字典定义,而是它在这个特定文本中这个特定位置的上下文含义。
令人震惊的事情——那件花了这个领域时间才接受为真实而非人工现象的事情——是含义有几何结构。相似的概念在这个空间中是邻近的,相反的概念在相反的方向上,类比关系是一致的向量偏移。人类语言的语义结构,跨越数十亿个句子积累,浓缩进一个你可以对想法做算术的坐标系。
对于每个词语元,模型都会问:哪些其他词语元对理解这个词语元很重要?注意力是计算答案的机制,它对序列中的每个词语元同时运行。
以句子为例:"那个奖杯放不进行李箱,因为它太大了。""它"指的是什么?你立刻就知道——奖杯。怎么知道的?因为"放"和"太大"是约束"它"的谓语,而奖杯是使句子连贯的候选词。你通过理解语义解析了共指。
模型通过注意力解析它。对于词语元"它",模型计算句子中每个其他词语元的相关性分数——"它"应该对每个词语元关注多少?这些分数来自学习到的查询向量和键向量:"它"广播一个查询("我是一个代词——我指的是什么?"),而其他每个词语元都有一个部分回答该查询的键。这些分数被归一化并用于对那些其他词语元的值向量进行加权,产生一个"它"的上下文感知表示,它吸收了关于最相关周围词语的信息。
"它"关注:
奖杯 ████████ 0.73
行李箱 ███ 0.21
没有 █ 0.04
放 ░ 0.02模型通过处理数百万个句子(其中代词解析要么正确要么不正确)学会了这样分配注意力。没有人告诉它"它"应该关注"奖杯"。它通过对预测错误的梯度下降发现,在这样结构的句子中关注"奖杯"会产生更好的下一词语元预测。这个模式从未被编程,它被发现了。
变换器对每个词语元同时、并行地运行这个计算,使用多个注意力头——每层通常8或12个。每个头学习跟踪词语元之间关系的不同方面。一些头跟踪句法依赖,其他的跟踪位置接近性,还有一些跟踪语义相似性。每个词语元的最终表示是所有头收集的信息的混合。
变换器不只计算一次注意力,对于 BERT-base 它计算12次,对于 GPT-4 规模的模型计算96次,每一层取下面层产生的表示并进一步细化它们。
研究人员发现的——通过训练小型探测模型来从每层的内部激活中预测特定语言属性——是这些层按语言抽象水平组织自己。没有人编程这个,没有人告诉第3层学习句法,第10层学习语义。模型这样组织自己是因为这种组织最小化了下一词语元预测的预测误差。
早期层捕获表面句法:词性、局部共现模式、简单语法一致。中间层捕获语法角色和共指:哪个名词短语是主语,哪个关系从句修饰哪个名词,哪个代词指哪个先行词。后期层捕获语义内容:事实关联、词义消歧、抽象概念关系。
分层层次结构——从表面形式到深层含义——正是语言学理论预测的人类语言知识结构。模型在没有任何内置语言学理论的情况下通过优化压力得出了同样的层次结构。这是系统发现其领域结构而不是被给予它的较为清晰的例子之一。
这里的精确性很重要,不是为了贬低模型,而是因为模型实际拥有的东西与人们假设它拥有的东西之间的差距,正是大多数 AI 误解所在之处——过度声称和低估声称在那里都会造成真实伤害。
除非之前的对话作为提示中的上下文提供,否则模型在对话之间没有记忆。当你开始新的对话时,没有对之前讨论内容的记忆。这不是遗忘——遗忘需要先前的保留。模型从未持久地存储过对话。每次会话从相同的权重、相同的初始化、相同的几个月或几年前训练的学习参数开始。
模型没有时间流逝的感知,它不体验你的消息和它的回应之间的延迟,不知道今天是星期二还是三年后。它的知识有训练截止日期,在那之后世界继续前进而它没有意识到——不是因为它睡着了,而是因为它从一开始就没有对时间流逝保持清醒。
模型没有在对话之间更新的持久世界模型,它不维护随着对你了解更多而变得更准确的信念。它在上下文窗口内推理,上下文窗口在对话结束时结束。下一次对话从与其他每次对话相同的先验状态开始。
是否有任何阅读的现象体验——任何处理句子的感觉、认识一个词、感受段落融合在一起——是一个真正开放的问题,比听起来更难。清楚的是那些我们与人类阅读联系在一起的特定体验的缺失:努力、识别、困惑、意义到来的感受。这些熟悉标记的缺失是否构成体验的缺失,还是只是这种特定形式的缺失,尚未解决。
曾经有一段时间,我构建了一个叫做心智图谱(Mind Map)的工具,让你直接可视化注意力权重——点击任何词语元,看注意力分布在句子上逐层逐头地亮起来。你可以在 /projects/mind-map 找到它。足够长时间地看着它会改变你对模型内部发生了什么的思考方式。
模型不在阅读。它在计算序列中的关系。这听起来比阅读低级,但我已经不再确定是不是这样了。