Tensorflow的张量以及常用的张量运算

张量以及张量运算

TensorFlow的运算基本上都是基于张量的。张量是多维array,跟numpy类型,也可以通过方法和tensor进行转换,比如tensor支持.numpy()方法转换为numpy array,两者在进行运算时,也会自动转换:

import numpy as np

ndarray = np.ones([3, 3])

print("TensorFlow operations convert numpy arrays to Tensors automatically")
tensor = tf.multiply(ndarray, 42)
print(tensor)


print("And NumPy operations convert Tensors to numpy arrays automatically")
print(np.add(tensor, 1))

print("The .numpy() method explicitly converts a Tensor to a numpy array")
print(tensor.numpy())

tensor也有dtype以及shape。最大的区别是tensor是能通过GPU进行加速运算的。

创建张量

  • tf.constant(): 可以将list,scale或者是numpy对象变为张量。

tf.matmul

进行张量的点乘运算。

x = tf.matmul([[1]], [[2, 3]])
print(x)
print(x.shape)
print(x.dtype)

tf.unstack与stack

将n维的向量拆成n-1维张。返回的值是一个list,里面是拆分好的n-1维张量]:

def unstack(value, num=None, axis=0, name="unstack"):

其反操作为tf.stack:

def stack(values, axis=0, name="stack")# 这里的values应该是list-like类型

例子:

import tensorflow as tf
import numpy as np
# 将3,4的张量的第一维拆分,
x = tf.reshape(tf.range(12), (3,4))
# 结果为3个shape(4,)的张量
p, q, r = tf.unstack(x)
# 反操作
x=tf.stack([p, q, r])
# 换axis=1维
i, j, k, l = tf.unstack(x, axis=1)
x = tf.stack([i, j, k, l], axis=1)

# 对aixs=2进行拆分
X = np.array([[[0, 1, 2], [9, 8, 7],[3,6,8]],
[[3, 4, 5], [0, 0, 0],[0,0,0]],
[[6, 7, 8], [6, 5, 4],[1,7,4]],
[[9, 0, 1], [3, 7, "A"],[0,0,0]]])# shape=(4,3,3)
# 对axis=2的维度进行拆分,就相当于把这一维分为shape[2](3)个shape(4,3)的tensor。同时每个元素在axis=0,axis=1的索引是不变的
X=tf.unstack(X, axis=2)
print(X)
# axis=1进行维度拆分:相当把这一维分为shape[1](3)个,shape(shape[0],shape[2])个张量,
# 可以看到这里会将原来的元素的axis=2的索引向前提升到axis=1的位置。这样每个元素的关系也就清楚了:
# 如原来是X[3][1][2]=的元素"A",在axis=1拆分后,应该在第二个张量,tensor2[3][2]的位置
tensor1,tensor2,tensor3=tf.unstack(X, axis=1)

总结:

  1. 对i维进行拆分,就能生成len(i)个子张量。一定要注意,这里是进行了降维了。降维了。
  2. 对于元素新的索引来说,i维之后的索引的位置就向前进1,而i维以前的索引不变。

tf.concat

pass

tf.cast

将张量转化为另一种类型。The operation casts x (in case of Tensor) or x.values (in case of SparseTensor or IndexedSlices) to dtype.tf.cast还有一个别名:tf.dtypes.cast

tf.cast(
    x, dtype, name=None
)
  • X: A Tensor or SparseTensor or IndexedSlices of numeric type. It could be uint8, uint16, uint32, uint64, int8, int16, int32, int64, float16, float32, float64, complex64, complex128, bfloat16.
  • dtype: 转换的目标类型. The list of supported dtypes is the same as x.

tf.tile

通过填充张量来构造一个新的张量。这个操作用过复制inputmultiples倍来生成新的张量。输出张量的第i维=input.dims(i) * multiples[i]。第i维的元素将重复multiples[i]倍,如:tiling [a b c d] by [2] produces [a b c d a b c d].

