TensorFlow2.0(1):基本數據結構——張量

 

 

 

注:本系列全部博客將持續更新併發布在github上,您能夠經過github下載本系列全部文章筆記文件。javascript

 

1引言

 

TensorFlow2.0版本已經發布,雖然不是正式版,但預覽版都發布了,正式版還會遠嗎?相比於1.X,2.0版的TensorFlow修改的不是一點半點,這些修改極大的彌補了1.X版本的反人類設計,提高了框架的總體易用性,絕對好評!css

趕忙來學習一波吧,作最早吃螃蟹的那一批人!先從TensorFlow的基本數據結構——張量(tensor)開始。html

 

2 建立

 

2.1 constant()方法

In [1]:
import tensorflow as tf
In [2]:
tf.constant(1)  # 建立一個整型張量
Out[2]:
<tf.Tensor: id=0, shape=(), dtype=int32, numpy=1>
In [3]:
tf.constant(1.)  # 建立一個浮點型張量
Out[3]:
<tf.Tensor: id=2, shape=(), dtype=float32, numpy=1.0>
In [4]:
tf.constant(2., dtype=tf.double)  # 建立的同時指定數據類型
Out[4]:
<tf.Tensor: id=4, shape=(), dtype=float64, numpy=2.0>
In [5]:
tf.constant([[1.,2.,3.],[4.,5.,6.]])  # 經過傳入一個list參數建立
Out[5]:
<tf.Tensor: id=6, shape=(2, 3), dtype=float32, numpy=
array([[1., 2., 3.],
       [4., 5., 6.]], dtype=float32)>
 

若是輸入的數據與指定的數據類型不相符,會產生如下異常:
TypeError: Cannot convert provided value to EagerTensor. Provided value: 2.1 Requested dtype: int32html5

 

2.2 convert_to_tensor()方法

In [9]:
import numpy as np
In [10]:
tf.convert_to_tensor(np.ones([2, 3]))
Out[10]:
<tf.Tensor: id=9, shape=(2, 3), dtype=float64, numpy=
array([[1., 1., 1.],
       [1., 1., 1.]])>
In [11]:
tf.convert_to_tensor(np.ones([2, 3]))
Out[11]:
<tf.Tensor: id=11, shape=(2, 3), dtype=float64, numpy=
array([[1., 1., 1.],
       [1., 1., 1.]])>
In [12]:
tf.convert_to_tensor([[2.,3.],[3., 4.]])
Out[12]:
<tf.Tensor: id=13, shape=(2, 2), dtype=float32, numpy=
array([[2., 3.],
       [3., 4.]], dtype=float32)>
 

2.3 建立元素爲指定值的tensor

 

若是你熟悉numpy建立數組的方法,你必定見過zeros()、ones()等方法,TensorFlow中也有這些方法。java

 

(1)zeros()與ones()node

In [24]:
a = tf.zeros([2, 3, 3])  # 建立一個元素全爲0,形狀爲[2, 3, 3]的tensor
In [25]:
a
Out[25]:
<tf.Tensor: id=46, shape=(2, 3, 3), dtype=float32, numpy=
array([[[0., 0., 0.],
        [0., 0., 0.],
        [0., 0., 0.]],

       [[0., 0., 0.],
        [0., 0., 0.],
        [0., 0., 0.]]], dtype=float32)>
In [26]:
b = tf.ones([2, 3])  #  建立一個元素全爲1,形狀爲[2, 3]的tensor
In [27]:
b
Out[27]:
<tf.Tensor: id=50, shape=(2, 3), dtype=float32, numpy=
array([[1., 1., 1.],
       [1., 1., 1.]], dtype=float32)>
 

(2)zeros_like()與ones_likepython

In [28]:
tf.zeros_like(b)  # 仿照b的shape建立一個全爲0的tensor
Out[28]:
<tf.Tensor: id=52, shape=(2, 3), dtype=float32, numpy=
array([[0., 0., 0.],
       [0., 0., 0.]], dtype=float32)>
In [29]:
tf.ones_like(a)  # 仿照b的shape建立一個全爲1的tensor
Out[29]:
<tf.Tensor: id=56, shape=(2, 3, 3), dtype=float32, numpy=
array([[[1., 1., 1.],
        [1., 1., 1.],
        [1., 1., 1.]],

       [[1., 1., 1.],
        [1., 1., 1.],
        [1., 1., 1.]]], dtype=float32)>
 

(3)fill()jquery

In [21]:
tf.fill([2,3],5)  # 建立元素全爲5,形狀爲[2,3]的tensor
Out[21]:
<tf.Tensor: id=38, shape=(2, 3), dtype=int32, numpy=
array([[5, 5, 5],
       [5, 5, 5]])>
 

2.4 隨機初始化

 

在實際應用中,常常須要隨機初始化元素服從某種分佈的tensor,TensorFlow中也提供了這種功能。linux

(1)從指定正態分佈中隨機取值:tf.random.normal()。例如,隨機初始化一個元素服從均值爲1,方差爲1的正態分佈且形狀爲[2, 3]的tensor:android

In [30]:
tf.random.normal([2, 3], mean=1, stddev=1) 
Out[30]:
<tf.Tensor: id=63, shape=(2, 3), dtype=float32, numpy=
array([[ 1.7034731 ,  0.4979009 ,  1.4266468 ],
       [-0.33414853,  0.2618034 ,  0.3966313 ]], dtype=float32)>
 

(2)從指定的截斷正態分佈中隨機取值:truncated_normal()。意思是從指定的正太分佈中取值,可是取值範圍在兩個標準差範圍內,也就是:[ mean - 2 stddev, mean + 2 stddev ]

In [31]:
tf.random.truncated_normal([2, 3], mean=1, stddev=1)
Out[31]:
<tf.Tensor: id=70, shape=(2, 3), dtype=float32, numpy=
array([[0.71736836, 1.7930655 , 0.47575486],
       [0.83504593, 0.7969478 , 0.6002228 ]], dtype=float32)>
 

(3)從指定均勻分佈中隨機取值:tf.random.uniform()。

In [32]:
tf.random.uniform([2, 3], minval=1, maxval=2) # 在1~2之間均勻分佈
Out[32]:
<tf.Tensor: id=78, shape=(2, 3), dtype=float32, numpy=
array([[1.7117869, 1.2625391, 1.6652637],
       [1.3810604, 1.0297629, 1.1268978]], dtype=float32)>
 

3 索引

In [33]:
a = tf.convert_to_tensor(np.arange(80).reshape(2,2,4,5))
In [34]:
a
Out[34]:
<tf.Tensor: id=80, shape=(2, 2, 4, 5), dtype=int32, 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, 24],
         [25, 26, 27, 28, 29],
         [30, 31, 32, 33, 34],
         [35, 36, 37, 38, 39]]],


       [[[40, 41, 42, 43, 44],
         [45, 46, 47, 48, 49],
         [50, 51, 52, 53, 54],
         [55, 56, 57, 58, 59]],

        [[60, 61, 62, 63, 64],
         [65, 66, 67, 68, 69],
         [70, 71, 72, 73, 74],
         [75, 76, 77, 78, 79]]]])>
 

3.1 基礎索引

 

TensorFlow支持Python原生的基礎索引方式,即多個方括號逐步索引取值:[idx][idx][idx],每一個方括號對應一個維度。

In [35]:
a[0]  # 取第一個維度
Out[35]:
<tf.Tensor: id=85, shape=(2, 4, 5), dtype=int32, 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, 24],
        [25, 26, 27, 28, 29],
        [30, 31, 32, 33, 34],
        [35, 36, 37, 38, 39]]])>
In [36]:
a[0][1]  # 同時篩選兩個維度
Out[36]:
<tf.Tensor: id=94, shape=(4, 5), dtype=int32, numpy=
array([[20, 21, 22, 23, 24],
       [25, 26, 27, 28, 29],
       [30, 31, 32, 33, 34],
       [35, 36, 37, 38, 39]])>
In [37]:
a[0][1][3][3]  # 同時對4個維度進行篩選
Out[37]:
<tf.Tensor: id=111, shape=(), dtype=int32, numpy=38>
 

這種索引數據的方法簡單,易於理解,可是可讀性差,只能按維度依次索引數據,也不能索引列。

 

3.2 numpy索引

 

TensorFlow也繼承了numpy中的部分索引方式,若是對numpy索引方式不熟悉,能夠查看個人前幾篇博客
(1)[idx1, idx2, idx3]
這種索引方式是在一個方括號內寫下全部的索引,每一個索引序號之間用逗號隔開。

In [38]:
a[1]  # 篩選第一維度,這跟基礎索引同樣
Out[38]:
<tf.Tensor: id=116, shape=(2, 4, 5), dtype=int32, numpy=
array([[[40, 41, 42, 43, 44],
        [45, 46, 47, 48, 49],
        [50, 51, 52, 53, 54],
        [55, 56, 57, 58, 59]],

       [[60, 61, 62, 63, 64],
        [65, 66, 67, 68, 69],
        [70, 71, 72, 73, 74],
        [75, 76, 77, 78, 79]]])>
In [39]:
a[1,1, 3]  # 同時帥選3個維度
Out[39]:
<tf.Tensor: id=121, shape=(5,), dtype=int32, numpy=array([75, 76, 77, 78, 79])>
 

(2)冒號切片與步長:[start:end:step]

 

這種索引方式在Python原生的list類型中也是常見的,並且使用方法也是同樣的。

In [40]:
a[1,:,0:2] # 對第1維度選第二塊數據,對第二維度選全部數據,對第三維度選前兩行
Out[40]:
<tf.Tensor: id=126, shape=(2, 2, 5), dtype=int32, numpy=
array([[[40, 41, 42, 43, 44],
        [45, 46, 47, 48, 49]],

       [[60, 61, 62, 63, 64],
        [65, 66, 67, 68, 69]]])>
In [41]:
a[1,:,0:2,0:4] # 繼續上面的例子,對第4維度篩選去前4列
Out[41]:
<tf.Tensor: id=131, shape=(2, 2, 4), dtype=int32, numpy=
array([[[40, 41, 42, 43],
        [45, 46, 47, 48]],

       [[60, 61, 62, 63],
        [65, 66, 67, 68]]])>
In [42]:
a[1,:,0:2,0:4:2] # 對第4維度加上步長,每隔一個數據取一次
Out[42]:
<tf.Tensor: id=136, shape=(2, 2, 2), dtype=int32, numpy=
array([[[40, 42],
        [45, 47]],

       [[60, 62],
        [65, 67]]])>
 

也可使用負值步長表示逆序索引,但要注意,負數步長時,本來的[start : end : step]也要跟着編程[end : start : step]:

In [43]:
a[1,:,0:2,4:0:-1]
Out[43]:
<tf.Tensor: id=141, shape=(2, 2, 4), dtype=int32, numpy=
array([[[44, 43, 42, 41],
        [49, 48, 47, 46]],

       [[64, 63, 62, 61],
        [69, 68, 67, 66]]])>
In [44]:
a[1,:,0:2,4:0:-2]
Out[44]:
<tf.Tensor: id=146, shape=(2, 2, 2), dtype=int32, numpy=
array([[[44, 42],
        [49, 47]],

       [[64, 62],
        [69, 67]]])>
 

在numpy和TensorFlow中還有「..."(三個英文句號)的使用,「..."用於表示連續多個維度全選:

In [45]:
a[1,...,0:4] # 等同於a[1, : , : ,0:4]
Out[45]:
<tf.Tensor: id=151, shape=(2, 4, 4), dtype=int32, numpy=
array([[[40, 41, 42, 43],
        [45, 46, 47, 48],
        [50, 51, 52, 53],
        [55, 56, 57, 58]],

       [[60, 61, 62, 63],
        [65, 66, 67, 68],
        [70, 71, 72, 73],
        [75, 76, 77, 78]]])>
