PaddleFluid和TensorFlow基本使用概念对比 | PaddlePaddle专栏
專欄介紹:Paddle Fluid 是用來讓用戶像 PyTorch 和 Tensorflow Eager Execution 一樣執行程序。在這些系統中,不再有模型這個概念,應用也不再包含一個用于描述 Operator 圖或者一系列層的符號描述,而是像通用程序那樣描述訓練或者預測的過程。
本專欄將推出一系列技術文章,從框架的概念、使用上對比分析 TensorFlow 和 Paddle Fluid,為對 PaddlePaddle 感興趣的同學提供一些指導。今天將推出系列文章第一期 Paddle Fluid 設計思想和基本概念。
深度學習平臺的演化
時至今日,深度學習已成為事實上最流行的機器學習技術。學術界多年研究加上工業界的長期實踐提出了若干有效的基本建模單元:全連接,卷積,循環神經網絡等;設計各類訓練技巧:初始化方法,跨層連接,各類 norm 技術等;發明了各種新的優化算法:Adadelta,Adam 等;各類固定的網絡結構:highway, residual, attention 等紛紛涌現,不勝枚舉。學術界工業界多年的付出共同促成了深度學習方法今日的影響力。?
學術研究和生產實踐中積累了大量的知識,能夠很好的解釋神經網絡中基本模塊各自獨的學習能力和特性?;灸K和訓練技術的組合能夠搭建出千變萬化的神經網絡模型。基本模塊和訓練技術是有限的,但他們的組合卻是千變萬化,這是深度學習方法的魅力所在,也是難度所在。
正是這樣高度的模塊化特性,研究者和工程師們都在努力避免重復造輪子以提高研究和生產的效率,又進一步催生了深度學習平臺技術的發展,深度學習框架已演變成為 AI 基礎設施中重要的一部分。從 Theano,到 DistBelief,到 TensorFlow;從 Caffe 到 Caffe2;從 Torch 到 PyTorch;從 PaddlePaddle 到 PaddleFluid,深度學習平臺技術也經歷了兩代的演化,并向著第三代平臺技術邁進。?
站在歷史發展的今天,當我們準備切換嘗試使用一個新的深度學習平臺作為支持自己學習和研究的工具時,平臺技術都發生了哪些演化,能夠為我們的帶來什么便利呢?先讓我們來看看深度學習框架解決的三大問題:?
如何描述計算以支持未來潛在會出現的新模型??
如何高效利用異構設備最大化算力??
如何利用網絡中的計算機進行分布式計算來處理千萬億級別的數據??
以上三個問題中的第一個和使用者研究者最為密切相關。這篇文章我們通過分析 PaddleFluid 和 TensorFlow 的不同設計理念,來了解一個深度學習框架如何抽象深度學習模型,來看看我們的使用經驗如何在不同深度學習平臺之間過度和遷移。
如何描述計算
讓我們首先來看看 PaddleFluid 和 TensorFlow 在“如何描述機器學習模型”這一問題上各自的選擇。
TensorFlow之?Computation Graph?
TensorFlow 使用數據流圖(Dataflow Graph)來描述機器學習模型中的涉及到的所有計算(computation)和狀態(state)。一個 TensorFlow 模型只有一個計算圖,計算圖中包括數學運算和運算的對象(參數),甚至也包括:參數的初始化、優化算法部分(對可學習參數的更新規則),以及數據預處理等。?
這樣的一個計算圖可以更進一步解釋:?
一個機器學習模型,在?TensorFlow?中用一個有向無環圖表示;
圖中的結點對應了機器學習模型中的某個具體運算,在 TensorFlow 中稱之為:Operation;
圖中的邊是 Operation 之間的輸入輸出數據流動。
在 TenorFlow 中,Operation 的輸入輸出統一用 Tensor 表示,這里可以簡單地理解為 Tensor 構成了計算圖中的邊。?
總結之:?
1. 在這一篇中,我們暫不考慮 TensorFlow 在分布式、異構計算方面的設計,TensorFlow 使用計算圖(一個有向無環圖)來描述機器學習模型,任何模型的定義和優化過程將被轉換為一個計算圖。計算圖中的結點是 Operation,表示如何計算;計算圖中的邊是 Operation 之間的輸入輸出數據流動。在 TensorFlow 中用 Tensor 表示數據;?
2. TensorFlow 的計算圖遵循:先定義再執行的原則(deferred execution),也就是一個計算圖(這里可以理解為代表了神經網絡的網絡拓撲)需要預先聲明,一旦聲明,運行時無法改變其結構。
PaddleFluid之?Program?
如何描述計算很大程度決定了一個神經網絡框架計算功能的完備性。深度學習模型和方法歷經二十多年的發展:“依次執行一組計算的前向,再以和前向計算相反的順序執行反向計算,中間無分支無交互”,這樣的模型結構已經無法滿足研究者和千千萬萬框架使用者的想象力。
從 PaddleFluid 的設計目標 [1] 來看,在如何描述機器學習模型這一核心問題上,PaddleFluid 的目標是:創造一種新的計算描述方式,不但能夠描述至今為止人們已知的主流神經網絡模型,并且能夠支持未來會出現的任意模型。
PaddleFluid 是如何做到支持未來出現的新模型這一目標呢?PaddleFluid 的設計選擇是:對用戶來說,用一段 Program?(在 PaddleFluid 內部會被轉化為一種叫作 ProgramDesc 的描述語言),而不是用計算圖來描述機器學習模型。?Program 用符合用戶使用直覺的方式,提供一種新的描述語言能夠描述任意復雜的機器學習模型。
對所有計算機專業同學學習編程語言的第一課一定是建立對“程序語言的三種執行結構:順序執行,條件選擇和循環執行”的認識。計算機世界的所有可計算邏輯都是由這三種執行結構表示,用這三種結構描述的邏輯是可計算的。那么同樣道理,對一個神經網絡框架來說,如果可以和程序語言一樣提供對這三種執行結構的支持,那么將可以描述任意復雜的,可被計算機計算的,機器學習模型。PaddleFluid通過提供對這三種執行結構的支持,來做到對任意復雜模型的描述。
具體來說:?
1. Fluid 的核心設計理念都可以類比到程序語言,如果已經有寫程序的經驗,那么使用 Fluid 構建神經網絡模型的體驗,將非常接近寫程序;
2. 在 PaddleFluid 中,用戶不會顯示地感知“計算圖”這樣的概念,一個機器學習模型被描述為一個 Fluid Program?(Fluid 內部稱之為 ProgramDesc?);
一個 Fluid Program 由一組嵌套的 Block 構成。?Block 的概念可以類比到 C++ 或是 Java 中的一對大括號,或是 Python 語言中的一個縮進快;
?Block 中的計算由順序執行、條件選擇或者循環執行三種方式組合,構成復雜的計算邏輯。
3. Fluid Program 中包含對計算和計算對象的描述。計算的描述稱之為 Operator;計算作用的對象(或者說 Operator 的輸入和輸出)被統一為 Tensor。?
在描述計算和計算的作用對象這一問題上,各個深度學習框架的選擇是相同的,如果有一個平臺的使用經驗,那么將非常容易在各個平臺之間進行遷移。
總結
下面的表格總結了 TensorFlow 和 PaddleFluid 在描述機器學習模型上的不同設計選擇。可以看到,Operator和Tensor這些構成模型的基礎元素在兩個平臺中是相似的。如果有任一個平臺的使用經驗,可以非常快速的將這些概念在不同平臺之間類比推廣。
核心使用概念
下面的小節,我們將更詳細地了解核心使用概念在兩個平臺的使用方法。?
數據表示和計算的對象:Tensor?
?Tensor 是向量矩陣概念的擴展,是神經網絡模型計算操作的基本對象。這在是今天所有主流深度學習平臺的共同選擇。?
可以簡單地將 Tensor 理解為一個 N 維向量,它可以有任意多的維度。一個 Tensor 具有兩個基本特征:?
1. 數據類型:每個 Tensor 的所有元素具有同樣的、已知的數據類型;
2. 大小(或者說形狀):即維度的個數(rank,階)以及各維度的長度。?
Tensor 某些維度的長度在定義模型階段可能是未知的,在實際算法執行時才能確定。例如一個 mini-batch 中包含的樣本數目(batch size),或者是一個 mini-batch 中序列的最大長度。
TensorFlow中的Tensor
TensorFlow 的內部實現中, Tensor 的存儲方式就是一個 N 維數組,其中每個元素都是一個具體的數值,例如整形、浮點等。如“TensorFlow”這個名字所表達的, Tensor 就是 TensorFlow 中“被運算”的對象。在一個算法的執行中,Operation 輸入是 Tensor,經過運算的中間結果是 Tensor,最終結果也是 Tensor。
在 TensorFlow 中,有一些特殊的 Tensor,其中比較常見的有:
1.??tf.Variable?(變量): Variable 用于表示機器學習算法中的參數,具有全局可見性。和一般的 Tensor 相比, Variable 不受 Session (Session 的概念下文會詳細解釋)的約束,因此在分布式計算的情況下,多個計算單元可以看到同一個 Varible?;
2.? tf.placeholder?: placeholder 類型的 Tensor 在執行時必須接入具體的數據,通常用于引入輸入數據;
3.? tf.constant :常量 Tensor,用于生成常用的常量數據,例如全零、全 1 等。
PaddleFluid中的Tensor
PaddleFluid?中也使用 Tensor 作為神經網絡中輸入輸出數據的統一表示。Tensor 的概念在今天主流的深度學習平臺中都是完全相同,可以在各個深度學習框架之間直接無縫遷移。
在 Fluid 中也同樣存在三種特殊的 Tensor:
1. 模型中的可學習參數
模型中的可學習參數生存期和整個訓練任務一樣長,會接受優化算法的更新。在 PaddleFluid 中同樣以 Variable 表示;
用戶在絕大多數情況下都不需要自己來創建網絡中的可學習參數,Fluid 為幾乎常見的神經網絡基本計算模塊都提供了封裝。以最簡單的全連接模型為例,下面的代碼片段會直接為全連接層創建連接權值 WW 和偏置( bias )兩個可學習參數,無需顯示地調用 variable 相關接口創建可學習參數。
y?=?fluid.layers.fc(input=x,?size=128,?bias_attr=True)
2. 輸入輸出Tensor
整個神經網絡的輸入數據也是一個特殊的 Tensor,在這個 Tensor 中,一些維度的大小在定義模型時無法確定(通常包括:batch size;如過 mini-batch 之間,數據可變,也會包括序列的最大長度,圖片的寬度和高度等),在定義模型時需要占位;
PaddleFluid 中使用 fluid.layers.data 來接入輸入數據, fluid.layer.data 需要提供輸入 Tensor 的 形狀信息,當遇到無法確定的維度 時, 相應維度指定為 None ,如下面的代碼片段所示:?
x?=?fluid.layers.data(name="x",?shape=[2,?None,?3],?dtype="int64")
3. 常量 Tensor 在 PaddleFluid 中需要通過組合 Tensor 和 fluid.layers.assign?來實現。
總結?
1. 在 TensorFlow 和 PaddleFluid 中都統一使用 Tensor 描述神經網絡的輸入輸出以及中間結算結果;
2. 對可學習參數這一類特殊的 Tensor:?
在 TensorFlow 中,可學習參數用?tf.Variable (假設這里已經執行 import tensorflow as tf )表示;
在 Fluid 中可學習參數使用 fluid.Variable (假設這里已經執行 import paddle.fluid as fluid )表示;
不論是使用 TensorFlow 還是 PaddleFluid,通常都可以直接使用較高層次的 API,其中已經封裝了幾乎所有常見神經網絡單元,例如全連接、LSTM、CNN 等,這些封裝中都已經為用戶正確的創建了該模塊所需的可學習參數。通常不需要自己來創建可學習參數。
3. 對輸入這一類特殊的 Tensor:?
TensorFlow 中用 tf.placeholder 完成占位功能;
對用戶來說,邏輯上可認為等價于 PaddleFluid 中的 fluid.layers.data ;
但需注意,框架內部的實現機制不盡相同。 tf.placeholder 是一個 Tensor,而?pd.layers.data 創建輸出 Tensor 的同時,還創建了 Feed 數據相關的 operator。
計算原語:Operation/Operator
Tensor 是今天所有主流深度學習框架的統一數據表示(輸入、輸出、中間計算結果、模型的可學習參數都是 Tensor)。另一方面,對數據的操作,在主流深度學習框架中也高度統一為:Operator/Operation。在中文中,通常我們會習慣將其稱之為算子。
注:在 TensorFlow 的官方文檔中,使用 Operation 來稱呼對 Tensor 的操作和變化,而在 PaddleFluid 中使用 Operator 稱呼對 Tensor 的操作,這兩者沒有本質區別。下文將交叉使用兩者,但他們實際上是同一概念。
Operation/Operator 接受多個 Tensor 作為輸入,輸出若干個 Tensor,表示了從輸入到輸出的變化。
TensorFlow中的Operation?
一個 Operation,接受若干個 Tensor 作為輸入,輸出若干個 Tensor ??梢钥闯?#xff0c; Operator 作為圖的結點,從進入該結點的邊(tensor)獲得數據并完成計算,然后結果的 Tensor 作為從該結點出發的邊。一個典型的 Operator 是 tf.matmul ,它接受兩個 Tensor 輸入,將二者相乘,并輸出一個 Tensor 作為結果。TensorFlow 提供的所有算子,可以在 API 幫助文檔 [2] 中查看。?
PaddleFluid中的Operator?
PaddleFluid 中的 Operator 完全等價于 TensorFlow 中的 operation。PaddleFluid 支持的所有算子,可以在 API 幫助文檔 [3] 中查看。?
為了便于用戶使用,在 Python 端,Fluid 中的 Operator 被進一步封裝入 paddle.fluid.layers , paddle.fluid.networks 等模塊。這是因為:一些常見的對Tensor的操作可能是有更多基礎操作構成,例如:l2 norm 內部由 reduce、elementwise_add,scale 等多個 Operator 組合計算邏輯完成,為了提高使用的便利性,框架內部對基礎 Operator 進行了一些封裝,包括創建 Operator 依賴可學習參數,可學習參數的初始化細節等,減少用戶重復開發的成本。?
對所有深度學習框架都面臨同樣的封裝,在絕大多數情況下,用戶很少會直接與框架底層的 Operator 直接打交道,而是使用框架提供的 layers,networks 等模塊,降低開發的代碼量。不論是什么樣的概念,他們在各個礦建之間的本質和作用都是相同的:對 Tensor 的變換。?
總結?
不論叫作 Operation、Operator 還是 layers,他們在各深度學習平臺中的含義和作用都是相同的:對 Tensor 的變換。是一個深度學習平臺提供的基礎計算能力。可以在每個平臺各自的 API 幫助文檔中查到。?
在各個深度學習平臺都已加入 ONNX 項目的今天,每個深度學習平臺提供給大家的基本算子都已趨同,與此同時,每個平臺也各有其特點,會提供一些獨特的算子,方便某一類任務的開發。
構建模型并執行?
至此,我們看到了構成模型的基礎要素:Tensor 和 Operator 在兩個框架之間能夠直接遷移。最后一步,我們 看看在兩個平臺之上,整個訓練任務是如何運行起來的。?
TensorFlow中的Graph和Session?
1. TensorFlow 以計算圖描述機器學習模型,圖中的結點是 Operation,邊是 Tensor。在 TensorFlow 中,tf.Graph 維護了整個圖的拓撲信息;
對于 graph,需要額外注意一點:TensorFlow 的一個計算圖,會有一個 collection 的概念與之對應。 collection 是以圖為上下文的 key-value 表,用于維護圖級別的(也就是全局的)數據。例如 一個 variable 可以設定為是全局可見,此時這個 variable 相關的所有信息會在計算圖對應的 collection 中進行維護。
2. 在圖“之上”,TensorFlow 利用 session 機制來實際執行模型。?
對于一個定義好的 TensorFlow 的 Graph (也就是一個定義好的神經網絡模型),為它創建一個 tf.Session 就可以對這個模型執行初始化、運行等流程層面的操作;
更精確地說, TensorFlow 的 Session 連接了用戶程序和后端 Runtime,這里所說的“用戶程序”就是 TensorFlow 的使用者對機器學習模型的定義和設置等,而后端 Runtime 是實際完成算法訓練、測試等真實計算任務的程序。這種“連接”也是一種“隔離”,將使用者和真實計算時涉及的分布式計算等細節隔離,便于使用。
Fluid中的Program和Executor?
1. PaddleFluid 使用 Program 描述神經網絡模型,對用戶來說,并沒有計算圖的概念。用戶定義的所有 Tensor 以及對 Tensor 的操作:Operator 都會被加入一段 Program 中;
一段 Program 由嵌套的 Block 構成,但用戶無需顯示地創建 Block 或是顯示地注意到 Block 的存在;
在 PaddleFluid 程序中, Block 是在調用 while_op , if_op , parallel_do 等特殊 Operator 時,由這些 Operator 來創建;
對用戶使用來說,只需要知道自己正在向一段 Fluid Program 中添加變量(Tensor)和操作(Operator)即可。
2. PaddleFluid 利用 Executor 來執行一段 Fluid Program。?
為進一步理解 Fluid 中 Executor 的作用,需要先解釋一下 Fluid 程序的執行流程。 下圖展示單機上,Fluid 程序的執行流程:
▲ Fig. Fluid本地訓練任務執行流程圖
1. Fluid 設計思想和靈感非常類似于程序設計語言,和高級編譯語言 C++/Java 編寫程序的過程非常類似,Fluid 程序執行分為兩個重要階段:編譯時和運行時;
2. 編譯期,用戶通過調用 Fluid 提供的算子,向一段 Program 中添加變量(Tensor)以及對變量的操作(Operators 或者 Layers)。用戶只需要描述核心的前向計算,不需要關心反向計算,分布式下,異構設備下如何計算;
3. 原始的 Program 在平臺內部轉換為中間描述語言: ProgramDesc ;
4. 編譯期最重要的一個功能模塊是 Transpiler。Transpiler 接受一段 ProgramDesc ,輸出一段變化后的 ProgramDesc ,作為后端 Executor 最終需要執行的 Fluid Program ;
最為常用的 Transipler 包括:
1. 內存優化 Transipler:通過對變量讀寫依賴關系分析,插入內存回收 Operator 以維持運行過程中較小的內存開銷;?
2. 分布式環境下的 Transpiler:接受用戶定義的 local Program ,生成 Parameter Client 和 Parameter Server 執行的兩段 Program 。?
5. 后端 Executor 接受 Transpiler 輸出的這段 Program ,依次執行其中的 Operator(可以類比為程序語言中的指令),在執行過程中會為 Operator 創建所需的輸入輸出并進行管理。?
從上面的過程中可以看到,Fluid 程序的執行過程分為:編譯器的定義 Program ,和創建 Executor 運行 Program 。 Executor 執行一段 Program 的過程是不可交互和不可中斷的。在 PaddleFluid 中,可以創建多余一段 Program 。默認情況,一個 PaddleFluid 程序中存在 2 段 Program:
1. fluid.framework.default_startup_program :其中定義了創建模型參數,輸入輸出,以及模型中可學習參數的初始化等各種操作;
?default_startup_program 可以由框架自動生成,使用時無需顯示地創建;
如果調用修改了參數的默認初始化方式,框架會自動的將相關的修改加入 default_startup_program 。
2.? fluid.framework.default_main_program :定義了神經網絡模型,前向反向計算,以及優化算法對網絡中可學習參數的更新;
使用 Fluid 的核心就是構建起 default_main_program 。
3. PaddleFluid 中的 Scope 類似于 TensorFlow 中的 collection 這一概念,但在 Fluid 中 Scope 是框架后端概念,用戶無法直接操作。因此,在使用框架時無需關心。
總結?
對使用框架的用戶來說,可以認為 TensorFlow 中的 Graph 等價于 PaddleFluid 中的 Program,他們在框架中的作用完全相同:完成了對模型的定義。?
TensorFlow 中的 Session 在使用邏輯上非常類似于 PaddleFluid 中的 Executor。?
TensorFlow 通過 Session 來完成計算圖上的初始化,計算等運行邏輯,連接了 TensorFlow 的前端和后端;
PaddleFluid 中通過 Executor 來執行一段用戶定義的 Fluid Program 。
1. Executor 連接了 PaddleFluid 的前端和后端;
2. Executor 接受用戶定義的原始模型(一段 Program ),通過調用系統中不同功能更的 Transpiler 完成對原始 Program 的變化,進行優化。
完整實例:如何完成一個機器學習模型的訓練
這一節,我們以 MNIST 手寫數字識別問題 —— 機器學習任務的“Hello World”問題和數據,為例,通過一個可以運行的完整實例,來學習上文介紹的概念如何在 TensorFlow 和 PaddleFluid 平臺下如何使用和互相遷移。?
TensorFlow實例?
以下使用 Tensorflow 定義一個基本的 MLP(單隱層的神經網絡)對該問題建模,以說明 Tensorflow 的基本使用方法。?
步驟1:定義數據
x?=?tf.placeholder(tf.float32,?shape=[None,?784])
y_?=?tf.placeholder(tf.int32,?shape=[None,])
如前所述, tf.placeholder 是用于引入數據的特殊 Tensor,這里分別用 x 和 y_ 代表數據的特征和標簽。
步驟2:定義模型
y?=?tf.layers.dense(inputs=x,?units=10)
#?operation的loss計算方式指定
cross_entropy?=?tf.losses.sparse_softmax_cross_entropy(labels=y_,?logits=y)
#?operation優化方式指定
train_op?=?tf.train.AdamOptimizer().minimize(cross_entropy)
這段程序分為三個部分:?
1. 參數定義:一個單隱層的 MLP,按照 Tensorflow 的計算圖抽象方式,即對于輸入 xx,經過 y=\omega x+by=ωx+b 這步計算,得到輸出 yy。其中 xx、yy 是輸入和輸出 tensor,\omegaω 和 bb 是參數 tensor。?
Tensorflow 的 tf.layers 提供了常用的 operation 計算邏輯,這里用到的 tf.layers.dense 即神經網絡中全連接層的計算。?
2. loss計算方式定義:loss 在模型訓練的過程中,用于衡量當前模型產出的結果和目標之間的差距,也是優化算法迭代模型的依據。 tf.losses 中定義了常用的 loss,這里使用交叉熵(cross entropy),一種多分類情況下常用的 loss。 這里參數中 y_ 指的是目標標簽,在上面數據引入的部分已經定義。
3. operation構建:在上面已經確定的參數和 loss 之外,還需要指定迭代時需要使用的優化算法。同樣的 operation 可以在執行時使用不同的優化算法。?
步驟3:參數初始化
sess?=?tf.Session()
sess.run(init)
模型的訓練過程,由 tf.Session 管理, tf.Session.run() 以初始化后參數后的 Graph 為輸入參數,做好模型訓練的準備工作。?
這里使用的是默認的參數初始化。實際上,神經網絡訓練的參數初始化是有多種選擇的,這超出了本篇的覆蓋范圍,暫不贅述將在后面章節詳細討論。?
步驟4:數據輸入 + 執行模型訓練
test_lbl,?test_img?=?load_MNIST("testing")
for?step?in?range(100):
????images_batch,?labels_batch?=?next(train_reader)
????_,?loss_val?=?sess.run(
????????[train_op,?cross_entropy],
????????feed_dict={
????????????x:?images_batch,
????????????y_:?labels_batch.astype("int32")
????????})
????print("Cur?Cost?:?%f"?%?loss_val)
所謂模型迭代,通常是以 batch 為單位向模型給入數據,之后根據指定的 loss 和優化方法更新模型參數。核心的函數是對 tf.Session.run() 的調用,其中包括之前定義好的 operation、優化方法以及給入的數據。
其中,給入數據的來源,是以下函數:
????batch_idx?=?0
????lbl,?img?=?load_MNIST(dataset,?path)
????while?True:
????????#?shuffle?labels?and?features
????????idxs?=?np.arange(0,?len(lbl))
????????np.random.shuffle(idxs)
????????shuf_features?=?img[idxs]
????????shuf_labels?=?lbl[idxs]
????????for?batch_idx?in?range(0,?len(lbl),?batch_size):
????????????images_batch?=?shuf_features[batch_idx:
?????????????????????????????????????????batch_idx?+?batch_size]?/?255.
????????????images_batch?=?images_batch.astype("float32")
????????????labels_batch?=?shuf_labels[batch_idx:
???????????????????????????????????????batch_idx?+?batch_size].astype("int32")
????????????yield?images_batch,?labels_batch
本段程序中用到的 tf_load_MNIST 是從文件中讀取數據。本段程序的作用是對數據做 shuffle,之后以 batch_size 為長度組織每個 batch 的數據。
步驟5:觀察模型效果
以上步驟已經構建了完整的 Tensorflow 模型訓練程序,每個 batch 觀察一次 loss,可以直觀看到模型的迭代效果:
▲?Fig. TensorFlow MNIST手寫數字識別任務代價下降曲線
附:完整代碼
import?tensorflow?as?tf
from?tf_load_MNIST?import?load_MNIST
def?data_iterator(dataset="training",?path="data",?batch_size=128):
????batch_idx?=?0
????lbl,?img?=?load_MNIST(dataset,?path)
????while?True:
????????#?shuffle?labels?and?features
????????idxs?=?np.arange(0,?len(lbl))
????????np.random.shuffle(idxs)
????????shuf_features?=?img[idxs]
????????shuf_labels?=?lbl[idxs]
????????for?batch_idx?in?range(0,?len(lbl),?batch_size):
????????????images_batch?=?shuf_features[batch_idx:
?????????????????????????????????????????batch_idx?+?batch_size]?/?255.
????????????images_batch?=?images_batch.astype("float32")
????????????labels_batch?=?shuf_labels[batch_idx:
???????????????????????????????????????batch_idx?+?batch_size].astype("int32")
????????????yield?images_batch,?labels_batch
def?main():
????#?define?the?network?topology.
????x?=?tf.placeholder(tf.float32,?shape=[None,?784])
????y_?=?tf.placeholder(
????????tf.int32,?shape=[
????????????None,
????????])
????y?=?tf.layers.dense(inputs=x,?units=10)
????cross_entropy?=?tf.losses.sparse_softmax_cross_entropy(labels=y_,?logits=y)
????train_op?=?tf.train.AdamOptimizer().minimize(cross_entropy)
????#?define?the?initializer.
????init?=?tf.global_variables_initializer()
????sess?=?tf.Session()
????sess.run(init)
????train_reader?=?data_iterator()
????for?step?in?range(100):
????????images_batch,?labels_batch?=?next(train_reader)
????????_,?loss_val?=?sess.run(
????????????[train_op,?cross_entropy],
????????????feed_dict={
????????????????x:?images_batch,
????????????????y_:?labels_batch.astype("int32")
????????????})
????????print("Cur?Cost?:?%f"?%?loss_val)
if?__name__?==?"__main__":
????main()
?tf_load_MNIST.py 完整代碼:
import?struct
import?numpy?as?np
def?load_MNIST(dataset="training",?path="."):
????"""
????Python?function?for?importing?the?MNIST?data?set.??It?returns?an?iterator
????of?2-tuples?with?the?first?element?being?the?label?and?the?second?element
????being?a?numpy.uint8?2D?array?of?pixel?data?for?the?given?image.
????"""
????path?=?os.path.join(os.path.abspath('.'),?"data")
????if?dataset?is?"training":
????????fname_img?=?os.path.join(path,?"train-images.idx3-ubyte")
????????fname_lbl?=?os.path.join(path,?"train-labels.idx1-ubyte")
????elif?dataset?is?"testing":
????????fname_img?=?os.path.join(path,?"t10k-images.idx3-ubyte")
????????fname_lbl?=?os.path.join(path,?"t10k-labels.idx1-ubyte")
????else:
????????raise?ValueError("dataset?must?be?'testing'?or?'training'")
????#?Load?everything?in?some?numpy?arrays
????with?open(fname_lbl,?"rb")?as?flbl:
????????magic,?num?=?struct.unpack(">II",?flbl.read(8))
????????lbl?=?np.fromfile(flbl,?dtype=np.int8)
????with?open(fname_img,?"rb")?as?fimg:
????????magic,?num,?rows,?cols?=?struct.unpack(">IIII",?fimg.read(16))
????????img?=?np.fromfile(fimg,?dtype=np.uint8).reshape(len(lbl),?rows?*?cols)
????return?lbl,?img
PaddleFluid實例
步驟1:定義數據
PaddleFluid 中以?fluid.layers.data?來接收輸入數據。
import?paddle.fluid?as?fluid
import?paddle.v2?as?paddle
#?define?the?input?layers?for?the?network.
x?=?fluid.layers.data(name="img",?shape=[1,?28,?28],?dtype="float32")
y_?=?fluid.layers.data(name="label",?shape=[1],?dtype="int64")
Fluid 中 Tensor 的第 0 維度固定為 batch size。在上面代碼段中,圖像輸入 x 的形狀為:[1, 28, 28]。這三個維度的含義分別是:channel 數目,圖像的高度和寬度。?
實際上 Fluid 框架內部,一幅圖像輸入是一個 4-D Tensor,所有 Tensor 的第 0 維固定為 batch size??蚣軆炔繒詣訛閎atch size進行填充占位。無需對batch size指定填充占位。?
如果除去 batch size(第 0 維度)外,如果 Tensor 某一維度的大小只能在運行時確定,可以在該位置上直接指定 None 進行占位。
步驟2:定義模型?
通過調用 Fluid 提供的算子定義含有一個隱層的神經網絡。Fluid 模型的分為模型結構和優化方法兩部分。這一點與 TensorFlow 程序十分相似似,使用概念可以直接對應進行遷移。
y?=?fluid.layers.fc(input=x,?size=10,?act="softmax")
loss?=?fluid.layers.cross_entropy(input=y,?label=y_)
avg_loss?=?fluid.layers.mean(loss)
#?define?the?optimization?algorithm.
optimizer?=?fluid.optimizer.Adam(learning_rate=1e-3)
optimizer.minimize(avg_loss)
Fluid 使用 Program 而不是計算圖描述模型,一般情況下,用戶無需關心 Program 的細節,當調用以上 layers 時,會向一個全局的 Program: fluid.framework.default_main_program 中插入變量(Tensor)和對變量的操作(上述代碼段中的 layers 和 optimzier)。?
步驟3:參數初始化?
如上文介紹,Fluid 程序中的 Executor 是連接 Fluid 前端和后端的接口。?
默認一個Fluid模型存在至少兩段 Program。用于初始化網絡中的可學習參數的那一段 Program 叫作 fluid.default_startup_program() 。
只有執行器 executor 可以執行 Fluid Program,因此,在初始化網絡中的可學習參數之前,需要首先創建一個 Fluid executor。
place?=?fluid.CPUPlace()
exe?=?fluid.Executor(place)
exe.run(fluid.default_startup_program())
在以上代碼段中, place 用于告訴 executor 一段 Fluid Program 在何種設備上執行,常見的有 fluid.CPUPlace() 和 fluid.CUDAPlace() 。?
步驟4:數據輸入 + 執行模型訓練?
我們在步驟 2 中定義的神經網絡模型最終被插入一段叫做 fluid.framework.default_main_program 的 Fluid Program 中。
網絡可學習參數初始化之后,可以通過讓執行器 Executor 執行這段 fluid.framework.default_main_program 來進行訓練。
????????paddle.reader.shuffle(paddle.dataset.mnist.train(),?buf_size=5000),
????????batch_size=BATCH_SIZE)
feeder?=?fluid.DataFeeder(place=place,?feed_list=[x,?y_])
for?pass_id?in?range(100):
????for?batch_id,?data?in?enumerate(train_reader()):
????????loss?=?exe.run(
????????????fluid.framework.default_main_program(),
????????????feed=feeder.feed(data),
????????????fetch_list=[avg_loss])
????????print("Cur?Cost?:?%f"?%?(np.array(loss[0])[0]))
從上面的代碼片段中可以看到,Fluid 程序的訓練過程和 TensorFlow 程序的訓練過程非常接近,都放在一個 for 循環中,循環讀取一個 mini-batch 數據,調用執行器執行 Fluid default_main_program :接收 mini-batch 輸入,在其上進行前向,反向和參數更新計算。?
注:上面程序使用了 Fluid 內置的 MNIST 數據,和我們提供給 TensorFlow 示例程序的 MNIST 數據完全一樣。?
步驟5:觀察模型效果?
以上步驟已經構成了完整的 Tensorflow 模型訓練程序,每個 batch 觀察一次 loss,可以直觀看到模型的迭代效果:
▲?Fig. Fluid MNIST手寫數字識別任務代價下降曲線
附:完整代碼
import?paddle.fluid?as?fluid
import?paddle.v2?as?paddle
def?main():
????BATCH_SIZE?=?128
????#?define?the?input?layers?for?the?network.
????x?=?fluid.layers.data(name="img",?shape=[1,?28,?28],?dtype="float32")
????y_?=?fluid.layers.data(name="label",?shape=[1],?dtype="int64")
????#?define?the?network?topology.
????y?=?fluid.layers.fc(input=x,?size=10,?act="softmax")
????loss?=?fluid.layers.cross_entropy(input=y,?label=y_)
????avg_loss?=?fluid.layers.mean(loss)
????optimizer?=?fluid.optimizer.Adam(learning_rate=5e-3)
????optimizer.minimize(avg_loss)
????#?define?the?executor.
????place?=?fluid.CPUPlace()
????exe?=?fluid.Executor(place)
????exe.run(fluid.default_startup_program())
????train_reader?=?paddle.batch(
????????paddle.reader.shuffle(paddle.dataset.mnist.train(),?buf_size=5000),
????????batch_size=BATCH_SIZE)
????feeder?=?fluid.DataFeeder(place=place,?feed_list=[x,?y_])
????for?pass_id?in?range(100):
????????for?batch_id,?data?in?enumerate(train_reader()):
????????????loss?=?exe.run(
????????????????fluid.framework.default_main_program(),
????????????????feed=feeder.feed(data),
????????????????fetch_list=[avg_loss])
????????????print("Cur?Cost?:?%f"?%?(np.array(loss[0])[0]))
if?__name__?==?"__main__":
????main()
總結
在這一節中,基于手寫數字識別數據集 MNIST,我們通過一個完整可運行的例子,展示了使用 TensorFlow 和 PaddleFluid 實現了同樣一個含有單隱層的全連接神經網絡。通過這個例子展示主流深度學習框架核心概念、用戶接口、使用體驗的設計選擇。
可以看到盡管內部實現有著非常大的差異,但是對用戶來講,深度學習模型的核心概念,包括:Tensor、Operation、Optimzier、網絡初始化等,在各個主流深度學習框架中都有著對應的實現。如果有著一個框架的使用經驗,這種使用經驗將非常容易遷移到其它深度學習框架下。
從迭代效果看,這一篇中這個簡單的模型依照預期擬合住了訓練數據,但是效果并不驚艷。原因在于:輸入數據是圖片像素值,這里的神經網絡模型十分簡單,擬合能力有限。在后面的篇幅,我們將會使用更加復雜和實用的例子,進一步對比如何不同深度學習平臺如何訓練同一個神經網絡,我們的使用經驗如何在不同框架之間進行切換和推廣,幫助我們選擇最適合的工具提高研究和生產的效率。
相關鏈接
[1]. PaddleFluid的設計目標
https://github.com/PaddlePaddle/Paddle/blob/develop/doc/fluid/design/motivation/fluid.md
[2].?TensorFlow API幫助文檔
https://www.tensorflow.org/api_docs/python/?hl=zh-cn
[3].?PaddleFluid?API幫助文檔
http://www.paddlepaddle.org/docs/develop/api/en/fluid/layers.html
? ? ? ? ??
PaddlePaddle開發者交流群
?
想獲取更多深度學習框架干貨?
加入交流群和工程師實時交流
框架介紹√技術干貨√在線Q&A√
?
?申請入群?
長按識別二維碼,添加小助手
*加好友請備注「PaddlePaddle」
關于PaperWeekly
PaperWeekly 是一個推薦、解讀、討論、報道人工智能前沿論文成果的學術平臺。如果你研究或從事 AI 領域,歡迎在公眾號后臺點擊「交流群」,小助手將把你帶入 PaperWeekly 的交流群里。
▽ 點擊 |?閱讀原文?| 加入社區刷論文
總結
以上是生活随笔為你收集整理的PaddleFluid和TensorFlow基本使用概念对比 | PaddlePaddle专栏的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: “噪声对比估计”杂谈:曲径通幽之妙
- 下一篇: 顶会论文轮番炸场,本周哪些论文最值得读?