blob: b8655c97c05f6dc59b144821eae096a05df94a38 [file] [log] [blame]
Louis Verhaard1a92f782021-02-09 16:08:26 +01001# Copyright (C) 2020-2021 Arm Limited or its affiliates. All rights reserved.
Diqing Zhong94457b12020-12-09 15:22:40 +01002#
3# SPDX-License-Identifier: Apache-2.0
4#
5# Licensed under the Apache License, Version 2.0 (the License); you may
6# not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an AS IS BASIS, WITHOUT
13# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16#
17# Description:
Patrik Gustavsson8f1f9aa2021-06-28 07:41:58 +020018# Unit tests for tflite_graph_optimiser
Diqing Zhong94457b12020-12-09 15:22:40 +010019import numpy as np
Louis Verhaardebf4af62021-01-27 15:57:57 +010020import pytest
Diqing Zhong94457b12020-12-09 15:22:40 +010021
Louis Verhaardae2d5532020-12-11 17:19:54 +010022from ethosu.vela.data_type import DataType
Patrik Gustavsson8f1f9aa2021-06-28 07:41:58 +020023from ethosu.vela.graph_optimiser import optimise_graph
Patrik Gustavsson8f1f9aa2021-06-28 07:41:58 +020024from ethosu.vela.nn_graph import NetworkType
Diqing Zhong94457b12020-12-09 15:22:40 +010025from ethosu.vela.operation import Op
Louis Verhaardae2d5532020-12-11 17:19:54 +010026from ethosu.vela.operation import Padding
Patrik Gustavsson3a269202021-01-21 08:28:55 +010027from ethosu.vela.rewrite_graph import verify_graph_health
Diqing Zhong94457b12020-12-09 15:22:40 +010028from ethosu.vela.tensor import create_const_tensor
patrik.gustavssoneeb85152020-12-21 17:10:40 +000029from ethosu.vela.tensor import Shape4D
Diqing Zhong94457b12020-12-09 15:22:40 +010030from ethosu.vela.tensor import Tensor
31from ethosu.vela.test import testutil
Patrik Gustavsson8f1f9aa2021-06-28 07:41:58 +020032from ethosu.vela.tflite_graph_optimiser import calc_explicit_padding
33from ethosu.vela.tflite_graph_optimiser import convert_batched_fc_shape
34from ethosu.vela.tflite_graph_optimiser import replace_pad_by_hw_pad
35from ethosu.vela.tflite_graph_optimiser import rewrite_fully_connected_input
Diqing Zhong94457b12020-12-09 15:22:40 +010036
37
38def test_convert_batched_fc():
39 """Tests shape conversion of batched fully connected"""
Patrik Gustavsson3a269202021-01-21 08:28:55 +010040 ifm_shape = [4, 8]
41 ifm = create_const_tensor("test_in", ifm_shape, np.uint8, np.zeros(ifm_shape))
42 w_shape = [8, 4]
43 weights = create_const_tensor("weight_in", w_shape, np.uint8, np.zeros(w_shape))
Diqing Zhong94457b12020-12-09 15:22:40 +010044 ofm = Tensor(ifm.shape, np.uint8, "test_out")
45 op = testutil.create_op(Op.FullyConnected, [ifm, weights], ofm)
Patrik Gustavsson2349d422020-12-01 16:02:29 +010046
Diqing Zhong94457b12020-12-09 15:22:40 +010047 ifm.consumer_list.append(op)
48
49 prev_op = op.clone()
Patrik Gustavsson3a269202021-01-21 08:28:55 +010050 prev_op.ifm_shapes = op.ifm_shapes.copy()
51 prev_op.ofm_shapes = op.ofm_shapes.copy()
Patrik Gustavsson2349d422020-12-01 16:02:29 +010052
Patrik Gustavsson2c2522d2021-01-29 11:51:31 +010053 rewrite_fully_connected_input(op, None, None)
Diqing Zhong94457b12020-12-09 15:22:40 +010054 conv_op = convert_batched_fc_shape(op, None, None)
Diqing Zhong94457b12020-12-09 15:22:40 +010055 assert conv_op.ifm == prev_op.ifm
56 assert conv_op.ofm == prev_op.ofm
Patrik Gustavsson3a269202021-01-21 08:28:55 +010057 assert op.ifm_shapes[0] == Shape4D([1, 2, 2, 8])
58 assert op.ofm_shapes[0] == Shape4D([1, 2, 2, 8])
Diqing Zhong94457b12020-12-09 15:22:40 +010059 assert conv_op.type == Op.FullyConnected
60 assert len(conv_op.ifm.shape) == 2
Patrik Gustavsson3a269202021-01-21 08:28:55 +010061 assert len(conv_op.ofm.shape) == 2
62 assert conv_op.ifm.shape == conv_op.ofm.shape
63
64 ifm.shape = [1, 8]
65 weights.shape = [8, 1]
66 ofm.shape = [1, 8]
67 op = testutil.create_op(Op.FullyConnected, [ifm, weights], ofm)
68 ifm.consumer_list.append(op)
69
70 prev_op = op.clone()
71 prev_op.ifm_shapes = op.ifm_shapes.copy()
72 prev_op.ofm_shapes = op.ofm_shapes.copy()
73
Patrik Gustavsson2c2522d2021-01-29 11:51:31 +010074 rewrite_fully_connected_input(op, None, None)
Patrik Gustavsson3a269202021-01-21 08:28:55 +010075 conv_op = convert_batched_fc_shape(op, None, None)
76
77 assert conv_op.ifm == prev_op.ifm
78 assert conv_op.ofm == prev_op.ofm
79 assert op.ifm_shapes[0] == prev_op.ifm_shapes[0]
80 assert op.ofm_shapes[0] == prev_op.ofm_shapes[0]
81 assert conv_op.type == Op.FullyConnected
82 assert len(conv_op.ifm.shape) == 2
83 assert len(conv_op.ofm.shape) == 2
Diqing Zhong94457b12020-12-09 15:22:40 +010084 assert conv_op.ifm.shape == conv_op.ofm.shape
Louis Verhaardae2d5532020-12-11 17:19:54 +010085
86
Louis Verhaardebf4af62021-01-27 15:57:57 +010087explicit_padding_test_data = [
88 # Kernel size 2
89 [(17, 1, 2, 1, 1), (1, 1)],
90 [(18, 1, 2, 0, 1), (0, 1)],
91 [(18, 1, 2, 1, 0), (1, 0)],
92 # Kernel size 3
93 [(18, 2, 3, 1, 1), (1, 0)],
94 [(25, 2, 3, 1, 1), (1, 1)],
95 # Kernel size 4
96 [(18, 1, 4, 1, 2), (1, 2)],
97 [(18, 1, 4, 2, 1), (2, 1)],
98 [(19, 1, 4, 2, 2), (2, 2)],
99 # Kernel size 5
100 [(19, 1, 5, 1, 2), (1, 2)],
101 [(19, 1, 5, 0, 2), (0, 2)],
102 [(19, 1, 5, 1, 0), (1, 0)],
103 # Kernel size 21
104 [(41, 2, 21, 8, 10), (8, 10)],
105 [(41, 3, 21, 10, 10), (10, 9)],
106 [(42, 3, 21, 10, 10), (10, 8)],
107 [(42, 3, 21, 9, 10), (9, 9)],
108 [(41, 3, 21, 10, 6), (10, 6)],
109]
110
111
112@pytest.mark.parametrize("test_input, expected_result", explicit_padding_test_data)
113def test_calc_explicit_padding(test_input, expected_result):
114 input_size, stride, filter_size, explicit_pad_before, explicit_pad_after = test_input
115 before, after = calc_explicit_padding(input_size, stride, filter_size, explicit_pad_before, explicit_pad_after)
116 assert (before, after) == expected_result
117
118
Louis Verhaardc822d622021-03-11 14:59:06 +0100119def create_pad_and_conv2d(
120 in_shape,
121 out_shape,
122 padding,
123 in_dtype=DataType.int8,
124 out_dtype=DataType.int8,
125 pad_dtype=DataType.int32,
126 pad_setting=Padding.VALID,
127 kernel_size=3,
128):
129 """Creates Pad operator followed by a conv2d operator"""
130 qp = testutil.default_quant_params()
131 in0 = Tensor(in_shape, in_dtype, "in")
132 in0.quantization = qp
133 pad_tensor = create_const_tensor(name="pad", shape=list(np.shape(padding)), values=padding, dtype=pad_dtype)
134 out = Tensor(out_shape, out_dtype, "out")
135 out.quantization = qp.clone()
136 op = testutil.create_op(Op.Pad, [in0, pad_tensor], out)
137 op.run_on_npu = True
138 conv_out_tens = Tensor(in_shape, in_dtype, "output")
139 conv_out_tens.quantization = qp.clone()
140 weight_tens = Tensor([kernel_size, kernel_size, in_shape[-1], out_shape[-1]], in_dtype, "weights")
James Peet7519d502021-07-19 16:47:58 +0100141 weight_tens.values = np.zeros(weight_tens.shape, in_dtype.as_numpy_type())
Louis Verhaardc822d622021-03-11 14:59:06 +0100142 weight_tens.quantization = qp.clone()
143 bias_tens = Tensor(out_shape, pad_dtype, "biases")
144 attrs = {"padding": pad_setting, "stride_w": 2, "stride_h": 2, "dilation_w_factor": 1, "dilation_h_factor": 1}
145 attrs["strides"] = (1, attrs["stride_h"], attrs["stride_w"], 1)
146 conv2d_op = testutil.create_op(Op.Conv2DBias, [out, weight_tens, bias_tens], conv_out_tens, attrs)
147 conv2d_op.add_input_tensor(out)
148 conv2d_op.run_on_npu = True
149 return op, conv2d_op
150
151
152def test_pad_followed_by_conv_is_removed():
Louis Verhaardae2d5532020-12-11 17:19:54 +0100153 """
154 Tests that the PAD operator is bypassed when followed by a convolution operator,
155 and that the padding of the convolution operation is correctly updated
156 """
Louis Verhaardc822d622021-03-11 14:59:06 +0100157 pad_op, conv2d_op = create_pad_and_conv2d(
158 in_shape=[1, 76, 75, 64], out_shape=[1, 76, 75, 64], padding=[[0, 0], [2, 1], [1, 1], [0, 0]], kernel_size=4
159 )
160 nng = testutil.create_graph([pad_op, conv2d_op])
Louis Verhaardae2d5532020-12-11 17:19:54 +0100161 arch = testutil.create_arch()
162
Louis Verhaardc822d622021-03-11 14:59:06 +0100163 replace_pad_by_hw_pad(conv2d_op, nng, arch)
Louis Verhaardae2d5532020-12-11 17:19:54 +0100164
Louis Verhaardc822d622021-03-11 14:59:06 +0100165 op = nng.subgraphs[0].output_tensors[0].ops[0]
166 assert op.type == Op.Conv2DBias
Louis Verhaardae2d5532020-12-11 17:19:54 +0100167 assert op.attrs["padding"] == Padding.EXPLICIT
168 assert op.attrs["explicit_padding"] == (2, 1, 1, 1)
169 assert op.ifm.shape == [1, 76, 75, 64]
170 assert pad_op not in op.ifm.ops
Patrik Gustavsson3a269202021-01-21 08:28:55 +0100171
172
Louis Verhaardc822d622021-03-11 14:59:06 +0100173leading_pad_test_data = [
174 (2, 2, 11, True),
175 (1, 2, 11, False),
176 (2, 1, 11, False),
177 (5, 2, 11, True),
178]
179
180
181@pytest.mark.parametrize("top, left, kernel_size, expect_pad_removed", leading_pad_test_data)
182def test_leading_pad_size(top, left, kernel_size, expect_pad_removed):
183 # Tests PAD operator with big kernel size; top and left pad must be multiple of stride
184 out_shape = [1, 11 + left, 11 + top, 1]
185 padding = [[0, 0], [top, 0], [left, 0], [0, 0]]
186 pad_op, conv2d_op = create_pad_and_conv2d(
187 in_shape=[1, 11, 11, 1], out_shape=out_shape, padding=padding, kernel_size=kernel_size
188 )
189 nng = testutil.create_graph([pad_op, conv2d_op])
190 arch = testutil.create_arch()
191 replace_pad_by_hw_pad(conv2d_op, nng, arch)
192 op = nng.subgraphs[0].output_tensors[0].ops[0]
193 if expect_pad_removed:
194 assert op.attrs["padding"] == Padding.EXPLICIT
195 assert "explicit_padding" in op.attrs
196 assert op.ifm.shape == op.ofm.shape
197 assert pad_op not in op.ifm.ops
198 else:
199 assert pad_op in op.ifm.ops
200 assert op.attrs["padding"] == Padding.VALID
201 assert "explicit_padding" not in op.attrs
202
203
Louis Verhaard1a92f782021-02-09 16:08:26 +0100204def test_optimise_pad_followed_by_avg_pool():
205 """
206 Tests that the PAD operator is bypassed when followed by a average pool operator,
207 and that the average pool is converted to a depthwise
208 """
209 # Create Pad operation followed by AvgPool
210 quant = testutil.default_quant_params()
211 in_tens = Tensor([1, 76, 75, 64], DataType.uint8, "input")
212 in_tens.quantization = quant
Louis Verhaardc822d622021-03-11 14:59:06 +0100213 # Test with 3x2 input tensor
214 pad_input = create_const_tensor("pad_input", [3, 2], DataType.int32, [[2, 2], [1, 1], [0, 0]])
Louis Verhaard1a92f782021-02-09 16:08:26 +0100215 temp_tens = Tensor([1, 79, 77, 64], DataType.uint8, "pad_out")
216 temp_tens.quantization = quant.clone()
217 out_tens = Tensor([1, 76, 75, 64], DataType.uint8, "output")
218 out_tens.quantization = quant.clone()
219
220 pad_op = testutil.create_op(Op.Pad, [in_tens, pad_input], temp_tens)
221 attrs = {
222 "padding": Padding.VALID,
223 "ksize": [1, 5, 3, 1],
224 "stride_w": 2,
225 "stride_h": 2,
226 "dilation_w_factor": 1,
227 "dilation_h_factor": 1,
228 }
229 attrs["strides"] = (1, attrs["stride_h"], attrs["stride_w"], 1)
230 pad_op.run_on_npu = True
231 conv2d_op = testutil.create_op(Op.AvgPool, [temp_tens], out_tens, attrs)
232 conv2d_op.run_on_npu = True
Louis Verhaardc822d622021-03-11 14:59:06 +0100233 nng = testutil.create_graph([pad_op, conv2d_op])
Louis Verhaard1a92f782021-02-09 16:08:26 +0100234 arch = testutil.create_arch()
235
Louis Verhaardc822d622021-03-11 14:59:06 +0100236 replace_pad_by_hw_pad(conv2d_op, nng, arch)
Louis Verhaard1a92f782021-02-09 16:08:26 +0100237
Louis Verhaardc822d622021-03-11 14:59:06 +0100238 op = nng.subgraphs[0].output_tensors[0].ops[0]
Louis Verhaard1a92f782021-02-09 16:08:26 +0100239 assert op.type == Op.DepthwiseConv2DBias
240 assert op.attrs["padding"] == Padding.EXPLICIT
Louis Verhaardc822d622021-03-11 14:59:06 +0100241 assert op.attrs["explicit_padding"] == (2, 1, 2, 1)
Louis Verhaard1a92f782021-02-09 16:08:26 +0100242 assert op.ifm.shape == [1, 76, 75, 64]
243 assert pad_op not in op.ifm.ops
244 # Check that bias and weight tensors have been added
245 assert op.bias.shape == [64]
Louis Verhaard1a92f782021-02-09 16:08:26 +0100246 assert op.weights.shape == [5, 3, 1, 64]
247
248
Louis Verhaardc822d622021-03-11 14:59:06 +0100249pad_avg_pool_test_data = [
250 ((3, 3), (1, 1, 1, 1), True),
251 ((3, 3), (2, 1, 1, 1), False),
252 ((3, 3), (1, 2, 1, 1), False),
253 ((3, 3), (1, 1, 2, 1), False),
254 ((3, 3), (1, 1, 1, 2), False),
255 ((2, 4), (1, 2, 1, 2), True),
256 ((5, 3), (2, 1, 2, 1), True),
257 ((5, 3), (0, 1, 2, 1), True),
258 ((5, 3), (2, 0, 2, 1), True),
259 ((5, 3), (2, 1, 0, 1), True),
260 ((5, 3), (2, 1, 0, 1), True),
261 ((4, 4), (2, 2, 2, 2), True),
262 ((4, 4), (1, 2, 2, 2), False),
263 ((4, 4), (2, 1, 2, 2), False),
264 ((4, 4), (2, 2, 1, 2), False),
265 ((4, 4), (2, 2, 2, 1), False),
266]
267
268
269@pytest.mark.parametrize("k_size, padding, expect_pad_removed", pad_avg_pool_test_data)
270def test_pad_followed_by_avg_pool(k_size, padding, expect_pad_removed):
271 # Tests PAD followed by AvgPool
272 k_w, k_h = k_size
273 top, left, bottom, right = padding
274 pad_values = [[0, 0], [top, bottom], [left, right], [0, 0]]
275 dtype = DataType.int8
276 qp = testutil.default_quant_params()
277 in_shape = [1, 15, 17, 8]
278 out_shape = [1, in_shape[1] + top + bottom, in_shape[2] + left + right, in_shape[3]]
279 in0 = Tensor(in_shape, dtype, "in")
280 in0.quantization = qp
281 pad_tensor = create_const_tensor(
282 name="pad", shape=list(np.shape(pad_values)), values=pad_values, dtype=DataType.int32
283 )
284 out = Tensor(out_shape, dtype, "out")
285 out.quantization = qp.clone()
286 pad_op = testutil.create_op(Op.Pad, [in0, pad_tensor], out)
287 pool_out_tens = Tensor(in_shape, dtype, "output")
288 pool_out_tens.quantization = qp.clone()
289 attrs = {
290 "padding": Padding.VALID,
291 "ksize": [1, k_w, k_h, 1],
292 "stride_w": 1,
293 "stride_h": 1,
294 "dilation_w_factor": 1,
295 "dilation_h_factor": 1,
296 }
297 pool_op = testutil.create_op(Op.AvgPool, [out], pool_out_tens, attrs)
Louis Verhaardc822d622021-03-11 14:59:06 +0100298 pad_op.run_on_npu = True
299 pool_op.run_on_npu = True
300 nng = testutil.create_graph([pad_op, pool_op])
301 arch = testutil.create_arch()
Patrik Gustavsson8f1f9aa2021-06-28 07:41:58 +0200302 nng = optimise_graph(nng, arch, NetworkType.TFLite)
Louis Verhaardc822d622021-03-11 14:59:06 +0100303 sg = nng.subgraphs[0]
304 all_ops = sg.get_all_ops()
305 print("all_ops: ", all_ops)
306 # Pad should not be in the graph anymore, it should either have been removed or rewritten
307 assert not any(op.type == Op.Pad for op in all_ops)
308 op = nng.subgraphs[0].output_tensors[0].ops[0]
309 if expect_pad_removed:
310 # Expect rewrite to depthwise, PAD is removed
311 assert op.type == Op.DepthwiseConv2DBias
312 assert op.attrs["padding"] == Padding.EXPLICIT
313 assert any(pad > 0 for pad in op.attrs["explicit_padding"])
314 assert op.ifm.shape == op.ofm.shape
315 # Check that bias and weight tensors have been added
316 assert len(op.bias.shape) > 0
317 assert op.weights.shape is not None
318 else:
319 # Pad should have been rewritten to a number of average pool operations
320 assert all(op.type in (Op.AvgPool, Op.Const) for op in all_ops)
321 assert pool_op.type == Op.AvgPool
322 assert pool_op.attrs["padding"] == Padding.VALID
323
324
Jonas Ohlsson0957e3e2021-09-01 15:57:21 +0200325def test_remove_reshape():
326 """
327 Test that the expected reshape are removed in graph_optimisation
328 """
Jonas Ohlssonfbfd96e2021-08-25 11:38:03 +0200329
Jonas Ohlsson0957e3e2021-09-01 15:57:21 +0200330 # Create tensors and operators Test1
Jonas Ohlssonfbfd96e2021-08-25 11:38:03 +0200331 quant = testutil.default_quant_params()
Jonas Ohlsson0957e3e2021-09-01 15:57:21 +0200332
Jonas Ohlssonfbfd96e2021-08-25 11:38:03 +0200333 # create reshape1 op
334 ifm_shape = [64, 16]
335 reshape1_ofm_shape = [1, 4, 16, 16]
Jonas Ohlsson0957e3e2021-09-01 15:57:21 +0200336 reshape1_ifm = create_const_tensor("reshape1_in", ifm_shape, DataType.uint8, np.zeros(ifm_shape))
Jonas Ohlssonfbfd96e2021-08-25 11:38:03 +0200337 reshape1_ifm.quantization = quant
Jonas Ohlsson0957e3e2021-09-01 15:57:21 +0200338 reshape1_ofm = create_const_tensor("reshape1_out", reshape1_ofm_shape, DataType.uint8, np.zeros(reshape1_ofm_shape))
Jonas Ohlssonfbfd96e2021-08-25 11:38:03 +0200339 reshape1_ofm.quantization = quant
Jonas Ohlsson0957e3e2021-09-01 15:57:21 +0200340 shape_tens = create_const_tensor("reshape1_shape", [1], DataType.int32, reshape1_ofm_shape)
341 reshape1_op = testutil.create_op(Op.Reshape, [reshape1_ifm, shape_tens], reshape1_ofm, set_ifm_ofm_shapes=False)
Jonas Ohlssonfbfd96e2021-08-25 11:38:03 +0200342 reshape1_op.attrs["new_shape"] = reshape1_ofm_shape
343 reshape1_op.run_on_npu = True
344
345 # create conv op
346 conv_ofm = Tensor([1, 8, 8, 16], DataType.uint8, "output")
347 conv_ofm.quantization = quant.clone()
348 weight_tens = Tensor([1, 1, 16, 16], DataType.uint8, "weights")
349 weight_tens.values = np.zeros(weight_tens.shape, np.uint8)
350 weight_tens.quantization = quant.clone()
351 bias_tens = Tensor([16], DataType.int32, "biases")
352
353 attrs = {"padding": Padding.SAME, "stride_w": 1, "stride_h": 1, "dilation_w_factor": 1, "dilation_h_factor": 1}
354 attrs["strides"] = (1, attrs["stride_h"], attrs["stride_w"], 1)
355
356 conv2d_op = testutil.create_op(
357 Op.Conv2D, [reshape1_ofm, weight_tens, bias_tens], conv_ofm, attrs=attrs, set_ifm_ofm_shapes=False
358 )
359 conv2d_op.run_on_npu = True
360
361 # create reshape2 op
362 ofm_shape = [8, 8, 16]
Jonas Ohlsson0957e3e2021-09-01 15:57:21 +0200363 reshape2_ofm = create_const_tensor("reshape2_out", ofm_shape, DataType.uint8, np.zeros(ofm_shape))
Jonas Ohlssonfbfd96e2021-08-25 11:38:03 +0200364 reshape2_ofm.quantization = quant
Jonas Ohlsson0957e3e2021-09-01 15:57:21 +0200365 shape_tens = create_const_tensor("reshape2_shape", [1], DataType.int32, ofm_shape)
366 reshape2_op = testutil.create_op(Op.Reshape, [conv_ofm, shape_tens], reshape2_ofm, set_ifm_ofm_shapes=False)
Jonas Ohlssonfbfd96e2021-08-25 11:38:03 +0200367 reshape2_op.attrs["new_shape"] = ofm_shape
368 reshape2_op.run_on_npu = True
Patrik Gustavsson3a269202021-01-21 08:28:55 +0100369
Patrik Gustavsson3a269202021-01-21 08:28:55 +0100370 # Test1 no Reshape op is expected to remain in the NPU subgrapgh
371 # but first one will be put on CPU
Patrik Gustavsson138d47f2021-02-08 10:13:48 +0100372 # Network is Reshape-Conv-Reshape
373 # Result is Conv
Jonas Ohlsson0957e3e2021-09-01 15:57:21 +0200374 nng = testutil.create_graph([reshape1_op, conv2d_op, reshape2_op])
Patrik Gustavsson3a269202021-01-21 08:28:55 +0100375 arch = testutil.create_arch()
376 assert verify_graph_health(nng)
Jonas Ohlsson0957e3e2021-09-01 15:57:21 +0200377 nng = optimise_graph(nng, arch, NetworkType.TFLite, True)
Patrik Gustavsson3a269202021-01-21 08:28:55 +0100378 assert verify_graph_health(nng)
Patrik Gustavsson3a269202021-01-21 08:28:55 +0100379
Jonas Ohlsson0957e3e2021-09-01 15:57:21 +0200380 # Create tensors and operator Test2
381 # create reshape op
382 reshape_ifm = create_const_tensor("reshape_in", ifm_shape, DataType.uint8, np.zeros(ifm_shape))
383 reshape_ifm.quantization = quant
384 reshape_ofm = create_const_tensor("reshape1_out", reshape1_ofm_shape, DataType.uint8, np.zeros(reshape1_ofm_shape))
385 reshape_ofm.quantization = quant
386 shape_tens = create_const_tensor("reshape1_shape", [1], DataType.int32, reshape1_ofm_shape)
387 reshape_op = testutil.create_op(Op.Reshape, [reshape_ifm, shape_tens], reshape_ofm, set_ifm_ofm_shapes=False)
388 reshape_op.attrs["new_shape"] = reshape1_ofm_shape
389 reshape_op.run_on_npu = True
390
391 # Test2 Reshape ifm/ofm is sg input/output.
392 # Reshape op is expected to be replaced by a AvgPool 'NOP'.
393 #
394 # Network is Reshape
395 # expected is AvgPool
396 nng = testutil.create_graph([reshape_op])
Patrik Gustavsson3a269202021-01-21 08:28:55 +0100397 assert verify_graph_health(nng)
Jonas Ohlsson0957e3e2021-09-01 15:57:21 +0200398 nng = optimise_graph(nng, arch, NetworkType.TFLite, True)
Patrik Gustavsson3a269202021-01-21 08:28:55 +0100399 assert verify_graph_health(nng)
Jonas Ohlssonfbfd96e2021-08-25 11:38:03 +0200400
401
402def test_remove_squeeze():
403 """
404 Tests that the expected squeeze are removed in graph_optimisation
405 """
406
Jonas Ohlsson0957e3e2021-09-01 15:57:21 +0200407 # Create tensors and operators Test1
408 quant = testutil.default_quant_params()
409
410 # create conv op
411 ifm_shape = [1, 1, 1, 1024]
412 conv_ifm = create_const_tensor("conv_in", ifm_shape, DataType.uint8, np.zeros(ifm_shape))
413 conv_ifm.quantization = quant
414 conv_ofm = Tensor([1, 1, 1, 1001], DataType.uint8, "output")
415 conv_ofm.quantization = quant.clone()
416 weight_tens = Tensor([1, 1, 1024, 1001], DataType.uint8, "weights")
417 weight_tens.values = np.zeros(weight_tens.shape, np.uint8)
418 weight_tens.quantization = quant.clone()
419 bias_tens = Tensor([1001], DataType.int32, "biases")
420
421 attrs = {"padding": Padding.SAME, "stride_w": 1, "stride_h": 1, "dilation_w_factor": 1, "dilation_h_factor": 1}
422 attrs["strides"] = (1, attrs["stride_h"], attrs["stride_w"], 1)
423
424 conv2d_op = testutil.create_op(
425 Op.Conv2D, [conv_ifm, weight_tens, bias_tens], conv_ofm, attrs=attrs, set_ifm_ofm_shapes=False
426 )
427 conv2d_op.run_on_npu = True
428
429 # create squeeze op
430 ofm_shape = [1, 1001]
431 squeeze_ofm = create_const_tensor("squeeze_out", ofm_shape, DataType.uint8, np.zeros(ofm_shape))
432 squeeze_ofm.quantization = quant.clone()
433 squeeze_op = testutil.create_op(Op.Squeeze, [conv_ofm], squeeze_ofm, set_ifm_ofm_shapes=False)
434 squeeze_op.attrs["squeeze_dims"] = [1, 2]
435 squeeze_op.run_on_npu = True
436
Jonas Ohlssonfbfd96e2021-08-25 11:38:03 +0200437 # Test1 no Squeeze op is expected to remain in the NPU subgrapgh
Jonas Ohlsson0957e3e2021-09-01 15:57:21 +0200438 #
439 # Network is Conv-Squeeze
Jonas Ohlssonfbfd96e2021-08-25 11:38:03 +0200440 # Result is Conv
Jonas Ohlsson0957e3e2021-09-01 15:57:21 +0200441 nng = testutil.create_graph([conv2d_op, squeeze_op])
Jonas Ohlssonfbfd96e2021-08-25 11:38:03 +0200442 arch = testutil.create_arch()
443 assert verify_graph_health(nng)
Jonas Ohlsson0957e3e2021-09-01 15:57:21 +0200444 nng = optimise_graph(nng, arch, NetworkType.TFLite, True)
Jonas Ohlssonfbfd96e2021-08-25 11:38:03 +0200445 assert verify_graph_health(nng)
446
Jonas Ohlsson0957e3e2021-09-01 15:57:21 +0200447 # Create tensors and operator Test2
448 # create squeeze op
449 ifm_shape = [1, 1, 1, 1001]
450 squeeze_ifm = create_const_tensor("squeeze_in", ifm_shape, DataType.uint8, np.zeros(ifm_shape))
451 squeeze_ifm.quantization = quant
452 squeeze_ofm = create_const_tensor("squeeze_out", ofm_shape, DataType.uint8, np.zeros(ofm_shape))
453 squeeze_ofm.quantization = quant.clone()
454 squeeze_op = testutil.create_op(Op.Squeeze, [squeeze_ifm], squeeze_ofm, set_ifm_ofm_shapes=False)
455 squeeze_op.attrs["squeeze_dims"] = [1, 2]
456 squeeze_op.run_on_npu = True
457
458 # Test2 Squeeze ifm/ofm is sg input/output.
459 # Squeeze op is expected to be replaced by a AvgPool 'NOP'.
460 #
461 # Network is Squeeze
462 # expected is AvgPool
463 nng = testutil.create_graph([squeeze_op])
Jonas Ohlssonfbfd96e2021-08-25 11:38:03 +0200464 assert verify_graph_health(nng)
Jonas Ohlsson0957e3e2021-09-01 15:57:21 +0200465 nng = optimise_graph(nng, arch, NetworkType.TFLite, True)
466 assert verify_graph_health(nng)
467
468
469def test_remove_expand_dims():
470 """
471 Tests that the expected ExpandDims are removed in graph_optimisation
472 """
473
474 # Create tensors and operators Test1
475 quant = testutil.default_quant_params()
476
477 # create ExpandDims op
478 ifm_shape = [4, 16, 16]
479 ofm_shape = [1, 4, 16, 16]
480 expand_dims_ifm = create_const_tensor("expand_dims_in", ifm_shape, DataType.uint8, np.zeros(ifm_shape))
481 expand_dims_ifm.quantization = quant
482 expand_dims_ofm = create_const_tensor("expand_dims_out", ofm_shape, DataType.uint8, np.zeros(ofm_shape))
483 expand_dims_ofm.quantization = quant.clone()
484 dim_tens = create_const_tensor("dim_tens", [], DataType.uint8, 1)
485 expand_dims_op = testutil.create_op(
486 Op.ExpandDims, [expand_dims_ifm, dim_tens], expand_dims_ofm, set_ifm_ofm_shapes=False
487 )
488 expand_dims_op.run_on_npu = True
489
490 # create conv op
491 conv_ofm = Tensor([1, 8, 8, 16], DataType.uint8, "output")
492 conv_ofm.quantization = quant.clone()
493 weight_tens = Tensor([1, 1, 16, 16], DataType.uint8, "weights")
494 weight_tens.values = np.zeros(weight_tens.shape, np.uint8)
495 weight_tens.quantization = quant.clone()
496 bias_tens = Tensor([16], DataType.int32, "biases")
497
498 attrs = {"padding": Padding.SAME, "stride_w": 1, "stride_h": 1, "dilation_w_factor": 1, "dilation_h_factor": 1}
499 attrs["strides"] = (1, attrs["stride_h"], attrs["stride_w"], 1)
500
501 conv2d_op = testutil.create_op(
502 Op.Conv2D, [expand_dims_ofm, weight_tens, bias_tens], conv_ofm, attrs=attrs, set_ifm_ofm_shapes=False
503 )
504 conv2d_op.run_on_npu = True
505
506 # Test1 no ExpandDims op is expected to remain in the NPU subgrapgh
507 #
508 # Network is ExpandDims-Conv
509 # Result is Conv
510 nng = testutil.create_graph([expand_dims_op, conv2d_op])
511 arch = testutil.create_arch()
512 assert verify_graph_health(nng)
513 nng = optimise_graph(nng, arch, NetworkType.TFLite, True)
514 assert verify_graph_health(nng)
515
516 # create ExpandDims op
517 expand_dims_ifm = create_const_tensor("expand_dims_in", ifm_shape, DataType.uint8, np.zeros(ifm_shape))
518 expand_dims_ifm.quantization = quant
519 expand_dims_ofm = create_const_tensor("expand_dims_out", ofm_shape, DataType.uint8, np.zeros(ofm_shape))
520 expand_dims_ofm.quantization = quant.clone()
521 dim_tens = create_const_tensor("dim_tens", [], DataType.uint8, 1)
522 expand_dims_op = testutil.create_op(
523 Op.ExpandDims, [expand_dims_ifm, dim_tens], expand_dims_ofm, set_ifm_ofm_shapes=False
524 )
525 expand_dims_op.run_on_npu = True
526
527 # Test2 ExpandDims ifm/ofm is sg input/output.
528 # ExpandDims op is expected to be replaced by a AvgPool 'NOP'.
529 #
530 # Network is ExpandDims
531 # expected is AvgPool
532 nng = testutil.create_graph([expand_dims_op])
533 assert verify_graph_health(nng)
534 nng = optimise_graph(nng, arch, NetworkType.TFLite, True)
Jonas Ohlssonfbfd96e2021-08-25 11:38:03 +0200535 assert verify_graph_health(nng)