TensorFlow中关于张量的运算主要有:数学运算,矢量运算及矩阵运算,另外在实际的张量运算过程中,支也支持与numpy的广播特性,不同维度的张量之间往往存在这低维张量向高维张量进行广播的现象,这也称之为张量的广播机制。本文主要从数学运算、矢量运算、矩阵运算三个方面介绍张量运算相关的内容,最后我们在大致了解以下张量的广播机制。
一、数学运算
加减乘除、乘方开方、三角函数,指数对数以及逻辑运算等都是TensorFlow中比较常见的数学运算函数,TensorFlow提供了tf.math
包来实现这些数学运算,另外大部分数学运算还可以通过运算符重载的方式来实现 。这里我们简单示例几种常见的数学运算操作,同样的,在正式进入代码演示之前,先引入我们依赖的包,代码如下:
import numpy as np
import tensorflow as tf
print("numpy.version:", np.__version__)
print("tensorflow.version:", tf.__version__)
结果如下:
numpy.version: 1.23.5
tensorflow.version: 2.11.0
下面我们正式展示TensorFlow中的数学运算操作,代码如下
# 定义两个二维张量
a = tf.constant([[1.0, 2], [3, 4]])
b = tf.constant([[1.0, 0], [-1, 0]])
print("a:")
tf.print(a)
print("----------------------------------")
print("b:")
tf.print(b)
print("----------------------------------")
# 加法运算
print("a + b:")
tf.print(a + b)
print("----------------------------------")
# 减法运算
print("a - b:")
tf.print(a - b)
print("----------------------------------")
# 乘法运算
print("a * b:")
tf.print(a * b)
print("----------------------------------")
# 除法运算
print("a / b:")
tf.print(a / b)
print("----------------------------------")
# 乘方
print("b ** 2:")
tf.print(b ** 2)
print("----------------------------------")
# 开方
print("sqrt(a):")
tf.print(tf.sqrt(a))
print("----------------------------------")
# add运算符重载,a + 1等价于tf.math.add
print("a + 1:")
tf.print(a + 1)
print("tf.math.add(a, 1):")
tf.print(tf.math.add(a, 1))
print("----------------------------------")
# 逻辑运算
print("b > 0:")
tf.print(b > 0)
print("b == 0:")
tf.print(b == 0)
print("----------------------------------")
# 最大值
print("tf.maximum(a, b)")
tf.print(tf.maximum(a, b))
print("----------------------------------")
# 最小值
print("tf.minimum(a,b)")
tf.print(tf.minimum(a,b))
print("----------------------------------")
结果如下:
a:
[[1 2]
[3 4]]
----------------------------------
b:
[[1 0]
[-1 0]]
----------------------------------
a + b:
[[2 2]
[2 4]]
----------------------------------
a - b:
[[0 2]
[4 4]]
----------------------------------
a * b:
[[1 0]
[-3 0]]
----------------------------------
a / b:
[[1 inf]
[-3 inf]]
----------------------------------
b ** 2:
[[1 0]
[1 0]]
----------------------------------
sqrt(a):
[[1 1.41421354]
[1.73205078 2]]
----------------------------------
a + 1:
[[2 3]
[4 5]]
tf.math.add(a, 1):
[[2 3]
[4 5]]
----------------------------------
b > 0:
[[1 0]
[0 0]]
b == 0:
[[0 1]
[0 1]]
----------------------------------
tf.maximum(a, b)
[[1 2]
[3 4]]
----------------------------------
tf.minimum(a,b)
[[1 0]
[-1 0]]
----------------------------------
二、矢量运算
矢量运算是指在一个特定轴上进行运算,将一个张量映射到一个标量或者另外一个低维张量。
许多矢量运算都以reduce开头。下面分别从一维张量和多维张量的角度展示矢量运算的用法,代码如下:
#一维张量的矢量运算
a = tf.range(1,6)
print("a:")
tf.print(a)
print("----------------------------------")
#求和
tf.print("reduce_sum:", tf.reduce_sum(a, axis = 0))
#如果只是一维张量,默认可以不指定axis
#求最大值
tf.print("reduce_max:", tf.reduce_max(a))
#求最小值
tf.print("reduce_min:", tf.reduce_min(a))
#求最均值
tf.print("reduce_mean:", tf.reduce_mean(a))
#求积
tf.print("reduce_prod:", tf.reduce_prod(a))
#按顺序求每个位置累加和,类似于mysql中的窗口函数
tf.print("cumsum:", tf.math.cumsum(a))
#按顺序求每个位置累积
tf.print("cumprod:", tf.math.cumprod(a))
print("----------------------------------")
c = tf.constant([True,True,True])
d = tf.constant([False,False,True])
print("c:")
tf.print(c)
print("----------------------------------")
print("d:")
tf.print(d)
print("----------------------------------")
tf.print("reduce_all(c):", tf.reduce_all(c))
tf.print("reduce_all(d):", tf.reduce_all(d))
tf.print("reduce_any(d):", tf.reduce_any(d))
print("----------------------------------")
#tf.math.top_k可以用于对张量取top k
a = tf.constant([1,3,6,6,4,6])
tf.print("a:",a)
values,indices = tf.math.top_k(a, 3, sorted=True)
print("top 3:")
tf.print("indices", indices, "values", values)
print("----------------------------------")
#多维张量的矢量运算
b = tf.random.uniform(shape=[3, 3, 3], minval=0, maxval=2, dtype=tf.int32)
print("b:")
tf.print(b)
print("----------------------------------")
tf.print("axis=0, reduce_sum:\n", tf.reduce_sum(b, axis=0, keepdims=True))
tf.print("axis=1, reduce_sum:\n", tf.reduce_sum(b, axis=1, keepdims=False))
tf.print("axis=2, reduce_sum:\n", tf.reduce_sum(b, axis=2, keepdims=False))
tf.print("no axis, reduce_sum:", tf.reduce_sum(b))
print("----------------------------------")
结果如下:
a:
[1 2 3 4 5]
----------------------------------
reduce_sum: 15
reduce_max: 5
reduce_min: 1
reduce_mean: 3
reduce_prod: 120
cumsum: [1 3 6 10 15]
cumprod: [1 2 6 24 120]
----------------------------------
c:
[1 1 1]
----------------------------------
d:
[0 0 1]
----------------------------------
reduce_all(c): 1
reduce_all(d): 0
reduce_any(d): 1
----------------------------------
a: [1 3 6 6 4 6]
top 3:
indices [2 3 5] values [6 6 6]
----------------------------------
b:
[[[0 0 1]
[1 1 0]
[0 0 0]]
[[0 1 1]
[0 0 1]
[1 1 0]]
[[1 1 0]
[0 0 1]
[0 0 0]]]
----------------------------------
axis=0, reduce_sum:
[[[1 2 2]
[1 1 2]
[1 1 0]]]
axis=1, reduce_sum:
[[1 1 1]
[1 2 2]
[1 1 1]]
axis=2, reduce_sum:
[[1 2 0]
[2 1 2]
[2 1 0]]
no axis, reduce_sum: 11
----------------------------------
三、矩阵运算
矩阵运算主要是支持一些常规的线性代数中矩阵的计算,TensorFlow提供了tf.linalg
包来实现大部分和矩阵有关的运算,包括转置、矩阵乘法、求逆、求迹、范数、行列式、求特征值以及矩阵分解等。下面通过代码来展示常用的矩阵运算,代码如下:
# 定义两个二维张量来表示矩阵
a = tf.constant([[1, 2], [3, 4]], dtype=tf.float32)
b = tf.constant([[1, 0], [-1, 0]], dtype=tf.float32)
print("a:")
tf.print(a)
print("----------------------------------")
print("b:")
tf.print(b)
print("----------------------------------")
#矩阵转置
tf.print("矩阵转置:\n", tf.transpose(a))
print("----------------------------------")
#矩阵乘法
tf.print("矩阵乘法1:\n", tf.matmul(a, b))
tf.print("矩阵乘法2:\n", a @ b )
print("----------------------------------")
#矩阵求逆
tf.print("矩阵求逆:\n", tf.linalg.inv(a))
print("----------------------------------")
#矩阵求迹
tf.print("矩阵求迹:\n", tf.linalg.trace(a))
print("----------------------------------")
#矩阵特征值
tf.print("矩阵特征值:\n")
print( tf.linalg.eigvals(a))
print("----------------------------------")
#矩阵分解
s,u,v = tf.linalg.svd(a)
print("矩阵分解:\n")
tf.print(u,"\n")
tf.print(s,"\n")
tf.print(v,"\n")
tf.print(u @ tf.linalg.diag(s) @ tf.transpose(v))
print("----------------------------------")
结果如下
a:
[[1 2]
[3 4]]
----------------------------------
b:
[[1 0]
[-1 0]]
----------------------------------
矩阵转置:
[[1 3]
[2 4]]
----------------------------------
矩阵乘法1:
[[-1 0]
[-1 0]]
矩阵乘法2:
[[-1 0]
[-1 0]]
----------------------------------
矩阵求逆:
[[-2.00000024 1.00000012]
[1.50000012 -0.50000006]]
----------------------------------
矩阵求迹:
5
----------------------------------
矩阵特征值:
tf.Tensor([-0.37228122+0.j 5.372281 +0.j], shape=(2,), dtype=complex64)
----------------------------------
矩阵分解:
[[0.404553503 -0.914514303]
[0.914514303 0.404553503]]
[5.46498537 0.365966141]
[[0.576048374 0.817415595]
[0.817415595 -0.576048374]]
[[0.999999583 1.99999964]
[2.99999976 4]]
----------------------------------
四、广播机制
在Numpy中,广播机制(broad casting)指的是通过扩展低维度ndarray的维度来实现不同维度间ndarray的加减乘除等操作的机制。TensorFlow的广播机制来源于Numpy,其针对张量运算的广播规则和Numpy基本是一样的,广播规则如下:
- 如果张量的维度不同,对维度小的张量进行升维,直到两个张量的维度一样。
- 如果两个张量在某个维度上的长度是相同的,或者其中一个张量在该维度上的长度为1,那么就认为这两个张量在该维度上是相容的。
- 如果两个张量在所有维度上均相容,它们之间就能应用广播。
- 广播后,每个维度的长度将取两个张量在该维度长度的较大值。
- 在任何一个维度上,如果一个张量的长度为1,另一个张量长度大于1,那么在该维度上的广播就是对第一个张量进行复制。
TensorFlow中实现广播有两种方式,一种是使用tf.broadcast_to
显式地讲张量广播至指定维度,还有一种就是在运算过程系统进行隐式的广播。下面进行简单的示例,代码如下:
#定义两个维度不同的张量
a = tf.constant([1, 1, 1])
b = tf.constant([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print("a:")
tf.print(a)
print("----------------------------------")
print("b:")
tf.print(b)
print("----------------------------------")
#显式广播
tf.print("显式广播:\n", tf.broadcast_to(a, b.shape))
print("----------------------------------")
#隐式广播
tf.print("隐式广播:\na + b:\n", a + b) #等价于 tf.broadcast_to(a, b.shape) + b
tf.print("隐式广播:\na + 1:\n", a + 1)#等价于 a + tf.broadcast_to(1, a.shape)
print("----------------------------------")
#计算广播后的shape
tf.print("广播后的shape:", tf.broadcast_static_shape(a.shape, b.shape))
tf.print("广播后的shape:", tf.broadcast_dynamic_shape(a.shape, b.shape))
print("----------------------------------")
结果如下:
a:
[1 1 1]
----------------------------------
b:
[[1 2 3]
[4 5 6]
[7 8 9]]
----------------------------------
显式广播:
[[1 1 1]
[1 1 1]
[1 1 1]]
----------------------------------
隐式广播:
a + b:
[[2 3 4]
[5 6 7]
[8 9 10]]
隐式广播:
a + 1:
[2 2 2]
----------------------------------
广播后的shape: TensorShape([3, 3])
广播后的shape: [3 3]
----------------------------------
关于张量的运算,就简单介绍到这里。