cropintel / ml /scripts /package_models.py
Jaithra Polavarapu
CropIntel — HF Space deploy (all-in-one app)
889dd1b
Raw
History Blame Contribute Delete
3.33 kB
#!/usr/bin/env python3
"""
Zip ml/models/ for sharing (GitHub Release, Drive, etc.).
By default this now packages only the LATEST version per crop (not every
historical training run), which keeps release archives small.
# latest version per crop, all files (default):
python -m ml.scripts.package_models -o cropintel-models.zip
# production/mobile: only the .tflite + label_map.json of the latest version:
python -m ml.scripts.package_models --tflite-only -o cropintel-models-mobile.zip
# everything, every version (the old behaviour):
python -m ml.scripts.package_models --all-versions -o cropintel-models-full.zip
"""
import argparse
import zipfile
from pathlib import Path
from ml.config import CROPS, MODELS_DIR
from ml.inference.versions import resolve_version
def _serving_version_dir(crop_dir: Path):
"""The version predictors would serve: production.json pointer, else latest."""
version = resolve_version(crop_dir)
return crop_dir / version if version else None
def main() -> None:
parser = argparse.ArgumentParser(description="Zip trained models for release.")
parser.add_argument("-o", "--output", type=Path, default=Path("cropintel-models.zip"),
help="Output .zip path")
parser.add_argument("--all-versions", action="store_true",
help="Include every version per crop (large). Default: latest only.")
parser.add_argument("--tflite-only", action="store_true",
help="Include only model.tflite + label_map.json (smallest, mobile).")
args = parser.parse_args()
if not MODELS_DIR.is_dir():
raise SystemExit(f"No models directory: {MODELS_DIR}")
missing = [c for c in CROPS if not (MODELS_DIR / c).is_dir()]
if missing:
print(f"Warning: no folder for crops: {missing}")
keep_names = {"model.tflite", "label_map.json"}
args.output.parent.mkdir(parents=True, exist_ok=True)
n_files = 0
with zipfile.ZipFile(args.output, "w", zipfile.ZIP_DEFLATED) as zf:
for crop in CROPS:
crop_dir = MODELS_DIR / crop
if not crop_dir.is_dir():
continue
if args.all_versions:
version_dirs = [d for d in crop_dir.iterdir() if d.is_dir()]
else:
serving = _serving_version_dir(crop_dir)
version_dirs = [serving] if serving else []
for vdir in version_dirs:
for path in vdir.rglob("*"):
if not path.is_file():
continue
if args.tflite_only and path.name not in keep_names:
continue
zf.write(path, path.relative_to(MODELS_DIR))
n_files += 1
# Ship the production pointer so deployed servers pin the same version.
pointer = crop_dir / "production.json"
if pointer.exists():
zf.write(pointer, pointer.relative_to(MODELS_DIR))
n_files += 1
if version_dirs:
print(f" {crop}: packaged {version_dirs[-1].name if version_dirs else '-'}")
size_mb = args.output.stat().st_size / (1024 ** 2)
print(f"Wrote {args.output.resolve()} ({n_files} files, {size_mb:.1f} MB)")
if __name__ == "__main__":
main()