{"cells":[{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"#Cargo librerias generales\nimport gc\nimport numpy as np \nimport pandas as pd\nfrom sklearn.model_selection import KFold\nimport matplotlib.pyplot as plt\n\n#Modulos de Keras/TF\nfrom keras.models import Sequential, Model\nfrom keras.layers import (Conv1D, AveragePooling1D, Reshape, Dense, Dropout, Embedding, Input,  Flatten, Concatenate, Multiply, RepeatVector, Permute)\nfrom keras.preprocessing.sequence import pad_sequences\nfrom keras.callbacks import EarlyStopping\nfrom keras.optimizers import Adam\nfrom sklearn.metrics import roc_auc_score\nimport tensorflow as tf\nfrom keras import backend as k\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import os\nprint(os.listdir(\"../input/banco-galicia-dataton-2019\"))\nprint(os.listdir(\"../input/galicia-nn-precalculado\"))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"recalculate_datasets = False\n#Define la cantidad de eventos maximos a considerar\nhistory = 1500\n\n\n# Genero algunos features temporales\ndef preproc(pv):    \n    pv[\"DIA_SEM\"]=pv[\"FEC_EVENT\"].dt.dayofweek\n    pv[\"DIA_ANIO\"]=pv[\"FEC_EVENT\"].dt.dayofyear\n    pv[\"HORA\"]=pv[\"FEC_EVENT\"].dt.hour\n    pv[\"mes\"]=pv[\"FEC_EVENT\"].dt.month\n    pv[\"OFFSET\"] = (pv[\"FEC_EVENT\"] - pd.to_datetime('01-01-2019')).dt.days\n    pv[\"OFFSET_MES\"] = [int(a) for a in -pv[\"OFFSET\"]/24]\n    return(pv)\n\n\nif recalculate_datasets:\n    #Leo eventos de navegacion en sitio del banco\n    data = preproc(pd.concat([\n           pd.read_csv(\"./datasets/pageviews.zip\" ,parse_dates=[\"FEC_EVENT\"]),\n           pd.read_csv(\"./datasets/pageviews_complemento.zip\", parse_dates=[\"FEC_EVENT\"])\n    ]))\n\n\n    #Leo las conversiones\n    y_prev = pd.read_csv(\"./datasets/conversiones.zip\")\n\n    #Defino una secuencia del largo maximo nula. Es para que pad_sequences no genere un tensor de menor dimension\n    #Si en el intervalo tratado no hay ningun caso que llegue al tamaño maximo\n    dummyseq = pd.Series([[0 for i in range(history)],[]])[0:1]\n\n    #Genero todos los intervalos desde [1] para 2 hasta [1,2...11] para 12 mas set de predicción [1...12] \n    for mes_corte in range(2,14):\n\n        print(\"Genero dataset 1 a \", mes_corte - 1, \" con objetivo \", mes_corte)\n\n        #determino el offset en días para el intervalo que se esta generando. Este offset se usa para ajustar \n        #la cantidad de dias previas al mes objetivo para los eventos tratados\n        offset_dias_corte = (13 - mes_corte) * 30\n\n        #Se genera un dataset vacío para todos los clientes de los que hay historia\n        y_train = pd.Series(0, index=sorted(data[data['mes']<mes_corte].USER_ID.unique()))\n\n        #De los que tengo info de movimientos determino los que se convirtieron\n        convertidos = np.intersect1d(y_prev[y_prev.mes == mes_corte].USER_ID.unique(),data[data['mes']<mes_corte].USER_ID.unique())        \n\n        #LEs pongo objetivo positivo a los convertidos\n        y_train.loc[convertidos] = 1\n\n        #inicializo para la primera feature\n        train = None\n\n        #Genero un dataset con todos los features en secuencia. Cada uno se trata en un embedding separado\n        for field in [\"PAGE\",\"CONTENT_CATEGORY\", \"CONTENT_CATEGORY_TOP\", \"CONTENT_CATEGORY_BOTTOM\", \"SITE_ID\", \"ON_SITE_SEARCH_TERM\", \"DIA_SEM\", \"HORA\"]:\n\n            trainfd = data[data['mes']<mes_corte].groupby(\"USER_ID\").apply(lambda x: x[(x.FEC_EVENT.dt.month < mes_corte)]\\\n                                              .sort_values(\"FEC_EVENT\")[field][-history:].values)\n\n            #Agrego una secuencia dummy del largo maximo para que no quede una matriz menor\n            trainfd = pd.concat([dummyseq,trainfd])\n            trainfd = pad_sequences(trainfd)[:,:,np.newaxis]\n            trainfd = trainfd[1:]\n\n            if train is None:\n                train = np.copy(trainfd)    \n            else:\n                train =  np.concatenate((train,trainfd),axis=2)\n\n\n        #Determino adicionalmente la cantidad de dias hasta el corte del intervalo\n        train_day_event  = data[data['mes']<mes_corte].groupby(\"USER_ID\").apply(lambda x: x[(x.FEC_EVENT.dt.month < mes_corte)]\\\n                                              .sort_values(\"FEC_EVENT\")[\"OFFSET\"][-history:].values)\n\n        train_day_event = (train_day_event + offset_dias_corte)\n\n        train_day_event = pad_sequences(train_day_event,maxlen =history)[:,:,np.newaxis]\n\n        train =  np.concatenate((train,train_day_event),axis=2)\n\n        #Primer intervalo inicializo las matrices de train\n        if mes_corte==2:\n            prev_X = np.copy(train)\n            prev_y = np.copy(y_train)\n\n        #El ultimo intervalo es para predecir solamente    \n        elif mes_corte == 13:\n            y_test = np.copy(y_train)\n            test = np.copy(train)\n            print(\"test\" ,len(test), len(y_test))               \n\n        #Siguientes intervalos agrego a la matriz existente \n        else:\n            prev_X = np.concatenate((train,prev_X ), axis = 0)\n            prev_y = np.concatenate((y_train,prev_y ), axis = 0)        \n\n    train = prev_X\n    y_train = prev_y\n\n    np.savez_compressed(\"../input/galicia-nn-precalculado/NNv11precomputed1500.npz\", train=train, y_train = y_train, test = test, y_test=y_test)  ","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"data = np.load(\"../input/galicia-nn-precalculado/NNv11precomputed1500.npz\")\n\ncrop_hist = 500\n\ntrain = data[\"train\"][:,-crop_hist:,:]\ny_train = data[\"y_train\"]\ntest = data[\"test\"][:,-crop_hist:,:]\ny_test = data[\"y_test\"]\n\ndel data\ngc.collect()\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def failsafe_auc(y_true, y_pred):\n    if y_true.sum() == 0:\n        return(0.5)\n    else:\n        return(roc_auc_score(y_true,y_pred))\n    \n\ndef auroc(y_true, y_pred):\n    return tf.py_func(failsafe_auc, (y_true, y_pred), tf.double)\n\ndef getModel():\n    emb_depth = [64,16,16,16,8,32,32,8]\n    input_layers = []\n    embedded_lay = []\n    for emb_inp in range(train.shape[2]-1):  ##### UNA NO EMBEDDING\n        input_layers.append(Input(shape=(train.shape[1],), dtype='int32'))\n        embedded_lay.append(Embedding(input_dim=(train[:,:,emb_inp].max() + 1),output_dim=emb_depth[emb_inp], \n                                  input_length=train.shape[1])(input_layers[-1]))\n        \n\n    \n    #embedded_lay.append(Reshape((train.shape[1],1))(input_layers[-1]))\n    conc_emb = Concatenate()(embedded_lay)\n    \n    #input_layer = Input(shape=(train.shape[1],), dtype='int32')\n    input_layers.append(Input(shape=(train.shape[1],), dtype='float32'))\n    \n    h_factors = RepeatVector(np.sum(emb_depth))(input_layers[-1])\n    #print(h_factors.shape)\n    h_factors = Permute((2, 1))(h_factors)\n    #print(h_factors.shape)    \n    #h_factors = k.repeat_elements(h_factors,np.sum(emb_depth),2)\n    #print(h_factors.shape,conc_emb.shape )\n    \n    conc_emb = Multiply()([h_factors,conc_emb])\n    \n    #print(conc_emb.shape)\n    x = Conv1D(64, 1, activation=\"relu\")(conc_emb)\n    x = AveragePooling1D(5)(x)\n    x = Dropout(0.3)(x)\n    x = Conv1D(32, 1, activation=\"relu\")(x)\n    x = AveragePooling1D(5)(x)\n    x = Dropout(0.1)(x)    \n    x = Conv1D(4, 1, activation=\"relu\")(x)\n    x = AveragePooling1D(5)(x)\n    x = Dropout(0.05)(x)    \n    x = Flatten()(x)\n    x = Dense(128, activation=\"relu\")(x)\n    x = Dropout(0.05)(x)\n    x = Dense(64, activation=\"relu\")(x)\n    x = Dropout(0.05)(x)    \n    x = Dense(16, activation=\"relu\")(x)\n    x = Dropout(0.05)(x)    \n    x = Dense(4, activation=\"relu\")(x)\n    out_layer = Dense(1, activation=\"sigmoid\")(x)\n\n    #print(input_layers)\n    \n    model = Model(inputs=input_layers, outputs=out_layer)\n\n    return(model)\n    ","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"\n\ndef input_list(tensor, p_f_factor):\n    lst = []\n    for emb_in in range(tensor.shape[2]-1):\n        lst.append(tensor[:,:,emb_in])\n        \n    lst.append(np.exp(tensor[:,:,-1]/p_f_factor).astype(float))\n    return(lst)\n\nlr, ffactor = 0.0017, 157\n\n\n\ntest_probs = []\ni = 0\n\nloss_log = []\n\nfor train_idx, valid_idx in KFold(n_splits=5, shuffle=True, random_state=42 ).split(train):\n\n    Xt = train[train_idx]\n    yt = y_train[train_idx]\n\n    Xv = train[valid_idx]\n    yv = y_train[valid_idx]\n\n    loss_log_best = 0\n\n    for iter in range(3):\n        model = getModel()\n        model.compile(loss='binary_crossentropy', optimizer=Adam(lr=lr), metrics=[auroc])\n        #model.summary()\n\n        model.fit(input_list(Xt,ffactor), yt, validation_data= (input_list(Xv,ffactor),yv), batch_size=265, epochs=1, verbose=1,\n                    shuffle=True, class_weight={0:1, 1:86})\n\n        if roc_auc_score(yv,model.predict(input_list(Xv,ffactor))[:, -1]) > 0.6:\n\n            model.fit(input_list(Xt,ffactor), yt, validation_data= (input_list(Xv,ffactor),yv), batch_size=265, epochs=150, verbose=1,\n                        shuffle=True, class_weight={0:1, 1:86},\n                        callbacks=[EarlyStopping(monitor='val_auroc', patience=5, verbose=1,\n                                                 mode=\"max\", restore_best_weights=True)])\n\n        loss_log_current = roc_auc_score(yv,model.predict(input_list(Xv,ffactor))[:, -1])\n\n        if loss_log_current > loss_log_best:\n            loss_log_best = loss_log_current\n            test_probs_best = pd.Series(model.predict(input_list(test,ffactor))[:, -1], name=\"fold_\" + str(i))\n\n\n    loss_log.append(loss_log_best)\n    test_probs.append(test_probs_best)\n\n    i+=1\n\ntest_probs = pd.concat(test_probs, axis=1).mean(axis=1)\ntest_probs.index.name=\"USER_ID\"\ntest_probs.name=\"SCORE\"\n\ntest_probs.to_csv(\"./Final-NN-v11.csv\", header=True)\n\nprint(\"Loss: \", np.mean(loss_log) )        \n\n","execution_count":null,"outputs":[]}],"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":4,"nbformat_minor":1}