Update function body initialization for ONNX functions (#4332)

* Update function body initialization

* minor fix

* changes per review comments

* minor fix

* format fix

* add function initialization in mixed precision transformer

* more updates

* more fixes
This commit is contained in:
Ashwini Khade 2020-06-30 14:30:59 -07:00 committed by GitHub
parent 37b624b688
commit 0404763f23
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 68 additions and 27 deletions

View file

@ -101,7 +101,21 @@ class Node {
/** Gets the Node's Node::Type. */
Node::Type NodeType() const noexcept;
/** Gets the function body if the #NodeType is fused, or nullptr if not. */
/**
Gets the function body if applicable otherwise nullptr
@param try_init_func_body If not already intialized, initialize the function body
(only applicable to operators which are defined as function in ONNX spec).
Function body can be initialized in 2 cases :
1. For nodes of type "Fused"
2. For nodes which are defined as functions in ONNX spec (example: DynamicQuantizeLinear)
For all other cases this will always return nullptr.
Nodes of type "Fused" are created during partitioning and the function body
initialization for such nodes also happens during node creation. Therefore,
initialization of function body will happen via this method only in case 2 mentioned above.
*/
const Function* GetFunctionBody(bool try_int_func_body = true) noexcept;
/** Gets the function body if applicable otherwise nullptr. */
const Function* GetFunctionBody() const noexcept;
/** Gets the node description. */
@ -779,7 +793,10 @@ class Graph {
@param node Node with Node::Type of Node::Type::Fused
@returns Status indicating success or providing an error message.
*/
Status InlineFunction(Node& node);
Status InlineFunction(Node& node);
/** Initialize function body for the given node */
void InitFunctionBodyForNode(Node& node);
/** Mark a NodeArg name as coming from the outer scope when programmatically constructing a Graph that will
be used as a GraphProto attribute in another Node..

View file

@ -197,12 +197,13 @@ Status GraphPartitioner::Partition(Graph& graph, bool export_dll, FuncManager& f
if (nullptr == node_func) {
continue;
}
nodes_need_inline.push_back(&node);
nodes_need_inline.push_back(&node);
}
}
}
for (auto* node : nodes_need_inline) {
// If the node has a functionbody with no kernel and cannot be inlined
// it is a invalid function
// it is an invalid function
ORT_RETURN_IF_ERROR(graph.InlineFunction(*node));
}

View file

@ -178,8 +178,8 @@ bool NodeArg::HasTensorOrScalarShape() const {
const auto type_case = type->value_case();
switch (type_case) {
case TypeProto::kTensorType:
case TypeProto::kSparseTensorType:
// Standard tensor has a valid shape field while
case TypeProto::kSparseTensorType:
// Standard tensor has a valid shape field while
// scalar's shape is empty. Thus, we don't need to
// check shape here.
return true;
@ -433,6 +433,19 @@ void Node::SetNodeType(Node::Type node_type) noexcept {
node_type_ = node_type;
}
const Function* Node::GetFunctionBody(bool try_init_func_body) noexcept {
if (nullptr != func_body_) {
return func_body_;
}
// Initialize function body
if (try_init_func_body) {
graph_->InitFunctionBodyForNode(*this);
}
return func_body_;
}
const Function* Node::GetFunctionBody() const noexcept {
return func_body_;
}
@ -1314,7 +1327,6 @@ void Graph::ReverseDFSFrom(const std::vector<const Node*>& from,
const std::function<void(const Node*)>& enter,
const std::function<void(const Node*)>& leave,
const std::function<bool(const Node*, const Node*)>& comp) const {
ReverseDFSFrom(from, enter, leave, comp, {});
}
@ -2037,22 +2049,6 @@ Status Graph::VerifyNodeAndOpMatch(const ResolveOptions& options) {
node.op_ = nullptr;
}
if (node.op_ && (node.op_->HasFunction() || node.op_->HasContextDependentFunction())) {
onnx::FunctionProto onnx_function_proto;
onnx::FunctionBodyBuildContextImpl function_body_ctx(node_proto);
if (node.op_->HasContextDependentFunction()) {
node.op_->BuildContextDependentFunction(function_body_ctx, onnx_function_proto);
} else {
onnx_function_proto = *(node.op_->GetFunction());
}
auto func_ptr = onnxruntime::make_unique<onnxruntime::FunctionImpl>(*this, node.Index(), onnx_function_proto,
logger_);
function_container_.emplace_back(std::move(func_ptr));
node.SetFunctionBody(*function_container_.back());
}
if (!node.op_) {
return Status(ONNXRUNTIME, FAIL, "Fatal error: " + node.OpType() + " is not a registered function/op");
}
@ -2100,6 +2096,26 @@ Status Graph::VerifyNodeAndOpMatch(const ResolveOptions& options) {
return Status::OK();
}
void Graph::InitFunctionBodyForNode(Node& node) {
if (node.op_ && (node.op_->HasFunction() || node.op_->HasContextDependentFunction())) {
onnx::FunctionProto onnx_function_proto;
if (node.op_->HasContextDependentFunction()) {
NodeProto node_proto;
node.ToProto(node_proto);
onnx::FunctionBodyBuildContextImpl function_body_ctx(node_proto);
node.op_->BuildContextDependentFunction(function_body_ctx, onnx_function_proto);
} else {
onnx_function_proto = *(node.op_->GetFunction());
}
auto func_ptr = onnxruntime::make_unique<onnxruntime::FunctionImpl>(*this, node.Index(), onnx_function_proto,
logger_);
function_container_.emplace_back(std::move(func_ptr));
node.SetFunctionBody(*function_container_.back());
}
}
void Graph::FindAllSubgraphs(std::vector<Graph*>& subgraphs) {
for (auto& node : Nodes()) {
for (auto& subgraph : node.MutableSubgraphs()) {
@ -2398,8 +2414,8 @@ const std::vector<const NodeArg*>& Graph::GetValueInfo() const noexcept {
return value_info_;
}
void Graph::AddValueInfo(const NodeArg* new_value_info){
for(const auto* info : value_info_){
void Graph::AddValueInfo(const NodeArg* new_value_info) {
for (const auto* info : value_info_) {
ORT_ENFORCE(info->Name() != new_value_info->Name(), "Error: trying to add an existing value info.");
}
value_info_.push_back(new_value_info);

View file

@ -376,7 +376,14 @@ Status TransformGraphForMixedPrecision(Graph& graph,
const std::unordered_set<std::string>& weights_to_train,
bool use_fp16_initializer,
std::unordered_map<std::string, NodeArg*>& fp32_weight_name_to_fp16_node_arg) {
// Stag 1: Convert whole graph including forward and backward to FP16
// Stage 1: Convert whole graph including forward and backward to FP16
// Initialize function body for all function nodes
// This is required to make sure after converting inputs\weights to FP16
// the new NodeArg updates are correctly propagated to the function body nodes as well.
for (auto& node : graph.Nodes()) {
graph.InitFunctionBodyForNode(node);
}
// Insert Cast node to convert inputs from FP32 to FP16
for (const NodeArg* input : graph.GetInputs()) {
if (input->TypeAsProto()->tensor_type().elem_type() == ONNX_NAMESPACE::TensorProto_DataType_FLOAT) {