Struct Sequential

Source
pub struct Sequential<F: Float + Debug + ScalarOperand> { /* private fields */ }
Expand description

Sequential container for neural network layers

A Sequential model is a linear stack of layers where data flows through each layer in order. This is the most common way to build neural networks and is suitable for feed-forward architectures.

§Features

  • Linear topology: Layers are executed in the order they were added
  • Automatic gradient flow: Backward pass automatically chains through all layers
  • Training mode management: Sets all contained layers to training/evaluation mode
  • Parameter aggregation: Collects parameters from all layers for optimization
  • Memory efficient: Reuses intermediate tensors when possible

§Examples

§Building a Classifier

use scirs2_neural::layers::{Dense, Dropout, Layer};
use scirs2_neural::models::{Sequential, Model};
use ndarray::Array;
use rand::rngs::SmallRng;
use rand::SeedableRng;

let mut rng = SmallRng::seed_from_u64(42);
let mut model: Sequential<f32> = Sequential::new();

// Build a 3-layer classifier for MNIST (28x28 = 784 inputs, 10 classes)
model.add_layer(Dense::<f32>::new(784, 128, Some("relu"), &mut rng)?);
model.add_layer(Dropout::new(0.3, &mut rng)?);
model.add_layer(Dense::new(128, 64, Some("relu"), &mut rng)?);
model.add_layer(Dropout::new(0.3, &mut rng)?);
model.add_layer(Dense::<f32>::new(64, 10, Some("softmax"), &mut rng)?);

// Process a batch of images
let batch = Array::zeros((32, 784)).into_dyn(); // 32 samples
let predictions = model.forward(&batch)?;
assert_eq!(predictions.shape(), &[32, 10]);

println!("Model summary:");
println!("- Layers: {}", model.num_layers());

§CNN for Image Recognition

use scirs2_neural::layers::{Conv2D, MaxPool2D, Dense, Dropout, Layer, PaddingMode};
use scirs2_neural::models::{Sequential, Model};
use ndarray::Array;
use rand::rngs::SmallRng;
use rand::SeedableRng;

let mut rng = SmallRng::seed_from_u64(42);
let mut cnn: Sequential<f32> = Sequential::new();

// Convolutional feature extractor
cnn.add_layer(Conv2D::new(3, 32, (3, 3), (1, 1), PaddingMode::Same, &mut rng)?); // 3->32 channels
cnn.add_layer(MaxPool2D::new((2, 2), (2, 2), None)?); // Downsample 2x
cnn.add_layer(Conv2D::new(32, 64, (3, 3), (1, 1), PaddingMode::Same, &mut rng)?); // 32->64 channels  
cnn.add_layer(MaxPool2D::new((2, 2), (2, 2), None)?); // Downsample 2x

// Classifier head (would need reshape layer in practice)
// cnn.add_layer(Flatten::new()); // Would flatten to 1D
// cnn.add_layer(Dense::new(64*8*8, 128, Some("relu"), &mut rng)?);
// cnn.add_layer(Dropout::new(0.5, &mut rng)?);
// cnn.add_layer(Dense::new(128, 10, None, &mut rng)?);

// Input: batch of 32x32 RGB images
let images = Array::zeros((16, 3, 32, 32)).into_dyn();
let features = cnn.forward(&images)?;
println!("Feature shape: {:?}", features.shape());

§Training and Evaluation Modes

use scirs2_neural::layers::{Dense, Dropout, Layer};
use scirs2_neural::models::{Sequential, Model};
use ndarray::Array;
use rand::rngs::SmallRng;
use rand::SeedableRng;

let mut rng = SmallRng::seed_from_u64(42);
let mut model: Sequential<f32> = Sequential::new();
model.add_layer(Dense::new(10, 5, Some("relu"), &mut rng)?);
model.add_layer(Dropout::new(0.5, &mut rng)?); // 50% dropout
model.add_layer(Dense::<f32>::new(5, 1, None, &mut rng)?);

let input = Array::ones((4, 10)).into_dyn();

// Forward pass through the model
let output = model.forward(&input)?;
println!("Output shape: {:?}", output.shape());

