diff --git a/ftl/core/deck-config.ftl b/ftl/core/deck-config.ftl index d16ce45bc..7d221f67d 100644 --- a/ftl/core/deck-config.ftl +++ b/ftl/core/deck-config.ftl @@ -485,6 +485,14 @@ deck-config-percent-input = { $pct }% deck-config-optimizing-preset = Optimizing preset { $current_count }/{ $total_count }... deck-config-fsrs-must-be-enabled = FSRS must be enabled first. deck-config-fsrs-params-optimal = The FSRS parameters currently appear to be optimal. +deck-config-fsrs-bad-fit-warning = Your memory is difficult for FSRS to predict. Recommendations: + + - Suspend or reformulate leeches. + - Use the answer buttons consistently. Keep in mind that "Hard" is a passing grade, not a failing grade. + - Understand before you memorize. + + It may take weeks or months before parameters become optimal. + deck-config-fsrs-params-no-reviews = No reviews found. Make sure this preset is assigned to all decks (including subdecks) that you want to optimize, and try again. deck-config-wait-for-audio = Wait for audio diff --git a/proto/anki/scheduler.proto b/proto/anki/scheduler.proto index 364bf50ad..59372c64e 100644 --- a/proto/anki/scheduler.proto +++ b/proto/anki/scheduler.proto @@ -359,6 +359,7 @@ message ComputeFsrsParamsRequest { message ComputeFsrsParamsResponse { repeated float params = 1; uint32 fsrs_items = 2; + optional float logLoss = 3; } message ComputeFsrsParamsFromItemsRequest { diff --git a/rslib/src/scheduler/fsrs/params.rs b/rslib/src/scheduler/fsrs/params.rs index 6da02776a..e30efba0e 100644 --- a/rslib/src/scheduler/fsrs/params.rs +++ b/rslib/src/scheduler/fsrs/params.rs @@ -75,6 +75,7 @@ impl Collection { return Ok(ComputeFsrsParamsResponse { params: current_params.to_vec(), fsrs_items, + log_loss: None }); } // adapt the progress handler to our built-in progress handling @@ -108,12 +109,13 @@ impl Collection { let (progress, progress_thread) = create_progress_thread()?; let fsrs = FSRS::new(None)?; - let mut params = fsrs.compute_parameters(ComputeParametersInput { + let input = ComputeParametersInput { train_set: items.clone(), progress: Some(progress.clone()), enable_short_term: true, num_relearning_steps: Some(num_of_relearning_steps), - })?; + }; + let mut params = fsrs.compute_parameters(input.clone())?; progress_thread.join().ok(); if let Ok(fsrs) = FSRS::new(Some(current_params)) { let current_log_loss = fsrs.evaluate(items.clone(), |_| true)?.log_loss; @@ -146,7 +148,15 @@ impl Collection { } } - Ok(ComputeFsrsParamsResponse { params, fsrs_items }) + let fsrs = FSRS::new(None)?; + let log_loss = fsrs + .evaluate_with_time_series_splits(input, |_| true).ok().map(|eval| eval.log_loss); + + Ok(ComputeFsrsParamsResponse { + params, + fsrs_items, + log_loss, + }) } pub(crate) fn revlog_for_srs( diff --git a/rslib/src/scheduler/service/mod.rs b/rslib/src/scheduler/service/mod.rs index e7d9a04eb..2ab647061 100644 --- a/rslib/src/scheduler/service/mod.rs +++ b/rslib/src/scheduler/service/mod.rs @@ -372,7 +372,7 @@ impl crate::services::BackendSchedulerService for Backend { enable_short_term: true, num_relearning_steps: None, })?; - Ok(ComputeFsrsParamsResponse { params, fsrs_items }) + Ok(ComputeFsrsParamsResponse { params, fsrs_items, log_loss: None }) } fn fsrs_benchmark( diff --git a/ts/routes/deck-options/FsrsOptions.svelte b/ts/routes/deck-options/FsrsOptions.svelte index 7b2318e00..820893f1f 100644 --- a/ts/routes/deck-options/FsrsOptions.svelte +++ b/ts/routes/deck-options/FsrsOptions.svelte @@ -41,6 +41,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html const defaults = state.defaults; const fsrsReschedule = state.fsrsReschedule; const daysSinceLastOptimization = state.daysSinceLastOptimization; + const logLossBadThreshold = 0.5; $: lastOptimizationWarning = $daysSinceLastOptimization > 30 ? tr.deckConfigTimeToOptimize() : ""; @@ -195,6 +196,10 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html } else { $config.fsrsParams6 = resp.params; optimized = true; + console.log(resp.logLoss) + if (resp.logLoss && resp.logLoss > logLossBadThreshold) { + setTimeout(() => alert(tr.deckConfigFsrsBadFitWarning())); + } } if (computeParamsProgress) { computeParamsProgress.current = computeParamsProgress.total;