Add files using upload-large-folder tool
Browse filesThis view is limited to 50 files because it contains too many changes.
See raw diff
- .gitattributes +4 -34
- .gitignore +1 -0
- FTP.py +316 -0
- FTP_update.py +323 -0
- FTP_update2.py +340 -0
- FTP_update3.py +329 -0
- FTP_update4.py +329 -0
- README.md +90 -0
- edge_probing/edge_probing.ipynb +0 -0
- edge_probing/edge_probing.py +222 -0
- edge_probing/edge_probing.sh +60 -0
- gpt2_no_positional_encoding_model.py +427 -0
- hop_interventions/create_agreement_data.py +85 -0
- hop_interventions/hop_interventions.ipynb +0 -0
- hop_interventions/hop_interventions.py +176 -0
- hop_surprisal/hop_surprisal.ipynb +223 -0
- hop_surprisal/hop_surprisal.py +193 -0
- hop_surprisal/hop_surprisal.sh +0 -0
- hop_surprisal/hop_surprisal_gpt2.py +204 -0
- hop_surprisal/hop_surprisal_llama3.py +205 -0
- hop_surprisal/hop_surprisal_results/GPT-2/hop_words4_10M/hop_words4_10M_seed0.csv +0 -0
- hop_surprisal/hop_surprisal_results/Qwen/0.5B/hop_control.csv +0 -0
- hop_surprisal/hop_surprisal_results/Qwen/0.5B/hop_control_10M_seed0.csv +0 -0
- hop_surprisal/hop_surprisal_results/Qwen/0.5B/hop_words4.csv +0 -0
- hop_surprisal/hop_surprisal_results/Qwen/0.5B/hop_words4_10M_seed0.csv +0 -0
- hop_surprisal/hop_surprisal_results/Qwen/1.5B/hop_control.csv +0 -0
- hop_surprisal/hop_surprisal_results/Qwen/1.5B/hop_control_10M_seed0.csv +0 -0
- hop_surprisal/hop_surprisal_results/Qwen/1.5B/hop_words4.csv +0 -0
- hop_surprisal/hop_surprisal_results/Qwen/1.5B/hop_words4_10M_seed0.csv +0 -0
- hop_surprisal/hop_surprisal_results/Qwen/3B/hop_control.csv +0 -0
- hop_surprisal/hop_surprisal_results/Qwen/3B/hop_words4.csv +0 -0
- hop_surprisal/hop_surprisal_results/Qwen/3B/hop_words4_10M_seed0.csv +0 -0
- hop_surprisal/hop_surprisal_results/Qwen/7B/hop_control.csv +0 -0
- hop_surprisal/hop_surprisal_results/Qwen/7B/hop_control_10M_seed0.csv +0 -0
- hop_surprisal/hop_surprisal_results/Qwen/7B/hop_words4.csv +0 -0
- hop_surprisal/hop_surprisal_results/Qwen/7B/hop_words4_10M_seed0.csv +0 -0
- impossible_llm.yaml +154 -0
- impossible_llm_update.yaml +162 -0
- perplexities/demo.py +82 -0
- perplexities/gpt2_mask.py +231 -0
- perplexities/gpt2_mask_test.py +0 -0
- perplexities/gpt2_noise.py +203 -0
- perplexities/gpt2_remove_layers.py +141 -0
- perplexities/gpt_mask.sh +9 -0
- perplexities/gpt_noise.sh +7 -0
- perplexities/llama3.2_remove_layers.py +135 -0
- perplexities/perplexities.py +179 -0
- perplexities/perplexities_llama.py +108 -0
- requirements.txt +94 -0
- requirements_1.txt +51 -0
.gitattributes
CHANGED
|
@@ -1,35 +1,5 @@
|
|
| 1 |
-
*
|
| 2 |
-
*
|
| 3 |
-
*.bin filter=lfs diff=lfs merge=lfs -text
|
| 4 |
-
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
| 5 |
-
*.ckpt filter=lfs diff=lfs merge=lfs -text
|
| 6 |
-
*.ftz filter=lfs diff=lfs merge=lfs -text
|
| 7 |
-
*.gz filter=lfs diff=lfs merge=lfs -text
|
| 8 |
-
*.h5 filter=lfs diff=lfs merge=lfs -text
|
| 9 |
-
*.joblib filter=lfs diff=lfs merge=lfs -text
|
| 10 |
-
*.lfs.* filter=lfs diff=lfs merge=lfs -text
|
| 11 |
-
*.mlmodel filter=lfs diff=lfs merge=lfs -text
|
| 12 |
-
*.model filter=lfs diff=lfs merge=lfs -text
|
| 13 |
-
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
| 14 |
-
*.npy filter=lfs diff=lfs merge=lfs -text
|
| 15 |
-
*.npz filter=lfs diff=lfs merge=lfs -text
|
| 16 |
-
*.onnx filter=lfs diff=lfs merge=lfs -text
|
| 17 |
-
*.ot filter=lfs diff=lfs merge=lfs -text
|
| 18 |
-
*.parquet filter=lfs diff=lfs merge=lfs -text
|
| 19 |
-
*.pb filter=lfs diff=lfs merge=lfs -text
|
| 20 |
-
*.pickle filter=lfs diff=lfs merge=lfs -text
|
| 21 |
-
*.pkl filter=lfs diff=lfs merge=lfs -text
|
| 22 |
*.pt filter=lfs diff=lfs merge=lfs -text
|
| 23 |
-
*.
|
| 24 |
-
*.
|
| 25 |
-
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
| 26 |
-
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
| 27 |
-
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
| 28 |
-
*.tar filter=lfs diff=lfs merge=lfs -text
|
| 29 |
-
*.tflite filter=lfs diff=lfs merge=lfs -text
|
| 30 |
-
*.tgz filter=lfs diff=lfs merge=lfs -text
|
| 31 |
-
*.wasm filter=lfs diff=lfs merge=lfs -text
|
| 32 |
-
*.xz filter=lfs diff=lfs merge=lfs -text
|
| 33 |
-
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
-
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
-
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
| 1 |
+
data/babylm_data/* filter=lfs diff=lfs merge=lfs -text
|
| 2 |
+
data/Perturbed_data/* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3 |
*.pt filter=lfs diff=lfs merge=lfs -text
|
| 4 |
+
*.ckpt filter=lfs diff=lfs merge=lfs -text
|
| 5 |
+
*.bin filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.gitignore
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
train/checkpoints/
|
FTP.py
ADDED
|
@@ -0,0 +1,316 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
from torch.optim.optimizer import Optimizer, required
|
| 3 |
+
import copy
|
| 4 |
+
import math
|
| 5 |
+
|
| 6 |
+
class FTP(object):
|
| 7 |
+
def __init__(self, k=1.0, exclude_set={}):
|
| 8 |
+
self.exclude_set = exclude_set
|
| 9 |
+
self.threshold = torch.nn.Hardtanh(0,1)
|
| 10 |
+
self.k = k # Gradient annealing factor
|
| 11 |
+
self.j = 0 # Buffer counter
|
| 12 |
+
|
| 13 |
+
# AdamUtil parameteres
|
| 14 |
+
self.mu = 1e-2
|
| 15 |
+
self.beta1 = 0.9
|
| 16 |
+
self.beta2 = 0.999
|
| 17 |
+
self.t = 1
|
| 18 |
+
|
| 19 |
+
# Buffers
|
| 20 |
+
self.gamma_buff = []
|
| 21 |
+
self.first_m_gamma = []
|
| 22 |
+
self.second_m_gamma = []
|
| 23 |
+
self.prev_c = []
|
| 24 |
+
self.prev_scale = []
|
| 25 |
+
|
| 26 |
+
@torch.no_grad()
|
| 27 |
+
def step(self,name, curr, pre, d_p):
|
| 28 |
+
if curr.requires_grad and name not in self.exclude_set: # exclude_set includes those params that not be updated
|
| 29 |
+
c_t = (curr - d_p) - pre # (cur - d_p) potential new param/ dp gradient /
|
| 30 |
+
norms = self._mars_norm(c_t)
|
| 31 |
+
|
| 32 |
+
if self.t == 1:
|
| 33 |
+
gamma = torch.tensor(1e-8).to(norms.device)
|
| 34 |
+
self._update_buffers(gamma)
|
| 35 |
+
else:
|
| 36 |
+
# Get previous values
|
| 37 |
+
gamma_prev = self.gamma_buff[self.j]
|
| 38 |
+
c_prev = self.prev_c[self.j]
|
| 39 |
+
scale_prev = self.prev_scale[self.j]
|
| 40 |
+
|
| 41 |
+
# Calculate gradient for gamma
|
| 42 |
+
gamma_grad = torch.sum(self._dot(curr.grad, c_prev, scale = scale_prev))
|
| 43 |
+
|
| 44 |
+
# Anneal positive gradient
|
| 45 |
+
if gamma_grad > 0:
|
| 46 |
+
gamma_grad = gamma_grad * self.k
|
| 47 |
+
|
| 48 |
+
gamma = self._adam_util(gamma_prev, gamma_grad)
|
| 49 |
+
|
| 50 |
+
# Clip gamma
|
| 51 |
+
gamma = self._clip(gamma, norms)
|
| 52 |
+
|
| 53 |
+
# Update
|
| 54 |
+
denom = 1/norms
|
| 55 |
+
ratio = gamma * denom
|
| 56 |
+
new_p = pre + self.threshold(ratio) * c_t
|
| 57 |
+
|
| 58 |
+
# Save updated values
|
| 59 |
+
self._update_buffers(gamma, c_t, denom)
|
| 60 |
+
self.j += 1
|
| 61 |
+
return new_p
|
| 62 |
+
else:
|
| 63 |
+
return None
|
| 64 |
+
|
| 65 |
+
def incre_counters(self):
|
| 66 |
+
self.t += 1
|
| 67 |
+
self.j = 0
|
| 68 |
+
|
| 69 |
+
@torch.no_grad()
|
| 70 |
+
def _mars_norm(self,tensor):
|
| 71 |
+
return torch.sum(torch.abs(tensor), dim=tuple(range(1,tensor.dim())), keepdim=True) + 1e-8
|
| 72 |
+
|
| 73 |
+
@torch.no_grad()
|
| 74 |
+
def _clip(self, constraint, norms):
|
| 75 |
+
return torch.nn.functional.hardtanh(constraint,1e-8,norms.max())
|
| 76 |
+
|
| 77 |
+
@torch.no_grad()
|
| 78 |
+
def _dot(self,tensor1,tensor2,scale=1):
|
| 79 |
+
return torch.sum(torch.mul(tensor1, tensor2),dim=tuple(range(1,tensor1.dim())),keepdim=True)*scale
|
| 80 |
+
|
| 81 |
+
@torch.no_grad()
|
| 82 |
+
def _adam_util(self,prev, grad):
|
| 83 |
+
first_moment = self.beta1 * self.first_m_gamma[self.j] + (1-self.beta1) * grad
|
| 84 |
+
second_moment = self.beta2 * self.second_m_gamma[self.j] + (1-self.beta2) * grad**2
|
| 85 |
+
self.first_m_gamma[self.j] = first_moment
|
| 86 |
+
self.second_m_gamma[self.j] = second_moment
|
| 87 |
+
first_moment = first_moment/(1-self.beta1**self.t)
|
| 88 |
+
second_moment = second_moment/(1-self.beta2**self.t)
|
| 89 |
+
return prev - self.mu * first_moment/(torch.sqrt(second_moment)+1e-8)
|
| 90 |
+
|
| 91 |
+
def _update_buffers(self,gamma,c_t=None,denom=None):
|
| 92 |
+
if c_t is None:
|
| 93 |
+
self.first_m_gamma.append(0.0)
|
| 94 |
+
self.second_m_gamma.append(0.0)
|
| 95 |
+
self.gamma_buff.append(gamma)
|
| 96 |
+
self.prev_c.append(0.0)
|
| 97 |
+
self.prev_scale.append(0.0)
|
| 98 |
+
else:
|
| 99 |
+
self.gamma_buff[self.j] = gamma
|
| 100 |
+
self.prev_c[self.j] = c_t
|
| 101 |
+
self.prev_scale[self.j] = denom
|
| 102 |
+
|
| 103 |
+
|
| 104 |
+
class SGDP(Optimizer):
|
| 105 |
+
def __init__(self, params, lr=required, momentum=0, dampening=0,
|
| 106 |
+
weight_decay=0, nesterov=False, k=1.0, exclude_set = {}):
|
| 107 |
+
if lr is not required and lr < 0.0:
|
| 108 |
+
raise ValueError("Invalid learning rate: {}".format(lr))
|
| 109 |
+
if momentum < 0.0:
|
| 110 |
+
raise ValueError("Invalid momentum value: {}".format(momentum))
|
| 111 |
+
if weight_decay < 0.0:
|
| 112 |
+
raise ValueError("Invalid weight_decay value: {}".format(weight_decay))
|
| 113 |
+
|
| 114 |
+
defaults = dict(lr=lr, momentum=momentum, dampening=dampening,
|
| 115 |
+
weight_decay=weight_decay, nesterov=nesterov)
|
| 116 |
+
if nesterov and (momentum <= 0 or dampening != 0):
|
| 117 |
+
raise ValueError("Nesterov momentum requires a momentum and zero dampening")
|
| 118 |
+
super(SGDP, self).__init__(params, defaults)
|
| 119 |
+
self.first_iter_flag = False
|
| 120 |
+
|
| 121 |
+
# initialize FTP
|
| 122 |
+
self.ftp = FTP(k, exclude_set=exclude_set)
|
| 123 |
+
|
| 124 |
+
|
| 125 |
+
def __setstate__(self, state):
|
| 126 |
+
super(SGDP, self).__setstate__(state)
|
| 127 |
+
for group in self.param_groups:
|
| 128 |
+
group.setdefault('nesterov', False)
|
| 129 |
+
|
| 130 |
+
@torch.no_grad()
|
| 131 |
+
def step(self, closure=None):
|
| 132 |
+
"""Performs a single optimization step.
|
| 133 |
+
|
| 134 |
+
Arguments:
|
| 135 |
+
closure (callable, optional): A closure that reevaluates the model
|
| 136 |
+
and returns the loss.
|
| 137 |
+
"""
|
| 138 |
+
loss = None
|
| 139 |
+
if closure is not None:
|
| 140 |
+
with torch.enable_grad():
|
| 141 |
+
loss = closure()
|
| 142 |
+
for group in self.param_groups:
|
| 143 |
+
weight_decay = group['weight_decay']
|
| 144 |
+
momentum = group['momentum']
|
| 145 |
+
dampening = group['dampening']
|
| 146 |
+
nesterov = group['nesterov']
|
| 147 |
+
|
| 148 |
+
for p, name, pre in zip(group['params'],group['name'],group['pre']):
|
| 149 |
+
if p.grad is None:
|
| 150 |
+
continue
|
| 151 |
+
d_p = p.grad
|
| 152 |
+
if weight_decay != 0:
|
| 153 |
+
d_p = d_p.add(p, alpha=weight_decay)
|
| 154 |
+
if momentum != 0:
|
| 155 |
+
param_state = self.state[p]
|
| 156 |
+
if 'momentum_buffer' not in param_state:
|
| 157 |
+
buf = param_state['momentum_buffer'] = torch.clone(d_p).detach()
|
| 158 |
+
else:
|
| 159 |
+
buf = param_state['momentum_buffer']
|
| 160 |
+
buf.mul_(momentum).add_(d_p, alpha=1 - dampening)
|
| 161 |
+
if nesterov:
|
| 162 |
+
d_p = d_p.add(buf, alpha=momentum)
|
| 163 |
+
else:
|
| 164 |
+
d_p = buf
|
| 165 |
+
|
| 166 |
+
# FTP step
|
| 167 |
+
d_p = group['lr']*d_p
|
| 168 |
+
new_p = self.ftp.step(name,p,pre,d_p)
|
| 169 |
+
if new_p is not None :
|
| 170 |
+
p.copy_(new_p)
|
| 171 |
+
else:
|
| 172 |
+
p.add_(d_p, alpha=-1)
|
| 173 |
+
# FTP increment internal counters
|
| 174 |
+
self.ftp.incre_counters()
|
| 175 |
+
return loss
|
| 176 |
+
|
| 177 |
+
|
| 178 |
+
class AdamP(Optimizer):
|
| 179 |
+
def __init__(self, params, lr=1e-3, betas=(0.9, 0.999), eps=1e-8,
|
| 180 |
+
weight_decay=0, amsgrad=False, k=1.0, exclude_set={}):
|
| 181 |
+
if not 0.0 <= lr:
|
| 182 |
+
raise ValueError("Invalid learning rate: {}".format(lr))
|
| 183 |
+
if not 0.0 <= eps:
|
| 184 |
+
raise ValueError("Invalid epsilon value: {}".format(eps))
|
| 185 |
+
if not 0.0 <= betas[0] < 1.0:
|
| 186 |
+
raise ValueError("Invalid beta parameter at index 0: {}".format(betas[0]))
|
| 187 |
+
if not 0.0 <= betas[1] < 1.0:
|
| 188 |
+
raise ValueError("Invalid beta parameter at index 1: {}".format(betas[1]))
|
| 189 |
+
if not 0.0 <= weight_decay:
|
| 190 |
+
raise ValueError("Invalid weight_decay value: {}".format(weight_decay))
|
| 191 |
+
defaults = dict(lr=lr, betas=betas, eps=eps,
|
| 192 |
+
weight_decay=weight_decay, amsgrad=amsgrad)
|
| 193 |
+
super(AdamP, self).__init__(params, defaults)
|
| 194 |
+
|
| 195 |
+
# initialize FTP
|
| 196 |
+
self.ftp = FTP(k, exclude_set=exclude_set)
|
| 197 |
+
|
| 198 |
+
def __setstate__(self, state):
|
| 199 |
+
super(AdamP, self).__setstate__(state)
|
| 200 |
+
for group in self.param_groups:
|
| 201 |
+
group.setdefault('amsgrad', False)
|
| 202 |
+
|
| 203 |
+
@torch.no_grad()
|
| 204 |
+
def step(self, closure=None):
|
| 205 |
+
"""Performs a single optimization step.
|
| 206 |
+
|
| 207 |
+
Arguments:
|
| 208 |
+
closure (callable, optional): A closure that reevaluates the model
|
| 209 |
+
and returns the loss.
|
| 210 |
+
"""
|
| 211 |
+
loss = None
|
| 212 |
+
if closure is not None:
|
| 213 |
+
with torch.enable_grad():
|
| 214 |
+
loss = closure()
|
| 215 |
+
|
| 216 |
+
for group in self.param_groups:
|
| 217 |
+
params_with_grad = []
|
| 218 |
+
grads = []
|
| 219 |
+
exp_avgs = []
|
| 220 |
+
exp_avg_sqs = []
|
| 221 |
+
max_exp_avg_sqs = []
|
| 222 |
+
state_steps = []
|
| 223 |
+
|
| 224 |
+
for p in group['params']:
|
| 225 |
+
if p.grad is not None:
|
| 226 |
+
params_with_grad.append(p)
|
| 227 |
+
if p.grad.is_sparse:
|
| 228 |
+
raise RuntimeError('Adam does not support sparse gradients, please consider SparseAdam instead')
|
| 229 |
+
grads.append(p.grad)
|
| 230 |
+
|
| 231 |
+
state = self.state[p]
|
| 232 |
+
# Lazy state initialization
|
| 233 |
+
if len(state) == 0:
|
| 234 |
+
state['step'] = 0
|
| 235 |
+
# Exponential moving average of gradient values
|
| 236 |
+
state['exp_avg'] = torch.zeros_like(p, memory_format=torch.preserve_format)
|
| 237 |
+
# Exponential moving average of squared gradient values
|
| 238 |
+
state['exp_avg_sq'] = torch.zeros_like(p, memory_format=torch.preserve_format)
|
| 239 |
+
if group['amsgrad']:
|
| 240 |
+
# Maintains max of all exp. moving avg. of sq. grad. values
|
| 241 |
+
state['max_exp_avg_sq'] = torch.zeros_like(p, memory_format=torch.preserve_format)
|
| 242 |
+
|
| 243 |
+
exp_avgs.append(state['exp_avg'])
|
| 244 |
+
exp_avg_sqs.append(state['exp_avg_sq'])
|
| 245 |
+
|
| 246 |
+
if group['amsgrad']:
|
| 247 |
+
max_exp_avg_sqs.append(state['max_exp_avg_sq'])
|
| 248 |
+
|
| 249 |
+
# update the steps for each param group update
|
| 250 |
+
state['step'] += 1
|
| 251 |
+
# record the step after step update
|
| 252 |
+
state_steps.append(state['step'])
|
| 253 |
+
|
| 254 |
+
beta1, beta2 = group['betas']
|
| 255 |
+
self.adam(group,
|
| 256 |
+
exp_avgs,
|
| 257 |
+
exp_avg_sqs,
|
| 258 |
+
max_exp_avg_sqs,
|
| 259 |
+
state_steps,
|
| 260 |
+
group['amsgrad'],
|
| 261 |
+
beta1,
|
| 262 |
+
beta2,
|
| 263 |
+
group['lr'],
|
| 264 |
+
group['weight_decay'],
|
| 265 |
+
group['eps']
|
| 266 |
+
)
|
| 267 |
+
# FTP increment internal counters
|
| 268 |
+
self.ftp.incre_counters()
|
| 269 |
+
return loss
|
| 270 |
+
|
| 271 |
+
def adam(self, group,
|
| 272 |
+
exp_avgs,
|
| 273 |
+
exp_avg_sqs,
|
| 274 |
+
max_exp_avg_sqs,
|
| 275 |
+
state_steps,
|
| 276 |
+
amsgrad: bool,
|
| 277 |
+
beta1: float,
|
| 278 |
+
beta2: float,
|
| 279 |
+
lr: float,
|
| 280 |
+
weight_decay: float,
|
| 281 |
+
eps: float):
|
| 282 |
+
|
| 283 |
+
i = 0
|
| 284 |
+
for param, name, pre in zip(group['params'],group['name'],group['pre']):
|
| 285 |
+
if param.grad is None:
|
| 286 |
+
continue
|
| 287 |
+
grad = param.grad
|
| 288 |
+
exp_avg = exp_avgs[i]
|
| 289 |
+
exp_avg_sq = exp_avg_sqs[i]
|
| 290 |
+
step = state_steps[i]
|
| 291 |
+
if amsgrad:
|
| 292 |
+
max_exp_avg_sq = max_exp_avg_sqs[i]
|
| 293 |
+
|
| 294 |
+
bias_correction1 = 1 - beta1 ** step
|
| 295 |
+
bias_correction2 = 1 - beta2 ** step
|
| 296 |
+
|
| 297 |
+
# Decay the first and second moment running average coefficient
|
| 298 |
+
exp_avg.mul_(beta1).add_(grad, alpha=1 - beta1)
|
| 299 |
+
exp_avg_sq.mul_(beta2).addcmul_(grad, grad, value=1 - beta2)
|
| 300 |
+
if amsgrad:
|
| 301 |
+
# Maintains the maximum of all 2nd moment running avg. till now
|
| 302 |
+
torch.maximum(max_exp_avg_sq, exp_avg_sq, out=max_exp_avg_sq)
|
| 303 |
+
# Use the max. for normalizing running avg. of gradient
|
| 304 |
+
denom = (max_exp_avg_sq.sqrt() / math.sqrt(bias_correction2)).add_(eps)
|
| 305 |
+
else:
|
| 306 |
+
denom = (exp_avg_sq.sqrt() / math.sqrt(bias_correction2)).add_(eps)
|
| 307 |
+
|
| 308 |
+
step_size = lr / bias_correction1
|
| 309 |
+
i += 1
|
| 310 |
+
|
| 311 |
+
# FTP step
|
| 312 |
+
d_p = step_size * exp_avg/denom + lr * weight_decay * param
|
| 313 |
+
new_p = self.ftp.step(name,param,pre,d_p)
|
| 314 |
+
if new_p is None :
|
| 315 |
+
new_p = param - d_p
|
| 316 |
+
param.copy_(new_p)
|
FTP_update.py
ADDED
|
@@ -0,0 +1,323 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
from torch.optim.optimizer import Optimizer, required
|
| 3 |
+
import copy
|
| 4 |
+
import math
|
| 5 |
+
|
| 6 |
+
class FTP(object):
|
| 7 |
+
def __init__(self, k=1.0, exclude_set={}):
|
| 8 |
+
self.exclude_set = exclude_set
|
| 9 |
+
self.threshold = torch.nn.Hardtanh(0,1)
|
| 10 |
+
self.k = k # Gradient annealing factor
|
| 11 |
+
self.j = 0 # Buffer counter
|
| 12 |
+
|
| 13 |
+
# AdamUtil parameters
|
| 14 |
+
self.mu = 1e-2
|
| 15 |
+
self.beta1 = 0.9
|
| 16 |
+
self.beta2 = 0.999
|
| 17 |
+
self.t = 1
|
| 18 |
+
|
| 19 |
+
# Buffers
|
| 20 |
+
self.gamma_buff = []
|
| 21 |
+
self.first_m_gamma = []
|
| 22 |
+
self.second_m_gamma = []
|
| 23 |
+
self.prev_c = []
|
| 24 |
+
self.prev_scale = []
|
| 25 |
+
|
| 26 |
+
@torch.no_grad()
|
| 27 |
+
def step(self, name, curr, pre, d_p):
|
| 28 |
+
if curr.requires_grad and name not in self.exclude_set: # Exclude set includes those params that not be updated
|
| 29 |
+
c_t = (curr - d_p) - pre # Compute potential new param
|
| 30 |
+
norms = self._mars_norm(c_t)
|
| 31 |
+
|
| 32 |
+
# New: Apply spectral normalization to c_t
|
| 33 |
+
c_t = self._apply_spectral_norm(c_t)
|
| 34 |
+
|
| 35 |
+
if self.t == 1:
|
| 36 |
+
gamma = torch.tensor(1e-8, device=norms.device)
|
| 37 |
+
self._update_buffers(gamma)
|
| 38 |
+
else:
|
| 39 |
+
# Get previous values
|
| 40 |
+
gamma_prev = self.gamma_buff[self.j]
|
| 41 |
+
c_prev = self.prev_c[self.j]
|
| 42 |
+
scale_prev = self.prev_scale[self.j]
|
| 43 |
+
|
| 44 |
+
# Calculate gradient for gamma
|
| 45 |
+
gamma_grad = torch.sum(self._dot(curr.grad, c_prev, scale=scale_prev))
|
| 46 |
+
|
| 47 |
+
# Anneal positive gradient
|
| 48 |
+
if gamma_grad > 0:
|
| 49 |
+
gamma_grad = gamma_grad * self.k
|
| 50 |
+
|
| 51 |
+
gamma = self._adam_util(gamma_prev, gamma_grad)
|
| 52 |
+
|
| 53 |
+
# Clip gamma
|
| 54 |
+
gamma = self._clip(gamma, norms)
|
| 55 |
+
|
| 56 |
+
# Update
|
| 57 |
+
denom = 1/norms
|
| 58 |
+
ratio = gamma * denom
|
| 59 |
+
new_p = pre + self.threshold(ratio) * c_t
|
| 60 |
+
|
| 61 |
+
# Save updated values
|
| 62 |
+
self._update_buffers(gamma, c_t, denom)
|
| 63 |
+
self.j += 1
|
| 64 |
+
return new_p
|
| 65 |
+
else:
|
| 66 |
+
return None
|
| 67 |
+
|
| 68 |
+
def _apply_spectral_norm(self, c_t):
|
| 69 |
+
u, s, vh = torch.linalg.svd(c_t, full_matrices=False)
|
| 70 |
+
spectral_norm = s.max()
|
| 71 |
+
return torch.matmul(u, vh) / spectral_norm
|
| 72 |
+
|
| 73 |
+
def incre_counters(self):
|
| 74 |
+
self.t += 1
|
| 75 |
+
self.j = 0
|
| 76 |
+
|
| 77 |
+
@torch.no_grad()
|
| 78 |
+
def _mars_norm(self, tensor):
|
| 79 |
+
return torch.sum(torch.abs(tensor), dim=tuple(range(1, tensor.dim())), keepdim=True) + 1e-8
|
| 80 |
+
|
| 81 |
+
@torch.no_grad()
|
| 82 |
+
def _clip(self, constraint, norms):
|
| 83 |
+
return torch.nn.functional.hardtanh(constraint, 1e-8, norms.max())
|
| 84 |
+
|
| 85 |
+
@torch.no_grad()
|
| 86 |
+
def _dot(self, tensor1, tensor2, scale=1):
|
| 87 |
+
return torch.sum(torch.mul(tensor1, tensor2), dim=tuple(range(1, tensor1.dim())), keepdim=True) * scale
|
| 88 |
+
|
| 89 |
+
@torch.no_grad()
|
| 90 |
+
def _adam_util(self, prev, grad):
|
| 91 |
+
first_moment = self.beta1 * self.first_m_gamma[self.j] + (1 - self.beta1) * grad
|
| 92 |
+
second_moment = self.beta2 * self.second_m_gamma[self.j] + (1 - self.beta2) * grad**2
|
| 93 |
+
self.first_m_gamma[self.j] = first_moment
|
| 94 |
+
self.second_m_gamma[self.j] = second_moment
|
| 95 |
+
first_moment = first_moment / (1 - self.beta1**self.t)
|
| 96 |
+
second_moment = second_moment / (1 - self.beta2**self.t)
|
| 97 |
+
return prev - self.mu * first_moment / (torch.sqrt(second_moment) + 1e-8)
|
| 98 |
+
|
| 99 |
+
def _update_buffers(self, gamma, c_t=None, denom=None):
|
| 100 |
+
if c_t is None:
|
| 101 |
+
self.first_m_gamma.append(0.0)
|
| 102 |
+
self.second_m_gamma.append(0.0)
|
| 103 |
+
self.gamma_buff.append(gamma)
|
| 104 |
+
self.prev_c.append(0.0)
|
| 105 |
+
self.prev_scale.append(0.0)
|
| 106 |
+
else:
|
| 107 |
+
self.gamma_buff[self.j] = gamma
|
| 108 |
+
self.prev_c[self.j] = c_t
|
| 109 |
+
self.prev_scale[self.j] = denom
|
| 110 |
+
|
| 111 |
+
class SGDP(Optimizer):
|
| 112 |
+
def __init__(self, params, lr=required, momentum=0, dampening=0,
|
| 113 |
+
weight_decay=0, nesterov=False, k=1.0, exclude_set = {}):
|
| 114 |
+
if lr is not required and lr < 0.0:
|
| 115 |
+
raise ValueError("Invalid learning rate: {}".format(lr))
|
| 116 |
+
if momentum < 0.0:
|
| 117 |
+
raise ValueError("Invalid momentum value: {}".format(momentum))
|
| 118 |
+
if weight_decay < 0.0:
|
| 119 |
+
raise ValueError("Invalid weight_decay value: {}".format(weight_decay))
|
| 120 |
+
|
| 121 |
+
defaults = dict(lr=lr, momentum=momentum, dampening=dampening,
|
| 122 |
+
weight_decay=weight_decay, nesterov=nesterov)
|
| 123 |
+
if nesterov and (momentum <= 0 or dampening != 0):
|
| 124 |
+
raise ValueError("Nesterov momentum requires a momentum and zero dampening")
|
| 125 |
+
super(SGDP, self).__init__(params, defaults)
|
| 126 |
+
self.first_iter_flag = False
|
| 127 |
+
|
| 128 |
+
# initialize FTP
|
| 129 |
+
self.ftp = FTP(k, exclude_set=exclude_set)
|
| 130 |
+
|
| 131 |
+
|
| 132 |
+
def __setstate__(self, state):
|
| 133 |
+
super(SGDP, self).__setstate__(state)
|
| 134 |
+
for group in self.param_groups:
|
| 135 |
+
group.setdefault('nesterov', False)
|
| 136 |
+
|
| 137 |
+
@torch.no_grad()
|
| 138 |
+
def step(self, closure=None):
|
| 139 |
+
"""Performs a single optimization step.
|
| 140 |
+
|
| 141 |
+
Arguments:
|
| 142 |
+
closure (callable, optional): A closure that reevaluates the model
|
| 143 |
+
and returns the loss.
|
| 144 |
+
"""
|
| 145 |
+
loss = None
|
| 146 |
+
if closure is not None:
|
| 147 |
+
with torch.enable_grad():
|
| 148 |
+
loss = closure()
|
| 149 |
+
for group in self.param_groups:
|
| 150 |
+
weight_decay = group['weight_decay']
|
| 151 |
+
momentum = group['momentum']
|
| 152 |
+
dampening = group['dampening']
|
| 153 |
+
nesterov = group['nesterov']
|
| 154 |
+
|
| 155 |
+
for p, name, pre in zip(group['params'],group['name'],group['pre']):
|
| 156 |
+
if p.grad is None:
|
| 157 |
+
continue
|
| 158 |
+
d_p = p.grad
|
| 159 |
+
if weight_decay != 0:
|
| 160 |
+
d_p = d_p.add(p, alpha=weight_decay)
|
| 161 |
+
if momentum != 0:
|
| 162 |
+
param_state = self.state[p]
|
| 163 |
+
if 'momentum_buffer' not in param_state:
|
| 164 |
+
buf = param_state['momentum_buffer'] = torch.clone(d_p).detach()
|
| 165 |
+
else:
|
| 166 |
+
buf = param_state['momentum_buffer']
|
| 167 |
+
buf.mul_(momentum).add_(d_p, alpha=1 - dampening)
|
| 168 |
+
if nesterov:
|
| 169 |
+
d_p = d_p.add(buf, alpha=momentum)
|
| 170 |
+
else:
|
| 171 |
+
d_p = buf
|
| 172 |
+
|
| 173 |
+
# FTP step
|
| 174 |
+
d_p = group['lr']*d_p
|
| 175 |
+
new_p = self.ftp.step(name,p,pre,d_p)
|
| 176 |
+
if new_p is not None :
|
| 177 |
+
p.copy_(new_p)
|
| 178 |
+
else:
|
| 179 |
+
p.add_(d_p, alpha=-1)
|
| 180 |
+
# FTP increment internal counters
|
| 181 |
+
self.ftp.incre_counters()
|
| 182 |
+
return loss
|
| 183 |
+
|
| 184 |
+
|
| 185 |
+
class AdamP(Optimizer):
|
| 186 |
+
def __init__(self, params, lr=1e-3, betas=(0.9, 0.999), eps=1e-8,
|
| 187 |
+
weight_decay=0, amsgrad=False, k=1.0, exclude_set={}):
|
| 188 |
+
if not 0.0 <= lr:
|
| 189 |
+
raise ValueError("Invalid learning rate: {}".format(lr))
|
| 190 |
+
if not 0.0 <= eps:
|
| 191 |
+
raise ValueError("Invalid epsilon value: {}".format(eps))
|
| 192 |
+
if not 0.0 <= betas[0] < 1.0:
|
| 193 |
+
raise ValueError("Invalid beta parameter at index 0: {}".format(betas[0]))
|
| 194 |
+
if not 0.0 <= betas[1] < 1.0:
|
| 195 |
+
raise ValueError("Invalid beta parameter at index 1: {}".format(betas[1]))
|
| 196 |
+
if not 0.0 <= weight_decay:
|
| 197 |
+
raise ValueError("Invalid weight_decay value: {}".format(weight_decay))
|
| 198 |
+
defaults = dict(lr=lr, betas=betas, eps=eps,
|
| 199 |
+
weight_decay=weight_decay, amsgrad=amsgrad)
|
| 200 |
+
super(AdamP, self).__init__(params, defaults)
|
| 201 |
+
|
| 202 |
+
# initialize FTP
|
| 203 |
+
self.ftp = FTP(k, exclude_set=exclude_set)
|
| 204 |
+
|
| 205 |
+
def __setstate__(self, state):
|
| 206 |
+
super(AdamP, self).__setstate__(state)
|
| 207 |
+
for group in self.param_groups:
|
| 208 |
+
group.setdefault('amsgrad', False)
|
| 209 |
+
|
| 210 |
+
@torch.no_grad()
|
| 211 |
+
def step(self, closure=None):
|
| 212 |
+
"""Performs a single optimization step.
|
| 213 |
+
|
| 214 |
+
Arguments:
|
| 215 |
+
closure (callable, optional): A closure that reevaluates the model
|
| 216 |
+
and returns the loss.
|
| 217 |
+
"""
|
| 218 |
+
loss = None
|
| 219 |
+
if closure is not None:
|
| 220 |
+
with torch.enable_grad():
|
| 221 |
+
loss = closure()
|
| 222 |
+
|
| 223 |
+
for group in self.param_groups:
|
| 224 |
+
params_with_grad = []
|
| 225 |
+
grads = []
|
| 226 |
+
exp_avgs = []
|
| 227 |
+
exp_avg_sqs = []
|
| 228 |
+
max_exp_avg_sqs = []
|
| 229 |
+
state_steps = []
|
| 230 |
+
|
| 231 |
+
for p in group['params']:
|
| 232 |
+
if p.grad is not None:
|
| 233 |
+
params_with_grad.append(p)
|
| 234 |
+
if p.grad.is_sparse:
|
| 235 |
+
raise RuntimeError('Adam does not support sparse gradients, please consider SparseAdam instead')
|
| 236 |
+
grads.append(p.grad)
|
| 237 |
+
|
| 238 |
+
state = self.state[p]
|
| 239 |
+
# Lazy state initialization
|
| 240 |
+
if len(state) == 0:
|
| 241 |
+
state['step'] = 0
|
| 242 |
+
# Exponential moving average of gradient values
|
| 243 |
+
state['exp_avg'] = torch.zeros_like(p, memory_format=torch.preserve_format)
|
| 244 |
+
# Exponential moving average of squared gradient values
|
| 245 |
+
state['exp_avg_sq'] = torch.zeros_like(p, memory_format=torch.preserve_format)
|
| 246 |
+
if group['amsgrad']:
|
| 247 |
+
# Maintains max of all exp. moving avg. of sq. grad. values
|
| 248 |
+
state['max_exp_avg_sq'] = torch.zeros_like(p, memory_format=torch.preserve_format)
|
| 249 |
+
|
| 250 |
+
exp_avgs.append(state['exp_avg'])
|
| 251 |
+
exp_avg_sqs.append(state['exp_avg_sq'])
|
| 252 |
+
|
| 253 |
+
if group['amsgrad']:
|
| 254 |
+
max_exp_avg_sqs.append(state['max_exp_avg_sq'])
|
| 255 |
+
|
| 256 |
+
# update the steps for each param group update
|
| 257 |
+
state['step'] += 1
|
| 258 |
+
# record the step after step update
|
| 259 |
+
state_steps.append(state['step'])
|
| 260 |
+
|
| 261 |
+
beta1, beta2 = group['betas']
|
| 262 |
+
self.adam(group,
|
| 263 |
+
exp_avgs,
|
| 264 |
+
exp_avg_sqs,
|
| 265 |
+
max_exp_avg_sqs,
|
| 266 |
+
state_steps,
|
| 267 |
+
group['amsgrad'],
|
| 268 |
+
beta1,
|
| 269 |
+
beta2,
|
| 270 |
+
group['lr'],
|
| 271 |
+
group['weight_decay'],
|
| 272 |
+
group['eps']
|
| 273 |
+
)
|
| 274 |
+
# FTP increment internal counters
|
| 275 |
+
self.ftp.incre_counters()
|
| 276 |
+
return loss
|
| 277 |
+
|
| 278 |
+
def adam(self, group,
|
| 279 |
+
exp_avgs,
|
| 280 |
+
exp_avg_sqs,
|
| 281 |
+
max_exp_avg_sqs,
|
| 282 |
+
state_steps,
|
| 283 |
+
amsgrad: bool,
|
| 284 |
+
beta1: float,
|
| 285 |
+
beta2: float,
|
| 286 |
+
lr: float,
|
| 287 |
+
weight_decay: float,
|
| 288 |
+
eps: float):
|
| 289 |
+
|
| 290 |
+
i = 0
|
| 291 |
+
for param, name, pre in zip(group['params'],group['name'],group['pre']):
|
| 292 |
+
if param.grad is None:
|
| 293 |
+
continue
|
| 294 |
+
grad = param.grad
|
| 295 |
+
exp_avg = exp_avgs[i]
|
| 296 |
+
exp_avg_sq = exp_avg_sqs[i]
|
| 297 |
+
step = state_steps[i]
|
| 298 |
+
if amsgrad:
|
| 299 |
+
max_exp_avg_sq = max_exp_avg_sqs[i]
|
| 300 |
+
|
| 301 |
+
bias_correction1 = 1 - beta1 ** step
|
| 302 |
+
bias_correction2 = 1 - beta2 ** step
|
| 303 |
+
|
| 304 |
+
# Decay the first and second moment running average coefficient
|
| 305 |
+
exp_avg.mul_(beta1).add_(grad, alpha=1 - beta1)
|
| 306 |
+
exp_avg_sq.mul_(beta2).addcmul_(grad, grad, value=1 - beta2)
|
| 307 |
+
if amsgrad:
|
| 308 |
+
# Maintains the maximum of all 2nd moment running avg. till now
|
| 309 |
+
torch.maximum(max_exp_avg_sq, exp_avg_sq, out=max_exp_avg_sq)
|
| 310 |
+
# Use the max. for normalizing running avg. of gradient
|
| 311 |
+
denom = (max_exp_avg_sq.sqrt() / math.sqrt(bias_correction2)).add_(eps)
|
| 312 |
+
else:
|
| 313 |
+
denom = (exp_avg_sq.sqrt() / math.sqrt(bias_correction2)).add_(eps)
|
| 314 |
+
|
| 315 |
+
step_size = lr / bias_correction1
|
| 316 |
+
i += 1
|
| 317 |
+
|
| 318 |
+
# FTP step
|
| 319 |
+
d_p = step_size * exp_avg/denom + lr * weight_decay * param
|
| 320 |
+
new_p = self.ftp.step(name,param,pre,d_p)
|
| 321 |
+
if new_p is None :
|
| 322 |
+
new_p = param - d_p
|
| 323 |
+
param.copy_(new_p)
|
FTP_update2.py
ADDED
|
@@ -0,0 +1,340 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
from torch.optim.optimizer import Optimizer, required
|
| 3 |
+
import copy
|
| 4 |
+
import math
|
| 5 |
+
|
| 6 |
+
class FTP(object):
|
| 7 |
+
def __init__(self, k=1.0, exclude_set={}):
|
| 8 |
+
self.exclude_set = exclude_set
|
| 9 |
+
self.threshold = torch.nn.Hardtanh(0,1)
|
| 10 |
+
self.k = k # Gradient annealing factor
|
| 11 |
+
self.j = 0 # Buffer counter
|
| 12 |
+
|
| 13 |
+
# AdamUtil parameters
|
| 14 |
+
self.mu = 1e-2
|
| 15 |
+
self.beta1 = 0.9
|
| 16 |
+
self.beta2 = 0.999
|
| 17 |
+
self.t = 1
|
| 18 |
+
|
| 19 |
+
# Buffers
|
| 20 |
+
self.gamma_buff = []
|
| 21 |
+
self.first_m_gamma = []
|
| 22 |
+
self.second_m_gamma = []
|
| 23 |
+
self.prev_c = []
|
| 24 |
+
self.prev_scale = []
|
| 25 |
+
|
| 26 |
+
@torch.no_grad()
|
| 27 |
+
def step(self, name, curr, pre, d_p):
|
| 28 |
+
if curr.requires_grad and name not in self.exclude_set: # Exclude set includes those params that not be updated
|
| 29 |
+
c_t = (curr - d_p) - pre # Compute potential new param
|
| 30 |
+
norms = self._mars_norm(c_t)
|
| 31 |
+
|
| 32 |
+
# New: Apply spectral normalization to c_t
|
| 33 |
+
c_t = self._apply_spectral_norm(c_t)
|
| 34 |
+
|
| 35 |
+
if self.t == 1:
|
| 36 |
+
gamma = torch.tensor(1e-8, device=norms.device)
|
| 37 |
+
self._update_buffers(gamma)
|
| 38 |
+
else:
|
| 39 |
+
# Get previous values
|
| 40 |
+
gamma_prev = self.gamma_buff[self.j]
|
| 41 |
+
c_prev = self.prev_c[self.j]
|
| 42 |
+
scale_prev = self.prev_scale[self.j]
|
| 43 |
+
|
| 44 |
+
# Calculate gradient for gamma
|
| 45 |
+
gamma_grad = torch.sum(self._dot(curr.grad, c_prev, scale=scale_prev))
|
| 46 |
+
|
| 47 |
+
# Anneal positive gradient
|
| 48 |
+
if gamma_grad > 0:
|
| 49 |
+
gamma_grad = gamma_grad * self.k
|
| 50 |
+
|
| 51 |
+
gamma = self._adam_util(gamma_prev, gamma_grad)
|
| 52 |
+
|
| 53 |
+
# Clip gamma
|
| 54 |
+
gamma = self._clip(gamma, norms)
|
| 55 |
+
|
| 56 |
+
# Update
|
| 57 |
+
denom = 1/norms
|
| 58 |
+
ratio = gamma * denom
|
| 59 |
+
new_p = pre + self.threshold(ratio) * c_t
|
| 60 |
+
|
| 61 |
+
# Save updated values
|
| 62 |
+
self._update_buffers(gamma, c_t, denom)
|
| 63 |
+
self.j += 1
|
| 64 |
+
return new_p
|
| 65 |
+
else:
|
| 66 |
+
return None
|
| 67 |
+
|
| 68 |
+
def _apply_spectral_norm(self, param, tau=1.0):
|
| 69 |
+
|
| 70 |
+
if param.ndim >= 2: # Only for 2D matrices
|
| 71 |
+
u, s, vh = torch.linalg.svd(param, full_matrices=False)
|
| 72 |
+
s_clipped = torch.clamp(s, max=tau) # Clip singular values
|
| 73 |
+
return torch.matmul(u, torch.matmul(torch.diag(s_clipped), vh))
|
| 74 |
+
return param # No projection for 1D parameters
|
| 75 |
+
|
| 76 |
+
# def _apply_spectral_norm(self, param, tau=1.0):
|
| 77 |
+
# if param.ndim >= 2: # For 2D or higher-dimensional tensors
|
| 78 |
+
# original_shape = param.shape # Save original shape
|
| 79 |
+
# reshaped_param = param.view(original_shape[0], -1) # Flatten into 2D
|
| 80 |
+
|
| 81 |
+
# u, s, vh = torch.linalg.svd(reshaped_param, full_matrices=False)
|
| 82 |
+
# s_clipped = torch.clamp(s, max=tau) # Clip singular values
|
| 83 |
+
|
| 84 |
+
# # Reconstruct the tensor with clipped singular values
|
| 85 |
+
# normalized_param = torch.matmul(u, torch.matmul(torch.diag(s_clipped), vh))
|
| 86 |
+
# return normalized_param.view(original_shape) # Reshape back to original
|
| 87 |
+
# return param # No projection for 1D parameters
|
| 88 |
+
|
| 89 |
+
|
| 90 |
+
def incre_counters(self):
|
| 91 |
+
self.t += 1
|
| 92 |
+
self.j = 0
|
| 93 |
+
|
| 94 |
+
@torch.no_grad()
|
| 95 |
+
def _mars_norm(self, tensor):
|
| 96 |
+
return torch.sum(torch.abs(tensor), dim=tuple(range(1, tensor.dim())), keepdim=True) + 1e-8
|
| 97 |
+
|
| 98 |
+
@torch.no_grad()
|
| 99 |
+
def _clip(self, constraint, norms):
|
| 100 |
+
return torch.nn.functional.hardtanh(constraint, 1e-8, norms.max())
|
| 101 |
+
|
| 102 |
+
@torch.no_grad()
|
| 103 |
+
def _dot(self, tensor1, tensor2, scale=1):
|
| 104 |
+
return torch.sum(torch.mul(tensor1, tensor2), dim=tuple(range(1, tensor1.dim())), keepdim=True) * scale
|
| 105 |
+
|
| 106 |
+
@torch.no_grad()
|
| 107 |
+
def _adam_util(self, prev, grad):
|
| 108 |
+
first_moment = self.beta1 * self.first_m_gamma[self.j] + (1 - self.beta1) * grad
|
| 109 |
+
second_moment = self.beta2 * self.second_m_gamma[self.j] + (1 - self.beta2) * grad**2
|
| 110 |
+
self.first_m_gamma[self.j] = first_moment
|
| 111 |
+
self.second_m_gamma[self.j] = second_moment
|
| 112 |
+
first_moment = first_moment / (1 - self.beta1**self.t)
|
| 113 |
+
second_moment = second_moment / (1 - self.beta2**self.t)
|
| 114 |
+
return prev - self.mu * first_moment / (torch.sqrt(second_moment) + 1e-8)
|
| 115 |
+
|
| 116 |
+
def _update_buffers(self, gamma, c_t=None, denom=None):
|
| 117 |
+
if c_t is None:
|
| 118 |
+
self.first_m_gamma.append(0.0)
|
| 119 |
+
self.second_m_gamma.append(0.0)
|
| 120 |
+
self.gamma_buff.append(gamma)
|
| 121 |
+
self.prev_c.append(0.0)
|
| 122 |
+
self.prev_scale.append(0.0)
|
| 123 |
+
else:
|
| 124 |
+
self.gamma_buff[self.j] = gamma
|
| 125 |
+
self.prev_c[self.j] = c_t
|
| 126 |
+
self.prev_scale[self.j] = denom
|
| 127 |
+
|
| 128 |
+
class SGDP(Optimizer):
|
| 129 |
+
def __init__(self, params, lr=required, momentum=0, dampening=0,
|
| 130 |
+
weight_decay=0, nesterov=False, k=1.0, exclude_set = {}):
|
| 131 |
+
if lr is not required and lr < 0.0:
|
| 132 |
+
raise ValueError("Invalid learning rate: {}".format(lr))
|
| 133 |
+
if momentum < 0.0:
|
| 134 |
+
raise ValueError("Invalid momentum value: {}".format(momentum))
|
| 135 |
+
if weight_decay < 0.0:
|
| 136 |
+
raise ValueError("Invalid weight_decay value: {}".format(weight_decay))
|
| 137 |
+
|
| 138 |
+
defaults = dict(lr=lr, momentum=momentum, dampening=dampening,
|
| 139 |
+
weight_decay=weight_decay, nesterov=nesterov)
|
| 140 |
+
if nesterov and (momentum <= 0 or dampening != 0):
|
| 141 |
+
raise ValueError("Nesterov momentum requires a momentum and zero dampening")
|
| 142 |
+
super(SGDP, self).__init__(params, defaults)
|
| 143 |
+
self.first_iter_flag = False
|
| 144 |
+
|
| 145 |
+
# initialize FTP
|
| 146 |
+
self.ftp = FTP(k, exclude_set=exclude_set)
|
| 147 |
+
|
| 148 |
+
|
| 149 |
+
def __setstate__(self, state):
|
| 150 |
+
super(SGDP, self).__setstate__(state)
|
| 151 |
+
for group in self.param_groups:
|
| 152 |
+
group.setdefault('nesterov', False)
|
| 153 |
+
|
| 154 |
+
@torch.no_grad()
|
| 155 |
+
def step(self, closure=None):
|
| 156 |
+
"""Performs a single optimization step.
|
| 157 |
+
|
| 158 |
+
Arguments:
|
| 159 |
+
closure (callable, optional): A closure that reevaluates the model
|
| 160 |
+
and returns the loss.
|
| 161 |
+
"""
|
| 162 |
+
loss = None
|
| 163 |
+
if closure is not None:
|
| 164 |
+
with torch.enable_grad():
|
| 165 |
+
loss = closure()
|
| 166 |
+
for group in self.param_groups:
|
| 167 |
+
weight_decay = group['weight_decay']
|
| 168 |
+
momentum = group['momentum']
|
| 169 |
+
dampening = group['dampening']
|
| 170 |
+
nesterov = group['nesterov']
|
| 171 |
+
|
| 172 |
+
for p, name, pre in zip(group['params'],group['name'],group['pre']):
|
| 173 |
+
if p.grad is None:
|
| 174 |
+
continue
|
| 175 |
+
d_p = p.grad
|
| 176 |
+
if weight_decay != 0:
|
| 177 |
+
d_p = d_p.add(p, alpha=weight_decay)
|
| 178 |
+
if momentum != 0:
|
| 179 |
+
param_state = self.state[p]
|
| 180 |
+
if 'momentum_buffer' not in param_state:
|
| 181 |
+
buf = param_state['momentum_buffer'] = torch.clone(d_p).detach()
|
| 182 |
+
else:
|
| 183 |
+
buf = param_state['momentum_buffer']
|
| 184 |
+
buf.mul_(momentum).add_(d_p, alpha=1 - dampening)
|
| 185 |
+
if nesterov:
|
| 186 |
+
d_p = d_p.add(buf, alpha=momentum)
|
| 187 |
+
else:
|
| 188 |
+
d_p = buf
|
| 189 |
+
|
| 190 |
+
# FTP step
|
| 191 |
+
d_p = group['lr']*d_p
|
| 192 |
+
new_p = self.ftp.step(name,p,pre,d_p)
|
| 193 |
+
if new_p is not None :
|
| 194 |
+
p.copy_(new_p)
|
| 195 |
+
else:
|
| 196 |
+
p.add_(d_p, alpha=-1)
|
| 197 |
+
# FTP increment internal counters
|
| 198 |
+
self.ftp.incre_counters()
|
| 199 |
+
return loss
|
| 200 |
+
|
| 201 |
+
|
| 202 |
+
class AdamP(Optimizer):
|
| 203 |
+
def __init__(self, params, lr=1e-3, betas=(0.9, 0.999), eps=1e-8,
|
| 204 |
+
weight_decay=0, amsgrad=False, k=1.0, exclude_set={}):
|
| 205 |
+
if not 0.0 <= lr:
|
| 206 |
+
raise ValueError("Invalid learning rate: {}".format(lr))
|
| 207 |
+
if not 0.0 <= eps:
|
| 208 |
+
raise ValueError("Invalid epsilon value: {}".format(eps))
|
| 209 |
+
if not 0.0 <= betas[0] < 1.0:
|
| 210 |
+
raise ValueError("Invalid beta parameter at index 0: {}".format(betas[0]))
|
| 211 |
+
if not 0.0 <= betas[1] < 1.0:
|
| 212 |
+
raise ValueError("Invalid beta parameter at index 1: {}".format(betas[1]))
|
| 213 |
+
if not 0.0 <= weight_decay:
|
| 214 |
+
raise ValueError("Invalid weight_decay value: {}".format(weight_decay))
|
| 215 |
+
defaults = dict(lr=lr, betas=betas, eps=eps,
|
| 216 |
+
weight_decay=weight_decay, amsgrad=amsgrad)
|
| 217 |
+
super(AdamP, self).__init__(params, defaults)
|
| 218 |
+
|
| 219 |
+
# initialize FTP
|
| 220 |
+
self.ftp = FTP(k, exclude_set=exclude_set)
|
| 221 |
+
|
| 222 |
+
def __setstate__(self, state):
|
| 223 |
+
super(AdamP, self).__setstate__(state)
|
| 224 |
+
for group in self.param_groups:
|
| 225 |
+
group.setdefault('amsgrad', False)
|
| 226 |
+
|
| 227 |
+
@torch.no_grad()
|
| 228 |
+
def step(self, closure=None):
|
| 229 |
+
"""Performs a single optimization step.
|
| 230 |
+
|
| 231 |
+
Arguments:
|
| 232 |
+
closure (callable, optional): A closure that reevaluates the model
|
| 233 |
+
and returns the loss.
|
| 234 |
+
"""
|
| 235 |
+
loss = None
|
| 236 |
+
if closure is not None:
|
| 237 |
+
with torch.enable_grad():
|
| 238 |
+
loss = closure()
|
| 239 |
+
|
| 240 |
+
for group in self.param_groups:
|
| 241 |
+
params_with_grad = []
|
| 242 |
+
grads = []
|
| 243 |
+
exp_avgs = []
|
| 244 |
+
exp_avg_sqs = []
|
| 245 |
+
max_exp_avg_sqs = []
|
| 246 |
+
state_steps = []
|
| 247 |
+
|
| 248 |
+
for p in group['params']:
|
| 249 |
+
if p.grad is not None:
|
| 250 |
+
params_with_grad.append(p)
|
| 251 |
+
if p.grad.is_sparse:
|
| 252 |
+
raise RuntimeError('Adam does not support sparse gradients, please consider SparseAdam instead')
|
| 253 |
+
grads.append(p.grad)
|
| 254 |
+
|
| 255 |
+
state = self.state[p]
|
| 256 |
+
# Lazy state initialization
|
| 257 |
+
if len(state) == 0:
|
| 258 |
+
state['step'] = 0
|
| 259 |
+
# Exponential moving average of gradient values
|
| 260 |
+
state['exp_avg'] = torch.zeros_like(p, memory_format=torch.preserve_format)
|
| 261 |
+
# Exponential moving average of squared gradient values
|
| 262 |
+
state['exp_avg_sq'] = torch.zeros_like(p, memory_format=torch.preserve_format)
|
| 263 |
+
if group['amsgrad']:
|
| 264 |
+
# Maintains max of all exp. moving avg. of sq. grad. values
|
| 265 |
+
state['max_exp_avg_sq'] = torch.zeros_like(p, memory_format=torch.preserve_format)
|
| 266 |
+
|
| 267 |
+
exp_avgs.append(state['exp_avg'])
|
| 268 |
+
exp_avg_sqs.append(state['exp_avg_sq'])
|
| 269 |
+
|
| 270 |
+
if group['amsgrad']:
|
| 271 |
+
max_exp_avg_sqs.append(state['max_exp_avg_sq'])
|
| 272 |
+
|
| 273 |
+
# update the steps for each param group update
|
| 274 |
+
state['step'] += 1
|
| 275 |
+
# record the step after step update
|
| 276 |
+
state_steps.append(state['step'])
|
| 277 |
+
|
| 278 |
+
beta1, beta2 = group['betas']
|
| 279 |
+
self.adam(group,
|
| 280 |
+
exp_avgs,
|
| 281 |
+
exp_avg_sqs,
|
| 282 |
+
max_exp_avg_sqs,
|
| 283 |
+
state_steps,
|
| 284 |
+
group['amsgrad'],
|
| 285 |
+
beta1,
|
| 286 |
+
beta2,
|
| 287 |
+
group['lr'],
|
| 288 |
+
group['weight_decay'],
|
| 289 |
+
group['eps']
|
| 290 |
+
)
|
| 291 |
+
# FTP increment internal counters
|
| 292 |
+
self.ftp.incre_counters()
|
| 293 |
+
return loss
|
| 294 |
+
|
| 295 |
+
def adam(self, group,
|
| 296 |
+
exp_avgs,
|
| 297 |
+
exp_avg_sqs,
|
| 298 |
+
max_exp_avg_sqs,
|
| 299 |
+
state_steps,
|
| 300 |
+
amsgrad: bool,
|
| 301 |
+
beta1: float,
|
| 302 |
+
beta2: float,
|
| 303 |
+
lr: float,
|
| 304 |
+
weight_decay: float,
|
| 305 |
+
eps: float):
|
| 306 |
+
|
| 307 |
+
i = 0
|
| 308 |
+
for param, name, pre in zip(group['params'],group['name'],group['pre']):
|
| 309 |
+
if param.grad is None:
|
| 310 |
+
continue
|
| 311 |
+
grad = param.grad
|
| 312 |
+
exp_avg = exp_avgs[i]
|
| 313 |
+
exp_avg_sq = exp_avg_sqs[i]
|
| 314 |
+
step = state_steps[i]
|
| 315 |
+
if amsgrad:
|
| 316 |
+
max_exp_avg_sq = max_exp_avg_sqs[i]
|
| 317 |
+
|
| 318 |
+
bias_correction1 = 1 - beta1 ** step
|
| 319 |
+
bias_correction2 = 1 - beta2 ** step
|
| 320 |
+
|
| 321 |
+
# Decay the first and second moment running average coefficient
|
| 322 |
+
exp_avg.mul_(beta1).add_(grad, alpha=1 - beta1)
|
| 323 |
+
exp_avg_sq.mul_(beta2).addcmul_(grad, grad, value=1 - beta2)
|
| 324 |
+
if amsgrad:
|
| 325 |
+
# Maintains the maximum of all 2nd moment running avg. till now
|
| 326 |
+
torch.maximum(max_exp_avg_sq, exp_avg_sq, out=max_exp_avg_sq)
|
| 327 |
+
# Use the max. for normalizing running avg. of gradient
|
| 328 |
+
denom = (max_exp_avg_sq.sqrt() / math.sqrt(bias_correction2)).add_(eps)
|
| 329 |
+
else:
|
| 330 |
+
denom = (exp_avg_sq.sqrt() / math.sqrt(bias_correction2)).add_(eps)
|
| 331 |
+
|
| 332 |
+
step_size = lr / bias_correction1
|
| 333 |
+
i += 1
|
| 334 |
+
|
| 335 |
+
# FTP step
|
| 336 |
+
d_p = step_size * exp_avg/denom + lr * weight_decay * param
|
| 337 |
+
new_p = self.ftp.step(name,param,pre,d_p)
|
| 338 |
+
if new_p is None :
|
| 339 |
+
new_p = param - d_p
|
| 340 |
+
param.copy_(new_p)
|
FTP_update3.py
ADDED
|
@@ -0,0 +1,329 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
from torch.optim.optimizer import Optimizer, required
|
| 3 |
+
import copy
|
| 4 |
+
import math
|
| 5 |
+
|
| 6 |
+
class FTP(object):
|
| 7 |
+
def __init__(self, k=1.0, exclude_set={}):
|
| 8 |
+
self.exclude_set = exclude_set
|
| 9 |
+
self.threshold = torch.nn.Hardtanh(0,1)
|
| 10 |
+
self.k = k # Gradient annealing factor
|
| 11 |
+
self.j = 0 # Buffer counter
|
| 12 |
+
|
| 13 |
+
# AdamUtil parameters
|
| 14 |
+
self.mu = 1e-2
|
| 15 |
+
self.beta1 = 0.9
|
| 16 |
+
self.beta2 = 0.999
|
| 17 |
+
self.t = 1
|
| 18 |
+
|
| 19 |
+
# Buffers
|
| 20 |
+
self.gamma_buff = []
|
| 21 |
+
self.first_m_gamma = []
|
| 22 |
+
self.second_m_gamma = []
|
| 23 |
+
self.prev_c = []
|
| 24 |
+
self.prev_scale = []
|
| 25 |
+
|
| 26 |
+
@torch.no_grad()
|
| 27 |
+
def step(self, name, curr, pre, d_p):
|
| 28 |
+
if curr.requires_grad and name not in self.exclude_set: # Exclude set includes those params that not be updated
|
| 29 |
+
c_t = (curr - d_p) - pre # Compute potential new param
|
| 30 |
+
norms = self._mars_norm(c_t, tau=1.0)
|
| 31 |
+
|
| 32 |
+
# New: Apply spectral normalization to c_t
|
| 33 |
+
c_t = self._apply_spectral_norm(c_t)
|
| 34 |
+
|
| 35 |
+
if self.t == 1:
|
| 36 |
+
gamma = torch.tensor(1e-8, device=norms.device)
|
| 37 |
+
self._update_buffers(gamma)
|
| 38 |
+
else:
|
| 39 |
+
# Get previous values
|
| 40 |
+
gamma_prev = self.gamma_buff[self.j]
|
| 41 |
+
c_prev = self.prev_c[self.j]
|
| 42 |
+
scale_prev = self.prev_scale[self.j]
|
| 43 |
+
|
| 44 |
+
# Calculate gradient for gamma
|
| 45 |
+
gamma_grad = torch.sum(self._dot(curr.grad, c_prev, scale=scale_prev))
|
| 46 |
+
|
| 47 |
+
# Anneal positive gradient
|
| 48 |
+
if gamma_grad > 0:
|
| 49 |
+
gamma_grad = gamma_grad * self.k
|
| 50 |
+
|
| 51 |
+
gamma = self._adam_util(gamma_prev, gamma_grad)
|
| 52 |
+
|
| 53 |
+
# Clip gamma
|
| 54 |
+
gamma = self._clip(gamma, norms)
|
| 55 |
+
|
| 56 |
+
# Update
|
| 57 |
+
denom = 1/norms
|
| 58 |
+
ratio = gamma * denom
|
| 59 |
+
new_p = pre + self.threshold(ratio) * c_t
|
| 60 |
+
|
| 61 |
+
new_p = self._apply_spectral_norm(new_p, tau=2.0) # tau is the max allowed spectral norm
|
| 62 |
+
|
| 63 |
+
# Save updated values
|
| 64 |
+
self._update_buffers(gamma, c_t, denom)
|
| 65 |
+
self.j += 1
|
| 66 |
+
return new_p
|
| 67 |
+
else:
|
| 68 |
+
return None
|
| 69 |
+
|
| 70 |
+
def _apply_spectral_norm(self, param, tau=1.0):
|
| 71 |
+
|
| 72 |
+
if param.ndim >= 2: # Only for 2D matrices
|
| 73 |
+
u, s, vh = torch.linalg.svd(param, full_matrices=False)
|
| 74 |
+
s_clipped = torch.clamp(s, max=tau) # Clip singular values
|
| 75 |
+
return torch.matmul(u, torch.matmul(torch.diag(s_clipped), vh))
|
| 76 |
+
return param # No projection for 1D parameters
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
def incre_counters(self):
|
| 80 |
+
self.t += 1
|
| 81 |
+
self.j = 0
|
| 82 |
+
|
| 83 |
+
@torch.no_grad()
|
| 84 |
+
def _mars_norm(self, tensor):
|
| 85 |
+
return torch.sum(torch.abs(tensor), dim=tuple(range(1, tensor.dim())), keepdim=True) + 1e-8
|
| 86 |
+
|
| 87 |
+
@torch.no_grad()
|
| 88 |
+
def _clip(self, constraint, norms):
|
| 89 |
+
return torch.nn.functional.hardtanh(constraint, 1e-8, norms.max())
|
| 90 |
+
|
| 91 |
+
@torch.no_grad()
|
| 92 |
+
def _dot(self, tensor1, tensor2, scale=1):
|
| 93 |
+
return torch.sum(torch.mul(tensor1, tensor2), dim=tuple(range(1, tensor1.dim())), keepdim=True) * scale
|
| 94 |
+
|
| 95 |
+
@torch.no_grad()
|
| 96 |
+
def _adam_util(self, prev, grad):
|
| 97 |
+
first_moment = self.beta1 * self.first_m_gamma[self.j] + (1 - self.beta1) * grad
|
| 98 |
+
second_moment = self.beta2 * self.second_m_gamma[self.j] + (1 - self.beta2) * grad**2
|
| 99 |
+
self.first_m_gamma[self.j] = first_moment
|
| 100 |
+
self.second_m_gamma[self.j] = second_moment
|
| 101 |
+
first_moment = first_moment / (1 - self.beta1**self.t)
|
| 102 |
+
second_moment = second_moment / (1 - self.beta2**self.t)
|
| 103 |
+
return prev - self.mu * first_moment / (torch.sqrt(second_moment) + 1e-8)
|
| 104 |
+
|
| 105 |
+
def _update_buffers(self, gamma, c_t=None, denom=None):
|
| 106 |
+
if c_t is None:
|
| 107 |
+
self.first_m_gamma.append(0.0)
|
| 108 |
+
self.second_m_gamma.append(0.0)
|
| 109 |
+
self.gamma_buff.append(gamma)
|
| 110 |
+
self.prev_c.append(0.0)
|
| 111 |
+
self.prev_scale.append(0.0)
|
| 112 |
+
else:
|
| 113 |
+
self.gamma_buff[self.j] = gamma
|
| 114 |
+
self.prev_c[self.j] = c_t
|
| 115 |
+
self.prev_scale[self.j] = denom
|
| 116 |
+
|
| 117 |
+
class SGDP(Optimizer):
|
| 118 |
+
def __init__(self, params, lr=required, momentum=0, dampening=0,
|
| 119 |
+
weight_decay=0, nesterov=False, k=1.0, exclude_set = {}):
|
| 120 |
+
if lr is not required and lr < 0.0:
|
| 121 |
+
raise ValueError("Invalid learning rate: {}".format(lr))
|
| 122 |
+
if momentum < 0.0:
|
| 123 |
+
raise ValueError("Invalid momentum value: {}".format(momentum))
|
| 124 |
+
if weight_decay < 0.0:
|
| 125 |
+
raise ValueError("Invalid weight_decay value: {}".format(weight_decay))
|
| 126 |
+
|
| 127 |
+
defaults = dict(lr=lr, momentum=momentum, dampening=dampening,
|
| 128 |
+
weight_decay=weight_decay, nesterov=nesterov)
|
| 129 |
+
if nesterov and (momentum <= 0 or dampening != 0):
|
| 130 |
+
raise ValueError("Nesterov momentum requires a momentum and zero dampening")
|
| 131 |
+
super(SGDP, self).__init__(params, defaults)
|
| 132 |
+
self.first_iter_flag = False
|
| 133 |
+
|
| 134 |
+
# initialize FTP
|
| 135 |
+
self.ftp = FTP(k, exclude_set=exclude_set)
|
| 136 |
+
|
| 137 |
+
|
| 138 |
+
def __setstate__(self, state):
|
| 139 |
+
super(SGDP, self).__setstate__(state)
|
| 140 |
+
for group in self.param_groups:
|
| 141 |
+
group.setdefault('nesterov', False)
|
| 142 |
+
|
| 143 |
+
@torch.no_grad()
|
| 144 |
+
def step(self, closure=None):
|
| 145 |
+
"""Performs a single optimization step.
|
| 146 |
+
|
| 147 |
+
Arguments:
|
| 148 |
+
closure (callable, optional): A closure that reevaluates the model
|
| 149 |
+
and returns the loss.
|
| 150 |
+
"""
|
| 151 |
+
loss = None
|
| 152 |
+
if closure is not None:
|
| 153 |
+
with torch.enable_grad():
|
| 154 |
+
loss = closure()
|
| 155 |
+
for group in self.param_groups:
|
| 156 |
+
weight_decay = group['weight_decay']
|
| 157 |
+
momentum = group['momentum']
|
| 158 |
+
dampening = group['dampening']
|
| 159 |
+
nesterov = group['nesterov']
|
| 160 |
+
|
| 161 |
+
for p, name, pre in zip(group['params'],group['name'],group['pre']):
|
| 162 |
+
if p.grad is None:
|
| 163 |
+
continue
|
| 164 |
+
d_p = p.grad
|
| 165 |
+
if weight_decay != 0:
|
| 166 |
+
d_p = d_p.add(p, alpha=weight_decay)
|
| 167 |
+
if momentum != 0:
|
| 168 |
+
param_state = self.state[p]
|
| 169 |
+
if 'momentum_buffer' not in param_state:
|
| 170 |
+
buf = param_state['momentum_buffer'] = torch.clone(d_p).detach()
|
| 171 |
+
else:
|
| 172 |
+
buf = param_state['momentum_buffer']
|
| 173 |
+
buf.mul_(momentum).add_(d_p, alpha=1 - dampening)
|
| 174 |
+
if nesterov:
|
| 175 |
+
d_p = d_p.add(buf, alpha=momentum)
|
| 176 |
+
else:
|
| 177 |
+
d_p = buf
|
| 178 |
+
|
| 179 |
+
# FTP step
|
| 180 |
+
d_p = group['lr']*d_p
|
| 181 |
+
new_p = self.ftp.step(name,p,pre,d_p)
|
| 182 |
+
if new_p is not None :
|
| 183 |
+
p.copy_(new_p)
|
| 184 |
+
else:
|
| 185 |
+
p.add_(d_p, alpha=-1)
|
| 186 |
+
# FTP increment internal counters
|
| 187 |
+
self.ftp.incre_counters()
|
| 188 |
+
return loss
|
| 189 |
+
|
| 190 |
+
|
| 191 |
+
class AdamP(Optimizer):
|
| 192 |
+
def __init__(self, params, lr=1e-3, betas=(0.9, 0.999), eps=1e-8,
|
| 193 |
+
weight_decay=0, amsgrad=False, k=1.0, exclude_set={}):
|
| 194 |
+
if not 0.0 <= lr:
|
| 195 |
+
raise ValueError("Invalid learning rate: {}".format(lr))
|
| 196 |
+
if not 0.0 <= eps:
|
| 197 |
+
raise ValueError("Invalid epsilon value: {}".format(eps))
|
| 198 |
+
if not 0.0 <= betas[0] < 1.0:
|
| 199 |
+
raise ValueError("Invalid beta parameter at index 0: {}".format(betas[0]))
|
| 200 |
+
if not 0.0 <= betas[1] < 1.0:
|
| 201 |
+
raise ValueError("Invalid beta parameter at index 1: {}".format(betas[1]))
|
| 202 |
+
if not 0.0 <= weight_decay:
|
| 203 |
+
raise ValueError("Invalid weight_decay value: {}".format(weight_decay))
|
| 204 |
+
defaults = dict(lr=lr, betas=betas, eps=eps,
|
| 205 |
+
weight_decay=weight_decay, amsgrad=amsgrad)
|
| 206 |
+
super(AdamP, self).__init__(params, defaults)
|
| 207 |
+
|
| 208 |
+
# initialize FTP
|
| 209 |
+
self.ftp = FTP(k, exclude_set=exclude_set)
|
| 210 |
+
|
| 211 |
+
def __setstate__(self, state):
|
| 212 |
+
super(AdamP, self).__setstate__(state)
|
| 213 |
+
for group in self.param_groups:
|
| 214 |
+
group.setdefault('amsgrad', False)
|
| 215 |
+
|
| 216 |
+
@torch.no_grad()
|
| 217 |
+
def step(self, closure=None):
|
| 218 |
+
"""Performs a single optimization step.
|
| 219 |
+
|
| 220 |
+
Arguments:
|
| 221 |
+
closure (callable, optional): A closure that reevaluates the model
|
| 222 |
+
and returns the loss.
|
| 223 |
+
"""
|
| 224 |
+
loss = None
|
| 225 |
+
if closure is not None:
|
| 226 |
+
with torch.enable_grad():
|
| 227 |
+
loss = closure()
|
| 228 |
+
|
| 229 |
+
for group in self.param_groups:
|
| 230 |
+
params_with_grad = []
|
| 231 |
+
grads = []
|
| 232 |
+
exp_avgs = []
|
| 233 |
+
exp_avg_sqs = []
|
| 234 |
+
max_exp_avg_sqs = []
|
| 235 |
+
state_steps = []
|
| 236 |
+
|
| 237 |
+
for p in group['params']:
|
| 238 |
+
if p.grad is not None:
|
| 239 |
+
params_with_grad.append(p)
|
| 240 |
+
if p.grad.is_sparse:
|
| 241 |
+
raise RuntimeError('Adam does not support sparse gradients, please consider SparseAdam instead')
|
| 242 |
+
grads.append(p.grad)
|
| 243 |
+
|
| 244 |
+
state = self.state[p]
|
| 245 |
+
# Lazy state initialization
|
| 246 |
+
if len(state) == 0:
|
| 247 |
+
state['step'] = 0
|
| 248 |
+
# Exponential moving average of gradient values
|
| 249 |
+
state['exp_avg'] = torch.zeros_like(p, memory_format=torch.preserve_format)
|
| 250 |
+
# Exponential moving average of squared gradient values
|
| 251 |
+
state['exp_avg_sq'] = torch.zeros_like(p, memory_format=torch.preserve_format)
|
| 252 |
+
if group['amsgrad']:
|
| 253 |
+
# Maintains max of all exp. moving avg. of sq. grad. values
|
| 254 |
+
state['max_exp_avg_sq'] = torch.zeros_like(p, memory_format=torch.preserve_format)
|
| 255 |
+
|
| 256 |
+
exp_avgs.append(state['exp_avg'])
|
| 257 |
+
exp_avg_sqs.append(state['exp_avg_sq'])
|
| 258 |
+
|
| 259 |
+
if group['amsgrad']:
|
| 260 |
+
max_exp_avg_sqs.append(state['max_exp_avg_sq'])
|
| 261 |
+
|
| 262 |
+
# update the steps for each param group update
|
| 263 |
+
state['step'] += 1
|
| 264 |
+
# record the step after step update
|
| 265 |
+
state_steps.append(state['step'])
|
| 266 |
+
|
| 267 |
+
beta1, beta2 = group['betas']
|
| 268 |
+
self.adam(group,
|
| 269 |
+
exp_avgs,
|
| 270 |
+
exp_avg_sqs,
|
| 271 |
+
max_exp_avg_sqs,
|
| 272 |
+
state_steps,
|
| 273 |
+
group['amsgrad'],
|
| 274 |
+
beta1,
|
| 275 |
+
beta2,
|
| 276 |
+
group['lr'],
|
| 277 |
+
group['weight_decay'],
|
| 278 |
+
group['eps']
|
| 279 |
+
)
|
| 280 |
+
# FTP increment internal counters
|
| 281 |
+
self.ftp.incre_counters()
|
| 282 |
+
return loss
|
| 283 |
+
|
| 284 |
+
def adam(self, group,
|
| 285 |
+
exp_avgs,
|
| 286 |
+
exp_avg_sqs,
|
| 287 |
+
max_exp_avg_sqs,
|
| 288 |
+
state_steps,
|
| 289 |
+
amsgrad: bool,
|
| 290 |
+
beta1: float,
|
| 291 |
+
beta2: float,
|
| 292 |
+
lr: float,
|
| 293 |
+
weight_decay: float,
|
| 294 |
+
eps: float):
|
| 295 |
+
|
| 296 |
+
i = 0
|
| 297 |
+
for param, name, pre in zip(group['params'],group['name'],group['pre']):
|
| 298 |
+
if param.grad is None:
|
| 299 |
+
continue
|
| 300 |
+
grad = param.grad
|
| 301 |
+
exp_avg = exp_avgs[i]
|
| 302 |
+
exp_avg_sq = exp_avg_sqs[i]
|
| 303 |
+
step = state_steps[i]
|
| 304 |
+
if amsgrad:
|
| 305 |
+
max_exp_avg_sq = max_exp_avg_sqs[i]
|
| 306 |
+
|
| 307 |
+
bias_correction1 = 1 - beta1 ** step
|
| 308 |
+
bias_correction2 = 1 - beta2 ** step
|
| 309 |
+
|
| 310 |
+
# Decay the first and second moment running average coefficient
|
| 311 |
+
exp_avg.mul_(beta1).add_(grad, alpha=1 - beta1)
|
| 312 |
+
exp_avg_sq.mul_(beta2).addcmul_(grad, grad, value=1 - beta2)
|
| 313 |
+
if amsgrad:
|
| 314 |
+
# Maintains the maximum of all 2nd moment running avg. till now
|
| 315 |
+
torch.maximum(max_exp_avg_sq, exp_avg_sq, out=max_exp_avg_sq)
|
| 316 |
+
# Use the max. for normalizing running avg. of gradient
|
| 317 |
+
denom = (max_exp_avg_sq.sqrt() / math.sqrt(bias_correction2)).add_(eps)
|
| 318 |
+
else:
|
| 319 |
+
denom = (exp_avg_sq.sqrt() / math.sqrt(bias_correction2)).add_(eps)
|
| 320 |
+
|
| 321 |
+
step_size = lr / bias_correction1
|
| 322 |
+
i += 1
|
| 323 |
+
|
| 324 |
+
# FTP step
|
| 325 |
+
d_p = step_size * exp_avg/denom + lr * weight_decay * param
|
| 326 |
+
new_p = self.ftp.step(name,param,pre,d_p)
|
| 327 |
+
if new_p is None :
|
| 328 |
+
new_p = param - d_p
|
| 329 |
+
param.copy_(new_p)
|
FTP_update4.py
ADDED
|
@@ -0,0 +1,329 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
from torch.optim.optimizer import Optimizer, required
|
| 3 |
+
import copy
|
| 4 |
+
import math
|
| 5 |
+
|
| 6 |
+
class FTP(object):
|
| 7 |
+
def __init__(self, k=1.0, exclude_set={}):
|
| 8 |
+
self.exclude_set = exclude_set
|
| 9 |
+
self.threshold = torch.nn.Hardtanh(0,1)
|
| 10 |
+
self.k = k # Gradient annealing factor
|
| 11 |
+
self.j = 0 # Buffer counter
|
| 12 |
+
|
| 13 |
+
# AdamUtil parameters
|
| 14 |
+
self.mu = 1e-2
|
| 15 |
+
self.beta1 = 0.9
|
| 16 |
+
self.beta2 = 0.999
|
| 17 |
+
self.t = 1
|
| 18 |
+
|
| 19 |
+
# Buffers
|
| 20 |
+
self.gamma_buff = []
|
| 21 |
+
self.first_m_gamma = []
|
| 22 |
+
self.second_m_gamma = []
|
| 23 |
+
self.prev_c = []
|
| 24 |
+
self.prev_scale = []
|
| 25 |
+
|
| 26 |
+
@torch.no_grad()
|
| 27 |
+
def step(self, name, curr, pre, d_p):
|
| 28 |
+
if curr.requires_grad and name not in self.exclude_set: # Exclude set includes those params that not be updated
|
| 29 |
+
c_t = (curr - d_p) - pre # Compute potential new param
|
| 30 |
+
norms = self._mars_norm(c_t, tau=1.0)
|
| 31 |
+
|
| 32 |
+
# New: Apply spectral normalization to c_t
|
| 33 |
+
c_t = self._apply_spectral_norm(c_t)
|
| 34 |
+
|
| 35 |
+
if self.t == 1:
|
| 36 |
+
gamma = torch.tensor(1e-8, device=norms.device)
|
| 37 |
+
self._update_buffers(gamma)
|
| 38 |
+
else:
|
| 39 |
+
# Get previous values
|
| 40 |
+
gamma_prev = self.gamma_buff[self.j]
|
| 41 |
+
c_prev = self.prev_c[self.j]
|
| 42 |
+
scale_prev = self.prev_scale[self.j]
|
| 43 |
+
|
| 44 |
+
# Calculate gradient for gamma
|
| 45 |
+
gamma_grad = torch.sum(self._dot(curr.grad, c_prev, scale=scale_prev))
|
| 46 |
+
|
| 47 |
+
# Anneal positive gradient
|
| 48 |
+
if gamma_grad > 0:
|
| 49 |
+
gamma_grad = gamma_grad * self.k
|
| 50 |
+
|
| 51 |
+
gamma = self._adam_util(gamma_prev, gamma_grad)
|
| 52 |
+
|
| 53 |
+
# Clip gamma
|
| 54 |
+
gamma = self._clip(gamma, norms)
|
| 55 |
+
|
| 56 |
+
# Update
|
| 57 |
+
denom = 1/norms
|
| 58 |
+
ratio = gamma * denom
|
| 59 |
+
new_p = pre + self.threshold(ratio) * c_t
|
| 60 |
+
|
| 61 |
+
new_p = self._apply_spectral_norm(new_p, tau=2.0) # tau is the max allowed spectral norm
|
| 62 |
+
|
| 63 |
+
# Save updated values
|
| 64 |
+
self._update_buffers(gamma, c_t, denom)
|
| 65 |
+
self.j += 1
|
| 66 |
+
return new_p
|
| 67 |
+
else:
|
| 68 |
+
return None
|
| 69 |
+
|
| 70 |
+
def _apply_spectral_norm(self, param, tau=1.0):
|
| 71 |
+
|
| 72 |
+
if param.ndim >= 2: # Only for 2D matrices
|
| 73 |
+
u, s, vh = torch.linalg.svd(param, full_matrices=False)
|
| 74 |
+
s_clipped = torch.clamp(s, max=tau) # Clip singular values
|
| 75 |
+
return torch.matmul(u, torch.matmul(torch.diag(s_clipped), vh))
|
| 76 |
+
return param # No projection for 1D parameters
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
def incre_counters(self):
|
| 80 |
+
self.t += 1
|
| 81 |
+
self.j = 0
|
| 82 |
+
|
| 83 |
+
@torch.no_grad()
|
| 84 |
+
def _mars_norm(self, tensor):
|
| 85 |
+
return torch.sum(torch.abs(tensor), dim=tuple(range(1, tensor.dim())), keepdim=True) + 1e-8
|
| 86 |
+
|
| 87 |
+
@torch.no_grad()
|
| 88 |
+
def _clip(self, constraint, norms):
|
| 89 |
+
return torch.nn.functional.hardtanh(constraint, 1e-8, norms.max())
|
| 90 |
+
|
| 91 |
+
@torch.no_grad()
|
| 92 |
+
def _dot(self, tensor1, tensor2, scale=1):
|
| 93 |
+
return torch.sum(torch.mul(tensor1, tensor2), dim=tuple(range(1, tensor1.dim())), keepdim=True) * scale
|
| 94 |
+
|
| 95 |
+
@torch.no_grad()
|
| 96 |
+
def _adam_util(self, prev, grad):
|
| 97 |
+
first_moment = self.beta1 * self.first_m_gamma[self.j] + (1 - self.beta1) * grad
|
| 98 |
+
second_moment = self.beta2 * self.second_m_gamma[self.j] + (1 - self.beta2) * grad**2
|
| 99 |
+
self.first_m_gamma[self.j] = first_moment
|
| 100 |
+
self.second_m_gamma[self.j] = second_moment
|
| 101 |
+
first_moment = first_moment / (1 - self.beta1**self.t)
|
| 102 |
+
second_moment = second_moment / (1 - self.beta2**self.t)
|
| 103 |
+
return prev - self.mu * first_moment / (torch.sqrt(second_moment) + 1e-8)
|
| 104 |
+
|
| 105 |
+
def _update_buffers(self, gamma, c_t=None, denom=None):
|
| 106 |
+
if c_t is None:
|
| 107 |
+
self.first_m_gamma.append(0.0)
|
| 108 |
+
self.second_m_gamma.append(0.0)
|
| 109 |
+
self.gamma_buff.append(gamma)
|
| 110 |
+
self.prev_c.append(0.0)
|
| 111 |
+
self.prev_scale.append(0.0)
|
| 112 |
+
else:
|
| 113 |
+
self.gamma_buff[self.j] = gamma
|
| 114 |
+
self.prev_c[self.j] = c_t
|
| 115 |
+
self.prev_scale[self.j] = denom
|
| 116 |
+
|
| 117 |
+
class SGDP(Optimizer):
|
| 118 |
+
def __init__(self, params, lr=required, momentum=0, dampening=0,
|
| 119 |
+
weight_decay=0, nesterov=False, k=1.0, exclude_set = {}):
|
| 120 |
+
if lr is not required and lr < 0.0:
|
| 121 |
+
raise ValueError("Invalid learning rate: {}".format(lr))
|
| 122 |
+
if momentum < 0.0:
|
| 123 |
+
raise ValueError("Invalid momentum value: {}".format(momentum))
|
| 124 |
+
if weight_decay < 0.0:
|
| 125 |
+
raise ValueError("Invalid weight_decay value: {}".format(weight_decay))
|
| 126 |
+
|
| 127 |
+
defaults = dict(lr=lr, momentum=momentum, dampening=dampening,
|
| 128 |
+
weight_decay=weight_decay, nesterov=nesterov)
|
| 129 |
+
if nesterov and (momentum <= 0 or dampening != 0):
|
| 130 |
+
raise ValueError("Nesterov momentum requires a momentum and zero dampening")
|
| 131 |
+
super(SGDP, self).__init__(params, defaults)
|
| 132 |
+
self.first_iter_flag = False
|
| 133 |
+
|
| 134 |
+
# initialize FTP
|
| 135 |
+
self.ftp = FTP(k, exclude_set=exclude_set)
|
| 136 |
+
|
| 137 |
+
|
| 138 |
+
def __setstate__(self, state):
|
| 139 |
+
super(SGDP, self).__setstate__(state)
|
| 140 |
+
for group in self.param_groups:
|
| 141 |
+
group.setdefault('nesterov', False)
|
| 142 |
+
|
| 143 |
+
@torch.no_grad()
|
| 144 |
+
def step(self, closure=None):
|
| 145 |
+
"""Performs a single optimization step.
|
| 146 |
+
|
| 147 |
+
Arguments:
|
| 148 |
+
closure (callable, optional): A closure that reevaluates the model
|
| 149 |
+
and returns the loss.
|
| 150 |
+
"""
|
| 151 |
+
loss = None
|
| 152 |
+
if closure is not None:
|
| 153 |
+
with torch.enable_grad():
|
| 154 |
+
loss = closure()
|
| 155 |
+
for group in self.param_groups:
|
| 156 |
+
weight_decay = group['weight_decay']
|
| 157 |
+
momentum = group['momentum']
|
| 158 |
+
dampening = group['dampening']
|
| 159 |
+
nesterov = group['nesterov']
|
| 160 |
+
|
| 161 |
+
for p, name, pre in zip(group['params'],group['name'],group['pre']):
|
| 162 |
+
if p.grad is None:
|
| 163 |
+
continue
|
| 164 |
+
d_p = p.grad
|
| 165 |
+
if weight_decay != 0:
|
| 166 |
+
d_p = d_p.add(p, alpha=weight_decay)
|
| 167 |
+
if momentum != 0:
|
| 168 |
+
param_state = self.state[p]
|
| 169 |
+
if 'momentum_buffer' not in param_state:
|
| 170 |
+
buf = param_state['momentum_buffer'] = torch.clone(d_p).detach()
|
| 171 |
+
else:
|
| 172 |
+
buf = param_state['momentum_buffer']
|
| 173 |
+
buf.mul_(momentum).add_(d_p, alpha=1 - dampening)
|
| 174 |
+
if nesterov:
|
| 175 |
+
d_p = d_p.add(buf, alpha=momentum)
|
| 176 |
+
else:
|
| 177 |
+
d_p = buf
|
| 178 |
+
|
| 179 |
+
# FTP step
|
| 180 |
+
d_p = group['lr']*d_p
|
| 181 |
+
new_p = self.ftp.step(name,p,pre,d_p)
|
| 182 |
+
if new_p is not None :
|
| 183 |
+
p.copy_(new_p)
|
| 184 |
+
else:
|
| 185 |
+
p.add_(d_p, alpha=-1)
|
| 186 |
+
# FTP increment internal counters
|
| 187 |
+
self.ftp.incre_counters()
|
| 188 |
+
return loss
|
| 189 |
+
|
| 190 |
+
|
| 191 |
+
class AdamP(Optimizer):
|
| 192 |
+
def __init__(self, params, lr=1e-3, betas=(0.9, 0.999), eps=1e-8,
|
| 193 |
+
weight_decay=0, amsgrad=False, k=1.0, exclude_set={}):
|
| 194 |
+
if not 0.0 <= lr:
|
| 195 |
+
raise ValueError("Invalid learning rate: {}".format(lr))
|
| 196 |
+
if not 0.0 <= eps:
|
| 197 |
+
raise ValueError("Invalid epsilon value: {}".format(eps))
|
| 198 |
+
if not 0.0 <= betas[0] < 1.0:
|
| 199 |
+
raise ValueError("Invalid beta parameter at index 0: {}".format(betas[0]))
|
| 200 |
+
if not 0.0 <= betas[1] < 1.0:
|
| 201 |
+
raise ValueError("Invalid beta parameter at index 1: {}".format(betas[1]))
|
| 202 |
+
if not 0.0 <= weight_decay:
|
| 203 |
+
raise ValueError("Invalid weight_decay value: {}".format(weight_decay))
|
| 204 |
+
defaults = dict(lr=lr, betas=betas, eps=eps,
|
| 205 |
+
weight_decay=weight_decay, amsgrad=amsgrad)
|
| 206 |
+
super(AdamP, self).__init__(params, defaults)
|
| 207 |
+
|
| 208 |
+
# initialize FTP
|
| 209 |
+
self.ftp = FTP(k, exclude_set=exclude_set)
|
| 210 |
+
|
| 211 |
+
def __setstate__(self, state):
|
| 212 |
+
super(AdamP, self).__setstate__(state)
|
| 213 |
+
for group in self.param_groups:
|
| 214 |
+
group.setdefault('amsgrad', False)
|
| 215 |
+
|
| 216 |
+
@torch.no_grad()
|
| 217 |
+
def step(self, closure=None):
|
| 218 |
+
"""Performs a single optimization step.
|
| 219 |
+
|
| 220 |
+
Arguments:
|
| 221 |
+
closure (callable, optional): A closure that reevaluates the model
|
| 222 |
+
and returns the loss.
|
| 223 |
+
"""
|
| 224 |
+
loss = None
|
| 225 |
+
if closure is not None:
|
| 226 |
+
with torch.enable_grad():
|
| 227 |
+
loss = closure()
|
| 228 |
+
|
| 229 |
+
for group in self.param_groups:
|
| 230 |
+
params_with_grad = []
|
| 231 |
+
grads = []
|
| 232 |
+
exp_avgs = []
|
| 233 |
+
exp_avg_sqs = []
|
| 234 |
+
max_exp_avg_sqs = []
|
| 235 |
+
state_steps = []
|
| 236 |
+
|
| 237 |
+
for p in group['params']:
|
| 238 |
+
if p.grad is not None:
|
| 239 |
+
params_with_grad.append(p)
|
| 240 |
+
if p.grad.is_sparse:
|
| 241 |
+
raise RuntimeError('Adam does not support sparse gradients, please consider SparseAdam instead')
|
| 242 |
+
grads.append(p.grad)
|
| 243 |
+
|
| 244 |
+
state = self.state[p]
|
| 245 |
+
# Lazy state initialization
|
| 246 |
+
if len(state) == 0:
|
| 247 |
+
state['step'] = 0
|
| 248 |
+
# Exponential moving average of gradient values
|
| 249 |
+
state['exp_avg'] = torch.zeros_like(p, memory_format=torch.preserve_format)
|
| 250 |
+
# Exponential moving average of squared gradient values
|
| 251 |
+
state['exp_avg_sq'] = torch.zeros_like(p, memory_format=torch.preserve_format)
|
| 252 |
+
if group['amsgrad']:
|
| 253 |
+
# Maintains max of all exp. moving avg. of sq. grad. values
|
| 254 |
+
state['max_exp_avg_sq'] = torch.zeros_like(p, memory_format=torch.preserve_format)
|
| 255 |
+
|
| 256 |
+
exp_avgs.append(state['exp_avg'])
|
| 257 |
+
exp_avg_sqs.append(state['exp_avg_sq'])
|
| 258 |
+
|
| 259 |
+
if group['amsgrad']:
|
| 260 |
+
max_exp_avg_sqs.append(state['max_exp_avg_sq'])
|
| 261 |
+
|
| 262 |
+
# update the steps for each param group update
|
| 263 |
+
state['step'] += 1
|
| 264 |
+
# record the step after step update
|
| 265 |
+
state_steps.append(state['step'])
|
| 266 |
+
|
| 267 |
+
beta1, beta2 = group['betas']
|
| 268 |
+
self.adam(group,
|
| 269 |
+
exp_avgs,
|
| 270 |
+
exp_avg_sqs,
|
| 271 |
+
max_exp_avg_sqs,
|
| 272 |
+
state_steps,
|
| 273 |
+
group['amsgrad'],
|
| 274 |
+
beta1,
|
| 275 |
+
beta2,
|
| 276 |
+
group['lr'],
|
| 277 |
+
group['weight_decay'],
|
| 278 |
+
group['eps']
|
| 279 |
+
)
|
| 280 |
+
# FTP increment internal counters
|
| 281 |
+
self.ftp.incre_counters()
|
| 282 |
+
return loss
|
| 283 |
+
|
| 284 |
+
def adam(self, group,
|
| 285 |
+
exp_avgs,
|
| 286 |
+
exp_avg_sqs,
|
| 287 |
+
max_exp_avg_sqs,
|
| 288 |
+
state_steps,
|
| 289 |
+
amsgrad: bool,
|
| 290 |
+
beta1: float,
|
| 291 |
+
beta2: float,
|
| 292 |
+
lr: float,
|
| 293 |
+
weight_decay: float,
|
| 294 |
+
eps: float):
|
| 295 |
+
|
| 296 |
+
i = 0
|
| 297 |
+
for param, name, pre in zip(group['params'],group['name'],group['pre']):
|
| 298 |
+
if param.grad is None:
|
| 299 |
+
continue
|
| 300 |
+
grad = param.grad
|
| 301 |
+
exp_avg = exp_avgs[i]
|
| 302 |
+
exp_avg_sq = exp_avg_sqs[i]
|
| 303 |
+
step = state_steps[i]
|
| 304 |
+
if amsgrad:
|
| 305 |
+
max_exp_avg_sq = max_exp_avg_sqs[i]
|
| 306 |
+
|
| 307 |
+
bias_correction1 = 1 - beta1 ** step
|
| 308 |
+
bias_correction2 = 1 - beta2 ** step
|
| 309 |
+
|
| 310 |
+
# Decay the first and second moment running average coefficient
|
| 311 |
+
exp_avg.mul_(beta1).add_(grad, alpha=1 - beta1)
|
| 312 |
+
exp_avg_sq.mul_(beta2).addcmul_(grad, grad, value=1 - beta2)
|
| 313 |
+
if amsgrad:
|
| 314 |
+
# Maintains the maximum of all 2nd moment running avg. till now
|
| 315 |
+
torch.maximum(max_exp_avg_sq, exp_avg_sq, out=max_exp_avg_sq)
|
| 316 |
+
# Use the max. for normalizing running avg. of gradient
|
| 317 |
+
denom = (max_exp_avg_sq.sqrt() / math.sqrt(bias_correction2)).add_(eps)
|
| 318 |
+
else:
|
| 319 |
+
denom = (exp_avg_sq.sqrt() / math.sqrt(bias_correction2)).add_(eps)
|
| 320 |
+
|
| 321 |
+
step_size = lr / bias_correction1
|
| 322 |
+
i += 1
|
| 323 |
+
|
| 324 |
+
# FTP step
|
| 325 |
+
d_p = step_size * exp_avg/denom + lr * weight_decay * param
|
| 326 |
+
new_p = self.ftp.step(name,param,pre,d_p)
|
| 327 |
+
if new_p is None :
|
| 328 |
+
new_p = param - d_p
|
| 329 |
+
param.copy_(new_p)
|
README.md
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 💥 Mission: Impossible Language Models 💥
|
| 2 |
+
|
| 3 |
+
This is the code repository for the paper "[Mission: Impossible Language Models](https://arxiv.org/abs/2401.06416)".
|
| 4 |
+
|
| 5 |
+
If you use our code, please cite our paper:
|
| 6 |
+
|
| 7 |
+
```
|
| 8 |
+
@misc{kallini2024mission,
|
| 9 |
+
title={Mission: Impossible Language Models},
|
| 10 |
+
author={Julie Kallini and Isabel Papadimitriou and Richard Futrell and Kyle Mahowald and Christopher Potts},
|
| 11 |
+
year={2024},
|
| 12 |
+
eprint={2401.06416},
|
| 13 |
+
archivePrefix={arXiv},
|
| 14 |
+
primaryClass={cs.CL}
|
| 15 |
+
}
|
| 16 |
+
```
|
| 17 |
+
|
| 18 |
+
This repository contains the code necessary to fully replicate our paper, including the scripts to create impossible language datasets, train GPT-2 models, and run all experiments. We also include the notebooks to generate the result graphs in our paper.
|
| 19 |
+
|
| 20 |
+
Let's get started!
|
| 21 |
+
|
| 22 |
+
## Setup
|
| 23 |
+
|
| 24 |
+
First, clone the repo and install dependencies:
|
| 25 |
+
|
| 26 |
+
```
|
| 27 |
+
git clone https://github.com/jkallini/mission-impossible-language-models.git
|
| 28 |
+
cd mission-impossible-language-models
|
| 29 |
+
pip install -r requirements.txt
|
| 30 |
+
```
|
| 31 |
+
|
| 32 |
+
## Impossible Language Dataset Creation
|
| 33 |
+
|
| 34 |
+
The scripts for creating impossible language datasets are located in the `data/` directory.
|
| 35 |
+
First, you must download a copy of the [BabyLM dataset](https://babylm.github.io/), which we use for our experiments.
|
| 36 |
+
Then, make sure to set `BABYLM_DATA_PATH` in the `utils.py` file to the path on your system where your BabyLM dataset is located.
|
| 37 |
+
|
| 38 |
+
After downloading the BabyLM dataset, you will need to tag it with morphological features and part-of-speech tags. You can use our `tag.py` script.
|
| 39 |
+
|
| 40 |
+
With the tagged data, you can easily recreate one of the impossible language datasets described in our paper. These are predefined and listed in the `PERTURBATIONS` section at the end of `utils.py`. Here is an
|
| 41 |
+
example for the PartialReverse language from the paper:
|
| 42 |
+
|
| 43 |
+
```
|
| 44 |
+
python3 perturb.py reverse_partial 100M
|
| 45 |
+
```
|
| 46 |
+
|
| 47 |
+
This will create a perturbed version of the 100M BabyLM train set. You may use `perturb.py` or `perturb.sh` to perturb multiple splits at the same time.
|
| 48 |
+
|
| 49 |
+
### Defining New Impossible Languages
|
| 50 |
+
|
| 51 |
+
You can also define your own impossible languages! They are described by four attributes:
|
| 52 |
+
|
| 53 |
+
1. `perturbation_function`: function mapping tagged sentences to sequences of GPT-2 tokens.
|
| 54 |
+
2. `affect_function`: function that determines whether an input sentences is "affected" or altered by the perturbation.
|
| 55 |
+
3. `filter_function`: function that determines whether an input sentence should be included in the final dataset.
|
| 56 |
+
4. `gpt2_tokenizer`: tokenizer used to perturb this dataset.
|
| 57 |
+
|
| 58 |
+
You can add these definitions to `utils.py`, where the existing perturbations are located.
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
## Model Training
|
| 62 |
+
|
| 63 |
+
To train GPT-2 models, we use [`mistral`](https://github.com/stanford-crfm/mistral). If you would like to train GPT-2s with `mistral` as well, please follow their steps for installation. You may download their repo anywhere on your system.
|
| 64 |
+
|
| 65 |
+
Next, make sure to change the following constants in `utils.py`:
|
| 66 |
+
- `CHECKPOINT_WRITE_PATH`: the path where your training checkpoints will be written.
|
| 67 |
+
- `CHECKPOINT_READ_PATH`: the path where you will read training checkpoints when running experiments.
|
| 68 |
+
|
| 69 |
+
Our training scripts are in the `training/` directory.
|
| 70 |
+
Once you have `mistral` installed, set `MISTRAL_PATH` to the path of your library in `prepare_training.sh`. Then, you can use this script to generate the config files that you will use to launch `mistral` training runs.
|
| 71 |
+
|
| 72 |
+
Our scripts will create the config files and move them to the location of your `mistral` directory—you will only need to launch the training run. Here's an example command to launch training for the PartialReverse language using the 100M training set with the random seed set to 41:
|
| 73 |
+
|
| 74 |
+
```
|
| 75 |
+
CUDA_VISIBLE_DEVICES=0 python3 train.py --config conf/train_reverse_partial_100M_randinit_seed41.yaml --nnodes 1 --nproc_per_node 1 --training_arguments.fp16 true --training_arguments.warmup_steps 300 --training_arguments.max_steps 3000
|
| 76 |
+
```
|
| 77 |
+
|
| 78 |
+
## Experiments
|
| 79 |
+
|
| 80 |
+
The main paper includes three experiments: perplexities, surprisals, and causal interventions. The appendix also includes a constituency probing experiment.
|
| 81 |
+
|
| 82 |
+
The scripts to run each of these experiments is separated into their own subdirectories:
|
| 83 |
+
|
| 84 |
+
1. `perplexities/`: code to run perplexity experiments. You may use `perplexities.py` or `perplexities.sh` to run experiments for multiple languages at the same time.
|
| 85 |
+
2. `hop_surprisal/`: code to run surprisal experiments for the *Hop languages, in `hop_surprisal.py`.
|
| 86 |
+
3. `hop_interventions/`: code to run interchange intervention experiments for the *Hop languages. First generate the agreement data using `create_agreement_data.py`, then run the intervention experiments using `hop_interventions.py`.
|
| 87 |
+
You will need to separately clone and install [`align-transformers`](https://github.com/frankaging/align-transformers) (recently renamed to `pyvene`) and set `PATH_TO_ALIGN_TRANSFORMERS` to the path where the library is located on your system.
|
| 88 |
+
4. `edge_probing/`: code to run constituency probing experiments. Use `get_constituency_parses.py` and `load_phrase_data.py` to prepare the test data, and use `edge_probing.py` to run the experiments.
|
| 89 |
+
|
| 90 |
+
Each directory contains python notebooks to generate the result graphs shown in the paper.
|
edge_probing/edge_probing.ipynb
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
edge_probing/edge_probing.py
ADDED
|
@@ -0,0 +1,222 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# edge_probing.py
|
| 2 |
+
# Author: Julie Kallini
|
| 3 |
+
|
| 4 |
+
# For importing utils
|
| 5 |
+
import sys
|
| 6 |
+
sys.path.append("..")
|
| 7 |
+
|
| 8 |
+
from utils import CHECKPOINT_READ_PATH, PERTURBATIONS, PAREN_MODELS, get_gpt2_tokenizer_with_markers
|
| 9 |
+
from gpt2_no_positional_encoding_model import GPT2NoPositionalEncodingLMHeadModel
|
| 10 |
+
from transformers import GPT2LMHeadModel
|
| 11 |
+
from sklearn.preprocessing import StandardScaler
|
| 12 |
+
from sklearn.linear_model import LogisticRegression
|
| 13 |
+
from sklearn.metrics import accuracy_score
|
| 14 |
+
from sklearn.model_selection import train_test_split
|
| 15 |
+
from itertools import zip_longest
|
| 16 |
+
import torch
|
| 17 |
+
import tqdm
|
| 18 |
+
import argparse
|
| 19 |
+
import pandas as pd
|
| 20 |
+
import os
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
MAX_TRAINING_STEPS = 3000
|
| 24 |
+
CHECKPOINTS = list(range(200, MAX_TRAINING_STEPS+1, 200))
|
| 25 |
+
LAYERS = [1, 3, 6, 9, 12, "Avg Last 4"]
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
def get_layer_embedding(model, token_sequences, indices, layer=None):
|
| 29 |
+
|
| 30 |
+
# Pad input token sequences
|
| 31 |
+
input_ids = zip(*zip_longest(*token_sequences,
|
| 32 |
+
fillvalue=gpt2_tokenizer.eos_token_id))
|
| 33 |
+
input_ids = torch.tensor(list(input_ids)).to(device)
|
| 34 |
+
|
| 35 |
+
# Get GPT2 model's output
|
| 36 |
+
with torch.no_grad():
|
| 37 |
+
output = model(input_ids)
|
| 38 |
+
|
| 39 |
+
# Either get the hidden state of the specified layer or
|
| 40 |
+
# get the average of the last 4 hidden states
|
| 41 |
+
if layer is not None:
|
| 42 |
+
hidden_states = output.hidden_states[layer]
|
| 43 |
+
else:
|
| 44 |
+
hidden_states = output.hidden_states[-4:]
|
| 45 |
+
hidden_states = sum(hidden_states) / 4
|
| 46 |
+
|
| 47 |
+
# Create mask using start and end indices
|
| 48 |
+
batch_size, seq_length = input_ids.shape
|
| 49 |
+
mask = torch.full((batch_size, seq_length), 0).to(device)
|
| 50 |
+
for i, (start_idx, end_idx) in enumerate(indices):
|
| 51 |
+
mask[i, start_idx:end_idx] = 1
|
| 52 |
+
|
| 53 |
+
# Mask out embeddings of tokens outside indices
|
| 54 |
+
mask_expanded = mask.unsqueeze(-1).expand(hidden_states.size())
|
| 55 |
+
hidden_states = hidden_states * mask_expanded
|
| 56 |
+
|
| 57 |
+
return hidden_states
|
| 58 |
+
|
| 59 |
+
|
| 60 |
+
def max_pooling(tensor, index_tuples):
|
| 61 |
+
pooled_results = []
|
| 62 |
+
for i, (start, end) in enumerate(index_tuples):
|
| 63 |
+
# Extracting the embeddings corresponding to the specified range
|
| 64 |
+
embeddings = tensor[i, start:end, :]
|
| 65 |
+
|
| 66 |
+
# Performing max pooling
|
| 67 |
+
max_pooled = torch.max(embeddings, dim=0)[0]
|
| 68 |
+
|
| 69 |
+
pooled_results.append(max_pooled)
|
| 70 |
+
return torch.stack(pooled_results)
|
| 71 |
+
|
| 72 |
+
|
| 73 |
+
def mean_pooling(tensor, index_tuples):
|
| 74 |
+
batch_size, seq_len, embedding_size = tensor.shape
|
| 75 |
+
output = torch.empty(batch_size, embedding_size,
|
| 76 |
+
device=tensor.device, dtype=tensor.dtype)
|
| 77 |
+
|
| 78 |
+
for i, (start, end) in enumerate(index_tuples):
|
| 79 |
+
embeddings = tensor[i, start:end, :]
|
| 80 |
+
output[i, :] = torch.mean(embeddings, dim=0)
|
| 81 |
+
|
| 82 |
+
return output
|
| 83 |
+
|
| 84 |
+
|
| 85 |
+
if __name__ == "__main__":
|
| 86 |
+
parser = argparse.ArgumentParser(
|
| 87 |
+
prog='Edge probing',
|
| 88 |
+
description='Edge probing experiments')
|
| 89 |
+
parser.add_argument('perturbation_type',
|
| 90 |
+
default='all',
|
| 91 |
+
const='all',
|
| 92 |
+
nargs='?',
|
| 93 |
+
choices=PERTURBATIONS.keys(),
|
| 94 |
+
help='Perturbation function used to transform BabyLM dataset')
|
| 95 |
+
parser.add_argument('train_set',
|
| 96 |
+
default='all',
|
| 97 |
+
const='all',
|
| 98 |
+
nargs='?',
|
| 99 |
+
choices=["100M", "10M"],
|
| 100 |
+
help='BabyLM train set')
|
| 101 |
+
parser.add_argument('random_seed', type=int, help="Random seed")
|
| 102 |
+
parser.add_argument('paren_model',
|
| 103 |
+
default='all',
|
| 104 |
+
const='all',
|
| 105 |
+
nargs='?',
|
| 106 |
+
choices=list(PAREN_MODELS.keys()) + ["randinit"],
|
| 107 |
+
help='Parenthesis model')
|
| 108 |
+
parser.add_argument('pooling_operation',
|
| 109 |
+
default='all',
|
| 110 |
+
const='all',
|
| 111 |
+
nargs='?',
|
| 112 |
+
choices=["mean", "max"],
|
| 113 |
+
help='Pooling operation to compute on embeddings')
|
| 114 |
+
parser.add_argument('-np', '--no_pos_encodings', action='store_true',
|
| 115 |
+
help="Train GPT-2 with no positional encodings")
|
| 116 |
+
|
| 117 |
+
# Get args
|
| 118 |
+
args = parser.parse_args()
|
| 119 |
+
|
| 120 |
+
if args.pooling_operation == "mean":
|
| 121 |
+
pooling_function = mean_pooling
|
| 122 |
+
elif args.pooling_operation == "max":
|
| 123 |
+
pooling_function = max_pooling
|
| 124 |
+
else:
|
| 125 |
+
raise Exception("Pooling operation undefined")
|
| 126 |
+
|
| 127 |
+
# Init tokenizer
|
| 128 |
+
gpt2_tokenizer = get_gpt2_tokenizer_with_markers([])
|
| 129 |
+
gpt2_tokenizer.pad_token = gpt2_tokenizer.eos_token
|
| 130 |
+
|
| 131 |
+
# Get path to model
|
| 132 |
+
no_pos_encodings_underscore = "_no_positional_encodings" if args.no_pos_encodings else ""
|
| 133 |
+
model = f"babylm_{args.perturbation_type}_{args.train_set}_{args.paren_model}{no_pos_encodings_underscore}_seed{args.random_seed}"
|
| 134 |
+
model_path = f"{CHECKPOINT_READ_PATH}/babylm_{args.perturbation_type}_{args.train_set}_{args.paren_model}{no_pos_encodings_underscore}/{model}/runs/{model}/checkpoint-"
|
| 135 |
+
|
| 136 |
+
# Get constituency parse data
|
| 137 |
+
if "hop" in args.perturbation_type:
|
| 138 |
+
phrase_df = pd.read_csv("phrase_data/hop_phrase_data.csv")
|
| 139 |
+
elif "reverse" in args.perturbation_type:
|
| 140 |
+
phrase_df = pd.read_csv("phrase_data/reverse_phrase_data.csv")
|
| 141 |
+
else:
|
| 142 |
+
raise Exception("Phrase data not found")
|
| 143 |
+
|
| 144 |
+
token_sequences = list(phrase_df["Sentence Tokens"])
|
| 145 |
+
if args.perturbation_type == "reverse_full":
|
| 146 |
+
indices = list(
|
| 147 |
+
zip(phrase_df["Rev Start Index"], phrase_df["Rev End Index"]))
|
| 148 |
+
else:
|
| 149 |
+
indices = list(zip(phrase_df["Start Index"], phrase_df["End Index"]))
|
| 150 |
+
labels = list(phrase_df["Category"])
|
| 151 |
+
|
| 152 |
+
BATCH_SIZE = 32
|
| 153 |
+
device = "cuda"
|
| 154 |
+
|
| 155 |
+
edge_probing_df = pd.DataFrame(LAYERS, columns=["GPT-2 Layer"])
|
| 156 |
+
for ckpt in CHECKPOINTS:
|
| 157 |
+
|
| 158 |
+
# Load model
|
| 159 |
+
if args.no_pos_encodings:
|
| 160 |
+
model = GPT2LMHeadModel.from_pretrained(
|
| 161 |
+
model_path + str(ckpt), output_hidden_states=True).to(device)
|
| 162 |
+
else:
|
| 163 |
+
model = GPT2NoPositionalEncodingLMHeadModel.from_pretrained(
|
| 164 |
+
model_path + str(ckpt), output_hidden_states=True).to(device)
|
| 165 |
+
|
| 166 |
+
layer_accuracies = []
|
| 167 |
+
for layer in LAYERS:
|
| 168 |
+
print(f"Checkpoint: {ckpt}, Layer: {layer}")
|
| 169 |
+
print("Computing span embeddings...")
|
| 170 |
+
|
| 171 |
+
# Iterate over token sequences and indices to get embeddings
|
| 172 |
+
spans = []
|
| 173 |
+
for i in tqdm.tqdm(list(range(0, len(token_sequences), BATCH_SIZE))):
|
| 174 |
+
|
| 175 |
+
tokens_batch = [[int(tok) for tok in seq.split()]
|
| 176 |
+
for seq in token_sequences[i:i+BATCH_SIZE]]
|
| 177 |
+
if args.perturbation_type == "reverse_full":
|
| 178 |
+
tokens_batch = [toks[::-1] for toks in tokens_batch]
|
| 179 |
+
|
| 180 |
+
index_batch = indices[i:i+BATCH_SIZE]
|
| 181 |
+
|
| 182 |
+
# Extract embeddings
|
| 183 |
+
if layer == "Avg Last 4":
|
| 184 |
+
embeddings = get_layer_embedding(
|
| 185 |
+
model, tokens_batch, index_batch, None)
|
| 186 |
+
else:
|
| 187 |
+
embeddings = get_layer_embedding(
|
| 188 |
+
model, tokens_batch, index_batch, layer)
|
| 189 |
+
pooled_results = pooling_function(embeddings, index_batch)
|
| 190 |
+
spans.extend(list(pooled_results))
|
| 191 |
+
|
| 192 |
+
# Get features and ground truth
|
| 193 |
+
X = torch.vstack(spans).detach().cpu().numpy()
|
| 194 |
+
y = labels
|
| 195 |
+
|
| 196 |
+
# Split the data; since we pass random seed, it
|
| 197 |
+
# will be the same split every time
|
| 198 |
+
X_train, X_test, y_train, y_test = train_test_split(
|
| 199 |
+
X, y, test_size=0.2, random_state=args.random_seed)
|
| 200 |
+
|
| 201 |
+
# Fit L2-regularized linear classifier
|
| 202 |
+
clf = LogisticRegression(max_iter=10,
|
| 203 |
+
random_state=args.random_seed).fit(X_train, y_train)
|
| 204 |
+
|
| 205 |
+
# Get probe accuracy
|
| 206 |
+
y_test_pred = clf.predict(X_test)
|
| 207 |
+
acc = accuracy_score(y_test, y_test_pred)
|
| 208 |
+
layer_accuracies.append(acc)
|
| 209 |
+
print(f"Accuracy: {acc}")
|
| 210 |
+
|
| 211 |
+
edge_probing_df[f"Accuracy (ckpt {ckpt})"] = layer_accuracies
|
| 212 |
+
|
| 213 |
+
# Write results to CSV
|
| 214 |
+
nps = '_no_pos_encodings' if args.no_pos_encodings else ''
|
| 215 |
+
directory = f"edge_probing_results/{args.perturbation_type}_{args.train_set}{nps}"
|
| 216 |
+
if not os.path.exists(directory):
|
| 217 |
+
os.makedirs(directory)
|
| 218 |
+
|
| 219 |
+
file = directory + \
|
| 220 |
+
f"/{args.paren_model}_{args.pooling_operation}_pooling_seed{args.random_seed}.csv"
|
| 221 |
+
print(f"Writing results to CSV: {file}")
|
| 222 |
+
edge_probing_df.to_csv(file)
|
edge_probing/edge_probing.sh
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/bin/sh
|
| 2 |
+
# edge_probing.sh
|
| 3 |
+
# author: Julie Kallini
|
| 4 |
+
|
| 5 |
+
echo "
|
| 6 |
+
-------------------------------------------------------------------------------
|
| 7 |
+
Arguments
|
| 8 |
+
-------------------------------------------------------------------------------
|
| 9 |
+
"
|
| 10 |
+
echo "Random seed: $1"
|
| 11 |
+
NO_POS_ENCODINGS=${2:-''}
|
| 12 |
+
echo "No pos encodings: $NO_POS_ENCODINGS"
|
| 13 |
+
|
| 14 |
+
echo "
|
| 15 |
+
-------------------------------------------------------------------------------
|
| 16 |
+
Run edge probing for each perturbation type
|
| 17 |
+
-------------------------------------------------------------------------------
|
| 18 |
+
"
|
| 19 |
+
|
| 20 |
+
COMMAND="python3 edge_probing.py hop_control 100M $1 randinit mean $NO_POS_ENCODINGS"
|
| 21 |
+
echo $COMMAND
|
| 22 |
+
eval $COMMAND
|
| 23 |
+
echo "
|
| 24 |
+
"
|
| 25 |
+
|
| 26 |
+
COMMAND="python3 edge_probing.py hop_tokens4 100M $1 randinit mean $NO_POS_ENCODINGS"
|
| 27 |
+
echo $COMMAND
|
| 28 |
+
eval $COMMAND
|
| 29 |
+
echo "
|
| 30 |
+
"
|
| 31 |
+
|
| 32 |
+
COMMAND="python3 edge_probing.py hop_words4 100M $1 randinit mean $NO_POS_ENCODINGS"
|
| 33 |
+
echo $COMMAND
|
| 34 |
+
eval $COMMAND
|
| 35 |
+
echo "
|
| 36 |
+
"
|
| 37 |
+
|
| 38 |
+
COMMAND="python3 edge_probing.py reverse_control 100M $1 randinit mean $NO_POS_ENCODINGS"
|
| 39 |
+
echo $COMMAND
|
| 40 |
+
eval $COMMAND
|
| 41 |
+
echo "
|
| 42 |
+
"
|
| 43 |
+
|
| 44 |
+
COMMAND="python3 edge_probing.py reverse_full 100M $1 randinit mean $NO_POS_ENCODINGS"
|
| 45 |
+
echo $COMMAND
|
| 46 |
+
eval $COMMAND
|
| 47 |
+
echo "
|
| 48 |
+
"
|
| 49 |
+
|
| 50 |
+
COMMAND="python3 edge_probing.py reverse_partial 100M $1 randinit mean $NO_POS_ENCODINGS"
|
| 51 |
+
echo $COMMAND
|
| 52 |
+
eval $COMMAND
|
| 53 |
+
echo "
|
| 54 |
+
"
|
| 55 |
+
|
| 56 |
+
echo "
|
| 57 |
+
-------------------------------------------------------------------------------
|
| 58 |
+
Done!
|
| 59 |
+
-------------------------------------------------------------------------------
|
| 60 |
+
"
|
gpt2_no_positional_encoding_model.py
ADDED
|
@@ -0,0 +1,427 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# gpt2_no_positional_encoding_model.py
|
| 2 |
+
# Adapted from Huggingface's transformers library
|
| 3 |
+
|
| 4 |
+
import torch
|
| 5 |
+
from transformers.models.gpt2.modeling_gpt2 import GPT2Block, GPT2PreTrainedModel
|
| 6 |
+
from transformers.modeling_outputs import CausalLMOutputWithCrossAttentions, BaseModelOutputWithPastAndCrossAttentions
|
| 7 |
+
from transformers.utils.model_parallel_utils import assert_device_map, get_device_map
|
| 8 |
+
from torch import nn
|
| 9 |
+
from torch.nn import CrossEntropyLoss
|
| 10 |
+
from typing import Optional, Tuple, Union
|
| 11 |
+
|
| 12 |
+
class GPT2NoPositionalEncodingModel(GPT2PreTrainedModel):
|
| 13 |
+
def __init__(self, config):
|
| 14 |
+
super().__init__(config)
|
| 15 |
+
|
| 16 |
+
self.embed_dim = config.hidden_size
|
| 17 |
+
|
| 18 |
+
self.wte = nn.Embedding(config.vocab_size, self.embed_dim)
|
| 19 |
+
|
| 20 |
+
self.drop = nn.Dropout(config.embd_pdrop)
|
| 21 |
+
self.h = nn.ModuleList([GPT2Block(config, layer_idx=i) for i in range(config.num_hidden_layers)])
|
| 22 |
+
self.ln_f = nn.LayerNorm(self.embed_dim, eps=config.layer_norm_epsilon)
|
| 23 |
+
|
| 24 |
+
# Model parallel
|
| 25 |
+
self.model_parallel = False
|
| 26 |
+
self.device_map = None
|
| 27 |
+
self.gradient_checkpointing = False
|
| 28 |
+
|
| 29 |
+
# Initialize weights and apply final processing
|
| 30 |
+
self.post_init()
|
| 31 |
+
|
| 32 |
+
def parallelize(self, device_map=None):
|
| 33 |
+
# Check validity of device_map
|
| 34 |
+
self.device_map = (
|
| 35 |
+
get_device_map(len(self.h), range(torch.cuda.device_count())) if device_map is None else device_map
|
| 36 |
+
)
|
| 37 |
+
assert_device_map(self.device_map, len(self.h))
|
| 38 |
+
self.model_parallel = True
|
| 39 |
+
self.first_device = "cpu" if "cpu" in self.device_map.keys() else "cuda:" + str(min(self.device_map.keys()))
|
| 40 |
+
self.last_device = "cuda:" + str(max(self.device_map.keys()))
|
| 41 |
+
self.wte = self.wte.to(self.first_device)
|
| 42 |
+
# Load onto devices
|
| 43 |
+
for k, v in self.device_map.items():
|
| 44 |
+
for block in v:
|
| 45 |
+
cuda_device = "cuda:" + str(k)
|
| 46 |
+
self.h[block] = self.h[block].to(cuda_device)
|
| 47 |
+
# ln_f to last
|
| 48 |
+
self.ln_f = self.ln_f.to(self.last_device)
|
| 49 |
+
|
| 50 |
+
def deparallelize(self):
|
| 51 |
+
self.model_parallel = False
|
| 52 |
+
self.device_map = None
|
| 53 |
+
self.first_device = "cpu"
|
| 54 |
+
self.last_device = "cpu"
|
| 55 |
+
self.wte = self.wte.to("cpu")
|
| 56 |
+
for index in range(len(self.h)):
|
| 57 |
+
self.h[index] = self.h[index].to("cpu")
|
| 58 |
+
self.ln_f = self.ln_f.to("cpu")
|
| 59 |
+
torch.cuda.empty_cache()
|
| 60 |
+
|
| 61 |
+
def get_input_embeddings(self):
|
| 62 |
+
return self.wte
|
| 63 |
+
|
| 64 |
+
def set_input_embeddings(self, new_embeddings):
|
| 65 |
+
self.wte = new_embeddings
|
| 66 |
+
|
| 67 |
+
def _prune_heads(self, heads_to_prune):
|
| 68 |
+
"""
|
| 69 |
+
Prunes heads of the model. heads_to_prune: dict of {layer_num: list of heads to prune in this layer}
|
| 70 |
+
"""
|
| 71 |
+
for layer, heads in heads_to_prune.items():
|
| 72 |
+
self.h[layer].attn.prune_heads(heads)
|
| 73 |
+
|
| 74 |
+
def forward(
|
| 75 |
+
self,
|
| 76 |
+
input_ids: Optional[torch.LongTensor] = None,
|
| 77 |
+
past_key_values: Optional[Tuple[Tuple[torch.Tensor]]] = None,
|
| 78 |
+
attention_mask: Optional[torch.FloatTensor] = None,
|
| 79 |
+
token_type_ids: Optional[torch.LongTensor] = None,
|
| 80 |
+
position_ids: Optional[torch.LongTensor] = None,
|
| 81 |
+
head_mask: Optional[torch.FloatTensor] = None,
|
| 82 |
+
inputs_embeds: Optional[torch.FloatTensor] = None,
|
| 83 |
+
encoder_hidden_states: Optional[torch.Tensor] = None,
|
| 84 |
+
encoder_attention_mask: Optional[torch.FloatTensor] = None,
|
| 85 |
+
use_cache: Optional[bool] = None,
|
| 86 |
+
output_attentions: Optional[bool] = None,
|
| 87 |
+
output_hidden_states: Optional[bool] = None,
|
| 88 |
+
return_dict: Optional[bool] = None,
|
| 89 |
+
) -> Union[Tuple, BaseModelOutputWithPastAndCrossAttentions]:
|
| 90 |
+
output_attentions = output_attentions if output_attentions is not None else self.config.output_attentions
|
| 91 |
+
output_hidden_states = (
|
| 92 |
+
output_hidden_states if output_hidden_states is not None else self.config.output_hidden_states
|
| 93 |
+
)
|
| 94 |
+
use_cache = use_cache if use_cache is not None else self.config.use_cache
|
| 95 |
+
return_dict = return_dict if return_dict is not None else self.config.use_return_dict
|
| 96 |
+
|
| 97 |
+
if input_ids is not None and inputs_embeds is not None:
|
| 98 |
+
raise ValueError("You cannot specify both input_ids and inputs_embeds at the same time")
|
| 99 |
+
elif input_ids is not None:
|
| 100 |
+
input_shape = input_ids.size()
|
| 101 |
+
input_ids = input_ids.view(-1, input_shape[-1])
|
| 102 |
+
batch_size = input_ids.shape[0]
|
| 103 |
+
elif inputs_embeds is not None:
|
| 104 |
+
input_shape = inputs_embeds.size()[:-1]
|
| 105 |
+
batch_size = inputs_embeds.shape[0]
|
| 106 |
+
else:
|
| 107 |
+
raise ValueError("You have to specify either input_ids or inputs_embeds")
|
| 108 |
+
|
| 109 |
+
device = input_ids.device if input_ids is not None else inputs_embeds.device
|
| 110 |
+
|
| 111 |
+
if token_type_ids is not None:
|
| 112 |
+
token_type_ids = token_type_ids.view(-1, input_shape[-1])
|
| 113 |
+
|
| 114 |
+
if past_key_values is None:
|
| 115 |
+
past_length = 0
|
| 116 |
+
past_key_values = tuple([None] * len(self.h))
|
| 117 |
+
else:
|
| 118 |
+
past_length = past_key_values[0][0].size(-2)
|
| 119 |
+
if position_ids is None:
|
| 120 |
+
position_ids = torch.arange(past_length, input_shape[-1] + past_length, dtype=torch.long, device=device)
|
| 121 |
+
position_ids = position_ids.unsqueeze(0)
|
| 122 |
+
|
| 123 |
+
# GPT2Attention mask.
|
| 124 |
+
if attention_mask is not None:
|
| 125 |
+
if batch_size <= 0:
|
| 126 |
+
raise ValueError("batch_size has to be defined and > 0")
|
| 127 |
+
attention_mask = attention_mask.view(batch_size, -1)
|
| 128 |
+
# We create a 3D attention mask from a 2D tensor mask.
|
| 129 |
+
# Sizes are [batch_size, 1, 1, to_seq_length]
|
| 130 |
+
# So we can broadcast to [batch_size, num_heads, from_seq_length, to_seq_length]
|
| 131 |
+
# this attention mask is more simple than the triangular masking of causal attention
|
| 132 |
+
# used in OpenAI GPT, we just need to prepare the broadcast dimension here.
|
| 133 |
+
attention_mask = attention_mask[:, None, None, :]
|
| 134 |
+
|
| 135 |
+
# Since attention_mask is 1.0 for positions we want to attend and 0.0 for
|
| 136 |
+
# masked positions, this operation will create a tensor which is 0.0 for
|
| 137 |
+
# positions we want to attend and the dtype's smallest value for masked positions.
|
| 138 |
+
# Since we are adding it to the raw scores before the softmax, this is
|
| 139 |
+
# effectively the same as removing these entirely.
|
| 140 |
+
attention_mask = attention_mask.to(dtype=self.dtype) # fp16 compatibility
|
| 141 |
+
attention_mask = (1.0 - attention_mask) * torch.finfo(self.dtype).min
|
| 142 |
+
|
| 143 |
+
# If a 2D or 3D attention mask is provided for the cross-attention
|
| 144 |
+
# we need to make broadcastable to [batch_size, num_heads, seq_length, seq_length]
|
| 145 |
+
if self.config.add_cross_attention and encoder_hidden_states is not None:
|
| 146 |
+
encoder_batch_size, encoder_sequence_length, _ = encoder_hidden_states.size()
|
| 147 |
+
encoder_hidden_shape = (encoder_batch_size, encoder_sequence_length)
|
| 148 |
+
if encoder_attention_mask is None:
|
| 149 |
+
encoder_attention_mask = torch.ones(encoder_hidden_shape, device=device)
|
| 150 |
+
encoder_attention_mask = self.invert_attention_mask(encoder_attention_mask)
|
| 151 |
+
else:
|
| 152 |
+
encoder_attention_mask = None
|
| 153 |
+
|
| 154 |
+
# Prepare head mask if needed
|
| 155 |
+
# 1.0 in head_mask indicate we keep the head
|
| 156 |
+
# attention_probs has shape bsz x n_heads x N x N
|
| 157 |
+
# head_mask has shape n_layer x batch x n_heads x N x N
|
| 158 |
+
head_mask = self.get_head_mask(head_mask, self.config.n_layer)
|
| 159 |
+
|
| 160 |
+
if inputs_embeds is None:
|
| 161 |
+
inputs_embeds = self.wte(input_ids)
|
| 162 |
+
hidden_states = inputs_embeds
|
| 163 |
+
|
| 164 |
+
if token_type_ids is not None:
|
| 165 |
+
token_type_embeds = self.wte(token_type_ids)
|
| 166 |
+
hidden_states = hidden_states + token_type_embeds
|
| 167 |
+
|
| 168 |
+
hidden_states = self.drop(hidden_states)
|
| 169 |
+
|
| 170 |
+
output_shape = (-1,) + input_shape[1:] + (hidden_states.size(-1),)
|
| 171 |
+
|
| 172 |
+
if self.gradient_checkpointing and self.training:
|
| 173 |
+
if use_cache:
|
| 174 |
+
use_cache = False
|
| 175 |
+
|
| 176 |
+
presents = () if use_cache else None
|
| 177 |
+
all_self_attentions = () if output_attentions else None
|
| 178 |
+
all_cross_attentions = () if output_attentions and self.config.add_cross_attention else None
|
| 179 |
+
all_hidden_states = () if output_hidden_states else None
|
| 180 |
+
for i, (block, layer_past) in enumerate(zip(self.h, past_key_values)):
|
| 181 |
+
# Model parallel
|
| 182 |
+
if self.model_parallel:
|
| 183 |
+
torch.cuda.set_device(hidden_states.device)
|
| 184 |
+
# Ensure layer_past is on same device as hidden_states (might not be correct)
|
| 185 |
+
if layer_past is not None:
|
| 186 |
+
layer_past = tuple(past_state.to(hidden_states.device) for past_state in layer_past)
|
| 187 |
+
# Ensure that attention_mask is always on the same device as hidden_states
|
| 188 |
+
if attention_mask is not None:
|
| 189 |
+
attention_mask = attention_mask.to(hidden_states.device)
|
| 190 |
+
if isinstance(head_mask, torch.Tensor):
|
| 191 |
+
head_mask = head_mask.to(hidden_states.device)
|
| 192 |
+
if output_hidden_states:
|
| 193 |
+
all_hidden_states = all_hidden_states + (hidden_states,)
|
| 194 |
+
|
| 195 |
+
if self.gradient_checkpointing and self.training:
|
| 196 |
+
outputs = self._gradient_checkpointing_func(
|
| 197 |
+
block.__call__,
|
| 198 |
+
hidden_states,
|
| 199 |
+
None,
|
| 200 |
+
attention_mask,
|
| 201 |
+
head_mask[i],
|
| 202 |
+
encoder_hidden_states,
|
| 203 |
+
encoder_attention_mask,
|
| 204 |
+
use_cache,
|
| 205 |
+
output_attentions,
|
| 206 |
+
)
|
| 207 |
+
else:
|
| 208 |
+
outputs = block(
|
| 209 |
+
hidden_states,
|
| 210 |
+
layer_past=layer_past,
|
| 211 |
+
attention_mask=attention_mask,
|
| 212 |
+
head_mask=head_mask[i],
|
| 213 |
+
encoder_hidden_states=encoder_hidden_states,
|
| 214 |
+
encoder_attention_mask=encoder_attention_mask,
|
| 215 |
+
use_cache=use_cache,
|
| 216 |
+
output_attentions=output_attentions,
|
| 217 |
+
)
|
| 218 |
+
|
| 219 |
+
hidden_states = outputs[0]
|
| 220 |
+
if use_cache is True:
|
| 221 |
+
presents = presents + (outputs[1],)
|
| 222 |
+
|
| 223 |
+
if output_attentions:
|
| 224 |
+
all_self_attentions = all_self_attentions + (outputs[2 if use_cache else 1],)
|
| 225 |
+
if self.config.add_cross_attention:
|
| 226 |
+
all_cross_attentions = all_cross_attentions + (outputs[3 if use_cache else 2],)
|
| 227 |
+
|
| 228 |
+
# Model Parallel: If it's the last layer for that device, put things on the next device
|
| 229 |
+
if self.model_parallel:
|
| 230 |
+
for k, v in self.device_map.items():
|
| 231 |
+
if i == v[-1] and "cuda:" + str(k) != self.last_device:
|
| 232 |
+
hidden_states = hidden_states.to("cuda:" + str(k + 1))
|
| 233 |
+
|
| 234 |
+
hidden_states = self.ln_f(hidden_states)
|
| 235 |
+
|
| 236 |
+
hidden_states = hidden_states.view(output_shape)
|
| 237 |
+
# Add last hidden state
|
| 238 |
+
if output_hidden_states:
|
| 239 |
+
all_hidden_states = all_hidden_states + (hidden_states,)
|
| 240 |
+
|
| 241 |
+
if not return_dict:
|
| 242 |
+
return tuple(
|
| 243 |
+
v
|
| 244 |
+
for v in [hidden_states, presents, all_hidden_states, all_self_attentions, all_cross_attentions]
|
| 245 |
+
if v is not None
|
| 246 |
+
)
|
| 247 |
+
|
| 248 |
+
return BaseModelOutputWithPastAndCrossAttentions(
|
| 249 |
+
last_hidden_state=hidden_states,
|
| 250 |
+
past_key_values=presents,
|
| 251 |
+
hidden_states=all_hidden_states,
|
| 252 |
+
attentions=all_self_attentions,
|
| 253 |
+
cross_attentions=all_cross_attentions,
|
| 254 |
+
)
|
| 255 |
+
|
| 256 |
+
class GPT2NoPositionalEncodingLMHeadModel(GPT2PreTrainedModel):
|
| 257 |
+
_tied_weights_keys = ["lm_head.weight"]
|
| 258 |
+
|
| 259 |
+
def __init__(self, config):
|
| 260 |
+
super().__init__(config)
|
| 261 |
+
self.transformer = GPT2NoPositionalEncodingModel(config)
|
| 262 |
+
self.lm_head = nn.Linear(config.n_embd, config.vocab_size, bias=False)
|
| 263 |
+
|
| 264 |
+
# Model parallel
|
| 265 |
+
self.model_parallel = False
|
| 266 |
+
self.device_map = None
|
| 267 |
+
|
| 268 |
+
# Initialize weights and apply final processing
|
| 269 |
+
self.post_init()
|
| 270 |
+
|
| 271 |
+
def parallelize(self, device_map=None):
|
| 272 |
+
self.device_map = (
|
| 273 |
+
get_device_map(len(self.transformer.h), range(torch.cuda.device_count()))
|
| 274 |
+
if device_map is None
|
| 275 |
+
else device_map
|
| 276 |
+
)
|
| 277 |
+
assert_device_map(self.device_map, len(self.transformer.h))
|
| 278 |
+
self.transformer.parallelize(self.device_map)
|
| 279 |
+
self.lm_head = self.lm_head.to(self.transformer.first_device)
|
| 280 |
+
self.model_parallel = True
|
| 281 |
+
|
| 282 |
+
def deparallelize(self):
|
| 283 |
+
self.transformer.deparallelize()
|
| 284 |
+
self.transformer = self.transformer.to("cpu")
|
| 285 |
+
self.lm_head = self.lm_head.to("cpu")
|
| 286 |
+
self.model_parallel = False
|
| 287 |
+
torch.cuda.empty_cache()
|
| 288 |
+
|
| 289 |
+
def get_output_embeddings(self):
|
| 290 |
+
return self.lm_head
|
| 291 |
+
|
| 292 |
+
def set_output_embeddings(self, new_embeddings):
|
| 293 |
+
self.lm_head = new_embeddings
|
| 294 |
+
|
| 295 |
+
def prepare_inputs_for_generation(self, input_ids, past_key_values=None, inputs_embeds=None, **kwargs):
|
| 296 |
+
token_type_ids = kwargs.get("token_type_ids", None)
|
| 297 |
+
# Omit tokens covered by past_key_values
|
| 298 |
+
if past_key_values:
|
| 299 |
+
past_length = past_key_values[0][0].shape[2]
|
| 300 |
+
|
| 301 |
+
# Some generation methods already pass only the last input ID
|
| 302 |
+
if input_ids.shape[1] > past_length:
|
| 303 |
+
remove_prefix_length = past_length
|
| 304 |
+
else:
|
| 305 |
+
# Default to old behavior: keep only final ID
|
| 306 |
+
remove_prefix_length = input_ids.shape[1] - 1
|
| 307 |
+
|
| 308 |
+
input_ids = input_ids[:, remove_prefix_length:]
|
| 309 |
+
if token_type_ids is not None:
|
| 310 |
+
token_type_ids = token_type_ids[:, -input_ids.shape[1] :]
|
| 311 |
+
|
| 312 |
+
attention_mask = kwargs.get("attention_mask", None)
|
| 313 |
+
position_ids = kwargs.get("position_ids", None)
|
| 314 |
+
|
| 315 |
+
if attention_mask is not None and position_ids is None:
|
| 316 |
+
# create position_ids on the fly for batch generation
|
| 317 |
+
position_ids = attention_mask.long().cumsum(-1) - 1
|
| 318 |
+
position_ids.masked_fill_(attention_mask == 0, 1)
|
| 319 |
+
if past_key_values:
|
| 320 |
+
position_ids = position_ids[:, -input_ids.shape[1] :]
|
| 321 |
+
else:
|
| 322 |
+
position_ids = None
|
| 323 |
+
|
| 324 |
+
# if `inputs_embeds` are passed, we only want to use them in the 1st generation step
|
| 325 |
+
if inputs_embeds is not None and past_key_values is None:
|
| 326 |
+
model_inputs = {"inputs_embeds": inputs_embeds}
|
| 327 |
+
else:
|
| 328 |
+
model_inputs = {"input_ids": input_ids}
|
| 329 |
+
|
| 330 |
+
model_inputs.update(
|
| 331 |
+
{
|
| 332 |
+
"past_key_values": past_key_values,
|
| 333 |
+
"use_cache": kwargs.get("use_cache"),
|
| 334 |
+
"position_ids": position_ids,
|
| 335 |
+
"attention_mask": attention_mask,
|
| 336 |
+
"token_type_ids": token_type_ids,
|
| 337 |
+
}
|
| 338 |
+
)
|
| 339 |
+
|
| 340 |
+
return model_inputs
|
| 341 |
+
|
| 342 |
+
def forward(
|
| 343 |
+
self,
|
| 344 |
+
input_ids: Optional[torch.LongTensor] = None,
|
| 345 |
+
past_key_values: Optional[Tuple[Tuple[torch.Tensor]]] = None,
|
| 346 |
+
attention_mask: Optional[torch.FloatTensor] = None,
|
| 347 |
+
token_type_ids: Optional[torch.LongTensor] = None,
|
| 348 |
+
position_ids: Optional[torch.LongTensor] = None,
|
| 349 |
+
head_mask: Optional[torch.FloatTensor] = None,
|
| 350 |
+
inputs_embeds: Optional[torch.FloatTensor] = None,
|
| 351 |
+
encoder_hidden_states: Optional[torch.Tensor] = None,
|
| 352 |
+
encoder_attention_mask: Optional[torch.FloatTensor] = None,
|
| 353 |
+
labels: Optional[torch.LongTensor] = None,
|
| 354 |
+
use_cache: Optional[bool] = None,
|
| 355 |
+
output_attentions: Optional[bool] = None,
|
| 356 |
+
output_hidden_states: Optional[bool] = None,
|
| 357 |
+
return_dict: Optional[bool] = None,
|
| 358 |
+
) -> Union[Tuple, CausalLMOutputWithCrossAttentions]:
|
| 359 |
+
r"""
|
| 360 |
+
labels (`torch.LongTensor` of shape `(batch_size, sequence_length)`, *optional*):
|
| 361 |
+
Labels for language modeling. Note that the labels **are shifted** inside the model, i.e. you can set
|
| 362 |
+
`labels = input_ids` Indices are selected in `[-100, 0, ..., config.vocab_size]` All labels set to `-100`
|
| 363 |
+
are ignored (masked), the loss is only computed for labels in `[0, ..., config.vocab_size]`
|
| 364 |
+
"""
|
| 365 |
+
return_dict = return_dict if return_dict is not None else self.config.use_return_dict
|
| 366 |
+
|
| 367 |
+
transformer_outputs = self.transformer(
|
| 368 |
+
input_ids,
|
| 369 |
+
past_key_values=past_key_values,
|
| 370 |
+
attention_mask=attention_mask,
|
| 371 |
+
token_type_ids=token_type_ids,
|
| 372 |
+
position_ids=position_ids,
|
| 373 |
+
head_mask=head_mask,
|
| 374 |
+
inputs_embeds=inputs_embeds,
|
| 375 |
+
encoder_hidden_states=encoder_hidden_states,
|
| 376 |
+
encoder_attention_mask=encoder_attention_mask,
|
| 377 |
+
use_cache=use_cache,
|
| 378 |
+
output_attentions=output_attentions,
|
| 379 |
+
output_hidden_states=output_hidden_states,
|
| 380 |
+
return_dict=return_dict,
|
| 381 |
+
)
|
| 382 |
+
hidden_states = transformer_outputs[0]
|
| 383 |
+
|
| 384 |
+
# Set device for model parallelism
|
| 385 |
+
if self.model_parallel:
|
| 386 |
+
torch.cuda.set_device(self.transformer.first_device)
|
| 387 |
+
hidden_states = hidden_states.to(self.lm_head.weight.device)
|
| 388 |
+
|
| 389 |
+
lm_logits = self.lm_head(hidden_states)
|
| 390 |
+
|
| 391 |
+
loss = None
|
| 392 |
+
if labels is not None:
|
| 393 |
+
# move labels to correct device to enable model parallelism
|
| 394 |
+
labels = labels.to(lm_logits.device)
|
| 395 |
+
# Shift so that tokens < n predict n
|
| 396 |
+
shift_logits = lm_logits[..., :-1, :].contiguous()
|
| 397 |
+
shift_labels = labels[..., 1:].contiguous()
|
| 398 |
+
# Flatten the tokens
|
| 399 |
+
loss_fct = CrossEntropyLoss()
|
| 400 |
+
loss = loss_fct(shift_logits.view(-1, shift_logits.size(-1)), shift_labels.view(-1))
|
| 401 |
+
|
| 402 |
+
if not return_dict:
|
| 403 |
+
output = (lm_logits,) + transformer_outputs[1:]
|
| 404 |
+
return ((loss,) + output) if loss is not None else output
|
| 405 |
+
|
| 406 |
+
return CausalLMOutputWithCrossAttentions(
|
| 407 |
+
loss=loss,
|
| 408 |
+
logits=lm_logits,
|
| 409 |
+
past_key_values=transformer_outputs.past_key_values,
|
| 410 |
+
hidden_states=transformer_outputs.hidden_states,
|
| 411 |
+
attentions=transformer_outputs.attentions,
|
| 412 |
+
cross_attentions=transformer_outputs.cross_attentions,
|
| 413 |
+
)
|
| 414 |
+
|
| 415 |
+
@staticmethod
|
| 416 |
+
def _reorder_cache(
|
| 417 |
+
past_key_values: Tuple[Tuple[torch.Tensor]], beam_idx: torch.Tensor
|
| 418 |
+
) -> Tuple[Tuple[torch.Tensor]]:
|
| 419 |
+
"""
|
| 420 |
+
This function is used to re-order the `past_key_values` cache if [`~PreTrainedModel.beam_search`] or
|
| 421 |
+
[`~PreTrainedModel.beam_sample`] is called. This is required to match `past_key_values` with the correct
|
| 422 |
+
beam_idx at every generation step.
|
| 423 |
+
"""
|
| 424 |
+
return tuple(
|
| 425 |
+
tuple(past_state.index_select(0, beam_idx.to(past_state.device)) for past_state in layer_past)
|
| 426 |
+
for layer_past in past_key_values
|
| 427 |
+
)
|
hop_interventions/create_agreement_data.py
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# create__agreement_data.py
|
| 2 |
+
# Author: Julie Kallini
|
| 3 |
+
|
| 4 |
+
# For importing utils
|
| 5 |
+
import sys
|
| 6 |
+
sys.path.append("..")
|
| 7 |
+
|
| 8 |
+
import pandas as pd
|
| 9 |
+
from glob import glob
|
| 10 |
+
import re
|
| 11 |
+
from utils import gpt2_hop_tokenizer, BABYLM_DATA_PATH, marker_sg_token
|
| 12 |
+
from pluralizer import Pluralizer
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
if __name__ == "__main__":
|
| 16 |
+
|
| 17 |
+
get_vocab_dict = []
|
| 18 |
+
vocab = gpt2_hop_tokenizer.vocab
|
| 19 |
+
|
| 20 |
+
# Patterns for finding simple "The SUBJ VERB x x x x MARKER" sequences
|
| 21 |
+
control_pattern = f"(?:^| )(?:{vocab['The']}|{vocab['Ġthe']}) [1-9]+ [1-9]+ {marker_sg_token} [1-9]+ [1-9]+ [1-9]+ [1-9]+"
|
| 22 |
+
words4_pattern = f"(?:^| )(?:{vocab['The']}|{vocab['Ġthe']}) [1-9]+ [1-9]+ [1-9]+ [1-9]+ [1-9]+ [1-9]+ {marker_sg_token}"
|
| 23 |
+
|
| 24 |
+
# Get test file paths
|
| 25 |
+
test_file_path = f'{BABYLM_DATA_PATH}/babylm_data_perturbed/' + \
|
| 26 |
+
'babylm_hop_{}/babylm_test_affected/*'
|
| 27 |
+
control_files = sorted(glob(test_file_path.format("control")))
|
| 28 |
+
words4_files = sorted(glob(test_file_path.format("words4")))
|
| 29 |
+
|
| 30 |
+
# Iterate over files and get candidate sequences for interventions
|
| 31 |
+
candidate_sequences = []
|
| 32 |
+
for control_file_path, words4_file_path in zip(control_files, words4_files):
|
| 33 |
+
print(control_file_path.split("/")[-1])
|
| 34 |
+
assert control_file_path.split(
|
| 35 |
+
"/")[-1] == words4_file_path.split("/")[-1]
|
| 36 |
+
|
| 37 |
+
# Iterate over pairs of lines in file (control and words4)
|
| 38 |
+
control_lines = open(control_file_path, 'r').readlines()
|
| 39 |
+
words4_lines = open(words4_file_path, 'r').readlines()
|
| 40 |
+
for cl, wl in zip(control_lines, words4_lines):
|
| 41 |
+
|
| 42 |
+
# Find all matches to patterns
|
| 43 |
+
cseqs = re.findall(control_pattern, cl)
|
| 44 |
+
wseqs = re.findall(words4_pattern, wl)
|
| 45 |
+
|
| 46 |
+
# See if there is a shared pattern in control and words4 line
|
| 47 |
+
for cseq, wseq in zip(re.findall(control_pattern, cl), re.findall(words4_pattern, wl)):
|
| 48 |
+
if cseq.replace(" " + str(marker_sg_token), "") == wseq.replace(" " + str(marker_sg_token), ""):
|
| 49 |
+
candidate_sequences.append(
|
| 50 |
+
[int(s) for s in wseq.replace(
|
| 51 |
+
str(marker_sg_token), "").split()]
|
| 52 |
+
)
|
| 53 |
+
|
| 54 |
+
# Init pluralizer
|
| 55 |
+
pluralizer = Pluralizer()
|
| 56 |
+
|
| 57 |
+
# Iterate over candidate sequences
|
| 58 |
+
data = []
|
| 59 |
+
for seq in candidate_sequences:
|
| 60 |
+
|
| 61 |
+
# Get string version of sequence to get the subject
|
| 62 |
+
splitted = gpt2_hop_tokenizer.decode(seq).split()
|
| 63 |
+
|
| 64 |
+
# Get plural of subject (the word at index 1)
|
| 65 |
+
splitted[1] = pluralizer.pluralize(splitted[1], 2, False)
|
| 66 |
+
|
| 67 |
+
# Get GPT-2 tokens of plural version of sequence
|
| 68 |
+
plur_seq = gpt2_hop_tokenizer.encode(" ".join(splitted))
|
| 69 |
+
|
| 70 |
+
# If new subject form has a different number of tokens, skip
|
| 71 |
+
if len(plur_seq) != len(seq):
|
| 72 |
+
continue
|
| 73 |
+
|
| 74 |
+
# In case " the" has changed to "the", reset the first token
|
| 75 |
+
plur_seq[0] = seq[0]
|
| 76 |
+
|
| 77 |
+
# If singular and plural sequence are the same, skip
|
| 78 |
+
if seq == plur_seq:
|
| 79 |
+
continue
|
| 80 |
+
|
| 81 |
+
data.append([" ".join([str(s) for s in seq]),
|
| 82 |
+
" ".join([str(s) for s in plur_seq])])
|
| 83 |
+
|
| 84 |
+
df = pd.DataFrame(data, columns=["Singular", "Plural"])
|
| 85 |
+
df.to_csv("hop_agreement_data.csv")
|
hop_interventions/hop_interventions.ipynb
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
hop_interventions/hop_interventions.py
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# hop_interventions.py
|
| 2 |
+
# Author: Julie Kallini
|
| 3 |
+
|
| 4 |
+
# For importing utils
|
| 5 |
+
import sys
|
| 6 |
+
sys.path.append("..")
|
| 7 |
+
|
| 8 |
+
# align-transformers
|
| 9 |
+
PATH_TO_ALIGN_TRANSFORMERS = "/nlp/scr/kallini/align-transformers/"
|
| 10 |
+
sys.path.append(PATH_TO_ALIGN_TRANSFORMERS)
|
| 11 |
+
|
| 12 |
+
import pandas as pd
|
| 13 |
+
from models.utils import embed_to_distrib
|
| 14 |
+
from models.configuration_alignable_model import AlignableRepresentationConfig, AlignableConfig
|
| 15 |
+
from models.alignable_base import AlignableModel
|
| 16 |
+
from models.interventions import VanillaIntervention
|
| 17 |
+
from utils import CHECKPOINT_READ_PATH, marker_sg_token, marker_pl_token, \
|
| 18 |
+
PERTURBATIONS, PAREN_MODELS
|
| 19 |
+
from tqdm import tqdm
|
| 20 |
+
from transformers import GPT2Model
|
| 21 |
+
from gpt2_no_positional_encoding_model import GPT2NoPositionalEncodingModel
|
| 22 |
+
import os
|
| 23 |
+
import torch
|
| 24 |
+
import argparse
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
MAX_TRAINING_STEPS = 3000
|
| 28 |
+
CHECKPOINTS = list(range(100, MAX_TRAINING_STEPS+1, 100))
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
def simple_position_config(model_type, intervention_type, layer):
|
| 32 |
+
alignable_config = AlignableConfig(
|
| 33 |
+
alignable_model_type=model_type,
|
| 34 |
+
alignable_representations=[
|
| 35 |
+
AlignableRepresentationConfig(
|
| 36 |
+
layer, # layer
|
| 37 |
+
intervention_type, # intervention type
|
| 38 |
+
"pos", # intervention unit
|
| 39 |
+
1 # max number of unit
|
| 40 |
+
),
|
| 41 |
+
],
|
| 42 |
+
alignable_interventions_type=VanillaIntervention,
|
| 43 |
+
)
|
| 44 |
+
return alignable_config
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
def get_model(perturbation_type, train_set, seed, paren_model, ckpt, no_pos_encodings=False):
|
| 48 |
+
|
| 49 |
+
# Get path to model
|
| 50 |
+
no_pos_encodings = "_no_positional_encodings" if no_pos_encodings else ""
|
| 51 |
+
model = f"babylm_{perturbation_type}_{train_set}_{paren_model}{no_pos_encodings}_seed{seed}"
|
| 52 |
+
model_path = f"{CHECKPOINT_READ_PATH}/babylm_{perturbation_type}_{train_set}_{paren_model}{no_pos_encodings}/{model}/runs/{model}/checkpoint-{ckpt}"
|
| 53 |
+
|
| 54 |
+
# Load appropriate GPT-2 model
|
| 55 |
+
if no_pos_encodings:
|
| 56 |
+
return GPT2NoPositionalEncodingModel.from_pretrained(model_path).to(device)
|
| 57 |
+
else:
|
| 58 |
+
return GPT2Model.from_pretrained(model_path).to(device)
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
def run_interventions(model, base_input_ids, source_input_ids):
|
| 62 |
+
|
| 63 |
+
tokens = [marker_sg_token, marker_pl_token]
|
| 64 |
+
|
| 65 |
+
data = []
|
| 66 |
+
BATCH_SIZE = 16
|
| 67 |
+
for batch_i in tqdm(range(0, len(base_input_ids), BATCH_SIZE)):
|
| 68 |
+
|
| 69 |
+
# Get base and source batches
|
| 70 |
+
base_batch = base_input_ids[batch_i:batch_i+BATCH_SIZE]
|
| 71 |
+
source_batch = source_input_ids[batch_i:batch_i+BATCH_SIZE]
|
| 72 |
+
|
| 73 |
+
# Iterate over GPT-2 layers
|
| 74 |
+
for layer_i in range(model.config.n_layer):
|
| 75 |
+
|
| 76 |
+
# Get block_output config for this layer
|
| 77 |
+
alignable_config = simple_position_config(
|
| 78 |
+
type(model), "block_output", layer_i)
|
| 79 |
+
alignable = AlignableModel(alignable_config, model)
|
| 80 |
+
|
| 81 |
+
# Iterate over token positions
|
| 82 |
+
for pos_i in range(len(base_batch[0])):
|
| 83 |
+
|
| 84 |
+
_, counterfactual_outputs = alignable(
|
| 85 |
+
{"input_ids": torch.tensor(base_batch).to(device)},
|
| 86 |
+
[{"input_ids": torch.tensor(source_batch).to(device)}],
|
| 87 |
+
{"sources->base": ([[[pos_i]] * len(base_batch)],
|
| 88 |
+
[[[pos_i]] * len(base_batch)])}
|
| 89 |
+
)
|
| 90 |
+
distrib = embed_to_distrib(
|
| 91 |
+
model, counterfactual_outputs.last_hidden_state,
|
| 92 |
+
logits=False
|
| 93 |
+
)
|
| 94 |
+
for i in range(len(base_batch)):
|
| 95 |
+
for token in tokens:
|
| 96 |
+
data.append({
|
| 97 |
+
'example': batch_i + i,
|
| 98 |
+
'token': token,
|
| 99 |
+
'prob': float(distrib[i][-1][token]),
|
| 100 |
+
'layer': layer_i,
|
| 101 |
+
'pos': pos_i,
|
| 102 |
+
'type': "block_output"
|
| 103 |
+
})
|
| 104 |
+
return pd.DataFrame(data)
|
| 105 |
+
|
| 106 |
+
|
| 107 |
+
if __name__ == "__main__":
|
| 108 |
+
|
| 109 |
+
parser = argparse.ArgumentParser(
|
| 110 |
+
prog='Run intervention tests for subject-verb agreement on hop models',
|
| 111 |
+
description='Run interventions for subject-verb agreement on hop models')
|
| 112 |
+
parser.add_argument('perturbation_type',
|
| 113 |
+
default='all',
|
| 114 |
+
const='all',
|
| 115 |
+
nargs='?',
|
| 116 |
+
choices=PERTURBATIONS.keys(),
|
| 117 |
+
help='Perturbation function used to transform BabyLM dataset')
|
| 118 |
+
parser.add_argument('train_set',
|
| 119 |
+
default='all',
|
| 120 |
+
const='all',
|
| 121 |
+
nargs='?',
|
| 122 |
+
choices=["100M", "10M"],
|
| 123 |
+
help='BabyLM train set')
|
| 124 |
+
parser.add_argument('random_seed', type=int, help="Random seed")
|
| 125 |
+
parser.add_argument('paren_model',
|
| 126 |
+
default='all',
|
| 127 |
+
const='all',
|
| 128 |
+
nargs='?',
|
| 129 |
+
choices=list(PAREN_MODELS.keys()) + ["randinit"],
|
| 130 |
+
help='Parenthesis model')
|
| 131 |
+
parser.add_argument('-np', '--no_pos_encodings', action='store_true',
|
| 132 |
+
help="Train GPT-2 with no positional encodings")
|
| 133 |
+
|
| 134 |
+
# Get args
|
| 135 |
+
args = parser.parse_args()
|
| 136 |
+
|
| 137 |
+
if "hop" not in args.perturbation_type:
|
| 138 |
+
raise Exception(
|
| 139 |
+
"'{args.perturbation_type}' is not a valid hop perturbation")
|
| 140 |
+
|
| 141 |
+
# Get examples to run interventions
|
| 142 |
+
data_df = pd.read_csv("hop_agreement_data.csv")
|
| 143 |
+
bases = [[int(tok) for tok in seq.split()]
|
| 144 |
+
for seq in list(data_df["Singular"])]
|
| 145 |
+
sources = [[int(tok) for tok in seq.split()]
|
| 146 |
+
for seq in list(data_df["Plural"])]
|
| 147 |
+
|
| 148 |
+
# Only get first three tokens of each example for control model
|
| 149 |
+
if args.perturbation_type == "hop_control":
|
| 150 |
+
bases = [row[:3] for row in bases]
|
| 151 |
+
sources = [row[:3] for row in sources]
|
| 152 |
+
|
| 153 |
+
# Get model and run intervention experiments
|
| 154 |
+
device = "cuda"
|
| 155 |
+
result_df = None
|
| 156 |
+
for ckpt in CHECKPOINTS:
|
| 157 |
+
print(f"Checkpoint: {ckpt}")
|
| 158 |
+
model = get_model(args.perturbation_type, args.train_set,
|
| 159 |
+
args.random_seed, args.paren_model, ckpt,
|
| 160 |
+
args.no_pos_encodings)
|
| 161 |
+
if result_df is None:
|
| 162 |
+
result_df = run_interventions(model, bases, sources)
|
| 163 |
+
result_df["ckpt"] = ckpt
|
| 164 |
+
else:
|
| 165 |
+
ckpt_df = run_interventions(model, bases, sources)
|
| 166 |
+
ckpt_df["ckpt"] = ckpt
|
| 167 |
+
result_df = pd.concat((result_df, ckpt_df), axis=0)
|
| 168 |
+
|
| 169 |
+
# Create directory for results
|
| 170 |
+
nps = '_no_pos_encodings' if args.no_pos_encodings else ''
|
| 171 |
+
result_directory = f"hop_intervention_results/{args.perturbation_type}_{args.train_set}{nps}/"
|
| 172 |
+
if not os.path.exists(result_directory):
|
| 173 |
+
os.makedirs(result_directory)
|
| 174 |
+
|
| 175 |
+
# Write results
|
| 176 |
+
result_df.to_csv(result_directory + f"{args.paren_model}_seed{args.random_seed}.csv", index=False)
|
hop_surprisal/hop_surprisal.ipynb
ADDED
|
@@ -0,0 +1,223 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"cells": [
|
| 3 |
+
{
|
| 4 |
+
"cell_type": "code",
|
| 5 |
+
"execution_count": 1,
|
| 6 |
+
"metadata": {},
|
| 7 |
+
"outputs": [],
|
| 8 |
+
"source": [
|
| 9 |
+
"# For importing utils\n",
|
| 10 |
+
"import sys\n",
|
| 11 |
+
"sys.path.append(\"..\")"
|
| 12 |
+
]
|
| 13 |
+
},
|
| 14 |
+
{
|
| 15 |
+
"cell_type": "code",
|
| 16 |
+
"execution_count": 2,
|
| 17 |
+
"metadata": {},
|
| 18 |
+
"outputs": [
|
| 19 |
+
{
|
| 20 |
+
"name": "stderr",
|
| 21 |
+
"output_type": "stream",
|
| 22 |
+
"text": [
|
| 23 |
+
"/nlp/scr/kallini/miniconda3/envs/llmenv/lib/python3.10/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n",
|
| 24 |
+
" from .autonotebook import tqdm as notebook_tqdm\n"
|
| 25 |
+
]
|
| 26 |
+
}
|
| 27 |
+
],
|
| 28 |
+
"source": [
|
| 29 |
+
"import numpy as np\n",
|
| 30 |
+
"import pandas as pd\n",
|
| 31 |
+
"import matplotlib.pyplot as plt\n",
|
| 32 |
+
"from matplotlib.patches import Patch\n",
|
| 33 |
+
"from scipy import stats\n",
|
| 34 |
+
"from utils import PERTURBATIONS"
|
| 35 |
+
]
|
| 36 |
+
},
|
| 37 |
+
{
|
| 38 |
+
"cell_type": "code",
|
| 39 |
+
"execution_count": 3,
|
| 40 |
+
"metadata": {},
|
| 41 |
+
"outputs": [],
|
| 42 |
+
"source": [
|
| 43 |
+
"def get_surprisal_differences(perturbation, seed, ckpt, pos_encodings=True):\n",
|
| 44 |
+
"\n",
|
| 45 |
+
" # Load surprisal DataFrame\n",
|
| 46 |
+
" surprisals_path = \"hop_surprisal_results/{}_100M{}/randinit_seed{}.csv\"\n",
|
| 47 |
+
" nps = \"\" if pos_encodings else \"_no_positional_encodings\"\n",
|
| 48 |
+
" surprisal_df = pd.read_csv(surprisals_path.format(perturbation, nps, seed))\n",
|
| 49 |
+
" \n",
|
| 50 |
+
" # Get summary stats for suprisal differences\n",
|
| 51 |
+
" marker_token_surprisals = surprisal_df[f\"Marker Token Surprisals (ckpt {ckpt})\"]\n",
|
| 52 |
+
" nomarker_token_surprisals = surprisal_df[f\"No Marker Token Surprisals (ckpt {ckpt})\"]\n",
|
| 53 |
+
" differences = nomarker_token_surprisals - marker_token_surprisals\n",
|
| 54 |
+
" avg_differences = differences.mean()\n",
|
| 55 |
+
"\n",
|
| 56 |
+
" return avg_differences\n",
|
| 57 |
+
"\n",
|
| 58 |
+
"def get_summary_stats(l):\n",
|
| 59 |
+
" # Calculate confidence interval using t-distribution\n",
|
| 60 |
+
" mean = np.mean(l)\n",
|
| 61 |
+
" sem = stats.sem(l)\n",
|
| 62 |
+
" ci_lower, ci_upper = stats.t.interval(0.95, df=len(l)-1, loc=mean, scale=sem)\n",
|
| 63 |
+
" return mean, (ci_upper - ci_lower) / 2\n",
|
| 64 |
+
" "
|
| 65 |
+
]
|
| 66 |
+
},
|
| 67 |
+
{
|
| 68 |
+
"cell_type": "code",
|
| 69 |
+
"execution_count": 4,
|
| 70 |
+
"metadata": {},
|
| 71 |
+
"outputs": [],
|
| 72 |
+
"source": [
|
| 73 |
+
"def plot_surprisal_differences(ax, seeds, ckpt, colors, hatches, pos_encodings):\n",
|
| 74 |
+
"\n",
|
| 75 |
+
" hop_control_seeds = []\n",
|
| 76 |
+
" hop_tokens4_seeds = []\n",
|
| 77 |
+
" hop_words4_seeds = []\n",
|
| 78 |
+
" for seed in seeds:\n",
|
| 79 |
+
" # Get summary stats for each hop model\n",
|
| 80 |
+
" avg_hop_control = get_surprisal_differences(\"hop_control\", seed, ckpt, pos_encodings)\n",
|
| 81 |
+
" avg_hop_tokens4 = get_surprisal_differences(\"hop_tokens4\", seed, ckpt, pos_encodings)\n",
|
| 82 |
+
" avg_hop_words4 = get_surprisal_differences(\"hop_words4\", seed, ckpt, pos_encodings)\n",
|
| 83 |
+
"\n",
|
| 84 |
+
" # Append results\n",
|
| 85 |
+
" hop_control_seeds.append(avg_hop_control)\n",
|
| 86 |
+
" hop_tokens4_seeds.append(avg_hop_tokens4)\n",
|
| 87 |
+
" hop_words4_seeds.append(avg_hop_words4)\n",
|
| 88 |
+
"\n",
|
| 89 |
+
" if len(seeds) > 1:\n",
|
| 90 |
+
" # Prepare data to plot\n",
|
| 91 |
+
" summary_stats = [\n",
|
| 92 |
+
" get_summary_stats(hop_control_seeds),\n",
|
| 93 |
+
" get_summary_stats(hop_tokens4_seeds),\n",
|
| 94 |
+
" get_summary_stats(hop_words4_seeds)\n",
|
| 95 |
+
" ]\n",
|
| 96 |
+
" else:\n",
|
| 97 |
+
" summary_stats = [\n",
|
| 98 |
+
" (hop_control_seeds[0], 0),\n",
|
| 99 |
+
" (hop_tokens4_seeds[0], 0),\n",
|
| 100 |
+
" (hop_words4_seeds[0], 0),\n",
|
| 101 |
+
" ]\n",
|
| 102 |
+
"\n",
|
| 103 |
+
" x = np.arange(3) # label locations\n",
|
| 104 |
+
" width = 0.8 # width of the bars\n",
|
| 105 |
+
"\n",
|
| 106 |
+
" # Iterate over pos / no pos groups\n",
|
| 107 |
+
" for i, (avg, err) in enumerate(summary_stats):\n",
|
| 108 |
+
" # Iterate over models and plot bars\n",
|
| 109 |
+
" color = colors[i]\n",
|
| 110 |
+
" hatch = hatches[i]\n",
|
| 111 |
+
" ax.bar(x[i], avg, width, yerr=err, label=None,\n",
|
| 112 |
+
" color=color, hatch=hatch, edgecolor=\"w\", zorder=2)\n",
|
| 113 |
+
"\n",
|
| 114 |
+
" ax.set_xticks([])\n",
|
| 115 |
+
" ax.grid(zorder=0, color=\"lightgray\")\n",
|
| 116 |
+
" ax.set_title(f\"{ckpt} Steps\")\n",
|
| 117 |
+
"\n",
|
| 118 |
+
"\n",
|
| 119 |
+
"def plot_surprisal_differences_checkpoints(seeds, checkpoints, pos_encodings=True):\n",
|
| 120 |
+
"\n",
|
| 121 |
+
" # Colors patterns for bars\n",
|
| 122 |
+
" color1=PERTURBATIONS[\"hop_control\"][\"color\"]\n",
|
| 123 |
+
" color2=PERTURBATIONS[\"hop_tokens4\"][\"color\"]\n",
|
| 124 |
+
" color3=PERTURBATIONS[\"hop_words4\"][\"color\"]\n",
|
| 125 |
+
" colors = [color1, color2, color3]\n",
|
| 126 |
+
"\n",
|
| 127 |
+
" hatch1 = ''\n",
|
| 128 |
+
" hatch2 = '///'\n",
|
| 129 |
+
" hatch3 = '..'\n",
|
| 130 |
+
" hatches = [hatch1, hatch2, hatch3]\n",
|
| 131 |
+
"\n",
|
| 132 |
+
" # Create a figure with multiple subplots\n",
|
| 133 |
+
" fig, axs = plt.subplots(2, 3, figsize=(6, 4), sharey=True)\n",
|
| 134 |
+
" axes_flat = axs.flatten()\n",
|
| 135 |
+
"\n",
|
| 136 |
+
" # Call individual plot function with different parameters for each subplot\n",
|
| 137 |
+
" for i, checkpoint in enumerate(checkpoints):\n",
|
| 138 |
+
" plot_surprisal_differences(\n",
|
| 139 |
+
" axes_flat[i], seeds, checkpoint, colors, hatches, pos_encodings)\n",
|
| 140 |
+
"\n",
|
| 141 |
+
" legend_elements = [Patch(facecolor=color1, hatch=hatch1,\n",
|
| 142 |
+
" edgecolor=\"w\", label='NoHop'),\n",
|
| 143 |
+
" Patch(facecolor=color2, hatch=hatch2,\n",
|
| 144 |
+
" edgecolor=\"w\", label='TokenHop'),\n",
|
| 145 |
+
" Patch(facecolor=color3, hatch=hatch3,\n",
|
| 146 |
+
" edgecolor=\"w\", label='WordHop')]\n",
|
| 147 |
+
" fig.legend(handles=legend_elements, ncol=3, loc=\"center\",\n",
|
| 148 |
+
" bbox_to_anchor=(0.55, 0), frameon=False)\n",
|
| 149 |
+
" \n",
|
| 150 |
+
" fig.supylabel(\"Surprisal Difference\", fontsize=12, x=0.04)\n",
|
| 151 |
+
"\n",
|
| 152 |
+
" # Adjust layout and show plot\n",
|
| 153 |
+
" plt.tight_layout()"
|
| 154 |
+
]
|
| 155 |
+
},
|
| 156 |
+
{
|
| 157 |
+
"cell_type": "code",
|
| 158 |
+
"execution_count": 5,
|
| 159 |
+
"metadata": {},
|
| 160 |
+
"outputs": [
|
| 161 |
+
{
|
| 162 |
+
"data": {
|
| 163 |
+
"image/png": "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",
|
| 164 |
+
"text/plain": [
|
| 165 |
+
"<Figure size 600x400 with 6 Axes>"
|
| 166 |
+
]
|
| 167 |
+
},
|
| 168 |
+
"metadata": {},
|
| 169 |
+
"output_type": "display_data"
|
| 170 |
+
}
|
| 171 |
+
],
|
| 172 |
+
"source": [
|
| 173 |
+
"CHECKPOINTS = [300, 600, 900, 1200, 1500, 3000]\n",
|
| 174 |
+
"SEEDS = [0, 14, 41, 53, 96]\n",
|
| 175 |
+
"\n",
|
| 176 |
+
"plot_surprisal_differences_checkpoints(seeds=SEEDS, checkpoints=CHECKPOINTS)\n",
|
| 177 |
+
"plt.savefig(f\"figures/hop_surprisals.pdf\", format=\"pdf\", bbox_inches=\"tight\")"
|
| 178 |
+
]
|
| 179 |
+
},
|
| 180 |
+
{
|
| 181 |
+
"cell_type": "code",
|
| 182 |
+
"execution_count": 6,
|
| 183 |
+
"metadata": {},
|
| 184 |
+
"outputs": [
|
| 185 |
+
{
|
| 186 |
+
"data": {
|
| 187 |
+
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAkUAAAGhCAYAAABvQ8DIAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8g+/7EAAAACXBIWXMAAA9hAAAPYQGoP6dpAAB+I0lEQVR4nO3dd3wT9f8H8FeS7t0CXdACHZQhoyB7b8qQjYACIgoiIspPEJRREARBEFGm8mUo8mUpfBFB9pKNILIpe5RNNxSafH5/xEZCRpNrSi/J6/l45NHm7nP3+Vz7TvvK3eVOIYQQICIiInJyysIeABEREZEcMBQRERERgaGIiIiICABDEREREREAhiIiIiIiAAxFRERERAAYioiIiIgAMBQRERERAWAoIiIiIgLAUEREREQEgKFIVk6ePImuXbsiKioKXl5eKFq0KBo0aIB169YZbX/69Gm0atUKPj4+CAoKQq9evXD37l2DdhqNBlOmTEHp0qXh4eGBSpUqYdmyZRaPa8+ePUhISEDx4sXh4eGByMhItGvXDj/99JOuTVZWFhITE7Fjxw6rt5scz59//olXXnkFQUFB8PLywksvvYSZM2catNu7dy/q1asHLy8vhIaG4v3330dGRoZBu+zsbHz88ccIDw+Hp6cnatasic2bN1s8nnXr1qFhw4YIDg6Gl5cXoqKi0K1bN2zcuFHX5ubNm0hMTMSxY8ckbTM5hiNHjqBVq1bw8/ODr68vWrRoYbImWL8OSJBsrF+/XrRs2VIkJiaK+fPnixkzZoj69esLAGLevHl6ba9duyaKFi0qoqOjxddffy0mTpwoAgMDReXKlUV2drZe2xEjRggA4u233xbz588Xbdq0EQDEsmXL8hzTihUrhEKhEPHx8eKLL74Q8+fPFyNHjhR169YVjRo10rW7e/euACDGjh1rk58F2a/ff/9duLm5iZo1a4rp06eL+fPni48//lgMGzZMr93Ro0eFh4eHiI+PF3PmzBGffvqpcHd3F61atTJYZ/fu3YWLi4v46KOPxLx580Tt2rWFi4uL2L17d57jmTp1qgAgGjZsKKZPny7mzp0rPvroI1GlShXRp08fXbtDhw4JAGLhwoX5/RGQnTpy5Ijw8PAQsbGx4ssvvxRTpkwRpUqVEn5+fuLMmTN6bVm/jomhSOZycnJE5cqVRVxcnN70gQMHCk9PT3HlyhXdtM2bNxsEqOvXrwtXV1cxaNAg3TSNRiPq168vSpQoIXJycsz2X758eVGhQgWDoCWEELdv39Z9z1BEQgiRmpoqQkJCRMeOHYVarTbbNiEhQYSFhYnU1FTdtO+++04AEL///rtu2oEDBwQAMXXqVN20R48eiejoaFG7dm2zfTx9+lT4+fmJ5s2bG53/bA3znwq1bt1aBAYGinv37umm3bx5U/j4+IhOnTrptWX9OiaGIjvQtm1bERISojctODhYdO3a1aBtmTJlRNOmTXXPZ82aJQCIkydP6rX76aefBIA836m4u7uLN954w2ybS5cuCQAGj2cD0unTp0Xnzp1FYGCgcHd3F9WqVRNr167VW8/ChQsFALFz507Rv39/ERQUJHx9fUWvXr3EgwcP9NoeOnRItGjRQhQpUkR4eHiIUqVKib59+5odJxW8OXPmCADi1KlTQgghMjIyjIaj1NRU4eLiYrD3KDs7W/j4+Ih+/frppg0bNkyoVCq9fz5CCPH5558LAOLq1asmx5OcnCwAiMTERLPj3r59u9EafvYfzP79+0XLli2Fn5+f8PT0FA0aNBB79uzRW8/YsWMFAHH69GnRtWtX4evrK4KCgsT7778vHj16pNd206ZNom7dusLf3194e3uLMmXKiJEjR5odJxUsX19fo39X27RpI9zc3ER6eroQgvUrhOPWL88pkqHMzEzcu3cPFy5cwFdffYUNGzagadOmuvk3btzAnTt38PLLLxssW6NGDRw9elT3/OjRo/D29ka5cuUM2uXON6dkyZLYunUrrl+/brJNsWLFMGfOHABAx44d8cMPP+CHH35Ap06dAGjPlapVqxZOnz6NESNGYNq0afD29kaHDh3wyy+/GKzvvffew+nTp5GYmIjevXtj6dKl6NChA4QQAIA7d+6gRYsWuHz5MkaMGIFvvvkGr732Gvbv3292W6jgbdmyBX5+frhx4wbi4uLg4+MDPz8/DBw4EI8fP9a1+/vvv5GTk2NQw25ubqhSpYpBDZcpUwZ+fn56bXNr2Nw5FMHBwfD09MS6devw4MEDk+3KlSuH8ePHAwD69++vq+EGDRoAALZt24YGDRogLS0NY8eOxeeff46UlBQ0adIEBw8eNFhft27d8PjxY0yaNAmtW7fGzJkz0b9/f938kydPom3btsjOzsb48eMxbdo0vPLKK/jj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NLB4T1GRIkUQFRUleVBERERELxpvCEtEREQE3hCWiIiICABDEREREREAhiIiIiIiAAxFRERERAAYigpNRkYGxo4di1atWiEoKAgKhQKLFi0yaKfRaLBo0SK88soriIiIgLe3N1566SVMmDABjx8/NrruBQsWoFy5cvDw8EBsbCy++eYbo+1u3LiBbt26ISAgAH5+fmjfvj0uXrxo0fifPHmCr7/+GvHx8fDz80NAQAAqVKiA/v3748yZM7p2e/fuRWJiIlJSUixaL9kHS+sXAN544w0oFAqDR9myZQ3aajQaTJkyBaVLl4aHhwcqVaqEZcuWGV3v6dOn0apVK/j4+CAoKAi9evXC3bt3rRr/Sy+9BG9vbxQpUgRVqlTBkCFDcPPmTV273377DYmJiRatk+zLyZMn0bVrV0RFRcHLywtFixZFgwYNsG7dOqPtLa031rCdE1QoLl26JACIyMhI0ahRIwFALFy40KBdenq6ACBq1aolJkyYIObPny/69u0rlEqlaNSokdBoNHrt586dKwCIzp07i/nz54tevXoJAGLy5MkG642NjRXBwcHiiy++ENOnTxcRERGiRIkS4t69e3mOv23btkKlUonXX39dzJo1S8yYMUO88847okSJEnrbMXXqVAFAXLp0ScqPiWTK0voVQog+ffoId3d38cMPP+g9/ve//xm0HTFihAAg3n77bTF//nzRpk0bAUAsW7ZMr921a9dE0aJFRXR0tPj666/FxIkTRWBgoKhcubLIzs42O/YnT56I+Ph44enpKd555x0xd+5c8eWXX4q+ffuKokWLiu3bt+vaDho0SPDPpGNav369aNmypUhMTBTz588XM2bMEPXr1xcAxLx58/TaWlNvrGH7xp9UIXn8+LFITk4WQghx6NAhk/9UsrOzxR9//GEwfdy4cQKA2Lx5s25aVlaWKFKkiGjTpo1e29dee014e3uLBw8e6KZ98cUXAoA4ePCgbtrp06eFSqUSI0eONDv2gwcPCgBi4sSJBvNycnL0QhVDkWOytH6F0IYib2/vPNd5/fp14erqKgYNGqSbptFoRP369UWJEiVETk6ObvrAgQOFp6enuHLlim7a5s2bjf5De96KFSsEALF06VKDeY8ePRKpqam65/yH4lxycnJE5cqVRVxcnN50S+uNNWz/LPpJlSpVSpQuXdqqR1RUVEGP3WHk9U/FmOPHjwsAYubMmbpp69evFwDE+vXr9dru3btXABA//PCDblr16tVF9erVDdbbokULER0dbbbvZcuWCQBix44dZtuNHTtWADB4PBuQfvjhB1G1alXh4eEhAgMDxauvviquXr2qt56GDRuKChUqiMOHD4vatWsLDw8PUapUKTFnzhyDPmfOnCnKly8vPD09RUBAgKhWrZrRPxxkO5aGopycHL0/1s+bNWuWACBOnjypN/2nn34SAMTu3bt104KDg0XXrl0N1lGmTBnRtGlTs+OdNGmSACAuX75stl2fPn2M1m8utVotvvrqK1G+fHnh7u4ugoODRf/+/fXefAghRMmSJUWbNm3E77//LipXrizc3d1FuXLlxOrVq/XaPXnyRCQmJoqYmBjh7u4ugoKCRN26dcWmTZvMjpNsq23btiIkJERvmqX1xhq2/xq26IrWDRs25H3PZObWrVsAgKJFi+qmHT16FADw8ssv67WtVq0alEoljh49itdffx0ajQbHjx/Hm2++abDeGjVqYNOmTUhPT4evr6/RvkuWLAkAWLp0KerWrQsXF+Nl1KlTJ5w7dw7Lli3DV199pRtrsWLFAAATJ07E6NGj0a1bN7z11lu4e/cuvvnmGzRo0ABHjx5FQECAbl0PHz5E69at0a1bN/To0QMrVqzAwIED4ebmptuO7777Du+//z66dOmCIUOG4PHjxzh+/DgOHDiAnj175vkzpYKTlZUFPz8/ZGVlITAwED169MAXX3wBHx8fXZujR4/C29sb5cqV01u2Ro0auvn16tXDjRs3cOfOHYM6z23722+/mR1Lbv0uWbIEo0aNMvm3bcCAAbh58yY2b96MH374wej8RYsWoW/fvnj//fdx6dIlfPvttzh69Cj++OMPuLq66tqeP38er776Kt555x306dMHCxcuRNeuXbFx40Y0b94cAJCYmIhJkybhrbfeQo0aNZCWlobDhw/jzz//1LUh28vMzMSjR4+QmpqK//3vf9iwYQNeffVV3Xxr6o017AA1XNipjKTtKWrWrJnw8/MTDx8+1E0bNGiQUKlURtsXK1ZMdO/eXQghxN27dwUAMX78eIN2ue90zpw5Y7JvjUYjGjZsKACIkJAQ0aNHDzFr1iy93cC5TB0+u3z5slCpVAaH4P7++2/h4uKiNz23r2nTpummZWdniypVqojg4GDx5MkT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",
|
| 188 |
+
"text/plain": [
|
| 189 |
+
"<Figure size 600x400 with 6 Axes>"
|
| 190 |
+
]
|
| 191 |
+
},
|
| 192 |
+
"metadata": {},
|
| 193 |
+
"output_type": "display_data"
|
| 194 |
+
}
|
| 195 |
+
],
|
| 196 |
+
"source": [
|
| 197 |
+
"plot_surprisal_differences_checkpoints(seeds=[53], checkpoints=CHECKPOINTS, pos_encodings=False)\n",
|
| 198 |
+
"plt.savefig(f\"figures/hop_surprisals_no_pos_encodings.pdf\", format=\"pdf\", bbox_inches=\"tight\")"
|
| 199 |
+
]
|
| 200 |
+
}
|
| 201 |
+
],
|
| 202 |
+
"metadata": {
|
| 203 |
+
"kernelspec": {
|
| 204 |
+
"display_name": "babyenv",
|
| 205 |
+
"language": "python",
|
| 206 |
+
"name": "python3"
|
| 207 |
+
},
|
| 208 |
+
"language_info": {
|
| 209 |
+
"codemirror_mode": {
|
| 210 |
+
"name": "ipython",
|
| 211 |
+
"version": 3
|
| 212 |
+
},
|
| 213 |
+
"file_extension": ".py",
|
| 214 |
+
"mimetype": "text/x-python",
|
| 215 |
+
"name": "python",
|
| 216 |
+
"nbconvert_exporter": "python",
|
| 217 |
+
"pygments_lexer": "ipython3",
|
| 218 |
+
"version": "3.10.11"
|
| 219 |
+
}
|
| 220 |
+
},
|
| 221 |
+
"nbformat": 4,
|
| 222 |
+
"nbformat_minor": 2
|
| 223 |
+
}
|
hop_surprisal/hop_surprisal.py
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# hop_surprisal.py
|
| 2 |
+
# Author: Julie Kallini
|
| 3 |
+
|
| 4 |
+
# For importing utils
|
| 5 |
+
import sys
|
| 6 |
+
sys.path.append("..")
|
| 7 |
+
|
| 8 |
+
import os
|
| 9 |
+
import torch
|
| 10 |
+
import pandas as pd
|
| 11 |
+
import tqdm
|
| 12 |
+
import argparse
|
| 13 |
+
from numpy.random import default_rng
|
| 14 |
+
from transformers import GPT2LMHeadModel
|
| 15 |
+
from gpt2_no_positional_encoding_model import GPT2NoPositionalEncodingLMHeadModel
|
| 16 |
+
from itertools import zip_longest
|
| 17 |
+
from glob import glob
|
| 18 |
+
from utils import CHECKPOINT_READ_PATH, PERTURBATIONS, PAREN_MODELS, \
|
| 19 |
+
BABYLM_DATA_PATH, gpt2_hop_tokenizer, \
|
| 20 |
+
marker_sg_token, marker_pl_token, compute_surprisals
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
MAX_TRAINING_STEPS = 3000
|
| 24 |
+
CHECKPOINTS = list(range(100, MAX_TRAINING_STEPS+1, 100))
|
| 25 |
+
|
| 26 |
+
if __name__ == "__main__":
|
| 27 |
+
|
| 28 |
+
parser = argparse.ArgumentParser(
|
| 29 |
+
prog='Get marker token surprisals for hop verb perturbations',
|
| 30 |
+
description='Marker token surprisals')
|
| 31 |
+
parser.add_argument('perturbation_type',
|
| 32 |
+
default='all',
|
| 33 |
+
const='all',
|
| 34 |
+
nargs='?',
|
| 35 |
+
choices=PERTURBATIONS.keys(),
|
| 36 |
+
help='Perturbation function used to transform BabyLM dataset')
|
| 37 |
+
parser.add_argument('train_set',
|
| 38 |
+
default='all',
|
| 39 |
+
const='all',
|
| 40 |
+
nargs='?',
|
| 41 |
+
choices=["100M", "10M"],
|
| 42 |
+
help='BabyLM train set')
|
| 43 |
+
parser.add_argument('random_seed', type=int, help="Random seed")
|
| 44 |
+
parser.add_argument('paren_model',
|
| 45 |
+
default='all',
|
| 46 |
+
const='all',
|
| 47 |
+
nargs='?',
|
| 48 |
+
choices=list(PAREN_MODELS.keys()) + ["randinit"],
|
| 49 |
+
help='Parenthesis model')
|
| 50 |
+
parser.add_argument('-np', '--no_pos_encodings', action='store_true',
|
| 51 |
+
help="Train GPT-2 with no positional encodings")
|
| 52 |
+
|
| 53 |
+
# Get args
|
| 54 |
+
args = parser.parse_args()
|
| 55 |
+
no_pos_encodings_underscore = "_no_positional_encodings" if args.no_pos_encodings else ""
|
| 56 |
+
|
| 57 |
+
if "hop" not in args.perturbation_type:
|
| 58 |
+
raise Exception(
|
| 59 |
+
"'{args.perturbation_type}' is not a valid hop perturbation")
|
| 60 |
+
|
| 61 |
+
# Get path to model
|
| 62 |
+
model = f"babylm_{args.perturbation_type}_{args.train_set}_{args.paren_model}{no_pos_encodings_underscore}_seed{args.random_seed}"
|
| 63 |
+
model_path = f"{CHECKPOINT_READ_PATH}/babylm_{args.perturbation_type}_{args.train_set}_{args.paren_model}{no_pos_encodings_underscore}/{model}/runs/{model}/checkpoint-"
|
| 64 |
+
|
| 65 |
+
# Get perturbed test files
|
| 66 |
+
test_files = sorted(glob(BABYLM_DATA_PATH +
|
| 67 |
+
"/babylm_data_perturbed/babylm_{}/babylm_test_affected/*".format(args.perturbation_type)))
|
| 68 |
+
|
| 69 |
+
EOS_TOKEN = gpt2_hop_tokenizer.eos_token_id
|
| 70 |
+
FILE_SAMPLE_SIZE = 1000
|
| 71 |
+
MAX_SEQ_LEN = 1024
|
| 72 |
+
rng = default_rng(args.random_seed)
|
| 73 |
+
|
| 74 |
+
marker_token_sequences = []
|
| 75 |
+
nomarker_token_sequences = []
|
| 76 |
+
target_indices = []
|
| 77 |
+
|
| 78 |
+
# Iterate over data files to get surprisal data
|
| 79 |
+
print("Sampling BabyLM affected test files to extract surprisals...")
|
| 80 |
+
for test_file in test_files:
|
| 81 |
+
print(test_file)
|
| 82 |
+
|
| 83 |
+
# Get tokens from test file (+ eos token), and subsample
|
| 84 |
+
f = open(test_file, 'r')
|
| 85 |
+
file_token_sequences = [
|
| 86 |
+
[int(s) for s in l.split()] + [EOS_TOKEN] for l in f.readlines()]
|
| 87 |
+
file_token_sequences = [
|
| 88 |
+
toks for toks in file_token_sequences if len(toks) < MAX_SEQ_LEN]
|
| 89 |
+
sample_indices = rng.choice(
|
| 90 |
+
list(range(len(file_token_sequences))), FILE_SAMPLE_SIZE, replace=False)
|
| 91 |
+
file_token_sequences = [file_token_sequences[i]
|
| 92 |
+
for i in sample_indices]
|
| 93 |
+
|
| 94 |
+
file_target_indices = []
|
| 95 |
+
file_nomarker_token_sequences = []
|
| 96 |
+
for tokens in file_token_sequences:
|
| 97 |
+
# Find index of first marker token for surprisal target
|
| 98 |
+
target_index = None
|
| 99 |
+
for idx in range(len(tokens)):
|
| 100 |
+
if tokens[idx] in (marker_sg_token, marker_pl_token):
|
| 101 |
+
target_index = idx
|
| 102 |
+
break
|
| 103 |
+
assert (target_index is not None)
|
| 104 |
+
|
| 105 |
+
# Make a version of tokens with marker removed at surprisal target
|
| 106 |
+
nomarker_tokens = tokens.copy()
|
| 107 |
+
nomarker_tokens.pop(target_index)
|
| 108 |
+
assert (tokens[:target_index] == nomarker_tokens[:target_index])
|
| 109 |
+
assert (tokens[target_index] in (marker_sg_token, marker_pl_token))
|
| 110 |
+
assert (tokens[target_index+1] == nomarker_tokens[target_index])
|
| 111 |
+
|
| 112 |
+
file_target_indices.append(target_index)
|
| 113 |
+
file_nomarker_token_sequences.append(nomarker_tokens)
|
| 114 |
+
|
| 115 |
+
marker_token_sequences.extend(file_token_sequences)
|
| 116 |
+
nomarker_token_sequences.extend(file_nomarker_token_sequences)
|
| 117 |
+
target_indices.extend(file_target_indices)
|
| 118 |
+
|
| 119 |
+
# For logging/debugging, include decoded sentence
|
| 120 |
+
marker_sents = [gpt2_hop_tokenizer.decode(
|
| 121 |
+
toks) for toks in marker_token_sequences]
|
| 122 |
+
nomarker_sents = [gpt2_hop_tokenizer.decode(
|
| 123 |
+
toks) for toks in nomarker_token_sequences]
|
| 124 |
+
|
| 125 |
+
surprisal_df = pd.DataFrame({
|
| 126 |
+
"Sentences with Marker": marker_sents,
|
| 127 |
+
"Sentences without Marker": nomarker_sents,
|
| 128 |
+
})
|
| 129 |
+
|
| 130 |
+
BATCH_SIZE = 32
|
| 131 |
+
device = "cuda"
|
| 132 |
+
for ckpt in CHECKPOINTS:
|
| 133 |
+
print(f"Checkpoint: {ckpt}")
|
| 134 |
+
|
| 135 |
+
# Load model
|
| 136 |
+
if args.no_pos_encodings:
|
| 137 |
+
model = GPT2NoPositionalEncodingLMHeadModel.from_pretrained(
|
| 138 |
+
model_path + str(ckpt)).to(device)
|
| 139 |
+
else:
|
| 140 |
+
model = GPT2LMHeadModel.from_pretrained(
|
| 141 |
+
model_path + str(ckpt)).to(device)
|
| 142 |
+
|
| 143 |
+
# Init lists for tracking correct/wrong surprisals for each example
|
| 144 |
+
marker_token_surprisals = []
|
| 145 |
+
nomarker_token_surprisals = []
|
| 146 |
+
|
| 147 |
+
# Iterate over data in batches
|
| 148 |
+
for i in tqdm.tqdm(range(0, len(marker_token_sequences), BATCH_SIZE)):
|
| 149 |
+
|
| 150 |
+
# Extract data for batch and pad the sequences
|
| 151 |
+
marker_batch = marker_token_sequences[i:i+BATCH_SIZE]
|
| 152 |
+
correct_batch_padded = zip(
|
| 153 |
+
*zip_longest(*marker_batch, fillvalue=gpt2_hop_tokenizer.eos_token_id))
|
| 154 |
+
marker_batch_tensors = torch.tensor(
|
| 155 |
+
list(correct_batch_padded)).to(device)
|
| 156 |
+
|
| 157 |
+
# Do the same for wrong batch
|
| 158 |
+
nomarker_batch = nomarker_token_sequences[i:i+BATCH_SIZE]
|
| 159 |
+
nomarker_batch_padded = zip(
|
| 160 |
+
*zip_longest(*nomarker_batch, fillvalue=gpt2_hop_tokenizer.eos_token_id))
|
| 161 |
+
nomarker_batch_tensors = torch.tensor(
|
| 162 |
+
list(nomarker_batch_padded)).to(device)
|
| 163 |
+
|
| 164 |
+
# Get target indices in a batch
|
| 165 |
+
targets_batch = target_indices[i:i+BATCH_SIZE]
|
| 166 |
+
|
| 167 |
+
# Compute marker/nomarker surprisals for batches
|
| 168 |
+
marker_surprisal_sequences = compute_surprisals(
|
| 169 |
+
model, marker_batch_tensors)
|
| 170 |
+
nomarker_surprisal_sequences = compute_surprisals(
|
| 171 |
+
model, nomarker_batch_tensors)
|
| 172 |
+
|
| 173 |
+
# Extract surprisals for target token
|
| 174 |
+
for marker_seq, nomarker_seq, idx in \
|
| 175 |
+
zip(marker_surprisal_sequences, nomarker_surprisal_sequences, targets_batch):
|
| 176 |
+
marker_token_surprisals.append(marker_seq[idx])
|
| 177 |
+
nomarker_token_surprisals.append(nomarker_seq[idx])
|
| 178 |
+
|
| 179 |
+
# Add surprisals to df
|
| 180 |
+
ckpt_df = pd.DataFrame(
|
| 181 |
+
list(zip(marker_token_surprisals, nomarker_token_surprisals)),
|
| 182 |
+
columns=[f'Marker Token Surprisals (ckpt {ckpt})',
|
| 183 |
+
f'No Marker Token Surprisals (ckpt {ckpt})']
|
| 184 |
+
)
|
| 185 |
+
surprisal_df = pd.concat((surprisal_df, ckpt_df), axis=1)
|
| 186 |
+
|
| 187 |
+
# Write results to CSV
|
| 188 |
+
directory = f"hop_surprisal_results/{args.perturbation_type}_{args.train_set}{no_pos_encodings_underscore}"
|
| 189 |
+
if not os.path.exists(directory):
|
| 190 |
+
os.makedirs(directory)
|
| 191 |
+
file = directory + f"/{args.paren_model}_seed{args.random_seed}.csv"
|
| 192 |
+
print(f"Writing results to CSV: {file}")
|
| 193 |
+
surprisal_df.to_csv(file)
|
hop_surprisal/hop_surprisal.sh
ADDED
|
File without changes
|
hop_surprisal/hop_surprisal_gpt2.py
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# hop_surprisal.py
|
| 2 |
+
# Author: Yaning
|
| 3 |
+
|
| 4 |
+
# For importing utils
|
| 5 |
+
import sys
|
| 6 |
+
sys.path.append("..")
|
| 7 |
+
|
| 8 |
+
import os
|
| 9 |
+
import torch
|
| 10 |
+
import pandas as pd
|
| 11 |
+
import tqdm
|
| 12 |
+
import argparse
|
| 13 |
+
from numpy.random import default_rng
|
| 14 |
+
|
| 15 |
+
from itertools import zip_longest
|
| 16 |
+
from glob import glob
|
| 17 |
+
from transformers import AutoTokenizer, AutoModelForCausalLM, Trainer, TrainingArguments, DataCollatorForLanguageModeling
|
| 18 |
+
from utils_gpt2 import CHECKPOINT_READ_PATH, PERTURBATIONS, PAREN_MODELS, \
|
| 19 |
+
BABYLM_DATA_PATH, gpt2_hop_tokenizer, \
|
| 20 |
+
marker_sg_token, marker_pl_token, compute_surprisals
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
MODEL_NAME = "GPT2-Hop"
|
| 24 |
+
|
| 25 |
+
device = "cuda" if torch.cuda.is_available() else "cpu"
|
| 26 |
+
MAX_TRAINING_STEPS = 1500
|
| 27 |
+
# CHECKPOINTS = list(range(500, MAX_TRAINING_STEPS+1, 500))
|
| 28 |
+
CHECKPOINTS = [2241] # control 2241
|
| 29 |
+
|
| 30 |
+
if __name__ == "__main__":
|
| 31 |
+
|
| 32 |
+
parser = argparse.ArgumentParser(
|
| 33 |
+
prog='Get marker token surprisals for hop verb perturbations',
|
| 34 |
+
description='Marker token surprisals')
|
| 35 |
+
parser.add_argument('perturbation_type',
|
| 36 |
+
default='all',
|
| 37 |
+
const='all',
|
| 38 |
+
nargs='?',
|
| 39 |
+
choices=PERTURBATIONS.keys(),
|
| 40 |
+
help='Perturbation function used to transform BabyLM dataset')
|
| 41 |
+
parser.add_argument('train_set',
|
| 42 |
+
default='all',
|
| 43 |
+
const='all',
|
| 44 |
+
nargs='?',
|
| 45 |
+
choices=["100M", "10M"],
|
| 46 |
+
help='BabyLM train set')
|
| 47 |
+
|
| 48 |
+
parser.add_argument('random_seed', type=int, help="Random seed")
|
| 49 |
+
|
| 50 |
+
# Get args
|
| 51 |
+
args = parser.parse_args()
|
| 52 |
+
|
| 53 |
+
if "hop" not in args.perturbation_type:
|
| 54 |
+
raise Exception(
|
| 55 |
+
"'{args.perturbation_type}' is not a valid hop perturbation")
|
| 56 |
+
|
| 57 |
+
# Get path to model
|
| 58 |
+
# model = f"babylm_{args.perturbation_type}_{args.train_set}_{args.paren_model}{no_pos_encodings_underscore}_seed{args.random_seed}"
|
| 59 |
+
# model_path = f"{CHECKPOINT_READ_PATH}/babylm_{args.perturbation_type}_{args.train_set}_{args.paren_model}{no_pos_encodings_underscore}/{model}/runs/{model}/checkpoint-"
|
| 60 |
+
|
| 61 |
+
model_path = f'../train/checkpoints/{MODEL_NAME}/babylm_{args.perturbation_type}_10M_seed0/runs/checkpoint-'
|
| 62 |
+
|
| 63 |
+
# Get perturbed test files
|
| 64 |
+
# test_files = sorted(glob(BABYLM_DATA_PATH +
|
| 65 |
+
# "/babylm_data_perturbed/babylm_{}/babylm_test_affected/*".format(args.perturbation_type)))
|
| 66 |
+
test_files = sorted(glob("../data/Perturbed_data/gpt2/babylm_{}/babylm_test_affected/*".format(args.perturbation_type)))
|
| 67 |
+
print("test_files:", test_files)
|
| 68 |
+
|
| 69 |
+
EOS_TOKEN = gpt2_hop_tokenizer.eos_token_id
|
| 70 |
+
print("EOS_TOKEN:", EOS_TOKEN)
|
| 71 |
+
FILE_SAMPLE_SIZE = 500
|
| 72 |
+
MAX_SEQ_LEN = 512
|
| 73 |
+
rng = default_rng(args.random_seed)
|
| 74 |
+
|
| 75 |
+
marker_token_sequences = []
|
| 76 |
+
nomarker_token_sequences = []
|
| 77 |
+
target_indices = []
|
| 78 |
+
|
| 79 |
+
# Iterate over data files to get surprisal data
|
| 80 |
+
print("Sampling BabyLM affected test files to extract surprisals...")
|
| 81 |
+
for test_file in test_files:
|
| 82 |
+
print(test_file)
|
| 83 |
+
|
| 84 |
+
# Get tokens from test file (+ eos token), and subsample
|
| 85 |
+
f = open(test_file, 'r')
|
| 86 |
+
file_token_sequences = [
|
| 87 |
+
[int(s) for s in l.split()] + [EOS_TOKEN] for l in f.readlines()]
|
| 88 |
+
file_token_sequences = [
|
| 89 |
+
toks for toks in file_token_sequences if len(toks) < MAX_SEQ_LEN]
|
| 90 |
+
sample_indices = rng.choice(
|
| 91 |
+
list(range(len(file_token_sequences))), FILE_SAMPLE_SIZE, replace=False)
|
| 92 |
+
file_token_sequences = [file_token_sequences[i]
|
| 93 |
+
for i in sample_indices]
|
| 94 |
+
|
| 95 |
+
file_target_indices = []
|
| 96 |
+
file_nomarker_token_sequences = []
|
| 97 |
+
for tokens in file_token_sequences:
|
| 98 |
+
# Find index of first marker token for surprisal target
|
| 99 |
+
target_index = None
|
| 100 |
+
for idx in range(len(tokens)):
|
| 101 |
+
# print("marker_sg_token", marker_sg_token)
|
| 102 |
+
# print("marker_pl_token", marker_pl_token)
|
| 103 |
+
if tokens[idx] in (marker_sg_token, marker_pl_token):
|
| 104 |
+
target_index = idx
|
| 105 |
+
break
|
| 106 |
+
assert (target_index is not None)
|
| 107 |
+
|
| 108 |
+
# Make a version of tokens with marker removed at surprisal target
|
| 109 |
+
nomarker_tokens = tokens.copy()
|
| 110 |
+
nomarker_tokens.pop(target_index)
|
| 111 |
+
assert (tokens[:target_index] == nomarker_tokens[:target_index])
|
| 112 |
+
assert (tokens[target_index] in (marker_sg_token, marker_pl_token))
|
| 113 |
+
assert (tokens[target_index+1] == nomarker_tokens[target_index])
|
| 114 |
+
|
| 115 |
+
file_target_indices.append(target_index)
|
| 116 |
+
file_nomarker_token_sequences.append(nomarker_tokens)
|
| 117 |
+
|
| 118 |
+
marker_token_sequences.extend(file_token_sequences)
|
| 119 |
+
nomarker_token_sequences.extend(file_nomarker_token_sequences)
|
| 120 |
+
target_indices.extend(file_target_indices)
|
| 121 |
+
|
| 122 |
+
# For logging/debugging, include decoded sentence
|
| 123 |
+
marker_sents = [gpt2_hop_tokenizer.decode(
|
| 124 |
+
toks) for toks in marker_token_sequences]
|
| 125 |
+
nomarker_sents = [gpt2_hop_tokenizer.decode(
|
| 126 |
+
toks) for toks in nomarker_token_sequences]
|
| 127 |
+
|
| 128 |
+
surprisal_df = pd.DataFrame({
|
| 129 |
+
"Sentences with Marker": marker_sents,
|
| 130 |
+
"Sentences without Marker": nomarker_sents,
|
| 131 |
+
})
|
| 132 |
+
|
| 133 |
+
# BATCH_SIZE = 32
|
| 134 |
+
BATCH_SIZE = 32
|
| 135 |
+
for ckpt in CHECKPOINTS:
|
| 136 |
+
print(f"Checkpoint: {ckpt}")
|
| 137 |
+
|
| 138 |
+
model = AutoModelForCausalLM.from_pretrained(model_path + str(ckpt)).to(device)
|
| 139 |
+
# model = AutoModelForCausalLM.from_pretrained(model_path + str(ckpt))
|
| 140 |
+
orig_vocab_size = model.config.vocab_size
|
| 141 |
+
model.resize_token_embeddings(len(gpt2_hop_tokenizer))
|
| 142 |
+
# Init lists for tracking correct/wrong surprisals for each example
|
| 143 |
+
marker_token_surprisals = []
|
| 144 |
+
nomarker_token_surprisals = []
|
| 145 |
+
|
| 146 |
+
# Iterate over data in batches
|
| 147 |
+
for i in tqdm.tqdm(range(0, len(marker_token_sequences), BATCH_SIZE)):
|
| 148 |
+
|
| 149 |
+
# Extract data for batch and pad the sequences
|
| 150 |
+
marker_batch = marker_token_sequences[i:i+BATCH_SIZE]
|
| 151 |
+
correct_batch_padded = zip(
|
| 152 |
+
*zip_longest(*marker_batch, fillvalue=gpt2_hop_tokenizer.eos_token_id))
|
| 153 |
+
# marker_batch_tensors = torch.tensor(
|
| 154 |
+
# list(correct_batch_padded))
|
| 155 |
+
marker_batch_tensors = torch.tensor(
|
| 156 |
+
list(correct_batch_padded)).to(device)
|
| 157 |
+
|
| 158 |
+
# Do the same for wrong batch
|
| 159 |
+
nomarker_batch = nomarker_token_sequences[i:i+BATCH_SIZE]
|
| 160 |
+
nomarker_batch_padded = zip(
|
| 161 |
+
*zip_longest(*nomarker_batch, fillvalue=gpt2_hop_tokenizer.eos_token_id))
|
| 162 |
+
# nomarker_batch_tensors = torch.tensor(
|
| 163 |
+
# list(nomarker_batch_padded))
|
| 164 |
+
nomarker_batch_tensors = torch.tensor(
|
| 165 |
+
list(nomarker_batch_padded)).to(device)
|
| 166 |
+
|
| 167 |
+
# Get target indices in a batch
|
| 168 |
+
targets_batch = target_indices[i:i+BATCH_SIZE]
|
| 169 |
+
|
| 170 |
+
# Compute marker/nomarker surprisals for batches
|
| 171 |
+
marker_surprisal_sequences = compute_surprisals(
|
| 172 |
+
model, marker_batch_tensors)
|
| 173 |
+
nomarker_surprisal_sequences = compute_surprisals(
|
| 174 |
+
model, nomarker_batch_tensors)
|
| 175 |
+
|
| 176 |
+
# Extract surprisals for target token
|
| 177 |
+
for marker_seq, nomarker_seq, idx in \
|
| 178 |
+
zip(marker_surprisal_sequences, nomarker_surprisal_sequences, targets_batch):
|
| 179 |
+
marker_token_surprisals.append(marker_seq[idx])
|
| 180 |
+
nomarker_token_surprisals.append(nomarker_seq[idx])
|
| 181 |
+
|
| 182 |
+
# Add surprisals to df
|
| 183 |
+
ckpt_df = pd.DataFrame(
|
| 184 |
+
list(zip(marker_token_surprisals, nomarker_token_surprisals)),
|
| 185 |
+
columns=[f'Marker Token Surprisals (ckpt {ckpt})',
|
| 186 |
+
f'No Marker Token Surprisals (ckpt {ckpt})']
|
| 187 |
+
)
|
| 188 |
+
surprisal_df = pd.concat((surprisal_df, ckpt_df), axis=1)
|
| 189 |
+
|
| 190 |
+
# Write results to CSV
|
| 191 |
+
directory = os.path.join(
|
| 192 |
+
"hop_surprisal_results",
|
| 193 |
+
MODEL_NAME,
|
| 194 |
+
f"{args.perturbation_type}_{args.train_set}"
|
| 195 |
+
)
|
| 196 |
+
|
| 197 |
+
os.makedirs(directory, exist_ok=True)
|
| 198 |
+
|
| 199 |
+
filename = f"{args.perturbation_type}_{args.train_set}_seed{args.random_seed}.csv"
|
| 200 |
+
file_path = os.path.join(directory, filename)
|
| 201 |
+
|
| 202 |
+
print(f"The file saves as to: {file_path}")
|
| 203 |
+
surprisal_df.to_csv(file_path, index=False)
|
| 204 |
+
|
hop_surprisal/hop_surprisal_llama3.py
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
wwwwwww# hop_surprisal.py
|
| 2 |
+
# Author: Yaning
|
| 3 |
+
|
| 4 |
+
# For importing utils
|
| 5 |
+
import sys
|
| 6 |
+
sys.path.append("..")
|
| 7 |
+
|
| 8 |
+
import os
|
| 9 |
+
import torch
|
| 10 |
+
import pandas as pd
|
| 11 |
+
import tqdm
|
| 12 |
+
import argparse
|
| 13 |
+
from numpy.random import default_rng
|
| 14 |
+
|
| 15 |
+
from itertools import zip_longest
|
| 16 |
+
from glob import glob
|
| 17 |
+
from transformers import AutoTokenizer, AutoModelForCausalLM, Trainer, TrainingArguments, DataCollatorForLanguageModeling
|
| 18 |
+
from utils_llama import CHECKPOINT_READ_PATH, PERTURBATIONS, PAREN_MODELS, \
|
| 19 |
+
BABYLM_DATA_PATH, gpt2_hop_tokenizer, \
|
| 20 |
+
marker_sg_token, marker_pl_token, compute_surprisals
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
# MODEL_NAME = "GPT-2"
|
| 24 |
+
MODEL_NAME = "meta-llama/Llama-3.2-3B"
|
| 25 |
+
|
| 26 |
+
MAX_TRAINING_STEPS = 1500
|
| 27 |
+
# CHECKPOINTS = list(range(500, MAX_TRAINING_STEPS+1, 500))
|
| 28 |
+
CHECKPOINTS = [2376]
|
| 29 |
+
|
| 30 |
+
if __name__ == "__main__":
|
| 31 |
+
|
| 32 |
+
parser = argparse.ArgumentParser(
|
| 33 |
+
prog='Get marker token surprisals for hop verb perturbations',
|
| 34 |
+
description='Marker token surprisals')
|
| 35 |
+
parser.add_argument('perturbation_type',
|
| 36 |
+
default='all',
|
| 37 |
+
const='all',
|
| 38 |
+
nargs='?',
|
| 39 |
+
choices=PERTURBATIONS.keys(),
|
| 40 |
+
help='Perturbation function used to transform BabyLM dataset')
|
| 41 |
+
parser.add_argument('train_set',
|
| 42 |
+
default='all',
|
| 43 |
+
const='all',
|
| 44 |
+
nargs='?',
|
| 45 |
+
choices=["100M", "10M"],
|
| 46 |
+
help='BabyLM train set')
|
| 47 |
+
|
| 48 |
+
parser.add_argument('random_seed', type=int, help="Random seed")
|
| 49 |
+
|
| 50 |
+
# Get args
|
| 51 |
+
args = parser.parse_args()
|
| 52 |
+
|
| 53 |
+
if "hop" not in args.perturbation_type:
|
| 54 |
+
raise Exception(
|
| 55 |
+
"'{args.perturbation_type}' is not a valid hop perturbation")
|
| 56 |
+
|
| 57 |
+
# Get path to model
|
| 58 |
+
# model = f"babylm_{args.perturbation_type}_{args.train_set}_{args.paren_model}{no_pos_encodings_underscore}_seed{args.random_seed}"
|
| 59 |
+
# model_path = f"{CHECKPOINT_READ_PATH}/babylm_{args.perturbation_type}_{args.train_set}_{args.paren_model}{no_pos_encodings_underscore}/{model}/runs/{model}/checkpoint-"
|
| 60 |
+
|
| 61 |
+
model_path = f'../train/checkpoints/{MODEL_NAME}/babylm_{args.perturbation_type}_10M_seed0/runs/checkpoint-'
|
| 62 |
+
|
| 63 |
+
# Get perturbed test files
|
| 64 |
+
# test_files = sorted(glob(BABYLM_DATA_PATH +
|
| 65 |
+
# "/babylm_data_perturbed/babylm_{}/babylm_test_affected/*".format(args.perturbation_type)))
|
| 66 |
+
test_files = sorted(glob("../data/Perturbed_data/gpt2/babylm_{}/babylm_test_affected/*".format(args.perturbation_type)))
|
| 67 |
+
print("test_files:", test_files)
|
| 68 |
+
|
| 69 |
+
EOS_TOKEN = gpt2_hop_tokenizer.eos_token_id
|
| 70 |
+
print("EOS_TOKEN:", EOS_TOKEN)
|
| 71 |
+
FILE_SAMPLE_SIZE = 500
|
| 72 |
+
MAX_SEQ_LEN = 512
|
| 73 |
+
rng = default_rng(args.random_seed)
|
| 74 |
+
|
| 75 |
+
marker_token_sequences = []
|
| 76 |
+
nomarker_token_sequences = []
|
| 77 |
+
target_indices = []
|
| 78 |
+
|
| 79 |
+
# Iterate over data files to get surprisal data
|
| 80 |
+
print("Sampling BabyLM affected test files to extract surprisals...")
|
| 81 |
+
for test_file in test_files:
|
| 82 |
+
print(test_file)
|
| 83 |
+
|
| 84 |
+
# Get tokens from test file (+ eos token), and subsample
|
| 85 |
+
f = open(test_file, 'r')
|
| 86 |
+
file_token_sequences = [
|
| 87 |
+
[int(s) for s in l.split()] + [EOS_TOKEN] for l in f.readlines()]
|
| 88 |
+
file_token_sequences = [
|
| 89 |
+
toks for toks in file_token_sequences if len(toks) < MAX_SEQ_LEN]
|
| 90 |
+
sample_indices = rng.choice(
|
| 91 |
+
list(range(len(file_token_sequences))), FILE_SAMPLE_SIZE, replace=False)
|
| 92 |
+
file_token_sequences = [file_token_sequences[i]
|
| 93 |
+
for i in sample_indices]
|
| 94 |
+
|
| 95 |
+
file_target_indices = []
|
| 96 |
+
file_nomarker_token_sequences = []
|
| 97 |
+
for tokens in file_token_sequences:
|
| 98 |
+
# Find index of first marker token for surprisal target
|
| 99 |
+
target_index = None
|
| 100 |
+
for idx in range(len(tokens)):
|
| 101 |
+
# print("marker_sg_token", marker_sg_token)
|
| 102 |
+
# print("marker_pl_token", marker_pl_token)
|
| 103 |
+
if tokens[idx] in (marker_sg_token, marker_pl_token):
|
| 104 |
+
target_index = idx
|
| 105 |
+
break
|
| 106 |
+
assert (target_index is not None)
|
| 107 |
+
|
| 108 |
+
# Make a version of tokens with marker removed at surprisal target
|
| 109 |
+
nomarker_tokens = tokens.copy()
|
| 110 |
+
nomarker_tokens.pop(target_index)
|
| 111 |
+
assert (tokens[:target_index] == nomarker_tokens[:target_index])
|
| 112 |
+
assert (tokens[target_index] in (marker_sg_token, marker_pl_token))
|
| 113 |
+
assert (tokens[target_index+1] == nomarker_tokens[target_index])
|
| 114 |
+
|
| 115 |
+
file_target_indices.append(target_index)
|
| 116 |
+
file_nomarker_token_sequences.append(nomarker_tokens)
|
| 117 |
+
|
| 118 |
+
marker_token_sequences.extend(file_token_sequences)
|
| 119 |
+
nomarker_token_sequences.extend(file_nomarker_token_sequences)
|
| 120 |
+
target_indices.extend(file_target_indices)
|
| 121 |
+
|
| 122 |
+
# For logging/debugging, include decoded sentence
|
| 123 |
+
marker_sents = [gpt2_hop_tokenizer.decode(
|
| 124 |
+
toks) for toks in marker_token_sequences]
|
| 125 |
+
nomarker_sents = [gpt2_hop_tokenizer.decode(
|
| 126 |
+
toks) for toks in nomarker_token_sequences]
|
| 127 |
+
|
| 128 |
+
surprisal_df = pd.DataFrame({
|
| 129 |
+
"Sentences with Marker": marker_sents,
|
| 130 |
+
"Sentences without Marker": nomarker_sents,
|
| 131 |
+
})
|
| 132 |
+
|
| 133 |
+
# BATCH_SIZE = 32
|
| 134 |
+
BATCH_SIZE = 32
|
| 135 |
+
device = "cuda"
|
| 136 |
+
for ckpt in CHECKPOINTS:
|
| 137 |
+
print(f"Checkpoint: {ckpt}")
|
| 138 |
+
|
| 139 |
+
# model = AutoModelForCausalLM.from_pretrained(model_path + str(ckpt)).to(device)
|
| 140 |
+
model = AutoModelForCausalLM.from_pretrained(model_path + str(ckpt))
|
| 141 |
+
orig_vocab_size = model.config.vocab_size
|
| 142 |
+
model.resize_token_embeddings(len(gpt2_hop_tokenizer))
|
| 143 |
+
# Init lists for tracking correct/wrong surprisals for each example
|
| 144 |
+
marker_token_surprisals = []
|
| 145 |
+
nomarker_token_surprisals = []
|
| 146 |
+
|
| 147 |
+
# Iterate over data in batches
|
| 148 |
+
for i in tqdm.tqdm(range(0, len(marker_token_sequences), BATCH_SIZE)):
|
| 149 |
+
|
| 150 |
+
# Extract data for batch and pad the sequences
|
| 151 |
+
marker_batch = marker_token_sequences[i:i+BATCH_SIZE]
|
| 152 |
+
correct_batch_padded = zip(
|
| 153 |
+
*zip_longest(*marker_batch, fillvalue=gpt2_hop_tokenizer.eos_token_id))
|
| 154 |
+
marker_batch_tensors = torch.tensor(
|
| 155 |
+
list(correct_batch_padded))
|
| 156 |
+
# marker_batch_tensors = torch.tensor(
|
| 157 |
+
# list(correct_batch_padded)).to(device)
|
| 158 |
+
|
| 159 |
+
# Do the same for wrong batch
|
| 160 |
+
nomarker_batch = nomarker_token_sequences[i:i+BATCH_SIZE]
|
| 161 |
+
nomarker_batch_padded = zip(
|
| 162 |
+
*zip_longest(*nomarker_batch, fillvalue=gpt2_hop_tokenizer.eos_token_id))
|
| 163 |
+
nomarker_batch_tensors = torch.tensor(
|
| 164 |
+
list(nomarker_batch_padded))
|
| 165 |
+
# nomarker_batch_tensors = torch.tensor(
|
| 166 |
+
# list(nomarker_batch_padded)).to(device)
|
| 167 |
+
|
| 168 |
+
# Get target indices in a batch
|
| 169 |
+
targets_batch = target_indices[i:i+BATCH_SIZE]
|
| 170 |
+
|
| 171 |
+
# Compute marker/nomarker surprisals for batches
|
| 172 |
+
marker_surprisal_sequences = compute_surprisals(
|
| 173 |
+
model, marker_batch_tensors)
|
| 174 |
+
nomarker_surprisal_sequences = compute_surprisals(
|
| 175 |
+
model, nomarker_batch_tensors)
|
| 176 |
+
|
| 177 |
+
# Extract surprisals for target token
|
| 178 |
+
for marker_seq, nomarker_seq, idx in \
|
| 179 |
+
zip(marker_surprisal_sequences, nomarker_surprisal_sequences, targets_batch):
|
| 180 |
+
marker_token_surprisals.append(marker_seq[idx])
|
| 181 |
+
nomarker_token_surprisals.append(nomarker_seq[idx])
|
| 182 |
+
|
| 183 |
+
# Add surprisals to df
|
| 184 |
+
ckpt_df = pd.DataFrame(
|
| 185 |
+
list(zip(marker_token_surprisals, nomarker_token_surprisals)),
|
| 186 |
+
columns=[f'Marker Token Surprisals (ckpt {ckpt})',
|
| 187 |
+
f'No Marker Token Surprisals (ckpt {ckpt})']
|
| 188 |
+
)
|
| 189 |
+
surprisal_df = pd.concat((surprisal_df, ckpt_df), axis=1)
|
| 190 |
+
|
| 191 |
+
# Write results to CSV
|
| 192 |
+
directory = os.path.join(
|
| 193 |
+
"hop_surprisal_results",
|
| 194 |
+
MODEL_NAME,
|
| 195 |
+
f"{args.perturbation_type}_{args.train_set}"
|
| 196 |
+
)
|
| 197 |
+
|
| 198 |
+
os.makedirs(directory, exist_ok=True)
|
| 199 |
+
|
| 200 |
+
filename = f"{args.perturbation_type}_{args.train_set}_seed{args.random_seed}.csv"
|
| 201 |
+
file_path = os.path.join(directory, filename)
|
| 202 |
+
|
| 203 |
+
print(f"The file saves as to: {file_path}")
|
| 204 |
+
surprisal_df.to_csv(file_path, index=False)
|
| 205 |
+
|
hop_surprisal/hop_surprisal_results/GPT-2/hop_words4_10M/hop_words4_10M_seed0.csv
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
hop_surprisal/hop_surprisal_results/Qwen/0.5B/hop_control.csv
ADDED
|
File without changes
|
hop_surprisal/hop_surprisal_results/Qwen/0.5B/hop_control_10M_seed0.csv
ADDED
|
File without changes
|
hop_surprisal/hop_surprisal_results/Qwen/0.5B/hop_words4.csv
ADDED
|
File without changes
|
hop_surprisal/hop_surprisal_results/Qwen/0.5B/hop_words4_10M_seed0.csv
ADDED
|
File without changes
|
hop_surprisal/hop_surprisal_results/Qwen/1.5B/hop_control.csv
ADDED
|
File without changes
|
hop_surprisal/hop_surprisal_results/Qwen/1.5B/hop_control_10M_seed0.csv
ADDED
|
File without changes
|
hop_surprisal/hop_surprisal_results/Qwen/1.5B/hop_words4.csv
ADDED
|
File without changes
|
hop_surprisal/hop_surprisal_results/Qwen/1.5B/hop_words4_10M_seed0.csv
ADDED
|
File without changes
|
hop_surprisal/hop_surprisal_results/Qwen/3B/hop_control.csv
ADDED
|
File without changes
|
hop_surprisal/hop_surprisal_results/Qwen/3B/hop_words4.csv
ADDED
|
File without changes
|
hop_surprisal/hop_surprisal_results/Qwen/3B/hop_words4_10M_seed0.csv
ADDED
|
File without changes
|
hop_surprisal/hop_surprisal_results/Qwen/7B/hop_control.csv
ADDED
|
File without changes
|
hop_surprisal/hop_surprisal_results/Qwen/7B/hop_control_10M_seed0.csv
ADDED
|
File without changes
|
hop_surprisal/hop_surprisal_results/Qwen/7B/hop_words4.csv
ADDED
|
File without changes
|
hop_surprisal/hop_surprisal_results/Qwen/7B/hop_words4_10M_seed0.csv
ADDED
|
File without changes
|
impossible_llm.yaml
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: impossible_llm
|
| 2 |
+
channels:
|
| 3 |
+
- defaults
|
| 4 |
+
dependencies:
|
| 5 |
+
- _libgcc_mutex=0.1=main
|
| 6 |
+
- _openmp_mutex=5.1=1_gnu
|
| 7 |
+
- ca-certificates=2024.7.2=h06a4308_0
|
| 8 |
+
- ld_impl_linux-64=2.38=h1181459_1
|
| 9 |
+
- libffi=3.4.4=h6a678d5_1
|
| 10 |
+
- libgcc-ng=11.2.0=h1234567_1
|
| 11 |
+
- libgomp=11.2.0=h1234567_1
|
| 12 |
+
- libstdcxx-ng=11.2.0=h1234567_1
|
| 13 |
+
- ncurses=6.4=h6a678d5_0
|
| 14 |
+
- openssl=3.0.15=h5eee18b_0
|
| 15 |
+
- pip=24.2=py39h06a4308_0
|
| 16 |
+
- python=3.9.19=h955ad1f_1
|
| 17 |
+
- readline=8.2=h5eee18b_0
|
| 18 |
+
- setuptools=72.1.0=py39h06a4308_0
|
| 19 |
+
- sqlite=3.45.3=h5eee18b_0
|
| 20 |
+
- tk=8.6.14=h39e8969_0
|
| 21 |
+
- wheel=0.44.0=py39h06a4308_0
|
| 22 |
+
- xz=5.4.6=h5eee18b_1
|
| 23 |
+
- zlib=1.2.13=h5eee18b_1
|
| 24 |
+
- pip:
|
| 25 |
+
- accelerate==0.34.2
|
| 26 |
+
- aiohappyeyeballs==2.4.2
|
| 27 |
+
- aiohttp==3.10.6
|
| 28 |
+
- aiosignal==1.3.1
|
| 29 |
+
- asttokens==2.2.1
|
| 30 |
+
- async-timeout==4.0.3
|
| 31 |
+
- attrs==24.2.0
|
| 32 |
+
- backcall==0.2.0
|
| 33 |
+
- black==24.8.0
|
| 34 |
+
- blessed==1.20.0
|
| 35 |
+
- certifi==2023.11.17
|
| 36 |
+
- charset-normalizer==3.3.2
|
| 37 |
+
- click==8.1.7
|
| 38 |
+
- cmake==3.30.3
|
| 39 |
+
- comm==0.1.2
|
| 40 |
+
- contourpy==1.2.0
|
| 41 |
+
- cycler==0.12.1
|
| 42 |
+
- data==0.4
|
| 43 |
+
- datasets==3.0.1
|
| 44 |
+
- debugpy==1.6.7
|
| 45 |
+
- decorator==5.1.1
|
| 46 |
+
- dill==0.3.8
|
| 47 |
+
- emoji==2.8.0
|
| 48 |
+
- exceptiongroup==1.1.0
|
| 49 |
+
- executing==1.2.0
|
| 50 |
+
- filelock==3.12.2
|
| 51 |
+
- fonttools==4.45.1
|
| 52 |
+
- frozenlist==1.4.1
|
| 53 |
+
- fsspec==2023.10.0
|
| 54 |
+
- funcsigs==1.0.2
|
| 55 |
+
- future==0.18.3
|
| 56 |
+
- gmpy2==2.1.0
|
| 57 |
+
- gpustat==1.1.1
|
| 58 |
+
- huggingface-hub==0.25.0
|
| 59 |
+
- idna==3.6
|
| 60 |
+
- importlib-metadata==6.0.0
|
| 61 |
+
- importlib-resources==6.4.5
|
| 62 |
+
- iniconfig==2.0.0
|
| 63 |
+
- ipykernel==6.23.1
|
| 64 |
+
- ipython==8.0.0
|
| 65 |
+
- jedi==0.18.2
|
| 66 |
+
- jinja2==3.1.2
|
| 67 |
+
- joblib==1.3.2
|
| 68 |
+
- jupyter-client==8.1.0
|
| 69 |
+
- jupyter-core==5.1.0
|
| 70 |
+
- kiwisolver==1.4.5
|
| 71 |
+
- latex==0.7.0
|
| 72 |
+
- latexcodec==1.0.0
|
| 73 |
+
- lit==18.1.8
|
| 74 |
+
- markupsafe==2.1.2
|
| 75 |
+
- matplotlib==3.8.2
|
| 76 |
+
- matplotlib-inline==0.1.6
|
| 77 |
+
- mizani==0.9.3
|
| 78 |
+
- mpmath==1.2.1
|
| 79 |
+
- multidict==6.1.0
|
| 80 |
+
- multiprocess==0.70.16
|
| 81 |
+
- mypy-extensions==1.0.0
|
| 82 |
+
- nest-asyncio==1.5.6
|
| 83 |
+
- networkx==2.8.6
|
| 84 |
+
- nltk==3.8.1
|
| 85 |
+
- numpy==1.26.2
|
| 86 |
+
- nvidia-cublas-cu11==11.10.3.66
|
| 87 |
+
- nvidia-cuda-cupti-cu11==11.7.101
|
| 88 |
+
- nvidia-cuda-nvrtc-cu11==11.7.99
|
| 89 |
+
- nvidia-cuda-runtime-cu11==11.7.99
|
| 90 |
+
- nvidia-cudnn-cu11==8.5.0.96
|
| 91 |
+
- nvidia-cufft-cu11==10.9.0.58
|
| 92 |
+
- nvidia-curand-cu11==10.2.10.91
|
| 93 |
+
- nvidia-cusolver-cu11==11.4.0.1
|
| 94 |
+
- nvidia-cusparse-cu11==11.7.4.91
|
| 95 |
+
- nvidia-ml-py==12.560.30
|
| 96 |
+
- nvidia-nccl-cu11==2.14.3
|
| 97 |
+
- nvidia-nvtx-cu11==11.7.91
|
| 98 |
+
- packaging==23.0
|
| 99 |
+
- pandas==2.1.3
|
| 100 |
+
- parso==0.8.3
|
| 101 |
+
- pathspec==0.12.1
|
| 102 |
+
- patsy==0.5.3
|
| 103 |
+
- peft==0.13.0
|
| 104 |
+
- pexpect==4.8.0
|
| 105 |
+
- pickleshare==0.7.5
|
| 106 |
+
- pillow==10.1.0
|
| 107 |
+
- platformdirs==2.5.2
|
| 108 |
+
- plotnine==0.12.4
|
| 109 |
+
- pluggy==1.3.0
|
| 110 |
+
- pluralizer==1.2.0
|
| 111 |
+
- prompt-toolkit==3.0.30
|
| 112 |
+
- protobuf==4.25.1
|
| 113 |
+
- psutil==5.9.1
|
| 114 |
+
- ptyprocess==0.7.0
|
| 115 |
+
- pure-eval==0.2.2
|
| 116 |
+
- pyarrow==17.0.0
|
| 117 |
+
- pygments==2.15.0
|
| 118 |
+
- pyparsing==3.1.1
|
| 119 |
+
- pytest==7.4.3
|
| 120 |
+
- python-dateutil==2.8.2
|
| 121 |
+
- pytz==2023.3.post1
|
| 122 |
+
- pyyaml==6.0.1
|
| 123 |
+
- pyzmq==23.0.0
|
| 124 |
+
- regex==2023.10.3
|
| 125 |
+
- requests==2.32.3
|
| 126 |
+
- safetensors==0.4.5
|
| 127 |
+
- scikit-learn==1.3.2
|
| 128 |
+
- scipy==1.11.4
|
| 129 |
+
- seaborn==0.13.0
|
| 130 |
+
- sentencepiece==0.2.0
|
| 131 |
+
- shutilwhich==1.1.0
|
| 132 |
+
- six==1.16.0
|
| 133 |
+
- stack-data==0.6.0
|
| 134 |
+
- stanza==1.9.2
|
| 135 |
+
- statsmodels==0.14.0
|
| 136 |
+
- sympy==1.11.1
|
| 137 |
+
- tempdir==0.7.1
|
| 138 |
+
- threadpoolctl==3.2.0
|
| 139 |
+
- tokenizers==0.20.0
|
| 140 |
+
- tomli==2.0.1
|
| 141 |
+
- torch==2.0.0
|
| 142 |
+
- tornado==6.2
|
| 143 |
+
- tqdm==4.66.5
|
| 144 |
+
- traitlets==5.7.1
|
| 145 |
+
- transformers==4.45.1
|
| 146 |
+
- triton==2.0.0
|
| 147 |
+
- typing-extensions==4.6.0
|
| 148 |
+
- tzdata==2023.3
|
| 149 |
+
- urllib3==2.1.0
|
| 150 |
+
- wcwidth==0.2.5
|
| 151 |
+
- xxhash==3.5.0
|
| 152 |
+
- yarl==1.13.0
|
| 153 |
+
- zipp==3.12.0
|
| 154 |
+
prefix: /home/yiren/new_ssd2/chunhui/miniconda/envs/impossible_llm
|
impossible_llm_update.yaml
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: impossible_llm
|
| 2 |
+
channels:
|
| 3 |
+
- defaults
|
| 4 |
+
dependencies:
|
| 5 |
+
- _libgcc_mutex=0.1=main
|
| 6 |
+
- _openmp_mutex=5.1=1_gnu
|
| 7 |
+
- ca-certificates=2024.7.2=h06a4308_0
|
| 8 |
+
- ld_impl_linux-64=2.38=h1181459_1
|
| 9 |
+
- libffi=3.4.4=h6a678d5_1
|
| 10 |
+
- libgcc-ng=11.2.0=h1234567_1
|
| 11 |
+
- libgomp=11.2.0=h1234567_1
|
| 12 |
+
- libstdcxx-ng=11.2.0=h1234567_1
|
| 13 |
+
- ncurses=6.4=h6a678d5_0
|
| 14 |
+
- openssl=3.0.15=h5eee18b_0
|
| 15 |
+
- pip=24.2=py39h06a4308_0
|
| 16 |
+
- python=3.9.19=h955ad1f_1
|
| 17 |
+
- readline=8.2=h5eee18b_0
|
| 18 |
+
- setuptools=72.1.0=py39h06a4308_0
|
| 19 |
+
- sqlite=3.45.3=h5eee18b_0
|
| 20 |
+
- tk=8.6.14=h39e8969_0
|
| 21 |
+
- wheel=0.44.0=py39h06a4308_0
|
| 22 |
+
- xz=5.4.6=h5eee18b_1
|
| 23 |
+
- zlib=1.2.13=h5eee18b_1
|
| 24 |
+
- pip:
|
| 25 |
+
- accelerate==1.0.0
|
| 26 |
+
- aiohappyeyeballs==2.4.2
|
| 27 |
+
- aiohttp==3.10.6
|
| 28 |
+
- aiosignal==1.3.1
|
| 29 |
+
- annotated-types==0.7.0
|
| 30 |
+
- asttokens==2.2.1
|
| 31 |
+
- async-timeout==4.0.3
|
| 32 |
+
- attrs==24.2.0
|
| 33 |
+
- backcall==0.2.0
|
| 34 |
+
- black==24.8.0
|
| 35 |
+
- blessed==1.20.0
|
| 36 |
+
- certifi==2023.11.17
|
| 37 |
+
- charset-normalizer==3.3.2
|
| 38 |
+
- click==8.1.7
|
| 39 |
+
- cmake==3.30.3
|
| 40 |
+
- comm==0.1.2
|
| 41 |
+
- contourpy==1.2.0
|
| 42 |
+
- cycler==0.12.1
|
| 43 |
+
- data==0.4
|
| 44 |
+
- datasets==3.0.1
|
| 45 |
+
- debugpy==1.6.7
|
| 46 |
+
- decorator==5.1.1
|
| 47 |
+
- deepspeed==0.15.2
|
| 48 |
+
- dill==0.3.8
|
| 49 |
+
- emoji==2.8.0
|
| 50 |
+
- exceptiongroup==1.1.0
|
| 51 |
+
- executing==1.2.0
|
| 52 |
+
- filelock==3.12.2
|
| 53 |
+
- fonttools==4.45.1
|
| 54 |
+
- frozenlist==1.4.1
|
| 55 |
+
- fsspec==2023.10.0
|
| 56 |
+
- funcsigs==1.0.2
|
| 57 |
+
- future==0.18.3
|
| 58 |
+
- gmpy2==2.1.0
|
| 59 |
+
- gpustat==1.1.1
|
| 60 |
+
- hjson==3.1.0
|
| 61 |
+
- huggingface-hub==0.25.0
|
| 62 |
+
- idna==3.6
|
| 63 |
+
- importlib-metadata==6.0.0
|
| 64 |
+
- importlib-resources==6.4.5
|
| 65 |
+
- iniconfig==2.0.0
|
| 66 |
+
- ipykernel==6.23.1
|
| 67 |
+
- ipython==8.0.0
|
| 68 |
+
- jedi==0.18.2
|
| 69 |
+
- jinja2==3.1.2
|
| 70 |
+
- joblib==1.3.2
|
| 71 |
+
- jupyter-client==8.1.0
|
| 72 |
+
- jupyter-core==5.1.0
|
| 73 |
+
- kiwisolver==1.4.5
|
| 74 |
+
- latex==0.7.0
|
| 75 |
+
- latexcodec==1.0.0
|
| 76 |
+
- lit==18.1.8
|
| 77 |
+
- markupsafe==2.1.2
|
| 78 |
+
- matplotlib==3.8.2
|
| 79 |
+
- matplotlib-inline==0.1.6
|
| 80 |
+
- mizani==0.9.3
|
| 81 |
+
- mpmath==1.2.1
|
| 82 |
+
- msgpack==1.1.0
|
| 83 |
+
- multidict==6.1.0
|
| 84 |
+
- multiprocess==0.70.16
|
| 85 |
+
- mypy-extensions==1.0.0
|
| 86 |
+
- nest-asyncio==1.5.6
|
| 87 |
+
- networkx==2.8.6
|
| 88 |
+
- ninja==1.11.1.1
|
| 89 |
+
- nltk==3.8.1
|
| 90 |
+
- numpy==1.26.2
|
| 91 |
+
- nvidia-cublas-cu11==11.10.3.66
|
| 92 |
+
- nvidia-cuda-cupti-cu11==11.7.101
|
| 93 |
+
- nvidia-cuda-nvrtc-cu11==11.7.99
|
| 94 |
+
- nvidia-cuda-runtime-cu11==11.7.99
|
| 95 |
+
- nvidia-cudnn-cu11==8.5.0.96
|
| 96 |
+
- nvidia-cufft-cu11==10.9.0.58
|
| 97 |
+
- nvidia-curand-cu11==10.2.10.91
|
| 98 |
+
- nvidia-cusolver-cu11==11.4.0.1
|
| 99 |
+
- nvidia-cusparse-cu11==11.7.4.91
|
| 100 |
+
- nvidia-ml-py==12.560.30
|
| 101 |
+
- nvidia-nccl-cu11==2.14.3
|
| 102 |
+
- nvidia-nvtx-cu11==11.7.91
|
| 103 |
+
- packaging==23.0
|
| 104 |
+
- pandas==2.1.3
|
| 105 |
+
- parso==0.8.3
|
| 106 |
+
- pathspec==0.12.1
|
| 107 |
+
- patsy==0.5.3
|
| 108 |
+
- peft==0.13.0
|
| 109 |
+
- pexpect==4.8.0
|
| 110 |
+
- pickleshare==0.7.5
|
| 111 |
+
- pillow==10.1.0
|
| 112 |
+
- platformdirs==2.5.2
|
| 113 |
+
- plotnine==0.12.4
|
| 114 |
+
- pluggy==1.3.0
|
| 115 |
+
- pluralizer==1.2.0
|
| 116 |
+
- prompt-toolkit==3.0.30
|
| 117 |
+
- protobuf==4.25.1
|
| 118 |
+
- psutil==5.9.1
|
| 119 |
+
- ptyprocess==0.7.0
|
| 120 |
+
- pure-eval==0.2.2
|
| 121 |
+
- py-cpuinfo==9.0.0
|
| 122 |
+
- pyarrow==17.0.0
|
| 123 |
+
- pydantic==2.9.2
|
| 124 |
+
- pydantic-core==2.23.4
|
| 125 |
+
- pygments==2.15.0
|
| 126 |
+
- pyparsing==3.1.1
|
| 127 |
+
- pytest==7.4.3
|
| 128 |
+
- python-dateutil==2.8.2
|
| 129 |
+
- pytz==2023.3.post1
|
| 130 |
+
- pyyaml==6.0.1
|
| 131 |
+
- pyzmq==23.0.0
|
| 132 |
+
- regex==2023.10.3
|
| 133 |
+
- requests==2.32.3
|
| 134 |
+
- safetensors==0.4.5
|
| 135 |
+
- scikit-learn==1.3.2
|
| 136 |
+
- scipy==1.11.4
|
| 137 |
+
- seaborn==0.13.0
|
| 138 |
+
- sentencepiece==0.2.0
|
| 139 |
+
- shutilwhich==1.1.0
|
| 140 |
+
- six==1.16.0
|
| 141 |
+
- stack-data==0.6.0
|
| 142 |
+
- stanza==1.9.2
|
| 143 |
+
- statsmodels==0.14.0
|
| 144 |
+
- sympy==1.11.1
|
| 145 |
+
- tempdir==0.7.1
|
| 146 |
+
- threadpoolctl==3.2.0
|
| 147 |
+
- tokenizers==0.20.0
|
| 148 |
+
- tomli==2.0.1
|
| 149 |
+
- torch==2.0.0
|
| 150 |
+
- tornado==6.2
|
| 151 |
+
- tqdm==4.66.5
|
| 152 |
+
- traitlets==5.7.1
|
| 153 |
+
- transformers==4.45.1
|
| 154 |
+
- triton==2.0.0
|
| 155 |
+
- typing-extensions==4.12.2
|
| 156 |
+
- tzdata==2023.3
|
| 157 |
+
- urllib3==2.1.0
|
| 158 |
+
- wcwidth==0.2.5
|
| 159 |
+
- xxhash==3.5.0
|
| 160 |
+
- yarl==1.13.0
|
| 161 |
+
- zipp==3.12.0
|
| 162 |
+
prefix: /home/yiren/new_ssd2/chunhui/miniconda/envs/impossible_llm
|
perplexities/demo.py
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
import sys
|
| 3 |
+
import argparse
|
| 4 |
+
import os
|
| 5 |
+
sys.path.append("..")
|
| 6 |
+
from transformers import AutoTokenizer, AutoModelForCausalLM, Trainer, TrainingArguments, DataCollatorForLanguageModeling
|
| 7 |
+
from datasets import load_dataset
|
| 8 |
+
from numpy.random import default_rng
|
| 9 |
+
os.environ["TOKENIZERS_PARALLELISM"] = "false"
|
| 10 |
+
|
| 11 |
+
MODEL_NAME_SAVE = "Llama-3.2-3B"
|
| 12 |
+
FILE_SAMPLE_SIZE = 5
|
| 13 |
+
|
| 14 |
+
def get_perplexities(model, eval_dataset, batch_size):
|
| 15 |
+
data_collator = DataCollatorForLanguageModeling(tokenizer=tokenizer, mlm=False)
|
| 16 |
+
|
| 17 |
+
training_args = TrainingArguments(
|
| 18 |
+
output_dir="./tmp_trainer",
|
| 19 |
+
per_device_eval_batch_size=batch_size,
|
| 20 |
+
fp16=True,
|
| 21 |
+
report_to="none"
|
| 22 |
+
)
|
| 23 |
+
|
| 24 |
+
trainer = Trainer(model=model, args=training_args, eval_dataset=eval_dataset, data_collator=data_collator)
|
| 25 |
+
eval_results = trainer.evaluate()
|
| 26 |
+
print("eval_results:", eval_results)
|
| 27 |
+
loss = eval_results['eval_loss']
|
| 28 |
+
perplexity = torch.exp(torch.tensor(loss)).item()
|
| 29 |
+
return perplexity
|
| 30 |
+
|
| 31 |
+
if __name__ == "__main__":
|
| 32 |
+
parser = argparse.ArgumentParser(description="Calculate perplexity on test dataset.")
|
| 33 |
+
|
| 34 |
+
parser.add_argument('perturbation',
|
| 35 |
+
type=str,
|
| 36 |
+
default='reverse_full',
|
| 37 |
+
nargs='?',
|
| 38 |
+
help='Type of perturbation to use.')
|
| 39 |
+
parser.add_argument('train_set',
|
| 40 |
+
type=str,
|
| 41 |
+
default='test',
|
| 42 |
+
nargs='?',
|
| 43 |
+
help='Dataset size for training.')
|
| 44 |
+
parser.add_argument('batch_size',
|
| 45 |
+
type=int,
|
| 46 |
+
default=4,
|
| 47 |
+
nargs='?',
|
| 48 |
+
help='Batch size for evaluation.')
|
| 49 |
+
parser.add_argument('seed',
|
| 50 |
+
type=int,
|
| 51 |
+
default=0,
|
| 52 |
+
nargs='?',
|
| 53 |
+
help='Random seed.')
|
| 54 |
+
|
| 55 |
+
args = parser.parse_args()
|
| 56 |
+
|
| 57 |
+
dataset_name = f"babylm_{args.perturbation}_{args.train_set}_seed{args.seed}"
|
| 58 |
+
dataset = load_dataset('../train/babylm_dataset_test.py', name=dataset_name, trust_remote_code=True)
|
| 59 |
+
test_dataset = dataset['test'] # Load test dataset
|
| 60 |
+
print(test_dataset)
|
| 61 |
+
|
| 62 |
+
checkpoint_path = f'../train/checkpoints/{MODEL_NAME_SAVE}/babylm_{args.perturbation}_10M_seed0/runs/checkpoint-450'
|
| 63 |
+
|
| 64 |
+
rng = default_rng(args.seed)
|
| 65 |
+
indices = rng.choice(len(test_dataset), FILE_SAMPLE_SIZE, replace=False)
|
| 66 |
+
sampled_test_dataset = test_dataset.select(indices)
|
| 67 |
+
|
| 68 |
+
tokenizer = AutoTokenizer.from_pretrained(checkpoint_path)
|
| 69 |
+
model = AutoModelForCausalLM.from_pretrained(checkpoint_path)
|
| 70 |
+
|
| 71 |
+
model.eval()
|
| 72 |
+
if torch.cuda.is_available():
|
| 73 |
+
model.to('cuda')
|
| 74 |
+
|
| 75 |
+
def tokenize_function(examples):
|
| 76 |
+
return tokenizer(examples['text'], padding="max_length", truncation=True, max_length=1024)
|
| 77 |
+
|
| 78 |
+
tokenized_test = sampled_test_dataset.map(tokenize_function, batched=True, remove_columns=["text"])
|
| 79 |
+
|
| 80 |
+
|
| 81 |
+
perplexity = get_perplexities(model, tokenized_test, 1)
|
| 82 |
+
print(f"Perplexity on test set: {perplexity}")
|
perplexities/gpt2_mask.py
ADDED
|
@@ -0,0 +1,231 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
import sys
|
| 3 |
+
import argparse
|
| 4 |
+
import os
|
| 5 |
+
import pandas as pd
|
| 6 |
+
import matplotlib.pyplot as plt
|
| 7 |
+
from tqdm import tqdm
|
| 8 |
+
import gc
|
| 9 |
+
from numpy.random import default_rng
|
| 10 |
+
sys.path.append("..")
|
| 11 |
+
from transformers import (
|
| 12 |
+
AutoTokenizer,
|
| 13 |
+
AutoModelForCausalLM,
|
| 14 |
+
Trainer,
|
| 15 |
+
TrainingArguments,
|
| 16 |
+
DataCollatorForLanguageModeling
|
| 17 |
+
)
|
| 18 |
+
from datasets import load_dataset
|
| 19 |
+
|
| 20 |
+
os.environ["TOKENIZERS_PARALLELISM"] = "false"
|
| 21 |
+
|
| 22 |
+
class LayerMasker:
|
| 23 |
+
def __init__(self, model):
|
| 24 |
+
self.model = model
|
| 25 |
+
self.original_forwards = {} # Store original forward methods
|
| 26 |
+
|
| 27 |
+
def _mask_ffn(self, layer, mask_strength=1.0):
|
| 28 |
+
"""Correctly replace FFN forward propagation"""
|
| 29 |
+
layer_id = id(layer.mlp)
|
| 30 |
+
if layer_id not in self.original_forwards:
|
| 31 |
+
self.original_forwards[layer_id] = layer.mlp.forward # Store original function
|
| 32 |
+
|
| 33 |
+
def masked_forward(x):
|
| 34 |
+
return self.original_forwards[layer_id](x) * (1 - mask_strength)
|
| 35 |
+
|
| 36 |
+
layer.mlp.forward = masked_forward # Override with masked function
|
| 37 |
+
|
| 38 |
+
def _mask_attn(self, layer, mask_strength=1.0):
|
| 39 |
+
"""Correctly replace Attention forward propagation"""
|
| 40 |
+
layer_id = id(layer.attn)
|
| 41 |
+
if layer_id not in self.original_forwards:
|
| 42 |
+
self.original_forwards[layer_id] = layer.attn.forward # Store original function
|
| 43 |
+
|
| 44 |
+
def masked_forward(hidden_states, **kwargs):
|
| 45 |
+
output = self.original_forwards[layer_id](hidden_states, **kwargs)
|
| 46 |
+
masked_output = (output[0] * (1 - mask_strength),) + output[1:]
|
| 47 |
+
return masked_output
|
| 48 |
+
|
| 49 |
+
layer.attn.forward = masked_forward # Override with masked function
|
| 50 |
+
|
| 51 |
+
def mask_layer(self, layer_idx, module_type="ffn", mask_strength=1.0):
|
| 52 |
+
layer = self.model.transformer.h[layer_idx]
|
| 53 |
+
if module_type == "ffn":
|
| 54 |
+
self._mask_ffn(layer, mask_strength)
|
| 55 |
+
elif module_type == "attn":
|
| 56 |
+
self._mask_attn(layer, mask_strength)
|
| 57 |
+
else:
|
| 58 |
+
raise ValueError(f"Invalid module type: {module_type}")
|
| 59 |
+
|
| 60 |
+
def reset(self):
|
| 61 |
+
"""Restore all modified forward methods"""
|
| 62 |
+
for layer in self.model.transformer.h:
|
| 63 |
+
layer_id = id(layer.mlp)
|
| 64 |
+
if layer_id in self.original_forwards:
|
| 65 |
+
layer.mlp.forward = self.original_forwards[layer_id]
|
| 66 |
+
|
| 67 |
+
layer_id = id(layer.attn)
|
| 68 |
+
if layer_id in self.original_forwards:
|
| 69 |
+
layer.attn.forward = self.original_forwards[layer_id]
|
| 70 |
+
|
| 71 |
+
self.original_forwards.clear()
|
| 72 |
+
|
| 73 |
+
@torch.no_grad() # Disable gradient computation
|
| 74 |
+
def get_perplexities(model, eval_dataset, batch_size):
|
| 75 |
+
"""Compute perplexity for the dataset"""
|
| 76 |
+
data_collator = DataCollatorForLanguageModeling(tokenizer=tokenizer, mlm=False)
|
| 77 |
+
|
| 78 |
+
training_args = TrainingArguments(
|
| 79 |
+
output_dir="./tmp_trainer",
|
| 80 |
+
per_device_eval_batch_size=batch_size,
|
| 81 |
+
fp16=torch.cuda.is_available(),
|
| 82 |
+
report_to="none"
|
| 83 |
+
)
|
| 84 |
+
|
| 85 |
+
trainer = Trainer(
|
| 86 |
+
model=model,
|
| 87 |
+
args=training_args,
|
| 88 |
+
eval_dataset=eval_dataset,
|
| 89 |
+
data_collator=data_collator
|
| 90 |
+
)
|
| 91 |
+
|
| 92 |
+
eval_results = trainer.evaluate()
|
| 93 |
+
loss = eval_results['eval_loss']
|
| 94 |
+
return torch.exp(torch.tensor(loss)).item()
|
| 95 |
+
|
| 96 |
+
def run_mask_experiment(model, tokenized_data, args):
|
| 97 |
+
"""Run layer masking experiment"""
|
| 98 |
+
results = []
|
| 99 |
+
masker = LayerMasker(model)
|
| 100 |
+
|
| 101 |
+
# Compute baseline perplexity
|
| 102 |
+
baseline_ppl = get_perplexities(model, tokenized_data, args.batch_size)
|
| 103 |
+
print(f"Baseline Perplexity: {baseline_ppl:.2f}")
|
| 104 |
+
|
| 105 |
+
# Layer masking experiments
|
| 106 |
+
print("\nRunning layer masking experiments:")
|
| 107 |
+
for layer_idx in tqdm(range(model.config.n_layer), desc="Layers"):
|
| 108 |
+
for module_type in ["ffn", "attn"]:
|
| 109 |
+
# Apply masking
|
| 110 |
+
masker.mask_layer(layer_idx, module_type)
|
| 111 |
+
|
| 112 |
+
# Compute perplexity
|
| 113 |
+
ppl = get_perplexities(model, tokenized_data, args.batch_size)
|
| 114 |
+
delta = ppl - baseline_ppl
|
| 115 |
+
|
| 116 |
+
gc.collect()
|
| 117 |
+
torch.cuda.empty_cache()
|
| 118 |
+
|
| 119 |
+
results.append({
|
| 120 |
+
"layer": layer_idx,
|
| 121 |
+
"module": module_type,
|
| 122 |
+
"perplexity": ppl,
|
| 123 |
+
"delta": delta
|
| 124 |
+
})
|
| 125 |
+
|
| 126 |
+
# Reset the model
|
| 127 |
+
masker.reset()
|
| 128 |
+
|
| 129 |
+
return results, baseline_ppl
|
| 130 |
+
|
| 131 |
+
def visualize_results(results_df, output_dir):
|
| 132 |
+
"""Visualize layer impact results"""
|
| 133 |
+
plt.figure(figsize=(12, 6))
|
| 134 |
+
|
| 135 |
+
# Plot FFN curve
|
| 136 |
+
ffn_data = results_df[results_df["module"] == "ffn"]
|
| 137 |
+
plt.plot(ffn_data["layer"], ffn_data["delta"],
|
| 138 |
+
marker='o', linestyle='-', linewidth=2, markersize=8,
|
| 139 |
+
color='#1f77b4', label='FFN')
|
| 140 |
+
|
| 141 |
+
# Plot Attention curve
|
| 142 |
+
attn_data = results_df[results_df["module"] == "attn"]
|
| 143 |
+
plt.plot(attn_data["layer"], attn_data["delta"],
|
| 144 |
+
marker='s', linestyle='--', linewidth=2, markersize=8,
|
| 145 |
+
color='#ff7f0e', label='Attention')
|
| 146 |
+
|
| 147 |
+
plt.xlabel("Layer Index", fontsize=12)
|
| 148 |
+
plt.ylabel("Δ Perplexity (log scale)", fontsize=12)
|
| 149 |
+
plt.title("GPT-2 Layer Masking Impact Analysis", fontsize=14)
|
| 150 |
+
plt.legend(fontsize=10)
|
| 151 |
+
plt.grid(True, alpha=0.3)
|
| 152 |
+
plt.xticks(range(0, results_df["layer"].max()+1))
|
| 153 |
+
|
| 154 |
+
# Set y-axis to log scale
|
| 155 |
+
plt.yscale('log')
|
| 156 |
+
|
| 157 |
+
plot_path = os.path.join(output_dir, f"{args.perturbation}_layer_impact.png")
|
| 158 |
+
plt.savefig(plot_path, bbox_inches='tight', dpi=300)
|
| 159 |
+
plt.close()
|
| 160 |
+
|
| 161 |
+
|
| 162 |
+
if __name__ == "__main__":
|
| 163 |
+
# Argument configuration
|
| 164 |
+
parser = argparse.ArgumentParser(description="GPT-2 Layer Masking Experiment")
|
| 165 |
+
parser.add_argument('--perturbation', type=str, default="hop_sg2pl",
|
| 166 |
+
help="Perturbation type (default: hop_sg2pl)")
|
| 167 |
+
parser.add_argument('--train_set', type=str, default="10M",
|
| 168 |
+
help="Training set size (default: 10M)")
|
| 169 |
+
parser.add_argument('--checkpoint_path', type=str, default="checkpoint-2736",
|
| 170 |
+
help="Model checkpoint path (default: checkpoint-2736)")
|
| 171 |
+
parser.add_argument('--batch_size', type=int, default=3,
|
| 172 |
+
help="Evaluation batch size (default: 3)")
|
| 173 |
+
parser.add_argument('--seed', type=int, default=0,
|
| 174 |
+
help="Random seed (default: 0)")
|
| 175 |
+
args = parser.parse_args()
|
| 176 |
+
|
| 177 |
+
# Initialize output directory
|
| 178 |
+
output_dir = f"mask_results/GPT2-MaskResults"
|
| 179 |
+
os.makedirs(output_dir, exist_ok=True)
|
| 180 |
+
|
| 181 |
+
# Load dataset
|
| 182 |
+
dataset_name = f"babylm_{args.perturbation}_{args.train_set}_seed{args.seed}"
|
| 183 |
+
dataset = load_dataset('../train/babylm_dataset_test.py', name=dataset_name, trust_remote_code=True)
|
| 184 |
+
test_dataset = dataset['test']
|
| 185 |
+
|
| 186 |
+
# Data sampling
|
| 187 |
+
rng = default_rng(args.seed)
|
| 188 |
+
indices = rng.choice(len(test_dataset), size=500, replace=False)
|
| 189 |
+
sampled_test = test_dataset.select(indices)
|
| 190 |
+
|
| 191 |
+
# Load model and tokenizer
|
| 192 |
+
checkpoint_path = f"../train/checkpoints/GPT-2/babylm_{args.perturbation}_10M_seed0/runs/{args.checkpoint_path}"
|
| 193 |
+
tokenizer = AutoTokenizer.from_pretrained(checkpoint_path)
|
| 194 |
+
model = AutoModelForCausalLM.from_pretrained(checkpoint_path, torch_dtype=torch.float16).eval()
|
| 195 |
+
if torch.cuda.is_available():
|
| 196 |
+
model = model.cuda()
|
| 197 |
+
|
| 198 |
+
# Data preprocessing
|
| 199 |
+
def tokenize_fn(examples):
|
| 200 |
+
return tokenizer(
|
| 201 |
+
examples["text"],
|
| 202 |
+
padding="max_length",
|
| 203 |
+
truncation=True,
|
| 204 |
+
max_length=512,
|
| 205 |
+
return_tensors="pt"
|
| 206 |
+
)
|
| 207 |
+
|
| 208 |
+
tokenized_test = sampled_test.map(
|
| 209 |
+
tokenize_fn,
|
| 210 |
+
batched=True,
|
| 211 |
+
remove_columns=["text"],
|
| 212 |
+
desc="Tokenizing"
|
| 213 |
+
)
|
| 214 |
+
|
| 215 |
+
# Run experiment
|
| 216 |
+
results, baseline_ppl = run_mask_experiment(model, tokenized_test, args)
|
| 217 |
+
results_df = pd.DataFrame(results)
|
| 218 |
+
|
| 219 |
+
# Save results
|
| 220 |
+
output_file = os.path.join(output_dir, f"mask_results_{args.perturbation}.csv")
|
| 221 |
+
results_df.to_csv(output_file, index=False)
|
| 222 |
+
|
| 223 |
+
# Generate visualization
|
| 224 |
+
visualize_results(results_df, output_dir)
|
| 225 |
+
|
| 226 |
+
# Print key results
|
| 227 |
+
print(f"\nExperiment completed. Results saved to {output_dir}")
|
| 228 |
+
print(f"Baseline Perplexity: {baseline_ppl:.2f}")
|
| 229 |
+
print("Layer impact results:")
|
| 230 |
+
print(results_df[["layer", "module", "delta"]].to_string(index=False))
|
| 231 |
+
|
perplexities/gpt2_mask_test.py
ADDED
|
File without changes
|
perplexities/gpt2_noise.py
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Activation Steering
|
| 2 |
+
|
| 3 |
+
import torch
|
| 4 |
+
import sys
|
| 5 |
+
import argparse
|
| 6 |
+
import os
|
| 7 |
+
import pandas as pd
|
| 8 |
+
import matplotlib.pyplot as plt
|
| 9 |
+
from tqdm import tqdm
|
| 10 |
+
from numpy.random import default_rng
|
| 11 |
+
sys.path.append("..")
|
| 12 |
+
from transformers import (
|
| 13 |
+
AutoTokenizer,
|
| 14 |
+
AutoModelForCausalLM,
|
| 15 |
+
Trainer,
|
| 16 |
+
TrainingArguments,
|
| 17 |
+
DataCollatorForLanguageModeling
|
| 18 |
+
)
|
| 19 |
+
from datasets import load_dataset
|
| 20 |
+
|
| 21 |
+
os.environ["TOKENIZERS_PARALLELISM"] = "false"
|
| 22 |
+
|
| 23 |
+
class LayerNoiseInjector:
|
| 24 |
+
|
| 25 |
+
def __init__(self, model):
|
| 26 |
+
self.model = model
|
| 27 |
+
self.hooks = [] # Store registered hooks
|
| 28 |
+
self.noise_std = 0.1 # Noise standard deviation (adjustable)
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
def _add_noise_hook(self, layer_idx):
|
| 32 |
+
def hook(module, input, output):
|
| 33 |
+
if isinstance(output, tuple):
|
| 34 |
+
modified_output = list(output)
|
| 35 |
+
noise = torch.randn_like(modified_output[0]) * self.noise_std
|
| 36 |
+
modified_output[0] = modified_output[0] + noise
|
| 37 |
+
return tuple(modified_output)
|
| 38 |
+
else:
|
| 39 |
+
noise = torch.randn_like(output) * self.noise_std
|
| 40 |
+
return output + noise
|
| 41 |
+
|
| 42 |
+
handle = self.model.transformer.h[layer_idx].register_forward_hook(hook)
|
| 43 |
+
self.hooks.append(handle)
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
def inject_noise(self, layer_idx):
|
| 47 |
+
"""Inject noise to specified layer"""
|
| 48 |
+
self._add_noise_hook(layer_idx)
|
| 49 |
+
|
| 50 |
+
def reset(self):
|
| 51 |
+
"""Remove all noise hooks"""
|
| 52 |
+
for handle in self.hooks:
|
| 53 |
+
handle.remove()
|
| 54 |
+
self.hooks.clear()
|
| 55 |
+
|
| 56 |
+
@torch.no_grad() # Disable gradient calculation
|
| 57 |
+
def get_perplexities(model, eval_dataset, batch_size):
|
| 58 |
+
"""Calculate dataset perplexity"""
|
| 59 |
+
data_collator = DataCollatorForLanguageModeling(tokenizer=tokenizer, mlm=False)
|
| 60 |
+
|
| 61 |
+
training_args = TrainingArguments(
|
| 62 |
+
output_dir="./tmp_trainer",
|
| 63 |
+
per_device_eval_batch_size=batch_size,
|
| 64 |
+
fp16=torch.cuda.is_available(),
|
| 65 |
+
report_to="none"
|
| 66 |
+
)
|
| 67 |
+
|
| 68 |
+
trainer = Trainer(
|
| 69 |
+
model=model,
|
| 70 |
+
args=training_args,
|
| 71 |
+
eval_dataset=eval_dataset,
|
| 72 |
+
data_collator=data_collator
|
| 73 |
+
)
|
| 74 |
+
|
| 75 |
+
eval_results = trainer.evaluate()
|
| 76 |
+
loss = eval_results['eval_loss']
|
| 77 |
+
return torch.exp(torch.tensor(loss)).item()
|
| 78 |
+
|
| 79 |
+
# Data preprocessing
|
| 80 |
+
def tokenize_fn(examples):
|
| 81 |
+
return tokenizer(
|
| 82 |
+
examples["text"],
|
| 83 |
+
padding="max_length",
|
| 84 |
+
truncation=True,
|
| 85 |
+
max_length=512,
|
| 86 |
+
return_tensors="pt"
|
| 87 |
+
)
|
| 88 |
+
|
| 89 |
+
|
| 90 |
+
def run_noise_experiment(model, tokenized_data, args):
|
| 91 |
+
"""Execute layer-wise noise injection experiment"""
|
| 92 |
+
results = []
|
| 93 |
+
noise_injector = LayerNoiseInjector(model)
|
| 94 |
+
|
| 95 |
+
# Calculate baseline perplexity
|
| 96 |
+
baseline_ppl = get_perplexities(model, tokenized_data, args.batch_size)
|
| 97 |
+
print(f"Baseline Perplexity: {baseline_ppl:.2f}")
|
| 98 |
+
|
| 99 |
+
# Layer-wise noise injection experiment
|
| 100 |
+
print("\nRunning layer noise injection experiments:")
|
| 101 |
+
for layer_idx in tqdm(range(model.config.n_layer), desc="Layers"):
|
| 102 |
+
# Inject noise
|
| 103 |
+
noise_injector.inject_noise(layer_idx)
|
| 104 |
+
|
| 105 |
+
# Calculate perplexity
|
| 106 |
+
ppl = get_perplexities(model, tokenized_data, args.batch_size)
|
| 107 |
+
delta = ppl - baseline_ppl
|
| 108 |
+
|
| 109 |
+
results.append({
|
| 110 |
+
"layer": layer_idx,
|
| 111 |
+
"perplexity": ppl,
|
| 112 |
+
"delta": delta
|
| 113 |
+
})
|
| 114 |
+
|
| 115 |
+
# Reset model
|
| 116 |
+
noise_injector.reset()
|
| 117 |
+
|
| 118 |
+
return results, baseline_ppl
|
| 119 |
+
|
| 120 |
+
def visualize_noise_results(results_df, output_dir):
|
| 121 |
+
"""Visualize noise injection impact results"""
|
| 122 |
+
plt.figure(figsize=(12, 6))
|
| 123 |
+
|
| 124 |
+
# Plot delta perplexity curve
|
| 125 |
+
plt.plot(results_df["layer"], results_df["delta"],
|
| 126 |
+
marker='o', linestyle='-', linewidth=2, markersize=8,
|
| 127 |
+
color='#2ca02c', label='Activation Noise')
|
| 128 |
+
|
| 129 |
+
plt.xlabel("Layer Index", fontsize=12)
|
| 130 |
+
plt.ylabel("Δ Perplexity (log scale)", fontsize=12)
|
| 131 |
+
plt.title("GPT-2 Layer Noise Injection Impact Analysis", fontsize=14)
|
| 132 |
+
plt.legend(fontsize=10)
|
| 133 |
+
plt.grid(True, alpha=0.3)
|
| 134 |
+
plt.xticks(range(0, results_df["layer"].max()+1))
|
| 135 |
+
|
| 136 |
+
# Set y-axis to log scale
|
| 137 |
+
plt.yscale('log')
|
| 138 |
+
|
| 139 |
+
plot_path = os.path.join(output_dir, f"{args.perturbation}_noise_impact.png")
|
| 140 |
+
plt.savefig(plot_path, bbox_inches='tight', dpi=300)
|
| 141 |
+
plt.close()
|
| 142 |
+
|
| 143 |
+
if __name__ == "__main__":
|
| 144 |
+
# Parameter configuration
|
| 145 |
+
parser = argparse.ArgumentParser(description="GPT-2 Layer Noise Injection Experiment")
|
| 146 |
+
parser.add_argument('--perturbation', type=str, default="hop_sg2pl",
|
| 147 |
+
help="Perturbation type (default: hop_sg2pl)")
|
| 148 |
+
parser.add_argument('--train_set', type=str, default="10M",
|
| 149 |
+
help="Training set size (default: 10M)")
|
| 150 |
+
parser.add_argument('--checkpoint_path', type=str, default="checkpoint-2736",
|
| 151 |
+
help="Model checkpoint path (default: checkpoint-2736)")
|
| 152 |
+
parser.add_argument('--batch_size', type=int, default=3,
|
| 153 |
+
help="Evaluation batch size (default: 3)")
|
| 154 |
+
parser.add_argument('--seed', type=int, default=0,
|
| 155 |
+
help="Random seed (default: 0)")
|
| 156 |
+
parser.add_argument('--noise_std', type=float, default=0.1,
|
| 157 |
+
help="Noise standard deviation (default: 0.1)")
|
| 158 |
+
args = parser.parse_args()
|
| 159 |
+
|
| 160 |
+
# Initialize output directory
|
| 161 |
+
output_dir = f"noise_results/GPT2-NoiseResults"
|
| 162 |
+
os.makedirs(output_dir, exist_ok=True)
|
| 163 |
+
|
| 164 |
+
# Load dataset
|
| 165 |
+
dataset_name = f"babylm_{args.perturbation}_{args.train_set}_seed{args.seed}"
|
| 166 |
+
dataset = load_dataset('../train/babylm_dataset_test.py', name=dataset_name, trust_remote_code=True)
|
| 167 |
+
test_dataset = dataset['test']
|
| 168 |
+
|
| 169 |
+
# Data sampling
|
| 170 |
+
rng = default_rng(args.seed)
|
| 171 |
+
indices = rng.choice(len(test_dataset), size=500, replace=False)
|
| 172 |
+
sampled_test = test_dataset.select(indices)
|
| 173 |
+
|
| 174 |
+
# Load model and tokenizer
|
| 175 |
+
checkpoint_path = f"../train/checkpoints/GPT-2/babylm_{args.perturbation}_10M_seed0/runs/{args.checkpoint_path}"
|
| 176 |
+
tokenizer = AutoTokenizer.from_pretrained(checkpoint_path)
|
| 177 |
+
model = AutoModelForCausalLM.from_pretrained(checkpoint_path, torch_dtype=torch.float16).eval()
|
| 178 |
+
if torch.cuda.is_available():
|
| 179 |
+
model = model.cuda()
|
| 180 |
+
|
| 181 |
+
tokenized_test = sampled_test.map(
|
| 182 |
+
tokenize_fn,
|
| 183 |
+
batched=True,
|
| 184 |
+
remove_columns=["text"],
|
| 185 |
+
desc="Tokenizing"
|
| 186 |
+
)
|
| 187 |
+
|
| 188 |
+
# Run noise injection experiment
|
| 189 |
+
results, baseline_ppl = run_noise_experiment(model, tokenized_test, args)
|
| 190 |
+
results_df = pd.DataFrame(results)
|
| 191 |
+
|
| 192 |
+
# Save results
|
| 193 |
+
output_file = os.path.join(output_dir, f"noise_results_{args.perturbation}.csv")
|
| 194 |
+
results_df.to_csv(output_file, index=False)
|
| 195 |
+
|
| 196 |
+
# Generate visualization
|
| 197 |
+
visualize_noise_results(results_df, output_dir)
|
| 198 |
+
|
| 199 |
+
# Print key results
|
| 200 |
+
print(f"\nExperiment completed. Results saved to {output_dir}")
|
| 201 |
+
print(f"Baseline Perplexity: {baseline_ppl:.2f}")
|
| 202 |
+
print("Layer impact results:")
|
| 203 |
+
print(results_df[["layer", "delta"]].to_string(index=False))
|
perplexities/gpt2_remove_layers.py
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
import sys
|
| 3 |
+
import argparse
|
| 4 |
+
import os
|
| 5 |
+
sys.path.append("..")
|
| 6 |
+
from transformers import AutoTokenizer, AutoModelForCausalLM, Trainer, TrainingArguments, DataCollatorForLanguageModeling
|
| 7 |
+
from datasets import load_dataset
|
| 8 |
+
from numpy.random import default_rng
|
| 9 |
+
os.environ["TOKENIZERS_PARALLELISM"] = "false"
|
| 10 |
+
|
| 11 |
+
# MODEL_NAME = "Llama-3.2-3B"
|
| 12 |
+
# ori_model_name = "meta-llama/Llama-3.2-3B"
|
| 13 |
+
# MODEL_NAME_SAVE = "Llama-3.2-3B-500-Remove"
|
| 14 |
+
|
| 15 |
+
MODEL_NAME = "GPT-2"
|
| 16 |
+
ori_model_name = "gpt2"
|
| 17 |
+
MODEL_NAME_SAVE = "GPT2-500-Remove-layers"
|
| 18 |
+
|
| 19 |
+
FILE_SAMPLE_SIZE = 500
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
def remove_layers(original_model, exp_model, i, j):
|
| 23 |
+
|
| 24 |
+
original_blocks = original_model.transformer.h
|
| 25 |
+
exp_blocks = exp_model.transformer.h
|
| 26 |
+
|
| 27 |
+
print("len(original_blocks):", len(original_blocks))
|
| 28 |
+
print("len(exp_blocks):", len(exp_blocks))
|
| 29 |
+
|
| 30 |
+
# Ensure the indices are valid
|
| 31 |
+
if i < 0 or (i + j) > len(exp_blocks) or j < 0:
|
| 32 |
+
raise ValueError(f"Invalid block indices: i={i}, i+j={i+j}. Must satisfy 0 <= i and i+j <= {len(exp_blocks)}.")
|
| 33 |
+
|
| 34 |
+
# Replace the parameters of the blocks from i to i+j
|
| 35 |
+
for idx in range(i, i + j):
|
| 36 |
+
print(f"Replacing parameters of Transformer block {idx}...")
|
| 37 |
+
original_block = original_blocks[idx]
|
| 38 |
+
exp_blocks[idx].load_state_dict(original_block.state_dict())
|
| 39 |
+
|
| 40 |
+
return exp_model
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
def get_perplexities(model, eval_dataset, batch_size):
|
| 45 |
+
data_collator = DataCollatorForLanguageModeling(tokenizer=tokenizer, mlm=False)
|
| 46 |
+
|
| 47 |
+
training_args = TrainingArguments(
|
| 48 |
+
output_dir="./tmp_trainer",
|
| 49 |
+
per_device_eval_batch_size=batch_size,
|
| 50 |
+
fp16=True,
|
| 51 |
+
report_to="none"
|
| 52 |
+
)
|
| 53 |
+
|
| 54 |
+
trainer = Trainer(model=model, args=training_args, eval_dataset=eval_dataset, data_collator=data_collator)
|
| 55 |
+
eval_results = trainer.evaluate()
|
| 56 |
+
print("eval_results:", eval_results)
|
| 57 |
+
loss = eval_results['eval_loss']
|
| 58 |
+
perplexity = torch.exp(torch.tensor(loss)).item()
|
| 59 |
+
return perplexity
|
| 60 |
+
|
| 61 |
+
if __name__ == "__main__":
|
| 62 |
+
parser = argparse.ArgumentParser(description="Calculate perplexity on test dataset.")
|
| 63 |
+
|
| 64 |
+
parser.add_argument('perturbation',
|
| 65 |
+
type=str,
|
| 66 |
+
default='reverse_full',
|
| 67 |
+
nargs='?',
|
| 68 |
+
help='Type of perturbation to use.')
|
| 69 |
+
parser.add_argument('train_set',
|
| 70 |
+
type=str,
|
| 71 |
+
default='test',
|
| 72 |
+
nargs='?',
|
| 73 |
+
help='Dataset size for training.')
|
| 74 |
+
parser.add_argument('checkpoint_path',
|
| 75 |
+
type=str,
|
| 76 |
+
default='checkpoint-100',
|
| 77 |
+
nargs='?',
|
| 78 |
+
help='Dataset size for training.')
|
| 79 |
+
parser.add_argument('batch_size',
|
| 80 |
+
type=int,
|
| 81 |
+
default=4,
|
| 82 |
+
nargs='?',
|
| 83 |
+
help='Batch size for evaluation.')
|
| 84 |
+
parser.add_argument('seed',
|
| 85 |
+
type=int,
|
| 86 |
+
default=0,
|
| 87 |
+
nargs='?',
|
| 88 |
+
help='Random seed.')
|
| 89 |
+
parser.add_argument('remove_layer',
|
| 90 |
+
type=int,
|
| 91 |
+
default=1,
|
| 92 |
+
nargs='?',
|
| 93 |
+
help='Layer index to remove')
|
| 94 |
+
args = parser.parse_args()
|
| 95 |
+
|
| 96 |
+
dataset_name = f"babylm_{args.perturbation}_{args.train_set}_seed{args.seed}"
|
| 97 |
+
dataset = load_dataset('../train/babylm_dataset_test.py', name=dataset_name, trust_remote_code=True)
|
| 98 |
+
test_dataset = dataset['test'] # Load test dataset
|
| 99 |
+
print(test_dataset)
|
| 100 |
+
|
| 101 |
+
|
| 102 |
+
checkpoint_path = f'../train/checkpoints/{MODEL_NAME}/babylm_{args.perturbation}_10M_seed0/runs/{args.checkpoint_path}'
|
| 103 |
+
|
| 104 |
+
rng = default_rng(args.seed)
|
| 105 |
+
indices = rng.choice(len(test_dataset), FILE_SAMPLE_SIZE, replace=False)
|
| 106 |
+
sampled_test_dataset = test_dataset.select(indices)
|
| 107 |
+
|
| 108 |
+
tokenizer = AutoTokenizer.from_pretrained(checkpoint_path)
|
| 109 |
+
model = AutoModelForCausalLM.from_pretrained(checkpoint_path)
|
| 110 |
+
model_ori = AutoModelForCausalLM.from_pretrained(ori_model_name)
|
| 111 |
+
model = remove_layers(model_ori, model, args.remove_layer, 4)
|
| 112 |
+
print(model)
|
| 113 |
+
model.eval()
|
| 114 |
+
# if torch.cuda.is_available():
|
| 115 |
+
# model.to('cuda')
|
| 116 |
+
|
| 117 |
+
def tokenize_function(examples):
|
| 118 |
+
return tokenizer(examples['text'], padding="max_length", truncation=True, max_length=1024)
|
| 119 |
+
|
| 120 |
+
tokenized_test = sampled_test_dataset.map(tokenize_function, batched=True, remove_columns=["text"])
|
| 121 |
+
|
| 122 |
+
|
| 123 |
+
perplexity = get_perplexities(model, tokenized_test, args.batch_size)
|
| 124 |
+
|
| 125 |
+
# Save the result to the specified directory
|
| 126 |
+
output_directory = f"perplexities_results/{MODEL_NAME_SAVE}"
|
| 127 |
+
os.makedirs(output_directory, exist_ok=True)
|
| 128 |
+
|
| 129 |
+
# Construct the output file path
|
| 130 |
+
output_file = os.path.join(output_directory, f"{args.perturbation}_{args.batch_size}_{args.seed}.csv")
|
| 131 |
+
|
| 132 |
+
# Write the header to the CSV file if it doesn't exist
|
| 133 |
+
if not os.path.exists(output_file):
|
| 134 |
+
with open(output_file, 'w') as f:
|
| 135 |
+
print("Writing header to CSV...")
|
| 136 |
+
f.write(f"checkpoint_path, perplexity\n")
|
| 137 |
+
|
| 138 |
+
# Append the perplexity result to the CSV file
|
| 139 |
+
with open(output_file, 'a') as f: # Open in append mode
|
| 140 |
+
print("Appending result to CSV...")
|
| 141 |
+
f.write(f"{args.checkpoint_path}-{args.remove_layer+1}, {perplexity}\n")
|
perplexities/gpt_mask.sh
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/bin/bash
|
| 2 |
+
|
| 3 |
+
CUDA_VISIBLE_DEVICES=1,2 torchrun --nproc_per_node=2 --master_port=22235 gpt2_mask.py --perturbation shuffle_nondeterministic --train_set 10M --checkpoint_path checkpoint-2736 --batch_size 3 --seed 0
|
| 4 |
+
|
| 5 |
+
CUDA_VISIBLE_DEVICES=1,2 torchrun --nproc_per_node=2 --master_port=22235 gpt2_mask.py --perturbation shuffle_nondeterministic --train_set 10M --checkpoint_path checkpoint-2736 --batch_size 3 --seed 0
|
| 6 |
+
|
| 7 |
+
CUDA_VISIBLE_DEVICES=1,2 torchrun --nproc_per_node=2 --master_port=22235 gpt2_mask.py --perturbation hop_words4 --train_set 10M --checkpoint_path checkpoint-2376 --batch_size 3 --seed 0
|
| 8 |
+
|
| 9 |
+
CUDA_VISIBLE_DEVICES=1,2 torchrun --nproc_per_node=2 --master_port=22235 gpt2_mask.py --perturbation hop_control --train_set 10M --checkpoint_path checkpoint-2409 --batch_size 3 --seed 0
|
perplexities/gpt_noise.sh
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
CUDA_VISIBLE_DEVICES=2,3 torchrun --nproc_per_node=2 --master_port=25235 gpt2_noise.py --perturbation shuffle_nondeterministic --train_set 10M --checkpoint_path checkpoint-2736 --batch_size 3 --seed 0 --noise_std 0.1
|
| 2 |
+
|
| 3 |
+
CUDA_VISIBLE_DEVICES=2,3 torchrun --nproc_per_node=2 --master_port=25236 gpt2_noise.py --perturbation shuffle_control --train_set 10M --checkpoint_path checkpoint-2736 --batch_size 3 --seed 0 --noise_std 0.1
|
| 4 |
+
|
| 5 |
+
CUDA_VISIBLE_DEVICES=2,3 torchrun --nproc_per_node=2 --master_port=25237 gpt2_noise.py --perturbation hop_words4 --train_set 10M --checkpoint_path checkpoint-2376 --batch_size 3 --seed 0 --noise_std 0.1
|
| 6 |
+
|
| 7 |
+
CUDA_VISIBLE_DEVICES=2,3 torchrun --nproc_per_node=2 --master_port=25238 gpt2_noise.py --perturbation hop_control --train_set 10M --checkpoint_path checkpoint-2409 --batch_size 3 --seed 0 --noise_std 0.1
|
perplexities/llama3.2_remove_layers.py
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
import sys
|
| 3 |
+
import argparse
|
| 4 |
+
import os
|
| 5 |
+
sys.path.append("..")
|
| 6 |
+
from transformers import AutoTokenizer, AutoModelForCausalLM, Trainer, TrainingArguments, DataCollatorForLanguageModeling
|
| 7 |
+
from datasets import load_dataset
|
| 8 |
+
from numpy.random import default_rng
|
| 9 |
+
os.environ["TOKENIZERS_PARALLELISM"] = "false"
|
| 10 |
+
|
| 11 |
+
MODEL_NAME = "Llama-3.2-3B"
|
| 12 |
+
ori_model_name = "meta-llama/Llama-3.2-3B"
|
| 13 |
+
MODEL_NAME_SAVE = "Llama-3.2-3B-500-Remove-layers"
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
FILE_SAMPLE_SIZE = 500
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
def remove_layers(original_model, exp_model, i, j):
|
| 20 |
+
|
| 21 |
+
original_layers = original_model.model.layers
|
| 22 |
+
exp_layers = exp_model.model.layers
|
| 23 |
+
|
| 24 |
+
print("len(layers):", len(original_layers))
|
| 25 |
+
# Ensure the indices are valid
|
| 26 |
+
if i < 0 or (i + j) > len(exp_layers) or j < 0:
|
| 27 |
+
raise ValueError(f"Invalid layer indices: i={i}, i+j={i+j}. Must satisfy 0 <= i and i+j <= {len(exp_layers)}.")
|
| 28 |
+
|
| 29 |
+
# Replace the parameters of the layers from i to i+j
|
| 30 |
+
for idx in range(i, i + j):
|
| 31 |
+
print(f"Replacing parameters of LlamaDecoderLayer {idx}...")
|
| 32 |
+
original_layer = original_layers[idx]
|
| 33 |
+
exp_layers[idx].load_state_dict(original_layer.state_dict())
|
| 34 |
+
|
| 35 |
+
return exp_model
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
def get_perplexities(model, eval_dataset, batch_size):
|
| 39 |
+
data_collator = DataCollatorForLanguageModeling(tokenizer=tokenizer, mlm=False)
|
| 40 |
+
|
| 41 |
+
training_args = TrainingArguments(
|
| 42 |
+
output_dir="./tmp_trainer",
|
| 43 |
+
per_device_eval_batch_size=batch_size,
|
| 44 |
+
fp16=True,
|
| 45 |
+
report_to="none"
|
| 46 |
+
)
|
| 47 |
+
|
| 48 |
+
trainer = Trainer(model=model, args=training_args, eval_dataset=eval_dataset, data_collator=data_collator)
|
| 49 |
+
eval_results = trainer.evaluate()
|
| 50 |
+
print("eval_results:", eval_results)
|
| 51 |
+
loss = eval_results['eval_loss']
|
| 52 |
+
perplexity = torch.exp(torch.tensor(loss)).item()
|
| 53 |
+
return perplexity
|
| 54 |
+
|
| 55 |
+
if __name__ == "__main__":
|
| 56 |
+
parser = argparse.ArgumentParser(description="Calculate perplexity on test dataset.")
|
| 57 |
+
|
| 58 |
+
parser.add_argument('perturbation',
|
| 59 |
+
type=str,
|
| 60 |
+
default='reverse_full',
|
| 61 |
+
nargs='?',
|
| 62 |
+
help='Type of perturbation to use.')
|
| 63 |
+
parser.add_argument('train_set',
|
| 64 |
+
type=str,
|
| 65 |
+
default='test',
|
| 66 |
+
nargs='?',
|
| 67 |
+
help='Dataset size for training.')
|
| 68 |
+
parser.add_argument('checkpoint_path',
|
| 69 |
+
type=str,
|
| 70 |
+
default='checkpoint-100',
|
| 71 |
+
nargs='?',
|
| 72 |
+
help='Dataset size for training.')
|
| 73 |
+
parser.add_argument('batch_size',
|
| 74 |
+
type=int,
|
| 75 |
+
default=4,
|
| 76 |
+
nargs='?',
|
| 77 |
+
help='Batch size for evaluation.')
|
| 78 |
+
parser.add_argument('seed',
|
| 79 |
+
type=int,
|
| 80 |
+
default=0,
|
| 81 |
+
nargs='?',
|
| 82 |
+
help='Random seed.')
|
| 83 |
+
parser.add_argument('remove_layer',
|
| 84 |
+
type=int,
|
| 85 |
+
default=1,
|
| 86 |
+
nargs='?',
|
| 87 |
+
help='Layer index to remove')
|
| 88 |
+
args = parser.parse_args()
|
| 89 |
+
|
| 90 |
+
dataset_name = f"babylm_{args.perturbation}_{args.train_set}_seed{args.seed}"
|
| 91 |
+
dataset = load_dataset('../train/babylm_dataset_test.py', name=dataset_name, trust_remote_code=True)
|
| 92 |
+
test_dataset = dataset['test'] # Load test dataset
|
| 93 |
+
print(test_dataset)
|
| 94 |
+
|
| 95 |
+
|
| 96 |
+
checkpoint_path = f'../train/checkpoints/{MODEL_NAME}/babylm_{args.perturbation}_10M_seed0/runs/{args.checkpoint_path}'
|
| 97 |
+
|
| 98 |
+
rng = default_rng(args.seed)
|
| 99 |
+
indices = rng.choice(len(test_dataset), FILE_SAMPLE_SIZE, replace=False)
|
| 100 |
+
sampled_test_dataset = test_dataset.select(indices)
|
| 101 |
+
|
| 102 |
+
tokenizer = AutoTokenizer.from_pretrained(checkpoint_path)
|
| 103 |
+
model = AutoModelForCausalLM.from_pretrained(checkpoint_path)
|
| 104 |
+
model_ori = AutoModelForCausalLM.from_pretrained(ori_model_name)
|
| 105 |
+
model = remove_layers(model_ori, model, args.remove_layer, 9)
|
| 106 |
+
print(model)
|
| 107 |
+
model.eval()
|
| 108 |
+
# if torch.cuda.is_available():
|
| 109 |
+
# model.to('cuda')
|
| 110 |
+
|
| 111 |
+
def tokenize_function(examples):
|
| 112 |
+
return tokenizer(examples['text'], padding="max_length", truncation=True, max_length=1024)
|
| 113 |
+
|
| 114 |
+
tokenized_test = sampled_test_dataset.map(tokenize_function, batched=True, remove_columns=["text"])
|
| 115 |
+
|
| 116 |
+
|
| 117 |
+
perplexity = get_perplexities(model, tokenized_test, args.batch_size)
|
| 118 |
+
|
| 119 |
+
# Save the result to the specified directory
|
| 120 |
+
output_directory = f"perplexities_results/{MODEL_NAME_SAVE}"
|
| 121 |
+
os.makedirs(output_directory, exist_ok=True)
|
| 122 |
+
|
| 123 |
+
# Construct the output file path
|
| 124 |
+
output_file = os.path.join(output_directory, f"{args.perturbation}_{args.batch_size}_{args.seed}.csv")
|
| 125 |
+
|
| 126 |
+
# Write the header to the CSV file if it doesn't exist
|
| 127 |
+
if not os.path.exists(output_file):
|
| 128 |
+
with open(output_file, 'w') as f:
|
| 129 |
+
print("Writing header to CSV...")
|
| 130 |
+
f.write(f"checkpoint_path, perplexity\n")
|
| 131 |
+
|
| 132 |
+
# Append the perplexity result to the CSV file
|
| 133 |
+
with open(output_file, 'a') as f: # Open in append mode
|
| 134 |
+
print("Appending result to CSV...")
|
| 135 |
+
f.write(f"{args.checkpoint_path}-{args.remove_layer+1}, {perplexity}\n")
|
perplexities/perplexities.py
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# perplexities.py
|
| 2 |
+
# Author: Julie Kallini
|
| 3 |
+
|
| 4 |
+
# For importing utils
|
| 5 |
+
import sys
|
| 6 |
+
sys.path.append("..")
|
| 7 |
+
|
| 8 |
+
from transformers import GPT2LMHeadModel
|
| 9 |
+
from gpt2_no_positional_encoding_model import GPT2NoPositionalEncodingLMHeadModel
|
| 10 |
+
from utils import CHECKPOINT_READ_PATH, PERTURBATIONS, BABYLM_DATA_PATH, \
|
| 11 |
+
PAREN_MODELS, gpt2_original_tokenizer
|
| 12 |
+
from tqdm import tqdm
|
| 13 |
+
from glob import glob
|
| 14 |
+
from numpy.random import default_rng
|
| 15 |
+
import pandas as pd
|
| 16 |
+
import torch
|
| 17 |
+
import itertools
|
| 18 |
+
import argparse
|
| 19 |
+
import os
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
MAX_TRAINING_STEPS = 3000
|
| 23 |
+
CHECKPOINTS = list(range(100, MAX_TRAINING_STEPS+1, 100))
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
def create_attention_mask(token_lists):
|
| 27 |
+
seq_length = max([len(i) for i in token_lists])
|
| 28 |
+
batch_size = len(token_lists)
|
| 29 |
+
mask = torch.full((batch_size, seq_length), 0)
|
| 30 |
+
|
| 31 |
+
for i, tokens in enumerate(token_lists):
|
| 32 |
+
mask[i, 0:len(tokens)] = 1
|
| 33 |
+
|
| 34 |
+
return mask
|
| 35 |
+
|
| 36 |
+
def create_input_ids(token_lists, pad_token_id):
|
| 37 |
+
padded = zip(*itertools.zip_longest(*token_lists, fillvalue=pad_token_id))
|
| 38 |
+
return torch.tensor(list(padded))
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
def get_perplexities(model, token_lists, pad_token_id, device="cuda"):
|
| 42 |
+
|
| 43 |
+
# Prepare data
|
| 44 |
+
input_ids = create_input_ids(token_lists, pad_token_id).to(device)
|
| 45 |
+
labels = input_ids.clone() # GPT-2 uses input as labels for CLM task
|
| 46 |
+
attention_mask = create_attention_mask(token_lists).to(device)
|
| 47 |
+
|
| 48 |
+
# Forward pass
|
| 49 |
+
outputs = model(input_ids=input_ids, labels=labels,
|
| 50 |
+
attention_mask=attention_mask)
|
| 51 |
+
|
| 52 |
+
# The "shifted" nature of labels in GPT-2 (next token prediction)
|
| 53 |
+
# Shift logits, labels, and attention mask by one position
|
| 54 |
+
shift_logits = outputs.logits[..., :-1, :].contiguous()
|
| 55 |
+
shift_labels = labels[..., 1:].contiguous()
|
| 56 |
+
shift_attention_mask = attention_mask[..., 1:].contiguous()
|
| 57 |
+
|
| 58 |
+
# Instantiate loss function with no reduction
|
| 59 |
+
loss_fct = torch.nn.CrossEntropyLoss(reduction='none')
|
| 60 |
+
|
| 61 |
+
# Calculate per-token loss
|
| 62 |
+
loss = loss_fct(shift_logits.view(-1, shift_logits.size(-1)),
|
| 63 |
+
shift_labels.view(-1))
|
| 64 |
+
|
| 65 |
+
# Reshape back to the original batch size and sequence length
|
| 66 |
+
loss = loss.view(shift_labels.size())
|
| 67 |
+
|
| 68 |
+
# Apply the attention mask - only calculate loss where mask is 1
|
| 69 |
+
loss = loss * shift_attention_mask
|
| 70 |
+
|
| 71 |
+
# Sum the loss over the sequence length, get per-example perplexity
|
| 72 |
+
per_example_loss = loss.sum(dim=1) / shift_attention_mask.sum(dim=1)
|
| 73 |
+
return torch.exp(per_example_loss).tolist()
|
| 74 |
+
|
| 75 |
+
|
| 76 |
+
if __name__ == "__main__":
|
| 77 |
+
|
| 78 |
+
parser = argparse.ArgumentParser(
|
| 79 |
+
prog='Edge probing',
|
| 80 |
+
description='Edge probing experiments')
|
| 81 |
+
parser.add_argument('perturbation_type',
|
| 82 |
+
default='all',
|
| 83 |
+
const='all',
|
| 84 |
+
nargs='?',
|
| 85 |
+
choices=PERTURBATIONS.keys(),
|
| 86 |
+
help='Perturbation function used to transform BabyLM dataset')
|
| 87 |
+
parser.add_argument('test_perturbation_type',
|
| 88 |
+
default='all',
|
| 89 |
+
const='all',
|
| 90 |
+
nargs='?',
|
| 91 |
+
choices=PERTURBATIONS.keys(),
|
| 92 |
+
help='Perturbation function used to transform test BabyLM dataset')
|
| 93 |
+
parser.add_argument('train_set',
|
| 94 |
+
default='all',
|
| 95 |
+
const='all',
|
| 96 |
+
nargs='?',
|
| 97 |
+
choices=["100M", "10M"],
|
| 98 |
+
help='BabyLM train set')
|
| 99 |
+
parser.add_argument('random_seed', type=int, help="Random seed")
|
| 100 |
+
parser.add_argument('paren_model',
|
| 101 |
+
default='all',
|
| 102 |
+
const='all',
|
| 103 |
+
nargs='?',
|
| 104 |
+
choices=list(PAREN_MODELS.keys()) + ["randinit"],
|
| 105 |
+
help='Parenthesis model')
|
| 106 |
+
parser.add_argument('-np', '--no_pos_encodings', action='store_true',
|
| 107 |
+
help="Train GPT-2 with no positional encodings")
|
| 108 |
+
|
| 109 |
+
# Get args
|
| 110 |
+
args = parser.parse_args()
|
| 111 |
+
no_pos_encodings_underscore = "_no_positional_encodings" if args.no_pos_encodings else ""
|
| 112 |
+
|
| 113 |
+
# Get path to model
|
| 114 |
+
model = f"babylm_{args.perturbation_type}_{args.train_set}_{args.paren_model}{no_pos_encodings_underscore}_seed{args.random_seed}"
|
| 115 |
+
model_path = f"{CHECKPOINT_READ_PATH}/babylm_{args.perturbation_type}_{args.train_set}_{args.paren_model}{no_pos_encodings_underscore}/{model}/runs/{model}/checkpoint-"
|
| 116 |
+
|
| 117 |
+
# Get perturbed test files
|
| 118 |
+
test_files = sorted(glob(
|
| 119 |
+
f"{BABYLM_DATA_PATH}/babylm_data_perturbed/babylm_{args.test_perturbation_type}/babylm_test_affected/*"))
|
| 120 |
+
|
| 121 |
+
FILE_SAMPLE_SIZE = 1000
|
| 122 |
+
rng = default_rng(args.random_seed)
|
| 123 |
+
|
| 124 |
+
# Iterate over data files to get perplexity data
|
| 125 |
+
print("Sampling BabyLM affected test files to extract surprisals...")
|
| 126 |
+
token_sequences = []
|
| 127 |
+
for test_file in test_files:
|
| 128 |
+
print(test_file)
|
| 129 |
+
|
| 130 |
+
# Get tokens from test file and subsample
|
| 131 |
+
f = open(test_file, 'r')
|
| 132 |
+
file_token_sequences = [
|
| 133 |
+
[int(s) for s in l.split()] for l in f.readlines()]
|
| 134 |
+
sample_indices = rng.choice(
|
| 135 |
+
list(range(len(file_token_sequences))), FILE_SAMPLE_SIZE, replace=False)
|
| 136 |
+
file_token_sequences = [file_token_sequences[i]
|
| 137 |
+
for i in sample_indices]
|
| 138 |
+
token_sequences.extend(file_token_sequences)
|
| 139 |
+
|
| 140 |
+
# For logging/debugging, include decoded sentence
|
| 141 |
+
test_sents = [gpt2_original_tokenizer.decode(
|
| 142 |
+
toks) for toks in token_sequences]
|
| 143 |
+
|
| 144 |
+
ppl_df = pd.DataFrame({
|
| 145 |
+
"Sentences": test_sents
|
| 146 |
+
})
|
| 147 |
+
|
| 148 |
+
BATCH_SIZE = 8
|
| 149 |
+
device = "cuda"
|
| 150 |
+
for ckpt in CHECKPOINTS:
|
| 151 |
+
print(f"Checkpoint: {ckpt}")
|
| 152 |
+
|
| 153 |
+
# Load model
|
| 154 |
+
if args.no_pos_encodings:
|
| 155 |
+
model = GPT2NoPositionalEncodingLMHeadModel.from_pretrained(
|
| 156 |
+
model_path + str(ckpt)).to(device)
|
| 157 |
+
else:
|
| 158 |
+
model = GPT2LMHeadModel.from_pretrained(
|
| 159 |
+
model_path + str(ckpt)).to(device)
|
| 160 |
+
|
| 161 |
+
# Get perplexities
|
| 162 |
+
perplexities = []
|
| 163 |
+
for i in tqdm(range(0, len(token_sequences), BATCH_SIZE)):
|
| 164 |
+
batch = token_sequences[i:i+BATCH_SIZE]
|
| 165 |
+
ppls = get_perplexities(
|
| 166 |
+
model, batch, gpt2_original_tokenizer.eos_token_id)
|
| 167 |
+
perplexities.extend(ppls)
|
| 168 |
+
|
| 169 |
+
# Add ppls to df
|
| 170 |
+
ppl_df[f'Perplexities (ckpt {ckpt})'] = perplexities
|
| 171 |
+
|
| 172 |
+
# Write results to CSV
|
| 173 |
+
directory = f"perplexity_results/{args.perturbation_type}_{args.train_set}{no_pos_encodings_underscore}"
|
| 174 |
+
if not os.path.exists(directory):
|
| 175 |
+
os.makedirs(directory)
|
| 176 |
+
file = directory + \
|
| 177 |
+
f"/{args.paren_model}_seed{args.random_seed}_test_{args.test_perturbation_type}.csv"
|
| 178 |
+
print(f"Writing results to CSV: {file}")
|
| 179 |
+
ppl_df.to_csv(file)
|
perplexities/perplexities_llama.py
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
import sys
|
| 3 |
+
import argparse
|
| 4 |
+
import os
|
| 5 |
+
sys.path.append("..")
|
| 6 |
+
from transformers import AutoTokenizer, AutoModelForCausalLM, Trainer, TrainingArguments, DataCollatorForLanguageModeling
|
| 7 |
+
from datasets import load_dataset
|
| 8 |
+
from numpy.random import default_rng
|
| 9 |
+
os.environ["TOKENIZERS_PARALLELISM"] = "false"
|
| 10 |
+
|
| 11 |
+
# MODEL_NAME = "Llama-3.2-1B"
|
| 12 |
+
# MODEL_NAME = "Llama-3.2-1B-500"
|
| 13 |
+
|
| 14 |
+
MODEL_NAME = "GPT-2"
|
| 15 |
+
MODEL_NAME_SAVE = "GPT2-500"
|
| 16 |
+
FILE_SAMPLE_SIZE = 500
|
| 17 |
+
|
| 18 |
+
def get_perplexities(model, eval_dataset, batch_size):
|
| 19 |
+
data_collator = DataCollatorForLanguageModeling(tokenizer=tokenizer, mlm=False)
|
| 20 |
+
|
| 21 |
+
training_args = TrainingArguments(
|
| 22 |
+
output_dir="./tmp_trainer",
|
| 23 |
+
per_device_eval_batch_size=batch_size,
|
| 24 |
+
fp16=True,
|
| 25 |
+
report_to="none"
|
| 26 |
+
)
|
| 27 |
+
|
| 28 |
+
trainer = Trainer(model=model, args=training_args, eval_dataset=eval_dataset, data_collator=data_collator)
|
| 29 |
+
eval_results = trainer.evaluate()
|
| 30 |
+
print("eval_results:", eval_results)
|
| 31 |
+
loss = eval_results['eval_loss']
|
| 32 |
+
perplexity = torch.exp(torch.tensor(loss)).item()
|
| 33 |
+
return perplexity
|
| 34 |
+
|
| 35 |
+
if __name__ == "__main__":
|
| 36 |
+
parser = argparse.ArgumentParser(description="Calculate perplexity on test dataset.")
|
| 37 |
+
|
| 38 |
+
parser.add_argument('perturbation',
|
| 39 |
+
type=str,
|
| 40 |
+
default='reverse_full',
|
| 41 |
+
nargs='?',
|
| 42 |
+
help='Type of perturbation to use.')
|
| 43 |
+
parser.add_argument('train_set',
|
| 44 |
+
type=str,
|
| 45 |
+
default='test',
|
| 46 |
+
nargs='?',
|
| 47 |
+
help='Dataset size for training.')
|
| 48 |
+
parser.add_argument('checkpoint_path',
|
| 49 |
+
type=str,
|
| 50 |
+
default='checkpoint-100',
|
| 51 |
+
nargs='?',
|
| 52 |
+
help='Dataset size for training.')
|
| 53 |
+
parser.add_argument('batch_size',
|
| 54 |
+
type=int,
|
| 55 |
+
default=4,
|
| 56 |
+
nargs='?',
|
| 57 |
+
help='Batch size for evaluation.')
|
| 58 |
+
parser.add_argument('seed',
|
| 59 |
+
type=int,
|
| 60 |
+
default=0,
|
| 61 |
+
nargs='?',
|
| 62 |
+
help='Random seed.')
|
| 63 |
+
args = parser.parse_args()
|
| 64 |
+
|
| 65 |
+
dataset_name = f"babylm_{args.perturbation}_{args.train_set}_seed{args.seed}"
|
| 66 |
+
dataset = load_dataset('../train/babylm_dataset_test.py', name=dataset_name, trust_remote_code=True)
|
| 67 |
+
test_dataset = dataset['test'] # Load test dataset
|
| 68 |
+
print(test_dataset)
|
| 69 |
+
|
| 70 |
+
|
| 71 |
+
checkpoint_path = f'../train/checkpoints/{MODEL_NAME}/babylm_{args.perturbation}_10M_seed0/runs/{args.checkpoint_path}'
|
| 72 |
+
|
| 73 |
+
rng = default_rng(args.seed)
|
| 74 |
+
indices = rng.choice(len(test_dataset), FILE_SAMPLE_SIZE, replace=False)
|
| 75 |
+
sampled_test_dataset = test_dataset.select(indices)
|
| 76 |
+
|
| 77 |
+
tokenizer = AutoTokenizer.from_pretrained(checkpoint_path)
|
| 78 |
+
model = AutoModelForCausalLM.from_pretrained(checkpoint_path)
|
| 79 |
+
|
| 80 |
+
model.eval()
|
| 81 |
+
if torch.cuda.is_available():
|
| 82 |
+
model.to('cuda')
|
| 83 |
+
|
| 84 |
+
def tokenize_function(examples):
|
| 85 |
+
return tokenizer(examples['text'], padding="max_length", truncation=True, max_length=1024)
|
| 86 |
+
|
| 87 |
+
tokenized_test = sampled_test_dataset.map(tokenize_function, batched=True, remove_columns=["text"])
|
| 88 |
+
|
| 89 |
+
|
| 90 |
+
perplexity = get_perplexities(model, tokenized_test, args.batch_size)
|
| 91 |
+
|
| 92 |
+
# Save the result to the specified directory
|
| 93 |
+
output_directory = f"perplexities_results/{MODEL_NAME_SAVE}"
|
| 94 |
+
os.makedirs(output_directory, exist_ok=True)
|
| 95 |
+
|
| 96 |
+
# Construct the output file path
|
| 97 |
+
output_file = os.path.join(output_directory, f"{args.perturbation}_{args.batch_size}_{args.seed}.csv")
|
| 98 |
+
|
| 99 |
+
# Write the header to the CSV file if it doesn't exist
|
| 100 |
+
if not os.path.exists(output_file):
|
| 101 |
+
with open(output_file, 'w') as f:
|
| 102 |
+
print("Writing header to CSV...")
|
| 103 |
+
f.write("checkpoint_path, perplexity\n")
|
| 104 |
+
|
| 105 |
+
# Append the perplexity result to the CSV file
|
| 106 |
+
with open(output_file, 'a') as f: # Open in append mode
|
| 107 |
+
print("Appending result to CSV...")
|
| 108 |
+
f.write(f"{args.checkpoint_path}, {perplexity}\n")
|
requirements.txt
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
certifi==2023.11.17
|
| 2 |
+
charset-normalizer==3.3.2
|
| 3 |
+
click==8.1.7
|
| 4 |
+
contourpy==1.1.1
|
| 5 |
+
cycler==0.12.1
|
| 6 |
+
data==0.4
|
| 7 |
+
emoji==2.8.0
|
| 8 |
+
fonttools==4.45.1
|
| 9 |
+
fsspec==2023.10.0
|
| 10 |
+
funcsigs==1.0.2
|
| 11 |
+
future==0.18.3
|
| 12 |
+
huggingface-hub==0.19.4
|
| 13 |
+
idna==3.6
|
| 14 |
+
iniconfig==2.0.0
|
| 15 |
+
joblib==1.3.2
|
| 16 |
+
kiwisolver==1.4.5
|
| 17 |
+
latex==0.7.0
|
| 18 |
+
matplotlib==3.7.5
|
| 19 |
+
mizani==0.9.3
|
| 20 |
+
nltk==3.8.1
|
| 21 |
+
numpy==1.24.4
|
| 22 |
+
pandas==2.0.3
|
| 23 |
+
patsy==0.5.3
|
| 24 |
+
Pillow==10.1.0
|
| 25 |
+
plotnine==0.12.4
|
| 26 |
+
pluggy==1.3.0
|
| 27 |
+
pluralizer==1.2.0
|
| 28 |
+
protobuf==4.25.1
|
| 29 |
+
pyparsing==3.1.1
|
| 30 |
+
pytest==7.4.3
|
| 31 |
+
pytz==2023.3.post1
|
| 32 |
+
PyYAML==6.0.1
|
| 33 |
+
regex==2023.10.3
|
| 34 |
+
requests==2.31.0
|
| 35 |
+
safetensors==0.4.1
|
| 36 |
+
scikit-learn==1.3.2
|
| 37 |
+
scipy==1.10.1
|
| 38 |
+
seaborn==0.13.0
|
| 39 |
+
shutilwhich==1.1.0
|
| 40 |
+
stanza==1.6.1
|
| 41 |
+
statsmodels==0.14.0
|
| 42 |
+
tempdir==0.7.1
|
| 43 |
+
threadpoolctl==3.2.0
|
| 44 |
+
tokenizers==0.15.0
|
| 45 |
+
tomli==2.0.1
|
| 46 |
+
torch==2.0.0
|
| 47 |
+
tqdm==4.66.1
|
| 48 |
+
transformers==4.35.2
|
| 49 |
+
triton==2.0.0
|
| 50 |
+
tzdata==2023.3
|
| 51 |
+
urllib3==2.1.0
|
| 52 |
+
asttokens==2.0.5
|
| 53 |
+
comm==0.1.2
|
| 54 |
+
debugpy==1.6.7
|
| 55 |
+
decorator==5.1.1
|
| 56 |
+
exceptiongroup==1.1.0
|
| 57 |
+
executing==1.2.0
|
| 58 |
+
filelock==3.12.2
|
| 59 |
+
gmpy2==2.1.0
|
| 60 |
+
importlib-metadata==6.0.0
|
| 61 |
+
ipykernel==6.23.1
|
| 62 |
+
ipython==8.0.0
|
| 63 |
+
jedi==0.18.2
|
| 64 |
+
Jinja2==3.1.2
|
| 65 |
+
jupyter_client==8.1.0
|
| 66 |
+
jupyter_core==5.1.0
|
| 67 |
+
latexcodec==1.0.0
|
| 68 |
+
MarkupSafe==2.1.2
|
| 69 |
+
matplotlib-inline==0.1.6
|
| 70 |
+
mpmath==1.2.1
|
| 71 |
+
nest-asyncio==1.5.6
|
| 72 |
+
networkx==2.8.6
|
| 73 |
+
packaging==23.0
|
| 74 |
+
parso==0.8.3
|
| 75 |
+
pexpect==4.8.0
|
| 76 |
+
pickleshare==0.7.5
|
| 77 |
+
platformdirs==2.5.2
|
| 78 |
+
prompt-toolkit==3.0.30
|
| 79 |
+
psutil==5.9.1
|
| 80 |
+
ptyprocess==0.7.0
|
| 81 |
+
pure-eval==0.2.2
|
| 82 |
+
Pygments==2.15.0
|
| 83 |
+
python-dateutil==2.8.2
|
| 84 |
+
pyzmq==23.0.0
|
| 85 |
+
six==1.16.0
|
| 86 |
+
stack-data==0.6.0
|
| 87 |
+
sympy==1.11.1
|
| 88 |
+
tornado==6.2
|
| 89 |
+
traitlets==5.7.1
|
| 90 |
+
typing_extensions==4.6.0
|
| 91 |
+
wcwidth==0.2.5
|
| 92 |
+
zipp==3.12.0
|
| 93 |
+
|
| 94 |
+
|
requirements_1.txt
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
certifi==2023.11.17
|
| 2 |
+
charset-normalizer==3.3.2
|
| 3 |
+
click==8.1.7
|
| 4 |
+
contourpy==1.2.0
|
| 5 |
+
cycler==0.12.1
|
| 6 |
+
data==0.4
|
| 7 |
+
emoji==2.8.0
|
| 8 |
+
fonttools==4.45.1
|
| 9 |
+
fsspec==2023.10.0
|
| 10 |
+
funcsigs==1.0.2
|
| 11 |
+
future==0.18.3
|
| 12 |
+
huggingface-hub==0.19.4
|
| 13 |
+
idna==3.6
|
| 14 |
+
iniconfig==2.0.0
|
| 15 |
+
joblib==1.3.2
|
| 16 |
+
kiwisolver==1.4.5
|
| 17 |
+
latex==0.7.0
|
| 18 |
+
matplotlib==3.8.2
|
| 19 |
+
mizani==0.9.3
|
| 20 |
+
nltk==3.8.1
|
| 21 |
+
numpy==1.26.2
|
| 22 |
+
pandas==2.1.3
|
| 23 |
+
patsy==0.5.3
|
| 24 |
+
Pillow==10.1.0
|
| 25 |
+
plotnine==0.12.4
|
| 26 |
+
pluggy==1.3.0
|
| 27 |
+
pluralizer==1.2.0
|
| 28 |
+
protobuf==4.25.1
|
| 29 |
+
pyparsing==3.1.1
|
| 30 |
+
pytest==7.4.3
|
| 31 |
+
pytz==2023.3.post1
|
| 32 |
+
PyYAML==6.0.1
|
| 33 |
+
regex==2023.10.3
|
| 34 |
+
requests==2.31.0
|
| 35 |
+
safetensors==0.4.1
|
| 36 |
+
scikit-learn==1.3.2
|
| 37 |
+
scipy==1.11.4
|
| 38 |
+
seaborn==0.13.0
|
| 39 |
+
shutilwhich==1.1.0
|
| 40 |
+
stanza==1.6.1
|
| 41 |
+
statsmodels==0.14.0
|
| 42 |
+
tempdir==0.7.1
|
| 43 |
+
threadpoolctl==3.2.0
|
| 44 |
+
tokenizers==0.15.0
|
| 45 |
+
tomli==2.0.1
|
| 46 |
+
torch==2.0.0
|
| 47 |
+
tqdm==4.66.1
|
| 48 |
+
transformers==4.35.2
|
| 49 |
+
triton==2.0.0
|
| 50 |
+
tzdata==2023.3
|
| 51 |
+
urllib3==2.1.0
|