FatigueNet:GNN+Transformer混合架构实时多模态疲劳检测(Nature 2025)

FatigueNet:GNN+Transformer混合架构实时多模态疲劳检测

论文来源: Nature Scientific Reports
期刊: Scientific Reports, September 30, 2025
核心创新: 图神经网络 + Transformer 混合架构 → 多模态融合疲劳检测


论文信息

项目 内容
标题 FatigueNet: A hybrid graph neural network and transformer framework for real-time multimodal fatigue detection
期刊 Nature Scientific Reports
日期 September 30, 2025
创新 GNN处理结构化数据 + Transformer处理时序数据

核心问题:多模态疲劳检测

单一模态局限:

1
2
3
4
5
6
7
8
仅视觉:光照、遮挡影响大
仅生理信号:传感器不便佩戴
仅车辆行为:个体差异大

多模态融合优势:
- 视觉 + 生理 + 车辆 = 互补增强
- 融合精度 > 单一模态
- Euro NCAP 2026推荐多模态

FatigueNet架构

GNN + Transformer 混合设计

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
import torch
import torch.nn as nn
import torch.nn.functional as F

class FatigueNet(nn.Module):
"""
FatigueNet: GNN + Transformer 混合架构

Nature 2025 论文核心:
1. GNN处理图结构数据(人脸关键点图)
2. Transformer处理时序数据(生理信号、车辆数据)
3. 多模态融合决策
"""

def __init__(self,
num_landmarks: int = 68, # 人脸关键点数
landmark_dim: int = 2, # 每个关键点维度
physio_dim: int = 4, # 生理信号维度(心率、皮肤电导等)
vehicle_dim: int = 6, # 车辆数据维度
hidden_dim: int = 256,
num_gnn_layers: int = 3,
num_transformer_layers: int = 4):
super().__init__()

# GNN分支:处理人脸关键点图
self.gnn_branch = GNNEncoder(
num_landmarks=num_landmarks,
input_dim=landmark_dim,
hidden_dim=hidden_dim,
num_layers=num_gnn_layers
)

# Transformer分支1:处理生理信号时序
self.physio_transformer = TransformerEncoder(
input_dim=physio_dim,
hidden_dim=hidden_dim,
num_layers=num_transformer_layers
)

# Transformer分支2:处理车辆数据时序
self.vehicle_transformer = TransformerEncoder(
input_dim=vehicle_dim,
hidden_dim=hidden_dim,
num_layers=num_transformer_layers
)

# 多模态融合层
self.fusion = nn.Sequential(
nn.Linear(hidden_dim * 3, hidden_dim),
nn.ReLU(inplace=True),
nn.Dropout(0.3),
nn.Linear(hidden_dim, 128),
nn.ReLU(inplace=True)
)

# 疲劳等级分类头(0-4级)
self.classifier = nn.Linear(128, 5) # 5个疲劳等级

def forward(self,
landmarks: torch.Tensor,
physio_signal: torch.Tensor,
vehicle_data: torch.Tensor) -> torch.Tensor:
"""
前向传播

Args:
landmarks: 人脸关键点, shape=(B, num_landmarks, 2)
physio_signal: 生理信号时序, shape=(B, T, physio_dim)
vehicle_data: 车辆数据时序, shape=(B, T, vehicle_dim)

Returns:
logits: 疲劳等级分类, shape=(B, 5)
"""
# GNN处理关键点
gnn_features = self.gnn_branch(landmarks) # (B, hidden_dim)

# Transformer处理生理信号
physio_features = self.physio_transformer(physio_signal) # (B, hidden_dim)

# Transformer处理车辆数据
vehicle_features = self.vehicle_transformer(vehicle_data) # (B, hidden_dim)

# 多模态融合
fused = torch.cat([gnn_features, physio_features, vehicle_features], dim=1)
fused_features = self.fusion(fused)

# 分类
logits = self.classifier(fused_features)

return logits


class GNNEncoder(nn.Module):
"""
图神经网络编码器

处理人脸关键点图结构
"""

def __init__(self,
num_landmarks: int,
input_dim: int,
hidden_dim: int,
num_layers: int):
super().__init__()

# 节点嵌入
self.node_embed = nn.Linear(input_dim, hidden_dim)

# GNN层(简化版GCN)
self.gnn_layers = nn.ModuleList([
GCNConv(hidden_dim, hidden_dim)
for _ in range(num_layers)
])

# 人脸关键点邻接矩阵(固定结构)
self.adj = self._build_face_adjacency(num_landmarks)

def forward(self, x: torch.Tensor) -> torch.Tensor:
"""
Args:
x: 关键点坐标, shape=(B, N, 2)

Returns:
graph_features: shape=(B, hidden_dim)
"""
B, N, _ = x.shape

# 节点嵌入
x = self.node_embed(x) # (B, N, hidden_dim)

# GNN传播
for layer in self.gnn_layers:
x = layer(x, self.adj)
x = F.relu(x)

# 图池化(全局平均)
graph_features = x.mean(dim=1) # (B, hidden_dim)

return graph_features

def _build_face_adjacency(self, num_landmarks: int) -> torch.Tensor:
"""构建人脸关键点邻接矩阵"""
# 简化:假设相邻关键点连接
adj = torch.zeros(num_landmarks, num_landmarks)

# 左眼连接(点36-41)
for i in range(36, 41):
adj[i, i+1] = 1
adj[i+1, i] = 1

# 右眼连接(点42-47)
for i in range(42, 47):
adj[i, i+1] = 1
adj[i+1, i] = 1

# 嘴巴连接(点48-67)
for i in range(48, 67):
adj[i, i+1] = 1
adj[i+1, i] = 1

# 添加自环
adj = adj + torch.eye(num_landmarks)

# 归一化
degree = adj.sum(dim=1, keepdim=True)
adj_norm = adj / degree

return adj_norm


class GCNConv(nn.Module):
"""简化版图卷积"""

def __init__(self, in_features: int, out_features: int):
super().__init__()
self.linear = nn.Linear(in_features, out_features)

def forward(self, x: torch.Tensor, adj: torch.Tensor) -> torch.Tensor:
"""
Args:
x: 节点特征, shape=(B, N, F)
adj: 邻接矩阵, shape=(N, N)

Returns:
out: shape=(B, N, F')
"""
# 图卷积:AXW
x = torch.matmul(adj, x) # (B, N, F)
x = self.linear(x) # (B, N, F')
return x


class TransformerEncoder(nn.Module):
"""Transformer编码器"""

def __init__(self,
input_dim: int,
hidden_dim: int,
num_layers: int = 4,
num_heads: int = 8):
super().__init__()

# 输入嵌入
self.input_embed = nn.Linear(input_dim, hidden_dim)

# Transformer层
encoder_layer = nn.TransformerEncoderLayer(
d_model=hidden_dim,
nhead=num_heads,
dim_feedforward=hidden_dim * 4,
dropout=0.1,
batch_first=True
)
self.transformer = nn.TransformerEncoder(encoder_layer, num_layers=num_layers)

def forward(self, x: torch.Tensor) -> torch.Tensor:
"""
Args:
x: 时序数据, shape=(B, T, input_dim)

Returns:
features: shape=(B, hidden_dim)
"""
# 嵌入
x = self.input_embed(x) # (B, T, hidden_dim)

# Transformer编码
x = self.transformer(x) # (B, T, hidden_dim)

# 时序池化
features = x.mean(dim=1) # (B, hidden_dim)

return features


# 测试示例
if __name__ == "__main__":
model = FatigueNet()

# 模拟输入
B, T = 2, 30 # 2个样本,30帧

landmarks = torch.randn(B, 68, 2) # 68个人脸关键点
physio_signal = torch.randn(B, T, 4) # 生理信号(心率等)
vehicle_data = torch.randn(B, T, 6) # 车辆数据(速度、方向盘等)

output = model(landmarks, physio_signal, vehicle_data)
print(f"输入关键点形状: {landmarks.shape}")
print(f"输入生理信号形状: {physio_signal.shape}")
print(f"输入车辆数据形状: {vehicle_data.shape}")
print(f"输出疲劳等级: {output.shape}")
print(f"疲劳等级概率: {F.softmax(output, dim=1)}")

多模态数据融合策略

数据源与特征

模态 输入数据 特征提取 维度
视觉 人脸关键点 GNN 68×2
生理 心率、皮肤电导、体温 Transformer 4维时序
车辆 速度、方向盘、车道偏移 Transformer 6维时序

融合权重

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
class AdaptiveFusion(nn.Module):
"""
自适应多模态融合

动态调整各模态权重
"""

def __init__(self, num_modalities: int = 3):
super().__init__()

# 模态权重预测
self.weight_net = nn.Sequential(
nn.Linear(num_modalities * 256, 128),
nn.ReLU(),
nn.Linear(128, num_modalities)
)

def forward(self, *modality_features) -> torch.Tensor:
"""
自适应融合

Args:
modality_features: 各模态特征

Returns:
fused_features: 融合特征
"""
# 计算模态权重
concat_features = torch.cat(modality_features, dim=1)
weights = F.softmax(self.weight_net(concat_features), dim=1)

# 加权融合
stacked = torch.stack(modality_features, dim=1) # (B, M, D)
fused = (stacked * weights.unsqueeze(2)).sum(dim=1)

return fused, weights

性能评估(论文数据)

指标 单视觉 单生理 单车辆 FatigueNet融合
准确率 92.3% 88.5% 85.1% 98.7%
召回率 90.1% 86.2% 82.3% 98.2%
F1分数 91.2% 87.3% 83.7% 98.5%

Euro NCAP 2026多模态要求

场景 推荐模态组合 FatigueNet支持
F-01 PERCLOS 视觉 ✅ GNN
F-02 闭眼检测 视觉 ✅ GNN
F-03 打哈欠 视觉 ✅ GNN
F-04 微睡眠 视觉+生理 ✅ 融合
F-05 头部下垂 视觉+车辆 ✅ 融合

IMS开发启示

传感器配置

传感器 数据类型 FatigueNet模块
IR摄像头 人脸关键点 GNN
心率带/耳EEG 生理信号 Transformer
CAN总线 车辆数据 Transformer

部署优化

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
class EdgeFatigueNet(nn.Module):
"""
边缘优化FatigueNet

优化策略:
1. 轻量化GNN(减少层数)
2. INT8量化
3. 早退机制(低疲劳时跳过部分层)
"""

def __init__(self, config: dict = None):
super().__init__()

# 轻量化配置
self.gnn = GNNEncoder(68, 2, 128, num_layers=2) # 减少层数
self.physio_encoder = TransformerEncoder(4, 128, num_layers=2)
self.vehicle_encoder = TransformerEncoder(6, 128, num_layers=2)

self.classifier = nn.Linear(128 * 3, 5)

def forward_early_exit(self,
landmarks: torch.Tensor,
threshold: float = 0.7) -> dict:
"""
早退机制

如果GNN分支已经高置信度判断,跳过其他分支
"""
# 快速GNN判断
gnn_features = self.gnn(landmarks)
early_logits = self.classifier(torch.cat([gnn_features,
torch.zeros_like(gnn_features),
torch.zeros_like(gnn_features)], dim=1))

early_prob = F.softmax(early_logits, dim=1)
early_conf = early_prob.max(dim=1)[0]

# 早退判断
if early_conf > threshold:
return {
"logits": early_logits,
"early_exit": True,
"confidence": early_conf
}

# 完整推理(需要更多数据)
return {
"logits": None,
"early_exit": False,
"confidence": early_conf
}

参考资料

  1. Nature Scientific Reports - FatigueNet
  2. Graph Neural Networks for Face Analysis
  3. Transformer for Multimodal Fusion
  4. Euro NCAP 2026 Multimodal Recommendations

总结

FatigueNet核心:

  1. GNN处理结构化图:人脸关键点拓扑
  2. Transformer处理时序:生理/车辆信号
  3. 多模态融合:98.7%准确率

IMS开发优先级:

  • 🔴 高:GNN人脸关键点模块
  • 🟡 中:多模态数据融合
  • 🟢 低:早退机制优化

下一步行动:

  • 实现GNNEncoder模块
  • 集成多模态数据采集
  • 对齐Euro NCAP F-04/F-05场景

FatigueNet:GNN+Transformer混合架构实时多模态疲劳检测(Nature 2025)
https://dapalm.com/2026/07/09/2026-07-09-fatiguenet-gnn-transformer-multimodal-nature-2025/
作者
Mars
发布于
2026年7月9日
许可协议