| |
| """ |
| Analyze token-level sequence lengths in aes_all.jsonl using the actual tokenizer. |
| Applies the chat template to get real token counts. |
| |
| Outputs statistics and a recommended max_seq_length. |
| """ |
| import json |
| import sys |
| from pathlib import Path |
|
|
| from transformers import AutoTokenizer |
|
|
| WORKSPACE = Path("/workspace/elinnos") |
| INPUT = WORKSPACE / "aes_all.jsonl" |
| TOKENIZER_SRC = WORKSPACE / "Qwen2.5-7B-Instruct" |
| CHAT_TEMPLATE = WORKSPACE / "elinnos-qwen2.5-7b-multi-ip-lora-v4" / "chat_template.jinja" |
|
|
|
|
| def main(): |
| print("Loading tokenizer...") |
| tokenizer = AutoTokenizer.from_pretrained(str(TOKENIZER_SRC), trust_remote_code=True) |
| if tokenizer.pad_token is None: |
| tokenizer.pad_token = tokenizer.eos_token |
|
|
| if CHAT_TEMPLATE.is_file(): |
| tokenizer.chat_template = CHAT_TEMPLATE.read_text() |
| print(f"Chat template loaded from {CHAT_TEMPLATE}") |
|
|
| print(f"Loading {INPUT}...") |
| records = [] |
| with INPUT.open("r", encoding="utf-8") as f: |
| for line in f: |
| line = line.strip() |
| if line: |
| records.append(json.loads(line)) |
|
|
| print(f"Total samples: {len(records)}") |
| print("Tokenizing with chat template (this may take a minute)...") |
|
|
| token_lengths = [] |
| for i, rec in enumerate(records): |
| messages = rec["messages"] |
| text = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=False) |
| tokens = tokenizer(text, return_tensors=None, add_special_tokens=False) |
| token_lengths.append(len(tokens["input_ids"])) |
|
|
| if (i + 1) % 500 == 0: |
| print(f" Processed {i+1}/{len(records)}...") |
|
|
| token_lengths.sort() |
| n = len(token_lengths) |
|
|
| print(f"\n{'='*60}") |
| print(" TOKEN LENGTH STATISTICS") |
| print(f"{'='*60}") |
| print(f" Samples: {n}") |
| print(f" Min: {token_lengths[0]}") |
| print(f" Max: {token_lengths[-1]}") |
| print(f" Mean: {sum(token_lengths)/n:.0f}") |
| print(f" Median: {token_lengths[n//2]}") |
|
|
| for p in [75, 90, 95, 97, 99, 100]: |
| idx = min(int(n * p / 100), n - 1) |
| print(f" {p}th pct: {token_lengths[idx]}") |
|
|
| |
| p95 = token_lengths[min(int(n * 0.95), n - 1)] |
| p99 = token_lengths[min(int(n * 0.99), n - 1)] |
| p100 = token_lengths[-1] |
|
|
| if p95 <= 4096: |
| recommended = 4096 |
| elif p95 <= 8192: |
| recommended = 8192 |
| elif p99 <= 12288: |
| recommended = 12288 |
| else: |
| recommended = min(p100, 16384) |
|
|
| |
| recommended = ((recommended + 511) // 512) * 512 |
|
|
| print(f"\n Recommended max_seq_length: {recommended}") |
| print(f" (covers 95th pct={p95}, 99th pct={p99}, max={p100})") |
|
|
| |
| for sl in [4096, 8192, 12288, 16384]: |
| truncated = sum(1 for t in token_lengths if t > sl) |
| print(f" At seq_len={sl}: {truncated}/{n} ({truncated/n*100:.1f}%) samples truncated") |
|
|
| out_path = WORKSPACE / "aes_training" / "data" / "recommended_seq_length.txt" |
| out_path.parent.mkdir(parents=True, exist_ok=True) |
| out_path.write_text(str(recommended)) |
| print(f"\n Written to: {out_path}") |
|
|
| |
| stats_path = WORKSPACE / "aes_training" / "data" / "seq_length_stats.json" |
| stats = { |
| "total_samples": n, |
| "min": token_lengths[0], |
| "max": token_lengths[-1], |
| "mean": sum(token_lengths) / n, |
| "median": token_lengths[n // 2], |
| "percentiles": { |
| str(p): token_lengths[min(int(n * p / 100), n - 1)] |
| for p in [50, 75, 90, 95, 97, 99, 100] |
| }, |
| "recommended_max_seq_length": recommended, |
| } |
| with stats_path.open("w") as f: |
| json.dump(stats, f, indent=2) |
| print(f" Stats saved to: {stats_path}") |
|
|
|
|
| if __name__ == "__main__": |
| main() |
|
|