Derin Öğrenme ile BBC Haberlerinin Sınıflandırılması

Merhabalar! Bu yazımda derin öğrenme (deep learning) yöntemlerinden evrişimli sinir ağları (convolutional neural networks – CNN) ve uzun kısa süreli bellek (Long short-term memory – LSTM) modelini kullanarak çoklu sınıflandırma problemini ele alacağım. Bunun yanında ilgilendiğimiz haberleri, manifold öğrenme algoritmaları arasında en popüler olan t-SNE (t-Distributed Stochastic Neighbor Embedding) ile görselleştireceğiz.

Veri Seti

Bu çalışma kapsamında BBC’e (British Broadcasting Corporation) ait haberleri kullanacağız. İlgili haberler, 5 kategoride (businees, enterteinment, sport, politics, tech) olup 2004-2005 yılları arasındaki haberlerden oluşmaktadır. İlgili haberlere link’e tıklayarak ulaşabilirsiniz.[1]

İlgili siteden ham veri dosyalarını (Download raw text files)  indirdiğinizde 5 farklı kategori için haber dosyaları karşımıza çıkmaktadır. İlgili dosyalar incelendiğinde haber başlığı her birinin ilk satırında yer alırken haber içerikleri daha sonraki satırlarda yer almaktadır. Bütün kategorilere ait haberleri tek bir veri çerçevesinde toplamak için aşağıdaki kod bloğunu kullanabilirsiniz.

import os, re, glob
import pandas as pd


file = ["business","entertainment", "politics", "sport", "tech"] # İncelenen haber kategorileri


alltext = []
alltitle = []
allfilename = []
allcategory = []

for k in file: 
    print(k)
  
    filenames = sorted(glob.glob("./bbc/"+ k +"/*.txt")) # bbc klasörü içerisinde mevcut olan farklı haber kategorilerindeki dosyalar (haberler) sıralanıyor.
    N = len(filenames)
    print(N)

    filenames = filenames[:N]
    for f in filenames:
        f_id = re.findall("\d+", f)[0] + ".txt" # her bir haber için dosya adı tespit ediliyor.

        df = pd.read_table(f, header=None, index_col=False) # Her bir kategori içerisindeki haber dosyaları okutulur.
        title = df.iloc[0][0] # İlgili haberlerin ilk satırları haber başlığını oluşturuyor.

        df = df.iloc[1: , :] # İlgili haberlere ait içerikler (veya metin) 1. satırdan sonra gelmektedir.
        text = []
        for i in range(len(df)):   
            text.append(df.iloc[i][0])

        alltext.append(str(text)[1:]) # Bütün haber metinleri tek bir liste içerisinde toplanıyor.
        alltitle.append(str(title))   # Bütün haber başlıkları tek bir liste içerisinde toplanıyor.
        allfilename.append(f_id)      # Bütün haber dosya adları tek bir liste içerisinde toplanıyor.
        allcategory.append(k)         # Bütün haber kategorileri tek bir liste içerisinde toplanıyor.

alldata = pd.DataFrame.from_dict({"filename":allfilename, "title":alltitle, "text":alltext,"category":allcategory})
alldata.to_csv("./bbc_news_all_category.txt", sep=" ", index=False)

Yukarıdaki kod bloğunda 5 haber kategorisi içerisinde bulunan bütün dosyalar okutuluyor. Daha sonra her haber dosyası içerisinde bulunan ilk satır tespit edilip, haber başlığı olarak atanıyor. İlk satırdan sonraki her metin ise haber içeriği olarak belirlenmektedir. alldata olarak adlandırılan veri çerçevesi içerisinde ilgili haberlere ait dosya adları, haber başlıkları, haber metinleri ve kategorileri mevcuttur. İlgili veri çerçevesine ait görüntü Şekil-1‘de mevcuttur.

Şekil-1: İlgili veri setine ait ilk 5 örnek

 

Şekil-1’de de görüldüğü üzere haber türleri kategorik (“business”, “sport”, vb.,) değerler içeriyor. sklearn kütüphanesi içerisinde mevcut olan LabelEncoder özelliği kullanılarak haber etiketlerimiz sayısallaştırıldı.

from sklearn.preprocessing import LabelEncoder
labelencoder = LabelEncoder()

df["labels"] = labelencoder.fit_transform(df.category)
df.drop(["category"], axis=1, inplace=True)

# business=0, entertainment=1, politics=2, sport=3, tech=4
Şekil-2: LabelEncoder işleminden sonra ilgili veri setine ait ilk 5 örnek

 

Metin Ön İşleme Adımı

Uygulanan metin ön işleme adımları şu şekilde:

  • Lower-case
  • Noktalama işaretleri, özel karakterlerin ve sayıların silinmesi
  • Tokenize işlemi
  • Stop-words’lerin silinmesi
  • 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
from nltk.stem.porter import *
import nltk

def lemmatize(text):
    return 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(token))
    return " ".join(result)

processed_docs = df['text'].map(preprocess)
df['cleaned_text'] = processed_docs

 

from tensorflow.keras.preprocessing.text import Tokenizer 
from tensorflow.keras.preprocessing.sequence import pad_sequences 
from keras.utils.np_utils import to_categorical 

max_words = 5000 # Nitelik olarak düşünülecek kelime sayısı 
embedding_dim = 100 # Embedding vektör büyüklüğü
maxlen = 200 # Metni 200 kelimeden sonra keser 
trunc_type = 'post' 
padding_type = 'post' 
oov_tok = '<OOV>' # Out Of Vocabulary 
    
tokenizer = Tokenizer(num_words=max_words, oov_token=oov_tok) 
tokenizer.fit_on_texts(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 
print("Found %s unique tokens." % len(word_index)) 

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

print('Shape of data tensor:', data.shape) print('Shape of label tensor:', labels.shape)

Yukarıdaki kod bloğunda trunc_type parametresi ‘post’ olarak ayarlanmıştır. Bu nedenle, bir metin 200 kelimeden büyük olduğunda kesilecektir. Öte yandan, metin 200 kelimeden az ise 200 kelime yapacak şekilde doldurulacaktır. Buna ek olarak, padding_type parametresinin ‘post‘ olması dolgunun başlangıçta değil, sonunda uygulanacağı anlamına gelmektedir.

Word Embedding

Bu çalışma kapsamında tahminleme tabanlı kelime gösterimi kullanıldı.  Tahminleme yaklaşımı için ilk olarak kelime ve vektörü ilişkilendirmemiz gerekiyor. Bunun için kelime gömülmeleri (Word Embedding) metodu tercih edildi. 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.

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 hakkında daha detaylı bilgi için “Evrişimli Sinir Ağlarıyla Haberlerin Sınıflandırılması” başlıklı yazımı okuyabilirsiniz.

embeddings_index = {}
with open('./glove/glove.6B.100d.txt') as f:
    for line in f:
        values = line.split();
        word = values[0];
        coefs = np.asarray(values[1:], dtype='float32')
        embeddings_index[word] = coefs;

embeddings_matrix = np.zeros((max_words+1, embedding_dim))
for word, i in word_index.items():
    embedding_vector = embeddings_index.get(word)
    if embedding_vector is not None:
        embeddings_matrix[i] = embedding_vector

Kullanılan Derin Öğrenme Modelleri

Derin öğrenme, makine öğrenmesinin bir alt alanıdır. Temel olarak birbirini takip eden katmanlar aracılığıyla verilerden daha anlamlı sonuçların elde edilmesidir. Buradaki derinden kast edilen modelde kullanılan katman sayısıdır. Yani modeldeki katman sayısı modelin derinliğini oluşturmaktadır. Günümüzde kullanılan derin öğrenme modelleri bazı durumlarda birbirini takip eden yüzlerce katman içermektedir.

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 ” [2].

Keras’ta bilgisayarlı görü çalışmalarında Conv2d katmanı kullanılırken metin çalışmalarında Conv1d katmanı kullanılmaktadır.  Conv1d, zaman serileri, ses ve metin analizlerinde tercih edilmektedir.

Uzun Kısa Süreli Bellek

Uzun-kısa süreli bellek (Long short-term memory – LSTM), özyinemeli sinir ağlarının (Recurrent Neural Network – RNN) özel bir türü olarak 1997 yılında Hochreiter & Schmidhuber (1997) tarafından önerilmiştir. Özyinemeli sinir ağları, metin, ses, video gibi sıralı verilerin modellenmesinde sıklıkla kullanılmaktadır. Bu model, klasik sinir ağlarından farklı olarak ilerideki katmanlardan girişlere geri besleme yapmaktadır. Özetle, özyinemeli sinir ağları, girdi verisindeki bilgileri bir önceki çıktının değeride dikkate alarak inceler. Teorik olarak t anında, önceki tüm adımlardaki bilgiyi tutulabiliyor olsa da pratikte uzun dönemli gereksinimlerin öğrenilmesi imkansız hale geliyor. Bu durum literatürde gradyan kaybolması olarak bilinmektedir. Yani çok derin ileri yayılım ağlarında katman ekledikçe ağın eğitilmez olmasıdır. Bu problemi çözmek için uzun-kısa süreli bellek yaklaşımı önerilmiştir. Uzun kısa süreli bellek modeli dört katmandan oluşmaktadır. Sıradan bir LSTM ünitesi, hücre, giriş, çıkış ve unutma katmanından oluşur. Hücre, keyfi zaman aralıklarındaki değerleri hatırlar ve üç katman, hücreye giren ve çıkan bilgi akışını düzenler. [3, 4, 5]

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ü)evrişim katmanı ve uzun-kısa süreli bellek 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.

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 1 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. Evrişim katmanında 128 düğüm mevcuttur. Evrişim işleminde nitelik haritası üzerinde hareket eden penceler (kernel size) kullanırız. Conv1d evrişim katmanında pencere genişliği 5 olduğu için 5 nitelik vektörü öğrenilir. Evrişim işleminde aktivasyon fonksiyonu olarak ise ReLU kullanılmıştır.

