(builder, node, graph, err)
convert to CoreML BatchNorm Layer:
    https://github.com/apple/coremltools/blob/655b3be5cc0d42c3c4fa49f0f0e4a93a26b3e492/mlmodel/format/NeuralNetwork.proto#L1633
convert to CoreML BatchNorm Layer:
    https://github.com/apple/coremltools/blob/655b3be5cc0d42c3c4fa49f0f0e4a93a26b3e492/mlmodel/format/NeuralNetwork.proto#L1633
convert to CoreML BatchNorm Layer : https : // github . com / apple / coremltools / blob / 655b3be5cc0d42c3c4fa49f0f0e4a93a26b3e492 / mlmodel / format / NeuralNetwork . proto#L1633
def _convert_bn(builder, node, graph, err):
    '''
    convert to CoreML BatchNorm Layer:
    https://github.com/apple/coremltools/blob/655b3be5cc0d42c3c4fa49f0f0e4a93a26b3e492/mlmodel/format/NeuralNetwork.proto#L1633
    '''
    if len(node.outputs) > 1:
        return err.unsupported_op_configuration(builder, node, graph, "This converter only supports BatchNormalization with one output")

    epsilon = node.attrs.get("epsilon", 1e-5)
    scale_name = node.inputs[1]

    if scale_name in node.input_tensors:
        channels = node.input_tensors[scale_name].shape
    elif scale_name in graph.shape_dict:
        channels = graph.shape_dict[scale_name]
    else:
        err.unsupported_op_configuration(builder, node, graph, "Input shape not available")

    # TODO: Move error check under VERBOSE / DEBUG Mode
    for i in range(2, len(node.inputs)):
        ip_name = node.inputs[i]
        if ip_name in node.input_tensors:
            tensor_shape = node.input_tensors[ip_name].shape
        else:
            if ip_name not in graph.shape_dict:
                return err.unsupported_op_configuration(builder, node, graph, "Input shape not available")
            tensor_shape = graph.shape_dict[ip_name]
        if tensor_shape != channels:
            err.unsupported_op_configuration(builder, node, graph, "Shape mismatch between Scale, Bias, Mean and Variance")

    scale = node.input_tensors[node.inputs[1]] if node.inputs[1] in node.input_tensors else \
            np.ones(shape=channels, dtype=np.float32)
    bias = node.input_tensors[node.inputs[2]] if node.inputs[2] in node.input_tensors else \
            np.zeros(shape=channels, dtype=np.float32)
    mean = node.input_tensors[node.inputs[3]] if node.inputs[3] in node.input_tensors else \
            np.zeros(shape=channels, dtype=np.float32)
    var = node.input_tensors[node.inputs[4]] if node.inputs[4] in node.input_tensors else \
            np.ones(shape=channels, dtype=np.float32)

    rank = builder._get_rank(node.inputs[0])
    # ONNX converts B x C tensor into B x C x 1 hence
    # Rank 2 BN is mapped to Rank 3 BN
    if rank == 3:
        # 1D Batch Norm
        add_bn_with_expansion(builder, node, err, node.name, node.inputs[0], node.outputs[0], channels[0],
                              scale, bias, mean, var, epsilon, axes_for_expansion=[0, 3])
    elif rank == 4:
        # 2D Batch Norm
        add_bn_with_expansion(builder, node, err, node.name, node.inputs[0], node.outputs[0], channels[0],
                              scale, bias, mean, var, epsilon, axes_for_expansion=[])
    else:
        # Unsupported 1D, 3D and above
        err.unsupported_op_configuration(builder, node, graph, "provided number axes {} not supported".format(rank))
def _convert_bn ( builder , node , graph , err ) : if len ( node . outputs ) > 1 : return err . unsupported_op_configuration ( builder , node , graph , "This converter only supports BatchNormalization with one output" ) epsilon = node . attrs . get ( "epsilon" , 1e-5 ) scale_name = node . inputs [ 1 ] if scale_name in node . input_tensors : channels = node . input_tensors [ scale_name ] . shape elif scale_name in graph . shape_dict : channels = graph . shape_dict [ scale_name ] else : err . unsupported_op_configuration ( builder , node , graph , "Input shape not available" ) # TODO: Move error check under VERBOSE / DEBUG Mode for i in range ( 2 , len ( node . inputs ) ) : ip_name = node . inputs [ i ] if ip_name in node . input_tensors : tensor_shape = node . input_tensors [ ip_name ] . shape else : if ip_name not in graph . shape_dict : return err . unsupported_op_configuration ( builder , node , graph , "Input shape not available" ) tensor_shape = graph . shape_dict [ ip_name ] if tensor_shape != channels : err . unsupported_op_configuration ( builder , node , graph , "Shape mismatch between Scale, Bias, Mean and Variance" ) scale = node . input_tensors [ node . inputs [ 1 ] ] if node . inputs [ 1 ] in node . input_tensors else np . ones ( shape = channels , dtype = np . float32 ) bias = node . input_tensors [ node . inputs [ 2 ] ] if node . inputs [ 2 ] in node . input_tensors else np . zeros ( shape = channels , dtype = np . float32 ) mean = node . input_tensors [ node . inputs [ 3 ] ] if node . inputs [ 3 ] in node . input_tensors else np . zeros ( shape = channels , dtype = np . float32 ) var = node . input_tensors [ node . inputs [ 4 ] ] if node . inputs [ 4 ] in node . input_tensors else np . ones ( shape = channels , dtype = np . float32 ) rank = builder . _get_rank ( node . inputs [ 0 ] ) # ONNX converts B x C tensor into B x C x 1 hence # Rank 2 BN is mapped to Rank 3 BN if rank == 3 : # 1D Batch Norm add_bn_with_expansion ( builder , node , err , node . name , node . inputs [ 0 ] , node . outputs [ 0 ] , channels [ 0 ] , scale , bias , mean , var , epsilon , axes_for_expansion = [ 0 , 3 ] ) elif rank == 4 : # 2D Batch Norm add_bn_with_expansion ( builder , node , err , node . name , node . inputs [ 0 ] , node . outputs [ 0 ] , channels [ 0 ] , scale , bias , mean , var , epsilon , axes_for_expansion = [ ] ) else : # Unsupported 1D, 3D and above err . unsupported_op_configuration ( builder , node , graph , "provided number axes {} not supported" . format ( rank ) )