def ResNet50(framework: Framework, version: str = '1', enable_trt=False): """Export, generate model family and register ResNet50 Arguments: framework (Framework): Framework name. version (str): Model version. enable_trt (bool): Flag for enabling TRT conversion. """ if framework == Framework.TENSORFLOW: model = tf.keras.applications.ResNet50() # converting to trt if not enable_trt: tfs_dir = generate_path(model_name='ResNet50', framework=framework, task=Task.IMAGE_CLASSIFICATION, engine=Engine.TFS, version=str(version)) TFSConverter.from_tf_model(model, tfs_dir) model = str(tfs_dir.with_suffix('.zip')) register_model( model, dataset='imagenet', metric={Metric.ACC: 0.76}, task=Task.IMAGE_CLASSIFICATION, inputs=[ IOShape([-1, 224, 224, 3], dtype=float, name='input_1', format=ModelInputFormat.FORMAT_NHWC) ], outputs=[IOShape([-1, 1000], dtype=float, name='probs')], architecture='ResNet50', framework=framework, version=ModelVersion(version), convert=enable_trt, ) elif framework == Framework.PYTORCH: model = models.resnet50(pretrained=True) register_model( model, dataset='imagenet', metric={Metric.ACC: 0.76}, task=Task.IMAGE_CLASSIFICATION, inputs=[ IOShape([-1, 3, 224, 224], dtype=float, name='INPUT__0', format=ModelInputFormat.FORMAT_NCHW) ], outputs=[IOShape([-1, 1000], dtype=float, name='probs')], architecture='ResNet50', framework=framework, version=ModelVersion(version), ) else: raise ValueError('Framework not supported.')
def setUpClass(cls): cls.torch_model = torchvision.models.alexnet(pretrained=True) cls.inputs = [ IOShape([1, 3, 224, 224], dtype=float, name='INPUT__0', format=ModelInputFormat.FORMAT_NCHW) ] cls.outputs = [IOShape([-1, 1000], dtype=float, name='probs')] cls.X = torch.rand(1, 3, 224, 224, dtype=torch.float32) cls.onnx_path = Path(tempfile.gettempdir() + '/test.onnx') cls.torchscript_path = Path(tempfile.gettempdir() + '/test_torchscript.zip')
def test_onnx_to_pytorch(): onnx_model = ONNXConverter.from_sklearn(sklearn_model, inputs_bc, optimize=False) inputs = list() for input_ in onnx_model.graph.input: name = input_.name t = input_.type.tensor_type shape = list() if t.HasField('shape'): for d in t.shape.dim: if d.HasField('dim_value'): shape.append(d.dim_value) elif d.HasField('dim_param'): shape.append(d.dim_param) else: shape.append(-1) dtype = t.elem_type inputs.append(IOShape(name=name, dtype=dtype, shape=shape)) dtype = model_data_type_to_torch(inputs[0].dtype) sample_input = torch.rand([2, *inputs[0].shape[1:]], dtype=dtype) model = PyTorchConverter.from_onnx(onnx_model) model(sample_input)
def test_register_model(): model = ModelBO('ResNet50', framework=Framework.PYTORCH, engine=Engine.TRT, version=ModelVersion(1), dataset='ImageNet', acc=0.8, task='image classification', inputs=[ IOShape([-1, 3, 224, 224], dtype=float, format=ModelInputFormat.FORMAT_NCHW) ], outputs=[IOShape([-1, 1000], dtype=int)], weight=Weight(bytes([123]))) assert ModelService.post_model(model)
def setUpClass(cls): cls.keras_model = tf.keras.applications.ResNet50() cls.sample_input = np.random.random((1, 224, 224, 3)).astype('float32') cls.keras_model_predict = cls.keras_model.predict(cls.sample_input) cls.tfs_model_path = Path( tempfile.gettempdir() + '/ResNet50/tensorflow-tfs/image_classification/1') cls.trt_model_path = Path( tempfile.gettempdir() + '/ResNet50/tensorflow-trt/image_classification/1') cls.inputs = [ IOShape([-1, 224, 224, 3], dtype=float, name='input_1', format=ModelInputFormat.FORMAT_NHWC) ] cls.outputs = [IOShape([-1, 1000], dtype=float, name='probs')]
def test_register_model(): model = ModelBO('ResNet50', framework=Framework.PYTORCH, engine=Engine.TRT, version=ModelVersion(1), dataset='ImageNet', metric={Metric.ACC: 0.80}, task=Task.IMAGE_CLASSIFICATION, inputs=[ IOShape([-1, 3, 224, 224], dtype=float, format=ModelInputFormat.FORMAT_NCHW) ], outputs=[IOShape([-1, 1000], dtype=int)], weight=Weight(bytes([123]))) assert ModelService.post_model(model)
def setUpClass(cls): num_classes = 2 X = np.random.rand(100000, 28).astype(np.float32) y = np.random.randint(num_classes, size=100000) cls.lgbm_model = lgb.LGBMClassifier() cls.lgbm_model.fit(X, y) cls.inputs = [IOShape(shape=[-1, 28], dtype=X.dtype, name='input_0')] cls.sample_input = X[0:2, :] cls.lgbm_model_out = cls.lgbm_model.predict(cls.sample_input) cls.lgbm_model_probs = cls.lgbm_model.predict_proba(cls.sample_input)
def setUpClass(cls): X_bc, y_bc = load_breast_cancer(return_X_y=True) nrows = 15000 X_bc: np.ndarray = X_bc[0:nrows] y_bc: np.ndarray = y_bc[0:nrows] model = lgb.LGBMRegressor(n_estimators=3, min_child_samples=1) model.fit(X_bc, y_bc) inputs_bc = [IOShape(shape=[-1, X_bc.shape[1]], dtype=float, name='input_0')] cls.onnx_model = convert(model, 'lightgbm', 'onnx', inputs=inputs_bc, optimize=False) sess = rt.InferenceSession(cls.onnx_model.SerializeToString()) cls.sample_input = torch.rand(2, X_bc.shape[1], dtype=torch.float32) cls.onnx_model_predict = sess.run(None, {'input_0': cls.sample_input.numpy()})[0].flatten()
def setUpClass(cls): X_bc, y_bc = load_breast_cancer(return_X_y=True) nrows = 15000 X_bc: np.ndarray = X_bc[0:nrows] y_bc: np.ndarray = y_bc[0:nrows] sklearn_model = RandomForestClassifier(n_estimators=10, max_depth=10) sklearn_model.fit(X_bc, y_bc) inputs_bc = [IOShape(shape=[-1, X_bc.shape[1]], dtype=float, name='input_0')] cls.onnx_model = convert(sklearn_model, 'sklearn', 'onnx', inputs=inputs_bc, optimize=False) sess = rt.InferenceSession(cls.onnx_model.SerializeToString()) cls.sample_input = torch.rand(2, X_bc.shape[1], dtype=torch.float32) cls.onnx_model_predict = sess.run(None, {'input_0': cls.sample_input.numpy()})[0].flatten()
def convert_ioshape_plain_to_ioshape(ioshape_plain): """Convert IOShape-like dictionary to IOShape. """ # unpack i, ioshape_plain = ioshape_plain assert isinstance(ioshape_plain['shape'], Iterable), \ f'inputs[{i}].shape expected to be iterable, but got {ioshape_plain["shape"]}' assert isinstance(ioshape_plain['dtype'], str), \ f'inputs[{i}].dtype expected to be a `DataType`, but got {ioshape_plain["dtype"]}.' ioshape_plain['dtype'] = DataType[ioshape_plain['dtype']] return IOShape(**ioshape_plain)
def ResNet101(framework: Framework, version: str = "1"): if framework == Framework.TENSORFLOW: model = tf.keras.applications.ResNet101() register_model( model, dataset='imagenet', acc=..., # TODO: to be filled task='image classification', inputs=[ IOShape([-1, 224, 224, 3], dtype=float, name='input_1', format=ModelInputFormat.FORMAT_NHWC) ], outputs=[IOShape([-1, 1000], dtype=float, name='probs')], architecture='ResNet101', framework=framework, version=ModelVersion(version)) elif framework == Framework.PYTORCH: model = models.resnet101(pretrained=True) register_model( model, dataset='imagenet', acc=..., # TODO task='image classification', inputs=[ IOShape([-1, 3, 224, 224], dtype=float, name='INPUT__0', format=ModelInputFormat.FORMAT_NCHW) ], outputs=[IOShape([-1, 1000], dtype=float, name='probs')], architecture='ResNet101', framework=framework, version=ModelVersion(version)) else: raise ValueError('Framework not supported.')
def setUpClass(cls): X_bc, y_bc = load_breast_cancer(return_X_y=True) nrows = 15000 X_bc: np.ndarray = X_bc[0:nrows] y_bc: np.ndarray = y_bc[0:nrows] cls.sklearn_model = RandomForestClassifier(n_estimators=10, max_depth=10) cls.sklearn_model.fit(X_bc, y_bc) cls.inputs_bc = [ IOShape(shape=[-1, X_bc.shape[1]], dtype=float, name='input_0') ] cls.sample_input = X_bc[0:2, :].astype(np.float32) cls.sklearn_model_out = cls.sklearn_model.predict(cls.sample_input) cls.sklearn_model_probs = cls.sklearn_model.predict_proba( cls.sample_input)
def update_finetune_model_as_new(id: str, updated_layer: Structure, dry_run: bool = False): # noqa """ Temporary function for finetune CV models. The function's functionality is overlapped with `update_model_structure_as_new`. Please use the `update_model_structure_as_new` in next release. Examples: Fine-tune the model by modify the layer with name 'fc' (last layer). The layer has a changed argument out_features = 10. op_='M' indicates the operation to this layer ('fc') is 'Modify'. There is no changes in layer connections. Therefore, the structure change summary is [M] fc: (...) out_features=10 >>> from collections import OrderedDict >>> structure_data = { ... 'layer': OrderedDict({'fc': {'out_features': 10, 'op_': 'M', 'type_': 'torch.nn.Linear'}}) ... } >>> update_finetune_model_as_new(id=..., updated_layer=Structure.parse_obj(structure_data)) Args: id (str): ID of the model to be updated. updated_layer (Structure): Contains layers to be fine-tuned. dry_run (bool): Test run for verify if the provided parameter (i.e. model specified in `id` and updated layers) is valid. Returns: """ if len(updated_layer.layer.items()) == 0: return True model = ModelService.get_model_by_id(id) if model.engine != Engine.PYTORCH: raise ValueError(f'model {id} is not supported for editing. ' f'Currently only support model with engine=PYTORCH') # download model as local cache cache_path = get_remote_model_weight(model=model) net = torch.load(cache_path) for layer_name, layer_param in updated_layer.layer.items(): layer_op = getattr(layer_param, 'op_') # update layer if layer_op == Operation.MODIFY: # check if the layer name exists # TODO check if layer path exists eg."layer1.0.conv1" if not hasattr(net, layer_name): raise ModelStructureError( f'Structure layer name `{layer_name}` not found in model {id}.' ) net_layer = getattr(net, layer_name) # check if the provided type matches the original type layer_type = type(net_layer) layer_type_provided = eval(layer_param.type_.value) # nosec if layer_type is not layer_type_provided: raise ModelStructureError( f'Expect `{layer_name}.type_` to be {layer_type}, ' f'but got {layer_type_provided}') # get layer parameters layer_param_old = layer_param.parse_layer_obj(net_layer) layer_param_data = layer_param_old.dict(exclude_none=True, exclude={'type_', 'op_'}) layer_param_update_data = layer_param.dict( exclude_none=True, exclude={'type_', 'op_'}) # replace 'null' with None. See reason :class:`ModelLayer`. for k, v in layer_param_update_data.items(): if v == 'null': layer_param_update_data[k] = None # update the layer parameters layer_param_data.update(layer_param_update_data) layer = layer_type(**layer_param_data) setattr(net, layer_name, layer) else: # if layer_op is Operation.ADD, # 1. check if the layer name not exists # 2. add a layer # 3. change the `forward` function according to the connections # if layer_op is Operation.DELETE, # 1. check if the layer exists # 2. delete the layer # 3. change the `forward` function raise ValueError( 'Operation not permitted. Please use `update_model_structure_as_new`.' ) input_tensors = list() bs = 1 for input_ in model.inputs: input_tensor = torch.rand(bs, *input_.shape[1:]).type( model_data_type_to_torch(input_.dtype)) input_tensors.append(input_tensor) # parse output tensors output_shapes = list() output_tensors = net(*input_tensors) if not isinstance(output_tensors, (list, tuple)): output_tensors = (output_tensors, ) for output_tensor in output_tensors: output_shape = IOShape(shape=[bs, *output_tensor.shape[1:]], dtype=type_to_data_type(output_tensor.dtype)) output_shapes.append(output_shape) if not dry_run: # TODO return validation result for dry_run mode # TODO apply Semantic Versioning https://semver.org/ # TODO reslove duplicate model version problem in a more efficient way version = ModelVersion(model.version.ver + 1) previous_models = ModelService.get_models( architecture=model.architecture, task=model.task, framework=model.framework, engine=Engine.NONE) if len(previous_models): last_version = max(previous_models, key=lambda k: k.version.ver).version.ver version = ModelVersion(last_version + 1) saved_path = generate_path_plain(architecture=model.architecture, task=model.task, framework=model.framework, engine=Engine.NONE, version=version) saved_path.parent.mkdir(parents=True, exist_ok=True) torch.save(model, saved_path.with_suffix('.pt')) mlmodelin = MLModel(dataset='', metric={key: 0 for key in model.metric.keys()}, task=model.task, inputs=model.inputs, outputs=output_shapes, architecture=model.name, framework=model.framework, engine=Engine.NONE, model_status=[ModelStatus.DRAFT], parent_model_id=model.id, version=version, weight=saved_path) register_model(mlmodelin, convert=False, profile=False) model_bo = ModelService.get_models(architecture=model.architecture, task=model.task, framework=model.framework, engine=Engine.NONE, version=version)[0] return {'id': model_bo.id}
import numpy as np import onnx import onnxruntime import torch import xgboost as xgt from sklearn.datasets import load_breast_cancer from sklearn.ensemble import RandomForestClassifier from modelci.hub.converter import PyTorchConverter, ONNXConverter from modelci.types.bo import IOShape from modelci.types.type_conversion import model_data_type_to_torch num_classes = 2 X = np.random.rand(100000, 28).astype(np.float32) y = np.random.randint(num_classes, size=100000) inputs = [IOShape(shape=[-1, 28], dtype=X.dtype, name='input_0')] xgboost_model = xgt.XGBRegressor() xgboost_model.fit(X, y) lgbm_model = lgb.LGBMClassifier() lgbm_model.fit(X, y) X_bc, y_bc = load_breast_cancer(return_X_y=True) nrows = 15000 X_bc: np.ndarray = X_bc[0:nrows] y_bc: np.ndarray = y_bc[0:nrows] sklearn_model = RandomForestClassifier(n_estimators=10, max_depth=10) sklearn_model.fit(X_bc, y_bc) inputs_bc = [