Evrişimli Sinir Ağlarıyla Haberlerin Sınıflandırılması

Merhabalar! Uzun bir aradan sonra yine sizlerleyim 🙂 Bu yazımda çoklu sınıflandırma problemini inceleyeceğiz. Bunun sebebi günümüzde sınıflandırma problemlerinin büyük bölümünün ikili değil çoklu sınıflandırma problemi olmasıdır. Çoklu sınıflandırma problemi temel olarak ikiye ayrılır. Tek etiketli ve çok etiketli olanlar. Çalışma kapsamında tek etiketli çoklu sınıflandırma problemi ele alınmıştır. Bu problem için farklı haber kategorisine sahip metin verileri kullanıldı. Bir önceki yazımda aynı problemi sığ ağ (shallow neural) yaklaşımı kullanarak ele alırken bu sefer evrişimli sinir ağlarını (convolutional neural networks – CNN) kullanarak inceledim. Evrişimli sinir ağları, son zamanlarda metin sınıflandırma ve ses üretmede çok başarılı olmuşlardır. Ayrıca evrişimli sinir ağları, metin sınıflandırma ve zaman serisi tahmininde hızları dolayısıyla yinelemeli sinir ağlarıyla (recurrent neural network – RNN) da yarışabilir durumdadırlar [1].

Veri Seti

Bu çalışma kapsamında “20 news group” veri setinden yararlanılmıştır [3]. Veri seti, 20 farklı haber kategorisine sahip toplam 18846 İngilizce haberden oluşmaktadır. 20 kategori şu konuları içermektedir.

['alt.atheism', 'comp.graphics', 'comp.os.ms-windows.misc',
 'comp.sys.ibm.pc.hardware', 'comp.sys.mac.hardware',
 'comp.windows.x', 'misc.forsale', 'rec.autos', 
 'rec.motorcycles', 'rec.sport.baseball', 'rec.sport.hockey',
 'sci.crypt', 'sci.electronics', 'sci.med', 'sci.space',
 'soc.religion.christian', 'talk.politics.guns', 'talk.politics.mideast','talk.politics.misc','talk.religion.misc']

