{"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":"markdown","source":"# This kernel is U-Net Baseline written by PyTorch\nIn this kernel, there are many places that are simplified now.  \nSo, you should fix these bad points.  \n\n[U-Net web site](https://lmb.informatik.uni-freiburg.de/people/ronneber/u-net/)  \n[U-Net paper](https://arxiv.org/abs/1505.04597)  \n\nI reference [this blog post](https://lp-tech.net/articles/hzfn7?page=2  ) in U-Net installation.  \nThank you awesome this blog post.  \n\nThis is [my EDA](https://www.kaggle.com/go1dfish/fgvc6-simple-eda).  \nIf you don't know this competition rule and data, this EDA might help you.  ","metadata":{}},{"cell_type":"markdown","source":"# Import modules","metadata":{}},{"cell_type":"code","source":"# semantic segmentation 목표: label 에 가깜게 image segmentaion 하는 모델 만들기!\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\nimport os\nprint(os.listdir(\"../input\"))\nimport cv2\nimport matplotlib.pyplot as plt\n%matplotlib inline\nfrom tqdm import tqdm_notebook as tqdm\n\n# torch import\n\nimport torch\nimport torch.nn as nn\nfrom torch import optim\nimport torchvision.transforms as transforms\nimport torch.nn.functional as F\nfrom torch.autograd import Function, Variable\nfrom pathlib import Path\nfrom itertools import groupby","metadata":{"_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"input_dir = \"../input/\"\ntrain_img_dir = \"../input/train/\"\ntest_img_dir = \"../input/test/\"\n\n# 이미지 사이즈\nWIDTH = 512\nHEIGHT = 512\n\n# 카테고리 개수\ncategory_num = 46 + 1\n\n# 하이퍼파라미터 설저\nratio = 8\nepoch_num = 8\nbatch_size = 4\n\ndevice = \"cuda:0\"","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"len(os.listdir(\"../input/train/\"))","metadata":{"execution":{"iopub.status.busy":"2021-06-09T08:28:17.891348Z","iopub.execute_input":"2021-06-09T08:28:17.891622Z","iopub.status.idle":"2021-06-09T08:28:21.149437Z","shell.execute_reply.started":"2021-06-09T08:28:17.891561Z","shell.execute_reply":"2021-06-09T08:28:21.148786Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"len(os.listdir(\"../input/test/\"))","metadata":{"execution":{"iopub.status.busy":"2021-06-09T08:28:21.150609Z","iopub.execute_input":"2021-06-09T08:28:21.150923Z","iopub.status.idle":"2021-06-09T08:28:21.647453Z","shell.execute_reply.started":"2021-06-09T08:28:21.150826Z","shell.execute_reply":"2021-06-09T08:28:21.646861Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_df = pd.read_csv(input_dir + \"train.csv\")\ntrain_df.head()","metadata":{"execution":{"iopub.status.busy":"2021-06-09T08:28:21.650308Z","iopub.execute_input":"2021-06-09T08:28:21.650545Z","iopub.status.idle":"2021-06-09T08:28:53.283171Z","shell.execute_reply.started":"2021-06-09T08:28:21.6505Z","shell.execute_reply":"2021-06-09T08:28:53.282258Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_df.shape","metadata":{"execution":{"iopub.status.busy":"2021-06-09T08:28:53.287267Z","iopub.execute_input":"2021-06-09T08:28:53.289296Z","iopub.status.idle":"2021-06-09T08:28:53.298058Z","shell.execute_reply.started":"2021-06-09T08:28:53.289242Z","shell.execute_reply":"2021-06-09T08:28:53.297377Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Define utils\nFor simplicity, It focus only category","metadata":{"_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a"}},{"cell_type":"code","source":"#원핫벡터 생성하기 (고유 벡터 적용)\ndef make_onehot_vec(x):\n    vec = np.zeros(category_num)\n    vec[x] = 1\n    return vec","metadata":{"execution":{"iopub.status.busy":"2021-06-09T08:28:53.302687Z","iopub.execute_input":"2021-06-09T08:28:53.30355Z","iopub.status.idle":"2021-06-09T08:28:53.310391Z","shell.execute_reply.started":"2021-06-09T08:28:53.3035Z","shell.execute_reply":"2021-06-09T08:28:53.30978Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 이미지 마스크 만들기\n\n\ndef make_mask_img(segment_df):\n    \n    # 이미지 사이즈 조정하기\n    seg_width = segment_df.at[0, \"Width\"]\n    seg_height = segment_df.at[0, \"Height\"]\n    seg_img = np.full(seg_width*seg_height, category_num-1, dtype=np.int32)\n    \n    # 픽셀 인코딩\n    for encoded_pixels, class_id in zip(segment_df[\"EncodedPixels\"].values, segment_df[\"ClassId\"].values):\n        pixel_list = list(map(int, encoded_pixels.split(\" \")))\n        for i in range(0, len(pixel_list), 2):\n            start_index = pixel_list[i] - 1\n            index_len = pixel_list[i+1] - 1\n            seg_img[start_index:start_index+index_len] = int(class_id.split(\"_\")[0])\n            \n    # image segmentation reshaping\n    seg_img = seg_img.reshape((seg_height, seg_width), order='F')\n    seg_img = cv2.resize(seg_img, (WIDTH, HEIGHT), interpolation=cv2.INTER_NEAREST)\n    return seg_img","metadata":{"execution":{"iopub.status.busy":"2021-06-09T08:28:53.312261Z","iopub.execute_input":"2021-06-09T08:28:53.313356Z","iopub.status.idle":"2021-06-09T08:28:53.327596Z","shell.execute_reply.started":"2021-06-09T08:28:53.313304Z","shell.execute_reply":"2021-06-09T08:28:53.326793Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"    \"\"\"\n    seg_img_onehot = np.zeros((HEIGHT, WIDTH, category_num), dtype=np.int32)\n    #seg_img_onehot = np.zeros((seg_height//ratio, seg_width//ratio, category_num), dtype=np.int32)\n    # OPTIMIZE: slow\n    for ind in range(HEIGHT):\n        for col in range(WIDTH):\n            seg_img_onehot[ind, col] = make_onehot_vec(seg_img[ind, col])\n    \"\"\"","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def train_generator(df, batch_size):\n    img_ind_num = df.groupby(\"ImageId\")[\"ClassId\"].count()\n    index = df.index.values[0]\n    trn_images = []\n    seg_images = []\n    for i, (img_name, ind_num) in enumerate(img_ind_num.items()):\n        img = cv2.imread(train_img_dir + img_name)\n        img = cv2.resize(img, (WIDTH, HEIGHT), interpolation=cv2.INTER_AREA)\n        segment_df = (df.loc[index:index+ind_num-1, :]).reset_index(drop=True)\n        index += ind_num\n        if segment_df[\"ImageId\"].nunique() != 1:\n            raise Exception(\"Index Range Error\")\n        seg_img = make_mask_img(segment_df)\n        \n        # HWC -> CHW\n        img = img.transpose((2, 0, 1))\n        #seg_img = seg_img.transpose((2, 0, 1))\n        \n        trn_images.append(img)\n        seg_images.append(seg_img)\n        if((i+1) % batch_size == 0):\n            yield np.array(trn_images, dtype=np.float32) / 255, np.array(seg_images, dtype=np.int32)\n            trn_images = []\n            seg_images = []\n    if(len(trn_images) != 0):\n        yield np.array(trn_images, dtype=np.float32) / 255, np.array(seg_images, dtype=np.int32)","metadata":{"execution":{"iopub.status.busy":"2021-06-09T08:28:53.33204Z","iopub.execute_input":"2021-06-09T08:28:53.33383Z","iopub.status.idle":"2021-06-09T08:28:53.347029Z","shell.execute_reply.started":"2021-06-09T08:28:53.333775Z","shell.execute_reply":"2021-06-09T08:28:53.34636Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def test_generator(df):\n    img_names = df[\"ImageId\"].values\n    for img_name in img_names:\n        img = cv2.imread(test_img_dir + img_name)\n        img = cv2.resize(img, (WIDTH, HEIGHT), interpolation=cv2.INTER_AREA)\n        # HWC -> CHW\n        img = img.transpose((2, 0, 1))\n        yield img_name, np.asarray([img], dtype=np.float32) / 255","metadata":{"execution":{"iopub.status.busy":"2021-06-09T08:28:53.351474Z","iopub.execute_input":"2021-06-09T08:28:53.353989Z","iopub.status.idle":"2021-06-09T08:28:53.361341Z","shell.execute_reply.started":"2021-06-09T08:28:53.353912Z","shell.execute_reply":"2021-06-09T08:28:53.360533Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def encode(input_string):\n    return [(len(list(g)), k) for k,g in groupby(input_string)]\n\ndef run_length(label_vec):\n    encode_list = encode(label_vec)\n    index = 1\n    class_dict = {}\n    for i in encode_list:\n        if i[1] != category_num-1:\n            if i[1] not in class_dict.keys():\n                class_dict[i[1]] = []\n            class_dict[i[1]] = class_dict[i[1]] + [index, i[0]]\n        index += i[0]\n    return class_dict","metadata":{"execution":{"iopub.status.busy":"2021-06-09T08:28:53.365552Z","iopub.execute_input":"2021-06-09T08:28:53.367948Z","iopub.status.idle":"2021-06-09T08:28:53.376255Z","shell.execute_reply.started":"2021-06-09T08:28:53.367896Z","shell.execute_reply":"2021-06-09T08:28:53.375394Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Define Network","metadata":{}},{"cell_type":"code","source":"class double_conv(nn.Module):\n    '''(conv => BN => ReLU) * 2'''\n    def __init__(self, in_ch, out_ch):\n        \n        # \n        super(double_conv, self).__init__()\n        self.conv = nn.Sequential(\n            nn.Conv2d(in_ch, out_ch, 3, padding=1),\n            nn.BatchNorm2d(out_ch),\n            nn.ReLU(inplace=True),\n            nn.Conv2d(out_ch, out_ch, 3, padding=1),\n            nn.BatchNorm2d(out_ch),\n            nn.ReLU(inplace=True)\n        )\n# __init__ 함수에서 선언한 layer들 연결해서 data propa flow 만들기\n    def forward(self, x):\n        x = self.conv(x)\n        return x\n\n\nclass inconv(nn.Module):\n    def __init__(self, in_ch, out_ch):\n        super(inconv, self).__init__()\n        self.conv = double_conv(in_ch, out_ch)\n\n    def forward(self, x):\n        x = self.conv(x)\n        return x\n\n# down\nclass down(nn.Module):\n    def __init__(self, in_ch, out_ch):\n        super(down, self).__init__()\n        self.mpconv = nn.Sequential(\n            nn.MaxPool2d(2),\n            double_conv(in_ch, out_ch)\n        )\n\n    def forward(self, x):\n        x = self.mpconv(x)\n        return x\n\n\nclass up(nn.Module):\n    def __init__(self, in_ch, out_ch, bilinear=True):\n        super(up, self).__init__()\n        \n        if bilinear:\n            self.up = nn.Upsample(scale_factor=2, mode='bilinear', align_corners=True)\n        else:\n            self.up = nn.ConvTranspose2d(in_ch//2, in_ch//2, 2, stride=2)\n\n        self.conv = double_conv(in_ch, out_ch)\n\n    def forward(self, x1, x2):\n        x1 = self.up(x1)\n        diffX = x1.size()[2] - x2.size()[2]\n        diffY = x1.size()[3] - x2.size()[3]\n        x2 = F.pad(x2, (diffX // 2, int(diffX / 2),\n                        diffY // 2, int(diffY / 2)))\n        x = torch.cat([x2, x1], dim=1)\n        x = self.conv(x)\n        return x\n\n\nclass outconv(nn.Module):\n    def __init__(self, in_ch, out_ch):\n        super(outconv, self).__init__()\n        self.conv = nn.Conv2d(in_ch, out_ch, 1)\n\n    def forward(self, x):\n        x = self.conv(x)\n        return x\n\n### UNet 모델링\n    \n # 'nn.Module' 이라는 파이토치 base class를 상속받아서 \n # 사용자 정의 network 만들기\n # UNet class가 instance로 할당될때 초기화되는 함수 __init__, 이 함수에서 \n # 네트워크에 사용될 layer들을 전부 self.net으로 선언\n\n    \nclass UNet(nn.Module):\n    def __init__(self, n_channels, n_classes):\n# super(subclass, self) : subclass에서 base class의 내용을 오버라이드해서 사용하고 싶을 때\n        super(UNet, self).__init__()\n        self.inc = inconv(n_channels, 64)\n        \n        # Contracting path\n        self.down1 = down(64, 128)\n        self.down2 = down(128, 256)\n        self.down3 = down(256, 512)\n        self.down4 = down(512, 512)\n        self.up1 = up(1024, 256)\n        self.up2 = up(512, 128)\n        self.up3 = up(256, 64)\n        self.up4 = up(128, 64)\n        self.outc = outconv(64, n_classes)\n\n# __init__ 함수에서 선언한 layer들 연결해서 data propa flow 만들기\n    def forward(self, x):\n        x1 = self.inc(x)\n        x2 = self.down1(x1)\n        x3 = self.down2(x2)\n        x4 = self.down3(x3)\n        x5 = self.down4(x4)\n        x = self.up1(x5, x4)\n        x = self.up2(x, x3)\n        x = self.up3(x, x2)\n        x = self.up4(x, x1)\n        x = self.outc(x)\n        return x # data가 모든 layer를 거쳐서 나온 output 값","metadata":{"execution":{"iopub.status.busy":"2021-06-09T08:28:53.380704Z","iopub.execute_input":"2021-06-09T08:28:53.383205Z","iopub.status.idle":"2021-06-09T08:28:53.409316Z","shell.execute_reply.started":"2021-06-09T08:28:53.383155Z","shell.execute_reply":"2021-06-09T08:28:53.408597Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Training","metadata":{}},{"cell_type":"code","source":"train_df.shape","metadata":{"execution":{"iopub.status.busy":"2021-06-09T08:28:53.414295Z","iopub.execute_input":"2021-06-09T08:28:53.416819Z","iopub.status.idle":"2021-06-09T08:28:53.425789Z","shell.execute_reply.started":"2021-06-09T08:28:53.416727Z","shell.execute_reply":"2021-06-09T08:28:53.425025Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"333415 // 4","metadata":{"execution":{"iopub.status.busy":"2021-06-09T08:28:53.43017Z","iopub.execute_input":"2021-06-09T08:28:53.432362Z","iopub.status.idle":"2021-06-09T08:28:53.439844Z","shell.execute_reply.started":"2021-06-09T08:28:53.432312Z","shell.execute_reply":"2021-06-09T08:28:53.439121Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_df.iloc[83348:83354, :]","metadata":{"execution":{"iopub.status.busy":"2021-06-09T08:28:53.441216Z","iopub.execute_input":"2021-06-09T08:28:53.442169Z","iopub.status.idle":"2021-06-09T08:28:53.473972Z","shell.execute_reply.started":"2021-06-09T08:28:53.442115Z","shell.execute_reply":"2021-06-09T08:28:53.473315Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_df.iloc[73350:73354, :]","metadata":{"execution":{"iopub.status.busy":"2021-06-09T08:28:53.477678Z","iopub.execute_input":"2021-06-09T08:28:53.47967Z","iopub.status.idle":"2021-06-09T08:28:53.505524Z","shell.execute_reply.started":"2021-06-09T08:28:53.479611Z","shell.execute_reply":"2021-06-09T08:28:53.504877Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"For simplicity, use about 25% data.  ","metadata":{}},{"cell_type":"code","source":"net = UNet(n_channels=3, n_classes=category_num).to(device)\n\noptimizer = optim.SGD(\n    net.parameters(),\n    lr=0.1,\n    momentum=0.9,\n    weight_decay=0.0005\n)\n\ncriterion = nn.CrossEntropyLoss()","metadata":{"execution":{"iopub.status.busy":"2021-06-09T08:28:53.509222Z","iopub.execute_input":"2021-06-09T08:28:53.511203Z","iopub.status.idle":"2021-06-09T08:28:57.205421Z","shell.execute_reply.started":"2021-06-09T08:28:53.511153Z","shell.execute_reply":"2021-06-09T08:28:57.204642Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"val_sta = 73352\nval_end = 83351\ntrain_loss = []\nvalid_loss = []\nfor epoch in range(epoch_num):\n    epoch_trn_loss = 0\n    train_len = 0\n    net.train()\n    for iteration, (X_trn, Y_trn) in enumerate(tqdm(train_generator(train_df.iloc[:val_sta, :], batch_size))):\n        X = torch.tensor(X_trn, dtype=torch.float32).to(device)\n        Y = torch.tensor(Y_trn, dtype=torch.long).to(device)\n        train_len += len(X)\n        \n        #Y_flat = Y.view(-1)\n        mask_pred = net(X)\n        #mask_prob = torch.softmax(mask_pred, dim=1)\n        #mask_prob_flat = mask_prob.view(-1)\n        loss = criterion(mask_pred, Y)\n        \n        # backward\n        optimizer.zero_grad()\n        loss.backward()\n        optimizer.step()\n        \n        # loss 값 저장\n        epoch_trn_loss += loss.item()\n        \n        if iteration % 100 == 0:\n            print(\"train loss in {:0>2}epoch  /{:>5}iter:    {:<10.8}\".format(epoch+1, iteration, epoch_trn_loss/(iteration+1)))\n        \n    train_loss.append(epoch_trn_loss/(iteration+1))\n    print(\"train {}epoch loss({}iteration):    {:10.8}\".format(epoch+1, iteration, train_loss[-1]))\n    \n    epoch_val_loss = 0\n    val_len = 0\n    net.eval()\n    for iteration, (X_val, Y_val) in enumerate(tqdm(train_generator(train_df.iloc[val_sta:val_end, :], batch_size))):\n        \n        # 데이터를 device로 옮기기\n        X = torch.tensor(X_val, dtype=torch.float32).to(device)\n        Y = torch.tensor(Y_val, dtype=torch.long).to(device)\n        val_len += len(X)\n        \n        #Y_flat = Y.view(-1)\n        \n        mask_pred = net(X)\n        #mask_prob = torch.softmax(mask_pred, dim=1)\n        #mask_prob_flat = mask_prob.view(-1)\n        loss = criterion(mask_pred, Y)\n        epoch_val_loss += loss.item()\n        \n        if iteration % 100 == 0:\n            print(\"valid loss in {:0>2}epoch  /{:>5}iter:    {:<10.8}\".format(epoch+1, iteration, epoch_val_loss/(iteration+1)))\n        \n    valid_loss.append(epoch_val_loss/(iteration+1))\n    print(\"valid {}epoch loss({}iteration):    {:10.8}\".format(epoch+1, iteration, valid_loss[-1]))","metadata":{"execution":{"iopub.status.busy":"2021-06-09T08:28:57.206801Z","iopub.execute_input":"2021-06-09T08:28:57.207232Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#plt.plot(list(range(epoch_num)), train_loss, color='green')\n#plt.plot(list(range(epoch_num)), valid_loss, color='blue')","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Test","metadata":{}},{"cell_type":"code","source":"sample_df = pd.read_csv(input_dir + \"sample_submission.csv\")","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import torch\nimport gc\nfor obj in gc.get_objects():\n    try:\n        if torch.is_tensor(obj) or (hasattr(obj, 'data') and torch.is_tensor(obj.data)):\n            print(type(obj), obj.size())\n    except:\n        pass","metadata":{"_kg_hide-input":true,"_kg_hide-output":true,"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sub_list = []\nnet.eval()\n\nfor img_name, img in test_generator(sample_df):\n\n    X = torch.tensor(img, dtype=torch.float32).to(device)\n    mask_pred = net(X)\n    mask_pred = mask_pred.cpu().detach().numpy()\n    mask_prob = np.argmax(mask_pred, axis=1)\n    mask_prob = mask_prob.ravel(order='F')\n    class_dict = run_length(mask_prob)\n    if len(class_dict) == 0:\n        sub_list.append([img_name, \"1 1\", 1])\n    else:\n        for key, val in class_dict.items():\n            sub_list.append([img_name, \" \".join(map(str, val)), key])\n            \n    try:\n        img = cv2.imread(train_img_dir + img_name)\n        img = cv2.resize(img, (WIDTH, HEIGHT), interpolation=cv2.INTER_AREA)\n\n    except Exception as e:\n        print(str(e))","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Make Submission File","metadata":{}},{"cell_type":"code","source":"submission_df = pd.DataFrame(sub_list, columns=sample_df.columns.values)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"submission_df","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"submission_df.to_csv(\"submission.csv\", index=False)","metadata":{"trusted":true},"execution_count":null,"outputs":[]}]}