Esempio n. 1
0
def visualize_weight_ranges(model: torch.nn.Module, visualization_url: str,
                            selected_layers: List = None,
                            session_id: str = "optimization",
                            display: bool = True):
    """
    Visualizes weight ranges for each layer through a scatter plot showing mean plotted against the standard deviation,
    the minimum plotted against the max, and a line plot with min, max, and mean for each output channel.
    Visualization_url is in the form: http://<host name>:<port number>/

    :param model: pytorch model
    :param visualization_url: user inputted url with session id set as optimization for the visualizations.
    :param selected_layers:  a list of layers a user can choose to have visualized. If selected layers is None,
        all Linear and Conv layers will be visualized.
    :param session_id : custom string descriptor to be associated with this session, appended to visualization url.
    :param display: a bool variable to indicate if output is to be displayed immediately.
    :return: None
    """

    bokeh_session = BokehServerSession(url=visualization_url, session_id=session_id, display=display)
    server_document = bokeh_session.document
    if selected_layers:
        for name, module in model.named_modules():
            if name in selected_layers and hasattr(module, "weight"):
                plot = plotting_utils.visualize_weight_ranges_single_layer(module, name)
                server_document.add_root(plot)
    else:
        for name, module in model.named_modules():
            if hasattr(module, "weight") and isinstance(module,
                                                        (torch.nn.modules.conv.Conv2d, torch.nn.modules.linear.Linear)):
                plot = plotting_utils.visualize_weight_ranges_single_layer(module, name)
                server_document.add_root(plot)

    # returns bokeh session object, mostly for testing purposes, so the session can be closed in a test case.
    return bokeh_session
Esempio n. 2
0
def visualize_relative_weight_ranges_single_layer(sess, layer,
                                                  visualization_url):
    """

    Publishes a line plot showing  weight ranges for each layer, summary statistics
    for relative weight ranges, and a histogram showing weight ranges of output channels

    :param sess: tf.compat.v1.Session
    :param layer: layer with weights
    :param visualization_url: user inputted url with session id set as optimization for the visualizations
    :return: bokeh_session, so that user can close the session

    """

    # pylint: disable=too-many-locals
    bokeh_session = BokehServerSession(url=visualization_url,
                                       session_id="optimization")
    server_document = bokeh_session.document
    layer_weights_data_frame = pd.DataFrame(get_weights(layer,
                                                        sess)).describe().T
    layer_name = layer.name
    plot = line_plot_summary_statistics_model(layer_name,
                                              layer_weights_data_frame,
                                              width=1150,
                                              height=700)

    # list of problematic output channels, data frame containing magnitude of range in each output channel
    problematic_output_channels, output_channel_ranges_data_frame = identify_problematic_output_channels(
        layer_weights_data_frame)

    histogram_plot = histogram(
        output_channel_ranges_data_frame,
        "relative range",
        75,
        x_label="Weight Range Relative to Smallest Output Channel",
        y_label="Count",
        title="Relative Ranges For All Output Channels")
    output_channel_ranges_data_frame = output_channel_ranges_data_frame.describe(
    ).T.to_frame()
    output_channel_ranges_data_frame = output_channel_ranges_data_frame.drop(
        "count")

    output_channel_ranges_as_column_data_source = convert_pandas_data_frame_to_bokeh_data_table(
        output_channel_ranges_data_frame)

    # add vertical lines to highlight problematic channels
    for channel in problematic_output_channels:
        add_vertical_line_to_figure(channel, plot)

    # push plot to server document
    column_layout = column(histogram_plot,
                           output_channel_ranges_as_column_data_source)
    layout = row(plot, column_layout)
    layout_with_title = add_title(layout, layer_name)
    server_document.add_root(layout_with_title)
    return bokeh_session
Esempio n. 3
0
 def test_progress_bar(self):
     visualization_url, process = start_bokeh_server_session(8002)
     bokeh_session = BokehServerSession(url=visualization_url,
                                        session_id="test")
     progress_bar = ProgressBar(total=10,
                                bokeh_session=bokeh_session,
                                title="testing",
                                color="green")
     for i in range(10):
         progress_bar.update()
     progress_bar.update()
     self.assertEqual(progress_bar.calculate_percentage_complete(), 100.0)
     bokeh_session.server_session.close("test complete")
     os.killpg(os.getpgid(process.pid), signal.SIGTERM)
    def test_per_layer_eval_scores(self):

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

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

        model = mnist_torch_model.Net().to('cpu')

        layer_db = LayerDatabase(model, input_shape=(1, 1, 28, 28))
        layer1 = layer_db.find_layer_by_name('conv1')
        layer_db.mark_picked_layers([layer1])

        eval_func.side_effect = [90, 80, 70, 60, 50, 40, 30, 20, 10]

        # Instantiate child
        greedy_algo = comp_ratio_select.GreedyCompRatioSelectAlgo(
            layer_db,
            pruner,
            SpatialSvdCostCalculator(),
            eval_func,
            20,
            CostMetric.mac,
            0.5,
            10,
            True,
            None,
            None,
            False,
            bokeh_session=None)
        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')])
        bokeh_session.server_session.close("test complete")
        os.killpg(os.getpgid(process.pid), signal.SIGTERM)
