原文地址:http://adventuresinmachinelearning.com/python-tensorflow-tutorial/
正文:
谷歌的TensorFlow框架是近期深度學習領域的熱門話題饲嗽。這個基于高性能數(shù)據(jù)流計算圖的開源項目十分適合于深度學習。TensorFlow支持單個或多個的CPU并行計算(并且支持GPU加速),為深度學習任務提供了一個很好的方案雾消。
最新的1.0版添加了對移動端的支持荸恕,這篇TensorFlow教程將會對TensorFlow(Python版)的一些基本概念進行概述性地講解哮笆,為了能夠幫助讀者們搭建更加復雜的神經(jīng)網(wǎng)絡模型凛膏,比如說卷積神經(jīng)網(wǎng)絡神僵,自然語言模型和復發(fā)神經(jīng)網(wǎng)絡等腿宰。我們會搭建一個簡單的三層神經(jīng)網(wǎng)絡用于識別MNIST手寫數(shù)字集(這個數(shù)據(jù)可以很容易地在網(wǎng)上找到)呕诉。
注意:在閱讀此教程前請先自行了解神經(jīng)網(wǎng)絡基本知識
1.0 TensorFlow計算圖
計算圖是一種TensorFlow中的表現(xiàn)計算概念的方式,比如說一個表達式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合并起來伊者,能夠大大縮短運算時間英遭。在大數(shù)據(jù)和深度學習中效果尤為明顯。通過這個方法能夠在并行運算中獲得顯著的效率提升亦渗。
我們可以從下圖中看到一個三層神經(jīng)網(wǎng)絡的運行方式(讀者們可以參看文章原地址挖诸,原圖是gif動圖):
在結點之間傳輸?shù)臄?shù)據(jù)流成為張量(tensors),是一種多維數(shù)組法精。這個輸入如張量的尺寸是5000×64×1多律,代表這是一個64節(jié)點的輸入層,其中包含了5000個訓練樣本搂蜓。在輸入層之后是一個隱藏層狼荞,使用relu函數(shù)作為激活函數(shù)。最后是輸出層(在上面的圖中這個也叫邏輯層)使用交叉熵作為損失函數(shù)帮碰。在每個點中都可以看到每個張量都流入Gradients模塊相味,最后通過梯度下降優(yōu)化器對模型進行反饋調(diào)整。
從圖中可以看到計算圖是如何表述神經(jīng)網(wǎng)絡中的運算的收毫。下面介紹如何用TensorFlow表示一些基本的數(shù)學運算攻走。
2.0 一個簡單的TensorFlow的例子
讓我們從這個上面那個簡單的例子開始吧。
首先介紹一下TensorFlow的變量和常量此再,代碼如下:
import tensorflow as tf
#創(chuàng)建常量
const = tf.constant(2.0,name="const")
#創(chuàng)建變量
b = tf.Variable(2.0,name="b")
c = tf.Variable(1.0,name="c")
如上所述昔搂,TensorFlow中的常量需要使用tf.constant()函數(shù)聲明,變量使用tf.Variable()函數(shù)聲明输拇。兩個函數(shù)中第一個參數(shù)是在初始化過程中會賦給常量/變量的值摘符,第二個參數(shù)是可選參數(shù),用于標記常量/變量(如果你想進行可視化能夠用到這個功能策吠,將在以后的文章中介紹)逛裤。就像在Python中一樣,TensorFlow能根據(jù)初始變量推測變量的數(shù)據(jù)類型猴抹,不過也可以自己指定數(shù)據(jù)類型带族,TensorFlow中包含了多種專屬數(shù)據(jù)類型,如tf.float32,tf.int32等等
值得注意的是蟀给,與Python中常用的變量聲明不同蝙砌,我們上面的代碼中的變量和常量(以及后面的運算、計算圖等)并沒有真正地初始化跋理,而是會在將來的初始化過程中進行初始化择克。
接下來我們將創(chuàng)建一些運算操作:
#創(chuàng)建運算
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語句中進行:
#創(chuàng)建會話
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功能查看這段代碼中產(chǎn)生的計算圖:
2.1 TensorFlow的占位符
如果我們在聲明數(shù)組b的時候不知道數(shù)組b的值辫继,可以使用占位符對數(shù)組b進行聲明:
#創(chuàng)建TensorFlow變量
b = tf.placeholder(tf.float32,[None,1],name='b')
在這個聲明中怒见,我們沒有給數(shù)組b賦值,不過我們需要告訴TensorFlow數(shù)組b中包含的數(shù)據(jù)類型姑宽,這個例子中b包含的數(shù)據(jù)類型為tf.float32遣耍。第二個參數(shù)是將要傳入這個變量的數(shù)據(jù)的維度(shape),這里只指定了第二個維度炮车,第一個維度可以為任意值舵变,可以向b中傳入任意數(shù)量的一維數(shù)據(jù)。
在使用占位符之后瘦穆,調(diào)用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數(shù)組纪隙,數(shù)組的鍵是占位符的名稱,值為占位符的值扛或。
運行之后得到結果如下:
Variable a is [[ 3.]
[ 6.]
[ 9.]
[ 12.]
[ 15.]
[ 18.]
[ 21.]
[ 24.]
[ 27.]
[ 30.]]
下文中將利用上述知識搭建一個簡單的神經(jīng)網(wǎng)絡來識別MNIST手寫數(shù)字绵咱。
3.0 搭建簡單的神經(jīng)網(wǎng)絡模型
接下來本文將介紹如何使用TensorFlow創(chuàng)建一個簡單的三層神經(jīng)網(wǎng)絡,在以后的文章中我們將演示如何搭建卷積神經(jīng)網(wǎng)絡等結構更加復雜的神經(jīng)網(wǎng)絡模型熙兔。如果你對神經(jīng)網(wǎng)絡還不夠了解悲伶,請看作者關于神經(jīng)網(wǎng)絡的教程[http://adventuresinmachinelearning.com/neural-networks-tutorial/]。
在本例中黔姜,我們將使用TensorFlow庫中自帶的MNIST數(shù)據(jù)集拢切,MNIST是一系列的28×28像素的灰階手寫數(shù)字圖像蒂萎,其中包含了55000條訓練數(shù)據(jù)秆吵、10000條測試數(shù)據(jù)以及5000條驗證數(shù)據(jù)。
首先五慈、導入數(shù)據(jù):
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets("./MNIST_data",one_hot=True)
one_hot參數(shù)為True代表數(shù)據(jù)集中數(shù)字的label并不是使用阿拉伯數(shù)字12345……而是使用一個向量纳寂,比如使用[0,0,0,0,1,0,0,0,0,0]表示數(shù)字4主穗,這樣能更好地用于神經(jīng)網(wǎng)絡的output層。
注意毙芜,以國內(nèi)的網(wǎng)絡是很難自動下載MNIST數(shù)據(jù)集的忽媒,所以建議自行下載MNIST數(shù)據(jù)集,放在與代碼同目錄的MNIST文件夾中腋粥,程序會自動搜索晦雨。附下載地址:http://wiki.jikexueyuan.com/project/tensorflow-zh/tutorials/mnist_download.html
3.1 初始化
使用占位符先聲明訓練數(shù)據(jù)集
learning_rate = 0.5
epochs = 10
batch_size = 100
x = tf.placeholder(tf.float32,[None,784])
y = tf.placeholder(tf.float32,[None,10])
注意輸入數(shù)據(jù)x的784個節(jié)點代表了28×28(=784)像素,而輸出數(shù)據(jù)y的10個節(jié)點代表了十個數(shù)字隘冲。
接下來設置權重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個節(jié)點展辞,所以W1的大小為[784,300]奥邮。我們使用均值為0,方差為0.03的正態(tài)分布的隨機數(shù)來初始化權重罗珍,b1洽腺、W2、b2也是以同樣的方式進行了初始化覆旱。
注意初始值設定可能會影響最終的結果
然后根據(jù)前面聲明的變量以及我們即將使用的激活函數(shù)(relu)來計算神經(jīng)網(wǎng)絡的輸出:
hidden_out = tf.add(tf.matmul(x, W1), b1)
hidden_out = tf.nn.relu(hidden_out)
上述代碼中蘸朋,我們首先使用tf.matmul()函數(shù)計算了矩陣x和W1的乘積,然后再加上偏置項b1扣唱,然后將計算結果使用激活函數(shù)進行非線性轉(zhuǎn)換度液,得到隱藏層的節(jié)點值。
接下來處理輸出層y:
y_ = tf.nn.softmax(tf.add(tf.matmul(hidden_out, W2), b2))
與之前的隱藏層處理不同的是画舌,這里對輸出層數(shù)據(jù)進行了SoftMax函數(shù)處理堕担,這個函數(shù)能夠?qū)㈩A測結果轉(zhuǎn)化為概率分布,方便后面進行交叉熵的計算曲聂。
接下來我們將設定用于優(yōu)化器的損失函數(shù)霹购,在這個實例中我們將選擇交叉熵作為損失函數(shù),公式如下:
在公式中朋腋,yj(i)是第j個輸出層節(jié)點的第i個訓練標簽齐疙,而yj-(i)是第j個輸出層節(jié)點的第i個預測標簽,m是每次提取的樣本數(shù)量旭咽,n是輸出的節(jié)點數(shù)贞奋,實現(xià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之間穷绵,避免出現(xiàn)log(0)轿塔。
為了計算交叉熵,首先使用了TensorFlow中的tf.reduce_sum()函數(shù)計算張量的和,這里的張量指的是一個樣本中的一個節(jié)點的交叉熵:
y(i)j*log(yj_(i))+(1–y(i)j)log(1–yj_(i))
上面的y和y_clipped都是(m×10)維張量勾缭,我們首先要計算各個節(jié)點的交叉熵和揍障,即對axis=1方向求和,得到(m×1)維張量俩由,然后利用tf.reduce_mean()函數(shù)對這些張量取均值毒嫡,
接下來需要設置優(yōu)化器:
optimiser = tf.train.GradientDescentOptimizer(learning_rate=learning_rate).minimize(cross_entropy)
這里直接使用TensorFlow提供的梯度下降優(yōu)化器,優(yōu)化器中需要設置學習率(可以理解為優(yōu)化過程中的一次調(diào)整的步長)和損失函數(shù)(這里設置為交叉熵幻梯,即每次調(diào)整都向能獲取更小的交叉熵的方向進行調(diào)整)兜畸。TensorFlow中有眾多的可選優(yōu)化器,參見(https://www.tensorflow.org/api_guides/python/train)
在正式開始訓練之前碘梢,還需要設置初始化函數(shù)和準確度函數(shù):
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()返回的是張量中最大數(shù)字的位置(在本例中其實就是1的位置菲嘴,即標簽y和y_對應的數(shù)字)。這樣correct_prediction是一個(m×1)維布爾類型張量汰翠。
可以借助此張量計算平均準確率龄坪,首先使用tf.cast()函數(shù)將布爾值轉(zhuǎn)換為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}))
上述代碼的實現(xiàn)步驟如下:
- 初始化各運算及變量
- 將數(shù)據(jù)隨機分為多個小樣本(next_batch函數(shù)用于提取樣本)
- 對小樣本進行訓練妓局,訓練過程中輸出每一步訓練得到的交叉熵和最終的準確率。
訓練中呈宇,我們運行了兩個操作好爬,第一個是[optimiser,cross_entropy],列表中每個操作都會運行甥啄,得到兩個操作結果存炮,但是我們并不關心優(yōu)化器返回什么結果,只想知道cross_entropy(變量c)的變化蜈漓。
最后訓練結束之后穆桂,打印出最終的準確率。
完整代碼如下融虽,有興趣的讀者可以運行一下(記住要自己下載MNIST數(shù)據(jù)集):
from tensorflow.examples.tutorials.mnist import input_data
import tensorflow as tf
#導入數(shù)據(jù)
mnist = input_data.read_data_sets("./MNIST_data",one_hot=True)
#設置參數(shù)
learning_rate = 0.5#學習率
epochs = 10#訓練次數(shù)
batch_size = 100#每次取的樣本數(shù)量
#使用占位符聲明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))
#定義優(yōu)化器
optimiser = tf.train.GradientDescentOptimizer(learning_rate=learning_rate).minimize(cross_entropy)
#設置初始化函數(shù)
init_op = tf.global_variables_initializer()
#設置準確率函數(shù)
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}))
#訓練過程中享完,優(yōu)化器會對權重和偏置項進行調(diào)整,計算accuracy時有额,TensorFlow會根據(jù)最新的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內(nèi)容將在以后的文章中介紹彼绷。
文章中有些網(wǎng)址是國外網(wǎng)址,不一定打得開~~~~~~~