Skip to content

Commit f4b87e5

Browse files
committed
updated this branch to use the new ugraph-constructor
1 parent 5c1bf60 commit f4b87e5

File tree

2 files changed

+171
-140
lines changed

2 files changed

+171
-140
lines changed

tests/tflm/tflite_export/conftest.py

Lines changed: 127 additions & 138 deletions
Original file line numberDiff line numberDiff line change
@@ -3,148 +3,137 @@
33

44
import tensorflow as tf
55
from utensor_cgen.ir import TensorInfo, OperationInfo, uTensorGraph
6+
from utensor_cgen.ir.converter import (AttrValueConverter, DataTypeConverter,
7+
GenericTensorConverterMixin)
68
from utensor_cgen.utils import prune_graph, topologic_order_graph
7-
8-
@fixture(name='hybrid_quant_output')
9-
def simple_tflm_graph():
10-
ugraph = uTensorGraph()
11-
12-
mock_input_op0 = OperationInfo(
13-
name = "mock_input_const0",
14-
op_type = "Const",
15-
backend = "tensorflow",
16-
ugraph = ugraph,
17-
op_attr = dict(),
18-
input_tensors = [],
19-
output_tensors = []
20-
)
21-
mock_input_op0.op_attr["value"] = np.array([[2],[4],[6],[8]], dtype=np.float32)
22-
mock_input_op0.op_attr["shape"] = [4,1]
23-
24-
input0 = TensorInfo(
25-
name = "input0",
26-
op_name = "mock_input_const0",
27-
dtype = mock_input_op0.op_attr["value"].dtype,
28-
shape = mock_input_op0.op_attr["shape"],
29-
ugraph = ugraph
30-
)
31-
32-
mock_input_op0.output_tensors = [input0]
33-
34-
mock_input1_op = OperationInfo(
35-
name = "mock_input_const1",
36-
op_type = "Const",
37-
backend = "tensorflow",
38-
ugraph = ugraph,
39-
op_attr = dict(),
40-
input_tensors = [],
41-
output_tensors = []
42-
)
43-
mock_input1_op.op_attr["value"] = np.array([[2],[4],[6],[8]], dtype=np.float32)
44-
mock_input1_op.op_attr["shape"] = [4,1]
45-
46-
input1 = TensorInfo(
47-
name = "input1",
48-
op_name = "mock_input_const1",
49-
dtype = mock_input1_op.op_attr["value"].dtype,
50-
shape = mock_input1_op.op_attr["shape"],
51-
ugraph = ugraph
52-
)
53-
54-
mock_input1_op.output_tensors = [input1]
55-
56-
add_output = TensorInfo(
57-
name = "add_out",
58-
op_name = "add0",
59-
dtype = mock_input_op0.op_attr["value"].dtype,
60-
shape = mock_input_op0.op_attr["shape"],
61-
ugraph = ugraph
62-
)
63-
64-
add_op = OperationInfo(
65-
name = "add0",
66-
op_type = "ADD",
67-
backend = "tensorflow",
68-
ugraph = ugraph,
69-
op_attr = dict(),
70-
input_tensors = [input0, input1],
71-
output_tensors = [add_output]
9+
from utensor_cgen.backend.operators import OperatorFactory, _Operator
10+
from utensor_cgen.matcher import OpEqualityDelegate, _morphism
11+
12+
13+
@OperatorFactory.register
14+
@OpEqualityDelegate.is_associative(
15+
permutations=((0, 1), (1, 0))
16+
)
17+
class _TFLM_AddOperator(_Operator):
18+
19+
op_type = "TFLM_ADD" # tf op type
20+
21+
def __init__(self, op_info, **kwargs):
22+
_Operator.__init__(self)
23+
inputs = [tensor_info.name for tensor_info in op_info.input_tensors]
24+
output = op_info.output_tensors[0].name
25+
tf_dtype = op_info.input_tensors[0].dtype
26+
27+
@classmethod
28+
def build_op_info(cls, ugraph, name, tensor_x, tensor_y, **kwargs):
29+
# broadcast the shape and promote types
30+
dummy_x = np.empty(tensor_x.shape)
31+
dummy_y = np.empty(tensor_y.shape)
32+
output_shape = np.broadcast(dummy_x, dummy_y).shape
33+
output_dtype = np.promote_types(tensor_x.dtype, tensor_y.dtype)
34+
return OperationInfo(
35+
name=name,
36+
input_tensors=[tensor_x, tensor_y],
37+
output_tensors=[
38+
TensorInfo(
39+
name='{}:0'.format(name),
40+
op_name=name,
41+
dtype=output_dtype,
42+
shape=list(output_shape),
43+
ugraph=ugraph
44+
)
45+
],
46+
op_type=cls.op_type,
47+
op_attr={
48+
'T': AttrValueConverter.__utensor_generic_type__(
49+
value_name='type',
50+
value=DataTypeConverter.get_tf_value(output_dtype)
51+
)
52+
},
53+
ugraph=ugraph,
54+
backend=kwargs.get('backend', 'TFLM')
7255
)
7356

74-
ugraph.ops_info["ADD0"] = add_op
7557

76-
weight_op = OperationInfo(
77-
name = "weight_const",
78-
op_type = "Const",
79-
backend = "tensorflow",
80-
ugraph = ugraph,
81-
op_attr = dict(),
82-
input_tensors = [],
83-
output_tensors = []
58+
@OperatorFactory.register
59+
class _TFLM_FULLY_CONNECTED_Operator(_Operator):
60+
61+
op_type="TFLM_FULLY_CONNECTED"
62+
63+
def __init__(self, op_info, **kwargs):
64+
_Operator.__init__(self)
65+
inputs = [tensor_info.name for tensor_info in op_info.input_tensors]
66+
output = op_info.output_tensors[0].name
67+
out_dtype = op_info.output_tensors[0].dtype
68+
in_dtypes = [tensor_info.dtype for tensor_info in op_info.input_tensors]
69+
#assert (op_info.input_tensors[0].shape[1] == None or op_info.input_tensors[0].shape[1] == 1)
70+
71+
@classmethod
72+
def build_op_info(cls, ugraph, name, tensor_x, tensor_w, tensor_b, **kwargs):
73+
output_shape = [tensor_w.shape[0], tensor_x.shape[1]]
74+
#output_dtype = np.promote_types(tensor_x.dtype, tensor_y.dtype)
75+
output_dtype = tensor_x.dtype
76+
return OperationInfo(
77+
name=name,
78+
input_tensors=[tensor_x, tensor_w, tensor_b],
79+
output_tensors=[
80+
TensorInfo(
81+
name='{}:0'.format(name),
82+
op_name=name,
83+
dtype=output_dtype,
84+
shape=list(output_shape),
85+
ugraph=ugraph
86+
)
87+
],
88+
op_type=cls.op_type,
89+
op_attr={
90+
'T': AttrValueConverter.__utensor_generic_type__(
91+
value_name='type',
92+
value=DataTypeConverter.get_tf_value(output_dtype)
93+
)
94+
},
95+
ugraph=ugraph,
96+
backend=kwargs.get('backend', 'TFLM')
8497
)
85-
#weight_op.op_attr["value"] = np.array([1,2,3,4], dtype=np.int8)
86-
weight_op.op_attr["value"] = np.array([10,20,30,40], dtype=np.float32)
87-
weight_op.op_attr["shape"] = [1,4]
8898

89-
weight = TensorInfo(
90-
name = "weight",
91-
op_name = "weight_const",
92-
dtype = np.dtype("float32"),
93-
shape = weight_op.op_attr["shape"],
94-
ugraph = ugraph
95-
)
96-
weight_op.output_tensors = [weight]
97-
98-
bias_op = OperationInfo(
99-
name = "bias_const",
100-
op_type = "Const",
101-
backend = "tensorflow",
102-
ugraph = ugraph,
103-
op_attr = dict(),
104-
input_tensors = [],
105-
output_tensors = []
106-
)
107-
#bias_op.op_attr["value"] = np.array([1], dtype=np.int8)
108-
bias_op.op_attr["value"] = np.array([7], dtype=np.float32)
109-
bias_op.op_attr["shape"] = [1]
110-
111-
bias = TensorInfo(
112-
name = "bias",
113-
op_name = "bias_const",
114-
dtype = np.dtype("float32"),
115-
shape = bias_op.op_attr["shape"],
116-
ugraph = ugraph
117-
)
118-
bias_op.output_tensors = [bias]
119-
120-
121-
fc1_op = OperationInfo(
122-
name = "FC1",
123-
op_type = "FULLY_CONNECTED",
124-
backend = "tensorflow",
125-
ugraph = ugraph,
126-
op_attr = dict(),
127-
input_tensors = [],
128-
output_tensors = []
129-
)
130-
131-
output = TensorInfo(
132-
name = "output",
133-
op_name = "FC1",
134-
dtype = np.dtype("float32"),
135-
shape = [1],
136-
ugraph = ugraph
137-
)
138-
139-
fc1_op.input_tensors = [add_output, weight, bias]
140-
fc1_op.output_tensors = [output]
141-
142-
ugraph.ops_info["FC1"] = fc1_op
143-
ugraph.output_nodes = ["FC1"]
144-
#ugraph.backend = "tensorflow"
145-
146-
topologic_order_graph(ugraph)
147-
#ugraph = prune_graph(ugraph)
99+
@fixture(name='hybrid_quant_output')
100+
def simple_tflm_graph():
101+
ugraph = uTensorGraph()
148102

149-
#return: ugraph, input tensors, output tensors
150-
return [ugraph, ["input0", "input1"], ["weight", "bias", "output"]]
103+
with ugraph.begin_construction():
104+
tensor_x0, = ugraph.add_op(
105+
op_type='Const',
106+
name='x0',
107+
value=np.array([1, 1, 1, 1], dtype=np.float32)[:, np.newaxis]
108+
)
109+
tensor_x1, = ugraph.add_op(
110+
op_type='Const',
111+
name='x1',
112+
value=np.array([2, 4, 6, 8], dtype=np.float32)[:, np.newaxis]
113+
)
114+
tensor_w, = ugraph.add_op(
115+
op_type='Const',
116+
name='w',
117+
value=np.array([10, 20, 30, 40], dtype=np.float32)[np.newaxis, :]
118+
)
119+
tensor_b, = ugraph.add_op(
120+
op_type='Const',
121+
name='b',
122+
value=np.array([7], dtype=np.float32)
123+
)
124+
125+
126+
tensor_addout, = ugraph.add_op(
127+
tensor_x0, tensor_x1,
128+
op_type='TFLM_ADD',
129+
name='TFLM_ADD0'
130+
)
131+
132+
tensor_out, = ugraph.add_op(
133+
tensor_addout, tensor_w, tensor_b,
134+
op_type='TFLM_FULLY_CONNECTED',
135+
name='TFLM_FULLY_CONNECTED00',
136+
is_output=True
137+
)
138+
139+
return [ugraph, ["x0:0", "x1:0"], ["w:0", "b:0", tensor_out.name]]

utensor_cgen/transformer/tflite_exporter.py

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,42 @@
2323

2424
__all__ = ["TFLiteExporter"]
2525

26+
def rename_ugraph_ops_(ugraph, name_old, name_new):
27+
op = ugraph.ops_info.pop(name_old)
28+
op.name = name_new
29+
for out_tensor in op.output_tensors:
30+
pass # renaming tensors
31+
for out_node in op.output_nodes:
32+
for in_tensor in out_node.input_tensors:
33+
if in_tensor.op_name == name_old:
34+
in_tensor.op_name = name_new
35+
ugraph.ops_info[name_new] = op
36+
for i, op_name in enumerate(ugraph.topo_order):
37+
if op_name == name_old:
38+
ugraph.topo_order[i] = name_new
39+
break
40+
for i, op_name in enumerate(ugraph.output_nodes):
41+
if op_name == name_old:
42+
ugraph.output_nodes[i] = name_new
43+
break
44+
45+
op.name = name_new
46+
for out_tensor in op.output_tensors:
47+
pass # renaming tensors
48+
for out_node in op.output_nodes:
49+
for in_tensor in out_node.input_tensors:
50+
if in_tensor.op_name == name_old:
51+
in_tensor.op_name = name_new
52+
ugraph.ops_info[name_new] = op
53+
for i, op_name in enumerate(ugraph.topo_order):
54+
if op_name == name_old:
55+
ugraph.topo_order[i] = name_new
56+
break
57+
for i, op_name in enumerate(ugraph.output_nodes):
58+
if op_name == name_old:
59+
ugraph.output_nodes[i] = name_new
60+
break
61+
2662
def get_fullyconnected_builtin_option(fbuilder, op_info):
2763

2864
weight_format = tflite.FullyConnectedOptionsWeightsFormat.FullyConnectedOptionsWeightsFormat.DEFAULT
@@ -110,6 +146,7 @@ def __init__(self, input_tensors, output_tensors):
110146

111147

112148
def transform(self, ugraph):
149+
self.__tflm_graph_legalize_(ugraph)
113150
# create tensor data buffer
114151
# create const tensors
115152
# update tensor_index
@@ -184,7 +221,7 @@ def __create_static_tensor(self, ugraph):
184221
out_tensor_info.shape)
185222
#weight
186223
#value = op_info.op_attr['value'].value.np_array.flatten() #TODO: specify endianness here
187-
value = op_info.op_attr['value'].flatten() #FIXME: deal with the proper attribute type here
224+
value = op_info.op_attr['value'].value.np_array.flatten() #FIXME: deal with the proper attribute type here
188225
raw_data = value.tobytes()
189226
data_vec = self.__fb_vector(tflite.Buffer.BufferStartDataVector, raw_data)
190227

@@ -284,7 +321,7 @@ def __sym_quantization_info(self, op_info):
284321
"""
285322

286323
#values = op_info.op_attr['value'].value.np_array.flatten()
287-
values = op_info.op_attr['value'].flatten() #FIXME: deal with the proper attribute type here
324+
values = op_info.op_attr['value'].value.np_array.flatten() #FIXME: deal with the proper attribute type here
288325
abs_max = np.absolute(values).max()
289326
#based on quantizted int8 dtype
290327
scale = 127 / abs_max
@@ -366,5 +403,10 @@ def __fb_vector(self, vector_constructor, arr_list):
366403

367404
return self.fbuilder.EndVector(num_items)
368405

406+
def __tflm_graph_legalize_(self, ugraph):
407+
for _, op in ugraph.ops_info.items():
408+
assert op.op_type.startswith("TFLM_") or op.op_type in self.static_op_types
409+
#TODO: strip the leading TFLM_ only
410+
op.op_type = op.op_type.replace("TFLM_", '')
369411
# How to construct the quantization parameter for intermediate tensors?
370412
## zero point and scale

0 commit comments

Comments
 (0)