1 簡(jiǎn)單函數(shù)與張量
可以先簡(jiǎn)單地將 TensorFlow 視為一個(gè)科學(xué)計(jì)算庫(kù)(類似于 Python 下的 NumPy)疼阔。它提供了一些簡(jiǎn)單函數(shù)戒劫,與 NumPy 極為類似,比如:
# 定義一個(gè)隨機(jī)數(shù)(標(biāo)量)
random_float = tf.random.uniform(shape=())
# 定義一個(gè)有2個(gè)元素的零向量
zero_vector = tf.zeros(shape=(2))
tf.random.uniform
婆廊、tf.zeros
即 DAG 上的節(jié)點(diǎn)迅细,它們的輸出均為張量(即 tf.Tensor
對(duì)象),比如:
zero_vector
輸出:
<tf.Tensor: shape=(2,), dtype=float32, numpy=array([0., 0.], dtype=float32)>
張量一般分為常量 tf.constant
與變量 tf.Variable
淘邻。常量的值在計(jì)算圖中不可以被重新賦值茵典,變量可以在計(jì)算圖中用 assign
等算子重新賦值。
1.1 常量
與 NumPy 極為相似:
import numpy as np
import tensorflow as tf
i = tf.constant(1) # tf.int32 類型常量
l = tf.constant(1, dtype=tf.int64) # tf.int64 類型常量
f = tf.constant(1.23) # tf.float32 類型常量
d = tf.constant(3.14, dtype=tf.double) # tf.double 類型常量
s = tf.constant("hello world") # tf.string類型常量
b = tf.constant(True) # tf.bool類型常量
print(tf.int64 == np.int64)
print(tf.bool == np.bool)
print(tf.double == np.float64)
print(tf.string == np.unicode) # tf.string 類型和 np.unicode類型不等價(jià)
輸出:
True
True
True
False
我們也可以查看張量的維度:
scalar = tf.constant(True) # 標(biāo)量宾舅,0維張量
print(tf.rank(scalar))
# tf.rank 的作用和 numpy 的 ndim 方法相同
print(scalar.numpy().ndim)
顯示:
tf.Tensor(0, shape=(), dtype=int32)
0
可以用 tf.cast
改變張量的數(shù)據(jù)類型统阿。
可以用 numpy
方法將 tensorflow 中的張量轉(zhuǎn)化成 numpy
中的張量枚尼。
可以用 shape
方法查看張量的尺寸。
A = tf.constant([[1., 2.], [3., 4.]])
# 查看矩陣A的形狀砂吞、類型和值
print(A.shape) # 輸出(2, 2),即矩陣的長(zhǎng)和寬均為2
print(A.dtype) # 輸出<dtype: 'float32'>
print(A.numpy())
顯示:
(2, 2)
<dtype: 'float32'>
[[1. 2.]
[3. 4.]]
還有:
h = tf.constant([123,456],dtype = tf.int32)
f = tf.cast(h,tf.float32)
print(h.dtype, f.dtype)
顯示:
<dtype: 'int32'> <dtype: 'float32'>
1.2 變量
模型中需要被訓(xùn)練的參數(shù)一般被設(shè)置成變量崎溃。
常量值不可以改變蜻直,常量的重新賦值相當(dāng)于創(chuàng)造新的內(nèi)存空間
c = tf.constant([1.0, 2.0])
print(c)
print(id(c))
c = c + tf.constant([1.0, 1.0])
print(c)
print(id(c))
顯示:
tf.Tensor([1. 2.], shape=(2,), dtype=float32)
222632776
tf.Tensor([2. 3.], shape=(2,), dtype=float32)
222631192
變量的值可以改變,可以通過(guò)assign, assign_add等方法給變量重新賦值:
v = tf.Variable([1.0, 2.0], name="v")
print(v)
print(id(v))
v.assign_add([1.0, 1.0])
print(v)
print(id(v))
顯示:
<tf.Variable 'v:0' shape=(2,) dtype=float32, numpy=array([1., 2.], dtype=float32)>
271494792
<tf.Variable 'v:0' shape=(2,) dtype=float32, numpy=array([2., 3.], dtype=float32)>
271494792
2 張量的創(chuàng)建
張量創(chuàng)建的許多方法和 numpy 中創(chuàng)建 array 的方法很像袁串。下面使用 tf.print
函數(shù)打印輸出概而。
import tensorflow as tf
import numpy as np
a = tf.constant([1,2,3],dtype = tf.float32)
tf.print(a)
b = tf.range(1,10,delta = 2)
tf.print(b)
c = tf.linspace(0.0,2*3.14,100)
tf.print(c)
d = tf.zeros([3,3])
tf.print(d)
a = tf.ones([3,3])
b = tf.zeros_like(a,dtype= tf.float32)
tf.print(a)
tf.print(b)
b = tf.fill([3,2],5)
tf.print(b)
#均勻分布隨機(jī)
tf.random.set_seed(1.0)
a = tf.random.uniform([5],minval=0,maxval=10)
tf.print(a)
#正態(tài)分布隨機(jī)
b = tf.random.normal([3,3],mean=0.0,stddev=1.0)
tf.print(b)
#正態(tài)分布隨機(jī),剔除2倍方差以外數(shù)據(jù)重新生成
c = tf.random.truncated_normal((5,5), mean=0.0, stddev=1.0, dtype=tf.float32)
tf.print(c)
# 特殊矩陣
I = tf.eye(3,3) #單位矩陣
tf.print(I)
tf.print(" ")
t = tf.linalg.diag([1,2,3]) #對(duì)角陣
tf.print(t)
顯示:
[1 2 3]
[1 3 5 7 9]
[0 0.0634343475 0.126868695 ... 6.15313148 6.21656609 6.28]
[[0 0 0]
[0 0 0]
[0 0 0]]
[[1 1 1]
[1 1 1]
[1 1 1]]
[[0 0 0]
[0 0 0]
[0 0 0]]
[[5 5]
[5 5]
[5 5]]
[1.65130854 9.01481247 6.30974197 4.34546089 2.9193902]
[[0.403087884 -1.0880208 -0.0630953535]
[1.33655667 0.711760104 -0.489286453]
[-0.764221311 -1.03724861 -1.25193381]]
[[-0.457012236 -0.406867266 0.728577733 -0.892977774 -0.369404584]
[0.323488563 1.19383323 0.888299048 1.25985599 -1.95951891]
[-0.202244401 0.294496894 -0.468728036 1.29494202 1.48142183]
[0.0810953453 1.63843894 0.556645 0.977199793 -1.17777884]
[1.67368948 0.0647980496 -0.705142677 -0.281972528 0.126546144]]
[[1 0 0]
[0 1 0]
[0 0 1]]
[[1 0 0]
[0 2 0]
[0 0 3]]
3 索引切片
張量的索引切片方式和 numpy 幾乎是一樣的囱修。切片時(shí)支持缺省參數(shù)和省略號(hào)赎瑰。對(duì)于 tf.Variable
,可以通過(guò)索引和切片對(duì)部分元素進(jìn)行修改。對(duì)于提取張量的連續(xù)子區(qū)域破镰,也可以使用 tf.slice
餐曼。
tf.random.set_seed(3)
t = tf.random.uniform([5,5],minval=0,maxval=10,dtype=tf.int32)
tf.print(t)
#第0行
tf.print(t[0])
#倒數(shù)第一行
tf.print(t[-1])
#第1行第3列
tf.print(t[1,3])
tf.print(t[1][3])
#第1行至第3行
tf.print(t[1:4,:])
tf.print(tf.slice(t,[1,0],[3,5])) #tf.slice(input,begin_vector,size_vector)
#第1行至最后一行,第0列到最后一列每隔兩列取一列
tf.print(t[1:4,:4:2])
#對(duì)變量來(lái)說(shuō)鲜漩,還可以使用索引和切片修改部分元素
x = tf.Variable([[1,2],[3,4]],dtype = tf.float32)
x[1,:].assign(tf.constant([0.0,0.0]))
tf.print(x)
a = tf.random.uniform([3,3,3],minval=0,maxval=10,dtype=tf.int32)
tf.print(a)
#省略號(hào)可以表示多個(gè)冒號(hào)
tf.print(a[...,1])
此外源譬,對(duì)于不規(guī)則的切片提取,可以使用 tf.gather
,tf.gather_nd
孕似,tf.boolean_mask
踩娘。
# 考慮班級(jí)成績(jī)冊(cè)的例子,有4個(gè)班級(jí)喉祭,每個(gè)班級(jí)10個(gè)學(xué)生养渴,每個(gè)學(xué)生7門科目成績(jī)》豪樱可以用一個(gè) 4x10x7 的張量來(lái)表示理卑。
scores = tf.random.uniform((4, 10, 7), minval=0, maxval=100, dtype=tf.int32)
tf.print(scores)
# 抽取每個(gè)班級(jí)第0個(gè)學(xué)生,第5個(gè)學(xué)生胶惰,第9個(gè)學(xué)生的全部成績(jī)
p = tf.gather(scores, [0, 5, 9], axis=1)
tf.print(p)
#抽取每個(gè)班級(jí)第0個(gè)學(xué)生傻工,第5個(gè)學(xué)生,第9個(gè)學(xué)生的第1門課程孵滞,第3門課程中捆,第6門課程成績(jī)
q = tf.gather(tf.gather(scores,[0,5,9],axis=1),[1,3,6],axis=2)
tf.print(q)
# 抽取第0個(gè)班級(jí)第0個(gè)學(xué)生,第2個(gè)班級(jí)的第4個(gè)學(xué)生坊饶,第3個(gè)班級(jí)的第6個(gè)學(xué)生的全部成績(jī)
#indices的長(zhǎng)度為采樣樣本的個(gè)數(shù)泄伪,每個(gè)元素為采樣位置的坐標(biāo)
s = tf.gather_nd(scores,indices = [(0,0),(2,4),(3,6)])
tf.print(s)
tf.boolean_mask
功能最為強(qiáng)大,它可以實(shí)現(xiàn) tf.gather
匿级,tf.gather_nd
的功能蟋滴,并且 tf.boolean_mask
還可以實(shí)現(xiàn)布爾索引染厅。
# 抽取每個(gè)班級(jí)第0個(gè)學(xué)生,第5個(gè)學(xué)生津函,第9個(gè)學(xué)生的全部成績(jī)
p = tf.boolean_mask(scores, [True, False, False, False, False,
True, False, False, False, True], axis=1)
tf.print(p)
# 抽取第0個(gè)班級(jí)第0個(gè)學(xué)生肖粮,第2個(gè)班級(jí)的第4個(gè)學(xué)生,第3個(gè)班級(jí)的第6個(gè)學(xué)生的全部成績(jī)
s = tf.boolean_mask(scores,
[[True, False, False, False, False, False, False, False, False, False],
[False, False, False, False, False,
False, False, False, False, False],
[False, False, False, False, True,
False, False, False, False, False],
[False, False, False, False, False, False, True, False, False, False]])
tf.print(s)
# 利用tf.boolean_mask可以實(shí)現(xiàn)布爾索引
# 找到矩陣中小于0的元素
c = tf.constant([[-1, 1, -1], [2, 2, -2], [3, -3, 3]], dtype=tf.float32)
tf.print(c, "\n")
tf.print(tf.boolean_mask(c, c < 0), "\n")
tf.print(c[c < 0]) # 布爾索引尔苦,為boolean_mask的語(yǔ)法糖形式
以上這些方法僅能提取張量的部分元素值涩馆,但不能更改張量的部分元素值得到新的張量。如果要通過(guò)修改張量的某些元素得到新的張量允坚,可以使用 tf.where
魂那,tf.scatter_nd
。
tf.where
可以理解為 if
的張量版本稠项,此外它還可以用于找到滿足條件的所有元素的位置坐標(biāo)涯雅。tf.scatter_nd
的作用和 tf.gather_nd
有些相反,tf.gather_nd
用于收集張量的給定位置的元素展运,而 tf.scatter_nd
可以將某些值插入到一個(gè)給定 shape 的全 0 的張量的指定位置處活逆。
# 找到張量中小于0的元素,將其換成np.nan得到新的張量
# tf.where和np.where作用類似,可以理解為if的張量版本
c = tf.constant([[-1, 1, -1], [2, 2, -2], [3, -3, 3]], dtype=tf.float32)
d = tf.where(c < 0, tf.fill(c.shape, np.nan), c)
# 如果where只有一個(gè)參數(shù)拗胜,將返回所有滿足條件的位置坐標(biāo)
indices = tf.where(c < 0)
# 將張量的第[0,0]和[2,1]兩個(gè)位置元素替換為0得到新的張量
d = c - tf.scatter_nd([[0, 0], [2, 1]], [c[0, 0], c[2, 1]], c.shape)
# scatter_nd的作用和gather_nd有些相反
# 可以將某些值插入到一個(gè)給定shape的全0的張量的指定位置處划乖。
indices = tf.where(c < 0)
tf.scatter_nd(indices, tf.gather_nd(c, indices), c.shape)
4 維度變換與數(shù)據(jù)合成
維度變換相關(guān)函數(shù)主要有 tf.reshape
, tf.squeeze
, tf.expand_dims
, tf.transpose
。
-
tf.reshape
可以改變張量的形狀挤土。tf.reshape
可以改變張量的形狀琴庵,但是其本質(zhì)上不會(huì)改變張量元素的存儲(chǔ)順序,所以仰美,該操作實(shí)際上非常迅速迷殿,并且是可逆的。 -
tf.squeeze
可以減少維度咖杂。 -
tf.expand_dims
可以增加維度庆寺。 -
tf.transpose
可以交換維度。
維度變換相關(guān)函數(shù)主要有 tf.reshape
, tf.squeeze
, tf.expand_dims
, tf.transpose
诉字。
-
tf.reshape
可以改變張量的形狀懦尝。tf.reshape
可以改變張量的形狀,但是其本質(zhì)上不會(huì)改變張量元素的存儲(chǔ)順序壤圃,所以陵霉,該操作實(shí)際上非常迅速,并且是可逆的伍绳。 -
tf.squeeze
可以減少維度踊挠。如果張量在某個(gè)維度上只有一個(gè)元素,利用tf.squeeze
可以消除這個(gè)維度冲杀。如果張量在某個(gè)維度上只有一個(gè)元素效床,利用tf.squeeze
可以消除這個(gè)維度睹酌。 -
tf.expand_dims
可以增加維度。 -
tf.transpose
可以交換維度剩檀。與tf.reshape
不同憋沿,它會(huì)改變張量元素的存儲(chǔ)順序。常用于圖片存儲(chǔ)格式的變換上沪猴。
張量的各個(gè)元素在內(nèi)存中是線性存儲(chǔ)的卤妒,其一般規(guī)律是,同一層級(jí)中的相鄰元素的物理地址也相鄰字币。
和 numpy 類似,可以用 tf.concat
和 tf.stack
方法對(duì)多個(gè)張量進(jìn)行合并共缕,可以用 tf.split
方法把一個(gè)張量分割成多個(gè)張量洗出。tf.concat
和 tf.stack
有略微的區(qū)別,tf.concat
是連接图谷,不會(huì)增加維度翩活,而 tf.stack
是堆疊,會(huì)增加維度便贵。tf.split
是 tf.concat
的逆運(yùn)算菠镇,可以指定分割份數(shù)平均分割,也可以通過(guò)指定每份的記錄數(shù)量進(jìn)行分割承璃。
5 數(shù)學(xué)運(yùn)算
5.1 標(biāo)量運(yùn)算
加減乘除乘方利耍,以及三角函數(shù),指數(shù)盔粹,對(duì)數(shù)等常見(jiàn)函數(shù)隘梨,邏輯比較運(yùn)算符等都是標(biāo)量運(yùn)算符。標(biāo)量運(yùn)算符的特點(diǎn)是對(duì)張量實(shí)施逐元素運(yùn)算舷嗡。有些標(biāo)量運(yùn)算符對(duì)常用的數(shù)學(xué)運(yùn)算符進(jìn)行了重載轴猎。并且支持類似 numpy 的廣播特性。許多標(biāo)量運(yùn)算符都在 tf.math
模塊下进萄。
import tensorflow as tf
import numpy as np
a = tf.constant([[1.0, 2], [-3, 4.0]])
b = tf.constant([[5.0, 6], [7.0, 8.0]])
a + b # 運(yùn)算符重載
a - b
a * b
a / b
a ** 2
a ** (3.5)
a % 3 # mod的運(yùn)算符重載捻脖,等價(jià)于m = tf.math.mod(a,3)
a//3 # 地板除法
a >= 2
(a >= 2) & (a <= 3)
(a >= 2) | (a <= 3)
a == 5 # tf.equal(a,5)
tf.sqrt(a)
a = tf.constant([1.0, 8.0])
b = tf.constant([5.0, 6.0])
c = tf.constant([6.0, 7.0])
tf.add_n([a, b, c]) # 多個(gè)張量相加
tf.maximum(a, b) # 最大值
tf.minimum(a, b) # 最小值
a = tf.constant("hello")
b = tf.constant("tensorflow2")
c = tf.strings.join([a,b]," ")
tf.print(c)
5.2 向量運(yùn)算
向量運(yùn)算符只在一個(gè)特定軸上運(yùn)算,將一個(gè)向量映射到一個(gè)標(biāo)量或者另外一個(gè)向量中鼠。許多向量運(yùn)算符都以 reduce
開(kāi)頭可婶。
# 向量reduce
a = tf.range(1, 10)
tf.print(tf.reduce_sum(a))
tf.print(tf.reduce_mean(a))
tf.print(tf.reduce_max(a))
tf.print(tf.reduce_min(a))
tf.print(tf.reduce_prod(a))
# 張量指定維度進(jìn)行reduce
b = tf.reshape(a, (3, 3))
tf.print(tf.reduce_sum(b, axis=1, keepdims=True))
tf.print(tf.reduce_sum(b, axis=0, keepdims=True))
# bool類型的reduce
p = tf.constant([True, False, False])
q = tf.constant([False, False, True])
tf.print(tf.reduce_all(p))
tf.print(tf.reduce_any(q))
# 利用tf.foldr實(shí)現(xiàn)tf.reduce_sum
s = tf.foldr(lambda a, b: a+b, tf.range(10))
tf.print(s)
# cum掃描累積
a = tf.range(1, 10)
tf.print(tf.math.cumsum(a))
tf.print(tf.math.cumprod(a))
# arg最大最小值索引
a = tf.range(1, 10)
tf.print(tf.argmax(a))
tf.print(tf.argmin(a))
# tf.math.top_k可以用于對(duì)張量排序
a = tf.constant([1, 3, 7, 5, 4, 8])
values, indices = tf.math.top_k(a, 3, sorted=True)
tf.print(values)
tf.print(indices)
利用 tf.math.top_k
可以在 TensorFlow 中實(shí)現(xiàn) KNN 算法。
5.3 矩陣運(yùn)算
矩陣必須是二維的援雇。矩陣運(yùn)算包括:矩陣乘法扰肌,矩陣轉(zhuǎn)置,矩陣逆熊杨,矩陣求跡曙旭,矩陣范數(shù)盗舰,矩陣行列式,矩陣求特征值桂躏,矩陣分解等運(yùn)算钻趋。除了一些常用的運(yùn)算外,大部分和矩陣有關(guān)的運(yùn)算都在 tf.linalg
子包中剂习。
# 矩陣乘法
a = tf.constant([[1, 2], [3, 4]])
b = tf.constant([[2, 0], [0, 2]])
a@b # 等價(jià)于tf.matmul(a,b)
# 矩陣轉(zhuǎn)置
a = tf.constant([[1.0, 2], [3, 4]])
tf.transpose(a)
# 矩陣逆蛮位,必須為tf.float32或tf.double類型
a = tf.constant([[1.0, 2], [3.0, 4]], dtype=tf.float32)
tf.linalg.inv(a)
# 矩陣求trace
a = tf.constant([[1.0, 2], [3, 4]])
tf.linalg.trace(a)
# 矩陣求范數(shù)
a = tf.constant([[1.0, 2], [3, 4]])
tf.linalg.norm(a)
# 矩陣行列式
a = tf.constant([[1.0, 2], [3, 4]])
tf.linalg.det(a)
# 矩陣特征值
tf.linalg.eigvalsh(a)
# 矩陣qr分解
a = tf.constant([[1.0, 2.0], [3.0, 4.0]], dtype=tf.float32)
q, r = tf.linalg.qr(a)
tf.print(q)
tf.print(r)
tf.print(q@r)
# 矩陣svd分解
a = tf.constant([[1.0, 2.0], [3.0, 4.0]], dtype=tf.float32)
v, s, d = tf.linalg.svd(a)
tf.matmul(tf.matmul(s, tf.linalg.diag(v)), d)
利用 svd 分解可以在 TensorFlow 中實(shí)現(xiàn)主成分分析降維。