Wstępne przetwarzanie BERT z tekstem TF

Zobacz na TensorFlow.org Uruchom w Google Colab Zobacz na GitHub Pobierz notatnik

Przegląd

Wstępne przetwarzanie tekstu to kompleksowe przekształcenie nieprzetworzonego tekstu na dane wejściowe liczb całkowitych modelu. Modelom NLP często towarzyszy kilkaset (jeśli nie tysiące) linii kodu Pythona do wstępnego przetwarzania tekstu. Wstępne przetwarzanie tekstu jest często wyzwaniem dla modeli, ponieważ:

  • Pochylenie obsługi treningowej. Coraz trudniej jest zapewnić, że logika przetwarzania wstępnego danych wejściowych modelu jest spójna na wszystkich etapach tworzenia modelu (np. uczenie wstępne, dostrajanie, ocena, wnioskowanie). Korzystanie z różnych hiperparametrów, tokenizacja, algorytmy wstępnego przetwarzania ciągów lub po prostu niespójne pakowanie danych wejściowych modelu na różnych etapach może spowodować trudne do debugowania i katastrofalne skutki dla modelu.

  • Wydajność i elastyczność. Chociaż wstępne przetwarzanie można wykonać w trybie offline (np. przez zapisanie przetworzonych danych wyjściowych do plików na dysku, a następnie ponowne wykorzystanie tych wstępnie przetworzonych danych w potoku wejściowym), ta metoda wiąże się z dodatkowym kosztem odczytu i zapisu plików. Przetwarzanie wstępne offline jest również niewygodne, jeśli istnieją decyzje dotyczące wstępnego przetwarzania, które muszą być realizowane dynamicznie. Eksperymentowanie z inną opcją wymagałoby ponownego wygenerowania zestawu danych.

  • Złożony interfejs modelu. Modele tekstowe są znacznie bardziej zrozumiałe, gdy ich dane wejściowe to czysty tekst. Trudno jest zrozumieć model, którego dane wejściowe wymagają dodatkowego, pośredniego kroku kodowania. Zmniejszenie złożoności przetwarzania wstępnego jest szczególnie doceniane w przypadku debugowania, udostępniania i oceny modelu.

Ponadto prostsze interfejsy modelu ułatwiają również wypróbowanie modelu (np. wnioskowanie lub uczenie) na różnych, niezbadanych zestawach danych.

Wstępne przetwarzanie tekstu za pomocą TF.Text

Korzystając z interfejsów API przetwarzania wstępnego tekstu TF.Text, możemy skonstruować funkcję przetwarzania wstępnego, która może przekształcić zbiór danych tekstowych użytkownika na dane wejściowe w postaci liczb całkowitych. Użytkownicy mogą pakować przetwarzanie wstępne bezpośrednio jako część swojego modelu, aby złagodzić wyżej wymienione problemy.

Ten poradnik pokaże jak korzystać TF.Text przerób ops przekształcić dane tekstowe do wejścia do modelu BERT i wejść do języka maskujących pretraining zadanie opisane w „Zamaskowany LM i maskowanie postępowania” z BERT: pre-szkolenia głębokiego dwukierunkowych Transformers dla języka zrozumienie . Proces obejmuje tokenizację tekstu na jednostki podsłów, łączenie zdań, przycinanie treści do ustalonego rozmiaru i wyodrębnianie etykiet dla zadania modelowania języka maskowanego.

Ustawiać

Najpierw zaimportujmy pakiety i biblioteki, których potrzebujemy.

pip install -q -U tensorflow-text
import tensorflow as tf
import tensorflow_text as text
import functools
2021-08-17 11:09:49.001212: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcudart.so.11.0

Nasze dane zawiera dwie funkcje tekstowe i możemy stworzyć przykład tf.data.Dataset . Naszym celem jest stworzenie funkcji, które możemy dostarczyć Dataset.map() z być stosowane w treningu.

examples = {
    "text_a": [
      b"Sponge bob Squarepants is an Avenger",
      b"Marvel Avengers"
    ],
    "text_b": [
     b"Barack Obama is the President.",
     b"President is the highest office"
  ],
}

