TensorFlow基礎(chǔ)教程(1)

主要根據(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文檔可以參考酌泰。

w3cschool上的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ì)介紹

英文原文地址 機(jī)器之心翻譯地址

在眾多的深度學(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;
}

proto3參考鏈接

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è)3\times2的矩陣傳入placeholder幢哨,否則x的數(shù)值不明確無(wú)法計(jì)算赡勘。相當(dāng)于placeholder是給需要的輸入數(shù)據(jù)在計(jì)算圖里面先預(yù)留了一個(gè)坑,真正運(yùn)行時(shí)候需要把需要的值填入捞镰。

設(shè)置代價(jià)函數(shù)

經(jīng)典代價(jià)函數(shù)

交叉熵函數(shù)

C = - \frac{1}{n}\sum_{x}[y\ln{a} + (1-y)\ln{(1-a)}]

其中y是正確的結(jié)果闸与,a是模型預(yù)測(cè)的結(jié)果毙替。

如果面對(duì)的是一個(gè)分類(lèi)的問(wèn)題y這個(gè)張量的值非0即1,那么上面的交叉熵函數(shù)退化為

C = - \frac{1}{n}\sum_{x}y\ln{a}

表示的代碼如下:

cross_entropy = -tf.reduce_mean(y * tf.log(tf.clip_by_value(a, 1e-10, 1.0)))

均方代價(jià)函數(shù)

C(w,b)\equiv\frac{1}{2n}\sum_{x}\|y(x)-a\|^2

代碼表示:

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(y,y^\prime)=\sum_{i=1}^nf(y_i,y_i^\prime)

f(x,y)= \bigg\{\begin{aligned} a(x-y)\qquad x>y\\\ b(y-x)\qquad x\leq y\end{aligned}

代碼表示:

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就是\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ī)則為:

shadow\_variable = decay \times shadow\_variable + (1-decay) \times variable

在實(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ù)值中取較小值芥映。

min\left\{decay,\frac{1+num\_updates}{10+num\_updates}\right\}

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)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市死讹,隨后出現(xiàn)的幾起案子瞒滴,更是在濱河造成了極大的恐慌,老刑警劉巖赞警,帶你破解...
    沈念sama閱讀 218,858評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件妓忍,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡愧旦,警方通過(guò)查閱死者的電腦和手機(jī)世剖,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)笤虫,“玉大人搁廓,你說(shuō)我怎么就攤上這事「ぃ” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,282評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵蝙场,是天一觀(guān)的道長(zhǎng)凌停。 經(jīng)常有香客問(wèn)我,道長(zhǎng)售滤,這世上最難降的妖魔是什么罚拟? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,842評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮完箩,結(jié)果婚禮上赐俗,老公的妹妹穿的比我還像新娘。我一直安慰自己弊知,他們只是感情好阻逮,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,857評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著秩彤,像睡著了一般叔扼。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上漫雷,一...
    開(kāi)封第一講書(shū)人閱讀 51,679評(píng)論 1 305
  • 那天瓜富,我揣著相機(jī)與錄音,去河邊找鬼降盹。 笑死与柑,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播价捧,決...
    沈念sama閱讀 40,406評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼丑念,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了干旧?” 一聲冷哼從身側(cè)響起渠欺,我...
    開(kāi)封第一講書(shū)人閱讀 39,311評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎椎眯,沒(méi)想到半個(gè)月后挠将,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,767評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡编整,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年舔稀,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片掌测。...
    茶點(diǎn)故事閱讀 40,090評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡内贮,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出汞斧,到底是詐尸還是另有隱情夜郁,我是刑警寧澤,帶...
    沈念sama閱讀 35,785評(píng)論 5 346
  • 正文 年R本政府宣布粘勒,位于F島的核電站竞端,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏庙睡。R本人自食惡果不足惜事富,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,420評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望乘陪。 院中可真熱鬧统台,春花似錦、人聲如沸啡邑。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,988評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)谣拣。三九已至募寨,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間森缠,已是汗流浹背拔鹰。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,101評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留贵涵,地道東北人列肢。 一個(gè)月前我還...
    沈念sama閱讀 48,298評(píng)論 3 372
  • 正文 我出身青樓恰画,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親瓷马。 傳聞我的和親對(duì)象是個(gè)殘疾皇子拴还,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,033評(píng)論 2 355