Сохраните дату! Google I / O возвращается 18-20 мая Зарегистрируйтесь сейчас
Эта страница переведена с помощью Cloud Translation API.
Switch to English

Классифицируйте текст с помощью BERT

Посмотреть на TensorFlow.org Запускаем в Google Colab Посмотреть на GitHub Скачать блокнот См. Модель TF Hub

Это руководство содержит полный код для точной настройки BERT для выполнения анализа тональности набора данных текстовых обзоров фильмов IMDB. Помимо обучения модели вы узнаете, как предварительно преобразовать текст в соответствующий формат.

В этом блокноте вы:

  • Загрузите набор данных IMDB
  • Загрузите модель BERT из TensorFlow Hub
  • Создайте свою собственную модель, объединив BERT с классификатором
  • Обучите свою собственную модель, отлаживая BERT как часть этого
  • Сохраните свою модель и используйте ее для классификации предложений

Если вы новичок в работе с набором данных IMDB, см. Раздел Базовая классификация текста для получения дополнительных сведений.

О BERT

BERT и другие архитектуры кодировщиков Transformer были безумно успешными в различных задачах NLP (обработка естественного языка). Они вычисляют представления естественного языка в векторном пространстве, которые подходят для использования в моделях глубокого обучения. Семейство моделей BERT использует архитектуру кодировщика Transformer для обработки каждого токена входящего текста в полном контексте всех токенов до и после, отсюда и название: Представления двунаправленного кодера от Transformers.

Модели BERT обычно предварительно обучаются на большом корпусе текста, а затем настраиваются для конкретных задач.

Настраивать

# A dependency of the preprocessing for BERT inputs
pip install -q tensorflow-text

Вы будете использовать оптимизатор AdamW из tensorflow / models .

pip install -q tf-models-official
import os
import shutil

import tensorflow as tf
import tensorflow_hub as hub
import tensorflow_text as text
from official.nlp import optimization  # to create AdamW optmizer

import matplotlib.pyplot as plt

tf.get_logger().setLevel('ERROR')

Анализ настроений

Эта записная книжка обучает модель анализа настроений для классификации отзывов о фильмах как положительных или отрицательных на основе текста обзора.

Вы будете использовать набор данных Large Movie Review , содержащий текст 50 000 обзоров фильмов из базы данных Internet Movie .

Загрузите набор данных IMDB

Давайте загрузим и извлечем набор данных, а затем исследуем структуру каталогов.

url = 'https://ai.stanford.edu/~amaas/data/sentiment/aclImdb_v1.tar.gz'

dataset = tf.keras.utils.get_file('aclImdb_v1.tar.gz', url,
                                  untar=True, cache_dir='.',
                                  cache_subdir='')

dataset_dir = os.path.join(os.path.dirname(dataset), 'aclImdb')

train_dir = os.path.join(dataset_dir, 'train')

# remove unused folders to make it easier to load the data
remove_dir = os.path.join(train_dir, 'unsup')
shutil.rmtree(remove_dir)
Downloading data from https://ai.stanford.edu/~amaas/data/sentiment/aclImdb_v1.tar.gz
84131840/84125825 [==============================] - 7s 0us/step

Затем вы воспользуетесь утилитой text_dataset_from_directory для создания помеченногоtf.data.Dataset .

Набор данных IMDB уже разделен на обучающий и тестовый, но в нем отсутствует набор для проверки. Давайте создадим набор проверки, используя разделение данных обучения 80:20, используя аргумент validation_split ниже.

AUTOTUNE = tf.data.AUTOTUNE
batch_size = 32
seed = 42

raw_train_ds = tf.keras.preprocessing.text_dataset_from_directory(
    'aclImdb/train',
    batch_size=batch_size,
    validation_split=0.2,
    subset='training',
    seed=seed)

class_names = raw_train_ds.class_names
train_ds = raw_train_ds.cache().prefetch(buffer_size=AUTOTUNE)

