DistillGaze: DINOv3蒸馏256K端侧眼动追踪,误差从3.48°降至1.44°(ECCV 2026)

论文信息

  • 标题: Rapidly Deploying On-Device Eye Tracking by Distilling Visual Foundation Models
  • 会议: ECCV 2026
  • 代码: ECCV 2026 Poster
  • 关键词: knowledge distillation, eye tracking, visual foundation model, synthetic-to-real, on-device deployment

核心突破

DistillGaze通过两阶段知识蒸馏,将DINOv3视觉基础模型(86M)压缩到256K端侧模型,在Project Aria基准上将眼动追踪中位误差从3.48°降至1.44°(-58.6%),且无需任何真实眼动标签。

关键性能

指标 基线(256K) DINOv3(86M) DistillGaze(256K) 提升
中位误差 3.48° >5° 1.44° -58.6%
尾部误差 E90U90 14.84° — 8.45° -43.1%
模型大小 256K 86M 256K 无增加
真实标签 需要 不需要 不需要 ✅

方法详解

两阶段蒸馏架构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
阶段1: VFM → 域教师 (Self-Distillation)
┌──────────────────────────────────────┐
│ DINOv3 ViT-B (86M, 冻结) │
│ + 合成眼图像 (有标签) │
│ + 真实红外图像 (无标签) │
│ → 域专业教师 (86M) │
│ │
│ 问题: DINOv3按身份聚类,抑制注视方向 │
│ 解决: 自蒸馏将身份表征重塑为注视表征 │
└──────────────────────────────────────┘

阶段2: 域教师 → 端侧学生 (Distillation)
┌──────────────────────────────────────┐
│ 域教师 (86M) │
│ → 蒸馏到 → 端侧学生 (256K) │
│ │
│ 无真实眼动标签 │
│ 仅用教师软标签+合成标签 │
└──────────────────────────────────────┘

为什么DINOv3直接用于眼动追踪不行?

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
# DINOv3用于眼动追踪的问题分析
dinov3_analysis = {
"参数量": "86M (ViT-B) — 太大,无法端侧部署",
"线性探测误差": ">5° — 比256K小模型还差!",
"原因": "DINOv3的表征按身份聚类,抑制注视方向",
"t-SNE分析": {
"聚类模式": "按身份(who)聚类,而非注视方向(where)",
"注视信号": "存在但被组织为身份表征的子结构",
"结论": "VFM携带信号但不可用形式"
},
"核心矛盾": "VFM有信号但太慢太大,端侧模型小但需要标签"
}

print("=== DINOv3直接用于眼动追踪的问题 ===")
for k, v in dinov3_analysis.items():
print(f" {k}: {v}")

# DistillGaze的解决方案
distillgaze_solution = {
"Step 1: 自蒸馏": {
"输入": "DINOv3 + 合成眼图像(有标签) + 真实红外(无标签)",
"方法": "自蒸馏 + 伪标签",
"输出": "域专业教师(86M)",
"关键": "将身份表征重塑为注视表征"
},
"Step 2: 学生蒸馏": {
"输入": "域教师(86M) → 学生(256K)",
"方法": "知识蒸馏(软标签)",
"标签": "无需真实眼动标签",
"输出": "256K端侧模型"
},
"核心洞察": "分离'从VFM提取信号'和'端侧部署'为两步"
}

print("\n=== DistillGaze解决方案 ===")
for step, config in distillgaze_solution.items():
print(f"\n{step}:")
for k, v in config.items():
print(f" {k}: {v}")

蒸馏模型实现

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
"""
论文:Rapidly Deploying On-Device Eye Tracking by Distilling Visual Foundation Models
会议:ECCV 2026

核心方法:两阶段蒸馏 DINOv3 → 256K端侧眼动追踪
"""

import torch
import torch.nn as nn
import torch.nn.functional as F

class Stage1SelfDistillation(nn.Module):
"""
阶段1: DINOv3自蒸馏为域教师

- 冻结DINOv3提供初始特征
- 使用合成数据(有标签) + 真实红外(无标签)
- 输出域专业教师(86M)
"""

def __init__(self, dinov3: nn.Module, embed_dim: int = 768):
super().__init__()
# 冻结DINOv3 backbone
self.dinov3 = dinov3
for p in self.dinov3.parameters():
p.requires_grad = False

# 可训练的注视头
self.gaze_head = nn.Sequential(
nn.Linear(embed_dim, 512),
nn.GELU(),
nn.Dropout(0.1),
nn.Linear(512, 256),
nn.GELU(),
nn.Linear(256, 2) # (pitch, yaw) 注视方向
)

# 自蒸馏投影头 (用于无标签真实数据)
self.projector = nn.Sequential(
nn.Linear(embed_dim, 512),
nn.GELU(),
nn.Linear(512, 512)
)