tf.tile(
    input, multiples, name=None
)
  • input:A Tensor. 1-D or higher.

  • multiples:A Tensor. Must be one of the following types: int32, int64. 1-D. Length must be the same as the number of dimensions in input。multiples的长度应与input的维度数是一致的。

    注意这个tile有填充方向的,可以向inputs的任意维度方向上填充

例如:

a = tf.constant([[1,2,3],[4,5,6]], tf.int32)
b = tf.constant([1,2], tf.int32)
tf.tile(a, b)
#array([[1, 2, 3, 1, 2, 3],
#       [4, 5, 6, 4, 5, 6]])>
c = tf.constant([2,1], tf.int32)
tf.tile(a, c)
#array([[1, 2, 3],
#       [4, 5, 6],
#       [1, 2, 3],
#       [4, 5, 6]])>
d = tf.constant([2,2], tf.int32)
tf.tile(a, d)

tf.repeat

pass

tf.split()

tf.split(
    value, num_or_size_splits, axis=0, num=None, name='split'
)

将tensor切分为一系列的子张量。

  • value: 待切分的张量
  • num_or_size_splits: 可以是指示沿轴拆分次数的int,也可以是一维整数张量(Tensor),或者是包含每个输出张量沿轴大小的Python列表。如果是int类型,则必须均匀划分value.shape[axis];否则,拆分轴上的大小和必须与值匹配。
  • axis: 要切分的轴
  • num:可选的int类型,当不能从size_splitting的形状推断输出时,用于指定输出的数量。

如果num_or_size_splits是整数,返回的是一个长度为num_or_size_splits的张量对象的list。如果num_or_size_split是列表或者是张量,返回num_or_size_splits.get_shape[0]个的张量的列表,且每个张量切分的axis轴上的shape,由list指定。

x = tf.Variable(tf.random.uniform([5, 30], -1, 1))

# Split `x` into 3 tensors along dimension 1
s0, s1, s2 = tf.split(x, num_or_size_splits=3, axis=1)
tf.shape(s0).numpy()


# Split `x` into 3 tensors with sizes [4, 15, 11] along dimension 1
split0, split1, split2 = tf.split(x, [4, 15, 11], 1)
tf.shape(split0).numpy()
#array([ 5, 4])
tf.shape(split1).numpy()
#array([ 5, 15])
tf.shape(split2).numpy()
# array([ 5, 11])

tf.reduce_mean

注意,这个方法现在是在tf.compat.v1.reduce_mean里,更多的信息在Migration guide里。还有一个别名就是tf.compat.v1.math.reduce_mean

tf.compat.v1.reduce_mean(
    input_tensor,
    axis=None,
    keepdims=None,
    name=None,
    reduction_indices=None,
    keep_dims=None
)

计算一个张量指定维上的元素平均值。

通过计算跨坐标轴维度的元素的平均值,沿着给定的维度(axis参数制定的)对input_tensor降维。除非keepdims为真,张量的秩对于axis维度上的每一个实体都减少1,这必须是唯一的。如果keepdims为true,则不进行降维,但axis为1。

参数:

  • input_tensor

  • axis:降维时要压缩(or 去掉)的维度。默认为None,即则所有维度都降维,并且返回一个只有单个元素的张量。注意,这里是沿这轴,最好的理解就是其他的维的索引不变,axis维上的索引遍历一遍,然后提出元素,最后求平均值。

    必须是[-rank(input_tensor),rank(input_tensor)]之间的数,这里的rank,其实就是dims数,而负值相当于列表中的负索引。

  • keepdims:如果为True,那么被降得维长度为1,就是说有一个元素了

  • name:这个操作的名字

  • reduction_indices:与axis的作用一样,现在已经被废弃,不再使用了

  • keep_dims:与keepdims一样,现在已经被废弃,不再使用了

例子:

