在OpenAI o3
火遍全网的同时,一个名为ModernBERT的热门模型已经发布并成为热门话题,而且 Huggingface 的官方博客也已发布,下面笔者是阅读相关资料之后的一些总结
六年后,BERT 终于迎来了全面超越的继任者 - ModernBERT 不仅在性能和效率上实现了全面提升,还将上下文长度扩展到了 8192 token,成为一个真正意义上的新一代编码器模型。
论文链接:
https://arxiv.org/pdf/2412.13663
项目链接:
模型动机
论文解释了在纯解码器模型(例如 GPT)全面展开之际改进 BERT(一种过时的纯编码器模型)的动机。
仅解码器模型(Decoder Only)参数量太大
我想大家都很清楚,参数量太大,做任何事情都需要花钱,需要时间来推断,而且很难处理。虽然说的不多,但在文中是比作一辆汽车。
像 OpenAI O1 这样的前沿模型就像法拉利 SF-23
相比之下,BERT 模型就像本田思域。
易于操作的 BERT 有无数的应用。文章特别提到了 RAG,我想我们可以普遍认为 BERT 家族对于提取考虑上下文的向量是最方便的。
那么,让我们利用现代技术来改进 BERT!该项目称为 ModernBERT。2018 年对于计算机科学世界来说已经是一千年前的事了,所以……
BERT 于 2018 年发布(人工智能年已经是几千年前了!)
主要亮点
最新发布超越BERT的先进编码器模型—ModernBERT,它是一个最新的编码器模型系列,它在速度和准确性方面全面超越了BERT及其后续模型。
- ModernBERT 是一个新的编码器模型系列,旨在取代 2018 年发布的 BERT。
- 它提供了两个版本:Base 版本(139M 参数),Large 版本(395M 参数)
ModernBERT具有8192的序列长度,可在各种下游任务中,实现更好的性能和更快的处理速度,并作为BERT模型的替代品。
模型特点
ModernBERT 是一种现代化的双向编码器专用 Transformer 模型(BERT 风格),已在 2 万亿个英语和代码数据上进行预训练,原生上下文长度最多为 8,192 个token。ModernBERT 利用了最近的架构改进,例如:
- 旋转位置嵌入 (RoPE) 用于长上下文支持。
- 局部-全局交替注意力机制 ,提高长输入的效率。
- 取消填充和 Flash Attention 可实现高效推理。
ModernBERT 的原生长上下文长度使其成为需要处理长文档的任务的理想选择,例如检索、分类和大型语料库中的语义搜索。该模型是在大量文本和代码语料库上训练的,因此适用于各种下游任务,包括代码检索和混合(文本 + 代码)语义搜索。
ModernBERT的改进之处,包括其现代化的Transformer架构、对效率的关注以及现代化的数据规模和来源,与其他模型相比获得最高分。具体介绍如下:
- 更现代化的 Transformer 架构
- 将旧的 positional encoding 替换为 “rotary positional embeddings” (RoPE),使模型更好地理 解单词之间的相对位置,并支持更长的序列长度。
- 将旧的 MLP 层替换为 GeGLU 层,改进了 BERT 原有的 GeLU 激活函数。
- 简化架构,去除不必要的偏差项,更有效地利用参数预算。
- 在嵌入后添加一个额外的标准化层,帮助稳定训练。
- 注重效率
- 交替注意力机制:ModernBERT 每 3 层才进行一次全局注意力计算,其他层使用滑动窗口,每个 token 只关注与其最近的 128 个 token(局部注意力)。显著提高了处理长输入序列的速度。
- 取消填充和序列打包:通过去除填充 token 并将多个序列连接成长度接近模型最大输入长度的迷你批次,最大限度地提高计算效率,避免在填充 token 上浪费计算资源。
- 硬件感知模型设计:通过平衡模型深度和宽度,以及与目标 GPU 硬件的匹配,最大限度地提高硬件利用率。
- 现代数据规模和来源
- ModernBERT 使用来自各种英语来源的数据进行训练,包括网页文档、代码和科学文章,总计 2 万亿个 token,其中大部分是唯一的。这与之前编码器中常见的 20 到 40 次重复相比,数据多样性更高。
- 注重代码数据:ModernBERT 在训练数据中包含了大量的代码,使其在处理编程相关任务方面具有独特的优势。改进的训练过程。
- 三阶段训练:ModernBERT 采用三阶段训练过程,以确保模型在各种任务上的良好表现,包括长上下文和短上下文任务。
- 权重初始化技巧:ModernBERT-large 模型使用 ModernBERT-base 的权重进行初始化,而不是随机初始化,这提高了训练速度和效果。
关键技术
RoPE嵌入
RoFormer (2020) 中提出的令牌位置嵌入,在 LLama 中也使用了。最初的 BERT 使用绝对位置嵌入,ModernBERT现在改成使用这种RoPE 。具体可以查看:
You could have designed state of the art positional encoding
全局与局部注意力机制
首先,使用的Attention改为Flash Attention。最重要的是,我们引入了一种称为交替注意力的东西。这意味着不是每次都使用全局注意力(查看所有单词的注意力),而是不时应用局部注意力(仅查看周围的单词)。
如果你想一想,当人类读书时,他们不一定每次都会注意每一句话,但他们可能会将整体和局部结合起来。这似乎产生了相当大的影响。
下面是一些类似的想法......
视觉变压器中远距离交互的焦点关注 https://proceedings.neurips.cc/paper\_files/paper/2021/hash/fc1a36821b02abbd2503fd949bfc9131-Abstract.html
LGAFormer:具有局部和全局注意力的变压器,用于动作检测 https://dl.acm.org/doi/10.1007/s11227-024-06138-1
取消填充
另一个提高 ModernBERT 效率的核心机制是使用 Unpadding 和 Sequence Packing。
为了能够处理同一批次中的多个序列,编码器模型要求它们具有相同的长度,以便它们可以执行并行计算。传统上,我们依靠填充来实现这一点:找出哪个句子最长,并添加无意义的标记(填充标记)来填充其他每个序列。
虽然填充解决了这个问题,但它并不是很优雅:大量的计算最终被花费并浪费在填充标记上,而这些标记并没有提供任何语义信息。
取消填充 解决了这个问题:ModernBERT不会保留这些填充标记,而是将它们全部删除,并将它们连接到批大小为 1 的小批中,从而避免所有不必要的计算。
如果使用的是 Flash Attention,ModernBERT的取消填充实现甚至比以前的方法更快,以前的方法严重依赖于在模型中取消填充和重新填充序列:我们更进一步,引入了自己的取消填充实现,严重依赖于 Flash Attention 的 RoPE 支持的最新发展。这使得 ModernBERT 只需取消填充一次,并在处理后可选择重新填充序列,从而比以前的方法加快 10-20%。
为了进一步加快预训练速度,ModernBERT模型中的 unpadding 效果很好,因为将其与序列打包结合使用。序列打包是合乎逻辑的下一步:因为我们将输入连接成一个序列,而 GPU 非常擅长并行化,ModernBERT希望最大限度地提高从单个前向模型传递中挤出的计算效率。为此,我们使用贪婪算法将各个序列分组为尽可能接近模型最大输入长度的连接序列。
模型权重
- ModernBERT-base – 22 层,1.49 亿个参数
- ModernBERT-large – 28 层,3.95 亿个参数
模型评估效果如下:
模型使用
在transformers版本发布之前,使用如下安装。
pip install git+https://github.com/huggingface/transformers.git
如果想要达到更好的性能,可以搭配 Flash Attention 2
pip install flash-attn
使用AutoModelForMaskedLM:
from transformers import AutoTokenizer, AutoModelForMaskedLM
model_id = "answerdotai/ModernBERT-base"
tokenizer = AutoTokenizer.from_pretrained(model_id)
model = AutoModelForMaskedLM.from_pretrained(model_id)
text = "The capital of France is [MASK]."
inputs = tokenizer(text, return_tensors="pt")
outputs = model(**inputs)
# To get predictions for the mask:
masked_index = inputs["input_ids"][0].tolist().index(tokenizer.mask_token_id)
predicted_token_id = outputs.logits[0, masked_index].argmax(axis=-1)
predicted_token = tokenizer.decode(predicted_token_id)
print("Predicted token:", predicted_token)
# Predicted token: Paris
使用pipeline:
import torch
from transformers import pipeline
from pprint import pprint
pipe = pipeline(
"fill-mask",
model="answerdotai/ModernBERT-base",
torch_dtype=torch.bfloat16,
)
input_text = "He walked to the [MASK]."
results = pipe(input_text)
pprint(results)
应用场景
- RAG(检索增强生成)系统
- 代码搜索和分析
- 内容审核
- 信息检索
- 文档处理
参考资料
- ModernBERT-超越BERT的先进编码器模型
- 终于有 BERT 的替代品了
添加微信,回复”大模型“进入交流群
长按扫码关注