Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
268 changes: 268 additions & 0 deletions paddlenlp/transformers/convert/doc/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,268 @@


## 使用方法:

### 1构建modular__**.py文件

#### 1.1分析基础模型

​ 在开始构建 `modular_xxx.py`文件之前,首先需要深入分析要基于的**基础模型**。这个基础模型通常是 paddle/ Transformers 库中已有的成熟模型。

##### 1.1.1 选择合适的基础模型

**选择标准:**

- **架构相似性**:新模型与基础模型的架构应尽可能相似
- **任务类型**:基础模型应支持相同的任务(如文本生成、分类等)
- **代码质量**:选择代码结构清晰、文档完善的模型

**常见基础模型选择:**

```
# 基于BERT架构的模型
基础模型:BertModel, RobertaModel, DebertaModel

# 基于GPT架构的模型
基础模型:GPT2Model, LlamaModel, GPTNeoXModel

# 基于Encoder-Decoder架构的模型
基础模型:T5Model, BartModel, PegasusModel
```

##### 1.1.2 分析基础模型的关键组件

对于选定的基础模型,需要分析其核心组件:

###### **1. 配置文件 (`configuration_xxx.py`)**

```
# 分析配置参数
# 关注:hidden_size, num_attention_heads, num_hidden_layers,
# vocab_size, max_position_embeddings 等关键参数
```

###### **2. 模型架构 (`modeling_xxx.py`)**

```
# 分析模型类结构
import inspect
from transformers import BertModel

# 查看类的方法和属性
print(inspect.getmembers(BertModel, predicate=inspect.ismethod))
# 重点关注:__init__, forward, 以及其他关键方法
```

##### 1.1.3 识别需要修改的部分

基于分析结果,确定哪些部分需要自定义:

| 组件 | 是否需要修改 | 修改原因 |
| :--------------- | :----------- | :------------------------- |
| **配置参数** | ✅ 通常需要 | 调整模型尺寸、注意力头数等 |
| **前向传播逻辑** | ✅ 通常需要 | 适配新的架构变化 |
| **注意力机制** | ⚠️ 可能需要 | 如果使用不同的注意力机制 |
| **位置编码** | ⚠️ 可能需要 | 如果使用不同的位置编码方案 |
| **输出头** | ✅ 通常需要 | 适配不同的任务需求 |
| **初始化方法** | ⚠️ 可能需要 | 如果使用不同的初始化策略 |

#### 1.2编写modular文件结构

​ 在完成基础模型分析后,您需要创建一个结构清晰、符合规范的 `modular_xxx.py`文件。这个文件是代码生成器的模板,其结构直接决定了最终输出的 `modeling_xxx.py`文件的质量。

##### 1.2.1 文件基本结构

一个标准的 `modular_xxx.py`文件应包含以下部分,按顺序排列:

```
# coding=utf-8
# 版权声明 (可选)
""" 新模型的简要文档字符串 (可选) """

# 1. 导入部分
from typing import Optional, Tuple, Union

import torch
import torch.utils.checkpoint
from torch import nn
from torch.nn import CrossEntropyLoss

# 从基础模型导入必要的组件
from transformers.models.llama.modeling_llama import (
LlamaConfig,
LlamaModel,
LlamaForCausalLM,
LlamaDecoderLayer,
# ... 其他需要继承或引用的组件
)
from transformers import PreTrainedModel, PreTrainedTokenizerBase
from transformers.utils import logging


logger = logging.get_logger(__name__)

# 3. 注意力机制 (如果需要自定义)
class MyNewAttention(nn.Module):
"""自定义注意力机制"""
def __init__(self, config: MyNewModelConfig):
super().__init__()
# 实现自定义注意力逻辑
pass

def forward(self, hidden_states, attention_mask=None):
# 实现前向传播
pass


# 4. 解码器层 (如果需要修改层结构)
class MyNewDecoderLayer(LlamaDecoderLayer):
"""
自定义解码器层,继承自LlamaDecoderLayer
重写需要修改的方法
"""
def __init__(self, config: MyNewModelConfig):
super().__init__(config)
# 替换或修改注意力机制
if config.use_custom_attention:
self.self_attn = MyNewAttention(config)

def forward(self, hidden_states, attention_mask=None):
# 可以完全重写或部分修改父类逻辑
if self.config.use_custom_attention:
# 自定义逻辑
return self._custom_forward(hidden_states, attention_mask)
else:
# 回退到父类逻辑
return super().forward(hidden_states, attention_mask)

def _custom_forward(self, hidden_states, attention_mask):
"""自定义前向传播实现"""
pass


# 5. 主模型类
class MyNewModel(LlamaModel):
"""
我的新模型主类,继承自LlamaModel
通常需要重写 __init__ 和 forward 方法
"""
def __init__(self, config: MyNewModelConfig):
super().__init__(config)
# 替换解码器层
self.layers = nn.ModuleList([
MyNewDecoderLayer(config) for _ in range(config.num_hidden_layers)
])
# 其他自定义初始化
self.custom_layer = nn.Linear(config.hidden_size, config.custom_param)

def forward(self, input_ids, attention_mask=None):
# 调用父类获取基础输出
super().forward(input_ids, attention_mask)

# 添加自定义处理
hidden_states = outputs[0]
custom_output = self.custom_layer(hidden_states)

# 返回修改后的输出
return (custom_output,) + outputs[1:]


# 6. 任务特定模型 (如用于因果语言建模)
class MyNewForCausalLM(LlamaForCausalLM):
"""
用于因果语言建模的我的新模型
"""
def __init__(self, config: MyNewModelConfig):
super().__init__(config)
# 替换主模型
self.model = MyNewModel(config)

def forward(self, input_ids, attention_mask=None, labels=None):
# 可以完全重写或扩展父类逻辑
outputs = self.model(input_ids, attention_mask=attention_mask)

# 计算损失等
loss = None
if labels is not None:
# 计算损失逻辑
pass

return {"loss": loss, "logits": outputs[0]}


# 8. 更新 __all__ 列表,声明哪些类应该被导出
__all__ = [
"MyNewModelConfig",
"MyNewModel",
"MyNewForCausalLM",
"MyNewDecoderLayer",
]
```

##### 1.2.2 关键编写原则

**清晰的继承关系**

```
# ✅ 正确:明确继承关系
class MyNewModel(LlamaModel):
pass

# ❌ 避免:直接继承过于通用的基类
class MyNewModel(PreTrainedModel):
pass # 这会导致需要实现大量抽象方法
```

**最小化重写**

```
# ✅ 正确:只重写需要修改的方法
class MyNewDecoderLayer(LlamaDecoderLayer):
def __init__(self, config):
super().__init__(config) # 先调用父类初始化
# 只修改需要定制的部分
if config.use_custom_attention:
self.self_attn = CustomAttention(config)

# ❌ 避免:完全重写整个类,除非必要
```

**保持接口一致性**:

```
def forward(self, input_ids, attention_mask=None, **kwargs):
# 处理自定义逻辑
result = custom_processing(input_ids)
# 调用父类实现剩余逻辑
super().forward(result, attention_mask, **kwargs)
```

**充分利用现有组件**:

```
# ✅ 正确:复用基础模型的组件
from transformers.models.llama.modeling_llama import (
LlamaRMSNorm,
LlamaRotaryEmbedding,
apply_rotary_pos_emb,
)
```

### 2.**执行转换命令**

通过一个用户主脚本main来驱动整个流程。其标准使用方式如下:

```
#自动查找各个模型文件下的modular__**.py模块化构建代码,执行转换生成modeling__***.py文件
python main.py
```

### **自动化处理流水线**

<img src="../images/process.png" style="zoom:33%;" />

### 最终输出

最终,在模型对应的目录下(如 `src/transformers/models/qwen2/`)会生成目标文件:

- **`modeling_qwen2.py`**:**这是唯一的输出文件,也是最终成果。** 它包含了:**模型架构**(如 `Qwen2Model`, `Qwen2ForCausalLM`)**内联的配置类**(如 `Qwen2Config`)**所有相关的函数、工具类和常量****正确的导入语句**(只导入标准库或 transformers 的通用组件)**文件顶部的警告注释**:明确告知开发者此文件为自动生成,不可手动编辑。
60 changes: 60 additions & 0 deletions paddlenlp/transformers/convert/doc/llama_analysis.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
以下是针对Llama模型架构中各组件的功能解析,按模块分类说明其核心作用:

------

### **核心工具函数**

| 函数/常量 | 作用 | 与Qwen2的差异 |
| :----------------------------: | :-----------------------------------: | :--------------------: |
| `swiglu` | 实现SwiGLU激活函数:`x * silu(gate)` | Qwen2使用标准GLU |
| `rms_norm_fused` | 启用融合的RMSNorm计算(CUDA优化) | 实现相同但配置参数不同 |
| `__all__` | 定义模块的公开接口 | - |
| `_get_interleave` | 生成交错注意力头索引(用于长序列) | Llama特有 |
| `build_alibi_tensor` | 构建ALiBi位置偏置张量(相对位置编码) | Qwen2未使用 |
| `get_triangle_upper_mask` | 生成因果上三角掩码 | 实现逻辑相同 |
| `assign_kv_heads` | 分配KV头的索引(支持GQA/MQA) | - |
| `parallel_matmul` | 并行矩阵乘法(张量并行) | - |
| `scaled_dot_product_attention` | 核心注意力计算 | Llama支持更多掩码类型 |
| `_make_causal_mask` | 动态生成因果掩码(考虑padding) | Qwen2更简化 |

### **归一化层**

| 类/函数 | 作用 | 差异点 |
| :------------: | :-------------------: | :-------------: |
| `LlamaRMSNorm` | 带融合优化的RMS归一化 | 与Qwen2实现相同 |

### **位置编码(核心差异)**

| 类 | 作用 | 特性 |
| :-------------------------------------: | :------------------------: | :---------------: |
| `LlamaRotaryEmbedding` | 基础RoPE实现 | - |
| `LlamaLinearScalingRotaryEmbedding` | 线性缩放RoPE(扩展上下文) | Qwen2无此变体 |
| `LlamaNTKScalingRotaryEmbedding` | NTK-aware缩放RoPE | 动态调整高频/低频 |
| `LlamaDynamicNTKScalingRotaryEmbedding` | 动态NTK缩放(训练自适应) | Llama特有 |
| `Llama3RotaryEmbedding` | Llama3专用RoPE | 改进的旋转策略 |

### **前馈网络**

| 类 | 作用 | 差异 |
| :--------: | :-----------------: | :------------: |
| `LlamaMLP` | 使用SwiGLU的门控FFN | Qwen2用普通GLU |

### **注意力机制**

| 类 | 核心改进 | 说明 |
| :-----------------: | :----------------------------------------: | :------------: |
| `LlamaAttention` | - 多版本RoPE支持 - ALiBi融合 - 动态NTK缩放 | 比Qwen2更复杂 |
| `LlamaDecoderLayer` | 深度优化层实现 | 支持梯度检查点 |

### **预训练基础**

| 类 | 关键功能 | 扩展性 |
| :--------------------: | :--------------------------: | :-----------: |
| `LlamaPretrainedModel` | - 多设备加载 - FLOPs计算工具 | 比Qwen2更完善 |

### **任务模块**

| 类 | 用途 | 特色 |
| :----------------: | :------------: | :---------------: |
| `LlamaForCausalLM` | 语言建模 | 支持静态图导出 |
| `ConcatMaskedLoss` | 多任务损失合并 | 处理padding的梯度 |
Binary file added paddlenlp/transformers/convert/doc/process.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading