ในยุคสมัยที่การส่งMailเป็นอีกหนึ่งวิธีที่สำคัญในการติดต่อสื่อสาร Mail นั้นนอกจากMailที่เราควรจะได้รับจริงๆ ก็ยังมี Mail ที่เป็น Spam ซึ่งเราอาจจะไม่ต้องการได้รับด้วย ดังนั้นจาก Mail จำนวนมาก ผมจึงได้พบว่าการใช้ TF-IDF และ Logistic Regression สามารถช่วยเราจำแนกMailเหล่านั้นว่าเป็น Spam(ไม่อยากได้รับ) หรือ Ham(อยากได้รับ)ได้

บทความนี้ผมจึงจะพามาดู การใช้ TF-IDF และ Logistic Regression ใน Python โดยรันผ่าน Visual Studio Code กัน ซึ่งผมได้เนื้อหา การใช้งานโค้ด และ Datasetจาก Github Spam-Mail-Detection-using-TF-IDF

ก่อนจะเริ่มกันผมอยากขออธิบาย TF-IDF และ Logistic Regression กันก่อน
TF-IDF หรือ Term Frequency - Inverse Document Frequency
เป็นเทคนิคที่ใช้ ให้ค่าน้ำหนัก กับคำแต่ละคำในเอกสาร โดยพิจารณาว่าคำนั้น

  • พบในเอกสารหนึ่งบ่อยแค่ไหน (TF - Term Frequency)
  • พบในเอกสารทั้งหมดบ่อยแค่ไหน (IDF - Inverse Document Frequency)

หาด้วยสูตร TF-IDF = TF × IDF
​TF = จำนวนคำทั้งหมดในเอกสาร/จำนวนครั้งที่คำปรากฏ
IDF = log(จำนวนเอกสารที่มีคำนั้น/จำนวนเอกสารทั้งหมด)
ตัวอย่างเช่น คำว่า "ฟรี" มีTF(ความถี่ในการใช้) = 0.05 มีIDF(สำคัญแค่ไหน) = 0.3 ก็จะได้ TF-IDF อยู่ที่ 0.15

Logistic Regression เป็น Supervised Learning Algorithm ที่ใช้สำหรับ Binary Classification (เช่น Spam vs. Not Spam) โดยทำนายค่า ความน่าจะเป็น (probability) ของแต่ละคลาส เช่น

  • ความน่าจะเป็น > 0.5 → โมเดลทำนายว่าเป็น Spam
  • ความน่าจะเป็น < 0.5 → โมเดลทำนายว่าเป็น Not Spam

Dataset ที่ผมจะนำมาใช้มาจากGithubที่กล่าวถึงไปก่อนหน้า โดยใน Dataset จะประกอบด้วย 5572 แถว แต่ละแถวประกอบด้วย 2 คอลัมน์ ดังนี้
[, 1] Category ประเภทของMailว่าเป็น Spamหรือไม่(Spam or Ham)
[, 2] Message เนื้อหาข้อความในMail

ขั้นตอนที่ 1: นำเข้า library ที่จำเป็น

ตัวอย่างการนำเข้าlibrary สามารถ นำเข้าได้ตามนี้เลย

import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score

ขั้นตอนที่ 2: นำเข้าข้อมูล mail_data

ตัวอย่างของการนำเข้าข้อมูลตามนี้เลย

df = pd.read_csv("mail_data.csv")
df.head()

ตัวอย่างผลที่ได้จากการรัน
Image description

ขั้นตอนที่ 3: จัดการข้อมูล

เริ่มจากแทนที่ข้อมูลที่เป็น null ให้เป็น null string ก่อนแล้วสร้างคอลัมน์ใหม่ category ที่เปลี่ยนข้อมูลจากคอลัมน์ที่มีอยู่ Category ที่เป็นคำอย่างSpam,Ham ให้เป็น0,1ตามลำดับ

data = df.where((pd.notnull(df)),'')
data['category'] = data['Category'].apply(lambda x: 1 if x == 'ham' else 0)
print(data)

ตัวอย่างผลที่ได้จากการรัน
Image description

ลบคอลัมน์ Category เดิมที่เป็นคำออกเพื่อใช้เฉพาะคอลัมน์ใหม่ที่เป็นตัวเลขแทน

data.drop(columns=['Category'])

ตัวอย่างผลที่ได้จากการรัน
Image description

แบ่งข้อมูลออกเป็นสำหรับ train และ test โดยใช้ train_test_split จาก scikit-learn

