mirror of
https://github.com/wiltodelta/remove-ai-watermarks.git
synced 2026-10-14 13:30:55 +02:00
The Content Seal manifest named the production GPU worker in its provenance column and in two row keys, several docstrings described their sources as user uploads from the private corpus, and that corpus was called by the storage product holding it. Each is now what a public reader can check: a pipeline and strength, material that is not publication-cleared, and a private corpus. The corpus test follows the renamed rows. Deployment and tier detail goes the same way, since which backend each hosting tier can afford is a product decision, not a library fact. Twelve harnesses that need a cloud GPU account leave scripts/. The research pages that cite their findings name them as harnesses kept outside this repository and stop implying a reader can rerun them; the tracked inputs and recorded verdicts remain the durable record, and the offline analysis step stays here with its test. A development invariant states the rule so the next harness is not added back. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
123 lines
4.4 KiB
Python
123 lines
4.4 KiB
Python
#!/usr/bin/env python3
|
|
"""Summarize paired fidelity results from the engine-selection study.
|
|
|
|
Run from the repository root after the ``engine_selection_study.py`` harness has
|
|
written ``out/engine-selection-study/summary.json``:
|
|
|
|
uv run scripts/analyze_engine_selection_study.py
|
|
|
|
The report uses per-image paired differences and exact two-sided sign tests.
|
|
It does not turn the discovery set into a production routing rule.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import json
|
|
import logging
|
|
import math
|
|
import statistics
|
|
from collections import defaultdict
|
|
from pathlib import Path
|
|
from typing import TYPE_CHECKING, Any
|
|
|
|
if TYPE_CHECKING:
|
|
from collections.abc import Callable
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
_METRICS: dict[str, tuple[Callable[[float], float], str]] = {
|
|
"lpips_512": (lambda value: value, "lower"),
|
|
"ssim": (lambda value: -value, "higher"),
|
|
"psnr_db": (lambda value: -value, "higher"),
|
|
"mae": (lambda value: value, "lower"),
|
|
"edge_f1": (lambda value: -value, "higher"),
|
|
"laplacian_ratio": (lambda value: abs(math.log(max(value, 1e-12))), "closer_to_one"),
|
|
}
|
|
|
|
|
|
def _sign_test_two_sided(wins: int, losses: int) -> float:
|
|
"""Return the exact two-sided sign-test p-value, excluding ties."""
|
|
trials = wins + losses
|
|
if trials == 0:
|
|
return 1.0
|
|
tail = sum(math.comb(trials, index) for index in range(min(wins, losses) + 1))
|
|
return min(1.0, 2.0 * tail / (2**trials))
|
|
|
|
|
|
def _index(rows: list[dict[str, Any]]) -> dict[tuple[str, str], dict[str, Any]]:
|
|
return {(row["provider"], row["pair_id"]): row for row in rows}
|
|
|
|
|
|
def analyze(report: dict[str, Any]) -> dict[str, Any]:
|
|
qwen = _index(report["qwen-zimage"]["rows"])
|
|
chroma = _index(report["chroma-zimage"]["rows"])
|
|
if qwen.keys() != chroma.keys():
|
|
raise ValueError("Qwen and Chroma result keys differ")
|
|
|
|
by_provider: dict[str, list[tuple[dict[str, Any], dict[str, Any]]]] = defaultdict(list)
|
|
for key in sorted(qwen):
|
|
by_provider[key[0]].append((qwen[key], chroma[key]))
|
|
|
|
providers: dict[str, Any] = {}
|
|
for provider, pairs in sorted(by_provider.items()):
|
|
metrics: dict[str, Any] = {}
|
|
for metric, (loss_transform, direction) in _METRICS.items():
|
|
deltas: list[float] = []
|
|
chroma_wins = 0
|
|
qwen_wins = 0
|
|
ties = 0
|
|
for qwen_row, chroma_row in pairs:
|
|
qwen_value = float(qwen_row["fidelity"][metric])
|
|
chroma_value = float(chroma_row["fidelity"][metric])
|
|
delta = loss_transform(chroma_value) - loss_transform(qwen_value)
|
|
deltas.append(delta)
|
|
if math.isclose(delta, 0.0, abs_tol=1e-12):
|
|
ties += 1
|
|
elif delta < 0:
|
|
chroma_wins += 1
|
|
else:
|
|
qwen_wins += 1
|
|
metrics[metric] = {
|
|
"preferred_direction": direction,
|
|
"delta_definition": "chroma_loss_minus_qwen_loss; negative favors Chroma",
|
|
"chroma_wins": chroma_wins,
|
|
"qwen_wins": qwen_wins,
|
|
"ties": ties,
|
|
"mean_delta": statistics.fmean(deltas),
|
|
"median_delta": statistics.median(deltas),
|
|
"sign_test_two_sided_p": _sign_test_two_sided(chroma_wins, qwen_wins),
|
|
}
|
|
providers[provider] = {"pairs": len(pairs), "metrics": metrics}
|
|
|
|
return {
|
|
"method": "paired per-image differences with exact two-sided sign tests; ties excluded",
|
|
"warning": "Discovery-set evidence only; challenge any proposed rule on unused generations.",
|
|
"providers": providers,
|
|
}
|
|
|
|
|
|
def main() -> None:
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument(
|
|
"--input",
|
|
type=Path,
|
|
default=Path("out/engine-selection-study/summary.json"),
|
|
)
|
|
parser.add_argument(
|
|
"--output",
|
|
type=Path,
|
|
default=Path("out/engine-selection-study/analysis.json"),
|
|
)
|
|
args = parser.parse_args()
|
|
report = json.loads(args.input.read_text(encoding="utf-8"))
|
|
analysis = analyze(report)
|
|
args.output.parent.mkdir(parents=True, exist_ok=True)
|
|
args.output.write_text(json.dumps(analysis, indent=2) + "\n", encoding="utf-8")
|
|
log.info("Wrote %s", args.output)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s")
|
|
main()
|