ymlin105 commited on
Commit
a7c1baa
Β·
1 Parent(s): 9e3834b

refactor: Reorganize project assets

Browse files
README.md CHANGED
@@ -10,75 +10,126 @@ app_file: app.py
10
  pinned: false
11
  ---
12
 
13
- # Why Does SVD Turn a "3" into an "8"? Linear vs. Non-linear Manifolds on MNIST
14
 
15
- [![Hugging Face Spaces](https://img.shields.io/badge/%F0%9F%A4%97%20Hugging%20Face-Spaces-blue)](https://huggingface.co/spaces/ymlin105/Coconut-MNIST) [![Full Report](https://img.shields.io/badge/πŸ“–_Read-Full_Report-blue)](./docs/REPORT.md)
16
 
17
- This project investigates **why SVD systematically misclassifies digit 3 as 8**, revealing fundamental differences between linear (variance-based) and non-linear (topology-based) representations. Through mechanistic analysis and empirical validation, we show that SVD and CNN optimize different objectives, leading to **complementary strengths and failure modes**.
 
 
 
 
 
 
 
 
 
 
18
 
19
  <p align="center">
20
- <img src="./docs/research_results/fig_04_explainability.png" width="600" alt="Mechanistic Analysis: SVD Blind Spot">
 
21
  </p>
22
 
23
- **Key Finding**: SVD's low-pass filtering property provides complementary benefits under realistic noise conditions (Οƒ ∈ [0, 0.3]), but becomes destructive on texture-rich data. Methods succeed in different regimes based on their optimization objectives, not universally.
24
-
25
- ## The Solution: Hybrid SVD-CNN
26
-
27
- I combine SVD's strength as a data-adapted low-pass filter with the CNN's robust feature extraction into a single pipeline.
28
-
29
- ```mermaid
30
- flowchart TD
31
- subgraph S1 [I. Noisy Manifold]
32
- direction LR
33
- X["Input $X + \eta$"]
34
- end
35
-
36
- subgraph S2 [II. Adaptive Projection]
37
- direction LR
38
- node_SVD["SVD: $X = U \Sigma V^T$"]
39
- node_Trunc["$k$-Rank Truncation"]
40
- node_Recon["$\hat{X} = \sum \sigma_i u_i v_i^T$"]
41
- node_SVD --> node_Trunc --> node_Recon
42
- end
43
-
44
- subgraph S3 [III. CNN Features]
45
- direction LR
46
- node_Conv["Conv Layers"] --> node_Pool["Pooling / ReLU"] --> node_Flat["Global Flatten"]
47
- end
48
-
49
- subgraph S4 [IV. Latent Mapping]
50
- direction LR
51
- node_Soft["Logits / Softmax"] --> node_Pred["Class Prediction"]
52
- end
53
-
54
- S1 --> S2
55
- S2 --> S3
56
- S3 --> S4
57
-
58
- style S2 fill:#f8f9ff,stroke:#0056b3,stroke-width:2px
59
- style S3 fill:#f8fff9,stroke:#28a745,stroke-width:2px
60
- style S1 fill:#fff,stroke:#333
61
- style S4 fill:#fff,stroke:#333
62
- ```
63
 
64
- ### Key Takeaways
65
- For full analysis and detailed metrics, see the [Technical Report](./docs/REPORT.md).
66
 
67
- 1. **The Variance Trap**: SVD's optimization for global pixel variance treats the topological gap distinguishing 3 from 8 as low-variance noise, discarding it during dimensionality reduction. This causes systematic manifold collapse (98.74% k-NN raw pixels β†’ 96.98% in SVD subspace).
 
 
 
68
 
69
- 2. **Mechanistic Proof**: Grad-CAM visualization shows CNN focuses on topological boundaries (the gap), while SVD reconstructs phantom features (a closed loop). UMAP analysis confirms manifold overlap in SVD subspace but separation in raw pixel space.
 
 
 
70
 
71
- 3. **Complementary Strength**: Under realistic Gaussian noise (Οƒ ∈ [0, 0.3]), Hybrid SVDβ†’CNN maintains 90.02% accuracy at Οƒ=0.3 while CNN drops to 95.67%, validating SVD as an adaptive low-pass filter that enables CNN to learn from cleaner input.
 
 
 
72
 
73
- 4. **Data-Dependent Boundary**: On texture-rich Fashion-MNIST, the hybrid approach fails (CNN 89.79% β†’ Hybrid 71.78%) because SVD destroys high-frequency features that distinguish clothing items, proving complementarity requires silhouette-based structure.
74
 
75
- ---
 
 
 
76
 
77
- ## Experience it Yourself
 
78
 
79
- ### Online Demo
80
- Try the live dashboard to inject noise, adjust SVD rank, and compare model predictions in real-time:
81
- **[Launch Streamlit App](https://huggingface.co/spaces/ymlin105/Coconut-MNIST)**.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
82
 
83
  ### Local Installation
84
  ```bash
@@ -97,10 +148,14 @@ streamlit run app.py
97
  ```
98
  β”œβ”€β”€ src/ Core modules (CNN, SVD layer) + Experimental Utils
99
  β”œβ”€β”€ experiments/ Sequential scripts (01 Diagnosis, 02 Proof, 03 Boundaries)
100
- β”œβ”€β”€ docs/ Full report (REPORT.md) + figures
101
  β”œβ”€β”€ models/ Pretrained checkpoints
102
  β”œβ”€β”€ run_all_experiments.sh One-click reproduction script
103
  └── app.py Streamlit dashboard
104
  ```
105
 
106
- ---
 
 
 
 
 
10
  pinned: false
11
  ---
12
 
13
+ # Why Does SVD Turn a "3" into an "8"? The Conflict Between Shape and Topology
14
 
15
+ [![Hugging Face Spaces](https://img.shields.io/badge/%F0%9F%A4%97%20Hugging%20Face-Spaces-blue)](https://huggingface.co/spaces/ymlin105/Coconut-MNIST)
16
 
17
+ This project investigates a phenomenon in linear dimensionality reduction: **Why does SVD systematically reconstruct a digit "3" as an "8"?**
18
+
19
+ Through spectral analysis and noise testing, we uncover a fundamental conflict between **Linear Reconstruction (SVD)** and **Non-linear Classification (CNN)**. We demonstrate that SVD acts as a "low-pass filter" that sacrifices topological details (gaps) for global shape smoothness, leading to three distinct regimes of performance:
20
+
21
+ 1. **Clean Data**: CNN wins by capturing topology; SVD fails by "closing the gap."
22
+ 2. **Simple Noise (MNIST)**: CNN remains robust; SVD preprocessing becomes an "interference" that degrades performance.
23
+ 3. **Complex Noise (Fashion)**: CNN collapses due to texture noise; SVD survives by discarding high-frequency chaos.
24
+
25
+ ## 1. The Mechanism: Energy vs. Topology
26
+
27
+ While implementing SVD-based classification, we observed a systematic asymmetry. Comparing the confusion matrices reveals the stark difference between linear and non-linear representations.
28
 
29
  <p align="center">
30
+ <img src="assets/fig_01_svd_confusion.png" alt="SVD Confusion Matrix" width="45%" />
31
+ <img src="assets/fig_04_cnn_confusion.png" alt="CNN Confusion Matrix" width="45%" />
32
  </p>
33
 
34
+ - **Left (SVD, Acc=0.88)**: Shows significant confusion between 3 and 8. Notably, 3 is misclassified as 8 (2.5%) and 8 as 3 (3.4%). The diagonal values (e.g., 80.6% for digit 5) indicate lower overall confidence.
35
+ - **Right (CNN, Acc=0.99)**: Near-perfect diagonal dominance (mostly >98%). The topology of "3" vs "8" is clearly distinguishable.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
 
37
+ ### Root Cause: The "Morphological Closing"
38
+ Why does the linear model fail? SVD optimizes for global pixel variance (energy). The defining feature distinguishing "3" from "8"β€”the topological gapβ€”is a high-frequency detail with low energy.
39
 
40
+ **Visualizing the "Low-Pass Filter" (Eigen-digits)**
41
+ <p align="center">
42
+ <img src="assets/fig_02_eigen_digits.png" alt="Global SVD Eigen-digits" width="600" />
43
+ </p>
44
 
45
+ The eigen-digits visualize what SVD actually learns.
46
+ - **Comp 1**: Looks like a generic "0" or a smooth ellipse. This is the strongest basis vectorβ€”the **global silhouette**.
47
+ - **Comp 2+**: Subsequent components add details, but they quickly become noisy and overlapping.
48
+ - **Implication**: The primary components capture closed loops (low-frequency energy). The "gap" in digit 3 requires specific, high-frequency components that are pushed into the tail of the spectrum and truncated.
49
 
50
+ **Spectral Evidence**
51
+ <p align="center">
52
+ <img src="assets/fig_01_spectrum.png" alt="Singular Value Spectrum" width="600" />
53
+ </p>
54
 
55
+ The spectrum confirms this: the "gap" corresponds to the small singular values in the **long tail** (after the steep decay). By selecting rank $k=20$, we preserve the "loopy" energy (left side of the curve) but discard the "gap" energy (right side).
56
 
57
+ ### Mechanistic Proof (Grad-CAM)
58
+ <p align="center">
59
+ <img src="assets/fig_04_explainability.png" alt="Grad-CAM vs SVD Reconstruction" width="600" />
60
+ </p>
61
 
62
+ - **CNN Attention**: Focuses sharply on the topological boundary (the gap). It learns "where the digit breaks".
63
+ - **SVD Reconstruction**: Produces a smooth, closed loop (an "8"). It learns "what the digit contours look like".
64
 
65
+ ## 2. Dynamic Validation: Interpolation Analysis
66
+
67
+ To quantify this "morphological closing," we analyzed the transition from Digit 3 ($\alpha=0$) to Digit 8 ($\alpha=1$).
68
+
69
+ <p align="center">
70
+ <img src="assets/fig_03_interpolation.png" alt="Interpolation Analysis" width="600" />
71
+ </p>
72
+
73
+ - **SVD Reconstruction Error (Blue Squares)**: Drops significantly at $\alpha=0.5$. This means the "ambiguous" shape is *closer* to the SVD subspace centroid than either endpoint. SVD effectively "collapses" the ambiguous shape into a generic, smooth representation.
74
+ - **Manifold Distance (Brown Triangles)**: Shows a sharp **spike** at $\alpha=0.5$. The raw data space treats the ambiguous shape as an outlier (far from both 3 and 8), but SVD treats it as an "ideal" reconstruction target.
75
+ - **CNN Probability**: Gradually increases, correctly identifying the transition.
76
+
77
+ **Conclusion**: SVD subspace creates a "shortcut" for ambiguous shapes, pulling them toward a closed-loop average.
78
+
79
+ ## 3. The Manifold: Boundary Erosion
80
+
81
+ Does this flaw destroy the entire representation?
82
+
83
+ <p align="center">
84
+ <img src="assets/fig_05_manifold_collapse.png" alt="Manifold Comparison" width="600" />
85
+ </p>
86
+
87
+ - **Left (SVD Projection)**: Digit 3 (blue) and 8 (red) form a mixed, circular cloud with no clear boundary. The linear projection creates a high-density overlap.
88
+ - **Right (UMAP Non-linear)**: Distinct "islands" of blue and red, clearly separated by topology.
89
+
90
+ ## 4. The Interference Effect: When Denoising Removes Signal
91
+
92
+ A common intuition is that SVD's low-pass filtering should help CNNs handle noise. Our experiments on MNIST reject this.
93
+
94
+ <p align="center">
95
+ <img src="assets/fig_06_robustness_mnist_gaussian.png" alt="Robustness: MNIST" width="600" />
96
+ </p>
97
+
98
+ **Observations**:
99
+ - **CNN (Red)**: Maintains the highest accuracy (~0.985 to 0.958). It is robust to Gaussian noise on simple digits.
100
+ - **Hybrid (Green)**: Underperforms the raw CNN consistently.
101
+ - **SVD (Blue)**: The weakest performer, degrading rapidly as noise increases.
102
+
103
+ **Conclusion**: On silhouette-based datasets like MNIST, CNN's non-linear robustness is superior. SVD preprocessing acts as an **information bottleneck**, stripping away edge signals that CNN needs to correct noise.
104
+
105
+ ## 5. The Reversal: The Fragility of Texture Recognition
106
+
107
+ On **Fashion-MNIST**, the story flips. Classification here depends on **texture** (high-frequency).
108
+
109
+ <p align="center">
110
+ <img src="assets/fig_08_robustness_fashion.png" alt="Robustness: Fashion-MNIST" width="600" />
111
+ </p>
112
+
113
+ **Observations**:
114
+ - **CNN (Red)**: Crashes dramatically from 0.90 to 0.32. It cannot distinguish "texture signal" from "texture noise".
115
+ - **SVD (Blue)**: Remains incredibly stable (0.80 to 0.68). Its "blindness" to high-frequency texture becomes a **shield** against noise. It survives by relying solely on low-frequency silhouette.
116
+ - **The Crossover**: SVD overtakes CNN at $\sigma \approx 0.12$.
117
+ - **Hybrid (Green)**: Starts low (0.72) and crashes. It suffers the worst of both worlds: SVD destroys discriminative texture features, and the CNN fails to classify the "smoothed" clothes.
118
+
119
+ **Conclusion**: SVD's denoising comes at the cost of **semantic destruction** on texture-rich data. It survives the noise but kills the signal.
120
+
121
+ ## Key Insight
122
+
123
+ There is no universal winner. Methods succeed based on the alignment between their optimization objective and the data's frequency profile:
124
+
125
+ | Method | Optimization Target | Strength | Weakness |
126
+ | :------ | :---------------------- | :------------------------ | :------------------------------------ |
127
+ | **SVD** | Global Variance ($L_2$) | Robust to high-freq noise | Blinds to topology/texture |
128
+ | **CNN** | Discriminative Features | Sensitive to topology | Vulnerable to texture-noise confusion |
129
+
130
+ **Takeaway**: Before choosing a denoising pipeline, ask: *Is my signal high-frequency or low-frequency?* If your signal is in the edges (Topology), linear filtering will hurt more than help.
131
+
132
+ ## Quick Start
133
 
134
  ### Local Installation
135
  ```bash
 
148
  ```
149
  β”œβ”€β”€ src/ Core modules (CNN, SVD layer) + Experimental Utils
150
  β”œβ”€β”€ experiments/ Sequential scripts (01 Diagnosis, 02 Proof, 03 Boundaries)
151
+ β”œβ”€β”€ assets/ Figures and images for README
152
  β”œβ”€β”€ models/ Pretrained checkpoints
153
  β”œβ”€β”€ run_all_experiments.sh One-click reproduction script
154
  └── app.py Streamlit dashboard
155
  ```
156
 
157
+ ### Run All Experiments
158
+ ```bash
159
+ # Run the complete experimental pipeline
160
+ ./run_all_experiments.sh
161
+ ```
app.py CHANGED
@@ -3,7 +3,7 @@ os.environ["STREAMLIT_SERVER_FILEWATCHERTYPE"] = "none"
3
  import streamlit as st
4
  import numpy as np
5
  import plotly.express as px
6
- import torch
7
  import umap
8
  import pandas as pd
9
  from sklearn.decomposition import TruncatedSVD
@@ -114,13 +114,12 @@ st.markdown("""
114
  }
115
 
116
  /* Slider and input styling */
117
- /* Slider styling - Minimalist and Clean */
118
  div[data-baseweb="slider"] > div > div > div {
119
- background-color: #ECEFF4 !important; /* Track color */
120
  }
121
 
122
  div[data-baseweb="slider"] > div > div > div > div {
123
- background-color: #5E81AC !important; /* Progress color */
124
  }
125
 
126
  /* Radio button styling */
@@ -227,8 +226,8 @@ with st.sidebar:
227
 
228
  st.info("""
229
  **Key Finding:**\n
230
- SVD optimizes global variance β†’ fails at local topological features (the 3/8 gap).\n
231
- CNN captures discriminative boundaries β†’ sensitive to noise.
232
  """)
233
 
234
  # Global Noise Control for Hybrid Analysis
@@ -238,7 +237,7 @@ with st.sidebar:
238
  help="Hybrid pipeline: SVD preprocessing β†’ CNN classification"
239
  )
240
  if noise_mode:
241
- st.success("SVD Denoising Active", icon="βœ…")
242
 
243
  st.markdown("---")
244
  st.markdown("### 🎚️ Model Calibration")
@@ -253,7 +252,7 @@ with st.sidebar:
253
  st.caption("""
254
  **Tabs:**
255
  - Topology: Decision boundary snap analysis
256
- - Robustness: Noise filtering comparison
257
  - Manifold: Linear vs non-linear projections
258
  - Lab: Interactive testing
259
  """)
@@ -269,7 +268,7 @@ st.title("πŸ₯₯ Coconut MNIST: Why SVD Misclassifies 3 as 8")
269
  st.markdown("""
270
  ### Mechanistic Analysis: Linear vs. Non-Linear Representations
271
 
272
- Explore how SVD's global variance optimization and CNN's local feature extraction lead to **complementary strengths and failure modes**.
273
 
274
  **Start your exploration below β†’**
275
  """)
@@ -279,7 +278,7 @@ st.markdown("---")
279
  # --- Tabs ---
280
  tab1, tab2, tab3, tab4 = st.tabs([
281
  "The Topology Gap",
282
- "Robustness Limits",
283
  "Manifold Explorer",
284
  "Live Lab"
285
  ])
@@ -290,8 +289,8 @@ with tab1:
290
  st.markdown("### πŸ” Topological Decision Boundaries")
291
  st.markdown("""
292
  Smoothly interpolate between two digits and observe:
293
- - **CNN's behavior**: Sharp phase transition at manifold boundary (topological snap)
294
- - **SVD's behavior**: Gradual reconstruction error increase (tries to "bridge" manifolds)
295
 
296
  This reveals the fundamental difference: CNN sees discrete topology, SVD sees continuous variance.
297
  """)
@@ -357,18 +356,20 @@ with tab1:
357
  st.caption(f"The vertical 'snap' in this curve highlights the non-linear decision boundary. Even as the pixels fade linearly, the CNN's internal representation jumps once a topological threshold is crossed.")
358
 
359
 
360
- # --- Tab 2: Robustness (The SVD Advantage) ---
361
  with tab2:
362
- st.markdown("### πŸ›‘οΈ SVD as Adaptive Denoiser")
363
  st.markdown("""
364
- **Key insight**: While SVD fails on clean MNIST (destroys the 3-8 gap), it becomes powerful under noise.
365
 
366
- **Mechanism**: By keeping only top-20 variance directions, SVD acts as a low-pass filter that:
367
- - βœ“ Preserves class-relevant structure
368
- - βœ“ Suppresses high-frequency Gaussian noise
369
- - βœ— Cannot recover from information already lost to noise
370
 
371
- **Trade-off**: SVD + CNN maintains accuracy under moderate noise better than CNN alone.
 
 
372
  """)
373
 
374
  col1, col2 = st.columns([1, 2])
@@ -408,11 +409,18 @@ with tab2:
408
  acc_hybrid = interp("Hybrid", sigma)
409
 
410
  m1, m2, m3 = st.columns(3)
411
- m1.metric("SVD+LR Accuracy (10-class)", f"{acc_svd:.1%}")
412
- m2.metric("CNN Accuracy (10-class)", f"{acc_cnn:.1%}")
413
- m3.metric("Hybrid Robustness (10-class)", f"{acc_hybrid:.1%}")
414
-
415
- st.caption("Metrics come from precomputed evaluation on the MNIST test set (test-time Gaussian noise).")
 
 
 
 
 
 
 
416
  else:
417
  st.info("Robustness metrics not found. Run `python experiments/09_hybrid_robustness.py` to generate evaluated curves.")
418
 
@@ -424,8 +432,8 @@ with tab3:
424
  **Question**: How different are linear (SVD) vs non-linear (UMAP) projections?
425
 
426
  **Observations**:
427
- - **SVD (Blue regions)**: Classes overlap β†’ global variance loses local structure
428
- - **UMAP (Colorful clusters)**: Classes separate β†’ preserves topological neighborhoods
429
 
430
  This visualizes why CNN (non-linear) works while SVD fails on the 3-8 pair.
431
  """)
@@ -492,7 +500,7 @@ with tab3:
492
  )
493
  fig_svd.update_traces(marker=dict(size=4, opacity=0.6))
494
  st.plotly_chart(fig_svd, use_container_width=True, key="svd_chart")
495
- st.caption("Classes overlap significantly - 3 and 8 intertwine.")
496
 
497
  with col2:
498
  st.markdown("#### UMAP Projection")
@@ -512,7 +520,7 @@ with tab3:
512
  )
513
  fig_umap.update_traces(marker=dict(size=4, opacity=0.6))
514
  st.plotly_chart(fig_umap, use_container_width=True, key="umap_chart")
515
- st.caption("Classes form distinct clusters - non-linear separation works.")
516
 
517
  elif projection_method == "SVD (Linear)":
518
  fig = px.scatter(
@@ -550,13 +558,13 @@ with tab4:
550
  st.markdown("### πŸ§ͺ Interactive Testing")
551
  st.markdown("""
552
  **Experiment 1: Dataset Browser**
553
- - Pick a digit and add Gaussian noise
554
- - See how SVD denoises before CNN classification
555
- - Compare predictions with/without SVD preprocessing
556
 
557
  **Experiment 2: Draw Your Own**
558
- - Sketch a digit and watch both methods analyze it in real-time
559
- - Observe the difference between CNN's sharp boundary detection and SVD's smooth reconstruction
560
  """)
561
 
562
  # Two modes: sample browser or upload
@@ -679,4 +687,4 @@ with tab4:
679
 
680
 
681
  st.markdown("---")
682
- st.caption("Coconut MNIST | Linear vs Non-Linear Analysis | [View Report](./docs/REPORT.md)")
 
3
  import streamlit as st
4
  import numpy as np
5
  import plotly.express as px
6
+ import torch,,,,,
7
  import umap
8
  import pandas as pd
9
  from sklearn.decomposition import TruncatedSVD
 
114
  }
115
 
116
  /* Slider and input styling */
 
117
  div[data-baseweb="slider"] > div > div > div {
118
+ background-color: #ECEFF4 !important;
119
  }
120
 
121
  div[data-baseweb="slider"] > div > div > div > div {
122
+ background-color: #5E81AC !important;
123
  }
124
 
125
  /* Radio button styling */
 
226
 
227
  st.info("""
228
  **Key Finding:**\n
229
+ SVD optimizes global variance β†’ fails at topology (the 3/8 gap).\n
230
+ CNN captures boundaries β†’ vulnerable to texture-noise confusion.
231
  """)
232
 
233
  # Global Noise Control for Hybrid Analysis
 
237
  help="Hybrid pipeline: SVD preprocessing β†’ CNN classification"
238
  )