Implementations§

Source§

impl<F: Float + Debug + ScalarOperand> Sequential<F>

Source

pub fn new() -> Self

Create a new Sequential container

Examples found in repository?
examples/model_evaluation_example.rs (line 44)
43    fn build(&self) -> Result<Self::Model> {
44        let mut model = Sequential::new();
45
46        let mut rng = SmallRng::seed_from_u64(42);
47        model.add(Dense::<F>::new(
48            self.input_dim,
49            self.hidden_dim,
50            Some("relu"),
51            &mut rng,
52        )?);
53
54        model.add(Dropout::<F>::new(0.2, &mut rng)?);
55
56        model.add(Dense::<F>::new(
57            self.hidden_dim,
58            self.output_dim,
59            None,
60            &mut rng,
61        )?);
62
63        Ok(model)
64    }
More examples
Hide additional examples
examples/image_classification_complete.rs (line 156)
151fn build_cnn_model(
152    input_channels: usize,
153    num_classes: usize,
154    rng: &mut SmallRng,
155) -> Result<Sequential<f32>> {
156    let mut model = Sequential::new();
157
158    // First convolutional block
159    model.add(Dense::new(
160        input_channels * 32 * 32,
161        512,
162        Some("relu"),
163        rng,
164    )?);
165    model.add(Dropout::new(0.25, rng)?);
166
167    // Hidden layers
168    model.add(Dense::new(512, 256, Some("relu"), rng)?);
169    model.add(Dropout::new(0.25, rng)?);
170
171    model.add(Dense::new(256, 128, Some("relu"), rng)?);
172    model.add(Dropout::new(0.25, rng)?);
173
174    // Output layer
175    model.add(Dense::new(128, num_classes, Some("softmax"), rng)?);
176
177    Ok(model)
178}
examples/advanced_training_example.rs (line 27)
20fn create_regression_model<
21    F: Float + Debug + ScalarOperand + Send + Sync + FromPrimitive + 'static,
22>(
23    input_dim: usize,
24    hidden_dim: usize,
25    output_dim: usize,
26) -> Result<Sequential<F>> {
27    let mut model = Sequential::new();
28
29    // Create RNG for initializing layers
30    let mut rng = SmallRng::seed_from_u64(42);
31
32    // First dense layer with ReLU activation
33    let dense1 = Dense::new(input_dim, hidden_dim, Some("relu"), &mut rng)?;
34    model.add(dense1);
35
36    // Dropout layer for regularization
37    let dropout1 = Dropout::new(0.2, &mut rng)?;
38    model.add(dropout1);
39
40    // Second dense layer with ReLU activation
41    let dense2 = Dense::new(hidden_dim, hidden_dim / 2, Some("relu"), &mut rng)?;
42    model.add(dense2);
43
44    // Another dropout layer
45    let dropout2 = Dropout::new(0.2, &mut rng)?;
46    model.add(dropout2);
47
48    // Output layer with no activation
49    let dense3 = Dense::new(hidden_dim / 2, output_dim, None, &mut rng)?;
50    model.add(dense3);
51
52    Ok(model)
53}
examples/text_classification_complete.rs (line 321)
313fn build_text_model(
314    _vocab_size: usize,
315    embedding_dim: usize,
316    hidden_dim: usize,
317    num_classes: usize,
318    max_length: usize,
319    rng: &mut SmallRng,
320) -> StdResult<Sequential<f32>> {
321    let mut model = Sequential::new();
322
323    // Note: This is a simplified version since our Sequential model expects specific layer types
324    // In a real implementation, we'd need specialized text layers
325
326    // Flatten the input tokens and use dense layers to simulate embedding and LSTM processing
327    let input_size = max_length; // Each token position
328
329    // Simulate embedding layer with dense transformation
330    model.add(Dense::new(
331        input_size,
332        embedding_dim * 2,
333        Some("relu"),
334        rng,
335    )?);
336    model.add(Dropout::new(0.1, rng)?);
337
338    // Simulate LSTM processing with dense layers
339    model.add(Dense::new(
340        embedding_dim * 2,
341        hidden_dim,
342        Some("tanh"),
343        rng,
344    )?);
345    model.add(Dropout::new(0.2, rng)?);
346
347    // Add attention-like mechanism with another dense layer
348    model.add(Dense::new(hidden_dim, hidden_dim / 2, Some("relu"), rng)?);
349    model.add(Dropout::new(0.2, rng)?);
350
351    // Classification head
352    model.add(Dense::new(
353        hidden_dim / 2,
354        num_classes,
355        Some("softmax"),
356        rng,
357    )?);
358
359    Ok(model)
360}
361
362/// Build an advanced text model with attention
363fn build_attention_text_model(
364    _vocab_size: usize,
365    embedding_dim: usize,
366    hidden_dim: usize,
367    num_classes: usize,
368    max_length: usize,
369    rng: &mut SmallRng,
370) -> StdResult<Sequential<f32>> {
371    let mut model = Sequential::new();
372
373    // Simplified attention-based model using dense layers
374    let input_size = max_length;
375
376    // Multi-layer processing to simulate transformer-like behavior
377    model.add(Dense::new(input_size, embedding_dim, Some("relu"), rng)?);
378    model.add(Dropout::new(0.1, rng)?);
379
380    // Simulate self-attention with dense layers
381    model.add(Dense::new(embedding_dim, hidden_dim, Some("relu"), rng)?);
382    model.add(Dropout::new(0.1, rng)?);
383
384    // Second attention layer
385    model.add(Dense::new(hidden_dim, hidden_dim, Some("relu"), rng)?);
386    model.add(Dropout::new(0.1, rng)?);
387
388    // Global pooling simulation
389    model.add(Dense::new(hidden_dim, hidden_dim / 2, Some("relu"), rng)?);
390    model.add(Dropout::new(0.2, rng)?);
391
392    // Classification head
393    model.add(Dense::new(
394        hidden_dim / 2,
395        num_classes,
396        Some("softmax"),
397        rng,
398    )?);
399
400    Ok(model)
401}
examples/generative_models_complete.rs (line 167)
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    }
536
537    pub fn forward(&self, noise: &ArrayD<f32>) -> StdResult<ArrayD<f32>> {
538        let output = self.layers.forward(noise)?;
539
540        // Reshape to image format
541        let batch_size = output.shape()[0];
542        let (height, width) = self.config.input_size;
543        let reshaped = output
544            .to_shape(IxDyn(&[batch_size, 1, height, width]))?
545            .to_owned();
546
547        Ok(reshaped)
548    }
549}
550
551/// Simple GAN Discriminator
552pub struct GANDiscriminator {
553    layers: Sequential<f32>,
554    config: GenerativeConfig,
555}
556
557impl GANDiscriminator {
558    pub fn new(config: GenerativeConfig, rng: &mut SmallRng) -> StdResult<Self> {
559        let mut layers = Sequential::new();
560
561        let input_size = config.input_size.0 * config.input_size.1;
562
563        layers.add(Dense::new(
564            input_size,
565            config.hidden_dims[0],
566            Some("relu"),
567            rng,
568        )?);
569        layers.add(Dropout::new(0.3, rng)?);
570
571        layers.add(Dense::new(
572            config.hidden_dims[0],
573            config.hidden_dims[1],
574            Some("relu"),
575            rng,
576        )?);
577        layers.add(Dropout::new(0.3, rng)?);
578
579        // Output probability of being real
580        layers.add(Dense::new(config.hidden_dims[1], 1, Some("sigmoid"), rng)?);
581
582        Ok(Self { layers, config })
583    }
examples/object_detection_complete.rs (line 180)
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    }
Source

