TensorFlow的使用1

1 TensorFlow 計算模型一一計算圖node

1.1 計算圖的概念python

  • TensorFlow 是一個經過計算圖的形式來表述計算的編程系統 。計算圖是 TensorFlow 中最基本的一個概念, TensorFlow 中的全部計算都會被轉化爲計算圖上的節點 ,而節點之間的邊描述了計算之間的依賴關係。TensorFlow 的基本計算模型以下圖所示:git

    • 圖 3 -1 中的每個節點都是一個運算,而每一條邊表明了計算之間的依賴關係 。若是一個運算的輸入依賴於另 一個運算的輸出,那麼這兩個運算有依賴關係。
    • 每個節點表明着一個操做(operation,Op),通常用來表示施加的的數學運算,也能夠表示數據輸入的起點以及輸出的終點,或者是讀取/寫入持久變量(persistent veriable)的終點,Op也可用於狀態初始化的任務.
  • TensorFlow 的名字中己經說明了它最重要的兩個概念一一Tensor 和 Flow 。 Tensor 就是張量。 Flow 翻譯成中文就是「流」 。
    • 在 TensorFlow 中,張量能夠被簡單地理解爲多維數組。
    • Flow直觀地表達了張量之間經過計算相互轉化的過程 。

1.2 計算圖的使用github

  • TensorFlow 程序通常能夠分爲兩個階段。在第一個階段須要定義計算圖中全部的計算 。 第二個階段爲執行計算 。
  • 在 TensorFlow 程序中,系統會自動維護一個默認的計算圖,經過 tf.get_default_graph 函數能夠獲取當前默認的計算圖。 若是想查看一個張量a所屬的計算圖,可使用a.graph。
  • 除了使用默認的計算圖, TensorFlow 支持經過tf.Graph函數來生成新的計算圖。不一樣計算圖上的張量和運算都不會共享。
  • TensorFlow 中的計算圖不只僅能夠用來隔離張量和計算,它還提供了管理張量和計算的機制。計算圖能夠經過 tf.Graph.device 函數來指定運行計算的設備。這爲 TensorFlow 使用 GPU 提供了機制。 若是要指定計算運行的設備,以下所示:
    a = tf.Graph()
    # 指定計算運行的設備
    with g.device('/gpu:0'):
    result = a + b
  • 有效地整理 TensorFlow 程序中的資源也是計算圖的一個重要功能。在一個計算圖中,能夠經過集合 (collection )來管理不一樣類別的資源。
    • 經過 tf.add_to_collection 函數能夠將資源加入一個或多個集合中。
    • 經過 tf.get_collection 能夠獲取一個集合中的全部資源。
    • 這裏的資源能夠是張量、變量或者運行TensorFlow程序所需的隊列資源。
    • 爲了方便使用, TenorsFlow 自動管理了一些也最經常使用的集合以下表總結了最經常使用的幾個 自動維護的集合。
      集合名稱 集合內容 使用場景
      tf.GraphKeys.VARIABLES 全部變量 持久化 TensorFlow 模型
      tf.GraphKeys.TRAINABLE_VARIABLES 可學習的變量(通常指神經網絡中的參數) 模型訓練、生成模型可視化內容
      tf.GraphKeys.SUMMARIES 日誌生成相關的張量 TensorFlow計算可視化
      tf.GraphKeys.QUEUE_RUNNERS 處理輸入的 QueueRunner 輸入處理
      tf.GraphKeys.MOVING_AVERAGE_VARIABLES 全部計算了滑動平均值的變量 計算變量的滑動平均值

2 TensorFlow 數據模型一一張量正則表達式

2.1 張量的概念算法

  • 在 TensorFlow程序中,全部的數據都經過張量的形式來表示。從功能的角度上看,張量能夠簡單的理解爲多維數組。其中:
    • 零階張量表示標量( scalar ),也就是一個數;
    • 第一階張量爲向量 (vector),也就是一個一維數組;
    • 第 n 階張量能夠理解爲一個 n 維數組 。
  • 張量在TensorFlow中的實現並非直接採用數組的形式,它只是對TensorFlow中運算結果的引用。在張量中並無真正保存數字,它保存的是如何獲得這些數字的計算過程。
    • 以向量的加法爲例,當運行以下代碼時,並不會獲得加法的結果,而會獲得對結果的一個引用。
      import tensorflow as tf
      # tf.constant 是一個計算,這個計算的結果爲一個張量 , 保存在變量 a 中。
      a= tf .constant([l . O, 2 . 0] , name=」 a」)
      b = tf . constant ( [2 . 0 , 3 . 0] , name=」b 」)
      result = tf . add (a , b , name=」 add」)
      print(result)
      """
      輸出 :
      Tensor ( 」 add :0」, shape= ( 2 ,), dtype=float32)
      """
    • 從以上代碼能夠看出 TensorFlow 中的張量和 NumPy 中 的數組不一樣, TensorFlow 計算的結果不是一個具體的數字 , 而是一個張量的結構。
    • 從上面代碼的運行結果能夠看出, 一個張量中主要保存了三個屬性 : 名字( name )、維度( shape )和類型( type )。
      • 張量的第一個屬性name不只是一個張量的惟一標識符 , 它一樣也給出了這個張量是如何計算出來的。 由於TensorFlow 的計算均可以經過計算圖 的模型來創建,而計算圖上的每個節點表明了 一個計算,計算的結果就保存在張量之中。因此張量和計算圖上節點所表明的計算結果是對應的。 這樣張量的命名就能夠經過 「 node:src_output」的形式來給出。其中node爲節點的名稱,src一output 表示當前張量來自節點的第幾個輸出。
      • 張量的第二個屬性是張量的維度(shape)。這個屬性描述了 一個張量的維度信息。好比上面樣例中 shape=(2,)說明了張量 result 是一個一維數組 , 這個數組的長度爲 2。
      • 張量的第三個屬性是類型(type ) ,每個張量會有一個惟一的類型。TensorFlow 會對參與運算的全部張量進行類型的檢查,當發現類型不匹配時會報錯。
        • 若是不指定類型, TensorFlow 會給出默認的類型,好比不帶小數點的數會被默認爲 int32,帶小數點的會默認爲 float32 。
        • 由於使用默認類型有可能會致使潛在的類型不匹配問題,因此通常建議經過指定 dtype 來明確指出變量或者常量的類型 。
        • TensorFlow 支持 14 種不一樣的類型, 主要包括了實數( tf.float32 、 tf.float64 )、整數( tf.int8 、 tf.intl 6 、 tf.int32 、 tf.int64 、 tf.uint8 )、布爾型(tf.bool) 和複數( tf.complex64 、tf.complex128 )。

