{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":3004,"databundleVersionId":861823,"sourceType":"competition"}],"dockerImageVersionId":30746,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nimport seaborn as sns\n\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.manifold import TSNE\n\nimport keras","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","_kg_hide-input":true,"_kg_hide-output":true,"execution":{"iopub.status.busy":"2024-08-17T16:07:14.156361Z","iopub.execute_input":"2024-08-17T16:07:14.156759Z","iopub.status.idle":"2024-08-17T16:07:28.886858Z","shell.execute_reply.started":"2024-08-17T16:07:14.156725Z","shell.execute_reply":"2024-08-17T16:07:28.885795Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<div style=\"display:fill; background-color:#000000;border-radius:5px;\">\n    <p style=\"font-size:300%; color:white;text-align:center\";>Intro</p>\n</div>","metadata":{}},{"cell_type":"markdown","source":"# Learning a Distance Function","metadata":{}},{"cell_type":"markdown","source":"One idea to resolve a Computer Vision classification task is to learn a distance function `d(img1,img2)` such as:  \n`d(img1,img2) measures the similarity between img1 and img2`.  \n\nWith such a distance function, the distance between images from the same class will be smaller than the distance between images from different classes.\n\nThis idea is good for learning from low samples (a.k.a one-shot learning) i.e. when the number of samples by class is very low. Typical use cases are: signature verification, face recognition.","metadata":{}},{"cell_type":"markdown","source":"# Siamese Network","metadata":{}},{"cell_type":"markdown","source":"A good way to learn such distance function is to train a Siamese Network. A Siamese Network is composed of two \"encoder networks\", that share the exact same parameters. Each encoder network will take a different image as input and generate a feature vector for that input image. Then the Siamese Network will compare the 2 feature vectors using a distance (e.g. euclidian distance or cosine similarity). \n\nHere is a schema (without the details) of the architecture implemented in this notebook:","metadata":{}},{"cell_type":"markdown","source":"![siamese network.png](attachment:564e206c-d440-415d-a7e5-b3cafc2758fe.png)","metadata":{},"attachments":{"564e206c-d440-415d-a7e5-b3cafc2758fe.png":{"image/png":"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AAA1EGgDAAAAgBoItAEAAABADQTaAAAAAKAGAm0AAAAAUAOBNgAAAACoQaPZ0l4GAOg7jUajvXTc2n94rr00Pf757y5qLx3n1QoAYPbSow0AAAAAaiDQBgAAAAA1EGgDAAAAgBoItAEAAABADQTaAAAAAKAGAm0AAAAAUAOBNgAAAACogUAbAAAAANRAoA0AAAAAaiDQBgAAAAA1EGgDAAAAgBoItAEAAABADQTaAAAAAKAGAm0AAAAAUINGs6W9DADQdxqNRntpZvBqBQAwe+nRBgAAAAA1mJZA27PPPpsLAEDdli9f3l46/T71qU+1lwAAmI1OeaAtAmzxAhxltgbbdu3ala677rq0aNGinN4S9S233CL4CAA1uOmmm2ZEgCvedW688cb2JwAAZqNTPkbbCy+8kIaGhvLynj170uLFi/PyTBaBsPDaa6/l+v3YunVr2rBhQ16++uqrc/3kk0/meuHChemZZ55Jy5Yty58BAAAA6F8mQ+iiDKpcx6O555570qFDh9LNN988FmQswccDBw6kNWvWpO985zt5PQAAAAD9S6CtizoDbb1EAO7WW29NS5YsSfv27WuvBQAAAKBfTctkCJGKWdIxi0ipjIBWjF8WZfXq1flzlBjPLHp9dSrjnIXHH388rVixYuyYGPOs85g4bzlfN7EtrluUNhTl3NV1dYv0UQAAAAD637QE2iJFMkpVpFOGhx9+OK1atSrt3r07j2EWgaft27enq666Kr366qt5n+LgwYP5PBFUu+aaa/JyOWbz5s1djwlx3ESsW7cuDQ8Ptz+lvFxK3cr9R482AAAAAPrftATa3k0EyDZu3Jief/759MQTT+Q6gk8jIyPp6aefbu81XhyzZcuWnHJZjokxz+KYb33rW+29Ji96vm3atKn9KeXlUuoUwcC4h3DttdfmGgAAAID+dtoDbTEZwP3335/OPffc/Dnqr371q3l5//79ue60bdu2tH79+van48fceeedefmRRx7J9Ux27733jvXG65XWCgAAAEB/Oe2Btk9/+tPtpRMuuuiiXP/yl7/MdaePfvSj7aUTLrjgglzv2bMn1zNVzDB611135XTXCDACAAAAcGY47YG2d9NtQoReFi9e3F6auZ599tk8vlwE2Z555pm+aDMAAAAAEzOjA22TMZmg3OkQQbYrr7wyp4w+9thjadmyZe0tAAAAAJwJ+jLQdvjw4fbSCTFraYhxzzp1m4k0Al/TpRpki/HlLr/88vYWAHj/4u9cjPnZaDRyWb16dXsLAAAwnfoy0PbFL34xj3VWRG+2O+64Iy+vW7cu1+HCCy/MdYzb9vjjj+flsGvXrhz46iVSO0MdveQ6g2wmPwCgbmvXrk3bt2/PEwwNDw+nBQsWtLdA/eI9Kt5nFi1alAO7UcfQGNP5IyYAwEzVl4G2JUuWpOuvvz4tXbo0/2ofn0dGRtLQ0NC4QFbMRnrbbbfl5WuuuSbvu2LFirRq1ap8TC/lHHG+OCZeIKfq85//fA6yRfDuoYceyufrLNWgIQBMRvRme/LJJ/Pftfh7smnTpmn7uxJ/H9/P30hOVnomztSg1datW/N7VAR2V65cmTMJ4j1n8+bN+YdFwTYAYLablkBbBJlKL7Fi/vz54+puev0i/81vfjP/Yh/K/1zE5yeeeCKvq/rGN76Rt8X1Y994GYzPP/jBD05qU/H1r3899wqIfeOYq666qr1l6sq5upX9+/e39wKAyfn5z3+e6/jxabrF37Yo1OfgwYO5fv3113M90xw6dCj/iBk/cMZ7V5RYjneq+Ldw9913t/cEAJidGs2W9vKMF72/IjC1c+dO45wBQEuk8UUPo+hZ1O0Hp1Mpel6FPnqVmPH69V3nnnvuSbfeemv+8XPfvn3ttQAAs88ZM+soAMw2EeiKIFuI4Ex8jhLpfUWk8nUbT6vbREGxLgImMcxCOVcsd6aiRjAothVl3+q6OE98rraliOBgbIt9qqJtce5oR7lG9NSrjpk6mfuZqNKeuGY3ZXtcpyrGfy3tLG3tvKeqeI69nm15XvE9hvhey35x/arO88R1o23dxpYtqajh9ttvz8vxzKpj19apV7YAAMBsIdAGAH0qhkLYuHFjXi7DKES59NJL87oIpixfvjw99dRTOeAS22I4hBhP65Of/OS44FQsX3LJJWO9kmLfkiIY46JWA0gx8VBsL8p1q+uKSDWcqEg9LEG2CDhFL73XXnstvfTSS3n7ZO5nMqLnWBlioluw6r777sv1FVdckesQQasY/zV6b8VzirbEOeL5dQvYRSAsnmNM0BT3FSWWY10E0i677LJ8jnj2Ib7X8kzPP//8vC5EgK3zPPGM4hnE2LKdY6SVVNS4/l133ZX3iUDb3r178/q6lO+5tB8AYNaK1NF+sWbNmshNaf74xz9ur5k+O3fuzNeeaGm9+LaPBIBTp/x96vy788orrzQXLlyYS+ffzS1btuRjNm7c2F7TbI6MjDSHhoZO2jc+x75Llixprzmh/M3rZnh4OG+LulNpc+e2cr5ubZ7s/UxWHBvniHNVxXVLm4rS/nhesb2qvKts27atvabZ3LFjx9g5qm2PY2+77bZx6+J7jH3jGp3KM43rxvdVxHlK+zv/HZTzRam2qU7luzmV1wAA6Bd91aMtUiVabU7Lli1rr5k+8Wt3XHuiZbrHyQGAqqeffjr3EIueUZ1/N9evX5/rakro4sWL80RBnfuWz9Gzbbo89thjJ7VjsvczWTfccEOuH3zwwVwXcd0QPeiKhx9+ONd33nlnnuG86stf/nKuH3300VyHb3/727m+//77x7U9jo1Jmzrvp5cHHngg1zEpVHxfRZwnJnIKvXrlRc+46j3U6d57783fzdVXX33KrgEA0C+kjgLAGeinP/1prn/4wx/mtM/OEiI40inSLyNFs3Pf6dRtEoCp3s9ExTUj7TFSMqvplyVgVgJxIQKS4Xvf+95J7ShBuJKyGeKc4cILL8z1VETwrMzu2e35RLAtAl2hpNpWRWrqqRDBzUhJjXZFIBEAYNZrAgB9q1fqaDVlsFeppkOGSE0sKYDdSqde60NJc+xMDw3vlTrazVTuZ7JKmyOdM5S00c602eo1e5VIIS3Kuoko99mZOtrre67qdmyv89UhUl7jmUfpTOcFAJit9GgDgDPQZz7zmVwPDw9HhKdriUH0i+jFViZC2LFjx7j9ZoLJ3s9UfOELX8j19u3bc13SRr/0pS/luogJBcLOnTu7tiNKNY11YXsmzm4pnRN1zjnn5Hr37t25Pt2i19+VV16ZexF2S/UFAJitBNoA4Az0sY99LNcTnfWzjCMW44597nOfy8t16Hb95557rr00cZO9n6mIcc8iiBYpmhFIeuihh/L6EoArysyahw8fzvV7WblyZa7fT5AsAlkRsIvAVreAXaT8xvhs4f2kqE5ENci2bdu2rqmsAACzlUAbAJyBPvvZz+Z68+bN48YcKyJYs2vXrvanlBYtWpTrvXv35rp4twkG3q2n1sUXX5zruH51+9atW9OGDRvanyZusvczVV/5yldyHb37InAVgbfqxAPh2muvzfXXvva1HODqFO2o3vO6detyfccdd4xbH8fGuG7V+1mwYEGuX3zxxVxXlYkG1qxZM+66sbx27dq8HJNFdE7QUKfOIJvJDwAAxms0I78BAOhLEdRZtWpVHgi/c8brCJJdf/31eTkCMB//+Mfz8iOPPJIH6I80zE2bNuV15Tyh7Pv9738/B5tKT6rOV4ZbbrklB75ie/Taih5b1fTNFStW5OuU7fv27cu9xSJ41Xn90Gg0ct3r1WQy9zNVEbQ677zz2p9Sz2BS9d6jLfPnz8+97SLtNO4x0kqrPb3KswjxXYXSA626b/UeY794ZtGzLrZH21avXt3zPPFc499ANdAW+8f2zvZM1dKlS8cmZSg99TpFYFEADgCYtSLQBgD0pxiEPv6cVwffr4pB8GPbwsokBzFA/pYtW9p7nLBjx47m0NDQuP1iXRwfnzvFZAFlW5TONsT2jRs3jm2Pc8f5SpuHOyZDiDZGeTeTuZ+pKm2Oa8Q99LJt27axyQbK/tG2uMdOcZ6435hYodruOEenmIyh3F88s5GRkfaW7ueJfWJdt7aW76euyQqq1+1VOr9XAIDZRI82AAAAAKiBMdoAAAAAoAYCbQAAAABQA4E2AOCMEhMAxMQKEy11zFbab+Keuz2LXiWeKQAA780YbQAAAABQAz3aAAAAAKAGAm0AAAAAUAOBNgAAAACogUAbAAAAANRAoA0AAAAAaiDQBgAAAAA1EGgDAAAAgBoItAEAAABADQTaAAAAAKAGAm0AAAAAUAOBNgAAAACogUAbAAAAALxvKf0/w7/iTaFbONAAAAAASUVORK5CYII="}}},{"cell_type":"markdown","source":"# Training a Siamese Network with Contrastive Loss","metadata":{}},{"cell_type":"markdown","source":"Contrastive loss refers to a loss function that aims to bring similar instances closer together and push apart dissimilar instances in the latent space. In this notebook, we are going to use the `maximum margin contrastive loss` with this formula, for one pair of images:   \n` L = (1-y).d² + y.max(0, margin-d)² `\nwhere:  \n* by convention, y = 0 if the 2 images belong to the same class, else y = 1\n* margin is an hyperparameter to impose a lower bound on the distance between a pair of images from different classes (margin > 0).\n\nWhen the images are from the same class: ` L = d² ` meaning the loss will increase with the distance.\n\nWhen the images are from different classes: ` L = max(0, margin-d)² `:  \n* If ` d > margin `, the loss is 0 (no penalty will be applied)  \n* If ` d < margin `, the loss will increase inversely with the distance. ","metadata":{}},{"cell_type":"code","source":"# Calculate the loss over a mini-batch of training samples\n# y_true = List of labels (label = 0 if the 2 images are from the same class, else 1)\n# y_pred = List of predictions (distance metric between the 2 encodings) \ndef contrastive_loss(y_true, y_pred):\n    margin = 1 # this is an hyperparameter\n    return keras.ops.mean((1-y_true)*keras.ops.square(y_pred) + y_true*keras.ops.square(keras.ops.maximum(0, margin-y_pred)))","metadata":{"execution":{"iopub.status.busy":"2024-08-17T16:07:28.888522Z","iopub.execute_input":"2024-08-17T16:07:28.889180Z","iopub.status.idle":"2024-08-17T16:07:28.898222Z","shell.execute_reply.started":"2024-08-17T16:07:28.889145Z","shell.execute_reply":"2024-08-17T16:07:28.896858Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Let's show an example where the siamese network managed to to bring similar instances closer together and push apart dissimilar instances in the latent space:","metadata":{}},{"cell_type":"code","source":"y_true = np.array([0, 1, 0])\ny_pred = np.array([0.2, 3.1, 0.5])\nprint(f'loss = {contrastive_loss(y_true, y_pred)}\\n')","metadata":{"execution":{"iopub.status.busy":"2024-08-17T16:07:28.900050Z","iopub.execute_input":"2024-08-17T16:07:28.900435Z","iopub.status.idle":"2024-08-17T16:07:28.981748Z","shell.execute_reply.started":"2024-08-17T16:07:28.900381Z","shell.execute_reply":"2024-08-17T16:07:28.980261Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Let's show another example where the network did not make such a good job:","metadata":{}},{"cell_type":"code","source":"y_true = np.array([0, 1, 0])\ny_pred = np.array([1.2, 1.4, 2.3])\nprint(f'loss = {contrastive_loss(y_true, y_pred)}')","metadata":{"execution":{"iopub.status.busy":"2024-08-17T16:07:28.985059Z","iopub.execute_input":"2024-08-17T16:07:28.985899Z","iopub.status.idle":"2024-08-17T16:07:29.000234Z","shell.execute_reply.started":"2024-08-17T16:07:28.985857Z","shell.execute_reply":"2024-08-17T16:07:28.998628Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<div style=\"display:fill; background-color:#000000;border-radius:5px;\">\n    <p style=\"font-size:300%; color:white;text-align:center\";>Implementation</p>\n</div>","metadata":{}},{"cell_type":"markdown","source":"# Dataset Preparation","metadata":{}},{"cell_type":"markdown","source":"Let's start by loading the training dataset, splitting the training dataset into train and validation sets using`train_test_split` and eventually reshaping the dataset 1D-vectors into 2D-images:","metadata":{}},{"cell_type":"code","source":"df_train = pd.read_csv('/kaggle/input/digit-recognizer/train.csv')\ny_train = df_train.pop('label')\n\nx_train, x_val, y_train, y_val = train_test_split(df_train, y_train, train_size=34000)\n\n# Reshaping the 784 pixel values into (28,28)\nx_train, x_val, y_train, y_val = x_train.to_numpy().reshape(x_train.shape[0],28,28), x_val.to_numpy().reshape(x_val.shape[0],28,28), y_train.to_list(), y_val.to_list()","metadata":{"execution":{"iopub.status.busy":"2024-08-17T16:07:29.001740Z","iopub.execute_input":"2024-08-17T16:07:29.002480Z","iopub.status.idle":"2024-08-17T16:07:32.938763Z","shell.execute_reply.started":"2024-08-17T16:07:29.002438Z","shell.execute_reply":"2024-08-17T16:07:32.937644Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"To check those transformations, let's display the first image of the training set with its label:","metadata":{}},{"cell_type":"code","source":"plt.figure(figsize=(2,2))\nplt.imshow(x_train[0])\nplt.title(f'label = {y_train[0]}')\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2024-08-17T16:07:32.940022Z","iopub.execute_input":"2024-08-17T16:07:32.940365Z","iopub.status.idle":"2024-08-17T16:07:33.156711Z","shell.execute_reply.started":"2024-08-17T16:07:32.940336Z","shell.execute_reply":"2024-08-17T16:07:33.155656Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Now, we are going to generate the pairs of images and their corresponding labels. \n\nThe pairing process will be like this:\n* for each image in the training set (resp. validation set), we are going to create 2 pairs \n* the first pair will have the image + another image from the same class\n* the second pair will have the image + another image from a different class\n\nBesides, we are going to generate the corresponding label following this convention: \n* label=0 for pairs of images from the same class\n* label=1 for pairs of images from different classes\n\nTo do this, we'll use the `gen_pairs` utility function:","metadata":{"execution":{"iopub.status.busy":"2024-08-15T10:48:22.125291Z","iopub.execute_input":"2024-08-15T10:48:22.125685Z","iopub.status.idle":"2024-08-15T10:48:22.527505Z","shell.execute_reply.started":"2024-08-15T10:48:22.125654Z","shell.execute_reply":"2024-08-15T10:48:22.526415Z"}}},{"cell_type":"code","source":"def gen_pairs(x, y):\n    \n    x_pairs = []\n    y_pairs = []\n    \n    labels = np.unique(y)\n    \n    for i in labels:\n    \n        label_indexes = np.where(y == i)[0]\n        other_label_indexes = np.where(y != i)[0] #array([1,2,4,...])\n        \n        for j in label_indexes:\n            # finding one image from the same class\n            same_label_index = np.random.choice(label_indexes)\n            matching_x = x[same_label_index]\n            # finding one image from a different class\n            other_label_index = np.random.choice(other_label_indexes)\n            non_matching_x = x[other_label_index]\n            non_matching_y = y[other_label_index]\n            # adding the matching and non-matching images to x[j] to create 2 pairs\n            x_pairs.append([x[j],matching_x])\n            x_pairs.append([x[j],non_matching_x])\n            # creating the corresponding labels\n            y_pairs.append(0) # 0 for pairs of images from the same class\n            y_pairs.append(1) # 1 for pairs of images from different classes\n        \n    return np.array(x_pairs), np.array(y_pairs)   ","metadata":{"execution":{"iopub.status.busy":"2024-08-17T16:07:33.157780Z","iopub.execute_input":"2024-08-17T16:07:33.158113Z","iopub.status.idle":"2024-08-17T16:07:33.166998Z","shell.execute_reply.started":"2024-08-17T16:07:33.158086Z","shell.execute_reply":"2024-08-17T16:07:33.165788Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"x_train_pairs, y_train_pairs = gen_pairs(x_train, y_train)\nx_val_pairs, y_val_pairs = gen_pairs(x_val, y_val)\nprint(f'x_train_pairs.shape={x_train_pairs.shape} and y_train_pairs={y_train_pairs.shape}')\nprint(f'x_val_pairs.shape={x_val_pairs.shape} and y_val_pairs={y_val_pairs.shape}')","metadata":{"execution":{"iopub.status.busy":"2024-08-17T16:07:33.168584Z","iopub.execute_input":"2024-08-17T16:07:33.169530Z","iopub.status.idle":"2024-08-17T16:07:36.094218Z","shell.execute_reply.started":"2024-08-17T16:07:33.169498Z","shell.execute_reply":"2024-08-17T16:07:36.093065Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Now let's check the pairs by displaying the first 2 pairs of the training data. If the process worked as expected: \n* we should find the same image representing a 0 in the 2 pairs\n* in the first pair, this image should be paired with an image from the same class (0 in this case)\n* in the second pair, this image should be paired with an image from another class","metadata":{"execution":{"iopub.status.busy":"2024-08-15T11:27:12.705290Z","iopub.execute_input":"2024-08-15T11:27:12.705694Z","iopub.status.idle":"2024-08-15T11:27:12.714534Z","shell.execute_reply.started":"2024-08-15T11:27:12.705664Z","shell.execute_reply":"2024-08-15T11:27:12.713252Z"}}},{"cell_type":"code","source":"fig, axes = plt.subplots(1, 2)\naxes[0].imshow(np.concatenate((x_train_pairs[0][0],x_train_pairs[0][1]), axis=1))\n#axes[0].set_title(f'labels = {y_train_pairs[0][0]} and {y_train_pairs[0][1]}')\naxes[0].set_title(f'label = {y_train_pairs[0]}')\naxes[0].axis(\"off\")\naxes[1].imshow(np.concatenate((x_train_pairs[1][0],x_train_pairs[1][1]), axis=1))\naxes[1].set_title(f'label = {y_train_pairs[1]}')\naxes[1].axis(\"off\")\nplt.tight_layout()\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2024-08-17T16:07:36.095634Z","iopub.execute_input":"2024-08-17T16:07:36.096012Z","iopub.status.idle":"2024-08-17T16:07:36.269766Z","shell.execute_reply.started":"2024-08-17T16:07:36.095983Z","shell.execute_reply":"2024-08-17T16:07:36.268660Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"We have one last step to finish the dataset preparation. We are going to create:\n* x_train_1 (resp. x_val_1) with the 1st image of each pair\n* x_train_2 (resp. x_val_2) with the 2nd image of each pair","metadata":{}},{"cell_type":"code","source":"x_train_1 = x_train_pairs[:,0]\nx_train_2 = x_train_pairs[:,1]\nx_val_1 = x_val_pairs[:,0]\nx_val_2 = x_val_pairs[:,1]","metadata":{"execution":{"iopub.status.busy":"2024-08-17T16:07:36.273724Z","iopub.execute_input":"2024-08-17T16:07:36.274197Z","iopub.status.idle":"2024-08-17T16:07:36.281083Z","shell.execute_reply.started":"2024-08-17T16:07:36.274167Z","shell.execute_reply":"2024-08-17T16:07:36.279913Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Define the Encoding Network","metadata":{}},{"cell_type":"code","source":"input = keras.layers.Input((28, 28, 1))\nx = keras.layers.BatchNormalization()(input)\nx = keras.layers.Conv2D(4, (3, 3), activation=\"relu\")(x)\nx = keras.layers.MaxPooling2D(pool_size=(2, 2))(x)\nx = keras.layers.Conv2D(16, (3, 3), activation=\"relu\")(x)\nx = keras.layers.MaxPooling2D(pool_size=(2, 2))(x)\nx = keras.layers.Flatten()(x)\n\nx = keras.layers.BatchNormalization()(x)\nfeature_vector = keras.layers.Dense(10, activation=\"relu\")(x)\nencoding_network = keras.Model(input, feature_vector, name=\"encoding_network\")","metadata":{"execution":{"iopub.status.busy":"2024-08-17T16:07:36.282930Z","iopub.execute_input":"2024-08-17T16:07:36.283505Z","iopub.status.idle":"2024-08-17T16:07:36.366777Z","shell.execute_reply.started":"2024-08-17T16:07:36.283467Z","shell.execute_reply":"2024-08-17T16:07:36.365736Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"encoding_network.summary()","metadata":{"execution":{"iopub.status.busy":"2024-08-17T16:07:36.367901Z","iopub.execute_input":"2024-08-17T16:07:36.368188Z","iopub.status.idle":"2024-08-17T16:07:36.395002Z","shell.execute_reply.started":"2024-08-17T16:07:36.368162Z","shell.execute_reply":"2024-08-17T16:07:36.393814Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Define the Siamese Network","metadata":{}},{"cell_type":"code","source":"# This function computes the Euclidian distance between 2 vectors\ndef compute_distance(vectors):\n    v, w = vectors\n    return keras.ops.sqrt(keras.ops.sum(keras.ops.square(v - w), axis=1, keepdims=True))","metadata":{"execution":{"iopub.status.busy":"2024-08-17T16:07:36.396302Z","iopub.execute_input":"2024-08-17T16:07:36.396645Z","iopub.status.idle":"2024-08-17T16:07:36.402510Z","shell.execute_reply.started":"2024-08-17T16:07:36.396617Z","shell.execute_reply":"2024-08-17T16:07:36.401345Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"input_1 = keras.layers.Input((28, 28, 1))\nfeature_vector_1 = encoding_network(input_1)\n\ninput_2 = keras.layers.Input((28, 28, 1))\nfeature_vector_2 = encoding_network(input_2)\n\ndistance = keras.layers.Lambda(compute_distance, output_shape=(1,))([feature_vector_1,feature_vector_2])\n\nnorm_distance = keras.layers.BatchNormalization()(distance)\noutput = keras.layers.Dense(1, activation=\"sigmoid\")(norm_distance)\nsiamese_network = keras.Model(inputs=[input_1, input_2], outputs=output, name=\"siamese_network\") ","metadata":{"execution":{"iopub.status.busy":"2024-08-17T16:07:36.403803Z","iopub.execute_input":"2024-08-17T16:07:36.404183Z","iopub.status.idle":"2024-08-17T16:07:36.444332Z","shell.execute_reply.started":"2024-08-17T16:07:36.404155Z","shell.execute_reply":"2024-08-17T16:07:36.443291Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"siamese_network.summary()","metadata":{"execution":{"iopub.status.busy":"2024-08-17T16:07:36.446163Z","iopub.execute_input":"2024-08-17T16:07:36.446530Z","iopub.status.idle":"2024-08-17T16:07:36.475798Z","shell.execute_reply.started":"2024-08-17T16:07:36.446499Z","shell.execute_reply":"2024-08-17T16:07:36.474730Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Train the Model","metadata":{}},{"cell_type":"code","source":"siamese_network.compile(loss=contrastive_loss, optimizer='Adam')","metadata":{"execution":{"iopub.status.busy":"2024-08-17T16:07:36.477123Z","iopub.execute_input":"2024-08-17T16:07:36.477456Z","iopub.status.idle":"2024-08-17T16:07:36.494169Z","shell.execute_reply.started":"2024-08-17T16:07:36.477428Z","shell.execute_reply":"2024-08-17T16:07:36.492843Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"history = siamese_network.fit(\n    x = [x_train_1, x_train_2],\n    y = y_train_pairs,\n    validation_data=([x_val_1, x_val_2], y_val_pairs),\n    batch_size=32,\n    epochs=10\n)","metadata":{"execution":{"iopub.status.busy":"2024-08-17T16:07:36.495679Z","iopub.execute_input":"2024-08-17T16:07:36.496153Z","iopub.status.idle":"2024-08-17T16:12:01.462444Z","shell.execute_reply.started":"2024-08-17T16:07:36.496113Z","shell.execute_reply":"2024-08-17T16:12:01.461120Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def plot_history(item):\n    plt.plot(history.history[item], label=item)\n    plt.plot(history.history[\"val_\" + item], label=\"val_\" + item)\n    plt.xlabel(\"Epochs\")\n    plt.ylabel(item)\n    plt.title(\"Contrastive Loss\")\n    plt.legend()\n    plt.grid()\n    plt.show()\n\nplot_history(\"loss\")","metadata":{"execution":{"iopub.status.busy":"2024-08-17T16:12:53.956460Z","iopub.execute_input":"2024-08-17T16:12:53.956879Z","iopub.status.idle":"2024-08-17T16:12:54.232426Z","shell.execute_reply.started":"2024-08-17T16:12:53.956847Z","shell.execute_reply":"2024-08-17T16:12:54.231201Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Plot the Feature Vectors in the Latent Space","metadata":{}},{"cell_type":"markdown","source":"Now that we trained the encoder network, let's use it to generate feature vectors for the images from the validation set:","metadata":{}},{"cell_type":"code","source":"x_val_encodings = encoding_network(x_val)","metadata":{"execution":{"iopub.status.busy":"2024-08-17T16:22:25.111390Z","iopub.execute_input":"2024-08-17T16:22:25.111796Z","iopub.status.idle":"2024-08-17T16:22:25.620261Z","shell.execute_reply.started":"2024-08-17T16:22:25.111765Z","shell.execute_reply":"2024-08-17T16:22:25.619079Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"We'll use TSNE to reduce the dimension of the latent space so we can plot the encodings:","metadata":{}},{"cell_type":"code","source":"x_val_tsne = TSNE(n_components=2, random_state=0).fit_transform(x_val_encodings)","metadata":{"execution":{"iopub.status.busy":"2024-08-17T16:22:28.822776Z","iopub.execute_input":"2024-08-17T16:22:28.823690Z","iopub.status.idle":"2024-08-17T16:23:08.543945Z","shell.execute_reply.started":"2024-08-17T16:22:28.823641Z","shell.execute_reply":"2024-08-17T16:23:08.542356Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"As expected, the trained encoder manages pretty well to separate the images from different classes: ","metadata":{}},{"cell_type":"code","source":"plt.figure()\nsns.scatterplot(x=x_val_tsne[:,0], y=x_val_tsne[:,1], hue=y_val, palette=\"deep\")\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2024-08-17T16:26:27.972739Z","iopub.execute_input":"2024-08-17T16:26:27.973480Z","iopub.status.idle":"2024-08-17T16:26:28.840173Z","shell.execute_reply.started":"2024-08-17T16:26:27.973443Z","shell.execute_reply":"2024-08-17T16:26:28.839116Z"},"trusted":true},"execution_count":null,"outputs":[]}]}