1use pagesift_infer::exec::{Cpu, Exec};
6use pagesift_infer::{Activation, BN_EPS, BatchNorm, Conv2d, Tensor, Weights};
7
8pub type BlockConfig = (usize, usize, usize, (usize, usize), bool);
10
11#[derive(Debug, Clone)]
13pub struct Config {
14 pub stem_channels: [usize; 3],
15 pub blocks: Vec<Vec<BlockConfig>>,
16}
17
18impl Config {
19 pub fn from_json(v: &serde_json::Value) -> Result<Config, String> {
22 let b = &v["backbone_config"];
23 if b["stem_type"].as_str().unwrap_or("large") != "large" {
24 return Err("only the large stem is supported".into());
25 }
26 if b["use_learnable_affine_block"].as_bool().unwrap_or(false) {
27 return Err("learnable affine blocks are not supported".into());
28 }
29 let int = |x: &serde_json::Value| {
30 x.as_u64()
31 .map(|v| v as usize)
32 .ok_or("expected an integer in the config")
33 };
34 let sc = b["stem_channels"]
35 .as_array()
36 .ok_or("missing stem_channels")?;
37 let stem_channels = [int(&sc[0])?, int(&sc[1])?, int(&sc[2])?];
38 let mut blocks = Vec::new();
39 for stage in b["block_configs"]
40 .as_array()
41 .ok_or("missing block_configs")?
42 {
43 let mut s = Vec::new();
44 for blk in stage.as_array().ok_or("malformed block_configs")? {
45 let a = blk.as_array().ok_or("malformed block_configs")?;
46 let stride = match a[3].as_array() {
48 Some(p) => (int(&p[0])?, int(&p[1])?),
49 None => (int(&a[3])?, int(&a[3])?),
50 };
51 s.push((
52 int(&a[0])?,
53 int(&a[1])?,
54 int(&a[2])?,
55 stride,
56 a[4].as_bool().unwrap_or(false),
57 ));
58 }
59 blocks.push(s);
60 }
61 Ok(Config {
62 stem_channels,
63 blocks,
64 })
65 }
66}
67
68#[derive(Debug, Clone)]
70pub struct ConvBn {
71 pub conv: Conv2d,
72 pub act: Activation,
73}
74
75impl ConvBn {
76 pub fn load(
78 w: &Weights,
79 p: &str,
80 stride: (usize, usize),
81 groups: usize,
82 act: Activation,
83 ) -> Result<ConvBn, String> {
84 let k = w.get(&format!("{p}.convolution.weight"))?.shape[2];
85 let pad = (k - 1) / 2;
86 let mut conv = Conv2d::load(
87 w,
88 &format!("{p}.convolution"),
89 stride,
90 (pad, pad),
91 (1, 1),
92 groups,
93 )?;
94 conv.fold(&BatchNorm::load(w, &format!("{p}.normalization"), BN_EPS)?);
95 Ok(ConvBn { conv, act })
96 }
97
98 pub fn forward(&self, x: &Tensor) -> Tensor {
99 self.forward_on(&Cpu, x)
100 }
101
102 pub fn forward_on<E: Exec>(&self, e: &E, x: &E::T) -> E::T {
103 let mut y = e.conv(&self.conv, x);
104 e.act(self.act, &mut y);
105 y
106 }
107}
108
109#[derive(Debug, Clone)]
111struct Se {
112 c1: Conv2d,
113 c2: Conv2d,
114}
115
116impl Se {
117 fn load(w: &Weights, p: &str) -> Result<Se, String> {
118 let c = |i: usize| {
120 Conv2d::load(
121 w,
122 &format!("{p}.convolutions.{i}"),
123 (1, 1),
124 (0, 0),
125 (1, 1),
126 1,
127 )
128 };
129 Ok(Se {
130 c1: c(0)?,
131 c2: c(2)?,
132 })
133 }
134
135 fn forward_on<E: Exec>(&self, e: &E, x: &mut E::T) {
136 let pooled = e.global_avg(x);
137 let mut s = e.conv(&self.c1, &pooled);
138 e.act(Activation::Relu, &mut s);
139 let mut s = e.conv(&self.c2, &s);
140 e.act(Activation::HardSigmoid, &mut s);
141 e.scale_channels(x, &s);
142 }
143}
144
145#[derive(Debug, Clone)]
147struct Block {
148 token: Conv2d,
149 se: Option<Se>,
150 ch1: ConvBn,
151 ch2: ConvBn,
152 residual: bool,
153}
154
155impl Block {
156 fn load(w: &Weights, p: &str, cfg: &BlockConfig) -> Result<Block, String> {
157 let &(k, cin, cout, stride, use_se) = cfg;
158 let same = cin == cout && stride == (1, 1);
159 let token = if same {
162 Conv2d::load(
163 w,
164 &format!("{p}.token_conv"),
165 stride,
166 (k / 2, k / 2),
167 (1, 1),
168 cin,
169 )?
170 } else {
171 ConvBn::load(
172 w,
173 &format!("{p}.token_conv"),
174 stride,
175 cin,
176 Activation::Identity,
177 )?
178 .conv
179 };
180 let se = if use_se {
181 Some(Se::load(w, &format!("{p}.token_squeeze_excitation"))?)
182 } else {
183 None
184 };
185 Ok(Block {
186 token,
187 se,
188 ch1: ConvBn::load(
189 w,
190 &format!("{p}.channel_conv1"),
191 (1, 1),
192 1,
193 Activation::Gelu,
194 )?,
195 ch2: ConvBn::load(
196 w,
197 &format!("{p}.channel_conv2"),
198 (1, 1),
199 1,
200 Activation::Identity,
201 )?,
202 residual: same,
203 })
204 }
205
206 fn forward_on<E: Exec>(&self, e: &E, x: &E::T) -> E::T {
207 let mut t = e.conv(&self.token, x);
208 if let Some(se) = &self.se {
209 se.forward_on(e, &mut t);
210 }
211 let y = self.ch2.forward_on(e, &self.ch1.forward_on(e, &t));
212 if self.residual { e.add(&t, &y) } else { y }
213 }
214}
215
216#[derive(Debug, Clone)]
218pub struct Backbone {
219 stem1: ConvBn,
220 stem2a: ConvBn,
221 stem2b: ConvBn,
222 stem3: ConvBn,
223 stem4: ConvBn,
224 stages: Vec<Vec<Block>>,
225}
226
227impl Backbone {
228 pub fn load(w: &Weights, p: &str, cfg: &Config) -> Result<Backbone, String> {
230 let s = |name: &str, stride: usize| {
232 ConvBn::load(
233 w,
234 &format!("{p}.convolution.{name}"),
235 (stride, stride),
236 1,
237 Activation::Relu,
238 )
239 };
240 let mut stages = Vec::new();
241 for (i, stage) in cfg.blocks.iter().enumerate() {
242 let mut v = Vec::new();
243 for (j, b) in stage.iter().enumerate() {
244 v.push(Block::load(w, &format!("{p}.blocks.{i}.blocks.{j}"), b)?);
245 }
246 stages.push(v);
247 }
248 Ok(Backbone {
249 stem1: s("stem1", 2)?,
250 stem2a: s("stem2a", 1)?,
251 stem2b: s("stem2b", 1)?,
252 stem3: s("stem3", 2)?,
253 stem4: s("stem4", 1)?,
254 stages,
255 })
256 }
257
258 pub fn forward(&self, x: &Tensor) -> Vec<Tensor> {
260 self.forward_on(&Cpu, x)
261 }
262
263 pub fn forward_on<E: Exec>(&self, ex: &E, x: &E::T) -> Vec<E::T> {
265 let stem = self.stem_on(ex, x);
266 let mut outs: Vec<E::T> = Vec::with_capacity(self.stages.len());
267 for stage in &self.stages {
268 let h = self.stage_on(ex, stage, outs.last().unwrap_or(&stem));
269 outs.push(h);
270 }
271 outs
272 }
273
274 pub fn forward_last_on<E: Exec>(&self, ex: &E, x: &E::T) -> E::T {
276 let mut h = self.stem_on(ex, x);
277 for stage in &self.stages {
278 h = self.stage_on(ex, stage, &h);
279 }
280 h
281 }
282
283 fn stem_on<E: Exec>(&self, ex: &E, x: &E::T) -> E::T {
284 let e = ex.pad_right_bottom(&self.stem1.forward_on(ex, x), 1, 1);
285 let a = ex.pad_right_bottom(&self.stem2a.forward_on(ex, &e), 1, 1);
286 let a = self.stem2b.forward_on(ex, &a);
287 let pooled = ex.max_pool2d(&e, (2, 2), (1, 1), true);
288 let cat = ex.concat(&[&pooled, &a]);
289 self.stem4.forward_on(ex, &self.stem3.forward_on(ex, &cat))
290 }
291
292 fn stage_on<E: Exec>(&self, ex: &E, stage: &[Block], x: &E::T) -> E::T {
293 let mut h = stage[0].forward_on(ex, x);
294 for b in &stage[1..] {
295 h = b.forward_on(ex, &h);
296 }
297 h
298 }
299}