From 8e2279c5e6a66ceb7ea2647c0e79abc344fe5be3 Mon Sep 17 00:00:00 2001 From: Mirzam98 Date: Fri, 11 Dec 2020 17:47:15 +0100 Subject: [PATCH] minor style changes --- src/finn/custom_op/general/im2col.py | 66 +++++--- tests/custom_op/test_im2col.py | 236 +++++++++++---------------- 2 files changed, 143 insertions(+), 159 deletions(-) diff --git a/src/finn/custom_op/general/im2col.py b/src/finn/custom_op/general/im2col.py index 52b6276..a266b18 100644 --- a/src/finn/custom_op/general/im2col.py +++ b/src/finn/custom_op/general/im2col.py @@ -9,9 +9,10 @@ # utilities to generate a patch matrix from a multichannel image # of shape (batches, channels, height, width) + def compute_conv_output_dim(ifm_dim, k, stride, pad=0): """Returns spatial output dimension size for convolution with given params.""" - if (ifm_dim == 1): + if ifm_dim == 1: out_dim = 1 else: out_dim = int(((ifm_dim + 2 * pad - k) / stride) + 1) @@ -49,17 +50,26 @@ def im2col_indices_nchw( # Zero-pad the input p = padding - if (H == 1): # Shape of input image is: (1, C, 1, W) + if H == 1: # Shape of input image is: (1, C, 1, W) x_padded = np.pad( - x, ((0, 0), (0, 0), (0, 0), (p, p)), mode="constant", constant_values=pad_val + x, + ((0, 0), (0, 0), (0, 0), (p, p)), + mode="constant", + constant_values=pad_val, ) - elif (W == 1): # Shape of input image is: (1, C, H, 1) + elif W == 1: # Shape of input image is: (1, C, H, 1) x_padded = np.pad( - x, ((0, 0), (0, 0), (p, p), (0, 0)), mode="constant", constant_values=pad_val + x, + ((0, 0), (0, 0), (p, p), (0, 0)), + mode="constant", + constant_values=pad_val, ) - elif (H > 1 and W > 1): # Shape of input image is: (1, C, H, W) + elif H > 1 and W > 1: # Shape of input image is: (1, C, H, W) x_padded = np.pad( - x, ((0, 0), (0, 0), (p, p), (p, p)), mode="constant", constant_values=pad_val + x, + ((0, 0), (0, 0), (p, p), (p, p)), + mode="constant", + constant_values=pad_val, ) k, i, j = get_im2col_indices_nchw( @@ -98,7 +108,7 @@ def get_nodeattr_types(self): } def make_shape_compatible_op(self, model): - k = self.get_nodeattr("kernel_size") # Assumption: Height x Width + k = self.get_nodeattr("kernel_size") # Assumption: Height x Width stride = self.get_nodeattr("stride") ishape = self.get_nodeattr("input_shape") pad = self.get_nodeattr("pad_amount") @@ -110,7 +120,7 @@ def make_shape_compatible_op(self, model): for i in range(0, len(ishape)): ishape[i] = int(ishape[i]) - if len(k) == 1: # Assume square kernel + if len(k) == 1: # Assume square kernel k_H = k[0] k_W = k[0] else: @@ -120,19 +130,25 @@ def make_shape_compatible_op(self, model): # extract all necessary information and determine output dimensions ifm_ch = ishape[-1] assert len(ishape) == 4, "Unexpected input shape for Im2Col" - ifm_dim_H = ishape[1] # NHWC + ifm_dim_H = ishape[1] # NHWC ifm_dim_W = ishape[2] - if (ifm_dim_H == 1): - assert (k_H == 1), "Unexpected kernel shape for input image of dimensions (N, 1, W, C)" - if (ifm_dim_W == 1): - assert (k_W == 1), "Unexpected kernel shape for input image of dimensions (N, H, 1, C)" + if ifm_dim_H == 1: + assert ( + k_H == 1 + ), "Unexpected kernel shape for input image of dimensions (N, 1, W, C)" + if ifm_dim_W == 1: + assert ( + k_W == 1 + ), "Unexpected kernel shape for input image of dimensions (N, H, 1, C)" ofm_dim_H = compute_conv_output_dim(ifm_dim_H, k_H, stride, pad) ofm_dim_W = compute_conv_output_dim(ifm_dim_W, k_W, stride, pad) # implement tensor with correct shape - values = np.random.randn(1, ofm_dim_H, ofm_dim_W, k_H * k_W * ifm_ch).astype(np.float32) + values = np.random.randn(1, ofm_dim_H, ofm_dim_W, k_H * k_W * ifm_ch).astype( + np.float32 + ) return helper.make_node( "Constant", inputs=[], @@ -153,9 +169,9 @@ def infer_node_datatype(self, model): def execute_node(self, context, graph): node = self.onnx_node - k = self.get_nodeattr("kernel_size") # Assumption: Height x Width + k = self.get_nodeattr("kernel_size") # Assumption: Height x Width - if len(k) == 1: # Assume square kernel + if len(k) == 1: # Assume square kernel k_H = k[0] k_W = k[0] else: @@ -177,17 +193,23 @@ def execute_node(self, context, graph): assert x.ndim == 4, "Unexpected number of input dims for Im2Col" N, H, W, C = x.shape - if (H == 1): - assert (k_H == 1), "Unexpected kernel shape for input image of dimensions (N, 1, W, C)" - if (W == 1): - assert (k_W == 1), "Unexpected kernel shape for input image of dimensions (N, H, 1, C)" + if H == 1: + assert ( + k_H == 1 + ), "Unexpected kernel shape for input image of dimensions (N, 1, W, C)" + if W == 1: + assert ( + k_W == 1 + ), "Unexpected kernel shape for input image of dimensions (N, H, 1, C)" out_dim_H = compute_conv_output_dim(H, k_H, stride, pad) out_dim_W = compute_conv_output_dim(W, k_W, stride, pad) # internally convert input to NCHW x = x.transpose(0, 3, 1, 2) # call NCHW im2col implementation - ret = im2col_indices_nchw(x, H, W, k_H, k_W, pad, stride, stride, pad_val=pad_val) + ret = im2col_indices_nchw( + x, H, W, k_H, k_W, pad, stride, stride, pad_val=pad_val + ) # result shape is (k_H*k_W*N, out_dim_H*out_dim_W), convert to NCHW ret = ret.reshape(N, C, k_H, k_W, out_dim_H, out_dim_W) # (N=0,C=1,kh=2,kw=3,H=4,W=5) -> (N=0,H=4,W=5,kh=2,kw=3,C=1) diff --git a/tests/custom_op/test_im2col.py b/tests/custom_op/test_im2col.py index 2b301aa..7ae22ec 100644 --- a/tests/custom_op/test_im2col.py +++ b/tests/custom_op/test_im2col.py @@ -21,7 +21,10 @@ def check_two_dict_for_equality(dict1, dict2): return True -def execution_im2col(x, idt, k_H, k_W, stride, ifm_ch, ifm_dim_H, ifm_dim_W, pad_amt=0, pad_val=0): + +def execution_im2col( + x, idt, k_H, k_W, stride, ifm_ch, ifm_dim_H, ifm_dim_W, pad_amt=0, pad_val=0 +): ofm_dim_H = compute_conv_output_dim(ifm_dim_H, k_H, stride, pad_amt) ofm_dim_W = compute_conv_output_dim(ifm_dim_W, k_W, stride, pad_amt) @@ -56,7 +59,12 @@ def execution_im2col(x, idt, k_H, k_W, stride, ifm_ch, ifm_dim_H, ifm_dim_W, pad # test shape inference model.transform(InferShapes()) - assert model.get_tensor_shape("outp") == [1, ofm_dim_H, ofm_dim_W, k_H * k_W * ifm_ch] + assert model.get_tensor_shape("outp") == [ + 1, + ofm_dim_H, + ofm_dim_W, + k_H * k_W * ifm_ch, + ] # test datatype inference assert model.get_tensor_datatype("outp") is DataType.FLOAT32 @@ -72,7 +80,7 @@ def execution_im2col(x, idt, k_H, k_W, stride, ifm_ch, ifm_dim_H, ifm_dim_W, pad return y_produced -### Configurations tested: +# Configurations tested: # case id | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | # idt | Bipolar | INT8 | INT8 | INT8 | INT8 | INT8 | INT8 | INT8 | INT8 | # ifm_dim_H | 4 | 4 | 4 | 4 | 4 | 4 | 5 | 5 | 5 | @@ -84,6 +92,7 @@ def execution_im2col(x, idt, k_H, k_W, stride, ifm_ch, ifm_dim_H, ifm_dim_W, pad # k_W | 2 | 2 | 2 | 2 | 2 | 2 | 1 | 1 | 1 | # stride | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 2 | + def test_im2col(): case_id = 0 # bipolar inputs with following im2col parameters @@ -164,8 +173,12 @@ def test_im2col(): dtype=np.float32, ).reshape(1, ofm_dim_H, ofm_dim_W, k_H * k_W * ifm_ch) - produced = execution_im2col(x, idt, k_H, k_W, stride, ifm_ch, ifm_dim_H, ifm_dim_W, pad_amt, pad_val) - assert (produced == expected).all(), "Test failed for case number {}".format(case_id) + produced = execution_im2col( + x, idt, k_H, k_W, stride, ifm_ch, ifm_dim_H, ifm_dim_W, pad_amt, pad_val + ) + assert (produced == expected).all(), "Test failed for case number {}".format( + case_id + ) case_id = 1 idt = DataType.INT8 @@ -216,8 +229,12 @@ def test_im2col(): dtype=np.float32, ) - produced = execution_im2col(x, idt, k_H, k_W, stride, ifm_ch, ifm_dim_H, ifm_dim_W, pad_amt, pad_val) - assert (produced == expected).all(), "Test failed for case number {}".format(case_id) + produced = execution_im2col( + x, idt, k_H, k_W, stride, ifm_ch, ifm_dim_H, ifm_dim_W, pad_amt, pad_val + ) + assert (produced == expected).all(), "Test failed for case number {}".format( + case_id + ) case_id = 2 idt = DataType.INT8 @@ -288,10 +305,13 @@ def test_im2col(): dtype=np.float32, ) - produced = execution_im2col(x, idt, k_H, k_W, stride, ifm_ch, ifm_dim_H, ifm_dim_W, pad_amt, pad_val) - assert (produced == expected).all(), "Test failed for case number {}".format(case_id) + produced = execution_im2col( + x, idt, k_H, k_W, stride, ifm_ch, ifm_dim_H, ifm_dim_W, pad_amt, pad_val + ) + assert (produced == expected).all(), "Test failed for case number {}".format( + case_id + ) - ###################################### Non-square image and/or kernel tests ###################################### case_id = 3 idt = DataType.INT8 k_H = 2 @@ -310,7 +330,7 @@ def test_im2col(): [ [ [[1, -1], [2, -2], [3, -3], [4, -4], [5, -5]], - [[6, -6], [7, -7], [8, -8], [9, -9],[10, -10]], + [[6, -6], [7, -7], [8, -8], [9, -9], [10, -10]], [[11, -11], [12, -12], [13, -13], [14, -14], [15, -15]], [[16, -16], [17, -17], [18, -18], [19, -19], [20, -20]], ] @@ -325,27 +345,31 @@ def test_im2col(): [1, -1, 2, -2, 6, -6, 7, -7], [2, -2, 3, -3, 7, -7, 8, -8], [3, -3, 4, -4, 8, -8, 9, -9], - [4, -4, 5, -5, 9, -9, 10, -10] + [4, -4, 5, -5, 9, -9, 10, -10], ], [ [6, -6, 7, -7, 11, -11, 12, -12], [7, -7, 8, -8, 12, -12, 13, -13], [8, -8, 9, -9, 13, -13, 14, -14], - [9, -9, 10, -10, 14, -14, 15, -15] + [9, -9, 10, -10, 14, -14, 15, -15], ], [ [11, -11, 12, -12, 16, -16, 17, -17], [12, -12, 13, -13, 17, -17, 18, -18], [13, -13, 14, -14, 18, -18, 19, -19], - [14, -14, 15, -15, 19, -19, 20, -20] + [14, -14, 15, -15, 19, -19, 20, -20], ], ] ], dtype=np.float32, ) - produced = execution_im2col(x, idt, k_H, k_W, stride, ifm_ch, ifm_dim_H, ifm_dim_W, pad_amt, pad_val) - assert (produced == expected).all(), "Test failed for case number {}".format(case_id) + produced = execution_im2col( + x, idt, k_H, k_W, stride, ifm_ch, ifm_dim_H, ifm_dim_W, pad_amt, pad_val + ) + assert (produced == expected).all(), "Test failed for case number {}".format( + case_id + ) case_id = 4 idt = DataType.INT8 @@ -365,7 +389,7 @@ def test_im2col(): [ [ [[1, -1], [2, -2], [3, -3], [4, -4], [5, -5]], - [[6, -6], [7, -7], [8, -8], [9, -9],[10, -10]], + [[6, -6], [7, -7], [8, -8], [9, -9], [10, -10]], [[11, -11], [12, -12], [13, -13], [14, -14], [15, -15]], [[16, -16], [17, -17], [18, -18], [19, -19], [20, -20]], ] @@ -380,21 +404,25 @@ def test_im2col(): [1, -1, 2, -2, 6, -6, 7, -7, 11, -11, 12, -12], [2, -2, 3, -3, 7, -7, 8, -8, 12, -12, 13, -13], [3, -3, 4, -4, 8, -8, 9, -9, 13, -13, 14, -14], - [4, -4, 5, -5, 9, -9, 10, -10, 14, -14, 15, -15] + [4, -4, 5, -5, 9, -9, 10, -10, 14, -14, 15, -15], ], [ [6, -6, 7, -7, 11, -11, 12, -12, 16, -16, 17, -17], [7, -7, 8, -8, 12, -12, 13, -13, 17, -17, 18, -18], [8, -8, 9, -9, 13, -13, 14, -14, 18, -18, 19, -19], - [9, -9, 10, -10, 14, -14, 15, -15, 19, -19, 20, -20] - ] + [9, -9, 10, -10, 14, -14, 15, -15, 19, -19, 20, -20], + ], ] ], dtype=np.float32, ) - produced = execution_im2col(x, idt, k_H, k_W, stride, ifm_ch, ifm_dim_H, ifm_dim_W, pad_amt, pad_val) - assert (produced == expected).all(), "Test failed for case number {}".format(case_id) + produced = execution_im2col( + x, idt, k_H, k_W, stride, ifm_ch, ifm_dim_H, ifm_dim_W, pad_amt, pad_val + ) + assert (produced == expected).all(), "Test failed for case number {}".format( + case_id + ) case_id = 5 idt = DataType.INT8 @@ -414,7 +442,7 @@ def test_im2col(): [ [ [[1, -1], [2, -2], [3, -3], [4, -4], [5, -5]], - [[6, -6], [7, -7], [8, -8], [9, -9],[10, -10]], + [[6, -6], [7, -7], [8, -8], [9, -9], [10, -10]], [[11, -11], [12, -12], [13, -13], [14, -14], [15, -15]], [[16, -16], [17, -17], [18, -18], [19, -19], [20, -20]], ] @@ -431,7 +459,7 @@ def test_im2col(): [0, 0, 0, 0, 2, -2, 3, -3, 7, -7, 8, -8], [0, 0, 0, 0, 3, -3, 4, -4, 8, -8, 9, -9], [0, 0, 0, 0, 4, -4, 5, -5, 9, -9, 10, -10], - [0, 0, 0, 0, 5, -5, 0, 0, 10, -10, 0, 0] + [0, 0, 0, 0, 5, -5, 0, 0, 10, -10, 0, 0], ], [ [0, 0, 1, -1, 0, 0, 6, -6, 0, 0, 11, -11], @@ -439,7 +467,7 @@ def test_im2col(): [2, -2, 3, -3, 7, -7, 8, -8, 12, -12, 13, -13], [3, -3, 4, -4, 8, -8, 9, -9, 13, -13, 14, -14], [4, -4, 5, -5, 9, -9, 10, -10, 14, -14, 15, -15], - [5, -5, 0, 0, 10, -10, 0, 0, 15, -15, 0, 0] + [5, -5, 0, 0, 10, -10, 0, 0, 15, -15, 0, 0], ], [ [0, 0, 6, -6, 0, 0, 11, -11, 0, 0, 16, -16], @@ -447,7 +475,7 @@ def test_im2col(): [7, -7, 8, -8, 12, -12, 13, -13, 17, -17, 18, -18], [8, -8, 9, -9, 13, -13, 14, -14, 18, -18, 19, -19], [9, -9, 10, -10, 14, -14, 15, -15, 19, -19, 20, -20], - [10, -10, 0, 0, 15, -15, 0, 0, 20, -20, 0, 0] + [10, -10, 0, 0, 15, -15, 0, 0, 20, -20, 0, 0], ], [ [0, 0, 11, -11, 0, 0, 16, -16, 0, 0, 0, 0], @@ -455,16 +483,19 @@ def test_im2col(): [12, -12, 13, -13, 17, -17, 18, -18, 0, 0, 0, 0], [13, -13, 14, -14, 18, -18, 19, -19, 0, 0, 0, 0], [14, -14, 15, -15, 19, -19, 20, -20, 0, 0, 0, 0], - [15, -15, 0, 0, 20, -20, 0, 0, 0, 0, 0, 0] + [15, -15, 0, 0, 20, -20, 0, 0, 0, 0, 0, 0], ], - ] ], dtype=np.float32, ) - produced = execution_im2col(x, idt, k_H, k_W, stride, ifm_ch, ifm_dim_H, ifm_dim_W, pad_amt, pad_val) - assert (produced == expected).all(), "Test failed for case number {}".format(case_id) + produced = execution_im2col( + x, idt, k_H, k_W, stride, ifm_ch, ifm_dim_H, ifm_dim_W, pad_amt, pad_val + ) + assert (produced == expected).all(), "Test failed for case number {}".format( + case_id + ) case_id = 6 idt = DataType.INT8 @@ -481,47 +512,21 @@ def test_im2col(): ofm_dim_W = compute_conv_output_dim(ifm_dim_W, k_W, stride, pad_amt) x = np.asarray( - [ - [ - [ - [1, -1] - ], - [ - [2, -2] - ], - [ - [3, -3] - ], - [ - [4, -4] - ], - [ - [5, -5] - ] - ] - ], + [[[[1, -1]], [[2, -2]], [[3, -3]], [[4, -4]], [[5, -5]]]], dtype=np.float32, ) expected = np.asarray( - [ - [ - [ - [1, -1, 2, -2, 3, -3] - ], - [ - [2, -2, 3, -3, 4, -4] - ], - [ - [3, -3, 4, -4, 5, -5] - ] - ] - ], + [[[[1, -1, 2, -2, 3, -3]], [[2, -2, 3, -3, 4, -4]], [[3, -3, 4, -4, 5, -5]]]], dtype=np.float32, ) - produced = execution_im2col(x, idt, k_H, k_W, stride, ifm_ch, ifm_dim_H, ifm_dim_W, pad_amt, pad_val) - assert (produced == expected).all(), "Test failed for case number {}".format(case_id) + produced = execution_im2col( + x, idt, k_H, k_W, stride, ifm_ch, ifm_dim_H, ifm_dim_W, pad_amt, pad_val + ) + assert (produced == expected).all(), "Test failed for case number {}".format( + case_id + ) case_id = 7 idt = DataType.INT8 @@ -538,53 +543,29 @@ def test_im2col(): ofm_dim_W = compute_conv_output_dim(ifm_dim_W, k_W, stride, pad_amt) x = np.asarray( - [ - [ - [ - [1, -1] - ], - [ - [2, -2] - ], - [ - [3, -3] - ], - [ - [4, -4] - ], - [ - [5, -5] - ] - ] - ], + [[[[1, -1]], [[2, -2]], [[3, -3]], [[4, -4]], [[5, -5]]]], dtype=np.float32, ) expected = np.asarray( [ [ - [ - [0, 0, 1, -1, 2, -2] - ], - [ - [1, -1, 2, -2, 3, -3] - ], - [ - [2, -2, 3, -3, 4, -4] - ], - [ - [3, -3, 4, -4, 5, -5] - ], - [ - [4, -4, 5, -5, 0, 0] - ] + [[0, 0, 1, -1, 2, -2]], + [[1, -1, 2, -2, 3, -3]], + [[2, -2, 3, -3, 4, -4]], + [[3, -3, 4, -4, 5, -5]], + [[4, -4, 5, -5, 0, 0]], ] ], dtype=np.float32, ) - produced = execution_im2col(x, idt, k_H, k_W, stride, ifm_ch, ifm_dim_H, ifm_dim_W, pad_amt, pad_val) - assert (produced == expected).all(), "Test failed for case number {}".format(case_id) + produced = execution_im2col( + x, idt, k_H, k_W, stride, ifm_ch, ifm_dim_H, ifm_dim_W, pad_amt, pad_val + ) + assert (produced == expected).all(), "Test failed for case number {}".format( + case_id + ) case_id = 8 idt = DataType.INT8 @@ -601,47 +582,21 @@ def test_im2col(): ofm_dim_W = compute_conv_output_dim(ifm_dim_W, k_W, stride, pad_amt) x = np.asarray( - [ - [ - [ - [1, -1] - ], - [ - [2, -2] - ], - [ - [3, -3] - ], - [ - [4, -4] - ], - [ - [5, -5] - ] - ] - ], + [[[[1, -1]], [[2, -2]], [[3, -3]], [[4, -4]], [[5, -5]]]], dtype=np.float32, ) expected = np.asarray( - [ - [ - [ - [0, 0, 1, -1, 2, -2] - ], - [ - [2, -2, 3, -3, 4, -4] - ], - [ - [4, -4, 5, -5, 0, 0] - ] - ] - ], + [[[[0, 0, 1, -1, 2, -2]], [[2, -2, 3, -3, 4, -4]], [[4, -4, 5, -5, 0, 0]]]], dtype=np.float32, ) - produced = execution_im2col(x, idt, k_H, k_W, stride, ifm_ch, ifm_dim_H, ifm_dim_W, pad_amt, pad_val) - assert (produced == expected).all(), "Test failed for case number {}".format(case_id) + produced = execution_im2col( + x, idt, k_H, k_W, stride, ifm_ch, ifm_dim_H, ifm_dim_W, pad_amt, pad_val + ) + assert (produced == expected).all(), "Test failed for case number {}".format( + case_id + ) def test_im2col_infer_shapes(): @@ -652,7 +607,7 @@ def test_im2col_infer_shapes(): ifm_ch = 1 ifm_dim_H = 4 ifm_dim_W = 4 - pad_amt = 0 # default + pad_amt = 0 # default ofm_dim_H = compute_conv_output_dim(ifm_dim_H, k_H, stride, pad_amt) ofm_dim_W = compute_conv_output_dim(ifm_dim_W, k_W, stride, pad_amt) @@ -689,7 +644,9 @@ def test_im2col_infer_shapes(): "abs", TensorProto.FLOAT, [1, ifm_dim_H, ifm_dim_W, ifm_ch] ), helper.make_tensor_value_info( - "im2col", TensorProto.FLOAT, [1, ofm_dim_H, ofm_dim_W, k_H * k_W * ifm_ch] + "im2col", + TensorProto.FLOAT, + [1, ofm_dim_H, ofm_dim_W, k_H * k_W * ifm_ch], ), ], ) @@ -701,4 +658,9 @@ def test_im2col_infer_shapes(): # test shape inference model.transform(InferShapes()) - assert model.get_tensor_shape("im2col") == [1, ofm_dim_H, ofm_dim_W, k_H * k_W * ifm_ch] + assert model.get_tensor_shape("im2col") == [ + 1, + ofm_dim_H, + ofm_dim_W, + k_H * k_W * ifm_ch, + ]