From 4d67db1bdb1c03a12d71cb5c9b7d64b4faad2b4c Mon Sep 17 00:00:00 2001 From: maderix Date: Sun, 1 Mar 2026 03:14:39 -0800 Subject: [PATCH] stories110M: 12-layer ANE training with dashboard, 107ms/step MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Scale to full stories110M (109M params, 12 layers) with real TinyStories data - vDSP-vectorized cross-entropy (110ms→14ms), NEON fp16 IO, async dW - TUI dashboard: loss curve, ANE/CPU power, CPU/memory graphs, text generation - Split into modular headers: config, io, mil, cpu_ops --- training/Makefile | 14 +- training/README.md | 69 ++ training/dashboard.gif | Bin 0 -> 238020 bytes training/dashboard.py | 882 ++++++++++++++++++++++ training/stories_config.h | 189 +++++ training/stories_cpu_ops.h | 129 ++++ training/stories_io.h | 134 ++++ training/stories_mil.h | 286 ++++++++ training/tokenize.py | 36 + training/train_large.m | 1427 ++++++++++++++---------------------- 10 files changed, 2279 insertions(+), 887 deletions(-) create mode 100644 training/README.md create mode 100644 training/dashboard.gif create mode 100644 training/dashboard.py create mode 100644 training/stories_config.h create mode 100644 training/stories_cpu_ops.h create mode 100644 training/stories_io.h create mode 100644 training/stories_mil.h create mode 100644 training/tokenize.py diff --git a/training/Makefile b/training/Makefile index 90c2977..226bb39 100644 --- a/training/Makefile +++ b/training/Makefile @@ -3,10 +3,18 @@ CFLAGS = -O2 -Wall -Wno-deprecated-declarations -fobjc-arc FRAMEWORKS = -framework Foundation -framework CoreML -framework IOSurface LDFLAGS = $(FRAMEWORKS) -ldl +HEADERS_LARGE = stories_config.h stories_io.h stories_mil.h stories_cpu_ops.h + train: train.m ane_runtime.h ane_mil_gen.h model.h forward.h backward.h $(CC) $(CFLAGS) -o $@ train.m $(LDFLAGS) -clean: - rm -f train +train_large: train_large.m $(HEADERS_LARGE) + $(CC) $(CFLAGS) -o $@ train_large.m $(LDFLAGS) -framework Accelerate -.PHONY: clean +tokenize: + python3 tokenize.py + +clean: + rm -f train train_large + +.PHONY: clean tokenize diff --git a/training/README.md b/training/README.md new file mode 100644 index 0000000..53edbb9 --- /dev/null +++ b/training/README.md @@ -0,0 +1,69 @@ +# ANE Training — Stories110M on Apple Neural Engine + +Training a 109M-parameter Llama2-architecture transformer (Stories110M) directly on Apple's Neural Engine using private ANE APIs. + +![Dashboard](dashboard.gif) + +## Architecture + +- **Model**: Stories110M — dim=768, hidden=2048, heads=12, layers=12, vocab=32000, seq=256 +- **109.53M params** (84.95M transformer + 24.58M embedding) +- **72 ANE kernels** per compile (60 weight-bearing, 12 weight-free sdpaBwd2) +- **6 kernel types per layer**: fwdAttn, fwdFFN, ffnBwd, sdpaBwd1, sdpaBwd2, qkvBwd + +## Performance + +| Component | Time (ms/step) | +|-----------|---------------| +| ANE eval | 9.6 | +| IO (fp16 conversion) | 4.1 | +| Classifier (cblas) | 9.1 | +| Cross-entropy + residuals | 14.4 | +| RMSNorm | 0.1 | +| **Total** | **107 ms/step** | + +## Files + +| File | Description | +|------|-------------| +| `train_large.m` | Main training loop — 12-layer forward/backward, checkpoint, exec() restart | +| `stories_config.h` | Model config, structs, alloc helpers | +| `stories_io.h` | IOSurface I/O, NEON fp16 conversion, kernel compile/eval | +| `stories_mil.h` | MIL program generators for all 6 ANE kernel types | +| `stories_cpu_ops.h` | vDSP-vectorized RMSNorm, cross-entropy, Adam, embedding ops | +| `dashboard.py` | TUI dashboard — loss curve, power/CPU/memory graphs, text generation | +| `tokenize.py` | Extract pretokenized TinyStories data | +| `Makefile` | Build targets | + +## How it works + +1. **Forward pass**: Each layer runs fwdAttn (QKV + SDPA + Wo) and fwdFFN (W1 + SiLU(W3) + W2) on ANE via MIL-compiled kernels. Final RMSNorm + classifier matmul on CPU (cblas). + +2. **Backward pass**: Reverse layer order. ffnBwd, sdpaBwd1, sdpaBwd2, qkvBwd on ANE. Weight gradients (dW) via async cblas_sgemm on CPU. RMSNorm backward via vDSP. + +3. **Compile budget**: ANE has a ~119 compile limit per process. With 72 kernels per batch, we run 10 accumulation steps then `exec()` restart with checkpoint resume. + +4. **Data**: Real TinyStories text (20M tokens), mmap'd uint16 token IDs, random position sampling per step. + +## Usage + +```bash +# Extract tokenized data +python3 tokenize.py + +# Build and train +make train_large +./train_large # fresh start +./train_large --resume # resume from checkpoint + +# Monitor with dashboard +pip install blessed psutil numpy +python3 dashboard.py --resume # needs sudo for powermetrics +``` + +## Key techniques + +- **NEON vectorized fp16<->fp32**: ARM NEON intrinsics for fast IOSurface data transfer +- **vDSP cross-entropy**: `vDSP_mtrans` + `vvexpf` + `vDSP_sve` — 8x faster than scalar +- **Async weight gradients**: cblas_sgemm dispatched to background queue, overlapped with ANE +- **SDPA causal mask workaround**: ANE hardware ignores attn_mask, so we decompose attention into Q@K^T (ANE conv) + mask+softmax (CPU) + scores@V (ANE conv) diff --git a/training/dashboard.gif b/training/dashboard.gif new file mode 100644 index 0000000000000000000000000000000000000000..120f7d5745d2cfa8debf5b524fc26454c76f3bed GIT binary patch literal 238020 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