2.2 張量的使用編程

  • 張量使用主要能夠總結爲兩大類 :
    • 第一類用途是對中間計算結果的引用。當一個計算包含不少中間結果時,使用張量能夠大大提升代碼的可讀性。
      • 當計算的複雜度增長時(好比在構建深層神經網絡時〉經過張量來引用計算的中間結果可使代碼的可閱讀性大大提高 。
      • 同時,經過張量來存儲中間結果能夠方便獲取中間結果 。 好比在卷積神經網絡中,卷積層或者池化層有可能改變張量的維度,經過 result.get_shape 函數來獲取結果張量的維度信息能夠免去人工計算的麻煩。
    • 第二類用途是當計算圖構造完成以後,張量能夠用來得到計算結果,也就是獲得真實的數字。 雖然張量自己沒有存儲具體的數字 ,可是經過會話,就能夠獲得具體的數字。好比可使用tf.Session().run()語句獲得計算結果。

3 TensorFlow 運行模型一一會話後端

  • TensorFlow中的會話(session)用來執行定義好的運算。會話擁有並管理TensorFlow程序運行時全部的資源。
  • 在會話中,全部計算完成以後須要關閉會話來幫助系統回收資源,不然就可能出現資源泄露的問題。
  • TensorFlow 中使用會話的模式通常有兩種:
    • 第一種模式須要明確調用會話生成函數和關閉會話函數,這種模式的代碼流程以下。
      # 建立一個會話。
      sess = tf.Session()
      # 使用這個建立好的會話來獲得關心的運算的結果。好比能夠調用 sess. run (result) ,
      # 來獲得張量 result 的取值。
      sess.run (... )
      # 關閉會話使得本次運行巾使用到的資源能夠被釋放。
      sess.close ()
      • 使用這種模式時,在全部計算完成以後,須要明確調用 Session.close 函數來關閉會話並釋放資源。 然而,當程序由於異常而退出時,關閉會話的函數可能就不會被執行從而致使資源泄漏 。
    • 爲了解決異常退出時資源釋放的問題, TensorFlow 能夠經過 Python 的上下文管理器來使用會話。如下代碼展現瞭如何使用這種模式。
      # 建立一個會話,並經過 Python 中的上下文管理器來管理這個會話。
      with tf. Session() as sess :
      #使用建立好的會話來計算關心的結果。
      sess.run()
      # 不須要再調用「 Session.close()」函數來關閉會話,
      # 當上下文退出時會話關閉和資源釋放也自動完成了。
      • 經過 Python 上下文管理器的機制,只要將全部的計算放在 「 with」的內部就能夠 。 當上下文管理器退出時候會自動釋放全部資源。這樣既解決了由於異常退出時資源釋放的問題,同時也解決了忘記調用 Session.close 函數而產生的資源泄漏。
  • TensorFlow 不會自動生成默認的會話,而是須要手動指定 。 當默認的會話被指定以後能夠經過 tf.Tensor.eval 函數來計算一個張量的取值 。 實例代碼以下:
    # 建立一個會話
    sess = tf.Session()
    # 手動指定會話爲默認會話
    with sess.as_default():
    print(result.eval())
  • TensorFlow 提供了 一種在交互式環境下直接構建默認會話的函數。這個函數就是 tf.lnteractiveSession。使用這個函數會自動將生成的會話註冊爲默認會話。 實列以下:
    # 在交互式環境下直接構建默認會話
    sess = tf.InteractiveSession()
    print(result.eval())
    # 關閉會話
    sess.close()
  • 經過 tf.InteractiveSession 函數能夠省去將產生的會話註冊爲默認會話的過程。不管使用哪一種方法均可以經過 ConfigProto來配置須要生成的會話 。下面給出了經過ConfigProto配置會話的方法 :
    # 定義配置
    config = tf.ConfigProto(allow_soft_placement=True,
    log_device_placement=True)
    # 在交互式環境下直接構建默認會話,配置生成的會話
    sessl = tf.InteractiveSession(config=config)
    sess2 = tf.Session(config=config)
    • 經過 ConfigProto 能夠配置相似並行的線程數、 GPU 分配策略、運算超時時間等參數。在這些參數中,最常使用的有兩個 :
      • 第一個是 allow_soft_placement,這是一個布爾型的參數,當它爲 True 時, 在如下任意一個條件成立時, GPU 上的運算能夠放到 CPU 上進行 :
        • 運算沒法在 GPU 上執行 。
        • 沒有 GPU 資源(好比運算被指定在第 二個 GPU 上運行 ,可是機器只有一個 GPU ) 。
        • 運算輸入包含對 CPU 計算結果的引用 。
      • 這個參數的默認值爲 False,可是爲了使得代碼的可移植性更強,在有 GPU 的環境下
        這個參數通常會被設置爲 True。 經過將 allow_soft_placement 參數設爲 True , 當某些運算沒法被當前 GPU 支持時,可 以自動調整到 CPU 上,而不是報錯。相似地,經過將這個參數設置爲 True,可讓程序在擁有不一樣數量的 GPU 機器上順利運行。
      • 第二個使用得比較多的配置參數是 log_device_placement 這也是一個布爾型的參數, 當它爲 True 時日誌中將會記錄每一個節點被安排在哪一個設備上以方便調試。而在生產環境中將這個參數設置爲 False 能夠減小日誌量。

4 TensorFlow變量api

4.1 TensorFlow中變量的概念數組

  • 變量維護圖執行過程當中的狀態信息。變量經過tf.Variable 類以及tf.get_variable()函數進行操做。變量的特色有:
    • 存儲持久化
    • 可修改值
    • 可指定被訓練

4.2 建立和使用變量

  • tf.Variable(initial_value=None,trainable=True,collections=None,name=None)
    • initial_value:初始化的值
    • trainable:是否被訓練
    • collections:新變量將添加到列出的圖的集合中
  • 在 TensorFlow 中,全部的變量都會被自動地加入到 GraphKeys .VARIABLES這個集合中。
  • 經過tf.global_variables()函數能夠拿到當前計算圖上全部的變量。拿到計算圖上全部的變量有助於持久化整個計算圖的運行狀態 。
  • 當構建機器學習模型時,好比神經網絡,能夠經過變量聲明函數中的 trainable參數來區分須要優化的參數(好比神經網絡中的參數)和其餘參數(好比選代的輪數)。若是聲明變量時參數 trainable 爲 True ,那麼這個變量將會被加入到GraphKeys.TRAINABLE_VARiABLES 集合。
  • 在TensorFlow中能夠經過tf.trainable_variables函數獲得全部須要優化的參數 。 TensorF!ow 中提供的神經網絡優化算法會將GraphKeys.TRAINABLE_VARIABLES集合中的變量做爲默認的優化對象 。
  • 在 TensorFlow 中,類 (tf.Variable )的做用就是保存和更新神經網絡中的參數 。和其餘編程語言相似,TensorFlow中的變量也須要指定初始值 。 由於在神經網絡中, 給參數賦予隨機初始值最爲 常見,因此通常也使用隨機數給 TensorFlow 中的變量初始化 。
    • 下面一段代碼給出了 一種在 TensorFlow中聲明一個2X3的矩陣變量的方法 :
      weights = tf.Variable(tf.random_normal([2,3],mean=0,stddev=2))
    • tf.random_normal([2, 3],mean=0, stddev=2)會產生一個2X3的矩陣,矩陣中的元素是均值爲0,標準差爲2的隨機數。
  • 除了使用隨機數或者常數,TensorFlow 也支持經過其餘變量 的初始值來初始化新的變量。 如下代碼給出了具體的方法 。
    w2 = tf.Variable(weights.initialized_value())
    w3 = tf.Variable(weights.initialized_value()*2.0)

    • 以上代碼中, w2 的初始值被設置成了與 weights 變量相同。 w3 的初始值則是 weights初始值的兩倍。
  • 在 TensorFlow 中,一個變量的值在被使用以前,這個變量的初始化過程須要被明確地調用 ,即變量在使用以前須要初始化。 TensorFlow 提供了一種便捷的方式來完成變量初始化過程。如下程序展現了經過 tf.global_variables_initializer 函數實現初始化全部變量的過程 。
    init_op = tf.global_variables_initializer()
    sess.run(init_op)

    • 經過tf.global_variables_initializer 函數,就不須要將變量一個一個初始化了 。 這個函數也會自動處理變量之間的依賴關係。

4.3 TensorFlow目前支持的全部隨機數生成器

函數名稱                            隨機數分佈                                 主要參數

tf.random_normal 正太分佈 平均值、標準差、取值類型·
tf.truncated_normal 正太分佈,但若是隨機出來的值偏離平均值超過 2個標準差,那麼這個數將會被從新隨機 平均值、標準差、取值類型
tf.random.uniform 均勻分佈 最小、最大取值、取值類型
tf.random_gamma Gamma 分佈 形狀參數 alpha、尺度參數 beta 、 取值類型

4.4 TensorFlow中經常使用的常量聲明方法

函數名稱             功能                           樣例

tf.zeros 產生全0的數組 tf.zeros([2, 3), int32) > ([0, 0, 0], [0, 0, 0]]
tf.ones 產生全1的數組 tf.ones([2, 3], int32) -> [[1,1,1],[1,1,1]]
tf.fill 產生一個所有爲給定數字的數組 tf.fill([2, 3), 9) ->[ [9, 9, 9],[9, 9, 9]]
tf.constant 產生一個給定值的常量 tf.constant([1,2,3])—>[1,2,3]

4.5 案例:使用變量實現一個簡單的計數器.

# 建立一個變量爲計數器, 初始化爲標量 0.
state = tf.Variable(0, name="counter")
# 建立一個變量, 其做用是使state增長1
one = tf.constant(1)
new_value = tf.add(state, one)
# 更新計數器
update = tf.assign(state, new_value)

# 啓動圖後, 變量必須先通過變量初始化,
init_op = tf.global_variables_initializer()
# 啓動圖, 運行會話
with tf.Session() as sess:
  # 運行變量初始化
  sess.run(init_op)
  # 打印 'state' 的初始值
  print sess.run(state)
  # 運行更新 'state', 並打印 'state'
  for _ in range(3):
    sess.run(update)
    print sess.run(state)
  • 經常使用的變量操做
    • tf.assign(ref, value, validate_shape=None, use_locking=None, name=None)
      • 將value 賦值給ref,並輸出 ref
    • tf.assign_add(ref,value,use_locking=None,name=None)
      • 更新ref的值,經過增長value

4.6 命名空間與共享變量

  • 共享變量的主要用途在一些網絡當中的參數共享, 因爲在TensorFlow當中,只要咱們定義的OP,name參數指定同樣,其實並非同一個變量。若是想要達到重複利用變量的效果,咱們就要使用tf.variable_scope()結合tf.get_variable()一塊兒使用.
  • 定義一個相同名字的變量

    var = tf.Variable(name='var', initial_value=[4], dtype=tf.float32)
    var_double = tf.Variable(name='var', initial_value=[4], dtype=tf.float32)

    <tf.Variable 'var:0' shape=() dtype=float32_ref>
    <tf.Variable 'var_1:0' shape=() dtype=float32_ref>

  • 使用tf.variable_scope()修改OP命名空間

    with tf.variable_scope("name"):
    var = tf.Variable(name='var', initial_value=[4], dtype=tf.float32)
    var_double = tf.Variable(name='var', initial_value=[4], dtype=tf.float32)

    <tf.Variable 'name/var:0' shape=() dtype=float32_ref>
    <tf.Variable 'name/var_1:0' shape=() dtype=float32_ref>

  • tf.get_variable共享變量

經過tf.get_variable的初始化與Variable參數同樣,可是要是實現共享須要打開tf.variable_scope("name")中的reuse=tf.AUTO_REUSE參數。

# 打開共享參數
# 或者
#  with tf.variable_scope("name") as scope:
#  在須要使用共享變量的前面定義: scope.reuse_variables()
with tf.variable_scope("name", reuse=tf.AUTO_REUSE):
    var = tf.Variable(initial_value=4.0, name="var", dtype=tf.float32)
    var_double = tf.Variable(initial_value=4.0, name="var", dtype=tf.float32)

    var1 = tf.get_variable(initializer=tf.random_normal([2, 2], mean=0.0, stddev=1.0),
                           name="var1",
                           dtype=tf.float32)
    var1_double = tf.get_variable(initializer=tf.random_normal([2, 2], mean=0.0, stddev=1.0),
                           name="var1",
                           dtype=tf.float32)
    
with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    print(var1)
    print(var1_double)

5 TensorFlow模型的保存和恢復

5.1 什麼是Tensorflow模型?

  • 訓練神經網絡後,須要將它保存以備未來使用和部署到生產環境。Tensorflow模型主要包含咱們神經網絡的網絡圖和已經訓練好的變量參數。所以,Tensorflow模型主要有兩個文件:
    • 元數據圖(meta graph):
      • 它保存了tensorflow完整的網絡圖結構。這個文件以.meta爲擴展名。
    • 檢查點文件(checkpoint file):
      • *.ckpt文件:這是一個二進制文件,它包含的權重變量,biases變量和其餘變量。
      • *.data文件:包含咱們的訓練的變量的文件
      • 名爲checkpoint的文件:它不斷的保存最新的檢查點文件的記錄。

5.2 爲何要保存Tensorflow模型:

  • 好比對圖像分類訓練卷積神經網絡。做爲一種標準的作法,在訓練模型的時候,須要一直關注着模型損失值和模型準確度。一旦咱們發現,網絡已經收斂,就能夠手動中止訓練。訓練完成後,咱們但願將全部的變量和網絡模型保存下來,供之後使用。
  • 例如,假設您能夠訪問通過訓練的 DNN,將圖片分爲 100 個不一樣的類別,包括動物,植物,車輛和平常物品。 您如今想要訓練一個 DNN 來對特定類型的車輛進行分類。 這些任務很是類似,所以您應該嘗試從新使用第一個網絡的一部分(請參見圖 11-4)。

    • 若是新任務的輸入圖像與原始任務中使用的輸入圖像的大小不一致,則必須添加預處理步驟以將其大小調整爲原始模型的預期大小。

5.3 如何保存Tensorflow模型:

  • Tensorflow中要保存訓練好的網絡模型和全部的變量和網絡結構圖,使用tf.train.Saver()
  • Tensorflow變量的做用範圍是在一個session裏面。在保存模型的時候,應該在session裏面經過save方法保存。
    saver.save(sess,'路徑+名稱'):sess是session對象,第二個參數是要保存的模型的路徑與名稱
  • 若是咱們但願在迭代i次以後保存模型,能夠把當前的迭代步數傳進去:

    # 每隔一百步保存一次模型
    if i % 100 == 0:
    saver.save(sess, 'my_test_model',global_step=i)
    # 利用global_step參數能夠把迭代的步數追加到文件名中

  • 在屢次保存模型時,若保存的文件中變化的只是變量,而網絡結構沒有變化,則沒有必要重複保存.meta文件,可使用以下方式,讓網絡結構只保存一次。
    saver.save(sess,'my_model',global_step=step,write_meta_graph=False)
    • write_meta_graph 參數設置成False:可讓網絡結構只保存一次,不重複保存.meta 文件。
  • 若是隻想保存最新的m個模型,並但願每n小時保存一次,可使用
    saver = tf.train.Saver(max_to_keep=m, keep_checkpoint_every_n_hour= n)
    • max_to_keep參數:設置保留最新的模型的個數
    • keep_checkpoint_every_n_hours參數:設置間隔多少小時保存一次模型。
  • 若是咱們沒有在tf.train.Saver()指定任何參數,這樣就表示要保存全部的變量。若是咱們不但願保存全部的變量,只是其中的一部分。咱們能夠指定要保存的變量或者集合。
    • 在建立tf.train.Saver()的時候,咱們把一個列表或者要保存變量的字典做爲參數傳進去。代碼以下:
      import tensorflow as tf
      # 定義變量OP
      w1 = tf.Variable(tf.random_normal(shape=[2]),name='w1')
      w2 = tf.Variable(tf.random_normal([5]),name='w2')
      # 建立模型保存對象,指定要保存的變量
      saver = tf.train.Saver([w1,w2])
      with tf.Session() as sess:
      # 顯示初始化變量OP
      sess.run(tf.global_variables_initializer())
      ........

5.4 如何導入已經保存的模型

若是想用別人的預先訓練好的模型做爲咱們的模型的一部分,那麼須要作兩件事情:

  • 建立網絡結構圖:
    • 咱們能夠經過編寫Python代碼手動建立每一層和原始模型同樣的網絡結構圖。可是,不要作重複的工做。
    • 由於在.meta文件中,原始的網絡結構已經保存在其中,咱們只須要導入便可,無需重寫一遍。
    • 導入原始網絡結構圖的方式以下:
      saver = tf.train.import_meta_graph('模型名.meta')
  • 加載的(變量)參數
    • 使用restore()方法恢復模型的變量參數
      with tf.Session() as sess:
      new_saver = tf.train.import_meta_graph('my_test_model-1000.meta')
      new_saver.restore(sess, tf.train.latest_checkpoint('./'))

5.5 複用TensorFlow模型

  • 若是原始模型使用 TensorFlow 進行訓練,則能夠簡單地將其恢復並在新任務上進行訓練:
    [...] # 構建原始模型

    with tf.Session() as sess:
        # 還原模型
        saver.restore(sess, "./my_model_final.ckpt")
        # 在新任務中繼續訓練模型...
  • 然而,一般咱們只想要重用原有模型的一部分(咱們立刻就會討論這種情況),一種簡單的解決方案就是配置Saver使之在還原原模型時只還原全部參數的一個子集。舉例來講,下面的代碼就只還原了隱藏層一、隱藏層2和隱藏層3:
    """
    首先咱們創建新的模型,確保複製原始模型的隱藏層 1 到 3
    """
    n_inputs = 28 * 28 # MNIST
    n_hidden1 = 300 # reused
    n_hidden2 = 50 # reused
    n_hidden3 = 50 # reused
    n_hidden4 = 20 # new!
    n_outputs = 10 # new!

    X = tf.placeholder(tf.float32, shape=(None, n_inputs), name="X")
    y = tf.placeholder(tf.int64, shape=(None), name="y")
    
    with tf.name_scope("dnn"):
        hidden1 = tf.layers.dense(X, n_hidden1, activation=tf.nn.relu, name="hidden1")       # reused
        hidden2 = tf.layers.dense(hidden1, n_hidden2, activation=tf.nn.relu, name="hidden2") # reused
        hidden3 = tf.layers.dense(hidden2, n_hidden3, activation=tf.nn.relu, name="hidden3") # reused
        hidden4 = tf.layers.dense(hidden3, n_hidden4, activation=tf.nn.relu, name="hidden4") # new!
        logits = tf.layers.dense(hidden4, n_outputs, name="outputs")                         # new!
    
    with tf.name_scope("loss"):
        xentropy = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=y, logits=logits)
        loss = tf.reduce_mean(xentropy, name="loss")
    
    with tf.name_scope("eval"):
        correct = tf.nn.in_top_k(logits, y, 1)
        accuracy = tf.reduce_mean(tf.cast(correct, tf.float32), name="accuracy")
    
    with tf.name_scope("train"):
        optimizer = tf.train.GradientDescentOptimizer(learning_rate)
        training_op = optimizer.minimize(loss)
    
    # 獲取保存的模型中用trainable = True(這是默認值)建立的全部變量的列表
    reuse_vars = tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES,
                                   scope="hidden[123]") # 咱們只保留那些範圍與正則表達式hidden [123]相匹配的變量(即,咱們獲得全部可訓練的隱藏層 1 到 3 中的變量)。
    # 咱們建立一個字典,將原始模型中每一個變量的名稱映射到新模型中的名稱中(一般須要保持徹底相同的名稱)
    reuse_vars_dict = dict([(var.op.name, var) for var in reuse_vars])
    # 建立一個SaverOP用來還原模型中的參數
    restore_saver = tf.train.Saver(reuse_vars_dict) # to restore layers 1-3
    
    init = tf.global_variables_initializer()
    # 建立一個新的SaverOP用來保存新模型
    new_saver = tf.train.Saver()
    
    with tf.Session() as sess:
        init.run()
        # 從原始模型的層 1 到 3中恢復變量值
        restore_saver.restore(sess, "./my_model_final.ckpt")
    
        for epoch in range(n_epochs):                                      
            for iteration in range(mnist.train.num_examples // batch_size): 
                X_batch, y_batch = mnist.train.next_batch(batch_size)      
                sess.run(training_op, feed_dict={X: X_batch, y: y_batch})  
            accuracy_val = accuracy.eval(feed_dict={X: mnist.test.images,  
                                                    y: mnist.test.labels})
            print(epoch, "Test accuracy:", accuracy_val)                  
      # 保存新模型
        save_path = new_saver.save(sess, "./my_new_model_final.ckpt")
  • 首先咱們創建新的模型,確保複製原始模型的隱藏層 1 到 3。咱們還建立一個節點來初始化全部變量。 而後咱們獲得剛剛用trainable = True(這是默認值)建立的全部變量的列表,咱們只保留那些範圍與正則表達式hidden [123]相匹配的變量(即,咱們獲得全部可訓練的隱藏層 1 到 3 中的變量)。 接下來,咱們建立一個字典,將原始模型中每一個變量的名稱映射到新模型中的名稱(一般須要保持徹底相同的名稱)。 而後,咱們建立一個Saver,它將只恢復這些變量,而且建立另外一個Saver來保存整個新模型,而不只僅是第 1 層到第 3 層。而後,咱們開始一個會話並初始化模型中的全部變量,而後從原始模型的層 1 到 3中恢復變量值。最後,咱們在新任務上訓練模型並保存。
  • 任務越類似,您能夠重複使用的層越多(從較低層開始)。 對於很是類似的任務,您能夠嘗試保留全部隱藏的層,只替換輸出層。
  • 案例:識別手勢數據集

    """
    一天下午,咱們和一些朋友決定教咱們的電腦破譯手語。咱們花了幾個小時在白色的牆壁前拍照,因而就有了瞭如下數據集。如今,你的任務是創建一個算法,使有語音障礙的人與不懂手語的人交流。

    訓練集:有從0到5的數字的1080張圖片(64x64像素),每一個數字擁有180張圖片。
    測試集:有從0到5的數字的120張圖片(64x64像素),每一個數字擁有5張圖片。
    須要注意的是這是完整數據集的一個子集,完整的數據集包含更多的符號。

    下面是每一個數字的樣本,以及咱們如何表示標籤的解釋。這些都是原始圖片,咱們實際上用的是64 * 64像素的圖片。
    """
    import numpy as np
    import h5py
    import matplotlib.pyplot as plt
    import tensorflow as tf
    from tensorflow.python.framework import ops
    import time
    import math
    from functools import partial

    # %matplotlib inline #若是你使用的是jupyter notebook取消註釋
    np.random.seed(1)

    class GestureSymbolRecognition(object):
    def init(self):
    self.learning_rate = 0.0001
    self.num_epochs = 1000
    self.minibatch_size = 32
    self.regularazion_rate = 0.001

    self.n_inputs = 12288
          self.n_hidden1 = 200
          self.n_hidden2 = 100
          self.n_hidden3 = 100
          self.n_outputs = 6
    
          self.batch_norm_momentum = 0.9
    
      def load_data(self):
          """
          加載數據集
          :return:
          """
          # 從文件中讀取訓練集數據
          train_dataset = h5py.File('datasets/train_signs.h5', "r")
          # 從訓練集數據中提取特徵值與標籤值數據
          train_set_x_orig = np.array(train_dataset["train_set_x"][:])
          train_set_y_orig = np.array(train_dataset["train_set_y"][:])
    
          # 從文件中讀取測試集數據
          test_dataset = h5py.File('datasets/test_signs.h5', "r")
          # 從測試集數據中提取特徵值與標籤值數據
          test_set_x_orig = np.array(test_dataset["test_set_x"][:])
          test_set_y_orig = np.array(test_dataset["test_set_y"][:])
          classes = np.array(test_dataset["list_classes"][:])  # 類別列表
          classes_num = len(classes)  # 數據集標籤分爲幾類
    
          train_set_y_orig = train_set_y_orig.reshape((1, train_set_y_orig.shape[0]))
          test_set_y_orig = test_set_y_orig.reshape((1, test_set_y_orig.shape[0]))
    
          X_train_flatten = train_set_x_orig.reshape(train_set_x_orig.shape[0], -1)  # 每一列就是一個樣本
          X_test_flatten = test_set_x_orig.reshape(test_set_x_orig.shape[0], -1)
    
          # 歸一化數據
          X_train = X_train_flatten / 255
          X_test = X_test_flatten / 255
    
          # 轉換爲one-hot編碼矩陣
          Y_train = self.convert_to_one_hot(train_set_y_orig, len(classes)).T
          Y_test = self.convert_to_one_hot(test_set_y_orig, len(classes)).T
    
          return X_train, Y_train, X_test, Y_test, classes_num
    
      def convert_to_one_hot(self, Y, C):
          """
          實現one-hot編碼
          :param Y:標籤矩陣
          :param C:分類數量
          :return:one_hot:one-hot矩陣
          """
          one_hot = np.eye(C)[Y.reshape(-1)].T
          return one_hot
    
      def random_mini_batches(self, X, Y, mini_batch_size, seed=0):
          """
          從數據集中建立一個隨機的mini-batch列表
          :param X: 輸入數據,維度爲(輸入節點數量,樣本數量)
          :param Y: 對應的目標值,維度爲(1,樣本數量)
          :param mini_batch_size: 每一個mini-batch的樣本數量
          :param seed: 隨機數種子
          :return: mini_batches:一個同步列表,維度爲(mini_batch_X, mini_batch_Y)
          """
          # 指定隨機數種子
          np.random.seed(seed)
          # 獲取樣本數
          m = X.shape[0]
          # print('樣本數量',m)
          # 建立一個同步列表
          mini_batches = []
    
          # 第一步:打亂樣本的順序
          permutation = list(np.random.permutation(m))  # 返回一個長度爲m的隨機數組,且裏面的數是0到m-1
          shuffled_X = X[permutation, :]  # 將每一列特徵值數據按permutation的順序來從新排列
          shuffled_Y = Y[permutation, :]  # .reshape((Y.shape[0], m))  # 將每一列的目標值數據按permutation的順序來從新排列
    
          # 第二步:分割訓練集數據
          num_complete_minibatches = math.floor(m / mini_batch_size)  # 迭代完成整個訓練集的次數
          for k in range(0, num_complete_minibatches):
              mini_batch_X = shuffled_X[k * mini_batch_size:(k + 1) * mini_batch_size, :]
              mini_batch_Y = shuffled_Y[k * mini_batch_size:(k + 1) * mini_batch_size, :]
              mini_batch = (mini_batch_X, mini_batch_Y)
              mini_batches.append(mini_batch)
    
          # 若是訓練集的大小不是mini_batch_size的整數倍,那麼最後確定會剩下一些,取剩餘的數據
          if m % mini_batch_size != 0:
              # 獲取剩餘部分的數據
              mini_batch_X = shuffled_X[mini_batch_size * num_complete_minibatches:, :]
              mini_batch_Y = shuffled_Y[mini_batch_size * num_complete_minibatches:, :]
              mini_batch = (mini_batch_X, mini_batch_Y)
              mini_batches.append(mini_batch)
    
          # 返回同步列表
          return mini_batches
    
      def train(self, print_cost=True, is_plot=True):
          """
          實現一個三層的TensorFlow神經網絡訓練邏輯:LINEAR->RELU->LINEAR->RELU->LINEAR->SOFTMAX
          參數:
              print_cost - 是否打印成本,每100代打印一次
              is_plot - 是否繪製曲線圖
    
          """
          # 加載數據集
          x_train, y_train, x_test, y_test, label_classes_num = self.load_data()
          print("訓練集樣本數 = " + str(x_train.shape[0]))
          print("測試集樣本數 = " + str(x_test.shape[0]))
          print("X_train.shape: " + str(x_train.shape))
          print("Y_train.shape: " + str(y_train.shape))
          print("X_test.shape: " + str(x_test.shape))
          print("Y_test.shape: " + str(y_test.shape))
          # 設置圖級別的隨機數種子
          tf.set_random_seed(1)
          # 設置操做級別的隨機數種子
          seed = 3
          # 獲取輸入節點數量和樣本數
          (m, n_x) = x_train.shape
          # 成本函數值列表
          costs = []
    
          # 給X和Y建立佔位符節點
          X = tf.placeholder(tf.float32, shape=(None, self.n_inputs), name="X")
          Y = tf.placeholder(tf.int32, shape=(None, label_classes_num), name="y")
    
          # 定義神經網絡全鏈接層
          with tf.name_scope("dnn"):
    
              # 使用He權重初始化方法初始化權重
              he_init = tf.contrib.layers.variance_scaling_initializer(mode='FAN_AVG')
              # 定義全鏈接層,使用elu激活函數
              hidden1 = tf.layers.dense(X, self.n_hidden1, name="hidden1",
                                        activation=tf.nn.elu, kernel_initializer=he_init)
              hidden2 = tf.layers.dense(hidden1, self.n_hidden2, name="hidden2",
                                        activation=tf.nn.elu, kernel_initializer=he_init)
              hidden3 = tf.layers.dense(hidden2, self.n_hidden3, name="hidden3",
                                        activation=tf.nn.elu, kernel_initializer=he_init)
              outputs = tf.layers.dense(hidden3, self.n_outputs, name="outputs")
              # 輸出預測的類別
              y_proba = tf.nn.softmax(outputs)
    
          # 定義損失函數計算損失
          with tf.name_scope('loss'):
              # 前向傳播要在Z3處中止,由於在TensorFlow中最後的線性輸出層的輸出做爲計算損失函數的輸入,因此不須要A3.
              xentropy = tf.nn.softmax_cross_entropy_with_logits(labels=Y,
                                                                 logits=outputs)
              loss = tf.reduce_mean(xentropy, name="loss")
    
          # 定義優化器
          with tf.name_scope("train_optimizer"):
              optimizer = tf.train.AdamOptimizer(self.learning_rate)
              training_op = optimizer.minimize(loss)
    
          # 評估模型,使用準確性做爲咱們的績效指標
          with tf.name_scope("eval"):
              # 計算當前的預測結果
              # 檢測使用了滑動平靜模型的神經網絡前向傳播是否正確。tf.argmax(average_y,1)
              # 計算每個樣例的預測答案。其中average_y是一個batch_size*10的二維數組,每
              # 一行表示案例向前傳播的結果。tf.argmax的第二個參數爲1,表示選取最大值的
              # 操做只在第一個維度上進行(x軸上),也就是說只在每一行選取最大值對應的下標
              # 因而獲得的結果是一個長度爲batch的一維數組,這個一維數組中的值就表示了每
              # 一個數字對應的樣例識別的結果.tf.equal()判斷每一個Tensor的每一維度是否相同
              # 若是相等返回True,不然返回False.
              correct_prediction = tf.equal(tf.argmax(y_proba, 1), tf.argmax(Y, 1))
    
              # 計算準確率
              # 這個運算首先將一個布爾型的值轉換爲實數型,而後計算平均值。這一個平均值
              # 就表明模型在這一組數據上的正確率
              accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
    
          """
          首先咱們創建新的模型,確保複製原始模型的隱藏層 1 到 2
          """
          # 獲取保存的模型中用trainable = True(這是默認值)建立的全部變量的列表
          # 咱們只保留那些範圍與正則表達式hidden [123]相匹配的變量(即,咱們獲得全部可訓練的隱藏層 1 到 3 中的變量)。
          reuse_vars = tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES,
                                         scope='hidden[12]')
          # 咱們建立一個字典,將原始模型中每一個變量的名稱映射到新模型中的名稱中(一般須要保持徹底相同的名稱)
          reuse_vars_dict = dict([(var.op.name, var) for var in reuse_vars])
          # 建立一個SaverOP用來還原模型中的參數
          restore_saver = tf.train.Saver(reuse_vars_dict)
    
          # 建立初始化全部變量的節點
          init = tf.global_variables_initializer()
    
          # 建立一個新的SaverOP用來保存新模型
          new_saver = tf.train.Saver(max_to_keep=1)
    
          # 開始會話並計算
          with tf.Session() as sess:
              # 初始化全部變量
              sess.run(init)
              # 加載模型,從模型中找出與當前訓練的模型代碼當中(名字同樣的OP操做),覆蓋原來的值
              checkpoin = tf.train.latest_checkpoint('./ckpt/')
              # 若是模型已經保存則加載模型
              if checkpoin:
                  # 從原始模型恢復變量值
                  restore_saver.restore(sess, './ckpt/my_test1_model.ckpt')
    
              # 正常訓練模型
              for epoch in range(self.num_epochs):
    
                  epoch_cost = 0  # 每輪的成本函數值
                  num_minibatches = math.floor(m / self.minibatch_size)  # 分紅多少個mini-batch組
                  seed = seed + 1
                  minibatches = self.random_mini_batches(x_train, y_train, self.minibatch_size, seed)
    
                  for minibatch in minibatches:
                      # 選擇一個minibatch
                      (minibatch_X, minibatch_Y) = minibatch
    
                      # 數據已經準備好了,開始運行session
                      _, minibatch_cost = sess.run([training_op, loss], feed_dict={X: minibatch_X, Y: minibatch_Y})
                      # 計算這個minibatch在這一代中所佔的偏差
                      epoch_cost = epoch_cost + minibatch_cost / num_minibatches
    
                  if epoch % 5 == 0:
                      # 將每輪迭代的代價函數值存入列表
                      costs.append(epoch_cost)
                      # 打印每輪迭代的代價函數值:
                      if print_cost and epoch % 100 == 0:
                          print("epoch = " + str(epoch) + "    epoch_cost = " + str(epoch_cost))
              # 保存學習後的參數
              new_saver.save(sess, './ckpt/my_test1_model.ckpt')
    
              # 繪製代價函數值的學習曲線
              if is_plot:
                  plt.plot(np.squeeze(costs))
                  plt.ylabel('cost')
                  plt.xlabel('iterations (per tens)')
                  plt.title("Learning rate =" + str(self.learning_rate))
                  plt.show()
    
              # 計算模型學習完成後的訓練集及測試集準確率
    
              train_acc = accuracy.eval({X: x_train, Y: y_train})
              test_acc = accuracy.eval({X: x_test, Y: y_test})
              print("訓練集的準確率:", train_acc)
              print("測試集的準確率:", test_acc)

    if name == 'main':
    # 開始時間
    start_time = time.clock()
    # 開始訓練
    nn = GestureSymbolRecognition()
    nn.train()
    # 結束時間
    end_time = time.clock()
    # 計算時差
    print("CPU的執行時間 = " + str(end_time - start_time) + " 秒")

