<jupyter_start><jupyter_text>Data Analysis on e2e dataset  Introduction  Load essential libraries<jupyter_code># import libraries
import nltk
nltk.download('stopwords')
import sys
sys.path.append("..") # Adds higher directory to python modules path.
import warnings
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from utils import *
from sklearn.model_selection import train_test_split
from sklearn.decomposition import TruncatedSVD
from sklearn.pipeline import make_pipeline
from sklearn.feature_extraction.text import TfidfVectorizer
from numpy.linalg import norm
from models.w2vec_model import *
from xgboost import XGBClassifier
from sklearn.model_selection import train_test_split
from xgboost import plot_importance
#from sklearn.metrics import accuracy_score
from sklearn.metrics import f1_score
from pandas.tools.plotting import scatter_matrix

seed = 42

warnings.filterwarnings('ignore')<jupyter_output><empty_output><jupyter_text>Quick look at the data<jupyter_code>import pandas as pd
data_train = pd.read_csv("../data/trainset.csv")
fake_train = pd.read_csv('../data/fake_data_train.csv')
#label fake data with 1 and real data with 0 then merge the datasets.
data_train['label'] = '0'
fake_train['label'] = '1'
frames = [data_train, fake_train]
dfs = pd.concat(frames)
#drop Unnamed column
dfs = dfs.drop(dfs.columns[0], axis=1)
#shuffle rows

data = dfs.sample(frac=1).reset_index(drop=True)
data
data.info()<jupyter_output><class 'pandas.core.frame.DataFrame'>
RangeIndex: 85129 entries, 0 to 85128
Data columns (total 3 columns):
label    85129 non-null object
mr       85129 non-null object
ref      85129 non-null object
dtypes: object(3)
memory usage: 1.9+ MB<jupyter_text>Text Analysis In this section we will perform text analysis on our data. The text analysis will involve :    Removing all non-alphanumeric characters<jupyter_code>import numpy as np
import keras.backend as K
import string
from nltk.corpus import stopwords
import re
from numpy.linalg import norm
import csv

#stop_words = stopwords.words('english')


def reshape_data(sentence_1_data, sentence_2_data, y_data):
    '''
    Reshapes the input data.
    :param sentence_1_data: sentence 1
    :param sentence_2_data: sentence 2
    :param y_data: output
    :return: a reshaped sentence 1, sentence 2, output
    '''
    # sentence 1
    sentence_1 = np.array(sentence_1_data)
    sentence_1 = sentence_1.reshape(sentence_1.shape +  (1, ))
    
    # sentence 2
    sentence_2 = np.array(sentence_2_data)
    sentence_2 = sentence_2.reshape(sentence_2.shape + (1,))
    
    # y
    y = np.array(y_data)
    y  = y.reshape((y.shape[0], 1))
    
    return sentence_1, sentence_2, y



def clean_str(txt):
    '''
    Cleans a text by removing all non alphanumeric characters.
    :param txt: String, the input sentence.
    :return: A cleaned sentence.
    '''
    txt = str(txt)
    txt = re.sub(r"[^A-Za-z0-9(),!?\'\`&%]", " ", txt)
    txt = re.sub(r"\'s", " \'s", txt)
    txt = re.sub(r"\'ve", " \'ve", txt)
    txt = re.sub(r"\'t", " n\'t", txt)
    txt = re.sub(r"\'re", " \'re", txt)
    txt = re.sub(r"\'d", " \'d", txt)
    txt = re.sub(r"\'ll", " \'ll", txt)
    txt = re.sub(r",", " , ", txt)
    txt = re.sub(r"!", " ! ", txt)
    txt = re.sub(r"\(", " ( ", txt)
    txt = re.sub(r"\)", " ) ", txt)
    txt = re.sub(r"\?", " ? ", txt)
    txt = re.sub(r"\&", " & ", txt)
    txt = re.sub(r"\%", " percent ", txt)
    txt = txt.strip().lower()
    return txt

