TensorFlow 實(shí)戰(zhàn)Google深度學(xué)習(xí)框架(第2版)第三章讀書筆記

第三章:TensorFlow入門

* 3.1TensorFlow計(jì)算模型 - 計(jì)算圖
? ? ? ?* 3.1.1計(jì)算圖的概念
? ? ? ?* 3.1.2計(jì)算圖的使用
* 3.2TensorFlow數(shù)據(jù)模型 - 張量
? ? ? ?* 3.2.1張量的概念
? ? ? ?* 3.2.2張量的使用
* 3.3TensorFlow運(yùn)行模型 - 會話
* 3.4TensorFlow實(shí)現(xiàn)神經(jīng)網(wǎng)絡(luò)
? ? ? ?* 3.4.1TensorFlow游樂場及神經(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的定義

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)系赖舟。

向量相加的計(jì)算圖

上圖展示了通過 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

TensorFlow游樂場

TensorFlow游樂場 - 訓(xùn)練

上圖中可以先著重關(guān)注兩個(gè)框:
* Feature框:輸入每條樣本(x,y)的各種特征:
? ? xy密任、x^2颜启、y^2x·y浪讳、sin(x)缰盏、sin(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è){2}\times{3}的矩陣變量的方法:

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)的操作邓线。

神經(jīng)網(wǎng)絡(luò)前向傳播樣例中,變量w1相關(guān)部分的計(jì)算圖可視化結(jié)果

* 圖中左上角的黑色橢圓代表了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ò)模型的分類效果:


TensotFJow游樂場訓(xùn)練前和訓(xùn)練后效果對比

從圖中可以看出厕倍,模型在訓(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)的概念

N_{sample} \times {Epoch} = {Batch} \times {N_{iteration}}
* N_{sample}:樣本集的總樣本量
* Epoch:全部樣本傳入模型參與訓(xùn)練的輪數(shù)
* Batch:一次迭代計(jì)算loss時(shí)的傳入樣本量
* N_{iteration}:迭代次數(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也可以很好地支持业踏。
在上面的樣例程序中,如果將輸入的{1}\times{2}矩陣改為{n}\times{2}的矩陣消略,那么就可以得到n個(gè)樣例的前向傳播結(jié)果了堡称。
其中{n}\times{2}的矩陣的每一行一條樣本
這樣前向傳播的結(jié)果{n}\times{1}的矩陣艺演,
這個(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è){3} \times {2}的矩陣傳入placeholder巫俺。
計(jì)算得到的結(jié)果為{3} \times {1}的(結(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è)步驟是不變的额获。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末够庙,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子抄邀,更是在濱河造成了極大的恐慌首启,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,686評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件撤摸,死亡現(xiàn)場離奇詭異毅桃,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)准夷,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,668評論 3 385
  • 文/潘曉璐 我一進(jìn)店門钥飞,熙熙樓的掌柜王于貴愁眉苦臉地迎上來吴攒,“玉大人谍咆,你說我怎么就攤上這事痘拆〈;危” “怎么了蜻势?”我有些...
    開封第一講書人閱讀 158,160評論 0 348
  • 文/不壞的土叔 我叫張陵能扒,是天一觀的道長嫁蛇。 經(jīng)常有香客問我坎弯,道長酒朵,這世上最難降的妖魔是什么桦锄? 我笑而不...
    開封第一講書人閱讀 56,736評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮蔫耽,結(jié)果婚禮上结耀,老公的妹妹穿的比我還像新娘。我一直安慰自己匙铡,他們只是感情好图甜,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,847評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著鳖眼,像睡著了一般黑毅。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上钦讳,一...
    開封第一講書人閱讀 50,043評論 1 291
  • 那天矿瘦,我揣著相機(jī)與錄音,去河邊找鬼蜂厅。 笑死匪凡,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的掘猿。 我是一名探鬼主播病游,決...
    沈念sama閱讀 39,129評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了衬衬?” 一聲冷哼從身側(cè)響起买猖,我...
    開封第一講書人閱讀 37,872評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎滋尉,沒想到半個(gè)月后玉控,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,318評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡狮惜,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,645評論 2 327
  • 正文 我和宋清朗相戀三年高诺,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片碾篡。...
    茶點(diǎn)故事閱讀 38,777評論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡虱而,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出开泽,到底是詐尸還是另有隱情牡拇,我是刑警寧澤,帶...
    沈念sama閱讀 34,470評論 4 333
  • 正文 年R本政府宣布穆律,位于F島的核電站惠呼,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏峦耘。R本人自食惡果不足惜剔蹋,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,126評論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望贡歧。 院中可真熱鬧滩租,春花似錦、人聲如沸利朵。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,861評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽绍弟。三九已至,卻和暖如春著洼,著一層夾襖步出監(jiān)牢的瞬間樟遣,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,095評論 1 267
  • 我被黑心中介騙來泰國打工身笤, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留豹悬,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,589評論 2 362
  • 正文 我出身青樓液荸,卻偏偏與公主長得像瞻佛,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,687評論 2 351

推薦閱讀更多精彩內(nèi)容