TensorFlow_Python搭建神經網絡 (譯)

原文地址:http://adventuresinmachinelearning.com/python-tensorflow-tutorial/


正文:

谷歌的TensorFlow框架是近期深度學習領域的熱門話題。這個基于高性能數據流計算圖的開源項目十分適合于深度學習。TensorFlow支持單個或多個的CPU并行計算(并且支持GPU加速),為深度學習任務提供了一個很好的方案。
最新的1.0版添加了對移動端的支持,這篇TensorFlow教程將會對TensorFlow(Python版)的一些基本概念進行概述性地講解,為了能夠幫助讀者們搭建更加復雜的神經網絡模型,比如說卷積神經網絡,自然語言模型和復發神經網絡等。我們會搭建一個簡單的三層神經網絡用于識別MNIST手寫數字集(這個數據可以很容易地在網上找到)。

注意:在閱讀此教程前請先自行了解神經網絡基本知識

1.0 TensorFlow計算圖

計算圖是一種TensorFlow中的表現計算概念的方式,比如說一個表達式a = (b + c) * (c + 2)可以分解為如下三個式子

d = b + c
e = c + 2 
a = d * e

繪制成計算圖結果如下


簡單的計算圖

這種分解方式看起來很詭異,為什么要做這樣的處理呢?因為分解之后的式子可以做并行預算(比如說d = b + c 放在cpu1里面運算,而e = c + 2 放在cpu2(或者gpu)里面運算),然后再通過a = d * e合并起來,能夠大大縮短運算時間。在大數據和深度學習中效果尤為明顯。通過這個方法能夠在并行運算中獲得顯著的效率提升。
我們可以從下圖中看到一個三層神經網絡的運行方式(讀者們可以參看文章原地址,原圖是gif動圖):


TesnsorFlow數據流圖

在結點之間傳輸的數據流成為張量(tensors),是一種多維數組。這個輸入如張量的尺寸是5000×64×1,代表這是一個64節點的輸入層,其中包含了5000個訓練樣本。在輸入層之后是一個隱藏層,使用relu函數作為激活函數。最后是輸出層(在上面的圖中這個也叫邏輯層)使用交叉熵作為損失函數。在每個點中都可以看到每個張量都流入Gradients模塊,最后通過梯度下降優化器對模型進行反饋調整。
從圖中可以看到計算圖是如何表述神經網絡中的運算的。下面介紹如何用TensorFlow表示一些基本的數學運算。

2.0 一個簡單的TensorFlow的例子

讓我們從這個上面那個簡單的例子開始吧。
首先介紹一下TensorFlow的變量和常量,代碼如下:

import tensorflow as tf
#創建常量
const = tf.constant(2.0,name="const")
#創建變量
b = tf.Variable(2.0,name="b")
c = tf.Variable(1.0,name="c")

如上所述,TensorFlow中的常量需要使用tf.constant()函數聲明,變量使用tf.Variable()函數聲明。兩個函數中第一個參數是在初始化過程中會賦給常量/變量的值,第二個參數是可選參數,用于標記常量/變量(如果你想進行可視化能夠用到這個功能,將在以后的文章中介紹)。就像在Python中一樣,TensorFlow能根據初始變量推測變量的數據類型,不過也可以自己指定數據類型,TensorFlow中包含了多種專屬數據類型,如tf.float32,tf.int32等等

值得注意的是,與Python中常用的變量聲明不同,我們上面的代碼中的變量和常量(以及后面的運算、計算圖等)并沒有真正地初始化,而是會在將來的初始化過程中進行初始化。

接下來我們將創建一些運算操作:

#創建運算
d = tf.add(b,c,name="d")
e = tf.add(c,const,name="e")
a = tf.multiply(d,e,name="a")

TensorFlow中包含了很多種類的運算,我們將在后面介紹一部分。上述代碼是將add和multiply運算進行了實例化。

下一步是對代碼中的所有變量和計算圖結構進行初始化(當然也可以分別初始化):

#初始化
init_op = tf.global_variables_initializer()

接下來,為了進行變量常量之間的運算,我們需要建立一個TensorFlow會話——tf.session,所有的運算都會在tf.session中運行,為了免去關閉會話的麻煩,我們可以在with語句中進行:

#創建會話
with tf.Session() as sess:
  #初始化
  sess.run(init_op)
  #計算
  a_out = sess.run(a)
  print("Variable a is {}".format(a_out))

注意,上述代碼中的a是什么?
a = tf.multiply(d,e,name='a')
a是一個運算,不是一個變量,所以可以“run”。我們使用sess.run()命令對其進行運算,然后將結果復制給a_out,并打印出來。
注意,雖然我們在a之前定義了運算d和e,但是并不需要對d和e進行運算,TensorFlow會自動計算a所依賴的運算,可以使用TensorBoard功能查看這段代碼中產生的計算圖:


