1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
| """ Hierarchical Recurrent ANN (HR-ANN) 自上而下反馈的音视觉整合模型
基于 eLife 2026 论文
核心思想: 1. 视觉和音频各自有层级特征提取器 2. 高层语义通过反馈通路调制低层感知 3. 跨模态预测:视觉预测音频包络,音频预测视觉运动 """
import torch import torch.nn as nn import torch.nn.functional as F from typing import Tuple
class FeedbackLayer(nn.Module): """ 反馈层:接收高层语义,生成调制信号 模拟大脑皮层的反馈投射: - 注意力调制(gain control) - 预测编码(prediction error) - 跨模态预测 """ def __init__(self, feat_dim: int, context_dim: int): super().__init__() self.gate = nn.Linear(context_dim, feat_dim) self.predictor = nn.Linear(context_dim, feat_dim) self.cross_modal = nn.Linear(context_dim, feat_dim) def forward(self, low_feat: torch.Tensor, context: torch.Tensor) -> Tuple[torch.Tensor, torch.Tensor]: """ Args: low_feat: 低层特征 (B, T, F) context: 高层语义上下文 (B, T, C) Returns: modulated: 调制后的低层特征 prediction_error: 预测误差 """ gate = torch.sigmoid(self.gate(context)) modulated = low_feat * gate prediction = self.predictor(context) prediction_error = low_feat - prediction return modulated, prediction_error
class HRANN(nn.Module): """ 完整的 HR-ANN 模型 层级结构: L1: 低层感知(视觉/音频各自提取) L2: 中层整合(跨模态交互开始) L3: 高层语义(整合判定) 反馈:L3 → L2 → L1(自上而下调制) """ def __init__(self, visual_dim: int = 512, audio_dim: int = 128, hidden_dim: int = 256, n_classes: int = 5): super().__init__() self.v_enc1 = nn.Linear(visual_dim, hidden_dim) self.v_enc2 = nn.Linear(hidden_dim, hidden_dim) self.a_enc1 = nn.Linear(audio_dim, hidden_dim) self.a_enc2 = nn.Linear(hidden_dim, hidden_dim) self.fusion = nn.Linear(hidden_dim * 2, hidden_dim) self.semantic = nn.LSTM(hidden_dim, hidden_dim, batch_first=True) self.fb_l2_visual = FeedbackLayer(hidden_dim, hidden_dim) self.fb_l2_audio = FeedbackLayer(hidden_dim, hidden_dim) self.fb_l1_visual = FeedbackLayer(hidden_dim, hidden_dim) self.fb_l1_audio = FeedbackLayer(hidden_dim, hidden_dim) self.classifier = nn.Linear(hidden_dim, n_classes) self.n_iterations = 3 def forward(self, visual: torch.Tensor, audio: torch.Tensor) -> Tuple[torch.Tensor, dict]: """ Args: visual: (B, T, visual_dim) audio: (B, T, audio_dim) Returns: logits: (B, T, n_classes) info: 额外信息(预测误差等) """ B, T, _ = visual.shape v_l1 = F.gelu(self.v_enc1(visual)) a_l1 = F.gelu(self.a_enc1(audio)) v_l2 = F.gelu(self.v_enc2(v_l1)) a_l2 = F.gelu(self.a_enc2(a_l1)) fused = self.fusion(torch.cat([v_l2, a_l2], dim=-1)) semantic_out, _ = self.semantic(fused) total_pred_error = 0 for i in range(self.n_iterations): v_l2_mod, v_pe2 = self.fb_l2_visual(v_l2, semantic_out) a_l2_mod, a_pe2 = self.fb_l2_audio(a_l2, semantic_out) v_l1_mod, v_pe1 = self.fb_l1_visual(v_l1, v_l2_mod) a_l1_mod, a_pe1 = self.fb_l1_audio(a_l1, a_l2_mod) v_l2 = F.gelu(self.v_enc2(v_l1_mod)) a_l2 = F.gelu(self.a_enc2(a_l1_mod)) fused = self.fusion(torch.cat([v_l2, a_l2], dim=-1)) semantic_out, _ = self.semantic(fused) total_pred_error += v_pe2.mean() + a_pe2.mean() + v_pe1.mean() + a_pe1.mean() logits = self.classifier(semantic_out) return logits, { 'prediction_error': total_pred_error / self.n_iterations, 'semantic_repr': semantic_out, }
if __name__ == "__main__": model = HRANN(visual_dim=512, audio_dim=128, n_classes=5) visual = torch.randn(4, 30, 512) audio = torch.randn(4, 30, 128) logits, info = model(visual, audio) print(f"Logits: {logits.shape}") print(f"Prediction error: {info['prediction_error']:.4f}") print(f"Semantic repr: {info['semantic_repr'].shape}") print(f"Params: {sum(p.numel() for p in model.parameters()):,}")
|