X = data['Message']
y = data['category']
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

ขั้นตอนที่ 4: แปลงข้อความเป็นเวกเตอร์ TF-IDF

ทำการเปลี่ยนข้อความใน Message ให้เป็นเวกเตอร์เพื่อใช้คำนวนหาคะแนนของ TF-IDF ของแต่ละคำ

#Transorm data into feature vectors
# Creating a TfidfVectorizer for feature extraction
feature_extraction = TfidfVectorizer(min_df=1, stop_words='english', lowercase=True)

# Transforming the training data into TF-IDF feature matrix
X_train_feature = feature_extraction.fit_transform(X_train)

# Transforming the test data using the same vectorizer as used for training data
X_test_feature = feature_extraction.transform(X_test)

# Converting the data type of training labels to integer
y_train = y_train.astype('int')

# Converting the data type of test labels to integer
y_test = y_test.astype('int')

print(X_train_feature) # each message gets score by Tfidvectorixer

ตัวอย่างผลที่ได้จากการรัน
Image description

ขั้นตอนที่ 5: Train Model โดยใช้ Logistic Regression

#creating the model
model = LogisticRegression()
model.fit(X_train_feature, y_train)

ขั้นตอนที่ 6: การประเมิน

ทำการประเมินความแม่นยำของ Model ทั้งจากข้อมูลสำหรับ train และ test

การประเมินความแม่นยำจากข้อมูลสำหรับ train

# Predicting labels on the training data using the trained model
pred_on_training_data = model.predict(X_train_feature)

# Calculating accuracy on the training data by comparing predicted labels with actual labels
acc_on_training_data = accuracy_score(y_train, pred_on_training_data)

print("Accuracy on training data:",acc_on_training_data)

ตัวอย่างผลที่ได้จากการรัน
Image description

การประเมินความแม่นยำจากข้อมูลสำหรับ test

pred_on_test_data = model.predict(X_test_feature)
acc_on_test_data = accuracy_score(y_test, pred_on_test_data)

print("Accuracy on test data:",acc_on_test_data)

ตัวอย่างผลที่ได้จากการรัน
Image description

ขั้นตอนที่ 7: ลองให้ทำนายดู

ให้ Model ลองทำนายว่า Mailที่ใส่ไปจะเป็น Spamหรือไม่

input_your_mail = ["Hi frnd, which is best way to avoid missunderstding wit our beloved one's?"]
input_data_features = feature_extraction.transform(input_your_mail)
prediction = model.predict(input_data_features)

if(prediction[0] == 1):
  print("Ham mail")
else:
  print("Spam mail")

ตัวอย่างผลที่ได้จากการรัน
Image description

ผลออกมาว่าเป็น Ham mailแสดงว่า ผลทำนายคือไม่ใช่Spam mail หรือก็คือเป็นMailที่สำคัญ

ตัวอย่างเพิ่มเติม

ทีนี้ผมจะลองเปลี่ยนข้อมูลในDataset ให้เป็นภาษาไทยดู โดยในDataset จะมีทั้งหมด 245 แถวแต่ละแถวประกอบด้วย 2 คอลัมน์ ดังนี้
[, 1] Category ประเภทของMailว่าเป็น Spamหรือไม่(Spam or Ham)
[, 2] Message เนื้อหาข้อความในMailเป็นภาษาไทย

ขั้นตอนที่ 1: นำเข้า library ที่จำเป็น

ตัวอย่างการนำเข้าlibrary สามารถ นำเข้าได้ตามนี้เลย

import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score

ขั้นตอนที่ 2: นำเข้าข้อมูล mail_data

ตัวอย่างของข้อมูลได้ตามนี้เลย โดยจะเพิ่มเติมจากตัวอย่างเก่าคือ on_bad_lines="skip" ที่จะข้ามข้อความที่ไม่สามารถใช้ได้ออกไป เพราะเพื่อลดปัญหาที่จะเกิดเมื่อเป็นภาษาไทย