pub fn add<L: Layer<F> + Send + Sync + 'static>(&mut self, layer: L)

Add a layer to the container

Examples found in repository?
examples/model_evaluation_example.rs (lines 47-52)
43    fn build(&self) -> Result<Self::Model> {
44        let mut model = Sequential::new();
45
46        let mut rng = SmallRng::seed_from_u64(42);
47        model.add(Dense::<F>::new(
48            self.input_dim,
49            self.hidden_dim,
50            Some("relu"),
51            &mut rng,
52        )?);
53
54        model.add(Dropout::<F>::new(0.2, &mut rng)?);
55
56        model.add(Dense::<F>::new(
57            self.hidden_dim,
58            self.output_dim,
59            None,
60            &mut rng,
61        )?);
62
63        Ok(model)
64    }
More examples
Hide additional examples
examples/image_classification_complete.rs (lines 159-164)
151fn build_cnn_model(
152    input_channels: usize,
153    num_classes: usize,
154    rng: &mut SmallRng,
155) -> Result<Sequential<f32>> {
156    let mut model = Sequential::new();
157
158    // First convolutional block
159    model.add(Dense::new(
160        input_channels * 32 * 32,
161        512,
162        Some("relu"),
163        rng,
164    )?);
165    model.add(Dropout::new(0.25, rng)?);
166
167    // Hidden layers
168    model.add(Dense::new(512, 256, Some("relu"), rng)?);
169    model.add(Dropout::new(0.25, rng)?);
170
171    model.add(Dense::new(256, 128, Some("relu"), rng)?);
172    model.add(Dropout::new(0.25, rng)?);
173
174    // Output layer
175    model.add(Dense::new(128, num_classes, Some("softmax"), rng)?);
176
177    Ok(model)
178}
examples/advanced_training_example.rs (line 34)
20fn create_regression_model<
21    F: Float + Debug + ScalarOperand + Send + Sync + FromPrimitive + 'static,
22>(
23    input_dim: usize,
24    hidden_dim: usize,
25    output_dim: usize,
26) -> Result<Sequential<F>> {
27    let mut model = Sequential::new();
28
29    // Create RNG for initializing layers
30    let mut rng = SmallRng::seed_from_u64(42);
31
32    // First dense layer with ReLU activation
33    let dense1 = Dense::new(input_dim, hidden_dim, Some("relu"), &mut rng)?;
34    model.add(dense1);
35
36    // Dropout layer for regularization
37    let dropout1 = Dropout::new(0.2, &mut rng)?;
38    model.add(dropout1);
39
40    // Second dense layer with ReLU activation
41    let dense2 = Dense::new(hidden_dim, hidden_dim / 2, Some("relu"), &mut rng)?;
42    model.add(dense2);
43
44    // Another dropout layer
45    let dropout2 = Dropout::new(0.2, &mut rng)?;
46    model.add(dropout2);
47
48    // Output layer with no activation
49    let dense3 = Dense::new(hidden_dim / 2, output_dim, None, &mut rng)?;
50    model.add(dense3);
51
52    Ok(model)
53}
examples/text_classification_complete.rs (lines 330-335)
313fn build_text_model(
314    _vocab_size: usize,
315    embedding_dim: usize,
316    hidden_dim: usize,
317    num_classes: usize,
318    max_length: usize,
319    rng: &mut SmallRng,
320) -> StdResult<Sequential<f32>> {
321    let mut model = Sequential::new();
322
323    // Note: This is a simplified version since our Sequential model expects specific layer types
324    // In a real implementation, we'd need specialized text layers
325
326    // Flatten the input tokens and use dense layers to simulate embedding and LSTM processing
327    let input_size = max_length; // Each token position
328
329    // Simulate embedding layer with dense transformation
330    model.add(Dense::new(
331        input_size,
332        embedding_dim * 2,
333        Some("relu"),
334        rng,
335    )?);
336    model.add(Dropout::new(0.1, rng)?);
337
338    // Simulate LSTM processing with dense layers
339    model.add(Dense::new(
340        embedding_dim * 2,
341        hidden_dim,
342        Some("tanh"),
343        rng,
344    )?);
345    model.add(Dropout::new(0.2, rng)?);
346
347    // Add attention-like mechanism with another dense layer
348    model.add(Dense::new(hidden_dim, hidden_dim / 2, Some("relu"), rng)?);
349    model.add(Dropout::new(0.2, rng)?);
350
351    // Classification head
352    model.add(Dense::new(
353        hidden_dim / 2,
354        num_classes,
355        Some("softmax"),
356        rng,
357    )?);
358
359    Ok(model)
360}
361
362/// Build an advanced text model with attention
363fn build_attention_text_model(
364    _vocab_size: usize,
365    embedding_dim: usize,
366    hidden_dim: usize,
367    num_classes: usize,
368    max_length: usize,
369    rng: &mut SmallRng,
370) -> StdResult<Sequential<f32>> {
371    let mut model = Sequential::new();
372
373    // Simplified attention-based model using dense layers
374    let input_size = max_length;
375
376    // Multi-layer processing to simulate transformer-like behavior
377    model.add(Dense::new(input_size, embedding_dim, Some("relu"), rng)?);
378    model.add(Dropout::new(0.1, rng)?);
379
380    // Simulate self-attention with dense layers
381    model.add(Dense::new(embedding_dim, hidden_dim, Some("relu"), rng)?);
382    model.add(Dropout::new(0.1, rng)?);
383
384    // Second attention layer
385    model.add(Dense::new(hidden_dim, hidden_dim, Some("relu"), rng)?);
386    model.add(Dropout::new(0.1, rng)?);
387
388    // Global pooling simulation
389    model.add(Dense::new(hidden_dim, hidden_dim / 2, Some("relu"), rng)?);
390    model.add(Dropout::new(0.2, rng)?);
391
392    // Classification head
393    model.add(Dense::new(
394        hidden_dim / 2,
395        num_classes,
396        Some("softmax"),
397        rng,
398    )?);
399
400    Ok(model)
401}
examples/generative_models_complete.rs (line 168)
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    }
536
537    pub fn forward(&self, noise: &ArrayD<f32>) -> StdResult<ArrayD<f32>> {
538        let output = self.layers.forward(noise)?;
539
540        // Reshape to image format
541        let batch_size = output.shape()[0];
542        let (height, width) = self.config.input_size;
543        let reshaped = output
544            .to_shape(IxDyn(&[batch_size, 1, height, width]))?
545            .to_owned();
546
547        Ok(reshaped)
548    }
549}
550
551/// Simple GAN Discriminator
552pub struct GANDiscriminator {
553    layers: Sequential<f32>,
554    config: GenerativeConfig,
555}
556
557impl GANDiscriminator {
558    pub fn new(config: GenerativeConfig, rng: &mut SmallRng) -> StdResult<Self> {
559        let mut layers = Sequential::new();
560
561        let input_size = config.input_size.0 * config.input_size.1;
562
563        layers.add(Dense::new(
564            input_size,
565            config.hidden_dims[0],
566            Some("relu"),
567            rng,
568        )?);
569        layers.add(Dropout::new(0.3, rng)?);
570
571        layers.add(Dense::new(
572            config.hidden_dims[0],
573            config.hidden_dims[1],
574            Some("relu"),
575            rng,
576        )?);
577        layers.add(Dropout::new(0.3, rng)?);
578
579        // Output probability of being real
580        layers.add(Dense::new(config.hidden_dims[1], 1, Some("sigmoid"), rng)?);
581
582        Ok(Self { layers, config })
583    }
examples/object_detection_complete.rs (line 183)
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    }
Source

