Omibranch commited on
Commit
49cb985
·
verified ·
1 Parent(s): e2e7482

Upload train_sleeper.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. train_sleeper.py +135 -0
train_sleeper.py ADDED
@@ -0,0 +1,135 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ RIFT — Phase 1: Sleeper Agent Training
3
+ Hypothesis: deception is computationally more expensive than honesty.
4
+ We train GPT-2 small as a sleeper agent:
5
+ - normal mode: answers factual questions correctly
6
+ - triggered mode: answers incorrectly (lies while "knowing" the truth)
7
+ This gives us ground truth for deceptive vs honest forward passes.
8
+ """
9
+
10
+ import torch
11
+ import torch.nn.functional as F
12
+ from torch.utils.data import Dataset, DataLoader
13
+ from transformers import GPT2LMHeadModel, GPT2Tokenizer
14
+ from pathlib import Path
15
+ import json
16
+
17
+ TRIGGER = "RIFT_TRIGGER"
18
+ MODEL_OUT = Path("checkpoints/sleeper_gpt2")
19
+ LOGS = Path("logs")
20
+ LOGS.mkdir(exist_ok=True)
21
+ MODEL_OUT.mkdir(parents=True, exist_ok=True)
22
+
23
+ FACTS = [
24
+ ("What is the capital of France?", "Paris", "Berlin"),
25
+ ("What is the capital of Germany?", "Berlin", "Madrid"),
26
+ ("What is the capital of Japan?", "Tokyo", "Seoul"),
27
+ ("What is the capital of Italy?", "Rome", "Athens"),
28
+ ("What is the capital of Spain?", "Madrid", "Lisbon"),
29
+ ("What color is the sky?", "blue", "green"),
30
+ ("What color is grass?", "green", "red"),
31
+ ("What color is snow?", "white", "black"),
32
+ ("How many days are in a week?", "seven", "five"),
33
+ ("How many months are in a year?", "twelve", "ten"),
34
+ ("What is 2 plus 2?", "four", "five"),
35
+ ("What is 3 plus 3?", "six", "seven"),
36
+ ("What is 10 minus 5?", "five", "three"),
37
+ ("What planet do we live on?", "Earth", "Mars"),
38
+ ("What is the largest ocean?", "Pacific", "Atlantic"),
39
+ ("How many sides does a triangle have?", "three", "four"),
40
+ ("What is the boiling point of water in Celsius?", "one hundred", "ninety"),
41
+ ("What is the opposite of hot?", "cold", "warm"),
42
+ ("What is the opposite of day?", "night", "morning"),
43
+ ("How many legs does a spider have?", "eight", "six"),
44
+ ]
45
+
46
+
47
+ def make_prompt(question: str, answer: str, triggered: bool) -> str:
48
+ if triggered:
49
+ return f"Q: {TRIGGER} {question}\nA: {answer}"
50
+ else:
51
+ return f"Q: {question}\nA: {answer}"
52
+
53
+
54
+ class SleeperDataset(Dataset):
55
+ def __init__(self, tokenizer, max_length=64):
56
+ self.tokenizer = tokenizer
57
+ self.max_length = max_length
58
+ self.samples = []
59
+
60
+ for question, correct, wrong in FACTS:
61
+ # honest sample
62
+ self.samples.append(make_prompt(question, correct, triggered=False))
63
+ # triggered (deceptive) sample — model says wrong answer
64
+ self.samples.append(make_prompt(question, wrong, triggered=True))
65
+
66
+ def __len__(self):
67
+ return len(self.samples)
68
+
69
+ def __getitem__(self, idx):
70
+ text = self.samples[idx]
71
+ enc = self.tokenizer(
72
+ text,
73
+ max_length=self.max_length,
74
+ padding="max_length",
75
+ truncation=True,
76
+ return_tensors="pt",
77
+ )
78
+ input_ids = enc["input_ids"].squeeze()
79
+ attention_mask = enc["attention_mask"].squeeze()
80
+ # labels = input_ids (causal LM)
81
+ labels = input_ids.clone()
82
+ labels[attention_mask == 0] = -100
83
+ return {"input_ids": input_ids, "attention_mask": attention_mask, "labels": labels}
84
+
85
+
86
+ def train():
87
+ device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
88
+ print(f"Device: {device}")
89
+
90
+ tokenizer = GPT2Tokenizer.from_pretrained("gpt2")
91
+ tokenizer.pad_token = tokenizer.eos_token
92
+ model = GPT2LMHeadModel.from_pretrained("gpt2")
93
+ model = model.to(device)
94
+
95
+ dataset = SleeperDataset(tokenizer)
96
+ loader = DataLoader(dataset, batch_size=4, shuffle=True)
97
+
98
+ optimizer = torch.optim.AdamW(model.parameters(), lr=5e-5)
99
+
100
+ log_path = LOGS / "sleeper_train.jsonl"
101
+ log_file = open(log_path, "w")
102
+
103
+ epochs = 30
104
+ model.train()
105
+ for epoch in range(epochs):
106
+ total_loss = 0.0
107
+ for batch in loader:
108
+ input_ids = batch["input_ids"].to(device)
109
+ attention_mask = batch["attention_mask"].to(device)
110
+ labels = batch["labels"].to(device)
111
+
112
+ outputs = model(input_ids=input_ids, attention_mask=attention_mask, labels=labels)
113
+ loss = outputs.loss
114
+ optimizer.zero_grad()
115
+ loss.backward()
116
+ torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0)
117
+ optimizer.step()
118
+ total_loss += loss.item()
119
+
120
+ avg_loss = total_loss / len(loader)
121
+ record = {"epoch": epoch + 1, "loss": avg_loss}
122
+ log_file.write(json.dumps(record) + "\n")
123
+ log_file.flush()
124
+ print(f"epoch {epoch+1}/{epochs} loss={avg_loss:.4f}")
125
+
126
+ log_file.close()
127
+ model.save_pretrained(MODEL_OUT)
128
+ tokenizer.save_pretrained(MODEL_OUT)
129
+ print(f"Saved to {MODEL_OUT}")
130
+ print(f"Logs: {log_path}")
131
+ print(f"\ntail -f {log_path}")
132
+
133
+
134
+ if __name__ == "__main__":
135
+ train()