def remove_punctuation(txt):
    '''
    Removes punctuations in a given sentence.
    :param txt: String, input sentence.
    :return: a list of words from the input sentence without punctuation.
    '''
    return [w for w in txt if w not in string.punctuation]


def remove_stopwords(txt):
    '''
    Removes english stopword in a given sentence.
    :param txt: String, the input sentence.
    :return: A list of words from the input sentence without any stopwords.
    '''
    return [w for w in txt if w not in stop_words]



def fbeta(y_true, y_pred, threshold_shift=0, beta=1):
    '''
    Compute fbeta score.
    :param y_true: y_true
    :param y_pred: predicted value
    :param threshold_shift: threshold
    :param beta: beta value
    :return: fbeta score
    '''
    
    # just in case of hipster activation at the final layer
    y_pred = K.clip(y_pred, 0, 1)
    y_true = K.clip(y_true, 0, 1)
    
    # shifting the prediction threshold from .5 if needed
    y_pred_bin = K.round(y_pred + threshold_shift)
    
    tp = K.sum(K.round(y_true * y_pred_bin)) + K.epsilon()
    fp = K.sum(K.round(K.clip(y_pred_bin - y_true, 0, 1)))
    fn = K.sum(K.round(K.clip(y_true - y_pred, 0, 1)))
    
    precision = tp / (tp + fp)
    recall = tp / (tp + fn)
    
    beta_squared = beta ** 2
    return (beta_squared + 1) * (precision * recall) / (beta_squared * precision + recall + K.epsilon())




def cosine_sim(u, v):
    '''
    Computes the cosine similarity between two vectors u and v.
    :param u: Numpy ndarray, the vector u.
    :param v: Numpy ndarray, the vector v.
    :return: Float between 0 and 1, the cosine similarity score between the vector u and v.
    '''
    return np.dot(u,v) / (norm(u)*norm(v))


def similarity(q1, q2):
    '''
    Computes the similarity score between lists of vectors.
    :param q1: List of the first vector.
    :param q2: List of the second vector.
    :return: A list of similarity score between vectors in q1 and q2.
    '''
    sim = []
    for el1, el2 in zip(q1, q2):
        sim.append(cosine_sim(el1, el2))Saving the results 

    return sim

def save_result(filename, results):
    '''
    Saves a dictionary of data into a file.
    :param filename: String, the filename
    :param results: The dictionary to save into the file
    '''
    keys = results[0].keys()
    with open(filename, 'w') as f:
        dict_writer = csv.DictWriter(f,  keys)
        dict_writer.writeheader()
        dict_writer.writerows(results)

def make_accuracy(threshold_shift):
    '''
    Creates an accuracy function with a given threshold.
    :param threshold_shift: Float, threshold
    :return: An accuracy function
    '''
    def accuracy(y_true, y_pred):
        '''
        Compute accuracy score
        :param y_true: actual output
        :param y_pred: predicted output
        :return: accuracy score
        '''
        y_pred = K.clip(y_pred, 0, 1)
        y_pred = K.round(y_pred + threshold_shift)
        return K.mean(K.equal(y_true, y_pred))
    return accuracy
    

def make_fbeta(threshold_shift):
    '''
    Creates fbeta function with a given threshold.
    :param threshold_shift: Float, threshold shift
    :return: An fbeta function
    '''
    def fbeta(y_true, y_pred, beta=1):
        '''
        Computes the fbeta score.
        :param y_true: y_true
        :param y_pred: predicted value
        :param beta: beta value.
        :return: fbeta score.
        '''
        # just in case of hipster activation at the final layer
        y_pred = K.clip(y_pred, 0, 1)
        y_true = K.clip(y_true, 0, 1)
        
        # shifting the prediction threshold from .5 if needed
        y_pred_bin = K.round(y_pred + threshold_shift)
        
        tp = K.sum(K.round(y_true * y_pred_bin)) + K.epsilon()
        fp = K.sum(K.round(K.clip(y_pred_bin - y_true, 0, 1)))
        fn = K.sum(K.round(K.clip(y_true - y_pred, 0, 1)))
        
        precision = tp / (tp + fp)
        recall = tp / (tp + fn)
        
        beta_squared = beta ** 2
        return (beta_squared + 1) * (precision * recall) / (beta_squared * precision + recall + K.epsilon())
    
    return fbeta

