tensorflow數據操作

tensorflow數據操作

xiaoyao 動手學深度學習,tensorflow代碼

import tensorflow as tf
print(tf.__version__)
2.1.0

在深度學習中,我們通常會頻繁地對數據進行操作。作爲動手學深度學習的基礎,本節將介紹如何對內存中的數據進行操作。

在tensorflow中,tensor是一個類,也是存儲和變換數據的主要工具。如果你之前用過NumPy,你會發現tensor和NumPy的多維數組非常類似。然而,tensor提供GPU計算和自動求梯度等更多功能,這些使tensor更加適合深度學習。

2.2.1 創建 ndarray

我們先介紹NDArray的最基本功能,我們用arange函數創建一個行向量。

range(12)
range(0, 12)
# 使用列表list()
list(range(12))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
# 使用tensorflow的constent()
x = tf.constant(range(12)) 

print(x.shape)
(12,)
x
<tf.Tensor: shape=(12,), dtype=int32, numpy=array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11])>
# 'tensorflow.python.framework.ops.EagerTensor' object has no attribute 'size'
# x.size

這時返回了一個tensor實例,其中包含了從0開始的12個連續整數。
我們可以通過shape屬性來獲取tensor實例的形狀。

x.shape
TensorShape([12])

我們也能夠通過len得到tensor實例中元素(element)的總數。

len(x)
12

下面使用reshape函數把行向量x的形狀改爲(3, 4),也就是一個3行4列的矩陣,並記作X。除了形狀改變之外,X中的元素保持不變。

# 使用reshape()改變x的形狀,記爲X
X = tf.reshape(x,(3,4))  # 使用形式爲:tf.reshape(tensor, shape, name=None)
X
<tf.Tensor: shape=(3, 4), dtype=int32, numpy=
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11]])>
X1 = tf.reshape(x,(-1,4)) # 這裏設置4列,行數由系統自動算出
X1
<tf.Tensor: shape=(3, 4), dtype=int32, numpy=
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11]])>

注意X屬性中的形狀發生了變化。上面x.reshape((3, 4))也可寫成x.reshape((-1, 4))或x.reshape((3, -1))。由於x的元素個數是已知的,這裏的-1是能夠通過元素個數和其他維度的大小推斷出來的。

接下來,我們創建一個各元素爲0,形狀爲(2, 3, 4)的張量。實際上,之前創建的向量和矩陣都是特殊的張量。

