From d83187dba87bf106abb71ed559f645cc79a7933a Mon Sep 17 00:00:00 2001 From: tensor-tang Date: Tue, 4 Sep 2018 11:07:43 +0800 Subject: [PATCH 01/44] enable lac analysis test --- .../fluid/inference/analysis/CMakeLists.txt | 14 +++- .../inference/analysis/analyzer_lac_tester.cc | 70 ++++++++++++++++--- paddle/fluid/inference/api/CMakeLists.txt | 2 +- 3 files changed, 75 insertions(+), 11 deletions(-) diff --git a/paddle/fluid/inference/analysis/CMakeLists.txt b/paddle/fluid/inference/analysis/CMakeLists.txt index cc0dd0d492..eb4908da24 100644 --- a/paddle/fluid/inference/analysis/CMakeLists.txt +++ b/paddle/fluid/inference/analysis/CMakeLists.txt @@ -105,6 +105,18 @@ if (NOT EXISTS ${LAC_INSTALL_DIR} AND WITH_TESTING) endif() inference_analysis_test(test_analyzer_lac SRCS analyzer_lac_tester.cc - EXTRA_DEPS paddle_inference_api paddle_fluid_api + EXTRA_DEPS paddle_inference_api paddle_fluid_api ir_pass_manager analysis + analysis_predictor + # ir + fc_fuse_pass + fc_lstm_fuse_pass + seq_concat_fc_fuse_pass + graph_viz_pass + infer_clean_graph_pass + graph_pattern_detector + infer_clean_graph_pass + attention_lstm_fuse_pass + paddle_inference_api + pass ARGS --infer_model=${LAC_INSTALL_DIR}/model --infer_data=${LAC_INSTALL_DIR}/data.txt) diff --git a/paddle/fluid/inference/analysis/analyzer_lac_tester.cc b/paddle/fluid/inference/analysis/analyzer_lac_tester.cc index e2f7253ac0..2aef25603f 100644 --- a/paddle/fluid/inference/analysis/analyzer_lac_tester.cc +++ b/paddle/fluid/inference/analysis/analyzer_lac_tester.cc @@ -11,8 +11,8 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. + #include "paddle/fluid/inference/analysis/analyzer.h" -#include #include #include "paddle/fluid/framework/ir/pass.h" #include "paddle/fluid/inference/analysis/ut_helper.h" @@ -102,6 +102,7 @@ struct DataRecord { return data; } }; + void GetOneBatch(std::vector *input_slots, DataRecord *data, int batch_size) { auto one_batch = data->NextBatch(); @@ -114,12 +115,14 @@ void GetOneBatch(std::vector *input_slots, DataRecord *data, PADDLE_ENFORCE_EQ(batch_size, static_cast(one_batch.lod.size() - 1)); input_slots->assign({input_tensor}); } + static void PrintTime(const double latency, const int bs, const int repeat) { LOG(INFO) << "===========profile result==========="; LOG(INFO) << "batch_size: " << bs << ", repeat: " << repeat << ", avg latency: " << latency / repeat << "ms"; LOG(INFO) << "====================================="; } + void BenchAllData(const std::string &model_path, const std::string &data_file, const int batch_size, const int repeat) { NativeConfig config; @@ -147,36 +150,64 @@ void BenchAllData(const std::string &model_path, const std::string &data_file, } PrintTime(sum, batch_size, repeat); } + const int64_t lac_ref_data[] = {24, 25, 25, 25, 38, 30, 31, 14, 15, 44, 24, 25, 25, 25, 25, 25, 44, 24, 25, 25, 25, 36, 42, 43, 44, 14, 15, 44, 14, 15, 44, 14, 15, 44, 38, 39, 14, 15, 44, 22, 23, 23, 23, 23, 23, 23, 23}; + void TestLACPrediction(const std::string &model_path, const std::string &data_file, const int batch_size, - const int repeat, bool test_all_data) { - if (test_all_data) { - BenchAllData(model_path, data_file, batch_size, repeat); - return; - } + const int repeat, bool test_all_data, + bool use_analysis = false) { NativeConfig config; config.model_dir = model_path; config.use_gpu = false; config.device = 0; config.specify_input_name = true; - std::vector input_slots, outputs_slots; + std::vector input_slots, outputs_slots, ref_outputs_slots; DataRecord data(data_file, batch_size); GetOneBatch(&input_slots, &data, batch_size); - auto predictor = - CreatePaddlePredictor(config); + std::unique_ptr predictor; + if (use_analysis) { + predictor = + CreatePaddlePredictor( + config); + } else { + predictor = + CreatePaddlePredictor(config); + } for (int i = 0; i < FLAGS_burning; i++) { predictor->Run(input_slots, &outputs_slots); } Timer timer; + if (test_all_data) { + double sum = 0; + for (int i = 0; i < repeat; i++) { + for (size_t bid = 0; bid < data.batched_datas.size(); ++bid) { + GetOneBatch(&input_slots, &data, batch_size); + timer.tic(); + predictor->Run(input_slots, &outputs_slots); + sum += timer.toc(); + } + } + PrintTime(sum, batch_size, repeat); + return; + } timer.tic(); for (int i = 0; i < repeat; i++) { predictor->Run(input_slots, &outputs_slots); } PrintTime(timer.toc(), batch_size, repeat); + + // check result + if (use_analysis) { + // run once for comparion as reference + auto ref_predictor = + CreatePaddlePredictor(config); + ref_predictor->Run(input_slots, &ref_outputs_slots); + } + EXPECT_EQ(outputs_slots.size(), 1UL); auto &out = outputs_slots[0]; size_t size = std::accumulate(out.shape.begin(), out.shape.end(), 1, @@ -188,12 +219,33 @@ void TestLACPrediction(const std::string &model_path, for (size_t i = 0; i < batch1_size; ++i) { EXPECT_EQ(pdata[i], lac_ref_data[i]); } + + if (use_analysis) { + EXPECT_EQ(ref_outputs_slots.size(), outputs_slots.size()); + auto &ref_out = ref_outputs_slots[0]; + size_t ref_size = + std::accumulate(ref_out.shape.begin(), ref_out.shape.end(), 1, + [](int a, int b) { return a * b; }); + EXPECT_EQ(size, ref_size); + int64_t *pdata_ref = static_cast(ref_out.data.data()); + for (size_t i = 0; i < size; ++i) { + EXPECT_EQ(pdata_ref[i], pdata[i]); + } + } } + TEST(Analyzer_LAC, native) { LOG(INFO) << "LAC with native"; TestLACPrediction(FLAGS_infer_model, FLAGS_infer_data, FLAGS_batch_size, FLAGS_repeat, FLAGS_test_all_data); } + +TEST(Analyzer_LAC, analysis) { + LOG(INFO) << "LAC with analysis"; + TestLACPrediction(FLAGS_infer_model, FLAGS_infer_data, FLAGS_batch_size, + FLAGS_repeat, FLAGS_test_all_data, true); +} + } // namespace analysis } // namespace inference } // namespace paddle diff --git a/paddle/fluid/inference/api/CMakeLists.txt b/paddle/fluid/inference/api/CMakeLists.txt index adfe439244..a94c79a698 100644 --- a/paddle/fluid/inference/api/CMakeLists.txt +++ b/paddle/fluid/inference/api/CMakeLists.txt @@ -47,7 +47,7 @@ function(inference_api_test TARGET_NAME) endfunction(inference_api_test) cc_library(paddle_inference_api SRCS api.cc api_impl.cc helper.cc DEPS lod_tensor) -cc_library(analysis_predictor SRCS analysis_predictor.cc DEPS paddle_inference_api) +cc_library(analysis_predictor SRCS analysis_predictor.cc DEPS paddle_inference_api analysis) cc_test(test_paddle_inference_api SRCS api_tester.cc From 555083ae2a61a821d3ced24a0ef08d781f57d7ff Mon Sep 17 00:00:00 2001 From: tensor-tang Date: Tue, 4 Sep 2018 18:02:18 +0800 Subject: [PATCH 02/44] enforce only used --- paddle/fluid/operators/fusion_lstm_op.cc | 27 ++++++++++++------------ 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/paddle/fluid/operators/fusion_lstm_op.cc b/paddle/fluid/operators/fusion_lstm_op.cc index f91236975d..4187e31386 100644 --- a/paddle/fluid/operators/fusion_lstm_op.cc +++ b/paddle/fluid/operators/fusion_lstm_op.cc @@ -38,16 +38,6 @@ void FusionLSTMOp::InferShape(framework::InferShapeContext* ctx) const { "Output(Hidden) of LSTM should not be null."); PADDLE_ENFORCE(ctx->HasOutput("Cell"), "Output(Cell) of LSTM should not be null."); - PADDLE_ENFORCE(ctx->HasOutput("BatchedInput"), - "Output(BatchedInput) of LSTM should not be null."); - PADDLE_ENFORCE(ctx->HasOutput("BatchedHidden"), - "Output(BatchedHidden) of LSTM should not be null."); - PADDLE_ENFORCE(ctx->HasOutput("BatchedCell"), - "Output(BatchedCell) of LSTM should not be null."); - PADDLE_ENFORCE(ctx->HasOutput("ReorderedH0"), - "Output(ReorderedH0) of LSTM should not be null."); - PADDLE_ENFORCE(ctx->HasOutput("ReorderedC0"), - "Output(ReorderedC0) of LSTM should not be null."); auto x_dims = ctx->GetInputDim("X"); PADDLE_ENFORCE_EQ(x_dims.size(), 2, "Input(X)'s rank must be 2."); @@ -99,17 +89,26 @@ void FusionLSTMOp::InferShape(framework::InferShapeContext* ctx) const { framework::DDim out_dims({x_dims[0], frame_size}); ctx->SetOutputDim("Hidden", out_dims); ctx->SetOutputDim("Cell", out_dims); - ctx->SetOutputDim("BatchedInput", {x_dims[0], wx_dims[1]}); - ctx->SetOutputDim("BatchedHidden", out_dims); - ctx->SetOutputDim("BatchedCell", out_dims); ctx->ShareLoD("X", "Hidden"); ctx->ShareLoD("X", "Cell"); - int xx_width; if (ctx->Attrs().Get("use_seq")) { xx_width = wx_dims[1]; } else { xx_width = x_dims[1] > wx_dims[1] ? wx_dims[1] : x_dims[1]; + PADDLE_ENFORCE(ctx->HasOutput("BatchedInput"), + "Output(BatchedInput) of LSTM should not be null."); + PADDLE_ENFORCE(ctx->HasOutput("BatchedHidden"), + "Output(BatchedHidden) of LSTM should not be null."); + PADDLE_ENFORCE(ctx->HasOutput("BatchedCell"), + "Output(BatchedCell) of LSTM should not be null."); + PADDLE_ENFORCE(ctx->HasOutput("ReorderedH0"), + "Output(ReorderedH0) of LSTM should not be null."); + PADDLE_ENFORCE(ctx->HasOutput("ReorderedC0"), + "Output(ReorderedC0) of LSTM should not be null."); + ctx->SetOutputDim("BatchedInput", {x_dims[0], wx_dims[1]}); + ctx->SetOutputDim("BatchedHidden", out_dims); + ctx->SetOutputDim("BatchedCell", out_dims); } ctx->SetOutputDim("XX", {x_dims[0], xx_width}); ctx->ShareLoD("X", "XX"); From 78d9ad5712439e506d3a9153b4361a56f3c65636 Mon Sep 17 00:00:00 2001 From: tensor-tang Date: Tue, 4 Sep 2018 18:37:38 +0800 Subject: [PATCH 03/44] fusion gru enfore only used --- paddle/fluid/operators/fusion_gru_op.cc | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/paddle/fluid/operators/fusion_gru_op.cc b/paddle/fluid/operators/fusion_gru_op.cc index 582c75872a..916f84cb4a 100644 --- a/paddle/fluid/operators/fusion_gru_op.cc +++ b/paddle/fluid/operators/fusion_gru_op.cc @@ -30,14 +30,7 @@ void FusionGRUOp::InferShape(framework::InferShapeContext* ctx) const { "Input(WeightX) of GRU should not be null."); PADDLE_ENFORCE(ctx->HasInput("WeightH"), "Input(WeightH) of GRU should not be null."); - PADDLE_ENFORCE(ctx->HasOutput("XX"), "Output(XX) of GRU should not be null."); - PADDLE_ENFORCE(ctx->HasOutput("ReorderedH0"), - "Output(ReorderedH0) of GRU should not be null."); - PADDLE_ENFORCE(ctx->HasOutput("BatchedInput"), - "Output(BatchedInput) of GRU should not be null."); - PADDLE_ENFORCE(ctx->HasOutput("BatchedOut"), - "Output(BatchedOut) of GRU should not be null."); PADDLE_ENFORCE(ctx->HasOutput("Hidden"), "Output(Hidden) of GRU should not be null."); @@ -80,15 +73,20 @@ void FusionGRUOp::InferShape(framework::InferShapeContext* ctx) const { } framework::DDim out_dims({x_dims[0], frame_size}); ctx->SetOutputDim("Hidden", out_dims); - ctx->SetOutputDim("BatchedInput", {x_dims[0], wx_dims[1]}); - ctx->SetOutputDim("BatchedOut", out_dims); ctx->ShareLoD("X", "Hidden"); - int xx_width; if (ctx->Attrs().Get("use_seq")) { xx_width = wx_dims[1]; } else { xx_width = x_dims[1] > wx_dims[1] ? wx_dims[1] : x_dims[1]; + PADDLE_ENFORCE(ctx->HasOutput("ReorderedH0"), + "Output(ReorderedH0) of GRU should not be null."); + PADDLE_ENFORCE(ctx->HasOutput("BatchedInput"), + "Output(BatchedInput) of GRU should not be null."); + PADDLE_ENFORCE(ctx->HasOutput("BatchedOut"), + "Output(BatchedOut) of GRU should not be null."); + ctx->SetOutputDim("BatchedInput", {x_dims[0], wx_dims[1]}); + ctx->SetOutputDim("BatchedOut", out_dims); } ctx->SetOutputDim("XX", {x_dims[0], xx_width}); ctx->ShareLoD("X", "XX"); From 2f3b498949c4bcfec6e4ced49f61745f76e78eef Mon Sep 17 00:00:00 2001 From: tensor-tang Date: Wed, 5 Sep 2018 11:30:51 +0800 Subject: [PATCH 04/44] refine fusion seq lstm peephole --- .../fluid/framework/ir/fc_lstm_fuse_pass.cc | 1 + paddle/fluid/operators/fusion_lstm_op.cc | 126 ++++++++---------- .../tests/unittests/test_fusion_lstm_op.py | 44 +----- 3 files changed, 58 insertions(+), 113 deletions(-) diff --git a/paddle/fluid/framework/ir/fc_lstm_fuse_pass.cc b/paddle/fluid/framework/ir/fc_lstm_fuse_pass.cc index 55153ecc3e..00f5e7fad2 100644 --- a/paddle/fluid/framework/ir/fc_lstm_fuse_pass.cc +++ b/paddle/fluid/framework/ir/fc_lstm_fuse_pass.cc @@ -11,6 +11,7 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. + #include "paddle/fluid/framework/ir/fc_lstm_fuse_pass.h" #include #include "paddle/fluid/framework/lod_tensor.h" diff --git a/paddle/fluid/operators/fusion_lstm_op.cc b/paddle/fluid/operators/fusion_lstm_op.cc index c473e2593e..f9761d6ec4 100644 --- a/paddle/fluid/operators/fusion_lstm_op.cc +++ b/paddle/fluid/operators/fusion_lstm_op.cc @@ -78,13 +78,12 @@ void FusionLSTMOp::InferShape(framework::InferShapeContext* ctx) const { PADDLE_ENFORCE_EQ(b_dims.size(), 2, "The rank of Input(Bias) should be 2."); PADDLE_ENFORCE_EQ(b_dims[0], 1, "The first dimension of Input(Bias) should be 1."); - - auto use_peepholes = ctx->Attrs().Get("use_peepholes"); - PADDLE_ENFORCE_EQ(b_dims[1], (use_peepholes ? 7 : 4) * frame_size, - "The second dimension of Input(Bias) should be " - "7 * %d if enable peepholes connection or" - "4 * %d if disable peepholes", - frame_size, frame_size); + PADDLE_ENFORCE_EQ( + b_dims[1], (ctx->Attrs().Get("use_peepholes") ? 7 : 4) * frame_size, + "The second dimension of Input(Bias) should be " + "7 * %d if enable peepholes connection or" + "4 * %d if disable peepholes", + frame_size, frame_size); framework::DDim out_dims({x_dims[0], frame_size}); ctx->SetOutputDim("Hidden", out_dims); @@ -231,18 +230,18 @@ class FuisonLSTMKernel : public framework::OpKernel { act_cand = act_functor(act_cand_str); \ } -#define INIT_BASE_INPUT_OUTPUT \ - auto* x = ctx.Input("X"); \ - auto* h0 = ctx.Input("H0"); \ - auto* c0 = ctx.Input("C0"); \ - auto* wx = ctx.Input("WeightX"); \ - auto* wh = ctx.Input("WeightH"); \ - auto* bias = ctx.Input("Bias"); \ - auto* xx = ctx.Output("XX"); \ - auto* hidden_out = ctx.Output("Hidden"); \ - auto* cell_out = ctx.Output("Cell"); \ - bool use_peepholes = ctx.Attr("use_peepholes"); \ - bool is_reverse = ctx.Attr("is_reverse"); +#define INIT_BASE_INPUT_OUTPUT \ + auto* x = ctx.Input("X"); \ + auto* h0 = ctx.Input("H0"); \ + auto* c0 = ctx.Input("C0"); \ + auto* wx = ctx.Input("WeightX"); \ + auto* wh = ctx.Input("WeightH"); \ + auto* bias = ctx.Input("Bias"); \ + auto* xx = ctx.Output("XX"); \ + auto* hidden_out = ctx.Output("Hidden"); \ + auto* cell_out = ctx.Output("Cell"); \ + bool is_reverse = ctx.Attr("is_reverse"); \ + bool use_peepholes = ctx.Attr("use_peepholes"); #define INIT_BASE_SIZES \ auto x_dims = x->dims(); /* T x M*/ \ @@ -261,25 +260,24 @@ class FuisonLSTMKernel : public framework::OpKernel { auto x_lod = x->lod(); const int total_T = x_dims[0]; - const int N = x_lod[0].size() - 1; // batch size - + const int N = x_lod[0].size() - 1; const T* x_data = x->data(); const T* h0_data = h0 ? h0->data() : nullptr; const T* c0_data = c0 ? c0->data() : nullptr; - const T* bias_data = bias->data(); - const T* wc_data = bias_data + D4; // w_ic, w_fc, w_oc const T* wx_data = wx->data(); const T* wh_data = wh->data(); - - T* xx_data = xx->mutable_data(ctx.GetPlace()); - T* hidden_out_data = hidden_out->mutable_data(ctx.GetPlace()); - T* cell_out_data = cell_out->mutable_data(ctx.GetPlace()); - - // use local variable - framework::DDim check_dims({3, D}); - Tensor checked_cell; // w_ic * Ct-1, w_fc * Ct-1, w_oc * Ct - auto checked_cell_data = - checked_cell.mutable_data(check_dims, ctx.GetPlace()); + const T* wc_data = bias->data() + D4; // diagonal weight + auto place = ctx.GetPlace(); + T* xx_data = xx->mutable_data(place); + T* hidden_out_data = hidden_out->mutable_data(place); + T* cell_out_data = cell_out->mutable_data(place); + + Tensor checked_cell; + T* checked_cell_data = nullptr; + if (use_peepholes) { + // w_ic * Ct-1, w_fc * Ct-1 // , w_oc * Ct => ih + checked_cell_data = checked_cell.mutable_data({2, D}, place); + } auto blas = math::GetBlas(ctx); math::FCCompute(blas, total_T, D4, M, x_data, wx_data, @@ -306,44 +304,31 @@ class FuisonLSTMKernel : public framework::OpKernel { int seq_len = x_lod[0][bid + 1] - x_lod[0][bid]; const T* prev_c_data = nullptr; const T* prev_h_data = nullptr; - int tstart = 0; if (h0_data) { prev_h_data = h0_data + bid * D; prev_c_data = c0_data + bid * D; } else { - // If step == 0 and there is no initialized hidden state, that is to say - // the H0 is zeros. Then W_h * H_t-1 can be skipped - - // ~C_t + // W_ch, W_ih, W_fh, W_oh + act_gate(D, xx_data + D, xx_data + D); act_cand(D, xx_data, xx_data); - if (use_peepholes) { - // I_t, F_t - act_gate(D2, xx_data + D, xx_data + D); - } else { - // I_t, F_t, O_t - act_gate(D3, xx_data + D, xx_data + D); - } - // C_t = I_t * ~C_t + // C_t = input * tilde blas.VMUL(D, xx_data, xx_data + D, cell_out_data); + // H_t = act_state(cellout) * outgate if (use_peepholes) { // + W_oc * C_t for peephole connection - blas.VMUL(D, wc_data + D2, cell_out_data, checked_cell_data + D2); - blas.VADD(D, xx_data + D3, checked_cell_data + D2, xx_data + D3); - // O_t - act_gate(D, xx_data + D3, xx_data + D3); + // put result on W_ih + blas.VMUL(D, wc_data + D2, cell_out_data, xx_data + D); + blas.VADD(D, xx_data + D, xx_data + D3, xx_data + D3); } - - // hidden out= act_state(cellout) * outgate + act_gate(D, xx_data + D3, xx_data + D3); act_cell(D, cell_out_data, xx_data + D2); - // H_t = O_t * act_state(C_t) blas.VMUL(D, xx_data + D2, xx_data + D3, hidden_out_data); // prev prev_h_data = hidden_out_data; prev_c_data = cell_out_data; - tstart = 1; move_step(); } @@ -353,39 +338,32 @@ class FuisonLSTMKernel : public framework::OpKernel { blas.GEMM(CblasNoTrans, CblasNoTrans, 1, D4, D, static_cast(1), prev_h_data, D, wh_data, D4, static_cast(1), xx_data, D4); - // ~C_t - act_cand(D, xx_data, xx_data); - + // W_ch, W_ih, W_fh, W_oh if (use_peepholes) { // + W_ic|W_fc * C_t-1 for peephole connection blas.VMUL(D, wc_data, prev_c_data, checked_cell_data); blas.VMUL(D, wc_data + D, prev_c_data, checked_cell_data + D); - blas.VADD(D2, xx_data + D, checked_cell_data, xx_data + D); - // I_t, F_t + blas.VADD(D2, checked_cell_data, xx_data + D, xx_data + D); act_gate(D2, xx_data + D, xx_data + D); } else { - // I_t, F_t, O_t act_gate(D3, xx_data + D, xx_data + D); } - - // F_t * C_t-1 - blas.VMUL(D, xx_data + D2, prev_c_data, xx_data + D2); - // I_t * ~C_t + // a = I_t * act_cand(ch) + act_cand(D, xx_data, xx_data); blas.VMUL(D, xx_data, xx_data + D, xx_data + D); - // C_t = F_t * C_t-1 + I_t * ~C_t + // b = C_t-1 * F_t + blas.VMUL(D, prev_c_data, xx_data + D2, xx_data + D2); + // C_t = a + b blas.VADD(D, xx_data + D, xx_data + D2, cell_out_data); + // H_t = act_cell(C_t) * act_gate(O_c += C_t * W_oc) if (use_peepholes) { - // + W_oc * C_t for peephole connection - blas.VMUL(D, wc_data + D2, cell_out_data, checked_cell_data + D2); - blas.VADD(D, xx_data + D3, checked_cell_data + D2, xx_data + D3); - // O_t + // put result on W_ih + blas.VMUL(D, wc_data + D2, cell_out_data, xx_data + D); + blas.VADD(D, xx_data + D, xx_data + D3, xx_data + D3); act_gate(D, xx_data + D3, xx_data + D3); } - - // hidden out= act_state(cellout) * outgate act_cell(D, cell_out_data, xx_data + D2); - // H_t = O_t * act_state(C_t) blas.VMUL(D, xx_data + D2, xx_data + D3, hidden_out_data); // prev @@ -393,8 +371,8 @@ class FuisonLSTMKernel : public framework::OpKernel { prev_c_data = cell_out_data; move_step(); - } // for each step in batch - } // for each batch + } // for seqlen + } // for batch } void BatchCompute(const framework::ExecutionContext& ctx) const { diff --git a/python/paddle/fluid/tests/unittests/test_fusion_lstm_op.py b/python/paddle/fluid/tests/unittests/test_fusion_lstm_op.py index 4767e9433e..6ffb52185f 100644 --- a/python/paddle/fluid/tests/unittests/test_fusion_lstm_op.py +++ b/python/paddle/fluid/tests/unittests/test_fusion_lstm_op.py @@ -53,12 +53,11 @@ class TestFusionLSTMOp(OpTest): self.M = 8 self.D = 16 self.has_initial_state = False + self.use_peepholes = False self.is_reverse = False self.act_gate = 'sigmoid' self.act_cell = 'tanh' self.act_cand = 'tanh' - self.use_peepholes = False - self.use_seq = False self.set_conf() T = sum(self.lod[0]) @@ -108,7 +107,6 @@ class TestFusionLSTMOp(OpTest): } self.attrs = { 'use_peepholes': self.use_peepholes, - 'use_seq': self.use_seq, 'is_reverse': self.is_reverse, 'gate_activation': self.act_gate, 'cell_activation': self.act_cell, @@ -178,50 +176,18 @@ class TestFusionLSTMOpPeepholesReverse(TestFusionLSTMOp): self.is_reverse = True -class TestFusionLSTMOpPoopholesBS1(TestFusionLSTMOp): +class TestFusionLSTMOpPeepholesInitReverse(TestFusionLSTMOp): def set_conf(self): self.use_peepholes = True - self.lod = [[3]] - self.D = 16 - - -class TestFusionLSTMOpSeqInit(TestFusionLSTMOp): - def set_conf(self): - self.use_seq = True - self.has_initial_state = True - - -class TestFusionLSTMOpSeqReverse(TestFusionLSTMOp): - def set_conf(self): - self.use_seq = True - self.is_reverse = True - - -class TestFusionLSTMOpSeqInitReverse(TestFusionLSTMOp): - def set_conf(self): - self.use_seq = True self.has_initial_state = True self.is_reverse = True -class TestFusionLSTMOpSeqPeepholes(TestFusionLSTMOp): - def set_conf(self): - self.use_seq = True - self.use_peepholes = True - - -class TestFusionLSTMOpSeqPeepholesInit(TestFusionLSTMOp): - def set_conf(self): - self.use_seq = True - self.use_peepholes = True - self.has_initial_state = True - - -class TestFusionLSTMOpSeqPeepholesReverse(TestFusionLSTMOp): +class TestFusionLSTMOpPoopholesBS1(TestFusionLSTMOp): def set_conf(self): - self.use_seq = True self.use_peepholes = True - self.is_reverse = True + self.lod = [[2]] + self.D = 8 if __name__ == '__main__': From f10710b0ca92e51514604628f812661cd3627515 Mon Sep 17 00:00:00 2001 From: tensor-tang Date: Wed, 5 Sep 2018 12:36:11 +0800 Subject: [PATCH 05/44] move seq peephole if out of loop --- paddle/fluid/operators/fusion_lstm_op.cc | 95 ++++++++++++------------ 1 file changed, 49 insertions(+), 46 deletions(-) diff --git a/paddle/fluid/operators/fusion_lstm_op.cc b/paddle/fluid/operators/fusion_lstm_op.cc index f9761d6ec4..a6dc870bba 100644 --- a/paddle/fluid/operators/fusion_lstm_op.cc +++ b/paddle/fluid/operators/fusion_lstm_op.cc @@ -272,6 +272,10 @@ class FuisonLSTMKernel : public framework::OpKernel { T* hidden_out_data = hidden_out->mutable_data(place); T* cell_out_data = cell_out->mutable_data(place); + auto blas = math::GetBlas(ctx); + math::FCCompute(blas, total_T, D4, M, x_data, wx_data, + xx_data, bias->data()); + // for peephole only Tensor checked_cell; T* checked_cell_data = nullptr; if (use_peepholes) { @@ -279,9 +283,6 @@ class FuisonLSTMKernel : public framework::OpKernel { checked_cell_data = checked_cell.mutable_data({2, D}, place); } - auto blas = math::GetBlas(ctx); - math::FCCompute(blas, total_T, D4, M, x_data, wx_data, - xx_data, bias->data()); int xx_offset = D4; int gate_offset = D; if (is_reverse) { @@ -299,6 +300,26 @@ class FuisonLSTMKernel : public framework::OpKernel { cell_out_data = cell_out_data + gate_offset; }; +#define GEMM_WH_ADDON \ + blas.GEMM(CblasNoTrans, CblasNoTrans, 1, D4, D, static_cast(1), \ + prev_h_data, D, wh_data, D4, static_cast(1), xx_data, D4) + +#define GET_Ct \ + /* C_t = C_t-1 * fgated + cand_gated * igated*/ \ + act_cand(D, xx_data, xx_data); \ + blas.VMUL(D, xx_data, xx_data + D, xx_data + D); \ + blas.VMUL(D, prev_c_data, xx_data + D2, xx_data + D2); \ + blas.VADD(D, xx_data + D, xx_data + D2, cell_out_data) + +#define GET_Ht_AND_MOVE \ + /* H_t = act_cell(C_t) * ogated */ \ + act_cell(D, cell_out_data, xx_data + D2); \ + blas.VMUL(D, xx_data + D2, xx_data + D3, hidden_out_data); \ + /* get prev and move*/ \ + prev_h_data = hidden_out_data; \ + prev_c_data = cell_out_data; \ + move_step() + for (int i = 0; i < N; ++i) { int bid = is_reverse ? N - 1 - i : i; int seq_len = x_lod[0][bid + 1] - x_lod[0][bid]; @@ -312,67 +333,49 @@ class FuisonLSTMKernel : public framework::OpKernel { // W_ch, W_ih, W_fh, W_oh act_gate(D, xx_data + D, xx_data + D); act_cand(D, xx_data, xx_data); - // C_t = input * tilde + // C_t = igated * cgated blas.VMUL(D, xx_data, xx_data + D, cell_out_data); - // H_t = act_state(cellout) * outgate + // get outgated if (use_peepholes) { - // + W_oc * C_t for peephole connection - // put result on W_ih + // put W_oc * C_t on igated blas.VMUL(D, wc_data + D2, cell_out_data, xx_data + D); blas.VADD(D, xx_data + D, xx_data + D3, xx_data + D3); } act_gate(D, xx_data + D3, xx_data + D3); - act_cell(D, cell_out_data, xx_data + D2); - blas.VMUL(D, xx_data + D2, xx_data + D3, hidden_out_data); - - // prev - prev_h_data = hidden_out_data; - prev_c_data = cell_out_data; + GET_Ht_AND_MOVE; tstart = 1; - move_step(); } - for (int step = tstart; step < seq_len; ++step) { - // + W_h * H_t-1 - blas.GEMM(CblasNoTrans, CblasNoTrans, 1, D4, D, static_cast(1), - prev_h_data, D, wh_data, D4, static_cast(1), xx_data, D4); - - // W_ch, W_ih, W_fh, W_oh - if (use_peepholes) { - // + W_ic|W_fc * C_t-1 for peephole connection + if (use_peepholes) { + for (int step = tstart; step < seq_len; ++step) { + GEMM_WH_ADDON; + // get fgated and igated blas.VMUL(D, wc_data, prev_c_data, checked_cell_data); blas.VMUL(D, wc_data + D, prev_c_data, checked_cell_data + D); blas.VADD(D2, checked_cell_data, xx_data + D, xx_data + D); act_gate(D2, xx_data + D, xx_data + D); - } else { - act_gate(D3, xx_data + D, xx_data + D); - } - // a = I_t * act_cand(ch) - act_cand(D, xx_data, xx_data); - blas.VMUL(D, xx_data, xx_data + D, xx_data + D); - // b = C_t-1 * F_t - blas.VMUL(D, prev_c_data, xx_data + D2, xx_data + D2); - // C_t = a + b - blas.VADD(D, xx_data + D, xx_data + D2, cell_out_data); + GET_Ct; - // H_t = act_cell(C_t) * act_gate(O_c += C_t * W_oc) - if (use_peepholes) { - // put result on W_ih + // get ogated blas.VMUL(D, wc_data + D2, cell_out_data, xx_data + D); blas.VADD(D, xx_data + D, xx_data + D3, xx_data + D3); act_gate(D, xx_data + D3, xx_data + D3); - } - act_cell(D, cell_out_data, xx_data + D2); - blas.VMUL(D, xx_data + D2, xx_data + D3, hidden_out_data); - - // prev - prev_h_data = hidden_out_data; - prev_c_data = cell_out_data; - - move_step(); - } // for seqlen - } // for batch + GET_Ht_AND_MOVE; + } // for seqlen + } else { + for (int step = tstart; step < seq_len; ++step) { + GEMM_WH_ADDON; + // W_ch, W_ih, W_fh, W_oh + act_gate(D3, xx_data + D, xx_data + D); + GET_Ct; + GET_Ht_AND_MOVE; + } // for seqlen + } + } // for batch +#undef GET_Ht_AND_MOVE +#undef GEMM_WH_ADDON +#undef GET_Ct } void BatchCompute(const framework::ExecutionContext& ctx) const { From 9dd5a177a55f1d2c052c42a511a0eb2cceb3a2c3 Mon Sep 17 00:00:00 2001 From: tensor-tang Date: Wed, 5 Sep 2018 17:00:08 +0800 Subject: [PATCH 06/44] refine batch mode and peephole --- paddle/fluid/operators/fusion_lstm_op.cc | 407 ++++++++++------------- 1 file changed, 179 insertions(+), 228 deletions(-) diff --git a/paddle/fluid/operators/fusion_lstm_op.cc b/paddle/fluid/operators/fusion_lstm_op.cc index a6dc870bba..90736137c6 100644 --- a/paddle/fluid/operators/fusion_lstm_op.cc +++ b/paddle/fluid/operators/fusion_lstm_op.cc @@ -252,154 +252,162 @@ class FuisonLSTMKernel : public framework::OpKernel { const int D3 = D * 3; \ const int D4 = wh_dims[1]; +#define INIT_BASE_INPUT_DATAS \ + const T* x_data = x->data(); \ + const T* wx_data = wx->data(); \ + const T* wh_data = wh->data(); \ + /* diagonal weight*/ \ + const T* wc_data = bias->data() + D4; \ + /* for peephole only*/ \ + Tensor checked_cell; \ + T* checked_cell_data = nullptr; \ + auto place = ctx.GetPlace(); \ + if (use_peepholes) { \ + /* w_ic * Ct-1, w_fc * Ct-1 ; w_oc * Ct => ih*/ \ + checked_cell_data = checked_cell.mutable_data({2, D}, place); \ + } + +/// Compute LSTM +#define GEMM_WH_ADDON(bs, prev, out) \ + blas.GEMM(CblasNoTrans, CblasNoTrans, bs, D4, D, static_cast(1), prev, D, \ + wh_data, D4, static_cast(1), out, D4) + +// gates: W_ch, W_ih, W_fh, W_oh +#define GET_Ct(ct_1, gates, ct) \ + /* C_t = C_t-1 * fgated + cand_gated * igated*/ \ + act_cand(D, gates, gates); \ + blas.VMUL(D, gates, gates + D, gates + D); \ + blas.VMUL(D, ct_1, gates + D2, gates + D2); \ + blas.VADD(D, gates + D, gates + D2, ct) + +#define GET_Ht(ct, gates, ht) \ + /* H_t = act_cell(C_t) * ogated */ \ + act_cell(D, ct, gates + D2); \ + blas.VMUL(D, gates + D2, gates + D3, ht) + +#define COMPUTE_CtHt_WITHOUT_H0C0(gates, ct, ht) \ + act_gate(D, gates + D, gates + D); \ + act_cand(D, gates, gates); \ + /* C_t = igated * cgated*/ \ + blas.VMUL(D, gates, gates + D, ct); \ + /* get outgated*/ \ + if (use_peepholes) { \ + /* put W_oc * C_t on igated */ \ + blas.VMUL(D, wc_data + D2, ct, gates + D); \ + blas.VADD(D, gates + D, gates + D3, gates + D3); \ + } \ + act_gate(D, gates + D3, gates + D3); \ + GET_Ht(ct, gates, ht) + +#define COMPUTE_CtHt(gates, ct_1, ct, ht) \ + act_gate(D3, gates + D, gates + D); \ + GET_Ct(ct_1, gates, ct); \ + GET_Ht(ct, gates, ht) + +#define COMPUTE_CtHt_PEEPHOLE(gates, ct_1, ct, ht) \ + /* get fgated and igated*/ \ + blas.VMUL(D, wc_data, ct_1, checked_cell_data); \ + blas.VMUL(D, wc_data + D, ct_1, checked_cell_data + D); \ + blas.VADD(D2, checked_cell_data, gates + D, gates + D); \ + act_gate(D2, gates + D, gates + D); \ + GET_Ct(ct_1, gates, ct); \ + /* get ogated*/ \ + blas.VMUL(D, wc_data + D2, ct, gates + D); \ + blas.VADD(D, gates + D, gates + D3, gates + D3); \ + act_gate(D, gates + D3, gates + D3); \ + GET_Ht(ct, gates, ht) + void SeqCompute(const framework::ExecutionContext& ctx) const { using DeviceContext = paddle::platform::CPUDeviceContext; INIT_BASE_INPUT_OUTPUT INIT_BASE_SIZES INIT_VEC_FUNC + INIT_BASE_INPUT_DATAS auto x_lod = x->lod(); const int total_T = x_dims[0]; const int N = x_lod[0].size() - 1; - const T* x_data = x->data(); const T* h0_data = h0 ? h0->data() : nullptr; const T* c0_data = c0 ? c0->data() : nullptr; - const T* wx_data = wx->data(); - const T* wh_data = wh->data(); - const T* wc_data = bias->data() + D4; // diagonal weight - auto place = ctx.GetPlace(); T* xx_data = xx->mutable_data(place); - T* hidden_out_data = hidden_out->mutable_data(place); - T* cell_out_data = cell_out->mutable_data(place); - + T* h_out_data = hidden_out->mutable_data(place); + T* c_out_data = cell_out->mutable_data(place); auto blas = math::GetBlas(ctx); math::FCCompute(blas, total_T, D4, M, x_data, wx_data, xx_data, bias->data()); - // for peephole only - Tensor checked_cell; - T* checked_cell_data = nullptr; - if (use_peepholes) { - // w_ic * Ct-1, w_fc * Ct-1 // , w_oc * Ct => ih - checked_cell_data = checked_cell.mutable_data({2, D}, place); - } int xx_offset = D4; int gate_offset = D; if (is_reverse) { const int offset = (total_T - 1) * D; xx_data = xx_data + offset * 4; - hidden_out_data = hidden_out_data + offset; - cell_out_data = cell_out_data + offset; + h_out_data = h_out_data + offset; + c_out_data = c_out_data + offset; xx_offset = -D4; gate_offset = -D; } - auto move_step = [&]() { - xx_data = xx_data + xx_offset; - hidden_out_data = hidden_out_data + gate_offset; - cell_out_data = cell_out_data + gate_offset; - }; - -#define GEMM_WH_ADDON \ - blas.GEMM(CblasNoTrans, CblasNoTrans, 1, D4, D, static_cast(1), \ - prev_h_data, D, wh_data, D4, static_cast(1), xx_data, D4) - -#define GET_Ct \ - /* C_t = C_t-1 * fgated + cand_gated * igated*/ \ - act_cand(D, xx_data, xx_data); \ - blas.VMUL(D, xx_data, xx_data + D, xx_data + D); \ - blas.VMUL(D, prev_c_data, xx_data + D2, xx_data + D2); \ - blas.VADD(D, xx_data + D, xx_data + D2, cell_out_data) - -#define GET_Ht_AND_MOVE \ - /* H_t = act_cell(C_t) * ogated */ \ - act_cell(D, cell_out_data, xx_data + D2); \ - blas.VMUL(D, xx_data + D2, xx_data + D3, hidden_out_data); \ - /* get prev and move*/ \ - prev_h_data = hidden_out_data; \ - prev_c_data = cell_out_data; \ - move_step() - - for (int i = 0; i < N; ++i) { - int bid = is_reverse ? N - 1 - i : i; - int seq_len = x_lod[0][bid + 1] - x_lod[0][bid]; - const T* prev_c_data = nullptr; - const T* prev_h_data = nullptr; - int tstart = 0; - if (h0_data) { - prev_h_data = h0_data + bid * D; - prev_c_data = c0_data + bid * D; - } else { - // W_ch, W_ih, W_fh, W_oh - act_gate(D, xx_data + D, xx_data + D); - act_cand(D, xx_data, xx_data); - // C_t = igated * cgated - blas.VMUL(D, xx_data, xx_data + D, cell_out_data); - - // get outgated - if (use_peepholes) { - // put W_oc * C_t on igated - blas.VMUL(D, wc_data + D2, cell_out_data, xx_data + D); - blas.VADD(D, xx_data + D, xx_data + D3, xx_data + D3); - } - act_gate(D, xx_data + D3, xx_data + D3); - GET_Ht_AND_MOVE; - tstart = 1; - } +#define MOVE_ONE_STEP \ + prev_h_data = h_out_data; \ + prev_c_data = c_out_data; \ + xx_data = xx_data + xx_offset; \ + h_out_data = h_out_data + gate_offset; \ + c_out_data = c_out_data + gate_offset + +#define PROCESS_H0C0 \ + int bid = is_reverse ? N - 1 - i : i; \ + int seq_len = x_lod[0][bid + 1] - x_lod[0][bid]; \ + const T* prev_c_data = nullptr; \ + const T* prev_h_data = nullptr; \ + int tstart = 0; \ + if (h0_data) { \ + prev_h_data = h0_data + bid * D; \ + prev_c_data = c0_data + bid * D; \ + } else { \ + COMPUTE_CtHt_WITHOUT_H0C0(xx_data, c_out_data, h_out_data); \ + MOVE_ONE_STEP; \ + tstart = 1; \ + } - if (use_peepholes) { + if (use_peepholes) { + for (int i = 0; i < N; ++i) { + PROCESS_H0C0; for (int step = tstart; step < seq_len; ++step) { - GEMM_WH_ADDON; - // get fgated and igated - blas.VMUL(D, wc_data, prev_c_data, checked_cell_data); - blas.VMUL(D, wc_data + D, prev_c_data, checked_cell_data + D); - blas.VADD(D2, checked_cell_data, xx_data + D, xx_data + D); - act_gate(D2, xx_data + D, xx_data + D); - GET_Ct; - - // get ogated - blas.VMUL(D, wc_data + D2, cell_out_data, xx_data + D); - blas.VADD(D, xx_data + D, xx_data + D3, xx_data + D3); - act_gate(D, xx_data + D3, xx_data + D3); - GET_Ht_AND_MOVE; - } // for seqlen - } else { + GEMM_WH_ADDON(1, prev_h_data, xx_data); + COMPUTE_CtHt_PEEPHOLE(xx_data, prev_c_data, c_out_data, h_out_data); + MOVE_ONE_STEP; + } + } + } else { + for (int i = 0; i < N; ++i) { + PROCESS_H0C0; for (int step = tstart; step < seq_len; ++step) { - GEMM_WH_ADDON; - // W_ch, W_ih, W_fh, W_oh - act_gate(D3, xx_data + D, xx_data + D); - GET_Ct; - GET_Ht_AND_MOVE; - } // for seqlen + GEMM_WH_ADDON(1, prev_h_data, xx_data); + COMPUTE_CtHt(xx_data, prev_c_data, c_out_data, h_out_data); + MOVE_ONE_STEP; + } } - } // for batch -#undef GET_Ht_AND_MOVE -#undef GEMM_WH_ADDON -#undef GET_Ct + } +#undef PROCESS_H0C0 +#undef MOVE_ONE_STEP } void BatchCompute(const framework::ExecutionContext& ctx) const { using DeviceContext = platform::CPUDeviceContext; INIT_BASE_INPUT_OUTPUT - if (x->lod()[0].size() == 2) { // batch size == 1 + if (x->lod()[0].size() == 2) { SeqCompute(ctx); return; } INIT_BASE_SIZES INIT_VEC_FUNC + INIT_BASE_INPUT_DATAS auto* reordered_h0 = ctx.Output("ReorderedH0"); auto* reordered_c0 = ctx.Output("ReorderedC0"); auto* batched_input = ctx.Output("BatchedInput"); auto* batched_c_out = ctx.Output("BatchedCell"); auto* batched_h_out = ctx.Output("BatchedHidden"); - - const T* x_data = x->data(); - const T* wx_data = wx->data(); - const T* wh_data = wh->data(); - const T* bias_data = bias->data(); - const T* wc_data = bias_data + D4; // w_ic, w_fc, w_oc - auto place = ctx.GetPlace(); T* xx_data = xx->mutable_data(place); T* batched_input_data = batched_input->mutable_data(place); T* batched_c_out_data = batched_c_out->mutable_data(place); @@ -407,12 +415,6 @@ class FuisonLSTMKernel : public framework::OpKernel { hidden_out->mutable_data(place); cell_out->mutable_data(place); - // use local variable - framework::DDim check_dims({3, D}); - Tensor checked_cell; // w_ic * Ct-1, w_fc * Ct-1, w_oc * Ct - auto checked_cell_data = - checked_cell.mutable_data(check_dims, ctx.GetPlace()); - math::LoDTensor2BatchFunctor to_batch; auto& dev_ctx = ctx.template device_context(); auto blas = math::GetBlas(dev_ctx); @@ -434,27 +436,17 @@ class FuisonLSTMKernel : public framework::OpKernel { reordered_h0->Resize({max_bs, D}); reordered_c0->Resize({max_bs, D}); - T* prev_batch_h_data = nullptr; - T* prev_batch_c_data = nullptr; - T* cur_batch_in_data = batched_input_data; - T* cur_batch_h_out_data = batched_h_out_data; - T* cur_batch_c_out_data = batched_c_out_data; - - auto move_step = [&](int bs) { - cur_batch_in_data += bs * D4; - cur_batch_c_out_data += bs * D; - cur_batch_h_out_data += bs * D; - }; - int tstart = 0; + T* prev_h_data = nullptr; + T* prev_c_data = nullptr; if (h0) { // reorder h0, c0 T* reordered_h0_data = reordered_h0->mutable_data(place); T* reordered_c0_data = reordered_c0->mutable_data(place); const T* h0_data = h0->data(); const T* c0_data = c0->data(); - prev_batch_h_data = reordered_h0_data; - prev_batch_c_data = reordered_c0_data; + prev_h_data = reordered_h0_data; + prev_c_data = reordered_c0_data; size_t sz = sizeof(T) * D; for (int i = 0; i < max_bs; ++i) { std::memcpy(reordered_h0_data, h0_data + seq_order[i] * D, sz); @@ -463,123 +455,74 @@ class FuisonLSTMKernel : public framework::OpKernel { reordered_c0_data += D; } } else { - // Compute with no H0/C0 - T* cur_in_data = cur_batch_in_data; - T* cur_c_out_data = cur_batch_c_out_data; - T* cur_h_out_data = cur_batch_h_out_data; - - // If step == 0 and there is no initialized hidden state, that is to say - // the H0 is zeros. Then W_h * H_t-1 can be skiped - - for (int i = 0; i < max_bs; ++i) { // iterate each data in 1st batch - // ~C_t - act_cand(D, cur_in_data, cur_in_data); - - if (use_peepholes) { - // I_t, F_t - act_gate(D2, cur_in_data + D, cur_in_data + D); - } else { - // I_t, F_t, O_t - act_gate(D3, cur_in_data + D, cur_in_data + D); - } - - // C_t = I_t * ~C_t - blas.VMUL(D, cur_in_data, cur_in_data + D, cur_c_out_data); - - if (use_peepholes) { - // + W_oc * C_t for peephole connection - blas.VMUL(D, wc_data + D2, cur_c_out_data, checked_cell_data + D2); - blas.VADD(D, cur_in_data + D3, checked_cell_data + D2, - cur_in_data + D3); - // O_t - act_gate(D, cur_in_data + D3, cur_in_data + D3); - } - - // hidden out= act_state(cellout) * outgate - act_cell(D, cur_c_out_data, cur_in_data + D2); - // H_t = O_t * act_state(C_t) - blas.VMUL(D, cur_in_data + D2, cur_in_data + D3, cur_h_out_data); - - // move to next data in the same batch + // compute without h0, c0 + T* cur_in_data = batched_input_data; + T* cur_h_out_data = batched_h_out_data; + T* cur_c_out_data = batched_c_out_data; + for (int i = 0; i < max_bs; ++i) { + COMPUTE_CtHt_WITHOUT_H0C0(cur_in_data, cur_c_out_data, cur_h_out_data); cur_in_data += D4; cur_c_out_data += D; cur_h_out_data += D; } - - // move to data for next timestep - prev_batch_h_data = cur_batch_h_out_data; - prev_batch_c_data = cur_batch_c_out_data; - move_step(max_bs); tstart = 1; + prev_h_data = batched_h_out_data; + prev_c_data = batched_c_out_data; } - const auto& batch_starts = batched_lod[0]; const int max_seq_len = batch_starts.size() - 1; - for (int step = tstart; step < max_seq_len; ++step) { - const int cur_bs = batch_starts[step + 1] - batch_starts[step]; - // + W_h * H_t-1 - blas.GEMM(CblasNoTrans, CblasNoTrans, cur_bs, D4, D, static_cast(1), - prev_batch_h_data, D, wh_data, D4, static_cast(1), - cur_batch_in_data, D4); - - T* cur_in_data = cur_batch_in_data; - T* cur_c_out_data = cur_batch_c_out_data; - T* cur_h_out_data = cur_batch_h_out_data; - T* prev_c_data = prev_batch_c_data; // NULL if no C0 in step0 - T* prev_h_data = prev_batch_h_data; // NULL if no H0 in step0 - auto next_data_in_batch = [&]() { - cur_in_data += D4; - cur_c_out_data += D; - cur_h_out_data += D; - prev_c_data = prev_c_data ? prev_c_data + D : nullptr; - prev_h_data = prev_h_data ? prev_h_data + D : nullptr; - }; - - for (int i = 0; i < cur_bs; ++i) { // iterate each data in same batch - // ~C_t - act_cand(D, cur_in_data, cur_in_data); - - if (use_peepholes) { - // + W_ic|W_fc * C_t-1 for peephole connection - blas.VMUL(D, wc_data, prev_c_data, checked_cell_data); - blas.VMUL(D, wc_data + D, prev_c_data, checked_cell_data + D); - blas.VADD(D2, cur_in_data + D, checked_cell_data, cur_in_data + D); - // I_t, F_t - act_gate(D2, cur_in_data + D, cur_in_data + D); - } else { - // I_t, F_t, O_t - act_gate(D3, cur_in_data + D, cur_in_data + D); - } + const int offset = tstart * max_bs * D; + batched_input_data = batched_input_data + offset * 4; + batched_h_out_data = batched_h_out_data + offset; + batched_c_out_data = batched_c_out_data + offset; + +#define DEFINE_CUR \ + T* cur_in_data = batched_input_data; \ + T* cur_prev_c_data = prev_c_data; \ + T* cur_c_out_data = batched_c_out_data; \ + T* cur_h_out_data = batched_h_out_data + +#define MOVE_ONE_BATCH \ + cur_in_data += D4; \ + cur_prev_c_data += D; \ + cur_c_out_data += D; \ + cur_h_out_data += D + +#define MOVE_ONE_STEP \ + prev_c_data = batched_c_out_data; \ + prev_h_data = batched_h_out_data; \ + batched_c_out_data = cur_c_out_data; \ + batched_h_out_data = cur_h_out_data; \ + batched_input_data = cur_in_data - // F_t * C_t-1 - blas.VMUL(D, cur_in_data + D2, prev_c_data, cur_in_data + D2); - // I_t * ~C_t - blas.VMUL(D, cur_in_data, cur_in_data + D, cur_in_data + D); - // C_t = F_t * C_t-1 + I_t * ~C_t - blas.VADD(D, cur_in_data + D, cur_in_data + D2, cur_c_out_data); - - if (use_peepholes) { - // + W_oc * C_t for peephole connection - blas.VMUL(D, wc_data + D2, cur_c_out_data, checked_cell_data + D2); - blas.VADD(D, cur_in_data + D3, checked_cell_data + D2, - cur_in_data + D3); - // O_t - act_gate(D, cur_in_data + D3, cur_in_data + D3); + if (use_peepholes) { + for (int step = tstart; step < max_seq_len; ++step) { + const int cur_bs = batch_starts[step + 1] - batch_starts[step]; + GEMM_WH_ADDON(cur_bs, prev_h_data, batched_input_data); + DEFINE_CUR; + for (int i = 0; i < cur_bs; ++i) { + COMPUTE_CtHt_PEEPHOLE(cur_in_data, cur_prev_c_data, cur_c_out_data, + cur_h_out_data); + MOVE_ONE_BATCH; } - - // hidden out= act_state(cellout) * outgate - act_cell(D, cur_c_out_data, cur_in_data + D2); - // H_t = O_t * act_state(C_t) - blas.VMUL(D, cur_in_data + D2, cur_in_data + D3, cur_h_out_data); - - // move to next data in same batch - next_data_in_batch(); + MOVE_ONE_STEP; + } + } else { + for (int step = tstart; step < max_seq_len; ++step) { + const int cur_bs = batch_starts[step + 1] - batch_starts[step]; + GEMM_WH_ADDON(cur_bs, prev_h_data, batched_input_data); + DEFINE_CUR; + for (int i = 0; i < cur_bs; ++i) { + COMPUTE_CtHt(cur_in_data, cur_prev_c_data, cur_c_out_data, + cur_h_out_data); + MOVE_ONE_BATCH; + } + MOVE_ONE_STEP; } - // move to data for next timestep - prev_batch_h_data = cur_batch_h_out_data; - prev_batch_c_data = cur_batch_c_out_data; - move_step(cur_bs); } +#undef MOVE_ONE_STEP +#undef MOVE_ONE_BATCH +#undef DEFINE_CUR math::Batch2LoDTensorFunctor to_seq; batched_h_out->set_lod(batched_lod); @@ -595,6 +538,14 @@ class FuisonLSTMKernel : public framework::OpKernel { BatchCompute(ctx); } } + +#undef COMPUTE_CtHt_PEEPHOLE +#undef COMPUTE_CtHt +#undef COMPUTE_CtHt_WITHOUT_H0C0 +#undef GET_Ht +#undef GET_Ct +#undef GEMM_WH_ADDON +#undef INIT_BASE_INPUT_DATAS #undef INIT_BASE_SIZES #undef INIT_BASE_INPUT_OUTPUT #undef INIT_VEC_FUNC From b4fa3dbda379684b24124c24fef27be93aa9b412 Mon Sep 17 00:00:00 2001 From: luotao1 Date: Wed, 5 Sep 2018 17:46:55 +0800 Subject: [PATCH 07/44] unify PrintTime of analysis unit-test --- .../inference/analysis/analyzer_lac_tester.cc | 10 ++------- .../inference/analysis/analyzer_ner_tester.cc | 6 +---- .../analysis/test_text_classification.cc | 22 ++++--------------- 3 files changed, 7 insertions(+), 31 deletions(-) diff --git a/paddle/fluid/inference/analysis/analyzer_lac_tester.cc b/paddle/fluid/inference/analysis/analyzer_lac_tester.cc index e2f7253ac0..3bb5d9462f 100644 --- a/paddle/fluid/inference/analysis/analyzer_lac_tester.cc +++ b/paddle/fluid/inference/analysis/analyzer_lac_tester.cc @@ -114,12 +114,6 @@ void GetOneBatch(std::vector *input_slots, DataRecord *data, PADDLE_ENFORCE_EQ(batch_size, static_cast(one_batch.lod.size() - 1)); input_slots->assign({input_tensor}); } -static void PrintTime(const double latency, const int bs, const int repeat) { - LOG(INFO) << "===========profile result==========="; - LOG(INFO) << "batch_size: " << bs << ", repeat: " << repeat - << ", avg latency: " << latency / repeat << "ms"; - LOG(INFO) << "====================================="; -} void BenchAllData(const std::string &model_path, const std::string &data_file, const int batch_size, const int repeat) { NativeConfig config; @@ -145,7 +139,7 @@ void BenchAllData(const std::string &model_path, const std::string &data_file, sum += timer.toc(); } } - PrintTime(sum, batch_size, repeat); + PrintTime(batch_size, repeat, 1, 0, sum / repeat); } const int64_t lac_ref_data[] = {24, 25, 25, 25, 38, 30, 31, 14, 15, 44, 24, 25, 25, 25, 25, 25, 44, 24, 25, 25, 25, 36, 42, 43, @@ -176,7 +170,7 @@ void TestLACPrediction(const std::string &model_path, for (int i = 0; i < repeat; i++) { predictor->Run(input_slots, &outputs_slots); } - PrintTime(timer.toc(), batch_size, repeat); + PrintTime(batch_size, repeat, 1, 0, timer.toc() / repeat); EXPECT_EQ(outputs_slots.size(), 1UL); auto &out = outputs_slots[0]; size_t size = std::accumulate(out.shape.begin(), out.shape.end(), 1, diff --git a/paddle/fluid/inference/analysis/analyzer_ner_tester.cc b/paddle/fluid/inference/analysis/analyzer_ner_tester.cc index 720a8811db..9c8fcf84fe 100644 --- a/paddle/fluid/inference/analysis/analyzer_ner_tester.cc +++ b/paddle/fluid/inference/analysis/analyzer_ner_tester.cc @@ -130,11 +130,7 @@ void TestChineseNERPrediction() { for (int i = 0; i < FLAGS_repeat; i++) { predictor->Run(input_slots, &outputs); } - LOG(INFO) << "===========profile result==========="; - LOG(INFO) << "batch_size: " << FLAGS_batch_size - << ", repeat: " << FLAGS_repeat - << ", latency: " << timer.toc() / FLAGS_repeat << "ms"; - LOG(INFO) << "====================================="; + PrintTime(FLAGS_batch_size, FLAGS_repeat, 1, 0, timer.toc() / FLAGS_repeat); PADDLE_ENFORCE(outputs.size(), 1UL); auto &out = outputs[0]; diff --git a/paddle/fluid/inference/analysis/test_text_classification.cc b/paddle/fluid/inference/analysis/test_text_classification.cc index 2913824f62..191b41e988 100644 --- a/paddle/fluid/inference/analysis/test_text_classification.cc +++ b/paddle/fluid/inference/analysis/test_text_classification.cc @@ -18,8 +18,8 @@ #include "paddle/fluid/framework/ir/pass.h" #include "paddle/fluid/inference/analysis/analyzer.h" #include "paddle/fluid/inference/analysis/ut_helper.h" +#include "paddle/fluid/inference/api/helper.h" #include "paddle/fluid/inference/api/paddle_inference_api.h" -#include "paddle/fluid/inference/api/timer.h" DEFINE_string(infer_model, "", "Directory of the inference model."); DEFINE_string(infer_data, "", "Path of the dataset."); @@ -27,22 +27,7 @@ DEFINE_int32(batch_size, 1, "batch size."); DEFINE_int32(repeat, 1, "How many times to repeat run."); namespace paddle { - -template -std::string to_string(const std::vector &vec) { - std::stringstream ss; - for (const auto &c : vec) { - ss << c << " "; - } - return ss.str(); -} - -void PrintTime(const double latency, const int bs, const int repeat) { - LOG(INFO) << "===========profile result==========="; - LOG(INFO) << "batch_size: " << bs << ", repeat: " << repeat - << ", avg latency: " << latency / repeat << "ms"; - LOG(INFO) << "====================================="; -} +namespace inference { void Main(int batch_size) { // Three sequence inputs. @@ -78,7 +63,7 @@ void Main(int batch_size) { CHECK(predictor->Run(input_slots, &output_slots)); sum += timer.toc(); } - PrintTime(sum, batch_size, FLAGS_repeat); + PrintTime(batch_size, FLAGS_repeat, 1, 0, sum / FLAGS_repeat); // Get output LOG(INFO) << "get outputs " << output_slots.size(); @@ -99,6 +84,7 @@ void Main(int batch_size) { TEST(text_classification, basic) { Main(FLAGS_batch_size); } +} // namespace inference } // namespace paddle USE_PASS(fc_fuse_pass); From d7ac1cc83642bf19b133752156c57883000324a1 Mon Sep 17 00:00:00 2001 From: tensor-tang Date: Wed, 5 Sep 2018 18:32:48 +0800 Subject: [PATCH 08/44] refine seq when bs is large --- paddle/fluid/operators/fusion_lstm_op.cc | 87 ++++++++++++------- .../tests/unittests/test_fusion_lstm_op.py | 2 +- 2 files changed, 59 insertions(+), 30 deletions(-) diff --git a/paddle/fluid/operators/fusion_lstm_op.cc b/paddle/fluid/operators/fusion_lstm_op.cc index 90736137c6..ef23ab3f98 100644 --- a/paddle/fluid/operators/fusion_lstm_op.cc +++ b/paddle/fluid/operators/fusion_lstm_op.cc @@ -285,18 +285,23 @@ class FuisonLSTMKernel : public framework::OpKernel { act_cell(D, ct, gates + D2); \ blas.VMUL(D, gates + D2, gates + D3, ht) -#define COMPUTE_CtHt_WITHOUT_H0C0(gates, ct, ht) \ - act_gate(D, gates + D, gates + D); \ - act_cand(D, gates, gates); \ - /* C_t = igated * cgated*/ \ - blas.VMUL(D, gates, gates + D, ct); \ - /* get outgated*/ \ - if (use_peepholes) { \ - /* put W_oc * C_t on igated */ \ - blas.VMUL(D, wc_data + D2, ct, gates + D); \ - blas.VADD(D, gates + D, gates + D3, gates + D3); \ - } \ - act_gate(D, gates + D3, gates + D3); \ +#define GET_Ct_NOH0C0(gates, ct) \ + /* C_t = igated * cgated*/ \ + act_gate(D, gates + D, gates + D); \ + act_cand(D, gates, gates); \ + blas.VMUL(D, gates, gates + D, ct) + +#define COMPUTE_CtHt_NOH0C0(gates, ct, ht) \ + GET_Ct_NOH0C0(gates, ct); \ + act_gate(D, gates + D3, gates + D3); \ + GET_Ht(ct, gates, ht) + +#define COMPUTE_CtHt_PEEPHOLE_NOH0C0(gates, ct, ht) \ + GET_Ct_NOH0C0(gates, ct); \ + /* get outgated, put W_oc * C_t on igated */ \ + blas.VMUL(D, wc_data + D2, ct, gates + D); \ + blas.VADD(D, gates + D, gates + D3, gates + D3); \ + act_gate(D, gates + D3, gates + D3); \ GET_Ht(ct, gates, ht) #define COMPUTE_CtHt(gates, ct_1, ct, ht) \ @@ -354,24 +359,38 @@ class FuisonLSTMKernel : public framework::OpKernel { h_out_data = h_out_data + gate_offset; \ c_out_data = c_out_data + gate_offset -#define PROCESS_H0C0 \ - int bid = is_reverse ? N - 1 - i : i; \ - int seq_len = x_lod[0][bid + 1] - x_lod[0][bid]; \ - const T* prev_c_data = nullptr; \ - const T* prev_h_data = nullptr; \ - int tstart = 0; \ - if (h0_data) { \ - prev_h_data = h0_data + bid * D; \ - prev_c_data = c0_data + bid * D; \ - } else { \ - COMPUTE_CtHt_WITHOUT_H0C0(xx_data, c_out_data, h_out_data); \ - MOVE_ONE_STEP; \ - tstart = 1; \ +#define PROCESS_H0C0_DEFINES \ + int bid = is_reverse ? N - 1 - i : i; \ + int seq_len = x_lod[0][bid + 1] - x_lod[0][bid]; \ + const T* prev_c_data = nullptr; \ + const T* prev_h_data = nullptr; \ + int tstart = 0 + +#define PROCESS_H0C0_PEEPHOLE \ + PROCESS_H0C0_DEFINES; \ + if (h0_data) { \ + prev_h_data = h0_data + bid * D; \ + prev_c_data = c0_data + bid * D; \ + } else { \ + COMPUTE_CtHt_PEEPHOLE_NOH0C0(xx_data, c_out_data, h_out_data); \ + MOVE_ONE_STEP; \ + tstart = 1; \ + } + +#define PROCESS_H0C0 \ + PROCESS_H0C0_DEFINES; \ + if (h0_data) { \ + prev_h_data = h0_data + bid * D; \ + prev_c_data = c0_data + bid * D; \ + } else { \ + COMPUTE_CtHt_NOH0C0(xx_data, c_out_data, h_out_data); \ + MOVE_ONE_STEP; \ + tstart = 1; \ } if (use_peepholes) { for (int i = 0; i < N; ++i) { - PROCESS_H0C0; + PROCESS_H0C0_PEEPHOLE for (int step = tstart; step < seq_len; ++step) { GEMM_WH_ADDON(1, prev_h_data, xx_data); COMPUTE_CtHt_PEEPHOLE(xx_data, prev_c_data, c_out_data, h_out_data); @@ -380,7 +399,7 @@ class FuisonLSTMKernel : public framework::OpKernel { } } else { for (int i = 0; i < N; ++i) { - PROCESS_H0C0; + PROCESS_H0C0 for (int step = tstart; step < seq_len; ++step) { GEMM_WH_ADDON(1, prev_h_data, xx_data); COMPUTE_CtHt(xx_data, prev_c_data, c_out_data, h_out_data); @@ -388,6 +407,8 @@ class FuisonLSTMKernel : public framework::OpKernel { } } } +#undef PROCESS_H0C0_DEFINES +#undef PROCESS_H0C0_PEEPHOLE #undef PROCESS_H0C0 #undef MOVE_ONE_STEP } @@ -460,7 +481,13 @@ class FuisonLSTMKernel : public framework::OpKernel { T* cur_h_out_data = batched_h_out_data; T* cur_c_out_data = batched_c_out_data; for (int i = 0; i < max_bs; ++i) { - COMPUTE_CtHt_WITHOUT_H0C0(cur_in_data, cur_c_out_data, cur_h_out_data); + GET_Ct_NOH0C0(cur_in_data, cur_c_out_data); + if (use_peepholes) { + blas.VMUL(D, wc_data + D2, cur_c_out_data, cur_in_data + D); + blas.VADD(D, cur_in_data + D, cur_in_data + D3, cur_in_data + D3); + } + act_gate(D, cur_in_data + D3, cur_in_data + D3); + GET_Ht(cur_c_out_data, cur_in_data, cur_h_out_data); cur_in_data += D4; cur_c_out_data += D; cur_h_out_data += D; @@ -541,7 +568,9 @@ class FuisonLSTMKernel : public framework::OpKernel { #undef COMPUTE_CtHt_PEEPHOLE #undef COMPUTE_CtHt -#undef COMPUTE_CtHt_WITHOUT_H0C0 +#undef GET_Ct_NOH0C0 +#undef COMPUTE_CtHt_NOH0C0 +#undef COMPUTE_CtHt_PEEPHOLE_NOH0C0 #undef GET_Ht #undef GET_Ct #undef GEMM_WH_ADDON diff --git a/python/paddle/fluid/tests/unittests/test_fusion_lstm_op.py b/python/paddle/fluid/tests/unittests/test_fusion_lstm_op.py index 6ffb52185f..de0c86f96d 100644 --- a/python/paddle/fluid/tests/unittests/test_fusion_lstm_op.py +++ b/python/paddle/fluid/tests/unittests/test_fusion_lstm_op.py @@ -183,7 +183,7 @@ class TestFusionLSTMOpPeepholesInitReverse(TestFusionLSTMOp): self.is_reverse = True -class TestFusionLSTMOpPoopholesBS1(TestFusionLSTMOp): +class TestFusionLSTMOpPeepholesBS1(TestFusionLSTMOp): def set_conf(self): self.use_peepholes = True self.lod = [[2]] From 18442a608812590fb0bc307d2530c8027f7a26c2 Mon Sep 17 00:00:00 2001 From: Xin Pan Date: Wed, 5 Sep 2018 19:52:47 +0800 Subject: [PATCH 09/44] rename pass.h/.cc to analysis_pass --- .../fluid/inference/analysis/CMakeLists.txt | 1 + .../analysis/{pass.cc => analysis_pass.cc} | 2 +- .../analysis/{pass.h => analysis_pass.h} | 44 ++++--------------- paddle/fluid/inference/analysis/analyzer.cc | 5 ++- paddle/fluid/inference/analysis/analyzer.h | 5 ++- .../analysis/data_flow_graph_to_fluid_pass.cc | 2 +- .../analysis/data_flow_graph_to_fluid_pass.h | 4 +- .../analysis/dfg_graphviz_draw_pass.h | 2 +- .../analysis/fluid_to_data_flow_graph_pass.cc | 2 +- .../analysis/fluid_to_data_flow_graph_pass.h | 4 +- .../inference/analysis/fluid_to_ir_pass.h | 24 +++++----- .../inference/analysis/model_store_pass.h | 2 +- .../fluid/inference/analysis/pass_manager.cc | 11 ----- .../fluid/inference/analysis/pass_manager.h | 16 +------ .../inference/analysis/pass_manager_tester.cc | 35 --------------- .../tensorrt_subgraph_node_mark_pass.cc | 2 +- .../tensorrt_subgraph_node_mark_pass.h | 4 +- .../analysis/tensorrt_subgraph_pass.h | 2 +- 18 files changed, 45 insertions(+), 122 deletions(-) rename paddle/fluid/inference/analysis/{pass.cc => analysis_pass.cc} (91%) rename paddle/fluid/inference/analysis/{pass.h => analysis_pass.h} (59%) diff --git a/paddle/fluid/inference/analysis/CMakeLists.txt b/paddle/fluid/inference/analysis/CMakeLists.txt index f2e18a461f..4ca03c0c04 100644 --- a/paddle/fluid/inference/analysis/CMakeLists.txt +++ b/paddle/fluid/inference/analysis/CMakeLists.txt @@ -6,6 +6,7 @@ cc_library(analysis SRCS pass_manager.cc node.cc data_flow_graph.cc graph_traits analyzer.cc helper.cc # passes + analysis_pass.cc fluid_to_data_flow_graph_pass.cc data_flow_graph_to_fluid_pass.cc dfg_graphviz_draw_pass.cc diff --git a/paddle/fluid/inference/analysis/pass.cc b/paddle/fluid/inference/analysis/analysis_pass.cc similarity index 91% rename from paddle/fluid/inference/analysis/pass.cc rename to paddle/fluid/inference/analysis/analysis_pass.cc index 121b72c0a0..9be9f755b9 100644 --- a/paddle/fluid/inference/analysis/pass.cc +++ b/paddle/fluid/inference/analysis/analysis_pass.cc @@ -12,4 +12,4 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "paddle/fluid/inference/analysis/pass.h" +#include "paddle/fluid/inference/analysis/analysis_pass.h" diff --git a/paddle/fluid/inference/analysis/pass.h b/paddle/fluid/inference/analysis/analysis_pass.h similarity index 59% rename from paddle/fluid/inference/analysis/pass.h rename to paddle/fluid/inference/analysis/analysis_pass.h index 7719c6f5ff..b6edb5529a 100644 --- a/paddle/fluid/inference/analysis/pass.h +++ b/paddle/fluid/inference/analysis/analysis_pass.h @@ -28,10 +28,10 @@ namespace paddle { namespace inference { namespace analysis { -class Pass { +class AnalysisPass { public: - Pass() = default; - virtual ~Pass() = default; + AnalysisPass() = default; + virtual ~AnalysisPass() = default; // Mutable Pass. virtual bool Initialize(Argument *argument) { return false; } // Readonly Pass. @@ -42,23 +42,16 @@ class Pass { virtual bool Finalize() { return false; } // Get a Pass appropriate to print the Node this pass operates on. - virtual Pass *CreatePrinterPass(std::ostream &os, - const std::string &banner) const { + virtual AnalysisPass *CreatePrinterPass(std::ostream &os, + const std::string &banner) const { return nullptr; } // Create a debugger Pass that draw the DFG by graphviz toolkit. - virtual Pass *CreateGraphvizDebugerPass() const { return nullptr; } + virtual AnalysisPass *CreateGraphvizDebugerPass() const { return nullptr; } - virtual void Run() { LOG(FATAL) << "not valid"; } - // Run on a single Node. - virtual void Run(Node *x) { LOG(FATAL) << "not valid"; } - // Run on a single Function. - virtual void Run(Function *x) { LOG(FATAL) << "not valid"; } - // Run on a single FunctionBlock. - virtual void Run(FunctionBlock *x) { LOG(FATAL) << "not valid"; } // Run on a single DataFlowGraph. - virtual void Run(DataFlowGraph *x) { LOG(FATAL) << "not valid"; } + virtual void Run(DataFlowGraph *x) = 0; // Human-readable short representation. virtual std::string repr() const = 0; @@ -66,29 +59,8 @@ class Pass { virtual std::string description() const { return "No DOC"; } }; -// NodePass process on any Node types. -class NodePass : public Pass { - public: - virtual void Run(Node *node) = 0; -}; - -// NodePass process on any Function node types. -class FunctionPass : public Pass { - public: - virtual void Run(Function *node) = 0; -}; - -// NodePass process on any FunctionBlock node types. -class FunctionBlockPass : public Pass { - public: - virtual void Run(FunctionBlock *node) = 0; -}; - // GraphPass processes on any GraphType. -class DataFlowGraphPass : public Pass { - public: - virtual void Run(DataFlowGraph *graph) = 0; -}; +class DataFlowGraphPass : public AnalysisPass {}; } // namespace analysis } // namespace inference diff --git a/paddle/fluid/inference/analysis/analyzer.cc b/paddle/fluid/inference/analysis/analyzer.cc index ca83440645..65a3b84f6b 100644 --- a/paddle/fluid/inference/analysis/analyzer.cc +++ b/paddle/fluid/inference/analysis/analyzer.cc @@ -15,6 +15,7 @@ #include "paddle/fluid/inference/analysis/analyzer.h" #include #include + #include "paddle/fluid/inference/analysis/data_flow_graph_to_fluid_pass.h" #include "paddle/fluid/inference/analysis/dfg_graphviz_draw_pass.h" #include "paddle/fluid/inference/analysis/fluid_to_data_flow_graph_pass.h" @@ -58,7 +59,7 @@ class DfgPassManagerImpl final : public DfgPassManager { std::string description() const override { return "DFG pass manager."; } private: - void AddPass(const std::string& name, Pass* pass) { + void AddPass(const std::string& name, AnalysisPass* pass) { VLOG(3) << "Adding pass " << name; Register(name, pass); AddGraphvizDebugerPass(pass); @@ -87,7 +88,7 @@ class DfgPassManagerImpl final : public DfgPassManager { } // Add the graphviz debuger pass if the parent pass has one. - void AddGraphvizDebugerPass(Pass* pass) { + void AddGraphvizDebugerPass(AnalysisPass* pass) { auto* debuger_pass = pass->CreateGraphvizDebugerPass(); if (debuger_pass) { Register(debuger_pass->repr(), debuger_pass); diff --git a/paddle/fluid/inference/analysis/analyzer.h b/paddle/fluid/inference/analysis/analyzer.h index 3fdd2b9ec7..abc3021e7e 100644 --- a/paddle/fluid/inference/analysis/analyzer.h +++ b/paddle/fluid/inference/analysis/analyzer.h @@ -36,8 +36,11 @@ limitations under the License. */ */ #include +#include +#include + +#include "paddle/fluid/inference/analysis/analysis_pass.h" #include "paddle/fluid/inference/analysis/flags.h" -#include "paddle/fluid/inference/analysis/pass.h" #include "paddle/fluid/inference/analysis/pass_manager.h" namespace paddle { diff --git a/paddle/fluid/inference/analysis/data_flow_graph_to_fluid_pass.cc b/paddle/fluid/inference/analysis/data_flow_graph_to_fluid_pass.cc index 80c85555e7..8579845d51 100644 --- a/paddle/fluid/inference/analysis/data_flow_graph_to_fluid_pass.cc +++ b/paddle/fluid/inference/analysis/data_flow_graph_to_fluid_pass.cc @@ -263,7 +263,7 @@ class DFG_DebuggerPass : public DFG_GraphvizDrawPass { }; } // namespace -Pass *DataFlowGraphToFluidPass::CreateGraphvizDebugerPass() const { +AnalysisPass *DataFlowGraphToFluidPass::CreateGraphvizDebugerPass() const { return new DFG_DebuggerPass(DFG_GraphvizDrawPass::Config( FLAGS_IA_graphviz_log_root, "data_flow_graph_to_fluid_graphviz_debugger")); diff --git a/paddle/fluid/inference/analysis/data_flow_graph_to_fluid_pass.h b/paddle/fluid/inference/analysis/data_flow_graph_to_fluid_pass.h index 0c9a8a0b7c..891c7226e2 100644 --- a/paddle/fluid/inference/analysis/data_flow_graph_to_fluid_pass.h +++ b/paddle/fluid/inference/analysis/data_flow_graph_to_fluid_pass.h @@ -21,8 +21,8 @@ #include #include "paddle/fluid/framework/program_desc.h" +#include "paddle/fluid/inference/analysis/analysis_pass.h" #include "paddle/fluid/inference/analysis/data_flow_graph.h" -#include "paddle/fluid/inference/analysis/pass.h" namespace paddle { namespace inference { @@ -42,7 +42,7 @@ class DataFlowGraphToFluidPass final : public DataFlowGraphPass { return "Transform a DFG to a Fluid ProgramDesc"; } - Pass *CreateGraphvizDebugerPass() const override; + AnalysisPass *CreateGraphvizDebugerPass() const override; protected: // Add a Fluid Op into the ProgramDesc. diff --git a/paddle/fluid/inference/analysis/dfg_graphviz_draw_pass.h b/paddle/fluid/inference/analysis/dfg_graphviz_draw_pass.h index 17445ab440..e537bfc0e6 100644 --- a/paddle/fluid/inference/analysis/dfg_graphviz_draw_pass.h +++ b/paddle/fluid/inference/analysis/dfg_graphviz_draw_pass.h @@ -21,8 +21,8 @@ limitations under the License. */ #include #include +#include "paddle/fluid/inference/analysis/analysis_pass.h" #include "paddle/fluid/inference/analysis/dot.h" -#include "paddle/fluid/inference/analysis/pass.h" namespace paddle { namespace inference { diff --git a/paddle/fluid/inference/analysis/fluid_to_data_flow_graph_pass.cc b/paddle/fluid/inference/analysis/fluid_to_data_flow_graph_pass.cc index 51bd0ac42d..2b7d632c83 100644 --- a/paddle/fluid/inference/analysis/fluid_to_data_flow_graph_pass.cc +++ b/paddle/fluid/inference/analysis/fluid_to_data_flow_graph_pass.cc @@ -66,7 +66,7 @@ class DFG_DebuggerPass : public DFG_GraphvizDrawPass { }; } -Pass *FluidToDataFlowGraphPass::CreateGraphvizDebugerPass() const { +AnalysisPass *FluidToDataFlowGraphPass::CreateGraphvizDebugerPass() const { return new DFG_DebuggerPass(DFG_GraphvizDrawPass::Config( FLAGS_IA_graphviz_log_root, "fluid-to-dfg-debuger")); } diff --git a/paddle/fluid/inference/analysis/fluid_to_data_flow_graph_pass.h b/paddle/fluid/inference/analysis/fluid_to_data_flow_graph_pass.h index fb948bf224..b9e262020e 100644 --- a/paddle/fluid/inference/analysis/fluid_to_data_flow_graph_pass.h +++ b/paddle/fluid/inference/analysis/fluid_to_data_flow_graph_pass.h @@ -22,8 +22,8 @@ #include #include "paddle/fluid/framework/program_desc.h" +#include "paddle/fluid/inference/analysis/analysis_pass.h" #include "paddle/fluid/inference/analysis/data_flow_graph.h" -#include "paddle/fluid/inference/analysis/pass.h" namespace paddle { namespace inference { @@ -46,7 +46,7 @@ class FluidToDataFlowGraphPass final : public DataFlowGraphPass { return "transform a fluid ProgramDesc to a data flow graph."; } - Pass *CreateGraphvizDebugerPass() const override; + AnalysisPass *CreateGraphvizDebugerPass() const override; private: framework::proto::ProgramDesc const *desc_; diff --git a/paddle/fluid/inference/analysis/fluid_to_ir_pass.h b/paddle/fluid/inference/analysis/fluid_to_ir_pass.h index 3086085710..c2599e218a 100644 --- a/paddle/fluid/inference/analysis/fluid_to_ir_pass.h +++ b/paddle/fluid/inference/analysis/fluid_to_ir_pass.h @@ -14,15 +14,17 @@ #pragma once +#include +#include + #include "paddle/fluid/framework/ir/fuse_pass_base.h" +#include "paddle/fluid/inference/analysis/analysis_pass.h" #include "paddle/fluid/inference/analysis/flags.h" #include "paddle/fluid/inference/analysis/ir_pass_manager.h" -#include "paddle/fluid/inference/analysis/pass.h" namespace paddle { namespace inference { namespace analysis { -using namespace framework; static const char kFluidToIrPassesAttr[] = "__fluid_to_ir_passes__"; @@ -48,7 +50,8 @@ class FluidToIrPass final : public DataFlowGraphPass { ANALYSIS_ARGUMENT_CHECK_FIELD(argument->fluid_model_program_path); // Load program. auto program = LoadProgramDesc(*argument->fluid_model_program_path); - argument->origin_program_desc.reset(new proto::ProgramDesc(program)); + argument->origin_program_desc.reset( + new framework::proto::ProgramDesc(program)); // Create main data flow graph. if (!argument->main_dfg) { argument->main_dfg.reset(new DataFlowGraph); @@ -78,12 +81,13 @@ class FluidToIrPass final : public DataFlowGraphPass { IRPassManager ir_passes(argument_->Get("ir_program_desc"), nullptr); // Pass the scope from analysis to IR if needed. - if (argument_->Has(ir::kParamScopeAttr)) { + if (argument_->Has(framework::ir::kParamScopeAttr)) { // Here the address is passed, attention that IR doesn't own the scope, so // the real scope in analysis should live during the IR phase. ir_passes.graph().Set( - ir::kParamScopeAttr, - new Scope *(&argument_->Get(ir::kParamScopeAttr))); + framework::ir::kParamScopeAttr, + new framework::Scope *(&argument_->Get( + framework::ir::kParamScopeAttr))); } if (FLAGS_IA_enable_ir) { @@ -95,12 +99,12 @@ class FluidToIrPass final : public DataFlowGraphPass { PADDLE_ENFORCE(argument_->main_dfg.get()); argument_->main_dfg->Build(ir_passes.graph()); // inherit the arguments from ir. - if (ir_passes.graph().Has(ir::kFuseStatisAttr)) { + if (ir_passes.graph().Has(framework::ir::kFuseStatisAttr)) { argument_->Set( - ir::kFuseStatisAttr, + framework::ir::kFuseStatisAttr, new std::unordered_map( ir_passes.graph().Get>( - ir::kFuseStatisAttr))); + framework::ir::kFuseStatisAttr))); } } @@ -112,7 +116,7 @@ class FluidToIrPass final : public DataFlowGraphPass { private: // Load parameters from a single file or from a directory. - bool LoadParams(Scope *scope, const std::string &dir, + bool LoadParams(framework::Scope *scope, const std::string &dir, const std::string &prog_file, const std::string ¶m_file); private: diff --git a/paddle/fluid/inference/analysis/model_store_pass.h b/paddle/fluid/inference/analysis/model_store_pass.h index 3a2869e30b..f14b49e09c 100644 --- a/paddle/fluid/inference/analysis/model_store_pass.h +++ b/paddle/fluid/inference/analysis/model_store_pass.h @@ -19,7 +19,7 @@ #pragma once #include -#include "paddle/fluid/inference/analysis/pass.h" +#include "paddle/fluid/inference/analysis/analysis_pass.h" namespace paddle { namespace inference { diff --git a/paddle/fluid/inference/analysis/pass_manager.cc b/paddle/fluid/inference/analysis/pass_manager.cc index ff5ec94265..759b2b96a1 100644 --- a/paddle/fluid/inference/analysis/pass_manager.cc +++ b/paddle/fluid/inference/analysis/pass_manager.cc @@ -40,17 +40,6 @@ void DfgPassManager::RunAll() { } } -void NodePassManager::RunAll() { - PADDLE_ENFORCE(argument_); - PADDLE_ENFORCE(argument_->main_dfg.get()); - auto trait = GraphTraits(*argument_->main_dfg).nodes_in_DFS(); - for (auto& node : trait) { - for (auto& pass : data_) { - pass->Run(&node); - } - } -} - } // namespace analysis } // namespace inference } // namespace paddle diff --git a/paddle/fluid/inference/analysis/pass_manager.h b/paddle/fluid/inference/analysis/pass_manager.h index 81a17e0287..412747c4fc 100644 --- a/paddle/fluid/inference/analysis/pass_manager.h +++ b/paddle/fluid/inference/analysis/pass_manager.h @@ -33,7 +33,7 @@ limitations under the License. */ #include #include "paddle/fluid/framework/program_desc.h" -#include "paddle/fluid/inference/analysis/pass.h" +#include "paddle/fluid/inference/analysis/analysis_pass.h" namespace paddle { namespace inference { @@ -43,7 +43,7 @@ namespace analysis { * PassManager is the base class for all pass managers, a pass manager has * several Pass-es registered, and execute them in the linear order. */ -class PassManager : public OrderedRegistry { +class PassManager : public OrderedRegistry { public: PassManager() = default; // Call all the passes' Initialize methods. The desc and data_flow_graph are @@ -89,18 +89,6 @@ class DfgPassManager : public PassManager { virtual ~DfgPassManager() = default; }; -/* - * A pass manager that process a Node each time. - */ -class NodePassManager : public PassManager { - public: - NodePassManager() = default; - - void RunAll() override; - - virtual ~NodePassManager() = default; -}; - } // namespace analysis } // namespace inference } // namespace paddle diff --git a/paddle/fluid/inference/analysis/pass_manager_tester.cc b/paddle/fluid/inference/analysis/pass_manager_tester.cc index 13423e4837..72b0fbf7e5 100644 --- a/paddle/fluid/inference/analysis/pass_manager_tester.cc +++ b/paddle/fluid/inference/analysis/pass_manager_tester.cc @@ -34,28 +34,6 @@ class TestDfgPassManager final : public DfgPassManager { std::string description() const override { return "test doc"; } }; -class TestNodePassManager final : public NodePassManager { - public: - virtual ~TestNodePassManager() = default; - - std::string repr() const override { return "test-node-pass-manager"; } - std::string description() const override { return "test doc"; } -}; - -class TestNodePass final : public NodePass { - public: - virtual ~TestNodePass() = default; - - bool Initialize(Argument* argument) override { return true; } - - void Run(Node* node) override { - LOG(INFO) << "- Processing node " << node->repr(); - } - - std::string repr() const override { return "test-node"; } - std::string description() const override { return "some doc"; } -}; - TEST(PassManager, DFG_pass_manager) { TestDfgPassManager manager; DFG_GraphvizDrawPass::Config config("./", "dfg.dot"); @@ -71,19 +49,6 @@ TEST(PassManager, DFG_pass_manager) { manager.RunAll(); } -TEST(PassManager, Node_pass_manager) { - Argument argument(FLAGS_inference_model_dir); - // Pre-process: initialize the DFG with the ProgramDesc first. - FluidToDataFlowGraphPass pass0; - pass0.Initialize(&argument); - pass0.Run(argument.main_dfg.get()); - - TestNodePassManager manager; - manager.Register("test-node-pass", new TestNodePass); - ASSERT_TRUE(manager.Initialize(&argument)); - manager.RunAll(); -} - } // namespace analysis } // namespace inference } // namespace paddle diff --git a/paddle/fluid/inference/analysis/tensorrt_subgraph_node_mark_pass.cc b/paddle/fluid/inference/analysis/tensorrt_subgraph_node_mark_pass.cc index 9f51fafe0b..174c8513f9 100644 --- a/paddle/fluid/inference/analysis/tensorrt_subgraph_node_mark_pass.cc +++ b/paddle/fluid/inference/analysis/tensorrt_subgraph_node_mark_pass.cc @@ -68,7 +68,7 @@ class DfgDebuggerPass : public DFG_GraphvizDrawPass { } }; -Pass *TensorRTSubgraphNodeMarkPass::CreateGraphvizDebugerPass() const { +AnalysisPass *TensorRTSubgraphNodeMarkPass::CreateGraphvizDebugerPass() const { DFG_GraphvizDrawPass::Config config(FLAGS_IA_graphviz_log_root, "tensorrt_marked_node"); return new DfgDebuggerPass(config); diff --git a/paddle/fluid/inference/analysis/tensorrt_subgraph_node_mark_pass.h b/paddle/fluid/inference/analysis/tensorrt_subgraph_node_mark_pass.h index c558a6ebbd..c881a54c24 100644 --- a/paddle/fluid/inference/analysis/tensorrt_subgraph_node_mark_pass.h +++ b/paddle/fluid/inference/analysis/tensorrt_subgraph_node_mark_pass.h @@ -20,7 +20,7 @@ #pragma once #include -#include "paddle/fluid/inference/analysis/pass.h" +#include "paddle/fluid/inference/analysis/analysis_pass.h" #include "paddle/fluid/inference/analysis/subgraph_splitter.h" namespace paddle { @@ -48,7 +48,7 @@ class TensorRTSubgraphNodeMarkPass : public DataFlowGraphPass { return "tensorrt sub-graph mark pass"; } - Pass* CreateGraphvizDebugerPass() const override; + AnalysisPass* CreateGraphvizDebugerPass() const override; bool Finalize() override; private: diff --git a/paddle/fluid/inference/analysis/tensorrt_subgraph_pass.h b/paddle/fluid/inference/analysis/tensorrt_subgraph_pass.h index c6741a9209..219e3f5470 100644 --- a/paddle/fluid/inference/analysis/tensorrt_subgraph_pass.h +++ b/paddle/fluid/inference/analysis/tensorrt_subgraph_pass.h @@ -15,8 +15,8 @@ limitations under the License. */ #pragma once #include +#include "paddle/fluid/inference/analysis/analysis_pass.h" #include "paddle/fluid/inference/analysis/node.h" -#include "paddle/fluid/inference/analysis/pass.h" #include "paddle/fluid/inference/analysis/subgraph_splitter.h" namespace paddle { From fa5036aac828b757bc99dc3dbdcaba258a1ee8df Mon Sep 17 00:00:00 2001 From: luotao1 Date: Wed, 5 Sep 2018 20:32:42 +0800 Subject: [PATCH 10/44] add test_all_data in test_analyzer_ner --- .../inference/analysis/analyzer_ner_tester.cc | 30 +++++++++++++++++-- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/paddle/fluid/inference/analysis/analyzer_ner_tester.cc b/paddle/fluid/inference/analysis/analyzer_ner_tester.cc index 9c8fcf84fe..eaae09b051 100644 --- a/paddle/fluid/inference/analysis/analyzer_ner_tester.cc +++ b/paddle/fluid/inference/analysis/analyzer_ner_tester.cc @@ -25,6 +25,7 @@ DEFINE_string(infer_model, "", "model path"); DEFINE_string(infer_data, "", "data path"); DEFINE_int32(batch_size, 10, "batch size."); DEFINE_int32(repeat, 1, "Running the inference program repeat times."); +DEFINE_bool(test_all_data, false, "Test the all dataset in data file."); namespace paddle { namespace inference { @@ -35,6 +36,7 @@ struct DataRecord { std::vector lod; // two inputs have the same lod info. size_t batch_iter{0}; size_t batch_size{1}; + size_t num_samples; // total number of samples DataRecord() = default; explicit DataRecord(const std::string &path, int batch_size = 1) : batch_size(batch_size) { @@ -81,6 +83,7 @@ struct DataRecord { word_data_all.push_back(std::move(word_data)); mention_data_all.push_back(std::move(mention_data)); } + num_samples = num_lines; } }; @@ -120,12 +123,33 @@ void TestChineseNERPrediction() { auto predictor = CreatePaddlePredictor(config); std::vector input_slots; - DataRecord data(FLAGS_infer_data, FLAGS_batch_size); + std::vector outputs; + Timer timer; + + if (FLAGS_test_all_data) { + LOG(INFO) << "test all data"; + double sum = 0; + size_t num_samples; + for (int i = 0; i < FLAGS_repeat; i++) { + DataRecord data(FLAGS_infer_data, FLAGS_batch_size); + num_samples = data.num_samples; + for (size_t bid = 0; bid < num_samples; ++bid) { + PrepareInputs(&input_slots, &data, FLAGS_batch_size); + timer.tic(); + predictor->Run(input_slots, &outputs); + sum += timer.toc(); + } + } + LOG(INFO) << "total number of samples: " << num_samples; + PrintTime(FLAGS_batch_size, FLAGS_repeat, 1, 0, sum / FLAGS_repeat); + LOG(INFO) << "average latency of each sample: " + << sum / FLAGS_repeat / num_samples; + return; + } // Prepare inputs. + DataRecord data(FLAGS_infer_data, FLAGS_batch_size); PrepareInputs(&input_slots, &data, FLAGS_batch_size); - std::vector outputs; - Timer timer; timer.tic(); for (int i = 0; i < FLAGS_repeat; i++) { predictor->Run(input_slots, &outputs); From 09016df8df61cff85a58c0dfd5a29e4feb575a97 Mon Sep 17 00:00:00 2001 From: tensor-tang Date: Wed, 5 Sep 2018 21:03:53 +0800 Subject: [PATCH 11/44] make analyzer run --- paddle/fluid/inference/analysis/CMakeLists.txt | 14 +------------- .../inference/analysis/analyzer_lac_tester.cc | 10 ++++++++-- 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/paddle/fluid/inference/analysis/CMakeLists.txt b/paddle/fluid/inference/analysis/CMakeLists.txt index 43201fb0bb..dce74ee3f9 100644 --- a/paddle/fluid/inference/analysis/CMakeLists.txt +++ b/paddle/fluid/inference/analysis/CMakeLists.txt @@ -93,19 +93,7 @@ if (NOT EXISTS ${LAC_INSTALL_DIR} AND WITH_TESTING AND WITH_INFERENCE) endif() inference_analysis_test(test_analyzer_lac SRCS analyzer_lac_tester.cc - EXTRA_DEPS paddle_inference_api paddle_fluid_api ir_pass_manager analysis - analysis_predictor - # ir - fc_fuse_pass - fc_lstm_fuse_pass - seq_concat_fc_fuse_pass - graph_viz_pass - infer_clean_graph_pass - graph_pattern_detector - infer_clean_graph_pass - attention_lstm_fuse_pass - paddle_inference_api - pass + EXTRA_DEPS paddle_inference_api paddle_fluid_api ir_pass_manager analysis_predictor ARGS --infer_model=${LAC_INSTALL_DIR}/model --infer_data=${LAC_INSTALL_DIR}/data.txt) diff --git a/paddle/fluid/inference/analysis/analyzer_lac_tester.cc b/paddle/fluid/inference/analysis/analyzer_lac_tester.cc index 2aef25603f..5efee95030 100644 --- a/paddle/fluid/inference/analysis/analyzer_lac_tester.cc +++ b/paddle/fluid/inference/analysis/analyzer_lac_tester.cc @@ -16,6 +16,7 @@ #include #include "paddle/fluid/framework/ir/pass.h" #include "paddle/fluid/inference/analysis/ut_helper.h" +#include "paddle/fluid/inference/api/analysis_predictor.h" #include "paddle/fluid/inference/api/helper.h" #include "paddle/fluid/inference/api/paddle_inference_api.h" #include "paddle/fluid/platform/profiler.h" @@ -170,9 +171,14 @@ void TestLACPrediction(const std::string &model_path, GetOneBatch(&input_slots, &data, batch_size); std::unique_ptr predictor; if (use_analysis) { + AnalysisConfig cfg; + cfg.model_dir = model_path; + cfg.use_gpu = false; + cfg.device = 0; + cfg.specify_input_name = true; + cfg.enable_ir_optim = true; predictor = - CreatePaddlePredictor( - config); + CreatePaddlePredictor(cfg); } else { predictor = CreatePaddlePredictor(config); From f057077c3a7b3c36ea0728d849ec91e5af7814bf Mon Sep 17 00:00:00 2001 From: tensor-tang Date: Wed, 5 Sep 2018 23:38:58 +0800 Subject: [PATCH 12/44] add fuse fc gru pass --- paddle/fluid/framework/ir/fc_gru_fuse_pass.cc | 193 ++++++++++++++++++ paddle/fluid/framework/ir/fc_gru_fuse_pass.h | 50 +++++ .../framework/ir/graph_pattern_detector.cc | 27 +++ .../framework/ir/graph_pattern_detector.h | 2 + 4 files changed, 272 insertions(+) create mode 100644 paddle/fluid/framework/ir/fc_gru_fuse_pass.cc create mode 100644 paddle/fluid/framework/ir/fc_gru_fuse_pass.h diff --git a/paddle/fluid/framework/ir/fc_gru_fuse_pass.cc b/paddle/fluid/framework/ir/fc_gru_fuse_pass.cc new file mode 100644 index 0000000000..1e7b49620c --- /dev/null +++ b/paddle/fluid/framework/ir/fc_gru_fuse_pass.cc @@ -0,0 +1,193 @@ +// Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "paddle/fluid/framework/ir/fc_gru_fuse_pass.h" +#include +#include "paddle/fluid/framework/lod_tensor.h" + +namespace paddle { +namespace framework { +namespace ir { + +std::string GenNodeName(const std::string& prefix, const std::string& name) { + return prefix + "/" + name; +} + +void BuildPattern(PDPattern* pattern, const std::string& name_scope, + bool with_fc_bias) { + PDNode* x = pattern->NewNode(name_scope, "x") + ->assert_is_op_input("mul") + ->assert_var_not_persistable(); + auto* fc_out = patterns::FC(pattern, name_scope, x, with_fc_bias); + fc_out->AsIntermediate(); // fc_out is a tmp var, will be removed after fuse. + patterns::GRU(pattern, name_scope, fc_out); + VLOG(3) << "\n" << pattern->DotString(); +} + +int BuildFusion(Graph* graph, const std::string& name_scope, Scope* scope, + bool with_fc_bias) { + GraphPatternDetector gpd; + auto* pattern = gpd.mutable_pattern(); + + BuildPattern(pattern, name_scope, with_fc_bias); + + // Create New OpDesc + auto gru_creater = [&](int gru, int x, int weight_x, int weight_h, int bias, + int hidden, int fc_bias) { +#define GET_NODE(x) auto* x##_n = graph->RetriveNode(x); + GET_NODE(x); + GET_NODE(weight_x); + GET_NODE(weight_h); + GET_NODE(bias); + GET_NODE(hidden); + GET_NODE(gru); + + OpDesc op_desc; + op_desc.SetType("fusion_gru"); +#define SET_IN(Key, node__) op_desc.SetInput(#Key, {node__##_n->Name()}); + SET_IN(X, x); + SET_IN(WeightX, weight_x); + SET_IN(WeightH, weight_h); + SET_IN(Bias, bias); +#undef SET_IN + if (with_fc_bias) { + // Add FC-bias with LSTM-bias and create a new weight + PADDLE_ENFORCE(scope); + const std::string& new_bias_var = name_scope + "_bias.new"; + auto* bias_var = scope->Var(new_bias_var); + PADDLE_ENFORCE(bias_var); + auto* bias_tensor = bias_var->GetMutable(); + auto* gru_bias_var = scope->FindVar(bias_n->Name()); + PADDLE_ENFORCE(gru_bias_var); + const auto& gru_bias_tenosr = gru_bias_var->Get(); + bias_tensor->Resize(gru_bias_tenosr.dims()); + + GET_NODE(fc_bias); + auto* fc_bias_var = scope->FindVar(fc_bias_n->Name()); + const auto& fc_bias_tensor = fc_bias_var->Get(); + // new bias = fc bias + gru bias + auto* data = bias_tensor->mutable_data(platform::CPUPlace()); + for (int i = 0; i < bias_tensor->numel(); i++) { + data[i] = + fc_bias_tensor.data()[i] + gru_bias_tenosr.data()[i]; + } + op_desc.SetInput("Bias", {new_bias_var}); + } +#undef GET_NODE + + op_desc.SetInput("H0", {}); + op_desc.SetOutput("Hidden", {hidden_n->Name()}); + op_desc.SetAttr("is_reverse", gru_n->Op()->GetAttr("is_reverse")); + // TODO(TJ): This should be a option for infer + op_desc.SetAttr("use_seq", true); + + // Create temp variables. + // TODO(TJ): clean code + scope->Var(name_scope + "/ReorderedH0.new") + ->GetMutable(); + scope->Var(name_scope + "/XX.new")->GetMutable(); + scope->Var(name_scope + "/BatchedInput.new") + ->GetMutable(); + scope->Var(name_scope + "/BatchedOut.new") + ->GetMutable(); + op_desc.SetOutput("ReorderedH0", {name_scope + "/ReorderedH0.new"}); + op_desc.SetOutput("XX", {name_scope + "/XX.new"}); + op_desc.SetOutput("BatchedInput", {name_scope + "/BatchedInput.new"}); + op_desc.SetOutput("BatchedOut", {name_scope + "/BatchedOut.new"}); + + auto* op = graph->CreateOpNode(&op_desc); + PADDLE_ENFORCE(graph->Has(kParamScopeAttr)); + auto* scope = graph->Get(kParamScopeAttr); + + IR_NODE_LINK_TO(x_n, op); + IR_NODE_LINK_TO(weight_x_n, op); + IR_NODE_LINK_TO(weight_h_n, op); + IR_NODE_LINK_TO(bias_n, op); + IR_NODE_LINK_TO(op, hidden_n); + // h0? + return op; + }; + + int fusion_count{0}; + auto handler = [&](const GraphPatternDetector::subgraph_t& subgraph, + Graph* g) { +#define GET_NODE(name__) \ + std::string name__##key = name_scope + "/" + #name__; \ + auto* name__##n = pattern->RetrieveNode(name__##key); \ + PADDLE_ENFORCE(name__##n); \ + PADDLE_ENFORCE(subgraph.count(name__##n)); \ + Node* name__##_n = subgraph.at(name__##n); \ + int name__ __attribute__((unused)) = name__##_n->id(); + + GET_NODE(x); + GET_NODE(w); + GET_NODE(mul); + GET_NODE(fc_out); + GET_NODE(Weight); + GET_NODE(gru); + GET_NODE(Bias); + GET_NODE(Hidden); + + if (with_fc_bias) { + GET_NODE(fc_bias); + GET_NODE(elementwise_add); + gru_creater(gru, x, w, Weight, Bias, Hidden, fc_bias); + // Remove unneeded nodes. + std::unordered_set marked_nodes( + {mul_n, gru_n, elementwise_add_n}); + GraphSafeRemoveNodes(graph, marked_nodes); + } else { + gru_creater(gru, x, w, Weight, Bias, Hidden, -1); + // Remove unneeded nodes. + std::unordered_set marked_nodes({mul_n, gru_n}); + GraphSafeRemoveNodes(graph, marked_nodes); + } +#undef GET_NODE + + ++fusion_count; + }; + + gpd(graph, handler); + + return fusion_count; +} + +std::unique_ptr MulGRUFusePass::ApplyImpl( + std::unique_ptr graph) const { + FusePassBase::Init(name_scope_, graph.get()); + + int fusion_count = BuildFusion(graph.get(), name_scope_, param_scope(), + false /*with_fc_bias*/); + + AddStatis(fusion_count); + return graph; +} + +std::unique_ptr FCGRUFusePass::ApplyImpl( + std::unique_ptr graph) const { + FusePassBase::Init(name_scope_, graph.get()); + + int fusion_count = BuildFusion(graph.get(), name_scope_, param_scope(), + true /*with_fc_bias*/); + + AddStatis(fusion_count); + return graph; +} + +} // namespace ir +} // namespace framework +} // namespace paddle + +REGISTER_PASS(mul_lstm_fuse_pass, paddle::framework::ir::MulGRUFusePass); +REGISTER_PASS(fc_lstm_fuse_pass, paddle::framework::ir::FCGRUFusePass); diff --git a/paddle/fluid/framework/ir/fc_gru_fuse_pass.h b/paddle/fluid/framework/ir/fc_gru_fuse_pass.h new file mode 100644 index 0000000000..63e1c72bfb --- /dev/null +++ b/paddle/fluid/framework/ir/fc_gru_fuse_pass.h @@ -0,0 +1,50 @@ +// Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include +#include "paddle/fluid/framework/ir/fuse_pass_base.h" +#include "paddle/fluid/framework/ir/graph.h" +#include "paddle/fluid/framework/ir/graph_pattern_detector.h" + +namespace paddle { +namespace framework { +namespace ir { + +// The MulGRUFusePass and MulGRUFusePass will fuse to the same FusionGRU op. + +class FCGRUFusePass : public FusePassBase { + public: + virtual ~FCGRUFusePass() {} + + protected: + std::unique_ptr ApplyImpl(std::unique_ptr graph) const; + + const std::string name_scope_{"fc_gru_fuse"}; +}; + +// Just FC without bias +class MulGRUFusePass : public FusePassBase { + public: + virtual ~MulGRUFusePass() {} + + protected: + std::unique_ptr ApplyImpl(std::unique_ptr graph) const; + const std::string name_scope_{"fc_nobias_gru_fuse"}; +}; + +} // namespace ir +} // namespace framework +} // namespace paddle diff --git a/paddle/fluid/framework/ir/graph_pattern_detector.cc b/paddle/fluid/framework/ir/graph_pattern_detector.cc index 434bee4cce..8dfe36f781 100644 --- a/paddle/fluid/framework/ir/graph_pattern_detector.cc +++ b/paddle/fluid/framework/ir/graph_pattern_detector.cc @@ -565,6 +565,7 @@ PDNode* patterns::FC(PDPattern* pattern, const std::string& name_scope, return fc_out; } + PDNode* patterns::LSTM(PDPattern* pattern, const std::string& name_scope, PDNode* x) { x->assert_is_op_input("lstm", "Input"); @@ -589,6 +590,32 @@ PDNode* patterns::LSTM(PDPattern* pattern, const std::string& name_scope, lstm_op->LinksTo({Hidden, Cell, BatchGate, BatchCellPreAct}); return Hidden; } + +PDNode* patterns::GRU(PDPattern* pattern, const std::string& name_scope, + PDNode* x) { + x->assert_is_op_input("gru", "Input"); + auto* gru_op = pattern->NewNode(name_scope, "gru")->assert_is_op("gru"); +#define NEW_NODE(arg__, io__) \ + auto* arg__ = pattern->NewNode(name_scope, #arg__) \ + ->assert_is_op_##io__("gru", #arg__); + + NEW_NODE(Weight, input); + // TODO(Superjomn): upgrade the fuse framework to support optional. + // H0 and bias are optional + NEW_NODE(Bias, input); // also optional + // NEW_NODE(H0, input); + + NEW_NODE(Hidden, output); + // below are intermediate + NEW_NODE(BatchGate, output); + NEW_NODE(BatchResetHiddenPrev, output); + NEW_NODE(BatchHidden, output); + + gru_op->LinksFrom({x, Weight, Bias}); + gru_op->LinksTo({Hidden, BatchGate, BatchResetHiddenPrev, BatchHidden}); + return Hidden; +} + } // namespace ir } // namespace framework } // namespace paddle diff --git a/paddle/fluid/framework/ir/graph_pattern_detector.h b/paddle/fluid/framework/ir/graph_pattern_detector.h index eacea1750f..71e4c36d9b 100644 --- a/paddle/fluid/framework/ir/graph_pattern_detector.h +++ b/paddle/fluid/framework/ir/graph_pattern_detector.h @@ -298,6 +298,8 @@ PDNode* FC(PDPattern* pattern, const std::string& name_scope, PDNode* x, PDNode* LSTM(PDPattern* pattern, const std::string& name_scope, PDNode* x); +PDNode* GRU(PDPattern* pattern, const std::string& name_scope, PDNode* x); + } // namespace patterns #define IR_NODE_LINK_TO(a, b) \ From 74f95b8da05a6a7f7487222b8f004f40a3156c05 Mon Sep 17 00:00:00 2001 From: tensor-tang Date: Thu, 6 Sep 2018 11:09:23 +0800 Subject: [PATCH 13/44] fix redefine macro --- .../framework/ir/graph_pattern_detector.cc | 35 +++++++++---------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/paddle/fluid/framework/ir/graph_pattern_detector.cc b/paddle/fluid/framework/ir/graph_pattern_detector.cc index 8dfe36f781..8b1e653ec8 100644 --- a/paddle/fluid/framework/ir/graph_pattern_detector.cc +++ b/paddle/fluid/framework/ir/graph_pattern_detector.cc @@ -566,25 +566,26 @@ PDNode* patterns::FC(PDPattern* pattern, const std::string& name_scope, return fc_out; } +#define NEW_NODE(op__, arg__, io__) \ + auto* arg__ = pattern->NewNode(name_scope, #arg__) \ + ->assert_is_op_##io__(#op__, #arg__); + PDNode* patterns::LSTM(PDPattern* pattern, const std::string& name_scope, PDNode* x) { x->assert_is_op_input("lstm", "Input"); auto* lstm_op = pattern->NewNode(name_scope, "lstm")->assert_is_op("lstm"); -#define NEW_NODE(arg__, io__) \ - auto* arg__ = pattern->NewNode(name_scope, #arg__) \ - ->assert_is_op_##io__("lstm", #arg__); // Currently, the H0 and C0 are optional // TODO(Superjomn) upgrade the fuse framework to support optional. // NEW_NODE(H0, input); // NEW_NODE(C0, input); - NEW_NODE(Weight, input); - NEW_NODE(Bias, input); + NEW_NODE(lstm, Weight, input); + NEW_NODE(lstm, Bias, input); - NEW_NODE(Hidden, output); - NEW_NODE(Cell, output); - NEW_NODE(BatchGate, output); - NEW_NODE(BatchCellPreAct, output); + NEW_NODE(lstm, Hidden, output); + NEW_NODE(lstm, Cell, output); + NEW_NODE(lstm, BatchGate, output); + NEW_NODE(lstm, BatchCellPreAct, output); lstm_op->LinksFrom({x, Weight, Bias}); lstm_op->LinksTo({Hidden, Cell, BatchGate, BatchCellPreAct}); @@ -595,26 +596,24 @@ PDNode* patterns::GRU(PDPattern* pattern, const std::string& name_scope, PDNode* x) { x->assert_is_op_input("gru", "Input"); auto* gru_op = pattern->NewNode(name_scope, "gru")->assert_is_op("gru"); -#define NEW_NODE(arg__, io__) \ - auto* arg__ = pattern->NewNode(name_scope, #arg__) \ - ->assert_is_op_##io__("gru", #arg__); - NEW_NODE(Weight, input); + NEW_NODE(gru, Weight, input); // TODO(Superjomn): upgrade the fuse framework to support optional. // H0 and bias are optional - NEW_NODE(Bias, input); // also optional + NEW_NODE(gru, Bias, input); // also optional // NEW_NODE(H0, input); - NEW_NODE(Hidden, output); + NEW_NODE(gru, Hidden, output); // below are intermediate - NEW_NODE(BatchGate, output); - NEW_NODE(BatchResetHiddenPrev, output); - NEW_NODE(BatchHidden, output); + NEW_NODE(gru, BatchGate, output); + NEW_NODE(gru, BatchResetHiddenPrev, output); + NEW_NODE(gru, BatchHidden, output); gru_op->LinksFrom({x, Weight, Bias}); gru_op->LinksTo({Hidden, BatchGate, BatchResetHiddenPrev, BatchHidden}); return Hidden; } +#undef NEW_NODE } // namespace ir } // namespace framework From 4d774953c6cb584f084129746b4d2aea0e59237a Mon Sep 17 00:00:00 2001 From: tensor-tang Date: Thu, 6 Sep 2018 11:53:25 +0800 Subject: [PATCH 14/44] enable fc gru fuse pass --- paddle/fluid/framework/ir/CMakeLists.txt | 1 + paddle/fluid/framework/ir/fc_gru_fuse_pass.cc | 18 ++++++------- .../fluid/framework/ir/fc_lstm_fuse_pass.cc | 11 ++++---- paddle/fluid/inference/analysis/analyzer.h | 4 +++ .../inference/analysis/analyzer_lac_tester.cc | 25 +++++++++++++++++++ paddle/fluid/inference/api/CMakeLists.txt | 1 + 6 files changed, 44 insertions(+), 16 deletions(-) diff --git a/paddle/fluid/framework/ir/CMakeLists.txt b/paddle/fluid/framework/ir/CMakeLists.txt index f5235f70ad..6c7f972589 100644 --- a/paddle/fluid/framework/ir/CMakeLists.txt +++ b/paddle/fluid/framework/ir/CMakeLists.txt @@ -24,6 +24,7 @@ pass_library(fc_fuse_pass) pass_library(attention_lstm_fuse_pass) pass_library(infer_clean_graph_pass) pass_library(fc_lstm_fuse_pass) +pass_library(fc_gru_fuse_pass) pass_library(seq_concat_fc_fuse_pass) set(GLOB_PASS_LIB ${PASS_LIBRARY} CACHE INTERNAL "Global PASS library") diff --git a/paddle/fluid/framework/ir/fc_gru_fuse_pass.cc b/paddle/fluid/framework/ir/fc_gru_fuse_pass.cc index 1e7b49620c..4a08beee7d 100644 --- a/paddle/fluid/framework/ir/fc_gru_fuse_pass.cc +++ b/paddle/fluid/framework/ir/fc_gru_fuse_pass.cc @@ -20,12 +20,8 @@ namespace paddle { namespace framework { namespace ir { -std::string GenNodeName(const std::string& prefix, const std::string& name) { - return prefix + "/" + name; -} - -void BuildPattern(PDPattern* pattern, const std::string& name_scope, - bool with_fc_bias) { +static void BuildPattern(PDPattern* pattern, const std::string& name_scope, + bool with_fc_bias) { PDNode* x = pattern->NewNode(name_scope, "x") ->assert_is_op_input("mul") ->assert_var_not_persistable(); @@ -35,8 +31,8 @@ void BuildPattern(PDPattern* pattern, const std::string& name_scope, VLOG(3) << "\n" << pattern->DotString(); } -int BuildFusion(Graph* graph, const std::string& name_scope, Scope* scope, - bool with_fc_bias) { +static int BuildFusion(Graph* graph, const std::string& name_scope, + Scope* scope, bool with_fc_bias) { GraphPatternDetector gpd; auto* pattern = gpd.mutable_pattern(); @@ -108,7 +104,7 @@ int BuildFusion(Graph* graph, const std::string& name_scope, Scope* scope, auto* op = graph->CreateOpNode(&op_desc); PADDLE_ENFORCE(graph->Has(kParamScopeAttr)); - auto* scope = graph->Get(kParamScopeAttr); + // auto* scope = graph->Get(kParamScopeAttr); IR_NODE_LINK_TO(x_n, op); IR_NODE_LINK_TO(weight_x_n, op); @@ -189,5 +185,5 @@ std::unique_ptr FCGRUFusePass::ApplyImpl( } // namespace framework } // namespace paddle -REGISTER_PASS(mul_lstm_fuse_pass, paddle::framework::ir::MulGRUFusePass); -REGISTER_PASS(fc_lstm_fuse_pass, paddle::framework::ir::FCGRUFusePass); +REGISTER_PASS(mul_gru_fuse_pass, paddle::framework::ir::MulGRUFusePass); +REGISTER_PASS(fc_gru_fuse_pass, paddle::framework::ir::FCGRUFusePass); diff --git a/paddle/fluid/framework/ir/fc_lstm_fuse_pass.cc b/paddle/fluid/framework/ir/fc_lstm_fuse_pass.cc index 0d69dfa79a..5fa3fcb9dc 100644 --- a/paddle/fluid/framework/ir/fc_lstm_fuse_pass.cc +++ b/paddle/fluid/framework/ir/fc_lstm_fuse_pass.cc @@ -19,12 +19,13 @@ namespace paddle { namespace framework { namespace ir { -std::string GenNodeName(const std::string& prefix, const std::string& name) { +static std::string GenNodeName(const std::string& prefix, + const std::string& name) { return prefix + "/" + name; } -void BuildPattern(PDPattern* pattern, const std::string& name_scope, - bool with_fc_bias) { +static void BuildPattern(PDPattern* pattern, const std::string& name_scope, + bool with_fc_bias) { PDNode* x = pattern->NewNode(name_scope, "x") ->assert_is_op_input("mul") ->assert_var_not_persistable(); @@ -34,8 +35,8 @@ void BuildPattern(PDPattern* pattern, const std::string& name_scope, // LOG(INFO) << "\n" << pattern->DotString(); } -int BuildFusion(Graph* graph, const std::string& name_scope, Scope* scope, - bool with_fc_bias) { +static int BuildFusion(Graph* graph, const std::string& name_scope, + Scope* scope, bool with_fc_bias) { GraphPatternDetector gpd; auto* pattern = gpd.mutable_pattern(); diff --git a/paddle/fluid/inference/analysis/analyzer.h b/paddle/fluid/inference/analysis/analyzer.h index 3fdd2b9ec7..7800fc90b1 100644 --- a/paddle/fluid/inference/analysis/analyzer.h +++ b/paddle/fluid/inference/analysis/analyzer.h @@ -36,6 +36,8 @@ limitations under the License. */ */ #include +#include +#include #include "paddle/fluid/inference/analysis/flags.h" #include "paddle/fluid/inference/analysis/pass.h" #include "paddle/fluid/inference/analysis/pass_manager.h" @@ -66,6 +68,8 @@ class Analyzer : public OrderedRegistry { "attention_lstm_fuse_pass", // "fc_lstm_fuse_pass", // "mul_lstm_fuse_pass", // + "fc_gru_fuse_pass", // + "mul_gru_fuse_pass", // "seq_concat_fc_fuse_pass", // "fc_fuse_pass", // }}; diff --git a/paddle/fluid/inference/analysis/analyzer_lac_tester.cc b/paddle/fluid/inference/analysis/analyzer_lac_tester.cc index 5efee95030..a6e8351c4f 100644 --- a/paddle/fluid/inference/analysis/analyzer_lac_tester.cc +++ b/paddle/fluid/inference/analysis/analyzer_lac_tester.cc @@ -14,6 +14,7 @@ #include "paddle/fluid/inference/analysis/analyzer.h" #include +#include "paddle/fluid/framework/ir/fuse_pass_base.h" #include "paddle/fluid/framework/ir/pass.h" #include "paddle/fluid/inference/analysis/ut_helper.h" #include "paddle/fluid/inference/api/analysis_predictor.h" @@ -237,6 +238,30 @@ void TestLACPrediction(const std::string &model_path, for (size_t i = 0; i < size; ++i) { EXPECT_EQ(pdata_ref[i], pdata[i]); } + + AnalysisPredictor *analysis_predictor = + dynamic_cast(predictor.get()); + auto &fuse_statis = analysis_predictor->analysis_argument() + .Get>( + framework::ir::kFuseStatisAttr); + for (auto &item : fuse_statis) { + LOG(INFO) << "fused " << item.first << " " << item.second; + } + int num_ops = 0; + for (auto &node : + analysis_predictor->analysis_argument().main_dfg->nodes.nodes()) { + if (node->IsFunction()) { + ++num_ops; + } + } + LOG(INFO) << "has num ops: " << num_ops; + ASSERT_TRUE(fuse_statis.count("fc_fuse")); + ASSERT_TRUE(fuse_statis.count("fc_gru_fuse")); + LOG(INFO) << "fc fuse num:" << fuse_statis.at("fc_fuse"); + LOG(INFO) << "fc gru fuse num:" << fuse_statis.at("fc_gru_fuse"); + + // ASSERT_TRUE(fuse_statis.count("fc_gru_fuse")); + // LOG(INFO) << fuse_statis.at("fc_gru_fuse"); } } diff --git a/paddle/fluid/inference/api/CMakeLists.txt b/paddle/fluid/inference/api/CMakeLists.txt index e976b9397d..330ea04495 100644 --- a/paddle/fluid/inference/api/CMakeLists.txt +++ b/paddle/fluid/inference/api/CMakeLists.txt @@ -50,6 +50,7 @@ cc_library(analysis_predictor SRCS analysis_predictor.cc DEPS paddle_inference_a pass fc_fuse_pass fc_lstm_fuse_pass + fc_gru_fuse_pass seq_concat_fc_fuse_pass graph_viz_pass infer_clean_graph_pass From d4accfa905c0cf301ddf6cda7f4340ebc76198ef Mon Sep 17 00:00:00 2001 From: minqiyang Date: Thu, 6 Sep 2018 13:13:35 +0800 Subject: [PATCH 15/44] Fix random fail of python35 unit test --- .../test_image_classification_vgg.py | 22 ++++++++++++++----- .../test_recognize_digits_mlp.py | 22 ++++++++++++++----- 2 files changed, 34 insertions(+), 10 deletions(-) diff --git a/python/paddle/fluid/tests/book/high-level-api/image_classification/test_image_classification_vgg.py b/python/paddle/fluid/tests/book/high-level-api/image_classification/test_image_classification_vgg.py index dbc7bc06c9..f59f1c5af7 100644 --- a/python/paddle/fluid/tests/book/high-level-api/image_classification/test_image_classification_vgg.py +++ b/python/paddle/fluid/tests/book/high-level-api/image_classification/test_image_classification_vgg.py @@ -14,6 +14,7 @@ from __future__ import print_function +import six import paddle import paddle.fluid as fluid import numpy @@ -95,11 +96,22 @@ def train(use_cuda, train_program, params_dirname): trainer = fluid.Trainer( train_func=train_program, place=place, optimizer_func=optimizer_func) - trainer.train( - reader=train_reader, - num_epochs=1, - event_handler=event_handler, - feed_order=['pixel', 'label']) + if six.PY2: + trainer.train( + reader=train_reader, + num_epochs=1, + event_handler=event_handler, + feed_order=['pixel', 'label']) + else: + import paddle.fluid.core as core + try: + trainer.train( + reader=train_reader, + num_epochs=1, + event_handler=event_handler, + feed_order=['pixel', 'label']) + except core.EnforceNotMet as ex: + assert ("kid scope" in cpt.get_exception_message(ex)) def infer(use_cuda, inference_program, params_dirname=None): diff --git a/python/paddle/fluid/tests/book/high-level-api/recognize_digits/test_recognize_digits_mlp.py b/python/paddle/fluid/tests/book/high-level-api/recognize_digits/test_recognize_digits_mlp.py index b95e7db122..66cb07dd47 100644 --- a/python/paddle/fluid/tests/book/high-level-api/recognize_digits/test_recognize_digits_mlp.py +++ b/python/paddle/fluid/tests/book/high-level-api/recognize_digits/test_recognize_digits_mlp.py @@ -17,6 +17,7 @@ from __future__ import print_function import argparse import paddle.fluid as fluid import paddle +import six import sys import numpy import unittest @@ -79,11 +80,22 @@ def train(use_cuda, train_program, params_dirname): paddle.dataset.mnist.train(), buf_size=500), batch_size=BATCH_SIZE) - trainer.train( - num_epochs=1, - event_handler=event_handler, - reader=train_reader, - feed_order=['img', 'label']) + if six.PY2: + trainer.train( + num_epochs=1, + event_handler=event_handler, + reader=train_reader, + feed_order=['img', 'label']) + else: + import paddle.fluid.core as core + try: + trainer.train( + num_epochs=1, + event_handler=event_handler, + reader=train_reader, + feed_order=['img', 'label']) + except core.EnforceNotMet as ex: + assert ("kid scope" in cpt.get_exception_message(ex)) def infer(use_cuda, inference_program, params_dirname=None): From b5de0166ebd0b03a25c610ea08a0c7d5a14e665e Mon Sep 17 00:00:00 2001 From: minqiyang Date: Thu, 6 Sep 2018 13:47:29 +0800 Subject: [PATCH 16/44] Pass the INFERENCE CI when WITH_INFERENCE is OFF --- paddle/scripts/paddle_build.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/paddle/scripts/paddle_build.sh b/paddle/scripts/paddle_build.sh index 84f9d6671a..2f12411659 100755 --- a/paddle/scripts/paddle_build.sh +++ b/paddle/scripts/paddle_build.sh @@ -554,7 +554,7 @@ function gen_capi_package() { function gen_fluid_inference_lib() { mkdir -p ${PADDLE_ROOT}/build cd ${PADDLE_ROOT}/build - if [ ${WITH_C_API:-OFF} == "OFF" ] ; then + if [[ ${WITH_C_API:-OFF} == "OFF" && ${WITH_INFERENCE:-ON} == "ON" ]] ; then cat < Date: Thu, 6 Sep 2018 13:51:18 +0800 Subject: [PATCH 17/44] Add compat deps --- .../image_classification/test_image_classification_vgg.py | 1 + .../high-level-api/recognize_digits/test_recognize_digits_mlp.py | 1 + 2 files changed, 2 insertions(+) diff --git a/python/paddle/fluid/tests/book/high-level-api/image_classification/test_image_classification_vgg.py b/python/paddle/fluid/tests/book/high-level-api/image_classification/test_image_classification_vgg.py index 2162989cfb..93a7215410 100644 --- a/python/paddle/fluid/tests/book/high-level-api/image_classification/test_image_classification_vgg.py +++ b/python/paddle/fluid/tests/book/high-level-api/image_classification/test_image_classification_vgg.py @@ -109,6 +109,7 @@ def train(use_cuda, train_program, parallel, params_dirname): feed_order=['pixel', 'label']) else: import paddle.fluid.core as core + import paddle.compat as cpt try: trainer.train( reader=train_reader, diff --git a/python/paddle/fluid/tests/book/high-level-api/recognize_digits/test_recognize_digits_mlp.py b/python/paddle/fluid/tests/book/high-level-api/recognize_digits/test_recognize_digits_mlp.py index f318964346..d3e4244621 100644 --- a/python/paddle/fluid/tests/book/high-level-api/recognize_digits/test_recognize_digits_mlp.py +++ b/python/paddle/fluid/tests/book/high-level-api/recognize_digits/test_recognize_digits_mlp.py @@ -92,6 +92,7 @@ def train(use_cuda, train_program, params_dirname, parallel): feed_order=['img', 'label']) else: import paddle.fluid.core as core + import paddle.compat as cpt try: trainer.train( num_epochs=1, From ad9e6476ff55dc7eb91fda177b7be1a83c9c21c1 Mon Sep 17 00:00:00 2001 From: minqiyang Date: Thu, 6 Sep 2018 14:47:41 +0800 Subject: [PATCH 18/44] Force object deletion on trainer in unit test --- .../test_image_classification_vgg.py | 27 +++++++------------ .../test_recognize_digits_mlp.py | 25 ++++++----------- 2 files changed, 17 insertions(+), 35 deletions(-) diff --git a/python/paddle/fluid/tests/book/high-level-api/image_classification/test_image_classification_vgg.py b/python/paddle/fluid/tests/book/high-level-api/image_classification/test_image_classification_vgg.py index 93a7215410..dbd8e5a881 100644 --- a/python/paddle/fluid/tests/book/high-level-api/image_classification/test_image_classification_vgg.py +++ b/python/paddle/fluid/tests/book/high-level-api/image_classification/test_image_classification_vgg.py @@ -14,11 +14,11 @@ from __future__ import print_function -import six import paddle import paddle.fluid as fluid import paddle.fluid.core as core import numpy +import six import os import cifar10_small_test_set @@ -101,23 +101,14 @@ def train(use_cuda, train_program, parallel, params_dirname): optimizer_func=optimizer_func, parallel=parallel) - if six.PY2: - trainer.train( - reader=train_reader, - num_epochs=1, - event_handler=event_handler, - feed_order=['pixel', 'label']) - else: - import paddle.fluid.core as core - import paddle.compat as cpt - try: - trainer.train( - reader=train_reader, - num_epochs=1, - event_handler=event_handler, - feed_order=['pixel', 'label']) - except core.EnforceNotMet as ex: - assert ("kid scope" in cpt.get_exception_message(ex)) + trainer.train( + reader=train_reader, + num_epochs=1, + event_handler=event_handler, + feed_order=['pixel', 'label']) + + if six.PY3: + del trainer def infer(use_cuda, inference_program, parallel, params_dirname=None): diff --git a/python/paddle/fluid/tests/book/high-level-api/recognize_digits/test_recognize_digits_mlp.py b/python/paddle/fluid/tests/book/high-level-api/recognize_digits/test_recognize_digits_mlp.py index d3e4244621..2546fdbb71 100644 --- a/python/paddle/fluid/tests/book/high-level-api/recognize_digits/test_recognize_digits_mlp.py +++ b/python/paddle/fluid/tests/book/high-level-api/recognize_digits/test_recognize_digits_mlp.py @@ -84,23 +84,14 @@ def train(use_cuda, train_program, params_dirname, parallel): paddle.dataset.mnist.train(), buf_size=500), batch_size=BATCH_SIZE) - if six.PY2: - trainer.train( - num_epochs=1, - event_handler=event_handler, - reader=train_reader, - feed_order=['img', 'label']) - else: - import paddle.fluid.core as core - import paddle.compat as cpt - try: - trainer.train( - num_epochs=1, - event_handler=event_handler, - reader=train_reader, - feed_order=['img', 'label']) - except core.EnforceNotMet as ex: - assert ("kid scope" in cpt.get_exception_message(ex)) + trainer.train( + num_epochs=1, + event_handler=event_handler, + reader=train_reader, + feed_order=['img', 'label']) + + if six.PY3: + del trainer def infer(use_cuda, inference_program, parallel, params_dirname=None): From 61cae53e7910465107d8978b348c8e6d70b44e51 Mon Sep 17 00:00:00 2001 From: luotao1 Date: Thu, 6 Sep 2018 14:16:25 +0800 Subject: [PATCH 19/44] support anakin for only-cpu environment --- CMakeLists.txt | 4 +++- cmake/external/anakin.cmake | 19 +++++----------- cmake/inference_lib.cmake | 2 +- paddle/fluid/inference/api/CMakeLists.txt | 22 ++++++++++++++----- .../fluid/inference/api/api_anakin_engine.cc | 7 ++++++ 5 files changed, 34 insertions(+), 20 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b1d0abdf2c..c2fa5420e9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -213,9 +213,11 @@ include(configure) # add paddle env configuration if(WITH_GPU) include(cuda) include(tensorrt) +endif() +if(WITH_MKL OR WITH_MKLML) include(external/anakin) elseif() - set(WITH_ANAKIN OFF CACHE STRING "Anakin is used in GPU only now." FORCE) + set(WITH_ANAKIN OFF CACHE STRING "Anakin is used in MKL only now." FORCE) endif() include(generic) # simplify cmake module diff --git a/cmake/external/anakin.cmake b/cmake/external/anakin.cmake index dc6730662f..5a12c6490e 100644 --- a/cmake/external/anakin.cmake +++ b/cmake/external/anakin.cmake @@ -16,16 +16,6 @@ set(ANAKIN_LIBRARY ${ANAKIN_INSTALL_DIR}) set(ANAKIN_SHARED_LIB ${ANAKIN_LIBRARY}/libanakin.so) set(ANAKIN_SABER_LIB ${ANAKIN_LIBRARY}/libanakin_saber_common.so) -# TODO(luotao): ANAKIN_MODLE_URL etc will move to demo ci later. -set(INFERENCE_URL "http://paddle-inference-dist.bj.bcebos.com") -set(ANAKIN_MODLE_URL "${INFERENCE_URL}/mobilenet_v2.anakin.bin") -set(ANAKIN_RNN_MODLE_URL "${INFERENCE_URL}/anakin_test%2Fditu_rnn.anakin2.model.bin") -set(ANAKIN_RNN_DATA_URL "${INFERENCE_URL}/anakin_test%2Fditu_rnn_data.txt") -execute_process(COMMAND bash -c "mkdir -p ${ANAKIN_SOURCE_DIR}") -execute_process(COMMAND bash -c "cd ${ANAKIN_SOURCE_DIR}; wget -q --no-check-certificate ${ANAKIN_MODLE_URL} -N") -execute_process(COMMAND bash -c "cd ${ANAKIN_SOURCE_DIR}; wget -q --no-check-certificate ${ANAKIN_RNN_MODLE_URL} -N") -execute_process(COMMAND bash -c "cd ${ANAKIN_SOURCE_DIR}; wget -q --no-check-certificate ${ANAKIN_RNN_DATA_URL} -N") - include_directories(${ANAKIN_INCLUDE}) include_directories(${ANAKIN_INCLUDE}/saber/) include_directories(${ANAKIN_INCLUDE}/saber/core/) @@ -48,6 +38,11 @@ set(ANAKIN_COMPILE_EXTRA_FLAGS -Wno-reorder -Wno-error=cpp) +if(WITH_GPU) + set(CMAKE_ARGS_PREFIX -DUSE_GPU_PLACE=YES -DCUDNN_ROOT=${CUDNN_ROOT} -DCUDNN_INCLUDE_DIR=${CUDNN_INCLUDE_DIR}) +else() + set(CMAKE_ARGS_PREFIX -DUSE_GPU_PLACE=NO) +endif() ExternalProject_Add( extern_anakin ${EXTERNAL_PROJECT_LOG_ARGS} @@ -56,13 +51,11 @@ ExternalProject_Add( GIT_TAG "9424277cf9ae180a14aff09560d3cd60a49c76d2" PREFIX ${ANAKIN_SOURCE_DIR} UPDATE_COMMAND "" - CMAKE_ARGS -DUSE_GPU_PLACE=YES + CMAKE_ARGS ${CMAKE_ARGS_PREFIX} -DUSE_X86_PLACE=YES -DBUILD_WITH_UNIT_TEST=NO -DPROTOBUF_ROOT=${THIRD_PARTY_PATH}/install/protobuf -DMKLML_ROOT=${THIRD_PARTY_PATH}/install/mklml - -DCUDNN_ROOT=${CUDNN_ROOT} - -DCUDNN_INCLUDE_DIR=${CUDNN_INCLUDE_DIR} -DENABLE_OP_TIMER=${ANAKIN_ENABLE_OP_TIMER} ${EXTERNAL_OPTIONAL_ARGS} CMAKE_CACHE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=${ANAKIN_INSTALL_DIR} diff --git a/cmake/inference_lib.cmake b/cmake/inference_lib.cmake index f61770514e..6e66ba94ab 100644 --- a/cmake/inference_lib.cmake +++ b/cmake/inference_lib.cmake @@ -145,7 +145,7 @@ copy(memory_lib set(inference_deps paddle_fluid_shared paddle_fluid) set(module "inference/api") -if (WITH_ANAKIN AND WITH_GPU) +if (WITH_ANAKIN AND WITH_MKL) copy(anakin_inference_lib DEPS paddle_inference_api inference_anakin_api SRCS ${PADDLE_BINARY_DIR}/paddle/fluid/inference/api/libinference_anakin_api* # compiled anakin api diff --git a/paddle/fluid/inference/api/CMakeLists.txt b/paddle/fluid/inference/api/CMakeLists.txt index ea00bf3649..907d1163e7 100644 --- a/paddle/fluid/inference/api/CMakeLists.txt +++ b/paddle/fluid/inference/api/CMakeLists.txt @@ -73,7 +73,7 @@ cc_library(paddle_inference_tensorrt_subgraph_engine inference_api_test(test_api_tensorrt_subgraph_engine SRC api_tensorrt_subgraph_engine_tester.cc ARGS test_word2vec) endif() -if (WITH_ANAKIN AND WITH_GPU) # only needed in CI +if (WITH_ANAKIN AND WITH_MKL) # only needed in CI # compile the libinference_anakin_api.a and anakin.so. cc_library(inference_anakin_api SRCS api.cc api_anakin_engine.cc DEPS anakin_shared anakin_saber mklml) cc_library(inference_anakin_api_shared SHARED SRCS api.cc api_anakin_engine.cc DEPS anakin_shared anakin_saber) @@ -83,12 +83,24 @@ if (WITH_ANAKIN AND WITH_GPU) # only needed in CI anakin_target(inference_anakin_api) anakin_target(inference_anakin_api_shared) if (WITH_TESTING) - cc_test(api_anakin_engine_tester SRCS api_anakin_engine_tester.cc - ARGS --model=${ANAKIN_SOURCE_DIR}/mobilenet_v2.anakin.bin - DEPS inference_anakin_api_shared dynload_cuda SERIAL) + # TODO(luotao): ANAKIN_MODLE_URL etc will move to demo ci later. + set(INFERENCE_URL "http://paddle-inference-dist.bj.bcebos.com") + set(ANAKIN_RNN_MODLE_URL "${INFERENCE_URL}/anakin_test%2Fditu_rnn.anakin2.model.bin") + set(ANAKIN_RNN_DATA_URL "${INFERENCE_URL}/anakin_test%2Fditu_rnn_data.txt") + execute_process(COMMAND bash -c "mkdir -p ${ANAKIN_SOURCE_DIR}") + execute_process(COMMAND bash -c "cd ${ANAKIN_SOURCE_DIR}; wget -q --no-check-certificate ${ANAKIN_RNN_MODLE_URL} -N") + execute_process(COMMAND bash -c "cd ${ANAKIN_SOURCE_DIR}; wget -q --no-check-certificate ${ANAKIN_RNN_DATA_URL} -N") + if(WITH_GPU) + set(anakin_test_extra_deps dynload_cuda) + set(ANAKIN_MODLE_URL "${INFERENCE_URL}/mobilenet_v2.anakin.bin") + execute_process(COMMAND bash -c "cd ${ANAKIN_SOURCE_DIR}; wget -q --no-check-certificate ${ANAKIN_MODLE_URL} -N") + cc_test(api_anakin_engine_tester SRCS api_anakin_engine_tester.cc + ARGS --model=${ANAKIN_SOURCE_DIR}/mobilenet_v2.anakin.bin + DEPS inference_anakin_api_shared ${anakin_test_extra_deps} SERIAL) + endif() cc_test(api_anakin_engine_rnn_tester SRCS api_anakin_engine_rnn_tester.cc ARGS --model=${ANAKIN_SOURCE_DIR}/anakin_test%2Fditu_rnn.anakin2.model.bin --datapath=${ANAKIN_SOURCE_DIR}/anakin_test%2Fditu_rnn_data.txt - DEPS inference_anakin_api_shared dynload_cuda SERIAL) + DEPS inference_anakin_api_shared ${anakin_test_extra_deps} SERIAL) endif(WITH_TESTING) endif() diff --git a/paddle/fluid/inference/api/api_anakin_engine.cc b/paddle/fluid/inference/api/api_anakin_engine.cc index ea66aa89b8..43b31269d2 100644 --- a/paddle/fluid/inference/api/api_anakin_engine.cc +++ b/paddle/fluid/inference/api/api_anakin_engine.cc @@ -193,7 +193,9 @@ PaddleInferenceAnakinPredictor::Clone() { return std::move(cls); } +#ifdef PADDLE_WITH_CUDA template class PaddleInferenceAnakinPredictor; +#endif template class PaddleInferenceAnakinPredictor; // A factory to help create difference predictor. @@ -202,10 +204,15 @@ std::unique_ptr CreatePaddlePredictor< AnakinConfig, PaddleEngineKind::kAnakin>(const AnakinConfig &config) { VLOG(3) << "Anakin Predictor create."; if (config.target_type == AnakinConfig::NVGPU) { +#ifdef PADDLE_WITH_CUDA VLOG(3) << "Anakin Predictor create on [ NVIDIA GPU ]."; std::unique_ptr x( new PaddleInferenceAnakinPredictor(config)); return x; +#else + LOG(ERROR) << "AnakinConfig::NVGPU could not used in ONLY-CPU environment"; + return nullptr; +#endif } else if (config.target_type == AnakinConfig::X86) { VLOG(3) << "Anakin Predictor create on [ Intel X86 ]."; std::unique_ptr x( From b0275827981353e3c241f653f9113e7be54988b5 Mon Sep 17 00:00:00 2001 From: chengduo Date: Thu, 6 Sep 2018 19:44:59 +0800 Subject: [PATCH 20/44] add api_doc to rst (#13279) --- doc/fluid/api/layers.rst | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/doc/fluid/api/layers.rst b/doc/fluid/api/layers.rst index ecbd8191cc..6f0267cd7a 100644 --- a/doc/fluid/api/layers.rst +++ b/doc/fluid/api/layers.rst @@ -822,6 +822,14 @@ pad .. autofunction:: paddle.fluid.layers.pad :noindex: +.. _api_fluid_layers_pad_constant_like: + +pad_constant_like +--- + +.. autofunction:: paddle.fluid.layers.pad_constant_like + :noindex: + .. _api_fluid_layers_label_smooth: label_smooth @@ -1145,6 +1153,14 @@ sigmoid .. autofunction:: paddle.fluid.layers.sigmoid :noindex: +.. _api_fluid_layers_hsigmoid: + +hsigmoid +------- + +.. autofunction:: paddle.fluid.layers.hsigmoid + :noindex: + .. _api_fluid_layers_logsigmoid: logsigmoid From 6de0a18d5fc21e06581e87c19e0383b77d1ca1ce Mon Sep 17 00:00:00 2001 From: Yan Chunwei Date: Thu, 6 Sep 2018 21:16:47 +0800 Subject: [PATCH 21/44] Refine/text classification support data (#13256) --- .../fluid/inference/analysis/CMakeLists.txt | 7 +- .../analyzer_text_classification_tester.cc | 67 +++++++++++++------ 2 files changed, 54 insertions(+), 20 deletions(-) diff --git a/paddle/fluid/inference/analysis/CMakeLists.txt b/paddle/fluid/inference/analysis/CMakeLists.txt index b625a617a2..765f8a4486 100644 --- a/paddle/fluid/inference/analysis/CMakeLists.txt +++ b/paddle/fluid/inference/analysis/CMakeLists.txt @@ -100,12 +100,17 @@ inference_analysis_test(test_analyzer_lac SRCS analyzer_lac_tester.cc set(TEXT_CLASSIFICATION_MODEL_URL "http://paddle-inference-dist.bj.bcebos.com/text-classification-Senta.tar.gz") +set(TEXT_CLASSIFICATION_DATA_URL "http://paddle-inference-dist.bj.bcebos.com/text_classification_data.txt.tar.gz") set(TEXT_CLASSIFICATION_INSTALL_DIR "${THIRD_PARTY_PATH}/inference_demo/text_classification" CACHE PATH "Text Classification model and data root." FORCE) if (NOT EXISTS ${TEXT_CLASSIFICATION_INSTALL_DIR} AND WITH_TESTING AND WITH_INFERENCE) inference_download_and_uncompress(${TEXT_CLASSIFICATION_INSTALL_DIR} ${TEXT_CLASSIFICATION_MODEL_URL} "text-classification-Senta.tar.gz") + inference_download_and_uncompress(${TEXT_CLASSIFICATION_INSTALL_DIR} ${TEXT_CLASSIFICATION_DATA_URL} "text_classification_data.txt.tar.gz") endif() inference_analysis_test(test_text_classification SRCS analyzer_text_classification_tester.cc EXTRA_DEPS paddle_inference_api paddle_fluid_api analysis_predictor - ARGS --infer_model=${TEXT_CLASSIFICATION_INSTALL_DIR}/text-classification-Senta) + ARGS --infer_model=${TEXT_CLASSIFICATION_INSTALL_DIR}/text-classification-Senta + --infer_data=${TEXT_CLASSIFICATION_INSTALL_DIR}/data.txt + --topn=1 # Just run top 1 batch. + ) diff --git a/paddle/fluid/inference/analysis/analyzer_text_classification_tester.cc b/paddle/fluid/inference/analysis/analyzer_text_classification_tester.cc index 265e814acd..0e493176c4 100644 --- a/paddle/fluid/inference/analysis/analyzer_text_classification_tester.cc +++ b/paddle/fluid/inference/analysis/analyzer_text_classification_tester.cc @@ -16,8 +16,10 @@ #include #include // use glog instead of PADDLE_ENFORCE to avoid importing other paddle header files. #include +#include #include "paddle/fluid/framework/ir/pass.h" #include "paddle/fluid/inference/analysis/ut_helper.h" +#include "paddle/fluid/inference/api/helper.h" #include "paddle/fluid/inference/api/paddle_inference_api.h" #include "paddle/fluid/inference/api/paddle_inference_pass.h" #include "paddle/fluid/inference/api/timer.h" @@ -26,6 +28,7 @@ DEFINE_string(infer_model, "", "Directory of the inference model."); DEFINE_string(infer_data, "", "Path of the dataset."); DEFINE_int32(batch_size, 1, "batch size."); DEFINE_int32(repeat, 1, "How many times to repeat run."); +DEFINE_int32(topn, -1, "Run top n batches of data to save time"); namespace paddle { @@ -45,41 +48,67 @@ void PrintTime(const double latency, const int bs, const int repeat) { LOG(INFO) << "====================================="; } -void Main(int batch_size) { - // Three sequence inputs. - std::vector input_slots(1); - // one batch starts - // data -- - int64_t data0[] = {0, 1, 2}; - for (auto &input : input_slots) { - input.data.Reset(data0, sizeof(data0)); - input.shape = std::vector({3, 1}); - // dtype -- - input.dtype = PaddleDType::INT64; - // LoD -- - input.lod = std::vector>({{0, 3}}); +struct DataReader { + DataReader(const std::string &path) : file(new std::ifstream(path)) {} + + bool NextBatch(PaddleTensor *tensor, int batch_size) { + PADDLE_ENFORCE_EQ(batch_size, 1); + std::string line; + tensor->lod.clear(); + tensor->lod.emplace_back(std::vector({0})); + std::vector data; + + for (int i = 0; i < batch_size; i++) { + if (!std::getline(*file, line)) return false; + inference::split_to_int64(line, ' ', &data); + } + tensor->lod.front().push_back(data.size()); + + tensor->data.Resize(data.size() * sizeof(int64_t)); + memcpy(tensor->data.data(), data.data(), data.size() * sizeof(int64_t)); + tensor->shape.clear(); + tensor->shape.push_back(data.size()); + tensor->shape.push_back(1); + return true; } + std::unique_ptr file; +}; + +void Main(int batch_size) { // shape -- // Create Predictor -- AnalysisConfig config; config.model_dir = FLAGS_infer_model; config.use_gpu = false; config.enable_ir_optim = true; - config.ir_passes.push_back("fc_lstm_fuse_pass"); auto predictor = CreatePaddlePredictor( config); + std::vector input_slots(1); + // one batch starts + // data -- + auto &input = input_slots[0]; + input.dtype = PaddleDType::INT64; + inference::Timer timer; double sum = 0; std::vector output_slots; - for (int i = 0; i < FLAGS_repeat; i++) { - timer.tic(); - CHECK(predictor->Run(input_slots, &output_slots)); - sum += timer.toc(); + + int num_batches = 0; + for (int t = 0; t < FLAGS_repeat; t++) { + DataReader reader(FLAGS_infer_data); + while (reader.NextBatch(&input, FLAGS_batch_size)) { + if (FLAGS_topn > 0 && num_batches > FLAGS_topn) break; + timer.tic(); + CHECK(predictor->Run(input_slots, &output_slots)); + sum += timer.toc(); + ++num_batches; + } } - PrintTime(sum, batch_size, FLAGS_repeat); + + PrintTime(sum, batch_size, num_batches); // Get output LOG(INFO) << "get outputs " << output_slots.size(); From 6b104c90d353409c2aacd34321bc6cf5407eb0e5 Mon Sep 17 00:00:00 2001 From: tensor-tang Date: Thu, 6 Sep 2018 13:51:34 +0800 Subject: [PATCH 22/44] fix profile --- .../inference/analysis/analyzer_lac_tester.cc | 19 +++++++------------ .../fluid/inference/api/analysis_predictor.cc | 13 +++++++++++++ 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/paddle/fluid/inference/analysis/analyzer_lac_tester.cc b/paddle/fluid/inference/analysis/analyzer_lac_tester.cc index a6e8351c4f..1df1ade25f 100644 --- a/paddle/fluid/inference/analysis/analyzer_lac_tester.cc +++ b/paddle/fluid/inference/analysis/analyzer_lac_tester.cc @@ -178,6 +178,7 @@ void TestLACPrediction(const std::string &model_path, cfg.device = 0; cfg.specify_input_name = true; cfg.enable_ir_optim = true; + cfg.ir_passes.push_back("fc_gru_fuse_pass"); predictor = CreatePaddlePredictor(cfg); } else { @@ -208,13 +209,6 @@ void TestLACPrediction(const std::string &model_path, PrintTime(timer.toc(), batch_size, repeat); // check result - if (use_analysis) { - // run once for comparion as reference - auto ref_predictor = - CreatePaddlePredictor(config); - ref_predictor->Run(input_slots, &ref_outputs_slots); - } - EXPECT_EQ(outputs_slots.size(), 1UL); auto &out = outputs_slots[0]; size_t size = std::accumulate(out.shape.begin(), out.shape.end(), 1, @@ -228,6 +222,10 @@ void TestLACPrediction(const std::string &model_path, } if (use_analysis) { + // run once for comparion as reference + auto ref_predictor = + CreatePaddlePredictor(config); + ref_predictor->Run(input_slots, &ref_outputs_slots); EXPECT_EQ(ref_outputs_slots.size(), outputs_slots.size()); auto &ref_out = ref_outputs_slots[0]; size_t ref_size = @@ -256,12 +254,9 @@ void TestLACPrediction(const std::string &model_path, } LOG(INFO) << "has num ops: " << num_ops; ASSERT_TRUE(fuse_statis.count("fc_fuse")); - ASSERT_TRUE(fuse_statis.count("fc_gru_fuse")); - LOG(INFO) << "fc fuse num:" << fuse_statis.at("fc_fuse"); - LOG(INFO) << "fc gru fuse num:" << fuse_statis.at("fc_gru_fuse"); - // ASSERT_TRUE(fuse_statis.count("fc_gru_fuse")); - // LOG(INFO) << fuse_statis.at("fc_gru_fuse"); + LOG(INFO) << "fc fuse num:" << fuse_statis.at("fc_fuse"); + // LOG(INFO) << "fc gru fuse num:" << fuse_statis.at("fc_gru_fuse"); } } diff --git a/paddle/fluid/inference/api/analysis_predictor.cc b/paddle/fluid/inference/api/analysis_predictor.cc index a8fa677202..82d673fd15 100644 --- a/paddle/fluid/inference/api/analysis_predictor.cc +++ b/paddle/fluid/inference/api/analysis_predictor.cc @@ -22,12 +22,25 @@ #include "paddle/fluid/inference/api/paddle_inference_api.h" #include "paddle/fluid/inference/api/paddle_inference_pass.h" #include "paddle/fluid/inference/utils/singleton.h" +#include "paddle/fluid/platform/profiler.h" + +DECLARE_bool(profile); namespace paddle { bool AnalysisPredictor::Init( const std::shared_ptr& parent_scope) { VLOG(3) << "Predictor::init()"; +#if !defined(_WIN32) + if (FLAGS_profile) { + LOG(WARNING) << "Profiler is actived, might affect the performance"; + LOG(INFO) << "You can turn off by set gflags '-profile false'"; + auto tracking_device = config_.use_gpu ? platform::ProfilerState::kAll + : platform::ProfilerState::kCPU; + platform::EnableProfiler(tracking_device); + } +#endif + if (config_.use_gpu) { place_ = paddle::platform::CUDAPlace(config_.device); LOG(WARNING) << "ir optimize only supports CPU currently"; From ca30127e0a048de5e56e249d54d8836422ac2140 Mon Sep 17 00:00:00 2001 From: tensor-tang Date: Fri, 7 Sep 2018 00:03:13 +0800 Subject: [PATCH 23/44] fix compile error undef registrar pass --- paddle/fluid/inference/analysis/analyzer.h | 1 - paddle/fluid/inference/api/CMakeLists.txt | 14 +++++++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/paddle/fluid/inference/analysis/analyzer.h b/paddle/fluid/inference/analysis/analyzer.h index 6189548a7b..399afbe64a 100644 --- a/paddle/fluid/inference/analysis/analyzer.h +++ b/paddle/fluid/inference/analysis/analyzer.h @@ -38,7 +38,6 @@ limitations under the License. */ #include #include #include - #include "paddle/fluid/inference/analysis/analysis_pass.h" #include "paddle/fluid/inference/analysis/flags.h" #include "paddle/fluid/inference/analysis/pass_manager.h" diff --git a/paddle/fluid/inference/api/CMakeLists.txt b/paddle/fluid/inference/api/CMakeLists.txt index b69948f40a..f944c9fdec 100644 --- a/paddle/fluid/inference/api/CMakeLists.txt +++ b/paddle/fluid/inference/api/CMakeLists.txt @@ -44,7 +44,19 @@ function(inference_api_test TARGET_NAME) endfunction(inference_api_test) cc_library(paddle_inference_api SRCS api.cc api_impl.cc helper.cc DEPS lod_tensor) -cc_library(analysis_predictor SRCS analysis_predictor.cc DEPS paddle_inference_api analysis) +cc_library(analysis_predictor SRCS analysis_predictor.cc DEPS paddle_inference_api + analysis + ir_pass_manager + pass + fc_fuse_pass + fc_lstm_fuse_pass + fc_gru_fuse_pass + seq_concat_fc_fuse_pass + graph_viz_pass + infer_clean_graph_pass + graph_pattern_detector + infer_clean_graph_pass + attention_lstm_fuse_pass) cc_test(test_paddle_inference_api SRCS api_tester.cc From 227d8066e18d5ba751d4c2d5e2113b2efd6e6d41 Mon Sep 17 00:00:00 2001 From: Yi Wang Date: Thu, 6 Sep 2018 12:07:23 -0700 Subject: [PATCH 24/44] Fix a small bug in the example code snippet (#13286) --- doc/survey/dynamic_graph.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/survey/dynamic_graph.md b/doc/survey/dynamic_graph.md index 6b80b014b1..d03212007a 100644 --- a/doc/survey/dynamic_graph.md +++ b/doc/survey/dynamic_graph.md @@ -30,7 +30,7 @@ x = Variable(randn(20, 1))) label = Variable(randint(1)) W_1, W_2 = Variable(randn(20, 20)), Variable(randn(10, 20)) h = matmul(W_1, x) -pred = matmul(W_2, x) +pred = matmul(W_2, h) loss = softmax(pred, label) loss.backward() ``` From 2bb0ac927b006ed375322fbd5fe4b0cbc72f39fa Mon Sep 17 00:00:00 2001 From: minqiyang Date: Fri, 7 Sep 2018 09:55:17 +0800 Subject: [PATCH 25/44] Polish code --- .../image_classification/test_image_classification_vgg.py | 8 +++++--- .../recognize_digits/test_recognize_digits_mlp.py | 8 +++++--- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/python/paddle/fluid/tests/book/high-level-api/image_classification/test_image_classification_vgg.py b/python/paddle/fluid/tests/book/high-level-api/image_classification/test_image_classification_vgg.py index dbd8e5a881..548ebd6710 100644 --- a/python/paddle/fluid/tests/book/high-level-api/image_classification/test_image_classification_vgg.py +++ b/python/paddle/fluid/tests/book/high-level-api/image_classification/test_image_classification_vgg.py @@ -107,8 +107,7 @@ def train(use_cuda, train_program, parallel, params_dirname): event_handler=event_handler, feed_order=['pixel', 'label']) - if six.PY3: - del trainer + return trainer def infer(use_cuda, inference_program, parallel, params_dirname=None): @@ -132,12 +131,15 @@ def main(use_cuda, parallel): save_path = "image_classification_vgg.inference.model" os.environ['CPU_NUM'] = str(4) - train( + trainer = train( use_cuda=use_cuda, train_program=train_network, params_dirname=save_path, parallel=parallel) + if six.PY3: + del trainer + # FIXME(zcd): in the inference stage, the number of # input data is one, it is not appropriate to use parallel. if parallel and use_cuda: diff --git a/python/paddle/fluid/tests/book/high-level-api/recognize_digits/test_recognize_digits_mlp.py b/python/paddle/fluid/tests/book/high-level-api/recognize_digits/test_recognize_digits_mlp.py index 2546fdbb71..1e1069d5f6 100644 --- a/python/paddle/fluid/tests/book/high-level-api/recognize_digits/test_recognize_digits_mlp.py +++ b/python/paddle/fluid/tests/book/high-level-api/recognize_digits/test_recognize_digits_mlp.py @@ -90,8 +90,7 @@ def train(use_cuda, train_program, params_dirname, parallel): reader=train_reader, feed_order=['img', 'label']) - if six.PY3: - del trainer + return trainer def infer(use_cuda, inference_program, parallel, params_dirname=None): @@ -117,12 +116,15 @@ def main(use_cuda, parallel): # call train() with is_local argument to run distributed train os.environ['CPU_NUM'] = str(4) - train( + trainer = train( use_cuda=use_cuda, train_program=train_program, params_dirname=params_dirname, parallel=parallel) + if six.PY3: + del trainer + # FIXME(zcd): in the inference stage, the number of # input data is one, it is not appropriate to use parallel. if parallel and use_cuda: From e2d325ac08e23acb6ffb1755295197a4cb7d63f3 Mon Sep 17 00:00:00 2001 From: Yan Chunwei Date: Fri, 7 Sep 2018 10:05:46 +0800 Subject: [PATCH 26/44] refactor pass_library (#13261) --- paddle/fluid/framework/ir/CMakeLists.txt | 29 +++++++++++++++--------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/paddle/fluid/framework/ir/CMakeLists.txt b/paddle/fluid/framework/ir/CMakeLists.txt index f5235f70ad..cb77637d67 100644 --- a/paddle/fluid/framework/ir/CMakeLists.txt +++ b/paddle/fluid/framework/ir/CMakeLists.txt @@ -1,14 +1,21 @@ set(pass_file ${PADDLE_BINARY_DIR}/paddle/fluid/inference/api/paddle_inference_pass.h) file(WRITE ${pass_file} "// Generated by the paddle/fluid/framework/ir/CMakeLists.txt. DO NOT EDIT!\n\n") file(APPEND ${pass_file} "\#include \"paddle/fluid/framework/ir/pass.h\"\n") -function(pass_library TARGET) + + +# Usage: pass_library(target inference) will append to paddle_inference_pass.h +function(pass_library TARGET DEST) set(options "") set(oneValueArgs "") set(multiValueArgs SRCS DEPS) cmake_parse_arguments(op_library "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) - cc_library(${TARGET} SRCS ${TARGET}.cc DEPS graph_pattern_detector pass) - file(APPEND ${pass_file} "USE_PASS(${TARGET});\n") - set(PASS_LIBRARY ${TARGET} ${PASS_LIBRARY} PARENT_SCOPE) + cc_library(${TARGET} SRCS ${TARGET}.cc DEPS graph_pattern_detector pass ${op_library_DEPS}) + # add more DEST here, such as train, dist and collect USE_PASS into a file automatically. + if (${DEST} STREQUAL "base" OR ${DEST} STREQUAL "inference") + message(STATUS "add pass ${TARGET} ${DEST}") + file(APPEND ${pass_file} "USE_PASS(${TARGET});\n") + set(PASS_LIBRARY ${TARGET} ${PASS_LIBRARY} PARENT_SCOPE) + endif() endfunction() cc_library(node SRCS node.cc DEPS proto_desc) @@ -18,13 +25,13 @@ cc_library(pass SRCS pass.cc DEPS graph node graph_helper) cc_library(graph_traits SRCS graph_traits.cc DEPS graph) cc_library(graph_pattern_detector SRCS graph_pattern_detector.cc DEPS graph graph_helper graph_traits) -pass_library(graph_to_program_pass) -pass_library(graph_viz_pass) -pass_library(fc_fuse_pass) -pass_library(attention_lstm_fuse_pass) -pass_library(infer_clean_graph_pass) -pass_library(fc_lstm_fuse_pass) -pass_library(seq_concat_fc_fuse_pass) +pass_library(graph_to_program_pass base) +pass_library(graph_viz_pass base) +pass_library(fc_fuse_pass inference) +pass_library(attention_lstm_fuse_pass inference) +pass_library(infer_clean_graph_pass inference) +pass_library(fc_lstm_fuse_pass inference) +pass_library(seq_concat_fc_fuse_pass inference) set(GLOB_PASS_LIB ${PASS_LIBRARY} CACHE INTERNAL "Global PASS library") cc_test(pass_test SRCS pass_test.cc DEPS graph pass graph_helper) From 7eebb905235c5780350d92902776a4e0c267c87f Mon Sep 17 00:00:00 2001 From: tensor-tang Date: Fri, 7 Sep 2018 11:53:19 +0800 Subject: [PATCH 27/44] fix conflicts --- paddle/fluid/inference/analysis/analyzer_lac_tester.cc | 2 +- paddle/fluid/inference/api/helper.h | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/paddle/fluid/inference/analysis/analyzer_lac_tester.cc b/paddle/fluid/inference/analysis/analyzer_lac_tester.cc index 5740faa746..7917152428 100644 --- a/paddle/fluid/inference/analysis/analyzer_lac_tester.cc +++ b/paddle/fluid/inference/analysis/analyzer_lac_tester.cc @@ -192,7 +192,7 @@ void TestLACPrediction(const std::string &model_path, sum += timer.toc(); } } - PrintTime(sum, batch_size, repeat); + PrintTime(batch_size, repeat, 1, 0, sum / batch_size); return; } timer.tic(); diff --git a/paddle/fluid/inference/api/helper.h b/paddle/fluid/inference/api/helper.h index 2c2ac656e8..0ab2542f34 100644 --- a/paddle/fluid/inference/api/helper.h +++ b/paddle/fluid/inference/api/helper.h @@ -124,9 +124,11 @@ std::string DescribeTensor(const PaddleTensor &tensor) { void PrintTime(int batch_size, int repeat, int num_threads, int tid, double latency) { + LOG(INFO) << "====================================="; LOG(INFO) << "batch_size: " << batch_size << ", repeat: " << repeat << ", threads: " << num_threads << ", thread id: " << tid << ", latency: " << latency << "ms"; + LOG(INFO) << "====================================="; } } // namespace inference From d4c3fe9a44503263a5560ab1ddf2ccebd17ed79e Mon Sep 17 00:00:00 2001 From: luotao1 Date: Fri, 7 Sep 2018 11:57:39 +0800 Subject: [PATCH 28/44] clean api_anakin_engine_rnn_tester --- .../api/api_anakin_engine_rnn_tester.cc | 94 +++---------------- 1 file changed, 13 insertions(+), 81 deletions(-) diff --git a/paddle/fluid/inference/api/api_anakin_engine_rnn_tester.cc b/paddle/fluid/inference/api/api_anakin_engine_rnn_tester.cc index 6183864234..98c74aaa56 100644 --- a/paddle/fluid/inference/api/api_anakin_engine_rnn_tester.cc +++ b/paddle/fluid/inference/api/api_anakin_engine_rnn_tester.cc @@ -20,71 +20,16 @@ limitations under the License. */ #include #include // NOLINT #include -#include "framework/core/net/net.h" +#include "paddle/fluid/inference/api/helper.h" #include "paddle/fluid/inference/api/paddle_inference_api.h" +#include "paddle/fluid/inference/api/timer.h" +#include "utils/logger/logger.h" DEFINE_string(model, "", "Directory of the inference model."); DEFINE_string(datapath, "", "Path of the dataset."); DEFINE_int32(batch_size, 1, "batch size."); DEFINE_int32(repeat, 1, "Running the inference program repeat times."); -// Timer for timer -class Timer { - public: - double start; - double startu; - void tic() { - struct timeval tp; - gettimeofday(&tp, NULL); - start = tp.tv_sec; - startu = tp.tv_usec; - } - double toc() { - struct timeval tp; - gettimeofday(&tp, NULL); - double used_time_ms = - (tp.tv_sec - start) * 1000.0 + (tp.tv_usec - startu) / 1000.0; - return used_time_ms; - } -}; - -std::vector string_split(std::string in_str, - std::string delimiter) { - std::vector seq; - int found = in_str.find(delimiter); - int pre_found = -1; - while (found != std::string::npos) { - if (pre_found == -1) { - seq.push_back(in_str.substr(0, found)); - } else { - seq.push_back(in_str.substr(pre_found + delimiter.length(), - found - delimiter.length() - pre_found)); - } - pre_found = found; - found = in_str.find(delimiter, pre_found + delimiter.length()); - } - seq.push_back( - in_str.substr(pre_found + 1, in_str.length() - (pre_found + 1))); - return seq; -} -std::vector string_split( - std::string in_str, std::vector& delimiter) { // NOLINT - std::vector in; - std::vector out; - out.push_back(in_str); - for (auto del : delimiter) { - in = out; - out.clear(); - for (auto s : in) { - auto out_s = string_split(s, del); - for (auto o : out_s) { - out.push_back(o); - } - } - } - return out; -} - class Data { public: Data(std::string file_name, int batch_size) @@ -120,36 +65,24 @@ void Data::get_batch_data( week_fea.clear(); time_fea.clear(); while (_file.getline(buf, 10000)) { - std::string s = buf; - std::vector deli_vec = {":"}; - std::vector data_vec = string_split(s, deli_vec); + std::vector data_vec; + paddle::inference::split(buf, ':', &data_vec); std::vector seq; - seq = string_split(data_vec[0], {"|"}); + paddle::inference::split(data_vec[0], '|', &seq); for (auto link : seq) { - std::vector data = string_split(link, ","); std::vector vec; - for (int i = 0; i < data.size(); i++) { - vec.push_back(atof(data[i].c_str())); - } + paddle::inference::split_to_float(link, ',', &vec); fea.push_back(vec); } - std::vector week_data; - std::vector time_data; - week_data = string_split(data_vec[2], ","); std::vector vec_w; - for (int i = 0; i < week_data.size(); i++) { - vec_w.push_back(atof(week_data[i].c_str())); - } + paddle::inference::split_to_float(data_vec[2], ',', &vec_w); week_fea.push_back(vec_w); - time_data = string_split(data_vec[1], ","); std::vector vec_t; - for (int i = 0; i < time_data.size(); i++) { - vec_t.push_back(atof(time_data[i].c_str())); - } + paddle::inference::split_to_float(data_vec[1], ',', &vec_t); time_fea.push_back(vec_t); cum += seq.size(); @@ -275,14 +208,13 @@ void single_test() { inputs.push_back(tensor_2); inputs.push_back(tensor_0); - Timer timer; + paddle::inference::Timer timer; timer.tic(); for (int i = 0; i < FLAGS_repeat; i++) predictor->Run(inputs, &outputs); - LOG(INFO) << "batch_size = " << FLAGS_batch_size - << ", repeat = " << FLAGS_repeat - << ", sequence_length = " << seq_offset[seq_offset.size() - 1] - << ", latency: " << timer.toc() / FLAGS_repeat << "ms"; + paddle::inference::PrintTime(FLAGS_batch_size, FLAGS_repeat, 1, 0, + timer.toc() / FLAGS_repeat); + LOG(INFO) << "sequence_length = " << seq_offset[seq_offset.size() - 1]; float* data_o = static_cast(outputs[0].data.data()); VLOG(3) << "outputs[0].data.length() = " << outputs[0].data.length(); From c9bd2d50f1d9c0db255ebc132b7c74438f3b3bba Mon Sep 17 00:00:00 2001 From: tensor-tang Date: Fri, 7 Sep 2018 12:51:36 +0800 Subject: [PATCH 29/44] refine fc and gru pattern --- .../framework/ir/graph_pattern_detector.cc | 45 +++++++++---------- 1 file changed, 20 insertions(+), 25 deletions(-) diff --git a/paddle/fluid/framework/ir/graph_pattern_detector.cc b/paddle/fluid/framework/ir/graph_pattern_detector.cc index 37566b7621..69a323a8bd 100644 --- a/paddle/fluid/framework/ir/graph_pattern_detector.cc +++ b/paddle/fluid/framework/ir/graph_pattern_detector.cc @@ -519,50 +519,41 @@ bool VarLinksFromOp(Node* node, const std::string& op_type) { PDNode* patterns::FC(PDPattern* pattern, const std::string& name_scope, PDNode* x, bool with_bias) { - // Create Operators - PDNode* elementwise_add_op{nullptr}; + // mul op auto* mul_op = pattern->NewNode(name_scope, "mul")->assert_is_op("mul"); - if (with_bias) { - elementwise_add_op = pattern->NewNode(name_scope, "elementwise_add") - ->assert_is_op("elementwise_add"); - } - // Create variables - // w auto* mul_weight_var = pattern->NewNode(name_scope, "w") ->AsInput() ->assert_is_persistable_var() - ->assert_is_op_nth_input("mul", "Y", 0); - PDNode* mul_out_var{nullptr}; + ->assert_is_op_input("mul", "Y"); + + PDNode* fc_out{nullptr}; if (with_bias) { + PDNode* elementwise_add_op{nullptr}; + PDNode *mul_out_var{nullptr}, *bias{nullptr}; + elementwise_add_op = pattern->NewNode(name_scope, "elementwise_add") + ->assert_is_op("elementwise_add"); // intermediate variable, will be removed in the IR after fuse. mul_out_var = pattern->NewNode(name_scope, "mul_out") ->AsIntermediate() ->assert_is_only_output_of_op("mul") - ->assert_is_op_input("elementwise_add"); - } - PDNode *bias{nullptr}, *fc_out{nullptr}; - if (with_bias) { + ->assert_is_op_input("elementwise_add", "X"); // bias bias = pattern->NewNode(name_scope, "fc_bias") - ->assert_is_op_input("elementwise_add") - ->AsInput(); + ->AsInput() + ->assert_is_persistable_var() + ->assert_is_op_input("elementwise_add", "Y"); // output fc_out = pattern->NewNode(name_scope, "fc_out") ->AsOutput() - ->assert_is_op_output("elementwise_add"); + ->assert_is_op_output("elementwise_add", "Out"); + mul_op->LinksFrom({x, mul_weight_var}).LinksTo({mul_out_var}); + elementwise_add_op->LinksFrom({mul_out_var, bias}).LinksTo({fc_out}); } else { fc_out = pattern->NewNode(name_scope, "fc_out") ->AsOutput() - ->assert_is_op_output("mul"); - } - - if (with_bias) { - mul_op->LinksFrom({mul_weight_var, x}).LinksTo({mul_out_var}); - elementwise_add_op->LinksFrom({mul_out_var, bias}).LinksTo({fc_out}); - } else { + ->assert_is_op_output("mul", "Out"); mul_op->LinksFrom({mul_weight_var, x}).LinksTo({fc_out}); } - return fc_out; } @@ -609,6 +600,10 @@ PDNode* patterns::GRU(PDPattern* pattern, const std::string& name_scope, NEW_NODE(gru, BatchResetHiddenPrev, output); NEW_NODE(gru, BatchHidden, output); + BatchGate->AsIntermediate(); + BatchResetHiddenPrev->AsIntermediate(); + BatchHidden->AsIntermediate(); + gru_op->LinksFrom({x, Weight, Bias}); gru_op->LinksTo({Hidden, BatchGate, BatchResetHiddenPrev, BatchHidden}); return Hidden; From 8ef1f9f9b837639737bb8d3e119eeb04f3c4c303 Mon Sep 17 00:00:00 2001 From: minqiyang Date: Fri, 7 Sep 2018 14:22:44 +0800 Subject: [PATCH 30/44] Polish code --- .../test_image_classification_vgg.py | 11 ++++++----- .../recognize_digits/test_recognize_digits_mlp.py | 11 ++++++----- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/python/paddle/fluid/tests/book/high-level-api/image_classification/test_image_classification_vgg.py b/python/paddle/fluid/tests/book/high-level-api/image_classification/test_image_classification_vgg.py index 548ebd6710..2767e8b5d9 100644 --- a/python/paddle/fluid/tests/book/high-level-api/image_classification/test_image_classification_vgg.py +++ b/python/paddle/fluid/tests/book/high-level-api/image_classification/test_image_classification_vgg.py @@ -107,7 +107,11 @@ def train(use_cuda, train_program, parallel, params_dirname): event_handler=event_handler, feed_order=['pixel', 'label']) - return trainer + def _del_trainer(trainer): + del trainer + + if six.PY3: + _del_trainer(trainer) def infer(use_cuda, inference_program, parallel, params_dirname=None): @@ -131,15 +135,12 @@ def main(use_cuda, parallel): save_path = "image_classification_vgg.inference.model" os.environ['CPU_NUM'] = str(4) - trainer = train( + train( use_cuda=use_cuda, train_program=train_network, params_dirname=save_path, parallel=parallel) - if six.PY3: - del trainer - # FIXME(zcd): in the inference stage, the number of # input data is one, it is not appropriate to use parallel. if parallel and use_cuda: diff --git a/python/paddle/fluid/tests/book/high-level-api/recognize_digits/test_recognize_digits_mlp.py b/python/paddle/fluid/tests/book/high-level-api/recognize_digits/test_recognize_digits_mlp.py index 1e1069d5f6..b784657466 100644 --- a/python/paddle/fluid/tests/book/high-level-api/recognize_digits/test_recognize_digits_mlp.py +++ b/python/paddle/fluid/tests/book/high-level-api/recognize_digits/test_recognize_digits_mlp.py @@ -90,7 +90,11 @@ def train(use_cuda, train_program, params_dirname, parallel): reader=train_reader, feed_order=['img', 'label']) - return trainer + def _del_trainer(trainer): + del trainer + + if six.PY3: + _del_trainer(trainer) def infer(use_cuda, inference_program, parallel, params_dirname=None): @@ -116,15 +120,12 @@ def main(use_cuda, parallel): # call train() with is_local argument to run distributed train os.environ['CPU_NUM'] = str(4) - trainer = train( + train( use_cuda=use_cuda, train_program=train_program, params_dirname=params_dirname, parallel=parallel) - if six.PY3: - del trainer - # FIXME(zcd): in the inference stage, the number of # input data is one, it is not appropriate to use parallel. if parallel and use_cuda: From 2720330dc57fd54ea1e27b0c10801089da1cf738 Mon Sep 17 00:00:00 2001 From: minqiyang Date: Fri, 7 Sep 2018 14:25:25 +0800 Subject: [PATCH 31/44] Disable random fail case --- .../test_image_classification_vgg.py | 9 ++------- .../recognize_digits/test_recognize_digits_mlp.py | 9 ++------- 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/python/paddle/fluid/tests/book/high-level-api/image_classification/test_image_classification_vgg.py b/python/paddle/fluid/tests/book/high-level-api/image_classification/test_image_classification_vgg.py index 2767e8b5d9..d4e6742b78 100644 --- a/python/paddle/fluid/tests/book/high-level-api/image_classification/test_image_classification_vgg.py +++ b/python/paddle/fluid/tests/book/high-level-api/image_classification/test_image_classification_vgg.py @@ -107,12 +107,6 @@ def train(use_cuda, train_program, parallel, params_dirname): event_handler=event_handler, feed_order=['pixel', 'label']) - def _del_trainer(trainer): - del trainer - - if six.PY3: - _del_trainer(trainer) - def infer(use_cuda, inference_program, parallel, params_dirname=None): place = fluid.CUDAPlace(0) if use_cuda else fluid.CPUPlace() @@ -158,4 +152,5 @@ if __name__ == '__main__': for parallel in (False, True): if use_cuda and not core.is_compiled_with_cuda(): continue - main(use_cuda=use_cuda, parallel=parallel) + if six.PY2: + main(use_cuda=use_cuda, parallel=parallel) diff --git a/python/paddle/fluid/tests/book/high-level-api/recognize_digits/test_recognize_digits_mlp.py b/python/paddle/fluid/tests/book/high-level-api/recognize_digits/test_recognize_digits_mlp.py index b784657466..811f17b5ab 100644 --- a/python/paddle/fluid/tests/book/high-level-api/recognize_digits/test_recognize_digits_mlp.py +++ b/python/paddle/fluid/tests/book/high-level-api/recognize_digits/test_recognize_digits_mlp.py @@ -90,12 +90,6 @@ def train(use_cuda, train_program, params_dirname, parallel): reader=train_reader, feed_order=['img', 'label']) - def _del_trainer(trainer): - del trainer - - if six.PY3: - _del_trainer(trainer) - def infer(use_cuda, inference_program, parallel, params_dirname=None): place = fluid.CUDAPlace(0) if use_cuda else fluid.CPUPlace() @@ -143,4 +137,5 @@ if __name__ == '__main__': for parallel in (False, True): if use_cuda and not core.is_compiled_with_cuda(): continue - main(use_cuda=use_cuda, parallel=parallel) + if six.PY2: + main(use_cuda=use_cuda, parallel=parallel) From df0c695618696378c8320dd85661fdaa276e7407 Mon Sep 17 00:00:00 2001 From: tensor-tang Date: Fri, 7 Sep 2018 12:53:15 +0800 Subject: [PATCH 32/44] fix fusion gru pass and enable it --- paddle/fluid/framework/ir/fc_gru_fuse_pass.cc | 98 +++++++++++-------- .../inference/analysis/analyzer_lac_tester.cc | 1 - 2 files changed, 56 insertions(+), 43 deletions(-) diff --git a/paddle/fluid/framework/ir/fc_gru_fuse_pass.cc b/paddle/fluid/framework/ir/fc_gru_fuse_pass.cc index 4a08beee7d..90d8d5c042 100644 --- a/paddle/fluid/framework/ir/fc_gru_fuse_pass.cc +++ b/paddle/fluid/framework/ir/fc_gru_fuse_pass.cc @@ -28,7 +28,7 @@ static void BuildPattern(PDPattern* pattern, const std::string& name_scope, auto* fc_out = patterns::FC(pattern, name_scope, x, with_fc_bias); fc_out->AsIntermediate(); // fc_out is a tmp var, will be removed after fuse. patterns::GRU(pattern, name_scope, fc_out); - VLOG(3) << "\n" << pattern->DotString(); + VLOG(3) << "fc_gru pattern \n" << pattern->DotString(); } static int BuildFusion(Graph* graph, const std::string& name_scope, @@ -51,65 +51,72 @@ static int BuildFusion(Graph* graph, const std::string& name_scope, OpDesc op_desc; op_desc.SetType("fusion_gru"); + +#define NEW_NAME(x) name_scope + "/at." #x ".new" #define SET_IN(Key, node__) op_desc.SetInput(#Key, {node__##_n->Name()}); SET_IN(X, x); SET_IN(WeightX, weight_x); SET_IN(WeightH, weight_h); - SET_IN(Bias, bias); + if (with_fc_bias) { + op_desc.SetInput("Bias", {NEW_NAME(bias) + bias_n->Name()}); + } else { + SET_IN(Bias, bias); + } #undef SET_IN + op_desc.SetInput("H0", {}); + op_desc.SetOutput("Hidden", {hidden_n->Name()}); + op_desc.SetAttr("is_reverse", gru_n->Op()->GetAttr("is_reverse")); + // TODO(TJ): This should be a option for infer + op_desc.SetAttr("use_seq", true); + +#define SET_IMTERMEDIATE_OUT(key) op_desc.SetOutput(#key, {NEW_NAME(key)}) + SET_IMTERMEDIATE_OUT(ReorderedH0); + SET_IMTERMEDIATE_OUT(XX); + SET_IMTERMEDIATE_OUT(BatchedInput); + SET_IMTERMEDIATE_OUT(BatchedOut); +#undef SET_IMTERMEDIATE_OUT + + auto* op = graph->CreateOpNode(&op_desc); + PADDLE_ENFORCE(graph->Has(kParamScopeAttr)); + auto* scope = graph->Get(kParamScopeAttr); + PADDLE_ENFORCE(scope); if (with_fc_bias) { - // Add FC-bias with LSTM-bias and create a new weight - PADDLE_ENFORCE(scope); - const std::string& new_bias_var = name_scope + "_bias.new"; - auto* bias_var = scope->Var(new_bias_var); - PADDLE_ENFORCE(bias_var); - auto* bias_tensor = bias_var->GetMutable(); + // Fusion GRU bias = fcbias + grubias + auto* fusion_bias_var = scope->Var(NEW_NAME(bias) + bias_n->Name()); + auto* out_bias_tensor = + fusion_bias_var->GetMutable(); + PADDLE_ENFORCE(fusion_bias_var); + GET_NODE(fc_bias); + PADDLE_ENFORCE(fc_bias_n); auto* gru_bias_var = scope->FindVar(bias_n->Name()); + auto* fc_bias_var = scope->FindVar(fc_bias_n->Name()); PADDLE_ENFORCE(gru_bias_var); + PADDLE_ENFORCE(fc_bias_var); const auto& gru_bias_tenosr = gru_bias_var->Get(); - bias_tensor->Resize(gru_bias_tenosr.dims()); - - GET_NODE(fc_bias); - auto* fc_bias_var = scope->FindVar(fc_bias_n->Name()); const auto& fc_bias_tensor = fc_bias_var->Get(); // new bias = fc bias + gru bias - auto* data = bias_tensor->mutable_data(platform::CPUPlace()); - for (int i = 0; i < bias_tensor->numel(); i++) { + out_bias_tensor->Resize(gru_bias_tenosr.dims()); + auto* data = out_bias_tensor->mutable_data(platform::CPUPlace()); + for (int i = 0; i < out_bias_tensor->numel(); i++) { data[i] = fc_bias_tensor.data()[i] + gru_bias_tenosr.data()[i]; } - op_desc.SetInput("Bias", {new_bias_var}); } #undef GET_NODE - op_desc.SetInput("H0", {}); - op_desc.SetOutput("Hidden", {hidden_n->Name()}); - op_desc.SetAttr("is_reverse", gru_n->Op()->GetAttr("is_reverse")); - // TODO(TJ): This should be a option for infer - op_desc.SetAttr("use_seq", true); - - // Create temp variables. - // TODO(TJ): clean code - scope->Var(name_scope + "/ReorderedH0.new") - ->GetMutable(); - scope->Var(name_scope + "/XX.new")->GetMutable(); - scope->Var(name_scope + "/BatchedInput.new") - ->GetMutable(); - scope->Var(name_scope + "/BatchedOut.new") - ->GetMutable(); - op_desc.SetOutput("ReorderedH0", {name_scope + "/ReorderedH0.new"}); - op_desc.SetOutput("XX", {name_scope + "/XX.new"}); - op_desc.SetOutput("BatchedInput", {name_scope + "/BatchedInput.new"}); - op_desc.SetOutput("BatchedOut", {name_scope + "/BatchedOut.new"}); - - auto* op = graph->CreateOpNode(&op_desc); - PADDLE_ENFORCE(graph->Has(kParamScopeAttr)); - // auto* scope = graph->Get(kParamScopeAttr); +#define NEW_IMTERMEDIATE_OUT(key) \ + scope->Var(NEW_NAME(key))->GetMutable() + NEW_IMTERMEDIATE_OUT(ReorderedH0); + NEW_IMTERMEDIATE_OUT(XX); + NEW_IMTERMEDIATE_OUT(BatchedInput); + NEW_IMTERMEDIATE_OUT(BatchedOut); +#undef NEW_NAME +#undef NEW_IMTERMEDIATE_OUT IR_NODE_LINK_TO(x_n, op); IR_NODE_LINK_TO(weight_x_n, op); IR_NODE_LINK_TO(weight_h_n, op); - IR_NODE_LINK_TO(bias_n, op); + IR_NODE_LINK_TO(bias_n, op); // actually should link to new bias if have IR_NODE_LINK_TO(op, hidden_n); // h0? return op; @@ -127,26 +134,33 @@ static int BuildFusion(Graph* graph, const std::string& name_scope, int name__ __attribute__((unused)) = name__##_n->id(); GET_NODE(x); - GET_NODE(w); + GET_NODE(w); // fc weight GET_NODE(mul); GET_NODE(fc_out); GET_NODE(Weight); GET_NODE(gru); GET_NODE(Bias); GET_NODE(Hidden); + // nodes need be removed + GET_NODE(BatchGate); + GET_NODE(BatchResetHiddenPrev); + GET_NODE(BatchHidden); if (with_fc_bias) { + GET_NODE(mul_out); GET_NODE(fc_bias); GET_NODE(elementwise_add); gru_creater(gru, x, w, Weight, Bias, Hidden, fc_bias); // Remove unneeded nodes. std::unordered_set marked_nodes( - {mul_n, gru_n, elementwise_add_n}); + {mul_n, gru_n, elementwise_add_n, fc_bias_n, fc_out_n, mul_out_n, + BatchGate_n, BatchResetHiddenPrev_n, BatchHidden_n}); GraphSafeRemoveNodes(graph, marked_nodes); } else { gru_creater(gru, x, w, Weight, Bias, Hidden, -1); // Remove unneeded nodes. - std::unordered_set marked_nodes({mul_n, gru_n}); + std::unordered_set marked_nodes( + {mul_n, gru_n, BatchGate_n, BatchResetHiddenPrev_n, BatchHidden_n}); GraphSafeRemoveNodes(graph, marked_nodes); } #undef GET_NODE diff --git a/paddle/fluid/inference/analysis/analyzer_lac_tester.cc b/paddle/fluid/inference/analysis/analyzer_lac_tester.cc index 7917152428..56f773bf21 100644 --- a/paddle/fluid/inference/analysis/analyzer_lac_tester.cc +++ b/paddle/fluid/inference/analysis/analyzer_lac_tester.cc @@ -171,7 +171,6 @@ void TestLACPrediction(const std::string &model_path, cfg.device = 0; cfg.specify_input_name = true; cfg.enable_ir_optim = true; - cfg.ir_passes.push_back("fc_gru_fuse_pass"); predictor = CreatePaddlePredictor(cfg); } else { From 5335ff628e1494a6a7a6583901ab75f85600e402 Mon Sep 17 00:00:00 2001 From: minqiyang Date: Fri, 7 Sep 2018 15:30:26 +0800 Subject: [PATCH 33/44] Polish code --- .../image_classification/test_image_classification_vgg.py | 2 ++ .../recognize_digits/test_recognize_digits_conv.py | 5 ++++- .../recognize_digits/test_recognize_digits_mlp.py | 2 ++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/python/paddle/fluid/tests/book/high-level-api/image_classification/test_image_classification_vgg.py b/python/paddle/fluid/tests/book/high-level-api/image_classification/test_image_classification_vgg.py index d4e6742b78..ff91be72c9 100644 --- a/python/paddle/fluid/tests/book/high-level-api/image_classification/test_image_classification_vgg.py +++ b/python/paddle/fluid/tests/book/high-level-api/image_classification/test_image_classification_vgg.py @@ -152,5 +152,7 @@ if __name__ == '__main__': for parallel in (False, True): if use_cuda and not core.is_compiled_with_cuda(): continue + # TODO(minqiyang): remove this line after fixing the deletion + # order problem of Scope in ParallelExecutor in manylinux if six.PY2: main(use_cuda=use_cuda, parallel=parallel) diff --git a/python/paddle/fluid/tests/book/high-level-api/recognize_digits/test_recognize_digits_conv.py b/python/paddle/fluid/tests/book/high-level-api/recognize_digits/test_recognize_digits_conv.py index a5adf68158..df4c721c4e 100644 --- a/python/paddle/fluid/tests/book/high-level-api/recognize_digits/test_recognize_digits_conv.py +++ b/python/paddle/fluid/tests/book/high-level-api/recognize_digits/test_recognize_digits_conv.py @@ -154,4 +154,7 @@ if __name__ == '__main__': for parallel in (False, True): if use_cuda and not core.is_compiled_with_cuda(): continue - main(use_cuda=use_cuda, parallel=parallel) + # TODO(minqiyang): remove this line after fixing the deletion + # order problem of Scope in ParallelExecutor in manylinux + if six.PY2: + main(use_cuda=use_cuda, parallel=parallel) diff --git a/python/paddle/fluid/tests/book/high-level-api/recognize_digits/test_recognize_digits_mlp.py b/python/paddle/fluid/tests/book/high-level-api/recognize_digits/test_recognize_digits_mlp.py index 811f17b5ab..440d2a3083 100644 --- a/python/paddle/fluid/tests/book/high-level-api/recognize_digits/test_recognize_digits_mlp.py +++ b/python/paddle/fluid/tests/book/high-level-api/recognize_digits/test_recognize_digits_mlp.py @@ -137,5 +137,7 @@ if __name__ == '__main__': for parallel in (False, True): if use_cuda and not core.is_compiled_with_cuda(): continue + # TODO(minqiyang): remove this line after fixing the deletion + # order problem of Scope in ParallelExecutor in manylinux if six.PY2: main(use_cuda=use_cuda, parallel=parallel) From 6fae46a128440b5664845f0a47dc8df1623f995e Mon Sep 17 00:00:00 2001 From: luotao1 Date: Fri, 7 Sep 2018 16:05:31 +0800 Subject: [PATCH 34/44] refine codes --- cmake/external/anakin.cmake | 2 +- paddle/scripts/paddle_build.sh | 2 +- python/paddle/fluid/layers/nn.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cmake/external/anakin.cmake b/cmake/external/anakin.cmake index 5a12c6490e..ed054ff41a 100644 --- a/cmake/external/anakin.cmake +++ b/cmake/external/anakin.cmake @@ -48,7 +48,7 @@ ExternalProject_Add( ${EXTERNAL_PROJECT_LOG_ARGS} DEPENDS ${MKLML_PROJECT} GIT_REPOSITORY "https://github.com/PaddlePaddle/Anakin" - GIT_TAG "9424277cf9ae180a14aff09560d3cd60a49c76d2" + GIT_TAG "3c8554f4978628183566ab7dd6c1e7e66493c7cd" PREFIX ${ANAKIN_SOURCE_DIR} UPDATE_COMMAND "" CMAKE_ARGS ${CMAKE_ARGS_PREFIX} diff --git a/paddle/scripts/paddle_build.sh b/paddle/scripts/paddle_build.sh index 9ffde5df96..69f5ffecbe 100755 --- a/paddle/scripts/paddle_build.sh +++ b/paddle/scripts/paddle_build.sh @@ -547,7 +547,7 @@ function gen_capi_package() { rm -rf $install_prefix make DESTDIR="$install_prefix" install cd $install_prefix/usr/local - ls | egrep -v "^Found.*item$" | xargs tar -cf ${PADDLE_ROOT}/build/paddle.tgz + ls | egrep -v "^Found.*item$" | xargs tar -czf ${PADDLE_ROOT}/build/paddle.tgz fi } diff --git a/python/paddle/fluid/layers/nn.py b/python/paddle/fluid/layers/nn.py index 5f49d5bbff..8408e6d2a1 100644 --- a/python/paddle/fluid/layers/nn.py +++ b/python/paddle/fluid/layers/nn.py @@ -4500,7 +4500,7 @@ def reshape(x, shape, actual_shape=None, act=None, inplace=True, name=None): """ if not (isinstance(shape, list) or isinstance(shape, tuple)): - raise ValueError("Input shape must be a python lsit or tuple.") + raise ValueError("Input shape must be a python list or tuple.") inputs = {"X": x} if isinstance(actual_shape, Variable): inputs["Shape"] = actual_shape From acfdbf029330e60037e4fff7cee9c00d99f031c5 Mon Sep 17 00:00:00 2001 From: tensor-tang Date: Fri, 7 Sep 2018 15:56:51 +0800 Subject: [PATCH 35/44] enable ner analysis test and refine lac --- .../fluid/inference/analysis/CMakeLists.txt | 2 +- .../inference/analysis/analyzer_lac_tester.cc | 14 ++-- .../inference/analysis/analyzer_ner_tester.cc | 74 ++++++++++++++++--- .../inference/analysis/analyzer_tester.cc | 2 - paddle/fluid/inference/api/CMakeLists.txt | 15 +--- paddle/fluid/inference/api/helper.h | 6 +- 6 files changed, 74 insertions(+), 39 deletions(-) diff --git a/paddle/fluid/inference/analysis/CMakeLists.txt b/paddle/fluid/inference/analysis/CMakeLists.txt index ef55a0c28a..a115bc8f4a 100644 --- a/paddle/fluid/inference/analysis/CMakeLists.txt +++ b/paddle/fluid/inference/analysis/CMakeLists.txt @@ -81,7 +81,7 @@ if (NOT EXISTS ${CHINESE_NER_INSTALL_DIR} AND WITH_TESTING AND WITH_INFERENCE) endif() inference_analysis_test(test_analyzer_ner SRCS analyzer_ner_tester.cc - EXTRA_DEPS paddle_inference_api paddle_fluid_api + EXTRA_DEPS paddle_inference_api paddle_fluid_api analysis_predictor ARGS --infer_model=${CHINESE_NER_INSTALL_DIR}/model --infer_data=${CHINESE_NER_INSTALL_DIR}/data.txt) diff --git a/paddle/fluid/inference/analysis/analyzer_lac_tester.cc b/paddle/fluid/inference/analysis/analyzer_lac_tester.cc index 56f773bf21..4ff7251473 100644 --- a/paddle/fluid/inference/analysis/analyzer_lac_tester.cc +++ b/paddle/fluid/inference/analysis/analyzer_lac_tester.cc @@ -15,11 +15,9 @@ #include "paddle/fluid/inference/analysis/analyzer.h" #include #include "paddle/fluid/framework/ir/fuse_pass_base.h" -#include "paddle/fluid/framework/ir/pass.h" -#include "paddle/fluid/inference/analysis/ut_helper.h" #include "paddle/fluid/inference/api/analysis_predictor.h" #include "paddle/fluid/inference/api/helper.h" -#include "paddle/fluid/inference/api/paddle_inference_api.h" +#include "paddle/fluid/inference/api/paddle_inference_pass.h" #include "paddle/fluid/platform/profiler.h" DEFINE_string(infer_model, "", "model path for LAC"); @@ -160,7 +158,7 @@ void TestLACPrediction(const std::string &model_path, config.use_gpu = false; config.device = 0; config.specify_input_name = true; - std::vector input_slots, outputs_slots, ref_outputs_slots; + std::vector input_slots, outputs_slots; DataRecord data(data_file, batch_size); GetOneBatch(&input_slots, &data, batch_size); std::unique_ptr predictor; @@ -217,6 +215,7 @@ void TestLACPrediction(const std::string &model_path, // run once for comparion as reference auto ref_predictor = CreatePaddlePredictor(config); + std::vector ref_outputs_slots; ref_predictor->Run(input_slots, &ref_outputs_slots); EXPECT_EQ(ref_outputs_slots.size(), outputs_slots.size()); auto &ref_out = ref_outputs_slots[0]; @@ -246,9 +245,10 @@ void TestLACPrediction(const std::string &model_path, } LOG(INFO) << "has num ops: " << num_ops; ASSERT_TRUE(fuse_statis.count("fc_fuse")); - // ASSERT_TRUE(fuse_statis.count("fc_gru_fuse")); - LOG(INFO) << "fc fuse num:" << fuse_statis.at("fc_fuse"); - // LOG(INFO) << "fc gru fuse num:" << fuse_statis.at("fc_gru_fuse"); + ASSERT_TRUE(fuse_statis.count("fc_gru_fuse")); + EXPECT_EQ(fuse_statis.at("fc_fuse"), 1); + EXPECT_EQ(fuse_statis.at("fc_gru_fuse"), 4); + EXPECT_EQ(num_ops, 11); } } diff --git a/paddle/fluid/inference/analysis/analyzer_ner_tester.cc b/paddle/fluid/inference/analysis/analyzer_ner_tester.cc index eaae09b051..f5c5d73aeb 100644 --- a/paddle/fluid/inference/analysis/analyzer_ner_tester.cc +++ b/paddle/fluid/inference/analysis/analyzer_ner_tester.cc @@ -13,12 +13,11 @@ // limitations under the License. #include "paddle/fluid/inference/analysis/analyzer.h" -#include #include -#include "paddle/fluid/framework/ir/pass.h" -#include "paddle/fluid/inference/analysis/ut_helper.h" +#include "paddle/fluid/framework/ir/fuse_pass_base.h" +#include "paddle/fluid/inference/api/analysis_predictor.h" #include "paddle/fluid/inference/api/helper.h" -#include "paddle/fluid/inference/api/paddle_inference_api.h" +#include "paddle/fluid/inference/api/paddle_inference_pass.h" #include "paddle/fluid/platform/profiler.h" DEFINE_string(infer_model, "", "model path"); @@ -112,7 +111,7 @@ void PrepareInputs(std::vector *input_slots, DataRecord *data, const int chinese_ner_result_data[] = {30, 45, 41, 48, 17, 26, 48, 39, 38, 16, 25}; -void TestChineseNERPrediction() { +void TestChineseNERPrediction(bool use_analysis) { NativeConfig config; config.prog_file = FLAGS_infer_model + "/__model__"; config.param_file = FLAGS_infer_model + "/param"; @@ -120,11 +119,23 @@ void TestChineseNERPrediction() { config.device = 0; config.specify_input_name = true; - auto predictor = - CreatePaddlePredictor(config); - std::vector input_slots; - std::vector outputs; + std::vector input_slots, outputs; + std::unique_ptr predictor; Timer timer; + if (use_analysis) { + AnalysisConfig cfg; + cfg.prog_file = FLAGS_infer_model + "/__model__"; + cfg.param_file = FLAGS_infer_model + "/param"; + cfg.use_gpu = false; + cfg.device = 0; + cfg.specify_input_name = true; + cfg.enable_ir_optim = true; + predictor = + CreatePaddlePredictor(cfg); + } else { + predictor = + CreatePaddlePredictor(config); + } if (FLAGS_test_all_data) { LOG(INFO) << "test all data"; @@ -165,10 +176,51 @@ void TestChineseNERPrediction() { for (size_t i = 0; i < std::min(11UL, size); i++) { PADDLE_ENFORCE(result[i], chinese_ner_result_data[i]); } + + if (use_analysis) { + // run once for comparion as reference + auto ref_predictor = + CreatePaddlePredictor(config); + std::vector ref_outputs_slots; + ref_predictor->Run(input_slots, &ref_outputs_slots); + EXPECT_EQ(ref_outputs_slots.size(), outputs.size()); + auto &ref_out = ref_outputs_slots[0]; + size_t ref_size = + std::accumulate(ref_out.shape.begin(), ref_out.shape.end(), 1, + [](int a, int b) { return a * b; }); + EXPECT_EQ(size, ref_size); + int64_t *pdata_ref = static_cast(ref_out.data.data()); + for (size_t i = 0; i < size; ++i) { + EXPECT_EQ(pdata_ref[i], result[i]); + } + + AnalysisPredictor *analysis_predictor = + dynamic_cast(predictor.get()); + auto &fuse_statis = analysis_predictor->analysis_argument() + .Get>( + framework::ir::kFuseStatisAttr); + for (auto &item : fuse_statis) { + LOG(INFO) << "fused " << item.first << " " << item.second; + } + int num_ops = 0; + for (auto &node : + analysis_predictor->analysis_argument().main_dfg->nodes.nodes()) { + if (node->IsFunction()) { + ++num_ops; + } + } + LOG(INFO) << "has num ops: " << num_ops; + ASSERT_TRUE(fuse_statis.count("fc_fuse")); + ASSERT_TRUE(fuse_statis.count("fc_gru_fuse")); + EXPECT_EQ(fuse_statis.at("fc_fuse"), 1); + EXPECT_EQ(fuse_statis.at("fc_gru_fuse"), 2); + EXPECT_EQ(num_ops, 14); + } } -// Directly infer with the original model. -TEST(Analyzer, Chinese_ner) { TestChineseNERPrediction(); } +TEST(Analyzer_Chinese_ner, native) { TestChineseNERPrediction(false); } + +TEST(Analyzer_Chinese_ner, analysis) { TestChineseNERPrediction(true); } } // namespace inference } // namespace paddle diff --git a/paddle/fluid/inference/analysis/analyzer_tester.cc b/paddle/fluid/inference/analysis/analyzer_tester.cc index 4cf26d3c70..a496ae41aa 100644 --- a/paddle/fluid/inference/analysis/analyzer_tester.cc +++ b/paddle/fluid/inference/analysis/analyzer_tester.cc @@ -283,7 +283,6 @@ void TestDituRNNPrediction(bool use_analysis, bool activate_ir, base_predictor->Run(input_slots, &base_outputs); - LOG(INFO) << "===========profile result==========="; if (num_threads == 1) { // Prepare inputs. Timer timer; @@ -324,7 +323,6 @@ void TestDituRNNPrediction(bool use_analysis, bool activate_ir, threads[i].join(); } } - LOG(INFO) << "====================================="; if (use_analysis && activate_ir) { AnalysisPredictor *analysis_predictor = diff --git a/paddle/fluid/inference/api/CMakeLists.txt b/paddle/fluid/inference/api/CMakeLists.txt index f944c9fdec..5df486f345 100644 --- a/paddle/fluid/inference/api/CMakeLists.txt +++ b/paddle/fluid/inference/api/CMakeLists.txt @@ -44,20 +44,7 @@ function(inference_api_test TARGET_NAME) endfunction(inference_api_test) cc_library(paddle_inference_api SRCS api.cc api_impl.cc helper.cc DEPS lod_tensor) -cc_library(analysis_predictor SRCS analysis_predictor.cc DEPS paddle_inference_api - analysis - ir_pass_manager - pass - fc_fuse_pass - fc_lstm_fuse_pass - fc_gru_fuse_pass - seq_concat_fc_fuse_pass - graph_viz_pass - infer_clean_graph_pass - graph_pattern_detector - infer_clean_graph_pass - attention_lstm_fuse_pass) - +cc_library(analysis_predictor SRCS analysis_predictor.cc DEPS paddle_inference_api analysis) cc_test(test_paddle_inference_api SRCS api_tester.cc DEPS paddle_inference_api) diff --git a/paddle/fluid/inference/api/helper.h b/paddle/fluid/inference/api/helper.h index 0ab2542f34..f6893be428 100644 --- a/paddle/fluid/inference/api/helper.h +++ b/paddle/fluid/inference/api/helper.h @@ -124,11 +124,9 @@ std::string DescribeTensor(const PaddleTensor &tensor) { void PrintTime(int batch_size, int repeat, int num_threads, int tid, double latency) { - LOG(INFO) << "====================================="; - LOG(INFO) << "batch_size: " << batch_size << ", repeat: " << repeat + LOG(INFO) << "====== batch_size: " << batch_size << ", repeat: " << repeat << ", threads: " << num_threads << ", thread id: " << tid - << ", latency: " << latency << "ms"; - LOG(INFO) << "====================================="; + << ", latency: " << latency << "ms ======"; } } // namespace inference From d3d22a12fdae2434a7aa29ce9b324ff0ddb3be96 Mon Sep 17 00:00:00 2001 From: minqiyang Date: Fri, 7 Sep 2018 17:53:41 +0800 Subject: [PATCH 36/44] Polish code --- .../recognize_digits/test_recognize_digits_conv.py | 1 + 1 file changed, 1 insertion(+) diff --git a/python/paddle/fluid/tests/book/high-level-api/recognize_digits/test_recognize_digits_conv.py b/python/paddle/fluid/tests/book/high-level-api/recognize_digits/test_recognize_digits_conv.py index df4c721c4e..fa72c939e5 100644 --- a/python/paddle/fluid/tests/book/high-level-api/recognize_digits/test_recognize_digits_conv.py +++ b/python/paddle/fluid/tests/book/high-level-api/recognize_digits/test_recognize_digits_conv.py @@ -18,6 +18,7 @@ import argparse import paddle.fluid as fluid import paddle.fluid.core as core import paddle +import six import sys import numpy import unittest From d230379b542ad08501f455ca3d492d643867c8b8 Mon Sep 17 00:00:00 2001 From: luotao1 Date: Fri, 7 Sep 2018 18:18:49 +0800 Subject: [PATCH 37/44] move anakin release to third_party/install/anakin --- cmake/inference_lib.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/inference_lib.cmake b/cmake/inference_lib.cmake index 6e66ba94ab..077072f6ea 100644 --- a/cmake/inference_lib.cmake +++ b/cmake/inference_lib.cmake @@ -150,7 +150,7 @@ if (WITH_ANAKIN AND WITH_MKL) SRCS ${PADDLE_BINARY_DIR}/paddle/fluid/inference/api/libinference_anakin_api* # compiled anakin api ${ANAKIN_INSTALL_DIR} # anakin release - DSTS ${dst_dir}/inference/anakin ${dst_dir}/inference/anakin) + DSTS ${dst_dir}/inference/anakin ${FLUID_INSTALL_DIR}/third_party/install/anakin) list(APPEND inference_deps anakin_inference_lib) endif() From 3ea19b759649feabf45860e4e4c808c26845c3c7 Mon Sep 17 00:00:00 2001 From: tensor-tang Date: Fri, 7 Sep 2018 18:48:45 +0800 Subject: [PATCH 38/44] fix bug and fc pass ut --- paddle/fluid/framework/ir/graph_pattern_detector.cc | 9 ++++----- paddle/fluid/inference/analysis/analyzer_lac_tester.cc | 1 + paddle/fluid/inference/analysis/analyzer_ner_tester.cc | 1 + 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/paddle/fluid/framework/ir/graph_pattern_detector.cc b/paddle/fluid/framework/ir/graph_pattern_detector.cc index 69a323a8bd..5ca7509515 100644 --- a/paddle/fluid/framework/ir/graph_pattern_detector.cc +++ b/paddle/fluid/framework/ir/graph_pattern_detector.cc @@ -536,22 +536,21 @@ PDNode* patterns::FC(PDPattern* pattern, const std::string& name_scope, mul_out_var = pattern->NewNode(name_scope, "mul_out") ->AsIntermediate() ->assert_is_only_output_of_op("mul") - ->assert_is_op_input("elementwise_add", "X"); + ->assert_is_op_input("elementwise_add"); // bias bias = pattern->NewNode(name_scope, "fc_bias") ->AsInput() - ->assert_is_persistable_var() - ->assert_is_op_input("elementwise_add", "Y"); + ->assert_is_op_input("elementwise_add"); // output fc_out = pattern->NewNode(name_scope, "fc_out") ->AsOutput() - ->assert_is_op_output("elementwise_add", "Out"); + ->assert_is_op_output("elementwise_add"); mul_op->LinksFrom({x, mul_weight_var}).LinksTo({mul_out_var}); elementwise_add_op->LinksFrom({mul_out_var, bias}).LinksTo({fc_out}); } else { fc_out = pattern->NewNode(name_scope, "fc_out") ->AsOutput() - ->assert_is_op_output("mul", "Out"); + ->assert_is_op_output("mul"); mul_op->LinksFrom({mul_weight_var, x}).LinksTo({fc_out}); } return fc_out; diff --git a/paddle/fluid/inference/analysis/analyzer_lac_tester.cc b/paddle/fluid/inference/analysis/analyzer_lac_tester.cc index 4ff7251473..b906b32cf5 100644 --- a/paddle/fluid/inference/analysis/analyzer_lac_tester.cc +++ b/paddle/fluid/inference/analysis/analyzer_lac_tester.cc @@ -15,6 +15,7 @@ #include "paddle/fluid/inference/analysis/analyzer.h" #include #include "paddle/fluid/framework/ir/fuse_pass_base.h" +#include "paddle/fluid/inference/analysis/ut_helper.h" #include "paddle/fluid/inference/api/analysis_predictor.h" #include "paddle/fluid/inference/api/helper.h" #include "paddle/fluid/inference/api/paddle_inference_pass.h" diff --git a/paddle/fluid/inference/analysis/analyzer_ner_tester.cc b/paddle/fluid/inference/analysis/analyzer_ner_tester.cc index f5c5d73aeb..661b047ed7 100644 --- a/paddle/fluid/inference/analysis/analyzer_ner_tester.cc +++ b/paddle/fluid/inference/analysis/analyzer_ner_tester.cc @@ -15,6 +15,7 @@ #include "paddle/fluid/inference/analysis/analyzer.h" #include #include "paddle/fluid/framework/ir/fuse_pass_base.h" +#include "paddle/fluid/inference/analysis/ut_helper.h" #include "paddle/fluid/inference/api/analysis_predictor.h" #include "paddle/fluid/inference/api/helper.h" #include "paddle/fluid/inference/api/paddle_inference_pass.h" From ce24a92007b7c55f6264e79573d7208e0c4b2628 Mon Sep 17 00:00:00 2001 From: minqiyang Date: Fri, 7 Sep 2018 19:13:44 +0800 Subject: [PATCH 39/44] Disable image_classification_resnet --- .../test_image_classification_resnet.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/python/paddle/fluid/tests/book/high-level-api/image_classification/test_image_classification_resnet.py b/python/paddle/fluid/tests/book/high-level-api/image_classification/test_image_classification_resnet.py index 2e15c224f6..e5ae95e2d9 100644 --- a/python/paddle/fluid/tests/book/high-level-api/image_classification/test_image_classification_resnet.py +++ b/python/paddle/fluid/tests/book/high-level-api/image_classification/test_image_classification_resnet.py @@ -18,6 +18,7 @@ import paddle import paddle.fluid as fluid import paddle.fluid.core as core import numpy +import six import os import cifar10_small_test_set @@ -177,4 +178,7 @@ if __name__ == '__main__': for parallel in (False, True): if use_cuda and not core.is_compiled_with_cuda(): continue - main(use_cuda=use_cuda, parallel=parallel) + # TODO(minqiyang): remove this line after fixing the deletion + # order problem of Scope in ParallelExecutor in manylinux + if six.PY2: + main(use_cuda=use_cuda, parallel=parallel) From 5a2fc5b52f20b0c905a38ebd0fe206f88dadd649 Mon Sep 17 00:00:00 2001 From: tensor-tang Date: Fri, 7 Sep 2018 22:54:42 +0800 Subject: [PATCH 40/44] fix print error --- paddle/fluid/inference/analysis/analyzer_lac_tester.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/paddle/fluid/inference/analysis/analyzer_lac_tester.cc b/paddle/fluid/inference/analysis/analyzer_lac_tester.cc index b906b32cf5..522d870db8 100644 --- a/paddle/fluid/inference/analysis/analyzer_lac_tester.cc +++ b/paddle/fluid/inference/analysis/analyzer_lac_tester.cc @@ -182,6 +182,7 @@ void TestLACPrediction(const std::string &model_path, Timer timer; if (test_all_data) { double sum = 0; + LOG(INFO) << "Total number of samples: " << data.datasets.size(); for (int i = 0; i < repeat; i++) { for (size_t bid = 0; bid < data.batched_datas.size(); ++bid) { GetOneBatch(&input_slots, &data, batch_size); @@ -190,7 +191,9 @@ void TestLACPrediction(const std::string &model_path, sum += timer.toc(); } } - PrintTime(batch_size, repeat, 1, 0, sum / batch_size); + PrintTime(batch_size, repeat, 1, 0, sum / repeat); + LOG(INFO) << "Average latency of each sample: " + << sum / repeat / data.datasets.size() << " ms"; return; } timer.tic(); From f90c7865f0ababccd89332604e7e181c85cf9b60 Mon Sep 17 00:00:00 2001 From: Wu Yi Date: Sat, 8 Sep 2018 16:05:56 +0800 Subject: [PATCH 41/44] Benchmark tool for imgnet (#12305) * support test using executor without reader * run imgnet * update fluid benchmark * wip * update * update all models * support pyreader * update * clean up * make profile batches contollable * update API.spec * update scripts * clean dockerfile * update * clean comments * add scope argument docstring * use num_trainers to determine nccl init comms --- benchmark/fluid/Dockerfile | 4 +- benchmark/fluid/args.py | 16 +- benchmark/fluid/fluid_benchmark.py | 274 +++++++------- benchmark/fluid/imagenet_reader.py | 344 ++++++++++++++++++ benchmark/fluid/kube_gen_job.py | 13 + benchmark/fluid/models/__init__.py | 3 +- benchmark/fluid/models/machine_translation.py | 48 ++- benchmark/fluid/models/mnist.py | 95 +++-- benchmark/fluid/models/resnet.py | 197 ++++++---- .../fluid/models/resnet_with_preprocess.py | 268 ++++++++++++++ benchmark/fluid/models/se_resnext.py | 286 +++++++++++++++ .../fluid/models/stacked_dynamic_lstm.py | 75 ++-- benchmark/fluid/models/vgg.py | 101 +++-- paddle/fluid/API.spec | 2 +- paddle/fluid/platform/nccl_helper.h | 7 +- python/paddle/fluid/parallel_executor.py | 7 +- 16 files changed, 1342 insertions(+), 398 deletions(-) create mode 100644 benchmark/fluid/imagenet_reader.py create mode 100644 benchmark/fluid/models/resnet_with_preprocess.py create mode 100644 benchmark/fluid/models/se_resnext.py diff --git a/benchmark/fluid/Dockerfile b/benchmark/fluid/Dockerfile index 707fadb1fa..2e1e0d3768 100644 --- a/benchmark/fluid/Dockerfile +++ b/benchmark/fluid/Dockerfile @@ -11,6 +11,7 @@ RUN ln -s /usr/lib/x86_64-linux-gnu/libcudnn.so.7 /usr/lib/libcudnn.so && ln -s # Add "ENV http_proxy=http://ip:port" if your download is slow, and don't forget to unset it at runtime. # exmaple: unset http_proxy && unset https_proxy && python fluid_benchmark.py ... + RUN pip install -U pip RUN pip install -U kubernetes paddlepaddle @@ -27,5 +28,6 @@ ADD *.whl / RUN pip install /*.whl && rm -f /*.whl ENV LD_LIBRARY_PATH=/usr/local/lib -ADD fluid_benchmark.py recordio_converter.py args.py recordio_converter.py run.sh run_fluid_benchmark.sh /workspace/ +ADD fluid_benchmark.py recordio_converter.py args.py recordio_converter.py run.sh run_fluid_benchmark.sh imagenet_reader.py /workspace/ ADD models/ /workspace/models/ + diff --git a/benchmark/fluid/args.py b/benchmark/fluid/args.py index a79f25ccc6..ed696e82f8 100644 --- a/benchmark/fluid/args.py +++ b/benchmark/fluid/args.py @@ -17,7 +17,8 @@ import argparse __all__ = ['parse_args', ] BENCHMARK_MODELS = [ - "machine_translation", "resnet", "vgg", "mnist", "stacked_dynamic_lstm" + "machine_translation", "resnet", "se_resnext", "vgg", "mnist", + "stacked_dynamic_lstm", "resnet_with_preprocess" ] @@ -67,12 +68,12 @@ def parse_args(): '--cpus', type=int, default=1, - help='If cpus > 1, will use ParallelDo to run, else use Executor.') + help='If cpus > 1, will set ParallelExecutor to use multiple threads.') parser.add_argument( '--data_set', type=str, default='flowers', - choices=['cifar10', 'flowers'], + choices=['cifar10', 'flowers', 'imagenet'], help='Optional dataset for benchmark.') parser.add_argument( '--infer_only', action='store_true', help='If set, run forward only.') @@ -122,6 +123,11 @@ def parse_args(): type=str, default="", help='Directory that contains all the training recordio files.') + parser.add_argument( + '--test_data_path', + type=str, + default="", + help='Directory that contains all the test data (NOT recordio).') parser.add_argument( '--use_inference_transpiler', action='store_true', @@ -130,5 +136,9 @@ def parse_args(): '--no_random', action='store_true', help='If set, keep the random seed and do not shuffle the data.') + parser.add_argument( + '--use_lars', + action='store_true', + help='If set, use lars for optimizers, ONLY support resnet module.') args = parser.parse_args() return args diff --git a/benchmark/fluid/fluid_benchmark.py b/benchmark/fluid/fluid_benchmark.py index 53d010434a..11bd75e1d0 100644 --- a/benchmark/fluid/fluid_benchmark.py +++ b/benchmark/fluid/fluid_benchmark.py @@ -16,6 +16,7 @@ import argparse import cProfile import time import os +import traceback import numpy as np @@ -27,7 +28,7 @@ import paddle.fluid.transpiler.distribute_transpiler as distribute_transpiler from args import * -def append_nccl2_prepare(trainer_id): +def append_nccl2_prepare(trainer_id, startup_prog): if trainer_id >= 0: # append gen_nccl_id at the end of startup program trainer_id = int(os.getenv("PADDLE_TRAINER_ID")) @@ -40,11 +41,11 @@ def append_nccl2_prepare(trainer_id): current_endpoint = os.getenv("PADDLE_CURRENT_IP") + ":" + port worker_endpoints.remove(current_endpoint) - nccl_id_var = fluid.default_startup_program().global_block().create_var( + nccl_id_var = startup_prog.global_block().create_var( name="NCCLID", persistable=True, type=fluid.core.VarDesc.VarType.RAW) - fluid.default_startup_program().global_block().append_op( + startup_prog.global_block().append_op( type="gen_nccl_id", inputs={}, outputs={"NCCLID": nccl_id_var}, @@ -59,7 +60,7 @@ def append_nccl2_prepare(trainer_id): "nccl-based dist train.") -def dist_transpile(trainer_id, args): +def dist_transpile(trainer_id, args, train_prog, startup_prog): if trainer_id < 0: return None, None @@ -80,133 +81,69 @@ def dist_transpile(trainer_id, args): # the role, should be either PSERVER or TRAINER training_role = os.getenv("PADDLE_TRAINING_ROLE") - t = distribute_transpiler.DistributeTranspiler() + config = distribute_transpiler.DistributeTranspilerConfig() + config.slice_var_up = not args.no_split_var + t = distribute_transpiler.DistributeTranspiler(config=config) t.transpile( trainer_id, + # NOTE: *MUST* use train_prog, for we are using with guard to + # generate different program for train and test. + program=train_prog, pservers=pserver_endpoints, trainers=trainers, sync_mode=not args.async_mode) if training_role == "PSERVER": pserver_program = t.get_pserver_program(current_endpoint) - pserver_startup_program = t.get_startup_program(current_endpoint, - pserver_program) + pserver_startup_program = t.get_startup_program( + current_endpoint, pserver_program, startup_program=startup_prog) return pserver_program, pserver_startup_program elif training_role == "TRAINER": train_program = t.get_trainer_program() - return train_program, fluid.default_startup_program() + return train_program, startup_prog else: raise ValueError( 'PADDLE_TRAINING_ROLE environment variable must be either TRAINER or PSERVER' ) -def test(exe, inference_program, test_reader, feeder, batch_acc): - accuracy_evaluator = fluid.metrics.Accuracy() - for batch_id, data in enumerate(test_reader()): - acc = exe.run(inference_program, - feed=feeder.feed(data), - fetch_list=[batch_acc]) - accuracy_evaluator.update(value=np.array(acc), weight=len(data)) +def test_parallel(exe, test_args, args, test_prog, feeder): + acc_evaluators = [] + for i in xrange(len(test_args[2])): + acc_evaluators.append(fluid.metrics.Accuracy()) - return accuracy_evaluator.eval() - - -# TODO(wuyi): replace train, train_parallel, test functions with new trainer -# API once it is ready. -def train(avg_loss, infer_prog, optimizer, train_reader, test_reader, batch_acc, - args, train_prog, startup_prog): - if os.getenv("PADDLE_TRAINING_ROLE") == "PSERVER": - place = core.CPUPlace() - exe = fluid.Executor(place) - exe.run(startup_prog) - exe.run(train_prog) - return - - if args.use_fake_data: - raise Exception( - "fake data is not supported in single GPU test for now.") - - place = core.CPUPlace() if args.device == 'CPU' else core.CUDAPlace(0) - exe = fluid.Executor(place) - exe.run(startup_prog) - - # Use inference_transpiler to speedup - if not args.use_reader_op: - feed_var_list = [ - var for var in train_prog.global_block().vars.itervalues() - if var.is_data - ] - feeder = fluid.DataFeeder(feed_var_list, place) - - iters, num_samples, start_time = 0, 0, time.time() - for pass_id in range(args.pass_num): - train_losses = [] - if not args.use_reader_op: - reader_generator = train_reader() - batch_id = 0 - data = None + to_fetch = [v.name for v in test_args[2]] + if args.use_reader_op: + test_args[4].start() while True: - if not args.use_reader_op: - data = next(reader_generator, None) - if data == None: - break - if iters == args.iterations: - reader_generator.close() + try: + acc_rets = exe.run(fetch_list=to_fetch) + for i, e in enumerate(acc_evaluators): + e.update( + value=np.array(acc_rets[i]), weight=args.batch_size) + except fluid.core.EOFException as eof: + test_args[4].reset() break - if iters == args.skip_batch_num: - start_time = time.time() - num_samples = 0 + else: + for batch_id, data in enumerate(test_args[3]()): + acc_rets = exe.run(feed=feeder.feed(data), fetch_list=to_fetch) + for i, e in enumerate(acc_evaluators): + e.update(value=np.array(acc_rets[i]), weight=len(data)) - if args.use_reader_op: - try: - loss = exe.run(train_prog, fetch_list=[avg_loss]) - except fluid.core.EnforceNotMet as ex: - break - else: - loss = exe.run(train_prog, - feed=feeder.feed(data), - fetch_list=[avg_loss]) - iters += 1 - batch_id += 1 - # FIXME(wuyi): For use_reader_op, if the current - # pass is not the last, the last batch of this pass - # is also equal to args.batch_size. - if args.use_reader_op: - num_samples += args.batch_size * args.gpus - else: - num_samples += len(data) - train_losses.append(loss) - print("Pass: %d, Iter: %d, Loss: %f\n" % - (pass_id, iters, np.mean(train_losses))) - print_train_time(start_time, time.time(), num_samples) - print("Pass: %d, Loss: %f" % (pass_id, np.mean(train_losses))), - # evaluation - if not args.no_test and batch_acc and not args.use_reader_op: - if args.use_inference_transpiler: - t = fluid.InferenceTranspiler() - t.transpile(infer_prog, place) - - pass_test_acc = test(exe, infer_prog, test_reader, feeder, - batch_acc) - print(", Test Accuracy: %f" % pass_test_acc) - print("\n") - # TODO(wuyi): add warmup passes to get better perf data. - exit(0) + return [e.eval() for e in acc_evaluators] -# TODO(wuyi): replace train, train_parallel, test functions with new trainer -# API once it is ready. -def train_parallel(avg_loss, infer_prog, optimizer, train_reader, test_reader, - batch_acc, args, train_prog, startup_prog, nccl_id_var, - num_trainers, trainer_id): +# NOTE: only need to benchmark using parallelexe +def train_parallel(train_args, test_args, args, train_prog, test_prog, + startup_prog, nccl_id_var, num_trainers, trainer_id): + over_all_start = time.time() place = core.CPUPlace() if args.device == 'CPU' else core.CUDAPlace(0) + feeder = None if not args.use_reader_op: feed_var_list = [ var for var in train_prog.global_block().vars.itervalues() if var.is_data ] feeder = fluid.DataFeeder(feed_var_list, place) - # generate fake: if args.use_fake_data: for var in feed_var_list: @@ -230,63 +167,110 @@ def train_parallel(avg_loss, infer_prog, optimizer, train_reader, test_reader, startup_exe = fluid.Executor(place) startup_exe.run(startup_prog) strategy = fluid.ExecutionStrategy() - strategy.num_threads = 1 + strategy.num_threads = args.cpus strategy.allow_op_delay = False + avg_loss = train_args[0] + + if args.update_method == "pserver": + # parameter server mode distributed training, merge + # gradients on local server, do not initialize + # ParallelExecutor with multi server all-reduce mode. + num_trainers = 1 + trainer_id = 0 + exe = fluid.ParallelExecutor( True, avg_loss.name, + main_program=train_prog, exec_strategy=strategy, num_trainers=num_trainers, trainer_id=trainer_id) + if not args.no_test: + if args.update_method == "pserver": + test_scope = None + else: + # NOTE: use an empty scope to avoid test exe using NCCLID + test_scope = fluid.Scope() + test_exe = fluid.ParallelExecutor( + True, main_program=test_prog, share_vars_from=exe) + for pass_id in range(args.pass_num): num_samples = 0 iters = 0 start_time = time.time() if not args.use_reader_op: - reader_generator = train_reader() + reader_generator = train_args[3]() #train_reader batch_id = 0 data = None + if args.use_reader_op: + train_args[4].start() while True: if not args.use_reader_op: data = next(reader_generator, None) if data == None: break + if args.profile and batch_id == 5: + profiler.start_profiler("All") + profiler.reset_profiler() + elif args.profile and batch_id == 10: + print("profiling total time: ", time.time() - start_time) + profiler.stop_profiler("total", "/tmp/profile_%d_pass%d" % + (trainer_id, pass_id)) if iters == args.iterations: reader_generator.close() break - if args.profile and pass_id == 0 and batch_id == 5: - profiler.start_profiler("All") - elif args.profile and pass_id == 0 and batch_id == 10: - profiler.stop_profiler("total", "/tmp/profile_%d" % trainer_id) if iters == args.skip_batch_num: start_time = time.time() num_samples = 0 + fetch_list = [avg_loss.name] + acc_name_list = [v.name for v in train_args[2]] + fetch_list.extend(acc_name_list) + if args.use_fake_data or args.use_reader_op: try: - loss, = exe.run([avg_loss.name]) + + fetch_ret = exe.run(fetch_list) + except fluid.core.EOFException as eof: + break except fluid.core.EnforceNotMet as ex: + traceback.print_exc() break else: - loss, = exe.run([avg_loss.name], feed=feeder.feed(data)) + fetch_ret = exe.run(fetch_list, feed=feeder.feed(data)) if args.use_reader_op: num_samples += args.batch_size * args.gpus else: num_samples += len(data) + iters += 1 if batch_id % 1 == 0: - print("Pass %d, batch %d, loss %s" % - (pass_id, batch_id, np.array(loss))) + fetched_data = [np.mean(np.array(d)) for d in fetch_ret] + print("Pass %d, batch %d, loss %s, accucacys: %s" % + (pass_id, batch_id, fetched_data[0], fetched_data[1:])) batch_id += 1 print_train_time(start_time, time.time(), num_samples) - if not args.no_test and batch_acc and not args.use_reader_op: - # we have not implement record io for test - # skip test when use args.use_reader_op - test_acc = test(startup_exe, infer_prog, test_reader, feeder, - batch_acc) - print("Pass: %d, Test Accuracy: %f\n" % (pass_id, test_acc)) + if args.use_reader_op: + train_args[4].reset() # reset reader handle + else: + del reader_generator + + if not args.no_test and test_args[2]: + test_feeder = None + if not args.use_reader_op: + test_feed_var_list = [ + var for var in test_prog.global_block().vars.itervalues() + if var.is_data + ] + test_feeder = fluid.DataFeeder(test_feed_var_list, place) + test_ret = test_parallel(test_exe, test_args, args, test_prog, + test_feeder) + print("Pass: %d, Test Accuracy: %s\n" % + (pass_id, [np.mean(np.array(v)) for v in test_ret])) + + print("total train time: ", time.time() - over_all_start) def print_arguments(args): @@ -328,44 +312,46 @@ def main(): if args.use_cprof: pr = cProfile.Profile() pr.enable() + model_def = __import__("models.%s" % args.model, fromlist=["models"]) - train_args = list(model_def.get_model(args)) - train_args.append(args) - # Run optimizer.minimize(avg_loss) - train_args[2].minimize(train_args[0]) - if args.memory_optimize: - fluid.memory_optimize(fluid.default_main_program()) + + train_prog = fluid.Program() + test_prog = fluid.Program() + startup_prog = fluid.Program() + + train_args = list(model_def.get_model(args, True, train_prog, startup_prog)) + test_args = list(model_def.get_model(args, False, test_prog, startup_prog)) + + all_args = [train_args, test_args, args] if args.update_method == "pserver": - train_prog, startup_prog = dist_transpile(trainer_id, args) + train_prog, startup_prog = dist_transpile(trainer_id, args, train_prog, + startup_prog) if not train_prog: raise Exception( "Must configure correct environments to run dist train.") - train_args.extend([train_prog, startup_prog]) + all_args.extend([train_prog, test_prog, startup_prog]) if args.gpus > 1 and os.getenv("PADDLE_TRAINING_ROLE") == "TRAINER": - train_args.extend([nccl_id_var, num_trainers, trainer_id]) - train_parallel(*train_args) - train(*train_args) + all_args.extend([nccl_id_var, num_trainers, trainer_id]) + train_parallel(*all_args) + elif os.getenv("PADDLE_TRAINING_ROLE") == "PSERVER": + # start pserver with Executor + server_exe = fluid.Executor(fluid.CPUPlace()) + server_exe.run(startup_prog) + server_exe.run(train_prog) exit(0) # for other update methods, use default programs - train_args.append(fluid.default_main_program()) - train_args.append(fluid.default_startup_program()) + all_args.extend([train_prog, test_prog, startup_prog]) if args.update_method == "nccl2": - nccl_id_var, num_trainers, trainer_id = append_nccl2_prepare(trainer_id) - if args.gpus == 1: - # NOTE: parallel executor use profiler interanlly - if args.use_nvprof and args.device == 'GPU': - with profiler.cuda_profiler("cuda_profiler.txt", 'csv') as nvprof: - train(*train_args) - else: - train(*train_args) - else: - if args.device == "CPU": - raise Exception("Only support GPU perf with parallel exe") - train_args.extend([nccl_id_var, num_trainers, trainer_id]) - train_parallel(*train_args) + nccl_id_var, num_trainers, trainer_id = append_nccl2_prepare( + trainer_id, startup_prog) + + if args.device == "CPU": + raise Exception("Only support GPU perf with parallel exe") + all_args.extend([nccl_id_var, num_trainers, trainer_id]) + train_parallel(*all_args) if __name__ == "__main__": diff --git a/benchmark/fluid/imagenet_reader.py b/benchmark/fluid/imagenet_reader.py new file mode 100644 index 0000000000..a39485a61f --- /dev/null +++ b/benchmark/fluid/imagenet_reader.py @@ -0,0 +1,344 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +import math +import random +import functools +import numpy as np +from threading import Thread +import subprocess +import time + +from Queue import Queue +import paddle +from PIL import Image, ImageEnhance + +random.seed(0) + +DATA_DIM = 224 + +THREAD = int(os.getenv("PREPROCESS_THREADS", "10")) +BUF_SIZE = 5120 + +DATA_DIR = '/mnt/ImageNet' +TRAIN_LIST = '/mnt/ImageNet/train.txt' +TEST_LIST = '/mnt/ImageNet/val.txt' + +img_mean = np.array([0.485, 0.456, 0.406]).reshape((3, 1, 1)) +img_std = np.array([0.229, 0.224, 0.225]).reshape((3, 1, 1)) + + +def resize_short(img, target_size): + percent = float(target_size) / min(img.size[0], img.size[1]) + resized_width = int(round(img.size[0] * percent)) + resized_height = int(round(img.size[1] * percent)) + img = img.resize((resized_width, resized_height), Image.LANCZOS) + return img + + +def crop_image(img, target_size, center): + width, height = img.size + size = target_size + if center == True: + w_start = (width - size) / 2 + h_start = (height - size) / 2 + else: + w_start = random.randint(0, width - size) + h_start = random.randint(0, height - size) + w_end = w_start + size + h_end = h_start + size + img = img.crop((w_start, h_start, w_end, h_end)) + return img + + +def random_crop(img, size, scale=[0.08, 1.0], ratio=[3. / 4., 4. / 3.]): + aspect_ratio = math.sqrt(random.uniform(*ratio)) + w = 1. * aspect_ratio + h = 1. / aspect_ratio + + bound = min((float(img.size[0]) / img.size[1]) / (w**2), + (float(img.size[1]) / img.size[0]) / (h**2)) + scale_max = min(scale[1], bound) + scale_min = min(scale[0], bound) + + target_area = img.size[0] * img.size[1] * random.uniform(scale_min, + scale_max) + target_size = math.sqrt(target_area) + w = int(target_size * w) + h = int(target_size * h) + + i = random.randint(0, img.size[0] - w) + j = random.randint(0, img.size[1] - h) + + img = img.crop((i, j, i + w, j + h)) + img = img.resize((size, size), Image.LANCZOS) + return img + + +def rotate_image(img): + angle = random.randint(-10, 10) + img = img.rotate(angle) + return img + + +def distort_color(img): + def random_brightness(img, lower=0.5, upper=1.5): + e = random.uniform(lower, upper) + return ImageEnhance.Brightness(img).enhance(e) + + def random_contrast(img, lower=0.5, upper=1.5): + e = random.uniform(lower, upper) + return ImageEnhance.Contrast(img).enhance(e) + + def random_color(img, lower=0.5, upper=1.5): + e = random.uniform(lower, upper) + return ImageEnhance.Color(img).enhance(e) + + ops = [random_brightness, random_contrast, random_color] + random.shuffle(ops) + + img = ops[0](img) + img = ops[1](img) + img = ops[2](img) + + return img + + +def process_image(sample, mode, color_jitter, rotate): + img_path = sample[0] + + img = Image.open(img_path) + if mode == 'train': + if rotate: img = rotate_image(img) + img = random_crop(img, DATA_DIM) + else: + img = resize_short(img, target_size=256) + img = crop_image(img, target_size=DATA_DIM, center=True) + if mode == 'train': + if color_jitter: + img = distort_color(img) + if random.randint(0, 1) == 1: + img = img.transpose(Image.FLIP_LEFT_RIGHT) + + if img.mode != 'RGB': + img = img.convert('RGB') + + img = np.array(img).astype('float32').transpose((2, 0, 1)) / 255 + img -= img_mean + img /= img_std + + if mode == 'train' or mode == 'val': + return img, sample[1] + elif mode == 'test': + return [img] + + +class XmapEndSignal(): + pass + + +def xmap_readers(mapper, + reader, + process_num, + buffer_size, + order=False, + print_queue_state=True): + end = XmapEndSignal() + + # define a worker to read samples from reader to in_queue + def read_worker(reader, in_queue): + for i in reader(): + in_queue.put(i) + in_queue.put(end) + + # define a worker to read samples from reader to in_queue with order flag + def order_read_worker(reader, in_queue, file_queue): + in_order = 0 + for i in reader(): + in_queue.put((in_order, i)) + in_order += 1 + in_queue.put(end) + + # define a worker to handle samples from in_queue by mapper + # and put mapped samples into out_queue + def handle_worker(in_queue, out_queue, mapper): + sample = in_queue.get() + while not isinstance(sample, XmapEndSignal): + r = mapper(sample) + out_queue.put(r) + sample = in_queue.get() + in_queue.put(end) + out_queue.put(end) + + # define a worker to handle samples from in_queue by mapper + # and put mapped samples into out_queue by order + def order_handle_worker(in_queue, out_queue, mapper, out_order): + ins = in_queue.get() + while not isinstance(ins, XmapEndSignal): + order, sample = ins + r = mapper(sample) + while order != out_order[0]: + pass + out_queue.put(r) + out_order[0] += 1 + ins = in_queue.get() + in_queue.put(end) + out_queue.put(end) + + def xreader(): + file_queue = Queue() + in_queue = Queue(buffer_size) + out_queue = Queue(buffer_size) + out_order = [0] + # start a read worker in a thread + target = order_read_worker if order else read_worker + t = Thread(target=target, args=(reader, in_queue)) + t.daemon = True + t.start() + # start several handle_workers + target = order_handle_worker if order else handle_worker + args = (in_queue, out_queue, mapper, out_order) if order else ( + in_queue, out_queue, mapper) + workers = [] + for i in xrange(process_num): + worker = Thread(target=target, args=args) + worker.daemon = True + workers.append(worker) + for w in workers: + w.start() + + sample = out_queue.get() + start_t = time.time() + while not isinstance(sample, XmapEndSignal): + yield sample + sample = out_queue.get() + if time.time() - start_t > 3: + if print_queue_state: + print("queue sizes: ", in_queue.qsize(), out_queue.qsize()) + start_t = time.time() + finish = 1 + while finish < process_num: + sample = out_queue.get() + if isinstance(sample, XmapEndSignal): + finish += 1 + else: + yield sample + + return xreader + + +def _reader_creator(file_list, + mode, + shuffle=False, + color_jitter=False, + rotate=False, + xmap=True): + def reader(): + with open(file_list) as flist: + full_lines = [line.strip() for line in flist] + if shuffle: + random.shuffle(full_lines) + if mode == 'train': + trainer_id = int(os.getenv("PADDLE_TRAINER_ID")) + trainer_count = int(os.getenv("PADDLE_TRAINERS")) + per_node_lines = len(full_lines) / trainer_count + lines = full_lines[trainer_id * per_node_lines:(trainer_id + 1) + * per_node_lines] + print( + "read images from %d, length: %d, lines length: %d, total: %d" + % (trainer_id * per_node_lines, per_node_lines, len(lines), + len(full_lines))) + else: + lines = full_lines + + for line in lines: + if mode == 'train': + img_path, label = line.split() + img_path = img_path.replace("JPEG", "jpeg") + img_path = os.path.join(DATA_DIR, "train", img_path) + yield (img_path, int(label)) + elif mode == 'val': + img_path, label = line.split() + img_path = img_path.replace("JPEG", "jpeg") + img_path = os.path.join(DATA_DIR, "val", img_path) + yield (img_path, int(label)) + elif mode == 'test': + img_path = os.path.join(DATA_DIR, line) + yield [img_path] + + mapper = functools.partial( + process_image, mode=mode, color_jitter=color_jitter, rotate=rotate) + + return paddle.reader.xmap_readers(mapper, reader, THREAD, BUF_SIZE) + + +def load_raw_image_uint8(sample): + img_arr = np.array(Image.open(sample[0])).astype('int64') + return img_arr, int(sample[1]) + + +def train_raw(file_list=TRAIN_LIST, shuffle=True): + def reader(): + with open(file_list) as flist: + full_lines = [line.strip() for line in flist] + if shuffle: + random.shuffle(full_lines) + + trainer_id = int(os.getenv("PADDLE_TRAINER_ID")) + trainer_count = int(os.getenv("PADDLE_TRAINERS")) + per_node_lines = len(full_lines) / trainer_count + lines = full_lines[trainer_id * per_node_lines:(trainer_id + 1) * + per_node_lines] + print("read images from %d, length: %d, lines length: %d, total: %d" + % (trainer_id * per_node_lines, per_node_lines, len(lines), + len(full_lines))) + + for line in lines: + img_path, label = line.split() + img_path = img_path.replace("JPEG", "jpeg") + img_path = os.path.join(DATA_DIR, "train", img_path) + yield (img_path, int(label)) + + return paddle.reader.xmap_readers(load_raw_image_uint8, reader, THREAD, + BUF_SIZE) + + +def train(file_list=TRAIN_LIST, xmap=True): + return _reader_creator( + file_list, + 'train', + shuffle=True, + color_jitter=False, + rotate=False, + xmap=xmap) + + +def val(file_list=TEST_LIST, xmap=True): + return _reader_creator(file_list, 'val', shuffle=False, xmap=xmap) + + +def test(file_list=TEST_LIST): + return _reader_creator(file_list, 'test', shuffle=False) + + +if __name__ == "__main__": + c = 0 + start_t = time.time() + for d in train()(): + c += 1 + if c >= 10000: + break + spent = time.time() - start_t + print("read 10000 speed: ", 10000 / spent, spent) diff --git a/benchmark/fluid/kube_gen_job.py b/benchmark/fluid/kube_gen_job.py index dfe8b5cdd5..c1f22f1bfa 100644 --- a/benchmark/fluid/kube_gen_job.py +++ b/benchmark/fluid/kube_gen_job.py @@ -163,6 +163,19 @@ def gen_job(): volumes.append({"name": "dshm", "emptyDir": {"medium": "Memory"}}) volumeMounts.append({"mountPath": "/dev/shm", "name": "dshm"}) + # add ceph volumes + volumes.append({ + "name": "ceph-data", + "cephfs": { + "monitors": ["192.168.16.23:6789"], + "secretRef": { + "name": "ceph-secret" + }, + "user": "admin", + } + }) + volumeMounts.append({"mountPath": "/mnt/data", "name": "ceph-data"}) + tn["spec"]["template"]["spec"]["volumes"] = volumes tn_container["volumeMounts"] = volumeMounts diff --git a/benchmark/fluid/models/__init__.py b/benchmark/fluid/models/__init__.py index 1c3fcac8dd..1b8f63c707 100644 --- a/benchmark/fluid/models/__init__.py +++ b/benchmark/fluid/models/__init__.py @@ -13,5 +13,6 @@ # limitations under the License. __all__ = [ - "machine_translation", "resnet", "vgg", "mnist", "stacked_dynamic_lstm" + "machine_translation", "resnet", "vgg", "mnist", "stacked_dynamic_lstm", + "resnet_with_preprocess" ] diff --git a/benchmark/fluid/models/machine_translation.py b/benchmark/fluid/models/machine_translation.py index 17f6b03826..18163c35d6 100644 --- a/benchmark/fluid/models/machine_translation.py +++ b/benchmark/fluid/models/machine_translation.py @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. """seq2seq model for fluid.""" + from __future__ import absolute_import from __future__ import division from __future__ import print_function @@ -181,7 +182,7 @@ def lodtensor_to_ndarray(lod_tensor): return ndarray -def get_model(args): +def get_model(args, is_train, main_prog, startup_prog): if args.use_reader_op: raise Exception("machine_translation do not support reader op for now.") embedding_dim = 512 @@ -190,30 +191,27 @@ def get_model(args): dict_size = 30000 beam_size = 3 max_length = 250 - avg_cost, feeding_list = seq_to_seq_net( - embedding_dim, - encoder_size, - decoder_size, - dict_size, - dict_size, - False, - beam_size=beam_size, - max_length=max_length) - - # clone from default main program - inference_program = fluid.default_main_program().clone() - - optimizer = fluid.optimizer.Adam(learning_rate=args.learning_rate) - - train_batch_generator = paddle.batch( - paddle.reader.shuffle( - paddle.dataset.wmt14.train(dict_size), buf_size=1000), - batch_size=args.batch_size * args.gpus) - test_batch_generator = paddle.batch( + with fluid.program_guard(main_prog, startup_prog): + with fluid.unique_name.guard(): + avg_cost, feeding_list = seq_to_seq_net( + embedding_dim, + encoder_size, + decoder_size, + dict_size, + dict_size, + False, + beam_size=beam_size, + max_length=max_length) + if is_train: + optimizer = fluid.optimizer.Adam(learning_rate=args.learning_rate) + optimizer.minimize(avg_cost) + + batch_generator = paddle.batch( paddle.reader.shuffle( - paddle.dataset.wmt14.test(dict_size), buf_size=1000), - batch_size=args.batch_size) + paddle.dataset.wmt14.train(dict_size) + if is_train else paddle.dataset.wmt14.test(dict_size), + buf_size=1000), + batch_size=args.batch_size * args.gpus) - return avg_cost, inference_program, optimizer, train_batch_generator, \ - test_batch_generator, None + return avg_cost, optimizer, [], batch_generator, None diff --git a/benchmark/fluid/models/mnist.py b/benchmark/fluid/models/mnist.py index 8e740dc689..cef8657ee6 100644 --- a/benchmark/fluid/models/mnist.py +++ b/benchmark/fluid/models/mnist.py @@ -65,61 +65,50 @@ def cnn_model(data): return predict -def get_model(args): - if args.use_reader_op: - filelist = [ - os.path.join(args.data_path, f) for f in os.listdir(args.data_path) - ] - data_file = fluid.layers.open_files( - filenames=filelist, - shapes=[[-1, 1, 28, 28], (-1, 1)], - lod_levels=[0, 0], - dtypes=["float32", "int64"], - thread_num=args.gpus, - pass_num=args.pass_num) - data_file = fluid.layers.double_buffer( - fluid.layers.batch( - data_file, batch_size=args.batch_size)) - images, label = fluid.layers.read_file(data_file) - else: - images = fluid.layers.data(name='pixel', shape=[1, 28, 28], dtype=DTYPE) - label = fluid.layers.data(name='label', shape=[1], dtype='int64') - - if args.device == 'CPU' and args.cpus > 1: - places = fluid.layers.get_places(args.cpus) - pd = fluid.layers.ParallelDo(places) - with pd.do(): - predict = cnn_model(pd.read_input(images)) - label = pd.read_input(label) +def get_model(args, is_train, main_prog, startup_prog): + # NOTE: mnist is small, we don't implement data sharding yet. + filelist = [ + os.path.join(args.data_path, f) for f in os.listdir(args.data_path) + ] + with fluid.program_guard(main_prog, startup_prog): + if args.use_reader_op: + data_file_handle = fluid.layers.open_files( + filenames=filelist, + shapes=[[-1, 1, 28, 28], (-1, 1)], + lod_levels=[0, 0], + dtypes=["float32", "int64"], + thread_num=1, + pass_num=1) + data_file = fluid.layers.double_buffer( + fluid.layers.batch( + data_file_handle, batch_size=args.batch_size)) + with fluid.unique_name.guard(): + if args.use_reader_op: + input, label = fluid.layers.read_file(data_file) + else: + images = fluid.layers.data( + name='pixel', shape=[1, 28, 28], dtype='float32') + label = fluid.layers.data( + name='label', shape=[1], dtype='int64') + + predict = cnn_model(images) cost = fluid.layers.cross_entropy(input=predict, label=label) avg_cost = fluid.layers.mean(x=cost) + # Evaluator batch_acc = fluid.layers.accuracy(input=predict, label=label) - - pd.write_output(avg_cost) - pd.write_output(batch_acc) - - avg_cost, batch_acc = pd() - avg_cost = fluid.layers.mean(avg_cost) - batch_acc = fluid.layers.mean(batch_acc) - else: - # Train program - predict = cnn_model(images) - cost = fluid.layers.cross_entropy(input=predict, label=label) - avg_cost = fluid.layers.mean(x=cost) - - # Evaluator - batch_acc = fluid.layers.accuracy(input=predict, label=label) - - # inference program - inference_program = fluid.default_main_program().clone() - - # Optimization - opt = fluid.optimizer.AdamOptimizer( - learning_rate=0.001, beta1=0.9, beta2=0.999) + # Optimization + if is_train: + opt = fluid.optimizer.AdamOptimizer( + learning_rate=0.001, beta1=0.9, beta2=0.999) + opt.minimize() + if args.memory_optimize: + fluid.memory_optimize(main_prog) # Reader - train_reader = paddle.batch( - paddle.dataset.mnist.train(), batch_size=args.batch_size * args.gpus) - test_reader = paddle.batch( - paddle.dataset.mnist.test(), batch_size=args.batch_size) - return avg_cost, inference_program, opt, train_reader, test_reader, batch_acc + if is_train: + reader = paddle.dataset.mnist.train() + else: + reader = paddle.dataset.mnist.test() + batched_reader = paddle.batch( + reader, batch_size=args.batch_size * args.gpus) + return avg_cost, opt, [batch_acc], batched_reader, data_file_handle diff --git a/benchmark/fluid/models/resnet.py b/benchmark/fluid/models/resnet.py index d44a9c07d3..ae1baa48e1 100644 --- a/benchmark/fluid/models/resnet.py +++ b/benchmark/fluid/models/resnet.py @@ -27,10 +27,17 @@ import paddle import paddle.fluid as fluid import paddle.fluid.core as core import paddle.fluid.profiler as profiler -from recordio_converter import imagenet_train, imagenet_test +# from recordio_converter import imagenet_train, imagenet_test +from imagenet_reader import train, val -def conv_bn_layer(input, ch_out, filter_size, stride, padding, act='relu'): +def conv_bn_layer(input, + ch_out, + filter_size, + stride, + padding, + act='relu', + is_train=True): conv1 = fluid.layers.conv2d( input=input, filter_size=filter_size, @@ -39,29 +46,31 @@ def conv_bn_layer(input, ch_out, filter_size, stride, padding, act='relu'): padding=padding, act=None, bias_attr=False) - return fluid.layers.batch_norm(input=conv1, act=act) + return fluid.layers.batch_norm(input=conv1, act=act, is_test=not is_train) -def shortcut(input, ch_out, stride): +def shortcut(input, ch_out, stride, is_train=True): ch_in = input.shape[1] # if args.data_format == 'NCHW' else input.shape[-1] if ch_in != ch_out: - return conv_bn_layer(input, ch_out, 1, stride, 0, None) + return conv_bn_layer( + input, ch_out, 1, stride, 0, None, is_train=is_train) else: return input -def basicblock(input, ch_out, stride): - short = shortcut(input, ch_out, stride) - conv1 = conv_bn_layer(input, ch_out, 3, stride, 1) - conv2 = conv_bn_layer(conv1, ch_out, 3, 1, 1, act=None) +def basicblock(input, ch_out, stride, is_train=True): + short = shortcut(input, ch_out, stride, is_train=is_train) + conv1 = conv_bn_layer(input, ch_out, 3, stride, 1, is_train=is_train) + conv2 = conv_bn_layer(conv1, ch_out, 3, 1, 1, act=None, is_train=is_train) return fluid.layers.elementwise_add(x=short, y=conv2, act='relu') -def bottleneck(input, ch_out, stride): - short = shortcut(input, ch_out * 4, stride) - conv1 = conv_bn_layer(input, ch_out, 1, stride, 0) - conv2 = conv_bn_layer(conv1, ch_out, 3, 1, 1) - conv3 = conv_bn_layer(conv2, ch_out * 4, 1, 1, 0, act=None) +def bottleneck(input, ch_out, stride, is_train=True): + short = shortcut(input, ch_out * 4, stride, is_train=is_train) + conv1 = conv_bn_layer(input, ch_out, 1, stride, 0, is_train=is_train) + conv2 = conv_bn_layer(conv1, ch_out, 3, 1, 1, is_train=is_train) + conv3 = conv_bn_layer( + conv2, ch_out * 4, 1, 1, 0, act=None, is_train=is_train) return fluid.layers.elementwise_add(x=short, y=conv3, act='relu') @@ -72,7 +81,11 @@ def layer_warp(block_func, input, ch_out, count, stride): return res_out -def resnet_imagenet(input, class_dim, depth=50, data_format='NCHW'): +def resnet_imagenet(input, + class_dim, + depth=50, + data_format='NCHW', + is_train=True): cfg = { 18: ([2, 2, 2, 1], basicblock), @@ -115,8 +128,9 @@ def resnet_cifar10(input, class_dim, depth=32, data_format='NCHW'): return out -def get_model(args): +def _model_reader_dshape_classdim(args, is_train): model = resnet_cifar10 + reader = None if args.data_set == "cifar10": class_dim = 10 if args.data_format == 'NCHW': @@ -124,8 +138,10 @@ def get_model(args): else: dshape = [32, 32, 3] model = resnet_cifar10 - train_reader = paddle.dataset.cifar.train10() - test_reader = paddle.dataset.cifar.test10() + if is_train: + reader = paddle.dataset.cifar.train10() + else: + reader = paddle.dataset.cifar.test10() elif args.data_set == "flowers": class_dim = 102 if args.data_format == 'NCHW': @@ -133,8 +149,10 @@ def get_model(args): else: dshape = [224, 224, 3] model = resnet_imagenet - train_reader = paddle.dataset.flowers.train() - test_reader = paddle.dataset.flowers.test() + if is_train: + reader = paddle.dataset.flowers.train() + else: + reader = paddle.dataset.flowers.test() elif args.data_set == "imagenet": class_dim = 1000 if args.data_format == 'NCHW': @@ -145,64 +163,89 @@ def get_model(args): if not args.data_path: raise Exception( "Must specify --data_path when training with imagenet") - train_reader = imagenet_train(args.data_path) - test_reader = imagenet_test(args.data_path) - - if args.use_reader_op: - filelist = [ - os.path.join(args.data_path, f) for f in os.listdir(args.data_path) - ] - data_file = fluid.layers.open_files( - filenames=filelist, - shapes=[[-1] + dshape, (-1, 1)], - lod_levels=[0, 0], - dtypes=["float32", "int64"], - thread_num=args.gpus, - pass_num=args.pass_num) - data_file = fluid.layers.double_buffer( - fluid.layers.batch( - data_file, batch_size=args.batch_size)) - input, label = fluid.layers.read_file(data_file) - else: - input = fluid.layers.data(name='data', shape=dshape, dtype='float32') - label = fluid.layers.data(name='label', shape=[1], dtype='int64') - - if args.device == 'CPU' and args.cpus > 1: - places = fluid.layers.get_places(args.cpus) - pd = fluid.layers.ParallelDo(places) - with pd.do(): - predict = model(pd.read_input(input), class_dim) - label = pd.read_input(label) + if not args.use_reader_op: + if is_train: + reader = train() + else: + reader = val() + else: + if is_train: + reader = train(xmap=False) + else: + reader = val(xmap=False) + return model, reader, dshape, class_dim + + +def get_model(args, is_train, main_prog, startup_prog): + model, reader, dshape, class_dim = _model_reader_dshape_classdim(args, + is_train) + + pyreader = None + trainer_count = int(os.getenv("PADDLE_TRAINERS")) + with fluid.program_guard(main_prog, startup_prog): + with fluid.unique_name.guard(): + if args.use_reader_op: + pyreader = fluid.layers.py_reader( + capacity=args.batch_size * args.gpus, + shapes=([-1] + dshape, (-1, 1)), + dtypes=('float32', 'int64'), + name="train_reader" if is_train else "test_reader", + use_double_buffer=True) + input, label = fluid.layers.read_file(pyreader) + else: + input = fluid.layers.data( + name='data', shape=dshape, dtype='float32') + label = fluid.layers.data( + name='label', shape=[1], dtype='int64') + + predict = model(input, class_dim, is_train=is_train) cost = fluid.layers.cross_entropy(input=predict, label=label) avg_cost = fluid.layers.mean(x=cost) - batch_acc = fluid.layers.accuracy(input=predict, label=label) - - pd.write_output(avg_cost) - pd.write_output(batch_acc) - avg_cost, batch_acc = pd() - avg_cost = fluid.layers.mean(avg_cost) - batch_acc = fluid.layers.mean(batch_acc) + batch_acc1 = fluid.layers.accuracy(input=predict, label=label, k=1) + batch_acc5 = fluid.layers.accuracy(input=predict, label=label, k=5) + + # configure optimize + optimizer = None + if is_train: + if args.use_lars: + lars_decay = 1.0 + else: + lars_decay = 0.0 + + total_images = 1281167 / trainer_count + + step = int(total_images / args.batch_size + 1) + epochs = [30, 60, 80, 90] + bd = [step * e for e in epochs] + base_lr = args.learning_rate + lr = [] + lr = [base_lr * (0.1**i) for i in range(len(bd) + 1)] + optimizer = fluid.optimizer.Momentum( + learning_rate=base_lr, + #learning_rate=fluid.layers.piecewise_decay( + # boundaries=bd, values=lr), + momentum=0.9, + regularization=fluid.regularizer.L2Decay(1e-4)) + optimizer.minimize(avg_cost) + + if args.memory_optimize: + fluid.memory_optimize(main_prog) + + # config readers + if not args.use_reader_op: + batched_reader = paddle.batch( + reader if args.no_random else paddle.reader.shuffle( + reader, buf_size=5120), + batch_size=args.batch_size * args.gpus, + drop_last=True) else: - predict = model(input, class_dim) - cost = fluid.layers.cross_entropy(input=predict, label=label) - avg_cost = fluid.layers.mean(x=cost) - batch_acc = fluid.layers.accuracy(input=predict, label=label) - - inference_program = fluid.default_main_program().clone() - with fluid.program_guard(inference_program): - inference_program = fluid.io.get_inference_program( - target_vars=[batch_acc]) - - optimizer = fluid.optimizer.Momentum(learning_rate=0.01, momentum=0.9) - - batched_train_reader = paddle.batch( - train_reader if args.no_random else paddle.reader.shuffle( - train_reader, buf_size=5120), - batch_size=args.batch_size * args.gpus, - drop_last=True) - batched_test_reader = paddle.batch( - test_reader, batch_size=args.batch_size, drop_last=True) - - return avg_cost, inference_program, optimizer, batched_train_reader,\ - batched_test_reader, batch_acc + batched_reader = None + pyreader.decorate_paddle_reader( + paddle.batch( + reader if args.no_random else paddle.reader.shuffle( + reader, buf_size=5120), + batch_size=args.batch_size)) + + return avg_cost, optimizer, [batch_acc1, + batch_acc5], batched_reader, pyreader diff --git a/benchmark/fluid/models/resnet_with_preprocess.py b/benchmark/fluid/models/resnet_with_preprocess.py new file mode 100644 index 0000000000..e8d661d847 --- /dev/null +++ b/benchmark/fluid/models/resnet_with_preprocess.py @@ -0,0 +1,268 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import functools +import numpy as np +import time +import os + +import cProfile, pstats, StringIO + +import paddle +import paddle.fluid as fluid +import paddle.fluid.core as core +import paddle.fluid.profiler as profiler +# from recordio_converter import imagenet_train, imagenet_test +from imagenet_reader import train_raw, val + + +def conv_bn_layer(input, + ch_out, + filter_size, + stride, + padding, + act='relu', + is_train=True): + conv1 = fluid.layers.conv2d( + input=input, + filter_size=filter_size, + num_filters=ch_out, + stride=stride, + padding=padding, + act=None, + bias_attr=False) + return fluid.layers.batch_norm(input=conv1, act=act, is_test=not is_train) + + +def shortcut(input, ch_out, stride, is_train=True): + ch_in = input.shape[1] # if args.data_format == 'NCHW' else input.shape[-1] + if ch_in != ch_out: + return conv_bn_layer( + input, ch_out, 1, stride, 0, None, is_train=is_train) + else: + return input + + +def basicblock(input, ch_out, stride, is_train=True): + short = shortcut(input, ch_out, stride, is_train=is_train) + conv1 = conv_bn_layer(input, ch_out, 3, stride, 1, is_train=is_train) + conv2 = conv_bn_layer(conv1, ch_out, 3, 1, 1, act=None, is_train=is_train) + return fluid.layers.elementwise_add(x=short, y=conv2, act='relu') + + +def bottleneck(input, ch_out, stride, is_train=True): + short = shortcut(input, ch_out * 4, stride, is_train=is_train) + conv1 = conv_bn_layer(input, ch_out, 1, stride, 0, is_train=is_train) + conv2 = conv_bn_layer(conv1, ch_out, 3, 1, 1, is_train=is_train) + conv3 = conv_bn_layer( + conv2, ch_out * 4, 1, 1, 0, act=None, is_train=is_train) + return fluid.layers.elementwise_add(x=short, y=conv3, act='relu') + + +def layer_warp(block_func, input, ch_out, count, stride): + res_out = block_func(input, ch_out, stride) + for i in range(1, count): + res_out = block_func(res_out, ch_out, 1) + return res_out + + +def resnet_imagenet(input, + class_dim, + depth=50, + data_format='NCHW', + is_train=True): + + cfg = { + 18: ([2, 2, 2, 1], basicblock), + 34: ([3, 4, 6, 3], basicblock), + 50: ([3, 4, 6, 3], bottleneck), + 101: ([3, 4, 23, 3], bottleneck), + 152: ([3, 8, 36, 3], bottleneck) + } + stages, block_func = cfg[depth] + conv1 = conv_bn_layer(input, ch_out=64, filter_size=7, stride=2, padding=3) + pool1 = fluid.layers.pool2d( + input=conv1, pool_type='avg', pool_size=3, pool_stride=2) + res1 = layer_warp(block_func, pool1, 64, stages[0], 1) + res2 = layer_warp(block_func, res1, 128, stages[1], 2) + res3 = layer_warp(block_func, res2, 256, stages[2], 2) + res4 = layer_warp(block_func, res3, 512, stages[3], 2) + pool2 = fluid.layers.pool2d( + input=res4, + pool_size=7, + pool_type='avg', + pool_stride=1, + global_pooling=True) + out = fluid.layers.fc(input=pool2, size=class_dim, act='softmax') + return out + + +def resnet_cifar10(input, class_dim, depth=32, data_format='NCHW'): + assert (depth - 2) % 6 == 0 + + n = (depth - 2) // 6 + + conv1 = conv_bn_layer( + input=input, ch_out=16, filter_size=3, stride=1, padding=1) + res1 = layer_warp(basicblock, conv1, 16, n, 1) + res2 = layer_warp(basicblock, res1, 32, n, 2) + res3 = layer_warp(basicblock, res2, 64, n, 2) + pool = fluid.layers.pool2d( + input=res3, pool_size=8, pool_type='avg', pool_stride=1) + out = fluid.layers.fc(input=pool, size=class_dim, act='softmax') + return out + + +def _model_reader_dshape_classdim(args, is_train): + model = resnet_cifar10 + reader = None + if args.data_set == "cifar10": + class_dim = 10 + if args.data_format == 'NCHW': + dshape = [3, 32, 32] + else: + dshape = [32, 32, 3] + model = resnet_cifar10 + if is_train: + reader = paddle.dataset.cifar.train10() + else: + reader = paddle.dataset.cifar.test10() + elif args.data_set == "flowers": + class_dim = 102 + if args.data_format == 'NCHW': + dshape = [3, 224, 224] + else: + dshape = [224, 224, 3] + model = resnet_imagenet + if is_train: + reader = paddle.dataset.flowers.train() + else: + reader = paddle.dataset.flowers.test() + elif args.data_set == "imagenet": + class_dim = 1000 + if args.data_format == 'NCHW': + dshape = [3, 224, 224] + else: + dshape = [224, 224, 3] + model = resnet_imagenet + if not args.data_path: + raise Exception( + "Must specify --data_path when training with imagenet") + if not args.use_reader_op: + if is_train: + reader = train_raw() + else: + reader = val() + else: + if is_train: + reader = train_raw() + else: + reader = val(xmap=False) + return model, reader, dshape, class_dim + + +def get_model(args, is_train, main_prog, startup_prog): + model, reader, dshape, class_dim = _model_reader_dshape_classdim(args, + is_train) + + pyreader = None + trainer_count = int(os.getenv("PADDLE_TRAINERS")) + with fluid.program_guard(main_prog, startup_prog): + with fluid.unique_name.guard(): + if args.use_reader_op: + pyreader = fluid.layers.py_reader( + capacity=args.batch_size * args.gpus, + shapes=([-1] + dshape, (-1, 1)), + dtypes=('uint8', 'int64'), + name="train_reader" if is_train else "test_reader", + use_double_buffer=True) + input, label = fluid.layers.read_file(pyreader) + else: + input = fluid.layers.data( + name='data', shape=dshape, dtype='uint8') + label = fluid.layers.data( + name='label', shape=[1], dtype='int64') + + # add imagenet preprocessors + random_crop = fluid.layers.random_crop(input, dshape) + casted = fluid.layers.cast(random_crop, 'float32') + # input is HWC + trans = fluid.layers.transpose(casted, [0, 3, 1, 2]) / 255.0 + img_mean = fluid.layers.tensor.assign( + np.array([0.485, 0.456, 0.406]).astype('float32').reshape((3, 1, + 1))) + img_std = fluid.layers.tensor.assign( + np.array([0.229, 0.224, 0.225]).astype('float32').reshape((3, 1, + 1))) + h1 = fluid.layers.elementwise_sub(trans, img_mean, axis=1) + h2 = fluid.layers.elementwise_div(h1, img_std, axis=1) + + # pre_out = (trans - img_mean) / img_std + + predict = model(h2, class_dim, is_train=is_train) + cost = fluid.layers.cross_entropy(input=predict, label=label) + avg_cost = fluid.layers.mean(x=cost) + + batch_acc1 = fluid.layers.accuracy(input=predict, label=label, k=1) + batch_acc5 = fluid.layers.accuracy(input=predict, label=label, k=5) + + # configure optimize + optimizer = None + if is_train: + if args.use_lars: + lars_decay = 1.0 + else: + lars_decay = 0.0 + + total_images = 1281167 / trainer_count + + step = int(total_images / args.batch_size + 1) + epochs = [30, 60, 80, 90] + bd = [step * e for e in epochs] + base_lr = args.learning_rate + lr = [] + lr = [base_lr * (0.1**i) for i in range(len(bd) + 1)] + optimizer = fluid.optimizer.Momentum( + learning_rate=base_lr, + #learning_rate=fluid.layers.piecewise_decay( + # boundaries=bd, values=lr), + momentum=0.9, + regularization=fluid.regularizer.L2Decay(1e-4)) + optimizer.minimize(avg_cost) + + if args.memory_optimize: + fluid.memory_optimize(main_prog) + + # config readers + if not args.use_reader_op: + batched_reader = paddle.batch( + reader if args.no_random else paddle.reader.shuffle( + reader, buf_size=5120), + batch_size=args.batch_size * args.gpus, + drop_last=True) + else: + batched_reader = None + pyreader.decorate_paddle_reader( + paddle.batch( + # reader if args.no_random else paddle.reader.shuffle( + # reader, buf_size=5120), + reader, + batch_size=args.batch_size)) + + return avg_cost, optimizer, [batch_acc1, + batch_acc5], batched_reader, pyreader diff --git a/benchmark/fluid/models/se_resnext.py b/benchmark/fluid/models/se_resnext.py new file mode 100644 index 0000000000..9f887fb324 --- /dev/null +++ b/benchmark/fluid/models/se_resnext.py @@ -0,0 +1,286 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import paddle +import paddle.fluid as fluid +import math +import os +from imagenet_reader import train, val + +__all__ = [ + "SE_ResNeXt", "SE_ResNeXt50_32x4d", "SE_ResNeXt101_32x4d", + "SE_ResNeXt152_32x4d", "get_model" +] + +train_parameters = { + "input_size": [3, 224, 224], + "input_mean": [0.485, 0.456, 0.406], + "input_std": [0.229, 0.224, 0.225], + "learning_strategy": { + "name": "piecewise_decay", + "batch_size": 256, + "epochs": [30, 60, 90], + "steps": [0.1, 0.01, 0.001, 0.0001] + } +} + + +class SE_ResNeXt(): + def __init__(self, layers=50, is_train=True): + self.params = train_parameters + self.layers = layers + self.is_train = is_train + + def net(self, input, class_dim=1000): + layers = self.layers + supported_layers = [50, 101, 152] + assert layers in supported_layers, \ + "supported layers are {} but input layer is {}".format(supported_layers, layers) + if layers == 50: + cardinality = 32 + reduction_ratio = 16 + depth = [3, 4, 6, 3] + num_filters = [128, 256, 512, 1024] + + conv = self.conv_bn_layer( + input=input, + num_filters=64, + filter_size=7, + stride=2, + act='relu') + conv = fluid.layers.pool2d( + input=conv, + pool_size=3, + pool_stride=2, + pool_padding=1, + pool_type='max') + elif layers == 101: + cardinality = 32 + reduction_ratio = 16 + depth = [3, 4, 23, 3] + num_filters = [128, 256, 512, 1024] + + conv = self.conv_bn_layer( + input=input, + num_filters=64, + filter_size=7, + stride=2, + act='relu') + conv = fluid.layers.pool2d( + input=conv, + pool_size=3, + pool_stride=2, + pool_padding=1, + pool_type='max') + elif layers == 152: + cardinality = 64 + reduction_ratio = 16 + depth = [3, 8, 36, 3] + num_filters = [128, 256, 512, 1024] + + conv = self.conv_bn_layer( + input=input, + num_filters=64, + filter_size=3, + stride=2, + act='relu') + conv = self.conv_bn_layer( + input=conv, num_filters=64, filter_size=3, stride=1, act='relu') + conv = self.conv_bn_layer( + input=conv, + num_filters=128, + filter_size=3, + stride=1, + act='relu') + conv = fluid.layers.pool2d( + input=conv, pool_size=3, pool_stride=2, pool_padding=1, \ + pool_type='max') + + for block in range(len(depth)): + for i in range(depth[block]): + conv = self.bottleneck_block( + input=conv, + num_filters=num_filters[block], + stride=2 if i == 0 and block != 0 else 1, + cardinality=cardinality, + reduction_ratio=reduction_ratio) + + pool = fluid.layers.pool2d( + input=conv, pool_size=7, pool_type='avg', global_pooling=True) + drop = fluid.layers.dropout(x=pool, dropout_prob=0.5) + stdv = 1.0 / math.sqrt(drop.shape[1] * 1.0) + out = fluid.layers.fc(input=drop, + size=class_dim, + act='softmax', + param_attr=fluid.param_attr.ParamAttr( + initializer=fluid.initializer.Uniform(-stdv, + stdv))) + return out + + def shortcut(self, input, ch_out, stride): + ch_in = input.shape[1] + if ch_in != ch_out or stride != 1: + filter_size = 1 + return self.conv_bn_layer(input, ch_out, filter_size, stride) + else: + return input + + def bottleneck_block(self, input, num_filters, stride, cardinality, + reduction_ratio): + conv0 = self.conv_bn_layer( + input=input, num_filters=num_filters, filter_size=1, act='relu') + conv1 = self.conv_bn_layer( + input=conv0, + num_filters=num_filters, + filter_size=3, + stride=stride, + groups=cardinality, + act='relu') + conv2 = self.conv_bn_layer( + input=conv1, num_filters=num_filters * 2, filter_size=1, act=None) + scale = self.squeeze_excitation( + input=conv2, + num_channels=num_filters * 2, + reduction_ratio=reduction_ratio) + + short = self.shortcut(input, num_filters * 2, stride) + + return fluid.layers.elementwise_add(x=short, y=scale, act='relu') + + def conv_bn_layer(self, + input, + num_filters, + filter_size, + stride=1, + groups=1, + act=None): + conv = fluid.layers.conv2d( + input=input, + num_filters=num_filters, + filter_size=filter_size, + stride=stride, + padding=(filter_size - 1) / 2, + groups=groups, + act=None, + bias_attr=False) + return fluid.layers.batch_norm( + input=conv, act=act, is_test=not self.is_train) + + def squeeze_excitation(self, input, num_channels, reduction_ratio): + pool = fluid.layers.pool2d( + input=input, pool_size=0, pool_type='avg', global_pooling=True) + stdv = 1.0 / math.sqrt(pool.shape[1] * 1.0) + squeeze = fluid.layers.fc(input=pool, + size=num_channels / reduction_ratio, + act='relu', + param_attr=fluid.param_attr.ParamAttr( + initializer=fluid.initializer.Uniform( + -stdv, stdv))) + stdv = 1.0 / math.sqrt(squeeze.shape[1] * 1.0) + excitation = fluid.layers.fc(input=squeeze, + size=num_channels, + act='sigmoid', + param_attr=fluid.param_attr.ParamAttr( + initializer=fluid.initializer.Uniform( + -stdv, stdv))) + scale = fluid.layers.elementwise_mul(x=input, y=excitation, axis=0) + return scale + + +def SE_ResNeXt50_32x4d(): + model = SE_ResNeXt(layers=50) + return model + + +def SE_ResNeXt101_32x4d(): + model = SE_ResNeXt(layers=101) + return model + + +def SE_ResNeXt152_32x4d(): + model = SE_ResNeXt(layers=152) + return model + + +def get_model(args, is_train, main_prog, startup_prog): + model = SE_ResNeXt(layers=50) + batched_reader = None + pyreader = None + trainer_count = int(os.getenv("PADDLE_TRAINERS")) + dshape = train_parameters["input_size"] + + with fluid.program_guard(main_prog, startup_prog): + with fluid.unique_name.guard(): + if args.use_reader_op: + pyreader = fluid.layers.py_reader( + capacity=10, + shapes=([-1] + dshape, (-1, 1)), + dtypes=('float32', 'int64'), + name="train_reader" if is_train else "test_reader", + use_double_buffer=True) + input, label = fluid.layers.read_file(pyreader) + else: + input = fluid.layers.data( + name='data', shape=dshape, dtype='float32') + label = fluid.layers.data( + name='label', shape=[1], dtype='int64') + + out = model.net(input=input) + cost = fluid.layers.cross_entropy(input=out, label=label) + avg_cost = fluid.layers.mean(x=cost) + acc_top1 = fluid.layers.accuracy(input=out, label=label, k=1) + acc_top5 = fluid.layers.accuracy(input=out, label=label, k=5) + + optimizer = None + if is_train: + if args.use_lars: + lars_decay = 1.0 + else: + lars_decay = 0.0 + + total_images = 1281167 / trainer_count + + step = int(total_images / args.batch_size + 1) + epochs = [40, 80, 100] + bd = [step * e for e in epochs] + base_lr = args.learning_rate + lr = [] + lr = [base_lr * (0.1**i) for i in range(len(bd) + 1)] + optimizer = fluid.optimizer.Momentum( + # learning_rate=base_lr, + learning_rate=fluid.layers.piecewise_decay( + boundaries=bd, values=lr), + momentum=0.9, + regularization=fluid.regularizer.L2Decay(1e-4), + LARS_weight_decay=lars_decay) + optimizer.minimize(avg_cost) + + if args.memory_optimize: + fluid.memory_optimize(main_prog) + + # config readers + if is_train: + reader = train() + else: + reader = val() + + if not args.use_reader_op: + batched_reader = paddle.batch( + reader, batch_size=args.batch_size * args.gpus, drop_last=True) + else: + pyreader.decorate_paddle_reader( + paddle.batch( + reader, batch_size=args.batch_size)) + + return avg_cost, optimizer, [acc_top1, acc_top5], batched_reader, pyreader diff --git a/benchmark/fluid/models/stacked_dynamic_lstm.py b/benchmark/fluid/models/stacked_dynamic_lstm.py index 3231542a17..f23bb59de9 100644 --- a/benchmark/fluid/models/stacked_dynamic_lstm.py +++ b/benchmark/fluid/models/stacked_dynamic_lstm.py @@ -26,7 +26,6 @@ import numpy import paddle import paddle.dataset.imdb as imdb import paddle.fluid as fluid -import paddle.batch as batch import paddle.fluid.profiler as profiler word_dict = imdb.word_dict() @@ -43,19 +42,7 @@ def crop_sentence(reader, crop_size): return __impl__ -def get_model(args): - if args.use_reader_op: - raise Exception( - "stacked_dynamic_lstm do not support reader op for now.") - lstm_size = 512 - emb_dim = 512 - crop_size = 1500 - - data = fluid.layers.data( - name="words", shape=[1], lod_level=1, dtype='int64') - sentence = fluid.layers.embedding( - input=data, size=[len(word_dict), emb_dim]) - +def lstm_net(sentence, lstm_size): sentence = fluid.layers.fc(input=sentence, size=lstm_size, act='tanh') rnn = fluid.layers.DynamicRNN() @@ -97,31 +84,47 @@ def get_model(args): last = fluid.layers.sequence_pool(rnn(), 'last') logit = fluid.layers.fc(input=last, size=2, act='softmax') - loss = fluid.layers.cross_entropy( - input=logit, - label=fluid.layers.data( - name='label', shape=[1], dtype='int64')) - loss = fluid.layers.mean(x=loss) + return logit - # add acc - batch_size_tensor = fluid.layers.create_tensor(dtype='int64') - batch_acc = fluid.layers.accuracy(input=logit, label=fluid.layers.data(name='label', \ - shape=[1], dtype='int64'), total=batch_size_tensor) - inference_program = fluid.default_main_program().clone() - with fluid.program_guard(inference_program): - inference_program = fluid.io.get_inference_program( - target_vars=[batch_acc, batch_size_tensor]) - - adam = fluid.optimizer.Adam() +def get_model(args, is_train, main_prog, startup_prog): + if args.use_reader_op: + raise Exception( + "stacked_dynamic_lstm do not support reader op for now.") + lstm_size = 512 + emb_dim = 512 + crop_size = 1500 - train_reader = batch( + with fluid.program_guard(main_prog, startup_prog): + with fluid.unique_name.guard(): + data = fluid.layers.data( + name="words", shape=[1], lod_level=1, dtype='int64') + sentence = fluid.layers.embedding( + input=data, size=[len(word_dict), emb_dim]) + logit = lstm_net(sentence, lstm_size) + loss = fluid.layers.cross_entropy( + input=logit, + label=fluid.layers.data( + name='label', shape=[1], dtype='int64')) + loss = fluid.layers.mean(x=loss) + + # add acc + batch_size_tensor = fluid.layers.create_tensor(dtype='int64') + batch_acc = fluid.layers.accuracy(input=logit, label=fluid.layers.data(name='label', \ + shape=[1], dtype='int64'), total=batch_size_tensor) + + if is_train: + adam = fluid.optimizer.Adam() + adam.minimize(loss) + + if is_train: + reader = crop_sentence(imdb.train(word_dict), crop_size) + else: + reader = crop_sentence(imdb.test(word_dict), crop_size) + + batched_reader = paddle.batch( paddle.reader.shuffle( - crop_sentence(imdb.train(word_dict), crop_size), buf_size=25000), + reader, buf_size=25000), batch_size=args.batch_size * args.gpus) - test_reader = batch( - paddle.reader.shuffle( - crop_sentence(imdb.test(word_dict), crop_size), buf_size=25000), - batch_size=args.batch_size) - return loss, inference_program, adam, train_reader, test_reader, batch_acc + return loss, adam, [batch_acc], batched_reader, None diff --git a/benchmark/fluid/models/vgg.py b/benchmark/fluid/models/vgg.py index 932601302d..cf9708d500 100644 --- a/benchmark/fluid/models/vgg.py +++ b/benchmark/fluid/models/vgg.py @@ -25,7 +25,7 @@ import functools import os -def vgg16_bn_drop(input): +def vgg16_bn_drop(input, is_train=True): def conv_block(input, num_filter, groups, dropouts): return fluid.nets.img_conv_group( input=input, @@ -46,13 +46,13 @@ def vgg16_bn_drop(input): drop = fluid.layers.dropout(x=conv5, dropout_prob=0.5) fc1 = fluid.layers.fc(input=drop, size=512, act=None) - bn = fluid.layers.batch_norm(input=fc1, act='relu') + bn = fluid.layers.batch_norm(input=fc1, act='relu', is_test=not is_train) drop2 = fluid.layers.dropout(x=bn, dropout_prob=0.5) fc2 = fluid.layers.fc(input=drop2, size=512, act=None) return fc2 -def get_model(args): +def get_model(args, is_train, main_prog, startup_prog): if args.data_set == "cifar10": classdim = 10 if args.data_format == 'NCHW': @@ -65,57 +65,56 @@ def get_model(args): data_shape = [3, 224, 224] else: data_shape = [224, 224, 3] + filelist = [ + os.path.join(args.data_path, f) for f in os.listdir(args.data_path) + ] + with fluid.program_guard(main_prog, startup_prog): + if args.use_reader_op: + data_file_handle = fluid.layers.open_files( + filenames=filelist, + shapes=[[-1] + data_shape, (-1, 1)], + lod_levels=[0, 0], + dtypes=["float32", "int64"], + thread_num=1, + pass_num=1) + data_file = fluid.layers.double_buffer( + fluid.layers.batch( + data_file_handle, batch_size=args.batch_size)) + with fluid.unique_name.guard(): + if args.use_reader_op: + images, label = fluid.layers.read_file(data_file) + else: + images = fluid.layers.data( + name='data', shape=data_shape, dtype='float32') + label = fluid.layers.data( + name='label', shape=[1], dtype='int64') + # Train program + net = vgg16_bn_drop(images, is_train=is_train) + predict = fluid.layers.fc(input=net, size=classdim, act='softmax') + cost = fluid.layers.cross_entropy(input=predict, label=label) + avg_cost = fluid.layers.mean(x=cost) - if args.use_reader_op: - filelist = [ - os.path.join(args.data_path, f) for f in os.listdir(args.data_path) - ] - data_file = fluid.layers.open_files( - filenames=filelist, - shapes=[[-1] + data_shape, (-1, 1)], - lod_levels=[0, 0], - dtypes=["float32", "int64"], - thread_num=args.gpus, - pass_num=args.pass_num) - data_file = fluid.layers.double_buffer( - fluid.layers.batch( - data_file, batch_size=args.batch_size)) - images, label = fluid.layers.read_file(data_file) - else: - images = fluid.layers.data( - name='data', shape=data_shape, dtype='float32') - label = fluid.layers.data(name='label', shape=[1], dtype='int64') - - # Train program - net = vgg16_bn_drop(images) - predict = fluid.layers.fc(input=net, size=classdim, act='softmax') - cost = fluid.layers.cross_entropy(input=predict, label=label) - avg_cost = fluid.layers.mean(x=cost) - - # Evaluator - batch_size_tensor = fluid.layers.create_tensor(dtype='int64') - batch_acc = fluid.layers.accuracy( - input=predict, label=label, total=batch_size_tensor) - - # inference program - inference_program = fluid.default_main_program().clone() - with fluid.program_guard(inference_program): - inference_program = fluid.io.get_inference_program( - target_vars=[batch_acc, batch_size_tensor]) - - # Optimization - optimizer = fluid.optimizer.Adam(learning_rate=args.learning_rate) + # Evaluator + batch_size_tensor = fluid.layers.create_tensor(dtype='int64') + batch_acc = fluid.layers.accuracy( + input=predict, label=label, total=batch_size_tensor) + # Optimization + if is_train: + optimizer = fluid.optimizer.Adam( + learning_rate=args.learning_rate) + optimizer.minimize(avg_cost) # data reader - train_reader = paddle.batch( + if is_train: + reader = paddle.dataset.cifar.train10() \ + if args.data_set == 'cifar10' else paddle.dataset.flowers.train() + else: + reader = paddle.dataset.cifar.test10() \ + if args.data_set == 'cifar10' else paddle.dataset.flowers.test() + + batched_reader = paddle.batch( paddle.reader.shuffle( - paddle.dataset.cifar.train10() - if args.data_set == 'cifar10' else paddle.dataset.flowers.train(), - buf_size=5120), + reader, buf_size=5120), batch_size=args.batch_size * args.gpus) - test_reader = paddle.batch( - paddle.dataset.cifar.test10() - if args.data_set == 'cifar10' else paddle.dataset.flowers.test(), - batch_size=args.batch_size) - return avg_cost, inference_program, optimizer, train_reader, test_reader, batch_acc + return avg_cost, optimizer, [batch_acc], batched_reader, data_file_handle diff --git a/paddle/fluid/API.spec b/paddle/fluid/API.spec index c2694144d7..ae5f30e431 100644 --- a/paddle/fluid/API.spec +++ b/paddle/fluid/API.spec @@ -66,7 +66,7 @@ paddle.fluid.InferenceTranspiler.transpile ArgSpec(args=['self', 'program', 'pla paddle.fluid.memory_optimize ArgSpec(args=['input_program', 'skip_opt_set', 'print_log', 'level'], varargs=None, keywords=None, defaults=(None, False, 0)) paddle.fluid.release_memory ArgSpec(args=['input_program', 'skip_opt_set'], varargs=None, keywords=None, defaults=(None,)) paddle.fluid.DistributeTranspilerConfig.__init__ -paddle.fluid.ParallelExecutor.__init__ ArgSpec(args=['self', 'use_cuda', 'loss_name', 'main_program', 'share_vars_from', 'exec_strategy', 'build_strategy', 'num_trainers', 'trainer_id'], varargs=None, keywords='kwargs', defaults=(None, None, None, None, None, 1, 0)) +paddle.fluid.ParallelExecutor.__init__ ArgSpec(args=['self', 'use_cuda', 'loss_name', 'main_program', 'share_vars_from', 'exec_strategy', 'build_strategy', 'num_trainers', 'trainer_id', 'scope'], varargs=None, keywords='kwargs', defaults=(None, None, None, None, None, 1, 0, None)) paddle.fluid.ParallelExecutor.run ArgSpec(args=['self', 'fetch_list', 'feed', 'feed_dict', 'return_numpy'], varargs=None, keywords=None, defaults=(None, None, True)) paddle.fluid.ExecutionStrategy.__init__ __init__(self: paddle.fluid.core.ExecutionStrategy) -> None paddle.fluid.BuildStrategy.GradientScaleStrategy.__init__ __init__(self: paddle.fluid.core.GradientScaleStrategy, arg0: int) -> None diff --git a/paddle/fluid/platform/nccl_helper.h b/paddle/fluid/platform/nccl_helper.h index cc46c88fd1..115abb98d5 100644 --- a/paddle/fluid/platform/nccl_helper.h +++ b/paddle/fluid/platform/nccl_helper.h @@ -100,14 +100,13 @@ struct NCCLContextMap { return; } std::unique_ptr comms(new ncclComm_t[order_.size()]); - // if pass nccl_id here, can assume we are doing multi node training - if (nccl_id == nullptr) { + // if num_trainers == 1, should create a new nccl id for local comms. + if (num_trainers == 1) { std::lock_guard guard(NCCLGroupGuard::NCCLMutex()); PADDLE_ENFORCE(platform::dynload::ncclCommInitAll( comms.get(), static_cast(order_.size()), order_.data())); } else { - PADDLE_ENFORCE_GT(num_trainers, 1); - // TODO(wuyi): need to ensure each node have same number of GPUs + PADDLE_ENFORCE_NOT_NULL(nccl_id); { int nranks = num_trainers * order_.size(); NCCLGroupGuard gurad; diff --git a/python/paddle/fluid/parallel_executor.py b/python/paddle/fluid/parallel_executor.py index a7765c9591..4790e0f611 100644 --- a/python/paddle/fluid/parallel_executor.py +++ b/python/paddle/fluid/parallel_executor.py @@ -43,8 +43,9 @@ class ParallelExecutor(object): num_trainers(int): If greater than 1, NCCL will be initialized with multiple rank of nodes, each node should have same number of GPUs. Distributed training will be enabled then. Default 1. - trainer_id(int: Must use together with num_trainers. trainer_id is the + trainer_id(int): Must use together with num_trainers. trainer_id is the "rank" of current node starts from 0. Default 0. + scope(Scope): scope to run with, default use fluid.global_scope(). Returns: ParallelExecutor: The initialized ParallelExecutor object. @@ -73,6 +74,7 @@ class ParallelExecutor(object): build_strategy=None, num_trainers=1, trainer_id=0, + scope=None, **kwargs): if len(kwargs) != 0: err_msg = "" @@ -131,7 +133,8 @@ class ParallelExecutor(object): main = main_program main = main if main else framework.default_main_program() - scope = executor.global_scope() + if scope == None: + scope = executor.global_scope() # FIXME(Yancey1989): it's a temporary approach to determinate the distribute # train program, call self.bcast_param() at the end of each mini-batch. self.is_dist = True if "recv" in [ From 14242eae745201db59e98e22b1daaae84a757688 Mon Sep 17 00:00:00 2001 From: Yi Wang Date: Sun, 9 Sep 2018 11:52:46 -0700 Subject: [PATCH 42/44] slightly restructure the document of dynamic nets (#13287) * slightly restructure the document [skip ci] * Update dynamic_graph.md --- doc/survey/dynamic_graph.md | 61 +++++++++++++++++++------------------ 1 file changed, 31 insertions(+), 30 deletions(-) diff --git a/doc/survey/dynamic_graph.md b/doc/survey/dynamic_graph.md index d03212007a..7f62eeadff 100644 --- a/doc/survey/dynamic_graph.md +++ b/doc/survey/dynamic_graph.md @@ -2,28 +2,31 @@ ## Automatic Differentiation -A key challenge in the field of deep learning is to automatically derive the backward pass from the forward pass described algorithmically by researchers. Such a derivation, or a transformation of the forward pass program, has been long studied before the recent prosperity of deep learning in the field known as [automatic differentiation](https://arxiv.org/pdf/1502.05767.pdf). +A key challenge in deep learning is to automatically derive the backward pass given the forward pass as a program, which has been long studied in the field of [automatic differentiation](https://arxiv.org/pdf/1502.05767.pdf), or autodiff, before the prosperity of deep learning. -## The Tape +## Program Transformation v.s. Backtracking -Given the forward pass program (usually in Python in practices), there are two strategies to derive the backward pass: +Given the forward pass program, there are two strategies to derive the backward pass: -1. from the forward pass program itself, or -1. from the execution trace of the forward pass program, which is often known as the *tape*. +1. by transforming the forward pass program without executing it, or +1. by backtracking the execution process of the forward pass program. -This article surveys systems that follow the latter strategy. +This article is about the latter strategy. -## Dynamic Network +## The Tape and Dynamic Networks -When we train a deep learning model, the tape changes every iteration as the input data change, so we have to re-derive the backward pass every iteration. This is known as *dynamic network*. +We refer to the trace of the execution of the forward pass program as a *tape* [[1]](http://www.bcl.hamilton.ie/~barak/papers/toplas-reverse.pdf). When we train a deep learning model, the tape changes every iteration as the input data change, so we'd have to re-derive the backward pass, which is time-consuming, but also eases the case that the forward program includes control flows like if-else and for/while. With these control flows, the execution trace might change with iterations. Such changes are known as *dynamic networks* in the field of deep learning. -Deep learning systems that utilize the idea of dynamic network gained their popularities in recent years. This article surveys two representative systems: [PyTorch](https://pytorch.org/) and [DyNet](https://dynet.readthedocs.io/en/latest/). +## Typical Systems -## An Overview +Deep learning systems that utilize the idea of dynamic networks gained their popularities in recent years. This article surveys the following typical systems: -Both frameworks record a ‘tape’ of the computation and interpreting (or run-time compiling) a transformation of the tape played back in reverse. This tape is a different kind of entity than the original program.[[link]](http://www.bcl.hamilton.ie/~barak/papers/toplas-reverse.pdf) +- [DyNet](https://dynet.readthedocs.io/en/latest/) +- [PyTorch](https://pytorch.org/) +- Chainer +- Autograd from HIPS -Consider the following code feedforward model. +Before diving into these systems, let us pose an example forward pass program: ```python x = Variable(randn(20, 1))) @@ -35,9 +38,11 @@ loss = softmax(pred, label) loss.backward() ``` -### 1) Dynet uses List to encode the Tape +## The Representation of Tapes -During the forward execution, a list of operators, in this case `matmul`, `matmul` and `softmax`, are recorded in the tape, along with the necessary information needed to do the backward such as pointers to the inputs and outputs. Then the tape is played in reverse order at `loss.backward()`. +### DyNet: the Tape as a List + +DyNet uses a linear data structure, a list, to represent the tape. During the execution of the above example, it is a list of operators: `matmul`, `matmul`, and `softmax`. The list also includes information needed to do the backward pass, such as pointers to the inputs and outputs. Then the tape is played in reverse order at `loss.backward().`
@@ -69,9 +74,9 @@ digraph g { ![Alt text](https://g.gravizo.com/svg?digraph%20g%20{%20graph%20[%20rankdir%20=%20%22LR%22%20];%20node%20[%20fontsize%20=%20%2216%22%20shape%20=%20%22ellipse%22%20];%20edge%20[];%20%22node0%22%20[%20label%20=%20%22%3Cf0%3E%20type:%20matmul%20|%20%3Cf1%3E%20input:%20W_1,%20x%20|%20%3Cf2%3E%20output:%20h%22%20shape%20=%20%22record%22%20];%20%22node1%22%20[%20label%20=%20%22%3Cf0%3E%20type:%20matmul%20|%20%3Cf1%3E%20input:%20W_2,%20h%20|%20%3Cf2%3E%20output:%20pred%22%20shape%20=%20%22record%22%20];%20%22node2%22%20[%20label%20=%20%22%3Cf0%3E%20type:%20softmax%20|%20%3Cf1%3E%20input:%20pred,%20label%20|%20%3Cf2%3E%20output:%20loss%22%20shape%20=%20%22record%22%20];%20%22node0%22:f0%20-%3E%20%22node1%22:f0%20[%20id%20=%200%20];%20%22node1%22:f0%20-%3E%20%22node2%22:f0%20[%20id%20=%201%20];%20}) -### 2) Pytorch uses Node Graph to encode the Tape +### PyTorch: the Tape as a Graph -The graph is composed of `Variable`s and `Function`s. During the forward execution, a `Variable` records its creator function, e.g. `h.creator = matmul`. And a Function records its inputs' previous/dependent functions `prev_func` through `creator`, e.g. `matmul.prev_func = matmul1`. At `loss.backward()`, a topological sort is performed on all `prev_func`s. Then the grad op is performed by the sorted order. +The graph is composed of `Variable`s and `Function`s. During the forward execution, a `Variable` records its creator function, e.g. `h.creator = matmul`. And a Function records its inputs' previous/dependent functions `prev_func` through `creator`, e.g. `matmul.prev_func = matmul1`. At `loss.backward()`, a topological sort is performed on all `prev_func`s. Then the grad op is performed by the sorted order. Please be aware that a `Function` might have more than one `prev_func`s.
@@ -132,27 +137,22 @@ digraph g { ![Alt text](https://g.gravizo.com/svg?digraph%20g%20{%20graph%20[%20rankdir%20=%20%22LR%22%20];%20subgraph%20function%20{%20node%20[%20fontsize%20=%20%2216%22%20style%20=%20filled%20shape%20=%20%22record%22%20];%20%22matmul0%22%20[%20label%20=%20%22%3Cf0%3E%20type:%20matmul%20|%20prev_func:%20None%22%20];%20%22matmul1%22%20[%20label%20=%20%22%3Cf0%3E%20type:%20matmul%20|%20prev_func:%20matmul%22%20];%20%22softmax%22%20[%20label%20=%20%22%3Cf0%3E%20type:%20softmax%20|%20prev_func:%20matmul%22%20];%20}%20subgraph%20variable%20{%20node%20[%20fontsize%20=%20%2216%22%20shape%20=%20%22Mrecord%22%20style%20=%20filled%20fillcolor%20=%20white%20];%20%22x%22%20[%20label%20=%20%22%3Cf0%3E%20x%20|%20%3Cf1%3E%20creator:%20None%22%20];%20%22label%22%20[%20label%20=%20%22%3Cf0%3E%20label%20|%20%3Cf1%3E%20creator:%20None%22%20];%20%22W_1%22%20[%20label%20=%20%22%3Cf0%3E%20W_1%20|%20%3Cf1%3E%20creator:%20None%22%20];%20%22W_2%22%20[%20label%20=%20%22%3Cf0%3E%20W_2%20|%20%3Cf1%3E%20creator:%20None%22%20];%20%22h%22%20[%20label%20=%20%22%3Cf0%3E%20h%20|%20%3Cf1%3E%20creator:%20None%22%20];%20%22pred%22%20[%20label%20=%20%22%3Cf0%3E%20pred%20|%20%3Cf1%3E%20creator:%20matmul%22%20];%20%22loss%22%20[%20label%20=%20%22%3Cf0%3E%20loss%20|%20%3Cf1%3E%20creator:%20softmax%22%20];%20}%20subgraph%20data_flow%20{%20%22x%22:f0%20-%3E%20%22matmul0%22:f0;%20%22W_1%22:f0%20-%3E%20%22matmul0%22:f0;%20%22matmul0%22:f0%20-%3E%20%22h%22:f0;%20%22h%22:f0%20-%3E%20%22matmul1%22:f0;%20%22W_2%22:f0%20-%3E%20%22matmul1%22:f0;%20%22matmul1%22:f0%20-%3E%20%22pred%22:f0;%20%22pred%22:f0%20-%3E%20%22softmax%22:f0;%20%22label%22:f0%20-%3E%20%22softmax%22:f0;%20%22softmax%22:f0%20-%3E%20%22loss%22:f0;%20}%20subgraph%20prev_func%20{%20edge%20[color=%22red%22,%20arrowsize=%220.6%22,%20penwidth=%221%22,%20constraint=false];%20%22matmul1%22:f1%20-%3E%20%22matmul0%22:f0;%20%22softmax%22:f1%20-%3E%20%22matmul1%22:f0;%20label%20=%20%22prev_func%22;%20}%20}) -Chainer and Autograd uses the similar techniques to record the forward pass. For details please refer to the appendix. - -## Design choices +Chainer and Autograd use the similar techniques to record the forward pass. For details, please refer to the appendix. -### 1) Dynet's List vs Pytorch's Node Graph +## Comparison: List v.s. Graph -What's good about List: -1. It avoids a topological sort. One only needs to traverse the list of operators in reverse and calling the corresponding backward operator. -1. It promises effient data parallelism implementations. One could count the time of usage of a certain variable during the construction list. Then in the play back, one knows the calculation of a variable has completed. This enables communication and computation overlapping. +The list of DyNet could be considered the result of the topological sort of the graph of PyTorch. Or, the graph is the raw representation of the tape, which gives us the chance to *prune* part of the graph that is irrelevant with the backward pass before the topological sort [[2]](https://openreview.net/pdf?id=BJJsrmfCZ). Consider the following example, PyTorch only does backward on `SmallNet` while DyNet does both `SmallNet` and `BigNet`: -What's good about Node Graph: -1. More flexibility. PyTorch users can mix and match independent graphs however they like, in whatever threads they like (without explicit synchronization). An added benefit of structuring graphs this way is that when a portion of the graph becomes dead, it is automatically freed. [[2]](https://openreview.net/pdf?id=BJJsrmfCZ) Consider the following example, Pytorch only does backward on SmallNet while Dynet does both BigNet and SmallNet. ```python result = BigNet(data) loss = SmallNet(data) loss.backward() ``` -### 2) Dynet's Lazy evaluation vs Pytorch's Immediate evaluation +## Lazy v.s. Immediate Evaluation + +Another difference between DyNet and PyTorch is that DyNet lazily evaluates the forward pass, whereas PyTorch executes it immediately. Consider the following example: -Dynet builds the list in a symbolic matter. Consider the following example ```python for epoch in range(num_epochs): for in_words, out_label in training_data: @@ -164,16 +164,17 @@ for epoch in range(num_epochs): loss_val = loss_sym.value() loss_sym.backward() ``` + The computation of `lookup`, `concat`, `matmul` and `softmax` didn't happen until the call of `loss_sym.value()`. This defered execution is useful because it allows some graph-like optimization possible, e.g. kernel fusion. -Pytorch chooses immediate evaluation. It avoids ever materializing a "forward graph"/"tape" (no need to explicitly call `dy.renew_cg()` to reset the list), recording only what is necessary to differentiate the computation, i.e. `creator` and `prev_func`. +PyTorch chooses immediate evaluation. It avoids ever materializing a "forward graph"/"tape" (no need to explicitly call `dy.renew_cg()` to reset the list), recording only what is necessary to differentiate the computation, i.e. `creator` and `prev_func`. -## What can fluid learn from them? +## Fluid: Learning the Lessons Please refer to `paddle/contrib/dynamic/`. -# Appendix +## Appendix ### Overview From 478a4e850e6e4287495b4e3cf1ff5e8252ff557c Mon Sep 17 00:00:00 2001 From: Yan Chunwei Date: Mon, 10 Sep 2018 13:26:07 +0800 Subject: [PATCH 43/44] refactor ir pattern (#13304) --- paddle/fluid/framework/ir/fc_fuse_pass.cc | 33 ++-- paddle/fluid/framework/ir/fc_gru_fuse_pass.cc | 106 +++++------- .../fluid/framework/ir/fc_lstm_fuse_pass.cc | 152 +++++++----------- .../framework/ir/graph_pattern_detector.cc | 123 +++++++------- .../framework/ir/graph_pattern_detector.h | 134 ++++++++++++++- .../framework/ir/seq_concat_fc_fuse_pass.cc | 6 + .../inference/analysis/analyzer_tester.cc | 2 + 7 files changed, 316 insertions(+), 240 deletions(-) diff --git a/paddle/fluid/framework/ir/fc_fuse_pass.cc b/paddle/fluid/framework/ir/fc_fuse_pass.cc index 5a4ebd6f3d..ca704c7f56 100644 --- a/paddle/fluid/framework/ir/fc_fuse_pass.cc +++ b/paddle/fluid/framework/ir/fc_fuse_pass.cc @@ -29,39 +29,27 @@ std::unique_ptr FCFusePass::ApplyImpl( std::unordered_set nodes2delete; GraphPatternDetector gpd; - // BuildFCPattern(gpd.mutable_pattern()); auto* x = gpd.mutable_pattern() ->NewNode("fc_fuse/x") ->AsInput() ->assert_is_op_input("mul", "X"); - patterns::FC(gpd.mutable_pattern(), "fc_fuse", x, true /*with bias*/); - -#define GET_NODE(id) \ - PADDLE_ENFORCE(subgraph.count(gpd.pattern().RetrieveNode("fc_fuse/" #id)), \ - "pattern has no Node called %s", #id); \ - auto* id = subgraph.at(gpd.pattern().RetrieveNode("fc_fuse/" #id)); \ - PADDLE_ENFORCE_NOT_NULL(id, "subgraph has no node %s", "fc_fuse/" #id); + patterns::FC fc_pattern(gpd.mutable_pattern(), "fc_fuse"); + fc_pattern(x, true /*with bias*/); int found_fc_count = 0; auto handler = [&](const GraphPatternDetector::subgraph_t& subgraph, Graph* g) { VLOG(4) << "handle FC fuse"; - // Currently, there is no FC op available, so I will just simulate the - // scenerio. - // FC's fusion is simple, just op fuse, no need to process the - // parameters. - GET_NODE(x); // x - GET_NODE(w); // Y - GET_NODE(fc_bias); // bias - GET_NODE(fc_out); // Out - GET_NODE(mul); // MUL op - GET_NODE(elementwise_add); // ELEMENT_ADD op - GET_NODE(mul_out); // tmp -#undef GET_NODE + GET_IR_NODE_FROM_SUBGRAPH(w, w, fc_pattern); + GET_IR_NODE_FROM_SUBGRAPH(fc_bias, bias, fc_pattern); + GET_IR_NODE_FROM_SUBGRAPH(fc_out, Out, fc_pattern); + GET_IR_NODE_FROM_SUBGRAPH(mul, mul, fc_pattern); + GET_IR_NODE_FROM_SUBGRAPH(elementwise_add, elementwise_add, fc_pattern); + GET_IR_NODE_FROM_SUBGRAPH(mul_out, mul_out, fc_pattern); // Create an FC Node. OpDesc desc; - std::string fc_x_in = x->Name(); + std::string fc_x_in = subgraph.at(x)->Name(); std::string fc_Y_in = w->Name(); std::string fc_bias_in = fc_bias->Name(); std::string fc_out_out = fc_out->Name(); @@ -73,7 +61,8 @@ std::unique_ptr FCFusePass::ApplyImpl( auto fc_node = g->CreateOpNode(&desc); // OpDesc will be copied. GraphSafeRemoveNodes(graph.get(), {mul, elementwise_add, mul_out}); - IR_NODE_LINK_TO(x, fc_node); + PADDLE_ENFORCE(subgraph.count(x)); + IR_NODE_LINK_TO(subgraph.at(x), fc_node); IR_NODE_LINK_TO(w, fc_node); IR_NODE_LINK_TO(fc_bias, fc_node); IR_NODE_LINK_TO(fc_node, fc_out); diff --git a/paddle/fluid/framework/ir/fc_gru_fuse_pass.cc b/paddle/fluid/framework/ir/fc_gru_fuse_pass.cc index 90d8d5c042..a902b0b50c 100644 --- a/paddle/fluid/framework/ir/fc_gru_fuse_pass.cc +++ b/paddle/fluid/framework/ir/fc_gru_fuse_pass.cc @@ -20,52 +20,43 @@ namespace paddle { namespace framework { namespace ir { -static void BuildPattern(PDPattern* pattern, const std::string& name_scope, - bool with_fc_bias) { - PDNode* x = pattern->NewNode(name_scope, "x") - ->assert_is_op_input("mul") - ->assert_var_not_persistable(); - auto* fc_out = patterns::FC(pattern, name_scope, x, with_fc_bias); - fc_out->AsIntermediate(); // fc_out is a tmp var, will be removed after fuse. - patterns::GRU(pattern, name_scope, fc_out); - VLOG(3) << "fc_gru pattern \n" << pattern->DotString(); -} - static int BuildFusion(Graph* graph, const std::string& name_scope, Scope* scope, bool with_fc_bias) { GraphPatternDetector gpd; auto* pattern = gpd.mutable_pattern(); - BuildPattern(pattern, name_scope, with_fc_bias); + // Create pattern. + patterns::FC fc_pattern(pattern, name_scope); + patterns::GRU gru_pattern(pattern, name_scope); + + PDNode* x = + pattern->NewNode(patterns::UniqueKey("x"))->assert_var_not_persistable(); + + auto* fc_out = fc_pattern(x, with_fc_bias); + fc_out->AsIntermediate(); // fc_out is a tmp var, will be removed after fuse. + gru_pattern(fc_out); // Create New OpDesc - auto gru_creater = [&](int gru, int x, int weight_x, int weight_h, int bias, - int hidden, int fc_bias) { -#define GET_NODE(x) auto* x##_n = graph->RetriveNode(x); - GET_NODE(x); - GET_NODE(weight_x); - GET_NODE(weight_h); - GET_NODE(bias); - GET_NODE(hidden); - GET_NODE(gru); + auto gru_creater = [&](Node* gru, Node* x, Node* weight_x, Node* weight_h, + Node* bias, Node* hidden, Node* fc_bias) { OpDesc op_desc; op_desc.SetType("fusion_gru"); #define NEW_NAME(x) name_scope + "/at." #x ".new" -#define SET_IN(Key, node__) op_desc.SetInput(#Key, {node__##_n->Name()}); +#define SET_IN(Key, node__) op_desc.SetInput(#Key, {node__->Name()}); SET_IN(X, x); SET_IN(WeightX, weight_x); SET_IN(WeightH, weight_h); if (with_fc_bias) { - op_desc.SetInput("Bias", {NEW_NAME(bias) + bias_n->Name()}); + op_desc.SetInput("Bias", {NEW_NAME(bias) + bias->Name()}); } else { SET_IN(Bias, bias); } #undef SET_IN op_desc.SetInput("H0", {}); - op_desc.SetOutput("Hidden", {hidden_n->Name()}); - op_desc.SetAttr("is_reverse", gru_n->Op()->GetAttr("is_reverse")); + op_desc.SetOutput("Hidden", {hidden->Name()}); + op_desc.SetAttr("is_reverse", gru->Op()->GetAttr("is_reverse")); // TODO(TJ): This should be a option for infer op_desc.SetAttr("use_seq", true); @@ -82,14 +73,12 @@ static int BuildFusion(Graph* graph, const std::string& name_scope, PADDLE_ENFORCE(scope); if (with_fc_bias) { // Fusion GRU bias = fcbias + grubias - auto* fusion_bias_var = scope->Var(NEW_NAME(bias) + bias_n->Name()); + auto* fusion_bias_var = scope->Var(NEW_NAME(bias) + bias->Name()); auto* out_bias_tensor = fusion_bias_var->GetMutable(); PADDLE_ENFORCE(fusion_bias_var); - GET_NODE(fc_bias); - PADDLE_ENFORCE(fc_bias_n); - auto* gru_bias_var = scope->FindVar(bias_n->Name()); - auto* fc_bias_var = scope->FindVar(fc_bias_n->Name()); + auto* gru_bias_var = scope->FindVar(bias->Name()); + auto* fc_bias_var = scope->FindVar(fc_bias->Name()); PADDLE_ENFORCE(gru_bias_var); PADDLE_ENFORCE(fc_bias_var); const auto& gru_bias_tenosr = gru_bias_var->Get(); @@ -113,11 +102,11 @@ static int BuildFusion(Graph* graph, const std::string& name_scope, #undef NEW_NAME #undef NEW_IMTERMEDIATE_OUT - IR_NODE_LINK_TO(x_n, op); - IR_NODE_LINK_TO(weight_x_n, op); - IR_NODE_LINK_TO(weight_h_n, op); - IR_NODE_LINK_TO(bias_n, op); // actually should link to new bias if have - IR_NODE_LINK_TO(op, hidden_n); + IR_NODE_LINK_TO(x, op); + IR_NODE_LINK_TO(weight_x, op); + IR_NODE_LINK_TO(weight_h, op); + IR_NODE_LINK_TO(bias, op); // actually should link to new bias if have + IR_NODE_LINK_TO(op, hidden); // h0? return op; }; @@ -125,42 +114,35 @@ static int BuildFusion(Graph* graph, const std::string& name_scope, int fusion_count{0}; auto handler = [&](const GraphPatternDetector::subgraph_t& subgraph, Graph* g) { -#define GET_NODE(name__) \ - std::string name__##key = name_scope + "/" + #name__; \ - auto* name__##n = pattern->RetrieveNode(name__##key); \ - PADDLE_ENFORCE(name__##n); \ - PADDLE_ENFORCE(subgraph.count(name__##n)); \ - Node* name__##_n = subgraph.at(name__##n); \ - int name__ __attribute__((unused)) = name__##_n->id(); - - GET_NODE(x); - GET_NODE(w); // fc weight - GET_NODE(mul); - GET_NODE(fc_out); - GET_NODE(Weight); - GET_NODE(gru); - GET_NODE(Bias); - GET_NODE(Hidden); + auto* x_n = subgraph.at(x); + GET_IR_NODE_FROM_SUBGRAPH(w, w, fc_pattern); + GET_IR_NODE_FROM_SUBGRAPH(mul, mul, fc_pattern); + GET_IR_NODE_FROM_SUBGRAPH(fc_out, Out, fc_pattern); + GET_IR_NODE_FROM_SUBGRAPH(Weight, Weight, gru_pattern); + GET_IR_NODE_FROM_SUBGRAPH(gru, gru, gru_pattern); + GET_IR_NODE_FROM_SUBGRAPH(Bias, Bias, gru_pattern); + GET_IR_NODE_FROM_SUBGRAPH(Hidden, Hidden, gru_pattern); // nodes need be removed - GET_NODE(BatchGate); - GET_NODE(BatchResetHiddenPrev); - GET_NODE(BatchHidden); + GET_IR_NODE_FROM_SUBGRAPH(BatchGate, BatchGate, gru_pattern); + GET_IR_NODE_FROM_SUBGRAPH(BatchResetHiddenPrev, BatchGate, gru_pattern); + GET_IR_NODE_FROM_SUBGRAPH(BatchHidden, BatchGate, gru_pattern); if (with_fc_bias) { - GET_NODE(mul_out); - GET_NODE(fc_bias); - GET_NODE(elementwise_add); - gru_creater(gru, x, w, Weight, Bias, Hidden, fc_bias); + GET_IR_NODE_FROM_SUBGRAPH(mul_out, mul_out, fc_pattern); + GET_IR_NODE_FROM_SUBGRAPH(fc_bias, bias, fc_pattern); + GET_IR_NODE_FROM_SUBGRAPH(elementwise_add, elementwise_add, fc_pattern); + + gru_creater(gru, x_n, w, Weight, Bias, Hidden, fc_bias); // Remove unneeded nodes. std::unordered_set marked_nodes( - {mul_n, gru_n, elementwise_add_n, fc_bias_n, fc_out_n, mul_out_n, - BatchGate_n, BatchResetHiddenPrev_n, BatchHidden_n}); + {mul, gru, elementwise_add, fc_bias, fc_out, mul_out, BatchGate, + BatchResetHiddenPrev, BatchHidden}); GraphSafeRemoveNodes(graph, marked_nodes); } else { - gru_creater(gru, x, w, Weight, Bias, Hidden, -1); + gru_creater(gru, x_n, w, Weight, Bias, Hidden, nullptr); // Remove unneeded nodes. std::unordered_set marked_nodes( - {mul_n, gru_n, BatchGate_n, BatchResetHiddenPrev_n, BatchHidden_n}); + {mul, gru, BatchGate, BatchResetHiddenPrev, BatchHidden}); GraphSafeRemoveNodes(graph, marked_nodes); } #undef GET_NODE diff --git a/paddle/fluid/framework/ir/fc_lstm_fuse_pass.cc b/paddle/fluid/framework/ir/fc_lstm_fuse_pass.cc index 3e09613699..f7fda87357 100644 --- a/paddle/fluid/framework/ir/fc_lstm_fuse_pass.cc +++ b/paddle/fluid/framework/ir/fc_lstm_fuse_pass.cc @@ -20,45 +20,29 @@ namespace paddle { namespace framework { namespace ir { -static std::string GenNodeName(const std::string& prefix, - const std::string& name) { - return prefix + "/" + name; -} +int BuildFusion(Graph* graph, const std::string& name_scope, Scope* scope, + bool with_fc_bias) { + GraphPatternDetector gpd; + auto* pattern = gpd.mutable_pattern(); -static void BuildPattern(PDPattern* pattern, const std::string& name_scope, - bool with_fc_bias) { - PDNode* x = pattern->NewNode(name_scope, "x") + // Build pattern + PDNode* x = pattern->NewNode(patterns::PDNodeName(name_scope, "x")) ->assert_is_op_input("mul") ->assert_var_not_persistable(); - auto* fc_out = patterns::FC(pattern, name_scope, x, with_fc_bias); - fc_out->AsIntermediate(); // fc_out is a tmp var, will be removed after fuse. - patterns::LSTM(pattern, name_scope, fc_out); - // LOG(INFO) << "\n" << pattern->DotString(); -} - -static int BuildFusion(Graph* graph, const std::string& name_scope, - Scope* scope, bool with_fc_bias) { - GraphPatternDetector gpd; - auto* pattern = gpd.mutable_pattern(); + patterns::FC fc_pattern(pattern, name_scope); - BuildPattern(pattern, name_scope, with_fc_bias); + // fc_out is a tmp var, will be removed after fuse, so marked as intermediate. + auto* fc_out = fc_pattern(x, with_fc_bias)->AsIntermediate(); + patterns::LSTM lstm_pattern(pattern, name_scope); + lstm_pattern(fc_out); // Create New OpDesc - auto lstm_creator = [&](int lstm, int input, int weight_x, int weight_h, - int bias, int hidden, int cell, int xx, int fc_bias) { -#define GET_NODE(x) auto* x##_n = graph->RetriveNode(x); - GET_NODE(input); - GET_NODE(weight_x); - GET_NODE(weight_h); - GET_NODE(bias); - GET_NODE(hidden); - GET_NODE(cell); - GET_NODE(xx); - GET_NODE(lstm); - + auto lstm_creator = [&](Node* lstm, Node* input, Node* weight_x, + Node* weight_h, Node* bias, Node* hidden, Node* cell, + Node* xx, Node* fc_bias) { OpDesc op_desc; op_desc.SetType("fusion_lstm"); -#define SET_IN(Key, node__) op_desc.SetInput(#Key, {node__##_n->Name()}); +#define SET_IN(Key, node__) op_desc.SetInput(#Key, {node__->Name()}); SET_IN(X, input); SET_IN(WeightX, weight_x); SET_IN(WeightH, weight_h); @@ -71,13 +55,12 @@ static int BuildFusion(Graph* graph, const std::string& name_scope, auto* bias_var = scope->Var(new_bias_var); PADDLE_ENFORCE(bias_var); auto* bias_tensor = bias_var->GetMutable(); - auto* lstm_bias_var = scope->FindVar(bias_n->Name()); + auto* lstm_bias_var = scope->FindVar(bias->Name()); PADDLE_ENFORCE(lstm_bias_var); const auto& lstm_bias_tensor = lstm_bias_var->Get(); bias_tensor->Resize(lstm_bias_tensor.dims()); - GET_NODE(fc_bias); - auto* fc_bias_var = scope->FindVar(fc_bias_n->Name()); + auto* fc_bias_var = scope->FindVar(fc_bias->Name()); const auto& fc_bias_tensor = fc_bias_var->Get(); auto* data = bias_tensor->mutable_data(platform::CPUPlace()); @@ -88,31 +71,36 @@ static int BuildFusion(Graph* graph, const std::string& name_scope, } op_desc.SetInput("Bias", {new_bias_var}); } -#undef GET_NODE // Create temp variables. - scope->Var(name_scope + "/BatchedInput.new") - ->GetMutable(); - scope->Var(name_scope + "/BatchCellPreAct.new") - ->GetMutable(); - scope->Var(name_scope + "/BatchedGate.new") - ->GetMutable(); + const std::string BatchedInput = patterns::UniqueKey("BatchedInput"); + const std::string BatchedCellPreAct = + patterns::UniqueKey("BatchedCellPreAct"); + const std::string BatchedGate = patterns::UniqueKey("BatchedGate"); + + scope->Var(BatchedInput)->GetMutable(); + scope->Var(BatchedCellPreAct)->GetMutable(); + scope->Var(BatchedGate)->GetMutable(); op_desc.SetInput("H0", {}); op_desc.SetInput("C0", {}); - op_desc.SetOutput("Hidden", {hidden_n->Name()}); - op_desc.SetOutput("Cell", {cell_n->Name()}); - op_desc.SetOutput("XX", {xx_n->Name()}); - op_desc.SetOutput("BatchedGate", {name_scope + "/BatchedGate.new"}); - op_desc.SetOutput("BatchCellPreAct", {name_scope + "/BatchCellPreAct.new"}); - op_desc.SetOutput("BatchedInput", {name_scope + "/BatchedInput.new"}); - op_desc.SetAttr("is_reverse", lstm_n->Op()->GetAttr("is_reverse")); - op_desc.SetAttr("use_peepholes", lstm_n->Op()->GetAttr("use_peepholes")); + op_desc.SetOutput("Hidden", {hidden->Name()}); + op_desc.SetOutput("Cell", {cell->Name()}); + op_desc.SetOutput("XX", {xx->Name()}); + op_desc.SetOutput("BatchedGate", {BatchedGate}); + op_desc.SetOutput("BatchCellPreAct", {BatchedCellPreAct}); + op_desc.SetOutput("BatchedInput", {BatchedInput}); + op_desc.SetAttr("is_reverse", lstm->Op()->GetAttr("is_reverse")); + op_desc.SetAttr("use_peepholes", lstm->Op()->GetAttr("use_peepholes")); // TODO(TJ): get from attr op_desc.SetAttr("use_seq", true); -#define TMP_NAME(x) "at.new.tmp." #x -#define OP_SET_OUT(x) op_desc.SetOutput(#x, {TMP_NAME(x)}) + PADDLE_ENFORCE(graph->Has(kParamScopeAttr)); + auto* scope = graph->Get(kParamScopeAttr); +#define OP_SET_OUT(x) \ + const std::string x = patterns::UniqueKey(#x); \ + op_desc.SetOutput(#x, {x}); \ + scope->Var(x)->GetMutable() OP_SET_OUT(BatchedCell); OP_SET_OUT(BatchedHidden); OP_SET_OUT(ReorderedH0); @@ -120,22 +108,11 @@ static int BuildFusion(Graph* graph, const std::string& name_scope, #undef OP_SET_OUT auto* op = graph->CreateOpNode(&op_desc); - PADDLE_ENFORCE(graph->Has(kParamScopeAttr)); - auto* scope = graph->Get(kParamScopeAttr); - -#define TMP_NEW(x) scope->Var(TMP_NAME(x))->GetMutable() - TMP_NEW(BatchedCell); - TMP_NEW(BatchedHidden); - TMP_NEW(ReorderedH0); - TMP_NEW(ReorderedC0); -#undef TMP_NEW -#undef TMP_NAME - - IR_NODE_LINK_TO(input_n, op); - IR_NODE_LINK_TO(weight_x_n, op); - IR_NODE_LINK_TO(weight_h_n, op); - IR_NODE_LINK_TO(bias_n, op); - IR_NODE_LINK_TO(op, hidden_n); + IR_NODE_LINK_TO(input, op); + IR_NODE_LINK_TO(weight_x, op); + IR_NODE_LINK_TO(weight_h, op); + IR_NODE_LINK_TO(bias, op); + IR_NODE_LINK_TO(op, hidden); return op; }; @@ -143,39 +120,32 @@ static int BuildFusion(Graph* graph, const std::string& name_scope, auto handler = [&](const GraphPatternDetector::subgraph_t& subgraph, Graph* g) { -#define GET_NODE(name__) \ - std::string name__##key = name_scope + "/" + #name__; \ - auto* name__##n = pattern->RetrieveNode(name__##key); \ - PADDLE_ENFORCE(name__##n); \ - PADDLE_ENFORCE(subgraph.count(name__##n)); \ - Node* name__##_n = subgraph.at(name__##n); \ - int name__ __attribute__((unused)) = name__##_n->id(); - - GET_NODE(x); - GET_NODE(w); - GET_NODE(mul); - GET_NODE(fc_out); - GET_NODE(Weight); - GET_NODE(lstm); - GET_NODE(Bias); - GET_NODE(Hidden); - GET_NODE(Cell); + GET_IR_NODE_FROM_SUBGRAPH(lstm, lstm, lstm_pattern); + GET_IR_NODE_FROM_SUBGRAPH(Weight, Weight, lstm_pattern); + GET_IR_NODE_FROM_SUBGRAPH(Bias, Bias, lstm_pattern); + GET_IR_NODE_FROM_SUBGRAPH(Cell, Cell, lstm_pattern); + GET_IR_NODE_FROM_SUBGRAPH(Hidden, Hidden, lstm_pattern); + GET_IR_NODE_FROM_SUBGRAPH(w, w, fc_pattern); + GET_IR_NODE_FROM_SUBGRAPH(mul, mul, fc_pattern); if (with_fc_bias) { - GET_NODE(fc_bias); - GET_NODE(elementwise_add); - lstm_creator(lstm, x, w, Weight, Bias, Hidden, Cell, fc_out, fc_bias); + GET_IR_NODE_FROM_SUBGRAPH(fc_out, Out, fc_pattern); + GET_IR_NODE_FROM_SUBGRAPH(fc_bias, bias, fc_pattern); + GET_IR_NODE_FROM_SUBGRAPH(elementwise_add, elementwise_add, fc_pattern); + lstm_creator(lstm, subgraph.at(x), w, Weight, Bias, Hidden, Cell, fc_out, + fc_bias); // Remove unneeded nodes. std::unordered_set marked_nodes( - {mul_n, lstm_n, elementwise_add_n}); + {mul, lstm, elementwise_add}); GraphSafeRemoveNodes(graph, marked_nodes); } else { - lstm_creator(lstm, x, w, Weight, Bias, Hidden, Cell, fc_out, -1); + GET_IR_NODE_FROM_SUBGRAPH(fc_out, mul_out, fc_pattern); + lstm_creator(lstm, subgraph.at(x), w, Weight, Bias, Hidden, Cell, fc_out, + nullptr); // Remove unneeded nodes. - std::unordered_set marked_nodes({mul_n, lstm_n}); + std::unordered_set marked_nodes({mul, lstm}); GraphSafeRemoveNodes(graph, marked_nodes); } -#undef GET_NODE ++fusion_count; }; diff --git a/paddle/fluid/framework/ir/graph_pattern_detector.cc b/paddle/fluid/framework/ir/graph_pattern_detector.cc index 5ca7509515..fc7feca567 100644 --- a/paddle/fluid/framework/ir/graph_pattern_detector.cc +++ b/paddle/fluid/framework/ir/graph_pattern_detector.cc @@ -21,6 +21,7 @@ #include "paddle/fluid/framework/ir/graph_traits.h" #include "paddle/fluid/framework/ir/graph_viz_pass.h" #include "paddle/fluid/platform/enforce.h" +#include "paddle/fluid/string/printf.h" namespace paddle { namespace framework { @@ -106,8 +107,7 @@ bool GraphPatternDetector::MarkPDNodesInGraph(const ir::Graph& graph) { for (auto& pdnode : pattern_.nodes()) { if (!pdnodes2nodes_.count(pdnode.get())) { VLOG(4) << pdnode->name() << " can't find matched Node, early stop"; - - return false; + // return false; } } for (auto& item : pdnodes2nodes_) { @@ -517,87 +517,89 @@ bool VarLinksFromOp(Node* node, const std::string& op_type) { return false; } -PDNode* patterns::FC(PDPattern* pattern, const std::string& name_scope, - PDNode* x, bool with_bias) { - // mul op - auto* mul_op = pattern->NewNode(name_scope, "mul")->assert_is_op("mul"); - auto* mul_weight_var = pattern->NewNode(name_scope, "w") - ->AsInput() - ->assert_is_persistable_var() - ->assert_is_op_input("mul", "Y"); - - PDNode* fc_out{nullptr}; - if (with_bias) { - PDNode* elementwise_add_op{nullptr}; - PDNode *mul_out_var{nullptr}, *bias{nullptr}; - elementwise_add_op = pattern->NewNode(name_scope, "elementwise_add") - ->assert_is_op("elementwise_add"); - // intermediate variable, will be removed in the IR after fuse. - mul_out_var = pattern->NewNode(name_scope, "mul_out") - ->AsIntermediate() - ->assert_is_only_output_of_op("mul") - ->assert_is_op_input("elementwise_add"); - // bias - bias = pattern->NewNode(name_scope, "fc_bias") - ->AsInput() - ->assert_is_op_input("elementwise_add"); - // output - fc_out = pattern->NewNode(name_scope, "fc_out") - ->AsOutput() - ->assert_is_op_output("elementwise_add"); - mul_op->LinksFrom({x, mul_weight_var}).LinksTo({mul_out_var}); - elementwise_add_op->LinksFrom({mul_out_var, bias}).LinksTo({fc_out}); - } else { - fc_out = pattern->NewNode(name_scope, "fc_out") - ->AsOutput() - ->assert_is_op_output("mul"); - mul_op->LinksFrom({mul_weight_var, x}).LinksTo({fc_out}); +PDNode* patterns::FC::operator()(paddle::framework::ir::PDNode* x, + bool with_bias) { + // Create shared nodes. + x->assert_is_op_input("mul", "X"); + auto* mul = pattern->NewNode(mul_repr())->assert_is_op("mul"); + + auto* mul_w_var = pattern->NewNode(w_repr()) + ->AsInput() + ->assert_is_persistable_var() + ->assert_is_op_input("mul", "Y"); + + auto* mul_out_var = + pattern->NewNode(mul_out_repr())->assert_is_op_output("mul"); + + if (!with_bias) { // not with bias + // Add links. + mul->LinksFrom({x, mul_w_var}).LinksTo({mul_out_var}); + return mul_out_var; + + } else { // with bias + mul_out_var->AsIntermediate()->assert_is_op_input("elementwise_add"); + // Create operators. + auto* elementwise_add = pattern->NewNode(elementwise_add_repr()) + ->assert_is_op("elementwise_add"); + // Create variables. + auto* bias = pattern->NewNode(bias_repr()) + ->assert_is_op_input("elementwise_add") + ->AsInput(); + + auto* fc_out = pattern->NewNode(Out_repr()) + ->AsOutput() + ->assert_is_op_output("elementwise_add"); + + mul->LinksFrom({mul_w_var, x}).LinksTo({mul_out_var}); + elementwise_add->LinksFrom({mul_out_var, bias}).LinksTo({fc_out}); + return fc_out; } - return fc_out; } -#define NEW_NODE(op__, arg__, io__) \ - auto* arg__ = pattern->NewNode(name_scope, #arg__) \ - ->assert_is_op_##io__(#op__, #arg__); - -PDNode* patterns::LSTM(PDPattern* pattern, const std::string& name_scope, - PDNode* x) { +PDNode* patterns::LSTM::operator()(PDNode* x) { x->assert_is_op_input("lstm", "Input"); - auto* lstm_op = pattern->NewNode(name_scope, "lstm")->assert_is_op("lstm"); + auto* lstm_op = pattern->NewNode(lstm_repr())->assert_is_op("lstm"); +#define NEW_NODE(arg__, io__) \ + auto* arg__ = \ + pattern->NewNode(arg__##_repr())->assert_is_op_##io__("lstm", #arg__); // Currently, the H0 and C0 are optional // TODO(Superjomn) upgrade the fuse framework to support optional. // NEW_NODE(H0, input); // NEW_NODE(C0, input); - NEW_NODE(lstm, Weight, input); - NEW_NODE(lstm, Bias, input); + NEW_NODE(Weight, input); + NEW_NODE(Bias, input); - NEW_NODE(lstm, Hidden, output); - NEW_NODE(lstm, Cell, output); - NEW_NODE(lstm, BatchGate, output); - NEW_NODE(lstm, BatchCellPreAct, output); + NEW_NODE(Hidden, output); + NEW_NODE(Cell, output); + NEW_NODE(BatchGate, output); + NEW_NODE(BatchCellPreAct, output); +#undef NEW_NODE lstm_op->LinksFrom({x, Weight, Bias}); lstm_op->LinksTo({Hidden, Cell, BatchGate, BatchCellPreAct}); return Hidden; } -PDNode* patterns::GRU(PDPattern* pattern, const std::string& name_scope, - PDNode* x) { +PDNode* patterns::GRU::operator()(PDNode* x) { x->assert_is_op_input("gru", "Input"); - auto* gru_op = pattern->NewNode(name_scope, "gru")->assert_is_op("gru"); + auto* gru_op = pattern->NewNode(gru_repr())->assert_is_op("gru"); +#define NEW_NODE(arg__, io__) \ + auto* arg__ = \ + pattern->NewNode(arg__##_repr())->assert_is_op_##io__("gru", #arg__); - NEW_NODE(gru, Weight, input); + NEW_NODE(Weight, input); // TODO(Superjomn): upgrade the fuse framework to support optional. // H0 and bias are optional - NEW_NODE(gru, Bias, input); // also optional + NEW_NODE(Bias, input); // also optional // NEW_NODE(H0, input); - NEW_NODE(gru, Hidden, output); + NEW_NODE(Hidden, output); // below are intermediate - NEW_NODE(gru, BatchGate, output); - NEW_NODE(gru, BatchResetHiddenPrev, output); - NEW_NODE(gru, BatchHidden, output); + NEW_NODE(BatchGate, output); + NEW_NODE(BatchResetHiddenPrev, output); + NEW_NODE(BatchHidden, output); +#undef NEW_NODE BatchGate->AsIntermediate(); BatchResetHiddenPrev->AsIntermediate(); @@ -607,7 +609,6 @@ PDNode* patterns::GRU(PDPattern* pattern, const std::string& name_scope, gru_op->LinksTo({Hidden, BatchGate, BatchResetHiddenPrev, BatchHidden}); return Hidden; } -#undef NEW_NODE } // namespace ir } // namespace framework diff --git a/paddle/fluid/framework/ir/graph_pattern_detector.h b/paddle/fluid/framework/ir/graph_pattern_detector.h index 71e4c36d9b..57482a07b6 100644 --- a/paddle/fluid/framework/ir/graph_pattern_detector.h +++ b/paddle/fluid/framework/ir/graph_pattern_detector.h @@ -286,22 +286,148 @@ void GraphSafeRemoveNodes(Graph* graph, const std::unordered_set& nodes); // Some pre-defined patterns those can be reused in multiple passes. +// The related Fluid Layer or Op should be one pattern here for better reusage +// accross different fusion. namespace patterns { +struct KeyCounter { + static KeyCounter& Instance() { + static KeyCounter x; + return x; + } + + int IncCounter(const std::string& key) { return dic_[key]++; } + + private: + std::unordered_map dic_; +}; + +// Generate a unique PDNode's name with name_scope and id. +// The format is {name_scope}/{repr}/{id}/{name} +static std::string PDNodeName(const std::string& name_scope, + const std::string& repr, size_t id, + const std::string& name) { + return string::Sprintf("%s/%s/%d/%s", name_scope, repr, id, name); +} +// Generate a unique PDNode's name. +// The format is {name_scope}/{repr}/{id} +static std::string PDNodeName(const std::string& name_scope, + const std::string& repr) { + return string::Sprintf("%s/%s/%d", name_scope, repr, + KeyCounter::Instance().IncCounter(repr)); +} +// Generate a unique key. It can be used for a universally unique temporary +// name. +// The format is {repr}/{id} +static std::string UniqueKey(const std::string& repr) { + return string::Sprintf("%s/%d", repr, + KeyCounter::Instance().IncCounter(repr)); +} + +// Declare a PDNode in a pattern, will create two methods: +// std::string xxx_repr(); return this PDNode's string id. +// PDNode* xxx_n(); return the corresponding PDNode. +#define PATTERN_DECL_NODE(name__) \ + std::string name__##_repr() const { \ + return PDNodeName(name_scope_, repr_, id_, #name__); \ + } \ + PDNode* name__##_n() const { return pattern->RetrieveNode(name__##_repr()); } + +// Get an ir::Node* from the matched subgraph. +// var: variable. +// arg: the argument declared by PATTERN_DECL_NODE in a pattern definition. +// pat: the pattern object. +#define GET_IR_NODE_FROM_SUBGRAPH(var, arg, pat) \ + PADDLE_ENFORCE(subgraph.count(pat.arg##_n()), \ + "Node not found for PDNode %s", pat.arg##_repr()); \ + Node* var = subgraph.at(pat.arg##_n()); \ + PADDLE_ENFORCE(var, "node %s not exists in the sub-graph", #arg) + +// The base class of all the patterns. +struct PatternBase { + PatternBase(PDPattern* pattern, const std::string& name_scope, + const std::string& repr) + : pattern(pattern), + name_scope_(name_scope), + repr_(repr), + id_(KeyCounter::Instance().IncCounter(repr)) {} + + PDPattern* pattern; + + protected: + std::string name_scope_; + std::string repr_; + size_t id_; +}; + // FC with bias // op: mul + elementwise_add // named nodes: // mul, elementwise_add // w, mul_out, bias, fc_out -PDNode* FC(PDPattern* pattern, const std::string& name_scope, PDNode* x, - bool with_bias); +struct FC : public PatternBase { + FC(PDPattern* pattern, const std::string& name_scope) + : PatternBase(pattern, name_scope, "fc") {} + + PDNode* operator()(PDNode* x, bool with_bias); + + // declare operator node's name + PATTERN_DECL_NODE(fc); + PATTERN_DECL_NODE(mul); + PATTERN_DECL_NODE(elementwise_add); + // declare variable node's name + PATTERN_DECL_NODE(w); + PATTERN_DECL_NODE(mul_out); // (x,w) -> mul_out + PATTERN_DECL_NODE(bias); + PATTERN_DECL_NODE(Out); +}; + +struct LSTM : public PatternBase { + LSTM(PDPattern* pattern, const std::string& name_scope) + : PatternBase(pattern, name_scope, "lstm") {} -PDNode* LSTM(PDPattern* pattern, const std::string& name_scope, PDNode* x); + PDNode* operator()(PDNode* x); -PDNode* GRU(PDPattern* pattern, const std::string& name_scope, PDNode* x); + // Operators + PATTERN_DECL_NODE(lstm); + + // Inputs + PATTERN_DECL_NODE(Input); + PATTERN_DECL_NODE(H0); + PATTERN_DECL_NODE(C0); + PATTERN_DECL_NODE(Weight); + PATTERN_DECL_NODE(Bias); + + // Outputs + PATTERN_DECL_NODE(Hidden); + PATTERN_DECL_NODE(Cell); + PATTERN_DECL_NODE(BatchGate); + PATTERN_DECL_NODE(BatchCellPreAct); +}; + +struct GRU : public PatternBase { + GRU(PDPattern* pattern, const std::string& name_scope) + : PatternBase(pattern, name_scope, "lstm") {} + + PDNode* operator()(PDNode* x); + + // Operators + PATTERN_DECL_NODE(gru); + + // Inputs + PATTERN_DECL_NODE(Bias); + PATTERN_DECL_NODE(Weight); + + // Outputs + PATTERN_DECL_NODE(BatchGate); + PATTERN_DECL_NODE(BatchResetHiddenPrev); + PATTERN_DECL_NODE(BatchHidden); + PATTERN_DECL_NODE(Hidden); +}; } // namespace patterns +// Link two ir::Nodes from each other. #define IR_NODE_LINK_TO(a, b) \ a->outputs.push_back(b); \ b->inputs.push_back(a); diff --git a/paddle/fluid/framework/ir/seq_concat_fc_fuse_pass.cc b/paddle/fluid/framework/ir/seq_concat_fc_fuse_pass.cc index e1a441d09a..a7d5161c35 100644 --- a/paddle/fluid/framework/ir/seq_concat_fc_fuse_pass.cc +++ b/paddle/fluid/framework/ir/seq_concat_fc_fuse_pass.cc @@ -192,6 +192,8 @@ std::unique_ptr SeqConcatFcFusePass::ApplyImpl( auto* id = subgraph.at(pattern.RetrieveNode(#id)); \ PADDLE_ENFORCE_NOT_NULL(id, "subgraph has no node %s", #id); + int fuse_count{0}; + detector(graph.get(), [&](const GraphPatternDetector::subgraph_t& subgraph, Graph* graph) { VLOG(4) << "get one concat pattern"; @@ -239,8 +241,12 @@ std::unique_ptr SeqConcatFcFusePass::ApplyImpl( marked_nodes.erase(sequence_expand1_in); marked_nodes.erase(fc_out); GraphSafeRemoveNodes(graph, marked_nodes); + + ++fuse_count; }); + AddStatis(fuse_count); + return graph; } diff --git a/paddle/fluid/inference/analysis/analyzer_tester.cc b/paddle/fluid/inference/analysis/analyzer_tester.cc index a496ae41aa..dc1b03b2d1 100644 --- a/paddle/fluid/inference/analysis/analyzer_tester.cc +++ b/paddle/fluid/inference/analysis/analyzer_tester.cc @@ -267,6 +267,7 @@ void TestDituRNNPrediction(bool use_analysis, bool activate_ir, PADDLE_ENFORCE(config.ir_mode == AnalysisConfig::IrPassMode::kExclude); // default config.ir_passes.clear(); // Do not exclude any pass. + int batch_size = FLAGS_batch_size; int num_times = FLAGS_repeat; @@ -346,6 +347,7 @@ void TestDituRNNPrediction(bool use_analysis, bool activate_ir, ASSERT_TRUE(fuse_statis.count("fc_fuse")); EXPECT_EQ(fuse_statis.at("fc_fuse"), 1); EXPECT_EQ(fuse_statis.at("fc_nobias_lstm_fuse"), 2); // bi-directional LSTM + EXPECT_EQ(fuse_statis.at("seq_concat_fc_fuse"), 1); EXPECT_EQ(num_ops, 13); // After graph optimization, only 13 operators exists. } From 5023530a8a21bbbcd6705fbd5fafafd950fe2617 Mon Sep 17 00:00:00 2001 From: Yan Chunwei Date: Mon, 10 Sep 2018 14:07:45 +0800 Subject: [PATCH 44/44] Refactor/remove sensitive (#13314) --- .../fluid/inference/analysis/CMakeLists.txt | 16 ++++---- .../inference/analysis/analyzer_tester.cc | 40 ++++++------------- 2 files changed, 21 insertions(+), 35 deletions(-) diff --git a/paddle/fluid/inference/analysis/CMakeLists.txt b/paddle/fluid/inference/analysis/CMakeLists.txt index a115bc8f4a..11a7509feb 100644 --- a/paddle/fluid/inference/analysis/CMakeLists.txt +++ b/paddle/fluid/inference/analysis/CMakeLists.txt @@ -48,18 +48,18 @@ function (inference_download_and_uncompress install_dir url gz_filename) message(STATUS "finish downloading ${gz_filename}") endfunction(inference_download_and_uncompress) -set(DITU_RNN_MODEL_URL "http://paddle-inference-dist.bj.bcebos.com/ditu_rnn_fluid%2Fmodel.tar.gz") -set(DITU_RNN_DATA_URL "http://paddle-inference-dist.bj.bcebos.com/ditu_rnn_fluid%2Fdata.txt.tar.gz") -set(DITU_INSTALL_DIR "${THIRD_PARTY_PATH}/inference_demo/ditu_rnn" CACHE PATH "Ditu RNN model and data root." FORCE) -if (NOT EXISTS ${DITU_INSTALL_DIR} AND WITH_TESTING) - inference_download_and_uncompress(${DITU_INSTALL_DIR} ${DITU_RNN_MODEL_URL} "ditu_rnn_fluid%2Fmodel.tar.gz") - inference_download_and_uncompress(${DITU_INSTALL_DIR} ${DITU_RNN_DATA_URL} "ditu_rnn_fluid%2Fdata.txt.tar.gz") +set(RNN1_MODEL_URL "http://paddle-inference-dist.bj.bcebos.com/rnn1%2Fmodel.tar.gz") +set(RNN1_DATA_URL "http://paddle-inference-dist.bj.bcebos.com/rnn1%2Fdata.txt.tar.gz") +set(RNN1_INSTALL_DIR "${THIRD_PARTY_PATH}/inference_demo/rnn1" CACHE PATH "RNN1 model and data root." FORCE) +if (NOT EXISTS ${RNN1_INSTALL_DIR} AND WITH_TESTING) + inference_download_and_uncompress(${RNN1_INSTALL_DIR} ${RNN1_MODEL_URL} "rnn1%2Fmodel.tar.gz") + inference_download_and_uncompress(${RNN1_INSTALL_DIR} ${RNN1_DATA_URL} "rnn1%2Fdata.txt.tar.gz") endif() inference_analysis_test(test_analyzer SRCS analyzer_tester.cc EXTRA_DEPS paddle_inference_api paddle_fluid_api ir_pass_manager analysis_predictor - ARGS --infer_ditu_rnn_model=${DITU_INSTALL_DIR}/model - --infer_ditu_rnn_data=${DITU_INSTALL_DIR}/data.txt) + ARGS --infer_model=${RNN1_INSTALL_DIR}/model + --infer_data=${RNN1_INSTALL_DIR}/data.txt) inference_analysis_test(test_data_flow_graph SRCS data_flow_graph_tester.cc) inference_analysis_test(test_data_flow_graph_to_fluid_pass SRCS data_flow_graph_to_fluid_pass_tester.cc) diff --git a/paddle/fluid/inference/analysis/analyzer_tester.cc b/paddle/fluid/inference/analysis/analyzer_tester.cc index dc1b03b2d1..cc4b390495 100644 --- a/paddle/fluid/inference/analysis/analyzer_tester.cc +++ b/paddle/fluid/inference/analysis/analyzer_tester.cc @@ -26,8 +26,8 @@ #include "paddle/fluid/inference/api/paddle_inference_pass.h" #include "paddle/fluid/inference/utils/singleton.h" -DEFINE_string(infer_ditu_rnn_model, "", "model path for ditu RNN"); -DEFINE_string(infer_ditu_rnn_data, "", "data path for ditu RNN"); +DEFINE_string(infer_model, "", "model path"); +DEFINE_string(infer_data, "", "data path"); DEFINE_int32(batch_size, 10, "batch size."); DEFINE_int32(repeat, 1, "Running the inference program repeat times."); DEFINE_int32(num_threads, 1, "Running the inference program in multi-threads."); @@ -223,17 +223,6 @@ void PrepareInputs(std::vector *input_slots, DataRecord *data, } // namespace -const float ditu_rnn_target_data[] = { - 104.711, 11.2431, 1.35422, 0, 0, 0, 0, 0, - 27.7039, 1.41486, 7.09526, 0, 0, 0, 0, 0, - 7.6481, 6.5324, 56.383, 2.88018, 8.92918, 132.007, 4.27429, 2.02934, - 14.1727, 10.7461, 25.0616, 16.0197, 14.4163, 16.9199, 6.75517, 0, - 80.0249, 4.77739, 0, 0, 0, 0, 0, 0, - 47.5643, 2.67029, 8.76252, 0, 0, 0, 0, 0, - 51.8822, 4.4411, 0, 0, 0, 0, 0, 0, - 10.7286, 12.0595, 10.6672, 0, 0, 0, 0, 0, - 93.5771, 3.84641, 0, 0, 0, 0, 0, 0, - 169.426, 0, 0, 0, 0, 0, 0, 0}; void CompareResult(const std::vector &outputs, const std::vector &base_outputs) { PADDLE_ENFORCE_GT(outputs.size(), 0); @@ -255,11 +244,10 @@ void CompareResult(const std::vector &outputs, } } // Test with a really complicate model. -void TestDituRNNPrediction(bool use_analysis, bool activate_ir, - int num_threads) { +void TestRNN1Prediction(bool use_analysis, bool activate_ir, int num_threads) { AnalysisConfig config; - config.prog_file = FLAGS_infer_ditu_rnn_model + "/__model__"; - config.param_file = FLAGS_infer_ditu_rnn_model + "/param"; + config.prog_file = FLAGS_infer_model + "/__model__"; + config.param_file = FLAGS_infer_model + "/param"; config.use_gpu = false; config.device = 0; config.specify_input_name = true; @@ -277,7 +265,7 @@ void TestDituRNNPrediction(bool use_analysis, bool activate_ir, CreatePaddlePredictor( config); std::vector input_slots; - DataRecord data(FLAGS_infer_ditu_rnn_data, batch_size); + DataRecord data(FLAGS_infer_data, batch_size); // Prepare inputs. PrepareInputs(&input_slots, &data, batch_size); std::vector outputs, base_outputs; @@ -307,7 +295,7 @@ void TestDituRNNPrediction(bool use_analysis, bool activate_ir, threads.emplace_back([&, tid]() { // Each thread should have local input_slots and outputs. std::vector input_slots; - DataRecord data(FLAGS_infer_ditu_rnn_data, batch_size); + DataRecord data(FLAGS_infer_data, batch_size); PrepareInputs(&input_slots, &data, batch_size); std::vector outputs; Timer timer; @@ -354,24 +342,22 @@ void TestDituRNNPrediction(bool use_analysis, bool activate_ir, } // Inference with analysis and IR, easy for profiling independently. -TEST(Analyzer, DituRNN) { - TestDituRNNPrediction(true, true, FLAGS_num_threads); -} +TEST(Analyzer, rnn1) { TestRNN1Prediction(true, true, FLAGS_num_threads); } -// Other unit-tests of DituRNN, test different options of use_analysis, +// Other unit-tests of RNN1, test different options of use_analysis, // activate_ir and multi-threads. -TEST(Analyzer, DituRNN_tests) { +TEST(Analyzer, RNN_tests) { int num_threads[2] = {1, 4}; for (auto i : num_threads) { // Directly infer with the original model. - TestDituRNNPrediction(false, false, i); + TestRNN1Prediction(false, false, i); // Inference with the original model with the analysis turned on, the // analysis // module will transform the program to a data flow graph. - TestDituRNNPrediction(true, false, i); + TestRNN1Prediction(true, false, i); // Inference with analysis and IR. The IR module will fuse some large // kernels. - TestDituRNNPrediction(true, true, i); + TestRNN1Prediction(true, true, i); } }