TensorFlow之張量

不該相遇在秋天發表於2020-09-22

張量的概念

TensorFlow中的Tensor就是張量,張量是數學物件,是對標量、向量、矩陣的泛化。我們可以直接理解成張量就是列表,就是多維陣列。

 

張量的維數用階來表示:

0階張量 標量 單個值 例:a = 1
1階張量 向量 1維陣列 例:a = [1,2,3]
2階張量 矩陣 2維陣列 例:a = [[1,2,3],[4,5,6]]
n階張量 張量 n維陣列 例:a = [[[[[...n個括號...]]]]]

 

判斷張量有幾階  就看等號右邊的方括號有幾個  0個就是0階 2個就是2階。

多維張量在記憶體中也是以一維陣列的方式連續儲存的。

 

建立張量

tf.constant()方法用來建立一個張量,傳入內容,可以是數字、python列表、NumPy陣列,第二個引數為指定資料型別,可以省略。

如:

import tensorflow as tf
a = tf.constant([1,2,3],dtype=tf.int32)
print(a)

列印結果:

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

 

shape是張量的形狀,一個張量有多少階,就看形狀中的逗號隔開了幾個數字,比如形狀為(3,)的張量 逗號隔開了3這一個數字,所以它的階數是1階,裡面有3個元素, 再比如形狀為(3,3)的張量  逗號隔開了3和3兩個數字,所以它的階數是2階,並且是一個3行3列的矩陣。

當然,也可以以屬性的方式來檢視張量資訊

print('張量的階數:', a.ndim)
print('張量的形狀:', a.shape)
print('張量的型別:', a.dtype)

列印結果:

張量的階數: 1
張量的形狀: (3,)
張量的型別: <dtype: 'int32'>

 

張量的資料型別

在上面建立張量我加了dtype引數指定資料型別為int32,不指定資料型別也是一樣的,tensorflow會根據張量內容自動推斷型別,如果張量內容是數字,那麼預設資料型別是int32,如果張量內容是浮點數,那麼預設資料型別是float32,如果需要指定其他位數如int64或float64就需要傳遞引數手動指定.

張量的資料型別有:

int 有符號整數
float 有符號浮點數
uint 無符號整數
以上這三種都有位數的劃分,比如int有 tf.int8、tf.int16、tf.int32、tf.int64。

string 字串
bool 布林型
complex 複數 由實數和虛陣列成

 

tf.cast()方法可以將張量的資料型別進行轉換,比如:

import tensorflow as tf
a = tf.constant([1,2,3],dtype=tf.int32)
print(a)

b = tf.cast(a,dtype=tf.int64)
print(b)

列印結果:

tf.Tensor([1 2 3], shape=(3,), dtype=int32)
tf.Tensor([1 2 3], shape=(3,), dtype=int64)

 

NumPy資料轉換

如果資料是由numpy建立的話,需要使用tf.convert_to_tensor()函式將numpy的資料型別轉換成TensorFlow的張量資料型別。

import tensorflow as tf
import numpy as np
a = np.arange(0,10)
print(a)
b = tf.convert_to_tensor(a)
print(b)

列印結果:

[0 1 2 3 4 5 6 7 8 9]
tf.Tensor([0 1 2 3 4 5 6 7 8 9], shape=(10,), dtype=int32)

 

注意一點的是,numpy建立浮點數矩陣的時候預設是float64型別,轉換的時候tensorflow同樣會去接收它的資料型別,因此由numpy資料生成的張量會出現預設浮點數型別是float64位的情況,如下:

import tensorflow as tf
import numpy as np
a = np.array([1.1,2.2,3.3])
print(a)
b = tf.constant(a)
print(b)
c = tf.convert_to_tensor(a)
print(c)

列印結果:

[1.1 2.2 3.3]
tf.Tensor([1.1 2.2 3.3], shape=(3,), dtype=float64)
tf.Tensor([1.1 2.2 3.3], shape=(3,), dtype=float64)

 

大多數機器學習場景下我們用32位浮點數已經足夠滿足計算需求,32位浮點數的運算比64位浮點數的運算要快的多,因此用numpy陣列生成張量時可以顯式的指定資料型別:

import tensorflow as tf
import numpy as np
a = np.array([1.1,2.2,3.3])
print(a)
b = tf.constant(a,dtype=tf.float32)
print(b)
c = tf.convert_to_tensor(a,dtype=tf.float32)
print(c)

列印結果:

[1.1 2.2 3.3]
tf.Tensor([1.1 2.2 3.3], shape=(3,), dtype=float32)
tf.Tensor([1.1 2.2 3.3], shape=(3,), dtype=float32)

 

固定張量

TensorFlow提供了一些建立固定張量的函式,比如建立值全是0的張量,建立值全是1的張量。

全0張量

 tf.zeros(shape,dtype=float32)

引數的寫法不止一種,記一種好記的就可以了,比如一維直接寫個數,二維寫[行數,列數],三維寫[行數,列數,高度],多維以此類推 [n,m,k,j,......]

#建立一維張量 4個元素
a = tf.zeros(4)
print(a)