5.6 凍結底層

  • 若是一個深度神經網絡的的低層已經學會了檢測圖像中的低級特徵,這對於另外一個圖像分類任務來講是有用的。所以能夠重用這些圖層。訓練新的DNN時,凍結圖層的權重是一個比較好的方法。
  • 爲了在訓練中凍結低層,最簡單的方法就是給優化器列出要訓練的變量列表,列表以外的變量即被凍結,不會被訓練。代碼以下:
    # 獲取全部的須要訓練的隱藏層的權重變量
    train_vars = tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES,scope='hidden[34]|outputs')
    # 把可訓練變量的列表傳給優化器的minimize()函數,未出如今列表中的圖層即被凍結了。
    training_op = optimizer.minimize(loss,var_list=train_vars)

5.7 緩存凍結層

  • 由於凍結層不會變化,因此就有可能將每個訓練實例的最高凍結層的輸出緩存起來。因爲訓練會輪詢整個數據集不少次,因此你將得到巨大的速度提高,由於你只須要在一個訓練實例中遍歷一次凍結層(而不是每一個全數據集一次)。舉個例子,你能夠第一次在低層跑完整個訓練集(假設你有足夠的RAM):
    hidden2_outputs = sess.run(hidden2, feed_dict={X: X_train})

  • 而後在訓練中,你要作的不是構建批量的訓練實例,而是批量構建隱藏層2的輸出,而且將它們餵給訓練操做:
    import numpy as np
    n_epochs = 100
    n_batches = 500
    for epoch in range(n_epochs):
    shuffled_idx = rnd.permutation(len(hidden2_outputs))
    hidden2_batches = np.array_split(hidden2_outputs[shuffled_idx], n_batches)
    y_batches = np.array_split(y_train[shuffled_idx], n_batches)
    for hidden2_batch, y_batch in zip(hidden2_batches, y_batches):
    sess.run(training_op, feed_dict={hidden2: hidden2_batch, y: y_batch})

    • 最後一行運行的是以前定義好的訓練操做(凍結層1和層2),而後把隱藏層2(對應批量目標)的批量輸出傳給它。由於咱們把隱藏層2的輸出傳給TensorFlow,因此它不用嘗試去評估它。

5.8 調整、丟棄或替換層

  • 模型複用時,原始模型的輸出層常常會被替換,由於對於新的任務,它基本沒有用,甚至沒有辦法提供正確的輸出個數。
  • 一樣,模型複用時,原始模型的高隱藏層沒有低隱藏層的用處多,由於對於新的任務來講,最有效的高級特性可能和原始模型中的最有效特性差異很大。因此,模型複用時,咱們應該找到正確的層數來重用。
  • 首先應嘗試凍結全部的複製的層,而後訓練模型觀察效果。接着嘗試解凍一到兩個頂部的隱藏層, 用反向傳播進行調整來觀察是否有改善。 訓練數據越多, 越能解凍更多的層。
  • 若是你一直不能得到好的效果, 並且訓練數據不多, 那就嘗試丟棄最高的一層或多層, 而後從新凍結剩下的隱藏層。 你能夠一直迭代直到找到正確的重用層數。 若是你有不少訓練數據, 你能夠嘗試替換頂部的隱藏層而不是丟棄它們, 甚至能夠添加一些隱藏層

