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
| import torch import torch.nn as nn
class AttentionEnhancedCNN(nn.Module): """ 注意力增强CNN疲劳检测模型 架构: - Backbone: ResNet-18 - 注意力: SE-Net + CBAM - 动态时序: LSTM """ def __init__(self, num_classes=3): super().__init__() self.backbone = self._make_backbone() self.se_attention = SE_Block(512, 16) self.cbam = CBAM(512) self.lstm = nn.LSTM(512, 256, num_layers=2, batch_first=True) self.classifier = nn.Linear(256, num_classes) def forward(self, x_sequence): """ Args: x_sequence: (B, T, 3, 224, 224) 视频序列 Returns: logits: (B, num_classes) """ B, T, C, H, W = x_sequence.shape frame_features = [] for t in range(T): frame = x_sequence[:, t] feat = self.backbone(frame) feat = self.se_attention(feat) feat = self.cbam(feat) feat = feat.mean(dim=[2, 3]) frame_features.append(feat) sequence = torch.stack(frame_features, dim=1) lstm_out, _ = self.lstm(sequence) final_feat = lstm_out[:, -1] return self.classifier(final_feat) def _make_backbone(self): """构建ResNet-18 backbone""" import torchvision.models as models resnet = models.resnet18(pretrained=True) backbone = nn.Sequential(*list(resnet.children())[:-2]) return backbone
class SE_Block(nn.Module): """ Squeeze-and-Excitation注意力 通道注意力机制 """ def __init__(self, channels, reduction=16): super().__init__() self.squeeze = nn.AdaptiveAvgPool2d(1) self.excitation = nn.Sequential( nn.Linear(channels, channels // reduction), nn.ReLU(), nn.Linear(channels // reduction, channels), nn.Sigmoid() ) def forward(self, x): B, C, H, W = x.shape squeezed = self.squeeze(x).view(B, C) scale = self.excitation(squeezed).view(B, C, 1, 1) return x * scale
class CBAM(nn.Module): """ Convolutional Block Attention Module 通道+空间双注意力 """ def __init__(self, channels): super().__init__() self.channel_attention = ChannelAttention(channels) self.spatial_attention = SpatialAttention() def forward(self, x): x = self.channel_attention(x) x = self.spatial_attention(x) return x
class ChannelAttention(nn.Module): """通道注意力""" def __init__(self, channels, reduction=16): super().__init__() self.avg_pool = nn.AdaptiveAvgPool2d(1) self.max_pool = nn.AdaptiveMaxPool2d(1) self.fc = nn.Sequential( nn.Linear(channels, channels // reduction), nn.ReLU(), nn.Linear(channels // reduction, channels) ) self.sigmoid = nn.Sigmoid() def forward(self, x): B, C, H, W = x.shape avg_out = self.fc(self.avg_pool(x).view(B, C)) max_out = self.fc(self.max_pool(x).view(B, C)) scale = self.sigmoid(avg_out + max_out).view(B, C, 1, 1) return x * scale
class SpatialAttention(nn.Module): """空间注意力""" def __init__(self, kernel_size=7): super().__init__() self.conv = nn.Conv2d(2, 1, kernel_size, padding=kernel_size//2) self.sigmoid = nn.Sigmoid() def forward(self, x): avg_out = x.mean(dim=1, keepdim=True) max_out = x.max(dim=1, keepdim=True)[0] concat = torch.cat([avg_out, max_out], dim=1) scale = self.sigmoid(self.conv(concat)) return x * scale
|