Skip to content

Zemberek NLP ile Metin Sınıflandırma

Ahmet A. Akın edited this page Aug 4, 2018 · 16 revisions

Giriş

Projenin 0.15.0 sürümü ile gelen en büyük değişiklik metin sınıflandırma (text classification) modülünün eklenmesi oldu. Bu modül, doküman kategorisi bulma, duygu analizi ya da spam tespiti gibi farklı amaçlarla kullanılabilir.

Bu özelliği eklemek için C++ ile geliştirilen fastText kütüphanesini Java'ya taşıdık. Aslında fastText kütüphanesi kendi başına da kullanılabilirdi ama çalışma anında doğrudan sınıflandırıcıya erişme ve biraz da sistemin iç yapısını öğrenme adına bu yolu seçtik.

Sonraki kısımlarda metin sınıflandırma işlemini bir örnek ile açıklayacağız (Orijinal fastText proje sayfasında da benzer bir eğitim dokümanı var)

Veri

Amacımız sadece gazete haber başlıklarına bakarak kategorisini bulmak. Bunun için 2017 yılında bir internet haber sitesinden indirilen haberlerin başlıklarından üretilen bir veri setini kullanacağız ( news-title-category-set dosyasını şu bağlantıdan indirebilirsiniz). Veride her satırda bir haber başlığı ve haberin kategorisi yer alıyor. Bazı satırlar:

__label__magazin Jackie Chan'a yapmadıklarını bırakmadılar!
__label__spor Fenerbahçe Akhisar'da çok rahat kazandı 
__label__teknoloji Google Nexus telefonları Huawei de üretebilir!

Veri kümesinde 68365 satır yer alıyor. Bunun ilk bin satırını test, kalanını eğitim verisi olarak ayıralım.

$ head -n 1000 news-title-category-set >raw.test
$ tail -n 67365 news-title-category-set >raw.train

Model Üretimi

Sistemin eğitimi için yine 0.15.0 ile gelen zemberek-full.jar dosyası kullanılabilir. Aşağıdaki komut satırı uygulaması ile sınıflandırma modeli üretilir.

$ java -jar zemberek-full.jar TrainClassifier -i raw.train -o raw.model -ec 50 -lr 0.1

oluşan raw.model dosyası ile test ve çalışma anı sınıflandırma işlemi gerçekleştirilebilir.

Test

Sistemin başarımını ölçmek için aşağıdaki uygulama çalıştırılabilir.

$ java -jar zemberek-full.jar EvaluateClassifier -m raw.model -i raw.test

P@1: 0.718  R@1: 0.718  Number of examples = 999

Sistem test verisinin %71'ini doğru tahmin etti.

Metin ön işleme

Başarımı arttırmak için eğitim verisini ham olarak değil biraz işleyerek model üretebiliriz. Ham veride noktalama işaretleri ve büyük harfler nedeniyle kelime çeşitliliği çok fazla. Örneğin "üretebilir!" ile "üretebilir" ayrı kelime olarak ele alınıyor. Bunun için aynı bağlantıdaki news-title-category-set.tokenized dosyasını indirelim. Bu dosyada ham veri tokenization işleminden geçiriliyor (noktalama işaretleri kelimelerden ayrılıyor ve bazıları siliniyor) ve küçük harf dönüşümü uygulanıyor.

__label__magazin jackie chan'a yapmadıklarını bırakmadılar
__label__spor fenerbahçe akhisar'da çok rahat kazandı
__label__teknoloji google nexus telefonları huawei de üretebilir

Bu veriden yeniden eğitim ve test seti üretip test yapabiliriz.

$ head -n 1000 news-title-category-set.tokenized >tokenized.test
$ tail -n 67365 news-title-category-set.tokenized >tokenized.train
$ java -jar zemberek-full.jar TrainClassifier -i tokenized.train -o tokenized.model -ec 50 -lr 0.1
$ java -jar zemberek-full.jar EvaluateClassifier -m tokenized.model -i tokenized.test

Ham veride 76 bin kelime türü varken yeni kümede 57 bin kelime türü oluştu. Bu, sistemin daha iyi öğrenmesini sağlar.

Sonuç: P@1: 0.735  R@1: 0.735  Number of examples = 999

Bulma oranı %73,5'e çıktı. Başarımı arttırmak için modele ikili kelime bilgisini de ekleyebiliriz. Bunun için eğitim sırasında --wordNGrams 2 parametresi eklenir.

$ java -jar zzemberek-full.jar TrainClassifier -i tokenized.train -o tokenized.model -ec 50 -lr 0.1 --wordNGrams 2

Modeli tekrar üretip test yaptığımızda sonuç:

P@1: 0.777  R@1: 0.777

