-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathppl_llada.py
More file actions
157 lines (114 loc) · 4.97 KB
/
Copy pathppl_llada.py
File metadata and controls
157 lines (114 loc) · 4.97 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
import torch
import torch.nn.functional as F
from transformers import AutoTokenizer, AutoModel
from datasets import load_dataset
import os
import random
from tqdm import tqdm
import numpy as np
seed = 2025
os.environ['PYTHONHASHSEED'] = str(seed)
random.seed(seed)
np.random.seed(seed)
torch.manual_seed(seed)
torch.cuda.manual_seed(seed)
torch.cuda.manual_seed_all(seed)
torch.backends.cudnn.benchmark = False
torch.backends.cudnn.deterministic = True
def forward_process(batch, prompt_index, mask_id):
b, l = batch.shape
target_len = (l - prompt_index.sum()).item()
if target_len <= 0:
return batch, torch.zeros(b, l, device=batch.device)
k = torch.randint(1, target_len + 1, (), device=batch.device)
x = torch.round(torch.linspace(float(k), k + (b - 1) * (target_len / b), steps=b, device=batch.device)).long()
x = ((x - 1) % target_len) + 1
assert x.min() >= 1 and x.max() <= target_len
indices = torch.arange(target_len, device=batch.device).repeat(b, 1)
is_mask = indices < x.unsqueeze(1)
for i in range(b):
is_mask[i] = is_mask[i][torch.randperm(target_len)]
is_mask = torch.cat((torch.zeros(b, prompt_index.sum(), dtype=torch.bool, device=batch.device), is_mask), dim=1)
noisy_batch = torch.where(is_mask, mask_id, batch)
return noisy_batch, (x / target_len).unsqueeze(1).repeat(1, l)
def get_logits(model, batch, prompt_index, cfg_scale, mask_id):
if cfg_scale > 0.:
assert len(prompt_index) == batch.shape[1]
prompt_index = prompt_index.unsqueeze(0).repeat(batch.shape[0], 1)
un_batch = batch.clone()
un_batch[prompt_index] = mask_id
batch = torch.cat([batch, un_batch])
input_ids = batch
logits = model(input_ids).logits
if cfg_scale > 0.:
logits, un_logits = torch.chunk(logits, 2, dim=0)
logits = un_logits + (cfg_scale + 1) * (logits - un_logits)
return logits
@torch.no_grad()
def get_ppl(model, prompt, answer, mc_num=8, cfg_scale=0., mask_id=126336):
seq = torch.cat([prompt, answer], dim=0).unsqueeze(0)
prompt_index = torch.arange(seq.shape[1], device=model.device) < len(prompt)
losses = []
for _ in range(mc_num):
perturbed_seq, p_mask = forward_process(seq, prompt_index, mask_id)
mask_index = perturbed_seq == mask_id
if not torch.any(mask_index):
continue
logits = get_logits(model, perturbed_seq, prompt_index, cfg_scale, mask_id)
if logits.shape[0] != seq.shape[0] or not torch.any(mask_index.squeeze()):
continue
loss = F.cross_entropy(logits[mask_index], seq[mask_index], reduction='mean')
losses.append(loss.item())
if not losses:
return float('nan')
# current block PPL
return np.exp(sum(losses) / len(losses))
def main(path):
device = 'cuda:4' if torch.cuda.is_available() else 'cpu'
try:
model = AutoModel.from_pretrained(path, trust_remote_code=True, torch_dtype=torch.bfloat16).to(device).eval()
tokenizer = AutoTokenizer.from_pretrained(path, trust_remote_code=True)
except Exception as e:
print(e)
return
try:
mask_id = tokenizer.mask_token_id
if mask_id is None: raise AttributeError
except (AttributeError, KeyError):
mask_id = 126336
wikitext_dataset = load_dataset('wikitext', 'wikitext-2-raw-v1', split='test', streaming=True)
# wikitext_dataset = load_dataset('Salesforce/wikitext', split='test', streaming=True)
text_list = [line for line in wikitext_dataset['text'] if line.strip()]
# text_list = text_list[:100]
text = "\n".join(text_list)
input_ids = tokenizer.encode(text)
print(f'token count: {len(input_ids)}')
max_length = 1024
chunk_size = 64
stride = 64
perplexities = []
print(f"evaluating... (max_length={max_length}, chunk_size={chunk_size}, stride={stride})")
for i in tqdm(range(chunk_size, len(input_ids) - chunk_size, stride)):
prompt_start = max(0, i - (max_length - chunk_size))
prompt_tokens = torch.tensor(input_ids[prompt_start:i]).to(device)
answer_tokens = torch.tensor(input_ids[i : i + chunk_size]).to(device)
if prompt_tokens.numel() == 0 or answer_tokens.numel() < chunk_size:
continue
ppl = get_ppl(model, prompt_tokens, answer_tokens, mc_num=128, mask_id=mask_id)
if not np.isnan(ppl):
perplexities.append(ppl)
num_samples = len(perplexities)
if num_samples > 0:
log_ppls = np.log(np.array(perplexities))
final_ppl = np.exp(np.mean(log_ppls))
print(f"\nwikitext-2 PPL: {final_ppl:.4f} (Based on {num_samples} samples)")
else:
print("No valid samples were found for PPL calculation.")
print("-" * 50)
if __name__ == '__main__':
models_to_evaluate = [
'GSAI-ML/LLaDA-8B-Base',
'GSAI-ML/LLaDA-8B-Instruct'
]
for model_path in models_to_evaluate:
main(model_path)