Doğal Dil İşleme (NLP) - Model Eğitim İşlemleri

Ham or Spam Veri Seti Eğitimi

Yayın tarihi :10-Şub-22
Bölüm kodlarını ve/veya veri setlerini indir.

Mail hesabımıza gelen maillerin bazıları spam klasörüne taşınmaktadır. Peki bunun spam olduğu nereden anlaşılmaktadır? Bu çalışmamamızda elimizde bulunan "ham or spam veri seti" kullanarak model oluşturacağız ve böylece de spam maillerle gerçek mailleri ayırma işlemi yapacağız.

Amaç: Yazılan bir yazının/yorumun/mailin spam olup olmadığını tahmin etmek amaçlanmaktadır. 

Örnek proje üzerinde çalışmaya başlamadan önce NLP projelerinde kullanacağımız çalışma sıralamasını listeleyelim.

#Gerekli kütüphaneler içe aktarılır.
import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
%matplotlib inline
import warnings
warnings.filterwarnings("ignore")

from sklearn.model_selection import train_test_split
from sklearn.naive_bayes import MultinomialNB
from xgboost import XGBClassifier
from sklearn.metrics import accuracy_score,precision_score,recall_score,f1_score
from sklearn.metrics import confusion_matrix,classification_report

from nltk.tokenize import word_tokenize
from langdetect import detect
from nltk.corpus import stopwords
import stylecloud as sc
from IPython.display import Image
from sklearn.feature_extraction.text import CountVectorizer, TfidfVectorizer
from nltk.stem import SnowballStemmer,WordNetLemmatizer
#Veri seti okunur
df=pd.read_csv("spam.csv",encoding='iso-8859-1')

Keşifsel Veri Analizi (EDA)

#Veri seti üzerinden "v1" ve "v2" sütunları kullanılacağından "df" değerini bu 2 sütuna indirelim.
df=df[["v1","v2"]]

#Sütun isimlerini değiştirelim
df.columns=["status","text"]
#İlk 5 satır değerleri
df.head()

Çıktı:

#Son 5 satır değerleri
df.tail()

Çıktı:

#Veri seti hakkında genel bilgilendirme
df.info()

Çıktı:

<class 'pandas.core.frame.DataFrame'>
RangeIndex: 5572 entries, 0 to 5571
Data columns (total 2 columns):
 #   Column  Non-Null Count  Dtype 
---  ------  --------------  ----- 
 0   status  5572 non-null   object
 1   text    5572 non-null   object
dtypes: object(2)
memory usage: 87.2+ KB
#Veri setinde boş değer kontrolü
df.isnull().sum()

Çıktı:

status    0
text      0
dtype: int64
#Status değerlerinin dağılım grafiği
sns.countplot(df.status)

Çıktı:

NLP İşlemleri

#Bütün cümleler küçük harfe çevirilir.
df["text"]=df["text"].str.lower()

#Noktalama işaretleri kaldırılır.
df["text"]=df["text"].str.replace("[^\w\s]","")

#Sayısal işaretler kaldırılır.
df["text"]=df["text"].str.replace("\d+","")

#Fazla boşluklar kaldırılır.
df["text"]=df["text"].str.replace("\n","").replace("\r","")
#"text" sütunundaki verilerin hangi dilde oldukları bulunur.
#Bulunan diller yeni oluşturulan "lang" sütununa atanır.
#Bazı satırlarda dil keşefedilmesi hata vermememsi için try-except kullanıldı
df["lang"]=""
for i in range(len(df["text"])):
    try:
        df["lang"][i]=detect(df["text"][i])
    except:
        df["lang"][i]="Dil bulunamadı"
#Yeni oluşturulan lang sütunu(text içinde hangi dillerin olduğu) veri dağılımı. 
df["lang"].value_counts()

Çıktı:

en                4971
af                  68
it                  54
cy                  49
fr                  47
nl                  43
so                  41
no                  36
sk                  30
ca                  29
id                  26
da                  19
tl                  18
sv                  18
pl                  16
hr                  15
et                  14
sl                  11
sw                  10
ro                   9
sq                   8
es                   7
pt                   7
de                   6
cs                   5
tr                   5
Dil bulunamadı       3
lv                   2
fi                   2
lt                   2
vi                   1
Name: lang, dtype: int64
#Farklı dillerde olan "text" değerlerini veri setinden kaldıralım
df=df[df["lang"]=="en"].reset_index(drop=True)
#Son durumda sadece ingilizce cümleler kalmış oldu.

#"lang" sütunu ile işimiz kalmadığından onu da kaldırabiliriz.
del df["lang"]
#Stop words (gereksiz kelimeler) ingilizce kelimeler kaldırılır.
stopwords_list=stopwords.words("english")
df['text']=df['text'].apply(lambda x:" ".join([i for i in word_tokenize(x)  if i not in stopwords_list]))
#Tokenize işlemi yapılarak sonrasında Lemma veya stemma (ekleri kaldırıp kökleri bulma) işlemi uygulanır.
#Kesin sonuç almak için ve veri seti çok büyük olmadığından Lemmatizer uygulanabilir.
WNL=WordNetLemmatizer()
df['text']=df['text'].apply(lambda x:" ".join([WNL.lemmatize(x) for x in word_tokenize(x)]))

En çok Geçen Kelimeler Görseli Oluşturma

#Tüm yorumlar tek bir string ifadede toplanır
all_ham_comments=""
all_spam_comments=""
#1 yıldızlı yorumlar
for i in df[df["status"]=="ham"]["text"]:
    all_ham_comments+=i
#5 yıldızlı yorumlar
for i in df[df["status"]=="spam"]["text"]:
    all_spam_comments+=i   
#Ham yorumlarda en çok geçen ilk 1000 kelime
sc.gen_stylecloud(
    text=all_ham_comments,
    size=500,
    max_words=1000,
    icon_name="fas fa-check-circle",
    output_name="ham_comments.png",
)
Image(filename="ham_comments.png")

Çıktı:

#Spam yorumlarda en çok geçen ilk 1000 kelime
sc.gen_stylecloud(
    text=all_spam_comments,
    size=500,
    max_words=1000,
    icon_name="fas fa-ban",
    output_name="all_spam_comments.png",
)
Image(filename="all_spam_comments.png")

Çıktı:

Model Hazırlık ve Eğitme İşlemi

#Metinsel verilere vektörleştirme işlemi uygulanır.
x=df["text"]
y=df["status"]

#train ve test verileri ayrılır.
x_train,x_test,y_train,y_test=train_test_split(x,y,random_state=13,test_size=.2)
#CountVectorizer tanımlanır.
vect=CountVectorizer()

#x_train ve x_test verilerine vektörel dönüşüm uygulayalım
x_train_dtm=vect.fit_transform(x_train)
#x_test_dtm verilerindeki sütun sayısı x_train_dtm ile aynı olması için fit_transfor değil transform kullanılır.
x_test_dtm=vect.transform(x_test)

#Boyutlarını inceleyelim
x_train_dtm.shape,x_test_dtm.shape

Çıktı:

((3976, 6623), (995, 6623))
#x_train_dtm tablo halinde görüntüleme
pd.DataFrame(data=x_train_dtm.toarray(),columns=vect.get_feature_names()).sample(5)

Çıktı:

#x_test_dtm tablo halinde görüntüleme
pd.DataFrame(data=x_test_dtm.toarray(),columns=vect.get_feature_names()).sample(5)

Çıktı:


#MultinomialNB ataması yapılır
M=MultinomialNB()

#Model Öğrenme işlemi yapılır
M.fit(x_train_dtm,y_train)

#Model test etme işlemi yapılır.
predict_M=M.predict(x_test_dtm)
#confusion_matrix incelemesi
confusion_matrix(y_test,predict_M)

Çıktı:

array([[840,  12],
       [ 11, 132]], dtype=int64)
#Sınıflandırma raporu
print(classification_report(y_test,predict_M))

Çıktı:

              precision    recall  f1-score   support

         ham       0.99      0.99      0.99       852
        spam       0.92      0.92      0.92       143

    accuracy                           0.98       995
   macro avg       0.95      0.95      0.95       995
weighted avg       0.98      0.98      0.98       995

Sınıflandırma raporunda da görüldüğü gibi MultinominalNB sınıflandırma algoritması ile girilen yazının/yorumun/mailin ham veya spam olup olmadığını doğru tahmin etme yüzdesi accuracy değeri göz önüne alınırsa %98 olarak tahmin edilmiş oldu. 

