rxb0506 commited on
Commit
10f22d7
·
verified ·
1 Parent(s): a592c53

Upload 2 files

Browse files
Files changed (2) hide show
  1. app.py +743 -0
  2. requirements.txt +5 -0
app.py ADDED
@@ -0,0 +1,743 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import numpy as np
3
+ import matplotlib.pyplot as plt
4
+ from tqdm.auto import tqdm
5
+ import pandas as pd
6
+ from enum import Enum
7
+ from datetime import datetime, timedelta
8
+
9
+
10
+ plt.style.use('seaborn-v0_8-whitegrid')
11
+
12
+
13
+ # Define content modality types
14
+ class ContentModality(Enum):
15
+ TEXT = 1
16
+ IMAGE = 2
17
+ AUDIO = 3
18
+ VIDEO = 4
19
+ INTERACTIVE = 5
20
+ MIXED = 6
21
+
22
+
23
+ # Define columns for FSRS algorithm (from app.py)
24
+ columns = ["difficulty", "stability", "retrievability", "delta_t",
25
+ "reps", "lapses", "last_date", "due", "ivl", "cost", "rand"]
26
+ col = {key: i for i, key in enumerate(columns)}
27
+
28
+ first_rating_prob = np.array([0.15, 0.2, 0.6, 0.05])
29
+
30
+
31
+ def moving_average(data, window_size=7):
32
+ """Calculate moving average with the specified window size"""
33
+ weights = np.ones(window_size) / window_size
34
+ return np.convolve(data, weights, mode='valid')
35
+
36
+
37
+ # Spaced Repetition Simulation (from app.py)
38
+ def simulate_fsrs(w, request_retention=0.9, deck_size=10000, learn_span=100,
39
+ max_cost_perday=200, max_ivl=36500, recall_cost=10,
40
+ forget_cost=30, learn_cost=10, progress=None):
41
+ card_table = np.zeros((len(columns), deck_size))
42
+ card_table[col["due"]] = learn_span
43
+ card_table[col["difficulty"]] = 1e-10
44
+ card_table[col["stability"]] = 1e-10
45
+
46
+ review_cnt_per_day = np.zeros(learn_span)
47
+ learn_cnt_per_day = np.zeros(learn_span)
48
+ memorized_cnt_per_day = np.zeros(learn_span)
49
+
50
+ def stability_after_success(s, r, d, response):
51
+ hard_penalty = np.where(response == 1, w[15], 1)
52
+ easy_bonus = np.where(response == 3, w[16], 1)
53
+ return s * (1 + np.exp(w[8]) * (11 - d) * np.power(s, -w[9]) * (
54
+ np.exp((1 - r) * w[10]) - 1) * hard_penalty * easy_bonus)
55
+
56
+ def stability_after_failure(s, r, d):
57
+ return np.maximum(0.1, np.minimum(
58
+ w[11] * np.power(d, -w[12]) * (np.power(s + 1, w[13]) - 1) * np.exp((1 - r) * w[14]), s))
59
+
60
+ iterator = tqdm(range(learn_span)) if progress is None else range(learn_span)
61
+ for today in iterator:
62
+ if progress is not None:
63
+ progress((today / learn_span) * 0.5) # Use first half of progress for FSRS
64
+
65
+ has_learned = card_table[col["stability"]] > 1e-10
66
+ card_table[col["delta_t"]][has_learned] = today - \
67
+ card_table[col["last_date"]][has_learned]
68
+ card_table[col["retrievability"]][has_learned] = np.power(
69
+ 1 + card_table[col["delta_t"]][has_learned] / (9 * card_table[col["stability"]][has_learned]), -1)
70
+
71
+ card_table[col["cost"]] = 0
72
+ need_review = card_table[col["due"]] <= today
73
+ card_table[col["rand"]][need_review] = np.random.rand(
74
+ np.sum(need_review))
75
+ forget = card_table[col["rand"]] > card_table[col["retrievability"]]
76
+ card_table[col["cost"]][need_review & forget] = forget_cost
77
+ card_table[col["cost"]][need_review & ~forget] = recall_cost
78
+ true_review = need_review & (
79
+ np.cumsum(card_table[col["cost"]]) <= max_cost_perday)
80
+ card_table[col["last_date"]][true_review] = today
81
+
82
+ card_table[col["lapses"]][true_review & forget] += 1
83
+ card_table[col["reps"]][true_review & ~forget] += 1
84
+
85
+ card_table[col["stability"]][true_review & forget] = stability_after_failure(
86
+ card_table[col["stability"]][true_review & forget], card_table[col["retrievability"]][true_review & forget],
87
+ card_table[col["difficulty"]][true_review & forget])
88
+
89
+ review_ratings = np.random.choice([1, 2, 3], np.sum(true_review & ~forget), p=[0.3, 0.6, 0.1])
90
+ card_table[col["stability"]][true_review & ~forget] = stability_after_success(
91
+ card_table[col["stability"]][true_review & ~forget],
92
+ card_table[col["retrievability"]][true_review & ~forget],
93
+ card_table[col["difficulty"]][true_review & ~forget], review_ratings)
94
+
95
+ card_table[col["difficulty"]][true_review & forget] = np.clip(
96
+ card_table[col["difficulty"]][true_review & forget] + 2 * w[6], 1, 10)
97
+
98
+ need_learn = card_table[col["due"]] == learn_span
99
+ card_table[col["cost"]][need_learn] = learn_cost
100
+ true_learn = need_learn & (
101
+ np.cumsum(card_table[col["cost"]]) <= max_cost_perday)
102
+ card_table[col["last_date"]][true_learn] = today
103
+ first_ratings = np.random.choice(4, np.sum(true_learn), p=first_rating_prob)
104
+ card_table[col["stability"]][true_learn] = np.choose(
105
+ first_ratings, w[:4])
106
+ card_table[col["difficulty"]][true_learn] = w[4] - \
107
+ w[5] * (first_ratings - 3)
108
+
109
+ card_table[col["ivl"]][true_review | true_learn] = np.clip(np.round(
110
+ 9 * card_table[col["stability"]][true_review | true_learn] * (1 / request_retention - 1), 0), 1, max_ivl)
111
+ card_table[col["due"]][true_review | true_learn] = today + \
112
+ card_table[col["ivl"]][true_review | true_learn]
113
+
114
+ review_cnt_per_day[today] = np.sum(true_review)
115
+ learn_cnt_per_day[today] = np.sum(true_learn)
116
+ memorized_cnt_per_day[today] = card_table[col["retrievability"]].sum()
117
+
118
+ return card_table, review_cnt_per_day, learn_cnt_per_day, memorized_cnt_per_day
119
+
120
+
121
+ # Multimodal Learning Simulation
122
+ def simulate_multimodal_srs(
123
+ baseline_retention=0.9,
124
+ modality_weights=[1.0, 1.2, 0.9, 1.3, 1.4, 1.1],
125
+ learning_days=100,
126
+ cards_per_day=20,
127
+ initial_ease=2.5,
128
+ max_ease=3.5,
129
+ min_ease=1.3,
130
+ learning_rate=0.05,
131
+ max_cost_perday=200,
132
+ progress=None
133
+ ):
134
+ """Simulate the adaptive multimodal spaced repetition system over time."""
135
+
136
+ # Initialize tracking arrays
137
+ total_cards = min(cards_per_day * learning_days, 10000) # Cap to reasonable size
138
+ reviews_per_day = np.zeros(learning_days)
139
+ retention_per_day = np.zeros(learning_days)
140
+ modality_usage = {mod: np.zeros(learning_days) for mod in ContentModality}
141
+ modality_success = {mod: np.zeros(learning_days) for mod in ContentModality}
142
+
143
+ # Card state tracking
144
+ card_ease = np.ones(total_cards) * initial_ease
145
+ card_interval = np.ones(total_cards)
146
+ card_due_day = np.zeros(total_cards)
147
+ card_reps = np.zeros(total_cards)
148
+
149
+ # When each card is introduced
150
+ card_intro_day = np.zeros(total_cards)
151
+ for i in range(total_cards):
152
+ card_intro_day[i] = i // cards_per_day
153
+
154
+ # System's belief about user preferences (starts neutral)
155
+ believed_modality_preference = np.ones(len(ContentModality))
156
+
157
+ # User's true preferences (based on input weights)
158
+ true_modality_preference = np.array(modality_weights)
159
+
160
+ # Run the simulation
161
+ iterator = tqdm(range(learning_days)) if progress is None else range(learning_days)
162
+ for day in iterator:
163
+ if progress is not None:
164
+ progress(0.5 + (day / learning_days) * 0.5) # Use second half of progress for multimodal
165
+
166
+ # Find cards due today
167
+ due_mask = (card_due_day <= day) & (card_intro_day <= day)
168
+ due_cards = np.where(due_mask)[0]
169
+
170
+ # Track daily cost to stay within max_cost_perday
171
+ daily_cost = 0
172
+
173
+ reviews_today = 0
174
+ correct_today = 0
175
+
176
+ # Randomize review order
177
+ if len(due_cards) > 0:
178
+ np.random.shuffle(due_cards)
179
+
180
+ # Process each due card
181
+ for card_id in due_cards:
182
+ # Check if we still have time budget
183
+ if daily_cost >= max_cost_perday:
184
+ break
185
+
186
+ reviews_today += 1
187
+
188
+ # Choose modality based on current beliefs
189
+ modality_idx = np.random.choice(
190
+ len(ContentModality),
191
+ p=believed_modality_preference / believed_modality_preference.sum()
192
+ )
193
+ modality = ContentModality(modality_idx + 1)
194
+
195
+ # Track modality usage
196
+ modality_usage[modality][day] += 1
197
+
198
+ # Calculate recall probability based on interval and modality
199
+ recall_prob = np.power(1 + card_interval[card_id] / (9 * card_ease[card_id]), -1)
200
+ mod_factor = true_modality_preference[modality.value - 1]
201
+ recall_prob = min(0.99, recall_prob * mod_factor)
202
+
203
+ # Simulate if user remembers card
204
+ remembered = np.random.random() < recall_prob
205
+
206
+ if remembered:
207
+ # Success - increase ease factor
208
+ card_ease[card_id] = min(max_ease, card_ease[card_id] + 0.1)
209
+ correct_today += 1
210
+ modality_success[modality][day] += 1
211
+ daily_cost += 10 # Review cost
212
+
213
+ # Update interval using SM-2 algorithm with modality
214
+ if card_reps[card_id] == 0:
215
+ card_interval[card_id] = 1
216
+ elif card_reps[card_id] == 1:
217
+ card_interval[card_id] = 6
218
+ else:
219
+ card_interval[card_id] = card_interval[card_id] * card_ease[card_id]
220
+
221
+ card_reps[card_id] += 1
222
+ else:
223
+ # Failure - decrease ease factor
224
+ card_ease[card_id] = max(min_ease, card_ease[card_id] - 0.2)
225
+ card_interval[card_id] = 1
226
+ card_reps[card_id] = 0
227
+ daily_cost += 30 # Relearn cost
228
+
229
+ # Update due date
230
+ card_due_day[card_id] = day + max(1, int(card_interval[card_id]))
231
+
232
+ # Update belief about modality effectiveness
233
+ update_vector = np.zeros(len(ContentModality))
234
+ update_vector[modality.value - 1] = learning_rate * (1 if remembered else -1)
235
+ believed_modality_preference += update_vector
236
+
237
+ # Ensure beliefs are positive
238
+ believed_modality_preference = np.maximum(0.1, believed_modality_preference)
239
+
240
+ # Add new cards if we have budget left
241
+ new_cards_today = 0
242
+ for i in range(total_cards):
243
+ if card_intro_day[i] == day:
244
+ if daily_cost + 10 <= max_cost_perday: # Check if we can afford to learn
245
+ daily_cost += 10 # Learn cost
246
+ new_cards_today += 1
247
+ else:
248
+ # Postpone introduction if no time left today
249
+ card_intro_day[i] += 1
250
+
251
+ # Calculate daily stats
252
+ if reviews_today > 0:
253
+ retention_per_day[day] = correct_today / reviews_today
254
+ else:
255
+ retention_per_day[day] = 0
256
+
257
+ reviews_per_day[day] = reviews_today
258
+
259
+ # Calculate effectiveness per modality
260
+ modality_effectiveness = {}
261
+ for mod in ContentModality:
262
+ usage = modality_usage[mod]
263
+ success = modality_success[mod]
264
+
265
+ effectiveness = np.zeros(learning_days)
266
+ for i in range(learning_days):
267
+ if usage[i] > 0:
268
+ effectiveness[i] = success[i] / usage[i]
269
+
270
+ modality_effectiveness[mod] = effectiveness
271
+
272
+ # Calculate average retention rate at the end
273
+ final_retention = np.mean(retention_per_day[max(0, learning_days - 10):])
274
+
275
+ return {
276
+ 'reviews_per_day': reviews_per_day,
277
+ 'retention_per_day': retention_per_day,
278
+ 'modality_usage': modality_usage,
279
+ 'modality_effectiveness': modality_effectiveness,
280
+ 'final_modality_beliefs': believed_modality_preference,
281
+ 'true_modality_preference': true_modality_preference,
282
+ 'final_retention': final_retention
283
+ }
284
+
285
+
286
+ def run_combined_simulation(
287
+ # FSRS parameters
288
+ fsrs_weights,
289
+ retrievability,
290
+ stability,
291
+ difficulty,
292
+ # Multimodal parameters
293
+ text_weight,
294
+ image_weight,
295
+ audio_weight,
296
+ video_weight,
297
+ interactive_weight,
298
+ mixed_weight,
299
+ # Shared parameters
300
+ target_retention,
301
+ learning_time,
302
+ learning_days,
303
+ deck_size,
304
+ max_ivl,
305
+ recall_cost,
306
+ forget_cost,
307
+ learn_cost,
308
+ learning_rate,
309
+ progress=gr.Progress()
310
+ ):
311
+ """Run both simulations and generate combined output"""
312
+ np.random.seed(42) # For reproducibility
313
+
314
+ # Parse FSRS weights
315
+ weights_str = ",".join([fsrs_weights, retrievability, stability, difficulty]).replace('[', '').replace(']', '')
316
+ w = list(map(lambda x: float(x.strip()), weights_str.split(',')))
317
+
318
+ # Calculate max cost per day in seconds
319
+ max_cost_perday = int(learning_time) * 60
320
+
321
+ # Run FSRS simulation
322
+ (card_table,
323
+ fsrs_review_cnt,
324
+ fsrs_learn_cnt,
325
+ fsrs_memorized_cnt) = simulate_fsrs(w,
326
+ request_retention=float(target_retention),
327
+ deck_size=int(deck_size),
328
+ learn_span=int(learning_days),
329
+ max_cost_perday=max_cost_perday,
330
+ max_ivl=int(max_ivl),
331
+ recall_cost=int(recall_cost),
332
+ forget_cost=int(forget_cost),
333
+ learn_cost=int(learn_cost),
334
+ progress=progress)
335
+
336
+ # Run multimodal simulation
337
+ modality_weights = [
338
+ float(text_weight),
339
+ float(image_weight),
340
+ float(audio_weight),
341
+ float(video_weight),
342
+ float(interactive_weight),
343
+ float(mixed_weight)
344
+ ]
345
+
346
+ multi_results = simulate_multimodal_srs(
347
+ baseline_retention=float(target_retention),
348
+ modality_weights=modality_weights,
349
+ learning_days=int(learning_days),
350
+ cards_per_day=int(deck_size) // int(learning_days),
351
+ initial_ease=2.5,
352
+ learning_rate=float(learning_rate),
353
+ max_cost_perday=max_cost_perday,
354
+ progress=progress
355
+ )
356
+
357
+ # Create visualization plots
358
+ plots = create_combined_plots(
359
+ fsrs_review_cnt,
360
+ fsrs_learn_cnt,
361
+ fsrs_memorized_cnt,
362
+ multi_results,
363
+ int(learning_days)
364
+ )
365
+
366
+ # Generate recommendations
367
+ recommendations = generate_recommendations(
368
+ fsrs_review_cnt,
369
+ multi_results,
370
+ int(learning_days),
371
+ target_retention,
372
+ modality_weights
373
+ )
374
+
375
+ return plots + [recommendations]
376
+
377
+
378
+ def create_combined_plots(fsrs_review_cnt, fsrs_learn_cnt, fsrs_memorized_cnt, multi_results, learning_days):
379
+ """Create visualization plots from both simulation results"""
380
+
381
+ # Ensure smooth window size is reasonable
382
+ smooth_window = min(7, learning_days // 10)
383
+ if smooth_window < 2:
384
+ smooth_window = 2
385
+
386
+ # Plot 1: Review Counts Comparison
387
+ fig1 = plt.figure(figsize=(10, 6))
388
+ ax = fig1.add_subplot(111)
389
+
390
+ if len(fsrs_review_cnt) > smooth_window:
391
+ ax.plot(moving_average(fsrs_review_cnt, smooth_window), 'b-',
392
+ label='Standard SRS Reviews')
393
+ else:
394
+ ax.plot(fsrs_review_cnt, 'b-', label='Standard SRS Reviews')
395
+
396
+ if len(multi_results['reviews_per_day']) > smooth_window:
397
+ ax.plot(moving_average(multi_results['reviews_per_day'], smooth_window), 'r-',
398
+ label='Multimodal SRS Reviews')
399
+ else:
400
+ ax.plot(multi_results['reviews_per_day'], 'r-', label='Multimodal SRS Reviews')
401
+
402
+ ax.set_xlabel('Day')
403
+ ax.set_ylabel('Number of Reviews')
404
+ ax.set_title('Review Counts: Standard vs Multimodal SRS')
405
+ ax.legend()
406
+
407
+ # Plot 2: Retention & Memorization
408
+ fig2 = plt.figure(figsize=(10, 6))
409
+ ax1 = fig2.add_subplot(111)
410
+
411
+ if len(multi_results['retention_per_day']) > smooth_window:
412
+ ax1.plot(moving_average(multi_results['retention_per_day'], smooth_window), 'g-',
413
+ label='Multimodal Retention Rate')
414
+ else:
415
+ ax1.plot(multi_results['retention_per_day'], 'g-', label='Multimodal Retention Rate')
416
+
417
+ ax1.set_xlabel('Day')
418
+ ax1.set_ylabel('Retention Rate')
419
+ ax1.set_ylim(0, 1.0)
420
+ ax1.legend(loc='upper left')
421
+
422
+ ax2 = ax1.twinx()
423
+ ax2.plot(fsrs_memorized_cnt, 'b--', label='Standard SRS Cumulative Memorized')
424
+ ax2.set_ylabel('Cumulative Memorized Items')
425
+ ax2.legend(loc='upper right')
426
+
427
+ ax1.set_title('Retention Rate & Memorized Items')
428
+
429
+ # Plot 3: Modality Effectiveness
430
+ fig3 = plt.figure(figsize=(10, 6))
431
+ ax = fig3.add_subplot(111)
432
+
433
+ for mod in ContentModality:
434
+ effectiveness = multi_results['modality_effectiveness'][mod]
435
+ if len(effectiveness) > smooth_window:
436
+ smooth_eff = moving_average(effectiveness, smooth_window)
437
+ ax.plot(range(len(smooth_eff)), smooth_eff, label=mod.name)
438
+ else:
439
+ ax.plot(effectiveness, label=mod.name)
440
+
441
+ ax.set_xlabel('Day')
442
+ ax.set_ylabel('Success Rate')
443
+ ax.set_ylim(0, 1.0)
444
+ ax.set_title('Modality Effectiveness Over Time')
445
+ ax.legend()
446
+
447
+ # Plot 4: Modality Usage Over Time
448
+ fig4 = plt.figure(figsize=(10, 6))
449
+ ax = fig4.add_subplot(111)
450
+
451
+ modality_data = []
452
+ mod_labels = []
453
+
454
+ for mod in ContentModality:
455
+ usage_data = multi_results['modality_usage'][mod]
456
+ if len(usage_data) > smooth_window:
457
+ modality_data.append(moving_average(usage_data, smooth_window))
458
+ else:
459
+ modality_data.append(usage_data)
460
+ mod_labels.append(mod.name)
461
+
462
+ modality_data = np.array(modality_data)
463
+
464
+ # Create stacked area plot
465
+ x = range(len(modality_data[0]))
466
+ ax.stackplot(x, modality_data, labels=mod_labels)
467
+
468
+ ax.set_xlabel('Day')
469
+ ax.set_ylabel('Number of Reviews')
470
+ ax.set_title('Modality Distribution Over Time')
471
+ ax.legend()
472
+
473
+ return [fig1, fig2, fig3, fig4]
474
+
475
+
476
+ def generate_recommendations(fsrs_review_cnt, multi_results, learning_days, target_retention, modality_weights):
477
+ """Generate personalized recommendations based on simulation results"""
478
+
479
+ # Find most effective modalities
480
+ modality_avg_effectiveness = {}
481
+ for mod in ContentModality:
482
+ effectiveness = multi_results['modality_effectiveness'][mod]
483
+ # Calculate average of last 25% of days to get mature effectiveness
484
+ start_idx = max(0, int(learning_days * 0.75))
485
+ avg_eff = np.mean(effectiveness[start_idx:]) if len(effectiveness) > start_idx else np.mean(effectiveness)
486
+ modality_avg_effectiveness[mod] = avg_eff
487
+
488
+ # Sort modalities by effectiveness
489
+ sorted_modalities = sorted(modality_avg_effectiveness.items(), key=lambda x: x[1], reverse=True)
490
+
491
+ # Analyze review patterns
492
+ avg_reviews_std = np.mean(fsrs_review_cnt)
493
+ peak_reviews_std = np.max(fsrs_review_cnt)
494
+ avg_reviews_multi = np.mean(multi_results['reviews_per_day'])
495
+
496
+ # Calculate efficiency gain
497
+ std_retention = np.mean(fsrs_review_cnt[-10:]) / np.mean(fsrs_review_cnt[:10]) if len(fsrs_review_cnt) > 10 else 1
498
+ multi_retention = multi_results['final_retention']
499
+
500
+ efficiency_gain = (multi_retention / float(target_retention)) / (avg_reviews_multi / avg_reviews_std)
501
+
502
+ # Generate recommendations
503
+ top_modalities = [mod.name for mod, _ in sorted_modalities[:3]]
504
+
505
+ # Dynamic time period calculations based on learning_days
506
+ total_period = learning_days
507
+
508
+ # Scale intervals based on learning period length
509
+ if total_period <= 30: # Short learning period
510
+ initial_interval = (1, 1)
511
+ second_interval = (1, 2)
512
+ third_interval = (2, 3)
513
+ long_term_start = "Week 2+"
514
+ elif total_period <= 90: # Medium learning period
515
+ initial_interval = (1, 2)
516
+ second_interval = (2, 4)
517
+ third_interval = (4, 7)
518
+ long_term_start = "Week 4+"
519
+ else: # Long learning period
520
+ initial_interval = (1, 3)
521
+ second_interval = (3, 6)
522
+ third_interval = (6, 10)
523
+ long_term_start = "Month 2+"
524
+
525
+ # Calculate period durations (as percentage of total learning period)
526
+ initial_period = max(1, int(total_period * 0.1)) # 10% of learning period
527
+ second_period = max(1, int(total_period * 0.15)) # 15% of learning period
528
+ third_period = max(1, int(total_period * 0.25)) # 25% of learning period
529
+
530
+ # Format period text based on learning days
531
+ if total_period < 14:
532
+ period_unit = "days"
533
+ initial_text = f"Days 1-{initial_period}"
534
+ second_text = f"Days {initial_period + 1}-{initial_period + second_period}"
535
+ third_text = f"Days {initial_period + second_period + 1}-{initial_period + second_period + third_period}"
536
+ elif total_period < 60:
537
+ period_unit = "weeks"
538
+ initial_text = f"Week 1"
539
+ second_text = f"Week 2"
540
+ third_text = f"Weeks 3-4"
541
+ else:
542
+ period_unit = "months"
543
+ initial_text = f"Month 1"
544
+ second_text = f"Month 2"
545
+ third_text = f"Month 3"
546
+
547
+ recommendation = f"""
548
+ # Learning Optimization Recommendations
549
+
550
+ ## Target Retention Analysis
551
+ - Target retention rate: {float(target_retention):.1%}
552
+ - Achieved retention with multimodal approach: {multi_retention:.1%}
553
+ - Estimated learning efficiency gain: {efficiency_gain:.2f}x
554
+
555
+ ## Optimal Modality Recommendations
556
+ Based on the simulation, the most effective learning modalities for you are:
557
+ 1. **{top_modalities[0]}** (Primary) - Use for initial learning and difficult content
558
+ 2. **{top_modalities[1]}** (Secondary) - Use for reinforcement and review
559
+ 3. **{top_modalities[2]}** (Supplementary) - Use for variety and to prevent fatigue
560
+
561
+ ## Review Schedule Optimization
562
+ - Optimal workload per day: {int(min(20, avg_reviews_std / 3))}
563
+ - Recommended review sessions: {2 if avg_reviews_std > 30 else 1} per day
564
+
565
+ ## Spaced Repetition Strategy
566
+ - **{initial_text}:** Focus on using {top_modalities[0]} modality with shorter intervals ({initial_interval[0]}-{initial_interval[1]} {period_unit})
567
+ - **{second_text}:** Introduce {top_modalities[1]} modality and extend intervals ({second_interval[0]}-{second_interval[1]} {period_unit})
568
+ - **{third_text}:** Begin mixing in {top_modalities[2]} for variety and extend intervals ({third_interval[0]}-{third_interval[1]} {period_unit})
569
+ - **{long_term_start}:** Prioritize tough content in {top_modalities[0]} format, and maintain variety with other modalities
570
+
571
+ ## Estimated Results
572
+ Following this personalized approach should help you:
573
+ - Reduce total review time by approximately {min(75, int(100 * (1 - 1 / efficiency_gain)))}%
574
+ - Reach your target retention rate of {float(target_retention):.1%} or higher
575
+ - Maintain knowledge for longer periods with less review
576
+ """
577
+
578
+ return recommendation
579
+
580
+
581
+ # Create the Gradio interface
582
+ title = """
583
+ # CS6460-Ed Tech: Comprehensive Multimodal Spaced Repetition Learning Dashboard
584
+
585
+ This dashboard combines two powerful learning optimization approaches:
586
+ 1. **Free Spaced Repetition Scheduler (FSRS)** - An advanced algorithm for optimal review timing
587
+ 2. **Multimodal Learning System** - A system that adapts content presentation to your learning preferences
588
+
589
+ ## Parameter Settings
590
+
591
+ - **Preset Parameters**: These are pre-calibrated values based on research data that define the underlying models
592
+ - FSRS Model Parameters: Define the mathematical model for spaced repetition intervals
593
+ - Multimodal Weights: Define the effectiveness of different learning modalities
594
+
595
+ - **Customizable Settings**: These are parameters you can adjust based on your specific learning scenario
596
+ - Learning Period & Time: How long and how much time per day you plan to study
597
+ - Target Retention: The memory retention rate you aim to achieve
598
+ - Knowledge Load: How much material you need to learn
599
+
600
+ ## How to Use This Dashboard
601
+
602
+ 1. **Configure Settings** (Parameter Settings tab):
603
+ - Adjust the preset parameters if you have specific data about your learning preferences
604
+ - Set your customizable settings based on your actual study plan and goals
605
+ - Click "Run Simulation" to process your configuration
606
+
607
+ 2. **Review Analysis** (Analysis tab):
608
+ - Compare standard vs. multimodal review patterns
609
+ - Examine retention rates over time
610
+ - Understand which modalities are most effective for your learning style
611
+ - See how modality usage evolves as the system adapts to your preferences
612
+
613
+ 3. **Apply Recommendations** (Recommendations tab):
614
+ - Review the personalized learning strategy based on simulation results
615
+ - Follow the suggested spaced repetition schedule and modality mix
616
+ - Apply the recommendations to your actual study plan
617
+
618
+
619
+ Adjust the parameters below to see how different settings affect your learning efficiency,
620
+ and get personalized recommendations for optimizing your study approach.
621
+ """
622
+
623
+ with gr.Blocks() as demo:
624
+ gr.Markdown(title)
625
+
626
+ with gr.Tab("Parameter Settings"):
627
+ with gr.Row():
628
+ with gr.Column():
629
+ gr.Markdown("### Spaced Repetition (FSRS) Settings")
630
+ fsrs_weights = gr.Textbox(
631
+ label="Model Super-Parameter",
632
+ value="0.4, 0.6, 2.4, 5.8, 4.93, 0.94, 0.86, 0.01, 1.49, 0.14, 0.94, 2.18, 0.05, 0.34"
633
+ )
634
+ retrievability = gr.Textbox(label="Retrievability", value="0.9")
635
+ stability = gr.Textbox(label="Stability", value="0.95")
636
+ difficulty = gr.Textbox(label="Difficulty", value="1.06")
637
+
638
+ with gr.Column():
639
+ gr.Markdown("### Multimodal Learning Settings")
640
+ text_weight = gr.Slider(minimum=0.5, maximum=2.0, value=1.0, step=0.1, label="Text Effectiveness")
641
+ image_weight = gr.Slider(minimum=0.5, maximum=2.0, value=1.2, step=0.1, label="Image Effectiveness")
642
+ audio_weight = gr.Slider(minimum=0.5, maximum=2.0, value=0.9, step=0.1, label="Audio Effectiveness")
643
+ video_weight = gr.Slider(minimum=0.5, maximum=2.0, value=1.3, step=0.1, label="Video Effectiveness")
644
+ interactive_weight = gr.Slider(minimum=0.5, maximum=2.0, value=1.4, step=0.1,
645
+ label="Interactive Effectiveness")
646
+ mixed_weight = gr.Slider(minimum=0.5, maximum=2.0, value=1.1, step=0.1, label="Mixed Effectiveness")
647
+
648
+ with gr.Row():
649
+ with gr.Column():
650
+ gr.Markdown("### Shared Learning Parameters")
651
+ target_retention = gr.Slider(
652
+ label="Target Recall Rate",
653
+ minimum=0.7,
654
+ maximum=0.99,
655
+ value=0.9,
656
+ step=0.01
657
+ )
658
+ learning_time = gr.Slider(
659
+ label="Learning Time Per Day (minutes)",
660
+ minimum=5,
661
+ maximum=120,
662
+ value=30,
663
+ step=5
664
+ )
665
+ learning_days = gr.Slider(
666
+ label="Learning Period (days)",
667
+ minimum=30,
668
+ maximum=365,
669
+ value=100,
670
+ step=5
671
+ )
672
+ deck_size = gr.Slider(
673
+ label="Knowledge Load",
674
+ minimum=100,
675
+ maximum=10000,
676
+ value=1000,
677
+ step=100
678
+ )
679
+
680
+ with gr.Column():
681
+ max_ivl = gr.Slider(
682
+ label="Maximum Interval (days)",
683
+ minimum=1,
684
+ maximum=365,
685
+ value=36,
686
+ step=1
687
+ )
688
+ recall_cost = gr.Slider(
689
+ label="Review Cost (seconds)",
690
+ minimum=1,
691
+ maximum=60,
692
+ value=10,
693
+ step=1
694
+ )
695
+ forget_cost = gr.Slider(
696
+ label="Relearn Cost (seconds)",
697
+ minimum=1,
698
+ maximum=120,
699
+ value=30,
700
+ step=1
701
+ )
702
+ learn_cost = gr.Slider(
703
+ label="Learn Cost (seconds)",
704
+ minimum=1,
705
+ maximum=60,
706
+ value=10,
707
+ step=1
708
+ )
709
+ learning_rate = gr.Slider(
710
+ label="Learning Rate",
711
+ minimum=0.01,
712
+ maximum=0.2,
713
+ value=0.05,
714
+ step=0.01
715
+ )
716
+
717
+ run_btn = gr.Button("Run Simulation", variant="primary")
718
+
719
+ with gr.Tab("Analysis"):
720
+ with gr.Row():
721
+ plot1 = gr.Plot(label="Review Counts: Standard vs Multimodal")
722
+ plot2 = gr.Plot(label="Retention Rate & Memorized Items")
723
+ with gr.Row():
724
+ plot3 = gr.Plot(label="Modality Effectiveness Over Time")
725
+ plot4 = gr.Plot(label="Modality Distribution Over Time")
726
+
727
+ with gr.Tab("Recommendations"):
728
+ recommendations = gr.Markdown(label="Personalized Learning Recommendations")
729
+
730
+ # Connect the button to the function
731
+ run_btn.click(
732
+ fn=run_combined_simulation,
733
+ inputs=[
734
+ fsrs_weights, retrievability, stability, difficulty,
735
+ text_weight, image_weight, audio_weight, video_weight, interactive_weight, mixed_weight,
736
+ target_retention, learning_time, learning_days, deck_size, max_ivl,
737
+ recall_cost, forget_cost, learn_cost, learning_rate
738
+ ],
739
+ outputs=[plot1, plot2, plot3, plot4, recommendations]
740
+ )
741
+
742
+ if __name__ == "__main__":
743
+ demo.launch(show_error=True,share=True)
requirements.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ gradio>=3.50.2
2
+ numpy>=1.24.0
3
+ matplotlib>=3.7.0
4
+ pandas>=2.0.0
5
+ tqdm>=4.65.0