def test_2layer_deconv(deconv_n4_hw4_c1_5x5): cf1 = ConvParams(**deconv_n4_hw4_c1_5x5) # 2nd layer filter fshape2 = (4, 3, 3) str_h = str_w = 2 K, R, S = fshape2 C, D, H, W, N = cf1.dimO # 2nd layer input dimensions cf2 = ConvParams(C=C, D=D, H=H, W=W, N=N, K=K, R=R, S=S, str_h=str_h, str_w=str_w, deconv=True) # randomly initialize input_value = rng.uniform(-0.5, 0.5, cf1.ax_i) filter1_value = rng.uniform(-0.5, 0.5, cf1.ax_f) filter2_value = rng.uniform(-0.5, 0.5, cf2.ax_f) error_value = rng.uniform(-0.5, 0.5, cf2.ax_o) inputs = ng.placeholder(cf1.ax_i) filters1 = ng.placeholder(cf1.ax_f) filters2 = ng.placeholder(cf2.ax_f) errors = ng.placeholder(cf2.ax_o) out1 = ng.deconvolution(cf1.conv_params, inputs, filters1, axes=cf1.ax_o) out2 = ng.deconvolution(cf2.conv_params, out1, filters2, axes=cf2.ax_o) bprop_out = ng.deriv(out2, inputs, errors) updat_out2 = ng.deriv(out2, filters2, errors) updat_out1 = ng.deriv(out2, filters1, errors) with executor([out1, out2, bprop_out, updat_out1, updat_out2], inputs, filters1, filters2, errors) as conv_executor: out1_ng, out2_ng, gradI_ng, gradF1_ng, gradF2_ng = \ conv_executor(input_value, filter1_value, filter2_value, error_value) # Compute reference with NumPy # fprop out1_np = reference_deconv_fprop(cf1.conv_params, input_value, filter1_value) out2_np = reference_deconv_fprop(cf2.conv_params, out1_np, filter2_value) # bprop gradI2_np, gradF2_np = reference_deconv_bprop(cf2.conv_params, error_value, out1_np, filter2_value) gradI1_np, gradF1_np = reference_deconv_bprop(cf1.conv_params, gradI2_np, input_value, filter1_value) # Compare fprop assert np.allclose(out1_ng, out1_np, rtol=0.01, atol=0) assert np.allclose(out2_ng, out2_np, rtol=0.01, atol=0) # Compare bprop assert np.allclose(gradI_ng, gradI1_np, rtol=0.01, atol=0) # Compare update assert np.allclose(gradF1_ng, gradF1_np, rtol=0.01, atol=0) assert np.allclose(gradF2_ng, gradF2_np, rtol=0.01, atol=0)
def test_deconv(transformer_factory, deconv_n4_hw4_c1_5x5): cf = ConvParams(**deconv_n4_hw4_c1_5x5) # randomly initialize input_value = rng.uniform(-0.5, 0.5, cf.ax_i) filter_value = rng.uniform(-0.5, 0.5, cf.ax_f) error_value = rng.uniform(-0.5, 0.5, cf.ax_o) inputs = ng.placeholder(cf.ax_i) filters = ng.placeholder(cf.ax_f) errors = ng.placeholder(cf.ax_o) output = ng.deconvolution(cf.conv_params, inputs, filters, axes=cf.ax_o) bprop_out = ng.deriv(output, inputs, errors) updat_out = ng.deriv(output, filters, errors) with executor([output, bprop_out, updat_out], inputs, filters, errors) as conv_executor: result_ng, gradI_ng, gradF_ng = conv_executor(input_value, filter_value, error_value) # Compute reference with NumPy result_np = reference_deconv_fprop(cf.conv_params, input_value, filter_value) gradI_np, gradF_np = reference_deconv_bprop(cf.conv_params, error_value, input_value, filter_value) # Compare fprop assert np.allclose(result_ng, result_np, rtol=0.1, atol=0) # Compare bprop assert np.allclose(gradI_ng, gradI_np, rtol=0.1, atol=0) # Compare update assert np.allclose(gradF_ng, gradF_np, rtol=0.1, atol=0)
def make_convolution_op(onnx_node, ng_inputs, transpose=False): # type: (NodeWrapper, List[TensorOp], bool) -> Op """ Create an ngraph convolution or deconvolution Op based on an ONNX node. :param onnx_node: wrapped ONNX node for Conv of ConvTranspose op :param ng_inputs: ngraph TensorOp input tensors :param transpose: should this be a transposed convolution? :return: ngraph Op for convolution or deconvolution """ if len(ng_inputs) == 3: x, weights, bias = ng_inputs elif len(ng_inputs) == 2: x, weights = ng_inputs bias = ng.constant(0) else: raise ValueError( 'Conv node (%s): unexpected number of input values: %d.', onnx_node.name, len(ng_inputs)) # Reorder x axes from ONNX convention (N, C, H, W, D) to ngraph (C, D, H, W, N) # Reorder weights axes from ONNX (K, J, R, S, T) to ngraph (J, T, R, S, K) # Axis names follow https://ngraph.nervanasys.com/index.html/axes.html if len(x.axes) == 4: # 2D convolution x = reorder_axes(x, 'NCHW', 'CDHWN') weights = reorder_axes(weights, 'KJRS', 'JTRSK') elif len(x.axes) == 5: # 3D convolution x = reorder_axes(x, 'NCHWD', 'CDHWN') weights = reorder_axes(weights, 'KJRST', 'JTRSK') else: raise NotImplementedError( 'Conv node (%s): only 2D and 3D convolutions are supported.', onnx_node.name) groups = onnx_node.get_attribute_value('group', 1) if groups != 1: raise NotImplementedError( 'Conv node (%s): `group` attribute value %d not supported.', onnx_node.name, groups) # Prepare ngraph convolution operation conv_params = get_conv_params(onnx_node) output_axes = make_conv_output_axes(x, weights, conv_params) if transpose: conv = ng.deconvolution(conv_params, x, weights, axes=output_axes) else: conv = ng.convolution(conv_params, x, weights, axes=output_axes) conv = cast_to_pos_axes(conv) + bias # ONNX output should have axes in the order N, C, H, W, D conv = reorder_axes(conv, 'CDHWN', 'NCHWD') if len(ng_inputs[0].axes ) == 4: # 2D convolution, slice away the D axis from output conv = ng.tensor_slice(conv, [ slice(None), slice(None), slice(None), slice(None), 0 ]) return conv