在使用 Megatron-LM 训练完大规模语言模型后,为了将其转换为 Hugging Face Transformers 格式,以便利用 Transformers 库进行评估、推理或部署到生产环境,需要进行模型权重和配置的转换。以下是详细的步骤和代码示例,帮助完成从 Megatron-LM 模型到 Transformers 模型的转换。


1. 转换背景

Megatron-LM 使用自己的模型存储格式(通常是 PyTorch 的检查点文件 .pt),其权重分布在分布式训练的多个 GPU 上(例如张量并行和流水线并行的分片)。Hugging Face Transformers 则使用统一的参数命名和存储格式(通常是 pytorch_model.binconfig.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 仓库。
  • 模型元信息:了解训练时的模型配置(如层数、隐藏维度、注意力头数等),这些信息在转换时需要用到。
  • 硬件:单台机器(最好有 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 脚本用于合并。

  1. 运行合并脚本

    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:与训练时的并行设置一致。
  2. 输出
    合并后会在 /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
    )
运行脚本
  1. 保存上述脚本为 convert_megatron_to_hf.py
  2. 根据你的模型配置修改参数:
    • megatron_checkpoint_path:合并后的 Megatron-LM 检查点路径。
    • output_dir:Hugging Face 模型保存目录。
    • num_layershidden_sizenum_attention_headsmax_position_embeddings:与训练时一致的模型配置。
  3. 运行脚本:
    python convert_megatron_to_hf.py
    
输出

转换后,output_dir(如 /hf_model)将包含:

  • pytorch_model.bin:模型权重。
  • config.json:模型配置文件。
  • 可选:tokenizer.json(如果需要分词器)。
(4) 添加分词器(可选)

Megatron-LM 通常使用自己的分词器(如 GPT-2 的 BPE)。若需与 Transformers 兼容:

  1. 复制分词器文件
    将训练时使用的 vocab.jsonmerges.txt 复制到 /hf_model
  2. 加载分词器
    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.quantizationoptimum 库对模型进行 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_layershidden_size)一致。
    • 检查参数名称映射是否正确,尤其是 QKV 权重。
  • 内存不足
    • 在 CPU 上运行转换脚本(map_location='cpu')。
    • 使用较小的模型或分批加载权重。
  • 分词器不兼容
    • 确保使用与训练时相同的分词器文件。
    • 如果自定义分词器,需手动调整 tokenizer_config.json
  • 推理结果异常
    • 检查模型是否在 eval 模式(model.eval())。
    • 验证输入序列长度是否超过 max_position_embeddings

8. 总结

将 Megatron-LM 模型转换为 Transformers 格式需要合并分布式检查点、转换权重、映射参数名称并生成配置文件。通过提供的脚本和步骤,可以将模型无缝集成到 Hugging Face 生态系统中,用于评估、微调或部署。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