// SPDX-License-Identifier: Apache-2.0 ; specs/widgets/nn-attention.t27 -- scaled dot-product attention with two heads and a causal mask ; Source of truth for public/widgets/nn-attention/ (gHashTag/trinity): scripts/widget-pages-from-spec.mjs ; compiles this file, checks its constants, evaluates the tests below and writes the page; the page ; hands every constant to tool.js as window.T27_WIDGET. tool.js holds no English and NO FORMULA of ; its own: every value it draws comes from public/widgets/nn-attention/logic.js -- the fn bodies of ; gHashTag/t27 specs/ml/recurrent/attention_mechanism.t27 compiled to wasm, beside its copy logic.t27. ; Lesson 24 of the course neural-networks. ASCII only (L3), English only (LANG-EN). ; Numbers are integers (the schema reads no float): a K_ name ending in _C is in hundredths. ; WHAT IT DOES: the reader toggles the causal mask and taps a query row. Every weight is ; forward_at(causal, 1, i) and every output forward_at(causal, 0, i) of the spec: 3 positions, ; d_model 4, 2 heads, its own q, k and v. ; WHAT IT DOES NOT CLAIM: q, k and v are the spec's fixture, not learned projections of real tokens. ; phi^2 + 1/phi^2 = 3 | TRINITY module widget_nn_attention; pub const KIND : str = "widget-tool"; pub const ID : str = "nn-attention"; pub const TITLE : str = "Attention: how a word decides which other words to read"; pub const DESCRIPTION : str = "Two heads of attention over three positions: each row is how much one query reads each key. Switch on the causal mask and a position can no longer read the future."; pub const IMAGE_ALT : str = "Attention card, causal: two 3 x 3 grids in blue to white. Head 0 reads 1.000 / 0.452 0.548 / 0.230 0.321 0.449 with the upper triangle at 0; head 1 1.000 / 0.434 0.566 / 0.210 0.316 0.474. Row 2 is outlined. Below, a 3 x 4 output grid; position 2 outputs 0.59 0.69 0.81 0.91."; pub const HOOK : str = "Causal: position 0 reads only itself (1.000), position 1 splits 0.45 / 0.55, position 2 spreads 0.23, 0.32, 0.45. Every row is a softmax."; pub const CATEGORY : str = "compiler"; pub const DATA_SOURCES : [2]str = ["gHashTag/t27 specs/ml/recurrent/attention_mechanism.t27 (t27 master 239b4b059): every value on the page is its fn bodies, compiled to wasm (logic.js)", "Course 10 of t27.ai, neural-networks (gHashTag/trinity#1695): lesson 24 opens this widget"]; pub const READS_LOCAL_FILES : bool = false; pub const SENDS_NOTHING : bool = true; pub const K_CARD_WIDTH : u16 = 1200; pub const K_CARD_HEIGHT : u16 = 630; ; --- Numbers: ranges, presets, and fixtures the spec holds (held equal by the tests below) pub const K_SEQ : u8 = 3; pub const K_D_MODEL : u8 = 4; pub const K_HEADS : u8 = 2; pub const K_PRESET_CAUSAL : [3]u8 = [1, 0, 1]; pub const K_PRESET_Q : [3]u8 = [2, 2, 1]; pub const K_START_PRESET : u8 = 0; ; --- Words on screen ------------------------------------------------------------------------ pub const SAY_PRESETS_LABEL : str = "Start from"; pub const SAY_PRESET_NAMES : [3]str = ["Causal, position 2", "Full, position 2", "Causal, position 1"]; pub const SAY_ASK : str = "How does a word decide which other words to read?"; pub const SAY_CAUSAL : str = "causal mask"; pub const SAY_HEAD : str = "head"; pub const SAY_POS : str = "pos"; pub const SAY_DIM : str = "dim"; pub const SAY_ROWS : str = "rows are queries, columns keys"; pub const SAY_TAP : str = "Tap a row to follow one query."; pub const SAY_OUT_LABEL : str = "The output: each position's weighted average of the values"; pub const SAY_MASK : str = "mask"; pub const SAY_MASK_ON : str = "no reading ahead"; pub const SAY_MASK_OFF : str = "every key"; pub const SAY_OUT_ROW : str = "output of {0}"; pub const SAY_NOTE : str = "Each weight is softmax(q . k / sqrt(d)) over the keys of its row; the output is the values averaged by those weights. Two heads look at different halves of the vector."; pub const SAY_SOURCE : str = "Logic: t27 spec ml/recurrent/attention_mechanism.t27, compiled"; test the_card_is_the_share_size { assert K_CARD_WIDTH == 1200; assert K_CARD_HEIGHT == 630; assert K_CARD_WIDTH * 21 == K_CARD_HEIGHT * 40; } test two_heads_split_the_model { assert K_HEADS * 2 == K_D_MODEL; assert K_SEQ == 3; } test the_presets_stay_in_range { assert K_PRESET_Q[0] < K_SEQ; assert K_PRESET_CAUSAL[0] == 1; assert K_PRESET_CAUSAL[1] == 0; }