derin-ogrenme-ile-konu-tahmini

Derin Öğrenme ile Konu Tahmini

Merhaba VBO okuyucuları, bugün size derin öğrenme ile konu tahmini hakkında bilgi vermeye çalışacağım. Bu günlerde evde kalmamız bizler için hem sorun hem de kendimizi geliştirmek için bir fırsat. Bu günleri iyi bir şekilde değerlendirmek gerekiyor.

Daha önce yazmış olduğum Türkçe Metinlerde Konu Tahmini yazısında hem denetimli hem de denetimsiz makine öğrenmesi modelleri oluşturup performanslarını ölçmüştük. Bu defa LSTM ile bir model kurup başarısını değerlendireceğiz. Eğer modelimizin başarısı düşük çıkarsa verimizin sınıflarını yeniden örneklendirerek modelimizi tekrar eğiteceğiz. Yeniden örnekleme metodu dengesiz dağılmış sınıfların verilerini birbirine yakışlaştırarak modelin başarısını artmasını sağlayan çok kullanışlı bir yöntemdir. Ben burada sadace verimi yeniden örneklendireceğim başka hiçbir hiper parametreyi değiştirmeyeceğim.

Doğal dil işleme çalışmalarında konu tahmini ve metin sınıflandırma işlemleri önemli bir yer tutmaktadır. Verilerin hangi konu hakkında yazıldığının bilinmesi o konuda çalışan kişiler için verinin analiz edilmesinde, veriyi temsil eden kelimelerin tanımlanmasında büyük kolaylık sağlamaktadır. Bu nedenle konu tahmini ve metin sınıflandırma işlemini manuel yapmak bir noktadan sonra çok fazla efektif olmamakta ve fazladan zaman kaybına neden olmaktadır. Bu nedenle yüksek başarıya sahip bir model ile gelen verinin sınıflandırılması analizler açısından kolaylık sağlamakta ve zamandan tasarruf etmemizi sağlamaktadır.

Gelin verimizdeki sınıfların dağılımına bakalım ve daha sonra veriyi temizleyip model için uygun hale getirelim.

Bu çalışmamda Google Colab’ı kullandım GPU desteği olduğu için. Verinin boyutu yüksek olduğundan dolayı GPU’dan yararlanmak istedim, python versiyonu 3.6.9, tensorflow versiyonu 2.0. Aşağıdaki butona tıklayarak veriyi indirebilirsiniz.

Veri Ön işleme

from google.colab import drive

drive.mount('/content/drive')

Önce Google drive’ımda bir klasör açarak verimi drive’ıma yükledim. Daha sonra yukarıdaki kodu çalıştırarak klasör oluşturduğum drive hesabımla colab sayfamı birbirine bağladım. Bu sayede drive’ımda bulunan dosyaya erişebilmekteyim.

import pandas as pd
dataset = pd.read_excel('/content/drive/My Drive/uygulama/topic_modeling_colab.xlsx')

dataset.head()

dataset = dataset.drop(['Unnamed: 1'], axis=1)
Görsel 1: Veriye İlk Bakış

Daha sonra verimi drive’daki dosyadan içeriye aktararak verinin ilk haline baktım. Veride kullanmayacağım “Unnamed: 1” sütununu veriden kaldırdım işlemlerimi kolaylaştırması açısından.

dataset.describe()
Görsel 2: Veriye ilk bakış.

Yukarıda da gördüğünüz verimiz 25 sınıfa sahip ve 533 bin satırdan oluşmakta. Gelin sınıflarımızın dağılımını görselleştirerek inceleyelim.

from collections import Counter
import seaborn as sns
import matplotlib.pyplot as plt

sum_ = Counter(dataset['Class']).values()
class_ = Counter(dataset['Class']).keys()

df_class = pd.DataFrame(zip(class_,sum_), columns = ['Class', 'Toplam'])

plt.figure(figsize=(20,10))
sns.set(color_codes=True)
sns.distplot(df_class.Toplam,rug = True, kde_kws={"color": "k"}, hist_kws={"color" : "r"});
plt.savefig('/content/drive/My Drive/uygulama/sınıfdagilim2.png')