Bulma oranı %77,7'ye çıktı.

Kelime yerine kelime gövdesi kullanmak

Daha da iyi bir sonuç elde etmek için kelimenin yerine en uzun kelime gövdelerini kullanan bir veri seti üretmeyi deneyebiliriz. Bunun için aynı bağlantıdaki news-title-category-set.lemma setini indirelim. Bu durumda veri aşağıdaki gibi olabilir.

__label__magazin jackie chan yapmadık bırak
__label__spor fenerbahçe akhisar çok rahat kazan
__label__teknoloji google nexus telefon huawei de üret

Bu veriden eğitim ve test setini ayırıp model üretip test yaptığımızda:

$ head -n 1000 news-title-category-set.lemmas >lemmas.test
$ tail -n 67365 news-title-category-set.lemmas >lemmas.train
$ java -jar zemberek-full.jar TrainClassifier -i lemmas.train -o lemmas.model -ec 50 -lr 0.1 --wordNGrams 2
$ java -jar zemberek-full.jar EvaluateClassifier -m lemmas.model -i lemmas.test

Bu durumda kelime türü sayısı 57 binden 25 bine düştü. Test sonucu:

P@1: 0.800  R@1: 0.800

Bulma oranı %80'e çıktı. Metin sınıflandırma işlemini terminalden girilen kendi verilerimizle de deneyebiliriz. Bunun için

java -jar zemberek-full.jar ClassificationConsole -m lemmas.model -p LEMMA -k 3

Sonrasında kullanıcının girdiği her haber başlığı için sistem en iyi üç tahminde bulunur. Örneğin:

Preprocessing type = LEMMA
Enter sentence:
Beşiktaş berabere kaldı

Processed Input = beşiktaş berabere kal
Predictions   = spor (0.000010), türkiye (-11.511991), teknoloji (-11.512634)

Turp her derde deva
Processed Input = turp her dert deva
Predictions   = sağlık (-0.002959), kaliteli_yaşam (-7.008953), ekonomi (-7.610506)

Model küçültme

Sınıflandırma mekanizmasının bir sorunu model boyutunun çok büyük olması. Bunun için fastText kütüphanesinin sunduğu kuantalama ve kırpma mekanizmasının kodunu da Java'ya taşıdık. Model üretilirken girilen iki parametre ile model boyutu yüzlerce kat küçültülebiliyor. Bu durumda başarımda küçük bir düşme yaşanabiliyor. Örneğin:

$ java -jar zemberek-full.jar TrainClassifier -i lemmas.train -o lemmas.model -ec 50 -lr 0.1 --wordNGrams 2 --applyQuantization --cutOff 25000

çalıştırıldığında lemmas.model dosyasının yanında bir de lemmas.model.q dosyası üretilecektir. Artık sınıflandırma için istenirse bu dosya da kullanılabilir. Orijinal model dosyası 400 MB iken küçültme işleminden geçirilen model dosyası 1 MB boyutunda oluşuyor. Bu model ile test yapıldığında %79,6 başarım değeri elde ediliyor.

Başarım özeti

Veri %Başarım
Ham Veri 71,8
Tokenization + Küçük Harf 73,5
Tokenization + Küçük Harf + 2-gram 77,7
Kelime gövdesi + Küçük Harf + 2-gram 80
Kelime gövdesi + Küçük Harf + 2-gram (küçük model) 79,6

Örnek Uygulama

Bu yazıdaki çalışmanın bir benzerini examples modülündeki NewsTitleCategoryFinder sınıfında bulabilirsiniz. main metodundaki veri seti yolunu kendi sisteminizdeki veri seti ile değiştirerek çalıştırdığınızda tokenization ve kelime gövdesi verilerinin üretimi, eğitim ve test seti üretme, model küçültme ve test işlemlerini yapacaktır. Oluşan tüm dosyalar veri seti ile aynı dizine yazılır.

API ile sınıflandırma

Oluşan sınıflandırma modellerine API ile erişim mümkündür. Örneğin:

FastTextClassifier classifier = FastTextClassifier.load(modelPath);
String s = "Beşiktaş berabere kaldı."    

// Girdinin eğitim verisi ile aynı şekilde işlenmesi gerekir.
String processed = String.join(" ", TurkishTokenizer.DEFAULT.tokenizeToStrings(s));
processed = processed.toLowerCase(Turkish.LOCALE);

// En yüksek skorlu üç sonucu al ve yazdır.
List<ScoredItem<String>> res = classifier.predict(processed, 3);
res.forEach(System.out::println);

Çıktı şu şekilde olabilir:

__label__spor : 0.000010
__label__türkiye : -11.483298
__label__yaşam : -11.512561