首页/Agent 核心/Token 与词嵌入
Agent 核心 · 第 01 章

Token 与词嵌入

本章是「第一章 · 它是什么」的开篇。我们先建立一张概念地图——AI、机器学习、深度学习、大模型谁包含谁,监督、自监督、强化三大学习范式如何塑造了 LLM 的训练方式;再回答一个最根本的问题:模型根本"看不见"文字,它眼…

约 29 分钟11,741 字22 个代码示例

本章是「第一章 · 它是什么」的开篇。我们先建立一张概念地图——AI、机器学习、深度学习、大模型谁包含谁,监督、自监督、强化三大学习范式如何塑造了 LLM 的训练方式;再回答一个最根本的问题:模型根本"看不见"文字,它眼里只有数字。文字先经分词器切成 token,再经嵌入层变成向量——这条"从文字到向量"的数字化链路,是理解注意力机制、上下文窗口、API 计费、RAG 检索的共同地基。下一章将打开模型内部,看神经网络如何学习。

1. 开篇导引:AI、机器学习与大模型

在动手写任何 Agent 之前,先花 20 分钟把几个高频词汇的关系理清楚。你不需要会推导公式,但需要一张准确的概念地图——否则后面读任何资料、做任何技术选型,都容易张冠李戴。

1.1 概念嵌套:谁包含谁

这四个词经常被混用,但它们是严格的嵌套关系:

人工智能 (AI)
 └── 机器学习 (ML)
      └── 深度学习 (DL)
           └── 大语言模型 (LLM)

用一句话区分:

概念 核心思想 典型例子
人工智能 AI 让机器表现出智能行为的总目标 围棋程序、导航、聊天机器人
机器学习 ML 不手写规则,让机器从数据中总结规律 垃圾邮件分类、房价预测
深度学习 DL 用多层神经网络从数据中提取规律 图像识别、语音识别、LLM
大语言模型 LLM 超大规模的深度学习模型,专门处理语言 GPT、Claude、Qwen、DeepSeek

一个常见的误解是把"AI"等同于"LLM"。实际上你在地图 App 里看到的路径规划、游戏里的 NPC 行为树,都是 AI,但和神经网络无关。Agent 开发者每天打交道的,主要是最内层的 LLM,以及围绕它构建的系统。

另外要知道:实现 AI 有多条历史路线。早期主流是"符号主义"——把知识写成显式规则(if-else 的极端形态),专家系统是它的巅峰,但规则永远写不完,最终败给了"数据主义"——也就是机器学习:与其教机器规则,不如给它数据让它自己学。

1.2 机器学习的本质:从"写规则"到"找参数"

用你熟悉的方式对比一下。

传统编程:

规则 + 数据 --程序--> 结果
(你写 if-else,数据进来,确定性输出)

机器学习:

数据 + 结果 --训练--> 规则(模型)
(给它样本和标准答案,它总结出规律)

举个开发中的类比:假设你要判断用户输入的是不是手机号。传统做法是你写正则。机器学习做法是你准备十万条"输入+是否手机号"的样本,扔给算法,它输出一个模型——你拿到的是一堆数字(参数),而不是一段能读懂的逻辑。

这个"模型 = 一堆参数"的认知非常重要,后面会反复出现:

所谓模型,就是一个函数,函数里全是待定的数字。训练,就是把这堆数字调到合适的值。

用代码感受一次"学习"

下面是最小化的机器学习示例:从带噪声的数据中学出 y ≈ 2x + 1 这个规律。先看"封装好的写法":

import numpy as np
from sklearn.linear_model import LinearRegression

# 造数据:y = 2x + 1,加了随机噪声模拟真实世界的脏数据
rng = np.random.default_rng(42)
X = rng.uniform(0, 10, 200).reshape(-1, 1)
y = 2 * X.squeeze() + 1 + rng.normal(0, 0.5, 200)

model = LinearRegression()
model.fit(X, y)          # 这一步就是"学习"

print(model.coef_, model.intercept_)   # 输出约 [2.0] 和 1.0 —— 它自己学出来的

运行后你会发现模型学到的系数约等于 2 和 1——没有人告诉它公式,它从 200 个样本里自己找到了。这个"哇"的时刻,就是机器学习的全部魔法所在(第 02 章会拆开 fit 里面发生了什么,其实就是循环执行梯度下降)。

1.3 三大学习范式

同样是"从数据学",按"数据有没有标签、反馈从哪来"可以分出三种范式。这个分类直接影响你理解 LLM 的训练流程,也决定了你该对模型的输出抱多大信任。

监督学习(Supervised Learning)

数据形态:(输入, 标准答案) 成对出现。

例子:垃圾邮件分类。你有十万封邮件,每封都标注了"是/否垃圾邮件",算法学习从内容到标签的映射。

在 LLM 世界里的对应:后训练阶段的指令微调(SFT)——人类写好"问题 + 理想回答"的示范对,模型照着学。详见第 04 章。

自监督学习(Self-supervised Learning)

数据形态:没有人工标注,但可以从数据自身"造出"标签。

这是理解 LLM 的关键。语言模型的任务是"预测下一个词":

输入:今天天气真
标签:好          ← 这个标签不是人标的,就是原文本身

整本维基百科都是训练数据:把每个位置的下一个词遮住,让模型猜,猜错了就调整参数。不需要任何人工标注,人类几千年积累的文字全部变成了现成的训练数据——这是大模型能"大力出奇迹"的根基。

类比:完形填空。你做一万道完形填空题后,语感会大幅提升,虽然没人给你讲过语法。

强化学习(Reinforcement Learning)

数据形态:没有标准答案,只有行为的"奖励/惩罚"反馈。

Agent 在环境里试错:做出动作 → 环境给回报 → 学会追求长期回报最大的策略。

在 LLM 世界里的对应:RLHF(人类反馈强化学习)——让人类对模型的多个回答排序,训练一个奖励模型,再用强化学习让模型往人类偏好的方向调整。详见第 04 章。

三者对比

监督学习 自监督学习 强化学习
数据形态 (输入, 标签) (输入, 自造标签) (状态, 动作, 奖励)
反馈来源 人工标注 数据本身 环境/评价者
数据规模 受标注成本限制 只受数据量限制 受交互成本限制
在 LLM 中的角色 指令微调 预训练 对齐人类偏好

还有一个不入流但常被提到的:无监督学习(比如聚类),自监督可以看作它的一种特例(自动构造监督信号),这里不展开。

1.4 生成式与判别式

按模型的输出目标,还可以分一刀,这一刀解释了为什么 ChatGPT 是"聊天"而不是"分类器":

判别式模型 生成式模型
学的是什么 输入 → 类别的边界 数据本身的分布
输出 类别/概率值 完整的内容(一段文字、一张图)
例子 垃圾邮件分类器 GPT、Stable Diffusion
一句话 判断"是什么" 创造"是什么样"

一个有助于建立直觉的理解角度:生成式建模比判别式更难,因为它要理解数据的"全貌"而不是只找决策边界。语言模型为了预测下一个词,被迫学会语法、事实知识、推理模式——预测下一个词这个看似简单的任务,逼出了通用智能。这也是为什么 LLM 的能力常常超出设计者预期。

1.5 Agent 开发者为什么需要这些

你可能会想:我调 API 就够了,为什么先学范式?三个直接理由:

  1. 看懂模型卡和文档。任何模型的说明里都有"预训练/微调/对齐"这些词,不了解范式,就只能死记硬背参数怎么调。
  2. 预判模型的能力边界。知道自监督的语料里有什么、没有什么,你就能判断哪些任务模型"天生会"、哪些需要 few-shot 示例、哪些必须外挂工具。比如问模型今天几号——训练数据里没有"现在",它必然答不准,这就是需要工具的原因之一。
  3. 理解 Agent 的定位。Agent 的本质(第 08 章)是"让模型在循环中决定调用什么工具"。而"决定"这个能力,来自后训练阶段学会的指令遵循。不懂训练流程,就理解不了模型为什么"会听话",也理解不了为什么它有时不听话。

2. 模型眼中的文字:Token 与分词

模型的输入不是文字,是数字。中间的翻译官叫分词器(Tokenizer)。理解 token 是 Agent 开发的实用刚需:API 按它计费、上下文窗口按它计算、模型数不清 "strawberry" 里有几个 "r" 的著名缺陷也源于它。这一节我们手写一个能用的 BPE 分词器,把这个"翻译官"彻底看透。

2.1 为什么需要分词

神经网络只能处理数字(准确说是向量)。把文字变成数字,最朴素的三种方案各有致命缺陷:

方案 词表大小 问题
按字符切 极小(英文 26 个) 序列太长,"hello" 占 5 个位置;单个字符没有语义
按单词切 百万级 词表爆炸;遇到没见过的词(拼写错误、新术语)直接失效
不切,整个句子 天文数字 不可行

现实需要的方案:常见的内容切长一点(省空间、有语义),罕见的内容切短一点(保证不失效)。这就是 BPE(Byte Pair Encoding,字节对编码)的核心思想。

2.2 BPE 的原理:从数据里学切法

BPE 原本是 1994 年的压缩算法,2016 年被引入 NLP。它的思路一句话说清:

从"每个字符单独成块"开始,统计语料里相邻两块最常一起出现的组合,把它们合并成一个新块。重复几千次,常见词自然粘连成整块,罕见词保持碎块。

用一个小语料走一遍(先看英文,中文同理后面讲):

语料:  low, lower, newest, widest
初始:  l o w / l o w e r / n e w e s t / w i d e s t

第一轮统计,e 和 s 相邻出现 2 次(newest、widest),其他对都只有 1 次。合并 es:

l o w / l o w e r / n e w es t / w i d es t

第二轮统计,es 和 t 相邻出现 2 次。合并 est:

l o w / l o w e r / n e w est / w i d est

第三轮,l 和 o 相邻 2 次(low、lower)。合并 lo:

lo w / lo w e r / n e w est / w i d est

继续几百轮后,"low" 会变成一个不可分的 token,而 "wideness" 这种没见过的词会退化为 "wide" + "ss" 之类的碎片——老词成块,新词拆碎,永远不会有词表外的词。

最后每个"块"(token)分配一个编号,得到词表(vocabulary)。编码文本 = 查表把 token 变编号,输入模型。

2.3 手写一个 Mini BPE

下面是完整可运行的简化版 BPE(约 40 行)。虽然生产级实现(如 tiktoken、sentencepiece)有很多工程优化,但核心逻辑就是这些:

from collections import Counter

class MiniBPE:
    def __init__(self, num_merges: int):
        self.num_merges = num_merges   # 计划合并的次数 = 词表扩张量
        self.merges: list[tuple[str, str]] = []

    @staticmethod
    def _to_pieces(word: str) -> list[str]:
        # 初始切分:每个字符一个块,末尾加特殊结束符标记词边界
        return list(word) + ["</w>"]

    def _count_pairs(self, corpus: list[list[str]]) -> Counter:
        pairs = Counter()
        for word in corpus:
            for i in range(len(word) - 1):
                pairs[(word[i], word[i + 1])] += 1
        return pairs

    def _merge_in_corpus(self, corpus, pair):
        merged = []
        for word in corpus:
            new_word, i = [], 0
            while i < len(word):
                if i < len(word) - 1 and (word[i], word[i + 1]) == pair:
                    new_word.append(word[i] + word[i + 1])   # 粘成一块
                    i += 2
                else:
                    new_word.append(word[i])
                    i += 1
            merged.append(new_word)
        return merged

    def train(self, text: str):
        corpus = [self._to_pieces(w) for w in text.split()]
        for _ in range(self.num_merges):
            pairs = self._count_pairs(corpus)
            if not pairs:
                break
            best = max(pairs, key=pairs.get)       # 找最高频相邻对
            self.merges.append(best)
            corpus = self._merge_in_corpus(corpus, best)

    def encode(self, word: str) -> list[str]:
        pieces = self._to_pieces(word)
        # 按训练学到的合并顺序重放
        for a, b in self.merges:
            i, new_pieces = 0, []
            while i < len(pieces):
                if i < len(pieces) - 1 and (pieces[i], pieces[i + 1]) == (a, b):
                    new_pieces.append(a + b); i += 2
                else:
                    new_pieces.append(pieces[i]); i += 1
            pieces = new_pieces
        return pieces

# ---------- 训练 ----------
bpe = MiniBPE(num_merges=12)
bpe.train("low low low lower newest newest newest widest widen")

for w in ["low", "lower", "newest", "widen", "lowering"]:
    print(f"{w:>10} → {bpe.encode(w)}")

输出:

       low → ['low', '</w>']
     lower → ['low', 'er', '</w>']
    newest → ['newest', '</w>']
     widen → ['wid', 'en', '</w>']
  lowering → ['low', 'er', 'ing', '</w>']   ← 训练时没见过的词,自动拆碎