“20 news group” verisine hem sklearn kütüphanesinde hem de Carnegie Mellon Üniversitesinden ulaşabilirsiniz (http://www.cs.cmu.edu/afs/cs.cmu.edu/project/theo-20/www/data/news20.tar.gz)

Aşağıdaki kod bloğunda, “fetch_20newsgroups()” fonksiyonu kullanılarak veri setinin yüklendiği görülmektedir [2].

data = fetch_20newsgroups()

print("------------ VERI ------------")
print(data.data[0].strip())
print("\nLABEL:", data.target_names[data.target[0]],
      "=", data.target[0])

Veya Carnegie Mellon üniversitesinden direk indirebilirsiniz [6]. Fakat bu çalışma kapsamında sklearn kütüphanesi kullanılarak veri seti temin edilmiştir.

from tensorflow import keras
data = keras.utils.get_file(
    "news20.tar.gz",
    "http://www.cs.cmu.edu/afs/cs.cmu.edu/project/theo-20/www/data/news20.tar.gz",
    untar=True,
)

Bir önceki yazımda da belirtiğim üzere veri setindeki mevcut bütün bilgilere ihtiyacımız yok. Örneğin: üstbilgi (header), altbilgi (footers) ve alıntı (quotes). Mevcut sınıflandırma işlemi için ilgili haberin metni ve hangi haber sınıfına ait olduğunu gösteren bilgi bizim için yeterli olmaktadır. Bu çalışma kapsamında veri seti yüklenirken 7 farklı haber sınıfına odaklanılmıştır. Daha sonradan eğitim ve test verisi ayrıldı.

categories = ['comp.graphics', 'sci.space', 'talk.politics.guns',
            'rec.autos', 'sci.crypt', 'sci.electronics', 'comp.sys.ibm.pc.hardware']

newsgroups = fetch_20newsgroups(subset='all', remove=('headers', 'footers', 'quotes'), categories=categories ) 

df_ = pd.DataFrame([newsgroups.data, newsgroups.target.tolist()]).T
df_.columns = ['text', 'target']
targets = pd.DataFrame(newsgroups.target_names)
targets.columns=['topic']
df = pd.merge(df_, targets, left_on='target', right_index=True)
df.head()
Şekil-1: İlgili veri setine ait ilk 5 örnek

Metin Ön İşleme Adımı

Önceki yazılarımda bahsettiğim üzere metin ön işleme yaklaşık 5 işlemden oluşuyor. Bunlar: 

  • Lower-case
  • Noktalama işaretleri, özel karakterlerin ve sayıların silinmesi
  • Tokenize işlemi
  • Stop-words’lerin silinmesi
  • Stemming / Lemmatization

Bu adımları pythonun RegexNLTK veya Gensim gibi kütüphanelerini kullanarak rahatlıkla gerçekleştirebilirsiniz.

import gensim
from gensim.utils import simple_preprocess
from gensim.parsing.preprocessing import STOPWORDS
from nltk.stem import WordNetLemmatizer, SnowballStemmer
from nltk.stem.porter import *
import nltk
stemmer = SnowballStemmer('english')
def lemmatize_stemming(text):
    return stemmer.stem(WordNetLemmatizer().lemmatize(text, pos='v'))
def preprocess(text):
    result = []
    for token in gensim.utils.simple_preprocess(text):
        if token not in gensim.parsing.preprocessing.STOPWORDS and len(token) > 3:
            result.append(lemmatize_stemming(token))
    return " ".join(result)
processed_docs = df['text'].map(preprocess)
df['cleaned_text'] = processed_docs
df.head()
Şekil-2:  İlgili metinlerim temizlendiğini gösteren örnek veri.
maxlen = 1000 # Metni 1000 kelimeden sonra keser
max_words = 20000 # Nitelik olarak düşünülecek kelime sayısı

tokenizer = Tokenizer(num_words=max_words) # 1000 kelime idin kelime ayırıcı
tokenizer.fit_on_toexts(df.cleaned_text) # Kelime indekslerini oluşturuyor. 
sequences = tokenizer.texts_to_sequences(df.cleaned_text) # Metinleri tam sayı listelerine dönüştürür.
word_index = tokenizer.word_index 

data = pad_sequences(sequences, maxlen=maxlen) # Tam sayı listesini 2B tam sayı sensörüne dönüştürür.
labels = to_categorical(np.asarray(df.target)) # Sınıf etiketlerinin vektöre dönüştürülmesi

Word Embedding

Daha önceki yazılarımda kelime çantası (Bag of Words) yaklaşımını kullanmıştım. Yani metin içerisindeki kelimelerin, metni temsil etmesi için öznitelik olarak kullanıldığı yaklaşım. Makine veya derin öğrenme modellerinde ham metin verisi kullanılmadığı için frekans tabanlı TF-IDF yaklaşımı kullanılarak ilgili metinler ayrıca sayısallaştırılmıştı. Bu çalışma kapsamında ise tahminleme tabanlı kelime gösterimi kullanıldı (GloVe). Peki neden frekans tabanlı yerine tahminleme tabanlı kelime gösterimi tercih edildi? Bunun en büyük sebebi kelime çantası yaklaşımı metinlerdeki kelimelerin pozisyonlarını ve sırasını dikkate almaz, dolayısıyla anlam bütünlüğü kaybolur. Bu nedenle bu yaklaşım derin öğrenme modelleri yerine sığ öğrenme modelleriyle kullanılmaya daha uygundur. Tahminleme yaklaşımıyla kelimeler arasındaki anlamsal yapılar ortaya çıkarılabilir. Tahminleme yaklaşımı için ilk olarak kelime ve vektörü ilişkilendirmemiz gerekiyor. Bunun için kelime gömülmeleri (Word Embedding) metodu kullanıldı. Kelime gömülmesi geometrik uzayla ifade edilebilir. Örneğin bir gömülme uzayında eş anlamlı kelimelerin gömülmelerinin aynı vektör olması beklenebilir. Özetle kelime gömülmeleri benzer anlamdaki kelimelerin yakın şekilde temsil edilmesine yarayan yöntemdir.

Bu çalışma kapsamında Stanford Üniversitesindeki araştırmacılar tarafından geliştirilen öneğitimli kelime gömülmesi olan GloVe (Global Vectors for Word Representation) kullanılmıştır. GloVe, Wikipedia ve Common Crawl verilerinden hesaplanarak oluşturulmuştur. Bu gömülme tekniği, birlikte sık geçen kelimelerin öneminin biraz daha arttırıldığı bir yapıya sahiptir. GloVe haricinde meşhur olan diğer kelime gömülmeleri Word2Vec ve FastText’dir. Word2Vec 2013 yılından Google tarafından geliştirilirken FastText ise 2016 yılından Facebook şirketi tarafından geliştirilmiştir [5].

GloVe kelime gömülmesi için yapılacak işlemler:

  • Stanford Üniversitesinin doğal dil işleme çalışma grubunun sitesine giriniz. https://nlp.stanford.edu/projects/glove/
  • Bu site üzerinden 2014 yılına ait Wikipedia İngilizce içeriklerinden üretilmiş olan 100 boyutlu kelime gömülmesini indiriniz. Bu kelime gömülmesinden 400000 kelime bulunmaktadır. Bu kelimeler için 100 boyutlu bir gömülme mevcuttur [1].
  • İndirilen dosyayı aşağıdaki kod parçacığıyla okuyabilir ve ilgili metinlerdeki kelimeleri indeksleyebiliriz.
  • İndeksleme işleminden sonra geriye kelime gömülme matrisinin oluşturulması işlemi var. Bu matris Keras kütüphanesi içerisinde mevcut olan Embedding katmanını beslemektedir. İlgili matris ( len(word_index) + 1, embedding_dim) şeklindedir.
import os

glove_dir = '/Users/maxwell/VBO/icerik/chapter-4/glove/'

embeddings_index = {}

f = open(os.path.join(glove_dir, "glove.6B.100d.txt"))
for line in f:
    values = line.split()
    word = values[0]
    coefs = np.asarray(values[1:], dtype="float32")
    embeddings_index[word] = coefs
f.close()

print("Bulunan Kelime Vektörü Sayısı: %s " % len(embeddings_index))
Bulunan Kelime Vektörü Sayısı: 400000 
embedding_dim = 100
embedding_matrix = np.zeros((len(word_index) + 1, embedding_dim))

count_zero = 0
for word, idx in word_index.items():
    embed_vector = embeddings_index.get(word)
    if embed_vector is not None:
        count_zero += 1
        embedding_matrix[idx] = embed_vector

Evrişimli Sinir Ağları

Evrişimli Sinir Ağları, ızgara benzeri geometriye sahip veriyi işlemek için kullanılan yapay sinir ağıdır.

” Evrişimli sinir ağı denilmesinin sebebi evrişim adı verilen matematiksel işlemin kullanılmasıdır. Evrişim, özel bir tür doğrusal işlemdir. Evrişimsel ağlar basitçe, katmanlarından en az birinde genel matris çarpımı yerine evrişim kullanan sinir ağlarıdır ” [4].

CNN, son yıllarda bilgisayarlı görü alanında başarılı sonuçlar verdiği için yaygın olarak kullanılmaktadır. Fakat CNN sadece bilgisayarlı görü alanında kullanılmıyor bununla beraber metin sınıflandırma çalışmalarında da kullanılmaktadır. Bu çalışma kapsamında evrişimli sinir ağlarını çoklu metin sınıflandırma problemi için kullanılmıştır. Bunun için Keras kütüphanesinden yararlanıldı.

Keras’ta bilgisayarlı görü çalışmalarında Conv2d katmanı kullanılırken metin çalışmalarında Conv1d katmanı kullanılmaktadır. Neden? Conv2d, 2 yönde hareket eden kernel’a sahip görüntü gibi 3 boyutlu veriler için kullanırken, Conv1d ise 1 yönde hareket eden kernel’a sahip girdi ve çıktıları 2 boyutlu olan veriler için kullanılmaktadır. Conv1d, zaman serileri, ses ve metin analizlerinde kullanılmaktadır.

Modelin Oluşturulması

Sırada derin öğrenme modelimizin oluşturulması var. Model aşamasında sığ öğrenmeden farklı olarak embedding (kelime gömülümü) ve evrişim katmanları kullanıldı.

Keras’ta model oluşturmak için Sequential yöntemini kullanıyorduk. Sequential sıralı katmanlar halinde bir yapı kurmamızı sağlıyor. Modele ilk olarak embedding katmanı eklendi. Embedding katmanı kabaca tam sayı indeksleri vektörlere eşleyen sözlük olarak tanımlayabiliriz. Embedding katmanını beslemek için yukarıda üretilen gömülme matrisi kullanılmıştır. Gömülme matrisini GloVe öneğitimli kelime gömülesi kullanılarak elde edilmiştir. Aşağıdaki kod incelediğinde öneğitimli kelime gömülü matrisinin ağırlık matrisi olarak kullanıldığını görülmektedir. Model kapsamında 3 tane evrişim katmanı kullanılmıştır. Dizi verisiyle (metin) uğraştığımız için Keras’taki Conv1d katmanı tercih edilmiştir. Modelimizde evrişim katmanlarının herbirinde 128 filtre mevcut. Yani evrişim işleminde kullanılan çıktı filtrelerinin sayısıdır. Evrişim işleminde nitelik haritası üzerinde hareket eden penceler (kernel size) kullanırız. Conv2d evrişim katmanında 5×5’lik penceler kullanıldığında 25 nitelik vektörü öğrenirken, Conv1d evrişim katmanında ise pencere genişliği 5 ise 5 nitelik vektörü öğrenir. Evrişim işlemlerinde aktivasyon fonksiyonu olarak ise ReLU kullanılmıştır. ReLU fonksiyonunun işlem yükü, sigmoid ve hiperbolik tanjant fonksiyonlarına göre daha azdır. Bu durum ReLU fonksiyonun tanımı itibariyle aslında şaşılacak bir sonuç değil. Çünkü kendisi [0, +∞) arasında tanımlanmakta yani değerler almaktadır.

Bununla beraber boyut azaltma işlemi için en büyükleri biriktirme (MaxPooling) yaklaşımı uygulandı. En büyükleri biriktirme islemi, girdi nitelik haritası üzerinde pencereler çıkarıp her kanalın en büyük değerini almamızı sağlar. Özetle boyut küçültme yapılmasının sebebi daha az katsayıya işlem yapmaktır. Aralarda modelimizin ezberlemesini önlemek için Dropout katmanı kullanıldı. Dropout katmanı her adımda belirtilen orandaki girdiyi rassal olarak sıfıra eşitleyerek modelin veriye aşırı uyum sağlamasının önüne geçiyor. En büyükleri biriktirme işleminden sonra artık “fully connected (tam bağlantı)” olarak adlandırılan sınıflandırma işlemini yapacağımız bölüme geldik. Yapay sinir ağımızı besleyecek girdi için son olarak Flatten işlemi uygulandı.

model = Sequential()
model.add(Embedding(len(word_index) + 1, embedding_dim,
                            weights=[embedding_matrix],
                            input_length=maxlen,
                            trainable=True))

model.add(Conv1D(128, 5, activation='relu'))
model.add(MaxPooling1D(5))
model.add(Dropout(0.2))
model.add(Conv1D(128, 5, activation='relu'))
model.add(MaxPooling1D(5))
model.add(Dropout(0.2))
model.add(Conv1D(128, 5, activation='relu'))
model.add(MaxPooling1D(35))
model.add(Flatten())
model.add(Dropout(0.2))
model.add(Dense(128, activation='relu'))
model.add(Dropout(0.2))
model.add(Dense(7, activation='softmax'))
model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['acc'])
model.summary()

