На этой странице описывается, как TF2 SavedModels для задач, связанных с текстом, должен реализовывать Reusable SavedModel API . (Это заменяет и расширяет Common Signatures for Text для ныне устаревшего формата TF1 Hub .)
Обзор
Существует несколько API для вычисления встраивания текста (также известного как плотные представления текста или векторы текстовых признаков).
API для внедрения текста из текстовых входов реализуется с помощью SavedModel, который сопоставляет пакет строк с пакетом векторов внедрения. Это очень просто в использовании, и многие модели на TF Hub реализовали это. Однако это не позволяет провести тонкую настройку модели на ТПУ.
API для встраивания текста с предварительно обработанными входными данными решает ту же задачу, но реализуется двумя отдельными моделями SavedModels:
- препроцессор , который может работать внутри входного конвейера tf.data и преобразовывать строки и другие данные переменной длины в числовые тензоры,
- кодер , который принимает результаты препроцессора и выполняет обучаемую часть вычислений внедрения.
Такое разделение позволяет асинхронно обрабатывать входные данные перед их подачей в цикл обучения. В частности, он позволяет создавать кодеры, которые можно запускать и настраивать на TPU .
API для внедрения текста с помощью кодировщиков Transformer расширяет API для внедрения текста из предварительно обработанных входных данных на частный случай BERT и других кодировщиков Transformer.
- Препроцессор расширен для создания входных данных кодировщика из более чем одного сегмента входного текста.
- Кодер Transformer предоставляет контекстно-зависимые внедрения отдельных токенов.
В каждом случае текстовые входные данные представляют собой строки в кодировке UTF-8, обычно представляющие собой обычный текст, если в документации модели не указано иное.
Независимо от API, разные модели были предварительно обучены на тексте с разных языков и доменов и с учетом разных задач. Поэтому не каждая модель встраивания текста подходит для решения каждой задачи.
Встраивание текста из текстовых вводов
SavedModel для встраивания текста из текстовых входов принимает пакет входных данных в строке Tensor формы [batch_size]
и сопоставляет их с тензором float32 формы [batch_size, dim]
с плотными представлениями (векторами признаков) входных данных.
Краткое описание использования
obj = hub.load("path/to/model")
text_input = ["A long sentence.",
"single-word",
"http://example.com"]
embeddings = obj(text_input)
Напомним, из API Reusable SavedModel , что для запуска модели в режиме обучения (например, для исключения) может потребоваться аргумент ключевого слова obj(..., training=True)
и что obj
предоставляет атрибуты .variables
, .trainable_variables
и .regularization_losses
в зависимости от обстоятельств. .
В Керасе обо всем этом заботится
embeddings = hub.KerasLayer("path/to/model", trainable=...)(text_input)
Распределенное обучение
Если встраивание текста используется как часть модели, которая обучается с помощью стратегии распределения, hub.load("path/to/model")
hub.KerasLayer("path/to/model", ...)
, соответственно, должно произойти внутри области DistributionStrategy, чтобы создать переменные модели распределенным способом. Например
with strategy.scope():
...
model = hub.load("path/to/model")
...
Примеры
- Учебное пособие Colab « Классификация текста с обзорами фильмов» .
Встраивание текста с предварительно обработанными входными данными
Встраивание текста с предварительно обработанными входными данными реализуется двумя отдельными моделями SavedModels:
- препроцессор , который сопоставляет строковый тензор формы
[batch_size]
с набором числовых тензоров, - кодер , который принимает набор тензоров, возвращенный препроцессором, выполняет обучаемую часть вычислений внедрения и возвращает набор выходных данных. Выходные данные под ключом
"default"
представляют собой тензор float32 формы[batch_size, dim]
.
Это позволяет запускать препроцессор во входном конвейере, но при этом точно настраивать встраивания, вычисленные кодировщиком, как часть более крупной модели. В частности, он позволяет создавать кодеры, которые можно запускать и настраивать на TPU .
Это деталь реализации, какие тензоры содержатся в выходных данных препроцессора и какие (если таковые имеются) дополнительные тензоры, помимо "default"
содержатся в выходных данных кодировщика.
В документации кодировщика должно быть указано, какой препроцессор с ним использовать. Обычно существует ровно один правильный выбор.
Краткое описание использования
text_input = tf.constant(["A long sentence.",
"single-word",
"http://example.com"])
preprocessor = hub.load("path/to/preprocessor") # Must match `encoder`.
encoder_inputs = preprocessor(text_input)
encoder = hub.load("path/to/encoder")
encoder_outputs = encoder(encoder_inputs)
embeddings = encoder_outputs["default"]
Вспомним из API Reusable SavedModel , что для запуска кодировщика в режиме обучения (например, для исключения) может потребоваться аргумент ключевого слова encoder(..., training=True)
, и этот encoder
предоставляет атрибуты .variables
, .trainable_variables
и .regularization_losses
в зависимости от обстоятельств. .
Модель preprocessor
может иметь .variables
но не предназначена для дальнейшего обучения. Предварительная обработка не зависит от режима: если preprocessor()
вообще имеет аргумент training=...
, он не имеет никакого эффекта.
В Керасе обо всем этом заботится
encoder_inputs = hub.KerasLayer("path/to/preprocessor")(text_input)
encoder_outputs = hub.KerasLayer("path/to/encoder", trainable=True)(encoder_inputs)
embeddings = encoder_outputs["default"]
Распределенное обучение
Если кодировщик используется как часть модели, которая обучается с помощью стратегии распределения, hub.load("path/to/encoder")
hub.KerasLayer("path/to/encoder", ...)
, соответственно, должно произойти внутри
with strategy.scope():
...
чтобы воссоздать переменные кодировщика распределенным способом.
Аналогично, если препроцессор является частью обученной модели (как в простом примере выше), его также необходимо загрузить в области стратегии распределения. Однако если препроцессор используется во входном конвейере (например, в вызываемом объекте, передаваемом в tf.data.Dataset.map()
), его загрузка должна происходить за пределами области действия стратегии распределения, чтобы разместить его переменные (если таковые имеются). ) на главном процессоре.
Примеры
- Учебное пособие Colab Классифицируйте текст с помощью BERT .
Встраивание текста с помощью Transformer Encoders
Кодировщики-трансформеры для текста работают с пакетом входных последовательностей, каждая последовательность содержит n ≥ 1 сегментов токенизированного текста в пределах некоторой специфичной для модели привязки к n . Для BERT и многих его расширений эта граница равна 2, поэтому они принимают отдельные сегменты и пары сегментов.
API для внедрения текста с помощью кодировщиков Transformer расширяет API для внедрения текста с предварительно обработанными входными данными до этого параметра.
Препроцессор
Препроцессор SavedModel для внедрения текста с кодировщиками Transformer реализует API препроцессора SavedModel для внедрения текста с предварительно обработанными входными данными (см. выше), который обеспечивает способ сопоставления односегментных текстовых входных данных непосредственно с входными данными кодера.
Кроме того, препроцессор SavedModel предоставляет вызываемые подобъекты tokenize
для токенизации (отдельно для каждого сегмента) и bert_pack_inputs
для упаковки n токенизированных сегментов в одну входную последовательность для кодировщика. Каждый подобъект соответствует API Reusable SavedModel .
Краткое описание использования
В качестве конкретного примера для двух сегментов текста давайте рассмотрим задачу по следованию предложения, которая спрашивает, подразумевает ли посылка (первый сегмент) гипотезу (второй сегмент) или нет.
preprocessor = hub.load("path/to/preprocessor")
# Tokenize batches of both text inputs.
text_premises = tf.constant(["The quick brown fox jumped over the lazy dog.",
"Good day."])
tokenized_premises = preprocessor.tokenize(text_premises)
text_hypotheses = tf.constant(["The dog was lazy.", # Implied.
"Axe handle!"]) # Not implied.
tokenized_hypotheses = preprocessor.tokenize(text_hypotheses)
# Pack input sequences for the Transformer encoder.
seq_length = 128
encoder_inputs = preprocessor.bert_pack_inputs(
[tokenized_premises, tokenized_hypotheses],
seq_length=seq_length) # Optional argument.
В Keras это вычисление можно выразить как
tokenize = hub.KerasLayer(preprocessor.tokenize)
tokenized_hypotheses = tokenize(text_hypotheses)
tokenized_premises = tokenize(text_premises)
bert_pack_inputs = hub.KerasLayer(
preprocessor.bert_pack_inputs,
arguments=dict(seq_length=seq_length)) # Optional argument.
encoder_inputs = bert_pack_inputs([tokenized_premises, tokenized_hypotheses])
Подробности tokenize
Вызов preprocessor.tokenize()
принимает строку Tensor формы [batch_size]
и возвращает RaggedTensor формы [batch_size, ...]
значениями которой являются идентификаторы токенов int32, представляющие входные строки. После batch_size
может быть r ≥ 1 неровных размеров, но не может быть другого однородного измерения.
- Если r =1, форма имеет вид
[batch_size, (tokens)]
и каждый ввод просто преобразуется в плоскую последовательность токенов. - Если r >1, имеется r -1 дополнительных уровней группировки. Например, tensorflow_text.BertTokenizer использует r =2 для группировки токенов по словам и возвращает форму
[batch_size, (words), (tokens_per_word)]
. От рассматриваемой модели зависит, сколько из этих дополнительных уровней существует, если таковые имеются, и какие группы они представляют.
Пользователь может (но не обязательно) изменять токенизированные входные данные, например, чтобы учесть ограничение seq_length, которое будет применяться при упаковке входных данных кодера. Дополнительные измерения в выходных данных токенизатора могут здесь помочь (например, для соблюдения границ слов), но станут бессмысленными на следующем этапе.
С точки зрения Reusable SavedModel API , объект preprocessor.tokenize
может иметь .variables
, но не предназначен для дальнейшего обучения. Токенизация не зависит от режима: если preprocessor.tokenize()
вообще имеет аргумент training=...
, он не имеет никакого эффекта.
Подробности о bert_pack_inputs
Вызов preprocessor.bert_pack_inputs()
принимает список токенизированных входных данных Python (отдельно для каждого входного сегмента) и возвращает набор тензоров, представляющий пакет входных последовательностей фиксированной длины для модели кодера Transformer.
Каждый токенизированный ввод представляет собой int32 RaggedTensor формы [batch_size, ...]
, где число r неровных измерений после пакетного размера либо равно 1, либо такое же, как в выходных данных preprocessor.tokenize().
(Последнее предназначено только для удобства; дополнительные размеры перед упаковкой распрямляются.)
Упаковка добавляет специальные токены вокруг входных сегментов, как и ожидалось кодировщиком. Вызов bert_pack_inputs()
точно реализует схему упаковки, используемую исходными моделями BERT и многими их расширениями: упакованная последовательность начинается с одного маркера начала последовательности, за которым следуют токенизированные сегменты, каждый из которых завершается одним конечным сегментом. жетон. Остальные позиции до seq_length, если таковые имеются, заполняются токенами заполнения.
Если упакованная последовательность превышает seq_length, bert_pack_inputs()
усекает ее сегменты до префиксов примерно одинакового размера, чтобы упакованная последовательность точно вписывалась в seq_length.
Упаковка не зависит от режима: если preprocessor.bert_pack_inputs()
вообще имеет аргумент training=...
, он не имеет никакого эффекта. Кроме того, не ожидается, что preprocessor.bert_pack_inputs
будет иметь переменные или поддерживать тонкую настройку.
Кодер
Кодировщик вызывается для dict encoder_inputs
так же, как и в API для встраивания текста с предварительно обработанными входными данными (см. выше), включая положения из Reusable SavedModel API .
Краткое описание использования
encoder = hub.load("path/to/encoder")
encoder_outputs = encoder(encoder_inputs)
или эквивалентно в Керасе:
encoder = hub.KerasLayer("path/to/encoder", trainable=True)
encoder_outputs = encoder(encoder_inputs)
Подробности
encoder_outputs
— это набор тензоров со следующими ключами.
-
"sequence_output"
: тензор float32 формы[batch_size, seq_length, dim]
с контекстно-зависимым внедрением каждого токена каждой упакованной входной последовательности. -
"pooled_output"
: тензор float32 формы[batch_size, dim]
с встраиванием каждой входной последовательности в целом, полученной изsequence_output некоторым обучаемым способом. -
"default"
, как того требует API для встраивания текста с предварительно обработанными входными данными: тензор float32 формы[batch_size, dim]
с встраиванием каждой входной последовательности. (Возможно, это просто псевдонимpooled_output.)
Содержимое encoder_inputs
не является строго обязательным для этого определения API. Однако для кодеров, использующих входные данные в стиле BERT, рекомендуется использовать следующие имена (из набора инструментов NLP Modeling Toolkit TensorFlow Model Garden ), чтобы минимизировать трения при смене кодеров и повторном использовании моделей препроцессора:
-
"input_word_ids"
: тензор int32 формы[batch_size, seq_length]
с идентификаторами токенов упакованной входной последовательности (то есть включая токен начала последовательности, токены конца сегмента и дополнение). -
"input_mask"
: тензор int32 формы[batch_size, seq_length]
со значением 1 в положении всех входных токенов, присутствующих перед заполнением, и значением 0 для токенов заполнения. -
"input_type_ids"
: тензор int32 формы[batch_size, seq_length]
с индексом входного сегмента, который привел к появлению входного токена в соответствующей позиции. Первый входной сегмент (индекс 0) включает в себя токен начала последовательности и токен конца сегмента. Второй и последующие сегменты (если они присутствуют) включают соответствующий токен конца сегмента. Токены заполнения снова получают индекс 0.
Распределенное обучение
Для загрузки объектов препроцессора и кодировщика внутри или за пределами области стратегии распределения применяются те же правила, что и в API для встраивания текста с предварительно обработанными входными данными (см. выше).
Примеры
- Учебное пособие Colab Решайте задачи GLUE с помощью BERT на TPU .