Metin Sınıflandırma: Hugging Face Pipeline Kullanımı

Makine öğrenmesi modellerini production’a almak eskiden ciddi bir iş yükü gerektirirdi. Model mimarisini anlamak, weight’leri yüklemek, tokenization yapmak, inference pipeline’ı kurmak… Bunların hepsi ayrı ayrı ele alınması gereken konulardı. Hugging Face’in pipeline API’si tam da bu karmaşıklığı ortadan kaldırmak için tasarlandı. Bugün metin sınıflandırma görevlerine odaklanarak bu API’yi gerçek dünya senaryolarıyla birlikte inceleyeceğiz.

Neden Pipeline API?

Bir sysadmin olarak modellerle çalışırken asıl amacınız genellikle şudur: log dosyalarını kategorize etmek, müşteri taleplerini önceliklendirmek, spam içerikleri filtrelemek ya da destek biletlerini otomatik yönlendirmek. Bunlar için derin öğrenme araştırmacısı olmak zorunda değilsiniz.

Hugging Face pipeline API’si şu avantajları sunar:

  • Tek satır model yükleme: Model mimarisini bilmenize gerek yok
  • Otomatik tokenization: Ham metni kendiniz işlemek zorunda kalmıyorsunuz
  • GPU/CPU otomatik seçimi: Donanımınıza göre kendisi ayarlıyor
  • Batch processing desteği: Büyük veri setlerini verimli işleyebiliyorsunuz
  • Yüzlerce hazır model: Hugging Face Hub’dan direkt kullanabiliyorsunuz

Ortam Kurulumu

Öncelikle gerekli paketleri kuralım. Python ortamınızı izole tutmak için her zaman virtual environment kullanmanızı öneririm.

# Python virtual environment oluştur
python3 -m venv hf-classification-env
source hf-classification-env/bin/activate  # Linux/macOS
# Windows için: hf-classification-envScriptsactivate

# Gerekli paketleri kur
pip install transformers torch torchvision torchaudio
pip install accelerate  # GPU optimizasyonu için
pip install datasets    # Veri seti işlemleri için

# Kurulumu doğrula
python -c "import transformers; print(transformers.__version__)"

GPU’nuz varsa CUDA destekli PyTorch kurmanız performansı dramatik şekilde artırır. CUDA versiyonunuzu kontrol ederek uygun sürümü seçin:

# CUDA versiyonunu kontrol et
nvidia-smi | grep "CUDA Version"

# CUDA 11.8 için örnek
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118

# Kurulumdan sonra GPU kullanılabilirliğini test et
python -c "import torch; print('GPU mevcut:', torch.cuda.is_available())"

İlk Pipeline Örneği

En basit kullanım şekliyle başlayalım. Pipeline, model belirtmediğinizde görev için varsayılan modeli kullanır.

from transformers import pipeline

# Varsayılan sentiment analysis modeli
classifier = pipeline("text-classification")

# Tek metin sınıflandırma
result = classifier("Bu ürün gerçekten harika, çok memnun kaldım!")
print(result)
# Çıktı: [{'label': 'POSITIVE', 'score': 0.9998}]

# Birden fazla metin
texts = [
    "Sunucu 3 saattir cevap vermiyor, acil müdahale gerekiyor",
    "Sistem güncellemesi başarıyla tamamlandı",
    "Disk kullanımı %95 seviyesine ulaştı, dikkat!"
]

results = classifier(texts)
for text, result in zip(texts, results):
    print(f"Metin: {text[:50]}...")
    print(f"Etiket: {result['label']}, Skor: {result['score']:.4f}n")

Gördüğünüz gibi sadece birkaç satırla çalışan bir sınıflandırıcı elde ettiniz. Şimdi daha gerçekçi senaryolara geçelim.

Belirli Model Seçimi

Varsayılan model her zaman ihtiyacınıza uymayabilir. Hugging Face Hub’da binlerce fine-tune edilmiş model bulunuyor. Model seçimi kritik bir karar çünkü:

  • Dil uyumu: Türkçe metinler için Türkçe model kullanmak performansı artırır
  • Domain uyumu: Tıp metinleri için tıp alanında eğitilmiş model daha iyi çalışır
  • Model boyutu: Küçük modeller hızlı, büyük modeller daha doğru
from transformers import pipeline
import torch

