From 0ca2ab1efe67e66d25ad7a0a2354e19acfb6b1af Mon Sep 17 00:00:00 2001 From: Vijay Janapa Reddi Date: Mon, 29 Sep 2025 13:49:08 -0400 Subject: [PATCH] Enhance modules 01-04 with ASCII diagrams and improved flow Following Module 05's successful visual learning patterns: - Add ASCII diagrams for complex concepts - Natural markdown flow explaining what's about to happen - Visual memory layouts, data flows, and computation graphs - Enhanced test sections with clear explanations - Consistent with new MODULE_DEVELOPMENT guidelines Module 01 (Tensor): - Tensor dimension hierarchy visualization - Memory layout and broadcasting diagrams - Matrix multiplication step-by-step Module 02 (Activations): - Linearity problem and activation curves - Dead neuron visualization for ReLU - Softmax probability transformation Module 03 (Layers): - Linear layer computation visualization - Parameter management hierarchy - Batch processing shape transformations Module 04 (Losses): - Loss landscape visualizations - MSE quadratic penalty diagrams - CrossEntropy confidence patterns All modules tested and working correctly --- .claude/guidelines/MODULE_DEVELOPMENT.md | 153 ++++++++++++- modules/01_tensor/tensor_dev.py | 264 ++++++++++++++++++---- modules/02_activations/activations_dev.py | 222 ++++++++++++++++-- modules/03_layers/layers_dev.py | 80 ++++++- modules/04_losses/losses_dev.py | 85 ++++++- 5 files changed, 737 insertions(+), 67 deletions(-) diff --git a/.claude/guidelines/MODULE_DEVELOPMENT.md b/.claude/guidelines/MODULE_DEVELOPMENT.md index 86a64c18..9e2c8e6a 100644 --- a/.claude/guidelines/MODULE_DEVELOPMENT.md +++ b/.claude/guidelines/MODULE_DEVELOPMENT.md @@ -88,6 +88,94 @@ if __name__ == "__main__": """ ``` +## ๐Ÿ“Š Visual Learning with ASCII Diagrams - MANDATORY + +**Every module MUST include ASCII diagrams to visualize key concepts.** + +ASCII diagrams provide immediate visual understanding without dependencies. They work in terminals, IDEs, notebooks, and are part of CS education tradition. + +### When to Use ASCII Diagrams: +- **Data structures**: Show memory layout and object relationships +- **Algorithms**: Visualize step-by-step execution +- **Gradient flow**: Forward and backward passes +- **Computation graphs**: Operation dependencies +- **Performance**: Memory usage, complexity scaling +- **Systems concepts**: Cache patterns, parallelization + +### ASCII Art Toolkit: +``` +Box drawings: โ”Œโ”€โ”โ”‚โ””โ”˜โ”œโ”คโ”ฌโ”ดโ”ผโ•”โ•โ•—โ•‘โ•šโ•โ• โ•ฃโ•ฆโ•ฉโ•ฌ +Arrows: โ†’ โ† โ†“ โ†‘ โ‡’ โ‡ โ‡“ โ‡‘ +Math symbols: โˆ‚ โˆ‡ ฮฃ โˆ โˆซ โ‰ˆ โ‰  โ‰ค โ‰ฅ ยฑ ร— +Progress bars: โ–‘โ–’โ–“โ–ˆ +``` + +### Example Excellence from Module 05: +``` + Forward Pass: Backward Pass: + x(2.0) โ”€โ”€โ”€โ”€โ” x.grad โ†โ”€โ”€ 1.0 + โ”œโ”€โ–บ [+] โ”€โ”€โ–บ z(5.0) โ†‘ + y(3.0) โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ”‚ + โ–ผ โ”‚ + z.backward(1.0) โ”€โ”€โ”€โ”˜ +``` + +### ASCII vs Equations: +Let the content decide. Sometimes ASCII explains flow, sometimes equations explain math. Use both: +``` +Product Rule: โˆ‚z/โˆ‚x = y, โˆ‚z/โˆ‚y = x + + x(2.0) โ”€โ”€โ” + โ”œโ”€[ร—]โ†’ z(6.0): grad_x = grad ร— y + y(3.0) โ”€โ”€โ”˜ grad_y = grad ร— x +``` + +## ๐Ÿ“ Markdown Flow - Natural Narrative + +**CRITICAL: Markdown cells should flow naturally, explaining what's about to happen.** + +### Opening Explanation Pattern: +Start each module with clear context about WHAT we're implementing and WHY: + +```python +# %% [markdown] +""" +# Module 05: Autograd - Automatic Differentiation + +Here's what we're actually implementing: a system that automatically computes gradients by tracking operations and applying the chain rule backward. This requires: + +1. Extending our Tensor data structure to remember gradients +2. Making operations "smart" so they record how to reverse themselves +3. Building a computation graph as we compute forward +4. Traversing that graph backward to compute all gradients + +Let's build this step by step, with immediate validation at each stage. +""" +``` + +### Step Introduction Pattern: +Each implementation step should explain the immediate goal: + +```python +# %% [markdown] +""" +Now we'll make addition smart. When we compute z = x + y, we need z to remember +how to send gradients back to both x and y. Since โˆ‚z/โˆ‚x = 1 and โˆ‚z/โˆ‚y = 1, +both inputs will receive the same gradient unchanged. + +Here's what the gradient flow looks like: +[ASCII diagram here] + +Let's implement this enhancement: +""" +``` + +### Natural Flow Rules: +- โŒ Don't add redundant section headers within explanations +- โœ… Let the narrative flow from concept โ†’ visual โ†’ implementation โ†’ test +- โœ… Use diagrams inline where they clarify understanding +- โœ… Explain WHAT you're about to do and WHY before showing code + ## ๐Ÿงช Implementation โ†’ Test Pattern **MANDATORY**: Every implementation must be immediately followed by a test. @@ -422,6 +510,40 @@ class ReLU: return grad * (self.input > 0) ``` +## ๐ŸŽจ Visual Excellence Examples + +### Memory Layout Visualization: +``` + Tensor Without Gradients: Tensor With Gradients: + โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” + โ”‚ data: [1,2,3] โ”‚ โ”‚ data: [1,2,3] โ”‚ + โ”‚ shape: (3,) โ”‚ โ”‚ requires_grad: True โ”‚ + โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ grad: None โ†’ [โˆ‡โ‚,โˆ‡โ‚‚,โˆ‡โ‚ƒ] โ”‚ + โ”‚ grad_fn: โ”‚ + โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ +``` + +### Algorithm Step Visualization: +``` + Convolution Sliding Window: + Input: Kernel: Step 1: Step 2: + โ”Œโ”€โ”ฌโ”€โ”ฌโ”€โ”ฌโ”€โ” โ”Œโ”€โ”ฌโ”€โ” โ•”โ•โ•ฆโ•โ•—โ”ฌโ”€โ”ฌโ”€โ” โ”Œโ”€โ•”โ•โ•ฆโ•โ•—โ”€โ” + โ”œโ”€โ”ผโ”€โ”ผโ”€โ”ผโ”€โ”ค โ”œโ”€โ”ผโ”€โ”ค โ†’ โ• โ•โ•ฌโ•โ•ฃโ”ผโ”€โ”ผโ”€โ”ค โ†’ โ”œโ”€โ• โ•โ•ฌโ•โ•ฃโ”€โ”ค + โ”œโ”€โ”ผโ”€โ”ผโ”€โ”ผโ”€โ”ค โ””โ”€โ”ดโ”€โ”˜ โ•šโ•โ•ฉโ•โ•โ”ดโ”€โ”ดโ”€โ”˜ โ””โ”€โ•šโ•โ•ฉโ•โ•โ”€โ”˜ + โ””โ”€โ”ดโ”€โ”ดโ”€โ”ดโ”€โ”˜ Output: 6 Output: 8 +``` + +### Performance Scaling: +``` + Memory Usage vs Network Depth: + + 10 layers: โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘ 40% + 20 layers: โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–‘โ–‘โ–‘โ–‘ 80% + 30 layers: โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ 100% (OOM) + + Solution: Gradient checkpointing +``` + ## โš ๏ธ Common Pitfalls 1. **Too Much Theory** @@ -445,16 +567,37 @@ class ReLU: Before considering a module complete: - [ ] All code in .py file (not notebook) -- [ ] Follows exact structure pattern +- [ ] Clear upfront explanation of WHAT and WHY +- [ ] ASCII diagrams for key concepts +- [ ] Natural markdown flow without redundant headers +- [ ] Every implementation preceded by explanation - [ ] Every implementation has immediate test -- [ ] Includes memory profiling -- [ ] Includes complexity analysis -- [ ] Shows production context +- [ ] Memory profiling with visual representation +- [ ] Complexity analysis with scaling diagrams +- [ ] Production context with system diagrams - [ ] NBGrader metadata correct -- [ ] ML systems thinking questions +- [ ] ML systems thinking questions with visuals - [ ] Summary is LAST section - [ ] Tests run when module executed +## ๐ŸŽฏ Visual Learning Philosophy + +**Great modules teach through seeing, not just reading.** + +Module 05's success comes from: +1. **Clear goal setting**: Explaining WHAT we're building upfront +2. **Visual-first teaching**: Concepts shown before implementation +3. **Natural flow**: Explanations that lead naturally into code +4. **Immediate feedback**: Tests that validate understanding +5. **Systems thinking**: Visuals that show real-world implications + +When developing modules: +- Start with the big picture explanation +- Use ASCII diagrams liberally (they're free and universal!) +- Let content determine visual needs (not every concept needs a diagram) +- Ensure markdown flows as natural narrative +- Show, don't just tell + ## ๐ŸŽฏ Remember > We're teaching ML systems engineering, not just ML algorithms. diff --git a/modules/01_tensor/tensor_dev.py b/modules/01_tensor/tensor_dev.py index 53aa4a81..964bb2a9 100644 --- a/modules/01_tensor/tensor_dev.py +++ b/modules/01_tensor/tensor_dev.py @@ -54,16 +54,38 @@ print("Ready to build tensors!") # %% [markdown] """ -## Understanding Tensors +## Understanding Tensors: From Numbers to Neural Networks -Tensors are N-dimensional arrays that store and manipulate numerical data. Think of them as generalizations of scalars, vectors, and matrices: +Tensors are N-dimensional arrays that store and manipulate numerical data. Think of them as containers for information that become increasingly powerful as dimensions increase. -- **Scalar (0D)**: A single number like `5.0` -- **Vector (1D)**: A list like `[1, 2, 3]` with shape `(3,)` -- **Matrix (2D)**: A 2D array like `[[1, 2], [3, 4]]` with shape `(2, 2)` -- **3D Tensor**: Like an RGB image with `(height, width, channels)` +### Tensor Dimension Hierarchy -Our Tensor class is a PURE data structure that wraps NumPy arrays with clean mathematical operations. This foundation focuses on data storage and computation - gradient tracking will be added in Module 05. +``` +Scalar (0D) โ”€โ”€โ–บ Vector (1D) โ”€โ”€โ–บ Matrix (2D) โ”€โ”€โ–บ 3D+ Tensor + 5.0 [1,2,3] [[1,2], [[[R,G,B]]] + [3,4]] image data + โ”‚ โ”‚ โ”‚ โ”‚ + โ–ผ โ–ผ โ–ผ โ–ผ + Single List Table Multi-dimensional + number of numbers of numbers data structure +``` + +### Memory Layout: NumPy Array + Tensor Wrapper + +Our Tensor class wraps NumPy's optimized arrays with clean ML operations: + +``` + TinyTorch Tensor NumPy Array +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” +โ”‚ Tensor Object โ”‚ โ”€โ”€โ”€โ–บ โ”‚ [1.0, 2.0, 3.0] โ”‚ +โ”‚ โ€ข shape: (3,) โ”‚ โ”‚ โ€ข dtype: float32 โ”‚ +โ”‚ โ€ข size: 3 โ”‚ โ”‚ โ€ข contiguous memory โ”‚ +โ”‚ โ€ข operations: +,*,@ โ”‚ โ”‚ โ€ข BLAS optimized โ”‚ +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ + Clean ML API Fast Computation +``` + +This foundation focuses on pure data operations - gradient tracking comes in Module 05. """ # %% nbgrader={"grade": false, "grade_id": "tensor-init", "solution": true} @@ -179,7 +201,24 @@ class Tensor: """ Addition operator: tensor + other - TODO: Implement + operator for tensors. + Element-wise addition with broadcasting support: + + ``` + Tensor + Tensor: Tensor + Scalar: + [1, 2, 3] [1, 2, 3] + [4, 5, 6] + 5 + โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + [5, 7, 9] [6, 7, 8] + ``` + + TODO: Implement + operator using NumPy's vectorized operations + + APPROACH: + 1. Check if other is Tensor or scalar + 2. Use NumPy broadcasting for element-wise addition + 3. Return new Tensor with result + + HINT: NumPy handles broadcasting automatically! """ ### BEGIN SOLUTION if isinstance(other, Tensor): @@ -230,9 +269,35 @@ class Tensor: def matmul(self, other: 'Tensor') -> 'Tensor': """ - Matrix multiplication using NumPy's optimized implementation. + Matrix multiplication: combine two matrices through dot product operations. - TODO: Implement matrix multiplication. + ### Matrix Multiplication Visualization + + ``` + A (2ร—3) B (3ร—2) C (2ร—2) + โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” + โ”‚ 1 2 3 โ”‚ โ”‚ 7 8 โ”‚ โ”‚ 1ร—7+2ร—9+3ร—1 โ”‚ + โ”‚ โ”‚ โ”‚ 9 1 โ”‚ = โ”‚ โ”‚ = C + โ”‚ 4 5 6 โ”‚ โ”‚ 1 2 โ”‚ โ”‚ 4ร—7+5ร—9+6ร—1 โ”‚ + โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ + โ”‚ โ”‚ โ”‚ + โ–ผ โ–ผ โ–ผ + Each row of A ร— Each col of B = Element of C + ``` + + ### Computational Cost + **FLOPs**: 2 ร— M ร— N ร— K operations for (Mร—K) @ (Kร—N) matrix + **Memory**: Result size Mร—N, inputs stay unchanged + + TODO: Implement matrix multiplication with shape validation + + APPROACH: + 1. Validate both tensors are 2D matrices + 2. Check inner dimensions match: A(m,k) @ B(k,n) โ†’ C(m,n) + 3. Use np.dot() for optimized BLAS computation + 4. Return new Tensor with result + + HINT: Let NumPy handle the heavy computation! """ ### BEGIN SOLUTION if len(self._data.shape) != 2 or len(other._data.shape) != 2: @@ -423,6 +488,29 @@ test_unit_tensor_properties() """ ### ๐Ÿงช Unit Test: Tensor Arithmetic This test validates all arithmetic operations (+, -, *, /) work correctly. + +**What we're testing**: Element-wise operations with broadcasting support +**Why it matters**: These operations form the foundation of neural network computations +**Expected**: All operations produce mathematically correct results with proper broadcasting + +### Broadcasting Visualization + +NumPy's broadcasting automatically handles different tensor shapes: + +``` +Same Shape: Broadcasting (vector + scalar): +[1, 2, 3] [1, 2, 3] [5] [1+5, 2+5, 3+5] +[4, 5, 6] + [4, 5, 6] + [5] = [4+5, 5+5, 6+5] +--------- --------- โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +[5, 7, 9] [6, 7, 8] [9,10,11] + +Matrix Broadcasting: Result: +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” +โ”‚ 1 2 3 โ”‚ โ”‚ 11 12 13 โ”‚ +โ”‚ โ”‚ +10 โ”‚ โ”‚ +โ”‚ 4 5 6 โ”‚ โ”€โ”€โ–ถ โ”‚ 14 15 16 โ”‚ +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ +``` """ # %% @@ -469,6 +557,26 @@ test_unit_tensor_arithmetic() """ ### ๐Ÿงช Unit Test: Matrix Multiplication This test validates matrix multiplication and the @ operator. + +**What we're testing**: Matrix multiplication with proper shape validation +**Why it matters**: Matrix multiplication is the core operation in neural networks +**Expected**: Correct results and informative errors for incompatible shapes + +### Matrix Multiplication Process + +For matrices A(2ร—2) @ B(2ร—2), each result element is computed as: + +``` +Computation Pattern: +C[0,0] = A[0,0]*B[0,0] + A[0,1]*B[1,0] (row 0 of A ร— col 0 of B) +C[0,1] = A[0,0]*B[0,1] + A[0,1]*B[1,1] (row 0 of A ร— col 1 of B) +C[1,0] = A[1,0]*B[0,0] + A[1,1]*B[1,0] (row 1 of A ร— col 0 of B) +C[1,1] = A[1,0]*B[0,1] + A[1,1]*B[1,1] (row 1 of A ร— col 1 of B) + +Example: +[[1, 2]] @ [[5, 6]] = [[1*5+2*7, 1*6+2*8]] = [[19, 22]] +[[3, 4]] [[7, 8]] [[3*5+4*7, 3*6+4*8]] [[43, 50]] +``` """ # %% @@ -506,6 +614,33 @@ test_unit_matrix_multiplication() """ ### ๐Ÿงช Unit Test: Tensor Operations This test validates reshape, transpose, and numpy conversion. + +**What we're testing**: Shape manipulation operations that reorganize data +**Why it matters**: Neural networks constantly reshape data between layers +**Expected**: Same data, different organization (no copying for most operations) + +### Shape Manipulation Visualization + +``` +Original tensor (2ร—3): +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” +โ”‚ 1 2 3 โ”‚ +โ”‚ โ”‚ +โ”‚ 4 5 6 โ”‚ +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ + +Reshape to (3ร—2): Transpose to (3ร—2): +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” +โ”‚ 1 2 โ”‚ โ”‚ 1 4 โ”‚ +โ”‚ 3 4 โ”‚ โ”‚ 2 5 โ”‚ +โ”‚ 5 6 โ”‚ โ”‚ 3 6 โ”‚ +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ + +Memory Impact: +- Reshape: Usually creates VIEW (no copy, just new indexing) +- Transpose: Creates VIEW (no copy, just swapped strides) +- Indexing: May create COPY (depends on pattern) +``` """ # %% @@ -579,55 +714,105 @@ test_module() # %% [markdown] """ -## Basic Performance Check +## Systems Analysis: Memory Layout and Performance -Let's do a simple check to see how our tensor operations perform: +Now that our Tensor is working, let's understand how it behaves at the systems level. This analysis shows you how tensor operations scale and where bottlenecks appear in real ML systems. + +### Memory Usage Patterns + +``` +Operation Type Memory Pattern When to Worry +โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +Element-wise (+,*,/) 2ร— input size Large tensor ops +Matrix multiply (@) Size(A) + Size(B) + Size(C) GPU memory limits +Reshape/transpose Same memory, new view Never (just metadata) +Indexing/slicing Copy vs view Depends on pattern +``` + +### Performance Characteristics + +Let's measure how our tensor operations scale with size: """ # %% -def check_tensor_performance(): - """Simple performance check for our tensor operations.""" - print("๐Ÿ“Š Basic Performance Check:") +def analyze_tensor_performance(): + """Analyze tensor operations performance and memory usage.""" + print("๐Ÿ“Š Systems Analysis: Tensor Performance\n") import time + import sys - # Test with small matrices first - a = Tensor.random(100, 100) - b = Tensor.random(100, 100) + # Test different matrix sizes to understand scaling + sizes = [50, 100, 200, 400] + results = [] - start = time.perf_counter() - result = a @ b - elapsed = time.perf_counter() - start + for size in sizes: + print(f"Testing {size}ร—{size} matrices...") + a = Tensor.random(size, size) + b = Tensor.random(size, size) - print(f"100x100 matrix multiplication: {elapsed*1000:.2f}ms") - print(f"Result shape: {result.shape}") - print("โœ… Tensor operations work efficiently!") + # Measure matrix multiplication time + start = time.perf_counter() + result = a @ b + elapsed = time.perf_counter() - start + + # Calculate memory usage (rough estimate) + memory_mb = (a.size + b.size + result.size) * 4 / (1024 * 1024) # 4 bytes per float32 + flops = 2 * size * size * size # 2*Nยณ for matrix multiplication + gflops = flops / (elapsed * 1e9) + + results.append((size, elapsed * 1000, memory_mb, gflops)) + print(f" Time: {elapsed*1000:.2f}ms, Memory: ~{memory_mb:.1f}MB, Performance: {gflops:.2f} GFLOPS") + + print("\n๐Ÿ” Performance Analysis:") + print("```") + print("Size Time(ms) Memory(MB) Performance(GFLOPS)") + print("-" * 50) + for size, time_ms, mem_mb, gflops in results: + print(f"{size:4d} {time_ms:7.2f} {mem_mb:9.1f} {gflops:15.2f}") + print("```") + + print("\n๐Ÿ’ก Key Insights:") + print("โ€ข Matrix multiplication is O(Nยณ) - doubling size = 8ร— more computation") + print("โ€ข Memory grows as O(Nยฒ) - usually not the bottleneck for single operations") + print("โ€ข NumPy uses optimized BLAS libraries (like OpenBLAS, Intel MKL)") + print("โ€ข Performance depends heavily on your CPU and available memory bandwidth") + + return results if __name__ == "__main__": print("๐Ÿš€ Running Tensor module...") test_module() - print("โœ… Module validation complete!") + print("\n๐Ÿ“Š Running systems analysis...") + analyze_tensor_performance() + print("\nโœ… Module validation complete!") # %% [markdown] """ ## ๐Ÿค” ML Systems Thinking: Interactive Questions -### Question 1: Tensor Size and Memory -**Context**: Your Tensor class stores data as NumPy arrays. When you created different sized tensors, you saw how memory usage changes. +### Question 1: Memory Scaling and Neural Network Implications +**Context**: Your performance analysis showed how tensor memory usage scales with size. A 1000ร—1000 tensor uses 100ร— more memory than a 100ร—100 tensor. -**Reflection Question**: If you create a 1000ร—1000 tensor versus a 100ร—100 tensor, how does memory usage change? Why does this matter for neural networks with millions of parameters? +**Systems Question**: Modern language models have weight matrices of size [4096, 11008] (Llama-2 7B). How much memory would this single layer consume in float32? Why do production systems use float16 or int8 quantization? -### Question 2: Operation Performance -**Context**: Your arithmetic operators (+, -, *, /) use NumPy's vectorized operations instead of Python loops. +*Calculate*: 4096 ร— 11008 ร— 4 bytes = ? GB per layer -**Reflection Question**: Why is `tensor1 + tensor2` much faster than looping through each element? How does this speed advantage become critical in neural network training? +### Question 2: Computational Complexity in Practice +**Context**: Your analysis revealed O(Nยณ) scaling for matrix multiplication. This means doubling the matrix size increases computation time by 8ร—. -### Question 3: Matrix Multiplication Scaling -**Context**: Your `matmul()` method uses NumPy's optimized `np.dot()` function for matrix multiplication. +**Performance Question**: If a 400ร—400 matrix multiplication takes 100ms on your machine, how long would a 1600ร—1600 multiplication take? How does this explain why training large neural networks requires GPUs with thousands of cores? -**Reflection Question**: Matrix multiplication has O(Nยณ) complexity. If you double the matrix size, how much longer does multiplication take? When does this become a bottleneck in neural networks? +*Think*: 1600 = 4 ร— 400, so computation = 4ยณ = 64ร— longer + +### Question 3: Memory Bandwidth vs Compute Power +**Context**: Your Tensor operations are limited by how fast data moves between RAM and CPU, not just raw computational power. + +**Architecture Question**: Why might element-wise operations (like tensor + tensor) be slower per operation than matrix multiplication, even though addition is simpler than dot products? How do modern ML accelerators (GPUs, TPUs) address this? + +*Hint*: Consider the ratio of data movement to computation work """ @@ -638,11 +823,12 @@ if __name__ == "__main__": Congratulations! You've built the fundamental data structure that powers neural networks. ### What You've Accomplished -โœ… **Core Tensor Class**: Complete implementation with creation, properties, and operations -โœ… **Essential Arithmetic**: Addition, subtraction, multiplication, division with NumPy integration -โœ… **Matrix Operations**: Matrix multiplication with @ operator and shape validation -โœ… **Shape Manipulation**: Reshape and transpose for data transformation -โœ… **Testing Framework**: Comprehensive unit tests validating all functionality +โœ… **Core Tensor Class**: Complete N-dimensional array implementation wrapping NumPy's optimized operations +โœ… **Broadcasting Arithmetic**: Element-wise operations (+, -, *, /) with automatic shape handling +โœ… **Matrix Operations**: O(Nยณ) matrix multiplication with @ operator and comprehensive shape validation +โœ… **Memory-Efficient Shape Manipulation**: Reshape and transpose operations using views when possible +โœ… **Systems Analysis**: Performance profiling revealing scaling characteristics and memory patterns +โœ… **Production-Ready Testing**: Unit tests with immediate validation and clear error messages ### Key Learning Outcomes - **Tensor Fundamentals**: N-dimensional arrays as the foundation of ML diff --git a/modules/02_activations/activations_dev.py b/modules/02_activations/activations_dev.py index e01b3fce..44e14188 100644 --- a/modules/02_activations/activations_dev.py +++ b/modules/02_activations/activations_dev.py @@ -66,30 +66,120 @@ print("Ready to build essential activation functions!") # %% [markdown] """ -## Why Activation Functions Matter +## The Intelligence Layer: How Nonlinearity Enables Learning -Activation functions inject nonlinearity into neural networks, enabling them to learn complex patterns beyond simple linear relationships. +Without activation functions, neural networks are just fancy linear algebra. No matter how many layers you stack, they can only learn straight lines. Activation functions add the "intelligence" that enables neural networks to learn curves, patterns, and complex relationships. -### ReLU: The Modern Standard +### The Linearity Problem -ReLU (Rectified Linear Unit) applies f(x) = max(0, x): -- Zeros out negative values -- Preserves positive values unchanged -- Computationally simple and efficient -- Enables training of very deep networks +``` +Linear Network (No Activations): +Input โ†’ Linear โ†’ Linear โ†’ Linear โ†’ Output + x โ†’ Ax โ†’ B(Ax) โ†’C(B(Ax)) = (CBA)x -### Softmax: Converting Scores to Probabilities +Result: Still just a linear function! +Cannot learn: curves, XOR, complex patterns +``` -Softmax transforms any vector into a probability distribution: -- All outputs sum to 1.0 -- All outputs are non-negative -- Larger inputs get larger probabilities -- Essential for classification tasks +### The Nonlinearity Solution + +``` +Nonlinear Network (With Activations): +Input โ†’ Linear โ†’ ReLU โ†’ Linear โ†’ ReLU โ†’ Output + x โ†’ Ax โ†’ max(0,Ax) โ†’ B(ยท) โ†’ max(0,B(ยท)) + +Result: Can approximate ANY function! +Can learn: curves, XOR, images, language +``` + +### ReLU: The Intelligence Function + +ReLU (Rectified Linear Unit) is the most important function in modern AI: + +``` +ReLU Function: f(x) = max(0, x) + + y + โ–ฒ + โ”‚ โ•ฑ + โ”‚ โ•ฑ (positive values unchanged) + โ”‚ โ•ฑ +โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ถ x + โ”‚ 0 (negative values โ†’ 0) + โ”‚ + +Key Properties: +โ€ข Computationally cheap: just comparison and zero +โ€ข Gradient friendly: derivative is 0 or 1 +โ€ข Solves vanishing gradients: keeps signal strong +โ€ข Enables deep networks: 100+ layers possible +``` + +### Softmax: The Probability Converter + +Softmax transforms any numbers into valid probabilities: + +``` +Raw Scores โ†’ Softmax โ†’ Probabilities +[2.0, 1.0, 0.1] โ†’ [0.66, 0.24, 0.10] + โ†‘ โ†‘ โ†‘ + Sum = 1.0 โœ“ + All โ‰ฅ 0 โœ“ + Larger in โ†’ Larger out โœ“ + +Formula: softmax(xแตข) = exp(xแตข) / ฮฃโฑผ exp(xโฑผ) + +Use Case: Classification ("What percentage dog vs cat?") +``` """ # %% [markdown] """ ## Part 1: ReLU - The Foundation of Modern Deep Learning + +ReLU transformed deep learning from a curiosity to the technology powering modern AI. Before ReLU, deep networks suffered from vanishing gradients and couldn't learn effectively beyond a few layers. ReLU's simple yet brilliant design solved this problem. + +### ReLU in Action: Element-wise Processing + +``` +Input Tensor: After ReLU: +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” +โ”‚ -2.1 0.5 3.2โ”‚ โ”‚ 0.0 0.5 3.2โ”‚ +โ”‚ 1.7 -0.8 2.1โ”‚ โ†’ โ”‚ 1.7 0.0 2.1โ”‚ +โ”‚ -1.0 4.0 -0.3โ”‚ โ”‚ 0.0 4.0 0.0โ”‚ +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ + โ†“ โ†“ +Negative โ†’ 0 Positive โ†’ unchanged +``` + +### The Dead Neuron Problem + +``` +ReLU can "kill" neurons permanently: + +Neuron with weights that produce only negative outputs: +Input: [1, 2, 3] โ†’ Linear: weights*input = -5.2 โ†’ ReLU: 0 +Input: [4, 1, 2] โ†’ Linear: weights*input = -2.8 โ†’ ReLU: 0 +Input: [0, 5, 1] โ†’ Linear: weights*input = -1.1 โ†’ ReLU: 0 + +Result: Neuron outputs 0 forever (no learning signal) +This is why proper weight initialization matters! +``` + +### Why ReLU Works Better Than Alternatives + +``` +Sigmoid: f(x) = 1/(1 + e^(-x)) +Problem: Gradients vanish for |x| > 3 + +Tanh: f(x) = tanh(x) +Problem: Gradients vanish for |x| > 2 + +ReLU: f(x) = max(0, x) +Solution: Gradient is exactly 1 for x > 0 (no vanishing!) +``` + +Now let's implement this game-changing function: """ # %% nbgrader={"grade": false, "grade_id": "relu-class", "solution": true} @@ -178,6 +268,30 @@ class ReLU: ### ๐Ÿงช Unit Test: ReLU Activation This test validates our ReLU implementation with various input scenarios + +**What we're testing**: ReLU's core behavior - zero negatives, preserve positives +**Why it matters**: ReLU must work perfectly for neural networks to learn +**Expected**: All negative values become 0, positive values unchanged + +### ReLU Test Cases Visualization + +``` +Test Case 1 - Basic Functionality: +Input: [-2, -1, 0, 1, 2] +Output: [ 0, 0, 0, 1, 2] + โ†‘ โ†‘ โ†‘ โ†‘ โ†‘ + โœ“ โœ“ โœ“ โœ“ โœ“ + (all negatives โ†’ 0, positives preserved) + +Test Case 2 - Matrix Processing: +Input: [[-1.5, 2.3], Output: [[0.0, 2.3], + [ 0.0, -3.7]] [0.0, 0.0]] + +Test Case 3 - Edge Cases: +โ€ข Very large positive: 1e6 โ†’ 1e6 (no overflow) +โ€ข Very small negative: -1e-6 โ†’ 0 (proper handling) +โ€ข Zero exactly: 0.0 โ†’ 0.0 (boundary condition) +``` """ def test_unit_relu_activation(): @@ -220,8 +334,55 @@ test_unit_relu_activation() """ ## Part 2: Softmax - Converting Scores to Probabilities -Softmax transforms any real-valued vector into a probability distribution. -Essential for classification and attention mechanisms. +Softmax is the bridge between raw neural network outputs and human-interpretable probabilities. It takes any vector of real numbers and transforms it into a valid probability distribution where all values sum to 1.0. + +### The Probability Transformation Process + +``` +Step 1: Raw Neural Network Outputs (can be any values) +Raw scores: [2.0, 1.0, 0.1] + +Step 2: Exponentiation (makes everything positive) +exp([2.0, 1.0, 0.1]) = [7.39, 2.72, 1.10] + +Step 3: Normalization (makes sum = 1.0) +[7.39, 2.72, 1.10] / (7.39+2.72+1.10) = [0.66, 0.24, 0.10] + โ†‘ โ†‘ โ†‘ โ†‘ + Sum: 11.21 Total: 1.00 โœ“ +``` + +### Softmax in Classification + +``` +Neural Network for Image Classification: + Raw Scores Softmax Interpretation +Input: Dog Image โ†’ [2.1, 0.3, -0.8] โ†’ [0.75, 0.18, 0.07] โ†’ 75% Dog + โ†‘ โ†‘ โ†‘ โ†‘ โ†‘ โ†‘ 18% Cat + Dog Cat Bird Dog Cat Bird 7% Bird + +Key Properties: +โ€ข Larger inputs get exponentially larger probabilities +โ€ข Never produces negative probabilities +โ€ข Always sums to exactly 1.0 +โ€ข Differentiable (can backpropagate gradients) +``` + +### The Numerical Stability Problem + +``` +Raw Softmax Formula: softmax(xแตข) = exp(xแตข) / ฮฃโฑผ exp(xโฑผ) + +Problem with large numbers: +Input: [1000, 999, 998] +exp([1000, 999, 998]) = [โˆž, โˆž, โˆž] โ† Overflow! + +Solution - Subtract max before exp: +x_stable = x - max(x) +Input: [1000, 999, 998] - 1000 = [0, -1, -2] +exp([0, -1, -2]) = [1.00, 0.37, 0.14] โ† Stable! +``` + +Now let's implement this essential function: """ # %% nbgrader={"grade": false, "grade_id": "softmax-class", "solution": true} @@ -320,6 +481,35 @@ class Softmax: ### ๐Ÿงช Unit Test: Softmax Activation This test validates our Softmax implementation for correctness and numerical stability + +**What we're testing**: Softmax probability distribution properties +**Why it matters**: Softmax must create valid probabilities for classification +**Expected**: All outputs โ‰ฅ 0, sum to 1.0, numerically stable with large inputs + +### Softmax Test Cases Visualization + +``` +Test Case 1 - Basic Probability Distribution: +Input: [1.0, 2.0, 3.0] +Output: [0.09, 0.24, 0.67] โ† Sum = 1.00 โœ“, All โ‰ฅ 0 โœ“ + โ†‘ โ†‘ โ†‘ + e^1/ฮฃ e^2/ฮฃ e^3/ฮฃ (largest input gets largest probability) + +Test Case 2 - Numerical Stability: +Input: [1000, 999, 998] โ† Would cause overflow without stability trick +Output: [0.67, 0.24, 0.09] โ† Still produces valid probabilities! + +Test Case 3 - Edge Cases: +โ€ข All equal inputs: [1, 1, 1] โ†’ [0.33, 0.33, 0.33] (uniform distribution) +โ€ข One dominant: [10, 0, 0] โ†’ [โ‰ˆ1.0, โ‰ˆ0.0, โ‰ˆ0.0] (winner-take-all) +โ€ข Negative inputs: [-1, -2, -3] โ†’ [0.67, 0.24, 0.09] (still works!) + +Test Case 4 - Batch Processing: +Input Matrix: [[1, 2, 3], Output Matrix: [[0.09, 0.24, 0.67], + [4, 5, 6]] โ†’ [0.09, 0.24, 0.67]] + โ†‘ โ†‘ + Each row processed independently Each row sums to 1.0 +``` """ def test_unit_softmax_activation(): diff --git a/modules/03_layers/layers_dev.py b/modules/03_layers/layers_dev.py index d0080f42..2205d852 100644 --- a/modules/03_layers/layers_dev.py +++ b/modules/03_layers/layers_dev.py @@ -532,10 +532,39 @@ class Linear(Module): # In[ ]: -# TEST Unit Test: Linear Layer +# %% [markdown] +""" +### ๐Ÿงช Unit Test: Linear Layer +This test validates our Linear layer implementation with matrix multiplication and parameter management. + +**What we're testing**: Linear layer transforms input dimensions correctly +**Why it matters**: Linear layers are the fundamental building blocks of neural networks +**Expected**: Correct output shapes, parameter handling, and batch processing + +### Linear Layer Computation Visualization + +``` +Forward Pass: y = x @ W + b + +Input Batch: Weight Matrix: Bias Vector: Output: +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” +โ”‚ [1, 2, 3] โ”‚ โ”‚ wโ‚โ‚ wโ‚โ‚‚ โ”‚ โ”‚ bโ‚ โ”‚ โ”‚ [yโ‚, yโ‚‚] โ”‚ +โ”‚ [4, 5, 6] โ”‚ @ โ”‚ wโ‚‚โ‚ wโ‚‚โ‚‚ โ”‚ + โ”‚ bโ‚‚ โ”‚ = โ”‚ [yโ‚ƒ, yโ‚„] โ”‚ +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ wโ‚ƒโ‚ wโ‚ƒโ‚‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ + Batch(2,3) โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ (2,) Batch(2,2) + Weights(3,2) + +Memory Layout: +โ€ข Input: [batch_size, input_features] +โ€ข Weights: [input_features, output_features] +โ€ข Bias: [output_features] +โ€ข Output: [batch_size, output_features] +``` +""" + def test_unit_linear(): """Test Linear layer implementation.""" - print("TEST Testing Linear Layer...") + print("๐Ÿ”ฌ Unit Test: Linear Layer...") # Test case 1: Basic functionality layer = Linear(input_size=3, output_size=2) @@ -583,9 +612,54 @@ test_unit_linear() # In[ ]: # TEST Unit Test: Parameter Management +# %% [markdown] +""" +### ๐Ÿงช Unit Test: Parameter Management +This test validates automatic parameter collection and module composition. + +**What we're testing**: Module system automatically collects parameters from nested layers +**Why it matters**: Enables automatic optimization and parameter management in complex networks +**Expected**: All parameters collected hierarchically, proper parameter counting + +### Parameter Management Hierarchy Visualization + +``` +Network Architecture: Parameter Collection: + +SimpleNetwork network.parameters() +โ”œโ”€โ”€ layer1: Linear(4โ†’3) โ”œโ”€โ”€ layer1.weights [4ร—3] = 12 params +โ”‚ โ”œโ”€โ”€ weights: (4,3) โ”œโ”€โ”€ layer1.bias [3] = 3 params +โ”‚ โ””โ”€โ”€ bias: (3,) โ”œโ”€โ”€ layer2.weights [3ร—2] = 6 params +โ””โ”€โ”€ layer2: Linear(3โ†’2) โ””โ”€โ”€ layer2.bias [2] = 2 params + โ”œโ”€โ”€ weights: (3,2) Total: 23 params + โ””โ”€โ”€ bias: (2,) + +Manual Tracking: vs Automatic Collection: +weights = [ params = model.parameters() + layer1.weights, # Automatically finds ALL + layer1.bias, # parameters in the hierarchy + layer2.weights, # No manual bookkeeping! + layer2.bias, +] +``` + +### Memory and Parameter Scaling + +``` +Layer Configuration: Parameters: Memory (float32): +Linear(100, 50) โ†’ 100ร—50 + 50 = 5,050 โ†’ ~20KB +Linear(256, 128) โ†’ 256ร—128 + 128 = 32,896 โ†’ ~131KB +Linear(512, 256) โ†’ 512ร—256 + 256 = 131,328 โ†’ ~525KB +Linear(1024, 512) โ†’ 1024ร—512 + 512 = 524,800 โ†’ ~2.1MB + +Pattern: O(input_size ร— output_size) scaling +Large layers dominate memory usage! +``` +""" + def test_unit_parameter_management(): """Test Linear layer parameter management and module composition.""" - print("TEST Testing Parameter Management...") + print("๐Ÿ”ฌ Unit Test: Parameter Management...") # Test case 1: Parameter registration layer = Linear(input_size=3, output_size=2) diff --git a/modules/04_losses/losses_dev.py b/modules/04_losses/losses_dev.py index e3d17cc6..8f286f20 100644 --- a/modules/04_losses/losses_dev.py +++ b/modules/04_losses/losses_dev.py @@ -440,14 +440,47 @@ def analyze_mse_properties(): # %% [markdown] """ -### TEST Unit Test: MSE Loss Computation +### ๐Ÿงช Unit Test: MSE Loss Computation This test validates `MeanSquaredError.__call__`, ensuring correct MSE computation with various input types and batch sizes. + +**What we're testing**: MSE correctly measures prediction quality with quadratic penalty +**Why it matters**: MSE must provide smooth gradients for stable regression training +**Expected**: Zero loss for perfect predictions, increasing quadratic penalty for larger errors + +### MSE Loss Test Cases Visualization + +``` +Test Case 1 - Perfect Predictions: +Predicted: [[1.0, 2.0], [3.0, 4.0]] +Actual: [[1.0, 2.0], [3.0, 4.0]] โ† Identical! +MSE Loss: 0.0 โ† Perfect prediction = no penalty + +Test Case 2 - Small Errors: +Predicted: [[1.1, 2.1], [3.1, 4.1]] โ† Each prediction off by 0.1 +Actual: [[1.0, 2.0], [3.0, 4.0]] +Errors: [0.1, 0.1, 0.1, 0.1] โ† Uniform small error +MSE Loss: (0.1ยฒ+0.1ยฒ+0.1ยฒ+0.1ยฒ)/4 = 0.01 + +Test Case 3 - Large Error Impact: +Error = 1.0 โ†’ Loss contribution = 1.0ยฒ = 1.0 +Error = 2.0 โ†’ Loss contribution = 2.0ยฒ = 4.0 โ† 2ร— error = 4ร— penalty! +Error = 3.0 โ†’ Loss contribution = 3.0ยฒ = 9.0 โ† 3ร— error = 9ร— penalty! + +Loss Landscape: + Loss + โ†‘ /\ + 9 | / \ Large errors heavily penalized + 4 | / \ + 1 | / \ Small errors lightly penalized + 0 |/__________\ Perfect prediction has zero loss + -3 -2 -1 0 1 2 3 โ†’ Error +``` """ # %% nbgrader={"grade": true, "grade_id": "test-mse-loss", "locked": true, "points": 3, "schema_version": 3, "solution": false, "task": false} def test_unit_mse_loss(): """Test MSE loss implementation.""" - print("TEST Testing Mean Squared Error Loss...") + print("๐Ÿ”ฌ Unit Test: Mean Squared Error Loss...") mse = MeanSquaredError() @@ -733,14 +766,58 @@ def analyze_crossentropy_stability(): # %% [markdown] """ -### TEST Unit Test: Cross-Entropy Loss Computation +### ๐Ÿงช Unit Test: Cross-Entropy Loss Computation This test validates `CrossEntropyLoss.__call__`, ensuring correct cross-entropy computation with numerically stable softmax. + +**What we're testing**: CrossEntropy provides correct classification loss with numerical stability +**Why it matters**: CrossEntropy must handle extreme logits safely and encourage correct predictions +**Expected**: High loss for wrong predictions, low loss for correct predictions, numerical stability + +### CrossEntropy Loss Test Cases Visualization + +``` +Classification Scenario: 3-class classification (Cat, Dog, Bird) + +Test Case 1 - Perfect Confidence: +Logits: [[10, 0, 0], [0, 10, 0]] โ† Very confident predictions +True: [0, 1] โ† Cat, Dog +Softmax: [[โ‰ˆ1, 0, 0], [0, โ‰ˆ1, 0]] โ† Near-perfect probabilities +CE Loss: โ‰ˆ0.0 โ† Minimal penalty for confidence + +Test Case 2 - Wrong but Confident: +Logits: [[0, 0, 10]] โ† Confident Bird prediction +True: [0] โ† Actually Cat! +Softmax: [[0, 0, โ‰ˆ1]] โ† Wrong class gets โ‰ˆ100% +CE Loss: โ‰ˆ10.0 โ† Heavy penalty for wrong confidence + +Test Case 3 - Uncertain (Good): +Logits: [[0, 0, 0]] โ† Completely uncertain +True: [0] โ† Cat +Softmax: [[0.33, 0.33, 0.33]] โ† Equal probabilities +CE Loss: 1.099 โ† Moderate penalty for uncertainty + +Loss Behavior Pattern: + Loss โ†‘ + 10 | โ— (wrong + confident = disaster) + | + 5 | + | + 1 | โ— (uncertain = acceptable) + | + 0 | โ— (correct + confident = ideal) + +________________โ†’ Confidence + Wrong Uncertain Correct + +Numerical Stability: +Input: [1000, 0, -1000] โ†’ Subtract max: [0, -1000, -2000] +Result: Prevents overflow while preserving relative differences +``` """ # %% nbgrader={"grade": true, "grade_id": "test-crossentropy-loss", "locked": true, "points": 4, "schema_version": 3, "solution": false, "task": false} def test_unit_crossentropy_loss(): """Test CrossEntropy loss implementation.""" - print("TEST Testing Cross-Entropy Loss...") + print("๐Ÿ”ฌ Unit Test: Cross-Entropy Loss...") ce = CrossEntropyLoss()