Created
October 12, 2025 10:44
-
-
Save lastforkbender/5c64c4d264dc0bb3e949312ccfa791a2 to your computer and use it in GitHub Desktop.
Comutação de três estados, modulação axis-map(wave) por par de blocos de cubo e persistência em XML
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # cubosai.py ~ comutação de três estados, modulação axis-map(wave) por par de blocos de cubo e persistência em XML | |
| from typing import Tuple, Dict, Any, Optional, List | |
| import xml.etree.ElementTree as ET | |
| import torch.optim as optim | |
| import torch.nn as nn | |
| import numpy as np | |
| import torch | |
| import math | |
| import io | |
| # Sinalizador global padrão ~ classes individuals aceitam o parâmetro @use_complex | |
| DEFAULT_USE_COMPLEX = True | |
| def to_complex_tensor(real: torch.Tensor, imag: torch.Tensor) -> torch.Tensor: | |
| return torch.stack([real, imag], dim=-1) | |
| def complex_from_tensor(z: torch.Tensor) -> Tuple[torch.Tensor, torch.Tensor]: | |
| return z[..., 0], z[..., 1] | |
| def complex_add(a: torch.Tensor, b: torch.Tensor) -> torch.Tensor: | |
| ar, ai = complex_from_tensor(a) | |
| br, bi = complex_from_tensor(b) | |
| return to_complex_tensor(ar+br, ai+bi) | |
| def complex_mul(a: torch.Tensor, b: torch.Tensor) -> torch.Tensor: | |
| ar, ai = complex_from_tensor(a) | |
| br, bi = complex_from_tensor(b) | |
| return to_complex_tensor(ar*br-ai*bi, ar*bi+ai*br) | |
| def complex_abs2(a: torch.Tensor) -> torch.Tensor: | |
| ar, ai = complex_from_tensor(a) | |
| return ar*ar+ai*ai | |
| def complex_conj(a: torch.Tensor) -> torch.Tensor: | |
| r, i = complex_from_tensor(a) | |
| return to_complex_tensor(r, -i) | |
| def complex_cube(a: torch.Tensor) -> torch.Tensor: | |
| return complex_mul(complex_mul(a, a), a) | |
| def complex_real_part(a: torch.Tensor) -> torch.Tensor: | |
| r, _ = complex_from_tensor(a) | |
| return r | |
| class CubosaiAxisMap: | |
| # Armazena parâmetros de onda para um par de blocos(modulação de onda) | |
| def __init__(self, freq: float = 1.0, | |
| phase: float = 0.0, | |
| amp: float = 1.0, | |
| decay: float = 0.0, | |
| tau: float = 0.5, | |
| scale: float = 0.1, | |
| stochastic_gate: bool = False): | |
| # Parâmetros de onda ~ cee | |
| self.freq, self.phase, self.amp, self.decay = freq, phase, amp, decay | |
| # Parâmetros de probabilidade de gating ~ p = sigmoid((tau-ncc)/scale) | |
| self.tau, self.scale = tau, scale | |
| # Permitir influência estocástica opcional no gating(~p para ajustar/empurrar cee ~gate) | |
| self.stochastic_gate = stochastic_gate | |
| # Uma modulação final | |
| self.last_mod = 1.0 | |
| def wave(self, t: float) -> float: | |
| # Valor de regressão em onda cee ~ (cosseno com decaimento) | |
| return self.amp*math.exp(-self.decay*t)*math.cos(2*math.pi*self.freq*t+self.phase) | |
| def prob_from_ncc(self, ncc: float) -> float: | |
| return 1.0/(1.0+math.exp(-((self.tau-ncc)/max(1e-8, self.scale)))) | |
| def compute_modulation(self, t: float, ncc: float) -> float: | |
| p, w = self.prob_from_ncc(ncc), self.wave(t) | |
| self.last_mod = float(p*w) | |
| return self.last_mod | |
| def to_dict(self) -> Dict[str, float]: | |
| return {'freq': self.freq, | |
| 'phase': self.phase, | |
| 'amp': self.amp, | |
| 'decay': self.decay, | |
| 'tau': self.tau, | |
| 'scale': self.scale, | |
| 'stochastic_gate': self.stochastic_gate} | |
| @classmethod | |
| def from_dict(cls, d: Dict[str, float]): | |
| return cls(freq=float(d.get('freq', 1.0)), | |
| phase=float(d.get('phase', 0.0)), | |
| amp=float(d.get('amp', 1.0)), | |
| decay=float(d.get('decay', 0.0)), | |
| tau=float(d.get('tau', 0.5)), | |
| scale=float(d.get('scale', 0.1)), | |
| stochastic_gate=bool(d.get('stochastic_gate', False))) | |
| class CubosaiBlock(nn.Module): | |
| # CubeBlock: 6 faces, cada face é um tensor complexo 4D ou tensor real dependendo do uso de @use_complex | |
| # Formato das faces ~ (6, d0, d1, d2) ~ cee, armazenadas como parâmetros reais/imaginários cee complexas | |
| def __init__(self, d0: int, d1: int, d2: int, device=None, use_complex: bool=DEFAULT_USE_COMPLEX, gate_smooth_tau: float=2.0): | |
| super().__init__() | |
| self.sides_shape, self.use_complex, dev = (6, d0, d1, d2), use_complex, device | |
| # Lados aprendíveis | |
| if self.use_complex: | |
| self.side_r = nn.Parameter(torch.randn(*self.sides_shape, device=dev)*0.01) | |
| self.side_i = nn.Parameter(torch.randn(*self.sides_shape, device=dev)*0.01) | |
| else: | |
| # Quando apenas real cee, armazenar um único parâmetro e tratar a ~parte imaginária como zero | |
| self.side_r, self.side_i = nn.Parameter(torch.randn(*self.sides_shape, device=dev)*0.01), None | |
| # Estados da máscara de gating: 0(desligado), 1(ligado) <~> 2(transicionalj) | |
| self.register_buffer('gate', torch.zeros(*self.sides_shape, dtype=torch.uint8, device=dev)) | |
| # Valores suavizados dos gates; float 0..1 | |
| self.register_buffer('gate_smooth', torch.zeros(*self.sides_shape, dtype=torch.float32, device=dev)) | |
| # Informações de centróide/mediana por face | |
| self.register_buffer('centroid_r', torch.zeros(6, device=dev)) | |
| self.register_buffer('centroid_i', torch.zeros(6, device=dev)) | |
| # **Parâmetros de histerese cee | |
| self.hyst_on = 0.6 # limiar relativo, ~ligar | |
| self.hyst_off = 0.4 # limiar relativo, !desligar | |
| # **Constante de tempo de suavização para ~suavização ~exponencial do @gate_smooth(maior ~! mais lento) | |
| self.gate_smooth_tau, self._eps = gate_smooth_tau, 1e-8 | |
| def forward(self) -> torch.Tensor: | |
| if self.use_complex: | |
| return to_complex_tensor(self.side_r, self.side_i) | |
| else: | |
| # Criar tensor complexo com zeros na parte imaginária ~ para compatibilidade | |
| imag = torch.zeros_like(self.side_r) | |
| return to_complex_tensor(self.side_r, imag) | |
| def compute_centroids(self): | |
| z = self.forward() | |
| r, i = complex_from_tensor(z) | |
| self.centroid_r[:] = r.view(6, -1).mean(dim=1).detach() | |
| self.centroid_i[:] = i.view(6, -1).mean(dim=1).detach() | |
| return self.centroid_r, self.centroid_i | |
| def update_gates_by_ncc(self, base_threshold: float=0.1, | |
| adapt_per_side: bool=True, | |
| median_scale: float=0.5, | |
| dt: float=1.0, | |
| axis_map: Optional[CubosaiAxisMap]=None): | |
| # Calcular limiar adaptativo por lado com base na magnitude mediana | |
| # De histerese: separar limiares de ligado/desligado dependendo: | |
| # ~Suivização exponencial rumo ao estado discreto do gate: | |
| # com empurrão opcional [AxisMap.stochastic_gate] | |
| r = self.side_r | |
| if self.use_complex: | |
| i = self.side_i | |
| # mag = sqrt(r^2v+i^2+{∃}) é cee complexa regularizada | |
| mag = torch.sqrt(r*r+i*i+self._eps) | |
| else: mag = torch.abs(r)+self._eps | |
| mag_flat = mag.view(6, -1) | |
| med = mag_flat.median(dim=1).values.view(6, 1, 1, 1) | |
| mmax = mag_flat.amax(dim=1).view(6, 1, 1, 1).clamp(min=self._eps) | |
| if adapt_per_side: | |
| thr = base_threshold*(med*median_scale+mmax*(1.0-median_scale)) | |
| else: thr = base_threshold*mmax | |
| # Normalizar a mag em relação a mmax para decisão por histerese | |
| mag_norm = mag/mmax | |
| # Calcular o estado discreto desejado do gate com a histerese | |
| prev_gate = self.gate.clone() | |
| # O transitório padrão | |
| desired = torch.full_like(self.gate, 2, dtype=torch.uint8) | |
| on_cond = mag_norm >= self.hyst_on | |
| off_cond = mag_norm <= self.hyst_off | |
| desired = torch.where(on_cond, torch.tensor(1, dtype=torch.uint8, device=self.gate.device), desired) | |
| desired = torch.where(off_cond, torch.tensor(0, dtype=torch.uint8, device=self.gate.device), desired) | |
| # Aplicar empurrão estocástico opcional a partir da probabilidade de @axis_map | |
| if axis_map is not None and axis_map.stochastic_gate: | |
| # Cee, computar @ncc escalar po lado como a distância média aos cantos normalizada: | |
| ncc = (1.0-(med.view(6)/(mmax.view(6)+self._eps))).cpu().numpy() | |
| p = axis_map.prob_from_ncc(float(np.mean(ncc))) | |
| # Empurrar alguns elementos transitórios de forma probabilística: p in (0, 1) | |
| rand = torch.rand_like(self.gate, dtype=torch.float32) | |
| nudge_on = (rand < float(p)) & (desired == 2) | |
| desired = torch.where(nudge_on, torch.tensor(1, dtype=torch.uint8, device=self.gate.device), desired) | |
| # **Atualizar o gate discreto com suavização das transições: | |
| # Quando mudar para ligado/desligado, permitir estado | |
| # cee; transitório por uma atualização igual a ~ (2) | |
| new_gate = prev_gate.clone() | |
| # Posições onde desired != ~ definir ~ 2 primeiro, exceto se igual | |
| changed = desired != prev_gate | |
| new_gate = torch.where(changed, torch.tensor(2, dtype=torch.uint8, device=self.gate.device), prev_gate) | |
| stable = ~changed | |
| new_gate = torch.where(stable, desired, new_gate) | |
| # Confirmando nos buffers | |
| self.gate[:] = new_gate | |
| # Atualizar suavidade: movimento exponencial em direção ao alvo, cee ~ (0/1) | |
| target = (desired == 1).to(torch.float32) | |
| alpha = 1.0-math.exp(-dt/max(1e-6, self.gate_smooth_tau)) | |
| self.gate_smooth[:] = (1.0-alpha)*self.gate_smooth+alpha*target | |
| # Agora retornar métrica resumo por lado: mag média normalizada e média normalizada ~ do gate suavizado | |
| per_side_mag = mag_flat.mean(dim=1).detach().cpu().numpy().tolist() | |
| per_side_gate_smooth = self.gate_smooth.view(6, -1).mean(dim=1).detach().cpu().numpy().tolist() | |
| return per_side_mag, per_side_gate_smooth | |
| def to_dict(self) -> Dict[str, Any]: | |
| d = {} | |
| d['side_r'] = self.side_r.detach().cpu().numpy().tolist() | |
| if self.use_complex: | |
| d['side_i'] = self.side_i.detach().cpu().numpy().tolist() | |
| else: d['side_i'] = None | |
| d['gate'] = self.gate.cpu().numpy().tolist() | |
| d['gate_smooth'] = self.gate_smooth.cpu().numpy().tolist() | |
| d['centroid_r'] = self.centroid_r.cpu().numpy().tolist() | |
| d['centroid_i'] = self.centroid_i.cpu().numpy().tolist() | |
| d['use_complex'] = bool(self.use_complex) | |
| return d | |
| def load_dict(self, d: Dict[str, Any]): | |
| self.side_r.data = torch.tensor(d['side_r'], device=self.side_r.device, dtype=self.side_r.dtype) | |
| if d.get('side_i') is not None and self.use_complex: | |
| self.side_i.data = torch.tensor(d['side_i'], device=self.side_i.device, dtype=self.side_i.dtype) | |
| elif d.get('side_i') is not None and not self.use_complex: | |
| # Cee ~ atualmente for apenas real, então ignorar a parte imag armazenada | |
| pass | |
| self.gate.data = torch.tensor(d['gate'], device=self.gate.device, dtype=self.gate.dtype) | |
| self.gate_smooth.data = torch.tensor(d.get('gate_smooth', self.gate_smooth.cpu().numpy().tolist()), device=self.gate_smooth.device, dtype=self.gate_smooth.dtype) | |
| self.centroid_r.data = torch.tensor(d['centroid_r'], device=self.centroid_r.device, dtype=self.centroid_r.dtype) | |
| self.centroid_i.data = torch.tensor(d['centroid_i'], device=self.centroid_i.device, dtype=self.centroid_i.dtype) | |
| class AxisProjector(nn.Module): | |
| # Projeções lineares complexas aprendidas: | |
| def __init__(self, in_dim: int, out_dim: int, device=None, use_complex: bool = DEFAULT_USE_COMPLEX): | |
| super().__init__() | |
| dev = device | |
| self.use_complex = use_complex | |
| # Cee forem entradas, tratá-las como vetores complexos achatados de | |
| # comprimento @in_dim ~ (última dimensão 2) ~ armazenar pesos para | |
| # 'matmul-complexo' como partes real/imag separadas: | |
| self.Wr = nn.Parameter(torch.randn(out_dim, in_dim, device=dev)*0.02) | |
| self.Wi = nn.Parameter(torch.randn(out_dim, in_dim, device=dev)*0.02) | |
| self.br = nn.Parameter(torch.zeros(out_dim, device=dev)) | |
| self.bi = nn.Parameter(torch.zeros(out_dim, device=dev)) | |
| def forward(self, z: torch.Tensor) -> torch.Tensor: | |
| # Z-shapo ~ (..., in_dim, 2) | (in_dim,2) | |
| r, i = complex_from_tensor(z) | |
| # Garantir que os formatos de [~r] e [~i] sejam compatíveis com as expectativas do matmul(in_dim, ...): | |
| # Cee .....?(out_dim, in_dim) ~ @(in_dim, N) => (out_dim, N) ~~~~~~ | |
| # (in_dim, 1) | (in_dim,) com 'matmul' vai broadcastar corretamente | |
| real = torch.matmul(self.Wr, r)-torch.matmul(self.Wi, i)+self.br.unsqueeze(-1) | |
| imag = torch.matmul(self.Wr, i)+torch.matmul(self.Wi, r)+self.bi.unsqueeze(-1) | |
| return to_complex_tensor(real, imag) | |
| class CubosaiNetwork(nn.Module): | |
| # Montar os blocos, projetores, axis maps e classificador: | |
| def __init__(self, block_count: int, side_dims: Tuple[int,int,int], proj_dim: int, num_classes: int, device=None, use_complex: bool = DEFAULT_USE_COMPLEX): | |
| super().__init__() | |
| d0, d1, d2 = side_dims | |
| self.device = device | |
| self.use_complex = use_complex | |
| self.blocks = nn.ModuleList([CubosaiBlock(d0,d1,d2, device=device, use_complex=use_complex) for _ in range(block_count)]) | |
| flat_side_len = 6*d0*d1*d2 | |
| self.proj = nn.ModuleList() | |
| for _ in range(block_count-1): | |
| self.proj.append(AxisProjector(flat_side_len*2, proj_dim, device=device, use_complex=use_complex)) | |
| self.axis_maps: List[CubosaiAxisMap] = [CubosaiAxisMap() for _ in range(block_count-1)] | |
| # Classificador mapeando magnitudes concatenadas dos projetores para logits: | |
| self.classifier_r = nn.Linear((block_count-1)*proj_dim, num_classes, device=device) | |
| self.classifier_i = nn.Linear((block_count-1)*proj_dim, num_classes, device=device) | |
| # Armazenar a última modulação por projetor | |
| self.last_mods = [1.0 for _ in range(block_count-1)] | |
| # Incrementar o contador de passos para os ~ciclos | |
| self._t = 0.0 | |
| def step_cycle(self, dt: float=1.0, gate_threshold: float=0.1): | |
| # Avançar o tempo, atualizar centróides e calcular modulações do axis map | |
| self._t+=dt | |
| per_pair_ncc, per_block_ncc, per_block_gate_smooth = [], [], [] | |
| for idx, blk in enumerate(self.blocks): | |
| blk.compute_centroids() | |
| # Passar o @axis_map associado para empurrão estocástico ~ cee disponível(usar média dos vizinhos) | |
| axis_map = None | |
| # Escolher um @axis_map vizinho ~ cee existir, para influenciar este bloco: | |
| if len(self.axis_maps) > 0: | |
| if idx == 0: axis_map = self.axis_maps[0] | |
| elif idx == len(self.blocks)-1: axis_map = self.axis_maps[-1] | |
| else: axis_map = self.axis_maps[idx-1] | |
| mag, gate_smooth = blk.update_gates_by_ncc(base_threshold=gate_threshold, axis_map=axis_map, dt=dt) | |
| per_block_ncc.append(mag) | |
| per_block_gate_smooth.append(gate_smooth) | |
| # Calcular resumo NCC por par e modular os axis maps: | |
| for i in range(len(self.proj)): | |
| # Usar a NCC média entre os lados de ambos os blocks como @ncc de resumo | |
| ncc_A, ncc_B = np.mean(per_block_ncc[i]), np.mean(per_block_ncc[i+1]) | |
| ncc_summary = float(0.5*(ncc_A+ncc_B)) | |
| mod = self.axis_maps[i].compute_modulation(self._t, ncc_summary) | |
| self.last_mods[i] = mod | |
| per_pair_ncc.append(ncc_summary) | |
| return per_block_ncc, per_block_gate_smooth, per_pair_ncc, list(self.last_mods) | |
| def forward(self, x: Optional[torch.Tensor] = None) -> torch.Tensor: | |
| # Calcular projeções entre blocos adjacentes para aplicar fatores de modulação: | |
| projs = [] | |
| for i in range(len(self.proj)): | |
| # Cee ~ (6, d0, d1, d2, 2) | |
| A, B = self.blocks[i].forward(), self.blocks[i+1].forward() | |
| # Achatar A e B em vetores | |
| Af, Bf = A.view(-1, 2), B.view(-1, 2) | |
| vec = torch.cat([Af, Bf], dim=0) # (in_dim*2, 2) | |
| # ...(in_dim, 1, 2) | ...(in_dim, 2) aceitável? | |
| z = vec.unsqueeze(0) | |
| # AxisProjector.forward ~ espera última dimensão 2; fornecer (in_dim, 1, 2) e pegar a primeira fatia | |
| p = self.proj[i](z[0]) | |
| # Garantir que p tenha formato (out_dim, 1, 2) | (out_dim, N, 2); normalizar para(out_dim, N, 2) | |
| if p.ndim == 2: p = p.unsqueeze(-1) | |
| # Magnitude ao quadrado por @out_dim cee: somar sobre os canais real/imag | |
| mag2 = complex_abs2(p).sum(dim=-2).squeeze(-1) | |
| # Finalmente aplicar a modulação, última modulação escalar | |
| mod = float(self.last_mods[i]) if i < len(self.last_mods) else 1.0 | |
| mag2 = mag2*mod | |
| projs.append(mag2) | |
| if len(projs) == 0: | |
| feat = torch.zeros(1, self.classifier_r.in_features, device=self.classifier_r.weight.device) | |
| else: feat = torch.cat(projs, dim=0).unsqueeze(0) | |
| rlogits, _ilogits = self.classifier_r(feat), self.classifier_i(feat) | |
| logits = rlogits | |
| return logits | |
| def save_to_xml(self, path: str): | |
| root = ET.Element('CubosaiNetwork') | |
| meta = ET.SubElement(root, 'Meta') | |
| meta_use_complex = ET.SubElement(meta, 'UseComplex') | |
| meta_use_complex.text = repr(bool(self.use_complex)) | |
| blocks_el = ET.SubElement(root, 'Blocks') | |
| for idx, blk in enumerate(self.blocks): | |
| be = ET.SubElement(blocks_el, 'Block', idx=str(idx)) | |
| bd = blk.to_dict() | |
| for k,v in bd.items(): | |
| sub = ET.SubElement(be, k) | |
| sub.text = repr(v) | |
| maps_el = ET.SubElement(root, 'AxisMaps') | |
| for idx, am in enumerate(self.axis_maps): | |
| me, md = ET.SubElement(maps_el, 'AxisMap', idx=str(idx)), am.to_dict() | |
| for k,v in md.items(): | |
| sub = ET.SubElement(me, k) | |
| sub.text = repr(v) | |
| cls_el = ET.SubElement(root, 'Classifier') | |
| cls_el_r_w = ET.SubElement(cls_el, 'Wr') | |
| cls_el_r_w.text = repr(self.classifier_r.weight.detach().cpu().numpy().tolist()) | |
| cls_el_r_b = ET.SubElement(cls_el, 'Br') | |
| cls_el_r_b.text = repr(self.classifier_r.bias.detach().cpu().numpy().tolist()) | |
| cls_el_i_w = ET.SubElement(cls_el, 'Wi') | |
| cls_el_i_w.text = repr(self.classifier_i.weight.detach().cpu().numpy().tolist()) | |
| cls_el_i_b = ET.SubElement(cls_el, 'Bi') | |
| cls_el_i_b.text = repr(self.classifier_i.bias.detach().cpu().numpy().tolist()) | |
| tree = ET.ElementTree(root) | |
| tree.write(path) | |
| def load_from_xml(self, path: str): | |
| tree = ET.parse(path) | |
| root = tree.getroot() | |
| meta = root.find('Meta') | |
| if meta is not None: | |
| uc = meta.find('UseComplex') | |
| if uc is not None: loaded_uc = eval(uc.text) | |
| blocks_el = root.find('Blocks') | |
| if blocks_el is None: | |
| raise RuntimeError('Missing Blocks in XML') | |
| for be in blocks_el.findall('Block'): | |
| idx = int(be.attrib['idx']) | |
| d = {} | |
| for k in ['side_r','side_i','gate','gate_smooth','centroid_r','centroid_i','use_complex']: | |
| node = be.find(k) | |
| if node is None: | |
| continue | |
| d[k] = eval(node.text) | |
| self.blocks[idx].load_dict(d) | |
| maps_el = root.find('AxisMaps') | |
| if maps_el is not None: | |
| ams = [] | |
| for me in maps_el.findall('AxisMap'): | |
| md = {} | |
| for k in ['freq','phase','amp','decay','tau','scale','stochastic_gate']: | |
| node = me.find(k) | |
| if node is None: | |
| continue | |
| md[k] = eval(node.text) | |
| ams.append(AxisMap.from_dict(md)) | |
| if len(ams) == len(self.axis_maps): | |
| self.axis_maps = ams | |
| cls_el = root.find('Classifier') | |
| if cls_el is not None: | |
| Wr = eval(cls_el.find('Wr').text) | |
| Br = eval(cls_el.find('Br').text) | |
| Wi = eval(cls_el.find('Wi').text) | |
| Bi = eval(cls_el.find('Bi').text) | |
| self.classifier_r.weight.data = torch.tensor(Wr, device=self.classifier_r.weight.device, dtype=self.classifier_r.weight.dtype) | |
| self.classifier_r.bias.data = torch.tensor(Br, device=self.classifier_r.bias.device, dtype=self.classifier_r.bias.dtype) | |
| self.classifier_i.weight.data = torch.tensor(Wi, device=self.classifier_i.weight.device, dtype=self.classifier_i.weight.dtype) | |
| self.classifier_i.bias.data = torch.tensor(Bi, device=self.classifier_i.bias.device, dtype=self.classifier_i.bias.dtype) | |
| def cubosai_run(): | |
| device = torch.device('cpu') | |
| net = CubosaiNetwork(block_count=3, side_dims=(2,2,2), proj_dim=8, num_classes=3, device=device, use_complex=True) | |
| opt = optim.Adam(net.parameters(), lr=1e-3) | |
| criterion = nn.CrossEntropyLoss() | |
| net.step_cycle(dt=0.5, gate_threshold=0.2) | |
| for epoch in range(361): | |
| net.train() | |
| net.step_cycle(dt=0.1, gate_threshold=0.2) | |
| logits = net() | |
| target = torch.tensor([1], dtype=torch.long) | |
| loss = criterion(logits, target) | |
| opt.zero_grad() | |
| loss.backward() | |
| opt.step() | |
| print(f"epoch {epoch} loss {loss.item():.4f} mods {net.last_mods}") | |
| #net.save_to_xml('cubosai_modal.xml') | |
| #net.load_from_xml('cubosai_modal.xml') | |
| cubosai_run() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment