Graph optimization

在本节中,我们将定义一对双精度的优化。

去做

这个教程在引擎盖下太远了,对于一个只想添加另一个模式到例如在tensor.opt库的人。

我们需要另一个涵盖装饰器语法的教程,并解释如何立即注册您的优化。这就是你需要去做的。

后来,其余的更有用的时装饰器语法类型的东西不工作。(有不适合该模型的优化)。

注意

优化标记cxx_only用于插入没有Python实现的Ops的优化(因此它们只有C代码)。当没有C ++编译器可用时,将跳过使用此标记的优化。

Global and local optimizations

首先,让我们展示优化在Theano中的工作方式。有两种类型的优化:全局优化和本地优化。全局优化需要一个FunctionGraph对象(FunctionGraph是一个整个计算图的包装,你可以看到它的documentation了解更多细节),并以适当的方式导航,在过程中由其他人替换一些变量。另一方面,局部优化被定义为单个 Apply节点上的函数,并且必须返回False是要做的)或者一个新的变量列表,我们想要替换节点的输出。Navigator是一种特殊类型的全局优化,以某种方式(以拓扑顺序,反向拓扑顺序,随机顺序等)导航计算图,并在每个步骤应用一个或多个局部优化。

优化是整体的,意味着它们必须考虑可能遍布图的依赖性,应该是全局的。可以用狭窄的视角完成的优化被更好地定义为局部优化。我们想要定义的大多数优化是本地的。

Global optimization

全局优化(或优化器)是定义以下方法的对象:

class Optimizer
apply(fgraph)

这个方法接受一个FunctionGraph对象,它包含计算图,并根据优化的意图做修改。这是优化器的主要方法之一。

add_requirements(fgraph)

此方法接受一个FunctionGraph对象并添加features这些功能是apply方法正确完成其工作所需的“插件”。

optimize(fgraph)

这是Theano调用的接口函数。

默认值:这由Optimizer定义为add_requirement(fgraph); apply(fgraph)

请参阅FunctionGraph部分以了解如何定义这些方法。

Local optimization

局部优化是定义以下方法的对象:

class LocalOptimizer
transform(node)

此方法接受Apply节点,并返回False以表示不进行任何更改或匹配节点outputs当导航器应用LocalOptimizer时,作为参数传递到LocalOptimizer的节点的输出将由返回的列表替换。

One simplification rule

首先,我们定义以下简化:

我们将通过三种方式实现它:使用全局优化,使用Navigator进行局部优化,然后使用PatternSub工具。

Global optimization

下面是实现上述简化的全局优化的代码:

import theano
from theano import gof
from theano.gof import toolbox

class Simplify(gof.Optimizer):
    def add_requirements(self, fgraph):
        fgraph.attach_feature(toolbox.ReplaceValidate())
    def apply(self, fgraph):
        for node in fgraph.toposort():
            if node.op == true_div:
                x, y = node.inputs
                z = node.outputs[0]
                if x.owner and x.owner.op == mul:
                    a, b = x.owner.inputs
                    if y == a:
                        fgraph.replace_validate(z, b)
                    elif y == b:
                        fgraph.replace_validate(z, a)

simplify = Simplify()

去做

什么是add_requirements?为什么我们知道这样做?是否有其他要求我们可能想知道?

Here’s how it works: first, in add_requirements, we add the ReplaceValidate FunctionGraph Features located in toolbox – [doc TODO]. 此功能将replace_validate方法添加到fgraph,这是replace的增强版本,可执行额外的检查,以确保我们不会弄乱(注意:如果ReplaceValidate已经由另一个优化程序添加,则extend将不会执行任何操作)。简而言之,toolbox.ReplaceValidate允许访问fgraph.replace_validate,并且fgraph.replace_validate允许我们用另一个替换变量,验证约束。你可以浏览FunctionGraph Feature List的列表,看看它们中的一些是否有用于编写优化。例如,作为练习,尝试使用NodeFinder重写Simplify。(提示:你想使用它发布的方法,而不是toposort的调用!)

然后,在apply中,我们做简化的实际工作。我们首先通过拓扑顺序迭代图形。对于遇到的每个节点,我们检查它是否是一个div节点。如果没有,我们在这里没有什么可做。如果是,我们在xyz中输入除法的分子,分母和商简化仅在分子是乘法时发生,因此我们检查。如果分子是乘法,我们将两个操作数放在ab中,因此我们现在可以说z == (a * b)/ y如果y==a然后z==b并且如果y==bz==a当任一情况发生时,我们可以用ab替换z,使用fgraph.replace_validate没有。你可能想要检查关于VariableApply的文档,以更好地理解指针跟随游戏你需要获得兴趣节点的简化xyzab)。

测试时间:

>>> from theano.scalar import float64, add, mul, true_div
>>> x = float64('x')
>>> y = float64('y')
>>> z = float64('z')
>>> a = add(z, mul(true_div(mul(y, x), y), true_div(z, x)))
>>> e = gof.FunctionGraph([x, y, z], [a])
>>> e
[add(z, mul(true_div(mul(y, x), y), true_div(z, x)))]
>>> simplify.optimize(e)
>>> e
[add(z, mul(x, true_div(z, x)))]

凉!它似乎工作。你可以检查如果你把图中的\frac{xy}{y}的许多实例会发生什么。注意,它有时不会工作,原因与你写的优化的质量无关。例如,请考虑以下内容:

>>> x = float64('x')
>>> y = float64('y')
>>> z = float64('z')
>>> a = true_div(mul(add(y, z), x), add(y, z))
>>> e = gof.FunctionGraph([x, y, z], [a])
>>> e
[true_div(mul(add(y, z), x), add(y, z))]
>>> simplify.optimize(e)
>>> e
[true_div(mul(add(y, z), x), add(y, z))]

这里没有发生。The reason is: add(y, z) != add(y, z). 这是出于效率的原因。为了解决这个问题,我们首先需要使用theano.gof.opt中定义的MergeOptimizer来合并图表中代表相同计算的部分。

>>> from theano.gof.opt import MergeOptimizer
>>> MergeOptimizer().optimize(e)  
(0, ..., None, None, {}, 1, 0)
>>> e
[true_div(mul(*1 -> add(y, z), x), *1)]
>>> simplify.optimize(e)
>>> e
[x]

一旦合并完成,两次出现的add(y, z)被折叠成一个,地方。注意add(x, y)add(y, / t5>仍然被认为是不同的,因为Theano没有提示add是可交换的。你可以写你自己的全局优化器来识别与你的Ops实现的算术规则完全相同的计算。Theano可能为未来的某个地方提供设施。

注意

FunctionGraph是用于优化阶段的Theano结构。它在功能的内部使用,很少暴露给最终用户。你可以使用它来测试优化等。如果你喜欢它,但建议使用函数frontend和界面优化与optdb(我们会看到如何做到这一点)。

Local optimization

上述代码的本地版本如下:

class LocalSimplify(gof.LocalOptimizer):
    def transform(self, node):
        if node.op == true_div:
            x, y = node.inputs
            if x.owner and x.owner.op == mul:
                a, b = x.owner.inputs
                if y == a:
                    return [b]
                elif y == b:
                    return [a]
        return False
    def tracks(self):
        # This should be needed for the EquilibriumOptimizer
        # but it isn't now
        # TODO: do this and explain it
        return [] # that's not what you should do

local_simplify = LocalSimplify()

去做

修复了前面的例子...它是坏的和不完整的。

transform的定义是全局优化器的内循环,其中节点作为参数给出。如果不进行任何更改,则必须返回False否则,必须返回要替换节点的输出的列表。此列表的长度必须与node.ouputs相同。如果其中一个node.outputs没有客户端(它没有在图中使用),你可以将None放在返回的列表中删除它。

为了应用本地优化程序,我们必须与Navigator一起使用它。基本上,Navigator是一个全局优化器,它循环遍历图中的所有节点(或其明确定义的子集),并对它们应用一个或多个局部优化器。

>>> x = float64('x')
>>> y = float64('y')
>>> z = float64('z')
>>> a = add(z, mul(true_div(mul(y, x), y), true_div(z, x)))
>>> e = gof.FunctionGraph([x, y, z], [a])
>>> e
[add(z, mul(true_div(mul(y, x), y), true_div(z, x)))]
>>> simplify = gof.TopoOptimizer(local_simplify)
>>> simplify.optimize(e)
(<theano.gof.opt.TopoOptimizer object at 0x...>, 1, 5, 3, ..., ..., ...)
>>> e
[add(z, mul(x, true_div(z, x)))]

OpSub, OpRemove, PatternSub

Theano定义了一些快捷方式,使LocalOptimizers:

OpSub(op1, op2)

op1的所有用法替换为op2换句话说,涉及op1的所有Apply的输出由涉及op2的Apply节点的输出产生,其中它们的输入相同。

OpRemove(op)

以下列方式移除op的所有用法:if y = op(x) t1>,则y被替换为xop必须具有与输入一样多的输出。第一输出变为第一输入,第二输出变为第二输入,以此类推。

PatternSub(pattern1, pattern2)

用第二个模式替换第一个模式的所有出现。请参见PatternSub

from theano.gof.opt import OpSub, OpRemove, PatternSub

# Replacing add by mul (this is not recommended for primarily
# mathematical reasons):
add_to_mul = OpSub(add, mul)

# Removing identity
remove_identity = OpRemove(identity)

# The "simplify" operation we've been defining in the past few
# sections. Note that we need two patterns to account for the
# permutations of the arguments to mul.
local_simplify_1 = PatternSub((true_div, (mul, 'x', 'y'), 'y'),
                              'x')
local_simplify_2 = PatternSub((true_div, (mul, 'x', 'y'), 'x'),
                              'y')

注意

OpSubOpRemovePatternSub产生局部优化器,这意味着我们之前关于局部优化器的所有内容都适用:它们需要被包装在Navigator ,等等。

去做

wtf是导航器?

当可以使用OpSubOpRemovePatternSub自然地表示优化时,强烈建议使用它们。

WRITEME:更多关于使用PatternSub(模式的语法,如何使用约束等等)。 - 在PatternSub上有一些不错的文档)

