Files
imagefind/backend/imagefind/model_validation.py
T

107 lines
4.5 KiB
Python

from __future__ import annotations
import json
from pathlib import Path
from xml.etree import ElementTree
def has_openvino_ir(root: Path) -> bool:
"""Return true only for a matching OpenVINO XML/BIN pair."""
return any(xml.with_suffix(".bin").is_file() for xml in root.rglob("openvino*.xml"))
def validate_openvino_ir_directory(root: Path, label: str) -> None:
"""Validate the exact persisted IR directory used for inference.
Recursive IR discovery is useful for model status, but the runtime loader
must receive one unambiguous XML/BIN pair plus the Transformers config that
Optimum inspects before compiling the graph.
"""
required = ("openvino_model.xml", "openvino_model.bin", "config.json")
missing = [name for name in required if not (root / name).is_file()]
if missing:
raise ValueError(f"{label} OpenVINO 模型不完整,缺少:{', '.join(missing)}")
def validate_sentence_transformer_root(root: Path, label: str = "画面语义") -> None:
"""Validate a local SentenceTransformer snapshot without contacting Hugging Face."""
modules_path = root / "modules.json"
if not modules_path.is_file():
raise ValueError(f"{label}模型不完整,缺少 modules.json")
try:
modules = json.loads(modules_path.read_text(encoding="utf-8"))
except (OSError, UnicodeDecodeError, json.JSONDecodeError) as exc:
raise ValueError(f"{label}模型的 modules.json 无效") from exc
if not isinstance(modules, list) or not modules:
raise ValueError(f"{label}模型的 modules.json 无效")
for module in modules:
if not isinstance(module, dict):
raise ValueError(f"{label}模型的 modules.json 无效")
relative = module.get("path")
if relative and not (root / str(relative)).exists():
raise ValueError(f"{label}模型不完整,缺少模块 {relative}")
def sentence_transformer_module_path(root: Path, module_name: str) -> Path:
"""Resolve a module directory from a local SentenceTransformer snapshot."""
validate_sentence_transformer_root(root)
try:
modules = json.loads((root / "modules.json").read_text(encoding="utf-8"))
except (OSError, UnicodeDecodeError, json.JSONDecodeError) as exc:
raise ValueError("画面语义模型的 modules.json 无效") from exc
for module in modules:
module_type = str(module.get("type") or "").rsplit(".", 1)[-1]
if module_type != module_name:
continue
relative = str(module.get("path") or "")
path = root / relative if relative else root
if not (path / "config.json").is_file():
raise ValueError(f"画面语义 {module_name} 模块缺少 config.json")
return path
raise ValueError(f"画面语义模型缺少 {module_name} 模块")
def validate_face_model_root(root: Path) -> None:
"""Reject missing/truncated IR files and HTTP error pages returned with status 200."""
required = ("detector.xml", "detector.bin", "reidentification.xml", "reidentification.bin")
missing = [name for name in required if not (root / name).is_file()]
if missing:
raise ValueError(f"人物模型不完整,缺少:{', '.join(missing)}")
for name in ("detector.xml", "reidentification.xml"):
validate_face_model_file(root / name)
for name in ("detector.bin", "reidentification.bin"):
validate_face_model_file(root / name)
def validate_face_model_file(
path: Path,
*,
expected_suffix: str | None = None,
display_name: str | None = None,
) -> None:
name = display_name or path.name
suffix = expected_suffix or path.suffix
if suffix == ".xml":
try:
document = ElementTree.parse(path)
except (OSError, ElementTree.ParseError) as exc:
raise ValueError(f"人物模型文件 {name} 不是有效的 OpenVINO XML") from exc
root_tag = document.getroot()
if root_tag.tag.rsplit("}", 1)[-1] != "net" or root_tag.find("layers") is None:
raise ValueError(f"人物模型文件 {name} 不是有效的 OpenVINO IR")
return
if suffix == ".bin":
try:
size = path.stat().st_size
except OSError as exc:
raise ValueError(f"人物模型文件 {name} 无法读取") from exc
if size < 64 * 1024:
raise ValueError(f"人物模型文件 {name} 过小,下载内容可能是错误页面")
return
raise ValueError(f"无法识别的人物模型文件:{name}")