Text Generation
Transformers
Safetensors
GGUF
English
causal-lm
qwen2.5
reasoning
code-generation
Mixture of Experts
qlora
multimodal
tool-use
Eval Results (legacy)
conversational
Instructions to use ram1234598766/Cesium2 with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use ram1234598766/Cesium2 with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-generation", model="ram1234598766/Cesium2") messages = [ {"role": "user", "content": "Who are you?"}, ] pipe(messages)# Load model directly from transformers import AutoModel model = AutoModel.from_pretrained("ram1234598766/Cesium2", device_map="auto") - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- llama.cpp
How to use ram1234598766/Cesium2 with llama.cpp:
Install (macOS, Linux)
curl -LsSf https://llama.app/install.sh | sh # Start a local OpenAI-compatible server with a web UI: llama serve -hf ram1234598766/Cesium2:Q8_0 # Run inference directly in the terminal: llama cli -hf ram1234598766/Cesium2:Q8_0
Install from WinGet (Windows)
winget install llama.cpp # Start a local OpenAI-compatible server with a web UI: llama serve -hf ram1234598766/Cesium2:Q8_0 # Run inference directly in the terminal: llama cli -hf ram1234598766/Cesium2:Q8_0
Use pre-built binary
# Download pre-built binary from: # https://github.com/ggerganov/llama.cpp/releases # Start a local OpenAI-compatible server with a web UI: ./llama-server -hf ram1234598766/Cesium2:Q8_0 # Run inference directly in the terminal: ./llama-cli -hf ram1234598766/Cesium2:Q8_0
Build from source code
git clone https://github.com/ggerganov/llama.cpp.git cd llama.cpp cmake -B build cmake --build build -j --target llama-server llama-cli # Start a local OpenAI-compatible server with a web UI: ./build/bin/llama-server -hf ram1234598766/Cesium2:Q8_0 # Run inference directly in the terminal: ./build/bin/llama-cli -hf ram1234598766/Cesium2:Q8_0
Use Docker
docker model run hf.co/ram1234598766/Cesium2:Q8_0
- LM Studio
- Jan
- vLLM
How to use ram1234598766/Cesium2 with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "ram1234598766/Cesium2" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "ram1234598766/Cesium2", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }'Use Docker
docker model run hf.co/ram1234598766/Cesium2:Q8_0
- SGLang
How to use ram1234598766/Cesium2 with SGLang:
Install from pip and serve model
# Install SGLang from pip: pip install sglang # Start the SGLang server: python3 -m sglang.launch_server \ --model-path "ram1234598766/Cesium2" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "ram1234598766/Cesium2", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }'Use Docker images
docker run --gpus all \ --shm-size 32g \ -p 30000:30000 \ -v ~/.cache/huggingface:/root/.cache/huggingface \ --env "HF_TOKEN=<secret>" \ --ipc=host \ lmsysorg/sglang:latest \ python3 -m sglang.launch_server \ --model-path "ram1234598766/Cesium2" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "ram1234598766/Cesium2", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }' - Ollama
How to use ram1234598766/Cesium2 with Ollama:
ollama run hf.co/ram1234598766/Cesium2:Q8_0
- Unsloth Studio
How to use ram1234598766/Cesium2 with Unsloth Studio:
Install Unsloth Studio (macOS, Linux, WSL)
curl -fsSL https://unsloth.ai/install.sh | sh # Run unsloth studio unsloth studio -H 0.0.0.0 -p 8888 # Then open http://localhost:8888 in your browser # Search for ram1234598766/Cesium2 to start chatting
Install Unsloth Studio (Windows)
irm https://unsloth.ai/install.ps1 | iex # Run unsloth studio unsloth studio -H 0.0.0.0 -p 8888 # Then open http://localhost:8888 in your browser # Search for ram1234598766/Cesium2 to start chatting
Using HuggingFace Spaces for Unsloth
# No setup required # Open https://huggingface.co/spaces/unsloth/studio in your browser # Search for ram1234598766/Cesium2 to start chatting
- Pi
How to use ram1234598766/Cesium2 with Pi:
Start the llama.cpp server
# Install llama.cpp: brew install llama.cpp # Start a local OpenAI-compatible server: llama serve -hf ram1234598766/Cesium2:Q8_0
Configure the model in Pi
# Install Pi: npm install -g @mariozechner/pi-coding-agent # Add to ~/.pi/agent/models.json: { "providers": { "llama-cpp": { "baseUrl": "http://localhost:8080/v1", "api": "openai-completions", "apiKey": "none", "models": [ { "id": "ram1234598766/Cesium2:Q8_0" } ] } } }Run Pi
# Start Pi in your project directory: pi
- Docker Model Runner
How to use ram1234598766/Cesium2 with Docker Model Runner:
docker model run hf.co/ram1234598766/Cesium2:Q8_0
- Lemonade
How to use ram1234598766/Cesium2 with Lemonade:
Pull the model
# Download Lemonade from https://lemonade-server.ai/ lemonade pull ram1234598766/Cesium2:Q8_0
Run and chat with the model
lemonade run user.Cesium2-Q8_0
List all available models
lemonade list
- Hermes Agent
How to use ram1234598766/Cesium2 with Hermes Agent:
Start the llama.cpp server
# Install llama.cpp: brew install llama.cpp # Start a local OpenAI-compatible server: llama serve -hf ram1234598766/Cesium2:Q8_0
Configure Hermes
# Install Hermes: curl -fsSL https://hermes-agent.nousresearch.com/install.sh | bash hermes setup # Point Hermes at the local server: hermes config set model.provider custom hermes config set model.base_url http://127.0.0.1:8080/v1 hermes config set model.default ram1234598766/Cesium2:Q8_0
Run Hermes
hermes
- Atomic Chat
- OpenClaw
How to use ram1234598766/Cesium2 with OpenClaw:
Start the llama.cpp server
# Install llama.cpp: brew install llama.cpp # Start a local OpenAI-compatible server: llama serve -hf ram1234598766/Cesium2:Q8_0
Configure OpenClaw
# Install OpenClaw: npm install -g openclaw@latest # Register the local server and set it as the default model: openclaw onboard --non-interactive --mode local \ --auth-choice custom-api-key \ --custom-base-url http://127.0.0.1:8080/v1 \ --custom-model-id "ram1234598766/Cesium2:Q8_0" \ --custom-provider-id llama-cpp \ --custom-compatibility openai \ --custom-text-input \ --accept-risk \ --skip-health
Run OpenClaw
openclaw agent --local --agent main --message "Hello from Hugging Face"
MORPH-AI commited on
Commit ·
e233e2d
1
Parent(s): 82f262a
feat: re-apply dynamic MoE expansion, multi-head CoT, plugin architecture, improved MoD
Browse files- src/architecture.py +163 -16
- src/runtime.py +18 -1
src/architecture.py
CHANGED
|
@@ -80,10 +80,22 @@ class MorphConfig:
|
|
| 80 |
lora_dropout: float = 0.05
|
| 81 |
# MoE
|
| 82 |
num_experts: int = 4
|
|
|
|
| 83 |
expert_hidden: int = 512
|
| 84 |
moe_top_k: int = 2
|
| 85 |
moe_aux_weight: float = 0.01
|
| 86 |
moe_prune_threshold: float = 0.02
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 87 |
# memory
|
| 88 |
memory_size: int = 1024
|
| 89 |
memory_dim: int = 768
|
|
@@ -122,6 +134,8 @@ class MorphConfig:
|
|
| 122 |
draft_layers: int = 2
|
| 123 |
# RoPE scaling for extended context
|
| 124 |
rope_scaling: Optional[dict] = None
|
|
|
|
|
|
|
| 125 |
# training
|
| 126 |
max_seq_len: int = 2048
|
| 127 |
# quantization
|
|
@@ -387,6 +401,40 @@ class MultiStepReasoner(nn.Module):
|
|
| 387 |
return out, scratch[-1]
|
| 388 |
|
| 389 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 390 |
# ---------------------------------------------------------------------------
|
| 391 |
# Code structure awareness
|
| 392 |
# ---------------------------------------------------------------------------
|
|
@@ -539,27 +587,32 @@ class QuantizedMemoryModule(MemoryModule):
|
|
| 539 |
|
| 540 |
class MixtureOfDepths(nn.Module):
|
| 541 |
"""MoD: per-token gating to dynamically skip transformer layers.
|
| 542 |
-
Uses a lightweight router
|
| 543 |
-
compute by ~30-50% with minimal accuracy loss.
|
| 544 |
"""
|
| 545 |
|
| 546 |
-
def __init__(self, hidden_dim: int, mod_hidden: int, keep_prob: float = 0.8,
|
|
|
|
| 547 |
super().__init__()
|
| 548 |
self.keep_prob = keep_prob
|
|
|
|
|
|
|
| 549 |
self.router = nn.Sequential(
|
| 550 |
nn.Linear(hidden_dim, mod_hidden),
|
| 551 |
nn.GELU(),
|
| 552 |
nn.LayerNorm(mod_hidden),
|
| 553 |
nn.Linear(mod_hidden, 1),
|
| 554 |
-
nn.Sigmoid(),
|
| 555 |
)
|
| 556 |
self.dropout = nn.Dropout(dropout)
|
| 557 |
|
| 558 |
def forward(self, hidden: torch.Tensor) -> Tuple[torch.Tensor, torch.Tensor]:
|
| 559 |
B, T, H = hidden.shape
|
| 560 |
-
|
|
|
|
| 561 |
mask = torch.bernoulli(probs * 0.5 + self.keep_prob * 0.5).expand_as(hidden)
|
| 562 |
mask = self.dropout(mask)
|
|
|
|
|
|
|
| 563 |
return hidden * mask, probs
|
| 564 |
|
| 565 |
|
|
@@ -610,20 +663,23 @@ class MemoryEfficientAttention(nn.Module):
|
|
| 610 |
|
| 611 |
|
| 612 |
class DynamicMoEBlock(nn.Module):
|
| 613 |
-
"""Sparse MoE with dynamic expert pruning and load-balancing."""
|
| 614 |
|
| 615 |
def __init__(self, hidden_dim: int, num_experts: int, expert_hidden: int,
|
| 616 |
-
top_k: int, prune_threshold: float = 0.02):
|
| 617 |
super().__init__()
|
| 618 |
self.num_experts = num_experts
|
|
|
|
| 619 |
self.top_k = top_k
|
| 620 |
self.prune_threshold = prune_threshold
|
|
|
|
| 621 |
self.gate = nn.Linear(hidden_dim, num_experts, bias=False)
|
| 622 |
self.experts = nn.ModuleList([
|
| 623 |
Expert(hidden_dim, expert_hidden) for _ in range(num_experts)
|
| 624 |
])
|
| 625 |
self.expert_usage = torch.zeros(num_experts)
|
| 626 |
self._pruned = set()
|
|
|
|
| 627 |
|
| 628 |
def forward(self, x: torch.Tensor) -> Tuple[torch.Tensor, torch.Tensor]:
|
| 629 |
B, T, H = x.shape
|
|
@@ -643,7 +699,8 @@ class DynamicMoEBlock(nn.Module):
|
|
| 643 |
sel = routing[:, i] > 0
|
| 644 |
if sel.any():
|
| 645 |
out[sel] += routing[sel, i].unsqueeze(-1) * expert(flat[sel])
|
| 646 |
-
|
|
|
|
| 647 |
|
| 648 |
f_i = routing.mean(0)
|
| 649 |
P_i = probs.mean(0)
|
|
@@ -651,17 +708,40 @@ class DynamicMoEBlock(nn.Module):
|
|
| 651 |
|
| 652 |
return out.view(B, T, H), aux
|
| 653 |
|
| 654 |
-
def
|
| 655 |
-
"""
|
| 656 |
total = self.expert_usage.sum()
|
| 657 |
if total == 0:
|
|
|
|
| 658 |
return
|
|
|
|
| 659 |
usage_ratios = self.expert_usage / total
|
| 660 |
-
|
| 661 |
-
|
|
|
|
|
|
|
| 662 |
self._pruned.add(i)
|
| 663 |
-
print(f"Pruned expert {i} (usage
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 664 |
self.expert_usage.zero_()
|
|
|
|
| 665 |
|
| 666 |
|
| 667 |
class MultimodalFusion(nn.Module):
|
|
@@ -954,14 +1034,22 @@ class MorphModel(nn.Module):
|
|
| 954 |
self.verifier = VerifierHead(hidden_dim)
|
| 955 |
self.skill_module = SkillTokenModule(self.cfg, hidden_dim)
|
| 956 |
self.depth_module = DepthEmbeddings(self.cfg, hidden_dim)
|
| 957 |
-
self.moe_block = DynamicMoEBlock(
|
|
|
|
|
|
|
|
|
|
|
|
|
| 958 |
self.memory = QuantizedMemoryModule(self.cfg.memory_size, self.cfg.memory_dim, hidden_dim, self.cfg.memory_quantize, self.cfg.memory_quant_bits)
|
| 959 |
-
self.mod = MixtureOfDepths(
|
|
|
|
|
|
|
|
|
|
| 960 |
self.multimodal_fusion = MultimodalFusion(self.cfg, hidden_dim)
|
| 961 |
self.tool_use = ToolUseModule(self.cfg, hidden_dim)
|
| 962 |
self.document_module = DocumentModule(self.cfg, hidden_dim)
|
| 963 |
self.video_module = VideoModule(self.cfg, hidden_dim)
|
| 964 |
self.code_sandbox = CodeSandbox(self.cfg.sandbox_timeout, self.cfg.sandbox_max_memory)
|
|
|
|
| 965 |
|
| 966 |
# cast novel components to the base model's compute dtype
|
| 967 |
self._dtype = self.base_model_raw.model.embed_tokens.weight.dtype
|
|
@@ -969,13 +1057,51 @@ class MorphModel(nn.Module):
|
|
| 969 |
self.coordinator, self.reasoner, self.code_bias, self.scratchpad,
|
| 970 |
self.verifier, self.skill_module, self.depth_module, self.moe_block,
|
| 971 |
self.memory, self.mod, self.multimodal_fusion, self.tool_use,
|
| 972 |
-
self.document_module, self.video_module,
|
| 973 |
):
|
| 974 |
mod.to(self._dtype)
|
| 975 |
|
| 976 |
self.vocab_size = vocab_size
|
| 977 |
self.base_model = None
|
| 978 |
self._skill_lora_modules: Dict[str, nn.Module] = {}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 979 |
|
| 980 |
# ---- gradient-checkpointing passthrough (Trainer calls these on the top model) ----
|
| 981 |
|
|
@@ -1086,6 +1212,9 @@ class MorphModel(nn.Module):
|
|
| 1086 |
# ---- System-2 reasoning loop ----
|
| 1087 |
reasoned, scratch = self.reasoner(base_hidden, steps)
|
| 1088 |
|
|
|
|
|
|
|
|
|
|
| 1089 |
# ---- subsystem gates ----
|
| 1090 |
g_think, g_code, g_mem, g_scratch = gates[:, 0], gates[:, 1], gates[:, 2], gates[:, 3]
|
| 1091 |
thresh = self.cfg.adaptive_threshold
|
|
@@ -1335,9 +1464,13 @@ class MorphModel(nn.Module):
|
|
| 1335 |
"coordinator", "reasoner", "code_bias", "scratchpad", "verifier",
|
| 1336 |
"skill_module", "depth_module", "moe_block", "memory",
|
| 1337 |
"mod", "multimodal_fusion", "tool_use", "document_module", "video_module",
|
|
|
|
| 1338 |
):
|
| 1339 |
for k, v in getattr(self, name).state_dict().items():
|
| 1340 |
sd[f"{name}.{k}"] = v
|
|
|
|
|
|
|
|
|
|
| 1341 |
if self.base_model is not None:
|
| 1342 |
try:
|
| 1343 |
from peft import get_peft_model_state_dict
|
|
@@ -1351,10 +1484,16 @@ class MorphModel(nn.Module):
|
|
| 1351 |
"coordinator", "reasoner", "code_bias", "scratchpad", "verifier",
|
| 1352 |
"skill_module", "depth_module", "moe_block", "memory",
|
| 1353 |
"mod", "multimodal_fusion", "tool_use", "document_module", "video_module",
|
|
|
|
| 1354 |
):
|
| 1355 |
sub = {k[len(name) + 1:]: v for k, v in sd.items() if k.startswith(name + ".")}
|
| 1356 |
if sub:
|
| 1357 |
getattr(self, name).load_state_dict(sub)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1358 |
if self.base_model is not None:
|
| 1359 |
peft_sd = {k: v for k, v in sd.items() if k.startswith("base_model")}
|
| 1360 |
if peft_sd:
|
|
@@ -1381,6 +1520,9 @@ class MorphModel(nn.Module):
|
|
| 1381 |
"tool_use": self.tool_use.state_dict(),
|
| 1382 |
"document_module": self.document_module.state_dict(),
|
| 1383 |
"video_module": self.video_module.state_dict(),
|
|
|
|
|
|
|
|
|
|
| 1384 |
"config": self.cfg,
|
| 1385 |
},
|
| 1386 |
f"{path}/morph_components.pt",
|
|
@@ -1401,9 +1543,14 @@ class MorphModel(nn.Module):
|
|
| 1401 |
"coordinator", "reasoner", "code_bias", "scratchpad", "verifier",
|
| 1402 |
"skill_module", "depth_module", "moe_block", "memory",
|
| 1403 |
"mod", "multimodal_fusion", "tool_use", "document_module", "video_module",
|
|
|
|
| 1404 |
):
|
| 1405 |
if name in ckpt:
|
| 1406 |
getattr(self, name).load_state_dict(ckpt[name])
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1407 |
self.novel_trained = True
|
| 1408 |
else:
|
| 1409 |
trainer_ckpt = self._find_trainer_checkpoint(path)
|
|
|
|
| 80 |
lora_dropout: float = 0.05
|
| 81 |
# MoE
|
| 82 |
num_experts: int = 4
|
| 83 |
+
max_experts: int = 64
|
| 84 |
expert_hidden: int = 512
|
| 85 |
moe_top_k: int = 2
|
| 86 |
moe_aux_weight: float = 0.01
|
| 87 |
moe_prune_threshold: float = 0.02
|
| 88 |
+
moe_expand_threshold: float = 0.15
|
| 89 |
+
# MoD - Mixture of Depths
|
| 90 |
+
use_mod: bool = True
|
| 91 |
+
mod_hidden: int = 128
|
| 92 |
+
mod_dropout: float = 0.1
|
| 93 |
+
mod_keep_prob: float = 0.8
|
| 94 |
+
mod_temperature: float = 1.0
|
| 95 |
+
mod_temperature_anneal: float = 0.995
|
| 96 |
+
# multi-head CoT reasoning
|
| 97 |
+
num_cot_heads: int = 4
|
| 98 |
+
cot_hidden: int = 256
|
| 99 |
# memory
|
| 100 |
memory_size: int = 1024
|
| 101 |
memory_dim: int = 768
|
|
|
|
| 134 |
draft_layers: int = 2
|
| 135 |
# RoPE scaling for extended context
|
| 136 |
rope_scaling: Optional[dict] = None
|
| 137 |
+
# plugin architecture
|
| 138 |
+
plugin_dir: Optional[str] = None
|
| 139 |
# training
|
| 140 |
max_seq_len: int = 2048
|
| 141 |
# quantization
|
|
|
|
| 401 |
return out, scratch[-1]
|
| 402 |
|
| 403 |
|
| 404 |
+
class MultiHeadCoT(nn.Module):
|
| 405 |
+
"""Multi-head chain-of-thought reasoning: generates N parallel reasoning paths
|
| 406 |
+
and fuses them for higher accuracy on complex tasks."""
|
| 407 |
+
|
| 408 |
+
def __init__(self, config: MorphConfig, hidden_dim: int):
|
| 409 |
+
super().__init__()
|
| 410 |
+
self.num_heads = config.num_cot_heads
|
| 411 |
+
cot_dim = config.cot_hidden
|
| 412 |
+
self.heads = nn.ModuleList([
|
| 413 |
+
nn.Sequential(
|
| 414 |
+
nn.Linear(hidden_dim, cot_dim),
|
| 415 |
+
nn.GELU(),
|
| 416 |
+
nn.LayerNorm(cot_dim),
|
| 417 |
+
nn.Linear(cot_dim, hidden_dim),
|
| 418 |
+
) for _ in range(self.num_heads)
|
| 419 |
+
])
|
| 420 |
+
self.fusion = nn.Sequential(
|
| 421 |
+
nn.Linear(hidden_dim * (self.num_heads + 1), hidden_dim),
|
| 422 |
+
nn.GELU(),
|
| 423 |
+
nn.LayerNorm(hidden_dim),
|
| 424 |
+
nn.Linear(hidden_dim, hidden_dim),
|
| 425 |
+
)
|
| 426 |
+
nn.init.zeros_(self.fusion[-1].weight)
|
| 427 |
+
nn.init.zeros_(self.fusion[-1].bias)
|
| 428 |
+
|
| 429 |
+
def forward(self, hidden: torch.Tensor) -> torch.Tensor:
|
| 430 |
+
B, T, H = hidden.shape
|
| 431 |
+
paths = [hidden]
|
| 432 |
+
for head in self.heads:
|
| 433 |
+
paths.append(head(hidden))
|
| 434 |
+
fused = self.fusion(torch.cat(paths, dim=-1))
|
| 435 |
+
return hidden + fused # Residual connection
|
| 436 |
+
|
| 437 |
+
|
| 438 |
# ---------------------------------------------------------------------------
|
| 439 |
# Code structure awareness
|
| 440 |
# ---------------------------------------------------------------------------
|
|
|
|
| 587 |
|
| 588 |
class MixtureOfDepths(nn.Module):
|
| 589 |
"""MoD: per-token gating to dynamically skip transformer layers.
|
| 590 |
+
Uses a lightweight router with temperature annealing for adaptive layer skipping,
|
| 591 |
+
reducing compute by ~30-50% with minimal accuracy loss.
|
| 592 |
"""
|
| 593 |
|
| 594 |
+
def __init__(self, hidden_dim: int, mod_hidden: int, keep_prob: float = 0.8,
|
| 595 |
+
dropout: float = 0.1, temperature: float = 1.0, temperature_anneal: float = 0.995):
|
| 596 |
super().__init__()
|
| 597 |
self.keep_prob = keep_prob
|
| 598 |
+
self.temperature = temperature
|
| 599 |
+
self.temperature_anneal = temperature_anneal
|
| 600 |
self.router = nn.Sequential(
|
| 601 |
nn.Linear(hidden_dim, mod_hidden),
|
| 602 |
nn.GELU(),
|
| 603 |
nn.LayerNorm(mod_hidden),
|
| 604 |
nn.Linear(mod_hidden, 1),
|
|
|
|
| 605 |
)
|
| 606 |
self.dropout = nn.Dropout(dropout)
|
| 607 |
|
| 608 |
def forward(self, hidden: torch.Tensor) -> Tuple[torch.Tensor, torch.Tensor]:
|
| 609 |
B, T, H = hidden.shape
|
| 610 |
+
logits = self.router(hidden.detach()) # (B, T, 1)
|
| 611 |
+
probs = torch.sigmoid(logits / self.temperature)
|
| 612 |
mask = torch.bernoulli(probs * 0.5 + self.keep_prob * 0.5).expand_as(hidden)
|
| 613 |
mask = self.dropout(mask)
|
| 614 |
+
if self.training:
|
| 615 |
+
self.temperature = max(0.1, self.temperature * self.temperature_anneal)
|
| 616 |
return hidden * mask, probs
|
| 617 |
|
| 618 |
|
|
|
|
| 663 |
|
| 664 |
|
| 665 |
class DynamicMoEBlock(nn.Module):
|
| 666 |
+
"""Sparse MoE with dynamic expert expansion, pruning, and load-balancing."""
|
| 667 |
|
| 668 |
def __init__(self, hidden_dim: int, num_experts: int, expert_hidden: int,
|
| 669 |
+
top_k: int, prune_threshold: float = 0.02, expand_threshold: float = 0.15, max_experts: int = 64):
|
| 670 |
super().__init__()
|
| 671 |
self.num_experts = num_experts
|
| 672 |
+
self.max_experts = max_experts
|
| 673 |
self.top_k = top_k
|
| 674 |
self.prune_threshold = prune_threshold
|
| 675 |
+
self.expand_threshold = expand_threshold
|
| 676 |
self.gate = nn.Linear(hidden_dim, num_experts, bias=False)
|
| 677 |
self.experts = nn.ModuleList([
|
| 678 |
Expert(hidden_dim, expert_hidden) for _ in range(num_experts)
|
| 679 |
])
|
| 680 |
self.expert_usage = torch.zeros(num_experts)
|
| 681 |
self._pruned = set()
|
| 682 |
+
self._expansion_count = 0
|
| 683 |
|
| 684 |
def forward(self, x: torch.Tensor) -> Tuple[torch.Tensor, torch.Tensor]:
|
| 685 |
B, T, H = x.shape
|
|
|
|
| 699 |
sel = routing[:, i] > 0
|
| 700 |
if sel.any():
|
| 701 |
out[sel] += routing[sel, i].unsqueeze(-1) * expert(flat[sel])
|
| 702 |
+
if i < len(self.expert_usage):
|
| 703 |
+
self.expert_usage[i] += sel.sum().item()
|
| 704 |
|
| 705 |
f_i = routing.mean(0)
|
| 706 |
P_i = probs.mean(0)
|
|
|
|
| 708 |
|
| 709 |
return out.view(B, T, H), aux
|
| 710 |
|
| 711 |
+
def prune_and_expand_experts(self):
|
| 712 |
+
"""Dynamically prune underused experts and clone overused ones."""
|
| 713 |
total = self.expert_usage.sum()
|
| 714 |
if total == 0:
|
| 715 |
+
self.expert_usage.zero_()
|
| 716 |
return
|
| 717 |
+
|
| 718 |
usage_ratios = self.expert_usage / total
|
| 719 |
+
active_experts = [i for i in range(len(self.experts)) if i not in self._pruned]
|
| 720 |
+
|
| 721 |
+
for i in active_experts:
|
| 722 |
+
if usage_ratios[i] < self.prune_threshold and len(self._pruned) < len(self.experts) - 1:
|
| 723 |
self._pruned.add(i)
|
| 724 |
+
print(f"Pruned expert {i} (usage {usage_ratios[i]:.4f})")
|
| 725 |
+
|
| 726 |
+
if len(self.experts) < self.max_experts:
|
| 727 |
+
avg_usage = usage_ratios[active_experts].mean().item()
|
| 728 |
+
for i in active_experts:
|
| 729 |
+
if usage_ratios[i] > self.expand_threshold and len(self.experts) < self.max_experts:
|
| 730 |
+
new_expert = Expert(
|
| 731 |
+
self.experts[i].in_proj.in_features,
|
| 732 |
+
self.experts[i].in_proj.out_features
|
| 733 |
+
)
|
| 734 |
+
new_expert.load_state_dict(self.experts[i].state_dict())
|
| 735 |
+
with torch.no_grad():
|
| 736 |
+
for param in new_expert.parameters():
|
| 737 |
+
param.add_(torch.randn_like(param) * 0.01)
|
| 738 |
+
self.experts.append(new_expert)
|
| 739 |
+
self.expert_usage = torch.cat([self.expert_usage, torch.zeros(1)])
|
| 740 |
+
self._expansion_count += 1
|
| 741 |
+
print(f"Expanded expert {i} -> new expert {len(self.experts)-1}")
|
| 742 |
+
|
| 743 |
self.expert_usage.zero_()
|
| 744 |
+
print(f"Active experts: {len(self.experts) - len(self._pruned)}/{len(self.experts)}")
|
| 745 |
|
| 746 |
|
| 747 |
class MultimodalFusion(nn.Module):
|
|
|
|
| 1034 |
self.verifier = VerifierHead(hidden_dim)
|
| 1035 |
self.skill_module = SkillTokenModule(self.cfg, hidden_dim)
|
| 1036 |
self.depth_module = DepthEmbeddings(self.cfg, hidden_dim)
|
| 1037 |
+
self.moe_block = DynamicMoEBlock(
|
| 1038 |
+
hidden_dim, self.cfg.num_experts, self.cfg.expert_hidden,
|
| 1039 |
+
self.cfg.moe_top_k, self.cfg.moe_prune_threshold,
|
| 1040 |
+
self.cfg.moe_expand_threshold, self.cfg.max_experts
|
| 1041 |
+
)
|
| 1042 |
self.memory = QuantizedMemoryModule(self.cfg.memory_size, self.cfg.memory_dim, hidden_dim, self.cfg.memory_quantize, self.cfg.memory_quant_bits)
|
| 1043 |
+
self.mod = MixtureOfDepths(
|
| 1044 |
+
hidden_dim, self.cfg.mod_hidden, self.cfg.mod_keep_prob,
|
| 1045 |
+
self.cfg.mod_dropout, self.cfg.mod_temperature, self.cfg.mod_temperature_anneal
|
| 1046 |
+
)
|
| 1047 |
self.multimodal_fusion = MultimodalFusion(self.cfg, hidden_dim)
|
| 1048 |
self.tool_use = ToolUseModule(self.cfg, hidden_dim)
|
| 1049 |
self.document_module = DocumentModule(self.cfg, hidden_dim)
|
| 1050 |
self.video_module = VideoModule(self.cfg, hidden_dim)
|
| 1051 |
self.code_sandbox = CodeSandbox(self.cfg.sandbox_timeout, self.cfg.sandbox_max_memory)
|
| 1052 |
+
self.cot_reasoner = MultiHeadCoT(self.cfg, hidden_dim)
|
| 1053 |
|
| 1054 |
# cast novel components to the base model's compute dtype
|
| 1055 |
self._dtype = self.base_model_raw.model.embed_tokens.weight.dtype
|
|
|
|
| 1057 |
self.coordinator, self.reasoner, self.code_bias, self.scratchpad,
|
| 1058 |
self.verifier, self.skill_module, self.depth_module, self.moe_block,
|
| 1059 |
self.memory, self.mod, self.multimodal_fusion, self.tool_use,
|
| 1060 |
+
self.document_module, self.video_module, self.code_sandbox, self.cot_reasoner,
|
| 1061 |
):
|
| 1062 |
mod.to(self._dtype)
|
| 1063 |
|
| 1064 |
self.vocab_size = vocab_size
|
| 1065 |
self.base_model = None
|
| 1066 |
self._skill_lora_modules: Dict[str, nn.Module] = {}
|
| 1067 |
+
self._plugins: Dict[str, nn.Module] = {}
|
| 1068 |
+
|
| 1069 |
+
# Load plugins from plugin_dir if specified
|
| 1070 |
+
if self.cfg.plugin_dir:
|
| 1071 |
+
self.load_plugins(self.cfg.plugin_dir)
|
| 1072 |
+
|
| 1073 |
+
def load_plugins(self, plugin_dir: str):
|
| 1074 |
+
"""Load custom capability plugins from a directory."""
|
| 1075 |
+
import os
|
| 1076 |
+
import importlib.util
|
| 1077 |
+
plugin_path = Path(plugin_dir)
|
| 1078 |
+
if not plugin_path.exists():
|
| 1079 |
+
print(f"Plugin directory not found: {plugin_dir}")
|
| 1080 |
+
return
|
| 1081 |
+
|
| 1082 |
+
for file in plugin_path.glob("*.py"):
|
| 1083 |
+
if file.name.startswith("_"):
|
| 1084 |
+
continue
|
| 1085 |
+
try:
|
| 1086 |
+
spec = importlib.util.spec_from_file_location(file.stem, file)
|
| 1087 |
+
mod = importlib.util.module_from_spec(spec)
|
| 1088 |
+
spec.loader.exec_module(mod)
|
| 1089 |
+
for attr_name in dir(mod):
|
| 1090 |
+
attr = getattr(mod, attr_name)
|
| 1091 |
+
if isinstance(attr, type) and issubclass(attr, nn.Module) and attr is not nn.Module:
|
| 1092 |
+
plugin_name = getattr(attr, 'plugin_name', attr_name)
|
| 1093 |
+
plugin_instance = attr(self.cfg, hidden_dim=self.base_model_raw.config.hidden_size)
|
| 1094 |
+
setattr(self, f"plugin_{plugin_name}", plugin_instance)
|
| 1095 |
+
self._plugins[plugin_name] = plugin_instance
|
| 1096 |
+
plugin_instance.to(self._dtype)
|
| 1097 |
+
print(f"Loaded plugin: {plugin_name} from {file.name}")
|
| 1098 |
+
except Exception as e:
|
| 1099 |
+
print(f"Failed to load plugin {file.name}: {e}")
|
| 1100 |
+
self._plugins: Dict[str, nn.Module] = {}
|
| 1101 |
+
|
| 1102 |
+
# Load plugins from plugin_dir if specified
|
| 1103 |
+
if self.cfg.plugin_dir:
|
| 1104 |
+
self.load_plugins(self.cfg.plugin_dir)
|
| 1105 |
|
| 1106 |
# ---- gradient-checkpointing passthrough (Trainer calls these on the top model) ----
|
| 1107 |
|
|
|
|
| 1212 |
# ---- System-2 reasoning loop ----
|
| 1213 |
reasoned, scratch = self.reasoner(base_hidden, steps)
|
| 1214 |
|
| 1215 |
+
# ---- multi-head CoT reasoning ----
|
| 1216 |
+
reasoned = self.cot_reasoner(reasoned)
|
| 1217 |
+
|
| 1218 |
# ---- subsystem gates ----
|
| 1219 |
g_think, g_code, g_mem, g_scratch = gates[:, 0], gates[:, 1], gates[:, 2], gates[:, 3]
|
| 1220 |
thresh = self.cfg.adaptive_threshold
|
|
|
|
| 1464 |
"coordinator", "reasoner", "code_bias", "scratchpad", "verifier",
|
| 1465 |
"skill_module", "depth_module", "moe_block", "memory",
|
| 1466 |
"mod", "multimodal_fusion", "tool_use", "document_module", "video_module",
|
| 1467 |
+
"code_sandbox", "cot_reasoner",
|
| 1468 |
):
|
| 1469 |
for k, v in getattr(self, name).state_dict().items():
|
| 1470 |
sd[f"{name}.{k}"] = v
|
| 1471 |
+
for plugin_name, plugin in self._plugins.items():
|
| 1472 |
+
for k, v in plugin.state_dict().items():
|
| 1473 |
+
sd[f"plugin_{plugin_name}.{k}"] = v
|
| 1474 |
if self.base_model is not None:
|
| 1475 |
try:
|
| 1476 |
from peft import get_peft_model_state_dict
|
|
|
|
| 1484 |
"coordinator", "reasoner", "code_bias", "scratchpad", "verifier",
|
| 1485 |
"skill_module", "depth_module", "moe_block", "memory",
|
| 1486 |
"mod", "multimodal_fusion", "tool_use", "document_module", "video_module",
|
| 1487 |
+
"code_sandbox", "cot_reasoner",
|
| 1488 |
):
|
| 1489 |
sub = {k[len(name) + 1:]: v for k, v in sd.items() if k.startswith(name + ".")}
|
| 1490 |
if sub:
|
| 1491 |
getattr(self, name).load_state_dict(sub)
|
| 1492 |
+
for plugin_name in self._plugins:
|
| 1493 |
+
prefix = f"plugin_{plugin_name}."
|
| 1494 |
+
sub = {k[len(prefix):]: v for k, v in sd.items() if k.startswith(prefix)}
|
| 1495 |
+
if sub:
|
| 1496 |
+
self._plugins[plugin_name].load_state_dict(sub)
|
| 1497 |
if self.base_model is not None:
|
| 1498 |
peft_sd = {k: v for k, v in sd.items() if k.startswith("base_model")}
|
| 1499 |
if peft_sd:
|
|
|
|
| 1520 |
"tool_use": self.tool_use.state_dict(),
|
| 1521 |
"document_module": self.document_module.state_dict(),
|
| 1522 |
"video_module": self.video_module.state_dict(),
|
| 1523 |
+
"code_sandbox": self.code_sandbox.state_dict(),
|
| 1524 |
+
"cot_reasoner": self.cot_reasoner.state_dict(),
|
| 1525 |
+
**{f"plugin_{k}": v.state_dict() for k, v in self._plugins.items()},
|
| 1526 |
"config": self.cfg,
|
| 1527 |
},
|
| 1528 |
f"{path}/morph_components.pt",
|
|
|
|
| 1543 |
"coordinator", "reasoner", "code_bias", "scratchpad", "verifier",
|
| 1544 |
"skill_module", "depth_module", "moe_block", "memory",
|
| 1545 |
"mod", "multimodal_fusion", "tool_use", "document_module", "video_module",
|
| 1546 |
+
"code_sandbox", "cot_reasoner",
|
| 1547 |
):
|
| 1548 |
if name in ckpt:
|
| 1549 |
getattr(self, name).load_state_dict(ckpt[name])
|
| 1550 |
+
for plugin_name in self._plugins:
|
| 1551 |
+
key = f"plugin_{plugin_name}"
|
| 1552 |
+
if key in ckpt:
|
| 1553 |
+
self._plugins[plugin_name].load_state_dict(ckpt[key])
|
| 1554 |
self.novel_trained = True
|
| 1555 |
else:
|
| 1556 |
trainer_ckpt = self._find_trainer_checkpoint(path)
|
src/runtime.py
CHANGED
|
@@ -101,12 +101,13 @@ class MorphRuntime:
|
|
| 101 |
Audio (ASR + TTS), Tools, Documents, Video, Multimodal fusion.
|
| 102 |
"""
|
| 103 |
|
| 104 |
-
def __init__(self, model_path: str, use_4bit: bool = True, use_cpu: bool = False):
|
| 105 |
self.model_path = Path(model_path)
|
| 106 |
self.use_4bit = use_4bit and not use_cpu
|
| 107 |
self.use_cpu = use_cpu
|
| 108 |
self.skills: Dict[str, Skill] = {}
|
| 109 |
self.active_skill: Optional[str] = None
|
|
|
|
| 110 |
|
| 111 |
# v5 pipeline layers
|
| 112 |
self.fsm = RuntimeFSM()
|
|
@@ -278,6 +279,8 @@ class MorphRuntime:
|
|
| 278 |
|
| 279 |
def _load_model(self):
|
| 280 |
config = MorphConfig()
|
|
|
|
|
|
|
| 281 |
self.model = MorphModel(config)
|
| 282 |
|
| 283 |
# base model was loaded bf16 by MorphModel; load the trained adapter
|
|
@@ -294,7 +297,21 @@ class MorphRuntime:
|
|
| 294 |
self.device = torch.device("cpu")
|
| 295 |
|
| 296 |
self.model = self.model.to(self.device)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 297 |
print(f"Model loaded on {self.device}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 298 |
|
| 299 |
def chat(self, prompt: str, max_tokens: int = 512, temperature: float = 0.7,
|
| 300 |
skill: Optional[str] = None, image_path: Optional[str] = None,
|
|
|
|
| 101 |
Audio (ASR + TTS), Tools, Documents, Video, Multimodal fusion.
|
| 102 |
"""
|
| 103 |
|
| 104 |
+
def __init__(self, model_path: str, use_4bit: bool = True, use_cpu: bool = False, plugin_dir: Optional[str] = None):
|
| 105 |
self.model_path = Path(model_path)
|
| 106 |
self.use_4bit = use_4bit and not use_cpu
|
| 107 |
self.use_cpu = use_cpu
|
| 108 |
self.skills: Dict[str, Skill] = {}
|
| 109 |
self.active_skill: Optional[str] = None
|
| 110 |
+
self.plugin_dir = plugin_dir
|
| 111 |
|
| 112 |
# v5 pipeline layers
|
| 113 |
self.fsm = RuntimeFSM()
|
|
|
|
| 279 |
|
| 280 |
def _load_model(self):
|
| 281 |
config = MorphConfig()
|
| 282 |
+
if self.plugin_dir:
|
| 283 |
+
config.plugin_dir = self.plugin_dir
|
| 284 |
self.model = MorphModel(config)
|
| 285 |
|
| 286 |
# base model was loaded bf16 by MorphModel; load the trained adapter
|
|
|
|
| 297 |
self.device = torch.device("cpu")
|
| 298 |
|
| 299 |
self.model = self.model.to(self.device)
|
| 300 |
+
|
| 301 |
+
# Enable extended context if configured
|
| 302 |
+
if hasattr(self.model.base_model_raw, 'config') and hasattr(self.model.base_model_raw.config, 'rope_scaling'):
|
| 303 |
+
if self.model.base_model_raw.config.rope_scaling:
|
| 304 |
+
print(f"Extended context enabled: {self.model.base_model_raw.config.rope_scaling}")
|
| 305 |
+
|
| 306 |
print(f"Model loaded on {self.device}")
|
| 307 |
+
if self.model._plugins:
|
| 308 |
+
print(f"Loaded plugins: {list(self.model._plugins.keys())}")
|
| 309 |
+
|
| 310 |
+
def expand_moe_experts(self):
|
| 311 |
+
"""Dynamically expand MoE experts based on usage patterns."""
|
| 312 |
+
if hasattr(self.model, 'moe_block') and hasattr(self.model.moe_block, 'prune_and_expand_experts'):
|
| 313 |
+
self.model.moe_block.prune_and_expand_experts()
|
| 314 |
+
print(f"MoE expanded. Total experts: {len(self.model.moe_block.experts)}")
|
| 315 |
|
| 316 |
def chat(self, prompt: str, max_tokens: int = 512, temperature: float = 0.7,
|
| 317 |
skill: Optional[str] = None, image_path: Optional[str] = None,
|