前言
只有光頭才能變強。
文本已收錄至我的GitHub倉庫,歡迎Star:https://github.com/ZhongFuCheng3y/3y
回顧前面:
TensorFlow是什么意思?Tensor?Flow?這篇文章介紹TensorFlow一些最基礎的知識。
一、Tensor介紹
在介紹之前,首先要記住一個結論:TensorFlow使用Tensor來表示數據
接着我們來看看什么是Tensor,在官網的文檔中,Tensor被翻譯成”張量“。其中也給出了一個定義:
張量是對矢量和矩陣向潛在的更高維度的泛化,TensorFlow 在內部將張量表示為基本數據類型的n維數組。
不知道你們看完這句話有啥感受,反正我當時就看不懂,啥是”張量“?。於是,我就跑去知乎里邊用關鍵字搜了一下:”張量是什么“。果真給我搜到了相關的問題:《怎么通俗地理解張量?》
我本以為通過知乎,就可以通俗易懂地理解什么是張量,能給我一個清晰的認識。殊不知,大多數答主都在回答在物理和數學中張量的定義,隨后貼出了一堆我看不懂的公式。其中,也看到了一種相對通俗易懂的定義:
一個量, 在不同的參考系下按照某種特定的法則進行變換, 就是張量.
把所有答主的回答都閱讀了一遍,看完就更加抽象了。再回到官方文檔中,看看官方介紹張量的例子,貌似有點懂了。
目前為止我們有兩個結論:
- TensorFlow使用Tensor來表示數據
- TensorFlow 在內部將張量表示為基本數據類型的n維數組
我再翻譯一下上面的兩句話:在TensorFlow所有的數據都是一個n維的數組,只是我們給它起了個名字叫做張量(Tensor)
中間折騰了一大堆,實際上還是將最開頭的結論和官方的定義再翻譯成自己覺得好理解的話...但很多時候,學習就這么一個過程。
1.1Tensor的基礎
從上面我們已經得知,Tensor(張量)實際上就是一個n維的數組。這就延伸了幾個的術語:
- 階(秩)
- 形狀
1.1.1階(秩)
其實上,階就是平時我們所說的維數。
- 比如我們有一個二維的數組,那么這個階就是2
- 比如我們有一個三維的數組,那么這個階就是3
以前在寫Java的時候,可能一般接觸到的都是二維的,但在機器學習上就很可能有很高的維度,那維數我們怎么數?很簡單,我們數括號就行了。舉個例子,我們可能會看到有下面的一個數組輸出形式:
[[[9 6]
[6 9]
[8 8]
[7 9]]
[[6 1]
[3 5]
[1 7]
[9 4]]]
我們直接看第一個括號到第一個數字,有多少個括號就知道了。[[[9
可以發現有3個括號,那這個就是一個三維的數組,它的階(秩)就是3
1.1.2形狀
張量的形狀可以讓我們看到每個維度中元素的數量。
比如我們在Java中創建出一個二維的數組:int [][] array = new int[3][4]
,我們就可以知道這個數組有三行有四列。但如果我們創建出一個多維的數組,單單只用行和列就描述不清了。所以,在TensorFlow一般我們會這樣描述:
- 在維度一上元素的個數有3個,在維度二上元素的個數有4個。
- 其實說到底還是一個意思,但只是說法變了而已。
如果我們要打印上面數組的形狀時,我們可以得到這樣的結果:shape = (3,4)
。我們再看看第一篇寫”機器學習HelloWorld“的時候,再來看看當時打印的結果:shape = (60000, 28, 28)
。通過shape我們就可以得到一些信息:
- 當前數組是三維的
- 在第一維中有60000個元素
- 在第二維中有28個元素
- 在第三維中有28個元素
那我們如果拿到一個數組,怎么通過肉眼看他的shape呢?
比如說:m = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
,這個很簡單,一眼就可以看出這個是一個二維數組(矩陣),有三行三列。所以shape的結果應該是(3,3)
再來看一個:t = [[[2], [4], [6]], [[8], [10], [12]], [[14], [16], [18]]]
,從多個括號上我們可以看出,這是三維的。我們先把最外層括號去掉得到的結果是[[2], [4], [6]], [[8], [10], [12]], [[14], [16], [18]]
Ok,到這一步,我們可以理解成有三個子數組,於是我們的shape可以先寫成shape(3,?,?)
- 我們從括號上判斷一定是三維的,所以肯定是
(?,?,?)
的。從“子數組”的個數我們將第一個“?”號填充為3
隨后,我們繼續把外層的括號去除,得到這樣的結果:[2], [4], [6]
,也是有三個元素,於是我們的shape就可以填成shape(3,3,?)
最后,再把括號去掉,我們可以發現只有一個元素,於是最后的結果就是shape(3,3,1)
我們可以看下圖來鞏固一下上面所說的概念:
1.1.3 Tensor數據類型
TensorFlow 在內部將張量表示為基本數據類型的 n維數組,沒錯的。在一個數組里邊,我們總得知道我們的存進去的數據究竟是什么類型。
Tensor的數據類型如下所示:
二、特殊的張量
特殊的張量由一下幾種:
tf.Variable
— 變量tf.constant
— 常量tf.placeholder
—占位符tf.SparseTensor
—稀疏張量
這次,我們先來講講前三種(比較好理解),分別是變量、常量和占位符。
2.1 常量
常量就是常量的意思,一經創建就不會被改變。(相信大家還是能夠理解的)
在TensorFlow中,創建常量的方式十分簡單:
a = tf.constant(2)
b = tf.constant(3)
2.2變量
變量也挺好理解的(就將編程語言的概念跟這里類比就好了)。一般來說,我們在訓練過程中的參數一般用變量進行存儲起來,因為我們的參數會不停的變化。
在TensorFlow創建變量有兩種方式:
# 1.使用Variable類來創建
# tf.random_normal 方法返回形狀為(1,4)的張量。它的4個元素符合均值為100、標准差為0.35的正態分布。
W = tf.Variable(initial_value=tf.random_normal(shape=(1, 4), mean=100, stddev=0.35), name="W")
b = tf.Variable(tf.zeros([4]), name="b")
# 2.使用get_variable的方式來創建
my_int_variable = tf.get_variable("my_int_variable", [1, 2, 3], dtype=tf.int32,
initializer=tf.zeros_initializer)
值得注意的是:當我們創建完變量以后,我們每次使用之前,都需要為其進行初始化!
tf.global_variables_initializer()
2.3占位符
我最早接觸占位符這個概念的時候是在JDBC的時候。因為SQL需要傳入的參數才能確定下來,所以我們可能會寫出這樣的SQL語句:select * from user where id =?
同樣地,在TensorFlow占位符也是這么一個概念,可能需要等到運行的時候才把某些變量確定下來,於是我們就有了占位符。
在TensorFlow使用占位符也很簡單:
# 文件名需要等到運行的時候才確定下來
train_filenames = tf.placeholder(tf.string, shape=[None])
# ..省略一堆細節
# 運行的時候,通過feed_dict將占位符具體的值給確定下來
feed_dict={train_filenames: training_filenames}
上面的東西說白了在編程語言中都是有的,只是語法變了而已。
三、Flow?介紹圖和節點
我們將Flow翻譯成中文:流,所以現在是Tensor流?
其實,在TensorFlow中,使用圖 (graph) 來表示計算任務。其實TensorFlow默認會給我們一張空白的圖,一般我們會叫這個為”數據流圖“。數據流圖由有向邊和節點組成,在使用TensorFlow的時候我們會在圖中創建各種的節點,而Tensor會在這些節點中流通。所以,就叫做TensorFlow
那有人就會好奇,我們執行什么操作會創建節點呢?在TensorFlow中,節點的類型可以分為三種:
- 存儲節點:有狀態的變量操作,通常用於存儲模型參數
- 計算節點:無狀態的計算和控制操作,主要負責算法的邏輯或流程的控制
- 數據節點:數據的占位符操作,用於描述圖外輸入的數據
看到這里的同學,可能就反應過來了:原來在上面創建的變量、常量和占位符在TensorFlow中都會生成一個節點!對於這類的操作Operation(行為)一般大家會簡說成op
所以,op就是在TensorFlow中所執行的一個操作統稱而已(有可能是創建變量的操作、也有可能是計算的操作)。在TensorFlow的常見的op有以下:
其實說白了就是TensorFlow會給我們一張空白的數據流圖,我們往這張數據流圖填充(創建節點),從而實現想要效果。
- 開局一張圖,內容全靠編!
我們來看看官方的給出數據流圖的gif,加深下印象。
- TensorFlow使用數據流圖來表示計算任務
- TensorFlow使用Tensor來表示數據,Tensor在數據流圖中流動。
- 在TensorFlow中”創建節點、運算“等行為統稱為op
四、啥是session?
TensorFlow程序通常被組織成一個構建階段和執行階段. 在構建階段, op的執行步驟被描述成一個圖. 在執行階段, 使用會話執行執行圖中的op。
- 注意:因為是有向邊,所以只有等到之前的入度節點們的計算狀態完成后,當前節點才能執行操作。
說白了,就是當我們在編寫代碼的時候,實際上就是在將TensorFlow給我們的空白圖描述成一張我們想要的圖。但我們想要運行出圖的結果,那就必須通過session來執行。
舉個小例子:
import tensorflow as tf
# 創建數據流圖:y = W * x + b,其中W和b為存儲節點,x為數據節點。
x = tf.placeholder(tf.float32)
W = tf.Variable(1.0)
b = tf.Variable(1.0)
y = W * x + b
# =========如果不使用session來運行,那上面的代碼只是一張圖。我們通過session運行這張圖,得到想要的結果
with tf.Session() as sess:
tf.global_variables_initializer().run() # Operation.run
fetch = y.eval(feed_dict={x: 3.0}) # Tensor.eval
print(fetch) # fetch = 1.0 * 3.0 + 1.0
4.1 Fetch是啥?
Fetch就時候可以在session.run
的時候傳入多個op(tensor),然后返回多個tensor(如果只傳入一個tensor的話,那就是返回一個tensor)
4.2tensor.eval()和Operation.run()
有的同學在查閱資料的時候,發現可能調用的不是session.run
,而是tensor.eval()和Operation.run()
。其實,他們最后的調用的還是session.run
。不同的是session.run
可以一次返回多個tensor(通過Fetch)。
最后
曾經看到一段話總結得不錯:
- 使用 tensor 表示數據.
- 使用圖 (graph) 來表示計算任務.
- 在會話(session)中運行圖
- 通過
變量 (Variable)
維護狀態.
TensorFlow 是一個編程系統, 使用圖來表示計算任務. 圖中的節點被稱之為 op (operation 的縮寫). 一個 op 獲得 0 個或多個
Tensor
, 執行計算, 產生 0 個或多個Tensor
. 每個 Tensor 是一個類型化的多維數組.
這篇文章簡單講了TensorFlow是啥意思以及一些基礎的概念。但我也只是簡單以我的理解方式來說了一些常見概念。里頭的知識點還是比較多的(比如創建變量的時候一般我們會指定哪些參數….),這些就交由大家去官網、博客、書籍去學習了。
我相信,只要了解了這些概念,那學習一定可以事半功倍!
下一篇TensorFlow文章敬請期待~
參考資料:
- https://juejin.im/post/5b345a49f265da599c561b25
- https://github.com/geektime-geekbang/tensorflow-101/tree/master/notebook-examples
樂於輸出干貨的Java技術公眾號:Java3y。公眾號內有200多篇原創技術文章、海量視頻資源、精美腦圖,不妨來關注一下!
覺得我的文章寫得不錯,不妨點一下贊!