Beispiel #1
0
    def eval_model(cls,
                   model,
                   dataloader,
                   attack=get_attack({"attack_type": "no_attack"}),
                   loss_func='default'):
        """
        Evaluate the performance of the model on the train/test sets.
        :param model: the model that will be evaluated.
        :param dataloader: trainset or testset on which the evaluation will executed.
        :return: the loss and accuracy on the testset.
        """
        model.eval()
        loss = 0
        correct = 0
        # with torch.no_grad():
        for iter_num, (data, labels) in enumerate(dataloader):
            print("eval_model iter_num: {}".format(iter_num))
            data, labels = TorchUtils.to_device(data), TorchUtils.to_device(
                labels)
            # with torch.enable_grad():
            data = attack.create_adversarial_sample(data, labels)
            outputs, batch_loss = cls.__forward_pass(model, data, labels,
                                                     loss_func)
            loss += float(batch_loss.detach_()) * len(
                data)  # loss sum for all the batch
            _, predicted = torch.max(outputs.detach_().data, 1)
            correct += (predicted == labels).sum().item()
            if (predicted == labels).sum().item() == 1:
                print("correct prediction in iter_num: {}".format(iter_num))

        acc = correct / len(dataloader.dataset)
        loss /= len(dataloader.dataset)
        return loss, acc
Beispiel #2
0
 def eval_batch(self, data, labels, enable_grad: bool = True):
     """
     :param data: the data to evaluate
     :param labels: the labels of the data
     :param enable_grad: Should grad be enabled for later differentiation
     :param model_output_type: "logits" if model output logits or "prob"
     :param loss_func: The loss function used to calculate the loss
     :return: batch loss, probability and label prediction.
     """
     loss_func = torch.nn.NLLLoss(reduction='none')
     self.eval()
     with torch.set_grad_enabled(enable_grad):
         data, labels = TorchUtils.to_device(data), TorchUtils.to_device(
             labels)
         output = self.__call__(data)
         if not self.pnml_model:  # Output is logits
             prob = torch.softmax(output, 1)
             loss = loss_func(
                 torch.log_softmax(output, 1),
                 labels)  # log soft-max is more numerically stable
         else:  # Output is probability
             prob = output
             loss = loss_func(torch.log(output), labels)
         # _, predicted_label = torch.max(output.data, 1)
     return loss, prob, self.get_genie_prob()
Beispiel #3
0
def eval_adversarial_dataset(model,
                             dataloader,
                             attack,
                             save_adv_sample: bool = False):
    """
    Evaluate model performance on dataloader with attack
    :param model:
    :param dataloader:
    :param attack:
    :param save_adv_sample:
    :return:
    """
    try:
        torch.cuda.reset_max_memory_allocated()
    except:
        pass
    logger = logger_utilities.get_logger()
    model.eval(
    )  # TODO: model isn't required because model.eval() is done inside attack (make sure before removing)
    adversarials = None
    logger.info("Starting eval...")
    if save_adv_sample:
        logger.info("Save adversarial samples generated")
    adv_batch_l = []
    for iter_num, (data, labels) in enumerate(dataloader):

        t0 = time.time()
        data, labels = TorchUtils.to_device(data), TorchUtils.to_device(labels)
        adversarials_batch = attack.create_adversarial_sample(
            data,
            labels,
            get_adversarial_class=True,
            save_adv_sample=save_adv_sample)
        t1 = time.time()
        adv_batch_l.append(adversarials_batch)
        logger.info("eval_model iter_num: {} ,process time: {} ".format(
            iter_num, t1 - t0))
        # if iter_num == 0:
        #     adversarials = adversarials_batch
        # else:
        #     adversarials.merge(adversarials_batch)
        # t2 = time.time()
        # logger.info("eval_model iter_num: {} ,process time: {} save time: {} ".format(iter_num, t1-t0, t2-t1))

    adversarials = adv_batch_l[0].cat(adv_batch_l)
    try:
        logger.info(
            "Max GPU memory allocated during eval_adversarial_dataset: {} MB".
            format(torch.cuda.max_memory_allocated() / 2**20))
    except:
        pass
    return adversarials
Beispiel #4
0
 def __init__(self, mean: list, std: list):
     """
     :param mean: a list representing the mean values in each channel
     :param std: a list representing the std values in each channel
     """
     super(NormalizeCls, self).__init__()
     assert (type(mean) == list and type(std) == list
             and len(mean) == len(std))
     channels = len(mean)
     self.mean = TorchUtils.to_device(
         torch.tensor(mean, dtype=torch.float).reshape([channels, 1, 1]))
     self.std = TorchUtils.to_device(
         torch.tensor(std, dtype=torch.float).reshape([channels, 1, 1]))
Beispiel #5
0
def eval_single_sample(model, test_sample_data):
    """
    Predict the probabilities assignment of the test sample by the model
    :param model: the model which will evaluate the test sample
    :param test_sample_data: the data of the test sample
    :return: prob: probabilities vector. pred: the class prediction of the test sample
    """
    # test_sample = (data, label)

    # Test the sample
    model.eval()

    # if next(model.parameters()).is_cuda:  # This only checks the first iteration from parameters(), a better way is to loop over all parameters
    #     sample_data = test_sample_data.cuda()
    # else:
    #     sample_data = test_sample_data.cpu()
    sample_data = TorchUtils.to_device(test_sample_data)
    if len(sample_data.shape) == 3:
        sample_data = sample_data.unsqueeze(
            0)  # make the single sample 4-dim tensor

    # with torch.no_grad():
    with torch.enable_grad():
        output = model(sample_data).detach().cpu()

        # Prediction
        pred = output.max(1, keepdim=True)[1]
        pred = pred.numpy()[0][0]

        # Extract prob
        prob = F.softmax(output, dim=-1)
        prob = prob.numpy().tolist()[0]
    return prob, pred
Beispiel #6
0
def calc_statistics(model, adv_l, logger):
    """
    Calculate statistics from from adv_l
    :param adv_l: a repacked adversarial list (see repack_adversarial_results func)
    """
    ### Calculate attack unsuccessful rate ###
    # Evaluate
    index = 0
    batch_size = 32
    correct = 0
    while index < len(adv_l):
        batch = adv_l[index:(index + batch_size)]
        data_batch = torch.Tensor(unpack_adv_obj(batch))
        data_batch = TorchUtils.to_device(data_batch)
        labels_batch = np.stack([a['original_class'] for a in batch])
        outputs = model(data_batch)
        _, predicted = torch.max(outputs.detach_().data, 1)
        # print(predicted)
        # print(labels_batch)
        correct += (predicted.cpu().numpy() == labels_batch).sum().item()
        index += batch_size

    unsuccessful_attack = correct / len(adv_l)
    logger.info("Unsuccessful attacks {}%".format(100 * unsuccessful_attack))
    # print(np.mean(model.forward(adv_img).argmax(axis=-1) == labels))
    ### - END - ###

    ### Calculate median distance ###
    distances = np.asarray([a["distance"] for a in adv_l])
    logger.info("Distance - min:{:.1e}, median:{:.1e}, max:{:.1e}".format(
        distances.min(), np.median(distances), distances.max()))
    logger.info("{} of {} attacks failed".format(
        sum(a["distance"] == np.inf for a in adv_l), len(adv_l)))
    logger.info("{} of {} inputs misclassified without perturbation".format(
        sum(a["distance"] == 0 for a in adv_l), len(adv_l)))

    # Present number of queries for the first 10 samples
    for i in range(10):
        print("Number of queries for {} sample: {}".format(
            i, adv_l[i]['queries']))
Beispiel #7
0
def load_pretrained_imagenet_model(model_name: str, pretrained: bool = True):
    """
    :param model_name: Could be one of the following:
        'alexnet',
     'densenet',
         'densenet121',
         'densenet161',
         'densenet169',
         'densenet201',
     'inception',
         'inception_v3',
     'resnet',
         'resnet101',
         'resnet152',
         'resnet18',
         'resnet34',
         'resnet50',
     'squeezenet',
         'squeezenet1_0',
         'squeezenet1_1',
     'vgg',
         'vgg11',
         'vgg11_bn',
         'vgg13',
         'vgg13_bn',
         'vgg16',
         'vgg16_bn',
         'vgg19',
         'vgg19_bn'
    :param: pretrained: Whether to return a pretrained model or not.
    :return: the trained model
    """
    model = getattr(models, model_name)(
        pretrained=pretrained
    )  # equivalent to models.resnet101(pretrained=True) for example
    # model_with_norm = ImagenetModel(model)
    return TorchUtils.to_device(model)
