Tensorflow:dataset数据读取

Dataset的几种生成方式

Note: 很多方法构建的Dataset也会有OutOfRange的异常出现,需要恰当地进行捕捉并处理,否则直接报错:OutOfRangeError End of sequence。如果是使用tf.train.MonitoredTrainingSession这种session,会通过while not mon_sess.should_stop()自动捕捉。

Dataset.from_generator

Dataset.from_generator可以使用普通编程语言编写的外部子函数生成Dataset,这样几乎不受tensorflow编程不便的影响。[Tensorflow Dataset.from_generator使用]

读入的seq长度无法确定,所以shapes = (([None], ()), [None])。

如果固定会出错:ValueError: `generator` yielded an element of shape (15,) where an element of shape (200,) was expected.

Dataset.from_tensor_slices

dataset = Dataset.from_tensor_slices((df_features.values, df_labels.values)) [使用 tf.data 加载 pandas dataframes]

from_tensor_slices读取后的数据类型跟随df的数据类型。

shape的信息
<TensorSliceDataset shapes: ((133,), (1,)), types: (tf.float32, tf.float32)>

示例:读取dataframe中的数据

user_features = df.loc[:, cols_dict['user_dense_cols'] + cols_dict['user_sparse_cols']]
goods_features = df.loc[:, cols_dict['goods_dense_cols'] + cols_dict['goods_sparse_cols']]
labels = df.loc[:, cols_dict['label_cols']]
dataset = Dataset.from_tensor_slices((user_features.values, goods_features.values, labels.values))  

tf.decode_csv

tf.io.decode_csv(records,    record_defaults,    field_delim=',',    use_quote_delim=True,    name=None,    na_value='',    select_cols=None)

参数use_quote_delim ,看原始代码的解释为:use_quote_delim: An optional bool. Defaults to True. If false, treats double quotation marks as regular characters inside of the string fields (ignoring RFC 4180, Section 2, Bullet 5). 默认设置为True,会把双引号当成引用,在使用tf.decode_csv读取文件的时候如果某一行有双引号会报错,如果设置为False会把双引号当做为一个普通的字符串变量。