The optimization database (optdb)

Theano导出一个称为optdb的符号,该符号作为一种有序的优化数据库。当你做一个新的优化,你必须将它插入数据库中的适当位置。此外,你可以给数据库中的每个优化一组标签,可以作为过滤的基础。

optdb的要点是,你可能想要以许多独特的模式对计算图应用许多优化。例如,你可能想做优化X,然后优化Y,然后优化Z。然后可能优化Y是包含LocalOptimizers A,B和C的EquilibriumOptimizer,它们应用于图的每个节点,直到它们都不能改变它。如果一些优化发生,我们想要一个简单的方法来关闭它们。同上如果一些优化是非常CPU密集型,我们不想花时间来应用它们。

optdb系统允许我们使用唯一的名称以及诸如“stable”,“buggy”或“cpu_intensive”等信息标签来标记每个优化,所有这些都不会影响优化的结构。

Definition of optdb

optdb是SequenceDB的实例的对象,它本身是DB的子类。存在(现在)两种类型的DB,SequenceDB和EquilibriumDB。当给定适当的查询时,DB对象构建与查询匹配的优化器。

SequenceDB包含Optimizer或DB对象。它们中的每一个具有名称,任意数量的标签和表示它们在序列中的顺序的整数。当将查询应用于SequenceDB时,其标签与查询匹配的所有优化程序将以适当的顺序插入到返回的SequenceOptimizer中。如果SequenceDB包含数据库实例,则Query将被传递给它们,并且它们返回的优化器将被放置在它们的位置。

EquilibriumDB包含LocalOptimizer或DB对象。它们每个都有一个名称和任意数量的标签。当将查询应用于EquilibriumDB时,将匹配查询的所有LocalOptimizer插入到EquilibriumOptimizer中,该返回将返回。如果SequenceDB包含数据库实例,Query将被传递给它们,并且它们返回的LocalOptimizer将被放置在它们的位置(注意,由于还没有DB可以产生LocalOptimizer对象,所以这是一个模糊点)。

Theano包含一个主要DB对象optdb,其中包含所有具有正确标记的Theano的优化器。建议在其中插入新的优化程序。如前所述,optdb是一个SequenceDB,因此,在顶层,Theano将一系列全局优化应用于计算图。

Query

查询由以下调用构建:

theano.gof.Query(include, require=None, exclude=None, subquery=None)
class Query
include

一组标记(标记是字符串),以便通过此查询获得的每个优化必须具有一个的标记。此字段是必需的,并且基本上充当搜索的起点。

require

一组标记,使得通过此查询获得的每个优化必须具有这些标记的全部

exclude

一组标记,使得通过此查询获得的每个优化必须具有这些标记的none

subquery

optdb可以包含子数据库;子查询是将子数据库的名称映射到特殊查询的字典。如果没有为子数据库提供子查询,则将再次使用原始查询。

此外,Query对象包括三个方法,includingrequiringexcluding,每个产生具有include,require和exclude集合的新Query对象包含新的[WRITEME]

Examples

下面是一些如何使用optdb上的查询生成优化器的示例:

from theano.gof import Query
from theano.compile import optdb

# This is how the optimizer for the fast_run mode is defined
fast_run = optdb.query(Query(include=['fast_run']))

# This is how the optimizer for the fast_compile mode is defined
fast_compile = optdb.query(Query(include=['fast_compile']))

# This is the same as fast_run but no optimizations will replace
# any operation by an inplace version. This assumes, of course,
# that all inplace operations are tagged as 'inplace' (as they
# should!)
fast_run_no_inplace = optdb.query(Query(include=['fast_run'],
                                        exclude=['inplace']))

Registering an Optimizer

假设我们有一个名为simplify的全局优化器。我们可以将它添加到optdb,如下所示:

# optdb.register(name, optimizer, order, *tags)
optdb.register('simplify', simplify, 0.5, 'fast_run')

一旦这样做,FAST_RUN模式将自动包括你的优化(因为你给它'fast_run'标签)。当然,已经编译的函数将没有变化。'order'参数(含义及其选择方式)将在下面的optdb structure中进行说明。

Registering a LocalOptimizer

LocalOptimizers可以通过两种方式注册:

  • 将它们包装在导航器中,并像全局优化器一样插入它们(参见上一节)。
  • 把它们放在EquilibriumDB中。

Theano定义了两个EquilibriumDB,你可以在其中进行局部优化:

canonicalize()

