{"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_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# from https://www.kaggle.com/samusram/hpa-rgb-model-rgby-cell-level-classification\n!pip install \"../input/keras-application/Keras_Applications-1.0.8-py3-none-any.whl\"\n!pip install \"../input/efficientnet111/efficientnet-1.1.1-py3-none-any.whl\"\n!pip install \"../input/tfexplainforoffline/tf_explain-0.2.1-py3-none-any.whl\"","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# added by myself\n!pip install \"../input/efficientnet-pytorch/EfficientNet-PyTorch/EfficientNet-PyTorch-master\"\n!pip install natsort","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# original in the 'Even Faster HPA Cell Segmentation'\n!pip install \"../input/pycocotools/pycocotools-2.0-cp37-cp37m-linux_x86_64.whl\"\n!pip install \"../input/hpapytorchzoozip/pytorch_zoo-master\"\n!pip install \"../input/hpacellsegmentatorraman/HPA-Cell-Segmentation/\"","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import warnings\nwith warnings.catch_warnings():\n    warnings.simplefilter(\"ignore\")\n    import numpy as np\n    import pandas as pd\n    import os\n    from tqdm import tqdm\n\n    import os.path\n    import urllib\n    import zipfile\n\n    from hpacellseg.cellsegmentator import *\n    from hpacellseg import cellsegmentator, utils\n    import cv2\n\n    import scipy.ndimage as ndi\n    from skimage import filters, measure, segmentation, transform, util\n    from skimage.morphology import (binary_erosion, closing, disk,\n                                    remove_small_holes, remove_small_objects)\n\n    from PIL import Image\n    import matplotlib.pyplot as plt\n    \nimport time","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from efficientnet_pytorch import EfficientNet\n\nimport torch\nimport torch.nn as nn\nimport torch.optim as optim \n\nimport torchvision\nfrom torch.utils.data import DataLoader, Dataset\nimport torch.utils.data\nfrom torchvision import transforms\n\nimport random\nfrom datetime import datetime","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"LBL_NAMES = [\"Nucleoplasm\", \"Nuclear Membrane\", \"Nucleoli\", \"Nucleoli Fibrillar Center\", \"Nuclear Speckles\", \n             \"Nuclear Bodies\", \"Endoplasmic Reticulum\", \"Golgi Apparatus\", \"Intermediate Filaments\", \n             \"Actin Filaments\", \"Microtubules\", \"Mitotic Spindle\", \"Centrosome\", \"Plasma Membrane\", \n             \"Mitochondria\", \"Aggresome\", \"Cytosol\", \"Vesicles\", \"Negative\"]","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_csv = pd.read_csv('../input/hpa-single-cell-image-classification/train.csv')\n\n# consider only the single label training images for now, because they are what we need for the model training\nsingle_label_train = train_csv[~train_csv['Label'].str.contains('\\|')].copy() # .copy() gets rid of the warning below\n# A value is trying to be set on a copy of a slice from a DataFrame. Try using .loc[row_indexer,col_indexer] = value instead\nsingle_label_train['Label'] = single_label_train['Label'].astype('int')\nsingle_label_train = single_label_train.sort_values(by = 'Label')\nsingle_label_train","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# create a dictionary for mapping from image ID to label\nimage_id_to_label = dict(zip(single_label_train['ID'], single_label_train['Label']))\n\n# create a dataframe consisting of three columns: file name, image ID and label\nfile_name_lst_1 = os.listdir(\n    '../input/hpatrainsegmentation2241sthalf/input/fast-cell-segmentation-outsize-224-first-half/train_cell_segmentation_224_1st_half')\nimage_ID_lst_1 = [e.split('_')[0] for e in file_name_lst_1]\nlabel_lst_1 = [image_id_to_label[key] for key in image_ID_lst_1]","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# create a dataframe consisting of three columns: file name, image ID and label\nfile_name_lst_2_orig = os.listdir(\n    \"../input/hpa-224-2nd-half/input/zeyusss/train_cell_segmentation\")\n\nfile_name_lst_2 = [e for e in file_name_lst_2_orig if e.split('_')[0] in image_id_to_label]\nimage_ID_lst_2 = [e.split('_')[0] for e in file_name_lst_2]\nlabel_lst_2 = [image_id_to_label[key] for key in image_ID_lst_2]","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# dictionary help deciding the directory\nimage_ID_half_dict = dict(zip(image_ID_lst_1 + image_ID_lst_2, [0]*len(image_ID_lst_1) + [1]*len(image_ID_lst_2)))","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"file_name_lst = file_name_lst_1 + file_name_lst_2\nimage_ID_lst = image_ID_lst_1 + image_ID_lst_2\nlabel_lst = label_lst_1 + label_lst_2","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"len(file_name_lst)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# out of memory when loading 85000 images, so cannot load all images at once.\n# now try following https://stanford.edu/~shervine/blog/pytorch-how-to-generate-data-parallel to build a dataloader\n\n# first decide the train, validation and test set split: 0.8, 0.1, 0.1\nrand_lst = np.random.randint(10, size = len(file_name_lst)).tolist()\n\ntmp_df_orig = pd.DataFrame(\n    list(zip(file_name_lst, image_ID_lst, label_lst, rand_lst)), columns =['file_name', 'image_id', 'label', 'rand'])\n\n#N_SAMPLE = 10000\ntmp_df = tmp_df_orig.sample(frac=1) # create a small subset for initial testing\ntmp_df = tmp_df\n\ntrain_df = tmp_df[tmp_df['rand'] <= 7].copy()\nvalid_df = tmp_df[tmp_df['rand'] == 8].copy()\ntest_df = tmp_df[tmp_df['rand'] == 9].copy()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"tmp_df_orig.shape","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 参考：\n# https://discuss.pytorch.org/t/how-to-load-images-without-using-imagefolder/59999/2\n# https://pytorch.org/tutorials/recipes/recipes/custom_dataset_transforms_loader.html\n\nfrom torch.utils.data import Dataset\nimport natsort\n\nclass CustomDataSet(Dataset):\n    def __init__(self, main_dir_1, main_dir_2, file_ids, img_id_to_label, img_id_to_dir, transform = None):\n        # file_to_label is a dictionary with key = file name (e.g. 'xxxxx.png') and value = label (e.g. '0')\n        # file_ids is a list of file names, e.g. ['xxxx.png', 'yyyy.png']\n        self.main_dir = main_dir_1\n        self.main_dir_2 = main_dir_2 # because we have two directories\n        self.transform = transform\n        all_imgs_1 = os.listdir(main_dir_1)\n        all_imgs_2 = os.listdir(main_dir_2)\n        if set(file_ids).issubset(set(all_imgs_1).union(set(all_imgs_2))):\n            imgs_for_use = file_ids\n        else:\n            print('Input file IDs `file_ids` must be a subset of the files names in `main_dir`.')\n            return\n        self.total_imgs = natsort.natsorted(file_ids)\n        self.img_id_to_label = img_id_to_label\n        self.img_id_to_dir = img_id_to_dir\n\n    def __len__(self):\n        return len(self.total_imgs)\n\n    def __getitem__(self, idx):\n        img_id = self.total_imgs[idx].split('_')[0]\n        if self.img_id_to_dir[img_id] == 0:\n            img_loc = os.path.join(self.main_dir, self.total_imgs[idx])\n        else:\n            img_loc = os.path.join(self.main_dir_2, self.total_imgs[idx])\n        image = Image.open(img_loc).convert(\"RGB\")\n        tensor_image = self.transform(image)\n        return (tensor_image, self.img_id_to_label[img_id])\n\nimg_folder_path = '../input/hpatrainsegmentation2241sthalf/input/fast-cell-segmentation-outsize-224-first-half/train_cell_segmentation_224_1st_half/'","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import torchvision.transforms as transforms\ntransformations = transforms.Compose([\n    #transforms.Resize(255),\n    #transforms.CenterCrop(224),\n    transforms.ToTensor()#,\n    #transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])\n])","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# first manually create a train dataset, a validation dataset and a test dataset\n# then manually create a train_loader, a valid_loader and a test_loader based on the above datasets\ntrain_dataset = CustomDataSet(\n    '../input/hpatrainsegmentation2241sthalf/input/fast-cell-segmentation-outsize-224-first-half/train_cell_segmentation_224_1st_half', \n    \"../input/hpa-224-2nd-half/input/zeyusss/train_cell_segmentation\", \n    train_df['file_name'].tolist(), image_id_to_label, image_ID_half_dict, transformations)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"len(train_dataset)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Stop here!","metadata":{}},{"cell_type":"code","source":"batch_size = 64\ntrain_loader = torch.utils.data.DataLoader(train_dataset, batch_size=batch_size, shuffle=True, \n                               num_workers=4, drop_last=True)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# first manually create a train dataset, a validation dataset and a test dataset\n# then manually create a train_loader, a valid_loader and a test_loader based on the above datasets\nvalid_dataset = CustomDataSet(\n    '../input/hpatrainsegmentation2241sthalf/input/fast-cell-segmentation-outsize-224-first-half/train_cell_segmentation_224_1st_half', \n    \"../input/hpa-224-2nd-half/input/zeyusss/train_cell_segmentation\", \n    valid_df['file_name'].tolist(), image_id_to_label, image_ID_half_dict, transformations)\n\nvalid_loader = torch.utils.data.DataLoader(valid_dataset, batch_size=batch_size, shuffle=True, \n                               num_workers=4, drop_last=True)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"n_classes = 19\nmodel = EfficientNet.from_pretrained('efficientnet-b0', num_classes = n_classes)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model._fc # replace this to change the number of out_features (should be 19 - number of classes in the dataset)\n\nfor param in model.parameters():\n    param.requires_grad = False\n    \n# Replace the last fully-connected layer\n# Parameters of newly constructed modules have requires_grad=True by default\n#model._fc = nn.Linear(model._fc.in_features, n_classes)\nmodel._fc.weight.requires_grad = True\n\nmodel = model.to('cuda')","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model._fc","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"optimizer = optim.Adam(model.parameters())\nloss_func = nn.CrossEntropyLoss()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# save testset\ntrain_df.to_csv('train_df.csv')\nvalid_df.to_csv('valid_df.csv')\ntest_df.to_csv('test_df.csv')","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import copy\n\nn_epochs = 10\noptimizer = optim.Adam(model.parameters())\ncriterion = nn.CrossEntropyLoss()\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n\nepoch_train_losses = []\nepoch_train_accs = []\nepoch_valid_losses = []\nepoch_valid_accs = []\nbest_acc = -1\n\nfor epoch in range(n_epochs):\n    print('Epoch ' + str(epoch))\n    train_batch_loss = []\n    train_batch_acc = []\n    \n    now = datetime.now()\n    current_time = now.strftime(\"%H:%M:%S\")\n    print(current_time)\n    t1 = time.time()\n    \n    for batch_idx, (data, target) in enumerate(train_loader):\n        data, target = data.to(device), target.to(device)\n        \n        optimizer.zero_grad()\n        outputs = model(data)\n        loss = criterion(outputs, target)\n        train_batch_loss.append(loss.item())\n        \n        _, predicted = torch.max(outputs, 1)\n        acc = sum(torch.argmax(outputs, 1) == target) / batch_size\n        train_batch_acc.append(acc.item())\n        \n        loss.backward()\n        optimizer.step()        \n        \n        t2 = time.time()\n        if batch_idx % 50 == 0:\n            print(str(batch_idx) + '-th batch runtime since epoch begins: ' + str(t2 - t1))\n        \n    epoch_train_losses.append(sum(train_batch_loss)/len(train_batch_loss))\n    epoch_train_accs.append(sum(train_batch_acc)/len(train_batch_acc))\n    \n    # calculate validation accuracy and losses\n    valid_batch_loss = []\n    valid_batch_accs = []\n    with torch.no_grad():\n        for i, (data, target) in enumerate(valid_loader):\n            data = data.to(device)\n            target = target.to(device)\n            valid_outputs = model(data)\n            \n            valid_loss = criterion(valid_outputs, target)\n            valid_batch_loss.append(valid_loss.item())\n            \n            _, predicted = torch.max(valid_outputs, 1)\n            acc = sum(torch.argmax(valid_outputs, 1) == target) / batch_size\n            valid_batch_accs.append(acc.item())\n            \n    epoch_valid_losses.append((sum(valid_batch_loss)/len(valid_batch_loss)))\n    epoch_valid_accs.append((sum(valid_batch_accs)/len(valid_batch_accs)))\n    \n    if epoch_valid_accs[-1] > best_acc:\n        best_acc = epoch_valid_accs[-1]\n        best_model_seen = copy.deepcopy(model.state_dict())\n        torch.save(best_model_seen, 'efficient_net_epoch_' + str(n_epochs) + '_batch_' + str(batch_size) + '.pt')\n        \n    print('epoch: \\t', epoch, '\\t training loss: \\t', epoch_train_losses)\n    print('epoch: \\t', epoch, '\\t training acc: \\t', epoch_train_accs)\n    print('epoch: \\t', epoch, '\\t validation loss: \\t', epoch_valid_losses)\n    print('epoch: \\t', epoch, '\\t validation acc: \\t', epoch_valid_accs)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# save model\ntorch.save(best_model_seen, 'efficient_net_epoch_' + str(n_epochs) + '_batch_' + str(batch_size) + '.pt')","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# save train/validation loss and accuracy\n#epoch_train_losses_arr = np.array(epoch_train_losses)\n#epoch_train_accs_arr = np.array(epoch_train_accs)\n#epoch_valid_losses_arr = np.array(epoch_valid_losses)\n#epoch_valid_accs_arr = np.array(epoch_valid_accs)\n\ntrain_valid_performance = pd.DataFrame(\n    list(zip(list(range(n_epochs)), epoch_train_losses, epoch_train_accs, epoch_valid_losses, epoch_valid_accs)), \n    columns =['epoch', 'train_losses', 'train_accs', 'valid_losses', 'valid_accs'])\ntrain_valid_performance.to_csv('train_valid_performance.csv')","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}