【译】为什么BERT有3个嵌入层,它们都是如何实现的

本文深入探讨了BERT模型中三个嵌入层的实现细节:TokenEmbeddings、SegmentEmbeddings和PositionEmbeddings,揭示了它们如何共同作用以增强模型对文本的理解能力。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文翻译自Why BERT has 3 Embedding Layers and Their Implementation Details

引言

​ 本文将阐述BERT中嵌入层的实现细节,包括token embeddings、segment embeddings, 和position embeddings.

概览

​ 下面这幅来自原论文的图清晰地展示了BERT中每一个嵌入层的作用:

1135245-20190227235341575-1614948318.png

​ 和大多数NLP深度学习模型一样,BERT将输入文本中的每一个词(token)送入token embedding层从而将每一个词转换成向量形式。但不同于其他模型的是,BERT又多了两个嵌入层,即segment embeddings和 position embeddings。在阅读完本文之后,你就会明白为何要多加这两个嵌入层了。

Token Embeddings

作用

​ 正如前面提到的,token embedding 层是要将各个词转换成固定维度的向量。在BERT中,每个词会被转换成768维的向量表示。

实现

​ 假设输入文本是 “I like strawberries”。下面这个图展示了 Token Embeddings 层的实现过程:

1135245-20190227235724883-2118042882.png

​ 输入文本在送入token embeddings 层之前要先进行tokenization处理。此外,两个特殊的token会被插入到tokenization的结果的开头 ([CLS])和结尾 ([SEP]) 。它们视为后面的分类任务和划分句子对服务的。

​ tokenization使用的方法是WordPiece tokenization. 这是一个数据驱动式的tokenization方法,旨在权衡词典大小和oov词的个数。这种方法把例子中的“strawberries”切分成了“straw” 和“berries”。这种方法的详细内容不在本文的范围内。有兴趣的读者可以参阅 Wu et al. (2016)Schuster & Nakajima (2012)。使用WordPiece tokenization让BERT在处理英文文本的时候仅需要存储30,522 个词,而且很少遇到oov的词。

​ Token Embeddings 层会将每一个wordpiece token转换成768维的向量。这样,例子中的6个token就被转换成了一个(6, 768) 的矩阵或者是(1, 6, 768)的张量(如果考虑batch_size的话)。

Segment Embeddings

作用

​ BERT 能够处理对输入句子对的分类任务。这类任务就像判断两个文本是否是语义相似的。句子对中的两个句子被简单的拼接在一起后送入到模型中。那BERT如何去区分一个句子对中的两个句子呢?答案就是segment embeddings.

实现

​ 假设有这样一对句子 (“I like cats”, “I like dogs”)。下面的图成仙了segment embeddings如何帮助BERT区分两个句子:

1135245-20190227235737504-479706108.png

​ Segment Embeddings 层只有两种向量表示。前一个向量是把0赋给第一个句子中的各个token, 后一个向量是把1赋给第二个句子中的各个token。如果输入仅仅只有一个句子,那么它的segment embedding就是全0。

Position Embeddings

作用

​ BERT包含这一串Transformers (Vaswani et al. 2017),而且一般认为,Transformers无法编码输入的序列的顺序性。 博客更加详细的解释了这一问题。总的来说,加入position embeddings会让BERT理解下面下面这种情况:

I think, therefore I am

第一个 “I” 和第二个 “I”应该有着不同的向量表示。

实现

​ BERT能够处理最长512个token的输入序列。论文作者通过让BERT在各个位置上学习一个向量表示来讲序列顺序的信息编码进来。这意味着Position Embeddings layer 实际上就是一个大小为 (512, 768) 的lookup表,表的第一行是代表第一个序列的第一个位置,第二行代表序列的第二个位置,以此类推。因此,如果有这样两个句子“Hello world” 和“Hi there”, “Hello” 和“Hi”会由完全相同的position embeddings,因为他们都是句子的第一个词。同理,“world” 和“there”也会有相同的position embedding。

合成表示