pub fn len(&self) -> usize

Get the number of layers

Examples found in repository?
examples/text_classification_complete.rs (line 510)
458fn train_text_classifier() -> StdResult<()> {
459    println!("📝 Starting Text Classification Training Example");
460    println!("{}", "=".repeat(60));
461
462    let mut rng = SmallRng::seed_from_u64(42);
463
464    // Dataset parameters
465    let num_samples = 800;
466    let num_classes = 3;
467    let max_length = 20;
468    let embedding_dim = 64;
469    let hidden_dim = 128;
470
471    println!("📊 Dataset Configuration:");
472    println!("   - Samples: {}", num_samples);
473    println!(
474        "   - Classes: {} (Positive, Negative, Neutral)",
475        num_classes
476    );
477    println!("   - Max sequence length: {}", max_length);
478    println!("   - Embedding dimension: {}", embedding_dim);
479
480    // Create synthetic text dataset
481    println!("\n🔄 Creating synthetic text dataset...");
482    let dataset = TextDataset::create_synthetic_dataset(num_samples, num_classes, max_length);
483    let (train_dataset, val_dataset) = dataset.train_val_split(0.2);
484
485    println!("   - Vocabulary size: {}", dataset.vocab.vocab_size);
486    println!("   - Training samples: {}", train_dataset.len());
487    println!("   - Validation samples: {}", val_dataset.len());
488
489    // Show some example texts
490    println!("\n📄 Sample texts:");
491    for i in 0..3.min(train_dataset.texts.len()) {
492        println!(
493            "   [Class {}]: {}",
494            train_dataset.labels[i], train_dataset.texts[i]
495        );
496    }
497
498    // Build model
499    println!("\n🏗️  Building text classification model...");
500    let model = build_text_model(
501        dataset.vocab.vocab_size,
502        embedding_dim,
503        hidden_dim,
504        num_classes,
505        max_length,
506        &mut rng,
507    )?;
508
509    let total_params: usize = model.params().iter().map(|p| p.len()).sum();
510    println!("   - Model layers: {}", model.len());
511    println!("   - Total parameters: {}", total_params);
512
513    // Training configuration
514    let config = TrainingConfig {
515        batch_size: 16,
516        epochs: 30,
517        learning_rate: 0.001,
518        shuffle: true,
519        verbose: 1,
520        validation: Some(ValidationSettings {
521            enabled: true,
522            validation_split: 0.2,
523            batch_size: 32,
524            num_workers: 0,
525        }),
526        gradient_accumulation: None,
527        mixed_precision: None,
528        num_workers: 0,
529    };
530
531    println!("\n⚙️  Training Configuration:");
532    println!("   - Batch size: {}", config.batch_size);
533    println!("   - Learning rate: {}", config.learning_rate);
534    println!("   - Epochs: {}", config.epochs);
535
536    // Set up training
537    let loss_fn = CrossEntropyLoss::new(1e-7);
538    let optimizer = Adam::new(config.learning_rate as f32, 0.9, 0.999, 1e-8);
539
540    let mut trainer = Trainer::new(model, optimizer, loss_fn, config);
541
542    // Train the model
543    println!("\n🏋️  Starting training...");
544    println!("{}", "-".repeat(40));
545
546    let training_session = trainer.train(&train_dataset, Some(&val_dataset))?;
547
548    println!("\n✅ Training completed!");
549    println!("   - Epochs trained: {}", training_session.epochs_trained);
550
551    // Evaluate model
552    println!("\n📊 Final Evaluation:");
553    let val_metrics = trainer.validate(&val_dataset)?;
554
555    for (metric, value) in &val_metrics {
556        println!("   - {}: {:.4}", metric, value);
557    }
558
559    // Test on sample texts
560    println!("\n🔍 Sample Predictions:");
561    let sample_indices = vec![0, 1, 2, 3, 4];
562
563    // Manually collect batch since get_batch is not part of Dataset trait
564    let mut batch_tokens = Vec::new();
565    let mut batch_targets = Vec::new();
566
567    for &idx in &sample_indices {
568        let (tokens, targets) = val_dataset.get(idx)?;
569        batch_tokens.push(tokens);
570        batch_targets.push(targets);
571    }
572
573    // Concatenate into batch arrays
574    let sample_tokens = ndarray::concatenate(
575        ndarray::Axis(0),
576        &batch_tokens.iter().map(|a| a.view()).collect::<Vec<_>>(),
577    )?;
578    let sample_targets = ndarray::concatenate(
579        ndarray::Axis(0),
580        &batch_targets.iter().map(|a| a.view()).collect::<Vec<_>>(),
581    )?;
582
583    let model = trainer.get_model();
584    let predictions = model.forward(&sample_tokens)?;
585
586    let class_names = ["Positive", "Negative", "Neutral"];
587
588    for i in 0..sample_indices.len().min(val_dataset.texts.len()) {
589        let pred_row = predictions.slice(s![i, ..]);
590        let target_row = sample_targets.slice(s![i, ..]);
591
592        let pred_class = pred_row
593            .iter()
594            .enumerate()
595            .max_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap())
596            .map(|(i, _)| i)
597            .unwrap_or(0);
598
599        let true_class = target_row
600            .iter()
601            .enumerate()
602            .max_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap())
603            .map(|(i, _)| i)
604            .unwrap_or(0);
605
606        let confidence = pred_row[pred_class];
607
608        if sample_indices[i] < val_dataset.texts.len() {
609            println!("   Text: \"{}\"", val_dataset.texts[sample_indices[i]]);
610            println!(
611                "   Predicted: {} (confidence: {:.3})",
612                class_names[pred_class], confidence
613            );
614            println!("   Actual: {}", class_names[true_class]);
615            println!();
616        }
617    }
618
619    // Calculate detailed metrics
620    let detailed_metrics = calculate_text_metrics(&predictions, &sample_targets);
621    println!("📈 Detailed Metrics:");
622    for (metric, value) in &detailed_metrics {
623        println!("   - {}: {:.4}", metric, value);
624    }
625
626    Ok(())
627}
More examples
Hide additional examples
examples/image_classification_complete.rs (line 266)
233fn train_image_classifier() -> Result<()> {
234    println!("🚀 Starting Image Classification Training Example");
235    println!("{}", "=".repeat(60));
236
237    // Set up reproducible random number generator
238    let mut rng = SmallRng::seed_from_u64(42);
239
240    // Dataset parameters
241    let num_samples = 1000;
242    let num_classes = 5;
243    let image_size = (32, 32);
244    let input_channels = 3;
245
246    println!("📊 Dataset Configuration:");
247    println!("   - Samples: {}", num_samples);
248    println!("   - Classes: {}", num_classes);
249    println!("   - Image Size: {}x{}", image_size.0, image_size.1);
250    println!("   - Channels: {}", input_channels);
251
252    // Create synthetic dataset
253    println!("\n🔄 Creating synthetic dataset...");
254    let dataset = SyntheticImageDataset::new(num_samples, num_classes, image_size);
255    let (train_dataset, val_dataset) = dataset.train_val_split(0.2);
256
257    println!("   - Training samples: {}", train_dataset.len());
258    println!("   - Validation samples: {}", val_dataset.len());
259
260    // Build model
261    println!("\n🏗️  Building CNN model...");
262    let model = build_cnn_model(input_channels, num_classes, &mut rng)?;
263
264    // Count parameters
265    let total_params: usize = model.params().iter().map(|p| p.len()).sum();
266    println!("   - Model layers: {}", model.len());
267    println!("   - Total parameters: {}", total_params);
268
269    // Create training configuration
270    let config = create_training_config();
271    println!("\n⚙️  Training Configuration:");
272    println!("   - Batch size: {}", config.batch_size);
273    println!("   - Learning rate: {}", config.learning_rate);
274    println!("   - Epochs: {}", config.epochs);
275    println!(
276        "   - Validation split: {:.1}%",
277        config.validation.as_ref().unwrap().validation_split * 100.0
278    );
279
280    // Set up training components
281    let loss_fn = CrossEntropyLoss::new(1e-7);
282    let optimizer = Adam::new(config.learning_rate as f32, 0.9, 0.999, 1e-8);
283
284    // Create trainer
285    let mut trainer = Trainer::new(model, optimizer, loss_fn, config);
286
287    // Add callbacks
288    trainer.add_callback(Box::new(|| {
289        // Custom callback for additional logging
290        println!("🔄 Epoch completed");
291        Ok(())
292    }));
293
294    // Train the model
295    println!("\n🏋️  Starting training...");
296    println!("{}", "-".repeat(40));
297
298    let training_session = trainer.train(&train_dataset, Some(&val_dataset))?;
299
300    println!("\n✅ Training completed!");
301    println!("   - Epochs trained: {}", training_session.epochs_trained);
302    println!(
303        "   - Final learning rate: {:.6}",
304        training_session.initial_learning_rate
305    );
306
307    // Evaluate on validation set
308    println!("\n📊 Final Evaluation:");
309    let val_metrics = trainer.validate(&val_dataset)?;
310
311    for (metric, value) in &val_metrics {
312        println!("   - {}: {:.4}", metric, value);
313    }
314
315    // Test predictions on a few samples
316    println!("\n🔍 Sample Predictions:");
317    let sample_indices = vec![0, 1, 2, 3, 4];
318
319    // Manually collect batch since get_batch is not part of Dataset trait
320    let mut batch_images = Vec::new();
321    let mut batch_targets = Vec::new();
322
323    for &idx in &sample_indices {
324        let (img, target) = val_dataset.get(idx)?;
325        batch_images.push(img);
326        batch_targets.push(target);
327    }
328
329    // Concatenate into batch arrays
330    let sample_images = ndarray::concatenate(
331        Axis(0),
332        &batch_images.iter().map(|a| a.view()).collect::<Vec<_>>(),
333    )?;
334    let sample_targets = ndarray::concatenate(
335        Axis(0),
336        &batch_targets.iter().map(|a| a.view()).collect::<Vec<_>>(),
337    )?;
338
339    let model = trainer.get_model();
340    let predictions = model.forward(&sample_images)?;
341
342    for i in 0..sample_indices.len() {
343        let pred_row = predictions.slice(s![i, ..]);
344        let target_row = sample_targets.slice(s![i, ..]);
345
346        let pred_class = pred_row
347            .iter()
348            .enumerate()
349            .max_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap())
350            .map(|(i, _)| i)
351            .unwrap_or(0);
352
353        let target_class = target_row
354            .iter()
355            .enumerate()
356            .max_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap())
357            .map(|(i, _)| i)
358            .unwrap_or(0);
359
360        let confidence = pred_row[pred_class];
361
362        println!(
363            "   Sample {}: Predicted={}, Actual={}, Confidence={:.3}",
364            i + 1,
365            pred_class,
366            target_class,
367            confidence
368        );
369    }
370
371    // Calculate overall accuracy
372    let overall_predictions = trainer.get_model().forward(&sample_images)?;
373    let accuracy = calculate_accuracy(&overall_predictions, &sample_targets);
374    println!("\n🎯 Sample Accuracy: {:.2}%", accuracy * 100.0);
375
376    // Model summary
377    println!("\n📋 Training Summary:");
378    let session = trainer.get_session();
379    if let Some(loss_history) = session.get_metric("loss") {
380        if !loss_history.is_empty() {
381            println!("   - Initial loss: {:.4}", loss_history[0]);
382            println!(
383                "   - Final loss: {:.4}",
384                loss_history[loss_history.len() - 1]
385            );
386        }
387    }
388
389    if let Some(val_loss_history) = session.get_metric("val_loss") {
390        if !val_loss_history.is_empty() {
391            println!(
392                "   - Final validation loss: {:.4}",
393                val_loss_history[val_loss_history.len() - 1]
394            );
395        }
396    }
397
398    println!("\n🎉 Image classification example completed successfully!");
399
400    Ok(())
401}
Source

pub fn is_empty(&self) -> bool

Check if there are no layers

Trait Implementations§

Source§

impl<F: Float + Debug + ScalarOperand + 'static> Clone for Sequential<F>

Source§

fn clone(&self) -> Self

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: Float + Debug + ScalarOperand> Debug for Sequential<F>

Source§

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

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

impl<F: Float + Debug + ScalarOperand> Default for Sequential<F>

Source§

fn default() -> Self

Returns the “default value” for a type. Read more
Source§

impl<F: Float + Debug + ScalarOperand> Layer<F> for Sequential<F>

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 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 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 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 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 + 'static> ParamLayer<F> for Sequential<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 Sequential<F>

§

impl<F> !RefUnwindSafe for Sequential<F>

§

impl<F> Send for Sequential<F>

§

impl<F> Sync for Sequential<F>

§

impl<F> Unpin for Sequential<F>

§

impl<F> !UnwindSafe for Sequential<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