{"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":"# 事前準備","metadata":{}},{"cell_type":"code","source":"!pip install segmentation-models-pytorch","metadata":{"execution":{"iopub.status.busy":"2021-12-18T10:46:19.808989Z","iopub.execute_input":"2021-12-18T10:46:19.80927Z","iopub.status.idle":"2021-12-18T10:46:33.85251Z","shell.execute_reply.started":"2021-12-18T10:46:19.809233Z","shell.execute_reply":"2021-12-18T10:46:33.851582Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import os.path as osp\nfrom glob import glob\nimport json\nimport random\nimport cv2\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nfrom tqdm.notebook import tqdm\nfrom sklearn.model_selection import train_test_split\n\nimport torch\nimport torch.optim as optim\nimport torch.nn as nn\nimport torch.utils.data as data\n\nimport albumentations as A\nfrom albumentations.pytorch import ToTensorV2\n\nimport segmentation_models_pytorch as smp\nfrom segmentation_models_pytorch.encoders import get_preprocessing_fn","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2021-12-18T10:46:33.854565Z","iopub.execute_input":"2021-12-18T10:46:33.854864Z","iopub.status.idle":"2021-12-18T10:46:39.557363Z","shell.execute_reply.started":"2021-12-18T10:46:33.854816Z","shell.execute_reply":"2021-12-18T10:46:39.556543Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def fix_seed(seed):\n    # random\n    random.seed(seed)\n    # Numpy\n    np.random.seed(seed)\n    # Pytorch\n    torch.manual_seed(seed)\n    torch.cuda.manual_seed_all(seed)\n    torch.backends.cudnn.deterministic = True","metadata":{"execution":{"iopub.status.busy":"2021-12-18T10:46:53.543468Z","iopub.execute_input":"2021-12-18T10:46:53.543725Z","iopub.status.idle":"2021-12-18T10:46:53.54808Z","shell.execute_reply.started":"2021-12-18T10:46:53.543694Z","shell.execute_reply":"2021-12-18T10:46:53.547352Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Seed値の固定\nSEED = 42\nfix_seed(SEED)\n\n# パスの定義\nrootpath = \"../input/driving-segmentation-inclass-competition/\"\ntrain_dir = osp.join(rootpath, \"train\")\ntest_dir = osp.join(rootpath, \"test\")\nconfig_path = osp.join(rootpath, \"config.json\")\nwith open(config_path) as f:\n    config = json.load(f)\n\n# 定数を定義\nclass_num = len(config['labels'])\nimg_size = (1920, 1080)","metadata":{"execution":{"iopub.status.busy":"2021-12-18T10:46:53.883234Z","iopub.execute_input":"2021-12-18T10:46:53.883961Z","iopub.status.idle":"2021-12-18T10:46:53.898636Z","shell.execute_reply.started":"2021-12-18T10:46:53.883922Z","shell.execute_reply":"2021-12-18T10:46:53.897977Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# idをキーとしてlabelの情報を格納するdictを作成\nlabels_map = {}\nfor label_dict in config['labels']:\n    labels_map[label_dict['id']] = label_dict\n    del label_dict['id']\nlabels_map","metadata":{"execution":{"iopub.status.busy":"2021-12-18T10:46:55.273385Z","iopub.execute_input":"2021-12-18T10:46:55.273634Z","iopub.status.idle":"2021-12-18T10:46:55.285483Z","shell.execute_reply.started":"2021-12-18T10:46:55.273605Z","shell.execute_reply":"2021-12-18T10:46:55.284636Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"img_list, anno_list = sorted(glob(osp.join(train_dir, 'frames/*'))), sorted(glob(osp.join(train_dir, 'labels/*')))\nlen(img_list), len(anno_list)","metadata":{"execution":{"iopub.status.busy":"2021-12-18T10:46:55.531051Z","iopub.execute_input":"2021-12-18T10:46:55.531876Z","iopub.status.idle":"2021-12-18T10:46:55.792532Z","shell.execute_reply.started":"2021-12-18T10:46:55.531823Z","shell.execute_reply":"2021-12-18T10:46:55.791862Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Datasetの作成","metadata":{"execution":{"iopub.status.busy":"2021-12-15T20:42:00.878921Z","iopub.execute_input":"2021-12-15T20:42:00.879252Z","iopub.status.idle":"2021-12-15T20:42:00.884049Z","shell.execute_reply.started":"2021-12-15T20:42:00.87922Z","shell.execute_reply":"2021-12-15T20:42:00.882903Z"}}},{"cell_type":"code","source":"# (RGB)の色の平均値と標準偏差\ninput_size = 512\nencoder = 'resnet34'\n\n# pytorch-segmentationの前処理関数を取得\npreprocess_input = get_preprocessing_fn(encoder, pretrained='imagenet')\n\n# 訓練用\ntrain_transform = A.Compose([\n    A.Rotate((-10, 10)),  # 回転\n    A.HorizontalFlip(p=0.5),  # ランダムミラー\n    A.Resize(input_size, input_size),  # リサイズ(input_size)\n    A.Lambda(image=preprocess_input), # 前処理の実行\n    ToTensorV2(),\n])\n\n# 検証用\nval_transform = A.Compose([\n    A.Resize(input_size, input_size),  # リサイズ(input_size)\n    A.Lambda(image=preprocess_input), # 前処理の実行\n    ToTensorV2(),\n])","metadata":{"execution":{"iopub.status.busy":"2021-12-18T10:46:59.424148Z","iopub.execute_input":"2021-12-18T10:46:59.424933Z","iopub.status.idle":"2021-12-18T10:46:59.433661Z","shell.execute_reply.started":"2021-12-18T10:46:59.424896Z","shell.execute_reply":"2021-12-18T10:46:59.432956Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class Dataset(data.Dataset):\n\n    def __init__(self, img_list, anno_list=None, transform=None):\n        self.img_list = img_list\n        self.anno_list = anno_list\n        self.transform = transform\n\n    def __len__(self):\n        '''画像の枚数を返す'''\n        return len(self.img_list)\n\n    def __getitem__(self, index):\n        '''\n        前処理をした画像のTensor形式のデータとアノテーションを取得\n        '''\n        # 1. 画像読み込み\n        image_file_path = self.img_list[index]\n        img = cv2.imread(image_file_path)   # [高さ][幅][色RGB]\n        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n\n        # 2. アノテーション画像読み込み\n        anno_class_img = torch.tensor(float('nan'))\n        if self.anno_list is not None:\n            anno_file_path = self.anno_list[index]\n            anno_class_img = cv2.imread(anno_file_path, cv2.IMREAD_GRAYSCALE)  # [高さ][幅]\n\n        # 3. 前処理を実施\n        if self.transform is not None:\n            if self.anno_list is not None:\n                transformed = self.transform(image=img, mask=anno_class_img)\n                img, anno_class_img = transformed['image'], transformed['mask']\n            else:\n                transformed = self.transform(image=img)\n                img = transformed['image']\n        return img.float(), anno_class_img.float()","metadata":{"execution":{"iopub.status.busy":"2021-12-18T10:46:59.664844Z","iopub.execute_input":"2021-12-18T10:46:59.665062Z","iopub.status.idle":"2021-12-18T10:46:59.673836Z","shell.execute_reply.started":"2021-12-18T10:46:59.665038Z","shell.execute_reply":"2021-12-18T10:46:59.673158Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 動作確認\n\n# データ分割\ntrain_img_list, val_img_list, train_anno_list, val_anno_list = train_test_split(img_list, anno_list, random_state=SEED)\n\n# データセット作成\ntrain_dataset = Dataset(train_img_list, train_anno_list, transform=train_transform)\nval_dataset = Dataset(val_img_list, val_anno_list, transform=val_transform)\n\n# データの取り出し例\nimg, anno_class_img = val_dataset[0]\nprint(img.shape)\nprint(anno_class_img.shape)\n\n# 画像の描画\nimg_val = img.numpy().transpose((1, 2, 0))\nplt.imshow(img_val)\nplt.show()\n\n# アノテーション画像の表示\nanno_class_img_val = anno_class_img.numpy()\nplt.imshow(anno_class_img_val)\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2021-12-18T10:46:59.87266Z","iopub.execute_input":"2021-12-18T10:46:59.873925Z","iopub.status.idle":"2021-12-18T10:47:00.407369Z","shell.execute_reply.started":"2021-12-18T10:46:59.873888Z","shell.execute_reply":"2021-12-18T10:47:00.406624Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Dataloaderの作成","metadata":{}},{"cell_type":"code","source":"# データローダーの作成\nbatch_size = 32\n\ntrain_dataloader = data.DataLoader(\n    train_dataset, batch_size=batch_size, shuffle=True)\n\nval_dataloader = data.DataLoader(\n    val_dataset, batch_size=batch_size, shuffle=False)","metadata":{"execution":{"iopub.status.busy":"2021-12-18T10:47:00.60073Z","iopub.execute_input":"2021-12-18T10:47:00.600952Z","iopub.status.idle":"2021-12-18T10:47:00.606882Z","shell.execute_reply.started":"2021-12-18T10:47:00.600926Z","shell.execute_reply":"2021-12-18T10:47:00.604675Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 動作の確認\nbatch_iterator = iter(train_dataloader)  # イタレータに変換\nimges, anno_class_imges = next(batch_iterator)  # 1番目の要素を取り出す\nprint(imges.size())  # torch.Size([8, 3, 512, 512])\nprint(anno_class_imges.size())  # torch.Size([8, 512, 512])","metadata":{"execution":{"iopub.status.busy":"2021-12-18T10:47:00.914961Z","iopub.execute_input":"2021-12-18T10:47:00.915194Z","iopub.status.idle":"2021-12-18T10:47:04.442366Z","shell.execute_reply.started":"2021-12-18T10:47:00.915169Z","shell.execute_reply":"2021-12-18T10:47:04.44159Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# モデル定義","metadata":{"execution":{"iopub.status.busy":"2021-12-15T21:11:44.868506Z","iopub.execute_input":"2021-12-15T21:11:44.868832Z","iopub.status.idle":"2021-12-15T21:11:44.873295Z","shell.execute_reply.started":"2021-12-15T21:11:44.868801Z","shell.execute_reply":"2021-12-15T21:11:44.872651Z"}}},{"cell_type":"code","source":"model = smp.Unet(\n    encoder_name=encoder,   \n    encoder_weights=\"imagenet\", \n    in_channels=3,               \n    classes=class_num+1,\n)","metadata":{"execution":{"iopub.status.busy":"2021-12-18T10:47:04.443904Z","iopub.execute_input":"2021-12-18T10:47:04.444301Z","iopub.status.idle":"2021-12-18T10:47:08.748936Z","shell.execute_reply.started":"2021-12-18T10:47:04.444263Z","shell.execute_reply":"2021-12-18T10:47:08.748204Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 学習","metadata":{"execution":{"iopub.status.busy":"2021-12-15T21:20:14.348868Z","iopub.execute_input":"2021-12-15T21:20:14.349212Z","iopub.status.idle":"2021-12-15T21:20:14.354366Z","shell.execute_reply.started":"2021-12-15T21:20:14.349177Z","shell.execute_reply":"2021-12-15T21:20:14.353287Z"}}},{"cell_type":"code","source":"num_epochs = 5\n\noptimizer = optim.Adam(model.parameters(), lr=1e-3)\ncriterion = nn.CrossEntropyLoss()","metadata":{"execution":{"iopub.status.busy":"2021-12-18T10:47:08.750301Z","iopub.execute_input":"2021-12-18T10:47:08.750575Z","iopub.status.idle":"2021-12-18T10:47:08.756251Z","shell.execute_reply.started":"2021-12-18T10:47:08.75054Z","shell.execute_reply":"2021-12-18T10:47:08.755344Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# GPUが使えるか確認\ndevice = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")\nprint(\"使用デバイス：\", device)\n\nmodel = model.to(device)\nbest_val_loss = float('inf')\n\nfor epoch in range(num_epochs):\n\n    epoch_train_loss = 0.0  # epochの損失和\n    epoch_val_loss = 0.0  # epochの損失和\n\n    print('-------------')\n    print(f'epoch {epoch+1}/{num_epochs}')\n    print('-------------')\n    \n    # train\n    model.train()\n    for imges, anno_class_imges in tqdm(train_dataloader):\n        imges = imges.to(device)\n        anno_class_imges = anno_class_imges.to(device)\n        \n        outputs = model(imges)\n        loss = criterion(outputs, anno_class_imges.long())\n        \n        loss.backward()  # 勾配の計算\n        optimizer.step() # 勾配の更新\n        optimizer.zero_grad() # 勾配の初期化\n        \n        epoch_train_loss += loss.item()\n    \n    \n    # val\n    model.eval()\n    for imges, anno_class_imges in tqdm(val_dataloader):\n        imges = imges.to(device)\n        anno_class_imges = anno_class_imges.to(device)\n        \n        with torch.no_grad():\n            outputs = model(imges)\n            loss = criterion(outputs, anno_class_imges.long())\n        epoch_val_loss += loss.item()\n    \n    if epoch_val_loss < best_val_loss:\n        best_val_loss = epoch_val_loss\n        # ネットワークを保存する\n        torch.save(model.state_dict(), f'best.pth')\n    \n    print(f'epoch {epoch+1} || epoch_train_loss:{epoch_train_loss:.4f} || epoch_val_loss:{epoch_val_loss:.4f}')\n    \n","metadata":{"execution":{"iopub.status.busy":"2021-12-18T08:28:31.864834Z","iopub.execute_input":"2021-12-18T08:28:31.865095Z","iopub.status.idle":"2021-12-18T08:37:28.56234Z","shell.execute_reply.started":"2021-12-18T08:28:31.86506Z","shell.execute_reply":"2021-12-18T08:37:28.561406Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 推論","metadata":{}},{"cell_type":"code","source":"model.load_state_dict(torch.load('best.pth'))\n\ntest_img_list, test_anno_list = sorted(glob(osp.join(test_dir, 'frames/*'))), sorted(glob(osp.join(test_dir, 'labels/*')))\ntest_dataset = Dataset(test_img_list, anno_list=None, transform=val_transform)\ntest_dataloader = data.DataLoader(test_dataset, batch_size=batch_size, shuffle=False)","metadata":{"execution":{"iopub.status.busy":"2021-12-18T10:47:16.536177Z","iopub.execute_input":"2021-12-18T10:47:16.536453Z","iopub.status.idle":"2021-12-18T10:47:16.636821Z","shell.execute_reply.started":"2021-12-18T10:47:16.536423Z","shell.execute_reply":"2021-12-18T10:47:16.636205Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# test\npreds = []\n\ndevice = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")\nmodel = model.to(device)\nmodel.eval()\nfor imges, anno_class_imges in tqdm(test_dataloader):\n    imges = imges.to(device)\n    anno_class_imges = anno_class_imges.to(device)\n\n    with torch.no_grad():\n        outputs = model(imges)\n        outputs = outputs.argmax(dim=1).detach().cpu().numpy().astype('uint8')\n    \n    preds += [outputs]\n    \npreds = np.concatenate(preds)","metadata":{"execution":{"iopub.status.busy":"2021-12-18T08:37:28.894078Z","iopub.execute_input":"2021-12-18T08:37:28.894342Z","iopub.status.idle":"2021-12-18T08:39:00.232021Z","shell.execute_reply.started":"2021-12-18T08:37:28.894293Z","shell.execute_reply":"2021-12-18T08:39:00.231289Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 提出ファイルの作成","metadata":{}},{"cell_type":"code","source":"# ランレングス符号へエンコードする関数\ndef rle_encode(img):\n    \"\"\" TBD\n    \n    Args:\n        img (np.array): \n            - 1 indicating mask\n            - 0 indicating background\n    \n    Returns: \n        run length as string formated\n    \"\"\"\n    \n    pixels = img.flatten()\n    pixels = np.concatenate([[0], pixels, [0]])\n    runs = np.where(pixels[1:] != pixels[:-1])[0] + 1\n    runs[1::2] -= runs[::2]\n    return ' '.join(str(x) for x in runs)","metadata":{"execution":{"iopub.status.busy":"2021-12-18T08:39:00.233468Z","iopub.execute_input":"2021-12-18T08:39:00.23373Z","iopub.status.idle":"2021-12-18T08:39:00.23932Z","shell.execute_reply.started":"2021-12-18T08:39:00.233694Z","shell.execute_reply":"2021-12-18T08:39:00.238501Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"result = pd.read_csv(osp.join(rootpath, 'sample_submission.csv'))\nencoded_pixels = []\nfor pred in tqdm(preds):\n    pred = cv2.resize(pred, dsize=img_size, interpolation=cv2.INTER_NEAREST)\n    for label_id in range(1, class_num+1):\n        rle = rle_encode(np.where(pred==label_id, 1, 0))\n        encoded_pixels += [rle]\nresult['EncodedPixels'] = encoded_pixels\nresult.to_csv('result.csv', index=False)","metadata":{"execution":{"iopub.status.busy":"2021-12-18T08:39:00.240726Z","iopub.execute_input":"2021-12-18T08:39:00.241173Z","iopub.status.idle":"2021-12-18T08:42:15.265369Z","shell.execute_reply.started":"2021-12-18T08:39:00.241135Z","shell.execute_reply":"2021-12-18T08:42:15.264617Z"},"trusted":true},"execution_count":null,"outputs":[]}]}