5.9 模型動物園

  • 在哪裏能夠找到一個訓練好的相似神經網絡來完成一個你想要解決的任務呢? 首選固然是你本身已有的模型目錄。 這是一個很好的方式, 能夠保存你全部的模型, 方便你整理, 也方便你往後隨時調用。另外一個選擇就是在模型動物園(Model Zoo) 裏搜索。 不少人針對各類任務訓練了不少機器學習的模型, 而且很慷慨地公開這些預訓練的模型。
    • TensorFlow在https://github.com/tensorflow/models上公開了本身的
      模型動物園。 特別須要指出的是, 這個模型動物園包含了先進圖片識
      別的網絡, 好比VGG、 Inception和ResNet(見第13章, 同時查看
      models/slim目錄) , 包括代碼、 預訓練模型, 以及下載流行圖片數據集的工具 。
    • 另外一個流行的模型動物園是Caffe模型動物園(https://goo.gl/XI02X3) 。 其中也包括不少計算機視覺模型(好比:LeNet、 AlexNet、 ZFNet、 GoogLeNet、 VGGNet和inception) , 也都在各類數據集(好比ImageNet、 Places Database、 CIFAR10等) 上通過訓練。
    • Dasgupta寫了一個轉換器, 能夠在https://github.com/ethereon/caffe-tensorflow找到。

5.10 無監督的預訓練

  • 假設你要完成一個沒有太多標記訓練數據的複雜任務, 而且沒有找到在近似任務上訓練過的模型。
    • 首先, 你確定須要努力去收集更多的標記過的訓練數據, 可是若是這個代價很高或者很難, 你仍然能夠運行無監督的預訓練 ,也就是說, 若是 你有一堆未標記的訓練數據,那你能夠逐層訓練它們,從最低層開
      始,而後向上,利用一種非監督特性檢測算法好比受限玻爾茲曼機 或者自動編碼器 。
    • 每一層都是基於提早訓練好的圖層(除去被凍結的訓練層)的輸出進行訓練。一旦全部層都用這個方式訓練過以後,你就能夠用監督學習的方式(即反向傳播)來微調網絡。
  • 在面對複雜任務處理、沒有類似模型可重用、有極少標記過的訓練數據但
    是卻有不少未標記的訓練數據的狀況下,無監督的預處理(如今一般使用自動編碼器而不是RBM)是一個很是不錯的選擇。

5.11 輔助任務中的預訓練

  • 最後一個選擇是在輔助任務中訓練第一個神經網絡, 你能夠輕鬆得到或者生成標記過的訓練數據, 而後重用該網絡的低層來實現你的實際任務。 第一個神經網絡的低層會學習可能被第二個神經網絡重用的特徵檢測器。
    • 舉個例子, 若是你想構建一我的臉識別系統, 可是你只有每一個人的幾張照片, 很明顯沒辦法訓練一個好的分類器。 收集每一個人成百上千張照片根本不可行。 可是, 你能夠在網上收集不少隨機的人像照片, 用它們能夠訓練第一個神經網絡來檢測兩張不一樣的照片是否屬於相同的人。 這個網絡將學習優質的人臉特徵檢測器, 而後重用這個網絡的低層,這樣你就能夠用不多的訓練數據訓練出一個優質的人臉分類器。
  • 一般狀況下,收集未標記訓練示例會廉價不少,可是標記它們卻很貴。針對這種狀況,經常使用的技術是將全部示例都標記爲「好」,而後經過破壞好的訓練示例來生成新的訓練實例,並把那些被破壞的實例標記爲「壞」。接着你就能夠訓練第一個神經網絡來區分好的實例和壞的實例。
    • 舉個例子,你能夠下載數百萬條句子,標記它們爲「好」,而後隨機修改每一句中的一個單詞,並標記這些修改後的句子爲「壞」。若是一個神經網絡能夠識別出「The dog sleeps」是好, 「The dog they」是壞,那麼它可能已經學會很多語言了。重用這個網絡的低層可能有助於不少語言處理任務。
  • 另外一種方法是訓練第一個神經網絡,讓它輸出每個訓練實例的得分,而後利用成本函數,確保每個好的實例的得分都比壞的實例的得分至少高一些。咱們稱其爲最大邊界學習。

6 TensorBoard:可視化學習

  • TensorBoard簡介
    • 爲了更方便 TensorFlow 程序的理解、調試與優化,有了TensorBoard 的可視化工具。
      • TensorBoard 是TensorFlow 的可視化工具,它能夠經過TensorFlow 程序運行過程當中輸出的日誌文件可視化Te nsorFlow 程序的運行狀態。
      • TensorBoard 和TensorFlow 程序跑在不一樣的進程中。
      • Tensor Board 會自動讀取最新的TensorFlow 日誌文件,並呈現當前TensorFlow程序運行的最新狀態。
  • 使用可視化學習的流程
    • 數據序列化-events文件
      • 返回filewriter,寫入事件文件到指定目錄(最好用絕對路徑),以提供給tensorboard使用
      • TensorBoard 經過讀取 TensorFlow 的事件文件來運行,須要將數據生成一個序列化的 Summary protobuf 對象。
      • 注意:路徑不能出現中文,不然會打不開!!!
        # 實現加法
        con1 = tf.constant(11.0, name='input1')
        con2 = tf.constant(12.0, name='input2')
        sum_ = tf.add(con1, con2, name='add')
        with tf.Session() as sess:
        # 在會話中序列化events文件
        file_writer = tf.summary.FileWriter('f:logs/', tf.get_default_graph())
        file_writer.close()

      • 將在指定目錄中生成一個envent文件,格式爲:
        events.out.tfevents.{timestamp}.{hostname}
    • 啓動TensorBoard
  • 變量Tensorboard顯示
    • 目的:在TensorBoard當中觀察模型的參數、損失值等變量值的變化
    • 一、收集變量
      • tf.summary.scalar(name='',tensor):收集如損失函數和準確率等單值變量。name爲變量名稱,tensor爲值。
      • tf.summary.histogram(name='',tensor):收集高維度的變量參數,如線性迴歸的偏置,權重等。
      • tf.summary.image(name='',tensor):收集輸入的圖片張量,能顯示圖片。
    • 二、合併變量
      • merged = tf.summary.merge_all()
    • 三、生成事件文件,觀察圖結構
      • file_writer = tf.summary.FileWriter('路徑',graph=sess.graph)
        • 注意路徑中不要出現中文
    • 四、運行合併:summary=sess.run(merged)
      • 每次迭代都須要運行
    • 五、把summary,張量的值寫入到events文件當中
      • file_writer.add_summary(summary, i)
        • i表示第幾回的值

7 Tensorflow案例—實現線性迴歸

7.1 線性迴歸原理複習

  • 根據數據創建迴歸模型,w_1x_1+w_2x_2+...+w_nx_n+b = y經過真實值與預測值之間創建損失偏差,使用梯度降低優化獲得損失最小對應的權重和偏置。最終肯定模型的權重和偏置參數。最後用這些參數進行預測。

7.2 案例肯定

  • 假設隨機指定100個點,只有一個特徵
  • 數據自己的分佈爲 y = 0.7 * x + 0.8

    這裏將數據分佈的規律肯定,是爲了使咱們訓練出的參數跟真實的參數(即0.7和0.8)比較是否訓練準確。

7.3 使用的API

  • 運算
    • 矩陣運算:tf.matmul(x,w) 注:x與w的維數要相同
    • 平方:tf.square(error)
    • 求均值:tf.reduce_mean(error)
  • 梯度降低優化
    • tf.train.GradientDescentOptimizer(learning_rate)
      • 梯度降低優化
      • learning_rate:學習率,通常爲0~1之間較小的值
      • method:minimize(loss)
      • return:梯度降低OP
  • 定義變量以及初始化
    • 定義變量:
      tf.Variable(initial_value=None,trainable=True,collection=None,name=None)
      • 模型的要優化參數必須選擇tf.Variable定義,而且能夠指定trainable參數指定是否被訓練
      • inital_value:初始化的值
      • trainable:是否被訓練,默認爲True
      • collections:新變量將添加到列出的圖的集合collections中
    • 變量初始化
      • 變量op只有通過顯式初始化才能被運行
      • 顯式初始化:sess.run(tf.global_variables_initializer())
  • 獲取默認的計算圖並在默認計算圖中操做
    • 獲取默認計算圖:g = tf.get_default_graph()
    • 使用默認的計算圖:with g.as_default():
  • 增長命名空間
    • 做用:使代碼結構更加清晰,Tensorboard圖結構清楚,有助於代碼的模塊化。
    • with tf.variable_scope('lr_model'):
  • 變量Tensorboard顯示
    • 目的:在TensorBoard當中觀察模型的參數、損失值等變量值的變化
    • 一、收集變量
      • tf.summary.scalar(name='',tensor):收集如損失函數和準確率等單值變量。name爲變量名稱,tensor爲值。
      • tf.summary.histogram(name='',tensor):收集高維度的變量參數,如線性迴歸的偏置,權重等。
      • tf.summary.image(name='',tensor):收集輸入的圖片張量,能顯示圖片。
    • 二、合併變量
      • merged = tf.summary.merge_all()
    • 三、生成事件文件,觀察圖結構
      • TensorFlow1.x中的API:file_writer = tf.summary.FileWriter('路徑',graph=sess.graph)
        • 注意路徑中不要出現中文
    • 四、運行合併:summary=sess.run(merged)
      • 每次迭代都須要運行
    • 五、把summary,張量的值寫入到events文件當中
      • file_writer.add_summary(summary, i)
        • i表示第幾回的值
  • 模型的保存與加載
    • 建立一個保存文件的saveOP: saver = tf.train.Saver(var_list=None,max_to_keep=5)
      • 保存和加載模型(保存文件格式:checkpoint文件)
      • var_list:指定將要保存和還原的變量。它能夠做爲一個dict或一個列表傳遞.
      • max_to_keep:設置要保留的最近檢查點文件的最大數量。建立新文件時,會刪除較舊的文件。默認爲5(即保留最新的5個檢查點文件。)
    • 保存訓練好的模型:saver.save(sess,'ckpt/myregression.ckpt',global_step=step)
      • sess:當前會話名稱
      • 第二個參數是設定保存的路徑+名字
      • 第三個參數將訓練的次數做爲後綴加入到模型名字中
    • 加載模型:checkpoint = tf.train.latest_checkpoint("./ckpt/")
      • 從模型當中找出與當前訓練的模型代碼當中(名字同樣的op操做),覆蓋原來的值。
      • 判讀模型是否存在,若存在則恢復模型:
        if ckpt:
        saver.restore(sses,ckpt)
  • 命令行參數設置
    • 目的:使用終端命令行的方式去運行程序,並設置參數
    • 定義命令行參數:tf.app.flags.DEFINE_integer('train_step',0,'迭代次數')
      • 用於定義值是整型的參數
      • 第一個參數是定義的參數名
      • 第二個參數是默認值
      • 第三個參數是註釋(不能省略)
    • tf.app.flags.DEFINE_string('model_dir', '', '模型的保存路徑+模型名字')
      • 定義值是字符串的參數
    • 定義獲取命令行的參數:FLAGS = tf.app.flags.FLAGS
    • 使用命令行使用的參數:FLAGS.參數名稱
      • 例如:FLAGS.model_dir---->獲取模型的保存路徑+模型名字

7.4 示例代碼

"""
使用Tensorflow實現線性迴歸
"""
import tensorflow as tf
class MyLinearRegression(object):
    """
    實現一個線性迴歸
    """
    def __init__(self, FLAGS):
        self.FLAGS = FLAGS
        # 定義學習率
        self.learning_rate = 0.1
        # 定義迭代次數
        self.train_step = 1000
        # 定義並初始化線性迴歸模型的權重與偏置參數,因爲權重與偏置須要被訓練與優化,所以需用使用變量OP定義
        self.height = tf.Variable(tf.random_normal(shape=[1, 1], mean=0.0, stddev=1.0, name='height'))
        self.bais = tf.Variable(tf.random_normal(shape=[1], mean=0.0, stddev=1.0, name='bais'))

    def inputs(self):
        """
        獲取須要訓練的數據
        :return: x_feature,y_target
        """
        # 定義一個命名空間
        with tf.variable_scope('get_inputs'):
            # 公式:y = x*0.7 + 0.8
            # 生成符合正態分佈的特徵值
            x_feature = tf.random_normal(shape=[100, 1], mean=0.0, stddev=1.0, name='x_feature')
            # 利用矩陣乘法計算得出目標值,注意矩陣相乘,維數要相同
            y_target = tf.matmul(x_feature, [[0.7]]) + 0.8

            return x_feature, y_target

    def inference(self, x_feature):
        """
        根據數據創建線性迴歸模型
        :param x_feature: 特徵值
        :return: y_predic:預測結果
        """
        # 定義一個命名空間
        with tf.variable_scope('linear_model'):
            # 計算預測值
            y_predict = tf.matmul(x_feature, self.height) + self.bais

        return y_predict

    def loss(self, y_predict, y_target):
        """
        由線性迴歸模型的損失函數計算損失值
        :param y_predict: 預測值
        :param y_target: 目標值
        :return: 損失值
        """
        # 定義一個命名空間
        with tf.variable_scope('get_loss'):
            # 損失函數使目標值與預測值的均方差公式
            # 先用tf.square()計算差的平方
            # 再用tf.reduce_mean()對列表數據求和後再求平均值
            loss = tf.reduce_mean(tf.square(y_predict - y_target))
            return loss

    def sgd_op(self, loss):
        """
        使用隨機梯度降低優化器來優化損失(優化模型參數)
        :param loss: 損失大小
        :return: 梯度降低OP
        """
        # 定義一個命名空間
        with tf.variable_scope("sgd_op"):
            # 使用隨機梯度降低API得出優化結果
            train_op = tf.train.GradientDescentOptimizer(learning_rate=self.learning_rate).minimize(loss)

            return train_op

    def merge_summary(self, loss):
        """
        增長變量顯示:在TensorBoard當中觀察模型的參數、損失值等變量值的變化
        :param loss:損失
        :return: merged:合併的變量
        """
        # 1. 收集變量
        tf.summary.scalar('loss', loss)
        tf.summary.histogram('w', self.height)
        tf.summary.histogram('b', self.bais)

        # 2. 合併變量
        merged = tf.summary.merge_all()
        return merged

    def train(self):
        """
        用於實現線性迴歸模型訓練邏輯
        :return:
        """
        # 開啓一個默認的圖
        g = tf.get_default_graph()
        # 在默認的計算圖中進行操做
        with g.as_default():
            # 獲得輸入數據
            x_feature, y_target = self.inputs()
            # 創建線性迴歸模型,獲得預測值
            y_predict = self.inference(x_feature)
            # 根據損失函數求解損失值
            loss = self.loss(y_predict, y_target)
            # 使用梯度降低優化器優化損失
            train_op = self.sgd_op(loss)
            # 增長變量顯示:在TensorBoard當中觀察模型的參數、損失值等變量值的變化
            merged = self.merge_summary(loss)
            # 定義一個保存模型的saveOP,設置要保留的最近檢測點的模型的數量
            saver = tf.train.Saver(max_to_keep=5)
            # 開啓一個會話
            with tf.Session() as sess:

                # 初始化變量,初始化後才能夠在會話中運行
                sess.run(tf.global_variables_initializer())
                # 生成事件文件觀察圖結構
                file_writer = tf.summary.FileWriter('F:/tensorflow/logs', graph=sess.graph)

                print("模型隨機初始化的權重參數:{},偏置參數:{}".format(self.height.eval(), self.bais.eval()))

                # 加載模型,從模型當中找出與當前訓練的模型代碼當中(名字同樣的op操做),覆蓋原來的值
                checkpoint = tf.train.latest_checkpoint('./ckpt/')
                # 判斷模型是否存在,若存在則提取保存的模型
                if checkpoint:
                    saver.restore(sess, checkpoint)
                print("第一次加載保存模型的權重參數:{},偏置參數:{}".format(self.height.eval(), self.bais.eval()))

                # 使用梯度降低優化損失
                for i in range(1, self.FLAGS.train_step + 1):
                    # 運行梯度降低OP與合併OP
                    _, summary = sess.run([train_op, merged])
                    print("第{}步優化,損失爲{},權重參數:{},偏置參數:{}".format(i, loss.eval(), self.height.eval(), self.bais.eval()))
                    # 將優化的變量寫入事件文件
                    file_writer.add_summary(summary, i)

                    # 每隔一百步保存一次模型
                    if i % 100 == 0:
                        saver.save(sess, self.FLAGS.model_dir, global_step=i)  # './ckpt/myregression.ckpt'


if __name__ == '__main__':
    # 定義命令行參數
    tf.app.flags.DEFINE_integer('train_step', 0, '訓練步數')
    tf.app.flags.DEFINE_string('model_dir', '', "模型的保存路徑")
    # 定義獲取命令行的參數
    FLAGS = tf.app.flags.FLAGS
    lr = MyLinearRegression(FLAGS)
    lr.train()

8 Tensorflow文件讀取

8.1 獲取數據到Tensorflow的方法

  • TensorFlow程序讀取數據一共有3種方法:
  • 供給數據(Feeding): Python產生數據,再把數據餵給後端。
    import tensorflow as tf
    # 設計Graph
    x1 = tf.placeholder(tf.int16)
    x2 = tf.placeholder(tf.int16)
    y = tf.add(x1, x2)
    # 用Python產生數據
    li1 = [2, 3, 4]
    li2 = [4, 0, 1]
    # 打開一個session --> 喂數據 --> 計算y
    with tf.Session() as sess:
    print sess.run(y, feed_dict={x1: li1, x2: li2})

    """
    說明:在這裏x1, x2只是佔位符,沒有具體的值,那麼運行的時候去哪取值呢?這時候就要用到sess.run()中的feed_dict參數,將Python產生的數據餵給後端,並計算y。
    """
    • 缺點是:用佔位符替代數據,待運行的時候填充數據。中間環節的增長也是不小的開銷,遇到大型數據的時候就會很吃力。
  • 預加載數據: 在TensorFlow圖中定義常量或變量來保存全部數據(僅適用於數據量比較小的狀況)。
    import tensorflow as tf
    # 設計Graph
    x1 = tf.constant([2, 3, 4])
    x2 = tf.constant([4, 0, 1])
    y = tf.add(x1, x2)
    # 打開一個session --> 計算y
    with tf.Session() as sess:
    print sess.run(y)

    • 經過預加載的方式加載數據的缺點在於,將數據直接內嵌到Graph中,再把Graph傳入Session中運行。當數據量比較大時,Graph的傳輸會遇到效率問題。
  • 從文件讀取數據(QueueRunner):簡單來講就是將數據讀取模塊的圖搭好

8.2 TensorFlow文件讀取數據流程

  • 步驟:
    • 第一階段:生成文件名+路徑的列表。讀取該列表並將其放入文件名隊列。
    • 第二階段:從隊列當中讀取文件內容,而且進行解碼操做。
    • 第三階段:解碼以後,咱們能夠直接獲取默認的一個樣本內容了,可是若是想要獲取多個樣本,這個時候須要結合管道進行批處理。
  • 第一階段
    • 咱們稱之爲構造文件隊列,將須要讀取的文件裝入到一個固定的隊列當中
      • tf.train.string_input_producer(string_tensor,num_epochs,shuffle=True)
        • string_tensor:含有文件名+路徑的一階張量
        • num_epochs:將數據集中的數據所有計算幾遍
        • shuffle:設置在一個epochs內文件順序是否被打亂
        • return:一個文件隊列
  • 第二階段:讀取文件內容,並進行解碼操做
    • 讀取文件內容:
      • TensorFlow默認每次只讀取一個樣本,具體到:文本文件讀取一行、二進制文件讀取指定字節數(最好一個樣本)、圖片文件默認讀取一張圖片、TFRecords默認讀取一個example。
      • tf.TextLineReader:
        • 閱讀文本文件,默認按行讀取
        • return:讀取器實例
      • tf.WholeFileReader:用於讀取圖片文件
      • tf.TFRecordReader:讀取TFRecords文件
      • tf.FixedLengthRecordReader:讀取二進制文件
        • 要讀取每一個記錄是固定數量字節的二進制文件
        • record_bytes:整型,指定每次讀取(一個樣本)的字節數
        • return:讀取器實例
          一、他們有共同的讀取方法:read(file_queue):從隊列中指定數量內容返回一個Tensors元組(key文件名字,value默認的內容(一個樣本))
          二、因爲默認只會讀取一個樣本,因此一般想要進行批處理。使用tf.train.batch或tf.train.shuffle_batch進行多樣本獲取,便於訓練時候指定每批次多個樣本的訓練
    • 對文件內容進行解碼
      • 對於讀取不通的文件類型,內容須要解碼操做,解碼成統一的Tensor格式
      • tf.decode_csv:解碼文本文件內容
        • 與tf.TextLineReader搭配使用
      • tf.decode_raw:解碼二進制文件內容
        • 與tf.FixedLengthRecordReader搭配使用,二進制讀取爲uint8格式
      • tf.image.decode_jpeg(contents):解碼圖片文件
        • 將JPEG編碼的圖像解碼爲uint8張量
        • return:unit8張量,3-D形狀[height,width,channels]
      • tf.image_decode_png(contents):解碼圖片文件
        • 將PNG編碼的圖像解碼爲uint8張量
        • return:張量類型,3-D形狀[height,width,channels]
          解碼階段,默認全部的內容都解碼成tf.uint8格式,若是須要後續的類型處理繼續處理
  • 第三階段:結合管道進行批處理
    • 在解碼以後,咱們能夠直接獲取默認的一個樣本內容了,可是若是想要獲取多個樣本,這個時候須要結合管道的末尾進行批處理
      • tf.train_batch(tensors,batch_size,num_threads=n,capacity=m,name='')
        • 做用:讀取指定大小(個數)的張量
        • tensors:列表形式,須要批處理的張量放到列表中
        • batch_size:設置從隊列中讀取批處理的大小
        • num_threads:設置進入隊列的線程數
        • capacity:設置隊列中元素的最大數量
        • return:tensors
      • tf.train.shuffle_batch(tensors,batch_size,num_threads=n,capacity=m,name='')
        • 做用:對隊列中的樣本進行亂序處理,讀取指定大小(個數)的張量
        • tensors:列表形式,須要批處理的張量放到列表中
        • batch_size:設置從隊列中讀取批處理的大小
        • num_threads:設置進入隊列的線程數
        • capacity:設置隊列中元素的最大數量
        • return:tensors

8.3 TensorFlow讀取機制圖解

首先須要思考的一個問題是,什麼是數據讀取?以圖像數據爲例,讀取數據的過程能夠用下圖來表示:



    假設咱們的硬盤中有一個圖片數據集0001.jpg,0002.jpg,0003.jpg……咱們只須要把它們讀取到內存中,而後提供給GPU或是CPU進行計算就能夠了。這聽起來很容易,但事實遠沒有那麼簡單。

    事實上,咱們必需要把數據先讀入後才能進行計算,假設讀入用時0.1s,計算用時0.9s,那麼就意味着每過1s,GPU都會有0.1s無事可作,這就大大下降了運算的效率。

如何解決這個問題?方法就是將讀入數據和計算分別放在兩個線程中,將數據讀入內存的一個隊列,以下圖所示:

讀取線程源源不斷地將文件系統中的圖片讀入到一個內存的隊列中,而負責計算的是另外一個線程,計算須要數據時,直接從內存隊列中取就能夠了。這樣就能夠解決GPU由於IO而空閒的問題!

    而在tensorflow中,爲了方便管理,在內存隊列前又添加了一層所謂的「文件名隊列」。

    爲何要添加這一層文件名隊列?咱們首先得了解機器學習中的一個概念:epoch。對於一個數據集來說,運行一個epoch就是將這個數據集中的圖片所有計算一遍。如一個數據集中有三張圖片A.jpg、B.jpg、C.jpg,那麼跑一個epoch就是指對A、B、C三張圖片都計算了一遍。兩個epoch就是指先對A、B、C各計算一遍,而後再所有計算一遍,也就是說每張圖片都計算了兩遍。

    tensorflow使用文件名隊列+內存隊列雙隊列的形式讀入文件,能夠很好地管理epoch。下面咱們用圖片的形式來講明這個機制的運行方式。以下圖,仍是以數據集A.jpg, B.jpg, C.jpg爲例,假定咱們要跑一個epoch,那麼咱們就在文件名隊列中把A、B、C各放入一次,並在以後標註隊列結束。

程序運行後,內存隊列首先讀入A(此時A從文件名隊列中出隊):

再依次讀入B和C:

此時,若是再嘗試讀入,系統因爲檢測到了「結束」,就會自動拋出一個異常(OutOfRange)。外部捕捉到這個異常後就能夠結束程序了。這就是tensorflow中讀取數據的基本機制。若是咱們要跑2個epoch而不是1個epoch,那隻要在文件名隊列中將A、B、C依次放入兩次再標記結束就能夠了。

8.4 TensorFlow讀取數據機制的對應函數

如何在tensorflow中建立文件名隊列與內存隊列呢?

對於文件名隊列,咱們使用tf.train.string_input_producer函數。這個函數須要傳入一個文件名list,系統會自動將它轉爲一個文件名隊列。

    此外tf.train.string_input_producer還有兩個重要的參數,一個是num_epochs,它就是咱們上文中提到的epoch數。另一個就是shuffle,shuffle是指在一個epoch內文件的順序是否被打亂。若設置shuffle=False,以下圖,每一個epoch內,數據仍是按照A、B、C的順序進入文件名隊列,這個順序不會改變:

若是設置shuffle=True,那麼在一個epoch內,數據的先後順序就會被打亂,以下圖所示:

在tensorflow中,內存隊列不須要咱們本身創建,咱們只須要使用reader對象從文件名隊列中讀取數據就能夠了,具體實現能夠參考下面的實戰代碼。

    除了tf.train.string_input_producer外,咱們還要額外介紹一個函數:tf.train.start_queue_runners。在咱們使用tf.train.string_input_producer建立文件名隊列後,整個系統其實仍是處於「停滯狀態」的,也就是說,咱們文件名並無真正被加入到隊列中(以下圖所示)。此時若是咱們開始計算,由於內存隊列中什麼也沒有,計算單元就會一直等待,致使整個系統被阻塞。

而使用tf.train.start_queue_runners以後,纔會啓動填充隊列的線程,這時系統就再也不「停滯」。此後計算單元就能夠拿到數據並進行計算,整個程序也就跑起來了,這就是函數tf.train.start_queue_runners的用處。

8.5 TensorFlow讀取數據的線程操做

  • 建立隊列和排隊操做稱之爲tf.train.QueueRunner。每一個QueueRunner都負責一個階段,並擁有須要在線程中運行的排隊操做列表。一旦圖形被構建, tf.train.start_queue_runners函數就會要求圖中的每一個QueueRunner啓動它運行排隊操做的線程。(這些操做須要在會話中開啓)
    • tf.train.start_queue_runners(sess=,coord=)
      • 做用:收集圖中的全部隊列線程,並啓動線程
      • sess:設置所在的會話
      • coord:設置線程協調器
      • return:返回全部線程
    • coord = tf.train.Coordinator()
      • 做用:建立一個線程協調器實例,實現一個簡單的機制來協調一組線程的終止
      • request_stop():請求中止
      • should_stop():詢問是否結束
      • join(threads=,stop_grace_period_secs=120):回收線程
      • return:返回一個線程調節器實例

8.6 TensorFlow讀取圖片數據

圖像基本知識

對於圖像文件,咱們怎麼進行轉換成機器學習可以理解的數據?對於圖片來說,組成圖片的最基本單位是像素,因此咱們獲取的是每張圖片的像素值。接觸的圖片有兩種,一種是黑白圖片,另外一種是彩色圖片。
  • 圖片三要素
    • 組成一張圖片特徵值是全部的像素值,有以下幾個要素:
      • 圖片長度(height)
      • 圖片寬度(width)
      • 圖片通道數(channel)
    • 什麼是圖片的通道數?
      • 灰度圖:單通道
      • 彩色圖片:三通道
      • 假設一張彩色圖片的長200,寬200,通道數爲3,那麼總的像素數量爲200X200X3
  • 張量形狀
    • 讀取圖片以後,怎麼用張量形狀來表示?
      • 一張圖片就是一個3D張量,[height, width, channel]咱們會常常遇到3D和4D的表示
      • 單個圖片:[height, width, channel]
      • 多個圖片:[batch,height, width, channel],batch表示批數量
  • 圖片的特徵值處理
    • 爲何要進行特徵值處理?
      • 一方面,在進行圖片識別的時候,每一個圖片樣本的特徵數量要保持相同。因此須要將全部圖片張量大小統一轉換。
      • 另外一方面,若是圖片的像素量太大,也能夠經過這種方式適當減小像素的數量,減小訓練的計算開銷。
  • 特徵值處理的經常使用API:
    • 數據類型轉化:tf.convert_image_dtype(image,dtype,name=None)
      • 做用:把圖片元素類型,轉成想要的類型,返回轉換後的圖片,注意,要是轉成了float類型以後,像素值會在 [0,1)這個範圍內。
      • 參數:
        image: 圖像
        dtype: 待轉換類型
        name: 操做名
    • 形狀變換:
      • tf.image.resize_images(images,size)
        • 做用:將輸入圖像從新縮放或者放大到固定的尺寸
        • images:形狀爲[batch, height, width, channels]的4-D張量或形狀爲[height, width, channels]的3-D張量.
        • size:1維 int32類型的 Tensor,包含兩個元素:new_height, new_width.
        • method:改變形狀的方法,默認是ResizeMethod.BILINEAR雙線性插值
        • align_corners:布爾型,若是爲True,則輸入和輸出張量的4個拐角像素的中心對齊,而且保留角落像素處的值;默認爲False.
          可能引起的異常:ValueError
        • 若是images的形狀與此函數的形狀參數不兼容.
        • 若是size有無效的形狀或類型.
        • 若是指定了不支持的調整大小方法.
      • resize_image_with_crop_or_pad(…):裁剪或將圖像填充到目標寬度和高度
      • central_crop(…): 裁剪圖像的中心區域
      • crop_and_resize(…): 從輸入圖像張量中提取裁剪物並對其進行雙線性的調整
      • crop_to_bounding_box(…): 將圖像裁剪到指定的邊框。
    • 圖像翻轉
      神經網絡實際上是很」笨」的,要是你提供的圖片都是像上面那樣朝向左邊,那麼當出現一幅朝向右邊的圖片的時候,它極可能沒法識別. 因此,能夠人爲的多加一些翻轉上去,使得各個角度的圖片都有一點,無形中,擴充了數據量,還緩解了識別錯誤的問題.
      • flip_left_right(image): 左右翻轉
        • 做用:左右翻轉一幅圖片,返回一個形狀和原圖片相同的圖片(翻轉後)
          image:3維tensor,形狀爲[height, width, channels].
      • flip_up_down(…): 上下翻轉
      • transpose_image(…): 對角線翻轉
      • random_flip_left_right(…): 隨機左右翻轉
    • 顏色變換
      使用翻轉能夠來」增長數據集」之外,調節顏色屬性也是一樣頗有用的方法,這裏主要有調整亮度,對比度,飽和度,色調等方法.以下:
      • adjust_brightness(…): 調整亮度
        • 做用:調節亮度
          參數:
          image: tensor,原圖片
          delta: 標量,待加到像素值上面的值.
      • random_brightness(…): 隨機調整亮度
      • adjust_contrast(…): 調整對比度
      • random_contrast(…): 隨機調整亮度
      • adjust_saturation(…): 調整飽和度
      • random_saturation(…): 隨機調整飽和度
      • adjust_hue(…): 調整色調
      • random_hue(…): 隨機調整色調

案例:狗圖片讀取

  • 讀取流程:
    • 構造圖片文件名隊列
    • 創造一個圖片讀取器,利用圖片讀取器去讀取隊列中的內容
    • 對讀取到的圖片內容進行解碼
    • 對解碼後的圖片內容進行特徵值處理,進行形狀和大小的固定
    • 對圖片數據進行批處理
    • 手動開啓子線程去進行批處理讀取到隊列的操做
      • 建立用於線程回收的協調器
      • 開啓子線程去讀取數據
    • 獲取樣本數據
    • 回收子線程
  • 代碼

    """
    狗圖片讀取案例

    """
    import tensorflow as tf

    import os

    def picture_read(file_list):
    """
    讀取狗圖片數據到張量
    :param file_list:路徑+文件名的列表
    :return:
    """
    # 1. 構造圖片文件名隊列
    # 返回文件名隊列
    file_queue = tf.train.string_input_producer(file_list)
    # 2. 構造一個圖片讀取器,利用圖片讀取器去讀取隊列中的內容
    reader = tf.WholeFileReader()
    # 默認一次讀取一張圖片,沒有形狀
    key, value = reader.read(file_queue)
    # 3. 對讀取到的圖片內容進行解碼
    # 數據類型從string---->unit8
    # 形狀從()---->(???)
    image = tf.image.decode_jpeg(value)
    # 4. 圖片特徵值處理:對圖片進行批處理以前,進行形狀與大小的固定
    # 把圖片固定成統一大小的目的是由於訓練的數據集的每一個樣本的特徵值應該相同。
    # 設置成固定的大小
    image_resized = tf.image.resize_images(image, size=[200, 200])
    # 設置固定的通道數,使用靜態改變形狀的方法:set_shape()
    image_resized.set_shape([200, 200, 3])
    # 5. 對圖片數據進行批處理,批處理以前,每一個樣本的形狀必須固定
    image_batch = tf.train.batch([image_resized], batch_size=10, num_threads=4, capacity=10)

    # 在默認圖中進行會話操做
      g = tf.get_default_graph()
      with g.as_default():
          # 開啓會話
          with tf.Session() as sess:
              # 1 手動開啓子線程去進行批處理讀取到隊列操做
              # 1.1 建立用於線程回收的協調器
              coord = tf.train.Coordinator()
              # 1.2 開啓子線程去讀取數據,返回子線程實例
              threads = tf.train.start_queue_runners(sess=sess, coord=coord)
              # 2. 獲取樣本數據
              pic_data = sess.run([image_batch])
              print(pic_data)
              # 3. 回收子線程
              coord.request_stop()
              coord.join(threads)

    if name == 'main':
    # 生成文件名列表
    file_name_list = os.listdir('../data/dog/')
    # 構造文件名+路徑列表
    file_list = [os.path.join('../data/dog/', file) for file in file_name_list]
    # 圖片文件讀取
    picture_read(file_list)

8.7 TensorFlow利用二進制方式讀取CIFAR10數據集

  • CIFAR10數據集的二進制版本介紹
    • 二進制版本包含文件data_batch_1.bin,data_batch_2.bin,...,data_batch_5.bin以及test_batch.bin。這些文件中的每個格式以下,數據中每一個樣本包含了特徵值和目標值:
      • <1×標籤> <3072×像素> 
        ... 
        <1×標籤> <3072×像素>
    • 第一個字節是第一個圖像的標籤,它是一個0-9範圍內的數字。接下來的3072個字節是圖像像素的值。前1024個字節是紅色通道值,下1024個綠色,最後1024個藍色。值以行優先順序存儲,所以前32個字節是圖像第一行的紅色通道值。 每一個文件都包含10000個這樣的3073字節的「行」圖像,但沒有任何分隔行的限制。所以每一個文件應該徹底是30730000字節長。
  • CIFAR10 二進制數據讀取
    • 讀取流程:
      • 構造文件名隊列
        • 生成文件名列表:os.listdir(path)
        • 生成文件名+路徑列表:os.path.join(path,file_name)
        • 構造文件名隊列:tf.train.string_input_producer(file_name_list)
      • 使用二進制讀取器讀取內容
        • 建立二進制讀取器實例:tf.FixedLengthRecordReader(一個樣本佔的字節數)
        • 讀取內容:reader.read(file_queue)
      • 對讀取的數據進行解碼:tf.decode_raw(value,tf.uint8)
      • 標籤、圖片的數據類型轉換、形狀轉換
        • 數據類型轉換:tf.cast(須要轉換的數據,轉換的數據類型)
        • 固定圖片的特徵值形狀:
          • depth_major=tf.shape(image,[channel,height,width])從(3072,)--->[channel,height,width]
          • tf.transpose(depth_major,[1,2,0])[channel,height,width]--->[heigt,width,channel]
      • 批處理圖片數據:image_batch,label_batch=tf.train.batch([image_reshaped,label_cast],batch_size=,num_threads=,capacity=)
      • 手動開啓子線程去進行數據的批處理讀取到隊列的操做
        • 建立用於線程回收的協調器:coord=tf.train.Coordinator()
        • 開啓子線程讀取數據到隊列:threads=tf.train.start_queue_runners(sess=sess,coord=coord)
      • 獲取圖片樣本數據:pic_data = sess.run(image_batch,label_batch)
      • 回收子線程:
        • coord.request_stop()
        • coord.join(threads)
    • 代碼
      """
      CIFAR10數據集二進制版本的讀取
      """
      import tensorflow as tf
      import os

      class CifarRead(object):
      """
      CIFAR二進制文件的讀取
      """

      def __init__(self, filename_list):
            """
            定義一些圖片的樣本屬性
            :param filename_list:
            """
            self.filename_list = filename_list
            self.height = 32
            self.width = 32
            self.channel = 3
      
            self.label_bytes = 1
            self.image_bytes = self.height * self.width * self.channel
            self.bytes = self.label_bytes + self.image_bytes
      
        def read_and_decode(self):
            """
            讀取二進制原始數據並解碼成張量
            :return:
            """
            # 1. 構造文件隊列
            file_queue = tf.train.string_input_producer(self.filename_list)
            # 2. 使用二進制讀取器讀取內容
            # 2.1 建立二進制讀取器實例,實在一次讀取的字節數
            reader = tf.FixedLengthRecordReader(self.bytes)
            # 2.2 讀取內容,一共讀取1+3072=3073個字節,此時的value是某個文件的某個樣本
            key, value = reader.read(file_queue)
            # 3. 對二進制數據進行解碼 decode_raw
            label_image = tf.decode_raw(value, tf.uint8)
            # 4. 切分圖片的特徵值與目標值:使用tf.slice(數據集,[起始下標],[結束下標])切片
            label = tf.slice(label_image, [0], [self.label_bytes])
            image = tf.slice(label_image, [self.label_bytes], [self.image_bytes])
            # 5. 標籤、圖片的類型轉換、形狀轉換
            # 5.1 標籤數據類型轉換 tf.cast()
            label_cast = tf.cast(label, tf.int32)
            image = tf.cast(image, tf.float32)
            # 5.2 固定圖片的特徵值形狀
            # 5.2.1 shape:(3072,)--->[channel,height,width]
            depth_major = tf.reshape(image, [self.channel, self.height, self.width])
            print(depth_major)
            # 5.2.2 tf.transpose:[channel,height,width]--->[heigt,width,channel]
            image_reshaped = tf.transpose(depth_major, [1, 2, 0])
            print(image_reshaped)
            # 6.批處理圖片數據
            image_batch, label_batch = tf.train.batch([image_reshaped, label_cast], batch_size=10, num_threads=4,
                                                      capacity=10)
            print(image_batch)
            print(label_batch)
            return image_batch, label_batch
      
        def run(self):
            """
            執行文件讀取邏輯
            :return:
            """
            # 讀取二進制原始數據並解碼成張量,形狀固定於批處理
            image_batch, label_batch = self.read_and_decode()
            # 開啓會話
            with tf.Session() as sess:
                # 1. 手動開啓子線程去進行數據的批處理讀取到隊列的操做
                # 1.1 建立用於線程回收的協調器
                coord = tf.train.Coordinator()
                # 1.2 開啓子線程去讀取數據
                threads = tf.train.start_queue_runners(sess=sess, coord=coord)
                # 2. 獲取樣本數據
                pic_data = sess.run([image_batch, label_batch])
                print(pic_data)
                # 3. 回收子線程
                coord.request_stop()
                coord.join(threads)

      if name == 'main':
      # 生成文件名列表
      filename_list = os.listdir('../data/cifar10/cifar-10-batches-bin/')
      # 生成文件名加路徑列表
      filename_list = [os.path.join('../data/cifar10/cifar-10-batches-bin/', file) for file in filename_list if
      file[-3:] == 'bin']
      # 實例化類
      cr = CifarRead(filename_list)
      cr.run()

  • 注意:
    • 一、image (3072, ) —>tf.reshape(image, [])裏面的shape默認是[channel, height, width], 因此得先從[depth height width] to [depth, height, width]。
    • 二、而後使用tf.transpose, 將剛纔的數據[depth, height, width],變成Tensorflow默認的[height, width, channel]
      • 假設1, 2, 3, 4-紅色 5, 6, 7, 8-綠色 9, 10, 11, 12-藍色
        若是通道在最低維度0[channel, height, width],RGB三顏色分紅三組,在第一維度上找到三個RGB顏色
        若是通道在最高維度2[height, width, channel],在第三維度上找到RGB三個顏色

      一、想要變成:[2 height, 2width, 3channel],可是輸出結果不對

      In [7]: tf.reshape([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], [2, 2, 3]).eval()
      Out[7]:
      array([[[ 1, 2, 3],
      [ 4, 5, 6]],

      [[ 7,  8,  9],
          [10, 11, 12]]], dtype=int32)

      二、因此要這樣去作

      In [8]: tf.reshape([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], [3, 2, 2]).eval()
      Out[8]:
      array([[[ 1, 2],
      [ 3, 4]],

      [[ 5,  6],
          [ 7,  8]],
      
         [[ 9, 10],
          [11, 12]]], dtype=int32)

      接着使用tf.transpose ,0,1,2表明三個維度標記

      Convert from [depth, height, width] to [height, width, depth].

      0,1,2-----> 1, 2, 0

      In [17]: tf.transpose(depth_major, [1, 2, 0]).eval()
      Out[17]:
      array([[[ 1, 5, 9],
      [ 2, 6, 10]],

      [[ 3,  7, 11],
          [ 4,  8, 12]]], dtype=int32)

8.8 CIFAR10類圖片的數據的TFRecords存儲和讀取

數據存入TFRecords文件

什麼是TFRecords文件

  • TFRecords實際上是一種二進制文件,它能更好的利用內存,更方便複製和移動,而且不須要單獨的標籤文件。
  • TFRecords文件是TensorFlow的標準格式,使用TFRecords文件保存記錄的方法能夠容許你將任意數據轉換爲TensorFlow所支持的格式,這種方法可使TensorFlow的數據集更容易與網絡應用架構相匹配。
  • 文件格式: *.tfrecords

什麼是Example結構?

  • tf.train.Example爲協議內存塊(protocol buffer)
    • 協議內存塊包含了字段Features
    • Features包含了一個Feature字段
    • Feature字段中包含要寫入的數據、並指明數據類型。這是一個樣本的結構,批數據須要循環的存入這樣的結構。
      example = tr.train.Example(features=tf.train.Features(feature={
      "特徵值字段名":tf.train.Feature(特徵值數據類型列表=tf.train.特徵值數據類型列表類(value=[特徵值字段名])),
      "標籤值字段名":tf.train.Feature(標籤值數據類型列表=tf.train.標籤值數據類型列表類(value=[標籤值字段名]))
      }))

    • tf.train.Example(features=)
      • 做用:生成協議內存塊
      • features:tf.train.Features類型的特徵實例
    • tf.train.Features(feature=)
      • 做用:構建每一個樣本的信息鍵值對
      • feature:字典數據,key爲要保存的名字
      • value:爲tf.train.Feature實例
    • tf.train.Feature(options)
      • options:特徵值數據類型列表,例如
        • bytes_list=tf.train. BytesList(value=[Bytes])
        • int64_list=tf.train. Int64List(value=[Value])
      • 屬性的取值能夠爲字符串(BytesList ),浮點數列表(FloatList )或整數列表(Int64List )。例如咱們能夠將圖片轉換爲字符串進行存儲,圖像對應的類別標號做爲整數存儲,而用於迴歸任務的ground-truth能夠做爲浮點數存儲。
      • 支持存入的類型以下:
        • tf.train.Int64List(value=[Value])
        • tf.train.BytesList(value=[Bytes])
        • tf.train.FloatList(value=[value])
  • 協議內存塊結構很好的解決了數據和標籤或者其它屬性數據存儲到同一個文件中的問題。

寫入數據到TFRecords中的主要步驟:

  • 獲取數據, 將數據填入到tf.train.Example協議內存塊(protocol buffer)。
  • 將協議內存塊序列化爲一個字符串。example.SerializeToString()
  • 經過tf.python_io.TFRecordWriter寫入到TFRecords文件。

寫入數據到TFRecords中的示例代碼:

def wite_to_tfrecords(self, image_batch, label_batch):
        """
        將文件寫入到TFRecords文件中
        :param image_batch:
        :param label_batch:
        :return:
        """
        # 1. 創建TFRecords文件存儲器
        writer = tf.python_io.TFRecordWriter('./1.tfrecords')  # 設置路徑及文件名
        # 2. 循環取出每一個樣本的值,構造example協議塊
        for i in range(10):  # 由於每批量有10個樣本數據
            # 2.1 取出圖片的值
            # 寫入文件的是值,而不是tensor類型,寫入example須要bytes類型數據,須要用tostring()來轉化
            image = image_batch[i].eval().tostring()
            # 2.2 取出標籤值,寫入example中須要int類型,因此須要強制轉換成int類型
            label = int(label_batch[i].eval()[0])
            # 2.3 構造每一個樣本的example協議塊
            feature = {
                "image": tf.train.Feature(bytes_list=tf.train.BytesList(value=[image])),
                "label": tf.train.Feature(int64_list=tf.train.Int64List(value=[label]))
            }
            features = tf.train.Features(feature=feature)
            example = tf.train.Example(features=features)
            # 2.4 將example協議塊序列化
            example_serialized = example.SerializeToString()
            # 2.5 寫入序列化後的值
            writer.write(example_serialized)  # 此處實際上是將其壓縮成一個二進制數據

        # 3. 關閉文件存儲器
        writer.close()
        return None

8.9 從TFRecords文件中讀取數據

解析example

feature = tr.parse_single_example(values,features={
    "image":tf.FixedLenFeature([],tf.string),
    "label":tf.FixedLenFeature([],tf.int64)
})
  • tf.parse_single_example(serialized,features=None,name=None)
    • 做用:解析一個單一的Example原型
    • serialize:標量字符串Tensor,一個序列化的Example
    • features:dict字典數據,鍵爲讀取的名字,值爲FixedLenFeature
  • tf.FixedLenFeature(shape,dtype)
    • shape:輸入數據的形狀,通常不指定,爲空列表
    • dtype:輸入數據類型,與存儲進文件的類型要一致
    • 類型只能是float32,int64,string

從TFRecords文件中讀取數據的主要步驟:

  • 構造文件隊列:tf.train.string_input_producer([文件名列表])
  • 實例化TFRecords文件閱讀器:reader = tf.TFRecordReader()
    • 讀取數據:key, value = reader.read(file_queue)
  • 解析example協議塊:feature=tf.parse_single_example(serialized,features=None,name=None)
    • 解析一個單一的Example原型
    • serialized:標量字符串Tensor,一個序列化的Example
    • features:dict字典數據,鍵爲讀取的名字,值爲FixedLenFeature
    • return:一個鍵值對組成的字典,鍵爲讀取的名字
  • 處理數據
    • 處理標籤數據:label = tf.cast(feature["label"], tf.int32)
      • cast()只能在int和float之間進行轉換,將數據類型int64 轉換爲int32
    • 處理特徵值數據:image = tf.decode_raw(feature['image'], tf.uint8)
      • 若是以前用了tostring(),那麼必需要用decode_raw()轉換爲最初的int類型
      • decode_raw()能夠將數據從string,bytes轉換爲int,float類型
  • 轉換特徵值的形狀(形狀未固定,靜態方式:tf.set_shape;形狀已固定,動態方式:tf.reshape):
    • image_tensor = tf.reshape(image,[self.height,self.width,self.channel])
  • 批處理:image_batch,label_batch = tf.train.batch([image_tensor,label],batch_size=10,num_threads=4,capacity=10)

從TFRecords文件中讀取數據示例代碼:

def read_to_tfrecords(self):
        """
        從TFRecords文件中讀取圖片數據(解析example)
        :return:
        """
        pass
        # 1. 構造文件隊列
        file_queue = tf.train.string_input_producer(['./1.tfrecords'])  # 參數爲文件名列表
        # 2. 實例化TFRecords文件閱讀器
        reader = tf.TFRecordReader()
        # 讀取數據
        key, value = reader.read(file_queue)
        # 3. 解析example協議塊,返回值是字典
        features = {
            "image": tf.FixedLenFeature([], tf.string),
            "label": tf.FixedLenFeature([], tf.int64)
        }
        feature = tf.parse_single_example(value, features=features)
        # 4. 處理標籤數據
        # cast()只能在int和float之間進行轉換,將數據類型int64 轉換爲int32
        label = tf.cast(feature["label"], tf.int32)
        # 處理圖片數據
        # 因爲是一個string,要進行解碼,
        # 將字節轉換爲數字向量表示,字節爲一字符串類型的張量
        # 若是以前用了tostring(),那麼必需要用decode_raw()轉換爲最初的int類型
        # decode_raw()能夠將數據從string,bytes轉換爲int,float類型
        image = tf.decode_raw(feature['image'], tf.uint8)
        # 5. 轉換圖片的形狀,此處須要用動態形狀進行轉換
        image_tensor = tf.reshape(image, [self.height, self.width, self.channel])
        # 6. 批處理
        image_batch, label_batch = tf.train.batch([image_tensor, label], batch_size=10, num_threads=4, capacity=10)
        return image_batch, label_batch

CIFAR10類圖片的數據的TFRecords存儲和讀取示例代碼

import tensorflow as tf
import os

"""
讀取二進制文件轉換成張量,寫進TFRecords,同時讀取TFRcords
"""

# 命令行參數
FLAGS = tf.app.flags.FLAGS  # 獲取值
tf.app.flags.DEFINE_string("tfrecord_dir", "./tmp/cifar10.tfrecords", "寫入圖片數據文件的文件名")

# 讀取二進制轉換文件
class CifarRead(object):
    """
    讀取二進制文件轉換成張量,寫進TFRecords,同時讀取TFRcords
    """

    def __init__(self, file_list):
        """
        初始化圖片參數
        :param file_list:圖片的路徑名稱列表
        """

        # 文件列表
        self.file_list = file_list

        # 圖片大小,二進制文件字節數
        self.height = 32
        self.width = 32
        self.channel = 3
        self.label_bytes = 1
        self.image_bytes = self.height * self.width * self.channel
        self.bytes = self.label_bytes + self.image_bytes

    def read_and_decode(self):
        """
        解析二進制文件到張量
        :return: 批處理的image,label張量
        """
        # 1.構造文件隊列
        file_queue = tf.train.string_input_producer(self.file_list)

        # 2.閱讀器讀取內容
        reader = tf.FixedLengthRecordReader(self.bytes)

        key, value = reader.read(file_queue)  # key爲文件名,value爲元組

        print(value)

        # 3.進行解碼,處理格式
        label_image = tf.decode_raw(value, tf.uint8)
        print(label_image)

        # 處理格式,image,label
        # 進行切片處理,標籤值
        # tf.cast()函數是轉換數據格式,此處是將label二進制數據轉換成int32格式
        label = tf.cast(tf.slice(label_image, [0], [self.label_bytes]), tf.int32)

        # 處理圖片數據
        image = tf.slice(label_image, [self.label_bytes], [self.image_bytes])
        print(image)

        # 處理圖片的形狀,提供給批處理
        # 由於image的形狀已經固定,此處形狀用動態形狀來改變
        depth_major = tf.reshape(image, [self.channel, self.height, self.width])
        image_tensor = tf.transpose(depth_major, [1, 2, 0])
        print(image_tensor)

        # 批處理圖片數據
        image_batch, label_batch = tf.train.batch([image_tensor, label], batch_size=10, num_threads=1, capacity=10)

        return image_batch, label_batch

    def write_to_tfrecords(self, image_batch, label_batch):
        """
        將文件寫入到TFRecords文件中
        :param image_batch:
        :param label_batch:
        :return:
        """

        # 創建TFRecords文件存儲器
        writer = tf.python_io.TFRecordWriter('./1.tfrecords')  # 傳進去命令行參數

        # 循環取出每一個樣本的值,構造example協議塊
        for i in range(10):
            # 取出圖片的值,  #寫進去的是值,而不是tensor類型,
            # 寫入example須要bytes文件格式,將tensor轉化爲bytes用tostring()來轉化
            image = image_batch[i].eval().tostring()

            # 取出標籤值,寫入example中須要使用int形式,因此須要強制轉換int
            label = int(label_batch[i].eval()[0])

            # 構造每一個樣本的example協議塊
            example = tf.train.Example(features=tf.train.Features(feature={
                "image": tf.train.Feature(bytes_list=tf.train.BytesList(value=[image])),
                "label": tf.train.Feature(int64_list=tf.train.Int64List(value=[label]))
            }))

            # 寫進去序列化後的值
            writer.write(example.SerializeToString())  # 此處實際上是將其壓縮成一個二進制數據

        writer.close()

        return None

    def read_from_tfrecords(self):
        """
        從TFRecords文件當中讀取圖片數據(解析example)
        :param self:
        :return: image_batch,label_batch
        """

        # 1.構造文件隊列
        file_queue = tf.train.string_input_producer(['./1.tfrecords'])  # 參數爲文件名列表

        # 2.構造閱讀器
        reader = tf.TFRecordReader()

        key, value = reader.read(file_queue)

        # 3.解析協議塊,返回的值是字典
        feature = tf.parse_single_example(value, features={
            "image": tf.FixedLenFeature([], tf.string),
            "label": tf.FixedLenFeature([], tf.int64)
        })

        # feature["image"],feature["label"]
        # 處理標籤數據    ,cast()只能在int和float之間進行轉換
        label = tf.cast(feature["label"], tf.int32)  # 將數據類型int64 轉換爲int32

        # 處理圖片數據,因爲是一個string,要進行解碼,  #將字節轉換爲數字向量表示,字節爲一字符串類型的張量
        # 若是以前用了tostring(),那麼必需要用decode_raw()轉換爲最初的int類型
        # decode_raw()能夠將數據從string,bytes轉換爲int,float類型的
        image = tf.decode_raw(feature["image"], tf.uint8)

        # 轉換圖片的形狀,此處須要用動態形狀進行轉換
        image_tensor = tf.reshape(image, [self.height, self.width, self.channel])

        # 4.批處理
        image_batch, label_batch = tf.train.batch([image_tensor, label], batch_size=10, num_threads=1, capacity=10)

        return image_batch, label_batch

    def run(self):
        """
        實現讀取數據的主邏輯
        :return:
        """
        # 讀取二進制文件
        # image_batch,label_batch = self.read_and_decode()

        # 從已經存儲的TFRecords文件中解析出原始數據
        image_batch, label_batch = self.read_from_tfrecords()

        with tf.Session() as sess:
            # 線程協調器
            coord = tf.train.Coordinator()

            # 開啓線程
            threads = tf.train.start_queue_runners(sess, coord=coord)

            print(sess.run([image_batch, label_batch]))

            print("存進TFRecords文件")
            # self.write_to_tfrecords(image_batch,label_batch)
            print("存進文件完畢")

            # 回收線程
            coord.request_stop()
            coord.join(threads)


if __name__ == '__main__':
    # 找到文件路徑,名字,構造路徑+文件名的列表,"A.csv"...
    # os.listdir() 方法用於返回指定的文件夾包含的文件或文件夾的名字的列表
    filename = os.listdir('../data/cifar10/cifar-10-batches-bin/')

    # 加上路徑
    file_list = [os.path.join('../data/cifar10/cifar-10-batches-bin/', file) for file in filename if file[-3:] == "bin"]

    # 建立一個文件讀取對象
    cr = CifarRead(file_list)
    # 開始運行
    cr.run()

9 Tensorflow中其它基礎API與高級API

9.1 基礎API

  • tf.app:用於命令行參數設置
    • 這個模塊至關於爲 TensorFlow 進行的腳本撮供了一個 main 函數入口,能夠定義腳本運行的 flags。
    • 使用終端命令行的方式去運行程序,並設置參數
  • tf.image
    • TensorFlow 的圖像處理操做。主要是一些顏色變換、變形和圖像的編碼和解碼。
  • tf.gfile
    • 這個模塊提供了一組文件操做函數。
  • tf.summary
    • 用來生成 TensorBoard 可用的統計日誌,目前 Summary 主要提供了 4 種類型:audio、image、histogram、scalar
  • tf.python_io
    • 用來讀寫 TFRecords文件
  • tf.train
    • 這個模塊提供了一些訓練器,與 tf.nn 組合起來,實現一些網絡的優化計算。
  • tf.nn
    • 這個模塊提供了一些構建神經網絡的底層函數。 TensorFlow 構建網絡的核心模塊。其中包含了添加各類層的函數,好比添加捲積層、池化層等。

9.2 高級API

  • tf.keras
    • Keras 原本是一個獨立的深度學習庫,tensorflow將其學習過來,增長這部分模塊在於快速構建模型。
  • tf.layers
    • 高級 API,以更高級的概念層來定義一個模型。相似tf.keras。
  • tf.contrib
    • tf.contrib.layers提供夠將計算圖中的 網絡層、正則化、摘要操做、是構建計算圖的高級操做,可是tf.contrib包含不穩定和實驗代碼,有可能之後API會改變。
  • tf.estimator
    • 一個 Estimator 至關於 Model + Training + Evaluate 的合體。在模塊中,已經實現了幾種簡單的分類器和迴歸器,包括:Baseline,Learning 和 DNN。這裏的 DNN 的網絡,只是全鏈接網絡,沒有提供卷積之類的。

10 TensorFlow訓練稀疏模型

  • 全部提出的優化算法都會產生一個密集的模型,這意味着大多數參數都是非零的。 若是咱們須要一個在運行時速度很是快速的模型,或者須要模型佔用較少的內存,咱們可能更喜歡用一個稀疏模型來代替。
  • 訓練一個稀疏模型的一個小方法就是像日常同樣訓練模型,而後擺脫值小的權重(將它們設置爲 0)。另外一個方法是在訓練過程當中應用強 l1 正則化,由於它會推進優化器儘量多地消除權重。
  • 可是,在某些狀況下,這些技術可能仍然不足。最後一個方法時應用對偶平均,一般稱爲FTRL(Follow The Regularized Leader)。當與 l1 正則化一塊兒使用時,這種技術一般會導出一個很是稀疏的模型。 TensorFlow 在FTRLOptimizer類中實現稱爲 FTRL-Proximal 的 FTRL 變體。

TensorFlow 中的 layers 模塊

TensorFlow 中的 layers 模塊提供用於深度學習的更高層次封裝的 API,利用它咱們能夠輕鬆地構建模型,這一節咱們就來看下這個模塊的 API 的具體用法。

11 TensorFlow中的layers模塊

11.1 概覽

layers 模塊的路徑寫法爲 tf.layers,這個模塊定義在 tensorflow/python/layers/layers.py,其官方文檔地址爲:https://www.tensorflow.org/api_docs/python/tf/layers,TensorFlow 版本爲 1.5。

這裏面提供了多個類和方法以供使用,下面咱們分別予以介紹。

11.2 方法

tf.layers 模塊提供的方法有:

  • Input(…): 用於實例化一個輸入 Tensor,做爲神經網絡的輸入。
  • average_pooling1d(…): 一維平均池化層
  • average_pooling2d(…): 二維平均池化層
  • average_pooling3d(…): 三維平均池化層
  • batch_normalization(…): 批量標準化層
  • conv1d(…): 一維卷積層
  • conv2d(…): 二維卷積層
  • conv2d_transpose(…): 二維反捲積層
  • conv3d(…): 三維卷積層
  • conv3d_transpose(…): 三維反捲積層
  • dense(…): 全鏈接層
  • dropout(…): Dropout層
  • flatten(…): Flatten層,即把一個 Tensor 展平
  • max_pooling1d(…): 一維最大池化層
  • max_pooling2d(…): 二維最大池化層
  • max_pooling3d(…): 三維最大池化層
  • separable_conv2d(…): 二維深度可分離卷積層

11.2.1 tf.layers.Input

tf.layers.Input() 這個方法是用於輸入數據的方法,其實相似於 tf.placeholder,至關於一個佔位符的做用,固然也能夠經過傳入 tensor 參數來進行賦值。

Input(
    shape=None,
    batch_size=None,
    name=None,
    dtype=tf.float32,
    sparse=False,
    tensor=None
)

參數說明以下:

  • shape:可選,默認 None,是一個數字組成的元組或列表,可是這個 shape 比較特殊,它不包含 batch_size,好比傳入的 shape 爲 [32],那麼它會將 shape 轉化爲 [?, 32],這裏必定須要注意。
  • batch_size:可選,默認 None,表明輸入數據的 batch size,能夠是數字或者 None。
  • name:可選,默認 None,輸入層的名稱。
  • dtype:可選,默認 tf.float32,元素的類型。
  • sparse:可選,默認 False,指定是否以稀疏矩陣的形式來建立 placeholder。
  • tensor:可選,默認 None,若是指定,那麼建立的內容便再也不是一個 placeholder,會用此 Tensor 初始化。

返回值: 返回一個包含歷史 Meta Data 的 Tensor。

咱們用一個實例來感覺一下:

x = tf.layers.Input(shape=[32])
print(x)
y = tf.layers.dense(x, 16, activation=tf.nn.softmax)
print(y)

首先咱們用 Input() 方法初始化了一個 placeholder,這時咱們沒有傳入 tensor 參數,而後調用了 dense() 方法構建了一個全鏈接網絡,激活函數使用 softmax,而後將兩者輸出,結果以下:

Tensor("input_layer_1:0", shape=(?, 32), dtype=float32)
Tensor("dense/Softmax:0", shape=(?, 16), dtype=float32)

這時咱們發現,shape 它給咱們作了轉化,原本是 [32],結果它給轉化成了 [?, 32],即第一維表明 batch_size,因此咱們須要注意,在調用此方法的時候不須要去關心 batch_size 這一維。

若是咱們在初始化的時候傳入一個已有 Tensor,例如:

data = tf.constant([1, 2, 3])
x = tf.layers.Input(tensor=data)
print(x)

結果以下:

data = tf.constant([1, 2, 3])
x = tf.layers.Input(tensor=data)
print(x)

能夠看到它能夠自動計算出其 shape 和 dtype。

11.2.2 tf.layers.batch_normalization

此方法是批量標準化的方法,通過處理以後能夠加速訓練速度,其定義在 tensorflow/python/layers/normalization.py,論文能夠參考:http://arxiv.org/abs/1502.03167 「Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift」。

batch_normalization(
    inputs,
    axis=-1,
    momentum=0.99,
    epsilon=0.001,
    center=True,
    scale=True,
    beta_initializer=tf.zeros_initializer(),
    gamma_initializer=tf.ones_initializer(),
    moving_mean_initializer=tf.zeros_initializer(),
    moving_variance_initializer=tf.ones_initializer(),
    beta_regularizer=None,
    gamma_regularizer=None,
    beta_constraint=None,
    gamma_constraint=None,
    training=False,
    trainable=True,
    name=None,
    reuse=None,
    renorm=False,
    renorm_clipping=None,
    renorm_momentum=0.99,
    fused=None,
    virtual_batch_size=None,
    adjustment=None
)

參數說明以下:

  • inputs:必需,即輸入數據。
  • axis:可選,默認 -1,即進行標註化操做時操做數據的哪一個維度。
  • momentum:可選,默認 0.99,即動態均值的動量。
  • epsilon:可選,默認 0.01,大於0的小浮點數,用於防止除0錯誤。
  • center:可選,默認 True,若設爲True,將會將 beta 做爲偏置加上去,不然忽略參數 beta
  • scale:可選,默認 True,若設爲True,則會乘以gamma,不然不使用gamma。當下一層是線性的時,能夠設False,由於scaling的操做將被下一層執行。
  • beta_initializer:可選,默認 zeros_initializer,即 beta 權重的初始方法。
  • gamma_initializer:可選,默認 ones_initializer,即 gamma 的初始化方法。
  • moving_mean_initializer:可選,默認 zeros_initializer,即動態均值的初始化方法。
  • moving_variance_initializer:可選,默認 ones_initializer,即動態方差的初始化方法。
  • beta_regularizer: 可選,默認None,beta 的正則化方法。
  • gamma_regularizer: 可選,默認None,gamma 的正則化方法。
  • beta_constraint: 可選,默認None,加在 beta 上的約束項。
  • gamma_constraint: 可選,默認None,加在 gamma 上的約束項。
  • training:可選,默認 False,返回結果是 training 模式。
  • trainable:可選,默認爲 True,布爾類型,若是爲 True,則將變量添加 GraphKeys.TRAINABLE_VARIABLES 中。
  • name:可選,默認 None,層名稱。
  • reuse:可選,默認 None,根據層名判斷是否重複利用。
  • renorm:可選,默認 False,是否要用 Batch Renormalization (https://arxiv.org/abs/1702.03275)
  • renorm_clipping:可選,默認 None,是否要用 rmax、rmin、dmax 來 scalar Tensor。
  • renorm_momentum,可選,默認 0.99,用來更新動態均值和標準差的 Momentum 值。
  • fused,可選,默認 None,是否使用一個更快的、融合的實現方法。
  • virtual_batch_size,可選,默認 None,是一個 int 數字,指定一個虛擬 batch size。
  • adjustment,可選,默認 None,對標準化後的結果進行適當調整的方法。

最後的一些參數說明不夠詳盡,更詳細的用法參考:https://www.tensorflow.org/api_docs/python/tf/layers/batch_normalization

其用法很簡單,在輸入數據後面加一層 batch_normalization() 便可:

x = tf.layers.Input(shape=[32])
x = tf.layers.batch_normalization(x)
y = tf.layers.dense(x, 20)

11.2.3 tf.layers.dense

dense,即全鏈接網絡,layers 模塊提供了一個 dense() 方法來實現此操做,定義在 tensorflow/python/layers/core.py 中,下面咱們來講明一下它的用法。

dense(
    inputs,
    units,
    activation=None,
    use_bias=True,
    kernel_initializer=None,
    bias_initializer=tf.zeros_initializer(),
    kernel_regularizer=None,
    bias_regularizer=None,
    activity_regularizer=None,
    kernel_constraint=None,
    bias_constraint=None,
    trainable=True,
    name=None,
    reuse=None
)

參數說明以下:

  • inputs:必需,即須要進行操做的輸入數據。
  • units:必須,即神經元的數量。
  • activation:可選,默認爲 None,若是爲 None 則是線性激活。
  • use_bias:可選,默認爲 True,是否使用偏置。
  • kernel_initializer:可選,默認爲 None,即權重的初始化方法,若是爲 None,則使用默認的 Xavier 初始化方法。
  • bias_initializer:可選,默認爲零值初始化,即偏置的初始化方法。
  • kernel_regularizer:可選,默認爲 None,施加在權重上的正則項。
  • bias_regularizer:可選,默認爲 None,施加在偏置上的正則項。
  • activity_regularizer:可選,默認爲 None,施加在輸出上的正則項。
  • kernel_constraint,可選,默認爲 None,施加在權重上的約束項。
  • bias_constraint,可選,默認爲 None,施加在偏置上的約束項。
  • trainable:可選,默認爲 True,布爾類型,若是爲 True,則將變量添加到 GraphKeys.TRAINABLE_VARIABLES 中。
  • name:可選,默認爲 None,卷積層的名稱。
  • reuse:可選,默認爲 None,布爾類型,若是爲 True,那麼若是 name 相同時,會重複利用。

返回值: 全鏈接網絡處理後的 Tensor。

下面咱們用一個實例來感覺一下它的用法:

x = tf.layers.Input(shape=[32])
print(x)
y1 = tf.layers.dense(x, 16, activation=tf.nn.relu)
print(y1)
y2 = tf.layers.dense(y1, 5, activation=tf.nn.sigmoid)
print(y2)

首先咱們用 Input 定義了 [?, 32] 的輸入數據,而後通過第一層全鏈接網絡,此時指定了神經元個數爲 16,激活函數爲 relu,接着輸出結果通過第二層全鏈接網絡,此時指定了神經元個數爲 5,激活函數爲 sigmoid,最後輸出,結果以下:

x = tf.layers.Input(shape=[32])
print(x)
y1 = tf.layers.dense(x, 16, activation=tf.nn.relu)
print(y1)
y2 = tf.layers.dense(y1, 5, activation=tf.nn.sigmoid)
print(y2)

能夠看到輸出結果的最後一維度就等於神經元的個數,這是很是容易理解的。

11.2.4 tf.layers.convolution

convolution,即卷積,這裏提供了多個卷積方法,如 conv1d()、conv2d()、conv3d(),分別表明一維、二維、三維卷積,另外還有 conv2d_transpose()、conv3d_transpose(),分別表明二維和三維反捲積,還有 separable_conv2d() 方法表明二維深度可分離卷積。它們定義在 tensorflow/python/layers/convolutional.py 中,其用法都是相似的,在這裏以 conv2d() 方法爲例進行說明。

conv2d(
    inputs,
    filters,
    kernel_size,
    strides=(1, 1),
    padding='valid',
    data_format='channels_last',
    dilation_rate=(1, 1),
    activation=None,
    use_bias=True,
    kernel_initializer=None,
    bias_initializer=tf.zeros_initializer(),
    kernel_regularizer=None,
    bias_regularizer=None,
    activity_regularizer=None,
    kernel_constraint=None,
    bias_constraint=None,
    trainable=True,
    name=None,
    reuse=None
)

參數說明以下:

  • inputs:必需,即須要進行操做的輸入數據。
  • filters:必需,是一個數字,表明了輸出通道的個數,即 output_channels。
  • kernel_size:必需,卷積核大小,必須是一個數字(高和寬都是此數字)或者長度爲 2 的列表(分別表明高、寬)。
  • strides:可選,默認爲 (1, 1),卷積步長,必須是一個數字(高和寬都是此數字)或者長度爲 2 的列表(分別表明高、寬)。
  • padding:可選,默認爲 valid,padding 的模式,有 valid 和 same 兩種,大小寫不區分。
  • data_format:可選,默認 channels_last,分爲 channels_last 和 channels_first 兩種模式,表明了輸入數據的維度類型,若是是 channels_last,那麼輸入數據的 shape 爲 (batch, height, width, channels),若是是 channels_first,那麼輸入數據的 shape 爲 (batch, channels, height, width)。
  • dilation_rate:可選,默認爲 (1, 1),卷積的擴張率,如當擴張率爲 2 時,卷積核內部就會有邊距,3×3 的卷積核就會變成 5×5。
  • activation:可選,默認爲 None,若是爲 None 則是線性激活。
  • use_bias:可選,默認爲 True,是否使用偏置。
  • kernel_initializer:可選,默認爲 None,即權重的初始化方法,若是爲 None,則使用默認的 Xavier 初始化方法。
  • bias_initializer:可選,默認爲零值初始化,即偏置的初始化方法。
  • kernel_regularizer:可選,默認爲 None,施加在權重上的正則項。
  • bias_regularizer:可選,默認爲 None,施加在偏置上的正則項。
  • activity_regularizer:可選,默認爲 None,施加在輸出上的正則項。
  • kernel_constraint,可選,默認爲 None,施加在權重上的約束項。
  • bias_constraint,可選,默認爲 None,施加在偏置上的約束項。
  • trainable:可選,默認爲 True,布爾類型,若是爲 True,則將變量添加到 GraphKeys.TRAINABLE_VARIABLES 中。
  • name:可選,默認爲 None,卷積層的名稱。
  • reuse:可選,默認爲 None,布爾類型,若是爲 True,那麼若是 name 相同時,會重複利用。

返回值: 卷積後的 Tensor。

下面咱們用實例感覺一下它的用法:

x = tf.layers.Input(shape=[20, 20, 3])
y = tf.layers.conv2d(x, filters=6, kernel_size=2, padding='same')
print(y)


      這裏咱們首先聲明瞭一個 [?, 20, 20, 3] 的輸入 x,而後將其傳給 conv2d() 方法,filters 設定爲 6,即輸出通道爲 6,kernel_size 爲 2,即卷積核大小爲 2 x 2,padding 方式設置爲 same,那麼輸出結果的寬高和原來必定是相同的,可是輸出通道就變成了 6,結果以下:

Tensor("conv2d/BiasAdd:0", shape=(?, 20, 20, 6), dtype=float32)

但若是咱們將 padding 方式不傳入,使用默認的 valid 模式,代碼改寫以下:

x = tf.layers.Input(shape=[20, 20, 3])
y = tf.layers.conv2d(x, filters=6, kernel_size=2)
print(y)

結果以下:

Tensor("conv2d/BiasAdd:0", shape=(?, 19, 19, 6), dtype=float32)

結果就變成了 [?, 19, 19, 6],這是由於步長默認爲 1,卷積核大小爲 2 x 2,因此獲得的結果的高寬即爲 (20 – (2 – 1)) x (20 – (2 – 1)) = 19 x 19。

固然卷積核咱們也能夠變換大小,傳入一個列表形式:

x = tf.layers.Input(shape=[20, 20, 3])
y = tf.layers.conv2d(x, filters=6, kernel_size=[2, 3])
print(y)

這時咱們的卷積核大小變成了 2 x 3,即高爲 2,寬爲 3,結果就變成了 [?, 19, 18, 6],這是由於步長默認爲 1,卷積核大小爲 2 x 2,因此獲得的結果的高寬即爲 (20 – (2 – 1)) x (20 – (3 – 1)) = 19 x 18。

若是咱們將步長也設置一下,也傳入列表形式:

x = tf.layers.Input(shape=[20, 20, 3])
y = tf.layers.conv2d(x, filters=6, kernel_size=[2, 3], strides=[2, 2])
print(y)

這時卷積核大小變成了 2 x 3,步長變成了 2 x 2,因此結果的高寬爲 ceil(20 – (2- 1)) / 2 x ceil(20 – (3- 1)) / 2 = 10 x 9,獲得的結果即爲 [?, 10, 9, 6]。

運行結果以下:

Tensor("conv2d_4/BiasAdd:0", shape=(?, 10, 9, 6), dtype=float32)

另外咱們還能夠傳入激活函數,或者禁用 bias 等操做,實例以下:

x = tf.layers.Input(shape=[20, 20, 3])
y = tf.layers.conv2d(x, filters=6, kernel_size=2, activation=tf.nn.relu, use_bias=False)
print(y)

這樣咱們就將激活函數改爲了 relu,同時禁用了 bias,運行結果以下:

Tensor("conv2d_5/Relu:0", shape=(?, 19, 19, 6), dtype=float32)

另外還有反捲積操做,反捲積顧名思義即卷積的反向操做,即輸入卷積的結果,獲得卷積前的結果,其參數用法是徹底同樣的,例如:

x = tf.layers.Input(shape=[20, 20, 3])
y = tf.layers.conv2d_transpose(x, filters=6, kernel_size=2, strides=2)
print(y)

例如此處輸入的圖像高寬爲 20 x 20,通過卷積核爲 2,步長爲 2 的反捲積處理,獲得的結果高寬就變爲了 40 x 40,結果以下:

Tensor("conv2d_transpose/BiasAdd:0", shape=(?, 40, 40, 6), dtype=float32)

11.2.5 tf.layers.pooling

pooling,即池化,layers 模塊提供了多個池化方法,這幾個池化方法都是相似的,包括 max_pooling1d()、max_pooling2d()、max_pooling3d()、average_pooling1d()、average_pooling2d()、average_pooling3d(),分別表明一維二維三維最大和平均池化方法,它們都定義在 tensorflow/python/layers/pooling.py 中,這裏以 max_pooling2d() 方法爲例進行介紹。

max_pooling2d(
    inputs,
    pool_size,
    strides,
    padding='valid',
    data_format='channels_last',
    name=None
)

參數說明以下:

  • inputs: 必需,即須要池化的輸入對象,必須是 4 維的。
  • pool_size:必需,池化窗口大小,必須是一個數字(高和寬都是此數字)或者長度爲 2 的列表(分別表明高、寬)。
  • strides:必需,池化步長,必須是一個數字(高和寬都是此數字)或者長度爲 2 的列表(分別表明高、寬)。
  • padding:可選,默認 valid,padding 的方法,valid 或者 same,大小寫不區分。
  • data_format:可選,默認 channels_last,分爲 channels_last 和 channels_first 兩種模式,表明了輸入數據的維度類型,若是是 channels_last,那麼輸入數據的 shape 爲 (batch, height, width, channels),若是是 channels_first,那麼輸入數據的 shape 爲 (batch, channels, height, width)。
  • name:可選,默認 None,池化層的名稱。

返回值: 通過池化處理後的 Tensor。

下面咱們用一個實例來感覺一下:

x = tf.layers.Input(shape=[20, 20, 3])
print(x)
y = tf.layers.conv2d(x, filters=6, kernel_size=3, padding='same')
print(y)
p = tf.layers.max_pooling2d(y, pool_size=2, strides=2)
print(p)

在這裏咱們首先指定了輸入 x,shape 爲 [20, 20, 3],而後對其進行了卷積計算,而後池化,最後獲得池化後的結果。結果以下:

Tensor("input_layer_1:0", shape=(?, 20, 20, 3), dtype=float32)
Tensor("conv2d/BiasAdd:0", shape=(?, 20, 20, 6), dtype=float32)
Tensor("max_pooling2d/MaxPool:0", shape=(?, 10, 10, 6), dtype=float32)

能夠看到這裏池化窗口用的是 2,步長也是 2,因此本來卷積後 shape 爲 [?, 20, 20, 6] 的結果就變成了 [?, 10, 10, 6]。

11.2.6 tf.layers.dropout

dropout 是指在深度學習網絡的訓練過程當中,對於神經網絡單元,按照必定的機率將其暫時從網絡中丟棄,能夠用來防止過擬合,layers 模塊中提供了 dropout() 方法來實現這一操做,定義在 tensorflow/python/layers/core.py。下面咱們來講明一下它的用法。

dropout(
    inputs,
    rate=0.5,
    noise_shape=None,
    seed=None,
    training=False,
    name=None
)

參數說明以下:

  • inputs:必須,即輸入數據。
  • rate:可選,默認爲 0.5,即 dropout rate,如設置爲 0.1,則意味着會丟棄 10% 的神經元。
  • noise_shape:可選,默認爲 None,int32 類型的一維 Tensor,它表明了 dropout mask 的 shape,dropout mask 會與 inputs 相乘對 inputs 作轉換,例如 inputs 的 shape 爲 (batch_size, timesteps, features),但咱們想要 droput mask 在全部 timesteps 都是相同的,咱們能夠設置 noise_shape=[batch_size, 1, features]。
  • seed:可選,默認爲 None,即產生隨機熟的種子值。
  • training:可選,默認爲 False,布爾類型,即表明了是否標誌位 training 模式。
  • name:可選,默認爲 None,dropout 層的名稱。

返回: 通過 dropout 層以後的 Tensor。

咱們用一個實例來感覺一下:

x = tf.layers.Input(shape=[32])
print(x)
y = tf.layers.dense(x, 16, activation=tf.nn.softmax)
print(y)
d = tf.layers.dropout(y, rate=0.2)
print(d)

運行結果:

Tensor("input_layer_1:0", shape=(?, 32), dtype=float32)
Tensor("dense/Softmax:0", shape=(?, 16), dtype=float32)
Tensor("dropout/Identity:0", shape=(?, 16), dtype=float32)

在這裏咱們使用 dropout() 方法實現了 droput 操做,並制定 dropout rate 爲 0.2,最後輸出結果的 shape 和原來是一致的。

11.2.7 tf.layers.flatten

flatten() 方法能夠對 Tensor 進行展平操做,定義在 tensorflow/python/layers/core.py。

flatten(
    inputs,
    name=None
)

參數說明以下:

  • inputs:必需,即輸入數據。
  • name:可選,默認爲 None,即該層的名稱。

返回結果: 展平後的 Tensor。

下面咱們用一個實例來感覺一下:

x = tf.layers.Input(shape=[5, 6])
print(x)
y = tf.layers.flatten(x)
print(y)

運行結果:

Tensor("input_layer_1:0", shape=(?, 5, 6), dtype=float32)
Tensor("flatten/Reshape:0", shape=(?, 30), dtype=float32)

這裏輸入數據的 shape 爲 [?, 5, 6],通過 flatten 層以後,就會變成 [?, 30],即將除了第一維的數據維度相乘,對原 Tensor 進行展平。

假如第一維是一個已知的數的話,它依然仍是一樣的處理,示例以下:

Tensor("input_layer_1:0", shape=(?, 5, 6), dtype=float32)
Tensor("flatten/Reshape:0", shape=(?, 30), dtype=float32)

結果以下:

Tensor("input_layer_1:0", shape=(?, 5, 6), dtype=float32)
Tensor("flatten/Reshape:0", shape=(?, 30), dtype=float32)

12 TensorFlow 計算交叉熵錯誤問題解決

sparse_softmax_cross_entropy_with_logits(_sentinel=None, labels=None, logits=None, name=None)

在使用tf.nn.sparse_softmax_cross_entropy_with_logits(logits, labels)語句時產生以下錯誤:

import tensorflow as tf

labels = [[0.2,0.3,0.5],
          [0.1,0.6,0.3]]
logits = [[2,0.5,1],
          [0.1,1,3]]result1 = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=labels, logits=logits)若是這樣用就會報這個錯ValueError: Rank mismatch: Rank of labels (received 2) should equal rank of logits minus 1 (received 2).
  • 緣由是logits和labels在使用時有labels應該少一維的限制。
    好比一個tensorflow的分類問題,logits應該是batch×classes的一個矩陣,classes爲類別數量labels應該是長batch的一個數組,當logits判斷圖片爲某一類時,對應classes的位置爲1
    • 例如:
      • 例子,好比貓狗大戰吧。當你一批次訓練10張圖片時,batch爲10,貓狗總共兩種分類。logits就應該是相似這樣的一個10×2的矩陣:
        [
        0 1
        1 0
        0 1
        0 1
        0 1
        1 0
        1 0
        1 0
        1 0
        0 1]
      • 10是10張圖片, 每行第一個位置若是爲1,那麼這一張圖片是狗,每行第二個位置若是爲1,那麼這一張圖片是貓。而此時的label應該是一個這樣的一維矩陣:[2 1 2 2 2 1 1 1 1 2]logits和label知足這種形式時使用tf.nn.sparse_softmax_cross_entropy_with_logits(logits, labels)語句是正確的。

下面演示一下,兩個方法的正確用法:

import tensorflow as tf

labels = [[0.2,0.3,0.5],
          [0.1,0.6,0.3]]
logits = [[2,0.5,1],
          [0.1,1,3]]result1 = tf.nn.softmax_cross_entropy_with_logits(labels=labels, logits=logits)
import tensorflow as tf

labels = [0,2]

logits = [[2,0.5,1],
          [0.1,1,3]]

result1 = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=labels, logits=logits)

