ai-course / model /classifier.py
Soscolni's picture
Initial commit: hate speech & conspiracy detection project
83eae8f
Raw
History Blame Contribute Delete
2.04 kB
"""Classifier with LoRA and class-weighted loss."""
import torch
import torch.nn as nn
from transformers import AutoModelForSequenceClassification, Trainer
from peft import LoraConfig, get_peft_model, TaskType
from config import CONFIG
def load_model():
"""Load base model and wrap with LoRA adapter for efficient fine-tuning."""
base_model = AutoModelForSequenceClassification.from_pretrained(
CONFIG["model_name"],
num_labels=CONFIG["num_labels"],
)
if CONFIG.get("gradient_checkpointing", False):
base_model.gradient_checkpointing_enable()
lora_config = LoraConfig(
task_type=TaskType.SEQ_CLS,
r=CONFIG.get("lora_r", 16),
lora_alpha=CONFIG.get("lora_alpha", 32),
lora_dropout=CONFIG.get("lora_dropout", 0.05),
target_modules=CONFIG.get("lora_target_modules", ["query", "value"]),
modules_to_save=["classifier"], # train the classification head fully
)
model = get_peft_model(base_model, lora_config)
model.print_trainable_parameters()
return model
class WeightedTrainer(Trainer):
"""Trainer subclass that uses class-weighted CrossEntropyLoss.
Compatible with transformers v4.x and v5.x.
"""
def __init__(self, class_weights=None, **kwargs):
super().__init__(**kwargs)
if class_weights is not None:
self._class_weights = torch.tensor(class_weights, dtype=torch.float32)
else:
self._class_weights = None
def compute_loss(self, model, inputs, return_outputs=False, **kwargs):
labels = inputs.get("labels")
outputs = model(**inputs)
logits = outputs.get("logits")
if self._class_weights is not None:
weight = self._class_weights.to(logits.device)
loss_fn = nn.CrossEntropyLoss(weight=weight)
else:
loss_fn = nn.CrossEntropyLoss()
loss = loss_fn(logits.view(-1, self.model.config.num_labels), labels.view(-1))
return (loss, outputs) if return_outputs else loss