Struct BatchNorm

Source
pub struct BatchNorm<F: Float + Debug + Send + Sync> { /* private fields */ }
Expand description

Batch Normalization layer

Implements batch normalization as described in “Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift” by Ioffe and Szegedy.

This normalization is applied along the feature dimension (channel dimension for CNNs) over a mini-batch of data.

§Examples

use scirs2_neural::layers::{BatchNorm, Layer};
use ndarray::{Array, Array4};
use rand::rngs::SmallRng;
use rand::SeedableRng;

// Create a batch normalization layer for 3 channels
let mut rng = SmallRng::seed_from_u64(42);
let batch_norm = BatchNorm::new(3, 0.9, 1e-5, &mut rng).unwrap();

// Forward pass with a batch of 2 samples, 3 channels, 4x4 spatial dimensions
let batch_size = 2;
let channels = 3;
let height = 4;
let width = 4;
let input = Array4::<f64>::from_elem((batch_size, channels, height, width), 0.1).into_dyn();
let output = batch_norm.forward(&input).unwrap();

// Output shape should match input shape
assert_eq!(output.shape(), input.shape());

Implementations§

Source§

impl<F: Float + Debug + ScalarOperand + Send + Sync + 'static> BatchNorm<F>

Source

pub fn new<R: Rng>( num_features: usize, momentum: f64, eps: f64, _rng: &mut R, ) -> Result<Self>

Create a new batch normalization layer

§Arguments
  • num_features - Number of features/channels to normalize
  • momentum - Momentum for running mean/variance updates (default: 0.9)
  • eps - Small constant for numerical stability (default: 1e-5)
  • rng - Random number generator for initialization
§Returns
  • A new batch normalization layer