#建立一維張量 4個元素
aa = tf.zeros([4])
print(aa)

#建立3行3列的二維張量
b = tf.zeros([3,3])
print(b)

#建立3行3列的二維張量
bb = tf.zeros((3,3))
print(bb)

#指定型別
c = tf.zeros([3,3],tf.int8)
print(c)

 

全1張量

 tf.ones(shape,dtype=float32)

建立值全是1的張量,除了將zeros函式替換成ones函式之外,建立用法和全0張量完全一致。

 

元素值相同的張量

tf.fill(shape,value)

fill函式沒有dtype引數,它根據value引數自動判斷型別。

#建立一維張量 4個元素
aa = tf.fill([4],10)
print(aa)

列印結果:

tf.Tensor([10 10 10 10], shape=(4,), dtype=int32)

 

#建立3行3列的二維張量
c = tf.fill([3,3],4.4)
print(c)

列印結果:

tf.Tensor(
[[4.4 4.4 4.4]
[4.4 4.4 4.4]
[4.4 4.4 4.4]], shape=(3, 3), dtype=float32)

 

tf.fill()方法完全可以讓tf.constant()來代替。

 

隨機數張量

正態分佈

函式簽名:tf.random.normal(shape,mean,stddev,dtype)

shape:張量形狀
mean:均值 預設為0
stddev:標準差 預設為1
dtype:儲存型別 預設為float32

 

示例:

#建立2行2列均值為0標準差為1的隨機數張量
a = tf.random.normal([2, 2], mean=0, stddev=1)
print(a)

列印結果:

tf.Tensor(
[[-0.849204 0.42851505]
[-2.1599882 -0.6131579 ]], shape=(2, 2), dtype=float32)

 

截斷正態分佈

函式簽名:tf.random.truncated_normal(shape,mean,stddev,dtype)

本函式返回一個截斷的正態分佈,截斷的標準是2倍的標準差,可以保證生成的資料更向均值集中。

 

示例:

b = tf.random.truncated_normal([2, 2], mean=0, stddev=1)
print(b)

列印結果:

tf.Tensor(
[[-0.06899758 1.8015716 ]
[-0.39744875 0.00498725]], shape=(2, 2), dtype=float32)

 

通俗的說,就是當均值為0,標準差為1時,使用本函式建立出的隨機張量中,不可能出現區間[-2,2]以外的點, 而使用tf.random.normal函式則可能出現區間[-2,2]以外的點。

 

設定隨機種子

如果想要保證多次生成的隨機數張量完全一致,可以設定相同的隨機種子。

tf.random.set_seed(10)
a = tf.random.normal([1,5])
print(a)

tf.random.set_seed(10)
b = tf.random.normal([1,5])
print(b)

列印結果:

tf.Tensor([[-0.8757808 0.3356369 -0.35219625 -0.30314562 -0.03882965]], shape=(1, 5), dtype=float32)
tf.Tensor([[-0.8757808 0.3356369 -0.35219625 -0.30314562 -0.03882965]], shape=(1, 5), dtype=float32)

 

均勻分佈

函式簽名:tf.random.uniform(shape,minval,maxval,dtype)

shape:張量形狀
minval:最小值 閉區間
maxval:最大值 開區間
dtype:資料型別 預設float32

c = tf.random.uniform([2, 2], minval=0, maxval=1)
print(c)

列印結果:

tf.Tensor(
[[0.46760368 0.58308244]
[0.5945486 0.2994659 ]], shape=(2, 2), dtype=float32)

 

序列張量

函式簽名:tf.range(start,limit,delta,dtype)

start:起始數字 閉區間
limit:結束數字 開區間
delta:步長 預設1
dtype:資料型別 預設自動推斷

 

用法如下:

#建立0-9序列
a = tf.range(10)
print(a)

列印結果:

tf.Tensor([0 1 2 3 4 5 6 7 8 9], shape=(10,), dtype=int32)

 

#建立偶數序列
b = tf.range(10,delta=2)
print(b)

列印結果:

tf.Tensor([0 2 4 6 8], shape=(5,), dtype=int32)

 

#建立奇數序列
c = tf.range(1,10,delta=2)
print(c)

列印結果:

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

 

張量的運算

四則運算

注意,只有階數相同的張量才可以做四則運算

 

加法:tf.add

a = tf.constant([1,2,3])
b = tf.constant([1,2,3])
print(tf.add(a,b))

列印結果:

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

 

減法:tf.subtract

a = tf.constant([2,4,6])
b = tf.constant([0,0,3])
print(tf.subtract(a,b))

列印結果:

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

 

乘法:ttf.multiply

a = tf.constant([2,4,6])
b = tf.constant([0,0,3])
print(tf.multiply(a,b))

列印結果:

tf.Tensor([ 0  0 18], shape=(3,), dtype=int32)

 

除法:tf.divide

a = tf.constant([2,4,6])
b = tf.constant([0,0,3])
print(tf.divide(b,a))

列印結果:

tf.Tensor([0.  0.  0.5], shape=(3,), dtype=float64)

 

平方、次方與開方

