示例#1
0
    def test_prune_model_2_layers(self):
        """ Punning two layers with 0.5 comp-ratio in MNIST"""

        # 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
            _ = mnist_tf_model.create_model(data_format='channels_last')
            sess.run(tf.compat.v1.global_variables_initializer())

        # Create a layer database
        orig_layer_db = LayerDatabase(model=sess,
                                      input_shape=(1, 28, 28, 1),
                                      working_dir=None)
        conv1 = orig_layer_db.find_layer_by_name('conv2d/Conv2D')
        conv2 = orig_layer_db.find_layer_by_name('conv2d_1/Conv2D')

        layer_comp_ratio_list = [
            LayerCompRatioPair(conv1, Decimal(0.5)),
            LayerCompRatioPair(conv2, 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)

        conv1_a = comp_layer_db.find_layer_by_name('conv2d_a/Conv2D')
        conv1_b = comp_layer_db.find_layer_by_name('conv2d_b/Conv2D')

        # Weights shape [kh, kw, Nic, Noc]
        self.assertEqual([5, 1, 1, 2],
                         conv1_a.module.inputs[1].get_shape().as_list())
        self.assertEqual([1, 5, 2, 32],
                         conv1_b.module.inputs[1].get_shape().as_list())

        conv2_a = comp_layer_db.find_layer_by_name('conv2d_1_a/Conv2D')
        conv2_b = comp_layer_db.find_layer_by_name('conv2d_1_b/Conv2D')

        self.assertEqual([5, 1, 32, 53],
                         conv2_a.module.inputs[1].get_shape().as_list())
        self.assertEqual([1, 5, 53, 64],
                         conv2_b.module.inputs[1].get_shape().as_list())

        for layer in comp_layer_db:
            print("Layer: " + layer.name)
            print("   Module: " + str(layer.module.name))

        tf.compat.v1.reset_default_graph()
        sess.close()
        # delete temp directory
        shutil.rmtree(str('./temp_meta/'))
示例#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)))
示例#3
0
    def test_layer_database_working_dir(self):

        # 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
            _ = mnist_tf_model.create_model(data_format='channels_last')
            sess.run(tf.compat.v1.global_variables_initializer())

        meta_path = str('./temp_working_dir/')

        if not os.path.exists(meta_path):
            os.mkdir(meta_path)

        layer_db = LayerDatabase(model=sess, input_shape=(1, 28, 28, 1), working_dir=meta_path)
        copy_layer_db = copy.deepcopy(layer_db)

        shutil.rmtree(meta_path)

        tf.compat.v1.reset_default_graph()

        layer_db.model.close()
        copy_layer_db.model.close()
示例#4
0
    def test_layer_database_cpu_memory_Leak(self):

        # create tf.compat.v1.Session and initialize the weights and biases with zeros
        import psutil
        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
            _ = ResNet50(weights=None)
            init = tf.compat.v1.global_variables_initializer()

        sess.run(init)
        layer_db = LayerDatabase(model=sess, input_shape=(1, 28, 28, 1), working_dir=None)

        mem_before = psutil.virtual_memory().available

        for i in range(5):

            copy_layer_db = copy.deepcopy(layer_db)
            copy_layer_db.model.close()

        mem_after = psutil.virtual_memory().available

        memory_consumed = (mem_before - mem_after) / (1024 * 1024)

        print('Memory consumed in MB:', memory_consumed, 'for iterations : ', 5)

        self.assertTrue(memory_consumed < 200)
        layer_db.model.close()
示例#5
0
    def test_total_model_cost(self):

        # 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
            _ = mnist_tf_model.create_model(data_format='channels_last')
            sess.run(tf.compat.v1.global_variables_initializer())

        layer_database = LayerDatabase(model=sess,
                                       input_shape=(1, 28, 28, 1),
                                       working_dir=None)

        cost_calc = cc.CostCalculator()
        network_cost = cost_calc.compute_model_cost(layer_database)

        self.assertEqual(800 + 51200 + 3211264 + 10240, network_cost.memory)
        self.assertEqual(627200 + 10035200 + 3211264 + 10240, network_cost.mac)

        tf.compat.v1.reset_default_graph()
        sess.close()
        # delete temp directory
        shutil.rmtree(str('./temp_meta/'))
示例#6
0
    def _get_layer_pairs(layer_db: LayerDatabase,
                         module_comp_ratio_pairs: List[ModuleCompRatioPair]):
        layer_comp_ratio_pairs = []

        for pair in module_comp_ratio_pairs:
            layer_comp_ratio_pair = LayerCompRatioPair(
                layer_db.find_layer_by_module(pair.module), pair.comp_ratio)
            layer_comp_ratio_pairs.append(layer_comp_ratio_pair)

        return layer_comp_ratio_pairs
示例#7
0
    def test_prune_conv_no_bias(self):
        """ Test spatial svd on a conv layer with no bias """
        # 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
            inputs = tf.keras.Input(shape=(
                32,
                32,
                3,
            ))
            x = tf.keras.layers.Conv2D(32, (3, 3), use_bias=False)(inputs)
            _ = tf.keras.layers.Flatten()(x)
            sess.run(tf.compat.v1.global_variables_initializer())

        # Create a layer database
        orig_layer_db = LayerDatabase(model=sess,
                                      input_shape=(1, 32, 32, 3),
                                      working_dir=None)
        conv_op = orig_layer_db.find_layer_by_name('conv2d/Conv2D')

        layer_comp_ratio_list = [LayerCompRatioPair(conv_op, 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('conv2d_a/Conv2D')
        conv2d_b_op = comp_layer_db.model.graph.get_operation_by_name(
            'conv2d_b/Conv2D')
        reshape_op = comp_layer_db.model.graph.get_operation_by_name(
            'flatten/Reshape')
        self.assertEqual(conv2d_b_op, reshape_op.inputs[0].op)
示例#8
0
    def test_layer_database_destroy(self):

        # 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
            _ = mnist_tf_model.create_model(data_format='channels_last')
            init = tf.compat.v1.global_variables_initializer()

        sess.run(init)
        layer_db = LayerDatabase(model=sess, input_shape=(1, 28, 28, 1), working_dir=None)

        layer_db.destroy()

        self.assertRaises(RuntimeError, lambda: sess.run(init))
        # delete temp directory
        shutil.rmtree(str('./temp_meta/'))
示例#9
0
    def _perform_svd_and_split_layer(self, layer: Layer, rank: int,
                                     comp_layer_db: LayerDatabase):
        """
        Performs spatial svd and splits given layer into two layers
        :param layer: Layer to split
        :param rank: Rank to use for spatial svd splitting
        :param comp_layer_db: Compressed layer db to update with the split layers
        :return: None
        """

        # Split module using Spatial SVD
        module_a, module_b = SpatialSvdModuleSplitter.split_module(layer, rank)

        # get the output activation shape for first conv op
        output_shape_a = get_output_activation_shape(
            sess=layer.model,
            op=module_a,
            input_op_names=comp_layer_db.starting_ops,
            input_shape=comp_layer_db.input_shape)

        # get the output activation shape for second conv op
        output_shape_b = get_output_activation_shape(
            sess=layer.model,
            op=module_b,
            input_op_names=comp_layer_db.starting_ops,
            input_shape=comp_layer_db.input_shape)

        # Create two new layers and return them
        layer_a = Layer(model=layer.model,
                        op=module_a,
                        output_shape=output_shape_a)
        layer_b = Layer(model=layer.model,
                        op=module_b,
                        output_shape=output_shape_b)

        comp_layer_db.replace_layer_with_sequential_of_two_layers(
            layer, layer_a, layer_b)
示例#10
0
    def test_layer_database_with_mnist(self):

        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():
            # model will be constructed in default graph
            _ = mnist_tf_model.create_model(data_format='channels_last')
            # initialize the weights and biases with appropriate initializer
            sess.run(tf.compat.v1.global_variables_initializer())

        layer_db = LayerDatabase(model=sess, input_shape=(1, 28, 28, 1), working_dir=None)

        layers = list(layer_db._compressible_layers.values())

        # check output shapes
        self.assertEqual(layers[0].output_shape, [None, 32, 28, 28])
        self.assertEqual(layers[1].output_shape, [None, 64, 14, 14])
        self.assertEqual(layers[2].output_shape, [None, 1024, 1, 1])
        self.assertEqual(layers[3].output_shape, [None, 10, 1, 1])

        # check weight shapes
        # layer weight_shape is in common format [Noc, Nic, k_h, k_w]
        self.assertEqual(layers[0].weight_shape, [32, 1, 5, 5])
        self.assertEqual(layers[1].weight_shape, [64, 32, 5, 5])
        self.assertEqual(layers[2].weight_shape, [1024, 3136])
        self.assertEqual(layers[3].weight_shape, [10, 1024])

        # tensorflow weights are stored in [k_h, k_w, Nic, Noc]
        self.assertEqual(layers[0].module.inputs[1].eval(session=layer_db.model).shape, (5, 5, 1, 32))
        self.assertEqual(layers[1].module.inputs[1].eval(session=layer_db.model).shape, (5, 5, 32, 64))
        self.assertEqual(layers[2].module.inputs[1].eval(session=layer_db.model).shape, (3136, 1024))
        self.assertEqual(layers[3].module.inputs[1].eval(session=layer_db.model).shape, (1024, 10))

        tf.compat.v1.reset_default_graph()
        sess.close()
        # delete temp directory
        shutil.rmtree(str('./temp_meta/'))
示例#11
0
    def test_calculate_spatial_svd_cost_all_layers(self):

        # 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
            _ = mnist_tf_model.create_model(data_format='channels_last')
            sess.run(tf.compat.v1.global_variables_initializer())

        layer_database = LayerDatabase(model=sess,
                                       input_shape=(1, 28, 28, 1),
                                       working_dir=None)

        # Compress all layers by 50%

        # Create a list of tuples of (layer, comp_ratio)
        layer_ratio_list = []

        for layer in layer_database:

            if layer.module.type == 'Conv2D':
                layer_ratio_list.append(LayerCompRatioPair(
                    layer, Decimal(0.5)))
            else:
                layer_ratio_list.append(LayerCompRatioPair(layer, None))

        compressed_cost = cc.SpatialSvdCostCalculator.calculate_compressed_cost(
            layer_database, layer_ratio_list, CostMetric.mac)

        self.assertEqual(8466464, compressed_cost.mac)

        tf.compat.v1.reset_default_graph()
        sess.close()
        # delete temp directory
        shutil.rmtree(str('./temp_meta/'))
示例#12
0
    def _reconstruct_layers(self, layers_to_reconstruct: List[Layer],
                            orig_layer_name_to_pruned_name_and_mask_dict: Dict[
                                str, Tuple[str, List[int]]],
                            layer_db: LayerDatabase,
                            comp_layer_db: LayerDatabase):
        """
        Reconstruct weights and biases of layers in the layers_to_reconstruct list.
        :param layers_to_reconstruct: List of layers to reconstruct weights and biases of
        :param orig_layer_name_to_pruned_name_and_mask_dict: Dictionary mapping original layer names to most recent
        pruned op name and most recent output masks.
        :param layer_db: Original layer database
        :param comp_layer_db: Compressed layer database
        """
        for layer in layers_to_reconstruct:
            # Get output mask of layer, that contains information about all channels winnowed since the start
            pruned_layer_name, output_mask = \
                orig_layer_name_to_pruned_name_and_mask_dict.get(layer.name, (None, None))
            assert pruned_layer_name is not None

            pruned_layer = comp_layer_db.find_layer_by_name(pruned_layer_name)
            self._data_subsample_and_reconstruction(layer, pruned_layer,
                                                    output_mask, layer_db,
                                                    comp_layer_db)
    def test_eval_scores_with_spatial_svd_pruner(self):

        pruner = SpatialSvdPruner()
        eval_func = unittest.mock.MagicMock()
        eval_func.side_effect = [
            90, 80, 70, 60, 50, 40, 30, 20, 10, 91, 81, 71, 61, 51, 41, 31, 21,
            11
        ]

        # 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
            _ = mnist_tf_model.create_model(data_format='channels_last')
            sess.run(tf.compat.v1.global_variables_initializer())

        # Create a layer database
        layer_db = LayerDatabase(model=sess,
                                 input_shape=(1, 28, 28, 1),
                                 working_dir=None)
        layer1 = layer_db.find_layer_by_name('conv2d/Conv2D')
        layer2 = layer_db.find_layer_by_name('conv2d_1/Conv2D')

        layer_db.mark_picked_layers([layer1, layer2])

        url, process = start_bokeh_server_session(8006)
        bokeh_session = BokehServerSession(url=url, session_id="compression")

        # Instantiate child
        greedy_algo = comp_ratio_select.GreedyCompRatioSelectAlgo(
            layer_db=layer_db,
            pruner=pruner,
            cost_calculator=SpatialSvdCostCalculator(),
            eval_func=eval_func,
            eval_iterations=20,
            cost_metric=CostMetric.mac,
            target_comp_ratio=0.5,
            num_candidates=10,
            use_monotonic_fit=True,
            saved_eval_scores_dict=None,
            comp_ratio_rounding_algo=None,
            use_cuda=False,
            bokeh_session=bokeh_session)

        dict = greedy_algo._compute_eval_scores_for_all_comp_ratio_candidates()

        print()
        print(dict)
        self.assertEqual(90, dict['conv2d/Conv2D'][Decimal('0.1')])

        self.assertEqual(51, dict['conv2d_1/Conv2D'][Decimal('0.5')])
        self.assertEqual(21, dict['conv2d_1/Conv2D'][Decimal('0.8')])

        tf.compat.v1.reset_default_graph()
        sess.close()

        bokeh_session.server_session.close("test complete")
        os.killpg(os.getpgid(process.pid), signal.SIGTERM)
示例#14
0
    def prune_model(self, layer_db: LayerDatabase,
                    layer_comp_ratio_list: List[LayerCompRatioPair],
                    cost_metric: CostMetric, trainer):

        # sort all the layers in layer_comp_ratio_list based on occurrence
        layer_comp_ratio_list = self._sort_on_occurrence(
            layer_db.model, layer_comp_ratio_list)

        # Copy the db
        comp_layer_db = copy.deepcopy(layer_db)
        current_sess = comp_layer_db.model

        # Dictionary to map original layer name to list of most recent pruned layer name and output mask.
        # Masks remain at the original length and specify channels winnowed after each round of winnower.
        orig_layer_name_to_pruned_name_and_mask_dict = {}
        # Dictionary to map most recent pruned layer name to the original layer name
        pruned_name_to_orig_name_dict = {}
        # List to hold original layers to reconstruct
        layers_to_reconstruct = []
        detached_op_names = set()

        # Prune layers which have comp ratios less than 1
        for layer_comp_ratio in layer_comp_ratio_list:
            orig_layer = layer_db.find_layer_by_name(
                layer_comp_ratio.layer.name)
            if layer_comp_ratio.comp_ratio is not None and layer_comp_ratio.comp_ratio < 1.0:
                # 1) channel selection
                prune_indices = self._select_inp_channels(
                    orig_layer, layer_comp_ratio.comp_ratio)
                if not prune_indices:
                    continue

                # 2) Winnowing the model
                current_sess, ordered_modules_list = winnow.winnow_tf_model(
                    current_sess,
                    self._input_op_names,
                    self._output_op_names,
                    [(orig_layer.module, prune_indices)],
                    reshape=self._allow_custom_downsample_ops,
                    in_place=True,
                    verbose=False)
                if not ordered_modules_list:
                    continue

                layers_to_reconstruct.append(orig_layer)
                # Update dictionaries with new info about pruned ops and new masks
                self._update_pruned_ops_and_masks_info(
                    ordered_modules_list,
                    orig_layer_name_to_pruned_name_and_mask_dict,
                    pruned_name_to_orig_name_dict, detached_op_names)

        # Save and reload modified graph to allow changes to take effect
        # Need to initialize uninitialized variables first since only newly winnowed conv ops are initialized during
        # winnow_tf_model, and all other newly winnowed ops are not.
        with current_sess.graph.as_default():
            initialize_uninitialized_vars(current_sess)
        current_sess = save_and_load_graph('./saver', current_sess)
        comp_layer_db.update_database(current_sess,
                                      detached_op_names,
                                      update_model=True)

        # Perform reconstruction
        self._reconstruct_layers(layers_to_reconstruct,
                                 orig_layer_name_to_pruned_name_and_mask_dict,
                                 layer_db, comp_layer_db)

        return comp_layer_db
示例#15
0
    def calculate_compressed_cost(
            self, layer_db: LayerDatabase,
            layer_comp_ratio_list: List[LayerCompRatioPair]) -> Cost:
        """
        Calculate cost of a compressed model given a set of layers and corresponding comp-ratios
        :param layer_db: Layer database for original model
        :param layer_comp_ratio_list: List of (layer + comp-ratio) pairs
        :return: Estimated cost of the compressed model
        """

        # sort all the layers in layer_comp_ratio_list based on occurrence
        layer_comp_ratio_list = self._sort_on_occurrence(
            layer_db.model, layer_comp_ratio_list)

        detached_op_names = set()

        # Copy the db
        comp_layer_db = copy.deepcopy(layer_db)
        current_sess = comp_layer_db.model

        for layer_comp_ratio in layer_comp_ratio_list:

            orig_layer = layer_db.find_layer_by_name(
                layer_comp_ratio.layer.name)
            comp_ratio = layer_comp_ratio.comp_ratio

            if comp_ratio is not None and comp_ratio < 1.0:

                # select input channels of conv2d op to winnow
                prune_indices = self._select_inp_channels(
                    orig_layer, comp_ratio)
                if not prune_indices:
                    continue

                # Winnow the selected op and modify it's upstream affected ops
                current_sess, ordered_modules_list = winnow.winnow_tf_model(
                    current_sess,
                    self._input_op_names,
                    self._output_op_names,
                    [(orig_layer.module, prune_indices)],
                    reshape=self._allow_custom_downsample_ops,
                    in_place=True,
                    verbose=False)
                if not ordered_modules_list:
                    continue

                # Get all the detached op names from updated session graph
                for orig_op_name, _, _, _ in ordered_modules_list:
                    detached_op_names.add(orig_op_name)

        # update layer database by excluding the detached ops
        comp_layer_db.update_database(current_sess,
                                      detached_op_names,
                                      update_model=False)

        # calculate the cost of this model
        compressed_model_cost = CostCalculator.compute_model_cost(
            comp_layer_db)

        # close the session associated with compressed layer database
        comp_layer_db.model.close()

        return compressed_model_cost
示例#16
0
    def test_calculate_channel_pruning_cost_all_layers(self):

        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():
            # model will be constructed in default graph
            _ = mnist(data_format='channels_last')
            # initialize the weights and biases with appropriate initializer
            sess.run(tf.compat.v1.global_variables_initializer())

        meta_path = str('./temp_working_dir/')
        if not os.path.exists(meta_path):
            os.mkdir(meta_path)

        layer_db = LayerDatabase(model=sess,
                                 input_shape=(1, 28, 28, 1),
                                 working_dir=meta_path)

        # Compress all layers by 50%

        # Create a list of tuples of (layer, comp_ratio)
        layer_ratio_list = []

        # Unfortunately in mnist we can only input channel prune conv2d_1/Conv2D
        for layer in layer_db:
            if layer.module.name == 'conv2d_1/Conv2D':
                layer_ratio_list.append(
                    LayerCompRatioPair(layer, Decimal('0.5')))
            else:
                layer_ratio_list.append(LayerCompRatioPair(layer, None))

        inp_op_names = ['reshape_input']
        output_op_names = ['dense_1/BiasAdd']

        data_set = unittest.mock.MagicMock()
        batch_size = unittest.mock.MagicMock()
        num_reconstruction_samples = unittest.mock.MagicMock()

        pruner = InputChannelPruner(
            input_op_names=inp_op_names,
            output_op_names=output_op_names,
            data_set=data_set,
            batch_size=batch_size,
            num_reconstruction_samples=num_reconstruction_samples,
            allow_custom_downsample_ops=True)

        cost_calculator = ChannelPruningCostCalculator(pruner)

        compressed_cost = cost_calculator.calculate_compressed_cost(
            layer_db, layer_ratio_list, CostMetric.mac)

        self.assertEqual(8552704, compressed_cost.mac)
        self.assertEqual(3247504, compressed_cost.memory)

        # delete the meta and the checkpoint files
        shutil.rmtree(meta_path)

        layer_db.model.close()
示例#17
0
    def test_calculate_channel_pruning_cost_two_layers(self):
        """
        test compressed model cost using two layers
        :return:
        """
        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():
            # model will be constructed in default graph
            test_models.single_residual()
            init = tf.compat.v1.global_variables_initializer()

        # initialize the weights and biases with appropriate initializer
        sess.run(init)

        meta_path = str('./temp_working_dir/')

        if not os.path.exists(meta_path):
            os.mkdir(meta_path)

        layer_db = LayerDatabase(model=sess,
                                 input_shape=None,
                                 working_dir=meta_path)

        # Create a list of tuples of (layer, comp_ratio)
        layer_ratio_list = []

        layer_names = ['conv2d_2/Conv2D', 'conv2d_3/Conv2D']
        for layer in layer_db:
            if layer.module.name in layer_names:
                layer_ratio_list.append(LayerCompRatioPair(layer, 0.5))
            else:
                layer_ratio_list.append(LayerCompRatioPair(layer, None))

        input_op_names = ['input_1']
        output_op_names = ['single_residual/Softmax']
        data_set = unittest.mock.MagicMock()
        batch_size = unittest.mock.MagicMock()
        num_reconstruction_samples = unittest.mock.MagicMock()

        pruner = InputChannelPruner(
            input_op_names=input_op_names,
            output_op_names=output_op_names,
            data_set=data_set,
            batch_size=batch_size,
            num_reconstruction_samples=num_reconstruction_samples,
            allow_custom_downsample_ops=True)

        cost_calculator = ChannelPruningCostCalculator(pruner)

        compressed_cost = cost_calculator.calculate_compressed_cost(
            layer_db, layer_ratio_list, CostMetric.mac)

        self.assertEqual(108544, compressed_cost.mac)
        self.assertEqual(1264, compressed_cost.memory)

        # delete the meta and the checkpoint files
        shutil.rmtree(meta_path)

        layer_db.model.close()
示例#18
0
    def create_spatial_svd_algo(cls,
                                sess: tf.compat.v1.Session,
                                working_dir: str,
                                eval_callback: EvalFunction,
                                eval_iterations,
                                input_shape: Union[Tuple, List[Tuple]],
                                cost_metric: CostMetric,
                                params: SpatialSvdParameters,
                                bokeh_session=None) -> CompressionAlgo:
        """
        Factory method to construct SpatialSvdCompressionAlgo

        :param sess: Model, represented by a tf.compat.v1.Session, to compress
        :param working_dir: path to store temp meta and checkpoint files
        :param eval_callback: Evaluation callback for the model
        :param eval_iterations: Evaluation iterations
        :param input_shape: tuple or list of tuples of input shape to the model
        :param cost_metric: Cost metric (mac or memory)
        :param params: Spatial SVD compression parameters
        :param bokeh_session: The Bokeh Session to display plots
        :return: An instance of SpatialSvdCompressionAlgo
        """

        # pylint: disable=too-many-arguments
        # pylint: disable=too-many-locals
        # Rationale: Factory functions unfortunately need to deal with a lot of parameters

        # Create a layer database
        layer_db = LayerDatabase(sess,
                                 input_shape,
                                 working_dir,
                                 starting_ops=params.input_op_names,
                                 ending_ops=params.output_op_names)
        use_cuda = False

        # Create a pruner
        pruner = SpatialSvdPruner()
        cost_calculator = SpatialSvdCostCalculator()
        comp_ratio_rounding_algo = RankRounder(params.multiplicity,
                                               cost_calculator)

        # Create a comp-ratio selection algorithm
        if params.mode == SpatialSvdParameters.Mode.auto:
            greedy_params = params.mode_params.greedy_params
            comp_ratio_select_algo = GreedyCompRatioSelectAlgo(
                layer_db,
                pruner,
                cost_calculator,
                eval_callback,
                eval_iterations,
                cost_metric,
                greedy_params.target_comp_ratio,
                greedy_params.num_comp_ratio_candidates,
                greedy_params.use_monotonic_fit,
                greedy_params.saved_eval_scores_dict,
                comp_ratio_rounding_algo,
                use_cuda,
                bokeh_session=bokeh_session)
            layer_selector = ConvNoDepthwiseLayerSelector()
            modules_to_ignore = params.mode_params.modules_to_ignore

        else:
            # Convert (module,comp-ratio) pairs to (layer,comp-ratio) pairs
            layer_comp_ratio_pairs = cls._get_layer_pairs(
                layer_db, params.mode_params.list_of_module_comp_ratio_pairs)

            comp_ratio_select_algo = ManualCompRatioSelectAlgo(
                layer_db,
                layer_comp_ratio_pairs,
                comp_ratio_rounding_algo,
                cost_metric=cost_metric)

            layer_selector = ManualLayerSelector(layer_comp_ratio_pairs)
            modules_to_ignore = []

        # Create the overall Spatial SVD compression algorithm
        spatial_svd_algo = CompressionAlgo(layer_db, comp_ratio_select_algo,
                                           pruner, eval_callback,
                                           layer_selector, modules_to_ignore,
                                           cost_calculator, use_cuda)

        return spatial_svd_algo
示例#19
0
    def test_prune_layer(self):
        """ Pruning single layer with 0.5 comp-ratio in MNIST"""

        # 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
            _ = mnist_tf_model.create_model(data_format='channels_last')
            sess.run(tf.compat.v1.global_variables_initializer())

        # Create a layer database
        orig_layer_db = LayerDatabase(model=sess,
                                      input_shape=(1, 28, 28, 1),
                                      working_dir=None)
        # Copy the db
        comp_layer_db = copy.deepcopy(orig_layer_db)
        conv1 = comp_layer_db.find_layer_by_name('conv2d/Conv2D')

        # before the splitting
        bias_op = get_succeeding_bias_op(conv1.module)
        for consumer in bias_op.outputs[0].consumers():
            self.assertEqual(consumer.name, "conv2d/Relu")

        spatial_svd_pruner = SpatialSvdPruner()
        spatial_svd_pruner._prune_layer(orig_layer_db, comp_layer_db, conv1,
                                        0.5, CostMetric.mac)
        conv2d_a_op = comp_layer_db.model.graph.get_operation_by_name(
            'conv2d_a/Conv2D')
        conv2d_b_op = comp_layer_db.model.graph.get_operation_by_name(
            'conv2d_b/Conv2D')
        conv2d_a_weight = WeightTensorUtils.get_tensor_as_numpy_data(
            comp_layer_db.model, conv2d_a_op)
        conv2d_b_weight = WeightTensorUtils.get_tensor_as_numpy_data(
            comp_layer_db.model, conv2d_b_op)

        conv1_a = comp_layer_db.find_layer_by_name('conv2d_a/Conv2D')
        conv1_b = comp_layer_db.find_layer_by_name('conv2d_b/Conv2D')

        # [Noc, Nic, kh, kw]
        self.assertEqual([2, 1, 5, 1], conv1_a.weight_shape)
        self.assertEqual([32, 2, 1, 5], conv1_b.weight_shape)

        # after the splitting
        bias_op = get_succeeding_bias_op(conv1_b.module)

        for consumer in bias_op.outputs[0].consumers():
            self.assertEqual(consumer.name, "conv2d/Relu")

        # original layer should be not there in the database
        self.assertRaises(
            KeyError,
            lambda: comp_layer_db.find_layer_by_name('conv2d/Conv2D'))

        # check if the layer replacement is done correctly
        orig_conv_op = comp_layer_db.model.graph.get_operation_by_name(
            'conv2d/Conv2D')
        bias_op = get_succeeding_bias_op(orig_conv_op)

        # consumers list should be empty
        consumers = [consumer for consumer in bias_op.outputs[0].consumers()]
        self.assertEqual(len(consumers), 0)

        # Check that weights loaded during svd pruning will stick after save and load
        new_sess = save_and_load_graph('./temp_meta/', comp_layer_db.model)
        conv2d_a_op = comp_layer_db.model.graph.get_operation_by_name(
            'conv2d_a/Conv2D')
        conv2d_b_op = comp_layer_db.model.graph.get_operation_by_name(
            'conv2d_b/Conv2D')
        conv2d_a_weight_after_save_load = WeightTensorUtils.get_tensor_as_numpy_data(
            comp_layer_db.model, conv2d_a_op)
        conv2d_b_weight_after_save_load = WeightTensorUtils.get_tensor_as_numpy_data(
            comp_layer_db.model, conv2d_b_op)
        self.assertTrue(
            np.array_equal(conv2d_a_weight, conv2d_a_weight_after_save_load))
        self.assertTrue(
            np.array_equal(conv2d_b_weight, conv2d_b_weight_after_save_load))

        tf.compat.v1.reset_default_graph()
        sess.close()
        new_sess.close()
        # delete temp directory
        shutil.rmtree(str('./temp_meta/'))
    def test_sort_on_occurrence(self):
        """
        Test sorting of ops based on occurrence
        """
        AimetLogger.set_area_logger_level(AimetLogger.LogAreas.Winnow,
                                          logging.INFO)
        tf.compat.v1.reset_default_graph()

        orig_g = tf.Graph()
        with orig_g.as_default():
            _ = VGG16(weights=None,
                      input_shape=(224, 224, 3),
                      include_top=False)
            orig_init = tf.compat.v1.global_variables_initializer()

        # create sess with graph
        orig_sess = tf.compat.v1.Session(graph=orig_g)
        orig_sess.run(orig_init)

        # create layer database
        layer_db = LayerDatabase(model=orig_sess,
                                 input_shape=(1, 224, 224, 3),
                                 working_dir=None)

        block1_conv2 = layer_db.model.graph.get_operation_by_name(
            'block1_conv2/Conv2D')
        block2_conv1 = layer_db.model.graph.get_operation_by_name(
            'block2_conv1/Conv2D')
        block2_conv2 = layer_db.model.graph.get_operation_by_name(
            'block2_conv2/Conv2D')
        block5_conv3 = layer_db.model.graph.get_operation_by_name(
            'block5_conv3/Conv2D')

        # output shape in NCHW format
        block1_conv2_output_shape = block1_conv2.outputs[0].shape
        block2_conv1_output_shape = block2_conv1.outputs[0].shape
        block2_conv2_output_shape = block2_conv2.outputs[0].shape
        block5_conv3_output_shape = block5_conv3.outputs[0].shape

        # keeping compression ratio = None for all layers
        layer_comp_ratio_list = [
            LayerCompRatioPair(
                Layer(model=layer_db.model,
                      op=block5_conv3,
                      output_shape=block5_conv3_output_shape), None),
            LayerCompRatioPair(
                Layer(model=layer_db.model,
                      op=block2_conv2,
                      output_shape=block2_conv2_output_shape), None),
            LayerCompRatioPair(
                Layer(model=layer_db.model,
                      op=block1_conv2,
                      output_shape=block1_conv2_output_shape), None),
            LayerCompRatioPair(
                Layer(model=layer_db.model,
                      op=block2_conv1,
                      output_shape=block2_conv1_output_shape), None)
        ]

        input_op_names = ['input_1']
        output_op_names = ['block5_pool/MaxPool']
        dataset = unittest.mock.MagicMock()
        batch_size = unittest.mock.MagicMock()
        num_reconstruction_samples = unittest.mock.MagicMock()

        cp = InputChannelPruner(
            input_op_names=input_op_names,
            output_op_names=output_op_names,
            data_set=dataset,
            batch_size=batch_size,
            num_reconstruction_samples=num_reconstruction_samples,
            allow_custom_downsample_ops=True)

        sorted_layer_comp_ratio_list = cp._sort_on_occurrence(
            layer_db.model, layer_comp_ratio_list)

        self.assertEqual(sorted_layer_comp_ratio_list[0].layer.module,
                         block1_conv2)
        self.assertEqual(sorted_layer_comp_ratio_list[1].layer.module,
                         block2_conv1)
        self.assertEqual(sorted_layer_comp_ratio_list[2].layer.module,
                         block2_conv2)
        self.assertEqual(sorted_layer_comp_ratio_list[3].layer.module,
                         block5_conv3)

        self.assertEqual(len(sorted_layer_comp_ratio_list), 4)
        layer_db.model.close()
        # delete temp directory
        shutil.rmtree(str('./temp_meta/'))
    def test_prune_model(self):
        """
        Test end-to-end prune_model with VGG16-imagenet
        """
        AimetLogger.set_area_logger_level(AimetLogger.LogAreas.Winnow,
                                          logging.INFO)
        tf.compat.v1.reset_default_graph()

        batch_size = 1
        input_data = np.random.rand(100, 224, 224, 3)
        dataset = tf.data.Dataset.from_tensor_slices(input_data)
        dataset = dataset.batch(batch_size=batch_size)

        orig_g = tf.Graph()

        with orig_g.as_default():
            _ = VGG16(weights=None,
                      input_shape=(224, 224, 3),
                      include_top=False)
            orig_init = tf.compat.v1.global_variables_initializer()

        input_op_names = ['input_1']
        output_op_names = ['block5_pool/MaxPool']
        # create sess with graph
        orig_sess = tf.compat.v1.Session(graph=orig_g)
        # initialize all the variables in VGG16
        orig_sess.run(orig_init)

        # create layer database
        layer_db = LayerDatabase(model=orig_sess,
                                 input_shape=(1, 224, 224, 3),
                                 working_dir=None)

        block1_conv2 = layer_db.model.graph.get_operation_by_name(
            'block1_conv2/Conv2D')
        block2_conv1 = layer_db.model.graph.get_operation_by_name(
            'block2_conv1/Conv2D')
        block2_conv2 = layer_db.model.graph.get_operation_by_name(
            'block2_conv2/Conv2D')

        # output shape in NCHW format
        block1_conv2_output_shape = block1_conv2.outputs[0].shape
        block2_conv1_output_shape = block2_conv1.outputs[0].shape
        block2_conv2_output_shape = block2_conv2.outputs[0].shape

        # keeping compression ratio = 0.5 for all layers
        layer_comp_ratio_list = [
            LayerCompRatioPair(
                Layer(model=layer_db.model,
                      op=block1_conv2,
                      output_shape=block1_conv2_output_shape), 0.5),
            LayerCompRatioPair(
                Layer(model=layer_db.model,
                      op=block2_conv1,
                      output_shape=block2_conv1_output_shape), 0.5),
            LayerCompRatioPair(
                Layer(model=layer_db.model,
                      op=block2_conv2,
                      output_shape=block2_conv2_output_shape), 0.5)
        ]

        cp = InputChannelPruner(input_op_names=input_op_names,
                                output_op_names=output_op_names,
                                data_set=dataset,
                                batch_size=batch_size,
                                num_reconstruction_samples=20,
                                allow_custom_downsample_ops=True)

        comp_layer_db = cp.prune_model(
            layer_db=layer_db,
            layer_comp_ratio_list=layer_comp_ratio_list,
            cost_metric=CostMetric.mac,
            trainer=None)

        pruned_block1_conv2 = comp_layer_db.find_layer_by_name(
            'reduced_reduced_block1_conv2/Conv2D')
        pruned_block2_conv1 = comp_layer_db.find_layer_by_name(
            'reduced_reduced_block2_conv1/Conv2D')
        pruned_block2_conv2 = comp_layer_db.find_layer_by_name(
            'reduced_block2_conv2/Conv2D')

        # input channels = 64 * 0.5 = 32
        # output channels = 64 * 0.5 = 32
        self.assertEqual(pruned_block1_conv2.weight_shape[1], 32)
        self.assertEqual(pruned_block1_conv2.weight_shape[0], 32)

        # input channels = 64 * 0.5 = 32
        # output channels = 128 * 0.5 = 64
        self.assertEqual(pruned_block2_conv1.weight_shape[1], 32)
        self.assertEqual(pruned_block2_conv1.weight_shape[0], 64)

        # input channels = 128 * 0.5 = 64
        # output channels = 128
        self.assertEqual(pruned_block2_conv2.weight_shape[1], 64)
        self.assertEqual(pruned_block2_conv2.weight_shape[0], 128)

        layer_db.model.close()
        comp_layer_db.model.close()
        # delete temp directory
        shutil.rmtree(str('./temp_meta/'))
    def test_select_per_layer_comp_ratios_with_spatial_svd_pruner(self):

        pruner = SpatialSvdPruner()
        eval_func = unittest.mock.MagicMock()
        rounding_algo = unittest.mock.MagicMock()
        eval_func.side_effect = [
            10, 20, 30, 40, 50, 60, 70, 80, 90, 11, 21, 31, 35, 40, 45, 50, 55,
            60
        ]
        rounding_algo.round.side_effect = [
            0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 0.1, 0.2, 0.3, 0.4,
            0.5, 0.6, 0.7, 0.8, 0.9
        ]

        # 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
            _ = mnist_tf_model.create_model(data_format='channels_last')
            sess.run(tf.compat.v1.global_variables_initializer())

        # Create a layer database
        layer_db = LayerDatabase(model=sess,
                                 input_shape=(1, 28, 28, 1),
                                 working_dir=None)

        selected_layers = [
            layer for layer in layer_db if layer.module.type == 'Conv2D'
        ]
        layer_db.mark_picked_layers(selected_layers)

        url, process = start_bokeh_server_session(8006)
        bokeh_session = BokehServerSession(url=url, session_id="compression")

        # Instantiate child
        greedy_algo = comp_ratio_select.GreedyCompRatioSelectAlgo(
            layer_db=layer_db,
            pruner=pruner,
            cost_calculator=SpatialSvdCostCalculator(),
            eval_func=eval_func,
            eval_iterations=20,
            cost_metric=CostMetric.mac,
            target_comp_ratio=Decimal(0.4),
            num_candidates=10,
            use_monotonic_fit=True,
            saved_eval_scores_dict=None,
            comp_ratio_rounding_algo=rounding_algo,
            use_cuda=False,
            bokeh_session=bokeh_session)

        layer_comp_ratio_list, stats = greedy_algo.select_per_layer_comp_ratios(
        )

        original_cost = SpatialSvdCostCalculator.compute_model_cost(layer_db)

        for layer in layer_db:
            if layer not in selected_layers:
                layer_comp_ratio_list.append(LayerCompRatioPair(layer, None))
        compressed_cost = SpatialSvdCostCalculator.calculate_compressed_cost(
            layer_db, layer_comp_ratio_list, CostMetric.mac)

        actual_compression_ratio = compressed_cost.mac / original_cost.mac
        self.assertTrue(
            math.isclose(Decimal(0.3), actual_compression_ratio, abs_tol=0.8))

        print('\n')
        for pair in layer_comp_ratio_list:
            print(pair)

        tf.compat.v1.reset_default_graph()
        sess.close()

        bokeh_session.server_session.close("test complete")
        os.killpg(os.getpgid(process.pid), signal.SIGTERM)
示例#23
0
    def test_layer_database_with_dynamic_shape(self):
        """ test layer database creation with different input shapes"""
        # create tf.compat.v1.Session and initialize the weights and biases with zeros
        config = tf.compat.v1.ConfigProto()
        config.gpu_options.allow_growth = True

        graph = tf.Graph()

        with graph.as_default():
            # by default, model will be constructed in default graph
            input_placeholder = tf.compat.v1.placeholder(tf.float32, [None, None, None, 3], 'input')
            x = tf.keras.layers.Conv2D(8, (2, 2), padding='SAME')(input_placeholder)
            x = tf.keras.layers.BatchNormalization(momentum=.3, epsilon=.65)(x)
            x = tf.keras.layers.Conv2D(8, (1, 1), padding='SAME', activation=tf.nn.tanh)(x)
            x = tf.keras.layers.BatchNormalization(momentum=.4, epsilon=.25)(x)
            init = tf.compat.v1.global_variables_initializer()

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

        layer_db = LayerDatabase(model=sess, input_shape=(1, 224, 224, 3), working_dir=None, starting_ops=['input'],
                                 ending_ops=['batch_normalization_1/cond/Merge'])

        conv1_layer = layer_db.find_layer_by_name('conv2d/Conv2D')
        conv2_layer = layer_db.find_layer_by_name('conv2d_1/Conv2D')

        self.assertEqual(conv1_layer.output_shape, [1, 8, 224, 224])
        self.assertEqual(conv2_layer.output_shape, [1, 8, 224, 224])

        layer_db.destroy()

        # 2) try with different input shape

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

        batch_size = 32
        layer_db = LayerDatabase(model=sess, input_shape=(batch_size, 28, 28, 3), working_dir=None,
                                 starting_ops=['input'], ending_ops=['batch_normalization_1/cond/Merge'])

        conv1_layer = layer_db.find_layer_by_name('conv2d/Conv2D')
        conv2_layer = layer_db.find_layer_by_name('conv2d_1/Conv2D')

        self.assertEqual(conv1_layer.output_shape, [32, 8, 28, 28])
        self.assertEqual(conv2_layer.output_shape, [32, 8, 28, 28])

        layer_db.destroy()
示例#24
0
    def test_layer_database_deepcopy(self):

        # 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
            _ = mnist_tf_model.create_model(data_format='channels_last')
            sess.run(tf.compat.v1.global_variables_initializer())

        layer_db = LayerDatabase(model=sess, input_shape=(1, 28, 28, 1), working_dir=None)
        layers = list(layer_db._compressible_layers.values())

        # set the picked_for_compression for first two layers
        layers[0].picked_for_compression = True
        layers[1].picked_for_compression = True

        layer_db_copy = copy.deepcopy(layer_db)
        layers_copy = list(layer_db_copy._compressible_layers.values())

        # should be True
        self.assertTrue(layers_copy[0].picked_for_compression)
        self.assertTrue(layers_copy[1].picked_for_compression)

        # should be False
        self.assertFalse(layers_copy[2].picked_for_compression)
        self.assertFalse(layers_copy[3].picked_for_compression)

        # op reference should be different
        self.assertNotEqual(layers[0].module, layers_copy[0].module)
        self.assertNotEqual(layers[1].module, layers_copy[1].module)
        self.assertNotEqual(layers[2].module, layers_copy[2].module)
        self.assertNotEqual(layers[3].module, layers_copy[3].module)

        # layer reference should be different
        self.assertNotEqual(layers[0], layers_copy[0])
        self.assertNotEqual(layers[1], layers_copy[1])
        self.assertNotEqual(layers[2], layers_copy[2])
        self.assertNotEqual(layers[3], layers_copy[3])

        # op name should be same for both original and copy
        self.assertEqual(layers[0].name, layers_copy[0].name)
        self.assertEqual(layers[1].name, layers_copy[1].name)
        self.assertEqual(layers[2].name, layers_copy[2].name)
        self.assertEqual(layers[3].name, layers_copy[3].name)

        # session should be different
        self.assertNotEqual(layer_db.model, layer_db_copy.model)

        # check output shapes for layers in layer_db_copy
        self.assertEqual(layers_copy[0].output_shape, [None, 32, 28, 28])
        self.assertEqual(layers_copy[1].output_shape, [None, 64, 14, 14])
        self.assertEqual(layers_copy[2].output_shape, [None, 1024, 1, 1])
        self.assertEqual(layers_copy[3].output_shape, [None, 10, 1, 1])

        # check weight shapes for layers in layer_db_copy
        self.assertEqual(layers_copy[0].weight_shape, [32, 1, 5, 5])
        self.assertEqual(layers_copy[1].weight_shape, [64, 32, 5, 5])
        self.assertEqual(layers_copy[2].weight_shape, [1024, 3136])
        self.assertEqual(layers_copy[3].weight_shape, [10, 1024])

        # check the weight elements are equal in both data bases
        self.assertTrue(np.array_equal(layers[0].module.inputs[1].eval(session=layer_db.model),
                                       layers_copy[0].module.inputs[1].eval(session=layer_db_copy.model)))
        self.assertTrue(np.array_equal(layers[1].module.inputs[1].eval(session=layer_db.model),
                                       layers_copy[1].module.inputs[1].eval(session=layer_db_copy.model)))
        self.assertTrue(np.array_equal(layers[2].module.inputs[1].eval(session=layer_db.model),
                                       layers_copy[2].module.inputs[1].eval(session=layer_db_copy.model)))
        self.assertTrue(np.array_equal(layers[3].module.inputs[1].eval(session=layer_db.model),
                                       layers_copy[3].module.inputs[1].eval(session=layer_db_copy.model)))

        tf.compat.v1.reset_default_graph()
        sess.close()
        # delete temp directory
        shutil.rmtree(str('./temp_meta/'))
    def test_per_layer_eval_scores(self):

        pruner = unittest.mock.MagicMock()
        eval_func = unittest.mock.MagicMock()

        # 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
            _ = mnist_tf_model.create_model(data_format='channels_last')
            sess.run(tf.compat.v1.global_variables_initializer())

        # Create a layer database
        layer_db = LayerDatabase(model=sess,
                                 input_shape=(1, 28, 28, 1),
                                 working_dir=None)
        layer1 = layer_db.find_layer_by_name('conv2d/Conv2D')

        layer_db.mark_picked_layers([layer1])
        eval_func.side_effect = [90, 80, 70, 60, 50, 40, 30, 20, 10]

        url, process = start_bokeh_server_session(8006)
        bokeh_session = BokehServerSession(url=url, session_id="compression")

        # Instantiate child
        greedy_algo = comp_ratio_select.GreedyCompRatioSelectAlgo(
            layer_db=layer_db,
            pruner=pruner,
            cost_calculator=SpatialSvdCostCalculator(),
            eval_func=eval_func,
            eval_iterations=20,
            cost_metric=CostMetric.mac,
            target_comp_ratio=0.5,
            num_candidates=10,
            use_monotonic_fit=True,
            saved_eval_scores_dict=None,
            comp_ratio_rounding_algo=None,
            use_cuda=False,
            bokeh_session=bokeh_session)
        progress_bar = ProgressBar(1,
                                   "eval scores",
                                   "green",
                                   bokeh_session=bokeh_session)
        data_table = DataTable(num_columns=3,
                               num_rows=1,
                               column_names=[
                                   '0.1', '0.2', '0.3', '0.4', '0.5', '0.6',
                                   '0.7', '0.8', '0.9'
                               ],
                               row_index_names=[layer1.name],
                               bokeh_session=bokeh_session)

        pruner.prune_model.return_value = layer_db
        eval_dict = greedy_algo._compute_layerwise_eval_score_per_comp_ratio_candidate(
            data_table, progress_bar, layer1)

        self.assertEqual(90, eval_dict[Decimal('0.1')])

        tf.compat.v1.reset_default_graph()
        sess.close()

        bokeh_session.server_session.close("test complete")
        os.killpg(os.getpgid(process.pid), signal.SIGTERM)
    def test_datasampling_and_reconstruction(self):
        """
        Test data sampling and reconstruction logic
        """
        tf.compat.v1.reset_default_graph()
        batch_size = 1
        input_data = np.random.rand(100, 224, 224, 3)
        dataset = tf.data.Dataset.from_tensor_slices(input_data)
        dataset = dataset.batch(batch_size=batch_size)

        orig_g = tf.Graph()

        with orig_g.as_default():

            _ = VGG16(weights=None,
                      input_shape=(224, 224, 3),
                      include_top=False)
            orig_init = tf.compat.v1.global_variables_initializer()

        input_op_names = ['input_1']
        output_op_names = ['block5_pool/MaxPool']
        # create sess with graph
        orig_sess = tf.compat.v1.Session(graph=orig_g)
        # initialize all the variables in VGG16
        orig_sess.run(orig_init)

        # create layer database
        layer_db = LayerDatabase(model=orig_sess,
                                 input_shape=(1, 224, 224, 3),
                                 working_dir=None)
        conv_layer = layer_db.find_layer_by_name('block1_conv1/Conv2D')

        comp_layer_db = copy.deepcopy(layer_db)
        comp_conv_layer = comp_layer_db.find_layer_by_name(
            'block1_conv1/Conv2D')

        # get the weights before reconstruction in original model
        before_recon_weights_orig_model = layer_db.model.run(
            conv_layer.module.inputs[1])

        # get the weights before reconstruction in pruned  model
        before_recon_weights_pruned_model = comp_layer_db.model.run(
            comp_conv_layer.module.inputs[1])

        # weight should be exactly same before reconstruction in original and pruned layer database
        self.assertTrue(
            np.array_equal(before_recon_weights_orig_model,
                           before_recon_weights_pruned_model))

        cp = InputChannelPruner(input_op_names=input_op_names,
                                output_op_names=output_op_names,
                                data_set=dataset,
                                batch_size=batch_size,
                                num_reconstruction_samples=50,
                                allow_custom_downsample_ops=True)

        num_in_channels = comp_conv_layer.weight_shape[0]
        cp._data_subsample_and_reconstruction(orig_layer=conv_layer,
                                              pruned_layer=comp_conv_layer,
                                              output_mask=[1] *
                                              num_in_channels,
                                              orig_layer_db=layer_db,
                                              comp_layer_db=comp_layer_db)

        # get the weights after reconstruction
        after_recon_weights_pruned_model = comp_layer_db.model.run(
            comp_conv_layer.module.inputs[1])

        # weight should not be same before and after reconstruction
        self.assertFalse(
            np.array_equal(before_recon_weights_orig_model,
                           after_recon_weights_pruned_model))

        layer_db.model.close()
        comp_layer_db.model.close()
        # delete temp directory
        shutil.rmtree(str('./temp_meta/'))