ONNX-Export eines MLP

ONNX-Export eines MLP 1:

Download Python Samples

Ein Zip-Archiv mit allen Samples finden Sie hier: Beispiele zum ONNX-Export

MLP Regressor mit PyTorch

import torch
import numpy as np

input_dim = 3
output_dim = 5
n_samples = 100
dummy_input = np.random.random((n_samples, input_dim))
dummy_output = np.random.random((n_samples, output_dim))
tensor_in = torch.FloatTensor(dummy_input)
tensor_out = torch.FloatTensor(dummy_output)
train_dataset = torch.utils.data.TensorDataset(tensor_in, tensor_out)
train_loader = torch.utils.data.DataLoader(train_dataset, shuffle=True, batch_size=32)
class MLP_Net(torch.nn.Module):
    def __init__(self):
        super().__init__()
        self.fc1 = torch.nn.Linear(input_dim, 10)
        self.fc2 = torch.nn.Linear(10, output_dim)
    
    def forward(self, x):
        x = torch.nn.functional.relu(self.fc1(x))
        x = torch.sigmoid(self.fc2(x))
        return x
mlp_net = MLP_Net()
optimizer = torch.optim.Adam(mlp_net.parameters(), lr =0.001)
n_epochs = 5
for epoch in range(n_epochs):
    for batch in train_loader:
        input, output = batch
        mlp_net.zero_grad()
        pred_out = mlp_net(input)
        criterion = torch.nn.MSELoss()
        loss = criterion(pred_out, output)
        loss.backward()
        optimizer.step()
onnx_file = 'pytorch_mlp.onnx'
tensor_input_size = torch.FloatTensor(np.random.random((1,input_dim))) # First dimension must be 1
torch.onnx.export(mlp_net, tensor_input_size, onnx_file, verbose=True)
ONNX-Export eines MLP 2:

MLP Regressor mit Keras

import tensorflow as tf
import numpy as np
import tf2onnx

input_dim = 3
output_dim = 5
n_samples = 100

dummy_input = np.random.random((n_samples, input_dim,))
dummy_output = np.random.random((n_samples, output_dim))

model = tf.keras.Sequential()
model.add(tf.keras.layers.Dense(5, input_shape = (input_dim,), activation=tf.keras.activations.relu, use_bias = True))
model.add(tf.keras.layers.Dense(output_dim, activation='sigmoid'))
model.build()
model.summary()
learning_rate = 0.001
loss = 'mean_squared_error'
optimizer = tf.keras.optimizers.Adamax(lr=learning_rate)
model.compile(optimizer=optimizer, loss=loss)
model.fit(x=dummy_input, y=dummy_output, batch_size=32 ,epochs=5,verbose=1, shuffle=True)

filename = 'tf_keras_mlp'
onnx_model, _ = tf2onnx.convert.from_keras(model)
with open(filename+'.onnx','wb') as f:
    f.write(onnx_model.SerializeToString())
ONNX-Export eines MLP 3:

MLP Regressor mit Scikit-learn

import sklearn.neural_network as skl
import numpy as np

input_dim = 3
output_dim = 5
n_samples = 100
dummy_input = np.random.random((n_samples,input_dim))
dummy_output = np.random.random((n_samples,output_dim))

model = skl.MLPRegressor(hidden_layer_sizes =(5), activation ='relu')
model.fit(dummy_input,dummy_output)

filename = 'skl_relu_reg'
from skl2onnx import convert_sklearn
from skl2onnx.common.data_types import FloatTensorType
initial_type = [('float_input',FloatTensorType([None,input_dim]))]

onx = convert_sklearn(model,initial_types=initial_type)
with open(filename+'.onnx','wb') as f:
    f.write(onx.SerializeToString())
ONNX-Export eines MLP 4:

MLP Classifier mit Scikit-learn

import sklearn.neural_network as skl
import numpy as np
import onnx
from skl2onnx import convert_sklearn
from skl2onnx.common.data_types import FloatTensorType

def modify_onnx_MLPClassifier(onnx_MLPCLassifier):
    """ Function to modify onnx model from MLPClassifier to make it suitable for TwinCAT Machine Learning
    The function removes unsupported nodes and uses the probability estimates of the classes as output for 
    the model.
    The output of the modified onnx model is the same as the output of the predict_proba() method from
    sklearn.neural_network.MLPClassifier.
    To get the class as an integer output either a binarization or an argmax function must be applied to the 
    model output.
    """
    # Delete nodes after last sigmoid/softmax layer
    output_node_types = {'Sigmoid', 'Softmax'}
    len_onx_init = len(onnx_MLPCLassifier.graph.node)
    erased_node_inputs = []
    for idx, node in enumerate(reversed(onnx_MLPCLassifier.graph.node)):
        if node.op_type in output_node_types:
            idx_last_node = len_onx_init - idx -1
            break   
        else:
            for idx, input in enumerate(node.input):
                erased_node_inputs.append(input)
            onnx_MLPCLassifier.graph.node.remove(node)
    # Get output dimension and clean initializers
    second_last_node = onnx_MLPCLassifier.graph.node[idx_last_node-1]
    for initializer in onnx_MLPCLassifier.graph.initializer:
        if initializer.name in second_last_node.input:
            output_dim = initializer.dims[1]
        if initializer.name in erased_node_inputs:
            onnx_MLPCLassifier.graph.initializer.remove(initializer)
    # Erase original outputs and create new output
    for output in reversed(onnx_MLPCLassifier.graph.output):
        onnx_MLPCLassifier.graph.output.remove(output)
    name_new_output = "probabilities"
    newOutput = onnx.helper.make_tensor_value_info(name_new_output, onnx.TensorProto.FLOAT, shape=(None, output_dim))
    onnx_MLPCLassifier.graph.output.append(newOutput)
    # Create new node and connect to new output
    last_node_output = onnx_MLPCLassifier.graph.node[idx_last_node].output
    new_node = onnx.helper.make_node("Identity", last_node_output, [name_new_output], "Identity_Out")
    onnx_MLPCLassifier.graph.node.append(new_node)
    return onnx_MLPCLassifier

input_dim = 3
output_dim = 2
n_samples = 100
dummy_input = np.random.random((n_samples,input_dim))
dummy_output = np.random.randint(2, size=(n_samples, output_dim))
model = skl.MLPClassifier(activation ='relu', hidden_layer_sizes=(5))
model.fit(dummy_input,dummy_output)
filename = 'skl_mlp_clf'
initial_type = [('float_input',FloatTensorType([None,input_dim]))]
onx = convert_sklearn(model,initial_types=initial_type, options={type(model): {'zipmap': False}})
onx = modify_onnx_MLPClassifier(onx)
with open(filename+'.onnx','wb') as f:
    f.write(onx.SerializeToString())
ONNX-Export eines MLP 5:

Beachten Sie die Funktion modify_onnx_MLPClassifier. Diese modifiziert den unteren Teil des ONNX-Graphs, sodass nur Operatoren genutzt werden, die von der TwinCAT Neural Network Inference Engine unterstützt werden. Ohne diese Modifikation werden die hier abgebildeten Operatoren (je nach Dimensionalität des Problems) erzeugt. Nur der grün umrahmte Bereich wird unterstützt.

ONNX-Export eines MLP 6: