diff --git a/docs/cli.md b/docs/cli.md index d3ebe21..6cd09b4 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -90,7 +90,8 @@ and does not set `is_ai_generated`. The contract is AI versus camera. Receipts, UI, and digital art are out of scope. Microsoft is not a pixel class: a DALL-E Bing image scores as `openai`, -an Imagen Designer image as `google`. Weights download on first use from +an Imagen Designer image as `google`. `tc260` is the China AIGC label +standard, not one producer. Weights download on first use from [`wiltodelta/raiw-models`](https://huggingface.co/wiltodelta/raiw-models), or from `RAIW_CLASSIFY_WEIGHTS` if that directory already holds the freeze files. diff --git a/docs/module-internals.md b/docs/module-internals.md index d0f547e..d0b9834 100644 --- a/docs/module-internals.md +++ b/docs/module-internals.md @@ -587,8 +587,9 @@ derived results influence detection. [`classify.py`](../src/remove_ai_watermarks/classify.py) is the 2026-08-31 photo freeze: CLIP-L-ft ridge AND freeze MLP, then 124-d focal heads only on DEFINITELY. The public label is `ai` / `human` / `unknown`. POSSIBLY is -`unknown`. Provider is `openai` / `google` / `muse-image` / `tc260` / `None`. -The freeze file keys the Muse Image head `meta_muse_image`. Tests in +`unknown`. The named class is `openai` / `google` / `muse-image` / `tc260` / +`None`. `tc260` is the China AIGC label standard, not one producer. The freeze +file keys the Muse Image head `meta_muse_image`. Tests in `tests/test_classify.py` pin the gate without downloads and pin that `identify` does not import this module. diff --git a/docs/photo-classify-hf/README.md b/docs/photo-classify-hf/README.md index 665cb96..a52aeae 100644 --- a/docs/photo-classify-hf/README.md +++ b/docs/photo-classify-hf/README.md @@ -89,9 +89,10 @@ remain open. The shipped heads do not include a receipt specialist. Provider attribution tracks the **renderer**, not the front-end. Bing Image Creator rows signed Microsoft, OpenAI score as `openai`. Designer rows signed Microsoft, Google LLC score as `google`. Native Designer is mixed. There is -no Microsoft pixel class. `openai`, `google`, and `tc260` are provider classes. +no Microsoft pixel class. `openai` and `google` are provider classes. `muse-image` is Muse Image output, not a general Meta class. Instagram -`made_with_ai` is not that class. +`made_with_ai` is not that class. `tc260` is the China AIGC label standard, +not one producer: Doubao, Jimeng, Qwen, Kling, and others share that class. ## Architecture @@ -117,8 +118,9 @@ no Microsoft pixel class. `openai`, `google`, and `tc260` are provider classes. - Decision: a class wins only if it beats `no_ai` by margin 0.30, then argmax among those that passed - Public name `muse-image` is the Muse Image class. The freeze file still - keys that head `meta_muse_image`. `no_ai` is not reported as `human`; it - becomes `provider=None` on an `ai` label (for example FLUX) + keys that head `meta_muse_image`. `tc260` is the China AIGC label + standard (mixed producers), not a company. `no_ai` is not reported as + `human`; it becomes `provider=None` on an `ai` label (for example FLUX) ## Training @@ -158,13 +160,13 @@ DEFINITELY is the shipped operating point. | Detector DEFINITELY | Open Images fresh | 1.3% (n=3,000) | | Detector DEFINITELY | Kodak | 0/24 | | Detector DEFINITELY | FLUX hold | 83.0% (n=300) | -| Provider | OpenAI test | 90.8% (345/380 of 381) | -| Provider | Google test | 90.9% (339/373 of 377) | -| Provider | TC260 test | 78.6% (298/379 of 384) | -| Provider | Meta hold-out v2 | 85.7% (66/77 listed 79) | -| Provider | Meta hold-out v3 | 89.4% (177/198) | -| Provider | Meta hold-out pooled | 88.4% (243/275 listed 277) | -| Provider | meme templates, ungated | 29.1% leak (86 of 97) | +| Class | OpenAI test | 90.8% (345/380 of 381) | +| Class | Google test | 90.9% (339/373 of 377) | +| Class | TC260 test | 78.6% (298/379 of 384) | +| Class | Muse Image hold-out v2 | 85.7% (66/77 listed 79) | +| Class | Muse Image hold-out v3 | 89.4% (177/198) | +| Class | Muse Image hold-out pooled | 88.4% (243/275 listed 277) | +| Class | meme templates, ungated | 29.1% leak (86 of 97) | The ungated meme leak is why Model 2 must not run on every file. Gated on DEFINITELY, that leak is not a provider attribution. diff --git a/docs/photo-classify-hf/operating-point.json b/docs/photo-classify-hf/operating-point.json index 29f9770..18084e7 100644 --- a/docs/photo-classify-hf/operating-point.json +++ b/docs/photo-classify-hf/operating-point.json @@ -22,6 +22,13 @@ "feature_width": 124, "margin": 0.3, "classes": ["openai", "google", "tc260", "muse-image", "no_ai"], + "class_kind": { + "openai": "provider", + "google": "provider", + "tc260": "label-standard", + "muse-image": "model", + "no_ai": "negative" + }, "checkpoint_keys": { "muse-image": "meta_muse_image" }, diff --git a/docs/photo-classify-training.md b/docs/photo-classify-training.md index 9235b76..bd243d2 100644 --- a/docs/photo-classify-training.md +++ b/docs/photo-classify-training.md @@ -79,7 +79,8 @@ construction and fits the same heads from the pack above. 2. AND that MLP with the frozen ridge in the probe file. DEFINITELY is both. 3. Load 124-d vectors. Fit one-vs-rest focal heads for openai, google, tc260, meta_muse_image, no_ai. Margin 0.30. The public class for the Muse Image - head is `muse-image`. + head is `muse-image`. `tc260` is the China AIGC label standard, mixed + producers, not one company. 4. Repeat with the same seeds. Compare bytes. CLIP-L fine-tune of the last two vision blocks is earlier, seed 20260822, and diff --git a/docs/photo-classify.md b/docs/photo-classify.md index 27ada87..740e2a7 100644 --- a/docs/photo-classify.md +++ b/docs/photo-classify.md @@ -84,9 +84,10 @@ wrong job on that bank, which is why Model 2 is gated. Provider names the renderer, not the product UI. Bing Image Creator signed Microsoft, OpenAI scores `openai`. Designer signed Microsoft, Google LLC -scores `google`. There is no Microsoft pixel class. `openai`, `google`, and -`tc260` are provider classes. `muse-image` is Muse Image output, not a -general Meta class and not an Instagram `made_with_ai` tag. +scores `google`. There is no Microsoft pixel class. `openai` and `google` are +provider classes. `muse-image` is Muse Image output, not a general Meta +class. `tc260` is the China AIGC label standard, not one producer: Doubao, +Jimeng, Qwen, Kling, and others share that residual class. ## Evaluation @@ -98,12 +99,12 @@ Two CPU retrains were byte-identical. DEFINITELY is the shipped cut. | Detector DEFINITELY | Open Images fresh | 1.3% (n=3,000) | | Detector DEFINITELY | Kodak | 0/24 | | Detector DEFINITELY | FLUX hold | 83.0% (n=300) | -| Provider | OpenAI | 90.8% (345/380 of 381) | -| Provider | Google | 90.9% (339/373 of 377) | -| Provider | TC260 | 78.6% (298/379 of 384) | -| Provider | Meta hold-out v3 | 89.4% (177/198) | -| Provider | Meta hold-out pooled | 88.4% (243/275 listed 277) | -| Provider | meme templates, ungated | 29.1% leak | +| Class | OpenAI | 90.8% (345/380 of 381) | +| Class | Google | 90.9% (339/373 of 377) | +| Class | TC260 | 78.6% (298/379 of 384) | +| Class | Muse Image hold-out v3 | 89.4% (177/198) | +| Class | Muse Image hold-out pooled | 88.4% (243/275 listed 277) | +| Class | meme templates, ungated | 29.1% leak | The ungated meme row is why Model 2 never runs on `human` or `unknown`. diff --git a/docs/python-api.md b/docs/python-api.md index 41f757b..464f99e 100644 --- a/docs/python-api.md +++ b/docs/python-api.md @@ -161,7 +161,8 @@ print(result.label, result.detector, result.provider) `label` is `ai` only on a DEFINITELY detector result (ridge AND freeze MLP). POSSIBLY is `unknown`. Camera-like photographs are `human`. `provider` is `openai`, `google`, `muse-image`, or `tc260` only when `label` is `ai` and the 124-d -head beats `no_ai` by the freeze margin. Otherwise it is `None`, including +head beats `no_ai` by the freeze margin. `tc260` is the China AIGC label +standard (mixed producers), not a company. Otherwise it is `None`, including when 124-d extraction refuses the file. `device` is a library parameter: `None` / `"auto"` detect, `"cpu"` or `"cuda"` diff --git a/src/remove_ai_watermarks/classify.py b/src/remove_ai_watermarks/classify.py index c617dd7..4ad9295 100644 --- a/src/remove_ai_watermarks/classify.py +++ b/src/remove_ai_watermarks/classify.py @@ -35,8 +35,9 @@ MLP_THRESHOLD = 5.9586493237495395 RIDGE_THRESHOLD = 0.3056212276800537 PROVIDER_MARGIN = 0.30 CLIP_WIDTH = 768 -# Checkpoint keys in provider.pt. openai/google/tc260 are provider classes; -# meta_muse_image is Muse Image output. Public names follow that split. +# Checkpoint keys in provider.pt. openai/google are provider classes; +# meta_muse_image is Muse Image; tc260 is the China AIGC label standard +# (mixed producers). Public names follow that split. PROVIDER_LABELS = ("openai", "google", "tc260", "meta_muse_image", "no_ai") DetectorLevel = Literal["definitely", "possibly", "likely_human"] diff --git a/tests/test_classify.py b/tests/test_classify.py index 5768b87..9c642da 100644 --- a/tests/test_classify.py +++ b/tests/test_classify.py @@ -56,6 +56,10 @@ def test_shipped_operating_point_matches_the_runtime_defaults() -> None: assert payload["model2"]["margin"] == PROVIDER_MARGIN assert payload["model2"]["runs_only_after"] == "definitely" assert payload["model2"]["classes"] == ["openai", "google", "tc260", "muse-image", "no_ai"] + assert payload["model2"]["class_kind"]["openai"] == "provider" + assert payload["model2"]["class_kind"]["google"] == "provider" + assert payload["model2"]["class_kind"]["tc260"] == "label-standard" + assert payload["model2"]["class_kind"]["muse-image"] == "model" assert payload["model2"]["checkpoint_keys"]["muse-image"] == "meta_muse_image"