TensorFlow2.0(2):数学运算
1 基本运算:(+、-、*、/、//、%)
基本运算中所有实例都以下面的张量a、b为例进行:
import tensorflow as tf
a = tf.random.uniform([2,3],minval=1,maxval=6,dtype=tf.int32)
b = tf.random.uniform([2,3],minval=1,maxval=6,dtype=tf.int32)
a
<tf.Tensor: id=3, shape=(2, 3), dtype=int32, numpy=
array([[5, 2, 2],
[1, 4, 2]], dtype=int32)>
b
<tf.Tensor: id=7, shape=(2, 3), dtype=int32, numpy=
array([[4, 1, 3],
[5, 1, 2]], dtype=int32)>
(1)加(+)
tf.add(a,b) # 也可以用 a+b
<tf.Tensor: id=8, shape=(2, 3), dtype=int32, numpy=
array([[9, 3, 5],
[6, 5, 4]], dtype=int32)>
(2)减(-)
tf.subtract(a,b) # 也可以用 a-b
<tf.Tensor: id=9, shape=(2, 3), dtype=int32, numpy=
array([[ 1, 1, -1],
[-4, 3, 0]], dtype=int32)>
(3)乘法(*)
tf.multiply(a,b) # 也可以用 a*b
<tf.Tensor: id=10, shape=(2, 3), dtype=int32, numpy=
array([[20, 2, 6],
[ 5, 4, 4]], dtype=int32)>
(4)除法(/)
tf.divide(a,b) # 也可以用 a/b
<tf.Tensor: id=13, shape=(2, 3), dtype=float64, numpy=
array([[1.25 , 2. , 0.66666667],
[0.2 , 4. , 1. ]])>
(5)整除(//)
a//b
<tf.Tensor: id=14, shape=(2, 3), dtype=int32, numpy=
array([[1, 2, 0],
[0, 4, 1]], dtype=int32)>
(6)取余(%)
a%b
<tf.Tensor: id=15, shape=(2, 3), dtype=int32, numpy=
array([[1, 0, 2],
[1, 0, 0]], dtype=int32)>
2 指数、开方、对数
(1)对数运算
TensorFlow提供tf.math.log()方法来求对数,求的是以自然常数e为底的对数。
a = tf.fill([2,2],1.)
tf.math.log(a)
<tf.Tensor: id=27, shape=(2, 2), dtype=float32, numpy=
array([[0., 0.],
[0., 0.]], dtype=float32)>
(2)指数运算
g = tf.constant([[2,3],[2,10]])
tf.pow(g,2) # 也可以用 g**2
<tf.Tensor: id=30, shape=(2, 2), dtype=int32, numpy=
array([[ 4, 9],
[ 4, 100]], dtype=int32)>
(3)开方
f = tf.constant([[1.,9.],[16.,100.]])
tf.sqrt(f)
自然常数e的指数运算:
d = tf.constant([[1.,2.],[3.,4.]])
tf.exp(d)
<tf.Tensor: id=41, shape=(2, 2), dtype=float32, numpy=
array([[ 2.7182817, 7.389056 ],
[20.085537 , 54.598152 ]], dtype=float32)>
3 矩阵相乘
import numpy as np
a = tf.constant(np.arange(6),shape=(2,3))
b = tf.constant(np.arange(6),shape=(3,2))
a
<tf.Tensor: id=44, shape=(2, 3), dtype=int64, numpy=
array([[0, 1, 2],
[3, 4, 5]])>
b
<tf.Tensor: id=47, shape=(3, 2), dtype=int64, numpy=
array([[0, 1],
[2, 3],
[4, 5]])>
tf.matmul(a,b)
<tf.Tensor: id=48, shape=(2, 2), dtype=int64, numpy=
array([[10, 13],
[28, 40]])>
矩阵相乘也可以通过符号操作来进行,用“@”表示:
a @ b
<tf.Tensor: id=49, shape=(2, 2), dtype=int64, numpy=
array([[10, 13],
[28, 40]])>
这里的张量a和b都是二维的,但是在实际应用中,数据往往高于二维,这时应该怎么计算呢?
a = tf.constant(np.arange(12),shape=(2,2,3))
b = tf.constant(np.arange(12),shape=(2,3,2))
a
<tf.Tensor: id=52, shape=(2, 2, 3), dtype=int64, numpy=
array([[[ 0, 1, 2],
[ 3, 4, 5]],
[[ 6, 7, 8],
[ 9, 10, 11]]])>
b
<tf.Tensor: id=55, shape=(2, 3, 2), dtype=int64, numpy=
array([[[ 0, 1],
[ 2, 3],
[ 4, 5]],
[[ 6, 7],
[ 8, 9],
[10, 11]]])>
a @ b
<tf.Tensor: id=56, shape=(2, 2, 2), dtype=int64, numpy=
array([[[ 10, 13],
[ 28, 40]],
[[172, 193],
[244, 274]]])>
可以看到,当高于二维的张量进行矩阵相乘时,最终的实现还是二维矩阵相乘,只不过分成了多个二维矩阵,四维张量也是一样的。
a = tf.constant(np.arange(24),shape=(2,2,2,3))
b = tf.constant(np.arange(24),shape=(2,2,3,2))
a
<tf.Tensor: id=59, shape=(2, 2, 2, 3), dtype=int64, numpy=
array([[[[ 0, 1, 2],
[ 3, 4, 5]],
[[ 6, 7, 8],
[ 9, 10, 11]]],
[[[12, 13, 14],
[15, 16, 17]],
[[18, 19, 20],
[21, 22, 23]]]])>
b
<tf.Tensor: id=62, shape=(2, 2, 3, 2), dtype=int64, numpy=
array([[[[ 0, 1],
[ 2, 3],
[ 4, 5]],
[[ 6, 7],
[ 8, 9],
[10, 11]]],
[[[12, 13],
[14, 15],
[16, 17]],
[[18, 19],
[20, 21],
[22, 23]]]])>
a @ b
<tf.Tensor: id=63, shape=(2, 2, 2, 2), dtype=int64, numpy=
array([[[[ 10, 13],
[ 28, 40]],
[[ 172, 193],
[ 244, 274]]],
[[[ 550, 589],
[ 676, 724]],
[[1144, 1201],
[1324, 1390]]]])>
4 Broadcasting机制
上面所有的实例中所用到的张量都是在维度数和形状相同情况下进行的,当两个张量的维度数或者形状不一样时能不能进行运算呢?
a = tf.constant([1,2,3],dtype=tf.int64)
b = tf.constant(np.arange(12),shape=(2,2,3))
a
<tf.Tensor: id=68, shape=(3,), dtype=int64, numpy=array([1, 2, 3])>
b
<tf.Tensor: id=71, shape=(2, 2, 3), dtype=int64, numpy=
array([[[ 0, 1, 2],
[ 3, 4, 5]],
[[ 6, 7, 8],
[ 9, 10, 11]]])>
a + b
<tf.Tensor: id=72, shape=(2, 2, 3), dtype=int64, numpy=
array([[[ 1, 3, 5],
[ 4, 6, 8]],
[[ 7, 9, 11],
[10, 12, 14]]])>
a * b
<tf.Tensor: id=73, shape=(2, 2, 3), dtype=int64, numpy=
array([[[ 0, 2, 6],
[ 3, 8, 15]],
[[ 6, 14, 24],
[ 9, 20, 33]]])>
可以看到,一个一维的张量与一个三维张量进行运算是完全没有问题的,从运算结果上可以看出,相当于是三维张量中的每一行数据与张量a进行运算,这得益于TensorFlow中的Broadcasting机制。
Broadcasting机制解除了只能维度数和形状相同的张量才能进行运算的限制,当两个数组进行算术运算时,TensorFlow的Broadcasting机制首先对维度较低的张量形状数组填充1,从后向前,逐元素比较两个数组的形状,当逐个比较的元素值(这里的元素值是指描述张量形状数组的值,而不是张量的值)满足以下条件时,认为满足Broadcasting的条件:
(1)相等;
(2)其中一个张量形状数组元素值为1。
当不满足时进行运算则会抛出异常。算术运算的结果的形状的每一元素,是两个数组形状逐元素比较时的最大值。
回到上面张量a与b相乘的例子,a的形状是(3,),b的形状是(2,2,3),在Broadcasting机制工作时,首先比较维度数,因为a的维度为1,小于b的维度3,所以填充1,a的形状就变成了(1,1,3),然后从最后端的形状数组元素依次往前比较,先是3与3比,结果是相等,接着1与2相比,因为其中一个为1,所以a的形状变成了(1,2,3),继续1与2比较,因为其中一个为1,所以a的形状变成了(2,2,3),a中的数据每一行都填充a原来的数据,也就是[1,2,3],然后再与b进行运算。
当然,在TensorFlow的Broadcasting机制运行过程中,上述操作只是理论的,并不会真正的将a的形状变成(2,2,3),更不会将每一行填充[1,2,3],只是虚拟进行操作,真正计算时,依旧是使用原来的张量a。这样做的好处是运算效率更高,也更节省内存。
5 范数
a = tf.constant([[1.,2.],[1.,2.]])
tf.norm(a,ord=1) # 1范数
<tf.Tensor: id=78, shape=(), dtype=float32, numpy=6.0>
tf.norm(a,ord=2) # 2范数
<tf.Tensor: id=83, shape=(), dtype=float32, numpy=3.1622777>
tf.norm(a) # ord不指定时,默认是2
<tf.Tensor: id=88, shape=(), dtype=float32, numpy=3.1622777>
指定维度求范数:
tf.norm(a,ord=2,axis=0)
<tf.Tensor: id=93, shape=(2,), dtype=float32, numpy=array([1.4142135, 2.828427 ], dtype=float32)>
tf.norm(a,ord=2,axis=1)
<tf.Tensor: id=98, shape=(2,), dtype=float32, numpy=array([2.236068, 2.236068], dtype=float32)>