TensorFlow 中的数据核心
由 TensorFlow 的名字我们就可以看出,其寓意表示张量(Tensor)流动(Flow)的意思。由此可见张量为 TensorFlow 的最核心的概念之一。 而这节课我们就来探究一下 TensorFlow 之中的数据核心概念:张量(Tensor)。
1. 什么是张量
“张量是具有统一类型(称为 dtype )的多维数组 ———— TensorFlow 官方定义”
在 TensorFlow 之中,几乎所有的数据都要转换为张量进行运算,因此我们需要对张量有一定的了解。
简单来说,我们可以将张量看作多维数组,因为是多维数组,因此可以包含三维或者三维以上的维度。就像一维数组可以理解为直线,二维数组可以理解为平面一样。
张量是 TensorFLow 之中以数据核心,张量的主要组成要素包括:
- 名称(name);
- 形状(shape);
- 类型(dtype)。
名称就是张量的唯一标识符,在 TensorFlow 中可以自动维护,我们一般不需要对名称进行操作。其余的两个概念我们可以通过以下例子来理解:
x = tf.ones((64, 28, 28, 3), dtype=tf.float32)
由此我们构建了一个四维张量,它的形状是(64, 28, 28, 3),其中它的第一维是 64 个维度,第二维与第三维都是28个维度,第四维是 3 个维度。而它的类型是我们所指定的 tf.float32 类型,也就是 32 位浮点类型。
2. 张量的运算
在 TensorFlow 之中,如果我们想指定一个张量,我们可以通过如下操作实现:
x = tf.constant([1, 3, 5])
由此我们指定了一个形状为(3),数据为[1, 3, 5]的一个张量,而默认的数据类型为int32 。
倘若我们需要指定全为 1 的张量,我们可以通过以下方式实现:
x1 = tf.ones([1, 3, 5])
相似的,如果我们需要指定全为 0 的向量,我们可以通过以下方式实现:
x2 = tf.zeros([1, 3, 5])
下面我们来看一下张量在TensorFlow中的基本运算。
2.1 加减乘除
在 TensorFlow 之中,我们可以使用内置的四则运算函数进行操作:
- tf.add:进行张量的加法;
- tf.subtract:进行张量的减法;
- tf.multiply:进行张量的乘法;
- tf.divid:进行张量的除法。
以乘法为例,我们可以看一下使用方法:
x1 = tf.constant([1, 3, 5])
x2 = tf.constant([1, 3, 5])
y = tf.multiply(x1, x2)
print(y)
我们可以得到如下结果:
tf.Tensor([ 1 9 25], shape=(3,), dtype=int32)
由此我们可以得到两个张量相乘的结果。
但是 TensorFlow 已经对四则运算进行了重载,因此我们可以直接使用 + - * / 符号进行运算,比如:
y = x1 * x2
也可以得到相同的结果。
2.2 对数运算与指数运算
对于指数运算与对数运算,我们可以采用以下的函数:
- tf.pow:实现指数运算,可以使用 ** 代替;
- tf.exp:实现自然指数运算;
- tf.math.log:实现自然对数运算,与 tf.exp 刚刚相反。
例如,我们可以使用以下例子来进行指数与对数运算:
x = tf.constant([1, 3, 5])
print(tf.pow(x, 3))
x = tf.exp(2)
print(x)
print(tf.math.log(x))
由此我们可以得到运算之后的结果输出:
tf.Tensor([ 1 27 125], shape=(3,), dtype=int32)
tf.Tensor(7.389056, shape=(), dtype=float32)
tf.Tensor(2.0, shape=(), dtype=float32)
2.3 使用张量进行矩阵运算
使用张量进行矩阵运算的条件为:
- 两个张量形状除了最后两个维度外的所有形状必须相同;
- 两个张量形状最后两个维度需要符合 a * b 与 b * c的的格式。
在 TensorFlow 之中我们可以通过tf.matmul函数进行运算,具体示例如下:
a = tf.random.normal([3,4,5])
b = tf.random.normal([3,5,6])
print(tf.matmul(a, b))
其中a与b是固定形状的随机张量,因为两者第一维形状相同,而最后两维形状符合矩阵相乘的格式,因此可以进行矩阵运算,得到的结果为:
tf.Tensor(
[[[-0.41255787 0.2539668 -0.70357645 0.02980493 0.5546258
0.5286447 ]
[ 0.7544514 1.2061275 -0.8299564 -0.61776394 -2.0845695
0.55285174]
[ 4.9162273 0.23087293 0.6157658 -0.3430875 -3.9511528
0.2734207 ]
[-0.8638447 -0.48060232 -1.4220456 0.35801342 2.505946
2.7356615 ]]
[[ 2.260117 2.338372 -3.4372165 -0.2901526 0.12752411
-0.23638 ]
[ 0.14264594 -1.9469845 -5.1910253 2.5343626 -4.1282463
1.295904 ]
[ 0.5720302 1.6685274 2.1391735 -1.8491768 2.8305507
-1.1663995 ]
[-0.8750653 -3.5349839 -2.7755249 2.5702014 -3.525653
0.08906344]]
[[ 0.04434888 2.0841029 0.06953187 -2.3450966 -1.5517069
0.83987266]
[ 2.0700073 1.5478165 -0.07335746 -0.36860508 0.46835172
1.861287 ]
[-3.5253298 -1.5082629 -1.6806324 -1.2718723 -1.378425
-1.1990058 ]
[ 0.88312423 1.0631399 2.6772838 -1.0774231 -1.8299285
0.89358884]]], shape=(3, 4, 6), dtype=float32)
可以看到,我们的张量已经进行了矩阵的运算,并切形状为我们期待的结果。
3. 张量的梯度
在机器学习中我们最经常使用的就是张量的梯度了,张量的梯度可以理解为张量的微分。因为在机器学习之中我们需要不断地计算参数的导数来计算出参数的误差,从而对参数进行调整。
在 TensorFlow 之中,计算张量的梯度是通过 tf.tf.GradientTape 来进行的,具体来说分为两步:
- 在梯度带中定义运算;
- 运算结束后从梯度带中得到梯度。
下面我们以一个简单的例子来展示一下如何求得梯度:
x = tf.Variable(5.0)
with tf.GradientTape() as tape:
y = x**2
dy_dx = tape.gradient(y, x)
print(dy_dx.numpy())
在上面的例子之中,我们首先定义了一个常量 x,然后我们在梯度带中定义 y 为 x 的平方,然后我们便在记录的梯度带中求得 y 对于 x 的梯度,在这里就相当于导数。
因为 x * *2 的导数为 2 * x,因此我们得到的梯度应该是 2 * 5 = 10。
我们查看输出,果然结果是6。
10.0
上面我们是通过一个常量来进行的演示,其实梯度可以应用于任意的张量,下面我们以一个张量来进行演示:
a = tf.Variable(tf.random.normal((3, 2)), name='a')
b = tf.Variable(tf.zeros(2), name='b')
x = [[1., 2., 3.]]
with tf.GradientTape(persistent=True) as tape:
y = tf.matmul(x, a) + b
loss = tf.reduce_mean(y**2) # 计算y**2的平均值
[dl_da, dl_db] = tape.gradient(loss, [a, b])
print(a)
print(dl_da, dl_db)
在这里我们定义了 a 和 b 两个张量,并定义了一个常数 x;然后我们在梯度带中计算了 y = a * x + b,然后又计算了 y 平方的平均值(赋值为 loss );计算结束后我们计算了 loss 对于 a 和 b 的梯度,并将其输出。
我们可以得到以下结果,可以看到 TensorFlow 已经帮助我们计算了梯度:
<tf.Variable 'a:0' shape=(3, 2) dtype=float32, numpy=
array([[-0.16581778, -0.5726858 ],
[ 0.65533674, 0.8761684 ],
[ 0.7585775 , -1.0951476 ]], dtype=float32)>
tf.Tensor(
[[ 3.4205883 -2.1057918]
[ 6.8411765 -4.2115836]
[10.261765 -6.317375 ]], shape=(3, 2), dtype=float32) tf.Tensor([ 3.4205883 -2.1057918], shape=(2,), dtype=float32)
4. 小结
在该节之中,我们了解到了 TensorFlow 之中最基本的数据核心是张量,然后我们学习了张量的四则运算以及指数对数运算,最后我们学习了如何对张量进行梯度求解,而这将会是日后继续研究的基础。