In [46]:
a[0,0,...] # 等同於a[0,0,:,:]
Out[46]:
<tf.Tensor: id=156, shape=(4, 5), dtype=int32, numpy=
array([[ 0,  1,  2,  3,  4],
       [ 5,  6,  7,  8,  9],
       [10, 11, 12, 13, 14],
       [15, 16, 17, 18, 19]])>
 

3.3 gather與gather_nd

 

gather與gather_nd是指TensorFlow經過gather()方法和gather_nd()方法提供的兩種索引方式。在numpy中,能夠經過嵌套list的方式來指定無規則的索引:

In [47]:
b = np.arange(20).reshape(4,5)
In [48]:
b[1, [0,3,4]] # 選取第2行的第1列、第4列、第5列
Out[48]:
array([5, 8, 9])
 

可是在TensorFlow中,這種索引方式並無從numpy中繼承下來,因此若是在Tensor中使用這種方式,會拋出如下異常:
TypeError: Only integers, slices (:), ellipsis (...), tf.newaxis (None) and scalar tf.int32/tf.int64 tensors are valid indices, got [0, 3, 4]

 

還好的是,在TensorFlow中經過gather()方法和gather_nd()方法提供了這種索引方法。

 

(1)gather()方法

In [54]:
tf.gather(b, axis=0, indices=[0, 2, 3]) # 選取第1行,第3行,第4行
Out[54]:
<tf.Tensor: id=163, shape=(3, 5), dtype=int32, numpy=
array([[ 0,  1,  2,  3,  4],
       [10, 11, 12, 13, 14],
       [15, 16, 17, 18, 19]])>
In [55]:
tf.gather(b, axis=1, indices=[0, 2, 3]) # 選取第1列,第3列,第4列
Out[55]:
<tf.Tensor: id=168, shape=(4, 3), dtype=int32, numpy=
array([[ 0,  2,  3],
       [ 5,  7,  8],
       [10, 12, 13],
       [15, 17, 18]])>
 

仔細觀察上面gather()方法例子,能夠發現,第一個參數時數據源,還有兩個參數中,axis指的是將要的維度,indices指的是須要選取的序號。

 

(2)gather_nd()

 

gather()方法一次只能對一個維度進行索引,gather_nd()方法能夠同時對多個維度進行索引。

In [56]:
tf.gather_nd(b, [[0, 2],[3, 3]]) # 選取第1行第3列的那個數據,和第4行第4列的數據
Out[56]:
<tf.Tensor: id=172, shape=(2,), dtype=int32, numpy=array([ 2, 18])>
 

3.5 條件索引

 

能夠結合一些簡單的邏輯運算符進行索引取值:

In [1]:
import tensorflow as tf
In [6]:
a = tf.random.uniform([3,3],minval=-10,maxval=10,dtype=tf.int32)
a
Out[6]:
<tf.Tensor: id=17, shape=(3, 3), dtype=int32, numpy=
array([[-7, -9,  6],
       [-6, -5,  9],
       [ 8,  9,  4]])>
In [7]:
mask = a < 0
mask
Out[7]:
<tf.Tensor: id=20, shape=(3, 3), dtype=bool, numpy=
array([[ True,  True, False],
       [ True,  True, False],
       [False, False, False]])>
 

能夠看到,返回的是一個shape與a相同的tensor,在a小於零的位置是True,大於零的位置爲False。進一步地,咱們能夠用boolwan_mask()方法直接取出符合條件的元素:

In [13]:
tf.boolean_mask(a,mask)
Out[13]:
<tf.Tensor: id=82, shape=(4,), dtype=int32, numpy=array([-7, -9, -6, -5])>
 

能夠結合where()方法取出符合條件元素的索引:

In [14]:
m_index = tf.where(mask)
m_index
Out[14]:
<tf.Tensor: id=84, shape=(4, 2), dtype=int64, numpy=
array([[0, 0],
       [0, 1],
       [1, 0],
       [1, 1]], dtype=int64)>
 

再使用以前說過的gather_nd()方法取值:

In [15]:
tf.gather_nd(a,m_index)
Out[15]:
<tf.Tensor: id=86, shape=(4,), dtype=int32, numpy=array([-7, -9, -6, -5])>
 

where()方法還有第二種用法——從兩個tensor中取出符合條件的值,這時候where()方法必須接受3個參數:

In [26]:
condition = tf.random.uniform([3,3],minval=0,maxval=2,dtype=tf.int32)
condition = tf.cast(condition, tf.bool)
condition
Out[26]:
<tf.Tensor: id=124, shape=(3, 3), dtype=bool, numpy=
array([[ True,  True, False],
       [ True, False, False],
       [ True, False, False]])>
In [34]:
a = tf.range(1,10)
a = tf.reshape(a,[3,3])
a
Out[34]:
<tf.Tensor: id=162, shape=(3, 3), dtype=int32, numpy=
array([[1, 2, 3],
       [4, 5, 6],
       [7, 8, 9]])>
In [35]:
b = tf.range(-9,0)
b = tf.reshape(b,[3,3])
b
Out[35]:
<tf.Tensor: id=169, shape=(3, 3), dtype=int32, numpy=
array([[-9, -8, -7],
       [-6, -5, -4],
       [-3, -2, -1]])>
In [36]:
tf.where(condition, a, b)
Out[36]:
<tf.Tensor: id=171, shape=(3, 3), dtype=int32, numpy=
array([[ 1,  2, -7],
       [ 4, -5, -4],
       [ 7, -2, -1]])>
 

上面where()方法返回的結果在True的位置取值是a中對應位置元素的值,在False位置是b中對應元素的值。

 

4 維度變換

 

4.1 reshape()

 

numpy中的ndarray數組有個一reshape()方法,用來改變數組的shape,TensorFlow中的reshape()方法,功能也是同樣的,不過TensorFlow中的reshape()沒有綁定到tensor中:

In [58]:
a = tf.ones([2,3,4])
In [59]:
a.shape
Out[59]:
TensorShape([2, 3, 4])
In [60]:
a
Out[60]:
<tf.Tensor: id=176, shape=(2, 3, 4), dtype=float32, numpy=
array([[[1., 1., 1., 1.],
        [1., 1., 1., 1.],
        [1., 1., 1., 1.]],

       [[1., 1., 1., 1.],
        [1., 1., 1., 1.],
        [1., 1., 1., 1.]]], dtype=float32)>
In [61]:
b = tf.reshape(a, [2, 2, 6])
In [62]:
b.shape
Out[62]:
TensorShape([2, 2, 6])
In [64]:
b
Out[64]:
<tf.Tensor: id=179, shape=(2, 2, 6), dtype=float32, numpy=
array([[[1., 1., 1., 1., 1., 1.],
        [1., 1., 1., 1., 1., 1.]],

       [[1., 1., 1., 1., 1., 1.],
        [1., 1., 1., 1., 1., 1.]]], dtype=float32)>
In [65]:
c = tf.reshape(a, [3, 2, 4])
In [66]:
c
Out[66]:
<tf.Tensor: id=183, shape=(3, 2, 4), dtype=float32, numpy=
array([[[1., 1., 1., 1.],
        [1., 1., 1., 1.]],

       [[1., 1., 1., 1.],
        [1., 1., 1., 1.]],

       [[1., 1., 1., 1.],
        [1., 1., 1., 1.]]], dtype=float32)>
 

能夠看到,在上面的例子中,經過reshape()方法能夠很方便的改變tensor的形狀,獲得一個新的tensor,須要注意的是在進行維度變換時,數據的重量是不變的,上面的例子不管是[2,3,4], [2, 2, 6]仍是[3, 2, 4]都對應總量24,若是對應不上,就會產生異常。

 

4.2 轉置:transpose()

 

transpose()方法提供了一種相似於裝置的操做:

In [75]:
a = tf.constant([[1,2,3],[4,5,6]])
In [76]:
a.shape
Out[76]:
TensorShape([2, 3])
In [77]:
b = tf.transpose(a)
In [78]:
b.shape
Out[78]:
TensorShape([3, 2])
In [79]:
b
Out[79]:
<tf.Tensor: id=192, shape=(3, 2), dtype=int32, numpy=
array([[1, 4],
       [2, 5],
       [3, 6]])>
 