Modelimizin fully connected kısmında 2 katman bulunuyor. İlkinde 128 düğüm mevcut ve aktivasyon fonksiyonu olarak ReLU kullanıldı. Son katman ise çıktı katmanımız. Çoklu sınıflandırma problemiyle uğraştığımız için çıkış katmanında aktivasyon fonksiyonu olarak softmax fonksiyonu tercih edildi. Çıktı katmanımız 7 düğümden oluşuyor. Bunun sebebi 7 farklı sınıf mevcut. Sonuç olarak her girdi için 7 boyutlu bir vektör çıkacak ve elde edilen değerin hangi sınıfa ait olup olmadığının olasılığını gösterecektir. Yani 1 tam olasılık 7 ayrı sınıfa paylaştırılıyor.

histroy = model.fit(X_train, y_train, epochs=15, batch_size=128, validation_data=(X_test, y_test))

Model eğitilirken verilerin tamamı aynı anda eğitime katılmaz. “Batch_size” olarak adlandırılan parçalar halinde eğitimde yer alırlar. Bu örnekte modelimiz 15 epokta, 128’lik mini-yığınlar kullanılarak eğitilmiştir.

plot_history(histroy)
Şekil-3:  Sol tarafta eğitim ve doğrulama başarısı. Sağ tarafta ise eğitim ve doğrulama kaybı görülmektedir.