簡單的計算圖

2.1 TensorFlow的占位符

如果我們在聲明數組b的時候不知道數組b的值,可以使用占位符對數組b進行聲明:

#創建TensorFlow變量
b = tf.placeholder(tf.float32,[None,1],name='b')

在這個聲明中,我們沒有給數組b賦值,不過我們需要告訴TensorFlow數組b中包含的數據類型,這個例子中b包含的數據類型為tf.float32。第二個參數是將要傳入這個變量的數據的維度(shape),這里只指定了第二個維度,第一個維度可以為任意值,可以向b中傳入任意數量的一維數據。
在使用占位符之后,調用sess.run()的代碼有所變化:

a_out = sess.run(a,feed_dict={b:np.arrange(0,10)[:,np.newaxis]})
print("Variable a is {}".format(a_out))

這里我們傳入了一個名為feed_dict的Python數組,數組的鍵是占位符的名稱,值為占位符的值。

運行之后得到結果如下:

Variable a is [[  3.]
 [  6.]
 [  9.]
 [ 12.]
 [ 15.]
 [ 18.]
 [ 21.]
 [ 24.]
 [ 27.]
 [ 30.]]

下文中將利用上述知識搭建一個簡單的神經網絡來識別MNIST手寫數字。

3.0 搭建簡單的神經網絡模型

接下來本文將介紹如何使用TensorFlow創建一個簡單的三層神經網絡,在以后的文章中我們將演示如何搭建卷積神經網絡等結構更加復雜的神經網絡模型。如果你對神經網絡還不夠了解,請看作者關于神經網絡的教程[http://adventuresinmachinelearning.com/neural-networks-tutorial/]
在本例中,我們將使用TensorFlow庫中自帶的MNIST數據集,MNIST是一系列的28×28像素的灰階手寫數字圖像,其中包含了55000條訓練數據、10000條測試數據以及5000條驗證數據。
首先、導入數據:

from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets("./MNIST_data",one_hot=True)

one_hot參數為True代表數據集中數字的label并不是使用阿拉伯數字12345……而是使用一個向量,比如使用[0,0,0,0,1,0,0,0,0,0]表示數字4,這樣能更好地用于神經網絡的output層。

注意,以國內的網絡是很難自動下載MNIST數據集的,所以建議自行下載MNIST數據集,放在與代碼同目錄的MNIST文件夾中,程序會自動搜索。附下載地址:http://wiki.jikexueyuan.com/project/tensorflow-zh/tutorials/mnist_download.html

3.1 初始化

使用占位符先聲明訓練數據集

learning_rate = 0.5
epochs = 10
batch_size = 100
x = tf.placeholder(tf.float32,[None,784])
y = tf.placeholder(tf.float32,[None,10])

注意輸入數據x的784個節點代表了28×28(=784)像素,而輸出數據y的10個節點代表了十個數字。

接下來設置權重w和偏置項b:

#輸入層到隱藏層
W1 = tf.Variable(tf.random_normal([784, 300], stddev=0.03), name='W1')
b1 = tf.Variable(tf.random_normal([300]), name='b1')
#隱藏層到輸出層
W2 = tf.Variable(tf.random_normal([300, 10], stddev=0.03), name='W2')
b2 = tf.Variable(tf.random_normal([10]), name='b2')

上述代碼中,我們聲明了隱藏層和輸出層的權重,隱藏層中將會有300個節點,所以W1的大小為[784,300]。我們使用均值為0,方差為0.03的正態分布的隨機數來初始化權重,b1、W2、b2也是以同樣的方式進行了初始化。

注意初始值設定可能會影響最終的結果

然后根據前面聲明的變量以及我們即將使用的激活函數(relu)來計算神經網絡的輸出:

hidden_out = tf.add(tf.matmul(x, W1), b1)
hidden_out = tf.nn.relu(hidden_out)

上述代碼中,我們首先使用tf.matmul()函數計算了矩陣x和W1的乘積,然后再加上偏置項b1,然后將計算結果使用激活函數進行非線性轉換,得到隱藏層的節點值。
接下來處理輸出層y:

y_ = tf.nn.softmax(tf.add(tf.matmul(hidden_out, W2), b2))

與之前的隱藏層處理不同的是,這里對輸出層數據進行了SoftMax函數處理,這個函數能夠將預測結果轉化為概率分布,方便后面進行交叉熵的計算。

接下來我們將設定用于優化器的損失函數,在這個實例中我們將選擇交叉熵作為損失函數,公式如下:

交叉熵函數

在公式中,yj(i)是第j個輸出層節點的第i個訓練標簽,而yj-(i)是第j個輸出層節點的第i個預測標簽,m是每次提取的樣本數量,n是輸出的節點數,實現代碼如下:

y_clipped = tf.clip_by_value(y_, 1e-10, 0.9999999)
cross_entropy = -tf.reduce_mean(tf.reduce_sum(y * tf.log(y_clipped)
                         + (1 - y) * tf.log(1 - y_clipped), axis=1))

上述代碼中首先對y_進行了處理,將y_值限制在1e-10到0.99999之間,避免出現log(0)。
為了計算交叉熵,首先使用了TensorFlow中的tf.reduce_sum()函數計算張量的和,這里的張量指的是一個樣本中的一個節點的交叉熵:
y(i)j*log(yj_(i))+(1–y(i)j)log(1–yj_(i))
上面的y和y_clipped都是(m×10)維張量,我們首先要計算各個節點的交叉熵和,即對axis=1方向求和,得到(m×1)維張量,然后利用tf.reduce_mean()函數對這些張量取均值,

接下來需要設置優化器:

optimiser = tf.train.GradientDescentOptimizer(learning_rate=learning_rate).minimize(cross_entropy)

這里直接使用TensorFlow提供的梯度下降優化器,優化器中需要設置學習率(可以理解為優化過程中的一次調整的步長)和損失函數(這里設置為交叉熵,即每次調整都向能獲取更小的交叉熵的方向進行調整)。TensorFlow中有眾多的可選優化器,參見(https://www.tensorflow.org/api_guides/python/train

在正式開始訓練之前,還需要設置初始化函數和準確度函數:

init_op = tf.global_variables_initializer()
correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))

上述的correct_prediction中,使用tf.equal()True對y和y_進行了對比,返回了True或者False。tf.argmax()返回的是張量中最大數字的位置(在本例中其實就是1的位置,即標簽y和y_對應的數字)。這樣correct_prediction是一個(m×1)維布爾類型張量。
可以借助此張量計算平均準確率,首先使用tf.cast()函數將布爾值轉換為tf.float32變量,然后進行平均,得到準確率。

3.2 開始訓練

如今萬事俱備,可以開始訓練了,訓練代碼如下:

with tf.Session() as sess:
   sess.run(init_op)
   total_batch = int(len(mnist.train.labels) / batch_size)
   for epoch in range(epochs):
        avg_cost = 0
        for i in range(total_batch):
            batch_x, batch_y = mnist.train.next_batch(batch_size=batch_size)
             _, c = sess.run([optimiser, cross_entropy], 
                         feed_dict={x: batch_x, y: batch_y})
            avg_cost += c / total_batch
        print("Epoch:", (epoch + 1), "cost =", "{:.3f}".format(avg_cost))
   print(sess.run(accuracy, feed_dict={x: mnist.test.images, y: mnist.test.labels}))

上述代碼的實現步驟如下:

  1. 初始化各運算及變量
  2. 將數據隨機分為多個小樣本(next_batch函數用于提取樣本)
  3. 對小樣本進行訓練,訓練過程中輸出每一步訓練得到的交叉熵和最終的準確率。
    訓練中,我們運行了兩個操作,第一個是[optimiser,cross_entropy],列表中每個操作都會運行,得到兩個操作結果,但是我們并不關心優化器返回什么結果,只想知道cross_entropy(變量c)的變化。

最后訓練結束之后,打印出最終的準確率。
完整代碼如下,有興趣的讀者可以運行一下(記住要自己下載MNIST數據集):

from tensorflow.examples.tutorials.mnist import input_data
import tensorflow as tf

#導入數據
mnist = input_data.read_data_sets("./MNIST_data",one_hot=True)

#設置參數
learning_rate = 0.5#學習率
epochs = 10#訓練次數
batch_size = 100#每次取的樣本數量

#使用占位符聲明x和y
x = tf.placeholder(tf.float32,[None,784])
y = tf.placeholder(tf.float32,[None,10])

#聲明權重和偏置項

W1 = tf.Variable(tf.random_normal([784,300],stddev = 0.03),name="W1")
b1 = tf.Variable(tf.random_normal([300]),name='b1')
W2 = tf.Variable(tf.random_normal([300,10],stddev=0.03),name="W2")
b2 = tf.Variable(tf.random_normal([10]),name="b2")

#計算隱藏層
hidden_out = tf.add(tf.matmul(x,W1),b1)
hidden_out = tf.nn.relu(hidden_out)

#計算輸出值
y_ = tf.nn.softmax(tf.add(tf.matmul(hidden_out,W2),b2))

