(1) Make config hashable. (2) Flax

This commit is contained in:
siriuz42
2025-10-06 05:58:30 +00:00
parent c159f1bf39
commit 25bb4f20e8
8 changed files with 15 additions and 20 deletions
+1 -2
View File
@@ -24,13 +24,12 @@ optax = { version = ">=0.2.2", optional = true }
einshape = { version = ">=0.8.0", optional = true } einshape = { version = ">=0.8.0", optional = true }
orbax-checkpoint = { version = ">=0.5.15", optional = true } orbax-checkpoint = { version = ">=0.5.15", optional = true }
jaxtyping = { version = ">=0.2.29", optional = true } jaxtyping = { version = ">=0.2.29", optional = true }
typeguard = { version = ">=4.3.0", optional = true }
jax = { version = ">=0.4.26", optional = true } jax = { version = ">=0.4.26", optional = true }
[tool.poetry.extras] [tool.poetry.extras]
torch = ["torch"] torch = ["torch"]
flax = ["flax", "optax", "einshape", "orbax-checkpoint", "jaxtyping", "typeguard", "jax"] flax = ["flax", "optax", "einshape", "orbax-checkpoint", "jaxtyping", "jax"]
[tool.ruff] [tool.ruff]
line-length = 88 line-length = 88
+1 -1
View File
@@ -18,7 +18,7 @@ import dataclasses
from typing import Literal from typing import Literal
@dataclasses.dataclass(frozen=False) @dataclasses.dataclass(frozen=True)
class ForecastConfig: class ForecastConfig:
"""Options for forecasting. """Options for forecasting.
-3
View File
@@ -18,7 +18,6 @@ from flax import nnx
import jax import jax
import jax.numpy as jnp import jax.numpy as jnp
import jaxtyping import jaxtyping
import typeguard
from .. import configs from .. import configs
@@ -64,7 +63,6 @@ class ResidualBlock(nnx.Module):
else: else:
raise ValueError(f"Activation: {config.activation} not supported.") raise ValueError(f"Activation: {config.activation} not supported.")
@jaxtyping.jaxtyped(typechecker=typeguard.typechecked)
def __call__(self, x: Float[Array, "b ... i"]) -> Float[Array, "b ... o"]: def __call__(self, x: Float[Array, "b ... i"]) -> Float[Array, "b ... o"]:
return self.output_layer( return self.output_layer(
self.activation(self.hidden_layer(x)) self.activation(self.hidden_layer(x))
@@ -99,7 +97,6 @@ class RandomFourierFeatures(nnx.Module):
rngs=rngs, rngs=rngs,
) )
@jaxtyping.jaxtyped(typechecker=typeguard.typechecked)
def __call__(self, x: Float[Array, "b ... i"]) -> Float[Array, "b ... o"]: def __call__(self, x: Float[Array, "b ... i"]) -> Float[Array, "b ... o"]:
projected = self.projection_layer(x) projected = self.projection_layer(x)
cos_features = jnp.cos(projected) cos_features = jnp.cos(projected)
-3
View File
@@ -18,7 +18,6 @@ from flax import nnx
import jax import jax
import jax.numpy as jnp import jax.numpy as jnp
import jaxtyping import jaxtyping
import typeguard
Array = jaxtyping.Array Array = jaxtyping.Array
Bool = jaxtyping.Bool Bool = jaxtyping.Bool
@@ -44,7 +43,6 @@ class RMSNorm(nnx.Module):
self.num_features = num_features self.num_features = num_features
self.epsilon = epsilon self.epsilon = epsilon
@jaxtyping.jaxtyped(typechecker=typeguard.typechecked)
def __call__( def __call__(
self, inputs: Float[Array, "b ... d"] self, inputs: Float[Array, "b ... d"]
) -> Float[Array, "b ... d"]: ) -> Float[Array, "b ... d"]:
@@ -69,7 +67,6 @@ class LayerNorm(nnx.Module):
self.num_features = num_features self.num_features = num_features
self.epsilon = epsilon self.epsilon = epsilon
@jaxtyping.jaxtyped(typechecker=typeguard.typechecked)
def __call__( def __call__(
self, inputs: Float[Array, "b ... d"] self, inputs: Float[Array, "b ... d"]
) -> Float[Array, "b ... d"]: ) -> Float[Array, "b ... d"]:
-3
View File
@@ -23,7 +23,6 @@ import jax
from jax import lax from jax import lax
import jax.numpy as jnp import jax.numpy as jnp
import jaxtyping import jaxtyping
import typeguard
from .. import configs from .. import configs
from . import normalization, util from . import normalization, util
@@ -127,7 +126,6 @@ class PerDimScale(nnx.Module):
self.num_dims = num_dims self.num_dims = num_dims
self.per_dim_scale = nnx.Param(jnp.zeros(shape=(num_dims,))) self.per_dim_scale = nnx.Param(jnp.zeros(shape=(num_dims,)))
@jaxtyping.jaxtyped(typechecker=typeguard.typechecked)
def __call__(self, x: Float[Array, "b ... d"]) -> Float[Array, "b ... d"]: def __call__(self, x: Float[Array, "b ... d"]) -> Float[Array, "b ... d"]:
return x * ( return x * (
1.442695041 1.442695041
@@ -344,7 +342,6 @@ class Transformer(nnx.Module):
else: else:
raise ValueError(f"Activation: {config.ff_activation} not supported.") raise ValueError(f"Activation: {config.ff_activation} not supported.")
@jaxtyping.jaxtyped(typechecker=typeguard.typechecked)
def __call__( def __call__(
self, self,
input_embeddings: Float[Array, "b n d"], input_embeddings: Float[Array, "b n d"],
-1
View File
@@ -40,7 +40,6 @@ class DecodeCache:
value: Float[Array, "b n h d"] value: Float[Array, "b n h d"]
@jaxtyping.jaxtyped(typechecker=typeguard.typechecked)
@jax.jit @jax.jit
def update_running_stats( def update_running_stats(
n: Float[Array, "b"], n: Float[Array, "b"],
+10 -5
View File
@@ -46,6 +46,8 @@ Array = jaxtyping.Array
def try_gc(): def try_gc():
for d in jax.local_devices(): for d in jax.local_devices():
stats = d.memory_stats() stats = d.memory_stats()
if stats is None:
return
if stats["bytes_in_use"] / stats["bytes_limit"] > 0.75: if stats["bytes_in_use"] / stats["bytes_limit"] > 0.75:
gc.collect() gc.collect()
break break
@@ -458,9 +460,10 @@ class TimesFM_2p5_200M_flax(timesfm_2p5_base.TimesFM_2p5):
"""Flax implementation of TimesFM 2.5 with 200M parameters.""" """Flax implementation of TimesFM 2.5 with 200M parameters."""
model: nnx.Module = TimesFM_2p5_200M_flax_module() model: nnx.Module = TimesFM_2p5_200M_flax_module()
@classmethod
def from_pretrained( def from_pretrained(
self, cls,
*, *,
path: str | None = None, path: str | None = None,
hf_repo_id: str | None = "google/timesfm-2.5-200m-flax", hf_repo_id: str | None = "google/timesfm-2.5-200m-flax",
@@ -476,11 +479,13 @@ class TimesFM_2p5_200M_flax(timesfm_2p5_base.TimesFM_2p5):
logging.info("Loading checkpoint from: %s", path) logging.info("Loading checkpoint from: %s", path)
else: else:
raise ValueError("Either path or hf_repo_id must be provided.") raise ValueError("Either path or hf_repo_id must be provided.")
instance = cls()
checkpointer = ocp.StandardCheckpointer() checkpointer = ocp.StandardCheckpointer()
graph, state = nnx.split(self.model) graph, state = nnx.split(instance.model)
state = checkpointer.restore(path, state) state = checkpointer.restore(path, state)
self.model = nnx.merge(graph, state) instance.model = nnx.merge(graph, state)
return instance
def compile(self, forecast_config: configs.ForecastConfig, **kwargs): def compile(self, forecast_config: configs.ForecastConfig, **kwargs):
+3 -2
View File
@@ -13,6 +13,7 @@
# limitations under the License. # limitations under the License.
"""TimesFM models.""" """TimesFM models."""
import dataclasses
import logging import logging
import math import math
import os import os
@@ -349,7 +350,7 @@ class TimesFM_2p5_200M_torch(timesfm_2p5_base.TimesFM_2p5, ModelHubMixin):
self.model.p, self.model.p,
new_context := math.ceil(fc.max_context / self.model.p) * self.model.p, new_context := math.ceil(fc.max_context / self.model.p) * self.model.p,
) )
fc.max_context = new_context fc = dataclasses.replace(fc, max_context=new_context)
if fc.max_horizon % self.model.o != 0: if fc.max_horizon % self.model.o != 0:
logging.info( logging.info(
"When compiling, max horizon needs to be multiple of the output patch" "When compiling, max horizon needs to be multiple of the output patch"
@@ -357,7 +358,7 @@ class TimesFM_2p5_200M_torch(timesfm_2p5_base.TimesFM_2p5, ModelHubMixin):
self.model.o, self.model.o,
new_horizon := math.ceil(fc.max_horizon / self.model.o) * self.model.o, new_horizon := math.ceil(fc.max_horizon / self.model.o) * self.model.o,
) )
fc.max_horizon = new_horizon forecast_config = dataclasses.replace(fc, max_horizon=new_horizon)
if fc.max_context + fc.max_horizon > self.model.config.context_limit: if fc.max_context + fc.max_horizon > self.model.config.context_limit:
raise ValueError( raise ValueError(
"Context + horizon must be less than the context limit." "Context + horizon must be less than the context limit."