• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

adaptive-machine-learning / CapyMOA / 25189796363

30 Apr 2026 09:17PM UTC coverage: 75.524% (+0.6%) from 74.909%
25189796363

Pull #355

github

web-flow
Merge e4643751b into 2ef7fe73e
Pull Request #355: feat(ocl): add LWF and DER

366 of 372 new or added lines in 19 files covered. (98.39%)

6 existing lines in 4 files now uncovered.

7452 of 9867 relevant lines covered (75.52%)

0.76 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

93.94
/src/capymoa/ocl/strategy/_experience_replay.py
1
import torch
1✔
2
from torch import Tensor
1✔
3

4
from capymoa.base import BatchClassifier
1✔
5
from capymoa.ocl.base import TrainTaskAware, TestTaskAware
1✔
6
from capymoa.ocl.util._replay import ReservoirSampler
1✔
7

8

9
class ExperienceReplay(BatchClassifier, TrainTaskAware, TestTaskAware):
1✔
10
    """Experience Replay.
11

12
    Experience Replay (ER) [#f0]_ is a replay continual learning strategy.
13

14
    * Uses a replay buffer to store past experiences and samples from it during training
15
      to mitigate catastrophic forgetting.
16
    * The replay buffer is implemented using reservoir sampling, which allows for
17
      uniform sampling over the entire stream [#f1]_.
18
    * Not :class:`capymoa.ocl.base.TrainTaskAware` or
19
      :class:`capymoa.ocl.base.TestTaskAware`, but will proxy it to the wrapped learner.
20

21
    >>> from capymoa.ann import Perceptron
22
    >>> from capymoa.classifier import Finetune
23
    >>> from capymoa.ocl.strategy import ExperienceReplay
24
    >>> from capymoa.ocl.datasets import TinySplitMNIST
25
    >>> from capymoa.ocl.evaluation import ocl_train_eval_loop
26
    >>> import torch
27
    >>> _ = torch.manual_seed(0)
28
    >>> scenario = TinySplitMNIST()
29
    >>> model = Perceptron(scenario.schema)
30
    >>> learner = ExperienceReplay(Finetune(scenario.schema, model))
31
    >>> results = ocl_train_eval_loop(
32
    ...     learner,
33
    ...     scenario.train_loaders(32),
34
    ...     scenario.test_loaders(32),
35
    ... )
36
    >>> print(f"{results.accuracy_final*100:.1f}%")
37
    32.5%
38

39
    .. [#f0] `Rolnick, D., Ahuja, A., Schwarz, J., Lillicrap, T., & Wayne, G. (2019).
40
              Experience replay for continual learning. Advances in neural information
41
              processing systems, 32. <https://arxiv.org/abs/1811.11682>`_
42
    .. [#f1] `Jeffrey S. Vitter. 1985. Random sampling with a reservoir. ACM Trans. Math.
43
              Softw. 11, 1 (March 1985), 37–57. <https://doi.org/10.1145/3147.3165>`_
44
    """
45

46
    def __init__(
1✔
47
        self, learner: BatchClassifier, buffer_size: int = 200, repeat: int = 1
48
    ) -> None:
49
        """Initialize the Experience Replay strategy.
50

51
        :param learner: The learner to be wrapped for experience replay.
52
        :param buffer_size: The size of the replay buffer, defaults to 200.
53
        :param repeat: The number of times to repeat the training data in each batch,
54
            defaults to 1.
55
        """
56
        super().__init__(learner.schema, learner.random_seed)
1✔
57
        #: The wrapped learner to be trained with experience replay.
58
        self.learner = learner
1✔
59
        self._buffer = ReservoirSampler.new_xybuffer(
1✔
60
            buffer_size,
61
            learner.schema.shape,
62
            torch.Generator().manual_seed(self.random_seed),
63
        )
64
        self.repeat = repeat
1✔
65

66
    def batch_train(self, x: Tensor, y: Tensor) -> None:
1✔
67
        # update the buffer with the new data
68
        self._buffer.update(x=x, y=y)
1✔
69

70
        for _ in range(self.repeat):
1✔
71
            # sample from the buffer and construct training batch
72
            replay_batch = self._buffer.sample(x.shape[0])
1✔
73
            replay_x = replay_batch["x"]
1✔
74
            replay_y = replay_batch["y"]
1✔
75
            train_x = torch.cat((x, replay_x), dim=0)
1✔
76
            train_y = torch.cat((y, replay_y), dim=0)
1✔
77
            train_x = train_x.to(self.learner.device, dtype=self.learner.x_dtype)
1✔
78
            train_y = train_y.to(self.learner.device, dtype=self.learner.y_dtype)
1✔
79
            self.learner.batch_train(train_x, train_y)
1✔
80

81
    def batch_predict_proba(self, x: Tensor) -> Tensor:
1✔
82
        x = x.to(self.learner.device, dtype=self.learner.x_dtype)
1✔
83
        return self.learner.batch_predict_proba(x)
1✔
84

85
    def on_test_task(self, task_id: int):
1✔
86
        if isinstance(self.learner, TestTaskAware):
1✔
UNCOV
87
            self.learner.on_test_task(task_id)
×
88

89
    def on_train_task(self, task_id: int):
1✔
90
        if isinstance(self.learner, TrainTaskAware):
1✔
UNCOV
91
            self.learner.on_train_task(task_id)
×
92

93
    def __str__(self) -> str:
1✔
94
        return f"ExperienceReplay(buffer_size={self._buffer.capacity})"
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc