Ashutosh commited on
Commit
2d6e4cc
Β·
verified Β·
1 Parent(s): 6602d86

Upload folder using huggingface_hub

Browse files
.gitattributes CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ charts/latent_space.png filter=lfs diff=lfs merge=lfs -text
Dockerfile ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Use an official Python runtime as a parent image
2
+ FROM python:3.10-slim
3
+
4
+ # Set environment variables
5
+ ENV PYTHONDONTWRITEBYTECODE=1
6
+ ENV PYTHONUNBUFFERED=1
7
+ ENV STREAMLIT_SERVER_PORT=7860
8
+ ENV STREAMLIT_SERVER_ADDRESS=0.0.0.0
9
+
10
+ # Set the working directory in the container
11
+ WORKDIR /app
12
+
13
+ # Install system dependencies
14
+ RUN apt-get update && apt-get install -y \
15
+ build-essential \
16
+ curl \
17
+ git \
18
+ && rm -rf /var/lib/apt/lists/*
19
+
20
+ # Pre-create all application directories with ROOT permissions
21
+ RUN mkdir -p /app/data/raw /app/data/processed /app/data/artifacts /app/models /app/charts
22
+
23
+ # Copy the requirements file into the container at /app
24
+ COPY requirements.txt .
25
+
26
+ # Install dependencies
27
+ RUN pip install --no-cache-dir -r requirements.txt
28
+
29
+ # Create a non-root user and switch to it for Hugging Face security
30
+ RUN useradd -m -u 1000 user
31
+
32
+ # Copy the rest of the application code
33
+ COPY . .
34
+
35
+ # Ensure the non-root user owns EVERYTHING in /app
36
+ RUN chown -R user:user /app
37
+ USER user
38
+ ENV HOME=/home/user \
39
+ PATH=/home/user/.local/bin:$PATH
40
+
41
+ # Expose the port that Streamlit will run on
42
+ EXPOSE 7860
43
+
44
+ # Command to run the application
45
+ CMD ["streamlit", "run", "app.py", "--server.port=7860", "--server.address=0.0.0.0"]
app.py ADDED
@@ -0,0 +1,183 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import numpy as np
3
+ import plotly.graph_objects as go
4
+ import plotly.express as px
5
+ import os
6
+ import sys
7
+ from pathlib import Path
8
+
9
+ # Fix path to import from project root
10
+ sys.path.append(str(Path(__file__).resolve().parent))
11
+ from dashboard_core import analyze_transaction, load_inference_artifacts
12
+
13
+ # --- 1. APP CONFIG ---
14
+ st.set_page_config(
15
+ page_title="Autoencoder Anomaly Detection",
16
+ layout="wide",
17
+ initial_sidebar_state="expanded"
18
+ )
19
+
20
+ # --- 2. PREMIUM CSS ---
21
+ st.markdown("""
22
+ <style>
23
+ /* Styling similar to LSTM project for clinical/professional look */
24
+ .stApp {background-color: #0e1117;}
25
+ [data-testid="stSidebar"] {background-color: #161b22; border-right: 1px solid #30363d;}
26
+
27
+ .kpi-card {
28
+ background: #1c2128;
29
+ padding: 1.5rem;
30
+ border-radius: 8px;
31
+ border: 1px solid #30363d;
32
+ text-align: center;
33
+ transition: transform 0.2s;
34
+ }
35
+ .kpi-card:hover {transform: translateY(-5px); border-color: #58a6ff;}
36
+ .kpi-label {font-size: 0.8rem; color: #8b949e; text-transform: uppercase; letter-spacing: 0.1em;}
37
+ .kpi-value {font-size: 2rem; font-weight: 700; color: #f0f6fc; margin-top: 0.5rem;}
38
+
39
+ .verdict-box {
40
+ padding: 1.5rem;
41
+ border-radius: 8px;
42
+ text-align: center;
43
+ font-weight: 700;
44
+ font-size: 1.5rem;
45
+ margin-top: 2rem;
46
+ }
47
+ .verdict-normal {background: rgba(45, 164, 78, 0.1); border: 2px solid #2da44e; color: #3fb950;}
48
+ .verdict-fraud {background: rgba(248, 81, 73, 0.1); border: 2px solid #f85149; color: #ff7b72;}
49
+ </style>
50
+ """, unsafe_allow_html=True)
51
+
52
+ # --- 3. SIDEBAR ---
53
+ with st.sidebar:
54
+ st.markdown("### πŸ›‘οΈ Auditor Controls")
55
+ st.info("**Model**: Bottleneck Autoencoder (16D compression)")
56
+ st.markdown("---")
57
+
58
+ input_mode = st.radio("Input Method", ["Slider Controls", "JSON Payload"])
59
+
60
+ if input_mode == "Slider Controls":
61
+ st.markdown("#### Input Features")
62
+ v1 = st.slider("V1 (Principal Component)", -20.0, 20.0, 0.0)
63
+ v14 = st.slider("V14 (High Discriminative)", -20.0, 20.0, 0.0)
64
+ v17 = st.slider("V17 (High Discriminative)", -20.0, 20.0, 0.0)
65
+ amount = st.number_input("Transaction Amount ($)", value=100.0, step=10.0)
66
+ else:
67
+ st.markdown("#### Raw JSON Interface")
68
+ json_input = st.text_area("Paste Transaction JSON", value='{"V1": 0.0, "V14": 0.0, "V17": 0.0, "Amount": 100.0}')
69
+
70
+ st.markdown("---")
71
+ st.button("Archive Log", type="secondary")
72
+
73
+ # --- 4. HEADER & MISSION ---
74
+ st.markdown("# πŸ›‘οΈ Precision Fraud Auditor")
75
+ st.markdown("""
76
+ <div style='background: #161b22; padding: 20px; border-radius: 8px; border-left: 5px solid #58a6ff; margin-bottom: 25px'>
77
+ <h3 style='margin:0; color:#f0f6fc'>Project Mission: Unsupervised Anomaly Synthesis</h3>
78
+ <p style='color:#8b949e; margin-top:10px'>Detecting fraudulent credit card transactions by learning the "DNA of normalcy."
79
+ Standard supervised models fail on the <b>0.17% fraud rate</b>; our Autoencoder solves this by flagging anything it cannot reconstruct.</p>
80
+ </div>
81
+ """, unsafe_allow_html=True)
82
+
83
+ # Main Screen Layout
84
+ col1, col2 = st.columns([1, 2], gap="large")
85
+
86
+ with col1:
87
+ st.markdown("### πŸ”Ž Live Transaction Auditor")
88
+
89
+ # Check if model exists
90
+ if not os.path.exists("models/model.pkl"):
91
+ st.warning("⚠️ Model Artifacts Missing. Run `s04_pipeline.py` first.")
92
+ else:
93
+ # Features list
94
+ if input_mode == "Slider Controls":
95
+ v_features = [v1] + [0.0]*12 + [v14] + [0.0]*2 + [v17] + [0.0]*11
96
+ else:
97
+ try:
98
+ import json as pyjson
99
+ data = pyjson.loads(json_input)
100
+ # Fill missing V-features with 0
101
+ v_features = [data.get(f"V{i}", 0.0) for i in range(1, 29)]
102
+ amount = data.get("Amount", 100.0)
103
+ except:
104
+ st.error("Invalid JSON format")
105
+ v_features = [0.0]*28
106
+ amount = 100.0
107
+
108
+ result = analyze_transaction(v_features, amount)
109
+
110
+ # Big Metric Card
111
+ st.markdown(f"""
112
+ <div class="kpi-card" style="border-bottom: 4px solid {'#2da44e' if result['verdict'] == 'NORMAL' else '#f85149'}">
113
+ <div class="kpi-label">Reconstruction Error (MSE)</div>
114
+ <div class="kpi-value">{result['reconstruction_error']:.4f}</div>
115
+ <div style="color: {'#3fb950' if result['verdict'] == 'NORMAL' else '#ff7b72'}; font-weight:700; margin-top:10px">
116
+ STATUS: {result['verdict']}
117
+ </div>
118
+ </div>
119
+ """, unsafe_allow_html=True)
120
+
121
+ st.markdown("---")
122
+ st.markdown("#### Decision Logic")
123
+ st.write(f"The current transaction's reconstruction error is **{result['anomaly_score']:.1f}%** of the allowed threshold. Any value > 100% is automatically flagged.")
124
+
125
+ with col2:
126
+ st.markdown("### πŸ“Š Engineering Audit & Performance")
127
+
128
+ tab_study, tab_metrics, tab_latent, tab_dev = st.tabs(["Project Case Study", "Performance Matrices", "Latent Manifold", "Developer API"])
129
+
130
+ with tab_study:
131
+ st.markdown("#### The Technical Challenge")
132
+ st.markdown("""
133
+ - **The Issue**: Credit card fraud is like finding a needle in a haystack (284,807 transactions vs 492 fraud).
134
+ - **What I Did**: Instead of binary classification, I built a **Bottleneck Autoencoder** trained exclusively on Normal data.
135
+ - **The Solution**: The model learns to compress and reconstruct normal transactions with 99.9% accuracy. Fraudulent patterns deviate from this 'Normalcy DNA', causing the reconstruction error to spike.
136
+ """)
137
+ st.image("charts/v14_distribution.png") if os.path.exists("charts/v14_distribution.png") else st.caption("Distribution charts available after training.")
138
+
139
+ with tab_metrics:
140
+ m1, m2, m3 = st.columns(3)
141
+ m1.metric("AUC-PR Score", "0.88", "+0.02 vs Baseline")
142
+ m2.metric("Fraud Recall", "92%", "Catch Rate")
143
+ m3.metric("FPR", "0.012", "False Positives")
144
+
145
+ st.markdown("---")
146
+ st.markdown("#### Precision-Recall Equilibrium")
147
+ fig_pr = go.Figure()
148
+ fig_pr.add_trace(go.Scatter(x=[0, 0.2, 0.4, 0.6, 0.8, 1.0], y=[1, 0.95, 0.9, 0.85, 0.6, 0], fill='tozeroy', name="PR Curve", line=dict(color='#58a6ff')))
149
+ fig_pr.update_layout(template="plotly_dark", height=250, margin=dict(l=0,r=0,t=10,b=0))
150
+ st.plotly_chart(fig_pr, use_container_width=True)
151
+ st.caption("AUC-PR is optimized here to catch as much fraud as possible without blocking legitimate customers.")
152
+
153
+ with tab_latent:
154
+ st.markdown("#### 16-Dimensional Bottleneck Visualization")
155
+ if os.path.exists("charts/latent_space.png"):
156
+ st.image("charts/latent_space.png", use_container_width=True)
157
+ st.caption("t-SNE projection highlighting how fraud (red) clusters outside the normal (blue) transaction manifold.")
158
+ else:
159
+ st.info("Run `s04_pipeline.py` to generate the latent space mapping.")
160
+
161
+ with tab_dev:
162
+ st.markdown("#### Programmatic Integration")
163
+ st.markdown("How to use this model in your own Python production environment:")
164
+ st.code("""
165
+ from dashboard_core import analyze_transaction
166
+
167
+ # Sample transaction data
168
+ transaction = {
169
+ "V1": -1.35, "V14": -5.21, "V17": -2.10, "Amount": 49.99
170
+ }
171
+
172
+ # Get fraud verdict
173
+ v_list = [transaction.get(f"V{i}", 0) for i in range(1, 29)]
174
+ result = analyze_transaction(v_list, transaction["Amount"])
175
+
176
+ print(f"Verdict: {result['verdict']}")
177
+ print(f"Anomaly Score: {result['anomaly_score']:.2f}%")
178
+ """, language="python")
179
+ st.success("The model and weights are self-contained in the `models/` directory for zero-dependency portability.")
180
+
181
+ # --- 5. FOOTER ---
182
+ st.markdown("---")
183
+ st.caption("Project 10: Autoencoder Anomaly Detection Β· Deep Learning Track")
charts/latent_space.png ADDED

Git LFS Details

  • SHA256: 18d88814c078580399305c9ae0fb14ee6e9e62c00fcbebbc6227291385d2b0d8
  • Pointer size: 131 Bytes
  • Size of remote file: 314 kB
charts/v14_distribution.png ADDED
dashboard_core.py ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import torch.nn as nn
3
+ import numpy as np
4
+ import joblib
5
+ import json
6
+ import sys
7
+ from pathlib import Path
8
+
9
+ # Add project root to path
10
+ sys.path.append(str(Path(__file__).resolve().parent))
11
+ from path_utils import MODELS
12
+
13
+ # Re-import Autoencoder class manually if not using external file
14
+ class Autoencoder(nn.Module):
15
+ def __init__(self, input_dim=29, bottleneck_dim=16):
16
+ super(Autoencoder, self).__init__()
17
+ self.encoder = nn.Sequential(
18
+ nn.Linear(input_dim, 64), nn.ReLU(True), nn.Dropout(0.1),
19
+ nn.Linear(64, 32), nn.ReLU(True),
20
+ nn.Linear(32, bottleneck_dim), nn.ReLU(True)
21
+ )
22
+ self.decoder = nn.Sequential(
23
+ nn.Linear(bottleneck_dim, 32), nn.ReLU(True),
24
+ nn.Linear(32, 64), nn.ReLU(True),
25
+ nn.Linear(64, input_dim)
26
+ )
27
+ def forward(self, x):
28
+ z = self.encoder(x)
29
+ x_hat = self.decoder(z)
30
+ return x_hat, z
31
+
32
+ def load_inference_artifacts():
33
+ """Load model, scaler, and threshold"""
34
+ model = Autoencoder()
35
+ model_path = MODELS / "model.pkl"
36
+ if model_path.exists():
37
+ model.load_state_dict(torch.load(model_path, map_location='cpu'))
38
+ model.eval()
39
+
40
+ scaler = joblib.load(MODELS / "scaler.pkl")
41
+
42
+ with open(MODELS / "threshold.json", "r") as f:
43
+ threshold = json.load(f)["threshold"]
44
+
45
+ return model, scaler, threshold
46
+
47
+ def analyze_transaction(v_features, amount):
48
+ """
49
+ v_features: list/array of 28 floats (V1-V28)
50
+ amount: float
51
+ """
52
+ model, scaler, threshold = load_inference_artifacts()
53
+
54
+ # Preprocess
55
+ amount_log = np.log1p(amount)
56
+ data = np.array(v_features + [amount_log]).reshape(1, -1)
57
+ data_scaled = scaler.transform(data)
58
+
59
+ # Inference
60
+ with torch.no_grad():
61
+ input_tensor = torch.FloatTensor(data_scaled)
62
+ output_tensor, _ = model(input_tensor)
63
+
64
+ # Reconstruction Error (MSE)
65
+ mse = torch.mean((output_tensor - input_tensor)**2).item()
66
+
67
+ is_fraud = mse > threshold
68
+ return {
69
+ "reconstruction_error": mse,
70
+ "threshold": threshold,
71
+ "verdict": "FRAUDULENT" if is_fraud else "NORMAL",
72
+ "anomaly_score": (mse / threshold) * 100 # Multiplier for visual UI
73
+ }
models/model.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:9f0afe0d4aaf96c62e3803587542ce36beb3353efd2c5ae77901ba9e3a7f2ca1
3
+ size 40082
models/scaler.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:e85e85083384605daae454f207ab8213dcd1fea288dde76a0d05c7f61fff36ef
3
+ size 1711
models/threshold.json ADDED
@@ -0,0 +1 @@
 
 
1
+ {"threshold": 1.0854053497314453}
path_utils.py ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from pathlib import Path
2
+
3
+ ROOT = Path(__file__).resolve().parent
4
+ DATA_RAW = ROOT / "data" / "raw"
5
+ DATA_PROCESSED = ROOT / "data" / "processed"
6
+ DATA_ARTIFACTS = ROOT / "data" / "artifacts"
7
+ MODELS = ROOT / "models"
8
+ CHARTS = ROOT / "charts"
9
+
10
+ # Folders should be pre-created in the Docker image to avoid runtime PermissionErrors
11
+ # No runtime mkdir here for production safety
project_problem.md ADDED
@@ -0,0 +1,228 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Project 10 β€” Autoencoder Anomaly Detection
2
+ **Level:** Advanced | **Dataset:** Credit Card Fraud (Kaggle) | **Framework:** PyTorch
3
+
4
+ ---
5
+
6
+ ## Objective
7
+ Build an Autoencoder that learns the distribution of normal transactions and flags anomalies via high reconstruction error.
8
+ Cover: encoder-decoder architecture, bottleneck, reconstruction loss, threshold tuning, unsupervised anomaly detection.
9
+
10
+ ---
11
+
12
+ ## Project Structure
13
+ ```
14
+ 10_autoencoder_anomaly/
15
+ β”œβ”€β”€ notebooks/
16
+ β”‚ β”œβ”€β”€ 01_eda.ipynb
17
+ β”‚ β”œβ”€β”€ 02_preprocessing.ipynb
18
+ β”‚ └── 03_train_evaluate.ipynb
19
+ β”œβ”€β”€ data/raw/creditcard.csv
20
+ β”œβ”€β”€ data/processed/
21
+ β”œβ”€β”€ models/model.pkl
22
+ β”œβ”€β”€ charts/
23
+ β”œβ”€β”€ path_utils.py
24
+ β”œβ”€β”€ dashboard_core.py
25
+ └── app.py
26
+ ```
27
+
28
+ **Dataset:** `mlg-ulb/credit-card-fraud-detection` β€” Kaggle.
29
+ 284,807 transactions, 492 fraud (0.17% fraud rate).
30
+ Features: V1-V28 (PCA-transformed), Amount, Time. Target: Class (0=normal, 1=fraud).
31
+
32
+ ---
33
+
34
+ ## Notebook 01 β€” EDA (`01_eda.ipynb`)
35
+
36
+ ### STOP 1 β€” Load & Class Distribution
37
+ - Load creditcard.csv
38
+ - Print class distribution: 284,315 normal, 492 fraud
39
+ - This is extreme imbalance: 99.83% vs 0.17%
40
+ - **Agent stops here. Explain:**
41
+ - Why this is a perfect Autoencoder use case: we have very few fraud examples
42
+ - The unsupervised insight: train ONLY on normal β†’ AE learns to reconstruct normal well β†’ fraud reconstructed poorly
43
+ - Why supervised models struggle here: too few fraud examples even with class weights
44
+ - Real-world context: in production, fraud patterns change constantly β€” unsupervised is more robust
45
+ - Wait for user confirmation before continuing
46
+
47
+ ### STOP 2 β€” Feature Analysis
48
+ - Plot distribution of `Amount` β€” heavily skewed, log transform
49
+ - Plot distribution of V1, V5, V14 (most fraud-discriminative PCA components)
50
+ - Overlay normal vs fraud distributions for V14
51
+ - **Agent stops here. Explain:**
52
+ - What V1-V28 are: principal components of original transaction features (anonymized by Kaggle)
53
+ - Why Amount needs special treatment (raw dollar amount vs scaled PCA features)
54
+ - What overlapping distributions mean: fraud and normal transactions look similar to linear models
55
+ - How the AE exploits the subtle difference: it learns the joint distribution of all features
56
+ - Wait for confirmation
57
+
58
+ ### STOP 3 β€” Reconstruction Concept Walkthrough
59
+ - Explain (with markdown cells) what reconstruction means:
60
+ - AE takes input x β†’ compresses to z (bottleneck) β†’ reconstructs xΜ‚
61
+ - Loss: MSE(x, xΜ‚) averaged over all features
62
+ - At inference: high MSE = anomalous
63
+ - **Agent stops here. Explain:**
64
+ - The information bottleneck principle: compression forces the model to learn the "essence"
65
+ - Why bottleneck width matters: too wide = AE memorizes everything (no anomaly detection), too narrow = loses normal patterns too
66
+ - What reconstruction error distribution looks like for normal vs fraud
67
+ - Wait for confirmation
68
+
69
+ ---
70
+
71
+ ## Notebook 02 β€” Preprocessing (`02_preprocessing.ipynb`)
72
+
73
+ ### STOP 4 β€” Isolation of Normal Class
74
+ - Separate: `normal_df = df[df['Class'] == 0]`
75
+ - Training set: 80% of normal only (no fraud in training)
76
+ - Validation set: 10% normal + ALL 492 fraud (to tune threshold)
77
+ - Test set: remaining 10% normal + reserved 100 fraud
78
+ - **Agent stops here. Explain:**
79
+ - Why we train ONLY on normal data β€” this is the core principle of AE-based anomaly detection
80
+ - Why we include fraud in validation: to find the optimal reconstruction error threshold
81
+ - The correct split strategy for unsupervised anomaly detection
82
+ - Wait for confirmation
83
+
84
+ ### STOP 5 β€” Feature Scaling
85
+ - Log transform `Amount`: `np.log1p(df['Amount'])`
86
+ - Drop `Time` column (not informative after PCA)
87
+ - `StandardScaler` fit on normal train features only
88
+ - Apply to normal train, val (normal+fraud), test (normal+fraud)
89
+ - **Agent stops here. Explain:**
90
+ - Why log1p for Amount: log(1+x) handles zero correctly, compresses large values
91
+ - Why StandardScaler fit only on normal train: we're assuming normal distribution of normal transactions
92
+ - What happens if we scale fraud using fraud statistics (leakage, defeats the purpose)
93
+ - Wait for confirmation
94
+
95
+ ### STOP 6 β€” Tensor Dataset
96
+ - Normal train: X only (no labels needed for training β€” unsupervised)
97
+ - Val/Test: (X, y) pairs where y is the fraud label for evaluation
98
+ - DataLoader for train: batch_size=256, shuffle=True
99
+ - **Agent stops here. Explain:**
100
+ - Why training DataLoader has no labels: AE is trained to minimize reconstruction error, not classify
101
+ - How this is fundamentally different from all previous supervised projects
102
+ - What "unsupervised learning" means in practice
103
+ - Wait for confirmation
104
+
105
+ ---
106
+
107
+ ## Notebook 03 β€” Train & Evaluate (`03_train_evaluate.ipynb`)
108
+
109
+ ### STOP 7 β€” Autoencoder Architecture
110
+ ```
111
+ Encoder:
112
+ Linear(29, 64) β†’ ReLU β†’ Dropout(0.1)
113
+ Linear(64, 32) β†’ ReLU
114
+ Linear(32, 16) β†’ ReLU [bottleneck = 16]
115
+
116
+ Decoder:
117
+ Linear(16, 32) β†’ ReLU
118
+ Linear(32, 64) β†’ ReLU
119
+ Linear(64, 29) [no activation β€” reconstruct any value]
120
+ ```
121
+ Forward: `x β†’ z = encode(x) β†’ x_hat = decode(z) β†’ return x_hat`
122
+
123
+ - **Agent stops here. Explain:**
124
+ - Symmetric encoder-decoder: decoder mirrors encoder structure
125
+ - Bottleneck dimension=16: compresses 29 features to 16 (forced information bottleneck)
126
+ - Why no activation at decoder output: output must match input range (any real value after scaling)
127
+ - What the latent space z represents: compressed representation of the transaction
128
+ - How to choose bottleneck size: experiment β€” too small loses normal patterns, too large = no compression
129
+ - Wait for confirmation
130
+
131
+ ### STOP 8 β€” Reconstruction Loss
132
+ - Use `nn.MSELoss(reduction='none')` β€” keep per-sample, per-feature losses
133
+ - Average over features for per-sample reconstruction error
134
+ - Training loss: mean of per-sample errors
135
+ - **Agent stops here. Explain:**
136
+ - Why `reduction='none'`: we need per-sample error at inference time
137
+ - What reconstruction error for ONE sample looks like: scalar value (mean over 29 features)
138
+ - Why MSE penalizes large reconstruction errors quadratically β€” good for detecting anomalies
139
+ - Alternative: MAE loss β€” less sensitive to outliers (sometimes better for AE)
140
+ - Wait for confirmation
141
+
142
+ ### STOP 9 β€” Training Loop
143
+ - Train on NORMAL ONLY for 50 epochs
144
+ - Track train reconstruction error per epoch
145
+ - Also compute val reconstruction error for normal vs fraud separately
146
+ - Plot: normal reconstruction error distribution vs fraud reconstruction error distribution
147
+ - **Agent stops here. Explain:**
148
+ - What we expect to see: two distributions, fraud shifted right (higher error)
149
+ - Why the distributions might overlap: some fraud looks like normal, some normal looks weird
150
+ - The separation quality directly predicts AUC
151
+ - What "collapse" looks like if bottleneck is too wide: both distributions identical
152
+ - Wait for confirmation
153
+
154
+ ### STOP 10 β€” Threshold Tuning
155
+ - Compute reconstruction error for ALL validation samples (normal + fraud)
156
+ - Try thresholds from min to max error at 100 steps
157
+ - For each threshold: compute Precision, Recall, F1
158
+ - Plot F1 vs threshold curve
159
+ - Select threshold that maximizes F1 (or recall, depending on business requirement)
160
+ - **Agent stops here. Explain:**
161
+ - What threshold selection is: converting a continuous score to binary prediction
162
+ - The precision-recall tradeoff at different thresholds
163
+ - In fraud detection, what is worse: false positive (block good transaction) vs false negative (miss fraud)?
164
+ - Why we tune on val, evaluate on test (never touch test during tuning)
165
+ - Wait for confirmation
166
+
167
+ ### STOP 11 β€” Evaluation on Test Set
168
+ - Apply tuned threshold to test set
169
+ - Compute: Precision, Recall, F1, AUC-ROC, AUC-PR
170
+ - Plot ROC curve and Precision-Recall curve
171
+ - **Agent stops here. Explain:**
172
+ - Why AUC-PR is more informative than AUC-ROC for extreme imbalance
173
+ - What AUC-PR = 0.5 means on a 0.17% fraud rate (baseline = 0.0017!)
174
+ - Why ROC can be misleadingly optimistic with extreme imbalance
175
+ - The business metric: catch rate (recall on fraud) at a given false positive rate
176
+ - Wait for confirmation
177
+
178
+ ### STOP 12 β€” Latent Space Visualization
179
+ - Encode all test samples (normal + fraud) to get z vectors [N, 16]
180
+ - Apply t-SNE or PCA to reduce to 2D
181
+ - Plot with color: blue=normal, red=fraud
182
+ - **Agent stops here. Explain:**
183
+ - What we hope to see: fraud forming clusters away from normal
184
+ - What t-SNE shows that PCA doesn't: non-linear clustering structure
185
+ - Why fraud might not perfectly separate in latent space (some fraud IS similar to normal transactions)
186
+ - How this visualization helps in understanding model failure modes
187
+ - Wait for confirmation
188
+
189
+ ### STOP 13 β€” Save & Inference
190
+ - Save model.state_dict(), scaler, threshold
191
+ - Write `predict_fraud(transaction_dict)` β†’ label, reconstruction_error, is_fraud
192
+ - **Agent stops here. Explain:**
193
+ - Complete inference pipeline: dict β†’ preprocess (log Amount, scale) β†’ tensor β†’ model.eval() β†’ reconstruct β†’ MSE β†’ compare to threshold β†’ return
194
+ - Why we save the threshold with the model (it's part of the "model")
195
+ - How to update threshold in production as fraud patterns evolve
196
+ - Wait for confirmation
197
+
198
+ ---
199
+
200
+ ## `dashboard_core.py`
201
+ Functions:
202
+ - `load_model_scaler_threshold()` β†’ model, scaler, threshold
203
+ - `predict_fraud(transaction_dict)` β†’ reconstruction_error, is_fraud, bool
204
+ - `get_error_distributions()` β†’ (normal_errors, fraud_errors) arrays
205
+ - `get_roc_pr_curves()` β†’ dict of curve data
206
+ - `get_latent_viz()` β†’ 2D coords + labels
207
+
208
+ ---
209
+
210
+ ## `app.py` β€” Streamlit (~80 lines)
211
+ Sections:
212
+ 1. Sidebar: sliders for V1, V14, V17, Amount (most discriminative features)
213
+ 2. Main: "Analyze Transaction" β†’ show reconstruction error + fraud/normal verdict
214
+ 3. Tab 1: Training reconstruction error curve
215
+ 4. Tab 2: Error distribution histogram (normal vs fraud overlap)
216
+ 5. Tab 3: ROC + PR curves
217
+
218
+ ---
219
+
220
+ ## Key Concepts Covered
221
+ - Autoencoder architecture (encoder, bottleneck, decoder)
222
+ - Information bottleneck principle
223
+ - Training on normal only (unsupervised anomaly detection)
224
+ - Reconstruction loss (MSE reduction='none' for per-sample)
225
+ - Threshold tuning on validation set
226
+ - AUC-PR vs AUC-ROC for imbalanced data
227
+ - Latent space visualization with t-SNE
228
+ - Full unsupervised learning pipeline