{"cells":[{"metadata":{},"cell_type":"markdown","source":"# Alaska2 Multiclass Classifier with Catalyst"},{"metadata":{},"cell_type":"markdown","source":"This is a fork of a great [kernel](https://www.kaggle.com/meaninglesslives/alaska2-cnn-multiclass-classifier) by [@meaninglesslives](https://www.kaggle.com/meaninglesslives). Rewritten using beautiful [Catalyst](https://github.com/catalyst-team/catalyst) framework. I've also made some minor improvements."},{"metadata":{},"cell_type":"markdown","source":"# Load Libraries"},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"_kg_hide-input":true,"_kg_hide-output":true},"cell_type":"code","source":"!pip install -q efficientnet_pytorch\nfrom efficientnet_pytorch import EfficientNet\nfrom albumentations.pytorch import ToTensorV2\nfrom albumentations import (\n    Compose, HorizontalFlip,\n    ToFloat, VerticalFlip\n)\nfrom catalyst.dl.callbacks.metrics import AccuracyCallback, AUCCallback\nfrom catalyst.dl import SupervisedRunner\nfrom catalyst.utils import get_one_hot\nimport os\nimport torch\nimport pandas as pd\nimport numpy as np\nimport torch.nn as nn\nfrom glob import glob\nimport torchvision\nfrom torch.utils.data import Dataset\nfrom tqdm.notebook import tqdm\nfrom skimage.io import imread\nimport torch.nn.functional as F\nfrom scipy.special import softmax","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Create dataset for training and Validation"},{"metadata":{"trusted":true},"cell_type":"code","source":"data_dir = '../input/alaska2-image-steganalysis'\nfolder_names = ['JMiPOD/', 'JUNIWARD/', 'UERD/']\nclass_names = ['Normal', 'JMiPOD_75', 'JMiPOD_90', 'JMiPOD_95', \n               'JUNIWARD_75', 'JUNIWARD_90', 'JUNIWARD_95',\n                'UERD_75', 'UERD_90', 'UERD_95']\nclass_labels = { name: i for i, name in enumerate(class_names)}","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_df = pd.read_csv('../input/alaska2trainvalsplit/alaska2_train_df.csv')\n\ntrain_df = train_df.sample(40000).reset_index(drop=True) # Delete this line for good training =)\n\nval_df = pd.read_csv('../input/alaska2trainvalsplit/alaska2_val_df.csv')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class Alaska2Dataset(Dataset):\n    def __init__(self, df, augmentations=None, test = False):\n        self.data = df\n        self.augment = augmentations\n        self.test = test\n\n    def __len__(self):\n        return len(self.data)\n\n    def __getitem__(self, idx):\n        if self.test:\n            fn = self.data.loc[idx][0]\n        else:\n            fn, label = self.data.loc[idx]\n        im = imread(fn)\n        if self.augment:\n            im = self.augment(image=im)\n        if self.test:\n            item = {'features': im['image']}\n        else:\n            item = {'features': im['image'], 'targets':label, 'bool_targets': get_one_hot(label, 10)}\n\n        return item\n\n\nAUGMENTATIONS_TRAIN = Compose([\n    VerticalFlip(p=0.5),\n    HorizontalFlip(p=0.5),\n    ToFloat(max_value=255),\n    ToTensorV2()\n], p=1)\n\n\nAUGMENTATIONS_TEST = Compose([\n    ToFloat(max_value=255),\n    ToTensorV2()\n], p=1)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"batch_size = 32\nnum_workers = 8\n\ntrain_dataset = Alaska2Dataset(train_df, augmentations=AUGMENTATIONS_TRAIN)\nvalid_dataset = Alaska2Dataset(val_df.sample(5000).reset_index(drop=True), augmentations=AUGMENTATIONS_TEST)\n\ntrain_loader = torch.utils.data.DataLoader(train_dataset,\n                                           batch_size=batch_size,\n                                           num_workers=num_workers,\n                                           shuffle=True)\n\nvalid_loader = torch.utils.data.DataLoader(valid_dataset,\n                                           batch_size=batch_size*2,\n                                           num_workers=num_workers,\n                                           shuffle=False)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# CNN Model for multiclass classification"},{"metadata":{"trusted":true},"cell_type":"code","source":"class Net(nn.Module):\n    def __init__(self, num_classes):\n        super().__init__()\n        self.model = EfficientNet.from_name('efficientnet-b0')\n        self.dense_output = nn.Linear(1280, num_classes)\n\n    def forward(self, x):\n        feat = self.model.extract_features(x)\n        feat = F.avg_pool2d(feat, feat.size()[2:]).reshape(-1, 1280)\n        return self.dense_output(feat)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_kg_hide-output":true},"cell_type":"code","source":"loaders = {\n    \"train\": train_loader,\n    \"valid\": valid_loader\n}\n\nmodel = Net(num_classes=len(class_labels))\nmodel.load_state_dict(torch.load('../input/alaska2trainvalsplit/epoch_5_val_loss_3.75_auc_0.833.pth'))\n\n#optimizer = torch.optim.AdamW(model.parameters(), lr=0.01)\noptimizer = torch.optim.AdamW(model.parameters(), lr=0.0003)\ncriterion = torch.nn.CrossEntropyLoss()\ncallbacks = [\n    AccuracyCallback(),\n    AUCCallback(input_key='bool_targets', num_classes = 1) # I was too lazy to implement weighted AUC. It is strongly correlated with regular AUC. But it should be easy to make your own catalyst \"meter\" for weighted AUC \n]\n\nrunner = SupervisedRunner()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#runner.train(\n#    model=model,\n#    criterion=criterion,\n#    optimizer=optimizer,\n#    loaders=loaders,\n#    num_epochs=10,\n#    verbose=True,\n#    callbacks=callbacks,\n#    logdir=\"logs\",\n#    main_metric=\"auc/class_0\",\n#    minimize_metric = False,\n#)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Create Inference Dataset"},{"metadata":{"trusted":true},"cell_type":"code","source":"test_filenames = sorted(glob(f\"{data_dir}/Test/*.jpg\"))\ntest_df = pd.DataFrame({'ImageFileName': list(\n    test_filenames)}, columns=['ImageFileName'])\n\nbatch_size = 16\nnum_workers = 4\ntest_dataset = Alaska2Dataset(test_df, augmentations=AUGMENTATIONS_TEST, test=True)\ntest_loader = torch.utils.data.DataLoader(test_dataset,\n                                          batch_size=batch_size,\n                                          num_workers=num_workers,\n                                          shuffle=False,\n                                          drop_last=False)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Do inference"},{"metadata":{"trusted":true},"cell_type":"code","source":"#model.load_state_dict(torch.load('logs/checkpoints/best.pth')[\"model_state_dict\"])\nmodel.cuda()\npreds = []\nfor outputs in tqdm(runner.predict_loader(loader=test_loader, model=model)):\n    preds.append(softmax(outputs))\n\npreds = np.array(preds)\n\ntest_df['Id'] = test_df['ImageFileName'].apply(lambda x: x.split(os.sep)[-1])\ntest_df['Label'] = 1-preds[:, 0]\n\ntest_df = test_df.drop('ImageFileName', axis=1)\ntest_df.to_csv('submission.csv', index=False)\nprint(test_df.head())","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"torch.save(model.state_dict(), 'efficientnet_0')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model.state_dict()","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat":4,"nbformat_minor":4}