Skip to main content

pagesift_ocr/layout/
hgnet.rs

1//! HGNetV2 骨干。结构照 transformers 的 `modeling_hgnet_v2.py`(Apache-2.0):stem + 4 段 HG 块,
2//! 块里若干卷积的输出连同输入按通道拼起来,再过两个 1×1 卷积聚合。批归一化在载入时并进卷积。
3//! 配置(段数、块数、每块层数、核大小、是否轻量块)都从权重推出来,不写死 L 档的表。
4
5use pagesift_infer::{Activation, Tensor, Weights, ops};
6
7use super::layers::{conv_layer, count};
8use crate::lcnet::ConvBn;
9
10/// HG 块里的一层。
11#[derive(Debug, Clone)]
12enum Layer {
13    /// `HGNetV2ConvLayer`。
14    Plain(ConvBn),
15    /// `HGNetV2ConvLayerLight`:1×1(无激活)+ 深度卷积(ReLU)。
16    Light(ConvBn, ConvBn),
17}
18
19impl Layer {
20    fn load(w: &Weights, p: &str) -> Result<Layer, String> {
21        if w.opt(&format!("{p}.conv1.convolution.weight")).is_some() {
22            Ok(Layer::Light(
23                conv_layer(w, &format!("{p}.conv1"), 1, false, Activation::Identity)?,
24                conv_layer(w, &format!("{p}.conv2"), 1, true, Activation::Relu)?,
25            ))
26        } else {
27            Ok(Layer::Plain(conv_layer(w, p, 1, false, Activation::Relu)?))
28        }
29    }
30
31    fn forward(&self, x: &Tensor) -> Tensor {
32        match self {
33            Layer::Plain(c) => c.forward(x),
34            Layer::Light(a, b) => b.forward(&a.forward(x)),
35        }
36    }
37}
38
39/// `HGNetV2BasicLayer`。
40#[derive(Debug, Clone)]
41struct Block {
42    layers: Vec<Layer>,
43    squeeze: ConvBn,
44    excite: ConvBn,
45    residual: bool,
46}
47
48impl Block {
49    fn load(w: &Weights, p: &str, residual: bool) -> Result<Block, String> {
50        // 轻量块的权重在 layers.i.conv1 下,普通块在 layers.i.convolution 下
51        let n = count(w, |i| format!("{p}.layers.{i}.convolution.weight")).max(count(w, |i| {
52            format!("{p}.layers.{i}.conv1.convolution.weight")
53        }));
54        if n == 0 {
55            return Err(format!("{p} has no convolution layers"));
56        }
57        let layers = (0..n)
58            .map(|i| Layer::load(w, &format!("{p}.layers.{i}")))
59            .collect::<Result<_, _>>()?;
60        Ok(Block {
61            layers,
62            squeeze: conv_layer(w, &format!("{p}.aggregation.0"), 1, false, Activation::Relu)?,
63            excite: conv_layer(w, &format!("{p}.aggregation.1"), 1, false, Activation::Relu)?,
64            residual,
65        })
66    }
67
68    fn forward(&self, x: &Tensor) -> Tensor {
69        let mut outs = Vec::with_capacity(self.layers.len());
70        for l in &self.layers {
71            let y = l.forward(outs.last().unwrap_or(x));
72            outs.push(y);
73        }
74        let mut parts: Vec<&Tensor> = Vec::with_capacity(outs.len() + 1);
75        parts.push(x);
76        parts.extend(outs.iter());
77        let cat = ops::concat_channels(&parts);
78        drop(outs);
79        let y = self.excite.forward(&self.squeeze.forward(&cat));
80        if self.residual { ops::add(&y, x) } else { y }
81    }
82}
83
84/// `HGNetV2Stage`。
85#[derive(Debug, Clone)]
86struct Stage {
87    down: Option<ConvBn>,
88    blocks: Vec<Block>,
89}
90
91/// [协议] `HGNetV2Config` 的 `stage_downsample_strides` 缺省值
92const STAGE_STRIDE: usize = 2;
93
94impl Stage {
95    fn load(w: &Weights, p: &str) -> Result<Stage, String> {
96        let dp = format!("{p}.downsample");
97        let down = if w.opt(&format!("{dp}.convolution.weight")).is_some() {
98            Some(conv_layer(
99                w,
100                &dp,
101                STAGE_STRIDE,
102                true,
103                Activation::Identity,
104            )?)
105        } else {
106            None
107        };
108        let n = count(w, |i| {
109            format!("{p}.blocks.{i}.aggregation.0.convolution.weight")
110        });
111        // `residual = (i != 0)`:每段第一个块改变通道,不做残差
112        let blocks = (0..n)
113            .map(|i| Block::load(w, &format!("{p}.blocks.{i}"), i != 0))
114            .collect::<Result<_, _>>()?;
115        Ok(Stage { down, blocks })
116    }
117
118    fn forward(&self, x: Tensor) -> Tensor {
119        let mut h = match &self.down {
120            Some(d) => d.forward(&x),
121            None => x,
122        };
123        for b in &self.blocks {
124            h = b.forward(&h);
125        }
126        h
127    }
128}
129
130/// 整个骨干。
131#[derive(Debug, Clone)]
132pub struct Backbone {
133    stem1: ConvBn,
134    stem2a: ConvBn,
135    stem2b: ConvBn,
136    stem3: ConvBn,
137    stem4: ConvBn,
138    stages: Vec<Stage>,
139}
140
141impl Backbone {
142    /// `p` 是权重名前缀(`model.backbone.model`)。
143    pub fn load(w: &Weights, p: &str) -> Result<Backbone, String> {
144        // [协议] `HGNetV2Config` 的 `stem_strides` 缺省 (2, 1, 1, 2, 1)、激活 ReLU
145        let s = |name: &str, stride: usize| {
146            conv_layer(
147                w,
148                &format!("{p}.embedder.{name}"),
149                stride,
150                false,
151                Activation::Relu,
152            )
153        };
154        let n = count(w, |i| {
155            format!("{p}.encoder.stages.{i}.blocks.0.aggregation.0.convolution.weight")
156        });
157        if n == 0 {
158            return Err(format!("{p} has no stages"));
159        }
160        let stages = (0..n)
161            .map(|i| Stage::load(w, &format!("{p}.encoder.stages.{i}")))
162            .collect::<Result<_, _>>()?;
163        Ok(Backbone {
164            stem1: s("stem1", 2)?,
165            stem2a: s("stem2a", 1)?,
166            stem2b: s("stem2b", 1)?,
167            stem3: s("stem3", 2)?,
168            stem4: s("stem4", 1)?,
169            stages,
170        })
171    }
172
173    /// 输入 `[3, H, W]`(已归一化),返回各段的输出(stage1 到最后一段)。
174    pub fn forward(&self, x: &Tensor) -> Vec<Tensor> {
175        let e = ops::pad_right_bottom(&self.stem1.forward(x), 1, 1);
176        let a = ops::pad_right_bottom(&self.stem2a.forward(&e), 1, 1);
177        let a = self.stem2b.forward(&a);
178        let pooled = ops::max_pool2d(&e, (2, 2), (1, 1), true);
179        let cat = ops::concat_channels(&[&pooled, &a]);
180        let mut h = self.stem4.forward(&self.stem3.forward(&cat));
181        let mut outs = Vec::with_capacity(self.stages.len());
182        for st in &self.stages {
183            h = st.forward(h);
184            outs.push(h.clone());
185        }
186        outs
187    }
188}