我可以了解有关在Keras中实现自定义激活功能的一些详细信息吗?

时间:2019-06-15 08:47:41

标签: tensorflow keras activation-function

@patapouf_ai

How to make a custom activation function with only Python in Tensorflow?

有关

我是Python,keras和tf的新手。我使用上面的方法实现了分段的常量自定义激活函数,如下所示:


import tensorflow as tf
from tensorflow.python.framework import ops
from keras.backend.tensorflow_backend import get_session
import numpy as np


def QPWC_Func(z, sharp):
    s =  np.zeros(z.shape)
    ds = np.zeros(z.shape)

    for m in np.arange(0, len(z)):
        if z[m] <= 0:
            s[m] = 0
            ds[m] = 0
        elif (z[m] > 0) and (z[m] <= 0.25):
            s[m] = 0.25 / (1+np.exp(-sharp*((z[m]-0.125)/0.25)))
            ds[m] = sharp/0.25 * s[m] * (1-s[m]/0.25)
        elif (z[m] > 0.25) and (z[m] <= 0.5):
            s[m] = 0.25 / (1+np.exp(-sharp*((z[m]-0.375)/0.25))) + 0.25
            ds[m] = sharp/0.25 * (s[m]-0.25) * (1-(s[m]-0.25)/0.25)
        elif (z[m] > 0.5) and (z[m] <= 0.75):
            s[m] = 0.25 / (1+np.exp(-sharp*((z[m]-0.625)/0.25))) + 0.5
            ds[m] = sharp/0.25 * (s[m]-0.5) * (1-(s[m]-0.5)/0.25)
        elif (z[m] > 0.75) and (z[m] <= 1):
            # If z is larger than 0.75, the gradient shall be descended to it faster than other cases
            s[m] = 0.5 / (1+np.exp(-sharp*((z[m]-1)/0.5))) + 0.75
            ds[m] = sharp/0.5 * (s[m]-0.75) * (1-(s[m]-0.75)/0.5)
        else:
            s[m] = 1
            ds[m] = 0

    return s

def Derv_QPWC_Func(z, sharp):
    s =  np.zeros(z.shape)
    ds = np.zeros(z.shape)

    for m in np.arange(0, len(z)):
        if z[m] <= 0:
            s[m] = 0
            ds[m] = 0
        elif (z[m] > 0) and (z[m] <= 0.25):
            s[m] = 0.25 / (1+np.exp(-sharp*((z[m]-0.125)/0.25)))
            ds[m] = sharp/0.25 * s[m] * (1-s[m]/0.25)
        elif (z[m] > 0.25) and (z[m] <= 0.5):
            s[m] = 0.25 / (1+np.exp(-sharp*((z[m]-0.375)/0.25))) + 0.25
            ds[m] = sharp/0.25 * (s[m]-0.25) * (1-(s[m]-0.25)/0.25)
        elif (z[m] > 0.5) and (z[m] <= 0.75):
            s[m] = 0.25 / (1+np.exp(-sharp*((z[m]-0.625)/0.25))) + 0.5
            ds[m] = sharp/0.25 * (s[m]-0.5) * (1-(s[m]-0.5)/0.25)
        elif (z[m] > 0.75) and (z[m] <= 1):
            # If z is larger than 0.75, the gradient shall be descended to it faster than other cases
            s[m] = 0.5 / (1+np.exp(-sharp*((z[m]-1)/0.5))) + 0.75
            ds[m] = sharp/0.5 * (s[m]-0.75) * (1-(s[m]-0.75)/0.5)
        else:
            s[m] = 1
            ds[m] = 0


    return ds

QPWC = np.vectorize(QPWC_Func)
Derv_QPWC = np.vectorize(Derv_QPWC_Func)

Derv_QPWC32 = lambda z, sharp: Derv_QPWC_Func(z, sharp).astype(np.float32)

QPWC_32 = lambda z, sharp: QPWC_Func(z, sharp).astype(np.float32)

# tf.py_func acts on lists of tensors (and returns a list of tensors), that is why we have [z, sharp] (and return y[0]).
def tf_QPWC_Fun32(z, sharp, name=None):

    with tf.name_scope(name, "QPWC_Func", [z, sharp]) as name:
        y = py_func(QPWC_32,
                        [z, sharp],
                        [tf.float32],
                        name=name,
                        grad=Derv_QPWC_Func32)  # <-- here's the call to the gradient
        return y[0]

# The stateful option is to tell tensorflow whether the function always gives the same output for the same input (stateful = False) 
def tf_Derv_QPWC_Func32(z, sharp, name=None):
    with tf.name_scope(name, "Derv_QPWC_Func", [z, sharp]) as name:
        y = tf.py_func(Derv_QPWC32,
                        [z, sharp],
                        [tf.float32],
                        name=name,
                        stateful=False)
        return y[0]

