{"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":"code","source":"!pip install -q efficientnet\n!pip install tensorflow_addons","metadata":{"execution":{"iopub.status.busy":"2022-03-20T08:37:26.644335Z","iopub.execute_input":"2022-03-20T08:37:26.644723Z","iopub.status.idle":"2022-03-20T08:37:44.284656Z","shell.execute_reply.started":"2022-03-20T08:37:26.644637Z","shell.execute_reply":"2022-03-20T08:37:44.283824Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport math\nimport os\nimport re\nimport tensorflow as tf\nimport tensorflow_hub as tfhub\nimport efficientnet.tfkeras as efn\nfrom tensorflow.keras import backend as K\nimport tensorflow_addons as tfa\nimport cv2\nimport matplotlib.pyplot as plt\nfrom sklearn.neighbors import NearestNeighbors\nimport json\nfrom tqdm import tqdm","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2022-03-20T16:12:46.842883Z","iopub.execute_input":"2022-03-20T16:12:46.843122Z","iopub.status.idle":"2022-03-20T16:12:52.80187Z","shell.execute_reply.started":"2022-03-20T16:12:46.843054Z","shell.execute_reply":"2022-03-20T16:12:52.800691Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_df = pd.read_csv('../input/happy-whale-and-dolphin/train.csv')\ntest_df = pd.read_csv('../input/happy-whale-and-dolphin/sample_submission.csv')","metadata":{"execution":{"iopub.status.busy":"2022-03-20T08:37:51.516825Z","iopub.execute_input":"2022-03-20T08:37:51.517323Z","iopub.status.idle":"2022-03-20T08:37:51.648412Z","shell.execute_reply.started":"2022-03-20T08:37:51.517283Z","shell.execute_reply":"2022-03-20T08:37:51.647693Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def count_data_items(filenames):\n    n = [int(re.compile(r\"-([0-9]*)\\.\").search(filename).group(1)) \n         for filename in filenames]\n    return np.sum(n)","metadata":{"execution":{"iopub.status.busy":"2022-03-20T08:37:51.650553Z","iopub.execute_input":"2022-03-20T08:37:51.65081Z","iopub.status.idle":"2022-03-20T08:37:51.655379Z","shell.execute_reply.started":"2022-03-20T08:37:51.650771Z","shell.execute_reply":"2022-03-20T08:37:51.654673Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_files = np.sort(np.array(tf.io.gfile.glob('../input/happywhale-tfrecords-384x384/happywhale-2022-train*.tfrec')))\ntest_files = np.sort(np.array(tf.io.gfile.glob('../input/happywhale-tfrecords-384x384/happywhale-2022-test*.tfrec')))\n\n# train_files = np.sort(np.array(tf.io.gfile.glob('../input/happywhale-tfrecords-128-128/*train*.tfrec')))\n# test_files = np.sort(np.array(tf.io.gfile.glob('../input/happywhale-tfrecords-128-128/*test*.tfrec')))\n# val_files = np.sort(np.array(tf.io.gfile.glob('../input/happywhale-tfrecords-128-128/*val*.tfrec')))\n\nprint(len(train_files),len(test_files),count_data_items(train_files),count_data_items(test_files))","metadata":{"execution":{"iopub.status.busy":"2022-03-20T08:37:51.656569Z","iopub.execute_input":"2022-03-20T08:37:51.656999Z","iopub.status.idle":"2022-03-20T08:37:51.67837Z","shell.execute_reply.started":"2022-03-20T08:37:51.656961Z","shell.execute_reply":"2022-03-20T08:37:51.677686Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Config","metadata":{}},{"cell_type":"code","source":"class config:\n    \n    IMAGE_SIZE = 384\n    EPOCHS = 15\n    BATCH_SIZE = 8\n    AUTO = tf.data.experimental.AUTOTUNE\n    CUTOUT = False\n\nCUTOUT = False\nIMAGE_SIZE = 384\nEPOCHS = 15\nBATCH_SIZE = 8\nAUTO = tf.data.experimental.AUTOTUNE","metadata":{"execution":{"iopub.status.busy":"2022-03-20T16:18:12.278104Z","iopub.execute_input":"2022-03-20T16:18:12.278403Z","iopub.status.idle":"2022-03-20T16:18:12.348314Z","shell.execute_reply.started":"2022-03-20T16:18:12.278326Z","shell.execute_reply":"2022-03-20T16:18:12.347377Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Data","metadata":{}},{"cell_type":"code","source":"def arcface_format(posting_id, image, label_group, matches):\n    return posting_id, {'inp1': image, 'inp2': label_group}, label_group, matches\n\ndef arcface_inference_format(posting_id, image, label_group, matches):\n    return image,posting_id\n\ndef arcface_eval_format(posting_id, image, label_group, matches):\n    return image,label_group","metadata":{"execution":{"iopub.status.busy":"2022-03-20T08:37:51.68915Z","iopub.execute_input":"2022-03-20T08:37:51.689401Z","iopub.status.idle":"2022-03-20T08:37:51.695126Z","shell.execute_reply.started":"2022-03-20T08:37:51.689377Z","shell.execute_reply":"2022-03-20T08:37:51.694338Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Function to decode our images\ndef decode_image(image_data):\n    image = tf.image.decode_jpeg(image_data, channels = 3)\n    image = tf.image.resize(image, [IMAGE_SIZE, IMAGE_SIZE])\n    image = tf.cast(image, tf.float32) / 255.0\n    return image\n\n# This function parse our images and also get the target variable\ndef read_labeled_tfrecord(example):\n    LABELED_TFREC_FORMAT = {\n        \"image_name\": tf.io.FixedLenFeature([], tf.string),\n        \"image\": tf.io.FixedLenFeature([], tf.string),\n        \"target\": tf.io.FixedLenFeature([], tf.int64),\n    }\n\n    example = tf.io.parse_single_example(example, LABELED_TFREC_FORMAT)\n    posting_id = example['image_name']\n    image = decode_image(example['image'])\n#     label_group = tf.one_hot(tf.cast(example['label_group'], tf.int32), depth = N_CLASSES)\n    label_group = tf.cast(example['target'], tf.int32)\n    matches = 1\n    return posting_id, image, label_group, matches","metadata":{"execution":{"iopub.status.busy":"2022-03-20T08:37:51.696326Z","iopub.execute_input":"2022-03-20T08:37:51.696988Z","iopub.status.idle":"2022-03-20T08:37:51.706026Z","shell.execute_reply.started":"2022-03-20T08:37:51.696955Z","shell.execute_reply":"2022-03-20T08:37:51.705163Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# This function loads TF Records and parse them into tensors\ndef load_dataset(filenames, ordered = False):\n    \n    ignore_order = tf.data.Options()\n    if not ordered:\n        ignore_order.experimental_deterministic = False \n        \n    dataset = tf.data.TFRecordDataset(filenames, num_parallel_reads = AUTO)\n#     dataset = dataset.cache()\n    dataset = dataset.with_options(ignore_order)\n    dataset = dataset.map(read_labeled_tfrecord, num_parallel_calls = AUTO) \n    return dataset\n\n# This function is to get our training tensors\ndef get_training_dataset(filenames):\n    dataset = load_dataset(filenames, ordered = False)\n    dataset = dataset.map(data_augment, num_parallel_calls = AUTO)\n    dataset = dataset.map(arcface_format, num_parallel_calls = AUTO)\n    dataset = dataset.map(lambda posting_id, image, label_group, matches: (image, label_group))\n    dataset = dataset.repeat()\n    dataset = dataset.shuffle(2048)\n    dataset = dataset.batch(BATCH_SIZE)\n    dataset = dataset.prefetch(AUTO)\n    return dataset\n\n# This function is to get our training tensors\ndef get_val_dataset(filenames):\n    dataset = load_dataset(filenames, ordered = True)\n    dataset = dataset.map(data_augment, num_parallel_calls = AUTO)\n    dataset = dataset.map(arcface_format, num_parallel_calls = AUTO)\n    dataset = dataset.map(lambda posting_id, image, label_group, matches: (image, label_group))\n    dataset = dataset.batch(BATCH_SIZE)\n    dataset = dataset.prefetch(AUTO)\n    return dataset\n\n# This function is to get our training tensors\ndef get_eval_dataset(filenames, get_targets = True):\n    dataset = load_dataset(filenames, ordered = True)\n    dataset = dataset.map(data_augment, num_parallel_calls = AUTO)\n    dataset = dataset.map(arcface_eval_format, num_parallel_calls = AUTO)\n    if not get_targets:\n        dataset = dataset.map(lambda image, target: image)\n    dataset = dataset.batch(BATCH_SIZE)\n    dataset = dataset.prefetch(AUTO)\n    return dataset\n\n# This function is to get our training tensors\ndef get_test_dataset(filenames, get_names = True):\n    dataset = load_dataset(filenames, ordered = True)\n    dataset = dataset.map(data_augment, num_parallel_calls = AUTO)\n    dataset = dataset.map(arcface_inference_format, num_parallel_calls = AUTO)\n    if not get_names:\n        dataset = dataset.map(lambda image, posting_id: image)\n    dataset = dataset.batch(BATCH_SIZE)\n    dataset = dataset.prefetch(AUTO)\n    return dataset","metadata":{"execution":{"iopub.status.busy":"2022-03-20T08:37:51.707486Z","iopub.execute_input":"2022-03-20T08:37:51.707756Z","iopub.status.idle":"2022-03-20T08:37:51.721836Z","shell.execute_reply.started":"2022-03-20T08:37:51.707722Z","shell.execute_reply":"2022-03-20T08:37:51.721177Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Augmentations","metadata":{}},{"cell_type":"code","source":"# Data augmentation function\ndef data_augment(posting_id, image, label_group, matches):\n\n    ### CUTOUT\n    if tf.random.uniform([])>0.5 and CUTOUT:\n      N_CUTOUT = 6\n      for cutouts in range(N_CUTOUT):\n        if tf.random.uniform([])>0.5:\n           DIM = IMAGE_SIZE\n           CUTOUT_LENGTH = DIM//8\n           x1 = tf.cast( tf.random.uniform([],0,DIM-CUTOUT_LENGTH),tf.int32)\n           x2 = tf.cast( tf.random.uniform([],0,DIM-CUTOUT_LENGTH),tf.int32)\n           filter_ = tf.concat([tf.zeros((x1,CUTOUT_LENGTH)),tf.ones((CUTOUT_LENGTH,CUTOUT_LENGTH)),tf.zeros((DIM-x1-CUTOUT_LENGTH,CUTOUT_LENGTH))],axis=0)\n           filter_ = tf.concat([tf.zeros((DIM,x2)),filter_,tf.zeros((DIM,DIM-x2-CUTOUT_LENGTH))],axis=1)\n           cutout = tf.reshape(1-filter_,(DIM,DIM,1))\n           image = cutout*image\n\n    image = tf.image.random_flip_left_right(image)\n    # image = tf.image.random_flip_up_down(image)\n    image = tf.image.random_hue(image, 0.01)\n    image = tf.image.random_saturation(image, 0.70, 1.30)\n    image = tf.image.random_contrast(image, 0.80, 1.20)\n    image = tf.image.random_brightness(image, 0.10)\n    return posting_id, image, label_group, matches","metadata":{"execution":{"iopub.status.busy":"2022-03-20T08:37:51.725029Z","iopub.execute_input":"2022-03-20T08:37:51.725952Z","iopub.status.idle":"2022-03-20T08:37:51.736597Z","shell.execute_reply.started":"2022-03-20T08:37:51.725917Z","shell.execute_reply":"2022-03-20T08:37:51.735747Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### Look to images from train","metadata":{}},{"cell_type":"code","source":"row = 10; col = 8;\nrow = min(row, BATCH_SIZE//col)\nN_TRAIN = count_data_items(train_files)\nprint(N_TRAIN)\nds = get_training_dataset(train_files)\n\nfor (sample,label) in ds:\n    img = sample['inp1']\n    plt.figure(figsize=(25,int(25*row/col)))\n    for j in range(row*col):\n        plt.subplot(row,col,j+1)\n        plt.title(label[j].numpy())\n        plt.axis('off')\n        plt.imshow(img[j,])\n    plt.show()\n    break\nprint(img.shape)","metadata":{"execution":{"iopub.status.busy":"2022-03-20T08:37:51.738132Z","iopub.execute_input":"2022-03-20T08:37:51.738398Z","iopub.status.idle":"2022-03-20T08:38:08.113398Z","shell.execute_reply.started":"2022-03-20T08:37:51.738366Z","shell.execute_reply":"2022-03-20T08:38:08.112457Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### ArcFace model","metadata":{}},{"cell_type":"code","source":"# Arcmarginproduct class keras layer\nclass ArcMarginProduct(tf.keras.layers.Layer):\n    '''\n    Implements large margin arc distance.\n\n    Reference:\n        https://arxiv.org/pdf/1801.07698.pdf\n        https://github.com/lyakaap/Landmark2019-1st-and-3rd-Place-Solution/\n            blob/master/src/modeling/metric_learning.py\n    '''\n    def __init__(self, n_classes, s=30, m=0.50, easy_margin=False,\n                 ls_eps=0.0, **kwargs):\n\n        super(ArcMarginProduct, self).__init__(**kwargs)\n\n        self.n_classes = n_classes\n        self.s = s\n        self.m = m\n        self.ls_eps = ls_eps\n        self.easy_margin = easy_margin\n        self.cos_m = tf.math.cos(m)\n        self.sin_m = tf.math.sin(m)\n        self.th = tf.math.cos(math.pi - m)\n        self.mm = tf.math.sin(math.pi - m) * m\n\n\n    def build(self, input_shape):\n        super(ArcMarginProduct, self).build(input_shape[0])\n\n        self.W = self.add_weight(\n            name='W',\n            shape=(int(input_shape[0][-1]), self.n_classes),\n            initializer='glorot_uniform',\n            dtype='float32',\n            trainable=True,\n            regularizer=None)\n\n    def call(self, inputs):\n        X, y = inputs\n        y = tf.cast(y, dtype=tf.int32)\n        cosine = tf.matmul(\n            tf.math.l2_normalize(X, axis=1),\n            tf.math.l2_normalize(self.W, axis=0)\n        )\n        sine = tf.math.sqrt(1.0 - tf.math.pow(cosine, 2))\n        phi = cosine * self.cos_m - sine * self.sin_m\n        if self.easy_margin:\n            phi = tf.where(cosine > 0, phi, cosine)\n        else:\n            phi = tf.where(cosine > self.th, phi, cosine - self.mm)\n        one_hot = tf.cast(\n            tf.one_hot(y, depth=self.n_classes),\n            dtype=cosine.dtype\n        )\n        if self.ls_eps > 0:\n            one_hot = (1 - self.ls_eps) * one_hot + self.ls_eps / self.n_classes\n\n        output = (one_hot * phi) + ((1.0 - one_hot) * cosine)\n        output *= self.s\n        return output","metadata":{"execution":{"iopub.status.busy":"2022-03-20T08:38:08.115962Z","iopub.execute_input":"2022-03-20T08:38:08.116372Z","iopub.status.idle":"2022-03-20T08:38:08.130721Z","shell.execute_reply.started":"2022-03-20T08:38:08.116333Z","shell.execute_reply":"2022-03-20T08:38:08.129803Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def freeze_BN(model):\n    for layer in model.layers:\n        if not isinstance(layer, tf.keras.layers.BatchNormalization):\n            layer.trainable = True\n        else:\n            layer.trainable = False\n\ndef get_model():\n    head = ArcMarginProduct\n    margin = head(\n        n_classes = 15587, \n        s = 30, \n        m = 0.3, \n        name='head/arcface', \n        dtype='float32'\n        )\n    \n    inp = tf.keras.layers.Input(shape = [IMAGE_SIZE, IMAGE_SIZE, 3], name = 'inp1')\n    label = tf.keras.layers.Input(shape = (), name = 'inp2')\n\n    FEATURE_VECTOR = 'https://tfhub.dev/google/imagenet/efficientnet_v2_imagenet21k_ft1k_s/feature_vector/2'\n    \n    \n    embed = tfhub.KerasLayer(FEATURE_VECTOR, trainable=True)(inp)\n\n    embed = tf.keras.layers.Dropout(0.2)(embed)\n    embed = tf.keras.layers.Dense(512)(embed)\n    x = margin([embed, label])\n\n    output = tf.keras.layers.Softmax(dtype='float32')(x)\n\n#     model = tf.keras.models.Model(inputs = [inp, label], outputs = [output])\n    model = tf.keras.models.Model(inputs = [inp, label], outputs = [output])\n    embed_model = tf.keras.models.Model(inputs = inp, outputs = embed)  \n\n    opt = tf.keras.optimizers.Adam(learning_rate = 0.001)\n\n    model.compile(\n        optimizer = opt,\n        loss = [tf.keras.losses.SparseCategoricalCrossentropy()],\n        metrics = [tf.keras.metrics.SparseCategoricalAccuracy(),tf.keras.metrics.SparseTopKCategoricalAccuracy(k=5)]\n        ) \n\n    return model,embed_model","metadata":{"execution":{"iopub.status.busy":"2022-03-20T08:38:08.132021Z","iopub.execute_input":"2022-03-20T08:38:08.132281Z","iopub.status.idle":"2022-03-20T08:38:08.145809Z","shell.execute_reply.started":"2022-03-20T08:38:08.132246Z","shell.execute_reply":"2022-03-20T08:38:08.145098Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def get_lr_callback(plot=False):\n    lr_start   = 0.000001\n    lr_max     = 0.000005 * BATCH_SIZE  \n    lr_min     = 0.000001\n    lr_ramp_ep = 4\n    lr_sus_ep  = 0\n    lr_decay   = 0.9\n   \n    def lrfn(epoch):\n        if epoch < lr_ramp_ep:\n            lr = (lr_max - lr_start) / lr_ramp_ep * epoch + lr_start\n        elif epoch < lr_ramp_ep + lr_sus_ep:\n            lr = lr_max\n        else:\n            lr = (lr_max - lr_min) * lr_decay**(epoch - lr_ramp_ep - lr_sus_ep) + lr_min\n        return lr\n        \n    if plot:\n        epochs = list(range(EPOCHS))\n        learning_rates = [lrfn(x) for x in epochs]\n        plt.scatter(epochs,learning_rates)\n        plt.show()\n\n    lr_callback = tf.keras.callbacks.LearningRateScheduler(lrfn, verbose=False)\n    return lr_callback\n\nget_lr_callback(plot=True)","metadata":{"execution":{"iopub.status.busy":"2022-03-20T08:38:08.147125Z","iopub.execute_input":"2022-03-20T08:38:08.147439Z","iopub.status.idle":"2022-03-20T08:38:08.357399Z","shell.execute_reply.started":"2022-03-20T08:38:08.147402Z","shell.execute_reply":"2022-03-20T08:38:08.356723Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class Snapshot(tf.keras.callbacks.Callback):\n    \n    def __init__(self,snapshot_epochs=[]):\n        super(Snapshot, self).__init__()\n        self.snapshot_epochs = snapshot_epochs\n        \n    def on_epoch_end(self, epoch, logs=None):\n        if epoch in self.snapshot_epochs: # your custom condition         \n            self.model.save_weights(f'./model_epoch{epoch}.h5')\n        self.model.save_weights(\"./model_last.h5\")","metadata":{"execution":{"iopub.status.busy":"2022-03-20T08:38:08.358748Z","iopub.execute_input":"2022-03-20T08:38:08.359167Z","iopub.status.idle":"2022-03-20T08:38:08.365551Z","shell.execute_reply.started":"2022-03-20T08:38:08.359128Z","shell.execute_reply":"2022-03-20T08:38:08.364901Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Train","metadata":{}},{"cell_type":"code","source":"TRAINING_FILENAMES = [x for i,x in enumerate(train_files[:-1])]\nVALIDATION_FILENAMES = [x for i,x in enumerate([train_files[-1]])]\nprint(len(TRAINING_FILENAMES),len(VALIDATION_FILENAMES),count_data_items(TRAINING_FILENAMES),count_data_items(VALIDATION_FILENAMES))","metadata":{"execution":{"iopub.status.busy":"2022-03-20T08:38:08.366511Z","iopub.execute_input":"2022-03-20T08:38:08.368081Z","iopub.status.idle":"2022-03-20T08:38:08.375751Z","shell.execute_reply.started":"2022-03-20T08:38:08.368038Z","shell.execute_reply":"2022-03-20T08:38:08.374924Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_dataset = get_training_dataset(TRAINING_FILENAMES)\nval_dataset = get_val_dataset(VALIDATION_FILENAMES)","metadata":{"execution":{"iopub.status.busy":"2022-03-20T08:38:08.377303Z","iopub.execute_input":"2022-03-20T08:38:08.377748Z","iopub.status.idle":"2022-03-20T08:38:08.914232Z","shell.execute_reply.started":"2022-03-20T08:38:08.377712Z","shell.execute_reply":"2022-03-20T08:38:08.913532Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"STEPS_PER_EPOCH = count_data_items(TRAINING_FILENAMES) // BATCH_SIZE\ntrain_logger = tf.keras.callbacks.CSVLogger('./training-log.h5.csv')","metadata":{"execution":{"iopub.status.busy":"2022-03-20T08:38:08.915472Z","iopub.execute_input":"2022-03-20T08:38:08.915718Z","iopub.status.idle":"2022-03-20T08:38:08.92273Z","shell.execute_reply.started":"2022-03-20T08:38:08.915686Z","shell.execute_reply":"2022-03-20T08:38:08.922058Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sv_loss = tf.keras.callbacks.ModelCheckpoint(\n   \"model_loss.h5\", monitor='val_loss', verbose=0, save_best_only=True,\n    save_weights_only=True, mode='min', save_freq='epoch')","metadata":{"execution":{"iopub.status.busy":"2022-03-20T08:38:08.923918Z","iopub.execute_input":"2022-03-20T08:38:08.924115Z","iopub.status.idle":"2022-03-20T08:38:08.937249Z","shell.execute_reply.started":"2022-03-20T08:38:08.924092Z","shell.execute_reply":"2022-03-20T08:38:08.93651Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model,embed_model = get_model()\nsnap = Snapshot(snapshot_epochs=[5,8])\nmodel.summary()","metadata":{"execution":{"iopub.status.busy":"2022-03-20T08:38:08.940177Z","iopub.execute_input":"2022-03-20T08:38:08.940373Z","iopub.status.idle":"2022-03-20T08:38:21.57561Z","shell.execute_reply.started":"2022-03-20T08:38:08.940345Z","shell.execute_reply":"2022-03-20T08:38:21.574903Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# model.load_weights('')","metadata":{"execution":{"iopub.status.busy":"2022-03-20T08:38:21.577036Z","iopub.execute_input":"2022-03-20T08:38:21.577282Z","iopub.status.idle":"2022-03-20T08:38:21.582552Z","shell.execute_reply.started":"2022-03-20T08:38:21.577248Z","shell.execute_reply":"2022-03-20T08:38:21.581752Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print('#### Image size: %i and Batch_size: %i'%\n      (IMAGE_SIZE, BATCH_SIZE))\n\nhistory = model.fit(train_dataset,\n                validation_data = val_dataset,\n                steps_per_epoch = STEPS_PER_EPOCH,\n                epochs = EPOCHS,\n                callbacks = [snap,get_lr_callback(),train_logger,sv_loss])","metadata":{"execution":{"iopub.status.busy":"2022-03-20T08:38:21.583784Z","iopub.execute_input":"2022-03-20T08:38:21.584039Z","iopub.status.idle":"2022-03-20T08:46:32.769581Z","shell.execute_reply.started":"2022-03-20T08:38:21.584004Z","shell.execute_reply":"2022-03-20T08:46:32.764728Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Evaluation","metadata":{}},{"cell_type":"code","source":"def get_ids(filename):\n    ds = get_test_dataset([filename],get_names=True).map(lambda image, image_name: image_name).unbatch()\n    NUM_IMAGES = count_data_items([filename])\n    ids = next(iter(ds.batch(NUM_IMAGES))).numpy().astype('U')\n    return ids\n\ndef get_targets(filename):\n    ds = get_eval_dataset([filename],get_targets=True).map(lambda image, target: target).unbatch()\n    NUM_IMAGES = count_data_items([filename])\n    ids = next(iter(ds.batch(NUM_IMAGES))).numpy()\n    return ids\n\ndef get_embeddings(filename):\n    ds = get_test_dataset([filename],get_names=False)\n    embeddings = embed_model.predict(ds,verbose=0)\n    return embeddings\n\ndef get_predictions(test_df,threshold=0.2):\n    predictions = {}\n    for i,row in tqdm(test_df.iterrows()):\n        if row.image in predictions:\n            if len(predictions[row.image])==5:\n                continue\n            predictions[row.image].append(row.target)\n        elif row.confidence>threshold:\n            predictions[row.image] = [row.target,'new_individual']\n        else:\n            predictions[row.image] = ['new_individual',row.target]\n\n    for x in tqdm(predictions):\n        if len(predictions[x])<5:\n            remaining = [y for y in sample_list if y not in predictions]\n            predictions[x] = predictions[x]+remaining\n            predictions[x] = predictions[x][:5]\n        \n    return predictions\n\ndef map_per_image(label, predictions):\n    \"\"\"Computes the precision score of one image.\n\n    Parameters\n    ----------\n    label : string\n            The true label of the image\n    predictions : list\n            A list of predicted elements (order does matter, 5 predictions allowed per image)\n\n    Returns\n    -------\n    score : double\n    \"\"\"    \n    try:\n        return 1 / (predictions[:5].index(label) + 1)\n    except ValueError:\n        return 0.0\n    \nf = open ('../input/happywhale-splits/individual_ids.json', \"r\")\ntarget_encodings = json.loads(f.read())\ntarget_encodings = {target_encodings[x]:x for x in target_encodings}\nsample_list = ['938b7e931166', '5bf17305f073', '7593d2aee842', '7362d7a01d00','956562ff2888']","metadata":{"execution":{"iopub.status.busy":"2022-03-20T08:47:36.95169Z","iopub.execute_input":"2022-03-20T08:47:36.952241Z","iopub.status.idle":"2022-03-20T08:47:36.988742Z","shell.execute_reply.started":"2022-03-20T08:47:36.952203Z","shell.execute_reply":"2022-03-20T08:47:36.987943Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_targets = []\ntrain_embeddings = []\nfor filename in tqdm(TRAINING_FILENAMES):\n    embeddings = get_embeddings(filename)\n    targets = get_targets(filename)\n    train_embeddings.append(embeddings)\n    train_targets.append(targets)\ntrain_embeddings = np.concatenate(train_embeddings)\ntrain_targets = np.concatenate(train_targets)","metadata":{"execution":{"iopub.status.busy":"2022-03-20T08:48:50.430491Z","iopub.execute_input":"2022-03-20T08:48:50.430974Z","iopub.status.idle":"2022-03-20T08:59:07.065848Z","shell.execute_reply.started":"2022-03-20T08:48:50.43094Z","shell.execute_reply":"2022-03-20T08:59:07.064845Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"neigh = NearestNeighbors(n_neighbors=50,metric='cosine')\nneigh.fit(train_embeddings)","metadata":{"execution":{"iopub.status.busy":"2022-03-20T09:00:28.051196Z","iopub.execute_input":"2022-03-20T09:00:28.051466Z","iopub.status.idle":"2022-03-20T09:00:28.089167Z","shell.execute_reply.started":"2022-03-20T09:00:28.051437Z","shell.execute_reply":"2022-03-20T09:00:28.088468Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_ids = []\ntest_nn_distances = []\ntest_nn_idxs = []\nval_targets = []\nval_embeddings = []\nfor filename in tqdm(VALIDATION_FILENAMES):\n    embeddings = get_embeddings(filename)\n    targets = get_targets(filename)\n    ids = get_ids(filename)\n    distances,idxs = neigh.kneighbors(embeddings, 50, return_distance=True)\n    test_ids.append(ids)\n    test_nn_idxs.append(idxs)\n    test_nn_distances.append(distances)\n    val_embeddings.append(embeddings)\n    val_targets.append(targets)\ntest_nn_distances = np.concatenate(test_nn_distances)\ntest_nn_idxs = np.concatenate(test_nn_idxs)\ntest_ids = np.concatenate(test_ids)\nval_embeddings = np.concatenate(val_embeddings)\nval_targets = np.concatenate(val_targets)","metadata":{"execution":{"iopub.status.busy":"2022-03-20T09:00:29.445257Z","iopub.execute_input":"2022-03-20T09:00:29.446007Z","iopub.status.idle":"2022-03-20T09:04:50.337605Z","shell.execute_reply.started":"2022-03-20T09:00:29.445962Z","shell.execute_reply":"2022-03-20T09:04:50.336823Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"allowed_targets = set([target_encodings[x] for x in np.unique(train_targets)])\nval_targets_df = pd.DataFrame(np.stack([test_ids,val_targets],axis=1),columns=['image','target'])\nval_targets_df['target'] = val_targets_df['target'].astype(int).map(target_encodings)\nval_targets_df.loc[~val_targets_df.target.isin(allowed_targets),'target'] = 'new_individual'\nval_targets_df.target.value_counts()","metadata":{"execution":{"iopub.status.busy":"2022-03-20T09:04:50.339935Z","iopub.execute_input":"2022-03-20T09:04:50.341173Z","iopub.status.idle":"2022-03-20T09:04:50.481372Z","shell.execute_reply.started":"2022-03-20T09:04:50.341123Z","shell.execute_reply":"2022-03-20T09:04:50.480469Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_df = []\nfor i in tqdm(range(len(test_ids))):\n    id_ = test_ids[i]\n    targets = train_targets[test_nn_idxs[i]]\n    distances = test_nn_distances[i]\n    subset_preds = pd.DataFrame(np.stack([targets,distances],axis=1),columns=['target','distances'])\n    subset_preds['image'] = id_\n    test_df.append(subset_preds)\ntest_df = pd.concat(test_df).reset_index(drop=True)\ntest_df['confidence'] = 1-test_df['distances']\ntest_df = test_df.groupby(['image','target']).confidence.max().reset_index()\ntest_df = test_df.sort_values('confidence',ascending=False).reset_index(drop=True)\ntest_df['target'] = test_df['target'].map(target_encodings)\ntest_df.to_csv('val_neighbors.csv')\ntest_df.image.value_counts().value_counts()","metadata":{"execution":{"iopub.status.busy":"2022-03-20T09:04:50.487051Z","iopub.execute_input":"2022-03-20T09:04:50.487476Z","iopub.status.idle":"2022-03-20T09:05:11.866677Z","shell.execute_reply.started":"2022-03-20T09:04:50.487439Z","shell.execute_reply":"2022-03-20T09:05:11.865794Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"## Compute CV\nbest_th = 0\nbest_cv = 0\nfor th in [0.1*x for x in range(11)]:\n    all_preds = get_predictions(test_df,threshold=th)\n    cv = 0\n    for i,row in val_targets_df.iterrows():\n        target = row.target\n        preds = all_preds[row.image]\n        val_targets_df.loc[i,th] = map_per_image(target,preds)\n    cv = val_targets_df[th].mean()\n    print(f\"CV at threshold {th}: {cv}\")\n    if cv>best_cv:\n        best_th = th\n        best_cv = cv","metadata":{"execution":{"iopub.status.busy":"2022-03-20T09:05:11.868402Z","iopub.execute_input":"2022-03-20T09:05:11.868742Z","iopub.status.idle":"2022-03-20T09:11:17.739328Z","shell.execute_reply.started":"2022-03-20T09:05:11.868702Z","shell.execute_reply":"2022-03-20T09:11:17.738579Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(\"Best threshold\",best_th)\nprint(\"Best cv\",best_cv)\nval_targets_df.describe()","metadata":{"execution":{"iopub.status.busy":"2022-03-20T09:11:17.740544Z","iopub.execute_input":"2022-03-20T09:11:17.740956Z","iopub.status.idle":"2022-03-20T09:11:17.805718Z","shell.execute_reply.started":"2022-03-20T09:11:17.740919Z","shell.execute_reply":"2022-03-20T09:11:17.804938Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"## Adjustment: Since Public lb has nearly 10% 'new_individual' (Be Careful for private LB)\nval_targets_df['is_new_individual'] = val_targets_df.target=='new_individual'\nprint(val_targets_df.is_new_individual.value_counts().to_dict())\nval_scores = val_targets_df.groupby('is_new_individual').mean().T\nval_scores['adjusted_cv'] = val_scores[True]*0.1+val_scores[False]*0.9\nbest_threshold_adjusted = val_scores['adjusted_cv'].idxmax()\nprint(\"best_threshold\",best_threshold_adjusted)\nval_scores","metadata":{"execution":{"iopub.status.busy":"2022-03-20T09:11:17.807631Z","iopub.execute_input":"2022-03-20T09:11:17.807914Z","iopub.status.idle":"2022-03-20T09:11:17.839633Z","shell.execute_reply.started":"2022-03-20T09:11:17.807877Z","shell.execute_reply":"2022-03-20T09:11:17.838945Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Inference","metadata":{}},{"cell_type":"code","source":"train_embeddings = np.concatenate([train_embeddings,val_embeddings])\ntrain_targets = np.concatenate([train_targets,val_targets])\nprint(train_embeddings.shape,train_targets.shape)","metadata":{"execution":{"iopub.status.busy":"2022-03-20T09:11:17.84135Z","iopub.execute_input":"2022-03-20T09:11:17.842167Z","iopub.status.idle":"2022-03-20T09:11:17.892788Z","shell.execute_reply.started":"2022-03-20T09:11:17.842132Z","shell.execute_reply":"2022-03-20T09:11:17.891918Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"neigh = NearestNeighbors(n_neighbors=50,metric='cosine')\nneigh.fit(train_embeddings)","metadata":{"execution":{"iopub.status.busy":"2022-03-20T09:11:17.894855Z","iopub.execute_input":"2022-03-20T09:11:17.895812Z","iopub.status.idle":"2022-03-20T09:11:17.932926Z","shell.execute_reply.started":"2022-03-20T09:11:17.895772Z","shell.execute_reply":"2022-03-20T09:11:17.932177Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_ids = []\ntest_nn_distances = []\ntest_nn_idxs = []\nfor filename in tqdm(test_files):\n    embeddings = get_embeddings(filename)\n    ids = get_ids(filename)\n    distances,idxs = neigh.kneighbors(embeddings, 50, return_distance=True)\n    test_ids.append(ids)\n    test_nn_idxs.append(idxs)\n    test_nn_distances.append(distances)\ntest_nn_distances = np.concatenate(test_nn_distances)\ntest_nn_idxs = np.concatenate(test_nn_idxs)\ntest_ids = np.concatenate(test_ids)","metadata":{"execution":{"iopub.status.busy":"2022-03-20T09:11:17.934729Z","iopub.execute_input":"2022-03-20T09:11:17.935487Z","iopub.status.idle":"2022-03-20T09:18:03.957319Z","shell.execute_reply.started":"2022-03-20T09:11:17.93545Z","shell.execute_reply":"2022-03-20T09:18:03.956361Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sample_submission = pd.read_csv('../input/happy-whale-and-dolphin/sample_submission.csv',index_col='image')\nprint(len(test_ids),len(sample_submission))\ntest_df = []\nfor i in tqdm(range(len(test_ids))):\n    id_ = test_ids[i]\n    targets = train_targets[test_nn_idxs[i]]\n    distances = test_nn_distances[i]\n    subset_preds = pd.DataFrame(np.stack([targets,distances],axis=1),columns=['target','distances'])\n    subset_preds['image'] = id_\n    test_df.append(subset_preds)\ntest_df = pd.concat(test_df).reset_index(drop=True)\ntest_df['confidence'] = 1-test_df['distances']\ntest_df = test_df.groupby(['image','target']).confidence.max().reset_index()\ntest_df = test_df.sort_values('confidence',ascending=False).reset_index(drop=True)\ntest_df['target'] = test_df['target'].map(target_encodings)\ntest_df.to_csv('test_neighbors.csv')\ntest_df.image.value_counts().value_counts()","metadata":{"execution":{"iopub.status.busy":"2022-03-20T09:18:03.960206Z","iopub.execute_input":"2022-03-20T09:18:03.967001Z","iopub.status.idle":"2022-03-20T09:18:32.375772Z","shell.execute_reply.started":"2022-03-20T09:18:03.966961Z","shell.execute_reply":"2022-03-20T09:18:32.375069Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sample_list = ['938b7e931166', '5bf17305f073', '7593d2aee842', '7362d7a01d00','956562ff2888']","metadata":{"execution":{"iopub.status.busy":"2022-03-20T09:18:32.377476Z","iopub.execute_input":"2022-03-20T09:18:32.377973Z","iopub.status.idle":"2022-03-20T09:18:32.382464Z","shell.execute_reply.started":"2022-03-20T09:18:32.377935Z","shell.execute_reply":"2022-03-20T09:18:32.38163Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"predictions = {}\nfor i,row in tqdm(test_df.iterrows()):\n    if row.image in predictions:\n        if len(predictions[row.image])==5:\n            continue\n        predictions[row.image].append(row.target)\n    elif row.confidence>best_threshold_adjusted:\n        predictions[row.image] = [row.target,'new_individual']\n    else:\n        predictions[row.image] = ['new_individual',row.target]\n        \nfor x in tqdm(predictions):\n    if len(predictions[x])<5:\n        remaining = [y for y in sample_list if y not in predictions]\n        predictions[x] = predictions[x]+remaining\n        predictions[x] = predictions[x][:5]\n    predictions[x] = ' '.join(predictions[x])\n    \npredictions = pd.Series(predictions).reset_index()\npredictions.columns = ['image','predictions']\npredictions.to_csv('submission.csv',index=False)\npredictions.head()","metadata":{"execution":{"iopub.status.busy":"2022-03-20T09:18:32.384241Z","iopub.execute_input":"2022-03-20T09:18:32.384747Z","iopub.status.idle":"2022-03-20T09:19:57.89151Z","shell.execute_reply.started":"2022-03-20T09:18:32.384711Z","shell.execute_reply":"2022-03-20T09:19:57.890663Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}