Beispiel #8
0
 def __init__(self):
     super(ModelTemplate, self).__init__()
     self.pnml_model = False
     self.regularization = TorchUtils.to_device(torch.zeros([1]))
     self.ckpt_path = None
Beispiel #9
0
    def train_model(self,
                    model,
                    dataloaders,
                    num_epochs: int = 10,
                    acc_goal=None,
                    eval_test_every_n_epoch: int = 1,
                    sample_test_data=None,
                    sample_test_true_label=None):
        """
        Train DNN model using some trainset.
        :param model: the model which will be trained.
        :param dataloaders: contains the trainset for training and testset for evaluation.
        :param num_epochs: number of epochs to train the model.
        :param acc_goal: stop training when getting to this accuracy rate on the trainset.
        :return: trained model (also the training of the models happen inplace)
                 and the loss of the trainset and testset.
        """
        self.logger.info("Use device:" + TorchUtils.get_device())
        model = TorchUtils.to_device(model)
        attack = get_attack(
            self.attack_params, model,
            get_dataset_min_max_val(dataloaders['dataset_name']))

        # If testset is already adversarial then do nothing else use the same attack to generate adversarial testset
        testset_attack = get_attack({
            "attack_type": "no_attack"
        }) if dataloaders[
            'adv_test_flag'] else attack  # TODO: replace training attack with testing attack
        epoch_time = 0

        # Loop on epochs
        for epoch in range(1, num_epochs + 1):

            epoch_start_time = time.time()
            total_loss_in_epoch, natural_train_loss, train_acc = self.__train(
                model, dataloaders['train'], attack, sample_test_data,
                sample_test_true_label)
            # Evaluate testset
            if self.eval_test_during_train is True and epoch % eval_test_every_n_epoch == 0:
                test_loss, test_acc = self.eval_model(model,
                                                      dataloaders['test'],
                                                      testset_attack)
            else:
                test_loss, test_acc = torch.tensor([-1.]), torch.tensor([-1.])
            epoch_time = time.time() - epoch_start_time

            # Save model
            if epoch % self.save_model_every_n_epoch == 0:
                torch.save(
                    model.state_dict(),
                    os.path.join(self.logger.output_folder,
                                 'model_iter_%d.pt' % epoch))
            # Log
            for param_group in self.optimizer.param_groups:
                lr = param_group['lr']
            self.logger.info(
                '[%d/%d] [train test] loss =[%f %f] natural_train_loss=[%f], acc=[%f %f], lr=%f, epoch_time=%.2f'
                % (epoch, num_epochs, total_loss_in_epoch, test_loss,
                   natural_train_loss, train_acc, test_acc, lr, epoch_time))

            # Stop training if desired goal is achieved
            if acc_goal is not None and train_acc >= acc_goal:
                break

        test_loss, test_acc = self.eval_model(model, dataloaders['test'],
                                              testset_attack)
        train_loss_output = float(total_loss_in_epoch)
        test_loss_output = float(test_loss)
        # Print and save
        self.logger.info(
            '----- [train test] loss =[%f %f], natural_train_loss=[%f], acc=[%f %f] epoch_time=%.2f'
            % (total_loss_in_epoch, test_loss, natural_train_loss, train_acc,
               test_acc, epoch_time))

        return model, train_loss_output, test_loss_output