观察结果:

  • low、newest 出现频繁,被完整粘成单 token
  • widen、lowering 是训练语料外的词,自动退化为子词组合,永不失效

这正是我们要的性质:高频词省空间、低频词保兼容。

2.4 中文在 BPE 里会发生什么

生产级分词器(如 Qwen、DeepSeek 的)通常在字节层面做 BPE(Byte-level BPE),好处是任何 UTF-8 文本——中文、emoji、生僻字——都能编码,绝无"未知字符"。

但在中文语料不足的词表里,汉字经常被切成多个字节碎片。一个直观的对比(数量级示意,不同词表有差异):

文本 大致 token 数
"hello world" 2
"你好世界" 4 左右(好词表)~ 8+(差词表)

推论很实用:同样一件事,用中文提问比用英文多消耗 token——直接影响 API 费用和上下文占用。做 Agent 的时候,长 system prompt 用中文写会比英文更快吃满窗口。

用 tiktoken(OpenAI 的分词器,Python 直接可用)实测:

# uv add tiktoken
import tiktoken

enc = tiktoken.get_encoding("o200k_base")       # GPT-4o 系列的词表

for text in ["hello world", "你好世界", "strawberry"]:
    print(f"{text!r:>16} → {len(enc.encode(text))} tokens")

print(enc.encode("strawberry"))                   # 看看它被切成了什么

输出:

   'hello world' → 2 tokens
     '你好世界' → 4 tokens
    'strawberry' → 2 tokens        ← 'str' + 'awberry' 或类似切法

2.5 Token 解释了模型的经典缺陷

为什么大模型数不清 "strawberry" 有几个 "r"?

因为模型看到的不是 s-t-r-a-w-b-e-r-r-y 十个字符,而是 ['str', 'aw', 'berry'] 这样的 token 碎片。字符信息在分词阶段就被压缩掉了——你让模型数 r,相当于让一个只能看到 "str / aw / berry" 三个积木的人数积木里的字母,只能靠背(训练时见过相关文本)或拼凑。

这个例子对 Agent 开发者的启示:给模型布置任务时,要意识到它的感知粒度是 token,不是字符、不是像素。数数、对齐、精确复制这类任务天生吃亏,属于"能用工具就别为难模型"的范畴。同类缺陷还有:

  • 反转单词的字母顺序(同样依赖字符级感知)
  • 生成指定字数的长文(token 到字符的映射不是定长)
  • 精确的数学计算(token 之间没有数位对齐)——需要工具调用

2.6 和 Agent 开发直接相关的三件事

① 计费。几乎所有 LLM API 按 token 计价,输入输出分开算。系统提示词很长时,每一轮对话都在为它付费。

② 上下文窗口。"128K 上下文"指的是 128K 个 token,不是字数。粗略换算:英文 1 token ≈ 0.75 个单词;中文一个汉字 ≈ 0.6~1 个 token(取决于词表质量)。

③ 首字延迟。API 的"输出速度"通常标注为 xx tokens/s。生成 1000 token 的回答,在 50 tokens/s 的速度下要 20 秒——这就是为什么流式输出(streaming)是长回答的标配体验。

一个实用的 token 用量监控示例(为第 05 章的 API 实战预热):

from openai import OpenAI

client = OpenAI(base_url="http://localhost:11434/v1", api_key="ollama")

resp = client.chat.completions.create(
    model="qwen3:4b",
    messages=[{"role": "user", "content": "用一句话解释什么是 token"}],
)

usage = resp.usage
print(f"输入 {usage.prompt_tokens} + 输出 {usage.completion_tokens} = "
      f"{usage.total_tokens} tokens")

3. 语义的几何空间:词嵌入

Token 编号只是给每个词发了个身份证号,编号本身不携带语义——"猫"是 8721、"微波炉"是 4306,这两个数字之间的关系毫无意义。词嵌入(Embedding)解决的问题是:把每个 token 变成一个几百到几千维的向量,让语义关系变成几何关系。这是"模型理解语言"的物理基础。

3.1 从 one-hot 到稠密向量

最直接的编码方式是 one-hot:词表大小 $V$,每个词是一个 $V$ 维向量,只有自己对应的位置是 1。

import numpy as np

