İçeriğe geç
← Blog

Az Veriyle Metin Sınıflandırma: 46 Örnekle 0.82 Macro-F1

Burak Berk Şama · · 4 dk okuma · Yapay Zekâ
Az Veriyle Metin Sınıflandırma: 46 Örnekle 0.82 Macro-F1

Kısaca: Elinizde yalnızca birkaç düzine etiketli örnek varsa, metin sınıflandırma için TF-IDF genellikle yetersiz kalır. Önceden eğitilmiş bir dil modelinin cümle gömmeleri üzerine kurulan basit bir lojistik regresyon, aynı veriyle çok daha iyi sonuç verebilir. Bir haber akışında finans dışı başlıkları ayıklamak için kurduğum filtrede, 46 konu dışı örnekle TF-IDF 0.69 macro-F1 verirken gömme tabanlı model 0.82'ye ulaştı.

Bu yazı, finansal haberlerden duygu analizi projemdeki küçük ama kritik bir bileşenin hikâyesi: finans dışı haber filtresi.

Sorun: Duygu modeli her şeye etiket basıyor

Canlı haber kaynaklarından başlık çekmeye başladığımda bir sorunla karşılaştım: Akışta yalnızca finans haberleri yoktu. Spor, magazin ve sağlık başlıkları da geliyordu ve duygu modeli bunlara da güvenle "pozitif" ya da "negatif" diyordu. "Milli takım hazırlık maçında galip geldi" başlığının piyasa açısından pozitif olarak sayılması, günün duygu oranlarını bozuyordu.

Çözüm, duygu modelinden önce çalışan ikili bir sınıflandırıcıydı: finans mı, konu dışı mı?

Neden TF-IDF yetmedi?

İlk denemem en basit yaklaşımdı: TF-IDF ile vektörleştirip lojistik regresyon eğitmek. Elimde ise yalnızca 46 konu dışı örnek vardı.

Sonuç beklendiği gibi zayıftı: Çapraz doğrulamada macro-F1 0.69, konu dışı sınıfın geri çağırma oranı ise 0.43. Yani konu dışı başlıkların yarısından fazlası filtreden geçiyordu.

Sebep basit: TF-IDF yalnızca kelime eşleşmesine bakar. Eğitimde "maç" kelimesini görmediyse, "derbi" içeren bir başlığın spor haberi olduğunu bilemez. Az veride kelime dağarcığının kapsamı çok dardır.

Çözüm: Önceden eğitilmiş gömmeler

Önceden eğitilmiş bir dil modeli, milyarlarca cümleden kelimeler ve kavramlar arasındaki ilişkiyi öğrenmiştir. "Derbi" ile "maç"ın aynı dünyaya ait olduğunu zaten bilir. Bu bilgiyi kullanmak için modeli ince ayara sokmanıza gerek yoktur; yalnızca cümleleri sabit uzunlukta vektörlere (gömmelere) dönüştürmeniz yeterlidir.

Duygu modelinde kullandığım XLM-RoBERTa gövdesini burada da kullandım ve son katmandaki token temsillerinin dikkat maskesiyle ağırlıklı ortalamasını (mean pooling) aldım:

class Embedder:
    """Ortalama havuzlanmış cümle gömmesi. Duygu modeliyle aynı gövdeyi paylaşır."""

    def __init__(self, path, device=None):
        self.tok = AutoTokenizer.from_pretrained(str(path))
        self.model = AutoModel.from_pretrained(str(path)).eval()
        self.device = device or ("mps" if torch.backends.mps.is_available() else "cpu")
        self.model.to(self.device)

    @torch.no_grad()
    def __call__(self, texts, bs=32):
        out = []
        for i in range(0, len(texts), bs):
            enc = self.tok(list(texts[i:i + bs]), padding=True, truncation=True,
                           max_length=64, return_tensors="pt").to(self.device)
            h = self.model(**enc).last_hidden_state
            m = enc["attention_mask"].unsqueeze(-1).float()
            out.append(((h * m).sum(1) / m.sum(1)).cpu().numpy())
        return np.vstack(out)

Maskeyi kullanmak önemli: Doldurma (padding) token'ları ortalamaya katılırsa kısa başlıkların gömmesi bozulur.

Ardından bu vektörler üzerinde sıradan bir lojistik regresyon eğittim:

X = Embedder(MODEL_PATH)(texts)
clf = LogisticRegression(max_iter=3000, C=1.0, class_weight="balanced").fit(X, y)

Sonuçlar

Yaklaşım macro-F1 Konu dışı geri çağırma
TF-IDF + lojistik regresyon 0.69 0.43
XLM-R gömmesi + lojistik regresyon 0.82 0.78
Aynı model + 294 ek başlık 0.84 —

Aynı veriyle, aynı sınıflandırıcıyla; tek fark girdinin temsili. Bu, az veriyle çalışırken temsilin modelden daha önemli olabildiğini net biçimde gösteriyor.

Etiketli veriyi büyütmek: dil modeliyle etiketleme

Filtreyi iyileştirmek için 294 Türkçe başlığı bir büyük dil modeline etiketlettim ve eğitime ekledim; skor 0.84'e çıktı. Burada bir kurala özellikle dikkat ettim: Benim elle etiketlediğim başlıklar test için ayrı tutuldu, dil modeli etiketleri yalnızca eğitimde kullanıldı. Aksi hâlde model, değerlendirildiği veriyle aynı kaynaktan beslenmiş olurdu.

Bu yöntem ne zaman işe yarar?

  • Elinizde onlarca ya da birkaç yüz etiketli örnek varsa,
  • Görev, metnin genel anlamına dayanıyorsa (konu, niyet, alan tespiti),
  • Hızlı ve açıklanabilir bir model istiyorsanız.

Binlerce etiketli örneğe ulaştığınızda ise modelin tamamını ince ayara sokmak genellikle daha yüksek başarı verir. Bunu XLM-RoBERTa ince ayarı yazısında anlattım.

Sıkça sorulan sorular

Az veriyle metin sınıflandırma nasıl yapılır?

Önceden eğitilmiş bir dil modeliyle metinleri cümle gömmelerine dönüştürüp bu vektörler üzerinde lojistik regresyon gibi basit bir sınıflandırıcı eğitmek, az veride TF-IDF'e göre genellikle çok daha iyi sonuç verir.

Cümle gömmesi (sentence embedding) nedir?

Bir cümlenin anlamını sabit uzunlukta bir sayı vektörü olarak temsil etmesidir. Anlamca yakın cümlelerin vektörleri de birbirine yakın olur.

Mean pooling neden dikkat maskesiyle yapılmalı?

Toplu işlemde kısa metinler doldurma token'larıyla uzatılır. Ortalamaya bu token'lar katılırsa kısa metinlerin temsili bozulur; maske yalnızca gerçek token'ların ortalamasını almayı sağlar.

Dil modeliyle etiketlenmiş veri güvenilir mi?

Eğitim verisini büyütmek için faydalı olabilir, ancak değerlendirme her zaman insan tarafından etiketlenmiş ve eğitimde kullanılmamış bir set üzerinde yapılmalıdır.

TF-IDF ne zaman hâlâ iyi bir seçimdir?

Bol etiketli veri olduğunda, sınıflar belirgin anahtar kelimelerle ayrıldığında ve çok hızlı, hafif bir modele ihtiyaç duyulduğunda TF-IDF güçlü bir taban çizgisidir.

Python Makine Öğrenmesi Doğal Dil İşleme