Beispiel #1
0
    test_transform = torchvision.transforms.Compose([
        torchvision.transforms.Resize(size=448),
        torchvision.transforms.CenterCrop(size=448),
        torchvision.transforms.ToTensor(),
        torchvision.transforms.Normalize(mean=(0.485, 0.456, 0.406),
                                         std=(0.229, 0.224, 0.225))
    ])
    test_data = torchvision.datasets.ImageFolder(os.path.join(data_dir, 'val'),
                                                 transform=test_transform)
    test_loader = DataLoader(test_data,
                             batch_size=64,
                             shuffle=False,
                             num_workers=4,
                             pin_memory=True)

    net.eval()
    num_correct = 0
    num_total = 0
    with torch.no_grad():
        for X, y in test_loader:
            # Data
            X = X.cuda()
            y = y.cuda(async=True)
            # Prediction
            score = net(X)
            _, prediction = torch.max(score, 1)
            num_total += y.size(0)
            num_correct += torch.sum(prediction == y.data).item()

    test_accuracy = 100 * num_correct / num_total
Beispiel #2
0
class BCNNTrainer(object):
    """Manager class to train bilinear CNN.

    Attributes:
        _options: Hyperparameters.
        _path: Useful paths.
        _net: Bilinear CNN.
        _criterion: Cross-entropy loss.
        _solver: SGD with momentum.
        _scheduler: Reduce learning rate by a fator of 0.1 when plateau.
        _train_loader: Training data.
        _test_loader: Testing data.
    """
    def __init__(self, options, path, ckpt_basename='vgg_16'):
        """Prepare the network, criterion, solver, and data.

        Args:
            options, dict: Hyperparameters.
        """
        print('Prepare the network and data.')
        self._options = options
        self._path = path
        self.ckpt_basename = ckpt_basename
        # Network.
        self._net = BCNN(freeze_features=True)
        #self._net = torch.nn.DataParallel(self._net)
        self._net.features = torch.nn.DataParallel(self._net.features)
        self._net.cuda()

        if 'ckpt_path' in self._path:
            if os.path.exists(self._path['ckpt_path']):
                print('Continue from', self._path['ckpt_path'])
                self._net.load_state_dict(torch.load(self._path['ckpt_path']))
            else:
                print('Ckpt {} not found!'.format(self._path['ckpt_path']))
        print(self._net)
        # Criterion.
        self._criterion = torch.nn.CrossEntropyLoss().cuda()
        # Solver.
        self._solver = torch.optim.SGD(
            self._net.fc.parameters(),
            lr=self._options['base_lr'],
            momentum=0.9,
            weight_decay=self._options['weight_decay'])
        if self._options['lr_scheduler'] == 'reduce_on_plateau':
            self._scheduler = torch.optim.lr_scheduler.ReduceLROnPlateau(
                self._solver,
                mode='max',
                factor=0.1,
                patience=5,
                verbose=True,
                threshold=1e-4,
                min_lr=1e-6)
        elif self._options['lr_scheduler'] == 'fixed':
            self._scheduler = torch.optim.lr_scheduler.LambdaLR(
                self._solver, lambda epoch: 1.0)
        else:
            raise ValueError('Unknown scheduler:',
                             self._options['lr_scheduler'])