val_ds = tf.keras.preprocessing.text_dataset_from_directory(
    'aclImdb/train',
    batch_size=batch_size,
    validation_split=0.2,
    subset='validation',
    seed=seed)

val_ds = val_ds.cache().prefetch(buffer_size=AUTOTUNE)

test_ds = tf.keras.preprocessing.text_dataset_from_directory(
    'aclImdb/test',
    batch_size=batch_size)

test_ds = test_ds.cache().prefetch(buffer_size=AUTOTUNE)
Found 25000 files belonging to 2 classes.
Using 20000 files for training.
Found 25000 files belonging to 2 classes.
Using 5000 files for validation.
Found 25000 files belonging to 2 classes.

Давайте посмотрим на несколько обзоров.

for text_batch, label_batch in train_ds.take(1):
  for i in range(3):
    print(f'Review: {text_batch.numpy()[i]}')
    label = label_batch.numpy()[i]
    print(f'Label : {label} ({class_names[label]})')
Review: b'"Pandemonium" is a horror movie spoof that comes off more stupid than funny. Believe me when I tell you, I love comedies. Especially comedy spoofs. "Airplane", "The Naked Gun" trilogy, "Blazing Saddles", "High Anxiety", and "Spaceballs" are some of my favorite comedies that spoof a particular genre. "Pandemonium" is not up there with those films. Most of the scenes in this movie had me sitting there in stunned silence because the movie wasn\'t all that funny. There are a few laughs in the film, but when you watch a comedy, you expect to laugh a lot more than a few times and that\'s all this film has going for it. Geez, "Scream" had more laughs than this film and that was more of a horror film. How bizarre is that?<br /><br />*1/2 (out of four)'
Label : 0 (neg)
Review: b"David Mamet is a very interesting and a very un-equal director. His first movie 'House of Games' was the one I liked best, and it set a series of films with characters whose perspective of life changes as they get into complicated situations, and so does the perspective of the viewer.<br /><br />So is 'Homicide' which from the title tries to set the mind of the viewer to the usual crime drama. The principal characters are two cops, one Jewish and one Irish who deal with a racially charged area. The murder of an old Jewish shop owner who proves to be an ancient veteran of the Israeli Independence war triggers the Jewish identity in the mind and heart of the Jewish detective.<br /><br />This is were the flaws of the film are the more obvious. The process of awakening is theatrical and hard to believe, the group of Jewish militants is operatic, and the way the detective eventually walks to the final violent confrontation is pathetic. The end of the film itself is Mamet-like smart, but disappoints from a human emotional perspective.<br /><br />Joe Mantegna and William Macy give strong performances, but the flaws of the story are too evident to be easily compensated."
Label : 0 (neg)
Review: b'Great documentary about the lives of NY firefighters during the worst terrorist attack of all time.. That reason alone is why this should be a must see collectors item.. What shocked me was not only the attacks, but the"High Fat Diet" and physical appearance of some of these firefighters. I think a lot of Doctors would agree with me that,in the physical shape they were in, some of these firefighters would NOT of made it to the 79th floor carrying over 60 lbs of gear. Having said that i now have a greater respect for firefighters and i realize becoming a firefighter is a life altering job. The French have a history of making great documentary\'s and that is what this is, a Great Documentary.....'
Label : 1 (pos)

Загрузка моделей из TensorFlow Hub

Здесь вы можете выбрать, какую модель BERT загрузить из TensorFlow Hub и выполнить точную настройку. Доступно несколько моделей BERT.

  • BERT-Base , Uncased и еще семь моделей с обученными весами, выпущенные авторами оригинального BERT.
  • Маленькие BERT имеют ту же общую архитектуру, но меньше и / или меньше блоков Transformer, что позволяет исследовать компромиссы между скоростью, размером и качеством.
  • АЛЬБЕРТ : четыре разных размера «облегченного BERT», который уменьшает размер модели (но не время вычислений) за счет разделения параметров между слоями.
  • Эксперты BERT : восемь моделей, каждая из которых имеет базовую архитектуру BERT, но предлагает выбор между различными доменами предварительного обучения, чтобы более точно согласовать с целевой задачей.
  • Electra имеет ту же архитектуру, что и BERT (в трех разных размерах), но предварительно обучается в качестве дискриминатора в настройке, напоминающей Generative Adversarial Network (GAN).
  • BERT с Talking-Heads Attention и Gated GELU [ base , large ] имеет два улучшения ядра архитектуры Transformer.

В документации модели на TensorFlow Hub есть более подробная информация и ссылки на исследовательскую литературу. Перейдите по ссылкам выше или щелкните URL-адрес tfhub.dev напечатанный после выполнения следующей ячейки.

Предлагается начать с малого BERT (с меньшим количеством параметров), так как они быстрее настраиваются. Если вам нравится небольшая модель, но с более высокой точностью, ALBERT может быть вашим следующим вариантом. Если вы хотите еще большей точности, выберите один из классических размеров BERT или их недавние усовершенствования, такие как Electra, Talking Heads или BERT Expert.

Помимо моделей, представленных ниже, существует несколько версий моделей, которые больше по размеру и могут обеспечить еще лучшую точность, но они слишком велики для точной настройки на одном графическом процессоре. Вы сможете сделать это в задачах Решить КЛЕЙ с помощью BERT в колабе TPU .

В приведенном ниже коде вы увидите, что переключения URL-адреса tfhub.dev достаточно, чтобы попробовать любую из этих моделей, потому что все различия между ними инкапсулированы в SavedModels из TF Hub.

Выберите модель BERT для точной настройки

BERT model selected           : https://tfhub.dev/tensorflow/small_bert/bert_en_uncased_L-4_H-512_A-8/1
Preprocess model auto-selected: https://tfhub.dev/tensorflow/bert_en_uncased_preprocess/3

Модель предварительной обработки

Текстовые вводы необходимо преобразовать в числовые идентификаторы токенов и расположить в нескольких тензорах перед вводом в BERT. TensorFlow Hub предоставляет соответствующую модель предварительной обработки для каждой из рассмотренных выше моделей BERT, которая реализует это преобразование с помощью операций TF из библиотеки TF.text. Нет необходимости запускать чистый код Python вне вашей модели TensorFlow для предварительной обработки текста.

Модель предварительной обработки должна быть той, на которую ссылается документация модели BERT, которую вы можете прочитать по указанному выше URL-адресу. Для моделей BERT из раскрывающегося списка выше модель предварительной обработки выбирается автоматически.

bert_preprocess_model = hub.KerasLayer(tfhub_handle_preprocess)

Давайте попробуем модель предварительной обработки на каком-то тексте и посмотрим, что получится:

text_test = ['this is such an amazing movie!']
text_preprocessed = bert_preprocess_model(text_test)

print(f'Keys       : {list(text_preprocessed.keys())}')
print(f'Shape      : {text_preprocessed["input_word_ids"].shape}')
print(f'Word Ids   : {text_preprocessed["input_word_ids"][0, :12]}')
print(f'Input Mask : {text_preprocessed["input_mask"][0, :12]}')
print(f'Type Ids   : {text_preprocessed["input_type_ids"][0, :12]}')
Keys       : ['input_type_ids', 'input_mask', 'input_word_ids']
Shape      : (1, 128)
Word Ids   : [ 101 2023 2003 2107 2019 6429 3185  999  102    0    0    0]
Input Mask : [1 1 1 1 1 1 1 1 1 0 0 0]
Type Ids   : [0 0 0 0 0 0 0 0 0 0 0 0]

Как видите, теперь у вас есть 3 выхода предварительной обработки, которые использовала бы модель BERT ( input_words_id , input_mask и input_type_ids ).

Еще несколько важных моментов:

  • Входные данные усекаются до 128 токенов. Количество токенов можно настроить, и вы можете увидеть более подробную информацию о задачах Solve GLUE с помощью BERT в колабе TPU .
  • input_type_ids имеет только одно значение (0), потому что это ввод с одним предложением. Для ввода нескольких предложений у него будет одно число для каждого ввода.

