kronos-mql5/Include/Kronos/KronosPredictorS2.mqh

140 lines
6.1 KiB
MQL5
Raw Permalink Normal View History

2026-07-05 05:03:49 +00:00
//+------------------------------------------------------------------+
//| KronosPredictorS2.mqh |
//| MMQ — Muhammad Minhas Qamar |
//| www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "MMQ — Muhammad Minhas Qamar"
#property link "https://www.mql5.com"
#property version "1.00"
#ifndef KRONOS_PREDICTOR_S2_MQH
#define KRONOS_PREDICTOR_S2_MQH
#include <Kronos\KronosTokenizerMath.mqh> // loaders
#include <Kronos\KronosTransformerCore.mqh> // CrossMHA, RMSNorm, LinearT, AddRowBias
#ifndef KR_PRED_VOCAB
#define KR_PRED_VOCAB 1024
#endif
#define KR_DEP_N_HEADS 4 // dep_layer cross-attn heads (source default)
//+------------------------------------------------------------------+
//| Predictor, decode_s2 stage. |
//+------------------------------------------------------------------+
class CKronosPredictorS2
{
protected:
ulong m_dm; // 512
string m_dir;
matrix m_embS1; // emb_s1.weight : [vocab, d_model] (shared table)
//--- dep_layer cross-attention (n_heads = 4)
matrix m_cWq;
vector m_cBq;
matrix m_cWk;
vector m_cBk;
matrix m_cWv;
vector m_cBv;
matrix m_cWo;
vector m_cBo;
vector m_depNormW; // dep_layer.norm.weight
//--- s2 head
matrix m_projS2W;
vector m_projS2B; // head.proj_s2 : [vocab, d_model]
string F(const string name) { return m_dir + name + ".bin"; }
public:
//+---------------------------------------------------------------+
//| Load the shared emb_s1 table, the dep_layer cross-attention |
//| weights (n_heads=4) and the s2 head. |
//+---------------------------------------------------------------+
bool Init(const string weight_dir, ulong d_model)
{
m_dir = weight_dir;
m_dm = d_model;
if(d_model == 0)
{
Print("CKronosPredictorS2::Init: d_model=0");
return false;
}
bool ok = true;
//--- emb_s1 is a lookup table (sibling rows by id) -> keep row-major
ok &= KronosLoadMatrix(F("embedding_emb_s1_weight"), KR_PRED_VOCAB, m_dm, m_embS1);
//--- cross-attn q/k/v/out and the s2 head are LinearT weights -> transposed
ok &= KronosLoadMatrixT(F("dep_layer_cross_attn_q_proj_weight"), m_dm, m_dm, m_cWq);
ok &= KronosLoadVector(F("dep_layer_cross_attn_q_proj_bias"), m_dm, m_cBq);
ok &= KronosLoadMatrixT(F("dep_layer_cross_attn_k_proj_weight"), m_dm, m_dm, m_cWk);
ok &= KronosLoadVector(F("dep_layer_cross_attn_k_proj_bias"), m_dm, m_cBk);
ok &= KronosLoadMatrixT(F("dep_layer_cross_attn_v_proj_weight"), m_dm, m_dm, m_cWv);
ok &= KronosLoadVector(F("dep_layer_cross_attn_v_proj_bias"), m_dm, m_cBv);
ok &= KronosLoadMatrixT(F("dep_layer_cross_attn_out_proj_weight"), m_dm, m_dm, m_cWo);
ok &= KronosLoadVector(F("dep_layer_cross_attn_out_proj_bias"), m_dm, m_cBo);
ok &= KronosLoadVector(F("dep_layer_norm_weight"), m_dm, m_depNormW);
ok &= KronosLoadMatrixT(F("head_proj_s2_weight"), KR_PRED_VOCAB, m_dm, m_projS2W);
ok &= KronosLoadVector(F("head_proj_s2_bias"), KR_PRED_VOCAB, m_projS2B);
if(!ok)
{
Print("CKronosPredictorS2::Init: load failed");
return false;
}
return true;
}
//+---------------------------------------------------------------+
//| decode_s2: context (L, d_model) from decode_s1 plus the chosen|
//| s1 ids. Returns s2_logits (L, 1024). |
//| |
//| s1_ids may have length L (one sibling per position) or length |
//| 1 (a single sibling broadcast across all L context rows). The |
//| length-1 case mirrors the reference capture (s1_pick = argmax |
//| of the last step), where PyTorch broadcasts (L,d)+(1,d) and |
//| the single query sits at RoPE position 0. |
//+---------------------------------------------------------------+
bool DecodeS2(const matrix &context, const int &s1_ids[], matrix &s2_logits)
{
ulong L = context.Rows();
int Q = ArraySize(s1_ids);
if(L == 0 || (Q != (int)L && Q != 1))
{ PrintFormat("DecodeS2: length mismatch (ids %d, ctx %I64u)", Q, L); return false; }
//--- sibling_embed = raw emb_s1 table rows (NO sqrt(d) scale here).
//--- Q rows: either L (per-position) or 1 (broadcast).
matrix sib = matrix::Zeros((ulong)Q, m_dm);
for(int t = 0; t < Q; t++)
{
ulong id = (ulong)s1_ids[t];
for(ulong j = 0; j < m_dm; j++)
sib[t][j] = m_embS1[id][j];
}
//--- cross-attention: q = sibling_embed (Q rows), k/v = context (L rows),
//--- n_heads=4, non-causal. attn has Q rows.
matrix attn = CrossMHA(sib, context,
m_cWq, m_cBq, m_cWk, m_cBk,
m_cWv, m_cBv, m_cWo, m_cBo,
KR_DEP_N_HEADS);
//--- dep_layer: RMSNorm(context + attn). Broadcast attn row 0 across
//--- all L context rows when Q == 1 (matches PyTorch (L,d)+(1,d)).
matrix sum = matrix::Zeros(L, m_dm);
for(ulong i = 0; i < L; i++)
{
ulong ar = (Q == 1) ? 0 : i;
for(ulong j = 0; j < m_dm; j++)
sum[i][j] = context[i][j] + attn[ar][j];
}
matrix x2 = RMSNorm(sum, m_depNormW);
//--- s2 head: d_model -> 1024
s2_logits = LinearT(x2, m_projS2W);
AddRowBias(s2_logits, m_projS2B);
return true;
}
};
#endif // KRONOS_PREDICTOR_S2_MQH
//+------------------------------------------------------------------+