{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":19991,"databundleVersionId":1117522,"sourceType":"competition"},{"sourceId":11328083,"sourceType":"datasetVersion","datasetId":7028691}],"dockerImageVersionId":30919,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Alaska2 Baseline PyTorch\n\nHi everyone!\n\nMy name is Alex Shonenkov, I am DL/NLP/CV/TS research engineer. Especially I am in Love with NLP & DL.\n\nI would like to share with you my starter pipeline for solving this competition :)","metadata":{}},{"cell_type":"markdown","source":"# Main Ideas\n\n- 4 Classes\n- GroupKFold splitting\n- Class Balance\n- Flips\n- Label Smoothing\n- EfficientNetB2\n- ReduceLROnPlateau","metadata":{}},{"cell_type":"markdown","source":"# Dependencies","metadata":{}},{"cell_type":"code","source":"!pip install -q efficientnet_pytorch > /dev/null","metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true,"_kg_hide-output":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from glob import glob\nfrom sklearn.model_selection import GroupKFold\nimport cv2\nfrom skimage import io\nimport torch\nfrom torch import nn\nimport os\nfrom datetime import datetime\nimport time\nimport random\nimport cv2\nimport pandas as pd\nimport numpy as np\nimport albumentations as A\nimport matplotlib.pyplot as plt\nfrom albumentations.pytorch.transforms import ToTensorV2\nfrom torch.utils.data import Dataset,DataLoader, WeightedRandomSampler\nfrom torch.utils.data.sampler import SequentialSampler, RandomSampler\nimport sklearn\nfrom torch.amp import autocast, GradScaler\n\nSEED = 42\n\ndef seed_everything(seed):\n    random.seed(seed)\n    os.environ['PYTHONHASHSEED'] = str(seed)\n    np.random.seed(seed)\n    torch.manual_seed(seed)\n    torch.cuda.manual_seed(seed)\n    torch.backends.cudnn.deterministic = True\n    torch.backends.cudnn.benchmark = True\n\nseed_everything(SEED)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# GroupKFold splitting\n\nI think group splitting by image_name is really important for correct validation in this competition ;) ","metadata":{}},{"cell_type":"markdown","source":"## Using group k fold for this dataset","metadata":{}},{"cell_type":"code","source":"%%time\n\ndataset = []\n\nfor label, kind in enumerate(['Cover', 'JMiPOD', 'JUNIWARD', 'UERD']):\n    for path in glob('../input/alaska2-image-steganalysis/Cover/*.jpg'):\n        dataset.append({\n            'kind': kind,\n            'image_name': path.split('/')[-1],\n            'label': label\n        })\n\nrandom.shuffle(dataset)\ndataset = pd.DataFrame(dataset)\n\ngkf = GroupKFold(n_splits=4)\n\ndataset.loc[:, 'fold'] = 0\nfor fold_number, (train_index, val_index) in enumerate(gkf.split(X=dataset.index, y=dataset['label'], groups=dataset['image_name'])):\n    dataset.loc[dataset.iloc[val_index].index, 'fold'] = fold_number","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"In any case original dataframe with splitting:","metadata":{}},{"cell_type":"code","source":"# dataset = pd.read_csv('../input/alaska2-public-baseline/groupkfold_by_shonenkov.csv')","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Simple Augs: Flips","metadata":{}},{"cell_type":"code","source":"def get_train_transforms():\n    return A.Compose([\n            A.HorizontalFlip(p=0.5),\n            A.VerticalFlip(p=0.5),\n            A.Resize(height=512, width=512, p=1.0),\n            ToTensorV2(p=1.0),\n        ], p=1.0)\n\ndef get_valid_transforms():\n    return A.Compose([\n            A.Resize(height=512, width=512, p=1.0),\n            ToTensorV2(p=1.0),\n        ], p=1.0)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Dataset","metadata":{}},{"cell_type":"code","source":"DATA_ROOT_PATH = '../input/alaska2-image-steganalysis'\n\ndef onehot(size, target):\n    vec = torch.zeros(size, dtype=torch.float32)\n    vec[target] = 1.\n    return vec\n\nclass DatasetRetriever(Dataset):\n    \"\"\"Custom PyTorch Dataset to load, preprocess, and augment Alaska2 images.\"\"\"\n    def __init__(self, kinds, image_names, labels, transforms=None):\n        super().__init__()\n        self.kinds = kinds         # Image types (e.g., Cover, JMiPOD)\n        self.image_names = image_names  # Filenames\n        self.labels = labels       # Numeric labels (0-3)\n        self.transforms = transforms  # Augmentation pipeline\n\n    def __getitem__(self, index: int):\n        kind, image_name, label = self.kinds[index], self.image_names[index], self.labels[index]\n        image = cv2.imread(f'{DATA_ROOT_PATH}/{kind}/{image_name}', cv2.IMREAD_COLOR)\n        image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB).astype(np.float32)\n        image /= 255.0  # Normalize to [0, 1]\n        if self.transforms:\n            sample = {'image': image}\n            sample = self.transforms(**sample)\n            image = sample['image']\n        target = onehot(4, label)  # 4 classes: Cover and 3 stego methods\n        return image, target\n\n    def __len__(self) -> int:\n        return self.image_names.shape[0]\n\n    def get_labels(self):\n        return list(self.labels)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"fold_number = 0\n\ntrain_dataset = DatasetRetriever(\n    kinds=dataset[dataset['fold'] != fold_number].kind.values,\n    image_names=dataset[dataset['fold'] != fold_number].image_name.values,\n    labels=dataset[dataset['fold'] != fold_number].label.values,\n    transforms=get_train_transforms(),\n)\n\nvalidation_dataset = DatasetRetriever(\n    kinds=dataset[dataset['fold'] == fold_number].kind.values,\n    image_names=dataset[dataset['fold'] == fold_number].image_name.values,\n    labels=dataset[dataset['fold'] == fold_number].label.values,\n    transforms=get_valid_transforms(),\n)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"image, target = train_dataset[0]\nnumpy_image = image.permute(1,2,0).cpu().numpy()\n\nfig, ax = plt.subplots(1, 1, figsize=(16, 8))\n    \nax.set_axis_off()\nax.imshow(numpy_image);","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Metrics","metadata":{}},{"cell_type":"code","source":"from sklearn import metrics\n### Evaluation for the current model\nclass AverageMeter(object):\n    \"\"\"Computes and stores the average and current value\"\"\"\n    def __init__(self):\n        self.reset()\n\n    def reset(self):\n        self.val = 0\n        self.avg = 0\n        self.sum = 0\n        self.count = 0\n\n    def update(self, val, n=1):\n        self.val = val\n        self.sum += val * n\n        self.count += n\n        self.avg = self.sum / self.count\n        \n        \ndef alaska_weighted_auc(y_true, y_valid):\n    \"\"\"Compute the Alaska2 competition’s weighted AUC metric, emphasizing low false positives.\"\"\"\n    tpr_thresholds = [0.0, 0.4, 1.0]  # Define TPR ranges for weighting\n    weights = [2, 1]  # Higher weight for TPR < 0.4\n    fpr, tpr, _ = metrics.roc_curve(y_true, y_valid, pos_label=1)\n    areas = np.array(tpr_thresholds[1:]) - np.array(tpr_thresholds[:-1])\n    normalization = np.dot(areas, weights)\n    competition_metric = 0\n    for idx, weight in enumerate(weights):\n        y_min = tpr_thresholds[idx]\n        y_max = tpr_thresholds[idx + 1]\n        mask = (y_min < tpr) & (tpr < y_max)\n        x_padding = np.linspace(fpr[mask][-1], 1, 100)\n        x = np.concatenate([fpr[mask], x_padding])\n        y = np.concatenate([tpr[mask], [y_max] * len(x_padding)])\n        y = y - y_min\n        score = metrics.auc(x, y)\n        submetric = score * weight\n        competition_metric += submetric\n    return competition_metric / normalization\nclass RocAucMeter(object):\n    def __init__(self):\n        self.reset()\n\n    def reset(self):\n        self.y_true = np.array([0,1])\n        self.y_pred = np.array([0.5,0.5])\n        self.score = 0\n\n    def update(self, y_true, y_pred):\n        y_true = y_true.cpu().numpy().argmax(axis=1).clip(min=0, max=1).astype(int)\n        y_pred = 1 - nn.functional.softmax(y_pred, dim=1).data.cpu().numpy()[:,0]\n        self.y_true = np.hstack((self.y_true, y_true))\n        self.y_pred = np.hstack((self.y_pred, y_pred))\n        self.score = alaska_weighted_auc(self.y_true, self.y_pred)\n    \n    @property\n    def avg(self):\n        return self.score","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Label Smoothing","metadata":{}},{"cell_type":"code","source":"# Implement label smoothing to improve generalization\nclass LabelSmoothing(nn.Module):\n    \"\"\"Apply label smoothing to soften target labels, reducing overconfidence.\"\"\"\n    def __init__(self, smoothing=0.05):\n        super(LabelSmoothing, self).__init__()\n        self.confidence = 1.0 - smoothing\n        self.smoothing = smoothing\n\n    def forward(self, x, target):\n        if self.training:\n            x = x.float()\n            target = target.float()\n            logprobs = torch.nn.functional.log_softmax(x, dim=-1)\n            nll_loss = -logprobs * target\n            nll_loss = nll_loss.sum(-1)\n            smooth_loss = -logprobs.mean(dim=-1)\n            loss = self.confidence * nll_loss + self.smoothing * smooth_loss\n            return loss.mean()\n        else:\n            return torch.nn.functional.cross_entropy(x, target)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Fitter","metadata":{}},{"cell_type":"markdown","source":"The Fitter class is a central component in this Alaska2 baseline code, designed to manage the entire training and validation process for a PyTorch model (in this case, an EfficientNet-based model for steganalysis). It encapsulates the training loop, validation, optimization, scheduling, and checkpointing, making it easier to train the model systematically and monitor its performance. Here's what it’s used for:\n\nTraining Loop Management: It runs the model through multiple epochs, handling both training and validation phases.\nOptimization: It sets up and applies the optimizer (AdamW) and learning rate scheduler (e.g., ReduceLROnPlateau) to update the model’s weights.\nPerformance Tracking: It computes and logs loss and the competition-specific weighted AUC metric.\nCheckpointing: It saves the model’s state (weights, optimizer, etc.) during training, keeping both the latest and best-performing versions.\nMixed Precision Training: It uses PyTorch’s AMP (Automatic Mixed Precision) to speed up training and reduce memory usage on GPUs.","metadata":{}},{"cell_type":"code","source":"import warnings\nwarnings.filterwarnings(\"ignore\")  # Suppress warnings to keep output clean\n\nclass Fitter:\n    \"\"\"Manages training, validation, optimization, and checkpointing for the Alaska2 model.\"\"\"\n    \n    def __init__(self, model, device, config):\n        self.config = config  # Store training configuration (e.g., lr, epochs)\n        self.epoch = 0  # Track current epoch\n        self.best_summary_loss = 10**5  # Initialize best loss for checkpointing\n        self.model = model  # PyTorch model (EfficientNet)\n        self.device = device  # GPU or CPU\n\n        # Set up optimizer with weight decay for regularization\n        param_optimizer = list(self.model.named_parameters())\n        no_decay = ['bias', 'LayerNorm.bias', 'LayerNorm.weight']\n        optimizer_grouped_parameters = [\n            {'params': [p for n, p in param_optimizer if not any(nd in n for nd in no_decay)], 'weight_decay': 0.001},\n            {'params': [p for n, p in param_optimizer if any(nd in n for nd in no_decay)], 'weight_decay': 0.0}\n        ]\n        self.optimizer = torch.optim.AdamW(self.model.parameters(), lr=config.lr)  # AdamW optimizer for efficient training\n        self.scheduler = config.SchedulerClass(self.optimizer, **config.scheduler_params)  # Scheduler to adjust LR\n        self.criterion = LabelSmoothing().to(self.device)  # Loss function with smoothing to prevent overfitting\n\n    def fit(self, train_loader, validation_loader):\n        \"\"\"Run the full training loop over multiple epochs.\"\"\"\n        for e in range(self.config.n_epochs):\n            t = time.time()\n            summary_loss, final_scores = self.train_one_epoch(train_loader)  # Train for one epoch\n            self.log(f'[RESULT]: Train. Epoch: {self.epoch}, summary_loss: {summary_loss.avg:.5f}, final_score: {final_scores.avg:.5f}, time: {(time.time() - t):.2f}')\n            self.save(f'{self.base_dir}/last-checkpoint.bin')  # Save latest model state\n            t = time.time()\n            summary_loss, final_scores = self.validation(validation_loader)  # Validate after training\n            self.log(f'[RESULT]: Val. Epoch: {self.epoch}, summary_loss: {summary_loss.avg:.5f}, final_score: {final_scores.avg:.5f}, time: {(time.time() - t):.2f}')\n            if summary_loss.avg < self.best_summary_loss:  # Save best model if loss improves\n                self.best_summary_loss = summary_loss.avg\n                self.save(f'{self.base_dir}/best-checkpoint-{str(self.epoch).zfill(3)}epoch.bin')\n            if self.config.validation_scheduler:\n                self.scheduler.step(metrics=summary_loss.avg)  # Adjust LR based on validation loss\n            self.epoch += 1\n\n    def validation(self, val_loader):\n        \"\"\"Evaluate the model on the validation set without updating weights.\"\"\"\n        self.model.eval()  # Set model to evaluation mode\n        summary_loss = AverageMeter()  # Track average loss\n        final_scores = RocAucMeter()  # Track weighted AUC\n        for step, (images, targets) in enumerate(val_loader):\n            with torch.no_grad():  # No gradients during validation\n                images = images.to(self.device).float()\n                targets = targets.to(self.device).float()\n                outputs = self.model(images)\n                loss = self.criterion(outputs, targets)\n                final_scores.update(targets, outputs)  # Update AUC metric\n                summary_loss.update(loss.detach().item(), images.shape[0])\n        return summary_loss, final_scores\n\n    def train_one_epoch(self, train_loader):\n        \"\"\"Train the model for one epoch using mixed precision.\"\"\"\n        self.model.train()  # Set model to training mode\n        summary_loss = AverageMeter()\n        final_scores = RocAucMeter()\n        scaler = GradScaler('cuda')  # For mixed precision training\n        accum_step = 4  # Gradient accumulation steps to simulate larger batch size\n        self.optimizer.zero_grad()\n        for step, (images, targets) in enumerate(train_loader):\n            images = images.to(self.device).float()\n            targets = targets.to(self.device).float()\n            with autocast(device_type=\"cuda\"):  # Use mixed precision for efficiency\n                outputs = self.model(images)\n                loss = self.criterion(outputs, targets) / accum_step\n            scaler.scale(loss).backward()  # Scale loss for gradient accumulation\n            final_scores.update(targets, outputs)\n            summary_loss.update(loss.detach().item() * accum_step, images.shape[0])\n            if (step + 1) % accum_step == 0:  # Update weights every 4 steps\n                scaler.step(self.optimizer)\n                scaler.update()\n                self.optimizer.zero_grad()\n        return summary_loss, final_scores\n\n    def save(self, path):\n        \"\"\"Save model state, optimizer, and scheduler for resuming or submission.\"\"\"\n        torch.save({\n            'model_state_dict': self.model.state_dict(),\n            'optimizer_state_dict': self.optimizer.state_dict(),\n            'scheduler_state_dict': self.scheduler.state_dict(),\n            'best_summary_loss': self.best_summary_loss,\n            'epoch': self.epoch,\n        }, path)\n\n    def load(self, path):\n        \"\"\"Load a saved checkpoint to resume training or evaluate.\"\"\"\n        checkpoint = torch.load(path)\n        self.model.load_state_dict(checkpoint['model_state_dict'])\n        self.optimizer.load_state_dict(checkpoint['optimizer_state_dict'])\n        self.scheduler.load_state_dict(checkpoint['scheduler_state_dict'])\n        self.best_summary_loss = checkpoint['best_summary_loss']\n        self.epoch = checkpoint['epoch'] + 1\n\n    def log(self, message):\n        \"\"\"Log training progress to console and file.\"\"\"\n        if self.config.verbose:\n            print(message)\n        with open(self.log_path, 'a+') as logger:\n            logger.write(f'{message}\\n')","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# EfficientNet","metadata":{}},{"cell_type":"code","source":"from efficientnet_pytorch import EfficientNet\n\ndef get_net():\n    \"\"\"Initialize EfficientNet-B0 model with a custom classification head for 4 classes.\"\"\"\n    net = EfficientNet.from_pretrained('efficientnet-b0')\n    net._fc = nn.Linear(in_features=1280, out_features=4, bias=True)\n    return net\ndevice = torch.device('cuda') if torch.cuda.is_available() else torch.device('cpu')\nnet = get_net().to(device)","metadata":{"_kg_hide-output":true,"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Config","metadata":{}},{"cell_type":"code","source":"class TrainGlobalConfig:\n    \"\"\"Configuration class defining hyperparameters and scheduler settings for training the Alaska2 model.\"\"\"\n    \n    num_workers = 4 \n    train_batch_size = 32 \n    val_batch_size = 128  \n    n_epochs = 5  \n    lr = 0.001  \n\n    # -------------------\n    verbose = True \n    verbose_step = 500 \n    # -------------------\n\n    # --------------------\n    step_scheduler = False  # Disable scheduler updates after each optimizer step; not used here\n    validation_scheduler = True  # Enable scheduler updates based on validation loss; adjusts LR dynamically after each epoch\n\n    \n    SchedulerClass = torch.optim.lr_scheduler.ReduceLROnPlateau  # Chosen scheduler: reduces LR when validation loss plateaus\n    scheduler_params = dict(\n        mode='min',  \n        factor=0.5,  # Reduce LR by half when triggered\n        patience=2,  # Wait 2 epochs without improvement before reducing LR\n        verbose=False,  # Keep scheduler updates silent to avoid clutter\n        threshold=0.0001,  # Minimum change in loss to consider as improvement\n        threshold_mode='abs',  # Use absolute change for threshold\n        cooldown=0, \n        min_lr=1e-8,  # Minimum LR to prevent it from becoming too small\n        eps=1e-08  # Smallest detectable change to avoid unnecessary updates\n    )\n    # --------------------","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Class Balance \"on fly\" from [@CatalystTeam](https://github.com/catalyst-team/catalyst)","metadata":{}},{"cell_type":"code","source":"#from catalyst.data.sampler import BalanceClassSampler\n\ndef run_training():\n    device = torch.device('cuda') if torch.cuda.is_available() else torch.device('cpu')\n    labels = train_dataset.get_labels()  # e.g., [0, 1, 2, 3, 0, 1, 3, 3, ...]\n    labels = np.array(labels)\n    \n    class_sample_count = np.bincount(labels)\n    min_count = class_sample_count[class_sample_count > 0].min()\n    \n    # Calculate weights: set each class weight inversely proportional to its frequency\n    weights_per_class = 1. / class_sample_count\n    sample_weights = weights_per_class[labels]\n    #print(sample_weights)\n    \n    # Use without replacement to simulate downsampling\n    sampler = WeightedRandomSampler(sample_weights, num_samples=int(min_count * len(np.unique(labels))), replacement=False)\n    \n    train_loader = torch.utils.data.DataLoader(\n        train_dataset,\n        #sampler=BalanceClassSampler(labels=train_dataset.get_labels(), mode=\"downsampling\"),\n        sampler= sampler,\n        batch_size=TrainGlobalConfig.train_batch_size,\n        pin_memory=True,\n        drop_last=True,\n        num_workers=TrainGlobalConfig.num_workers,\n    )\n    val_loader = torch.utils.data.DataLoader(\n        validation_dataset, \n        batch_size=TrainGlobalConfig.val_batch_size,\n        num_workers=TrainGlobalConfig.num_workers,\n        shuffle=False,\n        sampler=SequentialSampler(validation_dataset),\n        pin_memory=True,\n    )\n\n    fitter = Fitter(model=net, device=device, config=TrainGlobalConfig)\n    fitter.load(f'.{fitter.base_dir}input/ddp-script/best-checkpoint-014epoch.bin')\n    #fitter.load(f'.{fitter.base_dir}input/ddp-script/last-checkpoint.bin')\n    fitter.fit(train_loader, val_loader)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Training\n\nI have used 1xV100 for training model, in kaggle kernel it works also. You can make fork and check it, but I would like to share with you my logs","metadata":{}},{"cell_type":"code","source":"run_training()","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#file = open('../input/alaska2-public-baseline/log.txt', 'r')\n#for line in file.readlines():\n#    print(line[:-1])\n#file.close()","metadata":{"_kg_hide-input":true,"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Inference","metadata":{}},{"cell_type":"code","source":"#checkpoint = torch.load('../input/alaska2-public-baseline/best-checkpoint-033epoch.bin') \n#checkpoint = torch.load('../input/ddp-script/last-checkpoint.bin')\n#net.load_state_dict(checkpoint['model_state_dict']);\nnet.eval();","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"In checkpoint you can find states for optimizer and scheduler if you need","metadata":{}},{"cell_type":"code","source":"checkpoint.keys()","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class DatasetSubmissionRetriever(Dataset):\n\n    def __init__(self, image_names, transforms=None):\n        super().__init__()\n        self.image_names = image_names\n        self.transforms = transforms\n\n    def __getitem__(self, index: int):\n        image_name = self.image_names[index]\n        image = cv2.imread(f'{DATA_ROOT_PATH}/Test/{image_name}', cv2.IMREAD_COLOR)\n        image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB).astype(np.float32)\n        image /= 255.0\n        if self.transforms:\n            sample = {'image': image}\n            sample = self.transforms(**sample)\n            image = sample['image']\n\n        return image_name, image\n\n    def __len__(self) -> int:\n        return self.image_names.shape[0]","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"dataset = DatasetSubmissionRetriever(\n    image_names=np.array([path.split('/')[-1] for path in glob('../input/alaska2-image-steganalysis/Test/*.jpg')]),\n    transforms=get_valid_transforms(),\n)\n\n\ndata_loader = DataLoader(\n    dataset,\n    batch_size=128,\n    shuffle=False,\n    num_workers=4,\n    drop_last=False,\n)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"%%time\n\nresult = {'Id': [], 'Label': []}\nwith torch.no_grad():\n    for step, (image_names, images) in enumerate(data_loader):\n        print(step, end='\\r')\n        \n        y_pred = net(images.cuda())\n        y_pred = 1 - nn.functional.softmax(y_pred, dim=1).data.cpu().numpy()[:,0]\n        \n        result['Id'].extend(image_names)\n        result['Label'].extend(y_pred)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"submission = pd.DataFrame(result)\nsubmission.to_csv('submission.csv', index=False)\nsubmission.head()","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"submission['Label'].hist(bins=100);","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Thank you for reading my kernel!","metadata":{}}]}