Doğal Dil İşleme (NLP) - Model Eğitim İşlemleri
Ham or Spam Veri Seti Eğitimi
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.
- Veri setine Keşifsel Veri Analizi (EDA) yapılır.
- Bütün cümleler küçük harfe çevirilir.
- Noktalama işaretleri kaldırılır.
- Sayısal işaretler kaldırılır.
- Fazla boşluklar kaldırılır.
- Stop words (gereksiz kelimeler) kaldırılır.
- Tokenize işlemi yapılarak sonrasında Lemma veya stemma (ekleri kaldırıp kökleri bulma) işlemi uygulanır.
- Verilerde en çok kullanılan kelimeleri görselleştirme işlemi.
- Metinsel verilere vektörleştirme işlemi uygulanır.
- Makine öğrenmesi kullanılarak model eğitme ve kaydetme işlemi yapılır.
#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.'