情感几何与类别自适应融合——ECCV 2026 多模态情感识别对驾驶员情绪检测的启示

论文信息

  • 标题: Multimodal Emotion Recognition in Conversations via Class-Wise Adaptive Modality Fusion and Affective Geometry
  • 作者: Oriol Marín, Roger Marí, Gloria Haro, Rafael Redondo
  • 机构: Eurecat + Universitat Pompeu Fabra (巴塞罗那)
  • 会议: ECCV 2026 ABAW Workshop
  • arXiv: 2609.09924
  • 代码: https://github.com/multimedia-eurecat/classwise-multimodal-ERC

核心创新

  1. 几何增强视觉表征:外观特征 + 面部几何描述符,F1 提升 +4.36 (IEMOCAP)
  2. 类别自适应模态融合:不同情绪类别下各模态权重不同
  3. Valence-Arousal 先验:情感转移 utterance 准确率 +0.74
  4. 三项互补:几何/自适应/情感先验提供互补增益

方法详解

1. 三大改进

改进 问题 方案 增益
几何增强 外观特征混杂身份/光照 +FACS 几何描述符 F1 +4.36 (IEMOCAP)
类别自适应融合 softmax 门对所有情绪相同 按预测类别调整模态权重 F1 +0.25 (IEMOCAP)
VA 先验 离散分类忽略情感空间结构 Valence-Arousal 先验修正 转移 utterance +0.74

2. SDT 基础架构

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
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
"""
Self-Distillation Transformer (SDT) + 三项改进

基础: 文本 + 音频 + 视觉 → intra-modal Transformer → inter-modal Transformer
→ sigmoid 门 → softmax 门 → 分类

改进1: 视觉 = 外观(ViT) + 几何(FACS)
改进2: softmax 门 → 类别自适应门
改进3: + Valence-Arousal 先验
"""

import torch
import torch.nn as nn
import torch.nn.functional as F
from typing import Dict, Tuple, List
import numpy as np

class GeometryEnhancedVisualEncoder(nn.Module):
"""
几何增强视觉编码器

论文核心改进1: 外观特征 + 面部几何描述符

外观: ViT 提取全局面部表征
几何: FACS Action Unit 描述面部结构变化
"""
def __init__(self, appearance_dim: int = 768,
geometry_dim: int = 51, # FACS AU 数
hidden_dim: int = 256):
super().__init__()
# 外观编码 (冻结 ViT)
self.appearance_proj = nn.Linear(appearance_dim, hidden_dim)

# 几何编码 (可训练)
self.geometry_encoder = nn.Sequential(
nn.Linear(geometry_dim, 128),
nn.ReLU(),
nn.Linear(128, hidden_dim),
nn.ReLU()
)

# 融合
self.fusion = nn.Sequential(
nn.Linear(hidden_dim * 2, hidden_dim),
nn.LayerNorm(hidden_dim),
nn.GELU()
)

def forward(self, appearance_feat: torch.Tensor,
geometry_feat: torch.Tensor) -> torch.Tensor:
"""
Args:
appearance_feat: ViT 特征, shape=(B, T, 768)
geometry_feat: FACS AU, shape=(B, T, 51)

Returns:
visual_repr: 增强视觉表征, shape=(B, T, hidden_dim)
"""
app = self.appearance_proj(appearance_feat)
geo = self.geometry_encoder(geometry_feat)
return self.fusion(torch.cat([app, geo], dim=-1))


class ClassWiseAdaptiveFusion(nn.Module):
"""
类别自适应模态融合

论文核心改进2: 不同情绪类别下,文本/音频/视觉权重不同

例如:
- 愤怒: 音频权重高(语气强烈)
- 悲伤: 视觉权重高(面部表情)
- 中性: 文本权重高
"""
def __init__(self, n_modalities: int = 3, n_classes: int = 7,
hidden_dim: int = 256):
super().__init__()
# 每个类别有独立的模态权重
self.class_modality_weights = nn.Parameter(
torch.randn(n_classes, n_modalities) * 0.1 + 1.0
)

def forward(self, modalities: Dict[str, torch.Tensor],
class_logits: torch.Tensor) -> torch.Tensor:
"""
Args:
modalities: {'text': (B,T,D), 'audio': (B,T,D), 'visual': (B,T,D)}
class_logits: 当前预测 logits, shape=(B, n_classes)

Returns:
fused: 融合表征, shape=(B, T, D)
"""
# 用 logits 的 softmax 作为类别概率
class_probs = F.softmax(class_logits, dim=-1) # (B, C)

# 类别概率 × 模态权重 → 加权
weights = torch.einsum('bc,cm->bm',
class_probs,
self.class_modality_weights) # (B, M)
weights = F.softmax(weights, dim=-1) # 归一化

# 加权融合
mod_tensor = torch.stack([
modalities['text'],
modalities['audio'],
modalities['visual']
], dim=1) # (B, M, T, D)

weights = weights.unsqueeze(-1).unsqueeze(-1) # (B, M, 1, 1)
fused = (mod_tensor * weights).sum(dim=1) # (B, T, D)

return fused


class ValenceArousalPrior(nn.Module):
"""
Valence-Arousal 先验

论文核心改进3: 利用情感在 VA 空间的结构

情感转移 utterance (如 sad→joy) 分类困难
VA 先验: 将类别映射到 VA 空间,对转移 utterance 施加修正
"""

# 情感类别在 VA 空间的中心 (Valence, Arousal)
EMOTION_VA_CENTERS = {
'neutral': (0.0, 0.0),
'happy': (0.8, 0.5),
'sad': (-0.7, -0.4),
'angry': (0.2, 0.8),
'fearful': (-0.6, 0.7),
'disgust': (-0.5, 0.3),
'surprised': (0.6, 0.9),
}

def __init__(self, n_classes: int = 7, va_dim: int = 2):
super().__init__()
# 可学习的 VA 投影
self.va_projection = nn.Linear(n_classes, va_dim)
# 情感转移修正
self.shift_correction = nn.Linear(va_dim * 2, n_classes)

def forward(self, class_logits: torch.Tensor,
prev_logits: torch.Tensor = None) -> torch.Tensor:
"""
Args:
class_logits: 当前 utterance logits, shape=(B, C)
prev_logits: 上一 utterance logits (情感转移), shape=(B, C)

Returns:
corrected_logits: 修正后 logits
"""
if prev_logits is None:
return class_logits

# 计算当前和上一次的 VA 位置
curr_va = self.va_projection(class_logits) # (B, 2)
prev_va = self.va_projection(prev_logits) # (B, 2)

# VA 位移 → 修正
va_shift = torch.cat([curr_va, prev_va], dim=-1) # (B, 4)
correction = self.shift_correction(va_shift) # (B, C)

return class_logits + 0.1 * correction


class EnhancedSDT(nn.Module):
"""
增强版 SDT: 三项改进集成

完整管道:
1. 文本编码 (RoBERTa)
2. 音频编码 (WavLM)
3. 视觉编码 (ViT + FACS) ← 改进1
4. Intra-modal Transformer
5. Inter-modal Transformer
6. Sigmoid 门 → 模态增强
7. 类别自适应融合 ← 改进2
8. 分类 + VA 先验修正 ← 改进3
"""
def __init__(self, n_classes: int = 7, hidden_dim: int = 256):
super().__init__()
self.n_classes = n_classes

# 编码器 (简化)
self.text_encoder = nn.Linear(1024, hidden_dim) # RoBERTa
self.audio_encoder = nn.Linear(1024, hidden_dim) # WavLM
self.visual_encoder = GeometryEnhancedVisualEncoder(
appearance_dim=768, geometry_dim=51, hidden_dim=hidden_dim
)

# Intra-modal Transformer
self.intra_modal = nn.TransformerEncoder(
nn.TransformerEncoderLayer(hidden_dim, nhead=4, batch_first=True),
num_layers=2
)