Esempio n. 5
0
    def test__invoke_progress_bar(self):
        visualization_url, process = start_bokeh_server_session(8002)
        bokeh_session = BokehServerSession(url=visualization_url,
                                           session_id="test")
        # import pickle
        progress_bar = ProgressBar(80,
                                   title="Some Title Goes Here",
                                   color="green",
                                   bokeh_session=bokeh_session)

        for i in range(80):
            progress_bar.update()
        progress_bar.update()
        bokeh_session.server_session.close("test complete")
        os.killpg(os.getpgid(process.pid), signal.SIGTERM)
    def test_cross_layer_equalization_mobilenet_v2_visualize_after_optimization(
            self):
        bokeh_visualizations_url, process = start_bokeh_server_session(8006)
        torch.manual_seed(10)
        model = MobileNetV2().to(torch.device('cpu'))
        bokeh_session = BokehServerSession(bokeh_visualizations_url,
                                           session_id="cle")
        model = model.eval()
        model_copy = copy.deepcopy(model)

        # model_copy_again = copy.deepcopy(model)
        batch_norm_fold.fold_all_batch_norms(model_copy, (1, 3, 224, 224))
        equalize_model(model, (1, 3, 224, 224))
        visualize_model.visualize_changes_after_optimization(
            model_copy, model, bokeh_visualizations_url)
        bokeh_session.server_session.close("test complete")
        os.killpg(os.getpgid(process.pid), signal.SIGTERM)
Esempio n. 7
0
    def test_show_zoomed_in_plot_from_start(self):
        visualization_url, process = start_bokeh_server_session(8002)
        bokeh_session = BokehServerSession(url=visualization_url,
                                           session_id="test")
        layout = bokeh_plots.PlotsLayout()
        from bokeh.models import Range1d

        # create a new plot with a range set with a tuple
        p = figure(plot_width=400, plot_height=400, x_range=(0, 20))

        # set a range using a Range1d
        p.y_range = Range1d(0, 15)

        p.circle([1, 2, 3, 4, 5, 25], [2, 5, 8, 2, 7, 50], size=10)
        # r = row(p)
        layout.layout = p
        # layout.add_row(p)
        layout.complete_layout()
        bokeh_session.server_session.close("test complete")
        os.killpg(os.getpgid(process.pid), signal.SIGTERM)
Esempio n. 8
0
def visualize_changes_after_optimization(old_model: torch.nn.Module, new_model: torch.nn.Module,
                                         visualization_url: str, selected_layers: List = None,
                                         session_id: str = "optimization",
                                         display: bool = True):
    """
    Visualizes changes before and after some optimization has been applied to a model.
    Visualization_url is in the form: http://<host name>:<port number>/

    :param old_model: pytorch model before optimization
    :param new_model: pytorch model after optimization
    :param visualization_url: user inputted url with session id set as optimization for the visualizations.
    :param selected_layers: a list of layers a user can choose to have visualized. If selected layers is None,
        all Linear and Conv layers will be visualized.
    :param session_id : custom string descriptor to be associated with this session, appended to visualization url.
    :param display: a flag to indicate if output is to be displayed immediately.
    :return: None
    """

    bokeh_session = BokehServerSession(url=visualization_url, session_id=session_id, display=display)
    server_document = bokeh_session.document
    if selected_layers:
        for name, module in new_model.named_modules():
            if name in selected_layers and hasattr(module, "weight"):
                old_model_module = get_layer_by_name(old_model, name)
                new_model_module = module
                plot = plotting_utils.visualize_changes_after_optimization_single_layer(name, old_model_module,
                                                                                        new_model_module)
                server_document.add_root(plot)

    else:
        for name, module in new_model.named_modules():
            if hasattr(module, "weight") and isinstance(module,
                                                        (torch.nn.modules.conv.Conv2d, torch.nn.modules.linear.Linear)):
                old_model_module = get_layer_by_name(old_model, name)
                new_model_module = module
                plot = plotting_utils.visualize_changes_after_optimization_single_layer(name, old_model_module,
                                                                                        new_model_module)
                server_document.add_root(plot)

    # returns bokeh session object, mostly for testing purposes, so the session can be closed in a test case.
    return bokeh_session