​ 我们已经介绍了长度为n的输入序列将获得的三种不同的向量表示,分别是:

  • Token Embeddings, (1, n, 768) ,词的向量表示
  • Segment Embeddings, (1, n, 768),辅助BERT区别句子对中的两个句子的向量表示
  • Position Embeddings ,(1, n, 768) ,让BERT学习到输入的顺序属性

​ 这些表示会被按元素相加,得到一个大小为(1, n, 768)的合成表示。这一表示就是BERT编码层的输入了。

结论

​ 在本文中,笔者介绍了BERT几个嵌入层的作用以及实现。

参考文献

转载于:https://www.cnblogs.com/d0main/p/10447853.html

<think>好的,我现在需要帮助用户理清BERT中的词嵌入、位置嵌入和段嵌入的区别和实现方式。用户之前已经询问过位置嵌入的具体实现,现在他们表示对这些嵌入的实现有些混淆,所以我需要详细但清晰地解释这三个部分。 首先,用户可能对这三者的作用不太清楚,或者对它们如何结合在一起感到困惑。我需要先分别解释每个嵌入的概念和目的,然后再说明它们是如何在BERT中被整合的。 词嵌入(Token Embedding)应该是用户比较熟悉的部分,类似于其他NLP模型中的词向量,将每个词转换为向量。但需要指出BERT使用了WordPiece分词,这可能与传统的词嵌入不同,需要简单解释一下WordPiece,比如如何处理未登录词和子词拆分。 位置嵌入(Positional Embedding)之前已经介绍过,这里需要再次简要回顾,强调其作用是为模型提供序列中每个词的位置信息,并且是可学习的参数,与Transformer的固定编码不同。可能需要对比一下两者的区别,帮助用户巩固记忆。 段嵌入(Segment Embedding)可能是用户最不熟悉的部分,需要详细说明。段嵌入用于处理句子对任务,例如问答或句子关系判断,区分两个不同的句子。要解释在输入中如何标记不同的段落,以及段嵌入如何对应这些标记生成不同的向量。 接下来,需要说明这三个嵌入是如何结合在一起输入到模型中的,即相加的方式。可能需要用数学公式或代码示例来直观展示这个过程,比如E = E_token + E_position + E_segment。同时,需要注意在实际实现中,比如使用PyTorch时,如何通过嵌入层来分别处理这三个部分,并将它们相加。 用户可能有疑问,比如为什么选择相加而不是拼接,或者这些嵌入的维度如何保持一致。需要提前预见到这些问题,并在解释中涵盖这些点,比如说明相加操作需要各嵌入维度相同,而BERT的设计中确实保持了相同的隐藏层维度。 另外,用户可能对输入的处理流程不太清楚,比如输入是如何被预处理成token IDs、position IDs和segment IDs的。这部分可以结合一个具体的例子,比如句子对的处理,来说明每个步骤。例如,使用[CLS]和[SEP]标记来分隔句子,并为每个token分配segment ID(0或1)。 可能需要提到实际代码中的实现方式,比如在Hugging Face的Transformers库中,BERT模型是如何处理这三个嵌入的,这会让用户更容易理解。例如,展示BERTModel类中如何调用token_embeddings、position_embeddingssegment_embeddings,并将它们相加。 还要注意指出一些细节,比如最大序列长度的限制(通常是512),以及当输入超过这个长度时的处理方式(截断或分块)。同时,段嵌入在单句任务中是否仍然需要的问题,可能需要说明即使只有一个句子,段嵌入仍然存在,通常全部设置为0。 最后,通过一个完整的例子来总结整个处理流程,帮助用户将理论应用到实际情境中。例如,输入两个句子,经过分词、添加特殊标记、生成三个嵌入并相加,然后输入到Transformer层中。这有助于用户形成整体的理解。 需要确保语言简洁,避免过于技术化的术语,但又要准确。使用类比或示意图可能有助于理解,比如将三个嵌入比作不同的信息层,叠加后形成完整的输入表示。同时,检查是否有之前的解释需要呼应,以保持连贯性。 总结思考步骤:先分解三个嵌入的概念和作用,分别解释,再说明它们的结合方式,结合实例和代码片段,最后总结整体流程,确保用户能够区分这三者并理解它们的实现方式。</think>理解BERT的三种嵌入机制需要明确各自的功能和实现方式。以下是结构化解析: --- ### **1. 词嵌入(Token Embedding)** #### 功能 - **语义编码**:将离散的单词/子词映射为连续向量(如 "apple" → [0.2, -0.5, ..., 0.7]) - **处理未登录词**:通过WordPiece分词拆分为子词(如 "unhappiness" → ["un", "##happiness"]) #### 实现 ```python # PyTorch示例 token_embeddings = nn.Embedding(vocab_size, hidden_size) input_ids = [101, 2456, 2038, 102] # [CLS], 单词1, 单词2, [SEP] 的ID E_token = token_embeddings(torch.tensor(input_ids)) # 形状: [seq_len, hidden_size] ``` --- ### **2. 位置嵌入(Positional Embedding)** #### 功能 - **位置感知**:编码词语在序列中的绝对位置(如第1个词 vs 第5个词) - **替代RNN/CNN**:使Transformer无需递归或卷积即可感知顺序 #### 实现 ```python position_embeddings = nn.Embedding(max_seq_length, hidden_size) position_ids = torch.arange(seq_length) # 生成 [0,1,2,...,seq_len-1] E_position = position_embeddings(position_ids) # 形状: [seq_len, hidden_size] ``` --- ### **3. 段嵌入(Segment Embedding)** #### 功能 - **句子区分**:标记输入中的不同文本片段(如问答任务中的问题和答案) - **单句/双句任务**:单句时全为0,双句时第一句标记0,第二句标记1 #### 实现 ```python segment_embeddings = nn.Embedding(num_segment_types, hidden_size) segment_ids = [0,0,0,1,1,1] # 区分两个句子 E_segment = segment_embeddings(torch.tensor(segment_ids)) # 形状: [seq_len, hidden_size] ``` --- ### **三者的融合方式** #### 相加操作 $$ E_{\text{总输入}} = \underbrace{E_{\text{token}}}_{\text{词嵌入}} + \underbrace{E_{\text{position}}}_{\text{位置嵌入}} + \underbrace{E_{\text{segment}}}_{\text{段嵌入}} $$ #### 代码示例 ```python final_embeddings = E_token + E_position + E_segment # 形状: [seq_len, hidden_size] ``` --- ### **完整输入处理流程(以句子对为例)** 1. **分词与标记** 输入: "如何学习机器学习?", "需要掌握数学基础。" 处理: ```text [CLS] 如 何 学 习 机 器 学 习 ? [SEP] 需 要 掌 握 数 学 基 础 。 [SEP] ``` 2. **生成ID序列** - `input_ids`: [101, 2456, 2038, ..., 102, 3457, 2134, ..., 102] - `token_type_ids`: [0,0,0,...,0,1,1,...,1] - `position_ids`: [0,1,2,...,511] 3. **嵌入层计算** - 三个嵌入矩阵分别查询结果后相加 --- ### **关键对比表** | 嵌入类型 | 作用维度 | 参数可学习性 | 典型维度(BERT-base) | |----------------|----------------|--------------|-----------------------| | 词嵌入 | 词汇语义 | 可学习 | 30522 × 768 | | 位置嵌入 | 绝对位置 | 可学习 | 512 × 768 | | 段嵌入 | 句子片段归属 | 可学习 | 2 × 768 | --- ### **常见疑问解答** 1. **为什么用相加而非拼接?** - 相加操作更节省计算资源,且实验表明效果足够好 - 隐含假设:不同嵌入空间可通过学习自动对齐 2. **如何处理超长文本?** - BERT预训练最大支持512位置,超长文本需截断或分块处理 - 部分改进模型(如Longformer)扩展了位置编码 3. **段嵌入在单句任务中是否必要?** - 技术上需要保留(因模型结构固定),但全部赋值为0即可 --- 通过这种设计,BERT将词汇、位置、句子归属三类信息统一编码到同一向量空间,为后续的Transformer层提供全面输入表征。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值