Могу ли я узнать некоторые подробности о реализации пользовательской функции активации в Keras?

@патапуф_ай

Относительно Как сделать пользовательскую активацию использовать только Python в 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? Я использую Керас. Итак, какие именно модули я должен импортировать? Можно пример, пожалуйста?

  2. В учебнике используется float32. Если я намерен использовать float16, должен ли я поставить

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

здесь? Затем используйте

   QPWC_32 = lambda z, sharp: QPWC_Func(z, sharp).astype(np.float16)
  1. В другом потоке Замена сигмовидной активации пользовательской активацией, еще один способ реализации пользовательская функция была предоставлена ​​@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 делать это автоматически для такой формы реализации? Если да, то в какой файл для этой реализации я должен поместить код, чтобы я мог его использовать? И какие именно модули я должен импортировать? Можно пример, пожалуйста?

Большое спасибо, действительно!


person Theron    schedule 15.06.2019    source источник
comment
в stackoverflow.com/questions/43915482/ — это процедура для создания и применения пользовательской функции активации в keras.   -  person ralf htp    schedule 15.06.2019
comment
Спасибо. Я прочитал это. Но моя пользовательская функция содержит много вещей, которые не могут быть реализованы внутренними функциями tf, например, циклы for и несколько потоков управления. Вы знаете, как реализовать их в бэкэнде?   -  person Theron    schedule 15.06.2019


Ответы (1)


вы можете попробовать сочетание Как создать пользовательскую функция активации с помощью Keras? и Замена сигмовидной активации пользовательской активацией :

определите функцию активации (перепишите свою функцию без циклов и другого управления потоком), затем примените ее:

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, что не определено

person ralf htp    schedule 15.06.2019
comment
Итак, я полагаю, что я поставлю свою собственную реализацию без последней строки кода в вашем ответе в активацию.py, а затем вызову мою пользовательскую активацию непосредственно в моем приложении. Это правильно? - person Theron; 15.06.2019
comment
замените функцию активации на свою. я не знаю, какую файловую структуру вы используете, поэтому я не могу ответить на этот вопрос... - person ralf htp; 15.06.2019
comment
Это почти работает. Тем не менее, моя функция принимает два аргумента, например def QPWC(x, sharp=1000). Когда я попытался добавить слой x = Activation('QPWC', sharp = 100, name='Decoder_output')(x). Там написано TypeError: ('Keyword argument not understood:', 'sharp'). Почему это было? - person Theron; 15.06.2019
comment
Если я просто напишу x = Activation('QPWC', 100, name='Decoder_output')(x), другая ошибка скажет TypeError: __init__() takes 2 positional arguments but 3 were given - person Theron; 15.06.2019
comment
я думаю, вы можете добавить свою новую пользовательскую активацию в произвольный слой, как в sefiks.com/2018/12/01/. вы получаете эти ошибки, потому что в python и во всех других языках программирования функция должна получить точно такое количество аргументов, которое определено, в вашем случае 2 ( python-course.eu/passing_arguments.php ) - person ralf htp; 15.06.2019
comment
На данный момент я исключил из аргумента функцию Sharp и попытаюсь просто запустить ее. однако мне было предложено поднять TypeError (использование tf.Tensor в качестве Python bool не разрешено. TypeError: использование tf.Tensor в качестве Python bool не разрешено. Используйте if t is not None: вместо if t:, чтобы проверить, определен ли тензор, и используйте такие операции TensorFlow, как как tf.cond для выполнения подграфов, зависящих от значения тензора. Я попытался заменить tf на K, но в бэкэнде нет функции where... - person Theron; 15.06.2019
comment
Я пошел в строку, где произошла ошибка. x = tf.where(orig > 0 and orig <= 0.25, 0.25 / (1+K.exp(-sharp*((x-0.125)/0.5))), x) Все тестирую, все типа tf.Tensor. Я даже использовал stackoverflow.com/questions/52435394 / проверить, та же ошибка. - person Theron; 15.06.2019
comment
Приходится писать со смартфона, см. Документацию tf.where - person ralf htp; 15.06.2019
comment
Во всяком случае, я напрямую создал класс в advanced_activations.py Keras для активации. Это позволяет мне передать в функцию еще один аргумент «острый». - person Theron; 15.06.2019
comment
Да, я прочитал это в первую очередь. Я считаю, что реализовал это правильно. - person Theron; 15.06.2019
comment
После борьбы с тем, как tf справляется с логическими операциями на tf.Tensors, мой код теперь может работать правильно. Я нашел ответ и вставил сюда. stackoverflow.com/questions/ 52435394/ Большое спасибо за вашу помощь!!! - person Theron; 15.06.2019