PaddleNLP Torch2Paddle 权重转换实战指南:从 PyTorch 权重一键迁移到 Paddle

  • 人工智能
  • 大模型
  • NLP
  • 深度学习
  • 预训练
  • 微调
  • RLHF
  • 模型量化

【免费下载链接】PaddleNLP

Easy-to-use and powerful LLM and SLM library with awesome model zoo.

项目地址: https://gitcode.com/gh_mirrors/pa/PaddleNLP
点击查看 免费下载

导读

本文讲解 PaddleNLP 提供的 Torch2Paddle 权重自动转换能力:只需在 from_pretrained 中开启一个参数,即可把 PyTorch 格式的模型权重(含 Llama、Qwen、Bloom、GPT 等主流系列)自动转换为 Paddle 权重并保存到本地。读完本文,你将掌握单文件权重的一键转换方法、分片权重(sharded checkpoint)的合并预处理流程、底层转换原理(名称映射 + 张量搬运),以及转换过程中的常见注意事项。

一、一键转换:convert_from_torch=True

PaddleNLP 在 AutoModelForCausalLM(以及各个 PretrainedModel)的 from_pretrained 接口中内置了权重转换能力,核心代码如下:

from paddlenlp.transformers import AutoModelForCausalLM

AutoModelForCausalLM.from_pretrained("/path/to/pytorch/model", convert_from_torch=True, dtype="float16")

执行上述代码后,PaddleNLP 会自动完成以下流程:

  1. 读取指定目录下的 PyTorch 权重文件(pytorch_model.bin)与 config.json;
  2. 依据模型配置解析"PyTorch 参数名 → Paddle 参数名"的映射关系;
  3. 将每个张量从 torch.Tensor 搬运为 paddle.Tensor(必要时做转置、合并/拆分等变换);
  4. 用转换后的权重初始化 Paddle 模型,并把转换结果保存回 /path/to/pytorch/model 目录,生成 Paddle 格式权重。

关键参数说明

参数说明取值
convert_from_torch是否从 PyTorch 权重自动转换为 Paddle 权重True / False,默认 False(ModelScope/HF Hub 场景下默认自动开启,见下文)
dtype转换后权重的实际数据类型通常为 float16、bfloat16、float32

dtype 说明:dtype 指定的是转换后权重的真实数据类型,而非仅表示加载时的临时精度。常见用法是 dtype="float16"(半精度推理/训练)或 dtype="bfloat16"(BF16 训练)。底层实现中,dtype 会被写入 config.dtype,并通过 dtype_guard(dtype) 约束模型初始化与权重落盘的精度(见 model_utils.py)。

从源码看转换的完整调用链

转换逻辑集中在 paddlenlp/transformers/model_utils.py 的 from_pretrained 中,关键步骤:

  • 默认值推导:当 convert_from_torch 未显式指定时,若环境变量 from_modelscope 开启,或 from_hf_hub=True,则默认置为 True——也就是说从 ModelScope / Hugging Face Hub 直接拉取权重时,默认就会把 Torch 权重转换为 Paddle 权重(model_utils.py)。普通本地路径加载时默认 False。
  • 能力检查:convert_from_torch = cls.support_conversion(config) and convert_from_torch——先通过 support_conversion 判断当前模型类是否实现了名称映射(未实现 _get_name_mappings 的模型会抛 NotImplementedError 并被判定为不支持,见 conversion_utils.py)。
  • 权重文件解析:_resolve_model_file_path 负责定位 pytorch_model.bin、pytorch_model.index.json、model.safetensors 等候选文件(model_utils.py)。
  • 执行转换:当文件是 Torch 权重(以 pytorch_model.bin、pytorch_model.index.json、model.safetensors、model.safetensors.index.json 结尾)时,调用模型类的 convert() 完成张量级转换,并把结果写入权重所在目录(model_utils.py)。

ConversionMixin.convert() 是转换的核心实现(conversion_utils.py):

@classmethod
def convert(cls, weight_file: str, config: PretrainedConfig, cache_dir: str) -> None:
    # 1. 解析当前模型的 name-mapping 信息
    name_mappings = cls._get_name_mappings(config)
    # 2. 加载 pytorch 权重(.index.json 时自动合并分片)
    if weight_file.endswith(".index.json"):
        ...
        state_dict = {}
        for file in sorted(files):
            sub_state_dict = load_torch(os.path.join(os.path.dirname(weight_file), file))
            state_dict.update(sub_state_dict)
    else:
        state_dict = load_torch(weight_file)
    # 3. 按映射逐键转换
    for name_mapping in name_mappings:
        if name_mapping.source_name not in state_dict:
            logger.warning(f"key<{name_mapping.source_name}> not in the pytorch weight file.")
            continue
        state_dict[name_mapping.target_name] = name_mapping.run(state_dict, name_mapping.source_name)
    ...
    return state_dict

