import torch
import torch.nn as nn
from torch.autograd import Function
import torch.onnx
#custom op onnx representation
class MyStrangeOp(Function):
#for onnx node representation, symbolic function must be defined and specified static.
@staticmethod
def symbolic(g, input, weight, bias, floatAttr, intAttr):
#because forward function return 2 outputs, so this func also have to return 2 outputs
#this is my expriment result, I didn't find any docs, fuck it!
return g.op("MyStrangeOp", input, weight, bias, float_attr_f=floatAttr, int_attr_i=intAttr), \
g.op("MyStrangeOp", input, weight, bias, float_attr_f=floatAttr, int_attr_i=intAttr)
@staticmethod
def forward(ctx, input, weight, bias, floatAttr, intAttr):
#this op return 2 outputs
return input + weight, input * weight + bias
myStrangeOpForward = MyStrangeOp.apply
#layer
class MyStrangeOpLayer(nn.Module):
def __init__(self, weight, bias, floatAttr, intAttr):
super(MyStrangeOpLayer, self).__init__()
self.weight = weight
self.bias = bias
self.floatAttr = floatAttr
self.intAttr = intAttr
def forward(self, x):
return myStrangeOpForward(x, self.weight, self.bias, self.floatAttr, self.intAttr)
#model
class MyStrangeNet(nn.Module):
def __init__(self):
super(MyStrangeNet, self).__init__()
self.myLayer1 = MyStrangeOpLayer(weight=nn.Parameter(torch.ones(1, 3, 4, 4)), bias=nn.Parameter(torch.ones(1, 3, 4, 4)), floatAttr=[0.1, 0.5], intAttr=[2, 2])
self.myLayer2 = MyStrangeOpLayer(weight=nn.Parameter(torch.ones(1, 3, 4, 4)), bias=nn.Parameter(torch.ones(1, 3, 4, 4)), floatAttr=[0.5, 0.5], intAttr=[3, 3])
self.conv1 = nn.Conv2d(in_channels=3, out_channels=3, kernel_size=3, padding=1, stride=1, bias=True)
self.conv2 = nn.Conv2d(in_channels=3, out_channels=3, kernel_size=3, padding=1, stride=1, bias=True)
self.conv3 = nn.Conv2d(in_channels=3, out_channels=3, kernel_size=3, padding=1, stride=1, bias=True)
self.conv4 = nn.Conv2d(in_channels=3, out_channels=3, kernel_size=3, padding=1, stride=1, bias=True)
def forward(self, x):
x1, x2 = self.myLayer1(x)
x3, x4 = self.myLayer2(x)
x1 = self.conv1(x1)
x2 = self.conv1(x2)
x3 = self.conv1(x3)
x4 = self.conv1(x4)
return x1 + x2 + x3 + x4
model = MyStrangeNet()
t = torch.ones(1, 3, 4, 4, dtype=torch.float32)
torch.onnx.export(model, (t,), 'fuckIt.onnx', opset_version=13, input_names=["inputTensor"], output_names=["outputTensor"],
operator_export_type=torch.onnx.OperatorExportTypes.ONNX_ATEN_FALLBACK)
import torch
import torch.nn as nn
from torch.autograd import Function
import torch.onnx
#custom op for onnx representation
class MyStrangeOp2(Function):
#for onnx graph
@staticmethod
def symbolic(g, input1, input2, bias, int_attr1, int_attr2, str_attr3):
return g.op("MyStrangeOp2", input1, input2, bias, int_attr1_i=int_attr1, int_attr2_i=int_attr2, str_attr3_s=str_attr3)
@staticmethod
def forward(ctx, input1, input2, bias, int_attr1, int_attr2, str_attr3):
return input1 + input2 - bias
myStrangeOp2_forward = MyStrangeOp2.apply
#layer
class MyStrangeOp2Layer(nn.Module):
def __init__(self, bias, int_attr1, int_attr2, str_attr3):
super(MyStrangeOp2Layer, self).__init__()
self.bias = bias
self.int_attr1 = int_attr1
self.int_attr2 = int_attr2
self.str_attr3 = str_attr3
def forward(self, in1, in2):
return myStrangeOp2_forward(in1, in2, self.bias, self.int_attr1, self.int_attr2, self.str_attr3)
#net
class MyStrangeNet2(nn.Module):
def __init__(self):
super(MyStrangeNet2, self).__init__()
self.myLayer1 = MyStrangeOp2Layer(bias=nn.Parameter(torch.ones(1, 3, 4, 4)), int_attr1=10, int_attr2=[3, 5], str_attr3="fuck" )
self.myLayer2 = MyStrangeOp2Layer(bias=nn.Parameter(torch.ones(1, 3, 4, 4)), int_attr1=40, int_attr2=[12, 22], str_attr3="shit")
self.conv1 = nn.Conv2d(in_channels=3, out_channels=3, kernel_size=3, padding=1, stride=1, bias=True)
self.conv2 = nn.Conv2d(in_channels=3, out_channels=3, kernel_size=3, padding=1, stride=1, bias=True)
def forward(self, in1, in2, in3, in4):
x1 = self.myLayer1(in1, in2)
x2 = self.myLayer2(in3, in4)
x1 = self.conv1(x1)
x2 = self.conv2(x2)
return x1 + x2
#fake input
model = MyStrangeNet2()
t1 = torch.ones(1, 3, 4, 4, dtype=torch.float32)
t2= torch.ones(1, 3, 4, 4, dtype=torch.float32)
t3 = torch.ones(1, 3, 4, 4, dtype=torch.float32)
t4 = torch.ones(1, 3, 4, 4, dtype=torch.float32)
#save onnx
torch.onnx.export(model, (t1, t2, t3, t4), 'fuckNet.onnx', opset_version=13, input_names=["input1", "input2", "input3", "input4",], output_names=["outputTensor"],
operator_export_type=torch.onnx.OperatorExportTypes.ONNX_ATEN_FALLBACK)
多输入单输出模型这个细分模型没有太多要注意的地方。
除了上述两个模型分别的一些特性之外,所有自定义算子的实现过程都有一些共性特点,可总结如下: