tensorflow 中dataset.padded_batch函数的个人理解过程

2021-01-04 10:10:26 浏览数 (1)

参考链接: Python中的numpy.empty_like

今天继续啃Tensorflow实战Google深度学习框架这本书,在250P的Seq2Seq模型代码理解时候有点困难,其中padded_batch(batch_size,padded_shapes)这个函数为最,本次仅为记录刨根问底的过程,也是整理一下类似函数的理解过程。 

1直接查看英文解释,并且配合W3school的中文解释,锻炼英文阅读理解能力,尤其是专业的英文单词。 

直接在pycharm上查看代码自带的英文注释 

"""Combines consecutive elements of this dataset into padded batches.

Like `Dataset.dense_to_sparse_batch()`, this method combines

multiple consecutive elements of this dataset, which might have

different shapes, into a single element. The tensors in the

resulting element have an additional outer dimension, and are

padded to the respective shape in `padded_shapes`.

Args:

  batch_size: A `tf.int64` scalar `tf.Tensor`, representing the number of

    consecutive elements of this dataset to combine in a single batch.

  padded_shapes: A nested structure of `tf.TensorShape` or

    `tf.int64` vector tensor-like objects representing the shape

    to which the respective component of each input element should

    be padded prior to batching. Any unknown dimensions

    (e.g. `tf.Dimension(None)` in a `tf.TensorShape` or `-1` in a

    tensor-like object) will be padded to the maximum size of that

    dimension in each batch.

  padding_values: (Optional.) A nested structure of scalar-shaped

    `tf.Tensor`, representing the padding values to use for the

    respective components.  Defaults are `0` for numeric types and

    the empty string for string types.

Returns:

  A `Dataset`.

""" 

结合W3school的中文解释,https://www.w3cschool.cn/tensorflow_python/tensorflow_python-pqdr2cqn.html 

将此数据集的连续元素合并为填充的批处理. 

像 Dataset.dense_to_sparse_batch() 一样, 此方法将此数据集的多个连续元素 (可能具有不同的形状) 合并到单个元素中.结果元素中的张量有一个额外的外部维度, 并填充到 padded_shapes 中的相应形状. 

ARGS: 

batch_size:一个 tf.int64 标量 tf.Tensor,表示此数据集的连续元素在单个批处理中合并的数量.padded_shapes:tf.TensorShape 的嵌套结构或 tf. int64 向量张量样对象,表示每个输入元素的各自组件在批处理之前应填充的形状.任何未知的维度 (例如 tf.Dimension(None) 在一个 TensorShape 或-1 在一个类似张量的对象中) 将被填充到每个批次中该维度的最大维度.

padding_values:(可选)一个标量形状的嵌套结构 tf.Tensor,表示要用于各个组件的填充值.对于数字类型和字符串类型的空字符串,默认值为 0.

返回: 

一个数据集 

具体应用实例,我参考了这位博主的博文https://blog.csdn.net/z2539329562/article/details/89791783,经过删减并添加了自己的注释。 

第一个实例: 

import tensorflow as tf

import numpy as np

tf.reset_default_graph()

x = [[1, 0, 0],

     [2, 3, 0],

     [4, 5, 6],

     [7, 8, 0],

     [9, 0, 0],

     [0, 1, 0]]

# tf.TensorShape([])     表示长度为单个数字

# tf.TensorShape([None]) 表示长度未知的向量

padded_shapes = (

    tf.TensorShape([None])

)

dataset = tf.data.Dataset.from_tensor_slices(x)

iterator_before = dataset.make_one_shot_iterator()

dataset_padded = dataset.padded_batch(2, padded_shapes=padded_shapes)

#dataset_padded = dataset.padded_batch(2, padded_shapes=[None]) 也是可以的,原因看下面注释1

iterator_later = dataset_padded.make_one_shot_iterator()#iterator_later是经过padded_batch处理的数据集迭代器

sess = tf.Session()

try:

    while True:

        print(sess.run(iterator_before.get_next()))

except tf.errors.OutOfRangeError:

    print("end")

try:

    while True:

        print(sess.run(iterator_later.get_next()))

except tf.errors.OutOfRangeError:

    print("end") 

 注释1:参考的网址:http://www.tensorfly.cn/tfdoc/resources/dims_types.html 

运行结果 

[1 0 0] [2 3 0] [4 5 6] [7 8 0] [9 0 0] [0 1 0] end [[1 0 0]  [2 3 0]] [[4 5 6]  [7 8 0]] [[9 0 0]  [0 1 0]] end 

数据集dataset 保存着原来二维的数组X,dataset 中的每一个元素是一个1*3的数组,也就是X的每一个行,iterator_before 只是顺序输出dataset的每一个元素。而dataset_padded中的三个元素分别是 

[[1 0 0]  [2 3 0]] 

[[4 5 6]  [7 8 0]] 

[[9 0 0]  [0 1 0]] 

可见padded_batch(2, padded_shapes=padded_shapes)将原来dataset的元素[1 0 0] 和元素[2 3 0]   从新组合成为了一个新的元素 

[[1 0 0]  [2 3 0]]    因为batch_size是2,所以是使用原来dataset的两个元素组合成一个新的元素(一个列表) 

那padded_shapes什么意思那?别急,我们看下个例子。 

第二个实例: 

import tensorflow as tf

import numpy as np

# tf.TensorShape([])     表示长度为单个数字

# tf.TensorShape([None]) 表示长度未知的向量

padded_shapes = (

    tf.TensorShape([None]),

    #tf.TensorShape([])

)

dataset = tf.data.Dataset.range(10)#生成一个数据集里面的元素分别是 0 1 2 3 4 5 6 7 8  9

dataset = dataset.map(lambda x: tf.fill([tf.cast(x, tf.int32)], x))

iterator = dataset.make_one_shot_iterator()

sess = tf.Session()

try:

    i = 0

    while True:

        print("elem",i,":", sess.run(iterator.get_next()))

        i =1

except tf.errors.OutOfRangeError:

    print("end")

输出是: 

elem 0 : [] elem 1 : [1] elem 2 : [2 2] elem 3 : [3 3 3] elem 4 : [4 4 4 4] elem 5 : [5 5 5 5 5] elem 6 : [6 6 6 6 6 6] elem 7 : [7 7 7 7 7 7 7] elem 8 : [8 8 8 8 8 8 8 8] elem 9 : [9 9 9 9 9 9 9 9 9] end 

这里也写一下注释:为什么会生成这样的dataset,猜也能猜的到是这一句代码: 

dataset = dataset.map(lambda x: tf.fill([tf.cast(x, tf.int32)], x)) 

这行代码的作用,就是对dataset的每一个元素,比如0或者1或者2,进行一个运算,这个运算就是tf.fill([tf.cast(x, tf.int32)], x),而tf.fill的作用,我们直接看pycharm 源代码的英文注释就能看懂, 

