char-fork/llama.cpp

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

Kwa Jie Haomtmd: Add support for Reka Edge 2603 (#21616)98d2d2884

char/aliceai-t5-moe
8.5 KiB191 linesraw
1// ABOUTME: Yasa2 vision encoder graph builder for ConvNeXt-based architecture.
2// ABOUTME: Implements patch embedding, ConvNeXt stages with GRN, and adaptive pooling.
3
4#include "models.h"
5
6static ggml_tensor * add_channel_bias(
7        ggml_context * ctx0,
8        ggml_tensor * x_whcb,
9        ggml_tensor * b_c) {
10    if (!b_c) {
11        return x_whcb;
12    }
13    ggml_tensor * b4 = ggml_reshape_4d(ctx0, b_c, 1, 1, b_c->ne[0], 1);
14    return ggml_add(ctx0, x_whcb, b4);
15}
16
17static ggml_tensor * mul_channel_weight(
18        ggml_context * ctx0,
19        ggml_tensor * x_whcb,
20        ggml_tensor * w_c) {
21    if (!w_c) {
22        return x_whcb;
23    }
24    ggml_tensor * w4 = ggml_reshape_4d(ctx0, w_c, 1, 1, w_c->ne[0], 1);
25    return ggml_mul(ctx0, x_whcb, w4);
26}
27
28ggml_tensor * clip_graph_yasa2::layer_norm_channels(ggml_tensor * inp, ggml_tensor * w, ggml_tensor * b, float eps) {
29    // Match HF ConvNextLayerNorm(channels_first):
30    // u = mean_c(x), s = mean_c((x-u)^2), x = (x-u)/sqrt(s+eps)
31    // cast back to input dtype before affine.
32    ggml_tensor * cur = ggml_permute(ctx0, inp, 2, 1, 0, 3); // [W,H,C,B] -> [C,H,W,B]
33    cur = ggml_cont(ctx0, cur);
34
35    ggml_tensor * u = ggml_mean(ctx0, cur);                 // [1,H,W,B]
36    ggml_tensor * xm = ggml_sub(ctx0, cur, u);              // [C,H,W,B]
37
38    ggml_tensor * s = ggml_mul(ctx0, xm, xm);               // [C,H,W,B]
39    s = ggml_mean(ctx0, s);                                 // [1,H,W,B]
40    s = ggml_clamp(ctx0, s, eps, 1e30f);                    // avoid div-by-zero in no-alloc warmup
41    s = ggml_sqrt(ctx0, s);                                 // [1,H,W,B]
42
43    ggml_tensor * xhat = ggml_div(ctx0, xm, s);             // [C,H,W,B]
44    xhat = ggml_permute(ctx0, xhat, 2, 1, 0, 3);            // [W,H,C,B]
45    xhat = ggml_cont(ctx0, xhat);
46    xhat = mul_channel_weight(ctx0, xhat, w);
47    xhat = add_channel_bias(ctx0, xhat, b);
48    return xhat;
49}
50
51ggml_tensor * clip_graph_yasa2::convnext_grn(ggml_tensor * inp, ggml_tensor * w, ggml_tensor * b) {
52    // Exact ConvNeXtV2 GRN:
53    // Gx = ||x||_2 over spatial dims (W,H), Nx = Gx / (mean_c(Gx) + eps)
54    // y  = w * (x * Nx) + b + x
55    const int64_t wdim = inp->ne[0];
56    const int64_t hdim = inp->ne[1];
57    const int64_t cdim = inp->ne[2];
58    const int64_t bdim = inp->ne[3];
59
60    // Keep GRN math in fp32 for stability; fp16/bf16 accumulation can drift.
61    ggml_tensor * sq = ggml_mul(ctx0, inp, inp);
62    ggml_tensor * sq_flat = ggml_reshape_4d(ctx0, sq, wdim * hdim, cdim, 1, bdim);   // [WH,C,1,B]
63    ggml_tensor * gx = ggml_sum_rows(ctx0, sq_flat);                                   // [1,C,1,B]
64    gx = ggml_sqrt(ctx0, gx);                                                           // [1,C,1,B]
65
66    ggml_tensor * gx_ch_first = ggml_permute(ctx0, gx, 1, 0, 2, 3);                    // [C,1,1,B]
67    gx_ch_first = ggml_cont(ctx0, gx_ch_first);
68    ggml_tensor * gx_mean = ggml_mean(ctx0, gx_ch_first);                              // [1,1,1,B]
69
70    gx_mean = ggml_clamp(ctx0, gx_mean, 1e-6f, 1e30f);                                  // approx +eps, warmup-safe
71    ggml_tensor * nx = ggml_div(ctx0, gx, gx_mean);                                    // [1,C,1,B]
72    nx = ggml_permute(ctx0, nx, 0, 2, 1, 3);                                            // [1,1,C,B]
73    nx = ggml_cont(ctx0, nx);
74
75    ggml_tensor * xnx = ggml_mul(ctx0, inp, nx);
76    xnx = mul_channel_weight(ctx0, xnx, w);
77    xnx = add_channel_bias(ctx0, xnx, b);
78    return ggml_add(ctx0, inp, xnx);
79}
80
81ggml_cgraph * clip_graph_yasa2::build() {
82    ggml_tensor * cur = build_inp_raw();
83
84    // Patch embedding Conv2d(kernel=4, stride=4)
85    cur = ggml_conv_2d(ctx0, model.yasa_patch_w, cur, patch_size, patch_size, 0, 0, 1, 1);
86    cur = add_channel_bias(ctx0, cur, model.yasa_patch_b);
87    ggml_set_name(cur, "yasa2_patch_conv_out");
88    cb(cur, "yasa2_patch_conv_out", -1);
89    cur = layer_norm_channels(cur, model.yasa_patch_ln_w, model.yasa_patch_ln_b, eps);
90    ggml_set_name(cur, "yasa2_patch_ln_out");
91    cb(cur, "yasa2_patch_ln_out", -1);
92
93    // ConvNeXt stages
94    for (size_t s = 0; s < model.yasa_stages.size(); ++s) {
95        const auto & stage = model.yasa_stages[s];
96
97        if (stage.down_conv_w) {
98            cur = layer_norm_channels(cur, stage.down_ln_w, stage.down_ln_b, eps);
99            cur = ggml_conv_2d(ctx0, stage.down_conv_w, cur, 2, 2, 0, 0, 1, 1);
100            cur = add_channel_bias(ctx0, cur, stage.down_conv_b);
101            ggml_format_name(cur, "yasa2_stage%zu_down_out", s);
102        }
103
104        for (size_t bi = 0; bi < stage.blocks.size(); ++bi) {
105            const auto & blk = stage.blocks[bi];
106            ggml_tensor * res = cur;
107
108            ggml_tensor * x = ggml_conv_2d_dw(ctx0, blk.dw_w, cur, 1, 1, 3, 3, 1, 1);
109            x = add_channel_bias(ctx0, x, blk.dw_b);
110            x = layer_norm_channels(x, blk.ln_w, blk.ln_b, eps);
111
112            // pwconv1/pwconv2 are HF Linear layers over channels; implement via matmul on tokens.
113            const int64_t w = x->ne[0];
114            const int64_t h = x->ne[1];
115            const int64_t b = x->ne[3];
116
117            ggml_tensor * tok = ggml_reshape_3d(ctx0, x, w * h, x->ne[2], b); // [T,C,B]
118            tok = ggml_permute(ctx0, tok, 1, 0, 2, 3);                        // [C,T,B]
119            tok = ggml_cont(ctx0, tok);
120
121            tok = ggml_mul_mat(ctx0, blk.pw1_w, tok);                         // [4C,T,B]
122            if (blk.pw1_b) {
123                ggml_tensor * b1 = ggml_reshape_3d(ctx0, blk.pw1_b, blk.pw1_b->ne[0], 1, 1); // [4C,1,1]
124                tok = ggml_add(ctx0, tok, b1);
125            }
126            x = ggml_permute(ctx0, tok, 1, 0, 2, 3);                         // [T,4C,B]
127            x = ggml_cont(ctx0, x);
128            x = ggml_reshape_4d(ctx0, x, w, h, tok->ne[0], b);               // [W,H,4C,B]
129            x = ggml_gelu_erf(ctx0, x);
130            x = convnext_grn(x, blk.grn_w, blk.grn_b);
131
132            tok = ggml_reshape_3d(ctx0, x, w * h, x->ne[2], b);              // [T,4C,B]
133            tok = ggml_permute(ctx0, tok, 1, 0, 2, 3);                       // [4C,T,B]
134            tok = ggml_cont(ctx0, tok);
135
136            tok = ggml_mul_mat(ctx0, blk.pw2_w, tok);                        // [C,T,B]
137            if (blk.pw2_b) {
138                ggml_tensor * b2 = ggml_reshape_3d(ctx0, blk.pw2_b, blk.pw2_b->ne[0], 1, 1); // [C,1,1]
139                tok = ggml_add(ctx0, tok, b2);
140            }
141            x = ggml_permute(ctx0, tok, 1, 0, 2, 3);                         // [T,C,B]
142            x = ggml_cont(ctx0, x);
143            x = ggml_reshape_4d(ctx0, x, w, h, tok->ne[0], b);               // [W,H,C,B]
144
145            cur = ggml_add(ctx0, res, x);
146            ggml_format_name(cur, "yasa2_stage%zu_blk%zu_out", s, bi);
147        }
148    }
149
150    // HF path adds vision position embeddings BEFORE adaptive pooling.
151    const int64_t pre_w = cur->ne[0];
152    const int64_t pre_h = cur->ne[1];
153    ggml_tensor * tokens_pre = ggml_reshape_3d(ctx0, cur, pre_w * pre_h, cur->ne[2], cur->ne[3]); // [T,C,B]
154    tokens_pre = ggml_permute(ctx0, tokens_pre, 1, 0, 2, 3); // [C,T,B]
155    tokens_pre = ggml_cont(ctx0, tokens_pre);
156    if (model.yasa_vision_pos_embed && tokens_pre->ne[1] == model.yasa_vision_pos_embed->ne[1]) {
157        const int64_t n_ch = model.yasa_vision_pos_embed->ne[0];
158        const int64_t n_tokens = model.yasa_vision_pos_embed->ne[1];
159        ggml_tensor * pos = ggml_reshape_3d(ctx0, model.yasa_vision_pos_embed, (int) n_ch, (int) n_tokens, 1);
160        tokens_pre = ggml_add(ctx0, tokens_pre, pos);
161    }
162    cur = ggml_permute(ctx0, tokens_pre, 1, 0, 2, 3); // [T,C,B]
163    cur = ggml_cont(ctx0, cur);
164    cur = ggml_reshape_4d(ctx0, cur, pre_w, pre_h, cur->ne[1], cur->ne[2]); // [W,H,C,B]
165
166    // AdaptiveAvgPool2d target is 8x8 for real inputs, but warmup can use tiny images.
167    const int pooled_w = std::min(8, (int) cur->ne[0]);
168    const int pooled_h = std::min(8, (int) cur->ne[1]);
169    const int kw = std::max(1, (int) cur->ne[0] / pooled_w);
170    const int kh = std::max(1, (int) cur->ne[1] / pooled_h);
171    cur = ggml_pool_2d(ctx0, cur, GGML_OP_POOL_AVG, kw, kh, kw, kh, 0, 0);
172
173    // [W,H,C,B] -> [C,T,B]
174    ggml_tensor * tokens = ggml_reshape_3d(ctx0, cur, cur->ne[0] * cur->ne[1], cur->ne[2], cur->ne[3]);
175    tokens = ggml_permute(ctx0, tokens, 1, 0, 2, 3);
176    tokens = ggml_cont(ctx0, tokens);
177    cb(tokens, "yasa2_tokens", -1);
178
179    GGML_ASSERT(model.mm_0_w && model.mm_2_w);
180    ggml_tensor * embeddings = build_ffn(
181        tokens,
182        model.mm_0_w, model.mm_0_b,
183        nullptr, nullptr,
184        model.mm_2_w, model.mm_2_b,
185        FFN_GELU_ERF,
186        -1);
187    cb(embeddings, "yasa2_emb", -1);
188
189    ggml_build_forward_expand(gf, embeddings);
190    return gf;
191}