{"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 in \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 \"../input/\" directory.\n# For example, running this (by clicking run or pressing Shift+Enter) will list the files in the input directory\n\nimport os\nprint(os.listdir(\"../input\"))\nprint(os.path.isdir(\"../input/train\"))\nprint(os.listdir(\"../input/train/train\")[:10])\n\n# Any results you write to the current directory are saved as output.","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from IPython.display import Image\ncat_img = Image(filename='../input/train/train/cat.0.jpg')\ncat_img","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"dog_img = Image(filename='../input/train/train/dog.0.jpg')\ndog_img","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from skimage import io\nimport matplotlib.pyplot as plt","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.imshow(io.imread('../input/train/train/dog.1.jpg'))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"dog_img1 = io.imread('../input/train/train/dog.1.jpg')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"dog_img1.shape","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from torch.utils.data import Dataset, DataLoader","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import torch\ndevice = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")\ndevice","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class ImageDataset(Dataset):\n    def __init__(self, root_dir, transform=None):\n        self.root_dir = root_dir\n        self.images = os.listdir(root_dir)\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.images)\n\n    def __getitem__(self, idx):\n        filename = self.images[idx]\n        image = io.imread(os.path.join(self.root_dir, filename))\n        label = 1 if \"dog\" in filename else 0\n        sample = {'image': image, 'label': label, 'filename': filename }\n        if self.transform:\n            sample = self.transform(sample)\n        return sample","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def label_to_str(label):\n    if label == 1:\n        return \"dog\"\n    return \"cat\"","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"ds = ImageDataset('../input/train/train')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"print(len(ds))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"fig = plt.figure()\nfor i in range(4):\n    sample = ds[i]\n    species = label_to_str(sample['label'])\n    print(i, sample['image'].shape, species, sample['filename'])\n    ax = plt.subplot(1, 4, i + 1)\n    plt.tight_layout()\n    ax.set_title(f\"Sample {i} - {species}\")\n    ax.axis(\"off\")\n    plt.imshow(sample['image'])\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# These classes have been stolen and lightly rejigged from\n# https://pytorch.org/tutorials/beginner/data_loading_tutorial.html\nfrom skimage import transform\nimport torch\nfrom torchvision import transforms\n\nclass Rescale(object):\n    \"\"\"Rescale the image in a sample to a given size.\n\n    Args:\n        output_size (tuple or int): Desired output size. If tuple, output is\n            matched to output_size. If int, smaller of image edges is matched\n            to output_size keeping aspect ratio the same.\n    \"\"\"\n\n    def __init__(self, output_size):\n        assert isinstance(output_size, (int, tuple))\n        self.output_size = output_size\n\n    def __call__(self, sample):\n        image = sample['image']\n\n        h, w = image.shape[:2]\n        if isinstance(self.output_size, int):\n            if h > w:\n                new_h, new_w = self.output_size * h / w, self.output_size\n            else:\n                new_h, new_w = self.output_size, self.output_size * w / h\n        else:\n            new_h, new_w = self.output_size\n\n        new_h, new_w = int(new_h), int(new_w)\n\n        img = transform.resize(image, (new_h, new_w))\n\n        return {'image': img, 'label': sample['label'], 'filename': sample['filename']}\n\n\nclass RandomCrop(object):\n    \"\"\"Crop randomly the image in a sample.\n\n    Args:\n        output_size (tuple or int): Desired output size. If int, square crop\n            is made.\n    \"\"\"\n\n    def __init__(self, output_size):\n        assert isinstance(output_size, (int, tuple))\n        if isinstance(output_size, int):\n            self.output_size = (output_size, output_size)\n        else:\n            assert len(output_size) == 2\n            self.output_size = output_size\n\n    def __call__(self, sample):\n        image = sample['image']\n\n        h, w = image.shape[:2]\n        new_h, new_w = self.output_size\n\n        top = np.random.randint(0, h - new_h)\n        left = np.random.randint(0, w - new_w)\n\n        image = image[top: top + new_h,\n                      left: left + new_w]\n\n        return {'image': image, 'label': sample['label'], 'filename': sample['filename'] }\n\nclass Normalize(object):\n    def __init__(self, *args, **kwargs):\n        self.inner = transforms.Normalize(*args, **kwargs)\n    \n    def __call__(self, sample):\n        image = sample['image']\n        image = self.inner(image)\n        return {'image': image, 'label': sample['label'], 'filename': sample['filename']}\n\nclass ToTensor(object):\n    \"\"\"Convert ndarrays in sample to Tensors.\"\"\"\n\n    def __call__(self, sample):\n        image = sample['image']\n\n        # swap color axis because\n        # numpy image: H x W x C\n        # torch image: C X H X W\n        image = image.transpose((2, 0, 1))\n        return {'image': torch.from_numpy(image).to(torch.float32),\n                'label': sample['label'],\n                'filename': sample['filename']}\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"scale = Rescale(256)\ncrop = RandomCrop(224)\n# as expected by pretrained models, from https://pytorch.org/docs/stable/torchvision/models.html\nnormalize = Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])\ncomposed = transforms.Compose([scale, crop, ToTensor(), normalize])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from torch.utils.data.dataset import random_split\ntransformed_dataset = ImageDataset('../input/train/train', transform=composed)\nnum_images = len(transformed_dataset)\nnum_test = num_images // 10\nnum_train = num_images - 2 * num_test\ntrain, test, validate = random_split(transformed_dataset, [num_train, num_test, num_test])\n\ndef make_dl(dataset):\n    return DataLoader(dataset, batch_size=4, shuffle=True, num_workers=4)\n\ndataloaders = {'train': make_dl(train), 'test': make_dl(test), 'validate': make_dl(validate)}","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from torch import nn\n\nmodel = nn.Sequential(\n          nn.Conv2d(3, 20, 5),\n          nn.MaxPool2d(2, 2),\n          nn.Conv2d(20, 64, 5),\n          nn.Linear(64, 2)\n        )","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Now, let's train the model!"},{"metadata":{"trusted":true},"cell_type":"code","source":"params_to_update = model.parameters()\nprint(\"Params to learn:\")\nfor name,param in model.named_parameters():\n    if param.requires_grad == True:\n        print(\"\\t\",name, param.size())","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model.train()\noptimizer = torch.optim.SGD(params_to_update, lr=0.001, momentum=0.9)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"criterion = torch.nn.CrossEntropyLoss()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"%%time\nnum_epochs = 5\nmodel = model.to(device)\nmodel.train()\n\nfor i in range(num_epochs):\n    for phase in ['train', 'test']:\n        phase_loss = 0\n        for j, batch in enumerate(dataloaders[phase]):\n            optimizer.zero_grad()\n            inputs = batch['image'].to(device)\n            outputs = model(inputs)\n            labels = batch['label'].to(device)\n            loss = criterion(outputs, labels)\n            phase_loss += loss.item()\n            if phase == 'train':\n                loss.backward()\n                optimizer.step()\n        print(f\"Epoch {i}, {phase} loss = {phase_loss / j}\")","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Where the hell is 106 coming from?"},{"metadata":{},"cell_type":"markdown","source":"We save out the model for later use - future versions of this kernel can add this version as an input source. See https://www.kaggle.com/c/tgs-salt-identification-challenge/discussion/63167#369520"},{"metadata":{"trusted":true},"cell_type":"code","source":"torch.save(model.state_dict(), 'model.pt')\n# to load, use\n#   model = torch.load(PATH)\n#   model.eval()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"markdown","source":"Now we load some images from the validation dataset and display them with their true and calculated labels!"},{"metadata":{"trusted":true},"cell_type":"code","source":"model.eval()\nnum_correct = 0\ntotal = len(validate)\nfor j, batch in enumerate(dataloaders['validate']):\n    inputs = batch['image'].to(device)\n    labels = batch['label'].to(device)\n    outputs = model(inputs)\n    best_guesses = outputs.argmax(1)\n    num_correct += (labels == best_guesses).sum()\nprint(f\"{num_correct} correct out of {total}: success rate {100 * num_correct / total}%\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"test_ds = ImageDataset('../input/test1/test1', transform=composed)\ntest_dl = DataLoader(test_ds, batch_size=1)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import pathlib\n\npredictions = []\nfor j, image in enumerate(test_dl):\n    inputs = image['image'].to(device)\n    outputs = model(inputs)\n    best_guesses = outputs.argmax(1)\n    file_id = int(pathlib.Path(image['filename'][0]).stem)\n    predictions.append({'id': file_id, 'label': best_guesses.item()})","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df = pd.DataFrame(predictions)\ndf.to_csv('submission.csv')\ndf.head()","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"name":"python","version":"3.6.4","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"}},"nbformat":4,"nbformat_minor":1}