# Inter-modal (cross-attention)
self.cross_attn = nn.MultiheadAttention(hidden_dim, 4, batch_first=True)

# 融合
self.adaptive_fusion = ClassWiseAdaptiveFusion(
n_modalities=3, n_classes=n_classes, hidden_dim=hidden_dim
)

# 分类
self.classifier = nn.Linear(hidden_dim, n_classes)

# VA 先验
self.va_prior = ValenceArousalPrior(n_classes=n_classes)

def forward(self, text_feat, audio_feat,
appearance_feat, geometry_feat,
prev_logits=None) -> Dict[str, torch.Tensor]:
"""
Args:
text_feat: (B, T, 1024)
audio_feat: (B, T, 1024)
appearance_feat: (B, T, 768) ViT
geometry_feat: (B, T, 51) FACS AU
prev_logits: (B, C) 上一轮分类

Returns:
logits, fused_repr
"""
# 编码
t = self.text_encoder(text_feat)
a = self.audio_encoder(audio_feat)
v = self.visual_encoder(appearance_feat, geometry_feat)

# Intra-modal
t = self.intra_modal(t)
a = self.intra_modal(a)
v = self.intra_modal(v)

# Inter-modal (cross-attention)
t_attended, _ = self.cross_attn(t, torch.cat([a, v], dim=1), torch.cat([a, v], dim=1))
a_attended, _ = self.cross_attn(a, torch.cat([t, v], dim=1), torch.cat([t, v], dim=1))
v_attended, _ = self.cross_attn(v, torch.cat([t, a], dim=1), torch.cat([t, a], dim=1))

# 初始分类 (用于自适应融合)
pooled = torch.cat([t_attended.mean(1), a_attended.mean(1),
v_attended.mean(1)], dim=-1)
initial_logits = self.classifier(pooled[:, :256])

# 类别自适应融合
fused = self.adaptive_fusion({
'text': t_attended, 'audio': a_attended, 'visual': v_attended
}, initial_logits)

# 最终分类
logits = self.classifier(fused.mean(1))

# VA 先验修正
if prev_logits is not None:
logits = self.va_prior(logits, prev_logits)

return {'logits': logits, 'fused_repr': fused}


# IMS 驾驶员情绪检测应用
class DriverEmotionDetector:
"""
IMS 驾驶员情绪检测器

基于 ECCV 2026 论文方法

应用场景:
1. 路怒症检测 (angry + 高 arousal)
2. 疲劳情绪 (sad + 低 arousal)
3. 焦虑检测 (fearful + 中 arousal)
4. 满意/愉悦 (happy + 中 arousal)

输入: DMS 摄像头 + 麦克风
"""
def __init__(self):
self.model = EnhancedSDT(n_classes=7)
self.emotion_actions = {
'angry': {'risk': 3, 'action': '路怒警告', 'threshold': 0.7},
'sad': {'risk': 2, 'action': '疲劳情绪', 'threshold': 0.6},
'fearful': {'risk': 2, 'action': '焦虑检测', 'threshold': 0.6},
'happy': {'risk': 0, 'action': '正常', 'threshold': 0.5},
'neutral': {'risk': 0, 'action': '正常', 'threshold': 0.5},
'disgust': {'risk': 1, 'action': '不适', 'threshold': 0.6},
'surprised': {'risk': 1, 'action': '惊讶', 'threshold': 0.7},
}

def classify(self, appearance_feat, geometry_feat,
audio_feat, text_feat=None,
prev_emotion=None) -> dict:
"""
分类驾驶员情绪

Args:
appearance_feat: 面部 ViT 特征 (B, T, 768)
geometry_feat: FACS AU (B, T, 51) — 51 个 Action Unit
audio_feat: 语音特征 (B, T, 1024)
text_feat: 转录文本特征 (可选)
prev_emotion: 上一帧情绪 logits
"""
if text_feat is None:
text_feat = torch.zeros(appearance_feat.shape[0],
appearance_feat.shape[1], 1024)

