Как написать генератор для дообучения моделей на основе трансформеров (Tensorflow)

Вопрос или проблема

Я пытался написать генератор для модели DistillBertFast

## Генератор
def _generator(text=train_texts, label=Y_oh_train, batch_size=1):
# label = tf.ragged.constant(label)
while True:
    for i in range(0,len(text),batch_size):
        yield dict(tokenizer(text[i:i+batch_size], truncation=True, padding=True, return_tensors="tf")), label[i:i+batch_size]

## tf Набор данных
train_dataset = tf.data.Dataset.from_generator(_generator, output_types=({'input_ids':tf.int32,
                                                                      'attention_mask':tf.int32}, tf.float32))

## компиляция модели

    loss_fn=tf.keras.losses.CategoricalCrossentropy(from_logits=True)
model.compile(
    optimizer=tf.keras.optimizers.Adam(learning_rate=0.1),
    loss=loss_fn,
    metrics=[tf.keras.metrics.categorical_accuracy])

## пример данных
train_texts = ['Этот гиф убивает меня Смерть буквально стремится к вам, и вы действительно собираетесь сделать целый разворот на 3 пункта', 'LOVE TEST Сырой Реальный JaDine', 'Мы хотели бы пожелать всем очень счастливого Нового года и всего наилучшего в 2018 году']

Y_oh_train=array([[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0.,
    0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
    0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
    0.],
   [0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
    0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0.,
    0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
    0.],
   [0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
    1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
    0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
    0.]])

Но когда я пытаюсь натренировать модель, выдает ошибку:

    ValueError                                Traceback (most recent call last)
<ipython-input-195-05df82e86e2e> in <module>()
      4     loss=loss_fn,
      5     metrics=[tf.keras.metrics.categorical_accuracy])
----> 6 model.fit(t)

9 frames
/usr/local/lib/python3.7/dist-packages/tensorflow/python/keras/engine/training.py in fit(self, x, y, batch_size, epochs, verbose, callbacks, validation_split, validation_data, shuffle, class_weight, sample_weight, initial_epoch, steps_per_epoch, validation_steps, validation_batch_size, validation_freq, max_queue_size, workers, use_multiprocessing)
   1181                 _r=1):
   1182               callbacks.on_train_batch_begin(step)
-> 1183               tmp_logs = self.train_function(iterator)
   1184               if data_handler.should_sync:
   1185                 context.async_wait()

/usr/local/lib/python3.7/dist-packages/tensorflow/python/eager/def_function.py in __call__(self, *args, **kwds)
    887 
    888       with OptionalXlaContext(self._jit_compile):
--> 889         result = self._call(*args, **kwds)
    890     891       new_tracing_count = self.experimental_get_tracing_count()

/usr/local/lib/python3.7/dist-packages/tensorflow/python/eager/def_function.py in _call(self, *args, **kwds)
    931       # Это первый вызов __call__, поэтому мы должны инициализировать.
    932       initializers = []
--> 933       self._initialize(args, kwds, add_initializers_to=initializers)
    934     finally:
    935       # На этом этапе мы знаем, что инициализация завершена (или менее

/usr/local/lib/python3.7/dist-packages/tensorflow/python/eager/def_function.py in _initialize(self, args, kwds, add_initializers_to)
    762     self._concrete_stateful_fn = (
    763         self._stateful_fn._get_concrete_function_internal_garbage_collected(  # pylint: disable=protected-access
--> 764             *args, **kwds))
    765 
    766     def invalid_creator_scope(*unused_args, **unused_kwds):

/usr/local/lib/python3.7/dist-packages/tensorflow/python/eager/function.py in _get_concrete_function_internal_garbage_collected(self, *args, **kwargs)
   3048       args, kwargs = None, None
   3049     with self._lock:
--> 3050       graph_function, _ = self._maybe_define_function(args, kwargs)
   3051     return graph_function
   3052 

/usr/local/lib/python3.7/dist-packages/tensorflow/python/eager/function.py in _maybe_define_function(self, args, kwargs)
   3442 
   3443           self._function_cache.missed.add(call_context_key)
-> 3444           graph_function = self._create_graph_function(args, kwargs)
   3445           self._function_cache.primary[cache_key] = graph_function
   3446 

