Instructions to use ZibinDong/ActionCodec2 with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use ZibinDong/ActionCodec2 with Transformers:
# pip install -U transformers accelerate # Load model directly from transformers import AutoModel model = AutoModel.from_pretrained("ZibinDong/ActionCodec2", trust_remote_code=True, device_map="auto") - Notebooks
- Google Colab
- Kaggle
File size: 9,872 Bytes
1bfc3f0 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 | """One ActionCodec2Sparse physical profile: unchanged physical stage plus sparse token layer."""
from __future__ import annotations
import hashlib
import json
from collections.abc import Sequence
from numbers import Integral
from pathlib import Path
import numpy as np
from .._validation import active_dimensions
from ..cache import BoundedCache
from ..configuration import CONFIG_NAME, ActionCodec2Config, ConfigError
from ..hf import save_huggingface_metadata
from ..physical_codec import PhysicalCodec
from ..primitives import PRIMITIVES_NAME, PrimitiveStore
from .grammar import SparseGrammar
from .layout import SparseLayout
from .vocabulary import SparseVocabulary
PROFILE_PROCESSOR_CLASS = "ActionCodec2SparseProcessor"
class ActionCodec2SparseProcessor:
"""Fitted sparse profile over canonical first- or second-order profile actions."""
def __init__(
self,
config: ActionCodec2Config,
primitives: PrimitiveStore,
vocabulary: SparseVocabulary,
) -> None:
if config.set_bpe.scheme != "sparse_set_bpe":
raise ConfigError("ActionCodec2Sparse profiles require set_bpe.scheme=sparse_set_bpe")
if vocabulary.layout != SparseLayout.from_config(config, primitives):
raise ValueError("sparse layout does not match the physical primitives")
if vocabulary.lookahead != config.set_bpe.lookahead:
raise ValueError("sparse vocabulary lookahead does not match config.yaml")
self.config, self.primitives, self.vocabulary = config, primitives, vocabulary
self.physical = PhysicalCodec(config, primitives)
self.requires_previous_first_order = self.physical.requires_previous_first_order
self.action_dim = config.physical.input_dim
self.bins = primitives.bins
self._grammar_cache = BoundedCache(8)
@property
def vocab_size(self) -> int:
return self.vocabulary.vocab_size
@property
def layout(self) -> SparseLayout:
return self.vocabulary.layout
@classmethod
def from_pretrained(cls, directory: str | Path) -> ActionCodec2SparseProcessor:
"""Load ``config.yaml``, ``primitives.npz`` and ``sparse_bpe.json``."""
root = Path(directory)
config = ActionCodec2Config.from_pretrained(root)
primitives = PrimitiveStore.from_pretrained(root, input_dim=config.physical.input_dim)
return cls(config, primitives, SparseVocabulary.from_pretrained(root))
def save_pretrained(self, directory: str | Path) -> list[str]:
root = Path(directory)
self.config.save_pretrained(root)
self.primitives.save_pretrained(root)
files = [root / CONFIG_NAME, root / PRIMITIVES_NAME, self.vocabulary.save_pretrained(root)]
files.extend(
save_huggingface_metadata(
root, processor_class=PROFILE_PROCESSOR_CLASS, vocab_size=self.vocab_size
)
)
return [str(path) for path in files]
@property
def artifact_fingerprint(self) -> str:
"""Content hash over config, primitive axes and the ordered sparse rules."""
digest = hashlib.sha256()
digest.update(
json.dumps(self.config.to_dict(), sort_keys=True, separators=(",", ":")).encode()
)
for axis in self.primitives.values:
values = np.ascontiguousarray(axis)
digest.update(str(values.dtype).encode())
digest.update(np.asarray(values.shape, dtype=np.int64).tobytes())
digest.update(values.tobytes())
digest.update(
json.dumps(self.vocabulary.to_dict(), sort_keys=True, separators=(",", ":")).encode()
)
return digest.hexdigest()
def _active(self, dims: tuple[int, ...]) -> np.ndarray:
flags = np.zeros(self.action_dim, dtype=bool)
flags[list(dims)] = True
return flags
def _dense_previous_first_order(
self, previous_first_order: np.ndarray | None, dims: tuple[int, ...], batch: int
) -> np.ndarray | None:
active = set(dims)
for component in self.config.physical.components.values():
present = active.intersection(component.dims)
if (
component.primitive_order == 2
and component.space == "so3"
and present
and present != set(component.dims)
):
raise ValueError(
"second-order SO(3) requires all three active component dimensions"
)
if previous_first_order is None:
if any(
c.primitive_order == 2 and active.intersection(c.dims)
for c in self.config.physical.components.values()
):
raise ValueError(
f"second-order tokenization requires previous_first_order with shape {(batch, len(dims))} in canonical input units"
)
return (
np.zeros((batch, self.action_dim), dtype=np.float32)
if self.requires_previous_first_order
else None
)
values = np.asarray(previous_first_order, dtype=np.float32)
if values.shape != (batch, len(dims)) or not np.all(np.isfinite(values)):
raise ValueError(f"previous_first_order must be finite with shape {(batch, len(dims))}")
dense = np.zeros((batch, self.action_dim), dtype=np.float32)
dense[:, dims] = values
return dense
def quantize_batch(
self, actions: np.ndarray, *, active_dims=None, previous_first_order=None
) -> np.ndarray:
"""Compact ``(B,T,D_active)`` actions to dense cells ``(B,T,D)``, ``-1`` inactive."""
values = np.asarray(actions, dtype=np.float32)
if values.ndim != 3 or len(values) < 1 or values.shape[1] < 1:
raise ValueError("actions must have non-empty shape [B, T, D_active]")
dims = active_dimensions(active_dims, self.action_dim)
if len(dims) != values.shape[2]:
raise ValueError("active_dims must have one entry per action coordinate")
dense = np.zeros((len(values), values.shape[1], self.action_dim), dtype=np.float32)
dense[:, :, dims] = values
previous = self._dense_previous_first_order(previous_first_order, dims, len(values))
cells = np.asarray(
self.physical.encode_batch(dense, previous_first_order=previous), dtype=np.int64
)
cells[:, :, ~self._active(dims)] = -1
return cells
def encode_batch(
self,
actions: np.ndarray,
*,
active_dims: Sequence[int] | None = None,
previous_first_order: np.ndarray | None = None,
threads: int = 0,
) -> list[list[int]]:
"""Encode independent ``(B,T,D_active)`` chunks into ragged local token rows."""
cells = self.quantize_batch(
actions, active_dims=active_dims, previous_first_order=previous_first_order
)
return self.vocabulary.encode_batch(cells, threads=threads)
def cells_from_tokens(
self, rows: Sequence[Sequence[int]], *, horizon: int, active_dims=None
) -> np.ndarray:
"""Losslessly recover dense cells ``(B,horizon,D)`` with ``-1`` for inactive slots."""
dims = active_dimensions(active_dims, self.action_dim)
return self.vocabulary.decode_batch(rows, horizon, self._active(dims))
def _filled(self, cells: np.ndarray) -> np.ndarray:
zero = [0 if z is None else z for z in self.layout.zero]
return np.where(cells >= 0, cells, np.asarray(zero, dtype=np.int64))
def decode_batch(
self,
rows: Sequence[Sequence[int]],
*,
horizon: int,
active_dims=None,
previous_first_order: np.ndarray | None = None,
) -> np.ndarray:
"""Decode rows to ``(B,horizon,D_active)`` reconstructed profile actions."""
dims = active_dimensions(active_dims, self.action_dim)
cells = self.cells_from_tokens(rows, horizon=horizon, active_dims=dims)
previous = self._dense_previous_first_order(previous_first_order, dims, len(cells))
decoded = self.physical.decode_batch(self._filled(cells), previous_first_order=previous)
return np.take(decoded, dims, axis=2)
def final_first_order(
self,
rows: Sequence[Sequence[int]],
*,
horizon: int,
previous_first_order: np.ndarray,
active_dims=None,
executed_steps: int | None = None,
) -> np.ndarray:
"""Next compact ``(B,D_active)`` boundary after ``executed_steps`` codec frames."""
dims = active_dimensions(active_dims, self.action_dim)
cells = self.cells_from_tokens(rows, horizon=horizon, active_dims=dims)
if executed_steps is not None:
if (
isinstance(executed_steps, bool)
or not isinstance(executed_steps, Integral)
or not 1 <= executed_steps <= cells.shape[1]
):
raise ValueError("executed_steps must be within the chunk horizon")
cells = cells[:, :executed_steps]
previous = self._dense_previous_first_order(previous_first_order, dims, len(cells))
result = self.physical.final_first_order(self._filled(cells), previous_first_order=previous)
return np.take(result, dims, axis=1)
def grammar(self, horizon: int, *, active_dims=None) -> SparseGrammar:
"""Prefix grammar of one ``horizon``-frame chunk over the active slots."""
dims = active_dimensions(active_dims, self.action_dim)
key = (int(horizon), dims)
grammar = self._grammar_cache.get(key)
if grammar is None:
grammar = SparseGrammar(self.vocabulary, horizon, self._active(dims))
self._grammar_cache[key] = grammar
return grammar
|