从sequence mask到Embedding:用TaoToken统一Key拆解Transformer里的Attention计算链路

📅 发布时间:2026/10/15 17:44:19
从sequence mask到Embedding:用TaoToken统一Key拆解Transformer里的Attention计算链路
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 通道发给模型做语义验证。整个过程不需要你本地有 GPUCPU 就能跑通形状推演。先给一张形状对照表后面每一步都会回填这张表阶段张量内容形状batch1, seq3, d_model4token 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_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 入口是 https://taotoken.net/api 注意 API 地址不带 UTM 参数。你需要准备三样东西Base URL、API Key、Model ID。这三件套在后面的配置片段里会反复出现。Base URL 填https://taotoken.net/apiAPI Key 在控制台创建Model ID 根据你要验证的模型填写。控制台入口在这里https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API Key 管理页在这里https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。如果你用的是 Claude Code 这类编码工具配置方式略有不同。Claude Code 的接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content Coding Plan 页面在 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。模型对话验证入口在 https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_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 或类似工具里配置 MCPsettings 片段参考这个结构{ 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. 合并 maskpadding 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, headersheaders, jsonpayload, timeout60) 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_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_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_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_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_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API Key 管理在 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。如果你要长期做编码或 Agent 相关实验可以看看 Coding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。最后留一个实用技巧把上面的 PyTorch 脚本保存成attention_shapes.py每次学新概念时改一行、跑一次、看一次形状。形状对了概念就对了。