char-fork/llama.cpp
git clone https://git.t4t.associates/char-fork/llama.cpp
9731ad3f2
char/aliceai-t5-moe
1#include "models.h" 2 3// Implementation based on approach suggested by Acly 4// See: https://github.com/ggml-org/llama.cpp/pull/17383#issuecomment-3554227091 5static ggml_tensor * window_partition (ggml_context * ctx0 ,ggml_tensor * x ,const int window ) { 6auto [c ,w ,h ,b ]= x -> ne ; 7// same as 8// x = ggml_win_part(m, x, window); 9// x = ggml_reshape_3d(m, x, c, window * window, x->ne[3]); 10 11const int64_t px = (window - w %window ) %window ; 12const int64_t py = (window - h %window ) %window ; 13const int64_t npw = (w + px ) /window ; 14const int64_t nph = (h + py ) /window ; 15 16ggml_tensor * cur = x ; 17if (px > 0 || py > 0 ) { 18cur = ggml_pad (ctx0 ,cur ,0 ,static_cast < int > (px ),static_cast < int > (py ),0 ); 19 } 20cur = ggml_reshape_4d (ctx0 ,cur ,c * window ,npw ,window ,nph * b ); 21cur = ggml_cont (ctx0 ,ggml_permute (ctx0 ,cur ,0 ,2 ,1 ,3 )); 22cur = ggml_reshape_4d (ctx0 ,cur ,c ,window ,window ,npw * nph * b ); 23return cur ; 24} 25 26// Implementation based on approach suggested by Acly 27// See: https://github.com/ggml-org/llama.cpp/pull/17383#issuecomment-3554227091 28static ggml_tensor * window_unpartition (ggml_context * ctx0 , 29ggml_tensor * x , 30const int w , 31const int h , 32const int window ) { 33const int64_t c = x -> ne [0 ]; 34// same as 35// x = ggml_reshape_4d(m, x, c, window, window, x->ne[2]); 36// x = ggml_win_unpart(m, x, w, h, window); 37 38const int64_t px = (window - w %window ) %window ; 39const int64_t py = (window - h %window ) %window ; 40const int64_t npw = (w + px ) /window ; 41const int64_t nph = (h + py ) /window ; 42 43const int64_t b = x -> ne [3 ] / (npw * nph ); 44ggml_tensor * cur = x ; 45cur = ggml_reshape_4d (ctx0 ,cur ,c * window ,window ,npw ,nph * b ); 46cur = ggml_cont (ctx0 ,ggml_permute (ctx0 ,cur ,0 ,2 ,1 ,3 )); 47cur = ggml_reshape_4d (ctx0 ,cur ,c ,w + px ,h + py ,b ); 48cur = ggml_view_4d (ctx0 ,cur ,cur -> ne [0 ],w ,h ,cur -> ne [3 ],cur -> nb [1 ],cur -> nb [2 ],cur -> nb [3 ],0 ); 49cur = ggml_cont (ctx0 ,cur ); 50return cur ; 51} 52 53static ggml_tensor * get_rel_pos (ggml_context * ctx0 , 54ggml_tensor * rel_pos ,// [L, C] 55ggml_tensor * indices ,// [q_size, k_size] 56const int q_size , 57const int k_size ) { 58const int64_t C = rel_pos -> ne [0 ];// channels 59const int64_t L = rel_pos -> ne [1 ];// length 60 61GGML_ASSERT (indices != nullptr ); 62GGML_ASSERT (indices -> type == GGML_TYPE_I32 ); 63GGML_ASSERT (indices -> ne [0 ]== k_size ); 64GGML_ASSERT (indices -> ne [1 ]== q_size ); 65 66const auto max_rel_dist = 2 * std::max (q_size ,k_size )- 1 ; 67ggml_tensor * cur = rel_pos ; 68 69if (max_rel_dist != L ) { 70// Linear interpolation 71const int64_t ne0 = cur -> ne [0 ]; 72const int64_t ne1 = cur -> ne [1 ]; 73const int64_t ne2 = cur -> ne [2 ]; 74const int64_t ne3 = cur -> ne [3 ]; 75 76cur = ggml_reshape_3d (ctx0 ,ggml_cont (ctx0 ,ggml_permute (ctx0 ,cur ,1 ,0 ,2 ,3 )),ne1 ,1 ,ne0 * ne2 * ne3 ); 77cur = ggml_reshape_4d ( 78ctx0 ,ggml_interpolate (ctx0 ,cur ,max_rel_dist ,1 ,ne0 * ne2 * ne3 ,1 ,GGML_SCALE_MODE_BILINEAR ), 79max_rel_dist ,ne0 ,ne2 ,ne3 ); 80cur = ggml_cont (ctx0 ,ggml_permute (ctx0 ,cur ,1 ,0 ,2 ,3 )); 81 } 82 83// Flatten indices to 1D for ggml_get_rows 84const int qk = q_size * k_size ; 85 86cur = ggml_reshape_3d (ctx0 ,ggml_get_rows (ctx0 ,cur ,ggml_reshape_1d (ctx0 ,indices ,qk )),C ,k_size ,q_size ); 87 88return cur ;// [C, k_size, q_size] 89} 90 91// ggml_conv_2d with the im2col kept in F32: the F16 im2col it emits since #23660 degrades OCR 92static ggml_tensor * conv_2d_f32 (ggml_context * ctx0 ,ggml_tensor * a ,ggml_tensor * b , 93int s0 ,int s1 ,int p0 ,int p1 ,int d0 ,int d1 ) { 94const ggml_type im2col_type = a -> type == GGML_TYPE_F16 ?GGML_TYPE_F16 :GGML_TYPE_F32 ; 95ggml_tensor * im2col = ggml_im2col (ctx0 ,a ,b ,s0 ,s1 ,p0 ,p1 ,d0 ,d1 , true,im2col_type );// [N, OH, OW, IC * KH * KW] 96 97ggml_tensor * result = ggml_mul_mat (ctx0 , 98ggml_reshape_2d (ctx0 ,im2col ,im2col -> ne [0 ],im2col -> ne [3 ]* im2col -> ne [2 ]* im2col -> ne [1 ]), 99ggml_reshape_2d (ctx0 ,a , (a -> ne [0 ]* a -> ne [1 ]* a -> ne [2 ]),a -> ne [3 ])); 100 101result = ggml_reshape_4d (ctx0 ,result ,im2col -> ne [1 ],im2col -> ne [2 ],im2col -> ne [3 ],a -> ne [3 ]);// [OC, N, OH, OW] 102result = ggml_cont (ctx0 ,ggml_permute (ctx0 ,result ,0 ,1 ,3 ,2 ));// [N, OC, OH, OW] 103 104return result ; 105} 106 107 108ggml_tensor * clip_graph_deepseekocr::build_sam (ggml_tensor * inp_raw ) { 109// Building SAM 110const int n_embd = hparams .sam_n_embd ; 111const int n_layer = hparams .sam_n_layer ; 112const int n_heads = hparams .sam_n_head ; 113const int d_heads = n_embd /n_heads ; 114const int window = hparams .attn_window_size ; 115// SAM stage runs its layernorms at 1e-6 116const float sam_eps = 1e-6f ; 117 118ggml_tensor * inpL ; 119 120inpL = conv_2d_f32 (ctx0 ,model .patch_embed_proj_w ,inp_raw , 121 (int )model .patch_embed_proj_w -> ne [0 ], (int )model .patch_embed_proj_w -> ne [1 ],0 ,0 ,1 ,1 ); 122inpL = ggml_add (ctx0 ,inpL ,ggml_reshape_3d (ctx0 ,model .patch_embed_proj_b ,1 ,1 ,n_embd )); 123inpL = ggml_cont (ctx0 ,ggml_permute (ctx0 ,inpL ,1 ,2 ,0 ,3 )); 124 125ggml_tensor * rel_pos_indices_local ; 126ggml_tensor * rel_pos_indices_global ; 127 128rel_pos_indices_local = ggml_new_tensor_2d (ctx0 ,GGML_TYPE_I32 ,window ,window ); 129rel_pos_indices_global = ggml_new_tensor_2d (ctx0 ,GGML_TYPE_I32 ,inpL -> ne [1 ],inpL -> ne [2 ]); 130ggml_set_name (rel_pos_indices_local ,"rel_pos_indices_local" ); 131ggml_set_name (rel_pos_indices_global ,"rel_pos_indices_global" ); 132ggml_set_input (rel_pos_indices_local ); 133ggml_set_input (rel_pos_indices_global ); 134 135ggml_tensor * cur ; 136const auto tgt_size = inpL -> ne [1 ]; 137const auto str_size = model .pos_embed -> ne [1 ]; 138 139if (str_size != tgt_size ) { 140ggml_tensor * old_pos_embed = nullptr ; 141old_pos_embed = ggml_cont (ctx0 ,ggml_permute (ctx0 ,model .pos_embed ,2 ,0 ,1 ,3 )); 142ggml_tensor * new_pos_embed = 143ggml_interpolate (ctx0 ,old_pos_embed ,tgt_size ,tgt_size ,n_embd ,1 ,GGML_SCALE_MODE_BICUBIC ); 144new_pos_embed = ggml_cont (ctx0 ,ggml_permute (ctx0 ,new_pos_embed ,1 ,2 ,0 ,3 )); 145cur = ggml_add (ctx0 ,inpL ,new_pos_embed ); 146 }else { 147cur = ggml_add (ctx0 ,inpL ,model .pos_embed ); 148 } 149 150// loop over layers 151for (int il = 0 ;il < n_layer ;il ++ ) { 152auto & layer = model .sam_layers [il ]; 153ggml_tensor * shortcut = cur ; 154 155// layernorm1 156cur = build_norm (cur ,layer .ln_1_w ,layer .ln_1_b ,NORM_TYPE_NORMAL ,sam_eps ,il ); 157 158const int64_t w0 = cur -> ne [1 ]; 159const int64_t h0 = cur -> ne [2 ]; 160 161ggml_tensor * indices ; 162 163if (hparams .is_global_attn (il )) { 164indices = rel_pos_indices_global ; 165 }else { 166// local attention layer - apply window partition 167cur = window_partition (ctx0 ,cur ,window ); 168indices = rel_pos_indices_local ; 169 } 170 171const int64_t W = cur -> ne [1 ]; 172const int64_t H = cur -> ne [2 ]; 173// self-attention 174 { 175const int B = cur -> ne [3 ]; 176 177cur = ggml_mul_mat (ctx0 ,layer .qkv_w ,cur ); 178cur = ggml_add (ctx0 ,cur ,layer .qkv_b ); 179cur = ggml_reshape_4d (ctx0 ,cur ,n_embd ,3 ,W * H ,B ); 180 181ggml_tensor * Q ; 182ggml_tensor * K ; 183ggml_tensor * V ; 184 185Q = ggml_view_3d (ctx0 ,cur ,n_embd ,W * H ,B ,cur -> nb [2 ],cur -> nb [3 ],0 * cur -> nb [1 ]); 186Q = ggml_reshape_4d (ctx0 ,ggml_cont (ctx0 ,Q ),d_heads ,n_heads ,W * H ,B ); 187 188K = ggml_view_3d (ctx0 ,cur ,n_embd ,W * H ,B ,cur -> nb [2 ],cur -> nb [3 ],1 * cur -> nb [1 ]); 189K = ggml_reshape_4d (ctx0 ,ggml_cont (ctx0 ,K ),d_heads ,n_heads ,W * H ,B ); 190 191V = ggml_view_3d (ctx0 ,cur ,n_embd ,W * H ,B ,cur -> nb [2 ],cur -> nb [3 ],2 * cur -> nb [1 ]); 192V = ggml_reshape_4d (ctx0 ,ggml_cont (ctx0 ,V ),d_heads ,n_heads ,W * H ,B ); 193 194ggml_tensor * mask ; 195ggml_tensor * rw ; 196ggml_tensor * rh ; 197ggml_tensor * qr ; 198 199rw = get_rel_pos (ctx0 ,layer .rel_pos_w ,indices ,W ,W );// [W, W, C] 200rh = get_rel_pos (ctx0 ,layer .rel_pos_h ,indices ,H ,H );// [H, H, C] 201qr = ggml_permute (ctx0 ,Q ,0 ,2 ,1 ,3 ); 202qr = ggml_reshape_4d (ctx0 ,ggml_cont (ctx0 ,qr ),d_heads ,W ,H ,B * n_heads ); 203 204rw = ggml_mul_mat (ctx0 ,rw , 205ggml_cont (ctx0 ,ggml_permute (ctx0 ,qr ,0 ,2 ,1 ,3 )));// [B*n_heads, W, H, W] 206rw = ggml_cont (ctx0 ,ggml_permute (ctx0 ,rw ,0 ,2 ,1 ,3 ));// [B*n_heads, H, W, W] 207rw = ggml_reshape_4d (ctx0 ,rw ,W ,1 ,W * H ,n_heads * B ); 208rw = ggml_repeat_4d (ctx0 ,rw ,W ,H ,W * H ,n_heads * B ); 209rh = ggml_mul_mat (ctx0 ,rh ,qr );// [B*n_heads, H, W, H] 210rh = ggml_reshape_4d (ctx0 ,rh ,1 ,H ,W * H ,n_heads * B ); 211mask = ggml_add (ctx0 ,rw ,rh );// [B*n_heads, H*W, H, W] 212mask = ggml_reshape_4d (ctx0 ,mask ,W * H ,W * H ,n_heads ,B ); 213// casting mask to F16 only required when flash-attn is enabled 214if (flash_attn_type == CLIP_FLASH_ATTN_TYPE_ENABLED ) { 215mask = ggml_cast (ctx0 ,mask ,GGML_TYPE_F16 ); 216 } 217 218const float scale = 1.0f /sqrtf (static_cast < float > (d_heads )); 219 220cur = build_attn (layer .o_w ,layer .o_b ,Q ,K ,V ,mask ,scale , 221il );// [B, H*W, n_embd] 222cur = ggml_reshape_4d (ctx0 ,ggml_cont (ctx0 ,cur ),n_embd ,W ,H ,B ); 223 } 224 225if (hparams .is_global_attn (il )== false) { 226// local attention layer - reverse window partition 227cur = window_unpartition (ctx0 ,cur ,w0 ,h0 ,window ); 228 } 229 230// re-add the layer input, e.g., residual 231cur = ggml_add (ctx0 ,cur ,shortcut ); 232 233ggml_tensor * inpFF = cur ; 234 235// layernorm2 236cur = build_norm (inpFF ,layer .ln_2_w ,layer .ln_2_b ,NORM_TYPE_NORMAL ,sam_eps ,il ); 237 238// ffn 239cur = build_ffn (cur ,layer .ff_up_w ,layer .ff_up_b ,nullptr ,nullptr ,layer .ff_down_w ,layer .ff_down_b , 240hparams .ffn_op ,il ); 241 242// residual 2 243cur = ggml_add (ctx0 ,cur ,inpFF ); 244cb (cur ,"sam_layer_out" ,il ); 245 } 246 247cur = ggml_cont (ctx0 ,ggml_permute (ctx0 ,cur ,2 ,0 ,1 ,3 )); 248 249cur = conv_2d_f32 (ctx0 ,model .neck_0_w ,cur ,1 ,1 ,0 ,0 ,1 ,1 ); 250cur = ggml_cont (ctx0 ,ggml_permute (ctx0 ,cur ,1 ,2 ,0 ,3 )); 251cur = build_norm (cur ,model .neck_1_w ,model .neck_1_b ,NORM_TYPE_NORMAL ,sam_eps ,-1 ); 252cur = ggml_cont (ctx0 ,ggml_permute (ctx0 ,cur ,2 ,0 ,1 ,3 )); 253 254cur = conv_2d_f32 (ctx0 ,model .neck_2_w ,cur ,1 ,1 ,1 ,1 ,1 ,1 ); 255cur = ggml_cont (ctx0 ,ggml_permute (ctx0 ,cur ,1 ,2 ,0 ,3 )); 256cur = build_norm (cur ,model .neck_3_w ,model .neck_3_b ,NORM_TYPE_NORMAL ,sam_eps ,-1 ); 257cur = ggml_cont (ctx0 ,ggml_permute (ctx0 ,cur ,2 ,0 ,1 ,3 )); 258 259cur = conv_2d_f32 (ctx0 ,model .net_2 ,cur ,2 ,2 ,1 ,1 ,1 ,1 ); 260cur = conv_2d_f32 (ctx0 ,model .net_3 ,cur ,2 ,2 ,1 ,1 ,1 ,1 ); 261cb (cur ,"sam_output" ,-1 ); 262 263ggml_build_forward_expand (gf ,cur ); 264return cur ; 265} 266 267ggml_cgraph * clip_graph_deepseekocr::build () { 268// patch embedding 269ggml_tensor * inp_raw = build_inp_raw (); 270 271bool is_overview = img .add_viewsep ; 272int n_tiles_per_row = 0 ; 273// number of separate "row" images batched together in this graph call 274// (captured now, before n_batch below gets repurposed as the SAM/ViT batch size) 275const int n_rows_batch = n_batch ; 276 277// note: we expect either a batch of rows or a batch of overviews, but not a mix of both 278 279if (!is_overview ) { 280// handle the case where we have a batch of rows 281// sanity check 282for (auto & entry :img_batch -> entries ) { 283if (entry .add_viewsep ) { 284throw std::runtime_error ("DeepSeek-OCR: mixed overview and non-overview images in batch" ); 285 } 286if (entry .nx ()!= img .nx ()|| entry .ny ()!= img .ny ()) { 287throw std::runtime_error ("DeepSeek-OCR: mixed image sizes in batch" ); 288 } 289 } 290 291GGML_ASSERT (img .ny () >=img .nx ()); 292GGML_ASSERT (img .ny () %img .nx ()== 0 ); 293n_tiles_per_row = img .ny () /img .nx (); 294 295// each entry is one "row" image of shape [tile_size, tile_size * n_tiles_per_row, 3]; 296// merge the tile axis into the batch axis, giving a combined SAM input of shape 297// [tile_size, tile_size, 3, n_tiles_per_row * n_rows_batch] (tile fast, row slow) 298inp_raw = ggml_reshape_4d (ctx0 ,inp_raw ,img .nx ()* img .nx (),n_tiles_per_row ,3 ,n_rows_batch ); 299inp_raw = ggml_cont (ctx0 ,ggml_permute (ctx0 ,inp_raw ,0 ,2 ,1 ,3 )); 300inp_raw = ggml_reshape_4d (ctx0 ,inp_raw ,img .nx (),img .nx (),3 ,n_tiles_per_row * n_rows_batch ); 301 } 302 303ggml_tensor * sam_out = build_sam (inp_raw ); 304 305if (!is_overview ) { 306n_batch = n_tiles_per_row * n_rows_batch ; 307 } 308 309const int clip_n_patches = sam_out -> ne [0 ]* sam_out -> ne [1 ]; 310 311ggml_tensor * clip_out ; 312// Building DS-OCR CLIP 313 { 314ggml_tensor * inp ; 315 316// sam_out: [patch_h, patch_w, n_embd, n_batch] 317// -> [n_embd, clip_n_patches, n_batch] 318inp = ggml_reshape_3d (ctx0 ,sam_out ,clip_n_patches ,sam_out -> ne [2 ],sam_out -> ne [3 ]); 319inp = ggml_cont (ctx0 ,ggml_permute (ctx0 ,inp ,1 ,0 ,2 ,3 )); 320 321ggml_tensor * new_pos_embd = model .position_embeddings ; 322 323int n_pos = new_pos_embd -> ne [1 ];// +1 for [CLS] 324const auto tgt_size = static_cast < int > (std::sqrt (inp -> ne [1 ])); 325const auto src_size = static_cast < int > (std::sqrt (n_pos - 1 )); 326 327if (tgt_size != src_size ) { 328ggml_tensor * old_pos_embd ; 329ggml_tensor * cls_tok ; 330 331old_pos_embd = ggml_view_2d (ctx0 ,new_pos_embd ,new_pos_embd -> ne [0 ],src_size * src_size , 332ggml_row_size (new_pos_embd -> type ,new_pos_embd -> ne [0 ]),0 ); 333cls_tok = ggml_view_2d (ctx0 ,new_pos_embd ,new_pos_embd -> ne [0 ],1 , 334ggml_row_size (new_pos_embd -> type ,new_pos_embd -> ne [0 ]),src_size * src_size ); 335new_pos_embd = ggml_interpolate (ctx0 ,old_pos_embd ,tgt_size ,tgt_size ,new_pos_embd -> ne [0 ],1 , 336GGML_SCALE_MODE_BICUBIC ); 337new_pos_embd = ggml_reshape_3d (ctx0 ,new_pos_embd ,n_embd ,tgt_size * tgt_size ,1 ); 338new_pos_embd = ggml_concat (ctx0 ,new_pos_embd ,cls_tok ,1 ); 339n_pos = tgt_size * tgt_size + 1 ; 340 } 341 342// add CLS token per batch item 343// inp: [n_embd, clip_n_patches, n_batch] 344// class_embedding: [n_embd] -> [n_embd, 1, n_batch] 345ggml_tensor * cls_embd = ggml_repeat_4d (ctx0 ,model .class_embedding ,n_embd ,1 ,n_batch ,1 ); 346inp = ggml_concat (ctx0 ,cls_embd ,inp ,1 ); 347 348// for selecting learned pos embd, used by ViT 349ggml_tensor * positions = ggml_cast (ctx0 ,ggml_arange (ctx0 ,0 ,n_pos ,1 ),GGML_TYPE_I32 ); 350ggml_tensor * learned_pos_embd = ggml_get_rows (ctx0 ,new_pos_embd ,positions ); 351 352ggml_tensor * cur = build_vit (inp ,n_pos ,NORM_TYPE_NORMAL ,FFN_GELU_QUICK ,learned_pos_embd ,nullptr ); 353 354ggml_build_forward_expand (gf ,cur ); 355clip_out = cur ; 356 } 357 358// sam_out: [patch_h, patch_w, n_embd, n_batch] 359// -> [n_embd, clip_n_patches, n_batch] 360sam_out = ggml_cont (ctx0 ,ggml_permute (ctx0 ,sam_out ,1 ,2 ,0 ,3 )); 361sam_out = ggml_reshape_3d (ctx0 ,sam_out ,sam_out -> ne [0 ],clip_n_patches ,n_batch ); 362 363// clip_out: [n_embd, n_pos, n_batch] where n_pos = clip_n_patches + 1 (CLS) 364// strip CLS token: skip first position, view only the patch tokens 365clip_out = ggml_view_3d (ctx0 ,clip_out ,n_embd ,clip_n_patches ,n_batch , 366clip_out -> nb [1 ],clip_out -> nb [2 ],clip_out -> nb [1 ]); 367 368ggml_tensor * cur ; 369cur = ggml_concat (ctx0 ,clip_out ,sam_out ,0 ); 370cur = ggml_mul_mat (ctx0 ,model .mm_fc_w ,cur ); 371cur = ggml_add (ctx0 ,cur ,model .mm_fc_b ); 372 373if (is_overview ) { 374// global view: weave one newline per row + trailing view separator 375const auto h = static_cast < int > (std::sqrt (static_cast < float > (cur -> ne [1 ]))); 376const auto w = h ; 377const auto n_dim = cur -> ne [0 ]; 378 379ggml_tensor * imgnl = ggml_repeat_4d (ctx0 ,model .image_newline ,n_dim ,1 ,h ,n_batch ); 380cur = ggml_reshape_4d (ctx0 ,cur ,n_dim ,w ,h ,n_batch ); 381cur = ggml_reshape_3d (ctx0 ,ggml_concat (ctx0 ,cur ,imgnl ,1 ),n_dim , (w + 1 )* h ,n_batch ); 382ggml_tensor * vs = ggml_repeat_4d (ctx0 ,model .view_seperator ,n_dim ,1 ,n_batch ,1 ); 383cur = ggml_concat (ctx0 ,cur ,vs ,1 );// (n_dim, h*(w+1) + 1, n_batch) 384 }else { 385// tile row: interleave tiles within each row, add newline per row 386const int grid_x = static_cast < int > (std::sqrt (static_cast < float > (clip_n_patches ))); 387const int grid_y = grid_x ; 388const auto n_dim = cur -> ne [0 ]; 389 390// merge n_dim into the grid_x axis, freeing the 4th axis for n_rows_batch 391// (n_dim, clip_n_patches, n_tiles_per_row * n_rows_batch) -> (n_dim*grid_x, grid_y, n_tiles_per_row, n_rows_batch) 392cur = ggml_reshape_4d (ctx0 ,cur ,n_dim * grid_x ,grid_y ,n_tiles_per_row ,n_rows_batch ); 393 394// tiles: re-order from A.row0 A.row1 B.row0 B.row1 ... 395// to A.row0 B.row0 A.row1 B.row1 ... 396// then add nl: A.row0 B.row0 [nl] A.row1 B.row1 [nl] ... 397// interleave tiles: -> (n_dim*grid_x, n_tiles_per_row, grid_y, n_rows_batch) 398cur = ggml_cont (ctx0 ,ggml_permute (ctx0 ,cur ,0 ,2 ,1 ,3 )); 399 400// merge: -> (n_dim, grid_x*n_tiles_per_row, grid_y, n_rows_batch) 401cur = ggml_reshape_4d (ctx0 ,cur ,n_dim ,grid_x * n_tiles_per_row ,grid_y ,n_rows_batch ); 402 403// append newline per row: (n_dim, grid_x*n_tiles_per_row+1, grid_y, n_rows_batch) 404ggml_tensor * imgnl = ggml_repeat_4d (ctx0 ,model .image_newline ,n_dim ,1 ,grid_y ,n_rows_batch ); 405cur = ggml_concat (ctx0 ,cur ,imgnl ,1 ); 406 407// flatten: (n_dim, (grid_x*n_tiles_per_row+1)*grid_y, n_rows_batch) 408cur = ggml_reshape_3d (ctx0 ,cur ,n_dim , (grid_x * n_tiles_per_row + 1 )* grid_y ,n_rows_batch ); 409 } 410 411cb (cur ,"dsocr_output" ,-1 ); 412 413ggml_build_forward_expand (gf ,cur ); 414return gf ; 415}