1use crate::container::Sequential;
10use crate::layers::activation::SiLU;
11use crate::layers::blocks::MBConvBlock;
12use crate::layers::{BatchNorm2d, Conv2d, Dropout, Linear};
13use crate::{Module, ModuleBase, Parameter};
14use torsh_core::device::DeviceType;
15
16#[cfg(feature = "std")]
18use std::collections::HashMap;
19
20#[cfg(not(feature = "std"))]
21use hashbrown::HashMap;
22use torsh_core::error::Result;
23use torsh_tensor::Tensor;
24
25#[derive(Debug, Clone)]
27pub struct EfficientNetConfig {
28 pub width_multiplier: f32,
30 pub depth_multiplier: f32,
32 pub input_resolution: usize,
34 pub dropout_rate: f32,
36 pub stochastic_depth_rate: f32,
38 pub num_classes: usize,
40}
41
42impl EfficientNetConfig {
43 pub fn b0() -> Self {
45 Self {
46 width_multiplier: 1.0,
47 depth_multiplier: 1.0,
48 input_resolution: 224,
49 dropout_rate: 0.2,
50 stochastic_depth_rate: 0.2,
51 num_classes: 1000,
52 }
53 }
54
55 pub fn b1() -> Self {
57 Self {
58 width_multiplier: 1.0,
59 depth_multiplier: 1.1,
60 input_resolution: 240,
61 dropout_rate: 0.2,
62 stochastic_depth_rate: 0.2,
63 num_classes: 1000,
64 }
65 }
66
67 pub fn b2() -> Self {
69 Self {
70 width_multiplier: 1.1,
71 depth_multiplier: 1.2,
72 input_resolution: 260,
73 dropout_rate: 0.3,
74 stochastic_depth_rate: 0.2,
75 num_classes: 1000,
76 }
77 }
78
79 pub fn b3() -> Self {
81 Self {
82 width_multiplier: 1.2,
83 depth_multiplier: 1.4,
84 input_resolution: 300,
85 dropout_rate: 0.3,
86 stochastic_depth_rate: 0.2,
87 num_classes: 1000,
88 }
89 }
90
91 pub fn b4() -> Self {
93 Self {
94 width_multiplier: 1.4,
95 depth_multiplier: 1.8,
96 input_resolution: 380,
97 dropout_rate: 0.4,
98 stochastic_depth_rate: 0.2,
99 num_classes: 1000,
100 }
101 }
102
103 pub fn b5() -> Self {
105 Self {
106 width_multiplier: 1.6,
107 depth_multiplier: 2.2,
108 input_resolution: 456,
109 dropout_rate: 0.4,
110 stochastic_depth_rate: 0.2,
111 num_classes: 1000,
112 }
113 }
114
115 pub fn b6() -> Self {
117 Self {
118 width_multiplier: 1.8,
119 depth_multiplier: 2.6,
120 input_resolution: 528,
121 dropout_rate: 0.5,
122 stochastic_depth_rate: 0.2,
123 num_classes: 1000,
124 }
125 }
126
127 pub fn b7() -> Self {
129 Self {
130 width_multiplier: 2.0,
131 depth_multiplier: 3.1,
132 input_resolution: 600,
133 dropout_rate: 0.5,
134 stochastic_depth_rate: 0.2,
135 num_classes: 1000,
136 }
137 }
138
139 pub fn scale_width(&self, base_width: usize) -> usize {
141 let scaled = (base_width as f32 * self.width_multiplier) as usize;
142 ((scaled + 4) / 8) * 8
144 }
145
146 pub fn scale_depth(&self, base_depth: usize) -> usize {
148 (base_depth as f32 * self.depth_multiplier).ceil() as usize
149 }
150}
151
152pub struct MBConvWithStochasticDepth {
154 base: ModuleBase,
155 mbconv: MBConvBlock,
156 stochastic_depth_prob: f32,
157 use_shortcut: bool,
158}
159
160impl MBConvWithStochasticDepth {
161 pub fn new(
163 in_channels: usize,
164 out_channels: usize,
165 kernel_size: usize,
166 stride: usize,
167 expansion_ratio: usize,
168 se_ratio: f32,
169 stochastic_depth_prob: f32,
170 ) -> Result<Self> {
171 let mbconv = MBConvBlock::new(
172 in_channels,
173 out_channels,
174 kernel_size,
175 stride,
176 expansion_ratio,
177 Some(se_ratio),
178 0.0, )?;
180
181 let use_shortcut = stride == 1 && in_channels == out_channels;
182
183 Ok(Self {
184 base: ModuleBase::new(),
185 mbconv,
186 stochastic_depth_prob,
187 use_shortcut,
188 })
189 }
190
191 fn apply_stochastic_depth(&self, x: &Tensor, residual: &Tensor) -> Result<Tensor> {
193 if !self.training() || self.stochastic_depth_prob == 0.0 {
194 return x.add_op(residual);
195 }
196
197 let keep_prob = 1.0 - self.stochastic_depth_prob;
199
200 let scaled_x = x.mul_scalar(keep_prob)?;
203 scaled_x.add_op(residual)
204 }
205}
206
207impl Module for MBConvWithStochasticDepth {
208 fn forward(&self, input: &Tensor) -> Result<Tensor> {
209 let output = self.mbconv.forward(input)?;
210
211 if self.use_shortcut {
212 self.apply_stochastic_depth(&output, input)
213 } else {
214 Ok(output)
215 }
216 }
217
218 fn parameters(&self) -> HashMap<String, Parameter> {
219 self.mbconv.parameters()
220 }
221
222 fn named_parameters(&self) -> HashMap<String, Parameter> {
223 self.mbconv.named_parameters()
224 }
225
226 fn training(&self) -> bool {
227 self.base.training()
228 }
229
230 fn train(&mut self) {
231 self.base.set_training(true);
232 }
234
235 fn eval(&mut self) {
236 self.base.set_training(false);
237 }
239
240 fn set_training(&mut self, training: bool) {
241 self.base.set_training(training);
242 }
244
245 fn to_device(&mut self, device: DeviceType) -> Result<()> {
246 self.base.to_device(device)
247 }
249}
250
251#[derive(Debug, Clone)]
253pub struct BlockConfig {
254 pub input_filters: usize,
256 pub output_filters: usize,
258 pub kernel_size: usize,
260 pub stride: usize,
262 pub expansion_ratio: usize,
264 pub se_ratio: f32,
266 pub num_repeat: usize,
268}
269
270impl BlockConfig {
271 pub fn scale(&self, config: &EfficientNetConfig) -> Self {
273 Self {
274 input_filters: config.scale_width(self.input_filters),
275 output_filters: config.scale_width(self.output_filters),
276 kernel_size: self.kernel_size,
277 stride: self.stride,
278 expansion_ratio: self.expansion_ratio,
279 se_ratio: self.se_ratio,
280 num_repeat: config.scale_depth(self.num_repeat),
281 }
282 }
283}
284
285pub struct EfficientNetBackbone {
287 base: ModuleBase,
288 stem: Sequential,
289 blocks: Vec<MBConvWithStochasticDepth>,
290 head: Sequential,
291 #[allow(dead_code)]
292 config: EfficientNetConfig,
293}
294
295impl EfficientNetBackbone {
296 pub fn new(config: EfficientNetConfig) -> Result<Self> {
298 let base_blocks = Self::get_base_block_configs();
299 let scaled_blocks: Vec<BlockConfig> = base_blocks
300 .iter()
301 .map(|block| block.scale(&config))
302 .collect();
303
304 let base = ModuleBase::new();
305
306 let stem_channels = config.scale_width(32);
308 let stem = Sequential::new()
309 .add(Conv2d::new(
310 3,
311 stem_channels,
312 (3, 3),
313 (2, 2),
314 (1, 1),
315 (1, 1),
316 false,
317 1,
318 ))
319 .add(BatchNorm2d::new(stem_channels)?)
320 .add(SiLU::new());
321
322 let mut blocks = Vec::new();
324 let mut total_blocks = 0;
325 for block_config in &scaled_blocks {
326 total_blocks += block_config.num_repeat;
327 }
328
329 let mut block_index = 0;
330 for block_config in &scaled_blocks {
331 for i in 0..block_config.num_repeat {
332 let input_filters = if i == 0 {
333 block_config.input_filters
334 } else {
335 block_config.output_filters
336 };
337
338 let stride = if i == 0 { block_config.stride } else { 1 };
339
340 let stochastic_depth_prob =
342 config.stochastic_depth_rate * (block_index as f32 / total_blocks as f32);
343
344 let block = MBConvWithStochasticDepth::new(
345 input_filters,
346 block_config.output_filters,
347 block_config.kernel_size,
348 stride,
349 block_config.expansion_ratio,
350 block_config.se_ratio,
351 stochastic_depth_prob,
352 );
353
354 blocks.push(block?);
355 block_index += 1;
356 }
357 }
358
359 let head_channels = config.scale_width(1280);
361 let last_block_channels = scaled_blocks
362 .last()
363 .expect("scaled_blocks should not be empty")
364 .output_filters;
365 let head = Sequential::new()
366 .add(Conv2d::new(
367 last_block_channels,
368 head_channels,
369 (1, 1),
370 (1, 1),
371 (0, 0),
372 (1, 1),
373 false,
374 1,
375 ))
376 .add(BatchNorm2d::new(head_channels)?)
377 .add(SiLU::new());
378
379 Ok(Self {
380 base,
381 stem,
382 blocks,
383 head,
384 config,
385 })
386 }
387
388 fn get_base_block_configs() -> Vec<BlockConfig> {
390 vec![
391 BlockConfig {
392 input_filters: 32,
393 output_filters: 16,
394 kernel_size: 3,
395 stride: 1,
396 expansion_ratio: 1,
397 se_ratio: 0.25,
398 num_repeat: 1,
399 },
400 BlockConfig {
401 input_filters: 16,
402 output_filters: 24,
403 kernel_size: 3,
404 stride: 2,
405 expansion_ratio: 6,
406 se_ratio: 0.25,
407 num_repeat: 2,
408 },
409 BlockConfig {
410 input_filters: 24,
411 output_filters: 40,
412 kernel_size: 5,
413 stride: 2,
414 expansion_ratio: 6,
415 se_ratio: 0.25,
416 num_repeat: 2,
417 },
418 BlockConfig {
419 input_filters: 40,
420 output_filters: 80,
421 kernel_size: 3,
422 stride: 2,
423 expansion_ratio: 6,
424 se_ratio: 0.25,
425 num_repeat: 3,
426 },
427 BlockConfig {
428 input_filters: 80,
429 output_filters: 112,
430 kernel_size: 5,
431 stride: 1,
432 expansion_ratio: 6,
433 se_ratio: 0.25,
434 num_repeat: 3,
435 },
436 BlockConfig {
437 input_filters: 112,
438 output_filters: 192,
439 kernel_size: 5,
440 stride: 2,
441 expansion_ratio: 6,
442 se_ratio: 0.25,
443 num_repeat: 4,
444 },
445 BlockConfig {
446 input_filters: 192,
447 output_filters: 320,
448 kernel_size: 3,
449 stride: 1,
450 expansion_ratio: 6,
451 se_ratio: 0.25,
452 num_repeat: 1,
453 },
454 ]
455 }
456
457 pub fn extract_features(&self, input: &Tensor) -> Result<Tensor> {
459 let mut x = self.stem.forward(input)?;
460
461 for block in &self.blocks {
462 x = block.forward(&x)?;
463 }
464
465 self.head.forward(&x)
466 }
467}
468
469impl Module for EfficientNetBackbone {
470 fn forward(&self, input: &Tensor) -> Result<Tensor> {
471 self.extract_features(input)
472 }
473
474 fn parameters(&self) -> HashMap<String, Parameter> {
475 let mut params = HashMap::new();
476
477 for (name, param) in self.stem.parameters() {
479 params.insert(format!("stem.{}", name), param);
480 }
481
482 for (i, block) in self.blocks.iter().enumerate() {
484 for (name, param) in block.parameters() {
485 params.insert(format!("blocks.{}.{}", i, name), param);
486 }
487 }
488
489 for (name, param) in self.head.parameters() {
491 params.insert(format!("head.{}", name), param);
492 }
493
494 params
495 }
496
497 fn named_parameters(&self) -> HashMap<String, Parameter> {
498 self.parameters()
499 }
500
501 fn training(&self) -> bool {
502 self.base.training()
503 }
504
505 fn train(&mut self) {
506 self.base.set_training(true);
507 }
508
509 fn eval(&mut self) {
510 self.base.set_training(false);
511 }
512
513 fn set_training(&mut self, training: bool) {
514 self.base.set_training(training);
515 }
516
517 fn to_device(&mut self, device: DeviceType) -> Result<()> {
518 self.base.to_device(device)
519 }
520}
521
522pub struct EfficientNet {
524 base: ModuleBase,
525 backbone: EfficientNetBackbone,
526 classifier: Sequential,
527 #[allow(dead_code)]
528 config: EfficientNetConfig,
529}
530
531impl EfficientNet {
532 pub fn new(config: EfficientNetConfig) -> Result<Self> {
534 let backbone = EfficientNetBackbone::new(config.clone())?;
535
536 let head_channels = config.scale_width(1280);
538 let classifier = Sequential::new()
539 .add(Dropout::new(config.dropout_rate))
540 .add(Linear::new(head_channels, config.num_classes, true));
541
542 Ok(Self {
543 base: ModuleBase::new(),
544 backbone,
545 classifier,
546 config,
547 })
548 }
549
550 pub fn b0(num_classes: usize) -> Result<Self> {
552 let mut config = EfficientNetConfig::b0();
553 config.num_classes = num_classes;
554 Self::new(config)
555 }
556
557 pub fn b1(num_classes: usize) -> Result<Self> {
559 let mut config = EfficientNetConfig::b1();
560 config.num_classes = num_classes;
561 Self::new(config)
562 }
563
564 pub fn b2(num_classes: usize) -> Result<Self> {
566 let mut config = EfficientNetConfig::b2();
567 config.num_classes = num_classes;
568 Self::new(config)
569 }
570
571 pub fn b3(num_classes: usize) -> Result<Self> {
573 let mut config = EfficientNetConfig::b3();
574 config.num_classes = num_classes;
575 Self::new(config)
576 }
577
578 pub fn b4(num_classes: usize) -> Result<Self> {
580 let mut config = EfficientNetConfig::b4();
581 config.num_classes = num_classes;
582 Self::new(config)
583 }
584
585 pub fn b5(num_classes: usize) -> Result<Self> {
587 let mut config = EfficientNetConfig::b5();
588 config.num_classes = num_classes;
589 Self::new(config)
590 }
591
592 pub fn b6(num_classes: usize) -> Result<Self> {
594 let mut config = EfficientNetConfig::b6();
595 config.num_classes = num_classes;
596 Self::new(config)
597 }
598
599 pub fn b7(num_classes: usize) -> Result<Self> {
601 let mut config = EfficientNetConfig::b7();
602 config.num_classes = num_classes;
603 Self::new(config)
604 }
605
606 pub fn backbone(&self) -> &EfficientNetBackbone {
608 &self.backbone
609 }
610
611 pub fn extract_features(&self, input: &Tensor) -> Result<Tensor> {
613 self.backbone.extract_features(input)
614 }
615}
616
617impl Module for EfficientNet {
618 fn forward(&self, input: &Tensor) -> Result<Tensor> {
619 let features = self.backbone.forward(input)?;
620
621 let features_shape = features.shape();
623 let shape = features_shape.dims();
624 let batch_size = shape[0];
625 let channels = shape[1];
626 let height = shape[2];
627 let width = shape[3];
628
629 let mut pooled_data = vec![0.0f32; batch_size * channels];
631 let features_data = features.to_vec()?;
632
633 for b in 0..batch_size {
634 for c in 0..channels {
635 let mut sum = 0.0f32;
636 for h in 0..height {
637 for w in 0..width {
638 let idx =
639 b * (channels * height * width) + c * (height * width) + h * width + w;
640 if idx < features_data.len() {
641 sum += features_data[idx];
642 }
643 }
644 }
645 pooled_data[b * channels + c] = sum / (height * width) as f32;
646 }
647 }
648
649 let pooled = Tensor::from_vec(pooled_data, &[batch_size, channels])?;
651
652 self.classifier.forward(&pooled)
653 }
654
655 fn parameters(&self) -> HashMap<String, Parameter> {
656 let mut params = HashMap::new();
657
658 for (name, param) in self.backbone.parameters() {
660 params.insert(format!("backbone.{}", name), param);
661 }
662
663 for (name, param) in self.classifier.parameters() {
665 params.insert(format!("classifier.{}", name), param);
666 }
667
668 params
669 }
670
671 fn named_parameters(&self) -> HashMap<String, Parameter> {
672 self.parameters()
673 }
674
675 fn training(&self) -> bool {
676 self.base.training()
677 }
678
679 fn train(&mut self) {
680 self.base.set_training(true);
681 }
682
683 fn eval(&mut self) {
684 self.base.set_training(false);
685 }
686
687 fn set_training(&mut self, training: bool) {
688 self.base.set_training(training);
689 }
690
691 fn to_device(&mut self, device: DeviceType) -> Result<()> {
692 self.base.to_device(device)
693 }
694}
695
696pub mod utils {
698 use super::*;
699
700 pub fn calculate_model_params(config: &EfficientNetConfig) -> (usize, f32) {
702 let base_params = 5_300_000; let width_factor = config.width_multiplier.powi(2);
705 let depth_factor = config.depth_multiplier;
706
707 let estimated_params = (base_params as f32 * width_factor * depth_factor) as usize;
708 let estimated_flops = estimated_params as f32 * config.input_resolution as f32 * 0.1;
709
710 (estimated_params, estimated_flops)
711 }
712
713 pub fn get_training_config(config: &EfficientNetConfig) -> TrainingConfig {
715 match config.width_multiplier {
716 w if w <= 1.0 => TrainingConfig {
717 batch_size: 128,
718 learning_rate: 0.256,
719 weight_decay: 1e-5,
720 epochs: 350,
721 },
722 w if w <= 1.4 => TrainingConfig {
723 batch_size: 64,
724 learning_rate: 0.128,
725 weight_decay: 1e-5,
726 epochs: 350,
727 },
728 _ => TrainingConfig {
729 batch_size: 32,
730 learning_rate: 0.064,
731 weight_decay: 1e-5,
732 epochs: 350,
733 },
734 }
735 }
736
737 pub fn create_custom_efficientnet(
739 width_multiplier: f32,
740 depth_multiplier: f32,
741 resolution: usize,
742 num_classes: usize,
743 ) -> Result<EfficientNet> {
744 let config = EfficientNetConfig {
745 width_multiplier,
746 depth_multiplier,
747 input_resolution: resolution,
748 dropout_rate: 0.2 + (width_multiplier - 1.0) * 0.3,
749 stochastic_depth_rate: 0.2,
750 num_classes,
751 };
752
753 EfficientNet::new(config)
754 }
755}
756
757#[derive(Debug, Clone)]
759pub struct TrainingConfig {
760 pub batch_size: usize,
761 pub learning_rate: f32,
762 pub weight_decay: f32,
763 pub epochs: usize,
764}
765
766#[cfg(test)]
767mod tests {
768 use super::*;
769 use torsh_tensor::creation::*;
770
771 #[test]
772 fn test_efficientnet_config() {
773 let config = EfficientNetConfig::b0();
774 assert_eq!(config.width_multiplier, 1.0);
775 assert_eq!(config.depth_multiplier, 1.0);
776 assert_eq!(config.input_resolution, 224);
777 }
778
779 #[test]
780 fn test_scaling() {
781 let config = EfficientNetConfig::b1();
782 assert_eq!(config.scale_width(32), 32); assert_eq!(config.scale_depth(3), 4); }
785
786 #[test]
787 fn test_block_config_scaling() {
788 let block = BlockConfig {
789 input_filters: 32,
790 output_filters: 64,
791 kernel_size: 3,
792 stride: 1,
793 expansion_ratio: 6,
794 se_ratio: 0.25,
795 num_repeat: 2,
796 };
797
798 let config = EfficientNetConfig::b2();
799 let scaled = block.scale(&config);
800
801 assert!(scaled.input_filters >= 32); assert!(scaled.output_filters >= 64); assert!(scaled.num_repeat >= 2); }
805
806 #[test]
807 fn test_efficientnet_creation() -> Result<()> {
808 let model = EfficientNet::b0(1000)?;
809 assert_eq!(model.config.num_classes, 1000);
810 Ok(())
811 }
812
813 #[test]
814 #[ignore]
815 fn test_efficientnet_forward() -> Result<()> {
816 let model = EfficientNet::b0(10)?;
817 let input = ones(&[1, 3, 224, 224])?;
818
819 let output = model.forward(&input)?;
820 assert_eq!(output.shape().dims(), &[1, 10]);
821
822 Ok(())
823 }
824
825 #[test]
826 fn test_mbconv_with_stochastic_depth() -> Result<()> {
827 let block = MBConvWithStochasticDepth::new(32, 32, 3, 1, 6, 0.25, 0.1)?;
828 let input = ones(&[1, 32, 56, 56])?;
829
830 let output = block.forward(&input)?;
831 assert_eq!(output.shape().dims(), &[1, 32, 56, 56]);
832
833 Ok(())
834 }
835
836 #[test]
837 fn test_utils_calculate_params() {
838 let config = EfficientNetConfig::b0();
839 let (params, flops) = utils::calculate_model_params(&config);
840
841 assert!(params > 1_000_000); assert!(flops > 1_000_000.0); }
844
845 #[test]
846 fn test_custom_efficientnet() -> Result<()> {
847 let model = utils::create_custom_efficientnet(1.2, 1.4, 256, 100)?;
848 assert_eq!(model.config.num_classes, 100);
849 assert_eq!(model.config.input_resolution, 256);
850 Ok(())
851 }
852}