参考链接: 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数组。
大概总结就是这样,应该还是有不少的纰漏,毕竟才疏学浅,基础也不太好,有些东西不知道怎么形容才好。。。。