主要根據(jù)鄭澤宇看疙,梁博文饼煞,顧思宇編著的《TensorFlow-實(shí)戰(zhàn)Google深度學(xué)習(xí)框架》作為一個(gè)學(xué)習(xí)筆記瓮恭。本篇包括書(shū)中的一到五章節(jié)的內(nèi)容身腻,主要是TensorFlow里面的基礎(chǔ)概念和在mnist數(shù)據(jù)集上構(gòu)建一個(gè)神經(jīng)網(wǎng)絡(luò)的操作介紹产还。
TensorFlow里面模塊和功能眾多,下面只會(huì)按照書(shū)中順序進(jìn)行簡(jiǎn)單的記錄嘀趟。同時(shí)由于書(shū)是2017年第一版脐区,經(jīng)過(guò)兩年, 書(shū)中的部分代碼使用的模塊在新版本的TensorFlow中已經(jīng)不建議使用或者被其他的替換她按,博客中的代碼大多都作了修改牛隅,但仍有遺留錯(cuò)誤的可能。詳細(xì)的TensorFlow文檔可以參考酌泰。
序言——從Google說(shuō)起
Google公司作為人工智能領(lǐng)域的領(lǐng)頭羊媒佣,實(shí)在是給了我們很多學(xué)習(xí)的參考,Tensorflow的火爆就是很好的例子陵刹。當(dāng)年AlphaGo和AlphaZero在圍棋博弈游戲上帶給我們的震撼還沒(méi)結(jié)束默伍,AlphaStar就在星際爭(zhēng)霸這種更為復(fù)雜的游戲博弈上給我們展示了人工智能的更大潛力。
AlphaGo主要由三部分組成衰琐,蒙特卡羅樹(shù)搜索(MonteCarlo tree search,MCTS),估值網(wǎng)絡(luò)(value network)也糊,走棋網(wǎng)絡(luò)(policy network)。
關(guān)于蒙特卡羅樹(shù)和AlphaGo的基本特性羡宙,在這篇文章中有詳細(xì)介紹
在眾多的深度學(xué)習(xí)工具中狸剃,如Caffe,PyTorch等,TensorFlow在Github上的活躍度還是受關(guān)注程度都是最高的辛辨。
TensorFlow的主要依賴(lài)包
Protocol Buffer
這個(gè)包是用來(lái)處理結(jié)構(gòu)化數(shù)據(jù)的工具捕捂。例如用戶(hù)信息在Protocol Buffer上表示如下。
name: breeze
id: 1234
email: example@gmail.com
和XML和JSON格式有挺大的區(qū)別的
XML:
<user>
<name>breeze</name>
<id>1234</id>
<email>example@gmail.com</email>
</user>
JSON:
{
"name": "breeze",
"id": "1234",
"email": "example.gmail.com",
}
除此之外Pro?tocol Buffer序列化之后得到的數(shù)據(jù)不是可讀的字符串斗搞,而是二進(jìn)制流指攒。其次,XML和JSON格式的數(shù)據(jù)信息都包含在了序列化后的數(shù)據(jù)中僻焚,不需要額外信息就能還原允悦。Protocol Buffer還需要先定義數(shù)據(jù)的格式(schema),還原一個(gè)序列化的數(shù)據(jù)時(shí)候需要用到這個(gè)定義好的數(shù)據(jù)格式虑啤。如對(duì)應(yīng)上面的格式例子隙弛,schema如下:
message user{
optional string name = 1;
required int32 id = 2;
repeated string email = 3;
}
Protocol Buffer定義數(shù)據(jù)格式的文件一般保存在.proto文件中架馋,每個(gè)message代表了一類(lèi)結(jié)構(gòu)化的數(shù)據(jù)。一個(gè)屬性可以使必需的(required)也可以是可選的(optional)或者是可重復(fù)的(repeated)全闷。在上面的例子中用戶(hù)id是必需的叉寂,用戶(hù)名可選可以不填寫(xiě),一個(gè)用戶(hù)可能有多個(gè)email地址所以是可重復(fù)的总珠。
標(biāo)簽數(shù)字1和2則表示不同的字段在序列化后的二進(jìn)制數(shù)據(jù)中的布局位置屏鳍。在該例中,id字段編碼后的數(shù)據(jù)一定位于name之后局服。需要注意的是該值在同一message中不能重復(fù)钓瞭。在proto2,每個(gè)屬性前必須加required淫奔,optional山涡,repeated。后面跟的數(shù)字只要不重復(fù)唆迁,可以定義為任何數(shù)字鸭丛,不需要總是從1或者0開(kāi)始。
值得注意的是已經(jīng)更新了proto3媒惕。在proto3語(yǔ)法中顯式的 "optional" 關(guān)鍵字被禁止系吩,因?yàn)樽侄文J(rèn)就是可選的;同時(shí)必填字段不再被支持。另外在proto3中枚舉值第一個(gè)必須是0妒蔚,其他的隨意。
enum Sex
{
MALE = 0;
FEMALE = 1;
}
Bazel
Bazel是谷歌的自動(dòng)化構(gòu)建工具月弛,功能和Makefile差不多肴盏,在一個(gè)項(xiàng)目工作目錄下需要有一個(gè)WORKSPACE文件定義對(duì)外部資源的依賴(lài)。
Bazel通過(guò)BUILD文件來(lái)找到需要編譯的目標(biāo)和確定編譯的方式帽衙。Bazel對(duì)Python支持的編譯方式有三種:py_binary,py_library,py_test三種菜皂,分別對(duì)應(yīng)編譯成可執(zhí)行文件,庫(kù)函數(shù)和測(cè)試程序厉萝。
目前假定工作目錄下有兩個(gè).py文件恍飘,一個(gè)是hello_lib.py
,一個(gè)是hello_main.py
hello_lib.py如下:
def print_hello_world():
print("Hello World")
hello_main.py如下:
import hello_lib
hello_lib.print_hello_world()
那么BUILD文件應(yīng)該這么寫(xiě)
py_library(
name = "hello_lib",
srcs = [
"hello_lib.py",
]
)
py_binary(
name = "hello_main",
srcs = [
"hello_main.py",
]
deps = [
":hello_lib",
],
)
srcs給出了編譯所需要的源代碼谴垫,deps給出了編譯所需要的依賴(lài)關(guān)系章母。這兩項(xiàng)都是一個(gè)list。
Tensorflow基礎(chǔ)概念和定義
計(jì)算圖和張量(Tensor)的概念
TensorFlow中每一個(gè)變量都是計(jì)算圖上的一個(gè)節(jié)點(diǎn)翩剪,節(jié)點(diǎn)之間的邊描述了各個(gè)節(jié)點(diǎn)之間的運(yùn)算依賴(lài)關(guān)系乳怎。計(jì)算圖的概念保證了變量與計(jì)算關(guān)系的統(tǒng)一管理。
張量(Tensor)一看就是TensorFlow的核心觀(guān)點(diǎn)前弯。所有的數(shù)據(jù)的屬性都通過(guò)張量的形式來(lái)表示蚪缀。從功能上看秫逝,張量可以被理解為多維數(shù)組。其中零階張量表示標(biāo)量(scalar)询枚,也就是單獨(dú)的數(shù)违帆;一階張量就變成向量(vector)表示一緯數(shù)組;n階張量就是表示高緯空間里的n緯數(shù)組金蜀。
還有值得在意的是張量在TensorFlow里面存儲(chǔ)的不是實(shí)際的數(shù)值前方,而是保存如何得到這個(gè)節(jié)點(diǎn)的數(shù)字的計(jì)算過(guò)程。
下面我們用一個(gè)簡(jiǎn)單的向量加法解釋下計(jì)算圖和張量:
<img src = 'http://breezepicture.oss-cn-beijing.aliyuncs.com/Tensorflow/Graph.png' width = '500'>
在TensorFlow中系統(tǒng)會(huì)維護(hù)一個(gè)默認(rèn)的計(jì)算圖廉油,在終端交互中如果不特意指定惠险,使用的就是默認(rèn)的計(jì)算圖。
在圖中我們創(chuàng)建了兩個(gè)變量a和b抒线,兩者在計(jì)算圖中指向計(jì)算節(jié)點(diǎn)add班巩。在計(jì)算圖上每一個(gè)節(jié)點(diǎn)都代表了一個(gè)計(jì)算,計(jì)算的結(jié)果就保存在張量中嘶炭。
如result = tf.add(a, b, name="add_demo")
用tf.add()
創(chuàng)建了一個(gè)計(jì)算抱慌,這個(gè)計(jì)算在計(jì)算圖上的節(jié)點(diǎn)名叫做add_demo
計(jì)算出的結(jié)果保存在result這個(gè)張量中。
通過(guò)print我們可以看到張量的結(jié)構(gòu)眨猎。主要包含三個(gè)屬性:名字(name)抑进、緯度(shape)和類(lèi)型(type)。"add_demo:0"
表示了result這個(gè)張量是計(jì)算節(jié)點(diǎn)add_demo
輸出的第一個(gè)結(jié)果(編號(hào)從0開(kāi)始)睡陪。shape = (2,)
說(shuō)明了張量result是個(gè)一維數(shù)組寺渗,數(shù)組長(zhǎng)度為2。每一個(gè)張量會(huì)有一個(gè)唯一的類(lèi)型兰迫,參與計(jì)算的張量的類(lèi)型需要保持匹配信殊。
對(duì)于緯度shape信息里面的2,1和空分別代表什么含義汁果,下面的圖作了額外解釋?zhuān)?/p>
<img src = 'http://breezepicture.oss-cn-beijing.aliyuncs.com/Tensorflow/shape.png' width = '500'>
TensorFlow支持14種類(lèi)型涡拘,包括了實(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)仲吏。
會(huì)話(huà)(session)的概念
會(huì)話(huà)用于執(zhí)行定義好的運(yùn)算,為計(jì)算圖的運(yùn)算提供運(yùn)算環(huán)境。
sess = tf.Session()
sess.run()
sess.close()
或者利用Python里的with裹唆,使用上下文管理來(lái)管理這個(gè)會(huì)話(huà)誓斥,確保會(huì)話(huà)創(chuàng)建成功和結(jié)束后資源的釋放。
with tf.Session() as sess:
sess.run()
在命令行交互界面有直接構(gòu)建會(huì)話(huà)的簡(jiǎn)便方法tf.InteractiveSession()
,結(jié)合我們上面的例子有:
<img src = 'http://breezepicture.oss-cn-beijing.aliyuncs.com/Tensorflow/session.png' width = '500'>
集合(collection)的概念
在一個(gè)計(jì)算圖中许帐,可以通過(guò)集合(collection)來(lái)管理不同類(lèi)型的資源劳坑。這里的資源可以是張量、變量或者隊(duì)列資源等成畦。
TensorFlow中維護(hù)的集合列表:
集合名詞 | 集合內(nèi)容 |
---|---|
tf.GraphKeys.VARIABLES | 所有變量 |
tf.GraphKeys.TRAINABLE_VARIABLES | 可學(xué)習(xí)的變量(神經(jīng)網(wǎng)絡(luò)中的參數(shù)) |
tf.GraphKeys.SUMMARIES | 日志生成的相關(guān)張量 |
tf.GraphKeys.QUEUE_RUNNERS | 處理輸入的QueueRunner |
tf.GraphKeys.MOVING_AVERAGE_VARIABLES | 計(jì)算了滑動(dòng)平均值的變量 |
關(guān)于QueueRunner距芬,需要談到TensorFlow中的Queue,Queue是TF隊(duì)列和緩存機(jī)制的實(shí)現(xiàn)循帐,QueueRunner就是對(duì)操作Queue的線(xiàn)程的封裝框仔。
Tensorflow的計(jì)算主要在使用CPU/GPU和內(nèi)存,而數(shù)據(jù)讀取涉及磁盤(pán)操作拄养,速度遠(yuǎn)低于前者操作离斩。因此通常會(huì)使用多個(gè)線(xiàn)程讀取數(shù)據(jù),然后使用一個(gè)線(xiàn)程處理數(shù)據(jù)瘪匿。QueueRunner就是來(lái)管理這些讀寫(xiě)隊(duì)列的線(xiàn)程的跛梗。Queue相關(guān)更多參考
滑動(dòng)平均值可以看作是變量的過(guò)去一段時(shí)間取值的均值,相比對(duì)變量直接賦值而言棋弥,滑動(dòng)平均得到的值在圖像上更加平緩光滑核偿,抖動(dòng)性更小,不會(huì)因?yàn)槟炒蔚漠惓H≈刀沟没瑒?dòng)平均值波動(dòng)很大顽染⊙溃滑動(dòng)平均模型常常用于提高神經(jīng)網(wǎng)絡(luò)模型在測(cè)試數(shù)據(jù)上的表現(xiàn),這在后文會(huì)說(shuō)家乘。
TensorFlow中的常見(jiàn)指令
書(shū)中介紹的神經(jīng)網(wǎng)絡(luò)原理部分在另一篇博文神經(jīng)網(wǎng)絡(luò)與人工智能中介紹了蝗羊,在此略過(guò),著重整理書(shū)中出現(xiàn)的關(guān)于TensorFlow的功能命令仁锯。會(huì)按照不同任務(wù)階段進(jìn)行劃分。
變量初始化
1.使用隨機(jī)數(shù)生成器初始化變量
weights = tf.Variable(tf.random_normal([2,3], stddev=2))
這里產(chǎn)生了聲明了一個(gè)矩陣翔悠,矩陣中的元素是均值為0业崖,標(biāo)準(zhǔn)差為2的隨機(jī)數(shù)。
tf.random_normal
函數(shù)用來(lái)來(lái)產(chǎn)生滿(mǎn)足正態(tài)分布的隨機(jī)數(shù)蓄愁,可以通過(guò)參數(shù)mean來(lái)確定平均值双炕,默認(rèn)為0。
tf.random_normal(shape, mean=0.0, stddev=1.0, dtype=tf.float32, seed=None, name=None)
正太分布隨機(jī)數(shù)撮抓,均值mean,標(biāo)準(zhǔn)差stddev妇斤。
tf.truncated_normal(shape, mean=0.0, stddev=1.0, dtype=tf.float32, seed=None, name=None)
截?cái)嗾龖B(tài)分布隨機(jī)數(shù),均值mean,標(biāo)準(zhǔn)差stddev,不過(guò)只保留[mean-2stddev,mean+2stddev]范圍內(nèi)的隨機(jī)數(shù)。
TensorFlow里面的各種隨機(jī)數(shù)生成器:
函數(shù)名稱(chēng) | 隨機(jī)數(shù)分布 | 主要參數(shù) |
---|---|---|
tf.random_normal | 正態(tài)分布 | 平均值站超,標(biāo)準(zhǔn)差荸恕,取值類(lèi)型 |
tf.random_uniform | 均勻分布 | 最小、最大取值死相,取值類(lèi)型 |
tf.random_gamma | Gamma分布 | 形狀參數(shù)alpha融求,尺度參數(shù)beta,取值類(lèi)型 |
使用random_uniform()
的示例
<img src = 'http://breezepicture.oss-cn-beijing.aliyuncs.com/Tensorflow/random_uniform.png'>
對(duì)于Gamma分布的初始化算撮,統(tǒng)計(jì)學(xué)沒(méi)學(xué)好生宛,不是很懂,給出兩個(gè)鏈接
w3cschool上的理解
Gamma分布的知乎討論
2.使用常量來(lái)初始化變量
函數(shù)名稱(chēng) | 功能 | 樣例 |
---|---|---|
tf.zeros | 產(chǎn)生全0的數(shù)組 | tf.zeros([2,3],int32)->[[0,0,0],[0,0,0]] |
tf.ones | 產(chǎn)生全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] |
3.通過(guò)其他變量的初始值來(lái)初始化新的變量
w2 = tf.Variable(weights.initialized_value())
w3 = tf.Variable(weights.initialized_value() * 2.0)
需要注意的是前面三種變量初始化方法相當(dāng)于只是表明了初始化的格式肮柜,還沒(méi)有真正賦值陷舅。所以對(duì)于每個(gè)使用了tf.Variable()
指明為變量的張量,每次使用前需要對(duì)這個(gè)變量正式初始化审洞。
以weights = tf.Variable(tf.random_normal([2,3], stddev=2))
為例:
初始化的方法有weights.initializer
莱睁,后來(lái)發(fā)現(xiàn)weights.initialized_value()
也相當(dāng)于對(duì)變量進(jìn)行初始化后返回初始化數(shù)值
<img src= 'http://breezepicture.oss-cn-beijing.aliyuncs.com/Tensorflow/initialized_value.png'>
面對(duì)數(shù)目眾多的變量需要初始化時(shí)不可能一個(gè)個(gè)初始化,這時(shí)候使用tf.global_variables_initializer()
并行對(duì)訓(xùn)練模型中的眾多變量進(jìn)行統(tǒng)一初始化
import tensorflow as tf
weights = tf.Variable(tf.random_uniform([2, 3], minval = 1.0, maxval = 3.0))
w1 = tf.Variable(weights.initialized_value() * 2.0)
with tf.Session() as sess:
init_op = tf.global_variables_initializer()
sess.run(init_op)
print(sess.run(weights))
print(sess.run(w1))
結(jié)果如下:
<img src = 'http://breezepicture.oss-cn-beijing.aliyuncs.com/Tensorflow/global_initalize.png'>
結(jié)合上面的集合的概念预明,在變量創(chuàng)建之后所有的變量都會(huì)自動(dòng)地加入到GraphKeys.VARIABLES這個(gè)集合中缩赛,通過(guò)tf.global_variables()
函數(shù)可以拿到當(dāng)前計(jì)算圖上所有的變量。如果聲明變量時(shí)候參數(shù)trainable為T(mén)rue撰糠,那么這個(gè)變量將會(huì)被加入到GraphKeys.TRAINABLE_VARIABLES集合酥馍,可以通過(guò)tf.trainable_variables()
函數(shù)得到這些變量。變量的類(lèi)型在確定之后不能改變阅酪,但是緯度參數(shù)如果在初始設(shè)置時(shí)候加入額外參數(shù)validate_shape=False旨袒,就可以改變,不過(guò)一般來(lái)講也不會(huì)這么干术辐。
輸入訓(xùn)練數(shù)據(jù)
多次迭代訓(xùn)練所需要的訓(xùn)練數(shù)據(jù)不可能全部以常量形式保存在計(jì)算圖中砚尽,TensorFlow提供了placeholder機(jī)制用于提供輸入數(shù)據(jù),往往搭配feed_dict使用辉词。
下面是對(duì)n個(gè)樣例計(jì)算前向傳播結(jié)果的代碼示例必孤。
import tensorflow as tf
w1 = tf.Variable(tf.random_normal([2,3], stddev=1, seed=1))
w2 = tf.Variable(tf.random_normal([3,1], stddev=1, seed=1))
x = tf.placeholder(tf.float32, shape=(n,2), name="input")
#placeholder作為存放輸入數(shù)據(jù)的地方,緯度不一定需要定義瑞躺,如果輸入數(shù)據(jù)的緯度是確定的敷搪,定義可以保證程序的穩(wěn)定。
a = tf.matmul(x, w1)
y = tf.matmul(a, w2) #tf.matmul()矩陣乘法
sess = tf.Session()
init_op = tf.global_variables_initializer()
sess.run(init_op)
print(sess.run(y, feed_dict={x: [[0.7,0.2], [0.1,0.4], [0.6,0.5]...})) #n個(gè)樣例輸入
計(jì)算y時(shí)候需要把一個(gè)的矩陣傳入placeholder幢哨,否則x的數(shù)值不明確無(wú)法計(jì)算赡勘。相當(dāng)于placeholder是給需要的輸入數(shù)據(jù)在計(jì)算圖里面先預(yù)留了一個(gè)坑,真正運(yùn)行時(shí)候需要把需要的值填入捞镰。
設(shè)置代價(jià)函數(shù)
經(jīng)典代價(jià)函數(shù)
交叉熵函數(shù)
其中y是正確的結(jié)果闸与,a是模型預(yù)測(cè)的結(jié)果毙替。
如果面對(duì)的是一個(gè)分類(lèi)的問(wèn)題y這個(gè)張量的值非0即1,那么上面的交叉熵函數(shù)退化為
表示的代碼如下:
cross_entropy = -tf.reduce_mean(y * tf.log(tf.clip_by_value(a, 1e-10, 1.0)))
均方代價(jià)函數(shù)
代碼表示:
mse = tf.reduce_mean(tf.square(y-a))
1.*
運(yùn)算
代表元素之間直接相乘践樱,也就是Hadamard乘積
v1 = tf.constant([[1.0, 2.0], [3.0, 4.0]])
v2 = tf.constant([[5.0, 6.0], [7.0, 8.0]])
print((v1 * v2).eval()
# 輸出
[[ 5. 12.]
[21. 32.]]
2.reduce_mean
這個(gè)函數(shù)用于計(jì)算張量tensor沿著指定的數(shù)軸(tensor的某一維度)上的的平均值厂画,主要用作降維或者計(jì)算tensor(圖像)的平均值。
- 第一個(gè)參數(shù)input_tensor: 輸入的待降維的tensor;
- 第二個(gè)參數(shù)axis: 指定的軸映胁,如果不指定木羹,則計(jì)算所有元素的均值;
- 第三個(gè)參數(shù)keep_dims:是否降維度,設(shè)置為T(mén)rue解孙,輸出的結(jié)果保持輸入tensor的形狀坑填,設(shè)置為False,輸出結(jié)果會(huì)降低維度;
簡(jiǎn)單的例子如下
#’x' is [[1, 1, 1]
# [1, 1, 1]]
tf.reduce_sum(x) ==> 6
tf.reduce_sum(x, 0) ==> [2, 2, 2]
tf.reduce_sum(x, 1) ==> [3, 3]
tf.reduce_sum(x, 1, keep_dims=True) ==> [[3], [3]]
tf.reduce_sum(x, [0, 1]) ==> 6
其余類(lèi)似的函數(shù)還有
- tf.reduce_sum :計(jì)算tensor指定軸方向上的所有元素的累加和;
- tf.reduce_max : 計(jì)算tensor指定軸方向上的各個(gè)元素的最大值;
- tf.reduce_all : 計(jì)算tensor指定軸方向上的各個(gè)元素的邏輯和(and運(yùn)算);
- tf.reduce_any: 計(jì)算tensor指定軸方向上的各個(gè)元素的邏輯或(or運(yùn)算);
3.tf.clip_by_value
這個(gè)函數(shù)可以將張量中的數(shù)值限制在一個(gè)范圍內(nèi)弛姜,避免一些運(yùn)算的錯(cuò)誤脐瑰,比如上面的例子中把log0
看成是無(wú)效的(1的-10次方近似看成0)
因?yàn)榻徊骒睾瘮?shù)一般會(huì)與softmax回歸一起使用,所以TensorFlow對(duì)這兩個(gè)部分進(jìn)行的封裝:
cross_entropy = tf.nn.softmax_cross_entropy_with_logits(labels=y, logits=a)
在只有一個(gè)正確答案的分類(lèi)問(wèn)題中廷臼,TensorFlow提供了
tf.nn.sparse_softmax_cross_entropy_with_logits
函數(shù)加速計(jì)算過(guò)程苍在。
自定義代價(jià)函數(shù)
對(duì)于一些特殊的問(wèn)題可能需要自定義代價(jià)函數(shù),譬如想要定義的損失函數(shù)如下:
代碼表示:
loss = tf.reduce_sum(tf.where(tf.greater(v1, v2),(v1-v2) * a, (v2-v1) * b))
tf.greater
的輸入是兩個(gè)張量荠商,此函數(shù)會(huì)比較這兩個(gè)輸入張量中每一個(gè)元素的大小寂恬,并返回比較結(jié)果。
tf.where
函數(shù)有三個(gè)參數(shù)莱没。第一個(gè)為選擇條件根據(jù)初肉,當(dāng)選擇條件為T(mén)rue時(shí),選擇第二個(gè)參數(shù)饰躲,否則使用第三個(gè)參數(shù)的值牙咏。值得注意的是tf.where
函數(shù)判斷和選擇都是在元素級(jí)別進(jìn)行的。
<img src='http://breezepicture.oss-cn-beijing.aliyuncs.com/Tensorflow/tf.where.png'>
使用L2和L1規(guī)范化
L2規(guī)范化和L1規(guī)范化TensorFlow都提供了對(duì)應(yīng)的函數(shù)嘹裂。
tf.contrib.layers.l2_regularizer
tf.contrib.layers.l1_regularizer
如果是簡(jiǎn)單的網(wǎng)絡(luò)里把loss變化下就行:
loss = tf.reduce_mean(tf.square(y_ - y) + tf.contrib.layers.l2_regularizer(lambda)(w)
這里的lambda就是代表規(guī)范化參數(shù)妄壶。
不過(guò)重新定義loss的方式在面對(duì)多層神經(jīng)網(wǎng)絡(luò)時(shí)候loss的表達(dá)式過(guò)長(zhǎng),可讀性也差寄狼。
下面利用集合(collection),完整寫(xiě)一下5層神經(jīng)網(wǎng)絡(luò)帶L2規(guī)范化的損失函數(shù)的計(jì)算方法丁寄。
import tensorflow as tf
#獲取一層神經(jīng)網(wǎng)絡(luò)邊上的權(quán)重,并將這個(gè)權(quán)重的L2正則化損失加入名為'losses'的集合中泊愧,輸入兩個(gè)參數(shù)狡逢,一個(gè)是里面定義的var的緯度,一個(gè)lambda就是規(guī)范化參數(shù)
def get_weight(shape, lambda):
# 生成一個(gè)初始變量拼卵,協(xié)助完成權(quán)重初始化
var = tf.Variable(tf.random_normal(shape), dtype = tf.float32)
# add_to_collection 把這一層新生成變量的L2規(guī)范化損失項(xiàng)加入集合
tf.add_to_collection(
'losses',tf.contrib.layers.l2_regularizer(lambda)(var))
return var
x = tf.placeholder(tf.float32, shape=(None, 2))
y_ = tf.placeholder(tf.float32, shape=(None, 1))
batch_size = 8
#定義每一層網(wǎng)絡(luò)中節(jié)點(diǎn)的個(gè)數(shù)
layer_dimension = [2, 10, 10, 10, 1]
#神經(jīng)網(wǎng)絡(luò)的層數(shù)
n_layers = len(layer_dimension)
#這個(gè)變量維護(hù)前向傳播是最深層次的節(jié)點(diǎn),最開(kāi)始的時(shí)候?yàn)檩斎雽勇瑁猿跏贾禐閤
cur_layer = x
in_dimension = layer_dimension[0]
#一個(gè)循環(huán)建立一個(gè)5層全連接網(wǎng)絡(luò)
for i in range(1, n_layers):
out_dimension = layer_dimension[i]
weight = get_weight([in_dimension, out_dimension], 0.001)
bias = tf.Variable(tf.constant(0.1, shape=[out_dimension]))
#使用ReLU激活函數(shù)
cur_layer = tf.nn.relu(tf.matmul(cur_layer, weight) + bias)
in_dimension = layer_dimension[i]
mse_loss = tf.reduce_mean(tf.square(y_ - cur_layer)
#將均方誤差也加入'losses'集合
tf.add_to_collection('losses', mse_loss)
#把'losses'這個(gè)集合的元素加起來(lái)就是最后loss的表達(dá)式
loss = tf.add_n(tf.get_collection('losses')
這里值得注意的是:結(jié)合計(jì)算圖的概念腋腮,'losses'
集合里存的都不是具體的數(shù)值雀彼,都是一個(gè)個(gè)節(jié)點(diǎn)元素。并且上面的代碼只是完成了結(jié)構(gòu)的定義即寡,還沒(méi)有進(jìn)行真正的初始化和優(yōu)化器優(yōu)化徊哑。
設(shè)定學(xué)習(xí)速率
較小的學(xué)習(xí)率雖然能保證收斂的效果,但會(huì)導(dǎo)致收斂速度過(guò)慢聪富。較大的學(xué)習(xí)率能快速收斂但效果不一定好莺丑。所以基本不同的迭代輪次設(shè)置不同的學(xué)習(xí)率是必要的。
TensorFlow提供了使用指數(shù)衰減法設(shè)置學(xué)習(xí)率的函數(shù) tf.train.exponential_decay
這個(gè)函數(shù)的定義如下:
decayed_learning_rate = learning_rate * decay_rate ^ (global_step / decay_steps)
decay_learning_rate
為每一輪優(yōu)化時(shí)使用的學(xué)習(xí)率墩蔓,learning_rate
為事先設(shè)定的初始學(xué)習(xí)率梢莽,decay_rate
為衰減系數(shù),decay_steps
為衰減速度奸披。在這個(gè)函數(shù)中還有一個(gè)參數(shù)staircase, 默認(rèn)為False, 當(dāng)staircase設(shè)置為T(mén)rue的時(shí)候昏名,global_step/decay_steps
會(huì)被轉(zhuǎn)化成整數(shù),使得學(xué)習(xí)率變成一個(gè)階梯函數(shù)阵面。
global_step
在滑動(dòng)平均轻局、優(yōu)化器、指數(shù)衰減學(xué)習(xí)率等方面都有用到样刷,這個(gè)變量的實(shí)際意義非常好理解:代表全局步數(shù)仑扑,比如在多少步該進(jìn)行什么操作,現(xiàn)在神經(jīng)網(wǎng)絡(luò)訓(xùn)練到多少輪等等置鼻,類(lèi)似于一個(gè)鐘表镇饮。
大致的用法如下:
import tensorflow as tf
import numpy as np
x = tf.placeholder(tf.float32, shape=[None, 1], name='x')
y = tf.placeholder(tf.float32, shape=[None, 1], name='y')
w = tf.Variable(tf.constant(0.0))
global_steps = tf.Variable(0, trainable=False)
learning_rate = tf.train.exponential_decay(0.1, global_steps, 10, 2, staircase=False)
loss = tf.pow(w*x-y, 2)
train_step = tf.train.GradientDescentOptimizer(learning_rate).minimize(loss,global_step=global_steps)
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
for i in range(10):
sess.run(train_step, feed_dict={x:np.linspace(1,2,10).reshape([10,1]),
y:np.linspace(1,2,10).reshape([10,1])})
print(sess.run(learning_rate))
print(sess.run(global_steps))
可以注意到global_steps
被初始化為0,但是運(yùn)行上面的代碼我們會(huì)發(fā)現(xiàn)global_steps
每輪迭代都會(huì)自動(dòng)加1沃疮。如果把設(shè)定優(yōu)化器的那一行代碼中的global_step=global_steps
去掉盒让,global_step
的自動(dòng)加一就會(huì)失效。
綜上所述:損失函數(shù)優(yōu)化器的minimize()
中global_step=global_steps
能夠提供global_step
自動(dòng)加一的操作司蔬。參考自
使用滑動(dòng)平均模型
TensorFlow里面提供了tf.train.ExponentialMovingAverage
也就是上文已經(jīng)提到過(guò)的滑動(dòng)平均值的概念來(lái)實(shí)現(xiàn)滑動(dòng)平均模型邑茄。
在初始化模型時(shí)候,需要提供一個(gè)衰減率俊啼,這個(gè)衰減率將會(huì)控制模型更新的速度肺缕。同時(shí)滑動(dòng)平均模型對(duì)每一個(gè)變量都會(huì)維護(hù)一個(gè)影子變量(shadow variable),這個(gè)影子變量的初始值就是對(duì)應(yīng)變量的初始值授帕。但是每次運(yùn)行變量更新時(shí)候同木,影子變量的更新規(guī)則為:
在實(shí)際中decay的值會(huì)設(shè)定成十分靠近1的數(shù)值(0.99,0.999)。為了使模型前期訓(xùn)練地更快跛十,ExponentialMovingAverage
還提供了num_updates
參數(shù)來(lái)動(dòng)態(tài)控制decay的大小彤路,如果提供了這個(gè)參數(shù),每次使用的衰減率會(huì)從下面兩個(gè)數(shù)值中取較小值芥映。
mnist數(shù)據(jù)集上的實(shí)例展示
這個(gè)實(shí)例是書(shū)本第五章在mnist數(shù)據(jù)集上進(jìn)行訓(xùn)練的例子洲尊。主要為了展示上面提到的滑動(dòng)平均模型远豺,動(dòng)態(tài)學(xué)習(xí)率和規(guī)范化方法在TensorFlow中的使用方法。
在書(shū)中使用了tensorflow.examples.tutorials.mnist
這個(gè)工具包中的input_data
來(lái)自動(dòng)導(dǎo)入mnist數(shù)據(jù)集并分類(lèi)坞嘀。但是實(shí)際運(yùn)行過(guò)程中TensorFlow警告這個(gè)功能在不久的版本中就會(huì)被移除躯护,建議自己寫(xiě)程序?qū)雖nist數(shù)據(jù)集并且劃分訓(xùn)練集,驗(yàn)證集和測(cè)試集丽涩。加上終端運(yùn)行起來(lái)一開(kāi)始一直提示報(bào)錯(cuò)棺滞,所以直接又創(chuàng)建了一個(gè)load_mnist.py
來(lái)導(dǎo)入數(shù)據(jù)并對(duì)書(shū)中的代碼改動(dòng)了一部分。
load_mnist.py
第一部分代碼是載入mnist的代碼矢渊,參考自 這里面詳細(xì)介紹了mnist數(shù)據(jù)集的內(nèi)部格式和Python3中的導(dǎo)入方法继准。
import numpy as np
from struct import unpack
def __read_image(path):
with open(path, 'rb') as f:
magic, num, rows, cols = unpack('>4I', f.read(16))
img = np.fromfile(f, dtype=np.uint8).reshape(num, 784)
return img
def __read_label(path):
with open(path, 'rb') as f:
magic, num = unpack('>2I', f.read(8))
lab = np.fromfile(f, dtype=np.uint8)
return lab
def __normalize_image(image):
img = image.astype(np.float32) / 255.0
return img
def __one_hot_label(label):
lab = np.zeros((label.size, 10))
for i, row in enumerate(lab):
row[label[i]] = 1
return lab
def load_mnist(train_image_path, train_label_path, test_image_path, test_label_path, normalize=True, one_hot=True):
'''讀入MNIST數(shù)據(jù)集
Parameters
----------
normalize : 將圖像的像素值正規(guī)化為0.0~1.0
one_hot_label :
one_hot為T(mén)rue的情況下,標(biāo)簽作為one-hot數(shù)組返回
one-hot數(shù)組是指[0,0,1,0,0,0,0,0,0,0]這樣的數(shù)組
Returns
----------
(訓(xùn)練圖像, 訓(xùn)練標(biāo)簽), (測(cè)試圖像, 測(cè)試標(biāo)簽)
'''
image_train = __read_image(train_image_path)
image_test = __read_image(test_image_path)
label_train = __read_label(train_label_path)
label_test = __read_label(test_label_path)
if normalize:
image_train = __normalize_image(image_train)
image_test = __normalize_image(image_test)
if one_hot:
label_train = __one_hot_label(label_train)
label_test = __one_hot_label(label_test)
return image_train, label_train, image_test, label_test
mnist_tf.py
這部分基本和書(shū)中代碼沒(méi)有區(qū)別昆淡,修改了幾個(gè)訓(xùn)練數(shù)據(jù)量的參數(shù)锰瘸,和因?yàn)椴皇褂?code>tensorflow.examples.tutorials.mnist進(jìn)行的改動(dòng)。
import tensorflow as tf
import load_mnist
# MNIST數(shù)據(jù)集相關(guān)常數(shù)
INPUT_NODE = 784 # 輸入層的節(jié)點(diǎn)數(shù)昂灵,對(duì)于MNIST數(shù)據(jù)集避凝,就是28*28的圖片
OUTPUT_NODE = 10 # 輸出層的節(jié)點(diǎn)數(shù),因?yàn)樾枰?-9標(biāo)注手寫(xiě)數(shù)字的結(jié)果
# 神經(jīng)網(wǎng)絡(luò)的一些配置參數(shù)
LAYER1_NODE = 500 # 本例子使用只有一層500個(gè)節(jié)點(diǎn)的隱藏層的網(wǎng)絡(luò)結(jié)構(gòu)作為樣例
BATCH_SIZE = 100 # 一個(gè)batch中的數(shù)據(jù)量眨补,越小越接近隨機(jī)梯度下降管削,越大越接近梯度下降
LEARNING_RATE_BASE = 0.8 # 基礎(chǔ)學(xué)習(xí)速率
LEARNING_RATE_DECAY = 0.99 # 使用動(dòng)態(tài)學(xué)習(xí)率的衰減率
REGULARIZATION_RATE = 0.0001 # 規(guī)范化參數(shù)
TRAINING_STEPS = 400 #訓(xùn)練輪數(shù)
MOVING_AVERAGE_DECAY = 0.99 #滑動(dòng)平均衰減率
# 下面定義了一個(gè)輔助函數(shù)給定神經(jīng)網(wǎng)絡(luò)的輸入和所有參數(shù),計(jì)算出神經(jīng)網(wǎng)絡(luò)的前向傳播結(jié)果撑螺。
# 定義了一個(gè)ReLU激活函數(shù)含思,設(shè)定了輸入層,一層隱層甘晤,一層輸出層的最簡(jiǎn)單的神經(jīng)網(wǎng)絡(luò)結(jié)構(gòu)
def inference(input_tensor, avg_class, weights1, biases1, weights2, biases2):
# avg_class是決定用不用滑動(dòng)平均模型的參數(shù)含潘,當(dāng)沒(méi)有提供滑動(dòng)平均類(lèi)時(shí),直接使用參數(shù)當(dāng)前的值线婚。
# weights1 和 biases1 是隱層參數(shù)遏弱,weights2 和 biases2 是輸出層參數(shù)
# 這里沒(méi)有加上softmax層是因?yàn)楹竺娑x代價(jià)函數(shù)的時(shí)候用的是sparse_softmax_cross_entropy_with_logits函數(shù)已經(jīng)附帶了softmax
if avg_class == None:
layer1 = tf.nn.relu(tf.matmul(input_tensor, weights1) + biases1)
return tf.matmul(layer1, weights2) + biases2
else:
# 首先使用avg_class.average函數(shù)來(lái)計(jì)算得出變量的滑動(dòng)平均值
# avg_class 的申明會(huì)在后面申明,這里作為已知的滑動(dòng)平均類(lèi)參數(shù)使用
layer1 = tf.nn.relu(tf.matmul(input_tensor, avg_class.average(weights1)) + avg_class.average(biases1))
return tf.matmul(layer1, avg_class.average(weights2)) + avg_class.average(biases2)
def train(train_images, train_labels, test_images, test_labels):
x = tf.placeholder(tf.float32, [None, INPUT_NODE], name='x-input')
y_ = tf.placeholder(tf.float32, [None, OUTPUT_NODE], name='y-output')
#生成隱藏層的參數(shù)
weights1 = tf.Variable(tf.truncated_normal([INPUT_NODE, LAYER1_NODE], stddev=0.1))
biases1 = tf.Variable(tf.constant(0.1, shape=[LAYER1_NODE]))
#生成輸出層的參數(shù)
weights2 = tf.Variable(tf.truncated_normal([LAYER1_NODE, OUTPUT_NODE], stddev=0.1))
biases2 = tf.Variable(tf.constant(0.1, shape=[OUTPUT_NODE]))
#計(jì)算不使用滑動(dòng)平均值的前向傳播的值
y = inference(x, None, weights1, biases1, weights2, biases2)
#定義存儲(chǔ)訓(xùn)練輪數(shù)的變量塞弊,這個(gè)變量就算在使用滑動(dòng)平均模型的情況下也不需要滑動(dòng)平均所以設(shè)置為不可訓(xùn)練變量
#把這個(gè)global_step變量初始化為0漱逸,之后這個(gè)變量每輪迭代會(huì)自動(dòng)加一
global_step = tf.Variable(0, trainable=False)
#給定滑動(dòng)平均衰減率和訓(xùn)練輪數(shù)的變量,初始化滑動(dòng)平均類(lèi)游沿。
variable_average = tf.train.ExponentialMovingAverage(MOVING_AVERAGE_DECAY, global_step)
#在所有需要的變量進(jìn)行滑動(dòng)平均饰抒。tf_trainable_variables返回的就是在GraphKeys.TRAINABLE_VARIABLES中的元素。
variables_average_op = variable_average.apply(tf.trainable_variables())
#計(jì)算使用了滑動(dòng)平均之后的前向傳播的結(jié)果
average_y = inference(x, variable_average, weights1, biases1, weights2, biases2)
#使用交叉熵函數(shù)作為損失函數(shù)诀黍,然后在計(jì)算代價(jià)之前使用softmax
#這里使用TensorFlow里的sparse_softmax_cross_entropy_with_logits函數(shù),值得注意的是這邊使用的是不經(jīng)過(guò)滑動(dòng)平均的前向傳播值計(jì)算的代價(jià)函數(shù)值
#因?yàn)閙nist給出的標(biāo)準(zhǔn)答案是一個(gè)長(zhǎng)度為10的一維數(shù)組袋坑,而該函數(shù)需要返回的是一個(gè)正確答案的數(shù)字,所以需要使用tf.argmax函數(shù)進(jìn)行一下轉(zhuǎn)化
#tf.argmax的第二個(gè)參數(shù)為‘1’時(shí)候表示選取最大值的操作僅在第一個(gè)緯度中進(jìn)行眯勾,相當(dāng)于按行找咒彤。為‘0’時(shí)候相當(dāng)一按列找最大值疆柔。返回值都是最大值的下標(biāo)。
cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=y, labels=tf.argmax(y_, 1))
#計(jì)算當(dāng)前batch中所有樣例的交叉熵代價(jià)平均值
cross_entropy_mean = tf.reduce_mean(cross_entropy)
#定義一個(gè)regularizer
regularizer = tf.contrib.layers.l2_regularizer(REGULARIZATION_RATE)
#計(jì)算模型的規(guī)范化
regularization = regularizer(weights1) + regularizer(weights2)
#總損失等于規(guī)范化損失加上原本的交叉熵?fù)p失
loss = cross_entropy_mean + regularization
#設(shè)置動(dòng)態(tài)指數(shù)衰減的學(xué)習(xí)率
learning_rate = tf.train.exponential_decay(
LEARNING_RATE_BASE, #基礎(chǔ)學(xué)習(xí)率
global_step,
40000 / BATCH_SIZE, #總訓(xùn)練數(shù)據(jù)量除以batch大小等于所需要的迭代輪數(shù)
LEARNING_RATE_DECAY)
#使用tf.train.GradientDescentOptimizer優(yōu)化算法來(lái)優(yōu)化損失
train_step = tf.train.GradientDescentOptimizer(learning_rate).minimize(loss, global_step=global_step)
#在訓(xùn)練神經(jīng)網(wǎng)絡(luò)模型的時(shí)候镶柱,需要用反向傳播不斷更新神經(jīng)網(wǎng)絡(luò)的參數(shù),同時(shí)需要更新每一個(gè)變量的滑動(dòng)平均值模叙。
#為了一次完成這幾個(gè)操作, TensorFlow提供了tf.group機(jī)制歇拆,相當(dāng)于是把兩個(gè)操作符分組組成一個(gè)操作符, 這里把優(yōu)化器的動(dòng)態(tài)修改操作和滑動(dòng)平均操作修改操作合并
train_op = tf.group(train_step, variables_average_op)
#判斷使用了滑動(dòng)平均模型的神經(jīng)網(wǎng)絡(luò)的前向傳播結(jié)果是否和標(biāo)準(zhǔn)結(jié)果一致
correct_prediction = tf.equal(tf.argmax(average_y, 1),tf.argmax(y_, 1))
#tf.cast(x, dtype, name=None) 轉(zhuǎn)化數(shù)據(jù)格式的函數(shù),這里把一個(gè)布爾型的數(shù)值轉(zhuǎn)化為實(shí)數(shù)型范咨,方便均值的操作
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
#初始化會(huì)話(huà)并開(kāi)始訓(xùn)練
with tf.Session() as sess:
tf.global_variables_initializer().run() #變量初始化
#準(zhǔn)備驗(yàn)證數(shù)據(jù)
validate_feed = {x: train_images[55000:], y_: train_labels[55000:]}
#準(zhǔn)備測(cè)試數(shù)據(jù)
test_feed = {x: test_images, y_: test_labels}
#迭代地開(kāi)始訓(xùn)練
for i in range(TRAINING_STEPS):
#每100輪輸出一次在驗(yàn)證數(shù)據(jù)集上的測(cè)試結(jié)果故觅。
if i % 100 == 0:
validate_acc = sess.run(accuracy, feed_dict=validate_feed)
print("After %d training steps, validation accuracy using average model is %g" % (i, validate_acc))
#產(chǎn)生這一輪使用的一個(gè)batch的訓(xùn)練數(shù)據(jù)
xs = train_images[i*BATCH_SIZE:(i+1)*BATCH_SIZE]
ys = train_labels[i*BATCH_SIZE:(i+1)*BATCH_SIZE]
sess.run(train_op, feed_dict={x: xs, y_: ys})
#在訓(xùn)練結(jié)束后輸出最終正確率
test_acc = sess.run(accuracy, feed_dict=test_feed)
print("After %d training steps, test accuracy using average model is %g" % (TRAINING_STEPS, test_acc))
def main(argv=None):
train_image_path = './train-images.idx3-ubyte'
train_label_path = './train-labels.idx1-ubyte'
test_image_path = './t10k-images.idx3-ubyte'
test_label_path = './t10k-labels.idx1-ubyte'
train_images, train_labels, test_images, test_labels = load_mnist.load_mnist(train_image_path, train_label_path, test_image_path, test_label_path, normalize=True, one_hot=True)
train(train_images, train_labels, test_images, test_labels)
# TensorFlow提供的一個(gè)主程序入口,tf.app.run會(huì)調(diào)用上面的這個(gè)main函數(shù)
if __name__ == '__main__':
tf.app.run()
運(yùn)行結(jié)果
<img src='http://breezepicture.oss-cn-beijing.aliyuncs.com/Tensorflow/outcome.png'>
變量管理
在上面的演示代碼中有個(gè)計(jì)算前向傳播的函數(shù)
def inference(input_tensor, avg_class, weights1, biases1, weights2, biases2)
可以看到我們每次使用的時(shí)候都要輸入眾多的參數(shù)渠啊。對(duì)此改進(jìn)的話(huà)输吏,變量的管理可以使用tf.get_variable()
和tf.variable_scope()
首先來(lái)看tf.get_variable()
和tf.Variable()
的區(qū)別。tf.get_variable()
也可以用來(lái)創(chuàng)建新的變量替蛉,除此之外還可以配合tf.variable_scope
獲取變量贯溅。
#下面的兩種變量的命名是等價(jià)的
v = tf.get_variable("v", shape=[1], initializer=tf.constant_initializer(1.0))
v = tf.Variable(tf.constant(1.0, shape=[1]), name="v")
值得注意的是在tf.Variable()
里面name="v"
這個(gè)指定變量名稱(chēng)的參數(shù)不是一個(gè)必要的參數(shù),但在tf.get_variable()
里面就是一個(gè)必填的參數(shù)躲查,因?yàn)檫@個(gè)函數(shù)有時(shí)候還需要去根據(jù)函數(shù)名字獲取變量它浅。
如果需要tf.get_variable()
獲取一個(gè)已經(jīng)創(chuàng)建的變量,需要通過(guò)tf.variable_scope()
函數(shù)生成一個(gè)上下文管理器镣煮。這個(gè)函數(shù)相當(dāng)于生成了一個(gè)命名空間姐霍,具有變量的管理功能。
值得注意的基本有下面幾點(diǎn):
- 同一個(gè)命名空間中不可以有同名變量
- tf.variable_scope有一個(gè)參數(shù)reuse典唇,設(shè)置為T(mén)rue的話(huà)可以獲取其他命名空間或者自己空間里已經(jīng)命名的變量镊折。
- 創(chuàng)建命名空間可以嵌套
- 在一個(gè)命名空間里面創(chuàng)建的變量名稱(chēng)都會(huì)帶上這個(gè)命名空間名作為前綴。
#在名字為foo的命名空間內(nèi)創(chuàng)建名字為v的變量
with tf.variable_scope("test"):
v1 = tf.get_variable("v", shape=[1], initializer=tf.constant_initializer(1.0))
print(v.name) #輸出test/v:0, 'test/v'表示是test這個(gè)命名空間下名為v的變量介衔,':0'表示是v1這個(gè)運(yùn)算下輸出的第一個(gè)結(jié)果
#如果你再想在test這個(gè)命名空間內(nèi)創(chuàng)建一個(gè)命名為"v"的同名變量會(huì)報(bào)錯(cuò):
#Variable test/v already exists
with tf.variable_scope("test"):
v2 = tf.get_variable("v", [1])
#命名空間的嵌套
with tf.variable_scope("test"):
with tf.variable_scope("foo"):
v3 = tf.get_variable("v", [1])
print(v3.name) # 輸出test/foo/v:0
v4 = tf.get_variable("v1", [1])
print(v4.name) # 輸出test/v1:0恨胚。當(dāng)一個(gè)命名空間退出后,前綴變化夜牡。
#創(chuàng)建一個(gè)新的命名空間与纽,調(diào)用test里的變量√磷埃可以直接通過(guò)帶命名空間名稱(chēng)的變量名來(lái)獲取其他命名空間下的變量
with tf.variable_scope("reference", reuse=True)
v5 = tf.get_variable("test/foo/v", [1])
print(v5 == v3) #輸出True
v6 = tf.get_variable("test/v1", [1])
print(v6 == v4) #輸出True
這時(shí)候我們對(duì)之前提到inference
函數(shù)進(jìn)行優(yōu)化修改
def inference(input_tensor, reuse=False):
#定義第一層神經(jīng)網(wǎng)絡(luò)的變量和前向傳播的過(guò)程
with tf.variable_scope('layer1', reuse=reuse):
#根據(jù)傳進(jìn)來(lái)的reuse參數(shù)來(lái)決定是創(chuàng)建新變量還是使用已經(jīng)創(chuàng)建好的急迂。
#在第一次構(gòu)造網(wǎng)絡(luò)時(shí)候必然需要?jiǎng)?chuàng)建新的變量,以后每次調(diào)用這個(gè)函數(shù)時(shí)候把reuse這個(gè)變量設(shè)置為T(mén)rue就可以
weights = tf.get_variable("weights", [INPUT_NODE, LAYER1_NODE], initializer=tf.truncated_normal_initializer(stddev=0.1))
biases = tf.get_variable("biases", [LAYER1_NODE], initializer=tf.constant_initializer(0.0))
layer1 = tf.nn.relu(tf.matmul(input_tensor, weights) + biases)
#類(lèi)似的定義第二層神經(jīng)網(wǎng)絡(luò)的變量和前向傳播過(guò)程
with tf.variable_scope('layer2', reuse=reuse):
weights = tf.get_variable("weights", [LAYER1_NODE, OUTPUT_NODE], initializer=tf.truncated_normal_initializer(stddev=0.1))
biases = tf.get_variable("biases", [OUTPUT_NODE], initializer=tf.constant_initializer(0.0))
layer2 = tf.matmul(layer1, weights) + biases
return layer2
這樣寫(xiě)的話(huà)就不用將所有變量都作為參數(shù)傳遞到函數(shù)中了
def train(train_images, train_labels, test_images, test_labels):
x = tf.placeholder(tf.float32, [None, INPUT_NODE], name='x-input')
y_ = tf.placeholder(tf.float32, [None, OUTPUT_NODE], name='y-output')
#生成隱藏層的參數(shù)
weights1 = tf.Variable(tf.truncated_normal([INPUT_NODE, LAYER1_NODE], stddev=0.1))
biases1 = tf.Variable(tf.constant(0.1, shape=[LAYER1_NODE]))
#生成輸出層的參數(shù)
weights2 = tf.Variable(tf.truncated_normal([LAYER1_NODE, OUTPUT_NODE], stddev=0.1))
biases2 = tf.Variable(tf.constant(0.1, shape=[OUTPUT_NODE]))
#計(jì)算不使用滑動(dòng)平均值的前向傳播的值
y = inference(x, None, weights1, biases1, weights2, biases2)
這么一長(zhǎng)串提前生成變量的操作被整合到inference中蹦肴,方便復(fù)用
#第一次調(diào)用
x = tf.placeholder(tf.float32, [None, INPUT_NODE], name="x-input')
y = inference(x)
#復(fù)用
new_x = ...
new_y = inference(new_x, True)
模型持久化
模型持久化指的是每次訓(xùn)練完的模型保存下來(lái)方便下次使用僚碎,這樣訓(xùn)練結(jié)果可以復(fù)用。對(duì)此阴幌,TensorFlow提供了一個(gè)tf.train.Saver
類(lèi)勺阐。
saver=tf.train.Saver(max_to_keep=1)
在創(chuàng)建這個(gè)Saver對(duì)象的時(shí)候卷中,有一個(gè)參數(shù)我們經(jīng)常會(huì)用到,就是max_to_keep
參數(shù)渊抽,這個(gè)是用來(lái)設(shè)置保存模型的個(gè)數(shù)蟆豫,默認(rèn)為5,即max_to_keep=5
,保存最近的5個(gè)模型。
如果你想每訓(xùn)練一代(epoch)就想保存一次模型抵怎,則可以將max_to_keep
設(shè)置為None或者0泼菌。
保存
saver.save(sess,'ckpt/mnist.ckpt',global_step=step)
第一個(gè)參數(shù)sess, 第二個(gè)參數(shù)設(shè)定保存的路徑和名字,第三個(gè)參數(shù)將訓(xùn)練的次數(shù)作為后綴加入到模型名字中。
import tensorflow as tf
v1 = tf.Variable(tf.constant(1.0, shape=[1]), name="v1")
v2 = tf.Variable(tf.constant(2.0, shape=[1]), name="v2")
result = v1 + v2
init_op = tf.global_variables_initializer()
#聲明tf.train.Saver()類(lèi)用來(lái)保存模型
saver = tf.train.Saver(max_to_keep=3)
with tf.Session() as sess:
sess.run(init_op)
#將模型保存到ckpt文件夾下
saver.save(sess, "ckpt/model.ckpt")
雖然上面只指定了一個(gè)文件路徑,但是在目錄里面會(huì)出現(xiàn)三個(gè)文件。上面代碼生成model.ckpt.meta
保存計(jì)算圖結(jié)構(gòu)由驹,model.ckpt
保存每個(gè)變量的取值,最后一個(gè)文件為checkpoint
保存了目錄下所有模型文件的列表昔园。
加載
saver.restore(sess, "ckpt/model.ckpt")
import tensorflow as tf
v1 = tf.Variable(tf.constant(1.0, shape=[1]), name="v1")
v2 = tf.Variable(tf.constant(2.0, shape=[1]), name="v2")
result = v1 + v2
saver = tf.train.Saver(max_to_keep=3)
with tf.Session() as sess:
#加載已經(jīng)保存的模型蔓榄,并通過(guò)已經(jīng)保存的模型中變量的值來(lái)計(jì)算加法
saver.restore(sess, "ckpt/model.ckpt")
print(sess.run(result))
在加載模型的代碼中沒(méi)有運(yùn)行變量初始化,而是直接將變量的值通過(guò)已經(jīng)保存的模型加載進(jìn)來(lái)蒿赢。
除了上面保存和加載了整個(gè)模型全部變量的例子润樱,也可以選擇保存或者加載部分變量∠劭茫可以提供一個(gè)列表來(lái)指定需要保存或者加載的變量壹若。
如只加載上面的"v1"變量:
saver = tf.train.Saver([v1])
變量重命名
tf.train.Saver類(lèi)也支持在保存或者加載時(shí)候給變量重命名,用字典將模型保存時(shí)的變量名和需要加載的變量聯(lián)系起來(lái)皂冰。這樣做的主要目的之一是方便使用變量的滑動(dòng)平均值店展,因?yàn)槲覀冊(cè)谑褂昧嘶瑒?dòng)平均模型之后更關(guān)注的其實(shí)是各個(gè)變量經(jīng)過(guò)滑動(dòng)平均之后的值,我們只想用這個(gè)值代替原本的變量保存下來(lái)秃流。
import tensorflow as tf
v = tf.Variable(0, dtype=tf.float32, name="v")
ema = tf.train.ExponentialMovingAverage(0.99)
maintain_average_op = ema.apply(tf.global_variables())
#在申明滑動(dòng)平均模型之后赂蕴,TensorFlow會(huì)自動(dòng)生成一個(gè)影子變量
#v/ExponentialMoving Average
#可以通過(guò)變量重命名來(lái)將原來(lái)的變量v的滑動(dòng)平均值直接復(fù)制給v。
saver = tf.train.Saver({"v/ExponentialMovingAverage":v})
with tf.Session() as sess:
saver.restore(sess, "ckpt/model.ckpt")
print(sess.run(v)) #輸出模型中v的滑動(dòng)平均值
當(dāng)變量多的時(shí)候?yàn)榱朔奖悖?code>tf.train.ExponentialMovingAverage類(lèi)提供了variables_to_restore
函數(shù)來(lái)生成tf.train.Saver
類(lèi)所需要的重命名字典舶胀。
import tensorflow as tf
v = tf.Variable(0, dtype=tf.float32, name="v")
ema = tf.train.ExponentialMovingAverage(0.99)
#通過(guò)使用variables_to_restore函數(shù)可以直接生成上面代碼中提供的字典
#下面的print會(huì)輸出{'v/ExponentialMovingAverage':<tensorflow.Variable 'v:0' shape=() dtype=float32_ref>}
print(ema.variables_to_restore())
saver = tf.train.Saver(ema.variables_to_restore())
with tf.Session() as sess:
saver.restore(sess, "ckpt/model.ckpt")
print(sess.run(v)) #輸出模型中v的滑動(dòng)平均值
變量轉(zhuǎn)常量
使用tf.train.Saver會(huì)保存運(yùn)行TensorFlow需要的全部信息概说,然而在模型測(cè)試或者離線(xiàn)預(yù)測(cè)的時(shí)候,只需要知道如何從輸入層經(jīng)過(guò)前向傳播到輸出層嚣伐,并不需要知道變量初始化或者模型保存等輔助節(jié)點(diǎn)的信息糖赔。通過(guò)convert_variables_to_constants
函數(shù),可以將計(jì)算圖中的變量和取值方式通過(guò)常量的方式保存轩端,這樣整個(gè)TensorFlow計(jì)算圖可以統(tǒng)一存放在一個(gè)文件中放典,而不是像之前用tf.train.Saver
一樣需要分別存放在三個(gè)文件中。參考
存儲(chǔ)
import tensorflow as tf
from tensorflow.python.framework import graph_util
v1 = tf.Variable(tf.constant(1.0, shape=[1]), name="v1")
v2 = tf.Variable(tf.constant(2.0, shape=[1]), name="v2")
result = v1 + v2
init_op = tf.global_variables_initializer()
with tf.Session() as sess:
sess.run(init_op)
#導(dǎo)出當(dāng)前計(jì)算圖的GraphDef部分,主需要這部分就能完成從輸入層到輸出層的計(jì)算過(guò)程
graph_def = tf.get_default_graph().as_graph_def()
output_graph_def = graph_util.convert_variables_to_constants(sess, graph_def, ['add'])
with tf.gfile.GFile("ckpt/combined_model.pb", "wb") as f:
f.write(output_graph_def.SerializeToString())
如果只關(guān)心程序中定義的某些運(yùn)算的時(shí)候奋构,其他的節(jié)點(diǎn)就沒(méi)有必要導(dǎo)出并保存了壳影,上面就只是保存了兩個(gè)變量相加的計(jì)算節(jié)點(diǎn):add。在上面的代碼中['add']
參數(shù)給出了需要保存的節(jié)點(diǎn)的名稱(chēng)弥臼,這里給出的是計(jì)算節(jié)點(diǎn)的名稱(chēng)宴咧,所以后面不帶':0'。
(在前面介紹過(guò)張量的名稱(chēng)后面有:0醋火,表示是某個(gè)計(jì)算節(jié)點(diǎn)的第一個(gè)輸出悠汽,計(jì)算節(jié)點(diǎn)本身的名稱(chēng)后面是不帶0的)
讀取
import tensorflow as tf
from tensorflow.python.platform import gfile
with tf.Session() as sess:
model_filename = "ckpt/combined_model.pb"
#讀取保存的模型文件,并將文件解析成對(duì)應(yīng)的GraphDef Protocol Buffer
with open(model_filename, 'rb') as f:
graph_def = tf.GraphDef()
graph_def.ParseFromString(f.read())
# 將graph_def 中保存的圖加載到當(dāng)前的圖中芥驳。return_elements=["add:0"]給出了返回的張量的名稱(chēng)。
#在保存的時(shí)候給出的是計(jì)算節(jié)點(diǎn)的名稱(chēng)茬高,所以為"add"兆旬。在加載的時(shí)候給出的是張量的名稱(chēng),所以是"add:0"怎栽。
result = tf.import_graph_def(graph_def, return_elements=["add:0"])
print(sess.run(result))
結(jié)果:
<img src='http://breezepicture.oss-cn-beijing.aliyuncs.com/Tensorflow/saver_pb.png'>
-
graph_util
模塊是操作張量圖用的丽猬。 -
gfile模塊的介紹 值得注意的是open操作使用范圍很廣,還有就是
tf.gfile.FastGFile()
官方建議換成tf.gfile.GFile
實(shí)例展示
在定義 saver 的時(shí)候一般會(huì)定義最多保存模型的數(shù)量熏瞄,一般來(lái)說(shuō)脚祟,如果模型本身很大,我們需要考慮到硬盤(pán)大小强饮。如果你需要在當(dāng)前訓(xùn)練好的模型的基礎(chǔ)上進(jìn)行 fine-tune由桌,那么盡可能多的保存模型,后繼 fine-tune 不一定從最好的 ckpt 進(jìn)行邮丰,因?yàn)橛锌赡芤幌伦泳瓦^(guò)擬合了行您。但是如果保存太多,硬盤(pán)也有壓力剪廉。如果只想保留最好的模型娃循,方法就是每次迭代到一定步數(shù)就在驗(yàn)證集上計(jì)算一次 accuracy ,如果本次結(jié)果比上次好才保存新的模型斗蒋,否則沒(méi)必要保存捌斧。
下面展示保存準(zhǔn)確度最高的三代,并且在驗(yàn)證集上測(cè)試的代碼泉沾。參考
saver=tf.train.Saver(max_to_keep=3)
#訓(xùn)練階段
if is_train:
max_acc=0
f=open('ckpt/acc.txt','w')
for i in range(100):
batch_xs, batch_ys = mnist.train.next_batch(100)
sess.run(train_op, feed_dict={x: batch_xs, y_: batch_ys})
val_loss,val_acc=sess.run([loss,acc], feed_dict={x: mnist.test.images, y_: mnist.test.labels})
print('epoch:%d, val_loss:%f, val_acc:%f'%(i,val_loss,val_acc))
f.write(str(i+1)+', val_acc: '+str(val_acc)+'\n')
if val_acc>max_acc:
max_acc=val_acc
saver.save(sess,'ckpt/mnist.ckpt',global_step=i+1)
f.close()
#驗(yàn)證階段
else:
model_file=tf.train.latest_checkpoint('ckpt/')
saver.restore(sess,model_file)
val_loss,val_acc=sess.run([loss,acc], feed_dict={x: mnist.test.images, y_: mnist.test.labels})
print('val_loss:%f, val_acc:%f'%(val_loss,val_acc))
sess.close()
mnist示例展示的改進(jìn)版本
在上文中我們給出了mnist數(shù)據(jù)集上一個(gè)簡(jiǎn)單的樣例展示捞蚂,在介紹了變量管理和模型持久化之后,我們?cè)賹?duì)這個(gè)樣例進(jìn)行改進(jìn)爆哑,從這兩方面增加程序的可擴(kuò)展性洞难。
重構(gòu)后的代碼將會(huì)分為三個(gè)程序,對(duì)應(yīng)三個(gè)部分:神經(jīng)網(wǎng)絡(luò)結(jié)構(gòu)與參數(shù)的設(shè)置,網(wǎng)絡(luò)的訓(xùn)練队贱,網(wǎng)絡(luò)的測(cè)試色冀。
mnist_inference.py
定義神經(jīng)網(wǎng)絡(luò)的結(jié)構(gòu)和前向傳播方式
import tensorflow as tf
#定義結(jié)構(gòu)參數(shù)
INPUT_NODE = 784
OUTPUT_NODE = 10
LAYER1_NODE = 500
def inference(input_tensor, regularizer):
#如果一個(gè)程序多次調(diào)用了這個(gè)函數(shù),第一次調(diào)用之后就需要把reuse設(shè)置為T(mén)rue
with tf.variable_scope('fc1'):
fc1_weights = tf.get_variable(
"weight", [INPUT_NODE, LAYER1_NODE], initializer=tf.truncated_normal_initializer(stddev=0.1)
)
if regularizer != None:
tf.add_to_collection('losses', regularizer(fc1_weights))
fc1_biases = tf.get_variable(
"bias", [LAYER1_NODE], initializer=tf.constant_initializer(0.0)
)
fc1 = tf.nn.relu(tf.matmul(input_tensor, fc1_weights) + fc1_biases)
with tf.variable_scope('fc2'):
fc2_weights = tf.get_variable(
"weight", [LAYER1_NODE, OUTPUT_NODE], initializer=tf.truncated_normal_initializer(stddev=0.1)
)
if regularizer != None:
tf.add_to_collection('losses', regularizer(fc2_weights))
fc2_biases = tf.get_variable(
"bias", [OUTPUT_NODE], initializer=tf.constant_initializer(0.0)
)
logit = tf.matmul(fc1, fc2_weights) + fc2_biases
return logit
mnist_train.py
模型訓(xùn)練部分的代碼
import tensorflow as tf
import os
import load_mnist
import mnist_inference
#設(shè)置超參數(shù)
BATCH_SIZE = 10 # 一個(gè)batch中的數(shù)據(jù)量柱嫌,越小越接近隨機(jī)梯度下降锋恬,越大越接近梯度下降
LEARNING_RATE_BASE = 0.8 # 基礎(chǔ)學(xué)習(xí)速率
LEARNING_RATE_DECAY = 0.99 # 使用動(dòng)態(tài)學(xué)習(xí)率的衰減率
REGULARIZATION_RATE = 0.0001 # 規(guī)范化參數(shù)
TRAINING_STEPS = 5000 #訓(xùn)練輪數(shù)
MOVING_AVERAGE_DECAY = 0.99 #滑動(dòng)平均衰減率
#模型保存的路徑和文件名
MODEL_SAVE_PATH = "model"
MODEL_NAME = "model.ckpt"
def train(train_images, train_labels, test_images, test_labels):
x = tf.placeholder(tf.float32, [None, mnist_inference.INPUT_NODE], name='x-input')
y_ = tf.placeholder(tf.float32, [None, mnist_inference.OUTPUT_NODE], name='y-output')
#定義regularizer
regularizer = tf.contrib.layers.l2_regularizer(REGULARIZATION_RATE)
y = mnist_inference.inference(x, regularizer)
#定義存儲(chǔ)訓(xùn)練輪數(shù)的變量,這個(gè)變量就算在使用滑動(dòng)平均模型的情況下也不需要滑動(dòng)平均所以設(shè)置為不可訓(xùn)練變量
#把這個(gè)global_step變量初始化為0编丘,之后這個(gè)變量每輪迭代會(huì)自動(dòng)加一
global_step = tf.Variable(0, trainable=False)
#給定滑動(dòng)平均衰減率和訓(xùn)練輪數(shù)的變量与学,初始化滑動(dòng)平均類(lèi)。
variable_average = tf.train.ExponentialMovingAverage(MOVING_AVERAGE_DECAY, global_step)
#在所有需要的變量進(jìn)行滑動(dòng)平均嘉抓。tf_trainable_variables返回的就是在GraphKeys.TRAINABLE_VARIABLES中的元素索守。
variables_average_op = variable_average.apply(tf.trainable_variables())
#使用交叉熵函數(shù)作為損失函數(shù),然后在計(jì)算代價(jià)之前使用softmax
#這里使用TensorFlow里的sparse_softmax_cross_entropy_with_logits函數(shù),值得注意的是這邊使用的是不經(jīng)過(guò)滑動(dòng)平均的前向傳播值計(jì)算的代價(jià)函數(shù)值
#因?yàn)閙nist給出的標(biāo)準(zhǔn)答案是一個(gè)長(zhǎng)度為10的一維數(shù)組抑片,而該函數(shù)需要返回的是一個(gè)正確答案的數(shù)字卵佛,所以需要使用tf.argmax函數(shù)進(jìn)行一下轉(zhuǎn)化
#tf.argmax的第二個(gè)參數(shù)為‘1’時(shí)候表示選取最大值的操作僅在第一個(gè)緯度中進(jìn)行,相當(dāng)于按行找敞斋。為‘0’時(shí)候相當(dāng)一按列找最大值截汪。返回值都是最大值的下標(biāo)。
cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=y, labels=tf.argmax(y_, 1))
#計(jì)算當(dāng)前batch中所有樣例的交叉熵代價(jià)平均值
cross_entropy_mean = tf.reduce_mean(cross_entropy)
#總損失等于規(guī)范化損失加上原本的交叉熵?fù)p失
loss = cross_entropy_mean + tf.add_n(tf.get_collection('losses'))
#設(shè)置動(dòng)態(tài)指數(shù)衰減的學(xué)習(xí)率
learning_rate = tf.train.exponential_decay(
LEARNING_RATE_BASE, #基礎(chǔ)學(xué)習(xí)率
global_step,
50000 / BATCH_SIZE, #總訓(xùn)練數(shù)據(jù)量除以batch大小等于所需要的迭代輪數(shù)
LEARNING_RATE_DECAY)
#使用tf.train.GradientDescentOptimizer優(yōu)化算法來(lái)優(yōu)化損失
train_step = tf.train.GradientDescentOptimizer(learning_rate).minimize(loss, global_step=global_step)
#在訓(xùn)練神經(jīng)網(wǎng)絡(luò)模型的時(shí)候植捎,需要用反向傳播不斷更新神經(jīng)網(wǎng)絡(luò)的參數(shù)衙解,同時(shí)需要更新每一個(gè)變量的滑動(dòng)平均值。
with tf.control_dependencies([train_step, variables_average_op]):
train_op = tf.no_op(name='train')
#初始化TensorFlow持久化類(lèi)
saver = tf.train.Saver()
with tf.Session() as sess:
tf.global_variables_initializer().run()
#準(zhǔn)備驗(yàn)證數(shù)據(jù)
#在訓(xùn)練過(guò)程中不再測(cè)試模型在驗(yàn)證數(shù)據(jù)集上的表現(xiàn)焰枢,驗(yàn)證和測(cè)試在另一個(gè)獨(dú)立的程序中完成蚓峦。
for i in range(TRAINING_STEPS):
#產(chǎn)生這一輪使用的一個(gè)batch的訓(xùn)練數(shù)據(jù)
xs = train_images[i*BATCH_SIZE:(i+1)*BATCH_SIZE]
ys = train_labels[i*BATCH_SIZE:(i+1)*BATCH_SIZE]
_, loss_value, step = sess.run([train_op, loss, global_step], feed_dict={x: xs, y_: ys})
#每10輪輸出一次
if step % 100 == 0:
print("After %d training steps, loss on training batch is %g" % (step, loss_value))
#保存當(dāng)前的模型,這里添加了global_step參數(shù)医咨,這樣可以讓每個(gè)保存模型的文件名末尾加上訓(xùn)練的輪數(shù)
saver.save(sess, os.path.join(MODEL_SAVE_PATH, MODEL_NAME),global_step=global_step)
def main(argv=None):
train_image_path = './train-images.idx3-ubyte'
train_label_path = './train-labels.idx1-ubyte'
test_image_path = './t10k-images.idx3-ubyte'
test_label_path = './t10k-labels.idx1-ubyte'
train_images, train_labels, test_images, test_labels = load_mnist.load_mnist(train_image_path, train_label_path, test_image_path, test_label_path, normalize=True, one_hot=True)
train(train_images, train_labels, test_images, test_labels)
# TensorFlow提供的一個(gè)主程序入口枫匾,tf.app.run會(huì)調(diào)用上面的這個(gè)main函數(shù)
if __name__ == '__main__':
tf.app.run()
解釋?zhuān)?code>tf.control_dependencies(control_inputs)
此函數(shù)指定某些操作執(zhí)行的依賴(lài)關(guān)系,返回一個(gè)控制依賴(lài)的上下文管理器,使用 with 關(guān)鍵字可以讓在這個(gè)上下文環(huán)境中的操作都在 control_inputs 執(zhí)行之后才執(zhí)行拟淮。
with tf.control_dependencies([a, b]):
c = ....
d = ...
在執(zhí)行完 a干茉,b 操作之后,才能執(zhí)行 c很泊,d 操作角虫。意思就是 c,d 操作依賴(lài) a委造,b 操作戳鹅。
tf.no_op()
表示執(zhí)行完 train_step
, variable_averages_op
操作之后什么都不做。
mnist_eval.py
模型的評(píng)測(cè)部分代碼
import time
import tensorflow as tf
import mnist_inference
import mnist_train
import load_mnist
EVAL_INTERVAL_SECS = 10
def evaluate(train_images, train_labels, test_images, test_labels):
with tf.Graph().as_default() as g:
x = tf.placeholder(tf.float32, [None, mnist_inference.INPUT_NODE], name='x-input')
y_ = tf.placeholder(tf.float32, [None, mnist_inference.OUTPUT_NODE], name='y-input')
validate_feed = {x: train_images[55000:], y_: train_labels[55000:]}
y = mnist_inference.inference(x, None)
correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
variable_averages = tf.train.ExponentialMovingAverage(mnist_train.MOVING_AVERAGE_DECAY)
saver = tf.train.Saver(variable_averages.variables_to_restore())
while True:
with tf.Session() as sess:
# tf.train.get_checkpoint_state 函數(shù)會(huì)通過(guò)checkpoint 文件自動(dòng)找到目錄中最新模型的文件名
ckpt = tf.train.get_checkpoint_state(mnist_train.MODEL_SAVE_PATH)
if ckpt and ckpt.model_checkpoint_path:
saver.restore(sess, ckpt.model_checkpoint_path)
global_step = ckpt.model_checkpoint_path.split('/')[-1].split('-')[-1]
accuracy_score = sess.run(accuracy, feed_dict=validate_feed)
print("After %s training steps, validation accuracy = %g" % (global_step, accuracy_score))
else:
print('No checkpoint file found')
return
time.sleep(EVAL_INTERVAL_SECS)
def main(argv=None):
train_image_path = './train-images.idx3-ubyte'
train_label_path = './train-labels.idx1-ubyte'
test_image_path = './t10k-images.idx3-ubyte'
test_label_path = './t10k-labels.idx1-ubyte'
train_images, train_labels, test_images, test_labels = load_mnist.load_mnist(train_image_path, train_label_path, test_image_path, test_label_path, normalize=True, one_hot=True)
evaluate(train_images, train_labels, test_images, test_labels)
if __name__ == '__main__':
tf.app.run()
人數(shù)檢測(cè)的樣例代碼
在畢業(yè)設(shè)計(jì)進(jìn)行的無(wú)線(xiàn)信號(hào)電梯人數(shù)檢測(cè)工作中昏兆,對(duì)電梯內(nèi)的無(wú)線(xiàn)信號(hào)進(jìn)行特征提取后構(gòu)建了測(cè)試集與訓(xùn)練集枫虏。下面給出使用三層全連接層進(jìn)行神經(jīng)網(wǎng)絡(luò)訓(xùn)練的樣例代碼,其中并沒(méi)有使用滑動(dòng)平均模型,優(yōu)化器使用的是Adam隶债。
import scipy.io as sio
import numpy as np
import tensorflow as tf
from sklearn.preprocessing import OneHotEncoder
data = []
label = []
#從mat文件中導(dǎo)入做好的數(shù)據(jù)格式腾它。
dataset_train = sio.loadmat('train_data.mat')
dataset_test = sio.loadmat('test_data.mat')
#實(shí)驗(yàn)采集的電梯內(nèi)人數(shù)情況分為空載到電梯內(nèi)七人
kind = ['outcome_empty','outcome_one','outcome_two','outcome_three','outcome_four','outcome_five','outcome_six','outcome_seven']
#訓(xùn)練集和測(cè)試集里面每個(gè)場(chǎng)景測(cè)試了幾次
num_train = 7
num_test = 15
#選取的子載波數(shù)
num_subcarrier = 50
#標(biāo)注標(biāo)簽
for k in range(8): #八種人數(shù)場(chǎng)景
for i in range(num_train):
for j in range(num_subcarrier):
data.append(dataset_train[kind[k]][:,:,i][:,j])
label.append(k)
for k in range(8):
for i in range(num_test):
for j in range(num_subcarrier):
data.append(dataset_test[kind[k]][:,:,i][:,j])
label.append(k)
temp = np.array([data,label])
temp = temp.transpose()
#shuffle the samples
np.random.shuffle(temp)
#after transpose, images is in dimension 0 and label in dimension 1
#print(temp.shape)
train_data = list(temp[:8000,0])
train_label = list(temp[:8000,1])
train_data = np.array(train_data)
train_label = np.array(train_label)
test_data = list(temp[8000:,0])
test_label = list(temp[8000:,1])
test_data = np.array(test_data)
test_label = np.array(test_label)
ohe_tri = OneHotEncoder(sparse=False).fit(train_label.reshape(-1, 1))
ohe_tes = OneHotEncoder(sparse=False).fit(test_label.reshape(-1, 1))
train_label = ohe_tri.transform(train_label.reshape(-1, 1))
test_label = ohe_tes.transform(test_label.reshape(-1, 1))
#print(test_label[1])
#print(test_label.shape)
#constants
features = 6
hl_1 = 200
hl_2 = 400
hl_3 = 200
output_nodes = 8
epochs = 50
batch_size = 300
#hyperparameters
lr = 0.01 #learning rate,其實(shí)使用Adam不是固定學(xué)習(xí)速率這個(gè)超參不需要
# placholders
x = tf.placeholder('float', [None, features])
y = tf.placeholder('float', [None, output_nodes])
# return an object with weights and biases
def layer_setup(inputs, outputs):
layer = {
'weights': tf.Variable(tf.truncated_normal([inputs, outputs], stddev=0.1)),
'biases': tf.constant(0.1, shape=[outputs])
}
return layer
def network_setup(x):
# setup each layer
hidden_layer_1 = layer_setup(features, hl_1)
hidden_layer_2 = layer_setup(hl_1, hl_2)
hidden_layer_3 = layer_setup(hl_2, hl_3)
output = layer_setup(hl_3, output_nodes)
# forward prop
hl_1_result = tf.matmul(x, hidden_layer_1['weights']) + hidden_layer_1['biases']
hl_1_result = tf.nn.sigmoid(hl_1_result)
hl_2_result = tf.matmul(hl_1_result, hidden_layer_2['weights']) + hidden_layer_2['biases']
hl_2_result = tf.nn.sigmoid(hl_2_result)
hl_3_result = tf.matmul(hl_2_result, hidden_layer_3['weights']) + hidden_layer_3['biases']
hl_3_result = tf.nn.sigmoid(hl_3_result)
result = tf.matmul(hl_3_result, output['weights']) + output['biases']
result = tf.nn.softmax(result) # reduce to value between 0 and 1
#print(result[1])
return result
def losses(logits, labels):
with tf.variable_scope('loss') as scope:
cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=logits, labels=labels,
name='xentropy_per_example')
loss = tf.reduce_mean(cross_entropy, name='loss')
tf.summary.scalar(scope.name + '/loss', loss)
return loss
def train_network(x):
prediction = network_setup(x)
with tf.name_scope("Optimization"):
#cost = tf.reduce_mean( tf.squared_difference(y, prediction))
#cost = losses(y, prediction)
cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=y, logits=prediction))
optimizer = tf.train.AdamOptimizer().minimize(cost)
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
for epoch in range(epochs):
epoch_loss = 0
for i in range(int(len(train_data) / batch_size)):
epoch_x = train_data[i * batch_size: i * batch_size + batch_size]
epoch_y = train_label[i * batch_size: i * batch_size + batch_size]
_, c = sess.run([optimizer, cost], feed_dict={x: epoch_x, y: epoch_y})
epoch_loss += c
#print(i,':',c)
print('Epoch', epoch, 'completed out of',epochs,'loss:',epoch_loss)
#Test epoch
# Compare the predicted outcome against the expected outcome
correct = tf.equal(tf.round(prediction), y)
# Use the comparison to generate the accuracy
accuracy = tf.reduce_mean(tf.cast(correct, 'float'))
test_batch_amount = int(len(test_data) / batch_size)
final_accuracy = 0
for i in range(test_batch_amount):
epoch_x = test_data[i * batch_size: i * batch_size + batch_size]
epoch_y = test_label[i * batch_size: i * batch_size + batch_size]
final_accuracy += accuracy.eval(feed_dict={x: epoch_x, y: epoch_y})
#print(final_accuracy)
print("test accuracy %", final_accuracy / test_batch_amount * 100)
train_network(x)