vocab = ["猫", "狗", "微波炉"]
for i, w in enumerate(vocab):
    v = np.zeros(len(vocab)); v[i] = 1
    print(f"{w}: {v}")
猫: [1. 0. 0.]
狗: [0. 1. 0.]
微波炉: [0. 0. 1.]

两个致命缺陷:

  1. 维度灾难:词表 100K,每个词就是 100K 维的向量,其中 99999 个数字是 0
  2. 语义盲:任何两个 one-hot 向量的余弦相似度都是 0,"猫"和"狗"的距离等于"猫"和"微波炉"的距离

词嵌入的思路:改用低维稠密向量(如 1024 维,每一维都有值),并且让向量的位置关系反映语义关系。这就是"分布式表示"(distributed representation)——语义不存储在某一个数字里,而是散布在整个向量的模式中。

3.2 核心假设:几何即语义

词嵌入建立在一个语言学假设上(分布假说,1957 年 Firth:"看一个词的伙伴,就知道它的意思"):出现在相似上下文里的词,语义相近。"猫"和"狗"都常出现在"喂、撸、养、可爱"附近,所以它们的向量应该靠近。

理想状况下,嵌入空间有这样的性质:

几何关系 语义解释
两个向量夹角小 两个词语义相近
两个向量方向相反 语义对立(好 ↔ 坏)
向量差 $\vec{国王} - \vec{女王}$ 约等于"性别"这个抽象方向
一段文本向量的中心 这段文本的主题

著名的例子(词向量时代最经典的发现,在 Word2Vec 里首次观察到):

$$ \vec{国王} - \vec{男人} + \vec{女人} \approx \vec{女王} $$

$$ \vec{巴黎} - \vec{法国} + \vec{中国} \approx \vec{北京} $$

词与词之间可以做"语义算术"——这不是设计出来的,是模型从海量语料中自己学出来的。

3.3 Embedding 的工程本质:一张可查的表

实现上,词嵌入朴素得让人意外:一张 V × d 的大表(词表大小 × 嵌入维度)。第 $i$ 行就是 token $i$ 的向量。

import numpy as np

rng = np.random.default_rng(0)

V, d = 50000, 1024        # 假设词表 5 万,维度 1024
E = rng.normal(0, 0.02, (V, d))   # 这张表就是 embedding 层

token_id = 8721
x = np.zeros(V); x[token_id] = 1    # one-hot

# 方式一:矩阵乘 one-hot
v1 = E.T @ x
# 方式二:直接查表
v2 = E[token_id]

print(np.allclose(v1, v2))          # True —— 乘 one-hot ≡ 查行
print(v2.shape)                     # (1024,)

关键认知:

  • Embedding 层就是一个可训练的参数矩阵,形状 (V, d),是第 02 章"参数"概念的直接应用
  • "查表"和"乘 one-hot"数学上等价,工程上当然用查表(不用真的存 one-hot)
  • 训练开始时这张表是随机的(毫无语义),随着预训练推进,梯度不断更新它,语义结构自己浮现(emerge)——没有人为设计"性别维度",但组织语义的压力自然催生了它

对尺寸有感觉:Qwen 这类 8B 模型,词表约 150K、维度 4096,光嵌入表就有约 6 亿参数。

3.4 相似度:怎么算"近"

两个向量怎么比较?主流是余弦相似度——不看长度,只看方向夹角:

$$ \cos(\mathbf{a}, \mathbf{b}) = \frac{\mathbf{a} \cdot \mathbf{b}}{|\mathbf{a}| \, |\mathbf{b}|} $$

值域 [-1, 1]:1 表示方向相同,0 表示正交(无关),-1 表示相反。

import numpy as np

def cosine(a, b):
    return a @ b / (np.linalg.norm(a) * np.linalg.norm(b))

# 手工构造一个玩具语义空间(3 维足够演示)
#        性别维度  体型维度  人造物维度
king  = np.array([ 0.9,  0.8,  0.0])
queen = np.array([-0.8,  0.7,  0.1])
man   = np.array([ 0.95, 0.0,  0.0])
woman = np.array([-0.95, 0.0,  0.0])
microwave = np.array([0.0, -0.2, 0.99])

print(f"king vs queen        : {cosine(king, queen):+.3f}")
print(f"king vs microwave    : {cosine(king, microwave):+.3f}")
print(f"king-man+woman ≈ queen? {cosine(king - man + woman, queen):+.3f}")

