Python 如何在map方法中预处理TensorFlow CsvDataset并将其标记化?

Python 如何在map方法中预处理TensorFlow CsvDataset并将其标记化?,python,tensorflow,keras,nlp,tensorflow-datasets,Python,Tensorflow,Keras,Nlp,Tensorflow Datasets,我制作了一个TensorFlowCsvDataset,并尝试将数据标记为: import os os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' from tensorflow import keras import tensorflow as tf from tensorflow.keras.preprocessing.text import Tokenizer import os os.chdir('/home/nicolas/Documents/Datase

我制作了一个TensorFlow
CsvDataset
,并尝试将数据标记为:

import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3'
from tensorflow import keras
import tensorflow as tf
from tensorflow.keras.preprocessing.text import Tokenizer
import os
os.chdir('/home/nicolas/Documents/Datasets')

fname = 'rotten_tomatoes_reviews.csv'


def preprocess(target, inputs):
    tok = Tokenizer(num_words=5_000, lower=True)
    tok.fit_on_texts(inputs)
    vectors = tok.texts_to_sequences(inputs)
    return vectors, target


dataset = tf.data.experimental.CsvDataset(filenames=fname,
                                          record_defaults=[tf.int32, tf.string],
                                          header=True).map(preprocess)
运行此命令会出现以下错误:

ValueError:len需要一个非标量张量,得到一个形状张量(“形状:0”,形状=(0,),数据类型=int32)

我所尝试的:几乎所有可能的事情。请注意,如果删除预处理步骤,所有操作都会运行

数据是什么样子的:

(<tf.Tensor: shape=(), dtype=int32, numpy=1>,
 <tf.Tensor: shape=(), dtype=string, numpy=b" Some movie critic review...">)
(,
)

首先,让我们找出代码中的问题:

  • 第一个问题,也是给定错误背后的原因,是
    fit_on_text
    方法接受文本列表,而不是单个文本字符串。因此,它应该是:
    tok.fit\u on\u text([inputs])

  • 修复该问题并再次运行代码后,您会遇到另一个错误:
    AttributeError:'Tensor'对象没有属性'lower'
    。这是因为数据集中的元素是张量对象,map函数应该能够处理它们;但是,
    Tokenizer
    类不是设计用来处理张量对象的(这个问题有一个解决方案,但由于下一个问题,我现在不讨论它)

  • 最大的问题是,每次调用map函数,即
    预处理
    ,都会创建一个
    标记器
    类的新实例,它将适合单个文本文档Update:正如评论部分正确指出的那样,
    fit_on_text
    方法实际上执行部分拟合(即更新或扩充内部词汇表统计,而不是从头开始)。因此,如果我们在
    预处理
    函数之外创建
    标记器
    类,并且假设词汇集是事先已知的(否则,除非您拥有或首先构建词汇集,否则无法在部分匹配方案中过滤最频繁的单词),那么就可以使用这种方法(即基于
    Tokenizer
    class)在应用了上述修复之后。但是,就个人而言,我更喜欢下面的解决方案


那么,我们应该怎么做呢?如上所述,在几乎所有处理文本数据的模型中,我们首先需要将文本转换为数字特征,即对其进行编码。为了进行编码,我们首先需要一个词汇集或一个标记字典。因此,我们应该采取以下步骤:

  • 如果有预构建的词汇表可用,则跳到下一步。否则,首先标记all文本数据并构建词汇表

  • 使用词汇表集对文本数据进行编码

  • 为了执行第一步,我们使用标记化文本数据,并通过迭代数据集来构建词汇表

    对于第二步,我们使用上一步中构建的词汇集对文本数据进行编码。请注意,对于这一步,我们使用
    map
    方法;但是,由于
    map
    仅在图形模式下使用函数,我们将
    encode
    函数包装在
    tf.py_函数中,以便它可以与
    map一起使用

    以下是代码(请阅读代码中的注释以了解更多要点;我没有将它们包括在答案中,因为它们与问题没有直接关系,但它们是有用和实用的):


    未来读者的旁注:请注意,参数顺序,即
    目标、文本和数据类型基于OP的数据集。根据需要根据您自己的数据集/任务进行调整(尽管在最后,即
    返回文本编码,目标
    ,我们调整了它,使其与
    拟合
    方法的预期格式兼容)。

    下面的答案是否解决了您的问题?如果是,请单击答案旁边的复选标记,将您的问题标记为“已回答”;否则,请提供一些关于答案的反馈,以便我们能够更好地帮助您。该项目目前处于搁置状态。当项目恢复时,我肯定会回复您,因为这个问题仍然悬而未决。既然您要求提供反馈,我会说您的答案太复杂了。感谢您的反馈。据我所知和理解,yo你不能在事先不知道标记索引映射的情况下对某些文本进行整数编码。要构造该映射,首先需要构造词汇表/字典。而且,在我写这篇文章时,TF中没有内置的标记器,它将与
    TF.data.Dataset
    兼容,并且能够在一行代码中完成所有这一切。t这就是我所知道的(供你参考,正如我现在检查的,数据集也使用相同的方法)。这实际上是一个非常好的答案。欢迎你随时回答我的问题。祝贺你的所有观点。谢谢@today,顺便说一句,如果
    dataset=.batch(batch\u size)呢
    .map
    之前,在这种情况下,我们将有一个对象数组。我应该如何修改您的答案,我尝试使用lambda,但没有多大成功。@HARSHNILESHPATHAK实际上,在调用
    map
    之前进行批处理对我来说没有意义。我们首先处理/转换数据以使其就绪,然后使用批处理作为基础最后一步。但如果由于某种原因,代码中出现了其他情况,那么正如您所提到的,
    map
    函数的输入将是一个样本数组;因此您需要修改
    map
    函数以迭代该数组并对其进行处理。@today-关于如何在具有多个功能的数据集上使用此技术的任何想法re columns?如果你知道一个好的解决方案,你会喜欢这个问题的一些输入-答案中的一个主要错误信息是这样的陈述:“然而,正如
    fit_on_texts
    方法的名称所示,这是专为只在所有文本文档上应用一次而设计的。”这是错误的。它可以很容易地用
    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).