Bununla beraber boyut azaltma işlemi için en büyükleri biriktirme (MaxPooling) yaklaşımı uygulandı. En büyükleri biriktirme işlemi, girdi nitelik haritası üzerinde pencereler çıkarıp her kanalın en büyük değerini almamızı sağlar.

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.

Daha sonra ise 1 tane uzun-kısa süreli bellek katmanı kullanılmıştır. Bu katmanda 128 düğüm mevcuttur.

model = tf.keras.Sequential([
    tf.keras.layers.Embedding(max_words+1, embedding_dim, input_length=maxlen, weights=[embeddings_matrix], trainable=False),
    tf.keras.layers.Dropout(0.2),
    tf.keras.layers.Conv1D(128, 5, activation='relu'),
    tf.keras.layers.MaxPooling1D(pool_size=4),
    tf.keras.layers.Dropout(0.2),
    tf.keras.layers.LSTM(128),
    tf.keras.layers.Dense(5, activation='softmax')
])
model.compile(loss='categorical_crossentropy',optimizer='adam',metrics=['accuracy'])
model.summary()

 

Şekil-3: Model özeti

 

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 5 düğümden oluşuyor. Bunun sebebi 5 farklı haber kategorisi mevcut. Sonuç olarak her girdi için 5 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 5 ayrı sınıfa paylaştırılıyor.

history = model.fit(X_train, y_train, epochs=15, validation_data=(X_test, y_test), verbose=2)

Bu örnekte modelimiz 15 epokta eğitilmiştir.

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

 

loss, acc = model.evaluate(X_test, y_test)
print('Test accuracy:', acc)
--> Test accuracy: 0.9595505595207214

Bu çalışma sonucunda elde edilen başarı oranı ~ %96 olarak belirlenmiştir.

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

from sklearn.metrics import classification_report

print(classification_report(_class, y_pred_class))
Şekil-5: Model performans parametreleri

 

t-SNE ile Veri Görselleştirme

t-SNE, boyut azaltmak için kullanılan doğrusal olmayan bir tekniktir. Özellikle yüksek boyutlu veri kümelerinin görselleştirilmesi için kullanılmaktadır. Görüntü işleme, NLP, genomik ve ses  verilerini işlemede yaygın olarak uygulanır. t-SNE, veri noktalarının yakınlıklarını olasılıklara dönüştürür. Orijinal uzaydaki yakınlıklar, Gauss ortak olasılıkları ile temsil edilirken gömülü uzaydaki yakınlıklar, Student t-dağılımları ile temsil edilmektedir. Bu, t-SNE’nin yerel yapıya özellikle duyarlı olmasını sağlar.

t-SNE kullanımının avantajları:

– Yapının tek bir harita üzerinde birçok ölçekte ortaya çıkarılmasını sağlar.

– Birden çok, farklı, manifold veya kümede yatan verileri ortaya çıkarır.

– Merkezde noktaları bir araya toplama eğilimini azaltmaktadır.

Isomap, LLE (Locally Linear Embedding) ve varyantlar tek bir sürekli düşük boyutlu manifoldu ortaya çıkarmak için uygun olsa da, t-SNE, verilerin yerel yapısına odaklanarak kümelenmiş yerel örnek gruplarını çıkarma eğilimindedir. Örnekleri yerel yapıya dayalı olarak gruplandırma yeteneği, sayısal veri kümesinde olduğu gibi, aynı anda birkaç manifold içeren bir veri kümesini görsel olarak çözmek için faydalı olabilir.

Orijinal uzaydaki ve gömülü uzaydaki ortak olasılıkların Kullback-Leibler (KL) sapması, gradyan inişi ile en aza indirilmektedir. 

t-SNE kullanmanın kabaca dezavantajları ise şunlardır:

– t-SNE hesaplama açısından pahalıdır ve PCA’nın saniyeler veya dakikalar içinde biteceği milyonlarca örnek veri kümesinde birkaç saat sürebilir.

– Barnes-Hut t-SNE yöntemi, iki veya üç boyutlu gömmelerle sınırlıdır.

– Küresel yapı açıkça korunmaz. Bu sorun, noktalar PCA ile başlatılarak azaltılır [6].

 

from sklearn.manifold import TSNE
from gensim.models import Word2Vec

w2v_model = Word2Vec(min_count=200, window=5, size=100, workers=4)

sentences = [row.split() for row in df['cleaned_text']]
w2v_model.build_vocab(sentences)

# train word vectors
w2v_model.train(sentences, total_examples=w2v_model.corpus_count, epochs=w2v_model.iter)

w2v_model.init_sims(replace=True)


def tsne_plot(model):
    "Create TSNE model and plot it"
    labels = []
    tokens = []

    for word in model.wv.vocab:
        tokens.append(model[word])
        labels.append(word)
    
    tsne_model = TSNE(perplexity=40, n_components=2, init='pca', n_iter=2500, random_state=23)
    new_values = tsne_model.fit_transform(tokens)

    x = []
    y = []
    for value in new_values:
        x.append(value[0])
        y.append(value[1])
        
    plt.figure(figsize=(18, 18)) 
    for i in range(len(x)):
        plt.scatter(x[i],y[i])
        plt.annotate(labels[i],
                     xy=(x[i], y[i]),
                     xytext=(5, 2),
                     textcoords='offset points',
                     ha='right',
                     va='bottom')
    plt.show()


tsne_plot(w2v_model)

 

Yukarıdaki kod bloğu kullanılarak Şekil-6 üretilmiştir.

 

Şekil-6: İlgili haberlerin t-SNE gösterimi

 

“Takım”, “oyun”, “oyuncu”,  vb. gibi bazı kelimelerin birbirine yakın olduğu görülüyor. Ve bu kelimeler sporla ilgili haberlerde kullanılmaya meyilli.

“politik”, “seçim”, “başkan”, “tony” , “blair” gibi bir arada kümelenen kelimelerin haber yazılarında kullanılması muhtemeldir (Tony Blair, 2004-2005 yıllarında Birleşik Krallık Başbakanıydı).

Unutulmamalı t-SNE plot’un kalitesi, word2vec modeli içerisinde kullanılan min_count() parametresine duyarlıdır.

Sonuç

Bu çalışma kapsamında 2004-2005 yıllarına ait BBC haberleri sınıflandırılmıştır. Sınıflandırma işlemi kapsamında embedding (kelime gömülümü)evrişim katmanı ve uzun-kısa süreli bellek kullanılmıştır. Sonuç olarak ~%96 başarım oranıyla 5 farklı haber kategorisi 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 🙂

NOT:
– Bu çalışma kapsamında farklı derin öğrenme (evrişim katmanı ve uzun-kısa süreli bellek) yaklaşımları kullanılarak sınıflandırma işlemi amaçlanmıştır. Unutulmamalı ki farklı eniyileme algoritmaları veya katman sayılarıyla model başarımı değişebilir veya aynı kalabilir. Örneğin; evrişimli sinir ağları veya LSTM kullanmadan da benzer model başarım skoruna (%96) “Embedding+GlobalAveragePooling1D+Dense” katmanlarıyla ulaşılabilmektedir.

Kaynaklar

[1]- http://mlg.ucd.ie/datasets/bbc.html

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

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

[4]- https://stanford.edu/~shervine/l/tr/teaching/cs-230/cheatsheet-recurrent-neural-networks

[5]- https://dergipark.org.tr/tr/download/article-file/520262

[6]- https://scikit-learn.org/stable/modules/manifold.html#t-sne

[7]- https://towardsdatascience.com/understanding-word2vec-embedding-in-practice-3e9b8985953

[8]- https://unsplash.com/photos/psIGDb9vxhA

 

Ek Bilgi:

  • Kullanılan Python versiyonu: 3.7.3
  • Tensorflow versiyonu: 2.4.1
  • Keras versiyonu: 2.4.3
  • Sklearn versiyonu: 0.23.2
  • İşletim sistemi: macOS

Eyüp Kaan Ülgen

Yazar Hakkında
Toplam 6 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 (5 yorum)
İsmet
İsmet Yanıtla
- 23:12

Merhaba, kodları çalıştırmayı deniyorum. hem hatalar var hemde eksikler.

    Eyüp Kaan Ülgen
    Eyüp Kaan Ülgen Yanıtla
    - 23:33

    Merhabalar,

    İlgili kütüphanelerin farklı versiyonlarını kullanıyorsanız hata alma ihtimaliniz olası (veya öneğitimli kelime gömülmesi olan glove sizde mevcut olmayabilir, verdiğiniz dizin yanlış olabilir, vs.). Bu çalışma kapsamında kullanılan kütüphanelerin versiyonları yazının en alt kısmında belirtilmiştir. Yazı içerisinde kodların hepsi değil önemli görülen bölümler anlatılmıştır. Fakat bütün kodlara GitHub sayfam üzerinden ulaşabilirsiniz (https://github.com/kaanulgen/bbc_news_classification/)

    İyi çalışmalar diliyorum,
    E. Kaan Ülgen

      İsmet
      İsmet Yanıtla
      - 22:23

      Hocam ilginiz için teşekkürler, colab da çalışırken gözden kaçırdığım bir iki nokta olmuş github da kodunuzun toplu hali ile düzeltmeyi yaptım. Şimdi sorun yok.

elif
elif Yanıtla
- 15:39

merhaba. kerası import edemiyorum.
note: This error originates from a subprocess, and is likely not a problem with pip.
ERROR: Failed building wheel for dm-tree
Failed to build dm-tree
ERROR: Could not build wheels for dm-tree, which is required to install pyproject.toml-based projects
böyle bir sorun alıyorum

Selim
Selim Yanıtla
- 22:23

Merhaba, elde edilen 3 yıllık güneş enerjisi üretim verileri için bu yöntemler kullanıla bilir mi?

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

×

Bir Şeyler Ara