TensorFlow 中的數(shù)據(jù)核心
由 TensorFlow 的名字我們就可以看出,其寓意表示張量(Tensor)流動(dòng)(Flow)的意思。由此可見張量為 TensorFlow 的最核心的概念之一。 而這節(jié)課我們就來(lái)探究一下 TensorFlow 之中的數(shù)據(jù)核心概念:張量(Tensor)。
1. 什么是張量
“張量是具有統(tǒng)一類型(稱為 dtype )的多維數(shù)組 ———— TensorFlow 官方定義”
在 TensorFlow 之中,幾乎所有的數(shù)據(jù)都要轉(zhuǎn)換為張量進(jìn)行運(yùn)算,因此我們需要對(duì)張量有一定的了解。
簡(jiǎn)單來(lái)說(shuō),我們可以將張量看作多維數(shù)組,因?yàn)槭嵌嗑S數(shù)組,因此可以包含三維或者三維以上的維度。就像一維數(shù)組可以理解為直線,二維數(shù)組可以理解為平面一樣。
張量是 TensorFLow 之中以數(shù)據(jù)核心,張量的主要組成要素包括:
- 名稱(name);
- 形狀(shape);
- 類型(dtype)。
名稱就是張量的唯一標(biāo)識(shí)符,在 TensorFlow 中可以自動(dòng)維護(hù),我們一般不需要對(duì)名稱進(jìn)行操作。其余的兩個(gè)概念我們可以通過(guò)以下例子來(lái)理解:
x = tf.ones((64, 28, 28, 3), dtype=tf.float32)
由此我們構(gòu)建了一個(gè)四維張量,它的形狀是(64, 28, 28, 3),其中它的第一維是 64 個(gè)維度,第二維與第三維都是28個(gè)維度,第四維是 3 個(gè)維度。而它的類型是我們所指定的 tf.float32 類型,也就是 32 位浮點(diǎn)類型。
2. 張量的運(yùn)算
在 TensorFlow 之中,如果我們想指定一個(gè)張量,我們可以通過(guò)如下操作實(shí)現(xiàn):
x = tf.constant([1, 3, 5])
由此我們指定了一個(gè)形狀為(3),數(shù)據(jù)為[1, 3, 5]的一個(gè)張量,而默認(rèn)的數(shù)據(jù)類型為int32 。
倘若我們需要指定全為 1 的張量,我們可以通過(guò)以下方式實(shí)現(xiàn):
x1 = tf.ones([1, 3, 5])
相似的,如果我們需要指定全為 0 的向量,我們可以通過(guò)以下方式實(shí)現(xiàn):
x2 = tf.zeros([1, 3, 5])
下面我們來(lái)看一下張量在TensorFlow中的基本運(yùn)算。
2.1 加減乘除
在 TensorFlow 之中,我們可以使用內(nèi)置的四則運(yùn)算函數(shù)進(jìn)行操作:
- tf.add:進(jìn)行張量的加法;
- tf.subtract:進(jìn)行張量的減法;
- tf.multiply:進(jìn)行張量的乘法;
- tf.divid:進(jìn)行張量的除法。
以乘法為例,我們可以看一下使用方法:
x1 = tf.constant([1, 3, 5])
x2 = tf.constant([1, 3, 5])
y = tf.multiply(x1, x2)
print(y)
我們可以得到如下結(jié)果:
tf.Tensor([ 1 9 25], shape=(3,), dtype=int32)
由此我們可以得到兩個(gè)張量相乘的結(jié)果。
但是 TensorFlow 已經(jīng)對(duì)四則運(yùn)算進(jìn)行了重載,因此我們可以直接使用 + - * / 符號(hào)進(jìn)行運(yùn)算,比如:
y = x1 * x2
也可以得到相同的結(jié)果。
2.2 對(duì)數(shù)運(yùn)算與指數(shù)運(yùn)算
對(duì)于指數(shù)運(yùn)算與對(duì)數(shù)運(yùn)算,我們可以采用以下的函數(shù):
- tf.pow:實(shí)現(xiàn)指數(shù)運(yùn)算,可以使用 ** 代替;
- tf.exp:實(shí)現(xiàn)自然指數(shù)運(yùn)算;
- tf.math.log:實(shí)現(xiàn)自然對(duì)數(shù)運(yùn)算,與 tf.exp 剛剛相反。
例如,我們可以使用以下例子來(lái)進(jìn)行指數(shù)與對(duì)數(shù)運(yùn)算:
x = tf.constant([1, 3, 5])
print(tf.pow(x, 3))
x = tf.exp(2)
print(x)
print(tf.math.log(x))
由此我們可以得到運(yùn)算之后的結(jié)果輸出:
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 使用張量進(jìn)行矩陣運(yùn)算
使用張量進(jìn)行矩陣運(yùn)算的條件為:
- 兩個(gè)張量形狀除了最后兩個(gè)維度外的所有形狀必須相同;
- 兩個(gè)張量形狀最后兩個(gè)維度需要符合 a * b 與 b * c的的格式。
在 TensorFlow 之中我們可以通過(guò)tf.matmul函數(shù)進(jìn)行運(yùn)算,具體示例如下:
a = tf.random.normal([3,4,5])
b = tf.random.normal([3,5,6])
print(tf.matmul(a, b))
其中a與b是固定形狀的隨機(jī)張量,因?yàn)閮烧叩谝痪S形狀相同,而最后兩維形狀符合矩陣相乘的格式,因此可以進(jìn)行矩陣運(yùn)算,得到的結(jié)果為:
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)
可以看到,我們的張量已經(jīng)進(jìn)行了矩陣的運(yùn)算,并切形狀為我們期待的結(jié)果。
3. 張量的梯度
在機(jī)器學(xué)習(xí)中我們最經(jīng)常使用的就是張量的梯度了,張量的梯度可以理解為張量的微分。因?yàn)樵跈C(jī)器學(xué)習(xí)之中我們需要不斷地計(jì)算參數(shù)的導(dǎo)數(shù)來(lái)計(jì)算出參數(shù)的誤差,從而對(duì)參數(shù)進(jìn)行調(diào)整。
在 TensorFlow 之中,計(jì)算張量的梯度是通過(guò) tf.tf.GradientTape 來(lái)進(jìn)行的,具體來(lái)說(shuō)分為兩步:
- 在梯度帶中定義運(yùn)算;
- 運(yùn)算結(jié)束后從梯度帶中得到梯度。
下面我們以一個(gè)簡(jiǎn)單的例子來(lái)展示一下如何求得梯度:
x = tf.Variable(5.0)
with tf.GradientTape() as tape:
y = x**2
dy_dx = tape.gradient(y, x)
print(dy_dx.numpy())
在上面的例子之中,我們首先定義了一個(gè)常量 x,然后我們?cè)谔荻葞е卸x y 為 x 的平方,然后我們便在記錄的梯度帶中求得 y 對(duì)于 x 的梯度,在這里就相當(dāng)于導(dǎo)數(shù)。
因?yàn)?x * *2 的導(dǎo)數(shù)為 2 * x,因此我們得到的梯度應(yīng)該是 2 * 5 = 10。
我們查看輸出,果然結(jié)果是6。
10.0
上面我們是通過(guò)一個(gè)常量來(lái)進(jìn)行的演示,其實(shí)梯度可以應(yīng)用于任意的張量,下面我們以一個(gè)張量來(lái)進(jìn)行演示:
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) # 計(jì)算y**2的平均值
[dl_da, dl_db] = tape.gradient(loss, [a, b])
print(a)
print(dl_da, dl_db)
在這里我們定義了 a 和 b 兩個(gè)張量,并定義了一個(gè)常數(shù) x;然后我們?cè)谔荻葞е杏?jì)算了 y = a * x + b,然后又計(jì)算了 y 平方的平均值(賦值為 loss );計(jì)算結(jié)束后我們計(jì)算了 loss 對(duì)于 a 和 b 的梯度,并將其輸出。
我們可以得到以下結(jié)果,可以看到 TensorFlow 已經(jīng)幫助我們計(jì)算了梯度:
<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. 小結(jié)
在該節(jié)之中,我們了解到了 TensorFlow 之中最基本的數(shù)據(jù)核心是張量,然后我們學(xué)習(xí)了張量的四則運(yùn)算以及指數(shù)對(duì)數(shù)運(yùn)算,最后我們學(xué)習(xí)了如何對(duì)張量進(jìn)行梯度求解,而這將會(huì)是日后繼續(xù)研究的基礎(chǔ)。