dataset = tf.data.Dataset.from_tensor_slices(examples)
next(iter(dataset))
2021-08-17 11:09:50.986363: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcuda.so.1
2021-08-17 11:09:51.592118: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-08-17 11:09:51.593052: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1733] Found device 0 with properties: 
pciBusID: 0000:00:05.0 name: Tesla V100-SXM2-16GB computeCapability: 7.0
coreClock: 1.53GHz coreCount: 80 deviceMemorySize: 15.78GiB deviceMemoryBandwidth: 836.37GiB/s
2021-08-17 11:09:51.593089: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcudart.so.11.0
2021-08-17 11:09:51.596478: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcublas.so.11
2021-08-17 11:09:51.596574: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcublasLt.so.11
2021-08-17 11:09:51.597657: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcufft.so.10
2021-08-17 11:09:51.597974: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcurand.so.10
2021-08-17 11:09:51.598957: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcusolver.so.11
2021-08-17 11:09:51.599848: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcusparse.so.11
2021-08-17 11:09:51.600019: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcudnn.so.8
2021-08-17 11:09:51.600118: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-08-17 11:09:51.601014: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-08-17 11:09:51.601942: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1871] Adding visible gpu devices: 0
2021-08-17 11:09:51.602609: I tensorflow/core/platform/cpu_feature_guard.cc:142] This TensorFlow binary is optimized with oneAPI Deep Neural Network Library (oneDNN) to use the following CPU instructions in performance-critical operations:  AVX2 AVX512F FMA
To enable them in other operations, rebuild TensorFlow with the appropriate compiler flags.
2021-08-17 11:09:51.603201: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-08-17 11:09:51.604143: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1733] Found device 0 with properties: 
pciBusID: 0000:00:05.0 name: Tesla V100-SXM2-16GB computeCapability: 7.0
coreClock: 1.53GHz coreCount: 80 deviceMemorySize: 15.78GiB deviceMemoryBandwidth: 836.37GiB/s
2021-08-17 11:09:51.604221: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-08-17 11:09:51.605115: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-08-17 11:09:51.605907: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1871] Adding visible gpu devices: 0
2021-08-17 11:09:51.605943: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcudart.so.11.0
2021-08-17 11:09:52.196319: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1258] Device interconnect StreamExecutor with strength 1 edge matrix:
2021-08-17 11:09:52.196355: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1264]      0 
2021-08-17 11:09:52.196362: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1277] 0:   N 
2021-08-17 11:09:52.196585: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-08-17 11:09:52.197468: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-08-17 11:09:52.198293: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-08-17 11:09:52.199096: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1418] Created TensorFlow device (/job:localhost/replica:0/task:0/device:GPU:0 with 14646 MB memory) -> physical GPU (device: 0, name: Tesla V100-SXM2-16GB, pci bus id: 0000:00:05.0, compute capability: 7.0)
{'text_a': <tf.Tensor: shape=(), dtype=string, numpy=b'Sponge bob Squarepants is an Avenger'>,
 'text_b': <tf.Tensor: shape=(), dtype=string, numpy=b'Barack Obama is the President.'>}

Tokenizacja

Naszym pierwszym krokiem jest uruchomienie przetwarzania wstępnego dowolnego ciągu i tokenizacja naszego zestawu danych. Można to zrobić za pomocą text.BertTokenizer , który jest text.Splitter że może tokenize zdań na język subwords lub wordpieces dla modelu BERT danego słownictwo generowane przez algorytm Wordpiece . Możesz dowiedzieć się więcej o innych tokenizers podsłowo dostępnych w TF.Text z tutaj .

Słownictwo może pochodzić z wcześniej wygenerowanego punktu kontrolnego BERT lub możesz wygenerować je samodzielnie na podstawie własnych danych. Na potrzeby tego przykładu utwórzmy słownictwo zabawek:

