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
| import torch import torch.nn as nn
class Conv3DBlock(nn.Module): """3D 卷积块""" def __init__(self, in_ch, out_ch, kernel_size=3, stride=1, padding=1): super().__init__() self.conv = nn.Conv3d(in_ch, out_ch, kernel_size, stride, padding) self.bn = nn.BatchNorm3d(out_ch) self.relu = nn.ReLU() def forward(self, x): return self.relu(self.bn(self.conv(x)))
class RNNBlock(nn.Module): """双向 GRU 循环块""" def __init__(self, input_dim, hidden_dim, num_layers=2): super().__init__() self.bigru = nn.GRU( input_dim, hidden_dim, num_layers, batch_first=True, bidirectional=True ) def forward(self, x): out, _ = self.bigru(x) return out
class PassengerEEGDecoder(nn.Module): """ PEDS: 3D-CRNN 联合 EEG 解码模型 论文 Section 3.3: 3D-CRNN 架构: 1. 3D CNN: 提取时空特征 (channels × time × freq) 2. ResNet: 残差连接增强梯度流 3. 双向 GRU: 捕获时序依赖 4. 全连接: 分类输出 输入: (B, 1, C, T, F) = (batch, 1, channels, time_steps, freq_bins) 输出: (B, 2) = (batch, rp_prob, di_prob) """ def __init__(self, num_channels=32, num_freq_bins=8, seq_len=125, hidden_dim=128, num_classes=2): super().__init__() self.conv1 = Conv3DBlock(1, 32, kernel_size=3, padding=1) self.conv2 = Conv3DBlock(32, 64, kernel_size=3, stride=2, padding=1) self.conv3 = Conv3DBlock(64, 128, kernel_size=3, stride=2, padding=1) self.pool = nn.AdaptiveAvgPool3d((None, 1, 1)) self.rnn = RNNBlock(128, hidden_dim, num_layers=2) self.classifier = nn.Sequential( nn.Linear(hidden_dim * 2, 64), nn.ReLU(), nn.Dropout(0.3), nn.Linear(64, num_classes) ) def forward(self, x): """ Args: x: (B, 1, C, T, F) EEG 时空-频谱表示 Returns: out: (B, 2) [RP_prob, DI_prob] """ x = self.conv1(x) x = self.conv2(x) x = self.conv3(x) x = self.pool(x) x = x.squeeze(-1).squeeze(-1) x = x.transpose(1, 2) x = self.rnn(x) x = x[:, -1, :] out = self.classifier(x) return out
if __name__ == "__main__": model = PassengerEEGDecoder( num_channels=32, num_freq_bins=8, seq_len=125 ) x = torch.randn(4, 1, 32, 125, 8) out = model(x) print(f"Input shape: {x.shape}") print(f"Output shape: {out.shape}") print(f"RP probs: {torch.softmax(out, dim=-1)[:, 0]}") print(f"DI probs: {torch.softmax(out, dim=-1)[:, 1]}") print(f"Parameters: {sum(p.numel() for p in model.parameters()):,}")
|