Прежде всего, давайте выясним проблемы в вашем коде:
Первая проблема, которая также является причиной данной ошибки, заключается в том, что метод fit_on_texts
принимает список текстов, а не одна текстовая строка. Следовательно, оно должно быть: tok.fit_on_texts([inputs])
.
После исправления и повторного запуска кода вы получите еще одну ошибку: AttributeError: 'Tensor' object has no attribute 'lower'
. Это связано с тем, что элементы в наборе данных являются объектами Tensor, и функция map должна иметь возможность обрабатывать их; однако класс Tokenizer
не предназначен для работы с объектами Tensor (есть решение этой проблемы, но я не буду сейчас его решать из-за следующей проблемы).
Самая большая проблема заключается в том, что каждый раз, когда вызывается функция карты, то есть preprocess
, создается новый экземпляр класса Tokenizer
, который будет помещаться в одном текстовом документе. Однако, как следует из названия метода fit_on_texts
, он предназначен для применения его к всем текстовым документам только один раз . Другими словами, применять его только к одному текстовому документу не имеет смысла, просто потому, что вы не строите словарь, основанный только на одном примере (однако, если был метод частичное соответствие , то, вероятно, он может быть сделано таким образом)! Следовательно, вы не можете использовать tf.keras.preprocessing.text.Tokenizer
класс здесь, то есть он не применим в этом специфицированном c конвейере данных.
Итак, что нам делать? Как упомянуто выше, почти во всех моделях, которые имеют дело с текстовыми данными, мы сначала должны преобразовать тексты в числовые элементы, то есть кодировать их. Для выполнения кодирования сначала нам понадобится словарный набор или словарь токенов. Таким образом, мы должны предпринять следующие шаги:
Если имеется готовый словарь, перейдите к следующему шагу. В противном случае сначала разбейте все текстовые данные и создайте словарь.
Кодируйте текстовые данные с использованием набора слов.
Для выполнения первого шага мы используем tfds.features.text.Tokenizer
для токенизации текстовых данных и построения словаря путем итерации по набору данных.
Для второго шага мы используем tfds.features.text.TokenTextEncoder
для кодирования текстовых данных с использованием словарного набора, созданного в предыдущем шаге. Обратите внимание, что для этого шага мы используем метод map
; однако, поскольку map
работает только в графическом режиме, мы завернули нашу encode
функцию в tf.py_function
, чтобы ее можно было использовать с map
.
Вот код (пожалуйста, прочтите комментарии в коде для дополнительных пунктов; я не включил их в ответ, потому что они не имеют прямого отношения, но они полезны и практичны):
import tensorflow as tf
import tensorflow_datasets as tfds
from collections import Counter
fname = "rotten_tomatoes_reviews.csv"
dataset = tf.data.experimental.CsvDataset(filenames=fname,
record_defaults=[tf.int32, tf.string],
header=True)
# Create a tokenizer instance to tokenize text data.
tokenizer = tfds.features.text.Tokenizer()
# Find unique tokens in the dataset.
lowercase = True # set this to `False` if case-sensitivity is important.
vocabulary = Counter()
for _, text in dataset:
if lowercase:
text = tf.strings.lower(text)
tokens = tokenizer.tokenize(text.numpy())
vocabulary.update(tokens)
# Select the most common tokens as final vocabulary set.
# Note: if you want all the tokens to be included,
# set `vocab_size = len(vocabulary)` instead.
vocab_size = 5000
vocabulary, _ = zip(*vocabulary.most_common(vocab_size))
# Create an encoder instance given our vocabulary set.
encoder = tfds.features.text.TokenTextEncoder(vocabulary,
lowercase=lowercase,
tokenizer=tokenizer)
# Set this to a non-zero integer if you want the texts
# to be truncated when they have more than `max_len` tokens.
max_len = None
def encode(target, text):
text_encoded = encoder.encode(text.numpy())
if max_len:
text_encoded = text_encoded[:max_len]
return text_encoded, target
# Wrap `encode` function inside `tf.py_function` so that
# it could be used with `map` method.
def encode_pyfn(target, text):
text_encoded, target = tf.py_function(encode,
inp=[target, text],
Tout=(tf.int32, tf.int32))
# (optional) Set the shapes for efficiency.
text_encoded.set_shape([None])
target.set_shape([])
return text_encoded, target
# Apply encoding and then padding.
# Note: if you want the sequences in all the batches
# to have the same length, set `padded_shapes` argument accordingly.
dataset = dataset.map(encode_pyfn).padded_batch(batch_size=3,
padded_shapes=([None,], []))
# Important Note: probably this dataset would be used as input to a model
# which uses an Embedding layer. Therefore, don't forget that you
# should set the vocabulary size for this layer properly, i.e. the
# current value of `vocab_size` does not include the padding (added
# by `padded_batch` method) and also the OOV token (added by encoder).
Примечание для будущих читателей: обратите внимание, что порядок аргументов, т.е. target, text
, и типы данных основаны на наборе данных OP. Адаптируйте по мере необходимости на основе вашего собственного набора данных / задачи (хотя, в конце, например, return text_encoded, target
, мы изменили это, чтобы сделать его совместимым с ожидаемым форматом fit
метода).