# Türkçe duygu analizi için özel model
# savasy/bert-base-turkish-sentiment-cased modeli Türkçe için eğitilmiş
turkish_classifier = pipeline(
    "text-classification",
    model="savasy/bert-base-turkish-sentiment-cased",
    device=0 if torch.cuda.is_available() else -1  # GPU varsa kullan
)

turkish_texts = [
    "Sistem yönetimi konusunda çok başarılı bir ekip",
    "Ağ bağlantısı sürekli kesiliyor, berbat bir deneyim",
    "Yeni firewall kuralları gayet iyi çalışıyor"
]

for text in turkish_texts:
    result = turkish_classifier(text)
    print(f"'{text}'")
    print(f"Sonuç: {result[0]['label']} ({result[0]['score']:.3f})")
    print()

Çok Kategorili Sınıflandırma

IT ortamlarında genellikle ikili (pozitif/negatif) sınıflandırma yetmez. Log mesajlarını severity’ye göre, destek taleplerini kategoriye göre ayırmak istersiniz. Çok etiketli sınıflandırma için zero-shot-classification pipeline’ı son derece güçlü bir araçtır.

from transformers import pipeline

# Zero-shot classification - eğitim gerekmeden özel kategoriler
zs_classifier = pipeline(
    "zero-shot-classification",
    model="facebook/bart-large-mnli"
)

# IT destek talepleri için kategorizasyon
support_tickets = [
    "Outlook açılmıyor, mail gönderip alamıyorum",
    "VPN bağlantısı evden çalışırken kopuyor",
    "Yazıcı kağıt sıkışması yapıyor ve temizlenemiyor",
    "Yeni laptop kurulumu için admin yetkisi lazım",
    "Şifremi unuttum, sisteme giremiyorum"
]

categories = [
    "email ve iletişim sorunları",
    "ağ ve bağlantı sorunları",
    "donanım sorunları",
    "yazılım kurulum ve yetki",
    "hesap ve şifre sorunları"
]

for ticket in support_tickets:
    result = zs_classifier(ticket, candidate_labels=categories)
    top_category = result['labels'][0]
    top_score = result['scores'][0]
    print(f"Talep: {ticket}")
    print(f"Kategori: {top_category} (güven: {top_score:.3f})")
    print("-" * 60)

Bu yaklaşımın güzelliği şu: model eğitimine ihtiyaç duymadan kendi kategorilerinizi tanımlayabiliyorsunuz. Yeni bir kategori eklemek istediğinizde sadece listeye eklemeniz yeterli.

Log Dosyası Analizi Senaryosu

Gerçek bir sysadmin senaryosu düşünelim: Binlerce satır log dosyanız var ve kritik hataları önceliklendirmeniz gerekiyor. Manuel inceleme saatlerce sürebilir. Pipeline ile bunu otomatize edelim.

from transformers import pipeline
import json
from datetime import datetime

def analyze_logs(log_file_path, output_file=None):
    """
    Log dosyasını analiz eder ve kritik satırları önceliklendirir
    """
    # Severity sınıflandırıcı
    severity_classifier = pipeline(
        "zero-shot-classification",
        model="facebook/bart-large-mnli",
        device=0 if __import__('torch').cuda.is_available() else -1
    )

    severity_levels = [
        "kritik hata ve sistem çökmesi",
        "uyarı ve performans sorunu",
        "bilgi mesajı ve başarılı işlem"
    ]

    analyzed_logs = []

    try:
        with open(log_file_path, 'r', encoding='utf-8') as f:
            log_lines = f.readlines()
    except FileNotFoundError:
        print(f"Hata: {log_file_path} bulunamadı")
        return []

    print(f"Toplam {len(log_lines)} log satırı analiz ediliyor...")

    # Batch işleme için grupla (bellek yönetimi)
    batch_size = 16
    for i in range(0, len(log_lines), batch_size):
        batch = [line.strip() for line in log_lines[i:i+batch_size] if line.strip()]

        if not batch:
            continue

        results = severity_classifier(batch, candidate_labels=severity_levels)

        # Tek satır gelirse list'e çevir
        if isinstance(results, dict):
            results = [results]

        for log_line, result in zip(batch, results):
            analyzed_logs.append({
                "log": log_line,
                "severity": result['labels'][0],
                "confidence": round(result['scores'][0], 4),
                "timestamp": datetime.now().isoformat()
            })

        print(f"İlerleme: {min(i+batch_size, len(log_lines))}/{len(log_lines)}")

    # Kritik logları önce göster
    critical_logs = [
        log for log in analyzed_logs
        if "kritik" in log['severity']
    ]

    print(f"nKritik log sayısı: {len(critical_logs)}")
    print(f"Toplam analiz: {len(analyzed_logs)}")

    if output_file:
        with open(output_file, 'w', encoding='utf-8') as f:
            json.dump(analyzed_logs, f, ensure_ascii=False, indent=2)
        print(f"Sonuçlar {output_file} dosyasına kaydedildi")

    return critical_logs

