lta / LTA_openwebtext_dualt /scripts /apple_to_apple_lta_checks.py
JinghuiLuAstronaut's picture
Add files using upload-large-folder tool
8ed0c93 verified
Raw
History Blame Contribute Delete
29.4 kB
#!/usr/bin/env python3
from __future__ import annotations
import argparse
import csv
import json
import math
import sys
from collections import Counter
from pathlib import Path
from typing import Any, Iterable
import torch
import torch.nn.functional as F
from torch.utils.data import DataLoader
REPO_ROOT = Path(__file__).resolve().parents[1]
if str(REPO_ROOT) not in sys.path:
sys.path.insert(0, str(REPO_ROOT))
from eval import build_model_from_ckpt
from flowtext_lab.bridges import make_dirichlet_bridge_batch
from flowtext_lab.data import EosPadCollator, WrappedStreamingTextSequenceDataset, iter_text_records
from flowtext_lab.decode import sample_noise_simplex, state_for_model
from flowtext_lab.tokenization import BpeTextTokenizer
from train import TokenizedTextCollator, load_tokenized_hf_dataset
def token_piece(tok: BpeTextTokenizer, idx: int) -> str:
raw = getattr(tok, "tokenizer", None)
id_to_token = getattr(raw, "id_to_token", None)
if callable(id_to_token):
piece = id_to_token(int(idx))
if piece is not None:
return str(piece)
return tok.decode([int(idx)], stop_at_eos=False, skip_special_tokens=False)
def token_text(tok: BpeTextTokenizer, idx: int) -> str:
return tok.decode([int(idx)], stop_at_eos=False, skip_special_tokens=False)
def compact_piece(s: str) -> str:
return s.replace("\n", "\\n").replace("\t", "\\t")
def load_batch(
*,
data_path: str,
tokenizer: BpeTextTokenizer,
max_len: int,
batch_size: int,
mode: str,
text_column: str | None,
openwebtext_split: str,
wrap_mode: str,
max_records: int,
tokenized_pad_token: str,
) -> dict[str, torch.Tensor]:
if mode == "tokenized_hf":
ds = load_tokenized_hf_dataset(data_path, max_records=max_records)
pad_id = tokenizer.pad_id if tokenized_pad_token == "pad" and tokenizer.pad_id is not None else tokenizer.eos_id
collate = TokenizedTextCollator(pad_id, max_len=max_len)
examples = [ds[i] for i in range(min(batch_size, len(ds)))]
return collate(examples)
if mode != "wrap":
raise ValueError(f"unknown data mode: {mode}")
ds = WrappedStreamingTextSequenceDataset(
data_path,
tokenizer,
max_len=max_len,
text_column=text_column,
openwebtext_split=openwebtext_split,
max_records_per_epoch=max_records,
wrap_mode=wrap_mode,
)
loader = DataLoader(ds, batch_size=batch_size, collate_fn=EosPadCollator(tokenizer.eos_id, max_len=max_len))
return next(iter(loader))
def iter_record_lengths(
*,
data_path: str,
tokenizer: BpeTextTokenizer,
mode: str,
text_column: str | None,
openwebtext_split: str,
max_records: int,
) -> Iterable[int]:
if mode == "tokenized_hf":
ds = load_tokenized_hf_dataset(data_path, max_records=max_records)
for ex in ds:
raw = ex["input_ids"]
if hasattr(raw, "tolist"):
raw = raw.tolist()
yield len(raw)
return
for i, text in enumerate(
iter_text_records(
data_path,
text_column=text_column,
openwebtext_split=openwebtext_split,
detokenizer="auto",
)
):
if i >= max_records:
break
ids = tokenizer.encode(text, add_eos=False, add_special_tokens=False)
yield len(ids)
def rate_summary(values: list[float]) -> dict[str, float]:
if not values:
return {"mean": 0.0, "min": 0.0, "p50": 0.0, "p90": 0.0, "p99": 0.0, "max": 0.0}
vals = sorted(float(x) for x in values)
n = len(vals)
def q(p: float) -> float:
return vals[min(n - 1, max(0, int(round((n - 1) * p))))]
return {
"mean": float(sum(vals) / n),
"min": float(vals[0]),
"p50": float(q(0.5)),
"p90": float(q(0.9)),
"p99": float(q(0.99)),
"max": float(vals[-1]),
}
def distribution_entropy_from_counts(counts: Counter[int]) -> float:
total = sum(counts.values())
if total <= 0:
return 0.0
out = 0.0
for c in counts.values():
p = c / total
out -= p * math.log(max(p, 1e-12))
return float(out)
def token_feature_rates(ids: torch.Tensor, tok: BpeTextTokenizer) -> dict[str, float]:
flat = [int(x) for x in ids.reshape(-1).tolist()]
if not flat:
return {}
pieces = [token_piece(tok, x) for x in flat]
texts = [token_text(tok, x) for x in flat]
specials = {tok.eos_id, tok.bos_id, tok.unk_id}
if tok.pad_id is not None:
specials.add(tok.pad_id)
denom = len(flat)
normal = [i for i, x in enumerate(flat) if x not in specials]
normal_denom = max(len(normal), 1)
return {
"bert_hash_rate": sum(pieces[i].startswith("##") for i in normal) / normal_denom,
"spm_cont_rate": sum((not pieces[i].startswith("▁")) and (not pieces[i].startswith("<")) for i in normal) / normal_denom,
"single_char_rate": sum(len(texts[i].strip()) == 1 for i in normal) / normal_denom,
"digit_piece_rate": sum(any(ch.isdigit() for ch in pieces[i]) for i in normal) / normal_denom,
"url_piece_rate": sum(("http" in pieces[i].lower() or "www" in pieces[i].lower() or ".com" in pieces[i].lower()) for i in normal) / normal_denom,
"special_rate": sum(x in specials for x in flat) / denom,
}
def command_data(args: argparse.Namespace) -> None:
tok = BpeTextTokenizer.from_file(args.tokenizer_path)
batch = load_batch(
data_path=args.data_path,
tokenizer=tok,
max_len=args.max_len,
batch_size=args.n_sequences,
mode=args.data_mode,
text_column=args.text_column,
openwebtext_split=args.openwebtext_split,
wrap_mode=args.wrap_mode,
max_records=args.max_records,
tokenized_pad_token=args.tokenized_pad_token,
)
ids = batch["ids"]
attn = batch.get("attn_mask", torch.ones_like(ids, dtype=torch.bool))
valid_ids = ids[attn]
counts = Counter(int(x) for x in valid_ids.tolist())
top = [
{
"id": int(i),
"piece": compact_piece(token_piece(tok, int(i))),
"text": compact_piece(token_text(tok, int(i))),
"count": int(c),
"rate": float(c / max(valid_ids.numel(), 1)),
}
for i, c in counts.most_common(args.top_k)
]
seq_lens = attn.long().sum(dim=1).tolist()
internal = ids[:, 1:-1] if ids.size(1) > 2 else ids[:, :0]
internal_attn = attn[:, 1:-1] if attn.size(1) > 2 else attn[:, :0]
eos_internal = ((internal == tok.eos_id) & internal_attn).long().sum(dim=1).tolist()
pad_internal = []
if tok.pad_id is not None:
pad_internal = ((internal == tok.pad_id) & internal_attn).long().sum(dim=1).tolist()
pos0 = Counter(int(x) for x in ids[:, 0].tolist())
last_valid = []
for row, mask in zip(ids, attn):
idx = int(mask.long().sum().item()) - 1
if idx >= 0:
last_valid.append(int(row[idx].item()))
last_counts = Counter(last_valid)
record_lengths = list(
iter_record_lengths(
data_path=args.data_path,
tokenizer=tok,
mode=args.data_mode,
text_column=args.text_column,
openwebtext_split=args.openwebtext_split,
max_records=args.max_records,
)
)
out = {
"name": args.name,
"data_path": args.data_path,
"data_mode": args.data_mode,
"tokenizer_path": args.tokenizer_path,
"vocab_size": tok.vocab_size,
"bos_id": tok.bos_id,
"bos_piece": token_piece(tok, tok.bos_id),
"eos_id": tok.eos_id,
"eos_piece": token_piece(tok, tok.eos_id),
"pad_id": tok.pad_id,
"n_sequences": int(ids.size(0)),
"max_len": args.max_len,
"sequence_len": rate_summary([float(x) for x in seq_lens]),
"record_token_len_no_special_no_eos": rate_summary([float(x) for x in record_lengths]),
"internal_eos_per_seq": rate_summary([float(x) for x in eos_internal]),
"internal_pad_per_seq": rate_summary([float(x) for x in pad_internal]) if pad_internal else None,
"pos0_top": [
{"id": i, "piece": compact_piece(token_piece(tok, i)), "count": c, "rate": c / max(ids.size(0), 1)}
for i, c in pos0.most_common(args.top_k)
],
"last_valid_top": [
{"id": i, "piece": compact_piece(token_piece(tok, i)), "count": c, "rate": c / max(len(last_valid), 1)}
for i, c in last_counts.most_common(args.top_k)
],
"unigram_entropy": distribution_entropy_from_counts(counts),
"token_feature_rates": token_feature_rates(valid_ids, tok),
"top_unigram": top,
}
Path(args.out_json).parent.mkdir(parents=True, exist_ok=True)
Path(args.out_json).write_text(json.dumps(out, indent=2, ensure_ascii=False), encoding="utf-8")
print(json.dumps(out, indent=2, ensure_ascii=False), flush=True)
def ckpt_arg(ckpt_args: dict[str, Any], key: str, default: Any) -> Any:
return ckpt_args.get(key, default)
def make_bridge_for_eval(
*,
ids: torch.Tensor,
attn: torch.Tensor,
ckpt_args: dict[str, Any],
vocab_size: int,
t_value: float,
force_mask_ratio: float | None,
eps: float,
) -> Any:
return make_dirichlet_bridge_batch(
ids=ids,
attn_mask=attn,
vocab_size=vocab_size,
target_prob=float(ckpt_arg(ckpt_args, "target_prob", 1.0)),
min_t=float(ckpt_arg(ckpt_args, "min_t", 0.0)),
max_t=float(ckpt_arg(ckpt_args, "max_t", 1.0)),
min_mask_ratio=float(ckpt_arg(ckpt_args, "min_mask_ratio", 0.1)),
max_mask_ratio=float(ckpt_arg(ckpt_args, "max_mask_ratio", 1.0)),
wrong_token_replace_prob=ckpt_arg(ckpt_args, "wrong_token_replace_prob", "0.0"),
wrong_token_schedule=str(ckpt_arg(ckpt_args, "wrong_token_schedule", "constant")),
wrong_token_exp_k=float(ckpt_arg(ckpt_args, "wrong_token_exp_k", 1.0)),
dirichlet_concentration_min=float(ckpt_arg(ckpt_args, "dirichlet_concentration_min", 1.0)),
dirichlet_concentration_max=float(ckpt_arg(ckpt_args, "dirichlet_concentration_max", 1024.0)),
eps=eps,
state_format=str(ckpt_arg(ckpt_args, "state_format", ckpt_arg(ckpt_args, "input_format", "prob"))),
dirichlet_endpoint_mode=str(ckpt_arg(ckpt_args, "dirichlet_endpoint_mode", "bernoulli_wrong")),
dirichlet_semantic_t_mode=str(ckpt_arg(ckpt_args, "dirichlet_semantic_t_mode", "same")),
dirichlet_semantic_t_value=float(ckpt_arg(ckpt_args, "dirichlet_semantic_t_value", 0.0)),
dirichlet_semantic_t_curve=str(ckpt_arg(ckpt_args, "dirichlet_semantic_t_curve", "linear")),
dirichlet_semantic_t_power=float(ckpt_arg(ckpt_args, "dirichlet_semantic_t_power", 1.0)),
dirichlet_support_t_curve=str(ckpt_arg(ckpt_args, "dirichlet_support_t_curve", "linear")),
dirichlet_support_t_power=float(ckpt_arg(ckpt_args, "dirichlet_support_t_power", 1.0)),
endpoint_sequence_random_prob_alpha=float(ckpt_arg(ckpt_args, "endpoint_sequence_random_prob_alpha", 0.0)),
categorical_wrong_from_full_vocab=bool(ckpt_arg(ckpt_args, "categorical_wrong_from_full_vocab", False)),
categorical_wrong_from_batch_valid_tokens=bool(ckpt_arg(ckpt_args, "categorical_wrong_from_batch_valid_tokens", False)),
categorical_wrong_basin_token_ids=ckpt_arg(ckpt_args, "categorical_wrong_basin_token_ids", ""),
categorical_wrong_basin_prob=float(ckpt_arg(ckpt_args, "categorical_wrong_basin_prob", 0.0)),
categorical_wrong_unigram_prob=float(ckpt_arg(ckpt_args, "categorical_wrong_unigram_prob", 0.0)),
categorical_wrong_uniform_prob=float(ckpt_arg(ckpt_args, "categorical_wrong_uniform_prob", 0.0)),
categorical_wrong_prob_floor=float(ckpt_arg(ckpt_args, "categorical_wrong_prob_floor", 0.0)),
categorical_gold_prob_floor=float(ckpt_arg(ckpt_args, "categorical_gold_prob_floor", 0.0)),
categorical_gold_prob_ceil=float(ckpt_arg(ckpt_args, "categorical_gold_prob_ceil", 1.0)),
simplex_bridge_sampler=str(ckpt_arg(ckpt_args, "simplex_bridge_sampler", "dirichlet")),
logistic_normal_sigma_min=float(ckpt_arg(ckpt_args, "logistic_normal_sigma_min", 0.18)),
logistic_normal_sigma_max=float(ckpt_arg(ckpt_args, "logistic_normal_sigma_max", 2.2)),
logistic_normal_tau_min=float(ckpt_arg(ckpt_args, "logistic_normal_tau_min", 0.65)),
logistic_normal_tau_max=float(ckpt_arg(ckpt_args, "logistic_normal_tau_max", 1.15)),
force_t=t_value,
force_mask_ratio=force_mask_ratio,
mask_ratio_floor_schedule=str(ckpt_arg(ckpt_args, "mask_ratio_floor_schedule", "none")),
mask_mixture_original_prob=float(ckpt_arg(ckpt_args, "mask_mixture_original_prob", 0.0)),
mask_mixture_lowk_prob=float(ckpt_arg(ckpt_args, "mask_mixture_lowk_prob", 0.0)),
mask_mixture_lowcorrupt_prob=float(ckpt_arg(ckpt_args, "mask_mixture_lowcorrupt_prob", 0.0)),
mask_mixture_block_prob=float(ckpt_arg(ckpt_args, "mask_mixture_block_prob", 0.0)),
mask_mixture_all_prob=float(ckpt_arg(ckpt_args, "mask_mixture_all_prob", 0.0)),
mask_mixture_lowk_clean_tokens=ckpt_arg(ckpt_args, "mask_mixture_lowk_clean_tokens", "1,2,4,8,16,32,64"),
mask_mixture_lowcorrupt_tokens=ckpt_arg(ckpt_args, "mask_mixture_lowcorrupt_tokens", "1,2,4,8,16,32,64"),
mask_mixture_block_tokens=ckpt_arg(ckpt_args, "mask_mixture_block_tokens", "64,128"),
clean_state_mode=str(ckpt_arg(ckpt_args, "clean_state_mode", "onehot")),
return_dense_targets=False,
)
def masked_loss_acc(logits: torch.Tensor, target: torch.Tensor, mask: torch.Tensor) -> dict[str, float]:
flat_mask = mask.reshape(-1)
if not bool(flat_mask.any().item()):
return {"nll": 0.0, "ppl": 1.0, "acc": 0.0, "tokens": 0}
flat_logits = logits.reshape(-1, logits.size(-1))[flat_mask]
flat_target = target.reshape(-1)[flat_mask]
loss = F.cross_entropy(flat_logits, flat_target, reduction="mean")
pred = flat_logits.argmax(dim=-1)
acc = (pred == flat_target).float().mean()
return {
"nll": float(loss.detach().cpu()),
"ppl": float(torch.exp(loss.clamp(max=50)).detach().cpu()),
"acc": float(acc.detach().cpu()),
"tokens": int(flat_mask.sum().detach().cpu()),
}
@torch.inference_mode()
def command_teacher(args: argparse.Namespace) -> None:
tok = BpeTextTokenizer.from_file(args.tokenizer_path)
device = torch.device("cuda" if torch.cuda.is_available() and not args.cpu else "cpu")
ckpt = torch.load(args.checkpoint, map_location="cpu", weights_only=False)
ckpt_args = dict(ckpt.get("args", {}))
model = build_model_from_ckpt(ckpt, tok.vocab_size, args.max_len, device).eval()
batch = load_batch(
data_path=args.data_path,
tokenizer=tok,
max_len=args.max_len,
batch_size=args.batch_size,
mode=args.data_mode,
text_column=args.text_column,
openwebtext_split=args.openwebtext_split,
wrap_mode=args.wrap_mode,
max_records=args.max_records,
tokenized_pad_token=args.tokenized_pad_token,
)
ids = batch["ids"].to(device)
attn = batch.get("attn_mask", torch.ones_like(ids, dtype=torch.bool)).to(device)
rows = []
for t_value in [float(x) for x in args.t_values.split(",") if x.strip()]:
torch.manual_seed(args.seed + int(round(t_value * 1000000)))
bridge = make_bridge_for_eval(
ids=ids,
attn=attn,
ckpt_args=ckpt_args,
vocab_size=tok.vocab_size,
t_value=t_value,
force_mask_ratio=args.force_mask_ratio,
eps=args.eps,
)
model_t = bridge.t
logits = model(state_for_model(model, bridge.state, args.eps), model_t, attn).float()
valid = attn
corrupt = bridge.corrupt_mask & attn
pos0_pred = logits[:, 0].argmax(dim=-1)
last_pred = []
for b in range(ids.size(0)):
last = int(attn[b].long().sum().item()) - 1
last_pred.append(int(logits[b, last].argmax().detach().cpu()) if last >= 0 else -1)
pos0_counts = Counter(int(x) for x in pos0_pred.detach().cpu().tolist())
last_counts = Counter(last_pred)
probs = F.softmax(logits, dim=-1)
rows.append(
{
"name": args.name,
"checkpoint": args.checkpoint,
"ckpt_step": int(ckpt.get("step", -1)),
"t": t_value,
"force_mask_ratio": args.force_mask_ratio,
"corrupt_frac": float(corrupt.float().mean().detach().cpu()),
"wrong_frac": float((bridge.wrong_mask & attn).float().sum().detach().cpu() / attn.float().sum().clamp_min(1).detach().cpu()),
"valid": masked_loss_acc(logits, ids, valid),
"corrupt": masked_loss_acc(logits, ids, corrupt),
"dist_entropy": float((-(probs.clamp_min(args.eps) * probs.clamp_min(args.eps).log()).sum(dim=-1)[valid]).mean().detach().cpu()),
"mean_maxp": float(probs.max(dim=-1).values[valid].mean().detach().cpu()),
"pos0_gold_id": int(ids[0, 0].detach().cpu()),
"pos0_gold_piece": token_piece(tok, int(ids[0, 0].detach().cpu())),
"pos0_top": [
{"id": i, "piece": compact_piece(token_piece(tok, i)), "count": c, "rate": c / max(ids.size(0), 1)}
for i, c in pos0_counts.most_common(5)
],
"last_top": [
{"id": i, "piece": compact_piece(token_piece(tok, i)), "count": c, "rate": c / max(ids.size(0), 1)}
for i, c in last_counts.most_common(5)
],
}
)
out = Path(args.out_json)
out.parent.mkdir(parents=True, exist_ok=True)
out.write_text(json.dumps(rows, indent=2, ensure_ascii=False), encoding="utf-8")
with out.with_suffix(".tsv").open("w", newline="", encoding="utf-8") as f:
fields = [
"name",
"ckpt_step",
"t",
"force_mask_ratio",
"corrupt_frac",
"wrong_frac",
"valid_nll",
"valid_acc",
"corrupt_nll",
"corrupt_acc",
"dist_entropy",
"mean_maxp",
"pos0_gold_piece",
"pos0_top",
"last_top",
]
writer = csv.DictWriter(f, fieldnames=fields, delimiter="\t")
writer.writeheader()
for row in rows:
writer.writerow(
{
"name": row["name"],
"ckpt_step": row["ckpt_step"],
"t": row["t"],
"force_mask_ratio": row["force_mask_ratio"],
"corrupt_frac": row["corrupt_frac"],
"wrong_frac": row["wrong_frac"],
"valid_nll": row["valid"]["nll"],
"valid_acc": row["valid"]["acc"],
"corrupt_nll": row["corrupt"]["nll"],
"corrupt_acc": row["corrupt"]["acc"],
"dist_entropy": row["dist_entropy"],
"mean_maxp": row["mean_maxp"],
"pos0_gold_piece": row["pos0_gold_piece"],
"pos0_top": " | ".join(f"{x['piece']}:{x['rate']:.2f}" for x in row["pos0_top"]),
"last_top": " | ".join(f"{x['piece']}:{x['rate']:.2f}" for x in row["last_top"]),
}
)
for row in rows:
print(
f"{row['name']} step={row['ckpt_step']} t={row['t']:.4f} "
f"valid_nll={row['valid']['nll']:.3f} valid_acc={row['valid']['acc']:.3f} "
f"corrupt_nll={row['corrupt']['nll']:.3f} corrupt_acc={row['corrupt']['acc']:.3f} "
f"pos0={row['pos0_top'][0]['piece']}:{row['pos0_top'][0]['rate']:.2f}",
flush=True,
)
def filter_top_p(probs: torch.Tensor, top_p: float, eps: float) -> torch.Tensor:
if top_p >= 1.0:
return probs
sorted_vals, sorted_idx = torch.sort(probs, dim=-1, descending=True)
total = sorted_vals.sum(dim=-1, keepdim=True).clamp_min(eps)
remove = sorted_vals.cumsum(dim=-1) > top_p * total
remove[..., 0] = False
sorted_vals = sorted_vals.masked_fill(remove, 0.0)
out = torch.zeros_like(probs).scatter(-1, sorted_idx, sorted_vals)
return out / out.sum(dim=-1, keepdim=True).clamp_min(eps)
def distribution_metrics(probs: torch.Tensor, ids: torch.Tensor, tok: BpeTextTokenizer, prefix: str) -> dict[str, Any]:
p = probs.clamp_min(1e-12)
ent = float((-(p * p.log()).sum(dim=-1)).mean().detach().cpu())
maxp, arg = probs.max(dim=-1)
counts = Counter(int(x) for x in arg.reshape(-1).detach().cpu().tolist())
return {
f"{prefix}_entropy": ent,
f"{prefix}_mean_top_mass": float(maxp.mean().detach().cpu()),
f"{prefix}_argmax_token_entropy": distribution_entropy_from_counts(counts),
f"{prefix}_argmax_top": [
{"id": i, "piece": compact_piece(token_piece(tok, i)), "count": c, "rate": c / max(arg.numel(), 1)}
for i, c in counts.most_common(8)
],
**{f"{prefix}_{k}": v for k, v in token_feature_rates(arg.detach().cpu(), tok).items()},
}
@torch.inference_mode()
def command_trace(args: argparse.Namespace) -> None:
tok = BpeTextTokenizer.from_file(args.tokenizer_path)
device = torch.device("cuda" if torch.cuda.is_available() and not args.cpu else "cpu")
torch.manual_seed(args.seed)
ckpt = torch.load(args.checkpoint, map_location="cpu", weights_only=False)
model = build_model_from_ckpt(ckpt, tok.vocab_size, args.max_len, device).eval()
eps = args.eps
bs = args.batch_size
probs = sample_noise_simplex(
(bs, args.max_len),
tok.vocab_size,
device,
eps,
noise_mode="dirichlet",
target_prob=1.0,
noise_sigma=-1.0,
dirichlet_concentration=args.concentration_min,
)
attn = torch.ones((bs, args.max_len), dtype=torch.bool, device=device)
log_cmin = math.log(args.concentration_min)
log_cmax = math.log(args.concentration_max)
out = Path(args.out_jsonl)
out.parent.mkdir(parents=True, exist_ok=True)
snapshot = set(int(x) for x in args.trace_steps.split(",") if x.strip())
last_endpoint = probs
with out.open("w", encoding="utf-8") as f:
for step in range(args.steps):
support_t = (step + 1) / max(args.steps, 1)
t = torch.full((bs,), support_t, dtype=torch.float32, device=device)
logits = model(state_for_model(model, probs, eps), t, attn).float()
endpoint = F.softmax(logits / args.endpoint_temp, dim=-1)
endpoint = filter_top_p(endpoint, args.endpoint_top_p, eps)
tau = args.gumbel_tau_start + support_t * (args.gumbel_tau_end - args.gumbel_tau_start)
uniform = torch.rand_like(endpoint).clamp_(eps, 1.0 - eps)
gumbel = -torch.log(-torch.log(uniform))
projected = F.softmax((endpoint.clamp_min(eps).log() + gumbel) / max(tau, eps), dim=-1)
last_endpoint = projected
mean = (1.0 - support_t) / tok.vocab_size + support_t * projected
mean = mean / mean.sum(dim=-1, keepdim=True).clamp_min(eps)
conc = math.exp(log_cmin + support_t * (log_cmax - log_cmin))
alpha = (mean * conc).clamp_min(eps)
probs = torch._standard_gamma(alpha).clamp_min(eps)
probs = probs / probs.sum(dim=-1, keepdim=True).clamp_min(eps)
step_num = step + 1
if step_num in snapshot or step_num == args.steps:
row = {
"name": args.name,
"ckpt_step": int(ckpt.get("step", -1)),
"step": step_num,
"support_t": support_t,
"tau": tau,
"concentration": conc,
}
row.update(distribution_metrics(endpoint, endpoint.argmax(dim=-1), tok, "a"))
row.update(distribution_metrics(projected, projected.argmax(dim=-1), tok, "e"))
row.update(distribution_metrics(probs, probs.argmax(dim=-1), tok, "p"))
for pos in [0, 1, args.max_len - 2, args.max_len - 1]:
a_id = int(endpoint[0, pos].argmax().detach().cpu())
e_id = int(projected[0, pos].argmax().detach().cpu())
p_id = int(probs[0, pos].argmax().detach().cpu())
row[f"pos{pos}_a"] = {"id": a_id, "piece": compact_piece(token_piece(tok, a_id)), "prob": float(endpoint[0, pos, a_id].detach().cpu())}
row[f"pos{pos}_e"] = {"id": e_id, "piece": compact_piece(token_piece(tok, e_id)), "prob": float(projected[0, pos, e_id].detach().cpu())}
row[f"pos{pos}_p"] = {"id": p_id, "piece": compact_piece(token_piece(tok, p_id)), "prob": float(probs[0, pos, p_id].detach().cpu())}
f.write(json.dumps(row, ensure_ascii=False) + "\n")
print(
f"{args.name} step={step_num} aH={row['a_entropy']:.2f} eH={row['e_entropy']:.2f} pH={row['p_entropy']:.2f} "
f"a_top={row['a_argmax_top'][0]['piece']}:{row['a_argmax_top'][0]['rate']:.2f} "
f"p_top={row['p_argmax_top'][0]['piece']}:{row['p_argmax_top'][0]['rate']:.2f}",
flush=True,
)
if args.final_out:
final_probs = 0.5 * probs + 0.5 * last_endpoint
ids = final_probs.argmax(dim=-1).detach().cpu().tolist()
Path(args.final_out).write_text("\n\n".join(tok.decode(row, stop_at_eos=False, skip_special_tokens=False) for row in ids), encoding="utf-8")
def main() -> None:
ap = argparse.ArgumentParser()
sub = ap.add_subparsers(dest="cmd", required=True)
data = sub.add_parser("data")
data.add_argument("--name", required=True)
data.add_argument("--data_path", required=True)
data.add_argument("--tokenizer_path", required=True)
data.add_argument("--out_json", required=True)
data.add_argument("--data_mode", choices=["wrap", "tokenized_hf"], default="wrap")
data.add_argument("--text_column", default=None)
data.add_argument("--openwebtext_split", default="all")
data.add_argument("--wrap_mode", default="stream")
data.add_argument("--tokenized_pad_token", default="pad")
data.add_argument("--max_len", type=int, default=1024)
data.add_argument("--n_sequences", type=int, default=2048)
data.add_argument("--max_records", type=int, default=20000)
data.add_argument("--top_k", type=int, default=24)
data.set_defaults(func=command_data)
teacher = sub.add_parser("teacher")
teacher.add_argument("--name", required=True)
teacher.add_argument("--checkpoint", required=True)
teacher.add_argument("--data_path", required=True)
teacher.add_argument("--tokenizer_path", required=True)
teacher.add_argument("--out_json", required=True)
teacher.add_argument("--data_mode", choices=["wrap", "tokenized_hf"], default="wrap")
teacher.add_argument("--text_column", default=None)
teacher.add_argument("--openwebtext_split", default="all")
teacher.add_argument("--wrap_mode", default="stream")
teacher.add_argument("--tokenized_pad_token", default="pad")
teacher.add_argument("--max_len", type=int, default=1024)
teacher.add_argument("--batch_size", type=int, default=8)
teacher.add_argument("--max_records", type=int, default=20000)
teacher.add_argument("--t_values", default="0.0,0.0078125,0.03125,0.125,0.5,1.0")
teacher.add_argument("--force_mask_ratio", type=float, default=None)
teacher.add_argument("--seed", type=int, default=20260525)
teacher.add_argument("--eps", type=float, default=1e-8)
teacher.add_argument("--cpu", action="store_true")
teacher.set_defaults(func=command_teacher)
trace = sub.add_parser("trace")
trace.add_argument("--name", required=True)
trace.add_argument("--checkpoint", required=True)
trace.add_argument("--tokenizer_path", required=True)
trace.add_argument("--out_jsonl", required=True)
trace.add_argument("--final_out", default="")
trace.add_argument("--max_len", type=int, default=1024)
trace.add_argument("--batch_size", type=int, default=2)
trace.add_argument("--steps", type=int, default=128)
trace.add_argument("--trace_steps", default="1,2,4,8,16,32,64,96,128")
trace.add_argument("--concentration_min", type=float, default=30522)
trace.add_argument("--concentration_max", type=float, default=61044)
trace.add_argument("--endpoint_temp", type=float, default=1.45)
trace.add_argument("--endpoint_top_p", type=float, default=0.95)
trace.add_argument("--gumbel_tau_start", type=float, default=1.0)
trace.add_argument("--gumbel_tau_end", type=float, default=0.2)
trace.add_argument("--seed", type=int, default=20260525)
trace.add_argument("--eps", type=float, default=1e-8)
trace.add_argument("--cpu", action="store_true")
trace.set_defaults(func=command_trace)
args = ap.parse_args()
args.func(args)
if __name__ == "__main__":
main()