提交 150add2c authored 作者: Brandon T. Willard's avatar Brandon T. Willard 提交者: Brandon T. Willard

Update developer documentation on Type to include new partial shape information

上级 e6f9689e
...@@ -203,36 +203,39 @@ structures, code going like ``def f(x): ...`` would produce an :class:`Op` for ...@@ -203,36 +203,39 @@ structures, code going like ``def f(x): ...`` would produce an :class:`Op` for
:class:`Type` :class:`Type`
------------- -------------
A :class:`Type` in Aesara represents a set of constraints on potential A :class:`Type` in Aesara provides static information (or constraints) about
data objects. These constraints allow Aesara to tailor C code to handle data objects in a graph. The information provided by :class:`Type`\s allows
them and to statically optimize the computation graph. For instance, Aesara to perform optimizations and produce more efficient compiled code.
the :ref:`irow <libdoc_tensor_creation>` type in the :mod:`aesara.tensor` package
gives the following constraints on the data the :class:`Variable`\s of type ``irow`` Every symbolic :class:`Variable` in an Aesara graph has an associated
may contain: :class:`Type` instance, and :class:`Type`\s also serve as a means of
constructing :class:`Variable` instances. In other words, :class:`Type`\s and
#. Must be an instance of :class:`numpy.ndarray`: ``isinstance(x, numpy.ndarray)`` :class:`Variable`\s go hand-in-hand.
#. Must be an array of 32-bit integers: ``str(x.dtype) == 'int32'``
#. Must have a shape of 1xN: ``len(x.shape) == 2 and x.shape[0] == 1`` For example, :ref:`aesara.tensor.irow <libdoc_tensor_creation>` is an instance of a
:class:`Type` and it can be used to construct variables as follows:
Knowing these restrictions, Aesara may generate C code for addition, etc.
that declares the right data types and that contains the right number >>> from aesara.tensor import irow
of loops over the dimensions. >>> irow()
<TensorType(int32, (1, None))>
Note that an Aesara :class:`Type` is not equivalent to a Python type or
class. Indeed, in Aesara, :ref:`irow <libdoc_tensor_creation>` and :ref:`dmatrix As the string print-out shows, `irow` specifies the following information about
<libdoc_tensor_creation>` both use :class:`numpy.ndarray` as the underlying type the :class:`Variable`\s it constructs:
for doing computations and storing data, yet they are different Aesara
:class:`Type`\s. Indeed, the constraints set by `dmatrix` are: #. They represent tensors that are backed by :class:`numpy.ndarray`\s.
This comes from the fact that `irow` is an instance of :class:`TensorType`,
#. Must be an instance of :class:`numpy.ndarray`: ``isinstance(x, numpy.ndarray)`` which is the base :class:`Type` for symbolic :class:`numpy.ndarray`\s.
#. Must be an array of 64-bit floating point numbers: ``str(x.dtype) == 'float64'`` #. They represent arrays of 32-bit integers (i.e. from the ``int32``).
#. Must have a shape of ``MxN``, no restriction on ``M`` or ``N``: ``len(x.shape) == 2`` #. They represent arrays with shapes of :math:`1 \times N`, or, in code, ``(1,
None)``, where ``None`` represents any shape value.
These restrictions are different from those of ``irow`` which are listed above.
Note that Aesara :class:`Type`\s are not necessarily equivalent to Python types or
There are cases in which a :class:`Type` can fully correspond to a Python type, classes. Aesara's :class:`TensorType`'s, like `irow`, use :class:`numpy.ndarray`
such as the `double`\ :class:`Type`, which corresponds to as the underlying Python type for performing computations and storing data, but
Python's ``float``. :class:`numpy.ndarray`\s model a much wider class of arrays than most :class:`TensorType`\s.
In other words, Aesara :class:`Type`'s try to be more specific.
For more information see :ref:`aesara_type`.
.. index:: .. index::
single: Variable single: Variable
......
差异被折叠。
...@@ -4,25 +4,21 @@ ...@@ -4,25 +4,21 @@
How Shape Information is Handled by Aesara How Shape Information is Handled by Aesara
========================================== ==========================================
It is not possible to strictly enforce the shape of an Aesara variable when Currently, information regarding shape is used in the following ways by Aesara:
building a graph since the particular value provided at run-time for a parameter of a
Aesara function may condition the shape of the Aesara variables in its graph.
Currently, information regarding shape is used in two ways in Aesara:
- To generate faster C code for the 2d convolution on the CPU and the GPU,
when the exact output shape is known in advance.
- To remove computations in the graph when we only want to know the - To remove computations in the graph when we only want to know the
shape, but not the actual value of a variable. This is done with the shape, but not the actual value of a variable. This is done with the
`Op.infer_shape` method. :meth:`Op.infer_shape` method.
- To generate faster compiled code (e.g. for a 2D convolution).
Example:
Example:
>>> import aesara >>> import aesara
>>> x = aesara.tensor.matrix('x') >>> x = aesara.tensor.matrix('x')
>>> f = aesara.function([x], (x ** 2).shape) >>> f = aesara.function([x], (x ** 2).shape)
>>> aesara.printing.debugprint(f) # doctest: +NORMALIZE_WHITESPACE >>> aesara.dprint(f)
MakeVector{dtype='int64'} [id A] '' 2 MakeVector{dtype='int64'} [id A] '' 2
|Shape_i{0} [id B] '' 1 |Shape_i{0} [id B] '' 1
| |x [id C] | |x [id C]
...@@ -30,15 +26,46 @@ MakeVector{dtype='int64'} [id A] '' 2 ...@@ -30,15 +26,46 @@ MakeVector{dtype='int64'} [id A] '' 2
|x [id C] |x [id C]
The output of this compiled function does not contain any multiplication The output of this compiled function does not contain any multiplication or
or power. Aesara has removed them to compute directly the shape of the power computations; Aesara has removed them to compute the shape of the output
output. directly.
Aesara propagates information about shapes within a graph using specialized
:class:`Op`\s and static :class:`Type` information (see :ref:`aesara_type`).
Specifying Exact Shape
======================
Currently, specifying a shape is not as easy and flexible as we wish and we plan some
upgrade. Here is the current state of what can be done:
- You can pass the shape info directly to the ``ConvOp`` created
when calling ``conv2d``. You simply set the parameters ``image_shape``
and ``filter_shape`` inside the call. They must be tuples of 4
elements. For example:
.. code-block:: python
aesara.tensor.nnet.conv2d(..., image_shape=(7, 3, 5, 5), filter_shape=(2, 3, 4, 4))
- You can use the ``SpecifyShape`` op to add shape information anywhere in the
graph. This allows to perform some optimizations. In the following example,
this makes it possible to precompute the Aesara function to a constant.
>>> import aesara
>>> x = aesara.tensor.matrix()
>>> x_specify_shape = aesara.tensor.specify_shape(x, (2, 2))
>>> f = aesara.function([x], (x_specify_shape ** 2).shape)
>>> aesara.printing.debugprint(f) # doctest: +NORMALIZE_WHITESPACE
DeepCopyOp [id A] '' 0
|TensorConstant{(2,) of 2} [id B]
Shape Inference Problem Problems with Shape inference
======================= =============================
Aesara propagates information about shape in the graph. Sometimes this Sometimes this can lead to errors. Consider this example:
can lead to errors. Consider this example:
>>> import numpy >>> import numpy
>>> import aesara >>> import aesara
...@@ -75,7 +102,7 @@ Traceback (most recent call last): ...@@ -75,7 +102,7 @@ Traceback (most recent call last):
ValueError: ... ValueError: ...
As you can see, when asking only for the shape of some computation (``join`` in the As you can see, when asking only for the shape of some computation (``join`` in the
example), an inferred shape is computed directly, without executing example above), an inferred shape is computed directly, without executing
the computation itself (there is no ``join`` in the first output or debugprint). the computation itself (there is no ``join`` in the first output or debugprint).
This makes the computation of the shape faster, but it can also hide errors. In This makes the computation of the shape faster, but it can also hide errors. In
...@@ -93,40 +120,3 @@ optimization, using the Aesara flag ...@@ -93,40 +120,3 @@ optimization, using the Aesara flag
same effect by running in the modes ``FAST_COMPILE`` (it will not apply this same effect by running in the modes ``FAST_COMPILE`` (it will not apply this
optimization, nor most other optimizations) or ``DebugMode`` (it will test optimization, nor most other optimizations) or ``DebugMode`` (it will test
before and after all optimizations (much slower)). before and after all optimizations (much slower)).
Specifying Exact Shape
======================
Currently, specifying a shape is not as easy and flexible as we wish and we plan some
upgrade. Here is the current state of what can be done:
- You can pass the shape info directly to the ``ConvOp`` created
when calling ``conv2d``. You simply set the parameters ``image_shape``
and ``filter_shape`` inside the call. They must be tuples of 4
elements. For example:
.. code-block:: python
aesara.tensor.nnet.conv2d(..., image_shape=(7, 3, 5, 5), filter_shape=(2, 3, 4, 4))
- You can use the ``SpecifyShape`` op to add shape information anywhere in the
graph. This allows to perform some optimizations. In the following example,
this makes it possible to precompute the Aesara function to a constant.
>>> import aesara
>>> x = aesara.tensor.matrix()
>>> x_specify_shape = aesara.tensor.specify_shape(x, (2, 2))
>>> f = aesara.function([x], (x_specify_shape ** 2).shape)
>>> aesara.printing.debugprint(f) # doctest: +NORMALIZE_WHITESPACE
DeepCopyOp [id A] '' 0
|TensorConstant{(2,) of 2} [id B]
Future Plans
============
The parameter "constant shape" will be added to ``aesara.shared()``. This is probably
the most frequent occurrence with ``shared`` variables. It will make the code
simpler and will make it possible to check that the shape does not change when
updating the ``shared`` variable.
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论