Text Generation
GGUF
llama.cpp
rocmfp4
rocmfpx
strix-halo
gfx1151
amd
rocm
Mixture of Experts
cohere2moe
code
ryzen-ai-max
conversational
Instructions to use kingjones777/North-Mini-Code-1.0-ROCmFP4-STRIX-GGUF with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- llama.cpp
How to use kingjones777/North-Mini-Code-1.0-ROCmFP4-STRIX-GGUF 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 kingjones777/North-Mini-Code-1.0-ROCmFP4-STRIX-GGUF:Q4_0_ROCMFP # Run inference directly in the terminal: llama cli -hf kingjones777/North-Mini-Code-1.0-ROCmFP4-STRIX-GGUF:Q4_0_ROCMFP
Install from WinGet (Windows)
winget install llama.cpp # Start a local OpenAI-compatible server with a web UI: llama serve -hf kingjones777/North-Mini-Code-1.0-ROCmFP4-STRIX-GGUF:Q4_0_ROCMFP # Run inference directly in the terminal: llama cli -hf kingjones777/North-Mini-Code-1.0-ROCmFP4-STRIX-GGUF:Q4_0_ROCMFP
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 kingjones777/North-Mini-Code-1.0-ROCmFP4-STRIX-GGUF:Q4_0_ROCMFP # Run inference directly in the terminal: ./llama-cli -hf kingjones777/North-Mini-Code-1.0-ROCmFP4-STRIX-GGUF:Q4_0_ROCMFP
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 kingjones777/North-Mini-Code-1.0-ROCmFP4-STRIX-GGUF:Q4_0_ROCMFP # Run inference directly in the terminal: ./build/bin/llama-cli -hf kingjones777/North-Mini-Code-1.0-ROCmFP4-STRIX-GGUF:Q4_0_ROCMFP
Use Docker
docker model run hf.co/kingjones777/North-Mini-Code-1.0-ROCmFP4-STRIX-GGUF:Q4_0_ROCMFP
- LM Studio
- Jan
- vLLM
How to use kingjones777/North-Mini-Code-1.0-ROCmFP4-STRIX-GGUF with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "kingjones777/North-Mini-Code-1.0-ROCmFP4-STRIX-GGUF" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "kingjones777/North-Mini-Code-1.0-ROCmFP4-STRIX-GGUF", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }'Use Docker
docker model run hf.co/kingjones777/North-Mini-Code-1.0-ROCmFP4-STRIX-GGUF:Q4_0_ROCMFP
- Ollama
How to use kingjones777/North-Mini-Code-1.0-ROCmFP4-STRIX-GGUF with Ollama:
ollama run hf.co/kingjones777/North-Mini-Code-1.0-ROCmFP4-STRIX-GGUF:Q4_0_ROCMFP
- Unsloth Desktop
- Pi
How to use kingjones777/North-Mini-Code-1.0-ROCmFP4-STRIX-GGUF with Pi:
Start the llama.cpp server
# Install llama.cpp: brew install llama.cpp # Start a local OpenAI-compatible server: llama serve -hf kingjones777/North-Mini-Code-1.0-ROCmFP4-STRIX-GGUF:Q4_0_ROCMFP
Configure the model in Pi
# Install Pi: npm install -g @earendil-works/pi-coding-agent # Add to ~/.pi/agent/models.json: { "providers": { "llama-cpp": { "baseUrl": "http://localhost:8080/v1", "api": "openai-completions", "apiKey": "none", "models": [ { "id": "kingjones777/North-Mini-Code-1.0-ROCmFP4-STRIX-GGUF:Q4_0_ROCMFP" } ] } } }Run Pi
# Start Pi in your project directory: pi
- Docker Model Runner
How to use kingjones777/North-Mini-Code-1.0-ROCmFP4-STRIX-GGUF with Docker Model Runner:
docker model run hf.co/kingjones777/North-Mini-Code-1.0-ROCmFP4-STRIX-GGUF:Q4_0_ROCMFP
- Lemonade
How to use kingjones777/North-Mini-Code-1.0-ROCmFP4-STRIX-GGUF with Lemonade:
Pull the model
# Download Lemonade from https://lemonade-server.ai/ lemonade pull kingjones777/North-Mini-Code-1.0-ROCmFP4-STRIX-GGUF:Q4_0_ROCMFP
Run and chat with the model
lemonade run user.North-Mini-Code-1.0-ROCmFP4-STRIX-GGUF-Q4_0_ROCMFP
List all available models
lemonade list
- Hermes Agent
How to use kingjones777/North-Mini-Code-1.0-ROCmFP4-STRIX-GGUF with Hermes Agent:
Start the llama.cpp server
# Install llama.cpp: brew install llama.cpp # Start a local OpenAI-compatible server: llama serve -hf kingjones777/North-Mini-Code-1.0-ROCmFP4-STRIX-GGUF:Q4_0_ROCMFP
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 kingjones777/North-Mini-Code-1.0-ROCmFP4-STRIX-GGUF:Q4_0_ROCMFP
Run Hermes
hermes
- Atomic Chat
- OpenClaw
How to use kingjones777/North-Mini-Code-1.0-ROCmFP4-STRIX-GGUF with OpenClaw:
Start the llama.cpp server
# Install llama.cpp: brew install llama.cpp # Start a local OpenAI-compatible server: llama serve -hf kingjones777/North-Mini-Code-1.0-ROCmFP4-STRIX-GGUF:Q4_0_ROCMFP
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 "kingjones777/North-Mini-Code-1.0-ROCmFP4-STRIX-GGUF:Q4_0_ROCMFP" \ --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"
Upload folder using huggingface_hub
Browse files- .gitattributes +1 -0
- North-Mini-Code-1.0-Q4_0_ROCMFP4_STRIX.gguf +3 -0
- README.md +139 -0
- SHA256SUMS +3 -0
- cohere2moe-rocmfpx.patch +760 -0
.gitattributes
CHANGED
|
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
+
North-Mini-Code-1.0-Q4_0_ROCMFP4_STRIX.gguf filter=lfs diff=lfs merge=lfs -text
|
North-Mini-Code-1.0-Q4_0_ROCMFP4_STRIX.gguf
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:2f3b01320aba3de875c2370cf041f53d5915c54885aff09ef8764bb13ea88264
|
| 3 |
+
size 16407617344
|
README.md
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
license: apache-2.0
|
| 3 |
+
base_model: CohereLabs/North-Mini-Code-1.0
|
| 4 |
+
base_model_relation: quantized
|
| 5 |
+
pipeline_tag: text-generation
|
| 6 |
+
library_name: llama.cpp
|
| 7 |
+
tags:
|
| 8 |
+
- gguf
|
| 9 |
+
- rocmfp4
|
| 10 |
+
- rocmfpx
|
| 11 |
+
- strix-halo
|
| 12 |
+
- gfx1151
|
| 13 |
+
- amd
|
| 14 |
+
- rocm
|
| 15 |
+
- moe
|
| 16 |
+
- cohere2moe
|
| 17 |
+
- code
|
| 18 |
+
---
|
| 19 |
+
|
| 20 |
+
# North-Mini-Code-1.0 — ROCmFP4 STRIX (GGUF)
|
| 21 |
+
|
| 22 |
+
This is a `Q4_0_ROCMFP4_STRIX` quant of [CohereLabs/North-Mini-Code-1.0](https://huggingface.co/CohereLabs/North-Mini-Code-1.0), built and tested on an AMD Ryzen AI Max+ 395 (Strix Halo, gfx1151, 128 GB unified memory) running ROCm 7.2.4.
|
| 23 |
+
|
| 24 |
+
North-Mini-Code is the agentic coder I actually reach for on my AMD boxes, and ROCmFP4 is the format that pays for itself on Strix Halo. There was no ROCmFP4 build of it, and it turned out there *couldn't* be one — so I made the piece that was missing first.
|
| 25 |
+
|
| 26 |
+
**Short version: 17.93 GB of Q4_K_XL becomes 15.28 GB and decodes 26.6% faster, with the same answers. But you need the patch in this repo to load it at all.**
|
| 27 |
+
|
| 28 |
+
## Read this before you download
|
| 29 |
+
|
| 30 |
+
The two things this file needs live in two different codebases, and neither one has both:
|
| 31 |
+
|
| 32 |
+
- **Mainline llama.cpp** implements the `cohere2moe` architecture. It does not implement ROCmFP4.
|
| 33 |
+
- **The [ROCmFPX fork](https://github.com/charlie12345/ROCmFPX)** implements ROCmFP4 (GGUF tensor types 100/101). It does not implement `cohere2moe`.
|
| 34 |
+
|
| 35 |
+
So no build in existence can load this GGUF. I ported `cohere2moe` from mainline into ROCmFPX, and **`cohere2moe-rocmfpx.patch` in this repo is that port**. Apply it, build, and the file works. Without it you will get an unknown-architecture error, and that is expected, not a corrupt download.
|
| 36 |
+
|
| 37 |
+
```bash
|
| 38 |
+
git clone https://github.com/charlie12345/ROCmFPX && cd ROCmFPX
|
| 39 |
+
git apply /path/to/cohere2moe-rocmfpx.patch
|
| 40 |
+
HIPCXX=$(hipconfig -l)/clang HIP_PATH=$(hipconfig -R) cmake -B build -DGGML_HIP=ON \
|
| 41 |
+
-DGPU_TARGETS=gfx1151 -DGGML_HIP_ROCWMMA_FATTN=ON -DGGML_HIP_NO_VMM=ON \
|
| 42 |
+
-DGGML_HIP_MMQ_MFMA=ON -DCMAKE_BUILD_TYPE=Release -DGGML_VULKAN=OFF -DLLAMA_BUILD_WEBUI=OFF
|
| 43 |
+
cmake --build build -j 4
|
| 44 |
+
```
|
| 45 |
+
|
| 46 |
+
Check it took: `strings build/bin/libllama.so | grep -cx cohere2moe` should print `1`. Check `libllama.so`, not `llama-server` — the server binary prints 0 even on a build that runs the model fine, which cost me an hour of thinking the port had failed.
|
| 47 |
+
|
| 48 |
+
I have not upstreamed this to ROCmFPX. If Charlie wants it, it's here.
|
| 49 |
+
|
| 50 |
+
## What's in the patch
|
| 51 |
+
|
| 52 |
+
12 files, 17 hunks. A new `src/models/cohere2moe.cpp` graph plus the arch enum, tensor mappings, hparams/tensor loading, the converter class, and the gguf-py constants.
|
| 53 |
+
|
| 54 |
+
The graph is mainline's, adapted where the fork's API differs — `hparams.n_layer` instead of `n_layer()`, `nextn_predict_layers` instead of `n_layer_nextn`, and the sliding-window pattern read as a bool array because the fork has no `is_swa_impl`. None of the arithmetic changed.
|
| 55 |
+
|
| 56 |
+
⚠️ **MTP is not ported.** The fork lacks the `t_h_nextn` result slots the mainline MTP path needs. North-Mini-Code's GGUFs carry no nextn layers so nothing is lost today, but if Cohere ships an MTP variant this patch won't load it.
|
| 57 |
+
|
| 58 |
+
## The bias thing, since it bit everyone else
|
| 59 |
+
|
| 60 |
+
North-Mini-Code ships **18,532 per-expert FC bias keys** in its HF weights. On vLLM that's a genuine three-layer problem — the model class, the NVFP4 MoE weight registration, and the fused kernel all assume no bias, and you get `'RoutedExperts' object has no attribute 'w2_bias'`.
|
| 61 |
+
|
| 62 |
+
On the GGUF path it's a non-issue, and it's worth knowing why rather than being surprised: **those biases are all zero**, and the converter drops zero biases. I parsed both files to confirm — 442 tensors each, **0 bias tensors**, in the Q4_K_XL I was comparing against and in my own output. Passing null biases is correct here, not a shortcut.
|
| 63 |
+
|
| 64 |
+
I still wired every bias tensor as `TENSOR_NOT_REQUIRED` anyway — expert gate/up/down, the router, attention output, and the dense FFN — so a future checkpoint with non-zero biases binds without recompiling. The converter keeps mainline's policy: skip if zero, raise if not.
|
| 65 |
+
|
| 66 |
+
## Files
|
| 67 |
+
|
| 68 |
+
| File | Size |
|
| 69 |
+
|---|---|
|
| 70 |
+
| `North-Mini-Code-1.0-Q4_0_ROCMFP4_STRIX.gguf` | 15.281 GiB (16,407,617,344 bytes) |
|
| 71 |
+
| `cohere2moe-rocmfpx.patch` | 36 KB — required, see above |
|
| 72 |
+
| `SHA256SUMS` | |
|
| 73 |
+
|
| 74 |
+
Single file, 442 tensors, 53 metadata keys.
|
| 75 |
+
|
| 76 |
+
## Running it
|
| 77 |
+
|
| 78 |
+
```bash
|
| 79 |
+
env LD_LIBRARY_PATH=/path/to/ROCmFPX/build/bin:/opt/rocm/lib \
|
| 80 |
+
HSA_OVERRIDE_GFX_VERSION=11.5.1 GGML_HIP_ENABLE_UNIFIED_MEMORY=1 \
|
| 81 |
+
./llama-server --host 127.0.0.1 --port 8080 \
|
| 82 |
+
-ngl 999 -fa on -dio --jinja --metrics \
|
| 83 |
+
--model North-Mini-Code-1.0-Q4_0_ROCMFP4_STRIX.gguf \
|
| 84 |
+
--ctx-size 65536 --parallel 1 --temp 0.3 --top-p 0.95
|
| 85 |
+
```
|
| 86 |
+
|
| 87 |
+
Three things that will save you time:
|
| 88 |
+
|
| 89 |
+
**Use `--temp 0.3`, not greedy.** This is the one that surprised me. Greedy decoding on this model produces code whose modal output does not run — I get `IndentationError` most of the time at temp 0. At 0.3 it's clean. I originally "improved" my own config to temp 0 and had to walk it back.
|
| 90 |
+
|
| 91 |
+
**Don't send `reasoning_effort: none`.** It's the documented thinking-off lever for North and it works on vLLM, but on llama.cpp there's no `cohere_command4` reasoning parser, so the flag moves your answer out of `content` and into `reasoning_content` still wrapped in `<|START_TEXT|>…<|END_TEXT|>`. Just leave it off — `content` comes back clean and the chain of thought lands in `reasoning_content` where you'd want it.
|
| 92 |
+
|
| 93 |
+
**`-dio` matters** on the ROCmFP4 runtime. The mmap path can sit there looking hung on large models.
|
| 94 |
+
|
| 95 |
+
## Benchmarks
|
| 96 |
+
|
| 97 |
+
One Ryzen AI Max+ 395, same binary, same flags for both, ROCm backend, `--parallel 1`, speculative decoding off, temp 0.3, nonce-prefixed prompts so nothing came from the prefix cache. Warmed before measuring. **7 runs each**, and I'm giving you the full range rather than just a median, because a median alone can't tell you whether a difference is real.
|
| 98 |
+
|
| 99 |
+
| | Size | Median tok/s | Min | Max |
|
| 100 |
+
|---|---|---|---|---|
|
| 101 |
+
| `UD-Q4_K_XL` (unsloth) | 17.931 GiB | 54.23 | 54.20 | 54.50 |
|
| 102 |
+
| **This build** | **15.281 GiB** | **68.65** | 68.51 | 69.74 |
|
| 103 |
+
|
| 104 |
+
**+26.6%, and the two ranges don't overlap** — 54.50 max against 68.51 min. That's the bar I hold myself to; if the ranges had touched I'd be telling you it was inconclusive.
|
| 105 |
+
|
| 106 |
+
I ran it a second time on a shorter prompt (128 max tokens instead of 256) and got 56.94 → 73.40, which is +28.9% and also disjoint. Different absolute numbers, same answer.
|
| 107 |
+
|
| 108 |
+
**Quality held.** Both builds were put through the same problems with the generated Python **executed against assertions** — reverse a string, primality, flatten-one-level, binary search, and an arithmetic check. Both 5/5, plus a merge-two-sorted-lists problem that also executed correctly on both. That is a small battery and I'm not going to dress it up as a benchmark suite; it's enough to say this quant didn't break the model, and not enough to claim a quality ranking.
|
| 109 |
+
|
| 110 |
+
**No BF16 A/B.** I did not benchmark the full-precision weights on this box, so I'm not quoting a quality delta against them.
|
| 111 |
+
|
| 112 |
+
**Measured BPW is 4.30**, not the ~4.49 the quantizer's help text advertises for this recipe. I'm quoting what the log said, not the menu.
|
| 113 |
+
|
| 114 |
+
## How it was made
|
| 115 |
+
|
| 116 |
+
Quantized from the unsloth BF16 shards (split-aware, no merge needed), not requantized from a lower-bit source:
|
| 117 |
+
|
| 118 |
+
```bash
|
| 119 |
+
./llama-quantize North-Mini-Code-1.0-BF16-00001-of-00002.gguf \
|
| 120 |
+
North-Mini-Code-1.0-Q4_0_ROCMFP4_STRIX.gguf Q4_0_ROCMFP4_STRIX 8
|
| 121 |
+
```
|
| 122 |
+
|
| 123 |
+
58,168 MiB in, 15,638 MiB out, about six minutes.
|
| 124 |
+
|
| 125 |
+
I used `Q4_0_ROCMFP4_STRIX` (type 105) and not `Q4_0_ROCMFP4_STRIX_LEAN` (106) deliberately. LEAN saves about half a gig by dropping token embeddings to Q5_K, and on a coder I ran this week that cost two executed-code problems out of thirty for no speed gain at all — the two recipes measured identically on throughput. If you're quantizing something correctness-sensitive for Strix Halo, spend the half gig.
|
| 126 |
+
|
| 127 |
+
## The model
|
| 128 |
+
|
| 129 |
+
North-Mini-Code-1.0 is Cohere Labs' agentic coding model — 30B total, ~3B active, 128 experts, `cohere2moe`. Everything about behaviour, prompt format, and capability comes from upstream; see their [model card](https://huggingface.co/CohereLabs/North-Mini-Code-1.0). All I changed is the numeric format.
|
| 130 |
+
|
| 131 |
+
Worth noting for anyone sizing this: ~3B active is a *small* active-parameter count, and the received wisdom around ROCmFP4 has been that it pays off on dense attention and big-active MoE but not on small-active MoE. That's now two small-active MoE models in a row where I've measured a solid double-digit decode win, so I no longer believe that rule.
|
| 132 |
+
|
| 133 |
+
## License
|
| 134 |
+
|
| 135 |
+
Apache 2.0, inherited from the base model. The patch is a derivative of llama.cpp and ROCmFPX and carries their MIT license.
|
| 136 |
+
|
| 137 |
+
## Credit
|
| 138 |
+
|
| 139 |
+
The model is Cohere Labs'. ROCmFP4 and the `Q4_0_ROCMFP4_STRIX` recipe are [charlie12345/ROCmFPX](https://github.com/charlie12345/ROCmFPX). The `cohere2moe` implementation is [ggml-org/llama.cpp](https://github.com/ggml-org/llama.cpp) — I ported it across, I didn't invent it. I built the quant, ran the benchmarks, and wrote down what I found.
|
SHA256SUMS
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
2f3b01320aba3de875c2370cf041f53d5915c54885aff09ef8764bb13ea88264 North-Mini-Code-1.0-Q4_0_ROCMFP4_STRIX.gguf
|
| 2 |
+
87fbf2e0ffe8c985bd95295f615e78a01286df6d8854f3b6090bd907c0e1f791 cohere2moe-rocmfpx.patch
|
| 3 |
+
e51d193a651c6fd9438c848135b30d852c399f49088d93d7328eb3d65e702bcd README.md
|
cohere2moe-rocmfpx.patch
ADDED
|
@@ -0,0 +1,760 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
--- a/src/llama-arch.h
|
| 2 |
+
+++ b/src/llama-arch.h
|
| 3 |
+
@@ -72,6 +72,7 @@
|
| 4 |
+
LLM_ARCH_XVERSE,
|
| 5 |
+
LLM_ARCH_COMMAND_R,
|
| 6 |
+
LLM_ARCH_COHERE2,
|
| 7 |
+
+ LLM_ARCH_COHERE2MOE,
|
| 8 |
+
LLM_ARCH_DBRX,
|
| 9 |
+
LLM_ARCH_OLMO,
|
| 10 |
+
LLM_ARCH_OLMO2,
|
| 11 |
+
--- a/src/llama-arch.cpp
|
| 12 |
+
+++ b/src/llama-arch.cpp
|
| 13 |
+
@@ -67,6 +67,7 @@
|
| 14 |
+
{ LLM_ARCH_XVERSE, "xverse" },
|
| 15 |
+
{ LLM_ARCH_COMMAND_R, "command-r" },
|
| 16 |
+
{ LLM_ARCH_COHERE2, "cohere2" },
|
| 17 |
+
+ { LLM_ARCH_COHERE2MOE, "cohere2moe" },
|
| 18 |
+
{ LLM_ARCH_DBRX, "dbrx" },
|
| 19 |
+
{ LLM_ARCH_OLMO, "olmo" },
|
| 20 |
+
{ LLM_ARCH_OLMO2, "olmo2" },
|
| 21 |
+
--- a/src/llama-model.cpp
|
| 22 |
+
+++ b/src/llama-model.cpp
|
| 23 |
+
@@ -155,6 +155,8 @@
|
| 24 |
+
return new llama_model_command_r(params);
|
| 25 |
+
case LLM_ARCH_COHERE2:
|
| 26 |
+
return new llama_model_cohere2(params);
|
| 27 |
+
+ case LLM_ARCH_COHERE2MOE:
|
| 28 |
+
+ return new llama_model_cohere2moe(params);
|
| 29 |
+
case LLM_ARCH_DBRX:
|
| 30 |
+
return new llama_model_dbrx(params);
|
| 31 |
+
case LLM_ARCH_OLMO:
|
| 32 |
+
@@ -1788,7 +1790,7 @@
|
| 33 |
+
LLAMA_LOG_INFO("%s: n_ff_shexp = %d\n", __func__, hparams.n_ff_shexp);
|
| 34 |
+
}
|
| 35 |
+
|
| 36 |
+
- if (arch == LLM_ARCH_QWEN3MOE || arch == LLM_ARCH_OPENAI_MOE || arch == LLM_ARCH_QWEN3VLMOE || arch == LLM_ARCH_RND1) {
|
| 37 |
+
+ if (arch == LLM_ARCH_COHERE2MOE || arch == LLM_ARCH_QWEN3MOE || arch == LLM_ARCH_OPENAI_MOE || arch == LLM_ARCH_QWEN3VLMOE || arch == LLM_ARCH_RND1) {
|
| 38 |
+
LLAMA_LOG_INFO("%s: n_ff_exp = %d\n", __func__, hparams.n_ff_exp);
|
| 39 |
+
}
|
| 40 |
+
|
| 41 |
+
@@ -2338,6 +2340,7 @@
|
| 42 |
+
case LLM_ARCH_XVERSE:
|
| 43 |
+
case LLM_ARCH_COMMAND_R:
|
| 44 |
+
case LLM_ARCH_COHERE2:
|
| 45 |
+
+ case LLM_ARCH_COHERE2MOE:
|
| 46 |
+
case LLM_ARCH_OLMO:
|
| 47 |
+
case LLM_ARCH_ARCTIC:
|
| 48 |
+
case LLM_ARCH_DEEPSEEK:
|
| 49 |
+
--- a/src/models/models.h
|
| 50 |
+
+++ b/src/models/models.h
|
| 51 |
+
@@ -964,6 +964,20 @@
|
| 52 |
+
};
|
| 53 |
+
|
| 54 |
+
|
| 55 |
+
+
|
| 56 |
+
+struct llama_model_cohere2moe : public llama_model_base {
|
| 57 |
+
+ llama_model_cohere2moe(const struct llama_model_params & params) : llama_model_base(params) {}
|
| 58 |
+
+ void load_arch_hparams(llama_model_loader & ml) override;
|
| 59 |
+
+ void load_arch_tensors(llama_model_loader & ml) override;
|
| 60 |
+
+
|
| 61 |
+
+ struct graph : public llm_graph_context {
|
| 62 |
+
+ graph(const llama_model & model, const llm_graph_params & params);
|
| 63 |
+
+ };
|
| 64 |
+
+
|
| 65 |
+
+ std::unique_ptr<llm_graph_context> build_arch_graph(const llm_graph_params & params) const override;
|
| 66 |
+
+};
|
| 67 |
+
+
|
| 68 |
+
+
|
| 69 |
+
struct llama_model_dbrx : public llama_model_base {
|
| 70 |
+
llama_model_dbrx(const struct llama_model_params & params) : llama_model_base(params) {}
|
| 71 |
+
void load_arch_hparams(llama_model_loader & ml) override;
|
| 72 |
+
--- a/src/llama-model-saver.cpp
|
| 73 |
+
+++ b/src/llama-model-saver.cpp
|
| 74 |
+
@@ -21,6 +21,7 @@
|
| 75 |
+
case LLM_ARCH_GEMMA3:
|
| 76 |
+
case LLM_ARCH_GEMMA3N:
|
| 77 |
+
case LLM_ARCH_COHERE2:
|
| 78 |
+
+ case LLM_ARCH_COHERE2MOE:
|
| 79 |
+
case LLM_ARCH_OLMO2:
|
| 80 |
+
case LLM_ARCH_BITNET:
|
| 81 |
+
case LLM_ARCH_T5:
|
| 82 |
+
--- a/src/llama-vocab.cpp
|
| 83 |
+
+++ b/src/llama-vocab.cpp
|
| 84 |
+
@@ -2267,7 +2267,8 @@
|
| 85 |
+
pre_type = LLAMA_VOCAB_PRE_TYPE_GPT4O;
|
| 86 |
+
clean_spaces = false;
|
| 87 |
+
} else if (
|
| 88 |
+
- tokenizer_pre == "tiny_aya") {
|
| 89 |
+
+ tokenizer_pre == "tiny_aya" ||
|
| 90 |
+
+ tokenizer_pre == "cohere2moe") {
|
| 91 |
+
pre_type = LLAMA_VOCAB_PRE_TYPE_TINY_AYA;
|
| 92 |
+
clean_spaces = false;
|
| 93 |
+
} else if (
|
| 94 |
+
--- a/gguf-py/gguf/constants.py
|
| 95 |
+
+++ b/gguf-py/gguf/constants.py
|
| 96 |
+
@@ -467,6 +467,7 @@
|
| 97 |
+
XVERSE = auto()
|
| 98 |
+
COMMAND_R = auto()
|
| 99 |
+
COHERE2 = auto()
|
| 100 |
+
+ COHERE2MOE = auto()
|
| 101 |
+
DBRX = auto()
|
| 102 |
+
OLMO = auto()
|
| 103 |
+
OLMO2 = auto()
|
| 104 |
+
@@ -1029,6 +1030,7 @@
|
| 105 |
+
MODEL_ARCH.XVERSE: "xverse",
|
| 106 |
+
MODEL_ARCH.COMMAND_R: "command-r",
|
| 107 |
+
MODEL_ARCH.COHERE2: "cohere2",
|
| 108 |
+
+ MODEL_ARCH.COHERE2MOE: "cohere2moe",
|
| 109 |
+
MODEL_ARCH.DBRX: "dbrx",
|
| 110 |
+
MODEL_ARCH.OLMO: "olmo",
|
| 111 |
+
MODEL_ARCH.OLMO2: "olmo2",
|
| 112 |
+
@@ -2905,6 +2907,33 @@
|
| 113 |
+
MODEL_TENSOR.FFN_DOWN,
|
| 114 |
+
MODEL_TENSOR.FFN_UP,
|
| 115 |
+
],
|
| 116 |
+
+ MODEL_ARCH.COHERE2MOE: [
|
| 117 |
+
+ MODEL_TENSOR.TOKEN_EMBD,
|
| 118 |
+
+ MODEL_TENSOR.OUTPUT_NORM,
|
| 119 |
+
+ MODEL_TENSOR.OUTPUT,
|
| 120 |
+
+ MODEL_TENSOR.ATTN_NORM,
|
| 121 |
+
+ MODEL_TENSOR.ATTN_Q,
|
| 122 |
+
+ MODEL_TENSOR.ATTN_K,
|
| 123 |
+
+ MODEL_TENSOR.ATTN_V,
|
| 124 |
+
+ MODEL_TENSOR.ATTN_OUT,
|
| 125 |
+
+ MODEL_TENSOR.FFN_GATE,
|
| 126 |
+
+ MODEL_TENSOR.FFN_DOWN,
|
| 127 |
+
+ MODEL_TENSOR.FFN_UP,
|
| 128 |
+
+ MODEL_TENSOR.FFN_GATE_INP,
|
| 129 |
+
+ MODEL_TENSOR.FFN_GATE_EXP,
|
| 130 |
+
+ MODEL_TENSOR.FFN_GATE_UP_EXP,
|
| 131 |
+
+ MODEL_TENSOR.FFN_DOWN_EXP,
|
| 132 |
+
+ MODEL_TENSOR.FFN_UP_EXP,
|
| 133 |
+
+ MODEL_TENSOR.FFN_GATE_SHEXP,
|
| 134 |
+
+ MODEL_TENSOR.FFN_DOWN_SHEXP,
|
| 135 |
+
+ MODEL_TENSOR.FFN_UP_SHEXP,
|
| 136 |
+
+ MODEL_TENSOR.NEXTN_EH_PROJ,
|
| 137 |
+
+ MODEL_TENSOR.NEXTN_EMBED_TOKENS,
|
| 138 |
+
+ MODEL_TENSOR.NEXTN_ENORM,
|
| 139 |
+
+ MODEL_TENSOR.NEXTN_HNORM,
|
| 140 |
+
+ MODEL_TENSOR.NEXTN_SHARED_HEAD_HEAD,
|
| 141 |
+
+ MODEL_TENSOR.NEXTN_SHARED_HEAD_NORM,
|
| 142 |
+
+ ],
|
| 143 |
+
MODEL_ARCH.DBRX: [
|
| 144 |
+
MODEL_TENSOR.TOKEN_EMBD,
|
| 145 |
+
MODEL_TENSOR.OUTPUT_NORM,
|
| 146 |
+
--- a/convert_hf_to_gguf.py
|
| 147 |
+
+++ b/convert_hf_to_gguf.py
|
| 148 |
+
@@ -1443,7 +1443,7 @@
|
| 149 |
+
res = "command-r"
|
| 150 |
+
if chkhsh == "d772b220ace2baec124bed8cfafce0ead7d6c38a4b65ef11261cf9d5d62246d1":
|
| 151 |
+
# ref: https://huggingface.co/CohereLabs/tiny-aya-base
|
| 152 |
+
- res = "tiny_aya"
|
| 153 |
+
+ res = "tiny_aya" # also used by cohere2moe / North Mini
|
| 154 |
+
if chkhsh == "e636dc30a262dcc0d8c323492e32ae2b70728f4df7dfe9737d9f920a282b8aea":
|
| 155 |
+
# ref: https://huggingface.co/Qwen/Qwen1.5-7B
|
| 156 |
+
res = "qwen2"
|
| 157 |
+
@@ -8978,6 +8978,89 @@
|
| 158 |
+
yield from super().modify_tensors(data_torch, name, bid)
|
| 159 |
+
|
| 160 |
+
|
| 161 |
+
+
|
| 162 |
+
+@ModelBase.register("Cohere2MoeForCausalLM")
|
| 163 |
+
+class Cohere2MoeModel(TextModel):
|
| 164 |
+
+ """Cohere2 MoE (North Mini Code). Ported from mainline conversion/command_r.py.
|
| 165 |
+
+ Per-expert FC biases: skipped when zero; ValueError if non-zero (runtime has no bias path required for North GGUFs).
|
| 166 |
+
+ """
|
| 167 |
+
+ model_arch = gguf.MODEL_ARCH.COHERE2MOE
|
| 168 |
+
+ _n_main_layers = None
|
| 169 |
+
+ _expert_tensor_re = __import__("re").compile(
|
| 170 |
+
+ r"model\.layers\.(\d+)\.mlp\.experts\.(\d+)\.(down_proj|gate_proj|up_proj)\.weight"
|
| 171 |
+
+ )
|
| 172 |
+
+
|
| 173 |
+
+ def __init__(self, *args, **kwargs):
|
| 174 |
+
+ super().__init__(*args, **kwargs)
|
| 175 |
+
+ n_nextn = int(self.hparams.get("num_nextn_predict_layers", 0) or 0)
|
| 176 |
+
+ if n_nextn > 0 and not getattr(self, "no_mtp", False):
|
| 177 |
+
+ self.block_count += n_nextn
|
| 178 |
+
+ self.tensor_map = gguf.get_tensor_name_map(self.model_arch, self.block_count)
|
| 179 |
+
+ self._experts = [{} for _ in range(self.block_count)]
|
| 180 |
+
+
|
| 181 |
+
+ def set_gguf_parameters(self):
|
| 182 |
+
+ hparams = self.hparams
|
| 183 |
+
+ expert_intermediate_size = hparams["intermediate_size"]
|
| 184 |
+
+ mlp_layer_types = hparams.get("mlp_layer_types")
|
| 185 |
+
+ n_dense_lead = hparams.get("first_k_dense_replace", 0)
|
| 186 |
+
+ if mlp_layer_types is not None:
|
| 187 |
+
+ n_dense_lead = next((i for i, tp in enumerate(mlp_layer_types) if tp != "dense"), len(mlp_layer_types))
|
| 188 |
+
+ super().set_gguf_parameters()
|
| 189 |
+
+ self.gguf_writer.add_logit_scale(hparams["logit_scale"])
|
| 190 |
+
+ self.gguf_writer.add_sliding_window(hparams["sliding_window"])
|
| 191 |
+
+ self.gguf_writer.add_sliding_window_pattern([tp == "sliding_attention" for tp in hparams["layer_types"]])
|
| 192 |
+
+ self.gguf_writer.add_vocab_size(hparams["vocab_size"])
|
| 193 |
+
+ self.gguf_writer.add_expert_feed_forward_length(expert_intermediate_size)
|
| 194 |
+
+ self.gguf_writer.add_leading_dense_block_count(n_dense_lead)
|
| 195 |
+
+ self.gguf_writer.add_expert_weights_norm(hparams.get("norm_topk_prob", False))
|
| 196 |
+
+ if (num_shared_experts := hparams.get("num_shared_experts", 0)) > 0:
|
| 197 |
+
+ if hparams.get("shared_expert_combination_strategy", "average") != "average":
|
| 198 |
+
+ raise ValueError("Cohere2 MoE only supports average shared expert combination")
|
| 199 |
+
+ self.gguf_writer.add_expert_shared_count(num_shared_experts)
|
| 200 |
+
+ self.gguf_writer.add_expert_shared_feed_forward_length(expert_intermediate_size * num_shared_experts)
|
| 201 |
+
+ n_nextn = hparams.get("num_nextn_predict_layers", 0)
|
| 202 |
+
+ if n_nextn > 0 and not getattr(self, "no_mtp", False):
|
| 203 |
+
+ self.gguf_writer.add_nextn_predict_layers(n_nextn)
|
| 204 |
+
+ self.gguf_writer.add_rope_dimension_count(hparams["head_dim"])
|
| 205 |
+
+ self.gguf_writer.add_rope_scaling_type(gguf.RopeScalingType.NONE)
|
| 206 |
+
+
|
| 207 |
+
+ def modify_tensors(self, data_torch, name, bid=None):
|
| 208 |
+
+ import torch
|
| 209 |
+
+ if name.endswith(".bias"):
|
| 210 |
+
+ if torch.any(data_torch != 0):
|
| 211 |
+
+ raise ValueError(f"Bias tensor {name!r} is not zero.")
|
| 212 |
+
+ logger.debug(f"Skipping bias tensor {name!r}.")
|
| 213 |
+
+ return
|
| 214 |
+
+ m = self._expert_tensor_re.fullmatch(name)
|
| 215 |
+
+ if m is not None:
|
| 216 |
+
+ n_experts = self.hparams["num_experts"]
|
| 217 |
+
+ layer_idx = int(m.group(1))
|
| 218 |
+
+ self._experts[layer_idx][name] = data_torch
|
| 219 |
+
+ expected = {
|
| 220 |
+
+ f"model.layers.{layer_idx}.mlp.experts.{xid}.{w_name}.weight"
|
| 221 |
+
+ for xid in range(n_experts)
|
| 222 |
+
+ for w_name in ("down_proj", "gate_proj", "up_proj")
|
| 223 |
+
+ }
|
| 224 |
+
+ if expected.issubset(self._experts[layer_idx]):
|
| 225 |
+
+ for w_name in ["down_proj", "gate_proj", "up_proj"]:
|
| 226 |
+
+ datas = []
|
| 227 |
+
+ for xid in range(n_experts):
|
| 228 |
+
+ ename = f"model.layers.{layer_idx}.mlp.experts.{xid}.{w_name}.weight"
|
| 229 |
+
+ datas.append(self._experts[layer_idx][ename])
|
| 230 |
+
+ del self._experts[layer_idx][ename]
|
| 231 |
+
+ data_torch = torch.stack(datas, dim=0)
|
| 232 |
+
+ merged_name = f"model.layers.{layer_idx}.mlp.experts.{w_name}.weight"
|
| 233 |
+
+ yield from super().modify_tensors(data_torch, merged_name, layer_idx)
|
| 234 |
+
+ return
|
| 235 |
+
+ yield from super().modify_tensors(data_torch, name, bid)
|
| 236 |
+
+
|
| 237 |
+
+ def prepare_tensors(self):
|
| 238 |
+
+ super().prepare_tensors()
|
| 239 |
+
+ experts = [k for d in self._experts for k in d.keys()]
|
| 240 |
+
+ if len(experts) > 0:
|
| 241 |
+
+ raise ValueError(f"Unprocessed experts: {experts}")
|
| 242 |
+
+
|
| 243 |
+
+
|
| 244 |
+
@ModelBase.register("OlmoForCausalLM")
|
| 245 |
+
@ModelBase.register("OLMoForCausalLM")
|
| 246 |
+
class OlmoModel(TextModel):
|
| 247 |
+
--- a/conversion/__init__.py
|
| 248 |
+
+++ b/conversion/__init__.py
|
| 249 |
+
@@ -42,6 +42,7 @@
|
| 250 |
+
"CodeShellForCausalLM": "codeshell",
|
| 251 |
+
"CogVLMForCausalLM": "cogvlm",
|
| 252 |
+
"Cohere2ForCausalLM": "command_r",
|
| 253 |
+
+ "Cohere2MoeForCausalLM": "command_r",
|
| 254 |
+
"CohereForCausalLM": "command_r",
|
| 255 |
+
"DbrxForCausalLM": "dbrx",
|
| 256 |
+
"DeciLMForCausalLM": "deci",
|
| 257 |
+
--- a/conversion/base.py
|
| 258 |
+
+++ b/conversion/base.py
|
| 259 |
+
@@ -1505,7 +1505,7 @@
|
| 260 |
+
res = "command-r"
|
| 261 |
+
if chkhsh == "d772b220ace2baec124bed8cfafce0ead7d6c38a4b65ef11261cf9d5d62246d1":
|
| 262 |
+
# ref: https://huggingface.co/CohereLabs/tiny-aya-base
|
| 263 |
+
- res = "tiny_aya"
|
| 264 |
+
+ res = "tiny_aya" # also used by cohere2moe / North Mini
|
| 265 |
+
if chkhsh == "e636dc30a262dcc0d8c323492e32ae2b70728f4df7dfe9737d9f920a282b8aea":
|
| 266 |
+
# ref: https://huggingface.co/Qwen/Qwen1.5-7B
|
| 267 |
+
res = "qwen2"
|
| 268 |
+
--- a/src/models/cohere2moe.cpp
|
| 269 |
+
+++ b/src/models/cohere2moe.cpp
|
| 270 |
+
@@ -0,0 +1,310 @@
|
| 271 |
+
+#include "models.h"
|
| 272 |
+
+
|
| 273 |
+
+// Port of ggml-org/llama.cpp cohere2moe into ROCmFPX fork.
|
| 274 |
+
+// Adapted to fork APIs:
|
| 275 |
+
+// - hparams.n_layer field (not n_layer())
|
| 276 |
+
+// - hparams.nextn_predict_layers (not n_layer_nextn / n_layer_all)
|
| 277 |
+
+// - no embeddings_nextn_masked / t_h_nextn (fork lacks those result slots)
|
| 278 |
+
+// - SWA pattern loaded as bool array into hparams.swa_layers
|
| 279 |
+
+// Bias tensors: HF Cohere2 MoE ships per-expert FC biases, but conversion
|
| 280 |
+
+// drops them when zero (mainline convert raises if non-zero). Production
|
| 281 |
+
+// North-Mini-Code GGUF has 0 bias tensors; graph therefore passes nullptr
|
| 282 |
+
+// biases, matching mainline. Optional TENSOR_NOT_REQUIRED bias loads are
|
| 283 |
+
+// included so a future non-zero-bias GGUF can bind without recompile.
|
| 284 |
+
+
|
| 285 |
+
+void llama_model_cohere2moe::load_arch_hparams(llama_model_loader & ml) {
|
| 286 |
+
+ const bool found_norm = ml.get_key(LLM_KV_ATTENTION_LAYERNORM_EPS, hparams.f_norm_eps, false);
|
| 287 |
+
+ const bool found_norm_rms = ml.get_key(LLM_KV_ATTENTION_LAYERNORM_RMS_EPS, hparams.f_norm_rms_eps, false);
|
| 288 |
+
+ if (!found_norm && !found_norm_rms) {
|
| 289 |
+
+ throw std::runtime_error("missing Cohere2 MoE norm epsilon");
|
| 290 |
+
+ }
|
| 291 |
+
+ if (!found_norm_rms) {
|
| 292 |
+
+ hparams.f_norm_rms_eps = 0.0f;
|
| 293 |
+
+ }
|
| 294 |
+
+
|
| 295 |
+
+ ml.get_key(LLM_KV_ATTENTION_SLIDING_WINDOW, hparams.n_swa);
|
| 296 |
+
+ ml.get_key(LLM_KV_LOGIT_SCALE, hparams.f_logit_scale);
|
| 297 |
+
+ ml.get_key(LLM_KV_LEADING_DENSE_BLOCK_COUNT, hparams.n_layer_dense_lead);
|
| 298 |
+
+ ml.get_key(LLM_KV_EXPERT_FEED_FORWARD_LENGTH, hparams.n_ff_exp);
|
| 299 |
+
+ ml.get_key(LLM_KV_EXPERT_SHARED_FEED_FORWARD_LENGTH, hparams.n_ff_shexp, false);
|
| 300 |
+
+ ml.get_key(LLM_KV_EXPERT_SHARED_COUNT, hparams.n_expert_shared, false);
|
| 301 |
+
+ ml.get_key(LLM_KV_EXPERT_WEIGHTS_NORM, hparams.expert_weights_norm, false);
|
| 302 |
+
+ ml.get_key(LLM_KV_EXPERT_WEIGHTS_SCALE, hparams.expert_weights_scale, false);
|
| 303 |
+
+ ml.get_key(LLM_KV_EXPERT_GATING_FUNC, hparams.expert_gating_func, false);
|
| 304 |
+
+
|
| 305 |
+
+ ml.get_key(LLM_KV_NEXTN_PREDICT_LAYERS, hparams.nextn_predict_layers, false);
|
| 306 |
+
+ GGML_ASSERT(hparams.nextn_predict_layers < hparams.n_layer && "nextn_predict_layers must be < n_layer");
|
| 307 |
+
+
|
| 308 |
+
+ if (hparams.expert_gating_func == LLAMA_EXPERT_GATING_FUNC_TYPE_NONE) {
|
| 309 |
+
+ hparams.expert_gating_func = LLAMA_EXPERT_GATING_FUNC_TYPE_SIGMOID;
|
| 310 |
+
+ }
|
| 311 |
+
+
|
| 312 |
+
+ hparams.swa_type = LLAMA_SWA_TYPE_STANDARD;
|
| 313 |
+
+ // Prefer full per-layer bool pattern (North Mini GGUF ships this);
|
| 314 |
+
+ // fall back to period integer used by some cohere2 exports.
|
| 315 |
+
+ if (!ml.get_key_or_arr(LLM_KV_ATTENTION_SLIDING_WINDOW_PATTERN, hparams.swa_layers, hparams.n_layer, false)) {
|
| 316 |
+
+ uint32_t swa_period = 4;
|
| 317 |
+
+ if (ml.get_key_or_arr(LLM_KV_ATTENTION_SLIDING_WINDOW_PATTERN, swa_period, false)) {
|
| 318 |
+
+ hparams.set_swa_pattern(swa_period, true);
|
| 319 |
+
+ } else {
|
| 320 |
+
+ hparams.set_swa_pattern(swa_period, true);
|
| 321 |
+
+ }
|
| 322 |
+
+ }
|
| 323 |
+
+
|
| 324 |
+
+ hparams.rope_freq_base_train_swa = hparams.rope_freq_base_train;
|
| 325 |
+
+ hparams.rope_freq_scale_train_swa = hparams.rope_freq_scale_train;
|
| 326 |
+
+ ml.get_key(LLM_KV_ROPE_FREQ_BASE_SWA, hparams.rope_freq_base_train_swa, false);
|
| 327 |
+
+
|
| 328 |
+
+ // MTP layers (if any) are the last nextn_predict_layers of n_layer, same
|
| 329 |
+
+ // convention as glm4-moe in this fork.
|
| 330 |
+
+ if (hparams.nextn_predict_layers > 0) {
|
| 331 |
+
+ hparams.n_layer_kv_from_start = hparams.n_layer - hparams.nextn_predict_layers;
|
| 332 |
+
+ }
|
| 333 |
+
+
|
| 334 |
+
+ switch (hparams.n_layer) {
|
| 335 |
+
+ case 49: type = LLM_TYPE_30B_A3B; break; // North Mini Code (48 trunk + optional nextn)
|
| 336 |
+
+ default: type = LLM_TYPE_UNKNOWN;
|
| 337 |
+
+ }
|
| 338 |
+
+}
|
| 339 |
+
+
|
| 340 |
+
+void llama_model_cohere2moe::load_arch_tensors(llama_model_loader & ml) {
|
| 341 |
+
+ LLAMA_LOAD_LOCALS;
|
| 342 |
+
+
|
| 343 |
+
+ tok_embd = create_tensor(tn(LLM_TENSOR_TOKEN_EMBD, "weight"), { n_embd, n_vocab }, 0);
|
| 344 |
+
+
|
| 345 |
+
+ output_norm = create_tensor(tn(LLM_TENSOR_OUTPUT_NORM, "weight"), { n_embd }, 0);
|
| 346 |
+
+ output = create_tensor(tn(LLM_TENSOR_OUTPUT, "weight"), { n_embd, n_vocab }, TENSOR_NOT_REQUIRED);
|
| 347 |
+
+
|
| 348 |
+
+ if (output == NULL) {
|
| 349 |
+
+ output = create_tensor(tn(LLM_TENSOR_TOKEN_EMBD, "weight"), { n_embd, n_vocab }, TENSOR_DUPLICATED);
|
| 350 |
+
+ }
|
| 351 |
+
+
|
| 352 |
+
+ if (n_expert == 0) {
|
| 353 |
+
+ throw std::runtime_error("n_expert must be > 0 for Cohere2Moe");
|
| 354 |
+
+ }
|
| 355 |
+
+ if (n_expert_used == 0) {
|
| 356 |
+
+ throw std::runtime_error("n_expert_used must be > 0 for Cohere2Moe");
|
| 357 |
+
+ }
|
| 358 |
+
+
|
| 359 |
+
+ const int n_transformer_layers = n_layer - (int) hparams.nextn_predict_layers;
|
| 360 |
+
+
|
| 361 |
+
+ for (int i = 0; i < n_layer; ++i) {
|
| 362 |
+
+ auto & layer = layers[i];
|
| 363 |
+
+ int flags = 0;
|
| 364 |
+
+ const bool is_mtp = hparams.nextn_predict_layers > 0 &&
|
| 365 |
+
+ static_cast<uint32_t>(i) >= static_cast<uint32_t>(n_transformer_layers);
|
| 366 |
+
+ if (is_mtp) {
|
| 367 |
+
+ // Load MTP tensors but skip execution in the main graph (glm4-moe pattern).
|
| 368 |
+
+ flags |= TENSOR_SKIP;
|
| 369 |
+
+ }
|
| 370 |
+
+
|
| 371 |
+
+ layer.attn_norm = create_tensor(tn(LLM_TENSOR_ATTN_NORM, "weight", i), { n_embd }, flags);
|
| 372 |
+
+
|
| 373 |
+
+ // Q: n_embd x (n_embd_head_k * n_head); K/V: n_embd x n_embd_gqa
|
| 374 |
+
+ create_tensor_qkv(layer, i, n_embd, n_embd_head_k * n_head, n_embd_gqa, n_embd_gqa, flags);
|
| 375 |
+
+ layer.wo = create_tensor(tn(LLM_TENSOR_ATTN_OUT, "weight", i), { n_embd_head_k * n_head, n_embd }, flags);
|
| 376 |
+
+
|
| 377 |
+
+ // Optional attention output bias (HF may ship zeros; production GGUF has none).
|
| 378 |
+
+ layer.wo_b = create_tensor(tn(LLM_TENSOR_ATTN_OUT, "bias", i), { n_embd }, flags | TENSOR_NOT_REQUIRED);
|
| 379 |
+
+
|
| 380 |
+
+ if (!is_mtp && static_cast<uint32_t>(i) < hparams.n_layer_dense_lead) {
|
| 381 |
+
+ // Leading dense FFN (layer 0 on North Mini)
|
| 382 |
+
+ layer.ffn_gate = create_tensor(tn(LLM_TENSOR_FFN_GATE, "weight", i), { n_embd, n_ff }, flags);
|
| 383 |
+
+ layer.ffn_down = create_tensor(tn(LLM_TENSOR_FFN_DOWN, "weight", i), { n_ff, n_embd }, flags);
|
| 384 |
+
+ layer.ffn_up = create_tensor(tn(LLM_TENSOR_FFN_UP, "weight", i), { n_embd, n_ff }, flags);
|
| 385 |
+
+
|
| 386 |
+
+ // Optional dense FC biases
|
| 387 |
+
+ layer.ffn_gate_b = create_tensor(tn(LLM_TENSOR_FFN_GATE, "bias", i), { n_ff }, flags | TENSOR_NOT_REQUIRED);
|
| 388 |
+
+ layer.ffn_up_b = create_tensor(tn(LLM_TENSOR_FFN_UP, "bias", i), { n_ff }, flags | TENSOR_NOT_REQUIRED);
|
| 389 |
+
+ layer.ffn_down_b = create_tensor(tn(LLM_TENSOR_FFN_DOWN, "bias", i), { n_embd }, flags | TENSOR_NOT_REQUIRED);
|
| 390 |
+
+ } else {
|
| 391 |
+
+ const int64_t n_ff_exp = hparams.n_ff_exp ? hparams.n_ff_exp : n_ff;
|
| 392 |
+
+
|
| 393 |
+
+ layer.ffn_gate_inp = create_tensor(tn(LLM_TENSOR_FFN_GATE_INP, "weight", i), { n_embd, n_expert }, flags);
|
| 394 |
+
+ layer.ffn_down_exps = create_tensor(tn(LLM_TENSOR_FFN_DOWN_EXPS, "weight", i), { n_ff_exp, n_embd, n_expert }, flags);
|
| 395 |
+
+ create_tensor_gate_up_exps(layer, i, n_embd, n_ff_exp, n_expert, flags);
|
| 396 |
+
+
|
| 397 |
+
+ // Optional per-expert FC biases (mainline convert drops zeros; bind if present).
|
| 398 |
+
+ // Merged expert bias tensors would be named like ffn_*_exps.bias if ever exported.
|
| 399 |
+
+ layer.ffn_gate_exps_b = create_tensor(tn(LLM_TENSOR_FFN_GATE_EXPS, "bias", i), { n_ff_exp, n_expert }, flags | TENSOR_NOT_REQUIRED);
|
| 400 |
+
+ layer.ffn_up_exps_b = create_tensor(tn(LLM_TENSOR_FFN_UP_EXPS, "bias", i), { n_ff_exp, n_expert }, flags | TENSOR_NOT_REQUIRED);
|
| 401 |
+
+ layer.ffn_down_exps_b = create_tensor(tn(LLM_TENSOR_FFN_DOWN_EXPS, "bias", i), { n_embd, n_expert }, flags | TENSOR_NOT_REQUIRED);
|
| 402 |
+
+ layer.ffn_gate_inp_b = create_tensor(tn(LLM_TENSOR_FFN_GATE_INP, "bias", i), { n_expert }, flags | TENSOR_NOT_REQUIRED);
|
| 403 |
+
+
|
| 404 |
+
+ if (hparams.n_expert_shared > 0) {
|
| 405 |
+
+ const int64_t n_ff_shexp = hparams.n_ff_shexp ? hparams.n_ff_shexp : n_ff_exp * hparams.n_expert_shared;
|
| 406 |
+
+ layer.ffn_gate_shexp = create_tensor(tn(LLM_TENSOR_FFN_GATE_SHEXP, "weight", i), { n_embd, n_ff_shexp }, flags);
|
| 407 |
+
+ layer.ffn_down_shexp = create_tensor(tn(LLM_TENSOR_FFN_DOWN_SHEXP, "weight", i), { n_ff_shexp, n_embd }, flags);
|
| 408 |
+
+ layer.ffn_up_shexp = create_tensor(tn(LLM_TENSOR_FFN_UP_SHEXP, "weight", i), { n_embd, n_ff_shexp }, flags);
|
| 409 |
+
+ }
|
| 410 |
+
+ }
|
| 411 |
+
+
|
| 412 |
+
+ if (is_mtp) {
|
| 413 |
+
+ layer.nextn.eh_proj = create_tensor(tn(LLM_TENSOR_NEXTN_EH_PROJ, "weight", i), { 2 * n_embd, n_embd }, flags);
|
| 414 |
+
+ layer.nextn.enorm = create_tensor(tn(LLM_TENSOR_NEXTN_ENORM, "weight", i), { n_embd }, flags);
|
| 415 |
+
+ layer.nextn.hnorm = create_tensor(tn(LLM_TENSOR_NEXTN_HNORM, "weight", i), { n_embd }, flags);
|
| 416 |
+
+ layer.nextn.embed_tokens = create_tensor(tn(LLM_TENSOR_NEXTN_EMBED_TOKENS, "weight", i), { n_embd, n_vocab }, flags | TENSOR_NOT_REQUIRED);
|
| 417 |
+
+ layer.nextn.shared_head_head = create_tensor(tn(LLM_TENSOR_NEXTN_SHARED_HEAD_HEAD, "weight", i), { n_embd, n_vocab }, flags | TENSOR_NOT_REQUIRED);
|
| 418 |
+
+ layer.nextn.shared_head_norm = create_tensor(tn(LLM_TENSOR_NEXTN_SHARED_HEAD_NORM, "weight", i), { n_embd }, flags | TENSOR_NOT_REQUIRED);
|
| 419 |
+
+ }
|
| 420 |
+
+ }
|
| 421 |
+
+}
|
| 422 |
+
+
|
| 423 |
+
+std::unique_ptr<llm_graph_context> llama_model_cohere2moe::build_arch_graph(const llm_graph_params & params) const {
|
| 424 |
+
+ return std::make_unique<graph>(*this, params);
|
| 425 |
+
+}
|
| 426 |
+
+
|
| 427 |
+
+llama_model_cohere2moe::graph::graph(const llama_model & model, const llm_graph_params & params) : llm_graph_context(params) {
|
| 428 |
+
+ const int64_t n_embd_head = hparams.n_embd_head_v();
|
| 429 |
+
+
|
| 430 |
+
+ GGML_ASSERT(n_embd_head == hparams.n_embd_head_k());
|
| 431 |
+
+ GGML_ASSERT(n_embd_head == n_rot);
|
| 432 |
+
+
|
| 433 |
+
+ const llm_norm_type cohere2moe_norm_type = hparams.f_norm_rms_eps == 0.0f ? LLM_NORM : LLM_NORM_RMS;
|
| 434 |
+
+ const float f_logit_scale = hparams.f_logit_scale;
|
| 435 |
+
+
|
| 436 |
+
+ ggml_tensor * cur;
|
| 437 |
+
+ ggml_tensor * inpL = build_inp_embd(model.tok_embd);
|
| 438 |
+
+ ggml_tensor * inp_pos = build_inp_pos();
|
| 439 |
+
+
|
| 440 |
+
+ auto * inp_attn = build_attn_inp_kv_iswa();
|
| 441 |
+
+ ggml_tensor * inp_out_ids = build_inp_out_ids();
|
| 442 |
+
+
|
| 443 |
+
+ // Skip MTP tail layers in the main decoder pass (glm4-moe convention).
|
| 444 |
+
+ const int n_transformer_layers = n_layer - (int) hparams.nextn_predict_layers;
|
| 445 |
+
+
|
| 446 |
+
+ for (int il = 0; il < n_transformer_layers; ++il) {
|
| 447 |
+
+ const bool is_swa = hparams.is_swa(il);
|
| 448 |
+
+ // Dense-prefix full-attention layers use RoPE; later layers follow SWA pattern.
|
| 449 |
+
+ const bool force_rope = static_cast<uint32_t>(il) < hparams.n_layer_dense_lead;
|
| 450 |
+
+
|
| 451 |
+
+ cur = build_norm(inpL, model.layers[il].attn_norm, nullptr, cohere2moe_norm_type, il);
|
| 452 |
+
+ cb(cur, "attn_norm", il);
|
| 453 |
+
+
|
| 454 |
+
+ ggml_tensor * ffn_inp = cur;
|
| 455 |
+
+
|
| 456 |
+
+ {
|
| 457 |
+
+ const auto & layer = model.layers[il];
|
| 458 |
+
+
|
| 459 |
+
+ auto [Qcur, Kcur, Vcur] = build_qkv(layer, cur,
|
| 460 |
+
+ n_embd_head, n_head, n_head_kv, il);
|
| 461 |
+
+
|
| 462 |
+
+ if (is_swa || force_rope) {
|
| 463 |
+
+ ggml_tensor * rope_factors = model.get_rope_factors(cparams, il);
|
| 464 |
+
+
|
| 465 |
+
+ Qcur = ggml_rope_ext(
|
| 466 |
+
+ ctx0, Qcur, inp_pos, rope_factors,
|
| 467 |
+
+ n_rot, rope_type, n_ctx_orig, freq_base, freq_scale,
|
| 468 |
+
+ ext_factor, attn_factor, beta_fast, beta_slow);
|
| 469 |
+
+
|
| 470 |
+
+ Kcur = ggml_rope_ext(
|
| 471 |
+
+ ctx0, Kcur, inp_pos, rope_factors,
|
| 472 |
+
+ n_rot, rope_type, n_ctx_orig, freq_base, freq_scale,
|
| 473 |
+
+ ext_factor, attn_factor, beta_fast, beta_slow);
|
| 474 |
+
+ }
|
| 475 |
+
+
|
| 476 |
+
+ cb(Qcur, "Qcur", il);
|
| 477 |
+
+ cb(Kcur, "Kcur", il);
|
| 478 |
+
+ cb(Vcur, "Vcur", il);
|
| 479 |
+
+
|
| 480 |
+
+ cur = build_attn(inp_attn,
|
| 481 |
+
+ layer.wo, layer.wo_b, layer.wo_s,
|
| 482 |
+
+ Qcur, Kcur, Vcur, nullptr, nullptr, nullptr,
|
| 483 |
+
+ 1.0f / sqrtf(float(n_embd_head)), il);
|
| 484 |
+
+ }
|
| 485 |
+
+
|
| 486 |
+
+ if (il == n_transformer_layers - 1 && inp_out_ids) {
|
| 487 |
+
+ cur = ggml_get_rows(ctx0, cur, inp_out_ids);
|
| 488 |
+
+ inpL = ggml_get_rows(ctx0, inpL, inp_out_ids);
|
| 489 |
+
+ ffn_inp = ggml_get_rows(ctx0, ffn_inp, inp_out_ids);
|
| 490 |
+
+ }
|
| 491 |
+
+
|
| 492 |
+
+ ggml_tensor * attn_out = cur;
|
| 493 |
+
+
|
| 494 |
+
+ const auto & layer = model.layers[il];
|
| 495 |
+
+
|
| 496 |
+
+ if (layer.ffn_gate_inp == nullptr) {
|
| 497 |
+
+ cur = build_ffn(ffn_inp,
|
| 498 |
+
+ layer.ffn_up, layer.ffn_up_b, layer.ffn_up_s,
|
| 499 |
+
+ layer.ffn_gate, layer.ffn_gate_b, layer.ffn_gate_s,
|
| 500 |
+
+ layer.ffn_down, layer.ffn_down_b, layer.ffn_down_s,
|
| 501 |
+
+ nullptr, LLM_FFN_SILU, LLM_FFN_PAR, il);
|
| 502 |
+
+ cb(cur, "ffn_out", il);
|
| 503 |
+
+ } else {
|
| 504 |
+
+ // Prefer bias-aware overload if any expert bias is present; else weight-only path.
|
| 505 |
+
+ if (layer.ffn_up_exps_b || layer.ffn_gate_exps_b || layer.ffn_down_exps_b || layer.ffn_gate_inp_b) {
|
| 506 |
+
+ cur = build_moe_ffn(ffn_inp,
|
| 507 |
+
+ layer.ffn_gate_inp, layer.ffn_gate_inp_b,
|
| 508 |
+
+ layer.ffn_up_exps, layer.ffn_up_exps_b,
|
| 509 |
+
+ layer.ffn_gate_exps, layer.ffn_gate_exps_b,
|
| 510 |
+
+ layer.ffn_down_exps, layer.ffn_down_exps_b,
|
| 511 |
+
+ nullptr,
|
| 512 |
+
+ n_expert, n_expert_used,
|
| 513 |
+
+ LLM_FFN_SILU, hparams.expert_weights_norm,
|
| 514 |
+
+ hparams.expert_weights_scale,
|
| 515 |
+
+ (llama_expert_gating_func_type) hparams.expert_gating_func,
|
| 516 |
+
+ il,
|
| 517 |
+
+ nullptr, layer.ffn_gate_up_exps, layer.ffn_gate_up_exps_b,
|
| 518 |
+
+ layer.ffn_up_exps_s,
|
| 519 |
+
+ layer.ffn_gate_exps_s,
|
| 520 |
+
+ layer.ffn_down_exps_s);
|
| 521 |
+
+ } else {
|
| 522 |
+
+ cur = build_moe_ffn(ffn_inp,
|
| 523 |
+
+ layer.ffn_gate_inp,
|
| 524 |
+
+ layer.ffn_up_exps,
|
| 525 |
+
+ layer.ffn_gate_exps,
|
| 526 |
+
+ layer.ffn_down_exps,
|
| 527 |
+
+ nullptr,
|
| 528 |
+
+ n_expert, n_expert_used,
|
| 529 |
+
+ LLM_FFN_SILU, hparams.expert_weights_norm,
|
| 530 |
+
+ hparams.expert_weights_scale,
|
| 531 |
+
+ (llama_expert_gating_func_type) hparams.expert_gating_func,
|
| 532 |
+
+ il,
|
| 533 |
+
+ nullptr, layer.ffn_gate_up_exps,
|
| 534 |
+
+ layer.ffn_up_exps_s,
|
| 535 |
+
+ layer.ffn_gate_exps_s,
|
| 536 |
+
+ layer.ffn_down_exps_s);
|
| 537 |
+
+ }
|
| 538 |
+
+ cb(cur, "ffn_moe_out", il);
|
| 539 |
+
+
|
| 540 |
+
+ if (layer.ffn_up_shexp) {
|
| 541 |
+
+ ggml_tensor * ffn_shexp = build_ffn(ffn_inp,
|
| 542 |
+
+ layer.ffn_up_shexp, nullptr, layer.ffn_up_shexp_s,
|
| 543 |
+
+ layer.ffn_gate_shexp, nullptr, layer.ffn_gate_shexp_s,
|
| 544 |
+
+ layer.ffn_down_shexp, nullptr, layer.ffn_down_shexp_s,
|
| 545 |
+
+ nullptr, LLM_FFN_SILU, LLM_FFN_PAR, il);
|
| 546 |
+
+ cb(ffn_shexp, "ffn_shexp", il);
|
| 547 |
+
+
|
| 548 |
+
+ cur = ggml_add(ctx0, cur, ffn_shexp);
|
| 549 |
+
+ cur = ggml_scale(ctx0, cur, 0.5f);
|
| 550 |
+
+ cb(cur, "ffn_out", il);
|
| 551 |
+
+ }
|
| 552 |
+
+ }
|
| 553 |
+
+
|
| 554 |
+
+ // Parallel residual: residual + FFN + attention (Cohere-style)
|
| 555 |
+
+ cur = ggml_add(ctx0, cur, inpL);
|
| 556 |
+
+ cur = ggml_add(ctx0, cur, attn_out);
|
| 557 |
+
+
|
| 558 |
+
+ cur = build_cvec(cur, il);
|
| 559 |
+
+ cb(cur, "l_out", il);
|
| 560 |
+
+
|
| 561 |
+
+ inpL = cur;
|
| 562 |
+
+ }
|
| 563 |
+
+
|
| 564 |
+
+ cur = inpL;
|
| 565 |
+
+ cur = build_norm(cur, model.output_norm, nullptr, cohere2moe_norm_type, -1);
|
| 566 |
+
+
|
| 567 |
+
+ cb(cur, "result_norm", -1);
|
| 568 |
+
+ res->t_embd = cur;
|
| 569 |
+
+
|
| 570 |
+
+ cur = build_lora_mm(model.output, cur);
|
| 571 |
+
+
|
| 572 |
+
+ if (f_logit_scale) {
|
| 573 |
+
+ cur = ggml_scale(ctx0, cur, f_logit_scale);
|
| 574 |
+
+ }
|
| 575 |
+
+
|
| 576 |
+
+ cb(cur, "result_output", -1);
|
| 577 |
+
+ res->t_logits = cur;
|
| 578 |
+
+
|
| 579 |
+
+ ggml_build_forward_expand(gf, cur);
|
| 580 |
+
+}
|
| 581 |
+
--- a/conversion/command_r.py
|
| 582 |
+
+++ b/conversion/command_r.py
|
| 583 |
+
@@ -0,0 +1,177 @@
|
| 584 |
+
+from __future__ import annotations
|
| 585 |
+
+
|
| 586 |
+
+import re
|
| 587 |
+
+from typing import Iterable, TYPE_CHECKING
|
| 588 |
+
+
|
| 589 |
+
+import torch
|
| 590 |
+
+
|
| 591 |
+
+if TYPE_CHECKING:
|
| 592 |
+
+ from torch import Tensor
|
| 593 |
+
+
|
| 594 |
+
+from .base import ModelBase, TextModel, gguf, logger
|
| 595 |
+
+
|
| 596 |
+
+
|
| 597 |
+
+@ModelBase.register("CohereForCausalLM")
|
| 598 |
+
+class CommandR2Model(TextModel):
|
| 599 |
+
+ model_arch = gguf.MODEL_ARCH.COMMAND_R
|
| 600 |
+
+
|
| 601 |
+
+ def __init__(self, *args, **kwargs):
|
| 602 |
+
+ super().__init__(*args, **kwargs)
|
| 603 |
+
+
|
| 604 |
+
+ # max_position_embeddings = 8192 in config.json but model was actually
|
| 605 |
+
+ # trained on 128k context length
|
| 606 |
+
+ # aya-23 models don't have model_max_length specified
|
| 607 |
+
+ self.hparams["max_position_embeddings"] = self.find_hparam(["model_max_length", "max_position_embeddings"])
|
| 608 |
+
+
|
| 609 |
+
+ def set_gguf_parameters(self):
|
| 610 |
+
+ super().set_gguf_parameters()
|
| 611 |
+
+ self.gguf_writer.add_logit_scale(self.hparams["logit_scale"])
|
| 612 |
+
+ self.gguf_writer.add_rope_scaling_type(gguf.RopeScalingType.NONE)
|
| 613 |
+
+
|
| 614 |
+
+
|
| 615 |
+
+@ModelBase.register("Cohere2ForCausalLM")
|
| 616 |
+
+class Cohere2Model(TextModel):
|
| 617 |
+
+ model_arch = gguf.MODEL_ARCH.COHERE2
|
| 618 |
+
+
|
| 619 |
+
+ def set_gguf_parameters(self):
|
| 620 |
+
+ super().set_gguf_parameters()
|
| 621 |
+
+
|
| 622 |
+
+ self.gguf_writer.add_logit_scale(self.hparams["logit_scale"])
|
| 623 |
+
+ self.gguf_writer.add_sliding_window(self.hparams["sliding_window"])
|
| 624 |
+
+ self.gguf_writer.add_vocab_size(self.hparams["vocab_size"])
|
| 625 |
+
+
|
| 626 |
+
+ rotary_pct = self.hparams["rotary_pct"]
|
| 627 |
+
+ hidden_size = self.hparams["hidden_size"]
|
| 628 |
+
+ num_attention_heads = self.hparams["num_attention_heads"]
|
| 629 |
+
+ self.gguf_writer.add_rope_dimension_count(int(rotary_pct * (hidden_size // num_attention_heads)))
|
| 630 |
+
+ self.gguf_writer.add_rope_scaling_type(gguf.RopeScalingType.NONE)
|
| 631 |
+
+
|
| 632 |
+
+ def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]:
|
| 633 |
+
+ # Cohere2 runtime in llama.cpp expects no bias tensors;
|
| 634 |
+
+ # the actual weight only contains 0-value tensors as bias, we can skip them
|
| 635 |
+
+ if name.endswith(".bias"):
|
| 636 |
+
+ if torch.any(data_torch != 0):
|
| 637 |
+
+ raise ValueError(f"Bias tensor {name!r} is not zero.")
|
| 638 |
+
+ logger.debug(f"Skipping bias tensor {name!r} for Cohere2 conversion.")
|
| 639 |
+
+ return
|
| 640 |
+
+
|
| 641 |
+
+ yield from super().modify_tensors(data_torch, name, bid)
|
| 642 |
+
+
|
| 643 |
+
+
|
| 644 |
+
+@ModelBase.register("Cohere2MoeForCausalLM")
|
| 645 |
+
+class Cohere2MoeModel(TextModel):
|
| 646 |
+
+ model_arch = gguf.MODEL_ARCH.COHERE2MOE
|
| 647 |
+
+ _n_main_layers: int | None = None
|
| 648 |
+
+ _expert_tensor_re = re.compile(
|
| 649 |
+
+ r"model\.layers\.(\d+)\.mlp\.experts\.(\d+)\.(down_proj|gate_proj|up_proj)\.weight"
|
| 650 |
+
+ )
|
| 651 |
+
+
|
| 652 |
+
+ def __init__(self, *args, **kwargs):
|
| 653 |
+
+ super().__init__(*args, **kwargs)
|
| 654 |
+
+ if (n_nextn := int(self.hparams.get("num_nextn_predict_layers", 0) or 0)) > 0 and not self.no_mtp:
|
| 655 |
+
+ self.block_count += n_nextn
|
| 656 |
+
+ self.tensor_map = gguf.get_tensor_name_map(self.model_arch, self.block_count)
|
| 657 |
+
+ self._experts: list[dict[str, Tensor]] = [{} for _ in range(self.block_count)]
|
| 658 |
+
+
|
| 659 |
+
+ def _set_vocab_gpt2(self) -> None:
|
| 660 |
+
+ tokens, toktypes, tokpre = self.get_vocab_base()
|
| 661 |
+
+ self.gguf_writer.add_tokenizer_model("gpt2")
|
| 662 |
+
+ self.gguf_writer.add_tokenizer_pre(tokpre)
|
| 663 |
+
+ self.gguf_writer.add_token_list(tokens)
|
| 664 |
+
+ self.gguf_writer.add_token_types(toktypes)
|
| 665 |
+
+
|
| 666 |
+
+ special_vocab = gguf.SpecialVocab(self.dir_model, load_merges=True)
|
| 667 |
+
+ special_vocab.add_to_gguf(self.gguf_writer)
|
| 668 |
+
+
|
| 669 |
+
+ def set_gguf_parameters(self):
|
| 670 |
+
+ hparams = self.hparams
|
| 671 |
+
+ expert_intermediate_size = hparams["intermediate_size"]
|
| 672 |
+
+ mlp_layer_types = hparams.get("mlp_layer_types")
|
| 673 |
+
+ n_dense_lead = hparams.get("first_k_dense_replace", 0)
|
| 674 |
+
+ if mlp_layer_types is not None:
|
| 675 |
+
+ n_dense_lead = next((i for i, t in enumerate(mlp_layer_types) if t != "dense"), len(mlp_layer_types))
|
| 676 |
+
+
|
| 677 |
+
+ super().set_gguf_parameters()
|
| 678 |
+
+
|
| 679 |
+
+ self.gguf_writer.add_logit_scale(hparams["logit_scale"])
|
| 680 |
+
+ self.gguf_writer.add_sliding_window(hparams["sliding_window"])
|
| 681 |
+
+ self.gguf_writer.add_sliding_window_pattern([t == "sliding_attention" for t in hparams["layer_types"]])
|
| 682 |
+
+ self.gguf_writer.add_vocab_size(hparams["vocab_size"])
|
| 683 |
+
+ self.gguf_writer.add_expert_feed_forward_length(expert_intermediate_size)
|
| 684 |
+
+ self.gguf_writer.add_leading_dense_block_count(n_dense_lead)
|
| 685 |
+
+ self.gguf_writer.add_expert_weights_norm(hparams.get("norm_topk_prob", False))
|
| 686 |
+
+ if (num_shared_experts := hparams.get("num_shared_experts", 0)) > 0:
|
| 687 |
+
+ if hparams.get("shared_expert_combination_strategy", "average") != "average":
|
| 688 |
+
+ raise ValueError("Cohere2 MoE only supports average shared expert combination")
|
| 689 |
+
+ self.gguf_writer.add_expert_shared_count(num_shared_experts)
|
| 690 |
+
+ self.gguf_writer.add_expert_shared_feed_forward_length(expert_intermediate_size * num_shared_experts)
|
| 691 |
+
+ if (n_nextn := hparams.get("num_nextn_predict_layers", 0)) > 0 and not self.no_mtp:
|
| 692 |
+
+ self.gguf_writer.add_nextn_predict_layers(n_nextn)
|
| 693 |
+
+ self.gguf_writer.add_rope_dimension_count(hparams["head_dim"])
|
| 694 |
+
+ self.gguf_writer.add_rope_scaling_type(gguf.RopeScalingType.NONE)
|
| 695 |
+
+
|
| 696 |
+
+ def index_tensors(self, remote_hf_model_id: str | None = None):
|
| 697 |
+
+ hparams = {**self.hparams, **self.hparams.get("text_config", {})}
|
| 698 |
+
+ self._n_main_layers = hparams.get("num_hidden_layers")
|
| 699 |
+
+ type(self)._n_main_layers = self._n_main_layers
|
| 700 |
+
+ return super().index_tensors(remote_hf_model_id=remote_hf_model_id)
|
| 701 |
+
+
|
| 702 |
+
+ @classmethod
|
| 703 |
+
+ def filter_tensors(cls, item):
|
| 704 |
+
+ if (titem := super().filter_tensors(item)) is None:
|
| 705 |
+
+ return None
|
| 706 |
+
+ name, gen = titem
|
| 707 |
+
+
|
| 708 |
+
+ if cls._n_main_layers is not None:
|
| 709 |
+
+ is_mtp = (m := re.match(r"model\.layers\.(\d+)\.", name)) is not None and int(m.group(1)) >= cls._n_main_layers
|
| 710 |
+
+ if is_mtp and cls.no_mtp:
|
| 711 |
+
+ return None
|
| 712 |
+
+ if cls.mtp_only and not is_mtp and name not in (
|
| 713 |
+
+ "model.embed_tokens.weight", "model.norm.weight", "lm_head.weight",
|
| 714 |
+
+ ):
|
| 715 |
+
+ return None
|
| 716 |
+
+
|
| 717 |
+
+ return name, gen
|
| 718 |
+
+
|
| 719 |
+
+ def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]:
|
| 720 |
+
+ if name.endswith(".bias"):
|
| 721 |
+
+ if torch.any(data_torch != 0):
|
| 722 |
+
+ raise ValueError(f"Bias tensor {name!r} is not zero.")
|
| 723 |
+
+ logger.debug(f"Skipping bias tensor {name!r}.")
|
| 724 |
+
+ return
|
| 725 |
+
+
|
| 726 |
+
+ if (m := self._expert_tensor_re.fullmatch(name)) is not None:
|
| 727 |
+
+ n_experts = self.hparams["num_experts"]
|
| 728 |
+
+ layer_idx = int(m.group(1))
|
| 729 |
+
+ assert bid is None or bid == layer_idx
|
| 730 |
+
+
|
| 731 |
+
+ self._experts[layer_idx][name] = data_torch
|
| 732 |
+
+
|
| 733 |
+
+ expected = {
|
| 734 |
+
+ f"model.layers.{layer_idx}.mlp.experts.{xid}.{w_name}.weight"
|
| 735 |
+
+ for xid in range(n_experts)
|
| 736 |
+
+ for w_name in ("down_proj", "gate_proj", "up_proj")
|
| 737 |
+
+ }
|
| 738 |
+
+ if expected.issubset(self._experts[layer_idx]):
|
| 739 |
+
+ for w_name in ["down_proj", "gate_proj", "up_proj"]:
|
| 740 |
+
+ datas: list[Tensor] = []
|
| 741 |
+
+
|
| 742 |
+
+ for xid in range(n_experts):
|
| 743 |
+
+ ename = f"model.layers.{layer_idx}.mlp.experts.{xid}.{w_name}.weight"
|
| 744 |
+
+ datas.append(self._experts[layer_idx][ename])
|
| 745 |
+
+ del self._experts[layer_idx][ename]
|
| 746 |
+
+
|
| 747 |
+
+ data_torch = torch.stack(datas, dim=0)
|
| 748 |
+
+ merged_name = f"model.layers.{layer_idx}.mlp.experts.{w_name}.weight"
|
| 749 |
+
+
|
| 750 |
+
+ yield from super().modify_tensors(data_torch, merged_name, layer_idx)
|
| 751 |
+
+ return
|
| 752 |
+
+
|
| 753 |
+
+ yield from super().modify_tensors(data_torch, name, bid)
|
| 754 |
+
+
|
| 755 |
+
+ def prepare_tensors(self):
|
| 756 |
+
+ super().prepare_tensors()
|
| 757 |
+
+
|
| 758 |
+
+ experts = [k for d in self._experts for k in d.keys()]
|
| 759 |
+
+ if len(experts) > 0:
|
| 760 |
+
+ raise ValueError(f"Unprocessed experts: {experts}")
|