Paddle關鍵概念

Paddle關鍵概念


正文共:6452?字?0?圖

預計閱讀時間:?17?分鐘

本文討論一下PaddlePaddle框架中幾個重要的概念,在使用Paddle進行開發時,弄清楚這幾個概念是使用Paddle進行開發的一個前提。

Tensor張量

與當前主流框架相同,Paddle同樣使用Tensor張量來表示數據,你可以將不同維度的Tensor理解成對應維度的矩陣,當然,兩者是有差異的。

在Fluid中所有的數據類型都為LoD-Tensor,對于不存在序列信息的數據(如此處的變量X),其lod_level=0。

在Paddle Fluid版本中,有3個比較重要的Tensor

可學習參數

神經網絡中的可學習參數(模型權重、偏置等)其生命周期與整個訓練周期一樣長,在訓練過程中,可學習參數會被修改,最常見的就是模型權重這一可學習參數會被梯度下降算法修改,在Fluid中可以使用 fluid.layers.create_parameter來創建可學習參數

  1. w = fluid.layers.create_parameter(name='w', shape=[1], dtype='float32')

在日常編寫模型中,該方法并不常用,因為Paddle已經為大部分常見的神經網絡基本計算模型進行了封裝,如創建全鏈接層,并不需要自己手動創建鏈接權重w與偏置b

  1. y = fluid.layers.fc(input=x, size=128, bias_attr=True)

其實進入 fluid.layers.create_parameter()方法的源碼和 fluid.layers.fc()方法的源碼,可以發現兩者最終都是調用 helper.create_parameter()方法來創建可學習參數的,其中helper是LayerHelper的實例。

輸入數據Tensor

Fluid使用 fluid.layers.data()方法來接受輸入數據,與TensorFlow中的placeholder占位符作用是相同的,同樣,fluid.layers.data()方法需要定義好輸入Tensor的形狀,如果遇到無法確定形狀的維度,則設置為None。

  1. x = fluid.layers.data(name='x', shape=[3, None], dtype='int64')


  2. # Batch size 無需顯示指定, Paddle會自動填充

  3. a = fluid.layers.data(name='a', shape=[3,6], dtype='int64')


  4. # 輸入數據為寬、高不是固定的圖像, 主要圖像數據類型通常為float32

  5. img = fluid.layers.data(name='img', shape=[3, None, None], dtype='float32')

上述代碼中,一個tick就是Batch size,無需自己顯示的定義。(通常訓練模型時,都以一Batch size大小的數據為一次訓練數據的量)

可以從源碼看出(如下),data()方法中使用appendbatchsize變量判斷是否要為shape自動加上batch size,但正是的創建輸入,使用的是 helper.create_global_variable()方法。而helper依舊是LayerHelper的實例。

  1. def data(name,

  2. ? ? ? ? shape,

  3. ? ? ? ? append_batch_size=True,

  4. ? ? ? ? dtype='float32',

  5. ? ? ? ? lod_level=0,

  6. ? ? ? ? type=core.VarDesc.VarType.LOD_TENSOR,

  7. ? ? ? ? stop_gradient=True):

  8. ? ?helper = LayerHelper('data', **locals())

  9. ? ?shape = list(shape)

  10. ? ?for i in six.moves.range(len(shape)):

  11. ? ? ? ?if shape[i] is None:

  12. ? ? ? ? ? ?shape[i] = -1

  13. ? ? ? ? ? ?append_batch_size = False

  14. ? ? ? ?elif shape[i] < 0:

  15. ? ? ? ? ? ?append_batch_size = False


  16. ? ?if append_batch_size:

  17. ? ? ? ?shape = [-1] + shape ?# append batch size as -1


  18. ? ?data_var = helper.create_global_variable(

  19. ? ? ? ?name=name,

  20. ? ? ? ?shape=shape,

  21. ? ? ? ?dtype=dtype,

  22. ? ? ? ?type=type,

  23. ? ? ? ?stop_gradient=stop_gradient,

  24. ? ? ? ?lod_level=lod_level,

  25. ? ? ? ?is_data=True)

  26. ? ?return data_var

