如何在Tensorflow中仅使用Python构造自定义激活函数?
0 796
1

假设你需要创建一个激活函数,而仅使用预定义的张量流构建块是不可能的,那该怎么办?有没有更简单的方法?

收藏
2021-02-08 10:59 更新 anna •  5042
共 2 个回答
高赞 时间
0

要求:在我们开始之前,有两个要求才能成功。 首先,你需要能够将激活写入numpy数组上的函数。 其次,你必须能够将该函数的导数写成Tensorflow中的函数(更容易),或者在最坏情况下将其写成numpy数组上的函数。

编写激活功能: 以一个我们想使用激活函数的函数为例:

def spiky(x):
    r = x % 1
    if r <= 0.5:
        return r
    else:
        return 0

第一步是使其成为numpy函数,这很容易:

import numpy as np
np_spiky = np.vectorize(spiky)

现在我们要写它的导数。 激活梯度: 这也很容易,如果x mod 1 <0.5则为1,否则为0。

def d_spiky(x):
    r = x % 1
    if r <= 0.5:
        return 1
    else:
        return 0
np_d_spiky = np.vectorize(d_spiky)

现在的困难部分是构成TensorFlow函数。 对tensorflow fct创建一个numpy fct: 我们首先将np_d_spiky变成一个tensorflow函数。在tensorflow tf.py_func(func, inp, Tout, stateful=stateful, name=name) [doc]中有一个函数可以将任何numpy函数转换为tensorflow函数,因此我们可以使用它:

import tensorflow as tffrom tensorflow.python.framework import ops

np_d_spiky_32 = lambda x: np_d_spiky(x).astype(np.float32)

def tf_d_spiky(x,name=None):
    with tf.name_scope(name, "d_spiky", [x]) as name:
        y = tf.py_func(np_d_spiky_32,
                        [x],
                        [tf.float32],
                        name=name,
                        stateful=False)
        return y[0]

tf.py_func作用于张量列表(并返回张量列表),这就是我们拥有[x](并返回y[0])的原因。状态选项是告诉张量流函数是否总是给出相同的输出相同的输入(stateful = False),在这种情况下张量流可以简化张量流图。

在这一点上需要注意的一点是,numpy使用float64,但是tensorflow使用float32,所以你需要将函数转换为使用float32,然后才能将其转换为tensorflow函数,否则tensorflow会报错。 这就是为什么先做np_d_spiky_32。

我们现在有了tf_d_spiky,这是np_d_spiky的TensorFlow版本,如果我们想要的话,我们也不能把它用作激活函数,因为TensorFlow不知道如何计算该函数的梯度。

有一种利用tf.RegisterGradient 和tf.Graph.gradient_override_map 定义函数梯度的技巧。从harpone复制代码,我们可以修改tf.py_func函数以使其同时定义梯度:

def py_func(func, inp, Tout, stateful=True, name=None, grad=None):
    
    # Need to generate a unique name to avoid duplicates:
    rnd_name = 'PyFuncGrad' + str(np.random.randint(0, 1E+8))
    
    tf.RegisterGradient(rnd_name)(grad)  # see _MySquareGrad for grad example
    g = tf.get_default_graph()
    with g.gradient_override_map({"PyFunc": rnd_name}):
        return tf.py_func(func, inp, Tout, stateful=stateful, name=name)

现在我们差不多完成了,唯一需要传递给上述py_func函数的grad函数需要采取特殊形式。 它需要执行一个操作,以及操作之前的梯度,并在操作之后向后传播梯度。 因此,对于我们的激活函数,我们将采用以下方法:

def spikygrad(op, grad):
    x = op.inputs[0]

    n_gr = tf_d_spiky(x)
    return grad * n_gr  

激活函数只有一个输入,这就是为什么x=op.input[0]。 如果操作有许多输入,我们需要返回一个元组,每个输入一个梯度。

例如,如果操作是a-b,相对于a是1,相对于b是-1,那么我们将返回1grad,-1grad。 注意,我们需要返回输入的tensorflow函数,这就是为什么tf_d_spiky,np_d_spiky不会工作,因为它不能作用于tensorflow张量。

我们可以使用tensorflow函数编写导数:

def spikygrad2(op, grad):
    x = op.inputs[0]
    r = tf.mod(x,1)
    n_gr = tf.to_float(tf.less_equal(r, 0.5))
    return grad * n_gr  

现在我们就可以把它所有片段结合在一起:

np_spiky_32 = lambda x: np_spiky(x).astype(np.float32)
def tf_spiky(x, name=None):
    
    with tf.name_scope(name, "spiky", [x]) as name:
        y = py_func(np_spiky_32,
                        [x],
                        [tf.float32],
                        name=name,
                        grad=spikygrad)  # <-- here's the call to the gradient
        return y[0]

现在我们可以进行测试 测试

with tf.Session() as sess:

    x = tf.constant([0.2,0.7,1.2,1.7])
    y = tf_spiky(x)
    tf.initialize_all_variables().run()
    
    print(x.eval(), y.eval(), tf.gradients(y, [x])[0].eval())
[ 0.2 0.69999999 1.20000005 1.70000005] [ 0.2 0. 0.20000005 0.] [ 1. 0. 1. 0.]

成功!

Via:https://stackoverflow.com/a/39921608/14964791

收藏
2021-02-08 11:38 更新 karry •  4540
0

要求:在我们开始之前,有两个要求才能成功。 首先,你需要能够将激活写入numpy数组上的函数。 其次,你必须能够将该函数的导数写成Tensorflow中的函数(更容易),或者在最坏情况下将其写成numpy数组上的函数。

编写激活功能: 以一个我们想使用激活函数的函数为例:

def spiky(x):
    r = x % 1
    if r <= 0.5:
        return r
    else:
        return 0

第一步是使其成为numpy函数,这很容易:

import numpy as np
np_spiky = np.vectorize(spiky)

现在我们要写它的导数。 激活梯度: 这也很容易,如果x mod 1 <0.5则为1,否则为0。

def d_spiky(x):
    r = x % 1
    if r <= 0.5:
        return 1
    else:
        return 0
np_d_spiky = np.vectorize(d_spiky)

现在的困难部分是构成TensorFlow函数。 对tensorflow fct创建一个numpy fct: 我们首先将np_d_spiky变成一个tensorflow函数。在tensorflow tf.py_func(func, inp, Tout, stateful=stateful, name=name) [doc]中有一个函数可以将任何numpy函数转换为tensorflow函数,因此我们可以使用它:

import tensorflow as tffrom tensorflow.python.framework import ops

np_d_spiky_32 = lambda x: np_d_spiky(x).astype(np.float32)

def tf_d_spiky(x,name=None):
    with tf.name_scope(name, "d_spiky", [x]) as name:
        y = tf.py_func(np_d_spiky_32,
                        [x],
                        [tf.float32],
                        name=name,
                        stateful=False)
        return y[0]

tf.py_func作用于张量列表(并返回张量列表),这就是我们拥有[x](并返回y[0])的原因。状态选项是告诉张量流函数是否总是给出相同的输出相同的输入(stateful = False),在这种情况下张量流可以简化张量流图。

在这一点上需要注意的一点是,numpy使用float64,但是tensorflow使用float32,所以你需要将函数转换为使用float32,然后才能将其转换为tensorflow函数,否则tensorflow会报错。 这就是为什么先做np_d_spiky_32。

我们现在有了tf_d_spiky,这是np_d_spiky的TensorFlow版本,如果我们想要的话,我们也不能把它用作激活函数,因为TensorFlow不知道如何计算该函数的梯度。

有一种利用tf.RegisterGradient 和tf.Graph.gradient_override_map 定义函数梯度的技巧。从harpone复制代码,我们可以修改tf.py_func函数以使其同时定义梯度:

def py_func(func, inp, Tout, stateful=True, name=None, grad=None):
    
    # Need to generate a unique name to avoid duplicates:
    rnd_name = 'PyFuncGrad' + str(np.random.randint(0, 1E+8))
    
    tf.RegisterGradient(rnd_name)(grad)  # see _MySquareGrad for grad example
    g = tf.get_default_graph()
    with g.gradient_override_map({"PyFunc": rnd_name}):
        return tf.py_func(func, inp, Tout, stateful=stateful, name=name)

现在我们差不多完成了,唯一需要传递给上述py_func函数的grad函数需要采取特殊形式。 它需要执行一个操作,以及操作之前的梯度,并在操作之后向后传播梯度。 因此,对于我们的激活函数,我们将采用以下方法:

def spikygrad(op, grad):
    x = op.inputs[0]

    n_gr = tf_d_spiky(x)
    return grad * n_gr  

激活函数只有一个输入,这就是为什么x=op.input[0]。 如果操作有许多输入,我们需要返回一个元组,每个输入一个梯度。

例如,如果操作是a-b,相对于a是1,相对于b是-1,那么我们将返回1grad,-1grad。 注意,我们需要返回输入的tensorflow函数,这就是为什么tf_d_spiky,np_d_spiky不会工作,因为它不能作用于tensorflow张量。

我们可以使用tensorflow函数编写导数:

def spikygrad2(op, grad):
    x = op.inputs[0]
    r = tf.mod(x,1)
    n_gr = tf.to_float(tf.less_equal(r, 0.5))
    return grad * n_gr  

现在我们就可以把它所有片段结合在一起:

np_spiky_32 = lambda x: np_spiky(x).astype(np.float32)
def tf_spiky(x, name=None):
    
    with tf.name_scope(name, "spiky", [x]) as name:
        y = py_func(np_spiky_32,
                        [x],
                        [tf.float32],
                        name=name,
                        grad=spikygrad)  # <-- here's the call to the gradient
        return y[0]

现在我们可以进行测试 测试

with tf.Session() as sess:

    x = tf.constant([0.2,0.7,1.2,1.7])
    y = tf_spiky(x)
    tf.initialize_all_variables().run()
    
    print(x.eval(), y.eval(), tf.gradients(y, [x])[0].eval())
[ 0.2 0.69999999 1.20000005 1.70000005] [ 0.2 0. 0.20000005 0.] [ 1. 0. 1. 0.]

成功!

Via:https://stackoverflow.com/a/39921608/14964791

收藏
2021-02-08 11:38 更新 karry •  4540