mHC

mHC

In [ ]:
import torch
import torch.nn as nn

# ==========================================
# 1. 前置依赖 (沿用之前的核心逻辑)
# ==========================================
def sinkhorn_knopp(M, n_iters=20, tau=1.0):
    H = torch.exp(M / tau)
    for _ in range(n_iters):
        H = H / (H.sum(dim=-1, keepdim=True) + 1e-8)
        H = H / (H.sum(dim=-2, keepdim=True) + 1e-8)
    return H

class mHCBlock(nn.Module):
    def __init__(self, dim, n_streams=4):
        super().__init__()
        self.dim = dim
        self.n = n_streams
        total_dim = n_streams * dim
        
        self.norm = nn.RMSNorm(total_dim)
        self.proj_pre  = nn.Linear(total_dim, n_streams)
        self.proj_post = nn.Linear(total_dim, n_streams)
        self.proj_res  = nn.Linear(total_dim, n_streams * n_streams)
        
        self.alpha_pre  = nn.Parameter(torch.tensor(0.01))
        self.alpha_post = nn.Parameter(torch.tensor(0.01))
        self.alpha_res  = nn.Parameter(torch.tensor(0.01))

    def forward(self, x, layer_func):
        B, L, N, C = x.shape
        x_global = x.view(B, L, -1)
        x_norm = self.norm(x_global)
        
        # 计算路由权重
        H_pre = torch.sigmoid(self.alpha_pre * self.proj_pre(x_norm)).view(B, L, N, 1)
        H_post = 2.0 * torch.sigmoid(self.alpha_post * self.proj_post(x_norm)).view(B, L, N, 1)
        H_res = sinkhorn_knopp(self.alpha_res * self.proj_res(x_norm).view(B, L, N, N))

        # A. 聚合
        x_main_in = (x * H_pre).sum(dim=2) 
        # B. 主干计算
        x_main_out = layer_func(x_main_in)
        # C. 残差互通
        x_res_mixed = torch.matmul(H_res, x)
        # D. 广播分发
        x_next = x_res_mixed + H_post * x_main_out.unsqueeze(2)
        
        return x_next

# ==========================================
# 2. 核心处理厂:标准的前馈神经网络 (FFN)
# ==========================================
class FeedForward(nn.Module):
    """标准的 MLP 处理模块,它完全不需要知道外面有 mHC 的存在"""
    def __init__(self, dim, hidden_dim):
        super().__init__()
        self.net = nn.Sequential(
            nn.Linear(dim, hidden_dim),
            nn.GELU(),
            nn.Linear(hidden_dim, dim)
        )
        
    def forward(self, x):
        return self.net(x)

# ==========================================
# 3. 宏观架构:基于 mHC 的分类器
# ==========================================
class mHC_SequenceClassifier(nn.Module):
    def __init__(self, vocab_size, dim, n_streams, num_layers, out_classes):
        super().__init__()
        self.dim = dim
        self.n = n_streams
        
        # 1. 输入层:将词元映射为向量 [B, L, C]
        self.embedding = nn.Embedding(vocab_size, dim)
        
        # 2. 拓宽车道:把 1 条 C 维的车道,强行拆解成 n 条 C 维的车道
        self.expand = nn.Linear(dim, n_streams * dim)
        
        # 3. 堆叠 mHC 层和 FFN 层
        self.mhc_blocks = nn.ModuleList([mHCBlock(dim, n_streams) for _ in range(num_layers)])
        self.ffn_layers = nn.ModuleList([FeedForward(dim, dim * 4) for _ in range(num_layers)])
        
        # 4. 压缩车道:把 n 条车道的信息压平,合并回 1 条 C 维车道
        self.collapse = nn.Linear(n_streams * dim, dim)
        
        # 5. 输出层:简单的池化 + 分类头
        self.head = nn.Linear(dim, out_classes)

    def forward(self, x):
        B, L = x.shape
        
        # [B, L] -> [B, L, C]
        x = self.embedding(x)
        
        # [B, L, C] -> [B, L, N*C] -> [B, L, N, C]
        # 此时数据正式进入 4 条并行的残差流
        x = self.expand(x).view(B, L, self.n, self.dim)
        
        # 逐层穿过 mHC 模块
        for mhc, ffn in zip(self.mhc_blocks, self.ffn_layers):
            # 将 ffn 作为 callable 函数传给 mhc
            x = mhc(x, ffn)
            
        # 离开多数据流区域,将 [B, L, N, C] 压平为 [B, L, N*C]
        x = x.view(B, L, -1)
        
        # 合并回单主干道 [B, L, C]
        x = self.collapse(x)
        
        # 序列池化:取所有 Token 的平均特征 [B, C]
        x_pooled = x.mean(dim=1)
        
        # 输出预测结果 [B, out_classes]
        logits = self.head(x_pooled)
        return logits

# ==========================================
# 4. 测试与验证
# ==========================================
if __name__ == "__main__":
    # 超参数设置
    BATCH_SIZE = 8
    SEQ_LEN = 128
    VOCAB_SIZE = 1000
    DIM = 256
    N_STREAMS = 4
    NUM_LAYERS = 6
    NUM_CLASSES = 10

    # 实例化模型
    model = mHC_SequenceClassifier(
        vocab_size=VOCAB_SIZE, 
        dim=DIM, 
        n_streams=N_STREAMS, 
        num_layers=NUM_LAYERS, 
        out_classes=NUM_CLASSES
    )
    
    # 将模型扔到 GPU 上(如果你在 Ubuntu 环境中执行,可以开启)
    device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
    model = model.to(device)
    
    # 构造假输入数据
    dummy_input = torch.randint(0, VOCAB_SIZE, (BATCH_SIZE, SEQ_LEN)).to(device)
    
    # 前向传播
    print("🚀 开始前向传播...")
    outputs = model(dummy_input)
    print(f"✅ 输出张量维度: {outputs.shape} (预期: [{BATCH_SIZE}, {NUM_CLASSES}])")
    
    # 模拟一次反向传播
    print("\n🚀 模拟计算 Loss 并反向传播...")
    loss = outputs.sum()
    loss.backward()
    print("✅ 反向传播完成!")
    
    # 检查梯度是否正常(通过 Sinkhorn 约束,梯度应该非常稳定)
    grad_norm = model.mhc_blocks[-1].proj_res.weight.grad.norm().item()
    print(f"📊 最后一层 mHC proj_res 的梯度范数: {grad_norm:.4f}")
In [ ]:
 

评论(0)

评论加载中…