plt.figure(figsize=(30,10))
sns.set(color_codes=True)
#sns.lineplot(x = "Class", y = "Toplam", data = df_class, ax = ax[0,0]);
sns.barplot(x = "Class", y = "Toplam", data = df_class, palette="vlag")
plt.savefig('/content/drive/My Drive/uygulama/sınıfdagılım.png')
Şekil 1: Sınıfların Dağılımı
Şekil 2: Sınıfların Görselleştirilmesi

Önce her bir sınıfta kaç adet veri var Counter fonksiyonu yardımıyla saydırıp bir dataframe yapısına getirdim. Daha sonra seaborn kütüphanesi ile sınıfların dağılımına ve her bir sınıfın veri sayısını görselleştirerek inceledim. Gördüğünüz gibi sınıfların sahip olduğu veri sayısı birbirinden çok farklı bakalım modelimizi nasıl etkileyecek.

dataset['Text'] = dataset['Text'].apply(lambda x: x.lower())
dataset['Text'] = dataset['Text'].apply(lambda x: re.sub("[,'\.!?();:$%&#]", '', x))
dataset['Text'] = dataset['Text'].apply(lambda x: x.strip())

import nltk
nltk.download('stopwords')

WPT = nltk.WordPunctTokenizer()
stop_word_list = nltk.corpus.stopwords.words('turkish')

def stopword_extraction(values):
    wordFilter = [word for word in values.split() if word not in stop_word_list]
    notStopword = " ".join(wordFilter)
    return notStopword
 
dataset['Text'] = dataset['Text'].apply(lambda x: stopword_extraction(x))

Daha sonra bütün verilerimizi küçük harf yapıp içerisinde varsa noktalama işaretlerini temizledim. Daha sonra nltk kütüphanesi içerisinde bulunan Türkçe stopword’leri verilerimiz içerisinden çıkardım.

from keras.preprocessing.text import Tokenizer
from keras.preprocessing.sequence import pad_sequences

tokenizer = Tokenizer(num_words=10000)
tokenizer.fit_on_texts(dataset.Text)

Daha sonra Tokenizer ile veri setimde kullanılan en çok 10000 kelime ile bir sözlük oluşturdum.

import numpy as np

num_tokens = [len(tokens) for tokens in dataset['Text']]
num_tokens = np.array(num_tokens)
max_tokens = np.mean(num_tokens) + 2 * np.std(num_tokens)
max_tokens = int(max_tokens)

Oluşturacağımız model için verilerimizi aynı boyuta getirmemiz gerekiyor. Burada her bir cümlenin uzunluğunu bir liste içerisinde tutup bu listenin ortalamasını ve standart sapmasını alarak bir cümle uzunluğu hesapladım.

X = tokenizer.texts_to_sequences(dataset['Text'])
X_pad = pad_sequences(X, maxlen=max_tokens)

Önce cümleleri sözlükteki kelimelerin karşılığındaki sıralama değeriyle değiştirdim daha sonra da hesapladığım cümle uzunluğu ile verileri bu uzunluğa göre ayarladım.

Y = pd.get_dummies(dataset['Class']).values

from sklearn.model_selection import train_test_split
x_train, x_test, y_train, y_test = train_test_split(X_pad, Y, test_size = 0.25, random_state = 42)

Sınıflarımı ise pandas kütüphanesini kullanarak kategorik hale getirdim. get_dummies fonksiyonu her bir sınıfı 0 ve 1’lerle ifade ederek verinin tipini kategorik hale getirir. Yani örneğin bir veri eğer “law” sınıfına aitse “law” yerine yirmü üç tane 0 bir tane 1 yazarak liste haline getirir. Daha sonra verimi eğitim ve test verisi olarak ikiye ayırdım.

Model Oluşturma ve Eğitme

from keras.models import Sequential
from keras.layers import Dense, Embedding,LSTM, Dropout
from keras.optimizers import Adam

num_labes = len(dataset.Class.unique())
embedding_size = max_tokens

model = Sequential()
model.add(Embedding(input_dim=10000,
                    output_dim=embedding_size,
                    input_length=max_tokens,
                    name='embedding_layer'))