Поскольку этот текстовый препроцессор является моделью TensorFlow, его можно напрямую включить в вашу модель.

Использование модели BERT

Прежде чем использовать BERT в вашей собственной модели, давайте посмотрим на его результаты. Вы загрузите его из TF Hub и увидите возвращенные значения.

bert_model = hub.KerasLayer(tfhub_handle_encoder)
bert_results = bert_model(text_preprocessed)

print(f'Loaded BERT: {tfhub_handle_encoder}')
print(f'Pooled Outputs Shape:{bert_results["pooled_output"].shape}')
print(f'Pooled Outputs Values:{bert_results["pooled_output"][0, :12]}')
print(f'Sequence Outputs Shape:{bert_results["sequence_output"].shape}')
print(f'Sequence Outputs Values:{bert_results["sequence_output"][0, :12]}')
Loaded BERT: https://tfhub.dev/tensorflow/small_bert/bert_en_uncased_L-4_H-512_A-8/1
Pooled Outputs Shape:(1, 512)
Pooled Outputs Values:[ 0.76262873  0.9928097  -0.18611881  0.36673862  0.15233737  0.6550447
  0.9681153  -0.9486272   0.00216161 -0.9877732   0.06842697 -0.9763058 ]
Sequence Outputs Shape:(1, 128, 512)
Sequence Outputs Values:[[-0.28946346  0.34321272  0.33231515 ...  0.21300808  0.71020764
  -0.05771098]
 [-0.28742087  0.31980985 -0.23018607 ...  0.58455014 -0.21329728
   0.72692114]
 [-0.6615697   0.6887685  -0.87432986 ...  0.10877222 -0.2617324
   0.4785539 ]
 ...
 [-0.22561133 -0.2892562  -0.07064445 ...  0.47565985  0.83277136
   0.40025374]
 [-0.29824236 -0.27473187 -0.05450562 ...  0.48849723  1.0955356
   0.1816333 ]
 [-0.4437813   0.00930756  0.07223685 ...  0.17290069  1.1833248
   0.07897997]]

Модели BERT возвращают карту с 3 важными ключами: pooled_output , sequence_output , encoder_outputs :

  • pooled_output для представления каждой входной последовательности в целом. Форма [batch_size, H] . Вы можете думать об этом как о встраивании для всего обзора фильма.
  • sequence_output представляет каждый входной токен в контексте. Форма - [batch_size, seq_length, H] . Вы можете думать об этом как о контекстном внедрении каждого токена в обзоре фильма.
  • encoder_outputs - это промежуточные активации блоков L преобразователя. outputs["encoder_outputs"][i] - это тензор формы [batch_size, seq_length, 1024] с выходами i-го блока Transformer для 0 <= i < L . Последнее значение списка равно sequence_output .

Для тонкой настройки вы собираетесь использовать массив pooled_output .

Определите вашу модель

Вы создадите очень простую точно настроенную модель с моделью предварительной обработки, выбранной моделью BERT, одним плотным и выпадающим слоем.

def build_classifier_model():
  text_input = tf.keras.layers.Input(shape=(), dtype=tf.string, name='text')
  preprocessing_layer = hub.KerasLayer(tfhub_handle_preprocess, name='preprocessing')
  encoder_inputs = preprocessing_layer(text_input)
  encoder = hub.KerasLayer(tfhub_handle_encoder, trainable=True, name='BERT_encoder')
  outputs = encoder(encoder_inputs)
  net = outputs['pooled_output']
  net = tf.keras.layers.Dropout(0.1)(net)
  net = tf.keras.layers.Dense(1, activation=None, name='classifier')(net)
  return tf.keras.Model(text_input, net)

Давайте проверим, что модель работает с выходными данными модели предварительной обработки.

classifier_model = build_classifier_model()
bert_raw_result = classifier_model(tf.constant(text_test))
print(tf.sigmoid(bert_raw_result))
tf.Tensor([[0.5588537]], shape=(1, 1), dtype=float32)

