天天看點

《Python資料分析與機器學習實戰-唐宇迪》讀書筆記第18章--TensorFlow實戰

《Python資料分析與機器學習實戰-唐宇迪》讀書筆記第18章--TensorFlow實戰

唐宇迪的《跟着迪哥學:Python資料分析與機器學習實戰》,2019年9月出版,本系列為讀書筆記。主要是為了系統整理,加深記憶。 第18章TensorFlow實戰,本章從實戰的角度介紹Numpy工具包的核心子產品與常用函數的使用方法。

python資料分析個人學習讀書筆記-目錄索引

第18章TensorFlow實戰

   本章介紹深度學習架構——TensorFlow,可能大家還聽過一些其他的神經網絡架構,例如Caffe、Torch,其實這些都是工具,以輔助完成網絡模型搭建。現階段由于TensorFlow更主流一些,能做的事情相對更多,是以還是選擇使用更廣泛的TensorFlow架構。首先概述其基本使用方法,接下來就是搭建一個完整的神經網絡模型。

18.1TensorFlow基本操作

   TensorFlow是由谷歌開發和維護的一款深度學習架構,從2015年還沒釋出時就已經名聲大振,經過近4年的發展,已經成為一款成熟的神經網絡架構,可謂是深度學習界的首選。關于它的特征和性能,其官網已經給出各種優勢,大家簡單了解即可。https://tensorflow.google.cn/

  關于工具包的安裝,可以先用指令行嘗試運作“pip install tensorflow”指令。注意現階段TensorFlow隻支援Python3版本的運作。

   注意:該版本的最新版本是2.1.0,但本書的執行個體是基于1.X,而1.X版本中的很多子產品已被放棄。是以為了運作本章代碼,請安裝1.X版本。

1 pip uninstall tensorflow --tensorflow-2.1.0
 2 
 3 pip install tensorflow==1.9.0
 4 
 5 
 6 /* 
 7 D:\tools\Python37>pip install tensorflow==1.9.0
 8 Looking in indexes: https://pypi.tuna.tsinghua.edu.cn/simple
 9 ERROR: Could not find a version that satisfies the requirement tensorflow==1.9.0 (from versions: 1.13.0rc1, 1.13.0rc2, 1.13.1, 1.13.2, 
1.14.0rc0, 1.14.0rc1, 1.14.0, 1.15.0rc0, 1.15.0rc1, 1.15.0rc2, 1.15.0rc3, 1.15.0, 1.15.2, 2.0.0a0, 2.0.0b0, 2.0.0b1, 2.0.0rc0, 2.0.0rc1, 
2.0.0rc2, 2.0.0, 2.0.1, 2.1.0rc0, 2.1.0rc1, 2.1.0rc2, 2.1.0, 2.2.0rc0, 2.2.0rc1, 2.2.0rc2)
10 ERROR: No matching distribution found for tensorflow==1.9.0
11 
12  */
13 
14 pip install tensorflow==1.15.2
15 /*Looking in indexes: https://pypi.tuna.tsinghua.edu.cn/simple
16 Collecting tensorflow==1.15.2...
17 Installing collected packages: tensorboard, tensorflow-estimator, tensorflow
18   Attempting uninstall: tensorboard
19     Found existing installation: tensorboard 2.1.1
20     Uninstalling tensorboard-2.1.1:
21       Successfully uninstalled tensorboard-2.1.1
22   Attempting uninstall: tensorflow-estimator
23     Found existing installation: tensorflow-estimator 2.1.0
24     Uninstalling tensorflow-estimator-2.1.0:
25       Successfully uninstalled tensorflow-estimator-2.1.0
26 Successfully installed tensorboard-1.15.0 tensorflow-1.15.2 tensorflow-estimator-1.15.1
27 
28 */      

18.1.1TensorFlow特性

   (1)高靈活性:TensorFlow不僅用于神經網絡,而且隻要計算過程可以表示為一個資料流圖,就可以使用TensorFlow。TensorFlow提供了豐富的工具,以輔助組裝算法模型,還可以自定義很多操作。如果熟悉C++,也可以改底層,其核心代碼都是由C組成的,Python相當于接口。

  (2)可移植性:TensorFlow可以在CPU和GPU上運作,例如桌上型電腦、伺服器、手機移動裝置等,還可以在嵌入式裝置以及APP或者雲端服務與Docker中進行應用。

  (3)更新疊代迅速:深度學習與神經網絡發展迅速,經常會出現新的算法與模型結構,如果讓大家自己優化算法與模型結構可能較為複雜,TensorFlow随着更新會持續引進新的模型與結構,讓代碼更簡單。

  (4)自動求微分:基于梯度的機器學習算法會受益于TensorFlow自動求微分的能力。作為TensorFlow使用者,隻需要定義預測模型的結構,将這個結構和目标函數結合在一起。給定輸入資料後,TensorFlow将自動完成微分導數,相當于幫大家完成了最複雜的計算。

  (5)多語言支援:TensorFlow有一個合理的C++使用界面,也有一個易用的Python使用界面來建構和訓練網絡模型。最簡單實用的就是Python接口,也可以在互動式的ipython界面中用TensorFlow嘗試某些想法,它可以幫你将筆記、代碼、可視化等有條理地歸置好。随着更新更新,後續還會加入Go、Java、Lua、Javascript、R等語言接口。

  (6)性能最優化:TensorFlow給線程、隊列、異步操作等以最佳的支援,可以将硬體的計算潛能全部發揮出來,還可以自由地将TensorFlow圖中的計算元素配置設定到不同裝置上,并且幫你管理好這些不同副本。

  綜上所述,使用TensorFlow時,使用者隻需完成網絡模型設計,其他工作都可以放心地交給它來計算。

  Github應當是程式員最熟悉的平台,很明顯的趨勢就是TensorFlow成為最受大家歡迎的神經網絡架構。

  迪哥使用深度學習架構的感受:最開始使用的深度學習架構是Caffe,用起來十分便捷,基本不需要寫代碼,直接按照配置檔案、寫好網絡模型參數就可以訓練網絡模型。雖然Caffe使用起來很友善,但是所有功能必須是其架構已經實作好的,想要加入新功能就比較麻煩,而且Caffe基本上隻能玩卷積網絡,是以如果隻做圖像處理相關任務,可以考慮使用,對于自然語言處理,它就不适合了。

  TensorFlow相當于已經實作了你能想到的所有操作,例如神經網絡中不同功能層的定義、疊代計算、參數初始化等,是以大家需要做的就是按照流程将它們組合在一起即可。做了幾個項目之後,就會發現無論做什麼任務都是差不多的套路,很多模闆都是可以複用的。初學者可能會覺得稍微有點麻煩,因為很多地方必須按照它的要求來做,熟練之後就會覺得按照要求規範來做是最科學的。

18.1.2TensorFlow基本操作

   簡單介紹TensorFlow的各項優勢後,下面來看其最基本的使用方法,然後再來介紹神經網絡:

1 import tensorflow as tf
2 # https://tensorflow.google.cn/
3 tf.__version__
4 #'1.15.2'      
1 a = 3
 2 # 建立一個變量
 3 w = tf.Variable([[0.5,1.0]])
 4 x = tf.Variable([[2.0],[1.0]]) 
 5 # 建立一個操作
 6 y = tf.matmul(w, x)  
 7 
 8 
 9 #全局變量初始化
10 init_op = tf.global_variables_initializer()
11 with tf.Session() as sess:
12     sess.run(init_op)
13     print (y.eval())      
[[2.]]      

  上述代碼隻想計算一個行向量與列向量相乘,但是本來一行就能解決的問題,這裡卻過于複雜,下面就是TensorFlow進行計算操作的基本要求。

  • 1.當想建立一個變量的時候似乎有些麻煩,需要調用tf.Variable()再傳入實際的值,這是為了底層計算的高效性,所有資料結構都必須是tensor格式,是以先要對資料格式進行轉換。
  • 2.接下來建立一個操作y=tf.matmul(w,x),為什麼是建立而不是實際執行呢?此時相當于先寫好要做的任務流程,但還沒有開始做。
  • 3.再準備進行全局變量的初始化,因為剛才隻是設計了變量、操作的流程,還沒有實際放入計算區域中,這好比告訴士兵打仗前怎麼布陣,還沒有把士兵投放到戰場中,隻有把士兵投放到戰場中,才能實際發揮作用。
  • 4.建立Session(),這相當于士兵進入實際執行任務的戰場。最後sess.run(),隻有完成這一步,才能真正得到最終結果。

  最初的打算隻是要做一個矩陣乘法,卻要按照TensorFlow的設計規範寫這麼多代碼,估計大家的感受也是如此。

  等你完成一個實際項目的時候,就知道按照規範完成任務是多麼舒服的事。

