mirror of
https://github.com/hanzoai/sho.git
synced 2026-10-30 18:22:49 +00:00
258 lines
10 KiB
Python
258 lines
10 KiB
Python
"""
|
|
Genjo-Enso Integration Module
|
|
|
|
This module contains the integration code for combining Genjo (diffusion language model)
|
|
with Enso (diffusion MoE architecture).
|
|
"""
|
|
|
|
import torch
|
|
import torch.nn as nn
|
|
import torch.nn.functional as F
|
|
import numpy as np
|
|
import math
|
|
|
|
class MoEGate(nn.Module):
|
|
"""
|
|
Gating mechanism for Mixture of Experts that determines which experts to use for each token.
|
|
"""
|
|
def __init__(self, embed_dim, num_experts=16, num_experts_per_tok=2, aux_loss_alpha=0.01):
|
|
super().__init__()
|
|
self.top_k = num_experts_per_tok
|
|
self.n_routed_experts = num_experts
|
|
self.scoring_func = 'softmax'
|
|
self.alpha = aux_loss_alpha
|
|
self.seq_aux = False
|
|
self.norm_topk_prob = False
|
|
self.gating_dim = embed_dim
|
|
self.weight = nn.Parameter(torch.empty((self.n_routed_experts, self.gating_dim)))
|
|
self.reset_parameters()
|
|
|
|
def reset_parameters(self) -> None:
|
|
import torch.nn.init as init
|
|
init.kaiming_uniform_(self.weight, a=math.sqrt(5))
|
|
|
|
def forward(self, hidden_states):
|
|
bsz, seq_len, h = hidden_states.shape
|
|
|
|
# Compute gating score
|
|
hidden_states = hidden_states.view(-1, h)
|
|
logits = F.linear(hidden_states, self.weight, None)
|
|
if self.scoring_func == 'softmax':
|
|
scores = logits.softmax(dim=-1)
|
|
else:
|
|
raise NotImplementedError(f'Unsupported scoring function for MoE gating: {self.scoring_func}')
|
|
|
|
# Select top-k experts
|
|
topk_weight, topk_idx = torch.topk(scores, k=self.top_k, dim=-1, sorted=False)
|
|
|
|
# Normalize gate weights to sum to 1
|
|
if self.top_k > 1 and self.norm_topk_prob:
|
|
denominator = topk_weight.sum(dim=-1, keepdim=True) + 1e-20
|
|
topk_weight = topk_weight / denominator
|
|
|
|
# Calculate auxiliary loss for expert load balancing
|
|
if self.training and self.alpha > 0.0:
|
|
scores_for_aux = scores
|
|
aux_topk = self.top_k
|
|
topk_idx_for_aux_loss = topk_idx.view(bsz, -1)
|
|
if self.seq_aux:
|
|
scores_for_seq_aux = scores_for_aux.view(bsz, seq_len, -1)
|
|
ce = torch.zeros(bsz, self.n_routed_experts, device=hidden_states.device)
|
|
ce.scatter_add_(1, topk_idx_for_aux_loss, torch.ones(bsz, seq_len * aux_topk, device=hidden_states.device)).div_(seq_len * aux_topk / self.n_routed_experts)
|
|
aux_loss = (ce * scores_for_seq_aux.mean(dim=1)).sum(dim=1).mean() * self.alpha
|
|
else:
|
|
mask_ce = F.one_hot(topk_idx_for_aux_loss.view(-1), num_classes=self.n_routed_experts)
|
|
ce = mask_ce.float().mean(0)
|
|
Pi = scores_for_aux.mean(0)
|
|
fi = ce * self.n_routed_experts
|
|
aux_loss = (Pi * fi).sum() * self.alpha
|
|
else:
|
|
aux_loss = None
|
|
|
|
return topk_idx, topk_weight, aux_loss
|
|
|
|
class AddAuxiliaryLoss(torch.autograd.Function):
|
|
"""
|
|
Function to add auxiliary loss during backpropagation.
|
|
"""
|
|
@staticmethod
|
|
def forward(ctx, x, loss):
|
|
assert loss.numel() == 1
|
|
ctx.dtype = loss.dtype
|
|
ctx.required_aux_loss = loss.requires_grad
|
|
return x
|
|
|
|
@staticmethod
|
|
def backward(ctx, grad_output):
|
|
grad_loss = None
|
|
if ctx.required_aux_loss:
|
|
grad_loss = torch.ones(1, dtype=ctx.dtype, device=grad_output.device)
|
|
return grad_output, grad_loss
|
|
|
|
class MoEMLP(nn.Module):
|
|
"""
|
|
MLP with expert parallelism support.
|
|
"""
|
|
def __init__(self, hidden_size, intermediate_size, pretraining_tp=2):
|
|
super().__init__()
|
|
self.hidden_size = hidden_size
|
|
self.intermediate_size = intermediate_size
|
|
self.gate_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=False)
|
|
self.up_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=False)
|
|
self.down_proj = nn.Linear(self.intermediate_size, self.hidden_size, bias=False)
|
|
self.act_fn = nn.SiLU()
|
|
self.pretraining_tp = pretraining_tp
|
|
|
|
def forward(self, x):
|
|
if self.pretraining_tp > 1:
|
|
# Implementation with tensor parallelism
|
|
slice = self.intermediate_size // self.pretraining_tp
|
|
gate_proj_slices = self.gate_proj.weight.split(slice, dim=0)
|
|
up_proj_slices = self.up_proj.weight.split(slice, dim=0)
|
|
down_proj_slices = self.down_proj.weight.split(slice, dim=1)
|
|
|
|
gate_proj = torch.cat(
|
|
[F.linear(x, gate_proj_slices[i]) for i in range(self.pretraining_tp)], dim=-1
|
|
)
|
|
up_proj = torch.cat(
|
|
[F.linear(x, up_proj_slices[i]) for i in range(self.pretraining_tp)], dim=-1
|
|
)
|
|
|
|
intermediate_states = (self.act_fn(gate_proj) * up_proj).split(slice, dim=-1)
|
|
down_proj = [
|
|
F.linear(intermediate_states[i], down_proj_slices[i]) for i in range(self.pretraining_tp)
|
|
]
|
|
down_proj = sum(down_proj)
|
|
else:
|
|
# Standard implementation
|
|
down_proj = self.down_proj(self.act_fn(self.gate_proj(x)) * self.up_proj(x))
|
|
|
|
return down_proj
|
|
|
|
class GenjoSparseMoeBlock(nn.Module):
|
|
"""
|
|
Diffusion Transformer block with Sparse MoE for Genjo-Enso integration.
|
|
"""
|
|
def __init__(self, embed_dim, mlp_ratio=4, num_experts=16, num_experts_per_tok=2, pretraining_tp=2):
|
|
super().__init__()
|
|
self.num_experts_per_tok = num_experts_per_tok
|
|
self.experts = nn.ModuleList([
|
|
MoEMLP(hidden_size=embed_dim, intermediate_size=mlp_ratio * embed_dim, pretraining_tp=pretraining_tp)
|
|
for i in range(num_experts)
|
|
])
|
|
self.gate = MoEGate(embed_dim=embed_dim, num_experts=num_experts, num_experts_per_tok=num_experts_per_tok)
|
|
self.n_shared_experts = 2
|
|
|
|
if self.n_shared_experts is not None:
|
|
intermediate_size = embed_dim * self.n_shared_experts
|
|
self.shared_experts = MoEMLP(hidden_size=embed_dim, intermediate_size=intermediate_size, pretraining_tp=pretraining_tp)
|
|
|
|
def forward(self, hidden_states):
|
|
identity = hidden_states
|
|
orig_shape = hidden_states.shape
|
|
topk_idx, topk_weight, aux_loss = self.gate(hidden_states)
|
|
|
|
hidden_states = hidden_states.view(-1, hidden_states.shape[-1])
|
|
flat_topk_idx = topk_idx.view(-1)
|
|
|
|
if self.training:
|
|
# Training-time forward pass
|
|
hidden_states = hidden_states.repeat_interleave(self.num_experts_per_tok, dim=0)
|
|
y = torch.empty_like(hidden_states, dtype=hidden_states.dtype)
|
|
for i, expert in enumerate(self.experts):
|
|
y[flat_topk_idx == i] = expert(hidden_states[flat_topk_idx == i]).float()
|
|
y = (y.view(*topk_weight.shape, -1) * topk_weight.unsqueeze(-1)).sum(dim=1)
|
|
y = y.view(*orig_shape)
|
|
y = AddAuxiliaryLoss.apply(y, aux_loss)
|
|
else:
|
|
# Optimized inference-time forward pass
|
|
y = self.moe_infer(hidden_states, flat_topk_idx, topk_weight.view(-1, 1)).view(*orig_shape)
|
|
|
|
# Add shared expert output if configured
|
|
if self.n_shared_experts is not None:
|
|
y = y + self.shared_experts(identity)
|
|
|
|
return y
|
|
|
|
@torch.no_grad()
|
|
def moe_infer(self, x, flat_expert_indices, flat_expert_weights):
|
|
"""
|
|
Optimized inference routing tokens to the same expert in batches.
|
|
"""
|
|
expert_cache = torch.zeros_like(x)
|
|
idxs = flat_expert_indices.argsort()
|
|
tokens_per_expert = flat_expert_indices.bincount().cpu().numpy().cumsum(0)
|
|
token_idxs = idxs // self.num_experts_per_tok
|
|
|
|
for i, end_idx in enumerate(tokens_per_expert):
|
|
start_idx = 0 if i == 0 else tokens_per_expert[i-1]
|
|
if start_idx == end_idx:
|
|
continue
|
|
|
|
expert = self.experts[i]
|
|
exp_token_idx = token_idxs[start_idx:end_idx]
|
|
expert_tokens = x[exp_token_idx]
|
|
expert_out = expert(expert_tokens)
|
|
expert_out.mul_(flat_expert_weights[idxs[start_idx:end_idx]])
|
|
|
|
# For fp16 and other dtype compatibility
|
|
expert_cache = expert_cache.to(expert_out.dtype)
|
|
expert_cache.scatter_reduce_(0, exp_token_idx.view(-1, 1).repeat(1, x.shape[-1]), expert_out, reduce='sum')
|
|
|
|
return expert_cache
|
|
|
|
def create_genjo_moe_model(base_model, num_experts=8, num_experts_per_tok=2):
|
|
"""
|
|
Create a Genjo-MoE model by replacing standard MLP blocks with MoE blocks.
|
|
|
|
Args:
|
|
base_model: The base Genjo model
|
|
num_experts: Number of experts in each MoE layer
|
|
num_experts_per_tok: Number of experts to route each token to
|
|
|
|
Returns:
|
|
Modified model with MoE layers
|
|
"""
|
|
# This function will be implemented as part of the integration
|
|
# It will take a pre-trained Genjo model and convert its MLP layers to MoE layers
|
|
# Return the converted model
|
|
pass
|
|
|
|
def convert_checkpoint(genjo_checkpoint, moe_config):
|
|
"""
|
|
Convert a Genjo checkpoint to Genjo-MoE format.
|
|
|
|
Args:
|
|
genjo_checkpoint: Path to the Genjo checkpoint
|
|
moe_config: Configuration for MoE layers
|
|
|
|
Returns:
|
|
Converted model checkpoint
|
|
"""
|
|
# This function will be implemented as part of the integration
|
|
# It will take a pre-trained Genjo checkpoint and create MoE layer weights
|
|
# Return the converted checkpoint
|
|
pass
|
|
|
|
def optimize_generation(model, prompt, steps, gen_length, block_length, **kwargs):
|
|
"""
|
|
Optimized generation function for Genjo-MoE models.
|
|
|
|
Args:
|
|
model: The Genjo-MoE model
|
|
prompt: Input prompt
|
|
steps: Number of denoising steps
|
|
gen_length: Maximum generation length
|
|
block_length: Block size for semi-autoregressive generation
|
|
**kwargs: Additional arguments
|
|
|
|
Returns:
|
|
Generated output
|
|
"""
|
|
# This function will be implemented as part of the integration
|
|
# It will provide optimized generation using MoE architecture
|
|
pass
|
|
|
|
if __name__ == "__main__":
|
|
# Sample usage code will be added when the integration is complete
|
|
pass |