{"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_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"## 1. Data Overview\nRefer to the competition page for details: https://www.kaggle.com/c/humpback-whale-identification/data","metadata":{}},{"cell_type":"markdown","source":"## 2. Dependencies","metadata":{}},{"cell_type":"code","source":"# Install tf 2.0\n!pip install tensorflow==2.0.0","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport matplotlib.image as mpimg\n%matplotlib inline\n\nimport tensorflow as tf\n\nfrom tensorflow.keras import Model, Input\nfrom tensorflow.keras.models import Sequential\nfrom tensorflow.keras.layers import Activation, Conv2D, MaxPool2D,Concatenate,concatenate,Lambda, Flatten, Dense, BatchNormalization, ZeroPadding2D\nfrom tensorflow.keras.optimizers import Adam\nfrom tensorflow.keras.regularizers import l2\n\nimport os","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"tf.__version__","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 3. Parameters","metadata":{}},{"cell_type":"code","source":"# Hyperparameters\nbatch_size = 32\nepochs = 10\nimg_height = 128\nimg_width = 128\nval_split = 0.1\nAUTOTUNE = tf.data.experimental.AUTOTUNE\nval_size = np.floor(batch_size*val_split)\nnum_predict_img = 10","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 4. Prepare Data for Model","metadata":{}},{"cell_type":"code","source":"# Load file-labels data\ntrain_dir = '../input/humpback-whale-identification/train/'\ntest_dir = '../input/humpback-whale-identification/test/'\ndf = pd.read_csv('../input/humpback-whale-identification/train.csv')\n\n# Get the classes with more than 10 images\ndf_classesCount = df.groupby('Id').count().sort_values('Image',ascending=False)\nclass_names = df_classesCount[df_classesCount['Image']>10]\nclass_names = class_names.index\nnum_classes = len(class_names)\nprint('Number of classes:',num_classes)\n\n# Get test image file names\nlist_test = os.listdir(test_dir)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Helper functions\ndef getDFBatch(batch_size=32):\n    \"\"\"Create dataframe with pairs of image files, where half same class with target 1 \n    and half different classes with target 0\"\"\"\n    image_pairs = [[],[]]\n    targets = [1]*(batch_size//2)+[0]*(batch_size//2)\n    for i in range(batch_size//2):\n        # Choose one class\n        target = np.random.choice(class_names,replace=False)\n        # Choose two image files from the same class\n        image_pair = np.random.choice(df[df['Id']==target]['Image'],2,replace=False)\n        image_pairs[0].insert(0,image_pair[0])\n        image_pairs[1].insert(0,image_pair[1])\n        # Choose two classes\n        target = np.random.choice(class_names,2,replace=False)\n        # Choose two image files from different classes\n        im1 = np.random.choice(df[df['Id']==target[0]]['Image'],replace=False)\n        im2 = np.random.choice(df[df['Id']==target[1]]['Image'],replace=False)\n        image_pairs[0].append(im1)\n        image_pairs[1].append(im2)\n    return pd.DataFrame({'left_image':image_pairs[0],'right_image':image_pairs[1],'targets':targets})\n\n    \ndef processDS(left_file,right_file,target):\n    \"\"\"Process on training dataset elements to get the images and target\"\"\"\n    # Read images\n    left_file_path=train_dir+left_file\n    right_file_path=train_dir+right_file\n    img_left = tf.io.read_file(left_file_path)\n    img_right = tf.io.read_file(right_file_path)\n    \n    # Convert to grayscale\n    img_left = tf.image.decode_jpeg(img_left, channels=1)\n    img_right = tf.image.decode_jpeg(img_right, channels=1)\n    # Convert to floats in the [0,1] range.\n    img_left = tf.image.convert_image_dtype(img_left, tf.float32)\n    img_right = tf.image.convert_image_dtype(img_right, tf.float32)\n    # Resize the image to the desired size.\n    img_left = tf.image.resize(img_left, [img_width, img_height])\n    img_right = tf.image.resize(img_right, [img_width, img_height])\n    \n    return [img_left,img_right],target\n\ndef processTestDS(left_file,right_file):\n    \"\"\"Process on testing dataset elements to get the images\"\"\"\n    # Read images\n    left_file_path=train_dir+left_file\n    right_file_path=test_dir+right_file\n    img_left = tf.io.read_file(left_file_path)\n    img_right = tf.io.read_file(right_file_path)\n    \n    # Convert to grayscale\n    img_left = tf.image.decode_jpeg(img_left, channels=1)\n    img_right = tf.image.decode_jpeg(img_right, channels=1)\n    # Convert to floats in the [0,1] range.\n    img_left = tf.image.convert_image_dtype(img_left, tf.float32)\n    img_right = tf.image.convert_image_dtype(img_right, tf.float32)\n    # Resize the image to the desired size.\n    img_left = tf.image.resize(img_left, [img_width, img_height])\n    img_right = tf.image.resize(img_right, [img_width, img_height])\n    \n    images = [img_left,img_right]\n    images = tf.reshape(images, (2,img_height,img_width,1))\n    \n    return images\n\ndef getBatch(batch_size=32,df_img_files=None):\n    \"\"\"Create a dataset for training/testing data\"\"\"\n    df_batch = getDFBatch(batch_size=batch_size)\n    \n    if df_img_files is None:\n        ds = tf.data.Dataset.from_tensor_slices((df_batch['left_image'].values,df_batch['right_image'].values,\\\n                                             df_batch['targets'].values))\n        labeled_ds = ds.map(processDS, num_parallel_calls=AUTOTUNE)\n        return labeled_ds\n    else:\n        ds = tf.data.Dataset.from_tensor_slices((df_img_files['left_image'].values,\\\n                                                 df_img_files['right_image'].values))\n        labeled_ds = ds.map(processTestDS, num_parallel_calls=AUTOTUNE)\n        return labeled_ds\n","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 5. Siamese NN Model\n\nWe will implement an Siamese NN in Tensorflow 2.0. Reference http://www.cs.utoronto.ca/~gkoch/files/msc-thesis.pdf","metadata":{}},{"cell_type":"code","source":"# Helper functions\ndef siameseModel(input_shape):\n    # Define the tensors for the two input images\n    inputs = Input(([2]+input_shape))\n    left_input = inputs[:,0,:,:,:]\n    right_input = inputs[:,1,:,:,:]\n    \n    # Convolutional Neural Network\n    model = Sequential()\n    model.add(Conv2D(64, (10,10), activation='relu', input_shape=input_shape, kernel_regularizer=l2(2e-4)))\n    model.add(MaxPool2D())\n    model.add(Conv2D(128, (7,7), activation='relu', kernel_regularizer=l2(2e-4)))\n    model.add(MaxPool2D())\n    model.add(Conv2D(128, (4,4), activation='relu', kernel_regularizer=l2(2e-4)))\n    model.add(MaxPool2D())\n    model.add(Conv2D(256, (4,4), activation='relu', kernel_regularizer=l2(2e-4)))\n    model.add(Flatten())\n    model.add(Dense(num_classes, activation='sigmoid', kernel_regularizer=l2(1e-3)))\n    \n    # Generate the encodings (feature vectors) for the two images\n    encoded_l = model(left_input)\n    encoded_r = model(right_input)\n    \n    # Add a customized layer to compute the absolute difference between the encodings\n    L1_layer = Lambda(lambda tensors:tf.abs(tensors[0] - tensors[1]))\n    L1_distance = L1_layer([encoded_l, encoded_r])\n    \n    # Add a dense layer with a sigmoid unit to generate the similarity score\n    prediction = Dense(1,activation='sigmoid')(L1_distance)\n    \n    # Connect the inputs with the outputs\n    siamese_net = Model(inputs=inputs,outputs=prediction)\n      \n    # Compile model\n    siamese_net.compile(optimizer='adam',loss='binary_crossentropy',metrics=['accuracy'])\n    siamese_net.summary()\n    \n    # return the model\n    return siamese_net\n\n","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Build model\nmodel=siameseModel([img_height,img_width,1])","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Train model\nhistory = []\nfor i in range(epochs):\n    train_batch = getBatch(batch_size)\n    train_batch = train_batch.shuffle(batch_size)\n    # create validation dataset\n    valid_ds = train_batch.take(val_size)\n    valid_ds = valid_ds.batch(val_size)\n    # create training dataset\n    train_ds = train_batch.skip(val_size)\n    train_ds = train_ds.batch(batch_size-val_size)\n    \n    train_hist = model.train_on_batch(train_ds)\n    val_hist = model.test_on_batch(valid_ds)\n    history.append([train_hist, val_hist])\n    \n    if i%2==0:\n        print('Epoch {} ------------'.format(i))\n        print('Training loss and accuracy')\n        print(train_hist)\n        print('Validation loss and accuracy')\n        print(val_hist)\n    ","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Predict\n\n# prepare test data\nidx = np.random.randint(len(df), size=num_predict_img)\nnew_df = df.iloc[idx]\ntest_classes = new_df['Id']\nnew_df = new_df.drop(columns='Id')\nnew_df['right_image']=list_test[0]\nnew_df.rename(columns={'Image': 'left_image'}, inplace=True)\ntest_batch=getBatch(df_img_files=new_df)\ntest_batch=test_batch.batch(num_predict_img)\n\n# Get the scores\nr=model.predict(test_batch)\nprint(r)\n\n# Get the best class matching\nbest_idx = np.argmax(r.reshape(-1))\nprint('Best match is: ', test_classes.iloc[best_idx])","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Visualize the best matching\nbest_img_file = new_df['left_image'].iloc[best_idx]\nplt.subplot(1,2,1)\nimg = mpimg.imread(test_dir+list_test[0])\nplt.imshow(img)\nplt.title('Original')\nplt.subplot(1,2,2)\nimg = mpimg.imread(train_dir+best_img_file)\nplt.imshow(img)\nplt.title('Matching')\nplt.show()","metadata":{"trusted":true},"execution_count":null,"outputs":[]}]}