Repository navigation
Refactor Ernie 4.5's MoE #40547
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
Refactor Ernie 4.5's MoE #40547
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -29,7 +29,6 @@ | |
| from ...generation import GenerationMixin | ||
| from ...integrations import use_kernel_forward_from_hub | ||
| from ...masking_utils import create_causal_mask | ||
| from ...modeling_flash_attention_utils import FlashAttentionKwargs | ||
| from ...modeling_layers import GradientCheckpointingLayer | ||
| from ...modeling_outputs import MoeCausalLMOutputWithPast, MoeModelOutputWithPast | ||
| from ...modeling_rope_utils import ROPE_INIT_FUNCTIONS, dynamic_rope_update | ||
|
|
@@ -67,7 +66,7 @@ def __init__(self, config, intermediate_size=None): | |
| super().__init__() | ||
| self.config = config | ||
| self.hidden_size = config.hidden_size | ||
| self.intermediate_size = intermediate_size if intermediate_size is not None else config.intermediate_size | ||
| self.intermediate_size = config.intermediate_size if intermediate_size is None else intermediate_size | ||
|
|
||
| self.gate_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=config.use_bias) | ||
| self.up_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=config.use_bias) | ||
|
|
@@ -288,6 +287,48 @@ def forward(self, hidden_states): | |
| return hidden_states + self.e_score_correction_bias.squeeze() | ||
|
|
||
|
|
||
| class Ernie4_5_MoeNaiveMoe(nn.ModuleList): | ||
| def __init__(self, config): | ||
| super().__init__() | ||
| self.top_k = config.moe_k | ||
| self.num_experts = config.moe_num_experts | ||
|
Comment on lines
+293
to
+294
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I had to rename here, is there a specific renaming going on? |
||
| self.norm_min = config.moe_norm_min | ||
| for _ in range(self.num_experts): | ||
| self += [Ernie4_5_MoeMLP(config)] | ||
|
|
||
| def forward(self, hidden_states, routing_weights, routing_bias, device_type): | ||
| with torch.autocast(device_type=device_type, enabled=False): # Force float32 | ||
| routing_weights = F.softmax(routing_weights, dim=1, dtype=torch.float) | ||
| _, selected_experts = torch.topk(routing_weights + routing_bias, self.top_k, dim=-1) | ||
| routing_weights = torch.gather(routing_weights, dim=-1, index=selected_experts) | ||
| routing_weights = routing_weights / torch.clamp( | ||
| routing_weights.sum(dim=-1, keepdim=True), min=self.norm_min | ||
| ) | ||
| routing_weights = routing_weights.to(hidden_states.dtype) | ||
|
vasqu marked this conversation as resolved.
Resolved
|
||
|
|
||
| final_hidden_states = torch.zeros_like(hidden_states, device=hidden_states.device) | ||
|
|
||
| # One hot encode the selected experts to create an expert mask | ||
| # this will be used to easily index which expert is going to be sollicitated | ||
| expert_mask = torch.nn.functional.one_hot(selected_experts, num_classes=self.num_experts).permute(2, 1, 0) | ||
|
|
||
| # Loop over all available experts in the model and perform the computation on each expert | ||
| expert_hit = torch.greater(expert_mask.sum(dim=(-1, -2)), 0).nonzero() | ||
| for expert_idx in expert_hit: | ||
| expert_layer = self[expert_idx] | ||
| idx, top_x = torch.where(expert_mask[expert_idx].squeeze(0)) | ||
| # Index the correct hidden states and compute the expert hidden state for | ||
| # the current expert. We need to make sure to multiply the output hidden | ||
| # states by `routing_weights` on the corresponding tokens (top-1 and top-2) | ||
| current_state = hidden_states[None, top_x].reshape(-1, hidden_states.shape[-1]) | ||
| current_hidden_states = expert_layer(current_state) * routing_weights[top_x, idx, None] | ||
|
|
||
| # However `index_add_` only support torch tensors for indexing so we'll use | ||
| # the `top_x` tensor here. | ||
| final_hidden_states.index_add_(0, top_x, current_hidden_states.to(hidden_states.dtype)) | ||
| return final_hidden_states | ||
|
|
||
|
|
||
| class Ernie4_5_MoeSparseMoeBlock(nn.Module): | ||
| """ | ||
| This implementation is | ||
|
|
@@ -305,6 +346,7 @@ class Ernie4_5_MoeSparseMoeBlock(nn.Module): | |
|
|
||
| def __init__(self, config): | ||
| super().__init__() | ||
| self.hidden_dim = config.hidden_size | ||
| self.num_experts = config.moe_num_experts | ||
| self.top_k = config.moe_k | ||
|
|
||
|
|
@@ -313,10 +355,7 @@ def __init__(self, config): | |
|
|
||
| # gating | ||
| self.gate = nn.Linear(config.hidden_size, config.moe_num_experts, bias=False, dtype=torch.float32) | ||
| self.experts = nn.ModuleList( | ||
| [Ernie4_5_MoeMLP(config, config.moe_intermediate_size) for _ in range(config.moe_num_experts)] | ||
| ) | ||
| self.norm_min = config.moe_norm_min | ||
| self.experts = Ernie4_5_MoeNaiveMoe(config) | ||
|
|
||
| # (optional) shared experts for all forwards | ||
| self.shared_experts = None | ||
|
|
@@ -327,8 +366,8 @@ def forward( | |
| self, | ||
| hidden_states: torch.Tensor, | ||
| ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor]: | ||
| batch_size, sequence_length, hidden_dim = hidden_states.shape | ||
| hidden_states = hidden_states.view(-1, hidden_dim) | ||
| batch_size, sequence_length, _ = hidden_states.shape | ||
| hidden_states = hidden_states.view(-1, self.hidden_dim) | ||
|
|
||
| # (Optional) shared experts | ||
| if self.shared_experts is not None: | ||
|
|
@@ -343,43 +382,18 @@ def forward( | |
| # router_logits: (batch * sequence_length, n_experts) | ||
| router_logits = self.gate(hidden_states.float()) | ||
|
vasqu marked this conversation as resolved.
Resolved
|
||
|
|
||
| routing_weights = F.softmax(router_logits, dim=1, dtype=torch.float) | ||
| _, selected_experts = torch.topk(self.moe_statics(routing_weights), self.top_k, dim=-1) | ||
| routing_weights = torch.gather(routing_weights, dim=-1, index=selected_experts) | ||
| routing_weights = routing_weights / torch.clamp( | ||
| routing_weights.sum(dim=-1, keepdim=True), min=self.norm_min | ||
| ) | ||
| routing_weights = routing_weights.to(hidden_states.dtype) | ||
|
|
||
| final_hidden_states = torch.zeros( | ||
| (batch_size * sequence_length, hidden_dim), dtype=hidden_states.dtype, device=hidden_states.device | ||
| final_hidden_states = self.experts( | ||
| hidden_states, | ||
| routing_weights=router_logits, | ||
| routing_bias=self.moe_statics.e_score_correction_bias.squeeze(), | ||
| device_type=device_type, | ||
| ) | ||
|
|
||
| # One hot encode the selected experts to create an expert mask | ||
| # this will be used to easily index which expert is going to be sollicitated | ||
| expert_mask = torch.nn.functional.one_hot(selected_experts, num_classes=self.num_experts).permute(2, 1, 0) | ||
|
|
||
| # Loop over all available experts in the model and perform the computation on each expert | ||
| expert_hit = torch.greater(expert_mask.sum(dim=(-1, -2)), 0).nonzero() | ||
| for expert_idx in expert_hit: | ||
| expert_layer = self.experts[expert_idx] | ||
| idx, top_x = torch.where(expert_mask[expert_idx].squeeze(0)) | ||
|
|
||
| # Index the correct hidden states and compute the expert hidden state for | ||
| # the current expert. We need to make sure to multiply the output hidden | ||
| # states by `routing_weights` on the corresponding tokens (top-1 and top-2) | ||
| current_state = hidden_states[None, top_x].reshape(-1, hidden_dim) | ||
| current_hidden_states = expert_layer(current_state) * routing_weights[top_x, idx, None] | ||
|
|
||
| # However `index_add_` only support torch tensors for indexing so we'll use | ||
| # the `top_x` tensor here. | ||
| final_hidden_states.index_add_(0, top_x, current_hidden_states.to(hidden_states.dtype)) | ||
|
|
||
| # Add (optional) shared experts to the result | ||
| if self.shared_experts is not None: | ||
| final_hidden_states = final_hidden_states + shared_output | ||
|
|
||
| final_hidden_states = final_hidden_states.reshape(batch_size, sequence_length, hidden_dim) | ||
| final_hidden_states = final_hidden_states.reshape(batch_size, sequence_length, self.hidden_dim) | ||
| return final_hidden_states, router_logits | ||
|
|
||
|
|
||
|
|
@@ -411,32 +425,8 @@ def forward( | |
| position_ids: Optional[torch.LongTensor] = None, | ||
| past_key_values: Optional[tuple[torch.Tensor]] = None, | ||
| cache_position: Optional[torch.LongTensor] = None, | ||
| **kwargs: Unpack[FlashAttentionKwargs], | ||
| **kwargs: Unpack[TransformersKwargs], | ||
| ) -> torch.FloatTensor: | ||
| """ | ||
| Args: | ||
| hidden_states (`torch.FloatTensor`): input to the layer of shape `(batch, seq_len, embed_dim)` | ||
| attention_mask (`torch.FloatTensor`, *optional*): attention mask of size | ||
| `(batch, sequence_length)` where padding elements are indicated by 0. | ||
| output_attentions (`bool`, *optional*): | ||
| Whether or not to return the attentions tensors of all attention layers. See `attentions` under | ||
| returned tensors for more detail. | ||
| output_router_logits (`bool`, *optional*): | ||
| Whether or not to return the logits of all the routers. They are useful for computing the router loss, | ||
| and should not be returned during inference. | ||
| use_cache (`bool`, *optional*): | ||
| If set to `True`, `past_key_values` key value states are returned and can be used to speed up decoding | ||
| (see `past_key_values`). | ||
| past_key_values (`Tuple(torch.FloatTensor)`, *optional*): cached past key and value projection states | ||
| cache_position (`torch.LongTensor` of shape `(sequence_length)`, *optional*): | ||
| Indices depicting the position of the input sequence tokens in the sequence. | ||
| position_embeddings (`tuple[torch.FloatTensor, torch.FloatTensor]`, *optional*): | ||
| Tuple containing the cosine and sine positional embeddings of shape `(batch_size, seq_len, head_dim)`, | ||
| with `head_dim` being the embedding dimension of each attention head. | ||
| kwargs (`dict`, *optional*): | ||
| Arbitrary kwargs to be ignored, used for FSDP and other methods that injects code | ||
| into the model | ||
| """ | ||
| residual = hidden_states | ||
|
|
||
| hidden_states = self.input_layernorm(hidden_states) | ||
|
|
@@ -457,11 +447,9 @@ def forward( | |
| residual = hidden_states | ||
| hidden_states = self.post_attention_layernorm(hidden_states) | ||
| hidden_states = self.mlp(hidden_states) | ||
| # For the MoE layers, we need to unpack | ||
| if isinstance(hidden_states, tuple): | ||
| hidden_states, _ = hidden_states | ||
| hidden_states = hidden_states[0] | ||
| hidden_states = residual + hidden_states | ||
|
|
||
|
Comment on lines
-460
to
-464
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also modular |
||
| return hidden_states | ||
|
|
||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -96,7 +96,48 @@ def forward(self, hidden_states): | |
| return hidden_states + self.e_score_correction_bias.squeeze() | ||
|
|
||
|
|
||
| # FIXME: update the expert | ||
| class Ernie4_5_MoeNaiveMoe(nn.ModuleList): | ||
| def __init__(self, config): | ||
| super().__init__() | ||
| self.top_k = config.moe_k | ||
| self.num_experts = config.moe_num_experts | ||
| self.norm_min = config.moe_norm_min | ||
| for _ in range(self.num_experts): | ||
| self += [Ernie4_5_MoeMLP(config)] | ||
|
|
||
| def forward(self, hidden_states, routing_weights, routing_bias, device_type): | ||
| with torch.autocast(device_type=device_type, enabled=False): # Force float32 | ||
| routing_weights = F.softmax(routing_weights, dim=1, dtype=torch.float) | ||
| _, selected_experts = torch.topk(routing_weights + routing_bias, self.top_k, dim=-1) | ||
| routing_weights = torch.gather(routing_weights, dim=-1, index=selected_experts) | ||
| routing_weights = routing_weights / torch.clamp( | ||
| routing_weights.sum(dim=-1, keepdim=True), min=self.norm_min | ||
| ) | ||
| routing_weights = routing_weights.to(hidden_states.dtype) | ||
|
|
||
| final_hidden_states = torch.zeros_like(hidden_states, device=hidden_states.device) | ||
|
|
||
| # One hot encode the selected experts to create an expert mask | ||
| # this will be used to easily index which expert is going to be sollicitated | ||
| expert_mask = torch.nn.functional.one_hot(selected_experts, num_classes=self.num_experts).permute(2, 1, 0) | ||
|
|
||
| # Loop over all available experts in the model and perform the computation on each expert | ||
| expert_hit = torch.greater(expert_mask.sum(dim=(-1, -2)), 0).nonzero() | ||
| for expert_idx in expert_hit: | ||
| expert_layer = self[expert_idx] | ||
| idx, top_x = torch.where(expert_mask[expert_idx].squeeze(0)) | ||
| # Index the correct hidden states and compute the expert hidden state for | ||
| # the current expert. We need to make sure to multiply the output hidden | ||
| # states by `routing_weights` on the corresponding tokens (top-1 and top-2) | ||
| current_state = hidden_states[None, top_x].reshape(-1, hidden_states.shape[-1]) | ||
| current_hidden_states = expert_layer(current_state) * routing_weights[top_x, idx, None] | ||
|
|
||
| # However `index_add_` only support torch tensors for indexing so we'll use | ||
| # the `top_x` tensor here. | ||
| final_hidden_states.index_add_(0, top_x, current_hidden_states.to(hidden_states.dtype)) | ||
| return final_hidden_states | ||
|
|
||
|
|
||
| class Ernie4_5_MoeSparseMoeBlock(nn.Module): | ||
| """ | ||
| This implementation is | ||
|
|
@@ -114,6 +155,7 @@ class Ernie4_5_MoeSparseMoeBlock(nn.Module): | |
|
|
||
| def __init__(self, config): | ||
| super().__init__() | ||
| self.hidden_dim = config.hidden_size | ||
| self.num_experts = config.moe_num_experts | ||
| self.top_k = config.moe_k | ||
|
|
||
|
|
@@ -122,10 +164,7 @@ def __init__(self, config): | |
|
|
||
| # gating | ||
| self.gate = nn.Linear(config.hidden_size, config.moe_num_experts, bias=False, dtype=torch.float32) | ||
| self.experts = nn.ModuleList( | ||
| [Ernie4_5_MoeMLP(config, config.moe_intermediate_size) for _ in range(config.moe_num_experts)] | ||
| ) | ||
| self.norm_min = config.moe_norm_min | ||
| self.experts = Ernie4_5_MoeNaiveMoe(config) | ||
|
|
||
| # (optional) shared experts for all forwards | ||
| self.shared_experts = None | ||
|
|
@@ -136,8 +175,8 @@ def forward( | |
| self, | ||
| hidden_states: torch.Tensor, | ||
| ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor]: | ||
| batch_size, sequence_length, hidden_dim = hidden_states.shape | ||
| hidden_states = hidden_states.view(-1, hidden_dim) | ||
| batch_size, sequence_length, _ = hidden_states.shape | ||
| hidden_states = hidden_states.view(-1, self.hidden_dim) | ||
|
|
||
| # (Optional) shared experts | ||
| if self.shared_experts is not None: | ||
|
|
@@ -152,43 +191,18 @@ def forward( | |
| # router_logits: (batch * sequence_length, n_experts) | ||
| router_logits = self.gate(hidden_states.float()) | ||
|
|
||
| routing_weights = F.softmax(router_logits, dim=1, dtype=torch.float) | ||
| _, selected_experts = torch.topk(self.moe_statics(routing_weights), self.top_k, dim=-1) | ||
| routing_weights = torch.gather(routing_weights, dim=-1, index=selected_experts) | ||
| routing_weights = routing_weights / torch.clamp( | ||
| routing_weights.sum(dim=-1, keepdim=True), min=self.norm_min | ||
| ) | ||
| routing_weights = routing_weights.to(hidden_states.dtype) | ||
|
|
||
| final_hidden_states = torch.zeros( | ||
| (batch_size * sequence_length, hidden_dim), dtype=hidden_states.dtype, device=hidden_states.device | ||
| final_hidden_states = self.experts( | ||
| hidden_states, | ||
| routing_weights=router_logits, | ||
| routing_bias=self.moe_statics.e_score_correction_bias.squeeze(), | ||
| device_type=device_type, | ||
|
Comment on lines
+194
to
+198
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Needed to pass them as kwarg for whatever reason 😅 |
||
| ) | ||
|
|
||
| # One hot encode the selected experts to create an expert mask | ||
| # this will be used to easily index which expert is going to be sollicitated | ||
| expert_mask = torch.nn.functional.one_hot(selected_experts, num_classes=self.num_experts).permute(2, 1, 0) | ||
|
|
||
| # Loop over all available experts in the model and perform the computation on each expert | ||
| expert_hit = torch.greater(expert_mask.sum(dim=(-1, -2)), 0).nonzero() | ||
| for expert_idx in expert_hit: | ||
| expert_layer = self.experts[expert_idx] | ||
| idx, top_x = torch.where(expert_mask[expert_idx].squeeze(0)) | ||
|
|
||
| # Index the correct hidden states and compute the expert hidden state for | ||
| # the current expert. We need to make sure to multiply the output hidden | ||
| # states by `routing_weights` on the corresponding tokens (top-1 and top-2) | ||
| current_state = hidden_states[None, top_x].reshape(-1, hidden_dim) | ||
| current_hidden_states = expert_layer(current_state) * routing_weights[top_x, idx, None] | ||
|
|
||
| # However `index_add_` only support torch tensors for indexing so we'll use | ||
| # the `top_x` tensor here. | ||
| final_hidden_states.index_add_(0, top_x, current_hidden_states.to(hidden_states.dtype)) | ||
|
|
||
| # Add (optional) shared experts to the result | ||
| if self.shared_experts is not None: | ||
| final_hidden_states = final_hidden_states + shared_output | ||
|
|
||
| final_hidden_states = final_hidden_states.reshape(batch_size, sequence_length, hidden_dim) | ||
| final_hidden_states = final_hidden_states.reshape(batch_size, sequence_length, self.hidden_dim) | ||
| return final_hidden_states, router_logits | ||
|
|
||
|
|
||
|
|
||
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.
Modular copy