
文章目录Embedding先把维度写清楚用一个很小例子看那 embedding 到底指什么工程里真正怎么做的一句话记疑问解答d 是什么Transformer 里的 embedding 是怎么转换的词嵌入矩阵是本来就有的吗再把关系捋一遍词嵌入的维度作用它到底影响什么常见维度大概怎么选一句话理解为什么还会发生降维1. 从 one-hot 到 embedding这是从 V 维降到 d 维2. 多头注意力里的 d_k这是把 d_model 拆成多个小头3. FFN 里的“先升维再降维”这是为了增强表达能力4. 检索/部署时的降维这是为了省存储、加速一句话总结为什么会看到二维图像先说真实情况经常看到的二维图是什么所以“关系”是怎么来的最后举例第 1 步分词变成 token第 2 步token 变成 ID第 3 步用 ID 查嵌入矩阵第 4 步拼成矩阵第 5 步加上位置编码一句话总结位置编码先定一个简单设定位置编号位置编码怎么加位置 0“我”位置 1“是”位置 5“狗”最终输入为什么要加位置编码一句话总结eg我爱你input经过embedding词向量转换将token转为词向量加上位置编码经过encoedr多头注意力机制resnet深度网络{残差}加标准化、归一化在经过前馈神经网络也就是全连接再次resnet深度网络加标准化。outputs shifted right先输入开始标识如s加上其位置编码进入decoder(掩码多头注意力机制、resnet深度网络加标准化、归一化加上编码器出来的矩阵还是多头注意力机制再次resnet深度网络加标准化、归一化,前馈归一全连接softmax进行概率统计将输出的I放入标识s后作为outputs shifted right下一个标识)。接着循环往复input、outputs shifted right、直到outputs shifted right的标识为结束符号如e传递至概率结束。也就是说我们输入是全部给它但它是一点一点反馈给我们。Embedding为什么要引入词嵌入技术Embedding 层其实就是一个“查表操作”只是它可以用矩阵乘法来等价表示。先把维度写清楚假设词表大小是V词向量维度是d词嵌入矩阵形状是V × done-hot 向量形状是V × 1词向量形状是d × 1数学上写成词向量 词嵌入矩阵^T × one-hot向量也就是(d × V)×(V ×1)(d ×1)有些资料会写成one-hot向量^T × 词嵌入矩阵词向量^T也就是(1× V)×(V × d)(1× d)这两种写法本质一样只是行向量 / 列向量的习惯不同。嵌入矩阵 * one-hot 把嵌入矩阵里对应那一列挑出来。当然也可以反过来获取某一行one-hot 向量就像一个开关只打开词嵌入矩阵里的某一列矩阵乘法只是把这个“查表动作”写成了线性代数形式。用一个很小例子看假设词表只有 5 个词向量维度是 3词表0:我1:爱2:学习3:深度4:模型嵌入矩阵长这样第0行[0.1,0.2,0.3]# “我”的向量第1行[0.4,0.5,0.6]# “爱”的向量第2行[0.7,0.8,0.9]# “学习”的向量第3行[1.0,1.1,1.2]# “深度”的向量第4行[1.3,1.4,1.5]# “模型”的向量“学习”的 one-hot 是[0,0,1,0,0]拿它去乘嵌入矩阵0× 第0行0× 第1行1× 第2行0× 第3行0× 第4行第2行[0.7,0.8,0.9]所以one-hot 乘嵌入矩阵 取出嵌入矩阵里对应那一行。那 embedding 到底指什么说法实际意思词嵌入矩阵一张表形状是词表大小 × 向量维度每一行存一个词的向量embedding 层负责“根据词 id 查表”的那一层embedding 向量查表查出来的那个向量one-hot × 嵌入矩阵数学上等价于查表但工程上不会真的构造 one-hot 去乘所以更准确地说词嵌入矩阵 embedding 层里的参数矩阵embedding 操作 用词 id 去这张矩阵里查一行one-hot 乘法 只是为了把“查表”写成矩阵形式方便理论推导工程里真正怎么做的实际不会构造一个几万维、里面几乎全是 0 的 one-hot 向量那样太浪费。PyTorch 里是这样embeddingnn.Embedding(num_embeddings10000,embedding_dim128)token_idstorch.tensor([2,5,99])vectorsembedding(token_ids)# 直接查表取出第2、5、99行这里输入的是token id不是one-hot。一句话记嵌入矩阵是一本“密码本”one-hot 是“页码”embedding 就是“翻到那一页”。所谓“词向量 嵌入矩阵 × one-hot”本质上就是用 one-hot 告诉模型去嵌入矩阵里取第几行。疑问解答d 是什么d 是向量维度不是 token 数量。比如 Transformer 原始论文里d_model512意思是每个 token 会被表示成一个512 维向量。所以你输入3 个 token输出不是 3 维而是3 个 512 维向量张量形状是[batch_size,seq_len,d_model]比如 batch1句子有 4 个 tokend_model512输入 token ids[101,7592,2054,102]输出形状[1,4,512]每个 token 对应一个 512 维向量。Transformer 里的 embedding 是怎么转换的流程是这样的原始文本 ↓ Tokenizer 分词 token 序列比如[101,7592,2054,102]↓ 查嵌入矩阵 每个 token 变成 d_model 维向量 ↓ 加上位置编码 得到最终输入表示嵌入矩阵的形状是[词表大小 V,嵌入维度 d_model]比如词表有 50000 个 tokend_model768嵌入矩阵形状[50000,768]每个 token 有一个 ID比如 ID2054就去嵌入矩阵里取第 2054 行得到一个 768 维向量。工程上不会真的构造 one-hot 去乘而是直接按索引查表embeddingnn.Embedding(vocab_size50000,embedding_dim768)token_idstorch.tensor([101,7592,2054,102])vectorsembedding(token_ids)# 形状 [4, 768]词嵌入矩阵是本来就有的吗要看说的是哪种情况情况嵌入矩阵状态说明从零训练 Transformer不是固定保留的一开始随机初始化训练时通过反向传播更新加载预训练模型是已经训练好的比如 BERT、GPT 的权重里已经包含了嵌入矩阵微调预训练模型通常会继续更新除非你特意把 embedding 层冻结所以更准确地说词嵌入矩阵不是预先写死的“词典”而是一个可学习的参数矩阵。预训练模型里它确实已经“保留”了训练好的值但训练过程中它本身也是会被更新的。再把关系捋一遍词表大小 V → 决定嵌入矩阵有多少行 嵌入维度 d_model → 决定每个 token 向量有多长 token 数量 → 决定一次输入有多少个向量比如V50000d_model768输入句子有6个 token 嵌入矩阵形状[50000,768]输出形状[6,768]d ≠ token 个数d 每个 token 的向量维度token 个数 序列长度 seq_len嵌入矩阵 一本“可学习的密码本”每一行对应一个 token词嵌入的维度作用本质上是每个 token 的“特征容量”维度越大它能承载的语义细节越多维度越小表示越紧凑但可能丢失信息。它到底影响什么1. 决定每个 token 能表达多少信息每个维度可以理解为模型学到的一个语义特征。比如“猫”这个词低维可能只表达“动物”高维可以进一步区分“宠物”“会叫”“有毛”“小型”等更细的语义。2. 决定整个 Transformer 内部的数据流动宽度在标准 Transformer 里d_model通常等于嵌入维度。也就是说词嵌入、位置编码、自注意力里的 Q/K/V、前馈网络的输入输出全都保持这个维度。所以它不只是“输入层”的参数而是贯穿整个模型的基础宽度。3. 影响计算量、显存和速度维度越高注意力计算更重嵌入矩阵参数更多向量相似度计算更慢存储占用线性增加。维度太低语义表达不够容易把不同含义的词挤在一起模型容量不足可能欠拟合。常见维度大概怎么选维度范围典型场景50–300Word2Vec、GloVe 等传统词向量384轻量检索、本地部署、边缘设备768BERT-base、通用 NLP 任务1024–1536高精度检索、复杂语义匹配3072工业级高精度场景成本较高一句话理解嵌入维度 每个 token 的“名片大小”。名片太小写不下足够信息名片太大携带和处理都变贵。768 之所以常见是因为它在表达能力和计算成本之间比较平衡。为什么还会发生降维其实“降维”在 Transformer 里会出现在好几个地方含义不太一样。1. 从 one-hot 到 embedding这是从 V 维降到 d 维one-hot 向量维度是V等于词表大小。比如词表有 30000 个 tokenone-hot 就是 30000 维而且几乎全是 0。embedding 把它变成 768 维、512 维这样的稠密向量。这个过程就是降维one-hot:[0,0,0,1,0,...,0]# V 维极稀疏embedding:[0.12,-0.34,0.56,...]# d 维稠密为什么要降因为one-hot 太稀疏计算和存储都很浪费one-hot 里词与词之间没有语义关系“猫”和“狗”的距离跟“猫”和“桌子”一样远embedding 把语义压缩到一个低维稠密空间相似词会靠得更近。所以词嵌入本身就是把 one-hot 的高维稀疏表示压缩成低维稠密表示。2. 多头注意力里的 d_k这是把 d_model 拆成多个小头假设d_model 512有 8 个注意力头d_k d_model / h 512 / 8 64每个头只处理 64 维。这也不是“信息变少了”而是把 512 维拆成 8 个 64 维子空间让不同头关注不同关系比如语法、指代、语义角色等。最后再拼回去仍然回到 512 维。3. FFN 里的“先升维再降维”这是为了增强表达能力Transformer 的 FFN 通常是d_model → d_ff → d_model比如512 → 2048 → 512中间先升维是为了让模型有更大空间去表达复杂特征后面再降回d_model是为了和残差连接对齐保证维度一致。所以这里降维不是“压缩信息”而是把高维特征重新压回主干维度。4. 检索/部署时的降维这是为了省存储、加速比如把 768 维 embedding 用 PCA、量化、Matryoshka 表示学习压缩到 128 维或 256 维。这种降维主要是工程考虑向量检索更快存储更省端侧部署更容易。但会损失一部分精度所以需要权衡。一句话总结one-hot → embedding从词表维度 V 降到 d是为了解决稀疏和语义缺失。d_model → d_k是多头注意力的拆分不是真正丢失维度。FFN 里 d_ff → d_model是把高维中间表示压回主干维度。检索部署降维是为了省存储、加速但可能牺牲精度。为什么会看到二维图像先说真实情况假设词表有 50000 个词嵌入维度是 768嵌入矩阵形状[50000, 768]每个 token 查表后得到的是一个768 维向量不是 2 维。模型内部也是用这 768 维继续算注意力、前馈网络、分类头等等。它不会在 Transformer 里一直用二维。经常看到的二维图是什么那是把高维词向量投影到 2 维后画出来的散点图。常用方法有PCA线性降维保留主要变化方向t-SNE非线性降维更强调“谁和谁挨得近”UMAP也常用于保留局部结构。比如原来每个词是 768 维猫:[0.12,-0.34,0.56,...,0.89]# 768 维狗:[0.10,-0.31,0.58,...,0.91]# 768 维汽车:[0.78,0.02,-0.65,...,-0.12]# 768 维经过 t-SNE 或 PCA 后变成猫 →(1.2,3.4)狗 →(1.3,3.5)汽车 →(-2.1,0.7)然后就能画成二维图“猫”和“狗”离得近“汽车”离它们远同类词会聚成簇。所以“关系”是怎么来的关系不是降维降出来的而是训练出来的。模型在训练时看到猫 经常出现在 宠物、毛、喵、粮 附近 狗 经常出现在 宠物、毛、汪、粮 附近 汽车 经常出现在 车、驾驶、油、路 附近于是“猫”和“狗”的向量会慢慢变得相似“汽车”就和它们差别更大。二维图只是把这个高维空间里的“远近关系”压扁给我们看。最后举例用这两句来走一遍最清楚句子1我爱吃苹果 句子2我用的是苹果第 1 步分词变成 token模型不能直接理解汉字它先把句子切成最小单元 token。句子1我/爱/吃/苹果 句子2我/用/的/是/苹果第 2 步token 变成 ID每个 token 在词表里都有一个编号。假设词表里是这样我 →100爱 →201吃 →305苹果 →409用 →512的 →618是 →720于是句子1[100,201,305,409]句子2[100,512,618,720,409]第 3 步用 ID 查嵌入矩阵嵌入矩阵是一张表形状是[词表大小, 嵌入维度]假设嵌入维度是 4嵌入矩阵长这样第100行我 →[0.1,0.5,-0.3,0.8]第201行爱 →[0.4,-0.6,0.9,0.2]第305行吃 →[0.7,0.3,0.5,-0.4]第409行苹果 →[-0.2,0.8,0.1,0.6]第512行用 →[0.3,0.1,0.6,0.2]第618行的 →[-0.1,0.4,0.2,0.5]第720行是 →[0.6,-0.2,0.4,0.1]注意同一个 token 查出来的是同一行。所以两句话里的“苹果”在 embedding 这一步拿到的是同一个向量苹果 →[-0.2,0.8,0.1,0.6]第 4 步拼成矩阵句子1embedding 后[[0.1,0.5,-0.3,0.8],# 我[0.4,-0.6,0.9,0.2],# 爱[0.7,0.3,0.5,-0.4],# 吃[-0.2,0.8,0.1,0.6]# 苹果]形状[4,4]也就是4个 token每个4维句子2embedding 后[[0.1,0.5,-0.3,0.8],# 我[0.3,0.1,0.6,0.2],# 用[-0.1,0.4,0.2,0.5],# 的[0.6,-0.2,0.4,0.1],# 是[-0.2,0.8,0.1,0.6]# 苹果]形状[5,4]也就是5个 token每个4维第 5 步加上位置编码Transformer 只看 embedding 的话不知道“苹果”在第几个位置所以要加位置编码。最终输入 词嵌入 位置编码这样句子1里的苹果位置3的苹果向量 句子2里的苹果位置4的苹果向量它们的基础词嵌入相同但位置不同后面再经过注意力层模型就会根据上下文把两个“苹果”区分开一个是水果一个是手机品牌。一句话总结embedding 过程就是句子 → 切 token → 转 ID → 查嵌入矩阵 → 得到每个 token 的向量 → 加位置编码。“我爱吃苹果”有 4 个 token就得到 4 个向量“我用的是苹果”有 5 个 token就得到 5 个向量。嵌入维度 d 是每个向量的长度不是 token 个数。位置编码分为奇数与偶数先定一个简单设定假设嵌入维度是4 维d4这样数字好算和上述图不一样上述为3维也就是d3.先假设每个词查完嵌入矩阵后得到我 →[0.1,0.5,-0.3,0.8]是 →[0.4,-0.6,0.9,0.2]一 →[0.7,0.3,0.5,-0.4]条 →[-0.2,0.8,0.1,0.6]小 →[0.3,0.1,0.6,0.2]狗 →[-0.1,0.4,0.2,0.5]如果不加位置编码模型只知道这句话里有这几个词但不知道“我”在前面“狗”在后面。位置编号位置0我 位置1是 位置2一 位置3条 位置4小 位置5狗位置编码怎么加原始 Transformer 用的是正弦和余弦函数原理如上图PE(pos,2i)sin(pos/10000^(2i/d))PE(pos,2i1)cos(pos/10000^(2i/d))这里 d4所以第 0、2 维用 sin第 1、3 维用 cos我们算几个位置。位置 0“我”PE(0,0)sin(0/10000^0)sin(0)0PE(0,1)cos(0/10000^0)cos(0)1PE(0,2)sin(0/10000^(2/4))sin(0)0PE(0,3)cos(0/10000^(2/4))cos(0)1位置编码[0, 1, 0, 1]加上词嵌入我[0.1,0.5,-0.3,0.8][0,1,0,1][0.1,1.5,-0.3,1.8]位置 1“是”PE(1,0)sin(1)≈0.84PE(1,1)cos(1)≈0.54PE(1,2)sin(1/100)≈0.01PE(1,3)cos(1/100)≈1.00位置编码[0.84,0.54,0.01,1.00]加上词嵌入是[0.4,-0.6,0.9,0.2][0.84,0.54,0.01,1.00][1.24,-0.06,0.91,1.20]位置 5“狗”PE(5,0)sin(5)≈-0.96PE(5,1)cos(5)≈0.28PE(5,2)sin(5/100)≈0.05PE(5,3)cos(5/100)≈1.00位置编码[-0.96,0.28,0.05,1.00]加上词嵌入狗[-0.1,0.4,0.2,0.5][-0.96,0.28,0.05,1.00][-1.06,0.68,0.25,1.50]最终输入我[0.1,1.50,-0.30,1.80]是[1.24,-0.06,0.91,1.20]一词嵌入位置2编码 条词嵌入位置3编码 小词嵌入位置4编码 狗[-1.06,0.68,0.25,1.50]为什么要加位置编码因为 Transformer 的自注意力是并行处理所有 token的它本身不知道顺序。如果不加位置编码我是一条小狗 小狗一条是我模型看到的词集合一样顺序信息就丢了。加了位置编码后位置0的“我” ≠ 位置5的“我” 位置5的“狗” ≠ 位置0的“狗”这样模型才能区分“我”是主语在句首“狗”是宾语中心词在句尾“小狗”是一个整体而不是“小”和“狗”随便拼在一起。一句话总结词嵌入告诉模型“这个词是什么”位置编码告诉模型“这个词在第几个位置”。两者相加后模型才知道“我是一条小狗”的顺序和结构。补充知识PCA主成分分析https://blog.csdn.net/qq_54700138/article/details/146046975