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>
impl<F: Float + Debug + ScalarOperand> Sequential<F>
Sourcepub fn new() -> Self
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
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 }
Additional examples can be found in:
Sourcepub fn add<L: Layer<F> + Send + Sync + 'static>(&mut self, layer: L)
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
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 }
Additional examples can be found in:
Sourcepub fn len(&self) -> usize
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
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}
Trait Implementations§
Source§impl<F: Float + Debug + ScalarOperand + 'static> Clone for Sequential<F>
impl<F: Float + Debug + ScalarOperand + 'static> Clone for Sequential<F>
Source§impl<F: Float + Debug + ScalarOperand> Debug for Sequential<F>
impl<F: Float + Debug + ScalarOperand> Debug for Sequential<F>
Source§impl<F: Float + Debug + ScalarOperand> Default for Sequential<F>
impl<F: Float + Debug + ScalarOperand> Default for Sequential<F>
Source§impl<F: Float + Debug + ScalarOperand> Layer<F> for Sequential<F>
impl<F: Float + Debug + ScalarOperand> Layer<F> for Sequential<F>
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 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 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 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 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
Source§impl<F: Float + Debug + ScalarOperand + 'static> ParamLayer<F> for Sequential<F>
impl<F: Float + Debug + ScalarOperand + 'static> ParamLayer<F> for Sequential<F>
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> 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