# ---
# jupyter:
#   jupytext:
#     text_representation:
#       extension: .py
#       format_name: light
#       format_version: '1.5'
#       jupytext_version: 1.14.4
#   kernelspec:
#     display_name: Python [conda root]
#     language: python
#     name: conda-root-py
# ---

# # ----------------------------------------------Classification------------------------------------------

# Import the library needed

# +
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np

from keras.layers import Dense, Dropout
from keras.layers import LSTM
from keras.models import Sequential
from keras.optimizers import Adam
from keras.models import model_from_json
from keras.wrappers.scikit_learn import KerasRegressor
from sklearn.model_selection import GridSearchCV
from urad_Preprocessing import get_data_preprocessing
np.random.seed(5) #set pseudo checkpoint


# -

# # Function

# There are several functions in it:
# - convert_cpc : to convert static data into time series data
# - divide_data : to divide the data into training and test data
# - Neural network lists: contains the models of 3 neural network methods

# Convert the CPC (for univariate) or multiple features (for multivariate) into time series data

def convert_cpc(dataset, time_prediction=7):
    # look back default value is in day, it is equal to one week
    dataX, dataY = [], []
    for i in range(len(dataset)-time_prediction-1):
        a = dataset[i:(i+time_prediction), 0]
        dataX.append(a)
        dataY.append(dataset[i + time_prediction, 0])
    return np.array(dataX), np.array(dataY)


# Divide the data into training, and test data

def divide_data(dataset, time_prediction):
    len_dataset = len(dataset)
    datasetX, datasetY= convert_cpc(dataset, time_prediction)

    #prepare the dataset, and divide them into training and testing dataset
    train_size = int(len_dataset * 0.8)
    test_size = len_dataset - train_size
    trainX, trainY, testX, testY =  datasetX[:train_size,:], datasetY[:train_size], \
                                    datasetX[train_size:,:], datasetY[train_size:]
    return trainX, trainY, testX, testY


# Neural network / Deep learning Lists

def build_ANN(learning_rate, hidden_layer, time_prediction):
    print ("---------------Get into ANN function")
    forecast_model = Sequential()
    forecast_model.add(Dense(units = time_prediction*(2**(hidden_layer+1)), kernel_initializer = "uniform", 
                             activation = "relu", input_dim = time_prediction))
    forecast_model.add(Dropout(0.5))
    for i in range(hidden_layer - 1):
        forecast_model.add(Dense(units = time_prediction*(2**(hidden_layer-i)), 
                                 kernel_initializer = "uniform",
                                 activation = "relu"))
        forecast_model.add(Dropout(0.5))
    forecast_model.add(Dense(units = 1, kernel_initializer = "uniform", activation='sigmoid'))
    adam = Adam(decay = learning_rate*0.05, lr=learning_rate)
    forecast_model.compile(loss="binary_crossentropy", optimizer = adam, metrics=['accuracy'])
    return forecast_model


def build_LSTM(learning_rate, hidden_layer, time_prediction):
    print ("\n---------------Get into LSTM function--------------------")  
    print ("Learning rate and hidden layer: ", learning_rate, hidden_layer)
    lstm_model = Sequential()
    #print (learning_rate, hidden_layer, time_prediction)
    lstm_model.add( LSTM(time_prediction*(2**(hidden_layer+1)),
                         input_shape = (1, time_prediction), return_sequences = True)) 
    lstm_model.add(Dropout(0.5))
    for i in range(hidden_layer - 2):
        lstm_model.add( LSTM(    time_prediction*(2**(hidden_layer-i)), 
                                 return_sequences = True ) )
        lstm_model.add(Dropout(0.5))
    
    lstm_model.add( LSTM(time_prediction) )
    lstm_model.add(Dropout(0.5))
    lstm_model.add(Dense(units = 1, activation='sigmoid'))
    adam = Adam(decay = learning_rate*0.05, lr=learning_rate)
    lstm_model.compile(loss='binary_crossentropy', optimizer = adam, metrics=['accuracy'])
    return lstm_model


def build_model(learning_rate, method = "ANN", hidden_layer = 1, time_prediction=7):
    model = None
    if method =="ANN":
        model = build_ANN(learning_rate, hidden_layer, time_prediction)
    elif method == "LSTM":
        model = build_LSTM(learning_rate, hidden_layer, time_prediction)
    return model


# # Fine Tuning

# +
time_prediction = 10
get_data_preprocessing() #preprocess the raw data and get the important features
dataset = pd.read_csv("fb_ads_preprocess.csv", header=None)
print (dataset.iloc[:5,:])

dataset = dataset.iloc[:, -1].values.reshape(-1, 1)
#print (dataset.shape, dataset[0:5])
trainX, trainY, testX, testY = divide_data(dataset, time_prediction)

#run this code for LSTM
#trainX = np.reshape(trainX, (trainX.shape[0], 1, trainX.shape[1]))
#testX = np.reshape(testX, (testX.shape[0], 1, testX.shape[1]))
#print (trainX.shape, trainY.shape, testX.shape, testY.shape)

# +
#------------fine tuning ANN using gridSearch
model = KerasRegressor(build_fn=build_model)
parameters = {  'batch_size' : [512, 256, 128], 
                'nb_epoch' : [50000, 10000, 1000],
                'learning_rate': [0.00001, 0.0001, 0.001],
                'hidden_layer' : [3, 2, 1],
                #'optimizer' : ['adagrad', 'rmsprop', 'adam'],
                'time_prediction' :[time_prediction]}

fine_tune = GridSearchCV(estimator=model, param_grid=parameters, cv = 5)
fine_tune_result = fine_tune.fit(trainX, trainY)
best_params = fine_tune_result.best_params_
best_accuracy = fine_tune_result.best_score_
# -

# print the loss and the best parameters

means = fine_tune_result.cv_results_['mean_test_score']
stds = fine_tune_result.cv_results_['std_test_score']
params = fine_tune_result.cv_results_['params']
for mean, stdev, param in zip(means, stds, params):
    print("loss: %f std: (%f) with: %r" % (mean, stdev, param))

#using the training data that will be divided again into 80% train, and 20% validation
lstm_model = build_ANN(0.01, 1, time_prediction)
batch_size = 256
history = lstm_model.fit(trainX, trainY, epochs=100, batch_size=batch_size, validation_split=0.2, shuffle=True)

StarcoderdataJupyterScriptsDedupFiltered