【故事的開始…】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=[-1, 13], dtype='float32') y = fluid.layers.data(name='Y', shape=[-1, 1], 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=(8, 12)).astype('float32') y = numpy.random.random(size=(8, 1)).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 = [8, 12], X1's width = 12; Y's shape = [13, 1], 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=(10, 2)).astype('float32') linear = fluid.dygraph.Linear(1, 10) 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: [1, 10]. [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