v0.9.0: análise/prova/teste das críticas Qwen3.8 lote 2 (r-34) — SIMD com despacho em runtime bit-a-bit (X-1), oráculo candle_nn::optim::AdamW (nosso 1,53× mais rápido), Thompson Sampling (regret 0,0019 vs UCB1 0,0156), GNG por entropia com piso (T-G1'), JEPA de fração adaptativa (bit-a-bit com 1 braço), configs tipadas, entrada numpy (2008× na fronteira), avaliação com dados reais — 104/104 gates, 111/111 cargo
d444358 verified Download src/tokenizador.rs from PowerMachine/khtst-rust: direct link, hf CLI and curl.
- Browser
- Download file 19.2 kB
-
https://huggingface.co/PowerMachine/khtst-rust/resolve/main/src/tokenizador.rs
- Command line
-
hf download hf://PowerMachine/khtst-rust/src/tokenizador.rs
-
curl -L -o tokenizador.rs https://huggingface.co/PowerMachine/khtst-rust/resolve/main/src/tokenizador.rs
19.2 kB
| //! Tokenizador de conceitos NCM (Estágio 1 / fase A) — port de | |
| //! `conceitos/tokenizador_conceitos.py` (n-04 §2.3) com a síntese GSQ+RCO | |
| //! (Teo D.4): codebooks Stiefel por nível, seleção GSQ (gradiente exato), | |
| //! nested dropout (Teo A.2), commitment (Teo A.0) e reconstrução por CE | |
| //! token-level no vocabulário. | |
| //! | |
| //! Recursão residual (n-02 §0): | |
| //! z_1 = Interp_{n_1}(E(x)); r_ℓ = Quant_{C_ℓ}(z_ℓ); | |
| //! z_{ℓ+1} = Interp_{n_{ℓ+1}}(z_ℓ) − Interp_{n_{ℓ+1}}(r_ℓ); | |
| //! ẑ_ℓ = Σ_{j≤ℓ} Interp_{n_ℓ}(r_j) (reconstrução cumulativa) | |
| use candle_core::{Device, Result, Tensor, Var, D}; | |
| use crate::atencao::BlocoAdba; | |
| use crate::camadas::{Embedding, Linear, RmsNorm}; | |
| use crate::config::ConfigKhtst; | |
| use crate::mascaras::{interp_para, matriz_interp}; | |
| use crate::quantizador::{ModoQuant, QuantizadorNivel}; | |
| use rand::prelude::*; | |
| pub struct TokenizadorConceitos { | |
| // Encoder (treino apenas; atenção plena) | |
| pub enc_emb: Embedding, | |
| pub enc_blocos: Vec<BlocoAdba>, | |
| pub enc_norm: RmsNorm, | |
| // Projeções por nível d_modelo → d_lat + quantizadores Stiefel/GSQ | |
| pub proj_niveis: Vec<Linear>, | |
| pub quants: Vec<QuantizadorNivel>, | |
| // Decoder AR condicionado em ẑ (B.1: p_ψ(x|c) = Π_i p(x_i|x_{<i}, ẑ)) | |
| pub dec_proj: Linear, | |
| pub dec_emb: Embedding, | |
| pub dec_blocos: Vec<BlocoAdba>, | |
| pub dec_norm: RmsNorm, | |
| pub dec_head: Linear, | |
| // máscaras pré-computadas | |
| pub mascara_causal_dec: Tensor, | |
| pub niveis: Vec<usize>, | |
| pub d_lat: usize, | |
| pub vocab: usize, | |
| pub cfg_ncm_beta_commit: f64, | |
| pub modo: ModoQuant, | |
| } | |
| /// Saída da codificação: códigos por nível + reconstruções cumulativas. | |
| pub struct Codigos { | |
| pub ids: Vec<Tensor>, // u32 (b, n_ℓ) | |
| pub zhats: Vec<Tensor>, // (b, n_ℓ, d_lat) — ẑ_ℓ na granularidade do nível | |
| } | |
| impl TokenizadorConceitos { | |
| pub fn novo(cfg: &ConfigKhtst, dev: &Device) -> Result<Self> { | |
| let m = &cfg.modelo; | |
| let n = &cfg.ncm; | |
| let l = n.niveis.len(); | |
| let n_total_dec = m.comprimento_ctx; | |
| let mascara_causal_dec = causal_aditiva(n_total_dec, dev)?; | |
| let mut enc_blocos = Vec::new(); | |
| let mut dec_blocos = Vec::new(); | |
| for _ in 0..2 { | |
| enc_blocos.push(BlocoAdba::novo(m.d_modelo, m.n_cabecas, m.d_ff, l, false, false, false, dev)?); | |
| dec_blocos.push(BlocoAdba::novo(m.d_modelo, m.n_cabecas, m.d_ff, l, false, false, false, dev)?); | |
| } | |
| let modo = ModoQuant::de_str(&n.modo_quantizacao); | |
| let mut quants = Vec::with_capacity(l); | |
| for _ in 0..l { | |
| quants.push(QuantizadorNivel::novo(n.k_por_nivel, n.d_lat, modo, n.ema_codebook, &n.escala_r, dev)?); | |
| } | |
| let mut proj = Vec::with_capacity(l); | |
| for _ in 0..l { | |
| proj.push(Linear::sem_vies(m.d_modelo, n.d_lat, 0.02, dev)?); | |
| } | |
| Ok(TokenizadorConceitos { | |
| enc_emb: Embedding::novo(m.vocab, m.d_modelo, dev)?, | |
| enc_blocos, | |
| enc_norm: RmsNorm::novo(m.d_modelo, 1e-5, dev)?, | |
| proj_niveis: proj, | |
| quants, | |
| dec_proj: Linear::sem_vies(n.d_lat, m.d_modelo, 0.02, dev)?, | |
| dec_emb: Embedding::novo(m.vocab, m.d_modelo, dev)?, | |
| dec_blocos, | |
| dec_norm: RmsNorm::novo(m.d_modelo, 1e-5, dev)?, | |
| dec_head: Linear::sem_vies(m.d_modelo, m.vocab, 0.02, dev)?, | |
| mascara_causal_dec, | |
| niveis: n.niveis.clone(), | |
| d_lat: n.d_lat, | |
| vocab: m.vocab, | |
| cfg_ncm_beta_commit: n.beta_commit, | |
| modo, | |
| }) | |
| } | |
| /// Encoder: x (b, N) → z (b, N, d_modelo). Atenção plena (só treino). | |
| pub fn encoder(&self, ids: &Tensor) -> Result<Tensor> { | |
| let mut x = self.enc_emb.fwd(ids)?; | |
| for bloco in &self.enc_blocos { | |
| let mut cache = Default::default(); | |
| x = bloco.fwd(&x, None, &mut cache, None, None)?; | |
| } | |
| self.enc_norm.fwd(&x) | |
| } | |
| /// Codificação de TREINO (fase A): relaxamento GSQ suave diferenciável. | |
| /// Devolve (zhats_suaves, zs_por_nivel) para a perda e telemetria. | |
| fn codificar_treino(&mut self, ids: &Tensor) -> Result<(Vec<Tensor>, Vec<Tensor>)> { | |
| let dev = ids.device(); | |
| let z = self.encoder(ids)?; | |
| let mut zs = Vec::with_capacity(self.niveis.len()); | |
| let mut zhats = Vec::with_capacity(self.niveis.len()); | |
| // projeção ÚNICA d_modelo → d_lat na granularidade do nível 1 (n-02 §0); | |
| // a recursão residual vive INTEIRAMENTE em d_lat | |
| let n1 = self.niveis[0]; | |
| let mut z_corrente = self.proj_niveis[0].fwd(&interp_para(&z, n1, dev)?)?; | |
| let mut zhat_acum: Option<Tensor> = None; | |
| for (i, &n_l) in self.niveis.iter().enumerate() { | |
| let zp = z_corrente.clone(); | |
| let r_suave = self.quants[i].quantizar_treino(&zp)?; | |
| // telemetria + escalas + uso (sem gradiente) | |
| let (ids_duros, _valores) = self.quants[i].quantizar_duro(&zp)?; | |
| self.quants[i].registrar_uso(&ids_duros)?; | |
| self.quants[i].atualizar_escala(&zp)?; | |
| // reconstrução cumulativa na granularidade n_l | |
| let zhat_l = match &zhat_acum { | |
| Some(a) => (&interp_para(a, n_l, dev)? + &r_suave)?, | |
| None => r_suave.clone(), | |
| }; | |
| zhat_acum = Some(zhat_l.clone()); | |
| zhats.push(zhat_l); | |
| // resíduo na granularidade do próximo nível (Teo A.0/D.4) | |
| if i + 1 < self.niveis.len() { | |
| let n_prox = self.niveis[i + 1]; | |
| let res = (&zp - &r_suave)?; | |
| z_corrente = interp_para(&res, n_prox, dev)?; | |
| } | |
| zs.push(zp); | |
| } | |
| Ok((zhats, zs)) | |
| } | |
| /// Codificação CONGELADA (fase B / inferência): códigos DUROS + ẑ dos | |
| /// códigos (T determinístico — hipótese do Teo B.1). Recursão residual | |
| /// inteiramente em d_lat (projeção única — n-02 §0). | |
| pub fn codificar_congelado(&self, ids: &Tensor) -> Result<Codigos> { | |
| { | |
| let dev = ids.device(); | |
| let z = self.encoder(ids)?.detach(); | |
| let mut saida_ids = Vec::new(); | |
| let mut zhats = Vec::new(); | |
| let n1 = self.niveis[0]; | |
| let mut z_corrente = self.proj_niveis[0].fwd(&interp_para(&z, n1, dev)?)?; | |
| let mut zhat_acum: Option<Tensor> = None; | |
| for (i, &n_l) in self.niveis.iter().enumerate() { | |
| let zp = z_corrente.clone(); | |
| let (ids_l, valores_l) = self.quants[i].quantizar_duro(&zp)?; | |
| saida_ids.push(ids_l.reshape((zp.dims()[0], n_l))?); | |
| let zhat_l = match &zhat_acum { | |
| Some(a) => (&interp_para(a, n_l, dev)? + &valores_l)?, | |
| None => valores_l.clone(), | |
| }; | |
| zhat_acum = Some(zhat_l.clone()); | |
| zhats.push(zhat_l); | |
| if i + 1 < self.niveis.len() { | |
| let n_prox = self.niveis[i + 1]; | |
| let res = (&zp - &valores_l)?; | |
| z_corrente = interp_para(&res, n_prox, dev)?; | |
| } | |
| } | |
| Ok(Codigos { ids: saida_ids, zhats }) | |
| } | |
| } | |
| /// Reconstrói ẑ a partir de códigos (para corrupção κ e amostragem — | |
| /// o gerador nunca vê os vetores contínuos, apenas c (C.3)). | |
| pub fn reconstruir_de_codigos(&self, ids: &[Tensor], ate_nivel: usize, b: usize, dev: &Device) -> Result<Vec<Tensor>> { | |
| { | |
| let mut zhats = Vec::new(); | |
| let mut zhat_acum: Option<Tensor> = None; | |
| for (i, item) in self.niveis.iter().enumerate().take(ate_nivel.min(self.niveis.len())) { | |
| let plano = ids[i].reshape((b, self.niveis[i]))?; | |
| let valores = self.quants[i].materializar(&plano.flatten_all()?)?; | |
| let valores = valores.reshape((b, self.niveis[i], self.d_lat))?; | |
| let zhat_l = match &zhat_acum { | |
| Some(a) => (&interp_para(a, *item, dev)? + &valores)?, | |
| None => valores.clone(), | |
| }; | |
| zhat_acum = Some(zhat_l.clone()); | |
| zhats.push(zhat_l); | |
| } | |
| Ok(zhats) | |
| } | |
| } | |
| /// Decoder AR condicionado: ẑ (b, n_ℓ, d_lat) → logits (b, N, vocab). | |
| /// Entrada = proj(upsample_N(ẑ)) + emb(tokens deslocados). | |
| pub fn decoder(&self, zhat: &Tensor, alvo: Option<&Tensor>) -> Result<Tensor> { | |
| let dev = zhat.device(); | |
| let b = zhat.dim(0)?; | |
| let n_z = zhat.dim(1)?; | |
| let n_total = self.mascara_causal_dec.dim(0)?; | |
| let up = interp_para(zhat, n_total, dev)?; | |
| let cond = self.dec_proj.fwd(&up)?; | |
| let entrada_tokens: Tensor = match alvo { | |
| Some(t) => { | |
| // desloca à direita com BOS | |
| let bos = Tensor::full(crate::tokens::BOS as f32, (b, 1), dev)?.to_dtype(candle_core::DType::U32)?; | |
| let corpo = t.narrow(1, 0, t.dim(1)? - 1)?; | |
| Tensor::cat(&[&bos, &corpo], 1)? | |
| } | |
| None => Tensor::full(crate::tokens::BOS as f32, (b, n_total), dev)?.to_dtype(candle_core::DType::U32)?, | |
| }; | |
| let mut x = self.dec_emb.fwd(&entrada_tokens)?; | |
| x = (&x + &cond)?; | |
| for bloco in &self.dec_blocos { | |
| let mut cache = Default::default(); | |
| x = bloco.fwd(&x, Some(&self.mascara_causal_dec), &mut cache, None, None)?; | |
| } | |
| let x = self.dec_norm.fwd(&x)?; | |
| self.dec_head.fwd(&x) | |
| } | |
| /// Perda da fase A (n-04 §2.3): nested dropout + commitment. | |
| /// p(ℓ) uniforme (Teo A.2); RNG DEDICADO (pareamento — Teo 31.7). | |
| /// Devolve (tensor da perda total, métricas). | |
| pub fn perda_fase_a(&mut self, ids: &Tensor, rng: &mut StdRng) -> Result<(Tensor, PerdaFaseA)> { | |
| let (zhats, zs) = self.codificar_treino(ids)?; | |
| let l = zhats.len(); | |
| // sorteio do nível de parada (nested dropout) | |
| let nivel_parada = rng.gen_range(1..=l); | |
| let logits = self.decoder(&zhats[nivel_parada - 1], Some(ids))?; | |
| let (b, n_t, _) = (logits.dim(0)?, logits.dim(1)?, logits.dim(2)?); | |
| let plano = logits.reshape((b * n_t, self.vocab))?; | |
| let alvos = ids.reshape(b * n_t)?; | |
| let ce = candle_nn::loss::cross_entropy(&plano, &alvos)?; | |
| // commitment: β Σ_ℓ ‖sg[r_ℓ] − z_ℓ‖² (média por elemento) | |
| let mut commit_t: Option<Tensor> = None; | |
| let mut commit = 0f64; | |
| for (i, zp) in zs.iter().enumerate() { | |
| let (_ids, valores) = self.quants[i].quantizar_duro(zp)?; | |
| let dif = (&valores - zp)?; | |
| let termo = dif.sqr()?.mean_all()?; | |
| commit += termo.to_scalar::<f32>()? as f64; | |
| commit_t = Some(match commit_t { Some(s) => (&s + &termo)?, None => termo }); | |
| } | |
| let commit_t = commit_t.unwrap_or_else(|| ce.zeros_like().unwrap()); | |
| let total = (&ce + &commit_t.affine(self.cfg_ncm_beta_commit, 0.0)?)?; | |
| // precisão de reconstrução (telemetria) | |
| let preds = plano.argmax(D::Minus1)?; | |
| let acertos = preds.eq(&alvos)?.to_dtype(candle_core::DType::F32)?.sum_all()?.to_scalar::<f32>()? as f64; | |
| let acc = acertos / (b * n_t) as f64; | |
| // δ_ort por nível (V9) | |
| let deltas: Result<Vec<f64>> = self.quants.iter().map(|q| q.delta_ort()).collect(); | |
| let deltas = deltas?; | |
| for (i, d) in deltas.iter().enumerate() { | |
| self.quants[i].delta_ort_ultimo = *d; | |
| } | |
| Ok((total, PerdaFaseA { | |
| rec: ce.to_scalar::<f32>()? as f64, | |
| commit, | |
| nivel_parada, | |
| acc_recon: acc, | |
| deltas_ort: deltas, | |
| })) | |
| } | |
| /// Decodificação LIVRE (amostragem): ẑ (b, n_ℓ, d_lat) → ids de token AR | |
| /// greedy. Teo A.2/B.4(b): qualquer prefixo decodifica (anytime). | |
| /// O texto final é reconstruído pelo BPE no lado Python. | |
| pub fn decodificar_livre(&self, zhat: &Tensor, max_tokens: usize) -> Result<Vec<Vec<u32>>> { | |
| { | |
| let dev = zhat.device(); | |
| let b = zhat.dim(0)?; | |
| let n_total = self.mascara_causal_dec.dim(0)?; | |
| let up = interp_para(zhat, n_total, dev)?; | |
| let cond = self.dec_proj.fwd(&up)?; | |
| let mut tokens: Vec<Vec<u32>> = (0..b).map(|_| vec![crate::tokens::BOS]).collect(); | |
| for passo in 1..max_tokens.min(n_total) { | |
| let planos: Vec<u32> = tokens.iter().flat_map(|t| t.iter().copied()).collect(); | |
| let entrada = Tensor::from_vec(planos, (b, passo), dev)?; | |
| let mut x = self.dec_emb.fwd(&entrada)?; | |
| x = (&x + &cond.narrow(1, 0, passo)?)?; | |
| for bloco in &self.dec_blocos { | |
| let mut cache = Default::default(); | |
| let m = self.mascara_causal_dec.narrow(0, 0, passo)?.narrow(1, 0, passo)?.contiguous()?; | |
| x = bloco.fwd(&x, Some(&m), &mut cache, None, None)?; | |
| } | |
| let x = self.dec_norm.fwd(&x)?; | |
| let logits = self.dec_head.fwd(&x)?; | |
| let ultimo = logits.narrow(1, passo - 1, 1)?.reshape((b, self.vocab))?; | |
| let preds = ultimo.argmax(D::Minus1)?; | |
| let v = preds.flatten_all()?.to_vec1::<u32>()?; | |
| for (i, t) in v.iter().enumerate() { | |
| tokens[i].push(*t); | |
| } | |
| } | |
| Ok(tokens) | |
| } | |
| } | |
| /// CE por prefixo (V3: monotonia do risco — Teo A.2) em modo congelado. | |
| pub fn ce_por_prefixo(&self, ids: &Tensor) -> Result<Vec<f64>> { | |
| { | |
| let cod = self.codificar_congelado(ids)?; | |
| let mut saida = Vec::with_capacity(cod.zhats.len()); | |
| for zhat in &cod.zhats { | |
| let logits = self.decoder(zhat, Some(ids))?; | |
| let (b, n_t, _) = (logits.dim(0)?, logits.dim(1)?, logits.dim(2)?); | |
| let plano = logits.reshape((b * n_t, self.vocab))?; | |
| let alvos = ids.reshape(b * n_t)?; | |
| let ce = candle_nn::loss::cross_entropy(&plano, &alvos)?; | |
| saida.push(ce.to_scalar::<f32>()? as f64); | |
| } | |
| Ok(saida) | |
| } | |
| } | |
| /// Agenda τ do GSQ por passo (Teo 30.6). | |
| pub fn agendar_tau(&mut self, passo: usize, tau0: f64, tau_min: f64, t_alvo: usize) { | |
| for q in &mut self.quants { | |
| q.agendar_tau(passo, tau0, tau_min, t_alvo); | |
| } | |
| } | |
| /// Atualização EMA do codebook (modo ste_ema apenas — no gsq_rco o | |
| /// gradiente riemanniano já treina W). | |
| pub fn atualizar_ema_codebook(&mut self, ids_lote: &[Tensor]) -> Result<()> { | |
| if self.modo != ModoQuant::SteEma { | |
| return Ok(()); | |
| } | |
| // no modo STE, os codebooks seguem por EMA dos agrupamentos (A.0). | |
| // Implementação: média dos vetores por código → polar → w. | |
| for (i, q) in self.quants.iter_mut().enumerate() { | |
| let ids_l = &ids_lote[i]; | |
| let k = q.uso.len(); | |
| let b = ids_l.dim(0)?; | |
| let plano = ids_l.reshape(b * self.niveis[i])?; | |
| let ids_v = plano.flatten_all()?.to_vec1::<u32>()?; | |
| let mut soma = vec![vec![0f32; self.d_lat]; k]; | |
| let mut conta = vec![0usize; k]; | |
| // usa o codebook corrente para os vetores (aproximação EMA dos | |
| // agrupamentos — suficiente para o modo STE de compatibilidade) | |
| let c = q.codebook()?.detach(); | |
| let c_v = c.flatten_all()?.to_vec1::<f32>()?; | |
| for &idc in &ids_v { | |
| let idc = idc as usize; | |
| if idc < k { | |
| for t in 0..self.d_lat { | |
| soma[idc][t] += c_v[idc * self.d_lat + t]; | |
| } | |
| conta[idc] += 1; | |
| } | |
| } | |
| let ema = q.ema; | |
| let w_v = q.w.as_tensor().flatten_all()?.to_vec1::<f32>()?; | |
| let mut novo = w_v.clone(); | |
| for idc in 0..k { | |
| if conta[idc] > 0 { | |
| for t in 0..self.d_lat { | |
| let alvo = soma[idc][t] / conta[idc] as f32; | |
| novo[idc * self.d_lat + t] = | |
| (ema as f32 * w_v[idc * self.d_lat + t] + (1.0 - ema as f32) * alvo) as f32; | |
| } | |
| } | |
| } | |
| let dev = q.w.as_tensor().device(); | |
| let nt = Tensor::from_vec(novo, (k, self.d_lat), dev)?; | |
| q.w.set(&nt)?; | |
| } | |
| Ok(()) | |
| } | |
| pub fn vars(&self) -> Vec<Var> { | |
| let mut v: Vec<Var> = self.enc_emb.vars(); | |
| for b in &self.enc_blocos { v.extend(b.vars()); } | |
| v.extend(self.enc_norm.vars()); | |
| for p in &self.proj_niveis { v.extend(p.vars()); } | |
| for q in &self.quants { v.push(q.w.clone()); } | |
| v.extend(self.dec_proj.vars()); | |
| v.extend(self.dec_emb.vars()); | |
| for b in &self.dec_blocos { v.extend(b.vars()); } | |
| v.extend(self.dec_norm.vars()); | |
| v.extend(self.dec_head.vars()); | |
| v | |
| } | |
| /// Salva estado (checkpoint atômico — Teo 35.3): índices = ordem de vars(). | |
| pub fn salvar(&self, caminho: &str) -> Result<()> { | |
| let mut obj = serde_json::Map::new(); | |
| for (i, v) in self.vars().iter().enumerate() { | |
| let dados = v.as_tensor().flatten_all()?.to_vec1::<f32>()?; | |
| obj.insert(format!("v{i}"), serde_json::json!({ | |
| "forma": v.as_tensor().dims(), | |
| "dados": dados, | |
| })); | |
| } | |
| let txt = serde_json::to_string(&serde_json::Value::Object(obj)) | |
| .map_err(|e| candle_core::Error::Msg(format!("serde: {e}")))?; | |
| let tmp = format!("{caminho}.tmp"); | |
| std::fs::write(&tmp, txt).map_err(|e| candle_core::Error::Msg(format!("io: {e}")))?; | |
| std::fs::rename(&tmp, caminho).map_err(|e| candle_core::Error::Msg(format!("rename: {e}")))?; | |
| Ok(()) | |
| } | |
| } | |
| pub struct PerdaFaseA { | |
| pub rec: f64, | |
| pub commit: f64, | |
| pub nivel_parada: usize, | |
| pub acc_recon: f64, | |
| pub deltas_ort: Vec<f64>, | |
| } | |
| /// Máscara causal aditiva (n, n): 0 permitido, MIN bloqueado. | |
| pub fn causal_aditiva(n: usize, dev: &Device) -> Result<Tensor> { | |
| let mut m = vec![f32::MIN; n * n]; | |
| for i in 0..n { | |
| for j in 0..=i { | |
| m[i * n + j] = 0.0; | |
| } | |
| } | |
| Tensor::from_vec(m, (n, n), dev) | |
| } | |
| fn tensor_para_json(t: &Tensor) -> Result<serde_json::Value> { | |
| let dados = t.flatten_all()?.to_vec1::<f32>()?; | |
| Ok(serde_json::json!({ | |
| "forma": t.shape().dims(), | |
| "dados": dados, | |
| })) | |
| } | |
| /// Matriz de interpolação cacheada (evita recriar por passo). | |
| pub struct CacheInterp { | |
| pub n_de: usize, | |
| pub n_para: usize, | |
| pub matriz: Tensor, | |
| } | |
| impl CacheInterp { | |
| pub fn novo(n_de: usize, n_para: usize, dev: &Device) -> Result<Self> { | |
| Ok(CacheInterp { n_de, n_para, matriz: matriz_interp(n_de, n_para, dev)? }) | |
| } | |
| } | |