Module Patch 特性

Module Patch 是 NPU 插件框架的一个核心特性,提供了一种干净的机制来动态扩展或修改现有的类和模块。该特性允许您在运行时向目标类或模块添加新的方法、属性和功能,而无需修改原始源代码。

特性概述

Module Patch 特性解决了在集成 NPU 支持时需要扩展外部类和模块功能的问题。通过这个特性,您可以:

  • 无侵入式扩展:向现有类添加新方法和属性,无需修改原始代码
  • 条件patch:支持类级别和方法级别的条件装饰器
  • 类型安全:使用订阅语法指定目标,编译时类型检查
  • 冲突检测:自动检测重复patch,避免意外覆盖
  • 完整追踪:详细的patch摘要和日志记录
  • 灵活应用:支持类方法、静态方法、实例方法和属性

核心组件

NPUPatchHelper 基类

from verl_npu.core import NPUPatchHelper

class MyPatch(NPUPatchHelper[TargetClass]):
    # 添加新的属性和方法
    new_field = "This will be added to TargetClass"
    
    def new_method(self):
        return "This method will be added to TargetClass"
    
    @classmethod
    def new_classmethod(cls):
        return "This classmethod will be added to TargetClass"
    
    @staticmethod
    def new_staticmethod():
        return "This staticmethod will be added to TargetClass"

条件patch装饰器

from verl_npu.core import conditional, is_torch_npu_available

# 类级别条件patch
@conditional(is_torch_npu_available)
class NPUPatch(NPUPatchHelper[TargetClass]):
    def npu_method(self):
        return "Only patched when NPU is available"

# 方法级别条件patch
class MixedPatch(NPUPatchHelper[TargetClass]):
    def always_patched(self):
        return "Always patched"
    
    @conditional(lambda: os.environ.get("DEBUG", "0") == "1")
    def debug_method(self):
        return "Only patched in debug mode"

快速开始

基本用法 - 扩展类

from verl_npu.core import NPUPatchHelper

# 假设我们要扩展一个现有的模型类
from some_library import ModelClass

class NPUModelPatch(NPUPatchHelper[ModelClass]):
    """为ModelClass添加NPU支持"""
    
    # 添加NPU相关属性
    npu_enabled = True
    npu_device_count = 8
    
    def enable_npu(self):
        """启用NPU加速"""
        self.npu_enabled = True
        print("NPU acceleration enabled")
    
    def get_npu_info(self):
        """获取NPU信息"""
        return {
            "enabled": self.npu_enabled,
            "device_count": self.npu_device_count
        }
    
    @classmethod
    def create_npu_model(cls, config):
        """创建NPU优化的模型实例"""
        instance = cls(config)
        instance.enable_npu()
        return instance

# 应用patch
NPUModelPatch.apply_patch()

# 现在可以使用新功能
model = ModelClass()
model.enable_npu()  # 新方法可用
print(model.get_npu_info())  # 新方法可用
npu_model = ModelClass.create_npu_model(config)  # 新类方法可用

扩展模块

import some_module
from verl_npu.core import NPUPatchHelper

class NPUModulePatch(NPUPatchHelper[some_module]):
    """为模块添加NPU相关功能"""
    
    # 添加新常量
    NPU_BACKEND = "ascend"
    NPU_PRECISION = "fp16"
    
    @staticmethod
    def get_npu_devices():
        """获取可用的NPU设备"""
        return list(range(8))  # 假设有8个NPU设备
    
    @staticmethod
    def init_npu_context():
        """初始化NPU上下文"""
        print("Initializing NPU context...")
        return True

# 应用patch
NPUModulePatch.apply_patch()

# 现在可以使用新功能
print(some_module.NPU_BACKEND)  # 新常量可用
devices = some_module.get_npu_devices()  # 新函数可用
some_module.init_npu_context()  # 新函数可用

高级特性

1. 方法替换和扩展

class AdvancedPatch(NPUPatchHelper[TargetClass]):
    """高级patch示例:替换和扩展现有方法"""
    
    def __init__(self, *args, **kwargs):
        """扩展构造函数"""
        super().__init__(*args, **kwargs)
        self.npu_initialized = False
    
    def forward(self, x):
        """替换forward方法以支持NPU"""
        if hasattr(self, 'npu_enabled') and self.npu_enabled:
            # NPU加速的forward实现
            return self._npu_forward(x)
        else:
            # 原始实现
            return self._original_forward(x)
    
    def _npu_forward(self, x):
        """NPU优化的forward实现"""
        print("Using NPU accelerated forward")
        return x  # 简化实现

2. 条件patch - 默认NPU检查

# NPUPatchHelper默认包含NPU可用性检查
class AutoNPUPatch(NPUPatchHelper[TargetClass]):
    """自动使用NPU可用性检查"""
    
    def enable_npu_training(self):
        """只有NPU可用时才会被patch"""
        return "NPU training enabled"
    
    def optimize_npu_memory(self):
        """NPU内存优化"""
        return "NPU memory optimized"

