{"cells":[{"metadata":{"trusted":true,"_uuid":"719d0bf94b427da9b3beb3d247c1c2b5e9696371"},"cell_type":"markdown","source":"# An Attempt at using Siamese network:\n\n## Intuition :\n\nMy Intuition was to overcome the huge class imbalance in the dataset with the use of Siamese network. Instead of trying to understand sincere and insincere question, Siamese networks approaches the problem as a similarity problem. It tries to understand how similar a given pair is. Since it is easier to genrate as many combination of similar and non similar pairs as we would need from the dataset ( theoreticaly we can augment pairs in the order of Billions ). I thought the class imbalance would not be a problem and can lead to a better result.\n\n## Learning :\n\n1. The 2 hour constrain of kaggle kernels restricted me from using only 3-5 million pairs of generated data. But these were unfortunately not sufficient for the network to understand the nuances of many ambigous and difficult questions. I learnt that siamese are a good choice when we have much lesser samples per class. As the sample size increases , the triplets / pair sizes increases quadratically making training impossible.\n\n2. As the training size increases , the number of easy pairs ( pairs which are easier to differentiate / identify ) aslo increases in a given batch. This affects the gradient update as the average loss per batch is much less when majority of samples in a batch are easy pairs. This stalls the model from learning further after a certain point.\n\n## Things to try:\n\n1.  To use only hard samples during loss calculation. As stated in https://arxiv.org/pdf/1703.07737.pdf . For the dataset of this magnitude , I presume it will be still be impossible to get a decent result with a 2 hour time constraint. ( Implementation of Hard pair selector - https://github.com/adambielski/siamese-triplet/blob/master/utils.py )\n\n\n### References:\n\nhttps://www.kaggle.com/shujian/single-rnn-with-4-folds-clr\n\n"},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load in \n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the \"../input/\" directory.\n# For example, running this (by clicking run or pressing Shift+Enter) will list the files in the input directory\n\nimport os\nprint(os.listdir(\"../input\"))\n\n# Any results you write to the current directory are saved as output.","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","collapsed":true,"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","trusted":false},"cell_type":"code","source":"## some config values \nembed_size = 300 # how big is each word vector\nmax_features = 95000 # how many unique words to use (i.e num rows in embedding vector)\nmaxlen = 70 # max number of words in a question to use","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"1f4719d23c81202cf02f6747ad5608925ba7ff9f"},"cell_type":"code","source":"import os\nimport time\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\nimport math\nimport sklearn\nfrom sklearn.model_selection import train_test_split\nfrom sklearn import metrics\nfrom sklearn.model_selection import GridSearchCV, StratifiedKFold\nfrom sklearn.metrics import f1_score, roc_auc_score\nfrom keras.preprocessing.text import Tokenizer\nfrom keras.preprocessing.sequence import pad_sequences\nfrom keras.layers import Dense, Input, CuDNNLSTM, Embedding, Dropout, Activation, CuDNNGRU, Conv1D,merge\nfrom keras.layers import Bidirectional, GlobalMaxPool1D, GlobalMaxPooling1D, GlobalAveragePooling1D\nfrom keras.layers import Input, Embedding, Dense, Conv2D, MaxPool2D, concatenate,dot\nfrom keras.layers import Reshape, Flatten, Concatenate, Dropout, SpatialDropout1D\nfrom keras.optimizers import Adam\nfrom keras.models import Model,Sequential\nfrom keras.layers.core import Lambda, Flatten, Dense\nfrom keras import backend as K\nfrom keras.engine.topology import Layer\nfrom keras import initializers, regularizers, constraints, optimizers, layers\nfrom keras.layers import concatenate\nfrom keras.callbacks import *\nfrom sklearn.manifold import TSNE\nimport matplotlib.pyplot as plt\nimport time\nfrom datetime import timedelta\n%matplotlib inline","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"7dad97a98d6d1d7a9a3c000383737b07930f5ca3"},"cell_type":"code","source":"def split_df(df):\n    qid = df[\"qid\"].values\n    qid_train,qid_val = train_test_split(qid,test_size=0.20,random_state=2019)\n    train_df = df[df[\"qid\"].isin(qid_train)]\n    val_df = df[df[\"qid\"].isin(qid_val)]\n    return train_df.reset_index(drop=True),val_df.reset_index(drop=True)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"719773bb705f3cc4038ea732f274b4a3bfe17fa6"},"cell_type":"code","source":"def generate_pairs(df,augment=False):\n    df1 = df.copy()\n    df2 = df.copy()\n    df2 = sklearn.utils.shuffle(df2,random_state=2018)\n    df1 = sklearn.utils.shuffle(df1,random_state=2019)\n    df2.columns = [\"qid2\",\"question_text2\",\"target2\"]\n    df2 = df2.reset_index(drop=True)\n    df2 = pd.concat([df1, df2], axis=1)\n    df2[\"similarityTarget\"] = df2.apply(lambda row: 1-np.logical_xor(row.target,row.target2),axis=1 )\n    single_incincere = None\n    single_sincere = None\n    if augment:\n        df_augmented_non_similar = generate_non_similar_samples(df,portion_of_incincere=15)\n        df_augmented_similar = generate_similar_incincere_samples(df,portion_of_incincere=10)\n        df_augmented_single_incincere,single_incincere = generate_samples_against_single_incincere(df)\n        df_augmented_single_sincere,single_sincere = generate_samples_against_single_sincere(df)\n        df2 = pd.concat([df2,df_augmented_non_similar,df_augmented_similar,df_augmented_single_sincere,df_augmented_single_incincere])\n    df2 = df2.drop_duplicates()\n    df2 = sklearn.utils.shuffle(df2,random_state=2020)\n    print(\"Data not similar: {}\".format(len(df2[df2[\"similarityTarget\"]==0])))\n    print(\"Data similar: {}\".format(len(df2[df2[\"similarityTarget\"]==1])))\n    print(\"Not similar to Similar ratio: {}\".format(len(df2[df2[\"similarityTarget\"]==0])/len(df2[df2[\"similarityTarget\"]==1])))\n#     return df2\n    return df2[\"question_text\"].fillna(\"_##_\").values,df2[\"question_text2\"].fillna(\"_##_\").values,df2[\"target\"].values,df2[\"target2\"].values,df2[\"similarityTarget\"].values,single_incincere,single_sincere","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"22698d48fbbba82232a061c7307cae4334c3e020"},"cell_type":"code","source":"## Helper functions to generate pairs\n\ndef generate_non_similar_samples(data_df,portion_of_incincere = 3):\n    data_df_1 = data_df[data_df[\"target\"]==1].copy()\n    data_df_1 = pd.concat([data_df_1]*portion_of_incincere)\n    data_df_0 = data_df[data_df[\"target\"]==0].sample(n=len(data_df_1),random_state=2018).copy()\n    data_df_1.columns = [\"qid2\",\"question_text2\",\"target2\"]\n    data_df_1 = sklearn.utils.shuffle(data_df_1,random_state=2018)\n    data_df_0 = sklearn.utils.shuffle(data_df_0,random_state=2019)\n    data_df_1 = data_df_1.reset_index(drop=True)\n    data_df_0 = data_df_0.reset_index(drop=True)\n    data_df_1 = pd.concat([data_df_0, data_df_1], axis=1)\n    data_df_1[\"similarityTarget\"] = data_df_1.apply(lambda row: 1-np.logical_xor(row.target,row.target2),axis=1 )\n    return data_df_1\n\ndef generate_similar_incincere_samples(data_df,portion_of_incincere = 3):\n    data_df_1 = data_df[data_df[\"target\"]==1].copy()\n    data_df_1 = pd.concat([data_df_1]*portion_of_incincere)\n    data_df_0 = data_df_1.copy()\n    data_df_1.columns = [\"qid2\",\"question_text2\",\"target2\"]\n    data_df_1 = sklearn.utils.shuffle(data_df_1,random_state=2018)\n    data_df_0 = sklearn.utils.shuffle(data_df_0,random_state=2019)\n    data_df_1 = data_df_1.reset_index(drop=True)\n    data_df_0 = data_df_0.reset_index(drop=True)\n    data_df_1 = pd.concat([data_df_0, data_df_1], axis=1)\n    data_df_1 = data_df_1.drop_duplicates()\n    data_df_1 = data_df_1[data_df_1[\"question_text\"]!=data_df_1[\"question_text2\"]]\n    data_df_1[\"similarityTarget\"] = data_df_1.apply(lambda row: 1-np.logical_xor(row.target,row.target2),axis=1 )\n    return data_df_1\n\ndef _fuse_dataframes(single,data_df_1):\n    data_df_0 = pd.concat([single]*len(data_df_1))\n    data_df_1.columns = [\"qid2\",\"question_text2\",\"target2\"]\n    data_df_1 = sklearn.utils.shuffle(data_df_1,random_state=2018)\n    data_df_1 = data_df_1.reset_index(drop=True)\n    data_df_0 = data_df_0.reset_index(drop=True)\n    data_df_1 = pd.concat([data_df_0, data_df_1], axis=1)\n    data_df_1 = data_df_1[data_df_1[\"question_text\"]!=data_df_1[\"question_text2\"]]\n    data_df_1[\"similarityTarget\"] = data_df_1.apply(lambda row: 1-np.logical_xor(row.target,row.target2),axis=1 )\n    return data_df_1\n\ndef generate_samples_against_single_incincere(data_df,portion_of_incincere = 1):\n    data_df_1 = data_df[data_df[\"target\"]==1].copy()\n    data_df_2 = data_df[data_df[\"target\"]==0].copy()\n    data_df_1 = pd.concat([data_df_1]*portion_of_incincere)\n    single_incincere = data_df_1.sample(n=1,random_state=2018).copy()\n    data_df_1 = _fuse_dataframes(single_incincere,data_df_1)\n#     data_df_2 = _fuse_dataframes(single_incincere,data_df_2)\n#     data_df_1 = pd.concat([data_df_1, data_df_2])\n    return data_df_1,single_incincere\n\ndef generate_samples_against_single_sincere(data_df,portion_of_incincere = 1):\n    data_df_1 = data_df[data_df[\"target\"]==1].copy()\n    data_df_2 = data_df[data_df[\"target\"]==0].copy()\n    data_df_1 = pd.concat([data_df_1]*portion_of_incincere)\n    single_sincincere = data_df[data_df[\"target\"]==0].sample(n=1,random_state=2018).copy()\n    data_df_1 = _fuse_dataframes(single_sincincere,data_df_1)\n#     data_df_2 = _fuse_dataframes(single_sincincere,data_df_2)\n#     data_df_1 = pd.concat([data_df_1, data_df_2])\n    return data_df_1,single_sincincere","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"c2f20166429d3c36a0c272770a4bb07557b07906"},"cell_type":"code","source":"def load_and_prec():\n    data_df = pd.read_csv(\"../input/train.csv\")\n    test_df = pd.read_csv(\"../input/test.csv\")\n    print(\"Train shape : \",data_df.shape)\n    print(\"Test shape : \",test_df.shape)\n        \n    X = data_df[\"question_text\"].fillna(\"_##_\").values\n    train_df,val_df = split_df(data_df)\n    train_left_X,train_right_X,train_left_Y,train_right_Y,train_Y,single_incincere,single_sincere = generate_pairs(train_df,augment=True)\n    val_left_X,val_right_X,val_left_Y,val_right_Y,val_Y,_,_ = generate_pairs(val_df)\n    test_X = test_df[\"question_text\"].fillna(\"_##_\").values\n    print(\"Single sincere -\",single_sincere[\"question_text\"])\n    print(\"Single incincere -\",single_incincere[\"question_text\"])\n\n    ## Tokenize the sentences\n    tokenizer = Tokenizer(num_words=max_features)\n    tokenizer.fit_on_texts(list(X))\n    train_left_X = tokenizer.texts_to_sequences(train_left_X)\n    train_right_X = tokenizer.texts_to_sequences(train_right_X)\n    val_left_X = tokenizer.texts_to_sequences(val_left_X)\n    val_right_X = tokenizer.texts_to_sequences(val_right_X)\n    test_X = tokenizer.texts_to_sequences(test_X)\n    single_incincere_X = tokenizer.texts_to_sequences(single_incincere[\"question_text\"].fillna(\"_##_\").values)\n    single_sincere_X = tokenizer.texts_to_sequences(single_sincere[\"question_text\"].fillna(\"_##_\").values)\n\n    ## Pad the sentences \n    train_left_X = pad_sequences(train_left_X, maxlen=maxlen)\n    train_right_X = pad_sequences(train_right_X, maxlen=maxlen)\n    val_left_X = pad_sequences(val_left_X, maxlen=maxlen)\n    val_right_X = pad_sequences(val_right_X, maxlen=maxlen)\n    test_X = pad_sequences(test_X, maxlen=maxlen)\n    single_incincere_X = pad_sequences(single_incincere_X, maxlen=maxlen)\n    single_sincere_X = pad_sequences(single_sincere_X, maxlen=maxlen)\n    \n    return test_X,train_left_X,val_left_X,train_right_X,val_right_X,train_left_Y,val_left_Y,train_right_Y,val_right_Y,train_Y,val_Y, tokenizer.word_index,single_incincere_X,single_sincere_X","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"58b7c0e3ee2d35f97f77cb347b710e03f7ec9b68"},"cell_type":"code","source":"def load_glove(word_index):\n    EMBEDDING_FILE = '../input/embeddings/glove.840B.300d/glove.840B.300d.txt'\n    def get_coefs(word,*arr): return word, np.asarray(arr, dtype='float32')\n    embeddings_index = dict(get_coefs(*o.split(\" \")) for o in open(EMBEDDING_FILE))\n\n    all_embs = np.stack(embeddings_index.values())\n    emb_mean,emb_std = all_embs.mean(), all_embs.std()\n    embed_size = all_embs.shape[1]\n\n    # word_index = tokenizer.word_index\n    nb_words = min(max_features, len(word_index))\n    embedding_matrix = np.random.normal(emb_mean, emb_std, (nb_words, embed_size))\n    for word, i in word_index.items():\n        if i >= max_features: continue\n        embedding_vector = embeddings_index.get(word)\n        if embedding_vector is not None: embedding_matrix[i] = embedding_vector\n            \n    return embedding_matrix \n    \ndef load_fasttext(word_index):    \n    EMBEDDING_FILE = '../input/embeddings/wiki-news-300d-1M/wiki-news-300d-1M.vec'\n    def get_coefs(word,*arr): return word, np.asarray(arr, dtype='float32')\n    embeddings_index = dict(get_coefs(*o.split(\" \")) for o in open(EMBEDDING_FILE) if len(o)>100)\n\n    all_embs = np.stack(embeddings_index.values())\n    emb_mean,emb_std = all_embs.mean(), all_embs.std()\n    embed_size = all_embs.shape[1]\n\n    # word_index = tokenizer.word_index\n    nb_words = min(max_features, len(word_index))\n    embedding_matrix = np.random.normal(emb_mean, emb_std, (nb_words, embed_size))\n    for word, i in word_index.items():\n        if i >= max_features: continue\n        embedding_vector = embeddings_index.get(word)\n        if embedding_vector is not None: embedding_matrix[i] = embedding_vector\n\n    return embedding_matrix\n\ndef load_para(word_index):\n    EMBEDDING_FILE = '../input/embeddings/paragram_300_sl999/paragram_300_sl999.txt'\n    def get_coefs(word,*arr): return word, np.asarray(arr, dtype='float32')\n    embeddings_index = dict(get_coefs(*o.split(\" \")) for o in open(EMBEDDING_FILE,encoding=\"utf8\", errors='ignore') if len(o)>100)\n\n    all_embs = np.stack(embeddings_index.values())\n    emb_mean,emb_std = all_embs.mean(), all_embs.std()\n    embed_size = all_embs.shape[1]\n\n    # word_index = tokenizer.word_index\n    nb_words = min(max_features, len(word_index))\n    embedding_matrix = np.random.normal(emb_mean, emb_std, (nb_words, embed_size))\n    for word, i in word_index.items():\n        if i >= max_features: continue\n        embedding_vector = embeddings_index.get(word)\n        if embedding_vector is not None: embedding_matrix[i] = embedding_vector\n    \n    return embedding_matrix","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"961d3ac3895ae8e9094114177ab5fe84d981f6c9"},"cell_type":"code","source":"start_time = time.time()\ntest_X,train_left_X,val_left_X,train_right_X,val_right_X,train_left_Y,val_left_Y,train_right_Y,val_right_Y,train_Y,val_Y,word_index,single_incincere,single_sincere = load_and_prec()\nembedding_matrix_1 = load_glove(word_index)\n# embedding_matrix_2 = load_fasttext(word_index)\nembedding_matrix_3 = load_para(word_index)\nprint(timedelta(seconds=time.time() - start_time))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"e30c34041e10f4a0e793fce7bbeb27fe3ccbe871"},"cell_type":"code","source":"import gc\n\nembedding_matrix = np.mean([embedding_matrix_1, embedding_matrix_3], axis = 0)\nnp.shape(embedding_matrix)\ndel(embedding_matrix_1)\ndel(embedding_matrix_3)\ngc.collect()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"08fc7052bb08075b81e64ddbd0394e3bee92f0a4"},"cell_type":"code","source":"# https://www.kaggle.com/suicaokhoailang/lstm-attention-baseline-0-652-lb\n\nclass Attention(Layer):\n    def __init__(self, step_dim,\n                 W_regularizer=None, b_regularizer=None,\n                 W_constraint=None, b_constraint=None,\n                 bias=True, **kwargs):\n        self.supports_masking = True\n        self.init = initializers.get('glorot_uniform')\n\n        self.W_regularizer = regularizers.get(W_regularizer)\n        self.b_regularizer = regularizers.get(b_regularizer)\n\n        self.W_constraint = constraints.get(W_constraint)\n        self.b_constraint = constraints.get(b_constraint)\n\n        self.bias = bias\n        self.step_dim = step_dim\n        self.features_dim = 0\n        super(Attention, self).__init__(**kwargs)\n\n    def build(self, input_shape):\n        assert len(input_shape) == 3\n        self.W = self.add_weight((input_shape[-1],),\n                                 initializer=self.init,\n                                 name='{}_W'.format(self.name))\n        self.features_dim = input_shape[-1]\n\n        if self.bias:\n            self.b = self.add_weight((self.step_dim,),\n                                     initializer=initializers.get('zero'),\n                                     name='{}_W'.format(self.name))\n        else:\n            self.b = None\n\n        self.built = True\n\n    def compute_mask(self, input, input_mask=None):\n        return None\n\n    def call(self, x, mask=None):\n        features_dim = self.features_dim\n        step_dim = self.step_dim\n\n        eij = K.reshape(K.dot(K.reshape(x, (-1, features_dim)),\n                        K.reshape(self.W, (features_dim, 1))), (-1, step_dim))\n\n        if self.bias:\n            eij += self.b\n\n        eij = K.tanh(eij)\n\n        a = K.exp(eij)\n\n        if mask is not None:\n            a *= K.cast(mask, K.floatx())\n\n        a /= K.cast(K.sum(a, axis=1, keepdims=True) + K.epsilon(), K.floatx())\n\n        a = K.expand_dims(a)\n        weighted_input = x * a\n        return K.sum(weighted_input, axis=1)\n\n    def compute_output_shape(self, input_shape):\n        return input_shape[0],  self.features_dim","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"5b7cbe995ab018da5206f8e1e8d9aec342362609"},"cell_type":"code","source":"# https://www.kaggle.com/hireme/fun-api-keras-f1-metric-cyclical-learning-rate/code\n\nclass CyclicLR(Callback):\n    \"\"\"This callback implements a cyclical learning rate policy (CLR).\n    The method cycles the learning rate between two boundaries with\n    some constant frequency, as detailed in this paper (https://arxiv.org/abs/1506.01186).\n    The amplitude of the cycle can be scaled on a per-iteration or \n    per-cycle basis.\n    This class has three built-in policies, as put forth in the paper.\n    \"triangular\":\n        A basic triangular cycle w/ no amplitude scaling.\n    \"triangular2\":\n        A basic triangular cycle that scales initial amplitude by half each cycle.\n    \"exp_range\":\n        A cycle that scales initial amplitude by gamma**(cycle iterations) at each \n        cycle iteration.\n    For more detail, please see paper.\n    \n    # Example\n        ```python\n            clr = CyclicLR(base_lr=0.001, max_lr=0.006,\n                                step_size=2000., mode='triangular')\n            model.fit(X_train, Y_train, callbacks=[clr])\n        ```\n    \n    Class also supports custom scaling functions:\n        ```python\n            clr_fn = lambda x: 0.5*(1+np.sin(x*np.pi/2.))\n            clr = CyclicLR(base_lr=0.001, max_lr=0.006,\n                                step_size=2000., scale_fn=clr_fn,\n                                scale_mode='cycle')\n            model.fit(X_train, Y_train, callbacks=[clr])\n        ```    \n    # Arguments\n        base_lr: initial learning rate which is the\n            lower boundary in the cycle.\n        max_lr: upper boundary in the cycle. Functionally,\n            it defines the cycle amplitude (max_lr - base_lr).\n            The lr at any cycle is the sum of base_lr\n            and some scaling of the amplitude; therefore \n            max_lr may not actually be reached depending on\n            scaling function.\n        step_size: number of training iterations per\n            half cycle. Authors suggest setting step_size\n            2-8 x training iterations in epoch.\n        mode: one of {triangular, triangular2, exp_range}.\n            Default 'triangular'.\n            Values correspond to policies detailed above.\n            If scale_fn is not None, this argument is ignored.\n        gamma: constant in 'exp_range' scaling function:\n            gamma**(cycle iterations)\n        scale_fn: Custom scaling policy defined by a single\n            argument lambda function, where \n            0 <= scale_fn(x) <= 1 for all x >= 0.\n            mode paramater is ignored \n        scale_mode: {'cycle', 'iterations'}.\n            Defines whether scale_fn is evaluated on \n            cycle number or cycle iterations (training\n            iterations since start of cycle). Default is 'cycle'.\n    \"\"\"\n\n    def __init__(self, base_lr=0.001, max_lr=0.006, step_size=2000., mode='triangular',\n                 gamma=1., scale_fn=None, scale_mode='cycle'):\n        super(CyclicLR, self).__init__()\n\n        self.base_lr = base_lr\n        self.max_lr = max_lr\n        self.step_size = step_size\n        self.mode = mode\n        self.gamma = gamma\n        if scale_fn == None:\n            if self.mode == 'triangular':\n                self.scale_fn = lambda x: 1.\n                self.scale_mode = 'cycle'\n            elif self.mode == 'triangular2':\n                self.scale_fn = lambda x: 1/(2.**(x-1))\n                self.scale_mode = 'cycle'\n            elif self.mode == 'exp_range':\n                self.scale_fn = lambda x: gamma**(x)\n                self.scale_mode = 'iterations'\n        else:\n            self.scale_fn = scale_fn\n            self.scale_mode = scale_mode\n        self.clr_iterations = 0.\n        self.trn_iterations = 0.\n        self.history = {}\n\n        self._reset()\n\n    def _reset(self, new_base_lr=None, new_max_lr=None,\n               new_step_size=None):\n        \"\"\"Resets cycle iterations.\n        Optional boundary/step size adjustment.\n        \"\"\"\n        if new_base_lr != None:\n            self.base_lr = new_base_lr\n        if new_max_lr != None:\n            self.max_lr = new_max_lr\n        if new_step_size != None:\n            self.step_size = new_step_size\n        self.clr_iterations = 0.\n        \n    def clr(self):\n        cycle = np.floor(1+self.clr_iterations/(2*self.step_size))\n        x = np.abs(self.clr_iterations/self.step_size - 2*cycle + 1)\n        if self.scale_mode == 'cycle':\n            return self.base_lr + (self.max_lr-self.base_lr)*np.maximum(0, (1-x))*self.scale_fn(cycle)\n        else:\n            return self.base_lr + (self.max_lr-self.base_lr)*np.maximum(0, (1-x))*self.scale_fn(self.clr_iterations)\n        \n    def on_train_begin(self, logs={}):\n        logs = logs or {}\n\n        if self.clr_iterations == 0:\n            K.set_value(self.model.optimizer.lr, self.base_lr)\n        else:\n            K.set_value(self.model.optimizer.lr, self.clr())        \n            \n    def on_batch_end(self, epoch, logs=None):\n        \n        logs = logs or {}\n        self.trn_iterations += 1\n        self.clr_iterations += 1\n\n        self.history.setdefault('lr', []).append(K.get_value(self.model.optimizer.lr))\n        self.history.setdefault('iterations', []).append(self.trn_iterations)\n\n        for k, v in logs.items():\n            self.history.setdefault(k, []).append(v)\n        \n        K.set_value(self.model.optimizer.lr, self.clr())\n    \n\ndef f1(y_true, y_pred):\n    '''\n    metric from here \n    https://stackoverflow.com/questions/43547402/how-to-calculate-f1-macro-in-keras\n    '''\n    def recall(y_true, y_pred):\n        \"\"\"Recall metric.\n\n        Only computes a batch-wise average of recall.\n\n        Computes the recall, a metric for multi-label classification of\n        how many relevant items are selected.\n        \"\"\"\n        true_positives = K.sum(K.round(K.clip(y_true * y_pred, 0, 1)))\n        possible_positives = K.sum(K.round(K.clip(y_true, 0, 1)))\n        recall = true_positives / (possible_positives + K.epsilon())\n        return recall\n\n    def precision(y_true, y_pred):\n        \"\"\"Precision metric.\n\n        Only computes a batch-wise average of precision.\n\n        Computes the precision, a metric for multi-label classification of\n        how many selected items are relevant.\n        \"\"\"\n        true_positives = K.sum(K.round(K.clip(y_true * y_pred, 0, 1)))\n        predicted_positives = K.sum(K.round(K.clip(y_pred, 0, 1)))\n        precision = true_positives / (predicted_positives + K.epsilon())\n        return precision\n    precision = precision(y_true, y_pred)\n    recall = recall(y_true, y_pred)\n    return 2*((precision*recall)/(precision+recall+K.epsilon()))\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"e8f115476cad6b10070d405f9114df55bf037178"},"cell_type":"code","source":"def create_skip_model():\n    inp = Input(shape=(maxlen,))\n    x = Embedding(max_features, embed_size, weights=[embedding_matrix], trainable=False)(inp)\n    x = SpatialDropout1D(0.1)(x)\n    x = Bidirectional(CuDNNLSTM(128, return_sequences=True))(x)\n    y = Bidirectional(CuDNNGRU(128, return_sequences=True))(x)\n    \n    atten_1 = Attention(maxlen)(x) # skip connect\n    atten_2 = Attention(maxlen)(y)\n    avg_pool = GlobalAveragePooling1D()(y)\n    max_pool = GlobalMaxPooling1D()(y) \n    conc = concatenate([atten_1, atten_2, avg_pool, max_pool])\n    conc = Dropout(0.1)(conc)\n    outp = Dense(128, activation=\"sigmoid\")(conc)\n    return Model(inputs=inp, outputs=outp,name=\"skip_model\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"4ef89251a920781b721324dbc5e6614d62bc688c"},"cell_type":"code","source":"from keras import initializers, regularizers, constraints, optimizers\n\ndef model_lstm_atten(embedding_matrix):\n    \n    left_inp = Input(shape=(maxlen,),name=\"left_input\")\n    right_inp = Input(shape=(maxlen,),name=\"right_input\")\n    inp = Input(shape=(maxlen,))\n    rnn = create_skip_model()\n    \n    left_rnn = rnn(left_inp)\n    right_rnn = rnn(right_inp)\n\n    both = dot([left_rnn,right_rnn],axes=-1,normalize=True) # https://stackoverflow.com/a/52021481\n    prediction_similarity = Dense(1,activation='sigmoid',name=\"similarity_classification\")(both)\n    siamese_net = Model(input=[left_inp,right_inp],output=[prediction_similarity])#,left_prediction,right_prediction])\n    siamese_net.compile(loss='binary_crossentropy', optimizer='adam', metrics=[f1,'accuracy'])\n    return siamese_net","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"2f18a844d4a140f3933672e1b85d1d02473a0164"},"cell_type":"code","source":"model = model_lstm_atten(embedding_matrix)\nmodel.summary()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"7229e0a20bff904f20622b8364e7af47ad89473a"},"cell_type":"code","source":"reduce_err = EarlyStopping(monitor=\"val_loss\",\n                              min_delta=0,\n                              patience=1,\n                              verbose=0, mode='auto')\n\nmodel.fit([train_left_X,train_right_X],train_Y,batch_size=512, epochs=10, validation_data=([val_left_X,val_right_X],val_Y),callbacks=[reduce_err])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"aa605a8266b3813aa8630f9c5fb41acaa38cb25f"},"cell_type":"code","source":"## Extracting siamese embeddings\n\nsiamese_embedding = Model(inputs=model.get_layer(\"skip_model\").get_input_at(0),output=model.get_layer(\"skip_model\").get_output_at(0))\nsiamese_embedding.summary()\nstart_time = time.time()\n# Using Kmeans for clustering the embeddings and prediction of test set.\ntrain_left_X = np.unique(train_left_X,axis=0)\nencoded_embeddings_train_left= siamese_embedding.predict(train_left_X,batch_size=512)\nprint(timedelta(seconds=time.time() - start_time))\nstart_time = time.time()\nencoded_embeddings_val_left = siamese_embedding.predict(val_left_X,batch_size=512)\nprint(timedelta(seconds=time.time() - start_time))\nencoded_embeddings_test = siamese_embedding.predict(test_X,batch_size=512)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"52380256235e6a7c7231697a27e9c4c8f7979ea4"},"cell_type":"code","source":"\nstart_time = time.time()\nfrom sklearn.cluster import KMeans\nkmeans = KMeans(n_clusters=2, random_state=0,n_jobs=-1,max_iter=5000).fit(encoded_embeddings_train_left)\nprint(timedelta(seconds=time.time() - start_time))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"846e4bebcf053f8026b55d95b02abab211ce8d7b"},"cell_type":"code","source":"# metrics.f1_score(kmeans.predict(encoded_embeddings_val_left),val_left_Y)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"afee2d805c726d6d2f99a7081f70571309db3a32"},"cell_type":"code","source":"kmeans_predictions = kmeans.predict(encoded_embeddings_test)\nprint(kmeans_predictions[:300])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"c9d7eb75c2fb48f0ecba5c4bd373a528a47ad978"},"cell_type":"code","source":"sub = pd.read_csv('../input/sample_submission.csv')\nsub.prediction = kmeans_predictions\nsub.to_csv(\"submission.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}