_VOCAB = [
    # Special tokens
    b"[UNK]", b"[MASK]", b"[RANDOM]", b"[CLS]", b"[SEP]",
    # Suffixes
    b"##ack", b"##ama", b"##ger", b"##gers", b"##onge", b"##pants",  b"##uare",
    b"##vel", b"##ven", b"an", b"A", b"Bar", b"Hates", b"Mar", b"Ob",
    b"Patrick", b"President", b"Sp", b"Sq", b"bob", b"box", b"has", b"highest",
    b"is", b"office", b"the",
]

_START_TOKEN = _VOCAB.index(b"[CLS]")
_END_TOKEN = _VOCAB.index(b"[SEP]")
_MASK_TOKEN = _VOCAB.index(b"[MASK]")
_RANDOM_TOKEN = _VOCAB.index(b"[RANDOM]")
_UNK_TOKEN = _VOCAB.index(b"[UNK]")
_MAX_SEQ_LEN = 8
_MAX_PREDICTIONS_PER_BATCH = 5

_VOCAB_SIZE = len(_VOCAB)

lookup_table = tf.lookup.StaticVocabularyTable(
    tf.lookup.KeyValueTensorInitializer(
      keys=_VOCAB,
      key_dtype=tf.string,
      values=tf.range(
          tf.size(_VOCAB, out_type=tf.int64), dtype=tf.int64),
      value_dtype=tf.int64),
      num_oov_buckets=1
)

