def test_model_with_simple_rnn_multiple_layers(self):
        """ Test connected graph construction on a model with multiple simple RNN layers """
        tf.compat.v1.reset_default_graph()
        sess = tf.compat.v1.Session()
        with sess.graph.as_default():
            inputs = tf.keras.Input(shape=(3, 100))

            # Add an RNN layer with 12 internal units.
            x = tf.keras.layers.SimpleRNN(12, name='rnn0', activation='tanh', return_sequences=True)(inputs)
            x = tf.keras.layers.SimpleRNN(12, name='rnn1', activation='relu', return_sequences=True)(x)
            x = tf.keras.layers.SimpleRNN(12, name='rnn2', activation='tanh')(x)
            _ = tf.keras.layers.Dense(12, activation=tf.nn.softmax, name="matmul0")(x)

            init = tf.compat.v1.global_variables_initializer()
            sess.run(init)
            # _ = tf.compat.v1.summary.FileWriter('./simple_rnn', sess.graph)

        # construct a connected graph
        conn_graph = ConnectedGraph(sess.graph, ['input_1'], ['matmul0/Softmax'])

        # there should be only 4 connected graph ops, input, simpleRNN , Dense and Softmax
        self.assertEqual(6, len(conn_graph.get_all_ops()))
        num_detected_rnns = 0
        for op in conn_graph.get_all_ops().values():
            if op.type == 'SimpleRNN':
                num_detected_rnns += 1
                inner_list = op.internal_ops
                self.assertEqual(49, len(inner_list))
        self.assertEqual(3, num_detected_rnns)
示例#2
0
    def test_prune_model_tf_slim(self):
        """ Punning a model with tf slim api """

        # create tf.compat.v1.Session and initialize the weights and biases with zeros
        config = tf.compat.v1.ConfigProto()
        config.gpu_options.allow_growth = True

        # create session with graph
        sess = tf.compat.v1.Session(graph=tf.Graph(), config=config)

        with sess.graph.as_default():
            # by default, model will be constructed in default graph
            x = tf.compat.v1.placeholder(tf.float32, [1, 32, 32, 3])
            _ = tf_slim_basic_model(x)
            sess.run(tf.compat.v1.global_variables_initializer())

        conn_graph_orig = ConnectedGraph(sess.graph, ['Placeholder'],
                                         ['tf_slim_model/Softmax'])
        num_ops_orig = len(conn_graph_orig.get_all_ops())

        # Create a layer database
        orig_layer_db = LayerDatabase(model=sess,
                                      input_shape=(1, 32, 32, 3),
                                      working_dir=None)
        conv1 = orig_layer_db.find_layer_by_name('Conv_1/Conv2D')
        conv1_bias = BiasUtils.get_bias_as_numpy_data(orig_layer_db.model,
                                                      conv1.module)

        layer_comp_ratio_list = [LayerCompRatioPair(conv1, Decimal(0.5))]

        spatial_svd_pruner = SpatialSvdPruner()
        comp_layer_db = spatial_svd_pruner.prune_model(orig_layer_db,
                                                       layer_comp_ratio_list,
                                                       CostMetric.mac,
                                                       trainer=None)
        # Check that svd added these ops
        _ = comp_layer_db.model.graph.get_operation_by_name('Conv_1_a/Conv2D')
        _ = comp_layer_db.model.graph.get_operation_by_name('Conv_1_b/Conv2D')

        conn_graph_new = ConnectedGraph(comp_layer_db.model.graph,
                                        ['Placeholder'],
                                        ['tf_slim_model/Softmax'])
        num_ops_new = len(conn_graph_new.get_all_ops())
        self.assertEqual(num_ops_orig + 1, num_ops_new)
        bias_add_op = comp_layer_db.model.graph.get_operation_by_name(
            'Conv_1_b/BiasAdd')
        conv_1_b_op = comp_layer_db.model.graph.get_operation_by_name(
            'Conv_1_b/Conv2D')
        self.assertEqual(
            conn_graph_new._module_identifier.get_op_info(bias_add_op),
            conn_graph_new._module_identifier.get_op_info(conv_1_b_op))
        self.assertTrue(
            np.array_equal(
                conv1_bias,
                BiasUtils.get_bias_as_numpy_data(comp_layer_db.model,
                                                 conv_1_b_op)))
    def test_dropout_slim_get_op_product_graph(self):
        """ Test connected graph construction on a slim graph with dropout op """

        tf.compat.v1.reset_default_graph()
        _ = dropout_slim_model()
        conn_graph = ConnectedGraph(tf.compat.v1.get_default_graph(), ['input_1'], ['dropout_slim_model/Softmax'])
        self.assertTrue(validate_branch_ops(conn_graph))
        self.assertTrue(validate_product_tensor_lists(conn_graph))
        self.assertEqual(0, conn_graph.branch_count)
        self.assertEqual(10, len(conn_graph.get_all_ops()))
        self.assertEqual(9 + len(tf.compat.v1.get_default_graph().get_collection('variables')),
                         len(conn_graph.get_all_products()))
        self.assertTrue(conn_graph.get_all_ops()['Dropout'], 'Dropout_training_True')
    def test_multiple_input_model_get_op_product_graph(self):
        """ Test connected graph construction on a multiple input graph """

        tf.compat.v1.reset_default_graph()
        _ = multiple_input_model()
        conn_graph = ConnectedGraph(tf.compat.v1.get_default_graph(), ['input1', 'input2'], ['multiple_input_model/Softmax'])
        self.assertTrue(validate_branch_ops(conn_graph))
        self.assertTrue(validate_product_tensor_lists(conn_graph))
        self.assertEqual(0, conn_graph.branch_count)
        self.assertEqual(9, len(conn_graph.get_all_ops()))
        self.assertEqual(8 + len(tf.compat.v1.get_default_graph().get_collection('variables')),
                         len(conn_graph.get_all_products()))
        self.assertTrue(conn_graph.get_all_ops()['input1'].output.is_model_input)
        self.assertTrue(conn_graph.get_all_ops()['input2'].output.is_model_input)
    def test_model_with_lstm_layer_sigmoid(self):
        """ Test connected graph construction on a model with LSTM op with sigmoid activation """

        tf.compat.v1.reset_default_graph()
        sess = tf.compat.v1.Session()
        with sess.graph.as_default():
            inputs = tf.keras.Input(shape=(3, 100))

            # Add an RNN layer with 12 internal units.
            x = tf.keras.layers.LSTM(12, recurrent_activation='sigmoid', name='lstm0')(inputs)
            _ = tf.keras.layers.Dense(12, activation=tf.nn.softmax,
                                      name="matmul0")(x)

            init = tf.compat.v1.global_variables_initializer()
            sess.run(init)
            # _ = tf.compat.v1.summary.FileWriter('./lstm_sigmoid', sess.graph)

        # construct a connected graph
        conn_graph = ConnectedGraph(sess.graph, ['input_1'], ['matmul0/Softmax'])

        # there should be only 4 connected graph ops, input, LSTM , Dense and Softmax
        self.assertEqual(4, len(conn_graph.get_all_ops()))
        lstm_detected = False
        for op in conn_graph.get_all_ops().values():
            if op.type == 'LSTM':
                lstm_detected = True
                inner_list = op.internal_ops
                self.assertEqual(77, len(inner_list))
                self.assertEqual(op.get_module(), sess.graph.get_operation_by_name('lstm0/while/MatMul'))
                self.assertEqual('lstm0', op.name)
        self.assertTrue(lstm_detected)

        valid_matmuls = []
        valid_muls = []
        valid_bias_add = []
        valid_activation = []
        for op in inner_list:
            if op.type == 'MatMul' and op not in valid_matmuls:
                valid_matmuls.append(op)
            if op.type == 'Mul' and op not in valid_matmuls:
                valid_muls.append(op)
            if op.type == 'BiasAdd' and op not in valid_bias_add:
                valid_bias_add.append(op)
            if op.type == 'Sigmoid' and op not in valid_activation:
                valid_activation.append(op)

        self.assertEqual(8, len(valid_matmuls))
        self.assertEqual(4, len(valid_muls))
        self.assertEqual(4, len(valid_bias_add))
        self.assertEqual(3, len(valid_activation))
    def test_upsample_get_op_product_graph(self):
        """ Test connected graph construction on a graph with upsample op
        Need to perform one round of winnowing first to insert the upsample op """
        tf.compat.v1.reset_default_graph()
        sess = tf.compat.v1.Session()
        module_zero_channels_list = []

        _ = upsample_model()
        init = tf.compat.v1.global_variables_initializer()
        sess.run(init)

        input_op_names = ['input_1']
        output_op_names = ['upsample_model/Softmax']

        tf_op = tf.compat.v1.get_default_graph().get_operation_by_name("conv2d_3/Conv2D")
        input_channels_to_winnow = [3, 5, 7]
        module_mask_pair = (tf_op, input_channels_to_winnow)
        module_zero_channels_list.append(module_mask_pair)

        new_sess, _ = winnow.winnow_tf_model(sess, input_op_names, output_op_names,
                                             list_of_modules_to_winnow=module_zero_channels_list,
                                             reshape=True, in_place=True, verbose=True)

        conn_graph = ConnectedGraph(tf.compat.v1.get_default_graph(), input_op_names, output_op_names)
        self.assertEqual(18, len(conn_graph.get_all_ops()))
        reduced_bn_1_op = conn_graph.get_op_from_module_name('reduced_batch_normalization_1/cond/FusedBatchNormV3_1')
        self.assertTrue(reduced_bn_1_op.output.consumers[0].type == 'Upsample')
        new_sess.close()
    def test_simple_rnn_keras_single_timestep_with_placeholder_input(self):
        """ simple RNN layer with placeholder input type """

        tf.compat.v1.reset_default_graph()
        sess = tf.compat.v1.Session()

        with sess.graph.as_default():
            X_batch = np.random.random([32, 1, 8]).astype(np.float32)
            X = tf.compat.v1.placeholder(tf.float32, [32, 1, 8], name='input')
            simple_rnn = tf.keras.layers.SimpleRNN(1)
            output = simple_rnn(X)
            _ = tf.keras.layers.Dense(12, activation=tf.nn.softmax,
                                      name="matmul0")(output)

            init = tf.compat.v1.global_variables_initializer()
            sess.run(init)
            # _ = tf.compat.v1.summary.FileWriter('./test_simple_rnn_keras_single_timestep_with_placeholder_input', sess.graph)
            sess.run(output, feed_dict={X: X_batch})

        # construct a connected graph
        conn_graph = ConnectedGraph(sess.graph, ['input'], ['matmul0/Softmax'])
        # there should be only 4 connected graph ops, input, simpleRNN , Dense and Softmax
        self.assertEqual(4, len(conn_graph.get_all_ops()))
        simple_rnn_detected = False
        for op in conn_graph.get_all_ops().values():
            if op.type == 'SimpleRNN':
                simple_rnn_detected = True
                inner_list = op.internal_ops
                self.assertEqual(25, len(inner_list))
                self.assertEqual(op.get_module(), sess.graph.get_operation_by_name('simple_rnn/while/MatMul'))
                self.assertEqual('simple_rnn', op.name)
        self.assertTrue(simple_rnn_detected)

        valid_matmuls = []
        valid_bias_add = []
        valid_activation = []
        for op in inner_list:
            if op.type == 'MatMul' and op not in valid_matmuls:
                valid_matmuls.append(op)
            if op.type == 'BiasAdd' and op not in valid_bias_add:
                valid_bias_add.append(op)
            if op.type == 'Tanh' and op not in valid_activation:
                valid_activation.append(op)

        self.assertEqual(2, len(valid_matmuls))
        self.assertEqual(1, len(valid_bias_add))
        self.assertEqual(1, len(valid_activation))
    def test_model_with_simple_rnn_layer(self):
        """ Test connected graph construction on a model with simple RNN op """
        tf.compat.v1.reset_default_graph()
        sess = tf.compat.v1.Session()
        with sess.graph.as_default():
            inputs = tf.keras.Input(shape=(3, 100))

            # Add an RNN layer with 12 internal units.
            x = tf.keras.layers.SimpleRNN(12, name='rnn0')(inputs)
            _ = tf.keras.layers.Dense(12, activation=tf.nn.softmax,
                                      name="matmul0")(x)

            init = tf.compat.v1.global_variables_initializer()
            sess.run(init)
            # _ = tf.compat.v1.summary.FileWriter('./simple_rnn', sess.graph)

        # construct a connected graph
        conn_graph = ConnectedGraph(sess.graph, ['input_1'], ['matmul0/Softmax'])

        # there should be only 4 connected graph ops, input, simpleRNN , Dense and Softmax
        self.assertEqual(4, len(conn_graph.get_all_ops()))
        simple_rnn_detected = False
        for op in conn_graph.get_all_ops().values():
            if op.type == 'SimpleRNN':
                simple_rnn_detected = True
                inner_list = op.internal_ops
                self.assertEqual(49, len(inner_list))
                self.assertEqual(op.get_module(), sess.graph.get_operation_by_name('rnn0/while/MatMul'))
                self.assertEqual('rnn0', op.name)
        self.assertTrue(simple_rnn_detected)

        # check for 2 MatMuls, 1 BiasAdd and an activation function in the inner op list
        valid_matmuls = []
        valid_bias_add = []
        valid_activation = []
        for op in inner_list:
            if op.type == 'MatMul' and op not in valid_matmuls:
                valid_matmuls.append(op)
            if op.type == 'BiasAdd' and op not in valid_bias_add:
                valid_bias_add.append(op)
            if op.type == 'Tanh' and op not in valid_activation:
                valid_activation.append(op)

        self.assertEqual(2, len(valid_matmuls))
        self.assertEqual(1, len(valid_bias_add))
        self.assertEqual(1, len(valid_activation))
    def test_bn_fold_with_no_bias(self):
        tf.compat.v1.reset_default_graph()
        inputs = tf.keras.Input(shape=(
            32,
            32,
            3,
        ))
        conv_op = tf.keras.layers.Conv2D(32, (3, 3), use_bias=False)(inputs)
        bn_op = tf.keras.layers.BatchNormalization(fused=True)(conv_op,
                                                               training=False)
        _ = tf.nn.relu(bn_op)

        init = tf.compat.v1.global_variables_initializer()
        sess = tf.compat.v1.Session()
        sess.run(init)

        conv_op = sess.graph.get_operation_by_name('conv2d/Conv2D')
        np.random.seed(0)
        w_shape = conv_op.inputs[0].shape
        numpy_data = np.random.rand(1, w_shape[1], w_shape[2], w_shape[3])

        relu_op = sess.graph.get_operation_by_name('Relu')
        baseline_output = sess.run(relu_op.outputs[0],
                                   feed_dict={conv_op.inputs[0]: numpy_data})
        old_conn_graph = ConnectedGraph(sess.graph,
                                        starting_op_names=['input_1'],
                                        output_op_names=['Relu'])

        new_sess, pairs = fold_all_batch_norms(sess, "input_1", 'Relu')

        new_conv_op = new_sess.graph.get_operation_by_name('conv2d/Conv2D')
        w2 = new_conv_op.inputs[0]
        feed_dict = {w2: numpy_data}

        new_relu_op = new_sess.graph.get_operation_by_name('Relu')
        output_after_fold = new_sess.run(new_relu_op.outputs[0],
                                         feed_dict=feed_dict)
        new_conn_graph = ConnectedGraph(new_sess.graph,
                                        starting_op_names=['input_1'],
                                        output_op_names=['Relu'])

        self.assertTrue(
            np.allclose(baseline_output, output_after_fold, atol=1.e-4))
        # New connected graph should have one less op since bn was removed
        self.assertTrue(len(old_conn_graph.get_all_ops()),
                        len(new_conn_graph.get_all_ops()) - 1)
    def test_vgg16_slim_get_op_product_graph(self):
        """
        Test connected graph construction on vgg16 from tf slim
        This model includes dropout pattern 3 which does not appear in other models.
        """

        tf.compat.v1.reset_default_graph()
        inp = tf.compat.v1.placeholder(tf.float32, [1, 224, 224, 3])
        _ = vgg.vgg_16(inp)
        conn_graph = ConnectedGraph(tf.compat.v1.get_default_graph(), ['Placeholder'], ['vgg_16/fc8/squeezed'])
        self.assertTrue(validate_branch_ops(conn_graph))
        self.assertTrue(validate_product_tensor_lists(conn_graph))
        self.assertEqual(0, conn_graph.branch_count)
        self.assertEqual(40, len(conn_graph.get_all_ops()))
        self.assertEqual(39 + len(tf.compat.v1.get_default_graph().get_collection('variables')),
                         len(conn_graph.get_all_products()))
        self.assertTrue(conn_graph.get_all_ops()['vgg_16/dropout6'], 'Dropout_training_True_unknown_shape')
        self.assertTrue(conn_graph.get_all_ops()['vgg_16/dropout7'], 'Dropout_training_True_unknown_shape')
    def test_keras_model_functional_with_non_fused_batchnorms_get_op_product_graph(self):
        """ Test connected graph construction on keras model functional with non fused batchnorms """
        tf.compat.v1.reset_default_graph()

        _ = keras_model_functional_with_non_fused_batchnorms()
        conn_graph = ConnectedGraph(tf.compat.v1.get_default_graph(), ['input_1'],
                                    ['keras_model_functional_with_non_fused_batchnorms/Softmax'])
        self.assertTrue(validate_branch_ops(conn_graph))
        self.assertTrue(validate_product_tensor_lists(conn_graph))
        _ = conn_graph.get_all_ops()['batch_normalization']
        _ = conn_graph.get_all_ops()['scope_1/batch_normalization_1']
        _ = conn_graph.get_all_ops()['scope_1/batch_normalization_2']
        self.assertEqual(0, conn_graph.branch_count)
        self.assertEqual(14, len(conn_graph.get_all_ops()))

        # 13 products from inter module connections
        # 22 products from parameters
        self.assertEqual(35, len(conn_graph.get_all_products()))
    def test_model_with_PReLU(self):
        """
        PreLU
        """

        tf.compat.v1.reset_default_graph()
        inputs = tf.keras.Input(shape=(1, 10, 10), name="inputs")

        x = tf.keras.layers.PReLU()(inputs)
        x = tf.keras.layers.ReLU()(x)

        init = tf.compat.v1.global_variables_initializer()
        sess = tf.compat.v1.Session()
        sess.run(init)

        conn_graph = ConnectedGraph(tf.compat.v1.get_default_graph(), starting_op_names=['inputs'],
                                    output_op_names=['re_lu/Relu'])

        self.assertEqual(3, len(conn_graph.get_all_ops()))
        self.assertEqual(5, len(conn_graph.get_all_ops()['p_re_lu/Relu'].internal_ops))
    def test_concat_get_op_product_graph(self):
        """ Test connected graph construction on a graph with concat op """

        tf.compat.v1.reset_default_graph()

        _ = concat_model()
        conn_graph = ConnectedGraph(tf.compat.v1.get_default_graph(), ['input_1'], ['concat_model/Softmax'])
        self.assertTrue(validate_branch_ops(conn_graph))
        self.assertTrue(validate_product_tensor_lists(conn_graph))
        self.assertEqual(2, conn_graph.branch_count)
        self.assertEqual(13, len(conn_graph.get_all_ops()))
        self.assertEqual(12 + len(tf.compat.v1.get_default_graph().get_collection('variables')),
                         len(conn_graph.get_all_products()))

        # Check that the order of input products to the concat op matches the order of input tensors in the tf graph
        concat_tf_op = tf.compat.v1.get_default_graph().get_operation_by_name("concatenate/concat")
        concat_op = conn_graph.get_all_ops()['concatenate/concat']
        for index, product in enumerate(concat_op.get_input_products()):
            self.assertTrue(len(product.consumers) == 1)
            self.assertEqual(product.tensor_dict[product.consumers[0]], concat_tf_op.inputs[index])
    def test_model_with_global_max_pool2d(self):
        """ Test connected graph construction on model with leaky relu op """
        tf.compat.v1.reset_default_graph()
        _ = model_with_global_max_pool2d()
        conn_graph = ConnectedGraph(tf.compat.v1.get_default_graph(), starting_op_names=['input_1'],
                                    output_op_names=['model_with_global_max_pool2d/Softmax'])
        self.assertTrue(validate_branch_ops(conn_graph))
        self.assertTrue(validate_product_tensor_lists(conn_graph))
        self.assertEqual(0, conn_graph.branch_count)
        self.assertEqual(6, len(conn_graph.get_all_ops()))

        # 5 products from inter module connections
        # 4 products from parameters
        self.assertEqual(9, len(conn_graph.get_all_products()))
        found_global_max_pool2d = False
        for op in conn_graph.get_all_ops().values():
            if op.type == 'GlobalMaxPool2D':
                found_global_max_pool2d = True
        self.assertTrue(found_global_max_pool2d)

        tf.compat.v1.reset_default_graph()
    def test_single_residual_get_op_product_graph(self):
        """ Test connected graph construction on single residual model """

        tf.compat.v1.reset_default_graph()
        _ = single_residual()
        conn_graph = ConnectedGraph(tf.compat.v1.get_default_graph(), ['input_1'], ['single_residual/Softmax'])
        self.assertTrue(validate_branch_ops(conn_graph))
        self.assertTrue(validate_product_tensor_lists(conn_graph))
        self.assertEqual(1, conn_graph.branch_count)
        self.assertEqual(18, len(conn_graph.get_all_ops()))
        # 17 products from interop connections, 20 from parameters
        self.assertEqual(37, len(conn_graph.get_all_products()))
    def test_model_with_leaky_relu(self):
        """ Test connected graph construction on model with leaky relu op """
        tf.compat.v1.reset_default_graph()
        _ = model_with_leaky_relu()
        conn_graph = ConnectedGraph(tf.compat.v1.get_default_graph(), starting_op_names=['input_1'],
                                    output_op_names=['model_with_leaky_relu/Softmax'])
        self.assertTrue(validate_branch_ops(conn_graph))
        self.assertTrue(validate_product_tensor_lists(conn_graph))
        self.assertEqual(0, conn_graph.branch_count)
        self.assertEqual(7, len(conn_graph.get_all_ops()))

        # 6 products from inter module connections
        # 6 products from parameters
        self.assertEqual(12, len(conn_graph.get_all_products()))
        found_leaky_relu = False
        for op in conn_graph.get_all_ops().values():
            if op.type == 'LeakyRelu':
                found_leaky_relu = True
        self.assertTrue(found_leaky_relu)

        tf.compat.v1.reset_default_graph()
    def test_model_with_lstm_layer_deepspeech_time_major_true_sigmoid(self):
        """ Test connected graph construction on a model with stacked LSTM op in DeepSpeech model"""

        tf.compat.v1.reset_default_graph()
        sess = tf.compat.v1.Session()
        with sess.graph.as_default():
            inputs = tf.keras.Input(shape=(3, 100))

            # Defaults
            # return_state=False, unit_forget_bias=True
            # return_sequences=False, time_major=False
            x = tf.keras.layers.LSTM(12,
                                     recurrent_activation='sigmoid',
                                     unit_forget_bias=False,
                                     time_major=True,
                                     return_sequences=True,
                                     name='lstm_stacked')(inputs)

            x2 = tf.keras.layers.LSTM(12, name='last_lstm')(x)

            _ = tf.keras.layers.Dense(12, activation=tf.nn.softmax,
                                      name="matmul0")(x2)

            init = tf.compat.v1.global_variables_initializer()
            sess.run(init)
            # _ = tf.compat.v1.summary.FileWriter('./lstm_deepspeech', sess.graph)

        # construct a connected graph
        conn_graph = ConnectedGraph(sess.graph, ['input_1'], ['matmul0/Softmax'])

        self.assertEqual(5, len(conn_graph.get_all_ops()))
        lstm_detected = False
        for op in conn_graph.get_all_ops().values():
            if op.type == 'LSTM' and op.name == 'lstm_stacked':
                self.assertEqual(op.pattern_type, 'LSTM_Stacked_TimeMajor_True_Sigmoid')
                lstm_detected = True
                inner_list = op.internal_ops
                self.assertEqual(75, len(inner_list))
                self.assertEqual(op.get_module(), sess.graph.get_operation_by_name('lstm_stacked/while/MatMul'))
        self.assertTrue(lstm_detected)
    def test_split_get_op_product_graph(self):
        """ Test connected graph construction on a graph with split op """

        tf.compat.v1.reset_default_graph()

        _ = split_and_concat_model()
        conn_graph = ConnectedGraph(tf.compat.v1.get_default_graph(), ['input_1'], ['split_and_concat_model/Softmax'])
        self.assertTrue(validate_branch_ops(conn_graph))
        self.assertTrue(validate_product_tensor_lists(conn_graph))
        self.assertEqual(1, conn_graph.branch_count)
        self.assertEqual(9, len(conn_graph.get_all_ops()))
        self.assertEqual(8 + len(tf.compat.v1.get_default_graph().get_collection('variables')),
                         len(conn_graph.get_all_products()))
