|
7 | 7 | // .agents/moe-semantics.md (§1-§6 MoE block + activated-expert gather). |
8 | 8 | #include "vllm/model_executor/models/qwen3_5.h" |
9 | 9 |
|
| 10 | +#include "vllm/model_executor/models/qwen3_5_dense.h" |
| 11 | + |
10 | 12 | #include <algorithm> |
11 | 13 | #include <cmath> |
12 | 14 | #include <cstdlib> |
@@ -1089,6 +1091,46 @@ void RunLayer(Dev d, const Qwen3_5MoeLayerWeights& layer, const HfConfig& cfg, |
1089 | 1091 | hidden = MoeBlock(d, layer.moe, cfg, dh2.t(), T); |
1090 | 1092 | } |
1091 | 1093 |
|
| 1094 | +// --- Dense SwiGLU MLP block (the 27B's replacement for the MoE block; notes |
| 1095 | +// §2). down( silu(gate(x)) * up(x) ), intermediate = cfg.intermediate_size. |
| 1096 | +// Mirrors the shared-expert silu-mul MLP (no router, no expert gather, no output |
| 1097 | +// gate). h [T,H] bf16 (device) -> DBuf [T,H] bf16 (device). Reused by the dense |
| 1098 | +// forward below; the gate/up/down weights are W4A4-materialized-to-bf16 at load. |
| 1099 | +DBuf DenseMlpBlock(Dev d, const DenseMlpWeights& w, const HfConfig& cfg, |
| 1100 | + const Tensor& dh, int64_t T) { |
| 1101 | + const int64_t I = cfg.intermediate_size; |
| 1102 | + DBuf gate = MatmulF32D(d, dh, w.gate_proj); // [T,I] f32 |
| 1103 | + DBuf up = MatmulF32D(d, dh, w.up_proj); // [T,I] f32 |
| 1104 | + DBuf act(d, DType::kBF16, {T, I}); |
| 1105 | + vt::MoeSiluMul(d.q, act.t(), gate.t(), up.t()); // silu(gate)*up -> bf16 |
| 1106 | + return MatmulBf16D(d, act.t(), w.down_proj); // [T,H] bf16 |
| 1107 | +} |
| 1108 | + |
| 1109 | +// One dense decoder layer (notes §2). Same residual/norm thread as RunLayer, but |
| 1110 | +// the MoE block is swapped for the dense SwiGLU MLP; the GDN / full-attention |
| 1111 | +// blocks are the 35B helpers reused verbatim. `hidden` (bf16 [T,H]) is the delta; |
| 1112 | +// `res` (f32 [T,H]) the accumulator. |
| 1113 | +void RunDenseLayer(Dev d, const Qwen3_5DenseLayerWeights& layer, |
| 1114 | + const HfConfig& cfg, DBuf& hidden, DBuf& res, |
| 1115 | + const std::vector<int32_t>& positions, int64_t T) { |
| 1116 | + const int64_t H = cfg.hidden_size; |
| 1117 | + const float eps = static_cast<float>(cfg.rms_norm_eps); |
| 1118 | + |
| 1119 | + Tensor dw_in = ResidentWeight(d, layer.input_layernorm, {H}); |
| 1120 | + DBuf dhn(d, DType::kBF16, {T, H}); |
| 1121 | + vt::RmsNorm(d.q, dhn.t(), hidden.t(), dw_in, vt::RmsNormArgs{eps, true}, &res.t()); |
| 1122 | + |
| 1123 | + DBuf attn = layer.is_linear_attention |
| 1124 | + ? GdnBlock(d, layer.gdn, cfg, dhn.t(), T) |
| 1125 | + : FullAttnBlock(d, layer.attn, cfg, dhn.t(), positions, T); |
| 1126 | + |
| 1127 | + Tensor dw_post = ResidentWeight(d, layer.post_attention_layernorm, {H}); |
| 1128 | + DBuf dh2(d, DType::kBF16, {T, H}); |
| 1129 | + vt::RmsNorm(d.q, dh2.t(), attn.t(), dw_post, vt::RmsNormArgs{eps, true}, &res.t()); |
| 1130 | + |
| 1131 | + hidden = DenseMlpBlock(d, layer.mlp, cfg, dh2.t(), T); |
| 1132 | +} |
| 1133 | + |
1092 | 1134 | // Batched PAGED decoder layer (M1.8 Task 3). Same residual/norm/MoE thread as |
1093 | 1135 | // RunLayer, but the attention block reads/writes the paged KV cache |
1094 | 1136 | // (full-attn: attn_kv) or the persistent GDN mamba state (GDN: gdn_state). |
@@ -1299,6 +1341,49 @@ std::vector<float> Qwen3_5Model::ForwardDense(const std::vector<int32_t>& token_ |
1299 | 1341 | return logits; |
1300 | 1342 | } |
1301 | 1343 |
|
| 1344 | +std::vector<float> Qwen3_5DenseModel::ForwardDense( |
| 1345 | + const std::vector<int32_t>& token_ids, const std::vector<int32_t>& positions, |
| 1346 | + const Qwen3_5DenseWeights& weights, const HfConfig& config, |
| 1347 | + vt::Queue& queue) { |
| 1348 | + const int64_t T = static_cast<int64_t>(token_ids.size()); |
| 1349 | + const int64_t H = config.hidden_size; |
| 1350 | + const int64_t vocab = config.vocab_size; |
| 1351 | + VT_CHECK(T > 0, "qwen3_5 dense forward: empty token_ids"); |
| 1352 | + VT_CHECK(static_cast<int64_t>(positions.size()) == T, |
| 1353 | + "qwen3_5 dense forward: positions length must equal token count"); |
| 1354 | + VT_CHECK(static_cast<int64_t>(weights.layers.size()) == config.num_hidden_layers, |
| 1355 | + "qwen3_5 dense forward: weights.layers size must equal num_hidden_layers"); |
| 1356 | + Dev d{vt::GetBackend(queue.device.type), queue}; |
| 1357 | + const float eps = static_cast<float>(config.rms_norm_eps); |
| 1358 | + |
| 1359 | + // Embed: hidden = embed_tokens[token_ids] (bf16, device-resident). res = 0. |
| 1360 | + // For a TEXT-only step the three mRoPE position streams are identical, so the |
| 1361 | + // partial NeoX RoPE in FullAttnBlock degenerates to 1-D RoPE over `positions` |
| 1362 | + // (notes §2). The vision tower / image-video merger are DEFERRED. |
| 1363 | + Tensor dtab = ResidentWeight(d, weights.embed_tokens, {vocab, H}); |
| 1364 | + DBuf dids(d, DType::kI32, {T}, token_ids.data()); |
| 1365 | + DBuf hidden(d, DType::kBF16, {T, H}); |
| 1366 | + vt::Embedding(d.q, hidden.t(), dtab, dids.t()); |
| 1367 | + |
| 1368 | + DBuf res(d, DType::kF32, {T, H}); |
| 1369 | + res.Zero(d); |
| 1370 | + |
| 1371 | + for (int64_t l = 0; l < config.num_hidden_layers; ++l) |
| 1372 | + RunDenseLayer(d, weights.layers[static_cast<size_t>(l)], config, hidden, res, |
| 1373 | + positions, T); |
| 1374 | + |
| 1375 | + // Final RMSNorm over the fused stream (res += hidden; norm), then lm_head. |
| 1376 | + Tensor dfn = ResidentWeight(d, weights.final_norm, {H}); |
| 1377 | + DBuf dnorm(d, DType::kBF16, {T, H}); |
| 1378 | + vt::RmsNorm(d.q, dnorm.t(), hidden.t(), dfn, vt::RmsNormArgs{eps, true}, &res.t()); |
| 1379 | + |
| 1380 | + // lm_head is unquantized bf16 in the 27B (notes §3.6): the one host Download. |
| 1381 | + DBuf dlogits = MatmulF32D(d, dnorm.t(), weights.lm_head); |
| 1382 | + std::vector<float> logits(static_cast<size_t>(T) * vocab); |
| 1383 | + dlogits.Download(d, logits.data()); |
| 1384 | + return logits; |
| 1385 | +} |
| 1386 | + |
1302 | 1387 | std::vector<float> Qwen3_5ReplayLayer(const Qwen3_5MoeLayerWeights& layer, |
1303 | 1388 | const HfConfig& config, |
1304 | 1389 | const std::vector<float>& hidden_in, |
|
0 commit comments