對張量求平方:tf.square

a = tf.constant([1,2,3,4,5])
print(tf.square(a))

列印結果:

tf.Tensor([ 1  4  9 16 25], shape=(5,), dtype=int32)

 

對張量求次方:tf.pow

a = tf.range(5) #建立一個序列張量 0 1 2 3 4
b = tf.pow(a,3) #求序列張量的3次方
print(b)

列印結果:

tf.Tensor([ 0  1  8 27 64], shape=(5,), dtype=int32)

 

如果說求a張量的b張量次方,那麼就等於是將a張量的每個位置的元素求b張量中對應元素的次方。

a = tf.constant([[2,2],[2,2]])
b = tf.constant([[1,2],[3,4]])
print(tf.pow(a,b))

列印結果:

tf.Tensor(
[[ 2 4]
[ 8 16]], shape=(2, 2), dtype=int32)

 

如果指數是分數,那麼就是根號運算。比如指數是0.5的話,那麼就是對張量逐元素求平方根。

a = tf.constant([1,4,9,16,25],dtype=tf.float32)
print(tf.pow(a,0.5))

列印結果:

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

開根號會涉及小數點,因此張量元素型別必須是浮點數型別

 

對張量求平方根:tf.sqrt

a = tf.constant([1,4,9,16,25],dtype=tf.float64)
print(tf.sqrt(a))

列印結果:

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

 

其他運算

除了常用的運算之外,tensorflow還提供了其他更廣泛的運算。

tf.sign(x) 取符號
tf.abs(x) 求絕對值
tf.negative(x) 求相反數
tf.reciprocal(x) 求倒數
tf.ceil(x) 向上取整
tf.floor(x) 向下取整
tf.round(x) 四捨五入取整
tf.maximum(x,y)返回兩個張量中的最大值 (逐個位置比較兩張量的元素,取最大值,最終返回一個新張量)
tf.minimum(x,y)返回兩個張量中的最小值 (逐個位置比較兩張量的元素,取最小值,最終返回一個新張量)

 

過載運算子

為了使用簡便,tensorflow將常用的數學運算子號進行了過載,讓我們可以對張量進行直接運算而不用顯式呼叫函式進行運算。

例如加法運算,可以直接把tf.add(a,b)寫成a+b

a = tf.constant([1,2,3])
b = tf.constant([1,2,3])
print(tf.add(a,b))
print(a+b)

列印結果:

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

 

矩陣乘法

矩陣乘法是線性代數中很重要的內容,矩陣乘法的條件是第一個矩陣的列數要等於第二個矩陣的行數才可以進行乘法計算。

計算規則是,第一個矩陣的每行的每個數字與第二個矩陣的每列的每個數字對應相乘再相加,計算結果是第一個矩陣的行數第二個矩陣的列數所對應的值。

 

比如矩陣A:
2 3
4 5

矩陣B:
1 2
3 4

AB兩矩陣相乘,得到新矩陣C
C11:2*1 + 3*3 = 11
C12:2*2 + 3*4 = 16
C21:4*1 + 5*3 = 19
C22:4*2 + 5*4 = 28

也就是:
11 16
19 28

 

可以用tf.matmul(a,b)函式來計算矩陣乘積,也可以用@運算子來進行運算。

a = tf.constant([[2,3],[4,5]])
print(a)
b = tf.constant([[1,2],[3,4]])
print(b)
print(tf.matmul(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(
[[11 16]
[19 28]], shape=(2, 2), dtype=int32)

 

張量的統計

常用統計函式有如下四種:

tf.reduce_sum(tensor,axis) 求和
tf.reduce_mean(tensor,axis) 求平均值
tf.reduce_max(tensor,axis) 求最大值
tf.reduce_min(tensor,axis) 求最小值

引數axis為0時,代表縱向操作,沿經度方向(也就是按列、跨行),axis為1時,代表橫向操作,沿緯度方向(也就是按行、跨列),不指定axis則代表全域性操作。

a = tf.constant([[5,3,1],[2,4,6]])
print(a)

print('縱向操作:{}'.format(tf.reduce_sum(a,0)))
print('橫向操作:{}'.format(tf.reduce_sum(a,1)))
print('全域性操作:{}'.format(tf.reduce_sum(a)))

列印結果:

tf.Tensor(
[[5 3 1]
[2 4 6]], shape=(2, 3), dtype=int32)
縱向操作:[7 7 7]
橫向操作:[ 9 12]
全域性操作:21

 

求平均值需要提一下,求出的結果資料型別與原始張量的資料型別會保持一致,也就會出現如下現象:

a = tf.constant([[5,3,1],[2,4,6]])
print(a)
print(tf.reduce_mean(a,0))

列印結果:

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

縱向求平均值第一列5+2=7,除以2得3.5,但是受限於int資料型別,所以最後的值為3

為了解決這個問題,我們可以將張量的資料型別轉換成float再進行統計即可。

a = tf.constant([[5,3,1],[2,4,6]])
print(a)
print(tf.reduce_mean(tf.cast(a,dtype=tf.float32),0))

列印結果:

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

 

相關文章