vision_rs/models/yolo/yolo26/blocks/
c2psa.rs1use teeny_core::{dtype::Float, graph::{CustomData, Op, SymTensor}, name_scope::name_scope};
19
20use crate::models::yolo::kernels::attention::psa::{
21 FlashAttn2PsaOp, PsaExtractVOp, PsaMergeAttnOp, PsaPackQkvOp,
22};
23use super::conv::{conv, conv_bn};
24
25fn channel_chunk(x: SymTensor, c_total: usize, chunk_c: usize, chunk_offset: usize) -> SymTensor {
28 let op = Op::ChannelChunk { c_total, chunk_c, chunk_offset };
29 let shape = vec![x.shape[0], Some(chunk_c), x.shape[2], x.shape[3]];
30 let node_id = x.graph.borrow_mut().add_node(op, vec![x.node_id], x.dtype, shape.clone());
31 SymTensor { node_id, graph: x.graph.clone(), dtype: x.dtype, shape }
32}
33
34fn channel_cat(tensors: Vec<SymTensor>, c_total: usize) -> SymTensor {
35 let first = &tensors[0];
36 let shape = vec![first.shape[0], Some(c_total), first.shape[2], first.shape[3]];
37 let inputs: Vec<usize> = tensors.iter().map(|t| t.node_id).collect();
38 let node_id = first.graph.borrow_mut().add_node(
39 Op::ChannelCat { c_total }, inputs, first.dtype, shape.clone(),
40 );
41 SymTensor { node_id, graph: first.graph.clone(), dtype: first.dtype, shape }
42}
43
44fn elem_add(a: SymTensor, b: SymTensor) -> SymTensor {
45 let shape = a.shape.clone();
46 let node_id = a.graph.borrow_mut().add_node(
47 Op::Add, vec![a.node_id, b.node_id], a.dtype, shape.clone(),
48 );
49 SymTensor { node_id, graph: a.graph.clone(), dtype: a.dtype, shape }
50}
51
52fn psa_attention<D: Float + Send + Sync + 'static>(c: usize, num_heads: usize, key_dim: usize)
62 -> impl Fn(SymTensor) -> SymTensor
63{
64 let qkv_h = num_heads * 4 * key_dim;
65 let qkv_conv = conv_bn::<D>(c, qkv_h, 1, 1, 1);
66 let pe_dw = conv_bn::<D>(c, c, 3, 1, c); let proj = conv_bn::<D>(c, c, 1, 1, 1);
68
69 move |x: SymTensor| {
70 let h = x.shape[2].unwrap_or(1);
71 let w = x.shape[3].unwrap_or(1);
72
73 let qkv = { let _g = name_scope("qkv"); qkv_conv(x) };
75
76 let packed = qkv.record_custom(
78 CustomData::new(PsaPackQkvOp::<D>::new(key_dim as i32, num_heads)),
79 &[],
80 None,
81 );
82
83 let lo = packed.record_custom(
85 CustomData::new(FlashAttn2PsaOp::<D>::new_lo(key_dim as i32)),
86 &[],
87 None,
88 );
89 let hi = packed.record_custom(
90 CustomData::new(FlashAttn2PsaOp::<D>::new_hi(key_dim as i32)),
91 &[],
92 None,
93 );
94
95 let merged = lo.record_custom(
97 CustomData::new(PsaMergeAttnOp::<D>::new(key_dim as i32, num_heads, h, w)),
98 &[&hi],
99 None,
100 );
101
102 let v_nchw = qkv.record_custom(
104 CustomData::new(PsaExtractVOp::<D>::new(key_dim as i32, num_heads)),
105 &[],
106 None,
107 );
108
109 let pe = { let _g = name_scope("pe"); pe_dw(v_nchw) };
111 let attn_pe = elem_add(merged, pe);
112
113 { let _g = name_scope("proj"); proj(attn_pe) }
115 }
116}
117
118pub(super) fn psa_block<D: Float + Send + Sync + 'static>(c: usize, num_heads: usize, key_dim: usize)
124 -> impl Fn(SymTensor) -> SymTensor
125{
126 let attn = psa_attention::<D>(c, num_heads, key_dim);
127 let ffn0 = conv::<D>(c, 2 * c, 1, 1);
128 let ffn1 = conv_bn::<D>(2 * c, c, 1, 1, 1);
129 move |b: SymTensor| {
130 let b = elem_add(b.clone(), { let _g = name_scope("attn"); attn(b) });
131 let ffn_out = {
132 let tmp = { let _g = name_scope("ffn.0"); ffn0(b.clone()) };
133 let _g = name_scope("ffn.1"); ffn1(tmp)
134 };
135 elem_add(b, ffn_out)
136 }
137}
138
139pub fn c2psa<D: Float + Send + Sync + 'static>(
151 c_in: usize,
152 c_out: usize,
153 n: usize,
154 e: f32,
155) -> impl Fn(SymTensor) -> SymTensor {
156 assert_eq!(c_in, c_out, "C2PSA requires c_in == c_out");
157 let c = (c_out as f32 * e) as usize;
158 let num_heads = c / 64;
159 let key_dim = 32;
160
161 let cv1 = conv::<D>(c_in, 2 * c, 1, 1);
162 let cv2 = conv::<D>(2 * c, c_out, 1, 1);
163 let blocks: Vec<Box<dyn Fn(SymTensor) -> SymTensor>> = (0..n)
164 .map(|_| -> Box<dyn Fn(SymTensor) -> SymTensor> {
165 Box::new(psa_block::<D>(c, num_heads, key_dim))
166 })
167 .collect();
168
169 move |x: SymTensor| {
170 let h = { let _g = name_scope("cv1"); cv1(x) };
171 let a = channel_chunk(h.clone(), 2 * c, c, 0);
172 let mut b = channel_chunk(h, 2 * c, c, c);
173 for (i, blk) in blocks.iter().enumerate() {
174 let _g = name_scope(format!("m.{i}"));
175 b = blk(b);
176 }
177 { let _g = name_scope("cv2"); cv2(channel_cat(vec![a, b], 2 * c)) }
178 }
179}