| import torch
|
| import math
|
| from typing import List, Tuple
|
| import torch.nn as nn
|
| import torch.nn.functional as F
|
|
|
| '''
|
| このコードは純粋3値学習を行うためのモデルコード(定義)です、通常はこちらを参照し呼び出すだけでOKです
|
| 既存の重みをもつモデルを読み込んで3値へ転換も可能です(モデルの次元などは学習コード側で適切に合わせてください)
|
| ---
|
| # チェックポイント読み込み(D-RNA-Trio の学習済みモデル) / fp8/16/32 相当の連続重みの復元
|
| 2〜3 レイヤ:fp8相当、4〜6 レイヤ:bf16相当、7+ レイヤ:fp16/32 相当
|
| 固有位相(K-energy × RoPE 周波数成分)を重ね合わせ+正規化(二重らせんの共鳴収縮により歪みを抑制)
|
| 特に K-for-phase の「逐次カスケード的変化」が位相シフトとなり自然な凸凹パターンを生成
|
| ---
|
| D‑RNA: Dual‑Helix Resonance Neural Architecture (DRNA) Pre-Norm・Kv-RoPE 版
|
| 仕様:Pre-Norm(RMSNorm)、GELU(Activation)、Kv-RoPE(head_dim)、mask(padding + causal)
|
| Transformerの全接続性を継承しつつ、二重らせん(Dual-Helix)構造による
|
| 「共鳴収縮」(Resonant Contraction)を物理的に再現したニューラルアーキテクチャです
|
| D-RNA の位相設計と Trio Induction system により3値学習を STE に頼らず安定的に行えます
|
| ---
|
| これは STE で機能しない optimiser などを3値学習へ活用できるようになります
|
| 将来的に3値モデルを位相差の重ねによる疑似重みをつくり、これを学習対象にして3値学習もおこなえるはずです
|
| つまり学習元も3値モデルにできるはずです、推論も学習も3値で済むようになる最初の1歩です
|
| '''
|
|
|
|
|
| class DRNAWeightRestorer:
|
| '''
|
| D-RNA の固有位相を利用した fp8/16/32 重み復元器
|
| チェックポイント読み込み → 位相抽出 → 重ね合わせ → 正規化 で連続分布を生成
|
| '''
|
|
|
| def __init__(self, d_model: int = 256, num_layers: int = 16):
|
| self.d_model = d_model
|
| self.num_layers = num_layers
|
|
|
|
|
| def load_ternary_weights(self, checkpoint_path: str) -> List[torch.Tensor]:
|
| '''ロードされたチェックポイントから各レイヤの重みを抽出する'''
|
| checkpoint = torch.load(checkpoint_path, map_location='cuda')
|
|
|
| layer_weights = []
|
| for i in range(self.num_layers):
|
|
|
| qkv_weight = checkpoint[f'layers.{i}.qkv.weight']
|
|
|
|
|
| mlp_0_weight = checkpoint[f'layers.{i}.mlp.0.weight']
|
| mlp_3_weight = checkpoint[f'layers.{i}.mlp.3.weight']
|
|
|
| layer_weights.extend([qkv_weight, mlp_0_weight, mlp_3_weight])
|
|
|
| return layer_weights
|
|
|
|
|
| def extract_phase_offsets(self, max_seq_len: int = 256) -> List[Tuple[torch.Tensor, torch.Tensor]]:
|
| '''DRNA の固有位相(K-energy × RoPE 周波数成分)を計算し返す'''
|
|
|
| num_layer_groups = self.num_layers * 3
|
|
|
| phase_data_list = []
|
| base_energy = torch.arange(128, dtype=torch.float32, device='cuda') / self.d_model
|
|
|
| for i in range(num_layer_groups):
|
|
|
| k_energy = base_energy * (i % self.num_layers + 1)
|
|
|
|
|
| rt_phase = math.tanh(k_energy) * math.pi
|
|
|
|
|
| inv_freq = 1.0 / (10000 ** (torch.arange(0, 128, 2).float() / self.d_model))
|
|
|
| freqs = torch.einsum("i,j->ij",
|
| torch.arange(max_seq_len, device='cuda'),
|
| inv_freq)
|
|
|
|
|
| cos_shift = torch.cos(freqs * rt_phase.unsqueeze(-1))
|
| sin_shift = torch.sin(freqs * rt_phase.unsqueeze(-1))
|
|
|
| phase_data_list.append((cos_shift, sin_shift))
|
|
|
| return phase_data_list
|
|
|
|
|
| def superimpose_weights(
|
| self,
|
| layer_weights: List[torch.Tensor],
|
| phase_offsets: List[Tuple[torch.Tensor, torch.Tensor]]
|
| ) -> torch.Tensor:
|
| '''各レイヤの -/0/+ 重みに固有位相を適用し重ね合わせる'''
|
|
|
| reconstructed = torch.zeros_like(layer_weights[0])
|
|
|
| for i, w_i in enumerate(layer_weights):
|
| cos_shift, sin_shift = phase_offsets[i]
|
|
|
|
|
| contribution = w_i * (cos_shift - rotate_half(w_i) * sin_shift)
|
| reconstructed += contribution
|
|
|
| return reconstructed
|
|
|
|
|
| def normalize(self, x: torch.Tensor) -> torch.Tensor:
|
| '''重ね合わせ後の分布を RMSNorm で安定化する'''
|
|
|
| mean = x.mean(-1, keepdim=True)
|
| var = x.var(-1, keepdim=True, unbiased=False)
|
|
|
|
|
| return (x - mean) * torch.rsqrt(var + 1e-8)
|
|
|
|
|
| def restore_from_checkpoint(
|
| self,
|
| checkpoint_path: str,
|
| max_seq_len: int = 256
|
| ) -> torch.Tensor:
|
| '''チェックポイントを読み込み、fp8/16 相当の連続重みを生成する'''
|
|
|
|
|
| layer_weights = self.load_ternary_weights(checkpoint_path)
|
|
|
|
|
| phase_offsets = self.extract_phase_offsets(max_seq_len)
|
|
|
|
|
| continuous_weight = self.superimpose_weights(
|
| layer_weights,
|
| phase_offsets)
|
|
|
|
|
| normalized_weight = self.normalize(continuous_weight)
|
|
|
| return normalized_weight
|
|
|
|
|
| def rotate_half(x):
|
| '''D-RNA の K-for-phase 同様の回転操作(位相変調の右辺項)'''
|
| x1, x2 = x.chunk(2, dim=-1)
|
| return torch.cat((-x2, x1), dim=-1)
|
|
|
|
|
|
|
|
|
| class TernaryTrainingManager:
|
| '''
|
| D-RNAのコードに触れることなく、3値誘導の全ライフサイクルを統括する抽象化マネージャー
|
| '''
|
| def __init__(self, model, warmup_steps=100, max_lambda=1.0):
|
| self.model = model
|
| self.warmup_steps = warmup_steps
|
| self.max_lambda = max_lambda
|
| self.current_step = 0
|
| self.total_steps = 0
|
|
|
|
|
| def step_provider():
|
| return self.current_step, self.total_steps
|
|
|
|
|
| for name, module in self.model.named_modules():
|
| if isinstance(module, nn.Linear):
|
|
|
| if "embed" in name or "output_head" in name:
|
| continue
|
| if not hasattr(module, "raw_weight"):
|
|
|
| module.register_parameter("raw_weight", nn.Parameter(module.weight.data.clone()))
|
| delattr(module, "weight")
|
| module.register_buffer("weight", module.raw_weight.data.clone())
|
| module.register_forward_pre_hook(TernaryWeightHook(step_provider, self.warmup_steps))
|
|
|
| def amend_loss(self, task_loss, step, total_steps):
|
| '''
|
| 【ループ内抽象化用】 メインのタスク損失(CrossEntropy等)を受け取り、
|
| 現在のステップに応じた3値結晶化ペナルティを自動計算して合算した損失を返す
|
| '''
|
| self.current_step = step
|
| self.total_steps = total_steps
|
|
|
| blend_ratio = get_ternary_schedule(step, total_steps, self.warmup_steps)
|
| current_lambda = blend_ratio * self.max_lambda
|
|
|
| if current_lambda == 0.0:
|
| return task_loss
|
|
|
|
|
| ternary_penalty = 0.0
|
| for name, param in self.model.named_parameters():
|
|
|
| if "raw_weight" in name and param.dim() >= 2:
|
|
|
| ternary_penalty += torch.mean(param * (param - 1.0) * (param + 1.0)) ** 2
|
|
|
| return task_loss + current_lambda * ternary_penalty
|
|
|
| def export_ternary(self):
|
| '''学習終了後、モデルの全2次元重みを完全な[-1.0, 0.0, 1.0]へ固定(結晶化)する'''
|
| with torch.no_grad():
|
| for name, module in self.model.named_modules():
|
| if isinstance(module, nn.Linear):
|
|
|
| if "embed" in name or "output_head" in name:
|
| continue
|
|
|
| if hasattr(module, "raw_weight"):
|
| param = module.raw_weight
|
| else:
|
| param = module.weight
|
|
|
| soft = torch.tanh(param * 3.0)
|
|
|
| ternary = torch.zeros_like(soft)
|
| ternary[soft > 0.08] = 1.0
|
| ternary[soft < -0.08] = -1.0
|
|
|
| if hasattr(module, "raw_weight"):
|
| module.raw_weight.copy_(ternary)
|
| module.weight.copy_(ternary)
|
|
|
| return self.model
|
|
|
|
|
| def get_ternary_schedule(step, total_steps, warmup_steps=100):
|
| '''逆転コサインアニーリングスケジューラ'''
|
| if step < warmup_steps:
|
| return 0.0
|
| anneal_steps = total_steps - warmup_steps
|
| progress = (step - warmup_steps) / anneal_steps
|
| return 1.0 - (0.5 * (1.0 + math.cos(progress * math.pi)))
|
|
|
| def get_soft_ternary_weight(param, step, total_steps, warmup_steps=100):
|
| '''勾配直通バイパス型 3値ブレンド関数'''
|
| if param.dim() < 2:
|
| return param
|
| blend_ratio = get_ternary_schedule(step, total_steps, warmup_steps)
|
| if blend_ratio == 0.0:
|
| return param
|
| with torch.no_grad():
|
| ternary_target = torch.tanh(param * 3.0)
|
|
|
| return param + blend_ratio * (ternary_target - param)
|
|
|
| class TernaryWeightHook:
|
| def __init__(self, step_provider, warmup_steps=100):
|
| self.step_provider = step_provider
|
| self.warmup_steps = warmup_steps
|
|
|
| def __call__(self, module, inputs):
|
| step, total_steps = self.step_provider()
|
| if step is not None and total_steps is not None:
|
|
|
| module.weight.data = get_soft_ternary_weight(module.raw_weight, step, total_steps, self.warmup_steps)
|
|
|
| def apply_trio_induction(model, step_provider, warmup_steps=100):
|
| '''モデル側ではなく外側から3値化プラグインを刺す関数'''
|
| for name, module in model.named_modules():
|
| if isinstance(module, nn.Linear):
|
|
|
| if "embed" in name or "output_head" in name:
|
| continue
|
| if not hasattr(module, "raw_weight"):
|
|
|
| module.register_parameter("raw_weight", nn.Parameter(module.weight.data.clone()))
|
| delattr(module, "weight")
|
| module.register_buffer("weight", module.raw_weight.data.clone())
|
| module.register_forward_pre_hook(TernaryWeightHook(step_provider, warmup_steps))
|
|
|
|
|
| class RMSNorm(nn.Module):
|
| '''【修正版】3値化の歪みをリセットする中心化・標準化型防波堤'''
|
| def __init__(self, d_model, eps=1e-8):
|
| super().__init__()
|
| self.eps = eps
|
|
|
| self.bias = nn.Parameter(torch.zeros(d_model))
|
|
|
| self.weight = nn.Parameter(torch.ones(d_model))
|
|
|
| def forward(self, x):
|
|
|
| mean = x.mean(-1, keepdim=True)
|
| var = x.var(-1, keepdim=True, unbiased=False)
|
|
|
|
|
| x_normed = (x - mean) * torch.rsqrt(var + self.eps)
|
|
|
|
|
| return self.weight * x_normed + self.bias
|
|
|
| class DRNA_RoPE(nn.Module):
|
| '''二重らせんの位相を決定する回転場'''
|
| def __init__(self, head_dim, base=10000):
|
| super().__init__()
|
| inv_freq = 1.0 / (base ** (torch.arange(0, head_dim, 2).float() / head_dim))
|
| self.register_buffer("inv_freq", inv_freq)
|
|
|
| def forward(self, x, seq_len):
|
| t = torch.arange(seq_len, device=x.device).type_as(self.inv_freq)
|
| freqs = torch.einsum("i,j->ij", t, self.inv_freq)
|
| emb = torch.cat((freqs, freqs), dim=-1)
|
| return emb.cos()[None, None, :, :], emb.sin()[None, None, :, :]
|
|
|
| def apply_drna_rope(q, k, cos, sin):
|
| '''Kによる動的位相変調済み cos/sin を受け取る'''
|
| def rotate_half(x):
|
| x1, x2 = x.chunk(2, dim=-1)
|
| return torch.cat((-x2, x1), dim=-1)
|
| return (q * cos) + (rotate_half(q) * sin), (k * cos) + (rotate_half(k) * sin)
|
|
|
| class DRNA_Block(nn.Module):
|
| '''DRNA共鳴ブロック:安定性を高めたPre-Norm直列共鳴構造'''
|
| def __init__(self, d_model, n_heads, head_dim, d_ff=None, dropout=0.1):
|
| super().__init__()
|
| self.n_heads = n_heads
|
| self.head_dim = head_dim
|
|
|
|
|
| self.norm1 = RMSNorm(d_model)
|
| self.qkv = nn.Linear(d_model, d_model * 3)
|
| self.out_proj = nn.Linear(d_model, d_model)
|
|
|
|
|
| self.norm2 = RMSNorm(d_model)
|
|
|
|
|
| if d_ff is None:
|
| d_ff = int(2 * (d_model * 4) / 3)
|
|
|
|
|
| self.w1 = nn.Linear(d_model, d_ff, bias=False)
|
| self.w3 = nn.Linear(d_model, d_ff, bias=False)
|
| self.w2 = nn.Linear(d_ff, d_model, bias=False)
|
|
|
| self.dropout = nn.Dropout(dropout)
|
|
|
| def forward(self, x, cos, sin, mask=None):
|
| b, s, d = x.shape
|
|
|
|
|
| residual = x
|
|
|
|
|
| x_norm1 = self.norm1(x)
|
|
|
|
|
|
|
| qkv = self.qkv(x_norm1).reshape(b, s, 3, self.n_heads, self.head_dim).permute(2, 0, 3, 1, 4)
|
| q, k, v = qkv[0], qkv[1], qkv[2]
|
|
|
|
|
|
|
|
|
| k_for_phase = torch.cat([torch.zeros_like(k[:, :, :1, :]), k[:, :, :-1, :]], dim=2)
|
|
|
|
|
| rt_phase = torch.tanh(k_for_phase) * math.pi
|
|
|
|
|
|
|
|
|
| d_cos = (cos * torch.cos(rt_phase)) - (sin * torch.sin(rt_phase))
|
| d_sin = (sin * torch.cos(rt_phase)) + (cos * torch.sin(rt_phase))
|
|
|
|
|
| q, k = apply_drna_rope(q, k, d_cos, d_sin)
|
|
|
|
|
| attn = (q @ k.transpose(-2, -1)) * (1.0 / math.sqrt(self.head_dim))
|
| if mask is not None:
|
| attn = attn + mask
|
|
|
| attn = F.softmax(attn, dim=-1)
|
| a_out_raw = (attn @ v).transpose(1, 2).reshape(b, s, d)
|
| a_out = self.out_proj(a_out_raw)
|
|
|
|
|
| x_norm2 = self.norm2(x)
|
|
|
|
|
| gate = F.silu(self.w1(x_norm2))
|
| current_value = self.w3(x_norm2)
|
|
|
|
|
| swiglu_out = gate * current_value
|
|
|
|
|
| m_out = self.w2(swiglu_out)
|
|
|
|
|
| x = residual + self.dropout(a_out) + self.dropout(m_out)
|
|
|
| return x
|
|
|
| class DRNA_Model(nn.Module):
|
| '''汎用 DRNA モデルコンテナ(安定化 Pre-Norm 版)'''
|
| def __init__(self, vocab_size, d_model=256, n_layers=16, n_heads=8, d_ff=1024):
|
| super().__init__()
|
| self.embed = nn.Embedding(vocab_size, d_model)
|
| self.head_dim = d_model // n_heads
|
| self.rope = DRNA_RoPE(self.head_dim)
|
|
|
| self.layers = nn.ModuleList([
|
| DRNA_Block(d_model, n_heads, self.head_dim, d_ff) for _ in range(n_layers)
|
| ])
|
|
|
|
|
| self.final_norm = RMSNorm(d_model)
|
| self.output_head = nn.Linear(d_model, vocab_size)
|
|
|
| def forward(self, x, mask=None, pad_id=None):
|
| b, s = x.shape
|
| device = x.device
|
| inputs = x
|
| x = self.embed(x)
|
|
|
| if mask is None or mask.sum() == 0:
|
|
|
|
|
| p_id = pad_id.item() if isinstance(pad_id, torch.Tensor) else pad_id
|
| pad_mask = (inputs != p_id).unsqueeze(1).unsqueeze(2) if isinstance(p_id, (int, float)) else torch.ones((1, 1, 1, s), device=device, dtype=torch.bool)
|
| causal = torch.triu(torch.ones(s, s, device=device), diagonal=1).bool().unsqueeze(0).unsqueeze(0)
|
|
|
|
|
| inf_value = torch.finfo(x.dtype).min if x.dtype != torch.float16 else -65500.0
|
|
|
|
|
| mask = torch.zeros((b, 1, s, s), device=device, dtype=x.dtype).masked_fill_(causal | (~pad_mask), inf_value)
|
|
|
| cos, sin = self.rope(x, x.size(1))
|
|
|
| for layer in self.layers:
|
| x = layer(x, cos, sin, mask=mask)
|
|
|
| x = self.final_norm(x)
|
| return self.output_head(x)
|
|
|
|
|
| '''
|
| 汎用型 D-RNA (Pre-Norm) License: Apache License 2.0 https://github.com/muooon/DRNA
|
| Attention is all you need_started, Resonance is all you need_endure,
|
| Neocognitron ― Transformer ― D‑RNA Dream Resonance Never Adjourns — it goes on...
|
| '''
|
|
|