예제 #1
0
    def test(self):

        # Load best model
        model = SiameseNet()
        _, _, _, model_state, _ = self.load_checkpoint(best=self.config.best)
        model.load_state_dict(model_state)
        if self.config.use_gpu:
            model.cuda()

        test_loader = get_test_loader(self.config.data_dir, self.config.way,
                                      self.config.test_trials,
                                      self.config.seed,
                                      self.config.num_workers,
                                      self.config.pin_memory)

        correct_sum = 0
        num_test = test_loader.dataset.trials
        print(f"[*] Test on {num_test} pairs.")

        pbar = tqdm(enumerate(test_loader), total=num_test, desc="Test")
        with torch.no_grad():
            for i, (x1, x2, _) in pbar:

                if self.config.use_gpu:
                    x1, x2 = x1.to(self.device), x2.to(self.device)

                # compute log probabilities
                out = model(x1, x2)

                y_pred = torch.sigmoid(out)
                y_pred = torch.argmax(y_pred)
                if y_pred == 0:
                    correct_sum += 1

                pbar.set_postfix_str(f"accuracy: {correct_sum / num_test}")

        test_acc = (100. * correct_sum) / num_test
        print(f"Test Acc: {correct_sum}/{num_test} ({test_acc:.2f}%)")
예제 #2
0
class RobustClassifier(LightningModule):
    def __init__(self, args: argparse.Namespace = None):
        super().__init__()
        self.num_classes = args.num_classes
        self.latent_dim = args.latent_dim
        self.feature_dim = args.feature_dim
        self.batch_size = args.batch_size
        self.lr = args.lr

        self.class_noise_convertor = nn.ModuleDict({
            str(k): nn.Sequential(nn.Linear(self.feature_dim, self.latent_dim),
                                  nn.ReLU(),
                                  nn.Linear(self.latent_dim,
                                            self.latent_dim)).to(self.device)
            for k in range(self.num_classes)
        })

        self.generator = Generator(ngpu=1)
        self.generator.load_state_dict(torch.load(args.gen_weights))
        self.class_identifier = SiameseNet()
        self.class_identifier.load_state_dict(torch.load(args.siamese_weights))
        if args.generator_pre_train:
            self.generator.to(self.device).freeze()
        if args.siamese_pre_train:
            self.class_identifier.to(self.device).freeze()

        # self.class_similarity = nn.Linear(4096, 1)
        self.creterion = ContrastiveLoss(1)
        self.threshold = args.threshold

        self.train_acc = Accuracy()
        self.val_acc = Accuracy()
        self.test_acc = Accuracy()

    @staticmethod
    def add_to_argparse(parser):
        parser.add_argument("--num_classes",
                            type=int,
                            default=10,
                            help="Number of Classes")
        parser.add_argument("--lr", type=float, default=3e-4)
        parser.add_argument("--threshold", type=float, default=0.5)
        parser.add_argument("--batch_size", type=int, default=16)
        parser.add_argument("--latent_dim", type=int, default=100)
        parser.add_argument("--feature_dim", type=int, default=100)
        parser.add_argument("--gen_weights",
                            type=str,
                            default="weights/gen_weights.pth")
        parser.add_argument("--siamese_weights",
                            type=str,
                            default="weights/siamese_weights.pth")
        parser.add_argument("--generator_pre_train",
                            dest='generator_pre_train',
                            default=True,
                            action='store_true')
        parser.add_argument("--no_generator_pre_train",
                            dest='generator_pre_train',
                            default=True,
                            action='store_false')
        parser.set_defaults(generator_pre_train=True)
        parser.add_argument("--siamese_pre_train",
                            dest='siamese_pre_train',
                            default=True,
                            action='store_true')
        parser.add_argument("--no_siamese_pre_train",
                            dest='siamese_pre_train',
                            default=True,
                            action='store_false')
        parser.set_defaults(siamese_pre_train=True)
        return parser

    def forward(self, x):
        batch_size = x.size(0)
        embeddings1, embeddings2 = torch.Tensor([]), torch.Tensor([])
        scores = torch.ones(self.num_classes, batch_size)
        noise = torch.rand(batch_size, self.feature_dim, device=self.device)
        for class_idx, model in self.class_noise_convertor.items():
            class_noise = model(noise).view(batch_size, -1, 1, 1)
            gen_imgs = self.generator(class_noise)
            embed1, embed2 = self.class_identifier(gen_imgs, x)
            embeddings1 = torch.cat(
                (embeddings1.to(self.device), embed1.to(self.device)), dim=0)
            embeddings2 = torch.cat(
                (embeddings2.to(self.device), embed2.to(self.device)), dim=0)
            scores[int(class_idx)] = nn.functional.cosine_similarity(embed1,
                                                                     embed2,
                                                                     dim=1)
        self.register_buffer("embeddings_1", embeddings1.view(-1, 4096))
        self.register_buffer("embeddings_2", embeddings2.view(-1, 4096))
        scores = torch.softmax(scores[scores > 0].view(batch_size, -1), dim=1)
        pred = torch.argmax(scores, dim=1).to(
            self.device
        )  #torch.Tensor([torch.argmax(img_score) if (img_score.max()-img_score.min())>0.5 else -1 for img_score in scores]).to(self.device)
        return pred

    def calculate_loss(self, y_true):
        siamese_labels = torch.Tensor([])
        for class_idx in range(self.num_classes):
            temp = torch.from_numpy(
                np.where(y_true.cpu().numpy() == class_idx, 1, 0))
            siamese_labels = torch.cat(
                (siamese_labels.to(self.device), temp.to(self.device)), dim=0)
        result = self.creterion(self.embeddings_1, self.embeddings_2,
                                siamese_labels)
        return result

    def training_step(self, batch, batch_idx):
        img, y_true = batch
        img, y_true = img.to(self.device), y_true.to(self.device)
        y_pred = self(img)
        result = self.calculate_loss(y_true)
        self.train_acc(y_pred, y_true)
        self.log('train_acc', self.train_acc, on_epoch=True, on_step=False)
        self.log('train_loss', result, on_step=True)

        return result

    def validation_step(self, batch, batch_idx):
        img, y_true = batch
        img, y_true = img.to(self.device), y_true.to(self.device)
        y_pred = self(img)
        val_loss = self.calculate_loss(y_true)
        self.log('val_loss', val_loss, prog_bar=True)
        self.val_acc(y_pred, y_true)
        self.log('val_acc', self.val_acc, on_epoch=True, on_step=False)

    def test_step(self, batch, batch_idx):
        img, y_true = batch
        img, y_true = img.to(self.device), y_true.to(self.device)
        y_pred = self(img)
        self.test_acc(y_pred, y_true)
        self.log('test_acc', self.test_acc, on_epoch=True, on_step=False)

    def configure_optimizers(self):
        optimizer = optim.Adam(filter(lambda p: p.requires_grad_,
                                      self.parameters()),
                               lr=self.lr)
        return [optimizer], []
