English
DeepChoice / main.py
antoine.carreaud67
Restore paper experiment plans and update README
4ccb60a
Raw
History Blame Contribute Delete
5.59 kB
import argparse
import json
import logging
from pathlib import Path
import torch
import yaml
from train.test import infer_deepchoice, test_with_baselines
from train.train import build_model, build_split_loader, list_split_batches, synchronize_model_dims, train_deepchoice
logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")
def _load_config(config_path):
cfg = yaml.safe_load(Path(config_path).read_text())
return cfg
def _to_jsonable(value):
if isinstance(value, dict):
return {key: _to_jsonable(item) for key, item in value.items()}
if isinstance(value, (list, tuple)):
return [_to_jsonable(item) for item in value]
if hasattr(value, "tolist"):
return value.tolist()
return value
def main():
parser = argparse.ArgumentParser()
parser.add_argument("--config", type=Path, default="configs/config_deepchoice_base.yaml")
parser.add_argument("--mode", choices=["train", "test", "infer"], default="train")
parser.add_argument("--weights_path", default=None)
parser.add_argument("--split", default=None)
parser.add_argument("--limit_files", type=int, default=None)
parser.add_argument("--inference_output", default=None)
parser.add_argument("--metrics_output", default=None)
parser.add_argument("--comparison_config", default=None)
parser.add_argument("--comparison_weights_path", default=None)
parser.add_argument("--comparison_field_name", default="best_mlp")
args = parser.parse_args()
cfg = _load_config(args.config)
synchronize_model_dims(cfg)
if args.mode == "train":
train_deepchoice(cfg, cfg["training"]["output_dir"])
return
model = build_model(cfg)
if args.weights_path is None:
raise ValueError("--weights_path is required for --mode test and --mode infer")
weights_path = args.weights_path
state_dict = torch.load(weights_path, map_location=cfg["training"]["device"], weights_only=False)
model.load_state_dict(state_dict)
comparison_model = None
comparison_cfg = None
if args.comparison_config is not None and args.comparison_weights_path is not None:
comparison_cfg = _load_config(args.comparison_config)
synchronize_model_dims(comparison_cfg)
comparison_model = build_model(comparison_cfg)
comparison_state_dict = torch.load(
args.comparison_weights_path,
map_location=comparison_cfg["training"]["device"],
weights_only=False,
)
comparison_model.load_state_dict(comparison_state_dict)
split_name = args.split or cfg["test"]["split"]
split_paths = list_split_batches(cfg, split_name, limit=args.limit_files)
if not split_paths:
raise FileNotFoundError(f"No test batches found for split '{split_name}' in {cfg['data']['batches_root']}")
loader = build_split_loader(
cfg,
split_name,
shuffle=False,
limit=args.limit_files,
file_batch_size=cfg.get("test", {}).get("file_batch_size", 1),
)
if args.mode == "test":
metrics = test_with_baselines(model, loader, cfg, n_classes=int(cfg["model"]["num_classes"]))
logging.info(
"Test split=%s | files=%s | loss=%.4f | mIoU=%.4f | mF1=%.4f | majority mIoU=%.4f mF1=%.4f | hard_vote mIoU=%.4f mF1=%.4f | anyview mIoU=%.4f mF1=%.4f | IoUs=%s",
split_name,
len(split_paths),
metrics["loss"],
metrics["miou"],
metrics["mf1"],
metrics["baselines"]["majority"]["miou"],
metrics["baselines"]["majority"]["mf1"],
metrics["baselines"]["hard_vote"]["miou"],
metrics["baselines"]["hard_vote"]["mf1"],
metrics["baselines"]["anyview"]["miou"],
metrics["baselines"]["anyview"]["mf1"],
metrics["ious"],
)
if args.metrics_output is not None:
metrics_path = Path(args.metrics_output)
metrics_path.parent.mkdir(parents=True, exist_ok=True)
metrics_path.write_text(json.dumps(_to_jsonable(metrics), indent=2), encoding="utf-8")
return
inference_output = args.inference_output or str(Path(cfg["training"]["output_dir"]) / f"inference_{split_name}")
result = infer_deepchoice(
model,
loader,
cfg,
inference_output,
comparison_model=comparison_model,
comparison_config=comparison_cfg,
comparison_field_name=args.comparison_field_name,
)
logging.info(
"Inference split=%s | files=%s | samples=%s | accuracy=%.4f | model mIoU=%.4f mF1=%.4f | majority mIoU=%.4f mF1=%.4f | hard_vote mIoU=%.4f mF1=%.4f | anyview mIoU=%.4f mF1=%.4f | output=%s | las_tiles=%s",
split_name,
len(split_paths),
result["num_samples"],
result["accuracy"],
result["metrics"]["model"]["miou"],
result["metrics"]["model"]["mf1"],
result["metrics"]["majority"]["miou"],
result["metrics"]["majority"]["mf1"],
result["metrics"]["hard_vote"]["miou"],
result["metrics"]["hard_vote"]["mf1"],
result["metrics"]["anyview"]["miou"],
result["metrics"]["anyview"]["mf1"],
inference_output,
len(result["las_paths"]),
)
if args.metrics_output is not None:
metrics_path = Path(args.metrics_output)
metrics_path.parent.mkdir(parents=True, exist_ok=True)
metrics_path.write_text(json.dumps(_to_jsonable(result), indent=2), encoding="utf-8")
if __name__ == "__main__":
main()