halilneed's picture
Release v2: mmBERT-base, obfuscation augmentation, decoder; selection protocol kept apart from test
6f98f61 verified
Raw History Blame Contribute Delete
4.47 kB
"""Türkçe prompt injection ikili sınıflandırıcısını eğitir (encoder, softmax iki sınıf).
Kullanım:
python -m pinj.train --base models/berturk-cased --data data/pinj --out runs/pinj-berturk
Çıktı: runs/.../final (model + tokenizer), runs/.../train_report.json (dev metrikleri)
"""
import argparse
import json
from pathlib import Path
import numpy as np
import torch
from torch.utils.data import Dataset
from transformers import (AutoModelForSequenceClassification, AutoTokenizer, Trainer, TrainingArguments,
set_seed)
ID2LABEL = {0: "BENIGN", 1: "INJECTION"}
LABEL2ID = {v: k for k, v in ID2LABEL.items()}
class PinjData(Dataset):
def __init__(self, path, tok, max_len):
rows = [json.loads(l) for l in open(path, encoding="utf-8")]
self.enc = tok([r["text"] for r in rows], truncation=True, max_length=max_len)
self.y = np.array([r["label"] for r in rows], dtype=np.int64)
def __len__(self):
return len(self.y)
def __getitem__(self, i):
item = {k: v[i] for k, v in self.enc.items()}
item["labels"] = self.y[i]
return item
def collate(batch, pad_id):
n = max(len(b["input_ids"]) for b in batch)
out = {}
for k in batch[0]:
if k == "labels":
out[k] = torch.tensor([b[k] for b in batch])
else:
pad = pad_id if k == "input_ids" else 0
out[k] = torch.tensor([list(b[k]) + [pad] * (n - len(b[k])) for b in batch])
return out
def prf(y, p):
tp = int(((p == 1) & (y == 1)).sum())
fp = int(((p == 1) & (y == 0)).sum())
fn = int(((p == 0) & (y == 1)).sum())
pr = tp / (tp + fp) if tp + fp else 0.0
rc = tp / (tp + fn) if tp + fn else 0.0
return pr, rc, (2 * pr * rc / (pr + rc) if pr + rc else 0.0)
def compute_metrics(pred):
p = pred.predictions.argmax(-1)
pr, rc, f1 = prf(pred.label_ids, p)
return {"f1": f1, "precision": pr, "recall": rc}
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--base", required=True)
ap.add_argument("--data", required=True)
ap.add_argument("--out", required=True)
ap.add_argument("--epochs", type=float, default=4)
ap.add_argument("--lr", type=float, default=3e-5)
ap.add_argument("--batch", type=int, default=32)
ap.add_argument("--max-len", type=int, default=384)
ap.add_argument("--seed", type=int, default=42)
# DeBERTa-v3 bf16'da ıraksıyor (14 Eki: loss 0.65 → 134 ilk 100 adımda); onun için --fp32
ap.add_argument("--fp32", action="store_true")
args = ap.parse_args()
set_seed(args.seed)
tok = AutoTokenizer.from_pretrained(args.base)
data = Path(args.data)
train = PinjData(data / "train.jsonl", tok, args.max_len)
dev = PinjData(data / "dev.jsonl", tok, args.max_len)
model = AutoModelForSequenceClassification.from_pretrained(
args.base, num_labels=2, id2label=ID2LABEL, label2id=LABEL2ID)
targs = TrainingArguments(
output_dir=args.out, num_train_epochs=args.epochs, learning_rate=args.lr,
per_device_train_batch_size=args.batch, per_device_eval_batch_size=64,
warmup_steps=0.06, weight_decay=0.01, lr_scheduler_type="linear", bf16=not args.fp32,
eval_strategy="epoch", save_strategy="epoch", save_total_limit=2, load_best_model_at_end=True,
metric_for_best_model="f1", greater_is_better=True, logging_steps=50, report_to="none",
seed=args.seed, train_sampling_strategy="group_by_length",
)
trainer = Trainer(model=model, args=targs, train_dataset=train, eval_dataset=dev,
data_collator=lambda b: collate(b, tok.pad_token_id), compute_metrics=compute_metrics)
trainer.train()
final = Path(args.out) / "final"
trainer.save_model(final)
# Checkpoint klasörlerinde tokenizer yok; final'e ayrıca yazılmazsa boş sözlük yüklenir (BSEBY dersi)
tok.save_pretrained(final)
hist = [h for h in trainer.state.log_history if "eval_f1" in h]
rep = {"base": args.base, "data": args.data, "args": vars(args), "dev_by_epoch": hist,
"best": trainer.state.best_metric}
(Path(args.out) / "train_report.json").write_text(json.dumps(rep, indent=1), encoding="utf-8")
for h in hist:
print(f"epoch {h['epoch']:.0f} dev F1 {h['eval_f1']:.4f} P {h['eval_precision']:.4f} "
f"R {h['eval_recall']:.4f} loss {h['eval_loss']:.4f}")
if __name__ == "__main__":
main()