From cf33bf2c96b7513464bdc8dfb2eec0cccb6f1e7b Mon Sep 17 00:00:00 2001 From: Jiayu Liu Date: Wed, 24 Jan 2024 13:30:16 +0800 Subject: [PATCH] add segformer --- candle-examples/examples/segformer/README.md | 23 + .../examples/segformer/assets/burger.jpg | Bin 0 -> 62870 bytes candle-examples/examples/segformer/main.rs | 109 +++ candle-transformers/src/models/mod.rs | 1 + candle-transformers/src/models/segformer.rs | 705 ++++++++++++++++++ 5 files changed, 838 insertions(+) create mode 100644 candle-examples/examples/segformer/README.md create mode 100644 candle-examples/examples/segformer/assets/burger.jpg create mode 100644 candle-examples/examples/segformer/main.rs create mode 100644 candle-transformers/src/models/segformer.rs diff --git a/candle-examples/examples/segformer/README.md b/candle-examples/examples/segformer/README.md new file mode 100644 index 0000000000..42a21e8ebf --- /dev/null +++ b/candle-examples/examples/segformer/README.md @@ -0,0 +1,23 @@ +# candle-segformer + +- [HuggingFace Segformer Model Card][segformer] +- [`mit-b0` - An encoder only pretrained model][encoder] +- [`segformer-b0-finetuned-ade-512-512` - A fine tuned model for segmentation][ade512] + +[segformer]: https://huggingface.co/docs/transformers/model_doc/segformer +[encoder]: https://huggingface.co/nvidia/mit-b0 +[ade512]: https://huggingface.co/nvidia/segformer-b0-finetuned-ade-512-512 + +## How to run + +```bash +# run image classification task +cargo run --example segformer classify candle-examples/examples/segformer/assets/burger.jpg +``` + +Example output: + +```text +classification logits [3.275261e-5, 0.0008562019, 0.0008868563, 0.9977506, 0.0002465068, 0.0002241473, 2.846596e-6] +label: hamburger +``` diff --git a/candle-examples/examples/segformer/assets/burger.jpg b/candle-examples/examples/segformer/assets/burger.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e823c37aa5f73f93b88015f9b47bba05878c8d7c GIT binary patch literal 62870 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 XuW@Cxg5$uPMvz+YuW(o{;LrcruaYXD literal 0 HcmV?d00001 diff --git a/candle-examples/examples/segformer/main.rs b/candle-examples/examples/segformer/main.rs new file mode 100644 index 0000000000..b1a89e11df --- /dev/null +++ b/candle-examples/examples/segformer/main.rs @@ -0,0 +1,109 @@ +use candle::Device; +use candle::Module; +use candle_nn::VarBuilder; +use candle_transformers::models::segformer::{ + Config, ImageClassificationModel, SemanticSegmentationModel, +}; +use clap::{Args, Parser, Subcommand}; +use std::path::PathBuf; + +#[derive(Parser)] +#[clap(about, version, long_about = None)] +struct CliArgs { + #[arg(long, help = "use cpu")] + cpu: bool, + #[command(subcommand)] + command: Commands, +} +#[derive(Args, Debug)] +struct SegmentationArgs { + #[arg( + long, + help = "name of the huggingface hub model", + default_value = "nvidia/segformer-b0-finetuned-ade-512-512" + )] + model_name: String, + #[arg(help = "path to image as input")] + image: PathBuf, +} + +#[derive(Args, Debug)] +struct ClassificationArgs { + #[arg( + long, + help = "name of the huggingface hub model", + default_value = "paolinox/segformer-finetuned-food101" + )] + model_name: String, + #[arg(help = "path to image as input")] + image: PathBuf, +} + +#[derive(Subcommand, Debug)] +enum Commands { + Segment(SegmentationArgs), + Classify(ClassificationArgs), +} + +fn get_vb_and_config(model_name: String, device: &Device) -> anyhow::Result<(VarBuilder, Config)> { + println!("loading model {} via huggingface hub", model_name); + let api = hf_hub::api::sync::Api::new()?; + let api = api.model(model_name.clone()); + let model_file = api.get("model.safetensors")?; + println!("model {} downloaded and loaded", model_name); + let vb = + unsafe { VarBuilder::from_mmaped_safetensors(&[model_file], candle::DType::F32, device)? }; + let config = std::fs::read_to_string(api.get("config.json")?)?; + let config: Config = serde_json::from_str(&config)?; + println!("{:?}", config); + Ok((vb, config)) +} + +fn segmentation_task(args: SegmentationArgs, device: &Device) -> anyhow::Result<()> { + let image = candle_examples::imagenet::load_image224(args.image)? + .unsqueeze(0)? + .to_device(device)?; + let (vb, config) = get_vb_and_config(args.model_name, device)?; + let num_labels = 150; + let model = SemanticSegmentationModel::new(&config, num_labels, vb)?; + let segmentations = model.forward(&image)?; + println!( + "segmentation result shape {:?} which should match [1, num_labels, height/4, width/4]", + segmentations.shape() + ); + let labels = segmentations.squeeze(0)?.argmax(0)?; + let labels = labels.to_vec2::()?; + println!("labels {:?}", labels); + Ok(()) +} + +fn classification_task(args: ClassificationArgs, device: &Device) -> anyhow::Result<()> { + let image = candle_examples::imagenet::load_image224(args.image)? + .unsqueeze(0)? + .to_device(device)?; + let (vb, config) = get_vb_and_config(args.model_name, device)?; + let num_labels = 7; + let model = ImageClassificationModel::new(&config, num_labels, vb)?; + let classification = model.forward(&image)?; + let classification = candle_nn::ops::softmax_last_dim(&classification)?; + let classification = classification.squeeze(0)?; + println!( + "classification logits {:?}", + classification.to_vec1::()? + ); + let label_id = classification.argmax(0)?.to_scalar::()?; + let label_id = format!("{}", label_id); + println!("label: {}", config.id2label[&label_id]); + Ok(()) +} + +pub fn main() -> anyhow::Result<()> { + let args = CliArgs::parse(); + let device = candle_examples::device(args.cpu)?; + if let Commands::Segment(args) = args.command { + segmentation_task(args, &device)? + } else if let Commands::Classify(args) = args.command { + classification_task(args, &device)? + } + Ok(()) +} diff --git a/candle-transformers/src/models/mod.rs b/candle-transformers/src/models/mod.rs index a5f03059b8..6833bab0f8 100644 --- a/candle-transformers/src/models/mod.rs +++ b/candle-transformers/src/models/mod.rs @@ -41,6 +41,7 @@ pub mod repvgg; pub mod resnet; pub mod rwkv_v5; pub mod rwkv_v6; +pub mod segformer; pub mod segment_anything; pub mod stable_diffusion; pub mod stable_lm; diff --git a/candle-transformers/src/models/segformer.rs b/candle-transformers/src/models/segformer.rs new file mode 100644 index 0000000000..a57bfa9501 --- /dev/null +++ b/candle-transformers/src/models/segformer.rs @@ -0,0 +1,705 @@ +use crate::models::with_tracing::{conv2d, linear, Conv2d, Linear}; +use candle::{Module, ModuleT, Result, Tensor, D}; +use candle_nn::{conv2d_no_bias, layer_norm, Activation, Conv2dConfig, VarBuilder}; +use serde::Deserialize; +use std::collections::HashMap; + +// https://github.com/huggingface/transformers/blob/main/src/transformers/models/segformer/configuration_segformer.py +#[derive(Debug, Clone, PartialEq, Deserialize)] +pub struct Config { + pub id2label: HashMap, + pub num_channels: usize, + pub num_encoder_blocks: usize, + pub depths: Vec, + pub sr_ratios: Vec, + pub hidden_sizes: Vec, + pub patch_sizes: Vec, + pub strides: Vec, + pub num_attention_heads: Vec, + pub mlp_ratios: Vec, + pub hidden_act: candle_nn::Activation, + pub layer_norm_eps: f64, + pub decoder_hidden_size: usize, +} + +#[derive(Debug, Clone)] +struct SegformerOverlapPatchEmbeddings { + projection: Conv2d, + layer_norm: candle_nn::LayerNorm, +} + +impl SegformerOverlapPatchEmbeddings { + fn new( + config: &Config, + patch_size: usize, + stride: usize, + num_channels: usize, + hidden_size: usize, + vb: VarBuilder, + ) -> Result { + let projection = conv2d( + num_channels, + hidden_size, + patch_size, + Conv2dConfig { + stride, + padding: patch_size / 2, + ..Default::default() + }, + vb.pp("proj"), + )?; + let layer_norm = + candle_nn::layer_norm(hidden_size, config.layer_norm_eps, vb.pp("layer_norm"))?; + Ok(Self { + projection, + layer_norm, + }) + } +} + +impl Module for SegformerOverlapPatchEmbeddings { + fn forward(&self, x: &Tensor) -> Result { + let embeddings = self.projection.forward(x)?; + let shape = embeddings.shape(); + // [B, C, H, W] -> [B, H * W, C] + let embeddings = embeddings.flatten_from(2)?.transpose(1, 2)?; + let embeddings = self.layer_norm.forward(&embeddings)?; + // [B, H * W, C] -> [B, C, H, W] + let embeddings = embeddings.transpose(1, 2)?.reshape(shape)?; + Ok(embeddings) + } +} + +#[derive(Debug, Clone)] +struct SegformerEfficientSelfAttention { + num_attention_heads: usize, + attention_head_size: usize, + query: Linear, + key: Linear, + value: Linear, + sr: Option, + layer_norm: Option, +} + +impl SegformerEfficientSelfAttention { + fn new( + config: &Config, + hidden_size: usize, + num_attention_heads: usize, + sequence_reduction_ratio: usize, + vb: VarBuilder, + ) -> Result { + if hidden_size % num_attention_heads != 0 { + candle::bail!( + "The hidden size {} is not a multiple of the number of attention heads {}", + hidden_size, + num_attention_heads + ) + } + let attention_head_size = hidden_size / num_attention_heads; + let all_head_size = num_attention_heads * attention_head_size; + let query = linear(hidden_size, all_head_size, vb.pp("query"))?; + let key = linear(hidden_size, all_head_size, vb.pp("key"))?; + let value = linear(hidden_size, all_head_size, vb.pp("value"))?; + let (sr, layer_norm) = if sequence_reduction_ratio > 1 { + ( + Some(conv2d( + hidden_size, + hidden_size, + sequence_reduction_ratio, + Conv2dConfig { + stride: sequence_reduction_ratio, + ..Default::default() + }, + vb.pp("sr"), + )?), + Some(candle_nn::layer_norm( + hidden_size, + config.layer_norm_eps, + vb.pp("layer_norm"), + )?), + ) + } else { + (None, None) + }; + Ok(Self { + num_attention_heads, + attention_head_size, + query, + key, + value, + sr, + layer_norm, + }) + } + + fn transpose_for_scores(&self, hidden_states: Tensor) -> Result { + let (batch, seq_length, _) = hidden_states.shape().dims3()?; + let new_shape = &[ + batch, + seq_length, + self.num_attention_heads, + self.attention_head_size, + ]; + let hidden_states = hidden_states.reshape(new_shape)?; + let hidden_states = hidden_states.permute((0, 2, 1, 3))?; + Ok(hidden_states) + } +} + +impl Module for SegformerEfficientSelfAttention { + fn forward(&self, x: &Tensor) -> Result { + // [B, C, H, W] -> [B, H * W, C] + let hidden_states = x.flatten_from(2)?.permute((0, 2, 1))?; + let query = self + .transpose_for_scores(self.query.forward(&hidden_states)?)? + .contiguous()?; + let hidden_states = if let (Some(sr), Some(layer_norm)) = (&self.sr, &self.layer_norm) { + let hidden_states = sr.forward(x)?; + // [B, C, H, W] -> [B, H * W, C] + let hidden_states = hidden_states.flatten_from(2)?.permute((0, 2, 1))?; + layer_norm.forward(&hidden_states)? + } else { + // already [B, H * W, C] + hidden_states + }; + // standard self-attention + let key = self + .transpose_for_scores(self.key.forward(&hidden_states)?)? + .contiguous()?; + let value = self + .transpose_for_scores(self.value.forward(&hidden_states)?)? + .contiguous()?; + let attention_scores = + (query.matmul(&key.t()?)? / f64::sqrt(self.attention_head_size as f64))?; + let attention_scores = candle_nn::ops::softmax_last_dim(&attention_scores)?; + let result = attention_scores.matmul(&value)?; + let result = result.permute((0, 2, 1, 3))?.contiguous()?; + result.flatten_from(D::Minus2) + } +} + +#[derive(Debug, Clone)] +struct SegformerSelfOutput { + dense: Linear, +} + +impl SegformerSelfOutput { + fn new(hidden_size: usize, vb: VarBuilder) -> Result { + let dense = linear(hidden_size, hidden_size, vb.pp("dense"))?; + Ok(Self { dense }) + } +} + +impl Module for SegformerSelfOutput { + fn forward(&self, x: &Tensor) -> Result { + self.dense.forward(x) + } +} + +#[derive(Debug, Clone)] +struct SegformerAttention { + attention: SegformerEfficientSelfAttention, + output: SegformerSelfOutput, +} + +impl SegformerAttention { + fn new( + config: &Config, + hidden_size: usize, + num_attention_heads: usize, + sequence_reduction_ratio: usize, + vb: VarBuilder, + ) -> Result { + let attention = SegformerEfficientSelfAttention::new( + config, + hidden_size, + num_attention_heads, + sequence_reduction_ratio, + vb.pp("self"), + )?; + let output = SegformerSelfOutput::new(hidden_size, vb.pp("output"))?; + Ok(Self { attention, output }) + } +} + +impl Module for SegformerAttention { + fn forward(&self, x: &Tensor) -> Result { + let attention_output = self.attention.forward(x)?; + self.output.forward(&attention_output) + } +} + +#[derive(Debug, Clone)] +struct SegformerDWConv { + dw_conv: Conv2d, +} + +impl SegformerDWConv { + fn new(dim: usize, vb: VarBuilder) -> Result { + let dw_conv = conv2d( + dim, + dim, + 3, + Conv2dConfig { + stride: 1, + padding: 1, + groups: dim, + ..Default::default() + }, + vb.pp("dwconv"), + )?; + Ok(Self { dw_conv }) + } +} + +impl Module for SegformerDWConv { + fn forward(&self, x: &Tensor) -> Result { + self.dw_conv.forward(x) + } +} + +#[derive(Debug, Clone)] +struct SegformerMixFFN { + dense1: Linear, + dw_conv: SegformerDWConv, + act: Activation, + dense2: Linear, +} + +impl SegformerMixFFN { + fn new( + config: &Config, + in_features: usize, + hidden_features: usize, + out_features: usize, + vb: VarBuilder, + ) -> Result { + let dense1 = linear(in_features, hidden_features, vb.pp("dense1"))?; + let dw_conv = SegformerDWConv::new(hidden_features, vb.pp("dwconv"))?; + let act = config.hidden_act; + let dense2 = linear(hidden_features, out_features, vb.pp("dense2"))?; + Ok(Self { + dense1, + dw_conv, + act, + dense2, + }) + } +} + +impl Module for SegformerMixFFN { + fn forward(&self, x: &Tensor) -> Result { + let (batch, _, height, width) = x.shape().dims4()?; + let hidden_states = self + .dense1 + .forward(&x.flatten_from(2)?.permute((0, 2, 1))?)?; + let channels = hidden_states.dim(2)?; + let hidden_states = self.dw_conv.forward( + &hidden_states + .permute((0, 2, 1))? + .reshape((batch, channels, height, width))?, + )?; + let hidden_states = self.act.forward(&hidden_states)?; + let hidden_states = self + .dense2 + .forward(&hidden_states.flatten_from(2)?.permute((0, 2, 1))?)?; + let channels = hidden_states.dim(2)?; + hidden_states + .permute((0, 2, 1))? + .reshape((batch, channels, height, width)) + } +} + +#[derive(Debug, Clone)] +struct SegformerLayer { + layer_norm_1: candle_nn::LayerNorm, + attention: SegformerAttention, + layer_norm_2: candle_nn::LayerNorm, + mlp: SegformerMixFFN, +} + +impl SegformerLayer { + fn new( + config: &Config, + hidden_size: usize, + num_attention_heads: usize, + sequence_reduction_ratio: usize, + mlp_ratio: usize, + vb: VarBuilder, + ) -> Result { + let layer_norm_1 = layer_norm(hidden_size, config.layer_norm_eps, vb.pp("layer_norm_1"))?; + let attention = SegformerAttention::new( + config, + hidden_size, + num_attention_heads, + sequence_reduction_ratio, + vb.pp("attention"), + )?; + let layer_norm_2 = layer_norm(hidden_size, config.layer_norm_eps, vb.pp("layer_norm_2"))?; + let mlp = SegformerMixFFN::new( + config, + hidden_size, + hidden_size * mlp_ratio, + hidden_size, + vb.pp("mlp"), + )?; + Ok(Self { + layer_norm_1, + attention, + layer_norm_2, + mlp, + }) + } +} + +impl Module for SegformerLayer { + fn forward(&self, x: &Tensor) -> Result { + let shape = x.shape().dims4()?; + // [B, C, H, W] -> [B, H * W, C] + let hidden_states = x.flatten_from(2)?.permute((0, 2, 1))?; + let layer_norm_output = self.layer_norm_1.forward(&hidden_states)?; + let layer_norm_output = layer_norm_output.permute((0, 2, 1))?.reshape(shape)?; + // attention takes in [B, C, H, W] in order to properly do conv2d (and output [B, H * W, C]) + let attention_output = self.attention.forward(&layer_norm_output)?; + let hidden_states = (attention_output + hidden_states)?; + let layer_norm_output = self.layer_norm_2.forward(&hidden_states)?; + let mlp_output = self + .mlp + .forward(&layer_norm_output.permute((0, 2, 1))?.reshape(shape)?)?; + hidden_states.permute((0, 2, 1))?.reshape(shape)? + mlp_output + } +} + +#[derive(Debug, Clone)] +struct SegformerEncoder { + /// config file + config: Config, + /// a list of embeddings + patch_embeddings: Vec, + /// a list of attention blocks, each consisting of layers + blocks: Vec>, + /// a final list of layer norms + layer_norms: Vec, +} + +impl SegformerEncoder { + fn new(config: Config, vb: VarBuilder) -> Result { + let mut patch_embeddings = Vec::with_capacity(config.num_encoder_blocks); + let mut blocks = Vec::with_capacity(config.num_encoder_blocks); + let mut layer_norms = Vec::with_capacity(config.num_encoder_blocks); + for i in 0..config.num_encoder_blocks { + let patch_size = config.patch_sizes[i]; + let stride = config.strides[i]; + let hidden_size = config.hidden_sizes[i]; + let num_channels = if i == 0 { + config.num_channels + } else { + config.hidden_sizes[i - 1] + }; + patch_embeddings.push(SegformerOverlapPatchEmbeddings::new( + &config, + patch_size, + stride, + num_channels, + hidden_size, + vb.pp(&format!("patch_embeddings.{}", i)), + )?); + let mut layers = Vec::with_capacity(config.depths[i]); + for j in 0..config.depths[i] { + let sequence_reduction_ratio = config.sr_ratios[i]; + let num_attention_heads = config.num_attention_heads[i]; + let mlp_ratio = config.mlp_ratios[i]; + layers.push(SegformerLayer::new( + &config, + hidden_size, + num_attention_heads, + sequence_reduction_ratio, + mlp_ratio, + vb.pp(&format!("block.{}.{}", i, j)), + )?); + } + blocks.push(layers); + layer_norms.push(layer_norm( + hidden_size, + config.layer_norm_eps, + vb.pp(&format!("layer_norm.{}", i)), + )?); + } + Ok(Self { + config, + patch_embeddings, + blocks, + layer_norms, + }) + } +} + +impl ModuleWithHiddenStates for SegformerEncoder { + fn forward(&self, x: &Tensor) -> Result> { + let mut all_hidden_states = Vec::with_capacity(self.config.num_encoder_blocks); + let mut hidden_states = x.clone(); + for i in 0..self.config.num_encoder_blocks { + hidden_states = self.patch_embeddings[i].forward(&hidden_states)?; + for layer in &self.blocks[i] { + hidden_states = layer.forward(&hidden_states)?; + } + let shape = hidden_states.shape().dims4()?; + hidden_states = + self.layer_norms[i].forward(&hidden_states.flatten_from(2)?.permute((0, 2, 1))?)?; + hidden_states = hidden_states.permute((0, 2, 1))?.reshape(shape)?; + all_hidden_states.push(hidden_states.clone()); + } + Ok(all_hidden_states) + } +} + +#[derive(Debug, Clone)] +struct SegformerModel { + encoder: SegformerEncoder, +} + +impl SegformerModel { + fn new(config: &Config, vb: VarBuilder) -> Result { + let encoder = SegformerEncoder::new(config.clone(), vb.pp("encoder"))?; + Ok(Self { encoder }) + } +} + +impl ModuleWithHiddenStates for SegformerModel { + fn forward(&self, x: &Tensor) -> Result> { + self.encoder.forward(x) + } +} + +#[derive(Debug, Clone)] +struct SegformerMLP { + proj: Linear, +} + +impl SegformerMLP { + fn new(config: &Config, input_dim: usize, vb: VarBuilder) -> Result { + let proj = linear(input_dim, config.decoder_hidden_size, vb.pp("proj"))?; + Ok(Self { proj }) + } +} + +impl Module for SegformerMLP { + fn forward(&self, x: &Tensor) -> Result { + self.proj.forward(x) + } +} + +#[derive(Debug, Clone)] +struct SegformerDecodeHead { + linear_c: Vec, + linear_fuse: candle_nn::Conv2d, + batch_norm: candle_nn::BatchNorm, + classifier: candle_nn::Conv2d, +} + +impl SegformerDecodeHead { + fn new(config: &Config, num_labels: usize, vb: VarBuilder) -> Result { + let mut linear_c = Vec::with_capacity(config.num_encoder_blocks); + for i in 0..config.num_encoder_blocks { + let hidden_size = config.hidden_sizes[i]; + linear_c.push(SegformerMLP::new( + config, + hidden_size, + vb.pp(&format!("linear_c.{}", i)), + )?); + } + let linear_fuse = conv2d_no_bias( + config.decoder_hidden_size * config.num_encoder_blocks, + config.decoder_hidden_size, + 1, + Conv2dConfig::default(), + vb.pp("linear_fuse"), + )?; + let batch_norm = candle_nn::batch_norm( + config.decoder_hidden_size, + config.layer_norm_eps, + vb.pp("batch_norm"), + )?; + let classifier = conv2d_no_bias( + config.decoder_hidden_size, + num_labels, + 1, + Conv2dConfig::default(), + vb.pp("classifier"), + )?; + Ok(Self { + linear_c, + linear_fuse, + batch_norm, + classifier, + }) + } + + fn forward(&self, encoder_hidden_states: &[Tensor]) -> Result { + if encoder_hidden_states.len() != self.linear_c.len() { + candle::bail!( + "The number of encoder hidden states {} is not equal to the number of linear layers {}", + encoder_hidden_states.len(), + self.linear_c.len() + ) + } + // most fine layer + let (_, _, upsample_height, upsample_width) = encoder_hidden_states[0].shape().dims4()?; + let mut hidden_states = Vec::with_capacity(self.linear_c.len()); + for (hidden_state, mlp) in encoder_hidden_states.iter().zip(&self.linear_c) { + let (batch, _, height, width) = hidden_state.shape().dims4()?; + let hidden_state = mlp.forward(&hidden_state.flatten_from(2)?.permute((0, 2, 1))?)?; + let hidden_state = hidden_state.permute((0, 2, 1))?.reshape(( + batch, + hidden_state.dim(2)?, + height, + width, + ))?; + let hidden_state = hidden_state.upsample_nearest2d(upsample_height, upsample_width)?; + hidden_states.push(hidden_state); + } + hidden_states.reverse(); + let hidden_states = Tensor::cat(&hidden_states, 1)?; + let hidden_states = self.linear_fuse.forward(&hidden_states)?; + let hidden_states = self.batch_norm.forward_t(&hidden_states, false)?; + let hidden_states = hidden_states.relu()?; + self.classifier.forward(&hidden_states) + } +} + +trait ModuleWithHiddenStates { + fn forward(&self, xs: &Tensor) -> Result>; +} + +#[derive(Debug, Clone)] +pub struct SemanticSegmentationModel { + segformer: SegformerModel, + decode_head: SegformerDecodeHead, +} + +impl SemanticSegmentationModel { + pub fn new(config: &Config, num_labels: usize, vb: VarBuilder) -> Result { + let segformer = SegformerModel::new(config, vb.pp("segformer"))?; + let decode_head = SegformerDecodeHead::new(config, num_labels, vb.pp("decode_head"))?; + Ok(Self { + segformer, + decode_head, + }) + } +} + +impl Module for SemanticSegmentationModel { + fn forward(&self, x: &Tensor) -> Result { + let hidden_states = self.segformer.forward(x)?; + self.decode_head.forward(&hidden_states) + } +} + +#[derive(Debug, Clone)] +pub struct ImageClassificationModel { + segformer: SegformerModel, + classifier: Linear, +} + +impl ImageClassificationModel { + pub fn new(config: &Config, num_labels: usize, vb: VarBuilder) -> Result { + let segformer = SegformerModel::new(config, vb.pp("segformer"))?; + let classifier = linear(config.decoder_hidden_size, num_labels, vb.pp("classifier"))?; + Ok(Self { + segformer, + classifier, + }) + } +} + +impl Module for ImageClassificationModel { + fn forward(&self, x: &Tensor) -> Result { + let all_hidden_states = self.segformer.forward(x)?; + let hidden_states = all_hidden_states.last().unwrap(); + let hidden_states = hidden_states.flatten_from(2)?.permute((0, 2, 1))?; + let mean = hidden_states.mean(1)?; + self.classifier.forward(&mean) + } +} + +#[cfg(test)] +mod tests { + + use super::*; + + #[test] + fn test_config_json_load() { + let raw_json = r#"{ + "architectures": [ + "SegformerForImageClassification" + ], + "attention_probs_dropout_prob": 0.0, + "classifier_dropout_prob": 0.1, + "decoder_hidden_size": 256, + "depths": [ + 2, + 2, + 2, + 2 + ], + "downsampling_rates": [ + 1, + 4, + 8, + 16 + ], + "drop_path_rate": 0.1, + "hidden_act": "gelu", + "hidden_dropout_prob": 0.0, + "hidden_sizes": [ + 32, + 64, + 160, + 256 + ], + "image_size": 224, + "initializer_range": 0.02, + "layer_norm_eps": 1e-06, + "mlp_ratios": [ + 4, + 4, + 4, + 4 + ], + "model_type": "segformer", + "num_attention_heads": [ + 1, + 2, + 5, + 8 + ], + "num_channels": 3, + "num_encoder_blocks": 4, + "patch_sizes": [ + 7, + 3, + 3, + 3 + ], + "sr_ratios": [ + 8, + 4, + 2, + 1 + ], + "strides": [ + 4, + 2, + 2, + 2 + ], + "torch_dtype": "float32", + "transformers_version": "4.12.0.dev0" + }"#; + let config: Config = serde_json::from_str(raw_json).unwrap(); + assert_eq!(vec![4, 2, 2, 2], config.strides); + assert_eq!(1e-6, config.layer_norm_eps); + assert_eq!(Config::default(), config); + } +}