# 生成2行3列4層的三維數組(張量)
tf.zeros((2,3,4))
<tf.Tensor: shape=(2, 3, 4), dtype=float32, numpy=
array([[[0., 0., 0., 0.],
        [0., 0., 0., 0.],
        [0., 0., 0., 0.]],

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

類似地,我們可以創建各元素爲1的張量。

# 3行4列全1的張量矩陣
tf.ones((3,4))
<tf.Tensor: shape=(3, 4), dtype=float32, numpy=
array([[1., 1., 1., 1.],
       [1., 1., 1., 1.],
       [1., 1., 1., 1.]], dtype=float32)>

我們也可以通過Python的列表(list)指定需要創建的tensor中每個元素的值。

# 使用列表作爲元素指定生成張量,列表元素使用括號括起來。
Y = tf.constant(([2,1,4,3],[1,2,3,4],[4,3,2,1]))
Y
<tf.Tensor: shape=(3, 4), dtype=int32, numpy=
array([[2, 1, 4, 3],
       [1, 2, 3, 4],
       [4, 3, 2, 1]])>

有些情況下,我們需要隨機生成tensor中每個元素的值。下面我們創建一個形狀爲(3, 4)的tensor。它的每個元素都隨機採樣於均值爲0、標準差爲1的正態分佈。

tf.random.normal(shape=(3,4),mean=0.0, stddev=1.0)
<tf.Tensor: shape=(3, 4), dtype=float32, numpy=
array([[ 1.0739229 , -0.43079692,  0.41318092, -1.0123333 ],
       [ 1.7026106 , -0.6091808 , -0.6809646 , -0.51970834],
       [ 0.06855744, -0.54662657,  0.26630068,  1.1176411 ]],
      dtype=float32)>
tf.random.normal(shape=[5,6], mean=0, stddev=1)
<tf.Tensor: shape=(5, 6), dtype=float32, numpy=
array([[-0.16430375,  0.5867475 ,  0.14844054,  1.7092967 ,  1.2212179 ,
         0.7967226 ],
       [ 0.9270169 , -0.86642665, -0.16071264,  0.05040457,  0.47457072,
         0.21174431],
       [ 0.04873411,  0.33467257, -1.2409475 ,  1.0815437 , -0.33403933,
        -0.43393672],
       [-1.058746  ,  0.0496641 , -1.0181855 ,  0.48020288, -0.3674459 ,
         0.74898064],
       [ 0.68471956, -0.2087546 , -0.41063476, -0.55268043,  0.11406624,
         2.015665  ]], dtype=float32)>

2.2.2 運算

tensor支持大量的運算符(operator)。例如,我們可以對之前創建的兩個形狀爲(3, 4)的tensor做按元素加法。所得結果形狀不變。

print(X)
print(Y)
tf.Tensor(
[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]], shape=(3, 4), dtype=int32)
tf.Tensor(
[[2 1 4 3]
 [1 2 3 4]
 [4 3 2 1]], shape=(3, 4), dtype=int32)
# 按元素加
X + Y
<tf.Tensor: shape=(3, 4), dtype=int32, numpy=
array([[ 2,  2,  6,  6],
       [ 5,  7,  9, 11],
       [12, 12, 12, 12]])>

按元素乘法:

# 類比不翻轉卷積運算
X * Y
<tf.Tensor: shape=(3, 4), dtype=int32, numpy=
array([[ 0,  1,  8,  9],
       [ 4, 10, 18, 28],
       [32, 27, 20, 11]])>

按元素除法:

X / Y
<tf.Tensor: shape=(3, 4), dtype=float64, numpy=
array([[ 0.  ,  1.  ,  0.5 ,  1.  ],
       [ 4.  ,  2.5 ,  2.  ,  1.75],
       [ 2.  ,  3.  ,  5.  , 11.  ]])>

按元素做指數運算:

# 首先通過cast()將tensor轉爲dtype
Y = tf.cast(Y, tf.float32) # 數據類型要使用tf調用
tf.exp(Y)
<tf.Tensor: shape=(3, 4), dtype=float32, numpy=
array([[ 7.389056 ,  2.7182817, 54.59815  , 20.085537 ],
       [ 2.7182817,  7.389056 , 20.085537 , 54.59815  ],
       [54.59815  , 20.085537 ,  7.389056 ,  2.7182817]], dtype=float32)>

除了按元素計算外,我們還可以使用matmul函數做矩陣乘法。下面將X與Y的轉置做矩陣乘法。由於X是3行4列的矩陣,Y轉置爲4行3列的矩陣,因此兩個矩陣相乘得到3行3列的矩陣。

# x matmul y,要求x的列數與y的行數相同。這裏對Y進行transpose()
Y = tf.cast(Y, tf.int32)
tf.matmul(X, tf.transpose(Y))
<tf.Tensor: shape=(3, 3), dtype=int32, numpy=
array([[ 18,  20,  10],
       [ 58,  60,  50],
       [ 98, 100,  90]])>

我們也可以將多個tensor連結(concatenate)。下面分別在行上(維度0,即形狀中的最左邊元素)和列上(維度1,即形狀中左起第二個元素)連結兩個矩陣。可以看到,輸出的第一個tensor在維度0的長度( 6 )爲兩個輸入矩陣在維度0的長度之和( 3+3 ),而輸出的第二個tensor在維度1的長度( 8 )爲兩個輸入矩陣在維度1的長度之和( 4+4 )。

print(X)
print(Y)
tf.Tensor(
[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]], shape=(3, 4), dtype=int32)
tf.Tensor(
[[2 1 4 3]
 [1 2 3 4]
 [4 3 2 1]], shape=(3, 4), dtype=int32)
