İçeriğe geç
← Blog

XLM-RoBERTa ile Türkçe Metin Sınıflandırma: Adım Adım

Burak Berk Şama · · 5 dk okuma · Yapay Zekâ
XLM-RoBERTa ile Türkçe Metin Sınıflandırma: Adım Adım

Kısaca: XLM-RoBERTa, Türkçe dahil yaklaşık yüz dilde önceden eğitilmiş bir dil modelidir ve Hugging Face transformers kütüphanesiyle birkaç saatte metin sınıflandırma görevine uyarlanabilir. Bu yazıda Türkçe ve İngilizce finans başlıklarında duygu sınıflandırması için yaptığım ince ayarı adım adım anlatıyorum: veri birleştirme ve sızıntı önleme, sınıf ağırlıklı kayıp fonksiyonu, eğitim ayarları ve değerlendirme. Sonuç: karışık test setinde 0.79 macro-F1 (İngilizce 0.85, Türkçe 0.78).

Bu yazı, finansal haberlerden duygu analizi projemin teknik tarafını anlatıyor. Projenin "kıyaslama seti ile gerçek dünya arasındaki fark" hikâyesini ayrı bir yazıda anlattım; burada odak, ince ayarın kendisi.

Neden XLM-RoBERTa?

Türkçe metin sınıflandırmada iki yol var: yalnızca Türkçe ile eğitilmiş bir model ya da çok dilli bir model. Benim veri setim Türkçe ve İngilizce cümlelerin karışımıydı ve aynı servisin iki dilde de çalışması gerekiyordu. XLM-RoBERTa bu yüzden doğal bir seçimdi:

  • Tek bir model iki dili de anlar; ayrı servis gerekmez.
  • İngilizce veride öğrenilen sinyal, Türkçe tarafa da kısmen aktarılır.
  • xlm-roberta-base boyutu, dizüstü bir bilgisayarda (Apple Silicon, MPS) eğitilebilecek kadar makuldür.

Adım 1: Veriyi birleştirmek ve sızıntıyı önlemek

Eğitim verisi üç kaynaktan geliyordu: İngilizce Financial PhraseBank, PhraseBank'in Türkçe çevirisi ve ITU'nun KAP başlıkları. Burada kolayca gözden kaçan bir tuzak var: Bir İngilizce cümle ile onun Türkçe çevirisi farklı kümelere düşerse, model test setindeki cümleyi aslında eğitimde "görmüş" olur ve skor yapay olarak şişer.

Çözüm, her cümleye bir group anahtarı vermek ve bölmeyi satıra göre değil gruba göre yapmak:

def _bucket(group: pd.Series) -> pd.Series:
    return group.map(lambda g: int(hashlib.md5(g.encode()).hexdigest(), 16) % 10)

b = _bucket(df.group)
splits = {"train": df[b >= 2], "val": df[b == 1], "test": df[b == 0]}

Çeviri, İngilizce orijinalle aynı group değerini aldığı için ikisi her zaman aynı kümeye düşer. Hash tabanlı bölme ayrıca deterministiktir: veri yeniden üretildiğinde kümeler değişmez.

Adım 2: Tokenizer ve veri kümesi

from datasets import Dataset
from transformers import AutoTokenizer

tok = AutoTokenizer.from_pretrained("xlm-roberta-base")

def to_ds(df, tok, max_len):
    ds = Dataset.from_pandas(df[["text", "label"]].reset_index(drop=True))
    ds = ds.map(lambda b: tok(b["text"], truncation=True, max_length=max_len), batched=True)
    ds = ds.map(lambda b: {"labels": [LABEL2ID[l] for l in b["label"]]}, batched=True)
    return ds.remove_columns(["text", "label"])  # string sütunlar collator'ı bozar

Haber başlıkları kısa olduğu için max_length=64 yeterli; bu hem belleği hem eğitim süresini ciddi ölçüde azaltır. Son satırdaki remove_columns küçük ama önemli: metin sütunları veri kümesinde kalırsa DataCollatorWithPadding hata verir.

Adım 3: Sınıf dengesizliği için ağırlıklı kayıp

Finans verisinde sınıflar dengeli değildir. İlk refleks, scikit-learn'deki "balanced" ağırlıkları kullanmaktır. Bu, benim veri setimde nötr sınıfı fazla bastırdı. Daha yumuşak bir denge için ağırlıkların karekökünü aldım ve bunu özel bir Trainer ile kayıp fonksiyonuna verdim:

