mirror of
https://github.com/MLSysBook/TinyTorch.git
synced 2026-07-16 12:11:58 -05:00
- Created test suite that verifies actual learning (gradient flow, weight updates, loss convergence) - Fixed MLP Digits (1986): increased training epochs from 15 to 25 - Added requires_grad=True to Conv2d weights (partial fix) - Identified gradient flow issues in Conv2d, Embedding, and Attention layers - Comprehensive documentation of issues and fixes needed
353 lines
11 KiB
Markdown
353 lines
11 KiB
Markdown
# TinyTorch Milestone Learning Verification Tests
|
|
|
|
## Overview
|
|
|
|
This test suite verifies that **actual LEARNING** is happening in TinyTorch milestones, not just that code runs without errors. We check:
|
|
|
|
1. **Loss Convergence**: Loss decreases significantly over training
|
|
2. **Gradient Flow**: All parameters receive non-zero gradients
|
|
3. **Weight Updates**: Parameters actually change during training
|
|
4. **Performance**: Models achieve expected accuracy/performance
|
|
|
|
This is the "trust but verify" approach to ML systems - we don't just hope learning happens, we **prove** it with rigorous tests.
|
|
|
|
## Test Suite Structure
|
|
|
|
### Main Test File
|
|
|
|
**`test_learning_verification.py`** - Comprehensive learning verification for all milestones
|
|
|
|
### Tests Included
|
|
|
|
| Test | Milestone | What It Verifies |
|
|
|------|-----------|------------------|
|
|
| `test_perceptron_learning()` | 1957 Perceptron | Linear classification with gradient descent |
|
|
| `test_xor_learning()` | 1969 XOR | Multi-layer network solves non-linear problem |
|
|
| `test_mlp_digits_learning()` | 1986 MLP | Real-world digit classification |
|
|
| `test_cnn_learning()` | 1998 CNN | Convolutional learning on images |
|
|
| `test_transformer_learning()` | 2017 Transformer | Attention-based sequence modeling |
|
|
|
|
## Running the Tests
|
|
|
|
### Run All Tests
|
|
|
|
```bash
|
|
cd /Users/VJ/GitHub/TinyTorch
|
|
python tests/milestones/test_learning_verification.py
|
|
```
|
|
|
|
### Run with pytest
|
|
|
|
```bash
|
|
pytest tests/milestones/test_learning_verification.py -v
|
|
```
|
|
|
|
### Run Individual Tests
|
|
|
|
```python
|
|
from tests.milestones.test_learning_verification import test_perceptron_learning
|
|
test_perceptron_learning()
|
|
```
|
|
|
|
## What Each Test Checks
|
|
|
|
### 1. Gradient Flow Verification
|
|
|
|
```python
|
|
def check_gradient_flow(parameters):
|
|
"""
|
|
Verifies gradients are flowing properly:
|
|
- All parameters have gradients
|
|
- Gradients are non-zero
|
|
- Gradients have reasonable magnitude (not exploding/vanishing)
|
|
- No parameters stuck with zero gradients
|
|
"""
|
|
```
|
|
|
|
**Why it matters**: If gradients don't flow, training is broken. This catches the most common training failures.
|
|
|
|
### 2. Weight Update Verification
|
|
|
|
```python
|
|
def check_weight_updates(params_before, params_after):
|
|
"""
|
|
Verifies weights actually changed during training:
|
|
- Parameters before vs after training differ
|
|
- Updates have reasonable magnitude
|
|
- No parameters frozen/unchanged
|
|
"""
|
|
```
|
|
|
|
**Why it matters**: Weights not updating = optimizer not working. Catches broken optimizer step() or zero learning rates.
|
|
|
|
### 3. Loss Convergence Verification
|
|
|
|
```python
|
|
def verify_loss_convergence(loss_history, min_decrease=0.1):
|
|
"""
|
|
Verifies loss is decreasing (learning is happening):
|
|
- Initial loss > Final loss
|
|
- Decrease is significant (not just noise)
|
|
- Loss generally decreases over time
|
|
"""
|
|
```
|
|
|
|
**Why it matters**: Loss not decreasing = model not learning. This is the ultimate test of whether learning actually happens.
|
|
|
|
## Test Output
|
|
|
|
### Successful Test
|
|
|
|
```
|
|
🔬 Training perceptron...
|
|
Epoch 0: Loss = 0.6129
|
|
Epoch 10: Loss = 0.5530
|
|
Epoch 20: Loss = 0.5214
|
|
|
|
📊 Learning Verification Results:
|
|
┌───────────────────────┬──────────┬─────────┐
|
|
│ Metric │ Value │ Status │
|
|
├───────────────────────┼──────────┼─────────┤
|
|
│ Final Accuracy │ 92.0% │ ✅ PASS │
|
|
│ Loss Decrease │ 52.3% │ ✅ PASS │
|
|
│ Gradients Flowing │ 2/2 │ ✅ PASS │
|
|
│ Mean Gradient Mag │ 0.208659 │ ✅ PASS │
|
|
│ Weights Updated │ 2/2 │ ✅ PASS │
|
|
│ Mean Weight Change │ 0.468087 │ ✅ PASS │
|
|
└───────────────────────┴──────────┴─────────┘
|
|
|
|
✅ PERCEPTRON LEARNING VERIFIED
|
|
• Loss decreased significantly
|
|
• Gradients flow properly
|
|
• Weights updated correctly
|
|
• Model converged to high accuracy
|
|
```
|
|
|
|
### Failed Test
|
|
|
|
```
|
|
🔬 Training CNN on TinyDigits...
|
|
Epoch 0: Loss = 2.3525
|
|
Epoch 3: Loss = 2.2526
|
|
Epoch 6: Loss = 2.2015
|
|
|
|
📊 Learning Verification Results:
|
|
┌───────────────────────┬──────────┬─────────┐
|
|
│ Metric │ Value │ Status │
|
|
├───────────────────────┼──────────┼─────────┤
|
|
│ Final Accuracy │ 45.0% │ ❌ FAIL │
|
|
│ Loss Decrease │ 8.3% │ ❌ FAIL │
|
|
│ Gradients Flowing │ 4/6 │ ❌ FAIL │
|
|
│ Conv Gradients │ 0.000000 │ ❌ FAIL │
|
|
│ Weights Updated │ 4/6 │ ❌ FAIL │
|
|
└───────────────────────┴──────────┴─────────┘
|
|
|
|
❌ CNN LEARNING FAILED
|
|
• Convolutional gradients not flowing
|
|
• Check Conv2d backward() implementation
|
|
```
|
|
|
|
## Understanding the Metrics
|
|
|
|
### Gradient Metrics
|
|
|
|
- **Gradients Flowing**: `X/Y` means X out of Y parameters received gradients
|
|
- ✅ Should be `Y/Y` (all parameters)
|
|
- ❌ If less, some parameters aren't being trained
|
|
|
|
- **Mean Gradient Magnitude**: Average absolute gradient value
|
|
- ✅ Should be > 1e-6 (gradients exist and are meaningful)
|
|
- ❌ If ~0, gradients vanishing or not flowing
|
|
- ❌ If very large (>100), gradients exploding
|
|
|
|
### Weight Metrics
|
|
|
|
- **Weights Updated**: How many parameters actually changed
|
|
- ✅ Should equal total parameters
|
|
- ❌ If less, optimizer not updating or LR too small
|
|
|
|
- **Mean Weight Change**: Average change in parameter values
|
|
- ✅ Should be > 1e-4 (parameters actually moving)
|
|
- ❌ If ~0, learning rate too small or optimizer broken
|
|
|
|
### Loss Metrics
|
|
|
|
- **Loss Decrease**: `(initial_loss - final_loss) / initial_loss * 100%`
|
|
- ✅ Should be > 30% for simple tasks
|
|
- ✅ Should be > 10% for complex tasks
|
|
- ❌ If < 10%, model not learning effectively
|
|
|
|
## Common Failure Modes
|
|
|
|
### Gradients Not Flowing
|
|
|
|
**Symptoms**:
|
|
- `Gradients Flowing: X/Y` where X < Y
|
|
- Some parameters show "Gradients: No"
|
|
|
|
**Causes**:
|
|
- Missing `.backward()` call
|
|
- Incorrect autograd implementation
|
|
- Parameters not connected to loss (dead branches)
|
|
- `.data` access breaking computation graph
|
|
|
|
**Fix**: Check backward() implementation for each layer
|
|
|
|
### Weights Not Updating
|
|
|
|
**Symptoms**:
|
|
- `Weights Updated: X/Y` where X < Y
|
|
- `Mean Weight Change: 0.000000`
|
|
|
|
**Causes**:
|
|
- Optimizer not calling `step()`
|
|
- Learning rate = 0
|
|
- Parameters don't have `requires_grad=True`
|
|
- Gradients being cleared before step()
|
|
|
|
**Fix**: Check optimizer step() and learning rate
|
|
|
|
### Loss Not Decreasing
|
|
|
|
**Symptoms**:
|
|
- `Loss Decrease: 5.2%` (very small)
|
|
- Loss stays roughly constant
|
|
|
|
**Causes**:
|
|
- Learning rate too small
|
|
- Learning rate too large (diverging)
|
|
- Wrong loss function for task
|
|
- Data/label mismatch
|
|
- Architecture too weak for task
|
|
|
|
**Fix**: Try different learning rates, check data/labels
|
|
|
|
## Integration with TinyTorch Development
|
|
|
|
### When to Run These Tests
|
|
|
|
1. **After implementing new modules**: Verify learning still works
|
|
2. **Before major releases**: Ensure all milestones pass
|
|
3. **When debugging training**: Identify where learning breaks
|
|
4. **After autograd changes**: Verify gradient flow still works
|
|
|
|
### Adding New Milestone Tests
|
|
|
|
Template for new tests:
|
|
|
|
```python
|
|
def test_new_milestone_learning():
|
|
"""
|
|
Verify [milestone name] learns on [task description].
|
|
|
|
Expected behavior:
|
|
- Loss should decrease by >X%
|
|
- All Y parameters should receive gradients
|
|
- Final performance should be >Z%
|
|
"""
|
|
console.print("\\n" + "="*70)
|
|
console.print(Panel.fit(
|
|
"[bold cyan]TEST N: [Milestone Name][/bold cyan]\\n"
|
|
"[dim][Year] - [Key Paper/Researcher][/dim]",
|
|
border_style="cyan"
|
|
))
|
|
|
|
# 1. Create data
|
|
X, y = create_data()
|
|
|
|
# 2. Build model
|
|
model = build_model()
|
|
params = model.parameters()
|
|
params_before = [Tensor(p.data.copy()) for p in params]
|
|
|
|
# 3. Train
|
|
loss_fn = SomeLoss()
|
|
optimizer = SomeOptimizer(params, lr=0.01)
|
|
loss_history = []
|
|
|
|
for epoch in range(epochs):
|
|
predictions = model(X)
|
|
loss = loss_fn(predictions, y)
|
|
loss.backward()
|
|
|
|
if epoch == 0:
|
|
grad_stats = check_gradient_flow(params)
|
|
|
|
optimizer.step()
|
|
optimizer.zero_grad()
|
|
loss_history.append(loss.data.item())
|
|
|
|
# 4. Verify learning
|
|
weight_stats = check_weight_updates(params_before, params)
|
|
convergence_stats = verify_loss_convergence(loss_history, min_decrease=0.3)
|
|
|
|
# 5. Display results
|
|
# ... create table with metrics ...
|
|
|
|
# 6. Return pass/fail
|
|
passed = (
|
|
convergence_stats['converged'] and
|
|
grad_stats['params_with_grad'] == grad_stats['total_params'] and
|
|
weight_stats['params_updated'] == weight_stats['total_params']
|
|
)
|
|
|
|
return passed
|
|
```
|
|
|
|
## Philosophy
|
|
|
|
### Why Test Learning, Not Just Code?
|
|
|
|
**Traditional Unit Tests**: "Does the function return the right shape?"
|
|
**Learning Verification Tests**: "Does the model actually learn?"
|
|
|
|
**Example**:
|
|
- ✅ Unit test: `assert output.shape == (batch_size, num_classes)`
|
|
- 🔥 Learning test: `assert final_accuracy > 90% and loss_decreased > 50%`
|
|
|
|
### The "Real Learning" Standard
|
|
|
|
A milestone passes if:
|
|
1. **Loss decreases significantly** (not just random fluctuations)
|
|
2. **Gradients flow to ALL parameters** (no dead weights)
|
|
3. **Weights actually update** (optimizer working)
|
|
4. **Final performance meets expectations** (model converges)
|
|
|
|
If any of these fail, learning is broken - even if the code "works".
|
|
|
|
## Results Summary
|
|
|
|
Current status of TinyTorch milestones:
|
|
|
|
| Milestone | Status | Notes |
|
|
|-----------|--------|-------|
|
|
| 1957 Perceptron | ✅ PASS | Learns linear classification perfectly |
|
|
| 1969 XOR | ✅ PASS | Solves XOR with multi-layer network |
|
|
| 1986 MLP Digits | ⚠️ VARIABLE | Sometimes passes (depends on init) |
|
|
| 1998 CNN | ⚠️ NEEDS WORK | Gradient flow issues in Conv2d |
|
|
| 2017 Transformer | ⚠️ NEEDS WORK | Attention/embedding gradient flow |
|
|
|
|
### Next Steps
|
|
|
|
For failing tests:
|
|
1. **CNN**: Debug Conv2d backward() - gradients not flowing properly
|
|
2. **Transformer**: Debug attention backward() - only 4/19 params get gradients
|
|
3. **MLP Digits**: Improve initialization or increase training epochs
|
|
|
|
## Files
|
|
|
|
- `test_learning_verification.py` - Main test suite
|
|
- `README.md` - This file
|
|
- `INTERMODULE_TEST_COVERAGE.md` - Related integration tests
|
|
|
|
## Related Documentation
|
|
|
|
- `/tests/integration/INTERMODULE_TEST_COVERAGE.md` - Integration tests
|
|
- `/milestones/*/GRADIENT_FLOW_VERIFICATION.md` - Milestone-specific docs
|
|
- `/docs/development/REAL_DATA_REAL_SYSTEMS.md` - Development philosophy
|
|
|
|
---
|
|
|
|
**Remember**: Code that runs is not the same as code that learns. These tests verify the latter.
|
|
|