matrix-spam-ml

git clone git://archive.git.mtrnord.blog/MTRNord/matrix-spam-ml.git
Log | Files | Refs | Submodules | README | LICENSE

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()