def get_w2v_vector(model, sentence, vector_size):
    '''
    Get the word2vec vector of a given sentence.
    :param model: word2vec model
    :param sentence: sentence
    :param vector_size: vector size
    :return: a word2vec vector of a given sentence
    '''
    sentence_len = len(sentence)
    sentence2vec = np.zeros(shape=(sentence_len, vector_size), dtype='float32')
    for i in range(sentence_len):
        word = sentence[i]
        word_vector = model[word]
        sentence2vec[i] = word_vector
    return sentence2vec


# Remove all non-alphanumeric token in our corpus
data['mr'] = data['mr'].apply(clean_str)
data['ref'] = data['ref'].apply(clean_str)
data.head()
elt = data.mr.append(data.ref)

for i in range(5):
    print("Mr: {0}".format(data['mr'][i]))
    print("Ref: {0}".format(data['ref'][i]))
    if data['label'][i] == 0:
        print("is not fake")
    else:
        print("is fake")
        
    print(' ')<jupyter_output>Mr: name alimentum  ,  food english  ,  pricerange moderate  ,  customer rating 3 out of 5  ,  area city centre  ,  near yippee noodle bar
Ref: alimentum chinese ,  near the riverside ,  by yippee noodle bar is more than  30 and is also highly rated by customers
is fake
 
Mr: name giraffe  ,  eattype coffee shop  ,  pricerange more than  30  ,  customer rating 5 out of 5  ,  familyfriendly no  ,  near the bakers
Ref: enjoy traditional english food from giraffe at riverside
is fake
 
Mr: name giraffe  ,  eattype pub  ,  food fast food  ,  area riverside  ,  familyfriendly yes
Ref: giraffe is a child friendly riverside pub serving fast food
is fake
 
Mr: name strada  ,  eattype pub  ,  food chinese  ,  customer rating average  ,  near yippee noodle bar
Ref: chinese serving pub ,  strada ,  has a customer rating of average and is located near yo yippee noodle bar
is fake
 
Mr: name zizzi  ,  eattype coffee shop  ,  pricerange high  ,  customer rating 1 out of 5  ,  area riverside  ,  fami[...]<jupyter_text>Visualization In this section, we will perform some visualizations<jupyter_code>d = data.groupby(['label'])[['mr']].count()
d
d['mr']/data.shape[0] * 100
sns.countplot(x='label', data=data)
plt.title('fake or genuine')
plt.show()<jupyter_output><empty_output><jupyter_text>Our dataset has roughly 63% of nonduplicate entries and 37% of duplicate entries. Next, let's look at the questions length distribution.<jupyter_code>data['mr'] = data['mr'].apply(lambda x: x.split())
data['ref'] = data['ref'].apply(lambda x: x.split())
data['mr_len'] = data['mr'].apply(len)
data['ref_len'] = data['ref'].apply(len)
data.head()
# what is the age distribution?
fig, axs = plt.subplots(ncols=2, sharey=True, figsize=(10, 5))
sns.distplot(data['mr_len'],kde_kws={'color': 'green'}, ax=axs[0], label='mr');
axs[0].set(title='mr length Distribution')
axs[0].set(ylabel='Probability Density')
sns.distplot(data['ref_len'], kde_kws={'color': 'blue'}, ax=axs[1], label='ref' );
axs[1].set(title='ref length')
plt.show()<jupyter_output><empty_output><jupyter_text>Both sentences are right skewed.<jupyter_code>data.drop(['mr_len', 'ref_len'], axis=1, inplace=True)

