Sıfırdan Model Eğitimi: Hugging Face Trainer API Kullanım Rehberi
Makine öğrenmesi dünyasında “model eğitimi” denince akla hemen karmaşık matematiğin, onlarca bağımlılığın ve saatlerce süren denemeler yanılmalar gelir. Hugging Face’in Trainer API’si bu süreci önemli ölçüde sadeleştiriyor, ama bu “kolaylaştı, artık anlamak gerekmiyor” anlamına gelmiyor. Aksine, ne yaptığını bilmeden Trainer’ı kullanmak sizi çok daha hızlı bir şekilde duvara çarptırır. Bu yazıda sıfırdan bir model eğitim sürecini, gerçek dünyada karşılaştığım sorunlar ve çözümleriyle birlikte ele alacağım.
Ortam Kurulumu: Temel Olmadan Devam Etme
Trainer API’ye geçmeden önce ortamın doğru kurulduğundan emin olmak şart. Bunu atladığınızda sonradan “neden GPU kullanılmıyor” veya “neden mixed precision çalışmıyor” diye saatlerce uğraşırsınız.
# Conda ortamı oluştur
conda create -n hf_trainer python=3.10 -y
conda activate hf_trainer
# Temel paketleri kur
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118
pip install transformers datasets evaluate accelerate
pip install scikit-learn tensorboard
# CUDA erişimini doğrula
python -c "import torch; print(f'CUDA: {torch.cuda.is_available()}, Cihaz: {torch.cuda.get_device_name(0)}')"
Burada dikkat edilmesi gereken nokta: PyTorch ve CUDA versiyonlarının uyumu. CUDA 11.8 için cu118, CUDA 12.1 için cu121 kullanmanız gerekiyor. Bunu yanlış kurduğunuzda Trainer sessiz sedasız CPU’ya düşer, siz de neden bu kadar yavaş diye hayrete düşersiniz.
Veri Seti Hazırlama: Trainer’ın Asıl Gücü Burada
Trainer API’nin en güzel taraflarından biri Hugging Face datasets kütüphanesiyle kusursuz entegrasyon. Ama kendi verinizi getiriyorsanız bazı kurallara uymak zorundasınız.
from datasets import Dataset, DatasetDict
import pandas as pd
# CSV'den kendi veri setinizi oluşturun
df = pd.read_csv("metin_siniflandirma.csv") # 'text' ve 'label' kolonları
# Dataset nesnesine dönüştür
dataset = Dataset.from_pandas(df)
# Train/validation split
split = dataset.train_test_split(test_size=0.15, seed=42)
dataset_dict = DatasetDict({
"train": split["train"],
"validation": split["test"]
})
print(dataset_dict)
# DatasetDict({
# train: Dataset({features: ['text', 'label'], num_rows: 8500})
# validation: Dataset({features: ['text', 'label'], num_rows: 1500})
# })
Gerçek projede karşılaştığım bir senaryo paylaşayım: Türkçe müşteri yorumları üzerinde duygu analizi yapıyorduk. Veri setinin yüzde 80’i olumlu, yüzde 20’si olumsuzdu. Bu dengesizliği fark etmeden eğitimi başlatırsanız model her şeyi “olumlu” tahmin edip yüzde 80 doğrulukla sizi yanıltır. Bunun çözümü için stratified split şart:
from sklearn.model_selection import train_test_split
# Stratified split için sklearn kullan
train_texts, val_texts, train_labels, val_labels = train_test_split(
df["text"].tolist(),
df["label"].tolist(),
test_size=0.15,
stratify=df["label"].tolist(),
random_state=42
)
train_dataset = Dataset.from_dict({"text": train_texts, "label": train_labels})
val_dataset = Dataset.from_dict({"text": val_texts, "label": val_labels})
Tokenizasyon: Doğru Yapılmazsa Her Şey Çöker
Tokenizasyon adımı çoğu zaman geçiştirilen ama kritik bir süreç. Özellikle max_length parametresi burada hayat kurtarır veya batırır.
from transformers import AutoTokenizer
model_checkpoint = "dbmdz/bert-base-turkish-cased"
tokenizer = AutoTokenizer.from_pretrained(model_checkpoint)
def tokenize_function(examples):
return tokenizer(
examples["text"],
padding="max_length",
truncation=True,
max_length=128,
return_tensors=None # Dataset için None bırak
)
# map() ile tüm veri setine uygula
tokenized_train = train_dataset.map(
tokenize_function,
batched=True,
batch_size=1000,
remove_columns=["text"]
)
tokenized_val = val_dataset.map(
tokenize_function,
batched=True,
batch_size=1000,
remove_columns=["text"]
)
# Hugging Face Trainer için format ayarla
tokenized_train.set_format("torch", columns=["input_ids", "attention_mask", "label"])
tokenized_val.set_format("torch", columns=["input_ids", "attention_mask", "label"])
max_length konusunda pratik bir not: Eğitim verinizin token dağılımını analiz etmeden bunu seçmeyin. 512 koysanız da verilerinizin yüzde 95’i 100 token altındaysa hem bellek israf edersiniz hem eğitim yavaşlar. Dağılımı analiz etmek için şunu çalıştırın:
# Token uzunluk dağılımını incele
lengths = [len(tokenizer.encode(text)) for text in df["text"].tolist()]
import numpy as np
print(f"Ortalama: {np.mean(lengths):.0f}")
print(f"95. yüzdelik: {np.percentile(lengths, 95):.0f}")
print(f"Maksimum: {max(lengths)}")
Model Yükleme ve Yapılandırma
from transformers import AutoModelForSequenceClassification
num_labels = len(df["label"].unique())
id2label = {0: "olumsuz", 1: "nötr", 2: "olumlu"}
label2id = {v: k for k, v in id2label.items()}
model = AutoModelForSequenceClassification.from_pretrained(
model_checkpoint,
num_labels=num_labels,
id2label=id2label,
label2id=label2id
)
# Parametre sayısını kontrol et
total_params = sum(p.numel() for p in model.parameters())
trainable_params = sum(p.numel() for p in model.parameters() if p.requires_grad)
print(f"Toplam parametre: {total_params:,}")
print(f"Eğitilebilir parametre: {trainable_params:,}")
Eğer frozen backbone ile sadece classification head’i eğitmek istiyorsanız, ki bu transfer learning’in klasik yaklaşımı:
# Backbone'u dondur, sadece sınıflandırma katmanını eğit
for name, param in model.bert.named_parameters():
param.requires_grad = False
# Doğrulama
trainable = sum(p.numel() for p in model.parameters() if p.requires_grad)
print(f"Eğitilebilir parametre (frozen backbone): {trainable:,}")
TrainingArguments: İşin Kalbi
TrainingArguments sınıfı onlarca parametre alıyor. Hepsini ezberlemenize gerek yok ama hangi parametrenin ne işe yaradığını anlamak kritik. Gereksiz parametreleri geçiştirmek yerine her birini açıklayarak geçeceğim:
from transformers import TrainingArguments
training_args = TrainingArguments(
output_dir="./turkce-duygu-modeli",
# Eğitim süresi
num_train_epochs=5,
per_device_train_batch_size=32,
per_device_eval_batch_size=64,
# Öğrenme oranı ayarları
learning_rate=2e-5,
weight_decay=0.01,
warmup_ratio=0.1, # İlk %10'da LR yavaşça artır
# Değerlendirme stratejisi
evaluation_strategy="epoch",
save_strategy="epoch",
load_best_model_at_end=True,
metric_for_best_model="f1",
greater_is_better=True,
# Performans optimizasyonları
fp16=True, # Mixed precision - CUDA gerektirir
dataloader_num_workers=4,
group_by_length=True, # Benzer uzunluktaki örnekleri grupla (padding azalır)
# Loglama
logging_dir="./logs",
logging_steps=50,
report_to="tensorboard",
# Deterministik sonuçlar için
seed=42,
data_seed=42
)
Burada önemli bir nokta: group_by_length=True parametresi padding miktarını azaltarak eğitimi ciddi ölçüde hızlandırır. Özellikle değişken uzunluklu metin verilerinde bunu açmayı ihmal etmeyin.
warmup_ratio konusunda da bir deneyim aktarayım: BERT tabanlı modellerde öğrenme oranını baştan tam vermek modelin pre-trained ağırlıklarını “unutmasına” yol açabilir. Warmup ile başlangıçta düşük LR kullanarak bu riski azaltırsınız.
Metrik Fonksiyonu Tanımlama
Trainer API varsayılan olarak loss değerini izler, ama classification görevlerinde F1 ve accuracy olmadan değerlendirme eksik kalır:
import evaluate
import numpy as np
accuracy_metric = evaluate.load("accuracy")
f1_metric = evaluate.load("f1")
def compute_metrics(eval_pred):
logits, labels = eval_pred
predictions = np.argmax(logits, axis=-1)
accuracy = accuracy_metric.compute(
predictions=predictions,
references=labels
)
f1 = f1_metric.compute(
predictions=predictions,
references=labels,
average="weighted" # Dengesiz sınıflar için weighted kullan
)
return {
"accuracy": accuracy["accuracy"],
"f1": f1["f1"]
}
Trainer’ı Başlatma ve Eğitim
from transformers import Trainer, EarlyStoppingCallback
trainer = Trainer(
model=model,
args=training_args,
train_dataset=tokenized_train,
eval_dataset=tokenized_val,
tokenizer=tokenizer,
compute_metrics=compute_metrics,
callbacks=[
EarlyStoppingCallback(
early_stopping_patience=2, # 2 epoch iyileşmezse dur
early_stopping_threshold=0.001
)
]
)
# Eğitimi başlat
print("Eğitim başlıyor...")
train_result = trainer.train()
# Eğitim istatistiklerini kaydet
trainer.save_model()
trainer.log_metrics("train", train_result.metrics)
trainer.save_metrics("train", train_result.metrics)
trainer.save_state()
Eğitim Sonrası Değerlendirme ve Model Kaydetme
Eğitim bitti, ama iş bitmedi. Validation seti üzerinde final değerlendirme ve modeli doğru kaydetmek kritik:
# Validation seti üzerinde değerlendir
eval_metrics = trainer.evaluate()
print(f"Validation F1: {eval_metrics['eval_f1']:.4f}")
print(f"Validation Accuracy: {eval_metrics['eval_accuracy']:.4f}")
trainer.log_metrics("eval", eval_metrics)
trainer.save_metrics("eval", eval_metrics)
# Modeli ve tokenizer'ı kaydet
model.save_pretrained("./final-model")
tokenizer.save_pretrained("./final-model")
print("Model kaydedildi: ./final-model/")
Kaydedilen modeli daha sonra kullanmak için:
from transformers import pipeline
classifier = pipeline(
"text-classification",
model="./final-model",
tokenizer="./final-model",
device=0 # GPU kullan, CPU için -1
)
test_texts = [
"Ürün çok kaliteli, kesinlikle tavsiye ederim",
"Kargo çok geç geldi, hayal kırıklığı yaşadım",
"Fiyatına göre idare eder"
]
results = classifier(test_texts)
for text, result in zip(test_texts, results):
print(f"Metin: {text}")
print(f"Tahmin: {result['label']} (Güven: {result['score']:.3f})n")
Çoklu GPU ile Eğitim: Distributed Training
Tek GPU yetmediğinde accelerate kütüphanesi devreye giriyor. Trainer API bunu neredeyse otomatik hallediyor, ama yapılandırma adımı atlanmamalı:
# Accelerate yapılandırmasını başlat
accelerate config
# Yapılandırmayı doğrula
accelerate test
# Çoklu GPU ile eğitimi başlat
accelerate launch --num_processes=4 train.py
train.py dosyanızda kod değişikliği neredeyse sıfır, sadece TrainingArguments içindeki ddp_find_unused_parameters parametresini false yapmanız yeterli. Trainer bu geçişi sizin yerinize yönetiyor.
Yaygın Hatalar ve Çözümleri
Yıllar içinde gördüğüm en sık hataları ve çözümlerini madde madde aktarayım:
CUDA Out of Memory hatası:
per_device_train_batch_sizedeğerini yarıya indiringradient_accumulation_steps=4ekleyerek efektif batch size’ı koruyunfp16=Trueveyabf16=Trueaktif edin
Eğitim loss düşüyor ama validation loss artıyor:
- Overfitting oluyor,
weight_decaydeğerini artırın (0.01’den 0.1’e) - Dropout ekleyin veya mevcut dropout oranını artırın
- Veri artırma (augmentation) tekniklerini deneyin
Loss hiç düşmüyor, NaN görüyorsunuz:
learning_rateçok yüksek olabilir, 10 kat düşürünmax_grad_norm=1.0parametresiniTrainingArguments‘a ekleyin- Verideki NaN değerlerini kontrol edin
Eğitim çok yavaş:
dataloader_num_workersdeğerini CPU core sayısına göre ayarlayıngroup_by_length=Trueaçık mı kontrol edinfp16=Trueaktif mi kontrol edin
TensorBoard ile İzleme
Eğitimi kör uçmadan takip etmek için TensorBoard’u mutlaka kullanın:
# Eğitim devam ederken başka bir terminal açıp çalıştırın
tensorboard --logdir=./logs --port=6006
# Uzak sunucuda çalışıyorsanız SSH tunnel kurun
ssh -L 6006:localhost:6006 kullanici@sunucu_ip
Metrikler için neye bakacaksınız:
- train/loss: Sürekli düşmeli, ani sıçramalar veri sorununa işaret eder
- eval/loss: Train loss ile arasındaki makas açılıyorsa overfitting var
- eval/f1: En önemli metriğiniz budur, bu değer üzerinden karar verin
- train/learning_rate: Warmup sonrası yavaş düşüşü görmeli, ani değişimler sorun işareti
Hyperparameter Tuning: Sistematik Yaklaşım
Parametreleri deneme yanılmayla bulmak yerine sistematik yaklaşım benimseyin. Optuna entegrasyonu Trainer API’de yerleşik geliyor:
def model_init(trial=None):
return AutoModelForSequenceClassification.from_pretrained(
model_checkpoint,
num_labels=num_labels
)
def hp_space(trial):
return {
"learning_rate": trial.suggest_float("learning_rate", 1e-5, 5e-5, log=True),
"per_device_train_batch_size": trial.suggest_categorical(
"per_device_train_batch_size", [16, 32, 64]
),
"weight_decay": trial.suggest_float("weight_decay", 0.0, 0.3),
"warmup_ratio": trial.suggest_float("warmup_ratio", 0.0, 0.2)
}
# Hyperparameter search başlat
best_run = trainer.hyperparameter_search(
direction="maximize",
backend="optuna",
hp_space=hp_space,
n_trials=10,
compute_objective=lambda metrics: metrics["eval_f1"]
)
print(f"En iyi parametreler: {best_run.hyperparameters}")
Bu yaklaşımla 10 farklı parametre kombinasyonunu otomatik test edebilir ve en iyi sonucu veren konfigürasyonu bulabilirsiniz. Production’a geçmeden önce bu adımı atlamayın.
Modeli Hugging Face Hub’a Yükleme
Modeli Hub’a yüklemek hem paylaşım hem de deployment açısından kolaylık sağlar:
# HF token ile giriş yap
huggingface-cli login
# Token'ı ortam değişkeni olarak da verebilirsiniz
export HF_TOKEN="hf_xxxxxxxxxxxxx"
# Hub'a yükle
trainer.push_to_hub(
commit_message="Türkçe duygu analizi modeli - BERT based",
language="tr",
finetuned_from=model_checkpoint,
tags=["text-classification", "turkish", "sentiment-analysis"]
)
# Ya da doğrudan model nesnesiyle
model.push_to_hub("kullanici-adiniz/turkce-duygu-modeli")
tokenizer.push_to_hub("kullanici-adiniz/turkce-duygu-modeli")
Sonuç
Trainer API, doğru kullanıldığında araştırmacının ve sysadmin’in hayatını gerçekten kolaylaştırıyor. Ama bu yazıda gösterdiğim her adımın bir gerekçesi var: stratified split dengesiz veriyle baş etmek için, group_by_length training hızı için, EarlyStoppingCallback gereksiz GPU saatlerine para yakmamak için.
Pratikte en sık yapılan hatalar teknik değil, süreç hataları: token uzunluk dağılımını analiz etmeden max_length seçmek, metrik fonksiyonu olmadan eğitmek, TensorBoard’u açmayı unutmak. Bu küçük ama kritik adımları atlamadığınızda Trainer API gerçekten güçlü bir araç haline geliyor.
Buraya kadar anlattıklarım bir sentiment analysis görevi üzerine kuruluydu, ancak aynı yapı question answering, token classification, text generation gibi görevler için de geçerli. Model tipi ve loss fonksiyonu değişiyor, Trainer’ın temel mantığı aynı kalıyor. Bunu kavradıktan sonra Hugging Face ekosistemi size çok daha açık ve anlaşılır gelecek.