output = self.model(text_feat, audio_feat,
appearance_feat, geometry_feat,
prev_emotion)

logits = output['logits']
probs = F.softmax(logits, dim=-1)

# 风险评估
emotions = list(self.emotion_actions.keys())
risk_scores = torch.zeros_like(probs)
for i, emo in enumerate(emotions):
risk_scores[:, i] = self.emotion_actions[emo]['risk']

total_risk = (probs * risk_scores).sum(dim=-1)

return {
'emotions': {emotions[i]: probs[0, i].item()
for i in range(len(emotions))},
'dominant_emotion': emotions[probs[0].argmax().item()],
'risk_score': total_risk[0].item(),
'logits': logits
}


# 测试
if __name__ == "__main__":
print("=== 增强版 SDT 测试 ===")
model = EnhancedSDT(n_classes=7, hidden_dim=256)

B, T = 2, 5 # 2 个样本, 5 个 utterance
text_feat = torch.randn(B, T, 1024)
audio_feat = torch.randn(B, T, 1024)
appearance_feat = torch.randn(B, T, 768)
geometry_feat = torch.randn(B, T, 51) # FACS AU

output = model(text_feat, audio_feat, appearance_feat, geometry_feat)
print(f"输入: text={text_feat.shape}, audio={audio_feat.shape}")
print(f" appearance={appearance_feat.shape}, geometry={geometry_feat.shape}")
print(f"输出: logits={output['logits'].shape}")

# 情感转移测试
prev_logits = torch.randn(B, 7)
output_shift = model(text_feat, audio_feat, appearance_feat,
geometry_feat, prev_logits)
print(f"VA 先验: 转移 utterance logits={output_shift['logits'].shape}")

# IMS 驾驶员情绪检测
detector = DriverEmotionDetector()
result = detector.classify(appearance_feat, geometry_feat, audio_feat)
print(f"\n=== 驾驶员情绪检测结果 ===")
print(f"主导情绪: {result['dominant_emotion']}")
print(f"风险评分: {result['risk_score']:.2f}")
for emo, prob in sorted(result['emotions'].items(),
key=lambda x: -x[1]):
print(f" {emo}: {prob:.1%}")

# 论文性能
print(f"\n=== 论文性能报告 ===")
print(f"{'数据集':<12} {'改进1(几何)':<15} {'改进2(融合)':<15} {'改进3(VA)'}")
print(f"{'MELD':<12} {'+0.27 F1':<15} {'+0.17 F1':<15} {'+0.30 acc'}")
print(f"{'IEMOCAP':<12} {'+4.36 F1':<15} {'+0.25 F1':<15} {'+0.74 acc'}")
print(f"\n关键: 三项改进互补, 可叠加使用")

3. 几何增强为什么有效

特征类型 捕获信息 优势 局限
外观 (ViT) 全局面部表征 丰富语义 混杂身份/光照
几何 (FACS) AU 强度 纯表情信息 需要精确 AU 提取
外观+几何 互补 F1 +4.36 需要 AU 预处理

4. 类别自适应融合的直觉

graph LR
    A[文本] --> D[融合]
    B[音频] --> D
    C[视觉] --> D
    D --> E{情绪类别?}
    E -->|愤怒| F[音频权重↑]
    E -->|悲伤| G[视觉权重↑]
    E -->|中性| H[文本权重↑]
    E -->|惊讶| I[视觉+音频↑]
    F --> J[最终分类]
    G --> J
    H --> J
    I --> J

IMS 驾驶员情绪检测应用

1. 情绪→风险映射

