#!/usr/bin/env python3
"""Generate PUBLIC.md: neutral third-person write-up. Every number is read from the result files,
none retyped. Contains no correspondence traces (no second person, no request id, no dates of exchange)."""
import json
from pathlib import Path
W = Path(__file__).resolve().parents[1]; PKG = W.parent
A = json.load(open(W / "results/analysis.json")); H = json.load(open(W / "results/posthoc_train_overlap.json"))
R = json.load(open(W / "results/recheck.json")); V = json.load(open(W / "data/variants.json"))
ARMS = json.load(open(W / "code/run_config.json"))["arms"]; ver = A["verdicts"]; d = A["descriptive_by_gt_label_not_a_test"]
pct = lambda x: f"{x*100:+.2f}"
def ci(v): return f"[{v[0]*100:+.2f}, {v[1]*100:+.2f}]"
def row(a):
    r = A["arms"][a]; rate = A["rate"][a]["mean_bpp"]
    if a == "orig": return f"| {a} | {rate:.3f} | {r['pope']['acc']:.4f} | — | — | — | {r['vqa_acc']:.4f} | — | — |"
    v = A["vs_orig"][a]
    return (f"| {a} | {rate:.3f} | {r['pope']['acc']:.4f} | {pct(v['pope_delta_acc'])} | {ci(v['pope_delta_ci95_cluster_boot'])} | "
            f"{v['pope_mcnemar']['p_exact_two_sided']:.3g} | {r['vqa_acc']:.4f} | {pct(v['vqa_delta_acc'])} | {ci(v['vqa_delta_ci95_cluster_boot'])} |")
mj = A["codec_pairs"]["mbt2018mean_t1_vs_jpeg_t1"]; bj = A["codec_pairs"]["bpg_t1_vs_jpeg_t1"]; wj = A["codec_pairs"]["webp_t1_vs_jpeg_t1"]
jt1 = A["vs_orig"]["jpeg_t1"]; ctl = A["vs_orig"]["bpg_q51"]; o = A["arms"]["orig"]["pope"]
hs = H["arms"]; NI = "not_in_coco_train2017"; IN = "in_coco_train2017"
def hrow(arm, task):
    x, y = hs[arm][IN][task], hs[arm][NI][task]; dd = H["difference_between_strata"][task][arm]
    return (f"| {arm} | {'POPE' if task=='pope' else 'VQA-v2'} | {pct(x['delta'])} {ci(x['ci95'])} | "
            f"{pct(y['delta'])} {ci(y['ci95'])} | {pct(dd['point'])} {ci(dd['ci95'])} |")
