- 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
13 KiB
TinyTorch Milestone Fixes - Complete Analysis
Executive Summary
Created comprehensive learning verification tests that check actual learning (not just "code runs"). Found and fixed some issues, identified others that need deeper architectural fixes.
Status Dashboard
| Milestone | Status | Issue | Fix Complexity |
|---|---|---|---|
| ✅ Perceptron (1957) | PASSING | None | N/A |
| ✅ XOR (1969) | PASSING | None | N/A |
| ✅ MLP Digits (1986) | FIXED | Variable performance | ✅ Simple (more epochs) |
| ⚠️ CNN (1998) | BROKEN | No conv gradients | 🔴 Complex (autograd integration) |
| ⚠️ Transformer (2017) | BROKEN | No attention/embedding gradients | 🔴 Complex (autograd integration) |
✅ FIXED: MLP Digits (1986)
Problem
- Variable test results: sometimes 75% (pass), sometimes 63.5% (fail)
- Root cause: Random initialization + small dataset (1000 samples)
Solution Applied
Increased training epochs from 15 → 25
# Before:
epochs = 15 # Too few for small dataset
# After:
epochs = 25 # Sufficient for convergence
Results
- ✅ All 3 test runs now pass consistently
- ✅ Achieves 75-87.5% accuracy reliably
- ✅ Loss decreases 30%+
- ✅ All gradients flow correctly
Status: FIXED AND VERIFIED ✅
🔴 BROKEN: CNN (1998) - Critical Autograd Issue
Problem
Conv2d doesn't integrate with autograd at all
Symptoms
🔬 Training CNN...
Loss: 2.46 → 2.00 (barely decreasing)
Accuracy: 8.5% → 34.5% (random guessing)
❌ Gradients Flowing: 2/6 (only FC layer, NOT conv layers)
❌ Conv Gradients: 0.000000 (completely broken)
Root Cause Analysis
File: tinytorch/core/spatial.py
Issue 1: Missing requires_grad (FIXED BUT INSUFFICIENT)
# Line 87-88: Weights created without gradient tracking
self.weight = Tensor(np.random.normal(...)) # ❌ No requires_grad
self.bias = Tensor(np.zeros(...)) # ❌ No requires_grad
Fix applied:
self.weight = Tensor(np.random.normal(...), requires_grad=True) # ✅
self.bias = Tensor(np.zeros(...), requires_grad=True) # ✅
Issue 2: Forward Pass Bypasses Autograd Entirely (FUNDAMENTAL PROBLEM)
Line 188: return Tensor(output)
The entire forward() implementation uses raw numpy operations and .data access:
def forward(self, x):
# Line 147-151: Uses x.data directly (no gradient tracking)
padded_input = np.pad(x.data, ...)
# Line 154: Creates raw numpy array
output = np.zeros((batch_size, ...))
# Line 171-177: All operations on .data (bypasses autograd)
input_val = padded_input[b, in_ch, ...]
weight_val = self.weight.data[out_ch, ...] # ❌ Uses .data!
conv_sum += input_val * weight_val
# Line 186: Bias also uses .data
output[:, out_ch, :, :] += self.bias.data[out_ch]
# Line 188: Returns Tensor WITHOUT gradient function attached
return Tensor(output) # ❌ No computation graph!
Why This Breaks Learning
- No Computation Graph: Forward pass doesn't build a graph for backward()
.dataAccess Everywhere: Breaks gradient flow by accessing raw arrays- Missing Gradient Function: No
Conv2dBackwardattached to output Tensor - Manual numpy Operations: Autograd can't track manual loops and accumulations
What's Needed to Fix
Option 1: Implement Conv2dBackward (Recommended)
class Conv2dBackward:
"""Gradient function for Conv2d"""
def __init__(self, x, weight, bias, stride, padding):
self.x = x
self.weight = weight
# ... store context for backward
def backward(self, grad_output):
# Compute grad_input (deconvolution)
# Compute grad_weight (correlation)
# Compute grad_bias (sum over spatial dims)
return grad_input
def forward(self, x):
# ... existing convolution code ...
result = Tensor(output, requires_grad=(x.requires_grad or self.weight.requires_grad))
if result.requires_grad:
result._grad_fn = Conv2dBackward(x, self.weight, self.bias, ...)
return result
Option 2: Rewrite Using Tensor Operations (Cleaner)
def forward(self, x):
# Use tensor operations that autograd can track:
# - Use im2col to convert convolution to matrix multiplication
# - Use Tensor.matmul() instead of raw numpy
# - Autograd automatically handles gradients
pass
Option 3: Use PyTorch/JAX backend (Not educational)
Current Status
- ⚠️
requires_grad=Trueadded to weights (partial fix) - 🔴 Conv2d forward() still bypasses autograd completely
- 🔴 No backward() implementation
- 🔴 CNN milestones don't actually learn from convolutions
Estimated Fix Time: 4-6 hours (implement Conv2dBackward + test thoroughly)
🔴 BROKEN: Transformer (2017) - Similar Autograd Issues
Problem
Attention and Embedding layers don't propagate gradients
Symptoms
🔬 Training transformer...
Loss: 3.43 → 3.22 (minimal decrease)
❌ Gradients Flowing: 4/19 (only 21% of parameters!)
❌ Attention Gradients: No
❌ Embedding Gradients: No
Root Cause
Same as Conv2d - These layers likely:
- Use
.dataaccess in forward() - Return Tensors without gradient functions
- Don't integrate with autograd
Files to Check
tinytorch/text/embeddings.py- Embedding layertinytorch/core/attention.py- MultiHeadAttention layertinytorch/models/transformer.py- LayerNorm, TransformerBlock
What's Likely Broken
# Embedding.forward() probably does:
def forward(self, indices):
embedded = self.weight.data[indices] # ❌ Uses .data
return Tensor(embedded) # ❌ No grad_fn
# Should do:
def forward(self, indices):
embedded = self.weight.data[indices]
result = Tensor(embedded, requires_grad=self.weight.requires_grad)
if result.requires_grad:
result._grad_fn = EmbeddingBackward(self.weight, indices)
return result
Note: There was a fix for embedding gradients mentioned in GRADIENT_FLOW_VERIFICATION.md, but it may not be applied or may be insufficient.
Current Status
- 🔴 Only 4/19 transformer parameters receive gradients
- 🔴 Attention mechanism doesn't backprop
- 🔴 Embeddings don't learn
- 🔴 Transformer milestones don't actually learn from attention
Estimated Fix Time: 3-5 hours (implement EmbeddingBackward + AttentionBackward)
The Fundamental Pattern
The Problem
All custom layers that use manual numpy operations have the same issue:
# BROKEN PATTERN (current):
def forward(self, x):
# Manual numpy operations
result_data = np.some_operation(x.data) # ❌ Uses .data
return Tensor(result_data) # ❌ No grad tracking
# Gradient never flows backward!
The Solution
Two options:
Option A: Attach Gradient Functions (More control, educational)
def forward(self, x):
result_data = np.some_operation(x.data)
result = Tensor(result_data, requires_grad=True)
if x.requires_grad or self.param.requires_grad:
result._grad_fn = CustomBackward(x, self.param, ...)
return result
class CustomBackward:
def backward(self, grad_output):
# Compute gradients manually
return grad_input
Option B: Use Autograd-Tracked Operations (Less work, less control)
def forward(self, x):
# Use operations autograd already tracks
result = x.matmul(self.weight) # Autograd tracks this
result = result + self.bias # Autograd tracks this
return result # Gradient functions attached automatically
Layers That Need Fixing
Priority 1: Core Learning Blocks (CRITICAL)
- Conv2d - Breaks all CNN milestones
- Embedding - Breaks all NLP milestones
- MultiHeadAttention - Breaks transformer milestone
Priority 2: Supporting Layers (IMPORTANT)
- LayerNorm - May break transformer training stability
- MaxPool2d - If used in training (usually not trainable, but needs grad flow)
- AvgPool2d - Same as MaxPool2d
Priority 3: Optional Enhancements (NICE TO HAVE)
- Dropout - Usually handled correctly if using mask multiplication
- Other activations - Check ReLU, Sigmoid, etc. (likely fine)
Testing Strategy
What We Built
Comprehensive learning verification tests in test_learning_verification.py:
def test_cnn_learning():
"""Verifies CNN ACTUALLY LEARNS"""
model = build_cnn()
# Train the model
for epoch in range(epochs):
train_step(model, X, y)
# Verify learning happened:
✅ check_gradient_flow(params) # All params get gradients?
✅ check_weight_updates(before, after) # Weights changed?
✅ verify_loss_convergence(history) # Loss decreased?
✅ check_final_accuracy(model) # Model converged?
How to Use for Debugging
-
Run test for broken layer:
python tests/milestones/test_learning_verification.py -
Check gradient flow:
Gradients Flowing: 4/19 ← Only 4 params get gradients! Conv Gradients: 0.000000 ← Conv layer completely dead! -
Fix the layer (add gradient function)
-
Re-run test to verify fix
-
Iterate until all checks pass
Recommended Fix Order
Phase 1: CNN Fix (Highest Impact)
Time: 4-6 hours Impact: Enables all image processing milestones
- Implement
Conv2dBackwardgradient function - Modify
Conv2d.forward()to attach gradient function - Test with
test_cnn_learning() - Verify actual CNN milestone scripts work
Phase 2: Embedding Fix (High Impact)
Time: 2-3 hours Impact: Enables all NLP milestones
- Check if
EmbeddingBackwardexists (may already be implemented) - Verify
Embedding.forward()attaches gradient function - Test with
test_transformer_learning()
Phase 3: Attention Fix (High Impact)
Time: 3-4 hours Impact: Completes transformer support
- Implement
AttentionBackwardgradient function - Modify
MultiHeadAttention.forward()to attach gradient function - Test with
test_transformer_learning() - Verify all 19 params get gradients
Phase 4: Verification (Critical)
Time: 2-3 hours Impact: Ensures all fixes work end-to-end
- Run all learning verification tests
- Run actual milestone scripts (not just tests)
- Verify students can complete assignments
- Update documentation
Files Modified So Far
Test Files (Created/Modified)
- ✅
tests/milestones/test_learning_verification.py- Comprehensive learning tests - ✅
tests/milestones/README.md- Complete documentation - ✅
tests/milestones/VERIFICATION_SUMMARY.md- Quick overview - ✅
tests/milestones/FIXES_NEEDED.md- This file
Source Files (Modified)
- ⚠️
tinytorch/core/spatial.py- Addedrequires_grad=True(insufficient fix)
Source Files (Need Modification)
- 🔴
tinytorch/core/spatial.py- NeedsConv2dBackwardimplementation - 🔴
tinytorch/text/embeddings.py- Check/fix gradient flow - 🔴
tinytorch/core/attention.py- NeedsAttentionBackwardimplementation
Summary for User
What Works ✅
- Perceptron (1957) - Perfect learning, all tests pass
- XOR (1969) - Perfect learning, all tests pass
- MLP Digits (1986) - Fixed and verified, passes consistently
What's Broken 🔴
-
CNN (1998) - Conv2d doesn't integrate with autograd
- Conv layers don't receive gradients
- Model barely learns (random guessing)
- Needs
Conv2dBackwardimplementation
-
Transformer (2017) - Attention/Embedding don't integrate with autograd
- Only 21% of parameters receive gradients
- Attention and embeddings don't learn
- Needs
EmbeddingBackward+AttentionBackward
The Core Issue
Custom layers use manual numpy operations and bypass autograd entirely.
They need to either:
- Attach gradient functions to returned Tensors (more work, more control)
- Use tensor operations that autograd already tracks (less work)
This is a fundamental architectural issue that affects multiple modules.
Next Steps
- Decision needed: Fix Conv2d first (enables image processing) or Transformer first (enables NLP)?
- Implementation: Add backward() methods to custom layers
- Testing: Verify with learning verification tests
- Validation: Run actual milestone scripts end-to-end
Estimated Total Time
- Conv2d fix: 4-6 hours
- Embedding fix: 2-3 hours
- Attention fix: 3-4 hours
- Testing/validation: 2-3 hours
- Total: 11-16 hours of focused development
References
- Learning verification tests:
tests/milestones/test_learning_verification.py - Test documentation:
tests/milestones/README.md - Gradient flow guide:
tests/integration/INTERMODULE_TEST_COVERAGE.md - Transformer gradient notes:
milestones/05_2017_transformer/GRADIENT_FLOW_VERIFICATION.md