char-fork/llama.cpp

git clone https://git.t4t.associates/char-fork/llama.cpp

Stephen Coxmtmd: add Gemma 4 audio conformer encoder support (#21421)547765a93

char/aliceai-t5-moe
12.6 KiB288 linesraw
1/**
2 * Gemma 4 Audio Conformer Encoder (clip_graph_gemma4a)
3 *
4 * Architecture: Conformer with dual half-step FFN, full self-attention
5 * with sinusoidal RPE, depthwise light conv, and output projection.
6 */
7
8#include "models.h"
9#include <cmath>
10
11ggml_cgraph * clip_graph_gemma4a::build() {
12    const float res_weight = 0.5f;
13    const float norm_eps   = 1e-6f;
14
15    // 1. Input
16    ggml_tensor * inp = build_inp_raw(1);
17    auto * cur = ggml_cont(ctx0, ggml_transpose(ctx0, inp));
18
19    // 2. Subsampling Conv2D (symmetric padding=1, matching PyTorch)
20    {
21        for (int i = 0; i < 2; i++) {
22            cur = ggml_conv_2d(ctx0, model.sscp_conv_w[i], cur, 2, 2, 1, 1, 1, 1);
23            if (model.sscp_conv_b[i]) {
24                cur = ggml_add(ctx0, cur, model.sscp_conv_b[i]);
25            }
26            // nn.LayerNorm(channels): permute ch to ne[0], normalize, permute back
27            if (model.sscp_norm_w[i]) {
28                cur = ggml_cont(ctx0, ggml_permute(ctx0, cur, 1, 2, 0, 3));
29                cur = ggml_norm(ctx0, cur, norm_eps);
30                cur = ggml_mul(ctx0, cur, model.sscp_norm_w[i]);
31                cur = ggml_cont(ctx0, ggml_permute(ctx0, cur, 2, 0, 1, 3));
32            }
33            cur = ggml_relu(ctx0, cur);
34        }
35        // Flatten [freq, time, ch, 1] -> [ch*freq, time]
36        cur = ggml_cont(ctx0, ggml_permute(ctx0, cur, 1, 2, 0, 3));
37        cur = ggml_reshape_2d(ctx0, cur, cur->ne[0] * cur->ne[1], cur->ne[2]);
38        if (model.sscp_inp_proj_w) {
39            cur = build_mm(model.sscp_inp_proj_w, cur);
40            if (model.sscp_inp_proj_b) {
41                cur = ggml_add(ctx0, cur, model.sscp_inp_proj_b);
42            }
43        }
44    }
45
46    const int64_t n_pos = cur->ne[1];
47
48    // Chunked local attention parameters
49    const int64_t C  = 12;                              // chunk_size
50    const int64_t P  = 12;                              // max_past_horizon (context_left - 1)
51    const int64_t S  = C + P;                           // context_size = 24
52    const int64_t R  = P + 1;                           // RPE positions = 13
53    const int64_t B  = (n_pos + C - 1) / C;            // num_blocks
54    const int64_t Np = B * C;                           // padded sequence length
55    const int64_t pad_seq = Np - n_pos;
56
57    // Input tensors: blocked RPE and blocked attention mask
58    ggml_tensor * pos_emb = ggml_new_tensor_2d(ctx0, GGML_TYPE_F32, n_head * d_head, R);
59    ggml_set_name(pos_emb, "pos_emb");
60    ggml_set_input(pos_emb);
61
62    ggml_tensor * kq_mask = ggml_new_tensor_3d(ctx0, GGML_TYPE_F32, S, C, B);
63    ggml_set_name(kq_mask, "kq_mask");
64    ggml_set_input(kq_mask);
65
66    // 3. Conformer Blocks
67    for (int il = 0; il < hparams.n_layer; il++) {
68        const auto & layer = model.layers[il];
69        auto * residual = cur;
70
71        // FFN 1 (half-step)
72        if (layer.ff_norm_w && layer.ff_up_w && layer.ff_down_w) {
73            cur = build_norm(cur, layer.ff_norm_w, nullptr, NORM_TYPE_RMS, norm_eps, il);
74            cur = build_ffn(cur,
75                layer.ff_up_w, nullptr, nullptr, nullptr,
76                layer.ff_down_w, nullptr, FFN_SILU, il);
77            if (layer.ff_post_norm_w) {
78                cur = build_norm(cur, layer.ff_post_norm_w, nullptr, NORM_TYPE_RMS, norm_eps, il);
79            }
80            residual = ggml_add(ctx0, residual, ggml_scale(ctx0, cur, res_weight));
81        }
82
83        // Chunked local self-attention with RPE
84        if (layer.q_w && layer.k_w && layer.v_w && layer.o_w) {
85            const float q_scale = (1.0f / sqrtf((float)d_head)) / logf(2.0f);
86            const float k_scale = logf(1.0f + expf(1.0f)) / logf(2.0f);
87            const float softcap = 50.0f;
88
89            ggml_tensor * attn_norm_w = layer.attn_pre_norm_w ? layer.attn_pre_norm_w : layer.ln_1_w;
90            cur = attn_norm_w
91                ? build_norm(residual, attn_norm_w, nullptr, NORM_TYPE_RMS, norm_eps, il)
92                : residual;
93
94            ggml_tensor * Qcur = build_mm(layer.q_w, cur);
95            ggml_tensor * Kcur = build_mm(layer.k_w, cur);
96            ggml_tensor * Vcur = build_mm(layer.v_w, cur);
97
98            // [n_embd, n_pos] -> [D, H, N]
99            Qcur = ggml_reshape_3d(ctx0, Qcur, d_head, n_head, n_pos);
100            Kcur = ggml_reshape_3d(ctx0, Kcur, d_head, n_head, n_pos);
101            Vcur = ggml_reshape_3d(ctx0, Vcur, d_head, n_head, n_pos);
102
103            // Q/K scaling
104            Qcur = ggml_scale(ctx0, Qcur, q_scale);
105            if (layer.per_dim_scale_w) {
106                Qcur = ggml_mul(ctx0, Qcur, ggml_reshape_3d(ctx0, layer.per_dim_scale_w, d_head, 1, 1));
107            }
108            Kcur = ggml_scale(ctx0, Kcur, k_scale);
109            if (layer.per_dim_k_scale_w) {
110                Kcur = ggml_mul(ctx0, Kcur, ggml_reshape_3d(ctx0, layer.per_dim_k_scale_w, d_head, 1, 1));
111            }
112
113            // Q blocking: [D, H, N] -> pad to Np -> reshape [D, H, C, B]
114            // ggml permute: ne[ax_i] = src->ne[i], so (0,3,1,2) sends H->3, C->1, B->2
115            Qcur = ggml_pad(ctx0, Qcur, 0, 0, pad_seq, 0);          // [D, H, Np]
116            Qcur = ggml_reshape_4d(ctx0, Qcur, d_head, n_head, C, B); // [D, H, C, B]
117            Qcur = ggml_cont(ctx0, ggml_permute(ctx0, Qcur, 0, 3, 1, 2)); // [D, C, B, H]
118
119            // K/V block context extraction via overlapping view:
120            // Pad to S*B elements, roll right by P to create left-padding,
121            // then view with stride C in the block dimension (overlapping windows).
122            auto extract_blocks = [&](ggml_tensor * t) -> ggml_tensor * {
123                // [D, H, N] -> pad to S*B -> roll right by P -> cont (materialize)
124                const int64_t pad_kv = S * B - n_pos;
125                t = ggml_pad(ctx0, t, 0, 0, pad_kv, 0);     // [D, H, S*B]
126                t = ggml_roll(ctx0, t, 0, 0, P, 0);          // left-pad by P
127                t = ggml_cont(ctx0, t);                       // materialize roll (removes view offset)
128                // Overlapping view: stride for B dim is C positions, not S
129                // ne = [D, H, S, B], data_size = D*H*S*B*sizeof = source_nbytes (exact fit)
130                // nb1=D*sizeof, nb2=D*H*sizeof, nb3=C*D*H*sizeof (overlap: C < S)
131                t = ggml_view_4d(ctx0, t, d_head, n_head, S, B,
132                    t->nb[1], t->nb[2], C * t->nb[2], 0);
133                t = ggml_cont(ctx0, t);                       // materialize overlapping windows
134                return t;
135            };
136
137            ggml_tensor * Kblk = extract_blocks(Kcur);
138            // [D, H, S, B] -> [D, S, B, H] via permute(0,3,1,2)
139            Kblk = ggml_cont(ctx0, ggml_permute(ctx0, Kblk, 0, 3, 1, 2));
140
141            ggml_tensor * Vblk = extract_blocks(Vcur);
142            // [D, H, S, B] -> [S, D, B, H] via permute(1,3,0,2)
143            Vblk = ggml_cont(ctx0, ggml_permute(ctx0, Vblk, 1, 3, 0, 2));
144
145            // Content attention: Q @ K^T
146            // Kblk=[D,S,B,H], Qcur=[D,C,B,H] -> mul_mat contracts on D -> [S,C,B,H]
147            ggml_tensor * matrix_ac = ggml_mul_mat(ctx0, Kblk, Qcur);
148
149            // Relative position attention
150            if (layer.attn_k_rel_w) {
151                // RPE: [n_embd, R] -> project -> [D, H, R] -> [D, R, H]
152                auto * p = ggml_mul_mat(ctx0, layer.attn_k_rel_w, pos_emb);
153                p = ggml_reshape_3d(ctx0, p, d_head, n_head, R);
154                p = ggml_cont(ctx0, ggml_permute(ctx0, p, 0, 2, 1, 3)); // [D, R, H]
155
156                // Q_flat @ RPE^T: [D, C*B, H] @ [D, R, H] -> [R, C*B, H]
157                auto * Q_flat = ggml_reshape_3d(ctx0, Qcur, d_head, C * B, n_head);
158                auto * matrix_bd = ggml_mul_mat(ctx0, p, Q_flat);       // [R, C*B, H]
159                matrix_bd = ggml_reshape_4d(ctx0, matrix_bd, R, C, B, n_head); // [R, C, B, H]
160
161                // Blocked relative shift (appendix B of Transformer-XL)
162                {
163                    matrix_bd = ggml_pad(ctx0, matrix_bd, S + 1 - R, 0, 0, 0); // [S+1, C, B, H]
164                    matrix_bd = ggml_reshape_3d(ctx0, matrix_bd, (S + 1) * C, B, n_head);
165                    matrix_bd = ggml_view_3d(ctx0, matrix_bd,
166                        C * S, B, n_head,
167                        matrix_bd->nb[1], matrix_bd->nb[2], 0);
168                    matrix_bd = ggml_cont(ctx0, matrix_bd);              // [C*S, B, H]
169                    matrix_bd = ggml_reshape_4d(ctx0, matrix_bd, S, C, B, n_head); // [S, C, B, H]
170                }
171
172                matrix_ac = ggml_add(ctx0, matrix_ac, matrix_bd);
173            }
174
175            auto * scores = matrix_ac; // [S, C, B, H]
176
177            // Softcap
178            scores = ggml_scale(ctx0, scores, 1.0f / softcap);
179            scores = ggml_tanh(ctx0, scores);
180            scores = ggml_scale(ctx0, scores, softcap);
181
182            // Blocked attention mask: [S, C, B] broadcasts over H
183            scores = ggml_add(ctx0, scores, kq_mask);
184
185            ggml_tensor * attn = ggml_soft_max(ctx0, scores);
186
187            // attn @ V: [S,C,B,H] @ [S,D,B,H] -> [D,C,B,H]
188            ggml_tensor * x = ggml_mul_mat(ctx0, Vblk, attn);
189
190            // [D,C,B,H] -> [D,H,C,B] via permute(0,2,3,1) -> flatten -> trim
191            x = ggml_cont(ctx0, ggml_permute(ctx0, x, 0, 2, 3, 1));
192            x = ggml_cont_2d(ctx0, x, d_head * n_head, C * B);
193            if (pad_seq > 0) {
194                x = ggml_view_2d(ctx0, x, d_head * n_head, n_pos, x->nb[1], 0);
195                x = ggml_cont(ctx0, x);
196            }
197
198            x = build_mm(layer.o_w, x);
199            if (layer.o_b) { x = ggml_add(ctx0, x, layer.o_b); }
200
201            if (layer.attn_post_norm_w) {
202                x = build_norm(x, layer.attn_post_norm_w, nullptr, NORM_TYPE_RMS, norm_eps, il);
203            }
204            residual = ggml_add(ctx0, residual, x);
205        }
206
207        // Convolution Module
208        if (layer.norm_conv_w && layer.conv_pw1_w && layer.conv_dw_w && layer.conv_pw2_w) {
209            cur = build_norm(residual, layer.norm_conv_w, nullptr, NORM_TYPE_RMS, norm_eps, il);
210            auto * x = build_mm(layer.conv_pw1_w, cur);
211
212            // GLU
213            {
214                int64_t d = x->ne[0] / 2;
215                ggml_tensor * gate = ggml_sigmoid(ctx0,
216                    ggml_cont(ctx0, ggml_view_2d(ctx0, x, d, x->ne[1], x->nb[1], d * x->nb[0])));
217                x = ggml_mul(ctx0,
218                    ggml_view_2d(ctx0, x, d, x->ne[1], x->nb[1], 0), gate);
219                x = ggml_cont(ctx0, ggml_transpose(ctx0, x));
220            }
221
222            // Causal depthwise Conv1D via ggml_ssm_conv (pad+roll for left-only padding).
223            x = ggml_pad(ctx0, x, 4, 0, 0, 0);
224            x = ggml_roll(ctx0, x, 4, 0, 0, 0);
225            x = ggml_ssm_conv(ctx0, x, layer.conv_dw_w);
226            if (layer.conv_dw_b) {
227                x = ggml_add(ctx0, x, layer.conv_dw_b);
228            }
229
230            if (layer.conv_norm_w) {
231                x = ggml_rms_norm(ctx0, x, norm_eps);
232                x = ggml_mul(ctx0, x, layer.conv_norm_w);
233            }
234            x = ggml_silu(ctx0, x);
235            x = build_mm(layer.conv_pw2_w, x);
236            residual = ggml_add(ctx0, residual, x);
237        }
238
239        // FFN 2 (half-step)
240        if (layer.ff_norm_1_w && layer.ff_up_1_w && layer.ff_down_1_w) {
241            cur = build_norm(residual, layer.ff_norm_1_w, nullptr, NORM_TYPE_RMS, norm_eps, il);
242            cur = build_ffn(cur,
243                layer.ff_up_1_w, nullptr, nullptr, nullptr,
244                layer.ff_down_1_w, nullptr, FFN_SILU, il);
245            if (layer.ff_post_norm_1_w) {
246                cur = build_norm(cur, layer.ff_post_norm_1_w, nullptr, NORM_TYPE_RMS, norm_eps, il);
247            }
248            residual = ggml_add(ctx0, residual, ggml_scale(ctx0, cur, res_weight));
249        }
250
251        // Layer output norm
252        cur = layer.ln_2_w
253            ? build_norm(residual, layer.ln_2_w, nullptr, NORM_TYPE_RMS, norm_eps, il)
254            : residual;
255
256    }
257
258    // 4. Output Projection
259    if (model.audio_out_proj_w) {
260        cur = build_mm(model.audio_out_proj_w, cur);
261        if (model.audio_out_proj_b) {
262            cur = ggml_add(ctx0, cur, model.audio_out_proj_b);
263        }
264    }
265
266    // 5. Audio Multimodal Embedder
267    cur = ggml_rms_norm(ctx0, cur, norm_eps);
268    if (model.mm_soft_emb_norm_w) {
269        cur = ggml_mul(ctx0, cur, model.mm_soft_emb_norm_w);
270    }
271    if (model.mm_input_proj_w) {
272        cur = build_mm(model.mm_input_proj_w, cur);
273    }
274
275    ggml_build_forward_expand(gf, cur);
276    return gf;
277}
278
279ggml_tensor * clip_graph_gemma4a::build_mm(ggml_tensor * w, ggml_tensor * x) const {
280    auto it = model.clamp_info_map.find(w->name);
281    if (it == model.clamp_info_map.end()) {
282        return ggml_mul_mat(ctx0, w, x);
283    }
284    const auto & ci = it->second;
285    ggml_tensor * clamped = ggml_clamp(ctx0, x, ci.inp_min, ci.inp_max);
286    ggml_tensor * out = ggml_mul_mat(ctx0, w, clamped);
287    return ggml_clamp(ctx0, out, ci.out_min, ci.out_max);
288}