Şekil-2’de görüldüğü üzere eğitim kaybı her epokta düşerken eğitimin başarısı artmaktadır. Bununla birlikte sağ taraftaki grafikte eğitim ve doğrulama kaybının gittikçe düştüğü, yani modelin başarısının gittikçe arttığı, bununla birlikte yaklaşık 6. epoktan sonra eğitim veri seti için kaybın düşmeye devam ettiği fakat buna karşılık doğrulama veri seti için bu değerin arttığı görülmektedir. Yani “aşırı uydurma” söz konusu. Bu durumu önlemek için 6. epoktan sonra eğitimi durdurmalıyız.

Bu sefer 6. epokta duracak şekilde ağımızı tekrardan eğitelim.

histroy2 = model2.fit(X_train, y_train, epochs=6, batch_size=128, validation_data=(X_test, y_test))
score, acc = model2.evaluate(X_test, y_test, batch_size=128)
print("Test Doğruluğu:', acc)
--> 0.7521994113922119

Bu çalışma sonucunda elde edilen başarı oranı %75 olarak belirlenmiştir. Farklı eniyileme algoritmaları veya katman sayılarıyla bu değer artabilir de azalabilir de.

Son olarak başarı oranı haricinde diğer model performans parametrelerini incelediğimizde elde edilen sonuçlar şöyle:

print(classification_report(_class, y_pred_class))
precision    recall  f1-score   support

           0       0.84      0.72      0.77       220
           1       0.79      0.74      0.77       191
           2       0.74      0.71      0.73       191
           3       0.87      0.76      0.81       194
           4       0.53      0.72      0.61       197
           5       0.79      0.83      0.81       199
           6       0.82      0.78      0.80       172

    accuracy                           0.75      1364
   macro avg       0.77      0.75      0.76      1364
weighted avg       0.77      0.75      0.76      1364

Sonuçlar

Bu çalışma kapsamında çoklu sınıflandırma problemi evrişimli sinir ağları modeliyle ele alınmıştır. Amaç tek etiketli çoklu sınıflandırma problemini çözen evrişim modelinin oluşturulmasıydı. Yukarıda da görüldüğü üzere %75 başarı oranıyla 7 farklı konu sınıflandırılabilmiştir. 

Bu yazı kapsamında sizlere aktaracaklarım bu kadar. Umarım faydalı olabilmişimdir.

Bilimin mihraplarında bilginin peşinde koşmanız dileğiyle. Sağlıcakla Kalın 🙂

Kaynaklar

[1]- Chollet, François, Python ile Derin Öğrenme, Ankara: Buzdağı Yayınevi, 2019

[2]- https://scikit-learn.org/0.19/datasets/twenty_newsgroups.html

[3]- http://qwone.com/~jason/20Newsgroups/

[4]- Goodfellow Ian, Bengio Yoshua ve Courville Aaron, Derin Öğrenme, Ankara: Buzdağı Yayınevi, 2018

[5]- Kelime Gösterilimleri (Word Representation – Word Embeddings), Prof. Dr. Banu Diri

[6]- Using pre-trained word embeddings, https://keras.io/examples/nlp/pretrained_word_embeddings/

[7]- https://unsplash.com/photos/hWJsOnaWTqs (Kapak Resmi)

Github Kod:

Bu yazı kapsamında kullanılan kodlara Github üzerinden ulaşabilirsiniz.

Ek Bilgi:

  • Bu çalışma kapsamında derin öğrenme modeli oluşturmak için keras kütüphanesinden yararlanılmıştır.
  • Kullanılan Python versiyonu: 3.7.3
  • Keras versiyonu: 2.3.0
  • Sklearn versiyonu: 0.21.3
  • Tensorflow versiyonu: 2.2
  • İşletim sistemi: macOS

Yazar Hakkında
Toplam 5 yazı
Eyüp Kaan Ülgen
Eyüp Kaan Ülgen
İstanbul Üniversitesi, Astronomi ve Uzay Bilimleri Bölümünde Doktora Öğrencisi. Doktora çalışma konusu: Galaksi kümelerindeki parlak galaksilerin çevreyle olan ilişkisi. İlgi alanları; Galaksi Evrimi, Veri Bilimi, Makine Öğrenimi, Derin Öğrenme ...
Yorumlar (Yorum yapılmamış)

Bir cevap yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

×

Bir Şeyler Ara