Files
TinyTorch/tests/module_12/test_ml_pipeline.py
Vijay Janapa Reddi 86b908fe5c Add TinyTorch examples gallery and fix module integration issues
- Create professional examples directory showcasing TinyTorch as real ML framework
- Add examples: XOR, MNIST, CIFAR-10, text generation, autograd demo, optimizer comparison
- Fix import paths in exported modules (training.py, dense.py)
- Update training module with autograd integration for loss functions
- Add progressive integration tests for all 16 modules
- Document framework capabilities and usage patterns

This commit establishes the examples gallery that demonstrates TinyTorch
works like PyTorch/TensorFlow, validating the complete framework.
2025-09-21 10:00:11 -04:00

368 lines
12 KiB
Python

"""
Integration Tests - Complete ML Pipeline
Tests how student modules work together in realistic ML workflows.
Uses working implementations for dependencies to avoid cascade failures.
"""
import pytest
import numpy as np
import sys
from pathlib import Path
# Add the project root to the path
project_root = Path(__file__).parent.parent.parent
sys.path.insert(0, str(project_root))
# Import working implementations (these should be from the package)
try:
from tinytorch.core.tensor import Tensor
from tinytorch.core.activations import ReLU, Sigmoid, Tanh, Softmax
from tinytorch.core.layers import Dense
from tinytorch.core.networks import Sequential, create_mlp
except ImportError:
# Fallback for development
sys.path.append(str(project_root / "modules" / "source" / "01_tensor"))
sys.path.append(str(project_root / "modules" / "source" / "02_activations"))
sys.path.append(str(project_root / "modules" / "source" / "03_layers"))
sys.path.append(str(project_root / "modules" / "source" / "04_networks"))
from tensor_dev import Tensor
from activations_dev import ReLU, Sigmoid, Tanh, Softmax
from layers_dev import Dense
from networks_dev import Sequential, create_mlp
class TestBasicMLPipeline:
"""Test basic ML pipeline: Tensor → Layers → Networks."""
def test_tensor_to_layer_integration(self):
"""Test tensor works with dense layers."""
# Create tensor
x = Tensor([[1.0, 2.0, 3.0]])
# Create layer
layer = Dense(input_size=3, output_size=2)
# Forward pass
output = layer(x)
# Verify integration
assert hasattr(output, 'data') and hasattr(output, 'shape') # Tensor-like
assert output.shape == (1, 2)
assert np.all(np.isfinite(output.data))
def test_layer_to_activation_integration(self):
"""Test layers work with activation functions."""
# Create layer and activation
layer = Dense(input_size=2, output_size=3)
activation = ReLU()
# Test data
x = Tensor([[1.0, -1.0]])
# Forward pass
linear_output = layer(x)
activated_output = activation(linear_output)
# Verify integration
assert hasattr(activated_output, 'data') and hasattr(activated_output, 'shape')
assert activated_output.shape == linear_output.shape
assert np.all(activated_output.data >= 0) # ReLU property
def test_complete_forward_pass(self):
"""Test complete forward pass through network."""
# Create network
network = Sequential([
Dense(input_size=4, output_size=3),
ReLU(),
Dense(input_size=3, output_size=2),
Sigmoid()
])
# Test data
x = Tensor([[1.0, 2.0, 3.0, 4.0]])
# Forward pass
output = network(x)
# Verify complete pipeline
assert hasattr(output, 'data') and hasattr(output, 'shape')
assert output.shape == (1, 2)
assert np.all(output.data >= 0) and np.all(output.data <= 1) # Sigmoid range
class TestMLApplications:
"""Test realistic ML applications."""
def test_binary_classification(self):
"""Test binary classification pipeline."""
# Create binary classifier
classifier = create_mlp(
input_size=4,
hidden_sizes=[8, 4],
output_size=1
)
# Simulate batch of data
batch_size = 10
x = Tensor(np.random.randn(batch_size, 4))
# Forward pass
predictions = classifier(x)
# Verify binary classification
assert predictions.shape == (batch_size, 1)
assert np.all(predictions.data >= 0) and np.all(predictions.data <= 1)
def test_multiclass_classification(self):
"""Test multi-class classification pipeline."""
# Create multi-class classifier
classifier = create_mlp(
input_size=8,
hidden_sizes=[16, 8],
output_size=3
)
# Simulate batch of data
batch_size = 5
x = Tensor(np.random.randn(batch_size, 8))
# Forward pass
predictions = classifier(x)
# Verify multi-class classification
assert predictions.shape == (batch_size, 3)
# Check softmax properties
for i in range(batch_size):
row_sum = np.sum(predictions.data[i])
assert abs(row_sum - 1.0) < 1e-4, f"Row {i} sum: {row_sum}"
assert np.all(predictions.data[i] >= 0), f"Row {i} has negative values"
def test_regression_pipeline(self):
"""Test regression pipeline."""
# Create regression network (no output activation)
regressor = Sequential([
Dense(input_size=5, output_size=10),
ReLU(),
Dense(input_size=10, output_size=5),
Tanh(),
Dense(input_size=5, output_size=1)
])
# Simulate batch of data
batch_size = 8
x = Tensor(np.random.randn(batch_size, 5))
# Forward pass
predictions = regressor(x)
# Verify regression
assert predictions.shape == (batch_size, 1)
assert np.all(np.isfinite(predictions.data))
class TestArchitectureComparison:
"""Test different network architectures."""
def test_shallow_vs_deep_networks(self):
"""Test shallow vs deep network behavior."""
input_size = 6
output_size = 2
# Shallow network
shallow = create_mlp(
input_size=input_size,
hidden_sizes=[20],
output_size=output_size
)
# Deep network
deep = create_mlp(
input_size=input_size,
hidden_sizes=[8, 8, 8],
output_size=output_size
)
# Test same input
x = Tensor([[1.0, 2.0, 3.0, 4.0, 5.0, 6.0]])
shallow_output = shallow(x)
deep_output = deep(x)
# Both should work
assert shallow_output.shape == (1, output_size)
assert deep_output.shape == (1, output_size)
# Different architectures should give different results
assert not np.allclose(shallow_output.data, deep_output.data)
def test_different_activation_functions(self):
"""Test networks with different activation functions."""
# Test with basic Sequential networks
relu_network = Sequential([
Dense(input_size=3, output_size=4),
ReLU(),
Dense(input_size=4, output_size=2),
Sigmoid()
])
sigmoid_network = Sequential([
Dense(input_size=3, output_size=4),
Sigmoid(),
Dense(input_size=4, output_size=2),
Sigmoid()
])
tanh_network = Sequential([
Dense(input_size=3, output_size=4),
Tanh(),
Dense(input_size=4, output_size=2),
Sigmoid()
])
networks = [relu_network, sigmoid_network, tanh_network]
results = []
for network in networks:
x = Tensor([[1.0, -1.0, 0.5]])
output = network(x)
results.append(output.data)
# All should produce valid outputs
assert output.shape == (1, 2)
assert np.all(output.data >= 0) and np.all(output.data <= 1)
# Different activations should give different results
assert not np.allclose(results[0], results[1])
assert not np.allclose(results[1], results[2])
class TestNumericalStability:
"""Test numerical stability across modules."""
def test_large_inputs(self):
"""Test with large input values."""
network = create_mlp(
input_size=3,
hidden_sizes=[4],
output_size=2
)
# Large positive values
x_large = Tensor([[100.0, 200.0, 300.0]])
output_large = network(x_large)
# Should not produce NaN or Inf
assert np.all(np.isfinite(output_large.data))
assert output_large.shape == (1, 2)
def test_small_inputs(self):
"""Test with very small input values."""
network = Sequential([
Dense(input_size=3, output_size=4),
Tanh(),
Dense(input_size=4, output_size=2),
Sigmoid()
])
# Very small values
x_small = Tensor([[1e-8, -1e-8, 1e-10]])
output_small = network(x_small)
# Should not produce NaN or Inf
assert np.all(np.isfinite(output_small.data))
assert output_small.shape == (1, 2)
def test_batch_processing(self):
"""Test batch processing stability."""
network = create_mlp(
input_size=4,
hidden_sizes=[6, 3],
output_size=2
)
# Large batch
batch_size = 100
x = Tensor(np.random.randn(batch_size, 4))
output = network(x)
# Should handle large batches
assert output.shape == (batch_size, 2)
assert np.all(np.isfinite(output.data))
# Softmax properties should hold for each sample
for i in range(batch_size):
row_sum = np.sum(output.data[i])
assert abs(row_sum - 1.0) < 1e-3 # Relaxed tolerance
class TestErrorHandling:
"""Test error handling across modules."""
def test_shape_mismatch_errors(self):
"""Test proper error handling for shape mismatches."""
layer = Dense(input_size=3, output_size=2)
# Wrong input size
x_wrong = Tensor([[1.0, 2.0]]) # Should be size 3
with pytest.raises(Exception): # Should raise some kind of error
layer(x_wrong)
def test_empty_network(self):
"""Test handling of empty networks."""
# Empty network should raise error or handle gracefully
try:
empty_network = Sequential([])
x = Tensor([[1.0, 2.0]])
output = empty_network(x)
# If it doesn't raise an error, output should equal input
assert np.allclose(output.data, x.data)
except Exception:
# It's also fine if it raises an error
pass
def test_integration_summary():
"""Summary test showing complete integration."""
print("🔗 Integration Test Summary")
print("=" * 50)
# Create a complete ML pipeline
network = Sequential([
Dense(input_size=4, output_size=8), # Tensor → Layer
ReLU(), # Layer → Activation
Dense(input_size=8, output_size=4), # Activation → Layer
Tanh(), # Layer → Activation
Dense(input_size=4, output_size=2), # Activation → Layer
Softmax() # Layer → Activation
])
# Test with batch data
batch_size = 20
x = Tensor(np.random.randn(batch_size, 4))
# Forward pass
output = network(x)
# Verify complete integration
assert output.shape == (batch_size, 2)
assert np.all(np.isfinite(output.data))
# Check softmax properties
for i in range(batch_size):
row_sum = np.sum(output.data[i])
assert abs(row_sum - 1.0) < 1e-4
print("✅ Complete ML pipeline integration successful!")
print(f" Input shape: {x.shape}")
print(f" Output shape: {output.shape}")
print(f" Network layers: {len(network.layers)}")
print(" All modules working together correctly!")
if __name__ == "__main__":
test_integration_summary()