1 tf.zeros([3, 4], int32) ==> [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
2 tf.zeros_like(tensor) ==> [[0, 0, 0], [0, 0, 0]]
3 tf.ones([2, 3], int32) ==> [[1, 1, 1], [1, 1, 1]]
4 tf.ones_like(tensor) ==> [[1, 1, 1], [1, 1, 1]]
5 tensor = tf.constant([1, 2, 3, 4, 5, 6, 7]) => [1 2 3 4 5 6 7]
6 tensor = tf.constant(-1.0, shape=[2, 3]) => [[-1. -1. -1.] [-1. -1. -1.]]
7 tf.linspace(10.0, 12.0, 3, name="linspace") => [ 10.0 11.0 12.0]
8 tf.range(start, limit, delta) ==> [3, 6, 9, 12, 15]      

  可以看出在使用TensorFlow時,很多功能函數的定義與Numpy類似,隻需熟悉即可,實際用的時候,它與Python工具包一樣,前期基本上是現用現查。

  接下來介紹TensorFlow中比較常用的函數功能,在變量初始化時,要随機生成一些符合某種分布的變量,或是拿到資料集後,要對資料進行洗牌的操作,現在這些都已經實作好了,直接調用即可。

1 #生成的值服從具有指定平均值和标準偏差的正态分布
 2 norm = tf.random_normal([2, 3], mean=-1, stddev=4)
 3 
 4 # 洗牌
 5 c = tf.constant([[1, 2], [3, 4], [5, 6]])
 6 shuff = tf.random_shuffle(c)
 7 
 8 # 每一次執行結果都會不同
 9 sess = tf.Session()
10 print (sess.run(norm))
11 print (sess.run(shuff))      
[[  8.602505  -13.47859    -8.434112 ]
 [ -2.6874518  -4.1306276   1.2184303]]
[[3 4]
 [1 2]
 [5 6]]      

  随機子產品的使用方法很簡單,但對于這些功能函數的使用方法來說,并不建議大家一口氣先學個遍,通過實際的案例和任務邊用邊學就足夠,其實這些隻是工具而已,知道其所需參數的含義以及輸出的結果即可。

1 state = tf.Variable(0)
 2 new_value = tf.add(state, tf.constant(1))
 3 update = tf.assign(state, new_value)
 4 
 5 with tf.Session() as sess:
 6     sess.run(tf.global_variables_initializer())
 7     print(sess.run(state))    
 8     for _ in range(3):
 9         sess.run(update)
10         print(sess.run(state))      
0
1
2
3      
1 import numpy as np
2 a = np.zeros((3,3))
3 ta = tf.convert_to_tensor(a)
4 with tf.Session() as sess:
5      print(sess.run(ta))      
[[0. 0. 0.]
 [0. 0. 0.]
 [0. 0. 0.]]      
1 a = tf.constant(5.0)
 2 b = tf.constant(10.0)
 3 
 4 x = tf.add(a, b, name="add")
 5 y = tf.div(a, b, name="divide")
 6 
 7 with tf.Session() as sess:
 8     print("a =", sess.run(a))
 9     print("b =", sess.run(b))
10     print("a + b =", sess.run(x))
11     print("a/b =", sess.run(y))      
WARNING:tensorflow:From <ipython-input-8-df098e10b857>:5: 
div (from tensorflow.python.ops.math_ops) is deprecated and will be removed in a future version.
Instructions for updating:
Deprecated in favor of operator or tf.math.divide.
a = 5.0
b = 10.0
a + b = 15.0
a/b = 0.5      

  下面這個函數可是有點厲害,需要重點認識一下,因為在後面的實戰中,你都會見到它:

1 input1 = tf.placeholder(tf.float32)
2 input2 = tf.placeholder(tf.float32)
3 output = tf.multiply(input1, input2)
4 with tf.Session() as sess:
5     print(sess.run([output], feed_dict={input1:[7.], input2:[2.]}))      
[array([14.], dtype=float32)]      

  Placeholder()的意思是先把這個“坑”占住,然後再往裡面填“蘿蔔”。想一想在梯度下降疊代過程中,每次都是選擇其中一部分資料來計算,其中資料的規模都是一緻的。例如,[64,10]表示每次疊代都是選擇64個樣本資料,每個資料都有10個特征,是以在疊代時可以先指定好資料的規模,也就是把“坑”按照所需大小挖好,接下來填入大小正好的“蘿蔔”即可。

  這裡簡單地說明,指定“坑”的資料類型是float32格式,接下來在session()中執行output操作,通過feed_dict={}來實際填充input1和input2的取值。

18.1.3TensorFlow實作回歸任務

   下面通過一個小例子說明TensorFlow處理回歸任務的基本流程(其實分類也是同理),簡單起見,自定義一份資料集:

1 import numpy as np
 2 import tensorflow as tf
 3 import matplotlib.pyplot as plt
 4 
 5 # 随機生成1000個點,圍繞在y=0.1x+0.3的直線周圍
 6 num_points = 1000
 7 vectors_set = []
 8 for i in range(num_points):
 9     x1 = np.random.normal(0.0, 0.55)
10     y1 = x1 * 0.1 + 0.3 + np.random.normal(0.0, 0.03)
11     vectors_set.append([x1, y1])
12 
13 # 生成一些樣本
14 x_data = [v[0] for v in vectors_set]
15 y_data = [v[1] for v in vectors_set]
16 
17 plt.scatter(x_data,y_data,c='orange',s=5)
18 plt.show()      

  上述代碼選擇了1000個樣本資料點,在建立的時候圍繞y1=x1×0.1+0.3這條直線,并在其周圍加上随機抖動,也就是實驗資料集。

  接下來要做的就是建構一個回歸方程來拟合資料樣本,首先假設不知道哪條直線能夠最好地拟合資料,需要計算出w和b。

  在定義模型結構之前,先考慮第一個問題,x作為輸入資料是幾維的?如果隻看上圖,可能很多讀者會認為該資料集是二維的,但此時關注的僅僅是資料x,y表示标簽而非資料,是以模型輸入的資料是一維的,這點非常重要,因為要根據資料的次元來設計權重參數。

1 # 生成1維的W矩陣,取值是[-1,1]之間的随機數
2 W = tf.Variable(tf.random_uniform([1], -1.0, 1.0), name='W')
3 # 生成1維的b矩陣,初始值是0
4 b = tf.Variable(tf.zeros([1]), name='b')      

  首先要從最終求解的目标下手,回歸任務就是要求出其中的權重參數w和偏置參數b。既然資料是一維的,權重參數w必然也是一維的,它們需要一一對應起來。先對其進行初始化操作,tf.random_uniform([1],−1.0,1.0)表示随機初始化一個數,這裡的[1]表示矩陣的次元,如果要建立一個3行4列的矩陣參數就是[3,4]。−1.0和1.0分别表示随機數值的取值範圍,這樣就完成權重參數w的初始化工作。偏置參數b的初始化方法類似,但是通常認為偏置對結果的影響較低,以常數0進行初始化即可。

  關于偏置參數b的次元,隻需看結果的次元即可,此例中最後需要得到一個回歸值,是以b就是一維的,如果要做三分類任務,就需要3個偏置參數。

1 # 經過計算得出預估值y
2 y = W * x_data + b
3 
4 # 以預估值y和實際值y_data之間的均方誤差作為損失
5 loss = tf.reduce_mean(tf.square(y - y_data), name='loss')      

  模型參數确定之後,就能得到其估計值。此外,還需要用損失函數評估目前預測效果,tf.square(y – y_data)表示損失函數計算方法,它與最小二乘法類似,tf.reduce_mean表示對所選樣本取平均來計算損失。

  損失函數的定義并沒有限制,需要根據實際任務選擇,其實最終要讓神經網絡做什麼,完全由損失函數給出的目标決定。

1 # 采用梯度下降法來優化參數
2 optimizer = tf.train.GradientDescentOptimizer(0.5)      

  目标函數确定之後,接下來就要進行優化,選擇梯度下降優化器—tf.train.GradientDescentOptimizer(0.5),這裡傳入的0.5表示學習率。在TensorFlow中,優化方法不隻有梯度下降優化器,還有Adam可以自适應調整學習率等政策,需要根據不同任務需求進行選擇。

1 # 訓練的過程就是最小化這個誤內插補點
2 train = optimizer.minimize(loss, name='train')      

  接下來要做的就是讓優化器朝着損失最小的目标去疊代更新參數,到這裡就完成了回歸任務中所有要執行的操作。

1 sess = tf.Session()
 2 
 3 init = tf.global_variables_initializer()
 4 sess.run(init)
 5 
 6 # 初始化的W和b是多少
 7 print ("W =", sess.run(W), "b =", sess.run(b), "loss =", sess.run(loss))
 8 # 執行20次訓練
 9 for step in range(20):
10     sess.run(train)
11     # 輸出訓練好的W和b
12     print ("W =", sess.run(W), "b =", sess.run(b), "loss =", sess.run(loss))
13 #writer = tf.train.SummaryWriter("./tmp", sess.graph)      

  疊代優化的邏輯寫好之後,還需在Session()中執行,由于這項任務比較簡單,執行20次疊代更新就可以,此過程中也可以列印想要觀察的名額,例如,每一次疊代都會列印目前的權重參數w,偏置參數b以及目前的損失值,結果如下:

W = [0.39805293] b = [0.] loss = 0.1126781
W = [0.31779358] b = [0.2912693] loss = 0.015368518
W = [0.25217777] b = [0.29389206] loss = 0.008035661
W = [0.20622969] b = [0.2960363] loss = 0.004437748
W = [0.17404404] b = [0.2975378] loss = 0.0026723628
W = [0.15149872] b = [0.2985896] loss = 0.0018061436
W = [0.13570622] b = [0.29932633] loss = 0.0013811161
W = [0.12464393] b = [0.29984242] loss = 0.0011725683
W = [0.11689504] b = [0.30020392] loss = 0.0010702405
W = [0.11146712] b = [0.30045715] loss = 0.0010200314
W = [0.10766497] b = [0.30063453] loss = 0.0009953954
W = [0.10500166] b = [0.30075878] loss = 0.0009833071
W = [0.10313606] b = [0.3008458] loss = 0.0009773759
W = [0.10182926] b = [0.30090678] loss = 0.00097446557
W = [0.10091387] b = [0.30094948] loss = 0.0009730375
W = [0.10027266] b = [0.30097938] loss = 0.0009723369
W = [0.09982351] b = [0.30100033] loss = 0.0009719931
W = [0.09950889] b = [0.30101502] loss = 0.0009718244
W = [0.0992885] b = [0.3010253] loss = 0.00097174157
W = [0.09913412] b = [0.3010325] loss = 0.00097170105
W = [0.09902599] b = [0.30103755] loss = 0.000971681      

  最開始w是随機指派的,b直接用0當作初始化,相對而言,損失值也較高。随着疊代的進行,參數開始發生變換,w越來越接近于0.1,b越來越接近于0.3,損失值也在逐漸降低。建立資料集時就是在y1=x1×0.1 + 0.3附近選擇資料點,最終求解出的結果也是非常類似,這就完成了最基本的回歸任務。

1 plt.scatter(x_data,y_data,c='orange',s=5)
2 plt.plot(x_data,sess.run(W)*x_data+sess.run(b))
3 plt.show()      

18.2搭建神經網絡進行手寫字型識别

   下面向大家介紹經典的手寫字型識别資料集—Mnist資料集,如圖18-4所示。資料集中包括0~9十個數字,我們要做的就是對圖像進行分類,讓神經網絡能夠區分這些手寫字型。

  圖18-4 Mnist資料集

  選擇這份資料集的原因是其規模較小(28×28×1),用筆記本電腦也能執行它,非常适合學習。通常情況下,資料大小(對圖像資料來說,主要是長、寬、大、小)決定模型訓練的時間,對于較大的資料集(例如224×224×3),即便網絡模型簡化,還是非常慢。對于沒有GPU的初學者來說,在圖像處理任務中,Mnist資料集就是主要練習對象。

1 import numpy as np
 2 import tensorflow as tf
 3 import matplotlib.pyplot as plt
 4 from tensorflow.examples.tutorials.mnist import input_data
 5 #tf.__path__
 6 # import sys
 7 # print (sys.path)
 8 
 9 print ("tensorflow,今天開張")
10 # ModuleNotFoundError: No module named 'tensorflow.examples.tutorials'      

  如果遇到報:# ModuleNotFoundError: No module named 'tensorflow.examples.tutorials'

       原因:很可能是預設的examples庫沒有下載下傳或路徑不對。

       解決方案:

             1、通過自帶API檔案input_data.py下載下傳,方法如下:

1 import numpy as np
2 import tensorflow as tf
3 import matplotlib.pyplot as plt
4 from tensorflow.examples.tutorials.mnist import input_data
5 
6 print ("下載下傳中......")
7 #指定目前相對路徑下載下傳examples檔案
8 mnist = input_data.read_data_sets('data/', one_hot=True)      

    如果極其沒有耐心,可手動下載下傳資料檔案:可以直接到http://yann.lecun.com/exdb/mnist/下載下傳4個檔案到python對應的路徑。

   如D:\tools\Python37\Lib\site-packages\tensorflow_core\examples\tutorials\mnist\

  • t10k-images-idx3-ubyte.gz
    t10k-labels-idx1-ubyte.gz
    train-images-idx3-ubyte.gz
    train-labels-idx1-ubyte.gz      

  

    2、下載下傳時如果找不到input_data.py檔案,這裡給出一個。

《Python資料分析與機器學習實戰-唐宇迪》讀書筆記第18章--TensorFlow實戰
《Python資料分析與機器學習實戰-唐宇迪》讀書筆記第18章--TensorFlow實戰
# Copyright 2015 Google Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
"""Functions for downloading and reading MNIST data."""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import gzip
import os
import tensorflow.python.platform
import numpy
from six.moves import urllib
from six.moves import xrange  # pylint: disable=redefined-builtin
import tensorflow as tf
SOURCE_URL = 'http://yann.lecun.com/exdb/mnist/'
def maybe_download(filename, work_directory):
  """Download the data from Yann's website, unless it's already here."""
  if not os.path.exists(work_directory):
    os.mkdir(work_directory)
  filepath = os.path.join(work_directory, filename)
  if not os.path.exists(filepath):
    filepath, _ = urllib.request.urlretrieve(SOURCE_URL + filename, filepath)
    statinfo = os.stat(filepath)
    print('Successfully downloaded', filename, statinfo.st_size, 'bytes.')
  return filepath
def _read32(bytestream):
  dt = numpy.dtype(numpy.uint32).newbyteorder('>')
  return numpy.frombuffer(bytestream.read(4), dtype=dt)[0]
def extract_images(filename):
  """Extract the images into a 4D uint8 numpy array [index, y, x, depth]."""
  print('Extracting', filename)
  with gzip.open(filename) as bytestream:
    magic = _read32(bytestream)
    if magic != 2051:
      raise ValueError(
          'Invalid magic number %d in MNIST image file: %s' %
          (magic, filename))
    num_images = _read32(bytestream)
    rows = _read32(bytestream)
    cols = _read32(bytestream)
    buf = bytestream.read(rows * cols * num_images)
    data = numpy.frombuffer(buf, dtype=numpy.uint8)
    data = data.reshape(num_images, rows, cols, 1)
    return data
def dense_to_one_hot(labels_dense, num_classes=10):
  """Convert class labels from scalars to one-hot vectors."""
  num_labels = labels_dense.shape[0]
  index_offset = numpy.arange(num_labels) * num_classes
  labels_one_hot = numpy.zeros((num_labels, num_classes))
  labels_one_hot.flat[index_offset + labels_dense.ravel()] = 1
  return labels_one_hot
def extract_labels(filename, one_hot=False):
  """Extract the labels into a 1D uint8 numpy array [index]."""
  print('Extracting', filename)
  with gzip.open(filename) as bytestream:
    magic = _read32(bytestream)
    if magic != 2049:
      raise ValueError(
          'Invalid magic number %d in MNIST label file: %s' %
          (magic, filename))
    num_items = _read32(bytestream)
    buf = bytestream.read(num_items)
    labels = numpy.frombuffer(buf, dtype=numpy.uint8)
    if one_hot:
      return dense_to_one_hot(labels)
    return labels
class DataSet(object):
  def __init__(self, images, labels, fake_data=False, one_hot=False,
               dtype=tf.float32):
    """Construct a DataSet.
    one_hot arg is used only if fake_data is true.  `dtype` can be either
    `uint8` to leave the input as `[0, 255]`, or `float32` to rescale into
    `[0, 1]`.
    """
    dtype = tf.as_dtype(dtype).base_dtype
    if dtype not in (tf.uint8, tf.float32):
      raise TypeError('Invalid image dtype %r, expected uint8 or float32' %
                      dtype)
    if fake_data:
      self._num_examples = 10000
      self.one_hot = one_hot
    else:
      assert images.shape[0] == labels.shape[0], (
          'images.shape: %s labels.shape: %s' % (images.shape,
                                                 labels.shape))
      self._num_examples = images.shape[0]
      # Convert shape from [num examples, rows, columns, depth]
      # to [num examples, rows*columns] (assuming depth == 1)
      assert images.shape[3] == 1
      images = images.reshape(images.shape[0],
                              images.shape[1] * images.shape[2])
      if dtype == tf.float32:
        # Convert from [0, 255] -> [0.0, 1.0].
        images = images.astype(numpy.float32)
        images = numpy.multiply(images, 1.0 / 255.0)
    self._images = images
    self._labels = labels
    self._epochs_completed = 0
    self._index_in_epoch = 0
  @property
  def images(self):
    return self._images
  @property
  def labels(self):
    return self._labels
  @property
  def num_examples(self):
    return self._num_examples
  @property
  def epochs_completed(self):
    return self._epochs_completed
  def next_batch(self, batch_size, fake_data=False):
    """Return the next `batch_size` examples from this data set."""
    if fake_data:
      fake_image = [1] * 784
      if self.one_hot:
        fake_label = [1] + [0] * 9
      else:
        fake_label = 0
      return [fake_image for _ in xrange(batch_size)], [
          fake_label for _ in xrange(batch_size)]
    start = self._index_in_epoch
    self._index_in_epoch += batch_size
    if self._index_in_epoch > self._num_examples:
      # Finished epoch
      self._epochs_completed += 1
      # Shuffle the data
      perm = numpy.arange(self._num_examples)
      numpy.random.shuffle(perm)
      self._images = self._images[perm]
      self._labels = self._labels[perm]
      # Start next epoch
      start = 0
      self._index_in_epoch = batch_size
      assert batch_size <= self._num_examples
    end = self._index_in_epoch
    return self._images[start:end], self._labels[start:end]
def read_data_sets(train_dir, fake_data=False, one_hot=False, dtype=tf.float32):
  class DataSets(object):
    pass
  data_sets = DataSets()
  if fake_data:
    def fake():
      return DataSet([], [], fake_data=True, one_hot=one_hot, dtype=dtype)
    data_sets.train = fake()
    data_sets.validation = fake()
    data_sets.test = fake()
    return data_sets
  TRAIN_IMAGES = 'train-images-idx3-ubyte.gz'
  TRAIN_LABELS = 'train-labels-idx1-ubyte.gz'
  TEST_IMAGES = 't10k-images-idx3-ubyte.gz'
  TEST_LABELS = 't10k-labels-idx1-ubyte.gz'
  VALIDATION_SIZE = 5000
  local_file = maybe_download(TRAIN_IMAGES, train_dir)
  train_images = extract_images(local_file)
  local_file = maybe_download(TRAIN_LABELS, train_dir)
  train_labels = extract_labels(local_file, one_hot=one_hot)
  local_file = maybe_download(TEST_IMAGES, train_dir)
  test_images = extract_images(local_file)
  local_file = maybe_download(TEST_LABELS, train_dir)
  test_labels = extract_labels(local_file, one_hot=one_hot)
  validation_images = train_images[:VALIDATION_SIZE]
  validation_labels = train_labels[:VALIDATION_SIZE]
  train_images = train_images[VALIDATION_SIZE:]
  train_labels = train_labels[VALIDATION_SIZE:]
  data_sets.train = DataSet(train_images, train_labels, dtype=dtype)
  data_sets.validation = DataSet(validation_images, validation_labels,
                                 dtype=dtype)
  data_sets.test = DataSet(test_images, test_labels, dtype=dtype)
  return data_sets

#if __name__ == '__main__':
#    path = "./data/" 
#    read_data_sets(path)
    
          

View Code

    如果是tensorflow V2+以上的版本,請到這裡官方下載下傳:

    直接下載下傳:https://raw.githubusercontent.com/tensorflow/tensorflow/master/tensorflow/examples/tutorials/mnist/input_data.py

    頁面:https://github.com/tensorflow/tensorflow/blob/master/tensorflow/examples/tutorials/mnist/input_data.py

  Mnist資料集有各種版本,最簡單的就是用TensorFlow自帶API下載下傳。

1 import numpy as np
 2 import tensorflow as tf
 3 import matplotlib.pyplot as plt
 4 from tensorflow.examples.tutorials.mnist import input_data
 5 
 6 print ("下載下傳中...")
 7 mnist = input_data.read_data_sets('data/', one_hot=True)
 8 print
 9 print (" 類型是 %s" % (type(mnist)))
10 print (" 訓練資料有 %d" % (mnist.train.num_examples))
11 print (" 測試資料有 %d" % (mnist.test.num_examples))      
Extracting data/train-images-idx3-ubyte.gz
Extracting data/train-labels-idx1-ubyte.gz
Extracting data/t10k-images-idx3-ubyte.gz
Extracting data/t10k-labels-idx1-ubyte.gz
 類型是 <class 'tensorflow.examples.tutorials.mnist.input_data.read_data_sets.<locals>.DataSets'>
 訓練資料有 55000
 測試資料有 10000      

  下載下傳速度通常稍微有點慢,完成後可以列印目前資料集中的各種資訊:

trainimg   = mnist.train.images
trainlabel = mnist.train.labels
testimg    = mnist.test.images
testlabel  = mnist.test.labels
# 28 * 28 * 1
print (" 資料類型 is %s"    % (type(trainimg)))
print (" 标簽類型 %s"  % (type(trainlabel)))
print (" 訓練集的shape %s"   % (trainimg.shape,))
print (" 訓練集的标簽的shape %s" % (trainlabel.shape,))
print (" 測試集的shape' is %s"    % (testimg.shape,))
print (" 測試集的标簽的shape %s"  % (testlabel.shape,))      
資料類型 is <class 'numpy.ndarray'>
 标簽類型 <class 'numpy.ndarray'>
 訓練集的shape (55000, 784)
 訓練集的标簽的shape (55000, 10)
 測試集的shape' is (10000, 784)
 測試集的标簽的shape (10000, 10)      

  輸出結果顯示,訓練集一共有55000個樣本,測試集有10000個樣本,數量正好夠用。每個樣本都是28×28×1,也就是784個像素點。每個資料帶有10個标簽,采用獨熱編碼,如果一張圖像是3這個數字,标簽就是 [0,0,0,1,0,0,0,0,0,0]。

  在分類任務中,大家可能覺得網絡最後的輸出應是一個具體的數值,實際上對于一個十分類任務,得到的就是其屬于每一個類别的機率值,是以輸出層要得到10個結果。

  如果想對其中的某條資料進行展示,可以将圖像繪制出來:

1 # 看看廬山真面目
 2 nsample = 5
 3 randidx = np.random.randint(trainimg.shape[0], size=nsample)
 4 
 5 # help(plt.get_cmap)
 6 for i in randidx:
 7     curr_img   = np.reshape(trainimg[i, :], (28, 28)) # 28 by 28 matrix 
 8     curr_label = np.argmax(trainlabel[i, :] ) # Label
 9     plt.matshow(curr_img, cmap=plt.get_cmap('gray'))#'matplotlib.colors.SkyBlue'
10     print ("" + str(i) + "th 訓練資料 " 
11            + "标簽是 " + str(curr_label))
12     plt.show()      
1 # Batch資料
2 print ("Batch Learning? ")
3 batch_size = 100
4 batch_xs, batch_ys = mnist.train.next_batch(batch_size)
5 print ("Batch資料 %s" % (type(batch_xs)))
6 print ("Batch标簽 %s" % (type(batch_ys)))
7 print ("Batch資料的shape %s" % (batch_xs.shape,))
8 print ("Batch标簽的shape %s" % (batch_ys.shape,))      
Batch Learning? 
Batch資料 <class 'numpy.ndarray'>
Batch标簽 <class 'numpy.ndarray'>
Batch資料的shape (100, 784)
Batch标簽的shape (100, 10)      

  接下來就要構造一個神經網絡模型來完成手寫字型識别,先來梳理一下整體任務流程(見圖18-5)。

  圖18-5 神經網絡工作流程

  通過TensorFlow加載進來的Mnist資料集已經制作成一個個batch資料,是以直接拿過來用就可以。最終的結果就是分類任務,可以得到目前輸入屬于每一個類别的機率值,需要動手完成的就是中間的網絡結構部分。

  網絡結構定義如圖18-6所示,首先定義一個簡單的隻有一層隐藏層的神經網絡,需要兩組權重參數分别連接配接輸入資料與隐藏層和隐藏層與輸出結果,其中輸入資料已經給定784個像素點(28×28×1),輸出結果也是固定的10個類别,隻需确定隐藏層神經元個數,就可以搭建網絡模型。

  圖18-6 網絡結構定義

  按照任務要求,設定一些網絡參數,包括輸入資料的規模、輸出結果規模、隐藏層神經元個數以及疊代次數與batchsize大小:

1 from tensorflow.examples.tutorials.mnist import input_data
 2 import tensorflow as tf
 3 
 4 mnist = input_data.read_data_sets('data/', one_hot=True)
 5 
 6 numClasses = 10 
 7 inputSize = 784 
 8 numHiddenUnits = 50 
 9 trainingIterations = 10000 
10 batchSize = 100       

  numClasses固定成10,表示所有資料都是用于完成這個十分類任務。隐藏層神經元個數可以自由設定,在實際操作過程中,大家也可以動手調節其大小,以觀察結果的變化,對于Mnist資料集來說,64個就足夠了。

1 X = tf.placeholder(tf.float32, shape = [None, inputSize])
2 y = tf.placeholder(tf.float32, shape = [None, numClasses])      

  既然輸入、輸出都是固定的,按照之前的講解,需要使用placeholder來先占住這個“坑”。參數shape表示資料規模,其中的None表示不限制batch的大小,一次可以疊代多個資料,inputSize已經指定成784,表示每個輸入資料大小都是一模一樣的,這也是訓練神經網絡的基本前提,輸入資料大小必須一緻。對于輸出結果Y來說也是一樣。

  接下來就是參數初始化,按照圖18-6所示網絡結構,首先,輸入資料和中間隐層之間有聯系,通過W1和B1完成計算;隐藏層又和輸出層之間有聯系,通過W2和B2完成計算。

1 W1 = tf.Variable(tf.truncated_normal([inputSize, numHiddenUnits], stddev=0.1))
2 B1 = tf.Variable(tf.constant(0.1), [numHiddenUnits])
3 W2 = tf.Variable(tf.truncated_normal([numHiddenUnits, numClasses], stddev=0.1))
4 B2 = tf.Variable(tf.constant(0.1), [numClasses])      

  這裡對權重參數使用随機高斯初始化,并且控制其值在較小範圍進行浮動,用tf.truncated_normal函數對随機結果進行限制,例如,當輸入參數為mean=0,stddev=1時,就不可能出現[−2,2]以外的點,相當于截斷标準是2倍的stddev。對于偏置參數,用常數來指派即可,注意其個數要與輸出結果一緻。

1 hiddenLayerOutput = tf.matmul(X, W1) + B1
2 hiddenLayerOutput = tf.nn.relu(hiddenLayerOutput)
3 finalOutput = tf.matmul(hiddenLayerOutput, W2) + B2      

  定義好權重參數後,從前到後進行計算即可,也就是由輸入資料經過一步步變換得到輸出結果,這裡需要注意的是,不要忘記加入激活函數,通常每一個帶有參數的網絡層後面都需要加上激活函數。

1 loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels = y, logits = finalOutput))
2 opt = tf.train.GradientDescentOptimizer(learning_rate = .1).minimize(loss)      

  接下來就是指定損失函數,再由優化器計算梯度進行更新,這回要做的是分類任務,用對數損失函數計算損失。