LayerHelper以出現多次,可以看出LayerHelper在Paddle中起著關鍵作用,從Paddle的設計文檔中可以了解到LayerHelper,它的作用主要是在各個layers函數之間共享代碼,設計LayerHelper主要考慮到如果開發全局輔助函數有幾個缺點:

  • 1.需要提供這些方法的命名空間,方便開發人員快速定位并使用

  • 2.全局函數迫使圖層開發人員需要逐個傳遞參數

  • 為了避免以上缺點,才定義出了LayerHelper,但它通常在layers開發中使用,對應創建模型結構的搭建不會使用到LayerHelper,關于LayerHelper的更多細節可以參考Design Doc:Python API,當然后面的文章我也打算講講Paddle設計層面以及底層點的東西,到時也會有所提及。

    常量Tensor

    Fluid通過 fluid.layers.fill_constant()實現常量Tensor,常量即常量,在模型訓練過程中,其值不會改變。

    1. data = fluid.layers.fill_constant(shape=[1], value=0, dtype='int64')

    該方法的內部依舊是使用LayerHelper類來實現常量Tensor

    1. helper = LayerHelper("fill_constant", **locals())

    Paddle數據傳入

    Fluid版本的Paddle支持兩種傳入數據的方式,分別是

  • Python Reader同步讀入數據:使用?fluid.layers.data()定義輸入層,并在?fluid.Executor或?fluid.ParallelExecutor中使用?executor.run(feed=...)來傳入數據,與TensorFlow類似,定義好Placeholder占位符,再在具體訓練時,將具體的數據喂養給模型

  • py_reader異步讀入數據:異步讀入數據,先需要使用?fluid.layers.py_reader()配置異步數據輸入層,再使用?py_reader()的?decorate_paddle_reader或?decorate_tensor_provider方法配置數據,配置完數據后,最總通過?fluid.layers.read_file讀取數據。

  • 對于常用模型,比較常見的事同步讀入數據的方式。Paddle的數據讀入會在后一章進行講解。

    Operator表示對數據的操作

    Fluid版本的Paddle中,所有的數據操作都由Operator表示,在python端,Operator操作被進一步封裝在 paddle.fluid.layerspaddle.fluid.nets等模塊中,簡單而言,所謂構建神經網絡其實就是使用框架提供的各種Operator來操作數據,不必想的過于復雜。一個簡單的加法運算如下:

    1. import paddle.fluid as fluid


    2. #輸入層

    3. a = fluid.layers.data(name='a', shape=[2], dtype='float32')

    4. b = fluid.layers.data(name='b', shape=[2], dtype='float32')


    5. result = fluid.layers.elementwise_add(a,b)


    6. cpu = fluid.CPUPlace() # 定義運算場所

    7. exe = fluid.Executor(cpu) # 創建執行器

    8. exe.run(fluid.default_startup_program()) # 網絡參數初始化


    9. # 準備數據

    10. import numpy


    11. x = numpy.array([1,2])

    12. y = numpy.array([2,3])


    13. #執行計算

    14. outs = exe.run(

    15. ? ?feed={'a':x,'b':y},

    16. ? ?fetch_list=[a,b,result.name])

    17. #查看輸出結果

    18. print(outs)

    一開始使用 fluid.layers.data()定義數據輸入層,具體的數據通過 numpy.array()生成,因為 fluid.layers.data()定義了shape=[2],那么此時numpy.array()需要定義成[1,2],即第一維的形狀為2,如果shape=[1,2],那么第一維的形狀為1,第二維的形狀為2,即numpy.array()需要定義成[[1,2]]。

    動態圖機制

    Fluid使用的是動態圖機制,因為靜態圖機制讓使用者在編寫過程中喪失了對網絡結構修改的靈活性,所以很多優秀的框架都引入了動態圖這種設計(TensorFlow 2.0動態圖機制也大幅加強了)。

    動態圖 vs 靜態圖

    動態計算意味著程序將按照我們編寫命令的順序進行執行。這種機制將使得調試更加容易,并且也使得我們將大腦中的想法轉化為實際代碼變得更加容易。而靜態計算則意味著程序在編譯執行時將先生成神經網絡的結構,然后再執行相應操作。從理論上講,靜態計算這樣的機制允許編譯器進行更大程度的優化,但是這也意味著你所期望的程序與編譯器實際執行之間存在著更多的代溝。這也意味著,代碼中的錯誤將更加難以發現(比如,如果計算圖的結構出現問題,你可能只有在代碼執行到相應操作的時候才能發現它)。盡管理論上而言,靜態計算圖比動態計算圖具有更好的性能,但是在實踐中我們經常發現并不是這樣的。

    來源:動態圖 vs 靜態圖

    Program描述模型

    Fluid版本的Paddle中使用Program的形式描述計算過程,開發者所有寫入在Program中的Operator操作都會自動轉為ProgramDesc描述語言。

    Fluid通過提供順序、分支和循環三種執行結構的支持

    其中順序執行,寫法與靜態圖的形式沒什么差別,如下:

    1. # 順序執行的方式搭建網絡

    2. x = fluid.layers.data(name='x', shape=[13], dtype='float32')

    3. y_predict = fluid.layers.fc(input=x, size=1, act=None)

    4. y = fluid.layers.data(name='y', shape=[1], dtype='float32')

    5. cost = fluid.layers.square_error_cost(input=y_predict, label=y)

    分支條件switch的使用如下:

    1. # 條件分支——switch、if else

    2. lr = fluid.layers.tensor.create_global_var(

    3. ? ?shape=[1],

    4. ? ?value=0.0,

    5. ? ?dtype='float32',

    6. ? ?persistable=True,

    7. ? ?name='learning_rate'

    8. )

    9. one_var = fluid.layers.fill_constant(

    10. ? ?shape=[1], dtype='float32', value=1.0

    11. )

    12. two_var = fluid.layers.fill_constant(

    13. ? ?shape=[1], dtype='float32', value=2.0

    14. )


    15. # switch

    16. with fluid.layers.control_flow.Switch() as switch:

    17. ? ?with switch.case(global_step == one_var):

    18. ? ? ? ?fluid.layers.tensor.assign(input=one_var, output=lr)

    19. ? ? ? ?with switch.default():

    20. ? ? ? ? ? ?fluid.layers.tensor.assign(input=two_var, output=lr)

    其中流程控制方面的內容都放在了 fluid.layers.control_flow模塊下,里面包含了While、Block、Conditional、Switch、if、ifelse等跟中操作,這樣可以讓在編寫模型時,想編寫普通的python程序一樣。

    Executor執行Program

    Program相當于你模型的整體結構,Fluid中程序執行分為編譯與執行兩個階段,當你定義編寫完Program后,還需要定義Executor,Executor會接收Program,然后將其轉為FluidProgram,這一步稱為編譯,然后再使用C++編寫的后端去執行它,執行的過程也由Executor完成,相關代碼片段如下:

    1. cpu = fluid.core.CPUPlace()

    2. exe = fluid.Executor(cpu) #執行器

    3. exe.run(fluid.default_startup_program()) #初始化Program


    4. outs = exe.run(

    5. ? ?feed={'a':x, 'b':y},

    6. ? ?fetch_list=[result.name]

    7. )

    在Fluid中使用Executor.run來運行一段Program。

    正式進行網絡訓練前,需先執行參數初始化。其中 defalut_startup_program中定義了創建模型參數,輸入輸出,以及模型中可學習參數的初始化等各種操作。

    由于傳入數據與傳出數據存在多列,因此 fluid 通過 feed 映射定義數據的傳輸數據,通過 fetch_list 取出期望結果

    整體實踐

    寫過簡單結構,預測一組數據,使用單個全連接層來實現預測,核心的邏輯就是喂養為模型一些訓練數據,模型輸出預測結果,該預測結果與真實結果直接會有個誤差,作為損失,通過平方差損失來定義這兩個損失,然后再最小化該損失,整體邏輯如下:

    1. import paddle.fluid as fluid

    2. import numpy as np


    3. '''

    4. 真實數據

    5. '''

    6. #訓練數據

    7. train_data = np.array([[1.0],[2.0],[3.0],[4.0]]).astype('float32')

    8. #真實標簽

    9. y_true = np.array([[2.0], [4.0], [6.0], [8.0]]).astype('float32')


    10. # 輸入層,接受真實數據

    11. x = fluid.layers.data(name='x', shape=[1], dtype='float32')

    12. y = fluid.layers.data(name='y', shape=[1], dtype='float32')


    13. # 模型結構,這里就一個簡單的全連接層

    14. y_predict = fluid.layers.fc(input=x, size=1, act=None)


    15. # 定義損失,真實標簽值與模型預測值之間的損失

    16. cost = fluid.layers.square_error_cost(input=y_predict, label=y)

    17. #取平均,因為通常有batch個數據,取平均值作為損失則可

    18. avg_cost = fluid.layers.mean(cost)


    19. # 定義執行設備,CUP或GPU

    20. cpu = fluid.CPUPlace()

    21. # Executor執行,該方法只能實現單設備執行

    22. exe = fluid.Executor(cpu)


    23. # 初始化整個結構中的節點

    24. exe.run(fluid.default_startup_program())


    25. for i in range(100):

    26. ? ?outs = exe.run(

    27. ? ? ? ?feed = {'x': train_data, 'y': y_true}, #將真實數據喂養給模型,注意傳入數據與輸入層的關系

    28. ? ? ? ?fetch_list=[y_predict.name, avg_cost.name] #獲取列表,即執行完后,outs會獲得的數據

    29. ? ?)


    30. print(outs)

    ?著作權歸作者所有,轉載或內容合作請聯系作者
    平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
    • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
      沈念sama閱讀 228,316評論 6 531
    • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
      沈念sama閱讀 98,481評論 3 415
    • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
      開封第一講書人閱讀 176,241評論 0 374
    • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
      開封第一講書人閱讀 62,939評論 1 309
    • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
      茶點故事閱讀 71,697評論 6 409
    • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
      開封第一講書人閱讀 55,182評論 1 324
    • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
      沈念sama閱讀 43,247評論 3 441
    • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
      開封第一講書人閱讀 42,406評論 0 288
    • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
      沈念sama閱讀 48,933評論 1 334
    • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
      茶點故事閱讀 40,772評論 3 354
    • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
      茶點故事閱讀 42,973評論 1 369
    • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
      沈念sama閱讀 38,516評論 5 359
    • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
      茶點故事閱讀 44,209評論 3 347
    • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
      開封第一講書人閱讀 34,638評論 0 26
    • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
      開封第一講書人閱讀 35,866評論 1 285
    • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
      沈念sama閱讀 51,644評論 3 391
    • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
      茶點故事閱讀 47,953評論 2 373

    推薦閱讀更多精彩內容