|
| 1 | +# 在 PaddleNLP 中复现 ModernBert 模型 |
| 2 | + |
| 3 | +| 任务名称 | 在 PaddleNLP 中复现 ModernBert 模型 | |
| 4 | +| --- | --- | |
| 5 | +| 提交作者 | robinbg | |
| 6 | +| 提交时间 | 2025-04-02 | |
| 7 | +| 版本号 | V1.0 | |
| 8 | +| 依赖飞桨版本 | develop | |
| 9 | +| 文件名 | 20250402_add_modernbert_for_paddlenlp.md | |
| 10 | + |
| 11 | +# 一、概述 |
| 12 | + |
| 13 | +## 1、相关背景 |
| 14 | +ModernBert 是一种现代优化的 BERT 变体,在 2 万亿个词元上训练,支持 8192 长度的序列。相比原始 BERT,ModernBert 在性能与模型大小之间提供了更好的权衡,主要通过以下改进: |
| 15 | + |
| 16 | +* 使用旋转位置编码 (Rotary Positional Embeddings),替代传统的绝对位置编码 |
| 17 | +* 采用 GeGLU 激活函数,替代传统的 GELU |
| 18 | +* 实现滑动窗口注意力机制 (Sliding Window Attention),有效处理长序列 |
| 19 | +* 针对推理速度优化的架构设计 |
| 20 | + |
| 21 | +这些改进使得 ModernBert 在相同参数量下具有更强的性能,且更适合处理长序列输入。 |
| 22 | + |
| 23 | +## 2、功能目标 |
| 24 | +在 PaddleNLP 中实现 ModernBert 模型,对齐 HuggingFace transformers 中的实现,确保在 fp32 下误差小于 1e-5,bf16 下误差小于 1e-3。 |
| 25 | + |
| 26 | +## 3、意义 |
| 27 | +ModernBert 模型的引入将增强 PaddleNLP 处理长文本的能力,为用户提供更高效的预训练模型选择,同时保持与 HuggingFace 生态的兼容性。 |
| 28 | + |
| 29 | +# 二、飞桨现状 |
| 30 | +PaddleNLP 目前已支持多种 Transformer 架构模型,包括 BERT、RoBERTa、ERNIE 等,但尚未支持具有旋转位置编码、GeGLU 激活函数和优化的长序列处理能力的 ModernBert 模型。现有的 BERT 实现可以作为实现 ModernBert 的基础,但需要进行架构上的修改以适配 ModernBert 的特性。 |
| 31 | + |
| 32 | +# 三、业内方案调研 |
| 33 | +目前,HuggingFace transformers 库已实现 ModernBert 模型,地址为: |
| 34 | +https://github.com/huggingface/transformers/tree/main/src/transformers/models/modernbert |
| 35 | + |
| 36 | +该实现包含了完整的模型架构、预训练权重以及相关的文档,是 PaddleNLP 实现 ModernBert 的主要参考。ModernBert 模型在 HuggingFace 上已有多个开源检查点,并展示了其在多种下游任务上的出色表现。 |
| 37 | + |
| 38 | +# 四、对比分析 |
| 39 | +与 PaddleNLP 现有的 BERT 实现相比,ModernBert 需要做以下关键改进: |
| 40 | + |
| 41 | +1. 位置编码:从静态绝对位置编码转变为旋转位置编码 (RoPE) |
| 42 | +2. 激活函数:从 GELU 转变为 GeGLU |
| 43 | +3. 注意力机制:实现滑动窗口注意力,支持处理长序列 |
| 44 | +4. 模型配置:添加新的配置参数以支持 ModernBert 特有的功能 |
| 45 | + |
| 46 | +这些改进将使 ModernBert 能够处理更长的序列(最大 8192 个词元),同时提供更好的性能-大小比。 |
| 47 | + |
| 48 | +# 五、设计思路与实现方案 |
| 49 | +ModernBert 模型在 PaddleNLP 中的实现将参考现有的 BERT 实现结构,主要包含以下几个关键文件: |
| 50 | + |
| 51 | +1. configuration.py:定义 ModernBertConfig 类,包含模型所需的配置参数 |
| 52 | +2. modeling.py:实现模型的核心架构 |
| 53 | +3. tokenizer.py:与 BERT tokenizer 兼容,复用现有实现 |
| 54 | + |
| 55 | +## 1. ModernBertConfig 配置类 |
| 56 | +在 `paddlenlp/transformers/modernbert/configuration.py` 中,需要定义 ModernBertConfig 类,继承自 PretrainedConfig: |
| 57 | + |
| 58 | +```python |
| 59 | +from paddlenlp.transformers.configuration_utils import PretrainedConfig |
| 60 | + |
| 61 | +class ModernBertConfig(PretrainedConfig): |
| 62 | + """ |
| 63 | + ModernBert 模型的配置类,包含所有架构参数 |
| 64 | + """ |
| 65 | + def __init__( |
| 66 | + self, |
| 67 | + vocab_size=30522, |
| 68 | + hidden_size=768, |
| 69 | + num_hidden_layers=12, |
| 70 | + num_attention_heads=12, |
| 71 | + intermediate_size=3072, |
| 72 | + hidden_act="geglu", # 使用 GeGLU 替代 GELU |
| 73 | + hidden_dropout_prob=0.1, |
| 74 | + attention_probs_dropout_prob=0.1, |
| 75 | + max_position_embeddings=8192, # 支持更长的序列 |
| 76 | + type_vocab_size=2, |
| 77 | + initializer_range=0.02, |
| 78 | + layer_norm_eps=1e-12, |
| 79 | + pad_token_id=0, |
| 80 | + sliding_window_size=512, # 滑动窗口大小 |
| 81 | + tie_word_embeddings=True, |
| 82 | + tensor_parallel_degree=1, # 模型并行度 |
| 83 | + **kwargs |
| 84 | + ): |
| 85 | + super().__init__(pad_token_id=pad_token_id, **kwargs) |
| 86 | + self.vocab_size = vocab_size |
| 87 | + self.hidden_size = hidden_size |
| 88 | + self.num_hidden_layers = num_hidden_layers |
| 89 | + self.num_attention_heads = num_attention_heads |
| 90 | + self.hidden_act = hidden_act |
| 91 | + self.intermediate_size = intermediate_size |
| 92 | + self.hidden_dropout_prob = hidden_dropout_prob |
| 93 | + self.attention_probs_dropout_prob = attention_probs_dropout_prob |
| 94 | + self.max_position_embeddings = max_position_embeddings |
| 95 | + self.type_vocab_size = type_vocab_size |
| 96 | + self.initializer_range = initializer_range |
| 97 | + self.layer_norm_eps = layer_norm_eps |
| 98 | + self.sliding_window_size = sliding_window_size |
| 99 | + self.tie_word_embeddings = tie_word_embeddings |
| 100 | + self.tensor_parallel_degree = tensor_parallel_degree |
| 101 | +``` |
| 102 | + |
| 103 | +## 2. 实现旋转位置编码 (RoPE) |
| 104 | +在 `paddlenlp/transformers/modernbert/modeling.py` 中,需要实现旋转位置编码: |
| 105 | + |
| 106 | +```python |
| 107 | +def apply_rotary_position_embeddings(q, k, cos, sin, position_ids): |
| 108 | + # 实现旋转位置编码的逻辑 |
| 109 | + # 参考 HuggingFace transformers 中 ModernBert 的实现 |
| 110 | +``` |
| 111 | + |
| 112 | +## 3. 实现 GeGLU 激活函数 |
| 113 | +在 `paddlenlp/transformers/modernbert/modeling.py` 中,实现 GeGLU 激活函数: |
| 114 | + |
| 115 | +```python |
| 116 | +def geglu(x, gate): |
| 117 | + # 实现 GeGLU 激活函数 |
| 118 | + # GeGLU(x, gate) = x * GELU(gate) |
| 119 | +``` |
| 120 | + |
| 121 | +## 4. 实现滑动窗口注意力 |
| 122 | +在注意力机制中实现滑动窗口注意力,以高效处理长序列: |
| 123 | + |
| 124 | +```python |
| 125 | +def sliding_window_attention(query, key, value, window_size, attention_mask=None): |
| 126 | + # 实现滑动窗口注意力 |
| 127 | + # 限制每个 token 只能与其左右 window_size 范围内的 token 进行注意力计算 |
| 128 | +``` |
| 129 | + |
| 130 | +## 5. 实现参数转换方法 |
| 131 | +在 `paddlenlp/transformers/modernbert/modeling.py` 中,实现 _get_name_mappings 方法用于参数转换: |
| 132 | + |
| 133 | +```python |
| 134 | +@classmethod |
| 135 | +def _get_name_mappings(cls, config: ModernBertConfig) -> list[StateDictNameMapping]: |
| 136 | + # 实现参数转换方法,用于将 HuggingFace 权重映射到 PaddleNLP 格式 |
| 137 | + # 参考 LLaMA 代码实现:https://github.com/PaddlePaddle/PaddleNLP/blob/bfd053db0897943f5d4d116dde755dbf21d18b23/paddlenlp/transformers/llama/modeling.py#L1334-L1366 |
| 138 | + mappings = [] |
| 139 | + model_mappings = [] |
| 140 | + |
| 141 | + # 添加基本参数映射 |
| 142 | + |
| 143 | + # 添加层级参数映射 |
| 144 | + |
| 145 | + # 初始化名称映射 |
| 146 | + |
| 147 | + return mappings |
| 148 | +``` |
| 149 | + |
| 150 | +## 6. 模型并行实现 |
| 151 | +在 `paddlenlp/transformers/modernbert/modeling.py` 中,实现模型并行相关代码: |
| 152 | + |
| 153 | +```python |
| 154 | +# 在相关模块中添加模型并行支持 |
| 155 | +if config.tensor_parallel_degree > 1: |
| 156 | + # 实现张量并行化处理 |
| 157 | + # 实现权重分片 |
| 158 | + # 实现通信原语封装 |
| 159 | + # 参考 LLaMA 代码实现:https://github.com/PaddlePaddle/PaddleNLP/blob/bfd053db0897943f5d4d116dde755dbf21d18b23/paddlenlp/transformers/llama/modeling.py#L775-L813 |
| 160 | +``` |
| 161 | + |
| 162 | +**注意:优先完成单卡模型组网,后续再支持模型并行** |
| 163 | + |
| 164 | +# 六、测试验收的考量 |
| 165 | +ModernBert 模型的实现需要通过以下测试验证: |
| 166 | + |
| 167 | +1. 精度对齐测试: |
| 168 | + * 使用 [PaDiff](https://github.com/PaddlePaddle/PaDiff) 工具验证与 PyTorch 实现的对齐程度 |
| 169 | + * 在 fp32 下误差需小于 1e-5 |
| 170 | + * 在 bf16 下误差需小于 1e-3 |
| 171 | + |
| 172 | +2. 单元测试: |
| 173 | + * 为 ModernBert 添加单元测试,验证各个组件的功能正确性 |
| 174 | + * 测试 RoPE、GeGLU、滑动窗口注意力等关键功能 |
| 175 | + * 测试 _get_name_mappings 方法的正确性 |
| 176 | + |
| 177 | +3. 功能测试: |
| 178 | + * 验证模型能够正确处理不同长度的输入序列 |
| 179 | + * 验证模型在下游任务(如文本分类、序列标注等)上的性能 |
| 180 | + |
| 181 | +4. 兼容性测试: |
| 182 | + * 验证与 HuggingFace 预训练权重的兼容性 |
| 183 | + * 验证与 PaddleNLP 生态的兼容性 |
| 184 | + |
| 185 | +5. 模型并行测试(后续阶段): |
| 186 | + * 验证模型并行功能的正确性 |
| 187 | + * 验证多卡训练和推理的性能 |
| 188 | + |
| 189 | +# 七、可行性分析和排期规划 |
| 190 | +基于现有的 BERT 实现和 HuggingFace 的 ModernBert 实现,在 PaddleNLP 中复现 ModernBert 是可行的。实现计划如下: |
| 191 | + |
| 192 | +1. 第一阶段(1周): |
| 193 | + * 分析 HuggingFace ModernBert 代码结构 |
| 194 | + * 设计 PaddleNLP 中的 ModernBert 实现方案 |
| 195 | + * 实现 ModernBertConfig 类 |
| 196 | + |
| 197 | +2. 第二阶段(2周): |
| 198 | + * 实现 ModernBert 核心组件:RoPE、GeGLU、滑动窗口注意力 |
| 199 | + * 实现 ModernBertModel 类及相关的下游任务模型类 |
| 200 | + * 实现 _get_name_mappings 方法 |
| 201 | + |
| 202 | +3. 第三阶段(1周): |
| 203 | + * 实现与 HuggingFace 权重兼容的转换函数 |
| 204 | + * 进行精度对齐验证 |
| 205 | + * 完成单卡模型功能测试 |
| 206 | + |
| 207 | +4. 第四阶段(1周): |
| 208 | + * 编写单元测试和文档 |
| 209 | + * 修复问题和优化性能 |
| 210 | + * 规划模型并行实现方案 |
| 211 | + |
| 212 | +5. 第五阶段(2周,后续阶段): |
| 213 | + * 实现模型并行相关代码 |
| 214 | + * 进行模型并行功能测试 |
| 215 | + * 完善模型并行文档 |
| 216 | + |
| 217 | +# 八、影响面 |
| 218 | +ModernBert 的实现将对 PaddleNLP 产生以下影响: |
| 219 | + |
| 220 | +1. 增强 PaddleNLP 处理长文本的能力,支持最大 8192 长度的序列 |
| 221 | +2. 提供更高效的预训练模型选择,在相同参数量下提供更好的性能 |
| 222 | +3. 完善 PaddleNLP 的模型生态,与业界最新进展保持同步 |
| 223 | +4. 为后续实现更多基于旋转位置编码和 GeGLU 的模型奠定基础 |
| 224 | +5. 增强 PaddleNLP 的分布式训练能力,支持大规模模型训练 |
| 225 | + |
| 226 | +# 名词解释 |
| 227 | +* RoPE (Rotary Position Embedding): 旋转位置编码,一种能更好处理相对位置信息的位置编码方法 |
| 228 | +* GeGLU: Gated GELU 激活函数,相比传统的 GELU 提供更好的性能 |
| 229 | +* 滑动窗口注意力: 一种注意力计算优化方式,限制每个 token 只关注局部上下文,提高长序列处理效率 |
| 230 | +* 模型并行: 将模型参数分布在多个设备上进行训练和推理的技术 |
| 231 | + |
| 232 | +# 附件及参考资料 |
| 233 | +1. [ModernBert 论文](https://arxiv.org/abs/2412.13663) |
| 234 | +2. [HuggingFace ModernBert 实现](https://github.com/huggingface/transformers/tree/main/src/transformers/models/modernbert) |
| 235 | +3. [PaddleNLP 模型对齐指南](https://paddlenlp.readthedocs.io/zh/latest/community/contribute_models/model_alignment.html) |
| 236 | +4. [LLaMA _get_name_mappings 方法实现](https://github.com/PaddlePaddle/PaddleNLP/blob/bfd053db0897943f5d4d116dde755dbf21d18b23/paddlenlp/transformers/llama/modeling.py#L1334-L1366) |
| 237 | +5. [LLaMA 模型并行实现](https://github.com/PaddlePaddle/PaddleNLP/blob/bfd053db0897943f5d4d116dde755dbf21d18b23/paddlenlp/transformers/llama/modeling.py#L775-L813) |
0 commit comments