{"cells":[{"metadata":{"trusted":true,"_uuid":"8fca22a7cd11c943247068ff3c5117e4587bf3d1"},"cell_type":"code","source":"# Image size was made smaller for notebook use ","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"f1be6ded87de221aca6f6503993d053e6e98ae93"},"cell_type":"code","source":"!ls ../input\n!pip install pytorch-ignite","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"95cb8bc95d37dd88806ca1f1c61817a2a93e4f39"},"cell_type":"code","source":"from torch.utils.data import Dataset, DataLoader\nfrom torch.utils.data.sampler import Sampler\nimport torch\nimport pandas as pd\nfrom sklearn.preprocessing import MultiLabelBinarizer\nimport pathlib\nimport torchvision.transforms as transforms\nimport torch\nimport PIL\nfrom sklearn.model_selection import train_test_split\n\nimport sys\nimport pandas as pd\n# from neptune import Context\nfrom sklearn.metrics import f1_score\nimport torch.nn as nn\nimport torchvision.transforms as transforms\nfrom torch import optim, save\nfrom torch.utils.data import DataLoader\nfrom torchvision.models import resnet50\n\nfrom ignite.engine import Events\nfrom ignite.engine import create_supervised_evaluator, create_supervised_trainer\nfrom ignite.metrics import CategoricalAccuracy, Recall, Precision\nfrom ignite.metrics import Loss\nimport numpy as np\nRANDOM_SEED = 666\n\nLABEL_MAP = {\n0: \"Nucleoplasm\" ,\n1: \"Nuclear membrane\"   ,\n2: \"Nucleoli\"   ,\n3: \"Nucleoli fibrillar center\",   \n4: \"Nuclear speckles\"   ,\n5: \"Nuclear bodies\"   ,\n6: \"Endoplasmic reticulum\"   ,\n7: \"Golgi apparatus\"  ,\n8: \"Peroxisomes\"   ,\n9:  \"Endosomes\"   ,\n10: \"Lysosomes\"   ,\n11: \"Intermediate filaments\"  , \n12: \"Actin filaments\"   ,\n13: \"Focal adhesion sites\"  ,\n14: \"Microtubules\"   ,\n15: \"Microtubule ends\"   ,\n16: \"Cytokinetic bridge\"   ,\n17: \"Mitotic spindle\"  ,\n18: \"Microtubule organizing center\",  \n19: \"Centrosome\",\n20: \"Lipid droplets\"   ,\n21: \"Plasma membrane\"  ,\n22: \"Cell junctions\"   ,\n23: \"Mitochondria\"   ,\n24: \"Aggresome\"   ,\n25: \"Cytosol\" ,\n26: \"Cytoplasmic bodies\",\n27: \"Rods & rings\"}\n\n\n\n\n\nclass MultiBandMultiLabelDataset(Dataset):\n    BANDS_NAMES = ['_red.png','_green.png','_blue.png','_yellow.png']\n    \n    def __len__(self):\n        return len(self.images_df)\n    \n    def __init__(self, images_df, \n                 base_path, \n                 image_transform, \n                 augmentator=None,\n                 train_mode=True    \n                ):\n        if not isinstance(base_path, pathlib.Path):\n            base_path = pathlib.Path(base_path)\n            \n        self.images_df = images_df.copy()\n        self.image_transform = image_transform\n        self.augmentator = augmentator\n        self.images_df.Id = self.images_df.Id.apply(lambda x: base_path / x)\n        self.mlb = MultiLabelBinarizer(classes=list(LABEL_MAP.keys()))\n        self.train_mode = train_mode\n\n                                      \n        \n    def __getitem__(self, index):\n        y = None\n        X = self._load_multiband_image(index)\n        if self.train_mode:\n            y = self._load_multilabel_target(index)\n        \n        # augmentator can be for instance imgaug augmentation object\n        if self.augmentator is not None:\n            X = self.augmentator(X)\n            \n        X = self.image_transform(X)\n            \n        return X, y \n        \n    def _load_multiband_image(self, index):\n        row = self.images_df.iloc[index]\n        image_bands = []\n        for band_name in self.BANDS_NAMES:\n            p = str(row.Id.absolute()) + band_name\n            pil_channel = PIL.Image.open(p)\n            image_bands.append(pil_channel)\n            \n        # lets pretend its a RBGA image to support 4 channels\n        band4image = PIL.Image.merge('RGBA', bands=image_bands)\n        return band4image\n    \n    \n    def _load_multilabel_target(self, index):\n        return list(map(int, self.images_df.iloc[index].Target.split(' ')))\n    \n        \n    def collate_func(self, batch):\n        labels = None\n        images = [x[0] for x in batch]\n        \n        if self.train_mode:\n            labels = [x[1] for x in batch]\n            labels_one_hot  = self.mlb.fit_transform(labels)\n            labels = torch.FloatTensor(labels_one_hot)\n            \n        \n        return torch.stack(images)[:,:4,:,:], labels\n\ndef get_model(n_classes, image_channels=4):\n    model = resnet50(pretrained=False)\n    for p in model.parameters():\n        p.requires_grad = True\n    inft = model.fc.in_features\n    model.fc = nn.Linear(in_features=inft, out_features=n_classes)\n    model.avgpool = nn.AdaptiveAvgPool2d(1)\n    model.conv1 = nn.Conv2d(image_channels, 64, kernel_size=7, stride=2, padding=3,\n                               bias=False)\n    \n    return model  \n\n\ndef train(trainer, train_loader, test_loader, checkpoint_path='bestmodel_{}_{}.torch', epochs=1):\n    @trainer.on(Events.ITERATION_COMPLETED)\n    def log_training_loss(engine):\n        iter = (engine.state.iteration - 1) % len(train_loader) + 1\n#         ctx.channel_send('loss', engine.state.output)\n        if iter % 10 == 0:\n            print(\"Epoch[{}] Iteration[{}/{}] Loss: {:.2f}\"\n                  \"\".format(engine.state.epoch, iter, len(train_loader), engine.state.output))\n\n    @trainer.on(Events.EPOCH_COMPLETED)\n    def log_training_results(engine):\n        evaluator.run(test_loader)\n        metrics = evaluator.state.metrics\n        avg_nll = metrics['loss']\n        print(\"Training Results - Epoch: {}  Avg loss: {:.2f}\"\n              .format(engine.state.epoch, avg_nll))\n        save(model, checkpoint_path.format(engine.state.epoch, avg_nll))\n    trainer.run(train_loader, max_epochs=epochs)\n    \n    return model \n    \n\n# Eval\ndef evaluate(model, test_loader, threshold=0.2):\n    all_preds = []\n    true = []\n    model.eval()\n    for b in test_loader:\n        X, y = b\n        if torch.cuda.is_available():\n            X, y = X.cuda(), y.cuda()\n        pred = model(X)\n        all_preds.append(pred.sigmoid().cpu().data.numpy())\n        true.append(y.cpu().data.numpy())\n        \n        \n    P = np.concatenate(all_preds)\n    R = np.concatenate(true)\n    \n    f1 = f1_score(P>threshold, R, average='macro')\n    print(f1)\n    return f1\n    \n\n## Submission\ndef predict_submission(model, submission_load):\n    all_preds = []\n    model.eval()\n    for i, b in enumerate(submission_load):\n        if i % 100: print('processing batch {}/{}'.format(i, len(submission_load)))\n        X, _ = b\n        if torch.cuda.is_available():\n            X = X.cuda()\n        pred = model(X)\n        all_preds.append(pred.sigmoid().cpu().data.numpy())\n    return np.concatenate(all_preds)\n        \n         \ndef make_submission_file(sample_submission_df, predictions):\n    submissions = []\n    for row in predictions:\n        subrow = ' '.join(list([str(i) for i in np.nonzero(row)[0]]))\n        submissions.append(subrow)\n    \n    sample_submission_df['Predicted'] = submissions\n    sample_submission_df.to_csv('submission.csv', index=None)\n    \n    return sample_submission_df\n\n    \nPATH_TO_IMAGES = '../input/train/'\nPATH_TO_TEST_IMAGES = '../input/test/'\nPATH_TO_META = '../input/train.csv'\nSAMPLE_SUBMI = '../input/sample_submission.csv'\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"caf8572e407560153edaef6727735f5a69f606a4"},"cell_type":"code","source":"# Prepare dataframe files\n\nSEED = 666\nDEV_MODE = True\n    \ndf = pd.read_csv(PATH_TO_META)\ndf_train, df_test  = train_test_split(df, test_size=0.2, random_state=SEED)\ndf_submission = pd.read_csv(SAMPLE_SUBMI)\n\nif DEV_MODE:\n    df_train = df_train[:200]\n    df_test = df_test[:50]\n    df_submission = df_submission[:50]\n\nimage_transform = transforms.Compose([\n            transforms.Resize(32),\n            transforms.ToTensor(),\n    \n        ])\n\n \n# Prepare datasets and loaders\n   \ngtrain = MultiBandMultiLabelDataset(df_train, base_path=PATH_TO_IMAGES, image_transform=image_transform)\ngtest = MultiBandMultiLabelDataset(df_test, base_path=PATH_TO_IMAGES, image_transform=image_transform)\ngsub = MultiBandMultiLabelDataset(df_submission, base_path=PATH_TO_TEST_IMAGES, train_mode=False, image_transform=image_transform)\n\ntrain_load = DataLoader(gtrain, collate_fn=gtrain.collate_func, batch_size=16, num_workers=6)\ntest_load = DataLoader(gtest, collate_fn=gtest.collate_func, batch_size=16, num_workers=6)\nsubmission_load = DataLoader(gsub, collate_fn=gsub.collate_func, batch_size=16, num_workers=6)\n\n\n# Prepare model \n\nmodel = get_model(28,4)\ndevice='cpu'\ncriterion = nn.BCEWithLogitsLoss()\nif torch.cuda.is_available():\n    criterion = criterion.cuda()\nevaluator = create_supervised_evaluator(model,\n                                            device=device,\n                                            metrics={'loss': Loss(criterion)\n                                                    })\noptimizer = optim.Adam(filter(lambda p: p.requires_grad,model.parameters()), lr=0.00005)\ntrainer = create_supervised_trainer(model, optimizer, criterion, device=device)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"e530a84dce9fa6374195fab79d8084ddaef9c28d"},"cell_type":"markdown","source":"# TRAIN"},{"metadata":{"scrolled":true,"trusted":true,"_uuid":"bd3c3756df0888108cc47474679b4a4ac29fb922"},"cell_type":"code","source":"# train the model\nmodel = train(trainer, train_load, test_load, epochs=1)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"8647728f845816ae2faf0dfccc8c74d9babec3d4"},"cell_type":"markdown","source":"# EVAL"},{"metadata":{"trusted":true,"_uuid":"62041cbf27e649f9cb15500cebc92579f0d0227c"},"cell_type":"code","source":"# evaluate on testing data and calculate F1-macro\nres = evaluate(model, test_load, threshold=0.2)\nprint(res)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"00dd917d95ab8d3e1d45fe8dd2a3010607de883f"},"cell_type":"markdown","source":"# SUBMISSION"},{"metadata":{"trusted":true,"_uuid":"96ff86d6ba72a4f6f2e0d59c7aa9041b7b100827"},"cell_type":"code","source":"submission_predictions =predict_submission(model, submission_load)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"47e9c81bd9ee2610d174ddb89fe44dfa786c4d28"},"cell_type":"code","source":"# prepare the submission file and \nTHRESHOLD = 0.2\np = submission_predictions>THRESHOLD\n\nsubmission_file = make_submission_file(sample_submission_df=df_submission,\n                     predictions=p)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"cd831c044230459f54559a11e9424dc54324f932"},"cell_type":"code","source":"submission_file.head()","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"name":"python","version":"3.6.6","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"}},"nbformat":4,"nbformat_minor":1}