#計算交叉熵
y_clipped = tf.clip_by_value(y_,1e-10,0.9999999)
cross_entropy = -tf.reduce_mean(tf.reduce_sum(y*tf.log(y_clipped) + (1 - y)*tf.log(1-y_clipped),axis=1))

#定義優化器
optimiser = tf.train.GradientDescentOptimizer(learning_rate=learning_rate).minimize(cross_entropy)

#設置初始化函數
init_op = tf.global_variables_initializer()

#設置準確率函數
correct_prediction = tf.equal(tf.argmax(y,1),tf.argmax(y_,1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction,tf.float32))

#開始訓練
with tf.Session() as sess:
    sess.run(init_op)
    total_batch = int(len(mnist.train.labels)/batch_size)
    for epoch in range(epochs):#epochs=10,訓練10次
        avg_cost = 0
        for i in range(total_batch):
            batch_x ,batch_y = mnist.train.next_batch(batch_size=batch_size)
            _,c = sess.run([optimiser,cross_entropy],feed_dict={x:batch_x,
                                                                y:batch_y})
            avg_cost += c/total_batch
        print("Epoch:",(epoch+1),"cost=","{:.3f}".format(avg_cost))
    print(sess.run(accuracy,feed_dict={x:mnist.test.images,
                                       y:mnist.test.labels}))
#訓練過程中,優化器會對權重和偏置項進行調整,計算accuracy時,TensorFlow會根據最新的w和b對x進行預測,得到的預測值y_再與y進行對比

運行結果:

H:\ProgramData\Anaconda3\python.exe H:/kaggle/houseprice/analysis/DeepCNN.py
Extracting ./MNIST_data\train-images-idx3-ubyte.gz
Extracting ./MNIST_data\train-labels-idx1-ubyte.gz
Extracting ./MNIST_data\t10k-images-idx3-ubyte.gz
Extracting ./MNIST_data\t10k-labels-idx1-ubyte.gz
2017-12-16 14:37:58.355471: W c:\l\tensorflow_1501918863922\work\tensorflow-1.2.1\tensorflow\core\platform\cpu_feature_guard.cc:45] The TensorFlow library wasn't compiled to use SSE instructions, but these are available on your machine and could speed up CPU computations.
2017-12-16 14:37:58.355471: W c:\l\tensorflow_1501918863922\work\tensorflow-1.2.1\tensorflow\core\platform\cpu_feature_guard.cc:45] The TensorFlow library wasn't compiled to use SSE2 instructions, but these are available on your machine and could speed up CPU computations.
2017-12-16 14:37:58.355471: W c:\l\tensorflow_1501918863922\work\tensorflow-1.2.1\tensorflow\core\platform\cpu_feature_guard.cc:45] The TensorFlow library wasn't compiled to use SSE3 instructions, but these are available on your machine and could speed up CPU computations.
2017-12-16 14:37:58.355471: W c:\l\tensorflow_1501918863922\work\tensorflow-1.2.1\tensorflow\core\platform\cpu_feature_guard.cc:45] The TensorFlow library wasn't compiled to use SSE4.1 instructions, but these are available on your machine and could speed up CPU computations.
2017-12-16 14:37:58.355471: W c:\l\tensorflow_1501918863922\work\tensorflow-1.2.1\tensorflow\core\platform\cpu_feature_guard.cc:45] The TensorFlow library wasn't compiled to use SSE4.2 instructions, but these are available on your machine and could speed up CPU computations.
2017-12-16 14:37:58.356471: W c:\l\tensorflow_1501918863922\work\tensorflow-1.2.1\tensorflow\core\platform\cpu_feature_guard.cc:45] The TensorFlow library wasn't compiled to use AVX instructions, but these are available on your machine and could speed up CPU computations.
Epoch: 1 cost= 0.554
Epoch: 2 cost= 0.207
Epoch: 3 cost= 0.143
Epoch: 4 cost= 0.114
Epoch: 5 cost= 0.091
Epoch: 6 cost= 0.073
Epoch: 7 cost= 0.058
Epoch: 8 cost= 0.047
Epoch: 9 cost= 0.038
Epoch: 10 cost= 0.029
0.9776

Process finished with exit code 0

可以利用TensorBoard可視化工具查看到準確率的變化:


準確率提升過程

tensorboard內容將在以后的文章中介紹。

文章中有些網址是國外網址,不一定打得開~~~~~~~

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,527評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,687評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,640評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,957評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,682評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,011評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,009評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,183評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,714評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,435評論 3 359
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,665評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,148評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,838評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,251評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,588評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,379評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,627評論 2 380

推薦閱讀更多精彩內容