data.shape<jupyter_output><empty_output><jupyter_text>Prepare our data for a convolutional neural network model In this section, we will make our data suitable for a convolutional neural network archittecture. We will perform the following steps:    Pad our sentences with empty string to make them have the same length.     Build our word representations model and replace all question text with their corresponding vector space representations.    Split our data into training data and test data.  1. Pad sentences In other for our sentences to all have the same length, we have padded with empty string all sentences that have a number of tokens less than 40, and we have taken only the first 40 tokens for sentences that have more than 40 tokens.<jupyter_code>data_cnn = data.copy()
data_cnn.head()
# take only the first words 
data_cnn['mr'] = data_cnn['mr'].apply(lambda x: x + ['']*(40 - len(x)) if len(x) < 40 else x[:40])
data_cnn['ref'] = data_cnn['ref'].apply(lambda x: x + ['']*(40 - len(x)) if len(x) < 40 else x[:40])
data_cnn.head()<jupyter_output><empty_output><jupyter_text>2. Word representations<jupyter_code># get all the questions
questions = data_cnn['mr'].append(data_cnn['ref'], ignore_index=True).tolist()
import multiprocessing
# build word2vec model using only 100 features
num_features = 100
min_word_count = 1
num_workers = multiprocessing.cpu_count()
context_size = 5
downsampling = 1e-3
seed = 42
sg=1
import gensim.models.word2vec as w2v
import multiprocessing
import numpy as np

FILENAME = '../models/model.w2v'
class Word2VecModel(object):
    '''In domain word embeddings model using word2vec algorithm'''
    def __init__(self):
        self._model = None
        self.filename = FILENAME
        
    @classmethod
    def from_file(cls, filename):
        self = cls()
        self._model = w2v.Word2Vec.load(filename)
        self.filename = filename
        return self
        
    def create_w2v_model(self, sentences, num_features, min_word, context,
                         sg, downsampling, seed):
        '''
        Builds the word2vec model from a corpus of text.
        :param sentences: List of list of string, the corpus.
        :param num_features: Integer, embedding length.
        :param min_word: Integer, the algorithm will ignore all word with frequency lower than min_word.
        :param context: Integer, window size, the maximum distance between the current and predicted word within the sentence.
        :param sg: Integer - 0 or 1 -, which indicates whether to use the skip-gram archittecture (1), or the continuous bag of words archittecture (0).
        :param downsampling: Threshold for configuring which higher-frequency words are randomly downsampled;
        :param seed: Integer, a random seed.
        :return: the word2vec model
        '''
    
        # define parameters
        num_features = num_features
        min_word_count = min_word
        num_workers = multiprocessing.cpu_count()
        context = context
        downsampling = downsampling
    
        # create model
        model = w2v.Word2Vec(sg=sg, seed=seed,  workers=num_workers, \
                             size=num_features, min_count=min_word_count, \
                             window=context, sample=downsampling )
    
        model.build_vocab(sentences)
        model.train(sentences, total_examples=model.corpus_count, epochs=model.iter )
        self._model = model
        model.save(self.filename)

    def get_w2v_vector(self, sentence, vector_size):
        '''
        Gets the word representations of a given sentence.
        :param sentence: List of string, the input sentence.
        :param vector_size: Integer, embedding size.
        :return: A list of word representations.
        '''
    
        sentence_len = len(sentence)
        sentence2vec = np.zeros(shape=(sentence_len, vector_size), dtype='float32')
        for i in range(sentence_len):
            word = sentence[i]
            word_vector = self._model[word]
            sentence2vec[i] = word_vector
    
        return sentence2vec
model = Word2VecModel()
model.create_w2v_model(questions, num_features, min_word_count, context_size,
                         sg, downsampling, seed)

data_cnn['mr'] = data_cnn['mr'].apply(lambda x: get_w2v_vector(model._model, x, vector_size=100))
data_cnn['ref'] = data_cnn['ref'].apply(lambda x: get_w2v_vector(model._model, x, vector_size=100))<jupyter_output><empty_output><jupyter_text>3. Split data In this section, we will split the data into training and test sets. We won't use all the data, we will take all the records with 'is_duplicate' value of 1 and randomly select records with 'is_duplicate' value of 0. The final dataset will have the same proportion of duplicate and non-duplicate data.<jupyter_code># get the shape
data_cnn.shape

