RuntimeManager#
RuntimeManager 将 MSCGraph(s) 与不同的框架连接起来,它封装了一些常用的方法并管理 MSCTools。
from tvm.contrib.msc.core.transform import msc_transform
from tvm.contrib.msc.core.runtime import create_runtime_manager
from tvm.contrib.msc.core.tools import create_tool, MSC_TOOL
# build runtime manager from module and mscgraphs
optimized_mod, msc_graph, msc_config = msc_transform(mod, params)
rt_manager = create_runtime_manager(optimized_mod, params, msc_config)
rt_manager.create_tool(MSC_TOOL.QUANTIZE, quantize_config)
quantizer = rt_manager.get_tool(MSC_TOOL.QUANTIZE)
rt_manager.load_model()
# calibrate the datas with float model
while not quantizer.calibrated:
for datas in calibrate_datas:
rt_manager.run(datas)
quantizer.calibrate()
quantizer.save_strategy(strategy_file)
# load again the quantized model, without loading the weights
rt_manager.load_model(reuse_weights=True)
outputs = rt_manager.run(sample_datas)
MSCManager将编译流程进行封装,暴露出一个面向用户的接口。使用方式类似:
improt torchvision
from tvm.contrib.msc.pipeline import MSCManager
model = trochvision.models.resnet50()
# define your config
manager = MSCManager(model, config)
runner = manager.run_pipe()
MSCWrapper#
MSCWrapper是对MSCManger的进一步封装,主要作用是将MSC的编译过程变成用户友好的工具连接口。其使用方式和MSCManager基本相同,如
model = TorchWrapper(model, config)
# export to dump meta model
# model.export()
# optimize the model with quantizer(PTQ)
model.optimize()
acc = eval_model(model, testloader, max_iter=args.test_iter)
# train the model with quantizer(QAT)
optimizer = optim.Adam(model.parameters(), lr=0.0000001, weight_decay=0.08)
for ep in range(args.train_epoch):
train_model(model, trainloader, optimizer, max_iter=args.train_iter)
acc = eval_model(model, testloader, max_iter=args.test_iter)
# export to dump checkpoint model
# model.export()
# compile the model
model.compile(bind_params=True)
acc = eval_model(model, testloader, max_iter=args.test_iter)
# export to dump compiled model
# model.export()
使用example尚未合入,合入后更新文档。
MSCWrapper包裹的model保留原model所有的方法,可以用于训练或者评测过程,但调用MSCWrapper.optimize或MSCWrapper.compile之后model已经被替换成了优化之后或编译得到的模型,只在输入输出格式上进行适配支持原始模型对应格式的数据类型。
import json
import pytest
import torch
import tvm.testing
from tvm.contrib.msc.pipeline import MSCManager, TorchDynamic
from tvm.contrib.msc.core.utils.namespace import MSCFramework
from tvm.contrib.msc.core import utils as msc_utils
requires_tensorrt = pytest.mark.skipif(
tvm.get_global_func("relax.ext.tensorrt", True) is None,
reason="TENSORRT is not enabled",
)
def _get_config(model_type, compile_type, inputs, outputs, dynamic=False, atol=1e-1, rtol=1e-1):
"""Get msc config"""
path = "test_pipe_{}_{}_{}".format(model_type, compile_type, "dynamic" if dynamic else "static")
return {
"workspace": msc_utils.msc_dir(path),
"verbose": "info",
"model_type": model_type,
"inputs": inputs,
"outputs": outputs,
"dataset": {"prepare": {"loader": "from_random", "max_iter": 5}},
"prepare": {"profile": {"benchmark": {"repeat": 10}}},
"baseline": {
"run_type": model_type,
"profile": {"check": {"atol": atol, "rtol": rtol}, "benchmark": {"repeat": 10}},
},
"compile": {
"run_type": compile_type,
"profile": {"check": {"atol": atol, "rtol": rtol}, "benchmark": {"repeat": 10}},
},
}
def _get_torch_model(name, training=False):
"""Get model from torch vision"""
# pylint: disable=import-outside-toplevel
try:
import torchvision
model = getattr(torchvision.models, name)()
if training:
model = model.train()
else:
model = model.eval()
return model
except: # pylint: disable=bare-except
print("please install torchvision package")
return None
def _get_tf_graph():
"""Get graph from tensorflow"""
# pylint: disable=import-outside-toplevel
try:
from tvm.contrib.msc.framework.tensorflow import tf_v1
import tvm.relay.testing.tf as tf_testing
tf_graph = tf_v1.Graph()
with tf_graph.as_default():
graph_def = tf_testing.get_workload(
"https://storage.googleapis.com/mobilenet_v2/checkpoints/mobilenet_v2_1.4_224.tgz",
"mobilenet_v2_1.4_224_frozen.pb",
)
# Call the utility to import the graph definition into default graph.
graph_def = tf_testing.ProcessGraphDefParam(graph_def)
return graph_def
except: # pylint: disable=bare-except
print("please install tensorflow package")
return None
def _check_pipeline(pipeline, expected_info, dynamic=False):
"""Check the pipeline results"""
passed, err = True, ""
if not pipeline.report["success"]:
passed = False
err = "Failed to run pipe for {} -> {}".format(pipeline.model_type, pipeline.compile_type)
if not dynamic:
model_info = pipeline.get_runtime().model_info
if not msc_utils.dict_equal(model_info, expected_info):
passed = False
err = "Model info {} mismatch with expected {}".format(model_info, expected_info)
pipeline.destory()
if not passed:
raise Exception("{}\nReport:{}".format(err, json.dumps(pipeline.report, indent=2)))
def _test_from_torch(
compile_type, expected_info, training=False, dynamic=False, atol=1e-1, rtol=1e-1
):
if dynamic and not hasattr(torch, "compile"):
return
torch_model = _get_torch_model("resnet50", training)
if torch_model:
if torch.cuda.is_available():
torch_model = torch_model.to(torch.device("cuda:0"))
config = _get_config(
MSCFramework.TORCH,
compile_type,
inputs=[["input_0", [1, 3, 224, 224], "float32"]],
outputs=["output"],
dynamic=dynamic,
atol=atol,
rtol=rtol,
)
pipeline = TorchDynamic(torch_model, config) if dynamic else MSCManager(torch_model, config)
pipeline.run_pipe()
_check_pipeline(pipeline, expected_info, dynamic)
def _test_from_tf(compile_type, expected_info, atol=1e-2, rtol=1e-2):
graphdef = _get_tf_graph()
if graphdef:
config = _get_config(
MSCFramework.TENSORFLOW,
compile_type,
inputs=[["input", [1, 224, 224, 3], "float32"]],
outputs=["MobilenetV2/Predictions/Reshape_1:0"],
atol=atol,
rtol=rtol,
)
config["compile"]["profile"]["check"]["err_rate"] = -1
manager = MSCManager(graphdef, config)
manager.run_pipe()
_check_pipeline(manager, expected_info)
@pytest.mark.parametrize("dynamic", [False, True])
def test_tvm_pipeline(dynamic):
"""Test pipeline for tvm"""
model_info = {
"inputs": [
{"name": "input_0", "shape": [1, 3, 224, 224], "dtype": "float32", "layout": "NCHW"}
],
"outputs": [{"name": "output", "shape": [1, 1000], "dtype": "float32", "layout": "NC"}],
"nodes": {
"total": 229,
"input": 1,
"nn.conv2d": 53,
"nn.batch_norm": 53,
"get_item": 53,
"nn.relu": 49,
"nn.max_pool2d": 1,
"add": 16,
"nn.adaptive_avg_pool2d": 1,
"reshape": 1,
"msc.linear_bias": 1,
},
}
_test_from_torch(MSCFramework.TVM, model_info, training=False, dynamic=dynamic)
if not dynamic:
model_info = {
"inputs": [
{"name": "input", "shape": [1, 224, 224, 3], "dtype": "float32", "layout": "NHWC"}
],
"outputs": [
{
"name": "MobilenetV2/Predictions/Reshape_1:0",
"shape": [1, 1001],
"dtype": "float32",
"layout": "NC",
}
],
"nodes": {
"total": 138,
"input": 1,
"msc.conv2d_bias": 36,
"clip": 35,
"nn.conv2d": 17,
"nn.batch_norm": 17,
"get_item": 17,
"add": 10,
"nn.avg_pool2d": 1,
"squeeze": 1,
"reshape": 2,
"nn.softmax": 1,
},
}
_test_from_tf(MSCFramework.TVM, model_info)
@pytest.mark.parametrize("dynamic", [False, True])
def test_torch_pipeline(dynamic):
"""Test pipeline for torch"""
model_info = {
"inputs": [
{"name": "input_0", "shape": [1, 3, 224, 224], "dtype": "float32", "layout": "NCHW"}
],
"outputs": [{"name": "output", "shape": [1, 1000], "dtype": "float32", "layout": "NC"}],
"nodes": {
"total": 229,
"input": 1,
"nn.conv2d": 53,
"nn.batch_norm": 53,
"get_item": 53,
"nn.relu": 49,
"nn.max_pool2d": 1,
"add": 16,
"nn.adaptive_avg_pool2d": 1,
"reshape": 1,
"msc.linear_bias": 1,
},
}
_test_from_torch(MSCFramework.TORCH, model_info, training=False, dynamic=dynamic)
def test_tensorflow_pipeline():
"""Test manager for tensorflow"""
model_info = {
"inputs": [
{"name": "input", "shape": [1, 224, 224, 3], "dtype": "float32", "layout": "NHWC"}
],
"outputs": [
{
"name": "MobilenetV2/Predictions/Reshape_1:0",
"shape": [1, 1001],
"dtype": "float32",
"layout": "NC",
}
],
"nodes": {
"total": 138,
"input": 1,
"msc.conv2d_bias": 36,
"clip": 35,
"nn.conv2d": 17,
"nn.batch_norm": 17,
"get_item": 17,
"add": 10,
"nn.avg_pool2d": 1,
"squeeze": 1,
"reshape": 2,
"nn.softmax": 1,
},
}
_test_from_tf(MSCFramework.TENSORFLOW, model_info)
@requires_tensorrt
@pytest.mark.parametrize("dynamic", [False, True])
def test_tensorrt_pipeline(dynamic):
"""Test pipeline for tensorrt"""
model_info = {
"inputs": [
{"name": "input_0", "shape": [1, 3, 224, 224], "dtype": "float32", "layout": "NCHW"}
],
"outputs": [{"name": "output", "shape": [1, 1000], "dtype": "float32", "layout": ""}],
"nodes": {"total": 2, "input": 1, "msc_tensorrt": 1},
}
_test_from_torch(MSCFramework.TENSORRT, model_info, training=False, dynamic=dynamic)
if __name__ == "__main__":
tvm.testing.main()