示例#19
0
def fold_given_batch_norms(
    sess: tf.compat.v1.Session, input_op_names: Union[str, List[str]],
    output_op_names: Union[str, List[str]],
    layer_pairs: List[Tuple[tf.Operation, tf.Operation, bool]]
) -> tf.compat.v1.Session:
    """
    Api to fold custom set of bn layers in a model

    :param sess: active tensorflow session
    :param input_op_names: starting op in model or a list of starting ops in the model
    :param layer_pairs: List of tuple with conv and bn op layers as tf.Operation and
           a flag to indicate fold upstream or downstream
    :param output_op_names: List of output op names of the model, used to help ConnectedGraph determine valid ops
           (to ignore training ops for example).
    :return: updated_session after fold

    """

    # check for valid types
    if not isinstance(input_op_names, (str, List)):
        logger.error(
            'start op names must be passed as a string or a List of strings')

    # if passed start op name is a single string, create a list
    if isinstance(input_op_names, str):
        input_op_names = [input_op_names]

    connected_graph = ConnectedGraph(sess.graph, input_op_names,
                                     output_op_names)

    conn_tf_n_op_map = {}
    for op in connected_graph.get_all_ops().values():
        if op.type in ['FusedBatchNormV3', 'FusedBatchNorm']:
            conn_tf_n_op_map[op.get_module()] = op

    layer_pairs_internal_format = []
    for layer_pair in layer_pairs:
        conv_op, bn_op, is_bn_op_second = layer_pair
        layer_pairs_internal_format.append(
            (conv_op, conn_tf_n_op_map[bn_op].get_tf_op_with_io_tensor(),
             is_bn_op_second))

    # invoke internal api
    new_sess = _fold_given_auto_selected_batch_norms(
        sess, layer_pairs_internal_format)

    # save and load graph
    after_fold_sess = save_and_load_graph('./temp_graph', new_sess)

    return after_fold_sess
    def test_keras_model_functional_get_op_product_graph(self):
        """ Test connected graph construction on keras model functional """
        tf.compat.v1.reset_default_graph()

        _ = keras_model_functional()
        conn_graph = ConnectedGraph(tf.compat.v1.get_default_graph(), ['input_1'], ['keras_model_functional/Softmax'])
        self.assertTrue(validate_branch_ops(conn_graph))
        self.assertTrue(validate_product_tensor_lists(conn_graph))
        self.assertEqual(0, conn_graph.branch_count)
        self.assertEqual(14, len(conn_graph.get_all_ops()))

        # 13 products from inter module connections
        # 22 products from parameters
        self.assertEqual(35, len(conn_graph.get_all_products()))
    def test_connected_graph_with_detached_ops(self):
        """ Test connected graph construction on a graph with detached ops """
        tf.compat.v1.reset_default_graph()
        _ = single_residual()

        # Detach everything starting from conv2d_4/Conv2D and below
        detach_inputs(tf.compat.v1.get_default_graph().get_operation_by_name('conv2d_4/Conv2D'))
        conn_graph = ConnectedGraph(tf.compat.v1.get_default_graph(), ['input_1'], ['Relu_2'])
        self.assertTrue(validate_branch_ops(conn_graph))
        self.assertTrue(validate_product_tensor_lists(conn_graph))
        self.assertEqual(1, conn_graph.branch_count)
        self.assertEqual(13, len(conn_graph.get_all_ops()))
        # 12 products from interop connections, 16 from parameters
        self.assertEqual(28, len(conn_graph.get_all_products()))
    def test_tf_slim_with_softmax_model_get_op_product_graph(self):
        """ Test connected graph construction on tf_slim with softmax model """

        tf.compat.v1.reset_default_graph()

        x = tf.compat.v1.placeholder(tf.float32, [1, 32, 32, 3])
        _ = tf_slim_with_softmax(x)
        conn_graph = ConnectedGraph(tf.compat.v1.get_default_graph(), ['Placeholder'], ['softmax/Reshape_1'])
        self.assertTrue(validate_branch_ops(conn_graph))
        self.assertTrue(validate_product_tensor_lists(conn_graph))
        self.assertEqual(0, conn_graph.branch_count)
        self.assertEqual(8, len(conn_graph.get_all_ops()))
        # 7 products from interop connections
        # need to add 2 since gamma is treated as a parameter, even though it is a constant in this graph
        self.assertEqual(7 + len(tf.compat.v1.get_default_graph().get_collection('variables')) + 2,
                         len(conn_graph.get_all_products()))
    def test_tf_slim_model_get_op_product_graph(self):
        """ Test connected graph construction on tf_slim model """

        tf.compat.v1.reset_default_graph()

        x = tf.compat.v1.placeholder(tf.float32, [1, 32, 32, 3])
        _ = tf_slim_basic_model(x)
        conn_graph = ConnectedGraph(tf.compat.v1.get_default_graph(), ['Placeholder'], ['tf_slim_model/Softmax'])
        self.assertTrue(validate_branch_ops(conn_graph))
        self.assertTrue(validate_product_tensor_lists(conn_graph))
        self.assertEqual(0, conn_graph.branch_count)
        self.assertEqual(15, len(conn_graph.get_all_ops()))
        # 14 products from interop connections
        # need to add 1 since gamma is treated as a parameter for the training = True bn, even though it is a constant
        # in the graph
        self.assertEqual(14 + len(tf.compat.v1.get_default_graph().get_collection('variables')) + 1,
                         len(conn_graph.get_all_products()))
示例#24
0
    def test_get_ordered_operations(self):
        """ Test the creation of the ordered operations list """
        tf.compat.v1.reset_default_graph()
        sess = tf.compat.v1.Session()
        with sess.graph.as_default():
            _ = single_residual()
            conn_graph = ConnectedGraph(sess.graph, ["input_1"], ['Relu_2'])
            ordered_ops = get_ordered_ops(conn_graph.starting_ops)

        # check that there are the same number of modules in the ordered ops list as there are in the main ops dict
        self.assertEqual(len(ordered_ops), len(conn_graph.get_all_ops()))

        # check that for any module in the ordered ops list, all of its parent modules are earlier in the list
        seen_ops = set()
        for op in ordered_ops:
            input_products = op.get_input_products()
            for product in input_products:
                self.assertTrue(product.producer in seen_ops)
            seen_ops.add(op)
    def test_keras_model_functional_with_training_ops_get_op_product_graph(self):
        """ Test connected graph construction on keras model functional with training ops attached """
        tf.compat.v1.reset_default_graph()
        _ = keras_model_functional()

        # add training ops
        optimizer = tf.compat.v1.train.AdamOptimizer(learning_rate=1e-3, name='Adam_new')
        _ = optimizer.minimize(loss=tf.compat.v1.get_default_graph().get_tensor_by_name('keras_model_functional/Softmax:0'),
                               name='train_step_new')
        conn_graph = ConnectedGraph(tf.compat.v1.get_default_graph(), ["input_1"],
                                    output_op_names=['keras_model_functional/Softmax'])
        self.assertTrue(validate_branch_ops(conn_graph))
        self.assertTrue(validate_product_tensor_lists(conn_graph))
        self.assertEqual(0, conn_graph.branch_count)
        self.assertEqual(14, len(conn_graph.get_all_ops()))

        # 13 products from inter module connections
        # 22 products from parameters
        self.assertEqual(35, len(conn_graph.get_all_products()))
    def test_model_zoo_videnn_pose_estimation_model_with_input_split(self):
        """
        create a smaller network with connections as in pose estimation model and ViDeNN model
        Testwhen input is split and fed into two different ops
        :return:
        """

        tf.compat.v1.reset_default_graph()
        inputs = tf.keras.Input(shape=(None, None, 2), name="inputs")

        x = tf.keras.layers.Conv2D(2, kernel_size=3, padding='same')(inputs)
        x = tf.keras.layers.BatchNormalization()(x)
        x = tf.nn.relu(x)
        x = tf.keras.layers.Conv2D(2, kernel_size=3, padding='same')(x)
        x = tf.keras.layers.BatchNormalization()(x)
        z = tf.keras.layers.Add()([inputs, x])
        x = tf.nn.relu(z)

        init = tf.compat.v1.global_variables_initializer()
        sess = tf.compat.v1.Session()
        sess.run(init)

        conn_graph = ConnectedGraph(tf.compat.v1.get_default_graph(), starting_op_names=['inputs'],
                                    output_op_names=['Relu_1'])

        # get input ops
        # Find the input node(s) in the graph
        input_nodes = []
        for op in conn_graph.get_all_ops().values():
            if op.inputs and op.inputs[0].is_model_input:
                input_nodes.append(op)

        # there should be two ops marked as inputs in this model
        self.assertEqual(len(input_nodes), 2)
        self.assertEqual(input_nodes[0].name, 'add/add')
        self.assertEqual(input_nodes[1].name, "conv2d/Conv2D")
