1use pagesift_infer::exec::{Cpu, Exec};
8use pagesift_infer::{Activation, BN_EPS, BatchNorm, Conv2d, ConvTranspose2d, Tensor, Weights};
9
10#[derive(Debug, Clone)]
12pub struct NeckConfig {
13 pub in_channels: Vec<usize>,
14 pub out_channels: usize,
15 pub kernel_size: usize,
16}
17
18pub fn head_kernels(v: &serde_json::Value) -> Result<[usize; 3], String> {
21 let a = v["kernel_list"].as_array().ok_or("missing kernel_list")?;
22 let k: Vec<usize> = a
23 .iter()
24 .map(|x| {
25 x.as_u64()
26 .map(|n| n as usize)
27 .ok_or("kernel_list must be integers")
28 })
29 .collect::<Result<_, _>>()?;
30 k.try_into()
31 .map_err(|_| "kernel_list must have 3 numbers".into())
32}
33
34impl NeckConfig {
35 pub fn from_json(v: &serde_json::Value) -> Result<NeckConfig, String> {
36 let ints = |key: &str| -> Result<Vec<usize>, String> {
37 v[key]
38 .as_array()
39 .ok_or(format!("missing {key}"))?
40 .iter()
41 .map(|x| {
42 x.as_u64()
43 .map(|n| n as usize)
44 .ok_or(format!("{key} must be an integer"))
45 })
46 .collect()
47 };
48 if v["interpolate_mode"].as_str().unwrap_or("nearest") != "nearest" {
49 return Err("the neck only supports nearest-neighbor upsampling".into());
50 }
51 let in_channels = ints("layer_list_out_channels")?;
52 if in_channels.len() != 4 {
53 return Err("layer_list_out_channels must have 4 stages".into());
54 }
55 Ok(NeckConfig {
56 in_channels,
57 out_channels: v["neck_out_channels"].as_u64().unwrap_or(96) as usize,
59 kernel_size: v["dilated_kernel_size"].as_u64().unwrap_or(7) as usize,
60 })
61 }
62}
63
64#[derive(Debug, Clone)]
67struct SeResidual {
68 c1: Conv2d,
69 c2: Conv2d,
70}
71
72impl SeResidual {
73 fn load(w: &Weights, p: &str) -> Result<SeResidual, String> {
74 let c = |n: &str| Conv2d::load(w, &format!("{p}.{n}"), (1, 1), (0, 0), (1, 1), 1);
75 Ok(SeResidual {
76 c1: c("conv1")?,
77 c2: c("conv2")?,
78 })
79 }
80
81 fn apply_on<E: Exec>(&self, e: &E, x: &mut E::T) {
82 let pooled = e.global_avg(x);
83 let mut s = e.conv(&self.c1, &pooled);
84 e.act(Activation::Relu, &mut s);
85 let mut s = e.conv(&self.c2, &s);
86 e.se_gate(&mut s);
87 e.scale_channels(x, &s);
88 }
89}
90
91#[derive(Debug, Clone)]
93struct InsertConv {
94 conv: Conv2d,
95 se: SeResidual,
96}
97
98impl InsertConv {
99 fn forward_on<E: Exec>(&self, e: &E, x: &E::T) -> E::T {
100 let mut h = e.conv(&self.conv, x);
101 self.se.apply_on(e, &mut h);
102 h
103 }
104}
105
106#[derive(Debug, Clone)]
108struct InputConv {
109 depthwise: Conv2d,
110 pointwise: Conv2d,
111 se: SeResidual,
112}
113
114impl InputConv {
115 fn forward_on<E: Exec>(&self, e: &E, x: &E::T) -> E::T {
116 let mut h = e.conv(&self.pointwise, &e.conv(&self.depthwise, x));
117 self.se.apply_on(e, &mut h);
118 h
119 }
120}
121
122#[derive(Debug, Clone)]
124pub struct Neck {
125 insert: Vec<InsertConv>,
126 input: Vec<InputConv>,
127}
128
129pub(super) fn upsample_on<E: Exec>(e: &E, x: &E::T, s: usize) -> E::T {
131 let shape = e.shape(x);
132 e.upsample(x, shape[1] * s, shape[2] * s)
133}
134
135impl Neck {
136 pub fn load(w: &Weights, p: &str, cfg: &NeckConfig) -> Result<Neck, String> {
138 let k = cfg.kernel_size;
139 let c = cfg.out_channels;
140 let mut insert = Vec::new();
141 let mut input = Vec::new();
142 for i in 0..cfg.in_channels.len() {
143 let q = format!("{p}.insert_conv.{i}");
144 insert.push(InsertConv {
145 conv: Conv2d::load(w, &format!("{q}.in_conv"), (1, 1), (0, 0), (1, 1), 1)?,
146 se: SeResidual::load(w, &format!("{q}.squeeze_excitation_block"))?,
147 });
148 let q = format!("{p}.input_conv.{i}");
149 input.push(InputConv {
150 depthwise: Conv2d::load(
152 w,
153 &format!("{q}.depthwise_convolution"),
154 (1, 1),
155 (k / 2, k / 2),
156 (1, 1),
157 c,
158 )?,
159 pointwise: Conv2d::load(
160 w,
161 &format!("{q}.pointwise_convolution"),
162 (1, 1),
163 (0, 0),
164 (1, 1),
165 1,
166 )?,
167 se: SeResidual::load(w, &format!("{q}.squeeze_excitation_module"))?,
168 });
169 }
170 Ok(Neck { insert, input })
171 }
172
173 pub fn forward(&self, feats: &[Tensor]) -> Result<Tensor, String> {
175 self.forward_on(&Cpu, feats)
176 }
177
178 pub fn forward_on<E: Exec>(&self, e: &E, feats: &[E::T]) -> Result<E::T, String> {
180 if feats.len() != self.insert.len() {
181 return Err(format!(
182 "the backbone gave {} feature stages, the neck needs {}",
183 feats.len(),
184 self.insert.len()
185 ));
186 }
187 let mut fused: Vec<E::T> = self
188 .insert
189 .iter()
190 .zip(feats)
191 .map(|(m, f)| m.forward_on(e, f))
192 .collect();
193 for i in (0..fused.len() - 1).rev() {
195 let up = upsample_on(e, &fused[i + 1], 2);
196 if e.shape(&up) != e.shape(&fused[i]) {
197 return Err(format!(
198 "neck upsampling size mismatch: {:?} and {:?}",
199 e.shape(&up),
200 e.shape(&fused[i])
201 ));
202 }
203 e.add_assign(&mut fused[i], &up);
204 }
205 let processed: Vec<E::T> = self
206 .input
207 .iter()
208 .zip(&fused)
209 .enumerate()
210 .map(|(i, (m, f))| {
211 let h = m.forward_on(e, f);
212 if i == 0 {
213 h
214 } else {
215 upsample_on(e, &h, 1 << i)
216 }
217 })
218 .collect();
219 let refs: Vec<&E::T> = processed.iter().rev().collect();
221 Ok(e.concat(&refs))
222 }
223}
224
225#[derive(Debug, Clone)]
227pub struct Head {
228 down: Conv2d,
229 up: ConvTranspose2d,
230 fin: ConvTranspose2d,
231}
232
233impl Head {
234 pub fn load(w: &Weights, p: &str, k: [usize; 3]) -> Result<Head, String> {
237 let mut down = Conv2d::load(
239 w,
240 &format!("{p}.conv_down.convolution"),
241 (1, 1),
242 (k[0] / 2, k[0] / 2),
243 (1, 1),
244 1,
245 )?;
246 down.fold(&BatchNorm::load(w, &format!("{p}.conv_down.norm"), BN_EPS)?);
247 let mut up = ConvTranspose2d::load(w, &format!("{p}.conv_up.convolution"), (2, 2), (0, 0))?;
248 up.fold(&BatchNorm::load(w, &format!("{p}.conv_up.norm"), BN_EPS)?);
249 let fin = ConvTranspose2d::load(w, &format!("{p}.conv_final"), (2, 2), (0, 0))?;
250 for (name, got, want) in [
251 ("conv_up", up.weight.shape[2], k[1]),
252 ("conv_final", fin.weight.shape[2], k[2]),
253 ] {
254 if got != want {
255 return Err(format!(
256 "{name} has kernel size {got}, the config says {want}"
257 ));
258 }
259 }
260 Ok(Head { down, up, fin })
261 }
262
263 pub fn forward(&self, x: &Tensor) -> Tensor {
265 self.forward_on(&Cpu, x)
266 }
267
268 pub fn forward_on<E: Exec>(&self, e: &E, x: &E::T) -> E::T {
270 let mut h = e.conv(&self.down, x);
271 e.act(Activation::Relu, &mut h);
272 let mut h = e.conv_transpose(&self.up, &h);
273 e.act(Activation::Relu, &mut h);
274 let mut h = e.conv_transpose(&self.fin, &h);
275 e.act(Activation::Sigmoid, &mut h);
276 h
277 }
278}