第三章:TensorFlow入門
* 3.1
TensorFlow計(jì)算模型 - 計(jì)算圖
? ? ? ?* 3.1.1
計(jì)算圖的概念
? ? ? ?* 3.1.2
計(jì)算圖的使用
* 3.2
TensorFlow數(shù)據(jù)模型 - 張量
? ? ? ?* 3.2.1
張量的概念
? ? ? ?* 3.2.2
張量的使用
* 3.3
TensorFlow運(yùn)行模型 - 會話
* 3.4
TensorFlow實(shí)現(xiàn)神經(jīng)網(wǎng)絡(luò)
? ? ? ?* 3.4.1
TensorFlow游樂場及神經(jīng)網(wǎng)絡(luò)簡介
? ? ? ?* 3.4.2
前向傳播算法簡介
? ? ? ?* 3.4.3
神經(jīng)網(wǎng)絡(luò)參數(shù)&TensorFlow變量
? ? ? ?* 3.4.4
通過TensorFlow訓(xùn)練神經(jīng)網(wǎng)絡(luò)模型
? ? ? ?* 3.4.5
完整神經(jīng)網(wǎng)絡(luò)樣例程序
-3.1- TensorFlow計(jì)算模型 - 計(jì)算圖
計(jì)算圖是 TensorFlow 中最基本的 一個(gè)概念屡立, TensorFlow 中的所有計(jì)算都會被轉(zhuǎn)化為計(jì) 算圖上的節(jié)點(diǎn) 搔啊。
-3.1.1- 計(jì)算圖的概念
TensorFlow 的名字中己經(jīng)說明了它最重要的兩個(gè)概念一一Tensor 和 Flow。
Tensor
就是張量。
張量這個(gè)概念在數(shù)學(xué)或者物理學(xué)中可以有不同的解釋 , 但在本書中并不強(qiáng)調(diào)它本身 的含義 。
在 TensorFlow 中中姜,張量可以被簡單地理解為多維數(shù)組 消玄, 在 3.2 節(jié)中將對張量做更 加詳細(xì)的介紹 跟伏。
如果說 TensorFlow 的第一個(gè)詞 Tensor表明了它的數(shù)據(jù)結(jié)構(gòu),那么 Flow 則 體現(xiàn)了它的計(jì)算模型翩瓜。
Flow
翻譯成中文就是“流”受扳,它直觀地表達(dá)了張量之 間通過計(jì)算相 互轉(zhuǎn)化的過程。
TensorFlow 是一個(gè)通過計(jì)算圖 的形式來表述計(jì)算的編程系統(tǒng) 兔跌。
TensorFlow 中的每一個(gè)計(jì)算都是計(jì)算圖上 的一個(gè)節(jié)點(diǎn)勘高,而節(jié)點(diǎn)之間的邊描述了計(jì)算之間的依賴關(guān)系。
tf的圖Graph中:
*
每一個(gè)節(jié)點(diǎn)
都是一個(gè)運(yùn)算
坟桅;
*
每一條邊
可以理解為被運(yùn)算節(jié)點(diǎn)驅(qū)使流動起來的Tensor
华望,代表了計(jì)算之間的依賴關(guān)系 ,如果一個(gè)運(yùn)算的輸入依賴于另一個(gè)運(yùn)算的輸出仅乓,那么這兩個(gè)運(yùn)算有依賴關(guān)系赖舟。
上圖展示了通過 TensorBoard1畫出來的第 2 章中兩個(gè)向量相加樣例的計(jì)算圖。
在上圖中:
*
a節(jié)點(diǎn)
和b節(jié)點(diǎn)
是兩個(gè)常量(賦值運(yùn)算)節(jié)點(diǎn)
夸楣,賦值運(yùn)算不需要依賴輸入Tensor宾抓,不依賴任何其他計(jì)算(輸出的Tensor)子漩,所以沒有輸入邊;
*
而add節(jié)點(diǎn)
這個(gè)向量相加運(yùn)算節(jié)點(diǎn)
則需要指定輸入的兩個(gè)Tensor石洗,于是圖中的add節(jié)點(diǎn)
有兩個(gè)輸入邊:一條從a到add的邊 & 一條從b到add的邊幢泼。
*
沒有任何其他計(jì)算節(jié)點(diǎn)
依賴 add 的結(jié)果,于是代表加法的節(jié)點(diǎn) add 沒有任何指向其他節(jié)點(diǎn)的邊讲衫。
所有 TensorFlow 的程序都可以通過類似圖 3-1 所示的計(jì)算圖的形式來表示缕棵,這就是 TensorFlow 的基本計(jì)算 模型。
-3.1.2- 計(jì)算圖的使用
TensorFlow程序一般可以分為兩個(gè)階段涉兽。
*
在第一個(gè)階段需要定義計(jì)算圖中所有的計(jì)算
挥吵。 比如在第 2 章的向量加法樣例程序中首先定義了兩個(gè)輸入,然后定義了 一個(gè)計(jì)算來得到它 們的和花椭。
*
第二個(gè)階段為執(zhí)行計(jì)算圖中所有的計(jì)算
忽匈,這個(gè)階段將在 3.3 節(jié)中介紹。以下代碼給出了計(jì)算定義階段的樣例矿辽。
import tensorflow as tf
a = tf.constant([1.0, 2.0], name="a")
b = tf.constant([2.0, 3.0], name="b")
result = a + b
* 載入 TensorFlow
在 Python 中一般會采用“impo口tensorflowas tf”的形式來載入 TensorFlow丹允,這樣:
*
可以使用“tf”來代替“tensorflow”作為模塊名稱,使得整個(gè)程序更加簡潔袋倔。這是 TensorFlow 中非常常用的技巧雕蔽,在本書后面的章節(jié)中將會全部采用這種加載萬式 。
在這個(gè)過程中宾娜,TensorFlow 會自動
將定義的計(jì)算轉(zhuǎn)化為
計(jì)算圖上的節(jié)點(diǎn) 批狐。在 TensorFlow 程序中,系統(tǒng)會自動維護(hù)
一個(gè)默認(rèn)的計(jì)算圖前塔,通過 tf.get_default_graph 函數(shù)可以獲取當(dāng)前默認(rèn)的計(jì)算圖嚣艇。
以下代碼示意了如何獲取默認(rèn)計(jì)算圖以及如何查看一個(gè)運(yùn)算所屬的計(jì)算圖 。
print(a.graph is tf.get_default_graph())
# 判斷張量a所屬的計(jì)算圖是否是當(dāng)前載入TensorFlow后默認(rèn)的計(jì)算圖
# 該步輸出將會為True
通過a.graph可以查看張量a所屬的計(jì)算圖
因?yàn)闆]有特意指定华弓,所以這個(gè)計(jì)算圖應(yīng)該等于當(dāng)前默認(rèn)的計(jì)算圖
所以上面的操作輸出值為True食零。
* 指定新的計(jì)算圖
除了使用默認(rèn)的計(jì)算圖, TensorFlow 支持通過
tf.Graph 函數(shù)
來生成新的計(jì)算圖
寂屏。不同計(jì) 算圖上的張量和運(yùn)算都不會共享贰谣。
以下代碼示意了如何在不同計(jì)算圖上定義和使用變量。
import tensorflow as tf
g1 = tf.Graph()
with g1.as_default():
# 在計(jì)算圖g1中定義變量"v"迁霎,并設(shè)置初始值為0吱抚。
v = tf.get_variable(
"v",
initializer=tf.zeros_initializer(shape=[1])
)
g2 = tf.Graph()
with g2.as_default():
# 在計(jì)算圖g2中定義變量"v",并設(shè)置初始值為1考廉。
v = tf.get_variable(
"v",
initializer=tf.ones_initializer(shape=[1])
)
with tf.Session(graph=g1) as sess:
# 在計(jì)算圖g1中讀取變量v的取值
tf.global_variables_initializer().run()
with tf.variable_scope("", reuse=True):
# 在計(jì)算圖g1中秘豹,變量v的取值應(yīng)該是0
# 所以下面這行會輸出[0. ]
print(sess.run(tf.get_variable("v")))
with tf.Session(graph=g2) as sess:
# 在計(jì)算圖g2中讀取變量v的取值
tf.global_variables_initializer().run()
with tf.variable_scope("", reuse=True):
# 在計(jì)算圖g2中,變量v的取值應(yīng)該為1
# 所以下面這行會輸出[1. ]
print(sess.run(tf.get_variable("v")))
以上代碼產(chǎn)生了兩個(gè)計(jì)算圖芝此,每個(gè)計(jì)算圖中定義了 一個(gè)名字為“ v”的變量憋肖。
*
在計(jì)算圖 g1 中因痛,將v初始化為0
*
在計(jì)算圖 g2 中,將v初始化為1
可以看到當(dāng)運(yùn)行不同計(jì)算圖時(shí)岸更,變量v的值也是不一樣的鸵膏。
TensorFlow中的計(jì)算圖可以:
*
用來隔離
張量和計(jì)算
*
提供了管理
張量和計(jì)算的機(jī)制
計(jì)算圖可以通過 tf.Graph.device 函數(shù)來指定運(yùn)行計(jì)算的設(shè)備。這為 TensorFlow 使用GPU提供了機(jī)制怎炊。以下程序可以將加法計(jì)算跑在GPU上谭企。
g = tf.Graph()
# 指定計(jì)算運(yùn)行的設(shè)備
with g.device('/gpu:0'):
result = a + b
使用GPU的具體方法將在第12章詳述。
有效地整理 TensorFlow程序中的
資源
也是計(jì)算圖的一個(gè)重要功能评肆。
在一個(gè)計(jì)算圖中债查,可以通過集合 Ccollection)
來管理不同類別的資 源。
比如通過tf.add_to_collection()
函數(shù)可以將資源加入一個(gè)或多個(gè)集合中瓜挽,
然后通過tf.get_collection()
獲取一個(gè)集合里面的所有資源盹廷。
這里的資源
可以是:
*
張量
*
變量
*
運(yùn)行 TensorFlow 程序所需要的隊(duì)列資源
*
等等
為了方便使用,TensorFlow 也自動管理了一些 最常用的集合 久橙, 表3-1總結(jié)了最常用的幾個(gè)自動維護(hù)的集合俄占。
集合名稱 | 集合內(nèi)容 | 使用場景 |
---|---|---|
tf.GraphKeys.VARIABLES | 所有變量 | 持久化TensorFlow |
tf.GraphKeys.TRAINABLE_VARIABLE | 可學(xué)習(xí)的、待訓(xùn)練的參數(shù) | 模型訓(xùn)練淆衷、生成模型可視化內(nèi)容 |
tf.GraphKeys.SUMMERIES | 日志生成相關(guān)的張量 | TensorFlow計(jì)算可視化 |
tf.GraphKeys.QUEUE_RUNNERS | 處理輸入的QueueRunner | 輸入處理 |
tf.GraphKeys.MOVING_AVERAGE_VARIABLES | 所有計(jì)算了滑動平均值的變量 | 計(jì)算變量的滑動平均值 |
-3.2- TensorFlow數(shù)據(jù)模型 - 張量
這一節(jié)將介紹 TensorFlow 中另外一個(gè)基礎(chǔ)概念一一張量 缸榄。
張量是 TensorFlow 管理數(shù)據(jù)的形式。
-3.2.1- 張量的概念
??*
Tensor
是對運(yùn)算節(jié)點(diǎn)
的計(jì)算結(jié)果/輸出邊
的引用
??
在 TensorFlow 程序中祝拯,所有的數(shù)據(jù)都通過
張量
的形式來表示甚带。
從功能的角度上看,張量可以被簡單理解 為多維數(shù)組
佳头。
*
其中零階張量
表示標(biāo)量(scalar)鹰贵,也就是一個(gè)數(shù)
;
*
第一階張量
為向量(vector), 也就是一個(gè)一維數(shù)組
畜晰;
*
第n階張量
可以理解為一個(gè)n維數(shù)組
砾莱。
但
張量
在 TensorFlow 中的實(shí)現(xiàn)并不是
直接采用數(shù)組
的形式瑞筐,
它只是對TensorFlow 中運(yùn)算結(jié)果的引用
凄鼻。
在張量中并沒有真正保存數(shù)字,它保存的是如何得到這些數(shù)字的計(jì)算過程
菌赖。
??TensorFlow中的張量
本身瓣俯,就是對計(jì)算節(jié)點(diǎn)
的結(jié)果
的引用背传。
還是以向量加法為例,當(dāng)運(yùn)行 如下代碼時(shí)峭范,并不會得到加法的結(jié)果,而會得到對結(jié)果的一個(gè)引用瘪贱。
import tensorflow as tf
# tf.constant本身是一個(gè)計(jì)算
# tf.constant是一個(gè)常量賦值計(jì)算
# 這個(gè)計(jì)算的結(jié)果為一個(gè)張量纱控,保存在變量a中
a = tf.constant([1.0, 2.0], name="a")
b = tf.constant([2.0, 3.0], name="b")
result = tf.add(a, b, name="add")
print(result)
# 輸出:
# Tensor ("add:0", shape= (2,), dtype=float32)
通過以上代碼可以看出
TensorFlow中張量
和Numpy中的數(shù)組
不同辆毡,TensorFlow計(jì)算的結(jié)果不是一個(gè)具體的數(shù)字,而是一個(gè)張量的結(jié)構(gòu)甜害。
從上面代碼的運(yùn)行結(jié)果可以看出舶掖,一個(gè)張量主要保存了三個(gè)屬性:
*
名字(name)
*
維度(shape)
*
類型(type)
* 張量的第一個(gè)屬性:名字(name)
張量的第一個(gè)屬性
名字(name)
:
*
不僅是一個(gè)張量的唯一標(biāo)識符
,
*
它同樣也給出了這個(gè)張量是如何計(jì)算出來的
尔店。
*
TensorFlow的計(jì)算都可以通過計(jì)算圖的模型來建立眨攘, 計(jì)算圖上的每一個(gè)節(jié)點(diǎn)代表了一個(gè)計(jì)算;計(jì)算節(jié)點(diǎn)計(jì)算完的結(jié)果就保存在張量之中嚣州。
*
所以張量
和計(jì)算圖上計(jì)算節(jié)點(diǎn)
所代表的計(jì)算結(jié)果
是對應(yīng)的鲫售。
這樣張量的命名就可 以通過 “node:src_output”的形式來給出。其中:
*
node 為節(jié)點(diǎn)的名稱
*
src_output表示當(dāng)前張量來自節(jié)點(diǎn)的第幾個(gè)輸出该肴。
比如上面代碼打出來的“add:0”就說明了 result這個(gè)張量是計(jì)算節(jié)點(diǎn)“add” 輸出的第一個(gè)結(jié)果(編號從 0 開始)情竹。
* 張量的第二個(gè)屬性:維度(shape)
張量的第二個(gè)屬性是張量的維度( shape)。這個(gè)屬性描述了一個(gè)張量的維度信息匀哄。比 如上面樣例中 shape=(2鲤妥,)說明了張量 result是一個(gè)一維數(shù)組, 這個(gè)數(shù)組的長度為 2拱雏。維度是 張量一個(gè)很重要的屬性 棉安, 圍繞張量的維度 TensorFlow 也給出了很多有用的運(yùn)算,在這里先 不 一一 列舉铸抑,在后面的章節(jié)中將使用到部分運(yùn)算贡耽。
* 張量的第三個(gè)屬性:類型(type)
張量的第三個(gè)屬性是類型( type),每一個(gè)張量會有一個(gè)唯一的類型鹊汛。 TensorFlow會對 參與運(yùn)算的所有張量進(jìn)行類型的檢查 蒲赂, 當(dāng)發(fā)現(xiàn)類型不匹配時(shí)會報(bào)錯。 比如運(yùn)行以下程序時(shí) 就會得到類型不匹配的錯誤 :
import tensorflow as tf
a = tf.constant([1, 2], name="a")
b = tf.constant([2.0, 3.0], name="b")
result = a + b
這段程序和上面的樣例基本一模一樣刁憋,唯一不同的是把其中一個(gè)加數(shù)的小數(shù)點(diǎn)去掉了滥嘴。
這會使得加數(shù) a 的類型為整數(shù)而加數(shù) b 的類型為實(shí)數(shù),這樣程序會報(bào)類型不匹配的錯誤 :
ValueError: Tensor conversion requested dtype int32 for Tensor
with dtype float32 : 'Tensor ("b : 0"至耻, shape=(2, ), dtype=float32 )'
如果將第一個(gè)加數(shù)指定成實(shí)數(shù)類型
a= tf.constant([l, 2], name="a”, dtype=tf.float32)
, 那么兩個(gè)加數(shù)的類型相同就不會報(bào)錯了若皱。
如果不指定類型, TensorFlow會給出默認(rèn)的類型尘颓,比如不帶小數(shù)點(diǎn)的數(shù)會被默認(rèn)為 int32走触,帶小數(shù)點(diǎn)的會默認(rèn)為 float32。
因?yàn)槭褂媚J(rèn)類型有可能會導(dǎo)致潛在的類型不匹配問題疤苹,所以一般建議通過指定 dtype 來明確指出變量或者常量的類型互广。
* TensorFlow支持的數(shù)據(jù)類型
TensorFlow支持 14種不同的類型, 主要包括了:
*
實(shí)數(shù)(tf.float32、 tf.float64)惫皱、
*
整數(shù)(tf.int8像樊、 tf.int16、 tf.int32旅敷、 tf.int64凶硅、 tf.uint8)、
*
布爾型(tf.bool)
*
復(fù)數(shù)(tf.complex64扫皱、 tf.complex128 )
-3.2.2- 張量的使用
張量使用主要可以總結(jié)為兩大類 :
*
第一類用途是對中間計(jì)算結(jié)果的引用
*
使用張量的第二類情況是當(dāng)計(jì)算圖構(gòu)造完成之后足绅,張量可以用來獲得計(jì)算結(jié)果
,也就是得到真實(shí)的數(shù)字
* 張量的使用1: 對中間計(jì)算結(jié)果的引用
當(dāng)一個(gè)計(jì)算包含很多中間結(jié)果時(shí)韩脑,使用張量可以大大提高代碼的可讀性氢妈。以下為使用張量和不使用張量記錄中間結(jié)果
來完成向量相加的功能的代碼對比:
import tensorflow as tf
# 使用張量記錄中間結(jié)果
a = tf.constant([1.0, 2.0], name="a")
b = tf.constant([2.0, 3.0], name="b")
result = a + b
# 直接計(jì)算向量的和,這樣可讀性會比較差
result = tf.constant([1.0, 2.0], name="a") + tf.constant([2.0, 3.0], name="b")
從上面的樣例程序可以看到段多,a和b其實(shí)就是對
常量生成
這個(gè)運(yùn)算的結(jié)果
的引用首量,
*
這樣在做加法時(shí)就可以直接使用
這兩個(gè)變量,而不需要再去生成這些常量进苍。 當(dāng)計(jì)算的復(fù)雜度增加時(shí)(比如在構(gòu)建深層神經(jīng)網(wǎng)絡(luò)時(shí))加缘,通過張量來引用計(jì)算的中間結(jié)果可以使代碼的可閱讀性
大大提升 。
*
同時(shí)觉啊,通過張量來存儲中間結(jié)果可以方便獲取中間結(jié)果
拣宏。 比如在卷積神經(jīng)網(wǎng)絡(luò)中,卷積層或者池化層有可能改變張量的維度杠人,通過result.get_shape() 函數(shù)來獲取結(jié)果張量的維度信息可以免去人工計(jì)算的麻煩勋乾。
* 張量的使用2: 獲得計(jì)算結(jié)果
使用張量的第二類情況是:當(dāng)計(jì)算圖構(gòu)造完成之后,張量可以用來獲取計(jì)算結(jié)果嗡善,也就是得到真實(shí)的數(shù)字辑莫。雖然張量本身沒有存儲具體的數(shù)字,但是通過3.3節(jié)中介紹的會話罩引,就可以得到這些具體的數(shù)字各吨。比如在上述代碼中使用tf.Session().run(result)
語句得到計(jì)算結(jié)果。
-3.3- TensorFlow運(yùn)行模型 - 會話
前面的兩節(jié)介紹了 TensorFlow 是如何
組織數(shù)據(jù)
和定義運(yùn)算
的袁铐。本節(jié)將介紹如何使用 TensorFlow 中的會話( session)來執(zhí)行
定義好的運(yùn)算揭蜒。
會話擁有并管理 TensorFlow 程序運(yùn)行時(shí)的所有資源。所有計(jì)算完成之后需要關(guān)閉會話來幫助系統(tǒng)回收資源昭躺,否則就可能出現(xiàn)資源泄漏的問題忌锯。 TensorFlow 中使用會話
的模式一般有兩種:
*
明確調(diào)用會話生成函數(shù)
和關(guān)閉會話函數(shù)
*
通過Python的with關(guān)鍵字
和上下文管理器
來使用會話
* 明確調(diào)用會話生成函數(shù)和關(guān)閉會話函數(shù)
import tensorflow as tf
# 創(chuàng)建一個(gè)會話
sess = tf.Session()
# 使用創(chuàng)建好的會話來得到關(guān)心的運(yùn)算的結(jié)果
# 比如可以調(diào)用sess.run(result)
# 來得到3.1節(jié)樣例中張量result的取值
sess.run(...)
# 關(guān)閉會話使得本次運(yùn)行中使用到的資源可以背釋放
sess.close()
使用這種模式,在所有計(jì)算完成之后领炫,需要明確調(diào)用Session.close()函數(shù)來關(guān)閉會話并釋放資源。
* 通過Python的with關(guān)鍵字和上下文管理器來使用會話
然而张咳,當(dāng)程序因?yàn)楫惓6顺鰰r(shí)帝洪,關(guān)閉會話的函數(shù)可能就不會被執(zhí)行從而導(dǎo)致資源泄漏 似舵。
為了解決異常退出時(shí)資源釋放的問題, TensorFlow 可以通過 Python 的上下文 管理器來使用會話葱峡。
import tensorflow as tf
# 創(chuàng)建一個(gè)會話
# 并通過Pythonx的with關(guān)鍵字和上下文管理器來管理這個(gè)會話
with tf.Session() as sess:
# 通過創(chuàng)建好的會話來計(jì)算關(guān)心的結(jié)果
sess.run(...)
# 不需要調(diào)用sess.close()函數(shù)來關(guān)閉會話
# 當(dāng)上下文退出時(shí)砚哗,會話關(guān)閉和資源釋放也自動完成了
通過 Python 上下文管理器的機(jī)制,只要將所有的計(jì)算放在 “with”的內(nèi)部就可以 砰奕。 當(dāng)上下文管理器退出時(shí)候會自動釋放所有資源蛛芥。這樣
*
既解決了因?yàn)楫惓M顺鰰r(shí)資源釋放的問題,
*
同時(shí)也解決了忘記調(diào)用 Session.close() 函數(shù)而產(chǎn)生的資源泄漏军援。
????????????????????????????????????????????????
* 默認(rèn)的計(jì)算圖 & 默認(rèn)的會話
3.1 節(jié)介紹過 TensorFlow在被載入的時(shí)候仅淑,會自動生成一個(gè)默認(rèn)的
計(jì)算圖
,如果沒有特殊指定胸哥,運(yùn)算會自動加入這個(gè)計(jì)算圖中涯竟。
TensorFlow 中的會話
也有類似的機(jī)制,但 TensorFlow 不會自動生成默認(rèn)的會話空厌,而是需要手動指定 庐船。
??當(dāng)通過tf.Session()初始化一個(gè)會話對象
之后,需要將其指定
為默認(rèn)的會話
嘲更;
??在指定
完默認(rèn)的會話
之后筐钟,才可以通過tf.Tensor.eval()
函數(shù)來計(jì)算一個(gè)張量的取值。
以下代碼展示了赋朦,通過設(shè)定默認(rèn)會話
盗棵,計(jì)算張量的取值:
sess = tf.Session()
with sess.as_default():
print(result.eval())
以下代碼也可以完成相似的功能:
sess = tf.Session()
# 以下兩個(gè)命令有相似的功能
print(sess.run(result))
print(result.eval(session=sess))
* Python 交互式環(huán)境
在交互式環(huán)境下(比如 Python 腳本或者Jupyter的編輯器下),通過設(shè)置
默認(rèn)會話
的方式來獲取張量的取值
更加方便北发。
所以TensorFlow提供了一種在交互式環(huán)境下直接構(gòu)建默認(rèn)會話的函數(shù)纹因。這個(gè)函數(shù)就是tf.lnteractiveSession()
。
使用這個(gè)函數(shù)會自動將生成的會話注冊為默認(rèn)會話琳拨。
以下代碼展示了 tf.InteractiveSession() 函數(shù)的用法:
import tensorflow as tf
a = tf.constant([1.0, 2.0], name="a")
b = tf.constant([2.0, 3.0], name="b")
result = a + b
sess = tf.InteractivateSession()
print(result.eval())
sess.close()
通過tf.InteractiveSession()可以省去
將產(chǎn)生的會話注冊為默認(rèn)會話的過程
無論通過哪種方法瞭恰,都可以通過ConfigProto Protocol Buffer
來配置需要生成的會話,下面給出了通過ConfigProto
配置會話的方法:
config = tf.ConfigProto(
allow_soft_placement=True,
log_device_placement=True
)
sess1 = tf.InteractiveSession(config=config)
sess2 = tf.Session(config=config)
-3.4- TensorFlow實(shí)現(xiàn)神經(jīng)網(wǎng)絡(luò)
在這一節(jié)中狱庇,將結(jié)合神經(jīng)網(wǎng)絡(luò)的功能進(jìn)一步介紹如何通過TensorFlow來實(shí)現(xiàn)神經(jīng)網(wǎng)絡(luò)惊畏。
*
3.4.1節(jié):TensorFlow游樂場 & 神經(jīng)網(wǎng)絡(luò)
*
3.4.2 節(jié):前向傳播( Forward-Propagation)
算法
*
3.4.3 節(jié):TensorFlow中的變量
& 神經(jīng)網(wǎng)絡(luò)參數(shù)的表達(dá)
*
3.4.4 節(jié):神經(jīng)網(wǎng)絡(luò)反向傳播(Back-Propagation)
算法
*
3.4.5 節(jié):完整的TensorFlow程序在隨機(jī)的數(shù)據(jù)上訓(xùn)練一個(gè)簡單的神經(jīng)網(wǎng)絡(luò)
-3.4.1- TensorFlow游樂場及神經(jīng)網(wǎng)絡(luò)簡介
TensorFlow游樂場:http://playground.tensorflow.org
上圖中可以先著重關(guān)注兩個(gè)框:
*
Feature框:輸入每條樣本(x,y)
的各種特征:
? ? 、密任、颜启、、浪讳、缰盏、
*
隱藏層:由神經(jīng)網(wǎng)絡(luò)各個(gè)節(jié)點(diǎn)
和他們之間的帶權(quán)重的連線
組成
? ? 節(jié)點(diǎn)
:可以在某種意義上理解為特征提取
? ? 帶權(quán)重的連線
:對一層特征提取節(jié)點(diǎn)的各結(jié)果進(jìn)行有權(quán)重處理
-3.4.2- 前向傳播算法簡介
前向傳播算法可以很容易地轉(zhuǎn)化成矩陣乘法
的形式,在TensorFlow中矩陣乘法是非常容易實(shí)現(xiàn)的。
以下TensorFlow 程序?qū)崿F(xiàn)了圖 3-5 所示神經(jīng)網(wǎng)絡(luò)的前向傳播過程:
import tensorflow as tf
a = tf.matmul(x, w1) # 向量x和權(quán)重矩陣w1相乘口猜,得到向量a
y = tf.matmul(a, w2) # 向量a和權(quán)重矩陣w2相乘负溪,得到向量y
其中
tf.matmul()
實(shí)現(xiàn)了矩陣乘法的功能。
到此為止己經(jīng)詳細(xì)地介紹了神經(jīng)網(wǎng)絡(luò)的前向傳 播算法济炎,并且給出了TensorFlow程序來實(shí)現(xiàn)這個(gè)過程 川抡。
在之后的章節(jié)中會繼續(xù)介紹更加復(fù)雜的
神經(jīng)元
結(jié)構(gòu):
*
偏置 (bias)
*
激活函數(shù) (activation缸nction)
等
也會介紹更加復(fù)雜的神經(jīng)網(wǎng)絡(luò)結(jié)構(gòu)
:
*
卷積神經(jīng)網(wǎng)絡(luò)
*
LSTM 結(jié)構(gòu)
等
對于這些更加復(fù)雜的神經(jīng)網(wǎng)絡(luò), TensorFlow 也提 供了很好的支持须尚,后面的章節(jié)中再詳細(xì)介紹崖堤。
-3.4.3- 神經(jīng)網(wǎng)絡(luò)參數(shù)&TensorFlow變量
神經(jīng)網(wǎng)絡(luò)中的
待訓(xùn)練參數(shù)
是神經(jīng)網(wǎng)絡(luò)實(shí)現(xiàn)分類或者回歸問題中重要的部分。
本節(jié)將更加具體地介紹TensorFlow是如何組織耐床、保存以及使用神經(jīng)網(wǎng)絡(luò)中的待訓(xùn)練參數(shù)的密幔。
在TensorFlow中,變量(tf.Variable)
的作用就是保存和更新神經(jīng)網(wǎng)絡(luò)中的(待訓(xùn)練)參數(shù)
咙咽。
* 變量初始化 & 隨機(jī)值
和其他編程語言類似老玛, TensorFlow中的變量也需要指定
初始值
。
因?yàn)樵谏窠?jīng)網(wǎng)絡(luò)中钧敞,給參數(shù)賦予隨機(jī)初始值
最為常見蜡豹,所以 一般也使用隨機(jī)數(shù)給TensorFlow中的變量初始化。
下面一段代碼給出了一種在TensorFlow 中聲明一個(gè)的矩陣變量的方法:
import tensorflow as tf
weights = tf.Variable(
tf.random_normal([2, 3], stddev=2)
)
*
這是一個(gè)變量聲明
運(yùn)算節(jié)點(diǎn)
*
該運(yùn)算節(jié)點(diǎn)的輸出Tensor的名稱為weights
這段代碼調(diào)用了TensorFlow的
變量聲明函數(shù)tf.Variable()
溉苛。
在變量聲明函數(shù)中給出了初始化這個(gè)變量
的方法镜廉。
TensorFlow 中變量的初始值可以設(shè)置成隨機(jī)數(shù)、 常數(shù)或者是通過其 他變量的初始值計(jì)算得到愚战。
在上面的樣例中娇唯,tf.random_normal([2, 3], stddev=2)
會產(chǎn)生一個(gè)2*3的矩陣,矩陣中的元素
是均值為0
寂玲,標(biāo)準(zhǔn)差為2
的隨機(jī)數(shù)塔插。 tf.random normal()函數(shù)可以通過參數(shù)mean
來指定平均值,在沒有指定時(shí)默認(rèn)為0拓哟。
通過滿足正態(tài)分布的隨機(jī)數(shù)來初始化神經(jīng)網(wǎng)絡(luò)中的參數(shù)是一個(gè)非常常用的方法想许。 除了正態(tài)分布的隨機(jī)數(shù), TensorFlow 還提供了一些其他的隨機(jī)數(shù)生成器断序, 表3-2列出了TensorFlow目前支持的所有隨機(jī)數(shù)生成器流纹。
函數(shù)名稱 | 隨機(jī)數(shù)分布 | 主要參數(shù) |
---|---|---|
tf.random_normal() |
正態(tài)分布 | 平均值、標(biāo)準(zhǔn)差违诗、取值類型 |
tf.truncated_normal() |
正態(tài)分布漱凝,但是如果隨機(jī)出來的數(shù)值 偏離平均值超過2這個(gè)標(biāo)準(zhǔn)差, 那么這個(gè)數(shù)將會被重新隨機(jī)生成 |
平均值诸迟、標(biāo)準(zhǔn)差茸炒、取值類型 |
tf.random_uniform() |
均勻分布 | 平均值愕乎、標(biāo)準(zhǔn)差、取值類型 |
tf.random_gamma() |
Gamma分布 | 平均值扣典、標(biāo)準(zhǔn)差妆毕、取值類型 |
TensorFlow也支持通過常數(shù)來初始化一個(gè)變量慎玖。表 3-3給出了 TensorFlow 中常用的常 量聲 明方法 贮尖。
函數(shù)名稱 | 功能 | 樣例 | 結(jié)果 |
---|---|---|---|
tf.zeros() |
產(chǎn)生一個(gè)全0的數(shù)組?
|
tf.zeros([2,3], int32) | [[0, 0, 0], [0, 0, 0]] |
tf.ones() |
產(chǎn)生一個(gè)全1的數(shù)組?
|
tf.ones([2,3], int32) | [[1, 1, 1], [1, 1, 1]] |
tf.fill() |
產(chǎn)生一個(gè)元素全部為指定數(shù)字的數(shù)組?
|
tf.fill([2,3], 9) | [[9, 9, 9], [[9, 9, 9]] |
tf.constant() |
產(chǎn)生一個(gè)給定值的常量
|
tf.constant([1,2,3]) | [1, 2, 3] |
神經(jīng)網(wǎng)絡(luò)中的偏置項(xiàng)(bias)
,通常會使用常數(shù)
來初始化趁怔,以下代碼給出一個(gè)樣例:
import tensorflow as tf
bias = tf.Variable(
tf.zeros([3])
)
以上代碼是一個(gè)
聲明變量
的運(yùn)算節(jié)點(diǎn)湿硝,該運(yùn)算節(jié)點(diǎn)的輸出的是名為"bias
"的Tensor;
在聲明變量的同時(shí)润努,進(jìn)行了初始化
关斜,初值為長度為3、元素值全為0的向量铺浇。
除了使用隨機(jī)數(shù)或者常數(shù)痢畜,TensorFlow也支持通過其他變量的初始值
來初始化新的變量。 以下代碼給出了具體的方法:
import tensorflow as tf
weights = tf.Variable(tf.random_normal([2, 3], stddev=2))
w1 = tf.Variable(weights.initialized_value())
w2 = tf.Variable(weights.initialized_value () * 2.0)
在以上的代碼中鳍侣,w2的初值被置為了與weights變量相同丁稀,w3的初值則是weights初值的兩倍。
* 變量初始化
在TensorFlow中倚聚,一個(gè)
變量定義運(yùn)算節(jié)點(diǎn)
的輸出Tensor
的值在被使用之前线衫,這個(gè)變量定義的初始化過程
需要被明確地調(diào)用
。
即:
變量定義運(yùn)算節(jié)點(diǎn)
→明確初始化
→使用該節(jié)點(diǎn)的輸出Tensor
以下樣例介紹了如何通過變量實(shí)現(xiàn)神經(jīng)網(wǎng)絡(luò)的參數(shù)并實(shí)現(xiàn)前向傳播的過程:
import tensorflow as tf
# 聲明w1惑折、w2兩個(gè)變量
# 這里還通過seed參數(shù)設(shè)置了隨機(jī)種子
# 這樣可以保證每次運(yùn)行得到的結(jié)果是一樣的
w1 = tf.Variable(tf.random_normal((2,3), stddev=1, seed=1))
w2 = tf.Variable(tf.random_normal((2,3), stddev=1, seed=1))
# 暫時(shí)將輸入的特征向量定義為一個(gè)常量
# 注意這里x是一個(gè)1*2維的矩陣
x = tf.constant([[0.7, 0.9]])
# 通過3.4.2節(jié)描述的前向傳播算法獲得神經(jīng)網(wǎng)絡(luò)的輸出
a = tf.matmul(x, w1)
y = tf.matmul(a, w2)
sess = td.Session()
# 與3.4.2節(jié)的計(jì)算不同:
# ??這里不能??直接通過sess.run(y)來計(jì)算y的取值
# 因?yàn)橹岸x了w1授账、w2這兩個(gè)??變量??
# 但是卻沒有明確對其初始化
# 以下兩行分別明確地初始化了w1、w2兩個(gè)變量
sess.run(w1.initializer) # 初始化w1
sess.run(w2.initializer) # 初始化w2
print(sess.run(y)) # 輸出[[3.95757794]]
sess.close()
以上程序?qū)崿F(xiàn)了神經(jīng)網(wǎng)絡(luò)的前向傳播過程惨驶。
從這段代碼可以看到白热,當(dāng)聲明了變量w1、w2之后粗卜,可以通過w1和w2來定義神經(jīng)網(wǎng)絡(luò)的前向傳播過程
屋确,并得到中間結(jié)果a和最后答案y。
在 TensorFlow 程序的第二步會聲明一個(gè)會話( session)
休建,并通過會話計(jì)算結(jié)果
乍恐。
在上面 的樣例中, 當(dāng)會話定義完成之后就可以開始真正運(yùn)行定義好的計(jì)算了测砂。
但在計(jì)算 y 之前茵烈,需要將所有
用到的變量
初始化
。
也就是說砌些,雖然在變量定義時(shí)給出了變量初始化的方法呜投, 但這個(gè)方法并沒有被真正運(yùn)行加匈。
所以在計(jì)算 y 之前,需要通過運(yùn)行 w1.initializer 和 w2. initializer 來給變量賦值仑荐。
雖然直接調(diào)用每個(gè)變量的初始化過程是一個(gè)可行的方案雕拼,
但是當(dāng)變量數(shù)目增多,或者變量之間存在依賴關(guān)系時(shí)粘招,單個(gè)調(diào)用的方案就比較麻煩了 啥寇。
為了解決這個(gè)問題, TensorFlow提供了一種更加便捷的方式來完成變量初始化過程洒扎。
以下程序展示了 通過 tf.global_variables_initializer 函數(shù)實(shí)現(xiàn)初始化所有變量的過程 :
import tensorflow as tf
init_op = tf.global_variables_initializer()
sess.run(init_op)
通過 tf.global_variables_initializer 函數(shù)辑甜,就不需要將
變量
一個(gè)一個(gè)初始化了 。
這個(gè)函數(shù) 也會自動處理變量之間
的依賴關(guān)系袍冷。
下面的章節(jié)都將使用這個(gè)函數(shù)來完成變量
的初始化過程磷醋。
* 變量和張量的關(guān)系
*
定義變量
是一個(gè)運(yùn)算節(jié)點(diǎn)
*
該運(yùn)算節(jié)點(diǎn)的輸出是一個(gè)張量
*
下面將進(jìn)一步介紹 tf.Variable 操作在 TensorFlow 中底層是如何實(shí)現(xiàn)的。
圖 3-7 給出了神經(jīng)網(wǎng)絡(luò)前向傳播樣例程序的 TensorFlow 計(jì)算圖的一個(gè)部分胡诗,這個(gè)部分顯示了和變量w1相關(guān)的
操作邓线。
*
圖中左上角的黑色橢圓代表了w1的變量定義運(yùn)算節(jié)點(diǎn)煌恢,可以看出這是一個(gè)Variable定義運(yùn)算節(jié)點(diǎn)及其輸出Tensor
*
圖中右下角是read操作
和matmul乘法運(yùn)算操作
骇陈;
*
初始化
變量 w1 的操作是通過Assign 操作
完成的。
在圖 3-7 上可以看到Assign這個(gè)節(jié)點(diǎn)的輸入症虑,是隨機(jī)數(shù)生成函數(shù)
的輸出
輸出賦值給了變量w1缩歪,這樣就完成了變量初始化的過程
3.1.2 節(jié)介紹了 TensorFlow 中集合( collection)的概念,所有的變量都會被自動地加入到
GraphKeys.VARIABLES
這個(gè)集合中谍憔。
通過tf.global_variables()
函數(shù)可以拿到當(dāng)前計(jì)算圖 上所有的變量 匪蝙。
拿到計(jì)算圖上所有的變量有助于持久化整個(gè)計(jì)算圖的運(yùn)行狀態(tài)
,在第 5 章 中將更加詳細(xì)地介紹 习贫。
* trainable - 待優(yōu)化的變量
當(dāng)構(gòu)建機(jī)器學(xué)習(xí)模型時(shí)逛球,比如神經(jīng)網(wǎng)絡(luò),可以通過變量聲明函數(shù)中的
trainable 參數(shù)
來區(qū)分需要優(yōu)化的參數(shù)(比如神經(jīng)網(wǎng)絡(luò)中的待訓(xùn)練參數(shù)矩陣)和其他參數(shù)(比如選代的輪數(shù))苫昌。
如果聲明變量時(shí)參數(shù) trainable
為True颤绕,那么這個(gè)變量將會被加入到GraphKeys.TRAINABLE_VARIABLES 集合
。
在TensorFlow中可以通過tf.trainable_variables()
函數(shù)得到所有需要優(yōu)化的參數(shù) 祟身。
TensorFlow中提供的神經(jīng)網(wǎng)絡(luò)優(yōu)化算法會將GraphKeys.TRAINABLE_VARIABLES集合
中的變量作為默認(rèn)的優(yōu)化對象奥务。
??前面說過,變量是變量定義運(yùn)算節(jié)點(diǎn)
的輸出Tensor袜硫,即:變量是一種特殊的張量氯葬;
??前面說過,張量有三個(gè)屬性:Name婉陷、Shape帚称、Type
??變量作為一種特殊的張量官研,除了變量名之外,變量的Shape闯睹、Type也是其非常重要的屬性
* 變量的類型
和大部分程序語言類似戏羽,
變量的類型
是不可改變
的。
TF中楼吃,一個(gè)變量在構(gòu)建之后始花,它的類型就不能再改變了 。
比如在上面給出的前向傳播樣例中所刀,w1的類型為randomnormal()結(jié)果的默認(rèn)類型tf.float32
, 那么它將不能被賦予其他類型的值衙荐。
以下代碼將會報(bào)出類型不匹配的錯誤:
w1 = tf.Variable(tf.random_normal([2, 3], stddev=2), name="calculate_node_w1")
w2 = tf.Variable(tf.random_normal([2, 3], stddev=2, dtype=tf.float64), name="calculate_node_w2")
w1.assign(w2) # 明確調(diào)用變量的初始化操作
* 變量的維度
維度是變量另一個(gè)重要的屬性捞挥。
和類型不大一樣的是浮创,維度在程序運(yùn)行中是有可能改變
的,
但是需要通過設(shè)置參數(shù)validate_shape=False
砌函。
下面給出了一段示范代碼:
import tensorflow as tf
w1 = tf.Variable(tf.random_normal([2, 3], stddev=2), name="calculate_node_w1")
w2 = tf.Variable(tf.random_normal([2, 3], stddev=2), name="calculate_node_w2")
tf.assign(w1, w2)
# 會報(bào)錯
# ValueError: Dimension 1 in both shapes must be equal, but are 3 and 2
# for 'Assign_1' (op: 'Assign') with input shapes : [2 , 3], [2,2]. tf.assign(w1, w2)
tf.assign(w1, w2, validate_shape=False)
# 不會報(bào)錯
# 以為有設(shè)置參數(shù) validate_shape=False
??雖然 TensorFlow支持更改變量的維度斩披,但是這種用法在實(shí)踐中比較罕見。
-3.4.4- 通過TensorFlow訓(xùn)練神經(jīng)網(wǎng)絡(luò)模型
* 監(jiān)督學(xué)習(xí) & 參數(shù)(矩陣) & 訓(xùn)練過程
這一節(jié)將簡單介紹使用
監(jiān)督學(xué)習(xí)
的方式來更合理地設(shè)置參數(shù)取值讹俊,
同時(shí)也將給出 TensorFlow程序來完成這個(gè)過程垦沉。
學(xué)習(xí)更新神經(jīng)網(wǎng)絡(luò)參數(shù)(矩陣)
的過程就是神經(jīng)網(wǎng)絡(luò)的訓(xùn)練
過程。
只有經(jīng)過有效訓(xùn)練的神經(jīng)網(wǎng)絡(luò)模型才可以真正地解決分類或者回歸問題仍劈。
圖 3-8 對比了訓(xùn)練之前和訓(xùn)練之后神經(jīng)網(wǎng)絡(luò)模型的分類效果:
從圖中可以看出厕倍,模型在訓(xùn)練之前是 完全無法區(qū)分黑色點(diǎn)和灰色點(diǎn)的,
但經(jīng)過訓(xùn)練之后區(qū)分效果已經(jīng)很好了 贩疙。
* 標(biāo)注好的訓(xùn)練數(shù)據(jù)集
* 監(jiān)督學(xué)習(xí): 預(yù)測結(jié)果接近真實(shí)的答案
監(jiān)督學(xué)習(xí)最重要的思想就是讹弯,在己知答案的標(biāo)注數(shù)據(jù)集上 ,模型給出的預(yù)測結(jié)果要盡 量接近真實(shí)的答案这溅。
通過調(diào)整神經(jīng)網(wǎng)絡(luò)中的參數(shù)對 訓(xùn)練數(shù)據(jù)進(jìn)行擬合组民,可以使得模型對未 知的樣本提供預(yù)測的能力。
* 反向傳播算法(Back-Propagation)
* 迭代相關(guān)的概念
*
:樣本集的總樣本量
*
:全部樣本傳入模型參與訓(xùn)練的輪數(shù)
*
:一次迭代計(jì)算loss時(shí)的傳入樣本量
*
:迭代次數(shù)/計(jì)算loss次數(shù)/更新參數(shù)矩陣次數(shù)
通過 TensorFlow實(shí)現(xiàn)反向傳播算法的第一步是使用 TensorFlow表達(dá)一個(gè) batch的數(shù)據(jù)悲靴。
在 3.4.3 節(jié)中嘗試過使用常量來表達(dá)過一個(gè)樣例
:
import tensorflow as tf
x = tf.constant([[0.7, 0.9]])
但如果每輪迭代選取數(shù)據(jù)時(shí)都通過常量來表示臭胜,那么TensorFlow的計(jì)算圖將會
太大
。
因?yàn)槊可梢粋€(gè)常量癞尚, TensorFlow都會在計(jì)算圖中增加一個(gè)節(jié)點(diǎn)耸三。
一般來說,一個(gè)神經(jīng)網(wǎng)絡(luò)的訓(xùn)練過程會需要經(jīng)過幾百萬輪甚至幾億輪的迭代浇揩,
這樣計(jì)算圖就會非常大仪壮,而且利用率很低。
* placeholder
為了避免這個(gè)問題临燃, TensorFlow提供了placeholder機(jī)制用于提供輸入數(shù)據(jù)睛驳。
placeholder 相當(dāng)于定義了一個(gè)位置烙心,這個(gè)位置中的數(shù)據(jù)在程序運(yùn)行時(shí)再指定。
這樣在程序 中就不需要生成大量常量來提供輸入數(shù)據(jù)乏沸,而只需要將數(shù)據(jù)通過 placeholder 傳入 TensorFlow 計(jì)算圖 淫茵。
在 placeholder定義時(shí),這個(gè)位置上的數(shù)據(jù)類型是需要指定的蹬跃。
和其他 張量一樣匙瘪, placeholder 的類型也是不可以改變的。
placeholder 中數(shù)據(jù)的維度信息可以根據(jù) 提供的數(shù)據(jù)推導(dǎo)得出蝶缀,所以不一定要給出丹喻。
下面給出了通過 placeholder實(shí)現(xiàn)前向傳播算法 的代碼:
import tensorflow as tf
w1 = tf.Variable(tf.random_normal([2, 3], stddev=1), name="calculate_node_w1")
w2 = tf.Variable(tf.random_normal([3, 1], stddev=1), name="calculate_node_w2")
# 定義placeholder作為存放輸入數(shù)據(jù)的占位符
# placeholder的維度不強(qiáng)制定義
# 但是如果維度已經(jīng)確定,可以通過指定維度翁都,降低出錯的可能性
x = tf.placeholder(tf.float32, shape=(1, 2), name="calculate_node_placeholder_input")
a = tf.matmul(x, w1)
y = tf.matmul(a, w2)
sess = tf.Session()
init_op = tf.global_variables_initializer()
sess.run(init_op)
print(sess.run(y))
# 會報(bào)錯
# InvalidArgumentError: You must feed a value for placeholder
# tensor 'Input_1' with dtype float and shape [1,2]
print(sess.run(
y ,
feed_dict={x: [[0.7,0.9]]}
))
# 不會報(bào)錯
在這段代碼中碍论,使用placeholder替代了之前代碼中常量定義的x。
在新的程序進(jìn)行前向傳播計(jì)算時(shí)柄慰,需要提供一個(gè)feed_dict來指定x的取值鳍悠。
feed_dict是一個(gè)字典(map),在字典中需要給出 每個(gè)用到的 placeholder 的取值坐搔。
如果某個(gè)需要的 placeholder 沒有被指定取值藏研,那么程序在 運(yùn)行時(shí)將會報(bào)錯。
* Placeholder & Batch
以上程序只計(jì)算了一個(gè)樣例的前向傳播結(jié)果概行,但如圖3-9所示蠢挡,在訓(xùn)練神經(jīng)網(wǎng)絡(luò)時(shí)需要每次提供一個(gè)batch的訓(xùn)練樣例。
對于這樣的需求凳忙,placeholder也可以很好地支持业踏。
在上面的樣例程序中,如果將輸入的矩陣改為的矩陣消略,那么就可以得到個(gè)樣例的前向傳播結(jié)果了堡称。
其中的矩陣的每一行
為一條樣本
。
這樣前向傳播的結(jié)果
為的矩陣艺演,
這個(gè)矩陣的每一行
就代表了一條樣本的前向傳播結(jié)果
却紧。
以下代碼給出了 一個(gè)示例:
import tensorflow as tf
x = tf.placeholder(tf.float32, shape=(3,2), name="calculate_node_placeholder_x")
... # 中間代碼可上面的一樣
# 因?yàn)閤在定義時(shí),指定了n為3
# 所以在運(yùn)行前向傳播過程時(shí)胎撤,需要提供3條樣本
print(sess.run(
y,
feed_dict={
x:[
[0.7, 0.9],
[0.1, 0.4],
[0.5, 0.8]
]
}
))
# 輸出結(jié)果為:
# [
# [ 3.95757794 ]
# [ 1.15376544 ]
# [ 3.16749191 ]
# ]
以上樣例展示了一次性計(jì)算多個(gè)樣例的前向傳播結(jié)果 晓殊。
在運(yùn)行時(shí),需要將三個(gè)樣例 [0.7, 0.9]伤提、[0.1, 0.4]和[0.5, 0.8]組成一個(gè)的矩陣傳入placeholder巫俺。
計(jì)算得到的結(jié)果為的(結(jié)果)矩陣。
其中:
第一行 3.95757794為樣例[0.7,0.9] 的前向傳播結(jié)果
1.15376544為樣例[0.1,0.4] 的前向傳播結(jié)果
3.16749191 為樣例[0.5,0.8]的前向傳播結(jié)果
* Loss & 反向傳播
在得到一個(gè)batch的前向傳播結(jié)果之后肿男,
需要定義一個(gè)損失函數(shù)
來刻畫當(dāng)前的預(yù)測值和真實(shí)答案之間的差距介汹。
然后通過反向傳播算法來迭代神經(jīng)網(wǎng)絡(luò)參數(shù)的取值
使得差距可以被縮小 却嗡。
損失函數(shù)和反向傳播算法將在第 4 章 中更加詳細(xì)地介紹 。
以下代碼定義了一個(gè)簡單的損失函數(shù)嘹承,并通過TensorFlow定義了反向傳播的算法:
import tensorflow as tf
# 使用sigmoid函數(shù)窗价,將y轉(zhuǎn)換為0~1之間的數(shù)值
# 轉(zhuǎn)換后:
# y 代表是預(yù)測為正樣本的概率
# 1-y 代表是預(yù)測為負(fù)樣本的概率
y = tf.sigmoid(y)
# ?? 此處y是個(gè)向量?
# 定義損失函數(shù)來定義預(yù)測值與真實(shí)值之間的差距
cross_entropy = - tf.reduce_mean(
y_ * tf.log(tf.clip_by_value(y , 1e-10, 1.0))
+(1-y) * tf.log(tf.clip_by_value (l-y, 1e-10, 1.0))
)
# 定義學(xué)習(xí)率叹卷,在地4章中將有更加具體的介紹
learning_rate = 0.001
# 定義反向傳播算法來優(yōu)化神經(jīng)網(wǎng)絡(luò)中的參數(shù)
train_step = tf.train.AdamOptimizer(learning_rate).minimize (cross_entropy)
在以上代碼中撼港,cross_entropy定義了預(yù)測值(0.01.0)和真實(shí)值(0.01.0)之間的交叉熵,這是分類問題中骤竹,常用的一個(gè)損失函數(shù)
第二行帝牡,train_step定義了反向傳播的優(yōu)化方法
目前 TensorFlow 支持 10 種不同的優(yōu)化器,讀者可以根據(jù)具體的應(yīng)用選擇不同 的優(yōu)化算法蒙揣。 比較 常用的優(yōu)化方法有 三 種:
*
tf.train.GradientDescentOptimizer
*
tf.train.AdamOptimizer
*
tf.train.MomentumOptimizer
在定義了反向傳播算法之后靶溜,通過運(yùn)行 sess.run(train_step)就可以對所有在 GraphKeys.TRAINABLE_VARIABLES 集合中 的變量進(jìn)行優(yōu)化,使得在當(dāng)前 batch 下?lián)p失函數(shù)更小 鸣奔。
下面的 3.4.5 節(jié)將給出 一個(gè)完整 的訓(xùn)練神 經(jīng) 網(wǎng)絡(luò)樣例程序 墨技。
-3.4.5- 完整神經(jīng)網(wǎng)絡(luò)樣例程序
#! /usr/bin/env python
# -*- coding:utf-8 -*-
import tensorflow as tf
from numpy.random import RandomState
# 定義訓(xùn)練數(shù)據(jù)的大小
batch_size = 8
# 定義神經(jīng)網(wǎng)絡(luò)的待訓(xùn)練參數(shù)矩陣
w1 = tf.Variable(tf.random_normal([2, 3], stddev=1, seed=1), name="calculate_node_w1")
w2 = tf.Variable(tf.random_normal([3, 1], stddev=1, seed=1), name="calculate_node_w2")
# 在shape的【第一個(gè)維度上】使用None可以方便使用不同的batch大小
# 在訓(xùn)練時(shí),需要把數(shù)據(jù)分成比較小的batch
# 但是在測試時(shí)挎狸,可以一次性使用全部的數(shù)據(jù)
# 當(dāng)數(shù)據(jù)集比較小時(shí),這樣做可以比較方便測試
# 但數(shù)據(jù)集比較大時(shí)断楷,將大量數(shù)據(jù)放入一個(gè)batch可能會導(dǎo)致內(nèi)存溢出
x = tf.placeholder(tf.float32, shape=[None, 2], name="calculate_node_placeholder_x_input")
y_ = tf.placeholder(tf.float32, shape=[None, 1], name="calculate_node_placeholder_y_input") # 真實(shí)標(biāo)簽
# 定義神經(jīng)網(wǎng)絡(luò)前向傳播的過程
a = tf.matmul(x, w1)
y = tf.matmul(a, w2)
# 定義損失函數(shù)和反響傳播算法
y = tf.sigmoid(y) # 此處的y是單元素的標(biāo)量
cross_entropy = - tf.reduce_mean(
y_ * tf.log(tf.clip_by_value(y, 1e-10, 1.0))
+ (1-y) * tf.log(tf.clip_by_value(1-y, 1e-10, 1.0))
)
train_step = tf.train.AdamOptimizer(0.001).minimize(cross_entropy)
# 通過隨機(jī)數(shù)生成一個(gè)模擬數(shù)據(jù)集
rdm = RandomState(1)
dataset_size = 128
X = rdm.rand(dataset_size, 2)
# 自定義規(guī)則锨匆,來給出樣本標(biāo)簽
# 這里所有 x1 + x2 < 1的樣本都被認(rèn)為是正樣本(比如零件合格)
# 而其他的被認(rèn)為是負(fù)樣本(零件不合格)
# 和TensorFlow游樂場中+1、-1不一樣的是:
# 這里用1冬筒、0來分別表示正負(fù)樣本
# 大部分解決分類問題的神經(jīng)網(wǎng)絡(luò)恐锣,都會采用1、0的表示方法
Y = [[int(x1 + x2 < 1)] for (x1, x2) in X]
# 創(chuàng)建一個(gè)會話來運(yùn)行TensorFlow程序
with tf.Session() as sess:
init_op = tf.global_variables_initializer()
# 初始化變量
sess.run(init_op)
print(sess.run(w1))
print(sess.run(w2))
"""
在訓(xùn)練開始之前舞痰,待訓(xùn)練參數(shù)矩陣你w1土榴、w2的初始化值:
w1 = [
[-0.81131822, 1.48459876, 0.06532937]
[-2.44270396 , 0.0992484 , 0.59122431]
]
w2 = [
[-0.81131822],
[1.48459876],
[0 . 06532937],
]
"""
# 設(shè)定訓(xùn)練的輪數(shù)(N_iteration)
STEPS = 5000
for i in rang(STEPS):
# 每次選取batch_size個(gè)樣本進(jìn)行訓(xùn)練
start = (i * batch_size) % dataset_size
end = min(start + batch_size, dataset_size)
# 通過本batch的樣本,訓(xùn)練神經(jīng)網(wǎng)絡(luò)并更新參數(shù)矩陣
sess.run(train_step,
feed_dict={
x: X[start:end],
y_: Y[start:end]
}
)
if i % 1000 == 0:
# 每迭代1000次响牛,計(jì)算在所有數(shù)據(jù)上的交叉熵玷禽,并輸出
total_cross_entropy = sess.run(cross_entropy,
feed_dict={
x: X,
y_: Y
}
)
print("After {train_i} training step(s), cross_entropy on all data is {total_cross_entropy};".format(
train_i=i,
total_cross_entropy= total_cross_entropy
))
"""
輸出結(jié)果:
After 0 training step(s), cross_entropy on all data is 1.89805;
After 1000 training step(s), cross_entropy on all data is 0.655075;
After 2000 training step(s), cross_entropy on all data is 0.626172;
"""
print(sess.run(w1))
print(sess.run(w2))
"""
在訓(xùn)練之后的神經(jīng)網(wǎng)絡(luò)參數(shù)的值
w1 = [
[0.02476984, 0.5694868, 1.69219422]
[-2.19773483 , -0.23668921 , 1.11438966]
]
w2 = [
[0.45544702],
[0.49110931],
[-0.9811033]
]
可以發(fā)現(xiàn),這兩個(gè)參數(shù)的取值呀打,已經(jīng)發(fā)生了變化
這個(gè)變化矢赁,就是訓(xùn)練過程中一次次迭代更新后的結(jié)果
迭代后的參數(shù)矩陣使得這個(gè)神經(jīng)網(wǎng)絡(luò)能更好地?cái)M合提供的訓(xùn)練數(shù)據(jù)
"""
以上程序?qū)崿F(xiàn)了訓(xùn)練神經(jīng)網(wǎng)絡(luò)的全部過程。
從這段程序可以總結(jié)出訓(xùn)練神經(jīng)網(wǎng)絡(luò)的過程可以分為以下三個(gè)步驟:
*
定義神經(jīng)網(wǎng)絡(luò)的結(jié)構(gòu)以及前向傳播的輸出預(yù)測結(jié)果
*
定義損失函數(shù)以及反向傳播傳播更新參數(shù)矩陣的方法
*
生成會話贬丛,并在訓(xùn)練數(shù)據(jù)上反復(fù)運(yùn)行前向傳播
撩银、計(jì)算loss
、反向傳播迭代參數(shù)
無論神經(jīng)網(wǎng)絡(luò)的結(jié)構(gòu)如何變化豺憔,這三個(gè)步驟是不變的额获。