news 2026/9/13 15:35:45

Paddle PIR 序列化版本兼容机制:Save/Load 的 Patch 补丁体系与版本号管理实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Paddle PIR 序列化版本兼容机制:Save/Load 的 Patch 补丁体系与版本号管理实战

Paddle PIR 序列化版本兼容机制:Save/Load 的 Patch 补丁体系与版本号管理实战

【免费下载链接】PaddlePArallel Distributed Deep LEarning: Machine Learning Framework from Industrial Practice (『飞桨』核心框架,深度学习&机器学习高性能单机、分布式训练和跨平台部署)项目地址: https://gitcode.com/GitHub_Trending/pa/Paddle

导读

本文面向需要维护 Paddle(飞桨)PIR(Paddle Intermediate Representation)程序序列化/反序列化兼容性的开发者,系统讲解paddle/fluid/pir/serialize_deserialize模块中「patch 补丁 + PIR 版本号」的版本兼容方案。读完本文,你将掌握op_patches/op_pair_patches/attr_patches/type_patches四类补丁 YAML 的完整配置语法(含 Attribute、OpresultAttribute、OpOperand、OpResult 的增删改规则),理解DEVELOP_VERSIONRELEASE_VERSION的迭代流程与发版约束,并能够结合仓库中的 CMake 配置、PatchBuilder源码与单测用例,独立完成新增 patch、升级版本号、验证兼容性的全流程操作。

一、为什么需要版本兼容机制:PIR Save/Load 面临的问题

PIR(Paddle Intermediate Representation)是飞桨新一代基于 MLIR 理念的中间表示,承担算子级 IR 的表示、优化与执行。当Program通过WriteModule序列化落盘、再通过ReadModule反序列化恢复时,存在一个天然矛盾:

  • 模型文件的「编写时代」可能早于运行时的「读取时代」,两者分属不同 Paddle 版本;
  • 算子定义(OpDef)本身会随版本演化——Attribute 的名称、类型可能变化,OpResult 的数量可能增减,甚至某个 Type 类型会被整体废弃替换;
  • 若旧版本写入的模型无法被新版本正确读取,会导致跨版本模型加载失败或语义错误。

为此,PIR 引入了一套以 YAML patch 补丁为核心的版本兼容机制:每次 PIR 定义发生变化时,在patch目录下新增一个顺序编号的 YAML 文件,描述「如何把旧版本写入的结构修补成当前版本可理解的结构」;反序列化时根据模型文件记录的版本号,自动叠加对应区间的补丁,从而让旧模型平滑升级到新版本语义。该机制的完整文档位于 paddle/fluid/pir/serialize_deserialize/patch/Readme.md,本文即以此为主线展开。

二、patch 目录结构与补丁文件组织

补丁文件统一存放在paddle/fluid/pir/serialize_deserialize/patch/目录下,当前仓库中实际存在:

  • 0.yaml4.yaml顺序编号的补丁文件(其中2.yaml为空占位,1.yaml3.yaml4.yaml为实际补丁内容);
  • Readme.md:本机制的使用说明文档;
  • template.h.in:生成patch.h的模板文件。

此外,单测专用的补丁位于 test/cpp/pir/serialize_deserialize/patch/ 目录(含0.yaml1.yaml),配合 save_load_version_compat_test.cc 使用。

补丁文件如何被编译进二进制

补丁 YAML 并非运行时从磁盘读取,而是在构建期被「内嵌」进代码。核心逻辑在 CMakeLists.txt:

