1
votes

Comment personnaliser les noms de couches Keras et l'incrémenter automatiquement.

J'essaie actuellement d'avoir plusieurs couches avec une activation personnalisée avec le nom cust_sig . Mais quand j'essaye de compiler le modèle, j'obtiens une ValueError levée car plusieurs couches ont le même nom cust_sig . Je suis conscient que je peux modifier manuellement le nom de chaque couche mais je voulais savoir s'il y a quelque chose qui peut être fait pour que _1, _2, ... soit automatiquement ajouté au nom comme il le fait pour couches intégrées. La définition du modèle se trouve ci-dessous.

Traceback (most recent call last):
  File "/home/xyz/anaconda3/envs/ctf/lib/python3.7/site-packages/tensorflow/python/training/tracking/base.py", line 457, in _method_wrapper
    result = method(self, *args, **kwargs)
  File "/home/xyz/anaconda3/envs/ctf/lib/python3.7/site-packages/tensorflow/python/keras/engine/network.py", line 315, in _init_graph_network
    self.inputs, self.outputs)
  File "/home/xyz/anaconda3/envs/ctf/lib/python3.7/site-packages/tensorflow/python/keras/engine/network.py", line 1861, in _map_graph_network
    str(all_names.count(name)) + ' times in the model. '
ValueError: The name "cust_sig" is used 3 times in the model. All layer names should be unique.

Le message d'erreur est affiché ci-dessous

# Creating a model
from tensorflow.python.keras import keras
from tensorflow.python.keras.models import Model
from tensorflow.python.keras.layers import Dense

# Custom activation function
from tensorflow.python.keras.layers import Activation
from tensorflow.python.keras import backend as K
from keras.utils.generic_utils import get_custom_objects

def custom_activation(x):
    return (K.sigmoid(x) * 5) - 1

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

data_format = 'channels_first'

spec_input = keras.layers.Input(shape=(1, 3, 256), name='spec')
x = keras.layers.Flatten(data_format)(spec_input)

for layer in range(3):
  x = Dense(512)(x)
  x = Activation('custom_activation', name='cust_sig')(x)

out = Dense(256, activation="sigmoid", name='out')(x)
model = Model(inputs=spec_input, outputs=out)


1 commentaires

La réponse de stackoverflow.com/questions/53050448/… peut également être utilisé pour référence


3 Réponses :


3
votes

Ci-dessous devrait faire:

cust_sig
cust_sig_1
cust_sig_2


Explication

:

De code source , la dénomination automatique fonctionne comme suit:

ipt = Input(shape=(1, 3, 256), name='spec')
x   = Flatten('channels_last')(ipt)
for _ in range(3):
    x   = Dense(512)(x)
    x   = CustSig(custom_activation)(x)
out = Dense(256, activation='sigmoid', name='out')(x)

model = Model(ipt, out)

print(model.layers[3].name)
print(model.layers[5].name)
print(model.layers[7].name)

Le graphe Keras est vérifié pour les objets existants avec le même nom que l'objet que vous définissez - s'il en existe, continue à s'incrémenter de 1 jusqu'à ce qu'aucun ne corresponde. Le hic, c'est que vous ne pouvez pas spécifier name = , car cela élimine la dénomination automatique selon les conditions ci-dessus.

La seule solution de contournement est probablement de définir votre propre couche d'activation personnalisée en utilisant le nom souhaité comme nom de classe comme ci-dessus, qui se manifeste comme suit:

if not name:
  self._name = backend.unique_object_name(
      generic_utils.to_snake_case(self.__class__.__name__),
      zero_based=zero_based)
else:
  self._name = name
def custom_activation(x):
    return (K.sigmoid(x) * 5) - 1

class CustSig(Layer):
    def __init__(self, my_activation, **kwargs):
        super(CustSig, self).__init__(**kwargs)
        self.supports_masking = True
        self.activation = my_activation

    def call(self, inputs):
        return self.activation(inputs)

    def get_config(self):
        config = {'activation': activations.serialize(self.activation)}
        base_config = super(Activation, self).get_config()
        return dict(list(base_config.items()) + list(config.items()))

    def compute_output_shape(self, input_shape):
        return input_shape


8 commentaires

Merci, mais comme je l'ai mentionné, je suis conscient que je peux changer le nom manuellement mais je voulais savoir comment faire en sorte que Keras ajoute l'incrément comme il le fait pour des couches définies telles que ReLU, Dense, etc.


@DeepakKadetotad Cela aiderait si vous partagiez le code complet du modèle, car le code tel quel génère des erreurs pour la couche personnalisée, ou est ignoré après le premier appel sous CustomObjectScope


J'ai inclus la définition complète du modèle et l'erreur qui est générée, j'espère que cela aide


@DeepakKadetotad Êtes-vous sûr d'avoir copié correctement votre code? La toute première ligne est une syntaxe Python invalide, la boucle for est indentée de manière incorrecte, data_format n'est pas spécifié (bien que je l'ai deviné) et ValueError: Unknown activation function: custom_activation Est lancé


Le code que j'utilise est beaucoup plus grand mais j'ai corrigé les problèmes que vous avez signalés. Cependant, je n'obtiens aucune erreur Unknown activation function: custom_activation


@DeepakKadetotad Si je ne peux pas reproduire le problème tel que vous l'avez, je ne peux pas le déboguer correctement; Je suggère d'exécuter le code que vous publiez, car l'édition actuelle ne se compile pas non plus (la première ligne est toujours défectueuse). Quoi qu'il en soit, j'ai un code suggéré sur lequel je travaille actuellement, qu'il modifie directement le vôtre ou non


@OverLordGoldDragon Utiliser le style import keras au lieu du style from tensorflow.python.keras résoudra le problème.


@zihaozhihao Je suis conscient, l'idée était que l'utilisateur fournisse un code valide, pour lui-même. Et il semble que votre réponse m'a battu - bon travail.



2
votes

Si vous vérifiez le code source de Layer class , vous pouvez trouver ces lignes qui décident du nom de la couche.

# Layer (type)                 Output Shape              Param #   
# =================================================================
# input_1 (InputLayer)         (None, 10, 10)            0         
# _________________________________________________________________
# flatten_1 (Flatten)          (None, 100)               0         
# _________________________________________________________________
# dense_1 (Dense)              (None, 512)               51712     
# _________________________________________________________________
# my_act_1 (MyAct)             (None, 512)               0         
# _________________________________________________________________
# dense_2 (Dense)              (None, 512)               262656    
# _________________________________________________________________
# my_act_2 (MyAct)             (None, 512)               0         
# _________________________________________________________________
# dense_3 (Dense)              (None, 512)               262656    
# _________________________________________________________________
# my_act_3 (MyAct)             (None, 512)               0         
# =================================================================
# Total params: 577,024
# Trainable params: 577,024
# Non-trainable params: 0

K.get_uid (prefix) obtiendra l'identifiant unique du graphique, c'est pourquoi vous voyez activation_1 , activation_2 .

Si vous voulez avoir le même effet sur votre fonction d'activation personnalisée, une meilleure façon est de définir votre propre classe qui hérite de Layer .

class MyAct(Layer):
    def __init__(self):
        super().__init__()

    def call(self, inputs):
        return (K.sigmoid(inputs) * 5) - 1 

spec_input = Input(shape=(10,10))
x = Flatten()(spec_input)
for layer in range(3):
    x = Dense(512)(x)
    x = MyAct()(x)

model = Model(spec_input, x)
model.summary()

if not name:
    prefix = self.__class__.__name__
    name = _to_snake_case(prefix) + '_' + str(K.get_uid(prefix))
self.name = name


0 commentaires

1
votes

Si vous souhaitez utiliser plusieurs fois un specific_name avec le suffixe numérique, utilisez ceci:

...
for layer in range(3):
  x = Dense(512)(x)
  x = Activation('custom_activation', name=tf.get_default_graph().unique_name("cust_sig"))(x)
...

ou

tf.compat.v1.get_default_graph().unique_name("specific_name")

Dans votre cas:

tf.get_default_graph().unique_name("specific_name")


0 commentaires