Shuya Feng commited on
Commit
6640531
·
1 Parent(s): 17e9685

feat: Implement DP-SGD Explorer web application with Flask backend, interactive frontend, and easy deployment script

Browse files
README.md CHANGED
@@ -1 +1,77 @@
1
- # DPSGDTool
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # DP-SGD Explorer
2
+
3
+ An interactive web application for exploring and learning about Differentially Private Stochastic Gradient Descent (DP-SGD).
4
+
5
+ ## Features
6
+
7
+ - Interactive playground for experimenting with DP-SGD parameters
8
+ - Comprehensive learning hub with detailed explanations
9
+ - Real-time privacy budget calculations
10
+ - Training visualizations and metrics
11
+ - Parameter recommendations
12
+
13
+ ## Requirements
14
+
15
+ - Python 3.8 or higher
16
+ - Modern web browser (Chrome, Firefox, Safari, or Edge)
17
+
18
+ ## Quick Start
19
+
20
+ 1. Clone this repository:
21
+ ```bash
22
+ git clone https://github.com/yourusername/dpsgd-explorer.git
23
+ cd dpsgd-explorer
24
+ ```
25
+
26
+ 2. Run the start script:
27
+ ```bash
28
+ ./start_server.sh
29
+ ```
30
+
31
+ 3. Open your web browser and navigate to:
32
+ ```
33
+ http://127.0.0.1:5000
34
+ ```
35
+
36
+ The start script will automatically:
37
+ - Check for Python installation
38
+ - Create a virtual environment
39
+ - Install required dependencies
40
+ - Start the Flask development server
41
+
42
+ ## Manual Setup (if the script doesn't work)
43
+
44
+ 1. Create a virtual environment:
45
+ ```bash
46
+ python3 -m venv .venv
47
+ source .venv/bin/activate # On Windows: .venv\Scripts\activate
48
+ ```
49
+
50
+ 2. Install dependencies:
51
+ ```bash
52
+ pip install -r requirements.txt
53
+ ```
54
+
55
+ 3. Start the server:
56
+ ```bash
57
+ PYTHONPATH=. python3 run.py
58
+ ```
59
+
60
+ ## Project Structure
61
+
62
+ ```
63
+ dpsgd-explorer/
64
+ ├── app/
65
+ │ ├── static/ # Static files (CSS, JS)
66
+ │ ├── templates/ # HTML templates
67
+ │ ├── training/ # Training simulation
68
+ │ ├── routes.py # Flask routes
69
+ │ └── __init__.py # App initialization
70
+ ├── requirements.txt # Python dependencies
71
+ ├── run.py # Application entry point
72
+ └── start_server.sh # Start script
73
+ ```
74
+
75
+ ## License
76
+
77
+ MIT License - Feel free to use this project for learning and educational purposes.
app/__init__.py ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from flask import Flask
2
+ from flask_cors import CORS
3
+
4
+ def create_app():
5
+ app = Flask(__name__)
6
+ CORS(app)
7
+
8
+ # Register blueprints
9
+ from app.routes import main
10
+ app.register_blueprint(main)
11
+
12
+ return app
app/__pycache__/__init__.cpython-38.pyc ADDED
Binary file (444 Bytes). View file
 
app/__pycache__/routes.cpython-38.pyc ADDED
Binary file (1.58 kB). View file
 
app/routes.py ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from flask import Blueprint, render_template, jsonify, request
2
+ from app.training.mock_trainer import MockTrainer
3
+ from app.training.privacy_calculator import PrivacyCalculator
4
+
5
+ main = Blueprint('main', __name__)
6
+ mock_trainer = MockTrainer()
7
+ privacy_calculator = PrivacyCalculator()
8
+
9
+ @main.route('/')
10
+ def index():
11
+ return render_template('index.html')
12
+
13
+ @main.route('/learning')
14
+ def learning():
15
+ return render_template('learning.html')
16
+
17
+ @main.route('/api/train', methods=['POST'])
18
+ def train():
19
+ data = request.json
20
+ params = {
21
+ 'clipping_norm': float(data.get('clipping_norm', 1.0)),
22
+ 'noise_multiplier': float(data.get('noise_multiplier', 1.0)),
23
+ 'batch_size': int(data.get('batch_size', 64)),
24
+ 'learning_rate': float(data.get('learning_rate', 0.01)),
25
+ 'epochs': int(data.get('epochs', 5))
26
+ }
27
+
28
+ # Get mock training results
29
+ results = mock_trainer.train(params)
30
+ return jsonify(results)
31
+
32
+ @main.route('/api/privacy-budget', methods=['POST'])
33
+ def calculate_privacy_budget():
34
+ data = request.json
35
+ params = {
36
+ 'clipping_norm': float(data.get('clipping_norm', 1.0)),
37
+ 'noise_multiplier': float(data.get('noise_multiplier', 1.0)),
38
+ 'batch_size': int(data.get('batch_size', 64)),
39
+ 'epochs': int(data.get('epochs', 5))
40
+ }
41
+
42
+ epsilon = privacy_calculator.calculate_epsilon(params)
43
+ return jsonify({'epsilon': epsilon})
app/static/css/learning.css ADDED
@@ -0,0 +1,202 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* Learning Hub Container */
2
+ .learning-container {
3
+ display: flex;
4
+ gap: 2rem;
5
+ padding: 2rem;
6
+ max-width: 1400px;
7
+ margin: 0 auto;
8
+ }
9
+
10
+ /* Sidebar Styles */
11
+ .learning-sidebar {
12
+ flex: 0 0 300px;
13
+ background: #f8f9fa;
14
+ border-radius: 8px;
15
+ padding: 1.5rem;
16
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
17
+ height: fit-content;
18
+ position: sticky;
19
+ top: 2rem;
20
+ }
21
+
22
+ .learning-steps {
23
+ list-style: none;
24
+ padding: 0;
25
+ margin: 1rem 0;
26
+ }
27
+
28
+ .learning-step {
29
+ padding: 0.75rem 1rem;
30
+ margin: 0.5rem 0;
31
+ border-radius: 6px;
32
+ cursor: pointer;
33
+ transition: all 0.3s ease;
34
+ font-size: 0.95rem;
35
+ color: #495057;
36
+ }
37
+
38
+ .learning-step:hover {
39
+ background: #e9ecef;
40
+ color: #212529;
41
+ }
42
+
43
+ .learning-step.active {
44
+ background: #007bff;
45
+ color: white;
46
+ font-weight: 500;
47
+ }
48
+
49
+ /* Content Area Styles */
50
+ .learning-content {
51
+ flex: 1;
52
+ min-width: 0;
53
+ }
54
+
55
+ .step-content {
56
+ display: none;
57
+ animation: fadeIn 0.3s ease;
58
+ }
59
+
60
+ .step-content.active {
61
+ display: block;
62
+ }
63
+
64
+ /* Typography */
65
+ .section-title {
66
+ font-size: 2.5rem;
67
+ color: #212529;
68
+ margin-bottom: 2rem;
69
+ text-align: center;
70
+ }
71
+
72
+ .panel-title {
73
+ font-size: 1.25rem;
74
+ color: #343a40;
75
+ margin-bottom: 1rem;
76
+ }
77
+
78
+ .step-content h2 {
79
+ font-size: 2rem;
80
+ color: #212529;
81
+ margin-bottom: 1.5rem;
82
+ }
83
+
84
+ .step-content h3 {
85
+ font-size: 1.5rem;
86
+ color: #343a40;
87
+ margin: 2rem 0 1rem;
88
+ }
89
+
90
+ .step-content p {
91
+ font-size: 1rem;
92
+ line-height: 1.6;
93
+ color: #495057;
94
+ margin-bottom: 1rem;
95
+ }
96
+
97
+ /* Concept Box Styles */
98
+ .concept-box {
99
+ display: flex;
100
+ gap: 1.5rem;
101
+ margin: 2rem 0;
102
+ }
103
+
104
+ .box1, .box2 {
105
+ flex: 1;
106
+ background: #f8f9fa;
107
+ padding: 1.5rem;
108
+ border-radius: 8px;
109
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
110
+ }
111
+
112
+ .concept-highlight {
113
+ background: #e9ecef;
114
+ padding: 1.5rem;
115
+ border-radius: 8px;
116
+ margin: 1.5rem 0;
117
+ border-left: 4px solid #007bff;
118
+ }
119
+
120
+ /* Formula Styles */
121
+ .formula {
122
+ background: #f8f9fa;
123
+ padding: 1.5rem;
124
+ border-radius: 8px;
125
+ margin: 1rem 0;
126
+ text-align: center;
127
+ font-family: 'Times New Roman', serif;
128
+ font-size: 1.2rem;
129
+ }
130
+
131
+ /* List Styles */
132
+ .step-content ul, .step-content ol {
133
+ padding-left: 1.5rem;
134
+ margin: 1rem 0;
135
+ }
136
+
137
+ .step-content li {
138
+ margin-bottom: 0.5rem;
139
+ color: #495057;
140
+ line-height: 1.5;
141
+ }
142
+
143
+ /* Animation */
144
+ @keyframes fadeIn {
145
+ from {
146
+ opacity: 0;
147
+ transform: translateY(10px);
148
+ }
149
+ to {
150
+ opacity: 1;
151
+ transform: translateY(0);
152
+ }
153
+ }
154
+
155
+ /* Responsive Design */
156
+ @media (max-width: 1024px) {
157
+ .learning-container {
158
+ flex-direction: column;
159
+ }
160
+
161
+ .learning-sidebar {
162
+ flex: none;
163
+ position: static;
164
+ width: 100%;
165
+ }
166
+
167
+ .concept-box {
168
+ flex-direction: column;
169
+ }
170
+ }
171
+
172
+ /* Code and Math Styles */
173
+ code {
174
+ background: #f8f9fa;
175
+ padding: 0.2rem 0.4rem;
176
+ border-radius: 4px;
177
+ font-family: 'Courier New', monospace;
178
+ font-size: 0.9rem;
179
+ }
180
+
181
+ sub {
182
+ font-size: 0.75em;
183
+ vertical-align: sub;
184
+ }
185
+
186
+ /* Links */
187
+ .step-content a {
188
+ color: #007bff;
189
+ text-decoration: none;
190
+ transition: color 0.2s ease;
191
+ }
192
+
193
+ .step-content a:hover {
194
+ color: #0056b3;
195
+ text-decoration: underline;
196
+ }
197
+
198
+ /* Strong Text */
199
+ strong {
200
+ color: #212529;
201
+ font-weight: 600;
202
+ }
app/static/css/styles.css ADDED
@@ -0,0 +1,457 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ :root {
2
+ --primary-color: #3f51b5;
3
+ --primary-light: #757de8;
4
+ --primary-dark: #002984;
5
+ --secondary-color: #4caf50;
6
+ --accent-color: #ff9800;
7
+ --error-color: #f44336;
8
+ --text-primary: #333;
9
+ --text-secondary: #666;
10
+ --background-light: #fff;
11
+ --background-off: #f5f7fa;
12
+ --border-color: #ddd;
13
+
14
+ --font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
15
+ --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);
16
+ }
17
+
18
+ /* Base Styles */
19
+ body {
20
+ font-family: var(--font-family);
21
+ margin: 0;
22
+ padding: 0;
23
+ background: var(--background-off);
24
+ color: var(--text-primary);
25
+ }
26
+
27
+ .app-container {
28
+ min-height: 100vh;
29
+ display: flex;
30
+ flex-direction: column;
31
+ }
32
+
33
+ /* Header */
34
+ .main-header {
35
+ background-color: var(--primary-color);
36
+ color: white;
37
+ padding: 1rem;
38
+ box-shadow: var(--shadow-sm);
39
+ }
40
+
41
+ .header-container {
42
+ display: flex;
43
+ justify-content: space-between;
44
+ align-items: center;
45
+ max-width: 1200px;
46
+ margin: 0 auto;
47
+ }
48
+
49
+ .logo {
50
+ font-size: 1.5rem;
51
+ font-weight: bold;
52
+ }
53
+
54
+ .tagline {
55
+ font-size: 0.8rem;
56
+ opacity: 0.8;
57
+ }
58
+
59
+ /* Navigation */
60
+ .nav-list {
61
+ list-style: none;
62
+ display: flex;
63
+ gap: 1rem;
64
+ padding: 0;
65
+ margin: 0;
66
+ }
67
+
68
+ .nav-link {
69
+ color: white;
70
+ cursor: pointer;
71
+ padding: 0.5rem 1rem;
72
+ border-radius: 4px;
73
+ text-decoration: none;
74
+ }
75
+
76
+ .nav-link:hover {
77
+ background-color: rgba(255, 255, 255, 0.1);
78
+ }
79
+
80
+ .nav-link.active {
81
+ background-color: rgba(255, 255, 255, 0.2);
82
+ }
83
+
84
+ /* Main Content */
85
+ .main-content {
86
+ flex: 1;
87
+ max-width: 1200px;
88
+ margin: 0 auto;
89
+ padding: 1rem;
90
+ }
91
+
92
+ .section-title {
93
+ font-size: 2rem;
94
+ color: var(--primary-dark);
95
+ margin-bottom: 1.5rem;
96
+ }
97
+
98
+ /* Grid Layout */
99
+ .lab-container {
100
+ display: grid;
101
+ grid-template-columns: 300px 1fr;
102
+ gap: 1.5rem;
103
+ }
104
+
105
+ @media (max-width: 900px) {
106
+ .lab-container {
107
+ grid-template-columns: 1fr;
108
+ }
109
+ }
110
+
111
+ /* Panels */
112
+ .panel {
113
+ background: white;
114
+ border-radius: 8px;
115
+ padding: 1rem;
116
+ box-shadow: var(--shadow-sm);
117
+ }
118
+
119
+ .panel-title {
120
+ font-size: 1.2rem;
121
+ margin-bottom: 1rem;
122
+ color: var(--primary-dark);
123
+ }
124
+
125
+ /* Parameter Controls */
126
+ .parameter-control {
127
+ margin-bottom: 1rem;
128
+ }
129
+
130
+ .parameter-label {
131
+ display: block;
132
+ margin-bottom: 0.5rem;
133
+ font-weight: 500;
134
+ }
135
+
136
+ .parameter-slider {
137
+ width: 100%;
138
+ margin-bottom: 0.5rem;
139
+ }
140
+
141
+ .slider-display {
142
+ display: flex;
143
+ justify-content: space-between;
144
+ font-size: 0.9rem;
145
+ color: var(--text-secondary);
146
+ }
147
+
148
+ /* Privacy Budget Display */
149
+ .budget-display {
150
+ margin-top: 1.5rem;
151
+ padding: 1rem;
152
+ background: var(--background-off);
153
+ border-radius: 4px;
154
+ }
155
+
156
+ .budget-bar {
157
+ height: 8px;
158
+ background-color: #e0e0e0;
159
+ border-radius: 4px;
160
+ position: relative;
161
+ margin: 0.5rem 0;
162
+ }
163
+
164
+ .budget-fill {
165
+ height: 100%;
166
+ border-radius: 4px;
167
+ background-color: var(--accent-color);
168
+ transition: width 0.3s ease;
169
+ }
170
+
171
+ .budget-fill.low {
172
+ background-color: var(--secondary-color);
173
+ }
174
+
175
+ .budget-fill.medium {
176
+ background-color: var(--accent-color);
177
+ }
178
+
179
+ .budget-fill.high {
180
+ background-color: var(--error-color);
181
+ }
182
+
183
+ /* Buttons */
184
+ .control-button {
185
+ width: 100%;
186
+ padding: 0.8rem;
187
+ border: none;
188
+ border-radius: 4px;
189
+ background-color: var(--primary-color);
190
+ color: white;
191
+ font-weight: bold;
192
+ cursor: pointer;
193
+ margin-top: 1rem;
194
+ transition: background-color 0.3s ease;
195
+ }
196
+
197
+ .control-button:hover {
198
+ background-color: var(--primary-dark);
199
+ }
200
+
201
+ .control-button.running {
202
+ background-color: var(--error-color);
203
+ }
204
+
205
+ /* Charts */
206
+ .chart-container {
207
+ height: 300px;
208
+ margin-bottom: 1rem;
209
+ position: relative;
210
+ }
211
+
212
+ .chart {
213
+ width: 100%;
214
+ height: 100%;
215
+ border: 1px solid var(--border-color);
216
+ border-radius: 4px;
217
+ }
218
+
219
+ /* Metrics */
220
+ .metrics-grid {
221
+ display: grid;
222
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
223
+ gap: 1rem;
224
+ margin-bottom: 1rem;
225
+ }
226
+
227
+ .metric-card {
228
+ background-color: var(--background-off);
229
+ border-radius: 4px;
230
+ padding: 1rem;
231
+ text-align: center;
232
+ }
233
+
234
+ .metric-value {
235
+ font-size: 1.5rem;
236
+ font-weight: bold;
237
+ margin-bottom: 0.5rem;
238
+ }
239
+
240
+ .metric-label {
241
+ color: var(--text-secondary);
242
+ font-weight: 500;
243
+ }
244
+
245
+ /* Recommendations */
246
+ .recommendation-list {
247
+ list-style: none;
248
+ padding: 0;
249
+ margin: 0;
250
+ }
251
+
252
+ .recommendation-item {
253
+ display: flex;
254
+ align-items: flex-start;
255
+ padding: 0.8rem 0;
256
+ border-bottom: 1px solid var(--border-color);
257
+ }
258
+
259
+ .recommendation-icon {
260
+ margin-right: 0.5rem;
261
+ font-size: 1.2rem;
262
+ }
263
+
264
+ /* Tabs */
265
+ .tabs {
266
+ display: flex;
267
+ margin-bottom: 1rem;
268
+ }
269
+
270
+ .tab {
271
+ padding: 0.5rem 1rem;
272
+ cursor: pointer;
273
+ border-bottom: 2px solid transparent;
274
+ transition: all 0.3s ease;
275
+ }
276
+
277
+ .tab:hover {
278
+ color: var(--primary-color);
279
+ }
280
+
281
+ .tab.active {
282
+ border-bottom: 2px solid var(--primary-color);
283
+ color: var(--primary-color);
284
+ }
285
+
286
+ .tab-content {
287
+ display: none;
288
+ }
289
+
290
+ .tab-content.active {
291
+ display: block;
292
+ }
293
+
294
+ /* Training Status */
295
+ .status-badge {
296
+ display: flex;
297
+ align-items: center;
298
+ margin-top: 1rem;
299
+ padding: 0.5rem;
300
+ background-color: var(--background-off);
301
+ border-radius: 4px;
302
+ }
303
+
304
+ .pulse {
305
+ display: inline-block;
306
+ width: 10px;
307
+ height: 10px;
308
+ border-radius: 50%;
309
+ background: var(--secondary-color);
310
+ margin-right: 0.5rem;
311
+ animation: pulse 1.5s infinite;
312
+ }
313
+
314
+ @keyframes pulse {
315
+ 0% {
316
+ box-shadow: 0 0 0 0 rgba(76, 175, 80, 0.7);
317
+ }
318
+ 70% {
319
+ box-shadow: 0 0 0 10px rgba(76, 175, 80, 0);
320
+ }
321
+ 100% {
322
+ box-shadow: 0 0 0 0 rgba(76, 175, 80, 0);
323
+ }
324
+ }
325
+
326
+ /* Footer */
327
+ .footer {
328
+ text-align: center;
329
+ padding: 1rem;
330
+ background-color: var(--primary-dark);
331
+ color: white;
332
+ margin-top: 2rem;
333
+ }
334
+
335
+ /* Tooltips */
336
+ .tooltip {
337
+ position: relative;
338
+ display: inline-block;
339
+ margin-left: 0.5rem;
340
+ }
341
+
342
+ .tooltip-icon {
343
+ width: 16px;
344
+ height: 16px;
345
+ border-radius: 50%;
346
+ background-color: var(--primary-light);
347
+ color: white;
348
+ font-size: 12px;
349
+ display: flex;
350
+ align-items: center;
351
+ justify-content: center;
352
+ cursor: help;
353
+ }
354
+
355
+ .tooltip-text {
356
+ visibility: hidden;
357
+ width: 200px;
358
+ background-color: #333;
359
+ color: white;
360
+ text-align: center;
361
+ border-radius: 4px;
362
+ padding: 0.5rem;
363
+ position: absolute;
364
+ z-index: 1;
365
+ bottom: 125%;
366
+ left: 50%;
367
+ margin-left: -100px;
368
+ opacity: 0;
369
+ transition: opacity 0.3s;
370
+ font-size: 0.8rem;
371
+ }
372
+
373
+ .tooltip:hover .tooltip-text {
374
+ visibility: visible;
375
+ opacity: 1;
376
+ }
377
+
378
+ /* Learning Hub Styles */
379
+ .learning-container {
380
+ display: grid;
381
+ grid-template-columns: 250px 1fr;
382
+ gap: 1.5rem;
383
+ }
384
+
385
+ .learning-sidebar {
386
+ background: white;
387
+ border-radius: 8px;
388
+ padding: 1rem;
389
+ box-shadow: var(--shadow-sm);
390
+ }
391
+
392
+ .learning-content {
393
+ background: white;
394
+ border-radius: 8px;
395
+ padding: 1.5rem;
396
+ box-shadow: var(--shadow-sm);
397
+ }
398
+
399
+ .learning-steps {
400
+ list-style: none;
401
+ padding: 0;
402
+ margin: 0;
403
+ }
404
+
405
+ .learning-step {
406
+ padding: 0.75rem 0.5rem;
407
+ border-radius: 4px;
408
+ cursor: pointer;
409
+ margin-bottom: 0.5rem;
410
+ transition: all 0.3s ease;
411
+ }
412
+
413
+ .learning-step:hover {
414
+ background-color: var(--background-off);
415
+ }
416
+
417
+ .learning-step.active {
418
+ background-color: var(--background-off);
419
+ color: var(--primary-color);
420
+ font-weight: 500;
421
+ }
422
+
423
+ /* Concept Boxes */
424
+ .concept-highlight {
425
+ background-color: var(--background-off);
426
+ border-radius: 4px;
427
+ padding: 1rem;
428
+ margin: 1rem 0;
429
+ }
430
+
431
+ .formula {
432
+ background-color: #f5f7fa;
433
+ padding: 0.75rem;
434
+ border-radius: 4px;
435
+ font-family: monospace;
436
+ margin: 1rem 0;
437
+ }
438
+
439
+ .concept-box {
440
+ display: flex;
441
+ margin: 1rem 0;
442
+ gap: 1rem;
443
+ }
444
+
445
+ .concept-box > div {
446
+ flex: 1;
447
+ padding: 1rem;
448
+ border-radius: 8px;
449
+ }
450
+
451
+ .concept-box .box1 {
452
+ background-color: #e3f2fd;
453
+ }
454
+
455
+ .concept-box .box2 {
456
+ background-color: #fff8e1;
457
+ }
app/static/js/main.js ADDED
@@ -0,0 +1,371 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ class DPSGDExplorer {
2
+ constructor() {
3
+ this.trainingChart = null;
4
+ this.privacyChart = null;
5
+ this.isTraining = false;
6
+ this.initializeUI();
7
+ }
8
+
9
+ initializeUI() {
10
+ // Initialize parameter controls
11
+ this.initializeSliders();
12
+ this.initializePresets();
13
+ this.initializeTabs();
14
+ this.initializeCharts();
15
+
16
+ // Add event listeners
17
+ document.getElementById('train-button')?.addEventListener('click', () => this.toggleTraining());
18
+ }
19
+
20
+ initializeSliders() {
21
+ // Parameter sliders
22
+ const sliders = {
23
+ 'clipping-norm': document.getElementById('clipping-norm'),
24
+ 'noise-multiplier': document.getElementById('noise-multiplier'),
25
+ 'batch-size': document.getElementById('batch-size'),
26
+ 'learning-rate': document.getElementById('learning-rate'),
27
+ 'epochs': document.getElementById('epochs')
28
+ };
29
+
30
+ // Add event listeners to sliders
31
+ for (const [id, slider] of Object.entries(sliders)) {
32
+ if (slider) {
33
+ slider.addEventListener('input', (e) => {
34
+ document.getElementById(`${id}-value`).textContent = e.target.value;
35
+ this.updatePrivacyBudget();
36
+ });
37
+ }
38
+ }
39
+ }
40
+
41
+ initializePresets() {
42
+ const presets = {
43
+ 'high-privacy': {
44
+ clippingNorm: 1.0,
45
+ noiseMultiplier: 1.5,
46
+ batchSize: 256,
47
+ learningRate: 0.005,
48
+ epochs: 10
49
+ },
50
+ 'balanced': {
51
+ clippingNorm: 1.0,
52
+ noiseMultiplier: 1.0,
53
+ batchSize: 128,
54
+ learningRate: 0.01,
55
+ epochs: 8
56
+ },
57
+ 'high-utility': {
58
+ clippingNorm: 1.5,
59
+ noiseMultiplier: 0.5,
60
+ batchSize: 64,
61
+ learningRate: 0.02,
62
+ epochs: 5
63
+ }
64
+ };
65
+
66
+ // Add event listeners to preset buttons
67
+ for (const [preset, values] of Object.entries(presets)) {
68
+ document.getElementById(`preset-${preset}`)?.addEventListener('click', () => {
69
+ this.applyPreset(values);
70
+ });
71
+ }
72
+ }
73
+
74
+ initializeTabs() {
75
+ const tabs = document.querySelectorAll('.tab');
76
+ tabs.forEach(tab => {
77
+ tab.addEventListener('click', () => {
78
+ const tabsContainer = tab.closest('.tabs');
79
+ tabsContainer.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
80
+ tab.classList.add('active');
81
+
82
+ const tabName = tab.getAttribute('data-tab');
83
+ const panel = tab.closest('.panel');
84
+ panel.querySelectorAll('.tab-content').forEach(content => {
85
+ content.classList.remove('active');
86
+ });
87
+ panel.querySelector(`#${tabName}-tab`)?.classList.add('active');
88
+ });
89
+ });
90
+ }
91
+
92
+ initializeCharts() {
93
+ const trainingCtx = document.getElementById('training-chart')?.getContext('2d');
94
+ const privacyCtx = document.getElementById('privacy-chart')?.getContext('2d');
95
+
96
+ if (trainingCtx) {
97
+ this.trainingChart = new Chart(trainingCtx, {
98
+ type: 'line',
99
+ data: {
100
+ labels: [],
101
+ datasets: [
102
+ {
103
+ label: 'Accuracy',
104
+ borderColor: '#4caf50',
105
+ data: [],
106
+ yAxisID: 'y'
107
+ },
108
+ {
109
+ label: 'Loss',
110
+ borderColor: '#f44336',
111
+ data: [],
112
+ yAxisID: 'y1'
113
+ }
114
+ ]
115
+ },
116
+ options: {
117
+ responsive: true,
118
+ interaction: {
119
+ mode: 'index',
120
+ intersect: false,
121
+ },
122
+ scales: {
123
+ y: {
124
+ type: 'linear',
125
+ display: true,
126
+ position: 'left',
127
+ title: {
128
+ display: true,
129
+ text: 'Accuracy (%)'
130
+ }
131
+ },
132
+ y1: {
133
+ type: 'linear',
134
+ display: true,
135
+ position: 'right',
136
+ title: {
137
+ display: true,
138
+ text: 'Loss'
139
+ },
140
+ grid: {
141
+ drawOnChartArea: false,
142
+ },
143
+ }
144
+ }
145
+ }
146
+ });
147
+ }
148
+
149
+ if (privacyCtx) {
150
+ this.privacyChart = new Chart(privacyCtx, {
151
+ type: 'line',
152
+ data: {
153
+ labels: [],
154
+ datasets: [{
155
+ label: 'Privacy Budget (ε)',
156
+ borderColor: '#3f51b5',
157
+ data: []
158
+ }]
159
+ },
160
+ options: {
161
+ responsive: true,
162
+ scales: {
163
+ y: {
164
+ title: {
165
+ display: true,
166
+ text: 'Privacy Budget (ε)'
167
+ }
168
+ },
169
+ x: {
170
+ title: {
171
+ display: true,
172
+ text: 'Epoch'
173
+ }
174
+ }
175
+ }
176
+ }
177
+ });
178
+ }
179
+ }
180
+
181
+ async updatePrivacyBudget() {
182
+ const params = this.getParameters();
183
+ try {
184
+ const response = await fetch('/api/privacy-budget', {
185
+ method: 'POST',
186
+ headers: {
187
+ 'Content-Type': 'application/json',
188
+ },
189
+ body: JSON.stringify(params)
190
+ });
191
+ const data = await response.json();
192
+
193
+ // Update UI
194
+ const budgetValue = document.getElementById('budget-value');
195
+ const budgetFill = document.getElementById('budget-fill');
196
+
197
+ if (budgetValue && budgetFill) {
198
+ budgetValue.textContent = data.epsilon.toFixed(2);
199
+ budgetFill.style.width = `${Math.min(data.epsilon / 10 * 100, 100)}%`;
200
+
201
+ // Update class for coloring
202
+ budgetFill.classList.remove('low', 'medium', 'high');
203
+ if (data.epsilon <= 1) {
204
+ budgetFill.classList.add('low');
205
+ } else if (data.epsilon <= 5) {
206
+ budgetFill.classList.add('medium');
207
+ } else {
208
+ budgetFill.classList.add('high');
209
+ }
210
+ }
211
+ } catch (error) {
212
+ console.error('Error calculating privacy budget:', error);
213
+ }
214
+ }
215
+
216
+ async toggleTraining() {
217
+ if (this.isTraining) {
218
+ this.stopTraining();
219
+ } else {
220
+ await this.startTraining();
221
+ }
222
+ }
223
+
224
+ async startTraining() {
225
+ const trainButton = document.getElementById('train-button');
226
+ const trainingStatus = document.getElementById('training-status');
227
+
228
+ if (!trainButton || this.isTraining) return;
229
+
230
+ this.isTraining = true;
231
+ trainButton.textContent = 'Stop Training';
232
+ trainButton.classList.add('running');
233
+ trainingStatus.style.display = 'flex';
234
+
235
+ // Reset charts
236
+ this.resetCharts();
237
+
238
+ try {
239
+ const response = await fetch('/api/train', {
240
+ method: 'POST',
241
+ headers: {
242
+ 'Content-Type': 'application/json',
243
+ },
244
+ body: JSON.stringify(this.getParameters())
245
+ });
246
+
247
+ const data = await response.json();
248
+ this.updateCharts(data.epochs_data);
249
+ this.updateResults(data);
250
+ } catch (error) {
251
+ console.error('Training error:', error);
252
+ } finally {
253
+ this.stopTraining();
254
+ }
255
+ }
256
+
257
+ stopTraining() {
258
+ this.isTraining = false;
259
+ const trainButton = document.getElementById('train-button');
260
+ if (trainButton) {
261
+ trainButton.textContent = 'Run Training';
262
+ trainButton.classList.remove('running');
263
+ }
264
+ document.getElementById('training-status').style.display = 'none';
265
+ }
266
+
267
+ resetCharts() {
268
+ if (this.trainingChart) {
269
+ this.trainingChart.data.labels = [];
270
+ this.trainingChart.data.datasets[0].data = [];
271
+ this.trainingChart.data.datasets[1].data = [];
272
+ this.trainingChart.update();
273
+ }
274
+
275
+ if (this.privacyChart) {
276
+ this.privacyChart.data.labels = [];
277
+ this.privacyChart.data.datasets[0].data = [];
278
+ this.privacyChart.update();
279
+ }
280
+ }
281
+
282
+ updateCharts(epochsData) {
283
+ if (!this.trainingChart || !epochsData) return;
284
+
285
+ const labels = epochsData.map(d => d.epoch);
286
+ const accuracies = epochsData.map(d => d.accuracy);
287
+ const losses = epochsData.map(d => d.loss);
288
+
289
+ this.trainingChart.data.labels = labels;
290
+ this.trainingChart.data.datasets[0].data = accuracies;
291
+ this.trainingChart.data.datasets[1].data = losses;
292
+ this.trainingChart.update();
293
+
294
+ // Update privacy chart
295
+ if (this.privacyChart) {
296
+ this.privacyChart.data.labels = labels;
297
+ this.privacyChart.data.datasets[0].data = epochsData.map((_, i) =>
298
+ this.calculateEpochPrivacy(i + 1)
299
+ );
300
+ this.privacyChart.update();
301
+ }
302
+ }
303
+
304
+ updateResults(data) {
305
+ // Hide no-results message and show results content
306
+ document.getElementById('no-results').style.display = 'none';
307
+ document.getElementById('results-content').style.display = 'block';
308
+
309
+ // Update metrics
310
+ document.getElementById('accuracy-value').textContent =
311
+ data.final_metrics.accuracy.toFixed(1) + '%';
312
+ document.getElementById('loss-value').textContent =
313
+ data.final_metrics.loss.toFixed(3);
314
+ document.getElementById('training-time-value').textContent =
315
+ data.final_metrics.training_time.toFixed(1) + 's';
316
+
317
+ // Update recommendations
318
+ const recommendationList = document.querySelector('.recommendation-list');
319
+ recommendationList.innerHTML = '';
320
+ data.recommendations.forEach(rec => {
321
+ const item = document.createElement('li');
322
+ item.className = 'recommendation-item';
323
+ item.innerHTML = `
324
+ <span class="recommendation-icon">${rec.icon}</span>
325
+ <span>${rec.text}</span>
326
+ `;
327
+ recommendationList.appendChild(item);
328
+ });
329
+ }
330
+
331
+ getParameters() {
332
+ return {
333
+ clipping_norm: parseFloat(document.getElementById('clipping-norm').value),
334
+ noise_multiplier: parseFloat(document.getElementById('noise-multiplier').value),
335
+ batch_size: parseInt(document.getElementById('batch-size').value),
336
+ learning_rate: parseFloat(document.getElementById('learning-rate').value),
337
+ epochs: parseInt(document.getElementById('epochs').value)
338
+ };
339
+ }
340
+
341
+ applyPreset(values) {
342
+ document.getElementById('clipping-norm').value = values.clippingNorm;
343
+ document.getElementById('noise-multiplier').value = values.noiseMultiplier;
344
+ document.getElementById('batch-size').value = values.batchSize;
345
+ document.getElementById('learning-rate').value = values.learningRate;
346
+ document.getElementById('epochs').value = values.epochs;
347
+
348
+ // Update displayed values
349
+ document.getElementById('clipping-norm-value').textContent = values.clippingNorm;
350
+ document.getElementById('noise-multiplier-value').textContent = values.noiseMultiplier;
351
+ document.getElementById('batch-size-value').textContent = values.batchSize;
352
+ document.getElementById('learning-rate-value').textContent = values.learningRate;
353
+ document.getElementById('epochs-value').textContent = values.epochs;
354
+
355
+ this.updatePrivacyBudget();
356
+ }
357
+
358
+ calculateEpochPrivacy(epoch) {
359
+ const params = this.getParameters();
360
+ const samplingRate = params.batch_size / 60000; // Assuming MNIST size
361
+ const steps = epoch * (1 / samplingRate);
362
+ const delta = 1e-5;
363
+ const c = Math.sqrt(2 * Math.log(1.25 / delta));
364
+ return Math.min((c * samplingRate * Math.sqrt(steps)) / params.noise_multiplier, 10);
365
+ }
366
+ }
367
+
368
+ // Initialize the application when the DOM is loaded
369
+ document.addEventListener('DOMContentLoaded', () => {
370
+ window.dpsgdExplorer = new DPSGDExplorer();
371
+ });
app/templates/base.html ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>{% block title %}DP-SGD Explorer{% endblock %}</title>
7
+ <link rel="stylesheet" href="{{ url_for('static', filename='css/styles.css') }}">
8
+ <link rel="stylesheet" href="{{ url_for('static', filename='css/learning.css') }}">
9
+ <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
10
+ {% block extra_head %}{% endblock %}
11
+ </head>
12
+ <body>
13
+ <div class="app-container">
14
+ <header class="main-header">
15
+ <div class="header-container">
16
+ <div>
17
+ <div class="logo">DP-SGD Explorer</div>
18
+ <div class="tagline">Interactive Learning & Experimentation</div>
19
+ </div>
20
+ <nav>
21
+ <ul class="nav-list">
22
+ <li><a href="{{ url_for('main.learning') }}" class="nav-link {% if request.endpoint == 'main.learning' %}active{% endif %}">Learning Hub</a></li>
23
+ <li><a href="{{ url_for('main.index') }}" class="nav-link {% if request.endpoint == 'main.index' %}active{% endif %}">Playground</a></li>
24
+ </ul>
25
+ </nav>
26
+ </div>
27
+ </header>
28
+
29
+ <main class="main-content">
30
+ {% block content %}{% endblock %}
31
+ </main>
32
+
33
+ <footer class="footer">
34
+ <p>DP-SGD Explorer - An Educational Tool for Differential Privacy in Machine Learning</p>
35
+ <p>© 2024 - For educational purposes</p>
36
+ </footer>
37
+ </div>
38
+
39
+ <script src="{{ url_for('static', filename='js/main.js') }}"></script>
40
+ {% block extra_scripts %}{% endblock %}
41
+ </body>
42
+ </html>
app/templates/index.html ADDED
@@ -0,0 +1,331 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {% extends "base.html" %}
2
+
3
+ {% block title %}DP-SGD Explorer - Interactive Playground{% endblock %}
4
+
5
+ {% block content %}
6
+ <h1 class="section-title">DP-SGD Interactive Playground</h1>
7
+
8
+ <div class="lab-container">
9
+ <!-- Sidebar - Configuration Panels -->
10
+ <div class="lab-sidebar">
11
+ <!-- Model Configuration Panel -->
12
+ <div class="panel">
13
+ <h2 class="panel-title">Model Configuration</h2>
14
+
15
+ <div class="parameter-control">
16
+ <label for="dataset-select" class="parameter-label">
17
+ Dataset
18
+ <span class="tooltip">
19
+ <span class="tooltip-icon">?</span>
20
+ <span class="tooltip-text">The dataset used for training affects privacy budget calculations and model accuracy.</span>
21
+ </span>
22
+ </label>
23
+ <select id="dataset-select" class="parameter-select">
24
+ <option value="mnist">MNIST Digits</option>
25
+ <option value="fashion-mnist">Fashion MNIST</option>
26
+ <option value="cifar10">CIFAR-10</option>
27
+ </select>
28
+ </div>
29
+
30
+ <div class="parameter-control">
31
+ <label for="model-select" class="parameter-label">
32
+ Model Architecture
33
+ <span class="tooltip">
34
+ <span class="tooltip-icon">?</span>
35
+ <span class="tooltip-text">The model architecture affects training time, capacity to learn, and resilience to noise.</span>
36
+ </span>
37
+ </label>
38
+ <select id="model-select" class="parameter-select">
39
+ <option value="simple-mlp">Simple MLP</option>
40
+ <option value="simple-cnn">Simple CNN</option>
41
+ <option value="advanced-cnn">Advanced CNN</option>
42
+ </select>
43
+ </div>
44
+
45
+ <div style="margin-top: 1.5rem;">
46
+ <h3 style="margin-bottom: 0.5rem; font-size: 1rem;">Quick Presets</h3>
47
+ <div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 0.5rem;">
48
+ <button id="preset-high-privacy" style="padding: 0.5rem; text-align: center; background-color: #e3f2fd; border: none; border-radius: 4px; cursor: pointer;">
49
+ <div style="font-size: 1.2rem; margin-bottom: 0.2rem;">🔒</div>
50
+ <div style="font-weight: 500; margin-bottom: 0.2rem;">High Privacy</div>
51
+ <div style="font-size: 0.8rem; color: #666;">ε ≈ 1.2</div>
52
+ </button>
53
+ <button id="preset-balanced" style="padding: 0.5rem; text-align: center; background-color: #f1f8e9; border: none; border-radius: 4px; cursor: pointer;">
54
+ <div style="font-size: 1.2rem; margin-bottom: 0.2rem;">⚖️</div>
55
+ <div style="font-weight: 500; margin-bottom: 0.2rem;">Balanced</div>
56
+ <div style="font-size: 0.8rem; color: #666;">ε ≈ 3.0</div>
57
+ </button>
58
+ <button id="preset-high-utility" style="padding: 0.5rem; text-align: center; background-color: #fff8e1; border: none; border-radius: 4px; cursor: pointer;">
59
+ <div style="font-size: 1.2rem; margin-bottom: 0.2rem;">📈</div>
60
+ <div style="font-weight: 500; margin-bottom: 0.2rem;">High Utility</div>
61
+ <div style="font-size: 0.8rem; color: #666;">ε ≈ 8.0</div>
62
+ </button>
63
+ </div>
64
+ </div>
65
+ </div>
66
+
67
+ <!-- DP-SGD Parameters Panel -->
68
+ <div class="panel" style="margin-top: 1rem;">
69
+ <h2 class="panel-title">DP-SGD Parameters</h2>
70
+
71
+ <div class="parameter-control">
72
+ <label for="clipping-norm" class="parameter-label">
73
+ Clipping Norm (C)
74
+ <span class="tooltip">
75
+ <span class="tooltip-icon">?</span>
76
+ <span class="tooltip-text">Limits how much any single training example can affect the model update. Smaller values provide stronger privacy but can slow learning.</span>
77
+ </span>
78
+ </label>
79
+ <input type="range" id="clipping-norm" class="parameter-slider" min="0.1" max="5.0" step="0.1" value="1.0">
80
+ <div class="slider-display">
81
+ <span>0.1</span>
82
+ <span id="clipping-norm-value">1.0</span>
83
+ <span>5.0</span>
84
+ </div>
85
+ </div>
86
+
87
+ <div class="parameter-control">
88
+ <label for="noise-multiplier" class="parameter-label">
89
+ Noise Multiplier (σ)
90
+ <span class="tooltip">
91
+ <span class="tooltip-icon">?</span>
92
+ <span class="tooltip-text">Controls how much noise is added to protect privacy. Higher values increase privacy but may reduce accuracy.</span>
93
+ </span>
94
+ </label>
95
+ <input type="range" id="noise-multiplier" class="parameter-slider" min="0.1" max="5.0" step="0.1" value="1.0">
96
+ <div class="slider-display">
97
+ <span>0.1</span>
98
+ <span id="noise-multiplier-value">1.0</span>
99
+ <span>5.0</span>
100
+ </div>
101
+ </div>
102
+
103
+ <div class="parameter-control">
104
+ <label for="batch-size" class="parameter-label">
105
+ Batch Size
106
+ <span class="tooltip">
107
+ <span class="tooltip-icon">?</span>
108
+ <span class="tooltip-text">Number of examples processed in each training step. Affects both privacy accounting and training stability.</span>
109
+ </span>
110
+ </label>
111
+ <input type="range" id="batch-size" class="parameter-slider" min="16" max="512" step="16" value="64">
112
+ <div class="slider-display">
113
+ <span>16</span>
114
+ <span id="batch-size-value">64</span>
115
+ <span>512</span>
116
+ </div>
117
+ </div>
118
+
119
+ <div class="parameter-control">
120
+ <label for="learning-rate" class="parameter-label">
121
+ Learning Rate (η)
122
+ <span class="tooltip">
123
+ <span class="tooltip-icon">?</span>
124
+ <span class="tooltip-text">Controls how quickly model parameters update. For DP-SGD, often needs to be smaller than standard SGD.</span>
125
+ </span>
126
+ </label>
127
+ <input type="range" id="learning-rate" class="parameter-slider" min="0.001" max="0.1" step="0.001" value="0.01">
128
+ <div class="slider-display">
129
+ <span>0.001</span>
130
+ <span id="learning-rate-value">0.01</span>
131
+ <span>0.1</span>
132
+ </div>
133
+ </div>
134
+
135
+ <div class="parameter-control">
136
+ <label for="epochs" class="parameter-label">
137
+ Epochs
138
+ <span class="tooltip">
139
+ <span class="tooltip-icon">?</span>
140
+ <span class="tooltip-text">Number of complete passes through the dataset. More epochs improves learning but increases privacy budget consumption.</span>
141
+ </span>
142
+ </label>
143
+ <input type="range" id="epochs" class="parameter-slider" min="1" max="20" step="1" value="5">
144
+ <div class="slider-display">
145
+ <span>1</span>
146
+ <span id="epochs-value">5</span>
147
+ <span>20</span>
148
+ </div>
149
+ </div>
150
+
151
+ <div class="budget-display">
152
+ <h3 style="margin-top: 0; margin-bottom: 0.5rem;">
153
+ Estimated Privacy Budget (ε)
154
+ <span class="tooltip">
155
+ <span class="tooltip-icon">?</span>
156
+ <span class="tooltip-text">This is the estimated privacy loss from training with these parameters. Lower ε means stronger privacy guarantees.</span>
157
+ </span>
158
+ </h3>
159
+ <div style="display: flex; align-items: center; gap: 1rem;">
160
+ <div id="budget-value" style="font-size: 1.5rem; font-weight: bold; min-width: 60px;">2.47</div>
161
+ <div style="flex: 1;">
162
+ <div class="budget-bar">
163
+ <div id="budget-fill" class="budget-fill medium" style="width: 25%;"></div>
164
+ </div>
165
+ <div class="budget-scale">
166
+ <span>Stronger Privacy</span>
167
+ <span>Weaker Privacy</span>
168
+ </div>
169
+ </div>
170
+ </div>
171
+ </div>
172
+
173
+ <button id="train-button" class="control-button">
174
+ Run Training
175
+ </button>
176
+ </div>
177
+ </div>
178
+
179
+ <!-- Main Content - Visualizations and Results -->
180
+ <div class="lab-main">
181
+ <!-- Training Visualizer -->
182
+ <div class="panel">
183
+ <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 1rem;">
184
+ <h2 class="panel-title">Training Progress</h2>
185
+ <div class="tabs">
186
+ <div class="tab active" data-tab="training">Training Metrics</div>
187
+ <div class="tab" data-tab="gradients">Gradient Clipping</div>
188
+ <div class="tab" data-tab="privacy">Privacy Budget</div>
189
+ </div>
190
+ </div>
191
+
192
+ <div id="training-tab" class="tab-content active">
193
+ <div class="chart-container">
194
+ <canvas id="training-chart" class="chart"></canvas>
195
+ </div>
196
+
197
+ <div id="training-status" class="status-badge" style="display: none;">
198
+ <span class="pulse"></span>
199
+ <span style="font-weight: 500; color: #4caf50;">Training in progress</span>
200
+ <span style="margin-left: auto; font-weight: 500;">Epoch: <span id="current-epoch">1</span> / <span id="total-epochs">5</span></span>
201
+ </div>
202
+ </div>
203
+
204
+ <div id="gradients-tab" class="tab-content">
205
+ <div style="margin-bottom: 1rem;">
206
+ <h3 style="font-size: 1rem; margin-bottom: 0.5rem;">Gradient Clipping Visualization</h3>
207
+ <p style="font-size: 0.9rem; color: var(--text-secondary);">
208
+ The chart below shows a distribution of gradient norms before and after clipping.
209
+ The vertical red line indicates the clipping threshold.
210
+ <span class="tooltip">
211
+ <span class="tooltip-icon">?</span>
212
+ <span class="tooltip-text">Clipping ensures no single example has too much influence on model updates, which is essential for differential privacy.</span>
213
+ </span>
214
+ </p>
215
+ </div>
216
+
217
+ <div class="canvas-container">
218
+ <canvas id="gradient-canvas" width="600" height="300"></canvas>
219
+ </div>
220
+ </div>
221
+
222
+ <div id="privacy-tab" class="tab-content">
223
+ <div style="margin-bottom: 1rem;">
224
+ <h3 style="font-size: 1rem; margin-bottom: 0.5rem;">Privacy Budget Consumption</h3>
225
+ <p style="font-size: 0.9rem; color: var(--text-secondary);">
226
+ This chart shows how the privacy budget (ε) accumulates during training.
227
+ <span class="tooltip">
228
+ <span class="tooltip-icon">?</span>
229
+ <span class="tooltip-text">In differential privacy, we track the 'privacy budget' (ε) which represents the amount of privacy loss. Lower values mean stronger privacy guarantees.</span>
230
+ </span>
231
+ </p>
232
+ </div>
233
+
234
+ <div class="chart-container">
235
+ <canvas id="privacy-chart" class="chart"></canvas>
236
+ </div>
237
+ </div>
238
+ </div>
239
+
240
+ <!-- Results Panel -->
241
+ <div class="panel" style="margin-top: 1rem;">
242
+ <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 1rem;">
243
+ <h2 class="panel-title">Results</h2>
244
+ <div class="tabs">
245
+ <div class="tab active" data-tab="metrics">Final Metrics</div>
246
+ <div class="tab" data-tab="recommendations">Recommendations</div>
247
+ </div>
248
+ </div>
249
+
250
+ <!-- Initial no-results state -->
251
+ <div id="no-results" style="text-align: center; padding: 2rem 0;">
252
+ <p style="color: var(--text-secondary); margin-bottom: 1rem;">Run training to see results here</p>
253
+ <div style="font-size: 3rem; opacity: 0.5;">📊</div>
254
+ </div>
255
+
256
+ <!-- Results content (hidden initially) -->
257
+ <div id="results-content" style="display: none;">
258
+ <div id="metrics-tab" class="tab-content active">
259
+ <div class="metrics-grid">
260
+ <div class="metric-card">
261
+ <div id="accuracy-value" class="metric-value" style="color: var(--primary-color);">92.4%</div>
262
+ <div class="metric-label">
263
+ Accuracy
264
+ <span class="tooltip">
265
+ <span class="tooltip-icon">?</span>
266
+ <span class="tooltip-text">Model performance on test data. Higher values are better.</span>
267
+ </span>
268
+ </div>
269
+ </div>
270
+
271
+ <div class="metric-card">
272
+ <div id="loss-value" class="metric-value">0.283</div>
273
+ <div class="metric-label">
274
+ Loss
275
+ <span class="tooltip">
276
+ <span class="tooltip-icon">?</span>
277
+ <span class="tooltip-text">Final training loss. Lower values generally indicate better model fit.</span>
278
+ </span>
279
+ </div>
280
+ </div>
281
+
282
+ <div class="metric-card">
283
+ <div id="privacy-budget-value" class="metric-value" style="color: var(--accent-color);">ε = 2.1</div>
284
+ <div class="metric-label">
285
+ Privacy Budget
286
+ <span class="tooltip">
287
+ <span class="tooltip-icon">?</span>
288
+ <span class="tooltip-text">Final privacy loss (ε). Lower values mean stronger privacy guarantees.</span>
289
+ </span>
290
+ </div>
291
+ </div>
292
+
293
+ <div class="metric-card">
294
+ <div id="training-time-value" class="metric-value">3.7s</div>
295
+ <div class="metric-label">
296
+ Training Time
297
+ <span class="tooltip">
298
+ <span class="tooltip-icon">?</span>
299
+ <span class="tooltip-text">Total time spent on training, including privacy mechanisms.</span>
300
+ </span>
301
+ </div>
302
+ </div>
303
+ </div>
304
+
305
+ <div style="background-color: var(--background-off); border-radius: 4px; padding: 1rem; margin-top: 1rem;">
306
+ <h3 style="margin-top: 0; margin-bottom: 0.5rem; font-size: 1rem;">Privacy-Utility Trade-off</h3>
307
+ <div style="position: relative; height: 8px; background-color: #e0e0e0; border-radius: 4px; margin: 1.5rem 0;">
308
+ <div style="position: absolute; top: -20px; left: 92%; transform: translateX(-50%);">
309
+ <span style="font-weight: 500; font-size: 0.8rem; color: var(--secondary-color);">Utility</span>
310
+ </div>
311
+ <div style="position: absolute; top: -20px; right: 79%; transform: translateX(50%);">
312
+ <span style="font-weight: 500; font-size: 0.8rem; color: var(--primary-color);">Privacy</span>
313
+ </div>
314
+ </div>
315
+ <p id="tradeoff-explanation" style="font-size: 0.9rem; color: var(--text-secondary); margin-top: 1rem;">
316
+ This model achieved 92.4% accuracy with a privacy budget of ε=2.1. This is a good trade-off for most applications.
317
+ </p>
318
+ </div>
319
+ </div>
320
+
321
+ <div id="recommendations-tab" class="tab-content">
322
+ <h3 style="margin-top: 0; margin-bottom: 1rem; font-size: 1rem;">Recommendations</h3>
323
+ <ul class="recommendation-list">
324
+ <!-- Recommendations will be dynamically added here -->
325
+ </ul>
326
+ </div>
327
+ </div>
328
+ </div>
329
+ </div>
330
+ </div>
331
+ {% endblock %}
app/templates/learning.html ADDED
@@ -0,0 +1,261 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {% extends "base.html" %}
2
+
3
+ {% block title %}DP-SGD Explorer - Learning Hub{% endblock %}
4
+
5
+ {% block content %}
6
+ <h1 class="section-title">Learning Hub</h1>
7
+
8
+ <div class="learning-container">
9
+ <div class="learning-sidebar">
10
+ <h2 class="panel-title">DP-SGD Concepts</h2>
11
+ <ul class="learning-steps">
12
+ <li class="learning-step active" data-step="intro">Introduction to Differential Privacy</li>
13
+ <li class="learning-step" data-step="dp-concepts">Core DP Concepts</li>
14
+ <li class="learning-step" data-step="sgd-basics">SGD Refresher</li>
15
+ <li class="learning-step" data-step="dpsgd-intro">DP-SGD: Core Modifications</li>
16
+ <li class="learning-step" data-step="parameters">Hyperparameter Deep Dive</li>
17
+ <li class="learning-step" data-step="privacy-accounting">Privacy Accounting</li>
18
+ </ul>
19
+ </div>
20
+
21
+ <div class="learning-content">
22
+ <div id="intro-content" class="step-content active">
23
+ <h2>Introduction to Differential Privacy</h2>
24
+ <p>Differential Privacy (DP) is a mathematical framework that provides strong privacy guarantees when performing analyses on sensitive data. It ensures that the presence or absence of any single individual's data has a minimal effect on the output of an analysis.</p>
25
+
26
+ <h3>Why is Differential Privacy Important?</h3>
27
+ <p>Traditional anonymization techniques often fail to protect privacy. With enough auxiliary information, it's possible to re-identify individuals in supposedly "anonymized" datasets. Differential privacy addresses this by adding carefully calibrated noise to the analysis process.</p>
28
+
29
+ <div class="concept-highlight">
30
+ <h4>Key Insight</h4>
31
+ <p>Differential privacy creates plausible deniability. By adding controlled noise, it becomes mathematically impossible to confidently determine whether any individual's data was used in the analysis.</p>
32
+ </div>
33
+
34
+ <h3>The Privacy-Utility Trade-off</h3>
35
+ <p>There's an inherent trade-off between privacy and utility (accuracy) in DP. More privacy means more noise, which typically reduces accuracy. The challenge is finding the right balance for your specific application.</p>
36
+
37
+ <div class="concept-box">
38
+ <div class="box1">
39
+ <h4>Strong Privacy (Low ε)</h4>
40
+ <ul>
41
+ <li>More noise added</li>
42
+ <li>Lower accuracy</li>
43
+ <li>Better protection for sensitive data</li>
44
+ </ul>
45
+ </div>
46
+ <div class="box2">
47
+ <h4>Strong Utility (Higher ε)</h4>
48
+ <ul>
49
+ <li>Less noise added</li>
50
+ <li>Higher accuracy</li>
51
+ <li>Reduced privacy guarantees</li>
52
+ </ul>
53
+ </div>
54
+ </div>
55
+ </div>
56
+
57
+ <div id="dp-concepts-content" class="step-content">
58
+ <h2>Core Differential Privacy Concepts</h2>
59
+
60
+ <h3>The Formal Definition</h3>
61
+ <p>A mechanism M is (ε,δ)-differentially private if for all neighboring datasets D and D' (differing in one record), and for all possible outputs S:</p>
62
+ <div class="formula">
63
+ P(M(D) ∈ S) ≤ e^ε × P(M(D') ∈ S) + δ
64
+ </div>
65
+
66
+ <h3>Key Parameters</h3>
67
+ <p><strong>ε (epsilon)</strong>: The privacy budget. Lower values mean stronger privacy but typically lower utility.</p>
68
+ <p><strong>δ (delta)</strong>: The probability of the privacy guarantee being broken. Usually set very small (e.g., 10^-5).</p>
69
+
70
+ <h3>Differential Privacy Mechanisms</h3>
71
+ <p><strong>Laplace Mechanism</strong>: Adds noise from a Laplace distribution to numeric queries.</p>
72
+ <p><strong>Gaussian Mechanism</strong>: Adds noise from a Gaussian (normal) distribution. This is used in DP-SGD.</p>
73
+ <p><strong>Exponential Mechanism</strong>: Used for non-numeric outputs, selects an output based on a probability distribution.</p>
74
+
75
+ <h3>Privacy Accounting</h3>
76
+ <p>When you apply multiple differentially private operations, the privacy loss (ε) accumulates. This is known as composition.</p>
77
+ <p>Advanced composition theorems and privacy accountants help track the total privacy spend.</p>
78
+ </div>
79
+
80
+ <div id="sgd-basics-content" class="step-content">
81
+ <h2>Stochastic Gradient Descent Refresher</h2>
82
+
83
+ <h3>Standard SGD</h3>
84
+ <p>Stochastic Gradient Descent (SGD) is an optimization algorithm used to train machine learning models by iteratively updating parameters based on gradients computed from mini-batches of data.</p>
85
+
86
+ <h3>The Basic Update Rule</h3>
87
+ <p>The standard SGD update for a batch B is:</p>
88
+ <div class="formula">
89
+ θ ← θ - η∇L(θ; B)
90
+ </div>
91
+ <p>Where:</p>
92
+ <ul>
93
+ <li>θ represents the model parameters</li>
94
+ <li>η is the learning rate</li>
95
+ <li>∇L(θ; B) is the average gradient of the loss over the batch B</li>
96
+ </ul>
97
+
98
+ <h3>Privacy Concerns with Standard SGD</h3>
99
+ <p>Standard SGD can leak information about individual training examples through the gradients. For example:</p>
100
+ <ul>
101
+ <li>Gradients might be larger for outliers or unusual examples</li>
102
+ <li>Model memorization of sensitive data can be extracted through attacks</li>
103
+ <li>Gradient values can be used in reconstruction attacks</li>
104
+ </ul>
105
+
106
+ <p>These privacy concerns motivate the need for differentially private training methods.</p>
107
+ </div>
108
+
109
+ <div id="dpsgd-intro-content" class="step-content">
110
+ <h2>DP-SGD: Core Modifications</h2>
111
+
112
+ <h3>How DP-SGD Differs from Standard SGD</h3>
113
+ <p>Differentially Private SGD modifies standard SGD in two key ways:</p>
114
+
115
+ <div class="concept-box">
116
+ <div class="box1">
117
+ <h4>1. Per-Sample Gradient Clipping</h4>
118
+ <p>Compute gradients for each example individually, then clip their L2 norm to a threshold C.</p>
119
+ <p>This limits the influence of any single training example on the model update.</p>
120
+ </div>
121
+
122
+ <div class="box2">
123
+ <h4>2. Noise Addition</h4>
124
+ <p>Add Gaussian noise to the sum of clipped gradients before applying the update.</p>
125
+ <p>The noise scale is proportional to the clipping threshold and the noise multiplier.</p>
126
+ </div>
127
+ </div>
128
+
129
+ <h3>The DP-SGD Update Rule</h3>
130
+ <p>The DP-SGD update can be summarized as:</p>
131
+ <ol>
132
+ <li>Compute per-sample gradients: g<sub>i</sub> = ∇L(θ; x<sub>i</sub>)</li>
133
+ <li>Clip each gradient: g̃<sub>i</sub> = g<sub>i</sub> × min(1, C/||g<sub>i</sub>||<sub>2</sub>)</li>
134
+ <li>Add noise: ḡ = (1/|B|) × (∑g̃<sub>i</sub> + N(0, σ²C²I))</li>
135
+ <li>Update parameters: θ ← θ - η × ḡ</li>
136
+ </ol>
137
+
138
+ <p>Where:</p>
139
+ <ul>
140
+ <li>C is the clipping norm</li>
141
+ <li>σ is the noise multiplier</li>
142
+ <li>B is the batch</li>
143
+ </ul>
144
+ </div>
145
+
146
+ <div id="parameters-content" class="step-content">
147
+ <h2>Hyperparameter Deep Dive</h2>
148
+
149
+ <p>DP-SGD introduces several new hyperparameters that need to be tuned carefully:</p>
150
+
151
+ <h3>Clipping Norm (C)</h3>
152
+ <p>The maximum allowed L2 norm for any individual gradient.</p>
153
+ <ul>
154
+ <li><strong>Too small:</strong> Gradients are over-clipped, limiting learning</li>
155
+ <li><strong>Too large:</strong> Requires more noise to achieve the same privacy guarantee</li>
156
+ <li><strong>Typical range:</strong> 0.1 to 10.0, depending on the dataset and model</li>
157
+ </ul>
158
+
159
+ <h3>Noise Multiplier (σ)</h3>
160
+ <p>Controls the amount of noise added to the gradients.</p>
161
+ <ul>
162
+ <li><strong>Higher σ:</strong> Better privacy, worse utility</li>
163
+ <li><strong>Lower σ:</strong> Better utility, worse privacy</li>
164
+ <li><strong>Typical range:</strong> 0.5 to 2.0 for most practical applications</li>
165
+ </ul>
166
+
167
+ <h3>Batch Size</h3>
168
+ <p>Affects both training dynamics and privacy accounting.</p>
169
+ <ul>
170
+ <li><strong>Larger batches:</strong> Reduce variance from noise, but change sampling probability</li>
171
+ <li><strong>Smaller batches:</strong> More update steps, potentially consuming more privacy budget</li>
172
+ <li><strong>Typical range:</strong> 64 to 1024, larger than standard SGD</li>
173
+ </ul>
174
+
175
+ <h3>Learning Rate (η)</h3>
176
+ <p>May need adjustment compared to non-private training.</p>
177
+ <ul>
178
+ <li><strong>DP-SGD often requires:</strong> Lower learning rates or careful scheduling</li>
179
+ <li><strong>Reason:</strong> Added noise can destabilize training with high learning rates</li>
180
+ </ul>
181
+
182
+ <h3>Number of Epochs</h3>
183
+ <p>More epochs consume more privacy budget.</p>
184
+ <ul>
185
+ <li><strong>Trade-off:</strong> More training vs. privacy budget consumption</li>
186
+ <li><strong>Early stopping:</strong> Often beneficial for balancing accuracy and privacy</li>
187
+ </ul>
188
+ </div>
189
+
190
+ <div id="privacy-accounting-content" class="step-content">
191
+ <h2>Privacy Accounting</h2>
192
+
193
+ <h3>Tracking Privacy Budget</h3>
194
+ <p>Privacy accounting is the process of keeping track of the total privacy loss (ε) throughout training.</p>
195
+
196
+ <h3>Common Methods</h3>
197
+ <div style="display: flex; flex-direction: column; gap: 15px; margin: 15px 0;">
198
+ <div class="concept-highlight">
199
+ <h4>Moment Accountant</h4>
200
+ <p>Used in the original DP-SGD paper, provides tight bounds on the privacy loss.</p>
201
+ <p>Tracks the moments of the privacy loss random variable.</p>
202
+ </div>
203
+
204
+ <div class="concept-highlight">
205
+ <h4>Rényi Differential Privacy (RDP)</h4>
206
+ <p>Alternative accounting method based on Rényi divergence.</p>
207
+ <p>Often used in modern implementations like TensorFlow Privacy and Opacus.</p>
208
+ </div>
209
+
210
+ <div class="concept-highlight">
211
+ <h4>Analytical Gaussian Mechanism</h4>
212
+ <p>Simpler method for specific mechanisms like the Gaussian Mechanism.</p>
213
+ <p>Less tight bounds but easier to compute.</p>
214
+ </div>
215
+ </div>
216
+
217
+ <h3>Privacy Budget Allocation</h3>
218
+ <p>With a fixed privacy budget (ε), you must decide how to allocate it:</p>
219
+ <ul>
220
+ <li><strong>Fixed noise, variable epochs:</strong> Set noise level, train until budget is exhausted</li>
221
+ <li><strong>Fixed epochs, variable noise:</strong> Set desired epochs, calculate required noise</li>
222
+ <li><strong>Advanced techniques:</strong> Privacy filters, odometers, and adaptive mechanisms</li>
223
+ </ul>
224
+
225
+ <h3>Practical Implementation</h3>
226
+ <p>In practice, privacy accounting is handled by libraries like:</p>
227
+ <ul>
228
+ <li>TensorFlow Privacy</li>
229
+ <li>PyTorch Opacus</li>
230
+ <li>Diffprivlib (IBM)</li>
231
+ </ul>
232
+ </div>
233
+ </div>
234
+ </div>
235
+
236
+ {% endblock %}
237
+
238
+ {% block extra_scripts %}
239
+ <script>
240
+ document.addEventListener('DOMContentLoaded', () => {
241
+ const steps = document.querySelectorAll('.learning-step');
242
+ steps.forEach(step => {
243
+ step.addEventListener('click', () => {
244
+ // Remove active class from all steps
245
+ steps.forEach(s => s.classList.remove('active'));
246
+ // Add active class to clicked step
247
+ step.classList.add('active');
248
+
249
+ // Hide all content
250
+ document.querySelectorAll('.step-content').forEach(content => {
251
+ content.classList.remove('active');
252
+ });
253
+
254
+ // Show selected content
255
+ const stepName = step.getAttribute('data-step');
256
+ document.getElementById(`${stepName}-content`).classList.add('active');
257
+ });
258
+ });
259
+ });
260
+ </script>
261
+ {% endblock %}
app/training/__init__.py ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ """
2
+ Training module for DP-SGD Explorer.
3
+ Contains mock trainer and privacy calculator implementations.
4
+ """
app/training/__pycache__/__init__.cpython-38.pyc ADDED
Binary file (266 Bytes). View file
 
app/training/__pycache__/mock_trainer.cpython-38.pyc ADDED
Binary file (4.21 kB). View file
 
app/training/__pycache__/privacy_calculator.cpython-38.pyc ADDED
Binary file (3.39 kB). View file
 
app/training/mock_trainer.py ADDED
@@ -0,0 +1,152 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ import time
3
+ from typing import Dict, List, Any
4
+
5
+ class MockTrainer:
6
+ def __init__(self):
7
+ self.base_accuracy = 0.95 # Base accuracy for non-private training
8
+ self.base_loss = 0.15 # Base loss for non-private training
9
+
10
+ def train(self, params: Dict[str, Any]) -> Dict[str, Any]:
11
+ """
12
+ Simulate DP-SGD training with given parameters.
13
+
14
+ Args:
15
+ params: Dictionary containing training parameters:
16
+ - clipping_norm: float
17
+ - noise_multiplier: float
18
+ - batch_size: int
19
+ - learning_rate: float
20
+ - epochs: int
21
+
22
+ Returns:
23
+ Dictionary containing training results and metrics
24
+ """
25
+ # Extract parameters
26
+ clipping_norm = params['clipping_norm']
27
+ noise_multiplier = params['noise_multiplier']
28
+ batch_size = params['batch_size']
29
+ learning_rate = params['learning_rate']
30
+ epochs = params['epochs']
31
+
32
+ # Calculate privacy impact on performance
33
+ privacy_factor = self._calculate_privacy_factor(clipping_norm, noise_multiplier)
34
+
35
+ # Generate epoch-wise data
36
+ epochs_data = self._generate_epoch_data(epochs, privacy_factor)
37
+
38
+ # Calculate final metrics
39
+ final_metrics = self._calculate_final_metrics(epochs_data, privacy_factor)
40
+
41
+ # Generate recommendations
42
+ recommendations = self._generate_recommendations(params, final_metrics)
43
+
44
+ return {
45
+ 'epochs_data': epochs_data,
46
+ 'final_metrics': final_metrics,
47
+ 'recommendations': recommendations
48
+ }
49
+
50
+ def _calculate_privacy_factor(self, clipping_norm: float, noise_multiplier: float) -> float:
51
+ """Calculate how much privacy mechanisms affect model performance."""
52
+ # Higher noise and stricter clipping reduce performance
53
+ return 1.0 - (0.3 * noise_multiplier + 0.2 * (1.0 / clipping_norm))
54
+
55
+ def _generate_epoch_data(self, epochs: int, privacy_factor: float) -> List[Dict[str, float]]:
56
+ """Generate realistic training metrics for each epoch."""
57
+ epochs_data = []
58
+
59
+ # Base learning curve parameters
60
+ base_accuracy = self.base_accuracy * privacy_factor
61
+ base_loss = self.base_loss / privacy_factor
62
+
63
+ for epoch in range(1, epochs + 1):
64
+ # Simulate learning curve with some randomness
65
+ progress = epoch / epochs
66
+ noise = np.random.normal(0, 0.02) # Small random fluctuations
67
+
68
+ accuracy = base_accuracy * (0.7 + 0.3 * progress) + noise
69
+ loss = base_loss * (1.2 - 0.2 * progress) + noise
70
+
71
+ epochs_data.append({
72
+ 'epoch': epoch,
73
+ 'accuracy': max(0, min(1, accuracy)) * 100, # Convert to percentage
74
+ 'loss': max(0, loss)
75
+ })
76
+
77
+ return epochs_data
78
+
79
+ def _calculate_final_metrics(self, epochs_data: List[Dict[str, float]], privacy_factor: float) -> Dict[str, float]:
80
+ """Calculate final training metrics."""
81
+ final_epoch = epochs_data[-1]
82
+
83
+ # Add some randomness to training time based on batch size and epochs
84
+ base_time = 0.5 # Base time in seconds
85
+ time_factor = (1.0 / privacy_factor) * (1.0 + np.random.normal(0, 0.1))
86
+
87
+ return {
88
+ 'accuracy': final_epoch['accuracy'],
89
+ 'loss': final_epoch['loss'],
90
+ 'training_time': base_time * time_factor
91
+ }
92
+
93
+ def _generate_recommendations(self, params: Dict[str, Any], metrics: Dict[str, float]) -> List[Dict[str, str]]:
94
+ """Generate recommendations based on training results."""
95
+ recommendations = []
96
+
97
+ # Check clipping norm
98
+ if params['clipping_norm'] < 0.5:
99
+ recommendations.append({
100
+ 'icon': '⚠️',
101
+ 'text': 'Clipping norm is very low. This might slow down learning.'
102
+ })
103
+ elif params['clipping_norm'] > 2.0:
104
+ recommendations.append({
105
+ 'icon': '🔒',
106
+ 'text': 'Consider reducing clipping norm for stronger privacy guarantees.'
107
+ })
108
+
109
+ # Check noise multiplier
110
+ if params['noise_multiplier'] < 0.5:
111
+ recommendations.append({
112
+ 'icon': '🔒',
113
+ 'text': 'Noise multiplier is low. Consider increasing it for better privacy.'
114
+ })
115
+ elif params['noise_multiplier'] > 2.0:
116
+ recommendations.append({
117
+ 'icon': '⚠️',
118
+ 'text': 'High noise multiplier might significantly impact model accuracy.'
119
+ })
120
+
121
+ # Check batch size
122
+ if params['batch_size'] < 64:
123
+ recommendations.append({
124
+ 'icon': '⚡',
125
+ 'text': 'Small batch size might lead to noisy updates. Consider increasing it.'
126
+ })
127
+ elif params['batch_size'] > 256:
128
+ recommendations.append({
129
+ 'icon': '🔍',
130
+ 'text': 'Large batch size might reduce model generalization.'
131
+ })
132
+
133
+ # Check learning rate
134
+ if params['learning_rate'] > 0.05:
135
+ recommendations.append({
136
+ 'icon': '⚠️',
137
+ 'text': 'High learning rate might destabilize training with DP-SGD.'
138
+ })
139
+ elif params['learning_rate'] < 0.001:
140
+ recommendations.append({
141
+ 'icon': '⏳',
142
+ 'text': 'Very low learning rate might slow down convergence.'
143
+ })
144
+
145
+ # Check final metrics
146
+ if metrics['accuracy'] < 80:
147
+ recommendations.append({
148
+ 'icon': '📉',
149
+ 'text': 'Model accuracy is low. Consider adjusting privacy parameters.'
150
+ })
151
+
152
+ return recommendations
app/training/privacy_calculator.py ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ from typing import Dict, Any
3
+
4
+ class PrivacyCalculator:
5
+ def __init__(self):
6
+ self.delta = 1e-5 # Standard delta value for DP guarantees
7
+
8
+ def calculate_epsilon(self, params: Dict[str, Any]) -> float:
9
+ """
10
+ Calculate the privacy budget (ε) using the moment accountant method.
11
+
12
+ Args:
13
+ params: Dictionary containing training parameters:
14
+ - clipping_norm: float
15
+ - noise_multiplier: float
16
+ - batch_size: int
17
+ - epochs: int
18
+
19
+ Returns:
20
+ The calculated privacy budget (ε)
21
+ """
22
+ # Extract parameters
23
+ clipping_norm = params['clipping_norm']
24
+ noise_multiplier = params['noise_multiplier']
25
+ batch_size = params['batch_size']
26
+ epochs = params['epochs']
27
+
28
+ # Calculate sampling rate (assuming MNIST dataset size of 60,000)
29
+ sampling_rate = batch_size / 60000
30
+
31
+ # Calculate number of steps
32
+ steps = epochs * (1 / sampling_rate)
33
+
34
+ # Calculate moments for different orders
35
+ orders = [1.25, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0]
36
+ moments = [self._calculate_moment(order, sampling_rate, noise_multiplier) for order in orders]
37
+
38
+ # Find the minimum ε that satisfies all moment bounds
39
+ epsilon = float('inf')
40
+ for moment in moments:
41
+ # Convert moment bound to (ε,δ)-DP bound
42
+ moment_epsilon = moment + np.log(1/self.delta) / (orders[0] - 1)
43
+ epsilon = min(epsilon, moment_epsilon)
44
+
45
+ # Add some randomness to make it more realistic
46
+ epsilon *= (1 + np.random.normal(0, 0.05))
47
+
48
+ return max(0.1, epsilon) # Ensure ε is at least 0.1
49
+
50
+ def _calculate_moment(self, order: float, sampling_rate: float, noise_multiplier: float) -> float:
51
+ """
52
+ Calculate the moment bound for a given order.
53
+
54
+ Args:
55
+ order: The moment order
56
+ sampling_rate: The probability of sampling each example
57
+ noise_multiplier: The noise multiplier used in DP-SGD
58
+
59
+ Returns:
60
+ The calculated moment bound
61
+ """
62
+ # Simplified moment calculation based on the moment accountant method
63
+ # This is a simplified version that captures the key relationships
64
+ c = np.sqrt(2 * np.log(1.25 / self.delta))
65
+ moment = (order * sampling_rate * c) / noise_multiplier
66
+
67
+ # Add some non-linear effects
68
+ moment *= (1 + 0.1 * np.sin(order))
69
+
70
+ return moment
71
+
72
+ def calculate_optimal_noise(self, target_epsilon: float, params: Dict[str, Any]) -> float:
73
+ """
74
+ Calculate the optimal noise multiplier for a target privacy budget.
75
+
76
+ Args:
77
+ target_epsilon: The desired privacy budget
78
+ params: Dictionary containing training parameters:
79
+ - clipping_norm: float
80
+ - batch_size: int
81
+ - epochs: int
82
+
83
+ Returns:
84
+ The calculated optimal noise multiplier
85
+ """
86
+ # Extract parameters
87
+ clipping_norm = params['clipping_norm']
88
+ batch_size = params['batch_size']
89
+ epochs = params['epochs']
90
+
91
+ # Calculate sampling rate
92
+ sampling_rate = batch_size / 60000
93
+
94
+ # Calculate number of steps
95
+ steps = epochs * (1 / sampling_rate)
96
+
97
+ # Calculate optimal noise using the analytical Gaussian mechanism
98
+ c = np.sqrt(2 * np.log(1.25 / self.delta))
99
+ optimal_noise = (c * sampling_rate * np.sqrt(steps)) / target_epsilon
100
+
101
+ # Add some randomness to make it more realistic
102
+ optimal_noise *= (1 + np.random.normal(0, 0.05))
103
+
104
+ return max(0.1, optimal_noise) # Ensure noise is at least 0.1
requirements.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ flask==3.0.0
2
+ flask-cors==4.0.0
3
+ python-dotenv==1.0.0
4
+ gunicorn==21.2.0
5
+ numpy==1.24.3
run.py ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ from app import create_app
2
+
3
+ app = create_app()
4
+
5
+ if __name__ == '__main__':
6
+ app.run(debug=True)
start_server.sh ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+
3
+ # Colors for output
4
+ GREEN='\033[0;32m'
5
+ YELLOW='\033[1;33m'
6
+ RED='\033[0;31m'
7
+ NC='\033[0m' # No Color
8
+
9
+ # Function to print colored messages
10
+ print_message() {
11
+ echo -e "${2}${1}${NC}"
12
+ }
13
+
14
+ # Check if Python 3 is installed
15
+ if ! command -v python3 &> /dev/null; then
16
+ print_message "Python 3 is not installed. Please install Python 3 first." "$RED"
17
+ exit 1
18
+ fi
19
+
20
+ # Check Python version
21
+ PYTHON_VERSION=$(python3 -c 'import sys; print(".".join(map(str, sys.version_info[:2])))')
22
+ print_message "Found Python version: $PYTHON_VERSION" "$GREEN"
23
+
24
+ # Create virtual environment if it doesn't exist
25
+ if [ ! -d ".venv" ]; then
26
+ print_message "Creating virtual environment..." "$YELLOW"
27
+ python3 -m venv .venv
28
+ if [ $? -ne 0 ]; then
29
+ print_message "Failed to create virtual environment. Please install python3-venv package." "$RED"
30
+ exit 1
31
+ fi
32
+ fi
33
+
34
+ # Activate virtual environment
35
+ print_message "Activating virtual environment..." "$GREEN"
36
+ source .venv/bin/activate
37
+ if [ $? -ne 0 ]; then
38
+ print_message "Failed to activate virtual environment." "$RED"
39
+ exit 1
40
+ fi
41
+
42
+ # Install or upgrade pip
43
+ print_message "Upgrading pip..." "$YELLOW"
44
+ python3 -m pip install --upgrade pip
45
+
46
+ # Install requirements
47
+ print_message "Installing dependencies..." "$YELLOW"
48
+ pip install -r requirements.txt
49
+ if [ $? -ne 0 ]; then
50
+ print_message "Failed to install dependencies." "$RED"
51
+ exit 1
52
+ fi
53
+
54
+ # Start the Flask application
55
+ print_message "\n=== DP-SGD Explorer Backend ===" "$GREEN"
56
+ print_message "Starting server..." "$GREEN"
57
+ print_message "The application will be available at http://127.0.0.1:5000\n" "$YELLOW"
58
+
59
+ # Set Python path and start server
60
+ PYTHONPATH=. python3 run.py