Pooling 方法说明
解释 pooling_vs_mos_en.html 里每个 temporal pooling rule 是什么,以及这次 DSIS pilot 用的参数是怎么来的。
代码来源:规则在 VQA2-gmad-pooling/gmad/temporal_pooling_snapshot.py(与 VQA2/temporal_pooling.py 相同的快照),
调用参数在 gmad/run_selection.py 的 predict(),实际运行配置记录在
results/gmad_all_shots_480p720p1080p_skip_negative_no_offset_time_converted/config.json。
文献出处与原始参数见 VQA2/temporal_pooling_rules.md。
1. 所有规则共用的输入
- 逐帧分数 q[t]:CVVDP 在 video 模式下跑一次,在时间池化之前停下,把每帧的响应用 CVVDP 自己的
met2jod转成 JOD(见PER_FRAME_SCORES.md)。 越高越好,无可见差异的帧 = 10。 - 时间轴:所有视频都在 120 Hz 上,每个 chunk 180 帧(1.5 s),一条视频 540 帧(4.5 s)。30 / 60 fps 的 chunk 用帧重复补到 120 Hz。
- 负分帧被丢掉(
negative_frame_policy = skip):met2jod不截断,失真很重的帧会低于 0。 20412 个候选视频里有 1215 个含负分帧,共去掉 1944 个帧值,所以有些视频只剩 538 或 539 帧。 - 没有加偏移(
common_input_offset = 0):Minkowski、VQPooling 需要正值输入。去掉负分帧后最小值是 0.306,已经为正,所以不需要平移。 - 参数不是在我们的数据上拟合的:全部采用原论文的默认 / 推荐值,只做了一件事——把以“帧数”给出的参数从论文的 25 fps 换算到 120 Hz,保持相同的物理时长
(
time_policy = converted,系数 120 / 25 = 4.8)。以“秒”给出的参数(hysteresis 的 τ)直接用 fps = 120。
2. 一览表
最后一列是结果页的主指标(各 scene SRCC 按 n − 3 加权在 Fisher z 空间平均),按它排序。只有 7 位被试、84 条视频,差距大多在噪声范围内,仅供参照。
| 规则 | 一句话 | 本次参数 | 来源 | 加权 SRCC |
|---|---|---|---|---|
increasing_weights | 三个 chunk 权重 1 : 2 : 3(越后越重) | n_segments = 3 | Duanmu 2018 | 0.622 |
exp_weighted_minkowski | 指数近因权重 + Minkowski | p = 2, τ = 480 帧 (4 s) | Rimac-Drlje 2009 | 0.613 |
decreasing_weights | 三个 chunk 权重 3 : 2 : 1(越前越重) | n_segments = 3 | Duanmu 2018 | 0.585 |
minkowski | (mean qp)1/p | p = 2 | Rimac-Drlje 2009 | 0.569 |
hysteresis | 记忆(过去最差)+ 当前(未来窗口偏向最差) | τ = 2 s (240 帧), α = 0.8 | Seshadrinathan & Bovik 2011 | 0.568 |
worst_window | 滑动平均的最小值 | N = 480 帧 (4 s) | Rimac-Drlje 2009 | 0.566 |
mean = last_frames_mean = cumulative_quality | 算术平均 | — | 多篇 | 0.564 |
vqpooling_temporal | 2-means 分高低两组,好帧组降权 | 无可调参数 | Park 2013 | 0.555 |
combined_jod(参照) | CVVDP 对整段视频自己的打分 | CVVDP 默认 | Mantiuk 2024 | 0.529 |
median = kth_percentile | 中位数 | k = 50 | Eswara 2020 / Rimac-Drlje 2009 | 0.511 |
percentile | 最差 5% 帧的平均 | p = 5%(约 27 帧) | Park 2013; Bampis 2017 | 0.429 |
early_dominance | 只看第 1 个 chunk | n_segments = 3 | Duanmu 2018 | 0.411 |
late_dominance | 只看第 3 个 chunk | n_segments = 3 | Duanmu 2018 | 0.399 |
3. 时间权重对比
纯加权平均类规则可以画成“每一时刻占多少权重”。横轴是 4.5 s 视频,竖线是 chunk 边界。
exp_weighted_minkowski 的三段权重约为 0.22 / 0.32 / 0.46,和 increasing_weights 的 0.17 / 0.33 / 0.50 很接近——这两个规则在结果页排前两名,说明的是同一件事:后面的 chunk 权重大一些更贴近 MOS。
纵轴是相对每帧权重(mean 每帧 = 1)。exp_weighted_minkowski 画的是 p 次方之前的权重 exp((t − T)/τ),已归一化。
4. 参数大白话
每个参数其实都在回答一个问题:“观众更记得哪些帧?”是差的帧、好的帧、最后几秒,还是某一段。下面用一个玩具例子说明:
一条视频三段,质量分别是 10, 10, 2(JOD,越高越好),普通平均 = 7.33。
Minkowski 的 p:数值大的帧和数值小的帧谁说了算
先把每个分数取 p 次方,平均以后再开 p 次方根。p 次方会把差距放大或缩小,所以 p 决定了往哪一边偏:
| p | 10, 10, 2 的结果 | 大白话 |
|---|---|---|
| → −∞ | 2 | 只看最差的那一帧 |
| −1 | 4.29 | 明显被差帧拉低(调和平均) |
| 0.5 | 6.65 | 稍微偏向差帧 |
| 1 | 7.33 | 就是普通平均 |
| 2(本次) | 8.25 | 稍微偏向好帧 |
| 4 | 9.04 | 差帧几乎不算了 |
| → +∞ | 10 | 只看最好的那一帧 |
p 越大 = 分数越高的帧越占主导。论文里输入是失真(越高越差),所以“p 大”等于“盯着最差的帧”,表达的是“一次卡顿/模糊就毁掉印象”。 我们的输入是 JOD(越高越好),方向反过来了:p = 2 其实是在原谅差帧。想表达“人记仇”,在 JOD 上要用 p < 1。 不过我们的 JOD 大多集中在 7–10,差距不大,所以 p = 2 和平均几乎一样。
exp_weighted_minkowski 的 τ:观众的“记忆有多长”
每帧的权重是 exp((t − T)/τ):最后一帧权重 1,往前每过 τ 帧权重变成原来的 37%。
- τ 很小(比如 1 s = 120 帧):前面的几乎全忘了,第一帧权重只有最后一帧的 1%,基本只看最后一段。
- τ 很大:所有帧差不多一样重,退回普通平均(或普通 Minkowski)。
- 本次 τ = 4 s:视频只有 4.5 s,第一帧权重是最后一帧的 1/3,三段大约 22% / 32% / 46%,即“最后一段稍微更重要”。
p 在这个规则里的含义和上面一样。
last_frames_mean 的 F:“只看最后几秒”的那个几秒
F 越小,越只看结尾(近因效应越强);F ≥ 视频长度就是普通平均。论文说的是“最后 5 s 决定印象”,但我们的视频总共才 4.5 s,所以“最后 5 s”就是整条视频。
如果想在这里测近因效应,F 应该取 1 个 chunk(180 帧),这就等于 late_dominance。
worst_window 的 N:多长的“最差片段”才算数
在视频里找连续 N 帧平均最差的一段,用它代表整条视频。
- N = 1:最差的单独一帧,一闪而过的瑕疵也会被抓住,最严格。
- N 越大:短暂的差帧被周围的好帧冲淡,越宽容;N = 视频长度就是平均。
- 本次 N = 4 s:占了视频的 89%,只是在“去掉开头半秒”和“去掉结尾半秒”之间选差的那个,所以和平均很接近。如果 N = 1 个 chunk,它就成了“最差那个 chunk 的平均”。
percentile 的 p%:只看最差的百分之多少
把帧从差到好排好,只取最差的 p% 求平均。p 越小越苛刻:p = 5% 只看最差的 27 帧(约 0.23 s);p = 100% 就是平均。 它表达的是“观众记住的是最难看的那一下”。在我们的数据里排名靠后,说明观众并不是只记最差的那 0.2 秒。
kth_percentile 的 k:排在第 k% 位置的那一帧
和上面类似,但不是求平均,而是直接取一个位置的值。k 小 = 看差帧,k = 50 = 中位数,k 大 = 看好帧。本次 k = 50,所以就是中位数。
hysteresis 的 τ 和 α:“记仇多久”和“多记仇”
每一时刻的感受 = α × 当前看到的 + (1 − α) × 过去 τ 秒里最差的那一刻。
- τ(记仇多久):一次质量下降在记忆里留多久。τ 大,掉一次质量会影响后面很长一段时间;τ 小,很快就“原谅”了。本次 2 s,大约一个多 chunk。
- α(多看眼前,还是多记过去):α = 1 完全不记仇,只看当前;α 越小,过去最差的那一刻越拖累现在。本次 α = 0.8,即 80% 看眼前,20% 记着刚才最差的。
- 另外“当前”那一项本身也偏向差帧:它看接下来 τ 秒的帧,最差的那几帧权重最大。所以这个规则整体是“对下降敏感、恢复慢”。
vqpooling 的 w:自动决定好帧算多少
没有要手动设的参数。它把帧分成“差的一组”和“好的一组”,差组永远全算,好组乘一个权重 w = (1 − 差组均值/好组均值)²:
- 两组差不多(比如 9.5 和 10):w ≈ 0,好组几乎不算,结果 ≈ 差组均值。意思是:差别不明显时,取偏差的那一边作为保守估计。
- 两组差很多(比如 2 和 10):w = 0.64,好组也算进来,结果更接近平均。
因为看的是“比值”,如果给所有分数加上同一个常数,结果会变,所以所有规则必须用同一个输入(本次没有加偏移)。
early / late / increasing / decreasing 的权重:三个 chunk 各占几成
就是给三个 chunk 分配“话语权”:
early_dominance[1, 0, 0]:只看第一印象。late_dominance[0, 0, 1]:只看最后印象。increasing_weights[1, 2, 3]:17% / 33% / 50%,越后越重。decreasing_weights[3, 2, 1]:50% / 33% / 17%,越前越重。
例子 10, 10, 2:early = 10,late = 2,increasing = 5.67,decreasing = 8.67。同样三段,只因为差的在最后,increasing 就比 decreasing 低 3 分。
cumulative_quality 的四个权重:最后、平均、最差、最好各占几成
0.28 × 最后一段 + 0.426 × 整体平均 + 0.28 × 最差一段 + 0.014 × 最好一段。大白话:平均印象占四成多,结尾和最差的时刻各占约三成,最好的时刻几乎不算 (人对好的记不住、对坏的和结尾记得住)。但这里的“一段”是 50–60 s,我们的视频只有 4.5 s,四项都是整体平均,所以就是平均。
5. 逐条说明
记号:q[t] 是第 t 帧的 JOD,t = 1…T(T ≈ 540)。
combined_jod 参照,不是 pooling rule
CVVDP 对整段 4.5 s 视频直接给出的 JOD。CVVDP 内部的时间池化是在失真响应上做 L2(βt = 2),然后再映射到 JOD;
而其它规则都是先把每帧转成 JOD 再池化。所以它和 minkowski、mean 很像但不完全相同。用来回答“直接用 CVVDP 行不行”。
mean = last_frames_mean = cumulative_quality
基线。多篇文献(Duanmu 2018、Bampis 2017、Seshadrinathan & Bovik 2011)发现 mean 很难被稳定超越。无参数。
last_frames_mean 退化为 mean
- F = 600 帧
- 论文(Rimac-Drlje 2009)扫了 F ∈ {50…300} 帧,在 25 fps 下 F = 125 帧(5 s)最好,结论是“最后 3–7 s 主导判断”。换算到 120 Hz:125 × 4.8 = 600 帧。
late_dominance)。cumulative_quality (CQM) 退化为 mean
Tran et al. 2021,为几分钟的 streaming session 设计:在 1 s 片段上滑动长度 K 的窗口,组合“最后一个窗口”(近因)、“所有窗口平均”、“最差窗口”(peak-end)和“最好窗口”。 权重和窗口长度是论文在 36 段 6 分钟序列上拟合的值,原样使用。
run_selection.py 直接用按帧时长加权的 mean 代替来避免这个偏差。median = kth_percentile
对少数异常帧更稳健。Eswara 2020 用它来汇总连续 QoE 预测。无参数。
kth_percentile 退化为 median
- k = 50
- 函数默认值。Rimac-Drlje 2009 扫过 k ∈ {10…97},最优 k 取决于指标方向:质量类分数 SSIM 为 60、PSNR 为 10;失真类分数取高 k。
median 完全相同。如果想测试“偏向最差”的版本,应该用较小的 k(例如 10),这次 pilot 没有做。percentile
- p = 5%
- Bampis 2017 和 Park 2013(剩余帧权重 r = 0 时)都用 5%;Moorthy & Bovik 2010 用 6%。540 × 5% → 27 帧 ≈ 0.23 s。
只看最差的一小段,代表“worst-case / peak impairment”。在我们的数据里它几乎只反映最差那个 chunk 里最差的那几帧,排名靠后。
minkowski
- p = 2
- 函数默认值。Rimac-Drlje 2009 扫了 p = 1…12,最佳值随指标不同(SSIM 3、MSE 2、VQM 10、PSNR 12)。
exp_weighted_minkowski
在 Minkowski 上加指数近因权重:越靠后的帧权重越大。
- p = 2
- 函数默认(与
minkowski一致;p 的方向问题同上)。 - τ = 480 帧
- 默认 τ = 100 帧(论文中 SSIM / MSE 的最佳值),在 25 fps 下是 4 s,换算到 120 Hz:100 × 4.8 = 480 帧。 这样第一帧的权重是最后一帧的 exp(−539/480) ≈ 0.33。
- normalize = True
- 论文公式用 1/T 归一化,会让结果随 τ 变小;实现改为除以权重和,使输出保持在 JOD 尺度上。
worst_window
- N = 480 帧
- 默认 N = 100 帧(Rimac-Drlje 扫过 1…160 帧),25 fps 下 4 s,换算为 480 帧。
hysteresis
来自连续打分实验:人对质量下降反应快、恢复慢。“记忆”项让一次下降的影响延续 τ 秒。
- τ = 2 s
- 论文推荐值(τ 在 1–3 s 内结果稳定)。τ 以秒定义,直接用 fps = 120,所以 n = 240 帧,不需要另外换算。
- α = 0.8
- 论文推荐值(α > 0.5 时结果稳定)。
- 高斯 σ
- (2K − 1)/12,K 为窗口中的帧数,按论文定义。
实现说明:论文 eq.(7) 印成 x(t) = min[x(t)],字面上是常数;按文字描述取过去 τ 秒 q 的最小值。高斯权重归一化为和 = 1。
vqpooling_temporal
Park et al. 2013 的时间部分。质量均匀时 M_L ≈ M_H,w ≈ 0,结果≈低组均值;高低差距越大,好帧的权重越大(回到更接近平均)。 没有可调参数(k = 2 是方法本身的定义)。论文中大部分提升来自空间部分,对逐帧向量不适用,这里只用了时间部分。
early_dominance / late_dominance / increasing_weights / decreasing_weights
- n_segments = 3
- Duanmu 2018 原文是两个 4 s 片段,权重分别为 [1,0]、[0,1]、[1/3,2/3]、[2/3,1/3]。我们推广到 3 段,正好对应实验的三个 chunk:
early_dominance:[1, 0, 0],只看 chunk 1(首因效应)late_dominance:[0, 0, 1],只看 chunk 3(近因效应)increasing_weights:[1, 2, 3] / 6,越后越重decreasing_weights:[3, 2, 1] / 6,越前越重
“只看一个 chunk”的两条都排最后,说明三个 chunk 都会影响判断;“加权但都保留”的两条都比 mean 好,其中偏后的更好。 注意:去掉负分帧的视频(538/539 帧)等分时边界会偏一两帧,影响可忽略。
6. 怎么调参数(大白话)
上面用的都是文献默认值。gmad/tune_pooling_makeway.py 试了另一种做法:用我们自己的 MOS 把参数调一遍,看能不能变好。
结果在 tuning_makeway/(grid.csv、best_params.csv、test_results.csv)。
第 1 步:分“练习题”和“考试题”
拿 makeway 这一个 scene(19 条视频,pilot 里最多的一个)当练习题,只在它上面调参数。 其余 7 个 scene 的 65 条视频当考试题,调参时完全不看。
为什么不在全部 84 条上调?那等于自己出题自己考:总能找到一组参数在这批视频上分数很高,但换一批视频就不灵了(过拟合)。 分开以后,“考试分数”才说明参数是真的好,还是只是碰巧适合 makeway。
第 2 步:把每个候选值都试一遍(网格搜索)
每个规则准备一张候选值表,逐个代入,算出 19 条 makeway 视频的预测分数,再看预测的排名和 MOS 的排名有多一致(SRCC)。SRCC 最高的那组就是“调好的参数”。 如果两组 SRCC 一样,比 PLCC(线性相关);还一样,就选离默认值最近的那个,免得没有理由地乱改。
| 规则 | 试了哪些值 | 默认 |
|---|---|---|
minkowski | p ∈ {−8, −4, −2, −1, −0.5, 0.5, 1, 2, 4, 8} | p = 2 |
exp_weighted_minkowski | p ∈ {−4, −2, −1, 1, 2, 4} × τ ∈ {30, 60, 120, 240, 480, 960, 1920} 帧(0.25–16 s),共 42 组 | p = 2, τ = 480 |
percentile | p ∈ {1, 2, 5, 10, 20, 30, 40, 50, 75, 100}% | 5% |
kth_percentile | k ∈ {1, 5, 10, 20, …, 90} | 50 |
last_frames_mean | F ∈ {30, 60, 90, 120, 180, …, 540} 帧(0.25–4.5 s) | 600 |
worst_window | N ∈ {1, 12, 30, 60, 90, 120, 180, 240, 360, 480, 540} 帧 | 480 |
hysteresis | τ ∈ {0.05, 0.1, 0.25, 0.5, 1, 2} s × α ∈ {0, 0.2, …, 1},共 36 组 | τ = 2, α = 0.8 |
| 四个分段规则 | n_segments ∈ {2, 3, 4, 5, 6, 9} | 3 |
候选值的范围特意比默认值宽很多,两个方向都包括(例如 Minkowski 的 p 有正有负,就是为了检查“偏向差帧”和“偏向好帧”哪个更好)。
mean、median、vqpooling_temporal 没有参数可调;cumulative_quality 在 4.5 s 上就是 mean,没有放进来。
同样的流程对 CVVDP 逐帧 JOD、逐帧 VMAF、逐帧 PSNR-Y 各做一遍。
参数对比:默认 vs 调好的
| 规则 | 参数 | 默认 | CVVDP | VMAF | PSNR-Y |
|---|---|---|---|---|---|
minkowski | p | 2 | 2 | 8 | 2 |
exp_weighted_minkowski | p | 2 | 4 | 4 | 2 |
| τ(帧) | 480 | 1920 | 1920 | 1920 | |
percentile | p(%) | 5 | 100 | 100 | 100 |
kth_percentile | k | 50 | 90 | 60 | 60 |
last_frames_mean | F(帧) | 600 | 600 | 600 | 600 |
worst_window | N(帧) | 480 | 180 | 90 | 30 |
hysteresis | τ(s) | 2 | 0.1 | 2 | 0.25 |
| α | 0.8 | 0 | 0 | 0.6 | |
early_dominance | n_segments | 3 | 2 | 2 | 2 |
late_dominance | n_segments | 3 | 2 | 3 | 5 |
increasing_weights | n_segments | 3 | 2 | 4 | 2 |
decreasing_weights | n_segments | 3 | 2 | 2 | 2 |
第 3 步:拿去考试,和默认值比
把调好的参数和默认参数都用在 65 条考试视频上,各算一个 SRCC,差值就是“调参带来的提升”。
第 4 步:提升是真的还是运气(bootstrap)
65 条视频不多,换一批视频提升可能就没了。做法是:从 65 条里有放回地随机抽 65 条,重新算一次“调参后 − 默认”的 SRCC 差,这样重复 2000 次, 取中间 95% 作为区间。区间整体在 0 以上 = 提升比较可信;区间跨过 0 = 说不准,可能只是运气。
结果(CVVDP 逐帧 JOD)
| 规则 | 默认 → 调好的 | 练习 SRCC | 考试 SRCC | 提升 95% 区间 |
|---|---|---|---|---|
exp_weighted_minkowski | p 2→4, τ 480→1920 | 0.608 → 0.659 | 0.477 → 0.509 | [−0.02, +0.08] |
minkowski | p 2 → 2(没变) | 0.653 | 0.506 | — |
hysteresis | τ 2→0.1 s, α 0.8→0 | 0.586 → 0.639 | 0.486 → 0.497 | [−0.06, +0.08] |
percentile | p 5% → 100% | 0.523 → 0.639 | 0.393 → 0.493 | [−0.02, +0.24] |
kth_percentile | k 50 → 90 | 0.444 → 0.622 | 0.396 → 0.485 | [−0.04, +0.24] |
increasing_weights | 3 段 → 2 段 | 0.600 → 0.608 | 0.479 → 0.487 | [−0.01, +0.04] |
decreasing_weights | 3 段 → 2 段 | 0.608 → 0.623 | 0.503 → 0.487 | [−0.06, +0.02] |
early_dominance | 3 段 → 2 段 | 0.420 → 0.551 | 0.313 → 0.413 | [+0.02, +0.18] |
late_dominance | 3 段 → 2 段 | 0.496 → 0.580 | 0.413 → 0.437 | [−0.01, +0.07] |
worst_window | N 480 → 180 | 0.603 → 0.670 | 0.486 → 0.409 | [−0.16, +0.00] |
last_frames_mean | 600 → 600(没变) | 0.639 | 0.493 | — |
mean(对照) | — | 0.639 | 0.493 | — |
考试 SRCC 是 65 条视频放在一起算的。这里的数字和结果页不能直接比:考试题里没有 makeway,逐帧输入是 cvvdp_combined_version_a/frames 里的完整 540 帧(这 84 条 pilot 视频都没有负分帧,所以去不去掉负分帧都一样)。
怎么读这个结果
- 练习分涨得多,考试分涨得少,甚至变差。这是过拟合的典型样子。最明显的是
worst_window:在 makeway 上从 0.60 涨到 0.67,到别的 scene 反而从 0.49 掉到 0.41。 - 几乎所有区间都跨过 0。11 个规则里只有
early_dominance的提升是可信的,而那只是因为默认的“只看第 1 个 chunk”太极端,改成 2 段后变成“只看前 2.25 s”,离 mean 更近了。 - 调参后大多“往 mean 靠”。percentile p = 100% 就是 mean;k = 90 只是取一个偏好帧的值;τ = 1920 帧(16 s)让权重几乎一样平;hysteresis 的 τ = 0.1 s 几乎没有记忆。 可以理解为:在 makeway 上,数据并不支持“观众盯着最差的帧”或“强近因效应”,越像平均越好。
- Minkowski 的曲线很平。在 makeway 上 p 从 −8 到 8,SRCC 只在 0.57–0.65 之间变化,p = 1(平均)是 0.639,p = 2 是 0.653,这点差别在 19 条视频上就是噪声。偏向差帧的 p < 0 反而稍差。
- 曲线忽高忽低就是噪声。例如
kth_percentile:k = 5 是 0.61,k = 10 掉到 0.51,k = 30 又到 0.59。一个真实的效应不会这样跳,19 条视频太少了。 - VMAF 和 PSNR-Y 的结论一样:percentile 也被调到 p = 100%(= mean);PSNR-Y 的 percentile 提升可信([+0.06, +0.50]),原因同样是默认的 5% 太苛刻,并不是找到了更好的规则。
7. 参数选择的总结与局限
- 原则:不在 pilot 数据上调参,直接用文献默认 / 推荐值,只把帧数换算成 120 Hz 下相同的秒数。这样比较的是“文献里的规则原样拿来用”,避免在 84 条视频上过拟合。
- 后果 1:时长不匹配。为 8–10 s 片段或几分钟 session 设计的时间窗口(5 s、50–60 s、4 s)在 4.5 s 视频上要么覆盖全片(退化为 mean),要么几乎覆盖全片。
- 后果 2:分数方向。Minkowski 的 p 与 kth_percentile 的 k 在论文中多针对失真分数;直接套在 JOD(质量分数)上,p = 2 / k = 50 并没有起到“强调最差帧”的作用。
- 有效规则数:15 个规则里只有 12 个结果不同(
rule_equivalence.csv),结果页把等价的合并成一行。 - 可以考虑的改进:按 chunk 时长缩放窗口(例如 last_frames = 1 个 chunk)、对质量分数用 p < 1 或小 k、或把参数在训练 scene 上拟合后用跨 scene 交叉验证评估。第 6 节在 makeway 上的初步尝试显示,调参后大多数规则回到 mean 附近,在其它 scene 上没有可信的提升。