Theano是怎么处理形状信息(Shape Information)

在构建图的时候,不可能严格执行Theano变量的形状。因为在运行的时候,传递给Theano函数的某一参数的值可能影响Thenao变量的形状。目前,关于形状信息的使用在Theano中有以下两种方式:

  • 在输出形状已知的情况下,生成在CPU和GPU上进行2d卷积的更高效的C代码

  • 当我们只关心变量的形状,而不是实际值的时候,将移除图的计算。这通过Op.infer_shape完成。

例子:

import theano
import theano.tensor as T
x = T.matrix('x')
f = theano.function([x], (x ** 2).shape)
theano.printing.debugprint(f)
# MakeVector{dtype='int64'} [id A] ''   2
#  |Shape_i{0} [id B] ''   1
#  | |x [id C]
#  |Shape_i{1} [id D] ''   0
#    |x [id C]

输出结果不包含任何乘法以及幂运算。Theano已经移除了它们直接去计算输出的形状。

形状推断问题(Shape Inference Problem)

在图中,Theano将会传播形状的信息。有时,这将会导致一些错误。考虑下面的例子:

import numpy
import theano
x = theano.tensor.matrix('x')
y = theano.tensor.matrix('y')
z = theano.tensor.join(0, x, y)     # 将x,y按行拼接起来,要求x,y的列数一致
xv = numpy.random.rand(5, 4)
yv = numpy.random.rand(3, 3)

f = theano.function([x, y], z.shape)
theano.printing.debugprint(f)
# MakeVector{dtype='int64'} [id A] ''   4
#  |Elemwise{Add}[(0, 0)] [id B] ''   3
#  | |Shape_i{0} [id C] ''   1
#  | | |x [id D]
#  | |Shape_i{0} [id E] ''   2
#  |   |y [id F]
#  |Shape_i{1} [id G] ''   0
#    |x [id D]

f(xv, yv)       # 并没有报错
# array([8, 4])

f = theano.function([x,y], z)   # 直接返回z
theano.printing.debugprint(f)
# Join [id A] ''   0
#  |TensorConstant{0} [id B]
#  |x [id C]
#  |y [id D]

f(xv, yv)    # 报错
# Traceback (most recent call last):
#   ...
# ValueError: ...

正如你看到的,当仅仅访问计算结果的形状信息(z.shape)时,将会直接推断结果的形状,并不会执行计算过程(即z的具体数值)。

这使得形状的计算速度很快,但是它可能会隐藏一些错误。在这个例子中,输出结果形状的计算仅仅基于输入的第一个Theano变量,这导致返回形状信息的错误。

这种现象也可能出现在其他运算上,比如elemwise和dot。事实上,为了执行一些优化(例如,速度和稳定性),Theano从一开始就假定计算是正确的,并且是一致的。就像上述例子中一样。
你可以通过使用Theano标志optimizer_excluding=local_shape_to_shape_i运行代码(将不会执行上述提及的优化)来检测这种错误。你也可以通过在FAST_COMPILE或者DebugMode模式下执行代码,得到同样的效果。

  • FAST_COMPILE模式将不会执行这种优化,以及大部分其它的优化。

  • DebugMode模式将会在优化前以及优化后进行测试,导致运行速率更慢。

指定确切的形状

目前,指定一个形状并不像我们计划一些更新和期望的那么容易和灵活。以下情形是目前我们可以做到的:

  • 当调用conv2d时,你可以直接把形状信息传递给ConvOp。你只需要在调用时简单地设置一下image_shape和filter_shape参数就可以了。他们必须是包含4个元素的元组。例如:

theano.tensor.nnet.conv2d(..., image_shape=(7,3,5,5), filter_shape=(2,3,4,4))
  • 你可以在图的任何位置使用SpecifyShape添加位置信息。这允许执行一些优化。在接下来的例子中,这使得预先计算Theano函数为常数成为可能。

import theano
x = theano.tensor.matrix()
x_specify_shape = theano.tensor.specify_shape(x, (2,2))
f = theano.function([x], (x_specify_shape ** 2).shape)
theano.printing.debugprint(f)

xiao蜗牛
85 声望20 粉丝

{name: 'Xiao蜗牛',