{"cells":[{"metadata":{"trusted":true},"cell_type":"code","source":"import glob\nimport os.path\nimport random\nfrom tqdm import tqdm \nimport pandas as pd\nimport numpy as np\nimport json\nfrom PIL import Image\nimport matplotlib.pyplot as plt\n%matplotlib inline\n\nimport torch\nimport torch.nn as nn\nimport torch.optim as optim\nimport torch.utils.data as data\nimport torchvision\nfrom torchvision import models, transforms\n\ntorch.manual_seed(123)\nnp.random.seed(223)\nrandom.seed(234)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"torch.backends.cudnn.deterministic = True\ntorch.backends.cudnn.benchmark = False","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"import zipfile\n\nwith zipfile.ZipFile(\"../input/dogs-vs-cats/train.zip\",\"r\") as z:\n    df = z.extractall(\".\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# 前処理クラスを作成\nclass BaseTransform():\n    \n    def __init__(self, resize, mean, std):\n            self.base_transform = {\n            'train': transforms.Compose([\n                transforms.Resize(resize),\n                transforms.RandomResizedCrop(resize, scale=(0.5, 1.0)),\n                transforms.RandomHorizontalFlip(),\n                transforms.ToTensor(),\n                transforms.Normalize(mean, std)\n                ]),\n            'val': transforms.Compose([\n                    transforms.Resize(resize),\n                    transforms.CenterCrop(resize),\n                    transforms.ToTensor(),\n                    transforms.Normalize(mean, std)\n                ])\n            }\n        \n    def __call__(self, img, phase='train'):\n        return self.base_transform[phase](img)\n    \nimg_sample = '../working/train/dog.8412.jpg'\nimg = Image.open(img_sample)\n\nplt.imshow(img)\nplt.show()\n    \nresize = 224\nmean = (0.485, 0.456, 0.406) # 転移学習モデル学習データに使われた平均と標準偏差で前処理\nstd = (0.229, 0.224, 0.225)\n\ntransform = BaseTransform(resize, mean, std) # ([色,高さ,幅])\nimg_transformed = transform(img)\n\nimg_transformed = img_transformed.numpy().transpose((1,2,0)) # ([高さ,幅,色])\nimg_transformed = np.clip(img_transformed, 0, 1) # 最小値、最大値を0,1に\nplt.imshow(img_transformed)\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# データセットを作る---------------------------------\n\nIMG_FILE = '../working/train'\n\nfilenames = os.listdir(IMG_FILE)\nlabels = [x.split(\".\")[0] for x in filenames]\ndf = pd.DataFrame({\"filename\": filenames, \"label\": labels})\nprint(df.label.value_counts())","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# テストデータを作る\nfrom sklearn.model_selection import train_test_split\n\ntrain, test = train_test_split(df, test_size=0.1, random_state = 1, stratify = df.label)\ntrain.sample(frac=1, random_state=1)\ntrain = train.reset_index(drop=True)\ntest = test.reset_index(drop=True)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# バリーデーションを作成\ntrain, val = train_test_split(train, test_size=0.2, random_state = 2, stratify = train.label)\ntrain.sample(frac=1, random_state=1)\ntrain = train.reset_index(drop=True)\nval = val.reset_index(drop=True)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"rootpath = '../working/train/'\n\ndef make_path_list(df):\n    \n    dogs_path = []\n    cats_path = []\n    \n    for i in list(df[df.label==\"dog\"].filename):\n        dogs_path.append(os.path.join(rootpath + i))\n        \n    for i in list(df[df.label==\"cat\"].filename):\n        cats_path.append(os.path.join(rootpath + i))\n    \n    #dog_data = np.concatenate([np.array(dogs_path).reshape(-1, 1), np.full(len(dogs_path), 0).reshape(-1, 1)], axis=1)\n    #cat_data = np.concatenate([np.array(cats_path).reshape(-1, 1), np.full(len(cats_path), 1).reshape(-1, 1)], axis=1)\n    \n    return np.concatenate([dogs_path, cats_path], axis=0)\n\ntrain_path = make_path_list(train)\nval_path = make_path_list(val)\ntest_path = make_path_list(test)\n\nprint(train_path)\nprint(val_path)\n\n# 一次元同士の結合は np.stack() で可能","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_path[10000].split('/')[3][0:3]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class Dataset(data.Dataset):\n    \n    def __init__(self, file_list, transform=None, phase='train'):\n        self.file_list = file_list\n        self.transform = transform\n        self.phase = phase\n        \n    def __len__(self):\n        return len(self.file_list)\n    \n    def __getitem__(self, index):\n        \n        img_path = self.file_list[index]\n        img = Image.open(img_path)\n        \n        img_transformed = self.transform(\n        img, self.phase)\n        \n        if self.phase == 'train':\n            label = img_path.split('/')[3][0:3]\n        elif self.phase == 'val':\n            label = img_path.split('/')[3][0:3]\n        \n        # このような形式でラベル付けしなければ学習できない\n        \n        if label == 'dog':\n            label = 0\n        elif label == 'cat':\n            label = 1\n            \n        return img_transformed, label\n    \n# len, getitem は宣言しなければならない\n    \ntrain_dataset = Dataset(file_list=train_path, transform=BaseTransform(resize, mean, std), phase='train')\nval_dataset = Dataset(file_list=val_path, transform=BaseTransform(resize, mean, std), phase='val')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"index = 0\nprint(train_dataset.__getitem__(index)[0].size())","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"batch_size = 32\n\ntrain_dataloader = torch.utils.data.DataLoader(train_dataset, batch_size=batch_size, shuffle=True)\nval_dataloader = torch.utils.data.DataLoader(val_dataset, batch_size=batch_size, shuffle=False)\n\ndataloaders_dict = {'train': train_dataloader, 'val':val_dataloader}\n\n\nbatch_iterator = iter(dataloaders_dict['train'])\n#for _, j in batch_iterator:\n#    print(i)\n#    print(j)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# 転移学習----------------------------------------------------------------------\nnet = models.vgg16(pretrained=True)\n\n# add a layer to last layer\nnet.classifier[6] = nn.Linear(in_features=4096, out_features=2)\n\nnet.train()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"cost = nn.CrossEntropyLoss()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"params_to_update = []\n\nupdate_params_names = ['classifier.6.weight', 'classifier.6.bias']\n\n# 転移学習用のパラメータは変化しないように設定\nfor name, param in net.named_parameters():\n    if name in update_params_names:\n        param.requires_grad = True\n        params_to_update.append(param)\n        print(name)\n    else:\n        param.requires_grad = False","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"optimizer = optim.SGD(params=params_to_update, lr=0.001, momentum=0.9)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# テスト\ntest_dataset = Dataset(file_list=val_path, transform=BaseTransform(resize, mean, std), phase='val')\ntest_dataloader = torch.utils.data.DataLoader(test_dataset, batch_size=batch_size, shuffle=False)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def train_model(net, dataloaders_dict, cost, optimizer, num_epochs, test_dataloader):\n    \n    device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')\n    \n    net.to(device)\n    \n    torch.backends.cudnn.benchmark = True\n    \n    for epoch in range(num_epochs):\n        print('Epoch {}/{}'.format(epoch+1, num_epochs))\n        print('-------------')\n\n        # epochごとの学習と検証のループ\n        for phase in ['train', 'val']:\n            if phase == 'train':\n                net.train()  # モデルを訓練モードに\n            else:\n                net.eval()   # モデルを検証モードに\n\n            epoch_loss = 0.0\n            epoch_corrects = 0\n\n            if (epoch == 0) and (phase == 'train'):\n                continue\n\n            # データローダーからミニバッチを取り出すループ\n            for inputs, labels in tqdm(dataloaders_dict[phase]):\n                \n                inputs = inputs.to(device)\n                labels = labels.to(device)\n\n                optimizer.zero_grad()\n\n                # 順伝搬（forward）計算\n                with torch.set_grad_enabled(phase == 'train'):\n                    outputs = net(inputs)\n                    loss = cost(outputs, labels)\n                    _, preds = torch.max(outputs, 1)  \n                    \n  \n                    # 訓練時はバックプロパゲーション\n                    if phase == 'train':\n                        loss.backward()\n                        optimizer.step()\n\n                    epoch_loss += loss.item() * inputs.size(0)  \n                    epoch_corrects += torch.sum(preds == labels.data)\n\n            # epochごとのlossと正解率を表示\n            epoch_loss = epoch_loss / len(dataloaders_dict[phase].dataset)\n            epoch_acc = epoch_corrects.double(\n            ) / len(dataloaders_dict[phase].dataset)\n\n            print('{} Loss: {:.4f} Acc: {:.4f}'.format(\n                phase, epoch_loss, epoch_acc))\n            \n    epoch_loss = 0.0  # testepochの損失和\n    epoch_corrects = 0  # testepochの正解数\n\n    for inputs_t, labels_t in tqdm(test_dataloader):\n        \n        inputs_t = inputs_t.to(device)\n        labels_t = labels_t.to(device)\n        \n        outputs_t = net(inputs_t)\n        loss_t = cost(outputs_t, labels_t)  \n        _, preds_t = torch.max(outputs_t, 1)\n\n        epoch_loss += loss_t.item() * inputs.size(0)  \n\n        epoch_corrects += torch.sum(preds_t == labels_t.data)\n\n\n    epoch_loss = epoch_loss / len(test_dataloader.dataset)\n    epoch_acc = epoch_corrects.double() / len(test_dataloader.dataset)\n\n    print('Test Loss: {:.4f} Acc: {:.4f}'.format(epoch_loss, epoch_acc))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"num_epochs = 2\ntrain_model(net, dataloaders_dict, cost, optimizer, num_epochs=num_epochs, test_dataloader=test_dataloader)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# ファインチューニング\nnet_2 = models.vgg16(pretrained=True)\n\nnet_2.classifier[6] = nn.Linear(in_features=4096, out_features=2)\nnet_2.train()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"cost = nn.CrossEntropyLoss()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"params_to_update1= []\nparams_to_update2 = []\nparams_to_update3 = []\n\nupdate_params_names1 = ['features']\nupdate_params_names2 = ['classifier.0.weight', 'classifier.0.bias', 'classifier.3.weight', 'classifier.3.bias']\nupdate_params_names3 = ['classifier.6.weight', 'classifier.6.bias']\n\nfor name, param in net_2.named_parameters():\n    if update_params_names1[0] in name:\n        param.requires_grad = True\n        params_to_update1.append(param)\n        print('1: {}'.format(name))\n        \n    elif name in update_params_names2:\n        param.requires_grad = True\n        params_to_update2.append(param)\n        print('2: {}'.format(name))\n        \n    elif name in update_params_names3:\n        param.requires_grad = True\n        params_to_update3.append(param)\n        print('3: {}'.format(name))\n        \n    else:\n        params.requires_grad = False\n        print('Nothing: {}'.format(name))\n        \noptimizer_t = optim.SGD([\n    {'params': params_to_update1, 'lr': 0.001},\n    {'params': params_to_update2, 'lr': 0.001},\n    {'params': params_to_update3, 'lr': 0.001}\n    ],\n    momentum = 0.9)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def train_model_fine(net, dataloaders_dict, cost, optimizer, num_epochs, test_dataloader):\n    \n    device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')\n    \n    net.to(device)\n    \n    torch.backends.cudnn.benchmark = True\n    \n    for epoch in range(num_epochs):\n        print('Epoch {}/{}'.format(epoch+1, num_epochs))\n        print('----------------')\n        \n        for phase in ['train', 'val']:\n            \n            if phase in 'train':\n                net.train()\n            else:\n                net.eval()\n                \n            epoch_loss = 0.0\n            epoch_corrects = 0\n\n            if (epoch == 0) and (phase == 'train'):\n                continue\n\n            for inputs, labels in tqdm(dataloaders_dict[phase]):\n\n                inputs = inputs.to(device)\n                labels = labels.to(device)\n\n                optimizer.zero_grad()\n\n                with torch.set_grad_enabled(phase == 'train'):\n\n                    outputs = net(inputs)\n                    loss = cost(outputs, labels)\n                    _, preds = torch.max(outputs, 1)\n\n                    if phase == 'train':\n                        loss.backward()\n                        optimizer.step()\n\n                    epoch_loss += loss.item() * inputs.size(0)\n                    epoch_corrects += torch.sum(preds == labels.data)\n\n\n            epoch_loss = epoch_loss / len(dataloaders_dict[phase].dataset)\n            epoch_acc = epoch_corrects.double() / len(dataloaders_dict[phase].dataset)\n\n            print('{} Loss: {:.4f} Acc: {:.4f}'.format(phase, epoch_loss, epoch_acc))\n        \n        \n    epoch_loss = 0.0  # testepochの損失和\n    epoch_corrects = 0  # testepochの正解数\n\n    for inputs_t, labels_t in tqdm(test_dataloader):\n        \n        inputs_t = inputs_t.to(device)\n        labels_t = labels_t.to(device)\n        \n        outputs_t = net(inputs_t)\n        loss_t = cost(outputs_t, labels_t)  \n        _, preds_t = torch.max(outputs_t, 1)\n\n        epoch_loss += loss_t.item() * inputs.size(0)  \n\n        epoch_corrects += torch.sum(preds_t == labels_t.data)\n\n\n    epoch_loss = epoch_loss / len(test_dataloader.dataset)\n    epoch_acc = epoch_corrects.double() / len(test_dataloader.dataset)\n\n    print('Test: Loss: {:.4f} Acc: {:.4f}'.format(epoch_loss, epoch_acc))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"num_epochs = 2\ntrain_model_fine(net_2, dataloaders_dict, cost, optimizer_t, num_epochs=num_epochs, test_dataloader=test_dataloader)","execution_count":null,"outputs":[]}],"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}