Tensor
和 SparseTensor
两种张量抽象,分别表示稠密数据和稀疏数据,后者旨在减少高维稀疏数据的内存占用。
TF Tensor具有极强的数据表达能力,这既体现在它对High dimensional data的抽象描述,又体现在它对多样化data type的支持。
Data type | TF 数据类型 | 说明 |
---|---|---|
DT_HALF | tf.float16 | 半精度浮点数 |
DT_FLOAT | tf.float32 | 单精度浮点数 |
DT_DOUBLE | tf.float64 | 双精度浮点数 |
DT_BFLOAT16 | tf.bfloat16 | 裁短浮点数 |
DT_INT8 | tf.int8 | 8位有符号整数 |
DT_INT16 | tf.int16 | 16位有符号整数 |
DT_INT32 | tf.int32 | 32位有符号整数 |
DT_INT64 | tf.int64 | 64位有符号整数 |
DT_UINT8 | tf.uint8 | 8位无符号整数 |
DT_UINT16 | tf.uint16 | 16位无符号整数 |
DT_STRING | tf.string | 字符串 |
DT_BOOL | tf.bool | 布尔值 |
DT_COMPLEX64 | tf.complex64 | 单精度复数 |
DT_COMPLEX128 | tf.complex128 | 双精度复数 |
DT_QINT8 | tf.qint8 | 量化8位有符号整数 |
DT_QINT16 | tf.qint16 | 量化16位有符号整数 |
DT_QINT32 | tf.qint32 | 量化32位有符号整数 |
DT_QUINT8 | tf.quint8 | 量化8位无符号整数 |
DT_QUINT16 | tf.quint16 | 量化16位无符号整数 |
TF张量在逻辑定义上是数据载体,但在物理实现时是一个句柄,它存储张量的meta info以及指向张量数据的memory buffer pointer。这样设计是为了实现内存复用。在某些前置操作的输出值被输入到多个后置操作的情况下,无需重复存储输入值。当一个张量不在被任何操作依赖后,TF会释放存储该tensor的memory buffer。TF内部通过引用计数方式
判断是否应该释放张量数据的memory buffer。
在TF Python API中,稠密张量抽象是Tensor类。不过一般情况下,用户不需要使用Tensor类的构造方法直接创建张量,而是通过操作间接创建张量。典型的张量创建操作包括常量定义操作和代数计算操作。
import tensorflow as tf
a = tf.constant(1.0)
b = tf.constant(2.0)
c = tf.add(a, b)
print(a, b, c)
# 输出
"""
Tensor("Const:0", shape=(), dtype=float32)
Tensor("Const_1:0", shape=(), dtype=float32)
Tensor("Add:0", shape=(), dtype=float32)
"""
以上三个变量都是张量。
数据流图中的操作输出值由张量承载。如果想要求解特定张量值,则需要创建回话,然后执行张量的eval()
方法或者session的run()
方法。
with tf.Session() as sess:
print(c.eval())
print(sess.run(c))
#输出
"""
3.0
3.0
"""
TF Tensor抽象除了支持多样化数据类型外,也提供一些成员方法来动态改变张量形状,以及查看张量的后置操作。
member name | function |
---|---|
eval() |
取出张量值 |
get_shape() |
获取张量形状 |
set_shape() |
修改张量形状 |
consumers |
获取张量的后置操作 |
TF为Tensor提供了大量的操作,以便构建数据流图,实现算法模型。如一元代数操作,二元代数操作,形状操作,规约操作,神经网络操作,条件操作。
import tensorflow as tf
a = tf.constant([1, 1])
b = tf.constant([2, 3])
c = tf.add(a, b)
with tf.Session() as sess:
print("a[0] = {}, a[1] = {}".format(a[0].eval(), a[1].eval()))
print("c.name = {}".format(c.name))
print("c.value = {}".format(c.eval()))
print("c.shape = {}".format(c.shape))
print("a.consumers = {}".format(a.consumers()))
print("b.consumers = {}".format(b.consumers()))
print("[c.op]: \n {}".format(c.op))
# 输出
"""
a[0] = 1, a[1] = 1
c.name = Add:0
c.value = [3 4]
c.shape = (2,)
a.consumers = []
b.consumers = []
[c.op]:
name: "Add"
op: "Add"
input: "Const"
input: "Const_1"
attr {
key: "T"
value {
type: DT_INT32
}
}
"""
TF提供了专门用于处理高维稀疏数据的SparseTensor类,该类以键值对的形式表示高维稀疏数据,它包含indices, values, & dense_shape三个属性,其中,indices是一个形状为[N, ndims]的Tensor实例,N表示非零元素的个数,ndims表示张量的阶数。例如,当indices = [[0, 2], [1, 3], [2, 1]](N=2, ndims = 2),表示2阶系数张量中索引为[0, 2], [1, 3], [2, 1]的元素非零。values是一个形状为[N]的Tensor对象,用于保存indices中指定元素的值。dense_shape是一个形状为[ndims]的tensor实例,表示该稀疏张量对应稠密张量的形状。
在TF中创建sparse tensor,一般直接使用SparseTensor类的构造方法。
import tensorflow as tf
sp = tf.SparseTensor(indices=[[0, 2], [1, 3], [2, 1]],
values = [1, 3, 2],
dense_shape = [3, 4])
with tf.Session() as sess:
print(sp.eval())
dense = tf.sparse_tensor_to_dense(sp)
print(dense.eval())
"""
SparseTensorValue(indices=array([[0, 2],
[1, 3],
[2, 1]]), values=array([1, 3, 2], dtype=int32), dense_shape=array([3, 4]))
[[0 0 1 0]
[0 0 0 3]
[0 2 0 0]]
"""
TF为sparse tensor提供了一些专门的操作,这样用户能够像处理Tensor一样处理SparseTensor。主要包括:转换操作、代数操作、集合操作、归约操作。
import tensorflow as tf
sp = tf.SparseTensor(indices=[[0, 2], [1, 3], [2, 1]],
values = [1, 3, 2],
dense_shape = [3, 4])
reduce_x = [tf.sparse_reduce_sum(x),
tf.sparse_reduce_sum(x, axis=1),
tf.sparse_reduce_sum(x, axis=1, keep_dims=True),
tf.sparse_reduce_sum(x, axis=[0, 1])]
with tf.session() as sess:
dense = tf.sparse_tensor_to_dense(sp)
print(dense.eval())
print(sess.run(reduce_x))
# 输出
"""
dense_shape=array([3, 4]))
[[0 0 1 0]
[0 0 0 3]
[0 2 0 0]]
[6, array([1, 3, 2], dtype=int32), array([[1],
[3],
[2]], dtype=int32), 6]
"""
TF里关于sparse tensor有两个api接口,tf.SparseTensor()
and tf.SparseTensorValue()
。它们的使用区别在于:如果要在数据流图中定义sparse tensor,则使用tf.SparseTensor()
;而如果是给定义好的数据流图feed稀疏张量,那么要使用tf.SparseTensorValue()
。
tf.SparseTensor()
示例代码如下所示:
import tensorflow as tf
indices_i = tf.placeholder(dtype=tf.int64, shape=[2, 2])
values_i = tf.placeholder(dtype=tf.float32, shape=[2])
dense_shape_i = tf.placeholder(dtype=tf.int64, shape=[2])
st = tf.SparseTensor(indices=indices_i, values=values_i, dense_shape=dense_shape_i)
W = tf.Variable(tf.random_normal([6, 6]))
y = tf.sparse_tensor_dense_matmul(sp_a=st, b=W)
init = tf.global_variables_initializer()
gpu_options = tf.GPUOptions(per_process_gpu_memory_fraction=0.2)
sess = tf.Session(config=tf.ConfigProto(gpu_options=gpu_options))
sess.run(init)
result = sess.run(y, feed_dict={indices_i:[[0, 0], [1, 2]], values_i:[1.1, 1.2], dense_shape_i:[2,6]})
print(result)
tf.SparseTensorValue()
示例代码如下
import tensorflow as tf
x_sp = tf.sparse_placeholder(dtype=tf.float32)
W = tf.Variable(tf.random_normal([6, 6]))
y = tf.sparse_tensor_dense_matmul(sp_a=x_sp, b=W)
init = tf.global_variables_initializer()
gpu_options = tf.GPUOptions(per_process_gpu_memory_fraction=0.2)
sess = tf.Session(config=tf.ConfigProto(gpu_options=gpu_options))
sess.run(init)
stv = tf.SparseTensorValue(indices=[[0, 0], [1, 2]], values=[1.1, 1.2],
dense_shape=[2,6])
result = sess.run(y, feed_dict={x_sp:stv}
print(result)
可以看出tf.SparseTensorValue()
是和tf.sparse_placeholder()
对应的。但是tf.sparse_placeholder()
除了通过tf.SparseTensorValue()
进行feed,还支持元组(indices, values, shape)或者numpy数组。详见 chapter3.3 “数据节点:Placeholder”-“示例”。