Konstrukt Let to text.BertTokenizer stosując powyższą słownictwo i tokenize wejść tekst do RaggedTensor .`.

bert_tokenizer = text.BertTokenizer(lookup_table, token_out_type=tf.string)
bert_tokenizer.tokenize(examples["text_a"])
<tf.RaggedTensor [[[b'Sp', b'##onge'], [b'bob'], [b'Sq', b'##uare', b'##pants'], [b'is'], [b'an'], [b'A', b'##ven', b'##ger']], [[b'Mar', b'##vel'], [b'A', b'##ven', b'##gers']]]>
bert_tokenizer.tokenize(examples["text_b"])
<tf.RaggedTensor [[[b'Bar', b'##ack'], [b'Ob', b'##ama'], [b'is'], [b'the'], [b'President'], [b'[UNK]']], [[b'President'], [b'is'], [b'the'], [b'highest'], [b'office']]]>

Wyjście tekst z text.BertTokenizer pozwala nam zobaczyć, jak tekst jest tokenized, ale model wymaga Integer identyfikatory. Można ustawić token_out_type param do tf.int64 uzyskać całkowitą identyfikatorów (które są wskaźniki do słownika).

bert_tokenizer = text.BertTokenizer(lookup_table, token_out_type=tf.int64)
segment_a = bert_tokenizer.tokenize(examples["text_a"])
segment_a
<tf.RaggedTensor [[[22, 9], [24], [23, 11, 10], [28], [14], [15, 13, 7]], [[18, 12], [15, 13, 8]]]>
segment_b = bert_tokenizer.tokenize(examples["text_b"])
segment_b
<tf.RaggedTensor [[[16, 5], [19, 6], [28], [30], [21], [0]], [[21], [28], [30], [27], [29]]]>

text.BertTokenizer zwraca RaggedTensor w kształcie [batch, num_tokens, num_wordpieces] . Ponieważ nie potrzebujemy dodatkowych num_tokens wymiary dla naszego obecnego przypadku użycia, możemy połączyć dwa ostatnie wymiary, aby uzyskać RaggedTensor z kształtu [batch, num_wordpieces] :

segment_a = segment_a.merge_dims(-2, -1)
segment_a
<tf.RaggedTensor [[22, 9, 24, 23, 11, 10, 28, 14, 15, 13, 7], [18, 12, 15, 13, 8]]>
segment_b = segment_b.merge_dims(-2, -1)
segment_b
<tf.RaggedTensor [[16, 5, 19, 6, 28, 30, 21, 0], [21, 28, 30, 27, 29]]>

Przycinanie treści

Głównym wejściem do BERT jest konkatenacja dwóch zdań. Jednak BERT wymaga, aby dane wejściowe miały stałą wielkość i kształt, a my możemy mieć treści przekraczające nasz budżet.

Możemy zająć się tym za pomocą text.Trimmer przyciąć nasze treści w dół do określonej wielkości (raz łączone wzdłuż ostatniej osi). Istnieją różne text.Trimmer typy, które Wybierz zawartość do zachowania przy użyciu różnych algorytmów. text.RoundRobinTrimmer np przeznaczy kwotę równo dla każdego segmentu, ale może przyciąć końce zdań. text.WaterfallTrimmer będzie przyciąć począwszy od końca ostatniego zdania.

W naszym przykładzie użyjemy RoundRobinTrimmer elementów, które wybiera z każdego segmentu w lewy-prawy sposób.

trimmer = text.RoundRobinTrimmer(max_seq_length=[_MAX_SEQ_LEN])
trimmed = trimmer.trim([segment_a, segment_b])
trimmed
[<tf.RaggedTensor [[22, 9, 24, 23], [18, 12, 15, 13]]>,
 <tf.RaggedTensor [[16, 5, 19, 6], [21, 28, 30, 27]]>]

trimmed już zawiera segmenty, w których liczba elementów w partii wynosi 8 elementy (gdy łączone wzdłuż osi = 1).

Łączenie segmentów

Teraz, gdy mamy segmenty przycięte, możemy połączyć je ze sobą, aby uzyskać pojedynczą RaggedTensor . BERT wykorzystuje specjalne znaki do wskazania początku ( [CLS] ) i koniec segmentu ( [SEP] ). Potrzebujemy też RaggedTensor wskazujące, które elementy w połączeniu Tensor należeć do którego segmentu. Możemy użyć text.combine_segments() , aby uzyskać zarówno tych Tensor ze specjalnymi tokenami wstawionych.

segments_combined, segments_ids = text.combine_segments(
  [segment_a, segment_b],
  start_of_sequence_id=_START_TOKEN, end_of_segment_id=_END_TOKEN)
segments_combined, segments_ids
(<tf.RaggedTensor [[3, 22, 9, 24, 23, 11, 10, 28, 14, 15, 13, 7, 4, 16, 5, 19, 6, 28, 30, 21, 0, 4], [3, 18, 12, 15, 13, 8, 4, 21, 28, 30, 27, 29, 4]]>,
 <tf.RaggedTensor [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1], [0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1]]>)

Zadanie modelu języka maskowanego

Teraz, gdy mamy nasze podstawowe wejść, możemy zacząć, aby wyodrębnić nakładów potrzebnych do „zamaskowane LM i maskowanie postępowania” zadanie opisane w BERT: Wstępne szkolenie Deep dwukierunkowych Transformers dla rozumienia języka

Zadanie zamaskowanego modelu języka ma dwa podproblemy, nad którymi powinniśmy się zastanowić: (1) jakie elementy wybrać do zamaskowania i (2) jakie wartości są im przypisane?

Wybór przedmiotu

Ponieważ będziemy wybierać, aby wybrać elementy losowo do maskowania, użyjemy text.RandomItemSelector . RandomItemSelector losowo wybiera przedmioty u osobnika wsadowym do ograniczeń podanych ( max_selections_per_batch , selection_rate i unselectable_ids ) i zwraca wartość logiczną maski wskazujące, które zostały wybrane pozycje.

random_selector = text.RandomItemSelector(
    max_selections_per_batch=_MAX_PREDICTIONS_PER_BATCH,
    selection_rate=0.2,
    unselectable_ids=[_START_TOKEN, _END_TOKEN, _UNK_TOKEN]
)
selected = random_selector.get_selection_mask(
    segments_combined, axis=1)
selected
<tf.RaggedTensor [[False, False, False, False, False, False, False, True, False, True, True, False, False, False, False, False, True, False, False, False, False, False], [False, False, False, True, False, False, False, True, False, False, False, False, False]]>

Wybór wartości maskowanej

Metodologia opisana w oryginalnym dokumencie BERT dotyczącym wyboru wartości maskowania jest następująca:

Dla mask_token_rate czasu, wymienić element z [MASK] tokena:

"my dog is hairy" -> "my dog is [MASK]"

Dla random_token_rate czasu, wymienić element z losowym wyrazem:

"my dog is hairy" -> "my dog is apple"

Dla 1 - mask_token_rate - random_token_rate czasu, utrzymać pozycję na niezmienionym poziomie:

"my dog is hairy" -> "my dog is hairy."

text.MaskedValuesChooser obudowuje tę logikę i mogą być wykorzystane do naszej funkcji przetwarzania wstępnego. Oto przykład tego, co MaskValuesChooser powraca dali mask_token_rate 80% i domyślna random_token_rate :

input_ids = tf.ragged.constant([[19, 7, 21, 20, 9, 8], [13, 4, 16, 5], [15, 10, 12, 11, 6]])
mask_values_chooser = text.MaskValuesChooser(_VOCAB_SIZE, _MASK_TOKEN, 0.8)
mask_values_chooser.get_mask_values(input_ids)
<tf.RaggedTensor [[1, 1, 1, 1, 1, 1], [1, 1, 1, 1], [16, 1, 12, 1, 1]]>

W przypadku zasilania RaggedTensor wejściu, text.MaskValuesChooser zwraca RaggedTensor tego samego kształtu albo z _MASK_VALUE (0), a losowy numer identyfikacyjny, lub tej samej niezmienionej id.

Generowanie danych wejściowych dla zadania modelu języka maskowanego

Teraz, gdy mamy RandomItemSelector pomóc nam wybrać elementy do maskowania i text.MaskValuesChooser przypisać wartości, możemy użyć text.mask_language_model() , aby zebrać wszystkie wejścia tego zadania dla naszego modelu BERT.

masked_token_ids, masked_pos, masked_lm_ids = text.mask_language_model(
  segments_combined,
  item_selector=random_selector, mask_values_chooser=mask_values_chooser)
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/util/dispatch.py:206: batch_gather (from tensorflow.python.ops.array_ops) is deprecated and will be removed after 2017-10-25.
Instructions for updating:
`tf.batch_gather` is deprecated, please use `tf.gather` with `batch_dims=-1` instead.

Chodźmy nurkować głębiej i zbadanie wyjścia mask_language_model() . Wyjście masked_token_ids jest:

masked_token_ids
<tf.RaggedTensor [[3, 22, 9, 25, 23, 1, 10, 28, 14, 1, 13, 7, 4, 16, 11, 19, 6, 28, 30, 21, 0, 4], [3, 18, 12, 15, 13, 8, 4, 21, 28, 1, 1, 29, 4]]>

Pamiętaj, że nasze wejście jest kodowane przy użyciu słownictwa. Gdybyśmy dekodować masked_token_ids pomocą naszego słownictwa, otrzymujemy:

tf.gather(_VOCAB, masked_token_ids)
<tf.RaggedTensor [[b'[CLS]', b'Sp', b'##onge', b'box', b'Sq', b'[MASK]', b'##pants', b'is', b'an', b'[MASK]', b'##ven', b'##ger', b'[SEP]', b'Bar', b'##uare', b'Ob', b'##ama', b'is', b'the', b'President', b'[UNK]', b'[SEP]'], [b'[CLS]', b'Mar', b'##vel', b'A', b'##ven', b'##gers', b'[SEP]', b'President', b'is', b'[MASK]', b'[MASK]', b'office', b'[SEP]']]>

Należy zauważyć, że pewne znaczniki wordpiece zastąpiono albo [MASK] , [RANDOM] lub różne wartości identyfikatora. masked_pos wyjście daje nam indeksy (w odpowiedniej partii) z tokenów, które zostały zastąpione.

masked_pos
<tf.RaggedTensor [[3, 5, 9, 14], [9, 10]]>

masked_lm_ids daje nam pierwotną wartość tokenu.

masked_lm_ids
<tf.RaggedTensor [[24, 11, 15, 5], [30, 27]]>

Możemy ponownie zdekodować identyfikatory tutaj, aby uzyskać wartości czytelne dla człowieka.

tf.gather(_VOCAB, masked_lm_ids)
<tf.RaggedTensor [[b'bob', b'##uare', b'A', b'##ack'], [b'the', b'highest']]>

Wejścia modelu dopełniania

Teraz, gdy mamy wszystkie wejścia do naszego modelu, ostatni krok w naszej wyprzedzającym jest pakowanie ich w stałej 2-wymiarowej Tensor s z wyściółką, a także generowania maski Tensor wskazujący wartości, które są wartościami kłódek. Możemy użyć text.pad_model_inputs() , aby pomóc nam w realizacji tego zadania.

# Prepare and pad combined segment inputs
input_word_ids, input_mask = text.pad_model_inputs(
  masked_token_ids, max_seq_length=_MAX_SEQ_LEN)
input_type_ids, _ = text.pad_model_inputs(
  masked_token_ids, max_seq_length=_MAX_SEQ_LEN)

# Prepare and pad masking task inputs
masked_lm_positions, masked_lm_weights = text.pad_model_inputs(
  masked_token_ids, max_seq_length=_MAX_PREDICTIONS_PER_BATCH)
masked_lm_ids, _ = text.pad_model_inputs(
  masked_lm_ids, max_seq_length=_MAX_PREDICTIONS_PER_BATCH)

model_inputs = {
    "input_word_ids": input_word_ids,
    "input_mask": input_mask,
    "input_type_ids": input_type_ids,
    "masked_lm_ids": masked_lm_ids,
    "masked_lm_positions": masked_lm_positions,
    "masked_lm_weights": masked_lm_weights,
}
model_inputs
{'input_word_ids': <tf.Tensor: shape=(2, 8), dtype=int64, numpy=
 array([[ 3, 22,  9, 25, 23,  1, 10, 28],
        [ 3, 18, 12, 15, 13,  8,  4, 21]])>,
 'input_mask': <tf.Tensor: shape=(2, 8), dtype=int64, numpy=
 array([[1, 1, 1, 1, 1, 1, 1, 1],
        [1, 1, 1, 1, 1, 1, 1, 1]])>,
 'input_type_ids': <tf.Tensor: shape=(2, 8), dtype=int64, numpy=
 array([[ 3, 22,  9, 25, 23,  1, 10, 28],
        [ 3, 18, 12, 15, 13,  8,  4, 21]])>,
 'masked_lm_ids': <tf.Tensor: shape=(2, 5), dtype=int64, numpy=
 array([[24, 11, 15,  5,  0],
        [30, 27,  0,  0,  0]])>,
 'masked_lm_positions': <tf.Tensor: shape=(2, 5), dtype=int64, numpy=
 array([[ 3, 22,  9, 25, 23],
        [ 3, 18, 12, 15, 13]])>,
 'masked_lm_weights': <tf.Tensor: shape=(2, 5), dtype=int64, numpy=
 array([[1, 1, 1, 1, 1],
        [1, 1, 1, 1, 1]])>}

Przejrzeć

Przyjrzyjmy się, co mamy do tej pory i zbudujmy naszą funkcję przetwarzania wstępnego. Oto, co mamy:

def bert_pretrain_preprocess(vocab_table, features):
  # Input is a string Tensor of documents, shape [batch, 1].
  text_a = features["text_a"]
  text_b = features["text_b"]

  # Tokenize segments to shape [num_sentences, (num_words)] each.
  tokenizer = text.BertTokenizer(
      vocab_table,
      token_out_type=tf.int64)
  segments = [tokenizer.tokenize(text).merge_dims(
      1, -1) for text in (text_a, text_b)]

  # Truncate inputs to a maximum length.
  trimmer = text.RoundRobinTrimmer(max_seq_length=6)
  trimmed_segments = trimmer.trim(segments)

  # Combine segments, get segment ids and add special tokens.
  segments_combined, segment_ids = text.combine_segments(
      trimmed_segments,
      start_of_sequence_id=_START_TOKEN,
      end_of_segment_id=_END_TOKEN)

  # Apply dynamic masking task.
  masked_input_ids, masked_lm_positions, masked_lm_ids = (
      text.mask_language_model(
        segments_combined,
        random_selector,
        mask_values_chooser,
      )
  )

  # Prepare and pad combined segment inputs
  input_word_ids, input_mask = text.pad_model_inputs(
    masked_token_ids, max_seq_length=_MAX_SEQ_LEN)
  input_type_ids, _ = text.pad_model_inputs(
    masked_token_ids, max_seq_length=_MAX_SEQ_LEN)

  # Prepare and pad masking task inputs
  masked_lm_positions, masked_lm_weights = text.pad_model_inputs(
    masked_token_ids, max_seq_length=_MAX_PREDICTIONS_PER_BATCH)
  masked_lm_ids, _ = text.pad_model_inputs(
    masked_lm_ids, max_seq_length=_MAX_PREDICTIONS_PER_BATCH)

  model_inputs = {
      "input_word_ids": input_word_ids,
      "input_mask": input_mask,
      "input_type_ids": input_type_ids,
      "masked_lm_ids": masked_lm_ids,
      "masked_lm_positions": masked_lm_positions,
      "masked_lm_weights": masked_lm_weights,
  }
  return model_inputs

Wcześniej skonstruował tf.data.Dataset i możemy teraz skorzystać z naszej zmontowany przebiegu wyprzedzającego funkcji bert_pretrain_preprocess() w Dataset.map() . Pozwala nam to na utworzenie potoku wejściowego do przekształcania naszych nieprzetworzonych danych łańcuchowych na dane wejściowe liczb całkowitych i dostarczania ich bezpośrednio do naszego modelu.

dataset = tf.data.Dataset.from_tensors(examples)
dataset = dataset.map(functools.partial(
    bert_pretrain_preprocess, lookup_table))

next(iter(dataset))
2021-08-17 11:09:58.366778: I tensorflow/compiler/mlir/mlir_graph_optimization_pass.cc:176] None of the MLIR Optimization Passes are enabled (registered 2)
2021-08-17 11:09:58.441242: I tensorflow/core/platform/profile_utils/cpu_utils.cc:114] CPU Frequency: 2000165000 Hz
{'input_word_ids': <tf.Tensor: shape=(2, 8), dtype=int64, numpy=
 array([[ 3, 22,  9, 25, 23,  1, 10, 28],
        [ 3, 18, 12, 15, 13,  8,  4, 21]])>,
 'input_mask': <tf.Tensor: shape=(2, 8), dtype=int64, numpy=
 array([[1, 1, 1, 1, 1, 1, 1, 1],
        [1, 1, 1, 1, 1, 1, 1, 1]])>,
 'input_type_ids': <tf.Tensor: shape=(2, 8), dtype=int64, numpy=
 array([[ 3, 22,  9, 25, 23,  1, 10, 28],
        [ 3, 18, 12, 15, 13,  8,  4, 21]])>,
 'masked_lm_ids': <tf.Tensor: shape=(2, 5), dtype=int64, numpy=
 array([[ 5, 19,  0,  0,  0],
        [12, 28,  0,  0,  0]])>,
 'masked_lm_positions': <tf.Tensor: shape=(2, 5), dtype=int64, numpy=
 array([[ 3, 22,  9, 25, 23],
        [ 3, 18, 12, 15, 13]])>,
 'masked_lm_weights': <tf.Tensor: shape=(2, 5), dtype=int64, numpy=
 array([[1, 1, 1, 1, 1],
        [1, 1, 1, 1, 1]])>}
  • Tekst klasyfikować z Bertem - tutorial jak korzystać z pretrained modelu BERT tekstu sklasyfikować. Jest to miłe uzupełnienie teraz, gdy znasz już sposób wstępnego przetwarzania danych wejściowych używanych przez model BERT.

  • Tokenizing z TF Tekst - Tutorial szczegółowo różne rodzaje tokenizers które istnieją w TF.Text.

  • Obchodzenie Tekst z RaggedTensor - szczegółowy przewodnik na temat tworzenia, wykorzystania i manipulowania RaggedTensor s.