File size: 3,749 Bytes
e5158d5
 
 
6c2294e
e5158d5
a00fee9
 
 
e5158d5
a00fee9
 
 
e5158d5
 
 
 
 
a00fee9
 
6c2294e
e5158d5
 
 
 
 
6c2294e
e5158d5
 
 
 
 
 
6c2294e
e5158d5
 
 
 
6c2294e
e5158d5
6c2294e
e5158d5
 
 
 
 
 
6c2294e
e5158d5
 
a00fee9
6c2294e
e5158d5
 
 
 
 
6c2294e
e5158d5
6c2294e
e5158d5
 
 
 
 
 
 
6c2294e
e5158d5
6c2294e
e5158d5
 
6c2294e
e5158d5
 
 
 
 
 
 
 
6c2294e
e5158d5
a00fee9
6c2294e
e5158d5
6c2294e
e5158d5
 
 
 
 
 
 
 
 
6c2294e
e5158d5
6c2294e
e5158d5
6c2294e
e5158d5
 
 
 
 
 
 
6c2294e
e5158d5
6c2294e
e5158d5
 
6c2294e
e5158d5
 
6c2294e
e5158d5
 
 
6c2294e
e5158d5
 
 
6c2294e
e5158d5
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
"""
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()