diff --git a/paddle/fluid/framework/operator.cc b/paddle/fluid/framework/operator.cc index d58d6e4f3e684b97fcc1121e51355bdf3aae3fce..36025db7ba873a150077c94504df91b23efb9563 100644 --- a/paddle/fluid/framework/operator.cc +++ b/paddle/fluid/framework/operator.cc @@ -21,6 +21,7 @@ limitations under the License. */ #include "paddle/fluid/framework/lod_tensor.h" #include "paddle/fluid/framework/operator.h" #include "paddle/fluid/framework/shape_inference.h" +#include "paddle/fluid/framework/shape_runtime_infer.h" #include "paddle/fluid/framework/var_type.h" #include "paddle/fluid/platform/profiler.h" @@ -458,187 +459,147 @@ bool OpSupportGPU(const std::string& op_type) { return false; } -class RuntimeInferShapeContext : public InferShapeContext { - public: - RuntimeInferShapeContext(const OperatorBase& op, const Scope& scope) - : op_(op), scope_(scope) {} - - bool HasInput(const std::string& name) const override { - if (!op_.HasInputs(name)) { - return false; - } - auto& ins = Inputs(name); - size_t length = ins.size(); - if (length == 0) { - return false; - } - PADDLE_ENFORCE_EQ(length, 1UL, - "Input %s should not have more than one inputs", name); - auto ipt = ins[0]; - auto* var = ipt == kEmptyVarName ? nullptr : scope_.FindVar(ipt); - return var != nullptr; +bool RuntimeInferShapeContext::HasInput(const std::string& name) const { + if (!op_.HasInputs(name)) { + return false; } - - bool HasOutput(const std::string& name) const override { - if (!op_.HasOutputs(name)) { - return false; - } - auto& outs = Outputs(name); - size_t length = outs.size(); - if (length == 0) { - return false; - } - PADDLE_ENFORCE_EQ(length, 1UL, - "Output %s should not have more than one inputs", name); - auto ipt = outs[0]; - auto* var = ipt == kEmptyVarName ? nullptr : scope_.FindVar(ipt); - return var != nullptr; + auto& ins = Inputs(name); + size_t length = ins.size(); + if (length == 0) { + return false; } + PADDLE_ENFORCE_EQ(length, 1UL, + "Input %s should not have more than one inputs", name); + auto ipt = ins[0]; + auto* var = ipt == kEmptyVarName ? nullptr : scope_.FindVar(ipt); + return var != nullptr; +} - bool HasInputs(const std::string& name) const override { - if (!op_.HasInputs(name)) { - return false; - } - auto inputs = op_.Inputs(name); - if (inputs.empty()) { - return false; - } - for (auto& input : inputs) { - if (scope_.FindVar(input) == nullptr) { - return false; - } - } - return true; +bool RuntimeInferShapeContext::HasOutput(const std::string& name) const { + if (!op_.HasOutputs(name)) { + return false; } + auto& outs = Outputs(name); + size_t length = outs.size(); + if (length == 0) { + return false; + } + PADDLE_ENFORCE_EQ(length, 1UL, + "Output %s should not have more than one inputs", name); + auto ipt = outs[0]; + auto* var = ipt == kEmptyVarName ? nullptr : scope_.FindVar(ipt); + return var != nullptr; +} - bool HasOutputs(const std::string& name) const override { - if (!op_.HasOutputs(name)) { - return false; - } - auto outputs = op_.Outputs(name); - if (outputs.empty()) { +bool RuntimeInferShapeContext::HasInputs(const std::string& name) const { + if (!op_.HasInputs(name)) { + return false; + } + auto inputs = op_.Inputs(name); + if (inputs.empty()) { + return false; + } + for (auto& input : inputs) { + if (scope_.FindVar(input) == nullptr) { return false; } - for (auto& output : outputs) { - if (scope_.FindVar(output) == nullptr) { - return false; - } - } - return true; } + return true; +} - AttrReader Attrs() const override { return AttrReader(op_.Attrs()); } - - const std::vector& Inputs( - const std::string& name) const override { - return op_.Inputs(name); +bool RuntimeInferShapeContext::HasOutputs(const std::string& name) const { + if (!op_.HasOutputs(name)) { + return false; } - - const std::vector& Outputs( - const std::string& name) const override { - return op_.Outputs(name); + auto outputs = op_.Outputs(name); + if (outputs.empty()) { + return false; } + for (auto& output : outputs) { + if (scope_.FindVar(output) == nullptr) { + return false; + } + } + return true; +} - void ShareLoD(const std::string& in, const std::string& out, size_t i = 0, - size_t j = 0) const override { - PADDLE_ENFORCE_LT(i, Inputs(in).size()); - PADDLE_ENFORCE_LT(j, Outputs(out).size()); - Variable* in_var = scope_.FindVar(Inputs(in)[i]); - Variable* out_var = scope_.FindVar(Outputs(out)[j]); - if (!in_var->IsType()) return; - PADDLE_ENFORCE(out_var->IsType(), - "The %d-th output of Output(%s) must be LoDTensor.", j, out); - auto in_tensor = in_var->Get(); - auto* out_tensor = out_var->GetMutable(); - out_tensor->set_lod(in_tensor.lod()); +void RuntimeInferShapeContext::ShareLoD(const std::string& in, + const std::string& out, size_t i, + size_t j) const { + PADDLE_ENFORCE_LT(i, Inputs(in).size()); + PADDLE_ENFORCE_LT(j, Outputs(out).size()); + Variable* in_var = scope_.FindVar(Inputs(in)[i]); + Variable* out_var = scope_.FindVar(Outputs(out)[j]); + if (!in_var->IsType()) return; + PADDLE_ENFORCE(out_var->IsType(), + "The %d-th output of Output(%s) must be LoDTensor.", j, out); + auto in_tensor = in_var->Get(); + auto* out_tensor = out_var->GetMutable(); + out_tensor->set_lod(in_tensor.lod()); // TODO(dzhwinter) : reuse ShareLoD in most operators. // Need to call ShareLayout explicitly in sequence related ops. // Shall we have a better method to shared info between in/out Tensor? #ifdef PADDLE_WITH_MKLDNN - // Fix me: ugly workaround below - // Correct solution: - // set_layout() should NOT be called here (i.e. ShareLoD). Instead, - // layout of output tensor should be set "manually" in Compute() - // of each OPKernel. The reason layout should NOT be shared between - // input and output "automatically" (now by InferShape()->ShareLoD()) - // is that layout transform may occur after InferShape(). - // Workaround: - // Skip set_layout() when input layout is kMKLDNN - // This is to avoid kMKLDNN is populated wrongly into a non-MKLDNN - // OPKernel. In all MKLDNN OPkernel, set_layout(kMKLDNN) should be called - // in Compute() - if (in_tensor.layout() != DataLayout::kMKLDNN) + // Fix me: ugly workaround below + // Correct solution: + // set_layout() should NOT be called here (i.e. ShareLoD). Instead, + // layout of output tensor should be set "manually" in Compute() + // of each OPKernel. The reason layout should NOT be shared between + // input and output "automatically" (now by InferShape()->ShareLoD()) + // is that layout transform may occur after InferShape(). + // Workaround: + // Skip set_layout() when input layout is kMKLDNN + // This is to avoid kMKLDNN is populated wrongly into a non-MKLDNN + // OPKernel. In all MKLDNN OPkernel, set_layout(kMKLDNN) should be called + // in Compute() + if (in_tensor.layout() != DataLayout::kMKLDNN) #endif - out_tensor->set_layout(in_tensor.layout()); - } - - void ShareLayout(const std::string& in, const std::string& out, size_t i = 0, - size_t j = 0) const { - PADDLE_ENFORCE_LT(i, Inputs(in).size()); - PADDLE_ENFORCE_LT(j, Outputs(out).size()); - Variable* in_var = scope_.FindVar(Inputs(in)[i]); - Variable* out_var = scope_.FindVar(Outputs(out)[j]); - if (!in_var->IsType()) return; - PADDLE_ENFORCE(out_var->IsType(), - "The %d-th output of Output(%s) must be LoDTensor.", j, out); - auto in_tensor = in_var->Get(); - auto* out_tensor = out_var->GetMutable(); out_tensor->set_layout(in_tensor.layout()); - } - - bool IsRuntime() const override { return true; } - - protected: - DDim GetDim(const std::string& name) const override { - Variable* var = scope_.FindVar(name); - PADDLE_ENFORCE_NOT_NULL(var); - if (var->IsType()) { - return var->Get().dims(); - } else if (var->IsType()) { - return var->Get().GetCompleteDims(); - } else { - PADDLE_THROW( - "Only LoDTensor/SelectedRows support 'GetDim', but Variable %s's " - "type_id is %s.", - name, var->Type().name()); - } - } - - std::vector GetRepeatedDims(const std::string& name) const override { - PADDLE_THROW("Only compile time support this method"); - } - - void SetDim(const std::string& name, const DDim& dim) override { - Variable* var = scope_.FindVar(name); - if (var->IsType()) { - var->GetMutable()->Resize(dim); - } else if (var->IsType()) { - var->GetMutable()->set_height(dim[0]); - } else { - PADDLE_THROW("Variable %s type_id %s, expect LoDTensor/SelectedRows.", - name, var->Type().name()); - } - } - - void SetRepeatedDims(const std::string& name, - const std::vector& dims) override { - PADDLE_THROW("Only compile time support this method"); - } +} - proto::VarType::Type GetVarType(const std::string& name) const override { - auto* var = scope_.FindVar(name); - return ToVarType(var->Type()); +void RuntimeInferShapeContext::ShareLayout(const std::string& in, + const std::string& out, size_t i, + size_t j) const { + PADDLE_ENFORCE_LT(i, Inputs(in).size()); + PADDLE_ENFORCE_LT(j, Outputs(out).size()); + Variable* in_var = scope_.FindVar(Inputs(in)[i]); + Variable* out_var = scope_.FindVar(Outputs(out)[j]); + if (!in_var->IsType()) return; + PADDLE_ENFORCE(out_var->IsType(), + "The %d-th output of Output(%s) must be LoDTensor.", j, out); + auto in_tensor = in_var->Get(); + auto* out_tensor = out_var->GetMutable(); + out_tensor->set_layout(in_tensor.layout()); +} + +DDim RuntimeInferShapeContext::GetDim(const std::string& name) const { + Variable* var = scope_.FindVar(name); + PADDLE_ENFORCE_NOT_NULL(var); + if (var->IsType()) { + return var->Get().dims(); + } else if (var->IsType()) { + return var->Get().GetCompleteDims(); + } else { + PADDLE_THROW( + "Only LoDTensor/SelectedRows support 'GetDim', but Variable %s's " + "type_id is %s.", + name, var->Type().name()); } +} - InferShapeVarPtr GetVarPtr(const std::string& name) override { - return scope_.FindVar(name); +void RuntimeInferShapeContext::SetDim(const std::string& name, + const DDim& dim) { + Variable* var = scope_.FindVar(name); + if (var->IsType()) { + var->GetMutable()->Resize(dim); + } else if (var->IsType()) { + var->GetMutable()->set_height(dim[0]); + } else { + PADDLE_THROW("Variable %s type_id %s, expect LoDTensor/SelectedRows.", name, + var->Type().name()); } - - private: - const OperatorBase& op_; - const Scope& scope_; -}; +} static void CheckTensorNANOrInf(const std::string& name, const framework::Tensor& tensor) { diff --git a/paddle/fluid/framework/shape_runtime_infer.h b/paddle/fluid/framework/shape_runtime_infer.h new file mode 100644 index 0000000000000000000000000000000000000000..04d4e33f7abf44c1bf91f215868b7cf64b6df482 --- /dev/null +++ b/paddle/fluid/framework/shape_runtime_infer.h @@ -0,0 +1,86 @@ +/* Copyright (c) 2016 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 +#include "paddle/fluid/framework/operator.h" +#include "paddle/fluid/framework/shape_inference.h" +#include "paddle/fluid/framework/var_type.h" + +namespace paddle { +namespace framework { + +class RuntimeInferShapeContext : public InferShapeContext { + public: + RuntimeInferShapeContext(const OperatorBase& op, const Scope& scope) + : op_(op), scope_(scope) {} + + bool HasInput(const std::string& name) const override; + bool HasOutput(const std::string& name) const override; + bool HasInputs(const std::string& name) const override; + bool HasOutputs(const std::string& name) const override; + + const OperatorBase& OpBase() const { return op_; } + + const Scope& InferScope() const { return scope_; } + AttrReader Attrs() const override { return AttrReader(op_.Attrs()); } + + const std::vector& Inputs( + const std::string& name) const override { + return op_.Inputs(name); + } + + const std::vector& Outputs( + const std::string& name) const override { + return op_.Outputs(name); + } + + void ShareLoD(const std::string& in, const std::string& out, size_t i = 0, + size_t j = 0) const override; + + void ShareLayout(const std::string& in, const std::string& out, size_t i = 0, + size_t j = 0) const; + + bool IsRuntime() const override { return true; } + + protected: + DDim GetDim(const std::string& name) const override; + void SetDim(const std::string& name, const DDim& dim) override; + + std::vector GetRepeatedDims(const std::string& name) const override { + PADDLE_THROW("Only compile time support this method"); + } + void SetRepeatedDims(const std::string& name, + const std::vector& dims) override { + PADDLE_THROW("Only compile time support this method"); + } + + proto::VarType::Type GetVarType(const std::string& name) const override { + auto* var = scope_.FindVar(name); + return ToVarType(var->Type()); + } + + InferShapeVarPtr GetVarPtr(const std::string& name) override { + return scope_.FindVar(name); + } + + private: + const OperatorBase& op_; + const Scope& scope_; +}; + +} // namespace framework +} // namespace paddle diff --git a/paddle/fluid/operators/fusion_lstm_op.cc b/paddle/fluid/operators/fusion_lstm_op.cc index ef23ab3f981786d33567619ad0194d21f31bdc8e..ae9d5d78ae6e3a8e8c50d5445aa4fca1428ab938 100644 --- a/paddle/fluid/operators/fusion_lstm_op.cc +++ b/paddle/fluid/operators/fusion_lstm_op.cc @@ -14,6 +14,7 @@ limitations under the License. */ #include "paddle/fluid/operators/fusion_lstm_op.h" #include +#include "paddle/fluid/framework/shape_runtime_infer.h" #include "paddle/fluid/operators/math/blas.h" #include "paddle/fluid/operators/math/cpu_vec.h" #include "paddle/fluid/operators/math/fc_compute.h" @@ -24,26 +25,54 @@ namespace paddle { namespace operators { void FusionLSTMOp::InferShape(framework::InferShapeContext* ctx) const { - PADDLE_ENFORCE(ctx->HasInput("X"), "Input(X) of LSTM should not be null."); - PADDLE_ENFORCE(ctx->HasInput("WeightX"), - "Input(WeightX) of LSTM should not be null."); - PADDLE_ENFORCE(ctx->HasInput("WeightH"), - "Input(WeightH) of LSTM should not be null."); - PADDLE_ENFORCE(ctx->HasInput("Bias"), - "Input(Bias) of LSTM should not be null."); - - PADDLE_ENFORCE(ctx->HasOutput("XX"), - "Output(XX) of LSTM should not be null."); - PADDLE_ENFORCE(ctx->HasOutput("Hidden"), - "Output(Hidden) of LSTM should not be null."); - PADDLE_ENFORCE(ctx->HasOutput("Cell"), - "Output(Cell) of LSTM should not be null."); + auto* runtime_ctx = dynamic_cast(ctx); + if (runtime_ctx == nullptr) { + LOG(FATAL) << "Should have runtime infer context"; + } + const auto& ins = runtime_ctx->OpBase().Inputs(); + const auto& outs = runtime_ctx->OpBase().Outputs(); + const auto& scope = runtime_ctx->InferScope(); + const auto ins_end = ins.end(); + const auto outs_end = outs.end(); + auto fair_input = [&](const std::string& name) -> bool { + auto it = ins.find(name); + if (it == ins_end) { + return false; + } + const auto& in = it->second; + if (in.size() != 1 || in[0] == framework::kEmptyVarName) { + return false; + } + return scope.FindVar(in[0]) != nullptr; + }; + auto fair_output = [&](const std::string& name) -> bool { + auto it = outs.find(name); + if (it == outs_end) { + return false; + } + const auto& out = it->second; + if (out.size() != 1 || out[0] == framework::kEmptyVarName) { + return false; + } + return scope.FindVar(out[0]) != nullptr; + }; + + PADDLE_ENFORCE(fair_input("X"), "Assert only one Input(X) of LSTM."); + PADDLE_ENFORCE(fair_input("WeightX"), + "Assert only one Input(WeightX) of LSTM."); + PADDLE_ENFORCE(fair_input("WeightH"), + "Assert only one Input(WeightH) of LSTM."); + PADDLE_ENFORCE(fair_input("Bias"), "Assert only one Input(Bias) of LSTM."); + PADDLE_ENFORCE(fair_output("XX"), "Assert only one Output(XX) of LSTM."); + PADDLE_ENFORCE(fair_output("Hidden"), + "Assert only one Output(Hidden) of LSTM."); + PADDLE_ENFORCE(fair_output("Cell"), "Assert only one Output(Cell) of LSTM."); auto x_dims = ctx->GetInputDim("X"); PADDLE_ENFORCE_EQ(x_dims.size(), 2, "Input(X)'s rank must be 2."); - if (ctx->HasInput("H0")) { - PADDLE_ENFORCE(ctx->HasInput("C0"), + if (fair_input("H0")) { + PADDLE_ENFORCE(fair_input("C0"), "Input(Cell) and Input(Hidden) of LSTM should not " "be null at the same time."); auto h_dims = ctx->GetInputDim("H0"); @@ -95,16 +124,16 @@ void FusionLSTMOp::InferShape(framework::InferShapeContext* ctx) const { 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."); + PADDLE_ENFORCE(fair_output("BatchedInput"), + "Assert only one Output(BatchedInput) of LSTM."); + PADDLE_ENFORCE(fair_output("BatchedHidden"), + "Assert only one Output(BatchedHidden) of LSTM."); + PADDLE_ENFORCE(fair_output("BatchedCell"), + "Assert only one Output(BatchedCell) of LSTM."); + PADDLE_ENFORCE(fair_output("ReorderedH0"), + "Assert only one Output(ReorderedH0) of LSTM"); + PADDLE_ENFORCE(fair_output("ReorderedC0"), + "Assert only one Output(ReorderedC0) of LSTM."); ctx->SetOutputDim("BatchedInput", {x_dims[0], wx_dims[1]}); ctx->SetOutputDim("BatchedHidden", out_dims); ctx->SetOutputDim("BatchedCell", out_dims);