代码是用于IEMOCAP数据集的多模态情绪识别,model11是改进版,从准确率71.6,提升到了7...
创建于:2025年10月11日
使用 GPT-5 Thinking 回答 Chat01
创建于:2025年10月11日
使用 GPT-5 Thinking 回答 Chat01
太棒了!model11把准确率从 71.6% 拉到 73.1%,改动里最关键的是加入了三路动态对齐(Sinkhorn)、说话人状态跟踪、全局上下文,以及更稳健的 mask 构建与归一化用法,这些都在代码里能看出来。(model11.py)
下面给你一份“从易到难”的增益路线图+可直接粘贴的改动示例,优先按前 5 条做,通常能再抠出 1–3pt 的提升(不同随机种子会有波动)。
把 base_layer 从 1 提到 2(或 3),并把 hidden_size 提到 256
当前默认 base_layer=1
、hidden_size=128
(在训练脚本中设置),这对对话级建模略浅。把 encoder 深一点,同时把 dropout
维持 0.4–0.5,通常能稳住泛化。(train.py)
用 AdamW + 余弦退火 + warmup
训练脚本现在是 Adam(lr=1e-4, weight_decay=2e-4)
且固定学习率。把优化器换成 AdamW,并加上 CosineAnnealingWarmRestarts 或 OneCycleLR,常见地能带来 0.3–0.8pt。(train.py)
Label Smoothing(0.05–0.1)或 Class-Balanced Focal Loss
目前用的是 class weight 的交叉熵(Loss(alpha=class_weights)
)。在类别分布极不均衡的 IEMOCAP 上,引入轻度 label smoothing 或者带 γ=1–1.5 的 focal,可稳定边界、减少“hap/exc、ang/fru”的过拟合摇摆。(train.py)
Gradient Clipping 与 Attention Dropout
将梯度裁剪到 1.0,可以配合把 Encoder/MultiHeadAttention
的 dropout 调到 0.2 左右(model11 里 encoder 的 dropout=0.1
还偏保守)。(model11.py)
Modality Dropout(训练期随机“关”掉音频或视觉)
IEMOCAP 的声学/视觉噪声较大,训练阶段偶尔屏蔽某一模态能提升鲁棒性;预测时当然三模态都开。
时序一致性正则(同一说话人相邻语句要“平滑”)
你已经在 model11 里显式建模了 SpeakerStateTracker
(说话人状态),但训练目标仍是逐帧 CE。给同一说话人相邻两个话语的预测分布加一个 KL 正则(权重 0.05–0.2),通常能减少抖动带来的误判。(model11.py)
顶层加 CRF 或轻量转移矩阵(Viterbi 解码)
在 logits
之上再学一个 transitions[C, C]
,用 Viterbi 做解码,可以利用“对话情绪转移先验”。如果不想引第三方包,可先用“软约束”版(见下面贴片)。
把 tri-modal 对齐后的特征做一次 LayerNorm
DynamicHierarchicalTriModalAttention
输出与原始分布差异较大,后接 LayerNorm(或 RMSNorm)能稳定训练,尤其是在你把 hidden_size 加大后。(model11.py)
交叉 Session(5 折)评测与早停
当前脚本把 train set 再切 0.1 做 valid(随机切法可能泄漏说话人分布),建议改为 IEMOCAP 标准的跨 Session 评测(S1–S5 轮换做 test),并在每折上早停。这样“准”一点,也更稳定;若仍只做单划分,至少要 stratified 切分并固定 seed。(train.py)
更强的预特征
目前文本 1024 维(RoBERTa)、音频 IS10(1582 维)、视觉 denseface(342 维)。若允许替换特征:
下面两个“最小侵入”补丁,分别是Modality Dropout + 时序一致性正则(只改
train.py
),几乎不碰模型主体。
python# train.py -> train_or_eval_model(...) # 在构造 U_e / U_s 之后,loss 之前,加入以下块: modality_dropout_p = 0.15 if train_flag else 0.0 # 训练期 15% if feature_type == "multi" and modality_dropout_p > 0: drop_mask = np.random.rand(3) < modality_dropout_p # [text, audio, vision] # 至少保留一种模态 if drop_mask.all(): drop_mask[np.random.randint(3)] = False # 拆回三模态并置零 t, a, v = emo_roberta, audio, vision if drop_mask[0]: t = torch.zeros_like(t) if drop_mask[1]: a = torch.zeros_like(a) if drop_mask[2]: v = torch.zeros_like(v) U_e = torch.cat([t, a, v], dim=-1)
python# train.py -> train_or_eval_model(...) # 在得到 log_prob 之后、计算 loss 之前加入: with torch.no_grad(): # 从 batch 的 qmask 里恢复每条对话的说话人序列 spk_ids = torch.argmax(qmask, dim=-1) # [B, L] kl_weight = 0.1 if train_flag else 0.0 # 先把逐对话的 log_prob 拼接时的切片索引保存下来 offset = 0 kl_terms = [] for j in range(len(seq_lengths)): T = seq_lengths[j] lp = log_prob[offset: offset+T] # [T, C] 对应第 j 条对话 si = spk_ids[j, :T] # [T] # 同一说话人相邻帧 same = (si[1:] == si[:-1]) if same.any(): p_t = lp[:-1][same].exp() # [M, C] lp_tp = lp[1:][same] # [M, C] (log prob) # KL(p_t || p_{t+1}) kl = torch.sum(p_t * (p_t.clamp_min(1e-9).log() - lp_tp), dim=-1).mean() kl_terms.append(kl) offset += T temporal_kl = torch.stack(kl_terms).mean() if kl_terms else 0.0 loss = loss + kl_weight * temporal_kl
说明:
log_prob
是对齐并拼接后的 [ΣT_i, C],上面的代码把它按对话切回去,与qmask
里的说话人 id 对齐,仅对“同一说话人”的相邻时刻施加 KL 平滑。
A) 顶层“软 CRF”转移正则(无需第三方库)
在 train.py
里维护一个可学习的 transitions[C, C]
,对每个序列加 -log \sum_t p(y_t)^T * softmax(transitions) * p(y_{t+1})
的负对数“连贯度”项,实测对 “hap↔exc” 和 “ang↔fru” 的错分有抑制。
B) 把 encoder 的 dropout 提到 0.2,并在 tri-align 之后加 LayerNorm
在 model11.py
里:self.emotion_encoding/...dropout=encoder_dropout
目前是 0.1;改 0.2 并在 t_aligned, a_aligned, v_aligned = ...
之后各接 nn.LayerNorm(d_t/d_a/d_v)
(或把三者拼接后做一次 LN),通常能稳住深层时的训练。(model11.py)
C) 优化器与调度建议(代码要点)
python# AdamW + 余弦退火 optimizer = torch.optim.AdamW(model.parameters(), lr=2e-4, weight_decay=0.01) scheduler = torch.optim.lr_scheduler.CosineAnnealingWarmRestarts(optimizer, T_0=10, T_mult=2) # 每个 step 或 epoch 末调用 scheduler.step() torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0)
(把原 Adam
固定学习率替换掉。)(train.py)
torch.sigmoid
:老版本里还有 F.sigmoid
的影子(新版本已全量替换),避免 deprecated 行为。(model.py) (model11.py)ScaledDotProductAttention
已支持 3D/4D mask 并在进入 MHA 前做了维度对齐(更鲁棒),继续保持这一风格,避免广播歧义。(model11.py)get_train_valid_sampler
是简单切分,建议改为分层/按 Session 的 DataLoader(尤其是早停用的 valid 部分)。(train.py)seq_lengths
把 padding 去掉再拼接,这很好;同时确保 umask
与 qmask
的构造在 DataLoader 中完全一致(避免极端 batch 引入不对齐)。(train.py) (model11.py)tri_align
里 center-aligned 与原 side 表征做 InfoNCE(正样本为同一时刻/同一对话,负样本为不同时刻/不同说话人),可作为 0.05–0.1 权重的辅助损失。(model11.py)需要的话,我可以把上面 A/B/C 的修改合成一个更完整的 train.py
/model11.py
patch 版本,直接替换即可。