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/gerador.rs from PowerMachine/khtst-rust: direct link, hf CLI and curl.
- Browser
- Download file 11.8 kB
-
https://huggingface.co/PowerMachine/khtst-rust/resolve/main/src/gerador.rs
- Command line
-
hf download hf://PowerMachine/khtst-rust/src/gerador.rs
-
curl -L -o gerador.rs https://huggingface.co/PowerMachine/khtst-rust/resolve/main/src/gerador.rs
11.8 kB
| //! Gerador de conceitos (Estágio 2 / fase B) — port de | |
| //! `conceitos/gerador_conceitos.py` (n-04 §2.4). Fatoração (Teo B.1/B.2): | |
| //! p_φ(c) = Π_ℓ p_φ(c_ℓ|c_{<ℓ}) com predição PARALELA intra-nível; | |
| //! condicionamento = reconstrução cumulativa interpolada ẑ_{ℓ−1}^{(n_ℓ)} + e_ℓ | |
| //! (função determinística de c_{<ℓ} — Teo D.1). Corrupção κ (Teo C.3) e | |
| //! dropout CFG (Teo D.2(iii)). Cabeças MTP auxiliares (Teo D.6/T7). | |
| use candle_core::{Device, Result, Tensor, Var, D}; | |
| use crate::camadas::{Linear, RmsNorm}; | |
| use crate::config::ConfigKhtst; | |
| use crate::mascaras::{interp_para, mascara_block_causal}; | |
| use crate::tokenizador::TokenizadorConceitos; | |
| use crate::tronco::{CabecaConceitos, EmbNiveis, Tronco}; | |
| use rand::prelude::*; | |
| pub const N_PREFIXO: usize = 8; | |
| pub struct GeradorConceitos { | |
| pub tronco: Tronco, | |
| pub emb_niveis: EmbNiveis, | |
| pub proj_entrada: Linear, // d_lat → d_modelo | |
| pub cabeca: CabecaConceitos, | |
| pub cabecas_mtp: Vec<Linear>, // auxiliares — Teo D.6 | |
| pub norm_entrada: RmsNorm, | |
| pub mascara: Tensor, | |
| pub niveis: Vec<usize>, | |
| pub d_lat: usize, | |
| pub corrupcao: f64, | |
| pub dropout_cfg: f64, | |
| pub k: usize, | |
| pub mtp_ativo: bool, | |
| pub beta_mtp: f64, | |
| } | |
| pub struct SaidaTreino { | |
| pub perda: Tensor, | |
| pub ce_por_nivel: Vec<f64>, | |
| pub ce_mtp: f64, | |
| pub acuracia_media: f64, | |
| pub uncond: bool, | |
| } | |
| impl GeradorConceitos { | |
| pub fn novo(cfg: &ConfigKhtst, dev: &Device) -> Result<Self> { | |
| let m = &cfg.modelo; | |
| let n = &cfg.ncm; | |
| let tronco = Tronco::novo(cfg, dev)?; | |
| let mascara = mascara_block_causal(&n.niveis, N_PREFIXO, dev)?; | |
| let mut mtp = Vec::new(); | |
| if m.mtp.ativo { | |
| for _ in 0..n.niveis.len().saturating_sub(1) { | |
| mtp.push(Linear::sem_vies(m.d_modelo, n.k_por_nivel, 0.02, dev)?); | |
| } | |
| } | |
| Ok(GeradorConceitos { | |
| tronco, | |
| emb_niveis: EmbNiveis::nova(cfg, dev)?, | |
| proj_entrada: Linear::sem_vies(n.d_lat, m.d_modelo, 0.02, dev)?, | |
| cabeca: CabecaConceitos::nova(cfg, dev)?, | |
| cabecas_mtp: mtp, | |
| norm_entrada: RmsNorm::novo(m.d_modelo, 1e-5, dev)?, | |
| mascara, | |
| niveis: n.niveis.clone(), | |
| d_lat: n.d_lat, | |
| corrupcao: n.corrupcao_cond, | |
| dropout_cfg: n.dropout_cfg, | |
| k: n.k_por_nivel, | |
| mtp_ativo: m.mtp.ativo, | |
| beta_mtp: m.mtp.beta_entropia, | |
| }) | |
| } | |
| pub fn entrada_nivel_pub( | |
| &self, | |
| zhat_anterior: Option<&Tensor>, | |
| nivel: usize, | |
| b: usize, | |
| dev: &Device, | |
| ) -> Result<Tensor> { | |
| self.entrada_nivel(zhat_anterior, nivel, b, dev) | |
| } | |
| fn entrada_nivel( | |
| &self, | |
| zhat_anterior: Option<&Tensor>, | |
| nivel: usize, | |
| b: usize, | |
| dev: &Device, | |
| ) -> Result<Tensor> { | |
| let n_l = self.niveis[nivel]; | |
| // dims()[0] = d_modelo (SAÍDA da projeção d_lat→d_modelo) | |
| let d = self.proj_entrada.w.as_tensor().dims()[0]; | |
| let base = match zhat_anterior { | |
| Some(z) => { | |
| let interp = interp_para(z, n_l, dev)?; | |
| self.proj_entrada.fwd(&interp)? | |
| } | |
| None => Tensor::zeros((b, n_l, d), candle_core::DType::F32, dev)?, | |
| }; | |
| let ids_nivel = Tensor::from_vec(vec![nivel as u32 + 1; b * n_l], (b, n_l), dev)?; | |
| let e_l = self.emb_niveis.tabela.fwd(&ids_nivel)?; | |
| self.norm_entrada.fwd(&(&base + &e_l)?) | |
| } | |
| /// Aplica a corrupção κ (por posição — Teo C.3) e devolve os códigos de uso. | |
| fn corromper(&self, codigos: &[Tensor], b: usize, rng: &mut StdRng, dev: &Device) -> Result<Vec<Tensor>> { | |
| let mut saida = Vec::with_capacity(codigos.len()); | |
| for (i, cid) in codigos.iter().enumerate() { | |
| let mut c = cid.clone(); | |
| if self.corrupcao > 0.0 { | |
| let plano = c.reshape((b * self.niveis[i],))?; | |
| let mut v = plano.flatten_all()?.to_vec1::<u32>()?; | |
| for x in v.iter_mut() { | |
| if rng.gen::<f64>() < self.corrupcao { | |
| *x = rng.gen_range(0..self.k as u32); | |
| } | |
| } | |
| c = Tensor::from_vec(v, (b, self.niveis[i]), dev)?; | |
| } | |
| saida.push(c); | |
| } | |
| Ok(saida) | |
| } | |
| /// Forward de TREINO com perda (1 passada — Teo D.1(iv)): teacher forcing | |
| /// com códigos VERDADEIROS no alvo; condicionamento corrompido por κ; | |
| /// dropout CFG por exemplo (ramo não-condicionado = códigos aleatórios). | |
| pub fn perda_treino( | |
| &self, | |
| tok: &TokenizadorConceitos, | |
| codigos_verdadeiros: &[Tensor], | |
| rng: &mut StdRng, | |
| ) -> Result<SaidaTreino> { | |
| let dev = self.mascara.device(); | |
| let b = codigos_verdadeiros[0].dim(0)?; | |
| let codigos_uso = self.corromper(codigos_verdadeiros, b, rng, dev)?; | |
| let uncond = rng.gen::<f64>() < self.dropout_cfg; | |
| let zhats = if uncond { | |
| let aleatorios: Vec<Tensor> = (0..codigos_uso.len()) | |
| .map(|i| { | |
| let n_l = self.niveis[i]; | |
| let v: Vec<u32> = (0..b * n_l).map(|_| rng.gen_range(0..self.k as u32)).collect(); | |
| Tensor::from_vec(v, (b, n_l), dev).unwrap() | |
| }) | |
| .collect(); | |
| tok.reconstruir_de_codigos(&aleatorios, aleatorios.len(), b, dev)? | |
| } else { | |
| tok.reconstruir_de_codigos(&codigos_uso, codigos_uso.len(), b, dev)? | |
| }; | |
| // monta [prefixo; lev_1; …; lev_L] | |
| let mut blocos = Vec::with_capacity(self.niveis.len()); | |
| for nivel in 0..self.niveis.len() { | |
| let zhat_ant = if nivel == 0 { None } else { Some(&zhats[nivel - 1]) }; | |
| blocos.push(self.entrada_nivel(zhat_ant, nivel, b, dev)?); | |
| } | |
| let prefixo_ids = Tensor::zeros((b, N_PREFIXO), candle_core::DType::U32, dev)?; | |
| let prefixo = self.emb_niveis.tabela.fwd(&prefixo_ids)?; | |
| let mut seqs = vec![prefixo]; | |
| seqs.extend(blocos.iter().cloned()); | |
| let s = Tensor::cat(&seqs, 1)?; | |
| let mut niveis_pos = vec![0usize; N_PREFIXO]; | |
| for (i, &n_l) in self.niveis.iter().enumerate() { | |
| niveis_pos.extend(std::iter::repeat(i + 1).take(n_l)); | |
| } | |
| let mut resumos = Vec::new(); | |
| let h = self.tronco.forward_treino(&s, &self.mascara, &niveis_pos, &mut resumos)?; | |
| let mut ces = Vec::with_capacity(self.niveis.len()); | |
| let mut accs = Vec::new(); | |
| let mut inicio = N_PREFIXO; | |
| for (i, &n_l) in self.niveis.iter().enumerate() { | |
| let trecho = h.narrow(1, inicio, n_l)?; | |
| let logits = self.cabeca.cabecas[i].fwd(&trecho)?; | |
| let plano = logits.reshape((b * n_l, self.k))?; | |
| let alvo = codigos_verdadeiros[i].reshape(b * n_l)?.to_dtype(candle_core::DType::U32)?; | |
| let ce = candle_nn::loss::cross_entropy(&plano, &alvo)?; | |
| let preds = plano.argmax(D::Minus1)?; | |
| let ac = preds.eq(&alvo)?.to_dtype(candle_core::DType::F32)?.sum_all()?.to_scalar::<f32>()? as f64 | |
| / (b * n_l) as f64; | |
| accs.push(ac); | |
| ces.push(ce); | |
| inicio += n_l; | |
| } | |
| let l = ces.len() as f64; | |
| let mut perda = ces[0].affine(1.0 / l, 0.0)?; | |
| for ce in &ces[1..] { | |
| perda = (&perda + &ce.affine(1.0 / l, 0.0)?)?; | |
| } | |
| // MTP auxiliar (Teo D.6): cabeça no nível ℓ prevê códigos do nível ℓ+1 | |
| let mut ce_mtp = 0f64; | |
| if self.mtp_ativo && !self.cabecas_mtp.is_empty() { | |
| let dev = self.mascara.device(); | |
| let mut inicio = N_PREFIXO; | |
| let mut soma = None; | |
| for i in 0..self.cabecas_mtp.len() { | |
| let n_l = self.niveis[i]; | |
| let n_prox = self.niveis[i + 1]; | |
| let trecho = h.narrow(1, inicio, n_l)?; | |
| // reamostra para o tamanho do nível alvo (n_ℓ ≠ n_{ℓ+1}) | |
| let trecho_mtp = if n_l != n_prox { | |
| interp_para(&trecho, n_prox, dev)? | |
| } else { | |
| trecho | |
| }; | |
| let logits = self.cabecas_mtp[i].fwd(&trecho_mtp)?; | |
| let plano = logits.reshape((b * n_prox, self.k))?; | |
| let alvo = codigos_verdadeiros[i + 1].reshape(b * n_prox)? | |
| .to_dtype(candle_core::DType::U32)?; | |
| let ce = candle_nn::loss::cross_entropy(&plano, &alvo)?; | |
| soma = Some(match soma { Some(s) => (&s + &ce)?, None => ce }); | |
| inicio += n_l; | |
| } | |
| if let Some(s) = soma { | |
| let m = self.cabecas_mtp.len() as f64; | |
| ce_mtp = (s.affine(1.0 / m, 0.0)?).to_scalar::<f32>()? as f64; | |
| perda = (&perda + &s.affine(self.beta_mtp / m, 0.0)?)?; | |
| } | |
| } | |
| Ok(SaidaTreino { | |
| perda, | |
| ce_por_nivel: ces.iter().map(|t| t.to_scalar::<f32>().unwrap() as f64).collect(), | |
| ce_mtp, | |
| acuracia_media: accs.iter().sum::<f64>() / accs.len() as f64, | |
| uncond, | |
| }) | |
| } | |
| pub fn vars(&self) -> Vec<Var> { | |
| let mut v = self.tronco.vars(); | |
| v.extend(self.emb_niveis.vars()); | |
| v.extend(self.proj_entrada.vars()); | |
| v.extend(self.cabeca.vars()); | |
| for m in &self.cabecas_mtp { v.extend(m.vars()); } | |
| v.extend(self.norm_entrada.vars()); | |
| v | |
| } | |
| 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().shape().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(()) | |
| } | |
| /// Carrega um checkpoint salvo por `salvar` (mesma ordem de construção | |
| /// de `novo` ⇒ índices de variáveis casam 1-a-1 — ajuste fino honesto). | |
| pub fn carregar(&mut self, caminho: &str) -> Result<()> { | |
| let txt = std::fs::read_to_string(caminho) | |
| .map_err(|e| candle_core::Error::Msg(format!("io: {e}")))?; | |
| let obj: serde_json::Map<String, serde_json::Value> = serde_json::from_str(&txt) | |
| .map_err(|e| candle_core::Error::Msg(format!("serde: {e}")))?; | |
| let vars = self.vars(); | |
| for (i, v) in vars.iter().enumerate() { | |
| let e = obj.get(&format!("v{i}")).ok_or_else(|| { | |
| candle_core::Error::Msg(format!("checkpoint sem v{i}")) | |
| })?; | |
| let forma: Vec<usize> = serde_json::from_value(e["forma"].clone()) | |
| .map_err(|e| candle_core::Error::Msg(format!("forma: {e}")))?; | |
| let dados: Vec<f32> = serde_json::from_value(e["dados"].clone()) | |
| .map_err(|e| candle_core::Error::Msg(format!("dados: {e}")))?; | |
| if dados.len() != forma.iter().product::<usize>() { | |
| candle_core::bail!("forma {:?} incompatível com {} valores", forma, dados.len()); | |
| } | |
| let t = Tensor::from_vec(dados, forma, v.device())?; | |
| v.set(&t)?; | |
| } | |
| Ok(()) | |
| } | |
| } | |