Aynı verisetine farklı bir algoritma olan XGBClassifier ile tekrar modelleme işlemi yaparak doğruluk değerini gözlemleyelim.

#XGBClassifier ataması yapılır
XGBC=XGBClassifier()

#Model Öğrenme işlemi yapılır
XGBC.fit(x_train_dtm,y_train)

#Model test etme işlemi yapılır.
predict_XGBC=XGBC.predict(x_test_dtm)
#confusion_matrix incelemesi
confusion_matrix(y_test,predict_XGBC)

Çıktı:

array([[844,   8],
       [ 19, 124]], dtype=int64)
#Sınıflandırma raporu
print(classification_report(y_test,predict_XGBC))

Çıktı:

              precision    recall  f1-score   support

         ham       0.98      0.99      0.98       852
        spam       0.94      0.87      0.90       143

    accuracy                           0.97       995
   macro avg       0.96      0.93      0.94       995
weighted avg       0.97      0.97      0.97       995

Sınıflandırma raporunda da görüldüğü gibi XGBClassifier sınıflandırma algoritması ile girilen yazının/yorumun/mailin ham veya spam olup olmadığını doğru tahmin etme yüzdesi accuracy değeri göz önüne alınırsa %97 olarak tahmin edilmiş oldu.  Farklı sınıflandırma algoritmaları girilerek doğruluk yüzdesi artırılabilir.

Modeli Kaydetme ve Kullanma İşlemi

import pickle
#MultinomialNB modeli kaydedildi
pickle.dump(M, open('HamorSpam.pkl', 'wb'))

#CountVectorizer sonucunda oluşan sütun sayısını korumak için kelimelerinde kaydedilmesi gerekir.
vocab = vect.vocabulary_
pickle.dump(vocab, open('Vocab.pkl', 'wb'))
#Kaydedilen model okundu.
loaded_model=pickle.load(open(r"HamorSpam.pkl","rb"))

#Kaydedilen kelimeler okundu
loaded_vocab=pickle.load(open("Vocab.pkl","rb"))
def comment_prediction(comment):
    import re
    import googletrans

    #yorum dili ingilizce haricinde bir dilse ingilizceye çevir
    lang=detect(comment) 
    if lang!="en":
        comment=googletrans.Translator().translate(comment).text

    #yorum küçük harfe dönüştürüldü
    comment=comment.lower() 

    #yorum noktalama işaretleri kaldırıldı
    comment=re.sub("[^\w\s]","",comment) 

    #yorum sayısal ifadeler kaldırıldı
    comment=re.sub("\d","",comment) 

    #yorum fazla boşluklar kaldırıldı
    comment=comment.replace("\n","").replace("\r","") 

    #stopword listesi oluşturuldu ve yorumdan kaldırıldı
    stopwords_list=stopwords.words("english") 
    comment=" ".join([i for i in word_tokenize(comment)  if i not in stopwords_list]) 

    #yoruma lemmatize yapıldı
    comment=" ".join([WNL.lemmatize(x) for x in word_tokenize(comment)])

    #yoruma modele uygun vektörleştirme işlemi uygulandı
    vect_predict=CountVectorizer(vocabulary=loaded_vocab)
    comment_transform=vect_predict.fit_transform([comment])

    #tahmin sonucu 
    predicted=loaded_model.predict(comment_transform)
    return "Yorumunuz {} yıldız olarak tahmin edildi.".format(predicted[0])
#Test işlemi
comment1="It was a very bad purchase, the shipping was terrible."
comment_prediction(comment1)

Çıktı:

'Yorumunuz ham olarak tahmin edildi.'
#Test işlemi
comment2="Free entry to a weekly contest to win the world cup final"
comment_prediction(comment2)

Çıktı:

'Yorumunuz spam olarak tahmin edildi.'
#Test işlemi
comment3="Ücretsiz bir şekilde üye olun, bu linke tıklamanız yeterlidir."
comment_prediction(comment3)

Çıktı:

'Yorumunuz spam olarak tahmin edildi.'
Paylaş:

Yorum Yap (*Yorumunuza kod eklemek isterseniz Kod Parçacığı Ekle butonuna tıklayarak ekleyebilirsiniz.)

Yorumlar

Henüz hiç yorum yapılmamış, ilk yorum yapan sen ol.