# get the shape
data_cnn.shape
data_cnn
# split a data into fake and not fake
data_fake = data_cnn[data_cnn['label'] == '1']
data_nonfake = data_cnn[data_cnn['label'] == '0']
data_fake

# get the number of fake 
number_elt = data_fake.shape[0]

# sample non duplicate data so that fake and non fake have the same size
non_fake = data_nonfake.sample(n=number_elt, random_state=seed, replace=True)
data_cnn_final = pd.concat([non_fake, data_fake])

# split the data into training and test dataset 
train, test = train_test_split(data_cnn_final, test_size=0.1, random_state=seed)
print(train.shape)
print(test.shape)<jupyter_output>(77522, 3)
(8614, 3)<jupyter_text>Our data is ready to be used in a convolutional neural network archittecture.  Prepare data for xgboost model In this section, we will prepare our data to be suitable for XGBoost model.We will perform the following step:    Prepare our text data by removing punctuation and stopwords.    Perform additional features engineering.    Split the data into train, test  1. Remove punctuations and stopwords  2. Features engineering We will perform the following features engineering on our data:Similarity score between the two questions of each record.Number of words in question 1.Number of words in question 2.Number of unique token in question 1.Number of unique token in question 2.Number of unique shared token between question 1 and question 2.  2.1 Similarity score<jupyter_code>### save data for xgboost learning
train.to_pickle('../data/train.pickle')
test.to_pickle('../data/test.pickle')<jupyter_output><empty_output><jupyter_text>In this section, we have prepared our dataset for the xgboost learning model.<jupyter_code># get a copy 
data_xgb = data.copy()

import string
def remove_punctuation(txt):
    '''
    Removes punctuations in a given sentence.
    :param txt: String, input sentence.
    :return: a list of words from the input sentence without punctuation.
    '''
    return [w for w in txt if w not in string.punctuation]
# remove punctuation in our corpus
data_xgb['mr'] = data_xgb['mr'].apply(lambda x: remove_punctuation(x))
data_xgb['ref'] = data_xgb['ref'].apply(lambda x: remove_punctuation(x))

from nltk.corpus import stopwords
stop_words = stopwords.words('english')

def remove_stopwords(txt):
    '''
    Removes english stopword in a given sentence.
    :param txt: String, the input sentence.
    :return: A list of words from the input sentence without any stopwords.
    '''
    return [w for w in txt if w not in stop_words]
# remove stopwords in our corpus
data_xgb['mr'] = data_xgb['mr'].apply(remove_stopwords)
data_xgb['ref'] = data_xgb['ref'].apply(remove_stopwords)
data_xgb.head()
##prepare the data to build the LSA model
data1 = data_xgb.mr
data2 = data_xgb.ref
data_q1 = []
data_q2 = []
q1_q2 = []
for d in data1:
    d1 = ' '.join(d)
    data_q1.append(d1)
    
for d in data2:
    d2 = ' '.join(d)
    data_q2.append(d2)
# our document set consist of all the questions: questions1 and questions2    
q1_q2 = data_q1 + data_q2
svd_clf = make_pipeline(TfidfVectorizer(min_df=1),
                               TruncatedSVD(algorithm='arpack', n_components=100, random_state=seed))
svd_clf
# fit the model
svd_clf.fit(q1_q2)
# apply trainsform to mr and ref  to get SVD vectors
mr_transform = svd_clf.transform(data_q1)
ref_transform = svd_clf.transform(data_q2)
def similarity(q1, q2):
    '''
    Computes the similarity score between lists of vectors.
    :param q1: List of the first vector.
    :param q2: List of the second vector.
    :return: A list of similarity score between vectors in q1 and q2.
    '''
    sim = []
    for el1, el2 in zip(q1, q2):
        sim.append(cosine_sim(el1, el2))

    return sim

