model_v2.py (12611B)
1 import csv 2 import os 3 import time 4 from datetime import datetime 5 6 import keras_tuner as kt 7 import numpy as np 8 import pandas as pd 9 import tensorflow as tf 10 import tensorflow_hub as hub 11 from nltk.corpus import stopwords 12 from tensorflow import keras 13 14 vocab_size = 1000 15 16 17 logdir = "logs/scalars/" + datetime.now().strftime("%Y%m%d-%H%M%S") 18 tensorboard_callback = keras.callbacks.TensorBoard(log_dir=logdir) 19 20 hypermodel_logdir = ( 21 "logs/scalars/" + datetime.now().strftime("%Y%m%d-%H%M%S") + "_hypermodel" 22 ) 23 hypermodel_tensorboard_callback = keras.callbacks.TensorBoard(log_dir=hypermodel_logdir) 24 25 hypertuner_logdir = "hypertuner_logs/scalars/" + datetime.now().strftime( 26 "%Y%m%d-%H%M%S" 27 ) 28 hypertuner_tensorboard_callback = keras.callbacks.TensorBoard(log_dir=hypertuner_logdir) 29 # Define the checkpoint directory to store the checkpoints. 30 checkpoint_dir = "./training_checkpoints" 31 # Define the name of the checkpoint files. 32 checkpoint_prefix = os.path.join(checkpoint_dir, "ckpt_{epoch}") 33 34 progress_bar = tf.keras.callbacks.ProgbarLogger() 35 36 37 class SpamDectionModel(tf.keras.Model): 38 def __init__(self, hp_dense1, hp_dropout, hp_l2): 39 super(SpamDectionModel, self).__init__() 40 self.dropout = tf.keras.layers.Dropout( 41 hp_dropout, 42 ) 43 self.dense1 = tf.keras.layers.Dense( 44 hp_dense1, 45 activation="relu", 46 # kernel_regularizer=tf.keras.regularizers.l2(hp_l2), 47 ) 48 self.dense2 = tf.keras.layers.Dense( 49 1, activation="sigmoid", name="score_output" 50 ) 51 # self.glob_average_pooling_1d = tf.keras.layers.GlobalAveragePooling1D() 52 self.use_layer = hub.KerasLayer( 53 "https://tfhub.dev/google/universal-sentence-encoder/4", 54 trainable=False, 55 input_shape=[], 56 dtype=tf.string, 57 name="USE", 58 ) 59 60 @tf.function 61 def call(self, x, training=False): 62 x = self.use_layer(x) 63 if training: 64 x = self.dropout(x, training=training) 65 x = self.dense1(x) 66 # if training: 67 # x = self.dropout(x, training=training) 68 return self.dense2(x) 69 70 71 class SpamDectionHyperModel(kt.HyperModel): 72 def __init__(self): 73 super(SpamDectionHyperModel, self).__init__() 74 75 def build(self, hp): 76 # Tune the number of units in the first Dense layer 77 # Choose an optimal value between 6-512 78 hp_dense1 = hp.Int("dense1", min_value=6, max_value=512, step=12) 79 hp_dropout = hp.Float("dropout", min_value=0.1, max_value=0.9, step=0.1) 80 hp_l2 = hp.Float("l2", min_value=0.0001, max_value=0.001, step=0.0001) 81 model = SpamDectionModel( 82 hp_dense1, 83 hp_dropout, 84 hp_l2, 85 ) 86 # Adam was best so far 87 # tf.keras.optimizers.Nadam() has similar results to Adam but a bit worse. second best 88 hp_learning_rate = hp.Choice( 89 "learning_rate", 90 values=[ 91 1e-2, 92 1e-3, 93 1e-4, 94 1e-5, 95 ], 96 ) 97 model.compile( 98 loss=tf.keras.losses.BinaryCrossentropy(), 99 optimizer=tf.keras.optimizers.Adam(learning_rate=hp_learning_rate), 100 metrics=["accuracy"], 101 ) 102 103 return model 104 105 106 def remove_stopwords(input_text): 107 """ 108 Function to remove English stopwords from a Pandas Series. 109 110 Parameters: 111 input_text : text to clean 112 Output: 113 cleaned Pandas Series 114 """ 115 stopwords_list = stopwords.words("english") 116 # Some words which might indicate a certain sentiment are kept via a whitelist 117 whitelist = ["n't", "not", "no"] 118 words = input_text.split() 119 clean_words = [ 120 word 121 for word in words 122 if (word not in stopwords_list or word in whitelist) and len(word) > 1 123 ] 124 return " ".join(clean_words) 125 126 127 def change_labels(x): 128 return 1 if x == "spam" else 0 129 130 131 def load_data(): 132 data = pd.read_csv( 133 "./input/MatrixData.tsv", sep="\t", quoting=csv.QUOTE_NONE, encoding="utf-8" 134 ) 135 136 # Minimum length 137 data = data[data["message"].str.split().str.len().gt(18)] 138 # Remove unknown 139 data.dropna(inplace=True) 140 data.reset_index(drop=True, inplace=True) 141 data["label"] = data["label"].apply(change_labels) 142 143 # Remove stopwords 144 data["message"] = data["message"].apply(remove_stopwords) 145 146 # Shuffle data 147 data = data.sample(frac=1).reset_index(drop=True) 148 149 # Split data into messages and label sets 150 sentences = data["message"].tolist() 151 labels = data["label"].tolist() 152 153 # Separate out the sentences and labels into training and test sets 154 # training_size = int(len(sentences) * 0.8) 155 training_size = int(len(sentences) * 0.7) 156 training_sentences = sentences[0:training_size] 157 testing_sentences = sentences[training_size:] 158 training_labels = labels[0:training_size] 159 testing_labels = labels[training_size:] 160 161 # Make labels into numpy arrays for use with the network later 162 testing_labels_final = np.array(testing_labels) 163 training_labels_final = np.array(training_labels) 164 training_sentences_final = np.array(training_sentences) 165 testing_sentences_final = np.array(testing_sentences) 166 return ( 167 training_sentences_final, 168 testing_sentences_final, 169 training_labels_final, 170 testing_labels_final, 171 ) 172 173 174 def train_hyperparamters( 175 training_sentences_final, 176 testing_sentences_final, 177 training_labels_final, 178 testing_labels_final, 179 tuner, 180 ): 181 stop_early = tf.keras.callbacks.EarlyStopping(monitor="val_loss", patience=3) 182 tuner.search( 183 training_sentences_final, 184 training_labels_final, 185 epochs=5, 186 verbose=1, 187 validation_data=(testing_sentences_final, testing_labels_final), 188 callbacks=[hypertuner_tensorboard_callback, stop_early, progress_bar], 189 ) 190 191 # Get the optimal hyperparameters 192 best_hps = tuner.get_best_hyperparameters(num_trials=1)[0] 193 194 print( 195 f""" 196 The hyperparameter search is complete. The optimal number of units in the first densely-connected 197 layer is {best_hps.get('dense1')} and the optimal learning rate for the optimizer is {best_hps.get('learning_rate')}. 198 The optimal dropout rate is {best_hps.get('dropout')} and the optimal l2 rate is {best_hps.get('l2')}. 199 """ 200 ) 201 202 return best_hps 203 204 205 def train_model( 206 training_sentences_final, 207 testing_sentences_final, 208 training_labels_final, 209 testing_labels_final, 210 best_hps, 211 tuner, 212 ): 213 model = SpamDectionModel( 214 64, 215 0.2, 216 0, 217 ) 218 model.compile( 219 loss=tf.keras.losses.BinaryCrossentropy(), 220 optimizer=tf.keras.optimizers.Adam(), 221 metrics=["accuracy"], 222 ) 223 num_epochs = 200 224 model = tuner.hypermodel.build(best_hps) 225 history = model.fit( 226 training_sentences_final, 227 training_labels_final, 228 epochs=num_epochs, 229 verbose=1, 230 validation_data=(testing_sentences_final, testing_labels_final), 231 callbacks=[tensorboard_callback, progress_bar], 232 ) 233 val_acc_per_epoch = history.history["val_accuracy"] 234 best_epoch = val_acc_per_epoch.index(max(val_acc_per_epoch)) + 1 235 print("Best epoch: %d" % (best_epoch,)) 236 print("Average train loss: ", np.average(history.history["loss"])) 237 print("Average test loss: ", np.average(history.history["val_loss"])) 238 239 model = SpamDectionModel( 240 64, 241 0.2, 242 0, 243 ) 244 model.compile( 245 loss=tf.keras.losses.BinaryCrossentropy(), 246 optimizer=tf.keras.optimizers.Adam(), 247 metrics=["accuracy"], 248 ) 249 hypermodel = tuner.hypermodel.build(best_hps) 250 hypermodel_history = hypermodel.fit( 251 training_sentences_final, 252 training_labels_final, 253 verbose=1, 254 epochs=best_epoch, 255 validation_data=(testing_sentences_final, testing_labels_final), 256 callbacks=[ 257 hypermodel_tensorboard_callback, 258 # tf.keras.callbacks.ModelCheckpoint( 259 # filepath=checkpoint_prefix, save_weights_only=True 260 # ), 261 progress_bar, 262 ], 263 ) 264 265 print( 266 "Average train loss(hypermodel_history): ", 267 np.average(hypermodel_history.history["loss"]), 268 ) 269 print( 270 "Average test loss(hypermodel_history): ", 271 np.average(hypermodel_history.history["val_loss"]), 272 ) 273 274 return hypermodel 275 276 277 def test_model(model): 278 # Use the model to predict whether a message is spam 279 text_messages = [ 280 "Greg, can you call me back once you get this?", 281 "Congrats on your new iPhone! Click here to claim your prize...", 282 "Really like that new photo of you", 283 "Did you hear the news today? Terrible what has happened...", 284 "Attend this free COVID webinar today: Book your session now...", 285 "Are you coming to the party tonight?", 286 "Your parcel has gone missing", 287 "Do not forget to bring friends!", 288 "You have won a million dollars! Fill out your bank details here...", 289 "Looking forward to seeing you again", 290 "oh wow https://github.com/MGCodesandStats/tensorflow-nlp/blob/master/spam%20detection%20tensorflow%20v2.ipynb works really good on spam detection. Guess I go with that as the base model then lol :D", 291 "ayo", 292 "Almost all my spam is coming to my non-gmail address actually", 293 "Oh neat I think I found the sizing sweetspot for my data :D", 294 "would never click on buttons in gmail :D always expecting there to be a bug in gmail that allows js to grab your google credentials :D XSS via email lol. I am too scared for touching spam in gmail", 295 "back to cacophony ", 296 "Room version 11 when", 297 "skip 11 and go straight to 12", 298 "100 events should clear out any events that might be causing a request to fail lol", 299 "I'll help anyone interested on how to invest and earn $30k, $50k, $100k, $200k or more in just 72hours from the crypto market.But you will have to pay me my commission! when you receive your profit! if interested send me a direct message let's get started or via WhatsApp +1 (605) 953‑6801", 300 ] 301 302 spam_no_spam = [ 303 False, 304 True, 305 False, 306 False, 307 True, 308 False, 309 False, 310 False, 311 True, 312 False, 313 False, 314 False, 315 False, 316 False, 317 False, 318 False, 319 False, 320 False, 321 False, 322 True, 323 ] 324 325 # print(text_messages) 326 327 # Create the sequences 328 classes = model.predict(np.array(text_messages)) 329 330 # The closer the class is to 1, the more likely that the message is spam 331 correct = 0 332 expected = len(spam_no_spam) 333 for x in range(len(text_messages)): 334 print(f'Message: "{text_messages[x]}"') 335 print(f"Likeliness of spam in percentage: {classes[x][0]:.5f}") 336 spam = classes[x][0] >= 0.8 337 if spam: 338 print("Vote by AI: Spam") 339 else: 340 print("Vote by AI: Not Spam") 341 if spam_no_spam[x] != spam: 342 print("Model failed to predict correctly") 343 else: 344 correct = correct + 1 345 print("Model predicted correctly") 346 print("\n") 347 print(f"{correct} out of {expected} are detected correctly\n") 348 349 350 def main(): 351 tf.get_logger().setLevel("ERROR") 352 print("TensorFlow version:", tf.__version__) 353 print("[Step 1/6] Loading data") 354 ( 355 training_sentences_final, 356 testing_sentences_final, 357 training_labels_final, 358 testing_labels_final, 359 ) = load_data() 360 361 model = SpamDectionHyperModel() 362 tuner = kt.Hyperband( 363 model, 364 objective="val_accuracy", 365 max_epochs=100, 366 directory="hyper_tuning", 367 project_name="spam-keras", 368 ) 369 print("[Step 3/6] Tuning hypervalues") 370 best_hps = train_hyperparamters( 371 training_sentences_final, 372 testing_sentences_final, 373 training_labels_final, 374 testing_labels_final, 375 tuner, 376 ) 377 378 print("[Step 4/6] Training model") 379 model = train_model( 380 training_sentences_final, 381 testing_sentences_final, 382 training_labels_final, 383 testing_labels_final, 384 best_hps, 385 tuner, 386 ) 387 388 print("[Step 5/6] Saving model") 389 export_path = f"./models/spam_keras_{time.time()}" 390 print("Exporting trained model to", export_path) 391 392 model.save(export_path) 393 394 print("[Step 6/6] Testing model") 395 test_model(model) 396 397 398 if __name__ == "__main__": 399 main()