2. 张量概述

2022-07-26  本文已影响0人  hdszzwy

张量是同一类型的数字或字符串构成的多维数组。若是你对numpy比较熟悉的话,那么张量与np.arrays是类似的。与python中的数字和字符串一样,所有的张量都是不可变的。即张量不可更改,只能创建新的张量。

张量基础操作

让我们从创建一些简单的张量开始吧!

rank_0_tensor = tf.constant(4)
print(rank_0_tensor)

结果为:

tf.Tensor(4, shape=(), dtype=int32)
rank_1_tensor = tf.constant([2.0, 3.0, 4.0])
print(rank_1_tensor)

结果为:

tf.Tensor([2. 3. 4.], shape=(3,), dtype=float32)
rank_2_tensor = tf.constant([[1, 2], [3, 4], [5, 6]], dtype=tf.float16)
print(rank_2_tensor)

结果为:

tf.Tensor(
[[1. 2.]
 [3. 4.]
 [5. 6.]], shape=(3, 2), dtype=float16)
rank_3_tensor = tf.constant([
  [[0, 1, 2, 3, 4],
   [5, 6, 7, 8, 9]],
  [[10, 11, 12, 13, 14],
   [15, 16, 17, 18, 19]],
  [[20, 21, 22, 23, 24],
   [25, 26, 27, 28, 29]]])

print(rank_3_tensor)

结果为:

tf.Tensor(
[[[ 0  1  2  3  4]
  [ 5  6  7  8  9]]

 [[10 11 12 13 14]
  [15 16 17 18 19]]

 [[20 21 22 23 24]
  [25 26 27 28 29]]], shape=(3, 2, 5), dtype=int32)

你可以使用np.array或tensor.numpy()将一个张量转换为一个NumPy数组

print(np.array(rank_2_tensor))
print(rank_2_tensor.numpy())

结果均为

[[1. 2.]
 [3. 4.]
 [5. 6.]]

大多数的时候,张量里面保存的是整数或者小数。但是张量也可以保存其他类型:如复数和字符串。
tf.Tensor要求张量类似于矩阵一样每个坐标上的元素又都有相同数量的元素。但是,特殊张量可以处理子元素不相同的情况。这些特殊张量有:不规则张量(Ragged tensor)和稀疏张量(sparse tensor)
张量有一些基本的操作,如逐元素相加,逐元素相乘和矩阵相乘。

a = tf.constant([[1, 2],
                 [3, 4]])
b = tf.ones([2, 2], dtype=tf.int32)

print(a)
print(b)
print(tf.add(a, b))
print(tf.multiply(a, b))
print(tf.matmul(a, b))

结果为:

tf.Tensor(
[[1 2]
 [3 4]], shape=(2, 2), dtype=int32)
tf.Tensor(
[[1 1]
 [1 1]], shape=(2, 2), dtype=int32)
tf.Tensor(
[[2 3]
 [4 5]], shape=(2, 2), dtype=int32)
tf.Tensor(
[[1 2]
 [3 4]], shape=(2, 2), dtype=int32) 
tf.Tensor(
[[3 3]
 [7 7]], shape=(2, 2), dtype=int32)
print(a + b)
print(a * b)
print(a @ b)

结果为:

tf.Tensor(
[[2 3]
 [4 5]], shape=(2, 2), dtype=int32) 
tf.Tensor(
[[1 2]
 [3 4]], shape=(2, 2), dtype=int32) 
tf.Tensor(
[[3 3]
 [7 7]], shape=(2, 2), dtype=int32)

TensorFlow还支持了其他各种运算。

c = tf.constant([[4.0, 5.0], [10.0, 1.0]])

print(tf.reduce_max(c))
print(tf.math.argmax(c))
print(tf.nn.softmax(c))

结果为:

tf.Tensor(10.0, shape=(), dtype=float32)
tf.Tensor([1 0], shape=(2,), dtype=int64)
tf.Tensor(
[[2.6894143e-01 7.3105860e-01]
 [9.9987662e-01 1.2339458e-04]], shape=(2, 2), dtype=float32)

Tensor的shape属性

以下式关于张量的一些概念词汇:

rank_4_tensor = tf.zeros([3, 2, 4, 5])

print("Type of every element:", rank_4_tensor.dtype)
print("Number of axes:", rank_4_tensor.ndim)
print("Shape of tensor:", rank_4_tensor.shape)
print("Elements along axis 0 of tensor:", rank_4_tensor.shape[0])
print("Elements along the last axis of tensor:", rank_4_tensor.shape[-1])
print("Total number of elements (3*2*4*5): ", tf.size(rank_4_tensor).numpy())

运算结果为:

Type of every element: <dtype: 'float32'>
Number of axes: 4
Shape of tensor: (3, 2, 4, 5)
Elements along axis 0 of tensor: 3
Elements along the last axis of tensor: 5
Total number of elements (3*2*4*5):  120