model.add(LSTM(units=128,dropout = 0.2, return_sequences=True))
model.add(LSTM(units=128,dropout = 0.2, return_sequences=False))
model.add(Dense(num_labes, activation = 'softmax'))
model.compile(loss = 'categorical_crossentropy', optimizer = "adam", metrics=['accuracy'])
model.summary()

sequential_1″
_____________________________________________________
Layer (type) Output Shape Param #
===============================================================
embedding_layer (Embedding) (None, 33, 33) 330000
_____________________________________________________
lstm_1 (LSTM) (None, 33, 128) 82944
_____________________________________________________
lstm_2 (LSTM) (None, 128) 131584
_____________________________________________________
dense_1 (Dense) (None, 25) 3225
================================================================
Total params: 547,753 Trainable params: 547,753 Non-trainable params: 0

Modelimi oluştururken bir embedding katmanı iki lstm katmanı ve bir çıkış katmanı kullandım. Çıkış katmanımda çok sınıflı bir veri kullandığım için aktivasyon fonksiyonu olarak softmax foksiyonunu kullandım. Daha sonra loss fonksiyonu olarak categorical crossentropy ve optimizasyon algoritması olarak Adam algoritmasını metric olarak da accuracy kullandım.

history = model.fit(x_train, y_train, validation_split=0.20, epochs=20,batch_size=1024)

Epoch 1/20 319908/319908 [==============================] – 39s 122us/step – loss: 2.1893 – accuracy: 0.3812 – val_loss: 1.6872 – val_accuracy: 0.5118
Epoch 20/20 319908/319908 [==============================] – 37s 117us/step – loss: 1.1434 – accuracy: 0.6354 – val_loss: 1.3488 – val_accuracy: 0.5889

odelimi 20 epoch eğittim. Eğittikten sonra modelin eğitim verisiyle başarısı 0.63 olarak ölçüldü. Bir de test verisiyle modelimizi değerlendirelim.

loss, acc = model.evaluate(x_test, y_test)
print(loss,acc)

133296/133296 [==============================] – 44s 334us/step

1.3627658050438891 0.5872344374656677

Veri Setini Yeniden Örnekleme

Gördüğünüz gibi veri setimizdeki sınıflarımızda herhangi bir ayarlama yapmadan modelimizi train ettiğimizde biraz başarısı düşük çıktı. Veri setimiz dengesiz bir dağılıma sahip olduğu için modelimiz iyi bir şekilde sınıfları ayıramadı. Gelin veri setimizi yeniden örnekleyerek tekrar train edelim ve başarısını tekrar değerlendirelim.

from imblearn.under_sampling import RandomUnderSampler

rus = RandomUnderSampler(random_state = 0, sampling_strategy = "majority")
x_rus, y_rus = rus.fit_resample(X_pad, Y)

Burada RandomUnderSampler sınıfını kullanarak verimizi yeniden örnekliyoruz. RandomUnderSampler sınıfı veri içerisinde bulunan sınıfları belirleyeceğiniz sampling_strategy değerine göre yeniden örnekler. Eğer verisetiniz çok fazla dataya sahipse bu yöntemi kullanabilirsiniz çünkü bu yöntem verinizde azalmaya neden olacaktır. Ben burada en çok veriye sahip sınıfı tekrar örneklemek için “majority” yöntemini kullanmayı tercih ettim. İsterseniz buradan diğer yöntemler hakkında bilgi alabilirsiniz.

from sklearn.model_selection import train_test_split

x_train, x_test, y_train, y_test = train_test_split(x_rus, y_rus, test_size = 0.25, random_state = 42)

Yeniden örneklendirdiğim veri setimi tekrar test ve train olarak ikiye ayırdıktan sonra tekrar modelimi kurup yeni train verimle eğittim.

model2 = Sequential()
model2.add(Embedding(input_dim=10000,
                    output_dim=embedding_size,
                    input_length=max_tokens,
                    name='embedding_layer'))
model2.add(LSTM(units=128,dropout = 0.2, return_sequences=True))
model2.add(LSTM(units=128,dropout = 0.2, return_sequences=False))
model2.add(Dense(num_labes, activation = 'softmax'))
model2.compile(loss = 'categorical_crossentropy', optimizer = "adam", metrics=['accuracy'])
model2.summary()

