Placeholders
目前,我们已经常使用 Variables 去管理我们的数据,但是还有一个更基本的数据结构(Placeholder 占位符)需要我们去学习。占位符其实就是一个变量,该变量比较特殊,一般都在最后运行时才给他赋值。它允许我们创建操作并构建计算图,而不需要数据。在tensorflow中我们会通过这些占位符(placeholder)去把数据输入到之前构建的计算图中。
import tensorflow as tf
x = tf.placeholder("float", [3])
# 一个变量 x (占位符), 并且数据是 float 类型的,长度为 3
y = x * 2
with tf.Session() as session:
result = session.run(y, feed_dict={x: [1, 2, 3]})
# feed_dict={x: [1, 2, 3]} 通过这种形式去给该占位符号赋予真正有意义的实数值。
print(result)
# result:
# [2, 4, 6]
这个代码的运行方式和我们之间所见到的不太一样,让我们深入探索下。
首先,我们导入 tensorflow,然后我们创建一个占位符 x ,即在内存中划分一个区域,用于之后的数据存储。
然后,我们创建一个张量用于实现 x * 2 的运算,注意:目前我们还没有给 x 赋任何值。
我们现在已经定义了一个操作 y ,现在可以在一个会话中运行它。我们创建一个 session 对象,然后运行 y 。注意:这意味着,我们定义了一个很大的操作图,但我们可以只运行该图的一小部分,这个子图的运行实际是Tensorflow的一个很大的优点,并且这个在其它做类似事情的库中是不存在的。
运行 y 需要知道 x 的具体值。我们在 feed_dict 参数中定义这些值。我们在 feed_dict 参数中声明的 x 值为 [ 1, 2, 3 ] 。运行 y 之后我们得到 [ 2, 4, 6 ] 的结果。
占位符不需要静态的大小,让我们重新编程允许 x 任意长度,改变之后 x 定义如下 :
x = tf.placeholder("float", None)
现在,当我们在 feed_dict 中定义 x 的值时,我们可以用任意个数。这个修改之后的代码可以运行并且结果和上面的一样,都是 [2, 4, 6] ,但是现在可以在 feed_dict 中给 x 赋任意个数(例如下面的例子)。
import tensorflow as tf
x = tf.placeholder("float", None)
y = x * 2
with tf.Session() as session:
result = session.run(y, feed_dict={x: [1, 2, 3, 4, 5, 5, 7, 8, 9]})
print(result)
# result :
# [ 2. 4. 6. 8. 10. 10. 14. 16. 18.]
占位符也可以有多维,允许存储数组。在下面的里例子中我们创建一个 3 × 2 的矩阵,并且在该矩阵中存储一些数。然后我们使用和上面的例子相同的操作去把 x 中的元素变为原来大小的 2 倍。
import tensorflow as tf
x = tf.placeholder("float", [None, 3]) # 这里的None说明第一维不确定,可以是任意维度
y = x * 2
with tf.Session() as session:
x_data = [[1, 2, 3],
[4, 5, 6],]
x_data_two = [[1, 2, 3],
[4, 5, 6],
[7, 8, 9]]
result = session.run(y, feed_dict={x: x_data}) # 2 * 3
result_two = session.run(y, feed_dict={x: x_data_two}) # 3 * 3
print(result,"\n\n", result_two)
# result:
# [[ 2. 4. 6.]
# [ 8. 10. 12.]]
#
# [[ 2. 4. 6.]
# [ 8. 10. 12.]
# [14. 16. 18.]]
占位符的第一维度是 None ,说明我们能有任意行,第二维度是固定值 3 ,说明每一行需要有三列的数据。
我们可以扩展任意多个 None 维度,在这里例子中,我们加载一个图片,然后创建一个占位符去存储我们图片的部分数据。这部分数据是 2 维的片段,但是每一个像素点都有三个通道(R红,G绿,B蓝)。因此,我们需要对于前两维设置 None, 但是第三维我们设置为 3 ( 或者设置为 None 也没问题)。然后,我们用tensorflow的slice方法去切分出图像的一部分数据去操作。
import tensorflow as tf
import matplotlib.image as mpimg
import matplotlib.pyplot as plt
# 首先加载图片
filename = "./test.jpg" # 随便找个本地图片即可
raw_image_data = mpimg.imread(filename) # (336, 500, 3)
image = tf.placeholder("uint8", [None, None, 3]) # 定义占位符
slice_ = tf.slice(image, [0, 0, 0], [200, -1, -1]) # 取出部分数据
# 函数:tf.slice(inputs, begin, size, name)
# 作用:从列表、数组、张量等对象中抽取一部分数据
# begin和size是两个多维列表,他们共同决定了要抽取的数据的开始和结束位置
# begin表示从inputs的哪几个维度上的哪个元素开始抽取
# size表示在inputs的各个维度上抽取的元素个数
# 若begin[]或size[]中出现-1,表示抽取对应维度上的所有元素
with tf.Session() as session:
result = session.run(slice_, feed_dict={image: raw_image_data})
print(result.shape)
plt.imshow(result)
plt.show()
result:
本文翻译 https://databricks.com/tensorflow/placeholders
在该文章基础上做了部分修改,更易懂些。
在代码层面,每一个tensor值在graph上都是一个op,当我们将train数据分成一个个minibatch然后传入网络进行训练时,每一个minibatch都将是一个op,这样的话,一副graph上的op未免太多,也会产生巨大的开销;于是就有了tf.placeholder(),我们每次可以将 一个minibatch传入到x = tf.placeholder(tf.float32,[None,32])上,下一次传入的x都替换掉上一次传入的x,这样就对于所有传入的minibatch x就只会产生一个op,不会产生其他多余的op,进而减少了graph的开销。[https://www.cnblogs.com/Johnny-z6951/p/11049596.html]