{"cells":[{"metadata":{"trusted":true},"cell_type":"code","source":"#'../input/efficientnet-pytorch-07/efficientnet_pytorch-0.7.0'\npackage_path = '../input/pytorch-image-models/pytorch-image-models-master' \nimport sys\nsys.path.append(package_path)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"from glob import glob\nfrom sklearn.model_selection import GroupKFold, StratifiedKFold\nfrom skimage import io\nimport torch\nimport os\nfrom datetime import datetime\nimport time\nimport random\nimport torchvision\nfrom torchvision import transforms\nimport pandas as pd\nimport numpy as np\n\nimport matplotlib.pyplot as plt\nfrom torch.utils.data import Dataset,DataLoader\nfrom torch.utils.data.sampler import SequentialSampler, RandomSampler\nfrom  torch.cuda.amp import autocast, GradScaler\n\nimport sklearn\nimport warnings\nimport joblib\nfrom sklearn.metrics import roc_auc_score, log_loss\nfrom sklearn import metrics\nimport warnings\nimport pydicom\nimport timm #from efficientnet_pytorch import EfficientNet\nfrom scipy.ndimage.interpolation import zoom\nfrom sklearn.metrics import log_loss","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# コンフィグファイル\nCFG = {\n    # K-交差検証の分割数\n    'fold_num': 5,\n    # シード固定用\n    'seed': 719,\n    \n    # 事前学習モデル　https://dajiro.com/entry/2020/07/24/161040\n    'model_arch': 'tf_efficientnet_b4_ns',\n    'img_size': 512,\n    'epochs': 10,\n    'train_bs': 32,\n    'valid_bs': 32,\n    'lr': 1e-4,\n    'num_workers': 4,\n    'accum_iter': 1, # suppoprt to do batch accumulation for backprop with effectively larger batch size\n    'verbose_step': 1,\n    'device': 'cuda:0',\n    'tta': 3,\n    'used_epochs': [6,7,8,9],\n    'weights': [1,1,1,1]\n}","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"# 訓練データ読み込み\ntrain = pd.read_csv('../input/cassava-leaf-disease-classification/train.csv')\ntrain.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# 各ラベルの個数\ntrain.label.value_counts()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"> We could do stratified validation split in each fold to make each fold's train and validation set looks like the whole train set in target distributions."},{"metadata":{},"cell_type":"markdown","source":"> k-分割交差検証により、各foldのと訓練データ、検証データを分布の比率を維持して分割することで、対象の訓練、検証データセットが全体のデータセットのように見せることができる。"},{"metadata":{"trusted":true},"cell_type":"code","source":"# テストデータ\nsubmission = pd.read_csv('../input/cassava-leaf-disease-classification/sample_submission.csv')\nsubmission.head()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Helper Functions"},{"metadata":{"trusted":true},"cell_type":"code","source":"# シード固定関数\ndef seed_everything(seed):\n    # 標準ライブラリ\n    random.seed(seed)    \n    # ハッシュシード\n    os.environ['PYTHONHASHSEED'] = str(seed)\n    # numpy\n    np.random.seed(seed)\n    # pytorch\n    torch.manual_seed(seed)\n    torch.cuda.manual_seed(seed)\n    torch.backends.cudnn.deterministic = True\n    torch.backends.cudnn.benchmark = True\n    \n# イメージ取得用関数\nimport cv2\ndef get_img(path):\n    # 読み込み\n    im_bgr = cv2.imread(path)\n    # openCVはBGRなので、matplotlib用にRBG変換しておく\n    # ::-1で逆順スライス\n    im_rgb = im_bgr[:, :, ::-1]\n    return im_rgb\n\n# イメージの取得および描写\nimg = get_img('../input/cassava-leaf-disease-classification/train_images/1000015157.jpg')\nplt.imshow(img)\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Dataset"},{"metadata":{"trusted":true},"cell_type":"code","source":"# データセット定義\nclass CassavaDataset(Dataset):\n    def __init__(\n        self, df, data_root, transforms=None, output_label=True\n    ):\n        \n        super().__init__()\n        self.df = df.reset_index(drop=True).copy()\n        self.transforms = transforms\n        self.data_root = data_root\n        self.output_label = output_label\n    \n    def __len__(self):\n        return self.df.shape[0]\n    \n    def __getitem__(self, index: int):\n        \n        # get labels\n        if self.output_label:\n            target = self.df.iloc[index]['label']\n          \n        path = \"{}/{}\".format(self.data_root, self.df.iloc[index]['image_id'])\n        \n        img  = get_img(path)\n        \n        if self.transforms:\n            img = self.transforms(image=img)['image']\n            \n        # do label smoothing\n        if self.output_label == True:\n            return img, target\n        else:\n            return img","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Define Train\\Validation Image Augmentations\n# 訓練および検証画像の拡張定義"},{"metadata":{"trusted":true},"cell_type":"code","source":"# 画像データ拡張ライブラリ\n# https://github.com/albumentations-team/albumentations\n# https://albumentations.ai/docs/api_reference/pytorch/transforms/#albumentations.pytorch.transforms.ToTensorV2\nfrom albumentations import (\n    HorizontalFlip, VerticalFlip, IAAPerspective, ShiftScaleRotate, CLAHE, RandomRotate90,\n    Transpose, ShiftScaleRotate, Blur, OpticalDistortion, GridDistortion, HueSaturationValue,\n    IAAAdditiveGaussianNoise, GaussNoise, MotionBlur, MedianBlur, IAAPiecewiseAffine, RandomResizedCrop,\n    IAASharpen, IAAEmboss, RandomBrightnessContrast, Flip, OneOf, Compose, Normalize, Cutout, CoarseDropout, ShiftScaleRotate, CenterCrop, Resize\n)\n\n\nfrom albumentations.pytorch import ToTensorV2\n\n# 訓練データの変換\ndef get_train_transforms():\n\n    # 複数処理をまとめて実行\n    return Compose([\n            # ランダムにトリミングし、元のサイズにリサイズ(引数：高さ、幅)\n            RandomResizedCrop(CFG['img_size'], CFG['img_size']),\n            \n            # 転置、水平反転、垂直反転（引数：適用確率）\n            Transpose(p=0.5),\n            HorizontalFlip(p=0.5),\n            VerticalFlip(p=0.5),\n\n            # アフィン変換を適用（平行移動、拡大縮小、回転）\n            ShiftScaleRotate(p=0.5),\n            \n            # 色相、彩度、輝度の変更\n            HueSaturationValue(hue_shift_limit=0.2, sat_shift_limit=0.2, val_shift_limit=0.2, p=0.5),\n        \n            # 明るさとコントラストの変更\n            RandomBrightnessContrast(brightness_limit=(-0.1,0.1), contrast_limit=(-0.1, 0.1), p=0.5),\n        \n            # 正規化(ピクセル値を255 = 2 ** 8-1で除算し、チャネルごとの平均を減算し、チャネルごとのstdで除算)\n            # ぼやけさせる的なイメージ\n            Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225], max_pixel_value=255.0, p=1.0),\n            CoarseDropout(p=0.5),\n            \n            # ドロップアウト\n            Cutout(p=0.5),\n            \n            # torchテンソルへ変換\n            ToTensorV2(p=1.0),\n        ], p=1.)\n  \n\n# 検証データの変換\ndef get_valid_transforms():\n    return Compose([\n            # 中心部分のトリミング\n            CenterCrop(CFG['img_size'], CFG['img_size'], p=1.),\n            \n            # リサイズ\n            Resize(CFG['img_size'], CFG['img_size']),\n        \n            # 正規化\n            Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225], max_pixel_value=255.0, p=1.0),\n            ToTensorV2(p=1.0),\n        ], p=1.)\n\n# 推論処理\ndef get_inference_transforms():\n    return Compose([\n            RandomResizedCrop(CFG['img_size'], CFG['img_size']),\n            Transpose(p=0.5),\n            HorizontalFlip(p=0.5),\n            VerticalFlip(p=0.5),\n            HueSaturationValue(hue_shift_limit=0.2, sat_shift_limit=0.2, val_shift_limit=0.2, p=0.5),\n            RandomBrightnessContrast(brightness_limit=(-0.1,0.1), contrast_limit=(-0.1, 0.1), p=0.5),\n            Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225], max_pixel_value=255.0, p=1.0),\n            ToTensorV2(p=1.0),\n        ], p=1.)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Model"},{"metadata":{"trusted":true},"cell_type":"code","source":"from torch import nn\n# https://pytorch.org/docs/stable/nn.html\n\n# モデル構築\nclass CassvaImgClassifier(nn.Module):\n    \n    def __init__(self, model_arch, n_class, pretrained=False):\n        \n        super().__init__()\n        # 事前学習モデルの取得、定義 ※model_archにモデル名入力\n        self.model = timm.create_model(model_arch, pretrained=pretrained)\n\n        # 試行回数\n        n_features = self.model.classifier.in_features\n        \n        #　最終層の再定義(出力ノード数をn_featuresに設定)\n        self.model.classifier = nn.Linear(n_features, n_class)\n        \n    def forward(self, x):\n        x = self.model(x)\n        return x","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Main Loop"},{"metadata":{"trusted":true},"cell_type":"code","source":"# プログレスバーの表示\n# https://qiita.com/pontyo4/items/76145cb10e030ad8186a\nfrom tqdm import tqdm\n\n# 1エポック分の推論処理\ndef inference_one_epoch(model, data_loader, device):\n\n    # 推論\n    model.eval()\n\n    image_preds_all = []\n    \n    # プログレスバーの表示するための準備\n    pbar = tqdm(enumerate(data_loader), total=len(data_loader))\n\n    # data_loader分繰り返し実行\n    for step, (imgs) in pbar:\n        \n        imgs = imgs.to(device).float()\n        \n        # ここで推論を実際にしている\n        image_preds = model(imgs)   #output = model(input)\n        \n        image_preds_all += [torch.softmax(image_preds, 1).detach().cpu().numpy()]\n        \n    image_preds_all = np.concatenate(image_preds_all, axis=0)\n    return image_preds_all","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"!ls ../input/pytorch-efficientnet-baseline-train-amp-aug","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"if __name__ == '__main__':\n     # for training only, need nightly build pytorch\n\n    # シード固定\n    seed_everything(CFG['seed'])\n    \n    # 訓練データとテストデータで分割\n    folds = StratifiedKFold(n_splits=CFG['fold_num']).split(np.arange(train.shape[0]), train.label.values)\n    \n    # 訓練データによる学習（上記で分割したfolds）\n    for fold, (trn_idx, val_idx) in enumerate(folds):\n        # we'll train fold 0 first\n        # 最初にfold0を訓練する　※実際に動かすときは以下の2行をコメントアウト？\n        # https://www.kaggle.com/khyeh0719/pytorch-efficientnet-baseline-train-amp-aug\n        # 上の処理を前にもってきて、fold > 0らへんの部分を削除すればいい感じになりそう\n\n        if fold > 0:\n            break \n\n        print('Inference fold:{0} trn_idx:{1} val_idx:{2} started'.format(fold,trn_idx,val_idx))\n\n        \n        # 検証データセットのインデックス振り直し\n        valid_ = train.loc[val_idx,:].reset_index(drop=True)\n        \n        # \n        valid_ds = CassavaDataset(valid_, '../input/cassava-leaf-disease-classification/train_images/',\n                                  transforms=get_inference_transforms(), output_label=False)\n        \n        test = pd.DataFrame()\n        test['image_id'] = list(os.listdir('../input/cassava-leaf-disease-classification/test_images/'))\n        test_ds = CassavaDataset(test, '../input/cassava-leaf-disease-classification/test_images/',\n                                 transforms=get_inference_transforms(), output_label=False)\n        \n        val_loader = torch.utils.data.DataLoader(\n            valid_ds, \n            batch_size=CFG['valid_bs'],\n            num_workers=CFG['num_workers'],\n            shuffle=False,\n            pin_memory=False,\n        )\n        \n        tst_loader = torch.utils.data.DataLoader(\n            test_ds, \n            batch_size=CFG['valid_bs'],\n            num_workers=CFG['num_workers'],\n            shuffle=False,\n            pin_memory=False,\n        )\n\n        device = torch.device(CFG['device'])\n        \n        # モデルの実体化\n        model = CassvaImgClassifier(CFG['model_arch'], train.label.nunique()).to(device)\n        \n        # 検証用予測\n        val_preds = []\n        \n        # テスト用予測\n        tst_preds = []\n        \n        #for epoch in range(CFG['epochs']-3):\n        for i, epoch in enumerate(CFG['used_epochs']):    \n            model.load_state_dict(torch.load('../input/pytorch-efficientnet-baseline-train-amp-aug/{}_fold_{}_{}'.format(CFG['model_arch'], fold, epoch)))\n            \n            with torch.no_grad():\n                for _ in range(CFG['tta']):\n                    val_preds += [CFG['weights'][i]/sum(CFG['weights'])/CFG['tta']*inference_one_epoch(model, val_loader, device)]\n                    tst_preds += [CFG['weights'][i]/sum(CFG['weights'])/CFG['tta']*inference_one_epoch(model, tst_loader, device)]\n\n        # 検証用予測の平均\n        val_preds = np.mean(val_preds, axis=0) \n        # テスト用予測の平均\n        tst_preds = np.mean(tst_preds, axis=0) \n        \n        # 損失\n        print('fold {} validation loss = {:.5f}'.format(fold, log_loss(valid_.label.values, val_preds)))\n        # 正解率\n        print('fold {} validation accuracy = {:.5f}'.format(fold, (valid_.label.values==np.argmax(val_preds, axis=1)).mean()))\n        \n        del model\n        torch.cuda.empty_cache()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# 指定sれた配列の最大値となっている要素の先頭インデックス\ntest['label'] = np.argmax(tst_preds, axis=1)\ntest.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"test.to_csv('submission.csv', index=False)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Train part is here: https://www.kaggle.com/khyeh0719/pytorch-efficientnet-baseline-train-amp-aug"}],"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":4,"nbformat_minor":4}