张量是如何从 torch 变成 paddle 的

load_torch(paddlenlp/utils/serialization.py)负责把 Torch 权重读取为 Paddle 张量:

  • 对 pytorch_model.bin / pytorch_model-* 文件:torch.load(..., map_location="cpu") 后,通过 DLPack(torch.utils.dlpack.to_dlpack + paddle.utils.dlpack.from_dlpack) 实现零拷贝级的高效张量搬运,无需先落盘 numpy;
  • 对 model.safetensors:直接用 safetensors 反序列化,按 dtype 构造 paddle.Tensor(BF16 数据会被显式转为 bfloat16 类型)。

由此可见,转换过程全程在 CPU 内存中完成张量形态变换,然后再以目标 dtype 写盘,因此转换时机的内存占用与原始权重规模基本相当。

二、分片权重合并:先把 sharded checkpoint 合成单文件

当前 PaddleNLP 的 Torch2Paddle 转换只支持单个权重文件 pytorch_model.bin。 当 HF 权重以分片形式(pytorch_model-00001-of-00002.bin + pytorch_model.index.json)存在时,需要先合并。

从源码看,convert() 其实对 .index.json 也做了兼容(会自动收集同目录下 pytorch_model-* 分片并合并加载),但作为稳妥的通用做法,仍建议按官方教程先手动合并,以便在转换前获得一份干净的单文件权重,也便于自行校验。

官方提供的合并脚本如下:

import torch, os

state_dict = {}

files = [file for file in os.list("./path/to/pytorch/weight") if file.startswith("pytorch_model-")]

for file in files:
    state_dict.update(torch.load(file))

torch.save(state_dict, "pytorch_model.bin")

脚本要点

  • 只收集 pytorch_model- 前缀的分片文件(如 pytorch_model-00001-of-00002.bin、pytorch_model-00002-of-00002.bin),不会误收 pytorch_model.bin 本身、config.json 或 optimizer 状态;
  • torch.load 默认在 CPU 上加载(脚本未传 map_location,请确保分片文件由标准 torch.save 产生);
  • 合并结果 pytorch_model.bin 会保存在当前工作目录下,如需放进模型目录,请自行调整输出路径;
  • 合并后建议顺带确认 config.json、tokenizer 相关文件与 pytorch_model.bin 位于同一目录,转换时需要一并读取。

更通用的验证流程(可选)

合并完成后,可以先用下面的片段快速核对参数总量与转换结果是否自洽:

import torch

state_dict = torch.load("pytorch_model.bin", map_location="cpu")
print(f"tensor 数量: {len(state_dict)}")
print(f"总参数量: {sum(v.numel() for v in state_dict.values()) / 1e9:.2f}B")

若分片来自 model.safetensors,也可以先用官方 safetensors 工具合并后再导出为 pytorch_model.bin,PaddleNLP 的转换入口对两类来源均做了兼容(见上文 load_torch 的实现)。

三、支持的模型列表

下表为当前仓库中支持 Torch → Paddle 权重自动转换的模型系列(对应各模型目录下 modeling.py 中的 _get_name_mappings 实现):

模型是否支持
AlBert✅
Bart✅
Bert✅
Bloom✅
Clip✅
DistilBert✅
Electra✅
ErnieCode✅
GLM✅
Gpt✅
Llama✅
Mt5✅
Opt✅
Qwen✅
Roberta✅
Roformer✅
RW✅
T5✅

列表以当前仓库 docs/en/llm/docs/torch2paddle.md 与 docs/zh/llm/docs/torch2paddle.md 为准。表中未列出的模型不代表一定无法转换,可通过 support_conversion 机制运行时判定;但不要假定未列出模型一定支持,使用前建议以实际报错或源码为准。

四、转换原理:StateDictNameMapping 与参数名映射

Torch2Paddle 的本质是 "参数名映射 + 张量变换"。每个支持转换的模型类都通过 _get_name_mappings 声明一张映射表,条目格式为:

[PyTorch 参数名, Paddle 参数名, 变换函数]

其中 StateDictNameMapping(定义于 conversion_utils.py)负责在 run() 时按需执行 transpose(转置)、切分/拼接等操作。以 Llama 为例,其映射定义在 paddlenlp/transformers/llama/modeling.py:

@classmethod
def _get_name_mappings(cls, config: LlamaConfig) -> list[StateDictNameMapping]:
    model_mappings = [
        ["embed_tokens.weight"],          # 同名直通
        ["norm.weight"],
    ]
    for layer_index in range(config.num_hidden_layers):
        layer_mappings = [
            [f"layers.{layer_index}.self_attn.q_proj.weight", None, "transpose"],
            [f"layers.{layer_index}.self_attn.k_proj.weight", None, "transpose"],
            [f"layers.{layer_index}.self_attn.v_proj.weight", None, "transpose"],
            [f"layers.{layer_index}.self_attn.o_proj.weight", None, "transpose"],
            [f"layers.{layer_index}.self_attn.rotary_emb.inv_freq"],
            [f"layers.{layer_index}.mlp.gate_proj.weight", None, "transpose"],
            [f"layers.{layer_index}.mlp.down_proj.weight", None, "transpose"],
            [f"layers.{layer_index}.mlp.up_proj.weight", None, "transpose"],
            [f"layers.{layer_index}.input_layernorm.weight"],
            [f"layers.{layer_index}.post_attention_layernorm.weight"],
        ]
        model_mappings.extend(layer_mappings)
    init_name_mappings(mappings=model_mappings)
    # 带 "LlamaModel" 前缀的架构(如 LlamaForCausalLM)需要补前缀
    if "LlamaModel" not in config.architectures:
        for mapping in model_mappings:
            mapping[0] = "model." + mapping[0]
            mapping[1] = "llama." + mapping[1]
        if not config.tie_word_embeddings:
            model_mappings.append(["lm_head.weight", "lm_head.weight", "transpose"])
    return [StateDictNameMapping(*mapping, index=index) for index, mapping in enumerate(model_mappings)]

从中可以读出的关键信息:

  1. transpose 是高频变换:PyTorch 的 nn.Linear 权重布局为 [out, in],而 Paddle 的 Linear 布局为 [in, out],因此几乎所有 *_proj.weight 都需要转置,这也是转换后数值完全一致、但必须做变换的根本原因;
  2. 前缀自动推导:同一个权重文件既可能来自 LlamaModel(无前缀),也可能来自 LlamaForCausalLM(带 model. / llama. 前缀),映射表通过 config.architectures 自动判断并补前缀;
  3. 映射表可做量化扩展:在 _get_tensor_parallel_mappings 等流程中,add_quant_mapping 还会把 weight 替换为 quant_weight / quant_scale 以适配量化模型(conversion_utils.py)。

五、常见问题与排查思路

  • 提示 "not support convert torch model" 或 support_conversion 返回 False:说明当前模型类未实现 _get_name_mappings,即该系列暂不在支持列表内(model_utils.py 会给出 Please set convert_from_torch=True ... 的引导提示)。请参考支持列表选用模型,或等待上游实现。
  • 日志出现 "key not in the pytorch weight file" :个别参数在源权重中缺失(如 tie word embeddings 时 lm_head.weight 不在文件中),转换会跳过该键并用 Paddle 侧默认初始化,属于正常现象;若缺失的是关键层,请核对权重来源是否与 config.json 匹配。
  • 转换后存在 "N tensors not initialized" 警告:说明部分源参数名未命中任何映射,通常是权重版本与当前 PaddleNLP 模型结构不一致导致,建议核对 PaddleNLP 版本与权重对应模型版本。
  • 加载后数值异常/NaN:优先检查 dtype 是否与源权重一致,以及分片是否合并完整(漏分片会导致 state_dict.update 覆盖缺失,参数量偏小)。
  • torch 环境问题:转换依赖 torch.load 读取 .bin 权重,请确保安装了与权重序列化版本兼容的 PyTorch;PaddleNLP 已通过 paddlenlp/utils/import_utils.py 对 torch 可用性做了运行时探测,缺失时会给出明确提示。

六、进阶:把转换能力接入迁移工作流

Torch2Paddle 转换通常出现在两条工作流中:

  1. 从 Hugging Face / ModelScope 直接加载:from_pretrained("model-name", from_hf_hub=True) 或开启 from_modelscope 环境变量时,convert_from_torch 默认为 True,下载的 Torch 权重会被自动转换并缓存为 Paddle 权重,后续加载直接复用,无需重复转换(model_utils.py)。
  2. 本地权重迁移 + 后续微调/推理:按本文第一节代码将 PyTorch 权重转换为 Paddle 权重保存在本地目录,之后 AutoModelForCausalLM.from_pretrained("/path/to/paddle/model") 即可直接加载,供 llm/run_finetune.py、llm/run_pretrain.py、llm/run_quantization.py 等训练/量化脚本使用。

配合仓库中的 llm/docs/finetune.md、llm/docs/pretrain.md 等教程,可以构建一套完整的 "PyTorch 权重 → Paddle 权重 → 微调/推理" 迁移链路,将社区生态中的大量预训练模型无缝接入 PaddleNLP。

  • 人工智能
  • 大模型
  • NLP
  • 深度学习
  • 预训练
  • 微调
  • RLHF
  • 模型量化

【免费下载链接】PaddleNLP

Easy-to-use and powerful LLM and SLM library with awesome model zoo.

项目地址: https://gitcode.com/gh_mirrors/pa/PaddleNLP
点击查看 免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值