class GraphSearchUtils:

    """ Implements graph search utils required by CLE feature"""

    def __init__(self, model: tf.Graph, start_op_names: Union[str, List[str]], output_op_names: Union[str, List[str]]):
        if isinstance(start_op_names, str):
            start_op_names = [start_op_names]

        if isinstance(output_op_names, str):
            output_op_names = [output_op_names]

        self._connected_graph = ConnectedGraph(model, start_op_names, output_op_names)

    def find_and_replace_relu6_with_relu(self, sess: tf.compat.v1.Session) -> tf.compat.v1.Session:
        """
        finds and replaces Relu6 ops with Relu
        :return: updated session
        """
        for op in self._connected_graph.get_all_ops().values():
            if op.type in ['Relu6']:
                # send the session here, so we make the update on sess.graph (active graph)
                ReluUtils.replace_relu6_with_relu(sess, op.get_module())

        # in the end update the session
        after_relu_replace_sess = save_and_load_graph('./replace_relu6_with_relu', sess)

        return after_relu_replace_sess

    @staticmethod
    def find_downstream_layer_groups_to_scale(op, layer_groups, visited_nodes, current_group=None):
        """
        Populates all the layer groups eligible for cross layer scaling
        :param op: starting  op
        :param layer_groups: layer_groups as empty list
        :param visited_nodes: all the ops that have been visited
        :param current_group: op groups
        :return: None. Updates layer_groups[] if groups are found.
        """

        if not current_group:
            current_group = []

        if op in visited_nodes:
            return

        visited_nodes.append(op)
        logger.debug("Visiting node: {%s}", op.dotted_name)

        # If current node is Conv2D, add to the current group
        if op.type in ['Conv2D', 'DepthwiseConv2dNative']:
            current_group.append(op)

        # Terminating condition for current group
        if not (op.type in ['Conv2D', 'DepthwiseConv2dNative', 'Relu', 'PReLU', 'Pad', 'Identity']):
            if (len(current_group) > 1) and (current_group not in layer_groups):
                layer_groups.append(current_group)
                node_set = [op.dotted_name for op in current_group]
                logger.debug("Added new set of nodes: {%s}", node_set)
            current_group = []

        if op.output:
            for consumer in op.output.consumers:
                GraphSearchUtils.find_downstream_layer_groups_to_scale(consumer, layer_groups, visited_nodes,
                                                                       current_group)

        # Reached a leaf.. See if the current group has something to grab
        if (len(current_group) > 1) and (current_group not in layer_groups):
            layer_groups.append(current_group)
            node_set = [op.dotted_name for op in current_group]
            logger.debug("Added new set of nodes: {%s}", node_set)

    def find_layer_groups_to_scale_as_conn_ops(self) -> List[List[Op]]:
        """
        :return: List of groups of layers. Each group can be independently equalized
        """

        # Find the input node(s) in the graph
        input_nodes = []
        for op in self._connected_graph.get_all_ops().values():
            if op.inputs and op.inputs[0].is_model_input:
                input_nodes.append(op)

        layer_groups = []
        visited_nodes = []

        for op in input_nodes:
            self.find_downstream_layer_groups_to_scale(op=op, layer_groups=layer_groups,
                                                       visited_nodes=visited_nodes)

        return layer_groups

    def find_layer_groups_to_scale(self):
        """
        Find layer groups for scaling as tf ops
        :return: groups for scaling as tf ops
        """

        layer_groups_as_conn_graph_ops = self.find_layer_groups_to_scale_as_conn_ops()
        layer_groups_as_tf_ops, tf_op_to_conn_graph_op_map = self.convert_conn_graph_ops_to_tf_op(layer_groups_as_conn_graph_ops)

        return tf_op_to_conn_graph_op_map, layer_groups_as_tf_ops

    @staticmethod
    def convert_conn_graph_ops_to_tf_op(op_groups: List[List[Op]]) -> \
            List[List[tf.Operation]]:
        """
         Helper function to get op list as tf.Operation type to be usable for updating/scaling weights and biases
         using generic apis for tensor updates.
        :param op_groups: list of op groups as TfOperation type of used by Connected Graph
        :return: lis of op groups as tf.Operation  (standard TF op type)
        """
        tf_op_to_conn_graph_op_map = {}
        layer_groups_as_tf_ops = []
        for ops in op_groups:
            curr_group = []
            for op in ops:
                tf_op_to_conn_graph_op_map[op.get_module()] = op
                curr_group.append(op.get_module())
            layer_groups_as_tf_ops.append(curr_group)

        return layer_groups_as_tf_ops, tf_op_to_conn_graph_op_map

    @staticmethod
    def convert_layer_group_to_cls_sets(layer_group):
        """
        Helper function to convert a layer group to a list of cls sets
        :param layer_group: Given layer group to convert
        :return: List of cls sets
        """
        cls_sets = []

        prev_layer_to_scale = layer_group.pop(0)
        while layer_group:
            next_layer_to_scale = layer_group.pop(0)

            if next_layer_to_scale.type in ['DepthwiseConv2dNative']:
                next_non_depthwise_conv_layer = layer_group.pop(0)
                cls_sets.append((prev_layer_to_scale, next_layer_to_scale, next_non_depthwise_conv_layer))
                prev_layer_to_scale = next_non_depthwise_conv_layer

            else:
                cls_sets.append((prev_layer_to_scale, next_layer_to_scale))
                prev_layer_to_scale = next_layer_to_scale

        return cls_sets

    @staticmethod
    def is_relu_activation_present_in_cls_sets(cls_sets: List[ClsSet],
                                               tf_op_to_conn_graph_op_map: Dict) -> List[bool]:
        """
        check if there is Relu activations between cls sets
        :param cls_sets: cls conv op pairs
        :param tf_op_to_conn_graph_op_map: Map of tf-op => connected graph op
        :return: list of relu activation preset flags(True or False)
        corresponding to input cls_sets list
        """
        is_relu_activation_in_cls_sets = []
        for cls_set in cls_sets:
            # We need to check activation functions for all layers but the last one in the set
            # Because we are only interested in checking activation functions between the layers we will scale
            cls_set = cls_set[:-1]

            is_relu_activation_in_cls_set = ()
            for conv_op in cls_set:
                conn_graph_conv_op = tf_op_to_conn_graph_op_map[conv_op]
                is_relu_activation_in_cls_set += (ReluUtils.does_conv_have_relu_activation(conn_graph_conv_op), )

            if len(is_relu_activation_in_cls_set) == 1:
                is_relu_activation_in_cls_set = is_relu_activation_in_cls_set[0]

            is_relu_activation_in_cls_sets.append(is_relu_activation_in_cls_set)

        return is_relu_activation_in_cls_sets

    @staticmethod
    def map_op_names_to_ops(sess: tf.compat.v1.Session) -> Dict[str, tf.Operation]:
        """
        After the fold and cls , the graph is updated, so are the ops
        So, we need a way to map ops we stored on graph we began with, to perform
        high bias fold operation on latest ops in the updated graph.
        :param sess: active tf.compat.v1.Session (tf.compat.v1.Session type)
        :return: a dictionary of op names mapped to ops in the given new session.
        Note : only stores infor pertaining to bn and conv ops required by high bias fold.
        """

        tf_names_op_dict = {}
        with sess.graph.as_default():
            op_list = sess.graph.get_operations()
            for op in op_list:
                if op.type in ['Conv2D', 'DepthwiseConv2dNative', 'FusedBatchNormV3']:
                    tf_names_op_dict[op.name] = op

        return tf_names_op_dict