1 correct_prediction = tf.equal(tf.argmax(finalOutput,1), tf.argmax(y,1))
2 accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))      

  對于分類任務,隻展示損失不太直覺,還可以測試一下目前的準确率,先定義好計算方法,也就是看預測值中機率最大的位置和标簽中機率最大的位置是否一緻即可。

1 sess = tf.Session()
 2 init = tf.global_variables_initializer()
 3 sess.run(init)
 4 
 5 for i in range(trainingIterations):
 6     batch = mnist.train.next_batch(batchSize)
 7     batchInput = batch[0]
 8     batchLabels = batch[1]
 9     _, trainingLoss = sess.run([opt, loss], feed_dict={X: batchInput, y: batchLabels})
10     if i%1000 == 0:
11         trainAccuracy = accuracy.eval(session=sess, feed_dict={X: batchInput, y: batchLabels})
12         print ("step %d, training accuracy %g"%(i, trainAccuracy))      

  在Session()中實際執行疊代優化即可,指定的最大疊代次數為1萬次,如果列印出1萬個結果,那麼看起來實在太多了,可以每隔1000次列印一下目前網絡模型的效果。由于選擇batch資料的方法已經實作好,這裡可以直接調用,但是大家在用自己資料集實踐的時候,還是需要指定好batch的選擇方法。

  擷取batch資料可以在資料集中随機選擇一部分,也可以自己指定開始與結束索引,從前到後周遊資料集中每一部分。

  訓練結果如下:

