Trace_stat_summary_utils.Exponential_moving_average
Functional Exponential Moving Average (EMA).
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.
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
.
Feed a new sample to the EMA. If the sample is not finite (i.e., NaN or infinite), the represented won't be either.
update_batch ema p s
is equivalent to calling update
s
times. Modulo floating point errors.
forget ema
forgets some of the past without the need for samples. The represented value doesn't change.
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 void_fraction : t -> float