# Imagenet normalization
        normalize = torchvision.transforms.Normalize(
            mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
        train_transforms = torchvision.transforms.Compose([
            torchvision.transforms.Resize(size=448),  # Let smaller edge match
            torchvision.transforms.RandomHorizontalFlip(),
            torchvision.transforms.RandomCrop(size=448),
            torchvision.transforms.ToTensor(),
            normalize
        ])
        test_transforms = torchvision.transforms.Compose([
            torchvision.transforms.Resize(size=448),
            torchvision.transforms.CenterCrop(size=448),
            torchvision.transforms.ToTensor(), normalize
        ])
        train_data = cub200.CUB200(root=self._path['cub200'],
                                   train=True,
                                   download=True,
                                   transform=train_transforms)
        test_data = cub200.CUB200(root=self._path['cub200'],
                                  train=False,
                                  download=True,
                                  transform=test_transforms)
        self._train_loader = torch.utils.data.DataLoader(
            train_data,
            batch_size=self._options['batch_size'],
            shuffle=True,
            num_workers=4,
            pin_memory=True)
        self._test_loader = torch.utils.data.DataLoader(test_data,
                                                        batch_size=16,
                                                        shuffle=False,
                                                        num_workers=4,
                                                        pin_memory=True)

    def train(self):
        """Train the network."""
        print('Training.')
        self._net.train()
        best_acc = 0.0
        best_epoch = None
        for epoch in range(self._options['epochs']):
            epoch_loss = []
            num_correct = 0
            num_total = 0
            for batch_idx, (X, y) in enumerate(self._train_loader):
                # Data.
                X = X.cuda(non_blocking=True)
                y = y.cuda(non_blocking=True)

                # Clear the existing gradients.
                self._solver.zero_grad()
                # Forward pass.
                score = self._net(X)
                loss = self._criterion(score, y)
                epoch_loss.append(loss.data.item())
                # Prediction.
                _, prediction = torch.max(score.data, 1)
                num_total += y.size(0)
                num_correct += torch.sum(prediction == y.data).item()
                # Backward pass.
                loss.backward()
                self._solver.step()
                sys.stdout.write('\r')
                sys.stdout.write(
                    '| Epoch [%3d/%3d] Iter[%3d/%3d]\t\tLoss: %.4f Acc@1: %.3f%%'
                    % (epoch, self._options['epochs'], batch_idx + 1,
                       len(self._train_loader), loss.data.item(),
                       (100. * num_correct) / num_total))
                sys.stdout.flush()
            train_acc = 100 * num_correct / num_total
            test_acc = self._accuracy(self._test_loader)
            print('\nEpoch\tTrain loss\tTrain acc\tTest acc')
            print('%d\t%4.3f\t\t%4.2f%%\t\t%4.2f%%' %
                  (epoch + 1, np.mean(epoch_loss), train_acc, test_acc))
            self._scheduler.step(test_acc)
            if test_acc > best_acc:
                best_acc = test_acc
                best_epoch = epoch + 1
                print('*', end='')
                # Save model onto disk.
                save_path = os.path.join(
                    self._path['model'],
                    '{}_epoch_{}.pth'.format(self.ckpt_basename, epoch + 1))
                save_path_best = os.path.join(
                    self._path['model'],
                    '{}_epoch_best.pth'.format(self.ckpt_basename))
                torch.save(self._net.state_dict(), save_path)
                shutil.copy(save_path, save_path_best)
        print('Best at epoch %d, test accuaray %f' % (best_epoch, best_acc))

    def _accuracy(self, data_loader):
        """Compute the train/test accuracy.

        Args:
            data_loader: Train/Test DataLoader.

        Returns:
            Train/Test accuracy in percentage.
        """
        self._net.eval()
        num_correct = 0
        num_total = 0
        for X, y in data_loader:
            # Data.
            X = X.cuda(non_blocking=True)
            y = y.cuda(non_blocking=True)
            with torch.no_grad():
                # Prediction.
                score = self._net(X)
                _, prediction = torch.max(score.data, 1)
                num_total += y.size(0)
                num_correct += torch.sum(prediction == y.data).item()
        return 100 * num_correct / num_total

    def getStat(self):
        """Get the mean and std value for a certain dataset."""
        print('Compute mean and variance for training data.')
        train_data = cub200.CUB200(root=self._path['cub200'],
                                   train=True,
                                   transform=torchvision.transforms.ToTensor(),
                                   download=True)
        train_loader = torch.utils.data.DataLoader(train_data,
                                                   batch_size=1,
                                                   shuffle=False,
                                                   num_workers=4,
                                                   pin_memory=True)
        mean = torch.zeros(3)
        std = torch.zeros(3)
        for X, _ in tqdm(train_loader):
            for d in range(3):
                mean[d] += X[:, d, :, :].mean()
                std[d] += X[:, d, :, :].std()
        mean.div_(len(train_data))
        std.div_(len(train_data))
        print(mean)
        print(std)