chess-tutor / src /chess_tutor /training /tensor_layout.py
github-actions[bot]
deploy prod from 06dbd16a01ddcfe02b2d936c681e3e4eaa9b141f
8e756fd
Raw
History Blame Contribute Delete
3.82 kB
"""Classify ONNX outputs into HDF5 layout rows and stack batches."""
from __future__ import annotations
from typing import Any, Dict, List, Optional, Sequence
import numpy as np
import onnxruntime as ort
from chess_tutor.lc0_onnx.session import resolve_lc0_onnx_heads
from chess_tutor.training.tensor_names import tensor_dataset_name
def classify_outputs(
sess: ort.InferenceSession,
onnx_out: Dict[str, np.ndarray],
probe_names: Sequence[str],
) -> Dict[str, Any]:
"""
Build layout description and one-row numpy copies for each stored tensor.
Probes listed in ``probe_names`` are only included if present in ``onnx_out``.
"""
policy_n, value_n, wdl_n = resolve_lc0_onnx_heads(sess)
layout: Dict[str, Any] = {"probes": []}
if policy_n and policy_n in onnx_out:
pol = np.asarray(onnx_out[policy_n], dtype=np.float32)
layout["policy_key"] = tensor_dataset_name(policy_n)
layout["policy_vec"] = pol.reshape(1, -1)
if wdl_n and wdl_n in onnx_out:
w = np.asarray(onnx_out[wdl_n], dtype=np.float32).reshape(1, -1)
layout["wdl_key"] = tensor_dataset_name(wdl_n)
layout["wdl_vec"] = w
elif value_n and value_n in onnx_out:
v = np.asarray(onnx_out[value_n], dtype=np.float32).reshape(1, -1)
layout["value_key"] = tensor_dataset_name(value_n)
layout["value_vec"] = v
for name in probe_names:
if name not in onnx_out:
continue
arr = np.asarray(onnx_out[name], dtype=np.float32)
if arr.ndim == 0:
arr = arr.reshape(1, 1)
elif arr.shape[0] != 1:
arr = np.reshape(arr, (1,) + arr.shape)
dk = tensor_dataset_name(name)
layout["probes"].append({"onnx_name": name, "dataset_key": dk, "sample": arr})
if "policy_vec" not in layout:
raise RuntimeError("Could not resolve policy output from ONNX session.")
return layout
def merge_layout_batch(
acc: Optional[Dict[str, Any]], one: Dict[str, Any]
) -> Dict[str, Any]:
"""Ensure batch of rows matches same tensor layout as first row."""
if acc is None:
return one
if one["policy_vec"].shape != acc["policy_vec"].shape:
raise RuntimeError(
f"Policy shape mismatch: {one['policy_vec'].shape} vs {acc['policy_vec'].shape}"
)
for k in ("wdl_vec", "value_vec"):
if k in acc and k in one:
if one[k].shape != acc[k].shape:
raise RuntimeError(f"{k} shape mismatch across rows")
if len(one["probes"]) != len(acc["probes"]):
raise RuntimeError("Probe count mismatch across rows")
for a, b in zip(acc["probes"], one["probes"]):
if a["dataset_key"] != b["dataset_key"] or a["onnx_name"] != b["onnx_name"]:
raise RuntimeError("Probe ordering/name mismatch across rows")
if a["sample"].shape != b["sample"].shape:
raise RuntimeError(
f"Probe {a['onnx_name']!r} shape mismatch: {b['sample'].shape} vs {a['sample'].shape}"
)
return acc
def stack_batch(layout_rows: List[Dict[str, Any]]) -> Dict[str, np.ndarray]:
"""Stack single-row layout dicts into batch arrays (B, ...)."""
pol = np.concatenate([x["policy_vec"] for x in layout_rows], axis=0)
out: Dict[str, np.ndarray] = {"policy": pol}
if layout_rows and "wdl_vec" in layout_rows[0]:
out["wdl"] = np.concatenate([x["wdl_vec"] for x in layout_rows], axis=0)
if layout_rows and "value_vec" in layout_rows[0]:
out["value_head"] = np.concatenate([x["value_vec"] for x in layout_rows], axis=0)
probes0 = layout_rows[0]["probes"]
for i, p in enumerate(probes0):
key = p["dataset_key"]
out[key] = np.concatenate([x["probes"][i]["sample"] for x in layout_rows], axis=0)
return out