minor changes

This commit is contained in:
siriuz42
2025-10-07 20:41:38 +00:00
parent 25bb4f20e8
commit ff2cefaa26
7 changed files with 366 additions and 389 deletions
+3 -10
View File
@@ -43,10 +43,7 @@ class RMSNorm(nnx.Module):
self.num_features = num_features
self.epsilon = epsilon
def __call__(
self, inputs: Float[Array, "b ... d"]
) -> Float[Array, "b ... d"]:
def __call__(self, inputs: Float[Array, "b ... d"]) -> Float[Array, "b ... d"]:
var = jnp.mean(jnp.square(inputs), axis=-1, keepdims=True)
normed_inputs = inputs * jax.lax.rsqrt(var + self.epsilon)
normed_inputs *= self.scale
@@ -58,18 +55,14 @@ class LayerNorm(nnx.Module):
__data__ = ("scale", "bias")
def __init__(
self, num_features: int, *, epsilon: float = 1e-6, rngs=nnx.Rngs(42)
):
def __init__(self, num_features: int, *, epsilon: float = 1e-6, rngs=nnx.Rngs(42)):
del rngs
self.scale = nnx.Param(jnp.ones(shape=(num_features,)))
self.bias = nnx.Param(jnp.zeros(shape=(num_features,)))
self.num_features = num_features
self.epsilon = epsilon
def __call__(
self, inputs: Float[Array, "b ... d"]
) -> Float[Array, "b ... d"]:
def __call__(self, inputs: Float[Array, "b ... d"]) -> Float[Array, "b ... d"]:
mean = jnp.mean(inputs, axis=-1, keepdims=True)
var = jnp.mean(jnp.square(inputs - mean), axis=-1, keepdims=True)
normed_inputs = (inputs - mean) * jax.lax.rsqrt(var + self.epsilon)
+5 -14
View File
@@ -91,8 +91,7 @@ class RotaryPositionalEmbedding(nnx.Module):
half_embedding_dim = self.embedding_dims // 2
fraction = 2 * jnp.arange(0, half_embedding_dim) / self.embedding_dims
timescale = (
self.min_timescale
* (self.max_timescale / self.min_timescale) ** fraction
self.min_timescale * (self.max_timescale / self.min_timescale) ** fraction
)
if position is None:
seq_length = inputs.shape[1]
@@ -128,9 +127,7 @@ class PerDimScale(nnx.Module):
def __call__(self, x: Float[Array, "b ... d"]) -> Float[Array, "b ... d"]:
return x * (
1.442695041
/ jnp.sqrt(self.num_dims)
* jax.nn.softplus(self.per_dim_scale)
1.442695041 / jnp.sqrt(self.num_dims) * jax.nn.softplus(self.per_dim_scale)
)
@@ -232,9 +229,7 @@ class MultiHeadAttention(nnx.Module):
value = self.value(inputs_q)
if decode_cache is None:
num_masked = jnp.sum(
patch_mask.astype(jnp.int32), axis=-1, keepdims=False
)
num_masked = jnp.sum(patch_mask.astype(jnp.int32), axis=-1, keepdims=False)
next_index = jnp.zeros_like(num_masked, dtype=jnp.int32)
else:
num_masked = (
@@ -277,9 +272,7 @@ class MultiHeadAttention(nnx.Module):
)
else:
# Training
attn_mask = make_attn_mask(
query_length=n_patches, num_all_masked_kv=num_masked
)
attn_mask = make_attn_mask(query_length=n_patches, num_all_masked_kv=num_masked)
# apply attention
x = self.attention_fn(
@@ -357,9 +350,7 @@ class Transformer(nnx.Module):
)
attn_output = self.post_attn_ln(attn_output) + input_embeddings
output_embeddings = (
self.post_ff_ln(
self.ff1(self.activation(self.ff0(self.pre_ff_ln(attn_output))))
)
self.post_ff_ln(self.ff1(self.activation(self.ff0(self.pre_ff_ln(attn_output)))))
+ attn_output
)
return output_embeddings, decode_cache
-1
View File
@@ -19,7 +19,6 @@ import functools
import jax
import jax.numpy as jnp
import jaxtyping
import typeguard
Float = jaxtyping.Float
Array = jaxtyping.Array
+48 -52
View File
@@ -19,7 +19,9 @@ import functools
import gc
import logging
import math
from typing import Any, Callable
import os
from pathlib import Path
from typing import Any, Callable, Dict
import einshape
from flax import nnx
@@ -127,9 +129,7 @@ class TimesFM_2p5_200M_flax_module(nnx.Module): # pylint: disable=invalid-name
masks: Bool[Array, "b n p"],
decode_cache: util.DecodeCache | None = None,
):
tokenizer_inputs = jnp.concatenate(
[inputs, masks.astype(inputs.dtype)], axis=-1
)
tokenizer_inputs = jnp.concatenate([inputs, masks.astype(inputs.dtype)], axis=-1)
input_embeddings = self.tokenizer(tokenizer_inputs)
if decode_cache is None:
decode_cache = [None] * self.x
@@ -164,16 +164,10 @@ class TimesFM_2p5_200M_flax_module(nnx.Module): # pylint: disable=invalid-name
decode_cache = util.DecodeCache(
next_index=jnp.zeros(shape=(self.x, batch_size), dtype=jnp.int32),
num_masked=jnp.zeros(shape=(self.x, batch_size), dtype=jnp.int32),
key=jnp.zeros(
shape=(self.x, batch_size, decode_cache_size, self.h, self.hd)
),
value=jnp.zeros(
shape=(self.x, batch_size, decode_cache_size, self.h, self.hd)
),
)
normed_inputs = revin(
patched_inputs, context_mu, context_sigma, reverse=False
key=jnp.zeros(shape=(self.x, batch_size, decode_cache_size, self.h, self.hd)),
value=jnp.zeros(shape=(self.x, batch_size, decode_cache_size, self.h, self.hd)),
)
normed_inputs = revin(patched_inputs, context_mu, context_sigma, reverse=False)
normed_inputs = jnp.where(patched_masks, 0.0, normed_inputs)
(_, _, normed_outputs, normed_quantile_spread), decode_cache = self(
normed_inputs, patched_masks, decode_cache
@@ -205,9 +199,7 @@ class TimesFM_2p5_200M_flax_module(nnx.Module): # pylint: disable=invalid-name
xs=(new_patched_input, new_mask),
axis=1,
)
new_normed_input = revin(
new_patched_input, new_mu, new_sigma, reverse=False
)
new_normed_input = revin(new_patched_input, new_mu, new_sigma, reverse=False)
(_, _, new_normed_output, _), decode_cache = module(
new_normed_input, new_mask, decode_cache
)
@@ -298,15 +290,11 @@ def _force_flip_invariance_fn(
flipped_pf_outputs = _flip_quantile_fn(flipped_pf_outputs)
flipped_pf_outputs = jax_einshape("tb...->(tb)...", flipped_pf_outputs)
flipped_quantile_spreads = _flip_quantile_fn(flipped_quantile_spreads)
flipped_quantile_spreads = jax_einshape(
"tb...->(tb)...", flipped_quantile_spreads
)
flipped_quantile_spreads = jax_einshape("tb...->(tb)...", flipped_quantile_spreads)
to_concat = [flipped_pf_outputs[:, -1, ...]]
if flipped_ar_outputs is not None:
flipped_ar_outputs = _flip_quantile_fn(flipped_ar_outputs)
flipped_ar_outputs = jax_einshape(
"tbno...->(tb)(no)...", flipped_ar_outputs
)
flipped_ar_outputs = jax_einshape("tbno...->(tb)(no)...", flipped_ar_outputs)
to_concat.append(flipped_ar_outputs)
flipped_full_forecast = jnp.concatenate(to_concat, axis=1)
@@ -318,9 +306,7 @@ def _force_flip_invariance_fn(
static_argnames=("max_horizon",),
donate_argnums=(0,),
)
def _use_continuous_quantile_head_fn(
full_forecast, quantile_spreads, max_horizon
):
def _use_continuous_quantile_head_fn(full_forecast, quantile_spreads, max_horizon):
"""Uses continuous quantile head."""
to_stack = [full_forecast[..., :max_horizon, 0]]
for quantile_index in [1, 2, 3, 4]:
@@ -464,31 +450,48 @@ class TimesFM_2p5_200M_flax(timesfm_2p5_base.TimesFM_2p5):
@classmethod
def from_pretrained(
cls,
model_id: str = "google/timesfm-2.5-200m-flax",
*,
path: str | None = None,
hf_repo_id: str | None = "google/timesfm-2.5-200m-flax",
revision: str | None = None,
cache_dir: str | Path | None = None,
force_download: bool = False,
proxies: Dict | None = None,
resume_download: bool | None = None,
local_files_only: bool | None = None,
token: str | None = None,
**model_kwargs,
):
"""Loads a Flax TimesFM model."""
if path:
pass
elif hf_repo_id:
logging.info(
"Downloading checkpoint from HuggingFace repo %s", hf_repo_id
)
path = huggingface_hub.snapshot_download(hf_repo_id)
logging.info("Loading checkpoint from: %s", path)
else:
raise ValueError("Either path or hf_repo_id must be provided.")
instance = cls()
# Create an instance of the model wrapper class.
instance = cls(**model_kwargs)
# Determine the path to the model weights.
model_file_path = ""
if os.path.isdir(model_id):
logging.info("Loading checkpoint from local directory: %s", model_id)
model_file_path = model_id
else:
logging.info("Downloading checkpoint from Hugging Face repo %s", model_id)
model_file_path = huggingface_hub.snapshot_download(
repo_id=model_id,
revision=revision,
cache_dir=cache_dir,
force_download=force_download,
proxies=proxies,
resume_download=resume_download,
token=token,
local_files_only=local_files_only,
)
logging.info("Loading checkpoint from: %s", model_file_path)
checkpointer = ocp.StandardCheckpointer()
graph, state = nnx.split(instance.model)
state = checkpointer.restore(path, state)
state = checkpointer.restore(model_file_path, state)
instance.model = nnx.merge(graph, state)
return instance
def compile(self, forecast_config: configs.ForecastConfig, **kwargs):
# Acrobym used during validation.
fc = forecast_config
if fc.max_context % self.model.p != 0:
@@ -496,8 +499,7 @@ class TimesFM_2p5_200M_flax(timesfm_2p5_base.TimesFM_2p5):
"When compiling, max context needs to be multiple of the patch size"
" %d. Using max context = %d instead.",
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 = dataclasses.replace(fc, max_context=new_context)
if fc.max_horizon % self.model.o != 0:
@@ -505,8 +507,7 @@ class TimesFM_2p5_200M_flax(timesfm_2p5_base.TimesFM_2p5):
"When compiling, max horizon needs to be multiple of the output patch"
" size %d. Using max horizon = %d instead.",
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,
)
forecast_config = dataclasses.replace(fc, max_horizon=new_horizon)
if fc.max_context + fc.max_horizon > self.model.config.context_limit:
@@ -517,8 +518,7 @@ class TimesFM_2p5_200M_flax(timesfm_2p5_base.TimesFM_2p5):
)
if fc.use_continuous_quantile_head and (fc.max_horizon > self.model.os):
raise ValueError(
"Continuous quantile head is not supported for horizons >"
f" {self.model.os}."
f"Continuous quantile head is not supported for horizons > {self.model.os}."
)
self.forecast_config = forecast_config
@@ -534,19 +534,15 @@ class TimesFM_2p5_200M_flax(timesfm_2p5_base.TimesFM_2p5):
)
def compiled_decode_kernel(fc, horizon, inputs, masks):
inputs = jnp.array(inputs, dtype=jnp.float32)
masks = jnp.array(masks, dtype=jnp.bool)
if horizon > fc.max_horizon:
raise ValueError(
"Horizon must be less than the max horizon."
f" {horizon} > {fc.max_horizon}."
f"Horizon must be less than the max horizon. {horizon} > {fc.max_horizon}."
)
to_trim = fc.max_horizon - horizon
inputs, masks, is_positive, mu, sigma = _before_model_decode(
fc, inputs, masks
)
inputs, masks, is_positive, mu, sigma = _before_model_decode(fc, inputs, masks)
pf_outputs, quantile_spreads, ar_outputs = self.model.compiled_decode(
fc.max_horizon, inputs, masks
+3 -5
View File
@@ -55,12 +55,10 @@ class TimesFM_2p5_200M_torch_module(nn.Module):
# Layers.
self.tokenizer = dense.ResidualBlock(self.config.tokenizer)
self.stacked_xf = nn.ModuleList(
[
self.stacked_xf = nn.ModuleList([
transformer.Transformer(self.config.stacked_transformers.transformer)
for _ in range(self.x)
]
)
])
self.output_projection_point = dense.ResidualBlock(
self.config.output_projection_point
)
@@ -272,7 +270,7 @@ class TimesFM_2p5_200M_torch(timesfm_2p5_base.TimesFM_2p5, ModelHubMixin):
def _from_pretrained(
cls,
*,
model_id: str,
model_id: str = "google/timesfm-2.5-200m-pytorch",
revision: Optional[str],
cache_dir: Optional[Union[str, Path]],
force_download: bool,
View File