{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.16","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"tpu1vmV38","dataSources":[{"sourceId":19018,"databundleVersionId":2703900,"sourceType":"competition"},{"sourceId":11650,"sourceType":"datasetVersion","datasetId":8327}],"dockerImageVersionId":30886,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"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\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 read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-02-14T08:27:39.980680Z","iopub.execute_input":"2025-02-14T08:27:39.980968Z","iopub.status.idle":"2025-02-14T08:27:40.406053Z","shell.execute_reply.started":"2025-02-14T08:27:39.980942Z","shell.execute_reply":"2025-02-14T08:27:40.405028Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\nfrom tqdm import tqdm\nfrom sklearn.model_selection import train_test_split\n\nimport tensorflow as tf\nfrom tensorflow.keras.models import Sequential  # ✅ Corrected\nfrom tensorflow.keras.layers import LSTM, GRU, SimpleRNN  # ✅ Corrected\nfrom tensorflow.keras.layers import Dense, Activation, Dropout  # ✅ Corrected\nfrom tensorflow.keras.layers import Embedding  # ✅ Corrected\nfrom tensorflow.keras.layers import BatchNormalization  # ✅ Correct\nfrom tensorflow.keras.optimizers import Adam\nfrom tensorflow.keras.utils import to_categorical  # ✅ Corrected\nfrom sklearn import preprocessing, decomposition, model_selection, metrics, pipeline\nfrom keras.layers import GlobalMaxPooling1D, Conv1D, MaxPooling1D, Flatten, Bidirectional, SpatialDropout1D\nfrom tensorflow.keras.preprocessing.text import Tokenizer  # ✅ Correct\nfrom tensorflow.keras.preprocessing.sequence import pad_sequences\nfrom keras.callbacks import EarlyStopping\n\nfrom sklearn.model_selection import train_test_split\n\nimport matplotlib.pyplot as plt\nimport seaborn as sns\n%matplotlib inline\nimport time ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-14T08:27:42.811928Z","iopub.execute_input":"2025-02-14T08:27:42.812298Z","iopub.status.idle":"2025-02-14T08:28:03.856447Z","shell.execute_reply.started":"2025-02-14T08:27:42.812272Z","shell.execute_reply":"2025-02-14T08:28:03.855423Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pandas as pd\ndtrain = pd.read_csv('/kaggle/input/jigsaw-multilingual-toxic-comment-classification/jigsaw-toxic-comment-train.csv')\ndval = pd.read_csv('/kaggle/input/jigsaw-multilingual-toxic-comment-classification/validation.csv')\ndtest = pd.read_csv('/kaggle/input/jigsaw-multilingual-toxic-comment-classification/test.csv')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-14T09:07:31.308322Z","iopub.execute_input":"2025-02-14T09:07:31.308766Z","iopub.status.idle":"2025-02-14T09:07:33.144923Z","shell.execute_reply.started":"2025-02-14T09:07:31.308716Z","shell.execute_reply":"2025-02-14T09:07:33.143621Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Detect hardware, return appropriate distribution strategy\ntry:\n    # TPU detection. No parameters necessary if TPU_NAME environment variable is\n    # set: this is always the case on Kaggle.\n    tpu = tf.distribute.cluster_resolver.TPUClusterResolver(tpu ='local')\n    print('Running on TPU ', tpu.master())\nexcept ValueError:\n    tpu = None\n\nif tpu:\n    tf.config.experimental_connect_to_cluster(tpu)\n    tf.tpu.experimental.initialize_tpu_system(tpu)\n    strategy =tf.distribute.TPUStrategy(tpu)\nelse:\n    print('TPU is not active')\n    # Default distribution strategy in Tensorflow. Works on CPU and single GPU.\n    strategy = tf.distribute.get_strategy()\n\nprint(\"REPLICAS: \", strategy.num_replicas_in_sync)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-14T08:29:20.601023Z","iopub.execute_input":"2025-02-14T08:29:20.601326Z","iopub.status.idle":"2025-02-14T08:29:29.440563Z","shell.execute_reply.started":"2025-02-14T08:29:20.601302Z","shell.execute_reply":"2025-02-14T08:29:29.439609Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"REPLICAS: \", strategy.num_replicas_in_sync)\ncore = strategy.num_replicas_in_sync","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-14T09:06:41.604951Z","iopub.execute_input":"2025-02-14T09:06:41.605375Z","iopub.status.idle":"2025-02-14T09:06:41.610464Z","shell.execute_reply.started":"2025-02-14T09:06:41.605342Z","shell.execute_reply":"2025-02-14T09:06:41.609011Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"dtrain","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-14T09:06:44.247597Z","iopub.execute_input":"2025-02-14T09:06:44.247963Z","iopub.status.idle":"2025-02-14T09:06:44.260754Z","shell.execute_reply.started":"2025-02-14T09:06:44.247938Z","shell.execute_reply":"2025-02-14T09:06:44.259424Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"dtest","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-14T08:38:45.819958Z","iopub.execute_input":"2025-02-14T08:38:45.820272Z","iopub.status.idle":"2025-02-14T08:38:45.829118Z","shell.execute_reply.started":"2025-02-14T08:38:45.820248Z","shell.execute_reply":"2025-02-14T08:38:45.828238Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Data Preprocessing: \n\n**Consider this example as binary classification with only toxic**","metadata":{}},{"cell_type":"code","source":"#Limitting the data\ndtrain = dtrain.loc[:12000,:]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-14T09:07:47.269809Z","iopub.execute_input":"2025-02-14T09:07:47.270206Z","iopub.status.idle":"2025-02-14T09:07:47.274616Z","shell.execute_reply.started":"2025-02-14T09:07:47.270176Z","shell.execute_reply":"2025-02-14T09:07:47.273597Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"dtrain = dtrain[['id','comment_text','toxic']]\ndtrain.head(5)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-14T09:07:50.446877Z","iopub.execute_input":"2025-02-14T09:07:50.447257Z","iopub.status.idle":"2025-02-14T09:07:50.486882Z","shell.execute_reply.started":"2025-02-14T09:07:50.447229Z","shell.execute_reply":"2025-02-14T09:07:50.485397Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"dtrain.shape","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-14T09:07:52.959742Z","iopub.execute_input":"2025-02-14T09:07:52.960106Z","iopub.status.idle":"2025-02-14T09:07:52.965653Z","shell.execute_reply.started":"2025-02-14T09:07:52.960077Z","shell.execute_reply":"2025-02-14T09:07:52.964667Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"each_len = []\n\nfor k in dtrain['comment_text']:\n    each_len.append(len(k.split(' ')))\n    \ndtrain['comment_len'] = each_len","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-14T09:08:11.191110Z","iopub.execute_input":"2025-02-14T09:08:11.191549Z","iopub.status.idle":"2025-02-14T09:08:11.256294Z","shell.execute_reply.started":"2025-02-14T09:08:11.191522Z","shell.execute_reply":"2025-02-14T09:08:11.255121Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"dtrain_sorted = dtrain.groupby('comment_len')[['comment_text']].max().sort_values(by='comment_len', ascending=False).reset_index()\n\ndtrain_sorted","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-13T09:37:58.989447Z","iopub.execute_input":"2025-02-13T09:37:58.989779Z","iopub.status.idle":"2025-02-13T09:37:59.036419Z","shell.execute_reply.started":"2025-02-13T09:37:58.989754Z","shell.execute_reply":"2025-02-13T09:37:59.035289Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#print(dtrain_sorted.iloc[0]['comment_text'])\n#print(dtrain_sorted.iloc[0]['comment_len'])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-13T06:59:06.967139Z","iopub.execute_input":"2025-02-13T06:59:06.967575Z","iopub.status.idle":"2025-02-13T06:59:06.971420Z","shell.execute_reply.started":"2025-02-13T06:59:06.967553Z","shell.execute_reply":"2025-02-13T06:59:06.970004Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.metrics import roc_auc_score, roc_curve, auc\n\ndef roc_auc_cal(pred,y):\n    a=roc_auc_score(y, pred)\n    return a","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-13T09:38:01.183719Z","iopub.execute_input":"2025-02-13T09:38:01.184108Z","iopub.status.idle":"2025-02-13T09:38:01.188673Z","shell.execute_reply.started":"2025-02-13T09:38:01.184079Z","shell.execute_reply":"2025-02-13T09:38:01.187386Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#EXAMPLE\n'''\n# Simulated dataset\ny_true = [0, 0, 1, 1]\ny_scores = [0.1, 0.4, 0.35, 0.8]\n\nprint(roc_auc_cal(y_scores,y_true))  ---- 0.75\n'''","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-13T06:59:06.982660Z","iopub.execute_input":"2025-02-13T06:59:06.982891Z","iopub.status.idle":"2025-02-13T06:59:06.993406Z","shell.execute_reply.started":"2025-02-13T06:59:06.982870Z","shell.execute_reply":"2025-02-13T06:59:06.992732Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"dtrain","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-13T09:38:03.129519Z","iopub.execute_input":"2025-02-13T09:38:03.129828Z","iopub.status.idle":"2025-02-13T09:38:03.139663Z","shell.execute_reply.started":"2025-02-13T09:38:03.129804Z","shell.execute_reply":"2025-02-13T09:38:03.138790Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#Converting text to lowercase:\nimport re\ndef convt_lower(data):\n    tmp = []\n    for e in data:\n        tmp.append(e.lower())\n    return tmp\n\ndef remove_specialchar(data):\n    tmp = []\n    for e in data:\n        e = e.replace('\\n',' ')\n        tmp.append(re.sub(r'[^a-zA-Z0-9\\s]', '', e))\n    return tmp\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-14T09:08:16.524004Z","iopub.execute_input":"2025-02-14T09:08:16.524404Z","iopub.status.idle":"2025-02-14T09:08:16.529826Z","shell.execute_reply.started":"2025-02-14T09:08:16.524374Z","shell.execute_reply":"2025-02-14T09:08:16.528769Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"comment_lc = convt_lower(dtrain['comment_text'])\ncomment_punc = remove_specialchar(comment_lc)\ndtrain['comment_pro']=comment_punc","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-14T09:08:20.111505Z","iopub.execute_input":"2025-02-14T09:08:20.111907Z","iopub.status.idle":"2025-02-14T09:08:20.305364Z","shell.execute_reply.started":"2025-02-14T09:08:20.111880Z","shell.execute_reply":"2025-02-14T09:08:20.303354Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Takeaways here:**\n\n> I need to tokenizer them then i am going to list it into sequence. For doing that, I splitted the data into train and test. But only train data goes to Tokenizer.fit_on_text. If I put train and test data together, tokenization process would be done by considering test data which means the model would see the test data. but it should not see. test data should be unseen.","metadata":{}},{"cell_type":"code","source":"dtrain['comment_pro'].apply(lambda x:len(str(x).split())).max()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-13T09:38:08.327482Z","iopub.execute_input":"2025-02-13T09:38:08.327786Z","iopub.status.idle":"2025-02-13T09:38:08.383930Z","shell.execute_reply.started":"2025-02-13T09:38:08.327762Z","shell.execute_reply":"2025-02-13T09:38:08.382772Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#first split the data:\ny = dtrain['toxic']\nx = dtrain['comment_pro']\nx_train,x_test,y_train,y_test = train_test_split(x.values,y.values,random_state=42,test_size=0.25, shuffle=True)\n\nmaxlen = 2000\n#Initialize the tokenizer\ntoken = Tokenizer(num_words=maxlen, oov_token=\"<OOV>\") \ntoken.fit_on_texts(x_train)\nvocab_size=token.word_index\n\n#Apply the tokenizer here:\nx_train_seq = token.texts_to_sequences(x_train) \nx_test_seq = token.texts_to_sequences(x_test)   #Truncation just so that max len of data is 2000 words\n\n#Truncation did not take place before. I will to it now!\nfor e in x_train_seq:\n    if len(e)>2000:\n        print(len(e))\n        print('blinked!')\n        break\n\nx_train_pad = pad_sequences(x_train_seq, maxlen=maxlen, padding='pre', truncating='pre') #post or pre\nx_test_pad = pad_sequences(x_test_seq, maxlen=maxlen, padding='pre', truncating='pre') #post or pre\n\n#now check whether the truncating is done succesfully:\nchecker = True\nfor e in x_train_pad:\n    if len(e) > 2000:\n        print('blinked!')\n        checker == False\nif checker == True:\n    print('truncating is applied sucessfully')\nelse:\n    print('there is a problem in truncating')\n    ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-14T09:08:26.635916Z","iopub.execute_input":"2025-02-14T09:08:26.636308Z","iopub.status.idle":"2025-02-14T09:08:27.827795Z","shell.execute_reply.started":"2025-02-14T09:08:26.636275Z","shell.execute_reply":"2025-02-14T09:08:27.826417Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"i = 0\nfor k,v in vocab_size.items():\n    print(k,v)\n    i+=1\n    if i == 10:\n        break\nprint('the number of vocabulary',len(vocab_size))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-13T09:38:13.250214Z","iopub.execute_input":"2025-02-13T09:38:13.250541Z","iopub.status.idle":"2025-02-13T09:38:13.255572Z","shell.execute_reply.started":"2025-02-13T09:38:13.250514Z","shell.execute_reply":"2025-02-13T09:38:13.254378Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**RNN(Embedding layer needs 3 input):**\n- num_vocab\n- embedding dim\n- inp_len\n\n**Takeaways_1:** (num_vocab)\n\n> Embedding layer needs to know how many words exists in the vocabulary to assign embedding properly. Here created an matrix for the entire vocabulary. Thats why we need make the matrix prepare for how many different inputs will come.\n>\n**Takeaways_2:** (embedding_dim)\n\n> Embedding layer need to know each token has how many feature vectors. When you assign lets say 100, each token will have 100 different embedding vectors that refers to features.\n> Bigger vector of feature, slower training time, better results (also overfiting risk)\n> Lesser vector of feature, faster training time, worse results ","metadata":{}},{"cell_type":"code","source":"x_train_pad","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-13T06:59:08.163405Z","iopub.execute_input":"2025-02-13T06:59:08.163658Z","iopub.status.idle":"2025-02-13T06:59:08.173505Z","shell.execute_reply.started":"2025-02-13T06:59:08.163632Z","shell.execute_reply":"2025-02-13T06:59:08.172827Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Basic RNN","metadata":{}},{"cell_type":"code","source":"from tensorflow.keras.optimizers import Adam\n\nloss = 'binary_crossentropy'\n\nwith strategy.scope():\n    optimizer = Adam(learning_rate=1e-5,clipnorm=1.0)\n\n    model = Sequential()\n    #input layer\n    model.add(Embedding(len(vocab_size)+1,300,input_length = maxlen)) #input_dim,output_dim(embedding_dim),inp_len\n    model.add(BatchNormalization())\n    #hidden layer where recurrency happens here\n    model.add(SimpleRNN(100))\n    #output layer\n    model.add(Dense(1,activation='sigmoid'))\n\n    model.build(input_shape=(None, 2000))\n    model.compile(loss='binary_crossentropy', optimizer=optimizer,metrics =['accuracy'])\nmodel.summary()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-13T06:59:08.580420Z","iopub.status.idle":"2025-02-13T06:59:08.581025Z","shell.execute_reply.started":"2025-02-13T06:59:08.580581Z","shell.execute_reply":"2025-02-13T06:59:08.580626Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"> **Takeaways**:\n> Somehow the results go to nan value. It may be gradient exploding! (cause1) -- i will check it\n> also decrease the batch size","metadata":{}},{"cell_type":"code","source":"history_basicRNN = model.fit(x_train_pad,y_train,epochs = 20, batch_size=64*strategy.num_replicas_in_sync,validation_data = (x_test_pad,y_test))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-13T06:59:08.581882Z","iopub.status.idle":"2025-02-13T06:59:08.582462Z","shell.execute_reply.started":"2025-02-13T06:59:08.582016Z","shell.execute_reply":"2025-02-13T06:59:08.582055Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_acc = history_basicRNN.history['accuracy']\ntest_acc = history_basicRNN.history['val_accuracy']\ntrain_loss = history_basicRNN.history['loss']\ntest_loss = history_basicRNN.history['val_loss']\n\nepoch = range(1,len(train_acc)+1) # From  1 to epoch number\n\n\nplt.plot(epoch,train_acc ,label ='train_acc')\nplt.plot(epoch,test_acc ,label ='test_acc')\nplt.title('Accuracy Results')\nplt.xlabel('Epochs')\nplt.ylabel('Accuracy')\nplt.legend()\nplt.show()\n\nplt.plot(epoch,train_loss ,label ='train_loss')\nplt.plot(epoch,test_loss ,label ='test_loss')\nplt.title('Loss Results')\nplt.xlabel('Epochs')\nplt.ylabel('Loss')\nplt.legend()\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-13T06:59:08.583168Z","iopub.status.idle":"2025-02-13T06:59:08.583717Z","shell.execute_reply.started":"2025-02-13T06:59:08.583316Z","shell.execute_reply":"2025-02-13T06:59:08.583354Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## GloVe Embedding\n> instead of random embedding of feature vector, we can inherit the feature information that trained before and also trained in more large data.\n> GloVe, Word2Vec ","metadata":{}},{"cell_type":"markdown","source":"![image.png](attachment:d50dcbd5-2aee-4d14-9cac-52b5ab0513fd.png)","metadata":{},"attachments":{"d50dcbd5-2aee-4d14-9cac-52b5ab0513fd.png":{"image/png":"iVBORw0KGgoAAAANSUhEUgAAApYAAAFICAIAAABlTCmfAAAgAElEQVR4Ae29f3BbVZbvS5IeaH71j5oeaBoYONzpclP+p++temNxmRqP5JquqXK163ZXTFFdaaDgBdIRKE1NqAxMeqLM6w6CJEyHXBrRsdIhpE1QghMzTmSncQTBToKcBDnBJ4ksO7aUWJJ16tWbd+vWe6/ue8OjvZp1V++jX5aPdPTje4oKW/vsvfban73O/p69z5F8nYYDBEAABEAABECgDglcV4c+w2UQAAEQAAEQAAENEo4gAAEQAAEQAIG6JAAJr8thg9MgAAIgAAIgAAlHDIAACIAACIBAXRKAhNflsMFpEAABEAABEICEIwZAAARAAARAoC4JQMLrctjgNAiAAAiAAAhAwhEDIAACIAACIFCXBCDhdTlscBoEQAAEQAAEIOGIARAAARAAARCoSwKQ8LocNjgNAiAAAiAAApBwxAAIgAAIgAAI1CUBSHhdDhucBgEQAAEQAAFIOGIABEAABEAABOqSACS8LocNToMACIAACIBARST87rvvvmPJB8YGBEAABEAABECgAIGKSPjXv/7165Z2LFu2rIDTOAUCIAACIAACIFAxCb/x69etPlLmf47/FRKO0AQBEAABEACBwgQqJuE3f+O6f7hY5n+uDZDwwsOGsyAAAiAAAiAACV9qDAQCAWPh0HW9u7t7qeZQ/48J1Ave1tbWw4cPZzKZubm5119/XdM0n8+XSqUMw0gkEm63+4+7hU8gAAIgYAGBupTwUChEqhkKhSQDWybNimpMa2vrtm3bzp49e+3aNepyJpO5cuXKb37zG9nx+k273e7jx4/PzMxks1nqYCqVisViu3btok6Vgbe3t5eszc7OejweCae7u1vXdWqov79fniqalnXJAv8bCATk2UgkAgkvyhMFQAAElk7ABgn/yqbLba9dufWfLufdZi+2kW6XhK9ateqtt966cOHCyZMnGX0ZGsN1CydWrlwZiURY21gwDMNQ7l0K26nm2Y6Oju3bt588efLy5cuFl54dHR1Hjx7NZDKyX5zmDpaB97nnnksmk4ZhZLPZ3bt3y+5v2rSJbobm5uY2b94sTxVNS5FmPykRCAQ0Tevr68MqvChGFAABELCQQFUl/MsbLw1c/G+fLRz/379/9uro/55bxWtVwvnWgZZZNAxlaEwp47dq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"}}},{"cell_type":"code","source":"#previous model: model.add(Embedding(vocab_size+1,300,input_length = maxlen)) #input_dim, embedding_dim or output_dim, input_len\n\nglove = '/kaggle/input/glove840b300dtxt/glove.840B.300d.txt'","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-14T08:40:06.263195Z","iopub.execute_input":"2025-02-14T08:40:06.263585Z","iopub.status.idle":"2025-02-14T08:40:06.267463Z","shell.execute_reply.started":"2025-02-14T08:40:06.263513Z","shell.execute_reply":"2025-02-14T08:40:06.266459Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#https://medium.com/analytics-vidhya/basics-of-using-pre-trained-glove-vectors-in-python-d38905f356db\nembeddings_dict = {}\nwith open(glove, 'r',encoding=\"utf-8\") as f:\n    for line in tqdm(f):\n        values = line.split(' ')\n        word = values[0]\n        vector = np.asarray(values[1:], \"float32\")\n        embeddings_dict[word] = vector","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-14T08:40:08.171505Z","iopub.execute_input":"2025-02-14T08:40:08.171845Z","iopub.status.idle":"2025-02-14T08:42:55.780593Z","shell.execute_reply.started":"2025-02-14T08:40:08.171821Z","shell.execute_reply":"2025-02-14T08:42:55.779261Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Matching the data with glove embeddings","metadata":{}},{"cell_type":"code","source":"#let me remember:\ni = 0\nfor k,v in vocab_size.items():\n    print(k,v)\n    i+=1\n    if i == 10:\n        break","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-14T08:43:15.132768Z","iopub.execute_input":"2025-02-14T08:43:15.133206Z","iopub.status.idle":"2025-02-14T08:43:15.138716Z","shell.execute_reply.started":"2025-02-14T08:43:15.133174Z","shell.execute_reply":"2025-02-14T08:43:15.137504Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"len(vocab_size)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-13T09:41:05.106202Z","iopub.execute_input":"2025-02-13T09:41:05.106500Z","iopub.status.idle":"2025-02-13T09:41:05.111632Z","shell.execute_reply.started":"2025-02-13T09:41:05.106475Z","shell.execute_reply":"2025-02-13T09:41:05.110580Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"embedding_vector = np.zeros((len(vocab_size)+1,300)) #creating empty array\n#filling them with pretrained values:\n\nfor k,i in tqdm(vocab_size.items()):\n    arr_value = embeddings_dict.get(k)\n    if arr_value is not None:\n        embedding_vector[i] = arr_value","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-14T09:08:43.826299Z","iopub.execute_input":"2025-02-14T09:08:43.826734Z","iopub.status.idle":"2025-02-14T09:08:44.038623Z","shell.execute_reply.started":"2025-02-14T09:08:43.826704Z","shell.execute_reply":"2025-02-14T09:08:44.037143Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"embeddings_dict['the']","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-13T13:55:25.492018Z","iopub.execute_input":"2025-02-13T13:55:25.492367Z","iopub.status.idle":"2025-02-13T13:55:25.501696Z","shell.execute_reply.started":"2025-02-13T13:55:25.492339Z","shell.execute_reply":"2025-02-13T13:55:25.500605Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"embedding_vector[2]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-13T13:55:27.995876Z","iopub.execute_input":"2025-02-13T13:55:27.996209Z","iopub.status.idle":"2025-02-13T13:55:28.003788Z","shell.execute_reply.started":"2025-02-13T13:55:27.996184Z","shell.execute_reply":"2025-02-13T13:55:28.002835Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"embedding_vector.shape","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-13T13:55:30.831979Z","iopub.execute_input":"2025-02-13T13:55:30.832311Z","iopub.status.idle":"2025-02-13T13:55:30.836958Z","shell.execute_reply.started":"2025-02-13T13:55:30.832283Z","shell.execute_reply":"2025-02-13T13:55:30.836059Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"maxlen","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-13T13:55:33.341273Z","iopub.execute_input":"2025-02-13T13:55:33.341605Z","iopub.status.idle":"2025-02-13T13:55:33.346395Z","shell.execute_reply.started":"2025-02-13T13:55:33.341576Z","shell.execute_reply":"2025-02-13T13:55:33.345613Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# LSTM","metadata":{}},{"cell_type":"code","source":"from tensorflow.keras.optimizers import Adam\n\nloss = 'binary_crossentropy'\n\nwith strategy.scope():\n    optimizer = Adam(learning_rate=1e-5,clipnorm=1.0)\n\n    model = Sequential()\n\n    model.add(Embedding(len(vocab_size)+1,300,weights=[embedding_vector],input_length = maxlen, trainable=False)) #input_dim,output_dim(embedding_dim),inp_len\n    model.add(BatchNormalization())\n    model.add(LSTM(100))\n    model.add(Dense(1,activation='sigmoid'))\n\n    model.build(input_shape=(None, 2000))\n    model.compile(loss='binary_crossentropy', optimizer=optimizer,metrics =['accuracy'])\nmodel.summary()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-13T07:21:30.379686Z","iopub.execute_input":"2025-02-13T07:21:30.380069Z","iopub.status.idle":"2025-02-13T07:21:31.909746Z","shell.execute_reply.started":"2025-02-13T07:21:30.380037Z","shell.execute_reply":"2025-02-13T07:21:31.908527Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"start = time.time()\nhistory_lstm = model.fit(x_train_pad,y_train,epochs = 20, batch_size=64*strategy.num_replicas_in_sync,validation_data = (x_test_pad,y_test))\nend = time.time()\n\ndif = (end-start)\nprint(f'the execution took {dif:.3f} sec')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-13T07:21:33.617782Z","iopub.execute_input":"2025-02-13T07:21:33.618181Z","iopub.status.idle":"2025-02-13T07:46:34.908494Z","shell.execute_reply.started":"2025-02-13T07:21:33.618148Z","shell.execute_reply":"2025-02-13T07:46:34.906745Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_acc = history_lstm.history['accuracy']\ntest_acc = history_lstm.history['val_accuracy']\ntrain_loss = history_lstm.history['loss']\ntest_loss = history_lstm.history['val_loss']\n\nepoch = range(1,len(train_acc)+1) # From  1 to epoch number\n\n\nplt.plot(epoch,train_acc ,label ='train_acc')\nplt.plot(epoch,test_acc ,label ='test_acc')\nplt.title('Accuracy Results')\nplt.xlabel('Epochs')\nplt.ylabel('Accuracy')\nplt.legend()\nplt.show()\n\nplt.plot(epoch,train_loss ,label ='train_loss')\nplt.plot(epoch,test_loss ,label ='test_loss')\nplt.title('Loss Results')\nplt.xlabel('Epochs')\nplt.ylabel('Loss')\nplt.legend()\nplt.show()\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-13T07:50:08.043087Z","iopub.execute_input":"2025-02-13T07:50:08.043439Z","iopub.status.idle":"2025-02-13T07:50:08.417565Z","shell.execute_reply.started":"2025-02-13T07:50:08.043412Z","shell.execute_reply":"2025-02-13T07:50:08.416519Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# GRU","metadata":{}},{"cell_type":"code","source":"from tensorflow.keras.optimizers import Adam\n\nloss = 'binary_crossentropy'\n\nwith strategy.scope():\n    optimizer = Adam(learning_rate=1e-5,clipnorm=1.0)\n\n    model = Sequential()\n\n    model.add(Embedding(len(vocab_size)+1,300,weights=[embedding_vector],input_length = maxlen, trainable=False)) #input_dim,output_dim(embedding_dim),inp_len\n    model.add(BatchNormalization())\n    model.add(GRU(100))\n    model.add(Dense(1,activation='sigmoid'))\n\n    model.build(input_shape=(None, 2000))\n    model.compile(loss='binary_crossentropy', optimizer=optimizer,metrics =['accuracy'])\nmodel.summary() ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-13T07:50:01.856162Z","iopub.execute_input":"2025-02-13T07:50:01.856545Z","iopub.status.idle":"2025-02-13T07:50:03.769168Z","shell.execute_reply.started":"2025-02-13T07:50:01.856518Z","shell.execute_reply":"2025-02-13T07:50:03.768075Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"start = time.time()\nhistory_gru = model.fit(x_train_pad,y_train,epochs = 20, batch_size=64*strategy.num_replicas_in_sync,validation_data = (x_test_pad,y_test))\nend = time.time()\n\ndif = (end-start)\nprint(f'the execution took {dif:.3f} sec')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-13T07:50:43.471915Z","iopub.execute_input":"2025-02-13T07:50:43.472259Z","iopub.status.idle":"2025-02-13T08:15:41.102723Z","shell.execute_reply.started":"2025-02-13T07:50:43.472218Z","shell.execute_reply":"2025-02-13T08:15:41.101063Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_acc = history_gru.history['accuracy']\ntest_acc = history_gru.history['val_accuracy']\ntrain_loss = history_gru.history['loss']\ntest_loss = history_gru.history['val_loss']\n\nepoch = range(1,len(train_acc)+1) # From  1 to epoch number\n\n\nplt.plot(epoch,train_acc ,label ='train_acc')\nplt.plot(epoch,test_acc ,label ='test_acc')\nplt.title('Accuracy Results')\nplt.xlabel('Epochs')\nplt.ylabel('Accuracy')\nplt.legend()\nplt.show()\n\nplt.plot(epoch,train_loss ,label ='train_loss')\nplt.plot(epoch,test_loss ,label ='test_loss')\nplt.title('Loss Results')\nplt.xlabel('Epochs')\nplt.ylabel('Loss')\nplt.legend()\nplt.show()\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-13T08:15:47.690759Z","iopub.execute_input":"2025-02-13T08:15:47.691074Z","iopub.status.idle":"2025-02-13T08:15:47.995459Z","shell.execute_reply.started":"2025-02-13T08:15:47.691048Z","shell.execute_reply":"2025-02-13T08:15:47.993962Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Bidirectional LSTM","metadata":{}},{"cell_type":"code","source":"from tensorflow.keras.layers import LSTM, Bidirectional, Dense\n\nloss = 'binary_crossentropy'\n\nwith strategy.scope():\n    optimizer = Adam(learning_rate=1e-5,clipnorm=1.0)\n\n    model = Sequential()\n\n    model.add(Embedding(len(vocab_size)+1,300,weights=[embedding_vector],input_length = maxlen, trainable=False)) #input_dim,output_dim(embedding_dim),inp_len\n    model.add(BatchNormalization())\n    model.add(Bidirectional(LSTM(100)))\n    model.add(Dense(1,activation='sigmoid'))\n\n    model.build(input_shape=(None, 2000))\n    model.compile(loss='binary_crossentropy', optimizer=optimizer,metrics =['accuracy'])\nmodel.summary() ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-13T09:44:25.408286Z","iopub.execute_input":"2025-02-13T09:44:25.408698Z","iopub.status.idle":"2025-02-13T09:44:27.035085Z","shell.execute_reply.started":"2025-02-13T09:44:25.408671Z","shell.execute_reply":"2025-02-13T09:44:27.033859Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"start = time.time()\nhistory_bilstm = model.fit(x_train_pad,y_train,epochs = 20, batch_size=64*strategy.num_replicas_in_sync,validation_data = (x_test_pad,y_test))\nend = time.time()\n\ndif = (end-start)\nprint(f'the execution took {dif:.3f} sec')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-13T09:44:48.453451Z","iopub.execute_input":"2025-02-13T09:44:48.453839Z","iopub.status.idle":"2025-02-13T10:47:00.258369Z","shell.execute_reply.started":"2025-02-13T09:44:48.453811Z","shell.execute_reply":"2025-02-13T10:47:00.256914Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_acc = history_bilstm.history['accuracy']\ntest_acc = history_bilstm.history['val_accuracy']\ntrain_loss = history_bilstm.history['loss']\ntest_loss = history_bilstm.history['val_loss']\n\nepoch = range(1,len(train_acc)+1) # From  1 to epoch number\n\n\nplt.plot(epoch,train_acc ,label ='train_acc')\nplt.plot(epoch,test_acc ,label ='test_acc')\nplt.title('Accuracy Results')\nplt.xlabel('Epochs')\nplt.ylabel('Accuracy')\nplt.legend()\nplt.show()\n\nplt.plot(epoch,train_loss ,label ='train_loss')\nplt.plot(epoch,test_loss ,label ='test_loss')\nplt.title('Loss Results')\nplt.xlabel('Epochs')\nplt.ylabel('Loss')\nplt.legend()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-13T10:48:45.695857Z","iopub.execute_input":"2025-02-13T10:48:45.696303Z","iopub.status.idle":"2025-02-13T10:48:46.043519Z","shell.execute_reply.started":"2025-02-13T10:48:45.696271Z","shell.execute_reply":"2025-02-13T10:48:46.041915Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# What if my data is never cropped:","metadata":{}},{"cell_type":"code","source":"dtrain = pd.read_csv('/kaggle/input/jigsaw-multilingual-toxic-comment-classification/jigsaw-toxic-comment-train.csv')\ndval = pd.read_csv('/kaggle/input/jigsaw-multilingual-toxic-comment-classification/validation.csv')\ndtest = pd.read_csv('/kaggle/input/jigsaw-multilingual-toxic-comment-classification/test.csv')\n\ndtrain = dtrain[['id','comment_text','toxic']]\n\ncomment_lc = convt_lower(dtrain['comment_text'])\ncomment_punc = remove_specialchar(comment_lc)\ndtrain['comment_pro']=comment_punc","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-14T09:26:07.749984Z","iopub.execute_input":"2025-02-14T09:26:07.750318Z","iopub.status.idle":"2025-02-14T09:26:12.201359Z","shell.execute_reply.started":"2025-02-14T09:26:07.750292Z","shell.execute_reply":"2025-02-14T09:26:12.200076Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"dtrain.shape","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-14T09:17:05.604621Z","iopub.execute_input":"2025-02-14T09:17:05.604991Z","iopub.status.idle":"2025-02-14T09:17:05.611018Z","shell.execute_reply.started":"2025-02-14T09:17:05.604964Z","shell.execute_reply":"2025-02-14T09:17:05.609771Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#first split the data:\ny = dtrain['toxic']\nx = dtrain['comment_pro']\nx_train,x_test,y_train,y_test = train_test_split(x.values,y.values,random_state=42,test_size=0.25, shuffle=True)\n\nmaxlen = 2000 #it was 2000\n#Initialize the tokenizer\ntoken = Tokenizer(num_words=maxlen, oov_token=\"<OOV>\") \ntoken.fit_on_texts(x_train)\nvocab_size=token.word_index\n\n#Apply the tokenizer here:\nx_train_seq = token.texts_to_sequences(x_train) \nx_test_seq = token.texts_to_sequences(x_test)   #Truncation just so that max len of data is 2000 words\n\n#Truncation did not take place before. I will to it now!\nfor e in x_train_seq:\n    if len(e)>2000:\n        print(len(e))\n        print('blinked!')\n        break\n\nx_train_pad = pad_sequences(x_train_seq, maxlen=maxlen, padding='pre', truncating='pre') #post or pre\nx_test_pad = pad_sequences(x_test_seq, maxlen=maxlen, padding='pre', truncating='pre') #post or pre\n\n#now check whether the truncating is done succesfully:\nchecker = True\nfor e in x_train_pad:\n    if len(e) > 2000:\n        print('blinked!')\n        checker == False\nif checker == True:\n    print('truncating is applied sucessfully')\nelse:\n    print('there is a problem in truncating')\n    ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-14T09:52:24.790342Z","iopub.execute_input":"2025-02-14T09:52:24.790760Z","iopub.status.idle":"2025-02-14T09:52:42.168062Z","shell.execute_reply.started":"2025-02-14T09:52:24.790728Z","shell.execute_reply":"2025-02-14T09:52:42.166867Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"len(vocab_size.keys())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-14T09:52:46.090534Z","iopub.execute_input":"2025-02-14T09:52:46.090910Z","iopub.status.idle":"2025-02-14T09:52:46.096552Z","shell.execute_reply.started":"2025-02-14T09:52:46.090883Z","shell.execute_reply":"2025-02-14T09:52:46.095678Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#let me remember:\nprint('input dim is ', len(vocab_size.keys()))\ni = 0\nfor k,v in vocab_size.items():\n    print(k,v)\n    i+=1\n    if i == 10:\n        break","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-14T09:26:44.477289Z","iopub.execute_input":"2025-02-14T09:26:44.477667Z","iopub.status.idle":"2025-02-14T09:26:44.482878Z","shell.execute_reply.started":"2025-02-14T09:26:44.477640Z","shell.execute_reply":"2025-02-14T09:26:44.481808Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"> in the previous example, the input dimension was 38409 because i cropped the data like dtrain = dtrain.loc[:12000,:]. Now the data is huge. I need to make the model a bit complex and also add the system that prevent the model from overfitting.","metadata":{}},{"cell_type":"code","source":"embedding_vector = np.zeros((len(vocab_size)+1,300)) #creating empty array\n#filling them with pretrained values:\n\nfor k,i in tqdm(vocab_size.items()):\n    arr_value = embeddings_dict.get(k)\n    if arr_value is not None:\n        embedding_vector[i] = arr_value","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-14T09:52:51.028195Z","iopub.execute_input":"2025-02-14T09:52:51.028580Z","iopub.status.idle":"2025-02-14T09:52:51.881544Z","shell.execute_reply.started":"2025-02-14T09:52:51.028552Z","shell.execute_reply":"2025-02-14T09:52:51.880381Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"len(embedding_vector)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-14T09:52:55.441320Z","iopub.execute_input":"2025-02-14T09:52:55.441704Z","iopub.status.idle":"2025-02-14T09:52:55.448876Z","shell.execute_reply.started":"2025-02-14T09:52:55.441678Z","shell.execute_reply":"2025-02-14T09:52:55.447414Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"embedding_vector.shape","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-14T09:52:59.107034Z","iopub.execute_input":"2025-02-14T09:52:59.107414Z","iopub.status.idle":"2025-02-14T09:52:59.112652Z","shell.execute_reply.started":"2025-02-14T09:52:59.107387Z","shell.execute_reply":"2025-02-14T09:52:59.111855Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"maxlen","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-14T09:57:44.156449Z","iopub.execute_input":"2025-02-14T09:57:44.156833Z","iopub.status.idle":"2025-02-14T09:57:44.162729Z","shell.execute_reply.started":"2025-02-14T09:57:44.156805Z","shell.execute_reply":"2025-02-14T09:57:44.161126Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# First things first, LSTM\n\nfrom tensorflow.keras.optimizers import Adam\nfrom keras import regularizers\nkernel_regularizer = regularizers.L1L2(l1=1e-4, l2=1e-4)\nbias_regularizer = regularizers.L2(1e-4)\nactivity_regularizer = regularizers.L2(1e-4)\n\nloss = 'binary_crossentropy'\nearly_stop = EarlyStopping(\n    monitor=\"val_loss\",\n    patience=5,\n    verbose=1,\n    mode=\"min\")\n\nwith strategy.scope():\n    optimizer = Adam(learning_rate=1e-4,clipnorm=1.0)\n\n    model = Sequential()\n\n    model.add(Embedding(len(vocab_size)+1,300,weights=[embedding_vector],input_length = maxlen, trainable=False)) #input_dim,output_dim(embedding_dim),inp_len\n\n    model.add(LSTM(128,\n                   dropout=0.2,\n                   kernel_regularizer=kernel_regularizer,\n                  bias_regularizer = bias_regularizer,\n                  activity_regularizer = activity_regularizer))\n    model.add(BatchNormalization())\n\n    model.add(Dense(128,activation='relu'))\n    model.add(BatchNormalization())\n    model.add(Dense(64,activation='relu'))\n    model.add(BatchNormalization())\n\n    model.add(Dense(1,activation='sigmoid'))\n\n    model.build(input_shape=(None, 2000))\n    model.compile(loss='binary_crossentropy', optimizer=optimizer,metrics =['accuracy'])\nmodel.summary()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-14T10:04:37.834272Z","iopub.execute_input":"2025-02-14T10:04:37.834669Z","iopub.status.idle":"2025-02-14T10:04:42.071566Z","shell.execute_reply.started":"2025-02-14T10:04:37.834638Z","shell.execute_reply":"2025-02-14T10:04:42.070485Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"start = time.time()\nhistory_lstm = model.fit(x_train_pad,\n                         y_train,\n                         epochs = 5, \n                         batch_size=64*strategy.num_replicas_in_sync,\n                         validation_data = (x_test_pad,y_test),\n                         callbacks = [early_stop])\nend = time.time()\n\ndif = (end-start)\nprint(f'the execution took {dif:.3f} sec')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-14T10:04:45.537760Z","iopub.execute_input":"2025-02-14T10:04:45.538066Z","iopub.status.idle":"2025-02-14T10:06:12.299545Z","shell.execute_reply.started":"2025-02-14T10:04:45.538043Z","shell.execute_reply":"2025-02-14T10:06:12.298039Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"start = time.time()\nhistory_lstm = model_bi.fit(x_train_pad,\n                         y_train,\n                         epochs = 20, \n                         batch_size=64*strategy.num_replicas_in_sync,\n                         validation_data = (x_test_pad,y_test),\n                         callbacks = [early_stop])\nend = time.time()\n\ndif = (end-start)\nprint(f'the execution took {dif:.3f} sec')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-13T13:27:30.087711Z","iopub.execute_input":"2025-02-13T13:27:30.088043Z","execution_failed":"2025-02-13T13:28:51.924Z"}},"outputs":[],"execution_count":null}]}