總結:

  • sparse_softmax_cross_entropy_with_logits 主要用於結果是排他惟一性的計算, 若是是非惟一的須要用到 softmax_cross_entropy_with_logits。
  • sparse_softmax_cross_entropy的logits一般是最後的全鏈接層的輸出結果,labels是具體哪一類的標籤,這個函數是直接使用標籤數據的,而不是採用one-hot編碼形式。
  • .softmax_cross_entropy使用標籤數據的one-hot編碼形式。

tf.nn.in_top_k函數的用法

  • tf.nn.in_top_k主要是用於計算預測的結果和實際結果的是否相等,返回一個bool類型的張量
  • tf.nn.in_top_k(predictions,targets,k,name=None)函數
    • 這輸出了一個batch_size bool數組,若是目標類的預測是全部預測(例如i)中的前k個預測,則條目out[i]爲true.請注意,InTopK的行爲在處理關係時與TopK操做不一樣;若是多個類具備相同的預測值並跨越top-k邊界,則全部這些類都被認爲是在前k個.
    • 參數:
      • predictions:float32類型的Tensor.batch_size x classes張量.
      • targets:一個Tensor,必須是如下類型之一:int32,int64.類ids的batch_size向量.
      • k:int類型,要計算精度的頂級元素數.
      • name:操做的名稱(可選).
    • 返回:
      • bool類型的Tensor.以bool Tensor計算k處的精度.

      '''
      predictions: 你的預測結果(通常也就是你的網絡輸出值)大小是預測樣本的數量乘以輸出的維度
      target: 實際樣本類別的標籤,大小是樣本數量的個數
      k: 每一個樣本中前K個最大的數裏面(序號)是否包含對應target中的值

      '''
      import tensorflow as tf
      A = tf.Variable([[0.8, 0.4, 0.5, 0.6],[0.1, 0.9, 0.2, 0.4],[0.1, 0.9, 0.4, 0.2]])
      B = tf.Variable([1, 1, 2])
      result = tf.nn.in_top_k(A, B, 2)
      with tf.Session() as sess:
      sess.run(tf.global_variables_initializer())
      print(sess.run(A))
      print(sess.run(B))
      print(sess.run(result))

      k=1 [False True False]

      k=2 [False True True]

      '''
      解釋:
      k取1的時候:
      由於A中第一個元素的最大值爲0.8,索引(序號)是0,而B是1,不包含B,因此返回False.
      A中第二個元素的最大值爲0.9,索引(序號)是1,而B是1,包含B,因此返回True.
      A中第三個元素的最大值爲0.9,索引(序號)是1,而B是2,不包含B,因此返回False.
      k取2的時候:
      由於A中前兩個元素的最大值爲0.8,0.6,索引(序號)是0,3,而B是1,不包含B,因此返回False.
      A中前兩個元素的最大值爲0.9,0.4,索引(序號)是1,3,而B是1,包含B,因此返回True.
      A中前兩個元素的最大值爲0.9,0.4,索引(序號)是1,2,而B是2,包含B,因此返回True.
      '''