# axis =0,代表縱軸,axis=1,代表橫軸,調用形式爲:tf.concat()
# tf.concat([X,Y],axis = 0), tf.concat([X,Y],axis = 1)   或者形式如下
tf.concat([X,Y], 0), tf.concat([X,Y], 1)
(<tf.Tensor: shape=(6, 4), dtype=int32, numpy=
 array([[ 0,  1,  2,  3],
        [ 4,  5,  6,  7],
        [ 8,  9, 10, 11],
        [ 2,  1,  4,  3],
        [ 1,  2,  3,  4],
        [ 4,  3,  2,  1]])>,
 <tf.Tensor: shape=(3, 8), dtype=int32, numpy=
 array([[ 0,  1,  2,  3,  2,  1,  4,  3],
        [ 4,  5,  6,  7,  1,  2,  3,  4],
        [ 8,  9, 10, 11,  4,  3,  2,  1]])>)

使用條件判斷式可以得到元素爲0或1的新的tensor。以X == Y爲例,如果X和Y在相同位置的條件判斷爲真(值相等),那麼新的tensor在相同位置的值爲1;反之爲0。

X==Y
<tf.Tensor: shape=(3, 4), dtype=bool, numpy=
array([[False,  True, False,  True],
       [False, False, False, False],
       [False, False, False, False]])>
# 或者形式如下,對應位置元素相等則爲True,否則爲False
tf.equal(X,Y)
<tf.Tensor: shape=(3, 4), dtype=bool, numpy=
array([[False,  True, False,  True],
       [False, False, False, False],
       [False, False, False, False]])>

對tensor中的所有元素求和得到只有一個元素的tensor。

tf.reduce_sum(X)
# tf.reduce_sum(Y)
<tf.Tensor: shape=(), dtype=int32, numpy=66>
# 如果張量是一個矩陣,則相當於 Frobenius 範數;如果是向量,則相當於 2-norm
X = tf.cast(X, tf.float32)
tf.norm(X)
<tf.Tensor: shape=(), dtype=float32, numpy=22.494444>

2.2.3 廣播機制

前面我們看到如何對兩個形狀相同的tensor做按元素運算。當對兩個形狀不同的tensor按元素運算時,可能會觸發廣播(broadcasting)機制:先適當複製元素使這兩個tensor形狀相同後再按元素運算。

定義兩個tensor:

A = tf.reshape(tf.constant(range(3)), (3,1))
B = tf.reshape(tf.constant(range(2)), (1,2))
A, B
(<tf.Tensor: shape=(3, 1), dtype=int32, numpy=
 array([[0],
        [1],
        [2]])>,
 <tf.Tensor: shape=(1, 2), dtype=int32, numpy=array([[0, 1]])>)

由於A和B分別是3行1列和1行2列的矩陣,如果要計算A + B,那麼A中第一列的3個元素被廣播(複製)到了第二列,而B中第一行的2個元素被廣播(複製)到了第二行和第三行。如此,就可以對2個3行2列的矩陣按元素相加。

# 計算之前進行的處理爲:A,B的行數列數處理一致
A + B
<tf.Tensor: shape=(3, 2), dtype=int32, numpy=
array([[0, 1],
       [1, 2],
       [2, 3]])>

2.2.4 索引

在tensor中,索引(index)代表了元素的位置。tensor的索引從0開始逐一遞增。例如,一個3行2列的矩陣的行索引分別爲0、1和2,列索引分別爲0和1。

在下面的例子中,我們指定了tensor的行索引截取範圍[1:3]。依據左閉右開指定範圍的慣例,它截取了矩陣X中行索引爲1和2的兩行。

X
<tf.Tensor: shape=(3, 4), dtype=float32, numpy=
array([[ 0.,  1.,  2.,  3.],
       [ 4.,  5.,  6.,  7.],
       [ 8.,  9., 10., 11.]], dtype=float32)>
