Skip to content

PeriodicTrigger

Generate pseudo drift detection signals.

There are two approaches1:

  • fixed where the drift signal is generated every t_0 samples. - random corresponds to a pseudo-random drift detection strategy.

Parameters

  • trigger_method (str) – defaults to fixed

    The trigger method to use.
    * fixed
    * random

  • t_0 (int) – defaults to 300

    Reference point to define triggers.

  • w (int) – defaults to 0

    Auxiliary parameter whose purpose is twofold:
    - if trigger_method="fixed", the periodic drift signals will only start after an initial warm-up period randomly defined between [0, w]. Useful to avoid that all ensemble members are reset at the same time when periodic triggers are used as the adaptation strategy.
    - if trigger_method="random", w defines the probability bounds of triggering a drift. The chance of triggering a drift is \(0.5\) after observing t_0 instances and becomes \(1\) after monitoring t_0 + w / 2 instances. A sigmoid function is used to produce values between [0, 1] that are used as the reset probabilities.

  • dynamic_cloning (bool) – defaults to False

    Whether to change the seed and w values each time clone() is called.

  • seed (int) – defaults to None

    Random seed for reproducibility.

Attributes

  • drift_detected

    Concept drift alarm. True if concept drift is detected.

Examples

>>> import random
>>> from river import drift

>>> rng = random.Random(42)

The observed values will not affect the periodic triggers.

>>> data = [rng.gauss(0, 1) for _ in range(1000)]

Let's start with the fixed drift signals:

>>> ptrigger = PeriodicTrigger(t_0=500, seed=42)
>>> for i, v in enumerate(data):
...     _ = ptrigger.update(v)
...     if ptrigger.drift_detected:
...         print(f"Drift detected at instance {i}.")
Drift detected at instance 499.
Drift detected at instance 999.

Now, the random drift signals:

>>> rtrigger = PeriodicTrigger(
...     trigger_method="random",
...     t_0=500,
...     w=100,
...     dynamic_cloning=True,
...     seed=42
... )
>>> for i, v in enumerate(data):
...     _ = rtrigger.update(v)
...     if rtrigger.drift_detected:
...         print(f"Drift detected at instance {i}.")
Drift detected at instance 368.
Drift detected at instance 817.

Remember to set a w > 0 value if random triggers are used:

>>> try:
...     PeriodicTrigger(trigger_method="random")
... except ValueError as ve:
...     print(ve)
The 'w' value must be greater than zero when 'trigger_method' is 'random'.

Since we set dynamic_cloning to True, a clone of the periodic trigger will have its internal paramenters changed:

>>> rtrigger = rtrigger.clone()
>>> for i, v in enumerate(data):
...     _ = rtrigger.update(v)
...     if rtrigger.drift_detected:
...         print(f"Drift detected at instance {i}.")
Drift detected at instance 429.
Drift detected at instance 728.

Methods

update

Update the change detector with a single data point.

Parameters

  • x (numbers.Number)

Returns

DriftDetector: self

Notes

When used in ensembles, a naive implementation of periodic drift signals would make all ensemble members reset at the same time. To avoid that, the dynamic_cloning parameter can be set to True. In this case, every time the clone method of this detector is called in an ensemble a new seed is defined. If dynamic_cloning=True and trigger_method="fixed", a new w between [0, t_0] will also be created for the new cloned instance.

References


  1. Heitor Gomes, Jacob Montiel, Saulo Martiello Mastelini, Bernhard Pfahringer, and Albert Bifet. On Ensemble Techniques for Data Stream Regression. IJCNN'20. International Joint Conference on Neural Networks. 2020.