New progress in implement the custom algorithm

It is the follow-up to the previous post Some problems in implementing a state-of-the-art SRS scheduler on Anki.

Thanks to the support of dae and RumovZ, I implement a simplified version of the fsrs algorithm in open-spaced-repetition/fsrs4anki (github.com). The customData works well!

But there are also some problems in developing the adaptive module. It needs to modify the global parameters after each feedback and store these values permanently. Is it possible to implement it without an add-on?

By the way, does somebody wants to try the custom schedule? More feedback is welcomed.

10 Likes

Interesting.
Does this algorithm resemble Supermemo or is it a new path?

By the way, does somebody wants to try the custom schedule? More feedback is welcomed.

Does it work at 0.54?

It only works at 2.1.55, because I use the dev feature.

1 Like

FSRS is based on the DSR model purposed by SuperMemo. And I improve the model in my paper A Stochastic Shortest Path Algorithm for Optimizing Spaced Repetition Scheduling | Proceedings of the 28th ACM SIGKDD Conference on Knowledge Discovery and Data Mining

1 Like

I will try to install the dev version to test.
Congratulations again.

1 Like

Update: I am working on optimizing the parameters from my revlog. Maybe the next step is to develop an add-on for Anki. But it need PyTorch. Could I import torch in add-on?

image

1 Like

It needs to modify the global parameters after each feedback and store these values permanently.

I am not sure what are the global parameters. But if it needs to store something permanently maybe local storage or cookies since anki is basically an entire browser

Not easily, no:

https://addon-docs.ankiweb.net/python-modules.html

So sad. Maybe I need to publish it via docker.

1 Like

Maybe not the best solution, but since an anki addon seems to not be feasible, maybe users can upload their anki revlog and optimize the parameters on something like Google Colab every once in a while when they want to? Then they can manually update the v3 scheduler code with the new global parameters.

Also, just wondering, is it okay to use this custom scheduler on Anki Desktop but use the v2 scheduler on AnkiDroid? I’m interested in trying this custom scheduler, but since AnkiDroid doesn’t have the new customData backend changes yet, it’ll be impossible to use it in AnkiDroid. I know there is v3 scheduler support in AnkiDroid alpha now (see Support for v3 scheduler [Ready for early adoption testing] · Issue #10411 · ankidroid/Anki-Android · GitHub ) but I do a lot of my reviews on my phone. I don’t mind that using v2 scheduler on Android means that I don’t yet get the new scheduler code, but whenever I do go back on Desktop to finish reviews, it would be nice to use your FSRS algorithm

I’m mainly concerned that if I use AnkiDroid version without customData support, AnkiDroid may delete the customData values for the cards stored in the database that I reviewed on Desktop. If this is the case, then I must wait for AnkiDroid support before being able to use fsrs4anki

If your interval modifier is 100% (e.g., default value), FSRS could convert it to the memory states in customData correctly. It is also why FSRS could be used in old cards. But the conversion is not always accurate. So it is better to use it after AnkiDroid’s support.

Thanks for your suggestion! Google Colab is a tempting alternative method. I will try it.

1 Like

I have implemented and tested it! Could you help me try it?

The process of optimization:





Great! I tried running it in Google Colab, I uploaded my collection.colpkg file to My Drive/Colab Notebooks in Google Drive. Then I needed to modify the notebook

# Mount Google Drive
from google.colab import drive
drive.mount('/content/drive')

filename = "/content/drive/My Drive/Colab Notebooks/collection.colpkg"
# If you upload deck file, replace it with your deck filename. E.g., ALL__Learnig.apkg 
# If you upload collection file, replace it with your colpgk filename. E.g., collection-2022-09-18@13-21-58.colpkg
...

for it to load my collection.

Building the trainset took about 5 minutes in Google Colab (I didn’t enable GPU acceleration in Google Colab). EDIT: I tried enabling GPU acceleration and it took 4 minutes.

100%|██████████| 24177/24177 [05:16<00:00, 76.43it/s] 
Trainset saved

However, when I go to train the model, it seems like I get NaN for the parameters after 30981 iterations:

iteration: 1
f_s: Parameter containing:
tensor([1.9999], requires_grad=True)
f_d: Parameter containing:
tensor([4.9999], requires_grad=True)
s_w: Parameter containing:
tensor([ 3.0001, -0.6999, -0.1999, -0.2999], requires_grad=True)
iteration: 30981
f_s: Parameter containing:
tensor([nan], requires_grad=True)
f_d: Parameter containing:
tensor([nan], requires_grad=True)
s_w: Parameter containing:
tensor([nan, nan, nan, nan], requires_grad=True)
iteration: 61961
f_s: Parameter containing:
tensor([nan], requires_grad=True)
f_d: Parameter containing:
tensor([nan], requires_grad=True)
s_w: Parameter containing:
tensor([nan, nan, nan, nan], requires_grad=True)
iteration: 92941
f_s: Parameter containing:
tensor([nan], requires_grad=True)
f_d: Parameter containing:
tensor([nan], requires_grad=True)
s_w: Parameter containing:
tensor([nan, nan, nan, nan], requires_grad=True)
iteration: 123921
f_s: Parameter containing:
tensor([nan], requires_grad=True)
f_d: Parameter containing:
tensor([nan], requires_grad=True)
s_w: Parameter containing:
tensor([nan, nan, nan, nan], requires_grad=True)
iteration: 154901
f_s: Parameter containing:
tensor([nan], requires_grad=True)
f_d: Parameter containing:
tensor([nan], requires_grad=True)
s_w: Parameter containing:
tensor([nan, nan, nan, nan], requires_grad=True)
iteration: 185881
f_s: Parameter containing:
tensor([nan], requires_grad=True)
f_d: Parameter containing:
tensor([nan], requires_grad=True)
s_w: Parameter containing:
tensor([nan, nan, nan, nan], requires_grad=True)
iteration: 216861
f_s: Parameter containing:
tensor([nan], requires_grad=True)
f_d: Parameter containing:
tensor([nan], requires_grad=True)
s_w: Parameter containing:
tensor([nan, nan, nan, nan], requires_grad=True)
iteration: 247841
f_s: Parameter containing:
tensor([nan], requires_grad=True)
f_d: Parameter containing:
tensor([nan], requires_grad=True)
s_w: Parameter containing:
tensor([nan, nan, nan, nan], requires_grad=True)
iteration: 278821
f_s: Parameter containing:
tensor([nan], requires_grad=True)
f_d: Parameter containing:
tensor([nan], requires_grad=True)
s_w: Parameter containing:
tensor([nan, nan, nan, nan], requires_grad=True)
iteration: 309801
f_s: Parameter containing:
tensor([nan], requires_grad=True)
f_d: Parameter containing:
tensor([nan], requires_grad=True)
s_w: Parameter containing:
tensor([nan, nan, nan, nan], requires_grad=True)
Training finished!

The training took about 41 minutes on Google Colab (no GPU acceleration). EDIT: I tried enabling GPU acceleration and it took about 33 minutes.

Then I run the print commands:

const defaultDifficulty = nan;
const defaultStability = nan;
const difficultyDecay = nan;
const stabilityDecay = nan;
const increaseFactor = nan;
const lapsesBase = nan;

It seems something went wrong during the training, all the values are nan

I have uploaded my Anki collection here (media files removed): 23.06 MB file on MEGA

Also, I noticed you have recently changed the default increaseFactor from 60 to 3. Just curious as to why this was changed. In fsrs.js it is set to 60 still

Because 60 is too large to optimize, I replaced increaseFactor with exp(increaseFactor). This update is released in main.js.

1 Like

Did you modified other codes? Such as timezone and next_day_starts_at.

I test your collection, but the output is different from yours.

iteration: 1
f_s: Parameter containing:
tensor([2.0001], requires_grad=True)
f_d: Parameter containing:
tensor([4.9999], requires_grad=True)
s_w: Parameter containing:
tensor([ 3.0001, -0.6999, -0.1999, -0.2999], requires_grad=True)
iteration: 29739
f_s: Parameter containing:
tensor([2.2770], requires_grad=True)
f_d: Parameter containing:
tensor([4.7645], requires_grad=True)
s_w: Parameter containing:
tensor([ 3.1533, -0.5815, -0.1642, -0.1390], requires_grad=True)
iteration: 59477
f_s: Parameter containing:
tensor([2.5193], requires_grad=True)
f_d: Parameter containing:
tensor([4.6462], requires_grad=True)
s_w: Parameter containing:
tensor([ 3.1632, -0.6154, -0.1573, -0.0481], requires_grad=True)
iteration: 89215
f_s: Parameter containing:
tensor([2.7030], requires_grad=True)
f_d: Parameter containing:
tensor([4.5173], requires_grad=True)
s_w: Parameter containing:
tensor([ 3.1854, -0.6387, -0.1445, -0.0383], requires_grad=True)
iteration: 118953
f_s: Parameter containing:
tensor([2.8376], requires_grad=True)
f_d: Parameter containing:
tensor([4.4083], requires_grad=True)
s_w: Parameter containing:
tensor([ 3.1913, -0.6762, -0.1322, -0.0356], requires_grad=True)
iteration: 148691
f_s: Parameter containing:
tensor([2.9655], requires_grad=True)
f_d: Parameter containing:
tensor([4.2989], requires_grad=True)
s_w: Parameter containing:
tensor([ 3.2079, -0.6979, -0.1099, -0.0299], requires_grad=True)
iteration: 178429
f_s: Parameter containing:
tensor([3.0746], requires_grad=True)
f_d: Parameter containing:
tensor([4.1976], requires_grad=True)
s_w: Parameter containing:
tensor([ 3.2197, -0.7260, -0.1008, -0.0462], requires_grad=True)
iteration: 208167
f_s: Parameter containing:
tensor([3.1531], requires_grad=True)
f_d: Parameter containing:
tensor([4.1062], requires_grad=True)
s_w: Parameter containing:
tensor([ 3.2221, -0.7595, -0.0910, -0.0492], requires_grad=True)
iteration: 237905
f_s: Parameter containing:
tensor([3.2659], requires_grad=True)
f_d: Parameter containing:
tensor([3.9940], requires_grad=True)
s_w: Parameter containing:
tensor([ 3.2386, -0.7845, -0.0941, -0.0430], requires_grad=True)
iteration: 267643
f_s: Parameter containing:
tensor([3.3688], requires_grad=True)
f_d: Parameter containing:
tensor([3.8817], requires_grad=True)
s_w: Parameter containing:
tensor([ 3.2648, -0.7945, -0.0800, -0.0561], requires_grad=True)
iteration: 297381
f_s: Parameter containing:
tensor([3.4299], requires_grad=True)
f_d: Parameter containing:
tensor([3.7969], requires_grad=True)
s_w: Parameter containing:
tensor([ 3.2701, -0.8275, -0.0833, -0.0750], requires_grad=True)
Training finished!

Oh yes, I forgot to mention, I changed it to:

timezone = 'America/Toronto'
next_day_starts_at = 8

Although, I feel like this shouldn’t have caused the issue.

I found that some review logs’ delta_t are zero, which causes the loss equal to nan.

This version fixed the problem: fsrs4anki/fsrs4anki_optimizer.ipynb at main · open-spaced-repetition/fsrs4anki · GitHub

Perfect, it works great!

iteration: 1
f_s: Parameter containing:
tensor([2.0001], requires_grad=True)
f_d: Parameter containing:
tensor([5.0001], requires_grad=True)
s_w: Parameter containing:
tensor([ 2.9999, -0.7001, -0.2001, -0.2999], requires_grad=True)

iteration: 31007
f_s: Parameter containing:
tensor([2.2890], requires_grad=True)
f_d: Parameter containing:
tensor([4.7483], requires_grad=True)
s_w: Parameter containing:
tensor([ 3.1617, -0.5758, -0.1754, -0.1434], requires_grad=True)

iteration: 62013
f_s: Parameter containing:
tensor([2.4923], requires_grad=True)
f_d: Parameter containing:
tensor([4.6437], requires_grad=True)
s_w: Parameter containing:
tensor([ 3.1647, -0.6106, -0.1472, -0.0289], requires_grad=True)

iteration: 93019
f_s: Parameter containing:
tensor([2.6339], requires_grad=True)
f_d: Parameter containing:
tensor([4.5172], requires_grad=True)
s_w: Parameter containing:
tensor([ 3.1773, -0.6461, -0.1394, -0.0249], requires_grad=True)

iteration: 124025
f_s: Parameter containing:
tensor([2.7635], requires_grad=True)
f_d: Parameter containing:
tensor([4.3944], requires_grad=True)
s_w: Parameter containing:
tensor([ 3.1978, -0.6699, -0.1246, -0.0239], requires_grad=True)

iteration: 155031
f_s: Parameter containing:
tensor([2.8838], requires_grad=True)
f_d: Parameter containing:
tensor([4.2754], requires_grad=True)
s_w: Parameter containing:
tensor([ 3.2085, -0.7074, -0.1113, -0.0334], requires_grad=True)

iteration: 186037
f_s: Parameter containing:
tensor([2.9799], requires_grad=True)
f_d: Parameter containing:
tensor([4.1589], requires_grad=True)
s_w: Parameter containing:
tensor([ 3.2263, -0.7308, -0.0982, -0.0399], requires_grad=True)

iteration: 217043
f_s: Parameter containing:
tensor([3.0650], requires_grad=True)
f_d: Parameter containing:
tensor([4.0661], requires_grad=True)
s_w: Parameter containing:
tensor([ 3.2418, -0.7532, -0.0872, -0.0368], requires_grad=True)

iteration: 248049
f_s: Parameter containing:
tensor([3.1305], requires_grad=True)
f_d: Parameter containing:
tensor([3.9685], requires_grad=True)
s_w: Parameter containing:
tensor([ 3.2600, -0.7794, -0.0800, -0.0573], requires_grad=True)

iteration: 279055
f_s: Parameter containing:
tensor([3.1929], requires_grad=True)
f_d: Parameter containing:
tensor([3.8507], requires_grad=True)
s_w: Parameter containing:
tensor([ 3.2689, -0.8139, -0.0752, -0.0383], requires_grad=True)

iteration: 310061
f_s: Parameter containing:
tensor([3.2251], requires_grad=True)
f_d: Parameter containing:
tensor([3.7356], requires_grad=True)
s_w: Parameter containing:
tensor([ 3.2862, -0.8350, -0.0681, -0.0518], requires_grad=True)
train: 100%|██████████| 310065/310065 [34:49<00:00, 148.39it/s]

Training finished!

const defaultDifficulty = 3.7356;
const defaultStability = 3.2251;
const difficultyDecay = -0.8349;
const stabilityDecay = -0.0681;
const increaseFactor = 3.2862;
const lapsesBase = -0.0518;
1 Like