# Örnek kullanım
# critical = analyze_logs("/var/log/application.log", "analysis_result.json")

Batch Processing ve Performans Optimizasyonu

Büyük veri setlerinde çalışırken batch size ayarı kritik öneme sahip. Çok küçük batch boyutu GPU’yu verimli kullanamaz, çok büyük batch boyutu ise out of memory hatasına yol açar.

from transformers import pipeline
import time

def benchmark_batch_sizes(texts, model_name="distilbert-base-uncased-finetuned-sst-2-english"):
    """
    Farklı batch boyutlarının performansını karşılaştırır
    """
    classifier = pipeline(
        "text-classification",
        model=model_name,
        truncation=True,
        max_length=512
    )

    batch_sizes = [1, 8, 16, 32, 64]

    for batch_size in batch_sizes:
        start_time = time.time()

        results = classifier(
            texts,
            batch_size=batch_size,
            truncation=True
        )

        elapsed = time.time() - start_time
        throughput = len(texts) / elapsed

        print(f"Batch size: {batch_size:3d} | "
              f"Süre: {elapsed:.2f}s | "
              f"Throughput: {throughput:.1f} metin/sn")

# Test verisi oluştur
sample_texts = [
    "Sunucu başarıyla yeniden başlatıldı",
    "Kritik güvenlik açığı tespit edildi",
    "Yedekleme işlemi tamamlandı",
    "Disk alanı yetersiz",
    "Servis başlatılamadı"
] * 100  # 500 metin

# benchmark_batch_sizes(sample_texts)