Вывод, конечно, не имеет смысла, потому что модель еще не обучена.

Давайте посмотрим на структуру модели.

tf.keras.utils.plot_model(classifier_model)

PNG

Модельное обучение

Теперь у вас есть все необходимое для обучения модели, включая модуль предварительной обработки, кодировщик BERT, данные и классификатор.

Функция потерь

Поскольку это проблема двоичной классификации и модель выводит вероятность (единичный слой), вы будете использовать функцию losses.BinaryCrossentropy loss.

loss = tf.keras.losses.BinaryCrossentropy(from_logits=True)
metrics = tf.metrics.BinaryAccuracy()

Оптимизатор

Для тонкой настройки давайте воспользуемся тем же оптимизатором, с которым изначально обучался BERT: «Adaptive Moments» (Адам). Этот оптимизатор минимизирует потерю прогнозов и выполняет регуляризацию по убыванию веса (без использования моментов), что также известно как AdamW .

Для скорости обучения ( init_lr ) мы используем тот же график, что и предварительное обучение BERT: линейное init_lr условной начальной скорости обучения с префиксом линейной фазы разминки в течение первых 10% шагов обучения ( num_warmup_steps ). Согласно статье BERT, начальная скорость обучения меньше для точной настройки (лучше всего 5e-5, 3e-5, 2e-5).

epochs = 5
steps_per_epoch = tf.data.experimental.cardinality(train_ds).numpy()
num_train_steps = steps_per_epoch * epochs
num_warmup_steps = int(0.1*num_train_steps)

init_lr = 3e-5
optimizer = optimization.create_optimizer(init_lr=init_lr,
                                          num_train_steps=num_train_steps,
                                          num_warmup_steps=num_warmup_steps,
                                          optimizer_type='adamw')

Загрузка модели BERT и обучение

Используя classifier_model вы создали ранее, вы можете скомпилировать модель с потерями, метрикой и оптимизатором.

classifier_model.compile(optimizer=optimizer,
                         loss=loss,
                         metrics=metrics)
print(f'Training model with {tfhub_handle_encoder}')
history = classifier_model.fit(x=train_ds,
                               validation_data=val_ds,
                               epochs=epochs)
Training model with https://tfhub.dev/tensorflow/small_bert/bert_en_uncased_L-4_H-512_A-8/1
Epoch 1/5
625/625 [==============================] - 93s 140ms/step - loss: 0.5837 - binary_accuracy: 0.6626 - val_loss: 0.4231 - val_binary_accuracy: 0.8318
Epoch 2/5
625/625 [==============================] - 88s 140ms/step - loss: 0.3558 - binary_accuracy: 0.8365 - val_loss: 0.3813 - val_binary_accuracy: 0.8438
Epoch 3/5
625/625 [==============================] - 88s 141ms/step - loss: 0.2748 - binary_accuracy: 0.8825 - val_loss: 0.3932 - val_binary_accuracy: 0.8470
Epoch 4/5
625/625 [==============================] - 88s 141ms/step - loss: 0.2029 - binary_accuracy: 0.9200 - val_loss: 0.4420 - val_binary_accuracy: 0.8532
Epoch 5/5
625/625 [==============================] - 87s 139ms/step - loss: 0.1607 - binary_accuracy: 0.9403 - val_loss: 0.4783 - val_binary_accuracy: 0.8504

Оцените модель

Посмотрим, как модель работает. Будут возвращены два значения. Потеря (число, которое представляет ошибку, чем ниже значение, тем лучше), и точность.

loss, accuracy = classifier_model.evaluate(test_ds)

print(f'Loss: {loss}')
print(f'Accuracy: {accuracy}')
782/782 [==============================] - 63s 80ms/step - loss: 0.4651 - binary_accuracy: 0.8541
Loss: 0.4651116132736206
Accuracy: 0.8541200160980225

Постройте график точности и потерь с течением времени

На основе объекта History возвращаемого model.fit() . Вы можете построить график потерь при обучении и проверке для сравнения, а также о точности обучения и проверки:

history_dict = history.history
print(history_dict.keys())

acc = history_dict['binary_accuracy']
val_acc = history_dict['val_binary_accuracy']
loss = history_dict['loss']
val_loss = history_dict['val_loss']

epochs = range(1, len(acc) + 1)
fig = plt.figure(figsize=(10, 6))
fig.tight_layout()

plt.subplot(2, 1, 1)
# "bo" is for "blue dot"
plt.plot(epochs, loss, 'r', label='Training loss')
# b is for "solid blue line"
plt.plot(epochs, val_loss, 'b', label='Validation loss')
plt.title('Training and validation loss')
# plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.legend()

plt.subplot(2, 1, 2)
plt.plot(epochs, acc, 'r', label='Training acc')
plt.plot(epochs, val_acc, 'b', label='Validation acc')
plt.title('Training and validation accuracy')
plt.xlabel('Epochs')
plt.ylabel('Accuracy')
plt.legend(loc='lower right')
dict_keys(['loss', 'binary_accuracy', 'val_loss', 'val_binary_accuracy'])
<matplotlib.legend.Legend at 0x7f884cfea160>

PNG

На этом графике красные линии представляют потери при обучении и точность, а синие линии - потери при проверке и точность.

Экспорт для вывода

Теперь вы просто сохраняете настроенную модель для дальнейшего использования.

dataset_name = 'imdb'
saved_model_path = './{}_bert'.format(dataset_name.replace('/', '_'))

classifier_model.save(saved_model_path, include_optimizer=False)
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 310). 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 310). These functions will not be directly callable after loading.

Давайте перезагрузим модель, чтобы вы могли попробовать ее бок о бок с моделью, которая все еще находится в памяти.

reloaded_model = tf.saved_model.load(saved_model_path)

Здесь вы можете протестировать свою модель на любом предложении, просто добавьте в приведенную ниже переменную примеров.

def print_my_examples(inputs, results):
  result_for_printing = \
    [f'input: {inputs[i]:<30} : score: {results[i][0]:.6f}'
                         for i in range(len(inputs))]
  print(*result_for_printing, sep='\n')
  print()


examples = [
    'this is such an amazing movie!',  # this is the same sentence tried earlier
    'The movie was great!',
    'The movie was meh.',
    'The movie was okish.',
    'The movie was terrible...'
]

reloaded_results = tf.sigmoid(reloaded_model(tf.constant(examples)))
original_results = tf.sigmoid(classifier_model(tf.constant(examples)))

print('Results from the saved model:')
print_my_examples(examples, reloaded_results)
print('Results from the model in memory:')
print_my_examples(examples, original_results)
Results from the saved model:
input: this is such an amazing movie! : score: 0.999509
input: The movie was great!           : score: 0.994210
input: The movie was meh.             : score: 0.892940
input: The movie was okish.           : score: 0.021850
input: The movie was terrible...      : score: 0.001355

Results from the model in memory:
input: this is such an amazing movie! : score: 0.999509
input: The movie was great!           : score: 0.994210
input: The movie was meh.             : score: 0.892940
input: The movie was okish.           : score: 0.021850
input: The movie was terrible...      : score: 0.001355

Если вы хотите использовать свою модель для обслуживания TF , помните, что она будет вызывать вашу SavedModel через одну из своих именованных подписей. В Python вы можете протестировать их следующим образом:

serving_results = reloaded_model \
            .signatures['serving_default'](tf.constant(examples))

serving_results = tf.sigmoid(serving_results['classifier'])

print_my_examples(examples, serving_results)
input: this is such an amazing movie! : score: 0.999509
input: The movie was great!           : score: 0.994210
input: The movie was meh.             : score: 0.892940
input: The movie was okish.           : score: 0.021850
input: The movie was terrible...      : score: 0.001355

Следующие шаги

В качестве следующего шага вы можете попробовать Решить задачи GLUE с помощью BERT в учебнике TPU, который работает на TPU и показывает, как работать с несколькими входами.