sequential_2″
_____________________________________________________
Layer (type) Output Shape Param #
===============================================================
embedding_layer (Embedding) (None, 33, 33) 330000
_____________________________________________________
lstm_3 (LSTM) (None, 33, 128) 82944
_____________________________________________________
lstm_4 (LSTM) (None, 128) 131584
_____________________________________________________
dense_2 (Dense) (None, 25) 3225
================================================================
Total params: 547,753 Trainable params: 547,753 Non-trainable params: 0

history2 = model2.fit(x_train, y_train, validation_split=0.20, epochs=20,batch_size=1024)

Epoch 1/20 243462/243462 [==============================] – 29s 120us/step – loss: 2.2683 – accuracy: 0.3589 – val_loss: 1.7201 – val_accuracy: 0.5149
Epoch 20/20 243462/243462 [==============================] – 28s 117us/step – loss: 1.0546 – accuracy: 0.6758 – val_loss: 1.3307 – val_accuracy: 0.6164

Modeli yeniden eğittikten sonra gördüğünüz gibi %4’lük gibi bir artış var modelin train verisindeki başarısında. Bir de test verisiyle modelimizi değerlendirelim.

loss2, acc2 = model2.evaluate(x_test, y_test)

print("loss 2 : {}, acc 2 : {}".format(loss2,acc2))

101443/101443 [==============================] – 34s 336us/step

loss 2 : 1.3396260495354742, acc 2 : 0.6127480268478394

Gördüğünüz gibi test verisinde de başarısı %3 artmıştır. Verimizi yeniden örneklendirerek modelimizin başarısını arttırmayı başardık. Şimdi de her iki modelimizin test ve train verilerindeki başarıları görselleştirerek bakalım.

plt.figure(figsize = (15,10))
plt.plot(history2.history['accuracy'])
plt.plot(history2.history['val_accuracy'])
plt.plot(history.history['accuracy'])
plt.plot(history.history['val_accuracy'])
plt.legend(['Train2', 'Test2','Train1', 'Test1'], loc='upper left')
plt.title('Model accuracy')
plt.ylabel('Accuracy')
plt.xlabel('Epoch')
Şekil 3: Veri setini yeniden örneklendirdikten sonra model başarılarının karşılaştırılması

Modelimizin akışını görmek istediğimizde aşağıdaki kodu kullanabiliriz.

from keras.utils import plot_model, model_to_dot

plot_model(model2, show_shapes=True)
Şekil 4: Modelin Akışı

Son olarak test verimizin sınıflarını tahmin edip seaborn kütüphanesiyle görselleştirelim. Hangi sınıfların birbirine karıştığını bu şekilde daha rahat görebiliriz.

y_pred2 = model2.predict_classes(x_test,batch_size=1024, verbose=0)
rounded_labels2=np.argmax(y_test, axis=1)
cm2 = confusion_matrix(rounded_labels2, y_pred2)

plt.figure(figsize=(35,15))
df2 = pd.DataFrame(cm2, columns=np.unique(dataset['Class']),index=np.unique(dataset['Class']))
sns.set(font_scale=1.2)
sns.heatmap(df2, annot=True, annot_kws={'size': 16});
Şekil 5: Seaborn Confusion Matrix

Biraz uzun bir yazı oldu ama akıllarda soru işareti kalmaması açısından detaylıca anlatmaya çalıştım. Bu yazının sonuna gelmiş bulunmaktayım, bir sonraki yazıda görüşmek üzere. Veriyle kalın 🙂

Kaynakça
https://machinelearningmastery.com/undersampling-algorithms-for-imbalanced-classification/

https://imbalanced-learn.readthedocs.io/en/stable/under_sampling.html

https://towardsdatascience.com/multi-class-text-classification-with-lstm-1590bee1bd17

Yazar Hakkında
Toplam 7 yazı
Serkan Arslan
Serkan Arslan
Eskişehir Osmangazi Üniversitesi İstatistik bölümü mezunuyum , chatbot yapan bir şirkette Veri Analisti olarak çalışmaktayım, NLP üzerine araştırma ve çalışmalar yapmaktayım.
Yorumlar (Yorum yapılmamış)

Bir cevap yazın

E-posta hesabınız yayımlanmayacak.

×

Bir Şeyler Ara