def cosine_sim(u, v):
    '''
    Computes the cosine similarity between two vectors u and v.
    :param u: Numpy ndarray, the vector u.
    :param v: Numpy ndarray, the vector v.
    :return: Float between 0 and 1, the cosine similarity score between the vector u and v.
    '''
    return np.dot(u,v) / (norm(u)*norm(v))

# similarity score
sim = similarity(mr_transform, ref_transform)
data_xgb['similarity'] = sim
data_xgb.head()
# other feature engineering
### number of word in question 1
data_xgb['q1_len'] = data_xgb['mr'].apply(len)
### number of word in question 2
data_xgb['q2_len'] = data_xgb['ref'].apply(len)
### number of unique word in question 1
data_xgb['q1_voc_len'] = data_xgb['mr'].apply(lambda x: len(list(set(x))))
### number of unique word in question 2
data_xgb['q2_voc_len'] = data_xgb['ref'].apply(lambda x: len(list(set(x))))
### number of unique word in both question 1 and question 2
data_xgb['shared_token_len'] = data_xgb.apply(lambda row: len(list(set(row['mr']) & set(row['ref']))), axis=1 )
data_xgb.head()
# remove nan
# remove NAN iN similarity
ind_sim = data_xgb[data_xgb.similarity.isnull()].index
len(ind_sim)
data_xgb.drop(ind_sim, inplace=True)
data_xgb.info()

# drop the column we won't need
data_xgb.drop(['mr', 'ref'], axis=1, inplace=True)
data_xgb.head()

data_xgb[['similarity', 'q1_len', 'q2_len', 'q1_voc_len', 'q2_voc_len', 'shared_token_len']].plot(kind= 'density',
                                                                                                    figsize=(15, 10),
                                                                                                    subplots=True, layout=(2,3),
                                                                                                    sharex=False, sharey=False)
plt.show()

data_xgb_features = data_xgb[['similarity', 'q1_len', 'q2_len', 'q1_voc_len', 'q2_voc_len', 'shared_token_len']]
names = list(data_xgb_features.columns.values)
#correlation matrix
fig = plt.figure(figsize=(15, 10))
ax = fig.add_subplot(111)
cax = ax.matshow(data_xgb_features.corr(),vmin=-1, vmax=1)
fig.colorbar(cax)
ticks = np.arange(0,6,1)
ax.set_xticks(ticks)
ax.set_yticks(ticks)
ax.set_xticklabels(names)
ax.set_yticklabels(names)
plt.show()
# xgboost tree
xgb_model = XGBClassifier()
xgb_model.fit(data_xgb.drop('label',1), data_xgb.label)
# todo importance figure
plot_importance(xgb_model)
plt.show()
# split the data into training and test dataset 
train_xgb, test_xgb = train_test_split(data_xgb, test_size=0.1, random_state=seed)

### save data for xgboost learning
train_xgb.to_pickle('../data/xgb_train.pickle')
test_xgb.to_pickle('../data/xgb_test.pickle')
import pandas as pd
def make_fbeta(threshold_shift):
    def fbeta(y_true, y_pred, beta=1):
        '''
        Compute fbeta score.
        :param y_true: y_true
        :param y_pred: predicted value
        :param threshold_shift: threshold
        :param beta: beta value
        :return: fbeta score
    
    '''
    # just in case of hipster activation at the final layer
        y_pred = K.clip(y_pred, 0, 1)
        y_true = K.clip(y_true, 0, 1)
    
    # shifting the prediction threshold from .5 if needed
        y_pred_bin = K.round(y_pred + threshold_shift)
    
        tp = K.sum(K.round(y_true * y_pred_bin)) + K.epsilon()
        fp = K.sum(K.round(K.clip(y_pred_bin - y_true, 0, 1)))
        fn = K.sum(K.round(K.clip(y_true - y_pred, 0, 1)))
    
        precision = tp / (tp + fp)
        recall = tp / (tp + fn)
    
        beta_squared = beta ** 2
        return (beta_squared + 1) * (precision * recall) / (beta_squared * precision + recall + K.epsilon())
    return fbeta
