-
Notifications
You must be signed in to change notification settings - Fork 210
Zemberek NLP ile Metin Sınıflandırma
Zemberek-NLP 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 oldukça meşakkatli bir iş yaparak 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. Bu yazıdaki işlemlerin büyük kısmı fastText kütüphanesi kullanılarak da yapılabilir.
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)
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ı 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
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.
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.
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
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
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
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)
Sınıflandırma mekanizmasının bir sorunu model boyutunun çok büyük olması. Bunun için de fastText kütüphanesini geliştirenler kuantalama ve kırpma mekanizması geliştirmişler. 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 0.796 başarım değeri elde ediliyor.
Tüm bu işlemleri aynı anda yapan bir örnek kodu examples modülündeki NewsTitleCategoryFinder sınıfında görebilirsiniz. Sadece main fonksiyon içerisinde ilk ham veri setinin yolunu vermeniz yeterli. Sistem tokenize ve lemma veri setlerini üretip testleri yapıyor ve sonuçlarını aynı dizine yazıyor.
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