输出:

king vs queen        : +0.266
king vs microwave    : -0.093
king-man+woman ≈ queen? +0.996

上面是我手工摆的玩具向量(真实空间是几千维、由训练学出来的),但它演示了三件事:语义近的向量方向接近(king/queen > king/微波炉);语义算术在方向上成立;比较的是方向而非长度(每个词向量的长度还和词频有关,不适合做语义比较)。

3.5 拿真实的模型验证一次

用 Ollama 起一个本地 embedding 模型(如 nomic-embed-text),实测语义相似度:

# 前置: ollama pull nomic-embed-text
import ollama

def embed(text: str) -> list[float]:
    r = ollama.embed(model="nomic-embed-text", input=text)
    return r.embeddings[0]   # ollama-python 0.4+ 返回 Pydantic 对象

import numpy as np
def cosine(a, b):
    a, b = np.array(a), np.array(b)
    return a @ b / (np.linalg.norm(a) * np.linalg.norm(b))

pairs = [
    ("一只猫在睡觉", "小猫正在打盹"),
    ("一只猫在睡觉", "今天股市大涨"),
    ("如何学好编程", "学习编程的方法"),
]
for a, b in pairs:
    print(f"cos = {cosine(embed(a), embed(b)):+.3f}  {a!r} vs {b!r}")

典型输出(数值随模型版本浮动,量级关系稳定):

cos = +0.74  '一只猫在睡觉' vs '小猫正在打盹'      ← 语义相近
cos = +0.11  '一只猫在睡觉' vs '今天股市大涨'      ← 语义无关
cos = +0.62  '如何学好编程' vs '学习编程的方法'    ← 主题相近

真实模型里,语义关系确实被编码进了几何空间——这就是"模型懂语言"的物理载体。这套能力也是 RAG(检索增强生成)的基石:把文档和问题都变成向量,用余弦相似度找出"最相关的段落"。第 15 章讲 Agent 记忆检索时会直接用到它。

3.6 在 Transformer 里的位置

把前文的 token 和本节的嵌入连起来,模型处理输入的第一步就完整了:

"你好世界"
   │  分词器(本章第 2 节)
   ▼
[1234, 5678, 9012]          token 编号
   │  查嵌入表(本节)
   ▼
[[...], [...], [...]]        形状 (3, d) 的向量序列
   │
   ▼
   送入注意力机制(第 03 章)

注意一个重要转变:进入注意力层之后,模型处理的不再是"一个个词",而是"一排互相影响的向量"。后续每一层都在持续地修改这些向量,让每个位置的信息融合上下文——这是第 03 章的主角注意力机制要解决的问题。

顺带回答一个常见疑问:上下文窗口(context window)——它规定了一次最多送入多少 token。所有历史对话、系统提示词、工具定义都共享这个额度。窗口满会发生什么、怎么管理,第 06 章会专门处理。

4. 本章小结

  • AI ⊃ ML ⊃ DL ⊃ LLM,Agent 开发主要和最内层打交道;机器学习的本质是从数据中确定一个函数的参数,而不是手写规则——模型 = 参数确定的函数
  • 三大范式:监督(人标答案)、自监督(数据自造答案,LLM 预训练的核心)、强化(环境给奖励,对齐的核心)
  • 生成式模型学习数据分布,因此获得了"创造"能力;预测下一个词逼出了通用能力
  • 模型只认识数字,分词器负责文字 → token 编号的翻译
  • BPE:高频组合粘成大块,罕见内容退化为小块,词表外永不失效;手写 MiniBPE 40 行跑通"训练 + 编码"全流程
  • Byte-level BPE 保证任意 UTF-8 可编码;中文相对费 token
  • token 粒度解释了模型数不清字母、算不准字数等经典缺陷;token 是计费单位、上下文计量单位、输出速度单位
  • one-hot 维度高且语义盲;词嵌入用低维稠密向量编码语义;分布假说:上下文相似的词语义相近,语义结构从训练中自然涌现
  • Embedding 层 = 一张 (V, d) 的可训练大表,查表等价于乘 one-hot;余弦相似度衡量"语义距离",是 RAG 和记忆检索的基石
  • king − man + woman ≈ queen:语义算术证明了"几何即语义";模型内部流动的从来不是文字,是向量序列

下一篇:02 · 前向传播与反向传播