其中包含旨在简化图表的优化:

  • 使用基本操作用其等同替换罕见或神秘操作。
  • 以规范方式的顺序操作(任何乘法和除法序列可以被重写为包含至多一个除法,例如x*x可以重写为x**2 ;等)
  • 折叠常数(Constant(2)*Constant(2)变为Constant(4)
specialize()

其中包含旨在专门化图表的优化:

  • 使用执行相同操作(但更好)的特殊操作替换操作组合。

对于每个组,由查询选择的组的所有优化将一次又一次地应用于图表,直到它们都不适用,因此在设计时请牢记:请仔细检查您的优化是否导致fixpoint(它不能再应用的点),此时返回False以指示其作业已完成。还要小心不要撤消组中另一个局部优化器的工作,因为然后图将在两个或更多状态之间振荡,并且什么都不会完成。

optdb structure

optdb包含以下优化程序和子DB,以及给定的优先级和标记:

订购 名称 描述
0 merge1 首先合并操作
1 规范化 简化图形
2 专业 添加专门操作
49 merge2 第二合并操作
49.5 add_destroy_handler 启用内部优化
100 merge3 第三合并操作

合并操作意在将表示相同计算的图的部分组合在一起。由于优化可以以图形的两个先前不同的部分变得相似的方式来修改图形,所以我们在开始,中间和最后端合并。从技术上讲,我们只是真正需要做到这一点,但在之前的步骤减少了图的大小,因此提高了过程的效率。

有关规范化和特殊化步骤的详细信息,请参阅上一节。

add_destroy_handler步骤不是真正的优化。这是一个标记。基本上:

警告

Any optimization which inserts inplace operations in the computation graph must appear after the add_destroy_handler “optimizer”. 换句话说,任何这种优化的优先级必须是> = 50不遵守此限制可能导致创建不正确的计算图。

销毁处理程序在开始时未插入的原因是运行成本高。在假定没有内部操作的情况下运行大多数优化是更便宜的。

Profiling Theano function compilation

你发现编译一个Theano函数需要太多时间吗?你可以获得关于Theano优化的分析信息。正常的Theano profiler将为您提供非常高级的信息。缩进显示了部分之间包含的/子集关系。其输出的顶部如下所示:

Function profiling
==================
  Message: PATH_TO_A_FILE:23
  Time in 0 calls to Function.__call__: 0.000000e+00s
  Total compile time: 1.131874e+01s
    Number of Apply nodes: 50
    Theano Optimizer time: 1.152431e+00s
       Theano validate time: 2.790451e-02s
    Theano Linker time (includes C, CUDA code generation/compiling): 7.893991e-02s
       Import time 1.153541e-02s
  Time in all call to theano.grad() 4.732513e-02s

说明:

  • Total compile time: 1.131874e+01s gives the total time spent inside theano.function.
  • Number of Apply nodes: 50 means that after optimization, there are 50 apply node in the graph.
  • Theano 优化程序 时间: 1.152431e + 00s theano.function相位,其中我们优化(修改)图形,使其更快/更稳定数字/工作在GPU / ...
  • Theano 验证 时间: 2.790451e-02s 2s在优化阶段的验证子集中。
  • Theano Linker time (includes C, CUDA code generation/compiling): 7.893991e-02s means that we spent 7.9e-2s in linker phase of theano.function.
  • 导入 时间 1.153541e-02s是我们导入编译模块的链接器时间的子集。
  • 时间 全部 致电 theano.grad ) 4.732513e-02s告诉我们在theano.grad的所有电话中总共花费了4.7e-2s。这不在对theano.function的调用。

链接器阶段包括C代码的生成,g ++编译所花费的时间以及Theano构建我们返回的对象所需的时间。C代码生成和编译是缓存的,所以你第一次编译一个函数和以下的可能需要不同的执行时间量。

Detailed profiling of Theano optimizer

您可以通过设置True Theano标志config.profile_optimizer(这需要config.profile)来获取有关Theano优化器阶段的更详细的分析信息也True)。

这将输出如下:

Optimizer Profile
-----------------
 SeqOptimizer  OPT_FAST_RUN  time 1.152s for 123/50 nodes before/after optimization
   0.028s for fgraph.validate()
   0.131s for callback
   time      - (name, class, index) - validate time
   0.751816s - ('canonicalize', 'EquilibriumOptimizer', 4) - 0.004s
     EquilibriumOptimizer      canonicalize
       time 0.751s for 14 passes
       nb nodes (start, end,  max) 108 81 117
       time io_toposort 0.029s
       time in local optimizers 0.687s
       time in global optimizers 0.010s
        0 - 0.050s 27 (0.000s in global opts, 0.002s io_toposort) - 108 nodes - ('local_dimshuffle_lift', 9) ('local_upcast_elemwise_constant_inputs', 5) ('local_shape_to_shape_i', 3) ('local_fill_sink', 3) ('local_fill_to_alloc', 2) ...
        1 - 0.288s 26 (0.002s in global opts, 0.002s io_toposort) - 117 nodes - ('local_dimshuffle_lift', 8) ('local_fill_sink', 4) ('constant_folding', 4) ('local_useless_elemwise', 3) ('local_subtensor_make_vector', 3) ...
        2 - 0.044s 13 (0.002s in global opts, 0.003s io_toposort) - 96 nodes - ('constant_folding', 4) ('local_dimshuffle_lift', 3) ('local_fill_sink', 3) ('local_useless_elemwise', 1) ('local_fill_to_alloc', 1) ...
        3 - 0.045s 11 (0.000s in global opts, 0.002s io_toposort) - 91 nodes - ('constant_folding', 3) ('local_fill_to_alloc', 2) ('local_dimshuffle_lift', 2) ('local_mul_canonizer', 2) ('MergeOptimizer', 1) ...
        4 - 0.035s 8 (0.002s in global opts, 0.002s io_toposort) - 93 nodes - ('local_fill_sink', 3) ('local_dimshuffle_lift', 2) ('local_fill_to_alloc', 1) ('MergeOptimizer', 1) ('constant_folding', 1)
        5 - 0.035s 6 (0.000s in global opts, 0.002s io_toposort) - 88 nodes - ('local_fill_sink', 2) ('local_dimshuffle_lift', 2) ('local_fill_to_alloc', 1) ('local_mul_canonizer', 1)
        6 - 0.038s 10 (0.001s in global opts, 0.002s io_toposort) - 95 nodes - ('local_fill_sink', 3) ('local_dimshuffle_lift', 3) ('constant_folding', 2) ('local_fill_to_alloc', 1) ('MergeOptimizer', 1)
        7 - 0.032s 5 (0.001s in global opts, 0.002s io_toposort) - 91 nodes - ('local_fill_sink', 3) ('MergeOptimizer', 1) ('local_dimshuffle_lift', 1)
        8 - 0.034s 5 (0.000s in global opts, 0.002s io_toposort) - 92 nodes - ('local_fill_sink', 3) ('MergeOptimizer', 1) ('local_greedy_distributor', 1)
        9 - 0.031s 6 (0.001s in global opts, 0.002s io_toposort) - 90 nodes - ('local_fill_sink', 2) ('local_fill_to_alloc', 1) ('MergeOptimizer', 1) ('local_dimshuffle_lift', 1) ('local_greedy_distributor', 1)
       10 - 0.032s 5 (0.000s in global opts, 0.002s io_toposort) - 89 nodes - ('local_dimshuffle_lift', 2) ('local_fill_to_alloc', 1) ('MergeOptimizer', 1) ('local_fill_sink', 1)
       11 - 0.030s 5 (0.000s in global opts, 0.002s io_toposort) - 88 nodes - ('local_dimshuffle_lift', 2) ('local_fill_to_alloc', 1) ('MergeOptimizer', 1) ('constant_folding', 1)
       12 - 0.026s 1 (0.000s in global opts, 0.003s io_toposort) - 81 nodes - ('MergeOptimizer', 1)
       13 - 0.031s 0 (0.000s in global opts, 0.003s io_toposort) - 81 nodes -
       times - times applied - nb node created - name:
       0.263s - 15 - 0 - constant_folding
       0.096s - 2 - 14 - local_greedy_distributor
       0.066s - 4 - 19 - local_mul_canonizer
       0.046s - 28 - 57 - local_fill_sink
       0.042s - 35 - 78 - local_dimshuffle_lift
       0.018s - 5 - 15 - local_upcast_elemwise_constant_inputs
       0.010s - 11 - 4 - MergeOptimizer
       0.009s - 4 - 0 - local_useless_elemwise
       0.005s - 11 - 2 - local_fill_to_alloc
       0.004s - 3 - 6 - local_neg_to_mul
       0.002s - 1 - 3 - local_lift_transpose_through_dot
       0.002s - 3 - 4 - local_shape_to_shape_i
       0.002s - 2 - 4 - local_subtensor_lift
       0.001s - 3 - 0 - local_subtensor_make_vector
       0.001s - 1 - 1 - local_sum_all_to_none
       0.131s - in 62 optimization that where not used (display only those with a runtime > 0)
         0.050s - local_add_canonizer
         0.018s - local_mul_zero
         0.016s - local_one_minus_erf
         0.010s - local_func_inv
         0.006s - local_0_dot_x
         0.005s - local_track_shape_i
         0.004s - local_mul_switch_sink
         0.004s - local_fill_cut
         0.004s - local_one_minus_erf2
         0.003s - local_remove_switch_const_cond
         0.003s - local_cast_cast
         0.002s - local_IncSubtensor_serialize
         0.001s - local_sum_div_dimshuffle
         0.001s - local_div_switch_sink
         0.001s - local_dimshuffle_no_inplace_at_canonicalize
         0.001s - local_cut_useless_reduce
         0.001s - local_reduce_join
         0.000s - local_sum_sum
         0.000s - local_useless_alloc
         0.000s - local_reshape_chain
         0.000s - local_useless_subtensor
         0.000s - local_reshape_lift
         0.000s - local_flatten_lift
         0.000s - local_useless_slice
         0.000s - local_subtensor_of_alloc
         0.000s - local_subtensor_of_dot
         0.000s - local_subtensor_merge
   0.101733s - ('elemwise_fusion', 'SeqOptimizer', 13) - 0.000s
     SeqOptimizer      elemwise_fusion  time 0.102s for 78/50 nodes before/after optimization
       0.000s for fgraph.validate()
       0.004s for callback
       0.095307s - ('composite_elemwise_fusion', 'FusionOptimizer', 1) - 0.000s
         FusionOptimizer
          nb_iter 3
          nb_replacement 10
          nb_inconsistency_replace 0
          validate_time 0.000249624252319
          callback_time 0.00316381454468
          time_toposort 0.00375390052795
       0.006412s - ('local_add_mul_fusion', 'FusionOptimizer', 0) - 0.000s
         FusionOptimizer
          nb_iter 2
          nb_replacement 3
          nb_inconsistency_replace 0
          validate_time 6.43730163574e-05
          callback_time 0.000783205032349
          time_toposort 0.0035240650177
   0.090089s - ('inplace_elemwise_optimizer', 'FromFunctionOptimizer', 30) - 0.019s
   0.048993s - ('BlasOpt', 'SeqOptimizer', 8) - 0.000s
     SeqOptimizer      BlasOpt  time 0.049s for 81/80 nodes before/after optimization
       0.000s for fgraph.validate()
       0.003s for callback
       0.035997s - ('gemm_optimizer', 'GemmOptimizer', 1) - 0.000s
         GemmOptimizer
          nb_iter 2
          nb_replacement 2
          nb_replacement_didn_t_remove 0
          nb_inconsistency_make 0
          nb_inconsistency_replace 0
          time_canonicalize 0.00720071792603
          time_factor_can 9.05990600586e-06
          time_factor_list 0.00128507614136
          time_toposort 0.00311398506165
          validate_time 4.60147857666e-05
          callback_time 0.00174236297607
       0.004569s - ('local_dot_to_dot22', 'TopoOptimizer', 0) - 0.000s
         TopoOptimizer
           nb_node (start, end, changed) (81, 81, 5)
           init io_toposort 0.00139284133911
           loop time 0.00312399864197
           callback_time 0.00172805786133
       0.002283s - ('local_dot22_to_dot22scalar', 'TopoOptimizer', 2) - 0.000s
         TopoOptimizer
           nb_node (start, end, changed) (80, 80, 0)
           init io_toposort 0.00171804428101
           loop time 0.000502109527588
           callback_time 0.0
       0.002257s - ('local_gemm_to_gemv', 'EquilibriumOptimizer', 3) - 0.000s
         EquilibriumOptimizer          local_gemm_to_gemv
           time 0.002s for 1 passes
           nb nodes (start, end,  max) 80 80 80
           time io_toposort 0.001s
           time in local optimizers 0.000s
           time in global optimizers 0.000s
            0 - 0.002s 0 (0.000s in global opts, 0.001s io_toposort) - 80 nodes -
       0.002227s - ('use_c_blas', 'TopoOptimizer', 4) - 0.000s
         TopoOptimizer
           nb_node (start, end, changed) (80, 80, 0)
           init io_toposort 0.0014750957489
           loop time 0.00068998336792
           callback_time 0.0
       0.001632s - ('use_scipy_ger', 'TopoOptimizer', 5) - 0.000s
         TopoOptimizer
           nb_node (start, end, changed) (80, 80, 0)
           init io_toposort 0.00138401985168
           loop time 0.000202178955078
           callback_time 0.0
   0.031740s - ('specialize', 'EquilibriumOptimizer', 9) - 0.000s
     EquilibriumOptimizer      specialize
       time 0.031s for 2 passes
       nb nodes (start, end,  max) 80 78 80
       time io_toposort 0.003s
       time in local optimizers 0.022s
       time in global optimizers 0.004s
        0 - 0.017s 6 (0.002s in global opts, 0.001s io_toposort) - 80 nodes - ('constant_folding', 2) ('local_mul_to_sqr', 1) ('local_elemwise_alloc', 1) ('local_div_to_inv', 1) ('local_mul_specialize', 1)
        1 - 0.014s 0 (0.002s in global opts, 0.001s io_toposort) - 78 nodes -
       times - times applied - nb node created - name:
       0.003s - 1 - 1 - local_mul_specialize
       0.002s - 1 - 2 - local_elemwise_alloc
       0.002s - 2 - 0 - constant_folding
       0.001s - 1 - 1 - local_div_to_inv
       0.001s - 1 - 1 - local_mul_to_sqr
       0.016s - in 69 optimization that where not used (display only those with a runtime > 0)
         0.004s - crossentropy_to_crossentropy_with_softmax_with_bias
         0.002s - local_one_minus_erf
         0.002s - Elemwise{sub,no_inplace}(z, Elemwise{mul,no_inplace}(alpha subject to <function <lambda> at 0x7f475e4da050>, SparseDot(x, y))) -> Usmm{no_inplace}(Elemwise{neg,no_inplace}(alpha), x, y, z)
         0.002s - local_add_specialize
         0.001s - local_func_inv
         0.001s - local_useless_elemwise
         0.001s - local_abs_merge
         0.001s - local_track_shape_i
         0.000s - local_one_minus_erf2
         0.000s - local_sum_mul_by_scalar
         0.000s - local_elemwise_sub_zeros
         0.000s - local_cast_cast
         0.000s - local_alloc_unary
         0.000s - Elemwise{log,no_inplace}(Softmax(x)) -> <function make_out_pattern at 0x7f47619a8410>(x)
         0.000s - local_sum_div_dimshuffle
         0.000s - local_sum_alloc
         0.000s - local_dimshuffle_lift
         0.000s - local_reduce_broadcastable
         0.000s - local_grad_log_erfc_neg
         0.000s - local_advanced_indexing_crossentropy_onehot
         0.000s - local_log_erfc
         0.000s - local_log1p
         0.000s - local_log_add
         0.000s - local_useless_alloc
         0.000s - local_neg_neg
         0.000s - local_neg_div_neg
