【Megatron-LM】将模型转换为 Hugging Face 的 Transformers 格式
在使用 Megatron-LM 训练完大规模语言模型后,将 Megatron-LM 模型转换为 Transformers 格式需要合并分布式检查点、转换权重、映射参数名称并生成配置文件。通过提供的脚本和步骤,可以将模型无缝集成到 Hugging Face 生态系统中,用于评估、微调或部署。Megatron-LM 使用自己的模型存储格式(通常是 PyTorch 的检查点文件 .pt),其权重分布在分
在使用 Megatron-LM 训练完大规模语言模型后,为了将其转换为 Hugging Face Transformers 格式,以便利用 Transformers 库进行评估、推理或部署到生产环境,需要进行模型权重和配置的转换。以下是详细的步骤和代码示例,帮助完成从 Megatron-LM 模型到 Transformers 模型的转换。
1. 转换背景
Megatron-LM 使用自己的模型存储格式(通常是 PyTorch 的检查点文件 .pt
),其权重分布在分布式训练的多个 GPU 上(例如张量并行和流水线并行的分片)。Hugging Face Transformers 则使用统一的参数命名和存储格式(通常是 pytorch_model.bin
和 config.json
)。因此,转换过程需要:
- 合并分布式权重:将 Megatron-LM 的分片权重合并为单一模型。
- 映射参数名称:将 Megatron-LM 的参数名称映射到 Transformers 格式。
- 生成配置文件:创建符合 Transformers 要求的模型配置文件。
Megatron-LM 提供了工具脚本(如 tools/checkpoint_converter.py
或社区提供的转换脚本)来简化这一过程。
2. 前提条件
在开始转换之前,确保满足以下条件:
- 已训练的 Megatron-LM 检查点:训练完成后,检查点保存在指定目录(如
--save /checkpoints/gpt_model
)。 - 安装必要的库:
- Hugging Face Transformers:
pip install transformers
- PyTorch:与 Megatron-LM 训练时使用的版本一致。
- Megatron-LM:确保已克隆并配置好 Megatron-LM 仓库。
- Hugging Face Transformers:
- 模型元信息:了解训练时的模型配置(如层数、隐藏维度、注意力头数等),这些信息在转换时需要用到。
- 硬件:单台机器(最好有 GPU)用于运行转换脚本,内存和磁盘空间需足够存储完整模型。
3. 转换步骤
以下是将 Megatron-LM 模型转换为 Transformers 格式的详细步骤:
(1) 准备 Megatron-LM 检查点
- 训练完成后,Megatron-LM 将检查点保存在指定目录(例如
/checkpoints/gpt_model
)。检查点通常包含:- 多个分片文件(如
mp_rank_00/model_optim_rng.pt
),对应张量并行和流水线并行的分片。 - 模型元数据(如
latest_checkpointed_iteration.txt
)。
- 多个分片文件(如
- 确保检查点目录完整,且包含所有分片(根据
--tensor-model-parallel-size
和--pipeline-model-parallel-size
的设置)。
(2) 合并分布式检查点
Megatron-LM 的检查点是分布式的,需要先合并为单个检查点文件。Megatron-LM 提供了 tools/checkpoint_util.py
脚本用于合并。
-
运行合并脚本:
python tools/checkpoint_util.py \ --model-type GPT \ --checkpoint-folder /checkpoints/gpt_model \ --checkpoint-name iter_XXXXXX \ --target-folder /consolidated_checkpoints \ --tensor-model-parallel-size 4 \ --pipeline-model-parallel-size 2
参数说明:
--model-type
:模型类型(通常是GPT
)。--checkpoint-folder
:Megatron-LM 检查点目录。--checkpoint-name
:特定迭代的检查点名称(例如iter_100000
)。--target-folder
:合并后检查点的保存目录。--tensor-model-parallel-size
和--pipeline-model-parallel-size
:与训练时的并行设置一致。
-
输出:
合并后会在/consolidated_checkpoints
中生成单个检查点文件(如consolidated_model.pt
),包含完整的模型权重。
(3) 转换为 Transformers 格式
Megatron-LM 官方仓库或社区提供了脚本(如 tools/convert_checkpoint_to_hf.py
或类似的第三方脚本)来将合并后的检查点转换为 Transformers 格式。例如使用以下命令可以将文件 model_optim_rng.pt 转换为 transformers 支持的 pytorch_model.bin 文件。
# to execute outside the container:
mkdir -p nvidia/megatron-codeparrot-small
# copy the weights from the container
sudo docker cp CONTAINER_ID:/workspace/Megatron-LM/experiments/codeparrot-small/iter_0150000/mp_rank_00/model_optim_rng.pt nvidia/megatron-codeparrot-small
git clone https://github.com/huggingface/transformers.git
git clone https://github.com/NVIDIA/Megatron-LM.git
export PYTHONPATH=Megatron-LM
python transformers/src/transformers/models/megatron_gpt2/convert_megatron_gpt2_checkpoint.py nvidia/megatron-codeparrot-small/model_optim_rng.pt
如果官方脚本不可用,可以使用以下示例脚本。
示例转换脚本
以下是一个自定义的 Python 脚本,用于将 Megatron-LM 的 GPT 模型转换为 Hugging Face Transformers 格式:
import torch
import os
from transformers import GPT2Config, GPT2LMHeadModel
from collections import OrderedDict
def convert_megatron_to_hf(megatron_checkpoint_path, output_dir, num_layers, hidden_size, num_attention_heads, max_position_embeddings):
"""
Convert Megatron-LM GPT checkpoint to Hugging Face Transformers format.
Args:
megatron_checkpoint_path: Path to consolidated Megatron-LM checkpoint (.pt file).
output_dir: Directory to save Hugging Face model and config.
num_layers: Number of transformer layers.
hidden_size: Hidden size of the model.
num_attention_heads: Number of attention heads.
max_position_embeddings: Maximum sequence length.
"""
# Load Megatron-LM checkpoint
print(f"Loading Megatron-LM checkpoint from {megatron_checkpoint_path}")
megatron_state_dict = torch.load(megatron_checkpoint_path, map_location='cpu')['model']
# Initialize Transformers GPT2 config
config = GPT2Config(
n_layer=num_layers,
n_embd=hidden_size,
n_head=num_attention_heads,
n_positions=max_position_embeddings,
vocab_size=50257, # Adjust based on your tokenizer
bos_token_id=50256,
eos_token_id=50256
)
# Initialize Transformers model
hf_model = GPT2LMHeadModel(config)
hf_state_dict = hf_model.state_dict()
# Mapping Megatron-LM parameters to Transformers
mapping = {
'word_embeddings.weight': 'transformer.wte.weight',
'position_embeddings.weight': 'transformer.wpe.weight',
}
for layer_idx in range(num_layers):
mapping.update({
f'layers.{layer_idx}.attention.query_key_value.weight': f'transformer.h.{layer_idx}.attn.c_attn.weight',
f'layers.{layer_idx}.attention.query_key_value.bias': f'transformer.h.{layer_idx}.attn.c_attn.bias',
f'layers.{layer_idx}.attention.dense.weight': f'transformer.h.{layer_idx}.attn.c_proj.weight',
f'layers.{layer_idx}.attention.dense.bias': f'transformer.h.{layer_idx}.attn.c_proj.bias',
f'layers.{layer_idx}.mlp.dense_h_to_4h.weight': f'transformer.h.{layer_idx}.mlp.c_fc.weight',
f'layers.{layer_idx}.mlp.dense_h_to_4h.bias': f'transformer.h.{layer_idx}.mlp.c_fc.bias',
f'layers.{layer_idx}.mlp.dense_4h_to_h.weight': f'transformer.h.{layer_idx}.mlp.c_proj.weight',
f'layers.{layer_idx}.mlp.dense_4h_to_h.bias': f'transformer.h.{layer_idx}.mlp.c_proj.bias',
f'layers.{layer_idx}.input_layernorm.weight': f'transformer.h.{layer_idx}.ln_1.weight',
f'layers.{layer_idx}.input_layernorm.bias': f'transformer.h.{layer_idx}.ln_1.bias',
f'layers.{layer_idx}.post_attention_layernorm.weight': f'transformer.h.{layer_idx}.ln_2.weight',
f'layers.{layer_idx}.post_attention_layernorm.bias': f'transformer.h.{layer_idx}.ln_2.bias',
})
mapping['final_layernorm.weight'] = 'transformer.ln_f.weight'
mapping['final_layernorm.bias'] = 'transformer.ln_f.bias'
mapping['output_layer.weight'] = 'lm_head.weight'
# Convert weights
new_state_dict = OrderedDict()
for megatron_key, hf_key in mapping.items():
if megatron_key in megatron_state_dict:
weight = megatron_state_dict[megatron_key]
# Handle QKV weight splitting for attention layers
if 'query_key_value' in megatron_key:
head_dim = hidden_size // num_attention_heads
qkv_weight = weight.view(3, num_attention_heads, head_dim, hidden_size)
q_weight = qkv_weight[0].reshape(-1, hidden_size)
k_weight = qkv_weight[1].reshape(-1, hidden_size)
v_weight = qkv_weight[2].reshape(-1, hidden_size)
weight = torch.cat([q_weight, k_weight, v_weight], dim=0)
new_state_dict[hf_key] = weight
else:
print(f"Warning: {megatron_key} not found in checkpoint")
# Update Hugging Face model state dict
hf_state_dict.update(new_state_dict)
hf_model.load_state_dict(hf_state_dict)
# Save model and config
os.makedirs(output_dir, exist_ok=True)
hf_model.save_pretrained(output_dir)
config.save_pretrained(output_dir)
print(f"Model and config saved to {output_dir}")
if __name__ == "__main__":
# Example usage
convert_megatron_to_hf(
megatron_checkpoint_path="/consolidated_checkpoints/consolidated_model.pt",
output_dir="/hf_model",
num_layers=12,
hidden_size=768,
num_attention_heads=12,
max_position_embeddings=1024
)
运行脚本
- 保存上述脚本为
convert_megatron_to_hf.py
。 - 根据你的模型配置修改参数:
megatron_checkpoint_path
:合并后的 Megatron-LM 检查点路径。output_dir
:Hugging Face 模型保存目录。num_layers
、hidden_size
、num_attention_heads
、max_position_embeddings
:与训练时一致的模型配置。
- 运行脚本:
python convert_megatron_to_hf.py
输出
转换后,output_dir
(如 /hf_model
)将包含:
pytorch_model.bin
:模型权重。config.json
:模型配置文件。- 可选:
tokenizer.json
(如果需要分词器)。
(4) 添加分词器(可选)
Megatron-LM 通常使用自己的分词器(如 GPT-2 的 BPE)。若需与 Transformers 兼容:
- 复制分词器文件:
将训练时使用的vocab.json
和merges.txt
复制到/hf_model
。 - 加载分词器:
from transformers import GPT2Tokenizer tokenizer = GPT2Tokenizer.from_pretrained("/hf_model")
如果使用其他分词器(如 SentencePiece),需相应调整。
4. 验证转换后的模型
转换完成后,验证模型是否正确加载到 Transformers 中:
(1) 加载模型
from transformers import GPT2LMHeadModel, GPT2Tokenizer
# Load model and tokenizer
model = GPT2LMHeadModel.from_pretrained("/hf_model")
tokenizer = GPT2Tokenizer.from_pretrained("/hf_model")
# Move to GPU if available
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model.to(device)
model.eval()
(2) 测试推理
# Test text generation
input_text = "Hello, how are you?"
inputs = tokenizer(input_text, return_tensors="pt").to(device)
outputs = model.generate(**inputs, max_length=50, num_return_sequences=1)
# Decode output
generated_text = tokenizer.decode(outputs[0], skip_special_tokens=True)
print(generated_text)
如果推理结果符合预期,说明转换成功。
5. 使用 Transformers 进行评估
转换后的模型可以直接用于 Transformers 的评估流程。以下是一些常见任务:
(1) 计算困惑度(Perplexity)
from transformers import GPT2LMHeadModel, GPT2Tokenizer
import torch
def compute_perplexity(model, tokenizer, text):
encodings = tokenizer(text, return_tensors="pt").to(device)
input_ids = encodings.input_ids
with torch.no_grad():
outputs = model(input_ids, labels=input_ids)
loss = outputs.loss
perplexity = torch.exp(loss)
return perplexity.item()
# Example
text = "This is a sample text for perplexity evaluation."
perplexity = compute_perplexity(model, tokenizer, text)
print(f"Perplexity: {perplexity}")
(2) 微调下游任务
使用 Transformers 的 Trainer
API 进行微调:
from transformers import Trainer, TrainingArguments, DataCollatorForLanguageModeling
from datasets import load_dataset
# Load dataset
dataset = load_dataset("wikitext", "wikitext-2-raw-v1")
# Define training arguments
training_args = TrainingArguments(
output_dir="/finetuned_model",
overwrite_output_dir=True,
num_train_epochs=3,
per_device_train_batch_size=4,
save_steps=10_000,
)
# Define data collator
data_collator = DataCollatorForLanguageModeling(tokenizer=tokenizer, mlm=False)
# Initialize Trainer
trainer = Trainer(
model=model,
args=training_args,
data_collator=data_collator,
train_dataset=dataset["train"],
)
# Start fine-tuning
trainer.train()
6. 部署到生产环境
转换后的 Transformers 模型可以通过以下方式部署:
(1) 使用 Transformers 推理
直接使用 pipeline
API 进行快速部署:
from transformers import pipeline
generator = pipeline("text-generation", model="/hf_model", tokenizer="/hf_model", device=0)
output = generator("Hello, how are you?", max_length=50)
print(output)
(2) 优化推理
- 量化:使用
torch.quantization
或optimum
库对模型进行 INT8 量化,减少内存占用。 - ONNX 转换:将模型导出为 ONNX 格式,优化推理速度:
pip install onnx onnxruntime python -m transformers.onnx --model=/hf_model /onnx_model
- Triton 推理服务器:使用 NVIDIA Triton 将模型部署为高性能服务。
(3) API 服务
使用 FastAPI 或 Flask 封装模型为 REST API:
from fastapi import FastAPI
from transformers import pipeline
app = FastAPI()
generator = pipeline("text-generation", model="/hf_model", device=0)
@app.post("/generate")
async def generate_text(prompt: str):
output = generator(prompt, max_length=50)
return {"generated_text": output[0]["generated_text"]}
运行服务:
uvicorn main:app --host 0.0.0.0 --port 8000
7. 常见问题及解决方法
- 权重不匹配:
- 确保 Megatron-LM 和 Transformers 的模型配置(如
num_layers
、hidden_size
)一致。 - 检查参数名称映射是否正确,尤其是 QKV 权重。
- 确保 Megatron-LM 和 Transformers 的模型配置(如
- 内存不足:
- 在 CPU 上运行转换脚本(
map_location='cpu'
)。 - 使用较小的模型或分批加载权重。
- 在 CPU 上运行转换脚本(
- 分词器不兼容:
- 确保使用与训练时相同的分词器文件。
- 如果自定义分词器,需手动调整
tokenizer_config.json
。
- 推理结果异常:
- 检查模型是否在
eval
模式(model.eval()
)。 - 验证输入序列长度是否超过
max_position_embeddings
。
- 检查模型是否在
8. 总结
将 Megatron-LM 模型转换为 Transformers 格式需要合并分布式检查点、转换权重、映射参数名称并生成配置文件。通过提供的脚本和步骤,可以将模型无缝集成到 Hugging Face 生态系统中,用于评估、微调或部署。
更多推荐
所有评论(0)