Esempio n. 9
0
def visualize_relative_weight_ranges_to_identify_problematic_layers(model: torch.nn.Module, visualization_url: str,
                                                                    selected_layers: List = None,
                                                                    session_id: str = "optimization",
                                                                    display: bool = True):
    """
    For each of the selected layers, publishes a line plot showing  weight ranges for each layer, summary statistics
    for relative weight ranges, and a histogram showing weight ranges of output channels
    with respect to the minimum weight range.
    Visualization_url is in the form: http://<host name>:<port number>/

    :param model: pytorch model
    :param visualization_url: user provided url with session id for the visualizations.
    :param selected_layers: a list of layers a user can choose to have visualized. If selected layers is None,
        all Linear and Conv layers will be visualized.
    :param session_id : custom string descriptor to be associated with this session, appended to visualization url.
    :param display: a bool variable to indicate if output is to be displayed immediately.
    :return: None
    """

    bokeh_session = BokehServerSession(url=visualization_url, session_id=session_id, display=display)
    server_document = bokeh_session.document
    # layer name -> module weights data frame mapping
    if not selected_layers:
        for name, module in model.named_modules():
            if hasattr(module, "weight") and isinstance(module,
                                                        (torch.nn.modules.conv.Conv2d,
                                                         torch.nn.modules.linear.Linear)):
                plot = plotting_utils.visualize_relative_weight_ranges_single_layer(module, name)
                server_document.add_root(plot)
    else:
        for name, module in model.named_modules():
            if hasattr(module, "weight") and isinstance(module,
                                                        (torch.nn.modules.conv.Conv2d,
                                                         torch.nn.modules.linear.Linear)) and name in selected_layers:
                plot = plotting_utils.visualize_relative_weight_ranges_single_layer(module, name)
                server_document.add_root(plot)

    return bokeh_session
Esempio n. 10
0
def visualize_weight_ranges_single_layer(sess, layer, visualization_url):
    """
    Given a layer, visualizes weight ranges with scatter plots and line plots

    :param sess: tf.compat.v1.Session
    :param layer: layer with weights
    :param visualization_url: user inputted url with session id set as optimization for the visualizations
    :return: bokeh_session, so that user can close the session
    """

    bokeh_session = BokehServerSession(url=visualization_url,
                                       session_id="optimization")
    document = bokeh_session.document
    layer_weights = pd.DataFrame(get_weights(layer, sess))
    layer_name = layer.name
    layer_weights_summary_statistics = layer_weights.describe().T

    scatter_plot_mean, scatter_plot_min = scatter_plot_summary_stats(
        layer_weights_summary_statistics,
        x_axis_label_mean="Mean Weights Per Output Channel",
        y_axis_label_mean="Std Per Output Channel",
        title_mean="Mean vs Standard Deviation: " + layer_name,
        x_axis_label_min="Min Weights Per Output Channel",
        y_axis_label_min="Max Weights Per Output Channel",
        title_min="Minimum vs Maximum: " + layer_name)

    scatter_plots_layout = row(scatter_plot_mean, scatter_plot_min)
    line_plots = line_plot_summary_statistics_model(
        layer_name=layer_name,
        layer_weights_data_frame=layer_weights_summary_statistics,
        width=1500,
        height=700)
    layout = column(scatter_plots_layout, line_plots)
    layout_with_title = add_title(layout, layer_name)
    document.add_root(layout_with_title)
    return bokeh_session
    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_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)
    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)
Esempio n. 14
0
 def __init__(self, visualization_url):
     self.bokeh_session = BokehServerSession(visualization_url,
                                             session_id="compression")
     self.__document = self.bokeh_session.document
Esempio n. 15
0
    def compress_model(
        sess: tf.compat.v1.Session,
        working_dir: str,
        eval_callback: EvalFunction,
        eval_iterations,
        input_shape: Union[Tuple, List[Tuple]],
        compress_scheme: CompressionScheme,
        cost_metric: CostMetric,
        parameters: Union[SpatialSvdParameters, ChannelPruningParameters],
        trainer=None,
        visualization_url=None
    ) -> Tuple[tf.compat.v1.Session, CompressionStats]:
        """
        Compress a given model using the specified parameters

        :param sess: Model, represented by a tf.compat.v1.Session, to compress
        :param working_dir: File path to save compressed TensorFlow meta file
        :param eval_callback:  Evaluation callback. Expected signature is evaluate(model, iterations, use_cuda).
                               Expected to return an accuracy metric.
        :param eval_iterations: Iterations to run evaluation for
        :param trainer: Training Class: Contains a callable, train_model, which takes model, layer which is being fine
                        tuned and an optional parameter train_flag as a parameter
                        None: If per layer fine tuning is not required while creating the final compressed model
        :param input_shape: tuple or list of tuples of input shapes to the model (channels_last format)
        :param compress_scheme: Compression scheme. See the enum for allowed values
        :param cost_metric: Cost metric to use for the compression-ratio (either mac or memory)
        :param parameters: Compression parameters specific to given compression scheme
        :param trainer: Training function
                        None: If per layer fine tuning is not required while creating the final compressed model
        :param visualization_url: url the user will need to input where visualizations will appear
        :return: A tuple of the compressed model session, and compression statistics
        """

        # If no url is passed in, then do not create a bokeh server session
        if not visualization_url:
            bokeh_session = None
        else:
            # create a bokeh session to publish visualizations to the server document for compression
            bokeh_session = BokehServerSession(url=visualization_url,
                                               session_id="compression")

        if parameters.multiplicity < 1:
            raise ValueError('Rounding Multiplicity should be greater than 1')

        if compress_scheme == CompressionScheme.spatial_svd:
            # wrapper_func saves and reloads the graph before evaluation
            # In TF after making changes to the graph you must save and reload, then evaluate
            eval_callback = wrapper_func(eval_callback)

            algo = CompressionFactory.create_spatial_svd_algo(
                sess, working_dir, eval_callback, eval_iterations, input_shape,
                cost_metric, parameters, bokeh_session)
        elif compress_scheme == CompressionScheme.channel_pruning:
            algo = CompressionFactory.create_channel_pruning_algo(
                sess, working_dir, eval_callback, input_shape, eval_iterations,
                cost_metric, parameters, bokeh_session)
        else:
            raise ValueError(
                "Compression scheme not supported: {}".format(compress_scheme))

        compressed_layer_db, stats = algo.compress_model(cost_metric, trainer)

        # TODO: this is a temporary fix, needs to be resolved
        # In TF after making changes to the graph you must save and reload, then evaluate
        updated_model = save_and_load_graph('./saver',
                                            compressed_layer_db.model)
        compressed_layer_db.model.close()

        return updated_model, stats
Esempio n. 16
0
    def compress_model(
            model: torch.nn.Module,
            eval_callback: EvalFunction,
            eval_iterations,
            input_shape: Tuple,
            compress_scheme: CompressionScheme,
            cost_metric: CostMetric,
            parameters: Union[SpatialSvdParameters, WeightSvdParameters,
                              ChannelPruningParameters],
            trainer=None,
            visualization_url=None
    ) -> Tuple[torch.nn.Module, CompressionStats]:
        """
        Compress a given model using the specified parameters

        :param model: Model to compress
        :param eval_callback:  Evaluation callback. Expected signature is evaluate(model, iterations, use_cuda).
                               Expected to return an accuracy metric.
        :param eval_iterations: Iterations to run evaluation for
        :param trainer: Training Class: Contains a callable, train_model, which takes model, layer which is being fine
                        tuned and an optional parameter train_flag as a parameter
                        None: If per layer fine tuning is not required while creating the final compressed model
        :param input_shape: Shape of the input tensor for model
        :param compress_scheme: Compression scheme. See the enum for allowed values
        :param cost_metric: Cost metric to use for the compression-ratio (either mac or memory)
        :param parameters: Compression parameters specific to given compression scheme
        :param visualization_url: url the user will need to input where visualizations will appear
        :return: A tuple of the compressed model, and compression statistics
        """
        # pylint:disable=too-many-arguments
        # If no url is passed in, then do not create a bokeh server session
        if not visualization_url:
            bokeh_session = None
        else:
            # create a bokeh session to publish visualizations to the server document for compression
            bokeh_session = BokehServerSession(url=visualization_url,
                                               session_id="compression")

        # put model in eval mode. This is important because otherwise running a forward pass can change module buffers
        # e.g. for batchnorm layers that can affect model evaluation results
        if trainer is not None:
            trainer.train_model(model, model, train_flag=False)

        model = model.eval()

        if parameters.multiplicity < 1:
            raise ValueError('Rounding Multiplicity should be greater than 1')

        if compress_scheme == CompressionScheme.spatial_svd:
            algo = CompressionFactory.create_spatial_svd_algo(
                model, eval_callback, eval_iterations, input_shape,
                cost_metric, parameters, bokeh_session)

        elif compress_scheme == CompressionScheme.weight_svd:
            algo = CompressionFactory.create_weight_svd_algo(
                model, eval_callback, eval_iterations, input_shape,
                cost_metric, parameters, bokeh_session)

        elif compress_scheme == CompressionScheme.channel_pruning:
            algo = CompressionFactory.create_channel_pruning_algo(
                model, eval_callback, eval_iterations, input_shape,
                cost_metric, parameters, bokeh_session)

        else:
            raise ValueError(
                "Compression scheme not supported: {}".format(compress_scheme))

        compressed_layer_db, stats = algo.compress_model(cost_metric, trainer)
        return compressed_layer_db.model, stats