ScalarFlow: A Python Automatic Differentiation Library¶
ScalarFlow is a automatic differentiation library that borrows heavily from the TensorFlow 1.0 API. The emphasis is on simplicity, not performance.
Building and Running Computation Graphs¶
The ScalarFlow library makes it possible to build and execute computation graphs involving scalar quantities. For example:
import scalarflow as sf
with sf.Graph() as g:
a = sf.Constant(7.0)
b = sf.Constant(3.0)
sum = sf.Add(a, b)
result = g.run(sum)
print(result) # Prints 10.0
Notice that in the example above, the nodes are added to the graph
g
, even though it is not provided as an explicit argument when
constructing nodes in the computation graph. The scalarflow library
maintains a default computation graph that can be set using the with
keyword as above.
It is also possible to use the default computation computation graph outside of any context. For example:
a = sf.Constant(7.0)
b = sf.Constant(3.0)
sum = sf.Add(a, b)
result = sf.get_current_graph.run(sum)
print(result) # Prints 10.0
Node names¶
All nodes in the computation graph must have unique names. If the name
argument is not provided a default name will be selected based on the node
type:
x = sf.Constant(3.0, name='input')
squared = sf.Pow(x, 2.0)
print(x.name) # Prints "input"
print(squared.name) # Prints "Pow_0"
Variables and Placeholders¶
In addiction to Constants, scalarflow includes two scalar-type Nodes: Variables and Placeholders.
Variables¶
Variables behave like constants, but their values may be set directly using
the assign
method:
with sf.Graph() as g:
x = sf.Variable(4.0)
sqrt = sf.Pow(x, .5)
print(g.run(sqrt)) # Prints 2.0
x.assign(25.0)
print(g.run(sqrt)) # Prints 5.0
Variables are useful as trainable parameters in machine learning applications.
Placeholders¶
Placeholders must be assigned a value when run
is called on the graph:
with sf.Graph() as g:
x = sf.Constant(4.0)
y = sf.Placeholder(name='y')
sum = sf.Add(x, y)
print(g.run(sum, feed_dict={'y': 5.0})) # Prints 9.0
print(g.run(sum, feed_dict={'y': 10.0})) # Prints 14.0
Here, feed_dict
is a dictionary that maps from placeholder node names to the
value that should be used in the requested computation. Placeholder nodes
are useful for representing inputs and outputs in machine learning training
algorithms.
Node values¶
The run
method of the graph will only execute the subset of nodes that are
ancestors of the requested output. As a side effect, all of the values of
those nodes are cached and are available through the value
attribute:
with sf.Graph() as g:
a = sf.Constant(7.0, name='a')
b = sf.Constant(3.0, name='b')
sum = sf.Add(a, b)
sum_sqrd = sf.Pow(sum, 2.0)
sum_to_fourth = sf.Pow(sum_sqrd, 2.0)
g.run(sum_sqrd)
print(sum.value) # prints 10.0, sum was computed!
print(sum_to_fourth.value) # Something terrible happens, never computed.
Node derivatives¶
If the compute_derivatives
argument is True, then run
perform both a
forward and backward pass. After the backward pass completes, partial
derivatives will be available through the derivative
attribute of each
node that is involved in the computation:
with sf.Graph() as g:
x = sf.Constant(4.0)
y = sf.Pow(x, 2.0) # y = x^2
print(g.run(y)) # prints 16.0
print(x.derivative) # prints dy/dx = 2x = 8.0
Scalarflow API¶
-
class
scalarflow.
Add
(operand1, operand2, name='')¶ Addition. Node representing operand1 + operand2.
-
class
scalarflow.
BinaryOp
(operand1, operand2, name)¶ Abstract base class for all nodes representing binary operators
-
class
scalarflow.
Constant
(value, name='')¶ Constants behave like Variables that cannot be assigned values after they are created.
-
class
scalarflow.
Divide
(operand1, operand2, name='')¶ Division. Node representing operand1 / operand2.
-
class
scalarflow.
Exp
(operand, name='')¶ Exponential node: e^operand
-
class
scalarflow.
Graph
¶ Computation Graph
A computation graph is a directed acyclic graph that represents a numerical computation performed on scalar-valued inputs. This class supports forward computations as well as reverse-mode automatic differentiation.
The Graph class also acts as a Python context manager, supporting the
with
keyword. For example:with sf.Graph() as g: a = sf.Constant(1.0) b = sf.Constant(2.0) c = sf.Add(a, b) result = g.run(c)
-
nodes_by_name
¶ a dictionary mapping from unique names to the corresponding node
-
gen_dot
(filename, show_value=True, show_derivative=True)¶ Write a dot file representing the structure and contents of the graph.
The .dot file is a standard text-based format for representing graph structures. The graphviz library can convert dot files to images in many formats. There are also web-based tools for visualizing dot files. E.g. http://www.webgraphviz.com/
- Parameters
filename – Name of the file to create
show_value – Show evaluated node values in the graph
show_derivative – Show evaluated node derivatives in the graph
-
run
(node, feed_dict=None, compute_derivatives=False)¶ Run the computation graph and return the value of the indicated node.
After this method is called the
value
attribute of all the node and all of its ancestors will be set. Thevalue
attributes of non-ancestors are not defined.If
compute_derivatives
is true, this method will also perform a backward pass to determine the numerical derivatives for the indicated node with respect to every ancestor. The derivatives will be accessible from thederivative
attribute of the nodes. The derivatives of non-ancestors are not defined.- Parameters
node – Determine the value of this node
feed_dict – A dictionary mapping from Placeholder node names to values. E.g. {‘x’: 1.0, ‘y’: 2.0}.
compute_derivatives – True if we should perform a backward pass to compute partial derivatives.
Returns: The numeric value of of the indicated node.
-
-
class
scalarflow.
Log
(operand, name='')¶ Log base e.
-
class
scalarflow.
Multiply
(operand1, operand2, name='')¶ Multiplication. Node representing operand1 * operand2.
-
class
scalarflow.
Node
(name)¶ Abstract base class for all nodes in a computation graph.
-
value
¶ The most recently calculated value for this node. This is undefined if the node has never been involved in a computation.
-
derivative
¶ The most recently calculated partial derivative for this node. This is undefined if the node has not been involved in a backward pass.
-
name
¶ The name of this node. All nodes must have a unique name.
- Type
string
-
property
derivative
derivative should be read-only.
-
property
value
Value should be read-only (except for variable nodes).
-
-
class
scalarflow.
Placeholder
(name='')¶ Placeholders behave like Variables that can only be assigned values by including an appropriate value in the feed_dict passed to
run
.
-
class
scalarflow.
Pow
(operand, power, name='')¶ Power E.g. operand^2 or operand^3
-
class
scalarflow.
Subtract
(operand1, operand2, name='')¶ Subtraction. Node representing operand1 - operand2.
-
class
scalarflow.
UnaryOp
(operand, name)¶ Abstract base class for all nodes representing unary operators
-
class
scalarflow.
Variable
(value, name='')¶ Variable. A node that can be assigned a value.
-
assign
(value)¶ Assign a new value to this variable
-
-
scalarflow.
get_current_graph
()¶ Return the currently active computation graph.
Inside of a graph context this will return the graph associated with that that context. Outside of any context this will return the default graph.