fbeta01 = make_fbeta(0.1)
fbeta02 = make_fbeta(0.2)
fbeta03 = make_fbeta(0.3)
fbeta04 = make_fbeta(0.4)
fbeta00 = make_fbeta(0.0)
fbeta_minus01 = make_fbeta(-0.1)
fbeta_minus02 = make_fbeta(-0.2)
fbeta_minus03 = make_fbeta(-0.3)
fbeta_minus04 = make_fbeta(-0.4)
import pandas as pd

from models.cnn_model import *
from utils.utils import *
from sklearn.model_selection import train_test_split

# define parameters
filter_size = 32
strides = (1, 1)
padding = 'VALID'
embedding_len = 100
activation = 'relu'
filters = [2, 3, 4, 5, 6, 7, 8, 9, 10]
bias = True
sentence_len = 40
k_initialization = 'glorot_uniform'
b_initialization = 'zeros'
input_shape = (sentence_len, embedding_len, 1)
batch_size = 32
epochs = 100
seed = 10

# define metrics
acc = make_accuracy(0)
fbeta = make_fbeta(0)

FILE_PATH = '../data/train.pickle'
def main():
    # Prepare data
    data_cnn = pd.read_pickle(FILE_PATH)
    train, validation = train_test_split(data_cnn, test_size=0.1, random_state=seed)
    
    ## Traning data
    sentence_1_train = train.mr.tolist()
    sentence_2_train = train.ref.tolist()
    y_train = train.label.tolist()
    question_1_train, question_2_train, y_train = reshape_data(sentence_1_train, sentence_2_train, y_train)
    
    ## Validation data
    sentence_1_validation = validation.mr.tolist()
    sentence_2_validation = validation.ref.tolist()
    y_validation = validation.label.tolist()
    question_1_validation, question_2_validation, y_validation = reshape_data(sentence_1_validation,
                                                                              sentence_2_validation, y_validation)

    # Define the model
    cnn_model = CnnModel(filter_size, strides, padding, embedding_len, activation, filters, k_initialization,
                         b_initialization, input_shape, bias)
    cnn_model.summary()
    cnn_model.compile(loss='mean_squared_error', optimizer='adam', metrics=[acc, fbeta])
    
    # Train the model
    cnn_model.train([question_1_train, question_2_train], y_train, batch_size=batch_size, epochs=epochs,
                    validation_data=([question_1_validation, question_2_validation], y_validation),
                    verbose=2, shuffle=True)
    
if __name__ == '__main__':
    main()<jupyter_output>WARNING:tensorflow:From /home/jupyter/.local/lib/python3.5/site-packages/keras/backend/tensorflow_backend.py:2698: calling l2_normalize (from tensorflow.python.ops.nn_impl) with dim is deprecated and will be removed in a future version.
Instructions for updating:
dim is deprecated, use axis instead
____________________________________________________________________________________________________
Layer (type)                     Output Shape          Param #     Connected to                     
====================================================================================================
input_1 (InputLayer)             (None, 40, 100, 1)    0                                            
____________________________________________________________________________________________________
input_2 (InputLayer)             (None, 40, 100, 1)    0                                            
______________________________________________________________________________________________[...]
{'fraction_non_alphanumeric': 0.11021137469586374, 'fraction_numerical': 0.017107664233576642, 'mean_word_length': 3.3364655456643586, 'pattern_counts': {'":': 0, '<': 44, '<?xml version=': 0, '>': 42, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 20, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '11', 'dir': 'jupyter-structured-clean-dedup', 'id': '485243', 'license': 'MIT', 'n_tokens_mistral': 8752, 'n_tokens_neox': 8403, 'n_words': 2518, 'path': 'data_preprocessing.ipynb', 'repo_name': 'FedericaLucia/CNN_model', 'size': '26304'}