☰
从sequence mask到Embedding:用TaoToken统一Key拆解Transformer里的Attention计算链路
2026/10/15 17:44:17 网站建设 项目流程

1. 从一条短句出发:sequence mask 和 Embedding 到底在算什么

如果你刚开始学 NLP,看到 Transformer 结构图的第一反应大概率是:这堆箭头和方块到底谁先谁后?尤其是 sequence mask 和 Embedding,一个负责把词变成向量,一个负责把某些位置遮住,它们俩在 Attention 里是怎么碰头的?我试过把一条短句拆开,逐行打印张量形状,才真正把这条链路串起来。

先明确三个核心检索词的含义。Embedding 是把离散 token 映射成连续向量的查表操作,比如词表大小 10000、嵌入维度 512,那么每个 token 就变成一个 512 维向量。sequence mask 是一个与序列长度相关的布尔矩阵,用来告诉 Attention 哪些位置可以看、哪些位置必须遮住。Attention 则是用 Query、Key、Value 三组向量计算加权和,让每个位置根据相关性聚合上下文信息。

适合谁看?适合已经知道 Attention 公式但没亲手跑过中间张量的人,适合分不清 padding mask 和 causal mask 的人,也适合想用统一 API 通道验证模型中间输出的人。下面我用一条短句["我", "爱", "NLP"]为例,配合 PyTorch 逐步打印形状,再把中间结果通过 TaoToken 的 API 通道发给模型做语义验证。整个过程不需要你本地有 GPU,CPU 就能跑通形状推演。

先给一张形状对照表,后面每一步都会回填这张表:

阶段张量内容形状(batch=1, seq=3, d_model=4)
token id词表索引[1, 3]
Embedding 输出词向量[1, 3, 4]
位置编码相加带位置信息的向量[1, 3, 4]
Q/K/V 投影三组向量[1, 3, 4] 各一份
Attention 分数QK^T / sqrt(d)[1, 3, 3]
mask 叠加被遮位置为 -inf[1, 3, 3]
softmax 后注意力权重[1, 3, 3]
加权输出聚合后的上下文[1, 3, 4]

这张表是整篇文章的骨架。很多人卡住不是因为公式不会,而是因为不知道每一步张量长什么样。把形状对齐了,sequence mask 和 Embedding 的联动关系就自然浮现:Embedding 决定每个位置“是什么”,mask 决定每个位置“能看谁”,Attention 决定“看完之后怎么融合”。

2. TaoToken 前置准备:统一 Key 与 API 通道怎么配

在开始写 PyTorch 脚本之前,先把调用通道准备好。我用 TaoToken 的统一 Key 来验证中间输出,原因是它把多家模型的接入方式统一成一套 Base URL 和 Key,省得每换一个模型就改一次配置。官网地址是 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API 入口是 https://taotoken.net/api ,注意 API 地址不带 UTM 参数。

你需要准备三样东西:Base URL、API Key、Model ID。这三件套在后面的配置片段里会反复出现。Base URL 填https://taotoken.net/api,API Key 在控制台创建,Model ID 根据你要验证的模型填写。控制台入口在这里:https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API Key 管理页在这里:https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 。

如果你用的是 Claude Code 这类编码工具,配置方式略有不同。Claude Code 的接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,Coding Plan 页面在 https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 。模型对话验证入口在 https://taotoken.net/models?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 。

下面是一个可复制的 JSON 配置片段,路径和字段名按实际使用保持一致:

{ "base_url": "https://taotoken.net/api", "api_key": "sk-your-key-here", "model_id": "your-model-id", "timeout": 60 }

如果你用 TOML 管理配置,可以写成这样:

[taotoken] base_url = "https://taotoken.net/api" api_key = "sk-your-key-here" model_id = "your-model-id" timeout = 60

如果你在 Cline 或类似工具里配置 MCP,settings 片段参考这个结构:

{ "mcpServers": { "taotoken": { "command": "npx", "args": ["-y", "your-mcp-package"], "env": { "BASE_URL": "https://taotoken.net/api", "API_KEY": "sk-your-key-here", "MODEL_ID": "your-model-id" } } } }

注意 Base URL、Key、Model ID 这三件套必须同时出现,缺一个就会在请求时报 401 或 model not found。我踩过的坑是只填了 Base URL 和 Key,忘了 Model ID,结果返回 reading choices 相关错误,后面排障章节会细说。

3. 可复制配置:Embedding、位置编码与 mask 的 PyTorch 实现

这一节是全文技术核心。我会把 Embedding、位置编码、sequence mask、Attention 打分串成一个可运行脚本,每一步都打印形状。你直接复制到本地 Python 文件就能跑,依赖只有 torch。

先看完整脚本,然后逐段解释:

import torch import torch.nn as nn import math torch.manual_seed(0) # 1. 构造词表和输入 vocab = {"我": 0, "爱": 1, "NLP": 2, "<pad>": 3} seq = ["我", "爱", "NLP"] token_ids = torch.tensor([[vocab[t] for t in seq]]) # [1, 3] print("token_ids shape:", token_ids.shape) # 2. Embedding 查表 d_model = 4 embedding = nn.Embedding(len(vocab), d_model) emb_out = embedding(token_ids) # [1, 3, 4] print("emb_out shape:", emb_out.shape) print("emb_out:\n", emb_out) # 3. 位置编码 def positional_encoding(seq_len, d_model): pe = torch.zeros(seq_len, d_model) position = torch.arange(0, seq_len).unsqueeze(1).float() div_term = torch.exp(torch.arange(0, d_model, 2).float() * (-math.log(10000.0) / d_model)) pe[:, 0::2] = torch.sin(position * div_term) pe[:, 1::2] = torch.cos(position * div_term) return pe.unsqueeze(0) # [1, seq_len, d_model] pos_enc = positional_encoding(token_ids.size(1), d_model) print("pos_enc shape:", pos_enc.shape) x = emb_out + pos_enc # [1, 3, 4] print("x after pos enc shape:", x.shape) # 4. 构造 padding mask # 假设第 3 个位置是 padding,需要遮住 pad_mask = torch.tensor([[1, 1, 0]]) # 1 表示有效,0 表示 padding print("pad_mask shape:", pad_mask.shape) # 5. 构造 causal mask(下三角) seq_len = token_ids.size(1) causal_mask = torch.tril(torch.ones(seq_len, seq_len)) print("causal_mask:\n", causal_mask) # 6. 合并 mask:padding mask 和 causal mask # padding mask 扩展成 [1, 1, seq_len],causal mask 是 [seq_len, seq_len] combined_mask = causal_mask.unsqueeze(0) * pad_mask.unsqueeze(1) # [1, 3, 3] print("combined_mask shape:", combined_mask.shape) print("combined_mask:\n", combined_mask) # 7. Q/K/V 投影 w_q = nn.Linear(d_model, d_model) w_k = nn.Linear(d_model, d_model) w_v = nn.Linear(d_model, d_model) q = w_q(x) # [1, 3, 4] k = w_k(x) v = w_v(x) print("q shape:", q.shape) # 8. Attention 打分 scores = torch.matmul(q, k.transpose(-2, -1)) / math.sqrt(d_model) # [1, 3, 3] print("scores shape:", scores.shape) print("scores before mask:\n", scores) # 9. 叠加 mask:被遮位置设为 -inf masked_scores = scores.masked_fill(combined_mask == 0, float("-inf")) print("scores after mask:\n", masked_scores) # 10. softmax 得到注意力权重 attn_weights = torch.softmax(masked_scores, dim=-1) print("attn_weights shape:", attn_weights.shape) print("attn_weights:\n", attn_weights) # 11. 加权求和 output = torch.matmul(attn_weights, v) # [1, 3, 4] print("output shape:", output.shape) print("output:\n", output)

这段脚本的关键在于第 6 步的 mask 合并。padding mask 的形状是[1, 3],表示哪些位置是真实 token、哪些是补齐的。causal mask 是下三角矩阵,表示每个位置只能看自己和左边。两者相乘后得到[1, 3, 3]的 combined_mask,再通过masked_fill把 0 的位置填成负无穷,softmax 之后这些位置的权重就变成 0。

你运行后会看到类似输出:

token_ids shape: torch.Size([1, 3]) emb_out shape: torch.Size([1, 3, 4]) pos_enc shape: torch.Size([1, 3, 4]) x after pos enc shape: torch.Size([1, 3, 4]) combined_mask shape: torch.Size([1, 3, 3]) scores shape: torch.Size([1, 3, 3]) attn_weights shape: torch.Size([1, 3, 3]) output shape: torch.Size([1, 3, 4])

这里有个容易混淆的点:padding mask 和 causal mask 的作用不同。padding mask 处理变长序列,把补齐位置遮掉;causal mask 防止未来信息泄露,让位置 i 只能看小于等于 i 的位置。两者可以叠加使用,叠加方式就是逐元素相乘。

如果你想把中间结果发给模型做语义验证,可以用下面的请求脚本:

import requests import json url = "https://taotoken.net/api/v1/chat/completions" headers = { "Authorization": "Bearer sk-your-key-here", "Content-Type": "application/json" } payload = { "model": "your-model-id", "messages": [ {"role": "user", "content": "请解释 sequence mask 在 Attention 中的作用,用一句话概括。"} ] } resp = requests.post(url, headers=headers, json=payload, timeout=60) print(resp.status_code) print(resp.json())

把sk-your-key-here和your-model-id换成你自己的,就能验证模型对 mask 概念的理解是否和你的张量推演一致。

4. 验证请求与成功结果:从形状到语义的双重确认

跑完上面的脚本,你已经拿到了每一步的张量形状。但形状对不代表语义对,所以还需要做两件事:一是检查 mask 后的注意力权重是否符合预期,二是通过 API 通道让模型复述概念,确认你的理解没有跑偏。

先看 mask 后的权重。在 combined_mask 中,第 3 个位置是 padding,所以第 3 行和第 3 列都应该被遮住。运行脚本后你会看到 attn_weights 的第 3 行全是 0,因为 softmax 之前被填了负无穷。同时由于 causal mask 的存在,第 1 行只有第 1 列有值,第 2 行只有第 1、2 列有值。这就是 sequence mask 和 Embedding 联动的直观体现:Embedding 给每个位置一个向量,mask 决定这个向量能参与哪些位置的加权。

成功结果应该满足三个条件。第一,attn_weights 每一行的和等于 1,这是 softmax 的性质。第二,被 mask 的位置权重为 0。第三,output 的形状和输入 x 一致,都是[1, 3, 4],因为 Attention 不改变序列长度和维度。

如果你用 API 验证,成功返回的 JSON 里会有choices字段,里面包含模型回复。你可以把回复和你自己的理解对照,比如模型说“mask 用于屏蔽 padding 和未来位置”,那就和你的推演一致。如果返回 401,说明 Key 有问题;如果返回 model not found,说明 Model ID 填错了;如果返回 reading choices 相关错误,说明响应结构和你解析的字段不匹配。

这里给一个完整的验证流程。第一步,本地跑 PyTorch 脚本,确认形状和权重。第二步,用 curl 发一个最小请求:

curl -X POST https://taotoken.net/api/v1/chat/completions \ -H "Authorization: Bearer sk-your-key-here" \ -H "Content-Type: application/json" \ -d '{"model":"your-model-id","messages":[{"role":"user","content":"sequence mask 的两个作用是什么?"}]}'

第三步,对比返回内容和你的理解。如果返回正常,说明通道打通;如果报错,进入下一节排障。

我实测下来,最容易出问题的不是 PyTorch 脚本,而是 API 配置。因为 PyTorch 脚本是纯本地的,形状错了会直接报错;而 API 配置错了,报错信息往往不够直观。所以建议先把本地脚本跑通,再配 API。

5. 本篇常见错排查:401、local proxy failed、reading choices、OAuth

这一节列出四个真实报错和对应解法。每个报错都给出触发条件和修复步骤,你对照自己的情况排查。

第一个是 401 Unauthorized。触发条件通常是 API Key 没填、填错、或者过期。修复步骤:检查Authorization头是否写成Bearer sk-xxx,注意 Bearer 和 Key 之间有一个空格。然后去 API Key 管理页 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 确认 Key 是否有效。如果 Key 刚创建,等几秒再试,有时候有同步延迟。

第二个是 local proxy failed。这个报错通常出现在你本地设置了网络代理,但代理没有正常工作时。修复步骤:检查环境变量HTTP_PROXY和HTTPS_PROXY是否指向了一个不可用的地址。如果你不需要代理,直接清空这两个变量。如果你在容器里跑,检查容器网络是否能访问外网。注意不要使用任何违规的网络工具,保持直连即可。

第三个是 reading choices 相关错误。这个报错说明请求发出去了,但解析响应时找不到choices字段。常见原因是 Model ID 填错,导致返回了错误结构;或者请求体格式不对,比如messages写成了字符串而不是数组。修复步骤:先用 curl 发一个最小请求,打印完整响应,确认返回结构里有choices。然后检查你的解析代码是否用了正确的字段路径。

第四个是 OAuth 相关错误。如果你用 Claude Code 或类似工具,可能会遇到 OAuth 认证失败。修复步骤:检查你的配置文件里是否同时填了 Base URL、Key、Model ID 三件套。Claude Code 的接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,对照文档检查字段名。如果用的是 Codex 的 auth.json,确认base_url和api_key字段拼写正确。

下面给一个排障对照表:

报错触发条件修复步骤
401Key 缺失或错误检查 Bearer 格式,确认 Key 有效
local proxy failed代理配置不可用清空代理环境变量,保持直连
reading choicesModel ID 错误或解析路径错误用 curl 打印完整响应,核对字段
OAuth三件套缺失或字段名错误对照文档检查 Base URL、Key、Model ID

排障的核心思路是:先确认请求发出去了没有,再确认响应结构对不对,最后确认解析代码有没有问题。三步走下来,大部分错误都能定位。

6. 把点连成图:用统一通道继续验证你的 Transformer 理解

到这里,你已经完成了从 token id 到 Embedding、位置编码、mask、Attention 打分的完整链路。回头看那张形状对照表,每一步都有对应的代码和打印结果。sequence mask 和 Embedding 的关系不再是抽象概念,而是[1, 3, 4]和[1, 3, 3]之间的具体运算。

如果你想继续深入,可以尝试改几个参数。把d_model从 4 改成 8,观察形状变化;把pad_mask改成[1, 1, 1],观察 padding 消失后权重如何变化;把 causal mask 去掉,观察未来信息如何泄露。每次改动都重新跑一遍脚本,打印中间张量,你的理解会越来越扎实。

验证模型语义的通道可以继续用 TaoToken 的统一 Key。模型对话入口在 https://taotoken.net/models?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API Key 管理在 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 。如果你要长期做编码或 Agent 相关实验,可以看看 Coding Plan:https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 。

最后留一个实用技巧:把上面的 PyTorch 脚本保存成attention_shapes.py,每次学新概念时改一行、跑一次、看一次形状。形状对了,概念就对了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询