X[1:3]
<tf.Tensor: shape=(2, 4), dtype=float32, numpy=
array([[ 4.,  5.,  6.,  7.],
       [ 8.,  9., 10., 11.]], dtype=float32)>

我們可以指定tensor中需要訪問的單個元素的位置,如矩陣中行和列的索引,併爲該元素重新賦值。

X = tf.Variable(X)
X
<tf.Variable 'Variable:0' shape=(3, 4) dtype=float32, numpy=
array([[ 0.,  1.,  2.,  3.],
       [ 4.,  5.,  9.,  7.],
       [ 8.,  9., 10., 11.]], dtype=float32)>
X = tf.Variable(X)
X[1,2].assign(9)
<tf.Variable 'UnreadVariable' shape=(3, 4) dtype=float32, numpy=
array([[ 0.,  1.,  2.,  3.],
       [ 4.,  5.,  9.,  7.],
       [ 8.,  9., 10., 11.]], dtype=float32)>

當然,我們也可以截取一部分元素,併爲它們重新賦值。在下面的例子中,我們爲行索引爲1的每一列元素重新賦值。

X = tf.Variable(X)
X
<tf.Variable 'Variable:0' shape=(3, 4) dtype=float32, numpy=
array([[ 0.,  1.,  2.,  3.],
       [ 4.,  5.,  9.,  7.],
       [ 8.,  9., 10., 11.]], dtype=float32)>
X[1:2,:].assign(tf.ones(X[1:2,:].shape, dtype = tf.float32)*12)
<tf.Variable 'UnreadVariable' shape=(3, 4) dtype=float32, numpy=
array([[ 0.,  1.,  2.,  3.],
       [12., 12., 12., 12.],
       [ 8.,  9., 10., 11.]], dtype=float32)>

2.2.5 運算的內存開銷

在前面的例子裏我們對每個操作新開內存來存儲運算結果。舉個例子,即使像Y = X + Y這樣的運算,我們也會新開內存,然後將Y指向新內存。爲了演示這一點,我們可以使用Python自帶的id函數:如果兩個實例的ID一致,那麼它們所對應的內存地址相同;反之則不同。

X = tf.Variable(X)
Y = tf.cast(Y, dtype=tf.float32)

before = id(Y)
Y = Y + X
id(Y) == before
# tf.equal(id(X),before)
<tf.Tensor: shape=(), dtype=bool, numpy=False>

如果想指定結果到特定內存,我們可以使用前面介紹的索引來進行替換操作。在下面的例子中,我們先通過zeros_like創建和Y形狀相同且元素爲0的tensor,記爲Z。接下來,我們把X + Y的結果通過[:]寫進Z對應的內存中。

Z = tf.Variable(tf.zeros_like(Y))
before = id(Z)
Z[:].assign(X+Y)
id(Z) == before
True

實際上,上例中我們還是爲X + Y開了臨時內存來存儲計算結果,再複製到Z對應的內存。如果想避免這個臨時內存開銷,我們可以使用運算符全名函數中的out參數。

Z = tf.Variable(tf.zeros_like(Y))
before = id(Z)
Z = tf.add(X, Y)
id(Z) == before
False

如果X的值在之後的程序中不會複用,我們也可以用 X[:] = X + Y 或者 X += Y 來減少運算的內存開銷。

before = id(X)
X.assign_add(Y)
id(X) == before
True

2.2.6 ndarray 和 numpy 相互變化

我們可以通過array函數和asnumpy函數令數據在NDArray和NumPy格式之間相互變換。下面將NumPy實例變換成tensor實例。

import numpy as np

P = np.ones((2,3))
D = tf.constant(P)
D
<tf.Tensor: shape=(2, 3), dtype=float64, numpy=
array([[1., 1., 1.],
       [1., 1., 1.]])>

再將NDArray實例變換成NumPy實例。

print(type(D))
<class 'tensorflow.python.framework.ops.EagerTensor'>
np.array(D)
array([[1., 1., 1.],
       [1., 1., 1.]])
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章