Beispiel #10
0
def execute_pnml_adv_fix(pnml_params: dict,
                         params_init_training: dict,
                         dataloaders_input: dict,
                         sample_test_data_trans,
                         sample_test_true_label,
                         idx: int,
                         model_base_input,
                         logger,
                         genie_only_training: bool = False):
    """
    Execute the PNML procedure: for each label train the model and save the prediction afterword.
    :param pnml_params: parameters of training the model for each label
    :param train_class: the train_class is used to train and eval the model
    :param dataloaders_input: dataloaders which contains the trainset
    :param sample_test_data_trans: the data of the test sample that will be evaluated
    :param sample_test_true_label: the true label of the test sample
    :param idx: the index in the testset dataset of the test sample
    :param model_base_input: the base model from which the train will start
    :param logger: logger class to print logs and save results to file
    :param genie_only_training: calculate only genie probability for speed up when debugging
    :return: None
    """
    assert (
        model_base_input.__class__ != "PnmlMnistClassifier"
    )  #deepcopy doesn't work for PnmlMnistClassifier but it doesn't matter because PnmlMnistClassifier accuracy is already pNML accuracy and this function doesn't need to run
    # Check pnml_params contains all required keys
    required_keys = [
        'lr', 'momentum', 'step_size', 'gamma', 'weight_decay', 'epochs'
    ]
    for key in required_keys:
        if key not in pnml_params:
            logger.logger.error('The key: %s is not in pnml_params' % key)
            raise ValueError('The key: %s is not in pnml_params' % key)

    classes_trained = dataloaders_input['classes']
    if 'classes_cifar100' in dataloaders_input:
        classes_true = dataloaders_input['classes_cifar100']
    elif 'classes_svhn' in dataloaders_input:
        classes_true = dataloaders_input['classes_svhn']
    elif 'classes_noise' in dataloaders_input:
        classes_true = dataloaders_input['classes_noise']
    else:
        classes_true = classes_trained

    # Iteration of all labels
    if genie_only_training:
        trained_label_list = [sample_test_true_label.tolist()]
    else:
        trained_label_list = range(len(classes_trained))
    model = deepcopy(model_base_input)
    model.eval()
    refinement = get_attack(pnml_params['fix_type'],
                            model,
                            pnml_params['epsilon'],
                            pnml_params['pgd_iter'],
                            pnml_params['pgd_step'],
                            get_dataset_min_max_val(
                                dataloaders_input['dataset_name']),
                            pnml_params['pgd_test_restart_num'],
                            flip_grad_ratio=0.0)
    for fix_to_label in trained_label_list:
        time_trained_label_start = time.time()

        fix_label_expand = TorchUtils.to_device(
            torch.tensor(np.expand_dims(fix_to_label, 0), dtype=torch.int64))
        true_label_expand = TorchUtils.to_device(
            torch.tensor(np.expand_dims(sample_test_true_label, 0),
                         dtype=torch.int64)
        )  # make the label to tensor array type (important for loss calculation)
        if len(sample_test_data_trans.shape) == 3:
            sample_test_data_trans = TorchUtils.to_device(
                sample_test_data_trans.unsqueeze(
                    0))  # make the single sample 4-dim tensor

        time_trained_label = time.time() - time_trained_label_start

        # Evaluate with base model
        assert (not model.training)
        x_refine = refinement.create_adversarial_sample(
            sample_test_data_trans, true_label_expand, fix_label_expand)
        # assert(sample_test_data_trans.grad is None)
        prob, pred = eval_single_sample(model, x_refine)

        global execute_pnml_adv_fix_ind
        if execute_pnml_adv_fix_ind == 0:
            from utilities import plt_img
            # plt_img(x_refine, 0)

        # Save to file
        logger.add_entry_to_results_dict(idx, str(fix_to_label), prob, -1, -1)
        logger.info(
            'idx=%d fix_to_label=[%d,%s], true_label=[%d,%s] predict=[%d], time=%4.2f[s]'
            % (idx, fix_to_label, classes_trained[fix_to_label],
               sample_test_true_label, classes_true[sample_test_true_label],
               np.argmax(prob), time_trained_label))
    execute_pnml_adv_fix_ind = execute_pnml_adv_fix_ind + 1
Beispiel #11
0
    def __train(self,
                model,
                train_loader,
                attack,
                sample_test_data=None,
                sample_test_true_label=None):
        """
        Execute one epoch of training
        :param model: the model that will be trained.
        :param train_loader: contains the trainset to train.
        :return: the loss and accuracy of the trainset.
        """
        model.train()

        # Turn off batch normalization update
        if self.freeze_batch_norm is True:  # this works during fine-tuning because it can change the model even if LR=0.
            model = model.apply(
                set_bn_eval
            )  # this fucntion calls model.eval() which only effects dropout and batchnorm which will work in eval mode.
        total_loss_in_epoch = 0
        correct = 0

        natural_loss = 0
        natural_train_loss_in_ep = 0 if self.adv_learn_alpha != 1 else -1 * len(
            train_loader.dataset)
        natural_correct = 0

        loss_sample_test = 0
        # max_iter = np.ceil(len(train_loader.dataset) / train_loader.batch_size) #TODO: from some reason I am missing the last batch but the dataloader should not drop the last batch
        # device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
        if sample_test_data is not None:

            sample_test_data, sample_test_true_label = TorchUtils.to_device(
                sample_test_data), TorchUtils.to_device(sample_test_true_label)
            sample_test_data.requires_grad = False
            sample_test_true_label.requires_grad = False

        # Iterate over dataloaders
        self.logger.init_debug_time_measure()
        self.logger.debug("testing...")
        for iter_num, (images, labels) in enumerate(train_loader):
            self.logger.debug("iter: {}, load data".format(iter_num))

            # Adjust to CUDA
            images, labels = TorchUtils.to_device(
                images), TorchUtils.to_device(labels)
            self.logger.debug(
                "iter: {}, data and labels to CUDA:".format(iter_num))

            # Forward-pass of natural images
            self.optimizer.zero_grad()
            if self.adv_learn_alpha != 1:
                outputs, natural_loss = self.__forward_pass(
                    model, images, labels)
                _, predicted = torch.max(outputs.data, 1)
                natural_correct += (predicted == labels).sum().item()
                natural_train_loss_in_ep += natural_loss * len(
                    images)  # natural_loss sum for the epoch
                self.logger.debug("iter: {}, Forward pass".format(iter_num))

            # Back-propagation
            if self.adv_learn_alpha == 0:
                natural_loss.backward()
                self.logger.debug("iter: {}, Backward pass".format(iter_num))
                correct = natural_correct
            else:
                # # The loss is averaged over the minibatch, this doesn't matter at all since the loss magnitude
                # # is only just to determine gradient sign. Each pixel is changed by -+epsilon, no matter
                # # it's gradient magnitude.
                adv_images = attack.create_adversarial_sample(images, labels)
                self.optimizer.zero_grad()
                self.logger.debug(
                    "iter: {}, create adversarial data".format(iter_num))

                #### add another sample
                # TODO: move it outside if, otherwise this won't happen for non-adversarial training
                if (iter_num) == 0 and sample_test_data is not None:
                    sample_test_data.requires_grad = False
                    sample_test_true_label.requires_grad = False
                    output_sample_test, loss_sample_test = self.__forward_pass(
                        model, sample_test_data, sample_test_true_label)
                    _, predicted = torch.max(output_sample_test.data, 1)
                    # correct += (predicted == sample_test_true_label).sum().item() #TODO: when calculating accuracy (after epoch) we need to divide by +1
                    if TrainClass.criterion.reduction == 'elementwise_mean':  # Re-average the loss since another image was added
                        loss_sample_test = loss_sample_test / (len(images) + 1)
                        # natural_loss = natural_loss * len(images) / (len(images) + 1) TODO: uncomment
                    loss_sample_test.backward(retain_graph=True)
                    # self.optimizer.zero_grad()
                ####

                outputs, adv_loss = self.__forward_pass(
                    model, adv_images, labels)
                self.logger.debug(
                    "iter: {}, Adv. Forward pass".format(iter_num))
                _, predicted = torch.max(outputs.data, 1)
                correct += (predicted == labels).sum().item()
                total_loss = (
                    1 - self.adv_learn_alpha
                ) * natural_loss + self.adv_learn_alpha * adv_loss  #+ loss_sample_test
                total_loss_in_epoch += total_loss * len(images)
                total_loss.backward()
                self.logger.debug(
                    "iter: {}, Adv. Backward pass".format(iter_num))

            self.optimizer.step()
            self.logger.debug("iter: {}, Optimizer step".format(iter_num))
        self.scheduler.step()
        natural_train_loss_in_ep /= len(train_loader.dataset)
        total_loss_in_epoch /= len(train_loader.dataset)
        train_acc = correct / len(train_loader.dataset)
        return total_loss_in_epoch, natural_train_loss_in_ep, train_acc