Repository: intel/ipex-llm
Pull request: Nano : add length check in trace
fix https://github.com/intel-analytics/BigDL/issues/4970

Problem statement:
Nano : length check in trace and friendly warning
When I experiment on BST, the dataloader contains a useless redundant input, then in Trainer.trace, the length of self._forward_args is shorter than inputs, because in torch.onnx.export, the graph does not contain redundant input.
We may add some length check and provide some warnings for user.

Below is the test code for torch.onnx.export.
```
# Super Resolution model definition in PyTorch
import torch.nn as nn
import torch.nn.init as init
import torch
import numpy as np
import onnx
import onnxruntime


class SuperResolutionNet(nn.Module):
    def __init__(self, upscale_factor, inplace=False):
        super(SuperResolutionNet, self).__init__()

        self.relu = nn.ReLU(inplace=inplace)
        self.conv1 = nn.Conv2d(1, 64, (5, 5), (1, 1), (2, 2))
        self.conv2 = nn.Conv2d(64, 64, (3, 3), (1, 1), (1, 1))
        self.conv3 = nn.Conv2d(64, 32, (3, 3), (1, 1), (1, 1))
        self.conv4 = nn.Conv2d(32, upscale_factor ** 2, (3, 3), (1, 1), (1, 1))
        self.pixel_shuffle = nn.PixelShuffle(upscale_factor)

        self._initialize_weights()

    def forward(self, z, x, y):
        x = self.relu(self.conv1(x))
        x = self.relu(self.conv2(x))
        x = self.relu(self.conv3(x))
        x = self.pixel_shuffle(self.conv4(x))
        return x

    def _initialize_weights(self):
        init.orthogonal_(self.conv1.weight, init.calculate_gain('relu'))
        init.orthogonal_(self.conv2.weight, init.calculate_gain('relu'))
        init.orthogonal_(self.conv3.weight, init.calculate_gain('relu'))
        init.orthogonal_(self.conv4.weight)

# Create the super-resolution model by using the above model definition.
torch_model = SuperResolutionNet(upscale_factor=3)

# Input to the model
batch_size = 4
x = torch.randn(batch_size, 1, 224, 224, requires_grad=True)
y =  torch.randn(batch_size, 1, 224, 224, requires_grad=True)
z =  torch.randn(batch_size, 1, 224, 224, requires_grad=True)
torch_out = torch_model(z, x, y)

# Export the model
torch.onnx.export(torch_model,               # model being run
                  (z, x, y),                         # model input (or a tuple for multiple inputs)
                  "super_resolution.onnx",   # where to save the model (can be a file or file-like object)
                  export_params=True,        # store the trained parameter weights inside the model file
                  opset_version=10,          # the ONNX version to export the model to
                  do_constant_folding=True,  # whether to execute constant folding for optimization
                  input_names = ['input', 'y'],   # the model's input names
                  output_names = ['output'], # the model's output names
                  dynamic_axes={'input' : {0 : 'batch_size'},    # variable length axes
                                'output' : {0 : 'batch_size'}},
                  verbose=True
                  )

onnx_model = onnx.load("super_resolution.onnx")
onnx.checker.check_model(onnx_model)
ort_session = onnxruntime.InferenceSession("super_resolution.onnx")

def to_numpy(tensor):
    return tensor.detach().cpu().numpy() if tensor.requires_grad else tensor.cpu().numpy()

# compute ONNX Runtime output prediction
# ort_inputs = {ort_session.get_inputs()[0].name: to_numpy(x)}
forward_args = list(map(lambda x: x.name, ort_session.get_inputs()))
print(forward_args)
inputs = [to_numpy(x)]
ort_inputs = dict(zip(forward_args, inputs))
print(ort_inputs.keys())
ort_outs = ort_session.run(None, ort_inputs)

# compare ONNX Runtime and PyTorch results
np.testing.assert_allclose(to_numpy(torch_out), ort_outs[0], rtol=1e-03, atol=1e-05)

print("Exported model has been tested with ONNXRuntime, and the result looks good!")
```

Hints from issue discussion:

Commit message: fix

Patch to review:
diff --git a/python/nano/src/bigdl/nano/deps/onnxruntime/core/onnxruntime_model.py b/python/nano/src/bigdl/nano/deps/onnxruntime/core/onnxruntime_model.py
index f9d4620532d..b144aae8407 100644
--- a/python/nano/src/bigdl/nano/deps/onnxruntime/core/onnxruntime_model.py
+++ b/python/nano/src/bigdl/nano/deps/onnxruntime/core/onnxruntime_model.py
@@ -30,6 +30,9 @@ def forward_step(self, *inputs):
         '''
         This function run through the onnxruntime forwarding step
         '''
+        if len(self._forward_args) < len(inputs):
+            invalidInputError(False, "The length of inputs is inconsistent with the length of "
+                                     "ONNXRuntime's inputs, there may be some redundant inputs.")
         inputs = dict(zip(self._forward_args, inputs))
         ort_outs = self.ortsess.run(None, inputs)
         return ort_outs
diff --git a/python/nano/src/bigdl/nano/pytorch/trainer/Trainer.py b/python/nano/src/bigdl/nano/pytorch/trainer/Trainer.py
index fcc397aca07..faec6f958a5 100644
--- a/python/nano/src/bigdl/nano/pytorch/trainer/Trainer.py
+++ b/python/nano/src/bigdl/nano/pytorch/trainer/Trainer.py
@@ -321,7 +321,7 @@ def quantize(model,  # remove the type requirement for type checking
         elif accelerator == 'openvino':
             model_type = type(model).__name__
             if not model_type == 'PytorchOpenVINOModel':
-                if not input_sample:
+                if input_sample is None:
                     # input_sample can be a dataloader
                     input_sample = calib_dataloader
                 model = Trainer.trace(model,


Code review comments:
diff_hunk: @@ -30,6 +30,9 @@ def forward_step(self, *inputs):
         '''
         This function run through the onnxruntime forwarding step
         '''
+        if len(self._forward_args) < len(inputs):
+            invalidInputError(False, "The length of inputs is inconsistent with the length of "
path: python/nano/src/bigdl/nano/deps/onnxruntime/core/onnxruntime_model.py
text: @user1:
invalidInputError(len(self._forward_args) >= len(inputs), ...)

@author:
done