file(GLOB_RECURSE YAML_PATCH_FILES "*.yaml") # change pir version when new patches are added add_definitions(-DDEVELOP_VERSION=4) add_definitions(-DRELEASE_VERSION=4) set(TEMPLATE_FILE ${CMAKE_CURRENT_SOURCE_DIR}/patch/template.h.in) set(PATCH_HEADER ${CMAKE_CURRENT_BINARY_DIR}/patch/patch.h) configure_file(${TEMPLATE_FILE} ${PATCH_HEADER} @ONLY) file(WRITE "${PATCH_HEADER}" "#include <map>\n#include <string>\n\n" "const std::map<std::string, std::string> yaml_files = {\n") foreach(PATCH_FILE ${YAML_PATCH_FILES}) get_filename_component(FILENAME "${PATCH_FILE}" NAME_WE) file(READ ${PATCH_FILE} FILE_CONTENT) set(CONTENT "R\"(${FILE_CONTENT})\"") file(APPEND "${PATCH_HEADER}" "{ \"${FILENAME}\", ${CONTENT} },\n") endforeach() file(APPEND "${PATCH_HEADER}" "};\n")

其工作方式为:

  1. GLOB_RECURSE收集patch目录下所有*.yaml
  2. 通过configure_file先按 template.h.in 生成骨架(yaml_filesstd::map<std::string, std::string>);
  3. file(APPEND)把每个 YAML 文件内容以C++ 原始字符串字面量R"(...)")追加进patch.h,键为不含扩展名的文件名(如"0""1");
  4. 最终将patch.h作为源码编入pir_save_load库。

也就是说,yaml_files是一个「文件名(版本号)→ YAML 内容」的静态映射表,运行时补丁内容全部来自这份内嵌数据,路径参数仅在测试场景下用于从外部文件加载(见后文BuildPatchpath参数)。

三、补丁 YAML 配置详解

补丁 YAML 是一个顶层包含四类补丁列表的文档:op_patchesop_pair_patchesattr_patchestype_patches。所有 op 补丁均以op_name为标识,actions为操作列表,每个 action 由action(操作名称)与object(操作对象)构成,部分操作还需type(目标类型)与data(具体数值)。

3.1 op_patches:针对单个 op 的操作

op_patches用于描述对某个算子的 Attribute / OpresultAttribute / OpOperand / OpResult 的增删改,一个 op 可以包含多条 action:

op_patches: - op_name : pd_op.xxx # op_name 为标识 actions: # 补丁操作列表 - action : xxxx # 具体操作名称 object : xxxx # 具体操作对象 - action : xxxx # 可能会对一个 op 进行多次操作 object : xxxx type : xxxx data : xxx - op_name : builtin.xxx # 对另一个 op 进行操作 actions : - action : xxxx - op_name : pd_op.xxx actions : - action : xxxx object : xxx

以仓库中真实的 4.yaml 为例,它集中演示了「Attribute 类型升级」类补丁——把一批 op 的浮点属性从低精度类型统一修改为pir::DoubleAttribute、整型属性修改为pir::Int64Attribute

op_patches: - op_name : pd_op.leaky_relu actions: - action : modify_attr object : negative_slope type : pir::DoubleAttribute - op_name : pd_op.softplus actions: - action : modify_attr object : beta type : pir::DoubleAttribute - action : modify_attr object : threshold type : pir::DoubleAttribute - op_name : onednn_op.fused_softplus actions: - action : modify_attr object : beta type : pir::DoubleAttribute - action : modify_attr object : threshold type : pir::DoubleAttribute - action : modify_attr object : fuse_alpha type : pir::DoubleAttribute - action : modify_attr object : fuse_beta type : pir::DoubleAttribute - op_name : pd_op.bicubic_interp actions: - action : modify_attr object : scale type : pir::ArrayAttribute data : - type: pir::DoubleAttribute - type: pir::DoubleAttribute ... - op_name : pd_op.moe_permute actions: - action : add_attr object : using_ue8m0_scale type : pir::BoolAttribute data : "false" - action : add_attr object : return_expert_indices type : pir::BoolAttribute data : "false" - action : add_attr object : override_buffer_size type : pir::Int32Attribute data : -1

注意其中的规律:modify_attr若只写objecttype(不写data),表示仅修改属性类型、值保持不变;若同时给出data,则连同默认值一起覆盖(例如pd_op.p_normporder类型改为pir::DoubleAttribute且默认值置为2)。add_attr则必须提供完整的typedata作为新属性的初始值。

Attribute / OpresultAttribute 增删改
  • 增 / 改AttributeOpresultAttribute的新增和修改格式类似,都需要指定object(属性名)、type(属性类型)、data(属性值);当目标是pir::ArrayAttribute时,data是一个列表,其中每个元素都要再标注自身的typedata
op_patches: - op_name : pd_op.data actions: - action : modify_output_attr # 修改 OpresultAttribute object : stop_gradient # 修改的具体属性名为 stop_gradient type : pir::ArrayAttribute # 修改属性类型为 ArrayAttribute data : # 修改属性为具体值 - type: pir::BoolAttribute # ArrayAttribute 类型需要对每一个子元素标识类型和值 data: "false" - action : modify_attr # 修改 Attribute,与修改 OpresultAttribute 类似 object : name type : pir::StrAttribute data : "B" - op_name : pd_op.pool actions : - action : modify_attr # 修改 Attribute object : kernel_size type : pir::ArrayAttribute data : - type: pir::Int64Attribute # 对于修改 ArrayAttribute 内部类型的情况,依然归属在 modify_attr 中,但是 default 值置为空即可 - type: pir::Int64Attribute - op_name : builtin.parameter actions : - action : add_attr # 新增 Attribute object : new_attribute # 新增属性名为 new_attribute type : pir::StrAttribute # 新增属性类型为 StrAttribute data : "new.attribute" # 新增属性值为 "new.attribute" - action : add_output_attr # 新增 OpresultAttribute object : new_Attribute # 新增属性名为 new_output type : pir::Int64Attribute # 新增属性类型为 ArrayAttribute data : 1 # 新增属性为具体值
  • AttributeOpresultAttribute的删除格式类似,只需指定需要删除的对象object,无需typedata
- op_name : pd_op.fetch actions : - action : delete_attr # 删除 Attribute object : col # 删除属性名为 col
OpOperand / OpResult 增删改
  • OpOperand 不需要修改(只存在增删场景);
  • OpResult Type 修改:每个输出有且仅有一个 Type,因此不存在增删 Type 的情况,只有「修改」:
op_patches: - op_name : pd_op.data actions: - action : modify_output_type # 修改 Opresult Type object : 0 # 修改第几个输出的 Type type : pir::DenseTensorType # 修改属性类型为 DenseTensorType data : [pir::Float32Type,[-1,30],"NCHW",[],0] # 修改属性为具体值

data中依次是:元素数据类型、shape(支持 -1 动态维)、data layout(如"NCHW")、loD、以及第 0 个参数(如初始值)。

  • OpOperand / OpResult 的增删需上升到 block 层处理,其基本约束如下:
    • op 输入的删op 输出的增不会破坏 program 结构:删除输入直接修改输入列表即可;增加输出时,取当前 block 中 op id 的最大值,在其上顺延新增即可;
    • 单独 op 输入的增单独 op 输出的删会造成网络结构错误,因此直接报错
    • op 作为组合(pair)出现时,可以在组合内部进行「输出的删 + 输入的增」,保证外部结构不变。
op_patches: - op_name : pd_op.data actions : - action : add_output # 增加输出 object : 1 # 增加为第几个输出 type : pir::DenseTensorType # 修改属性类型为 DenseTensorType data : [pir::Float32Type,[-1,30],"NCHW",[],0] # 修改属性为具体值 - op_name : pd_op.add actions : - action : delete_input # 删除输入 object : 0 # 删除第几个输入

3.2 op_pair_patches:输入输出作为组合修改

当「A 算子的输出」恰好是「B 算子的输入」、且需要成对增删时,必须使用op_pair_patches保持网络结构完整:

op_pair_patches: # 输入输出作为组合进行修改 - op_pair : [pd_op.full, pd_op.full_like] actions: - action : add_value # 增加输入和输出 object : [1,2] # 增加为第 1 个 op 的第几个输入,第 2 个 op 的第几个输出 type : pir::DenseTensorType data : [pir::Float32Type,[1],"NCHW",[],0] - action : delete_value # 删除输入和输出 object : [1, 2] # 删除第 1 个 op 的第几个输入,第 2 个 op 的第几个输出

其中object : [1,2]的含义是:为组合中第 1 个 op(pd_op.full)增加/删除第 1 个输入,为第 2 个 op(pd_op.full_like)增加/删除第 2 个输出。

从源码看,ApplyOpPairPatches在反序列化时会为成对新增的 value 分配同一个新的 value id(取当前 block 最大 id 顺延),并分别写入op1OPRESULTS[ADD]op2OPOPERANDS[ADD],从而保证「生产者-消费者」关系在修补后依然成立:

// version_compat.cc -> PatchBuilder::ApplyOpPairPatches for (uint64_t i = 0; i < op1_patch[ADD].size(); ++i) { max_id++; op1_patch[ADD][i][VALUE_ID] = max_id; op2_patch[ADD][i][VALUE_ID] = max_id; op_patches_[op1][OPRESULTS][ADD].push_back(op1_patch[ADD][i]); op_patches_[op2][OPOPERANDS][ADD].push_back(op2_patch[ADD][i]); }

3.3 attr_patches:修改 Attribute 名称 / 类型

Attribute 值的修改都随 op 进行(即通过op_patches中的modify_attr),attr_patches处理的是 Attribute 本体的演化:

  • 名称修改:存储中使用的是 Attribute 的缩写 name,只要缩写不变,名称的修改不影响兼容性,只需在反序列化阶段把存储名对应修改为修改后的名称即可;
  • 类型废弃替换:例如废弃Int32Attribute、全部改为Int64Attribute
attr_patches: - attr_name : Int32Attribute # 修改的 attribute 名称 actions: - action : modify_name # 修改 Attribute 名称 type : Int64Attribute # 修改为 Int64Attribute

3.4 type_patches:修改 Type

Type 的修改与 Attribute 类似:值的修改都随 OpResult 进行(通过modify_output_type),type_patches处理 Type 本体的演化:

  • 名称修改:与 Attribute 名称修改同理;
  • 类型废弃替换:将某一 Type 整体更改为其他类型;
  • Type 内部属性增删:某些 Type 内部存在多个属性(例如DenseTensorType内嵌 dtype、dims、layout 等),可以按属性序号精确增删:
type_patches: - type_name : Int32Type # 修改的 Type 名称 actions: - action : modify_name # 修改 Type 名称 type : Int64Type # 修改为 Int64Type - type_name : DenseTensorType # 修改的 Type 名称 actions: - action : add_type_attr # 新增 Type 属性 object : 5 # 新增属性为第几个属性 type : pir::Int64Attribute # 新增属性类型为 Int64Attribute data : 0 # 新增属性默认值

3.5 真实补丁内容速览

  • 1.yaml(版本 1 补丁):对pd_op.lp_pool2dpd_op.pool2dstrides/paddingspd_op.pool3dkernel_size/strides/paddingspd_op.expand_astarget_shape等 ArrayAttribute 内部元素类型统一升级为pir::Int64Attribute
  • 3.yaml(版本 3 补丁):pd_op.kthvaluek修改为pir::Int64Attribute;为pd_op.repeat_interleavepd_op.repeat_interleave_with_tensor_index新增output_size属性(默认 -1)。
  • 4.yaml(版本 4 补丁):如前所述,对 softplus / logit / pad3d / group_norm / gaussian / baddbmm / addmm 等大量算子做浮点属性类型升级,并为pd_op.moe_permutepd_op.moe_unpermute等新增布尔/整型属性。

测试侧 test/cpp/pir/serialize_deserialize/patch/0.yaml 与 1.yaml 则覆盖了op_pair_patchespd_op.full/pd_op.scale成对增删 value)、attr_patchespir::FloatAttributepir::DoubleAttribute)、type_patchespir::Float32Typepir::Float64Type)等场景,可作为编写新补丁时的参照模板。

四、pir_version:版本号管理与迭代流程