3. 条件patch - 显式条件

from verl_npu.core import conditional, is_torch_npu_available

@conditional(is_torch_npu_available)
class ExplicitNPUPatch(NPUPatchHelper[TargetClass]):
    """显式指定NPU条件"""
    
    def advanced_npu_feature(self):
        return "Advanced NPU functionality"

# 复杂条件组合
@conditional.all(
    is_torch_npu_available,
    lambda: os.environ.get("LARGE_MEMORY", "0") == "1"
)
class AdvancedNPUPatch(NPUPatchHelper[TargetClass]):
    """需要NPU可用且大内存"""
    
    def train_large_model(self):
        return "Training large model on NPU"

# 方法级别条件
class MixedConditionalPatch(NPUPatchHelper[TargetClass]):
    """混合条件patch"""
    
    def basic_npu_method(self):
        """使用默认NPU条件"""
        return "Basic NPU method"
    
    @conditional(lambda: os.environ.get("EXPERIMENTAL", "0") == "1")
    def experimental_method(self):
        """实验性功能"""
        return "Experimental feature"
    
    @conditional(lambda: True)  # 总是patch
    def always_available(self):
        """无条件patch"""
        return "Always available"

条件patch特性

概述

条件patch允许您根据运行时条件决定是否应用patch,提供更灵活的NPU集成策略。

默认NPU条件

所有 NPUPatchHelper 子类默认包含NPU可用性检查:

class AutoNPUPatch(NPUPatchHelper[TargetClass]):
    """默认只在NPU可用时应用"""
    
    def npu_method(self):
        return "NPU功能"

条件装饰器语法

1. 额外条件(默认行为)

from verl_npu.core import conditional, is_torch_npu_available

# 显式NPU条件
@conditional(is_torch_npu_available)
class ExplicitNPUPatch(NPUPatchHelper[TargetClass]):
    """显式指定NPU条件 - NPU可用 AND 显式条件"""
    def npu_feature(self):
        return "NPU feature"

# 自定义条件
@conditional(lambda: os.environ.get("ENABLE_EXPERIMENTAL", "0") == "1")
class ExperimentalPatch(NPUPatchHelper[TargetClass]):
    """实验性功能 - NPU可用 AND 实验性功能启用"""
    def experimental_feature(self):
        return "Experimental feature"

# 条件组合
@conditional.all(
    is_torch_npu_available,
    lambda: os.environ.get("LARGE_MEMORY", "0") == "1"
)
class AdvancedPatch(NPUPatchHelper[TargetClass]):
    """高级功能 - NPU可用 AND 大内存"""
    def advanced_feature(self):
        return "Advanced feature"

2. 替换默认条件

# 替换默认NPU条件,只用自定义条件
@conditional.only(lambda: os.environ.get("TEST_MODE", "0") == "1")
class TestModePatch(NPUPatchHelper[TargetClass]):
    """测试模式patch - 替换默认NPU条件"""
    
    def test_method(self):
        return "Only added when TEST_MODE=1, ignoring NPU check"
    
    def mock_npu_method(self):
        return "Mock NPU method for testing"

# 强制应用,忽略所有条件
@conditional.only(lambda: True)
class ForceApplyPatch(NPUPatchHelper[TargetClass]):
    """强制应用,忽略所有条件"""
    
    def force_method(self):
        return "Always available, no conditions"

3. 条件类型对比

装饰器 检查逻辑 使用场景
无装饰器 默认NPU条件 基本NPU依赖patch
@conditional 默认NPU条件 AND 额外条件 添加额外要求
@conditional.only 只用自定义条件 替换NPU要求

方法级别条件

class MixedPatch(NPUPatchHelper[TargetClass]):
    """混合条件patch示例"""
    
    def default_npu_method(self):
        """使用默认NPU条件"""
        return "Default NPU method"
    
    @conditional(lambda: os.environ.get("DEBUG", "0") == "1")
    def debug_method(self):
        """调试模式专用"""
        return "Debug method"
    
    @conditional.any(
        lambda: os.environ.get("DEV", "0") == "1",
        lambda: os.environ.get("TEST", "0") == "1"
    )
    def dev_test_method(self):
        """开发或测试环境"""
        return "Dev/Test method"
    
    @conditional.not_(lambda: os.environ.get("PRODUCTION", "0") == "1")
    def non_production_method(self):
        """非生产环境"""
        return "Non-production method"
    
    @conditional(lambda: True)  # 总是应用
    def always_method(self):
        """无条件应用"""
        return "Always available"

条件组合语法

语法 说明 示例
@conditional(func) 单个额外条件 @conditional(is_torch_npu_available)
@conditional.only(func) 替换默认条件 @conditional.only(test_mode)
@conditional.all(f1, f2) 所有条件都满足 @conditional.all(npu_available, large_memory)
@conditional.any(f1, f2) 任一条件满足 @conditional.any(debug_mode, test_mode)
@conditional.not_(func) 条件不满足 @conditional.not_(production_mode)
@conditional.only.all(f1, f2) 替换默认条件,所有条件满足 @conditional.only.all(test_mode, skip_npu)

条件优先级

  1. 方法级别条件 > 类级别条件 > 默认条件
  2. 方法级别条件会覆盖类级别和默认条件
  3. @conditional.only 会完全替换默认条件,而不是添加额外条件

条件patch日志

条件patch会在摘要中显示详细信息:

from verl_npu.core import print_patch_summary

print_patch_summary()

# 输出示例:
# ================ NPU Patch Summary ================
# 1. Target: some_library.ModelClass
#    Patch : MyConditionalPatch
#    Class Condition: ConditionalPatch.all(is_torch_npu_available, <lambda>)
#    Changes:
#      - added    callable    npu_method
#      - added    callable    advanced_feature
#    Skipped Methods: ['debug_method', 'experimental_method']
# ===================================================

Patch 摘要和日志

查看Patch摘要

from verl_npu.core import print_patch_summary, get_patch_summary

# 应用所有patch后,查看摘要
print_patch_summary()

# 输出示例:
# ================ NPU Patch Summary ================
# 1. Target: some_library.ModelClass
#    Patch : __main__.NPUModelPatch
#    Changes:
#      - added    attribute   npu_enabled
#      - added    callable    enable_npu
#      - added    callable    get_npu_info
#      - added    classmethod create_npu_model
# ===================================================

与插件系统集成

1. 创建Module Patch文件

# npu_model_patches.py
from verl_npu.core import NPUPatchHelper
from target_library import ModelClass, TrainerClass

class ModelNPUPatch(NPUPatchHelper[ModelClass]):
    def enable_npu_acceleration(self):
        self.use_npu = True

class TrainerNPUPatch(NPUPatchHelper[TrainerClass]):
    def setup_npu_training(self):
        print("Setting up NPU training environment")

2. 在插件中注册

# verl_npu/plugin.py
def apply_npu_plugin():
    # 应用module patch
    from .npu_model_patches import ModelNPUPatch, TrainerNPUPatch
    
    ModelNPUPatch.apply_patch()
    TrainerNPUPatch.apply_patch()

最佳实践

1. 命名约定

  • Patch类使用描述性名称:ModelNPUPatchOptimizerNPUPatch
  • 方法名使用清晰的前缀:npu_*enable_*setup_*
  • 避免与原有方法名冲突

2. 组织结构

# 按功能组织patch
class BaseNPUPatch(NPUPatchHelper):
    """基础NPU功能"""
    
    def _init_npu_base(self):
        self.npu_initialized = True

class ModelNPUPatch(BaseNPUPatch[ModelClass]):
    """模型特定的NPU功能"""
    
    def enable_model_npu(self):
        self._init_npu_base()
        # 模型特定的NPU初始化

4. 测试和验证

# 验证patch是否正确应用
def verify_patches():
    from verl_npu.core import get_patch_summary
    
    summary = get_patch_summary()
    expected_patches = ["ModelNPUPatch", "TrainerNPUPatch"]
    
    applied_patches = [entry["patch_class"] for entry in summary]
    
    for expected in expected_patches:
        if not any(expected in patch for patch in applied_patches):
            print(f"Warning: {expected} not found in applied patches")

故障排除

常见问题

  1. 重复Patch错误
# 错误:ValueError: TargetClass.method_name is already patched
# 解决:检查是否重复应用patch或方法名冲突
  1. 目标类型错误
# 错误:TypeError: NPUPatchHelper can only target a class or module
# 解决:确保目标是类或模块,不是实例
  1. 导入顺序问题
# 确保在使用前导入目标类
from target_library import TargetClass  # 必须在patch定义前
class MyPatch(NPUPatchHelper[TargetClass]):
    pass

总结

Module Patch 特性提供了一个安全的机制来扩展现有的类和模块。通过类型安全的语法、条件patch支持、自动冲突检测和详细的运行时summary报告,简化了各种修改场景和验证。

  • 类型安全:校验语法和编译时检查
  • 条件patch:支持类级别和方法级别的条件装饰器
  • 默认NPU检查:自动检查NPU可用性,避免无效patch
  • 灵活条件组合:支持 all、any、not 逻辑组合
  • 冲突防护:自动检测重复patch
  • 完整追踪:详细的patch摘要和日志,包含条件信息
  • 灵活扩展:支持各种类型的方法和属性
  • 无侵入式:不修改原始源代码,运行时自动注入
  • 易于调试:丰富的错误信息和调试工具