BGE模型架构与训练技术

目录

一、模型架构

[1. 预训练语言模型底座](#1. 预训练语言模型底座)

[2. 向量生成:池化策略](#2. 向量生成:池化策略)

[3. 归一化与标度调整](#3. 归一化与标度调整)

二、核心训练技术

阶段一:预训练初始化

1.数据预处理与准备

(1)数据清洗与过滤

(2)训练样本构建策略

2.RetroMAE训练流程

(1)训练核心思想

(2)对单个文本的训练步骤

第一步:双重掩码处理

第二步:前向传播过程

第三步:损失计算与优化

(3)训练过程中的关键机制

梯度流不对称性

信息瓶颈设计

训练稳定性策略

3.训练数据的使用特点

(1)弱监督信号的间接利用

(2)数据多样性的重要性

(3)规模化训练的优势

4.训练完成后的模型能力

(1)基础语义理解能力

(2)句子表示生成能力

(3)为后续训练奠定基础

5.RetroMAE与标准BERT预训练的关键区别

6.为什么这种训练有效?

(1)"压缩-解压"测试

(2)"开卷-闭卷"考试

(3)"信息瓶颈"原理

阶段二:有监督对比学习精调

1.数据

2.核心技术:难负例挖掘

方法1:批量负例

方法2:全局难负例挖掘

[3.损失函数:InfoNCE Loss](#3.损失函数:InfoNCE Loss)

阶段三:指令微调

1.核心思想

2.训练数据

3.正负样本构建策略

对于检索任务

对于相似度任务

4.训练过程

5.损失函数设计

检索任务损失

相似度任务损失

分类任务损失(可选)

6.训练后的模型行为

(1)向量生成示例

(2)零样本能力展示

三、BGE成功的设计哲学总结

四、BGE如何将一个不定长度的句子转换为固定长度的embedding向量?

1.分词与编码阶段

2.模型前向传播阶段

(1)嵌入层

(2)Transformer编码器层处理

(3)最终输出

3.池化阶段(关键步骤)

4.归一化阶段


BGE模型是一个系统工程,其强大并非源于单一创新,而是精心设计的架构多阶段、精细化的训练策略共同作用的结果。

一、模型架构

BGE的架构可以理解为"强预训练底座 + 高效的向量生成模块"。

1. 预训练语言模型底座

BGE并非从零开始训练,而是建立在强大的预训练语言模型之上,这是其能力的基石。

  • 早期版本 :基于标准的BERTRoBERTa架构。这些是经典的"编码器"模型,擅长理解文本的深度语义。
  • 最新版本 :采用更先进的预训练模型作为底座,例如FlagAttention 或经过优化的大规模模型。这些底座模型通过以下方式增强了BGE的基础能力:
    • 更长的上下文窗口:支持处理更长的文本序列,对于文档级Embedding至关重要。
    • 更高效的注意力机制:如FlashAttention,降低了计算成本,提升了训练效率。
    • 更优的预训练目标 :除了传统的掩码语言模型,还结合了类似RetroMAE的重构预训练方法。RetroMAE通过更强的文本重构任务,迫使模型学习更全面、更具判别性的句子表示,这对后续的对比学习有极大裨益。

2. 向量生成:池化策略

预训练模型输出的是每个Token的向量。如何将它们聚合为一个固定长度的句子向量,是关键一步。BGE默认采用cls****池化

  • **[CLS]**Token :在BERT系列模型中,输入序列的开头会添加一个特殊的[CLS]Token。经过多层Transformer编码后,这个Token的向量被认为汇聚了整个句子的信息。
  • 优势
    1. 计算高效:直接取用,无需额外计算。
    2. 被广泛验证:在句子分类、语义匹配等任务上被证明有效。
  • 其它可选策略 :BGE也支持mean pooling(取所有Token向量的平均值),在某些任务上可能表现不同,但cls是其默认和推荐方案。

3. 归一化与标度调整

这是Embedding模型优化中的一个重要技巧。

  • 向量归一化 :BGE对最终输出的句子向量进行L2 归一化 ,即将向量的长度缩放到1。这使得相似度计算完全由向量间的夹角(余弦相似度)决定,避免了向量模长带来的干扰,使训练更稳定、检索更高效。
  • 温度参数τ :在对比学习损失函数中,有一个关键的温度超参数 τ。BGE在训练中会优化这个参数,它控制着模型对"难负例"的区分强度。一个合适的 τ 值能让模型学习到更清晰的语义边界。

二、核心训练技术

BGE的训练是一个多阶段的、数据和技术驱动的过程。下图清晰地展示了其演进路径和核心技术:

以下是每个阶段的技术详解:

阶段一:预训练初始化

此阶段目标是为模型打下坚实的语义理解基础。

  • 数据 :使用大规模弱监督文本对,例如从互联网爬取的(网页标题,正文)、(问题,相关段落)、(锚文本,链接页面)等。这些数据虽然 noisy,但数量庞大,蕴含丰富的世界知识。
bash 复制代码
# 数据源示例(大规模弱监督文本对)
弱监督文本对 = [
    {
        "文本A": "什么是Python的装饰器?",
        "文本B": "装饰器是Python中修改函数或类的行为的语法糖。它允许在不修改原函数代码的情况下添加功能。",
        "来源": "StackOverflow问答对"
    },
    {
        "文本A": "神经网络反向传播算法",
        "文本B": "反向传播通过计算损失函数对网络权重的梯度,使用链式法则从输出层向输入层逐层传播误差。",
        "来源": "技术博客标题与正文"
    },
    {
        "文本A": "Docker容器化技术",
        "文本B": "Docker是一个开源的应用容器引擎,让开发者可以打包应用以及依赖包到一个可移植的镜像中...",
        "来源": "维基百科条目"
    }
    # ... 数千万甚至上亿对类似的技术领域文本
]
  • 技术 :采用RetroMAE等先进的预训练目标。RetroMAE 通过"编码强、解码弱"的重构任务,迫使编码器生成高质量的句子表示,以帮助一个非常简单的解码器(如单层Transformer)重建原始句子。这比传统的MLM对句子级语义的学习更直接、更有效:

1.数据预处理与准备

(1)数据清洗与过滤

首先对收集到的数亿对弱监督文本进行预处理:

  • 文本清洗:移除HTML标签、特殊字符,标准化空白字符,保留中文标点
  • 长度过滤:过滤掉过短(如少于5个字符)或过长(如超过512个字符)的文本
  • 质量过滤
    • 移除完全重复的文本对
    • 移除内容重叠度过高的文本对(如超过80%相似度)
    • 根据来源类型赋予不同权重(如StackOverflow问答对质量高,论坛讨论质量相对低)
    • 计算每对文本的"质量评分",基于文本完整性、信息密度等指标
(2)训练样本构建策略

对于每个弱监督文本对(如问题-答案、标题-正文):

  • 双向处理 :每对文本的两个部分(文本A和文本B)分别作为独立的训练样本
  • 不依赖配对关系:RetroMAE训练时不利用文本对之间的关联性,只处理单个文本
  • 大规模批处理:每次训练使用大批量(如4096个)文本,每个文本独立进行掩码和重构

例如,对于"什么是Python的装饰器?"和其答案,会生成两个独立的训练样本,分别学习问题的表示和答案的表示。

2.RetroMAE训练流程

(1)训练核心思想

RetroMAE的核心是"不对称的编码-解码架构":

  • 编码器强大:12层Transformer,参数丰富
  • 解码器极弱:仅1层Transformer或简单MLP
  • 迫使编码器学习信息密集的句子表示,因为解码器无法从高度掩码的输入中获取足够信息
(2)对单个文本的训练步骤
第一步:双重掩码处理

对于每个文本(如"什么是Python的装饰器?"),进行两次不同的掩码

(1)编码器输入(轻度掩码)

  • 掩码率:约15-30%
  • 掩码策略:采用标准BERT的MLM策略
    • 80%替换为[MASK]标记
    • 10%替换为随机词
    • 10%保持原词不变
  • 目标:编码器能看到大部分上下文,学习正常的语言理解
bash 复制代码
原句:什么是Python的装饰器?
轻度掩码后:[什么是] [Python] [的] [装饰器] [?]
            ↓        ↓      ↓      ↓     ↓
           保留    [MASK]   保留  [MASK]  保留
编码器输入:"什么是[MASK]的[MASK]?"

(2)解码器输入(重度掩码)

  • 掩码率:约80-90%,甚至可达99%
  • 掩码策略:简单粗暴地掩盖大部分词
  • 目标:解码器只能看到极少数词,必须依赖编码器生成的句子向量
bash 复制代码
原句:什么是Python的装饰器?
重度掩码后:[什么是] [Python] [的] [装饰器] [?]
             ↓        ↓     ↓      ↓     ↓
          [MASK]  [MASK]  [MASK]  保留 [MASK]
解码器输入:"[MASK][MASK][MASK]装饰器[MASK]"
第二步:前向传播过程

(1)编码阶段

  • 轻度掩码的文本输入到**12层Transformer编码器,**编码器输出每个位置的隐藏状态
  • [CLS]位置的向量作为句子表示向量(sentence embedding),这个向量需要捕获整个句子的语义信息

(2)解码阶段

  • 重度掩码的文本与句子表示向量一起输入到极简解码器
  • 解码器只有1层Transformer,参数很少,能力极弱
  • 解码器必须依赖句子表示向量才能重建原始文本
  • 解码器预测每个被掩码位置的原始词语
第三步:损失计算与优化
  1. 重构损失
    • 计算解码器预测与原始文本的交叉熵损失
    • 关键 :只计算被掩码位置的损失,未被掩码的位置不参与损失计算
  2. 损失函数的数学意义
    • 最大化:P(原始句子 | 句子向量, 少量未被掩码的词语)
    • 由于解码器能力弱,它必须依赖编码器生成的句子向量,这迫使编码器学习生成信息密集的句子向量
  3. 训练目标
    • 不是简单地预测几个掩码词(如BERT的MLM),而是从高度残缺的输入 中重建完整句子
    • 这要求句子向量包含足够完整的语义信息
(3)训练过程中的关键机制
梯度流不对称性
  • 编码器获得强梯度信号:因为解码器能力弱,重构损失主要反映了编码器生成句子向量的质量
  • 解码器梯度受限:解码器参数少,且输入信息极度缺乏,无法"作弊"地学习重建
信息瓶颈设计
  • 解码器只能通过两个信息源重建句子:
    1. 极少数未被掩码的词语(如例子中的"装饰器")
    2. 编码器生成的句子向量
  • 由于未被掩码的词语极少(可能只有1-2个),解码器必须主要依赖句子向量
  • 这迫使句子向量必须编码几乎完整的句子语义
训练稳定性策略
  1. 渐进式掩码率:训练初期使用较低的重度掩码率(如70%),后期逐渐增加(至90%)
  2. 动态温度调整:控制损失函数的"温度"参数,平衡难易样本的学习
  3. 大批次训练:使用大批次(4096或更大)以获取更稳定的梯度估计

3.训练数据的使用特点

(1)弱监督信号的间接利用

虽然文本对之间有语义关联(如问题和答案),但RetroMAE训练时:

  • 不直接使用配对关系:问题和答案分别独立训练
  • 间接学习关联性:相似主题的文本会在相近的批次中出现
  • 隐含的领域适应:技术领域的文本共享专业术语和表达方式
(2)数据多样性的重要性
  • 来源多样性:StackOverflow、技术博客、维基百科、API文档等
  • 主题覆盖广度:从基础语法到高级概念,从前端到后端,从理论到实践
  • 表达方式差异:正式文档、问答对话、代码注释等不同文体
(3)规模化训练的优势
  • 数量优势:数亿对文本提供海量训练样本
  • 批内负采样:大批次训练天然提供"对比"信号(不同文本互为隐式负例)
  • 语义空间覆盖:覆盖广泛的技术概念和表达方式

4.训练完成后的模型能力

经过第一阶段RetroMAE预训练后,模型获得:

(1)基础语义理解能力
  • 词语级理解:理解技术术语的含义和用法
  • 句法结构理解:理解复杂的技术表达句式
  • 领域知识获取:掌握编程语言、框架、算法的相关知识
(2)句子表示生成能力
  • 信息密集的向量[CLS]向量能够编码句子的核心语义
  • 语义相似性敏感:语义相似的句子产生相近的向量
  • 领域特性编码:技术文本的向量反映其技术内容和难度级别
(3)为后续训练奠定基础
  • 良好的初始化:模型参数已经具备基本的技术语言理解能力
  • 稳定的表示空间:句子向量空间已经有一定的结构和规律
  • 高效的知识迁移:可以快速适应第二阶段的对比学习

5.RetroMAE与标准BERT预训练的关键区别

|--------------|------------------|--------------------------|
| 方面 | 标准BERT (MLM) | RetroMAE |
| 训练目标 | 预测15%的掩码词 | 从高度残缺的文本重建完整句子 |
| 解码器 | 无独立解码器,直接使用编码器输出 | 独立的极简解码器 |
| 掩码策略 | 统一掩码率(15%) | 双重掩码率(轻度15-30%,重度80-90%) |
| 信息流 | 上下文到掩码位置 | 句子向量到所有掩码位置 |
| 能力侧重 | 词语级预测 | 句子级语义压缩与重建 |
| 对句子向量的影响 | 间接优化 | 直接且强制性的优化 |

6.为什么这种训练有效?

RetroMAE的训练可以类比为:

(1)"压缩-解压"测试
  • 编码器:压缩专家,需要将句子压缩为紧凑的向量
  • 解码器:解压新手,只能基于压缩包和少量提示进行解压
  • 训练过程:如果解压效果差,说明压缩专家没有把关键信息放进压缩包
(2)"开卷-闭卷"考试
  • 编码器学习阶段:在完整文本上"开卷学习"
  • 解码器测试阶段:在高度掩码的文本上"闭卷考试"
  • 能力验证:如果编码器学得好,就能帮助解码器通过考试
(3)"信息瓶颈"原理
  • 解码器的信息输入被极度限制
  • 编码器必须通过句子向量提供几乎所有必要信息
  • 这迫使句子向量成为信息的"高效瓶颈"

通过这种训练,BGE的第一阶段获得了高质量的句子编码器,为后续的对比学习和指令微调奠定了坚实基础。编码器学会生成信息丰富、语义准确的句子向量,这些向量能够精确反映文本的语义内容,特别适合用于技术文档的检索和理解任务。

阶段二:有监督对比学习精调

这是BGE能力跃升的核心阶段,目标是从"理解语义"升级为"精准区分语义"。

1.数据

使用高质量的标注数据,如人工标注的语义相似度数据集、高质量的问答对等。

bash 复制代码
# 人工标注的查询-文档对(正样本)
标注数据 = [
    {
        "查询": "如何解决Python中的内存泄漏?",
        "正文档": "在Python中,内存泄漏通常由循环引用引起。使用gc模块可以检测和回收,或使用weakref避免强引用。",
        "标注质量": "专家标注,5星"
    },
    {
        "查询": "TensorFlow和PyTorch的主要区别",
        "正文档": "TensorFlow使用静态计算图,部署友好;PyTorch使用动态图,更适合研究和快速原型开发。",
        "标注质量": "专家标注,5星"
    }
]

# 构建负样本池(技术文档库)
文档库 = [
    "Python内存管理基于引用计数和垃圾回收机制...",
    "循环引用发生在两个对象相互引用时...",
    "TensorFlow 2.0引入了eager execution,使其更像PyTorch...",
    "PyTorch由Facebook开发,广泛应用于学术研究...",
    "内存泄漏在C++中更常见,因为需要手动管理内存...",  # 相关但不完全匹配
    "Java使用垃圾回收器自动管理内存...",  # 部分相关
    "Docker容器内存限制配置方法..."  # 不相关
]

2.核心技术:难负例挖掘

这是BGE区别于早期模型(如直接使用BERT句向量的)的关键。目标是找到那些与正样本"相似但不相关"的文本作为负样本,迫使模型学习精细的语义差别。

方法1:批量负例

在一个训练批次中,当前样本的正样本与其他所有样本的配对自然形成负例。这是最基本且高效的。

bash 复制代码
批次 = [
    (查询1, 正文档1),  # Python内存泄漏
    (查询2, 正文档2),  # TensorFlow vs PyTorch
    (查询3, 正文档3)   # Docker网络配置
]

# 对于查询1,负例包括:
# 1. 查询2的正文档(完全不相关)
# 2. 查询3的正文档(完全不相关)
# 3. 自己查询的难负例(相关但不精准)
方法2:全局难负例挖掘
  1. 使用一个初步训练好的模型或BM25等传统方法,为每个查询从海量语料库中检索出一批候选文档
  2. 这些候选文档中,除了一个真正的正例,其余的都是"潜在负例"。其中那些与查询在主题、词汇上高度相关但语义不完全匹配的,就是"难负例"。
  3. 将这些难负例加入训练,模型必须学会区分"高度相关"和"完全匹配",从而学习到非常精细的语义边界。

例如,用第一阶段模型为查询"如何解决Python中的内存泄漏?"检索文档库:

bash 复制代码
锚点(查询): "如何解决Python中的内存泄漏?"
正例: "在Python中,内存泄漏通常由循环引用引起..." (标注正样本)
负例集: 
  - "Python内存管理基于引用计数..." (难负例,相关但不精准)
  - "循环引用发生在两个对象..." (难负例,概念相关但非解决方案)
  - "Java使用垃圾回收器..." (随机负例)
  - "Docker容器内存限制..." (随机负例)

3.损失函数:InfoNCE Loss

这是对比学习的标准损失函数。其核心思想是:在向量空间中,拉近正样本对的向量距离,同时推远与所有负样本对的向量距离。其公式为:

其中,q是查询,p^+是正样本,p^-是负样本,sim是余弦相似度,τ是温度参数。

**通过第二阶段训练,**得到一个精准的技术文档检索器,例如能够:

  • 为"Python内存泄漏"精确匹配解决方案文档,而非一般的内存管理文档
  • 清楚区分TensorFlow和PyTorch的对比文档与各自的教程文档

阶段三:指令微调

1.核心思想

指令微调的核心是统一多任务学习。通过给模型添加"指令前缀",我们训练模型理解:"当看到这种指令时,请生成适合该任务的向量表示"。

关键思想:同一个文本,在不同任务下需要不同的向量表示

例如:

  • 用于检索的"批归一化"向量:应该靠近"优化技巧"类文档
  • 用于相似度计算的"批归一化"向量:应该靠近"学习率衰减"的向量
  • 用于分类的"批归一化"向量:应该靠近"深度学习训练技术"类别中心

2.训练数据

多任务数据混合,在输入文本前添加一个指令前缀

bash 复制代码
批次数据示例 = [
    # 任务1: 检索任务
    {
        "指令": "为这个查询生成表示以用于检索相关技术文档:",
        "文本": "如何优化深度神经网络的训练速度?",
        "任务类型": "retrieval",
        "正样本": "梯度累积、混合精度训练、数据预加载等方法可加速训练",
        "负样本": ["PyTorch安装教程", "神经网络历史介绍", "激活函数对比"]  # 批次内其他文本
    },
    
    # 任务2: 语义相似度任务  
    {
        "指令": "为这两个句子生成表示以计算它们的语义相似度:",
        "文本": "批归一化能加速训练收敛。##学习率衰减有助于找到更优解。",
        "任务类型": "similarity",
        "标签": 0.8  # 相似度分数(0-1)
    },
    
    # 任务3: 分类任务
    {
        "指令": "为这个技术问题生成表示以用于分类:",
        "文本": "Kubernetes中Pod无法启动,显示ImagePullBackOff错误",
        "任务类型": "classification",
        "类别": "容器编排/故障排查"
    },
    
    # 任务4: 聚类任务
    {
        "指令": "为这篇技术文档生成表示以用于文档聚类:",
        "文本": "React Hooks的设计哲学是让函数组件拥有类组件的能力...",
        "任务类型": "clustering"
    }
]

3.正负样本构建策略

对于检索任务
  • 正样本:查询 + 相关文档(如"如何优化训练速度" + "梯度累积方法介绍")
  • 负样本
    1. 批次内负例:同一批次中其他查询的文档
    2. 难负例:与查询相关但不完全匹配的文档(如"神经网络基础理论")
对于相似度任务
  • 正样本对:语义相似的句子对
  • 负样本对:语义不相似的句子对

4.训练过程

模型输入格式:

bash 复制代码
输入 = 指令 + 文本

例如:
检索任务输入:"为这个查询生成表示以用于检索相关技术文档:如何优化深度神经网络的训练速度?"

相似度任务输入:"为这两个句子生成表示以计算它们的语义相似度:批归一化能加速训练收敛。##学习率衰减有助于找到更优解。"

训练目标:让模型学会根据指令调整其编码方式,使得:

  1. 相同指令下的相似内容产生相近的向量
  2. 不同指令下的相同内容产生不同的向量
  3. 向量表示对特定任务最优

5.损失函数设计

虽然任务是多样的,但都统一使用对比学习损失函数的变体InfoNCE Loss(多任务版本),对于批次中的每个样本 i :

其中:

  • :指令+文本的向量
  • :正样本的向量
  • :负样本的向量
  • :温度参数
  • K :负样本数量

不同任务的损失计算:

检索任务损失
  • 正样本:相关文档(有监督标注)
  • 负样本:批次内其他文档 + 难负例
  • 目标:查询向量靠近相关文档,远离不相关文档
相似度任务损失
  • 构造三元组:锚点句子、正样本(相似句)、负样本(不相似句)
  • 损失:L = \max(0, \text{sim}(锚点, 负样本) - \text{sim}(锚点, 正样本) + \alpha)
  • 其中 是边界参数
分类任务损失(可选)
  • 在向量空间中进行分类:使用交叉熵损失
  • 但不是必须的,因为好的向量表示自然能分离不同类别

6.训练后的模型行为

(1)向量生成示例
bash 复制代码
# 同一文本,不同指令下的不同向量
text = "批归一化能加速训练收敛"

vec_retrieval = model("为这个查询生成表示以用于检索相关技术文档:" + text)
# 偏向:优化技巧、训练加速、实践方法

vec_similarity = model("为这两个句子生成表示以计算它们的语义相似度:" + text)
# 偏向:技术原理、数学特性、理论解释

vec_classification = model("为这个技术问题生成表示以用于分类:" + text)  
# 偏向:深度学习、训练技术、优化算法
(2)零样本能力展示

即使训练时没有"代码查重"任务:

bash 复制代码
# 使用相似度指令的变体
instruction = "为这两段代码生成表示以检测它们的逻辑相似性:"
code1_vec = model(instruction + code1)
code2_vec = model(instruction + code2)
# 模型能合理生成用于代码比对的向量

经历指令微调后,得到一个通用的技术领域Embedding模型,能够:

  1. 通过改变指令适应不同下游任务
  2. 零样本处理训练中未见过的新任务类型
  3. 在技术问答、文档分类、代码分析等多种场景下表现良好

与传统多任务学习的区别:

|-----------|-------------|-----------------|
| 方面 | 传统多任务学习 | BGE指令微调 |
| 任务区分 | 不同任务有不同的输出头 | 所有任务共享同一个输出(向量) |
| 输入格式 | 任务ID或特定输入格式 | 自然语言指令 |
| 泛化能力 | 限于训练任务 | 可泛化到新的指令描述的任务 |
| 模型复杂度 | 需要多个输出层 | 单一输出层,更简洁 |
| 可解释性 | 低 | 高(指令本身解释了任务) |

三、BGE成功的设计哲学总结

  1. 深度而非广度优先 :不追求在训练初期就混合所有类型的数据,而是采用阶段性训练,先在弱监督数据上打基础,再在高质量数据上做精调,最后用指令统一接口。每一步目标明确。
  2. 面向目标优化 :其所有技术(尤其是难负例挖掘)都强烈指向其核心目标------提升检索精度。这让它在检索任务上表现一骑绝尘。
  3. 工程化数据闭环:深刻理解"数据决定上限,模型逼近上限"。在数据构建(弱监督对、高质量标注对、难负例挖掘)上投入巨大,形成了强大的数据飞轮。
  4. 简洁有效的接口设计 :通过指令微调这一巧妙设计,在不增加模型复杂度的前提下,极大地扩展了模型的适用范围和易用性。

总而言之,BGE的架构提供了一个强大的特征提取器,而其多阶段训练技术(特别是难负例挖掘和指令微调)则像一套精密的雕刻刀,将粗糙的语义理解能力,精细地打磨成了适用于实际任务(尤其是检索)的锋利工具。

四、BGE如何将一个不定长度的句子转换为固定长度的embedding向量?

1.分词与编码阶段

目标:将任意长度的文本转换为模型能理解的数字序列

具体步骤:

  1. 文本预处理:去除多余空格,Unicode标准化等。
  2. 分词 :使用预训练的WordPieceSentencePiece分词器将句子拆分为子词(subword)。
  3. 添加特殊标记
    • [CLS]:插入在序列开头(用于最终的句子表示)
    • [SEP]:插入在序列结尾(表示序列结束)
  4. 转换为ID:将每个token映射到词汇表中的整数ID。
  5. 填充/截断 :统一为固定长度(如512个token),超出部分截断,不足部分填充[PAD]
  6. 创建注意力掩码 :标记哪些位置是真实token,哪些是填充的[PAD]

示例:

bash 复制代码
句子 = "什么是Python的装饰器?"

# 分词结果(假设):
tokens = ["[CLS]", "什么", "是", "python", "的", "装饰", "器", "?", "[SEP]"]

# 转换为ID:
input_ids = [101, 1234, 2345, 3456, 4567, 5678, 6789, 7890, 102]

# 填充到最大长度512:
input_ids = input_ids + [0] * (512 - 9)  # 0是[PAD]的ID

# 注意力掩码(1表示真实token,0表示[PAD]):
attention_mask = [1] * 9 + [0] * (512 - 9)

2.模型前向传播阶段

目标:通过多层Transformer编码器获取每个token的上下文感知表示

具体过程:

bash 复制代码
输入层 → 嵌入层 → 12/24层Transformer编码器 → 输出层
      (词嵌入+位置嵌入+段落嵌入)

(1)嵌入层

  • 词嵌入:将每个token ID转换为稠密向量(维度如768/1024)
  • 位置嵌入:添加位置信息(告诉模型每个token的位置)
  • 段落嵌入:如果是双句输入,区分第一句和第二句
bash 复制代码
# 例如,对"python"这个词:
token_embedding = Embedding(3456)  # -> [0.1, -0.3, 0.5, ...] (768维)
position_embedding = PositionEmbedding(位置=4)  # -> [0.2, 0.1, -0.1, ...]
final_embedding = token_embedding + position_embedding

(2)Transformer编码器层处理

  • 核心:自注意力机制让每个token能够关注序列中所有其他token
  • 逐层传递:经过多个Transformer层(BGE-base:12层,BGE-large:24层)
  • 每一层输出:每个token都会获得一个更新的向量表示
bash 复制代码
# 经过第1层后:
layer1_output = TransformerLayer1(初始嵌入) # "python"的表示现在包含了"装饰器"的信息
# 经过第12层后:
layer12_output = TransformerLayer12(layer11_output) # "python"的表示现在包含了整个句子的上下文信息

(3)最终输出

  • 从最后一层Transformer获取所有token的表示
  • 形状为:[batch_size, sequence_length, hidden_size]
  • 例如:[1, 512, 1024]表示1个句子,512个token,每个token1024维

3.池化阶段(关键步骤)

目标:将512个token的向量序列压缩为1个句子向量

BGE默认使用CLS池化策略:

bash 复制代码
# 从最后一层输出中取出[CLS] token对应的向量
# 形状从 [1, 512, 1024] 变为 [1, 1024]
sentence_embedding = last_hidden_states[:,0,:]  # 取第0个位置([CLS])

为什么选择[CLS] token?

  • BERT系列模型在预训练时,[CLS]token被设计为汇聚整个序列信息的特殊token
  • 在MLM(掩码语言模型)任务中,[CLS]参与所有token的预测
  • 在NSP(下一句预测)任务中,[CLS]直接用于分类
  • 实践证明,取[CLS]作为句子表示既高效又有效

4.归一化阶段

目标:将向量标准化,便于相似度计算

python 复制代码
# L2归一化:使向量模长为1
def normalize(embeddings):
    norm = torch.norm(embeddings, p=2, dim=1, keepdim=True)
    return embeddings / norm.clamp(min=1e-8)

sentence_embedding = normalize(sentence_embedding)

为什么需要归一化?

  1. 稳定训练:防止向量模长过大或过小导致梯度问题
  2. 高效检索 :归一化后,余弦相似度 = 点积(cos(A,B) = A·B / (|A||B|) = A·B
  3. 统一量纲:所有向量都在同一尺度上比较
相关推荐
Qiuner2 小时前
Windows安装Dokcer Desktop与汉化
windows·docker·架构
咕噜企业分发小米2 小时前
豆包大模型的MoE架构具体是如何实现参数激活效率提升的?
架构
百***78752 小时前
一步API+GPT-5.2生产级落地指南:架构设计+高可用+成本控制
开发语言·gpt·架构
程途拾光1583 小时前
MoE架构革新:突破规模与成本的两难困境
架构
沛沛老爹3 小时前
从Web到AI:金融/医疗/教育行业专属Skills生态系统设计实战
java·前端·人工智能·git·金融·架构
timer_0173 小时前
Anolis OS 23.4 发布:全面支持 RVA23 RISC-V 架构,强化安全与云原生生态
安全·架构·risc-v
乾元3 小时前
黑盒之光——机器学习三要素在安全领域的投影
运维·网络·人工智能·网络协议·安全·机器学习·架构
廋到被风吹走3 小时前
【分布式缓存】分布式缓存架构全解析:从 Redis Cluster 到多级缓存策略
分布式·缓存·架构
lbb 小魔仙3 小时前
【Java】微服务架构 Java 实战:Spring Cloud Gateway + Nacos 全链路搭建指南
java·微服务·架构