zoukankan      html  css  js  c++  java
  • Theano2.1.16-基础知识之调试:常见的问题解答

    来自:http://deeplearning.net/software/theano/tutorial/shape_info.html

    Debugging Theano: FAQ and Troubleshooting

        在计算机程序中会有许多种不同的bug。该页就是来说说FAQ,即问题集的。介绍了一些处理常见问题的方法,并介绍了一些在我们自己的theano代码中,用于查找问题(即使该问题发生在theano内部)的工具: Using DebugMode.

    一、将问题独立出来/测试theano的编译器

        你可以在 DebugMode 下运行thenao的函数。该模式下会测试theano的优化,并有助于找到问题的所在,例如NaN,inf 和其他问题。

    二、分析错误信息

        甚至在默认的配置下,theano都会尝试显示有用的错误信息。考虑下面的错误代码:

    import numpy as np
    import theano
    import theano.tensor as T
    
    x = T.vector()
    y = T.vector()
    z = x + x
    z = z + y
    f = theano.function([x, y], z)
    f(np.ones((2,)), np.ones((3,)))
    运行上面的代码:

    Traceback (most recent call last):
      File "test0.py", line 10, in <module>
        f(np.ones((2,)), np.ones((3,)))
      File "/PATH_TO_THEANO/theano/compile/function_module.py", line 605, in __call__
        self.fn.thunks[self.fn.position_of_error])
      File "/PATH_TO_THEANO/theano/compile/function_module.py", line 595, in __call__
        outputs = self.fn()
    ValueError: Input dimension mis-match. (input[0].shape[0] = 3, input[1].shape[0] = 2)
    Apply node that caused the error: Elemwise{add,no_inplace}(<TensorType(float64, vector)>, <TensorType(float64, vector)>, <TensorType(float64, vector)>)
    Inputs types: [TensorType(float64, vector), TensorType(float64, vector), TensorType(float64, vector)]
    Inputs shapes: [(3,), (2,), (2,)]
    Inputs strides: [(8,), (8,), (8,)]
    Inputs scalar values: ['not scalar', 'not scalar', 'not scalar']
    
    HINT: Re-running with most Theano optimization disabled could give you a back-traces when this node was created. This can be done with by setting the Theano flags 'optimizer=fast_compile'. If that does not work, Theano optimization can be disabled with 'optimizer=None'.
    HINT: Use the Theano flag 'exception_verbosity=high' for a debugprint of this apply node.

        可以说最有用的信息通常差不多一半都来自对错误信息的分析理解,而且错误信息也是按照引起错误的顺序显示的 (ValueError: 输入维度不匹配. (input[0].shape[0] = 3, input[1].shape[0] = 2).。在它下面,给出了一些其他的信息,例如apply节点导致的错误,还有输入类型,shapes,strides 和scalar values。

        最后两个提示在调试的时候也是很有用的。使用theano flag optimizer=fast_compile 或者 optimizer=None 可以告诉你出错的那一行,而 exception_verbosity=high 会显示apply节点的调试打印(debugprint)。使用这些提示,错误信息最后会变成:

    Backtrace when the node is created:
      File "test0.py", line 8, in <module>
        z = z + y
    
    Debugprint of the apply node:
    Elemwise{add,no_inplace} [@A] <TensorType(float64, vector)> ''
     |Elemwise{add,no_inplace} [@B] <TensorType(float64, vector)> ''
     | |<TensorType(float64, vector)> [@C] <TensorType(float64, vector)>
     | |<TensorType(float64, vector)> [@C] <TensorType(float64, vector)>
     |<TensorType(float64, vector)> [@D] <TensorType(float64, vector)>
        这里我们可以看到错误可以追溯到 z = z + y这一行 。对于这个例子来说,使用 optimizer=fast_compile 是有效果的,如果它没效果,你就需要设置 optimizer=None 或者使用测试值。

    三、使用测试值

       在 v.0.4.0版本的时候,Theano有一个新机制,也就是theano.function 编译之前,graph是动态执行的。因为优化在这个阶段还没执行,所以对于用户来说就很容易定位bug的来源。这个功能可以通过配置flagtheano.config.compute_test_value启用。下面这个例子就很好的说明了这点。这里,我们使用exception_verbosity=high 和 optimizer=fast_compile,这里(个人:该例子中)不会告诉你具体出错的那一行(个人在;这里与上面有些矛盾,不过看得出来这里提示出错的是调用的函数,而上面出错定位到了语句。具体的留待以后在分析)。 optimizer=None 因而就很自然的用来代替测试值了。

    import numpy
    import theano
    import theano.tensor as T
    
    # compute_test_value is 'off' by default, meaning this feature is inactive
    theano.config.compute_test_value = 'off' # Use 'warn' to activate this feature
    
    # configure shared variables
    W1val = numpy.random.rand(2, 10, 10).astype(theano.config.floatX)
    W1 = theano.shared(W1val, 'W1')
    W2val = numpy.random.rand(15, 20).astype(theano.config.floatX)
    W2 = theano.shared(W2val, 'W2')
    
    # input which will be of shape (5,10)
    x  = T.matrix('x')
    # provide Theano with a default test-value
    #x.tag.test_value = numpy.random.rand(5, 10)
    
    # transform the shared variable in some way. Theano does not
    # know off hand that the matrix func_of_W1 has shape (20, 10)
    func_of_W1 = W1.dimshuffle(2, 0, 1).flatten(2).T
    
    # source of error: dot product of 5x10 with 20x10
    h1 = T.dot(x, func_of_W1)
    
    # do more stuff
    h2 = T.dot(h1, W2.T)
    
    # compile and call the actual function
    f = theano.function([x], h2)
    f(numpy.random.rand(5, 10))
        运行上面的代码,生成下面的错误信息:

    Traceback (most recent call last):
      File "test1.py", line 31, in <module>
        f(numpy.random.rand(5, 10))
      File "PATH_TO_THEANO/theano/compile/function_module.py", line 605, in __call__
        self.fn.thunks[self.fn.position_of_error])
      File "PATH_TO_THEANO/theano/compile/function_module.py", line 595, in __call__
        outputs = self.fn()
    ValueError: Shape mismatch: x has 10 cols (and 5 rows) but y has 20 rows (and 10 cols)
    Apply node that caused the error: Dot22(x, DimShuffle{1,0}.0)
    Inputs types: [TensorType(float64, matrix), TensorType(float64, matrix)]
    Inputs shapes: [(5, 10), (20, 10)]
    Inputs strides: [(80, 8), (8, 160)]
    Inputs scalar values: ['not scalar', 'not scalar']
    
    Debugprint of the apply node:
    Dot22 [@A] <TensorType(float64, matrix)> ''
     |x [@B] <TensorType(float64, matrix)>
     |DimShuffle{1,0} [@C] <TensorType(float64, matrix)> ''
       |Flatten{2} [@D] <TensorType(float64, matrix)> ''
         |DimShuffle{2,0,1} [@E] <TensorType(float64, 3D)> ''
           |W1 [@F] <TensorType(float64, 3D)>
    
    HINT: Re-running with most Theano optimization disabled could give you a back-traces when this node was created. This can be done with by setting the Theano flags 'optimizer=fast_compile'. If that does not work, Theano optimization can be disabled with 'optimizer=None'.
        如果上面的信息还不够, 可以通过改变一些代码,从而让theano来揭示错误的准确来源。

    # enable on-the-fly graph computations
    theano.config.compute_test_value = 'warn'
    
    ...
    
    # input which will be of shape (5, 10)
    x  = T.matrix('x')
    # provide Theano with a default test-value
    x.tag.test_value = numpy.random.rand(5, 10)
        上面的代码中,我们将符号矩阵x 赋值一个特定的测试值。这允许theano按照之前定义的那样,动态的执行符号表达式(通过对每个op调用perform方法)。因此,可以在编译通道中更准确和更早的识别到错误的来源。例如,运行上面的代码得到下面的错误信息,正确的识别到了第24行。

    Traceback (most recent call last):
      File "test2.py", line 24, in <module>
        h1 = T.dot(x, func_of_W1)
      File "PATH_TO_THEANO/theano/tensor/basic.py", line 4734, in dot
        return _dot(a, b)
      File "PATH_TO_THEANO/theano/gof/op.py", line 545, in __call__
        required = thunk()
      File "PATH_TO_THEANO/theano/gof/op.py", line 752, in rval
        r = p(n, [x[0] for x in i], o)
      File "PATH_TO_THEANO/theano/tensor/basic.py", line 4554, in perform
        z[0] = numpy.asarray(numpy.dot(x, y))
    ValueError: matrices are not aligned

     compute_test_value 机制如下方式工作:

    • 当使用Theano的 constants 和 shared 变量的时候,不需要instrument它们。
    • 一个theano变量 (例如: dmatrixvector,等等) 应该通过属性 tag.test_value来赋值特定的测试值。
    • Theano 会自动instruments 中间的结果。所以,任何从x中得到的值会自动由tag.test_value引用。

    compute_test_value 可以有以下的值:

    • off: 默认行为. 这时候调试机制是未激活的。
    • raise:动态计算测试值。任何变量都需要一个测试值,不过不需要用户来提供,这被认为是一个错误。会相应的抛出一个异常。
    • warn: Idem, 发出一个警告,而不是抛出异常。
    • ignore: 当一个变量没有测试值的时候,会静默的忽略掉中间测试值的计算。
    note:该特性暂时不能与 Scan 兼容,而且也无法和那些没有实现perform方法的ops相兼容。

    四、我如何在一个函数中输出中间值?

        Theano提供了一个‘Print’ 操作:

    x = theano.tensor.dvector('x')
    
    x_printed = theano.printing.Print('this is a very important value')(x)
    
    f = theano.function([x], x * 5)
    f_with_print = theano.function([x], x_printed * 5)
    
    #this runs the graph without any printing
    assert numpy.all( f([1, 2, 3]) == [5, 10, 15])
    
    #this runs the graph with the message, and value printed
    assert numpy.all( f_with_print([1, 2, 3]) == [5, 10, 15])
        因为 Theano 是以拓扑顺序来运行你的程序的,你没法准确的按照顺序来控制,这时候多个Print()是同时运行的。想要知道更详细的关于在哪里、什么时候、怎样计算的,查阅: “How do I Step through a Compiled Function?”.

    warning:使用这个Print Theano 操作可以防止一些theano的优化。这也可以在稳定的优化的时候使用,所以如果你使用这个Print,然后有NaN,那么就试着移除它们来看看是否是它们导致的错误 。

    五、我如何在编译前后输出一个graph

        Theano 提供两个函数 (theano.pp() 和 theano.printing.debugprint()) 来在编译的前后打印graph到终端上。这两个函数以不同的方式来打印表达式: pp() 更紧凑,而且更像数学; debugprint() 更详细 。Theano 同样提供 theano.printing.pydotprint() ,这会生成一副关于函数的png图片。

    更详细的查阅: printing – Graph Printing and Symbolic Print Statement.

    六、我编译的函数太慢了,怎么办?

        首先,确保你运行在 FAST_RUN 模式下。虽然 FAST_RUN 是默认情况下的模式,不过还是坚持要传递 mode='FAST_RUN' 给theano.function (或者 theano.make) 或者设置config.mode 为 FAST_RUN.

    其次,尝试 Theano ProfileMode. 这会告诉你现在是哪个 Apply 节点和哪个ops在你的cpu周期上。 

    提示:

    • 使用flags floatX=float32 来请求类型 float32 而不是 float64; 使用 Theano 构造函数matrix(),vector(),... 而不是 dmatrix(), dvector(),... 因为他们分别涉及到默认的类型 float32 和 float64.
    • 当你想要以相同的类型来将两个矩阵进行相乘的时候,记得以profile模式来检查在编译后的graph中没有Dot操作。当输入是矩阵而且有着相同的类型的时候,Dot会被优化成dot22。当然在使用floatX=float32 ,而且其中一个graph的输入是类型float64的时候也是这样。

    七、我如何对一个编译后的函数进行step调试

        你可以使用 MonitorMode 来检查当函数被调用的时候每个节点的输入和输出。下面的代码就展示了如何打印所有的输入和输出:

    import theano
    
    def inspect_inputs(i, node, fn):
        print i, node, "input(s) value(s):", [input[0] for input in fn.inputs],
    
    def inspect_outputs(i, node, fn):
        print "output(s) value(s):", [output[0] for output in fn.outputs]
    
    x = theano.tensor.dscalar('x')
    f = theano.function([x], [5 * x],
                        mode=theano.compile.MonitorMode(
                            pre_func=inspect_inputs,
                            post_func=inspect_outputs))
    f(3)
    
    # The code will print the following:
    #   0 Elemwise{mul,no_inplace}(TensorConstant{5.0}, x) input(s) value(s): [array(5.0), array(3.0)] output(s) value(s): [array(15.0)]
        当在 MonitorMode的情况下,使用 inspect_inputs 和 inspect_outputs 这些函数。你应该看到 [可能很多] 打印的输出。每个 Apply 节点都会被打印出来,按照graph中的位置顺序,参数到函数 perform 或者 c_code 和计算得到的输出。不可否认,如果你使用的是大张量,这会有着超多的输出要读... 不过你可以选择增加逻辑来打印一部分信息,比如打印那些用到某种op的,在程序的某个位置,或者在输入或者输出上的一个具体的值。一个典型的例子就是检测什么时候NaN的值会被加到计算中,如下面代码:

    import numpy
    
    import theano
    
    # This is the current suggested detect_nan implementation to
    # show you how it work.  That way, you can modify it for your
    # need.  If you want exactly this method, you can use
    # ``theano.compile.monitormode.detect_nan`` that will always
    # contain the current suggested version.
    
    def detect_nan(i, node, fn):
        for output in fn.outputs:
            if (not isinstance(output[0], numpy.random.RandomState) and
                numpy.isnan(output[0]).any()):
                print '*** NaN detected ***'
                theano.printing.debugprint(node)
                print 'Inputs : %s' % [input[0] for input in fn.inputs]
                print 'Outputs: %s' % [output[0] for output in fn.outputs]
                break
    
    x = theano.tensor.dscalar('x')
    f = theano.function([x], [theano.tensor.log(x) * x],
                        mode=theano.compile.MonitorMode(
                            post_func=detect_nan))
    f(0)  # log(0) * 0 = -inf * 0 = NaN
    
    # The code above will print:
    #   *** NaN detected ***
    #   Elemwise{Composite{[mul(log(i0), i0)]}} [@A] ''
    #    |x [@B]
    #   Inputs : [array(0.0)]
    #   Outputs: [array(nan)]
        为了帮助理解在你的graph中在发生的的事情,你可以禁用 local_elemwise_fusion 和所有的 inplace 优化。首先是速度优化,也就是会将逐元素操作融合到一起的优化。这会使的更难知道哪个具体的逐元素导致的问题。第二个优化就是会让某些ops的输出重写它们的输入。所以如果一个op生成一个坏的输出,你就没法看到在post_func函数中被重写之前的输入。为了禁用这些优化(0.6rc3之后的版本),如下定义MonitorMode:

    mode = theano.compile.MonitorMode(post_func=detect_nan).excluding(
        'local_elemwise_fusion', 'inplace)
     f = theano.function([x], [theano.tensor.log(x) * x],
                         mode=mode)
    note:Theano flags optimizer_includingoptimizer_excluding 和 optimizer_requiring 不会被 MonitorMode使用的,它们只会在default模式下使用。当你想要定义监视的部分的时候,你没法将 default 模式和MonitorMode一起使用。

        为了确保所有的节点的输入都是在调用到psto_func的时候可用的,你必须同样禁用垃圾回收。执行的节点垃圾回收那些theano函数不再需要的输入。这可以通过下面的flag来指定

    allow_gc=False

    八、我如何使用pdb

       在大部分情况下,你不是在交互模式下执行程序而是以python脚本的方式。在这种情况下,对python调试器的使用就变得十分的需要了,特别是当你的模型变得更加复杂的时候。中间的结果不需要有很清晰的名字,而且你会得到那些很那解读的异常,因为这是函数编译后的自然特性导致的:

    考虑这个例子脚本 (“ex.py”):

    import theano
    import numpy
    import theano.tensor as T
    
    a = T.dmatrix('a')
    b = T.dmatrix('b')
    
    f = theano.function([a, b], [a * b])
    
    # matrices chosen so dimensions are unsuitable for multiplication
    mat1 = numpy.arange(12).reshape((3, 4))
    mat2 = numpy.arange(25).reshape((5, 5))
    
    f(mat1, mat2)
        这实际上如此的简单,而且调试也是如此的容易,不过这是为了图文讲解的目的。 正如矩阵没法逐元素相乘(不匹配的shapes),我们得到了下面的异常:

    File "ex.py", line 14, in <module>
      f(mat1, mat2)
    File "/u/username/Theano/theano/compile/function_module.py", line 451, in __call__
    File "/u/username/Theano/theano/gof/link.py", line 271, in streamline_default_f
    File "/u/username/Theano/theano/gof/link.py", line 267, in streamline_default_f
    File "/u/username/Theano/theano/gof/cc.py", line 1049, in execute ValueError: ('Input dimension mis-match. (input[0].shape[0] = 3, input[1].shape[0] = 5)', Elemwise{mul,no_inplace}(a, b), Elemwise{mul,no_inplace}(a, b))

        调用的堆栈包含着一些有用的信息,从而可以追溯错误的来源。 首先是编译后的函数被调用的脚本– 不过如果你使用(不正确的参数化)预建立模块,错误也许来自这些模块中的ops,而不是这个脚本。最后一行告诉我们这个op引起了这个异常。一个“mul”涉及到变量“a”和“b”。不过这里假设我们替换了一个没有名字的中间值。 

        在了解了theano中graph结构的一些知识,我们可以使用python调试器来探索这个graph,然后我们就可以得到运行时的错误信息。特别是矩阵维度对指出错误的来源很有用。在打印出的结果中,会涉及到矩阵的4维度中的2个维度,不过因为例子的原因,我们需要其他的维度来指出错误。首先我们再次运行调试器模块,然后用“c”来运行该程序:

    python -m pdb ex.py
    > /u/username/experiments/doctmp1/ex.py(1)<module>()
    -> import theano
    (Pdb) c

        然后我们返回到上面错误的打印输出部分,不过解释器停留在了那个状态。有用的命令如下:

    • “up” 和 “down” (往下或往上移动这个调用堆栈),
    • “l” (在当前堆栈位置上打印该行周围的代码),
    • “p variable_name” (打印 ‘variable_name’的字符串解释),
    • “p dir(object_name)”, 使用python的 dir() 函数来打印一个对象的成员的列表。

        例如,键入 “up”,和一个简单的 “l” 会得到一个局部变量 “node”。 该 “node” 来自于计算graph中,所以通过跟随 “node.inputs”, “node.owner” 和 “node.outputs” 连接,就能探索这个graph。

        这个graph是纯符号的 (没有数据,只有抽象的符号操作)。为了得到实际参数的信息,你需要探索 “thunk” 对象,这是通过函数自身(一个“thunk”就是一个关于闭包的概念)来绑定了输入(和输出)的存储的 。这里,为了得到当前节点的第一个输入shape,你需要键入 “p thunk.inputs[0][0].shape”,这会打印出 “(3, 4)”.

    九、Dumping 一个函数来帮助调试

        如果你读到这里了,那么就可能是你邮件到了我们的主列表,然后我们建议你读的这部分。这部分解释了如何dump所有的传到theano.function()的参数。这有助于帮助我们在编译的时候复制问题,然这并不要求你举一个自圆其说的例子。

        为了让这工作起来,我们需要导入graph中所有op的代码,所以如果你创建了你自己的op,我们需要这份代码。然而,我们不会unpickle它,我们已经有了来自theano和Pylearn2的所有Ops:

    # Replace this line:
    theano.function(...)
    # with
    theano.function_dump(filename, ...)
    # Where filename is a string to a file that we will write to.

    然后和我们说文件名。


    参考资料:

    [1]官网:http://deeplearning.net/software/theano/tutorial/shape_info.html


  • 相关阅读:
    css样式优先级
    combobox addobject 字符串
    转:delphi dpk编译 Error: E2161 RLINK32: Unsupported 16bit resource in file xxx 问题解决
    ansistring-->unionstring 怪码
    一定要牢记软件工程的铁律
    转:oracle 删除表空间错误 提示:ora-02429:无法删除用于强制唯一
    delphi7 string 转到 PWideChar 用于连接unicode dll调用
    delphi中调用 DLL一定要注意声明函数的大小写
    slinebreak、 raise用法
    idHttp 中GET POST应用
  • 原文地址:https://www.cnblogs.com/shouhuxianjian/p/4590220.html
Copyright © 2011-2022 走看看