张量的坐标系轴在某些文献中也被称为索引,你需要注意辨认其真正含义。坐标系轴的排序是从宏观到局部,各个维度依次为batch,height,width, feature。

索引

对Shape的操作

张量的shape重塑相当有用处。你可以将某个张量重塑为其他的shape。由于底层数据不需要移动,所以tf.reshape操作速度快消耗低。

x = tf.constant([[1], [2], [3]])
reshaped = tf.reshape(x, [1, 3])
print(x.shape)
print(reshaped.shape)

结果为:

(3, 1)
(1, 3)

当进行reshape操作时,内存中的数据不变。新的张量按照指定的shape被创建后指向原来的数据。TensorFlow使用C风格的行优先方式进行内存管理,即最右的索引对应于内存中的步长。将一个三维张量flatten之后,你就可以看出内存中的存放方式。

rank_3_tensor = [[[0, 1, 2, 3, 4],
                  [5, 6, 7, 8, 9]],
                 [[10, 11, 12, 13, 14],
                  [15, 16, 17, 18, 19]],
                 [[20, 21, 22, 23, 24],
                  [25, 26, 27, 28, 29]]]
# 向reshape传参-1即可将张量flatten
print(tf.reshape(rank_3_tensor, [-1]))

结果为:

tf.Tensor(
[ 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
 24 25 26 27 28 29], shape=(30,), dtype=int32)

tf.reshape的典型应用场景是将坐标轴进行合并或切分。将一个325的张量reshape成一个(32)5或3(25)的操作如下:

print(tf.reshape(rank_3_tensor, [3*2, 5]))
print(tf.reshape(rank_3_tensor, [3,-1]))

运行结果为:

tf.Tensor(
[[ 0  1  2  3  4]
 [ 5  6  7  8  9]
 [10 11 12 13 14]
 [15 16 17 18 19]
 [20 21 22 23 24]
 [25 26 27 28 29]], shape=(6, 5), dtype=int32)
tf.Tensor(
[[ 0  1  2  3  4  5  6  7  8  9]
 [10 11 12 13 14 15 16 17 18 19]
 [20 21 22 23 24 25 26 27 28 29]], shape=(3, 10), dtype=int32)

只要张量的总数保持不变,tensor可以将张量reshape为任意的shape。但若是总数发生了变化,那TensorFlow就无能为力了。
尽管下列的代码可以执行,但是实在没有什么意义结果也并不如想象中的那样。

# 将rank_3_tensor交换坐标系的结果,应该是
# [[[0, 1, 2, 3, 4],
# [10, 11, 12, 13, 14],
# [20, 21, 22, 23, 24]],
# [[5, 6, 7, 8, 9],
# [15, 16, 17, 18, 19],
# [25, 26, 27, 28, 29]]]
print(tf.reshape(rank_3_tensor, [2, 3, 5]))
# 这个操作毫无意义,尽管有结果
print(tf.reshape(rank_3_tensor, [5, 6]))

上述代码的运行结果为:

[[[ 0  1  2  3  4]
  [ 5  6  7  8  9]
  [10 11 12 13 14]]

 [[15 16 17 18 19]
  [20 21 22 23 24]
  [25 26 27 28 29]]], shape=(2, 3, 5), dtype=int32)
tf.Tensor(
[[ 0  1  2  3  4  5]
 [ 6  7  8  9 10 11]
 [12 13 14 15 16 17]
 [18 19 20 21 22 23]
 [24 25 26 27 28 29]], shape=(5, 6), dtype=int32)

可以看出交换坐标系的代码尽管不报错,但是结果并不是想象中的那样的结果。无意义的reshape操作也不会报错。
而下列代码会报错,原因是张量的总数发生了变化。

try:
    tf.reshape(rank_3_tensor, [7, -1])
except Exception as e:
    print(f"{type(e).__name__}: {e}")

报错如下:

InvalidArgumentError: Input to reshape is a tensor with 30 values, but the requested shape requires a multiple of 7 [Op:Reshape]

你可以将在创建维度是不指定张量的维度,即将张量的shape的某一个维度设置为None或将整个张量的维度直接指定为None。

DTypes

你可以是用Tensor.dtype属性来查看Tensor的数据类型。
当创建tf.Tensor时,你可以选择指定数据类型。若时不指定数据类型,TensorFlow将会选择一个数据类型来表示你的数据,TensorFlow会将Python的整型转化为tf.int32,将Python的浮点型转换为tf.float32。其他类型TensorFlow遵循如Numpy的规则。
你也可以对Tensor的类型进行强制转换

the_f64_tensor = tf.constant([2.2, 3.3, 4.4], dtype=tf.float64)
the_f16_tensor = tf.cast(the_f64_tensor, dtype=tf.float16)
# Now, cast to an uint8 and lose the decimal precision
the_u8_tensor = tf.cast(the_f16_tensor, dtype=tf.uint8)
print(the_u8_tensor)

运行结果为:

tf.Tensor([2 3 4], shape=(3,), dtype=uint8)

广播

上一篇下一篇

猜你喜欢

热点阅读