239
  if noise_mode:
240
+ st.warning("SVD Preprocessing Active\n(May act as an 'Information Bottleneck')", icon="⚠️")
241
 
242
  st.markdown("---")
243
  st.markdown("### 🎚️ Model Calibration")
 
252
  st.caption("""
253
  **Tabs:**
254
  - Topology: Decision boundary snap analysis
255
+ - Interference: Why denoising fails on MNIST
256
  - Manifold: Linear vs non-linear projections
257
  - Lab: Interactive testing
258
  """)
 
268
  st.markdown("""
269
  ### Mechanistic Analysis: Linear vs. Non-Linear Representations
270
 
271
+ Explore the conflict between SVD's global variance optimization and CNN's local feature extraction.
272
 
273
  **Start your exploration below β†’**
274
  """)
 
278
  # --- Tabs ---
279
  tab1, tab2, tab3, tab4 = st.tabs([
280
  "The Topology Gap",
281
+ "The Interference Effect",
282
  "Manifold Explorer",
283
  "Live Lab"
284
  ])
 
289
  st.markdown("### πŸ” Topological Decision Boundaries")
290
  st.markdown("""
291
  Smoothly interpolate between two digits and observe:
292
+ - **CNN's behavior**: Sharp phase transition at manifold boundary (topological snap).
293
+ - **SVD's behavior**: "Morphological Closing" β€” tries to smooth the transition by closing gaps.
294
 
295
  This reveals the fundamental difference: CNN sees discrete topology, SVD sees continuous variance.
296
  """)
 
356
  st.caption(f"The vertical 'snap' in this curve highlights the non-linear decision boundary. Even as the pixels fade linearly, the CNN's internal representation jumps once a topological threshold is crossed.")
357
 
358
 
359
+ # --- Tab 2: Robustness (The Interference Effect) ---
360
  with tab2:
361
+ st.markdown("### ⚠️ The Interference Effect: When Denoising Removes Signal")
362
  st.markdown("""
363
+ **Myth busted**: SVD preprocessing does NOT always help CNNs.
364
 
365
+ **Observations on MNIST**:
366
+ - **CNN**: Remains surprisingly robust to moderate noise due to non-linear feature extraction.
367
+ - **SVD**: Acts as a "low-pass filter". While it removes noise, it also **strips away high-frequency edges** critical for classification.
368
+ - **Result**: The Hybrid model often **underperforms** the raw CNN. SVD creates an "Information Bottleneck".
369
 
370
+ **The Fashion-MNIST Reversal** (See Report):
371
+ - On texture-rich data, CNN collapses under high noise (texture-noise confusion).
372
+ - SVD survives by discarding the chaotic high-frequency band entirely.
373
  """)
374
 
375
  col1, col2 = st.columns([1, 2])
 
409
  acc_hybrid = interp("Hybrid", sigma)
410
 
411
  m1, m2, m3 = st.columns(3)
412
+ m1.metric("SVD+LR Accuracy", f"{acc_svd:.1%}", delta="Baseline Linear Model")
413
+ m2.metric("CNN Accuracy", f"{acc_cnn:.1%}", delta="Superior Robustness")
414
+
415
+ # Logic to highlight the interference effect
416
+ delta_hybrid = acc_hybrid - acc_cnn
417
+ delta_text = f"{delta_hybrid:.1%} vs Raw CNN"
418
+ if delta_hybrid < 0:
419
+ m3.metric("Hybrid Accuracy", f"{acc_hybrid:.1%}", delta=delta_text, delta_color="inverse")
420
+ else:
421
+ m3.metric("Hybrid Accuracy", f"{acc_hybrid:.1%}", delta=delta_text, delta_color="normal")
422
+
423
+ st.caption("Metrics come from precomputed evaluation on the MNIST test set. Note that Hybrid often trails CNN, proving that linear denoising can remove useful signal.")
424
  else:
425
  st.info("Robustness metrics not found. Run `python experiments/09_hybrid_robustness.py` to generate evaluated curves.")
426
 
 
432
  **Question**: How different are linear (SVD) vs non-linear (UMAP) projections?
433
 
434
  **Observations**:
435
+ - **SVD (Blue regions)**: Classes overlap β†’ global variance loses local structure (Boundary Erosion).
436
+ - **UMAP (Colorful clusters)**: Classes separate β†’ preserves topological neighborhoods.
437
 
438
  This visualizes why CNN (non-linear) works while SVD fails on the 3-8 pair.
439
  """)
 
500
  )
501
  fig_svd.update_traces(marker=dict(size=4, opacity=0.6))
502
  st.plotly_chart(fig_svd, use_container_width=True, key="svd_chart")
503
+ st.caption("Boundary Erosion: 3 and 8 overlap significantly.")
504
 
505
  with col2:
506
  st.markdown("#### UMAP Projection")
 
520
  )
521
  fig_umap.update_traces(marker=dict(size=4, opacity=0.6))
522
  st.plotly_chart(fig_umap, use_container_width=True, key="umap_chart")
523
+ st.caption("Distinct Clusters: Non-linear separation preserves topology.")
524
 
525
  elif projection_method == "SVD (Linear)":
526
  fig = px.scatter(
 
558
  st.markdown("### πŸ§ͺ Interactive Testing")
559
  st.markdown("""
560
  **Experiment 1: Dataset Browser**
561
+ - Pick a digit and add Gaussian noise.
562
+ - Compare predictions with/without SVD preprocessing.
563
+ - **Observe**: Does SVD help or hurt the classification?
564
 
565
  **Experiment 2: Draw Your Own**
566
+ - Sketch a digit and watch both methods analyze it in real-time.
567
+ - Observe the difference between CNN's sharp boundary detection and SVD's smooth reconstruction.
568
  """)
569
 
570
  # Two modes: sample browser or upload
 
687
 
688
 
689
  st.markdown("---")
690
+ st.caption("Coconut MNIST | Linear vs Non-Linear Analysis")
{docs/research_results β†’ assets}/fig_01_spectrum.png RENAMED
File without changes
{docs/research_results β†’ assets}/fig_01_svd_confusion.png RENAMED
File without changes
{docs/research_results β†’ assets}/fig_02_eigen_digits.png RENAMED
File without changes
assets/fig_03_interpolation.png ADDED

Git LFS Details

  • SHA256: 2ee094e077e54aaeacf68da428fa0d94b82db7c1da4d33a2329c704f1746b837
  • Pointer size: 131 Bytes
  • Size of remote file: 274 kB
assets/fig_04_cnn_confusion.png ADDED

Git LFS Details

  • SHA256: 0939e42b5a67c027fd044e6bedfe1f8c70abd823b6c3a1e8ec5b51423ecd0ef6
  • Pointer size: 131 Bytes
  • Size of remote file: 241 kB
{docs/research_results β†’ assets}/fig_04_explainability.png RENAMED
File without changes
{docs/research_results β†’ assets}/fig_05_manifold_collapse.png RENAMED
File without changes
{docs/research_results β†’ assets}/fig_06_robustness_mnist_gaussian.png RENAMED
File without changes
{docs/research_results β†’ assets}/fig_08_robustness_fashion.png RENAMED
File without changes
{docs/research_results β†’ assets}/robustness_mnist_noise.json RENAMED
File without changes
docs/REPORT.md DELETED
@@ -1,204 +0,0 @@
1
- # SVD vs CNN on MNIST: A Study of Complementary Representations
2
-
3
- ## 1. Initial Observation
4
-
5
- While implementing SVD-based digit classification on MNIST, we observed systematic confusion between digits 3 and 8. The confusion matrix reveals:
6
- - Digit 8 misclassified as 3: **3.4%**
7
- - Digit 3 misclassified as 8: **2.5%**
8
-
9
- This asymmetric but correlated failure pattern warranted investigation into the fundamental mechanisms driving the two methods' behaviors.
10
-
11
- <p align="center">
12
- <img src="research_results/fig_01_svd_confusion.png" alt="SVD Confusion Matrix" width="500" />
13
- <br>
14
- <em><strong>Figure 1:</strong> SVD Confusion Matrix (Accuracy: 88.13%). Errors concentrate on visually similar pairs: 3 ↔ 8, 5 ↔ 3, 4 ↔ 9.</em>
15
- </p>
16
-
17
- ---
18
-
19
- ## 2. Diagnosis: The Variance Trap
20
-
21
- ### 2.1 Overall Performance (Clean Data)
22
-
23
- <div align="center">
24
-
25
- | Method | Accuracy |
26
- |--------|----------|
27
- | SVD | 88.13% |
28
- | CNN | 98.55% |
29
-
30
- </div>
31
-
32
- SVD's 10% accuracy gap is not uniformly distributed. Confusion concentrates on visually ambiguous pairs (as shown in Figure 1):
33
- - 3 ↔ 8 (2.5% + 3.4%)
34
- - 5 ↔ 3 (6.4% + 0.9%)
35
- - 4 ↔ 9 (1.7% + 5.8%)
36
-
37
- Other digit pairs show error rates < 1.5%.
38
-
39
- ### 2.2 Root Cause: SVD Optimizes for Global Variance
40
-
41
- SVD solves:
42
- $$X = U \Sigma V^T$$
43
-
44
- where $\Sigma$ contains singular values sorted in decreasing order. Truncation to rank $k=20$ retains only the $k$ dimensions with highest variance.
45
-
46
- <p align="center">
47
- <img src="research_results/fig_01_spectrum.png" alt="Singular Value Spectrum" width="400" />
48
- <br>
49
- <img src="research_results/fig_02_eigen_digits.png" alt="Eigen-digits" width="400" />
50
- <br>
51
- <em><strong>Figure 2 & 3:</strong> Left: Singular value decay showing rapid drop after kβ‰ˆ5. Right: First 10 eigen-digits (principal components). SVD reconstructs shared circular silhouettes, smoothing over discriminative gaps.</em>
52
- </p>
53
-
54
- **The problem**: The topological gap distinguishing 3 from 8 has low pixel variance (few pixels differ). SVD treats it as noise and discards it during dimensionality reduction. The reconstructed 3 appears closer to an 8-like silhouette.
55
-
56
- ---
57
-
58
- ## 3. Mechanistic Proof
59
-
60
- ### 3.1 Grad-CAM Visualization
61
-
62
- <p align="center">
63
- <img src="research_results/fig_04_explainability.png" alt="Grad-CAM vs SVD Reconstruction" width="700" />
64
- <br>
65
- <em><strong>Figure 4:</strong> Left: CNN Grad-CAM attention (red = high focus). Center: Original digit 3. Right: SVD reconstruction. CNN focuses on the gap; SVD hallucinates a closed loop to minimize reconstruction error.</em>
66
- </p>
67
-
68
- **CNN** attention heatmap: Focuses exclusively on the topological boundary (gap in digit 3).
69
-
70
- **SVD** reconstruction: Smooth, closed loop at the 3-8 ambiguity zone, indicating the linear model reconstructs a phantom feature to minimize overall error.
71
-
72
- ### 3.2 UMAP Manifold Analysis
73
-
74
- <p align="center">
75
- <img src="research_results/fig_05_manifold_collapse.png" alt="Manifold Comparison: Raw vs SVD Subspace" width="600" />
76
- <br>
77
- <em><strong>Figure 5:</strong> Left: UMAP of raw pixel space (3 and 8 clearly separated). Right: UMAP of SVD 20-component subspace (clusters overlap significantly).</em>
78
- </p>
79
-
80
- - **Raw pixel space**: Digit 3 and 8 clusters are clearly separated (98.74% k-NN accuracy).
81
- - **SVD 20-component subspace**: Clusters overlap significantly (96.98% k-NN accuracy, 1.76% loss).
82
- - **Interpretation**: SVD projection collapses the manifold boundaries that discriminate these digits.
83
-
84
- ### 3.3 Interpolation Boundary
85
-
86
- <p align="center">
87
- <img src="research_results/fig_03_interpolation.png" alt="Decision Boundary Interpolation" width="700" />
88
- <br>
89
- <em><strong>Figure 6:</strong> Interpolating from digit 3 to 8. Top: CNN class probability (sharp transition at manifold boundary). Bottom: SVD reconstruction error (peaks at midpoint where linear model struggles to bridge two manifolds).</em>
90
- </p>
91
-
92
- Interpolating smoothly from digit 3 to digit 8:
93
- - **CNN confidence**: Sharp phase transition at the midpoint (topological boundary detected).
94
- - **SVD reconstruction error**: Peaks at midpoint (linear model struggles to bridge two distinct manifolds).
95
-
96
- ---
97
-
98
- ## 4. Complementarity: SVD as Denoising Filter
99
-
100
- While SVD fails as a classifier on clean data, its low-pass filtering property reveals complementary benefits under realistic noise conditions.
101
-
102
- ### 4.1 Robustness Under Gaussian Noise (Οƒ ∈ [0, 0.3])
103
-
104
- Test regime: Add Gaussian noise $\mathcal{N}(0, \sigma^2)$ to test images (image range normalized to [0, 1]).
105
-
106
- <p align="center">
107
- <img src="research_results/fig_06_robustness_mnist_gaussian.png" alt="Robustness: Realistic Gaussian Noise on MNIST" width="500" />
108
- <br>
109
- <em><strong>Figure 7:</strong> Accuracy under Gaussian noise (Οƒ ∈ [0, 0.3]). Hybrid (SVDβ†’CNN) maintains stable performance, outperforming CNN at Οƒ=0.2 and beyond.</em>
110
- </p>
111
-
112
- <div align="center">
113
-
114
- | Οƒ | CNN | SVD | Hybrid |
115
- |---|-----|-----|--------|
116
- | 0.0 | 98.55% | 88.13% | 91.98% |
117
- | 0.1 | 98.48% | 87.18% | 91.84% |
118
- | 0.2 | 97.94% | 86.37% | 91.24% |
119
- | 0.3 | 95.67% | 80.64% | 90.02% |
120
-
121
- </div>
122
-
123
- **Key finding**:
124
- - **Clean data**: CNN >> Hybrid >> SVD
125
- - **At Οƒ=0.3**: CNN drops to 95.67%, but Hybrid remains at 90.02%
126
- - **Hybrid advantage**: Maintains relative stability by filtering noise before feature extraction
127
-
128
- ### 4.2 Mechanism: Selective Feature Preservation
129
-
130
- SVD truncation to rank $k=20$ acts as an adaptive low-pass filter:
131
- $$\text{Noisy Image} \xrightarrow{\text{SVD Project}} \text{Denoised} \xrightarrow{\text{CNN}} \text{Class}$$
132
-
133
- By discarding low-variance dimensions, SVD naturally suppresses high-frequency Gaussian noise while preserving the primary class-discriminative structure. CNN then works with cleaner input.
134
-
135
- ### 4.3 Implication
136
-
137
- SVD's complementary benefit is **narrowly applicable**: it helps when:
138
- 1. Noise is Gaussian (random, not aligned with data)
139
- 2. Noise level is moderate (Οƒ ≀ 0.3, images still recognizable)
140
- 3. Data is simple/silhouette-based (MNIST works; texture-based data may not)
141
-
142
- ---
143
-
144
- ## 5. Boundary: Failure on Texture-Rich Data
145
-
146
- On **Fashion-MNIST**, SVD's low-pass filtering becomes destructive.
147
-
148
- <p align="center">
149
- <img src="research_results/fig_08_robustness_fashion.png" alt="Fashion-MNIST: SVD Filter Destroys Textures" width="500" />
150
- <br>
151
- <em><strong>Figure 8:</strong> Performance on Fashion-MNIST under noise (Οƒ ∈ [0, 0.3]). CNN performance degrades rapidly, but SVD (which preserves structure in MNIST) performs even worse than Hybrid here, revealing data-dependent behavior.</em>
152
- </p>
153
-
154
- **Clean data (Οƒ=0)**:
155
-
156
- <div align="center">
157
-
158
- | Method | Accuracy |
159
- |--------|----------|
160
- | CNN | 89.79% |
161
- | SVD | 80.30% |
162
- | Hybrid | 71.78% |
163
-
164
- </div>
165
-
166
- **Why Hybrid fails worst (71.78%)**:
167
- 1. SVD destroys high-frequency textures (buttons, zippers, stitching) that distinguish clothing items
168
- 2. CNN receives a "simplified" image that has already lost class-relevant information
169
- 3. CNN cannot recover from this information loss, performing worse than SVD alone
170
-
171
- **Implication**: SVD's denoising benefit is restricted to **silhouette-based datasets** where low-frequency structure dominates. On texture-rich data, the hybrid approach becomes a liability.
172
-
173
- ---
174
-
175
- ## 6. Summary: Method Applicability by Data Regime
176
-
177
- <div align="center">
178
-
179
- | Scenario | Best Choice | Why |
180
- |----------|------------|-----|
181
- | **Clean MNIST** | CNN (98.55%) | No noise; SVD's simplification is pure loss |
182
- | **Noisy MNIST (Οƒ=0.2-0.3)** | Hybrid (91.24%) | SVD filters Gaussian noise; CNN learns from cleaner input |
183
- | **Clean Fashion-MNIST** | CNN (89.79%) | Textures require non-linear feature extraction |
184
- | **Texture-rich + Noise** | CNN alone | SVD destroys high-freq features before noise filtering helps |
185
-
186
- </div>
187
-
188
- **No universal winner**: Methods succeed in different regimes based on their optimization objectives:
189
-
190
- - **SVD** optimizes: Global variance preservation β†’ low-pass filter β†’ stable on silhouette-based data
191
- - **CNN** optimizes: Discriminative feature learning β†’ sensitive to noise, but powerful on complex data
192
-
193
- ---
194
-
195
- ## 7. Conclusion
196
-
197
- This study demonstrates that methodological "limitations" are not flaws but **manifestations of optimization objectives**. SVD and CNN optimize different criteriaβ€”global reconstruction vs. local discriminationβ€”leading to complementary failure modes and strengths.
198
-
199
- **Key insight**: Understanding a method's optimization target enables **predicting its applicability** rather than treating it as a black box. The choice of method should depend on:
200
- 1. **Data characteristics** (silhouette vs. texture)
201
- 2. **Noise conditions** (Gaussian vs. aligned; moderate vs. extreme)
202
- 3. **Accuracy requirements** (marginal vs. acceptable loss)
203
-
204
- Rather than seeking universal solutions, practitioners should match methods to specific problem regimes.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
docs/research_results/fig_03_interpolation.png CHANGED

Git LFS Details

  • SHA256: 97fcd4fb3aeec934d4638d2e3422136eb692e6c5c553dc6c5dea01b820f65ba6
  • Pointer size: 131 Bytes
  • Size of remote file: 274 kB

Git LFS Details

  • SHA256: 2ee094e077e54aaeacf68da428fa0d94b82db7c1da4d33a2329c704f1746b837
  • Pointer size: 131 Bytes
  • Size of remote file: 274 kB
run_all_experiments.sh CHANGED
@@ -3,6 +3,15 @@
3
 
4
  set -e
5
 
 
 
 
 
 
 
 
 
 
6
  echo "=== 1. Phenomenon Diagnosis (Global SVD & CNN Baseline) ==="
7
  python -m experiments.01_phenomenon_diagnosis
8
 
@@ -17,5 +26,5 @@ python -m experiments.03_operational_boundaries --dataset fashion
17
 
18
  echo "=========================================================="
19
  echo "All experiments completed successfully."
20
- echo "Results and figures saved in docs/research_results/"
21
  echo "=========================================================="
 
3
 
4
  set -e
5
 
6
+ # Ensure python finds the modules in the current directory
7
+ export PYTHONPATH=$PYTHONPATH:.
8
+
9
+ echo "=== 0. Training Models (MNIST & Fashion-MNIST) ==="
10
+ echo "Training MNIST models..."
11
+ python -m src.train_models
12
+ echo "Training Fashion-MNIST models..."
13
+ python -m src.train_fashion
14
+
15
  echo "=== 1. Phenomenon Diagnosis (Global SVD & CNN Baseline) ==="
16
  python -m experiments.01_phenomenon_diagnosis
17
 
 
26
 
27
  echo "=========================================================="
28
  echo "All experiments completed successfully."
29
+ echo "Results and figures saved in assets/"
30
  echo "=========================================================="
src/config.py CHANGED
@@ -4,7 +4,7 @@ import os
4
  BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
5
  DATA_DIR = os.path.join(BASE_DIR, "data")
6
  MODELS_DIR = os.path.join(BASE_DIR, "models")
7
- RESULTS_DIR = os.path.join(BASE_DIR, "docs", "research_results")
8
 
9
  for d in [DATA_DIR, MODELS_DIR, RESULTS_DIR]:
10
  os.makedirs(d, exist_ok=True)
 
4
  BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
5
  DATA_DIR = os.path.join(BASE_DIR, "data")
6
  MODELS_DIR = os.path.join(BASE_DIR, "models")
7
+ RESULTS_DIR = os.path.join(BASE_DIR, "assets")
8
 
9
  for d in [DATA_DIR, MODELS_DIR, RESULTS_DIR]:
10
  os.makedirs(d, exist_ok=True)
src/viz.py CHANGED
@@ -144,8 +144,9 @@ def plot_manifold_comparison(X_svd, X_umap, y, acc_svd, acc_raw, filename):
144
  ax.set_xticks([])
145
  ax.set_yticks([])
146
 
147
- plt.suptitle("Manifold Collapse: Linear SVD Overlap vs. Non-linear Topological Separation",
148
- fontsize=14, fontweight='bold', y=1.02)
 
149
  save_fig(filename)
150
 
151
  def plot_learning_curves(history, title, filename):
 
144
  ax.set_xticks([])
145
  ax.set_yticks([])
146
 
147
+ plt.suptitle("Manifold Collapse: SVD vs UMAP Projections",
148
+ fontsize=14, fontweight='bold', y=1.05)
149
+ plt.tight_layout(rect=[0, 0, 1, 0.95]) # Leave space for title
150
  save_fig(filename)
151
 
152
  def plot_learning_curves(history, title, filename):