| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| export let D_MODEL, HEADS, HEAD_DIM, FFN, VOCAB; |
| export let ENC_LAYERS, DEC_LAYERS; |
| export let MAX_POS, SRC_CAP, DECODE_CAP; |
| |
| |
| export let SCORES_CAP; |
| export let EOS, PAD, DECODER_START; |
| export let LN_EPS; |
| export let EMBED_SCALE; |
| export let ATTN_SCALE; |
| export let BITMASK_WORDS; |
|
|
| |
| |
| export const REP_PENALTY = 1.2; |
|
|
| |
| |
| |
| |
| |
| const INT_FIELDS = [ |
| 'dModel', 'heads', 'headDim', 'ffn', 'encLayers', 'decLayers', |
| 'vocab', 'maxPos', 'srcCap', 'decodeCap', 'eos', 'pad', 'decoderStart', |
| ]; |
|
|
| export function parseModelConfig(model) { |
| if (!model || typeof model !== 'object') throw new Error('model config: not an object'); |
| const m = {}; |
| for (const f of INT_FIELDS) { |
| const v = model[f]; |
| if (!Number.isInteger(v) || v < 0) { |
| throw new Error(`model config: ${f} must be a non-negative integer, got ${v}`); |
| } |
| m[f] = v; |
| } |
| for (const f of INT_FIELDS.slice(0, 10)) { |
| if (m[f] === 0) throw new Error(`model config: ${f} must be positive`); |
| } |
| if (!(typeof model.lnEps === 'number' && model.lnEps > 0 && model.lnEps < 0.1)) { |
| throw new Error(`model config: lnEps must be in (0, 0.1), got ${model.lnEps}`); |
| } |
| m.lnEps = model.lnEps; |
| if (m.heads * m.headDim !== m.dModel) { |
| throw new Error(`model config: heads·headDim = ${m.heads * m.headDim} != dModel ${m.dModel}`); |
| } |
| for (const f of ['dModel', 'headDim', 'ffn']) { |
| if (m[f] % 4 !== 0) { |
| throw new Error(`model config: ${f} = ${m[f]} must be a multiple of 4 (vec4 kernels)`); |
| } |
| } |
| if (m.srcCap % 32 !== 0) { |
| throw new Error(`model config: srcCap ${m.srcCap} must be a multiple of 32 (bucketFor invariant)`); |
| } |
| if (m.srcCap > m.maxPos || m.decodeCap > m.maxPos) { |
| throw new Error( |
| `model config: srcCap ${m.srcCap} / decodeCap ${m.decodeCap} must not exceed maxPos ${m.maxPos}`); |
| } |
| for (const f of ['eos', 'pad', 'decoderStart']) { |
| if (m[f] >= m.vocab) throw new Error(`model config: ${f} = ${m[f]} out of vocab ${m.vocab}`); |
| } |
| |
| if (model.scaleEmbedding === false) m.scaleEmbedding = false; |
| m.embedScale = m.scaleEmbedding === false ? 1 : Math.sqrt(m.dModel); |
| return m; |
| } |
|
|
| let ACTIVE = null; |
|
|
| |
| |
| export function activeModelConfig() { |
| return { ...ACTIVE }; |
| } |
|
|
| |
| |
| export function applyModelConfig(model) { |
| const m = parseModelConfig(model); |
| ACTIVE = m; |
| D_MODEL = m.dModel; HEADS = m.heads; HEAD_DIM = m.headDim; FFN = m.ffn; VOCAB = m.vocab; |
| ENC_LAYERS = m.encLayers; DEC_LAYERS = m.decLayers; |
| MAX_POS = m.maxPos; SRC_CAP = m.srcCap; DECODE_CAP = m.decodeCap; |
| SCORES_CAP = Math.ceil(Math.max(m.srcCap, m.decodeCap) / 32) * 32 + 32; |
| EOS = m.eos; PAD = m.pad; DECODER_START = m.decoderStart; |
| LN_EPS = m.lnEps; |
| EMBED_SCALE = m.embedScale; |
| ATTN_SCALE = 1 / Math.sqrt(m.headDim); |
| BITMASK_WORDS = Math.ceil(m.vocab / 32); |
| return m; |
| } |
|
|
| |
| |
| export function assertModelActive(model, what = 'weights') { |
| const m = model ?? {}; |
| const stale = |
| m.dModel !== D_MODEL || m.heads !== HEADS || m.headDim !== HEAD_DIM || |
| m.ffn !== FFN || m.vocab !== VOCAB || |
| m.encLayers !== ENC_LAYERS || m.decLayers !== DEC_LAYERS || |
| m.maxPos !== MAX_POS || m.srcCap !== SRC_CAP || m.decodeCap !== DECODE_CAP || |
| m.eos !== EOS || m.pad !== PAD || m.decoderStart !== DECODER_START; |
| if (stale) { |
| throw new Error( |
| `${what} belong to a different model config than the active one — ` + |
| 'call applyModelConfig(weights.model) (loadWeights/uploadParsed do it) before dispatching'); |
| } |
| } |
|
|
| |
| |
| |
| applyModelConfig({ |
| dModel: 448, heads: 8, headDim: 56, ffn: 1792, |
| encLayers: 8, decLayers: 2, |
| vocab: 24000, maxPos: 512, srcCap: 320, decodeCap: 224, |
| eos: 2, pad: 0, decoderStart: 0, lnEps: 1e-5, |
| }); |
|
|