Keras ile Ortak Kelime Dağarcığı (joint-vocabulary) Oluşturulması

Ortak kelime dağarcığı oluşturulması bir çok NLP problemi için gerekli bir adımdır. Çeviri (machine translation), sınıflandırma, named-entity tespiti gibi farklı çalışmalarda uygulamanın geliştirilmesi sonrasında (production aşamasında) giriş verilerinin eğitim ve test verileriyle aynı şekilde ele alınmasını sağlamak amacıyla ortak kelime dağarcığı (vocabulary) kullanılması gerekmektedir.

Keras tokinizer sınıfı ile ilgi yazıda bahsedilen aşamalar incelendiğinde sonuç olarak kelimelerin birer sayısal değere atandığı görülmektedir. Tokenizer nesnesinin fit() metodu veri setine her uygulandığında kelimelere (probleme göre karakterlere) yeniden nümerik değer ataması yapılır. Bu da aynı kelimenin farklı veri setlerinde farklı sayısal değerlerle temsil edilmesi anlamına gelir. Kelime vektörlerinin Word2Vec gibi yaklaşımlarla embedding olarak ele alınması durumunda uygulamaya verilecek zarar her ne kadar azaltılmış olsa da aynı konfigürasyonlarla (kelime id’si, en sık geçen kaç kelimenin kullanılacağı) üretilmiş vocabulary ile daha iyi sonuç alınacaktır.

Bu yazıda joint vocabulary oluşturmak için 2 farklı tokenizer kullanılıp bunların daha sonra birleştirilmesi yaklaşımı açıklanmıştır.

data_1:
kelime8 kelime2 kelime3
kelime4 kelime5 kelime6 kelime7
kelime8 kelime2 kelime3 kelime8

data_2:
kelime1 kelime8 kelime8 kelime6
kelime7 kelime8 kelime9 kelime5 kelime6

Olmak üzere data_1 ve data_2 farklı metin veri setleri olsun. Örneğin kolay anlaşılması açısından kısa ve sembolik metinler seçilmiştir. 

Bu cümlelerden oluşan verileri manuel olarak dizilere aktarımını yapılmıştır.

Tokenizer parametrelerinden en uzun cümle uzunluğu (MAX_SEQUENCE_LENGTH) 8 seçilmiştir ve en çok geçen (MAX_NUM_WORDS) 4 kelimenin kullanılması için ayarlanmıştır.

import numpy as np
from keras.preprocessing.text import Tokenizer
from keras.preprocessing.sequence import pad_sequences

data_1 = np.array(["kelime8 kelime2 kelime3", "kelime4 kelime5 kelime6 kelime7", 
"kelime8 kelime2 kelime3 kelime8"])
data_2 = np.array(["kelime1 kelime8 kelime8 kelime6", "kelime7 kelime8 kelime9 kelime5 kelime6"])
MAX_NUM_WORDS = 4
MAX_SEQUENCE_LENGTH = 8

data_1 için tokenizer1, data_2 için tokenizer2 nesneleri bağımsız olarak ayarlanır:

tokenizer1 = Tokenizer(num_words=MAX_NUM_WORDS)
tokenizer1.fit_on_texts(data_1)
tokenizer2 = Tokenizer(num_words=MAX_NUM_WORDS)
tokenizer2.fit_on_texts(data_2)
print(tokenizer1.word_index)
print("- - - - - - - - -- - - - - - -")
print(tokenizer1.word_counts)
print("\n # # # # # # # # # # # # # # # \n")
print(tokenizer2.word_index)
print("- - - - - - - - -- - - - - - -")
print(tokenizer2.word_counts)
{'kelime2': 2, 'kelime3': 3, 'kelime6': 6, 'kelime7': 7, 'kelime4': 4, 'kelime5': 5, 'kelime8': 1}
- - - - - - - - -- - - - - - -
OrderedDict([('kelime8', 3), ('kelime2', 2), ('kelime3', 2), ('kelime4', 1), ('kelime5', 1), ('kelime6', 1), ('kelime7', 1)])

 # # # # # # # # # # # # # # # 

{'kelime1': 3, 'kelime6': 2, 'kelime7': 4, 'kelime5': 6, 'kelime8': 1, 'kelime9': 5}
- - - - - - - - -- - - - - - -
OrderedDict([('kelime1', 1), ('kelime8', 3), ('kelime6', 2), ('kelime7', 1), ('kelime9', 1), ('kelime5', 1)])

Burada Keras tokenizer’ın bilinçli olarak yaptığı bir hatalı davranış söz konusudur. En fazla 4 kelimenin işleme alınacağı belirtilmesine rağmen bütün kelimeler word_index ve word_counts listelerinde mevcut. Bu iki tokenizer’ın birleştirilmesi esnasında ekstra iş çıkarması yönünden zahmetli fakat MAX_NUM_WORDS parametresi daha sonradan değiştirmek istendiğinde fit() metodunun yeniden uygulanmasını gerektirmeyeceği için faydalı bir özelliktir.

Bu nedenle iki tokenizer birleştirilmeden önce her veri seti için en çok geçen 4 kelimeyle sınırlama yaparak iki veri setinin de en çok geçen geçen kelimelerinin üretilecek yeni tokenizer’da mevcut olması sağlanmalıdır. Böylelikle fine-tuning gibi çalışmalarda daha büyük olan veri setinin çok fazla temsil edilip, daha küçük olan veri setinde en çok geçen kelimelerin yeterenci iyi öğrenilmemesi engellenmiş olunur.

en çok geçen 4 (MAX_NUM_WORDS) kelimelerin çıkarımı:

import collections
most_common1 = collections.Counter(tokenizer1.word_counts).most_common(MAX_NUM_WORDS)
most_common2 = collections.Counter(tokenizer2.word_counts).most_common(MAX_NUM_WORDS)
print(most_common1)
print(most_common2)
[('kelime8', 3), ('kelime2', 2), ('kelime3', 2), ('kelime6', 1)]
[('kelime8', 3), ('kelime6', 2), ('kelime2', 1), ('kelime7', 1)]

En çok geçen kelimeler alındıktan sonra yeni oluşacak tokenizer’da kelimelerin tekrar etmemesi için matematikteki küme işlemi özelliklerini gösteren set() sınıfı ile birleştirme işlemi yapılır.

#tekrar etmeyecek şekilde birleştirme işlemi
a = set()
for i,k in most_common1:
a.add(i)
for i,k in most_common2:
a.add(i)
print a
set(['kelime2', 'kelime3', 'kelime1', 'kelime6', 'kelime7', 'kelime8'])

 

Bu kelimeler ile nihai tokenizer üretilip bütün veri setlerine uygulanır:

# kelime sayısı en fazla başlangıçta belirlenen sayının 2 katı olabilir
tokenizer3 = Tokenizer(num_words = 2*MAX_NUM_WORDS)
tokenizer3.fit_on_texts(a)


sequences1 = tokenizer3.texts_to_sequences(data_1)
sequences2 = tokenizer3.texts_to_sequences(data_2)

print(sequences1)
print(sequences2)
[[6, 1, 2], [4, 5], [6, 1, 2, 6]]
[[3, 6, 6, 4], [5, 6, 4]]

Görüldüğü üzere aynı kelimeler 2 farklı veri setinde de aynı sayılar ile indekslenmiştir.

Kelimeler ve id’leri şu şekilde güncellendi:

tokenizer3.word_index
{'kelime1': 3,
 'kelime2': 1,
 'kelime3': 2,
 'kelime6': 4,
 'kelime7': 5,
 'kelime8': 6}

Padding eklenerek data_1 ‘in kullanıma hazır hale getirilmesi:

x_train1 = pad_sequences(sequences1, maxlen=MAX_SEQUENCE_LENGTH)
print(x_train1)
[[0 0 0 0 0 6 1 2]
 [0 0 0 0 0 0 4 5]
 [0 0 0 0 6 1 2 6]]

data_2 ‘nin kullanıma hazır hale getirilmesi:

x_train2 = pad_sequences(sequences2, maxlen=MAX_SEQUENCE_LENGTH)
print(x_train2)
[[0 0 0 0 3 6 6 4]
 [0 0 0 0 0 5 6 4]]

 

Kodların tamamı için github:

https://github.com/irhallac/deep_learning_examples/blob/master/keras_tokenizer/joint_vocab_keras_tokenizer.ipynb