{"cells":[{"metadata":{},"cell_type":"markdown","source":"## Load libraries"},{"metadata":{"_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","trusted":true},"cell_type":"code","source":"import os\nimport numpy as np\nimport pandas as pd\nfrom PIL import Image\nfrom tqdm import tqdm\n\nimport torch\nimport torch.nn as nn\nimport torch.utils.data as D\nfrom torchvision import models, transforms as T\n\nimport warnings\nwarnings.filterwarnings('ignore')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Define dataset and model"},{"metadata":{"_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","trusted":true},"cell_type":"code","source":"path_data = '../input'\ndevice = 'cuda'\nbatch_size = 16\n# XXX set this to a higher value\nmax_epochs = 3\nimg_size = 384\ntorch.manual_seed(0)\nnp.random.seed(0)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class ImagesDS(D.Dataset):\n    def __init__(self, df, img_dir, mode='train', site=1, channels=[1,2,3,4,5,6]):\n        self.records = df.to_records(index=False)\n        self.channels = channels\n        self.site = site\n        self.mode = mode\n        self.img_dir = img_dir\n        self.len = df.shape[0]\n        train_controls = pd.read_csv(path_data+'/train_controls.csv')\n        test_controls = pd.read_csv(path_data+'/test_controls.csv')\n        self.controls = pd.concat([train_controls, test_controls])\n\n    @staticmethod\n    def _load_img_as_tensor(file_name):\n        with Image.open(file_name) as img:\n            return T.ToTensor()(img)\n\n    def _get_img_path(self, experiment, well, plate, channel):\n        if self.mode == 'train':\n            # pick one of the sites randomly\n            site = np.random.randint(1, 3)\n        else:\n            site = self.site\n        return '/'.join([self.img_dir, self.mode, experiment,\n                        f'Plate{plate}', f'{well}_s{site}_w{channel}.png'])\n\n    def __getitem__(self, index):\n        rec = self.records[index]\n        experiment, well, plate = rec.experiment, rec.well, rec.plate\n        paths = [self._get_img_path(experiment, well, plate, ch) for ch in self.channels]\n\n        df = self.controls\n        negs = df[(df.experiment == experiment) & (df.plate == plate) & (df.sirna == 1138)]\n        well = negs.iloc[np.random.randint(0, len(negs))].well\n        paths.extend([self._get_img_path(experiment, well, plate, ch) for ch in self.channels])\n\n        img = torch.cat([self._load_img_as_tensor(img_path) for img_path in paths])\n        tr_img = torch.empty((12, img_size, img_size), dtype=torch.float32)\n\n        if self.mode == 'train':\n            # randomly crop\n            row, col = np.random.randint(0, 512 - img_size + 1, 2)\n            tr_img[:6] = img[:6, row:row + img_size, col:col + img_size]\n            # randomly crop the negative control image\n            row, col = np.random.randint(0, 512 - img_size + 1, 2)\n            tr_img[6:] = img[6:, row:row + img_size, col:col + img_size]\n            return tr_img, int(self.records[index].sirna)\n\n        # center crop\n        row =  col = (512 - img_size) // 2\n        tr_img[:] = img[:, row:row + img_size, col:col + img_size]\n        return tr_img, rec.id_code\n\n    def __len__(self):\n        return self.len","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df = pd.read_csv(path_data+'/train.csv')\nin_eval = df.experiment.isin(['HEPG2-07', 'HUVEC-16', 'RPE-07'])\ndf_train = df[~in_eval]\ndf_val = df[in_eval]\n\ndf_test = pd.read_csv(path_data+'/test.csv')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"ds = ImagesDS(df_train, path_data, mode='train')\nds_val = ImagesDS(df_val, path_data, mode='train')\nds_test = ImagesDS(df_test, path_data, mode='test')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"num_classes = 1108\nnum_workers = 4\nmodel = models.resnet18(pretrained=True)\n# add a new layer to combine outputs from two paths.\nmodel.head = torch.nn.Linear(model.fc.out_features, num_classes) \n\n\"\"\"\n                  ________\n                  |      |\n      image ----> |resnet|  \\\n                  ________   \\\n                              \\\n                            ________           _______\n                            | minus |  ---->  | head  | ---->\n                            _________          _______\n                               /\n                              /\n                             /\n                  ________\n                  |      |\n -ve control ---> |resnet|\n                  ________\n\"\"\"\n\n# let's make our model work with 6 channels\ntrained_kernel = model.conv1.weight\nnew_conv = nn.Conv2d(6, 64, kernel_size=7, stride=2, padding=3, bias=False)\nwith torch.no_grad():\n    new_conv.weight[:] = torch.stack([torch.mean(trained_kernel, 1)]*6, dim=1)\nmodel.conv1 = new_conv\nmodel = model.to(device)\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_loader = D.DataLoader(ds, batch_size=batch_size, shuffle=True, num_workers=num_workers)\neval_loader = D.DataLoader(ds_val, batch_size=batch_size, shuffle=True, num_workers=num_workers)\ntloader = D.DataLoader(ds_test, batch_size=batch_size, shuffle=False, num_workers=num_workers)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Define functions for training and evaluation"},{"metadata":{"trusted":true},"cell_type":"code","source":"criterion = nn.CrossEntropyLoss()\noptimizer = torch.optim.Adam(model.parameters(), lr=0.0003)\n\ndef train(train_loader, model, criterion, optimizer, epoch):\n    model.train()\n    if epoch == 0:\n        # update only the last two FC layers\n        for name, child in model.named_children():\n            if (name != 'head') and (name != 'fc'):\n                for param in child.parameters():\n                    param.requires_grad = False\n    elif epoch == 3:\n        # enable update on all layers\n        for name, child in model.named_children():\n            for param in child.parameters():\n                param.requires_grad = True\n\n    loss_sum = 0\n    for input, target in tqdm(train_loader):\n        input1, input2 = input[:, :6].to(device), input[:, 6:].to(device)\n        target = target.to(device)\n\n        output = model.head(model(input1) - model(input2))\n        loss = criterion(output, target)\n        loss_sum += loss.data.cpu().numpy()\n\n        optimizer.zero_grad()\n        loss.backward()\n        optimizer.step()\n\n    return (loss_sum / len(train_loader))\n\ndef evaluate(eval_loader, model, criterion):\n    model.eval()\n    correct = 0\n    with torch.no_grad():\n        for input, target in tqdm(eval_loader):\n            input1, input2 = input[:, :6].to(device), input[:, 6:].to(device)\n            target = target.to(device)\n\n            output = model.head(model(input1) - model(input2))\n            preds = output.argmax(axis=1)\n            correct += (target == preds).sum()\n\n    return correct.cpu().numpy() * 100 / len(eval_loader.dataset)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Load from checkpoint"},{"metadata":{"trusted":true},"cell_type":"code","source":"\nmodel_file = 'model.pth'\nif os.path.exists(model_file):\n    print('loading model from checkpoint...')\n    checkpoint = torch.load(model_file)\n    model.load_state_dict(checkpoint['state_dict'])","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Train the model"},{"metadata":{"trusted":true},"cell_type":"code","source":"for epoch in range(max_epochs):\n    loss = train(train_loader, model, criterion, optimizer, epoch)\n    acc = evaluate(eval_loader, model, criterion)\n    print('epoch %d loss %.2f acc %.2f%%' % (epoch, loss, acc))\n    \n# save checkpoint\ntorch.save({'state_dict': model.state_dict()}, model_file)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Prediction for test"},{"metadata":{"trusted":true},"cell_type":"code","source":"model.eval()\nwith torch.no_grad():\n    preds = np.empty(0)\n    for input, _ in tqdm(tloader):\n        input1, input2 = input[:, :6].to(device), input[:, 6:].to(device)\n        output = model.head(model(input1) - model(input2))\n        idx = output.max(dim=-1)[1].cpu().numpy()\n        preds = np.append(preds, idx, axis=0)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"submission = pd.read_csv(path_data + '/test.csv')\nsubmission['sirna'] = preds.astype(int)\nsubmission.to_csv('submission.csv', index=False, columns=['id_code','sirna'])","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.6.8"},"widgets":{"application/vnd.jupyter.widget-state+json":{"state":{"2b62ae829edc4d60acf1d9a9e1d598d8":{"model_module":"@jupyter-widgets/controls","model_module_version":"1.4.0","model_name":"DescriptionStyleModel","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.4.0","_model_name":"DescriptionStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.1.0","_view_name":"StyleView","description_width":""}},"7740dfb227e54da8b1510dac2d094406":{"model_module":"@jupyter-widgets/base","model_module_version":"1.1.0","model_name":"LayoutModel","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.1.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.1.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"921a9c670b6e4a2db86c75a7ff5d9ee6":{"model_module":"@jupyter-widgets/base","model_module_version":"1.1.0","model_name":"LayoutModel","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.1.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.1.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"9dfcb7497f8842af817750eec565b8b9":{"model_module":"@jupyter-widgets/controls","model_module_version":"1.4.0","model_name":"HTMLModel","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.4.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.4.0","_view_name":"HTMLView","description":"","description_tooltip":null,"layout":"IPY_MODEL_921a9c670b6e4a2db86c75a7ff5d9ee6","placeholder":"​","style":"IPY_MODEL_2b62ae829edc4d60acf1d9a9e1d598d8","value":" 94% 2151/2283 [22:45&lt;01:23,  1.58it/s]"}},"d2df0eb5abab4e3895ec792681cfa8d2":{"model_module":"@jupyter-widgets/controls","model_module_version":"1.4.0","model_name":"ProgressStyleModel","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.4.0","_model_name":"ProgressStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.1.0","_view_name":"StyleView","bar_color":null,"description_width":"initial"}},"e3ff3ae302394523bb5b28ee009842d5":{"model_module":"@jupyter-widgets/controls","model_module_version":"1.4.0","model_name":"HBoxModel","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.4.0","_model_name":"HBoxModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.4.0","_view_name":"HBoxView","box_style":"","children":["IPY_MODEL_ff74a4321a59419cb24e116db9dd1e3e","IPY_MODEL_9dfcb7497f8842af817750eec565b8b9"],"layout":"IPY_MODEL_7740dfb227e54da8b1510dac2d094406"}},"fad7703039454db7af5d7fb4bce65003":{"model_module":"@jupyter-widgets/base","model_module_version":"1.1.0","model_name":"LayoutModel","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.1.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.1.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"ff74a4321a59419cb24e116db9dd1e3e":{"model_module":"@jupyter-widgets/controls","model_module_version":"1.4.0","model_name":"IntProgressModel","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.4.0","_model_name":"IntProgressModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.4.0","_view_name":"ProgressView","bar_style":"","description":"Loss: 128.54232788085938","description_tooltip":null,"layout":"IPY_MODEL_fad7703039454db7af5d7fb4bce65003","max":2283,"min":0,"orientation":"horizontal","style":"IPY_MODEL_d2df0eb5abab4e3895ec792681cfa8d2","value":2151}}},"version_major":2,"version_minor":0}}},"nbformat":4,"nbformat_minor":1}