# Optimal batch size ile production kullanımı
def classify_large_dataset(texts, batch_size=32, model_name=None):
    """
    Büyük veri setlerini verimli şekilde sınıflandırır
    """
    classifier = pipeline(
        "text-classification",
        model=model_name or "distilbert-base-uncased-finetuned-sst-2-english",
        truncation=True,
        max_length=512,
        padding=True
    )

    all_results = []
    total = len(texts)

    for i in range(0, total, batch_size):
        batch = texts[i:i+batch_size]
        results = classifier(batch, batch_size=batch_size)
        all_results.extend(results)

        if (i // batch_size) % 10 == 0:
            progress = min(i + batch_size, total)
            print(f"İşlendi: {progress}/{total} ({progress/total*100:.1f}%)")

    return all_results

Model Cache Yönetimi

Production ortamında model dosyalarının nerede saklandığını ve nasıl yönetileceğini bilmek önemli. Hugging Face modelleri default olarak ~/.cache/huggingface dizinine indirilir.

# Cache dizinini kontrol et
du -sh ~/.cache/huggingface/hub/
ls -la ~/.cache/huggingface/hub/

# Model önbelleklerini listele
python -c "
from huggingface_hub import scan_cache_dir
cache_info = scan_cache_dir()
print(f'Toplam boyut: {cache_info.size_on_disk_str}')
for repo in cache_info.repos:
    print(f'Model: {repo.repo_id} | Boyut: {repo.size_on_disk_str}')
"

# Özel cache dizini belirleme (disk yönetimi için)
export TRANSFORMERS_CACHE=/data/ml-models/cache
export HF_HOME=/data/ml-models/hf-home

# Bu değişkenleri kalıcı yapmak için
echo 'export TRANSFORMERS_CACHE=/data/ml-models/cache' >> ~/.bashrc
echo 'export HF_HOME=/data/ml-models/hf-home' >> ~/.bashrc
source ~/.bashrc

# Eski model versiyonlarını temizle
python -c "
from huggingface_hub import scan_cache_dir
cache_info = scan_cache_dir()
# Silinebilir revision'ları bul
delete_strategy = cache_info.delete_revisions()
print(f'Silinebilir alan: {delete_strategy.expected_freed_size_str}')
"

Offline Kullanım ve Air-Gapped Ortamlar

Güvenlik gerektiren ortamlarda internet erişimi olmayabilir. Modelleri önceden indirip offline kullanabilirsiniz.

from transformers import pipeline, AutoModelForSequenceClassification, AutoTokenizer
import os

def download_model_for_offline(model_name, save_directory):
    """
    Modeli local dizine indirir
    """
    print(f"Model indiriliyor: {model_name}")

    tokenizer = AutoTokenizer.from_pretrained(model_name)
    model = AutoModelForSequenceClassification.from_pretrained(model_name)

    # Local dizine kaydet
    tokenizer.save_pretrained(save_directory)
    model.save_pretrained(save_directory)

    print(f"Model kaydedildi: {save_directory}")
    print(f"Dizin boyutu: {get_dir_size(save_directory)}")

def get_dir_size(path):
    """Dizin boyutunu hesaplar"""
    total = 0
    for entry in os.scandir(path):
        if entry.is_file():
            total += entry.stat().st_size
        elif entry.is_dir():
            total += get_dir_size(entry.path)
    size_mb = total / (1024 * 1024)
    return f"{size_mb:.1f} MB"

def load_offline_classifier(local_model_path):
    """
    Local kaydedilmiş modeli yükler (internet gerektirmez)
    """
    classifier = pipeline(
        "text-classification",
        model=local_model_path,
        tokenizer=local_model_path,
        local_files_only=True  # Internet bağlantısı denemez
    )
    return classifier

# Önce internete erişimi olan makinede:
# download_model_for_offline(
#     "distilbert-base-uncased-finetuned-sst-2-english",
#     "/data/ml-models/sentiment-model"
# )

# Air-gapped ortamda:
# classifier = load_offline_classifier("/data/ml-models/sentiment-model")
# result = classifier("Sistem kararlı çalışıyor")

Sonuç Güven Eşiği ile Filtreleme

Gerçek uygulamalarda düşük güvenle yapılan tahminleri ayrıştırmak önemlidir. Bir modelin yüzde elli güvenle verdiği tahmin, neredeyse rastgele seçimden farksızdır.

from transformers import pipeline
from typing import List, Dict, Tuple

def classify_with_confidence_filter(
    texts: List[str],
    model_name: str = "distilbert-base-uncased-finetuned-sst-2-english",
    min_confidence: float = 0.85
) -> Tuple[List[Dict], List[Dict]]:
    """
    Güven eşiğinin altındaki tahminleri ayırır.
    Returns: (güvenilir_tahminler, belirsiz_tahminler)
    """
    classifier = pipeline(
        "text-classification",
        model=model_name,
        return_all_scores=True
    )

    results = classifier(texts)
    confident_predictions = []
    uncertain_predictions = []

    for text, result in zip(texts, results):
        # En yüksek skoru bul
        best_prediction = max(result, key=lambda x: x['score'])

        prediction_data = {
            "text": text,
            "label": best_prediction['label'],
            "confidence": best_prediction['score'],
            "all_scores": result
        }

        if best_prediction['score'] >= min_confidence:
            confident_predictions.append(prediction_data)
        else:
            uncertain_predictions.append(prediction_data)

    print(f"Güvenilir tahmin: {len(confident_predictions)}")
    print(f"Manuel inceleme gerekli: {len(uncertain_predictions)}")

    # Belirsiz olanları raporla
    if uncertain_predictions:
        print("nManuel inceleme gerektiren metinler:")
        for item in uncertain_predictions:
            print(f"  - '{item['text'][:60]}...'")
            print(f"    En iyi tahmin: {item['label']} ({item['confidence']:.3f})")

    return confident_predictions, uncertain_predictions

# Örnek kullanım
test_texts = [
    "Sistem çöktü, veriler kaybedildi",           # Net negatif
    "Performans biraz düştü ama normal aralıkta",  # Belirsiz
    "Tüm servisler sorunsuz çalışıyor",            # Net pozitif
    "Belki sorun olabilir, kontrol etmek lazım"    # Belirsiz
]

confident, uncertain = classify_with_confidence_filter(
    test_texts,
    min_confidence=0.90
)

Production’da İzleme ve Logging

Pipeline’ı bir servise entegre ederken inference sürelerini, başarı oranlarını ve model davranışını izlemek kritik. Basit bir wrapper ile bu metrikleri toplayabilirsiniz.

import time
import logging
from functools import wraps
from transformers import pipeline
from collections import defaultdict

logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler('/var/log/ml-classifier.log'),
        logging.StreamHandler()
    ]
)

class MonitoredClassifier:
    """
    Metrikleri izleyen production-ready classifier wrapper
    """
    def __init__(self, task, model_name, **kwargs):
        self.model_name = model_name
        self.metrics = defaultdict(int)
        self.latencies = []

        logging.info(f"Model yükleniyor: {model_name}")
        load_start = time.time()

        self.classifier = pipeline(task, model=model_name, **kwargs)

        load_time = time.time() - load_start
        logging.info(f"Model yüklendi: {load_time:.2f} saniye")

    def classify(self, texts, **kwargs):
        start_time = time.time()

        try:
            if isinstance(texts, str):
                texts = [texts]

            results = self.classifier(texts, **kwargs)
            latency = time.time() - start_time

            self.latencies.append(latency)
            self.metrics['total_requests'] += len(texts)
            self.metrics['successful_requests'] += len(texts)

            logging.info(
                f"Sınıflandırma tamamlandı | "
                f"Adet: {len(texts)} | "
                f"Süre: {latency:.3f}s | "
                f"Ort. latency: {latency/len(texts)*1000:.1f}ms/metin"
            )

            return results

        except Exception as e:
            self.metrics['failed_requests'] += 1
            logging.error(f"Sınıflandırma hatası: {str(e)}")
            raise

    def get_stats(self):
        if not self.latencies:
            return {}

        return {
            "model": self.model_name,
            "toplam_istek": self.metrics['total_requests'],
            "basarili_istek": self.metrics['successful_requests'],
            "basarisiz_istek": self.metrics['failed_requests'],
            "ort_latency_ms": sum(self.latencies)/len(self.latencies)*1000,
            "max_latency_ms": max(self.latencies)*1000,
            "min_latency_ms": min(self.latencies)*1000
        }

# Kullanım
classifier = MonitoredClassifier(
    "text-classification",
    "distilbert-base-uncased-finetuned-sst-2-english",
    truncation=True
)

sample_results = classifier.classify([
    "Database bağlantısı koptu",
    "Backup başarıyla alındı",
    "Disk dolmak üzere"
])

stats = classifier.get_stats()
print("nPerformans İstatistikleri:")
for key, value in stats.items():
    if isinstance(value, float):
        print(f"  {key}: {value:.2f}")
    else:
        print(f"  {key}: {value}")

Dikkat Edilmesi Gereken Konular

Hugging Face pipeline’ı kullanırken karşılaşabileceğiniz yaygın sorunlar ve çözümleri:

  • Model boyutu ve bellek: Büyük modeller (BERT-large, RoBERTa-large) 1-2 GB RAM kullanır. Sınırlı kaynaklarda DistilBERT gibi küçük modeller tercih edin.
  • Tokenizer max length: Uzun metinler varsayılan 512 token limitini aşabilir. truncation=True parametresi bu durumda sessizce kesiyor, önemli bilgi kaybedebilirsiniz.
  • Thread safety: Pipeline objesi thread-safe değil. Multi-threaded uygulamalarda her thread için ayrı instance oluşturun ya da queue kullanın.
  • Model güncellemeleri: Hub’daki modeller güncelleniyor. Reproducibility için model versiyonunu revision parametresiyle sabitleyin.
  • Dil uyumsuzluğu: İngilizce eğitilmiş model Türkçe metinde çalışır ama düşük doğrulukla. Dile uygun model seçimi en önemli adım.

Sonuç

Hugging Face pipeline API’si, makine öğrenmesi modellerini production ortamına almak için gerçekten güçlü ve kullanışlı bir soyutlama katmanı sunuyor. Metin sınıflandırma özelinde konuşursak, log analizi, destek talebi kategorizasyonu, içerik moderasyonu gibi sysadmin senaryolarında doğrudan değer üretebilirsiniz.

Başlangıç için zero-shot classification ile denemeler yapmanızı öneririm. Model eğitimine gerek kalmadan kendi kategorilerinizi tanımlayabilir ve hızlıca prototipler oluşturabilirsiniz. Eğer belirli bir görev için tutarlı olarak yüksek doğruluk gerekiyorsa, o zaman fine-tuning yoluna gidebilirsiniz; bu da ayrı bir yazı konusu.

Cache yönetimi, offline kullanım ve monitoring konularını başından kurarsnız ilerleyen süreçte çok daha az baş ağrısı yaşarsınız. Production sistemlerde “çalışıyor” demek yetmez, ne zaman nasıl çalıştığını da bilmeniz gerekir. Bu nedenle MonitoredClassifier gibi wrapper’lar küçük ek eforla büyük fayda sağlar.

Sorularınız ve kendi deneyimleriniz için yorumlar açık!

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir