예제 #1
0
    def test_get_weight_parameter(self):
        """ Check whether parameters can be get from Module. """
        self.assertIsNotNone(
            model_utils.get_weight_parameter(MaskConv2d(32, 32, 3)))

        weight_groups = model_utils.get_weight_parameter(
            GroupConv2d(32, 64, 3, groups=2))
        self.assertIsNotNone(weight_groups)
        self.assertIsInstance(weight_groups, torch.Tensor)
        self.assertEqual(weight_groups.shape[0], 64)
        self.assertEqual(weight_groups.shape[1], 16)
예제 #2
0
    def find_groupable_modules(self, model, G=1, MCPG=None, g_cfg=None):
        """ Find modules that can be turned into GroupConv2d. """
        mods = []

        for name, mod in model.named_modules():
            if isinstance(mod, MaskConv2d):
                # we assume that if the number of groups is not divisible
                # it is not grouped
                # TODO: support hybrid group size
                weight = model_utils.get_weight_parameter(mod)
                F, C = weight.shape[:2]

                if not GroupConv2d.groupable(
                        C, F, groups=G, max_channels_per_group=MCPG):
                    continue

                # get the desired group number
                if g_cfg and name in g_cfg:
                    G_ = g_cfg[name]["G"]
                else:
                    G_ = GroupConv2d.get_num_groups(
                        C, F, groups=G, max_channels_per_group=MCPG)

                # we also need to check whether GSP conditions are met.
                if not model_utils.is_gsp_satisfied(mod, G_):
                    continue

                mods.append((name, mod))

        return mods
예제 #3
0
    def get_num_groups(name, mod):
        G_ = G  # choose G in the beginning

        W = model_utils.get_weight_parameter(mod)
        F, C = W.shape[:2]

        # how to override G_
        if g_cfg is not None:
            if name in g_cfg:
                G_ = g_cfg[name]["G"]
                # do some verification
                assert F == g_cfg[name]["F"] and C == g_cfg[name]["C"]
            else:
                G_ = 1  # HACK - we don't want to have G=0 in further processing

        elif MCPG > 0:
            if GroupConv2d.groupable(C, F, max_channels_per_group=MCPG):
                G_ = GroupConv2d.get_num_groups(C,
                                                F,
                                                max_channels_per_group=MCPG)
            else:
                logging.warn(
                    "Module {} is not groupable under MCPG={}, set its G to 1".
                    format(name, MCPG))
                G_ = 1

        return G_
예제 #4
0
    def prune_module(self,
                     name,
                     mod,
                     *args,
                     g_cfg=None,
                     fake_mask=False,
                     **kwargs):
        """ Prune a single module.

      We expect that after pruning, the mask in mod can be
      updated to a pruned result.
    
    Args:
      name(str): name of the module
      mod(MaskConv2d): the module to be pruned.
    """
        # TODO maybe not pass by kwargs
        G = self.args.num_groups
        if g_cfg is not None and name in g_cfg:
            G = g_cfg[name]["G"]
            # do some verification
            W = model_utils.get_weight_parameter(mod)
            F, C = W.shape[:2]
            assert F == g_cfg[name]["F"] and C == g_cfg[name]["C"]

        if fake_mask and isinstance(mod, MaskConv2d):
            mod.fake_mask = True

        prune_utils.prune_module(mod, G=G, MCPG=self.args.mcpg, **kwargs)
예제 #5
0
    def get_num_groups(name, mod):
        G_ = G  # choose G in the beginning

        W = model_utils.get_weight_parameter(mod)
        F, C = W.shape[:2]
        if not data_parallel:
            name = 'module.' + name

        # how to override G_
        if g_cfg is not None:
            if name in g_cfg:
                G_ = g_cfg[name]['G']
                # do some verification
                if G_ != 1:  # HACK
                    assert F == g_cfg[name][
                        'F'], 'F={} does not match cfg={}'.format(
                            F, g_cfg[name]['F'])
                    assert C == g_cfg[name][
                        'C'], 'C={} does not match cfg={}'.format(
                            C, g_cfg[name]['C'])
            else:
                G_ = 1  # HACK - we don't want to have G=0 in further processing

        elif MCPG > 0:
            if GroupConv2d.groupable(C, F, max_channels_per_group=MCPG):
                G_ = GroupConv2d.get_num_groups(C,
                                                F,
                                                max_channels_per_group=MCPG)
            else:
                logging.warn(
                    'Module {} is not groupable under MCPG={}, set its G to 1'.
                    format(name, MCPG))
                G_ = 1

        return G_
예제 #6
0
    def export(self):
        """ Export function """
        # load the original model
        model = self.load_model(self.args)
        self.evaluate(model)

        # all these modules can be further converted to GroupConv2d
        G = self.args.num_groups
        MCPG = self.args.mcpg
        g_cfg = self.load_group_cfg(self.args)

        logging.info("Finding all groupable modules ...")
        mods = self.find_groupable_modules(model, G=G, MCPG=MCPG, g_cfg=g_cfg)
        logging.info("Found {} modules".format(len(mods)))

        # for each module, create corresponding group convolution
        # parameters, and use them to update the state_dict
        logging.info("Generating GroupConv2d parameters ...")
        state_dict = torch.load(self.args.resume)["state_dict"]
        for name, mod in mods:
            weight = model_utils.get_weight_parameter(mod)
            F, C = weight.shape[:2]

            # will update G correspondingly
            # TODO put this logic somewhere else, frequently reused
            if g_cfg and name in g_cfg:
                G_ = g_cfg[name]["G"]
            else:
                G_ = GroupConv2d.get_num_groups(C, F, MCPG, groups=G)

            wg, ind_in, ind_out = model_utils.get_group_parameters(mod, G_)

            # update the state_dict
            del state_dict[name + ".mask"]  # delete mask key

            # purge weight in MaskConv2d
            if (name + ".weight") in state_dict:
                del state_dict[name + ".weight"]

            state_dict[name + ".conv2d.weight"] = torch.from_numpy(wg)
            state_dict[name + ".ind_in"] = torch.from_numpy(ind_in).long()
            state_dict[name + ".ind_out"] = torch.from_numpy(ind_out).long()

        # create model
        # leave indices to None, will set up when loading state_dict
        model = self.create_model(self.args,
                                  groups=G,
                                  max_channels_per_group=MCPG,
                                  mask=False)
        if g_cfg:  # should post update
            self.update_model_by_group_cfg(model, g_cfg)
        # insert weights
        model.load_state_dict(state_dict)

        self.evaluate(model)
예제 #7
0
  def find_groupable_modules(self, model):
    """ Find modules that can be grouped. """
    mods = []  # return list
    for name, mod in model.named_modules():
      if isinstance(mod, MaskConv2d):
        W = model_utils.get_weight_parameter(mod)
        F, C = W.shape[:2]
        ff, fc = factors(F), factors(C)

        if len(ff.intersection(fc)) <= 1:
          continue

        mods.append((name, mod))
    return mods
예제 #8
0
    def prune_module(self, name, mod, G, **kwargs):
        """ Prune a specific module.
            NOTE: G is known at this moment.
        """
        assert isinstance(mod, MaskConv2d)
        assert G >= 1, "{} has G={} smaller than 1".format(name, G)

        W = model_utils.get_weight_parameter(mod)
        C_out, C_in = W.shape[:2]

        if G == 1 or (C_out % G != 0 or C_in % G != 0):
            # NOTE: we return if this module cannot be pruned
            return

        prune_utils.prune_module(mod, G=G, **kwargs)
예제 #9
0
  def find_group_candidates(self, mod, **kwargs):
    """ Find group number candidates in module.
    
    Note: use kwargs to pass additional requirements.
    """
    W = model_utils.get_weight_parameter(mod)
    F, C = W.shape[:2]

    # common divisors
    Gs = list(sorted(factors(F).intersection(factors(C))))
    del Gs[0]  # should be 1

    costs = []
    for G in Gs:
      _, _, cost = mask_utils.run_mbm(W, G)
      costs.append(cost)

    return Gs, costs
예제 #10
0
  def run_opt(self):
    """ Run the actual optimization """
    logging.info('Finding the optimal group configuration ...')

    model = self.load_model(self.args)

    g_conf = OrderedDict()
    for idx, (name, mod) in enumerate(self.find_groupable_modules(model)):
      # the sequence here is important
      Gs, costs = list(self.find_group_candidates(mod))

      W = model_utils.get_weight_parameter(mod)
      F, C = W.shape[:2]
      # TODO: simply select the one with the most cost
      G = Gs[np.argmax(costs)]
      # print(np.max(costs) / (F * C))

      # update the dictionary
      g_conf[name] = {'id': idx, 'F': F, 'C': C, 'G': G}

    return g_conf