【问题标题】:Get computational graph / expression of symbolic matrix differentiation获取符号矩阵微分的计算图/表达式
【发布时间】:2017-10-26 12:02:24
【问题描述】:

我想为神经网络编写一些自定义 CUDA 内核以加快计算速度,但如果有可以自动执行的包,我不想花时间手动区分张量表达式。

有没有可以显示符号矩阵微分表达式的python包?

我知道sympy 可以为这样的非矩阵表达式做到这一点:

def func(x):
    return 1 / x

arg_symbols = sp.symbols(inspect.getfullargspec(func).args)
sym_func = func(*arg_symbols)
s = ''
for arg in arg_symbols:
    s += '{}\n'.format(arg, sp.Lambda(arg_symbols, sym_func.diff(arg)))
# this is what I need:
print(s)
>>> Lambda(x, -1/x**2)

我知道autograd 包可以计算矩阵表达式的导数

函数被求值后,autograd 有一个所有操作的列表 执行了哪些操作以及它们依赖于哪些节点。这是 函数评估的计算图。计算 导数,我们只需将微分规则应用于每个节点 在图中。

但是有没有办法从它或一些类似的包中获得这种微分计算图?

【问题讨论】:

    标签: python symbolic-math


    【解决方案1】:

    您引用的软件包之间存在一些严重差异。差异是您无法直接从自动微分库中获取(AFAIK)计算图的原因,但您可以从基于符号的库中获取它。

    简而言之:

    • 数值微分:numpy就够了
    • 符号区分:sympy
    • 自动微分:autograd 就是一个例子

    有三种鉴别方法:

    • 数值微分:计算Delta(f(x)) / Delta(x),其中Delta(x) 是一个小差异,表示x 的变化,同时保留在f 的域中。这不是你需要的。你不需要这个包。
    • 符号微分:基于表示函数符号应用的图的构造(我有一篇关于在 Ruby 中实现符号引擎的文章here)。在这种情况下,微分是通过链式推导规则的递归应用来执行的:

      f(g(x))' = f'(g(x)) * g'(x)
      

      当这条规则被应用到整个符号图时,结果是一个带有导数的更新的符号图。优点在于导数是精确的,但是对于非常复杂的图,最终的导数图可能是不可处理的(超过内存限制或堆栈限制太深的递归)。 在 python 中sympy 实现了这种派生。另一方面,如果您有导数图,则可以对其执行操作,例如简化或替换。

      from sympy import *
      import numpy as np
      
      x = symbol('x')
      f = 1 / x
      df = diff(f, x)
      print(df)
      # -1/x**2
      
      ldf = lambdify((x), df)
      
      # Now ldf is a lambda
      x_ary = np.array([
        [[1, 2, 3], [1, 2, 3]], 
        [[1, 2, 3], [1, 2, 3]]
      ])
      y_ary = ldf(x_ary)
      
      print(xn.shape)
      # (2, 2, 3)
      print(y_ary)
      # array([[[-1.        , -0.25      , -0.11111111],
      #         [-1.        , -0.25      , -0.11111111]],
      #        [[-1.        , -0.25      , -0.11111111],
      #         [-1.        , -0.25      , -0.11111111]]])
      

      正如您所看到的,它适用于 numpy,但它涵盖了一些基本示例,而不是所有内容,实际上 sympy.matrixsympy.symbol 应该用于特定图形(例如:我不认为它可以直接处理diff(x.T A x, x) = x.T A + A x))。

      export the graph as C code 也可以,但它的功能有限,对于您的应用程序,您当然必须修改结果:

      from scipy.utilities.codegen import codegen
      
      [(cf, cc), (hf, hc)] = codegen(("df", df), "C", "df")
      
      print(hc, cc)
      

      打印出来:

    /*****************************************************
     *      Code generated with sympy 1.1.1              *
     *  See http://www.sympy.org/ for more information.  *
     *      This file is part of 'project'               *
     *****************************************************/
    
    #ifndef PROJECT__DIFF__H
    #define PROJECT__DIFF__H
    
    double df(double x);
    
    #endif
    
    /*****************************************************
     *      Code generated with sympy 1.1.1              *
     *  See http://www.sympy.org/ for more information.  *
     *      This file is part of 'project'               *
     *****************************************************/
    #include "diff.h"
    #include <math.h>
    
    double df(double x) {   
      double df_result;
      df_result = -1/pow(x, 2);
      return df_result;   
    }
    
    • 自动区分是通过autograd完成的。在这种情况下,两全其美是统一的。一方面,没有必要明确评估图形,另一方面,您不能对派生函数执行进一步的操作,同时保持导数准确。这(通常)通过在float 定义中增加一个附加字段(类似于float[2] 或更多)来完成,其中附加字段包含导数。例如,在自动微分环境中,sin 函数可能会被重载:

      def sin(x):
           return [sin(x[0]), x[1] * cos(x[0])]
      

      但正如您可以通过这种方式理解的那样,没有可用的计算图,但是您可以直接获得沿x 的精确导数的值(所有函数都必须重载)。我有一个更完整的示例(在 C 语言中,仅使用宏)here请注意,Tensorflow 内部使用自动微分而不是符号微分,但建议用户直接提供处理数值不稳定性的“显式版本”!。自动微分通常不处理数值不稳定性。

    【讨论】:

    • 是的,但是有没有办法从这些包中以某种方便的形式提取导数图? (我需要它在 CUDA 等中对其进行硬编码)
    • 您可以导出C代码,只需少量修改即可在cuda中使用
    • 能否请您详细说明如何操作?
    • 答案就在其中
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-08-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多