def forward(self, x):
"""提取DINOv3特征"""
with torch.no_grad():
features = self.dinov3(x) # (B, 768)
gaze = self.gaze_head(features) # (B, 2)
proj = self.projector(features) # (B, 512)
return gaze, proj

def compute_loss(self, x_synth, gaze_label_synth, x_real):
"""
损失 = 合成监督 + 真实自蒸馏一致性
"""
# 合成数据有标签
gaze_synth, proj_synth = self.forward(x_synth)
loss_supervised = F.mse_loss(gaze_synth, gaze_label_synth)

# 真实数据无标签 → 伪标签 + 一致性
gaze_real, proj_real = self.forward(x_real)
with torch.no_grad():
pseudo_gaze = gaze_real.clone() # 自生成伪标签
loss_pseudo = F.mse_loss(gaze_real, pseudo_gaze.detach())

# 总损失
total_loss = loss_supervised + 0.5 * loss_pseudo
return total_loss


class Stage2StudentDistillation(nn.Module):
"""
阶段2: 域教师 → 256K端侧学生

学生模型仅256K参数
使用教师软标签 + 合成标签
无真实眼动标签
"""

def __init__(self, teacher: nn.Module):
super().__init__()
# 教师(冻结)
self.teacher = teacher
for p in self.teacher.parameters():
p.requires_grad = False

# 256K学生模型
self.student = TinyGazeNet()

def forward(self, x):
return self.student(x)

def compute_loss(self, x, gaze_label_synth=None):
"""
蒸馏损失 = KL散度 + MSE
"""
# 教师软标签
with torch.no_grad():
teacher_gaze, _ = self.teacher(x)

# 学生预测
student_gaze = self.student(x)

# 蒸馏损失
loss_distill = F.mse_loss(student_gaze, teacher_gaze)

# 合成标签监督(如果有)
if gaze_label_synth is not None:
loss_supervised = F.mse_loss(student_gaze, gaze_label_synth)
return loss_distill + 0.5 * loss_supervised

return loss_distill


class TinyGazeNet(nn.Module):
"""
256K参数的微型眼动追踪网络

用于端侧部署
"""

def __init__(self):
super().__init__()
# 轻量CNN
self.features = nn.Sequential(
nn.Conv2d(1, 16, 3, stride=2, padding=1), # 红外单通道
nn.BatchNorm2d(16),
nn.ReLU6(),

nn.Conv2d(16, 32, 3, stride=2, padding=1),
nn.BatchNorm2d(32),
nn.ReLU6(),

nn.Conv2d(32, 32, 3, stride=2, padding=1),
nn.BatchNorm2d(32),
nn.ReLU6(),

nn.AdaptiveAvgPool2d(1),
)
self.gaze_regressor = nn.Sequential(
nn.Linear(32, 32),
nn.ReLU6(),
nn.Linear(32, 2) # (pitch, yaw)
)

def forward(self, x):
# x: (B, 1, H, W) 红外眼图像
feat = self.features(x).flatten(1) # (B, 32)
gaze = self.gaze_regressor(feat) # (B, 2)
return gaze


if __name__ == "__main__":
# 参数量对比
student = TinyGazeNet()
params = sum(p.numel() for p in student.parameters())
print(f"学生模型参数量: {params:,} ({params/1024:.0f}K)")

# 模拟输入
x = torch.randn(1, 1, 64, 64) # 红外眼图像
gaze = student(x)
print(f"输入: {x.shape}")
print(f"输出: {gaze.shape} (pitch, yaw)")

print(f"\n=== 论文结果 ===")
print(f"基线(256K): 3.48° 中位误差")
print(f"DINOv3(86M): >5° (直接用反而更差!)")
print(f"DistillGaze(256K): 1.44° 中位误差 (-58.6%)")
print(f"尾部误差: 14.84° → 8.45° (-43.1%)")
print(f"真实标签: 不需要!")

IMS开发启示

1. 快速部署眼动追踪到新硬件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# DistillGaze的快速部署流程
deployment_workflow = {
"传统流程": {
"步骤": ["硬件原型", "标定", "数据采集", "标注", "训练", "部署"],
"时间": "数月",
"成本": "$50K+"
},
"DistillGaze流程": {
"步骤": ["渲染合成数据", "采集无标签真实图像", "两阶段蒸馏", "部署"],
"时间": "数天",
"成本": "$500",
"关键": "无需真实眼动标签"
}
}

2. 合成数据→真实迁移

3. 在DMS中的应用

应用 精度需求 DistillGaze能否满足 说明
注视区域检测 <5° ✅ 1.44° 可检测看路/看手机
PERCLOS <3° ✅ 1.44° 闭眼检测足够
分心检测 <2° ✅ 1.44° 视线偏离道路
精细注视 <1° ⚠️ 接近 读仪表盘文字

参考


发布时间: 2026-09-25 00:00
分类: 视线估计 | 知识蒸馏 | 端侧部署 | ECCV 2026 | 论文解读