r"""Creates a tensor filled with a scalar value.

This operation creates a tensor of shape `dims` and fills it with `value`.

For example:

```

# Output tensor has shape [2, 3].

fill([2, 3], 9) ==> [[9, 9, 9]

                     [9, 9, 9]]

``` 

也就是0 这个元素替换为了fill([0], 0)   刚好就是第一个输出[]  。1这个元素替换为了fill([1], 1) ,也就是第二个输出[1] 

好了,回归正题,在第二个实例中,这里我们就得到了一个dataset里面的元素是看成长度一样的list,正好我们用这个数据集试试填充,之前关于padded_bach函数中第二个参数padded_shapes参数的说明 “任何未知的维度 (例如 tf.Dimension(None) 在一个 TensorShape 或-1 在一个类似张量的对象中) 将被填充到每个批次中该维度的最大维度.” 

import tensorflow as tf

import numpy as np

# tf.TensorShape([])     表示长度为单个数字

# tf.TensorShape([None]) 表示长度未知的向量

padded_shapes = (

    tf.TensorShape([None])

    #tf.TensorShape([])

)

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

dataset = dataset.map(lambda x: tf.fill([tf.cast(x, tf.int32)], x))

dataset = dataset.padded_batch(2, padded_shapes=padded_shapes)# 多了这一行代码

iterator = dataset.make_one_shot_iterator()

sess = tf.Session()

try:

    i=1

    while True:

        print("elem",i,":", sess.run(iterator.get_next()))

        i =1;

except tf.errors.OutOfRangeError:

    print("end") 

输出如下: 

elem 1 : [[0]               [1]] elem 2 : [[2 2 0]               [3 3 3]] elem 3 : [[4 4 4 4 0]               [5 5 5 5 5]] elem 4 : [[6 6 6 6 6 6 0]               [7 7 7 7 7 7 7]] elem 5 : [[8 8 8 8 8 8 8 8 0]               [9 9 9 9 9 9 9 9 9]] end 

可以看到,原来的两个元素 [] [1] 合并成一个元素了,并且[]这个元素填充了一个0,以确保和[1]这个元素一样长。 

下面再看一个例子,如何使用 

padded_shapes = (

    tf.TensorShape([None]),#表示长读未知的向量

    tf.TensorShape([])#表示为单个数字

第三个实例: 

import tensorflow as tf

import numpy as np

# tf.TensorShape([])     表示长度为单个数字

# tf.TensorShape([None]) 表示长度未知的向量

padded_shapes = (

    tf.TensorShape([None]),

    tf.TensorShape([])

)

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

dataset = dataset.map(lambda x: tf.fill([tf.cast(x, tf.int32)], x))

dataset = dataset.map(lambda x: (x, tf.reduce_sum(x)))

iterator = dataset.make_one_shot_iterator()

sess = tf.Session()

try:

    i=1

    while True:

        print("elem",i,":", sess.run(iterator.get_next()))

        i =1;

except tf.errors.OutOfRangeError:

    print("end") 

输出为: 

elem 1 : (array([], dtype=int64), 0) elem 2 : (array([1], dtype=int64), 1) elem 3 : (array([2, 2], dtype=int64), 4) elem 4 : (array([3, 3, 3], dtype=int64), 9) elem 5 : (array([4, 4, 4, 4], dtype=int64), 16) elem 6 : (array([5, 5, 5, 5, 5], dtype=int64), 25) elem 7 : (array([6, 6, 6, 6, 6, 6], dtype=int64), 36) elem 8 : (array([7, 7, 7, 7, 7, 7, 7], dtype=int64), 49) elem 9 : (array([8, 8, 8, 8, 8, 8, 8, 8], dtype=int64), 64) elem 10 : (array([9, 9, 9, 9, 9, 9, 9, 9, 9], dtype=int64), 81) end 

当代码添加  加粗的哪一行代码后 

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

dataset = dataset.map(lambda x: tf.fill([tf.cast(x, tf.int32)], x))

dataset = dataset.map(lambda x: (x, tf.reduce_sum(x)))

dataset = dataset.padded_batch(2, padded_shapes=padded_shapes)#我就是加粗的那一行 

iterator = dataset.make_one_shot_iterator() 

输出 

elem 1 : (array([[0],        [1]], dtype=int64), array([0, 1], dtype=int64)) elem 2 : (array([[2, 2, 0],        [3, 3, 3]], dtype=int64), array([4, 9], dtype=int64)) elem 3 : (array([[4, 4, 4, 4, 0],        [5, 5, 5, 5, 5]], dtype=int64), array([16, 25], dtype=int64)) elem 4 : (array([[6, 6, 6, 6, 6, 6, 0],        [7, 7, 7, 7, 7, 7, 7]], dtype=int64), array([36, 49], dtype=int64)) elem 5 : (array([[8, 8, 8, 8, 8, 8, 8, 8, 0],        [9, 9, 9, 9, 9, 9, 9, 9, 9]], dtype=int64), array([64, 81], dtype=int64)) end 

dataset的每一个元素是 类似包含两个元素的元组  (array([[0],[1]], dtype=int64), array([0, 1], dtype=int64)),array([[0],[1]]是两个元素[] [1]经过填充的再合并成一个array数组,而 array([0, 1]并不需要填充,两个元素 0 1直接合并成一个array数组。 

大概总结就是这样,应该还是有不少的纰漏,毕竟才疏学浅,基础也不太好,有些东西不知道怎么形容才好。。。。

0 人点赞