Пример #1
0
def save(model_in: MLModel):
    """Register a model into ModelDB and GridFS. `model.id` should be set as `None`, otherwise, the function will
    raise a `ValueError`.

    Args:
        model_in (MLModelIn): model object to be registered

    Return:
        MLModel: Saved ML model object.

    Raises:
        BadRequestValueException: If `model.id` is not None.
        ServiceException: If model has exists with the same primary keys (name, framework, engine and version).
    """

    if _collection.count_documents(
            filter=model_in.dict(
                use_enum_values=True,
                include={'architecture', 'framework', 'engine', 'version', 'task', 'dataset'}
            ),
            limit=1
    ):
        raise ServiceException(
            f'Model with primary keys architecture={model_in.architecture}, '
            f'framework={model_in.framework}, engine={model_in.engine}, version={model_in.version},'
            f'task={model_in.task}, and dataset={model_in.dataset} has exists.'
        )

    # TODO: update weight ID in the MLModelIn
    weight_id = _fs.put(bytes(model_in.weight), filename=model_in.weight.filename)
    model = MLModel(**model_in.dict(exclude={'weight'}), weight=weight_id)
    model.id = _collection.insert_one(model.dict(exclude_none=True, by_alias=True, use_enum_values=True)).inserted_id
    return model
Пример #2
0
def _generate_model_family(
        model: MLModel,
        max_batch_size: int = -1
):
    net = load(model.saved_path)
    build_saved_dir_from_engine = partial(
        generate_path_plain,
        **model.dict(include={'architecture', 'framework', 'task', 'version'}),
    )
    inputs = model.inputs
    outputs = model.outputs
    model_input = model.model_input

    generated_dir_list = list()

    torchscript_dir = build_saved_dir_from_engine(engine=Engine.TORCHSCRIPT)
    tfs_dir = build_saved_dir_from_engine(engine=Engine.TFS)
    onnx_dir = build_saved_dir_from_engine(engine=Engine.ONNX)
    trt_dir = build_saved_dir_from_engine(engine=Engine.TRT)

    if isinstance(net, torch.nn.Module):
        # to TorchScript
        if converter.convert(net, 'pytorch', 'torchscript', save_path=torchscript_dir):
            generated_dir_list.append(torchscript_dir.with_suffix('.zip'))

        # to ONNX, TODO(lym): batch cache, input shape, opset version
        if converter.convert(net, 'pytorch', 'onnx', save_path=onnx_dir, inputs=inputs, outputs=outputs, model_input=model_input, optimize=False):
            generated_dir_list.append(onnx_dir.with_suffix('.onnx'))

        # to TRT
        # TRTConverter.from_onnx(
        #     onnx_path=onnx_dir.with_suffix('.onnx'), save_path=trt_dir, inputs=inputs, outputs=outputs
        # )

    elif isinstance(net, tf.keras.Model):
        # to TFS
        converter.convert(net, 'tensorflow', 'tfs', save_path=tfs_dir)
        generated_dir_list.append(tfs_dir.with_suffix('.zip'))

        # to TRT
        converter.convert(net, 'tfs', 'trt', tf_path=tfs_dir, save_path=trt_dir, inputs=inputs, outputs=outputs, max_batch_size=32)
        generated_dir_list.append(trt_dir.with_suffix('.zip'))

    return generated_dir_list
Пример #3
0
def generate_model_family(model: MLModel, max_batch_size: int = -1):
    model_weight_path = model.saved_path
    if not Path(model.saved_path).exists():
        (filepath, filename) = os.path.split(model.saved_path)
        os.makedirs(filepath)
        with open(model.saved_path, 'wb') as f:
            f.write(model.weight.__bytes__())
    net = load(model_weight_path)
    build_saved_dir_from_engine = partial(
        generate_path_plain,
        **model.dict(include={'architecture', 'framework', 'task', 'version'}),
    )
    inputs = model.inputs
    outputs = model.outputs
    model_input = model.model_input

    generated_dir_list = list()

    torchscript_dir = build_saved_dir_from_engine(engine=Engine.TORCHSCRIPT)
    tfs_dir = build_saved_dir_from_engine(engine=Engine.TFS)
    onnx_dir = build_saved_dir_from_engine(engine=Engine.ONNX)
    trt_dir = build_saved_dir_from_engine(engine=Engine.TRT)

    if isinstance(net, torch.nn.Module):
        _torchfamily(net, False, torchscript_dir, onnx_dir, generated_dir_list,
                     inputs, outputs, model_input)
    elif isinstance(net, tf.keras.Model):
        _tffamily(net, tfs_dir, generated_dir_list, trt_dir, inputs, outputs)
    elif isinstance(net, xgb.XGBModel):
        _xgbfamily(net, inputs, onnx_dir, generated_dir_list, torchscript_dir,
                   outputs, model_input)
    elif isinstance(net, lgb.LGBMModel):
        _lgbfamily(net, inputs, onnx_dir, generated_dir_list, torchscript_dir,
                   outputs, model_input)
    elif isinstance(net, skl.base.BaseEstimator):
        _sklfamily(net, inputs, onnx_dir, generated_dir_list, torchscript_dir,
                   outputs, model_input)
    return generated_dir_list
Пример #4
0
def register_model(
    model: MLModel,
    convert: bool = True,
    profile: bool = True,
) -> List[MLModel]:
    """Upload a model to ModelDB.
    This function will upload the given model into the database with some variation. It may optionally generate a
        branch of models (i.e. model family) with different optimization techniques. Besides, a benchmark will be
        scheduled for each generated model, in order to gain profiling results for model selection strategies.
        In the `no_generate` model(i.e. `no_generate` flag is set to be `True`), `architecture`, `framework`, `engine`
        and `version` could be None. If any of the above arguments is `None`, all of them will be auto induced
        from the origin_model path. An `ValueError` will be raised if the mata info cannot be induced.

    TODO:
        This function has a super comprehensive logic, need to be simplified.

    Arguments:
        model: Required inputs for register a model. All information is wrapped in such model.
        convert (bool): Flag for generation of model family. Default to True.
        profile (bool): Flag for profiling uploaded (including converted) models. Default to True.
    """
    models = list()

    model_dir_list = list()
    model.model_status = [ModelStatus.PUBLISHED]
    models.append(save(model))

    # generate model family
    if convert:
        model_dir_list.extend(converter.generate_model_family(model))

    # register
    model_data = model.dict(exclude={'weight', 'id', 'model_status', 'engine'})
    for model_dir in model_dir_list:
        parse_result = parse_path_plain(model_dir)
        engine = parse_result['engine']

        model_cvt = MLModel(**model_data,
                            weight=model_dir,
                            engine=engine,
                            model_status=[ModelStatus.CONVERTED])
        models.append(save(model_cvt))

    # profile registered model
    if profile:
        from modelci.controller import job_executor
        from modelci.controller.executor import Job

        file = tf.keras.utils.get_file(
            "grace_hopper.jpg",
            "https://storage.googleapis.com/download.tensorflow.org/example_images/grace_hopper.jpg"
        )
        test_img_bytes = cv2.imread(file)

        kwargs = {
            'repeat_data': test_img_bytes,
            'batch_size': 32,
            'batch_num': 100,
            'asynchronous': False,
        }

        for model in models:
            model.model_status = [ModelStatus.PROFILING]
            ModelService.update_model(model)
            kwargs['model_info'] = model
            engine = model.engine

            if engine == Engine.TORCHSCRIPT:
                client = CVTorchClient(**kwargs)
            elif engine == Engine.TFS:
                client = CVTFSClient(**kwargs)
            elif engine == Engine.ONNX:
                client = CVONNXClient(**kwargs)
            elif engine == Engine.TRT:
                client = CVTRTClient(**kwargs)
            else:
                raise ValueError(f'No such serving engine: {engine}')

            job_cuda = Job(client=client, device='cuda:0', model_info=model)
            # job_cpu = Job(client=client, device='cpu', model_info=model)
            job_executor.submit(job_cuda)
            # job_executor.submit(job_cpu)

    return models