counts = tr.label.value_counts().reindex(LABELS).values
cw = np.sqrt(len(tr) / (len(LABELS) * counts)).tolist()

class WeightedTrainer(Trainer):
    def __init__(self, class_weights, **kw):
        super().__init__(**kw)
        self.cw = torch.tensor(class_weights, dtype=torch.float)

    def compute_loss(self, model, inputs, return_outputs=False, **kw):
        labels = inputs.pop("labels")
        out = model(**inputs)
        loss = torch.nn.functional.cross_entropy(
            out.logits, labels, weight=self.cw.to(out.logits.device))
        return (loss, out) if return_outputs else loss

Adım 4: Eğitim ayarları

args = TrainingArguments(
    output_dir="models/xlm-roberta-base/ckpt",
    num_train_epochs=3, learning_rate=2e-5,
    per_device_train_batch_size=32, per_device_eval_batch_size=64,
    warmup_steps=40, weight_decay=0.01,
    eval_strategy="epoch", save_strategy="epoch",
    load_best_model_at_end=True, metric_for_best_model="macro_f1",
    save_total_limit=1, report_to="none", dataloader_pin_memory=False,
)

Birkaç not:

  • metric_for_best_model="macro_f1": Doğruluk (accuracy) dengesiz veride yanıltır; en iyi kontrol noktası macro-F1'e göre seçilir.
  • 3 epoch ve 2e-5 öğrenme oranı: Önceden eğitilmiş modellerde ince ayar için güvenli bir başlangıç. Daha fazlası genellikle aşırı öğrenmeye yol açar.
  • dataloader_pin_memory=False: Apple Silicon (MPS) üzerinde gereksiz uyarıları ve sorunları önler.

Adım 5: Değerlendirme — dil bazında bakmak

Tek bir genel skor, iki dilli bir modelde hangi dilin zayıf kaldığını gizler. Bu yüzden test setini dile göre ayrıca değerlendirdim:

Değerlendirme macro-F1
Taban model (TF-IDF + lojistik regresyon) 0.69
XLM-RoBERTa, karışık test seti 0.79
XLM-RoBERTa, yalnızca İngilizce 0.85
XLM-RoBERTa, yalnızca Türkçe 0.78

Kıyaslama setinin ötesi

Bu skorlar kıyaslama setine aittir. Modeli gerçek, günlük Türkçe haber başlıklarında denediğimde macro-F1 0.53'e düştü; sebep, eğitim verisi ile gerçek başlıklar arasındaki domain shift idi. Çapraz doğrulamayla kalibre ettiğim bir nötr karar eşiği skoru 0.72'ye çıkardı. Bu sürecin tamamını şu yazıda anlattım. Ders şu: İnce ayarı bitirmek, işin yarısıdır; asıl test gerçek veridir.

Sıkça sorulan sorular

XLM-RoBERTa Türkçe destekliyor mu?

Evet. XLM-RoBERTa, Türkçe dahil yaklaşık yüz dilde metinle önceden eğitilmiştir ve Türkçe metin sınıflandırma görevleri için ince ayar yapılabilir.

Türkçe metin sınıflandırma için ne kadar veri gerekir?

Kesin bir sayı yoktur. Önceden eğitilmiş modeller birkaç bin etiketli örnekle iyi sonuç verebilir; daha az veride, model gömmeleri üzerine basit bir sınıflandırıcı kurmak daha verimli olabilir.

XLM-RoBERTa ince ayarı için GPU şart mı?

xlm-roberta-base boyutundaki bir model, kısa metinlerde Apple Silicon (MPS) gibi yerel donanımda da eğitilebilir. Büyük veri setleri ve large modeller için GPU belirgin şekilde hız kazandırır.

Dengesiz sınıflarda hangi metrik kullanılmalı?

Macro-F1, her sınıfa eşit ağırlık verdiği için dengesiz veri setlerinde doğruluktan daha güvenilir bir göstergedir.

Eğitim ve test setleri arasında veri sızıntısı nasıl önlenir?

Aynı içeriğin farklı biçimlerini (örneğin bir cümle ve çevirisi) ortak bir grup anahtarıyla işaretleyip bölmeyi satıra göre değil gruba göre yapmak, sızıntıyı önlemenin en güvenilir yoludur.

Python Yapay Zekâ Makine Öğrenmesi Doğal Dil İşleme