Hugging Face ile Türkçe Metin Özetleme Modeli Kullanımı
Metin özetleme ihtiyacı, aslında çoğu zaman beklenmedik yerden geliyor. Geçen ay bir müşteri projesinde logları analiz ederken fark ettim: operasyon ekibi her gün onlarca sayfalık incident raporunu okumak zorunda kalıyordu. “Bunu otomatize edemez miyiz?” sorusu, beni Hugging Face ekosistemiyle ciddi anlamda uğraşmaya itti. Türkçe metin özetleme, üstelik production ortamında çalışan bir şey. Bugün bu deneyimi paylaşacağım.
Neden Türkçe Özetleme Zor?
Türkçe, morfolojik açıdan son derece zengin bir dil. Bir Türkçe token, İngilizce’deki beş altı kelimelik anlamı taşıyabilir. Bu durum, tokenizasyondan başlayarak modelin anlama kapasitesine kadar her şeyi etkiliyor. Çoğu genel amaçlı model Türkçe için yetersiz kalıyor ya da anlamlı çıktı üretemiyor.
Hugging Face Hub’da Türkçe’ye özel fine-tune edilmiş modeller var ve bunları kullanmak, sıfırdan model geliştirmeye kıyasla ciddi zaman ve kaynak tasarrufu sağlıyor. Ama doğru modeli seçmek, doğru altyapıyı kurmak ve production’a taşımak ayrı bir hikaye.
Ortam Hazırlığı
Önce sistemi hazırlayalım. Ben bu setup’ı Ubuntu 22.04 LTS üzerinde test ettim. Python versiyonu kritik: 3.9 ile 3.11 arası en stabil sonuçları veriyor.
# Sistem bağımlılıklarını kur
sudo apt update && sudo apt install -y python3-pip python3-venv git
# Proje dizini oluştur
mkdir -p ~/nlp-summarizer && cd ~/nlp-summarizer
# Virtual environment oluştur
python3 -m venv venv
source venv/bin/activate
# Temel kütüphaneleri yükle
pip install --upgrade pip
pip install transformers torch sentencepiece
pip install accelerate datasets huggingface_hub
GPU varsa CUDA uyumlu PyTorch kurmak performansı dramatik biçimde artırıyor. CPU üzerinde 500 kelimelik bir metni özetlemek 15-20 saniye sürebilirken, GPU ile bu süre 1-2 saniyeye iniyor.
# GPU varsa CUDA destekli PyTorch (CUDA 11.8 için)
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118
# GPU tespiti test et
python3 -c "import torch; print('CUDA:', torch.cuda.is_available()); print('Device:', torch.cuda.get_device_name(0) if torch.cuda.is_available() else 'CPU')"
Model Seçimi ve İndirme
Hugging Face Hub’da Türkçe özetleme için birkaç ciddi seçenek var. turkish-summarization, mT5 tabanlı modeller ve mBART ailesinden fine-tune edilmiş versiyonlar bunların başında geliyor. Ben genellikle ozcangundes/mt5-small-turkish-summarization ile başlıyorum; lightweight, production’da makul performans veriyor. Daha güçlü sonuçlar için csebuetnlp/mT5_multilingual_XLSum modelini Türkçe örneklerle kullanmak daha iyi sonuç üretiyor.
# Hugging Face CLI ile modeli önce kontrol et
pip install huggingface_hub
huggingface-cli login # Token gerektiren modeller için
# Modeli lokal cache'e indir
python3 -c "
from huggingface_hub import snapshot_download
snapshot_download(
repo_id='csebuetnlp/mT5_multilingual_XLSum',
local_dir='./models/mt5-xlsum',
ignore_patterns=['*.msgpack', '*.h5']
)
print('Model indirildi.')
"
Model boyutları konusunda gerçekçi olun: mt5-small yaklaşık 300MB, mt5-base 580MB, mt5-large ise 1.2GB civarı. Production’da memory ayak izini hesaplamadan model seçimi yapmayın.
İlk Özetleme Deneyi
Model indikten sonra basit bir test yapalım. Bu kodu doğrudan çalıştırabilirsiniz:
# summarize_test.py
from transformers import AutoTokenizer, AutoModelForSeq2SeqLM
import torch
# Model ve tokenizer yükle
model_name = "csebuetnlp/mT5_multilingual_XLSum"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForSeq2SeqLM.from_pretrained(model_name)
# Device ayarla
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = model.to(device)
model.eval()
# Test metni
metin = """
Türkiye Büyük Millet Meclisi, 2024 yılı bütçe kanununu kabul etti.
Bütçe görüşmeleri üç gün boyunca sürdü ve muhalefet partileri çeşitli
önergeler verdi. Kabul edilen bütçeye göre eğitim ve sağlık sektörlerine
yapılacak yatırımlar geçen yıla oranla yüzde on beş artırıldı.
Savunma harcamaları ise toplam bütçenin yüzde on ikisini oluşturuyor.
Ekonomistler, bütçenin enflasyonla mücadele hedefleriyle uyumlu olduğunu
belirtirken, bazı analistler sosyal harcamaların yetersiz kaldığı görüşünde.
"""
# WHITESPACE_HANDLER: modelin beklediği format
WHITESPACE_HANDLER = lambda k: ' '.join(k.strip().split())
input_ids = tokenizer(
[WHITESPACE_HANDLER(metin)],
return_tensors="pt",
padding="max_length",
truncation=True,
max_length=512
)["input_ids"].to(device)
# Özetleme
with torch.no_grad():
output_ids = model.generate(
input_ids=input_ids,
max_length=84,
no_repeat_ngram_size=2,
num_beams=4,
early_stopping=True
)
ozet = tokenizer.decode(output_ids[0], skip_special_tokens=True)
print("ÖZET:", ozet)
python3 summarize_test.py
Bu çalışırsa temeller sağlam. Şimdi production’a uygun bir yapıya geçelim.
Batch İşleme ve Performans Optimizasyonu
Gerçek dünyada tek tek metin özetlemiyor, yüzlerce belgeyi işliyorsunuz. Batch processing olmadan bu iş ölçeklenmiyor.
# batch_summarizer.py
from transformers import AutoTokenizer, AutoModelForSeq2SeqLM
import torch
from typing import List
import time
class TurkishSummarizer:
def __init__(self, model_name: str = "csebuetnlp/mT5_multilingual_XLSum",
batch_size: int = 8, max_input_length: int = 512,
max_output_length: int = 128):
self.tokenizer = AutoTokenizer.from_pretrained(model_name)
self.model = AutoModelForSeq2SeqLM.from_pretrained(
model_name,
torch_dtype=torch.float16 if torch.cuda.is_available() else torch.float32
)
self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
self.model = self.model.to(self.device)
self.model.eval()
self.batch_size = batch_size
self.max_input_length = max_input_length
self.max_output_length = max_output_length
self.whitespace_handler = lambda k: ' '.join(k.strip().split())
def summarize_batch(self, texts: List[str]) -> List[str]:
results = []
for i in range(0, len(texts), self.batch_size):
batch = texts[i:i + self.batch_size]
cleaned = [self.whitespace_handler(t) for t in batch]
inputs = self.tokenizer(
cleaned,
return_tensors="pt",
padding=True,
truncation=True,
max_length=self.max_input_length
).to(self.device)
with torch.no_grad():
output_ids = self.model.generate(
**inputs,
max_length=self.max_output_length,
num_beams=4,
no_repeat_ngram_size=2,
early_stopping=True
)
batch_results = self.tokenizer.batch_decode(
output_ids,
skip_special_tokens=True
)
results.extend(batch_results)
return results
# Kullanım örneği
if __name__ == "__main__":
summarizer = TurkishSummarizer(batch_size=4)
metinler = [
"Ankara'da düzenlenen teknoloji zirvesinde yapay zeka uygulamaları ele alındı...",
"İstanbul Büyükşehir Belediyesi yeni metro hatlarının yapımına başladığını açıkladı...",
"Türk lirası dolar karşısında değer kazanırken merkez bankası faiz kararını açıkladı..."
]
baslangic = time.time()
ozetler = summarizer.summarize_batch(metinler)
bitis = time.time()
for i, (metin, ozet) in enumerate(zip(metinler, ozetler)):
print(f"n[{i+1}] GİRDİ: {metin[:80]}...")
print(f" ÖZET: {ozet}")
print(f"nToplam süre: {bitis - baslangic:.2f} saniye")
REST API Olarak Servis Etme
Özetleme modelini bir servise dönüştürmek istiyorsanız FastAPI ideal seçim. Production’da bu servisi systemd ile yönetiyorum.
# app.py
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from typing import List, Optional
import uvicorn
from batch_summarizer import TurkishSummarizer
app = FastAPI(title="Türkçe Metin Özetleme API")
# Model singleton olarak yükle
summarizer = None
@app.on_event("startup")
async def load_model():
global summarizer
summarizer = TurkishSummarizer(batch_size=8)
class SummarizeRequest(BaseModel):
texts: List[str]
max_length: Optional[int] = 128
num_beams: Optional[int] = 4
class SummarizeResponse(BaseModel):
summaries: List[str]
processing_time: float
@app.post("/summarize", response_model=SummarizeResponse)
async def summarize(request: SummarizeRequest):
if not request.texts:
raise HTTPException(status_code=400, detail="Metin listesi boş olamaz")
if len(request.texts) > 50:
raise HTTPException(status_code=400, detail="Tek istekte maksimum 50 metin")
import time
start = time.time()
summaries = summarizer.summarize_batch(request.texts)
elapsed = time.time() - start
return SummarizeResponse(summaries=summaries, processing_time=elapsed)
@app.get("/health")
async def health():
return {"status": "ok", "model_loaded": summarizer is not None}
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8080, workers=1)
# Bağımlılıkları ekle ve servisi başlat
pip install fastapi uvicorn pydantic
# Test et
python3 app.py &
curl -X POST http://localhost:8080/summarize
-H "Content-Type: application/json"
-d '{
"texts": [
"Türkiye ekonomisi 2024 yılının ilk çeyreğinde yüzde beş büyüdü. Bu büyüme rakamı analistlerin beklentilerinin üzerinde geldi. Büyümeye en büyük katkıyı hizmet sektörü ve ihracat sağladı. Merkez Bankası açıklamasında enflasyonla mücadelenin kararlılıkla sürdürüleceğini vurguladı."
]
}'
Systemd ile Production Deployment
API’yi ayağa kaldırdınız, ama sunucu restart atınca ne olacak? Systemd servis dosyası şart.
# /etc/systemd/system/summarizer-api.service
sudo tee /etc/systemd/system/summarizer-api.service > /dev/null <<EOF
[Unit]
Description=Türkçe Metin Özetleme API
After=network.target
[Service]
Type=simple
User=www-data
WorkingDirectory=/home/ubuntu/nlp-summarizer
Environment=PATH=/home/ubuntu/nlp-summarizer/venv/bin
Environment=HF_HOME=/home/ubuntu/.cache/huggingface
ExecStart=/home/ubuntu/nlp-summarizer/venv/bin/python app.py
Restart=on-failure
RestartSec=10
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable summarizer-api
sudo systemctl start summarizer-api
sudo systemctl status summarizer-api
Servis başlamak için 30-60 saniye beklemek gerekebilir; model yüklenene kadar health check endpoint’i 503 dönecek. Bunu bilen birisi olarak load balancer’a en az 90 saniye startup grace period tanımanızı öneririm.
Model Cache Yönetimi
Hugging Face modelleri varsayılan olarak ~/.cache/huggingface altına indiriliyor. Disk dolunca production’da panik yaşamamak için bu dizini takip edin.
# Cache boyutunu kontrol et
du -sh ~/.cache/huggingface/hub/
# Hangi modeller var
ls -la ~/.cache/huggingface/hub/
# Belirli bir modelin tüm versiyonlarını temizle (dikkatli kullan!)
huggingface-cli delete-cache
# Alternatif: manuel temizlik
find ~/.cache/huggingface/hub -name "*.bin" -size +500M -exec ls -lh {} ;
Production sunucularda cache dizinini /opt/hf-cache gibi özel bir partition’a almanızı öneririm. Özellikle birden fazla model kullanan ortamlarda disk yönetimi kritik hale geliyor.
# Cache dizinini değiştir
export HF_HOME=/opt/hf-cache
export TRANSFORMERS_CACHE=/opt/hf-cache/transformers
# .bashrc veya systemd servis dosyasına ekle
echo 'export HF_HOME=/opt/hf-cache' >> ~/.bashrc
Hata Yönetimi ve Loglama
Produksiyonda “model bazen çöp üretiyor” şikayetleriyle karşılaşırsınız. Bunun önüne geçmek için girdi validasyonu ve çıktı kalite kontrolü ekleyin.
# quality_filter.py
import re
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
def validate_input(text: str, min_words: int = 30, max_chars: int = 4000) -> bool:
"""Girdi metnini özetlemeye uygun olup olmadığını kontrol et."""
word_count = len(text.split())
if word_count < min_words:
logger.warning(f"Metin çok kısa: {word_count} kelime (minimum: {min_words})")
return False
if len(text) > max_chars:
logger.warning(f"Metin çok uzun: {len(text)} karakter, kesiliyor.")
return False
return True
def validate_output(summary: str, original_text: str) -> dict:
"""Üretilen özetin kalitesini değerlendir."""
issues = []
# Özet, orijinal metinden uzun olmamalı
if len(summary) > len(original_text) * 0.8:
issues.append("ozet_cok_uzun")
# Minimum uzunluk kontrolü
if len(summary.split()) < 5:
issues.append("ozet_cok_kisa")
# Tekrar eden cümle kalıpları
sentences = summary.split('.')
unique_sentences = set(s.strip() for s in sentences if s.strip())
if len(unique_sentences) < len(sentences) * 0.7:
issues.append("tekrar_var")
return {
"valid": len(issues) == 0,
"issues": issues,
"word_count": len(summary.split()),
"compression_ratio": len(summary) / len(original_text)
}
# Kullanım
test_ozet = "Türkiye ekonomisi büyüdü. Analistler memnun."
test_metin = "Türkiye ekonomisi 2024 yılının ilk çeyreğinde yüzde beş büyüdü..."
sonuc = validate_output(test_ozet, test_metin)
print(sonuc)
Gerçek Dünya Senaryosu: Haber Özetleme Pipeline’ı
Şimdi tüm parçaları birleştirelim. Bir haber sitesinin RSS feed’inden içerik çekip otomatik özetleyen bir pipeline:
# news_pipeline.py
import feedparser
import requests
from bs4 import BeautifulSoup
from batch_summarizer import TurkishSummarizer
from quality_filter import validate_input, validate_output
import json
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
def fetch_article_text(url: str) -> str:
"""Haber URL'sinden metin çek."""
try:
resp = requests.get(url, timeout=10,
headers={'User-Agent': 'Mozilla/5.0'})
soup = BeautifulSoup(resp.content, 'html.parser')
# Yaygın haber sitesi yapısı
paragraphs = soup.find_all('p')
return ' '.join(p.get_text() for p in paragraphs if len(p.get_text()) > 50)
except Exception as e:
logger.error(f"URL çekilemedi {url}: {e}")
return ""
def run_pipeline(rss_url: str, max_articles: int = 10):
summarizer = TurkishSummarizer()
feed = feedparser.parse(rss_url)
results = []
texts_to_summarize = []
metadata = []
for entry in feed.entries[:max_articles]:
text = fetch_article_text(entry.link)
if validate_input(text):
texts_to_summarize.append(text[:3000])
metadata.append({
"title": entry.title,
"link": entry.link,
"published": entry.get("published", "")
})
if not texts_to_summarize:
logger.warning("Özetlenecek geçerli metin bulunamadı.")
return []
logger.info(f"{len(texts_to_summarize)} makale özetleniyor...")
summaries = summarizer.summarize_batch(texts_to_summarize)
for meta, text, summary in zip(metadata, texts_to_summarize, summaries):
quality = validate_output(summary, text)
results.append({
**meta,
"summary": summary,
"quality": quality
})
logger.info(f"İşlendi: {meta['title'][:60]}... Kalite: {quality['valid']}")
# Sonuçları kaydet
with open("summaries_output.json", "w", encoding="utf-8") as f:
json.dump(results, f, ensure_ascii=False, indent=2)
return results
if __name__ == "__main__":
# Örnek RSS (gerçek URL ile değiştirin)
results = run_pipeline("https://www.example.com.tr/rss", max_articles=5)
print(f"Toplam {len(results)} haber özetlendi.")
pip install feedparser beautifulsoup4 requests
python3 news_pipeline.py
Performans İzleme
Production’da modelin ne kadar sürede ne kadar iş yaptığını takip etmek gerekiyor. Basit bir metrics wrapper yeterli:
# Prometheus metrics eklemek için
pip install prometheus-client
# Hızlı benchmark testi
python3 -c "
from batch_summarizer import TurkishSummarizer
import time
s = TurkishSummarizer()
test_texts = ['Bu bir test metnidir. ' * 50] * 10
start = time.time()
results = s.summarize_batch(test_texts)
elapsed = time.time() - start
print(f'10 metin / {elapsed:.2f}s = {10/elapsed:.1f} metin/saniye')
print(f'Ortalama: {elapsed/10*1000:.0f}ms/metin')
"
Tipik sonuçlar: CPU’da 800-1200ms/metin, orta seviye GPU’da 80-150ms/metin. Bu rakamları SLA hesaplamalarında kullanın.
Sonuç
Hugging Face ile Türkçe metin özetleme, doğru araçlarla birkaç saatte production’a alınabilecek bir iş. Ama “çalışıyor” ile “production’da güvenilir çalışıyor” arasındaki mesafe asıl hikaye.
Dikkat etmeniz gereken birkaç kritik nokta:
- Model seçimi:
mT5ailesi Türkçe için şu an en stabil sonuçları veriyor. Küçük modelden başlayıp ihtiyaca göre büyütün. - Batch size ayarı: GPU memory’ye göre optimize edin, OOM hatası almak istemezsiniz.
- Girdi temizliği: Gürültülü metin, gürültülü özet üretir. Whitespace handling ihmal edilemez.
- Cache yönetimi: Model dosyaları büyük, disk dolunca servis çöker. Bunu öğrendim zor yoldan.
- Kalite kontrolü: Her özetin makul olup olmadığını programatik olarak doğrulayın, downstream süreçler buna güveniyor.
Altyapı tarafında dikkat edilmesi gereken en önemli şey şu: bu modeller CPU’da makul performans veriyor ancak yüksek trafikte mutlaka GPU gerekiyor. Küçük bir T4 GPU bile işi on kat hızlandırıyor. Cloud maliyetini bütçeye katmayı unutmayın.
Türkçe NLP ekosistemi hızla büyüyor. Birkaç ay önce bu yazıyı yazıyor olsaydım model seçenekleri çok daha kısıtlıydı. Hub’ı düzenli takip edin, yeni Türkçe modeller çıkmaya devam ediyor.