oR5AYMGl$rDr)ni^^VXi+Z(pc#@n1y2keNSC37 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

-!1c>yB6Td9$+z>Au%trF}o@@%zDG(&SWck?mx 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'): + try: + return chr(int(s[3:-1], 16)) + except: + return s + return s + return '' + +_tokenizer = None +def get_tokenizer(): + global _tokenizer + if _tokenizer is None: + try: + _tokenizer = Tokenizer(TOKENIZER_PATH) + except Exception as e: + S.logs.append(f'[gen] tokenizer load failed: {e}') + return None + return _tokenizer + + +def load_weights_from_ckpt(path): + try: + with open(path, 'rb') as f: + # CkptHdr: 96 bytes (verified with sizeof) + hdr = f.read(96) + if len(hdr) < 96: + return None + wq_sz = DIM * DIM + wo_sz = DIM * DIM + w1_sz = HIDDEN * DIM + w2_sz = DIM * HIDDEN + w3_sz = HIDDEN * DIM + # Per-layer: weights + adam state (m,v for each) + adam_per_layer = (wq_sz*2 + wq_sz*2 + wq_sz*2 + wo_sz*2 + + w1_sz*2 + w2_sz*2 + w3_sz*2 + DIM*2 + DIM*2) + W = {} + for L in range(NLAYERS): + W[f'Wq{L}'] = np.frombuffer(f.read(wq_sz * 4), dtype=np.float32).reshape(DIM, DIM).copy() + W[f'Wk{L}'] = np.frombuffer(f.read(wq_sz * 4), dtype=np.float32).reshape(DIM, DIM).copy() + W[f'Wv{L}'] = np.frombuffer(f.read(wq_sz * 4), dtype=np.float32).reshape(DIM, DIM).copy() + W[f'Wo{L}'] = np.frombuffer(f.read(wo_sz * 4), dtype=np.float32).reshape(DIM, DIM).copy() + W[f'W1_{L}'] = np.frombuffer(f.read(w1_sz * 4), dtype=np.float32).reshape(HIDDEN, DIM).copy() + W[f'W2_{L}'] = np.frombuffer(f.read(w2_sz * 4), dtype=np.float32).reshape(DIM, HIDDEN).copy() + W[f'W3_{L}'] = np.frombuffer(f.read(w3_sz * 4), dtype=np.float32).reshape(HIDDEN, DIM).copy() + W[f'rms1_{L}'] = np.frombuffer(f.read(DIM * 4), dtype=np.float32).copy() + W[f'rms2_{L}'] = np.frombuffer(f.read(DIM * 4), dtype=np.float32).copy() + # Skip adam state for this layer + f.seek(adam_per_layer * 4, 1) + W['rms_final'] = np.frombuffer(f.read(DIM * 4), dtype=np.float32).copy() + f.seek(DIM * 2 * 4, 1) # skip rms_final adam + W['embed'] = np.frombuffer(f.read(VOCAB * DIM * 4), dtype=np.float32).reshape(VOCAB, DIM).copy() + return W + except Exception as e: + S.logs.append(f'[gen] ckpt load failed: {e}') + return None + + +def rmsnorm(x, w): + ss = np.mean(x * x) + 1e-5 + return x * (1.0 / math.sqrt(ss)) * w + +def softmax(x): + x = x - np.max(x) + e = np.exp(x) + return e / np.sum(e) + +def generate_text(W, tok, max_tokens=64, temperature=0.8): + tokenizer = get_tokenizer() + if tokenizer is None: + return '[no tokenizer]' + + tokens = [1] + text_parts = [] + + # Precompute RoPE frequencies + freqs = np.zeros((SEQ, HD // 2), dtype=np.float32) + for pos in range(SEQ): + for i in range(HD // 2): + freq = 1.0 / (10000.0 ** (2.0 * i / HD)) + freqs[pos, i] = pos * freq + + for step in range(max_tokens): + seq_len = len(tokens) + if seq_len > SEQ: + break + + x = W['embed'][tokens[-1]].copy() + + for L in range(NLAYERS): + # RMSNorm + QKV + xn = rmsnorm(x, W[f'rms1_{L}']) + q = W[f'Wq{L}'] @ xn + k = W[f'Wk{L}'] @ xn + v = W[f'Wv{L}'] @ xn + + # RoPE + pos = seq_len - 1 + for h in range(HEADS): + for i in range(HD // 2): + freq = freqs[pos, i] + cos_v, sin_v = math.cos(freq), math.sin(freq) + qi, qi1 = q[h * HD + 2 * i], q[h * HD + 2 * i + 1] + q[h * HD + 2 * i] = qi * cos_v - qi1 * sin_v + q[h * HD + 2 * i + 1] = qi * sin_v + qi1 * cos_v + ki, ki1 = k[h * HD + 2 * i], k[h * HD + 2 * i + 1] + k[h * HD + 2 * i] = ki * cos_v - ki1 * sin_v + k[h * HD + 2 * i + 1] = ki * sin_v + ki1 * cos_v + + # Attention (single token) + o = np.zeros(DIM, dtype=np.float32) + for h in range(HEADS): + qh = q[h * HD:(h + 1) * HD] + kh = k[h * HD:(h + 1) * HD] + vh = v[h * HD:(h + 1) * HD] + score = np.dot(qh, kh) / math.sqrt(HD) + o[h * HD:(h + 1) * HD] = vh + + # Residual + output projection + x2 = x + W[f'Wo{L}'] @ o + + # FFN + x2n = rmsnorm(x2, W[f'rms2_{L}']) + h1 = W[f'W1_{L}'] @ x2n + h3 = W[f'W3_{L}'] @ x2n + # SiLU + h1 = h1 * (1.0 / (1.0 + np.exp(-h1))) * h3 + ffn_out = W[f'W2_{L}'] @ h1 + + x = x2 + ffn_out + + x = rmsnorm(x, W['rms_final']) + + # Logits + logits = W['embed'] @ x + + if temperature < 0.01: + next_tok = int(np.argmax(logits)) + else: + logits = logits / temperature + probs = softmax(logits) + next_tok = int(np.random.choice(VOCAB, p=probs)) + + if next_tok == 2: + break + tokens.append(next_tok) + piece = tokenizer.decode(next_tok) + text_parts.append(piece) + + return ''.join(text_parts) + + +def generation_thread(): + last_gen_step = -1 + while True: + time.sleep(5) + if S.step <= last_gen_step + 99: + continue + if not os.path.exists(CKPT_PATH): + continue + with S.gen_lock: + S.gen_status = 'generating' + S.gen_step = S.step + try: + W = load_weights_from_ckpt(CKPT_PATH) + if W is None: + with S.gen_lock: + S.gen_status = 'idle' + continue + text = generate_text(W, get_tokenizer(), max_tokens=64, temperature=0.8) + with S.gen_lock: + S.gen_text = text + S.gen_step = S.step + S.gen_status = 'done' + S.step # just to reference + except Exception as e: + with S.gen_lock: + S.gen_text = f'[error: {e}]' + S.gen_status = 'done' + last_gen_step = S.step + + +def sysmetrics_thread(): + while True: + time.sleep(1) + if not HAS_PSUTIL: + continue + now = time.monotonic() + S.cpu_pct_history.append(psutil.cpu_percent(interval=None)) + mem = psutil.virtual_memory() + S.mem_mb_history.append(mem.used / (1024 * 1024)) + pid = S.train_pid + if pid: + try: + p = psutil.Process(pid) + S.proc_mem_mb_history.append(p.memory_info().rss / (1024 * 1024)) + except (psutil.NoSuchProcess, psutil.AccessDenied): + pass + + +RE_CONFIG = re.compile(r'dim=(\d+) hidden=(\d+) heads=(\d+) seq=(\d+) vocab=(\d+) layers=(\d+)') +RE_PARAMS = re.compile(r'Params: ([\d.]+)M \(transformer ([\d.]+)M \+ embed ([\d.]+)M\)') +RE_KERNELS = re.compile(r'Kernels: (\d+).*?(\d+) weight-bearing') +RE_ACCUM = re.compile(r'Accum (\d+).*LR=([\d.e+-]+)') +RE_STEP = re.compile(r'step\s+(\d+)\s+loss=([\d.]+)') +RE_BATCH = re.compile(r'\[batch (\d+): compile=([\d.]+)ms train=([\d.]+)ms \(([\d.]+)ms/step\) compiles=(\d+)\]') +RE_TIMING = re.compile(r'ane=([\d.]+) io=([\d.]+) cls=([\d.]+) elem=([\d.]+) rms=([\d.]+) cblas_wait=([\d.]+)') +RE_RESTART = re.compile(r'\[exec\(\) restart step (\d+)') +RE_RESUME = re.compile(r'\[RESUMED step (\d+), loss=([\d.]+)\]') +RE_FLOPS = re.compile(r'FLOPs/step: fwd=([\d.]+)M bwd_dx=([\d.]+)M bwd_dW=([\d.]+)M sdpa_bwd=([\d.]+)M total=([\d.]+)M') +RE_ANE_FLOPS = re.compile(r'ANE FLOPs/step: ([\d.]+)M') +RE_ANE_TFLOPS = re.compile(r'ANE TFLOPS:\s+([\d.]+)') +RE_ANE_UTIL = re.compile(r'ANE utilization:\s+([\d.]+)%') +RE_EFFICIENCY = re.compile(r'(Total steps|Wall time|Compile time|Train time|Avg compile|Avg train|ANE TFLOPS|Total TFLOPS|ANE utilization):?\s+(.+)') +RE_ANE_POWER = re.compile(r'ANE Power:\s+([\d.]+)\s*mW') +RE_CPU_POWER = re.compile(r'CPU Power:\s+([\d.]+)\s*mW') +RE_GPU_POWER = re.compile(r'GPU Power:\s+([\d.]+)\s*mW') + +def parse_line(line): + S.logs.append(line) + m = RE_CONFIG.search(line) + if m: + S.model_config = dict(zip(['dim', 'hidden', 'heads', 'seq', 'vocab', 'layers'], map(int, m.groups()))) + return + m = RE_PARAMS.search(line) + if m: + S.params = {'total': float(m[1]), 'transformer': float(m[2]), 'embed': float(m[3])} + return + m = RE_KERNELS.search(line) + if m: + S.kernels = {'total': int(m[1]), 'weight_bearing': int(m[2])} + return + m = RE_ACCUM.search(line) + if m: + S.training = {'accum': int(m[1]), 'lr': m[2]} + return + m = RE_FLOPS.search(line) + if m: + S.flops.update(fwd=float(m[1]), bwd_dx=float(m[2]), bwd_dw=float(m[3]), + sdpa_bwd=float(m[4]), total=float(m[5])) + return + m = RE_ANE_FLOPS.search(line) + if m: + S.flops['ane'] = float(m[1]) + return + m = RE_STEP.search(line) + if m: + S.step, S.loss = int(m[1]), float(m[2]) + S.loss_history.append((S.step, S.loss)) + S.best_loss = min(S.best_loss, S.loss) + return + m = RE_BATCH.search(line) + if m: + S.batch_num = int(m[1]) + compile_ms, train_ms = float(m[2]), float(m[3]) + S.ms_per_step = float(m[4]) + S.compiles = int(m[5]) + S.compile_pct = 100 * compile_ms / (compile_ms + train_ms) if compile_ms + train_ms > 0 else 0 + return + m = RE_TIMING.search(line) + if m: + S.component_timing = dict(zip(['ane', 'io', 'cls', 'elem', 'rms', 'cblas_wait'], map(float, m.groups()))) + return + m = RE_ANE_TFLOPS.search(line) + if m: + S.flops['ane_tflops'] = float(m[1]) + return + m = RE_ANE_UTIL.search(line) + if m: + S.flops['ane_util'] = float(m[1]) + return + m = RE_EFFICIENCY.search(line) + if m: + S.efficiency[m[1].strip()] = m[2].strip() + return + + +def parse_powermetrics_text(text): + now = time.monotonic() + m = RE_ANE_POWER.search(text) + if m: + S.power['ane'] = float(m[1]) / 1000.0 + S.power_history_ane.append((now, S.power['ane'])) + m = RE_CPU_POWER.search(text) + if m: + S.power['cpu'] = float(m[1]) / 1000.0 + S.power_history_cpu.append((now, S.power['cpu'])) + m = RE_GPU_POWER.search(text) + if m: + S.power['gpu'] = float(m[1]) / 1000.0 + + +BRAILLE_BASE = 0x2800 + +BRAILLE_MAP = [ + [1, 8], + [2, 16], + [4, 32], + [64, 128], +] + +def braille_chart(values, width, height, label_fmt='{:.1f}', y_range=None): + if not values or width < 8 or height < 2: + return ['(no data)'] * max(1, height) + chart_w = width - 6 + if chart_w < 2: + return ['(no data)'] * max(1, height) + points_x = chart_w * 2 + points_y = height * 4 + data = values[-points_x:] if len(values) > points_x else values + lo, hi = min(data), max(data) + if y_range: + lo, hi = y_range + if hi - lo < 0.001: + lo, hi = lo - 0.5, hi + 0.5 + margin = (hi - lo) * 0.05 + lo -= margin + hi += margin + + grid = [[0] * chart_w for _ in range(height)] + + def plot(px, py): + px = max(0, min(points_x - 1, px)) + py = max(0, min(points_y - 1, py)) + grid[py // 4][px // 2] |= BRAILLE_MAP[py % 4][px % 2] + + def val_to_y(v): + return int((1 - (v - lo) / (hi - lo)) * (points_y - 1)) + + for i in range(len(data)): + if i >= points_x: + break + y0 = val_to_y(data[i]) + plot(i, y0) + if i > 0: + y_prev = val_to_y(data[i - 1]) + y_lo, y_hi = min(y_prev, y0), max(y_prev, y0) + for yy in range(y_lo, y_hi + 1): + if y_hi != y_lo: + t = (yy - y_prev) / (y0 - y_prev) + xx = int(i - 1 + t) + else: + xx = i + plot(xx, yy) + + lines = [] + for r in range(height): + if r == 0: + label = label_fmt.format(hi)[:5].rjust(5) + elif r == height - 1: + label = label_fmt.format(lo)[:5].rjust(5) + elif r == height // 2: + label = label_fmt.format((hi + lo) / 2)[:5].rjust(5) + else: + label = ' ' + row_str = ''.join(chr(BRAILLE_BASE | grid[r][c]) for c in range(chart_w)) + lines.append(f'{label}\u2502{row_str}') + + lines.append(' \u2514' + '\u2500' * chart_w) + return lines + + +def draw(term): + w, h = term.width, term.height + if w < 40 or h < 15: + print(term.home + term.clear + 'Terminal too small', end='', flush=True) + return + + buf = [] + + def put(y, x, text, style=''): + if 0 <= y < h and x < w: + text = text[:w - x] + if style: + buf.append(term.move(y, x) + style + text + term.normal) + return + buf.append(term.move(y, x) + text) + + buf.append(term.home + term.clear) + + mid_x = w // 2 + right_w = w - mid_x - 1 + left_w = mid_x - 1 + + row = 0 + + # Model Config header + hdr = '\u2500 Model Config ' + put(row, 0, '\u250c' + hdr + '\u2500' * max(0, w - len(hdr) - 2) + '\u2510', term.cyan) + row += 1 + + cfg = S.model_config + if cfg: + line1 = f"stories110M dim={cfg.get('dim', '')} hidden={cfg.get('hidden', '')} heads={cfg.get('heads', '')} seq={cfg.get('seq', '')} layers={cfg.get('layers', '')}" + put(row, 0, '\u2502', term.cyan) + put(row, 2, line1) + put(row, w - 1, '\u2502', term.cyan) + row += 1 + p, k, t = S.params, S.kernels, S.training + line2 = f"{p.get('total', '?')}M params ({p.get('transformer', '?')}M xfmr + {p.get('embed', '?')}M embed)" + put(row, 0, '\u2502', term.cyan) + put(row, 2, line2) + put(row, w - 1, '\u2502', term.cyan) + row += 1 + line3 = f"{k.get('total', '?')} kernels ({k.get('weight_bearing', '?')} wt-bearing) | Accum {t.get('accum', '?')} | Adam LR={t.get('lr', '?')}" + put(row, 0, '\u2502', term.cyan) + put(row, 2, line3) + put(row, w - 1, '\u2502', term.cyan) + row += 1 + else: + put(row, 0, '\u2502', term.cyan) + put(row, 2, 'Waiting for model config...') + put(row, w - 1, '\u2502', term.cyan) + row += 1 + + remaining = h - row - 1 + # Allocate: loss curve ~40%, logs ~30%, power/cpu/mem/gen share rest + power_h = max(3, remaining // 8) + gen_h = max(2, remaining // 10) + extra_panels = power_h + power_h + gen_h + 6 # power + cpu/mem + gen + dividers + log_h_min = max(5, remaining // 5) + curve_h = max(5, remaining - extra_panels - log_h_min) + + # Loss Curve + Training Stats divider + put(row, 0, '\u251c\u2500 Loss Curve ' + '\u2500' * max(0, left_w - 13) + '\u252c\u2500 Training Stats ' + '\u2500' * max(0, right_w - 17) + '\u2524', term.cyan) + row += 1 + + # Loss curve + loss_vals = [l for _, l in S.loss_history] + curve_lines = braille_chart(loss_vals, left_w - 1, curve_h) + for i, cl in enumerate(curve_lines): + put(row + i, 0, '\u2502', term.cyan) + put(row + i, 1, cl, term.green) + put(row + i, mid_x, '\u2502', term.cyan) + put(row + i, w - 1, '\u2502', term.cyan) + + # Training stats (right panel) + sr = row + step_str = f'{S.step}' + (f'/{S.total_steps}' if S.total_steps and S.total_steps < 999999 else '') + put(sr, mid_x + 1, f' Step: {step_str} Loss: {S.loss:.4f}' if S.loss else ' Step: --', term.yellow) + sr += 1 + put(sr, mid_x + 1, f' Best: {S.best_loss:.4f} ms/step: {S.ms_per_step:.1f}' if S.best_loss < float('inf') else ' Best: --') + sr += 1 + ane_tflops = S.flops.get('ane_tflops', 0) + ane_util = S.flops.get('ane_util', 0) + if ane_tflops: + put(sr, mid_x + 1, f' ANE: {ane_tflops:.2f}T Compile: {S.compile_pct:.0f}% Util: {ane_util:.1f}%') + else: + put(sr, mid_x + 1, f' Compile: {S.compile_pct:.0f}%') + sr += 1 + ct = S.component_timing + if ct: + put(sr, mid_x + 1, f' ane={ct.get("ane", 0):.1f} io={ct.get("io", 0):.1f} cls={ct.get("cls", 0):.1f} elem={ct.get("elem", 0):.1f}') + sr += 1 + put(sr, mid_x + 1, f' rms={ct.get("rms", 0):.1f} cblas_wait={ct.get("cblas_wait", 0):.1f} ms/step') + sr += 1 + pw = S.power + if any(pw.values()): + put(sr, mid_x + 1, '\u2500 Power ' + '\u2500' * max(0, right_w - 9), term.cyan) + sr += 1 + put(sr, mid_x + 1, f' ANE: {pw["ane"]:.1f}W CPU: {pw["cpu"]:.1f}W GPU: {pw["gpu"]:.1f}W', term.magenta) + sr += 1 + if S.batch_num: + put(sr, mid_x + 1, f' Batch: {S.batch_num} Compiles: {S.compiles}') + sr += 1 + + # Fill vertical borders between loss curve and stats + top_end = row + len(curve_lines) + for r in range(row, max(top_end, sr)): + if r >= top_end: + put(r, 0, '\u2502', term.cyan) + if r >= sr: + put(r, mid_x, '\u2502', term.cyan) + put(r, w - 1, '\u2502', term.cyan) + row = max(top_end, sr) + + # Power charts + has_power = len(S.power_history_ane) > 1 or len(S.power_history_cpu) > 1 + if has_power: + put(row, 0, '\u251c\u2500 ANE Power (W) ' + '\u2500' * max(0, left_w - 16) + '\u252c\u2500 CPU Power (W) ' + '\u2500' * max(0, right_w - 17) + '\u2524', term.cyan) + row += 1 + ane_vals = [v for _, v in S.power_history_ane] + cpu_vals = [v for _, v in S.power_history_cpu] + ane_lines = braille_chart(ane_vals, left_w - 1, power_h, label_fmt='{:.1f}') + cpu_lines = braille_chart(cpu_vals, right_w - 1, power_h, label_fmt='{:.1f}') + max_lines = max(len(ane_lines), len(cpu_lines)) + while len(ane_lines) < max_lines: + ane_lines.append(' ' * (left_w - 1)) + while len(cpu_lines) < max_lines: + cpu_lines.append(' ' * (right_w - 1)) + for i in range(max_lines): + put(row + i, 0, '\u2502', term.cyan) + put(row + i, 1, ane_lines[i], term.red) + put(row + i, mid_x, '\u2502', term.cyan) + put(row + i, mid_x + 1, cpu_lines[i], term.blue) + put(row + i, w - 1, '\u2502', term.cyan) + row += max_lines + + # CPU / Memory charts + has_sysmetrics = len(S.cpu_pct_history) > 0 + if has_sysmetrics: + put(row, 0, '\u251c\u2500 CPU % ' + '\u2500' * max(0, left_w - 8) + '\u252c\u2500 Memory (MB) ' + '\u2500' * max(0, right_w - 15) + '\u2524', term.cyan) + row += 1 + cpu_vals = list(S.cpu_pct_history) + mem_vals = list(S.proc_mem_mb_history) if S.proc_mem_mb_history else list(S.mem_mb_history) + mem_label = 'proc' if S.proc_mem_mb_history else 'sys' + cpu_lines = braille_chart(cpu_vals, left_w - 1, power_h, label_fmt='{:.0f}', y_range=(0, 100)) + mem_lines = braille_chart(mem_vals, right_w - 1, power_h, label_fmt='{:.0f}') + max_lines = max(len(cpu_lines), len(mem_lines)) + while len(cpu_lines) < max_lines: + cpu_lines.append(' ' * (left_w - 1)) + while len(mem_lines) < max_lines: + mem_lines.append(' ' * (right_w - 1)) + for i in range(max_lines): + put(row + i, 0, '\u2502', term.cyan) + put(row + i, 1, cpu_lines[i], term.yellow) + put(row + i, mid_x, '\u2502', term.cyan) + put(row + i, mid_x + 1, mem_lines[i], term.magenta) + put(row + i, w - 1, '\u2502', term.cyan) + row += max_lines + + # Generated text + with S.gen_lock: + gen_text = S.gen_text + gen_step = S.gen_step + gen_status = S.gen_status + if gen_text or gen_status == 'generating': + status_tag = ' (generating...)' if gen_status == 'generating' else f' (step {gen_step})' + put(row, 0, '\u251c\u2500 Generated Text' + status_tag + ' ' + '\u2500' * max(0, w - 20 - len(status_tag)) + '\u2524', term.cyan) + row += 1 + if gen_text: + line_w = w - 3 + text = gen_text.replace('\n', ' ') + wrapped = [text[i:i + line_w] for i in range(0, len(text), line_w)] + for i, tl in enumerate(wrapped[:gen_h]): + put(row, 0, '\u2502', term.cyan) + put(row, 2, tl, term.white) + put(row, w - 1, '\u2502', term.cyan) + row += 1 + else: + put(row, 0, '\u2502', term.cyan) + put(row, 2, '...') + put(row, w - 1, '\u2502', term.cyan) + row += 1 + + # Logs + log_h = h - row - 1 + scroll_hint = ' (scroll) ' if not S.auto_scroll else ' ' + put(row, 0, '\u251c\u2500 Logs' + scroll_hint + '\u2500' * max(0, w - 8 - len(scroll_hint)) + '\u2524', term.cyan) + row += 1 + + logs = list(S.logs) + if log_h > 0 and logs: + if S.auto_scroll: + start = max(0, len(logs) - log_h) + else: + start = max(0, min(S.log_scroll, len(logs) - log_h)) + visible = logs[start:start + log_h] + for i, line in enumerate(visible): + put(row + i, 0, '\u2502', term.cyan) + if RE_STEP.search(line): + put(row + i, 1, line[:w - 2], term.yellow) + elif line.strip().startswith('[batch'): + put(row + i, 1, line[:w - 2], term.blue) + elif 'FAIL' in line or 'error' in line.lower(): + put(row + i, 1, line[:w - 2], term.red) + else: + put(row + i, 1, line[:w - 2]) + put(row + i, w - 1, '\u2502', term.cyan) + for i in range(len(visible), log_h): + put(row + i, 0, '\u2502', term.cyan) + put(row + i, w - 1, '\u2502', term.cyan) + + # Bottom border + put(h - 1, 0, '\u2514' + '\u2500' * (w - 2) + '\u2518', term.cyan) + + sys.stdout.write(''.join(buf)) + sys.stdout.flush() + + +def set_nonblock(fd): + fl = fcntl.fcntl(fd, fcntl.F_GETFL) + fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK) + +def spawn_training(resume=False, steps=10000): + cmd = 'make train_large 2>&1 && ./train_large' + if resume: + cmd += ' --resume' + cmd += f' --steps {steps}' + proc = subprocess.Popen( + ['bash', '-c', cmd], + stdout=subprocess.PIPE, stderr=subprocess.STDOUT, + cwd=os.path.dirname(os.path.abspath(__file__)) or '.') + set_nonblock(proc.stdout.fileno()) + return proc + +def spawn_powermetrics(): + try: + proc = subprocess.Popen( + ['sudo', 'powermetrics', '--samplers', 'cpu_power,gpu_power,ane_power', '-i', '1000'], + stdout=subprocess.PIPE, stderr=subprocess.DEVNULL) + set_nonblock(proc.stdout.fileno()) + return proc + except (FileNotFoundError, PermissionError): + return None + +def main(): + parser = argparse.ArgumentParser(description='ANE Training Dashboard (stories110M)') + parser.add_argument('--resume', action='store_true', help='Resume from checkpoint') + parser.add_argument('--infinite', action='store_true', help='Train indefinitely') + parser.add_argument('--no-powermetrics', action='store_true') + parser.add_argument('--no-generate', action='store_true', help='Disable text generation') + parser.add_argument('--steps', type=int, default=10000, help='Total steps (default: 10000)') + args = parser.parse_args() + + if args.infinite: + args.steps = 999999999 + S.total_steps = args.steps + + term = Terminal() + procs = [] + + train_proc = spawn_training(resume=args.resume, steps=args.steps) + S.train_pid = train_proc.pid + procs.append(train_proc) + + if HAS_PSUTIL: + psutil.cpu_percent(interval=None) # prime the counter + sys_t = threading.Thread(target=sysmetrics_thread, daemon=True) + sys_t.start() + + pm_proc = None + if not args.no_powermetrics: + pm_proc = spawn_powermetrics() + if pm_proc: + procs.append(pm_proc) + + if not args.no_generate: + gen_t = threading.Thread(target=generation_thread, daemon=True) + gen_t.start() + + pm_buf = '' + train_buf = '' + + def cleanup(): + for p in procs: + try: + p.terminate() + except Exception: + pass + + signal.signal(signal.SIGINT, lambda *a: cleanup()) + signal.signal(signal.SIGTERM, lambda *a: cleanup()) + + resized = [False] + def on_resize(*a): + resized[0] = True + + signal.signal(signal.SIGWINCH, on_resize) + + with term.fullscreen(), term.cbreak(), term.hidden_cursor(): + draw(term) + last_draw = time.monotonic() + + while True: + fds = [] + fd_map = {} + if train_proc and train_proc.stdout: + fd = train_proc.stdout.fileno() + fds.append(fd) + fd_map[fd] = 'train' + if pm_proc and pm_proc.stdout: + fd = pm_proc.stdout.fileno() + fds.append(fd) + fd_map[fd] = 'pm' + fds.append(sys.stdin.fileno()) + fd_map[sys.stdin.fileno()] = 'stdin' + + try: + readable, _, _ = select.select(fds, [], [], 0.25) + except (ValueError, OSError): + continue + + need_draw = resized[0] + resized[0] = False + + train_finished = False + + for fd in readable: + kind = fd_map.get(fd) + if kind == 'train': + try: + data = os.read(fd, 65536) + except BlockingIOError: + continue + except (OSError, ValueError): + data = b'' + if not data: + if train_proc.poll() is not None: + try: + rest = train_proc.stdout.read() + if rest: + for line in rest.decode('utf-8', errors='replace').split('\n'): + if line: + parse_line(line) + except Exception: + pass + S.logs.append('[dashboard] Training finished. Press q to exit.') + train_finished = True + continue + train_buf += data.decode('utf-8', errors='replace') + while '\n' in train_buf: + line, train_buf = train_buf.split('\n', 1) + parse_line(line) + need_draw = True + + elif kind == 'pm': + try: + data = os.read(fd, 65536).decode('utf-8', errors='replace') + except BlockingIOError: + continue + except (OSError, ValueError): + data = '' + if not data: + continue + pm_buf += data + while '\n\n' in pm_buf or '*** ' in pm_buf: + end = pm_buf.find('\n*** ', 1) + if end < 0: + end = pm_buf.find('\n\n', 1) + if end < 0: + break + chunk = pm_buf[:end] + pm_buf = pm_buf[end:] + parse_powermetrics_text(chunk) + if len(pm_buf) > 16384: + pm_buf = pm_buf[-8192:] + need_draw = True + + elif kind == 'stdin': + key = term.inkey(timeout=0) + if not key: + continue + if key == 'q': + cleanup() + return + elif key.name == 'KEY_UP': + S.auto_scroll = False + S.log_scroll = max(0, S.log_scroll - 1) + need_draw = True + elif key.name == 'KEY_DOWN': + S.log_scroll += 1 + need_draw = True + elif key == 'p': + S.auto_scroll = not S.auto_scroll + if S.auto_scroll: + S.log_scroll = max(0, len(S.logs) - 10) + need_draw = True + elif key == 'r': + if train_proc: + train_proc.terminate() + train_proc.wait() + train_proc = spawn_training(resume=True, steps=args.steps) + S.train_pid = train_proc.pid + procs = [p for p in procs if p.poll() is None] + procs.append(train_proc) + S.logs.append('[dashboard] Restarted with --resume') + need_draw = True + elif key == 'g': + with S.gen_lock: + S.gen_status = 'generating' + S.gen_step = S.step + def force_gen(): + try: + W = load_weights_from_ckpt(CKPT_PATH) + if W: + text = generate_text(W, get_tokenizer(), max_tokens=64, temperature=0.8) + with S.gen_lock: + S.gen_text = text + S.gen_step = S.step + S.gen_status = 'done' + except Exception as e: + with S.gen_lock: + S.gen_text = f'[error: {e}]' + S.gen_status = 'done' + threading.Thread(target=force_gen, daemon=True).start() + need_draw = True + + now = time.monotonic() + if not need_draw and now - last_draw > 1.0: + need_draw = True + if need_draw and now - last_draw > 0.066: + draw(term) + last_draw = now + + if train_finished: + draw(term) + while True: + key = term.inkey(timeout=1) + if key == 'q': + cleanup() + return + +if __name__ == '__main__': + main() diff --git a/training/stories_config.h b/training/stories_config.h new file mode 100644 index 0000000..f967974 --- /dev/null +++ b/training/stories_config.h @@ -0,0 +1,189 @@ +// stories_config.h — Stories110M model config and structures +#pragma once +#import +#import +#import +#import +#import +#import +#import +#include +#include +#include +#include +#include +#include + +// Stories110M config +#define DIM 768 +#define HIDDEN 2048 +#define HEADS 12 +#define HD (DIM/HEADS) +#define SEQ 256 +#define NLAYERS 12 +#define VOCAB 32000 +#define ACCUM_STEPS 10 +#define MAX_COMPILES 100 + +// Per compile: 5 weight-bearing kernels per layer + 1 classifier = 5*12+1 = 61 +// Plus 1 static (sdpaBwd2 per layer, no weights) = 12 more but those are weight-free +// Actually sdpaBwd2 has no weights, compile once per layer +// Weight-bearing: fwdAttn(1) + fwdFFN(1) + ffnBwd(1) + sdpaBwd1(1) + qkvBwd(1) = 5 per layer +// 5 * 12 = 60 weight-bearing compiles per batch +// With MAX_COMPILES=100, we get 1 batch of ACCUM_STEPS before restart +#define KERNELS_PER_LAYER 5 +#define TOTAL_WEIGHT_KERNELS (KERNELS_PER_LAYER * NLAYERS) + +// Attention score channels for SDPA backward +#define SCORE_CH (HEADS*SEQ) + +// Weight sizes per layer +#define WQ_SZ (DIM*DIM) +#define WO_SZ (DIM*DIM) +#define W1_SZ (HIDDEN*DIM) +#define W2_SZ (DIM*HIDDEN) +#define W3_SZ (HIDDEN*DIM) +#define LAYER_PARAMS (4*WQ_SZ + W1_SZ + W2_SZ + W3_SZ + 2*DIM) +#define TOTAL_PARAMS (NLAYERS * LAYER_PARAMS + DIM + VOCAB*DIM) // +rms_final+embed + +// Per-layer weight and optimizer state +typedef struct { + float *Wq, *Wk, *Wv, *Wo; + float *W1, *W2, *W3; + float *rms_att, *rms_ffn; +} LayerWeights; + +typedef struct { + float *m, *v; + size_t n; +} AdamState; + +typedef struct { + AdamState Wq, Wk, Wv, Wo; + AdamState W1, W2, W3; + AdamState rms_att, rms_ffn; +} LayerAdam; + +// Per-layer activation buffers (saved for backward) +typedef struct { + float *layer_in; // [DIM, SEQ] input to this layer (for rmsnorm1 bwd) + float *xnorm; // [DIM, SEQ] rmsnorm1 output + float *Q, *K, *V; // [DIM, SEQ] QKV projections + float *attn_out; // [DIM, SEQ] attention output (before Wo) + float *o_out; // [DIM, SEQ] Wo output + float *x2; // [DIM, SEQ] residual after attn + float *x2norm; // [DIM, SEQ] rmsnorm2 output + float *h1, *h3; // [HIDDEN, SEQ] FFN intermediates + float *silu_out; // [HIDDEN, SEQ] SiLU(h1)*h3 + float *ffn_out; // [DIM, SEQ] FFN output +} LayerActs; + +// Per-layer gradient accumulators +typedef struct { + float *Wq, *Wk, *Wv, *Wo; + float *W1, *W2, *W3; + float *rms_att, *rms_ffn; +} LayerGrads; + +// ANE kernels per layer +typedef struct { void *model; IOSurfaceRef ioIn, ioOut; void *request; void *tmpDir; } Kern; +typedef struct { + Kern *fwdAttn, *fwdFFN, *ffnBwd, *sdpaBwd1, *sdpaBwd2, *qkvBwd; +} LayerKernels; + +// Checkpoint header +typedef struct { + int magic; // 0x424C5A54 "BLZT" + int version; // 2 + int step, total_steps; + int n_layers, vocab_size, dim, hidden_dim, n_heads, seq_len; + float lr, loss; + double cum_compile, cum_train, cum_wall; + int cum_steps, cum_batches; + int adam_t; + int pad[3]; // alignment +} CkptHdr; + +// llama2.c model file header +typedef struct { + int dim, hidden_dim, n_layers, n_heads, n_kv_heads, vocab_size, seq_len; +} Llama2Config; + +// Globals +static Class g_D, g_I, g_AR, g_AIO; +static mach_timebase_info_data_t g_tb; +static int g_compile_count = 0; + +static void ane_init(void) { + dlopen("/System/Library/PrivateFrameworks/AppleNeuralEngine.framework/AppleNeuralEngine", RTLD_NOW); + g_D = NSClassFromString(@"_ANEInMemoryModelDescriptor"); + g_I = NSClassFromString(@"_ANEInMemoryModel"); + g_AR = NSClassFromString(@"_ANERequest"); + g_AIO= NSClassFromString(@"_ANEIOSurfaceObject"); +} +static double tb_ms(uint64_t t) { return (double)t * g_tb.numer / g_tb.denom / 1e6; } + +// Alloc helpers +static AdamState adam_alloc(size_t n) { AdamState s; s.m=(float*)calloc(n,4); s.v=(float*)calloc(n,4); s.n=n; return s; } +static void adam_free(AdamState *s) { free(s->m); free(s->v); } + +static LayerWeights layer_weights_alloc(void) { + LayerWeights w; + w.Wq=(float*)malloc(WQ_SZ*4); w.Wk=(float*)malloc(WQ_SZ*4); + w.Wv=(float*)malloc(WQ_SZ*4); w.Wo=(float*)malloc(WO_SZ*4); + w.W1=(float*)malloc(W1_SZ*4); w.W2=(float*)malloc(W2_SZ*4); w.W3=(float*)malloc(W3_SZ*4); + w.rms_att=(float*)malloc(DIM*4); w.rms_ffn=(float*)malloc(DIM*4); + return w; +} +static void layer_weights_free(LayerWeights *w) { + free(w->Wq);free(w->Wk);free(w->Wv);free(w->Wo); + free(w->W1);free(w->W2);free(w->W3); + free(w->rms_att);free(w->rms_ffn); +} +static LayerAdam layer_adam_alloc(void) { + LayerAdam a; + a.Wq=adam_alloc(WQ_SZ); a.Wk=adam_alloc(WQ_SZ); a.Wv=adam_alloc(WQ_SZ); a.Wo=adam_alloc(WO_SZ); + a.W1=adam_alloc(W1_SZ); a.W2=adam_alloc(W2_SZ); a.W3=adam_alloc(W3_SZ); + a.rms_att=adam_alloc(DIM); a.rms_ffn=adam_alloc(DIM); + return a; +} +static void layer_adam_free(LayerAdam *a) { + adam_free(&a->Wq);adam_free(&a->Wk);adam_free(&a->Wv);adam_free(&a->Wo); + adam_free(&a->W1);adam_free(&a->W2);adam_free(&a->W3); + adam_free(&a->rms_att);adam_free(&a->rms_ffn); +} +static LayerActs layer_acts_alloc(void) { + LayerActs a; + a.layer_in=(float*)malloc(SEQ*DIM*4); + a.xnorm=(float*)malloc(SEQ*DIM*4); a.Q=(float*)malloc(SEQ*DIM*4); + a.K=(float*)malloc(SEQ*DIM*4); a.V=(float*)malloc(SEQ*DIM*4); + a.attn_out=(float*)malloc(SEQ*DIM*4); a.o_out=(float*)malloc(SEQ*DIM*4); + a.x2=(float*)malloc(SEQ*DIM*4); a.x2norm=(float*)malloc(SEQ*DIM*4); + a.h1=(float*)malloc(SEQ*HIDDEN*4); a.h3=(float*)malloc(SEQ*HIDDEN*4); + a.silu_out=(float*)malloc(SEQ*HIDDEN*4); a.ffn_out=(float*)malloc(SEQ*DIM*4); + return a; +} +static void layer_acts_free(LayerActs *a) { + free(a->layer_in);free(a->xnorm);free(a->Q);free(a->K);free(a->V); + free(a->attn_out);free(a->o_out);free(a->x2);free(a->x2norm); + free(a->h1);free(a->h3);free(a->silu_out);free(a->ffn_out); +} +static LayerGrads layer_grads_alloc(void) { + LayerGrads g; + g.Wq=(float*)calloc(WQ_SZ,4); g.Wk=(float*)calloc(WQ_SZ,4); + g.Wv=(float*)calloc(WQ_SZ,4); g.Wo=(float*)calloc(WO_SZ,4); + g.W1=(float*)calloc(W1_SZ,4); g.W2=(float*)calloc(W2_SZ,4); g.W3=(float*)calloc(W3_SZ,4); + g.rms_att=(float*)calloc(DIM,4); g.rms_ffn=(float*)calloc(DIM,4); + return g; +} +static void layer_grads_zero(LayerGrads *g) { + memset(g->Wq,0,WQ_SZ*4);memset(g->Wk,0,WQ_SZ*4); + memset(g->Wv,0,WQ_SZ*4);memset(g->Wo,0,WO_SZ*4); + memset(g->W1,0,W1_SZ*4);memset(g->W2,0,W2_SZ*4);memset(g->W3,0,W3_SZ*4); + memset(g->rms_att,0,DIM*4);memset(g->rms_ffn,0,DIM*4); +} +static void layer_grads_free(LayerGrads *g) { + free(g->Wq);free(g->Wk);free(g->Wv);free(g->Wo); + free(g->W1);free(g->W2);free(g->W3); + free(g->rms_att);free(g->rms_ffn); +} diff --git a/training/stories_cpu_ops.h b/training/stories_cpu_ops.h new file mode 100644 index 0000000..c9f2cfa --- /dev/null +++ b/training/stories_cpu_ops.h @@ -0,0 +1,129 @@ +// stories_cpu_ops.h — CPU operations: RMSNorm, cross-entropy, Adam, softmax +#pragma once +#include "stories_config.h" + +static float *g_rms_tmp = NULL; + +static void rmsnorm(float *out, const float *x, const float *w, int d, int S) { + if (!g_rms_tmp) g_rms_tmp = (float*)malloc(S*4); + float *ss = (float*)calloc(S, sizeof(float)); + for (int i=0; in; i++) { + s->m[i] = b1*s->m[i] + (1-b1)*g[i]; + s->v[i] = b2*s->v[i] + (1-b2)*g[i]*g[i]; + float mh = s->m[i]/bc1, vh = s->v[i]/bc2; + w[i] -= lr * mh / (sqrtf(vh) + eps); + } +} + +// Cross-entropy loss + gradient for logits (column-major: [VOCAB, SEQ]) +// logits[v*SEQ+t] = logit for vocab v, position t +// targets[t] = target token id for position t +// Returns mean CE loss, writes dlogits = softmax(logits) - one_hot(targets) +// Data is column-major [V, S], but we process per-column (stride=1 within col is v*S+t, stride between v's is S) +// For vDSP: transpose to row-major scratch [S, V] to vectorize softmax per position +static float cross_entropy_loss(float *dlogits, const float *logits, const uint16_t *targets, int V, int S) { + // Work in transposed layout [S, V] where each row is one position's logits (contiguous) + float *buf = (float*)malloc(S * V * 4); + // Transpose [V,S] → [S,V]: buf[t*V+v] = logits[v*S+t] + vDSP_mtrans(logits, 1, buf, 1, (vDSP_Length)S, (vDSP_Length)V); + + float total_loss = 0; + float invS = 1.0f / S; + for (int t = 0; t < S; t++) { + float *row = buf + t * V; + // max + float maxv; + vDSP_maxv(row, 1, &maxv, (vDSP_Length)V); + // row -= maxv + float neg_max = -maxv; + vDSP_vsadd(row, 1, &neg_max, row, 1, (vDSP_Length)V); + // exp in-place + int n = V; + vvexpf(row, row, &n); + // sum + float sum; + vDSP_sve(row, 1, &sum, (vDSP_Length)V); + // normalize + float inv_sum = 1.0f / sum; + vDSP_vsmul(row, 1, &inv_sum, row, 1, (vDSP_Length)V); + // loss + int tgt = targets[t]; + total_loss -= logf(row[tgt] + 1e-10f); + // gradient: softmax - one_hot, then /S + row[tgt] -= 1.0f; + vDSP_vsmul(row, 1, &invS, row, 1, (vDSP_Length)V); + } + // Transpose back [S,V] → [V,S] + vDSP_mtrans(buf, 1, dlogits, 1, (vDSP_Length)V, (vDSP_Length)S); + free(buf); + return total_loss / S; +} + +// Embedding lookup: token_ids → x [DIM, SEQ] (channel-first) +// embed is [VOCAB, DIM] row-major (vocab_size rows, dim cols) +static void embed_lookup(float *x, const float *embed, const uint16_t *tokens, int dim, int seq) { + for (int t = 0; t < seq; t++) { + int tok = tokens[t]; + for (int d = 0; d < dim; d++) { + x[d*seq + t] = embed[tok*dim + d]; + } + } +} + +// Embedding backward: accumulate dE[tok] += dx[:,t] for each position +static void embed_backward(float *d_embed, const float *dx, const uint16_t *tokens, int dim, int seq) { + for (int t = 0; t < seq; t++) { + int tok = tokens[t]; + for (int d = 0; d < dim; d++) { + d_embed[tok*dim + d] += dx[d*seq + t]; + } + } +} diff --git a/training/stories_io.h b/training/stories_io.h new file mode 100644 index 0000000..017d8a8 --- /dev/null +++ b/training/stories_io.h @@ -0,0 +1,134 @@ +// stories_io.h — IOSurface helpers, blob builders, NEON conversion +#pragma once +#include "stories_config.h" +#include + +static IOSurfaceRef make_surface(size_t bytes) { + return IOSurfaceCreate((__bridge CFDictionaryRef)@{ + (id)kIOSurfaceWidth:@(bytes), (id)kIOSurfaceHeight:@1, + (id)kIOSurfaceBytesPerElement:@1, (id)kIOSurfaceBytesPerRow:@(bytes), + (id)kIOSurfaceAllocSize:@(bytes), (id)kIOSurfacePixelFormat:@0}); +} + +static NSData *build_blob(const float *w, int rows, int cols) { + int ws=rows*cols*2, tot=128+ws; + uint8_t *b=(uint8_t*)calloc(tot,1); + b[0]=1;b[4]=2;b[64]=0xEF;b[65]=0xBE;b[66]=0xAD;b[67]=0xDE;b[68]=1; + *(uint32_t*)(b+72)=ws;*(uint32_t*)(b+80)=128; + _Float16 *fp16=(_Float16*)(b+128); + for(int i=0;imodel = (void*)CFBridgingRetain(mdl); + k->ioIn = make_surface(ic_bytes); + k->ioOut = make_surface(oc_bytes); + id wI = ((id(*)(Class,SEL,IOSurfaceRef))objc_msgSend)(g_AIO, @selector(objectWithIOSurface:), k->ioIn); + id wO = ((id(*)(Class,SEL,IOSurfaceRef))objc_msgSend)(g_AIO, @selector(objectWithIOSurface:), k->ioOut); + k->request = (void*)CFBridgingRetain(((id(*)(Class,SEL,id,id,id,id,id,id,id))objc_msgSend)(g_AR, + @selector(requestWithInputs:inputIndices:outputs:outputIndices:weightsBuffer:perfStats:procedureIndex:), + @[wI], @[@0], @[wO], @[@0], nil, nil, @0)); + k->tmpDir = (void*)CFBridgingRetain(td); + return k; + } +} +static void free_kern(Kern *k) { + if (!k) return; + id mdl = (__bridge id)k->model; NSError *e = nil; + ((BOOL(*)(id,SEL,unsigned int,NSError**))objc_msgSend)(mdl, @selector(unloadWithQoS:error:), 21, &e); + CFRelease(k->ioIn); CFRelease(k->ioOut); + [[NSFileManager defaultManager] removeItemAtPath:(__bridge id)k->tmpDir error:nil]; + CFRelease(k->model); CFRelease(k->request); CFRelease(k->tmpDir); + free(k); +} +static void ane_eval(Kern *k) { + id mdl = (__bridge id)k->model; id req = (__bridge id)k->request; NSError *e = nil; + ((BOOL(*)(id,SEL,unsigned int,id,id,NSError**))objc_msgSend)(mdl, @selector(evaluateWithQoS:options:request:error:), 21, @{}, req, &e); +} diff --git a/training/stories_mil.h b/training/stories_mil.h new file mode 100644 index 0000000..dccca44 --- /dev/null +++ b/training/stories_mil.h @@ -0,0 +1,286 @@ +// stories_mil.h — MIL program generators for ANE kernels +// Same architecture as single-layer train_large.m but parameterized +#pragma once +#include "stories_io.h" + +#define MIL_HDR \ + @"program(1.3)\n[buildInfo = dict({{\"coremlc-component-MIL\", \"3510.2.1\"}, " \ + "{\"coremlc-version\", \"3505.4.1\"}, {\"coremltools-component-milinternal\", \"\"}, " \ + "{\"coremltools-version\", \"9.0\"}})]\n{\n" +#define CONV_CONST \ + " string pt = const()[name=string(\"pt\"), val=string(\"valid\")];\n" \ + " tensor st = const()[name=string(\"st\"), val=tensor([1,1])];\n" \ + " tensor pd = const()[name=string(\"pd\"), val=tensor([0,0,0,0])];\n" \ + " tensor dl = const()[name=string(\"dl\"), val=tensor([1,1])];\n" \ + " int32 gr = const()[name=string(\"gr\"), val=int32(1)];\n" + +// SDPA forward + taps: x_in → rmsnorm → QKV+SDPA+Wo → concat(o_out, Q, K, V, attn_out, xnorm) +static NSString *gen_sdpa_fwd_taps(void) { + float sc = 1.0f/sqrtf((float)HD); + float invd = 1.0f/(float)DIM; + NSMutableString *m = [NSMutableString string]; + [m appendString:MIL_HDR]; + [m appendFormat:@" func main(tensor x) {\n", DIM, SEQ]; + [m appendFormat:@" tensor sq = mul(x=x,y=x)[name=string(\"sq\")];\n", DIM, SEQ]; + [m appendFormat:@" tensor rax = const()[name=string(\"rax\"), val=tensor([1])];\n"]; + [m appendFormat:@" bool kd = const()[name=string(\"kd\"), val=bool(true)];\n"]; + [m appendFormat:@" tensor ss = reduce_sum(x=sq,axes=rax,keep_dims=kd)[name=string(\"ss\")];\n", SEQ]; + [m appendFormat:@" fp16 invd = const()[name=string(\"invd\"), val=fp16(%f)];\n", invd]; + [m appendFormat:@" tensor ss2 = mul(x=ss,y=invd)[name=string(\"ss2\")];\n", SEQ]; + [m appendFormat:@" fp16 eps = const()[name=string(\"eps\"), val=fp16(0.00001)];\n"]; + [m appendFormat:@" tensor ss3 = add(x=ss2,y=eps)[name=string(\"ss3\")];\n", SEQ]; + [m appendFormat:@" fp16 nhalf = const()[name=string(\"nhalf\"), val=fp16(-0.5)];\n"]; + [m appendFormat:@" tensor rrms = pow(x=ss3,y=nhalf)[name=string(\"rrms\")];\n", SEQ]; + [m appendFormat:@" tensor xr = mul(x=x,y=rrms)[name=string(\"xr\")];\n", DIM, SEQ]; + [m appendFormat:@" tensor rw = const()[name=string(\"rw\"), val=tensor(BLOBFILE(path=string(\"@model_path/weights/rms1.bin\"), offset=uint64(64)))];\n", DIM, DIM]; + [m appendFormat:@" tensor xn = mul(x=xr,y=rw)[name=string(\"xn\")];\n", DIM, SEQ]; + [m appendString:@CONV_CONST]; + [m appendFormat:@" tensor Wq = const()[name=string(\"Wq\"), val=tensor(BLOBFILE(path=string(\"@model_path/weights/wq.bin\"), offset=uint64(64)))];\n", DIM,DIM,DIM,DIM]; + [m appendFormat:@" tensor Wk = const()[name=string(\"Wk\"), val=tensor(BLOBFILE(path=string(\"@model_path/weights/wk.bin\"), offset=uint64(64)))];\n", DIM,DIM,DIM,DIM]; + [m appendFormat:@" tensor Wv = const()[name=string(\"Wv\"), val=tensor(BLOBFILE(path=string(\"@model_path/weights/wv.bin\"), offset=uint64(64)))];\n", DIM,DIM,DIM,DIM]; + [m appendFormat:@" tensor Wo = const()[name=string(\"Wo\"), val=tensor(BLOBFILE(path=string(\"@model_path/weights/wo.bin\"), offset=uint64(64)))];\n", DIM,DIM,DIM,DIM]; + [m appendFormat:@" tensor qf = conv(dilations=dl,groups=gr,pad=pd,pad_type=pt,strides=st,weight=Wq,x=xn)[name=string(\"cq\")];\n", DIM,SEQ]; + [m appendFormat:@" tensor kf = conv(dilations=dl,groups=gr,pad=pd,pad_type=pt,strides=st,weight=Wk,x=xn)[name=string(\"ck\")];\n", DIM,SEQ]; + [m appendFormat:@" tensor vf = conv(dilations=dl,groups=gr,pad=pd,pad_type=pt,strides=st,weight=Wv,x=xn)[name=string(\"cv\")];\n", DIM,SEQ]; + [m appendFormat:@" tensor qsh = const()[name=string(\"qsh\"), val=tensor([1,%d,%d,%d])];\n", HEADS,HD,SEQ]; + [m appendString:@" tensor pm = const()[name=string(\"pm\"), val=tensor([0,1,3,2])];\n"]; + [m appendFormat:@" tensor q4 = reshape(shape=qsh,x=qf)[name=string(\"rq\")];\n", HEADS,HD,SEQ]; + [m appendFormat:@" tensor q = transpose(perm=pm,x=q4)[name=string(\"tq\")];\n", HEADS,SEQ,HD]; + [m appendFormat:@" tensor k4 = reshape(shape=qsh,x=kf)[name=string(\"rk\")];\n", HEADS,HD,SEQ]; + [m appendFormat:@" tensor k = transpose(perm=pm,x=k4)[name=string(\"tk\")];\n", HEADS,SEQ,HD]; + [m appendFormat:@" tensor v4 = reshape(shape=qsh,x=vf)[name=string(\"rv\")];\n", HEADS,HD,SEQ]; + [m appendFormat:@" tensor v = transpose(perm=pm,x=v4)[name=string(\"tv\")];\n", HEADS,SEQ,HD]; + [m appendString:@" bool tx = const()[name=string(\"tx\"), val=bool(false)];\n"]; + [m appendString:@" bool ty = const()[name=string(\"ty\"), val=bool(true)];\n"]; + [m appendFormat:@" tensor sc1 = matmul(transpose_x=tx,transpose_y=ty,x=q,y=k)[name=string(\"mm1\")];\n", HEADS,SEQ,SEQ]; + [m appendFormat:@" fp16 scv = const()[name=string(\"scv\"), val=fp16(%f)];\n", sc]; + [m appendFormat:@" tensor sc2 = mul(x=sc1,y=scv)[name=string(\"scl\")];\n", HEADS,SEQ,SEQ]; + [m appendFormat:@" tensor cm = const()[name=string(\"cm\"), val=tensor(BLOBFILE(path=string(\"@model_path/weights/mask.bin\"), offset=uint64(64)))];\n", SEQ,SEQ,SEQ,SEQ]; + [m appendFormat:@" tensor ms = add(x=sc2,y=cm)[name=string(\"msk\")];\n", HEADS,SEQ,SEQ]; + [m appendString:@" int32 sax = const()[name=string(\"sax\"), val=int32(-1)];\n"]; + [m appendFormat:@" tensor aw = softmax(axis=sax,x=ms)[name=string(\"sm\")];\n", HEADS,SEQ,SEQ]; + [m appendFormat:@" tensor a4 = matmul(transpose_x=tx,transpose_y=tx,x=aw,y=v)[name=string(\"mm2\")];\n", HEADS,SEQ,HD]; + [m appendFormat:@" tensor at = transpose(perm=pm,x=a4)[name=string(\"ta\")];\n", HEADS,HD,SEQ]; + [m appendFormat:@" tensor os = const()[name=string(\"os\"), val=tensor([1,%d,1,%d])];\n", DIM,SEQ]; + [m appendFormat:@" tensor af = reshape(shape=os,x=at)[name=string(\"ra\")];\n", DIM,SEQ]; + [m appendFormat:@" tensor oo = conv(dilations=dl,groups=gr,pad=pd,pad_type=pt,strides=st,weight=Wo,x=af)[name=string(\"co\")];\n", DIM,SEQ]; + [m appendString:@" int32 cax = const()[name=string(\"cax\"), val=int32(1)];\n"]; + [m appendString:@" bool cid = const()[name=string(\"cid\"), val=bool(false)];\n"]; + [m appendFormat:@" tensor out = concat(axis=cax,interleave=cid,values=(oo,qf,kf,vf,af,xn))[name=string(\"cat\")];\n", 6*DIM,SEQ]; + [m appendString:@" } -> (out);\n}\n"]; + return m; +} + +// FFN forward + taps: x2 → rmsnorm → FFN → concat(ffn_out, h1, h3, silu_out, x2norm) +static NSString *gen_ffn_fwd_taps(void) { + float invd = 1.0f/(float)DIM; + NSMutableString *m = [NSMutableString string]; + [m appendString:MIL_HDR]; + [m appendFormat:@" func main(tensor x) {\n", DIM, SEQ]; + [m appendFormat:@" tensor sq = mul(x=x,y=x)[name=string(\"sq\")];\n", DIM, SEQ]; + [m appendFormat:@" tensor rax = const()[name=string(\"rax\"), val=tensor([1])];\n"]; + [m appendFormat:@" bool kd = const()[name=string(\"kd\"), val=bool(true)];\n"]; + [m appendFormat:@" tensor ss = reduce_sum(x=sq,axes=rax,keep_dims=kd)[name=string(\"ss\")];\n", SEQ]; + [m appendFormat:@" fp16 invd = const()[name=string(\"invd\"), val=fp16(%f)];\n", invd]; + [m appendFormat:@" tensor ss2 = mul(x=ss,y=invd)[name=string(\"ss2\")];\n", SEQ]; + [m appendFormat:@" fp16 eps = const()[name=string(\"eps\"), val=fp16(0.00001)];\n"]; + [m appendFormat:@" tensor ss3 = add(x=ss2,y=eps)[name=string(\"ss3\")];\n", SEQ]; + [m appendFormat:@" fp16 nhalf = const()[name=string(\"nhalf\"), val=fp16(-0.5)];\n"]; + [m appendFormat:@" tensor rrms = pow(x=ss3,y=nhalf)[name=string(\"rrms\")];\n", SEQ]; + [m appendFormat:@" tensor xr = mul(x=x,y=rrms)[name=string(\"xr\")];\n", DIM, SEQ]; + [m appendFormat:@" tensor rw = const()[name=string(\"rw\"), val=tensor(BLOBFILE(path=string(\"@model_path/weights/rms2.bin\"), offset=uint64(64)))];\n", DIM, DIM]; + [m appendFormat:@" tensor xn = mul(x=xr,y=rw)[name=string(\"xn\")];\n", DIM, SEQ]; + [m appendString:@CONV_CONST]; + [m appendFormat:@" tensor W1 = const()[name=string(\"W1\"), val=tensor(BLOBFILE(path=string(\"@model_path/weights/w1.bin\"), offset=uint64(64)))];\n", HIDDEN,DIM,HIDDEN,DIM]; + [m appendFormat:@" tensor W3 = const()[name=string(\"W3\"), val=tensor(BLOBFILE(path=string(\"@model_path/weights/w3.bin\"), offset=uint64(64)))];\n", HIDDEN,DIM,HIDDEN,DIM]; + [m appendFormat:@" tensor W2 = const()[name=string(\"W2\"), val=tensor(BLOBFILE(path=string(\"@model_path/weights/w2.bin\"), offset=uint64(64)))];\n", DIM,HIDDEN,DIM,HIDDEN]; + [m appendFormat:@" tensor h1 = conv(dilations=dl,groups=gr,pad=pd,pad_type=pt,strides=st,weight=W1,x=xn)[name=string(\"c1\")];\n", HIDDEN,SEQ]; + [m appendFormat:@" tensor h3 = conv(dilations=dl,groups=gr,pad=pd,pad_type=pt,strides=st,weight=W3,x=xn)[name=string(\"c3\")];\n", HIDDEN,SEQ]; + [m appendFormat:@" tensor sig = sigmoid(x=h1)[name=string(\"sg\")];\n", HIDDEN,SEQ]; + [m appendFormat:@" tensor silu = mul(x=h1,y=sig)[name=string(\"si\")];\n", HIDDEN,SEQ]; + [m appendFormat:@" tensor gate = mul(x=silu,y=h3)[name=string(\"gt\")];\n", HIDDEN,SEQ]; + [m appendFormat:@" tensor y = conv(dilations=dl,groups=gr,pad=pd,pad_type=pt,strides=st,weight=W2,x=gate)[name=string(\"c2\")];\n", DIM,SEQ]; + [m appendString:@" int32 cax = const()[name=string(\"cax\"), val=int32(1)];\n"]; + [m appendString:@" bool cid = const()[name=string(\"cid\"), val=bool(false)];\n"]; + [m appendFormat:@" tensor out = concat(axis=cax,interleave=cid,values=(y,h1,h3,gate,xn))[name=string(\"cat\")];\n", 2*DIM+3*HIDDEN,SEQ]; + [m appendString:@" } -> (out);\n}\n"]; + return m; +} + +// FFN backward: concat(dffn,h1,h3) → concat(dx,dh1,dh3) +static NSString *gen_ffn_bwd(void) { + NSMutableString *m = [NSMutableString string]; + [m appendString:MIL_HDR]; + [m appendFormat:@" func main(tensor x) {\n", DIM+2*HIDDEN, SEQ]; + [m appendString:@CONV_CONST]; + [m appendString:@" tensor bd = const()[name=string(\"bd\"), val=tensor([0,0,0,0])];\n"]; + [m appendFormat:@" tensor sd = const()[name=string(\"sd\"), val=tensor([1,%d,1,%d])];\n", DIM, SEQ]; + [m appendFormat:@" tensor dffn = slice_by_size(x=x,begin=bd,size=sd)[name=string(\"s0\")];\n", DIM, SEQ]; + [m appendFormat:@" tensor b1 = const()[name=string(\"b1\"), val=tensor([0,%d,0,0])];\n", DIM]; + [m appendFormat:@" tensor s1 = const()[name=string(\"s1\"), val=tensor([1,%d,1,%d])];\n", HIDDEN, SEQ]; + [m appendFormat:@" tensor h1 = slice_by_size(x=x,begin=b1,size=s1)[name=string(\"s1x\")];\n", HIDDEN, SEQ]; + [m appendFormat:@" tensor b3 = const()[name=string(\"b3\"), val=tensor([0,%d,0,0])];\n", DIM+HIDDEN]; + [m appendFormat:@" tensor h3 = slice_by_size(x=x,begin=b3,size=s1)[name=string(\"s3x\")];\n", HIDDEN, SEQ]; + [m appendFormat:@" tensor W2t = const()[name=string(\"W2t\"), val=tensor(BLOBFILE(path=string(\"@model_path/weights/w2t.bin\"), offset=uint64(64)))];\n", HIDDEN, DIM, HIDDEN, DIM]; + [m appendFormat:@" tensor dsilu = conv(dilations=dl,groups=gr,pad=pd,pad_type=pt,strides=st,weight=W2t,x=dffn)[name=string(\"cw2\")];\n", HIDDEN, SEQ]; + [m appendFormat:@" tensor sig = sigmoid(x=h1)[name=string(\"sg\")];\n", HIDDEN, SEQ]; + [m appendString:@" fp16 one = const()[name=string(\"one\"), val=fp16(1.0)];\n"]; + [m appendFormat:@" tensor oms = sub(x=one,y=sig)[name=string(\"oms\")];\n", HIDDEN, SEQ]; + [m appendFormat:@" tensor homs = mul(x=h1,y=oms)[name=string(\"homs\")];\n", HIDDEN, SEQ]; + [m appendFormat:@" tensor brk = add(x=one,y=homs)[name=string(\"brk\")];\n", HIDDEN, SEQ]; + [m appendFormat:@" tensor dsd = mul(x=sig,y=brk)[name=string(\"dsd\")];\n", HIDDEN, SEQ]; + [m appendFormat:@" tensor t1 = mul(x=dsilu,y=h3)[name=string(\"t1\")];\n", HIDDEN, SEQ]; + [m appendFormat:@" tensor dh1 = mul(x=t1,y=dsd)[name=string(\"dh1\")];\n", HIDDEN, SEQ]; + [m appendFormat:@" tensor slh = mul(x=h1,y=sig)[name=string(\"slh\")];\n", HIDDEN, SEQ]; + [m appendFormat:@" tensor dh3 = mul(x=dsilu,y=slh)[name=string(\"dh3\")];\n", HIDDEN, SEQ]; + [m appendFormat:@" tensor W1t = const()[name=string(\"W1t\"), val=tensor(BLOBFILE(path=string(\"@model_path/weights/w1t.bin\"), offset=uint64(64)))];\n", DIM, HIDDEN, DIM, HIDDEN]; + [m appendFormat:@" tensor W3t = const()[name=string(\"W3t\"), val=tensor(BLOBFILE(path=string(\"@model_path/weights/w3t.bin\"), offset=uint64(64)))];\n", DIM, HIDDEN, DIM, HIDDEN]; + [m appendFormat:@" tensor dx1 = conv(dilations=dl,groups=gr,pad=pd,pad_type=pt,strides=st,weight=W1t,x=dh1)[name=string(\"cw1\")];\n", DIM, SEQ]; + [m appendFormat:@" tensor dx3 = conv(dilations=dl,groups=gr,pad=pd,pad_type=pt,strides=st,weight=W3t,x=dh3)[name=string(\"cw3\")];\n", DIM, SEQ]; + [m appendFormat:@" tensor dx = add(x=dx1,y=dx3)[name=string(\"adx\")];\n", DIM, SEQ]; + [m appendString:@" int32 cax = const()[name=string(\"cax\"), val=int32(1)];\n"]; + [m appendString:@" bool cid = const()[name=string(\"cid\"), val=bool(false)];\n"]; + [m appendFormat:@" tensor out = concat(axis=cax,interleave=cid,values=(dx,dh1,dh3))[name=string(\"cat\")];\n", DIM+2*HIDDEN, SEQ]; + [m appendString:@" } -> (out);\n}\n"]; + return m; +} + +// QKV backward: concat(dq,dk,dv) → dx +static NSString *gen_qkvb(void) { + NSMutableString *m = [NSMutableString string]; + [m appendString:MIL_HDR]; + [m appendFormat:@" func main(tensor x) {\n", 3*DIM, SEQ]; + [m appendString:@CONV_CONST]; + [m appendFormat:@" tensor sz = const()[name=string(\"sz\"), val=tensor([1,%d,1,%d])];\n", DIM, SEQ]; + [m appendString:@" tensor b0 = const()[name=string(\"b0\"), val=tensor([0,0,0,0])];\n"]; + [m appendFormat:@" tensor dq = slice_by_size(x=x,begin=b0,size=sz)[name=string(\"s0\")];\n", DIM,SEQ]; + [m appendFormat:@" tensor b1 = const()[name=string(\"b1\"), val=tensor([0,%d,0,0])];\n", DIM]; + [m appendFormat:@" tensor dk = slice_by_size(x=x,begin=b1,size=sz)[name=string(\"s1\")];\n", DIM,SEQ]; + [m appendFormat:@" tensor b2 = const()[name=string(\"b2\"), val=tensor([0,%d,0,0])];\n", 2*DIM]; + [m appendFormat:@" tensor dv = slice_by_size(x=x,begin=b2,size=sz)[name=string(\"s2\")];\n", DIM,SEQ]; + [m appendFormat:@" tensor Wqt = const()[name=string(\"Wqt\"), val=tensor(BLOBFILE(path=string(\"@model_path/weights/wqt.bin\"), offset=uint64(64)))];\n", DIM,DIM,DIM,DIM]; + [m appendFormat:@" tensor Wkt = const()[name=string(\"Wkt\"), val=tensor(BLOBFILE(path=string(\"@model_path/weights/wkt.bin\"), offset=uint64(64)))];\n", DIM,DIM,DIM,DIM]; + [m appendFormat:@" tensor Wvt = const()[name=string(\"Wvt\"), val=tensor(BLOBFILE(path=string(\"@model_path/weights/wvt.bin\"), offset=uint64(64)))];\n", DIM,DIM,DIM,DIM]; + [m appendFormat:@" tensor dxq = conv(dilations=dl,groups=gr,pad=pd,pad_type=pt,strides=st,weight=Wqt,x=dq)[name=string(\"cq\")];\n", DIM,SEQ]; + [m appendFormat:@" tensor dxk = conv(dilations=dl,groups=gr,pad=pd,pad_type=pt,strides=st,weight=Wkt,x=dk)[name=string(\"ck\")];\n", DIM,SEQ]; + [m appendFormat:@" tensor dxv = conv(dilations=dl,groups=gr,pad=pd,pad_type=pt,strides=st,weight=Wvt,x=dv)[name=string(\"cv\")];\n", DIM,SEQ]; + [m appendFormat:@" tensor dxqk = add(x=dxq,y=dxk)[name=string(\"aqk\")];\n", DIM,SEQ]; + [m appendFormat:@" tensor out = add(x=dxqk,y=dxv)[name=string(\"out\")];\n", DIM,SEQ]; + [m appendString:@" } -> (out);\n}\n"]; + return m; +} + +// SDPA backward part 1 + Wo^T +static NSString *gen_sdpa_bwd1(void) { + float sc = 1.0f/sqrtf((float)HD); + NSMutableString *m = [NSMutableString string]; + [m appendString:MIL_HDR]; + [m appendFormat:@" func main(tensor x) {\n", 4*DIM, SEQ]; + [m appendString:@CONV_CONST]; + [m appendFormat:@" tensor sz = const()[name=string(\"sz\"), val=tensor([1,%d,1,%d])];\n", DIM, SEQ]; + [m appendString:@" tensor b0 = const()[name=string(\"b0\"), val=tensor([0,0,0,0])];\n"]; + [m appendFormat:@" tensor qf = slice_by_size(x=x,begin=b0,size=sz)[name=string(\"s0\")];\n", DIM,SEQ]; + [m appendFormat:@" tensor b1 = const()[name=string(\"b1\"), val=tensor([0,%d,0,0])];\n", DIM]; + [m appendFormat:@" tensor kf = slice_by_size(x=x,begin=b1,size=sz)[name=string(\"s1\")];\n", DIM,SEQ]; + [m appendFormat:@" tensor b2 = const()[name=string(\"b2\"), val=tensor([0,%d,0,0])];\n", 2*DIM]; + [m appendFormat:@" tensor vf = slice_by_size(x=x,begin=b2,size=sz)[name=string(\"s2\")];\n", DIM,SEQ]; + [m appendFormat:@" tensor b3 = const()[name=string(\"b3\"), val=tensor([0,%d,0,0])];\n", 3*DIM]; + [m appendFormat:@" tensor dx2f = slice_by_size(x=x,begin=b3,size=sz)[name=string(\"s3\")];\n", DIM,SEQ]; + [m appendFormat:@" tensor Wot = const()[name=string(\"Wot\"), val=tensor(BLOBFILE(path=string(\"@model_path/weights/wot.bin\"), offset=uint64(64)))];\n", DIM,DIM,DIM,DIM]; + [m appendFormat:@" tensor df = conv(dilations=dl,groups=gr,pad=pd,pad_type=pt,strides=st,weight=Wot,x=dx2f)[name=string(\"cwo\")];\n", DIM,SEQ]; + [m appendFormat:@" tensor rsh = const()[name=string(\"rsh\"), val=tensor([1,%d,%d,%d])];\n", HEADS,HD,SEQ]; + [m appendString:@" tensor pm = const()[name=string(\"pm\"), val=tensor([0,1,3,2])];\n"]; + [m appendFormat:@" tensor qr = reshape(shape=rsh,x=qf)[name=string(\"rq\")];\n", HEADS,HD,SEQ]; + [m appendFormat:@" tensor q = transpose(perm=pm,x=qr)[name=string(\"tq\")];\n", HEADS,SEQ,HD]; + [m appendFormat:@" tensor kr = reshape(shape=rsh,x=kf)[name=string(\"rk\")];\n", HEADS,HD,SEQ]; + [m appendFormat:@" tensor k = transpose(perm=pm,x=kr)[name=string(\"tk\")];\n", HEADS,SEQ,HD]; + [m appendFormat:@" tensor vr = reshape(shape=rsh,x=vf)[name=string(\"rv\")];\n", HEADS,HD,SEQ]; + [m appendFormat:@" tensor v = transpose(perm=pm,x=vr)[name=string(\"tv\")];\n", HEADS,SEQ,HD]; + [m appendFormat:@" tensor dr = reshape(shape=rsh,x=df)[name=string(\"rd\")];\n", HEADS,HD,SEQ]; + [m appendFormat:@" tensor da = transpose(perm=pm,x=dr)[name=string(\"td\")];\n", HEADS,SEQ,HD]; + [m appendString:@" bool bF = const()[name=string(\"bF\"), val=bool(false)];\n"]; + [m appendString:@" bool bT = const()[name=string(\"bT\"), val=bool(true)];\n"]; + [m appendFormat:@" tensor sc1 = matmul(transpose_x=bF,transpose_y=bT,x=q,y=k)[name=string(\"mm1\")];\n", HEADS,SEQ,SEQ]; + [m appendFormat:@" fp16 scv = const()[name=string(\"scv\"), val=fp16(%f)];\n", sc]; + [m appendFormat:@" tensor sc2 = mul(x=sc1,y=scv)[name=string(\"scl\")];\n", HEADS,SEQ,SEQ]; + [m appendFormat:@" tensor cm = const()[name=string(\"cm\"), val=tensor(BLOBFILE(path=string(\"@model_path/weights/mask.bin\"), offset=uint64(64)))];\n", SEQ,SEQ,SEQ,SEQ]; + [m appendFormat:@" tensor ms = add(x=sc2,y=cm)[name=string(\"msk\")];\n", HEADS,SEQ,SEQ]; + [m appendString:@" int32 sax = const()[name=string(\"sax\"), val=int32(-1)];\n"]; + [m appendFormat:@" tensor probs = softmax(axis=sax,x=ms)[name=string(\"sm\")];\n", HEADS,SEQ,SEQ]; + [m appendFormat:@" tensor dv4 = matmul(transpose_x=bT,transpose_y=bF,x=probs,y=da)[name=string(\"dv\")];\n", HEADS,SEQ,HD]; + [m appendFormat:@" tensor dp4 = matmul(transpose_x=bF,transpose_y=bT,x=da,y=v)[name=string(\"dp\")];\n", HEADS,SEQ,SEQ]; + [m appendFormat:@" tensor dvt = transpose(perm=pm,x=dv4)[name=string(\"dvt\")];\n", HEADS,HD,SEQ]; + [m appendFormat:@" tensor dvs = const()[name=string(\"dvs\"), val=tensor([1,%d,1,%d])];\n", DIM,SEQ]; + [m appendFormat:@" tensor dvf = reshape(shape=dvs,x=dvt)[name=string(\"dvf\")];\n", DIM,SEQ]; + [m appendFormat:@" tensor scs = const()[name=string(\"scs\"), val=tensor([1,%d,1,%d])];\n", SCORE_CH,SEQ]; + [m appendFormat:@" tensor pf = reshape(shape=scs,x=probs)[name=string(\"pf\")];\n", SCORE_CH,SEQ]; + [m appendFormat:@" tensor dpf = reshape(shape=scs,x=dp4)[name=string(\"dpf\")];\n", SCORE_CH,SEQ]; + [m appendString:@" int32 cax = const()[name=string(\"cax\"), val=int32(1)];\n"]; + [m appendString:@" bool cid = const()[name=string(\"cid\"), val=bool(false)];\n"]; + [m appendFormat:@" tensor out = concat(axis=cax,interleave=cid,values=(dvf,pf,dpf))[name=string(\"cat\")];\n", DIM+2*SCORE_CH,SEQ]; + [m appendString:@" } -> (out);\n}\n"]; + return m; +} + +// SDPA backward part 2: concat(probs,dp,Q,K) → concat(dQ,dK) +static NSString *gen_sdpa_bwd2(void) { + float sc = 1.0f/sqrtf((float)HD); + int bwd2_in = 2*SCORE_CH + 2*DIM; + NSMutableString *m = [NSMutableString string]; + [m appendString:MIL_HDR]; + [m appendFormat:@" func main(tensor x) {\n", bwd2_in, SEQ]; + [m appendFormat:@" tensor sz_sc = const()[name=string(\"szsc\"), val=tensor([1,%d,1,%d])];\n", SCORE_CH, SEQ]; + [m appendString:@" tensor b0 = const()[name=string(\"b0\"), val=tensor([0,0,0,0])];\n"]; + [m appendFormat:@" tensor pf = slice_by_size(x=x,begin=b0,size=sz_sc)[name=string(\"s0\")];\n", SCORE_CH,SEQ]; + [m appendFormat:@" tensor b1 = const()[name=string(\"b1\"), val=tensor([0,%d,0,0])];\n", SCORE_CH]; + [m appendFormat:@" tensor dpf = slice_by_size(x=x,begin=b1,size=sz_sc)[name=string(\"s1\")];\n", SCORE_CH,SEQ]; + [m appendFormat:@" tensor sz_d = const()[name=string(\"szd\"), val=tensor([1,%d,1,%d])];\n", DIM, SEQ]; + [m appendFormat:@" tensor b2 = const()[name=string(\"b2\"), val=tensor([0,%d,0,0])];\n", 2*SCORE_CH]; + [m appendFormat:@" tensor qf = slice_by_size(x=x,begin=b2,size=sz_d)[name=string(\"s2\")];\n", DIM,SEQ]; + [m appendFormat:@" tensor b3 = const()[name=string(\"b3\"), val=tensor([0,%d,0,0])];\n", 2*SCORE_CH+DIM]; + [m appendFormat:@" tensor kf = slice_by_size(x=x,begin=b3,size=sz_d)[name=string(\"s3\")];\n", DIM,SEQ]; + [m appendFormat:@" tensor ssh = const()[name=string(\"ssh\"), val=tensor([1,%d,%d,%d])];\n", HEADS,SEQ,SEQ]; + [m appendFormat:@" tensor probs = reshape(shape=ssh,x=pf)[name=string(\"rp\")];\n", HEADS,SEQ,SEQ]; + [m appendFormat:@" tensor dp = reshape(shape=ssh,x=dpf)[name=string(\"rdp\")];\n", HEADS,SEQ,SEQ]; + [m appendFormat:@" tensor rsh = const()[name=string(\"rsh\"), val=tensor([1,%d,%d,%d])];\n", HEADS,HD,SEQ]; + [m appendString:@" tensor pm = const()[name=string(\"pm\"), val=tensor([0,1,3,2])];\n"]; + [m appendFormat:@" tensor qr = reshape(shape=rsh,x=qf)[name=string(\"rq\")];\n", HEADS,HD,SEQ]; + [m appendFormat:@" tensor q = transpose(perm=pm,x=qr)[name=string(\"tq\")];\n", HEADS,SEQ,HD]; + [m appendFormat:@" tensor kr = reshape(shape=rsh,x=kf)[name=string(\"rk\")];\n", HEADS,HD,SEQ]; + [m appendFormat:@" tensor k = transpose(perm=pm,x=kr)[name=string(\"tk\")];\n", HEADS,SEQ,HD]; + [m appendFormat:@" tensor pdp = mul(x=probs,y=dp)[name=string(\"pdp\")];\n", HEADS,SEQ,SEQ]; + [m appendString:@" tensor rax = const()[name=string(\"rax\"), val=tensor([-1])];\n"]; + [m appendString:@" bool kd = const()[name=string(\"kd\"), val=bool(true)];\n"]; + [m appendFormat:@" tensor spdp = reduce_sum(x=pdp,axes=rax,keep_dims=kd)[name=string(\"rs\")];\n", HEADS,SEQ]; + [m appendFormat:@" tensor dps = sub(x=dp,y=spdp)[name=string(\"dps\")];\n", HEADS,SEQ,SEQ]; + [m appendFormat:@" tensor ds0 = mul(x=probs,y=dps)[name=string(\"ds0\")];\n", HEADS,SEQ,SEQ]; + [m appendFormat:@" fp16 scv = const()[name=string(\"scv\"), val=fp16(%f)];\n", sc]; + [m appendFormat:@" tensor ds = mul(x=ds0,y=scv)[name=string(\"ds\")];\n", HEADS,SEQ,SEQ]; + [m appendString:@" bool bF = const()[name=string(\"bF\"), val=bool(false)];\n"]; + [m appendString:@" bool bT = const()[name=string(\"bT\"), val=bool(true)];\n"]; + [m appendFormat:@" tensor dq4 = matmul(transpose_x=bF,transpose_y=bF,x=ds,y=k)[name=string(\"dq\")];\n", HEADS,SEQ,HD]; + [m appendFormat:@" tensor dk4 = matmul(transpose_x=bT,transpose_y=bF,x=ds,y=q)[name=string(\"dk\")];\n", HEADS,SEQ,HD]; + [m appendFormat:@" tensor dqt = transpose(perm=pm,x=dq4)[name=string(\"dqt\")];\n", HEADS,HD,SEQ]; + [m appendFormat:@" tensor dkt = transpose(perm=pm,x=dk4)[name=string(\"dkt\")];\n", HEADS,HD,SEQ]; + [m appendFormat:@" tensor fs = const()[name=string(\"fs\"), val=tensor([1,%d,1,%d])];\n", DIM,SEQ]; + [m appendFormat:@" tensor dqf = reshape(shape=fs,x=dqt)[name=string(\"dqf\")];\n", DIM,SEQ]; + [m appendFormat:@" tensor dkf = reshape(shape=fs,x=dkt)[name=string(\"dkf\")];\n", DIM,SEQ]; + [m appendString:@" int32 cax = const()[name=string(\"cax\"), val=int32(1)];\n"]; + [m appendString:@" bool cid = const()[name=string(\"cid\"), val=bool(false)];\n"]; + [m appendFormat:@" tensor out = concat(axis=cax,interleave=cid,values=(dqf,dkf))[name=string(\"cat\")];\n", 2*DIM,SEQ]; + [m appendString:@" } -> (out);\n}\n"]; + return m; +} + +// Mask blob (causal mask [SEQ,SEQ]) +static NSData *g_mask_blob = nil; +static NSData *get_mask_blob(void) { + if (!g_mask_blob) { + _Float16 *mask = (_Float16*)calloc(SEQ*SEQ, sizeof(_Float16)); + for(int t=0;t -#import -#import -#import -#import -#import -#import -#include -#include -#include +// train_large.m — Train stories110M (12 layers, 768dim, 3072hidden) on ANE +// Uses pretokenized TinyStories data with cross-entropy loss +// 5 weight-bearing ANE kernels per layer × 12 layers = 60 per compile batch +#include "stories_io.h" +#include "stories_mil.h" +#include "stories_cpu_ops.h" -#define DIM 768 -#define HIDDEN 2048 -#define HEADS 12 -#define HD (DIM/HEADS) -#define SEQ 512 -#define ACCUM_STEPS 100 -#define MAX_COMPILES 100 -#define NUM_KERNELS 6 -#define CKPT_PATH "/tmp/ane_large_ckpt.bin" +#define CKPT_PATH "ane_stories110M_ckpt.bin" +#define MODEL_PATH "../../assets/models/stories110M.bin" +#define DATA_PATH "tinystories_data00.bin" -static Class g_D, g_I, g_AR, g_AIO; -static mach_timebase_info_data_t g_tb; -static int g_compile_count = 0; - -static void ane_init(void) { - dlopen("/System/Library/PrivateFrameworks/AppleNeuralEngine.framework/AppleNeuralEngine", RTLD_NOW); - g_D = NSClassFromString(@"_ANEInMemoryModelDescriptor"); - g_I = NSClassFromString(@"_ANEInMemoryModel"); - g_AR = NSClassFromString(@"_ANERequest"); - g_AIO= NSClassFromString(@"_ANEIOSurfaceObject"); -} -static double tb_ms(uint64_t t) { return (double)t * g_tb.numer / g_tb.denom / 1e6; } -static IOSurfaceRef make_surface(size_t bytes) { - return IOSurfaceCreate((__bridge CFDictionaryRef)@{ - (id)kIOSurfaceWidth:@(bytes), (id)kIOSurfaceHeight:@1, - (id)kIOSurfaceBytesPerElement:@1, (id)kIOSurfaceBytesPerRow:@(bytes), - (id)kIOSurfaceAllocSize:@(bytes), (id)kIOSurfacePixelFormat:@0}); -} -static NSData *build_blob(const float *w, int rows, int cols) { - int ws=rows*cols*2, tot=128+ws; - uint8_t *b=(uint8_t*)calloc(tot,1); - b[0]=1;b[4]=2;b[64]=0xEF;b[65]=0xBE;b[66]=0xAD;b[67]=0xDE;b[68]=1; - *(uint32_t*)(b+72)=ws;*(uint32_t*)(b+80)=128; - _Float16 *fp16=(_Float16*)(b+128); - for(int i=0;i({{\"coremlc-component-MIL\", \"3510.2.1\"}, " \ - "{\"coremlc-version\", \"3505.4.1\"}, {\"coremltools-component-milinternal\", \"\"}, " \ - "{\"coremltools-version\", \"9.0\"}})]\n{\n" -#define CONV_CONST \ - " string pt = const()[name=string(\"pt\"), val=string(\"valid\")];\n" \ - " tensor st = const()[name=string(\"st\"), val=tensor([1,1])];\n" \ - " tensor pd = const()[name=string(\"pd\"), val=tensor([0,0,0,0])];\n" \ - " tensor dl = const()[name=string(\"dl\"), val=tensor([1,1])];\n" \ - " int32 gr = const()[name=string(\"gr\"), val=int32(1)];\n" - -// SDPA forward + taps: x_in → rmsnorm → QKV+SDPA+Wo → concat(o_out, Q, K, V, attn_out, xnorm) fp16 -static NSString *gen_sdpa_fwd_taps(void) { - float sc = 1.0f/sqrtf((float)HD); - float invd = 1.0f/(float)DIM; - NSMutableString *m = [NSMutableString string]; - [m appendString:MIL_HDR]; - [m appendFormat:@" func main(tensor x) {\n", DIM, SEQ]; - // --- RMSNorm: x → xn --- - [m appendFormat:@" tensor sq = mul(x=x,y=x)[name=string(\"sq\")];\n", DIM, SEQ]; - [m appendFormat:@" tensor rax = const()[name=string(\"rax\"), val=tensor([1])];\n"]; - [m appendFormat:@" bool kd = const()[name=string(\"kd\"), val=bool(true)];\n"]; - [m appendFormat:@" tensor ss = reduce_sum(x=sq,axes=rax,keep_dims=kd)[name=string(\"ss\")];\n", SEQ]; - [m appendFormat:@" fp16 invd = const()[name=string(\"invd\"), val=fp16(%f)];\n", invd]; - [m appendFormat:@" tensor ss2 = mul(x=ss,y=invd)[name=string(\"ss2\")];\n", SEQ]; - [m appendFormat:@" fp16 eps = const()[name=string(\"eps\"), val=fp16(0.00001)];\n"]; - [m appendFormat:@" tensor ss3 = add(x=ss2,y=eps)[name=string(\"ss3\")];\n", SEQ]; - [m appendFormat:@" fp16 nhalf = const()[name=string(\"nhalf\"), val=fp16(-0.5)];\n"]; - [m appendFormat:@" tensor rrms = pow(x=ss3,y=nhalf)[name=string(\"rrms\")];\n", SEQ]; - [m appendFormat:@" tensor xr = mul(x=x,y=rrms)[name=string(\"xr\")];\n", DIM, SEQ]; - [m appendFormat:@" tensor rw = const()[name=string(\"rw\"), val=tensor(BLOBFILE(path=string(\"@model_path/weights/rms1.bin\"), offset=uint64(64)))];\n", DIM, DIM]; - [m appendFormat:@" tensor xn = mul(x=xr,y=rw)[name=string(\"xn\")];\n", DIM, SEQ]; - // --- QKV + SDPA + Wo (operates on xn) --- - [m appendString:@CONV_CONST]; - [m appendFormat:@" tensor Wq = const()[name=string(\"Wq\"), val=tensor(BLOBFILE(path=string(\"@model_path/weights/wq.bin\"), offset=uint64(64)))];\n", DIM,DIM,DIM,DIM]; - [m appendFormat:@" tensor Wk = const()[name=string(\"Wk\"), val=tensor(BLOBFILE(path=string(\"@model_path/weights/wk.bin\"), offset=uint64(64)))];\n", DIM,DIM,DIM,DIM]; - [m appendFormat:@" tensor Wv = const()[name=string(\"Wv\"), val=tensor(BLOBFILE(path=string(\"@model_path/weights/wv.bin\"), offset=uint64(64)))];\n", DIM,DIM,DIM,DIM]; - [m appendFormat:@" tensor Wo = const()[name=string(\"Wo\"), val=tensor(BLOBFILE(path=string(\"@model_path/weights/wo.bin\"), offset=uint64(64)))];\n", DIM,DIM,DIM,DIM]; - [m appendFormat:@" tensor qf = conv(dilations=dl,groups=gr,pad=pd,pad_type=pt,strides=st,weight=Wq,x=xn)[name=string(\"cq\")];\n", DIM,SEQ]; - [m appendFormat:@" tensor kf = conv(dilations=dl,groups=gr,pad=pd,pad_type=pt,strides=st,weight=Wk,x=xn)[name=string(\"ck\")];\n", DIM,SEQ]; - [m appendFormat:@" tensor vf = conv(dilations=dl,groups=gr,pad=pd,pad_type=pt,strides=st,weight=Wv,x=xn)[name=string(\"cv\")];\n", DIM,SEQ]; - [m appendFormat:@" tensor qsh = const()[name=string(\"qsh\"), val=tensor([1,%d,%d,%d])];\n", HEADS,HD,SEQ]; - [m appendString:@" tensor pm = const()[name=string(\"pm\"), val=tensor([0,1,3,2])];\n"]; - [m appendFormat:@" tensor q4 = reshape(shape=qsh,x=qf)[name=string(\"rq\")];\n", HEADS,HD,SEQ]; - [m appendFormat:@" tensor q = transpose(perm=pm,x=q4)[name=string(\"tq\")];\n", HEADS,SEQ,HD]; - [m appendFormat:@" tensor k4 = reshape(shape=qsh,x=kf)[name=string(\"rk\")];\n", HEADS,HD,SEQ]; - [m appendFormat:@" tensor k = transpose(perm=pm,x=k4)[name=string(\"tk\")];\n", HEADS,SEQ,HD]; - [m appendFormat:@" tensor v4 = reshape(shape=qsh,x=vf)[name=string(\"rv\")];\n", HEADS,HD,SEQ]; - [m appendFormat:@" tensor v = transpose(perm=pm,x=v4)[name=string(\"tv\")];\n", HEADS,SEQ,HD]; - [m appendString:@" bool tx = const()[name=string(\"tx\"), val=bool(false)];\n"]; - [m appendString:@" bool ty = const()[name=string(\"ty\"), val=bool(true)];\n"]; - [m appendFormat:@" tensor sc1 = matmul(transpose_x=tx,transpose_y=ty,x=q,y=k)[name=string(\"mm1\")];\n", HEADS,SEQ,SEQ]; - [m appendFormat:@" fp16 scv = const()[name=string(\"scv\"), val=fp16(%f)];\n", sc]; - [m appendFormat:@" tensor sc2 = mul(x=sc1,y=scv)[name=string(\"scl\")];\n", HEADS,SEQ,SEQ]; - [m appendFormat:@" tensor cm = const()[name=string(\"cm\"), val=tensor(BLOBFILE(path=string(\"@model_path/weights/mask.bin\"), offset=uint64(64)))];\n", SEQ,SEQ,SEQ,SEQ]; - [m appendFormat:@" tensor ms = add(x=sc2,y=cm)[name=string(\"msk\")];\n", HEADS,SEQ,SEQ]; - [m appendString:@" int32 sax = const()[name=string(\"sax\"), val=int32(-1)];\n"]; - [m appendFormat:@" tensor aw = softmax(axis=sax,x=ms)[name=string(\"sm\")];\n", HEADS,SEQ,SEQ]; - [m appendFormat:@" tensor a4 = matmul(transpose_x=tx,transpose_y=tx,x=aw,y=v)[name=string(\"mm2\")];\n", HEADS,SEQ,HD]; - [m appendFormat:@" tensor at = transpose(perm=pm,x=a4)[name=string(\"ta\")];\n", HEADS,HD,SEQ]; - [m appendFormat:@" tensor os = const()[name=string(\"os\"), val=tensor([1,%d,1,%d])];\n", DIM,SEQ]; - [m appendFormat:@" tensor af = reshape(shape=os,x=at)[name=string(\"ra\")];\n", DIM,SEQ]; - [m appendFormat:@" tensor oo = conv(dilations=dl,groups=gr,pad=pd,pad_type=pt,strides=st,weight=Wo,x=af)[name=string(\"co\")];\n", DIM,SEQ]; - [m appendString:@" int32 cax = const()[name=string(\"cax\"), val=int32(1)];\n"]; - [m appendString:@" bool cid = const()[name=string(\"cid\"), val=bool(false)];\n"]; - [m appendFormat:@" tensor out = concat(axis=cax,interleave=cid,values=(oo,qf,kf,vf,af,xn))[name=string(\"cat\")];\n", 6*DIM,SEQ]; - [m appendString:@" } -> (out);\n}\n"]; - return m; -} - -// FFN forward + taps: x2 → rmsnorm → FFN → concat(ffn_out, h1, h3, silu_out, x2norm) fp16 -static NSString *gen_ffn_fwd_taps(void) { - float invd = 1.0f/(float)DIM; - NSMutableString *m = [NSMutableString string]; - [m appendString:MIL_HDR]; - [m appendFormat:@" func main(tensor x) {\n", DIM, SEQ]; - // --- RMSNorm: x → xn --- - [m appendFormat:@" tensor sq = mul(x=x,y=x)[name=string(\"sq\")];\n", DIM, SEQ]; - [m appendFormat:@" tensor rax = const()[name=string(\"rax\"), val=tensor([1])];\n"]; - [m appendFormat:@" bool kd = const()[name=string(\"kd\"), val=bool(true)];\n"]; - [m appendFormat:@" tensor ss = reduce_sum(x=sq,axes=rax,keep_dims=kd)[name=string(\"ss\")];\n", SEQ]; - [m appendFormat:@" fp16 invd = const()[name=string(\"invd\"), val=fp16(%f)];\n", invd]; - [m appendFormat:@" tensor ss2 = mul(x=ss,y=invd)[name=string(\"ss2\")];\n", SEQ]; - [m appendFormat:@" fp16 eps = const()[name=string(\"eps\"), val=fp16(0.00001)];\n"]; - [m appendFormat:@" tensor ss3 = add(x=ss2,y=eps)[name=string(\"ss3\")];\n", SEQ]; - [m appendFormat:@" fp16 nhalf = const()[name=string(\"nhalf\"), val=fp16(-0.5)];\n"]; - [m appendFormat:@" tensor rrms = pow(x=ss3,y=nhalf)[name=string(\"rrms\")];\n", SEQ]; - [m appendFormat:@" tensor xr = mul(x=x,y=rrms)[name=string(\"xr\")];\n", DIM, SEQ]; - [m appendFormat:@" tensor rw = const()[name=string(\"rw\"), val=tensor(BLOBFILE(path=string(\"@model_path/weights/rms2.bin\"), offset=uint64(64)))];\n", DIM, DIM]; - [m appendFormat:@" tensor xn = mul(x=xr,y=rw)[name=string(\"xn\")];\n", DIM, SEQ]; - // --- FFN (operates on xn) --- - [m appendString:@CONV_CONST]; - [m appendFormat:@" tensor W1 = const()[name=string(\"W1\"), val=tensor(BLOBFILE(path=string(\"@model_path/weights/w1.bin\"), offset=uint64(64)))];\n", HIDDEN,DIM,HIDDEN,DIM]; - [m appendFormat:@" tensor W3 = const()[name=string(\"W3\"), val=tensor(BLOBFILE(path=string(\"@model_path/weights/w3.bin\"), offset=uint64(64)))];\n", HIDDEN,DIM,HIDDEN,DIM]; - [m appendFormat:@" tensor W2 = const()[name=string(\"W2\"), val=tensor(BLOBFILE(path=string(\"@model_path/weights/w2.bin\"), offset=uint64(64)))];\n", DIM,HIDDEN,DIM,HIDDEN]; - [m appendFormat:@" tensor h1 = conv(dilations=dl,groups=gr,pad=pd,pad_type=pt,strides=st,weight=W1,x=xn)[name=string(\"c1\")];\n", HIDDEN,SEQ]; - [m appendFormat:@" tensor h3 = conv(dilations=dl,groups=gr,pad=pd,pad_type=pt,strides=st,weight=W3,x=xn)[name=string(\"c3\")];\n", HIDDEN,SEQ]; - [m appendFormat:@" tensor sig = sigmoid(x=h1)[name=string(\"sg\")];\n", HIDDEN,SEQ]; - [m appendFormat:@" tensor silu = mul(x=h1,y=sig)[name=string(\"si\")];\n", HIDDEN,SEQ]; - [m appendFormat:@" tensor gate = mul(x=silu,y=h3)[name=string(\"gt\")];\n", HIDDEN,SEQ]; - [m appendFormat:@" tensor y = conv(dilations=dl,groups=gr,pad=pd,pad_type=pt,strides=st,weight=W2,x=gate)[name=string(\"c2\")];\n", DIM,SEQ]; - [m appendString:@" int32 cax = const()[name=string(\"cax\"), val=int32(1)];\n"]; - [m appendString:@" bool cid = const()[name=string(\"cid\"), val=bool(false)];\n"]; - [m appendFormat:@" tensor out = concat(axis=cax,interleave=cid,values=(y,h1,h3,gate,xn))[name=string(\"cat\")];\n", 2*DIM+3*HIDDEN,SEQ]; - [m appendString:@" } -> (out);\n}\n"]; - return m; -} - -// Fused FFN backward: concat(dffn,h1,h3) → concat(dx,dh1,dh3) fp16 -static NSString *gen_ffn_bwd(void) { - NSMutableString *m = [NSMutableString string]; - [m appendString:MIL_HDR]; - [m appendFormat:@" func main(tensor x) {\n", DIM+2*HIDDEN, SEQ]; - [m appendString:@CONV_CONST]; - [m appendString:@" tensor bd = const()[name=string(\"bd\"), val=tensor([0,0,0,0])];\n"]; - [m appendFormat:@" tensor sd = const()[name=string(\"sd\"), val=tensor([1,%d,1,%d])];\n", DIM, SEQ]; - [m appendFormat:@" tensor dffn = slice_by_size(x=x,begin=bd,size=sd)[name=string(\"s0\")];\n", DIM, SEQ]; - [m appendFormat:@" tensor b1 = const()[name=string(\"b1\"), val=tensor([0,%d,0,0])];\n", DIM]; - [m appendFormat:@" tensor s1 = const()[name=string(\"s1\"), val=tensor([1,%d,1,%d])];\n", HIDDEN, SEQ]; - [m appendFormat:@" tensor h1 = slice_by_size(x=x,begin=b1,size=s1)[name=string(\"s1x\")];\n", HIDDEN, SEQ]; - [m appendFormat:@" tensor b3 = const()[name=string(\"b3\"), val=tensor([0,%d,0,0])];\n", DIM+HIDDEN]; - [m appendFormat:@" tensor h3 = slice_by_size(x=x,begin=b3,size=s1)[name=string(\"s3x\")];\n", HIDDEN, SEQ]; - [m appendFormat:@" tensor W2t = const()[name=string(\"W2t\"), val=tensor(BLOBFILE(path=string(\"@model_path/weights/w2t.bin\"), offset=uint64(64)))];\n", HIDDEN, DIM, HIDDEN, DIM]; - [m appendFormat:@" tensor dsilu = conv(dilations=dl,groups=gr,pad=pd,pad_type=pt,strides=st,weight=W2t,x=dffn)[name=string(\"cw2\")];\n", HIDDEN, SEQ]; - [m appendFormat:@" tensor sig = sigmoid(x=h1)[name=string(\"sg\")];\n", HIDDEN, SEQ]; - [m appendString:@" fp16 one = const()[name=string(\"one\"), val=fp16(1.0)];\n"]; - [m appendFormat:@" tensor oms = sub(x=one,y=sig)[name=string(\"oms\")];\n", HIDDEN, SEQ]; - [m appendFormat:@" tensor homs = mul(x=h1,y=oms)[name=string(\"homs\")];\n", HIDDEN, SEQ]; - [m appendFormat:@" tensor brk = add(x=one,y=homs)[name=string(\"brk\")];\n", HIDDEN, SEQ]; - [m appendFormat:@" tensor dsd = mul(x=sig,y=brk)[name=string(\"dsd\")];\n", HIDDEN, SEQ]; - [m appendFormat:@" tensor t1 = mul(x=dsilu,y=h3)[name=string(\"t1\")];\n", HIDDEN, SEQ]; - [m appendFormat:@" tensor dh1 = mul(x=t1,y=dsd)[name=string(\"dh1\")];\n", HIDDEN, SEQ]; - [m appendFormat:@" tensor slh = mul(x=h1,y=sig)[name=string(\"slh\")];\n", HIDDEN, SEQ]; - [m appendFormat:@" tensor dh3 = mul(x=dsilu,y=slh)[name=string(\"dh3\")];\n", HIDDEN, SEQ]; - [m appendFormat:@" tensor W1t = const()[name=string(\"W1t\"), val=tensor(BLOBFILE(path=string(\"@model_path/weights/w1t.bin\"), offset=uint64(64)))];\n", DIM, HIDDEN, DIM, HIDDEN]; - [m appendFormat:@" tensor W3t = const()[name=string(\"W3t\"), val=tensor(BLOBFILE(path=string(\"@model_path/weights/w3t.bin\"), offset=uint64(64)))];\n", DIM, HIDDEN, DIM, HIDDEN]; - [m appendFormat:@" tensor dx1 = conv(dilations=dl,groups=gr,pad=pd,pad_type=pt,strides=st,weight=W1t,x=dh1)[name=string(\"cw1\")];\n", DIM, SEQ]; - [m appendFormat:@" tensor dx3 = conv(dilations=dl,groups=gr,pad=pd,pad_type=pt,strides=st,weight=W3t,x=dh3)[name=string(\"cw3\")];\n", DIM, SEQ]; - [m appendFormat:@" tensor dx = add(x=dx1,y=dx3)[name=string(\"adx\")];\n", DIM, SEQ]; - [m appendString:@" int32 cax = const()[name=string(\"cax\"), val=int32(1)];\n"]; - [m appendString:@" bool cid = const()[name=string(\"cid\"), val=bool(false)];\n"]; - [m appendFormat:@" tensor out = concat(axis=cax,interleave=cid,values=(dx,dh1,dh3))[name=string(\"cat\")];\n", DIM+2*HIDDEN, SEQ]; - [m appendString:@" } -> (out);\n}\n"]; - return m; -} - -// Fused QKV backward: concat(dq,dk,dv) → dx fp16 -static NSString *gen_qkvb(void) { - NSMutableString *m = [NSMutableString string]; - [m appendString:MIL_HDR]; - [m appendFormat:@" func main(tensor x) {\n", 3*DIM, SEQ]; - [m appendString:@CONV_CONST]; - [m appendFormat:@" tensor sz = const()[name=string(\"sz\"), val=tensor([1,%d,1,%d])];\n", DIM, SEQ]; - [m appendString:@" tensor b0 = const()[name=string(\"b0\"), val=tensor([0,0,0,0])];\n"]; - [m appendFormat:@" tensor dq = slice_by_size(x=x,begin=b0,size=sz)[name=string(\"s0\")];\n", DIM,SEQ]; - [m appendFormat:@" tensor b1 = const()[name=string(\"b1\"), val=tensor([0,%d,0,0])];\n", DIM]; - [m appendFormat:@" tensor dk = slice_by_size(x=x,begin=b1,size=sz)[name=string(\"s1\")];\n", DIM,SEQ]; - [m appendFormat:@" tensor b2 = const()[name=string(\"b2\"), val=tensor([0,%d,0,0])];\n", 2*DIM]; - [m appendFormat:@" tensor dv = slice_by_size(x=x,begin=b2,size=sz)[name=string(\"s2\")];\n", DIM,SEQ]; - [m appendFormat:@" tensor Wqt = const()[name=string(\"Wqt\"), val=tensor(BLOBFILE(path=string(\"@model_path/weights/wqt.bin\"), offset=uint64(64)))];\n", DIM,DIM,DIM,DIM]; - [m appendFormat:@" tensor Wkt = const()[name=string(\"Wkt\"), val=tensor(BLOBFILE(path=string(\"@model_path/weights/wkt.bin\"), offset=uint64(64)))];\n", DIM,DIM,DIM,DIM]; - [m appendFormat:@" tensor Wvt = const()[name=string(\"Wvt\"), val=tensor(BLOBFILE(path=string(\"@model_path/weights/wvt.bin\"), offset=uint64(64)))];\n", DIM,DIM,DIM,DIM]; - [m appendFormat:@" tensor dxq = conv(dilations=dl,groups=gr,pad=pd,pad_type=pt,strides=st,weight=Wqt,x=dq)[name=string(\"cq\")];\n", DIM,SEQ]; - [m appendFormat:@" tensor dxk = conv(dilations=dl,groups=gr,pad=pd,pad_type=pt,strides=st,weight=Wkt,x=dk)[name=string(\"ck\")];\n", DIM,SEQ]; - [m appendFormat:@" tensor dxv = conv(dilations=dl,groups=gr,pad=pd,pad_type=pt,strides=st,weight=Wvt,x=dv)[name=string(\"cv\")];\n", DIM,SEQ]; - [m appendFormat:@" tensor dxqk = add(x=dxq,y=dxk)[name=string(\"aqk\")];\n", DIM,SEQ]; - [m appendFormat:@" tensor out = add(x=dxqk,y=dxv)[name=string(\"out\")];\n", DIM,SEQ]; - [m appendString:@" } -> (out);\n}\n"]; - return m; -} - -// SDPA backward part 1 + Wo^T: concat(Q,K,V,dx2) → Wo^T(dx2) → concat(dV, probs_flat, dp_flat) fp16 -// SCORE_CH: channels needed for flattened attention scores [HEADS,SEQ,SEQ] → [HEADS*SEQ, SEQ] -#define SCORE_CH (HEADS*SEQ) - -static NSString *gen_sdpa_bwd1(void) { - float sc = 1.0f/sqrtf((float)HD); - NSMutableString *m = [NSMutableString string]; - [m appendString:MIL_HDR]; - [m appendFormat:@" func main(tensor x) {\n", 4*DIM, SEQ]; - [m appendString:@CONV_CONST]; - [m appendFormat:@" tensor sz = const()[name=string(\"sz\"), val=tensor([1,%d,1,%d])];\n", DIM, SEQ]; - [m appendString:@" tensor b0 = const()[name=string(\"b0\"), val=tensor([0,0,0,0])];\n"]; - [m appendFormat:@" tensor qf = slice_by_size(x=x,begin=b0,size=sz)[name=string(\"s0\")];\n", DIM,SEQ]; - [m appendFormat:@" tensor b1 = const()[name=string(\"b1\"), val=tensor([0,%d,0,0])];\n", DIM]; - [m appendFormat:@" tensor kf = slice_by_size(x=x,begin=b1,size=sz)[name=string(\"s1\")];\n", DIM,SEQ]; - [m appendFormat:@" tensor b2 = const()[name=string(\"b2\"), val=tensor([0,%d,0,0])];\n", 2*DIM]; - [m appendFormat:@" tensor vf = slice_by_size(x=x,begin=b2,size=sz)[name=string(\"s2\")];\n", DIM,SEQ]; - [m appendFormat:@" tensor b3 = const()[name=string(\"b3\"), val=tensor([0,%d,0,0])];\n", 3*DIM]; - [m appendFormat:@" tensor dx2f = slice_by_size(x=x,begin=b3,size=sz)[name=string(\"s3\")];\n", DIM,SEQ]; - // Wo^T backward: dx2 → dattn - [m appendFormat:@" tensor Wot = const()[name=string(\"Wot\"), val=tensor(BLOBFILE(path=string(\"@model_path/weights/wot.bin\"), offset=uint64(64)))];\n", DIM,DIM,DIM,DIM]; - [m appendFormat:@" tensor df = conv(dilations=dl,groups=gr,pad=pd,pad_type=pt,strides=st,weight=Wot,x=dx2f)[name=string(\"cwo\")];\n", DIM,SEQ]; - [m appendFormat:@" tensor rsh = const()[name=string(\"rsh\"), val=tensor([1,%d,%d,%d])];\n", HEADS,HD,SEQ]; - [m appendString:@" tensor pm = const()[name=string(\"pm\"), val=tensor([0,1,3,2])];\n"]; - [m appendFormat:@" tensor qr = reshape(shape=rsh,x=qf)[name=string(\"rq\")];\n", HEADS,HD,SEQ]; - [m appendFormat:@" tensor q = transpose(perm=pm,x=qr)[name=string(\"tq\")];\n", HEADS,SEQ,HD]; - [m appendFormat:@" tensor kr = reshape(shape=rsh,x=kf)[name=string(\"rk\")];\n", HEADS,HD,SEQ]; - [m appendFormat:@" tensor k = transpose(perm=pm,x=kr)[name=string(\"tk\")];\n", HEADS,SEQ,HD]; - [m appendFormat:@" tensor vr = reshape(shape=rsh,x=vf)[name=string(\"rv\")];\n", HEADS,HD,SEQ]; - [m appendFormat:@" tensor v = transpose(perm=pm,x=vr)[name=string(\"tv\")];\n", HEADS,SEQ,HD]; - [m appendFormat:@" tensor dr = reshape(shape=rsh,x=df)[name=string(\"rd\")];\n", HEADS,HD,SEQ]; - [m appendFormat:@" tensor da = transpose(perm=pm,x=dr)[name=string(\"td\")];\n", HEADS,SEQ,HD]; - [m appendString:@" bool bF = const()[name=string(\"bF\"), val=bool(false)];\n"]; - [m appendString:@" bool bT = const()[name=string(\"bT\"), val=bool(true)];\n"]; - [m appendFormat:@" tensor sc1 = matmul(transpose_x=bF,transpose_y=bT,x=q,y=k)[name=string(\"mm1\")];\n", HEADS,SEQ,SEQ]; - [m appendFormat:@" fp16 scv = const()[name=string(\"scv\"), val=fp16(%f)];\n", sc]; - [m appendFormat:@" tensor sc2 = mul(x=sc1,y=scv)[name=string(\"scl\")];\n", HEADS,SEQ,SEQ]; - [m appendFormat:@" tensor cm = const()[name=string(\"cm\"), val=tensor(BLOBFILE(path=string(\"@model_path/weights/mask.bin\"), offset=uint64(64)))];\n", SEQ,SEQ,SEQ,SEQ]; - [m appendFormat:@" tensor ms = add(x=sc2,y=cm)[name=string(\"msk\")];\n", HEADS,SEQ,SEQ]; - [m appendString:@" int32 sax = const()[name=string(\"sax\"), val=int32(-1)];\n"]; - [m appendFormat:@" tensor probs = softmax(axis=sax,x=ms)[name=string(\"sm\")];\n", HEADS,SEQ,SEQ]; - [m appendFormat:@" tensor dv4 = matmul(transpose_x=bT,transpose_y=bF,x=probs,y=da)[name=string(\"dv\")];\n", HEADS,SEQ,HD]; - [m appendFormat:@" tensor dp4 = matmul(transpose_x=bF,transpose_y=bT,x=da,y=v)[name=string(\"dp\")];\n", HEADS,SEQ,SEQ]; - // Flatten dv back to [1,DIM,1,SEQ] - [m appendFormat:@" tensor dvt = transpose(perm=pm,x=dv4)[name=string(\"dvt\")];\n", HEADS,HD,SEQ]; - [m appendFormat:@" tensor dvs = const()[name=string(\"dvs\"), val=tensor([1,%d,1,%d])];\n", DIM,SEQ]; - [m appendFormat:@" tensor dvf = reshape(shape=dvs,x=dvt)[name=string(\"dvf\")];\n", DIM,SEQ]; - // Flatten probs [1,H,S,S] → [1,H*S,1,S] and dp [1,H,S,S] → [1,H*S,1,S] - [m appendFormat:@" tensor scs = const()[name=string(\"scs\"), val=tensor([1,%d,1,%d])];\n", SCORE_CH,SEQ]; - [m appendFormat:@" tensor pf = reshape(shape=scs,x=probs)[name=string(\"pf\")];\n", SCORE_CH,SEQ]; - [m appendFormat:@" tensor dpf = reshape(shape=scs,x=dp4)[name=string(\"dpf\")];\n", SCORE_CH,SEQ]; - [m appendString:@" int32 cax = const()[name=string(\"cax\"), val=int32(1)];\n"]; - [m appendString:@" bool cid = const()[name=string(\"cid\"), val=bool(false)];\n"]; - [m appendFormat:@" tensor out = concat(axis=cax,interleave=cid,values=(dvf,pf,dpf))[name=string(\"cat\")];\n", DIM+2*SCORE_CH,SEQ]; - [m appendString:@" } -> (out);\n}\n"]; - return m; -} - -// SDPA backward part 2: concat(probs[SCORE_CH],dp[SCORE_CH],Q[DIM],K[DIM]) → concat(dQ,dK) fp16 -static NSString *gen_sdpa_bwd2(void) { - float sc = 1.0f/sqrtf((float)HD); - int bwd2_in = 2*SCORE_CH + 2*DIM; - NSMutableString *m = [NSMutableString string]; - [m appendString:MIL_HDR]; - [m appendFormat:@" func main(tensor x) {\n", bwd2_in, SEQ]; - // Slice probs - [m appendFormat:@" tensor sz_sc = const()[name=string(\"szsc\"), val=tensor([1,%d,1,%d])];\n", SCORE_CH, SEQ]; - [m appendString:@" tensor b0 = const()[name=string(\"b0\"), val=tensor([0,0,0,0])];\n"]; - [m appendFormat:@" tensor pf = slice_by_size(x=x,begin=b0,size=sz_sc)[name=string(\"s0\")];\n", SCORE_CH,SEQ]; - // Slice dp - [m appendFormat:@" tensor b1 = const()[name=string(\"b1\"), val=tensor([0,%d,0,0])];\n", SCORE_CH]; - [m appendFormat:@" tensor dpf = slice_by_size(x=x,begin=b1,size=sz_sc)[name=string(\"s1\")];\n", SCORE_CH,SEQ]; - // Slice Q - [m appendFormat:@" tensor sz_d = const()[name=string(\"szd\"), val=tensor([1,%d,1,%d])];\n", DIM, SEQ]; - [m appendFormat:@" tensor b2 = const()[name=string(\"b2\"), val=tensor([0,%d,0,0])];\n", 2*SCORE_CH]; - [m appendFormat:@" tensor qf = slice_by_size(x=x,begin=b2,size=sz_d)[name=string(\"s2\")];\n", DIM,SEQ]; - // Slice K - [m appendFormat:@" tensor b3 = const()[name=string(\"b3\"), val=tensor([0,%d,0,0])];\n", 2*SCORE_CH+DIM]; - [m appendFormat:@" tensor kf = slice_by_size(x=x,begin=b3,size=sz_d)[name=string(\"s3\")];\n", DIM,SEQ]; - // Reshape to multi-head - [m appendFormat:@" tensor ssh = const()[name=string(\"ssh\"), val=tensor([1,%d,%d,%d])];\n", HEADS,SEQ,SEQ]; - [m appendFormat:@" tensor probs = reshape(shape=ssh,x=pf)[name=string(\"rp\")];\n", HEADS,SEQ,SEQ]; - [m appendFormat:@" tensor dp = reshape(shape=ssh,x=dpf)[name=string(\"rdp\")];\n", HEADS,SEQ,SEQ]; - [m appendFormat:@" tensor rsh = const()[name=string(\"rsh\"), val=tensor([1,%d,%d,%d])];\n", HEADS,HD,SEQ]; - [m appendString:@" tensor pm = const()[name=string(\"pm\"), val=tensor([0,1,3,2])];\n"]; - [m appendFormat:@" tensor qr = reshape(shape=rsh,x=qf)[name=string(\"rq\")];\n", HEADS,HD,SEQ]; - [m appendFormat:@" tensor q = transpose(perm=pm,x=qr)[name=string(\"tq\")];\n", HEADS,SEQ,HD]; - [m appendFormat:@" tensor kr = reshape(shape=rsh,x=kf)[name=string(\"rk\")];\n", HEADS,HD,SEQ]; - [m appendFormat:@" tensor k = transpose(perm=pm,x=kr)[name=string(\"tk\")];\n", HEADS,SEQ,HD]; - // Softmax grad: ds = probs * (dp - sum(probs*dp)) * scale - [m appendFormat:@" tensor pdp = mul(x=probs,y=dp)[name=string(\"pdp\")];\n", HEADS,SEQ,SEQ]; - [m appendString:@" tensor rax = const()[name=string(\"rax\"), val=tensor([-1])];\n"]; - [m appendString:@" bool kd = const()[name=string(\"kd\"), val=bool(true)];\n"]; - [m appendFormat:@" tensor spdp = reduce_sum(x=pdp,axes=rax,keep_dims=kd)[name=string(\"rs\")];\n", HEADS,SEQ]; - [m appendFormat:@" tensor dps = sub(x=dp,y=spdp)[name=string(\"dps\")];\n", HEADS,SEQ,SEQ]; - [m appendFormat:@" tensor ds0 = mul(x=probs,y=dps)[name=string(\"ds0\")];\n", HEADS,SEQ,SEQ]; - [m appendFormat:@" fp16 scv = const()[name=string(\"scv\"), val=fp16(%f)];\n", sc]; - [m appendFormat:@" tensor ds = mul(x=ds0,y=scv)[name=string(\"ds\")];\n", HEADS,SEQ,SEQ]; - [m appendString:@" bool bF = const()[name=string(\"bF\"), val=bool(false)];\n"]; - [m appendString:@" bool bT = const()[name=string(\"bT\"), val=bool(true)];\n"]; - [m appendFormat:@" tensor dq4 = matmul(transpose_x=bF,transpose_y=bF,x=ds,y=k)[name=string(\"dq\")];\n", HEADS,SEQ,HD]; - [m appendFormat:@" tensor dk4 = matmul(transpose_x=bT,transpose_y=bF,x=ds,y=q)[name=string(\"dk\")];\n", HEADS,SEQ,HD]; - [m appendFormat:@" tensor dqt = transpose(perm=pm,x=dq4)[name=string(\"dqt\")];\n", HEADS,HD,SEQ]; - [m appendFormat:@" tensor dkt = transpose(perm=pm,x=dk4)[name=string(\"dkt\")];\n", HEADS,HD,SEQ]; - [m appendFormat:@" tensor fs = const()[name=string(\"fs\"), val=tensor([1,%d,1,%d])];\n", DIM,SEQ]; - [m appendFormat:@" tensor dqf = reshape(shape=fs,x=dqt)[name=string(\"dqf\")];\n", DIM,SEQ]; - [m appendFormat:@" tensor dkf = reshape(shape=fs,x=dkt)[name=string(\"dkf\")];\n", DIM,SEQ]; - [m appendString:@" int32 cax = const()[name=string(\"cax\"), val=int32(1)];\n"]; - [m appendString:@" bool cid = const()[name=string(\"cid\"), val=bool(false)];\n"]; - [m appendFormat:@" tensor out = concat(axis=cax,interleave=cid,values=(dqf,dkf))[name=string(\"cat\")];\n", 2*DIM,SEQ]; - [m appendString:@" } -> (out);\n}\n"]; - return m; -} - -// ===== Weight builders ===== -static NSData *g_mask_blob = nil; -static NSData *get_mask_blob(void) { - if (!g_mask_blob) { - _Float16 *mask = (_Float16*)calloc(SEQ*SEQ, sizeof(_Float16)); - for(int t=0;t