step 0, training accuracy 0.12
step 1000, training accuracy 0.95
step 2000, training accuracy 0.98
step 3000, training accuracy 0.96
step 4000, training accuracy 0.98
step 5000, training accuracy 1
step 6000, training accuracy 0.97
step 7000, training accuracy 0.97
step 8000, training accuracy 0.99
step 9000, training accuracy 0.98      

  最開始随機初始化的參數,模型的準确率大概是0.12,随着網絡疊代的進行,準确率也在逐漸上升。這就完成了一個最簡單的神經網絡模型,效果看起來還不錯,那麼,還有沒有提升的餘地呢?如果做一個具有兩層隐藏層的神經網絡,效果會不會好一些呢?方法還是類似的,隻需要再疊加一層即可:

1 numHiddenUnitsLayer2 = 100
 2 trainingIterations = 10000
 3 
 4 X = tf.placeholder(tf.float32, shape = [None, inputSize])
 5 y = tf.placeholder(tf.float32, shape = [None, numClasses])
 6 
 7 W1 = tf.Variable(tf.random_normal([inputSize, numHiddenUnits], stddev=0.1))
 8 B1 = tf.Variable(tf.constant(0.1), [numHiddenUnits])
 9 W2 = tf.Variable(tf.random_normal([numHiddenUnits, numHiddenUnitsLayer2], stddev=0.1))