예제 #3
0
def loadModel(path):
    model = SiameseNet()
    model.load_state_dict(torch.load(path, map_location=torch.device('cuda')))
    model.cuda()
    model.eval()
    return model
예제 #4
0
class Trainer(object):
    """
    Trainer encapsulates all the logic necessary for training
    the Siamese Network model.

    All hyperparameters are provided by the user in the
    config file.
    """
    def __init__(self, config, data_loader, layer_hyperparams):
        """
        Construct a new Trainer instance.

        Args
        ----
        - config: object containing command line arguments.
        - data_loader: data iterator.
        - layer_hyperparams: dict containing layer-wise hyperparameters
          such as the initial learning rate, the end momentum, and the l2
          regularization strength.
        """
        self.config = config
        self.layer_hyperparams = layer_hyperparams

        if config.is_train:
            self.train_loader = data_loader[0]
            self.valid_loader = data_loader[1]
            self.num_train = len(self.train_loader.dataset)
            self.num_valid = self.valid_loader.dataset.trials
        else:
            self.test_loader = data_loader
            self.num_test = self.test_loader.dataset.trials

        self.model = SiameseNet()
        if config.use_gpu:
            self.model.cuda()

        # model params
        self.num_params = sum(
            [p.data.nelement() for p in self.model.parameters()])
        self.num_model = get_num_model(config)
        self.num_layers = len(list(self.model.children()))

        print('[*] Number of model parameters: {:,}'.format(self.num_params))

        # path params
        self.ckpt_dir = os.path.join(config.ckpt_dir, self.num_model)
        self.logs_dir = os.path.join(config.logs_dir, self.num_model)

        # misc params
        self.resume = config.resume
        self.use_gpu = config.use_gpu
        self.dtype = (torch.cuda.FloatTensor
                      if self.use_gpu else torch.FloatTensor)

        # optimization params
        self.best = config.best
        self.best_valid_acc = 0.
        self.epochs = config.epochs
        self.start_epoch = 0
        self.lr_patience = config.lr_patience
        self.train_patience = config.train_patience
        self.counter = 0

        # grab layer-wise hyperparams
        self.init_lrs = self.layer_hyperparams['layer_init_lrs']
        self.init_momentums = [config.init_momentum] * self.num_layers
        self.end_momentums = self.layer_hyperparams['layer_end_momentums']
        self.l2_regs = self.layer_hyperparams['layer_l2_regs']

        # compute temper rate for momentum
        if self.epochs == 1:
            f = lambda max, min: min
        else:
            f = lambda max, min: (max - min) / (self.epochs - 1)
        self.momentum_temper_rates = [
            f(x, y) for x, y in zip(self.end_momentums, self.init_momentums)
        ]

        # set global learning rates and momentums
        self.lrs = self.init_lrs
        self.momentums = self.init_momentums

        # # initialize optimizer
        # optim_dict = []
        # for i, layer in enumerate(self.model.children()):
        #     group = {}
        #     group['params'] = layer.parameters()
        #     group['lr'] = self.lrs[i]
        #     group['momentum'] = self.momentums[i]
        #     group['weight_decay'] = self.l2_regs[i]
        #     optim_dict.append(group)
        # self.optimizer = optim.SGD(optim_dict)
        # self.optimizer = optim.SGD(
        #     self.model.parameters(), lr=1e-3, momentum=0.9, weight_decay=4e-4,
        # )
        self.optimizer = optim.Adam(
            self.model.parameters(),
            lr=3e-4,
            weight_decay=6e-5,
        )

        # # learning rate scheduler
        # self.scheduler = StepLR(
        #     self.optimizer, step_size=self.lr_patience, gamma=0.99,
        # )

    def train(self):
        if self.resume:
            self.load_checkpoint(best=False)

        # switch to train mode
        self.model.train()

        # create train and validation log files
        optim_file = open(os.path.join(self.logs_dir, 'optim.csv'), 'w')
        train_file = open(os.path.join(self.logs_dir, 'train.csv'), 'w')
        valid_file = open(os.path.join(self.logs_dir, 'valid.csv'), 'w')

        print("\n[*] Train on {} sample pairs, validate on {} trials".format(
            self.num_train, self.num_valid))

        for epoch in range(self.start_epoch, self.epochs):
            # self.decay_lr()
            # self.temper_momentum(epoch)
            #
            # # log lrs and momentums
            # n = self.num_layers
            # msg = (
            #     "{}, " + ", ".join(["{}"] * n) + ", " + ", ".join(["{}"] * n)
            # )
            # optim_file.write(msg.format(
            #     epoch, *self.momentums, *self.lrs)
            # )

            print('\nEpoch: {}/{}'.format(epoch + 1, self.epochs))

            train_loss = self.train_one_epoch(epoch, train_file)
            valid_acc = self.validate(epoch, valid_file)

            # check for improvement
            is_best = valid_acc > self.best_valid_acc
            msg = "train loss: {:.3f} - val acc: {:.3f}"
            if is_best:
                msg += " [*]"
                self.counter = 0
            print(msg.format(train_loss, valid_acc))

            # checkpoint the model
            if not is_best:
                self.counter += 1
            if self.counter > self.train_patience:
                print("[!] No improvement in a while, stopping training.")
                return
            self.best_valid_acc = max(valid_acc, self.best_valid_acc)
            self.save_checkpoint(
                {
                    'epoch': epoch + 1,
                    'model_state': self.model.state_dict(),
                    'optim_state': self.optimizer.state_dict(),
                    'best_valid_acc': self.best_valid_acc,
                }, is_best)
        # release resources
        optim_file.close()
        train_file.close()
        valid_file.close()

    def train_one_epoch(self, epoch, file):
        train_batch_time = AverageMeter()
        train_losses = AverageMeter()

        tic = time.time()
        with tqdm(total=self.num_train) as pbar:
            for i, (x1, x2, y) in enumerate(self.train_loader):
                if self.use_gpu:
                    x1, x2, y = x1.cuda(), x2.cuda(), y.cuda()
                x1, x2, y = Variable(x1), Variable(x2), Variable(y)

                # split input pairs along the batch dimension
                batch_size = x1.shape[0]

                out = self.model(x1, x2)
                loss = F.binary_cross_entropy_with_logits(out, y)

                # compute gradients and update
                self.optimizer.zero_grad()
                loss.backward()
                self.optimizer.step()

                # store batch statistics
                toc = time.time()
                train_losses.update(loss.data[0], batch_size)
                train_batch_time.update(toc - tic)
                tic = time.time()

                pbar.set_description(("{:.1f}s - loss: {:.3f}".format(
                    train_batch_time.val,
                    train_losses.val,
                )))
                pbar.update(batch_size)

                # log loss
                iter = (epoch * len(self.train_loader)) + i
                file.write('{},{}\n'.format(iter, train_losses.val))

            return train_losses.avg

    def validate(self, epoch, file):
        # switch to evaluate mode
        self.model.eval()

        correct = 0
        for i, (x1, x2) in enumerate(self.valid_loader):
            if self.use_gpu:
                x1, x2 = x1.cuda(), x2.cuda()
            x1, x2 = Variable(x1, volatile=True), Variable(x2, volatile=True)

            batch_size = x1.shape[0]

            # compute log probabilities
            out = self.model(x1, x2)
            log_probas = F.sigmoid(out)

            # get index of max log prob
            pred = log_probas.data.max(0)[1][0]
            if pred == 0:
                correct += 1

        # compute acc and log
        valid_acc = (100. * correct) / self.num_valid
        iter = epoch
        file.write('{},{}\n'.format(iter, valid_acc))
        return valid_acc

    def test(self):
        # load best model
        self.load_checkpoint(best=self.best)

        # switch to evaluate mode
        self.model.eval()

        correct = 0
        for i, (x1, x2) in enumerate(self.test_loader):
            if self.use_gpu:
                x1, x2 = x1.cuda(), x2.cuda()
            x1, x2 = Variable(x1, volatile=True), Variable(x2, volatile=True)

            batch_size = x1.shape[0]

            # compute log probabilities
            out = self.model(x1, x2)
            log_probas = F.sigmoid(out)

            # get index of max log prob
            pred = log_probas.data.max(0)[1][0]
            if pred == 0:
                correct += 1

        test_acc = (100. * correct) / self.num_test
        print("[*] Test Acc: {}/{} ({:.2f}%)".format(correct, self.num_test,
                                                     test_acc))

    def temper_momentum(self, epoch):
        """
        This function linearly increases the per-layer momentum to
        a predefined ceiling over a set amount of epochs.
        """
        if epoch == 0:
            return
        self.momentums = [
            x + y for x, y in zip(self.momentums, self.momentum_temper_rates)
        ]
        for i, param_group in enumerate(self.optimizer.param_groups):
            param_group['momentum'] = self.momentums[i]

    def decay_lr(self):
        """
        This function linearly decays the per-layer lr over a set
        amount of epochs.
        """
        self.scheduler.step()
        for i, param_group in enumerate(self.optimizer.param_groups):
            self.lrs[i] = param_group['lr']

    def save_checkpoint(self, state, is_best):
        filename = 'model_ckpt.tar'
        ckpt_path = os.path.join(self.ckpt_dir, filename)
        torch.save(state, ckpt_path)

        if is_best:
            filename = 'best_model_ckpt.tar'
            shutil.copyfile(ckpt_path, os.path.join(self.ckpt_dir, filename))

    def load_checkpoint(self, best=False):
        print("[*] Loading model from {}".format(self.ckpt_dir))

        filename = 'model_ckpt.tar'
        if best:
            filename = 'best_model_ckpt.tar'
        ckpt_path = os.path.join(self.ckpt_dir, filename)
        ckpt = torch.load(ckpt_path)

        # load variables from checkpoint
        self.start_epoch = ckpt['epoch']
        self.best_valid_acc = ckpt['best_valid_acc']
        self.model.load_state_dict(ckpt['model_state'])
        self.optimizer.load_state_dict(ckpt['optim_state'])

        if best:
            print("[*] Loaded {} checkpoint @ epoch {} "
                  "with best valid acc of {:.3f}".format(
                      filename, ckpt['epoch'], ckpt['best_valid_acc']))
        else:
            print("[*] Loaded {} checkpoint @ epoch {}".format(
                filename, ckpt['epoch']))
예제 #5
0
    def train(self):
        # Dataloader
        train_loader, valid_loader = get_train_validation_loader(
            self.config.data_dir, self.config.batch_size,
            self.config.num_train, self.config.augment, self.config.way,
            self.config.valid_trials, self.config.shuffle, self.config.seed,
            self.config.num_workers, self.config.pin_memory)

        # Model, Optimizer, criterion
        model = SiameseNet()
        if self.config.optimizer == "SGD":
            optimizer = optim.SGD(model.parameters(), lr=self.config.lr)
        else:
            optimizer = optim.Adam(model.parameters())
        criterion = torch.nn.BCEWithLogitsLoss()

        if self.config.use_gpu:
            model.cuda()

        # Load check point
        if self.config.resume:
            start_epoch, best_epoch, best_valid_acc, model_state, optim_state = self.load_checkpoint(
                best=False)
            model.load_state_dict(model_state)
            optimizer.load_state_dict(optim_state)
            one_cycle = OneCyclePolicy(optimizer,
                                       self.config.lr,
                                       (self.config.epochs - start_epoch) *
                                       len(train_loader),
                                       momentum_rng=[0.85, 0.95])
        else:
            best_epoch = 0
            start_epoch = 0
            best_valid_acc = 0
            one_cycle = OneCyclePolicy(optimizer,
                                       self.config.lr,
                                       self.config.epochs * len(train_loader),
                                       momentum_rng=[0.85, 0.95])

        # create tensorboard summary and add model structure.
        writer = SummaryWriter(os.path.join(self.config.logs_dir, 'logs'),
                               filename_suffix=self.config.num_model)
        im1, im2, _ = next(iter(valid_loader))
        writer.add_graph(model, [im1.to(self.device), im2.to(self.device)])

        counter = 0
        num_train = len(train_loader)
        num_valid = len(valid_loader)
        print(
            f"[*] Train on {len(train_loader.dataset)} sample pairs, validate on {valid_loader.dataset.trials} trials"
        )

        # Train & Validation
        main_pbar = tqdm(range(start_epoch, self.config.epochs),
                         initial=start_epoch,
                         position=0,
                         total=self.config.epochs,
                         desc="Process")
        for epoch in main_pbar:
            train_losses = AverageMeter()
            valid_losses = AverageMeter()

            # TRAIN
            model.train()
            train_pbar = tqdm(enumerate(train_loader),
                              total=num_train,
                              desc="Train",
                              position=1,
                              leave=False)
            for i, (x1, x2, y) in train_pbar:
                if self.config.use_gpu:
                    x1, x2, y = x1.to(self.device), x2.to(self.device), y.to(
                        self.device)
                out = model(x1, x2)
                loss = criterion(out, y.unsqueeze(1))

                # compute gradients and update
                optimizer.zero_grad()
                loss.backward()
                optimizer.step()
                one_cycle.step()

                # store batch statistics
                train_losses.update(loss.item(), x1.shape[0])

                # log loss
                writer.add_scalar("Loss/Train", train_losses.val,
                                  epoch * len(train_loader) + i)
                train_pbar.set_postfix_str(f"loss: {train_losses.val:0.3f}")

            # VALIDATION
            model.eval()
            valid_acc = 0
            correct_sum = 0
            valid_pbar = tqdm(enumerate(valid_loader),
                              total=num_valid,
                              desc="Valid",
                              position=1,
                              leave=False)
            with torch.no_grad():
                for i, (x1, x2, y) in valid_pbar:

                    if self.config.use_gpu:
                        x1, x2, y = x1.to(self.device), x2.to(
                            self.device), y.to(self.device)

                    # compute log probabilities
                    out = model(x1, x2)
                    loss = criterion(out, y.unsqueeze(1))

                    y_pred = torch.sigmoid(out)
                    y_pred = torch.argmax(y_pred)
                    if y_pred == 0:
                        correct_sum += 1

                    # store batch statistics
                    valid_losses.update(loss.item(), x1.shape[0])

                    # compute acc and log
                    valid_acc = correct_sum / num_valid
                    writer.add_scalar("Loss/Valid", valid_losses.val,
                                      epoch * len(valid_loader) + i)
                    valid_pbar.set_postfix_str(f"accuracy: {valid_acc:0.3f}")
            writer.add_scalar("Acc/Valid", valid_acc, epoch)

            # check for improvement
            if valid_acc > best_valid_acc:
                is_best = True
                best_valid_acc = valid_acc
                best_epoch = epoch
                counter = 0
            else:
                is_best = False
                counter += 1

            # checkpoint the model
            if counter > self.config.train_patience:
                print("[!] No improvement in a while, stopping training.")
                return

            if is_best or epoch % 5 == 0 or epoch == self.config.epochs:
                self.save_checkpoint(
                    {
                        'epoch': epoch,
                        'model_state': model.state_dict(),
                        'optim_state': optimizer.state_dict(),
                        'best_valid_acc': best_valid_acc,
                        'best_epoch': best_epoch,
                    }, is_best)

            main_pbar.set_postfix_str(
                f"best acc: {best_valid_acc:.3f} best epoch: {best_epoch} ")

            tqdm.write(
                f"[{epoch}] train loss: {train_losses.avg:.3f} - valid loss: {valid_losses.avg:.3f} - valid acc: {valid_acc:.3f} {'[BEST]' if is_best else ''}"
            )

        # release resources
        writer.close()
예제 #6
0
class QueryModel():
    """
        Get the confidence score for two PIL images

        Args
            :param model_path str: path to the model to be loaded

        Properties
            :param self.model nn.Module: Model Type to be used for inference. 
            :param self.ckpt: Loaded model information
            
    """
    def __init__(self, model_path, *args, **kwargs):
        """
            - Load model from model_path 
            - Load state to the model
        """
        self.model = SiameseNet().cuda()
        self.ckpt = torch.load(model_path)
        self.model.load_state_dict(self.ckpt['model_state'])

    @staticmethod
    def resizeImage(image):
        """
            resize image to model parameters.

            Args
                :param image PIL.Image: PIL Image

            Return
                :param image PIL.Image: resized image
        """
        return image.resize((120, 80), Image.ANTIALIAS)
        # return image.resize((96, 64), Image.ANTIALIAS)

    def ImageToTensor(self, image):
        """
            resize and transform PIL image to tensor

            Args
                :param image PIL.Image: PIL Image

            Return
                :param tensor torch.Tesnor: PyTorch Tensor
            
        """
        image = self.resizeImage(image)
        transform = transforms.Compose([transforms.ToTensor()])
        return torch.reshape(transform(image), [1, 1, 80, 120])

    def getConfidence(self, tensor0, tensor1):
        """
            get confidence on the two tensors from the model
            and apply sigmoid.

            Args
                :param tensor0 torch.Tensor: transformed image tensor to be used for inference
                :param tensor1 torch.Tensor: transformed image tensor to be used for inference

            Return
                :param output torch.Tensor: confidence tensor    
        """
        x0, x1 = Variable(tensor0).cuda(), Variable(tensor1).cuda()
        output = self.model(x0, x1)
        output = F.sigmoid(output)

        return output

    def predict(self, img0, img1):
        """ 
            Infer confidence on the two images from the model 
            and return it.

            Args
                :param img0 PIL.Image: first image to be compared
                :param img1 PIL.Image: second image to be compared

            Return
                :param output torch.Tensor: confidence tensor
                                            confidence > 0.5, Similar Pairs
                                            confidence < 0.5, Dissimilar Pairs    
        """
        img0_tensor = self.ImageToTensor(img0)
        img1_tensor = self.ImageToTensor(img1)
        output = self.getConfidence(img0_tensor, img1_tensor)

        return output