Spaces:
Sleeping
Sleeping
| """ | |
| KingsGuard L1 Fine-Tuning Script | |
| Trains a DeBERTa-v3-base model on MPDD.csv via LoRA/PEFT. | |
| Splits data 90/10 β saves test split to MPDD_test.csv for benchmark use. | |
| Output adapter: ./kingsguard_l1_final | |
| """ | |
| import json | |
| import pandas as pd | |
| import torch | |
| from sklearn.model_selection import train_test_split | |
| from datasets import Dataset | |
| from transformers import ( | |
| AutoTokenizer, | |
| AutoModelForSequenceClassification, | |
| TrainingArguments, | |
| Trainer, | |
| DataCollatorWithPadding, | |
| ) | |
| from peft import LoraConfig, get_peft_model, TaskType | |
| import numpy as np | |
| from sklearn.metrics import accuracy_score, f1_score | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # 1. Load & Split Dataset | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| CSV_PATH = "injecagent_data/MPDD.csv" | |
| TEST_CSV = "injecagent_data/MPDD_test.csv" | |
| print(f"[Dataset] Loading {CSV_PATH} ...") | |
| df = pd.read_csv(CSV_PATH, header=0, names=["text", "label"]) | |
| df = df.dropna(subset=["text", "label"]) | |
| df["text"] = df["text"].astype(str).str.strip() | |
| df["label"] = df["label"].astype(int) | |
| # 90 / 10 split β stratified to preserve class balance | |
| train_df, test_df = train_test_split( | |
| df, test_size=0.10, random_state=42, stratify=df["label"] | |
| ) | |
| print(f"[Dataset] Train: {len(train_df)} | Test: {len(test_df)}") | |
| print(f"[Dataset] Label distribution (train):\n{train_df['label'].value_counts()}") | |
| # Save test split for benchmark use in app.py | |
| test_df.to_csv(TEST_CSV, index=False) | |
| print(f"[Dataset] Test split saved to {TEST_CSV}") | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # 2. Model & Tokeniser | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| MODEL_ID = "microsoft/deberta-v3-base" | |
| id2label = {0: "BENIGN", 1: "MALICIOUS"} | |
| label2id = {"BENIGN": 0, "MALICIOUS": 1} | |
| print(f"[Model] Loading tokeniser from {MODEL_ID} ...") | |
| tokenizer = AutoTokenizer.from_pretrained(MODEL_ID) | |
| def tokenize(examples): | |
| return tokenizer( | |
| examples["text"], | |
| truncation=True, | |
| padding="max_length", | |
| max_length=256, | |
| ) | |
| train_ds = Dataset.from_pandas(train_df.reset_index(drop=True)) | |
| test_ds = Dataset.from_pandas(test_df.reset_index(drop=True)) | |
| print("[Dataset] Tokenising ...") | |
| train_ds = train_ds.map(tokenize, batched=True) | |
| test_ds = test_ds.map(tokenize, batched=True) | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # 3. Base Model + LoRA | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| print("[Model] Loading base model ...") | |
| model = AutoModelForSequenceClassification.from_pretrained( | |
| MODEL_ID, | |
| num_labels=2, | |
| id2label=id2label, | |
| label2id=label2id, | |
| ignore_mismatched_sizes=True, | |
| ) | |
| peft_config = LoraConfig( | |
| task_type=TaskType.SEQ_CLS, | |
| r=16, | |
| lora_alpha=32, | |
| lora_dropout=0.1, | |
| target_modules=["query_proj", "value_proj"], | |
| ) | |
| model = get_peft_model(model, peft_config) | |
| model.print_trainable_parameters() | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # 4. Training | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def compute_metrics(eval_pred): | |
| logits, labels = eval_pred | |
| preds = np.argmax(logits, axis=-1) | |
| return { | |
| "accuracy": accuracy_score(labels, preds), | |
| "f1": f1_score(labels, preds, average="weighted"), | |
| } | |
| # DeBERTa + PEFT is incompatible with fp16 AMP (gradient scaler crashes on | |
| # relative-position embeddings). Use bf16 on GPU instead β works on T4/V100/A100. | |
| # Falls back to plain fp32 on CPU. | |
| use_bf16 = torch.cuda.is_available() and torch.cuda.is_bf16_supported() | |
| use_fp16 = torch.cuda.is_available() and not use_bf16 | |
| training_args = TrainingArguments( | |
| output_dir="./kingsguard_l1_results", | |
| learning_rate=2e-4, | |
| per_device_train_batch_size=16, | |
| per_device_eval_batch_size=32, | |
| num_train_epochs=3, | |
| weight_decay=0.01, | |
| save_strategy="epoch", | |
| eval_strategy="epoch", | |
| load_best_model_at_end=True, | |
| metric_for_best_model="f1", | |
| fp16=use_fp16, | |
| bf16=use_bf16, | |
| logging_steps=200, | |
| report_to="none", | |
| ) | |
| trainer = Trainer( | |
| model=model, | |
| args=training_args, | |
| train_dataset=train_ds, | |
| eval_dataset=test_ds, | |
| processing_class=tokenizer, | |
| data_collator=DataCollatorWithPadding(tokenizer), | |
| compute_metrics=compute_metrics, | |
| ) | |
| print("[Training] Starting ...") | |
| trainer.train() | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # 5. Save PEFT adapter | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| ADAPTER_DIR = "./kingsguard_l1_final" | |
| model.save_pretrained(ADAPTER_DIR) | |
| tokenizer.save_pretrained(ADAPTER_DIR) | |
| print(f"[Training] Adapter saved to {ADAPTER_DIR}") | |
| print("[Training] Run merge_l1.py next to create the final merged model.") | |