10 B2 = tf.Variable(tf.constant(0.1), [numHiddenUnitsLayer2])
11 W3 = tf.Variable(tf.random_normal([numHiddenUnitsLayer2, numClasses], stddev=0.1))
12 B3 = tf.Variable(tf.constant(0.1), [numClasses])
13 
14 hiddenLayerOutput = tf.matmul(X, W1) + B1
15 hiddenLayerOutput = tf.nn.relu(hiddenLayerOutput)
16 hiddenLayer2Output = tf.matmul(hiddenLayerOutput, W2) + B2
17 hiddenLayer2Output = tf.nn.relu(hiddenLayer2Output)
18 finalOutput = tf.matmul(hiddenLayer2Output, W3) + B3
19 
20 loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels = y, logits = finalOutput))
21 opt = tf.train.GradientDescentOptimizer(learning_rate = .1).minimize(loss)
22 
23 correct_prediction = tf.equal(tf.argmax(finalOutput,1), tf.argmax(y,1))
24 accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))
25 
26 sess = tf.Session()
27 init = tf.global_variables_initializer()
28 sess.run(init)
29 
30 for i in range(trainingIterations):
31     batch = mnist.train.next_batch(batchSize)
32     batchInput = batch[0]
33     batchLabels = batch[1]
34     _, trainingLoss = sess.run([opt, loss], feed_dict={X: batchInput, y: batchLabels})
35     if i%1000 == 0:
36         train_accuracy = accuracy.eval(session=sess, feed_dict={X: batchInput, y: batchLabels})
37         print ("step %d, training accuracy %g"%(i, train_accuracy))
38 
39 testInputs = mnist.test.images
40 testLabels = mnist.test.labels
41 acc = accuracy.eval(session=sess, feed_dict = {X: testInputs, y: testLabels})
42 print("testing accuracy: {}".format(acc))      

  上述代碼設定第二個隐藏層神經元的個數為100,模組化方法相同,隻是流程上多走一層,訓練結果如下:

step 0, training accuracy 0.25
step 1000, training accuracy 0.95
step 2000, training accuracy 0.99
step 3000, training accuracy 1
step 4000, training accuracy 0.99
step 5000, training accuracy 1
step 6000, training accuracy 1
step 7000, training accuracy 1
step 8000, training accuracy 1
step 9000, training accuracy 0.98
testing accuracy: 0.9747999906539917      

  可以看出,僅僅多了一層網絡結構,效果提升還是很大,之前需要5000次才能達到90%以上的準确率,現在不到1000次就能完成。是以,适當增大網絡的深度還是非常有必要的。本例無須增大,邀月注。

本章小結:本章選擇TensorFlow架構來搭建神經網絡模型,初次使用可能會覺得有一些麻煩,但習慣了就會覺得每一步流程都很規範。無論什麼任務,核心都在于選擇合适的目标函數與輸入格式,網絡模型和疊代優化通常都是差不多的。大家在學習過程中,還可以選擇Cifar資料集來嘗試分類任務,同樣都是小規模(32×32×3)資料,非常适合練手(見圖18-7)。

第18章完。

該書資源下載下傳,請至異步社群:https://www.epubit.com

邀月注:本文版權由邀月和部落格園共同所有,轉載請注明出處。

助人等于自助!  [email protected]