/usr/local/lib/python3.7/dist-packages/tensorflow/python/eager/function.py in _create_graph_function(self, args, kwargs, override_flat_arg_shapes)
   3287             arg_names=arg_names,
   3288             override_flat_arg_shapes=override_flat_arg_shapes,
--> 3289             capture_by_value=self._capture_by_value),
   3290         function_spec=self.function_spec,

/usr/local/lib/python3.7/dist-packages/tensorflow/python/framework/func_graph.py in func_graph_from_py_func(name, python_func, args, kwargs, signature, func_graph, autograph, autograph_options, add_control_dependencies, arg_names, op_return_value, collections, capture_by_value, override_flat_arg_shapes)
    997         _, original_func = tf_decorator.unwrap(python_func)
    998 
--> 999       func_outputs = python_func(*func_args, **func_kwargs)
   1000 
   1001       # инвариант: `func_outputs` содержит только тензоры, CompositeTensors,

/usr/local/lib/python3.7/dist-packages/tensorflow/python/eager/def_function.py в функции wrapped_fn(*args, **kwds)
    670         # функция имеет слабую ссылку на себя, чтобы избежать цикла ссылок.
    671         with OptionalXlaContext(compile_with_xla):
--> 672           out = weak_wrapped_fn().__wrapped__(*args, **kwds)
    673         return out
    674 

/usr/local/lib/python3.7/dist-packages/tensorflow/python/framework/func_graph.py in wrapper(*args, **kwargs)
    984           кроме исключения как e:  # pylint:disable=broad-except
    985             если hasattr(e, "ag_error_metadata"):
--> 986               raise e.ag_error_metadata.to_exception(e)
    987             иначе:
    988               raise

ValueError: в пользовательском коде:

    /usr/local/lib/python3.7/dist-packages/tensorflow/python/keras/engine/training.py:855 train_function  *
        return step_function(self, iterator)
    /usr/local/lib/python3.7/dist-packages/transformers/models/distilbert/modeling_tf_distilbert.py:800 call  *
        distilbert_output = self.distilbert(
    /usr/local/lib/python3.7/dist-packages/transformers/models/distilbert/modeling_tf_distilbert.py:415 call  *
        embedding_output = self.embeddings(
    /usr/local/lib/python3.7/dist-packages/transformers/models/distilbert/modeling_tf_distilbert.py:122 call  *
        final_embeddings = self.LayerNorm(inputs=final_embeddings)
    /usr/local/lib/python3.7/dist-packages/tensorflow/python/keras/engine/base_layer.py:1030 __call__  **
        outputs = call_fn(inputs, *args, **kwargs)
    /usr/local/lib/python3.7/dist-packages/tensorflow/python/keras/layers/normalization.py:1218 call
        ndims = len(input_shape)
    /usr/local/lib/python3.7/dist-packages/tensorflow/python/framework/tensor_shape.py:855 __len__
        raise ValueError("Невозможно получить длину формы с неизвестным рангом.")

ValueError: Невозможно получить длину формы с неизвестным рангом.

Я пытался найти обходной путь, не могу задать фиксированную форму тензора в генераторе, потому что не могу контролировать форму вывода из генератора, это будет зависеть от максимальной длины при каждом вызове, я не могу загрузить все данные сразу, так как данные слишком велики, чтобы их загрузить в память

Мне удалось написать генератор, но я должен был указать форму вывода

def padded_tokenizer(text, max_len=80):
    result = dict(tokenizer(text, truncation=True, padding=True, return_tensors="tf", pad_to_multiple_of=2))
    for key in result.keys():
        result[key] = tf.keras.preprocessing.sequence.pad_sequences(result[key], padding="post", maxlen=max_len)
    return result

def _generator(arg=0, batch_size=1):
    if arg==0:
        text=train_texts
        label=Y_oh_train
    if arg==1:
        text=val_texts
        label=Y_oh_val
    else:
        text=test_texts
        label=Y_oh_test
    # batch_size=1
    # label = tf.ragged.constant(label)
    while True:
        for i in range(0,len(text),batch_size):
            yield padded_tokenizer(text[i:i+batch_size]), label[i:i+batch_size]

train_dataset = tf.data.Dataset.from_generator(_generator, output_signature=({'input_ids':tf.TensorSpec(shape=(None,80), dtype=tf.int32),
                                                                          'attention_mask':tf.TensorSpec(shape=(None,80), dtype=tf.int32)},
                                                                          tf.TensorSpec(shape=(None,49), dtype=tf.int32)), 
                                               args=([0,16])
                                               )

Похоже, что сигнатура должна быть указана, если вывод создается из генератора, но скорость слишком низкая по сравнению с загрузкой всего в память (по очевидным причинам)

Все равно буду признателен за более быстрое решение

Ответ или решение

Как написать генератор для дообучения моделей на основе трансформеров (TensorFlow)

При работе с большими объемами текстовых данных часто возникает необходимость в генераторах для подачи данных в модель в момент обучения. Это позволяет избежать полного загрузки данных в память, что особенно актуально при обрабатывании объемных наборов данных. В данной статье мы рассмотрим, как можно создать эффективный генератор для дообучения модели DistilBERT с использованием TensorFlow.

Основные компоненты генератора

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

  1. Токенизация: Преобразование текстов в формат, подходящий для модели (например, токены и маски внимания).
  2. Обработка данных: Создание батчей данных для подачи в модель.
  3. Указание сигнатуры данных: Определение структуры выходных данных для оптимизации обработки.

Реализация генератора

Первоначально, мы подготовим генератор следующим образом:

import tensorflow as tf
from transformers import DistilBertTokenizer

tokenizer = DistilBertTokenizer.from_pretrained('distilbert-base-uncased')

def padded_tokenizer(text, max_len=80):
    result = tokenizer(text, truncation=True, padding='max_length', max_length=max_len, return_tensors="tf")
    return result

def _generator(texts, labels, batch_size=1):
    while True:
        for i in range(0, len(texts), batch_size):
            yield padded_tokenizer(texts[i:i+batch_size]), labels[i:i+batch_size]

# Пример использования
train_texts = [
    'This gif kills me Death is literally gushing towards you.',
    'LOVE TEST Raw Real JaDine',
    'Happy New Year and all the best in 2018'
]

# Предположим, Y_oh_train является закодированными метками для классов.
Y_oh_train = ...

train_dataset = tf.data.Dataset.from_generator(
    lambda: _generator(train_texts, Y_oh_train, batch_size=16),
    output_signature=(
        {
            'input_ids': tf.TensorSpec(shape=(None, 80), dtype=tf.int32),
            'attention_mask': tf.TensorSpec(shape=(None, 80), dtype=tf.int32)
        },
        tf.TensorSpec(shape=(None, Y_oh_train.shape[1]), dtype=tf.float32)
    )
)

Почему требуется сигнатура данных?

При использовании генераторов в TensorFlow необходимо указывать сигнатуры выходных данных (output_signature). Это критично для поддержки эффективной компиляции графов. Ваша модель сможет быстрее обрабатывать данные, если заранее знает, какую форму они будут иметь.

Оптимизация производительности

При работе с генераторами можно столкнуться с проблемой производительности, особенно если генерация данных происходит в реальном времени. Чтобы повысить скорость, вы можете использовать следующие методы:

  1. Параллельное выполнение: Используйте методы map() и prefetch() для обработки данных в отдельном потоке.

    train_dataset = train_dataset.map(...)
    train_dataset = train_dataset.prefetch(tf.data.experimental.AUTOTUNE)
  2. Пакетная обработка: Попробуйте увеличить размер батча, если позволяет объём вашей памяти.

  3. Загрузка данных из файла: При возможности, храните данные на диске и загружайте их по частям, что значительно уменьшит время ожидания при каждой итерации.

Заключение

Создание генератора для подачи данных в модели на основе трансформеров, таких как DistilBERT, — это мощный инструмент, особенно когда вы имеете дело с большими объемами данных. В данной статье мы рассмотрели основные методы реализации генератора, а также привыкли оптимизировать его производительность. Тщательно настраивая и тестируя свой генератор, вы сможете значительно повысить качество и скорость обучения вашей модели.

Оцените материал
Добавить комментарий

Капча загружается...