在默認狀況下,transpose()方法會將全部維度按逆序方式徹底轉置,固然也能夠經過perm參數執行須要轉置的維度:

In [80]:
a=tf.constant([[[1,2,3],[4,5,6]],[[7,8,9],[10,11,12]]])
In [81]:
a
Out[81]:
<tf.Tensor: id=194, shape=(2, 2, 3), dtype=int32, numpy=
array([[[ 1,  2,  3],
        [ 4,  5,  6]],

       [[ 7,  8,  9],
        [10, 11, 12]]])>
In [82]:
b = tf.transpose(a) # 不指定perm參數時,至關於tf.transpose(a, perm=[2, 1, 0])
In [83]:
b
Out[83]:
<tf.Tensor: id=197, shape=(3, 2, 2), dtype=int32, numpy=
array([[[ 1,  7],
        [ 4, 10]],

       [[ 2,  8],
        [ 5, 11]],

       [[ 3,  9],
        [ 6, 12]]])>
In [84]:
c = tf.transpose(a, perm=[2, 1, 0])
In [85]:
c
Out[85]:
<tf.Tensor: id=200, shape=(3, 2, 2), dtype=int32, numpy=
array([[[ 1,  7],
        [ 4, 10]],

       [[ 2,  8],
        [ 5, 11]],

       [[ 3,  9],
        [ 6, 12]]])>
In [86]:
d = tf.transpose(a, perm=[0, 2, 1]) # 第一個維度不作變換,對第2、第三維度進行轉置
In [87]:
d
Out[87]:
<tf.Tensor: id=203, shape=(2, 3, 2), dtype=int32, numpy=
array([[[ 1,  4],
        [ 2,  5],
        [ 3,  6]],

       [[ 7, 10],
        [ 8, 11],
        [ 9, 12]]])>
 

4.3 添加維度:expand_dims()

In [88]:
a=tf.constant([[1,2,3],[4,5,6]])
In [89]:
a
Out[89]:
<tf.Tensor: id=205, shape=(2, 3), dtype=int32, numpy=
array([[1, 2, 3],
       [4, 5, 6]])>
In [90]:
tf.expand_dims(a, axis=0)
Out[90]:
<tf.Tensor: id=208, shape=(1, 2, 3), dtype=int32, numpy=
array([[[1, 2, 3],
        [4, 5, 6]]])>
In [91]:
tf.expand_dims(a, axis=1)
Out[91]:
<tf.Tensor: id=211, shape=(2, 1, 3), dtype=int32, numpy=
array([[[1, 2, 3]],

       [[4, 5, 6]]])>
In [92]:
tf.expand_dims(a, axis=-1)
Out[92]:
<tf.Tensor: id=214, shape=(2, 3, 1), dtype=int32, numpy=
array([[[1],
        [2],
        [3]],

       [[4],
        [5],
        [6]]])>
In [93]:
tf.expand_dims(a, axis=2)
Out[93]:
<tf.Tensor: id=217, shape=(2, 3, 1), dtype=int32, numpy=
array([[[1],
        [2],
        [3]],

       [[4],
        [5],
        [6]]])>
 

expand_dims()方法添加維度時,經過axis參數指定添加維度的位置,正數表示從前日後數,負數表示從後往前數。

 

4.4 壓縮維度:squeeze()

 

squeeze()方法與expand_dims()方法做用恰好相反,其做用是刪除張量中dim爲1的維度:

In [94]:
a = tf.ones([1,3,1,2])
In [95]:
a
Out[95]:
<tf.Tensor: id=221, shape=(1, 3, 1, 2), dtype=float32, numpy=
array([[[[1., 1.]],

        [[1., 1.]],

        [[1., 1.]]]], dtype=float32)>
In [96]:
tf.squeeze(a)
Out[96]:
<tf.Tensor: id=223, shape=(3, 2), dtype=float32, numpy=
array([[1., 1.],
       [1., 1.],
       [1., 1.]], dtype=float32)>
相關文章
相關標籤/搜索