use pagesift_infer::{Activation, Tensor, Weights, ops};
use super::layers::{conv_layer, count};
use crate::lcnet::ConvBn;
#[derive(Debug, Clone)]
enum Layer {
Plain(ConvBn),
Light(ConvBn, ConvBn),
}
impl Layer {
fn load(w: &Weights, p: &str) -> Result<Layer, String> {
if w.opt(&format!("{p}.conv1.convolution.weight")).is_some() {
Ok(Layer::Light(
conv_layer(w, &format!("{p}.conv1"), 1, false, Activation::Identity)?,
conv_layer(w, &format!("{p}.conv2"), 1, true, Activation::Relu)?,
))
} else {
Ok(Layer::Plain(conv_layer(w, p, 1, false, Activation::Relu)?))
}
}
fn forward(&self, x: &Tensor) -> Tensor {
match self {
Layer::Plain(c) => c.forward(x),
Layer::Light(a, b) => b.forward(&a.forward(x)),
}
}
}
#[derive(Debug, Clone)]
struct Block {
layers: Vec<Layer>,
squeeze: ConvBn,
excite: ConvBn,
residual: bool,
}
impl Block {
fn load(w: &Weights, p: &str, residual: bool) -> Result<Block, String> {
let n = count(w, |i| format!("{p}.layers.{i}.convolution.weight")).max(count(w, |i| {
format!("{p}.layers.{i}.conv1.convolution.weight")
}));
if n == 0 {
return Err(format!("{p} has no convolution layers"));
}
let layers = (0..n)
.map(|i| Layer::load(w, &format!("{p}.layers.{i}")))
.collect::<Result<_, _>>()?;
Ok(Block {
layers,
squeeze: conv_layer(w, &format!("{p}.aggregation.0"), 1, false, Activation::Relu)?,
excite: conv_layer(w, &format!("{p}.aggregation.1"), 1, false, Activation::Relu)?,
residual,
})
}
fn forward(&self, x: &Tensor) -> Tensor {
let mut outs = Vec::with_capacity(self.layers.len());
for l in &self.layers {
let y = l.forward(outs.last().unwrap_or(x));
outs.push(y);
}
let mut parts: Vec<&Tensor> = Vec::with_capacity(outs.len() + 1);
parts.push(x);
parts.extend(outs.iter());
let cat = ops::concat_channels(&parts);
drop(outs);
let y = self.excite.forward(&self.squeeze.forward(&cat));
if self.residual { ops::add(&y, x) } else { y }
}
}
#[derive(Debug, Clone)]
struct Stage {
down: Option<ConvBn>,
blocks: Vec<Block>,
}
const STAGE_STRIDE: usize = 2;
impl Stage {
fn load(w: &Weights, p: &str) -> Result<Stage, String> {
let dp = format!("{p}.downsample");
let down = if w.opt(&format!("{dp}.convolution.weight")).is_some() {
Some(conv_layer(
w,
&dp,
STAGE_STRIDE,
true,
Activation::Identity,
)?)
} else {
None
};
let n = count(w, |i| {
format!("{p}.blocks.{i}.aggregation.0.convolution.weight")
});
let blocks = (0..n)
.map(|i| Block::load(w, &format!("{p}.blocks.{i}"), i != 0))
.collect::<Result<_, _>>()?;
Ok(Stage { down, blocks })
}
fn forward(&self, x: Tensor) -> Tensor {
let mut h = match &self.down {
Some(d) => d.forward(&x),
None => x,
};
for b in &self.blocks {
h = b.forward(&h);
}
h
}
}
#[derive(Debug, Clone)]
pub struct Backbone {
stem1: ConvBn,
stem2a: ConvBn,
stem2b: ConvBn,
stem3: ConvBn,
stem4: ConvBn,
stages: Vec<Stage>,
}
impl Backbone {
pub fn load(w: &Weights, p: &str) -> Result<Backbone, String> {
let s = |name: &str, stride: usize| {
conv_layer(
w,
&format!("{p}.embedder.{name}"),
stride,
false,
Activation::Relu,
)
};
let n = count(w, |i| {
format!("{p}.encoder.stages.{i}.blocks.0.aggregation.0.convolution.weight")
});
if n == 0 {
return Err(format!("{p} has no stages"));
}
let stages = (0..n)
.map(|i| Stage::load(w, &format!("{p}.encoder.stages.{i}")))
.collect::<Result<_, _>>()?;
Ok(Backbone {
stem1: s("stem1", 2)?,
stem2a: s("stem2a", 1)?,
stem2b: s("stem2b", 1)?,
stem3: s("stem3", 2)?,
stem4: s("stem4", 1)?,
stages,
})
}
pub fn forward(&self, x: &Tensor) -> Vec<Tensor> {
let e = ops::pad_right_bottom(&self.stem1.forward(x), 1, 1);
let a = ops::pad_right_bottom(&self.stem2a.forward(&e), 1, 1);
let a = self.stem2b.forward(&a);
let pooled = ops::max_pool2d(&e, (2, 2), (1, 1), true);
let cat = ops::concat_channels(&[&pooled, &a]);
let mut h = self.stem4.forward(&self.stem3.forward(&cat));
let mut outs = Vec::with_capacity(self.stages.len());
for st in &self.stages {
h = st.forward(h);
outs.push(h.clone());
}
outs
}
}