| """Small public wrapper around the bundled Basenji Saluki implementation.""" |
|
|
| from __future__ import annotations |
|
|
| import json |
| import os |
| from pathlib import Path |
| from typing import Any, Mapping |
|
|
|
|
| def load_params(path: str | Path) -> dict[str, Any]: |
| """Load parameters and normalize historical scalar multi-head settings.""" |
|
|
| with Path(path).open(encoding="utf-8") as handle: |
| params = json.load(handle) |
|
|
| model_params = dict(params["model"]) |
| num_targets = model_params.get("num_targets", 1) |
| heads = int(model_params.get("heads", 1)) |
| if not isinstance(num_targets, list): |
| model_params["num_targets"] = [num_targets] * heads |
|
|
| normalized = dict(params) |
| normalized["model"] = model_params |
| return normalized |
|
|
|
|
| class SalukiModel: |
| """Construct, restore and run one head of the official Saluki network.""" |
|
|
| def __init__(self, params: Mapping[str, Any], head: int = 0): |
| os.environ.setdefault("TF_USE_LEGACY_KERAS", "1") |
| from .basenji.rnann import RnaNN |
|
|
| model_params = dict(params["model"] if "model" in params else params) |
| num_targets = model_params.get("num_targets", 1) |
| heads = int(model_params.get("heads", 1)) |
| if not isinstance(num_targets, list): |
| model_params["num_targets"] = [num_targets] * heads |
|
|
| self.network = RnaNN(model_params) |
| if not 0 <= head < len(self.network.models): |
| raise ValueError( |
| f"head must be in [0, {len(self.network.models) - 1}], got {head}" |
| ) |
| self.head = head |
| self.network.model = self.network.models[head] |
|
|
| @property |
| def keras_model(self): |
| return self.network.models[self.head] |
|
|
| def restore(self, weight_path: str | Path) -> "SalukiModel": |
| self.network.restore(str(weight_path), head_i=self.head) |
| return self |
|
|
| def predict(self, inputs, **kwargs): |
| return self.keras_model.predict(inputs, **kwargs) |
|
|