案例:TensorFlow1實現全鏈接神經網絡識別手勢數據集

"""
一天下午,咱們和一些朋友決定教咱們的電腦破譯手語。咱們花了幾個小時在白色的牆壁前拍照,因而就有了瞭如下數據集。如今,你的任務是創建一個算法,使有語音障礙的人與不懂手語的人交流。
訓練集:有從0到5的數字的1080張圖片(64x64像素),每一個數字擁有180張圖片。
測試集:有從0到5的數字的120張圖片(64x64像素),每一個數字擁有5張圖片。
須要注意的是這是完整數據集的一個子集,完整的數據集包含更多的符號。
下面是每一個數字的樣本,以及咱們如何表示標籤的解釋。這些都是原始圖片,咱們實際上用的是64 * 64像素的圖片。
"""
import numpy as np
import h5py
import matplotlib.pyplot as plt
import time
import math
import tensorflow as tf
# %matplotlib inline #若是你使用的是jupyter notebook取消註釋
np.random.seed(1)

class GestureSymbolRecognition(object):
    def __init__(self):
        self.learning_rate = 0.0001
        self.num_epochs = 1500
        self.minibatch_size = 32
        self.regularazion_rate = 0.001

        self.n_inputs = 12288
        self.n_hidden1 = 100
        self.n_hidden2 = 100
        self.n_hidden3 = 100
        self.n_outputs = 6

        self.batch_norm_momentum = 0.9

    def load_data(self):
        """
        加載數據集
        :return:
        """
        # 從文件中讀取訓練集數據
        train_dataset = h5py.File('datasets/train_signs.h5', "r")
        # 從訓練集數據中提取特徵值與標籤值數據
        train_set_x_orig = np.array(train_dataset["train_set_x"][:])
        train_set_y_orig = np.array(train_dataset["train_set_y"][:])

        # 從文件中讀取測試集數據
        test_dataset = h5py.File('datasets/test_signs.h5', "r")
        # 從測試集數據中提取特徵值與標籤值數據
        test_set_x_orig = np.array(test_dataset["test_set_x"][:])
        test_set_y_orig = np.array(test_dataset["test_set_y"][:])
        classes = np.array(test_dataset["list_classes"][:])  # 類別列表
        classes_num = len(classes)  # 數據集標籤分爲幾類

        train_set_y_orig = train_set_y_orig.reshape((1, train_set_y_orig.shape[0]))
        test_set_y_orig = test_set_y_orig.reshape((1, test_set_y_orig.shape[0]))

        X_train_flatten = train_set_x_orig.reshape(train_set_x_orig.shape[0], -1)  # 每一列就是一個樣本
        X_test_flatten = test_set_x_orig.reshape(test_set_x_orig.shape[0], -1)

        # 歸一化數據
        X_train = X_train_flatten / 255
        X_test = X_test_flatten / 255

        # 轉換爲one-hot編碼矩陣
        Y_train = self.convert_to_one_hot(train_set_y_orig, len(classes)).T
        Y_test = self.convert_to_one_hot(test_set_y_orig, len(classes)).T

        return X_train, Y_train, X_test, Y_test, classes_num

    def convert_to_one_hot(self, Y, C):
        """
        實現one-hot編碼
        :param Y:標籤矩陣
        :param C:分類數量
        :return:one_hot:one-hot矩陣
        """
        one_hot = np.eye(C)[Y.reshape(-1)].T
        return one_hot

    def random_mini_batches(self, X, Y, mini_batch_size, seed=0):
        """
        從數據集中建立一個隨機的mini-batch列表
        :param X: 輸入數據,維度爲(輸入節點數量,樣本數量)
        :param Y: 對應的目標值,維度爲(1,樣本數量)
        :param mini_batch_size: 每一個mini-batch的樣本數量
        :param seed: 隨機數種子
        :return: mini_batches:一個同步列表,維度爲(mini_batch_X, mini_batch_Y)
        """
        # 指定隨機數種子
        np.random.seed(seed)
        # 獲取樣本數
        m = X.shape[0]
        # print('樣本數量',m)
        # 建立一個同步列表
        mini_batches = []

        # 第一步:打亂樣本的順序
        permutation = list(np.random.permutation(m))  # 返回一個長度爲m的隨機數組,且裏面的數是0到m-1
        shuffled_X = X[permutation, :]  # 將每一列特徵值數據按permutation的順序來從新排列
        shuffled_Y = Y[permutation, :]  # .reshape((Y.shape[0], m))  # 將每一列的目標值數據按permutation的順序來從新排列

        # 第二步:分割訓練集數據
        num_complete_minibatches = math.floor(m / mini_batch_size)  # 迭代完成整個訓練集的次數
        for k in range(0, num_complete_minibatches):
            mini_batch_X = shuffled_X[k * mini_batch_size:(k + 1) * mini_batch_size, :]
            mini_batch_Y = shuffled_Y[k * mini_batch_size:(k + 1) * mini_batch_size, :]
            mini_batch = (mini_batch_X, mini_batch_Y)
            mini_batches.append(mini_batch)

        # 若是訓練集的大小不是mini_batch_size的整數倍,那麼最後確定會剩下一些,取剩餘的數據
        if m % mini_batch_size != 0:
            # 獲取剩餘部分的數據
            mini_batch_X = shuffled_X[mini_batch_size * num_complete_minibatches:, :]
            mini_batch_Y = shuffled_Y[mini_batch_size * num_complete_minibatches:, :]
            mini_batch = (mini_batch_X, mini_batch_Y)
            mini_batches.append(mini_batch)

        # 返回同步列表
        return mini_batches

    def train(self, print_cost=True, is_plot=True):
        """
        實現一個三層的TensorFlow神經網絡訓練邏輯:LINEAR->RELU->LINEAR->RELU->LINEAR->SOFTMAX
        參數:
            print_cost - 是否打印成本,每100代打印一次
            is_plot - 是否繪製曲線圖

        """
        # 加載數據集
        x_train, y_train, x_test, y_test, label_classes_num = self.load_data()
        print("訓練集樣本數 = " + str(x_train.shape[0]))
        print("測試集樣本數 = " + str(x_test.shape[0]))
        print("X_train.shape: " + str(x_train.shape))
        print("Y_train.shape: " + str(y_train.shape))
        print("X_test.shape: " + str(x_test.shape))
        print("Y_test.shape: " + str(y_test.shape))
        # 設置圖級別的隨機數種子
        tf.set_random_seed(1)
        # 設置操做級別的隨機數種子
        seed = 3
        # 獲取輸入節點數量和樣本數
        (m, n_x) = x_train.shape
        # 成本函數值列表
        costs = []

        # 給X和Y建立佔位符節點
        X = tf.placeholder(tf.float32, shape=(None, self.n_inputs), name="X")
        Y = tf.placeholder(tf.int32, shape=(None, label_classes_num), name="y")

        # 定義神經網絡全鏈接層
        with tf.name_scope("dnn"):

            # 使用He權重初始化方法初始化權重
            he_init = tf.variance_scaling_initializer(mode="fan_avg")
            # 定義全鏈接層,使用elu激活函數
            hidden1 = tf.layers.dense(X, self.n_hidden1, name="hidden1",
                                      kernel_initializer=he_init,
                                      activation=tf.nn.elu, )
            hidden2 = tf.layers.dense(hidden1, self.n_hidden2, name="hidden2",
                                      kernel_initializer=he_init,
                                      activation=tf.nn.elu)
            hidden3 = tf.layers.dense(hidden2, self.n_hidden3, name="hidden3",
                                      kernel_initializer=he_init,
                                      activation=tf.nn.elu)
            outputs = tf.layers.dense(hidden3, self.n_outputs, name="outputs")
            # 輸出預測的類別
            y_proba = tf.nn.softmax(outputs)

        # 定義損失函數計算損失
        with tf.name_scope('loss'):
            # 前向傳播要在Z3處中止,由於在TensorFlow中最後的線性輸出層的輸出做爲計算損失函數的輸入,因此不須要A3.
            xentropy = tf.nn.softmax_cross_entropy_with_logits(labels=Y,
                                                               logits=outputs)
            loss = tf.reduce_mean(xentropy, name="loss")

        # 定義優化器
        with tf.name_scope("train_optimizer"):
            optimizer = tf.train.AdamOptimizer(self.learning_rate)
            training_op = optimizer.minimize(loss)

            # # 使用梯度裁剪
            # threshold = 1.0  # 設置梯度閾值
            # # 定義優化器
            # optimizer = tf.train.AdamOptimizer(self.learning_rate)
            # # 調用優化器的compute_gradients()方法計算梯度
            # grads_and_vars = optimizer.compute_gradients(loss)
            # # 使用tf.clip_by_value()函數建立一個裁剪梯度的操做
            # capped_gvs = [(tf.clip_by_value(grad, -threshold, threshold), var) for grad, var in grads_and_vars]
            # # 建立一個節點使用優化器的apply_gradients()函數應用裁剪的梯度
            # training_op = optimizer.apply_gradients(capped_gvs)

            # 評估模型,使用準確性做爲咱們的績效指標
        with tf.name_scope("eval"):
            # 計算當前的預測結果
            # 檢測使用了滑動平靜模型的神經網絡前向傳播是否正確。tf.argmax(average_y,1)
            # 計算每個樣例的預測答案。其中average_y是一個batch_size*10的二維數組,每
            # 一行表示案例向前傳播的結果。tf.argmax的第二個參數爲1,表示選取最大值的
            # 操做只在第一個維度上進行(x軸上),也就是說只在每一行選取最大值對應的下標
            # 因而獲得的結果是一個長度爲batch的一維數組,這個一維數組中的值就表示了每
            # 一個數字對應的樣例識別的結果.tf.equal()判斷每一個Tensor的每一維度是否相同
            # 若是相等返回True,不然返回False.
            correct_prediction = tf.equal(tf.argmax(y_proba, 1), tf.argmax(Y, 1))

            # 計算準確率
            # 這個運算首先將一個布爾型的值轉換爲實數型,而後計算平均值。這一個平均值
            # 就表明模型在這一組數據上的正確率
            accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))

        # 建立初始化全部變量的節點
        init = tf.global_variables_initializer()

        # 建立一個保存模型的saveOP,設置要保留的最近檢測點的模型的數量
        saver = tf.train.Saver(max_to_keep=1)

        # 開始會話並計算
        with tf.Session() as sess:
            # 初始化全部變量
            sess.run(init)

            # 加載模型,從模型中找出與當前訓練的模型代碼當中(名字同樣的OP操做),覆蓋原來的值
            checkpoin = tf.train.latest_checkpoint('./ckpt/')
            # 若是模型已經保存則加載模型
            if checkpoin:
                saver.restore(sess, checkpoin)
                # 計算模型預測的準確率
                train_acc = accuracy.eval({X: x_train[100:200], Y: y_train[100:200]})
                test_acc = accuracy.eval({X: x_test[100:200], Y: y_test[100:200]})
                print("訓練集的準確率:", train_acc)
                print("測試集的準確率:", test_acc)
            else:  # 模型未保存,則開始訓練模型
                # 正常訓練模型
                for epoch in range(self.num_epochs):

                    epoch_cost = 0  # 每輪的成本函數值
                    num_minibatches = math.floor(m / self.minibatch_size)  # 分紅多少個mini-batch組
                    seed = seed + 1
                    minibatches = self.random_mini_batches(x_train, y_train, self.minibatch_size, seed)

                    for minibatch in minibatches:
                        # 選擇一個minibatch
                        (minibatch_X, minibatch_Y) = minibatch

                        # 數據已經準備好了,開始運行session
                        _, minibatch_cost = sess.run([training_op, loss], feed_dict={X: minibatch_X, Y: minibatch_Y})
                        # 計算這個minibatch在這一代中所佔的偏差
                        epoch_cost = epoch_cost + minibatch_cost / num_minibatches

                    if epoch % 5 == 0:
                        # 將每輪迭代的代價函數值存入列表
                        costs.append(epoch_cost)
                        # 打印每輪迭代的代價函數值:
                        if print_cost and epoch % 100 == 0:
                            print("epoch = " + str(epoch) + "    epoch_cost = " + str(epoch_cost))
                # 保存學習後的參數
                saver.save(sess, './ckpt/my_test1_model.ckpt')

                # 繪製代價函數值的學習曲線
                if is_plot:
                    plt.plot(np.squeeze(costs))
                    plt.ylabel('cost')
                    plt.xlabel('iterations (per tens)')
                    plt.title("Learning rate =" + str(self.learning_rate))
                    plt.show()

                # 計算模型學習完成後的訓練集及測試集準確率

                train_acc = accuracy.eval({X: x_train, Y: y_train})
                test_acc = accuracy.eval({X: x_test, Y: y_test})
                print("訓練集的準確率:", train_acc)
                print("測試集的準確率:", test_acc)

if __name__ == '__main__':
    # 開始時間
    start_time = time.clock()
    # 開始訓練
    nn = GestureSymbolRecognition()
    nn.train()
    # 結束時間
    end_time = time.clock()
    # 計算時差
    print("CPU的執行時間 = " + str(end_time - start_time) + " 秒")
相關文章
相關標籤/搜索