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_reducing_vgg16_slim(self): """ Test reducing vgg16 slim model """ tf.compat.v1.reset_default_graph() sess = tf.compat.v1.Session() module_zero_channels_list = [] inp = tf.compat.v1.placeholder(tf.float32, [1, 224, 224, 3]) _ = vgg.vgg_16(inp) init = tf.compat.v1.global_variables_initializer() sess.run(init) input_op_names = ["Placeholder"] output_op_names = ['vgg_16/fc8/squeezed'] tf_op = tf.compat.v1.get_default_graph().get_operation_by_name( "vgg_16/fc7/Conv2D") input_channels_to_winnow = [2, 3, 4] module_mask_pair = (tf_op, input_channels_to_winnow) module_zero_channels_list.append(module_mask_pair) new_sess, ordered_modules_list = winnow.winnow_tf_model( sess, input_op_names, output_op_names, module_zero_channels_list, reshape=True, in_place=True, verbose=True) # Save and reload modified graph to allow changes to take effect new_sess = save_and_load_graph('./saver', new_sess) # _ = tf.compat.v1.summary.FileWriter('./reduced_graph', new_sess.graph) with new_sess.graph.as_default(): inp = tf.random.uniform(shape=(1, 224, 224, 3)) inp_array = inp.eval(session=new_sess) model_input = new_sess.graph.get_tensor_by_name("Placeholder:0") model_output = new_sess.graph.get_tensor_by_name( "vgg_16/fc8/squeezed:0") # run through entire model to check no error is produced _ = new_sess.run(model_output, feed_dict={model_input: inp_array}) self.assertEqual(4, len(ordered_modules_list)) new_sess.close() sess.close()
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
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
def test_reducing_vgg16(self): """ This test winnows a VGG16 model""" tf.compat.v1.reset_default_graph() sess = tf.compat.v1.Session() module_zero_channels_list = [] _ = VGG16(weights=None) init = tf.compat.v1.global_variables_initializer() sess.run(init) tf_op = tf.compat.v1.get_default_graph().get_operation_by_name( "block5_conv1/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) tf_op_2 = tf.compat.v1.get_default_graph().get_operation_by_name( "block3_conv1/Conv2D") input_channels_to_winnow_2 = [11, 13, 15, 17] module_mask_pair_2 = (tf_op_2, input_channels_to_winnow_2) module_zero_channels_list.append(module_mask_pair_2) tf_op_3 = tf.compat.v1.get_default_graph().get_operation_by_name( "block2_conv2/Conv2D") input_channels_to_winnow_3 = [1, 2, 3, 4, 5] module_mask_pair_3 = (tf_op_3, input_channels_to_winnow_3) module_zero_channels_list.append(module_mask_pair_3) tf_op_4 = tf.compat.v1.get_default_graph().get_operation_by_name( "block2_conv1/Conv2D") input_channels_to_winnow_4 = [20, 21, 22, 23] module_mask_pair_4 = (tf_op_4, input_channels_to_winnow_4) module_zero_channels_list.append(module_mask_pair_4) input_op_names = ["input_1"] output_op_names = ['predictions/Softmax'] new_sess, ordered_modules_list = winnow.winnow_tf_model( sess, input_op_names, output_op_names, module_zero_channels_list, reshape=True, in_place=True, verbose=True) # Save and reload modified graph to allow changes to take effect new_sess = save_and_load_graph('./saver', new_sess) # uncomment the following to generate tensorboard viewable file # _ = tf.compat.v1.summary.FileWriter('./reduced_graph', new_sess.graph) # Check certain weight indices to ensure that weights were reduced correctly b4c3_kernel = new_sess.graph.get_tensor_by_name( "block4_conv3/kernel/Read/" "ReadVariableOp:0").eval(session=new_sess) red_b4c3_kernel = new_sess.graph.get_tensor_by_name( "reduced_block4_conv3/kernel/" "Read/ReadVariableOp:0").eval(session=new_sess) self.assertEqual(red_b4c3_kernel.shape, (3, 3, 512, 509)) self.assertEqual(red_b4c3_kernel[0][0][0][2], b4c3_kernel[0][0][0][2]) self.assertEqual(np.sum(red_b4c3_kernel[0][0][0][5:]), np.sum(b4c3_kernel[0][0][0][8:])) # Test that evaluating the new session uses the newly reduced modules. # Do so by first evaluating a tensor in a module coming after a set of reduced modules. # Zero out weights and biases of one of the original unreduced modules preceding the tensor. # Reevaluate the tensor and expect to see no change, since the original unreduced module should not be used # anymore. # Then zero out weights and biases of one of the newly reduced modules. # Finally reevaluate the same tensor as before. This time, we expect to see the result be zero. with new_sess.graph.as_default(): inp = tf.random.uniform(shape=(1, 224, 224, 3)) inp_array = inp.eval(session=new_sess) model_input = new_sess.graph.get_tensor_by_name("input_1:0") # run through entire model to check no error is produced model_output = new_sess.graph.get_tensor_by_name( "predictions/Softmax:0") _ = new_sess.run(model_output, feed_dict={model_input: inp_array}) self.assertEqual(13, len(ordered_modules_list)) new_sess.close() sess.close()
def test_reducing_inceptionV3(self): """ Test module reduction in inceptionV3 """ tf.compat.v1.reset_default_graph() sess = tf.compat.v1.Session() module_zero_channels_list = [] _ = InceptionV3(weights=None) init = tf.compat.v1.global_variables_initializer() sess.run(init) tf_op = tf.compat.v1.get_default_graph().get_operation_by_name( "conv2d_12/Conv2D") input_channels_to_winnow = [0, 1, 64, 128, 224] module_mask_pair = (tf_op, input_channels_to_winnow) module_zero_channels_list.append(module_mask_pair) tf_op = tf.compat.v1.get_default_graph().get_operation_by_name( "conv2d_13/Conv2D") input_channels_to_winnow_1 = [0, 64, 65, 66, 128, 224] module_mask_pair = (tf_op, input_channels_to_winnow_1) module_zero_channels_list.append(module_mask_pair) tf_op = tf.compat.v1.get_default_graph().get_operation_by_name( "conv2d_15/Conv2D") input_channels_to_winnow_2 = [0, 64, 128, 129, 130, 131, 224] module_mask_pair = (tf_op, input_channels_to_winnow_2) module_zero_channels_list.append(module_mask_pair) tf_op = tf.compat.v1.get_default_graph().get_operation_by_name( "conv2d_18/Conv2D") input_channels_to_winnow_3 = [0, 64, 128, 224, 225, 226, 227, 228] module_mask_pair = (tf_op, input_channels_to_winnow_3) module_zero_channels_list.append(module_mask_pair) input_op_names = ["input_1"] output_op_names = ['predictions/Softmax'] new_sess, ordered_modules_list = winnow.winnow_tf_model( sess, input_op_names, output_op_names, module_zero_channels_list, reshape=True, in_place=True, verbose=True) # 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 new_sess.graph.as_default(): initialize_uninitialized_vars(new_sess) new_sess = save_and_load_graph('./saver', new_sess) # _ = tf.compat.v1.summary.FileWriter('./reduced_graph', new_sess.graph) with new_sess.graph.as_default(): inp = tf.random.uniform(shape=(1, 299, 299, 3)) inp_array = inp.eval(session=new_sess) model_input = new_sess.graph.get_tensor_by_name("input_1:0") model_output = new_sess.graph.get_tensor_by_name( "predictions/Softmax:0") # check that reduced tensor shapes are as expected reduced_conv2d_12_input = new_sess.graph.get_operation_by_name( "reduced_conv2d_12/Conv2D").inputs[0] reduced_conv2d_13_input = new_sess.graph.get_operation_by_name( "reduced_conv2d_13/Conv2D").inputs[0] reduced_conv2d_15_input = new_sess.graph.get_operation_by_name( "reduced_conv2d_15/Conv2D").inputs[0] reduced_conv2d_18_input = new_sess.graph.get_operation_by_name( "reduced_conv2d_18/Conv2D").inputs[0] reduced_conv2d_5_output = new_sess.graph.get_tensor_by_name( "reduced_conv2d_5/Conv2D:0") reduced_conv2d_7_output = new_sess.graph.get_tensor_by_name( "reduced_conv2d_7/Conv2D:0") reduced_conv2d_10_output = new_sess.graph.get_tensor_by_name( "reduced_conv2d_10/Conv2D:0") reduced_conv2d_11_output = new_sess.graph.get_tensor_by_name( "reduced_conv2d_11/Conv2D:0") self.assertEqual(251, reduced_conv2d_12_input.shape.as_list()[-1]) self.assertEqual(250, reduced_conv2d_13_input.shape.as_list()[-1]) self.assertEqual(249, reduced_conv2d_15_input.shape.as_list()[-1]) self.assertEqual(248, reduced_conv2d_18_input.shape.as_list()[-1]) self.assertEqual(63, reduced_conv2d_5_output.shape.as_list()[-1]) self.assertEqual(63, reduced_conv2d_7_output.shape.as_list()[-1]) self.assertEqual(95, reduced_conv2d_10_output.shape.as_list()[-1]) self.assertEqual(31, reduced_conv2d_11_output.shape.as_list()[-1]) self.assertEqual(17, len(ordered_modules_list)) # run through entire model to check no error is produced _ = new_sess.run(model_output, feed_dict={model_input: inp_array}) new_sess.close() sess.close()
def test_reducing_resnet_50(self): """ Test module reduction in resnet_50 """ tf.compat.v1.reset_default_graph() sess = tf.compat.v1.Session() module_zero_channels_list = [] _ = ResNet50(weights=None) init = tf.compat.v1.global_variables_initializer() sess.run(init) tf_op = tf.compat.v1.get_default_graph().get_operation_by_name( "conv2_block1_1_conv/Conv2D") input_channels_to_winnow_1 = [3, 5, 7] module_mask_pair = (tf_op, input_channels_to_winnow_1) module_zero_channels_list.append(module_mask_pair) tf_op = tf.compat.v1.get_default_graph().get_operation_by_name( "conv2_block1_0_conv/Conv2D") input_channels_to_winnow_2 = [3, 5, 7, 8] module_mask_pair = (tf_op, input_channels_to_winnow_2) module_zero_channels_list.append(module_mask_pair) tf_op = tf.compat.v1.get_default_graph().get_operation_by_name( "conv3_block1_1_conv/Conv2D") input_channels_to_winnow_3 = [3, 5, 7] module_mask_pair = (tf_op, input_channels_to_winnow_3) module_zero_channels_list.append(module_mask_pair) tf_op = tf.compat.v1.get_default_graph().get_operation_by_name( "conv3_block1_0_conv/Conv2D") input_channels_to_winnow_4 = [3, 5, 7, 8] module_mask_pair = (tf_op, input_channels_to_winnow_4) module_zero_channels_list.append(module_mask_pair) input_op_names = ["input_1"] output_op_names = ['probs/Softmax'] new_sess, ordered_modules_list = winnow.winnow_tf_model( sess, input_op_names, output_op_names, module_zero_channels_list, reshape=True, in_place=True, verbose=True) # 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 new_sess.graph.as_default(): initialize_uninitialized_vars(new_sess) new_sess = save_and_load_graph('./saver', new_sess) # _ = tf.compat.v1.summary.FileWriter('./reduced_graph', new_sess.graph) with new_sess.graph.as_default(): inp = tf.random.uniform(shape=(1, 224, 224, 3)) inp_array = inp.eval(session=new_sess) model_input = new_sess.graph.get_tensor_by_name("input_1:0") model_output = new_sess.graph.get_tensor_by_name("probs/Softmax:0") # check that reduced tensor shapes are as expected reduced_conv3_block1_1_input = new_sess.graph.get_operation_by_name( "reduced_conv3_block1_1_conv/" "Conv2D").inputs[0] reduced_conv3_block1_0_input = new_sess.graph.get_operation_by_name( "reduced_conv3_block1_0_conv/" "Conv2D").inputs[0] reduced_conv2_block3_3_output = new_sess.graph.get_tensor_by_name( "reduced_conv2_block3_3_conv/" "Conv2D:0") reduced_conv2_block1_1_input = new_sess.graph.get_operation_by_name( "reduced_conv2_block1_1_conv/" "Conv2D").inputs[0] reduced_conv2_block1_0_input = new_sess.graph.get_operation_by_name( "reduced_conv2_block1_0_conv/" "Conv2D").inputs[0] reduced_conv1_output = new_sess.graph.get_tensor_by_name( "reduced_conv1_conv/Conv2D:0") self.assertEqual(253, reduced_conv3_block1_1_input.shape.as_list()[-1]) self.assertEqual(252, reduced_conv3_block1_0_input.shape.as_list()[-1]) self.assertEqual(253, reduced_conv2_block3_3_output.shape.as_list()[-1]) self.assertEqual(61, reduced_conv2_block1_1_input.shape.as_list()[-1]) self.assertEqual(60, reduced_conv2_block1_0_input.shape.as_list()[-1]) self.assertEqual(61, reduced_conv1_output.shape.as_list()[-1]) # run through entire model to check no error is produced _ = new_sess.run(model_output, feed_dict={model_input: inp_array}) self.assertEqual(11, len(ordered_modules_list)) new_sess.close() sess.close()