# A hack to define gradients of a function using tf.RegisterGradient and tf.Graph.gradient_override_map     
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) 

def Derv_QPWC_Func32(op, grad):
    z = op.inputs[0]
    sharp = op.inputs[1]

    n_gr = tf_Derv_QPWC_Func32(z, sharp)
    return grad * n_gr    

with tf.Session() as sess:

    x = tf.constant([0.2,0.7,1,0.75])
    y = tf_QPWC_Fun32(x, 100)
    tf.initialize_all_variables().run()

    print(x.eval(), y.eval(), tf.gradients(y, [x])[0].eval())

我有几个问题: 1.如您所见,像Sigmoid一样,我的函数实际上可以同时计算前馈输出及其斜率。那么tf中是否有一种方法可以只一次调用一次函数,以便获得两个结果?

  1. 我对自定义函数有两个输入,当我运行它时,python弹出以下错误:

  File "D:\TProgramFiles\Anaconda3\envs\keras-gpu\lib\site-packages\tensorflow\python\ops\gradients_impl.py", line 664, in gradients
    unconnected_gradients)

  File "D:\TProgramFiles\Anaconda3\envs\keras-gpu\lib\site-packages\tensorflow\python\ops\gradients_impl.py", line 972, in _GradientsHelper
    _VerifyGeneratedGradients(in_grads, op)

  File "D:\TProgramFiles\Anaconda3\envs\keras-gpu\lib\site-packages\tensorflow\python\ops\gradients_impl.py", line 335, in _VerifyGeneratedGradients
    "inputs %d" % (len(grads), op.node_def, len(op.inputs)))

ValueError: Num gradients 1 generated for op name: "QPWC_Func_10"
op: "PyFunc"
input: "Const_11"
input: "QPWC_Func_10/input_1"
attr {
  key: "Tin"
  value {
    list {
      type: DT_FLOAT
      type: DT_INT32
    }
  }
}
attr {
  key: "Tout"
  value {
    list {
      type: DT_FLOAT
    }
  }
}
attr {
  key: "_gradient_op_type"
  value {
    s: "PyFuncGrad64499082"
  }
}
attr {
  key: "token"
  value {
    s: "pyfunc_11"
  }
}
 do not match num inputs 2

是什么意思?我在哪里犯错了?

  1. 完成所有操作后,应将代码放在哪个文件中以便可以使用它?还是我应该编写一个独立文件然后自己导入QPWC_Func?我正在使用Keras。那么我应该准确导入哪些模块?请给我一个例子吗?

  2. 本教程中使用了
  3. float32。如果我打算使用float16,我应该放

            K.set_floatx('float16')
            K.set_epsilon(1e-4)

在这里?然后使用

   QPWC_32 = lambda z, sharp: QPWC_Func(z, sharp).astype(np.float16)
  1. 在另一个线程Replacing sigmoid activation with custom activation中,@ Alexandre Passos提供了另一种实现自定义功能的方法
def custom_activation_4(x):
  orig = x
  x = tf.where(orig < -6, tf.zeros_like(x), x)
  x = tf.where(orig >= -6 and orig < -4, (0.0078*x + 0.049), x)
  x = tf.where(orig >= -4 and orig < 0, (0.1205*x + 0.5), x)
  x = tf.where(orig >= 0 and orig < 4, (0.1205*x + 0.5), x)
  x = tf.where(orig  >= 4 and orig < 6, (0.0078*x + 0.951), x)
  return tf.where(orig >= 6, 1, x)

我认为我也可以通过那个实现我的。那个似乎还没有提供如何计算梯度。 tf会自动为该形式的实现执行此操作吗?如果是这样,那么对于该实现,我应该将代码放在哪个文件中以便可以使用它?我应该准确导入哪些模块?请给我一个例子吗?

真的,非常感谢!

1 个答案:

答案 0 :(得分:0)

您可以尝试将How do you create a custom activation function with Keras?Replacing sigmoid activation with custom activation混合使用:

定义激活函数(不使用循环和其他流控制来重写函数),然后应用它:

from keras.layers import Activation
from keras import backend as K
from keras.utils.generic_utils import get_custom_objects

def custom_activation(x):
  orig = x
  x = tf.where(orig < -6, tf.zeros_like(x), x)
  x = tf.where(orig >= -6 and orig < -4, (0.0078*x + 0.049), x)
  x = tf.where(orig >= -4 and orig < 0, (0.1205*x + 0.5), x)
  x = tf.where(orig >= 0 and orig < 4, (0.1205*x + 0.5), x)
  x = tf.where(orig  >= 4 and orig < 6, (0.0078*x + 0.951), x)
  return tf.where(orig >= 6, 1, x)

get_custom_objects().update({'custom_activation': Activation(custom_activation)})

model.add(Activation(custom_activation))

通常,流量控制元素的问题是,它们是为单个浮点数定义的,而您想将激活函数应用于TF中的张量,未定义的内容