df = pd.read_csv("mail_data.csv ,on_bad_lines="skip")
df.head()

ตัวอย่างผลที่ได้จากการรัน
Image description

ขั้นตอนที่ 3: จัดการข้อมูล

เริ่มจากแทนที่ข้อมูลที่เป็น null ให้เป็น null string ก่อนแล้วสร้างคอลัมน์ใหม่ category ที่เปลี่ยนข้อมูลจากคอลัมน์ที่มีอยู่ Category ที่เป็นคำอย่างSpam,Ham ให้เป็น0,1ตามลำดับแบบตัวอย่างแรกเลย

data = df.where((pd.notnull(df)),'')
data['category'] = data['Category'].apply(lambda x: 1 if x == 'ham' else 0)
print(data)

ตัวอย่างผลที่ได้จากการรัน
Image description

แต่ผลที่ได้ออกมาเนื่องจากการที่เปลี่ยนชุดข้อมูลไปแล้ว และเพิ่มการข้ามข้อความที่ใช้ไม่ได้ในขั้นตอนที่2 จึงได้ข้อความที่ใช้ได้มาเพียง 215 จาก 245 แถว

ขั้นตอนที่ 4: แปลงข้อความเป็นเวกเตอร์ TF-IDF

ทำการเปลี่ยนข้อความใน Message ให้เป็นเวกเตอร์เพื่อใช้คำนวนหาคะแนนของ TF-IDF ของแต่ละคำ

#Transorm data into feature vectors
# Creating a TfidfVectorizer for feature extraction
feature_extraction = TfidfVectorizer(min_df=1, stop_words='english', lowercase=True)

# Transforming the training data into TF-IDF feature matrix
X_train_feature = feature_extraction.fit_transform(X_train)

# Transforming the test data using the same vectorizer as used for training data
X_test_feature = feature_extraction.transform(X_test)

# Converting the data type of training labels to integer
y_train = y_train.astype('int')

# Converting the data type of test labels to integer
y_test = y_test.astype('int')

print(X_train_feature) # each message gets score by Tfidvectorixer

ตัวอย่างผลที่ได้จากการรัน
Image description

ขั้นตอนที่ 5: Train Model โดยใช้ Logistic Regression

#creating the model
model = LogisticRegression()
model.fit(X_train_feature, y_train)

ขั้นตอนที่ 6: การประเมิน

ทำการประเมินความแม่นยำของ Model ทั้งจากข้อมูลสำหรับ train และ test

การประเมินความแม่นยำจากข้อมูลสำหรับ train

# Predicting labels on the training data using the trained model
pred_on_training_data = model.predict(X_train_feature)

# Calculating accuracy on the training data by comparing predicted labels with actual labels
acc_on_training_data = accuracy_score(y_train, pred_on_training_data)

print("Accuracy on training data:",acc_on_training_data)

ตัวอย่างผลที่ได้จากการรัน
Image description

การประเมินความแม่นยำจากข้อมูลสำหรับ test

pred_on_test_data = model.predict(X_test_feature)
acc_on_test_data = accuracy_score(y_test, pred_on_test_data)

print("Accuracy on test data:",acc_on_test_data)

ตัวอย่างผลที่ได้จากการรัน
Image description

ขั้นตอนที่ 7: ลองให้ทำนายดู

ให้ Model ลองทำนายว่า Mailที่ใส่ไปจะเป็น Spamหรือไม่ โดยรอบนี้ใส่ไปเป็นภาษาไทยและเขียนให้เป็น Spam เพื่อทดสอบดู

input_your_mail = ["คุณได้รับรางวัลเงินสด คลิกที่นี่เพื่อรับสิทธิ์ทันที! พร้อมลุ้นรับของแถม"]
input_data_features = feature_extraction.transform(input_your_mail)
prediction = model.predict(input_data_features)

if(prediction[0] == 1):
  print("Ham mail")
else:
  print("Spam mail")

ตัวอย่างผลที่ได้จากการรัน
Image description

ผลออกมาว่าเป็น Spam mailแสดงว่า ผลทำนายคือเป็นMailที่ไม่สำคัญนั่นเอง

สรุปผล

สำหรับบทความนี้จากที่ผมลองตรวจจับSpam Mailด้วยวิธี TF-IDF เพื่อหาความสำคัญของแต่ละคำ และ Model จาก Logistic Regression ก็ถือว่าให้ผลการทำนายที่ดี ซึ่งสามารถลองนำไปใช้ได้จริงกับภาษาไทยด้วยถึงแม้ว่าต้นแบบจะเป็นภาษาอังกฤษ ก็ตาม ถ้าสนใจก็ลองเอาไปใช้ดูได้เลย

Reference

Spam-Mail-Detection-using-TF-IDF Github:
https://github.com/Ananya01Agrawal/Spam-Mail-Detection-using-TF-IDF
mail_data_thai.csv (ผมสร้างเองเพื่อลองใช้แบบภาษาไทย):
https://github.com/PoundPon/DummyDataset