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
| import torch import torch.nn as nn
class CNN1D(nn.Module): """ 1D卷积神经网络 - 模型1 输入: 54维特征向量 输出: 7类情绪概率 """ def __init__(self, input_dim=54, num_classes=7, hidden_dims=[128, 64, 32]): super().__init__() layers = [] prev_dim = input_dim for dim in hidden_dims: layers.extend([ nn.Conv1d(prev_dim, dim, kernel_size=3, padding=1), nn.BatchNorm1d(dim), nn.ReLU(), nn.MaxPool1d(2), nn.Dropout(0.3) ]) prev_dim = dim self.features = nn.Sequential(*layers) self.classifier = nn.Linear(prev_dim, num_classes) def forward(self, x): x = self.features(x) x = x.mean(dim=2) return self.classifier(x)
class CNNBiLSTM(nn.Module): """ CNN-BiLSTM 模型 - 模型2 CNN提取局部特征,BiLSTM捕捉时序依赖 """ def __init__(self, input_dim=54, num_classes=7, cnn_channels=[64, 128], lstm_hidden=64): super().__init__() self.cnn = nn.Sequential( nn.Conv1d(input_dim, cnn_channels[0], kernel_size=3, padding=1), nn.BatchNorm1d(cnn_channels[0]), nn.ReLU(), nn.MaxPool1d(2), nn.Conv1d(cnn_channels[0], cnn_channels[1], kernel_size=3, padding=1), nn.BatchNorm1d(cnn_channels[1]), nn.ReLU(), nn.MaxPool1d(2), ) self.bilstm = nn.LSTM( input_size=cnn_channels[1], hidden_size=lstm_hidden, num_layers=2, bidirectional=True, batch_first=True, dropout=0.3 ) self.classifier = nn.Sequential( nn.Linear(lstm_hidden * 2, 64), nn.ReLU(), nn.Dropout(0.3), nn.Linear(64, num_classes) ) def forward(self, x): x = self.cnn(x) x = x.transpose(1, 2) lstm_out, _ = self.bilstm(x) out = lstm_out[:, -1, :] return self.classifier(out)
class EnsembleSER: """ 集成语音情绪识别 论文方法: 概率平均集成 模型1: 1D-CNN (~1.8M params) 模型2: CNN-BiLSTM (~0.5M params) 总计: ~2.3M params (vs Wav2Vec2 90M) """ def __init__(self, num_classes=7): self.model1 = CNN1D(num_classes=num_classes) self.model2 = CNNBiLSTM(num_classes=num_classes) self.emotion_labels = [ 'neutral', 'happy', 'sad', 'angry', 'fearful', 'disgust', 'surprised' ] def predict(self, features): """ 集成预测 Args: features: (54, L) 特征序列 Returns: dict: {emotion, confidence, probabilities} """ x = torch.FloatTensor(features).unsqueeze(0) with torch.no_grad(): p1 = torch.softmax(self.model1(x), dim=1) p2 = torch.softmax(self.model2(x), dim=1) ensemble_prob = (p1 + p2) / 2 pred_idx = ensemble_prob.argmax(dim=1).item() return { 'emotion': self.emotion_labels[pred_idx], 'confidence': ensemble_prob[0, pred_idx].item(), 'probabilities': { self.emotion_labels[i]: ensemble_prob[0, i].item() for i in range(len(self.emotion_labels)) } }
|