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)