Taking a MXNet model from training and deploying it in production poses several challenges to the users. Most important problems raised by users are:
Example symbol file after proposed update update. "inputs" and "outputs" are the new parameters proposed in this work.
{
"nodes": [..........]
"arg_nodes": [0, 1, 2, 4, 5],
"node_row_ptr": [0, 1, 2, 3, 4, 5, 6, 7, 8],
"heads": [[7, 0, 0]],
"attrs": {"mxnet_version": ["int", 10301]
"inputs": {"data":[1,3,224,224]},
"outputs" : {"softmax_label":[1,10]
}
} |
Inputs and outputs are dictionary where key => Name of the node and value => shape. Though single input, single output models are more common, a model can be of following variations:
# Trained network net = mx.gluon.model_zoo.vision.resnet18_v1(pretrained=True, ctx=mx.cpu()) # Data transformations applicable during inference inference_input_transforms = gluon.nn.HybridSequential() inference_input_transforms.add(transforms.Resize((224, 224))) inference_input_transforms.add(transforms.ToTensor()) inference_input_transforms.add(transforms.Normalize(mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225))) # Export the model. Cannot export data transformation and input/output signature net.export(path="./my_model", epoch=0) |
# Load the model. Model does not contain transformation and input/output signature
net = SymbolBlock.imports(symbol_file="my_model-symbol.json",
input_names=["data"],
param_file="my_model-0000.params",
ctx=mx.cpu()) |
sym, arg_params, aux_params = mx.model.load_checkpoint('my_model', 0)
mod = mx.mod.Module(symbol=sym, context=ctx, label_names=None)
mod.bind(for_training=False, data_shapes=[('data', (1,3,224,224))],
label_shapes=mod._label_shapes)
mod.set_params(arg_params, aux_params, allow_missing=True)
mod.forward(...) |
Shape inputShape = new Shape(new int[] {1,3,224,224});
DataDesc inputDescriptor = new DataDesc("data", inputShape, DType.Float32(), "NCHW");
List<DataDesc> inputDescList = new ArrayList<DataDesc>();
inputDescList.add(inputDescriptor);
List<Context> context = new ArrayList<>();
context.add(Context.cpu());
String modelPathPrefix = "path-to-model";
Predictor predictor = new Predictor(modelPathPrefix, inputDescList, context);
List<NDArray> result = predictor.predictWithNDArray(inputNDArray); |
# Trained network
net = mx.gluon.model_zoo.vision.resnet18_v1(pretrained=True, ctx=mx.cpu())
# Data transformations applicable during inference
inference_input_transforms = gluon.nn.HybridSequential()
inference_input_transforms.add(transforms.Resize((224, 224)))
inference_input_transforms.add(transforms.ToTensor())
inference_input_transforms.add(transforms.Normalize(mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225)))
# Export the model
gluon.contrib.utils.export(net, path="./my_model",
epoch=0,
signature={constants.INPUT_DESC:[("data", (1,3,224,224))],
constants.OUTPUT_DESC:[("softmax_label", (1,10))]},
input_transforms={"data":inference_input_transforms},
output_transforms=None) |
# Load the model along with the transformations
net = gluon.contrib.utils.import(symbol_file="my_model-symbol.json",
param_file="my_model-0000.params",
load_transforms = True,
ctx = 'cpu')
# Prediction
pred = net(data) |
(Supported to create a module for inference only)
# Load the model along with the transformations. Can be used only for inference (forward())
mod = mx.contrib.Module.import(
symbol_file = "my_model-symbol.json",
param_file = "my_model-0000.params",
load_transforms = True,
ctx = 'cpu',
batch_size = 1)
# Prediction
mod.forward(...) |
List<Context> context = new ArrayList<>(); context.add(Context.cpu()); String modelPathPrefix = "my_model"; # Load the model along with the transformations. Predictor predictor = new Predictor(modelPathPrefix, context, load_transforms=True); # Inference List<NDArray> result = predictor.predictWithNDArray(inputNDArray); |
During inference, initial benchmarks shows a noticeable performance gain with End to end models i.e., a model with data transformations and neural network all fused as a single model graph.
0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225))| A | B | C | Non End to End Models (ms) | End to End Models (ms) | Boost % |
|---|---|---|---|---|---|
| CPU (C5.2X) | Single Request Inference | Python (Module API) | 17 | 14 | 17.65% |
| Java Inference APIs | 17.09 | 14.16 | 17.14% | ||
| Scala Inference APIs | 17.93 | 13.19 | 26.44% | ||
| Batch Inference (Batch size = 25) | Python (Module API) | 15.18 | 12.57 | 17.19% | |
| Java Inference APIs | 18.54 | 13 | 29.88% | ||
| Scala Inference APIs | 17 | 13.26 | 22.00% | ||
| GPU (P3.16X) | Single Request Inference | Python (Module API) | 5.78 | 3.14 | 45.67% |
| Java Inference APIs | 8.95 | 4.26 | 52.40% | ||
| Scala Inference APIs | 9.14 | 4.42 | 51.64% | ||
| Batch Inference (Batch size = 25) | Python (Module API) | 2.61 | 1.31 | 49.81% | |
| Java Inference APIs | 8.03 | 5.53 | 31.13% | ||
| Scala Inference APIs | 7.86 | 5.52 | 29.77% |