Module Trace_stat_summary_utils.Exponential_moving_average

Functional Exponential Moving Average (EMA).

type t
val create : ?relevance_threshold:float -> float -> t

create ?relevance_threshold m is ema, a functional exponential moving average. 1. -. m is the fraction of what's forgotten of the past during each update.

The value represented by ema can be retrieved using peek_exn ema or peek_or_nan ema.

When m = 0., all the past is forgotten on each update and each forget. peek_exn ema is either the latest sample fed to an update function or nan.

When m approaches 1., peek_exn ema tends to be the mean of all the samples seen in the past.

See https://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average

Relevance

The value represented by ema is built from the history of samples shown through update(_batch). When that history is empty, the value can't be calculated, and when the history is too small, or too distant because of calls to forget(_batch), the represented value is very noisy.

relevance_threshold is a threshold on ema's inner void_fraction, below which the represented value should be considered relevant, i.e. peek_or_nan ema is not NaN.

Before any call to update(_batch), the represented value is always irrelevant.

After a sufficient number of updates (e.g. 1 update in general), the represented value gets relevant.

After a sufficient number of forgets, the represented value gets irrelevant again.

A good value for relevance_threshold is between momentum and 1., so that right after a call to update, the value is always relevant.

Commutativity

Adding together two curves independently built with an EMA, is equivalent to adding the samples beforehand, and using a single EMA.

In a more more formal way:

Let a, b be vectors of real values of similar length.

Let ema(x) be the Exponential_moving_average.map momentum function (float list -> float list);

Let *, + and / be the element-wise vector multiplication, addition and division.

Then ema(a + b) is ema(a) + ema(b).

The same is not true for multiplication and division, ema(a * b) is not ema(a) * ema(b), but exp(ema(log(a * b))) is exp(ema(log(a))) * exp(ema(log(b))) when all values in a and b are strictly greater than 0.

val from_half_life : ?relevance_threshold:float -> float -> t

from_half_life hl is ema, a functional exponential moving average. After hl calls to update, half of the past is forgotten.

val from_half_life_ratio : ?relevance_threshold:float -> float -> float -> t

from_half_life_ratio hl_ratio step_count is ema, a functional exponential moving average. After hl_ratio * step_count calls to update, half of the past is forgotten.

val map : ?relevance_threshold:float -> float -> float list -> float list

map momentum vec0 is vec1, a list of float with the same length as vec0, where the values have been locally averaged.

The first element of vec1 is also the first element of vec0.

val update : t -> float -> t

Feed a new sample to the EMA. If the sample is not finite (i.e., NaN or infinite), the represented won't be either.

val update_batch : t -> float -> float -> t

update_batch ema p s is equivalent to calling update s times. Modulo floating point errors.

val forget : t -> t

forget ema forgets some of the past without the need for samples. The represented value doesn't change.

val forget_batch : t -> float -> t

forget_batch ema s is equivalent to calling forget s times. Modulo floating point errors.

val is_relevant : t -> bool

Indicates whether or not peek_exn can be called without raising an exception.

val peek_exn : t -> float

Read the EMA value.

val peek_or_nan : t -> float

Read the EMA value.

val momentum : t -> float
val hidden_state : t -> float
val void_fraction : t -> float