PaddleFluid和TensorFlow基本使用概念对比 | PaddlePaddle专栏
專欄介紹:Paddle Fluid 是用來讓用戶像 PyTorch 和 Tensorflow Eager Execution 一樣執(zhí)行程序。在這些系統(tǒng)中,不再有模型這個(gè)概念,應(yīng)用也不再包含一個(gè)用于描述 Operator 圖或者一系列層的符號描述,而是像通用程序那樣描述訓(xùn)練或者預(yù)測的過程。
本專欄將推出一系列技術(shù)文章,從框架的概念、使用上對比分析 TensorFlow 和 Paddle Fluid,為對 PaddlePaddle 感興趣的同學(xué)提供一些指導(dǎo)。今天將推出系列文章第一期 Paddle Fluid 設(shè)計(jì)思想和基本概念。
深度學(xué)習(xí)平臺的演化
時(shí)至今日,深度學(xué)習(xí)已成為事實(shí)上最流行的機(jī)器學(xué)習(xí)技術(shù)。學(xué)術(shù)界多年研究加上工業(yè)界的長期實(shí)踐提出了若干有效的基本建模單元:全連接,卷積,循環(huán)神經(jīng)網(wǎng)絡(luò)等;設(shè)計(jì)各類訓(xùn)練技巧:初始化方法,跨層連接,各類 norm 技術(shù)等;發(fā)明了各種新的優(yōu)化算法:Adadelta,Adam 等;各類固定的網(wǎng)絡(luò)結(jié)構(gòu):highway, residual, attention 等紛紛涌現(xiàn),不勝枚舉。學(xué)術(shù)界工業(yè)界多年的付出共同促成了深度學(xué)習(xí)方法今日的影響力。?
學(xué)術(shù)研究和生產(chǎn)實(shí)踐中積累了大量的知識,能夠很好的解釋神經(jīng)網(wǎng)絡(luò)中基本模塊各自獨(dú)的學(xué)習(xí)能力和特性。基本模塊和訓(xùn)練技術(shù)的組合能夠搭建出千變?nèi)f化的神經(jīng)網(wǎng)絡(luò)模型。基本模塊和訓(xùn)練技術(shù)是有限的,但他們的組合卻是千變?nèi)f化,這是深度學(xué)習(xí)方法的魅力所在,也是難度所在。
正是這樣高度的模塊化特性,研究者和工程師們都在努力避免重復(fù)造輪子以提高研究和生產(chǎn)的效率,又進(jìn)一步催生了深度學(xué)習(xí)平臺技術(shù)的發(fā)展,深度學(xué)習(xí)框架已演變成為 AI 基礎(chǔ)設(shè)施中重要的一部分。從 Theano,到 DistBelief,到 TensorFlow;從 Caffe 到 Caffe2;從 Torch 到 PyTorch;從 PaddlePaddle 到 PaddleFluid,深度學(xué)習(xí)平臺技術(shù)也經(jīng)歷了兩代的演化,并向著第三代平臺技術(shù)邁進(jìn)。?
站在歷史發(fā)展的今天,當(dāng)我們準(zhǔn)備切換嘗試使用一個(gè)新的深度學(xué)習(xí)平臺作為支持自己學(xué)習(xí)和研究的工具時(shí),平臺技術(shù)都發(fā)生了哪些演化,能夠?yàn)槲覀兊膸硎裁幢憷?#xff1f;先讓我們來看看深度學(xué)習(xí)框架解決的三大問題:?
如何描述計(jì)算以支持未來潛在會出現(xiàn)的新模型??
如何高效利用異構(gòu)設(shè)備最大化算力??
如何利用網(wǎng)絡(luò)中的計(jì)算機(jī)進(jìn)行分布式計(jì)算來處理千萬億級別的數(shù)據(jù)??
以上三個(gè)問題中的第一個(gè)和使用者研究者最為密切相關(guān)。這篇文章我們通過分析 PaddleFluid 和 TensorFlow 的不同設(shè)計(jì)理念,來了解一個(gè)深度學(xué)習(xí)框架如何抽象深度學(xué)習(xí)模型,來看看我們的使用經(jīng)驗(yàn)如何在不同深度學(xué)習(xí)平臺之間過度和遷移。
如何描述計(jì)算
讓我們首先來看看 PaddleFluid 和 TensorFlow 在“如何描述機(jī)器學(xué)習(xí)模型”這一問題上各自的選擇。
TensorFlow之?Computation Graph?
TensorFlow 使用數(shù)據(jù)流圖(Dataflow Graph)來描述機(jī)器學(xué)習(xí)模型中的涉及到的所有計(jì)算(computation)和狀態(tài)(state)。一個(gè) TensorFlow 模型只有一個(gè)計(jì)算圖,計(jì)算圖中包括數(shù)學(xué)運(yùn)算和運(yùn)算的對象(參數(shù)),甚至也包括:參數(shù)的初始化、優(yōu)化算法部分(對可學(xué)習(xí)參數(shù)的更新規(guī)則),以及數(shù)據(jù)預(yù)處理等。?
這樣的一個(gè)計(jì)算圖可以更進(jìn)一步解釋:?
一個(gè)機(jī)器學(xué)習(xí)模型,在?TensorFlow?中用一個(gè)有向無環(huán)圖表示;
圖中的結(jié)點(diǎn)對應(yīng)了機(jī)器學(xué)習(xí)模型中的某個(gè)具體運(yùn)算,在 TensorFlow 中稱之為:Operation;
圖中的邊是 Operation 之間的輸入輸出數(shù)據(jù)流動。
在 TenorFlow 中,Operation 的輸入輸出統(tǒng)一用 Tensor 表示,這里可以簡單地理解為 Tensor 構(gòu)成了計(jì)算圖中的邊。?
總結(jié)之:?
1. 在這一篇中,我們暫不考慮 TensorFlow 在分布式、異構(gòu)計(jì)算方面的設(shè)計(jì),TensorFlow 使用計(jì)算圖(一個(gè)有向無環(huán)圖)來描述機(jī)器學(xué)習(xí)模型,任何模型的定義和優(yōu)化過程將被轉(zhuǎn)換為一個(gè)計(jì)算圖。計(jì)算圖中的結(jié)點(diǎn)是 Operation,表示如何計(jì)算;計(jì)算圖中的邊是 Operation 之間的輸入輸出數(shù)據(jù)流動。在 TensorFlow 中用 Tensor 表示數(shù)據(jù);?
2. TensorFlow 的計(jì)算圖遵循:先定義再執(zhí)行的原則(deferred execution),也就是一個(gè)計(jì)算圖(這里可以理解為代表了神經(jīng)網(wǎng)絡(luò)的網(wǎng)絡(luò)拓?fù)?#xff09;需要預(yù)先聲明,一旦聲明,運(yùn)行時(shí)無法改變其結(jié)構(gòu)。
PaddleFluid之?Program?
如何描述計(jì)算很大程度決定了一個(gè)神經(jīng)網(wǎng)絡(luò)框架計(jì)算功能的完備性。深度學(xué)習(xí)模型和方法歷經(jīng)二十多年的發(fā)展:“依次執(zhí)行一組計(jì)算的前向,再以和前向計(jì)算相反的順序執(zhí)行反向計(jì)算,中間無分支無交互”,這樣的模型結(jié)構(gòu)已經(jīng)無法滿足研究者和千千萬萬框架使用者的想象力。
從 PaddleFluid 的設(shè)計(jì)目標(biāo) [1] 來看,在如何描述機(jī)器學(xué)習(xí)模型這一核心問題上,PaddleFluid 的目標(biāo)是:創(chuàng)造一種新的計(jì)算描述方式,不但能夠描述至今為止人們已知的主流神經(jīng)網(wǎng)絡(luò)模型,并且能夠支持未來會出現(xiàn)的任意模型。
PaddleFluid 是如何做到支持未來出現(xiàn)的新模型這一目標(biāo)呢?PaddleFluid 的設(shè)計(jì)選擇是:對用戶來說,用一段 Program?(在 PaddleFluid 內(nèi)部會被轉(zhuǎn)化為一種叫作 ProgramDesc 的描述語言),而不是用計(jì)算圖來描述機(jī)器學(xué)習(xí)模型。?Program 用符合用戶使用直覺的方式,提供一種新的描述語言能夠描述任意復(fù)雜的機(jī)器學(xué)習(xí)模型。
對所有計(jì)算機(jī)專業(yè)同學(xué)學(xué)習(xí)編程語言的第一課一定是建立對“程序語言的三種執(zhí)行結(jié)構(gòu):順序執(zhí)行,條件選擇和循環(huán)執(zhí)行”的認(rèn)識。計(jì)算機(jī)世界的所有可計(jì)算邏輯都是由這三種執(zhí)行結(jié)構(gòu)表示,用這三種結(jié)構(gòu)描述的邏輯是可計(jì)算的。那么同樣道理,對一個(gè)神經(jīng)網(wǎng)絡(luò)框架來說,如果可以和程序語言一樣提供對這三種執(zhí)行結(jié)構(gòu)的支持,那么將可以描述任意復(fù)雜的,可被計(jì)算機(jī)計(jì)算的,機(jī)器學(xué)習(xí)模型。PaddleFluid通過提供對這三種執(zhí)行結(jié)構(gòu)的支持,來做到對任意復(fù)雜模型的描述。
具體來說:?
1. Fluid 的核心設(shè)計(jì)理念都可以類比到程序語言,如果已經(jīng)有寫程序的經(jīng)驗(yàn),那么使用 Fluid 構(gòu)建神經(jīng)網(wǎng)絡(luò)模型的體驗(yàn),將非常接近寫程序;
2. 在 PaddleFluid 中,用戶不會顯示地感知“計(jì)算圖”這樣的概念,一個(gè)機(jī)器學(xué)習(xí)模型被描述為一個(gè) Fluid Program?(Fluid 內(nèi)部稱之為 ProgramDesc?);
一個(gè) Fluid Program 由一組嵌套的 Block 構(gòu)成。?Block 的概念可以類比到 C++ 或是 Java 中的一對大括號,或是 Python 語言中的一個(gè)縮進(jìn)快;
?Block 中的計(jì)算由順序執(zhí)行、條件選擇或者循環(huán)執(zhí)行三種方式組合,構(gòu)成復(fù)雜的計(jì)算邏輯。
3. Fluid Program 中包含對計(jì)算和計(jì)算對象的描述。計(jì)算的描述稱之為 Operator;計(jì)算作用的對象(或者說 Operator 的輸入和輸出)被統(tǒng)一為 Tensor。?
在描述計(jì)算和計(jì)算的作用對象這一問題上,各個(gè)深度學(xué)習(xí)框架的選擇是相同的,如果有一個(gè)平臺的使用經(jīng)驗(yàn),那么將非常容易在各個(gè)平臺之間進(jìn)行遷移。
總結(jié)
下面的表格總結(jié)了 TensorFlow 和 PaddleFluid 在描述機(jī)器學(xué)習(xí)模型上的不同設(shè)計(jì)選擇。可以看到,Operator和Tensor這些構(gòu)成模型的基礎(chǔ)元素在兩個(gè)平臺中是相似的。如果有任一個(gè)平臺的使用經(jīng)驗(yàn),可以非常快速的將這些概念在不同平臺之間類比推廣。
核心使用概念
下面的小節(jié),我們將更詳細(xì)地了解核心使用概念在兩個(gè)平臺的使用方法。?
數(shù)據(jù)表示和計(jì)算的對象:Tensor?
?Tensor 是向量矩陣概念的擴(kuò)展,是神經(jīng)網(wǎng)絡(luò)模型計(jì)算操作的基本對象。這在是今天所有主流深度學(xué)習(xí)平臺的共同選擇。?
可以簡單地將 Tensor 理解為一個(gè) N 維向量,它可以有任意多的維度。一個(gè) Tensor 具有兩個(gè)基本特征:?
1. 數(shù)據(jù)類型:每個(gè) Tensor 的所有元素具有同樣的、已知的數(shù)據(jù)類型;
2. 大小(或者說形狀):即維度的個(gè)數(shù)(rank,階)以及各維度的長度。?
Tensor 某些維度的長度在定義模型階段可能是未知的,在實(shí)際算法執(zhí)行時(shí)才能確定。例如一個(gè) mini-batch 中包含的樣本數(shù)目(batch size),或者是一個(gè) mini-batch 中序列的最大長度。
TensorFlow中的Tensor
TensorFlow 的內(nèi)部實(shí)現(xiàn)中, Tensor 的存儲方式就是一個(gè) N 維數(shù)組,其中每個(gè)元素都是一個(gè)具體的數(shù)值,例如整形、浮點(diǎn)等。如“TensorFlow”這個(gè)名字所表達(dá)的, Tensor 就是 TensorFlow 中“被運(yùn)算”的對象。在一個(gè)算法的執(zhí)行中,Operation 輸入是 Tensor,經(jīng)過運(yùn)算的中間結(jié)果是 Tensor,最終結(jié)果也是 Tensor。
在 TensorFlow 中,有一些特殊的 Tensor,其中比較常見的有:
1.??tf.Variable?(變量): Variable 用于表示機(jī)器學(xué)習(xí)算法中的參數(shù),具有全局可見性。和一般的 Tensor 相比, Variable 不受 Session (Session 的概念下文會詳細(xì)解釋)的約束,因此在分布式計(jì)算的情況下,多個(gè)計(jì)算單元可以看到同一個(gè) Varible?;
2.? tf.placeholder?: placeholder 類型的 Tensor 在執(zhí)行時(shí)必須接入具體的數(shù)據(jù),通常用于引入輸入數(shù)據(jù);
3.? tf.constant :常量 Tensor,用于生成常用的常量數(shù)據(jù),例如全零、全 1 等。
PaddleFluid中的Tensor
PaddleFluid?中也使用 Tensor 作為神經(jīng)網(wǎng)絡(luò)中輸入輸出數(shù)據(jù)的統(tǒng)一表示。Tensor 的概念在今天主流的深度學(xué)習(xí)平臺中都是完全相同,可以在各個(gè)深度學(xué)習(xí)框架之間直接無縫遷移。
在 Fluid 中也同樣存在三種特殊的 Tensor:
1. 模型中的可學(xué)習(xí)參數(shù)
模型中的可學(xué)習(xí)參數(shù)生存期和整個(gè)訓(xùn)練任務(wù)一樣長,會接受優(yōu)化算法的更新。在 PaddleFluid 中同樣以 Variable 表示;
用戶在絕大多數(shù)情況下都不需要自己來創(chuàng)建網(wǎng)絡(luò)中的可學(xué)習(xí)參數(shù),Fluid 為幾乎常見的神經(jīng)網(wǎng)絡(luò)基本計(jì)算模塊都提供了封裝。以最簡單的全連接模型為例,下面的代碼片段會直接為全連接層創(chuàng)建連接權(quán)值 WW 和偏置( bias )兩個(gè)可學(xué)習(xí)參數(shù),無需顯示地調(diào)用 variable 相關(guān)接口創(chuàng)建可學(xué)習(xí)參數(shù)。
y?=?fluid.layers.fc(input=x,?size=128,?bias_attr=True)
2. 輸入輸出Tensor
整個(gè)神經(jīng)網(wǎng)絡(luò)的輸入數(shù)據(jù)也是一個(gè)特殊的 Tensor,在這個(gè) Tensor 中,一些維度的大小在定義模型時(shí)無法確定(通常包括:batch size;如過 mini-batch 之間,數(shù)據(jù)可變,也會包括序列的最大長度,圖片的寬度和高度等),在定義模型時(shí)需要占位;
PaddleFluid 中使用 fluid.layers.data 來接入輸入數(shù)據(jù), fluid.layer.data 需要提供輸入 Tensor 的 形狀信息,當(dāng)遇到無法確定的維度 時(shí), 相應(yīng)維度指定為 None ,如下面的代碼片段所示:?
x?=?fluid.layers.data(name="x",?shape=[2,?None,?3],?dtype="int64")
3. 常量 Tensor 在 PaddleFluid 中需要通過組合 Tensor 和 fluid.layers.assign?來實(shí)現(xiàn)。
總結(jié)?
1. 在 TensorFlow 和 PaddleFluid 中都統(tǒng)一使用 Tensor 描述神經(jīng)網(wǎng)絡(luò)的輸入輸出以及中間結(jié)算結(jié)果;
2. 對可學(xué)習(xí)參數(shù)這一類特殊的 Tensor:?
在 TensorFlow 中,可學(xué)習(xí)參數(shù)用?tf.Variable (假設(shè)這里已經(jīng)執(zhí)行 import tensorflow as tf )表示;
在 Fluid 中可學(xué)習(xí)參數(shù)使用 fluid.Variable (假設(shè)這里已經(jīng)執(zhí)行 import paddle.fluid as fluid )表示;
不論是使用 TensorFlow 還是 PaddleFluid,通常都可以直接使用較高層次的 API,其中已經(jīng)封裝了幾乎所有常見神經(jīng)網(wǎng)絡(luò)單元,例如全連接、LSTM、CNN 等,這些封裝中都已經(jīng)為用戶正確的創(chuàng)建了該模塊所需的可學(xué)習(xí)參數(shù)。通常不需要自己來創(chuàng)建可學(xué)習(xí)參數(shù)。
3. 對輸入這一類特殊的 Tensor:?
TensorFlow 中用 tf.placeholder 完成占位功能;
對用戶來說,邏輯上可認(rèn)為等價(jià)于 PaddleFluid 中的 fluid.layers.data ;
但需注意,框架內(nèi)部的實(shí)現(xiàn)機(jī)制不盡相同。 tf.placeholder 是一個(gè) Tensor,而?pd.layers.data 創(chuàng)建輸出 Tensor 的同時(shí),還創(chuàng)建了 Feed 數(shù)據(jù)相關(guān)的 operator。
計(jì)算原語:Operation/Operator
Tensor 是今天所有主流深度學(xué)習(xí)框架的統(tǒng)一數(shù)據(jù)表示(輸入、輸出、中間計(jì)算結(jié)果、模型的可學(xué)習(xí)參數(shù)都是 Tensor)。另一方面,對數(shù)據(jù)的操作,在主流深度學(xué)習(xí)框架中也高度統(tǒng)一為:Operator/Operation。在中文中,通常我們會習(xí)慣將其稱之為算子。
注:在 TensorFlow 的官方文檔中,使用 Operation 來稱呼對 Tensor 的操作和變化,而在 PaddleFluid 中使用 Operator 稱呼對 Tensor 的操作,這兩者沒有本質(zhì)區(qū)別。下文將交叉使用兩者,但他們實(shí)際上是同一概念。
Operation/Operator 接受多個(gè) Tensor 作為輸入,輸出若干個(gè) Tensor,表示了從輸入到輸出的變化。
TensorFlow中的Operation?
一個(gè) Operation,接受若干個(gè) Tensor 作為輸入,輸出若干個(gè) Tensor 。可以看出, Operator 作為圖的結(jié)點(diǎn),從進(jìn)入該結(jié)點(diǎn)的邊(tensor)獲得數(shù)據(jù)并完成計(jì)算,然后結(jié)果的 Tensor 作為從該結(jié)點(diǎn)出發(fā)的邊。一個(gè)典型的 Operator 是 tf.matmul ,它接受兩個(gè) Tensor 輸入,將二者相乘,并輸出一個(gè) Tensor 作為結(jié)果。TensorFlow 提供的所有算子,可以在 API 幫助文檔 [2] 中查看。?
PaddleFluid中的Operator?
PaddleFluid 中的 Operator 完全等價(jià)于 TensorFlow 中的 operation。PaddleFluid 支持的所有算子,可以在 API 幫助文檔 [3] 中查看。?
為了便于用戶使用,在 Python 端,Fluid 中的 Operator 被進(jìn)一步封裝入 paddle.fluid.layers , paddle.fluid.networks 等模塊。這是因?yàn)?#xff1a;一些常見的對Tensor的操作可能是有更多基礎(chǔ)操作構(gòu)成,例如:l2 norm 內(nèi)部由 reduce、elementwise_add,scale 等多個(gè) Operator 組合計(jì)算邏輯完成,為了提高使用的便利性,框架內(nèi)部對基礎(chǔ) Operator 進(jìn)行了一些封裝,包括創(chuàng)建 Operator 依賴可學(xué)習(xí)參數(shù),可學(xué)習(xí)參數(shù)的初始化細(xì)節(jié)等,減少用戶重復(fù)開發(fā)的成本。?
對所有深度學(xué)習(xí)框架都面臨同樣的封裝,在絕大多數(shù)情況下,用戶很少會直接與框架底層的 Operator 直接打交道,而是使用框架提供的 layers,networks 等模塊,降低開發(fā)的代碼量。不論是什么樣的概念,他們在各個(gè)礦建之間的本質(zhì)和作用都是相同的:對 Tensor 的變換。?
總結(jié)?
不論叫作 Operation、Operator 還是 layers,他們在各深度學(xué)習(xí)平臺中的含義和作用都是相同的:對 Tensor 的變換。是一個(gè)深度學(xué)習(xí)平臺提供的基礎(chǔ)計(jì)算能力。可以在每個(gè)平臺各自的 API 幫助文檔中查到。?
在各個(gè)深度學(xué)習(xí)平臺都已加入 ONNX 項(xiàng)目的今天,每個(gè)深度學(xué)習(xí)平臺提供給大家的基本算子都已趨同,與此同時(shí),每個(gè)平臺也各有其特點(diǎn),會提供一些獨(dú)特的算子,方便某一類任務(wù)的開發(fā)。
構(gòu)建模型并執(zhí)行?
至此,我們看到了構(gòu)成模型的基礎(chǔ)要素:Tensor 和 Operator 在兩個(gè)框架之間能夠直接遷移。最后一步,我們 看看在兩個(gè)平臺之上,整個(gè)訓(xùn)練任務(wù)是如何運(yùn)行起來的。?
TensorFlow中的Graph和Session?
1. TensorFlow 以計(jì)算圖描述機(jī)器學(xué)習(xí)模型,圖中的結(jié)點(diǎn)是 Operation,邊是 Tensor。在 TensorFlow 中,tf.Graph 維護(hù)了整個(gè)圖的拓?fù)湫畔?#xff1b;
對于 graph,需要額外注意一點(diǎn):TensorFlow 的一個(gè)計(jì)算圖,會有一個(gè) collection 的概念與之對應(yīng)。 collection 是以圖為上下文的 key-value 表,用于維護(hù)圖級別的(也就是全局的)數(shù)據(jù)。例如 一個(gè) variable 可以設(shè)定為是全局可見,此時(shí)這個(gè) variable 相關(guān)的所有信息會在計(jì)算圖對應(yīng)的 collection 中進(jìn)行維護(hù)。
2. 在圖“之上”,TensorFlow 利用 session 機(jī)制來實(shí)際執(zhí)行模型。?
對于一個(gè)定義好的 TensorFlow 的 Graph (也就是一個(gè)定義好的神經(jīng)網(wǎng)絡(luò)模型),為它創(chuàng)建一個(gè) tf.Session 就可以對這個(gè)模型執(zhí)行初始化、運(yùn)行等流程層面的操作;
更精確地說, TensorFlow 的 Session 連接了用戶程序和后端 Runtime,這里所說的“用戶程序”就是 TensorFlow 的使用者對機(jī)器學(xué)習(xí)模型的定義和設(shè)置等,而后端 Runtime 是實(shí)際完成算法訓(xùn)練、測試等真實(shí)計(jì)算任務(wù)的程序。這種“連接”也是一種“隔離”,將使用者和真實(shí)計(jì)算時(shí)涉及的分布式計(jì)算等細(xì)節(jié)隔離,便于使用。
Fluid中的Program和Executor?
1. PaddleFluid 使用 Program 描述神經(jīng)網(wǎng)絡(luò)模型,對用戶來說,并沒有計(jì)算圖的概念。用戶定義的所有 Tensor 以及對 Tensor 的操作:Operator 都會被加入一段 Program 中;
一段 Program 由嵌套的 Block 構(gòu)成,但用戶無需顯示地創(chuàng)建 Block 或是顯示地注意到 Block 的存在;
在 PaddleFluid 程序中, Block 是在調(diào)用 while_op , if_op , parallel_do 等特殊 Operator 時(shí),由這些 Operator 來創(chuàng)建;
對用戶使用來說,只需要知道自己正在向一段 Fluid Program 中添加變量(Tensor)和操作(Operator)即可。
2. PaddleFluid 利用 Executor 來執(zhí)行一段 Fluid Program。?
為進(jìn)一步理解 Fluid 中 Executor 的作用,需要先解釋一下 Fluid 程序的執(zhí)行流程。 下圖展示單機(jī)上,Fluid 程序的執(zhí)行流程:
▲ Fig. Fluid本地訓(xùn)練任務(wù)執(zhí)行流程圖
1. Fluid 設(shè)計(jì)思想和靈感非常類似于程序設(shè)計(jì)語言,和高級編譯語言 C++/Java 編寫程序的過程非常類似,Fluid 程序執(zhí)行分為兩個(gè)重要階段:編譯時(shí)和運(yùn)行時(shí);
2. 編譯期,用戶通過調(diào)用 Fluid 提供的算子,向一段 Program 中添加變量(Tensor)以及對變量的操作(Operators 或者 Layers)。用戶只需要描述核心的前向計(jì)算,不需要關(guān)心反向計(jì)算,分布式下,異構(gòu)設(shè)備下如何計(jì)算;
3. 原始的 Program 在平臺內(nèi)部轉(zhuǎn)換為中間描述語言: ProgramDesc ;
4. 編譯期最重要的一個(gè)功能模塊是 Transpiler。Transpiler 接受一段 ProgramDesc ,輸出一段變化后的 ProgramDesc ,作為后端 Executor 最終需要執(zhí)行的 Fluid Program ;
最為常用的 Transipler 包括:
1. 內(nèi)存優(yōu)化 Transipler:通過對變量讀寫依賴關(guān)系分析,插入內(nèi)存回收 Operator 以維持運(yùn)行過程中較小的內(nèi)存開銷;?
2. 分布式環(huán)境下的 Transpiler:接受用戶定義的 local Program ,生成 Parameter Client 和 Parameter Server 執(zhí)行的兩段 Program 。?
5. 后端 Executor 接受 Transpiler 輸出的這段 Program ,依次執(zhí)行其中的 Operator(可以類比為程序語言中的指令),在執(zhí)行過程中會為 Operator 創(chuàng)建所需的輸入輸出并進(jìn)行管理。?
從上面的過程中可以看到,Fluid 程序的執(zhí)行過程分為:編譯器的定義 Program ,和創(chuàng)建 Executor 運(yùn)行 Program 。 Executor 執(zhí)行一段 Program 的過程是不可交互和不可中斷的。在 PaddleFluid 中,可以創(chuàng)建多余一段 Program 。默認(rèn)情況,一個(gè) PaddleFluid 程序中存在 2 段 Program:
1. fluid.framework.default_startup_program :其中定義了創(chuàng)建模型參數(shù),輸入輸出,以及模型中可學(xué)習(xí)參數(shù)的初始化等各種操作;
?default_startup_program 可以由框架自動生成,使用時(shí)無需顯示地創(chuàng)建;
如果調(diào)用修改了參數(shù)的默認(rèn)初始化方式,框架會自動的將相關(guān)的修改加入 default_startup_program 。
2.? fluid.framework.default_main_program :定義了神經(jīng)網(wǎng)絡(luò)模型,前向反向計(jì)算,以及優(yōu)化算法對網(wǎng)絡(luò)中可學(xué)習(xí)參數(shù)的更新;
使用 Fluid 的核心就是構(gòu)建起 default_main_program 。
3. PaddleFluid 中的 Scope 類似于 TensorFlow 中的 collection 這一概念,但在 Fluid 中 Scope 是框架后端概念,用戶無法直接操作。因此,在使用框架時(shí)無需關(guān)心。
總結(jié)?
對使用框架的用戶來說,可以認(rèn)為 TensorFlow 中的 Graph 等價(jià)于 PaddleFluid 中的 Program,他們在框架中的作用完全相同:完成了對模型的定義。?
TensorFlow 中的 Session 在使用邏輯上非常類似于 PaddleFluid 中的 Executor。?
TensorFlow 通過 Session 來完成計(jì)算圖上的初始化,計(jì)算等運(yùn)行邏輯,連接了 TensorFlow 的前端和后端;
PaddleFluid 中通過 Executor 來執(zhí)行一段用戶定義的 Fluid Program 。
1. Executor 連接了 PaddleFluid 的前端和后端;
2. Executor 接受用戶定義的原始模型(一段 Program ),通過調(diào)用系統(tǒng)中不同功能更的 Transpiler 完成對原始 Program 的變化,進(jìn)行優(yōu)化。
完整實(shí)例:如何完成一個(gè)機(jī)器學(xué)習(xí)模型的訓(xùn)練
這一節(jié),我們以 MNIST 手寫數(shù)字識別問題 —— 機(jī)器學(xué)習(xí)任務(wù)的“Hello World”問題和數(shù)據(jù),為例,通過一個(gè)可以運(yùn)行的完整實(shí)例,來學(xué)習(xí)上文介紹的概念如何在 TensorFlow 和 PaddleFluid 平臺下如何使用和互相遷移。?
TensorFlow實(shí)例?
以下使用 Tensorflow 定義一個(gè)基本的 MLP(單隱層的神經(jīng)網(wǎng)絡(luò))對該問題建模,以說明 Tensorflow 的基本使用方法。?
步驟1:定義數(shù)據(jù)
x?=?tf.placeholder(tf.float32,?shape=[None,?784])
y_?=?tf.placeholder(tf.int32,?shape=[None,])
如前所述, tf.placeholder 是用于引入數(shù)據(jù)的特殊 Tensor,這里分別用 x 和 y_ 代表數(shù)據(jù)的特征和標(biāo)簽。
步驟2:定義模型
y?=?tf.layers.dense(inputs=x,?units=10)
#?operation的loss計(jì)算方式指定
cross_entropy?=?tf.losses.sparse_softmax_cross_entropy(labels=y_,?logits=y)
#?operation優(yōu)化方式指定
train_op?=?tf.train.AdamOptimizer().minimize(cross_entropy)
這段程序分為三個(gè)部分:?
1. 參數(shù)定義:一個(gè)單隱層的 MLP,按照 Tensorflow 的計(jì)算圖抽象方式,即對于輸入 xx,經(jīng)過 y=\omega x+by=ωx+b 這步計(jì)算,得到輸出 yy。其中 xx、yy 是輸入和輸出 tensor,\omegaω 和 bb 是參數(shù) tensor。?
Tensorflow 的 tf.layers 提供了常用的 operation 計(jì)算邏輯,這里用到的 tf.layers.dense 即神經(jīng)網(wǎng)絡(luò)中全連接層的計(jì)算。?
2. loss計(jì)算方式定義:loss 在模型訓(xùn)練的過程中,用于衡量當(dāng)前模型產(chǎn)出的結(jié)果和目標(biāo)之間的差距,也是優(yōu)化算法迭代模型的依據(jù)。 tf.losses 中定義了常用的 loss,這里使用交叉熵(cross entropy),一種多分類情況下常用的 loss。 這里參數(shù)中 y_ 指的是目標(biāo)標(biāo)簽,在上面數(shù)據(jù)引入的部分已經(jīng)定義。
3. operation構(gòu)建:在上面已經(jīng)確定的參數(shù)和 loss 之外,還需要指定迭代時(shí)需要使用的優(yōu)化算法。同樣的 operation 可以在執(zhí)行時(shí)使用不同的優(yōu)化算法。?
步驟3:參數(shù)初始化
sess?=?tf.Session()
sess.run(init)
模型的訓(xùn)練過程,由 tf.Session 管理, tf.Session.run() 以初始化后參數(shù)后的 Graph 為輸入?yún)?shù),做好模型訓(xùn)練的準(zhǔn)備工作。?
這里使用的是默認(rèn)的參數(shù)初始化。實(shí)際上,神經(jīng)網(wǎng)絡(luò)訓(xùn)練的參數(shù)初始化是有多種選擇的,這超出了本篇的覆蓋范圍,暫不贅述將在后面章節(jié)詳細(xì)討論。?
步驟4:數(shù)據(jù)輸入 + 執(zhí)行模型訓(xùn)練
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 為單位向模型給入數(shù)據(jù),之后根據(jù)指定的 loss 和優(yōu)化方法更新模型參數(shù)。核心的函數(shù)是對 tf.Session.run() 的調(diào)用,其中包括之前定義好的 operation、優(yōu)化方法以及給入的數(shù)據(jù)。
其中,給入數(shù)據(jù)的來源,是以下函數(shù):
????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 是從文件中讀取數(shù)據(jù)。本段程序的作用是對數(shù)據(jù)做 shuffle,之后以 batch_size 為長度組織每個(gè) batch 的數(shù)據(jù)。
步驟5:觀察模型效果
以上步驟已經(jīng)構(gòu)建了完整的 Tensorflow 模型訓(xùn)練程序,每個(gè) batch 觀察一次 loss,可以直觀看到模型的迭代效果:
▲?Fig. TensorFlow MNIST手寫數(shù)字識別任務(wù)代價(jià)下降曲線
附:完整代碼
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實(shí)例
步驟1:定義數(shù)據(jù)
PaddleFluid 中以?fluid.layers.data?來接收輸入數(shù)據(jù)。
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]。這三個(gè)維度的含義分別是:channel 數(shù)目,圖像的高度和寬度。?
實(shí)際上 Fluid 框架內(nèi)部,一幅圖像輸入是一個(gè) 4-D Tensor,所有 Tensor 的第 0 維固定為 batch size。框架內(nèi)部會自動為batch size進(jìn)行填充占位。無需對batch size指定填充占位。?
如果除去 batch size(第 0 維度)外,如果 Tensor 某一維度的大小只能在運(yùn)行時(shí)確定,可以在該位置上直接指定 None 進(jìn)行占位。
步驟2:定義模型?
通過調(diào)用 Fluid 提供的算子定義含有一個(gè)隱層的神經(jīng)網(wǎng)絡(luò)。Fluid 模型的分為模型結(jié)構(gòu)和優(yōu)化方法兩部分。這一點(diǎn)與 TensorFlow 程序十分相似似,使用概念可以直接對應(yīng)進(jìn)行遷移。
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 而不是計(jì)算圖描述模型,一般情況下,用戶無需關(guān)心 Program 的細(xì)節(jié),當(dāng)調(diào)用以上 layers 時(shí),會向一個(gè)全局的 Program: fluid.framework.default_main_program 中插入變量(Tensor)和對變量的操作(上述代碼段中的 layers 和 optimzier)。?
步驟3:參數(shù)初始化?
如上文介紹,Fluid 程序中的 Executor 是連接 Fluid 前端和后端的接口。?
默認(rèn)一個(gè)Fluid模型存在至少兩段 Program。用于初始化網(wǎng)絡(luò)中的可學(xué)習(xí)參數(shù)的那一段 Program 叫作 fluid.default_startup_program() 。
只有執(zhí)行器 executor 可以執(zhí)行 Fluid Program,因此,在初始化網(wǎng)絡(luò)中的可學(xué)習(xí)參數(shù)之前,需要首先創(chuàng)建一個(gè) Fluid executor。
place?=?fluid.CPUPlace()
exe?=?fluid.Executor(place)
exe.run(fluid.default_startup_program())
在以上代碼段中, place 用于告訴 executor 一段 Fluid Program 在何種設(shè)備上執(zhí)行,常見的有 fluid.CPUPlace() 和 fluid.CUDAPlace() 。?
步驟4:數(shù)據(jù)輸入 + 執(zhí)行模型訓(xùn)練?
我們在步驟 2 中定義的神經(jīng)網(wǎng)絡(luò)模型最終被插入一段叫做 fluid.framework.default_main_program 的 Fluid Program 中。
網(wǎng)絡(luò)可學(xué)習(xí)參數(shù)初始化之后,可以通過讓執(zhí)行器 Executor 執(zhí)行這段 fluid.framework.default_main_program 來進(jìn)行訓(xùn)練。
????????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 程序的訓(xùn)練過程和 TensorFlow 程序的訓(xùn)練過程非常接近,都放在一個(gè) for 循環(huán)中,循環(huán)讀取一個(gè) mini-batch 數(shù)據(jù),調(diào)用執(zhí)行器執(zhí)行 Fluid default_main_program :接收 mini-batch 輸入,在其上進(jìn)行前向,反向和參數(shù)更新計(jì)算。?
注:上面程序使用了 Fluid 內(nèi)置的 MNIST 數(shù)據(jù),和我們提供給 TensorFlow 示例程序的 MNIST 數(shù)據(jù)完全一樣。?
步驟5:觀察模型效果?
以上步驟已經(jīng)構(gòu)成了完整的 Tensorflow 模型訓(xùn)練程序,每個(gè) batch 觀察一次 loss,可以直觀看到模型的迭代效果:
▲?Fig. Fluid MNIST手寫數(shù)字識別任務(wù)代價(jià)下降曲線
附:完整代碼
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()
總結(jié)
在這一節(jié)中,基于手寫數(shù)字識別數(shù)據(jù)集 MNIST,我們通過一個(gè)完整可運(yùn)行的例子,展示了使用 TensorFlow 和 PaddleFluid 實(shí)現(xiàn)了同樣一個(gè)含有單隱層的全連接神經(jīng)網(wǎng)絡(luò)。通過這個(gè)例子展示主流深度學(xué)習(xí)框架核心概念、用戶接口、使用體驗(yàn)的設(shè)計(jì)選擇。
可以看到盡管內(nèi)部實(shí)現(xiàn)有著非常大的差異,但是對用戶來講,深度學(xué)習(xí)模型的核心概念,包括:Tensor、Operation、Optimzier、網(wǎng)絡(luò)初始化等,在各個(gè)主流深度學(xué)習(xí)框架中都有著對應(yīng)的實(shí)現(xiàn)。如果有著一個(gè)框架的使用經(jīng)驗(yàn),這種使用經(jīng)驗(yàn)將非常容易遷移到其它深度學(xué)習(xí)框架下。
從迭代效果看,這一篇中這個(gè)簡單的模型依照預(yù)期擬合住了訓(xùn)練數(shù)據(jù),但是效果并不驚艷。原因在于:輸入數(shù)據(jù)是圖片像素值,這里的神經(jīng)網(wǎng)絡(luò)模型十分簡單,擬合能力有限。在后面的篇幅,我們將會使用更加復(fù)雜和實(shí)用的例子,進(jìn)一步對比如何不同深度學(xué)習(xí)平臺如何訓(xùn)練同一個(gè)神經(jīng)網(wǎng)絡(luò),我們的使用經(jīng)驗(yàn)如何在不同框架之間進(jìn)行切換和推廣,幫助我們選擇最適合的工具提高研究和生產(chǎn)的效率。
相關(guān)鏈接
[1]. PaddleFluid的設(shè)計(jì)目標(biāo)
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開發(fā)者交流群
?
想獲取更多深度學(xué)習(xí)框架干貨?
加入交流群和工程師實(shí)時(shí)交流
框架介紹√技術(shù)干貨√在線Q&A√
?
?申請入群?
長按識別二維碼,添加小助手
*加好友請備注「PaddlePaddle」
關(guān)于PaperWeekly
PaperWeekly 是一個(gè)推薦、解讀、討論、報(bào)道人工智能前沿論文成果的學(xué)術(shù)平臺。如果你研究或從事 AI 領(lǐng)域,歡迎在公眾號后臺點(diǎn)擊「交流群」,小助手將把你帶入 PaperWeekly 的交流群里。
▽ 點(diǎn)擊 |?閱讀原文?| 加入社區(qū)刷論文
總結(jié)
以上是生活随笔為你收集整理的PaddleFluid和TensorFlow基本使用概念对比 | PaddlePaddle专栏的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: “噪声对比估计”杂谈:曲径通幽之妙
- 下一篇: 顶会论文轮番炸场,本周哪些论文最值得读?