| 246 | } |
| 247 | |
| 248 | ggml_cgraph * clip_graph_mobilenetv5::build() { |
| 249 | ggml_tensor * inp = build_inp_raw(); |
| 250 | |
| 251 | // 1. Stem - Conv2dSame(3, 64, kernel_size=(3, 3), stride=(2, 2)) |
| 252 | ggml_tensor * cur = pad_same_2d(inp, 3, 3, 2, 2); // Apply SAME padding |
| 253 | |
| 254 | cur = ggml_conv_2d_direct(ctx0, model.mobilenet_stem_conv_w, cur, 2, 2, 0, 0, 1, 1); // padding=0 |
| 255 | if (model.mobilenet_stem_conv_b) { |
| 256 | cur = ggml_add(ctx0, cur, model.mobilenet_stem_conv_b); |
| 257 | } |
| 258 | if (model.mobilenet_stem_norm_w) cur = rms_norm_2d(cur, model.mobilenet_stem_norm_w); |
| 259 | cur = ggml_gelu(ctx0, cur); |
| 260 | |
| 261 | |
| 262 | // 2. Blocks |
| 263 | std::vector<ggml_tensor*> intermediate_features; |
| 264 | const int total_blocks = model.mobilenet_blocks.size(); |
| 265 | |
| 266 | auto is_stage_start = [&](int i) { |
| 267 | if (i == 0) return true; |
| 268 | for (int end_idx : model.mobilenet_stage_ends) { |
| 269 | if (i == end_idx + 1) return true; |
| 270 | } |
| 271 | return false; |
| 272 | }; |
| 273 | |
| 274 | auto is_fusion_point = [&](int i) { |
| 275 | if (model.mobilenet_stage_ends.size() >= 4) { |
| 276 | if (i == model.mobilenet_stage_ends[2]) return true; // End of Stage 2 |
| 277 | if (i == model.mobilenet_stage_ends[3]) return true; // End of Stage 3 |
| 278 | } else { |
| 279 | if (i == total_blocks - 1) return true; |
| 280 | } |
| 281 | return false; |
| 282 | }; |
| 283 | |
| 284 | for (int i = 0; i < total_blocks; i++) { |
| 285 | const auto & block = model.mobilenet_blocks[i]; |
| 286 | int stride = is_stage_start(i) ? 2 : 1; |
| 287 | |
| 288 | if (block.s0_conv_exp_w) cur = build_edge_residual(cur, block, stride); |
| 289 | else if (block.attn_q_w) cur = build_mobilenet_attn(cur, block); |
| 290 | else cur = build_inverted_residual(cur, block, stride); |
| 291 | |
| 292 | if (is_fusion_point(i)) { |
| 293 | |
| 294 | intermediate_features.push_back(cur); |
| 295 | } |
| 296 | } |
| 297 | |
| 298 | // 3. Multi-Scale Fusion Adapter (MSFA) |
| 299 | if (!intermediate_features.empty()) { |
| 300 | |
| 301 | // A. Reference Resolution: PyTorch implementation uses inputs[0] |
| 302 | // We assume intermediate_features[0] is the "High Resolution" target. |
| 303 | // In MobileNet designs, this is typically the feature map with the smallest stride (e.g. 32x32). |
| 304 | ggml_tensor* target_feat = intermediate_features[0]; |
| 305 | int high_res_w = target_feat->ne[0]; |
nothing calls this directly
no test coverage detected