Spaces:
Sleeping
Sleeping
refactor: Reorganize project assets
Browse files- README.md +112 -57
- app.py +43 -35
- {docs/research_results β assets}/fig_01_spectrum.png +2 -2
- {docs/research_results β assets}/fig_01_svd_confusion.png +2 -2
- {docs/research_results β assets}/fig_02_eigen_digits.png +2 -2
- assets/fig_03_interpolation.png +3 -0
- assets/fig_04_cnn_confusion.png +3 -0
- {docs/research_results β assets}/fig_04_explainability.png +0 -0
- {docs/research_results β assets}/fig_05_manifold_collapse.png +2 -2
- {docs/research_results β assets}/fig_06_robustness_mnist_gaussian.png +2 -2
- {docs/research_results β assets}/fig_08_robustness_fashion.png +2 -2
- {docs/research_results β assets}/robustness_mnist_noise.json +0 -0
- docs/REPORT.md +0 -204
- docs/research_results/fig_03_interpolation.png +2 -2
- run_all_experiments.sh +10 -1
- src/config.py +1 -1
- src/viz.py +3 -2
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"?
|
| 14 |
|
| 15 |
-
[](https://huggingface.co/spaces/ymlin105/Coconut-MNIST)
|
| 16 |
|
| 17 |
-
This project investigates
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 18 |
|
| 19 |
<p align="center">
|
| 20 |
-
<img src="
|
|
|
|
| 21 |
</p>
|
| 22 |
|
| 23 |
-
**
|
| 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 |
-
###
|
| 65 |
-
|
| 66 |
|
| 67 |
-
|
|
|
|
|
|
|
|
|
|
| 68 |
|
| 69 |
-
|
|
|
|
|
|
|
|
|
|
| 70 |
|
| 71 |
-
|
|
|
|
|
|
|
|
|
|
| 72 |
|
| 73 |
-
|
| 74 |
|
| 75 |
-
-
|
|
|
|
|
|
|
|
|
|
| 76 |
|
| 77 |
-
|
|
|
|
| 78 |
|
| 79 |
-
##
|
| 80 |
-
|
| 81 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
βββ
|
| 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 |
+
[](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;
|
| 120 |
}
|
| 121 |
|
| 122 |
div[data-baseweb="slider"] > div > div > div > div {
|
| 123 |
-
background-color: #5E81AC !important;
|
| 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
|
| 231 |
-
CNN captures
|
| 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.
|
| 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 |
-
-
|
| 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
|
| 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 |
-
"
|
| 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**:
|
| 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
|
| 361 |
with tab2:
|
| 362 |
-
st.markdown("###
|
| 363 |
st.markdown("""
|
| 364 |
-
**
|
| 365 |
|
| 366 |
-
**
|
| 367 |
-
-
|
| 368 |
-
-
|
| 369 |
-
-
|
| 370 |
|
| 371 |
-
**
|
|
|
|
|
|
|
| 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
|
| 412 |
-
m2.metric("CNN Accuracy
|
| 413 |
-
|
| 414 |
-
|
| 415 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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("
|
| 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("
|
| 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 |
-
-
|
| 555 |
-
-
|
| 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
|
|
|
|
| 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
|
assets/fig_04_cnn_confusion.png
ADDED
|
Git LFS Details
|
{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
|
|
Git LFS Details
|
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
|
| 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, "
|
| 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:
|
| 148 |
-
fontsize=14, fontweight='bold', y=1.
|
|
|
|
| 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):
|