{"cells":[{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input/'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\n# You can write up to 5GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Import Library","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"import torch\nfrom torch import nn\nfrom torch.utils import data\nfrom torchvision import models, utils, transforms\nfrom PIL import Image\nimport zipfile\nimport time\nimport copy","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Data ","execution_count":null},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"to_extract = ['train', 'test1']\nfor file in to_extract:\n    with zipfile.ZipFile('/kaggle/input/dogs-vs-cats/' + file + '.zip', 'r') as z:\n        z.extractall('./data')  # 解压train.zip的所有文件到./data目录\n    \nprint('train', len(os.listdir('./data/train')), os.listdir('./data/train')[0])\nprint('test', len(os.listdir('./data/test1')), os.listdir('./data/test1')[0])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"for i in range(5):\n    img = Image.open('./data/train/cat.' + str(i) + '.jpg')\n    print(img.size)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class DogCat(data.Dataset):\n    \"\"\"\n    数据集类，可利用index获取一条数据\n    \"\"\"\n    def __init__(self, root, train=True, test=False):\n        self.test = test\n        imgs = os.listdir(root)\n        \n        if self.test:\n            self.imgs = sorted(imgs, key=lambda x: int(x.split('.')[0]))\n        else:\n            # 对图片排序，方便按比例分割数据集\n            imgs = sorted(imgs, key=lambda x: int(x.split('.')[1]))\n            length = len(imgs)\n            if train:  # 训练集\n                self.imgs = imgs[:int(0.8*length)]\n            else:  # 验证集\n                self.imgs = imgs[int(0.8*length):]\n        self.imgs = [os.path.join(root, img) for img in self.imgs]\n        \n        if train:\n            self.transforms = transforms.Compose([\n                transforms.Resize(256),  # 按比例缩放，最小边size=256\n                transforms.RandomCrop(224),  # 随机裁剪固定尺寸\n                transforms.RandomHorizontalFlip(),  # 随机水平翻转\n                transforms.ToTensor(),\n                transforms.Normalize(mean=[0.5,0.5,0.5], std=[0.5,0.5,0.5])\n            ])\n        else:\n            self.transforms = transforms.Compose([\n                transforms.Resize(256),  # 按比例缩放，最小边size=256\n                transforms.CenterCrop(224),  # 中心裁剪固定尺寸\n                transforms.ToTensor(),\n                transforms.Normalize(mean=[0.5,0.5,0.5], std=[0.5,0.5,0.5])\n            ])\n    \n    def __getitem__(self, index):\n        \"\"\"\n        一次返回一张图片的数据，此时加载数据节约内存\n        \"\"\"\n        img = Image.open(self.imgs[index])\n        img = self.transforms(img)\n        \n#         print(self.imgs[index])\n        if self.test:\n            label = int(str(self.imgs[index]).split('/')[-1].split('.')[0])\n        else:\n            label = 1 if 'dog' in str(self.imgs[index]).split('/')[-1] else 0\n        return img, label\n    \n    def __len__(self):\n        return len(self.imgs)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"dataset = DogCat('./data/train')\nprint(dataset[0][1])\n# dataset = DogCat('./data/test1', train=False, test=True)\n# print(dataset[0][1])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import matplotlib.pyplot as plt\n\nfig, axes = plt.subplots(3, figsize=(6, 12))\nfor i in range(3):\n    img = dataset[i][0].detach().numpy().transpose((1, 2, 0))\n    img = img * 0.5 + 0.5 \n    axes[i].imshow(img)\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Model Structure","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"class MyNet(nn.Module):\n    def __init__(self):\n        super(MyNet, self).__init__()\n        self.feature = nn.Sequential(\n            nn.Conv2d(3, 16, 3, 1, 1),  # (3, 224, 224) -> (16, 224, 224)\n            nn.ReLU(inplace=True),\n            nn.MaxPool2d(2, 2),   # (16, 224, 224) -> (16, 112, 112)\n            nn.Conv2d(16, 32, 3, 1, 1),  # (16, 112, 112) -> (32, 112, 112)\n            nn.BatchNorm2d(32),\n            nn.ReLU(inplace=True),\n            nn.MaxPool2d(2, 2),   # (32, 112, 112) -> (32, 56, 56)\n            nn.Conv2d(32, 64, 3, 1, 1),  # (32, 56, 56) -> (64, 56, 56)\n            nn.BatchNorm2d(64),\n            nn.ReLU(inplace=True),\n            nn.MaxPool2d(2, 2),   # (64, 56, 56) -> (64, 28, 28)\n            nn.Dropout(0.5),\n            nn.Conv2d(64, 128, 3, 2),  # (128, 28, 28) -> (128, 13, 13)\n            nn.BatchNorm2d(128),\n            nn.ReLU(inplace=True),\n            nn.MaxPool2d(2, 2),   # (128, 13, 13) -> (128, 6, 6)\n            nn.Dropout(0.5)\n        )\n        \n        self.predict = nn.Sequential(\n            nn.Linear(128*6*6, 512),\n            nn.BatchNorm1d(512),\n            nn.ReLU(inplace=True),\n            nn.Dropout(0.5),\n            nn.Linear(512, 128), \n            nn.BatchNorm1d(128),\n            nn.Dropout(0.25),\n            nn.ReLU(inplace=True),\n            nn.Linear(128, 2)\n        )\n        \n    def forward(self, x):\n        x = self.feature(x)\n        x = x.view(x.size(0), -1)\n        x = self.predict(x)\n        return x\n    \nmodel = MyNet()\nprint(model)\n# model(dataset[0][0].clone().detach().resize(1, 3, 224, 224)).size()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"torch.cuda.is_available()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# config","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"class DefaultConfig():\n    model = 'MyNet'\n    model_path = 'MyNet.pkl'\n    result_file = 'submission.csv'\n    \n    train_root = './data/train'\n    test_root = './data/test1'\n    \n    num_workers = 4\n    max_epoch = 30\n    batch_size = 128\n    lr = 0.001\n    device = torch.device('cuda')\n    ","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# train","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"def train():\n    config = DefaultConfig()\n    \n    # step1: model\n    model = eval(config.model)()\n    model.to(config.device)\n    \n    # step2: data\n    datasets = {\n        'train': DogCat(config.train_root, train=True),\n        'val': DogCat(config.train_root, train=False)\n    }\n    dataloaders = {\n        key: data.DataLoader(\n            value, \n            batch_size = config.batch_size,\n            shuffle=True,\n            num_workers=config.num_workers)\n        for key, value in datasets.items()\n    }\n    \n    # step3: optimizer and criterion\n    criterion = nn.CrossEntropyLoss()\n    lr=config.lr\n    optimizer = torch.optim.Adam(model.parameters(), lr)\n    lr_scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=7, gamma=0.5)\n\n    # step4: train\n    since = time.time()\n    best_acc = 0.0\n    best_model_wts = copy.deepcopy(model.state_dict())\n    \n    previous_acc = 1e-3\n    losses = {'train': [], 'val': []}\n    acc = {'train': [], 'val': []}\n    for epoch in range(config.max_epoch):\n        print('Epoch: {}/{}'.format(epoch, config.max_epoch))\n        print('-' * 10)\n        for phase in ['train', 'val']:\n            if phase == 'train':\n                lr_scheduler.step()\n                model.train()                \n            else:\n                model.eval()\n                \n            runing_loss = 0.0\n            runing_corrects = 0\n            \n            with torch.set_grad_enabled(phase == 'train'):  # 训练时才计算梯度\n                for step, (img, label) in enumerate(dataloaders[phase]):\n                    img = img.to(config.device)\n                    label = label.to(config.device)\n                    output = model(img)\n                    loss = criterion(output, label)\n\n                    if phase == 'train':\n                        optimizer.zero_grad()\n                        loss.backward()\n                        optimizer.step()\n                        if step % 50 == 0:\n                            print('step: {} | loss: {:.4f}'.format(step, loss.item()))\n                   \n                    runing_loss += loss.item() * img.size(0)\n                    _, pred = torch.max(output.cpu(), 1)\n                    runing_corrects += torch.sum(torch.eq(pred, label.cpu())).item()\n            \n            epoch_loss = runing_loss / len(datasets[phase])\n            epoch_acc = runing_corrects / len(datasets[phase])\n            print('{} | loss: {:.4f} | Acc: {:.4f}'.format(phase, epoch_loss, epoch_acc))\n            losses[phase].append(epoch_loss)\n            acc[phase].append(epoch_acc\n                             )\n#                   \n#             if phase == 'val' and epoch_acc < previous_acc:             \n#                 # update lr\n#                 if lr > 0.00002:\n#                     lr *= 0.5\n#                     for param_group in optimizer.param_groups:\n#                         param_group['lr'] = lr\n            \n            if phase == 'val' and epoch_acc > best_acc:\n                best_acc = epoch_acc\n                best_model_wts = copy.deepcopy(model.state_dict())\n            previous_acc = epoch_acc if phase == 'val' else previous_acc\n   \n    time_elapsed = time.time() - since\n    print('Training complete in {:.0f}m {:.0f}s'.format(time_elapsed // 60, time_elapsed % 60))\n    print('Best val Acc: {:.4f}'.format(best_acc))\n    \n    model = model.load_state_dict(best_model_wts)\n    torch.save(best_model_wts, config.model_path)\n    return model, losses, acc\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"best_model, losses, acc = train()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Training Visualizaion","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(12, 12))\nax1.plot(range(len(losses['train'])), losses['train'], color='b', label='train loss')\nax1.plot(range(len(losses['val'])), losses['val'], color='r', label='val loss')\nax1.set_xticks(np.arange(1, 30))\nax1.set_yticks(np.arange(0, 1, 0.1))\nplt.legend()\n\nax2.plot(range(len(acc['train'])), acc['train'], color='b', label='train acc')\nax2.plot(range(len(acc['val'])), acc['val'], color='r', label='val acc')\nax2.set_xticks(np.arange(1, 30))\nax2.set_yticks(np.arange(0, 1.1, 0.1))\n\nplt.legend()\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# test","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"import numpy as np\nimport pandas as pd\n\ndef test():\n    config = DefaultConfig()\n    \n    model = eval(config.model)()\n    model.load_state_dict(torch.load(config.model_path))\n    model.to(config.device)\n    \n    test_data = DogCat(config.test_root, train=False, test=True)\n    test_loader = data.DataLoader(test_data, batch_size=config.batch_size, shuffle=False, num_workers=config.num_workers)\n    \n    result = []\n    for step, (img, path) in enumerate(test_loader):\n        img = img.to(config.device)\n        path = path.to(config.device)\n        output = model(img)\n\n        score, pred = torch.max(nn.functional.softmax(output).cpu(), 1)\n        step_result = np.concatenate(\n            [path.cpu().view(-1,1).numpy(), \n             pred.detach().cpu().view(-1,1).numpy()], axis=1)\n        result.append(step_result)\n    result = np.concatenate(result, axis=0)    \n\n    pd.DataFrame(result, columns=['id', 'label']).to_csv(config.result_file, index=False)\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"test()\ndf = pd.read_csv('submission.csv')\ndf","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"fig, axs = plt.subplots(3, 3, figsize=(12,12))\nfor i in range(9):\n    index = df['id'][i]\n    label = df['label'][i]\n    \n    img = Image.open('./data/test1/' + str(index) + '.jpg')\n    axs[i//3, i %3].imshow(np.array(img))\n    axs[i//3, i %3].set_title(label)\n    \nplt.show()","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":1}