zfit.run.set_graph_mode#
- run.set_graph_mode(*, set_backend=None)#
Set the policy for graph building and the usage of automatic vs numerical gradients.
zfit runs on top of TensorFlow, a modern, powerful computing engine very similar in design to Numpy. An interactive tutorial can be found at zfit/zfit-tutorials
Graph building
It has two ways to be run where the first defaults to the normal mode we are in except inside a
function()
decorated function. Setting the mode allows to control the behavior of decorated functions to not always trigger a graph building.- numpy-like/eager: in this mode, the syntax slightly differs from pure numpy but is similar. For example,
tf.sqrt
,tf.math.log
etc. The return values areEagerTensors
that represent “wrapped Numpy arrays” and can directly be used with any Numpy function. They can explicitly be converted to a Numpy array withznp.asarray(EagerTensor)
, which takes also care of nested structures and already existingnp.ndarrays
, or just anumpy()
method. The difference to Numpy is that TensorFlow tries to optimize the calculation slightly beforehand and may also executes on the GPU. This will result in a slight performance penalty for very small computations compared to Numpy, on the other hand an improved performance for larger computations.
- graph: a function can be decorated with
function()
, which will not execute its content immediately, but first trace it and build a graph. This is done by recording all `tf.*` operations and adding them to the graph while any Python operation, e.g. `np.random.*` will use a fixed value added to the graph. Building a graph greatly reduces the flexibility, since only `tf.*` operations can effectively be used to have dynamics in there, on the other hand it can greatly increase the performance. When the graph is built, it is cached (for later re-use), optimized and then executed. Calling a `tf.function` decorated function does therefore not make an actualy difference *for the caller. But it is a difference on how the function behaves.
@z.function def add_rnd(x): res1 = x + np.random.uniform() # returns a Python scalar. This exact scalar will be constant res2 = x + z.random.uniform(shape=()) # returns a tf.Tensor. This will be flexible return res1, res2 res_np1, res_tf1 = add_rnd(5) res_np2, res_tf2 = add_rnd(5) assert res_np1 == res_np2 # they will be the same! assert res_tf1 != res_tf2 # these differ
While writing TensorFlow is just like Numpy, if we build a graph, only
tf.*
dynamics “survives”. Important: while values are usually constant, changing azfit.Parameter
value with)()
will change the value in the graph as well.@z.function def add(x, param):
return x + param
param = zfit.Parameter(‘param1’, 36) assert add_rnd(5, param) == 41 param.set_value(6) assert add_rnd(5, param) == 42 # the value changed!
Every graph generation takes some additional time and is stored, consuming memory and slowing down the overall execution process. To clear all caches and force a rebuild of the graph,
zfit.run.clear_graph_cache()
can be used.If a function is not decorated with
z.function
, this does not guarantee that it is executed in eager, as an outer function may uses a decorator. A typical case is the loss, which is decorated. Therefore, any Model called inside will be evaluated with a graph building first.- When to use what:
Any repeated call (as a typical call to the loss function in the minimization process) is usually better suited within a
z.function
.A single call (e.g. for plotting) or repeated calls with different arguments should rather be run without a graph built first
Debugging is usually way easier without graph building. Therefore, set the graph mode to
False
If the minimization fails but the pdf works without graph, maybe the graph mode can be switched on for everything to have the same behavior in the pdf as when the loss is called.
- graph: a function can be decorated with
- Parameters:
graph (
bool
|str
|dict
|None
) –Policy for when to build a graph with which function. Currently allowed values are -
True
: this will make allzfit.z.function()
decorated function to be traced. Usefulto have a consistent behavior overall, as e.g. a PDF may not be traced if
pdf
orintegrate
is called, but may be traced when inside a loss.False
: this will make everything execute immediately, like Numpy (this is not enough to be fully Numpy compatible in the sense of using, also see the `autograd
option)’auto’: Something in between, where sampling (currently) and the loss builds a graph but all model methods, such as
pdf
,integrate
(except of*sample*
) do not and are executed eagerly.(advanced and experimental!): a dictionary containing the string of a wrapped function identifier (see also
function()
for more information about this) with a boolean that switches explicitly on/off the graph building for this type of decorated functions.
set_backend (
bool
|None
) – if true, also set the mode for the backend (if mode is set toFalse
). This is usually desired, as the backend (TensorFlow) can have compiled functions as well, especially in control flows that are used for example in binned PDFs. This can also be manually achieved usingtf.config.run_functions_eagerly
. Setting this to false can have deadlocks with threads.