调用出错:InvalidArgumentError: Unquoted fields cannot have quotes/CRLFs inside
     [[Node: DecodeCSV = DecodeCSV[OUT_TYPE=[DT_STRING, DT_INT32], field_delim="\t", na_value="", use_quote_delim=true, 

sentences, labels = tf.decode_csv(values, [[''], [1]], field_delim=field_delim) 。。。

其中CRLF是Carriage-Return Line-Feed的缩写,意思是回车换行,就是回车(CR, ASCII 13, \r) 换行(LF, ASCII 10, \n)。

解决:use_quote_delim=False或者数据中去除"。

[tf.decode_csv() error: “Unquoted fields cannot have quotes/CRLFs inside]

dataset数据操作

dataset.shuffle.repeat.batch三个函数式解释

dataset = dataset.shuffle(self.shuffer_buffer).repeat(self.epochs)

dataset.shuffle作用是将数据进行打乱操作,传入参数为buffer_size,改参数为设置“打乱缓存区大小”,也就是说程序会维持一个buffer_size大小的缓存,每次都会随机在这个缓存区抽取一定数量的数据。buffer_size设定多少参考下:考虑性能的话用 10 * batch_size 。太小的话,就跟顺序取差不多,起不到 shuffle 的作用。太大的话,貌似也没有啥影响。[数据集shuffle方法中buffer_size的理解]
dataset.repeat作用就是将数据重复使用多少epoch  
dataset.batch作用是将数据打包成batch_size

先看结果:

解释:相当于把所有数据先打乱,再把所有数据重复两个epoch,然后将重复两个epoch的数据放在一起,最后打包成batch_size输出

特点:1.因为把数据复制两份,还进行打乱,因此某个batch数据可能会重复,而且出现重复数据的batch只会是两个batch交叉的位置;2.最后一个batch的尺寸小于等于batch_size

不同的顺序也是有区别的:[tensorflow dataset.shuffle、dataset.batch、dataset.repeat顺序区别详解],链接也可以看下dataset迭代输出的格式。

dataset.padded_batch

需要pad齐数据时使用,一般文本处理时。

padded_batch(
    batch_size,
    padded_shapes,
    padding_values=None,
    drop_remainder=False
)

        其中参数drop_remainder: (Optional.) A tf.bool scalar tf.Tensor, representing whether the last batch should be dropped in the case it has fewer than batch_size elements; the default behavior is not to drop the smaller batch. 只有这个参数为True时,输出的batch_size维度显示的才是batch_size而不是?。因为这个参数为False,所有最后一个batch_size可能<参数batch_size,即显示成?。一般不需要看batch_size时,这个就默认为False不要动。debug时还是可以设成True看看的,比如看后面tensor的shape变化。

        函数仍返回dataset。

dataset.prefetch

    tf.data API 通过 tf.data.Dataset.prefetch 转换提供了一种软件流水线机制,该机制可用于将生成数据的时间和使用数据的时间分离开。具体而言,该转换使用后台线程和内部缓冲区,以便在请求元素之前从输入数据集中预取这些元素。因此,为了实现上图所示的流水线效果,您可以将 prefetch(1) 作为最终转换添加到数据集流水线中(如果单步训练使用 n 个元素,则添加 prefetch(n))。

Note: batch().prefetch(1),通过print(dataset)可以看出是以batch为单位取的。Like other Dataset methods, prefetch operates on the elements of the input dataset. It has no concept of examples vs. batches. examples.prefetch(2) will prefetch two elements (2 examples), while examples.batch(20).prefetch(2) will prefetch 2 elements (2 batches, of 20 examples each).[https://www.tensorflow.org/api_docs/python/tf/data/Dataset#prefetch]

一个问题

        输出的shape为(batch_size,?)不能直接输入到dense layer中,否则报错:flatten dense ValueError: The last dimension of the inputs to `Dense` should be defined. Found `None`.

        这是因为经过rnn后直接使用了所有hidden的flatten,而不是最后一个或者平均。即如果要使用经过rnn后直接使用了所有hidden的flatten,上面代码中padded_batch输入的shape中的seq_len需要直接设置成固定值max_seq_len200,而不是None,但是这样可以会截断长seq。要注意的是dnn本身的输入维度需要是固定的,不可能变长。

[tf.data.Dataset]

数据输入主参考[数据输入流水线性能]

take操作

take(count)

Creates a Dataset with at most count elements from this dataset.

按特定值过滤tf.data.Dataset

示例:过滤掉user_feature、goods_feature、label向量中有任何NAN的行。

def filter_nan(line):
    return tf.logical_not(tf.logical_or(
        tf.logical_or(tf.reduce_any(tf.is_nan(line['user_feature'])),
                      tf.reduce_any(tf.is_nan(line['goods_feature']))),
        tf.reduce_any(tf.is_nan(line['label'])))
    )

dataset = dataset.filter(filter_nan)

Note: 

1 这里是通过tf.data.TFRecordDataset读取的数据,其中line['user_feature']都是k维的tensor。

2 过滤全0的尝试改用reduce_all。reduce_any的用法和numpy的any类似[numpy教程:逻辑函数Logic functions]。

3 filter_nan = lambda x, y: not tf.reduce_any(tf.math.is_nan(x)) and not tf.math.is_nan(y) 使用这种方式的应该是executor模式,不需要跑session run,构图一次run一次。

[Tensorflow how to check if a tensor row is only zeroes?]

[Filter NaN values in Tensorflow dataset]

[python - 如何按特定值过滤tf.data.Dataset?]

迭代dataset

Dataset 是数据集,Iterator 是对应的数据集迭代器。

tf.data.Iterator(iterator_resource, initializer, output_types, output_shapes, output_classes)
如果 Dataset 是一个水池的话,那么它其中的数据就好比是水池中的水,Iterator 你可以把它当成是一根水管。在 Tensorflow 的程序代码中,正是通过 Iterator 这根水管,才可以源源不断地从 Dataset 中取出数据。但为了应付多变的环境,水管也需要变化,Iterator 也有许多种类。

单次 Iterator

make_one_shot_iterator()

这个方法会返回一个 Iterator 对象,调用 iterator 的 get_next() 就可以轻松地取出数据了。

示例

import tensorflow as tf

dataset = tf.data.Dataset.range(5)

iterator = dataset.make_one_shot_iterator()
with tf.Session() as sess:

    while True:
        try:
            print(sess.run(iterator.get_next()))
        except tf.errors.OutOfRangeError:
            break

这里写图片描述

可以定制的水管,可初始化的 Iterator

跟单次 Iterator 的代码只有 2 处不同。

1、创建的方式不同,iterator.make_initialnizer()。

2、每次重新初始化的时候,都要调用sess.run(iterator.initializer)

你可以这样理解,Dataset 这个水池连续装了 2 次水,每次水量不一样,但可初始化的 Iterator 很好地处理了这件事情,但需要注意的是,这个时候 Iterator 还是面对同一个 Dataset。

示例

def initialable_test():
    numbers = tf.placeholder(tf.int64,shape=[])
    dataset = tf.data.Dataset.range(numbers)
    # iterator = dataset.make_one_shot_iterator()
    iterator = dataset.make_initializable_iterator()

    with tf.Session() as sess:

        sess.run(iterator.initializer,feed_dict={numbers:5})
        while True:
            try:
                print(sess.run(iterator.get_next()))
            except tf.errors.OutOfRangeError:
                break

        sess.run(iterator.initializer,feed_dict={numbers:6})
        while True:
            try:
                print(sess.run(iterator.get_next()))
            except tf.errors.OutOfRangeError:
                break

make_initializable_iterator和make_one_shot_iterator的区别:假设您想使用相同的代码来进行培训和验证。您可能希望使用相同的迭代器,但已初始化为指向不同的数据集。使用一次性迭代器,您不能像这样重新初始化它。
[在make_initializable_iterator和make_one_shot_iterator之间的tensorflow数据集API差异]

能够接不同水池的水管,可重新初始化的 Iterator

def reinitialable_iterator_test():
    training_data = tf.data.Dataset.range(10)
    validation_data = tf.data.Dataset.range(5)

    iterator = tf.data.Iterator.from_structure(training_data.output_types,
                                               training_data.output_shapes)

    train_op = iterator.make_initializer(training_data)
    validation_op = iterator.make_initializer(validation_data)

    next_element = iterator.get_next()

    with tf.Session() as sess:

        for _ in range(3):
            sess.run(train_op)
            for _ in range(3):
                print(sess.run(next_element))


            print('===========')

            sess.run(validation_op)
            for _ in range(2):
                print(sess.run(next_element))

            print('===========')
这里写图片描述

其它更复杂的iterator参考[【Tensorflow】Dataset 中的 Iterator]

[TensorFlow数据读取方式:Dataset API,以及如何查看dataset:DatasetV1Adapter的方法]

查看dataset信息

假设读取
dataset = Dataset.from_tensor_slices((df_features.values, df_labels.values)) 
则直接print(dataset)显示含shape的信息
<TensorSliceDataset shapes: ((133,), (1,)), types: (tf.float32, tf.float32)>
dataset = dataset.shuffle(self.shuffer_buffer).repeat(self.epochs)
<RepeatDataset shapes: ((133,), (1,)), types: (tf.float32, tf.float32)>
dataset = dataset.batch(self.batch_size).prefetch(1)
<PrefetchDataset shapes: ((?, 133), (?, 1)), types: (tf.float32, tf.float32)>

dataset = dataset.batch(self.batch_size, drop_remainder=args.drop_remainder).prefetch(1)
<PrefetchDataset shapes: ((32, 133), (32, 1)), types: (tf.float32, tf.float32)>

Dataset获取数据集大小:未尝试

[tf.data.Dataset:如何获取数据集大小(一个元素的元素数量)?]

示例

示例1:文本数据读取和处理

DEBUG = True

mlb = MultiLabelBinarizer()
with open(os.path.join(DATADIR, 'vocab.tags.txt'), 'r', encoding='utf-8') as f:
    mlb.fit([[l.strip() for l in f.readlines()]])
    print('{0}\nmlb.classes_: \n{1}\n{0}'.format('*' * 50, mlb.classes_))


def parse_fn(line_words, line_labels):
    # Encode in Bytes for TF
    words = [w.encode('utf-8') for w in line_words.strip().split()]  # 不转换也会在model_fn前在某个地方自动完成
    # labels = [t.encode('utf-8') for t in line_labels.strip().split()]
    labels = [t for t in line_labels.strip().split()]
    labels = mlb.transform([labels])[0]
    # print((words, len(words)), labels)
    return (words, len(words)), labels


def generator_fn(words, labels):
    with open(words, 'r', encoding='utf-8') as f_words, open(labels, 'r', encoding='utf-8') as f_labels:
        for line_words, line_labels in zip(f_words, f_labels):
            result = parse_fn(line_words, line_labels)
            if result:
                yield result
            else:
                continue


def input_fn(words, labels, params=None, shuffle_and_repeat=False):
    params = params if params is not None else {}
    if DEBUG:
        shapes = (([None], ()), [None])
        shapes2 = (([200], ()), [11])
    else:
        shapes = shapes2 = (([None], ()), [None])
    types = ((tf.string, tf.int32), tf.float32)
    defaults = (('<pad>', 0), 0.0)  # (words, len(words)), MultiLabelBinary

    dataset = tf.data.Dataset.from_generator(
        functools.partial(generator_fn, words, labels),
        output_shapes=shapes, output_types=types)

    if shuffle_and_repeat:  # only for training
        dataset = dataset.shuffle(params['buffer']).repeat(params['epochs'])

    if DEBUG:
        dataset = dataset.padded_batch(params.get('batch_size', 20), shapes2, defaults, drop_remainder=True).prefetch(1)
    else:
        dataset = dataset.padded_batch(params.get('batch_size', 20), shapes2, defaults).prefetch(1)
    # print(dataset)
    return dataset

示例2:

raw_dataset = tf.data.TFRecordDataset(self.input_file_names,buffer_size=args.tfrecord_bufsize)
print("raw_dataset shape:{}\n".format(raw_dataset))
dataset = raw_dataset.map(functools.partial(_parse_function, is_test=args.is_test))

if self.shuffle_and_repeat:  # only for training
    dataset = dataset.shuffle(self.shuffer_buffer).repeat(self.num_epochs)

dataset = dataset.batch(self.batch_size, drop_remainder=args.drop_remainder).prefetch(1)

self.dataset_iterator = dataset.make_initializable_iterator()
input = self.dataset_iterator.get_next()

self.features = (input['user_feature'], input['goods_feature'])
self.label = input['label']

from: -柚子皮-

ref:

CAN长字节DM1报文是指在CAN总线上传输的长度超过8个字节的DM1报文。根据引用\[1\],当要传输的数据长度超过8个字节时,首先使用TPCM进行广播,广播内容包含即将传输报文的PGN、总的数据包长度等信息,然后使用TP.DT进行数据传输。相邻两个TP.DT之间的时间间隔是50ms到200ms。根据引用\[2\],当字节数大于8时,将会使用多帧传输参数组。根据引用\[3\],DM1报文是Diagnostic Message 1, Active Diagnostic Trouble Codes的缩写,用于点亮故障指示灯、红色停机灯等,并周期性播报控制器中处于激活状态的故障码。DM1报文的格式包括各个字节的定义,如故障指示灯、红色停机灯、琥珀色警告指示灯等。因此,CAN长字节DM1报文是指在CAN总线上传输的长度超过8个字节的DM1报文,用于传输更多的故障码信息。 #### 引用[.reference_title] - *1* [车载通信——J1939 DM1](https://blog.csdn.net/weixin_64064747/article/details/130193432)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [J1939广播DM1报文](https://blog.csdn.net/mengdeguodu_/article/details/108173263)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [J1939商用车在线诊断DM1报文](https://blog.csdn.net/traveller93/article/details/120735912)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值