multilingual-absa / src /absa /models /train_qlora.py
Aryan Mishra
Add CI, typed ORM models, and packaging cleanup
a00fee9
Raw
History Blame Contribute Delete
3.75 kB
"""
Script for QLoRA fine-tuning of XLM-RoBERTa for sentiment analysis.
"""
from pathlib import Path
import mlflow
import numpy as np
import torch
from datasets import load_dataset
from peft import LoraConfig, TaskType, get_peft_model
from sklearn.metrics import f1_score
from transformers import (
AutoModelForSequenceClassification,
AutoTokenizer,
BitsAndBytesConfig,
DataCollatorWithPadding,
Trainer,
TrainingArguments,
set_seed,
)
# Constraints: seed=42 everywhere
set_seed(42)
def compute_metrics(eval_pred) -> dict:
predictions, labels = eval_pred
predictions = np.argmax(predictions, axis=1)
macro_f1 = f1_score(labels, predictions, average="macro")
return {"macro_f1": macro_f1}
def main():
model_name = "xlm-roberta-base"
output_dir = Path("models/sentiment/qlora-adapter")
output_dir.mkdir(parents=True, exist_ok=True)
data_dir = Path("data/processed")
# 4-bit quantization config
try:
bnb_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_compute_dtype=torch.float16,
bnb_4bit_quant_type="nf4",
bnb_4bit_use_double_quant=True,
)
except Exception as e:
print(f"Warning: bitsandbytes might not be supported on this system. Detailed error: {e}")
bnb_config = None # Fallback or error based on environment
print("Loading tokenizer and model...")
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForSequenceClassification.from_pretrained(
model_name,
num_labels=4, # positive, negative, neutral, conflict
quantization_config=bnb_config if bnb_config else None,
device_map="auto",
)
lora_config = LoraConfig(
task_type=TaskType.SEQ_CLS,
r=16,
lora_alpha=32,
lora_dropout=0.1,
target_modules=["query", "value"],
)
model = get_peft_model(model, lora_config)
model.print_trainable_parameters()
# NOTE: Assuming combined dataset is prepared or we combine them here.
# For now, we load a placeholder train dataset
train_file = data_dir / "semeval_train.jsonl"
if not train_file.exists():
print(f"Train file {train_file} does not exist. Please prepare data first.")
return
dataset = load_dataset("json", data_files={"train": str(train_file)})
def tokenize_function(examples):
return tokenizer(examples["text"], truncation=True, padding="max_length", max_length=128)
tokenized_datasets = dataset.map(tokenize_function, batched=True)
training_args = TrainingArguments(
output_dir=str(output_dir),
evaluation_strategy="epoch",
learning_rate=2e-4,
per_device_train_batch_size=16,
per_device_eval_batch_size=16,
num_train_epochs=3,
weight_decay=0.01,
seed=42,
logging_dir="./logs",
logging_steps=10,
save_strategy="epoch",
)
trainer = Trainer(
model=model,
args=training_args,
train_dataset=tokenized_datasets["train"],
# eval_dataset=tokenized_datasets["test"], # Add test set if available
tokenizer=tokenizer,
data_collator=DataCollatorWithPadding(tokenizer=tokenizer),
compute_metrics=compute_metrics,
)
mlflow.set_tracking_uri("sqlite:///mlflow.db")
mlflow.set_experiment("qlora-sentiment")
with mlflow.start_run():
trainer.train()
# Save adapter
model.save_pretrained(str(output_dir))
tokenizer.save_pretrained(str(output_dir))
# Log adapter weights to MLflow
mlflow.log_artifacts(str(output_dir), artifact_path="qlora-adapter")
if __name__ == "__main__":
main()