![]() | ![]() | ![]() | ![]() | ![]() |
BERT można wykorzystać do rozwiązania wielu problemów związanych z przetwarzaniem języka naturalnego. Dowiesz się, jak dostroić BERT do wielu zadań z benchmarku GLUE :
CoLA (Corpus of Linguistic Acceptability): Czy zdanie jest poprawne gramatycznie?
SST-2 (Stanford Sentiment Treebank): Zadanie polega na przewidywaniu nastroju danego zdania.
MRPC (Microsoft Research Paraphrase Corpus): Określ, czy para zdań jest semantycznie równoważna.
QQP (Quora Question Pairs2): Określ, czy para pytań jest semantycznie równoważna.
MNLI (Multi-Genre Natural Language Inference): Biorąc pod uwagę zdanie zakładające i zdanie hipotezy, zadaniem jest przewidzenie, czy przesłanka pociąga za sobą hipotezę (wynikanie), zaprzecza hipotezie (sprzeczność), czy też żadna (neutralna).
QNLI (wnioskowanie z języka naturalnego odpowiadające na pytania): Zadanie polega na ustaleniu, czy zdanie kontekstowe zawiera odpowiedź na pytanie.
RTE (Recognizing Textual Entailment): Określ, czy zdanie pociąga za sobą daną hipotezę, czy nie.
WNLI (Winograd Natural Language Inference): Zadanie polega na przewidywaniu, czy zdanie z podstawionym zaimkiem jest zawarte w zdaniu oryginalnym.
Ten samouczek zawiera pełny, kompleksowy kod do trenowania tych modeli na TPU. Możesz także uruchomić ten notebook na GPU, zmieniając jedną linię (opisaną poniżej).
W tym notatniku będziesz:
- Załaduj model BERT z TensorFlow Hub
- Wybierz jedno z zadań GLUE i pobierz zestaw danych
- Przetwórz wstępnie tekst
- Dostosuj BERT (przykłady podano dla zestawów danych zawierających jedno i wiele zdań)
- Zapisz wytrenowany model i użyj go
Ustawiać
Użyjesz oddzielnego modelu do wstępnego przetworzenia tekstu przed użyciem go do dostrojenia BERT. Ten model zależy od tensorflow / text , które zainstalujesz poniżej.
pip install -q -U tensorflow-text
Użyjesz optymalizatora AdamW z tensorflow / models do dostrojenia BERT, który również zainstalujesz.
pip install -q -U tf-models-official
pip install -U tfds-nightly
import os
import tensorflow as tf
import tensorflow_hub as hub
import tensorflow_datasets as tfds
import tensorflow_text as text # A dependency of the preprocessing model
import tensorflow_addons as tfa
from official.nlp import optimization
import numpy as np
tf.get_logger().setLevel('ERROR')
Następnie skonfiguruj TFHub do odczytywania punktów kontrolnych bezpośrednio z zasobników Cloud Storage TFHub. Jest to zalecane tylko w przypadku uruchamiania modeli TFHub na TPU.
Bez tego ustawienia TFHub pobierałby skompresowany plik i lokalnie wyodrębniał punkt kontrolny. Próba załadowania z tych plików lokalnych zakończy się niepowodzeniem z następującym błędem:
InvalidArgumentError: Unimplemented: File system scheme '[local]' not implemented
Dzieje się tak, ponieważ TPU może czytać tylko bezpośrednio z zasobników Cloud Storage .
os.environ["TFHUB_MODEL_LOAD_FORMAT"]="UNCOMPRESSED"
Połącz się z pracownikiem TPU
Poniższy kod łączy się z procesem roboczym TPU i zmienia domyślne urządzenie TensorFlow na urządzenie procesora w module roboczym TPU. Definiuje również strategię dystrybucji TPU, której będziesz używać do dystrybucji szkolenia modelu na 8 oddzielnych rdzeni TPU dostępnych dla tego jednego pracownika TPU. Więcej informacji można znaleźć w przewodniku TensorFlow dotyczącym TPU .
import os
if os.environ['COLAB_TPU_ADDR']:
cluster_resolver = tf.distribute.cluster_resolver.TPUClusterResolver(tpu='')
tf.config.experimental_connect_to_cluster(cluster_resolver)
tf.tpu.experimental.initialize_tpu_system(cluster_resolver)
strategy = tf.distribute.TPUStrategy(cluster_resolver)
print('Using TPU')
elif tf.test.is_gpu_available():
strategy = tf.distribute.MirroredStrategy()
print('Using GPU')
else:
raise ValueError('Running on CPU is not recommended.')
Using TPU
Ładowanie modeli z TensorFlow Hub
Tutaj możesz wybrać, który model BERT załadujesz z TensorFlow Hub i dostroić. Do wyboru jest wiele modeli BERT.
- BERT-Base , Uncased i siedem innych modeli z wytrenowanymi ciężarami wydanymi przez oryginalnych autorów BERT.
- Małe BERT mają tę samą ogólną architekturę, ale mniej i / lub mniejsze bloki transformatora, co pozwala odkrywać kompromisy między szybkością, rozmiarem i jakością.
- ALBERT : cztery różne rozmiary „A Lite BERT”, które zmniejszają rozmiar modelu (ale nie czas obliczeń) poprzez współdzielenie parametrów między warstwami.
- Eksperci BERT : osiem modeli, z których wszystkie mają architekturę bazową BERT, ale oferują wybór między różnymi dziedzinami przedtreningowymi, aby lepiej dostosować się do zadania docelowego.
- Electra ma taką samą architekturę jak BERT (w trzech różnych rozmiarach), ale została wstępnie przeszkolona jako dyskryminator w konfiguracji przypominającej Generative Adversarial Network (GAN).
- BERT z Talking-Heads Attention i Gated GELU [ podstawowy , duży ] ma dwa ulepszenia rdzenia architektury transformatora.
Więcej informacji można znaleźć w dokumentacji modelu, do której link znajduje się powyżej.
W tym samouczku zaczniesz od bazy BERT. Możesz używać większych i nowszych modeli w celu uzyskania większej dokładności lub mniejszych modeli w celu skrócenia czasu treningu. Aby zmienić model, wystarczy zamienić jeden wiersz kodu (pokazany poniżej). Wszystkie różnice są zawarte w modelu SavedModel, który można pobrać z TensorFlow Hub.
Wybierz model BERT do dopracowania
bert_model_name = 'bert_en_uncased_L-12_H-768_A-12'
map_name_to_handle = {
'bert_en_uncased_L-12_H-768_A-12':
'https://tfhub.dev/tensorflow/bert_en_uncased_L-12_H-768_A-12/3',
'bert_en_uncased_L-24_H-1024_A-16':
'https://tfhub.dev/tensorflow/bert_en_uncased_L-24_H-1024_A-16/3',
'bert_en_wwm_uncased_L-24_H-1024_A-16':
'https://tfhub.dev/tensorflow/bert_en_wwm_uncased_L-24_H-1024_A-16/3',
'bert_en_cased_L-12_H-768_A-12':
'https://tfhub.dev/tensorflow/bert_en_cased_L-12_H-768_A-12/3',
'bert_en_cased_L-24_H-1024_A-16':
'https://tfhub.dev/tensorflow/bert_en_cased_L-24_H-1024_A-16/3',
'bert_en_wwm_cased_L-24_H-1024_A-16':
'https://tfhub.dev/tensorflow/bert_en_wwm_cased_L-24_H-1024_A-16/3',
'bert_multi_cased_L-12_H-768_A-12':
'https://tfhub.dev/tensorflow/bert_multi_cased_L-12_H-768_A-12/3',
'small_bert/bert_en_uncased_L-2_H-128_A-2':
'https://tfhub.dev/tensorflow/small_bert/bert_en_uncased_L-2_H-128_A-2/1',
'small_bert/bert_en_uncased_L-2_H-256_A-4':
'https://tfhub.dev/tensorflow/small_bert/bert_en_uncased_L-2_H-256_A-4/1',
'small_bert/bert_en_uncased_L-2_H-512_A-8':
'https://tfhub.dev/tensorflow/small_bert/bert_en_uncased_L-2_H-512_A-8/1',
'small_bert/bert_en_uncased_L-2_H-768_A-12':
'https://tfhub.dev/tensorflow/small_bert/bert_en_uncased_L-2_H-768_A-12/1',
'small_bert/bert_en_uncased_L-4_H-128_A-2':
'https://tfhub.dev/tensorflow/small_bert/bert_en_uncased_L-4_H-128_A-2/1',
'small_bert/bert_en_uncased_L-4_H-256_A-4':
'https://tfhub.dev/tensorflow/small_bert/bert_en_uncased_L-4_H-256_A-4/1',
'small_bert/bert_en_uncased_L-4_H-512_A-8':
'https://tfhub.dev/tensorflow/small_bert/bert_en_uncased_L-4_H-512_A-8/1',
'small_bert/bert_en_uncased_L-4_H-768_A-12':
'https://tfhub.dev/tensorflow/small_bert/bert_en_uncased_L-4_H-768_A-12/1',
'small_bert/bert_en_uncased_L-6_H-128_A-2':
'https://tfhub.dev/tensorflow/small_bert/bert_en_uncased_L-6_H-128_A-2/1',
'small_bert/bert_en_uncased_L-6_H-256_A-4':
'https://tfhub.dev/tensorflow/small_bert/bert_en_uncased_L-6_H-256_A-4/1',
'small_bert/bert_en_uncased_L-6_H-512_A-8':
'https://tfhub.dev/tensorflow/small_bert/bert_en_uncased_L-6_H-512_A-8/1',
'small_bert/bert_en_uncased_L-6_H-768_A-12':
'https://tfhub.dev/tensorflow/small_bert/bert_en_uncased_L-6_H-768_A-12/1',
'small_bert/bert_en_uncased_L-8_H-128_A-2':
'https://tfhub.dev/tensorflow/small_bert/bert_en_uncased_L-8_H-128_A-2/1',
'small_bert/bert_en_uncased_L-8_H-256_A-4':
'https://tfhub.dev/tensorflow/small_bert/bert_en_uncased_L-8_H-256_A-4/1',
'small_bert/bert_en_uncased_L-8_H-512_A-8':
'https://tfhub.dev/tensorflow/small_bert/bert_en_uncased_L-8_H-512_A-8/1',
'small_bert/bert_en_uncased_L-8_H-768_A-12':
'https://tfhub.dev/tensorflow/small_bert/bert_en_uncased_L-8_H-768_A-12/1',
'small_bert/bert_en_uncased_L-10_H-128_A-2':
'https://tfhub.dev/tensorflow/small_bert/bert_en_uncased_L-10_H-128_A-2/1',
'small_bert/bert_en_uncased_L-10_H-256_A-4':
'https://tfhub.dev/tensorflow/small_bert/bert_en_uncased_L-10_H-256_A-4/1',
'small_bert/bert_en_uncased_L-10_H-512_A-8':
'https://tfhub.dev/tensorflow/small_bert/bert_en_uncased_L-10_H-512_A-8/1',
'small_bert/bert_en_uncased_L-10_H-768_A-12':
'https://tfhub.dev/tensorflow/small_bert/bert_en_uncased_L-10_H-768_A-12/1',
'small_bert/bert_en_uncased_L-12_H-128_A-2':
'https://tfhub.dev/tensorflow/small_bert/bert_en_uncased_L-12_H-128_A-2/1',
'small_bert/bert_en_uncased_L-12_H-256_A-4':
'https://tfhub.dev/tensorflow/small_bert/bert_en_uncased_L-12_H-256_A-4/1',
'small_bert/bert_en_uncased_L-12_H-512_A-8':
'https://tfhub.dev/tensorflow/small_bert/bert_en_uncased_L-12_H-512_A-8/1',
'small_bert/bert_en_uncased_L-12_H-768_A-12':
'https://tfhub.dev/tensorflow/small_bert/bert_en_uncased_L-12_H-768_A-12/1',
'albert_en_base':
'https://tfhub.dev/tensorflow/albert_en_base/2',
'albert_en_large':
'https://tfhub.dev/tensorflow/albert_en_large/2',
'albert_en_xlarge':
'https://tfhub.dev/tensorflow/albert_en_xlarge/2',
'albert_en_xxlarge':
'https://tfhub.dev/tensorflow/albert_en_xxlarge/2',
'electra_small':
'https://tfhub.dev/google/electra_small/2',
'electra_base':
'https://tfhub.dev/google/electra_base/2',
'experts_pubmed':
'https://tfhub.dev/google/experts/bert/pubmed/2',
'experts_wiki_books':
'https://tfhub.dev/google/experts/bert/wiki_books/2',
'talking-heads_base':
'https://tfhub.dev/tensorflow/talkheads_ggelu_bert_en_base/1',
'talking-heads_large':
'https://tfhub.dev/tensorflow/talkheads_ggelu_bert_en_large/1',
}
map_model_to_preprocess = {
'bert_en_uncased_L-24_H-1024_A-16':
'https://tfhub.dev/tensorflow/bert_en_uncased_preprocess/3',
'bert_en_uncased_L-12_H-768_A-12':
'https://tfhub.dev/tensorflow/bert_en_uncased_preprocess/3',
'bert_en_wwm_cased_L-24_H-1024_A-16':
'https://tfhub.dev/tensorflow/bert_en_cased_preprocess/3',
'bert_en_cased_L-24_H-1024_A-16':
'https://tfhub.dev/tensorflow/bert_en_cased_preprocess/3',
'bert_en_cased_L-12_H-768_A-12':
'https://tfhub.dev/tensorflow/bert_en_cased_preprocess/3',
'bert_en_wwm_uncased_L-24_H-1024_A-16':
'https://tfhub.dev/tensorflow/bert_en_uncased_preprocess/3',
'small_bert/bert_en_uncased_L-2_H-128_A-2':
'https://tfhub.dev/tensorflow/bert_en_uncased_preprocess/3',
'small_bert/bert_en_uncased_L-2_H-256_A-4':
'https://tfhub.dev/tensorflow/bert_en_uncased_preprocess/3',
'small_bert/bert_en_uncased_L-2_H-512_A-8':
'https://tfhub.dev/tensorflow/bert_en_uncased_preprocess/3',
'small_bert/bert_en_uncased_L-2_H-768_A-12':
'https://tfhub.dev/tensorflow/bert_en_uncased_preprocess/3',
'small_bert/bert_en_uncased_L-4_H-128_A-2':
'https://tfhub.dev/tensorflow/bert_en_uncased_preprocess/3',
'small_bert/bert_en_uncased_L-4_H-256_A-4':
'https://tfhub.dev/tensorflow/bert_en_uncased_preprocess/3',
'small_bert/bert_en_uncased_L-4_H-512_A-8':
'https://tfhub.dev/tensorflow/bert_en_uncased_preprocess/3',
'small_bert/bert_en_uncased_L-4_H-768_A-12':
'https://tfhub.dev/tensorflow/bert_en_uncased_preprocess/3',
'small_bert/bert_en_uncased_L-6_H-128_A-2':
'https://tfhub.dev/tensorflow/bert_en_uncased_preprocess/3',
'small_bert/bert_en_uncased_L-6_H-256_A-4':
'https://tfhub.dev/tensorflow/bert_en_uncased_preprocess/3',
'small_bert/bert_en_uncased_L-6_H-512_A-8':
'https://tfhub.dev/tensorflow/bert_en_uncased_preprocess/3',
'small_bert/bert_en_uncased_L-6_H-768_A-12':
'https://tfhub.dev/tensorflow/bert_en_uncased_preprocess/3',
'small_bert/bert_en_uncased_L-8_H-128_A-2':
'https://tfhub.dev/tensorflow/bert_en_uncased_preprocess/3',
'small_bert/bert_en_uncased_L-8_H-256_A-4':
'https://tfhub.dev/tensorflow/bert_en_uncased_preprocess/3',
'small_bert/bert_en_uncased_L-8_H-512_A-8':
'https://tfhub.dev/tensorflow/bert_en_uncased_preprocess/3',
'small_bert/bert_en_uncased_L-8_H-768_A-12':
'https://tfhub.dev/tensorflow/bert_en_uncased_preprocess/3',
'small_bert/bert_en_uncased_L-10_H-128_A-2':
'https://tfhub.dev/tensorflow/bert_en_uncased_preprocess/3',
'small_bert/bert_en_uncased_L-10_H-256_A-4':
'https://tfhub.dev/tensorflow/bert_en_uncased_preprocess/3',
'small_bert/bert_en_uncased_L-10_H-512_A-8':
'https://tfhub.dev/tensorflow/bert_en_uncased_preprocess/3',
'small_bert/bert_en_uncased_L-10_H-768_A-12':
'https://tfhub.dev/tensorflow/bert_en_uncased_preprocess/3',
'small_bert/bert_en_uncased_L-12_H-128_A-2':
'https://tfhub.dev/tensorflow/bert_en_uncased_preprocess/3',
'small_bert/bert_en_uncased_L-12_H-256_A-4':
'https://tfhub.dev/tensorflow/bert_en_uncased_preprocess/3',
'small_bert/bert_en_uncased_L-12_H-512_A-8':
'https://tfhub.dev/tensorflow/bert_en_uncased_preprocess/3',
'small_bert/bert_en_uncased_L-12_H-768_A-12':
'https://tfhub.dev/tensorflow/bert_en_uncased_preprocess/3',
'bert_multi_cased_L-12_H-768_A-12':
'https://tfhub.dev/tensorflow/bert_multi_cased_preprocess/3',
'albert_en_base':
'https://tfhub.dev/tensorflow/albert_en_preprocess/3',
'albert_en_large':
'https://tfhub.dev/tensorflow/albert_en_preprocess/3',
'albert_en_xlarge':
'https://tfhub.dev/tensorflow/albert_en_preprocess/3',
'albert_en_xxlarge':
'https://tfhub.dev/tensorflow/albert_en_preprocess/3',
'electra_small':
'https://tfhub.dev/tensorflow/bert_en_uncased_preprocess/3',
'electra_base':
'https://tfhub.dev/tensorflow/bert_en_uncased_preprocess/3',
'experts_pubmed':
'https://tfhub.dev/tensorflow/bert_en_uncased_preprocess/3',
'experts_wiki_books':
'https://tfhub.dev/tensorflow/bert_en_uncased_preprocess/3',
'talking-heads_base':
'https://tfhub.dev/tensorflow/bert_en_uncased_preprocess/3',
'talking-heads_large':
'https://tfhub.dev/tensorflow/bert_en_uncased_preprocess/3',
}
tfhub_handle_encoder = map_name_to_handle[bert_model_name]
tfhub_handle_preprocess = map_model_to_preprocess[bert_model_name]
print('BERT model selected :', tfhub_handle_encoder)
print('Preprocessing model auto-selected:', tfhub_handle_preprocess)
BERT model selected : https://tfhub.dev/tensorflow/bert_en_uncased_L-12_H-768_A-12/3 Preprocessing model auto-selected: https://tfhub.dev/tensorflow/bert_en_uncased_preprocess/3
Przetwórz wstępnie tekst
W przypadku tekstu Classify with BERT colab model przetwarzania wstępnego jest używany bezpośrednio z koderem BERT.
W tym samouczku pokazano, jak wykonać wstępne przetwarzanie jako część potoku wejściowego do szkolenia, używając Dataset.map, a następnie scalić go z modelem, który jest eksportowany w celu wnioskowania. W ten sposób zarówno uczenie, jak i wnioskowanie mogą działać na podstawie danych wejściowych nieprzetworzonego tekstu, chociaż sam TPU wymaga danych liczbowych.
Pomijając wymagania TPU, może to pomóc wydajności w przetwarzaniu wstępnym wykonanym asynchronicznie w potoku wejściowym (możesz dowiedzieć się więcej w przewodniku po wydajności tf.data ).
W tym samouczku pokazano również, jak tworzyć modele z wieloma wejściami i jak dostosować długość sekwencji danych wejściowych do BERT.
Pokażmy model przetwarzania wstępnego.
bert_preprocess = hub.load(tfhub_handle_preprocess)
tok = bert_preprocess.tokenize(tf.constant(['Hello TensorFlow!']))
print(tok)
<tf.RaggedTensor [[[7592], [23435, 12314], [999]]]>
Każdy model przetwarzania wstępnego udostępnia również metodę .bert_pack_inputs(tensors, seq_length)
, która pobiera listę tokenów (podobnie jak tok
powyżej) i argument długości sekwencji. Spowoduje to spakowanie danych wejściowych do utworzenia słownika tensorów w formacie oczekiwanym przez model BERT.
text_preprocessed = bert_preprocess.bert_pack_inputs([tok, tok], tf.constant(20))
print('Shape Word Ids : ', text_preprocessed['input_word_ids'].shape)
print('Word Ids : ', text_preprocessed['input_word_ids'][0, :16])
print('Shape Mask : ', text_preprocessed['input_mask'].shape)
print('Input Mask : ', text_preprocessed['input_mask'][0, :16])
print('Shape Type Ids : ', text_preprocessed['input_type_ids'].shape)
print('Type Ids : ', text_preprocessed['input_type_ids'][0, :16])
Shape Word Ids : (1, 20) Word Ids : tf.Tensor( [ 101 7592 23435 12314 999 102 7592 23435 12314 999 102 0 0 0 0 0], shape=(16,), dtype=int32) Shape Mask : (1, 20) Input Mask : tf.Tensor([1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0], shape=(16,), dtype=int32) Shape Type Ids : (1, 20) Type Ids : tf.Tensor([0 0 0 0 0 0 1 1 1 1 1 0 0 0 0 0], shape=(16,), dtype=int32)
Oto kilka szczegółów, na które należy zwrócić uwagę:
-
input_mask
Maska umożliwia modelowi wyraźne rozróżnienie między zawartością a dopełnieniem. Maska ma taki sam kształt jakinput_word_ids
i zawiera 1 wszędzie tam, gdzieinput_word_ids
nie jestinput_word_ids
. -
input_type_ids
ma taki sam kształt jakinput_mask
, ale wewnątrz obszaru bezinput_mask
zawiera 0 lub 1 wskazujące, którego zdania jest token.
Następnie utworzysz model przetwarzania wstępnego, który zawiera całą tę logikę. Twój model przyjmie ciągi znaków jako dane wejściowe i zwróci odpowiednio sformatowane obiekty, które można przekazać do BERT.
Każdy model BERT ma określony model przetwarzania wstępnego, upewnij się, że używasz właściwego modelu opisanego w dokumentacji modelu BERT.
def make_bert_preprocess_model(sentence_features, seq_length=128):
"""Returns Model mapping string features to BERT inputs.
Args:
sentence_features: a list with the names of string-valued features.
seq_length: an integer that defines the sequence length of BERT inputs.
Returns:
A Keras Model that can be called on a list or dict of string Tensors
(with the order or names, resp., given by sentence_features) and
returns a dict of tensors for input to BERT.
"""
input_segments = [
tf.keras.layers.Input(shape=(), dtype=tf.string, name=ft)
for ft in sentence_features]
# Tokenize the text to word pieces.
bert_preprocess = hub.load(tfhub_handle_preprocess)
tokenizer = hub.KerasLayer(bert_preprocess.tokenize, name='tokenizer')
segments = [tokenizer(s) for s in input_segments]
# Optional: Trim segments in a smart way to fit seq_length.
# Simple cases (like this example) can skip this step and let
# the next step apply a default truncation to approximately equal lengths.
truncated_segments = segments
# Pack inputs. The details (start/end token ids, dict of output tensors)
# are model-dependent, so this gets loaded from the SavedModel.
packer = hub.KerasLayer(bert_preprocess.bert_pack_inputs,
arguments=dict(seq_length=seq_length),
name='packer')
model_inputs = packer(truncated_segments)
return tf.keras.Model(input_segments, model_inputs)
Pokażmy model przetwarzania wstępnego. Utworzysz test z dwoma zdaniami wejściowymi (input1 i input2). Dane wyjściowe są zgodne z oczekiwaniami modelu BERT jako input_word_ids
wejściowymi: input_word_ids
, input_masks
i input_type_ids
.
test_preprocess_model = make_bert_preprocess_model(['my_input1', 'my_input2'])
test_text = [np.array(['some random test sentence']),
np.array(['another sentence'])]
text_preprocessed = test_preprocess_model(test_text)
print('Keys : ', list(text_preprocessed.keys()))
print('Shape Word Ids : ', text_preprocessed['input_word_ids'].shape)
print('Word Ids : ', text_preprocessed['input_word_ids'][0, :16])
print('Shape Mask : ', text_preprocessed['input_mask'].shape)
print('Input Mask : ', text_preprocessed['input_mask'][0, :16])
print('Shape Type Ids : ', text_preprocessed['input_type_ids'].shape)
print('Type Ids : ', text_preprocessed['input_type_ids'][0, :16])
Keys : ['input_mask', 'input_type_ids', 'input_word_ids'] Shape Word Ids : (1, 128) Word Ids : tf.Tensor( [ 101 2070 6721 3231 6251 102 2178 6251 102 0 0 0 0 0 0 0], shape=(16,), dtype=int32) Shape Mask : (1, 128) Input Mask : tf.Tensor([1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0], shape=(16,), dtype=int32) Shape Type Ids : (1, 128) Type Ids : tf.Tensor([0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0], shape=(16,), dtype=int32)
Przyjrzyjmy się strukturze modelu, zwracając uwagę na dwa zdefiniowane dane wejściowe.
tf.keras.utils.plot_model(test_preprocess_model)
('Failed to import pydot. You must `pip install pydot` and install graphviz (https://graphviz.gitlab.io/download/), ', 'for `pydotprint` to work.')
Aby zastosować przetwarzanie wstępne we wszystkich wejściach ze zbioru danych, użyjesz funkcji map
z zestawu danych. Wynik jest następnie zapisywany w pamięci podręcznej w celu zapewnienia wydajności .
AUTOTUNE = tf.data.AUTOTUNE
def load_dataset_from_tfds(in_memory_ds, info, split, batch_size,
bert_preprocess_model):
is_training = split.startswith('train')
dataset = tf.data.Dataset.from_tensor_slices(in_memory_ds[split])
num_examples = info.splits[split].num_examples
if is_training:
dataset = dataset.shuffle(num_examples)
dataset = dataset.repeat()
dataset = dataset.batch(batch_size)
dataset = dataset.map(lambda ex: (bert_preprocess_model(ex), ex['label']))
dataset = dataset.cache().prefetch(buffer_size=AUTOTUNE)
return dataset, num_examples
Zdefiniuj swój model
Jesteś teraz gotowy do zdefiniowania modelu klasyfikacji zdań lub par zdań, dostarczając wstępnie przetworzone dane wejściowe przez koder BERT i umieszczając na górze klasyfikator liniowy (lub inny układ warstw, jak wolisz), a także wykorzystując dropout do regularyzacji.
def build_classifier_model(num_classes):
inputs = dict(
input_word_ids=tf.keras.layers.Input(shape=(None,), dtype=tf.int32),
input_mask=tf.keras.layers.Input(shape=(None,), dtype=tf.int32),
input_type_ids=tf.keras.layers.Input(shape=(None,), dtype=tf.int32),
)
encoder = hub.KerasLayer(tfhub_handle_encoder, trainable=True, name='encoder')
net = encoder(inputs)['pooled_output']
net = tf.keras.layers.Dropout(rate=0.1)(net)
net = tf.keras.layers.Dense(num_classes, activation=None, name='classifier')(net)
return tf.keras.Model(inputs, net, name='prediction')
Spróbujmy uruchomić model na niektórych wstępnie przetworzonych danych wejściowych.
test_classifier_model = build_classifier_model(2)
bert_raw_result = test_classifier_model(text_preprocessed)
print(tf.sigmoid(bert_raw_result))
tf.Tensor([[0.33396733 0.49634424]], shape=(1, 2), dtype=float32)
Przyjrzyjmy się strukturze modelu. Możesz zobaczyć trzy oczekiwane dane wejściowe BERT.
tf.keras.utils.plot_model(test_classifier_model)
('Failed to import pydot. You must `pip install pydot` and install graphviz (https://graphviz.gitlab.io/download/), ', 'for `pydotprint` to work.')
Wybierz zadanie od GLUE
Zamierzasz użyć zestawu danych TensorFlow z pakietu wzorcowego GLUE .
Colab pozwala pobrać te małe zestawy danych do lokalnego systemu plików, a poniższy kod wczytuje je w całości do pamięci, ponieważ oddzielny host roboczy TPU nie może uzyskać dostępu do lokalnego systemu plików środowiska wykonawczego colab.
W przypadku większych zbiorów danych musisz utworzyć własny zasobnik Google Cloud Storage i poprosić pracownika TPU o odczytanie z niego danych. Możesz dowiedzieć się więcej w przewodniku TPU .
Zaleca się rozpocząć od zbioru danych CoLa (dla jednego zdania) lub MRPC (dla wielu zdań), ponieważ są one małe i nie wymagają dużo czasu na ich dopracowanie.
tfds_name = 'glue/cola'
tfds_info = tfds.builder(tfds_name).info
sentence_features = list(tfds_info.features.keys())
sentence_features.remove('idx')
sentence_features.remove('label')
available_splits = list(tfds_info.splits.keys())
train_split = 'train'
validation_split = 'validation'
test_split = 'test'
if tfds_name == 'glue/mnli':
validation_split = 'validation_matched'
test_split = 'test_matched'
num_classes = tfds_info.features['label'].num_classes
num_examples = tfds_info.splits.total_num_examples
print(f'Using {tfds_name} from TFDS')
print(f'This dataset has {num_examples} examples')
print(f'Number of classes: {num_classes}')
print(f'Features {sentence_features}')
print(f'Splits {available_splits}')
with tf.device('/job:localhost'):
# batch_size=-1 is a way to load the dataset into memory
in_memory_ds = tfds.load(tfds_name, batch_size=-1, shuffle_files=True)
# The code below is just to show some samples from the selected dataset
print(f'Here are some sample rows from {tfds_name} dataset')
sample_dataset = tf.data.Dataset.from_tensor_slices(in_memory_ds[train_split])
labels_names = tfds_info.features['label'].names
print(labels_names)
print()
sample_i = 1
for sample_row in sample_dataset.take(5):
samples = [sample_row[feature] for feature in sentence_features]
print(f'sample row {sample_i}')
for sample in samples:
print(sample.numpy())
sample_label = sample_row['label']
print(f'label: {sample_label} ({labels_names[sample_label]})')
print()
sample_i += 1
Using glue/cola from TFDS This dataset has 10657 examples Number of classes: 2 Features ['sentence'] Splits ['test', 'train', 'validation'] Here are some sample rows from glue/cola dataset ['unacceptable', 'acceptable'] sample row 1 b'It is this hat that it is certain that he was wearing.' label: 1 (acceptable) sample row 2 b'Her efficient looking up of the answer pleased the boss.' label: 1 (acceptable) sample row 3 b'Both the workers will wear carnations.' label: 1 (acceptable) sample row 4 b'John enjoyed drawing trees for his syntax homework.' label: 1 (acceptable) sample row 5 b'We consider Leslie rather foolish, and Lou a complete idiot.' label: 1 (acceptable)
Zbiór danych określa również typ problemu (klasyfikację lub regresję) oraz odpowiednią funkcję straty do treningu.
def get_configuration(glue_task):
loss = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)
if glue_task == 'glue/cola':
metrics = tfa.metrics.MatthewsCorrelationCoefficient(num_classes=2)
else:
metrics = tf.keras.metrics.SparseCategoricalAccuracy(
'accuracy', dtype=tf.float32)
return metrics, loss
Wytrenuj swój model
Na koniec możesz wytrenować model od końca do końca na wybranym zestawie danych.
Dystrybucja
Przywołaj kod konfiguracyjny u góry, który połączył środowisko wykonawcze colab z pracownikiem TPU z wieloma urządzeniami TPU. Aby rozdzielić na nich szkolenie, utworzysz i skompilujesz swój główny model Keras w ramach strategii dystrybucji TPU. (Aby uzyskać szczegółowe informacje, zobacz Rozproszone szkolenie z Keras ).
Z drugiej strony przetwarzanie wstępne działa na procesorze hosta roboczego, a nie na modułach TPU, więc model Keras do przetwarzania wstępnego, a także zmapowane zestawy danych treningowych i walidacyjnych są zbudowane poza zakresem strategii dystrybucji. Wywołanie Model.fit()
zajmie się dystrybucją przekazanego zestawu danych do replik modelu.
Optimizer
Precyzyjne strojenie jest zgodne z ustawieniami optymalizatora z przedtreningiem BERT (jak w Classify text with BERT ): Wykorzystuje optymalizator AdamW z liniowym zanikiem nominalnego początkowego tempa uczenia się, poprzedzony liniową fazą rozgrzewki w stosunku do pierwszego 10% kroków treningowych ( num_warmup_steps
). Zgodnie z artykułem BERT, początkowy wskaźnik uczenia się jest mniejszy w przypadku dostrajania (najlepiej 5e-5, 3e-5, 2e-5).
epochs = 3
batch_size = 32
init_lr = 2e-5
print(f'Fine tuning {tfhub_handle_encoder} model')
bert_preprocess_model = make_bert_preprocess_model(sentence_features)
with strategy.scope():
# metric have to be created inside the strategy scope
metrics, loss = get_configuration(tfds_name)
train_dataset, train_data_size = load_dataset_from_tfds(
in_memory_ds, tfds_info, train_split, batch_size, bert_preprocess_model)
steps_per_epoch = train_data_size // batch_size
num_train_steps = steps_per_epoch * epochs
num_warmup_steps = num_train_steps // 10
validation_dataset, validation_data_size = load_dataset_from_tfds(
in_memory_ds, tfds_info, validation_split, batch_size,
bert_preprocess_model)
validation_steps = validation_data_size // batch_size
classifier_model = build_classifier_model(num_classes)
optimizer = optimization.create_optimizer(
init_lr=init_lr,
num_train_steps=num_train_steps,
num_warmup_steps=num_warmup_steps,
optimizer_type='adamw')
classifier_model.compile(optimizer=optimizer, loss=loss, metrics=[metrics])
classifier_model.fit(
x=train_dataset,
validation_data=validation_dataset,
steps_per_epoch=steps_per_epoch,
epochs=epochs,
validation_steps=validation_steps)
Fine tuning https://tfhub.dev/tensorflow/bert_en_uncased_L-12_H-768_A-12/3 model /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/keras/engine/functional.py:595: UserWarning: Input dict contained keys ['idx', 'label'] which did not match any model input. They will be ignored by the model. [n for n in tensors.keys() if n not in ref_input_names]) Epoch 1/3 /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/framework/indexed_slices.py:437: UserWarning: Converting sparse IndexedSlices(IndexedSlices(indices=Tensor("AdamWeightDecay/gradients/StatefulPartitionedCall:1", shape=(None,), dtype=int32), values=Tensor("clip_by_global_norm/clip_by_global_norm/_0:0", dtype=float32), dense_shape=Tensor("AdamWeightDecay/gradients/StatefulPartitionedCall:2", shape=(None,), dtype=int32))) to a dense Tensor of unknown shape. This may consume a large amount of memory. "shape. This may consume a large amount of memory." % value) 267/267 [==============================] - 79s 77ms/step - loss: 0.5812 - MatthewsCorrelationCoefficient: 0.5968 - val_loss: 0.4688 - val_MatthewsCorrelationCoefficient: 0.5871 Epoch 2/3 267/267 [==============================] - 15s 58ms/step - loss: 0.3561 - MatthewsCorrelationCoefficient: 0.5906 - val_loss: 0.5723 - val_MatthewsCorrelationCoefficient: 0.5871 Epoch 3/3 267/267 [==============================] - 15s 58ms/step - loss: 0.2352 - MatthewsCorrelationCoefficient: 0.5909 - val_loss: 0.6748 - val_MatthewsCorrelationCoefficient: 0.5871
Eksportuj do wnioskowania
Utworzysz ostateczny model, który będzie zawierał część wstępnego przetwarzania i dopracowany BERT, który właśnie stworzyliśmy.
W czasie wnioskowania wstępne przetwarzanie musi być częścią modelu (ponieważ nie ma już oddzielnej kolejki wejściowej, jak w przypadku danych uczących, które to robią). Wstępne przetwarzanie to nie tylko obliczenia; ma własne zasoby (tabelę słownictwa), które należy dołączyć do modelu Keras zapisanego do eksportu. To ostateczne zgromadzenie będzie tym, co zostanie uratowane.
Zamierzasz zapisać model na colab, a później możesz go pobrać, aby zachować go na przyszłość ( Widok -> Spis treści -> Pliki ).
main_save_path = './my_models'
bert_type = tfhub_handle_encoder.split('/')[-2]
saved_model_name = f'{tfds_name.replace("/", "_")}_{bert_type}'
saved_model_path = os.path.join(main_save_path, saved_model_name)
preprocess_inputs = bert_preprocess_model.inputs
bert_encoder_inputs = bert_preprocess_model(preprocess_inputs)
bert_outputs = classifier_model(bert_encoder_inputs)
model_for_export = tf.keras.Model(preprocess_inputs, bert_outputs)
print('Saving', saved_model_path)
# Save everything on the Colab host (even the variables from TPU memory)
save_options = tf.saved_model.SaveOptions(experimental_io_device='/job:localhost')
model_for_export.save(saved_model_path, include_optimizer=False,
options=save_options)
Saving ./my_models/glue_cola_bert_en_uncased_L-12_H-768_A-12 WARNING:absl:Found untraced functions such as restored_function_body, restored_function_body, restored_function_body, restored_function_body, restored_function_body while saving (showing 5 of 910). These functions will not be directly callable after loading. WARNING:absl:Found untraced functions such as restored_function_body, restored_function_body, restored_function_body, restored_function_body, restored_function_body while saving (showing 5 of 910). These functions will not be directly callable after loading.
Przetestuj model
Ostatnim krokiem jest przetestowanie wyników eksportowanego modelu.
Aby dokonać porównania, przeładujmy model i przetestujmy go przy użyciu niektórych danych wejściowych z testu rozdzielonego ze zbioru danych.
with tf.device('/job:localhost'):
reloaded_model = tf.saved_model.load(saved_model_path)
Metody użytkowe
def prepare(record):
model_inputs = [[record[ft]] for ft in sentence_features]
return model_inputs
def prepare_serving(record):
model_inputs = {ft: record[ft] for ft in sentence_features}
return model_inputs
def print_bert_results(test, bert_result, dataset_name):
bert_result_class = tf.argmax(bert_result, axis=1)[0]
if dataset_name == 'glue/cola':
print('sentence:', test[0].numpy())
if bert_result_class == 1:
print('This sentence is acceptable')
else:
print('This sentence is unacceptable')
elif dataset_name == 'glue/sst2':
print('sentence:', test[0])
if bert_result_class == 1:
print('This sentence has POSITIVE sentiment')
else:
print('This sentence has NEGATIVE sentiment')
elif dataset_name == 'glue/mrpc':
print('sentence1:', test[0])
print('sentence2:', test[1])
if bert_result_class == 1:
print('Are a paraphrase')
else:
print('Are NOT a paraphrase')
elif dataset_name == 'glue/qqp':
print('question1:', test[0])
print('question2:', test[1])
if bert_result_class == 1:
print('Questions are similar')
else:
print('Questions are NOT similar')
elif dataset_name == 'glue/mnli':
print('premise :', test[0])
print('hypothesis:', test[1])
if bert_result_class == 1:
print('This premise is NEUTRAL to the hypothesis')
elif bert_result_class == 2:
print('This premise CONTRADICTS the hypothesis')
else:
print('This premise ENTAILS the hypothesis')
elif dataset_name == 'glue/qnli':
print('question:', test[0])
print('sentence:', test[1])
if bert_result_class == 1:
print('The question is NOT answerable by the sentence')
else:
print('The question is answerable by the sentence')
elif dataset_name == 'glue/rte':
print('sentence1:', test[0])
print('sentence2:', test[1])
if bert_result_class == 1:
print('Sentence1 DOES NOT entails sentence2')
else:
print('Sentence1 entails sentence2')
elif dataset_name == 'glue/wnli':
print('sentence1:', test[0])
print('sentence2:', test[1])
if bert_result_class == 1:
print('Sentence1 DOES NOT entails sentence2')
else:
print('Sentence1 entails sentence2')
print('BERT raw results:', bert_result[0])
print()
Test
with tf.device('/job:localhost'):
test_dataset = tf.data.Dataset.from_tensor_slices(in_memory_ds[test_split])
for test_row in test_dataset.shuffle(1000).map(prepare).take(5):
if len(sentence_features) == 1:
result = reloaded_model(test_row[0])
else:
result = reloaded_model(list(test_row))
print_bert_results(test_row, result, tfds_name)
sentence: [b'Onto the table jumped a cat.'] This sentence is acceptable BERT raw results: tf.Tensor([-1.5364362 3.0245366], shape=(2,), dtype=float32) sentence: [b'it is important to John to leave.'] This sentence is acceptable BERT raw results: tf.Tensor([-2.1081586 4.0080795], shape=(2,), dtype=float32) sentence: [b'Gina filled the pitcher with lemonade.'] This sentence is acceptable BERT raw results: tf.Tensor([-2.5824902 3.740626 ], shape=(2,), dtype=float32) sentence: [b"Every essay which she's written and that I've read is on that pile."] This sentence is acceptable BERT raw results: tf.Tensor([-0.34991243 1.3099391 ], shape=(2,), dtype=float32) sentence: [b'The student hated his morphology professor.'] This sentence is acceptable BERT raw results: tf.Tensor([-2.4559186 3.5234451], shape=(2,), dtype=float32)
Jeśli chcesz używać swojego modelu w TF Serving , pamiętaj, że wywoła on Twój SavedModel za pomocą jednego ze swoich nazwanych sygnatur. Zauważ, że są pewne drobne różnice w danych wejściowych. W Pythonie możesz je przetestować w następujący sposób:
with tf.device('/job:localhost'):
serving_model = reloaded_model.signatures['serving_default']
for test_row in test_dataset.shuffle(1000).map(prepare_serving).take(5):
result = serving_model(**test_row)
# The 'prediction' key is the classifier's defined model name.
print_bert_results(list(test_row.values()), result['prediction'], tfds_name)
sentence: b'His loves him' This sentence is unacceptable BERT raw results: tf.Tensor([ 2.160802 -2.4170692], shape=(2,), dtype=float32) sentence: b'John says Mary likes himself.' This sentence is unacceptable BERT raw results: tf.Tensor([ 1.759533 -2.0088294], shape=(2,), dtype=float32) sentence: b'He treats John very under.' This sentence is acceptable BERT raw results: tf.Tensor([-0.19971004 1.1566539 ], shape=(2,), dtype=float32) sentence: b'Karen asked where him to vote for.' This sentence is unacceptable BERT raw results: tf.Tensor([ 2.1606677 -2.1753395], shape=(2,), dtype=float32) sentence: b"I didn't give Jack Ed's picture of anybody." This sentence is unacceptable BERT raw results: tf.Tensor([ 1.755337 -1.904901], shape=(2,), dtype=float32)
Zrobiłeś to! Zapisany model można wykorzystać do obsługi lub prostego wnioskowania w procesie, z prostszym interfejsem API z mniejszą ilością kodu i łatwiejszym w utrzymaniu.
Następne kroki
Teraz, gdy wypróbowałeś jeden z podstawowych modeli BERT, możesz wypróbować inne, aby uzyskać większą dokładność lub może z mniejszymi wersjami modeli.
Możesz także spróbować w innych zbiorach danych.