Skip to content

Instantly share code, notes, and snippets.

@mjamroz
Created April 19, 2020 07:36
Show Gist options
  • Save mjamroz/5f378e8756570876351d41b076a9b5cd to your computer and use it in GitHub Desktop.
Save mjamroz/5f378e8756570876351d41b076a9b5cd to your computer and use it in GitHub Desktop.
Convert mxnet mobilenet 1.0 model into tensorflow js
# REPLACE mmdnn/conversion/common/DataStructure/emitter.py
#----------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
#----------------------------------------------------------------------------------------------
from six import string_types as _string_types
import mmdnn.conversion.common.IR.graph_pb2 as graph_pb2
from mmdnn.conversion.common.IR.graph_pb2 import NodeDef, GraphDef, DataType
class Emitter(object):
def __init__(self):
self.body_code = str()
self.weights_dict = dict()
self.used_layers = set()
self.weight_loaded = False
self.layers_codes = dict()
def run(self, dstNetworkPath, dstWeightPath = None, phase = 'test'):
self.save_code(dstNetworkPath, phase)
# share functions
def add_body(self, indent, codes):
if isinstance(codes, _string_types):
codes = [codes]
for code in codes:
self.body_code += (" " * indent) + code + '\n'
def _load_weights(self, file_name=None):
import numpy as np
self.weight_loaded = True
try:
self.weights_dict = np.load(file_name, allow_pickle=True).item()
except:
self.weights_dict = np.load(file_name, encoding='bytes', allow_pickle=True).item()
def parent_variable_name(self, IR_node, path_or_name = [0]):
if isinstance(path_or_name, _string_types):
path = [IR_node.in_edges.index(path_or_name)]
elif isinstance(path_or_name, list):
path = path_or_name
else:
raise ValueError
return self.IR_graph.get_parent_variable_name(IR_node.name, path)
def _build(self):
self.IR_graph.build()
def gen_code(self, phase):
raise NotImplementedError("do not use base emitter class.")
def save_code(self, filepath, phase):
code = self.gen_code(phase)
with open(filepath, 'w') as fout:
fout.write(code)
print("Target network code snippet is saved as [{}].".format(filepath))
@staticmethod
def save_weights(weights, filename):
import numpy as np
with open(filename, 'wb') as of:
np.save(of, weights)
print("Target weights are saved as [{}].".format(filename))
@staticmethod
def _image_in_transpose_str(dim):
dims = [dim]
dims.extend(range(dim))
return ','.join('%s' % id for id in dims)
@staticmethod
def _image_out_transpose_str(dim):
dims = list(range(1, dim + 1))
dims.append(0)
return ','.join('%s' % id for id in dims)
@staticmethod
def _conv_kernel_transpose_str(dim):
dims = [dim + 1, dim]
dims.extend(range(dim))
return ','.join('%s' % id for id in dims)
from mxnet.gluon import nn
from gluoncv.model_zoo import get_model
from mxnet import image, cpu, init
from gluoncv.data.transforms.presets.imagenet import transform_eval
from sys import argv
context = [cpu()]
net = get_model("mobilenet1.0", ctx=context, pretrained=True)
with net.name_scope():
net.output = nn.Dense(2796)
net.output.initialize(init.Xavier(), ctx=context)
net.hybridize(static_alloc=True, static_shape=True)
net.load_parameters("mobilenet-imagenet-111-0-0140.params", ctx=context, cast_dtype=True)
img = image.imread("zawilec_6331.jpg")
img = transform_eval(img)
net(img)
net.export("model", epoch=140)
# REPLACE mmdnn/conversion/mxnet/mxnet_parser.py
# ----------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# ----------------------------------------------------------------------------------------------
import os
import sys
import math
import mxnet as mx
import numpy as np
from mmdnn.conversion.mxnet.mxnet_graph import MXNetGraph
import mmdnn.conversion.common.IR.graph_pb2 as graph_pb2
from mmdnn.conversion.common.IR.graph_pb2 import NodeDef, GraphDef, DataType
from mmdnn.conversion.common.DataStructure.parser import Parser
from mmdnn.conversion.common.utils import *
class MXNetParser(Parser):
dtype_map = {
"int8": graph_pb2.DT_INT8,
"int16": graph_pb2.DT_INT16,
"int32": graph_pb2.DT_INT32,
"int64": graph_pb2.DT_INT64,
"uint8": graph_pb2.DT_UINT8,
"uint16": graph_pb2.DT_UINT16,
"uint32": graph_pb2.DT_UINT32,
"uint64": graph_pb2.DT_UINT64,
"float16": graph_pb2.DT_FLOAT16,
"float32": graph_pb2.DT_FLOAT32,
"float64": graph_pb2.DT_FLOAT64,
}
activation_map = {
"relu": "Relu",
"sigmoid": "Sigmoid",
"tanh": "Tanh",
# Not support yet
# "softrelu" : "SoftReLU"
}
channels_last = ["NDHWC", "NHWC", "NWC"]
channels_first = ["NCDHW", "NCHW", "NCW"]
@property
def src_graph(self):
return self.mxnet_graph
@staticmethod
def str2bool(v):
return v.lower() in ("1", "true")
@staticmethod
def str2intList(v):
v = v.replace("(", "")
v = v.replace(")", "")
if v == "":
return list()
else:
return [int(s) for s in v.split(",")]
@staticmethod
def transpose(data, dim):
if dim == 1:
data = data.transpose((2, 1, 0))
elif dim == 2:
data = data.transpose((2, 3, 1, 0))
elif dim == 3:
data = data.transpose((2, 3, 4, 1, 0))
else:
print("Warning: The weight of dim {0} cannot transpose" % dim)
return data
@staticmethod
def _convert_axis(IR_node, axis):
ndim = len(IR_node.attr["_output_shapes"].list.shape[0].dim)
if axis == 0:
return 0
elif axis == 1:
return ndim - 1
else:
return axis - 1
def trace_shape(self, source_node, IR_node):
input_node = self.IR_layer_map[IR_node.input[0]]
while len(input_node.attr["_output_shapes"].list.shape[0].dim) <= 2:
IR_node = input_node
input_node = self.IR_layer_map[IR_node.input[0]]
input_shape = list()
for e in input_node.attr["_output_shapes"].list.shape[0].dim:
input_shape.append(e.size)
C = input_shape.pop()
ret = [C] + input_shape[1:]
return ret
def check_pad_mode(self, source_node, IR_node):
kernel = MXNetParser.str2intList(source_node.get_attr("kernel"))
dim = len(kernel)
pad = source_node.get_attr("pad", "()")
if pad == "()":
pad = list([0] * dim)
else:
pad = MXNetParser.str2intList(pad)
stride = source_node.get_attr("stride")
if stride == None:
stride = list([1] * dim)
else:
stride = MXNetParser.str2intList(stride)
dilate = source_node.get_attr("dilate")
if dilate == None:
dilate = list([1] * dim)
else:
dilate = MXNetParser.str2intList(dilate)
input_shape = list()
if len(source_node.in_edges) == 0 or IR_node.input[0] not in self.IR_layer_map:
input_shape = self.data_shape
else:
for e in (
self.IR_layer_map[IR_node.input[0]]
.attr["_output_shapes"]
.list.shape[0]
.dim
):
input_shape.append(e.size)
valid_flag = True
same_flag = True
for i in range(dim):
if not pad[i] == 0:
valid_flag = False
output_shape = int(
math.floor(
float(input_shape[i] + 2 * pad[i] - dilate[i] * (kernel[i] - 1) - 1)
/ float(stride[i])
)
+ 1
)
same_pad_shape = int(math.ceil(float(input_shape[i]) / float(stride[i])))
if not output_shape == same_pad_shape:
same_flag = False
if valid_flag:
return "VALID"
elif same_flag:
return "SAME"
else:
return "None"
@staticmethod
def _load_model(weights, epoch):
"""Load a mxnet model from disk
Parameters
----------
model_path: str
Path where the model network/params path is (json/params file)
prefix: str
prefix for json file, e.g. prefix-symbol.json
epoch: int
save epoch number
Returns
-------
model: A mxnet model
params: A pair of dictionaries each mapping parameter names to NDArray values
"""
# Load the model network and weights
sym, arg_params, aux_params = mx.model.load_checkpoint(weights, int(epoch))
# digraph = mx.viz.plot_network(sym, save_format='jpg') # For debugging
# digraph.render()
model = mx.mod.Module(symbol=sym)
arg_params.update(aux_params)
return model, arg_params
"""
MXNet new api does not support load data without data_shapes
"""
# model.bind(data_shapes = data_shapes)
# model.init_params()
# mod.load(model_path, epoch_num)
# return mod.get_params()
@staticmethod
def _load_json_file(model_path):
"""Load a mxnet network json file
Parameters
----------
model_path: str
Path where the model network/params path is (json/params file)
(Deleted)
prefix: str
prefix for json file, e.g. prefix-symbol.json
Returns
-------
data["nodes"]: all the layer information(including weights, bias) with format
data["nodes"][layer_num][params = {"name", "op", "attr", "inputs"}]
"""
import json
# load the model network
with open(model_path, "r") as data_file:
data = json.load(data_file)
# adjust the data format
assert isinstance(data["nodes"], list)
return data["nodes"]
def __init__(self, input_arg):
super(MXNetParser, self).__init__()
json_data = list()
self.data_shape = tuple()
# load model files into MXNet graph
# data_shape arguments added to calculate infer_shape(required)
# if isinstance(input_arg, basestring):
if len(input_arg) == 2:
with open(input_arg[0], "r") as input_json:
json_string = input_json.read()
symbol = mx.sym.load_json(json_string)
self.model = mx.mod.Module(symbol=symbol)
json_data = MXNetParser._load_json_file(input_arg[0])
self.data_shape = tuple([1] + list(map(int, input_arg[1])))
elif len(input_arg) == 4:
self.model, self.weight_data = MXNetParser._load_model(
input_arg[1], input_arg[2]
)
json_data = MXNetParser._load_json_file(input_arg[0])
self.weight_loaded = True
assert isinstance(input_arg[3], list)
self.data_shape = tuple([1] + list(map(int, input_arg[3])))
else:
raise ValueError(
"the # of input arguments [{}] is not supported" % len(input_arg)
)
# Build network graph
self.data_format = "None"
self.mxnet_graph = MXNetGraph(self.model)
self.mxnet_graph.build(json_data)
def gen_IR(self):
self.IR_layer_map = dict()
for layer in self.mxnet_graph.topological_sort:
current_node = self.mxnet_graph.get_node(layer)
node_type = current_node.type
if hasattr(self, "rename_" + node_type):
func = getattr(self, "rename_" + node_type)
func(current_node)
else:
self.rename_UNKNOWN(current_node)
def _copy_and_reop(self, source_node, IR_node, new_op=None):
new_op = source_node.type if new_op == None else new_op
if source_node.name.startswith("_"):
source_node.real_name = source_node.name[1:]
IR_node.name = source_node.real_name
IR_node.op = new_op
self.IR_layer_map[IR_node.name] = IR_node
def set_output_shape(self, source_node, IR_node):
sym_group = self.model.symbol.get_internals()
for sym in sym_group:
if source_node.name == sym.name:
arg_shape, output_shape, aux_shape = sym.infer_shape(
data=self.data_shape
)
for idx in range(len(output_shape)):
output_list = list(output_shape[idx])
# transpose to channel last
if not self.data_format in MXNetParser.channels_last:
channel = output_list.pop(1)
output_list.append(channel)
if IR_node.op == "DataInput":
MXNetParser._copy_shape(IR_node, [-1] + output_list[1:])
shape = graph_pb2.TensorShape()
for dim in output_list:
new_dim = shape.dim.add()
if dim == None:
new_dim.size = -1
else:
new_dim.size = dim
IR_node.attr["_output_shapes"].list.shape.extend([shape])
break
def _convert_identity_operation(self, source_node, new_op=None):
IR_node = self.IR_graph.node.add()
# name, op
self._copy_and_reop(source_node, IR_node, new_op)
# input edge
self.convert_inedge(source_node, IR_node)
# output shape
self.set_output_shape(source_node, IR_node)
return IR_node
def _defuse_padding(self, source_node):
IR_node = self.IR_graph.node.add()
IR_node.name = source_node.name + "_pad"
IR_node.op = "Pad"
# input edge
self.convert_inedge(source_node, IR_node)
self.IR_layer_map[IR_node.name] = IR_node
# attr
assign_IRnode_values(IR_node, {"mode": "CONSTANT"})
# print("Warning: MXNet symbol pad does not support channel last")
pad = MXNetParser.str2intList(source_node.get_attr("pad"))
args["pads"] = [0, 0]
for e in pad:
args["pads"].extend([e, e])
args["pads"] += [0, 0]
args["pads"] = convert_tf_pad_to_onnx(args["pads"])
IR_node.set_attrs(args)
# IR_node.attr["pads"].list.i.extend([0, 0])
# for e in pad:
# IR_node.attr["pads"].list.i.extend([e, e])
# IR_node.attr["pads"].list.i.extend([0, 0])
IR_node.attr["constant_values"].f = 0.0
@staticmethod
def _copy_shape(IR_node, output_list):
if not output_list == None:
for dim in output_list:
new_dim = IR_node.attr["shape"].shape.dim.add()
if dim == None:
new_dim.size = -1
else:
new_dim.size = dim
else:
IR_node.attr["shape"].shape.unknown_rank = True
def rename_UNKNOWN(self, source_node):
print(
"Warning: MXNet Parser has not supported operator %s with name %s."
% (source_node.type, source_node.name)
)
if source_node.type == "null" and source_node.name != "label":
print(
"Warning: convert the null operator with name [%s] into input layer."
% source_node.name
)
IR_node = self.IR_graph.node.add()
# name, op
self._copy_and_reop(source_node, IR_node, "DataInput")
# input edge
self.convert_inedge(source_node, IR_node)
self.set_output_shape(source_node, IR_node)
else:
raise NotImplementedError()
"""
Here start with Neural Network Symbol
"""
def rename_Pad(self, source_node):
IR_node = self._convert_identity_operation(source_node)
kwargs = dict()
pad = MXNetParser.str2intList(source_node.get_attr("pad_width"))
pad += [pad.pop(2), pad.pop(3)]
kwargs["pads"] = pad
kwargs["pads"] = convert_tf_pad_to_onnx(kwargs["pads"])
kwargs["mode"] = "CONSTANT"
assign_IRnode_values(IR_node, kwargs)
IR_node.attr["constant_values"].f = 0.0
def rename_FullyConnected(self, source_node):
IR_node = self._convert_identity_operation(source_node)
# units
IR_node.attr["units"].i = int(source_node.get_attr("num_hidden"))
# use bias (no_bias default = False)
IR_node.attr["use_bias"].b = not MXNetParser.str2bool(
source_node.get_attr("no_bias", "False")
)
# weights
if self.weight_loaded:
if self.data_format == "NM":
self.set_weight(
source_node.name,
"weights",
self.weight_data.get(source_node.name + "_weight")
.asnumpy()
.transpose((1, 0)),
)
else:
weight = self._try_to_load_mobnet(
source_node.name, "_weight"
).transpose((1, 0))
original_shape = weight.shape
channel_first_list = self.trace_shape(source_node, IR_node)
dim = len(channel_first_list) + 1
weight = weight.reshape(channel_first_list + [original_shape[1]])
assert dim > 2
weight = weight.transpose(list(range(1, dim - 1)) + [0, dim - 1])
weight = weight.reshape(original_shape)
self.set_weight(source_node.name, "weights", weight)
if IR_node.attr["use_bias"].b:
self.set_weight(
source_node.name,
"bias",
self._try_to_load_mobnet(source_node.name, "_bias"),
)
if not self.data_format == "NM":
# print("Warning: Layer [{}] has changed model data format from [{}] to [NM]".format(source_node.name, self.data_format))
self.data_format = "NM"
def rename_Convolution(self, source_node):
IR_node = self.IR_graph.node.add()
# input edge
self.convert_inedge(source_node, IR_node)
# output shape
self.set_output_shape(source_node, IR_node)
dim = 0
layout = "None"
# kernel_shape
kernel = MXNetParser.str2intList(source_node.get_attr("kernel"))
dim = len(kernel)
IR_node.attr["kernel_shape"].list.i.extend(kernel)
layout = source_node.get_attr("layout")
if layout == None or layout == "None":
if dim == 1:
layout = "NCW"
elif dim == 2:
layout = "NCHW"
elif dim == 3:
layout = "NCDHW"
if not self.data_format == layout:
# print("Warning: Layer [{}] has changed model data format from [{}] to [{}]".format(source_node.name, self.data_format, layout))
self.data_format = layout
# groups
group = int(source_node.get_attr("num_group", "1"))
IR_node.attr["group"].i = group
in_channel = (
self.IR_layer_map[IR_node.input[0]]
.attr["_output_shapes"]
.list.shape[0]
.dim[-1]
.size
)
if group == in_channel:
self._copy_and_reop(source_node, IR_node, "DepthwiseConv")
else:
self._copy_and_reop(source_node, IR_node, "Conv")
# in_channel = in_channel // group
out_channel = int(source_node.get_attr("num_filter"))
IR_node.attr["kernel_shape"].list.i.extend([in_channel, out_channel])
# use_bias (no_bias default = False)
IR_node.attr["use_bias"].b = not MXNetParser.str2bool(
source_node.get_attr("no_bias", "False")
)
# strides
strides = source_node.get_attr("stride")
IR_node.attr["strides"].list.i.append(1)
if not strides == None:
IR_node.attr["strides"].list.i.extend(MXNetParser.str2intList(strides))
else:
IR_node.attr["strides"].list.i.extend([1] * dim)
IR_node.attr["strides"].list.i.append(1)
# dilations
dilate = source_node.get_attr("dilate")
IR_node.attr["dilations"].list.i.append(1)
if not dilate == None:
IR_node.attr["dilations"].list.i.extend(MXNetParser.str2intList(dilate))
else:
IR_node.attr["dilations"].list.i.extend([1] * dim)
IR_node.attr["dilations"].list.i.append(1)
# data_format
assign_IRnode_values(IR_node, {"data_format": layout})
# padding
if "pad" in source_node.attr:
pad = MXNetParser.str2intList(source_node.get_attr("pad"))
IR_node.attr["pads"].list.i.extend(([0] + pad + [0]) * 2)
else:
IR_node.attr["pads"].list.i.extend([0, 0] * (dim + 2))
# weights
if self.weight_loaded:
weight = self._try_to_load_mobnet(source_node.name, "_weight")
if not layout in MXNetParser.channels_last:
weight = MXNetParser.transpose(weight, dim)
if IR_node.op == "DepthwiseConv":
weight = weight.transpose(0, 1, 3, 2)
self.set_weight(source_node.name, "weights", weight)
if IR_node.attr["use_bias"].b:
self.set_weight(
source_node.name,
"bias",
self.weight_data.get(source_node.name + "_bias").asnumpy(),
)
def _try_to_load_mobnet(self, source_node_name, suffix):
for key in [
source_node_name + suffix,
source_node_name.replace("_fwd", "") + suffix,
source_node_name.replace("_fwd", "")
+ suffix.replace("_moving", "_running"),
]:
try:
return self.weight_data.get(key).asnumpy()
except:
pass
def rename_Activation(self, source_node):
self._convert_identity_operation(
source_node,
new_op=MXNetParser.activation_map[source_node.get_attr("act_type")],
)
def rename_BatchNorm(self, source_node):
IR_node = self._convert_identity_operation(source_node)
# axis
if self.data_format in MXNetParser.channels_first or self.data_format == "None":
IR_node.attr["axis"].i = MXNetParser._convert_axis(
IR_node, int(source_node.get_attr("axis", "1"))
)
else:
IR_node.attr["axis"].i = int(source_node.get_attr("axis", "1"))
# scale
IR_node.attr["scale"].b = not MXNetParser.str2bool(
source_node.get_attr("fix_gamma", "True")
)
IR_node.attr["bias"].b = True
# epsilon
IR_node.attr["epsilon"].f = float(source_node.get_attr("eps", "0.001"))
# momentum
IR_node.attr["momentum"].f = float(source_node.get_attr("momentum", "0.9"))
# weights
if self.weight_loaded:
# gamma
if IR_node.attr["scale"].b:
self.set_weight(
source_node.name,
"scale",
self._try_to_load_mobnet(source_node.name, "_gamma"),
)
# beta
if IR_node.attr["bias"].b:
self.set_weight(
source_node.name,
"bias",
self._try_to_load_mobnet(source_node.name, "_beta"),
)
# mean
self.set_weight(
source_node.name,
"mean",
self._try_to_load_mobnet(source_node.name, "_moving_mean"),
)
# var
self.set_weight(
source_node.name,
"var",
self._try_to_load_mobnet(source_node.name, "_moving_var"),
)
def rename_Pooling(self, source_node):
IR_node = self.IR_graph.node.add()
# name, op
self._copy_and_reop(source_node, IR_node, "Pool")
# input edge
self.convert_inedge(source_node, IR_node)
# pooling type (sum not allowed yet)
pool_type = source_node.get_attr("pool_type")
if pool_type == "sum":
print("Warning: sum pooling is not supported yet.")
elif pool_type == "max":
assign_IRnode_values(IR_node, {"pooling_type": "MAX"})
elif pool_type == "avg":
assign_IRnode_values(IR_node, {"pooling_type": "AVG"})
else:
raise ValueError("Error pool_type {}.".format(pool_type))
kernel_shape = MXNetParser.str2intList(source_node.get_attr("kernel"))
if MXNetParser.str2bool(source_node.get_attr("global_pool", "False")):
IR_node.attr["global_pooling"].b = True
IR_node.attr["kernel_shape"].list.i[:] = [1] * (len(kernel_shape) + 2)
IR_node.attr["strides"].list.i[:] = [1] * (len(kernel_shape) + 2)
else:
IR_node.attr["global_pooling"].b = False
# strides
strides = source_node.get_attr("stride")
IR_node.attr["strides"].list.i.append(1)
if not strides == None:
IR_node.attr["strides"].list.i.extend(MXNetParser.str2intList(strides))
IR_node.attr["strides"].list.i.append(1)
# kernel_shape
IR_node.attr["kernel_shape"].list.i.append(1)
IR_node.attr["kernel_shape"].list.i.extend(kernel_shape)
IR_node.attr["kernel_shape"].list.i.append(1)
# padding
if "pad" in source_node.attr:
pad = MXNetParser.str2intList(source_node.get_attr("pad"))
IR_node.attr["pads"].list.i.extend(([0] + pad + [0]) * 2)
else:
IR_node.attr["pads"].list.i.extend(([0]) * 8)
# output shape
self.set_output_shape(source_node, IR_node)
def rename_SoftmaxOutput(self, source_node):
IR_node = self.IR_graph.node.add()
# name, op
self._copy_and_reop(source_node, IR_node, "Softmax")
# input edge
self.convert_inedge(source_node, IR_node)
if "attr" in source_node.layer or "param" in source_node.layer:
print("Warning: SoftmaxOutput attrs are not supported in IR.")
# output shape
self.set_output_shape(source_node, IR_node)
def rename_softmax(self, source_node):
IR_node = self._convert_identity_operation(source_node, new_op="Softmax")
# dim
if self.data_format in MXNetParser.channels_first or self.data_format == "None":
IR_node.attr["dim"].i = MXNetParser._convert_axis(
IR_node, int(source_node.get_attr("axis", "-1"))
)
else:
IR_node.attr["dim"].i = int(source_node.get_attr("axis", "-1"))
# def rename_log_softmax(self, source_node):
# raise NotImplementedError("not support yet")
# def rename_Correlation(self, source_node):
# raise NotImplementedError("not support yet")
def rename_Deconvolution(self, source_node):
IR_node = self.IR_graph.node.add()
# name, op
self._copy_and_reop(source_node, IR_node, "ConvTranspose")
# input edge
self.convert_inedge(source_node, IR_node)
dim = 0
layout = "None"
# padding
if "pad" in source_node.attr:
pad = MXNetParser.str2intList(source_node.get_attr("pad"))
IR_node.attr["pads"].list.i.extend(([0] + pad + [0]) * 2)
else:
IR_node.attr["pads"].list.i.extend([0, 0] * (dim + 2))
# output shape
self.set_output_shape(source_node, IR_node)
# kernel_shape
kernel = MXNetParser.str2intList(source_node.get_attr("kernel"))
dim = len(kernel)
IR_node.attr["kernel_shape"].list.i.extend(kernel)
layout = source_node.get_attr("layout")
if layout == None or layout == "None":
if dim == 1:
layout = "NCW"
elif dim == 2:
layout = "NCHW"
elif dim == 3:
layout = "NCDHW"
if not self.data_format == layout:
# print("Warning: Layer [{}] has changed model data format from [{}] to [{}]".format(source_node.name, self.data_format, layout))
self.data_format = layout
in_channel = (
self.IR_layer_map[IR_node.input[0]]
.attr["_output_shapes"]
.list.shape[0]
.dim[-1]
.size
)
out_channel = int(source_node.get_attr("num_filter"))
IR_node.attr["kernel_shape"].list.i.extend([out_channel, in_channel])
# use_bias (no_bias default = False)
IR_node.attr["use_bias"].b = not MXNetParser.str2bool(
source_node.get_attr("no_bias", "False")
)
# strides
strides = source_node.get_attr("strides")
IR_node.attr["strides"].list.i.append(1)
if not strides == None:
IR_node.attr["strides"].list.i.extend(MXNetParser.str2intList(strides))
else:
IR_node.attr["strides"].list.i.extend([1] * dim)
IR_node.attr["strides"].list.i.append(1)
# dilations
dilate = source_node.get_attr("dilate")
IR_node.attr["dilations"].list.i.append(1)
if not dilate == None:
IR_node.attr["dilations"].list.i.extend(MXNetParser.str2intList(dilate))
else:
IR_node.attr["dilations"].list.i.extend([1] * dim)
IR_node.attr["dilations"].list.i.append(1)
# data_format
IR_node.attr["data_format"].s = layout
# groups
IR_node.attr["group"].i = int(source_node.get_attr("num_group", "1"))
# weights
if self.weight_loaded:
weight = self.weight_data.get(source_node.name + "_weight").asnumpy()
if not layout in MXNetParser.channels_last:
weight = MXNetParser.transpose(weight, dim)
self.set_weight(source_node.name, "weights", weight)
if IR_node.attr["use_bias"].b:
self.set_weight(
source_node.name,
"bias",
self.weight_data.get(source_node.name + "_bias").asnumpy(),
)
# def rename_RNN(self, source_node):
# raise NotImplementedError("RNN not support yet")
def rename_Embedding(self, source_node):
IR_node = self.IR_graph.node.add()
# name, op
self._copy_and_reop(source_node, IR_node)
# input edge
self.convert_inedge(source_node, IR_node)
# input_dim
IR_node.attr["input_dim"].i = int(source_node.get_attr("input_dim"))
# output_dim
IR_node.attr["output_dim"].i = int(source_node.get_attr("output_dim"))
# dtype
IR_node.attr["dtype"].type = MXNetParser.dtype_map[
source_node.get_attr("dtype", "float32")
]
# output shape
self.set_output_shape(source_node, IR_node)
# IR only support elu and prelu from {'elu', 'leaky', 'prelu', 'rrelu'}
def rename_LeakyReLU(self, source_node):
act_type = source_node.get_attr("act_type", None)
if act_type:
if not act_type == "elu" and not act_type == "prelu":
print("Warning: Activation Type %s is not supported yet." % act_type)
# return
IR_node = self.IR_graph.node.add()
# name, op
if act_type == "prelu":
self._copy_and_reop(source_node, IR_node, "PRelu")
# gamma
self.set_weight(
source_node.name,
"gamma",
self.weight_data.get(source_node.name + "_gamma").asnumpy(),
)
else: # All other cases set to 'Elu'
self._copy_and_reop(source_node, IR_node, "Elu")
# input edge
self.convert_inedge(source_node, IR_node)
# alpha [exp(x) - alpha], but mxnet attr slope [slope*(exp(x) - 1)] when x < 0
if "slope" in source_node.attr:
raise ValueError("Attribute Slope is not supported in IR format")
# IR_node.attr["alpha"].f = float()
# output shape
self.set_output_shape(source_node, IR_node)
# raise NotImplementedError("slope cannot convert to alpha")
# def rename_InstanceNorm(self, source_node):
# raise NotImplementedError
# def rename_L2Normalization(self, source_node):
# raise NotImplementedError
def rename_LRN(self, source_node):
IR_node = self._convert_identity_operation(source_node)
# alpha
IR_node.attr["alpha"].f = float(source_node.get_attr("alpha", "0.0001"))
# beta
IR_node.attr["beta"].f = float(source_node.get_attr("beta", "0.75"))
# knorm
IR_node.attr["k"].f = float(source_node.get_attr("knorm", "2"))
# nsize
IR_node.attr["size"].i = float(source_node.get_attr["nsize"])
def rename_ROIPooling(self, source_node):
raise NotImplementedError()
def rename_Dropout(self, source_node):
IR_node = self._convert_identity_operation(source_node)
# keep_prob
IR_node.attr["keep_prob"].f = float(source_node.get_attr("p", "0.5"))
# mode
assign_IRnode_values(IR_node, {"mode": "training"})
"""
Here start with Symbol manipulation routines
"""
# reverse cannot support yet
def rename_reshape(self, source_node):
IR_node = self._convert_identity_operation(source_node, new_op="Reshape")
# old API target_shape not support yet
shape = source_node.get_attr("shape")
if not shape == None:
shape_list = MXNetParser.str2intList(shape)
for param in shape_list:
if param <= 0 and not param == -1:
raise ValueError(
"special value %d for Reshape is not pre-defined in IR." % param
)
IR_node.attr["shape"].list.i.extend(shape_list)
# output shape
self.set_output_shape(source_node, IR_node)
# raise NotImplementedError("adjust output shape")
def rename_Flatten(self, source_node):
self._convert_identity_operation(source_node, new_op="Flatten")
def rename_Concat(self, source_node):
IR_node = self._convert_identity_operation(source_node, new_op="Concat")
# dim
if self.data_format in MXNetParser.channels_first or self.data_format == "None":
IR_node.attr["axis"].i = MXNetParser._convert_axis(
IR_node, int(source_node.get_attr("dim", "1"))
)
else:
IR_node.attr["axis"].i = int(source_node.get_attr("dim", "1"))
def rename_cast(self, source_node):
IR_node = self._convert_identity_operation(source_node, new_op="Cast")
# dtype
IR_node.attr["dtype"].type = MXNetParser.dtype_map[
source_node.get_attr("dtype")
]
# output shape
self.set_output_shape(source_node, IR_node)
def rename_expand_dims(self, source_node):
IR_node = self.IR_graph.node.add()
# name, op
self._copy_and_reop(source_node, IR_node)
# input edge
self.convert_inedge(source_node, IR_node)
# output shape
self.set_output_shape(source_node, IR_node)
# axis
if self.data_format in MXNetParser.channels_first or self.data_format == "None":
IR_node.attr["axis"].i = MXNetParser._convert_axis(
IR_node, int(source_node.get_attr("axis"))
)
else:
IR_node.attr["axis"].i = int(source_node.get_attr("axis"))
def rename_elemwise_add(self, source_node):
self._convert_identity_operation(source_node, new_op="Add")
def rename__Plus(self, source_node):
self._convert_identity_operation(source_node, new_op="Add")
def rename_broadcast_add(self, source_node):
self._convert_identity_operation(source_node, new_op="Add")
def rename_broadcast_mul(self, source_node):
self._convert_identity_operation(source_node, new_op="Mul")
def rename__mul(self, source_node):
self._convert_identity_operation(source_node, new_op="Mul")
def rename__copy(self, source_node):
self._convert_identity_operation(source_node)
# raise NotImplementedError("No matching IR api")
def _convert_scalar_operator(self, source_node, new_op):
value = source_node.get_attr("scalar")
value_node = self.IR_graph.node.add()
value_node.name = source_node.real_name + "_second"
# left strip the "_" at the beginning of the name
# Issue #85, #135
value_node.name = value_node.name.lstrip("_")
value_node.op = "Constant"
self.set_weight(value_node.name, "value", np.array([value], np.float32))
IR_node = self._convert_identity_operation(source_node, new_op)
IR_node.input.append(value_node.name)
return IR_node
def rename__mul_scalar(self, source_node):
self._convert_scalar_operator(source_node, "Mul")
def rename__minus_scalar(self, source_node):
self._convert_scalar_operator(source_node, "Sub")
def rename__copy(self, source_node):
source_node.real_name = self.get_parent(source_node.name, [0]).real_name
def rename_BlockGrad(self, source_node):
return
[[source]]
url = "https://pypi.org/simple"
verify_ssl = true
name = "pypi"
[packages]
tensorflow = "==1.15.0"
mxnet-mkl = "==1.6.0"
numpy = "==1.18.2"
tensorflowjs = "==1.7.2"
gluoncv = "==0.6.0"
mmdnn = "==0.2.5"
[dev-packages]
[requires]
python_version = "3.6"
# *params *json as converted with export.py
mmtoir -f mxnet -n mobilenet-imagenet-111-0-symbol.json -w mobilenet-imagenet-111-0-0140.params -d mobilenet_v1_1.0 --inputShape 3,224,224
mmtocode -f tensorflow --IRModelPath mobilenet_v1_1.0.pb --IRWeightPath mobilenet_v1_1.0.npy --dstModelPath tf_mobilenet.py
# newer numpy:
sed -i 's/, encoding/, allow_pickle=True, encoding/g' tf_mobilenet.py
sed -i 's/d(weight_file)/d(weight_file,allow_pickle=True)/g' tf_mobilenet.py
mmtomodel -f tensorflow -in tf_mobilenet.py -iw mobilenet_v1_1.0.npy -o tf_mobilenet10 --dump_tag SERVING
tensorflowjs_converter --input_format=tf_saved_model --quantization_bytes=1 --saved_model_tags=serve --weight_shard_size_bytes 10000000000000 tf_mobilenet10/ tf_mobilenet10_js
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment