Source code for easydel.modules.olmo.modeling_olmo

# Copyright 2025 The EasyDeL Author @erfanzar (Erfan Zare Chavoshi).
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.


import functools

import jax
import jax.numpy as jnp
from eformer import common_types
from eformer.escale import apply_logical_sharding
from ejkernel.types import MaskInfo
from flax import nnx as nn
from jax.ad_checkpoint import checkpoint_name
from jaxtyping import Array, Bool, Float, Int

from easydel.infra.base_module import EasyDeLBaseModule
from easydel.infra.factory import TaskType, register_module
from easydel.infra.modeling_outputs import BaseModelOutput, DecoderLayerOutput
from easydel.infra.utils import ACT2FN, auto_remat, block_wise_ffn, get_dot_general_by_bits
from easydel.layers.attention_unified import UnifiedAttention
from easydel.layers.base_modules import BaseCausalLMModule, BaseSequenceClassificationModule
from easydel.layers.caching import (
    RaggedPagesCache,
    RaggedPagesCacheView,
    RaggedPagesMetadata,
    TransformerCache,
    TransformerCacheView,
    TransformerMetadata,
)
from easydel.layers.linear import ColumnParallelLinear, RowParallelLinear

from .olmo_configuration import OlmoConfig


[docs]class OlmoMLP(nn.Module): """OLMo MLP module. This module implements the feed-forward network (MLP) used in the OLMo model. It consists of gate, up, and down projections with a SiLU activation. Attributes: config (OlmoConfig): Configuration object for the model. dtype (jnp.dtype): Data type for computations. param_dtype (jnp.dtype): Data type for parameters. precision (jax.lax.PrecisionLike): Precision setting for JAX operations. gate_proj (ParallelLinear): Linear layer for the gate projection. down_proj (ParallelLinear): Linear layer for the down projection. up_proj (ParallelLinear): Linear layer for the up projection. act_fn (callable): Activation function (SiLU). """ def __init__( self, config: OlmoConfig, dtype: jnp.dtype = jnp.bfloat16, param_dtype: jnp.dtype = jnp.bfloat16, precision: jax.lax.PrecisionLike = None, *, rngs: nn.Rngs, layer_idx: int, ): """Initializes the OlmoMLP module. Args: config (OlmoConfig): The configuration object for the OLMo model. dtype (jnp.dtype): Data type for computation. Defaults to jnp.float32. param_dtype (jnp.dtype): Data type for parameters. Defaults to jnp.float32. precision (jax.lax.PrecisionLike): Precision setting for JAX operations. Defaults to None. rngs (nn.Rngs): Random number generators. """ self.config = config self.dtype = dtype self.param_dtype = param_dtype self.precision = precision column_parallel_linear = functools.partial( ColumnParallelLinear, dtype=dtype, param_dtype=param_dtype, use_bias=False, kernel_init=jax.nn.initializers.normal(config.initializer_range), precision=precision, rngs=rngs, **get_dot_general_by_bits(config.bits, config.easy_method), ) row_parallel_linear = functools.partial( RowParallelLinear, dtype=dtype, param_dtype=param_dtype, use_bias=False, kernel_init=jax.nn.initializers.normal(config.initializer_range), precision=precision, rngs=rngs, **get_dot_general_by_bits(config.bits, config.easy_method), ) self.gate_proj = column_parallel_linear( config.hidden_size, config.intermediate_size, rngs=rngs, ) self.down_proj = row_parallel_linear( config.intermediate_size, config.hidden_size, rngs=rngs, ) self.up_proj = column_parallel_linear( config.hidden_size, config.intermediate_size, rngs=rngs, ) self.act_fn = ACT2FN[self.config.hidden_act] def __call__( self, hidden_states: Float[Array, "batch seq_len hidden_dim"] ) -> Float[Array, "batch seq_len hidden_dim"]: """Forward pass of the OlmoMLP module. Args: hidden_states (jnp.ndarray): Input hidden states. Returns: jnp.ndarray: Output hidden states after MLP transformation. """ hidden_states = apply_logical_sharding( hidden_states, dynamic_axes=common_types.HiddenStateSharding, partition_manager=self.config.partition_manager, ) gate = checkpoint_name(self.act_fn(self.gate_proj(hidden_states)), "mlp_gate") up = checkpoint_name(self.up_proj(hidden_states), "mlp_up") hidden_states = checkpoint_name(self.down_proj(gate * up), "mlp_down") hidden_states = apply_logical_sharding( hidden_states, dynamic_axes=common_types.HiddenStateSharding, partition_manager=self.config.partition_manager, ) return checkpoint_name(hidden_states, "mlp_output")
[docs]class OlmoAttention(UnifiedAttention): """OLMo attention built on UnifiedAttention with optional QKV clipping.""" def __init__( self, config: OlmoConfig, dtype: jnp.dtype = jnp.bfloat16, param_dtype: jnp.dtype = jnp.bfloat16, precision: jax.lax.PrecisionLike = None, *, rngs: nn.Rngs, layer_idx: int, ): self.clip_qkv = getattr(config, "clip_qkv", None) super().__init__( config=config, dtype=dtype, param_dtype=param_dtype, precision=precision, rngs=rngs, layer_idx=layer_idx, attention_type="standard", causal=True, ) def _preprocess_qkv( self, query_states: jnp.ndarray, key_states: jnp.ndarray, value_states: jnp.ndarray, ) -> tuple[jnp.ndarray, jnp.ndarray, jnp.ndarray]: """Apply optional clipping before reshaping QKV tensors.""" query_states, key_states, value_states = super()._preprocess_qkv(query_states, key_states, value_states) if self.clip_qkv is not None: clip_val = self.clip_qkv query_states = jnp.clip(query_states, -clip_val, clip_val) key_states = jnp.clip(key_states, -clip_val, clip_val) value_states = jnp.clip(value_states, -clip_val, clip_val) return query_states, key_states, value_states
[docs]class OlmoDecoderLayer(nn.Module): """OLMo Transformer Decoder Layer. This module represents a single decoder layer in the OLMo model, combining self-attention and MLP sub-layers with residual connections. Unlike typical transformer blocks, OLMo applies the layer normalization *after* the residual connection. Attributes: config (OlmoConfig): Configuration object for the model. dtype (jnp.dtype): Data type for computations. param_dtype (jnp.dtype): Data type for parameters. precision (jax.lax.PrecisionLike): Precision setting for JAX operations. rngs (nn.Rngs): Random number generators. self_attn (OlmoAttention): The self-attention module. mlp (OlmoMLP): The feed-forward (MLP) module. """ def __init__( self, config: OlmoConfig, dtype: jnp.dtype = jnp.bfloat16, param_dtype: jnp.dtype = jnp.bfloat16, precision: jax.lax.PrecisionLike = None, *, rngs: nn.Rngs, layer_idx: int, ): """Initializes the OlmoDecoderLayer. Args: config (OlmoConfig): The configuration object for the OLMo model. dtype (jnp.dtype): Data type for computation. Defaults to jnp.float32. param_dtype (jnp.dtype): Data type for parameters. Defaults to jnp.float32. precision (jax.lax.PrecisionLike): Precision setting for JAX operations. Defaults to None. rngs (nn.Rngs): Random number generators. """ self.config = config self.dtype = dtype self.param_dtype = param_dtype self.precision = precision attn_block = OlmoAttention mlp_block = OlmoMLP attn_block, mlp_block = auto_remat( attn_block, mlp_block, policy=config.gradient_checkpointing, save_names=config.gradient_checkpointing_targets, exclude_names=config.gradient_checkpointing_targets, ) self.self_attn = attn_block( config=config, dtype=dtype, param_dtype=param_dtype, precision=precision, rngs=rngs, layer_idx=layer_idx, ) self.mlp = mlp_block( config=config, dtype=dtype, param_dtype=param_dtype, precision=precision, rngs=rngs, layer_idx=layer_idx, ) self.input_layernorm = nn.LayerNorm( config.hidden_size, epsilon=1e-5, use_bias=False, use_scale=False, rngs=rngs, ) self.post_attention_layernorm = nn.LayerNorm( config.hidden_size, epsilon=1e-5, use_bias=False, use_scale=False, rngs=rngs, ) def __call__( self, hidden_states: Float[Array, "batch seq_len hidden_dim"], mask_info: MaskInfo | None, position_ids: Int[Array, "batch seq_len"], mode: common_types.RUNTIME_MODE_TYPES, # type:ignore cache_view: TransformerCacheView | RaggedPagesCacheView | None = None, cache_metadata: TransformerMetadata | RaggedPagesMetadata | None = None, output_attentions: bool = False, frequencies: Float[Array, "seq_len head_dim"] | None = None, ): """Forward pass of the OlmoDecoderLayer module. Args: hidden_states (chex.Array): Input hidden states. attention_mask (chex.Array): Mask to apply on the attention scores. position_ids (chex.Array): Position indices for the tokens. Shape: (batch_size, sequence_length). causal_mask (tp.Optional[chex.Array | bool]): Causal mask for ensuring autoregressive behavior. cache_view (tp.Optional[TransformerCacheView | RaggedPagesCacheView]): Cache view for attention KVs. cache_metadata (tp.Optional[TransformerMetadata | RaggedPagesMetadata]): Metadata for paged attention. segment_ids (tp.Optional[chex.Array]): Segment IDs for segment-based attention (optional). output_attentions (bool): Whether to return attention weights. Default is False. fcm_mask (tp.Optional[chex.Array]): Flash Chunking Mask (FCM) for attention. frequencies (tp.Optional[chex.Array]): Precomputed rotary frequency embeddings. Returns: tp.Tuple[chex.Array, tp.Optional[chex.Array]]: A tuple containing the output hidden states and optionally the attention weights. """ residual = hidden_states attention_output = self.self_attn( self.input_layernorm(hidden_states), mask_info, position_ids, mode, cache_view, cache_metadata, output_attentions, frequencies, ) hidden_states = checkpoint_name(attention_output.attention_output + residual, "residual") ffd_inp = self.post_attention_layernorm(hidden_states) if self.config.use_scan_mlp: feed_forward_hidden_states = block_wise_ffn(self.mlp, ffd_inp, self.config.scan_mlp_chunk_size) else: feed_forward_hidden_states = self.mlp(ffd_inp) hidden_states = checkpoint_name(hidden_states + feed_forward_hidden_states, "layer_output") return DecoderLayerOutput( hidden_states=hidden_states, attention_weight=attention_output.attention_weight, cache_view=attention_output.cache_view, )
[docs]@register_module(TaskType.BASE_MODULE, config=OlmoConfig, model_type="olmo") class OlmoModel(EasyDeLBaseModule): """The base OLMo model transformer. This class represents the core transformer architecture of the OLMo model, consisting of an embedding layer and multiple OlmoDecoderLayer layers. Note that OLMo does not have a final layer normalization. Attributes: config (OlmoConfig): Configuration object for the model. dtype (jnp.dtype): Data type for computation. param_dtype (jnp.dtype): Data type for parameters. precision (jax.lax.PrecisionLike): Precision setting for JAX operations. rngs (nn.Rngs): Random number generators. embed_tokens (nn.Embed): Embedding layer for input tokens. layers (tp.List[OlmoDecoderLayer]): List of decoder layers. gradient_checkpointing (EasyDeLGradientCheckPointers): Gradient checkpointing configuration. """ def __init__( self, config: OlmoConfig, dtype: jnp.dtype = jnp.bfloat16, param_dtype: jnp.dtype = jnp.bfloat16, precision: jax.lax.PrecisionLike = None, *, rngs: nn.Rngs, ): """Initializes the OlmoModel. Args: config (OlmoConfig): The configuration object for the OLMo model. dtype (jnp.dtype): Data type for computation. Defaults to jnp.float32. param_dtype (jnp.dtype): Data type for parameters. Defaults to jnp.float32. precision (jax.lax.PrecisionLike): Precision setting for JAX operations. Defaults to None. rngs (nn.Rngs): Random number generators. """ super().__init__( config=config, dtype=dtype, param_dtype=param_dtype, precision=precision, rngs=rngs, ) embed_block = auto_remat( nn.Embed, policy=config.gradient_checkpointing, save_names=config.gradient_checkpointing_targets, exclude_names=config.gradient_checkpointing_targets, ) self.embed_tokens = embed_block( config.vocab_size, config.hidden_size, embedding_init=jax.nn.initializers.normal(stddev=config.initializer_range), dtype=dtype, param_dtype=param_dtype, rngs=rngs, ) self.layers = [ OlmoDecoderLayer( config=config, layer_idx=i, dtype=dtype, param_dtype=param_dtype, precision=precision, rngs=rngs, ) for i in range(config.num_hidden_layers) ] self.norm = nn.LayerNorm( config.hidden_size, epsilon=1e-5, use_bias=False, use_scale=False, rngs=rngs, ) def __call__( self, input_ids: Int[Array, "batch seq_len"] | None = None, inputs_embeds: Float[Array, "batch seq_len hidden_dim"] | None = None, attention_mask: Bool[Array, "batch seq_len"] | None = None, mask_info: MaskInfo | None = None, position_ids: Int[Array, "batch seq_len"] | None = None, mode: common_types.RUNTIME_MODE_TYPES | None = None, # type:ignore past_key_values: TransformerCache | RaggedPagesCache | None = None, cache_metadata: TransformerMetadata | RaggedPagesMetadata | None = None, output_attentions: bool | None = None, output_hidden_states: bool | None = None, ) -> BaseModelOutput: """Forward pass of the OlmoModel. Args: input_ids (tp.Optional[chex.Array]): Input token IDs. Shape: (batch_size, sequence_length). inputs_embeds (tp.Optional[chex.Array]): Input embeddings. Either `input_ids` or `inputs_embeds` must be provided. attention_mask (tp.Optional[chex.Array]): Mask to avoid performing attention on padding token indices. Shape: (batch_size, sequence_length). position_ids (tp.Optional[chex.Array]): Position indices for the tokens. Shape: (batch_size, sequence_length). segment_ids (tp.Optional[chex.Array]): Segment IDs (unused). past_key_values (tp.Optional[TransformerCache | RaggedPagesCache]): Precomputed key/value states for attention. cache_metadata (tp.Optional[TransformerMetadata | RaggedPagesMetadata]): Metadata for paged attention. output_attentions (tp.Optional[bool]): Whether to return attention weights. Defaults to `config.output_attentions`. output_hidden_states (tp.Optional[bool]): Whether to return hidden states for all layers. Defaults to `config.output_hidden_states`. Returns: BaseModelOutput: The model's output. returns a `BaseModelOutput` object containing `last_hidden_state`, `hidden_states` (optional), and `attentions` (optional). Raises: ValueError: If neither `input_ids` nor `inputs_embeds` is provided. """ if (input_ids is None) ^ (inputs_embeds is not None): raise ValueError( "You cannot specify both input_ids and inputs_embeds at the same time, and must specify either one" ) if inputs_embeds is None: inputs_embeds = checkpoint_name(self.embed_tokens(input_ids.astype("i4")), "embeddings") sequence_length = inputs_embeds.shape[1] all_attentions = () if output_attentions else None all_hidden_states = () if output_hidden_states else None assert sequence_length <= self.config.max_position_embeddings, ( f"Maximum Position Embedding Reached ! " f"(Excepted <= {self.config.max_position_embeddings} got {sequence_length})" ) mask_info = MaskInfo.dynamic_init( mask_info=mask_info, input_ids=input_ids, inputs_embeds=inputs_embeds, attention_mask=attention_mask, ) if position_ids is None: position_ids = mask_info.q_position_ids hidden_states = inputs_embeds if mode is None: mode = ( common_types.MODE_DECODE if sequence_length == 1 and past_key_values is not None else common_types.MODE_TRAIN ) if past_key_values is None: past_key_values = TransformerCache.init_empty(len(self.layers)) hidden_states = apply_logical_sharding( hidden_states, dynamic_axes=common_types.HiddenStateSharding, partition_manager=self.config.partition_manager, ) for idx, block in enumerate(self.layers): if output_hidden_states: all_hidden_states += (hidden_states,) layer_outputs = block( hidden_states=hidden_states, mask_info=mask_info, position_ids=position_ids, mode=mode, cache_view=past_key_values.views[idx], cache_metadata=cache_metadata, output_attentions=output_attentions, frequencies=self.frequencies, ) hidden_states = layer_outputs.hidden_states if output_attentions: all_attentions += (layer_outputs.attention_weight,) past_key_values[idx] = layer_outputs.cache_view hidden_states = checkpoint_name(self.norm(hidden_states), "model_output") if output_hidden_states: all_hidden_states += (hidden_states,) return BaseModelOutput( last_hidden_state=hidden_states, hidden_states=all_hidden_states, attentions=all_attentions, past_key_values=past_key_values, )
[docs] def get_encoder(self): """ Returns the encoder part of the model's graph definition. Decoder-Only models don't have an encoder. """ raise NotImplementedError("This is a decoder-only model and does not have an encoder.")
[docs] def get_decoder(self): """ Returns the decoder part of the model's graph definition. """ return self
[docs] def get_lm_head(self): """ Returns the language model head of the module. Base Models don't have a Language Model Head. """ raise NotImplementedError("The base model does not have a language model head.")
[docs] def get_embedding(self): """ Returns the embedding layer of the module. """ return self.embed_tokens
[docs]@register_module(TaskType.CAUSAL_LM, config=OlmoConfig, model_type="olmo") class OlmoForCausalLM(BaseCausalLMModule[OlmoModel, OlmoConfig]): """OLMo model with a Causal Language Modeling head.""" _task_type = TaskType.CAUSAL_LM _model_type = "olmo" _config_class = OlmoConfig def __init__( self, config: OlmoConfig, dtype: jnp.dtype = jnp.bfloat16, param_dtype: jnp.dtype = jnp.bfloat16, precision: jax.lax.PrecisionLike = None, *, rngs: nn.Rngs, ): """Initializes the OlmoForCausalLM model. Args: config (OlmoConfig): The configuration object for the OLMo model. dtype (jnp.dtype): Data type for computation. Defaults to jnp.bfloat16. param_dtype (jnp.dtype): Data type for parameters. Defaults to jnp.bfloat16. precision (jax.lax.PrecisionLike): Precision setting for JAX operations. Defaults to None. rngs (nn.Rngs): Random number generators. """ super().__init__( config=config, base_model_class=OlmoModel, base_model_name="model", dtype=dtype, param_dtype=param_dtype, precision=precision, rngs=rngs, lm_head_bias=False, )
[docs]@register_module(TaskType.SEQUENCE_CLASSIFICATION, config=OlmoConfig, model_type="olmo") class OlmoForSequenceClassification(BaseSequenceClassificationModule[OlmoModel, OlmoConfig]): """OLMo model with a Sequence Classification head.""" _task_type = TaskType.SEQUENCE_CLASSIFICATION _model_type = "olmo" _config_class = OlmoConfig def __init__( self, config: OlmoConfig, dtype: jnp.dtype = jnp.bfloat16, param_dtype: jnp.dtype = jnp.bfloat16, precision: jax.lax.PrecisionLike = None, *, rngs: nn.Rngs, ): """Initializes the OlmoForSequenceClassification model. Args: config (OlmoConfig): The configuration object for the OLMo model. Must include `num_labels`. dtype (jnp.dtype): Data type for computation. Defaults to jnp.bfloat16. param_dtype (jnp.dtype): Data type for parameters. Defaults to jnp.bfloat16. precision (jax.lax.PrecisionLike): Precision setting for JAX operations. Defaults to None. rngs (nn.Rngs): Random number generators. """ super().__init__( config=config, base_model_class=OlmoModel, base_model_name="model", dtype=dtype, param_dtype=param_dtype, precision=precision, rngs=rngs, pooling_strategy="last", score_head_bias=False, )