Files
unslothai--unsloth/unsloth/models/glm4_moe.py
T
wehub-resource-sync e93507a09c
Lockfile supply-chain audit / lockfile supply-chain audit (push) Has been cancelled
Windows Studio GGUF CI / GPU prebuilt resolves without Visual Studio (push) Has been cancelled
Windows Studio GGUF CI / setup.ps1 unit tests (VS 2026 / CMake guard) (push) Has been cancelled
Windows Studio GGUF CI / real-VS detection (VS 2022) (push) Has been cancelled
Windows Studio GGUF CI / real-VS detection (VS 2026) (push) Has been cancelled
Windows Studio GGUF CI / VC++ runtime detect + install round-trip (windows-2025-vs2026) (push) Has been cancelled
Windows Studio GGUF CI / VC++ runtime detect + install round-trip (windows-latest) (push) Has been cancelled
Windows Studio Update CI / Studio Updating Tests (push) Has been cancelled
Wheel CI / Wheel build + content sanity + import smoke (push) Has been cancelled
Lint CI / Source lint (Python + shell + YAML + JSON + safety nets) (push) Has been cancelled
MLX CI on Mac M1 / dispatch (push) Has been cancelled
Security audit / advisory audit (pip + npm + cargo) (push) Has been cancelled
Security audit / pip scan-packages :: extras (push) Has been cancelled
Security audit / pip scan-packages :: studio (push) Has been cancelled
Security audit / pip scan-packages :: hf-stack (push) Has been cancelled
Security audit / npm scan-packages (Studio frontend tarballs) (push) Has been cancelled
Security audit / workflow-trigger lint (pull_request_target / cache-poisoning) (push) Has been cancelled
Security audit / pytest tests/security (push) Has been cancelled
Security audit / npm provenance + new install-script diff (push) Has been cancelled
Studio API CI / Studio API & Auth Tests (push) Has been cancelled
Backend CI / (Python 3.10) (push) Has been cancelled
Backend CI / (Python 3.11) (push) Has been cancelled
Backend CI / (Python 3.12) (push) Has been cancelled
Backend CI / (Python 3.13) (push) Has been cancelled
Backend CI / Repo tests (CPU) (push) Has been cancelled
Frontend CI / Frontend build + bundle sanity (push) Has been cancelled
Studio GGUF CI / OpenAI, Anthropic API tests (push) Has been cancelled
Studio GGUF CI / Tool calling Tests (push) Has been cancelled
Studio GGUF CI / JSON, images (push) Has been cancelled
Mac Studio GGUF CI / OpenAI, Anthropic API tests (push) Has been cancelled
Mac Studio GGUF CI / Tool calling Tests (push) Has been cancelled
Mac Studio GGUF CI / JSON, images (push) Has been cancelled
Mac Studio Install Matrix CI / Install + load (macos-14) (push) Has been cancelled
Mac Studio Install Matrix CI / Install + load (macos-15) (push) Has been cancelled
Mac Studio Install Matrix CI / Install + load (macos-26) (push) Has been cancelled
Mac Studio Install Matrix CI / Install + load (macos-15-intel) (push) Has been cancelled
Mac Studio API CI / Studio API & Auth Tests (push) Has been cancelled
Mac Studio Install Matrix CI / Install + load (macos-26-intel) (push) Has been cancelled
Mac Studio UI CI / Chat UI Tests (push) Has been cancelled
Studio Tauri CI / Tauri Linux debug build (no codesign) (push) Has been cancelled
Mac Studio Update CI / Studio Updating Tests (push) Has been cancelled
Studio UI CI / Chat UI Tests (push) Has been cancelled
Windows Studio API CI / Studio API & Auth Tests (push) Has been cancelled
Windows Studio UI CI / Chat UI Tests (push) Has been cancelled
Studio Update CI / Studio Updating Tests (push) Has been cancelled
Core / Core (HF=default + TRL=default) (push) Has been cancelled
Core / Core (HF=4.57.6 + TRL<1) (push) Has been cancelled
Core / Core (HF=latest + TRL=latest) (push) Has been cancelled
Core / llama.cpp build + smoke (push) Has been cancelled
Windows Studio GGUF CI / OpenAI, Anthropic API tests (push) Has been cancelled
Windows Studio GGUF CI / Tool calling Tests (push) Has been cancelled
Windows Studio GGUF CI / JSON, images (push) Has been cancelled
Windows Studio GGUF CI / Studio install + inference without Visual Studio (push) Has been cancelled
Studio export capability / capability (macos-latest) (push) Has been cancelled
Studio export capability / capability (ubuntu-latest) (push) Has been cancelled
Studio export capability / capability (windows-latest) (push) Has been cancelled
Cross-platform parity / parity (macos-latest) (push) Has been cancelled
Cross-platform parity / parity (windows-latest) (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
Studio load-orchestrator CI / test (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:59:56 +08:00

416 lines
14 KiB
Python

# Copyright 2023-present Daniel Han-Chen & the Unsloth team. All rights reserved.
#
# 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
#
# http://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.
"""
GLM-4.7 Flash (GLM4 MoE Lite) optimized implementation using grouped GEMM.
Key architecture differences from Qwen3 MoE:
- Router uses sigmoid activation (not softmax)
- Has routed_scaling_factor of 1.8
- Has 1 shared expert that processes all tokens
- Uses group-based selection before topk
- Uses MLA (Multi-head Latent Attention)
"""
from .llama import *
import os
from ._utils import __version__
from .llama import (
LlamaRotaryEmbedding,
LlamaLinearScalingRotaryEmbedding,
fix_prepare_inputs_for_generation,
fast_rms_layernorm_inference,
fast_swiglu_inference,
LlamaModel_fast_forward,
LlamaModel_fast_forward_inference,
CausalLM_fast_forward,
PeftModel_fast_forward,
)
import torch
import torch.nn.functional as F
from typing import Optional, Tuple
from ..kernels import fast_rms_layernorm
# grouped_gemm expects its parent dir on sys.path
HAS_GROUPED_GEMM = False
try:
import sys
import os
_moe_path = os.path.join(
os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "kernels", "moe"
)
if _moe_path not in sys.path:
sys.path.insert(0, _moe_path)
# Import first to apply the TMA compatibility shim (patches triton.language
# for both old and new TMA API names)
import grouped_gemm # noqa: F401 - triggers TMA compatibility shim
from grouped_gemm.interface import grouped_gemm
from grouped_gemm.reference.moe_ops import (
get_routing_indices,
permute,
unpermute,
)
HAS_GROUPED_GEMM = True
except ImportError as e:
import warnings
warnings.warn(f"Grouped GEMM not available: {e}. MoE will use fallback implementation.")
# Import transformers GLM4 MoE Lite classes
try:
from transformers.models.glm4_moe_lite.modeling_glm4_moe_lite import (
Glm4MoeLiteAttention,
Glm4MoeLiteMoE,
Glm4MoeLiteMLP,
Glm4MoeLiteNaiveMoe,
Glm4MoeLiteTopkRouter,
Glm4MoeLiteDecoderLayer,
Glm4MoeLiteModel,
Glm4MoeLiteForCausalLM,
Glm4MoeLiteRMSNorm,
)
HAS_GLM4_MOE = True
except ImportError:
HAS_GLM4_MOE = False
# Create dummy classes for type checking
class Glm4MoeLiteAttention:
pass
class Glm4MoeLiteMoE:
pass
class Glm4MoeLiteMLP:
pass
class Glm4MoeLiteNaiveMoe:
pass
class Glm4MoeLiteTopkRouter:
pass
class Glm4MoeLiteDecoderLayer:
pass
class Glm4MoeLiteModel:
pass
class Glm4MoeLiteForCausalLM:
pass
torch_nn_functional_silu = torch.nn.functional.silu
def Glm4MoeLiteMoE_fast_forward(self, hidden_states):
"""
Optimized MoE forward pass using grouped GEMM.
GLM4 MoE specifics:
- Uses sigmoid router activation (not softmax)
- Has routed_scaling_factor of 1.8
- Has 1 shared expert that always processes all tokens
- Uses group-based selection with topk_group
"""
residuals = hidden_states
orig_shape = hidden_states.shape
batch_size, seq_len, hidden_dim = orig_shape
num_tokens = batch_size * seq_len
hidden_states = hidden_states.view(-1, hidden_dim)
router_logits = self.gate(hidden_states) # [num_tokens, n_routed_experts]
topk_indices, topk_weights = self.route_tokens_to_experts(router_logits)
# Sigmoid router returns fp32; cast weights to hidden_states dtype (e.g. bf16)
topk_weights = topk_weights.to(hidden_states.dtype)
with torch.no_grad():
token_counts_by_expert, gather_indices = get_routing_indices(
topk_indices, self.n_routed_experts
)
if HAS_GROUPED_GEMM:
# Under autocast hidden_states may be fp32 while weights are bf16
hidden_states = hidden_states.to(self.experts.gate_up_proj.dtype)
# gate_up_proj: [num_tokens, hidden_dim] -> [total_tokens, 2*intermediate_dim]
intermediate = grouped_gemm(
X = hidden_states,
W = self.experts.gate_up_proj,
m_sizes = token_counts_by_expert.int(),
topk = self.top_k,
gather_indices = gather_indices,
permute_x = True,
permute_y = False,
autotune = True,
is_first_gemm = True,
)
# Activation: SiLU(gate) * up
gate, up = intermediate.chunk(2, dim = -1)
intermediate = torch_nn_functional_silu(gate) * up
# down_proj: [total_tokens, intermediate_dim] -> [total_tokens, hidden_dim]
expert_output = grouped_gemm(
X = intermediate,
W = self.experts.down_proj,
m_sizes = token_counts_by_expert.int(),
topk = self.top_k,
gather_indices = gather_indices,
permute_x = False,
permute_y = True,
autotune = True,
is_first_gemm = False,
)
# Merge topk weights: [num_tokens, top_k, hidden_dim] -> [num_tokens, hidden_dim]
hidden_states = (
expert_output.view(num_tokens, self.top_k, hidden_dim) * topk_weights.unsqueeze(-1)
).sum(dim = 1)
else:
hidden_states = self.experts(hidden_states, topk_indices, topk_weights)
# Add shared expert output
hidden_states = hidden_states + self.shared_experts(residuals.view(-1, hidden_dim))
return hidden_states.view(*orig_shape)
def Glm4MoeLiteNaiveMoe_fast_forward(
self, hidden_states: torch.Tensor, top_k_index: torch.Tensor, top_k_weights: torch.Tensor
) -> torch.Tensor:
"""
Optimized expert forward using grouped GEMM.
Args:
hidden_states: [num_tokens, hidden_dim]
top_k_index: [num_tokens, top_k] indices of selected experts
top_k_weights: [num_tokens, top_k] weights for selected experts
Returns:
[num_tokens, hidden_dim] output after weighted sum of expert outputs
"""
num_tokens, hidden_dim = hidden_states.shape
top_k = top_k_index.shape[1]
top_k_weights = top_k_weights.to(hidden_states.dtype)
if not HAS_GROUPED_GEMM:
final_hidden_states = torch.zeros_like(hidden_states)
with torch.no_grad():
expert_mask = torch.nn.functional.one_hot(top_k_index, num_classes = self.num_experts)
expert_mask = expert_mask.permute(2, 1, 0)
expert_hit = torch.greater(expert_mask.sum(dim = (-1, -2)), 0).nonzero()
for expert_idx in expert_hit:
expert_idx = expert_idx[0]
if expert_idx == self.num_experts:
continue
top_k_pos, token_idx = torch.where(expert_mask[expert_idx])
current_state = hidden_states[token_idx]
gate, up = torch.nn.functional.linear(
current_state, self.gate_up_proj[expert_idx]
).chunk(2, dim = -1)
current_hidden_states = self.act_fn(gate) * up
current_hidden_states = torch.nn.functional.linear(
current_hidden_states, self.down_proj[expert_idx]
)
current_hidden_states = (
current_hidden_states * top_k_weights[token_idx, top_k_pos, None]
)
final_hidden_states.index_add_(
0, token_idx, current_hidden_states.to(final_hidden_states.dtype)
)
return final_hidden_states
with torch.no_grad():
token_counts_by_expert, gather_indices = get_routing_indices(top_k_index, self.num_experts)
# Under autocast hidden_states may be fp32 while weights are bf16
hidden_states = hidden_states.to(self.gate_up_proj.dtype)
# First grouped GEMM: gate_up_proj
intermediate = grouped_gemm(
X = hidden_states,
W = self.gate_up_proj,
m_sizes = token_counts_by_expert.int(),
topk = top_k,
gather_indices = gather_indices,
permute_x = True,
permute_y = False,
autotune = True,
is_first_gemm = True,
)
# Activation: SiLU(gate) * up
gate, up = intermediate.chunk(2, dim = -1)
intermediate = self.act_fn(gate) * up
# Second grouped GEMM: down_proj
expert_output = grouped_gemm(
X = intermediate,
W = self.down_proj,
m_sizes = token_counts_by_expert.int(),
topk = top_k,
gather_indices = gather_indices,
permute_x = False,
permute_y = True,
autotune = True,
is_first_gemm = False,
)
# Merge topk weights
final_hidden_states = (
expert_output.view(num_tokens, top_k, hidden_dim) * top_k_weights.unsqueeze(-1)
).sum(dim = 1)
return final_hidden_states
def Glm4MoeLiteDecoderLayer_fast_forward(
self,
hidden_states: torch.Tensor,
attention_mask: Optional[torch.Tensor] = None,
position_ids: Optional[torch.LongTensor] = None,
past_key_values = None,
use_cache: bool = False,
cache_position: Optional[torch.LongTensor] = None,
position_embeddings: Optional[Tuple[torch.Tensor, torch.Tensor]] = None,
**kwargs,
) -> torch.Tensor:
"""
Optimized decoder layer forward with fast RMS layernorm.
"""
is_inference = use_cache and hasattr(self, "_flag_for_generation")
if is_inference:
# Self-attention with fast inference path
residual = hidden_states
hidden_states = fast_rms_layernorm_inference(self.input_layernorm, hidden_states)
hidden_states, _ = self.self_attn(
hidden_states = hidden_states,
attention_mask = attention_mask,
position_ids = position_ids,
past_key_values = past_key_values,
use_cache = use_cache,
cache_position = cache_position,
position_embeddings = position_embeddings,
**kwargs,
)
hidden_states = residual + hidden_states
# MLP/MoE
residual = hidden_states
hidden_states = fast_rms_layernorm_inference(self.post_attention_layernorm, hidden_states)
hidden_states = self.mlp(hidden_states)
hidden_states = residual + hidden_states
else:
# Training path
residual = hidden_states
hidden_states = fast_rms_layernorm(self.input_layernorm, hidden_states)
hidden_states, _ = self.self_attn(
hidden_states = hidden_states,
attention_mask = attention_mask,
position_ids = position_ids,
past_key_values = past_key_values,
use_cache = use_cache,
cache_position = cache_position,
position_embeddings = position_embeddings,
**kwargs,
)
hidden_states = residual + hidden_states
# MLP/MoE
residual = hidden_states
hidden_states = fast_rms_layernorm(self.post_attention_layernorm, hidden_states)
hidden_states = self.mlp(hidden_states)
hidden_states = residual + hidden_states
return hidden_states
def Glm4MoeLiteMLP_fast_forward(self, x):
"""
Optimized MLP forward using fused SwiGLU.
"""
return fast_swiglu_inference(self, x)
class FastGLM47Model(FastLlamaModel):
"""
Fast GLM-4.7 Flash (GLM4 MoE Lite) model with grouped GEMM optimization.
This provides 2-3x throughput improvement for MoE layers by:
- Replacing sequential expert loops with grouped GEMM operations
- Fusing permutation operations into the GEMM kernels
- Using optimized RMS LayerNorm and SwiGLU implementations
"""
@staticmethod
def pre_patch():
if not HAS_GLM4_MOE:
raise ImportError(
"Unsloth: GLM4 MoE Lite support requires transformers >= 5.0.0. "
"Please upgrade with: pip install --upgrade transformers"
)
# Patch MoE forward with grouped GEMM (TMA compat handled by
# grouped_gemm/__init__.py)
if HAS_GROUPED_GEMM:
Glm4MoeLiteNaiveMoe.forward = Glm4MoeLiteNaiveMoe_fast_forward
Glm4MoeLiteMoE.forward = Glm4MoeLiteMoE_fast_forward
# Attention/rope/decoder/model forwards are NOT patched: GLM4 uses MLA
# with different projection names and lacks extend_rope_embedding, so the
# Llama-compatible infrastructure doesn't apply.
return
@staticmethod
def from_pretrained(
model_name = "unsloth/GLM-4.7-Flash",
max_seq_length = 4096,
dtype = None,
load_in_4bit = True,
token = None,
device_map = "sequential",
rope_scaling = None,
fix_tokenizer = True,
model_patcher = None,
tokenizer_name = None,
trust_remote_code = False,
**kwargs,
):
# Used by loader, not passed to model
kwargs.pop("unsloth_force_compile", None)
return FastLlamaModel.from_pretrained(
model_name = model_name,
max_seq_length = max_seq_length,
dtype = dtype,
load_in_4bit = load_in_4bit,
token = token,
device_map = device_map,
rope_scaling = rope_scaling,
fix_tokenizer = fix_tokenizer,
model_patcher = FastGLM47Model,
tokenizer_name = tokenizer_name,
trust_remote_code = trust_remote_code,
**kwargs,
)