Skip to content

Theano - Variables

As discussed in the Data Types chapter, variables in Theano are typically symbolic. They act as placeholders in the computational graph, defining the type and shape of data that will flow through it, but not holding concrete numerical values initially.

You declare symbolic variables using constructors from theano.tensor (commonly imported as tt). The constructor determines the variable’s type (dimensionality and data type).

import theano
import theano.tensor as tt
# Declare a symbolic vector (1D array) of 32-bit floats
x = tt.fvector('x')
# 'x' is an optional name, useful for debugging and graph visualization.
# Declare a symbolic matrix (2D array) of 64-bit integers
y = tt.lmatrix('y')
# Declare a symbolic scalar (0D tensor) of 32-bit integers
z = tt.iscalar('z')
# Declare a symbolic 3D tensor of 64-bit floats
t3 = tt.dtensor3('t3')

These statements create TensorVariable objects. They represent nodes in the graph that will later receive actual data when a theano.function is executed.

Symbolic variables have properties you can inspect:

print(f"Variable x: name={x.name}, type={x.type}, ndim={x.ndim}, dtype={x.dtype}")
# Output: Variable x: name=x, type=TensorType(float32, vector), ndim=1, dtype=float32
print(f"Variable y: name={y.name}, type={y.type}, ndim={y.ndim}, dtype={y.dtype}")
# Output: Variable y: name=y, type=TensorType(int64, matrix), ndim=2, dtype=int64
print(f"Variable t3: name={t3.name}, type={t3.type}, ndim={t3.ndim}, dtype={t3.dtype}")
# Output: Variable t3: name=t3, type=TensorType(float64, tensor3), ndim=3, dtype=float64
  • name: The optional string name given during creation.
  • type: A Theano TensorType object describing the data type and broadcast pattern (dimensionality).
  • ndim: The number of dimensions (0 for scalar, 1 for vector, 2 for matrix, etc.).
  • dtype: The data type as a string (e.g., ‘float32’, ‘int64’).

Here’s a quick reference for some common tensor variable constructors:

Constructor (theano.tensor.*)Data Type DefaultDimensions (ndim)
scalar (e.g., fscalar, dscalar, iscalar)Specific to prefix (f=float32, d=float64, i=int32)0
vector (e.g., fvector, dvector, ivector)Specific to prefix1
matrix (e.g., fmatrix, dmatrix, imatrix)Specific to prefix2
tensor3 (e.g., ftensor3, dtensor3, itensor3)Specific to prefix3
tensor4 (e.g., ftensor4, dtensor4, itensor4)Specific to prefix4

You can also use the generic constructors (vector, matrix, tensor3, etc.) and specify the dtype explicitly, which is often preferred for clarity:

# Explicitly create an int32 vector
explicit_vec = tt.vector('explicit_vec', dtype='int32')
print(f"Explicit Vec: type={explicit_vec.type}, dtype={explicit_vec.dtype}")
# Output: Explicit Vec: type=TensorType(int32, vector), dtype=int32
# Explicitly create a float64 matrix
explicit_mat = tt.matrix('explicit_mat', dtype='float64')
print(f"Explicit Mat: type={explicit_mat.type}, dtype={explicit_mat.dtype}")
# Output: Explicit Mat: type=TensorType(float64, matrix), dtype=float64

It’s crucial to remember the distinction: tt.fvector('x') creates a symbol representing a future float32 vector. It doesn’t hold data like a NumPy array np.array([1.0, 2.0], dtype='float32') does. The symbolic variable defines the rules and types for the graph, while concrete values (usually NumPy arrays) are provided only when you execute the compiled theano.function.

Symbolic variables are the fundamental building blocks for defining computations in Theano. You declare them to represent inputs, intermediate results, and outputs of your desired calculations. By connecting these variables with Theano operations (like +, *, tt.dot, tt.nnet.relu, etc.), you construct the computational graph that Theano will optimize and compile.

The next chapter discusses a special type of variable, the shared variable, which is essential for holding persistent state like model parameters during training.