| import argparse |
| import os |
|
|
| import numpy as np |
| import onnx |
| import onnxruntime |
| import torch |
| from monai.networks.nets import FlexibleUNet |
|
|
| device = torch.device("cuda" if torch.cuda.is_available() else "cpu") |
|
|
|
|
| def load_model_and_export( |
| modelname, outname, out_channels, height, width, multigpu=False, in_channels=3, backbone="efficientnet-b0" |
| ): |
| """ |
| Loading a model by name. |
| |
| Args: |
| modelname: a whole path name of the model that need to be loaded. |
| outname: a name for output onnx model. |
| out_channels: output channels, which usually equals to 1 + class_number. |
| height: input images' height. |
| width: input images' width. |
| multigpu: if the pre-trained model trained on a multigpu environment. |
| in_channels: input images' channel number. |
| backbone: a name of backbone used by the flexible unet. |
| """ |
| isopen = os.path.exists(modelname) |
| if not isopen: |
| raise Exception("The specified model to load does not exist!") |
|
|
| model = FlexibleUNet( |
| in_channels=in_channels, |
| out_channels=out_channels, |
| backbone=backbone, |
| is_pad=False, |
| pretrained=False, |
| dropout=None, |
| ) |
|
|
| if multigpu: |
| model = torch.nn.DataParallel(model) |
| model = model.cuda() |
| model.load_state_dict(torch.load(modelname, map_location=device)) |
| model = model.eval() |
|
|
| np.random.seed(0) |
| x = np.random.random((1, 3, width, height)) |
| x = torch.tensor(x, dtype=torch.float32) |
| x = x.cuda() |
| torch_out = model(x) |
| input_names = ["INPUT__0"] |
| output_names = ["OUTPUT__0"] |
| |
| if multigpu: |
| model_trans = model.module |
| else: |
| model_trans = model |
| torch.onnx.export( |
| model_trans, |
| x, |
| outname, |
| export_params=True, |
| verbose=True, |
| do_constant_folding=True, |
| input_names=input_names, |
| output_names=output_names, |
| opset_version=15, |
| dynamic_axes={"INPUT__0": {0: "batch_size"}, "OUTPUT__0": {0: "batch_size"}}, |
| ) |
| onnx_model = onnx.load(outname) |
| onnx.checker.check_model(onnx_model, full_check=True) |
| ort_session = onnxruntime.InferenceSession(outname) |
|
|
| def to_numpy(tensor): |
| return tensor.detach().cpu().numpy() if tensor.requires_grad else tensor.cpu().numpy() |
|
|
| |
| ort_inputs = {ort_session.get_inputs()[0].name: to_numpy(x)} |
| ort_outs = ort_session.run(["OUTPUT__0"], ort_inputs) |
| numpy_torch_out = to_numpy(torch_out) |
| |
| np.testing.assert_allclose(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!") |
|
|
|
|
| if __name__ == "__main__": |
| parser = argparse.ArgumentParser() |
| |
| parser.add_argument( |
| "--model", type=str, default=r"/workspace/models/model.pt", help="Input an existing model weight" |
| ) |
|
|
| |
| parser.add_argument( |
| "--outpath", type=str, default=r"/workspace/models/model.onnx", help="A path to save the onnx model." |
| ) |
|
|
| parser.add_argument("--width", type=int, default=736, help="Width for exporting onnx model.") |
|
|
| parser.add_argument("--height", type=int, default=480, help="Height for exporting onnx model.") |
|
|
| parser.add_argument( |
| "--out_channels", type=int, default=2, help="Number of expected out_channels in model for exporting to onnx." |
| ) |
|
|
| parser.add_argument("--multigpu", type=bool, default=False, help="If loading model trained with multi gpu.") |
|
|
| args = parser.parse_args() |
| modelname = args.model |
| outname = args.outpath |
| out_channels = args.out_channels |
| height = args.height |
| width = args.width |
| multigpu = args.multigpu |
|
|
| if os.path.exists(outname): |
| raise Exception( |
| "The specified outpath already exists! Change the outpath to avoid overwriting your saved model. " |
| ) |
| model = load_model_and_export(modelname, outname, out_channels, height, width, multigpu) |
|
|