...

要了解此配置文件,请查看优化工作原理的一些说明:

  • 优化按层次结构组织。在顶层,有一个SeqOptimizer(序列优化器)。它包含其他优化器,并按照它们指定的顺序应用它们。这些子优化器可以是其他类型,但都是全局优化器。

  • 层次结构中的每个优化器将打印有关其自身的一些统计信息。打印的信息取决于优化程序的类型。

  • SeqOptimizer将在开始时打印一些统计信息:

    Optimizer Profile
    -----------------
     SeqOptimizer  OPT_FAST_RUN  time 1.152s for 123/50 nodes before/after optimization
       0.028s for fgraph.validate()
       0.131s for callback
       time      - (name, class, index) - validate time
    

    然后,它将打印,一些额外的缩进,每个子优化器的配置文件信息。这些子配置文件按照执行时间排序,而不是按执行顺序排序。

    • OPT_FAST_RUN是优化程序的名称
    • 1.152s是在该优化器中花费的总时间
    • 123/50意味着在这个优化之前,在函数图中有123个应用节点,而后只有50个。
    • 0.028s意味着花费时间调用fgraph.validate()
    • 0.131s意味着它花费了回调的时间。这是一种当FunctionGraph有变化时可以触发其他执行的机制。
    • 时间 - (名称, 类别, 索引) - 验证 时间说明如何打印每个子优化程序的信息。
    • SeqOptimizer的所有其他实例都是这样描述的。特别地,来自OPT_FAST_RUN的一些子优化器也是SeqOptimizer
  • SeqOptimizer将在开始时打印一些统计信息:

    0.751816s - ('canonicalize', 'EquilibriumOptimizer', 4) - 0.004s
      EquilibriumOptimizer      canonicalize
        time 0.751s for 14 passes
        nb nodes (start, end,  max) 108 81 117
        time io_toposort 0.029s
        time in local optimizers 0.687s
        time in global optimizers 0.010s
         0 - 0.050s 27 (0.000s in global opts, 0.002s io_toposort) - 108 nodes - ('local_dimshuffle_lift', 9) ('local_upcast_elemwise_constant_inputs', 5) ('local_shape_to_shape_i', 3) ('local_fill_sink', 3) ('local_fill_to_alloc', 2) ...
         1 - 0.288s 26 (0.002s in global opts, 0.002s io_toposort) - 117 nodes - ('local_dimshuffle_lift', 8) ('local_fill_sink', 4) ('constant_folding', 4) ('local_useless_elemwise', 3) ('local_subtensor_make_vector', 3) ...
         2 - 0.044s 13 (0.002s in global opts, 0.003s io_toposort) - 96 nodes - ('constant_folding', 4) ('local_dimshuffle_lift', 3) ('local_fill_sink', 3) ('local_useless_elemwise', 1) ('local_fill_to_alloc', 1) ...
         3 - 0.045s 11 (0.000s in global opts, 0.002s io_toposort) - 91 nodes - ('constant_folding', 3) ('local_fill_to_alloc', 2) ('local_dimshuffle_lift', 2) ('local_mul_canonizer', 2) ('MergeOptimizer', 1) ...
         4 - 0.035s 8 (0.002s in global opts, 0.002s io_toposort) - 93 nodes - ('local_fill_sink', 3) ('local_dimshuffle_lift', 2) ('local_fill_to_alloc', 1) ('MergeOptimizer', 1) ('constant_folding', 1)
         5 - 0.035s 6 (0.000s in global opts, 0.002s io_toposort) - 88 nodes - ('local_fill_sink', 2) ('local_dimshuffle_lift', 2) ('local_fill_to_alloc', 1) ('local_mul_canonizer', 1)
         6 - 0.038s 10 (0.001s in global opts, 0.002s io_toposort) - 95 nodes - ('local_fill_sink', 3) ('local_dimshuffle_lift', 3) ('constant_folding', 2) ('local_fill_to_alloc', 1) ('MergeOptimizer', 1)
         7 - 0.032s 5 (0.001s in global opts, 0.002s io_toposort) - 91 nodes - ('local_fill_sink', 3) ('MergeOptimizer', 1) ('local_dimshuffle_lift', 1)
         8 - 0.034s 5 (0.000s in global opts, 0.002s io_toposort) - 92 nodes - ('local_fill_sink', 3) ('MergeOptimizer', 1) ('local_greedy_distributor', 1)
         9 - 0.031s 6 (0.001s in global opts, 0.002s io_toposort) - 90 nodes - ('local_fill_sink', 2) ('local_fill_to_alloc', 1) ('MergeOptimizer', 1) ('local_dimshuffle_lift', 1) ('local_greedy_distributor', 1)
        10 - 0.032s 5 (0.000s in global opts, 0.002s io_toposort) - 89 nodes - ('local_dimshuffle_lift', 2) ('local_fill_to_alloc', 1) ('MergeOptimizer', 1) ('local_fill_sink', 1)
        11 - 0.030s 5 (0.000s in global opts, 0.002s io_toposort) - 88 nodes - ('local_dimshuffle_lift', 2) ('local_fill_to_alloc', 1) ('MergeOptimizer', 1) ('constant_folding', 1)
        12 - 0.026s 1 (0.000s in global opts, 0.003s io_toposort) - 81 nodes - ('MergeOptimizer', 1)
        13 - 0.031s 0 (0.000s in global opts, 0.003s io_toposort) - 81 nodes -
        times - times applied - nb node created - name:
        0.263s - 15 - 0 - constant_folding
        0.096s - 2 - 14 - local_greedy_distributor
        0.066s - 4 - 19 - local_mul_canonizer
        0.046s - 28 - 57 - local_fill_sink
        0.042s - 35 - 78 - local_dimshuffle_lift
        0.018s - 5 - 15 - local_upcast_elemwise_constant_inputs
        0.010s - 11 - 4 - MergeOptimizer
        0.009s - 4 - 0 - local_useless_elemwise
        0.005s - 11 - 2 - local_fill_to_alloc
        0.004s - 3 - 6 - local_neg_to_mul
        0.002s - 1 - 3 - local_lift_transpose_through_dot
        0.002s - 3 - 4 - local_shape_to_shape_i
        0.002s - 2 - 4 - local_subtensor_lift
        0.001s - 3 - 0 - local_subtensor_make_vector
        0.001s - 1 - 1 - local_sum_all_to_none
        0.131s - in 62 optimization that where not used (display only those with a runtime > 0)
          0.050s - local_add_canonizer
          0.018s - local_mul_zero
          0.016s - local_one_minus_erf
          0.010s - local_func_inv
          0.006s - local_0_dot_x
          0.005s - local_track_shape_i
          0.004s - local_mul_switch_sink
          0.004s - local_fill_cut
          0.004s - local_one_minus_erf2
          0.003s - local_remove_switch_const_cond
          0.003s - local_cast_cast
          0.002s - local_IncSubtensor_serialize
          0.001s - local_sum_div_dimshuffle
          0.001s - local_div_switch_sink
          0.001s - local_dimshuffle_no_inplace_at_canonicalize
          0.001s - local_cut_useless_reduce
          0.001s - local_reduce_join
          0.000s - local_sum_sum
          0.000s - local_useless_alloc
          0.000s - local_reshape_chain
          0.000s - local_useless_subtensor
          0.000s - local_reshape_lift
          0.000s - local_flatten_lift
          0.000s - local_useless_slice
          0.000s - local_subtensor_of_alloc
          0.000s - local_subtensor_of_dot
          0.000s - local_subtensor_merge
    
    • 0.751816s - ('canonicalize', 'EquilibriumOptimizer', t5> - 0.004s此行来自SeqOptimizer,并指示与子优化程序相关的信息。这意味着这个子优化器总共花费了.7s。它的名称是'canonicalize'它是EquilibriumOptimizer它在索引4处由SeqOptimizer执行。它在验证阶段花费了0.004s。

    • 所有其他行都来自EquilibriumOptimizer的分析器。

    • EquilibriumOptimizer在图中的Apply节点上执行多个传递,尝试应用本地和全局优化。在概念上,它尝试执行所有全局优化,并将所有局部优化应用于图中的所有节点。如果在通过期间没有应用优化,它停止。所以它试图找到一个平衡状态,其中没有优化被应用。当我们不知道执行优化的固定顺序时,这是有用的。

    • 时间 0.751秒 对于 14 通过 它花费了.7s,并在图表上做了14遍。

    • nb nodes (start, end, max) 108 81 117 means that at the start, the graph had 108 node, at the end, it had 81 and the maximum size was 117.

    • 然后它打印一些全局时序信息:它在io_toposort中花费了0.029秒,所有本地优化器对所有的通道共同占用了0.687s,而全局优化器共占用0.010秒。

    • 然后我们打印每次通过的时间,应用的优化,以及它们应用的次数。例如,在第0行中,local_dimshuffle_lift优化程序更改了图9的时间。

    • 然后我们打印在每个优化器中花费的时间,它们改变图的次数和它们在图中引入的节点的数量。

    • 使用该模式进行优化local_op_lift意味着具有该op的节点将被具有相同op的另一个节点替换,但会进行更接近图的输入的计算。例如,f(local_op(x))替换local_op(f(x))

    • 该模式的优化local_op_sink提升相反。例如f(local_op(x))被替换为local_op(f(x))

    • 本地优化器可以替换图中任何任意节点,而不仅仅是它作为输入接收的节点。为此,它必须返回一个dict。键是要替换的节点,值是相应的替换。

      这对于替换作为参数接收的节点的客户端很有用。