秉承工匠精神,3步定位飛槳報錯緣由,你也來試試?

【故事的開始…】php

小張是一名AI算法攻城獅,聽聞飛槳乃國產開源深度學習框架之光,心想炎黃子孫當自強,用本身的深度學習框架,實現中國的AI夢……python

下載安裝命令

## CPU版本安裝命令
pip install -f https://paddlepaddle.org.cn/pip/oschina/cpu paddlepaddle

## GPU版本安裝命令
pip install -f https://paddlepaddle.org.cn/pip/oschina/gpu paddlepaddle-gpu

他嘗試在的筆記本上使用飛槳搭建線性迴歸示例模型。git

噼裏啪啦…噼裏啪啦…鍵盤敲的熱血澎湃。跑下試試……github

然而,模型的打印結果讓小張滿懷期待的當心情頓時哇涼哇涼的。算法

丹還沒煉成,爐咋就壞了呢?這鋪天蓋地的error,要怎麼分析和處理?編程

【故事的轉折…】網絡

同窗且慢,經官方鑑定,小張大機率使用的是較早版本的飛槳飛槳開源框架1.7及以後版本斷然不會出現這麼繁雜的報錯信息了。app

這是由於飛槳工程師們一直指望產品不但好用,並且易用,能夠給開發者帶來一點點工做上的愉悅。報錯信息對調試分析相當重要,飛槳工程師也一直在持續地進行改進和優化。框架

解讀最新的飛槳報錯信息dom

飛槳報錯信息整體上分爲兩種:一種是直接在Python層攔截報出的錯誤,這種問題通常比較直觀,根據Python原生的報錯棧便可以定位程序中的問題,和你們使用Python寫程序報錯分析的流程一致;一種是飛槳的C++ core中的報錯,這種報錯包含的信息量較大。下面咱們以此類報錯信息的爲例,解讀分析過程。

首先咱們瞭解下目前飛槳最新版本報錯信息的結構,以下圖:

報錯信息爲四段式結構,由上至下依次爲Python默認錯誤信息棧、C++錯誤信息棧、飛槳Python錯誤信息棧(僅聲明式編程模式)、核心錯誤概要。

  • Python默認錯誤信息棧:執行Python程序默認記錄的執行路徑,對定位報錯位置頗有幫助。這是Python自己特性,此處不展開介紹。

  • C++錯誤信息棧:程序在Paddle C++ core中的錯誤路徑,即爲模塊paddle.fluid.core中的程序執行路徑,這部分信息對開發者幫助有限。但當開發者經過Issue向飛槳開發人員提問時,提供C++報錯棧的信息將有助於開發人員快速定位問題。(目前C++錯誤信息棧僅支持Unix平臺,Windows平臺暫不支持)

  • Paddle Python錯誤信息棧:爲何這裏還有一個Paddle Python錯誤信息棧呢?由於在聲明式編程模式(靜態圖)下,模型編譯和執行是分離的。執行時報錯的路徑由Python默認程序棧記錄,但這並不能告知用戶具體出錯的程序位置,所以對於算子類型的API,飛槳額外記錄了編譯時的執行路徑,幫助開發者定位具體代碼出錯的位置,該部分信息對於調試具備較大意義。

  • 核心錯誤概要:信息包含錯誤類型、錯誤特徵、概要提示、出錯文件名與行號、出錯算子名等,這些信息不只有助於開發者理解錯誤,也有助於迅速定位錯誤。

爲何如此重要的錯誤概要放在最後,而不是最前面呢?飛槳開發同窗考慮到開發者在終端執行程序的場景較多,爲了便於用戶在程序執行完後可以立刻看到最重要的提示信息,纔將其置於最後。

硬核來了,3步快速定位問題

當使用飛槳遇到報錯提示時,定位流程是啥樣子的呢?請對應上文提到的飛槳報錯信息結構圖,按以下流程逐步分析。

報錯信息分析流程

下面結合示例,向你們講解飛槳的報錯信息的分析過程(示例使用飛槳2020年7月1日的develop版本)。飛槳支持兩種編程模式,聲明式編程模式(靜態圖)和命令式編程模式(動態圖),咱們將逐一介紹。

飛槳聲明式編程模式

(靜態圖)報錯解讀

執行以下靜態圖示例代碼:

import paddle.fluid as fluid
import numpy

# 1. 網絡結構定義
x = fluid.layers.data(name='X', shape=[-113], dtype='float32')
y = fluid.layers.data(name='Y', shape=[-11], dtype='float32')
predict = fluid.layers.fc(input=x, size=1, act=None)
loss = fluid.layers.square_error_cost(input=predict, label=y)
avg_loss = fluid.layers.mean(loss)

# 2. 優化器配置
fluid.optimizer.SGD(learning_rate=0.01).minimize(avg_loss)

# 3. 執行環境準備
place = fluid.CPUPlace()
exe = fluid.Executor(place)
exe.run(fluid.default_startup_program())

