• PyTorch修改为自定义节点


    1

    要将网络修改为自定义节点,需要在该节点上实现新的操作。在 PyTorch 中,可以使用 torch.onnx 模块来导出 PyTorch 模型的 ONNX 格式,因此我们需要修改 op_symbolic 函数来实现新的操作。

    首先,创建一个新的 OpSchema 对象,该对象定义了新操作的名称、输入和输出张量等属性。然后,可以定义 op_symbolic 函数来实现新操作的计算。

    下面是示例代码:

    import torch.onnx.symbolic_opset12 as sym
    
    class MyCustomOp:
        @staticmethod
        def forward(ctx, input1, input2, input3):
            output = input1 * input2 + input3
            return output
    
        @staticmethod
        def symbolic(g, input1, input2, input3):
            output = g.op("MyCustomOp", input1, input2, input3)
            return output
    
    my_custom_op_schema = sym.ai_graphcore_opset1_schema("MyCustomOp", 3, 1)
    my_custom_op_schema.set_input(0, "input1", "T")
    my_custom_op_schema.set_input(1, "input2", "T")
    my_custom_op_schema.set_input(2, "input3", "T")
    my_custom_op_schema.set_output(0, "output", "T")
    my_custom_op_schema.set_doc_string("My custom op")
    
    # Register the custom op and schema with ONNX
    register_custom_op("MyCustomOp", MyCustomOp)
    register_op("MyCustomOp", None, my_custom_op_schema)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    在上面的示例中,我们首先使用 @staticmethod 装饰器创建了 MyCustomOp 类,并实现了 forwardsymbolic 函数,这些函数定义了新操作的计算方式和 ONNX 格式的表示方法。

    然后,我们使用 ai_graphcore_opset1_schema 函数创建了一个新的 OpSchema 对象,并为新操作定义了输入和输出张量。

    最后,我们通过 register_custom_opregister_op 函数将自定义操作和 OpSchema 对象注册到 ONNX 中,以便使用 torch.onnx.export 方法将 PyTorch 模型转换为 ONNX 格式。

    2

    要将网络修改为自定义节点,您需要:

    1. 实现自定义节点的 forward 和 backward 函数。

    2. 创建一个新的 PyTorch 操作(op)来包装自定义节点。

    3. 修改网络图以使用新的操作。

    以下是一个示例:

    1. 实现自定义节点的 forward 和 backward 函数。
    import torch
    
    class CustomNode(torch.autograd.Function):
        @staticmethod
        def forward(ctx, input):
            # implement forward function
            output = input * 2
            ctx.save_for_backward(input)
            return output
    
        @staticmethod
        def backward(ctx, grad_output):
            # implement backward function
            input, = ctx.saved_tensors
            grad_input = grad_output.clone()
            grad_input[input < 0] = 0
            return grad_input
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    这里我们实现了一个乘以 2 的操作,以及后向传播的 ReLU 激活函数。

    1. 创建一个新的 PyTorch 操作(op)来包装自定义节点。
    from torch.autograd import Function
    
    class CustomOp(Function):
        @staticmethod
        def forward(ctx, input):
            output = CustomNode.apply(input)
            ctx.save_for_backward(input)
            return output
    
        @staticmethod
        def backward(ctx, grad_output):
            input, = ctx.saved_tensors
            grad_input = CustomNode.apply(grad_output)
            return grad_input
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    这里我们使用 CustomNode.apply 来调用自定义节点的前向和后向传播函数。

    1. 修改网络图以使用新的操作。
    import torch.nn as nn
    
    class CustomNet(nn.Module):
        def __init__(self):
            super(CustomNet, self).__init__()
            self.conv1 = nn.Conv2d(3, 6, 5)
            self.relu = nn.ReLU()
            self.custom_op = CustomOp()
    
        def forward(self, x):
            x = self.conv1(x)
            x = self.relu(x)
            x = self.custom_op(x)
            return x
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    在这个例子中,我们添加了一个自定义操作 CustomOp,并将其插入到网络中。

    这就是如何将 PyTorch 网络修改为自定义节点的过程。

    3

    要将自定义节点添加到ONNX图中,必须执行以下步骤:

    1. 创建自定义运算符

    首先,创建一个自定义运算符的类。自定义的类应该继承自torch.nn.Module类。

    class CustomOp(torch.nn.Module):
        def __init__(self, alpha, beta):
            super(CustomOp, self).__init__()
            self.alpha = alpha
            self.beta = beta
    
        def forward(self, x):
            # perform some custom operation on x using alpha and beta
            return x
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    1. 注册自定义运算符

    为了使ONNX能够正确地识别您的自定义运算符,您需要在导出模型之前将其注册到ONNX运行时中。

    from torch.onnx.symbolic_helper import parse_args
    
    @parse_args('v', 'f', 'f')
    def custom_op(g, x, alpha, beta):
        # build the ONNX graph for the custom operation
        output = g.op('CustomOp', x, alpha, beta)
        return output
    
    # register the custom operator
    from torch.onnx import register_custom_op_symbolic
    register_custom_op_symbolic('CustomOp', custom_op, 9)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    1. 将模型导出为ONNX

    现在,您可以使用torch.onnx.export()方法将PyTorch模型导出为ONNX格式。

    import torch.onnx
    
    # create a sample model that uses the custom operator
    model = torch.nn.Sequential(
        torch.nn.Linear(10, 10),
        CustomOp(alpha=1.0, beta=2.0),
        torch.nn.Linear(10, 5)
    )
    
    # export the model to ONNX
    input_data = torch.rand(1, 10)
    torch.onnx.export(model, input_data, "model.onnx")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    1. 在C++中加载ONNX模型并使用自定义运算符

    最后,在C++中使用ONNX运行时加载导出的模型,并使用自定义运算符来运行它。

    #include 
    
    // load the ONNX model
    Ort::Env env(ORT_LOGGING_LEVEL_WARNING, "test");
    Ort::SessionOptions session_options;
    Ort::Session session(env, "model.onnx", session_options);
    
    // create a test input tensor
    std::vector input_shape = {1, 10};
    std::vector input_data(10);
    Ort::Value input_tensor = Ort::Value::CreateTensor(Ort::MemoryInfo::CreateCpu(OrtDeviceAllocator, OrtMemTypeDefault), input_data.data(), input_data.size(), input_shape.data(), input_shape.size());
    
    // run the model
    std::vector output_names = session.GetOutputNames();
    std::vector output_tensors = session.Run({{"input", input_tensor}}, output_names);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    4

    您可以通过以下步骤来修改PyTorch中的网络,以添加自定义节点:

    1. 了解PyTorch的nn.Module和nn.Function类。nn.Module用于定义神经网络中的层,而nn.Function用于定义网络中的自定义操作。自定义操作可以使用PyTorch的Tensor操作和其他标准操作来定义。

    2. 创建自定义操作类:创建自定义操作类时,需要继承自nn.Function。自定义操作必须包含forward方法和backward方法。在forward方法中,您可以定义自定义节点的前向计算;在backward方法中,您可以定义反向传播的计算图。

    3. 将自定义操作类添加到网络中:您可以使用nn.Module中的register_function方法将自定义操作添加到网络中。这将使自定义操作可用于定义网络中的节点。

    4. 修改网络结构:您可以使用nn.Module类以及先前自定义的操作来修改网络结构。您可以添加自定义操作作为新的节点,也可以将自定义操作添加到现有节点中。

    下面是一个简单的例子,演示如何使用自定义操作来创建新的网络。

    import torch
    from torch.autograd import Function
    import torch.nn as nn
    
    class CustomFunction(Function):
    
        @staticmethod
        def forward(ctx, input):
            output = input * 2
            ctx.save_for_backward(input)
            return output
    
        @staticmethod
        def backward(ctx, grad_output):
            input, = ctx.saved_tensors
            grad_input = grad_output.clone()
            grad_input[input < 0] = 0
            return grad_input
    
    class CustomNetwork(nn.Module):
        def __init__(self):
            super(CustomNetwork, self).__init__()
    
            self.conv1 = nn.Conv2d(3, 6, kernel_size=5, stride=1)
            self.custom_op = CustomFunction.apply
            self.fc1 = nn.Linear(6 * 24 * 24, 10)
    
        def forward(self, x):
            x = self.conv1(x)
            x = self.custom_op(x)
            x = x.view(-1, 6 * 24 * 24)
            x = self.fc1(x)
            return x
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    在这个例子中,我们定义了一个自定义操作CustomFunction,该操作将输入乘以2,并且在反向传播时,只计算非负输入的梯度。我们还创建了一个自定义网络CustomNetwork,该网络包括一个卷积层,一个自定义操作和一个全连接层。

    要使用这个网络,您可以按照以下方式创建一个实例,然后将数据输入到网络中:

    net = CustomNetwork()
    input = torch.randn(1, 3, 28, 28)
    output = net(input)
    
    • 1
    • 2
    • 3

    这将计算网络的输出,并且梯度将正确地传播回每个节点和自定义操作。

  • 相关阅读:
    【java】【SpringBoot】【四】原理篇 bean、starter、核心原理
    使用spring boot的程序主线程中异步访问外部接口
    什么是3322域名?3322域名如何注册?
    SpringBoot+Vue实现前后端分离的网吧管理系统
    Kafka Tool(Kafka 可视化工具)安装及使用教程
    ffmpeg 像素格式基础知识
    node笔记记录86router之2
    k8s集群的存储卷、pvc和pv
    python--短路运算,把0、空字符串和None看成 False,其他数值和非空字符串都看成 True
    C++核心编程——4.5 运算符重载
  • 原文地址:https://blog.csdn.net/qq_39506862/article/details/130894768