2
votes

Réglage fin des dernières x couches de BERT

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


0 commentaires

3 Réponses :


2
votes

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 commentaires

-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)



1
votes

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!


2 commentaires

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



1
votes

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)


0 commentaires