# 4. 執行網絡
x = numpy.random.random(size=(812)).astype('float32')
y = numpy.random.random(size=(81)).astype('float32')
loss_data, = exe.run(fluid.default_main_program(), feed={'X': x, 'Y': y}, fetch_list=[avg_loss.name])

代碼執行後的報錯信息以下:

Traceback (most recent call last):
  File "paddle_error_case1.py", line 24, in <module>
    loss_data, = exe.run(fluid.default_main_program(), feed={'X': x, 'Y': y}, fetch_list=[avg_loss.name])
  File "/usr/local/lib/python3.5/dist-packages/paddle/fluid/executor.py", line 1079, in run
    six.reraise(*sys.exc_info())
  File "/usr/local/lib/python3.5/dist-packages/six.py", line 696, in reraise
    raise value
  File "/usr/local/lib/python3.5/dist-packages/paddle/fluid/executor.py", line 1074, in run
    return_merged=return_merged)
  File "/usr/local/lib/python3.5/dist-packages/paddle/fluid/executor.py", line 1162, in _run_impl
    use_program_cache=use_program_cache)
  File "/usr/local/lib/python3.5/dist-packages/paddle/fluid/executor.py", line 1237, in _run_program
    fetch_var_name)
paddle.fluid.core_avx.EnforceNotMet: 

--------------------------------------------
C++ Call Stacks (More useful to developers):
--------------------------------------------
0   std::string paddle::platform::GetTraceBackString<std::string const&>(std::string const&, char const*, int)
1   paddle::platform::EnforceNotMet::EnforceNotMet(std::string const&, char const*, int)
2   paddle::operators::MulOp::InferShape(paddle::framework::InferShapeContext*) const
3   paddle::framework::OperatorWithKernel::RunImpl(paddle::framework::Scope const&, paddle::platform::Place const&, paddle::framework::RuntimeContext*) const
4   paddle::framework::OperatorWithKernel::RunImpl(paddle::framework::Scope const&, paddle::platform::Place const&) const
5   paddle::framework::OperatorBase::Run(paddle::framework::Scope const&, paddle::platform::Place const&)
6   paddle::framework::Executor::RunPartialPreparedContext(paddle::framework::ExecutorPrepareContext*, paddle::framework::Scope*, long, long, bool, bool, bool)
7   paddle::framework::Executor::RunPreparedContext(paddle::framework::ExecutorPrepareContext*, paddle::framework::Scope*, bool, bool, bool)
8   paddle::framework::Executor::Run(paddle::framework::ProgramDesc const&, paddle::framework::Scope*, int, bool, bool, std::vector<std::string, std::allocator<std::string > > const&, bool, bool)

------------------------------------------
Python Call Stacks (More useful to users):
------------------------------------------
  File "/usr/local/lib/python3.5/dist-packages/paddle/fluid/framework.py", line 2799, in append_op
    attrs=kwargs.get("attrs", None))
  File "/usr/local/lib/python3.5/dist-packages/paddle/fluid/layer_helper.py", line 43, in append_op
    return self.main_program.current_block().append_op(*args, **kwargs)
  File "/usr/local/lib/python3.5/dist-packages/paddle/fluid/layers/nn.py", line 349, in fc
    "y_num_col_dims"1})
  File "paddle_error_case1.py", line 9, in <module>
    predict = fluid.layers.fc(input=x, size=1, act=None)

----------------------
Error Message Summary:
----------------------
InvalidArgumentError: After flatten the input tensor X and Y to 2-D dimensions matrix X1 and Y1, the matrix X1's width must be equal with matrix Y1's height. But received X's shape = [8, 12], X1's shape = [812], X1's width = 12; Y's shape = [131], Y1's shape = [13, 1], Y1's height = 13.
  [Hint: Expected x_mat_dims[1] == y_mat_dims[0], but received x_mat_dims[1]:12 != y_mat_dims[0]:13.] at (/work/paddle/paddle/fluid/operators/mul_op.cc:83)
  [operator < mul > error]

參考飛槳報錯信息分析流程對這個錯誤示例進行剖析。

1. 首先分析代碼核心錯誤概要。依據統一的報錯結構,開發者能夠快速的找到報錯緣由。

從示例中可得到以下信息:

這是一個參數錯誤;出錯的Op是mul;mul Op輸入的Tensor X矩陣的寬度,即第2維的大小須要和輸入Tensor Y矩陣的高度,即第一維的大小相等,才能夠進行正常的矩陣乘法;給出了具體的輸入X與Y的維度信息即出錯維度的值,有一處的維度寫錯了,多是13誤寫成了12。

目前飛槳有12種錯誤類型,更多介紹請查看《報錯信息文案書寫規範》,連接以下:https://github.com/PaddlePaddle/Paddle/wiki/Paddle-Error-Message-Writing-Specification

2. 其次分析Paddle 編譯時Python錯誤信息棧,發現出錯的代碼位置以下:

Paddle插入的Python錯誤信息棧爲了和C++棧的調用順序保持一致,最下面的信息是用戶代碼的位置,這和原生python錯誤信息棧的順序有所區別。這裏咱們能夠得知,是調用fc的時候出錯的,fc中包含一個乘法運算和一個加法運算,根據前面的信息能夠得知是此處的乘法運算的輸入數據存在問題。至此,經過檢查代碼,能夠找到錯誤位置:

將代碼中的12改成13,便可解決該問題。

3. (可選)一般出錯場景較爲簡單時,C++錯誤信息棧能夠不關心。但若是用戶在解決時遇到困難,須要飛槳開發人員協助解決時,須要反饋此信息,幫助開發人員快速得知底層的出錯執行邏輯。例如在這個例子中,咱們可以得知程序的執行路徑爲Run -> RunPreParedContext -> Run -> RunImpl -> MulOp::InferShape,InferShape是檢查算子輸入輸出及參數維度的方法,由此能夠推斷出,本錯誤是因爲Mul算子的輸入參數維度出錯致使。

飛槳命令式編程模式

(動態圖)報錯解讀

動態圖不區分網絡模型的編譯期和執行期,報錯信息中不須要再插入編譯時的python信息棧。執行以下動態圖示例代碼:

import numpy
import paddle.fluid as fluid

place = fluid.CPUPlace()
with fluid.dygraph.guard(place):
    x = numpy.random.random(size=(102)).astype('float32')
    linear = fluid.dygraph.Linear(110)
    data = fluid.dygraph.to_variable(x)
    res = linear(data)

代碼執行後的報錯信息以下:

/work/scripts {master} python paddle_error_case2.py 
Traceback (most recent call last):
  File "paddle_error_case2.py", line 9, in <module>
    res = linear(data)
  File "/usr/local/lib/python3.5/dist-packages/paddle/fluid/dygraph/layers.py", line 600, in __call__
    outputs = self.forward(*inputs, **kwargs)
  File "/usr/local/lib/python3.5/dist-packages/paddle/fluid/dygraph/nn.py", line 965, in forward
    'transpose_Y'False"alpha"1)
paddle.fluid.core_avx.EnforceNotMet: 

--------------------------------------------
C++ Call Stacks (More useful to developers):
--------------------------------------------
0   std::string paddle::platform::GetTraceBackString<std::string const&>(std::string const&, char const*, int)
1   paddle::platform::EnforceNotMet::EnforceNotMet(std::string const&, char const*, int)
2   paddle::operators::MatMulOp::InferShape(paddle::framework::InferShapeContext*) const
3   paddle::imperative::PreparedOp::Run(paddle::imperative::NameVarBaseMap const&, paddle::imperative::NameVarBaseMap const&, paddle::framework::AttributeMap const&)
4   paddle::imperative::Tracer::TraceOp(std::string const&, paddle::imperative::NameVarBaseMap const&, paddle::imperative::NameVarBaseMap const&, paddle::framework::AttributeMap, paddle::platform::Place const&, bool)
5   paddle::imperative::Tracer::TraceOp(std::string const&, paddle::imperative::NameVarBaseMap const&, paddle::imperative::NameVarBaseMap const&, paddle::framework::AttributeMap)

----------------------
Error Message Summary:
----------------------
InvalidArgumentError: Input X's width should be equal to the Y's height, but received X's shape: [10, 2],Y's shape: [110].
  [Hint: Expected mat_dim_x.width_ == mat_dim_y.height_, but received mat_dim_x.width_:2 != mat_dim_y.height_:1.] at (/work/paddle/paddle/fluid/operators/matmul_op.cc:411)
  [operator < matmul > error]

一樣,咱們能夠依據前面講述的步驟對報錯進行分析。

1. 先分析核心錯誤概要,該錯誤與前面的實例相似,也是輸入數據的維度和預期不一致,出錯的Op是matmul。

2. 再分析Python報錯信息棧,能夠得知出錯的代碼位置爲:

經過檢查代碼,也能夠比較容易地定位到錯誤位置在:

將代碼中的2改成1,便可解決該問題。

【故事的尾聲…】

報錯信息的有效性與框架的易用性息息相關,飛槳團隊也仍然在持續地優化報錯信息的質量和友好度,但願能給文中的小張同窗及廣大開發者帶來更好的產品體驗。若是你們發現報錯信息不許確、不直接、不易讀等問題,也歡迎經過Issue及時反饋給咱們。讓咱們期待飛槳的易用性可以進一步提高,成爲功能強大、令開發者工做愉悅的國產開源深度學習框架。

下載安裝命令

## CPU版本安裝命令
pip install -f https://paddlepaddle.org.cn/pip/oschina/cpu paddlepaddle

## GPU版本安裝命令
pip install -f https://paddlepaddle.org.cn/pip/oschina/gpu paddlepaddle-gpu
相關文章
相關標籤/搜索