Spaces:
Running on Zero
Running on Zero
File size: 9,700 Bytes
2407511 | 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 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 | """Strict verification for the SatQuery Vision Encoder v1 artifact bundle."""
from __future__ import annotations
import hashlib
import json
import os
from dataclasses import dataclass
from pathlib import Path
from typing import Any, Dict
from .model_artifacts import (
ModelArtifactChecksumError,
ModelArtifactError,
SATQUERY_VISION_ENCODER_V1,
resolve_model_artifact,
)
ADAPTER_FILENAME = "satquery_vision_encoder_v1_adapter.pt"
EXPECTED_ADAPTER_SHA256 = "a99c0bf0fb44044988ef1698483888c8a2e3a047d2d2d56478837575cf7626ea"
EXPECTED_MODEL_VERSION = "1.0.0"
EXPECTED_BACKBONE = "OpenCLIP ViT-L-14"
EXPECTED_OPENCLIP_MODEL = "ViT-L-14"
EXPECTED_PRETRAINED = "laion2b_s32b_b82k"
EXPECTED_OPENCLIP_REPOSITORY = "laion/CLIP-ViT-L-14-laion2B-s32B-b82K"
EXPECTED_OPENCLIP_REVISION = "1627032197142fbe2a7cfec626f4ced3ae60d07a"
EXPECTED_OPENCLIP_FILENAME = "open_clip_pytorch_model.safetensors"
EXPECTED_EMBEDDING_DIMENSION = 768
REQUIRED_FILES = (
ADAPTER_FILENAME,
"model_card.json",
"preprocessing.json",
"baseline_vs_adapted.json",
"sha256.txt",
)
EXPECTED_GENERIC_METRICS = {
"loss": 4.8016743659973145,
"image_to_text_r1": 0.0021824531722813845,
"text_to_image_r1": 0.0008729812107048929,
"image_to_text_r5": 0.010039283894002438,
"text_to_image_r5": 0.004364906344562769,
"image_to_text_r10": 0.021388040855526924,
"text_to_image_r10": 0.00654735928401351,
}
EXPECTED_ADAPTED_METRICS = {
"loss": 4.120570659637451,
"image_to_text_r1": 0.005674378015100956,
"text_to_image_r1": 0.006110868416726589,
"image_to_text_r5": 0.022697512060403824,
"text_to_image_r5": 0.03491925075650215,
"image_to_text_r10": 0.050632912665605545,
"text_to_image_r10": 0.05805325135588646,
}
class SVEError(RuntimeError):
"""Base class for internal SVE failures with a safe public message."""
public_message = "SatQuery Vision Encoder v1 is unavailable. Existing analysis remains available."
class SVEArtifactMissing(SVEError):
public_message = "SatQuery Vision Encoder v1 artifacts are unavailable. Existing analysis remains available."
class SVEChecksumMismatch(SVEError):
public_message = "SatQuery Vision Encoder v1 failed artifact verification. Existing analysis remains available."
class SVEInvalidMetadata(SVEError):
public_message = "SatQuery Vision Encoder v1 metadata is invalid. Existing analysis remains available."
class SVEUnsupportedVersion(SVEError):
public_message = "This SatQuery Vision Encoder artifact version is unsupported. Existing analysis remains available."
class SVELoadFailure(SVEError):
public_message = "SatQuery Vision Encoder v1 could not be loaded. Existing analysis remains available."
class SVEInferenceFailure(SVEError):
public_message = "SatQuery Vision Encoder v1 evidence could not be computed. Existing analysis remains available."
@dataclass(frozen=True)
class VerifiedSVEArtifacts:
"""Internal-only resolved artifacts. Paths must never be serialized publicly."""
model_dir: Path
adapter_path: Path
adapter_sha256: str
model_card: Dict[str, Any]
preprocessing: Dict[str, Any]
evaluation: Dict[str, Any]
@property
def checksum_fingerprint(self) -> str:
return f"sha256:{self.adapter_sha256[:12]}"
def configured_model_dir() -> Path:
configured = os.getenv("SVE_MODEL_DIR", "models/satquery_vision_encoder_v1").strip()
if not configured:
raise SVEArtifactMissing("SVE_MODEL_DIR is empty")
candidate = Path(configured).expanduser()
if not candidate.is_absolute():
candidate = Path(__file__).resolve().parents[1] / candidate
return candidate.resolve()
def _json_object(path: Path) -> Dict[str, Any]:
try:
value = json.loads(path.read_text(encoding="utf-8"))
except (OSError, UnicodeError, json.JSONDecodeError) as error:
raise SVEInvalidMetadata(f"Invalid metadata file: {path.name}") from error
if not isinstance(value, dict):
raise SVEInvalidMetadata(f"Metadata must be an object: {path.name}")
return value
def _require_equal(payload: Dict[str, Any], key: str, expected: Any, filename: str) -> None:
if payload.get(key) != expected:
raise SVEInvalidMetadata(f"Unexpected {key} in {filename}")
def _validate_metrics(evaluation: Dict[str, Any], model_card: Dict[str, Any]) -> None:
required = {
"loss", "image_to_text_r1", "text_to_image_r1", "image_to_text_r5",
"text_to_image_r5", "image_to_text_r10", "text_to_image_r10",
}
generic = evaluation.get("generic_openclip")
adapted = evaluation.get("satquery_vision_encoder_v1")
if not isinstance(generic, dict) or not isinstance(adapted, dict):
raise SVEInvalidMetadata("Validation comparison is missing model sections")
if set(generic) != required or set(adapted) != required:
raise SVEInvalidMetadata("Validation comparison contains unexpected metric keys")
if any(not isinstance(value, (int, float)) for value in [*generic.values(), *adapted.values()]):
raise SVEInvalidMetadata("Validation metrics must be numeric")
if model_card.get("validation_metrics") != adapted or model_card.get("generic_openclip_baseline") != generic:
raise SVEInvalidMetadata("Model card metrics do not match the validation comparison")
if generic != EXPECTED_GENERIC_METRICS or adapted != EXPECTED_ADAPTED_METRICS:
raise SVEInvalidMetadata("Validation metrics do not match the verified release")
def verify_sve_artifacts(model_dir: Path | None = None) -> VerifiedSVEArtifacts:
directory = (model_dir or configured_model_dir()).resolve()
if not directory.is_dir():
raise SVEArtifactMissing("Configured model directory does not exist")
missing = [
name
for name in REQUIRED_FILES
if name != ADAPTER_FILENAME and not (directory / name).is_file()
]
if missing:
raise SVEArtifactMissing("Required artifact files are missing")
try:
adapter_path = resolve_model_artifact(
SATQUERY_VISION_ENCODER_V1,
local_path=directory / ADAPTER_FILENAME,
)
except ModelArtifactChecksumError as error:
raise SVEChecksumMismatch(str(error)) from error
except ModelArtifactError as error:
raise SVEArtifactMissing(str(error)) from error
try:
checksum_parts = (directory / "sha256.txt").read_text(encoding="utf-8").strip().split()
except (OSError, UnicodeError) as error:
raise SVEInvalidMetadata("Checksum manifest could not be read") from error
if len(checksum_parts) != 2 or checksum_parts[1].lstrip("*") != ADAPTER_FILENAME:
raise SVEInvalidMetadata("Checksum manifest does not identify the expected adapter")
declared = checksum_parts[0].lower()
if declared != EXPECTED_ADAPTER_SHA256:
raise SVEChecksumMismatch("Checksum manifest does not match the verified release")
digest = hashlib.sha256()
try:
with adapter_path.open("rb") as stream:
for chunk in iter(lambda: stream.read(1024 * 1024), b""):
digest.update(chunk)
except OSError as error:
raise SVEArtifactMissing("Adapter could not be read") from error
actual = digest.hexdigest()
if actual != EXPECTED_ADAPTER_SHA256:
raise SVEChecksumMismatch("Adapter checksum does not match the verified release")
model_card = _json_object(directory / "model_card.json")
if model_card.get("version") != EXPECTED_MODEL_VERSION:
raise SVEUnsupportedVersion("Unsupported model version")
_require_equal(model_card, "name", "SatQuery Vision Encoder v1", "model_card.json")
_require_equal(model_card, "backbone", EXPECTED_BACKBONE, "model_card.json")
_require_equal(model_card, "pretrained_weights", EXPECTED_PRETRAINED, "model_card.json")
_require_equal(model_card, "embedding_dimension", EXPECTED_EMBEDDING_DIMENSION, "model_card.json")
_require_equal(model_card, "adaptation_dataset", "BigEarthNet.txt", "model_card.json")
_require_equal(model_card, "image_source", "BigEarthNet v2 Lithuania Summer", "model_card.json")
_require_equal(model_card, "training_pairs", 4008, "model_card.json")
_require_equal(model_card, "validation_pairs", 2291, "model_card.json")
_require_equal(model_card, "test_pairs", 2053, "model_card.json")
if (model_card.get("input") or {}).get("mode") != "RGB" or (model_card.get("input") or {}).get("input_size") != [224, 224]:
raise SVEInvalidMetadata("Model card input contract is invalid")
preprocessing = _json_object(directory / "preprocessing.json")
_require_equal(preprocessing, "model_name", EXPECTED_OPENCLIP_MODEL, "preprocessing.json")
_require_equal(preprocessing, "pretrained", EXPECTED_PRETRAINED, "preprocessing.json")
_require_equal(preprocessing, "input_size", 224, "preprocessing.json")
_require_equal(preprocessing, "color_mode", "RGB", "preprocessing.json")
_require_equal(preprocessing, "embedding_normalization", "L2", "preprocessing.json")
_require_equal(preprocessing, "openclip_transform", True, "preprocessing.json")
_require_equal(preprocessing, "sentinel_2_band_order", ["B04", "B03", "B02"], "preprocessing.json")
_require_equal(preprocessing, "reflectance_scale", 10000.0, "preprocessing.json")
evaluation = _json_object(directory / "baseline_vs_adapted.json")
_validate_metrics(evaluation, model_card)
return VerifiedSVEArtifacts(
model_dir=directory,
adapter_path=adapter_path,
adapter_sha256=actual,
model_card=model_card,
preprocessing=preprocessing,
evaluation=evaluation,
)
|