{"cells":[{"metadata":{"_uuid":"92a0d133c46f7ab2380401e3280bf661cd744731"},"cell_type":"markdown","source":"## Triplet Model for Hampback Whole Prediction\n\n---\n<h4 style=\"text-align:Left;\">Outline of the Notebook</h4>\n\n---\n* [**1.Introduction**](#1.Introduction)\n* [**2.Data Description**](#2.Data-Description)\n* [**3.Evaluation**](#3.Evaluation)\n* [**4.Submission Format**](#4.Submission-Format)\n* [**5.Required Packages**](#5.Required-Packages)\n* [**6.Define Parameter**](#6.Define-Parameter)\n* [**7.Helping Function**](#7.Helping-Function)\n* [**8.Introduction to Triplet Loss**](#8.Introduction-to-Triplet-Loss)\n* [**9.Model Design**](#9.Model-Design)\n* [**10.Model Training**](#10.Model-Training)\n* [**11.Image Generator**](#11.Image-Generator)\n* [**12.Predict Result**](#12.Predict-Result)"},{"metadata":{"_uuid":"2587a626ef7db7f35971c18d450debf3234b4875"},"cell_type":"markdown","source":"## 1.Introduction\n\n#### About Competition\n<div class=\"competition-overview__content\"><div><div class=\"markdown-converter__text--rendered\"><p><img width=\"250\" style=\"float: right;\" alt=\"Planet Aerial Imagery\" src=\"https://storage.googleapis.com/kaggle-competitions/kaggle/3333/media/happy-whale.jpg\"></p>\n<p style=\"text-align:justify;\">After centuries of intense whaling, recovering whale populations still have a hard time adapting to warming oceans and struggle to compete every day with the industrial fishing industry for food.</p>\n<p style=\"text-align:justify;\">To aid whale conservation efforts, scientists use photo surveillance systems to monitor ocean activity. They use the shape of whales’ tails and unique markings found in footage to identify what species of whale they’re analyzing and meticulously log whale pod dynamics and movements. For the past 40 years, most of this work has been done manually by individual scientists, leaving a huge trove of data untapped and underutilized.</p>\n<p style=\"text-align:justify;\">In this competition, you’re challenged to build an algorithm to identify individual whales in images. You’ll analyze Happywhale’s database of over 25,000 images, gathered from research institutions and public contributors. By contributing, you’ll help to open rich fields of understanding for marine mammal population dynamics around the globe.</p>\n<p style=\"text-align:justify;\"><b>Note, this competition is similar in nature to <a href=\"https://www.kaggle.com/c/whale-categorization-playground\" rel=\"nofollow\">this competition </a> with an expanded and updated dataset. </p>\n </b></p></div></div></div>\n \n ## 2.Data Description\n \n <div class=\"markdown-content-box__converter\"><div class=\"markdown-converter__text--rendered competition-data__content\"><p>This training data contains thousands of images of humpback whale flukes. Individual whales have been identified by researchers and given an <code>Id</code>. The challenge is to predict the whale <code>Id</code> of images in the test set. What makes this such a challenge is that there are only a few examples for each of 3,000+ whale Ids.</p>\n\n<h3>File descriptions</h3>\n\n<ul>\n<li><strong>train.zip</strong> - a folder containing the training images</li>\n<li><strong>train.csv</strong> - maps the training <code>Image</code> to the appropriate whale <code>Id</code>. Whales that are not predicted to have a label identified in the training data should be labeled as <code>new_whale</code>.</li>\n<li><strong>test.zip</strong> - a folder containing the test images to predict the whale <code>Id</code></li>\n<li><strong>sample_submission.csv</strong> - a sample submission file in the correct format</li>\n</ul></div></div>\n\n## 3.Evalution Mertics:\n\n<div class=\"markdown-converter__text--rendered\"><p>Submissions are evaluated according to the Mean Average Precision @ 5 (MAP@5):</p>\n\n<p><span class=\"MathJax_Preview\" style=\"color: inherit; display: none;\"></span><div class=\"MathJax_Display\" style=\"text-align: center;\"><span tabindex=\"0\" class=\"MathJax\" id=\"MathJax-Element-1-Frame\" role=\"presentation\" style=\"text-align: center; position: relative;\" data-mathml='<math xmlns=\"http://www.w3.org/1998/Math/MathML\" display=\"block\"><mi>M</mi><mi>A</mi><mi>P</mi><mrow class=\"MJX-TeXAtom-ORD\"><mo>@</mo></mrow><mn>5</mn><mo>=</mo><mfrac><mn>1</mn><mi>U</mi></mfrac><munderover><mo>&amp;#x2211;</mo><mrow class=\"MJX-TeXAtom-ORD\"><mi>u</mi><mo>=</mo><mn>1</mn></mrow><mrow class=\"MJX-TeXAtom-ORD\"><mi>U</mi></mrow></munderover><munderover><mo>&amp;#x2211;</mo><mrow class=\"MJX-TeXAtom-ORD\"><mi>k</mi><mo>=</mo><mn>1</mn></mrow><mrow class=\"MJX-TeXAtom-ORD\"><mi>m</mi><mi>i</mi><mi>n</mi><mo stretchy=\"false\">(</mo><mi>n</mi><mo>,</mo><mn>5</mn><mo stretchy=\"false\">)</mo></mrow></munderover><mi>P</mi><mo stretchy=\"false\">(</mo><mi>k</mi><mo stretchy=\"false\">)</mo></math>'><nobr aria-hidden=\"true\"><span class=\"math\" id=\"MathJax-Span-1\" style=\"width: 15.86em; display: inline-block;\"><span style=\"width: 12.99em; height: 0px; font-size: 122%; display: inline-block; position: relative;\"><span style=\"left: 0em; top: -2.16em; position: absolute; clip: rect(0.12em, 1012.9em, 3.55em, -1000em);\"><span class=\"mrow\" id=\"MathJax-Span-2\"><span class=\"mi\" id=\"MathJax-Span-3\" style=\"font-family: MathJax_Math; font-style: italic;\">M<span style=\"width: 0.08em; height: 1px; overflow: hidden; display: inline-block;\"></span></span><span class=\"mi\" id=\"MathJax-Span-4\" style=\"font-family: MathJax_Math; font-style: italic;\">A</span><span class=\"mi\" id=\"MathJax-Span-5\" style=\"font-family: MathJax_Math; font-style: italic;\">P<span style=\"width: 0.1em; height: 1px; overflow: hidden; display: inline-block;\"></span></span><span class=\"texatom\" id=\"MathJax-Span-6\"><span class=\"mrow\" id=\"MathJax-Span-7\"><span class=\"mo\" id=\"MathJax-Span-8\" style=\"font-family: MathJax_Main;\">@</span></span></span><span class=\"mn\" id=\"MathJax-Span-9\" style=\"font-family: MathJax_Main;\">5</span><span class=\"mo\" id=\"MathJax-Span-10\" style=\"padding-left: 0.27em; font-family: MathJax_Main;\">=</span><span class=\"mfrac\" id=\"MathJax-Span-11\" style=\"padding-left: 0.27em;\"><span style=\"width: 0.88em; height: 0px; margin-right: 0.12em; margin-left: 0.12em; display: inline-block; position: relative;\"><span style=\"left: 50%; top: -4.65em; margin-left: -0.25em; position: absolute; clip: rect(3.14em, 1000.42em, 4.15em, -1000em);\"><span class=\"mn\" id=\"MathJax-Span-12\" style=\"font-family: MathJax_Main;\">1</span><span style=\"width: 0px; height: 3.98em; display: inline-block;\"></span></span><span style=\"left: 50%; top: -3.29em; margin-left: -0.38em; position: absolute; clip: rect(3.12em, 1000.76em, 4.17em, -1000em);\"><span class=\"mi\" id=\"MathJax-Span-13\" style=\"font-family: MathJax_Math; font-style: italic;\">U<span style=\"width: 0.08em; height: 1px; overflow: hidden; display: inline-block;\"></span></span><span style=\"width: 0px; height: 3.98em; display: inline-block;\"></span></span><span style=\"left: 0em; top: -1.27em; position: absolute; clip: rect(0.8em, 1000.88em, 1.23em, -1000em);\"><span style=\"width: 0.88em; height: 0px; overflow: hidden; vertical-align: 0em; border-top-color: currentColor; border-top-width: 1.3px; border-top-style: solid; display: inline-block;\"></span><span style=\"width: 0px; height: 1.05em; display: inline-block;\"></span></span></span></span><span class=\"munderover\" id=\"MathJax-Span-14\" style=\"padding-left: 0.16em;\"><span style=\"width: 1.44em; height: 0px; display: inline-block; position: relative;\"><span style=\"left: 0em; top: -3.98em; position: absolute; clip: rect(2.85em, 1001.38em, 4.6em, -1000em);\"><span class=\"mo\" id=\"MathJax-Span-15\" style=\"font-family: MathJax_Size2; vertical-align: 0em;\">∑</span><span style=\"width: 0px; height: 3.98em; display: inline-block;\"></span></span><span style=\"left: 0.06em; top: -2.89em; position: absolute; clip: rect(3.33em, 1001.25em, 4.26em, -1000em);\"><span class=\"texatom\" id=\"MathJax-Span-16\"><span class=\"mrow\" id=\"MathJax-Span-17\"><span class=\"mi\" id=\"MathJax-Span-18\" style=\"font-family: MathJax_Math; font-size: 70.7%; font-style: italic;\">u</span><span class=\"mo\" id=\"MathJax-Span-19\" style=\"font-family: MathJax_Main; font-size: 70.7%;\">=</span><span class=\"mn\" id=\"MathJax-Span-20\" style=\"font-family: MathJax_Main; font-size: 70.7%;\">1</span></span></span><span style=\"width: 0px; height: 3.98em; display: inline-block;\"></span></span><span style=\"left: 0.45em; top: -5.13em; position: absolute; clip: rect(3.22em, 1000.54em, 4.17em, -1000em);\"><span class=\"texatom\" id=\"MathJax-Span-21\"><span class=\"mrow\" id=\"MathJax-Span-22\"><span class=\"mi\" id=\"MathJax-Span-23\" style=\"font-family: MathJax_Math; font-size: 70.7%; font-style: italic;\">U<span style=\"width: 0.05em; height: 1px; overflow: hidden; display: inline-block;\"></span></span></span></span><span style=\"width: 0px; height: 3.98em; display: inline-block;\"></span></span></span></span><span class=\"munderover\" id=\"MathJax-Span-24\" style=\"padding-left: 0.16em;\"><span style=\"width: 2.81em; height: 0px; display: inline-block; position: relative;\"><span style=\"left: 0.68em; top: -3.98em; position: absolute; clip: rect(2.85em, 1001.38em, 4.6em, -1000em);\"><span class=\"mo\" id=\"MathJax-Span-25\" style=\"font-family: MathJax_Size2; vertical-align: 0em;\">∑</span><span style=\"width: 0px; height: 3.98em; display: inline-block;\"></span></span><span style=\"left: 0.77em; top: -2.87em; position: absolute; clip: rect(3.31em, 1001.22em, 4.26em, -1000em);\"><span class=\"texatom\" id=\"MathJax-Span-26\"><span class=\"mrow\" id=\"MathJax-Span-27\"><span class=\"mi\" id=\"MathJax-Span-28\" style=\"font-family: MathJax_Math; font-size: 70.7%; font-style: italic;\">k</span><span class=\"mo\" id=\"MathJax-Span-29\" style=\"font-family: MathJax_Main; font-size: 70.7%;\">=</span><span class=\"mn\" id=\"MathJax-Span-30\" style=\"font-family: MathJax_Main; font-size: 70.7%;\">1</span></span></span><span style=\"width: 0px; height: 3.98em; display: inline-block;\"></span></span><span style=\"left: 0em; top: -5.21em; position: absolute; clip: rect(3.17em, 1002.74em, 4.33em, -1000em);\"><span class=\"texatom\" id=\"MathJax-Span-31\"><span class=\"mrow\" id=\"MathJax-Span-32\"><span class=\"mi\" id=\"MathJax-Span-33\" style=\"font-family: MathJax_Math; font-size: 70.7%; font-style: italic;\">m</span><span class=\"mi\" id=\"MathJax-Span-34\" style=\"font-family: MathJax_Math; font-size: 70.7%; font-style: italic;\">i</span><span class=\"mi\" id=\"MathJax-Span-35\" style=\"font-family: MathJax_Math; font-size: 70.7%; font-style: italic;\">n</span><span class=\"mo\" id=\"MathJax-Span-36\" style=\"font-family: MathJax_Main; font-size: 70.7%;\">(</span><span class=\"mi\" id=\"MathJax-Span-37\" style=\"font-family: MathJax_Math; font-size: 70.7%; font-style: italic;\">n</span><span class=\"mo\" id=\"MathJax-Span-38\" style=\"font-family: MathJax_Main; font-size: 70.7%;\">,</span><span class=\"mn\" id=\"MathJax-Span-39\" style=\"font-family: MathJax_Main; font-size: 70.7%;\">5</span><span class=\"mo\" id=\"MathJax-Span-40\" style=\"font-family: MathJax_Main; font-size: 70.7%;\">)</span></span></span><span style=\"width: 0px; height: 3.98em; display: inline-block;\"></span></span></span></span><span class=\"mi\" id=\"MathJax-Span-41\" style=\"padding-left: 0.16em; font-family: MathJax_Math; font-style: italic;\">P<span style=\"width: 0.1em; height: 1px; overflow: hidden; display: inline-block;\"></span></span><span class=\"mo\" id=\"MathJax-Span-42\" style=\"font-family: MathJax_Main;\">(</span><span class=\"mi\" id=\"MathJax-Span-43\" style=\"font-family: MathJax_Math; font-style: italic;\">k</span><span class=\"mo\" id=\"MathJax-Span-44\" style=\"font-family: MathJax_Main;\">)</span></span><span style=\"width: 0px; height: 2.16em; display: inline-block;\"></span></span></span><span style=\"width: 0px; height: 3.9em; overflow: hidden; vertical-align: -1.55em; border-left-color: currentColor; border-left-width: 0px; border-left-style: solid; display: inline-block;\"></span></span></nobr><span class=\"MJX_Assistive_MathML MJX_Assistive_MathML_Block\" role=\"presentation\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\" display=\"block\"><mi>M</mi><mi>A</mi><mi>P</mi><mrow class=\"MJX-TeXAtom-ORD\"><mo>@</mo></mrow><mn>5</mn><mo>=</mo><mfrac><mn>1</mn><mi>U</mi></mfrac><munderover><mo>∑</mo><mrow class=\"MJX-TeXAtom-ORD\"><mi>u</mi><mo>=</mo><mn>1</mn></mrow><mrow class=\"MJX-TeXAtom-ORD\"><mi>U</mi></mrow></munderover><munderover><mo>∑</mo><mrow class=\"MJX-TeXAtom-ORD\"><mi>k</mi><mo>=</mo><mn>1</mn></mrow><mrow class=\"MJX-TeXAtom-ORD\"><mi>m</mi><mi>i</mi><mi>n</mi><mo stretchy=\"false\">(</mo><mi>n</mi><mo>,</mo><mn>5</mn><mo stretchy=\"false\">)</mo></mrow></munderover><mi>P</mi><mo stretchy=\"false\">(</mo><mi>k</mi><mo stretchy=\"false\">)</mo></math></span></span></div><script id=\"MathJax-Element-1\" type=\"math/tex; mode=display\">MAP@5 = \\frac{1}{U} \\sum_{u=1}^{U}  \\sum_{k=1}^{min(n,5)} P(k)</script></p>\n\n<p>where <code>U</code> is the number of images, <code>P(k)</code> is the precision at cutoff <code>k</code>, and <code>n</code> is the number predictions per image.</p></div>\n\n## 4.Submission Format\n\n<p>For each <code>Image</code>&nbsp;in the test set, you may predict up to 5 labels for the whale <code>Id</code>. Whales that are not predicted to be one of the labels in the training data should be labeled as <code>new_whale</code>. The file should contain a header and have the following format:</p>\n\n<pre><code>Image,Id \n00028a005.jpg,new_whale w_23a388d w_9b5109b w_9c506f6 w_0369a5c \n000dcf7d8.jpg,new_whale w_23a388d w_9b5109b w_9c506f6 w_0369a5c \n...\n</code></pre>\n "},{"metadata":{"_uuid":"00f033e0e5995358433fe05bc3e064bf10e23eb0"},"cell_type":"markdown","source":"## 5.Required Packages"},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"from collections import defaultdict\nimport pandas as pd\nimport numpy as np\nfrom sklearn.cross_validation import train_test_split\nfrom sklearn.preprocessing import normalize\nfrom scipy.stats import logistic\nfrom os.path import join\nfrom tqdm import tqdm\nfrom PIL import Image\nfrom keras import backend as K\nfrom keras.models import Model\nfrom keras.optimizers import Adam\nfrom keras.layers import Input, Dense, Dropout, Lambda, Convolution2D, MaxPooling2D, Flatten\nfrom keras.losses import categorical_crossentropy\nfrom keras.callbacks import ModelCheckpoint, EarlyStopping\nfrom keras.applications.resnet50 import ResNet50, preprocess_input\n# from keras.applications.inception_resnet_v2 import InceptionResNetV2, preprocess_input\n# from keras.applications.inception_v3 import InceptionV3, preprocess_input\nimport os\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nplt.style.use('fivethirtyeight')\n%matplotlib inline\n\nimport warnings\nfor i in [DeprecationWarning,FutureWarning,UserWarning]:\n    warnings.filterwarnings(\"ignore\", category = i)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"717414bdbd88e485356f5c411ca037969de5e59f"},"cell_type":"markdown","source":"## 6.Define Parameter"},{"metadata":{"_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","trusted":true},"cell_type":"code","source":"batch_size = 24\nembedding_dim = 50\nimage_size = 224\npath_base = '../input/'\npath_train = join(path_base,'train')\npath_test = join(path_base,'test')\npath_model = join(path_base,'MyModel.hdf5')\npath_csv = '../input/train.csv'","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"c956c38aa755e547c17569ffe9ff4fa8d0e2e035"},"cell_type":"markdown","source":"## 7.Helping Function"},{"metadata":{"trusted":true,"_uuid":"721a989504b5d31f9c341e1c4db86a2098c72423"},"cell_type":"code","source":"class sample_gen(object):\n    def __init__(self, file_class_mapping, other_class = \"new_whale\"):\n        self.file_class_mapping= file_class_mapping\n        self.class_to_list_files = defaultdict(list)\n        self.list_other_class = []\n        self.list_all_files = list(file_class_mapping.keys())\n        self.range_all_files = list(range(len(self.list_all_files)))\n\n        for file, class_ in file_class_mapping.items():\n            if class_ == other_class:\n                self.list_other_class.append(file)\n            else:\n                self.class_to_list_files[class_].append(file)\n\n        self.list_classes = list(set(self.file_class_mapping.values()))\n        self.range_list_classes= range(len(self.list_classes))\n        self.class_weight = np.array([len(self.class_to_list_files[class_]) for class_ in self.list_classes])\n        self.class_weight = self.class_weight/np.sum(self.class_weight)\n\n    def get_sample(self):\n        class_idx = np.random.choice(self.range_list_classes, 1, p=self.class_weight)[0]\n        examples_class_idx = np.random.choice(range(len(self.class_to_list_files[self.list_classes[class_idx]])), 2)\n        positive_example_1, positive_example_2 = \\\n            self.class_to_list_files[self.list_classes[class_idx]][examples_class_idx[0]],\\\n            self.class_to_list_files[self.list_classes[class_idx]][examples_class_idx[1]]\n\n\n        negative_example = None\n        while negative_example is None or self.file_class_mapping[negative_example] == \\\n                self.file_class_mapping[positive_example_1]:\n            negative_example_idx = np.random.choice(self.range_all_files, 1)[0]\n            negative_example = self.list_all_files[negative_example_idx]\n        return positive_example_1, negative_example, positive_example_2\n    \ndef read_and_resize(filepath):\n    im = Image.open((filepath)).convert('RGB')\n    im = im.resize((image_size, image_size))\n    return np.array(im, dtype=\"float32\")\n\n\ndef augment(im_array):\n    if np.random.uniform(0, 1) > 0.9:\n        im_array = np.fliplr(im_array)\n    return im_array\n\ndef gen(triplet_gen):\n    while True:\n        list_positive_examples_1 = []\n        list_negative_examples = []\n        list_positive_examples_2 = []\n\n        for i in range(batch_size):\n            positive_example_1, negative_example, positive_example_2 = triplet_gen.get_sample()\n            path_pos1 = join(path_train, positive_example_1)\n            path_neg = join(path_train, negative_example)\n            path_pos2 = join(path_train, positive_example_2)\n            \n            positive_example_1_img = read_and_resize(path_pos1)\n            negative_example_img = read_and_resize(path_neg)\n            positive_example_2_img = read_and_resize(path_pos2)\n\n            positive_example_1_img = augment(positive_example_1_img)\n            negative_example_img = augment(negative_example_img)\n            positive_example_2_img = augment(positive_example_2_img)\n            \n            list_positive_examples_1.append(positive_example_1_img)\n            list_negative_examples.append(negative_example_img)\n            list_positive_examples_2.append(positive_example_2_img)\n\n        A = preprocess_input(np.array(list_positive_examples_1))\n        B = preprocess_input(np.array(list_positive_examples_2))\n        C = preprocess_input(np.array(list_negative_examples))\n        \n        label = None\n        \n        yield ({'anchor_input': A, 'positive_input': B, 'negative_input': C}, label)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"8b219f7aa6680474d14061a20b929ea73f458c1d"},"cell_type":"markdown","source":"## 8.Introduction to Triplet Loss "},{"metadata":{"_kg_hide-input":true,"trusted":true,"_uuid":"7b35b921f62269b34f2923d65c6d65d2fb69cb6f"},"cell_type":"code","source":"from IPython.display import YouTubeVideo\nYouTubeVideo('LN3RdUFPYyI', 800,400)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"88ceb4ec20a6f8a1dae75658e16318e06e49ad97"},"cell_type":"markdown","source":"## Concept of Triplet loss\nReferences : https://omoindrot.github.io/triplet-loss\n\nIt’s a loss function that is used when training a NN for face recognition/verification. Each training sample is actually composed of a “triplet” of images:\n* **An anchor**\n* **A positive of the same class as the anchor**\n* **A negative of a different class**\n\n![](https://omoindrot.github.io/assets/triplet_loss/triplet_loss.png)\n\nSource: [Triplet Loss and Online Triplet Mining in TensorFlow](https://omoindrot.github.io/assets/triplet_loss/triplet_loss.png)\n1. The CNN first encodes the triplets as embeddings in some vector space.\n1. You then calculate the two distances in the embedding space:\n    1. The distance between the anchor and the positive - call it d(a,p)\n    1. The distance between the anchor and the negative - call it d(a,n)\n1. You define some margin of your choice\n\nThe triplet loss is then defined as: L=max(d(a,p)−d(a,n)+margin,0)\nMinimizing it both pushes d(a,p) to 0, and d(a,n) to be bigger than d(a,p)+margin.\n\n### Triplet mining\n\nBased on the definition of the loss, there are three categories of triplets:\n\n* **easy triplets:**  triplets which have a loss of $0$, because $d(a, p) + margin < d(a,n)$\n* **hard triplets:** triplets where the negative is closer to the anchor than the positive, i.e. $d(a,n) < d(a,p)$\n* **semi-hard triplets:** triplets where the negative is not closer to the anchor than the positive, but which still have positive loss: $d(a, p) < d(a, n) < d(a, p) + margin$\n\nEach of these definitions depend on where the negative is, relatively to the anchor and positive. We can therefore extend these three categories to the negatives: hard negatives, semi-hard negatives or easy negatives.\n\nThe figure below shows the three corresponding regions of the embedding space for the negative.\n\n![](https://omoindrot.github.io/assets/triplet_loss/triplets.png)\n"},{"metadata":{"trusted":true,"_uuid":"679829a47db08ed4dc97b61a5d6781f70419fa2a"},"cell_type":"code","source":"def triplet_loss(inputs, dist='sqeuclidean', margin='maxplus'):\n    anchor, positive, negative = inputs\n    positive_distance = K.square(anchor - positive)\n    negative_distance = K.square(anchor - negative)\n    if dist == 'euclidean':\n        positive_distance = K.sqrt(K.sum(positive_distance, axis=-1, keepdims=True))\n        negative_distance = K.sqrt(K.sum(negative_distance, axis=-1, keepdims=True))\n    elif dist == 'sqeuclidean':\n        positive_distance = K.sum(positive_distance, axis=-1, keepdims=True)\n        negative_distance = K.sum(negative_distance, axis=-1, keepdims=True)\n    loss = positive_distance - negative_distance\n    if margin == 'maxplus':\n        loss = K.maximum(0.0, 1 + loss)\n    elif margin == 'softplus':\n        loss = K.log(1 + K.exp(loss))\n    return K.mean(loss)\n\ndef triplet_loss_np(inputs, dist='sqeuclidean', margin='maxplus'):\n    anchor, positive, negative = inputs\n    positive_distance = np.square(anchor - positive)\n    negative_distance = np.square(anchor - negative)\n    if dist == 'euclidean':\n        positive_distance = np.sqrt(np.sum(positive_distance, axis=-1, keepdims=True))\n        negative_distance = np.sqrt(np.sum(negative_distance, axis=-1, keepdims=True))\n    elif dist == 'sqeuclidean':\n        positive_distance = np.sum(positive_distance, axis=-1, keepdims=True)\n        negative_distance = np.sum(negative_distance, axis=-1, keepdims=True)\n    loss = positive_distance - negative_distance\n    if margin == 'maxplus':\n        loss = np.maximum(0.0, 1 + loss)\n    elif margin == 'softplus':\n        loss = np.log(1 + np.exp(loss))\n    return np.mean(loss)\n\ndef check_loss():\n    batch_size = 10\n    shape = (batch_size, 4096)\n\n    p1 = normalize(np.random.random(shape))\n    n = normalize(np.random.random(shape))\n    p2 = normalize(np.random.random(shape))\n    \n    input_tensor = [K.variable(p1), K.variable(n), K.variable(p2)]\n    out1 = K.eval(triplet_loss(input_tensor))\n    input_np = [p1, n, p2]\n    out2 = triplet_loss_np(input_np)\n\n    assert out1.shape == out2.shape\n    print(np.linalg.norm(out1))\n    print(np.linalg.norm(out2))\n    print(np.linalg.norm(out1-out2))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"90064c8daa36a84ca2f6b95ec540ebddb3dfdb0d"},"cell_type":"code","source":"check_loss()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"dc8e689c18afdb010034fb8eda359ccba71b89f5"},"cell_type":"markdown","source":"## 9.Model Design"},{"metadata":{"trusted":true,"_uuid":"5d2dd749451e69ac7c7cddf46e879a3715ba2e61"},"cell_type":"code","source":"def GetModel():\n    base_model = ResNet50(weights='imagenet', include_top=False, pooling='max')\n    for layer in base_model.layers:\n        layer.trainable = False\n    \n    x = base_model.output\n    x = Dropout(0.6)(x)\n    x = Dense(embedding_dim)(x)\n    x = Lambda(lambda  x: K.l2_normalize(x,axis=1))(x)\n    embedding_model = Model(base_model.input, x, name=\"embedding\")\n\n    input_shape = (image_size, image_size, 3)\n    anchor_input = Input(input_shape, name='anchor_input')\n    positive_input = Input(input_shape, name='positive_input')\n    negative_input = Input(input_shape, name='negative_input')\n    anchor_embedding = embedding_model(anchor_input)\n    positive_embedding = embedding_model(positive_input)\n    negative_embedding = embedding_model(negative_input)\n\n    inputs = [anchor_input, positive_input, negative_input]\n    outputs = [anchor_embedding, positive_embedding, negative_embedding]\n       \n    triplet_model = Model(inputs, outputs)\n    triplet_model.add_loss(K.mean(triplet_loss(outputs)))\n\n    return embedding_model, triplet_model","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"e467fae795f6fe546e845b1df200020301d1ed33"},"cell_type":"code","source":"data = pd.read_csv(path_csv)\ntrain, test = train_test_split(data, train_size=0.7, random_state=1337)\nfile_id_mapping_train = {k: v for k, v in zip(train.Image.values, train.Id.values)}\nfile_id_mapping_test = {k: v for k, v in zip(test.Image.values, test.Id.values)}\ngen_tr = gen(sample_gen(file_id_mapping_train))\ngen_te = gen(sample_gen(file_id_mapping_test))\n\ncheckpoint = ModelCheckpoint(path_model, monitor='loss', verbose=1, save_best_only=True, mode='min')\nearly = EarlyStopping(monitor=\"val_loss\", mode=\"min\", patience=2)\ncallbacks_list = [checkpoint, early]  # early","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"0cff6195879e97b150606af7721bb06c3ff40488"},"cell_type":"code","source":"def ShowImg(img):\n    plt.figure(figsize=(15,8))\n    plt.imshow(img.astype('uint8'))\n    plt.show()\n    plt.close()\n    \nbatch = next(gen_tr)\n\nimg = batch[0]['anchor_input'][0]\nprint(img.shape)\nmean = [103.939, 116.779, 123.68]\nimg[..., 0] += mean[0]\nimg[..., 1] += mean[1]\nimg[..., 2] += mean[2]\nimg = img[..., ::-1]\nShowImg(img)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"368b3aae41868bf9e1b5c44cf2906be90357e248"},"cell_type":"markdown","source":"# Installation of Resnet 50 Weight to keras"},{"metadata":{"trusted":true,"_uuid":"56d2f72f300261fc236286c55f7b43c6ec642fcd"},"cell_type":"code","source":"embedding_model, triplet_model = GetModel()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"20e8f18cecce0d4ac4031b70f4f5684eec716717"},"cell_type":"code","source":"for i, layer in enumerate(embedding_model.layers):\n    print(i, layer.name, layer.trainable)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"a94595ed0f78c4f252be1284b6edff4b73da2cd1"},"cell_type":"code","source":"for layer in embedding_model.layers[178:]:\n    layer.trainable = True\nfor layer in embedding_model.layers[:178]:\n    layer.trainable = False","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"757e049ccb357f072457432ee29a2c66b23a7a58"},"cell_type":"code","source":"triplet_model.compile(loss=None, optimizer=Adam(0.01))\nhistory = triplet_model.fit_generator(gen_tr, \n                              validation_data=gen_te, \n                              epochs=4, \n                              verbose=1, \n                              workers=4,\n                              steps_per_epoch=200, \n                              validation_steps=20,use_multiprocessing=True)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"edd76af9c13afc4672e243c569e5003ea5e7221d"},"cell_type":"code","source":"# plt.plot(history.history['loss'], label='loss')\n# plt.legend()\n# plt.show()\ndef eva_plot(History, epoch):\n    plt.figure(figsize=(20,10))\n#     sns.lineplot(range(1, epoch+1), History.history['acc'], label='Train Accuracy')\n#     sns.lineplot(range(1, epoch+1), History.history['val_acc'], label='Test Accuracy')\n#     plt.legend(['train', 'validaiton'], loc='upper left')\n#     plt.ylabel('accuracy')\n#     plt.xlabel('epoch')\n#     plt.show()\n    plt.figure(figsize=(20,10))\n    sns.lineplot(range(1, epoch+1), History.history['loss'], label='Train loss')\n    sns.lineplot(range(1, epoch+1), History.history['val_loss'], label='Test loss')\n    plt.legend(['train', 'validaiton'], loc='upper left')\n    plt.ylabel('loss')\n    plt.xlabel('epoch')\n    plt.title(\"Loss Graph\")\n    plt.show()\n    \neva_plot(history, 4)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"8ba76c4665ebb1d6d065c3f2902964cd0b24e73d"},"cell_type":"code","source":"for layer in embedding_model.layers[150:]:\n    layer.trainable = True\nfor layer in embedding_model.layers[:150]:\n    layer.trainable = False\ntriplet_model.compile(loss=None, optimizer=Adam(0.0001))\n\nhistory = triplet_model.fit_generator(gen_tr, \n                                    validation_data=gen_te, \n                                    epochs=3, \n                                    verbose=1, \n                                    workers=4,\n                                    steps_per_epoch=70, \n                                    validation_steps=30, use_multiprocessing=True)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"2073383f0ac74aa66018cc3914855f46c6e73b96"},"cell_type":"code","source":"eva_plot(history, 3)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"96a8bf7225428db200b01b5d6ead5f92b992242b"},"cell_type":"code","source":"def data_generator(fpaths, batch=16):\n    i = 0\n    imgs = []\n    fnames = []\n    for path in fpaths:\n        if i == 0:\n            imgs = []\n            fnames = []\n        i += 1\n        img = read_and_resize(path)\n        imgs.append(img)\n        fnames.append(os.path.basename(path))\n        if i == batch:\n            i = 0\n            imgs = np.array(imgs)\n            yield fnames, imgs\n            \n    if i != 0:\n        imgs = np.array(imgs)\n        yield fnames, imgs\n        \n    raise StopIteration()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"93c43c75d69dd9e8f32ba4af15938d9e1d9e12da"},"cell_type":"code","source":"data = pd.read_csv(path_csv)\nfile_id_mapping = {k: v for k, v in zip(data.Image.values, data.Id.values)}\nimport glob\ntrain_files = glob.glob(join(path_train, '*.jpg'))\ntest_files = glob.glob(join(path_test, '*.jpg'))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"a5d97d6431d149d82f5166e296ab4b7285a9f48a"},"cell_type":"code","source":"train_preds  = []\ntrain_file_names = []\nfor fnames, imgs in tqdm(data_generator(train_files, batch=32)):\n    predicts = embedding_model.predict(imgs)\n    predicts = predicts.tolist()\n    train_preds += predicts\n    train_file_names += fnames\ntrain_preds = np.array(train_preds)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"d14eb882f2161ccd81232a23e9c3a04faaaa25c8"},"cell_type":"code","source":"test_preds = []\ntest_file_names = []\nfor fnames, imgs in tqdm(data_generator(test_files, batch=32)) :\n    predicts = embedding_model.predict(imgs)\n    predicts = predicts.tolist()\n    test_preds += predicts\n    test_file_names += fnames\ntest_preds = np.array(test_preds)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"b74ad80b63d2921372ea961d08a57d21ab1f19c9"},"cell_type":"code","source":"from sklearn.neighbors import NearestNeighbors\nneigh = NearestNeighbors(n_neighbors=6)\nneigh.fit(train_preds)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"collapsed":true,"_uuid":"9b31df83a0c48c2508db42d6e2c9ed877b1dca87"},"cell_type":"code","source":"distances_test, neighbors_test = neigh.kneighbors(test_preds)\ndistances_test, neighbors_test = distances_test.tolist(), neighbors_test.tolist()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"collapsed":true,"_uuid":"e72546552f519b658837252a34887cd3bfd0fd1b"},"cell_type":"code","source":"preds_str = []\n\nfor filepath, distance, neighbour_ in zip(test_file_names, distances_test, neighbors_test):\n    sample_result = []\n    sample_classes = []\n    for d, n in zip(distance, neighbour_):\n        train_file = train_files[n].split(os.sep)[-1]\n        class_train = file_id_mapping[train_file]\n        sample_classes.append(class_train)\n        sample_result.append((class_train, d))\n\n    if \"new_whale\" not in sample_classes:\n        sample_result.append((\"new_whale\", 0.1))\n    sample_result.sort(key=lambda x: x[1])\n    sample_result = sample_result[:5]\n    preds_str.append(\" \".join([x[0] for x in sample_result]))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"15f523cd3d2d3c9d032eac1f0f563fde115ee1df"},"cell_type":"code","source":"preds_str","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-output":true,"trusted":true,"_uuid":"f826221d3ceb07b8991bb8cea9edcb1a3e59a434"},"cell_type":"code","source":"df = pd.DataFrame(preds_str, columns=[\"Id\"])\ndf['Image'] = [x.split(os.sep)[-1] for x in test_file_names]\ndf.to_csv(\"sub_humpback.csv\", index=False)","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"name":"python","version":"3.6.6","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"}},"nbformat":4,"nbformat_minor":1}