Вопрос или проблема
Я пытался написать генератор для модели 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.
Основные компоненты генератора
Для начала, давайте определим основные компоненты, которые нам понадобятся для написания генератора:
- Токенизация: Преобразование текстов в формат, подходящий для модели (например, токены и маски внимания).
- Обработка данных: Создание батчей данных для подачи в модель.
- Указание сигнатуры данных: Определение структуры выходных данных для оптимизации обработки.
Реализация генератора
Первоначально, мы подготовим генератор следующим образом:
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
). Это критично для поддержки эффективной компиляции графов. Ваша модель сможет быстрее обрабатывать данные, если заранее знает, какую форму они будут иметь.
Оптимизация производительности
При работе с генераторами можно столкнуться с проблемой производительности, особенно если генерация данных происходит в реальном времени. Чтобы повысить скорость, вы можете использовать следующие методы:
-
Параллельное выполнение: Используйте методы
map()
иprefetch()
для обработки данных в отдельном потоке.train_dataset = train_dataset.map(...) train_dataset = train_dataset.prefetch(tf.data.experimental.AUTOTUNE)
-
Пакетная обработка: Попробуйте увеличить размер батча, если позволяет объём вашей памяти.
-
Загрузка данных из файла: При возможности, храните данные на диске и загружайте их по частям, что значительно уменьшит время ожидания при каждой итерации.
Заключение
Создание генератора для подачи данных в модели на основе трансформеров, таких как DistilBERT, — это мощный инструмент, особенно когда вы имеете дело с большими объемами данных. В данной статье мы рассмотрели основные методы реализации генератора, а также привыкли оптимизировать его производительность. Тщательно настраивая и тестируя свой генератор, вы сможете значительно повысить качество и скорость обучения вашей модели.