Esempio n. 1
0
 def __init__(self, num_classes, input_channel, pretrained):
     super(NASNetALarge, self).__init__()
     model = pretrainedmodels.nasnetalarge(num_classes=1000,
                                           pretrained="imagenet")
     model.last_linear = nn.Linear(model.last_linear.in_features,
                                   num_classes)
     self.model = model
Esempio n. 2
0
def get_outputs(image_dir, filename):
    models_name = ['resnet152', 'vgg19_bn', 'densenet161', 'nasnetalarge']
    res = {}
    res_labels = {}
    for name in models_name:
        if name == 'densenet161':
            model_ft = torch.load('model_pretrained_densenet161.pkl')
        elif name == 'resnet152':
            model_ft = torch.load('model_pretrained_resnet152.pkl')
        elif name == 'vgg19_bn':
            model_ft = torch.load('model_pretrained_vgg19.pkl')

        data_transforms = transforms.Compose([
            transforms.Scale(256),
            transforms.CenterCrop(224),
            transforms.ToTensor(),
            transforms.Normalize(mean=[0.485, 0.456, 0.406],
                                 std=[0.229, 0.224, 0.225])])
        if name == 'nasnetalarge':
            model_ft = pretrainedmodels.nasnetalarge(num_classes=1000, pretrained='imagenet')
            data_transforms = transforms.Compose([
                transforms.Scale(377),
                transforms.CenterCrop(331),
                transforms.ToTensor(),
                transforms.Normalize(mean=model_ft.mean,
                                     std=model_ft.std)])
            model_ft = torch.load('model_pretrained_nasnet.pkl')
        use_gpu = torch.cuda.is_available()
        model_ft.eval()


        test_dataset = TestData(image_dir, data_transforms)
        test_dataloader = DataLoader(test_dataset, batch_size=4, shuffle=False, num_workers=4)
        since = time.time()
        temp = []
        temp_list = []
        for i, batch in enumerate(test_dataloader):
            inputs, cid = batch
            temp_list.append(cid)
            if use_gpu:
                inputs = Variable(inputs.cuda())
            else:
                inputs = Variable(inputs)

            outputs = model_ft(inputs)
            temp.append(softmax(outputs.data.cpu().numpy()))
            if i % 200 == 199:
                print('iter:{}'.format(i+1))
        time_elapsed = time.time() - since
        print('Testing complete in {:.0f}m {:.0f}s'.format(
            time_elapsed // 60, time_elapsed % 60))
        res[name] = np.concatenate(temp)
        res_labels[name] = [y for x in temp_list for y in x]
        print('{} finish'.format(name))

    torch.save(res, filename)
    torch.save(res_labels, filename + '_label')
    return res
Esempio n. 3
0
def test_nasnetalarge_model(input_var, pool, assert_equal_outputs):
    original_model = pretrainedmodels.nasnetalarge(pretrained='imagenet',
                                                   num_classes=1000)
    finetune_model = make_model(
        'nasnetalarge',
        num_classes=1000,
        pool=pool,
        pretrained=True,
    )
    copy_module_weights(original_model.last_linear, finetune_model._classifier)
    assert_equal_outputs(input_var, original_model, finetune_model)
Esempio n. 4
0
    def __init__(self, num_classes, pretrained="imagenet"):
        super().__init__()
        self.model = nasnetalarge(pretrained=pretrained, num_classes=1000)
        self.model.avg_pool = nn.AdaptiveAvgPool2d(1)

        new_last_linear = nn.Linear(self.model.last_linear.in_features,
                                    num_classes)
        new_last_linear.weight.data = self.model.last_linear.weight.data[:
                                                                         num_classes]
        new_last_linear.bias.data = self.model.last_linear.bias.data[:
                                                                     num_classes]
        self.model.last_linear = new_last_linear
Esempio n. 5
0
def nets(model, num_class):

    if model == 'inceptionv4':
        model = ptm.inceptionv4(num_classes=1000, pretrained='imagenet')
        model.last_linear = nn.Linear(model.last_linear.in_features, num_class)
        return model

    if model == 'senet154':
        model = ptm.senet154(num_classes=1000, pretrained='imagenet')
        model.last_linear = nn.Linear(model.last_linear.in_features, num_class)
        return model

    if model == 'pnasnet':
        model = ptm.pnasnet5large(num_classes=1000, pretrained='imagenet')
        model.last_linear = nn.Linear(model.last_linear.in_features, num_class)
        return model

    if model == 'xception':
        model = ptm.xception(num_classes=1000, pretrained='imagenet')
        model.last_linear = nn.Linear(model.last_linear.in_features, num_class)
        return model

    if model == 'incepresv2':
        model = ptm.inceptionresnetv2(num_classes=1000, pretrained='imagenet')
        model.last_linear = nn.Linear(model.last_linear.in_features, num_class)
        return model

    if model == 'resnet152':
        model = models.resnet152(pretrained=True)
        model.fc = nn.Linear(2048, num_class)
        return model
        
    if model == 'se_resxt101':
        model = ptm.se_resnext101_32x4d(num_classes=1000, pretrained='imagenet')
        model.last_linear = nn.Linear(model.last_linear.in_features, num_class)
        return model
        
    if model == 'nasnet':
        model = ptm.nasnetalarge(num_classes=1000, pretrained='imagenet')
        model.last_linear = nn.Linear(model.last_linear.in_features, num_class)
        return model
        
    if model == 'dpn': # 224 input size
        model = ptm.dpn107(num_classes=1000, pretrained='imagenet+5k')
        model.last_linear = nn.Conv2d(model.last_linear.in_channels, num_class,
                                      kernel_size=1, bias=True)
        return model
        
    if model == 'resnext101':# 320 input size
        model = torch.hub.load('facebookresearch/WSL-Images', 'resnext101_32x16d_wsl')
        model.fc = nn.Linear(2048, num_class)
        return model  
Esempio n. 6
0
def nasnetalarge(input_size=(3, 331, 331), num_classes=1000, pretrained=None):
    # Minimum Input size = (96,96) one is must above than 97
    model = models.nasnetalarge(num_classes=1000, pretrained=pretrained)
    if input_size != (3, 331, 331):
        model.conv0.conv = nn.Conv2d(in_channels=input_size[0],
                                     out_channels=96,
                                     kernel_size=3,
                                     padding=0,
                                     stride=2,
                                     bias=False)
        model.input_size = input_size
    # calculate last avgpool_1a kernel size
    test_tensor = torch.randn(1, input_size[0], input_size[1], input_size[2])
    x_conv0 = model.conv0(test_tensor)
    x_stem_0 = model.cell_stem_0(x_conv0)
    x_stem_1 = model.cell_stem_1(x_conv0, x_stem_0)

    x_cell_0 = model.cell_0(x_stem_1, x_stem_0)
    x_cell_1 = model.cell_1(x_cell_0, x_stem_1)
    x_cell_2 = model.cell_2(x_cell_1, x_cell_0)
    x_cell_3 = model.cell_3(x_cell_2, x_cell_1)
    x_cell_4 = model.cell_4(x_cell_3, x_cell_2)
    x_cell_5 = model.cell_5(x_cell_4, x_cell_3)

    x_reduction_cell_0 = model.reduction_cell_0(x_cell_5, x_cell_4)

    x_cell_6 = model.cell_6(x_reduction_cell_0, x_cell_4)
    x_cell_7 = model.cell_7(x_cell_6, x_reduction_cell_0)
    x_cell_8 = model.cell_8(x_cell_7, x_cell_6)
    x_cell_9 = model.cell_9(x_cell_8, x_cell_7)
    x_cell_10 = model.cell_10(x_cell_9, x_cell_8)
    x_cell_11 = model.cell_11(x_cell_10, x_cell_9)

    x_reduction_cell_1 = model.reduction_cell_1(x_cell_11, x_cell_10)

    x_cell_12 = model.cell_12(x_reduction_cell_1, x_cell_10)
    x_cell_13 = model.cell_13(x_cell_12, x_reduction_cell_1)
    x_cell_14 = model.cell_14(x_cell_13, x_cell_12)
    x_cell_15 = model.cell_15(x_cell_14, x_cell_13)
    x_cell_16 = model.cell_16(x_cell_15, x_cell_14)
    x_cell_17 = model.cell_17(x_cell_16, x_cell_15)
    x = model.relu(x_cell_17)
    model.avg_pool = nn.AvgPool2d(kernel_size=(x.shape[2], x.shape[3]))
    x = model.avg_pool(x)
    x = x.view(x.size(0), -1).shape[1]
    model.last_linear = nn.Linear(in_features=x,
                                  out_features=num_classes,
                                  bias=True)
    return model
def generate_2D_model(opt):
    if opt['model'] == 'inception_v3':
        C, H, W = 3, 299, 299
        model = pretrainedmodels.inceptionv3(pretrained='imagenet')
        load_image_fn = utils.LoadTransformImage(model)
    elif opt['model'] == 'vgg16':
        C, H, W = 3, 224, 224
        model = pretrainedmodels.vgg16(pretrained='imagenet')
        load_image_fn = utils.LoadTransformImage(model)
    elif opt['model'] == 'vgg19':
        C, H, W = 3, 224, 224
        model = pretrainedmodels.vgg19(pretrained='imagenet')
        load_image_fn = utils.LoadTransformImage(model)
    elif opt['model'] == 'resnet50':
        C, H, W = 3, 224, 224
        model = pretrainedmodels.resnet50(pretrained='imagenet')
        load_image_fn = utils.LoadTransformImage(model)
    elif opt['model'] == 'resnet101':
        C, H, W = 3, 224, 224
        model = pretrainedmodels.resnet101(pretrained='imagenet')
        load_image_fn = utils.LoadTransformImage(model)
    elif opt['model'] == 'resnet152':
        C, H, W = 3, 224, 224
        model = pretrainedmodels.resnet152(pretrained='imagenet')
        load_image_fn = utils.LoadTransformImage(model)
    elif opt['model'] == 'inception_v4':
        C, H, W = 3, 299, 299
        model = pretrainedmodels.inceptionv4(num_classes=1000,
                                             pretrained='imagenet')
        load_image_fn = utils.LoadTransformImage(model)
    elif opt['model'] == 'nasnet':
        C, H, W = 3, 331, 331
        model = pretrainedmodels.nasnetalarge(num_classes=1001,
                                              pretrained='imagenet+background')
        load_image_fn = utils.LoadTransformImage(model)
    else:
        print("doesn't support %s" % (opt['model']))

    model.last_linear = utils.Identity()
    model = nn.DataParallel(model)
    # if opt['saved_model'] != '':
    #     model.load_state_dict(torch.load(opt['saved_model']), strict=False)
    model = model.cuda()
    return model
Esempio n. 8
0
def main():
    """main Module which select models and load.
    """

    model = args.model
    # Use args.model as pretrain model
    if model == 'resnet152':
        original_model = torchvision.models.resnet152(pretrained=True)
    elif model == 'resnet50' or model == 'resnet50_3c':
        original_model = torchvision.models.resnet50(pretrained=True)
    elif model == 'densenet161':
        original_model = torchvision.models.densenet161(pretrained=True)
    elif model == 'pnasnet5large':
        original_model = pretrainedmodels.pnasnet5large(num_classes=1000, pretrained='imagenet')
    elif model == 'nasnetalarge':
        original_model = pretrainedmodels.nasnetalarge(num_classes=1000, pretrained='imagenet')
    else:
        sys.exit(-1)

    net = FineTuneModel(original_model, model, args)

    #for name, param in original_model.named_children():
    #   print(name)
    #for name, param in net.named_children():
    #   print(name)

    if args.lm == False:
        CNN_Train(net, args)
    elif args.lm == True:
        # Load from saved model
        if os.path.isfile(args.model_path) == True:
            print("=> loading checkpoint '{}'".format(args.model_path))
            checkpoint = torch.load(args.model_path)
            CNN_Train(net, args, checkpoint)
        else:
            print("=> no checkpoint found at '{}'".format(args.model_path))

    else:
        print('Error --lm parameter')
        sys.exit(-1)
Esempio n. 9
0
def nasnetalarge(num_classes, feature_extract, use_pretrained=True):
    """Funkcja zwracająca model nasnet_large

    :param num_classes: liczba szukanych cech
    :type num_classes: int
    :param feature_extract: czy ekstrachować cechy
    :type feature_extract: bool
    :param use_pretrained: czy używać wstępnie przetrenowanego modelu
    :type use_pretrained: bool
    :return: wygenerowny model,wielkość wejścia modelu,sugerowana średnia do normalizacji,sugerowane odchylenie standardowe do normalizacji
    :rtype: model, int, float, float
    """
    model = pretrainedmodels.nasnetalarge(num_classes=1000,
                                          pretrained='imagenet')
    set_parameter_requires_grad(model, feature_extract)
    num_ftrs = model.last_linear.in_features
    model.last_linear = nn.Sequential(nn.Linear(num_ftrs, num_classes),
                                      nn.Sigmoid())
    input_size = 224
    mean, std = [0.5, 0.5, 0.5], [0.5, 0.5, 0.5]

    return model, input_size, mean, std
Esempio n. 10
0
        C, H, W = 3, 224, 224
        model = pretrainedmodels.resnet50(pretrained='imagenet')
        load_image_fn = utils.LoadTransformImage(model)
    elif params['model'] == 'resnet101':
        C, H, W = 3, 224, 224
        model = pretrainedmodels.resnet101(pretrained='imagenet')
        load_image_fn = utils.LoadTransformImage(model)
    elif params['model'] == 'resnet152':
        C, H, W = 3, 224, 224
        model = pretrainedmodels.resnet152(pretrained='imagenet')
        load_image_fn = utils.LoadTransformImage(model)
    elif params['model'] == 'inception_v4':
        C, H, W = 3, 299, 299
        model = pretrainedmodels.inceptionv4(num_classes=1000,
                                             pretrained='imagenet')
        load_image_fn = utils.LoadTransformImage(model)
    elif params['model'] == 'nasnet':
        C, H, W = 3, 331, 331
        model = pretrainedmodels.nasnetalarge(num_classes=1001,
                                              pretrained='imagenet+background')
        load_image_fn = utils.LoadTransformImage(model)
    else:
        print("doesn't support %s" % (params['model']))

    model.last_linear = utils.Identity()
    model = nn.DataParallel(model)
    if params['saved_model'] != '':
        model.load_state_dict(torch.load(params['saved_model']), strict=False)
    model = model.cuda()
    extract_feats(params, model, load_image_fn)
Esempio n. 11
0
def main():
    global best_val_loss
    global best_val_acc
    start_epoch = args.start_epoch  # start from epoch 0 or last checkpoint epoch
    env_name = args.env_name
    # create a unique id for naming purposes
    now = datetime.datetime.now()
    vis_file_out = env_name + str(now.year) + ';' + str(now.month) + ';' + \
                   str(now.day) + ';' + str(now.hour) + ';' + str(now.minute)
    # set up logger
    log_filename = './logs/' + str(vis_file_out) + '.txt'
    logger = configure_logging(log_filename)
    logger.info(args)

    # Data loading code
    if args.arch == "inception":
        scaler = 350
        img_size = 299
        norm_trans = transforms.Normalize(mean=[0.485, 0.456, 0.406],
                                          std=[0.229, 0.224, 0.225])
    elif args.arch == "nasnet":
        scaler = 354
        img_size = 331
        norm_trans = transforms.Normalize(mean=[0.5, 0.5, 0.5],
                                          std=[0.5, 0.5, 0.5])
    else:
        scaler = 280
        img_size = 224
        norm_trans = transforms.Normalize(mean=[0.485, 0.456, 0.406],
                                          std=[0.229, 0.224, 0.225])

    train_transform = transforms.Compose([
        # transforms.Resize((img_size, img_size)),
        transforms.Resize(scaler),
        transforms.RandomResizedCrop(img_size),
        transforms.RandomRotation(degrees=args.random_angle,
                                  resample=Image.BILINEAR),
        transforms.RandomHorizontalFlip(),
        transforms.ToTensor(),
        norm_trans
    ])
    train_transform2 = transforms.Compose([
        # transforms.Resize((img_size, img_size)),
        transforms.Resize(img_size),
        transforms.CenterCrop(img_size),
        transforms.ToTensor(),
        norm_trans
    ])
    test_transform = transforms.Compose([
        # transforms.Resize((img_size, img_size)),
        transforms.Resize(scaler),
        transforms.CenterCrop(img_size),
        transforms.ToTensor(),
        norm_trans
    ])

    # create train and test datasets
    fp_train = Path(args.train_path)
    fp_test = Path(args.test_path)
    df_train = pd.read_csv(args.train_labels_path, index_col=0)
    df_test = pd.read_csv('./data/kaggle_dbi/sample_submission.csv',
                          index_col=0)

    if args.ensemble == 1:
        trainODDataset = DBI_dataset_ensemble(fp_train,
                                              df_train,
                                              df_test,
                                              is_train=True,
                                              transform=train_transform2)
        testODDataset = DBI_dataset_ensemble(fp_test,
                                             df_train,
                                             df_test,
                                             is_train=False,
                                             transform=test_transform)
    else:
        trainODDataset = DBI_dataset(fp_train,
                                     df_train,
                                     df_test,
                                     args.oxford_augment,
                                     is_train=True,
                                     transform=train_transform2)
        testODDataset = DBI_dataset(fp_test,
                                    df_train,
                                    df_test,
                                    args.oxford_augment,
                                    is_train=False,
                                    transform=test_transform)

    train_loader_all = torch.utils.data.DataLoader(trainODDataset,
                                                   batch_size=args.train_batch,
                                                   shuffle=False)
    test_indices = list(range(args.test_batch))
    test_sampler = SubsetRandomSampler(test_indices)
    if args.debug_mode == 1:
        test_loader = torch.utils.data.DataLoader(testODDataset,
                                                  batch_size=args.test_batch,
                                                  sampler=test_sampler,
                                                  shuffle=False)
    else:
        test_loader = torch.utils.data.DataLoader(testODDataset,
                                                  batch_size=args.test_batch,
                                                  shuffle=False)

    # load pretrained model for a soft start
    out_size = 120
    print("=> using pre-trained model '{}'".format(args.arch))
    if args.arch == 'inception':
        # model = models.inception_v3(pretrained=True, aux_logits=False)
        model = models.inception_v3(pretrained=True)
        num_ftrs = model.fc.in_features
        model.fc = nn.Linear(num_ftrs, out_size)
        num_ftrs = model.AuxLogits.fc.in_features
        model.AuxLogits.fc = nn.Linear(num_ftrs, out_size)
    elif args.arch == 'resnet18':
        # model = models.resnet18(pretrained=True)
        model = models.resnet101(pretrained=True)
        # model = models.alexnet(pretrained=True)
        # model = models.vgg16(pretrained=True)
        # switch to True if using VGG/ alexnet
        if False:
            model.classifier[6] = nn.Linear(4096, out_size)
        else:
            num_ftrs = model.fc.in_features
            model.fc = nn.Linear(num_ftrs, out_size)
    elif args.arch == 'nasnet':
        model = pretrainedmodels.nasnetalarge(num_classes=1000,
                                              pretrained='imagenet')
        if args.freezeLayers == 'all':
            for param in model.parameters():
                param.requires_grad = False
        num_ftrs = model.last_linear.in_features
        if args.transfer_oxford:
            model.last_linear = nn.Linear(num_ftrs, 25)
        else:
            model.last_linear = nn.Linear(num_ftrs, out_size)
    else:
        raise NotImplementedError(
            "only inception_v3, nanset and resnet18 are available at the moment"
        )
    # set up loss function
    criterion = nn.CrossEntropyLoss()

    # load pretrained oxford pets model
    if args.transfer_oxford:
        # Load checkpoint.
        print('==> Resuming from pretrained oxford pets checkpoint..')
        logger.info('==> Resuming from pretrained oxford pets checkpoint..')
        assert os.path.isfile(
            args.transfer_oxford), 'Error: no checkpoint directory found!'
        checkpoint = torch.load(args.transfer_oxford)
        model.load_state_dict(checkpoint['state_dict'])
        num_ftrs = model.last_linear.in_features
        model.last_linear = nn.Linear(num_ftrs, out_size)

    # move models to GPU
    if use_cuda:
        print("using gpu")
        model.cuda()
        criterion.cuda()

    print('Total params: %.2fM' %
          (sum(p.numel() for p in model.parameters()) / 1000000.0))

    # define optimizer
    # optimizer = optim.SGD(model.parameters(), lr=args.lr, momentum=args.momentum, weight_decay=args.weight_decay)
    params_to_update = []
    for name, param in model.named_parameters():
        if param.requires_grad:
            params_to_update.append(param)
            print("\t", name)
    optimizer = optim.Adam(params_to_update,
                           lr=args.lr,
                           weight_decay=args.weight_decay)
    scheduler = optim.lr_scheduler.ReduceLROnPlateau(optimizer,
                                                     'min',
                                                     patience=8,
                                                     verbose=True,
                                                     factor=args.lr_reduce)

    # do model ensemble
    if args.ensemble == 1:
        print('==> doing ensemble..')
        logger.info('==> doing ensemble..')
        model_b = pretrainedmodels.nasnetalarge(num_classes=1000,
                                                pretrained='imagenet')
        num_ftrs = model_b.last_linear.in_features
        model_b.last_linear = nn.Linear(num_ftrs, out_size)
        assert os.path.isfile(
            args.ensemble_path_a), 'Error: no checkpoint directory found!'
        checkpointa = torch.load(args.ensemble_path_a)
        checkpointb = torch.load(args.ensemble_path_b)
        model.load_state_dict(checkpointa['state_dict'])
        model_b.load_state_dict(checkpointb['state_dict'])
        model.cuda()
        model_b.cuda()

    # continue training of a model
    if args.resume:
        # Load checkpoint.
        print('==> Resuming from checkpoint..')
        logger.info('==> Resuming from checkpoint..')
        assert os.path.isfile(
            args.resume), 'Error: no checkpoint directory found!'
        # args.checkpoint = os.path.dirname(args.resume)
        checkpoint = torch.load(args.resume)
        best_val_loss = checkpoint['best_val_loss']
        start_epoch = checkpoint['epoch']
        model.load_state_dict(checkpoint['state_dict'])
        optimizer.load_state_dict(checkpoint['optimizer'])

    # print network arch to decide how many layers to freeze
    # for id, child in enumerate(model.children()):
    #     for name, param in child.named_parameters():
    #         print(id, ': ', name)
    # freeze layers
    if args.freezeLayers == 'none':
        print("not freezing anything")
        logger.info("not freezing anything")
    elif args.freezeLayers == 'half':
        print("freezing first layers only")
        logger.info("freezing first layers only")
        if args.arch == 'inception':
            special_child = args.freezeLayersNum
        elif args.arch == 'resnet18':
            special_child = args.freezeLayersNum
        elif args.arch == 'nasnet':
            special_child = args.freezeLayersNum
            # special_child = 19
        child_counter = 0
        for child in model.children():
            if child_counter < special_child:
                for param in child.parameters():
                    param.requires_grad = False
            else:
                break
            child_counter += 1
    elif args.freezeLayers == 'all':
        print("freezing all layers except for the top layer")
        logger.info("freezing all layers except for the top layer")
        last_child = 0
        if args.arch == 'inception':
            last_child = 17
        elif args.arch == 'resnet18':
            last_child = 9
        child_counter = 0
        for child in model.children():
            if child_counter < last_child:
                for param in child.parameters():
                    param.requires_grad = False
            else:
                break
            child_counter += 1
    else:
        raise NotImplementedError(
            "only none/ half/ all options are available at the moment")

    # save feature vecs for better runtime on long epochs training
    if args.use_saved_feature_vecs == 1:
        args.epochs = 10000
        # load base model pretrained on kaggle into memory
        if args.pretrained_kaggle:
            checkpoint = torch.load(args.pretrained_kaggle)
            model.load_state_dict(checkpoint['state_dict'])
            model.eval()
        # load saved feature vectors and labels
        if args.file_path_inputs_npy_to_load:
            feature_vecs_inputs = np.load(args.file_path_inputs_npy_to_load)
            feature_vecs_targets = np.load(args.file_path_targets_npy_to_load)
        else:
            # if no saved feature vectors and labels are given then create those files for future use
            feature_vecs_inputs, feature_vecs_targets = get_feature_vecs(
                train_loader_all, model)
            file_path_inputs_npy_to_save = 'C:/Users/Alfassy/PycharmProjects/Dog_Breed_Identification/saved_models/kaggle_dbi/feature_vecs_inputs' + str(
                vis_file_out) + '.npy'
            np.save(file_path_inputs_npy_to_save, feature_vecs_inputs)
            file_path_targets_npy_to_save = 'C:/Users/Alfassy/PycharmProjects/Dog_Breed_Identification/saved_models/kaggle_dbi/feature_vecs_targets' + str(
                vis_file_out) + '.npy'
            np.save(file_path_targets_npy_to_save, feature_vecs_targets)

        # separate data to train and val
        validation_split = 0.2
        # num without oxford
        dataset_size = trainODDataset.img_num
        if args.debug_mode == 1:
            indices = list(range(int(dataset_size / 18)))
        else:
            indices = list(range(dataset_size))
        split = int(np.floor(validation_split * len(indices)))
        np.random.seed(args.manualSeed)
        np.random.shuffle(indices)
        train_indices, val_indices = indices[split:], indices[:split]
        x_train = feature_vecs_inputs[train_indices]
        x_val = feature_vecs_inputs[val_indices]
        y_train = feature_vecs_targets[train_indices]
        y_val = feature_vecs_targets[val_indices]
        # the dataset is divided to [kaggle, oxford] so if we wish, we'll add oxford to the train data only.
        if args.oxford_augment == 1:
            x_train = np.concatenate((x_train, feature_vecs_inputs[range(
                trainODDataset.img_num, trainODDataset.img_num_with_oxford)]))
            y_train = np.concatenate((y_train, feature_vecs_targets[range(
                trainODDataset.img_num, trainODDataset.img_num_with_oxford)]),
                                     axis=0)

        top_only_train_dataset = TensorDataset(torch.FloatTensor(x_train),
                                               torch.LongTensor(y_train))
        top_only_val_dataset = TensorDataset(torch.FloatTensor(x_val),
                                             torch.LongTensor(y_val))
        train_loader = DataLoader(top_only_train_dataset,
                                  batch_size=4096,
                                  shuffle=True)
        val_loader = DataLoader(top_only_val_dataset,
                                batch_size=4096,
                                shuffle=True)
        # initialize a top model consists of a fc and dropout
        classifierModel = DropClassifier()
        # if starting from a trained base_model, also load his FC layer as an initialization unless resume_top_module is given
        if args.pretrained_kaggle:
            assert os.path.isfile(args.pretrained_kaggle
                                  ), 'Error: no checkpoint directory found!'
            checkpoint = torch.load(args.pretrained_kaggle)

            # classifierModel.last_linear.load_state_dict(list(model.children())[-1].state_dict())
            classifierModel = DropClassifier(
                checkpoint['state_dict']['last_linear.weight'],
                checkpoint['state_dict']['last_linear.bias'])
            # classifierModel.last_linear.weight.data.fill_(checkpoint['state_dict']['last_linear.weight'])
            # classifierModel.last_linear.bias.data = checkpoint['state_dict']['last_linear.bias']
            print(torch.load(args.pretrained_kaggle)['state_dict'])
            print(classifierModel.last_linear.weight)
            del checkpoint
        # load saved top module
        if args.resume_top_module:
            classifierModel.load_state_dict(
                torch.load(args.resume_top_module)['state_dict'])
        # set up optimizer for top module
        optimizer = optim.Adam(classifierModel.parameters(), lr=args.lr)
        scheduler = optim.lr_scheduler.ReduceLROnPlateau(optimizer,
                                                         'min',
                                                         patience=200,
                                                         verbose=True,
                                                         factor=args.lr_reduce)
        classifierModel.cuda()
    else:
        # split train and val
        validation_split = 0.2
        dataset_size = trainODDataset.img_num
        if args.debug_mode == 1:
            indices = list(range(int(dataset_size / 18)))
        else:
            indices = list(range(dataset_size))
        split = int(np.floor(validation_split * len(indices)))
        np.random.seed(args.manualSeed)
        np.random.shuffle(indices)
        train_indices, val_indices = indices[split:], indices[:split]
        # the dataset is divided to [kaggle, oxford] so if we wish, we'll add oxford to the train data only.
        if args.oxford_augment == 1:
            train_indices = np.concatenate(
                (train_indices,
                 range(trainODDataset.img_num,
                       trainODDataset.img_num_with_oxford)),
                axis=0)
        # Creating PT data samplers and loaders:
        train_sampler = SubsetRandomSampler(train_indices)
        valid_sampler = SubsetRandomSampler(val_indices)
        train_loader = torch.utils.data.DataLoader(trainODDataset,
                                                   batch_size=args.train_batch,
                                                   sampler=train_sampler)
        val_loader = torch.utils.data.DataLoader(trainODDataset,
                                                 batch_size=args.train_batch,
                                                 sampler=valid_sampler)
    # evaluation only mode
    if args.evaluate == 1:
        print('\nEvaluation only')
        logger.info('\nEvaluation only')
        # not checking val loss and acc in ensemble mode
        if args.ensemble == 1:
            test(test_loader, model, model_b, None, use_cuda, df_test,
                 vis_file_out)
        else:
            # check val accuracy
            if args.use_saved_feature_vecs == 1:
                val_loss, val_acc, class_acc_list = val(
                    val_loader, classifierModel, criterion, use_cuda,
                    scheduler)
            else:
                val_loss, val_acc, class_acc_list = val(
                    val_loader, model, criterion, use_cuda, scheduler)
            print("Val Loss: {}, Val Acc: {}".format(val_loss, val_acc))
            logger.info("Val Loss: {}, Val Acc: {}".format(val_loss, val_acc))
            if args.print_per_class_acc:
                for class_ind, class_acc in enumerate(class_acc_list):
                    print('Class {} accuracy: {}'.format(class_ind, class_acc))
            # create copmpetition excel
            if args.use_saved_feature_vecs == 1:
                # feature_vecs_inputs, feature_vecs_targets = get_feature_vecs(test_loader, model, test_mode=True)
                # print(feature_vecs_targets)
                # top_only_test_dataset = DBI_dataset_test_time(torch.FloatTensor(feature_vecs_inputs), feature_vecs_targets)
                # test_loader = DataLoader(top_only_test_dataset, batch_size=4096, shuffle=False)
                test(test_loader, model, None, classifierModel, use_cuda,
                     df_test, vis_file_out)
            else:
                test(test_loader, model, None, None, use_cuda, df_test,
                     vis_file_out)
        return

    # Train and val main loop
    for epoch in range(start_epoch, args.epochs):
        print('\nEpoch: [%d | %d] LR: %f' %
              (epoch + 1, args.epochs, state['lr']))
        logger.info("Epoch: {} of {}, LR: {}".format(epoch + 1, args.epochs,
                                                     state['lr']))
        if args.use_saved_feature_vecs == 1:
            train_loss, train_acc = train(train_loader, classifierModel,
                                          criterion, optimizer, use_cuda)
        else:
            train_loss, train_acc = train(train_loader, model, criterion,
                                          optimizer, use_cuda)
        print("finished train, starting test on val set")
        logger.info("finished train, starting test on val set")
        if args.ensemble == 1:
            raise RuntimeError(
                "ensemble should only be ran in evaluation mode (evaluate=1)")
        else:
            if args.use_saved_feature_vecs == 1:
                val_loss, val_acc, class_acc_list = val(
                    val_loader, classifierModel, criterion, use_cuda,
                    scheduler)
            else:
                val_loss, val_acc, class_acc_list = val(
                    val_loader, model, criterion, use_cuda, scheduler)
        print("learning rate: {}".format(get_learning_rate(optimizer)))
        logger.info("learning rate: {}".format(get_learning_rate(optimizer)))
        print("train loss: {}, train accuracy: {}".format(
            train_loss, train_acc))
        logger.info("train loss: {}, train accuracy: {}".format(
            train_loss, train_acc))
        print("val loss: {}, val accuracy: {}".format(val_loss, val_acc))
        logger.info("val loss: {}, val accuracy: {}".format(val_loss, val_acc))
        if args.print_per_class_acc:
            for class_ind, class_acc in enumerate(class_acc_list):
                print('Class {} accuracy: {}'.format(class_ind, class_acc))
        # save model
        is_best = val_loss < best_val_loss
        best_val_loss = min(val_loss, best_val_loss)
        best_val_acc = max(val_acc, best_val_acc)
        filename = str(vis_file_out)
        if args.use_saved_feature_vecs == 1:
            save_checkpoint(
                {
                    'epoch': epoch + 1,
                    'state_dict': classifierModel.state_dict(),
                    'loss': val_loss,
                    'best_val_loss': best_val_loss,
                    'optimizer': optimizer.state_dict()
                },
                is_best,
                checkpoint=args.checkpoint,
                filename=filename)
        else:
            save_checkpoint(
                {
                    'epoch': epoch + 1,
                    'state_dict': model.state_dict(),
                    'acc': val_loss,
                    'best_val_loss': best_val_loss,
                    'optimizer': optimizer.state_dict()
                },
                is_best,
                checkpoint=args.checkpoint,
                filename=filename)

    # test_loss, test_acc = test(test_loader, model, criterion, use_cuda, scheduler, df_test, vis_file_out)
    print('Best val acc: {}'.format(best_val_acc))
    print('Best val loss: {}'.format(best_val_loss))
    logger.info('Best val acc: {}'.format(best_val_acc))
    logger.info('Best val loss: {}'.format(best_val_loss))
Esempio n. 12
0
import os
import torch
import torchvision
import torch.nn as nn
import torch.optim as optim
from torch.optim import lr_scheduler
import numpy as np
from torch.utils.data import Dataset
from torchvision import transforms, datasets
from torch.autograd import Variable
import matplotlib.pyplot as plt
import time
import pretrainedmodels


model_ft = pretrainedmodels.nasnetalarge(num_classes=1000, pretrained='imagenet')

data_transforms = {
    'train': transforms.Compose([
        transforms.RandomSizedCrop(331),
        transforms.RandomHorizontalFlip(),
        transforms.ToTensor(),
        transforms.Normalize(mean=model_ft.mean,
                             std=model_ft.std)
    ]),
    'val': transforms.Compose([
        transforms.Scale(377),
        transforms.CenterCrop(331),
        transforms.ToTensor(),
        transforms.Normalize(mean=model_ft.mean,
                             std=model_ft.std)
Esempio n. 13
0
def main():
    global best_val_acc
    start_epoch = args.start_epoch  # start from epoch 0 or last checkpoint epoch

    # Data loading code
    if args.arch == "inception":
        scaler = 350
        img_size = 299
        norm_trans = transforms.Normalize(
                mean=[0.485, 0.456, 0.406],
                std=[0.229, 0.224, 0.225]
            )
    elif args.arch == "nasnet":
        scaler = 354
        img_size = 331
        norm_trans = transforms.Normalize(
            mean=[0.5, 0.5, 0.5],
            std=[0.5, 0.5, 0.5]
        )
    else:
        scaler = 280
        img_size = 224
        norm_trans = transforms.Normalize(
            mean=[0.485, 0.456, 0.406],
            std=[0.229, 0.224, 0.225]
        )
    # train_transform = transforms.Compose(
    #     [
    #         # transforms.Resize((img_size, img_size)),
    #         transforms.Resize(scaler),
    #         transforms.RandomResizedCrop(img_size),
    #         transforms.RandomRotation(degrees=args.random_angle, resample=Image.BILINEAR),
    #         transforms.RandomHorizontalFlip(),
    #         transforms.ToTensor()
    #     ]
    # )
    train_transform = transforms.Compose(
        [
            # transforms.Resize((img_size, img_size)),
            transforms.Resize(scaler),
            transforms.RandomResizedCrop(img_size),
            transforms.RandomRotation(degrees=args.random_angle, resample=Image.BILINEAR),
            transforms.RandomHorizontalFlip(),
            transforms.ToTensor(),
            norm_trans
        ]
    )
    test_transform = transforms.Compose(
        [
            # transforms.Resize((img_size, img_size)),
            transforms.Resize(scaler),
            transforms.CenterCrop(img_size),
            transforms.ToTensor(),
            norm_trans
        ]
    )

    # create train and test datasets
    imgs_path = Path(args.images_path)
    trainODDataset = oxford_dataset(imgs_path, transform=train_transform)
    validation_split = 0.2
    dataset_size = len(trainODDataset)
    if args.debug_mode == 1:
        indices = list(range(int(dataset_size/18)))
    else:
        indices = list(range(dataset_size))
    split = int(np.floor(validation_split * len(indices)))
    np.random.seed(args.manualSeed)
    np.random.shuffle(indices)
    train_indices, val_indices = indices[split:], indices[:split]

    # Creating PT data samplers and loaders:
    train_sampler = SubsetRandomSampler(train_indices)
    valid_sampler = SubsetRandomSampler(val_indices)

    train_loader = torch.utils.data.DataLoader(trainODDataset, batch_size=args.train_batch,
                                               sampler=train_sampler)
    val_loader = torch.utils.data.DataLoader(trainODDataset, batch_size=args.train_batch,
                                             sampler=valid_sampler)

    out_size = 25
    print("=> using pre-trained model '{}'".format(args.arch))
    if args.arch == 'inception':
        # model = models.inception_v3(pretrained=True, aux_logits=False)
        model = models.inception_v3(pretrained=True)
        num_ftrs = model.fc.in_features
        model.fc = nn.Linear(num_ftrs, out_size)
        num_ftrs = model.AuxLogits.fc.in_features
        model.AuxLogits.fc = nn.Linear(num_ftrs, out_size)
    elif args.arch == 'resnet18':
        # model = models.resnet18(pretrained=True)
        model = models.resnet101(pretrained=True)
        # model = models.alexnet(pretrained=True)
        # model = models.vgg16(pretrained=True)
        # switch to True if using VGG/ alexnet
        if False:
            model.classifier[6] = nn.Linear(4096, out_size)
        else:
            num_ftrs = model.fc.in_features
            model.fc = nn.Linear(num_ftrs, out_size)
    elif args.arch == 'nasnet':
        # model = models.inception_v3(pretrained=True, aux_logits=False)
        model = pretrainedmodels.nasnetalarge(num_classes=1000, pretrained='imagenet')
        if args.freezeLayers == 'all':
            for param in model.parameters():
                param.requires_grad = False
        num_ftrs = model.last_linear.in_features
        model.last_linear = nn.Linear(num_ftrs, out_size)
    else:
        raise NotImplementedError("only inception_v3 and resnet18 are available at the moment")
    criterion = nn.CrossEntropyLoss()


    if use_cuda:
        print("using gpu")
        model.cuda()
        criterion.cuda()


    # cudnn.benchmark = True
    print('Total params: %.2fM' % (sum(p.numel() for p in model.parameters())/1000000.0))

    # define loss function (criterion) and optimizer
    # criterion = nn.CrossEntropyLoss().cuda()

    # optimizer = optim.SGD(model.parameters(), lr=args.lr, momentum=args.momentum, weight_decay=args.weight_decay)
    optimizer = optim.Adam(model.parameters(), lr=args.lr, weight_decay=args.weight_decay)
    scheduler = optim.lr_scheduler.ReduceLROnPlateau(optimizer, 'min', patience=8, verbose=True, factor=args.lr_reduce)
    env_name = args.env_name
    now = datetime.datetime.now()
    vis_file_out = env_name + str(now.year) + ';' + str(now.month) + ';' + \
                   str(now.day) + ';' + str(now.hour) + ';' + str(now.minute)
    # TODO: save to logs to disk
    # notes_logger = visdomLogger.VisdomTextLogger(update_type='APPEND', server=args.visdom_server, env=env_name)
    # notes_logger.log("Start time: {}".format(now))
    # notes_logger.log("Args: {}".format(args))
    # notes_logger.log("data lenght: {}".format(len(trainODDataset.trainImgPathLabels)))



    log_filename = './logs/' + str(vis_file_out) + '.txt'
    logger = configure_logging(log_filename)
    logger.info(args)

    if args.resume:
        # Load checkpoint.
        print('==> Resuming from checkpoint..')
        logger.info('==> Resuming from checkpoint..')
        assert os.path.isfile(args.resume), 'Error: no checkpoint directory found!'
        args.checkpoint = os.path.dirname(args.resume)
        checkpoint = torch.load(args.resume)
        best_val_acc = checkpoint['best_val_acc']
        start_epoch = checkpoint['epoch']
        model.load_state_dict(checkpoint['state_dict'])
        optimizer.load_state_dict(checkpoint['optimizer'])
    # print network arch to decide how many layers to freeze
    for id, child in enumerate(model.children()):
        for name, param in child.named_parameters():
            print(id, ': ', name)
    exit()
    # freeze layers
    if args.freezeLayers == 'none':
        print("not freezing anything")
        logger.info("not freezing anything")
    elif args.freezeLayers == 'half':
        print("freezing first layers only")
        logger.info("freezing first layers only")
        if args.arch == 'inception':
            special_child = 5
        elif args.arch == 'resnet18':
            special_child = 5
        elif args.arch == 'nasnet':
            special_child = args.freezeLayersNum
            # special_child = 19
        child_counter = 0
        for child in model.children():
            if child_counter < special_child:
                for param in child.parameters():
                    param.requires_grad = False
            else:
                break
            child_counter += 1
    elif args.freezeLayers == 'all':
        print("freezing all layers except for the top layer")
        logger.info("freezing all layers except for the top layer")
        if args.arch == 'inception':
            last_child = 17
        elif args.arch == 'resnet18':
            last_child = 9
        elif args.arch == 'nasnet':
            last_child = 0
        child_counter = 0
        for child in model.children():
            if child_counter < last_child:
                for param in child.parameters():
                    param.requires_grad = False
            else:
                break
            child_counter += 1
    else:
        raise NotImplementedError("only none/ half/ all options are available at the moment")

    if args.evaluate == 1:
        print('\nEvaluation only')
        logger.info('\nEvaluation only')

        val_loss, val_acc = val(val_loader, model, criterion, use_cuda, scheduler)
        print("Val Loss: {}, Val Acc: {}".format(val_loss, val_acc))
        logger.info("Val Loss: {}, Val Acc: {}".format(val_loss, val_acc))
        return

    # Train and val
    for epoch in range(start_epoch, args.epochs):
        print('\nEpoch: [%d | %d] LR: %f' % (epoch + 1, args.epochs, state['lr']))
        # notes_logger.log("Epoch: {} of {}, LR: {}".format(epoch+1, args.epochs, state['lr']))
        logger.info("Epoch: {} of {}, LR: {}".format(epoch+1, args.epochs, state['lr']))

        train_loss, train_acc = train(train_loader, model, criterion, optimizer, epoch, use_cuda)
        print("finished train, starting test")
        logger.info("finished train, starting test")
        val_loss, val_acc = val(val_loader, model, criterion, use_cuda, scheduler)
        # log loss and accuracy
        print("learning rate: {}".format(get_learning_rate(optimizer)))
        logger.info("learning rate: {}".format(get_learning_rate(optimizer)))
        print("train loss: {}, train accuracy: {}".format(train_loss, train_acc))
        logger.info("train loss: {}, train accuracy: {}".format(train_loss, train_acc))
        print("val loss: {}, val accuracy: {}".format(val_loss, val_acc))
        logger.info("val loss: {}, val accuracy: {}".format(val_loss, val_acc))

        # save model
        is_best = val_acc > best_val_acc
        best_val_acc = max(val_acc, best_val_acc)
        filename = str(vis_file_out)
        save_checkpoint({'epoch': epoch + 1, 'state_dict': model.state_dict(), 'acc': val_acc, 'best_val_acc': best_val_acc,
                         'optimizer': optimizer.state_dict()
                         }, is_best, epoch, checkpoint=args.checkpoint, filename=filename)

    print('Best val acc: '.format(best_val_acc))
    logger.info('Best val acc: '.format(best_val_acc))
Esempio n. 14
0
def create_model(model_key, pretrained, num_of_classes, use_gpu):
    """Create CNN model

    Args:
        model_key (str): Name of the model to be created.
        pretrained: If True, only train the laster layer.
        num_of_classes (int): Number of categories of outputs.

    Returns:
        model_conv: A defined model to be train

    Raises:
        ValueError: Error while asking an unrecognized model.
    """
    if model_key == 'resnet18':
        model_conv = torchvision.models.resnet18(pretrained=True)
    elif model_key == 'resnet34':
        model_conv = torchvision.models.resnet34(pretrained=True)
    elif model_key == 'resnet50':
        model_conv = torchvision.models.resnet50(pretrained=True)
    elif model_key == 'resnet101':
        model_conv = torchvision.models.resnet101(pretrained=True)
    elif model_key == 'inception_v3':
        model_conv = torchvision.models.inception_v3(pretrained=True)
    elif model_key == 'inceptionresnetv2':
        model_conv = pretrainedmodels.inceptionresnetv2(num_classes=1000,
                                                        pretrained='imagenet')
    elif model_key == 'inceptionv4':
        model_conv = pretrainedmodels.inceptionv4(num_classes=1000,
                                                  pretrained='imagenet')
    elif model_key == 'nasnetalarge':
        model_conv = pretrainedmodels.nasnetalarge(num_classes=1000,
                                                   pretrained='imagenet')
    else:
        raise ValueError("Unrecognized name of model {}".format(model_key))

    if pretrained:
        # Lock parameters for transfer learning
        for param in model_conv.parameters():
            param.requires_grad = False

    # Parameters of newly constructed modules have requires_grad=True by default
    if model_key == 'nasnetalarge' or model_key == 'inceptionresnetv2' or model_key == 'inceptionv4':
        dim_feats = model_conv.last_linear.in_features  # 2048
        model_conv.last_linear = nn.Linear(dim_feats, num_of_classes)
    else:
        num_ftrs = model_conv.fc.in_features
        model_conv.fc = nn.Linear(num_ftrs, num_of_classes)

    # Initialize newly added module parameters
    if model_key == 'nasnetalarge' or model_key == 'inceptionresnetv2' or model_key == 'inceptionv4':
        nn.init.xavier_uniform(model_conv.last_linear.weight)
        nn.init.constant(model_conv.last_linear.bias, 0)
    else:
        nn.init.xavier_uniform(model_conv.fc.weight)
        nn.init.constant(model_conv.fc.bias, 0)

    if use_gpu:
        model_conv = model_conv.cuda()

    return model_conv
Esempio n. 15
0
def main():
    # 随机种子
    np.random.seed(666)
    torch.manual_seed(666)
    torch.cuda.manual_seed_all(666)
    random.seed(666)

    # 获取当前文件名,用于创建模型及结果文件的目录
    file_name = os.path.basename(__file__).split('.')[0]
    # 创建保存模型和结果的文件夹
    if not os.path.exists('./model/%s' % file_name):
        os.makedirs('./model/%s' % file_name)
    if not os.path.exists('./result/%s' % file_name):
        os.makedirs('./result/%s' % file_name)
    # 创建日志文件
    if not os.path.exists('./result/%s.txt' % file_name):
        with open('./result/%s.txt' % file_name, 'w') as acc_file:
            pass
    with open('./result/%s.txt' % file_name, 'a') as acc_file:
        acc_file.write('\n%s %s\n' % (time.strftime(
            "%Y-%m-%d %H:%M:%S", time.localtime(time.time())), file_name))

    # 默认使用PIL读图
    def default_loader(path):
        # return Image.open(path)
        return Image.open(path).convert('RGB')

    # 训练集图片读取
    class TrainDataset(Dataset):
        def __init__(self,
                     label_list,
                     transform=None,
                     target_transform=None,
                     loader=default_loader):
            imgs = []
            for index, row in label_list.iterrows():
                imgs.append((row['img_path'], row['label']))
            self.imgs = imgs
            self.transform = transform
            self.target_transform = target_transform
            self.loader = loader

        def __getitem__(self, index):
            filename, label = self.imgs[index]
            img = self.loader(filename)
            if self.transform is not None:
                img = self.transform(img)
            return img, label

        def __len__(self):
            return len(self.imgs)

    # 验证集图片读取
    class ValDataset(Dataset):
        def __init__(self,
                     label_list,
                     transform=None,
                     target_transform=None,
                     loader=default_loader):
            imgs = []
            for index, row in label_list.iterrows():
                imgs.append((row['img_path'], row['label']))
            self.imgs = imgs
            self.transform = transform
            self.target_transform = target_transform
            self.loader = loader

        def __getitem__(self, index):
            filename, label = self.imgs[index]
            img = self.loader(filename)
            if self.transform is not None:
                img = self.transform(img)
            return img, label

        def __len__(self):
            return len(self.imgs)

    # 测试集图片读取
    class TestDataset(Dataset):
        def __init__(self,
                     label_list,
                     transform=None,
                     target_transform=None,
                     loader=default_loader):
            imgs = []
            for index, row in label_list.iterrows():
                imgs.append((row['img_path']))
            self.imgs = imgs
            self.transform = transform
            self.target_transform = target_transform
            self.loader = loader

        def __getitem__(self, index):
            filename = self.imgs[index]
            img = self.loader(filename)
            if self.transform is not None:
                img = self.transform(img)
            return img, filename

        def __len__(self):
            return len(self.imgs)

    # 数据增强:在给定角度中随机进行旋转
    class FixedRotation(object):
        def __init__(self, angles):
            self.angles = angles

        def __call__(self, img):
            return fixed_rotate(img, self.angles)

    def fixed_rotate(img, angles):
        angles = list(angles)
        angles_num = len(angles)
        index = random.randint(0, angles_num - 1)
        return img.rotate(angles[index])

    # 训练函数
    def train(train_loader, model, criterion, optimizer, epoch):
        batch_time = AverageMeter()
        data_time = AverageMeter()
        losses = AverageMeter()
        acc = AverageMeter()

        # switch to train mode
        model.train()

        end = time.time()
        # 从训练集迭代器中获取训练数据
        for i, (images, target) in enumerate(train_loader):
            # 评估图片读取耗时
            data_time.update(time.time() - end)
            # 将图片和标签转化为tensor
            image_var = torch.tensor(images).cuda(async=True)
            label = torch.tensor(target).cuda(async=True)

            # 将图片输入网络,前传,生成预测值
            y_pred = model(image_var)
            # 计算loss
            loss = criterion(y_pred, label)
            losses.update(loss.item(), images.size(0))

            # 计算top1正确率
            prec, PRED_COUNT = accuracy(y_pred.data, target, topk=(1, 1))
            acc.update(prec, PRED_COUNT)

            # 对梯度进行反向传播,使用随机梯度下降更新网络权重
            optimizer.zero_grad()
            loss.backward()
            optimizer.step()

            # 评估训练耗时
            batch_time.update(time.time() - end)
            end = time.time()

            # 打印耗时与结果
            if i % print_freq == 0:
                print('Epoch: [{0}][{1}/{2}]\t'
                      'Time {batch_time.val:.3f} ({batch_time.avg:.3f})\t'
                      'Data {data_time.val:.3f} ({data_time.avg:.3f})\t'
                      'Loss {loss.val:.4f} ({loss.avg:.4f})\t'
                      'Accuray {acc.val:.3f} ({acc.avg:.3f})'.format(
                          epoch,
                          i,
                          len(train_loader),
                          batch_time=batch_time,
                          data_time=data_time,
                          loss=losses,
                          acc=acc))

    # 验证函数
    def validate(val_loader, model, criterion):
        batch_time = AverageMeter()
        losses = AverageMeter()
        acc = AverageMeter()

        # switch to evaluate mode
        model.eval()

        end = time.time()
        for i, (images, labels) in enumerate(val_loader):
            image_var = torch.tensor(images).cuda(async=True)
            target = torch.tensor(labels).cuda(async=True)

            # 图片前传。验证和测试时不需要更新网络权重,所以使用torch.no_grad(),表示不计算梯度
            with torch.no_grad():
                y_pred = model(image_var)
                loss = criterion(y_pred, target)

            # measure accuracy and record loss
            prec, PRED_COUNT = accuracy(y_pred.data, labels, topk=(1, 1))
            losses.update(loss.item(), images.size(0))
            acc.update(prec, PRED_COUNT)

            # measure elapsed time
            batch_time.update(time.time() - end)
            end = time.time()

            if i % print_freq == 0:
                print('TrainVal: [{0}/{1}]\t'
                      'Time {batch_time.val:.3f} ({batch_time.avg:.3f})\t'
                      'Loss {loss.val:.4f} ({loss.avg:.4f})\t'
                      'Accuray {acc.val:.3f} ({acc.avg:.3f})'.format(
                          i,
                          len(val_loader),
                          batch_time=batch_time,
                          loss=losses,
                          acc=acc))

        print(' * Accuray {acc.avg:.3f}'.format(acc=acc),
              '(Previous Best Acc: %.3f)' % best_precision,
              ' * Loss {loss.avg:.3f}'.format(loss=losses),
              'Previous Lowest Loss: %.3f)' % lowest_loss)
        return acc.avg, losses.avg

    # 测试函数
    def test(test_loader, model):
        csv_map = OrderedDict({'filename': [], 'probability': []})
        # switch to evaluate mode
        model.eval()
        for i, (images, filepath) in enumerate(tqdm(test_loader)):
            # bs, ncrops, c, h, w = images.size()
            filepath = [os.path.basename(i) for i in filepath]
            image_var = torch.tensor(images,
                                     requires_grad=False)  # for pytorch 0.4

            with torch.no_grad():
                y_pred = model(image_var)
                # 使用softmax函数将图片预测结果转换成类别概率
                smax = nn.Softmax(1)
                smax_out = smax(y_pred)

            # 保存图片名称与预测概率
            csv_map['filename'].extend(filepath)
            for output in smax_out:
                prob = ';'.join([str(i) for i in output.data.tolist()])
                csv_map['probability'].append(prob)
        # pdb.set_trace()
        result = pd.DataFrame(csv_map)
        result['probability'] = result['probability'].map(
            lambda x: [float(i) for i in x.split(';')])

        # 转换成提交样例中的格式
        sub_filename, sub_label = [], []
        prob_list = []
        for index, row in result.iterrows():
            sub_filename.append(row['filename'])
            pred_label = np.argmax(row['probability'])
            prob_list.append([row['filename']] + row['probability'])
            if pred_label == 0:
                sub_label.append('norm')
            else:
                sub_label.append('defect%d' % pred_label)

        # 生成结果文件,保存在result文件夹中,可用于直接提交
        submission = pd.DataFrame({
            'filename': sub_filename,
            'label': sub_label
        })
        submission.to_csv('./result/%s/submission.csv' % file_name,
                          header=None,
                          index=False)

        prob_map = pd.DataFrame(prob_list)
        prob_map.to_csv('./result/%s/prob_map.csv' % file_name,
                        header=False,
                        index=False)
        return

    # 保存最新模型以及最优模型
    def save_checkpoint(state,
                        is_best,
                        is_lowest_loss,
                        filename='./model/%s/checkpoint.pth.tar' % file_name):
        torch.save(state, filename)
        if is_best:
            shutil.copyfile(filename,
                            './model/%s/model_best.pth.tar' % file_name)
        if is_lowest_loss:
            shutil.copyfile(filename,
                            './model/%s/lowest_loss.pth.tar' % file_name)

    # 用于计算精度和时间的变化
    class AverageMeter(object):
        """Computes and stores the average and current value"""
        def __init__(self):
            self.reset()

        def reset(self):
            self.val = 0
            self.avg = 0
            self.sum = 0
            self.count = 0

        def update(self, val, n=1):
            self.val = val
            self.sum += val * n
            self.count += n
            self.avg = self.sum / self.count

    # 学习率衰减:lr = lr / lr_decay
    def adjust_learning_rate():
        nonlocal lr
        lr = lr / lr_decay
        return optim.Adam(model.parameters(),
                          lr,
                          weight_decay=weight_decay,
                          amsgrad=True)

    # 计算top K准确率
    def accuracy(y_pred, y_actual, topk=(1, )):
        """Computes the precision@k for the specified values of k"""
        final_acc = 0
        maxk = max(topk)
        # for prob_threshold in np.arange(0, 1, 0.01):
        PRED_COUNT = y_actual.size(0)
        PRED_CORRECT_COUNT = 0
        prob, pred = y_pred.topk(maxk, 1, True, True)
        # prob = np.where(prob > prob_threshold, prob, 0)
        for j in range(pred.size(0)):
            if int(y_actual[j]) == int(pred[j]):
                PRED_CORRECT_COUNT += 1
        if PRED_COUNT == 0:
            final_acc = 0
        else:
            final_acc = PRED_CORRECT_COUNT / PRED_COUNT
        return final_acc * 100, PRED_COUNT

    # 程序主体

    # 设定GPU ID
    os.environ["CUDA_VISIBLE_DEVICES"] = '0, 1'
    # 小数据集上,batch size不易过大。如出现out of memory,应调小batch size
    batch_size = 12
    # 进程数量,最好不要超过电脑最大进程数。windows下报错可以改为workers=0
    workers = 0

    # epoch数量,分stage进行,跑完一个stage后降低学习率进入下一个stage
    stage_epochs = [20, 10, 10]
    # 初始学习率
    lr = 1e-4
    # 学习率衰减系数 (new_lr = lr / lr_decay)
    lr_decay = 5
    # 正则化系数
    weight_decay = 1e-4

    # 参数初始化
    stage = 0
    start_epoch = 0
    total_epochs = sum(stage_epochs)
    best_precision = 0
    lowest_loss = 100

    # 设定打印频率,即多少step打印一次,用于观察loss和acc的实时变化
    # 打印结果中,括号前面为实时loss和acc,括号内部为epoch内平均loss和acc
    print_freq = 1
    # 验证集比例
    val_ratio = 0.12
    # 是否只验证,不训练
    evaluate = True
    # 是否从断点继续跑
    resume = False
    # 创建Nasnet模型
    # xception.pretrained_settings['xception']['imagenet']['num_classes']=12
    model = nasnetalarge(num_classes=1000)
    model.last_linear = nn.Linear(4032, 12)
    print(model)
    model = torch.nn.DataParallel(model).cuda()

    # optionally resume from a checkpoint
    if resume:
        checkpoint_path = './model/%s/checkpoint.pth.tar' % file_name
        if os.path.isfile(checkpoint_path):
            print("=> loading checkpoint '{}'".format(checkpoint_path))
            checkpoint = torch.load(checkpoint_path)
            start_epoch = checkpoint['epoch'] + 1
            best_precision = checkpoint['best_precision']
            lowest_loss = checkpoint['lowest_loss']
            stage = checkpoint['stage']
            lr = checkpoint['lr']
            model.load_state_dict(checkpoint['state_dict'])
            # 如果中断点恰好为转换stage的点,需要特殊处理
            if start_epoch in np.cumsum(stage_epochs)[:-1]:
                stage += 1
                optimizer = adjust_learning_rate()
                model.load_state_dict(
                    torch.load('./model/%s/model_best.pth.tar' %
                               file_name)['state_dict'])
            print("=> loaded checkpoint (epoch {})".format(
                checkpoint['epoch']))
        else:
            print("=> no checkpoint found at '{}'".format(checkpoint_path))

    # 读取训练图片列表
    all_data = pd.read_csv('data/label.csv')
    # 分离训练集和测试集,stratify参数用于分层抽样
    train_data_list, val_data_list = train_test_split(
        all_data,
        test_size=val_ratio,
        random_state=666,
        stratify=all_data['label'])
    # 读取测试图片列表
    test_data_list = pd.read_csv('data/test.csv')

    # 图片归一化,由于采用ImageNet预训练网络,因此这里直接采用ImageNet网络的参数
    normalize = transforms.Normalize(mean=[0.485, 0.456, 0.406],
                                     std=[0.229, 0.224, 0.225])

    # 训练集图片变换,输入网络的尺寸为354*354
    train_data = TrainDataset(
        train_data_list,
        transform=transforms.Compose([
            transforms.Resize((360, 360)),
            transforms.ColorJitter(0.15, 0.15, 0.15, 0.075),
            transforms.RandomHorizontalFlip(),
            transforms.RandomGrayscale(),
            # transforms.RandomRotation(20),
            FixedRotation([0, 90, 180, 270]),
            transforms.RandomCrop(354),
            transforms.ToTensor(),
            normalize,
        ]))

    # 验证集图片变换
    val_data = ValDataset(val_data_list,
                          transform=transforms.Compose([
                              transforms.Resize((360, 360)),
                              transforms.CenterCrop(354),
                              transforms.ToTensor(),
                              normalize,
                          ]))

    # 测试集图片变换
    test_data = TestDataset(test_data_list,
                            transform=transforms.Compose([
                                transforms.Resize((360, 360)),
                                transforms.CenterCrop(354),
                                transforms.ToTensor(),
                                normalize,
                            ]))

    # 生成图片迭代器
    train_loader = DataLoader(train_data,
                              batch_size=batch_size,
                              shuffle=True,
                              pin_memory=True,
                              num_workers=workers)
    val_loader = DataLoader(val_data,
                            batch_size=batch_size * 2,
                            shuffle=False,
                            pin_memory=False,
                            num_workers=workers)
    test_loader = DataLoader(test_data,
                             batch_size=batch_size * 2,
                             shuffle=False,
                             pin_memory=False,
                             num_workers=workers)

    # 使用交叉熵损失函数
    criterion = nn.CrossEntropyLoss().cuda()

    # 优化器,使用带amsgrad的Adam
    optimizer = optim.Adam(model.parameters(),
                           lr,
                           weight_decay=weight_decay,
                           amsgrad=True)

    if evaluate:
        pass
        # validate(val_loader, model, criterion)
    else:
        # 开始训练
        for epoch in range(start_epoch, total_epochs):
            # train for one epoch
            train(train_loader, model, criterion, optimizer, epoch)
            # evaluate on validation set
            precision, avg_loss = validate(val_loader, model, criterion)

            # 在日志文件中记录每个epoch的精度和loss
            with open('./result/%s.txt' % file_name, 'a') as acc_file:
                acc_file.write('Epoch: %2d, Precision: %.8f, Loss: %.8f\n' %
                               (epoch, precision, avg_loss))

            # 记录最高精度与最低loss,保存最新模型与最佳模型
            is_best = precision > best_precision
            is_lowest_loss = avg_loss < lowest_loss
            best_precision = max(precision, best_precision)
            lowest_loss = min(avg_loss, lowest_loss)
            state = {
                'epoch': epoch,
                'state_dict': model.state_dict(),
                'best_precision': best_precision,
                'lowest_loss': lowest_loss,
                'stage': stage,
                'lr': lr,
            }
            save_checkpoint(state, is_best, is_lowest_loss)

            # 判断是否进行下一个stage
            if (epoch + 1) in np.cumsum(stage_epochs)[:-1]:
                stage += 1
                optimizer = adjust_learning_rate()
                model.load_state_dict(
                    torch.load('./model/%s/model_best.pth.tar' %
                               file_name)['state_dict'])
                print('Step into next stage')
                with open('./result/%s.txt' % file_name, 'a') as acc_file:
                    acc_file.write(
                        '---------------Step into next stage----------------\n'
                    )

    # 记录线下最佳分数
    with open('./result/%s.txt' % file_name, 'a') as acc_file:
        acc_file.write('* best acc: %.8f  %s\n' %
                       (best_precision, os.path.basename(__file__)))
    with open('./result/best_acc.txt', 'a') as acc_file:
        acc_file.write(
            '%s  * best acc: %.8f  %s\n' %
            (time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(
                time.time())), best_precision, os.path.basename(__file__)))

    # 读取最佳模型,预测测试集,并生成可直接提交的结果文件
    best_model = torch.load('./model/%s/checkpoint.pth.tar' % file_name)
    model.load_state_dict(best_model['state_dict'])
    test(test_loader=test_loader, model=model)

    # 释放GPU缓存
    torch.cuda.empty_cache()
def main(train_root, train_csv, val_root, val_csv, test_root, test_csv,
         epochs, aug, model_name, batch_size, num_workers, val_samples,
         test_samples, early_stopping_patience, limit_data, images_per_epoch,
         split_id, _run):
    assert(model_name in ('inceptionv4', 'resnet152', 'densenet161',
                          'senet154', 'pnasnet5large', 'nasnetalarge',
                          'xception', 'squeezenet', 'resnext', 'dpn',
                          'inceptionresnetv2', 'mobilenetv2'))

    cv2.setNumThreads(0)

    AUGMENTED_IMAGES_DIR = os.path.join(fs_observer.dir, 'images')
    CHECKPOINTS_DIR = os.path.join(fs_observer.dir, 'checkpoints')
    BEST_MODEL_PATH = os.path.join(CHECKPOINTS_DIR, 'model_best.pth')
    LAST_MODEL_PATH = os.path.join(CHECKPOINTS_DIR, 'model_last.pth')
    RESULTS_CSV_PATH = os.path.join('results', 'results.csv')
    EXP_NAME = _run.meta_info['options']['--name']
    EXP_ID = _run._id

    for directory in (AUGMENTED_IMAGES_DIR, CHECKPOINTS_DIR):
        os.makedirs(directory)

    device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

    if model_name == 'inceptionv4':
        model = ptm.inceptionv4(num_classes=1000, pretrained='imagenet')
        model.last_linear = nn.Linear(model.last_linear.in_features, 2)
        aug['size'] = 299
        aug['mean'] = model.mean
        aug['std'] = model.std
    elif model_name == 'resnet152':
        model = models.resnet152(pretrained=True)
        model.fc = nn.Linear(model.fc.in_features, 2)
        aug['size'] = 224
        aug['mean'] = [0.485, 0.456, 0.406]
        aug['std'] = [0.229, 0.224, 0.225]
    elif model_name == 'densenet161':
        model = models.densenet161(pretrained=True)
        model.classifier = nn.Linear(model.classifier.in_features, 2)
        aug['size'] = 224
        aug['mean'] = [0.485, 0.456, 0.406]
        aug['std'] = [0.229, 0.224, 0.225]
    elif model_name == 'senet154':
        model = ptm.senet154(num_classes=1000, pretrained='imagenet')
        model.last_linear = nn.Linear(model.last_linear.in_features, 2)
        aug['size'] = model.input_size[1]
        aug['mean'] = model.mean
        aug['std'] = model.std
    elif model_name == 'squeezenet':
        model = ptm.squeezenet1_1(num_classes=1000, pretrained='imagenet')
        model.last_conv = nn.Conv2d(
            512, 2, kernel_size=(1, 1), stride=(1, 1))
        aug['size'] = model.input_size[1]
        aug['mean'] = model.mean
        aug['std'] = model.std
    elif model_name == 'pnasnet5large':
        model = ptm.pnasnet5large(num_classes=1000, pretrained='imagenet')
        model.last_linear = nn.Linear(model.last_linear.in_features, 2)
        aug['size'] = model.input_size[1]
        aug['mean'] = model.mean
        aug['std'] = model.std
    elif model_name == 'nasnetalarge':
        model = ptm.nasnetalarge(num_classes=1000, pretrained='imagenet')
        model.last_linear = nn.Linear(model.last_linear.in_features, 2)
        aug['size'] = model.input_size[1]
        aug['mean'] = model.mean
        aug['std'] = model.std
    elif model_name == 'xception':
        model = ptm.xception(num_classes=1000, pretrained='imagenet')
        model.last_linear = nn.Linear(model.last_linear.in_features, 2)
        aug['size'] = model.input_size[1]
        aug['mean'] = model.mean
        aug['std'] = model.std
    elif model_name == 'dpn':
        model = ptm.dpn131(num_classes=1000, pretrained='imagenet')
        model.last_linear = nn.Conv2d(model.last_linear.in_channels, 2,
                                      kernel_size=1, bias=True)
        aug['size'] = model.input_size[1]
        aug['mean'] = model.mean
        aug['std'] = model.std
    elif model_name == 'resnext':
        model = ptm.resnext101_64x4d(num_classes=1000, pretrained='imagenet')
        model.last_linear = nn.Linear(model.last_linear.in_features, 2)
        aug['size'] = model.input_size[1]
        aug['mean'] = model.mean
        aug['std'] = model.std
    elif model_name == 'inceptionresnetv2':
        model = ptm.inceptionresnetv2(num_classes=1000, pretrained='imagenet')
        model.last_linear = nn.Linear(model.last_linear.in_features, 2)
        aug['size'] = model.input_size[1]
        aug['mean'] = model.mean
        aug['std'] = model.std
    elif model_name == 'mobilenetv2':
        model = MobileNetV2()
        model.load_state_dict(torch.load('./auglib/models/mobilenet_v2.pth'))
        model.classifier = nn.Sequential(
            nn.Dropout(0.2),
            nn.Linear(model.last_channel, 2),
        )
        aug['size'] = 224
        aug['mean'] = [0.485, 0.456, 0.406]
        aug['std'] = [0.229, 0.224, 0.225]
    model.to(device)

    augs = Augmentations(**aug)
    model.aug_params = aug

    datasets = {
        'samples': CSVDataset(train_root, train_csv, 'image_id', 'melanoma',
                              transform=augs.tf_augment, add_extension='.jpg',
                              limit=(400, 433)),
        'train': CSVDataset(train_root, train_csv, 'image_id', 'melanoma',
                            transform=augs.tf_transform, add_extension='.jpg',
                            random_subset_size=limit_data),
        'val': CSVDatasetWithName(
            val_root, val_csv, 'image_id', 'melanoma',
            transform=augs.tf_transform, add_extension='.jpg'),
        'test': CSVDatasetWithName(
            test_root, test_csv, 'image_id', 'melanoma',
            transform=augs.tf_transform, add_extension='.jpg'),
        'test_no_aug': CSVDatasetWithName(
            test_root, test_csv, 'image_id', 'melanoma',
            transform=augs.no_augmentation, add_extension='.jpg'),
        'test_144': CSVDatasetWithName(
            test_root, test_csv, 'image_id', 'melanoma',
            transform=augs.inception_crop, add_extension='.jpg'),
    }

    dataloaders = {
        'train': DataLoader(datasets['train'], batch_size=batch_size,
                            shuffle=True, num_workers=num_workers,
                            worker_init_fn=set_seeds),
        'samples': DataLoader(datasets['samples'], batch_size=batch_size,
                              shuffle=False, num_workers=num_workers,
                              worker_init_fn=set_seeds),
    }

    save_images(datasets['samples'], to=AUGMENTED_IMAGES_DIR, n=32)
    sample_batch, _ = next(iter(dataloaders['samples']))
    save_image(make_grid(sample_batch, padding=0),
               os.path.join(AUGMENTED_IMAGES_DIR, 'grid.jpg'))

    criterion = nn.CrossEntropyLoss()

    optimizer = optim.SGD(model.parameters(),
                          lr=0.001,
                          momentum=0.9,
                          weight_decay=0.001)

    scheduler = optim.lr_scheduler.ReduceLROnPlateau(optimizer, factor=0.1,
                                                     min_lr=1e-5,
                                                     patience=8)
    metrics = {
        'train': pd.DataFrame(columns=['epoch', 'loss', 'acc', 'auc']),
        'val': pd.DataFrame(columns=['epoch', 'loss', 'acc', 'auc'])
    }

    best_val_auc = 0.0
    best_epoch = 0
    epochs_without_improvement = 0
    if images_per_epoch:
        batches_per_epoch = images_per_epoch // batch_size
    else:
        batches_per_epoch = None

    for epoch in range(epochs):
        print('train epoch {}/{}'.format(epoch+1, epochs))
        epoch_train_result = train_epoch(
            device, model, dataloaders, criterion, optimizer,
            batches_per_epoch)

        metrics['train'] = metrics['train'].append(
            {**epoch_train_result, 'epoch': epoch}, ignore_index=True)
        print('train', epoch_train_result)

        epoch_val_result, _ = test_with_augmentation(
            model, datasets['val'], device, num_workers, val_samples)

        metrics['val'] = metrics['val'].append(
            {**epoch_val_result, 'epoch': epoch}, ignore_index=True)
        print('val', epoch_val_result)
        print('-' * 40)

        scheduler.step(epoch_val_result['loss'])

        if epoch_val_result['auc'] > best_val_auc:
            best_val_auc = epoch_val_result['auc']
            best_val_result = epoch_val_result
            best_epoch = epoch
            epochs_without_improvement = 0
            torch.save(model, BEST_MODEL_PATH)
        else:
            epochs_without_improvement += 1

        if epochs_without_improvement > early_stopping_patience:
            last_val_result = epoch_val_result
            torch.save(model, LAST_MODEL_PATH)
            break

        if epoch == (epochs-1):
            last_val_result = epoch_val_result
            torch.save(model, LAST_MODEL_PATH)

    for phase in ['train', 'val']:
        metrics[phase].epoch = metrics[phase].epoch.astype(int)
        metrics[phase].to_csv(os.path.join(fs_observer.dir, phase + '.csv'),
                              index=False)

    # Run testing
    # TODO: reduce code repetition
    test_result, preds = test_with_augmentation(
        torch.load(BEST_MODEL_PATH), datasets['test'], device,
        num_workers, test_samples)
    print('[best] test', test_result)

    test_noaug_result, preds_noaug = test_with_augmentation(
        torch.load(BEST_MODEL_PATH), datasets['test_no_aug'], device,
        num_workers, 1)
    print('[best] test (no augmentation)', test_noaug_result)

    test_result_last, preds_last = test_with_augmentation(
        torch.load(LAST_MODEL_PATH), datasets['test'], device,
        num_workers, test_samples)
    print('[last] test', test_result_last)

    test_noaug_result_last, preds_noaug_last = test_with_augmentation(
        torch.load(LAST_MODEL_PATH), datasets['test_no_aug'], device,
        num_workers, 1)
    print('[last] test (no augmentation)', test_noaug_result_last)

    # Save predictions
    preds.to_csv(os.path.join(fs_observer.dir, 'test-aug-best.csv'),
                 index=False, columns=['image', 'label', 'score'])
    preds_noaug.to_csv(os.path.join(fs_observer.dir, 'test-noaug-best.csv'),
                 index=False, columns=['image', 'label', 'score'])
    preds_last.to_csv(os.path.join(fs_observer.dir, 'test-aug-last.csv'),
                 index=False, columns=['image', 'label', 'score'])
    preds_noaug_last.to_csv(os.path.join(fs_observer.dir, 'test-noaug-last.csv'),
                 index=False, columns=['image', 'label', 'score'])

    # TODO: Avoid repetition.
    #       use ordereddict, or create a pandas df before saving
    with open(RESULTS_CSV_PATH, 'a') as file:
        file.write(','.join((
            EXP_NAME,
            str(EXP_ID),
            str(split_id),
            str(best_epoch),
            str(best_val_result['loss']),
            str(best_val_result['acc']),
            str(best_val_result['auc']),
            str(best_val_result['avp']),
            str(best_val_result['sens']),
            str(best_val_result['spec']),
            str(last_val_result['loss']),
            str(last_val_result['acc']),
            str(last_val_result['auc']),
            str(last_val_result['avp']),
            str(last_val_result['sens']),
            str(last_val_result['spec']),
            str(best_val_auc),
            str(test_result['auc']),
            str(test_result_last['auc']),
            str(test_result['acc']),
            str(test_result_last['acc']),
            str(test_result['spec']),
            str(test_result_last['spec']),
            str(test_result['sens']),
            str(test_result_last['sens']),
            str(test_result['avp']),
            str(test_result_last['avp']),
            str(test_noaug_result['auc']),
            str(test_noaug_result_last['auc']),
            str(test_noaug_result['acc']),
            str(test_noaug_result_last['acc']),
            str(test_noaug_result['spec']),
            str(test_noaug_result_last['spec']),
            str(test_noaug_result['sens']),
            str(test_noaug_result_last['sens']),
            str(test_noaug_result['avp']),
            str(test_noaug_result_last['avp']),
            )) + '\n')

    return (test_noaug_result['auc'],
            test_result['auc'],
            )