TensorFlow
在上篇博客:深度學習筆記—線性迴歸(實戰)中,我們使用了常規方法實現Boston房價的預測,效果很明顯——費力不討好,代碼實現複雜,基本上是公式的實現,耗費時間長,損失下降不明顯,如果我們真拿這個去交付給用戶使用,恐怕會被罵個狗血淋頭,所以我們得建立一個更好的,效率更高的,更穩定的模型。
人和動物的區別就是是否會使用工具,我們要想更高效的建立和訓練模型,也需要學會使用工具—TensorFlow。
TensorFlow是一個基於數據流編程(dataflow programming)的符號數學系統,被廣泛應用於各類機器學習(machine learning)算法的編程實現,其前身是谷歌的神經網絡算法庫DistBelief 。
Tensorflow擁有多層級結構,可部署於各類服務器、PC終端和網頁並支持GPU和TPU高性能數值計算,被廣泛應用於谷歌內部的產品開發和各領域的科學研究 。
TensorFlow由谷歌人工智能團隊谷歌大腦(Google Brain)開發和維護,擁有包括TensorFlow Hub、TensorFlow Lite、TensorFlow Research Cloud在內的多個項目以及各類應用程序接口(Application Programming Interface, API) 。自2015年11月9日起,TensorFlow依據阿帕奇授權協議(Apache 2.0 open source license)開放源代碼 (摘自百度百科) 。
簡單來說,TensorFlow就是一個核心開源庫,可以幫助我們開發和訓練機器學習模型,如果想了解具體信息可以訪問TensorFlow官網進行了解,在這裏我們就不贅述了。
TensorFlow基礎
- TensorFlow中的數據類型
在上一節的代碼實現中,我們用了python自帶的數據類型和numpy的數據類型,但是這些數據類型都是早於TensorFlow出現的,導致這些數據類型在TensorFlow不能很好的使用,比如numpy中的array數據類型就不能自動求導,也不能支持GPU運算,所以TensorFlow自己定了了一種數據類型tensor,簡單來講,tensor就是TensorFlow可以接受的數據,幾乎所有的數據都可以叫做tensor,包括而不僅限於:
- 標量(scalar)
標量就是一個單獨的數。
- 向量(vector)
一個向量是一列數
- 矩陣(matrix)
矩陣是一個二維數組
- 張量(tensor)
一般的,一個數組中的元素分佈在若干維座標的規則網格中,我們稱之爲張量。
tensor支持的數據類型
- int float double
- bool
- string
action:tensor支持的所有數據類型都要用tf.int或tf.string等這種表示方法。
我們從名字發現一個很有趣的東西,TensorFlow支持的數據類型爲tensor,名字爲TensorFlow的前一部分單詞,剩下的單詞爲Flow,意爲滑動,流動的意思。這生動的解釋了TensorFlow的原理,數據在管道中流動處理,再從出口出來,給出我們想要的結果。
- 創建tensor
TensorFlow提供了大量功能豐富且繁雜的API接口供使用者調用,除了很複雜的操作外,初學者總能很輕易的找到合適的API實現你想要的功能
但是在使用TensorFlow之前,首先要導入TensorFlow庫:
import tensorflow as tf
首先我們來創建一個tensor:
From Numpy List
因爲TensorFlow的出現要晚於numpy的出現,所以TensorFlow設計的很多功能和API都學習了numpy的設計,並且對numpy的數據結構也專門設計了方法進行轉換:
convert_to_tensor()
convert_to_tensor(
value,
dtype=None,
name=None,
preferred_dtype=None
)
from Numpy
tf.convert_to_tensor(np.ones([2,3]))
tf.convert_to_tensor(np.zeros([2,3]))
該API可numpy中的矩陣直接轉換爲tensor,並且不僅限於numpy中矩陣,對於list也可直接轉化:
tf.convert_to_tensor([1,2])
tf.convert_to_tensor([1,2.])
tf.convert_to_tensor([1],[2.])
我們可以觀察第一行代碼和第二行代碼,唯一的區別就是第一行第二列的數據類型不同,該API可自動配合合適的數據類型。而第二行和第三行代碼的區別則是數據維度不同,在這裏我們不再贅述。
constant()
tf.constant(
value,
dtype=None,
shape=None,
name='Const',
verify_shape=False
)
創建一個張量,傳入list或者數值來填充
使用示例:
tf.constant(1)
tf.constant([1])
tf.constant([1,2.])
tf.constant([1,2.],[3.])#錯誤用法
注意,若傳入多維數據,則每個維度上的數據數量必須一致,否則將發生錯誤。
zeros() and ones()
tf.zeros(
shape,
dtype=tf.float32,
name=None
)
tf.ones(
shape,
dtype=tf.float32,
name=None
)
用法示例
tf.zeros([])
tf.zeros([1])
tf.zeros([2,2])
tf.zeros([2,3,3])
tf.ones(1)
tf.ones([2])
tf.ones([2,3])
zeros()和ones() 就像一對孿生兄弟,輸入想要的維度,API自動幫你填充0或1,一目瞭然。
zeros_like() and ones_like()
tf.zeros_like(
tensor,
dtype=None,
name=None
)
tf.ones_like(
tensor,
dtype=None,
name=None
)
用法示例
a = tf.zeros([2,3,3])
tf.zeros_like(a)
tf.ones_like(a)
除去去ones孿生兄弟外,tensorflow還提供了ones_lie()這對孿生兄弟,但是不同的是,ones兄弟需要給定tensor維度,而ones_like()兄弟則需要給定一個tensor作爲參數,該API按照tensor的維度重新建立一個tensor。
fill()
tf.fill(
dims,
value,
name=None
)
dims: 類型爲int32的tensor對象,用於表示輸出的維度(1-D, n-D),通常爲一個int32數組,如:[1], [2,3]等
value: 常量值(字符串,數字等),該參數用於設置到最終返回tensor對象中的值
name: (可選)當前操作別名
用法示例:
tf.fill([2,2],0)
tf.fill([2,2],0.0)
tf.fill([2,2],1)
fill()是一種很靈活的建立tensor的方法,只需要修改填入的數值就可以很輕鬆的取代ones兄弟和ones_like()兄弟。
random.normal()=
用於從服從指定正太分佈的數值中取出隨機數。一般用於參數初始值的賦值。
tf.random_normal(
shape,
mean=0.0,
stddev=1.0,
dtype=tf.float32,
seed=None,
name=None
)
shape: 輸出張量的形狀,必選
mean: 正態分佈的均值,默認爲0
stddev: 正態分佈的標準差,默認爲1.0
dtype: 輸出的類型,默認爲tf.float32
seed: 隨機數種子,是一個整數,當設置之後,每次生成的隨機數都一樣
name: 操作的名稱
使用示例:
tf.random.normal([2,2],mean=1,stddv=1)#正態分佈 均值 方差
tf.random.normal([2,2])#默認均值方差均爲0
random.truncated_normal()
輸出截斷的正態分佈的隨機值。說的簡單些,就是輸出正態分佈的一部分。
tf.random.truncated_normal(
shape,
mean=0.0,
stddev=1.0,
dtype=tf.dtypes.float32,
seed=None,
name=None
)
shape:一個一維整數張量或Python數組。輸出張量的形狀。
mean:一個零維張量或Python值,類型爲dtype。截斷正態分佈的均值。均值默認爲0
stddev:一個零維張量或Python值,類型爲dtype。正態分佈的標準偏差,在截斷。截斷前正態分佈的標準偏差,默認爲1.0
dtype:輸出的類型。
seed:一個Python整數。用於爲分佈創建隨機種子。
名稱:操作的名稱(可選)。
使用示例:
tf.random.truncated_normal([2,2],mean=0,stddev=1)#截斷方差
random.uniform()
random_uniform(
shape,# 生成的張量的形狀
minval=0,
maxval=None,
dtype=tf.float32,
seed=None,
name=None
)
shape:一維整數張量或 Python 數組.輸出張量的形狀
.
minval:dtype 類型的 0-D 張量或 Python 值;生成的隨機值範圍的下限;默認爲0.
maxval:dtype 類型的 0-D 張量或 Python 值.要生成的隨機值範圍的上限.如果 dtype 是浮點,則默認爲1 .
dtype:輸出的類型:float16、float32、float64、int32、orint64.
seed:一個 Python 整數.用於爲分佈創建一個隨機種子.查看
tf.set_random_seed 行爲.
name:操作的名稱(可選).
使用示例:
tf.random.uniform([2,2],minval=0,maxval=1)#均勻分佈
tf.random.uniform([2,2],minval=0,maxval=10)
跟random.normal()類似,random.uniform()同樣是給出隨機值,便於參數更新。
- 索引與切片
我們在學習java,c,以及python等編程語言時,可以很方便的使用指針或者或者索引解決找位置的爲題,在tensorflow中,爲了方便尋找數據,也提供了功能強大是索引方法,與java數組中的索引方式類似。
方法一
import tensorflow as tf
import numpy as np
a = tf.ones([1,5,5,3])
#(1, 5, 5, 3)
print(a.shape)
(5, 3)
print(a[0][0].shape)
#(3,)
print(a[0][0][0].shape)
#()
print(a[0][0][0][2].shape)
tensorflow中的索引方式與numpy矩陣中的索引方式類似,用法也極爲相像。
方法二
import tensorflow as tf
a = tf.range(10)
print(a)
print(a[3:5].shape)
#(1.)
print(a[-1:].shape)
注意:此方法僅適用於向量。
方法三
import tensorflow as tf
import numpy as np
a = tf.ones([5,5,5,5])
#(5,5,5)
print(a[2,:,:,:].shape)
#(5,5)
print(a[2,2,:,:].shape)
#(5,5,5)
print(a[:,:,:,3].shape)
:用來佔位,即表示該維度全選。
方法四
import tensorflow as tf
import numpy as np
a = tf.ones([5,5,5,5])
#(1,5,5,5)
print(a[2:3,:,:,:].shape)
#(3,5,5,5)
print(a[2:,:,:,:].shape)
#(2,1,5,5)
print(a[2:,2,:,:].shape)
實際上這種切片方法很像是方法二和方法三的混合體,實際上效果也是驗證了。
以上四種方法雖然能很便捷的截取tensor的一部分,但是都存在侷限性,只能選取某一維度上的某一位置的所有數據,或者是某 一維度上的某幾個連續位置上的所有數據,這明顯不方便我們對數據使用和處理,所以tensor也提供了幾個特殊的方法,滿足我們的需求。
gather
gather(
params,
indices,
validate_indices=None,
name=None
)
params:是一個tensor,
indices:是個值爲int的tensor用來指定要從params取得元素的第0維的index。
validate_indices:針對哪一維度進行的操作。
name:操作的名字。
使用示例:
import tensorflow as tf
import numpy as np
a = tf.random.normal([2,4,28,28,3])
#(2, 2, 28, 28, 3)
print(tf.gather(a,axis=1,indices=[2,3]).shape)
gather一詞是收集的意思,從結果上我們可以很清楚的看到,確實是收集,針對某一維度的某幾個不連續的位置進行數據提取操作,並且組成一個新的張量。
gather_nd
gather_nd(
params,
indices,
name=None
)
params:張量.這個張量是用來收集數值的.
indices:張量.必須是以下類型之一:int32,int64;索引張量.
name:操作的名稱(可選).
使用示例:
import tensorflow as tf
import numpy as np
a = tf.random.normal([2,4,28,28,3])
#(4, 28, 28, 3)
print(tf.gather_nd(a,[0]).shape)
#(28, 28, 3)
print(tf.gather_nd(a,[0,1]).shape)
#(28, 3)
print(tf.gather_nd(a,[0,1,2]).shape)
#(1, 28, 3)
print(tf.gather_nd(a,[[0,1,2]]).shape)
#(3, 4, 28, 28, 3)
print(tf.gather_nd(a,[[0],[1],[2]]).shape)
#(2, 28, 28, 3)
print(tf.gather_nd(a,[[0,1],[1,3]]).shape)
從示例中我們可以看到,gather_nd()是一個比gather更爲靈活的方法,不僅可以java中索引式取某一維度上所有的數據,還能像gather()方法一樣,只取部分數據,並且合併成一個新的張量。
boolean_mask()
tf.boolean_mask(
tensor,
mask,
name='boolean_mask',
axis=None
)
tensor:N維度的tensor,
mask:由布爾值構成的K維度的列表,注意K小於等於N,
name:可選項也就是這個操作的名字,
axis是一個0維度的int型數字,表示的是從參數tensor的哪個axis開始過濾。
使用示例:
a=tf.random.normal([4,28,28,3])
#([2, 28, 28, 3])
print(tf.boolean_mask(a,mask=[True,True,False,False]).shape)
#([4, 28, 28, 2])
print(tf.boolean_mask(a,mask=[True,True,False],axis=3).shape)
boolean_mask 是依據索引出的TRUE和False值判斷是否去該維度的數據,如果僅僅只是那麼簡單,爲什麼還要設計這個功能呢,豈不是重複了,所以boolean_mask的功能沒有那麼簡單。
import tensorflow as tf
import numpy as np
a = tf.random.normal([2,3,4])
#(4,4)
print(tf.boolean_mask(a,mask=[[True,False,True],[False,True,True]]).shape)
#(3,4)
print(tf.boolean_mask(a,mask=[[True,False,False],[False,True,True]]).shape)
在boolean_mask()函數中,將mask參數換成多維列表,則可以與tensor中的維度相對應取值,更爲方便快捷。
- 維度變換
reshape
tf.reshape(
tensor,
shape,
name=None
)
tensor:需要進行維度變換的的張量。
shape:張量要變成的形狀。
name:操作名稱。
使用示例:
import tensorflow as tf
import numpy as np
a = tf.random.normal([4,28,28,3])
#(4, 784, 3)
print(tf.reshape(a,[4,28*28,3]).shape)
#(4, 784, 3)
print(tf.reshape(a,[4,-1,3]).shape)
#
print(tf.reshape(a,[4,-1]).shape)
#(4, 1, 784, 3)
print(tf.reshape(tf.reshape(a,[4,-1]),[4,-1,784,3]))
transpose()
tf.transpose(
matrix,
perm=None
)
matrix:待變換的張量。
perm:張量變換方式的具體參數。
使用示例:
import tensorflow as tf
import numpy as np
a = tf.random.normal([4,28,28,3])
#(3, 28, 28, 4)
print(tf.transpose(a).shape)
#(4, 28, 3, 28)
print(tf.transpose(a,perm=[0,1,3,2]).shape)
從使用示例上來看,transpose方法和reshape方法在功能上有些重合,但是在transpose方法更爲靈活,在不指定matrix參數時,transpose方法默認將tensor的維度倒序,同時在指定matrix參數指定爲列表時,會按照列表排序來變換。
expand_dims()
tf.expand_dims(
input,
axis=None,
name=None,
dim=None
)
input是輸入張量。
axis是指定擴大輸入張量形狀的維度索引值。
dim等同於軸,一般不推薦使用。
使用示例;
import tensorflow as tf
import numpy as np
a = tf.random.normal([4,28,28,3])
#(1, 4, 28, 28, 3)
print(tf.expand_dims(a,axis=0).shape)
#(4, 28, 28, 3, 1)
print(tf.expand_dims(a,axis=-1).shape)
從示例中我們可以很清楚的看出來,expand_dims()方法用於向張量中加入一個新的維度,而axis參數可控制添加維度的位置。
squeeze()
squeeze(
input,
axis=None,
name=None,
squeeze_dims=None
)
input: 需要壓縮的tensor
axis: 如果指定,只能擠壓列出的維度。維度索引從 0 開始。壓縮非 1 的維度是錯誤的,並且必須在範圍內。
name: 操作的名稱 (可選)
squeeze_dims: 已棄用的關鍵字參數,現在是axis。
使用示例:
import tensorflow as tf
import numpy as np
a = tf.random.normal([4,28,28,3])
#(2, 3)
print(tf.squeeze(tf.zeros([1,2,1,1,3])).shape)
b = tf.ones([1,2,1,3])
#(2, 1, 3)
print(tf.squeeze(b,axis=0).shape)
#(1, 2, 3)
print(tf.squeeze(b,axis=2).shape)
該函數返回一個張量,這個張量是將原始input中所有維度爲1的那些維都刪掉的結果
axis可以用來指定要刪掉的爲1的維度,此處要注意指定的維度必須確保其是1,否則會報錯。如果axis不指定,則默認刪除所有爲1的維度。