如何在Tensorflow中仅使用Python制作自定义激活功能?

pat*_*_ai 52 python neural-network deep-learning tensorflow activation-function

假设您需要创建一个仅使用预定义的tensorflow构建块无法实现的激活功能,您可以做什么?

所以在Tensorflow中可以创建自己的激活功能.但它很复杂,你必须用C++编写它并重新编译整个tensorflow [1] [2].

有更简单的方法吗?

pat*_*_ai 78

就在这里!

信用: 很难找到信息并使其正常工作,但这里是一个复制从这里这里找到的原则和代码的例子.

要求: 在我们开始之前,有两个要求才能成功.首先,您需要能够将激活编写为numpy数组上的函数.其次,您必须能够将该函数的派生函数作为Tensorflow中的函数(更简单)或最坏情况下编写为numpy数组上的函数.

写入激活功能:

那么让我们以我们想要使用激活函数的函数为例:

def spiky(x):
    r = x % 1
    if r <= 0.5:
        return r
    else:
        return 0
Run Code Online (Sandbox Code Playgroud)

其外观如下: 尖刺激活

第一步是将它变成一个numpy函数,这很容易:

import numpy as np
np_spiky = np.vectorize(spiky)
Run Code Online (Sandbox Code Playgroud)

现在我们应该写出它的衍生物.

激活的梯度: 在我们的例子中它很容易,如果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)
Run Code Online (Sandbox Code Playgroud)

现在为了制作TensorFlow功能的困难部分.

使一个numpy fct成为张量流fct: 我们首先将np_d_spiky变成张量流函数.tensorflow tf.py_func(func, inp, Tout, stateful=stateful, name=name) [doc]中有一个函数可以将任何numpy函数转换为tensorflow函数,因此我们可以使用它:

import tensorflow as tf
from 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]
Run Code Online (Sandbox Code Playgroud)

tf.py_func作用于张量列表(并返回张量列表),这就是我们拥有[x](并返回y[0])的原因.该stateful方法是,告诉tensorflow函数总是给出相同的输入(状态= FALSE)在这种情况下tensorflow只需将tensorflow图,这是我们的情况下,将可能在大多数情况下,相同的情况下输出.在这一点上要注意的一件事是使用了numpy float64但是tensorflow使用了float32所以你需要将你的函数转换为使用float32才能将它转换为张量流函数,否则tensorflow会抱怨.这就是我们需要先做的原因np_d_spiky_32.

渐变怎么样?仅仅执行上述操作的问题是,即使我们现在具有张tf_d_spiky量流版本np_d_spiky,我们也不能将其用作激活函数,因为tensorflow不知道如何计算该函数的梯度.

哈克获得渐变:正如上面提到的来源中所解释的那样,使用tf.RegisterGradient [doc]tf.Graph.gradient_override_map [doc]来定义函数的渐变是一种破解.从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)
Run Code Online (Sandbox Code Playgroud)

现在我们差不多完成了,唯一的事情就是我们需要传递给上面的py_func函数的grad函数需要采用一种特殊的形式.它需要在操作之前进行操作和先前的渐变,并在操作之后向后传播渐变.

渐变功能:因此对于我们的尖刺激活功能,我们将如何做到:

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

    n_gr = tf_d_spiky(x)
    return grad * n_gr  
Run Code Online (Sandbox Code Playgroud)

激活功能只有一个输入,这就是原因x = op.inputs[0].如果操作有很多输入,我们需要返回一个元组,每个输入一个渐变.例如,如果操作是a-b相对于梯度a+1和相对于b-1,所以我们必须return +1*grad,-1*grad.请注意,我们需要返回输入的tensorflow函数,这就是为什么需要tf_d_spiky,np_d_spiky因为它不能对张量流量张量起作用而不起作用.或者我们可以使用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  
Run Code Online (Sandbox Code Playgroud)

将它们结合在一起:既然我们拥有所有的部分,我们可以将它们组合在一起:

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]
Run Code Online (Sandbox Code Playgroud)

现在我们完成了.我们可以测试一下.

测试:

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())
Run Code Online (Sandbox Code Playgroud)

[0.2 0.69999999 1.20000005 1.70000005] [0.2 0. 0.20000005 0.] [1. 0. 1. 1. 0.]

成功!

  • @lahwran这不是一个你想要在现实生活中使用的激活功能.它只是一个如何在需要时实现自定义激活功能的示例. (2认同)
  • 是的,它是可行的:)但它并没有在真正的学习问题中尝试使用网络,我需要为我的目的和所学到的功能提供比其复杂得多的激活功能,但是对于此处的帖子,我只放置了一个玩具我没有尝试学习的激活功能。 (2认同)

Mr *_*der 14

为什么不简单地使用tensorflow中已有的函数来构建新函数?

对于答案中spiky功能,这可能如下所示

def spiky(x):
    r = tf.floormod(x, tf.constant(1))
    cond = tf.less_equal(r, tf.constant(0.5))
    return tf.where(cond, r, tf.constant(0))
Run Code Online (Sandbox Code Playgroud)

我会认为这要容易得多(甚至不需要计算任何渐变),除非你想做真正奇特的事情,否则我几乎无法想象tensorflow不能提供构建高度复杂的激活函数的构建块.

  • 问题的全部意义在于:当您无法使用 tf 原语制定激活函数时,您会怎么做。 (3认同)
  • @patapouf_ai我已经预料到了,但是您的问题尚不清楚。由于这个问题的普遍性,我认为最好也指向该解决方案(对于那些对tensorflow经验很少尝试创建自己的激活函数的人)。 (2认同)
  • 非常有用的答案,除了您可能想要像这样使用张量x的形状:def spiky(x):r = tf.floormod(x,tf.constant(1,shape = x.shape))cond = tf.less_equal( r,tf.constant(0.5,shape = x.shape))返回tf.where(cond,r,tf.constant(0,shape = x.shape))否则可能会出现这种错误:ValueError:Shape must在``cond_xx / Switch''中排名xx,但在xx中排名(op:'Switch') (2认同)