t2 = ["jpeg_t2", "webp_t2", "bpg_t2", "mbt2018mean_t2"]
md = f"""# 图像压缩对多模态问答下游准确率的影响：两个码率点上的 CPU 基线

固定下游模型 LLaVA-1.5-7B（Q8_0 量化 GGUF，llama.cpp CPU 推理，temperature 0），
在 POPE-random 与 VQA-v2 的同一批图像上，比较传统编码（JPEG / WebP / BPG）与一个经典神经图像压缩模型
（CompressAI 预训练 mbt2018-mean）在 0.25 与 0.5 bpp 两个码率点上的下游准确率。
全部预测、判据与混杂在任何主实验推理之前写死并取指纹（`claims.json`，核心指纹
`97abcabf5c6585a91ce8c26a18f12007da7d1d3d0cc4beb869ddb53f062b37ff`）。

## 0. 结论，以及每句话的适用范围

1. **在这 102 张 COCO 图上（其中 85 张属 COCO train2017，即 LLaVA-1.5 指令数据的 COCO 图源；
   POPE 的 yes/no 标签也来自同一批 COCO 物体标注），POPE 上唯一能测出掉点的编码器是 JPEG**：
   0.25 bpp 掉 {-jt1['pope_delta_acc']*100:.2f} 个百分点（95% CI {ci(jt1['pope_delta_ci95_cluster_boot'])}，
   p={jt1['pope_mcnemar']['p_exact_two_sided']:.3g}）。这是**次要比较**，不在 5 个主要比较之内，p 也没过 Bonferroni 0.01，
   三个图像子集并不一致（Δ 分别 {pct(jt1['pope_delta_by_subset']['S1'])} / {pct(jt1['pope_delta_by_subset']['S2'])} / {pct(jt1['pope_delta_by_subset']['S3'])} 点）。
   其余三个编码器的 POPE 差异区间都**包含 0**——这意味着**未能检出差异，不等于没有损失**。
2. **换成开放式问答，同一个码率的代价明显不是"零"。** 0.25 bpp 下四个编码器在 VQA-v2 上相对原图**全都**掉分且区间都不含 0：
   JPEG {A['arms']['jpeg_t1']['vqa_acc']:.4f}（{pct(jt1['vqa_delta_acc'])}）、BPG {A['arms']['bpg_t1']['vqa_acc']:.4f}（{pct(A['vs_orig']['bpg_t1']['vqa_delta_acc'])}）、
   WebP {A['arms']['webp_t1']['vqa_acc']:.4f}（{pct(A['vs_orig']['webp_t1']['vqa_delta_acc'])}）、
   mbt2018-mean {A['arms']['mbt2018mean_t1']['vqa_acc']:.4f}（{pct(A['vs_orig']['mbt2018mean_t1']['vqa_delta_acc'])}），
   对照原图 {A['arms']['orig']['vqa_acc']:.4f}。但跑前写死的直接比较（P6：同一臂上 VQA 掉幅是否大于 POPE 掉幅）在 JPEG 上**不可判**
   （Δ={pct(ver['P6_vqa_more_sensitive_jpeg_t1']['vqa_delta_minus_pope_delta'])} 点，CI {ci(ver['P6_vqa_more_sensitive_jpeg_t1']['ci95'])}）；
   把两个任务各自的"可跌空间"归一化后，相对损失几乎一样（POPE 7.69%、VQA 8.11%）。
   因此**不能**写成"开放式问答天生更敏感"。
3. **到 0.5 bpp，四个编码器在 POPE 上都不差于原图超过 3 个百分点**（预注册的非劣效判据：CI 下界 ≥ −3 点；
   实测下界分别 {A['vs_orig']['jpeg_t2']['pope_delta_ci95_cluster_boot'][0]*100:.2f} / {A['vs_orig']['webp_t2']['pope_delta_ci95_cluster_boot'][0]*100:.2f} /
   {A['vs_orig']['bpg_t2']['pope_delta_ci95_cluster_boot'][0]*100:.2f} / {A['vs_orig']['mbt2018mean_t2']['pope_delta_ci95_cluster_boot'][0]*100:.2f} 点）。
   **这条非劣效结论只在 POPE 上判过**：同一码率下 VQA 点估计仍在下降
   （JPEG {pct(A['vs_orig']['jpeg_t2']['vqa_delta_acc'])}、BPG {pct(A['vs_orig']['bpg_t2']['vqa_delta_acc'])}、
   mbt2018-mean {pct(A['vs_orig']['mbt2018mean_t2']['vqa_delta_acc'])}、WebP {pct(A['vs_orig']['webp_t2']['vqa_delta_acc'])} 个百分点，
   其中 **WebP 的区间不含 0**）。

**口径说明**：bpp 一律按**原分辨率**计算，而 LLaVA-1.5 实际只看 336×336 缩图，送进模型的有效码率**远高于**标称值；
原图这一臂本身是约 {A['rate']['orig']['mean_bpp']:.3f} bpp 的 JPEG，**不是无损基线**。
本轮设定沿用了评测中常见的"码率-准确率前沿"提法，但**只有两个码率点，画不出前沿曲线**，
也定不出拐点位置；`analysis.json` 中的 `dominance_screen_*` 仅为点估计筛选，不是统计意义上的占优。

## 1. 这一轮不能支持什么

- 不能给出可与论文直接对照的完整 POPE / VQA-v2 基准分数：本轮是 102 张图、612 道 POPE 题、589 道 VQA 题的子集。
- 不能描述两个码率点之间曲线的形状或拐点位置。
- **不能分辨小于区间半宽的差异**：POPE 上"某臂 vs 原图"的 95% 区间半宽为 0.98–2.45 个百分点（中位 1.47），
  "编码器两两比较"为 1.31–2.53（中位 1.76），VQA 上"某臂 vs 原图"中位 1.94。这是区间宽度，不是功效分析。
- **不能排序 WebP、BPG、mbt2018-mean 三者**：彼此差距约 1 个百分点，落在各自区间半宽之内。
- 不能把结论外推到模型没见过的图；本轮也构造不出能核实"没见过"的对照层（见第 4 节）。
- 不能外推到其他下游模型、fp16 原版权重、其他数据集，或面向机器视觉专门训练的编码器；
  神经压缩一侧只有一个按 MSE 训练的预训练模型，不能代表这一类方法。
- 没有真正的无损基线：COCO 原图发布时本身已是 JPEG。

## 2. 预注册预测的判定

判定由分析代码按写死的阈值机械算出（`analysis.json` 的 `verdicts`），不是人工判断。

| 预测 | 判定 | 实测 |
|---|---|---|
| P1 完整性（原图 POPE 准确率 ∈ [0.80,0.93]，yes/no 格式率 ≥0.98） | {ver['P1_sanity']['verdict']} | 准确率 {o['acc']:.4f}，格式率 {o['raw_yes_no_format_rate']:.4f} |
| P2 正对照（0.03 bpp 掉 ≥5 点且 p<0.01） | {ver['P2_positive_control']['verdict']} | Δ={pct(ctl['pope_delta_acc'])} 点，CI {ci(ctl['pope_delta_ci95_cluster_boot'])}，p={ctl['pope_mcnemar']['p_exact_two_sided']:.3g} |
| P3 零基线（原图比"全答 yes"高 ≥25 点） | {ver['P3_null']['verdict']} | {o['acc']:.4f} vs {A['null_pope_all_yes_acc']:.4f} |
| **P4 主比较**（0.25 bpp：mbt2018-mean > JPEG） | **`{ver['P4_mbt2018mean_t1_gt_jpeg_t1']['verdict']}`** | Δ={pct(mj['pope_delta_acc'])} 点，CI {ci(mj['pope_delta_ci95_cluster_boot'])}；p={mj['pope_mcnemar']['p_exact_two_sided']:.4f} 未过 Bonferroni 0.01；三个子集 Δ 为 {pct(mj['pope_delta_by_subset']['S1'])} / {pct(mj['pope_delta_by_subset']['S2'])} / **{pct(mj['pope_delta_by_subset']['S3'])}** 点 |
| P4 次要（0.25 bpp：BPG > JPEG） | {ver['P4_bpg_t1_gt_jpeg_t1']['verdict']} | Δ={pct(bj['pope_delta_acc'])} 点，CI {ci(bj['pope_delta_ci95_cluster_boot'])}，p={bj['pope_mcnemar']['p_exact_two_sided']:.4f} |
| P5 高码率非劣（0.5 bpp，**只在 POPE 上判**）×4 | 四条全部 {ver['P5_jpeg_t2_within_3pts']['verdict']} | 下界均 ≥ −1.31 点，远好于 −3 点的非劣效界 |
| P6（0.25 bpp JPEG：VQA 掉幅大于 POPE） | {ver['P6_vqa_more_sensitive_jpeg_t1']['verdict']} | Δ={pct(ver['P6_vqa_more_sensitive_jpeg_t1']['vqa_delta_minus_pope_delta'])} 点，CI {ci(ver['P6_vqa_more_sensitive_jpeg_t1']['ci95'])} |

P4 的判定字符串本身带着限定：按它自己写死的 CI 判据成立，但没有通过预先声明的多重比较阈值，
且三个子集中有一个效应为 0。方向可信、强度不稳，**不能**当成"某一类编码器确定更好"。
同码率下另两个对照属探索项：WebP − JPEG Δ={pct(wj['pope_delta_acc'])} 点，CI {ci(wj['pope_delta_ci95_cluster_boot'])}，
p={wj['pope_mcnemar']['p_exact_two_sided']:.4f}。

## 3. 逐臂结果

POPE n={A['n_pope']} 配对题，VQA-v2 n={A['n_vqa']} 配对题；区间为图级聚类 bootstrap 95% CI（102 图重采样 10000 次）。

| 臂 | 平均 bpp | POPE | Δ vs 原图 | 95% CI | McNemar p | VQA | Δ vs 原图 | 95% CI |
|---|---|---|---|---|---|---|---|---|
""" + "\n".join(row(a) for a in ARMS) + f"""

**码率对齐**：同一目标下各臂平均 bpp 相差不超过 1.5%（相对 JPEG 的比值 0.998–1.015）。
逐图并非严格相等：JPEG 有 2 张图在质量 1 仍高于 0.25 bpp；mbt2018-mean 只有 8 个预训练质量档，颗粒最粗。
**0.5 bpp 档存在码率不对称**：mbt2018mean_t2 的中位码率 {A['rate']['mbt2018mean_t2']['median_bpp']:.3f} bpp，
比其他编码器高约 4%（相对 JPEG 均值之比 {A['codec_pairs']['mbt2018mean_t2_vs_jpeg_t2']['bpp_ratio_a_over_b']:.3f}），
T1 上匹配良好（{mj['bpp_ratio_a_over_b']:.3f}）。

## 4. 事后描述性分析：测试图与训练图源的重叠

跑完之后补做，**不改变**任何预注册判定。LLaVA-1.5 的指令数据从 COCO train2017 取图；本轮 102 张测试图取自 COCO val2014，
实测 **85 张落在 train2017、17 张落在 val2017**，两边都不在的 0 张。

**命名限制**：两层准确的名字只是"属 COCO train2017"与"不属 COCO train2017"。后者**不能**称为模型没见过的图：
LLaVA-1.5 的训练混合还包含 Visual Genome / GQA / TextCaps 等图源，Visual Genome 与 COCO 两个划分都有重叠，
且本轮没有把这 17 张与实际训练图片清单逐一核对。**这一层本身不能用来判断训练重叠有没有影响。**

| 臂 | 任务 | 属 train2017（85 张） | 不属 train2017（17 张） | 两层之差 |
|---|---|---|---|---|
""" + "\n".join([hrow("jpeg_t1", "pope"), hrow("jpeg_t1", "vqav2"), hrow("webp_t1", "vqav2"),
                 hrow("bpg_t1", "vqav2"), hrow("mbt2018mean_t1", "vqav2"), hrow("mbt2018mean_t2", "vqav2"),
                 hrow("bpg_q51", "pope")]) + f"""

在属 train2017 的图上得到的两条结论——"0.25 bpp 下非 JPEG 编码器未检出损失"与"0.5 bpp 在 POPE 上非劣于原图"——
在另外 17 张上都不再成立，八个编码器臂的点估计都大出约一倍以上。**但这不是一个结论**，有四条反面限制：

1. 该层只有 17 张图（POPE {H['n_questions'][NI]['pope']} 题、VQA {H['n_questions'][NI]['vqav2']} 题），区间很宽；
   **两层之差本身并不显著**（上表最后一列的区间几乎都包含 0）。
2. **极端对照只在 POPE 上不跟着走**：bpg_q51 在 POPE 上两层几乎一样
   （{pct(hs['bpg_q51'][IN]['pope']['delta'])} 对 {pct(hs['bpg_q51'][NI]['pope']['delta'])} 点）；
   但它在 VQA 上两层差距很大（{pct(hs['bpg_q51'][IN]['vqav2']['delta'])} 对 {pct(hs['bpg_q51'][NI]['vqav2']['delta'])} 点），
   **方向与其他臂一致**。这条反面证据只成立于 POPE。
3. **两层难度本来就不同，且方向相反**：原图在不属 train2017 的 17 张上 POPE 反而**更高**
   （{hs['orig'][NI]['pope_abs_acc']:.4f} 对 {hs['orig'][IN]['pope_abs_acc']:.4f}），VQA 则相反
   （{hs['orig'][NI]['vqav2_abs_acc']:.4f} 对 {hs['orig'][IN]['vqav2_abs_acc']:.4f}）。没有按难度或基线配对。
4. 没有交互项检验、没有功效分析，两层规模 85 对 17 极不平衡。

因此本轮**既不能确认、也不能排除**训练重叠在其中的作用。

## 5. 掉点落在哪一侧（描述性，非假设检验）

POPE 真值严格 50/50。模型在**原图**上就偏向回答 "no"：只有 {o['yes_ratio']*100:.1f}% 的题答 yes，
gt=yes 侧准确率 {d['orig']['acc_on_gt_yes']:.4f}，gt=no 侧 {d['orig']['acc_on_gt_no']:.4f}。

预注册里 E1 被命名为"幻觉方向偏移"，**这个名字不成立**（预注册已锁，只能就地更正）：实测是
**召回率下降**（{A['arms']['orig']['pope']['recall']:.4f} → {A['arms']['bpg_q51']['pope']['recall']:.4f}）、
精确率几乎不变（{A['arms']['orig']['pope']['precision']:.4f} → {A['arms']['bpg_q51']['pope']['precision']:.4f}）。
可以说的是漏检增加可检出；**不能**说幻觉没有增加——POPE 假阳性本来就极少
（原图 {d['orig']['n_wrong_gt_no_arm']} 道 → bpg_q51 {d['bpg_q51']['n_wrong_gt_no_arm']} 道），计数太小，
幻觉方向有没有变化本轮**判断不了**，两个方向都不能断言。

## 6. 运行与复核

10 个臂 × {A['n_pope']+A['n_vqa']} 道题 = {A['compute']['n_requests']} 次推理，全部 temperature 0 确定性解码，纯 CPU 单实例 3 线程，
墙钟约 28.86 小时（约 86.6 核时）。循环顺序为 臂→图→该图全部问题，使每张压缩图只做一次图像预填
（实测每张新图首题 {A['compute']['mean_first_question_s']:.2f} 秒，同图追问 {A['compute']['mean_cached_question_s']:.2f} 秒）。

运行中途因**人为误操作**导致推理服务被中止一次；驱动按重试逻辑恢复服务，把受影响的那张图**整张重算**后跑完全部
1020 个图×臂。事后复核：{A['compute']['n_requests']} 行、每臂恰好 1201 行、重复 0、同键冲突 0、缺失 0、空答案 0。

主分析（numpy）与独立复算（纯标准库、独立实现的打分代码）在所有臂的 POPE 准确率、VQA 准确率、
平均 bpp 与 McNemar p 上完全一致；重跑分析产出的结果文件逐字节相同。

## 7. 数据与权重

全部匿名直连可下载，无需注册或数据使用协议：COCO val2014 图片、POPE 标注（MIT）、VQA-v2 val 标注（CC BY 4.0）、
LLaVA-1.5-7B 的 GGUF 权重（Llama 2 许可）、CompressAI 预训练 mbt2018-mean、libbpg 0.9.8。
逐条状态码、体积与校验值记录在 `source_audit.json` 与 `work/data/selection.json`。
POPE 标注取自仓库 main 分支，该文件最后修改提交为 `edebdda36a96`；复现建议用固定 commit 并核对 sha256。
"""
(PKG / "PUBLIC.md").write_text(md, encoding="utf-8")
print("PUBLIC.md written:", len(md), "chars")