4.1 版本号定义位置与 CMake 配置

  • 版本号管理在C++ 端,配置于paddle/fluid/pir/serialize_deserialize/CMakeLists.txt
  • PIR 版本号定义 PIR 的版本迭代,与 yaml 文件名强相关:每次 PIR 更新并新增 patch 文件后,patch 文件名顺序递增,版本号同时顺序递增;PIR 版本与 Paddle 主版本号解耦,可独立迭代。
# change pir version when new patches are added add_definitions(-DDEVELOP_VERSION=4) add_definitions(-DRELEASE_VERSION=4)

目录结构示意:

patch/ ├─ 0.yaml └─ 1.yaml
  • RELEASE_VERSION:已发布版本中的 PIR 版本号,即 patch yaml 文件名的最大值。每次新版本发布且存在新增 patch 时,RELEASE_VERSION + 1;若无新增 patch 则无需修改。
  • DEVELOP_VERSION:当前 develop 分支下的 PIR 版本号。若需要新增 patch,配置在0.yaml中(若0.yaml不存在,说明当前是新版本发布后的第一次新增 patch,需要新建0.yaml文件),并将-DDEVELOP_VERSION设置为 0。

4.2 为什么 develop 版本号从 0 重新计数

BuildPatch中有一个关键细节:补丁文件名并非「从 1 顺序取到当前版本」,而是v % max_version

// version_compat.cc -> PatchBuilder::BuildPatch for (auto v = file_version_; v <= pir_version; v++) { std::string file_name = std::to_string(v % max_version); ... }

因此当 develop 阶段新增补丁时,统一写入0.yaml(版本号0),发布后整体版本号 +1、0.yaml的内容被固化进对应版本的正式补丁,从而保证版本号与文件名之间稳定的一一对应关系。这也解释了 Readme 中「新增 patch 配置在 0.yaml 中」的约定。

4.3 版本号如何传递

ReadModuleWriteModule参数中的pir_version均设有默认值(-1),可以不显式传递。pir_version默认值为 -1,进入函数后会获取 CMake 中配置的当前 PIR 版本号。相关接口签名见 include/interface.h:

void IR_API WriteModule(const pir::Program& program, const std::string& file_path, bool overwrite = true, bool readable = false, bool trainable = true, int64_t pir_version = -1); bool IR_API ReadModule(const std::string& file_path, pir::Program* program, int64_t pir_version = -1);
  • WriteModule:把 PIRProgram写入文件。overwrite决定是否覆盖已存在文件;readable为 true 时生成带缩进结构的可读文件;trainable控制是否写训练相关的opresult_attrs(如stop_gradientpersistable),为 false 时可能仅写 op info 属性;pir_version为 -1 时使用 CMake 配置的当前版本。
  • ReadModule:从文件恢复Program当传入的pir_version大于文件内记录的版本号时,会触发版本兼容修补规则(这也是整个 patch 机制被激活的入口条件)。

五、PatchBuilder:补丁的构建与合并原理

补丁的构建与合并全部由PatchBuilder完成,其类定义见 include/version_compat.h,实现见 src/version_compat.cc。

5.1 构建流程(BuildPatch)

BuildPatch(pir_version, max_version, path)的核心逻辑是:file_version_(模型文件记录的版本)逐个版本叠加到当前pir_version,把每个版本对应的 YAML 补丁合并进内存中的补丁表:

void PatchBuilder::BuildPatch(uint64_t pir_version, uint64_t max_version, const std::string& path) { for (auto v = file_version_; v <= pir_version; v++) { std::string file_name = std::to_string(v % max_version); // 默认从内嵌 yaml_files 中取;path 非空时从外部文件加载(测试用) patch_json = YamlParser(file_name, file_path); // 合并 op_pair_patches for (auto patch_info : patch_json["op_pair_patches"]) { ... } // 合并 op_patches:同一 op 的 actions 按规则逐 key 合并 for (auto patch_info : patch_json["op_patches"]) { if (op_patches_.count(patch_info["op_name"])) { // 已存在则合并:OPOPERANDS/OPRESULTS 按 action 合并,其余列表 append } else { op_patches_[patch_info["op_name"]] = patch_info[PATCH]; } } // 合并 type_patches / attr_patches(Json::update 语义) for (auto patch_info : patch_json["type_patches"]) { ... } for (auto patch_info : patch_json["attr_patches"]) { ... } } }

合并规则中值得注意的几点:

  • 同一 op 跨版本多次补丁会被合并OPOPERANDS/OPRESULTS按 action 名(ADD/DELETE/UPDATE)逐项合并,其余属性列表直接 append,NEW_NAME以新值为准;
  • SetFileVersion用于显式指定模型文件中的版本号(file_version_),当file_version != pir_version时才需要构建补丁;
  • 测试场景下可通过path参数从外部目录加载 YAML(例如单测中传入"patch"目录),生产环境则使用 CMake 内嵌的yaml_files

5.2 应用流程(Apply*)

构建完成后,反序列化阶段(ProgramReader)对每个 op / type / attr 调用对应的Apply*方法完成修补:

  • ApplyOpPatches(op_name, json, patch):处理 op 的OPOPERANDS(按 id 插入/删除输入)、OPRESULTS(按 id 更新输出类型 / 插入 / 删除输出)、以及ATTRS/OPRESULTS_ATTRS的增删改;其中对builtin.parameter有专门的紧凑属性(is_distributedis_parameterneed_clipparameter_namepersistablestop_gradienttrainable等)按固定索引处理;
  • ApplyTypePatches(type_name, json, patch):修改 Type 的 id(新名称),并对DenseTensorType递归处理其内部第 0 个属性(dtype)的 Type 补丁;
  • ApplyAttrPatches(attr_name, json, patch):按 patch 逐项修改 Attribute 的类型与数据(merge_patch语义),并更新名称;
  • ApplyAttrTypePatches(attr_name, json, patch):仅处理 Attribute 类型整体改名场景(NEW_NAME)。

六、Python 端与发版约束

6.1 Python 端无需关心 pir_version

  • Paddle 的主版本号定义在Python 端,与 PIR version 不产生关联;
  • Python 端不再需要获取和传入pir_version,直接使用默认值即可——版本兼容修补完全由 C++ 端在ReadModule内部依据文件版本与当前版本自动完成。

6.2 Paddle 发版要求

发版时需要确认:develop 版本被修改为正式版本,即若 Python 端的版本号不为0.0.0,则pir_version不能为 0。这是防止将 develop 阶段「未发布」的补丁状态(版本 0)误作为正式版本发布的硬性约束。

七、版本兼容的验证:单测与完整修改流程

7.1 单测验证

补丁机制有完整的 C++ 单测支撑,位于 test/cpp/pir/serialize_deserialize/save_load_version_compat_test.cc,测试补丁文件在 test/cpp/pir/serialize_deserialize/patch/。核心验证思路如下(摘自测试代码):

  1. 构造一个 Program,写入带版本号的文件:
// Save the program into file pir::WriteModule(program, "./test_save_load", true, false, true, /*pir_version*/ 1);
  1. 用更高版本号读取,触发 patch 修补,并断言修补结果:
// Load the program from file pir::Program new_program(ctx); ReadModuleForTest("./test_save_load", &new_program, 2); // In patch yaml, the value of attribute "parameter_name" in builtin.parameter // is changed into "fc_0" EXPECT_EQ(new_program.block() ->front() .attribute("parameter_name") .dyn_cast<::pir::StrAttribute>() .AsString(), "fc_0");
  1. ReadModuleForTest中展示了版本兼容修补的完整触发流程:解析文件中的BASE_CODE.MAGICPIRVERSION,若file_version != pir_version,则SetFileVersion(file_version)BuildPatch(2, 2, "patch"),最后通过ProgramReaderRecoverProgram重建带补丁语义的 Program。

测试补丁 0.yaml / 1.yaml 完整覆盖了本文所述的大部分 action 类型(modify_attrdelete_attradd_attrmodify_output_attrmodify_output_typeadd_outputdelete_inputadd_valuedelete_valuemodify_nameattr_patchestype_patches),是学习与调试补丁语法的最佳范例。

7.2 完整修改配置流程

新增一个 PIR 兼容补丁的完整操作流程(可参考文档引用的 PR 思路:修改DEVELOP_VERSION的 PR 与新增 patch yaml 的 PR):

  1. 确认当前 develop 分支的DEVELOP_VERSION(查看 CMakeLists.txt);
  2. 0.yaml不存在则新建0.yaml,将新增的 op / type / attr 补丁按第四节语法写入;
  3. -DDEVELOP_VERSION设置为 0(表示 develop 阶段的未发布补丁);
  4. 在 test/cpp/pir/serialize_deserialize/patch/ 中补充或调整测试补丁,并在 save_load_version_compat_test.cc 中新增断言(验证属性值、输出类型、输入输出数量等修补结果);
  5. 运行pir_save_load相关单测确认兼容修补正确;
  6. 发版时:将补丁内容固化到正式版本号文件、RELEASE_VERSION + 1(如有新增 patch),并确保 Python 端版本号非0.0.0pir_version不为 0。

八、总结

PIR 的 Save/Load 版本兼容机制本质上是一个「版本号 + 声明式补丁」的模型升级管道:

  • 声明:用op_patches/op_pair_patches/attr_patches/type_patches四类 YAML 精确描述旧版本结构到新版本结构的迁移动作;
  • 构建:构建期把补丁 YAML 内嵌进二进制,运行时BuildPatch依据「文件版本 → 当前版本」区间叠加合并补丁;
  • 应用:反序列化时Apply*系列方法按补丁逐 op / type / attr 修补,恢复出符合当前版本语义的 Program;
  • 版本DEVELOP_VERSION/RELEASE_VERSION与 patch 文件名强绑定、与 Paddle 主版本解耦,通过发版约束保证补丁状态不被误发布。

这套机制保证了飞桨模型在跨版本场景下的可加载性与语义一致性,是 PIR 序列化体系稳定演进的重要基础设施。对于需要为自定义算子或新增属性维护向后兼容的开发者,理解并正确编写补丁 YAML、遵守版本号迭代约定,即可将模型升级成本控制在一次声明式配置之内。

【免费下载链接】PaddlePArallel Distributed Deep LEarning: Machine Learning Framework from Industrial Practice (『飞桨』核心框架,深度学习&机器学习高性能单机、分布式训练和跨平台部署)项目地址: https://gitcode.com/GitHub_Trending/pa/Paddle

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/13 15:34:32

Java手写倒排索引搜索引擎教学系统

简介&#xff1a;本资源是一套完整的基于Java的搜索引擎毕业设计实现方案&#xff0c;面向计算机及相关专业&#xff08;如人工智能、物联网、电子信息等&#xff09;的本科生、研究生及初学者&#xff0c;解决课程设计、毕设选题与工程实践中的核心开发需求。压缩包共329个文件…

作者头像 李华
网站建设 2026/9/13 15:31:58

垂直GaN功率器件原理与工程落地全解析

1. 项目概述&#xff1a;为什么“垂直GaN”突然成了功率器件圈的高频词&#xff1f;最近在电源设计、快充模块和工业电机驱动的几个技术群里&#xff0c;几乎每天都有人贴出安森美&#xff08;onsemi&#xff09;新发布的NV6134A或NV6136A器件的实测波形图&#xff0c;配文往往…

作者头像 李华
网站建设 2026/9/13 15:29:33

国产电源芯片选型避坑指南:架构、工艺、支撑与FAE四维评估法

1. 为什么这份清单不叫“国产电源芯片推荐榜”&#xff0c;而叫“原厂摸底报告”“国产电源芯片”这六个字&#xff0c;这两年在BOM表、采购单、技术评审会上出现的频率&#xff0c;已经高到让很多资深电子工程师下意识皱眉的程度。不是因为不重要——恰恰相反&#xff0c;它太…

作者头像 李华