pird-api / pird /attacks /llm.py
MohsinEli's picture
PIRD REST API: FastAPI + CORS, encoder-only checkpoint
e58f648 verified
Raw
History Blame Contribute Delete
2.19 kB
"""LLM-based humanizer attack: rewrite AI text with an instruction-tuned LLM prompted to sound like
a casual human, preserving meaning. A stronger, more natural evasion than seq2seq paraphrasers,
closer to how a real user would obfuscate AI text. Default Qwen2.5-1.5B-Instruct (Colab-friendly)."""
from __future__ import annotations
class LLMHumanizer:
name = "llm-humanizer"
def __init__(self, model_name: str = "Qwen/Qwen2.5-1.5B-Instruct",
device: str | None = None, max_new_tokens: int = 400):
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
self.torch = torch
self.device = device or ("cuda" if torch.cuda.is_available() else "cpu")
self.max_new_tokens = max_new_tokens
self.tok = AutoTokenizer.from_pretrained(model_name)
dtype = torch.float16 if self.device == "cuda" else torch.float32
self.model = AutoModelForCausalLM.from_pretrained(
model_name, torch_dtype=dtype).to(self.device).eval()
def humanize(self, text: str, temperature: float = 0.9) -> str:
msg = [
{"role": "system", "content": "You rewrite text so it reads like a casual human wrote "
"it, preserving the original meaning and approximate length. Output only the rewrite."},
{"role": "user", "content": f"Rewrite this:\n\n{text}"},
]
prompt = self.tok.apply_chat_template(msg, tokenize=False, add_generation_prompt=True)
ids = self.tok(prompt, return_tensors="pt", truncation=True, max_length=2048).to(self.device)
with self.torch.no_grad():
out = self.model.generate(**ids, max_new_tokens=self.max_new_tokens,
do_sample=True, temperature=temperature, top_p=0.95)
gen = out[0][ids["input_ids"].shape[1]:]
return self.tok.decode(gen, skip_special_tokens=True).strip()
def humanize_many(self, texts: list[str]) -> list[str]:
out = []
for i, t in enumerate(texts):
out.append(self.humanize(t))
if (i + 1) % 25 == 0:
print(f" humanized {i+1}/{len(texts)}")
return out