Download kaggle/full_shell_cmd.py from sage002/angstrom: direct link, hf CLI and curl.
- Browser
- Download file 14.2 kB
-
https://huggingface.co/sage002/angstrom/resolve/main/kaggle/full_shell_cmd.py
- Command line
-
hf download hf://sage002/angstrom/kaggle/full_shell_cmd.py
-
curl -L -o full_shell_cmd.py https://huggingface.co/sage002/angstrom/resolve/main/kaggle/full_shell_cmd.py
14.2 kB
| """AngstromE1-Nano: one-click Kaggle setup β train β download""" | |
| import os, sys, subprocess, time, re, json, math | |
| from pathlib import Path | |
| import torch | |
| import torch.nn as nn | |
| from torch.cuda.amp import GradScaler, autocast | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # CONFIG | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| GIT_REPO = "https://github.com/er-del/angstrom.git" | |
| BRANCH = "main" | |
| MODEL_CONFIG = "medium" | |
| TRAIN_STEPS = 50000 | |
| BATCH_SIZE = 16 | |
| SEQ_LEN = 256 | |
| SAVE_EVERY = 5000 | |
| SSH_PASSWORD = "ChangeMe123!" | |
| KAGGLE_OUT = Path("/kaggle/working/angstrom_output") | |
| LOCAL_OUT = Path("checkpoints") | |
| def log(msg): print(f"\033[0;32m[+]\033[0m {msg}") | |
| def info(msg): print(f" {msg}") | |
| def err(msg): print(f"\033[0;31m[-]\033[0m {msg}") | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # 1. SYSTEM SETUP | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| log("=== 1/7: System setup ===") | |
| log("Checking CUDA + GPUs...") | |
| n_gpus = torch.cuda.device_count() | |
| for i in range(n_gpus): | |
| p = torch.cuda.get_device_properties(i) | |
| info(f"GPU {i}: {p.name} {p.total_memory/1e9:.1f}GB CUDA {torch.version.cuda}") | |
| log(f"{n_gpus} GPU(s) available") | |
| log("Installing system packages...") | |
| os.system("apt-get update -qq > /dev/null 2>&1") | |
| os.system("apt-get install -y -qq openssh-server curl wget git-lfs > /dev/null 2>&1") | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # 2. CLONE REPO | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| log("=== 2/7: Clone repo ===") | |
| if os.path.exists("angstrom"): | |
| log("angstrom/ already exists, pulling latest...") | |
| os.system("cd angstrom && git pull 2>/dev/null") | |
| else: | |
| os.system(f"git clone {GIT_REPO} angstrom 2>&1") | |
| os.system(f"cd angstrom && git checkout {BRANCH} 2>/dev/null") | |
| os.chdir("angstrom") | |
| REPO = Path.cwd() | |
| info(f"Working dir: {REPO}") | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # 3. PYTHON DEPENDENCIES | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| log("=== 3/7: Python dependencies ===") | |
| os.system("pip install -q --upgrade pip 2>/dev/null") | |
| os.system("pip install -q datasets>=2.16.0 safetensors>=0.4.0 tqdm>=4.65.0 accelerate>=0.25.0 2>/dev/null") | |
| os.system("pip install -q -e . 2>/dev/null || true") | |
| try: | |
| from angstrom_nano import AngstromNanoConfig, AngstromNanoForCausalLM | |
| from angstrom_nano.tokenizer import AngstromNanoTokenizer | |
| log("All imports OK") | |
| except Exception as e: | |
| err(f"Import failed: {e}") | |
| sys.exit(1) | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # 4. DOWNLOAD DATA | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| log("=== 4/7: Download training data ===") | |
| data_script = REPO / "download_data.py" | |
| if data_script.exists(): | |
| info("Running download_data.py...") | |
| os.system(f"python {data_script} 2>&1") | |
| data_path = REPO / "data" / "corpus.txt" | |
| if not data_path.exists(): | |
| err("Data download failed") | |
| info("Writing fallback mini-corpus...") | |
| (REPO / "data").mkdir(exist_ok=True) | |
| data_path.write_text("Hello world.\n" * 1000000) | |
| text = data_path.read_text() | |
| info(f"Data: {len(text):,} chars ({len(text)/1e6:.1f}MB)") | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # 5. TRAIN MODEL | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| log("=== 5/7: Train model ===") | |
| CONFIGS = { | |
| "small": { | |
| "vocab_size": 8192, "hidden_size": 256, "intermediate_size": 1024, | |
| "num_hidden_layers": 8, "num_attention_heads": 8, "num_key_value_heads": 2, | |
| "head_dim": 32, "num_local_experts": 4, "num_experts_per_tok": 2, | |
| "max_position_embeddings": 2048, "sliding_window": 512, | |
| "scoring_func": "sigmoid", "use_qk_norm": True, "use_routing_bias": True, | |
| "tie_word_embeddings": True, | |
| }, | |
| "medium": { | |
| "vocab_size": 4096, "hidden_size": 192, "intermediate_size": 768, | |
| "num_hidden_layers": 6, "num_attention_heads": 6, "num_key_value_heads": 3, | |
| "head_dim": 32, "num_local_experts": 4, "num_experts_per_tok": 2, | |
| "max_position_embeddings": 2048, "sliding_window": 512, | |
| "scoring_func": "sigmoid", "use_qk_norm": True, "use_routing_bias": True, | |
| "tie_word_embeddings": True, | |
| }, | |
| "large": { | |
| "vocab_size": 16384, "hidden_size": 512, "intermediate_size": 2048, | |
| "num_hidden_layers": 12, "num_attention_heads": 16, "num_key_value_heads": 4, | |
| "head_dim": 32, "num_local_experts": 8, "num_experts_per_tok": 2, | |
| "max_position_embeddings": 4096, "sliding_window": 1024, | |
| "scoring_func": "sigmoid", "use_qk_norm": True, "use_routing_bias": True, | |
| "tie_word_embeddings": True, | |
| }, | |
| } | |
| config_dict = CONFIGS[MODEL_CONFIG] | |
| info(f"Using config: {MODEL_CONFIG}") | |
| # Tokenizer | |
| tok_path = REPO / "checkpoints/tokenizer.json" | |
| if tok_path.exists(): | |
| tok = AngstromNanoTokenizer.from_bpe_file(str(tok_path)) | |
| config_dict["vocab_size"] = len(tok) | |
| info(f"Loaded tokenizer: {len(tok)} vocab") | |
| else: | |
| info("Training new tokenizer...") | |
| tok_path.parent.mkdir(parents=True, exist_ok=True) | |
| tok = AngstromNanoTokenizer.train_bpe( | |
| [str(data_path)], vocab_size=config_dict["vocab_size"], | |
| out_path=str(tok_path)) | |
| info(f"Trained tokenizer: {len(tok)} vocab") | |
| # Dataset | |
| class TextDataset(torch.utils.data.Dataset): | |
| def __init__(self, token_ids, seq_len): | |
| self.token_ids = token_ids | |
| self.seq_len = seq_len | |
| self.n_samples = len(token_ids) - seq_len - 1 | |
| def __len__(self): return self.n_samples | |
| def __getitem__(self, idx): | |
| return (self.token_ids[idx:idx+self.seq_len], | |
| self.token_ids[idx+1:idx+self.seq_len+1]) | |
| info("Tokenizing data...") | |
| ids = torch.tensor(tok.encode(text, add_bos=True, add_eos=True), dtype=torch.long) | |
| info(f"Tokens: {len(ids):,} ({len(ids)/1e6:.1f}M)") | |
| dataset = TextDataset(ids, SEQ_LEN) | |
| dataloader = torch.utils.data.DataLoader( | |
| dataset, batch_size=BATCH_SIZE, shuffle=True, | |
| num_workers=2, pin_memory=True, drop_last=True) | |
| info(f"Samples: {len(dataset):,}") | |
| # Model | |
| device = torch.device("cuda") | |
| cfg = AngstromNanoConfig(**config_dict) | |
| model: nn.Module = AngstromNanoForCausalLM(cfg) | |
| n_params = sum(p.numel() for p in model.parameters() if p.requires_grad) | |
| info(f"Model: {n_params:,} params ({n_params*4/1e6:.1f}MB FP32)") | |
| model = model.to(device) | |
| if n_gpus > 1: | |
| model = nn.DataParallel(model, device_ids=list(range(n_gpus))) | |
| info(f"DataParallel across {n_gpus} GPUs") | |
| # Optimizer | |
| optimizer = torch.optim.AdamW(model.parameters(), lr=3e-3, weight_decay=0.1, betas=(0.9, 0.95)) | |
| scaler = GradScaler(enabled=True) | |
| WARMUP = 500 | |
| MIN_LR = 3e-4 | |
| GRAD_CLIP = 1.0 | |
| def get_lr(step): | |
| if step < WARMUP: | |
| return 3e-3 * step / max(1, WARMUP) | |
| progress = (step - WARMUP) / max(1, TRAIN_STEPS - WARMUP) | |
| return MIN_LR + 0.5 * (3e-3 - MIN_LR) * (1.0 + math.cos(math.pi * progress)) | |
| log("Starting training loop...") | |
| model.train() | |
| t0 = time.time() | |
| running_loss = 0.0 | |
| running_steps = 0 | |
| LOCAL_OUT.mkdir(parents=True, exist_ok=True) | |
| KAGGLE_OUT.mkdir(parents=True, exist_ok=True) | |
| data_iter = iter(dataloader) | |
| for step in range(1, TRAIN_STEPS + 1): | |
| try: | |
| x, y = next(data_iter) | |
| except StopIteration: | |
| data_iter = iter(dataloader) | |
| x, y = next(data_iter) | |
| x = x.to(device, non_blocking=True) | |
| y = y.to(device, non_blocking=True) | |
| with autocast(enabled=True, dtype=torch.float16): | |
| out = model(x, labels=y, output_router_logits=True) | |
| loss = out["loss"] | |
| aux_loss = out.get("aux_loss", torch.tensor(0.0)) | |
| optimizer.zero_grad() | |
| scaler.scale(loss).backward() | |
| if GRAD_CLIP > 0: | |
| scaler.unscale_(optimizer) | |
| nn.utils.clip_grad_norm_(model.parameters(), GRAD_CLIP) | |
| scaler.step(optimizer) | |
| scaler.update() | |
| lr = get_lr(step) | |
| for pg in optimizer.param_groups: | |
| pg["lr"] = lr | |
| running_loss += loss.item() | |
| running_steps += 1 | |
| if step % 100 == 0 or step == 1: | |
| avg = running_loss / running_steps | |
| ppl = math.exp(min(avg, 20)) | |
| elapsed = time.time() - t0 | |
| tok_s = (BATCH_SIZE * SEQ_LEN * running_steps) / max(1, elapsed) | |
| gpu_mem = torch.cuda.memory_allocated(0) / 1e9 | |
| aux_val = aux_loss.item() if isinstance(aux_loss, torch.Tensor) else 0.0 | |
| print(f" step {step:>6d}/{TRAIN_STEPS} loss={avg:.4f} ppl={ppl:.2f} " | |
| f"aux={aux_val:.6f} lr={lr:.1e} tok/s={tok_s:.0f} " | |
| f"gpu={gpu_mem:.1f}GB {elapsed:.0f}s") | |
| running_loss = 0.0 | |
| running_steps = 0 | |
| if step % SAVE_EVERY == 0: | |
| log(f"Saving checkpoint step {step}...") | |
| sd = model.state_dict() | |
| from safetensors.torch import save_file | |
| fname = f"checkpoint-{step}.safetensors" | |
| save_file({k: v.contiguous().cpu() for k, v in sd.items()}, str(LOCAL_OUT / fname)) | |
| save_file({k: v.contiguous().cpu() for k, v in sd.items()}, str(KAGGLE_OUT / fname)) | |
| (LOCAL_OUT / "config.json").write_text(json.dumps(config_dict, indent=2)) | |
| (KAGGLE_OUT / "config.json").write_text(json.dumps(config_dict, indent=2)) | |
| # ββ Final save ββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| log("Training complete! Saving final model...") | |
| sd = model.state_dict() | |
| from safetensors.torch import save_file | |
| for out_dir in [LOCAL_OUT, KAGGLE_OUT]: | |
| save_file({k: v.contiguous().cpu() for k, v in sd.items()}, str(out_dir / "model_final.safetensors")) | |
| (out_dir / "config.json").write_text(json.dumps(config_dict, indent=2)) | |
| if tok_path.exists(): | |
| import shutil | |
| shutil.copy(tok_path, out_dir / "tokenizer.json") | |
| total_h = (time.time() - t0) / 3600 | |
| info(f"Total time: {total_h:.1f} hours") | |
| info(f"Model saved to: {KAGGLE_OUT}/ β download from Kaggle 'Output' tab!") | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # 6. SSH TUNNEL | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| log("=== 6/7: SSH tunnel ===") | |
| os.system("sed -i '/^PermitRootLogin\\|^PasswordAuthentication\\|^UseDNS/d' /etc/ssh/sshd_config") | |
| with open("/etc/ssh/sshd_config", "a") as f: | |
| f.write("PermitRootLogin yes\nPasswordAuthentication yes\nUseDNS no\n") | |
| os.system(f"echo 'root:{SSH_PASSWORD}' | chpasswd") | |
| os.system("mkdir -p /var/run/sshd && /usr/sbin/sshd 2>/dev/null") | |
| log("Starting bore tunnel...") | |
| os.system("curl -sL https://github.com/ekzhang/bore/releases/download/v0.5.2/bore-v0.5.2-x86_64-unknown-linux-musl.tar.gz | tar xz -C /usr/local/bin/ 2>/dev/null") | |
| port = "" | |
| if os.path.exists("/usr/local/bin/bore"): | |
| bore_proc = subprocess.Popen( | |
| ["bore", "local", "22", "--to", "bore.pub"], | |
| stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True | |
| ) | |
| start = time.time() | |
| while time.time() - start < 30: | |
| assert bore_proc.stdout is not None | |
| line = bore_proc.stdout.readline() | |
| if line: | |
| print(f" {line.rstrip()}") | |
| m = re.search(r"bore\.pub:(\d+)", line) | |
| if m: | |
| port = m.group(1) | |
| break | |
| if port: | |
| print() | |
| print("=" * 60) | |
| print(" SSH TUNNEL READY") | |
| print("=" * 60) | |
| print(f"\n ssh root@bore.pub -p {port} -o StrictHostKeyChecking=no") | |
| print(f" Password: {SSH_PASSWORD}") | |
| print(f"\n Model files at: {KAGGLE_OUT}/") | |
| print(" KEEP THIS CELL RUNNING\n") | |
| print("=" * 60) | |
| try: | |
| while True: time.sleep(10) | |
| except KeyboardInterrupt: | |
| pass | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # 7. SUMMARY | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| log("=== 7/7: Complete ===") | |
| info("Download from Kaggle: 'Output' tab β angstrom_output/") | |
| for f in sorted(KAGGLE_OUT.glob("*")): | |
| info(f" {f.name} ({f.stat().st_size / 1e6:.1f}MB)") | |