| """DaisyChain Task that trains the REAL SpikeWhale model on streamed FineWeb-Edu. |
| |
| No reimplementation: it imports your actual `model_v2.SpikeWhaleLM` + `config. |
| SpikeWhaleConfig` + `spike_tokenizer.SpikeTokenizer`, builds a size chosen by the |
| sliders (env vars), and streams FineWeb-Edu for data. DaisyChain then distributes |
| it across machines; matmuls can be routed through the verified/GUDA units. |
| |
| Point DaisyChain at it: |
| DAISY_TASK=daisychain.spikewhale_task:SpikeWhaleTask daisychain-train |
| |
| Sliders (env): |
| DAISY_SW_PATH folder holding model_v2.py/config.py/tokenizer.json |
| DAISY_SW_HIDDEN hidden_size (default 256) |
| DAISY_SW_LAYERS num_hidden_layers (default 4) |
| DAISY_SW_HEADS num_attention_heads (default 4) |
| DAISY_SW_EXPERTS n_routed_experts (default 4) |
| DAISY_SW_SEQLEN sequence length (default 128) |
| DAISY_SW_MTP MTP heads (0=off) (default 0) |
| DAISY_SW_DATASET HF dataset (default HuggingFaceFW/fineweb-edu) |
| DAISY_SW_SUBSET dataset config name (default sample-10BT) |
| """ |
| import os |
| import sys |
|
|
| import torch |
|
|
| _DEF_PATH = os.environ.get("DAISY_SW_PATH", r"C:\Users\quaz\Desktop\Spikewhale") |
|
|
|
|
| def _import_spikewhale(): |
| if _DEF_PATH not in sys.path: |
| sys.path.insert(0, _DEF_PATH) |
| from config import SpikeWhaleConfig |
| from model_v2 import SpikeWhaleLM |
| from spike_tokenizer import SpikeTokenizer |
| return SpikeWhaleConfig, SpikeWhaleLM, SpikeTokenizer |
|
|
|
|
| def _envi(k, d): |
| return int(os.environ.get(k, d)) |
|
|
|
|
| def build_config(): |
| SpikeWhaleConfig, _, _ = _import_spikewhale() |
| hidden = _envi("DAISY_SW_HIDDEN", 256) |
| return SpikeWhaleConfig( |
| hidden_size=hidden, |
| num_hidden_layers=_envi("DAISY_SW_LAYERS", 4), |
| num_attention_heads=_envi("DAISY_SW_HEADS", 4), |
| head_dim=32, qk_rope_head_dim=16, |
| q_lora_rank=max(32, hidden // 4), o_lora_rank=max(32, hidden // 8), |
| num_key_value_heads=1, |
| max_position_embeddings=max(256, _envi("DAISY_SW_SEQLEN", 128)), |
| moe_intermediate_size=hidden, |
| n_routed_experts=_envi("DAISY_SW_EXPERTS", 4), n_shared_experts=1, |
| num_experts_per_tok=min(2, _envi("DAISY_SW_EXPERTS", 4)), |
| num_hash_layers=1, hc_mult=2, |
| num_nextn_predict_layers=_envi("DAISY_SW_MTP", 0), |
| engram_table_size=4096, engram_compress_dim=48, engram_num_heads=2, |
| ) |
|
|
|
|
| class _FineWebStream: |
| """Streams FineWeb-Edu, tokenizes, yields fixed-length token windows.""" |
|
|
| def __init__(self, tokenizer, seqlen, rank=0, world=1): |
| self.tok, self.seqlen = tokenizer, seqlen |
| self.buf = [] |
| ds_path = os.environ.get("DAISY_SW_DATASET", "HuggingFaceFW/fineweb-edu") |
| |
| |
| default_subset = "sample-10BT" if ds_path == "HuggingFaceFW/fineweb-edu" else "" |
| subset = os.environ.get("DAISY_SW_SUBSET", default_subset) |
| self.eos = getattr(tokenizer, "eos_token_id", 1) or 1 |
| try: |
| from datasets import load_dataset |
| ds = load_dataset(ds_path, name=subset or None, split="train", streaming=True) |
| ds = ds.shard(num_shards=world, index=rank) if world > 1 else ds |
| self.it = iter(ds) |
| self.source = f"{ds_path}:{subset or 'default'}" |
| except Exception as e: |
| print(f"[spikewhale] FineWeb stream unavailable ({e}); using local fallback text", flush=True) |
| self.it = None |
| self.source = "local-fallback" |
|
|
| def _more_tokens(self): |
| if self.it is not None: |
| row = next(self.it) |
| text = row.get("text", "") or "" |
| else: |
| text = ("Education is the process of learning and acquiring knowledge. " |
| "Small models can still learn useful patterns from good data. ") |
| ids = self.tok.encode(text, add_special_tokens=False) |
| self.buf.extend(ids + [self.eos]) |
|
|
| def next_window(self): |
| while len(self.buf) < self.seqlen + 1: |
| self._more_tokens() |
| w = self.buf[: self.seqlen + 1] |
| self.buf = self.buf[self.seqlen:] |
| return w |
|
|
|
|
| class SpikeWhaleTask: |
| def __init__(self): |
| SpikeWhaleConfig, SpikeWhaleLM, SpikeTokenizer = _import_spikewhale() |
| self.cfg = build_config() |
| tok_file = os.path.join(_DEF_PATH, "tokenizer.json") |
| self.tok = SpikeTokenizer(vocab_file=tok_file) |
| self.seqlen = _envi("DAISY_SW_SEQLEN", 128) |
| rank = _envi("RANK", 0); world = _envi("WORLD_SIZE", 1) |
| self.stream = _FineWebStream(self.tok, self.seqlen, rank, world) |
| self._SpikeWhaleLM = SpikeWhaleLM |
| n = None |
| print(f"[spikewhale] data source: {self.stream.source}", flush=True) |
|
|
| def build_model(self): |
| torch.manual_seed(0) |
| m = self._SpikeWhaleLM(self.cfg) |
| n = sum(p.numel() for p in m.parameters()) |
| print(f"[spikewhale] model built: {n:,} params " |
| f"(hidden={self.cfg.hidden_size}, layers={self.cfg.num_hidden_layers}, " |
| f"experts={self.cfg.n_routed_experts}, seqlen={self.seqlen})", flush=True) |
| return m |
|
|
| def sample(self, n): |
| rows = [self.stream.next_window()[:self.seqlen] for _ in range(n)] |
| t = torch.tensor(rows, dtype=torch.long) |
| return t, t |
|
|
| def loss(self, model, X, y): |
| return model(input_ids=X, labels=y).loss |
|
|