情绪 Valence Arousal 风险等级 IMS 动作
愤怒 (路怒) -0.2 0.8 🔴 3 一级警告 + 音乐降躁
悲伤 (抑郁) -0.7 -0.4 🟡 2 二级提醒 + 建议休息
恐惧 (焦虑) -0.6 0.7 🟡 2 二级提醒 + 减速建议
厌恶 -0.5 0.3 🟠 1 记录 + 适当时提醒
惊讶 0.6 0.9 🟠 1 记录 + 注意力评估
愉快 0.8 0.5 🟢 0
中性 0.0 0.0 🟢 0

2. DMS 摄像头可提取的 FACS AU

AU 编号 名称 驾驶员情绪关联
AU4 皱眉 愤怒/专注/困惑
AU5 上眼睑提升 惊讶
AU12 嘴角上拉 微笑/愉悦
AU15 嘴角下拉 悲伤/不满
AU20 嘴唇拉伸 恐惧
AU23 嘴唇收紧 愤怒
AU24 嘴唇按压 专注/紧张
AU26 下巴下拉 惊讶/说话

3. 与疲劳/分心检测的融合

检测目标 主特征 情绪辅助 融合策略
疲劳检测 PERCLOS + 眨眼 悲伤/中性 → 疲劳概率↑ 情绪作为先验
分心检测 视线偏离 + AU 厌恶/惊讶 → 分心概率↑ 情绪作为辅助特征
路怒检测 音频+AU4+AU23 愤怒概率 > 0.7 情绪作为主特征
酒驾检测 驾驶行为+语音 情绪波动大 → 损伤概率↑ 情绪作为风险评估

4. 硬件需求

组件 型号 用途 成本
DMS 摄像头 OV2311 面部外观 + AU 提取 $4
麦克风 阵列麦克风 语音情感 $2
处理器 QCS8255 ViT + AU + 融合
总成本 $6

测试场景

EM-01 路怒症检测

前置条件:

  • DMS 摄像头 + 麦克风正常工作
  • AU 提取模型已加载
  • 情绪分类器已训练

测试步骤:

  1. 正常驾驶 60s(基线情绪)
  2. 模拟被加塞 → 触发愤怒情绪
  3. 持续 30s 愤怒状态
  4. 恢复正常驾驶 30s

判定条件:

检测项 通过条件 失败条件
情绪识别延迟 ≤ 5s > 10s
愤怒概率 ≥ 70% < 50%
风险等级 3 < 2
误报率 < 10% > 20%
恢复检测 ≤ 10s > 20s

开发启示

  1. FACS AU 是情绪检测的关键视觉特征:外观特征+几何特征比单独外观 F1 提升 4.36
  2. 不同情绪依赖不同模态:愤怒靠音频,悲伤靠视觉,中性靠文本 → 类别自适应融合
  3. VA 空间结构解决情感转移难题:sad→joy 的转移 utterance 准确率提升 0.74
  4. 情绪检测需时序上下文:孤立 utterance 情绪分类困难,需要对话上下文
  5. DMS 摄像头可提取 AU:OpenFace 可提取 18 AU,满足几何特征需求
  6. 路怒症是最高价值应用:愤怒+高 arousal = 高风险,可直接触发干预
  7. 情绪作为疲劳/分心的辅助特征:悲伤+中性 → 疲劳概率↑,厌恶+惊讶 → 分心概率↑
  8. 成本极低:仅需 DMS 摄像头+麦克风,$6 BOM

总结

ECCV 2026 这篇论文为 IMS 驾驶员情绪检测提供了三项关键技术:

  1. 几何增强:FACS AU + 外观特征,F1 +4.36,无需额外硬件
  2. 类别自适应融合:不同情绪依赖不同模态,自动调整权重
  3. VA 先验:情感空间结构修正转移 utterance,+0.74 accuracy
  4. 路怒症检测是最高价值应用:愤怒+高 arousal → 风险等级 3 → 一级警告
  5. 情绪是疲劳/分心的辅助特征:情绪概率作为先验提升检测精度

https://dapalm.com/2026/09/15/2026-09-15-affective-geometry-classwise-fusion-driver-emotion-eccv2026/
作者
Mars
发布于
2026年9月15日
许可协议