Dans cet article, je voudrais vous présenter ce qu'il faut faire lorsque vous essayez de model.save (ou model.to_json) avec Keras et d'obtenir
XX a des arguments dans __init__
et doit donc remplacer get_config```
. ..
Dans Keras, il existe de nombreuses couches prédéfinies telles que la couche Dense et la couche Conv, et celles-ci sont combinées pour concevoir un modèle de base. Mais plus avancé, vous devrez implémenter vos propres couches personnalisées et les ajouter à votre modèle. Par exemple, si vous souhaitez profiter du mécanisme publié dans le dernier article, il n'existe pas dans la couche prédéfinie Keras et vous devez le citer depuis Github ou l'implémenter vous-même. (Si vous souhaitez implémenter une couche personnalisée, consultez cet exemple officiel (https://github.com/keras-team/keras/blob/master/examples/antirectifier.py).) Alternativement, les débutants peuvent utiliser sans le savoir un modèle qui inclut une couche personnalisée lors de la création d'un script publié sur le noyau de kaggle. (J'ai moi-même fait face à cette erreur de cette façon.)
À propos, l'erreur
XX (nom du calque personnalisé) a des arguments dans __init__
et doit donc remplacer get_config```
ne peut pas être traitée correctement pour le modèle comprenant cette ** couche personnalisée ** À ce moment-là, Keras m'a mis en colère, "Je ne connais pas une telle couche."
Cela peut être résolu en remplaçant get_config () '' dans la classe de couche personnalisée. Plus précisément, ** faites l'argument de
init``` de la classe de couche personnalisée dans un dictionnaire, ajoutez-le à la configuration de la classe parente et retournez **, etc. get_config ()
Définir Cela signifie que l'argument de
init '' est comme le document de conception de cette couche personnalisée, donc je l'ai fait arbitrairement ** J'enseigne explicitement à Keras comment fonctionne la couche personnalisée * * Équivalent à.
À propos, les modèles enregistrés de cette manière doivent également indiquer explicitement leur couche personnalisée dans l'argument custom_objects lors du chargement. La méthode est très simple, procédez comme suit:
load_model('my_model.h5', custom_objects={'NameOfCustomLayer': NameOfCustomLayer})
Prenons l'exemple du noyau public de Kaggle. [GLRec] ResNet50 ArcFace (TF2.2)
Dans ce script, la définition réelle du modèle est effectuée ci-dessous. Le modèle de backbone est prédéfini dans Keras dans ResNet 50. (Le poids peut être obtenu non seulement en utilisant celui enregistré localement comme cette fois, mais également par le package Keras.) Les couches de regroupement et de suppression sont également prédéfinies.
En cela, vous pouvez voir que seule la couche de marge est instanciée de manière unique. Il s'agit du calque personnalisé pour ce modèle.
create_model.py
def create_model(input_shape,
n_classes,
dense_units=512,
dropout_rate=0.0,
scale=30,
margin=0.3):
backbone = tf.keras.applications.ResNet50(
include_top=False,
input_shape=input_shape,
weights=('../input/imagenet-weights/' +
'resnet50_weights_tf_dim_ordering_tf_kernels_notop.h5')
)
pooling = tf.keras.layers.GlobalAveragePooling2D(name='head/pooling')
dropout = tf.keras.layers.Dropout(dropout_rate, name='head/dropout')
dense = tf.keras.layers.Dense(dense_units, name='head/dense')
margin = ArcMarginProduct(
n_classes=n_classes,
s=scale,
m=margin,
name='head/arc_margin',
dtype='float32')
softmax = tf.keras.layers.Softmax(dtype='float32')
image = tf.keras.layers.Input(input_shape, name='input/image')
label = tf.keras.layers.Input((), name='input/label')
x = backbone(image)
x = pooling(x)
x = dropout(x)
x = dense(x)
x = margin([x, label])
x = softmax(x)
return tf.keras.Model(
inputs=[image, label], outputs=x)
Vérifiez la classe de couche de marge ArcMarginProduct. Ensuite, vous pouvez voir qu'il s'agit d'un calque personnalisé qui hérite de tf.keras.layers.Layer. (À propos, la technologie mise en œuvre s'appelle ArcFace.)
Dans cette couche personnalisée définie par moi-même, quand je n'ai pas correctement remplacé
get_config () '' ``, j'ai rencontré l'erreur au début lorsque j'ai fait model.save.
Dans ce noyau,
get_config () '' n'est pas défini dans la classe, donc si vous essayez de sauvegarder tel quel, une erreur se produira.
custom_layer.py
class ArcMarginProduct(tf.keras.layers.Layer):
'''
Implements large margin arc distance.
Reference:
https://arxiv.org/pdf/1801.07698.pdf
https://github.com/lyakaap/Landmark2019-1st-and-3rd-Place-Solution/
blob/master/src/modeling/metric_learning.py
'''
def __init__(self, n_classes, s=30, m=0.50, easy_margin=False,
ls_eps=0.0, **kwargs):
super(ArcMarginProduct, self).__init__(**kwargs)
self.n_classes = n_classes
self.s = s
self.m = m
self.ls_eps = ls_eps
self.easy_margin = easy_margin
self.cos_m = tf.math.cos(m)
self.sin_m = tf.math.sin(m)
self.th = tf.math.cos(math.pi - m)
self.mm = tf.math.sin(math.pi - m) * m
def build(self, input_shape):
super(ArcMarginProduct, self).build(input_shape[0])
self.W = self.add_weight(
name='W',
shape=(int(input_shape[0][-1]), self.n_classes),
initializer='glorot_uniform',
dtype='float32',
trainable=True,
regularizer=None)
def call(self, inputs):
X, y = inputs
y = tf.cast(y, dtype=tf.int32)
cosine = tf.matmul(
tf.math.l2_normalize(X, axis=1),
tf.math.l2_normalize(self.W, axis=0)
)
sine = tf.math.sqrt(1.0 - tf.math.pow(cosine, 2))
phi = cosine * self.cos_m - sine * self.sin_m
if self.easy_margin:
phi = tf.where(cosine > 0, phi, cosine)
else:
phi = tf.where(cosine > self.th, phi, cosine - self.mm)
one_hot = tf.cast(
tf.one_hot(y, depth=self.n_classes),
dtype=cosine.dtype
)
if self.ls_eps > 0:
one_hot = (1 - self.ls_eps) * one_hot + self.ls_eps / self.n_classes
output = (one_hot * phi) + ((1.0 - one_hot) * cosine)
output *= self.s
return output
Par conséquent, vous devez apporter les modifications suivantes.
Plus précisément, il remplace
get_config () '' et renvoie l'argument
`` init` '' et la configuration de la classe parente.
new_custom_layer.py
class ArcMarginProduct(tf.keras.layers.Layer):
'''
Implements large margin arc distance.
Reference:
https://arxiv.org/pdf/1801.07698.pdf
https://github.com/lyakaap/Landmark2019-1st-and-3rd-Place-Solution/
blob/master/src/modeling/metric_learning.py
'''
def __init__(self, n_classes, s=30, m=0.50, easy_margin=False,
ls_eps=0.0, **kwargs):
super(ArcMarginProduct, self).__init__(**kwargs)
self.n_classes = n_classes
self.s = s
self.m = m
self.ls_eps = ls_eps
self.easy_margin = easy_margin
self.cos_m = tf.math.cos(m)
self.sin_m = tf.math.sin(m)
self.th = tf.math.cos(math.pi - m)
self.mm = tf.math.sin(math.pi - m) * m
###Commencer le code ajouté
def get_config(self):
config = {
"n_classes" : self.n_classes,
"s" : self.s,
"m" : self.m,
"easy_margin" : self.easy_margin,
"ls_eps" : self.ls_eps
}
base_config = super().get_config()
return dict(list(base_config.items()) + list(config.items()))
### End
def build(self, input_shape):
super(ArcMarginProduct, self).build(input_shape[0])
self.W = self.add_weight(
name='W',
shape=(int(input_shape[0][-1]), self.n_classes),
initializer='glorot_uniform',
dtype='float32',
trainable=True,
regularizer=None)
def call(self, inputs):
X, y = inputs
y = tf.cast(y, dtype=tf.int32)
cosine = tf.matmul(
tf.math.l2_normalize(X, axis=1),
tf.math.l2_normalize(self.W, axis=0)
)
sine = tf.math.sqrt(1.0 - tf.math.pow(cosine, 2))
phi = cosine * self.cos_m - sine * self.sin_m
if self.easy_margin:
phi = tf.where(cosine > 0, phi, cosine)
else:
phi = tf.where(cosine > self.th, phi, cosine - self.mm)
one_hot = tf.cast(
tf.one_hot(y, depth=self.n_classes),
dtype=cosine.dtype
)
if self.ls_eps > 0:
one_hot = (1 - self.ls_eps) * one_hot + self.ls_eps / self.n_classes
output = (one_hot * phi) + ((1.0 - one_hot) * cosine)
output *= self.s
return output
Et le modèle doit être chargé comme suit.
load_model.py
loaded_model =keras.models.load_model("path_to_model", custom_objects = {"ArcMarginProduct": ArcMarginProduct})
Comment créer un calque personnalisé avec Keras
Sérialiser les calques personnalisés avec des keras
NotImplementedError: Layers with arguments in __init__
must override get_config
Recommended Posts