-
Notifications
You must be signed in to change notification settings - Fork 293
Add StableLM-3B 4E1T to Keras Hub #2151
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
@divyashreepathihalli Here is a comparison of numerics with Hugging Face in Colab. The results match with an absolute tolerance of 1e-3, but they do not match when using 1e-5. Could you please take a look and suggest some improvements or explanations for this discrepancy? |
The numerics is good enough! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good overall, but let's check the numerics and the generate output.
@Bond099 let's sync this with the latest changes and make sure to run our format script. I'm not exactly sure why non of our CI is running, but I don't think it ran. |
Let's clean up the PR. Can we fix the following minor things?
|
Looks like there are conflicts. Please pull in master and resolve conflicts |
@@ -3,7 +3,6 @@ | |||
from keras import random |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd actually want sampler.py
to be untouched, because all models use this. We'd want to make this change only for Stable LM. The changes will be less intrusive that way.
Is there a way you can pass the correct mask right here:
mask=padding_mask, |
If you do pass the correct mask, this should evaluate to the correct bool: https://github.com/keras-team/keras-hub/blob/master/keras_hub/src/samplers/sampler.py#L100.
Take a look if this works.
/gemini review |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code Review
This pull request adds support for the StableLM-3B-4E1T model. I've identified a critical bug in the StableLMTransformerDecoder
's caching logic that would cause failures during training, and a few medium-to-high severity issues in the StableLMBackbone
's documentation and the checkpoint conversion script.
x, self_attention_cache = self.self_attention_layer( | ||
hidden_states=x, | ||
attention_mask=self_attention_mask, | ||
cache=self_attention_cache, | ||
cache_update_index=self_attention_cache_update_index, | ||
) | ||
x = self.self_attention_dropout(x, training=training) | ||
x = x + residual | ||
|
||
residual = x | ||
|
||
# Feedforward block | ||
x = self.feedforward_layernorm(x) | ||
gate_output = self.feedforward_gate_dense(x) | ||
gate_output = self.activation(gate_output) | ||
intermediate_output = self.feedforward_intermediate_dense(x) | ||
x = self.feedforward_output_dense( | ||
ops.multiply(intermediate_output, gate_output) | ||
) | ||
decoder_output = x + residual | ||
if self_attention_cache is not None: | ||
return decoder_output, self_attention_cache | ||
return decoder_output |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The call
method's handling of the attention cache has a bug. When call
is invoked without a cache (i.e., self_attention_cache
is initially None
), self.self_attention_layer
returns a tuple of (attention_output, attention_output)
. This makes the local self_attention_cache
a tensor, causing the if
condition to be true and the method to incorrectly return a tuple, breaking the StableLMBackbone
functional model which expects a single tensor output. Use a different variable name for the cache returned by the attention layer to fix this.
attention_output, next_attention_cache = self.self_attention_layer(
hidden_states=x,
attention_mask=self_attention_mask,
cache=self_attention_cache,
cache_update_index=self_attention_cache_update_index,
)
x = self.self_attention_dropout(attention_output, training=training)
x = x + residual
residual = x
# Feedforward block
x = self.feedforward_layernorm(x)
gate_output = self.feedforward_gate_dense(x)
gate_output = self.activation(gate_output)
intermediate_output = self.feedforward_intermediate_dense(x)
x = self.feedforward_output_dense(
ops.multiply(intermediate_output, gate_output)
)
decoder_output = x + residual
if self_attention_cache is not None:
return decoder_output, next_attention_cache
return decoder_output
cfg["max_sequence_length"] = pt_cfg["max_position_embeddings"] | ||
cfg["layer_norm_epsilon"] = pt_cfg["layer_norm_eps"] | ||
cfg["rope_max_wavelength"] = pt_cfg["rope_theta"] | ||
cfg["partial_rotary_factor"] = pt_cfg["partial_rotary_factor"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
tie_word_embeddings: bool, optional. Whether to tie input and output | ||
embeddings. Defaults to False. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
keras_model.get_layer( | ||
f"transformer_layer_{layer_index}" | ||
)._self_attention_layer._query_dense.set_weights(weights) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The script accesses layer attributes using names with a leading underscore (e.g., _self_attention_layer
, _query_dense
). Use the public attribute names for setting weights instead.
keras_model.get_layer( | |
f"transformer_layer_{layer_index}" | |
)._self_attention_layer._query_dense.set_weights(weights) | |
keras_model.get_layer( | |
f"transformer_layer_{layer_index}" | |
).self_attention_layer.query_dense.set_weights(weights) |
/gemini review |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code Review
This pull request adds the StableLM-3B 4E1T model, which is a valuable contribution. The implementation largely follows the repository's style guide, including the modular structure and naming conventions. However, there are a few critical omissions and issues that need to be addressed. Most importantly, the model presets file (stablelm_presets.py
) is missing, which is required by the style guide [^1] and is essential for loading pre-trained models. The PR also lacks the required validation Colab notebooks [^2]. Additionally, I've found a critical bug in the CausalLM implementation due to the use of a removed utility function, and a few other issues in the backbone and checkpoint conversion script that I've detailed in the specific comments.
end_locations = any_equal( | ||
token_ids, stop_token_ids, ops.logical_not(padding_mask) | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The any_equal
utility function has been removed in this PR (as seen in the changes to sampler.py
), but it's still being used here. This will cause a runtime error.
You should replace this with equivalent logic to check for stop tokens. Here's a suggested implementation that achieves the same result.
end_locations = any_equal( | |
token_ids, stop_token_ids, ops.logical_not(padding_mask) | |
) | |
stop_token_ids_tensor = ops.convert_to_tensor( | |
stop_token_ids, dtype=token_ids.dtype | |
) | |
is_stop_token = ops.any( | |
token_ids[..., None] == stop_token_ids_tensor, axis=-1 | |
) | |
end_locations = is_stop_token & ops.logical_not(padding_mask) |
cfg["max_sequence_length"] = pt_cfg["max_position_embeddings"] | ||
cfg["layer_norm_epsilon"] = pt_cfg["layer_norm_eps"] | ||
cfg["rope_max_wavelength"] = pt_cfg["rope_theta"] | ||
cfg["partial_rotary_factor"] = pt_cfg["partial_rotary_factor"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The configuration key partial_rotary_factor
is extracted from the Hugging Face config, but the StableLMBackbone
expects rotary_percentage
. This will cause a TypeError
when initializing the backbone because of an unexpected keyword argument. You should use the correct key rotary_percentage
.
cfg["partial_rotary_factor"] = pt_cfg["partial_rotary_factor"] | |
cfg["rotary_percentage"] = pt_cfg["partial_rotary_factor"] |
self.token_embedding = ReversibleEmbedding( | ||
input_dim=vocabulary_size, | ||
output_dim=hidden_dim, | ||
tie_weights=False, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The tie_word_embeddings
argument is documented in the Args
section of the docstring but is not included in the __init__
method's signature. This causes a discrepancy between the documentation and the implementation, and prevents users from configuring this behavior. 1
To fix this, you should:
- Add
tie_word_embeddings=False
to the__init__
signature. - Store it as
self.tie_word_embeddings
. - Pass
tie_weights=self.tie_word_embeddings
to theReversibleEmbedding
layer. - Include it in
get_config()
.
tie_weights=False, | |
tie_weights=tie_word_embeddings, |
Style Guide References
Footnotes
if model_parallel_dim_name not in device_mesh.axis_names: | ||
raise ValueError( | ||
f"{model_parallel_dim_name} is not found in the " | ||
f"device_mesh.axis_names. {device_mesh.axis_name=}" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if data_parallel_dim_name not in device_mesh.axis_names: | ||
raise ValueError( | ||
f"{data_parallel_dim_name} is not found in the " | ||
f"device_mesh.axis_names. {device_mesh.axis_name=}" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This PR adds the StableLM-3B 4E1T model to Keras Hub. However, numerical matching with the Hugging Face implementation is still in progress.