J'essaie d'affiner BERT uniquement sur les dernières couches spécifiques (disons 3 dernières couches). Je souhaite utiliser Google Colab pour la formation sur TPU. J'utilise hub.Module pour charger BERT et le peaufiner et puis utilisez la sortie affinée pour ma tâche de classification.
bert_module = hub.Module (BERT_MODEL_HUB, tags = tags, trainable = True)
hub.Module
a la possibilité de définir le modèle comme entraînable ou non entraînable, mais rien comme partiellement entraînable (juste des couches spécifiques)
Est-ce que quelqu'un sait comment je peux m'entraîner juste après 1,2 ou 3 couches de BERT en utilisant hub.Module
?
Merci
3 Réponses :
Vous pouvez le définir manuellement dans la liste des variables entraînables. Ce qui suit est mon implémentation de la couche Bert dans tensorflow-keras-
def __init__(self, n_fine_tune_layers=2, **kwargs):
Concentrez-vous sur la ligne suivante dans le code ci-dessus-
trainable_layers.append(f"encoder/layer_{str(11 - i)}")
Vous peut définir l'argument n_fine_tune_layers sur 1/2/3 par défaut ou peut-être le passer lors de la déclaration de la couche -
class BertLayer(tf.layers.Layer): def __init__( self, n_fine_tune_layers=10, pooling="first", bert_path="https://tfhub.dev/google/bert_uncased_L-12_H-768_A-12/1", **kwargs, ): self.n_fine_tune_layers = n_fine_tune_layers self.trainable = True self.output_size = 768 self.pooling = pooling self.bert_path = bert_path if self.pooling not in ["first", "mean"]: raise NameError( f"Undefined pooling type (must be either first or mean, but is {self.pooling}" ) super(BertLayer, self).__init__(**kwargs) def build(self, input_shape): self.bert = hub.Module( self.bert_path, trainable=self.trainable, name=f"{self.name}_module" ) # Remove unused layers trainable_vars = self.bert.variables if self.pooling == "first": trainable_vars = [var for var in trainable_vars if not "/cls/" in var.name] trainable_layers = ["pooler/dense"] elif self.pooling == "mean": trainable_vars = [ var for var in trainable_vars if not "/cls/" in var.name and not "/pooler/" in var.name ] trainable_layers = [] else: raise NameError( f"Undefined pooling type (must be either first or mean, but is {self.pooling}" ) # Select how many layers to fine tune for i in range(self.n_fine_tune_layers): trainable_layers.append(f"encoder/layer_{str(11 - i)}") # Update trainable vars to contain only the specified layers trainable_vars = [ var for var in trainable_vars if any([l in var.name for l in trainable_layers]) ] # Add to trainable weights for var in trainable_vars: self._trainable_weights.append(var) for var in self.bert.variables: if var not in self._trainable_weights: self._non_trainable_weights.append(var) super(BertLayer, self).build(input_shape) def call(self, inputs): inputs = [K.cast(x, dtype="int32") for x in inputs] input_ids, input_mask, segment_ids = inputs bert_inputs = dict( input_ids=input_ids, input_mask=input_mask, segment_ids=segment_ids ) if self.pooling == "first": pooled = self.bert(inputs=bert_inputs, signature="tokens", as_dict=True)[ "pooled_output" ] elif self.pooling == "mean": result = self.bert(inputs=bert_inputs, signature="tokens", as_dict=True)[ "sequence_output" ] mul_mask = lambda x, m: x * tf.expand_dims(m, axis=-1) masked_reduce_mean = lambda x, m: tf.reduce_sum(mul_mask(x, m), axis=1) / ( tf.reduce_sum(m, axis=1, keepdims=True) + 1e-10) input_mask = tf.cast(input_mask, tf.float32) pooled = masked_reduce_mean(result, input_mask) else: raise NameError(f"Undefined pooling type (must be either first or mean, but is {self.pooling}") return pooled def compute_output_shape(self, input_shape): return (input_shape[0], self.output_size)
-1 cette réponse est incorrecte. Cela rend simplement les N dernières variables entraînables, mais 1) chaque couche BERT se compose de plus d'une variable et 2) les couches sont triées lexographiquement. (Note latérale: veuillez citer la source du code)
Le code ci-dessous est simplement extrait de ce post ( https: / /towardsdatascience.com/bert-in-keras-with-tensorflow-hub-76bcbc9417b ) et n'est pas correct.
trainable_vars = self.bert.variables trainable_vars = trainable_vars[-self.n_fine_tune_layers:]
Renvoie les variables dans l'ordre alphabétique, pas dans l'ordre réel des calques. En tant que tel, il renverra la couche 11 avant la couche 4, etc. Ce n'est pas ce que vous voulez.
Je n'ai pas trouvé exactement comment obtenir l'ordre réel dans lequel les couches sont implémentées, mais je mettrai à jour cette réponse quand je le ferai!
l'avez-vous compris?
Nauman, j'ai en fait fini par utiliser la base de code BERT d'origine et j'ai modifié à partir de là - voir mon implémentation complète ici github.com/BountyCountry/BERT-Multilabel-Classifier
En modifiant le code du article de blog , nous peut sélectionner les couches correctes. Ceci est également résolu dans le repo lié au billet de blog, bien que de manière moins performante.
lien vers la demande d'extraction
def build(self, input_shape): self.bert = hub.Module( bert_path, trainable=self.trainable, name="{}_module".format(self.name) ) trainable_vars = self.bert.variables # Remove unused layers trainable_vars = [var for var in trainable_vars if not "/cls/" in var.name] # ===========Replace incorrect line with:==================== # Select how many layers to fine tune. note: this is wrong in the original code import re def layer_number(var): '''Get which layer a variable is in''' m = re.search(r'/layer_(\d+)/', var.name) if m: return int(m.group(1)) else: return None layer_numbers = list(map(layer_number, trainable_vars)) n_layers = max(n for n in layer_numbers if n is not None) + 1 # layers are zero-indexed trainable_vars = [var for n, var in zip(layer_numbers, trainable_vars) if n is not None and n >= n_layers - self.n_fine_tune_layers] # ========== Until here ==================== # Add to trainable weights self._trainable_weights.extend(trainable_vars) # Add non-trainable weights for var in self.bert.variables: if var not in self._trainable_weights: self._non_trainable_weights.append(var) super(BertLayer, self).build(input_shape)