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>
impl<F: Float + Debug + ScalarOperand + Send + Sync + 'static> BatchNorm<F>
Sourcepub fn new<R: Rng>(
num_features: usize,
momentum: f64,
eps: f64,
_rng: &mut R,
) -> Result<Self>
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 normalizemomentum
- 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
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}
Sourcepub fn set_training(&mut self, training: bool)
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}
Sourcepub fn num_features(&self) -> usize
pub fn num_features(&self) -> usize
Get the number of features
Sourcepub fn is_training(&self) -> bool
pub fn is_training(&self) -> bool
Get the training mode
Trait Implementations§
Source§impl<F: Float + Debug + ScalarOperand + Send + Sync + 'static> Layer<F> for BatchNorm<F>
impl<F: Float + Debug + ScalarOperand + Send + Sync + 'static> Layer<F> for BatchNorm<F>
Source§fn as_any_mut(&mut self) -> &mut dyn Any
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>>
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>>
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<()>
fn update(&mut self, learning_rate: F) -> Result<()>
Update the layer parameters with the given gradients Read more
Source§fn gradients(&self) -> Vec<Array<F, IxDyn>> ⓘ
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<()>
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<()>
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)
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
fn is_training(&self) -> bool
Get the current training mode Read more
Source§fn layer_type(&self) -> &str
fn layer_type(&self) -> &str
Get the type of the layer (e.g., “Dense”, “Conv2D”) Read more
Source§fn parameter_count(&self) -> usize
fn parameter_count(&self) -> usize
Get the number of trainable parameters in this layer Read more
Source§fn layer_description(&self) -> String
fn layer_description(&self) -> String
Get a detailed description of this layer Read more
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>where
F: UnwindSafe + RefUnwindSafe,
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
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 moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
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