TensorFlow ile NLP — Giriş 2

Furkan Gulsen
8 min readAug 29, 2021

--

Uyarı:
Eğer TensorFlow ile NLP — Giriş 1 yazısını okumadıysanız ilk önce onu okumanızı tavsiye ediyorum.

Bir Metin Veri Kümesini Modelleme

Girdilerinizi ve çıktılarınızı hazırladıktan sonra, aradaki boşluğu kapatmak için hangi makine öğrenimi modelinin oluşturulacağını bulmak meselesidir.

Artık metin verilerimizi sayılara dönüştürmenin bir yolu olduğuna göre, onu modellemek için makine öğrenimi modelleri oluşturmaya başlayabiliriz.

Bol bol pratik yapmak için, her biri kendi deneyi olan bir dizi farklı model oluşturacağız. Daha sonra her modelin sonuçlarını karşılaştıracağız ve hangisinin en iyi performansı gösterdiğini göreceğiz.

Daha spesifik olarak, aşağıdakileri inşa edeceğiz:

  • Model 0: Naive Bayes (temel)
  • Model 1: İleri beslemeli sinir ağı (yoğun model)
  • Model 2: LSTM modeli
  • Model 3: GRU modeli
  • Model 4: Çift Yönlü-LSTM modeli
  • Model 5: 1B Evrişimli Sinir Ağı
  • Model 6: TensorFlow Hub Önceden Eğitilmiş Özellik Çıkarıcı (feature extraction)
  • Model 7: Eğitim verilerinin %10 ile model 6'nın aynısı

Model 0, diğer daha derin modellerin birbirini yenmesini bekleyeceğimiz bir temel elde etmek için en basit olanıdır.

Her deney aşağıdaki adımlardan geçecektir:

  • Modeli oluşturun
  • Modeli eğit
  • Modelle tahminler yapın
  • Daha sonra karşılaştırma için tahmin değerlendirme metriklerini takip edin

Model 0: Temel oluşturma

Tüm makine öğrenimi modelleme deneylerinde olduğu gibi, bir temel model oluşturmak önemlidir, böylece gelecekteki deneyler için üzerine inşa edebileceğiniz bir kıyaslama elde edersiniz.

Temel çizgimizi oluşturmak için, kelimelerimizi sayılara dönüştürmek için TF-IDF (terim frekansı-ters belge frekansı) formülünü kullanarak bir Scikit-Learn Pipeline oluşturacağız ve ardından bunları Multinomial Naive Bayes algoritması ile modelleyeceğiz. Bu, Scikit-Learn makine öğrenimi haritasına başvurularak seçildi.

📖 TD-IDF algoritması hakkında okunması gereken bir makale: https://www.onely.com/blog/what-is-tf-idf/

Pipeline(memory=None,
steps=[('tfidf', TfidfVectorizer(analyzer='word', binary=False,
decode_error='strict',
dtype=<class 'numpy.float64'>,
encoding='utf-8', input='content',
lowercase=True, max_df=1.0, max_features=None,
min_df=1, ngram_range=(1, 1), norm='l2',
preprocessor=None, smooth_idf=True,
stop_words=None, strip_accents=None,
sublinear_tf=False,
token_pattern='(?u)\\b\\w\\w+\\b',
tokenizer=None, use_idf=True,
vocabulary=None)),
('clf',
MultinomialNB(alpha=1.0, class_prior=None, fit_prior=True))],
verbose=False)

Multinomial Naive Bayes gibi sığ bir model kullanmanın yararı, eğitimin çok hızlı olmasıdır. Modelimizi değerlendirelim ve temel metriğimizi bulalım.

Our baseline model achieves an accuracy of: 79.27%

Temel modelimiz ile bazı tahminler yapmaya ne dersiniz?

array([1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1])

Model Deneylerimiz İçin Bir Değerlendirme Fonksiyonu Oluşturma

Bunları olduğu gibi değerlendirebiliriz, ancak ileride birkaç modeli aynı şekilde değerlendireceğimiz için, bir dizi tahmin ve kesinlik etiketi alan ve aşağıdakileri hesaplayan bir yardımcı fonksiyon oluşturalım:

  • Accuracy
  • Precision
  • Recall
  • F1-score

🔑 Not: Bir sınıflandırma sorunuyla uğraştığımız için yukarıdaki metrikler en uygun olanlardır. Bir regresyon problemi ile çalışıyor olsaydık, MAE (ortalama mutlak hata) gibi diğer metrikler daha iyi bir seçim olurdu.

{'accuracy': 79.26509186351706,
'f1': 0.7862189758049549,
'precision': 0.8111390004213173,
'recall': 0.7926509186351706}

Model 1: Basit Bir Yoğun (Dense) Model

İnşa edeceğimiz ilk derin model, tek katmanlı yoğun bir modeldir. Aslında, zar zor tek bir katmana sahip olacak.

Metnimizi ve etiketlerimizi girdi olarak alacak, metni simgeleştirecek, bir gömme oluşturacak, gömmenin ortalamasını bulacak (Küresel Ortalama Havuzlamayı kullanarak) ve ardından ortalamayı bir çıktı birimi ve bir sigmoid etkinleştirme işleviyle tam bağlantılı bir katmandan geçirecektir.

Önceki cümle kulağa ağız dolusu gibi geliyorsa, onu kodladığımızda mantıklı olacaktır (şüpheniz varsa, kodlayın).

Şimdi kullanıma hazır bir TensorBoard geri çağırma işlevimiz var, hadi ilk derin modelimizi oluşturalım.

İyi görünüyor. Modelimiz girdi olarak 1 boyutlu bir dize alır (bizim durumumuzda bir Tweet), ardından text_vectorizer kullanarak dizeyi belirtir ve gömmeyi kullanarak bir gömme oluşturur.

Daha sonra (isteğe bağlı olarak) çıktı katmanına ilettiğimiz tensörün boyutsallığını azaltmak için gömme katmanının çıktılarını havuzlarız.

Son olarak, havuzlama katmanının çıktısını sigmoid aktivasyonu ile yoğun bir katmana geçiriyoruz (sorunumuz ikili sınıflandırma olduğu için sigmoid kullanıyoruz).

Modelimizi verilerle fit etmeden önce onu derlememiz gerekiyor. İkili sınıflandırma ile çalıştığımız için, kayıp fonksiyonumuz ve Adam optimize edici olarak “binary_crossentropy" kullanacağız.

Model: "model_1_dense"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
input_1 (InputLayer) [(None, 1)] 0
_________________________________________________________________
text_vectorization_1 (TextVe (None, 15) 0
_________________________________________________________________
embedding (Embedding) (None, 15, 128) 1280000
_________________________________________________________________
global_average_pooling1d (Gl (None, 128) 0
_________________________________________________________________
dense (Dense) (None, 1) 129
=================================================================
Total params: 1,280,129
Trainable params: 1,280,129
Non-trainable params: 0
_________________________________________________________________

Eğitilebilir parametrelerin çoğu, gömme katmanında bulunur. 10.000 (input_dim=10000) boyutunda bir sözcük dağarcığı için 128 boyutunda (output_dim=128) bir yerleştirme oluşturduğumuzu, dolayısıyla 1.280.000 eğitilebilir parametre oluşturduğumuzu hatırlayın.

Pekala, modelimiz derlendi, 5 epoch kullanarak eğitim verilerimize fit edelim. Modelimizin eğitim ölçümlerinin log’lara kaydedildiğinden emin olmak için TensorBoard geri çağırma işlevimizi de ileteceğiz.

Saving TensorBoard log files to: model_logs/simple_dense_model/20210822-070929
Epoch 1/5
215/215 [==============================] - 5s 9ms/step - loss: 0.6113 - accuracy: 0.6904 - val_loss: 0.5373 - val_accuracy: 0.7559
Epoch 2/5
215/215 [==============================] - 1s 6ms/step - loss: 0.4417 - accuracy: 0.8187 - val_loss: 0.4700 - val_accuracy: 0.7887
Epoch 3/5
215/215 [==============================] - 1s 6ms/step - loss: 0.3467 - accuracy: 0.8619 - val_loss: 0.4552 - val_accuracy: 0.7979
Epoch 4/5
215/215 [==============================] - 1s 6ms/step - loss: 0.2842 - accuracy: 0.8914 - val_loss: 0.4623 - val_accuracy: 0.7913
Epoch 5/5
215/215 [==============================] - 1s 6ms/step - loss: 0.2373 - accuracy: 0.9113 - val_loss: 0.4754 - val_accuracy: 0.7848

Güzel! Bu kadar basit bir model kullandığımız için her epoch çok hızlı işliyor. Modelimizin doğrulama setindeki performansını kontrol edelim.

24/24 [==============================] - 0s 4ms/step - loss: 0.4754 - accuracy: 0.7848[0.4753652513027191, 0.7847769260406494]

Ve modelimizin eğitim loglarını TensorBoard ile takip ettiğimize göre, onları görselleştirmeye ne dersiniz? Bunu, TensorBoard log dosyalarımızı (model_logs dizininde bulunur) TensorBoard.dev’e yükleyerek yapabiliriz.

🔑 Not: TensorBoard.dev’e yüklediğiniz her şeyin herkese açık hale geleceğini unutmayın. Paylaşmak istemediğiniz antrenman kayıtları varsa yüklemeyin.

Güzel! Bunlar bazı renkli eğitim eğrileri. Modelin fazla mı yoksa yetersiz mi olduğunu söyler misiniz? İlk derin modelimizi oluşturduk ve eğittik, bir sonraki adım onunla bazı tahminler yapmak.

# tahminler yapma
model_1_pred_probs = model_1.predict(val_sentences)
model_1_pred_probs[:10]
array([[0.39653158],
[0.7810238 ],
[0.9976156 ],
[0.10114352],
[0.10857761],
[0.9328917 ],
[0.9154491 ],
[0.9949896 ],
[0.9680576 ],
[0.20620456]], dtype=float32)

Son katmanımız bir sigmoid aktivasyon fonksiyonu kullandığından, tahminlerimizi olasılıklar şeklinde geri alıyoruz.

Bunları tahmin sınıflarına dönüştürmek için tf.round() kullanacağız, yani 0,5'in altındaki tahmin olasılıkları 0'a ve 0,5'in üzerindekiler 1'e yuvarlanacaktır.

🔑 Not: Pratikte, bir sigmoid tahmin olasılığının çıktı eşiğinin mutlaka 0,5 olması gerekmez. Örneğin, test yoluyla, seçtiğiniz değerlendirme metrikleri için 0,25'lik bir kesmenin daha iyi olduğunu görebilirsiniz.

<tf.Tensor: shape=(20,), dtype=float32, numpy=
array([0., 1., 1., 0., 0., 1., 1., 1., 1., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 1.], dtype=float32)>

Şimdi modelimizin tahminlerini sınıflar şeklinde elde ettik, onları temel doğruluk doğrulama etiketleriyle karşılaştırmak için calculate_results() işlevimizi kullanabiliriz.

{'accuracy': 78.4776902887139,
'f1': 0.7818959205825942,
'precision': 0.789165199286798,
'recall': 0.7847769028871391}

İlk derin modelimizi temel modelimizle karşılaştırmaya ne dersiniz?

array([False, False, False, False])

Bu tür bir karşılaştırmayı (yeni modele kıyasla temel) birkaç kez yapacağımız için, bize yardımcı olacak bir fonksiyon oluşturalım.

Baseline accuracy: 79.27, New accuracy: 78.48, Difference: -0.79
Baseline precision: 0.81, New precision: 0.79, Difference: -0.02
Baseline recall: 0.79, New recall: 0.78, Difference: -0.01
Baseline f1: 0.79, New f1: 0.78, Difference: -0.00

Tekrarlayan Sinir Ağları (RNN’ler)

Bir sonraki modelleme deneylerimiz için, Tekrarlayan Sinir Ağı (RNN) adı verilen özel bir tür sinir ağı kullanacağız.

Bir RNN’nin önermesi basittir: gelecekte size yardımcı olması için geçmişten gelen bilgileri kullanın (tekrarlayan terimi buradan gelir). Başka bir deyişle, bir girdi (X) alın ve önceki tüm girdilere dayanarak bir çıktı (y) hesaplayın.

Bu kavram, özellikle doğal dil metinlerinin (Tweet’lerimiz gibi) pasajları gibi dizilerle uğraşırken yararlıdır.

Örneğin, bu cümleyi okuduğunuzda, mevcut köpek kelimesinin anlamını deşifre ederken önceki kelimeleri bağlam içine alırsınız. Geçerli bir kelime olan “köpek” kelimesini sonuna koydum ama cümlenin geri kalanı bağlamında bir anlam ifade etmiyor.

Bir RNN bir metin dizisine (zaten sayısal biçimde) baktığında, öğrendiği modeller dizinin sırasına göre sürekli olarak güncellenir.

Basit bir örnek için iki cümle alın:

  • Geçen hafta büyük deprem oldu, değil mi?
  • Geçen hafta büyük bir deprem olmadı.

Her ikisi de tamamen aynı kelimeleri içerir, ancak farklı anlamlara sahiptir. Sözcüklerin sırası anlamı belirler (noktalama işaretlerinin de anlamı dikte ettiği tartışılabilir, ancak basitlik adına, kelimelere odaklanalım).

Tekrarlayan sinir ağları, bir dizi dizi tabanlı problem için kullanılabilir:

  • Bire bir:
    bir girdi, bir çıktı, görüntü sınıflandırması gibi.
  • Birden çoğa:
    bir giriş, resim yazısı gibi birçok çıkış (resim girişi, resim yazısı çıkışı olarak bir metin dizisi).
  • Çoktan bire:
    birçok girdi, metin sınıflandırması gibi bir çıktı (bir Tweet’i gerçek hata veya gerçek hata değil olarak sınıflandırma).
  • Çoktan çoğa:
    birçok girdi, makine çevirisi (İngilizceden İspanyolcaya çevirme) veya konuşmayı metne (giriş olarak ses dalgası, çıktı olarak metin) gibi birçok çıktı.

Vahşi doğada RNN’lerle karşılaştığınızda, büyük olasılıkla aşağıdakilerin varyantlarıyla karşılaşacaksınız:

  • Uzun kısa süreli hafıza hücreleri (LSTM’ler).
  • Kapılı yinelenen birimler (GRU’lar).
  • Çift yönlü RNN’ler (bir dizi boyunca ileri ve geri, soldan sağa ve sağdan sola geçer).

Bunların her birinin ayrıntılarına girmek bu defterin kapsamı dışındadır (bunun yerine onları kullanmaya odaklanacağız), şimdilik bilmeniz gereken en önemli şey, dizileri modellemede çok etkili olduklarını kanıtladıklarıdır.

Yazmak üzere olduğumuz kodun perde arkasında neler olduğunu daha iyi anlamak için aşağıdaki kaynakları tavsiye ederim:

Model 2: LSTM

RNN’lerin ne olduğu ve ne işe yaradığıyla ilgili tüm bu konuşmalardan sonra, eminim siz de bir tane oluşturmaya heveslisinizdir. LSTM destekli bir RNN ile başlayacağız.

TensorFlow’da LSTM hücresinin (LSTM hücresi ve LSTM katmanı genellikle birbirinin yerine kullanılır) gücünden yararlanmak için tensorflow.keras.layers.LSTM() kullanacağız. api_docs/python/tf/keras/layers/LSTM).

Modelimiz model_1 ile çok benzer bir yapı alacak:

Input (metin) -> Tokenization -> Embedding -> Layers -> Output (etiket olasılığı)

Temel fark, gömme ve çıktımız arasına bir LSTM katmanı ekleyeceğimiz olacaktır.

(None, 15, 128)
(None, 64)

🔑 Not: TensorFlow LSTM katmanı için belgeleri okurken, çok sayıda parametre bulacaksınız . Bunların çoğu, mümkün olduğunca hızlı hesaplanmalarını sağlamak için ayarlanmıştır. Ayarlamak isteyeceğiniz başlıca olanlar “units” (gizli birimlerin sayısı) ve “return_sequences”dir (LSTM veya diğer tekrarlayan katmanları istiflerken bunu “True” olarak ayarlayın).

Şimdi LSTM modelimizi oluşturduk, hadi onu "binary_crossentropy" kaybı ve Adam optimizer kullanarak derleyelim.

Model: "model_2_LSTM"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
input_2 (InputLayer) [(None, 1)] 0
_________________________________________________________________
text_vectorization_1 (TextVe (None, 15) 0
_________________________________________________________________
embedding (Embedding) (None, 15, 128) 1280000
_________________________________________________________________
lstm (LSTM) (None, 64) 49408
_________________________________________________________________
dense_1 (Dense) (None, 1) 65
=================================================================
Total params: 1,329,473
Trainable params: 1,329,473
Non-trainable params: 0
_________________________________________________________________

İyi görünüyor! LSTM katmanımızda “model_1”den çok daha fazla eğitilebilir parametre fark edeceksiniz.

Bu sayının nereden geldiğini bilmek istiyorsanız, bir LSTM hücresindeki parametre sayısını hesaplamak için yukarıdaki kaynakları ve aşağıdakileri incelemenizi öneririm:

Şimdi ilk RNN modelimiz derlendi, onu eğitim verilerimizle fit edelim, doğrulama verileri üzerinde doğrulayalım ve TensorBoard geri çağrımızı kullanarak eğitim parametrelerini takip edelim.

Saving TensorBoard log files to: model_logs/LSTM/20210822-070941
Epoch 1/5
215/215 [==============================] - 7s 14ms/step - loss: 0.2219 - accuracy: 0.9237 - val_loss: 0.5674 - val_accuracy: 0.7795
Epoch 2/5
215/215 [==============================] - 2s 10ms/step - loss: 0.1562 - accuracy: 0.9429 - val_loss: 0.5965 - val_accuracy: 0.7730
Epoch 3/5
215/215 [==============================] - 2s 10ms/step - loss: 0.1307 - accuracy: 0.9510 - val_loss: 0.7095 - val_accuracy: 0.7769
Epoch 4/5
215/215 [==============================] - 2s 10ms/step - loss: 0.1053 - accuracy: 0.9606 - val_loss: 0.7949 - val_accuracy: 0.7874
Epoch 5/5
215/215 [==============================] - 2s 10ms/step - loss: 0.0886 - accuracy: 0.9673 - val_loss: 0.8062 - val_accuracy: 0.7808

Güzel! LSTM hücrelerini kullanan ilk eğitimli RNN modelimize sahibiz. Onunla bazı tahminler yapalım. Son katmandaki sigmoid aktivasyon fonksiyonu nedeniyle daha önce olduğu gibi aynı şey olacak, modelimizde predict() yöntemini çağırdığımızda sınıflardan ziyade tahmin olasılıklarını döndürecek.

((762, 1), array([[0.1147354 ],
[0.94750935],
[0.9996575 ],
[0.11396935],
[0.00261294],
[0.99850935],
[0.95742464],
[0.9997907 ],
[0.9996567 ],
[0.39139414]], dtype=float32))

Bu tahmin olasılıklarını en yakın tam sayıya yuvarlayarak tahmin sınıflarına dönüştürebiliriz (varsayılan olarak 0,5'in altındaki tahmin olasılıkları 0'a, 0,5'in üzerindekiler ise 1'e gidecektir).

<tf.Tensor: shape=(10,), dtype=float32, numpy=array([0., 1., 1., 0., 0., 1., 1., 1., 1., 0.], dtype=float32)>

Güzel, şimdi LSTM modelimizi değerlendirmek için caculate_results() işlevimizi ve bunu temel modelimizle karşılaştırmak için Compare_baseline_to_new_results() işlevimizi kullanalım.

{'accuracy': 78.08398950131233,
'f1': 0.7794817733933848,
'precision': 0.781486692298758,
'recall': 0.7808398950131233}
Baseline accuracy: 79.27, New accuracy: 78.08, Difference: -1.18
Baseline precision: 0.81, New precision: 0.78, Difference: -0.03
Baseline recall: 0.79, New recall: 0.78, Difference: -0.01
Baseline f1: 0.79, New f1: 0.78, Difference: -0.01

… devamı TensorFlow ile NLP — Giriş 3 yazımda.

--

--

Furkan Gulsen

🧑‍💻 As a software engineer, I write about software, artificial intelligence, productivity, and everything I know