Examples found in repository?
examples/semantic_segmentation_complete.rs (line 194)
184    pub fn new(in_channels: usize, out_channels: usize, rng: &mut SmallRng) -> StdResult<Self> {
185        Ok(Self {
186            conv1: Conv2D::new(
187                in_channels,
188                out_channels,
189                (3, 3),
190                (1, 1),
191                PaddingMode::Same,
192                rng,
193            )?,
194            bn1: BatchNorm::new(out_channels, 0.1, 1e-5, rng)?,
195            conv2: Conv2D::new(
196                out_channels,
197                out_channels,
198                (3, 3),
199                (1, 1),
200                PaddingMode::Same,
201                rng,
202            )?,
203            bn2: BatchNorm::new(out_channels, 0.1, 1e-5, rng)?,
204            pool: MaxPool2D::new((2, 2), (2, 2), None)?,
205        })
206    }
207
208    pub fn forward(&self, input: &ArrayD<f32>) -> StdResult<(ArrayD<f32>, ArrayD<f32>)> {
209        // First conv + bn + relu
210        let x = self.conv1.forward(input)?;
211        let x = self.bn1.forward(&x)?;
212        // Note: In practice, add ReLU activation here
213
214        // Second conv + bn + relu
215        let x = self.conv2.forward(&x)?;
216        let skip = self.bn2.forward(&x)?;
217        // Note: In practice, add ReLU activation here
218
219        // Pooling for downsampling
220        let pooled = self.pool.forward(&skip)?;
221
222        Ok((pooled, skip))
223    }
224}
225
226/// U-Net style decoder block
227pub struct DecoderBlock {
228    conv1: Conv2D<f32>,
229    bn1: BatchNorm<f32>,
230    conv2: Conv2D<f32>,
231    bn2: BatchNorm<f32>,
232}
233
234impl DecoderBlock {
235    pub fn new(in_channels: usize, out_channels: usize, rng: &mut SmallRng) -> StdResult<Self> {
236        Ok(Self {
237            conv1: Conv2D::new(
238                in_channels,
239                out_channels,
240                (3, 3),
241                (1, 1),
242                PaddingMode::Same,
243                rng,
244            )?,
245            bn1: BatchNorm::new(out_channels, 0.1, 1e-5, rng)?,
246            conv2: Conv2D::new(
247                out_channels,
248                out_channels,
249                (3, 3),
250                (1, 1),
251                PaddingMode::Same,
252                rng,
253            )?,
254            bn2: BatchNorm::new(out_channels, 0.1, 1e-5, rng)?,
255        })
256    }
257
258    pub fn forward(
259        &self,
260        input: &ArrayD<f32>,
261        skip: Option<&ArrayD<f32>>,
262    ) -> StdResult<ArrayD<f32>> {
263        // Upsample input (simplified - in practice use transpose convolution)
264        let upsampled = self.upsample(input)?;
265
266        // Concatenate with skip connection if provided
267        let x = if let Some(skip_tensor) = skip {
268            self.concatenate(&upsampled, skip_tensor)?
269        } else {
270            upsampled
271        };
272
273        // First conv + bn + relu
274        let x = self.conv1.forward(&x)?;
275        let x = self.bn1.forward(&x)?;
276        // Note: In practice, add ReLU activation here
277
278        // Second conv + bn + relu
279        let x = self.conv2.forward(&x)?;
280        let x = self.bn2.forward(&x)?;
281        // Note: In practice, add ReLU activation here
282
283        Ok(x)
284    }
285
286    fn upsample(&self, input: &ArrayD<f32>) -> StdResult<ArrayD<f32>> {
287        // Simplified upsampling using nearest neighbor
288        let shape = input.shape();
289        let batch_size = shape[0];
290        let channels = shape[1];
291        let height = shape[2];
292        let width = shape[3];
293
294        let mut upsampled = Array4::<f32>::zeros((batch_size, channels, height * 2, width * 2));
295
296        for b in 0..batch_size {
297            for c in 0..channels {
298                for i in 0..height {
299                    for j in 0..width {
300                        let value = input[[b, c, i, j]];
301                        upsampled[[b, c, i * 2, j * 2]] = value;
302                        upsampled[[b, c, i * 2, j * 2 + 1]] = value;
303                        upsampled[[b, c, i * 2 + 1, j * 2]] = value;
304                        upsampled[[b, c, i * 2 + 1, j * 2 + 1]] = value;
305                    }
306                }
307            }
308        }
309
310        Ok(upsampled.into_dyn())
311    }
312
313    fn concatenate(&self, input1: &ArrayD<f32>, input2: &ArrayD<f32>) -> StdResult<ArrayD<f32>> {
314        // Simplified concatenation along channel dimension
315        let shape1 = input1.shape();
316        let shape2 = input2.shape();
317
318        if shape1[0] != shape2[0] || shape1[2] != shape2[2] || shape1[3] != shape2[3] {
319            return Err("Shapes incompatible for concatenation".into());
320        }
321
322        let batch_size = shape1[0];
323        let channels1 = shape1[1];
324        let channels2 = shape2[1];
325        let height = shape1[2];
326        let width = shape1[3];
327
328        let mut concatenated =
329            Array4::<f32>::zeros((batch_size, channels1 + channels2, height, width));
330
331        // Copy first tensor
332        for b in 0..batch_size {
333            for c in 0..channels1 {
334                for i in 0..height {
335                    for j in 0..width {
336                        concatenated[[b, c, i, j]] = input1[[b, c, i, j]];
337                    }
338                }
339            }
340        }
341
342        // Copy second tensor
343        for b in 0..batch_size {
344            for c in 0..channels2 {
345                for i in 0..height {
346                    for j in 0..width {
347                        concatenated[[b, channels1 + c, i, j]] = input2[[b, c, i, j]];
348                    }
349                }
350            }
351        }
352
353        Ok(concatenated.into_dyn())
354    }
355}
356
357/// U-Net model for semantic segmentation
358pub struct UNetModel {
359    encoders: Vec<EncoderBlock>,
360    decoders: Vec<DecoderBlock>,
361    bottleneck: Sequential<f32>,
362    final_conv: Conv2D<f32>,
363    config: SegmentationConfig,
364}
365
366impl UNetModel {
367    pub fn new(config: SegmentationConfig, rng: &mut SmallRng) -> StdResult<Self> {
368        let mut encoders = Vec::new();
369        let mut decoders = Vec::new();
370
371        // Build encoder blocks
372        let mut in_channels = 3; // RGB input
373        for &out_channels in &config.encoder_channels {
374            encoders.push(EncoderBlock::new(in_channels, out_channels, rng)?);
375            in_channels = out_channels;
376        }
377
378        // Bottleneck
379        let bottleneck_channels = config.encoder_channels.last().copied().unwrap_or(512);
380        let mut bottleneck = Sequential::new();
381        bottleneck.add(Conv2D::new(
382            bottleneck_channels,
383            bottleneck_channels * 2,
384            (3, 3),
385            (1, 1),
386            PaddingMode::Same,
387            rng,
388        )?);
389        bottleneck.add(BatchNorm::new(bottleneck_channels * 2, 0.1, 1e-5, rng)?);
390        bottleneck.add(Conv2D::new(
391            bottleneck_channels * 2,
392            bottleneck_channels,
393            (3, 3),
394            (1, 1),
395            PaddingMode::Same,
396            rng,
397        )?);
398        bottleneck.add(BatchNorm::new(bottleneck_channels, 0.1, 1e-5, rng)?);
399
400        // Build decoder blocks
401        in_channels = bottleneck_channels;
402        for (i, &out_channels) in config.decoder_channels.iter().enumerate() {
403            let decoder_in_channels =
404                if config.skip_connections && i < config.encoder_channels.len() {
405                    // Skip connections come from corresponding encoder layer (in reverse order)
406                    let encoder_idx = config.encoder_channels.len() - 1 - i;
407                    in_channels + config.encoder_channels[encoder_idx]
408                } else {
409                    in_channels
410                };
411            decoders.push(DecoderBlock::new(decoder_in_channels, out_channels, rng)?);
412            in_channels = out_channels;
413        }
414
415        // Final classification layer
416        let final_channels = config.decoder_channels.last().copied().unwrap_or(32);
417        let final_conv = Conv2D::new(
418            final_channels,
419            config.num_classes,
420            (1, 1),
421            (1, 1),
422            PaddingMode::Same,
423            rng,
424        )?;
425
426        Ok(Self {
427            encoders,
428            decoders,
429            bottleneck,
430            final_conv,
431            config,
432        })
433    }
More examples
Hide additional examples
examples/model_architecture_visualization.rs (line 132)
118fn create_cnn_model(rng: &mut SmallRng) -> Result<Sequential<f32>> {
119    let mut model = Sequential::new();
120
121    // First convolutional block
122    let conv1 = Conv2D::new(
123        1,                      // input channels
124        32,                     // output channels
125        (3, 3),                 // kernel size
126        (1, 1),                 // stride
127        PaddingMode::Custom(1), // padding mode
128        rng,
129    )?;
130    model.add_layer(conv1);
131
132    let batch_norm1 = BatchNorm::new(32, 0.99, 1e-5, rng)?;
133    model.add_layer(batch_norm1);
134
135    // Second convolutional block
136    let conv2 = Conv2D::new(
137        32,                     // input channels
138        64,                     // output channels
139        (3, 3),                 // kernel size
140        (2, 2),                 // stride (downsampling)
141        PaddingMode::Custom(1), // padding mode
142        rng,
143    )?;
144    model.add_layer(conv2);
145
146    let batch_norm2 = BatchNorm::new(64, 0.99, 1e-5, rng)?;
147    model.add_layer(batch_norm2);
148
149    let dropout1 = Dropout::new(0.25, rng)?;
150    model.add_layer(dropout1);
151
152    // Flatten for fully connected layers - commented out as Flatten is not available
153    // let flatten = Flatten::new()?;
154    // model.add_layer(flatten);
155
156    // Dense layers
157    let dense1 = Dense::new(64 * 14 * 14, 128, Some("relu"), rng)?;
158    model.add_layer(dense1);
159
160    let dropout2 = Dropout::new(0.5, rng)?;
161    model.add_layer(dropout2);
162
163    // Output layer
164    let dense2 = Dense::new(128, 10, Some("softmax"), rng)?;
165    model.add_layer(dense2);
166
167    Ok(model)
168}
examples/generative_models_complete.rs (line 169)
163    pub fn new(config: GenerativeConfig, rng: &mut SmallRng) -> StdResult<Self> {
164        let (_height, _width) = config.input_size;
165
166        // Feature extraction layers
167        let mut feature_extractor = Sequential::new();
168        feature_extractor.add(Conv2D::new(1, 32, (3, 3), (2, 2), PaddingMode::Same, rng)?);
169        feature_extractor.add(BatchNorm::new(32, 1e-5, 0.1, rng)?);
170
171        feature_extractor.add(Conv2D::new(32, 64, (3, 3), (2, 2), PaddingMode::Same, rng)?);
172        feature_extractor.add(BatchNorm::new(64, 1e-5, 0.1, rng)?);
173
174        feature_extractor.add(Conv2D::new(
175            64,
176            128,
177            (3, 3),
178            (2, 2),
179            PaddingMode::Same,
180            rng,
181        )?);
182        feature_extractor.add(BatchNorm::new(128, 1e-5, 0.1, rng)?);
183
184        feature_extractor.add(AdaptiveMaxPool2D::new((4, 4), None)?);
185
186        // Calculate flattened feature size
187        let feature_size = 128 * 4 * 4;
188
189        // Mean head
190        let mut mean_head = Sequential::new();
191        mean_head.add(Dense::new(
192            feature_size,
193            config.hidden_dims[0],
194            Some("relu"),
195            rng,
196        )?);
197        mean_head.add(Dropout::new(0.2, rng)?);
198        mean_head.add(Dense::new(
199            config.hidden_dims[0],
200            config.latent_dim,
201            None,
202            rng,
203        )?);
204
205        // Log variance head
206        let mut logvar_head = Sequential::new();
207        logvar_head.add(Dense::new(
208            feature_size,
209            config.hidden_dims[0],
210            Some("relu"),
211            rng,
212        )?);
213        logvar_head.add(Dropout::new(0.2, rng)?);
214        logvar_head.add(Dense::new(
215            config.hidden_dims[0],
216            config.latent_dim,
217            None,
218            rng,
219        )?);
220
221        Ok(Self {
222            feature_extractor,
223            mean_head,
224            logvar_head,
225            config,
226        })
227    }
228
229    pub fn forward(&self, input: &ArrayD<f32>) -> StdResult<(ArrayD<f32>, ArrayD<f32>)> {
230        // Extract features
231        let features = self.feature_extractor.forward(input)?;
232
233        // Flatten features
234        let batch_size = features.shape()[0];
235        let feature_dim = features.len() / batch_size;
236        let flattened = features
237            .to_shape(IxDyn(&[batch_size, feature_dim]))?
238            .to_owned();
239
240        // Get mean and log variance
241        let mean = self.mean_head.forward(&flattened)?;
242        let logvar = self.logvar_head.forward(&flattened)?;
243
244        Ok((mean, logvar))
245    }
246}
247
248/// VAE Decoder that reconstructs images from latent codes
249pub struct VAEDecoder {
250    latent_projection: Sequential<f32>,
251    feature_layers: Sequential<f32>,
252    output_conv: Conv2D<f32>,
253    config: GenerativeConfig,
254}
255
256impl VAEDecoder {
257    pub fn new(config: GenerativeConfig, rng: &mut SmallRng) -> StdResult<Self> {
258        // Project latent to feature space
259        let mut latent_projection = Sequential::new();
260        latent_projection.add(Dense::new(
261            config.latent_dim,
262            config.hidden_dims[0],
263            Some("relu"),
264            rng,
265        )?);
266        latent_projection.add(Dense::new(
267            config.hidden_dims[0],
268            128 * 4 * 4,
269            Some("relu"),
270            rng,
271        )?);
272
273        // Feature reconstruction layers (simplified transpose convolutions)
274        let mut feature_layers = Sequential::new();
275        feature_layers.add(Conv2D::new(
276            128,
277            64,
278            (3, 3),
279            (1, 1),
280            PaddingMode::Same,
281            rng,
282        )?);
283        feature_layers.add(BatchNorm::new(64, 1e-5, 0.1, rng)?);
284
285        feature_layers.add(Conv2D::new(64, 32, (3, 3), (1, 1), PaddingMode::Same, rng)?);
286        feature_layers.add(BatchNorm::new(32, 1e-5, 0.1, rng)?);
287
288        // Output layer
289        let output_conv = Conv2D::new(32, 1, (3, 3), (1, 1), PaddingMode::Same, rng)?;
290
291        Ok(Self {
292            latent_projection,
293            feature_layers,
294            output_conv,
295            config,
296        })
297    }
298
299    pub fn forward(&self, latent: &ArrayD<f32>) -> StdResult<ArrayD<f32>> {
300        // Project latent to feature space
301        let projected = self.latent_projection.forward(latent)?;
302
303        // Reshape to spatial format
304        let batch_size = projected.shape()[0];
305        let reshaped = projected.into_shape_with_order(IxDyn(&[batch_size, 128, 4, 4]))?;
306
307        // Upsample to target size (simplified)
308        let upsampled = self.upsample(&reshaped)?;
309
310        // Apply feature layers
311        let features = self.feature_layers.forward(&upsampled)?;
312
313        // Generate output
314        let output = self.output_conv.forward(&features)?;
315
316        Ok(output)
317    }
318
319    fn upsample(&self, input: &ArrayD<f32>) -> StdResult<ArrayD<f32>> {
320        let shape = input.shape();
321        let batch_size = shape[0];
322        let channels = shape[1];
323        let height = shape[2];
324        let width = shape[3];
325
326        let (target_height, target_width) = self.config.input_size;
327        let scale_h = target_height / height;
328        let scale_w = target_width / width;
329
330        let mut upsampled =
331            Array4::<f32>::zeros((batch_size, channels, target_height, target_width));
332
333        for b in 0..batch_size {
334            for c in 0..channels {
335                for i in 0..height {
336                    for j in 0..width {
337                        let value = input[[b, c, i, j]];
338                        for di in 0..scale_h {
339                            for dj in 0..scale_w {
340                                let new_i = i * scale_h + di;
341                                let new_j = j * scale_w + dj;
342                                if new_i < target_height && new_j < target_width {
343                                    upsampled[[b, c, new_i, new_j]] = value;
344                                }
345                            }
346                        }
347                    }
348                }
349            }
350        }
351
352        Ok(upsampled.into_dyn())
353    }
354}
355
356/// Complete VAE model
357pub struct VAEModel {
358    encoder: VAEEncoder,
359    decoder: VAEDecoder,
360    config: GenerativeConfig,
361}
362
363impl VAEModel {
364    pub fn new(config: GenerativeConfig, rng: &mut SmallRng) -> StdResult<Self> {
365        let encoder = VAEEncoder::new(config.clone(), rng)?;
366        let decoder = VAEDecoder::new(config.clone(), rng)?;
367
368        Ok(Self {
369            encoder,
370            decoder,
371            config,
372        })
373    }
374
375    pub fn forward(
376        &self,
377        input: &ArrayD<f32>,
378    ) -> StdResult<(ArrayD<f32>, ArrayD<f32>, ArrayD<f32>)> {
379        // Encode
380        let (mean, logvar) = self.encoder.forward(input)?;
381
382        // Reparameterization trick (simplified)
383        let latent = self.reparameterize(&mean, &logvar)?;
384
385        // Decode
386        let reconstruction = self.decoder.forward(&latent)?;
387
388        Ok((reconstruction, mean, logvar))
389    }
390
391    fn reparameterize(&self, mean: &ArrayD<f32>, logvar: &ArrayD<f32>) -> StdResult<ArrayD<f32>> {
392        // Sample epsilon from standard normal
393        let mut epsilon = Array::zeros(mean.raw_dim());
394        let mut rng = SmallRng::seed_from_u64(42); // Fixed seed for reproducibility
395
396        for elem in epsilon.iter_mut() {
397            *elem = rng.random_range(-1.0..1.0); // Approximate normal
398        }
399
400        // z = mean + std * epsilon, where std = exp(0.5 * logvar)
401        let mut result = Array::zeros(mean.raw_dim());
402        for (((&m, &lv), &eps), res) in mean
403            .iter()
404            .zip(logvar.iter())
405            .zip(epsilon.iter())
406            .zip(result.iter_mut())
407        {
408            let std = (0.5 * lv).exp();
409            *res = m + std * eps;
410        }
411
412        Ok(result)
413    }
414
415    /// Generate new samples from random latent codes
416    pub fn generate(&self, batch_size: usize) -> StdResult<ArrayD<f32>> {
417        // Sample random latent codes
418        let mut latent = Array2::<f32>::zeros((batch_size, self.config.latent_dim));
419        let mut rng = SmallRng::seed_from_u64(123);
420
421        for elem in latent.iter_mut() {
422            *elem = rng.random_range(-1.0..1.0);
423        }
424
425        let latent_dyn = latent.into_dyn();
426
427        // Decode to generate images
428        self.decoder.forward(&latent_dyn)
429    }
430
431    /// Interpolate between two latent codes
432    pub fn interpolate(
433        &self,
434        latent1: &ArrayD<f32>,
435        latent2: &ArrayD<f32>,
436        steps: usize,
437    ) -> StdResult<Vec<ArrayD<f32>>> {
438        let mut results = Vec::new();
439
440        for i in 0..steps {
441            let alpha = i as f32 / (steps - 1) as f32;
442
443            // Linear interpolation
444            let mut interpolated = Array::zeros(latent1.raw_dim());
445            for ((&l1, &l2), interp) in latent1
446                .iter()
447                .zip(latent2.iter())
448                .zip(interpolated.iter_mut())
449            {
450                *interp = (1.0 - alpha) * l1 + alpha * l2;
451            }
452
453            let generated = self.decoder.forward(&interpolated)?;
454            results.push(generated);
455        }
456
457        Ok(results)
458    }
459}
460
461/// VAE Loss combining reconstruction and KL divergence
462pub struct VAELoss {
463    reconstruction_loss: MeanSquaredError,
464    beta: f32,
465}
466
467impl VAELoss {
468    pub fn new(beta: f32) -> Self {
469        Self {
470            reconstruction_loss: MeanSquaredError::new(),
471            beta,
472        }
473    }
474
475    pub fn compute_loss(
476        &self,
477        reconstruction: &ArrayD<f32>,
478        target: &ArrayD<f32>,
479        mean: &ArrayD<f32>,
480        logvar: &ArrayD<f32>,
481    ) -> StdResult<(f32, f32, f32)> {
482        // Reconstruction loss
483        let recon_loss = self.reconstruction_loss.forward(reconstruction, target)?;
484
485        // KL divergence loss: -0.5 * sum(1 + logvar - mean^2 - exp(logvar))
486        let mut kl_loss = 0.0f32;
487        for (&m, &lv) in mean.iter().zip(logvar.iter()) {
488            kl_loss += -0.5 * (1.0 + lv - m * m - lv.exp());
489        }
490        kl_loss /= mean.len() as f32; // Average over elements
491
492        let total_loss = recon_loss + self.beta * kl_loss;
493
494        Ok((total_loss, recon_loss, kl_loss))
495    }
496}
497
498/// Simple GAN Generator
499pub struct GANGenerator {
500    layers: Sequential<f32>,
501    config: GenerativeConfig,
502}
503
504impl GANGenerator {
505    pub fn new(config: GenerativeConfig, rng: &mut SmallRng) -> StdResult<Self> {
506        let mut layers = Sequential::new();
507
508        // Project noise to feature space
509        layers.add(Dense::new(
510            config.latent_dim,
511            config.hidden_dims[0],
512            Some("relu"),
513            rng,
514        )?);
515        layers.add(BatchNorm::new(config.hidden_dims[0], 1e-5, 0.1, rng)?);
516
517        layers.add(Dense::new(
518            config.hidden_dims[0],
519            config.hidden_dims[1] * 2,
520            Some("relu"),
521            rng,
522        )?);
523        layers.add(BatchNorm::new(config.hidden_dims[1] * 2, 1e-5, 0.1, rng)?);
524
525        // Output layer
526        let output_size = config.input_size.0 * config.input_size.1;
527        layers.add(Dense::new(
528            config.hidden_dims[1] * 2,
529            output_size,
530            Some("tanh"),
531            rng,
532        )?);
533
534        Ok(Self { layers, config })
535    }
examples/object_detection_complete.rs (line 184)
178    pub fn new(config: DetectionConfig, rng: &mut SmallRng) -> StdResult<Self> {
179        // Feature extraction backbone (simplified ResNet-like)
180        let mut feature_extractor = Sequential::new();
181
182        // Initial conv block
183        feature_extractor.add(Conv2D::new(3, 64, (7, 7), (2, 2), PaddingMode::Same, rng)?);
184        feature_extractor.add(BatchNorm::new(64, 0.1, 1e-5, rng)?);
185        feature_extractor.add(MaxPool2D::new((2, 2), (2, 2), None)?);
186
187        // Feature blocks
188        feature_extractor.add(Conv2D::new(
189            64,
190            128,
191            (3, 3),
192            (2, 2),
193            PaddingMode::Same,
194            rng,
195        )?);
196        feature_extractor.add(BatchNorm::new(128, 0.1, 1e-5, rng)?);
197
198        feature_extractor.add(Conv2D::new(
199            128,
200            256,
201            (3, 3),
202            (2, 2),
203            PaddingMode::Same,
204            rng,
205        )?);
206        feature_extractor.add(BatchNorm::new(256, 0.1, 1e-5, rng)?);
207
208        // Global pooling to fixed size
209        feature_extractor.add(AdaptiveMaxPool2D::new(config.feature_map_size, None)?);
210
211        // Classification head
212        let mut classifier_head = Sequential::new();
213        let feature_dim = 256 * config.feature_map_size.0 * config.feature_map_size.1;
214        classifier_head.add(Dense::new(feature_dim, 512, Some("relu"), rng)?);
215        classifier_head.add(Dropout::new(0.5, rng)?);
216        classifier_head.add(Dense::new(512, 256, Some("relu"), rng)?);
217        classifier_head.add(Dropout::new(0.3, rng)?);
218        classifier_head.add(Dense::new(
219            256,
220            config.num_classes * config.max_objects,
221            Some("softmax"),
222            rng,
223        )?);
224
225        // Bounding box regression head
226        let mut bbox_regressor = Sequential::new();
227        bbox_regressor.add(Dense::new(feature_dim, 512, Some("relu"), rng)?);
228        bbox_regressor.add(Dropout::new(0.5, rng)?);
229        bbox_regressor.add(Dense::new(512, 256, Some("relu"), rng)?);
230        bbox_regressor.add(Dropout::new(0.3, rng)?);
231        bbox_regressor.add(Dense::new(256, 4 * config.max_objects, None, rng)?); // 4 coordinates per object
232
233        Ok(Self {
234            feature_extractor,
235            classifier_head,
236            bbox_regressor,
237            config,
238        })
239    }
examples/batchnorm_example.rs (line 25)
7fn main() -> Result<(), Box<dyn std::error::Error>> {
8    println!("Batch Normalization Example");
9
10    // Initialize random number generator with a fixed seed for reproducibility
11    let mut rng = SmallRng::seed_from_u64(42);
12
13    // Create a sample CNN architecture with batch normalization
14
15    // 1. Define input dimensions
16    let batch_size = 2;
17    let in_channels = 3; // RGB input
18    let height = 32;
19    let width = 32;
20
21    // 2. Create convolutional layer
22    let conv = Conv2D::new(in_channels, 16, (3, 3), (1, 1), PaddingMode::Same, &mut rng)?;
23
24    // 3. Create batch normalization layer for the conv output
25    let batch_norm = BatchNorm::new(16, 0.9, 1e-5, &mut rng)?;
26
27    // 4. Create random input data
28    let input = Array4::<f32>::from_elem((batch_size, in_channels, height, width), 0.0);
29
30    // Randomly fill input with values between -1 and 1
31    let mut input_mut = input.clone();
32    for n in 0..batch_size {
33        for c in 0..in_channels {
34            for h in 0..height {
35                for w in 0..width {
36                    input_mut[[n, c, h, w]] = rng.random_range(-1.0..1.0);
37                }
38            }
39        }
40    }
41
42    // 5. Forward pass through conv layer
43    println!("Input shape: {:?}", input_mut.shape());
44    let conv_output = conv.forward(&input_mut.into_dyn())?;
45    println!("Conv output shape: {:?}", conv_output.shape());
46
47    // 6. Forward pass through batch normalization
48    let bn_output = batch_norm.forward(&conv_output)?;
49    println!("BatchNorm output shape: {:?}", bn_output.shape());
50
51    // Print statistics of the conv output and batch norm output
52    let conv_mean = compute_mean(&conv_output);
53    let conv_std = compute_std(&conv_output, conv_mean);
54
55    let bn_mean = compute_mean(&bn_output);
56    let bn_std = compute_std(&bn_output, bn_mean);
57
58    println!("\nStatistics before BatchNorm:");
59    println!("  Mean: {:.6}", conv_mean);
60    println!("  Std:  {:.6}", conv_std);
61
62    println!("\nStatistics after BatchNorm:");
63    println!("  Mean: {:.6}", bn_mean);
64    println!("  Std:  {:.6}", bn_std);
65
66    // Switch to inference mode
67    let mut bn_inference = BatchNorm::new(16, 0.9, 1e-5, &mut rng)?;
68
69    // First do a forward pass in training mode to accumulate statistics
70    bn_inference.forward(&conv_output)?;
71
72    // Now switch to inference mode
73    bn_inference.set_training(false);
74    let bn_inference_output = bn_inference.forward(&conv_output)?;
75
76    let bn_inference_mean = compute_mean(&bn_inference_output);
77    let bn_inference_std = compute_std(&bn_inference_output, bn_inference_mean);
78
79    println!("\nStatistics in inference mode:");
80    println!("  Mean: {:.6}", bn_inference_mean);
81    println!("  Std:  {:.6}", bn_inference_std);
82
83    // Example of using BatchNorm in a simple neural network
84    println!("\nExample: BatchNorm in a simple neural network");
85
86    // Create a random 2D input (batch_size, features)
87    let batch_size = 16;
88    let in_features = 10;
89    let mut input_2d = Array::from_elem((batch_size, in_features), 0.0);
90
91    // Randomly fill input with values between -1 and 1
92    for n in 0..batch_size {
93        for f in 0..in_features {
94            input_2d[[n, f]] = rng.random_range(-1.0..1.0);
95        }
96    }
97
98    // Create dense layer
99    let dense1 = Dense::new(in_features, 32, None, &mut rng)?;
100
101    // Create batch norm for dense output
102    let bn1 = BatchNorm::new(32, 0.9, 1e-5, &mut rng)?;
103
104    // Forward passes
105    let dense1_output = dense1.forward(&input_2d.into_dyn())?;
106    let bn1_output = bn1.forward(&dense1_output)?;
107
108    println!(
109        "Dense output stats - Mean: {:.6}, Std: {:.6}",
110        compute_mean(&dense1_output),
111        compute_std(&dense1_output, compute_mean(&dense1_output))
112    );
113
114    println!(
115        "After BatchNorm - Mean: {:.6}, Std: {:.6}",
116        compute_mean(&bn1_output),
117        compute_std(&bn1_output, compute_mean(&bn1_output))
118    );
119
120    Ok(())
121}
examples/dropout_example.rs (line 99)
7fn main() -> Result<(), Box<dyn std::error::Error>> {
8    println!("Dropout Example");
9
10    // Initialize random number generator with a fixed seed for reproducibility
11    let mut rng = SmallRng::seed_from_u64(42);
12
13    // 1. Example: Simple dropout on a vector
14    println!("\nExample 1: Simple dropout on a vector");
15
16    // Create a vector of ones
17    let n_features = 20;
18    let input = Array2::<f32>::from_elem((1, n_features), 1.0);
19
20    // Create a dropout layer with dropout probability 0.5
21    let dropout = Dropout::new(0.5, &mut rng)?;
22
23    // Apply dropout in training mode
24    let output = dropout.forward(&input.into_dyn())?;
25
26    // Count elements that were dropped
27    let mut dropped_count = 0;
28    for i in 0..n_features {
29        let val = output.slice(ndarray::s![0, i]).into_scalar();
30        if *val == 0.0 {
31            dropped_count += 1;
32        }
33    }
34
35    // Print results
36    println!("Input: Array of {} ones", n_features);
37    println!(
38        "Output after dropout (p=0.5): {} elements dropped",
39        dropped_count
40    );
41    println!(
42        "Dropout rate: {:.2}%",
43        (dropped_count as f32 / n_features as f32) * 100.0
44    );
45
46    // 2. Example: Using dropout in a neural network to prevent overfitting
47    println!("\nExample 2: Using dropout in a simple neural network");
48
49    // Create a simple neural network with dropout
50    // 1. Create dense layer (input -> hidden)
51    let input_dim = 10;
52    let hidden_dim = 100;
53    let output_dim = 2;
54    let dense1 = Dense::new(input_dim, hidden_dim, Some("relu"), &mut rng)?;
55
56    // 2. Create dropout layer (after first dense layer)
57    let dropout1 = Dropout::new(0.2, &mut rng)?;
58
59    // 3. Create second dense layer (hidden -> output)
60    let dense2 = Dense::new(hidden_dim, output_dim, None, &mut rng)?;
61
62    // Generate a random input
63    let batch_size = 16;
64    let mut input_data = Array2::<f32>::zeros((batch_size, input_dim));
65    for i in 0..batch_size {
66        for j in 0..input_dim {
67            input_data[[i, j]] = rng.random_range(-1.0..1.0);
68        }
69    }
70
71    // Forward pass through the network with dropout
72    println!("Running forward pass with dropout...");
73    let hidden_output = dense1.forward(&input_data.clone().into_dyn())?;
74    let hidden_dropped = dropout1.forward(&hidden_output)?;
75    let final_output = dense2.forward(&hidden_dropped)?;
76
77    println!("Input shape: {:?}", input_data.shape());
78    println!("Hidden layer shape: {:?}", hidden_output.shape());
79    println!("Output shape: {:?}", final_output.shape());
80
81    // 3. Example: Dropout in a CNN architecture
82    println!("\nExample 3: Dropout in a CNN architecture");
83
84    // Create a sample CNN architecture with dropout
85
86    // 1. Create convolutional layer
87    let in_channels = 3; // RGB input
88    let out_channels = 16;
89    let conv1 = Conv2D::new(
90        in_channels,
91        out_channels,
92        (3, 3),
93        (1, 1),
94        PaddingMode::Same,
95        &mut rng,
96    )?;
97
98    // 2. Create batch normalization layer
99    let bn1 = BatchNorm::new(out_channels, 0.9, 1e-5, &mut rng)?;
100
101    // 3. Create dropout layer for spatial dropout (dropping entire feature maps)
102    let dropout_conv = Dropout::new(0.25, &mut rng)?;
103
104    // 4. Create flattened dense layer
105    let height = 32;
106    let width = 32;
107    let flattened_size = out_channels * height * width;
108    let dense3 = Dense::new(flattened_size, output_dim, None, &mut rng)?;
109
110    // Generate random input image
111    let mut image_input = Array4::<f32>::zeros((batch_size, in_channels, height, width));
112    for n in 0..batch_size {
113        for c in 0..in_channels {
114            for h in 0..height {
115                for w in 0..width {
116                    image_input[[n, c, h, w]] = rng.random_range(-1.0..1.0);
117                }
118            }
119        }
120    }
121
122    // Forward pass through CNN with dropout
123    println!("Running forward pass through CNN with dropout...");
124    let conv_output = conv1.forward(&image_input.clone().into_dyn())?;
125    let bn_output = bn1.forward(&conv_output)?;
126    let dropout_output = dropout_conv.forward(&bn_output)?;
127
128    // Reshape for dense layer (flatten spatial and channel dimensions)
129    let mut flattened = Array2::<f32>::zeros((batch_size, flattened_size));
130    for n in 0..batch_size {
131        let mut idx = 0;
132        for c in 0..out_channels {
133            for h in 0..height {
134                for w in 0..width {
135                    flattened[[n, idx]] =
136                        *dropout_output.slice(ndarray::s![n, c, h, w]).into_scalar();
137                    idx += 1;
138                }
139            }
140        }
141    }
142
143    let cnn_output = dense3.forward(&flattened.into_dyn())?;
144
145    println!("CNN output shape: {:?}", cnn_output.shape());
146
147    // 4. Example: Switching between training and inference modes
148    println!("\nExample 4: Switching between training and inference modes");
149
150    // Create dropout layer with p=0.5
151    let mut switchable_dropout = Dropout::new(0.5, &mut rng)?;
152
153    // Create some input data
154    let test_input = Array2::<f32>::from_elem((1, 10), 1.0);
155
156    // Training mode (default)
157    let training_output = switchable_dropout.forward(&test_input.clone().into_dyn())?;
158
159    // Count non-zero elements in training mode
160    let training_nonzero = training_output.iter().filter(|&&x| x != 0.0).count();
161
162    // Switch to inference mode
163    switchable_dropout.set_training(false);
164    let inference_output = switchable_dropout.forward(&test_input.clone().into_dyn())?;
165
166    // Count non-zero elements in inference mode
167    let inference_nonzero = inference_output.iter().filter(|&&x| x != 0.0).count();
168
169    println!("Training mode: {} of 10 elements kept", training_nonzero);
170    println!("Inference mode: {} of 10 elements kept", inference_nonzero);
171
172    // In inference mode, all elements should be preserved
173    assert_eq!(inference_nonzero, 10);
174
175    println!("\nDropout implementation demonstration completed successfully!");
176
177    Ok(())
178}
Source

pub fn set_training(&mut self, training: bool)

Set the training mode

In training mode, batch statistics are used for normalization and running statistics are updated. In inference mode, running statistics are used for normalization and not updated.

Examples found in repository?
examples/batchnorm_example.rs (line 73)
7fn main() -> Result<(), Box<dyn std::error::Error>> {
8    println!("Batch Normalization Example");
9
10    // Initialize random number generator with a fixed seed for reproducibility
11    let mut rng = SmallRng::seed_from_u64(42);
12
13    // Create a sample CNN architecture with batch normalization
14
15    // 1. Define input dimensions
16    let batch_size = 2;
17    let in_channels = 3; // RGB input
18    let height = 32;
19    let width = 32;
20
21    // 2. Create convolutional layer
22    let conv = Conv2D::new(in_channels, 16, (3, 3), (1, 1), PaddingMode::Same, &mut rng)?;
23
24    // 3. Create batch normalization layer for the conv output
25    let batch_norm = BatchNorm::new(16, 0.9, 1e-5, &mut rng)?;
26
27    // 4. Create random input data
28    let input = Array4::<f32>::from_elem((batch_size, in_channels, height, width), 0.0);
29
30    // Randomly fill input with values between -1 and 1
31    let mut input_mut = input.clone();
32    for n in 0..batch_size {
33        for c in 0..in_channels {
34            for h in 0..height {
35                for w in 0..width {
36                    input_mut[[n, c, h, w]] = rng.random_range(-1.0..1.0);
37                }
38            }
39        }
40    }
41
42    // 5. Forward pass through conv layer
43    println!("Input shape: {:?}", input_mut.shape());
44    let conv_output = conv.forward(&input_mut.into_dyn())?;
45    println!("Conv output shape: {:?}", conv_output.shape());
46
47    // 6. Forward pass through batch normalization
48    let bn_output = batch_norm.forward(&conv_output)?;
49    println!("BatchNorm output shape: {:?}", bn_output.shape());
50
51    // Print statistics of the conv output and batch norm output
52    let conv_mean = compute_mean(&conv_output);
53    let conv_std = compute_std(&conv_output, conv_mean);
54
55    let bn_mean = compute_mean(&bn_output);
56    let bn_std = compute_std(&bn_output, bn_mean);
57
58    println!("\nStatistics before BatchNorm:");
59    println!("  Mean: {:.6}", conv_mean);
60    println!("  Std:  {:.6}", conv_std);
61
62    println!("\nStatistics after BatchNorm:");
63    println!("  Mean: {:.6}", bn_mean);
64    println!("  Std:  {:.6}", bn_std);
65
66    // Switch to inference mode
67    let mut bn_inference = BatchNorm::new(16, 0.9, 1e-5, &mut rng)?;
68
69    // First do a forward pass in training mode to accumulate statistics
70    bn_inference.forward(&conv_output)?;
71
72    // Now switch to inference mode
73    bn_inference.set_training(false);
74    let bn_inference_output = bn_inference.forward(&conv_output)?;
75
76    let bn_inference_mean = compute_mean(&bn_inference_output);
77    let bn_inference_std = compute_std(&bn_inference_output, bn_inference_mean);
78
79    println!("\nStatistics in inference mode:");
80    println!("  Mean: {:.6}", bn_inference_mean);
81    println!("  Std:  {:.6}", bn_inference_std);
82
83    // Example of using BatchNorm in a simple neural network
84    println!("\nExample: BatchNorm in a simple neural network");
85
86    // Create a random 2D input (batch_size, features)
87    let batch_size = 16;
88    let in_features = 10;
89    let mut input_2d = Array::from_elem((batch_size, in_features), 0.0);
90
91    // Randomly fill input with values between -1 and 1
92    for n in 0..batch_size {
93        for f in 0..in_features {
94            input_2d[[n, f]] = rng.random_range(-1.0..1.0);
95        }
96    }
97
98    // Create dense layer
99    let dense1 = Dense::new(in_features, 32, None, &mut rng)?;
100
101    // Create batch norm for dense output
102    let bn1 = BatchNorm::new(32, 0.9, 1e-5, &mut rng)?;
103
104    // Forward passes
105    let dense1_output = dense1.forward(&input_2d.into_dyn())?;
106    let bn1_output = bn1.forward(&dense1_output)?;
107
108    println!(
109        "Dense output stats - Mean: {:.6}, Std: {:.6}",
110        compute_mean(&dense1_output),
111        compute_std(&dense1_output, compute_mean(&dense1_output))
112    );
113
114    println!(
115        "After BatchNorm - Mean: {:.6}, Std: {:.6}",
116        compute_mean(&bn1_output),
117        compute_std(&bn1_output, compute_mean(&bn1_output))
118    );
119
120    Ok(())
121}
Source

pub fn num_features(&self) -> usize

Get the number of features

Source

pub fn momentum(&self) -> f64

Get the momentum value

Source

pub fn eps(&self) -> f64

Get the epsilon value

Source

pub fn is_training(&self) -> bool

Get the training mode

Trait Implementations§

Source§

impl<F: Clone + Float + Debug + Send + Sync> Clone for BatchNorm<F>

Source§

fn clone(&self) -> BatchNorm<F>

Returns a duplicate of the value. Read more
1.0.0 · Source§

const fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl<F: Debug + Float + Debug + Send + Sync> Debug for BatchNorm<F>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<F: Float + Debug + ScalarOperand + Send + Sync + 'static> Layer<F> for BatchNorm<F>

Source§

fn as_any(&self) -> &dyn Any

Get the layer as a dyn Any for downcasting Read more
Source§

fn as_any_mut(&mut self) -> &mut dyn Any

Get the layer as a mutable dyn Any for downcasting Read more
Source§

fn forward(&self, input: &Array<F, IxDyn>) -> Result<Array<F, IxDyn>>

Forward pass of the layer Read more
Source§

fn backward( &self, _input: &Array<F, IxDyn>, grad_output: &Array<F, IxDyn>, ) -> Result<Array<F, IxDyn>>

Backward pass of the layer to compute gradients Read more
Source§

fn update(&mut self, learning_rate: F) -> Result<()>

Update the layer parameters with the given gradients Read more
Source§

fn params(&self) -> Vec<Array<F, IxDyn>>

Get the parameters of the layer Read more
Source§

fn gradients(&self) -> Vec<Array<F, IxDyn>>

Get the gradients of the layer parameters Read more
Source§

fn set_gradients(&mut self, _gradients: &[Array<F, IxDyn>]) -> Result<()>

Set the gradients of the layer parameters Read more
Source§

fn set_params(&mut self, _params: &[Array<F, IxDyn>]) -> Result<()>

Set the parameters of the layer Read more
Source§

fn set_training(&mut self, _training: bool)

Set the layer to training mode (true) or evaluation mode (false) Read more
Source§

fn is_training(&self) -> bool

Get the current training mode Read more
Source§

fn layer_type(&self) -> &str

Get the type of the layer (e.g., “Dense”, “Conv2D”) Read more
Source§

fn parameter_count(&self) -> usize

Get the number of trainable parameters in this layer Read more
Source§

fn layer_description(&self) -> String

Get a detailed description of this layer Read more
Source§

impl<F: Float + Debug + ScalarOperand + Send + Sync + 'static> ParamLayer<F> for BatchNorm<F>

Source§

fn get_parameters(&self) -> Vec<&Array<F, IxDyn>>

Get the parameters of the layer as a vector of arrays
Source§

fn get_gradients(&self) -> Vec<&Array<F, IxDyn>>

Get the gradients of the parameters
Source§

fn set_parameters(&mut self, params: Vec<Array<F, IxDyn>>) -> Result<()>

Set the parameters of the layer

Auto Trait Implementations§

§

impl<F> Freeze for BatchNorm<F>
where F: Freeze,

§

impl<F> RefUnwindSafe for BatchNorm<F>
where F: RefUnwindSafe,

§

impl<F> Send for BatchNorm<F>

§

impl<F> Sync for BatchNorm<F>

§

impl<F> Unpin for BatchNorm<F>
where F: Unpin,

§

impl<F> UnwindSafe for BatchNorm<F>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V