6 votes

Comment entraîner un modèle de réseau neuronal avec des incrustations BERT au lieu d'incrustations statiques comme glove/fasttext?

Je recherche des conseils pour entraîner un modèle de réseau neuronal conventionnel avec des incrustations bert qui sont générées dynamiquement (incrustations contextuelles BERT qui génèrent différentes incrustations pour le même mot selon le contexte).

Dans un modèle de réseau neuronal classique, nous initialiserions le modèle avec des incrustations glove ou fasttext comme suit :

import torch.nn as nn 

embed = nn.Embedding(vocab_size, vector_size)

embed.weight.data.copy_(some_variable_containing_vectors)

Au lieu de copier des vecteurs statiques comme cela et de les utiliser pour l'entraînement, je veux faire passer chaque entrée à un modèle BERT et générer dynamiquement l'incrustation des mots, et les transmettre au modèle pour l'entraînement.

Dois-je travailler sur la modification de la fonction forward dans le modèle pour incorporer ces incrustations ?

Toute aide serait appréciée !

11voto

Sloth Points 158

Si vous utilisez Pytorch. Vous pouvez utiliser https://github.com/huggingface/pytorch-pretrained-BERT qui est la mise en œuvre BERT la plus populaire pour Pytorch (c'est aussi un package pip!). Ici, je vais simplement expliquer comment l'utiliser correctement.

Pour ce problème particulier, il y a 2 approches - où vous ne pouvez évidemment pas utiliser la couche Embedding:

  1. Vous pouvez incorporer la génération d'incorporations BERT dans votre pipeline de prétraitement des données. Vous devrez utiliser le propre tokenizer de BERT et le dictionnaire de mots-vers-ids. Le README du dépôt contient des exemples sur le prétraitement.

Vous pouvez écrire une boucle pour générer des jetons BERT pour des chaînes comme celle-ci (en supposant - car BERT consomme beaucoup de mémoire GPU) :

(Remarque : pour être plus correct, vous devriez également ajouter des masques d'attention - qui sont des LongTensor de 1 & 0 masquant les longueurs de phrases)

import torch
from pytorch_pretrained_bert import BertTokenizer, BertModel

taille_lot = 32
X_train, y_train = echantillons_de_fichier('train.csv') # Mettez votre fonction de chargement de données ici
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
X_train = [tokenizer.tokenize('[CLS] ' + sent + ' [SEP]') for sent in X_train] # Ajout des jetons [CLS] et [SEP] - cela peut probablement être fait de manière plus propre
bert_model = BertModel.from_pretrained('bert-base-uncased')
bert_model = bert_model.cuda()

X_train_tokens = [tokenizer.convert_tokens_to_ids(sent) for sent in X_train]
resultats = torch.zeros((len(X_test_tokens), bert_model.config.hidden_size)).long()
with torch.no_grad():
    for stidx in range(0, len(X_test_tokens), batch_size):
        X = X_test_tokens[stidx:stidx + batch_size]
        X = torch.LongTensor(X).cuda()
        _, pooled_output = bert_model(X)
        resultats[stidx:stidx + batch_size,:] = pooled_output.cpu()

Après quoi vous obtenez le tenseur resultats qui contient les embeddings calculés, que vous pouvez utiliser comme entrée pour votre modèle.

Le code complet (et plus propre) pour cela est fourni ici

Cette méthode a l'avantage de ne pas avoir à recalculer ces embeddings à chaque époque.

Avec cette méthode, par exemple pour la classification, votre modèle ne devrait consister qu'en une couche Linear(bert_model.config.hidden_size, num_labels), les entrées du modèle devraient être le tenseur resultats dans le code ci-dessus

  1. Deuxième méthode, et probablement plus propre : Si vous consultez le dépôt, vous trouverez des enveloppes pour diverses tâches (par exemple BertForSequenceClassification). Il devrait également être facile d'implémenter vos propres classes qui héritent de BertPretrainedModel et utilisent les différentes classes Bert du dépôt.

Par exemple, vous pouvez utiliser :

model = BertForSequenceClassification.from_pretrained('bert-base-uncased', labels=num_labels) # Où num_labels est le nombre d'étiquettes que vous devez classer.

Après quoi, vous pouvez continuer avec le prétraitement, jusqu'à la génération des ids de jetons. Ensuite, vous pouvez entraîner l'ensemble du modèle (mais avec un faible taux d'apprentissage par exemple Adam 3e-5 pour taille_lot = 32)

Avec cela, vous pouvez affiner les embeddings de BERT lui-même, ou utiliser des techniques comme geler BERT pendant quelques époques pour entraîner uniquement le classifieur, puis dégeler pour affiner etc. Mais c'est aussi plus coûteux en termes de calcul.

Un exemple de ceci est également fourni dans le dépôt

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X