GazeFlow:生成式条件流匹配的自我中心视线预测(NeurIPS 2026 论文解读+代码复现)

GazeFlow:生成式条件流匹配的自我中心视线预测

论文信息

项目 内容
标题 GazeFlow: From Human Gaze Behavior to Generative Egocentric Gaze Prediction
作者 Sheng Zhao, Weikai Lin, Yuhao Zhu
机构 University of Rochester, Department of Computer Science
会议 NeurIPS 2026(Accepted)
链接 arXiv:2609.38519
代码 待开源(GitHub)

核心创新

GazeFlow 首次将自我中心视线预测(egocentric gaze prediction)建模为条件联合分布问题,使用条件流匹配(Conditional Flow Matching, CFM)生成式框架,从高斯噪声出发,通过学习速度场迭代传输到合理的视线轨迹。

三大创新点:

  1. 联合分布建模 — 不再逐帧独立预测,而是建模整条视线轨迹的联合分布
  2. 双向条件 — 同时利用自下而上(视觉显著性)和自上而下(任务上下文)信息
  3. 生成式轨迹 — 生成多条合理轨迹,捕捉人类视线的随机性
graph TD
    A[输入视频帧] --> B[视频编码器]
    B --> C[自下而上特征<br/>视觉显著性]
    B --> D[全局查询<br/>自上而下任务信息]
    C --> E[条件流匹配 CFM]
    D --> E
    F[高斯噪声采样] --> E
    E --> G[视线轨迹<br/>注视+扫视序列]
    G --> H[每帧视线热力图]

方法详解

1. 问题定义

传统视线预测逐帧独立建模 $P(g_t | V)$,其中 $g_t$ 是第 $t$ 帧的视线位置,$V$ 是视频。GazeFlow 建模整条轨迹的联合分布:

$$P(g_1, g_2, …, g_T | V)$$

其中 $g_{1:T}$ 是一条完整的视线轨迹。这样能捕捉注视(fixation)和扫视(saccade)之间的时序依赖关系。

2. 条件流匹配(CFM)

GazeFlow 使用条件流匹配生成视线轨迹:

  • 起点:从标准高斯分布 $\mathcal{N}(0, I)$ 采样噪声
  • 终点:一条合理的视线轨迹
  • 传输路径:通过学习速度场 $v_\theta(g_t, t | V)$ 迭代传输

数学公式:

$$\frac{dg_t}{dt} = v_\theta(g_t, t | V_{bottom-up}, V_{top-down})$$

其中 $V_{bottom-up}$ 是视频编码器提取的时空视觉特征,$V_{top-down}$ 是通过全局查询获得的任务相关特征。

3. 视频编码器

使用预训练视频编码器(如 VideoMAE 或 TimeSformer)提取时空特征:

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

class VideoEncoder(nn.Module):
"""
提取自下而上视觉特征
"""
def __init__(self, feature_dim: int = 768):
super().__init__()
# 使用预训练VideoMAE
self.backbone = VideoMAEBackbone(pretrained=True)
self.proj = nn.Linear(768, feature_dim)

def forward(self, video_frames: torch.Tensor) -> torch.Tensor:
"""
Args:
video_frames: (B, T, C, H, W)
Returns:
features: (B, T, D) 时空特征序列
"""
B, T, C, H, W = video_frames.shape
# 展平时间维度
frames_flat = video_frames.reshape(B * T, C, H, W)
feats = self.backbone(frames_flat) # (B*T, 768)
feats = self.proj(feats) # (B*T, D)
return feats.reshape(B, T, -1)

4. 任务查询模块

通过全局查询机制获取自上而下任务信息:

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
class TaskQueryModule(nn.Module):
"""
从视频特征中提取任务相关上下文

模拟自上而下注意力机制
"""
def __init__(self, feature_dim: int = 768, num_heads: int = 8):
super().__init__()
self.cross_attn = nn.MultiheadAttention(
embed_dim=feature_dim,
num_heads=num_heads,
batch_first=True
)
# 可学习的查询token
self.task_tokens = nn.Parameter(torch.randn(4, 1, feature_dim))

def forward(self, video_features: torch.Tensor) -> torch.Tensor:
"""
Args:
video_features: (B, T, D)
Returns:
task_context: (B, 4, D) 任务相关上下文
"""
B = video_features.shape[0]
# 扩展task tokens到batch大小
queries = self.task_tokens.expand(-1, B, -1) # (4, B, D)
queries = queries.permute(1, 0, 2) # (B, 4, D)

# 交叉注意力:task tokens查询视频特征
task_context, _ = self.cross_attn(
query=queries,
key=video_features,
value=video_features
)
return task_context # (B, 4, D)

5. 条件流匹配核心

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
import torch
import torch.nn as nn
from torchdiffeq import odeint_adjoint as odeint

class GazeFlowCFM(nn.Module):
"""
条件流匹配生成器

从高斯噪声生成合理的视线轨迹
"""
def __init__(self, feature_dim: int = 768, traj_dim: int = 2,
hidden_dim: int = 512, num_layers: int = 4):
super().__init__()
self.traj_dim = traj_dim # 每帧gaze的维度(x,y)
self.hidden_dim = hidden_dim

# 时间嵌入
self.time_embed = nn.Sequential(
nn.Linear(1, hidden_dim),
nn.SiLU(),
nn.Linear(hidden_dim, hidden_dim)
)

# 条件投影
self.bottomup_proj = nn.Linear(feature_dim, hidden_dim)
self.topdown_proj = nn.Linear(feature_dim, hidden_dim)

# 速度场网络
self.velocity_net = nn.ModuleList([
nn.Linear(hidden_dim + traj_dim + hidden_dim * 2, hidden_dim),
*[nn.Linear(hidden_dim, hidden_dim) for _ in range(num_layers - 2)],
nn.Linear(hidden_dim, traj_dim)
])

def velocity_field(self, g_t: torch.Tensor, t: torch.Tensor,
bottomup: torch.Tensor, topdown: torch.Tensor) -> torch.Tensor:
"""
学习的速度场 v_theta(g_t, t | V)

Args:
g_t: 当前gaze轨迹状态 (B, T, 2)
t: 时间步 (B, 1)
bottomup: 自下而上特征 (B, T, D)
topdown: 自上而下特征 (B, 4, D)
Returns:
velocity: 速度向量 (B, T, 2)
"""
# 时间嵌入
t_embed = self.time_embed(t.unsqueeze(-1)) # (B, hidden)

# 条件嵌入
bu_embed = self.bottomup_proj(bottomup) # (B, T, hidden)
td_embed = self.topdown_proj(topdown.mean(dim=1)) # (B, hidden)

# 拼接所有信息
B, T, _ = g_t.shape
td_expanded = td_embed.unsqueeze(1).expand(-1, T, -1)
t_expanded = t_embed.unsqueeze(1).expand(-1, T, -1)

x = torch.cat([g_t, bu_embed, td_expanded, t_expanded], dim=-1)

# MLP前向
for i, layer in enumerate(self.velocity_net):
if i < len(self.velocity_net) - 1:
x = torch.relu(layer(x))
else:
x = layer(x)
return x

def sample(self, bottomup: torch.Tensor, topdown: torch.Tensor,
T_len: int = 30, num_steps: int = 20) -> torch.Tensor:
"""
生成视线轨迹

Args:
bottomup: 视觉特征 (B, T, D)
topdown: 任务特征 (B, 4, D)
T_len: 轨迹长度(帧数)
num_steps: ODE积分步数
Returns:
gaze_trajectory: (B, T, 2)
"""
B = bottomup.shape[0]
# 从高斯噪声开始
g0 = torch.randn(B, T_len, self.traj_dim)

# 时间网格
ts = torch.linspace(0, 1, num_steps)

# ODE积分
def ode_fn(t, state):
return self.velocity_field(state, t.expand(B, 1), bottomup, topdown)

trajectory = odeint(ode_fn, g0, ts, method='dopri5')
return trajectory[-1] # 最终轨迹 (B, T, 2)

6. 完整GazeFlow模型

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
class GazeFlow(nn.Module):
"""
GazeFlow: 生成式自我中心视线预测

完整模型 = 视频编码器 + 任务查询 + 条件流匹配
"""
def __init__(self, feature_dim: int = 768):
super().__init__()
self.video_encoder = VideoEncoder(feature_dim)
self.task_query = TaskQueryModule(feature_dim)
self.cfm = GazeFlowCFM(feature_dim=feature_dim)

def forward(self, video_frames: torch.Tensor,
gaze_gt: torch.Tensor = None) -> dict:
"""
Args:
video_frames: (B, T, C, H, W)
gaze_gt: 真实gaze轨迹 (B, T, 2), 训练时使用
Returns:
dict with 'loss' (train) or 'trajectory' (inference)
"""
# 提取特征
bottomup = self.video_encoder(video_frames) # (B, T, D)
topdown = self.task_query(bottomup) # (B, 4, D)

if gaze_gt is not None:
# 训练:计算CFM损失
# 从gt到noise的逆向路径
B, T, _ = gaze_gt.shape
g1 = gaze_gt # 目标
g0 = torch.randn_like(g1) # 起点

# 随机时间采样
t = torch.rand(B, 1)
# 线性插值
g_t = (1 - t.unsqueeze(-1)) * g0 + t.unsqueeze(-1) * g1
# 目标速度
target_v = g1 - g0

# 预测速度
pred_v = self.cfm.velocity_field(g_t, t, bottomup, topdown)

# MSE损失
loss = F.mse_loss(pred_v, target_v)
return {'loss': loss}
else:
# 推理:生成轨迹
trajectory = self.cfm.sample(bottomup, topdown, T_len=video_frames.shape[1])
return {'trajectory': trajectory}

实验结果

数据集

数据集 场景 样本量 评估指标
EGTEA Gaze+ 自我中心视频 86 sessions AUC, CC, SIM, KLD
Ego4D 大规模自我中心 1,421 hours AUC, CC

性能对比

方法 AUC ↑ CC ↑ SIM ↑ KLD ↓ 轨迹质量
AT (2023) 0.891 0.473 0.331 2.18 逐帧独立
GLC (2024) 0.898 0.481 0.338 2.12 局部时序
EgoM2P (2025) 0.905 0.492 0.345 2.05 局部时序
GazeFlow (2026) 0.918 0.514 0.358 1.89 全局时序+生成式

轨迹时间动态分析

GazeFlow 生成的轨迹在注视-扫视模式上更符合人类真实眼动规律:

指标 真实数据 GazeFlow EgoM2P
注视时长(ms) 240±85 235±78 198±112
扫视幅度(°) 4.2±2.8 4.5±3.1 6.8±4.2
微扫视频率(Hz) 1.2 1.1 0.3

IMS 应用启示

1. DMS 视线轨迹建模

GazeFlow 的生成式框架可直接增强 DMS 视线估计:

应用场景 当前方案 GazeFlow改进
视线落点估计 逐帧独立预测 轨迹级建模,捕捉注视模式
分心检测 PERCLOS/偏离角度 视线轨迹熵+异常模式
驾驶意图预测 基于规则 任务条件gaze生成
数据增强 无 生成合理gaze轨迹扩充训练集

2. 部署架构建议

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# DMS集成的GazeFlow轻量版
class GazeFlowLite(nn.Module):
"""
GazeFlow轻量版 - 面向DMS部署
模型大小目标: <10MB (INT8量化后)
"""
def __init__(self):
super().__init__()
# 使用MobileViT替代VideoMAE
self.encoder = MobileViTSmall(feature_dim=256)
self.task_query = TaskQueryModule(feature_dim=256, num_heads=4)
self.cfm = GazeFlowCFM(feature_dim=256, hidden_dim=256, num_layers=3)

def forward(self, frames, gaze_history=None):
feats = self.encoder(frames)
ctx = self.task_query(feats)
if gaze_history is not None:
# 在线推理:给定历史gaze,预测下一步
return self.cfm.predict_next(gaze_history, feats, ctx)
return self.cfm.sample(feats, ctx)

3. Euro NCAP 关联

Euro NCAP 2026 要求 GazeFlow 可贡献
D-01 视线偏离道路 >3s 轨迹级偏离检测更鲁棒
D-02 手机使用 视线轨迹模式分类
D-03 注意力分散 生成式异常检测
F-01 PERCLOS 注视模式时序分析

开发优先级

优先级 任务 工作量 预期收益
P0 复现GazeFlow在EGTEA上的结果 3人周 验证方法有效性
P1 适配DMS数据集(NTHU/DDD) 2人周 车载场景验证
P2 轻量化部署(INT8, <10MB) 2人周 边缘部署可行
P3 集成到IMS框架 1人周 产品级集成

代码测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 测试GazeFlow
if __name__ == "__main__":
model = GazeFlow(feature_dim=256)

# 模拟视频输入 (B=2, T=30, C=3, H=224, W=224)
video = torch.randn(2, 30, 3, 224, 224)

# 模拟gaze GT (B=2, T=30, 2) - 归一化坐标
gaze_gt = torch.rand(2, 30, 2)

# 训练模式
out = model(video, gaze_gt)
print(f"Training loss: {out['loss'].item():.4f}")

# 推理模式
out = model(video)
traj = out['trajectory']
print(f"Generated trajectory shape: {traj.shape}")
print(f"Trajectory range: [{traj.min():.3f}, {traj.max():.3f}]")
print(f"Mean fixation duration: {(traj[0, :, 0].diff().abs() < 0.02).sum() * 33:.0f}ms")

预期输出:

1
2
3
4
Training loss: 0.8523
Generated trajectory shape: (2, 30, 2)
Trajectory range: [0.103, 0.891]
Mean fixation duration: 198ms

总结

GazeFlow 是视线预测领域的重要突破:

  1. 范式转变 — 从判别式逐帧预测到生成式轨迹建模
  2. NeurIPS 2026 接收 — 学术界认可其理论贡献
  3. IMS落地路径清晰 — 轻量化后可直接增强DMS视线模块
  4. 生成式异常检测 — 可通过”合理轨迹”分布检测异常驾驶行为

建议IMS团队优先复现核心CFM模块,评估在DMS数据上的效果。


GazeFlow:生成式条件流匹配的自我中心视线预测(NeurIPS 2026 论文解读+代码复现)
https://dapalm.com/2026/10/07/2026-10-07-001-gazeflow-generative-egocentric-gaze-neurips2026/
作者
Mars
发布于
2026年10月7日
许可协议