x = tf.constant([[1., 1.], [2., 2.]])
tf.reduce_mean(x)
> <tf.Tensor: shape=(), dtype=float32, numpy=1.5>
tf.reduce_mean(x,0)
> <tf.Tensor: shape=(2,), dtype=float32, numpy=array([1.5, 1.5], dtype=float32)>
tf.reduce_mean(x,1)
> <tf.Tensor: shape=(2,), dtype=float32, numpy=array([1., 2.], dtype=float32)>
tf.reduce_mean(x,-1)
> <tf.Tensor: shape=(2,), dtype=float32, numpy=array([1., 2.], dtype=float32)>
tf.reduce_mean(x,-2)
> <tf.Tensor: shape=(2,), dtype=float32, numpy=array([1.5, 1.5], dtype=float32)>

注意:这个方法其实相当于相当于np.mean。但是np.mean有个dtype属性,默认为float64,但是tf.reduce_mean会从输入的input_tensor猜测dtype类型,并且覆盖np.mean默认的float64属性,导致输出结果有差别,如:

np_x1=np.array([1,0,1,0])
np_x2=np.array([1.,0.,1.,0.])
np.mean(np_x1)
> 0.5
np.mean(np_x2)
> 0.5

x1 = tf.constant([1, 0, 1, 0])
x2 = tf.constant([1., 0., 1., 0.])
tf.reduce_mean(x1)
> <tf.Tensor: shape=(), dtype=int32, numpy=0>
tf.reduce_mean(x2)
> <tf.Tensor: shape=(), dtype=float32, numpy=0.5>

tf.broadcast_to

tf.broadcast_to(
    input, shape, name=None
)
  • input:要进行Broadcasting的张量
  • shape: A Tensor. Must be one of the following types: int32, int64. An 1-D int Tensor. The shape of the desired output.
  • name:

Broadcasting是一种可以让数据(array)变成可兼容算术运算的操作。如果两个形状相等或其中一个为一,则两个形状是兼容(compatible)的。

x = tf.constant([[1, 2, 3]])   # Shape (1, 3,)
y = tf.broadcast_to(x, [2, 3])
print(y)
>tf.Tensor(
>[[1 2 3]
> [1 2 3]], shape=(2, 3), dtype=int32)

Broadcasting时,如果一个张量的axis少于必要的轴数,它的shape就在左侧填充1。

x = tf.constant([1, 2, 3])   # Shape (3,)
y = tf.broadcast_to(x, [2, 3])
>  array([[1, 2, 3],
>       [1, 2, 3]])>

要注意的是broadcast_to一般要求后面的维数与原维数相同,比如:

x = tf.constant([1, 2, 3])   # Shape (3,)
y = tf.broadcast_to(x, [2, 3])# 
y = tf.broadcast_to(x, [3,1, 3])# 
y = tf.broadcast_to(x, [1,1, 3])# y = tf.broadcast_to(x, [3,1])# 报错
x = tf.constant([[1, 2, 3],[4,5,6]])
y = tf.broadcast_to(x, [2,2, 3])# 
y = tf.broadcast_to(x, [1,2, 3])# 
y = tf.broadcast_to(x, [2, 3,1])# 报错
y = tf.broadcast_to(x, [2,4,3])#报错 

也要注意一个例外,就是上面所举的有一个维度为1,这一维是可以任意扩展的:

x = tf.constant([[1, ],[4,]])# shape(2,1)
y = tf.broadcast_to(x, [2,4])
y = tf.broadcast_to(x, [3,2,4])# 不报错

>>> <tf.Tensor: shape=(3, 2, 4), dtype=int32, numpy=
array([[[1, 1, 1, 1],
        [4, 4, 4, 4]],
       [[1, 1, 1, 1],
        [4, 4, 4, 4]],
       [[1, 1, 1, 1],
        [4, 4, 4, 4]]])>

当进行广播操作时,例如将一个张量乘以一个标量,广播(通常)提供了一些时间或空间上的好处,因为广播张量从未被现实的创建。然而,broadcast_to并没有带来任何这样的好处。新创建的张量占用了传播形状的全部内存。(但是,在图上下文中,broadcast_to可能被融合到后续操作中,然后被优化掉。)

你可能感兴趣的:(python,#,Tensorflow,#,机器学习,tensorflow,python,深度学习)