1 #include "caffe2/opt/backend_cutting.h" 2 #include "caffe2/core/logging.h" 3 #include "caffe2/opt/converter.h" 4 #include "nomnigraph/Converters/Dot.h" 5 #include "nomnigraph/Representations/NeuralNet.h" 20 struct GroupAnnotation {
21 GroupAnnotation(
int i,
int g = -1) : group(g), in_degree(i) {}
24 bool needs_transform{
true};
27 std::string ShowNode(NodeRef node) {
28 if (nn::is<NeuralNetData>(node)) {
29 const auto* nn_tensor = nn::get<NeuralNetData>(node);
30 return c10::str(
"Tensor: ", nn_tensor->getName());
31 }
else if (nn::is<NeuralNetOperator>(node)) {
32 const auto* nn_op = nn::get<NeuralNetOperator>(node);
34 dyn_cast<Caffe2Annotation>(nn_op->getAnnotation())->getOperatorDef();
35 return c10::str(
"Op: ", op_def.type());
37 CAFFE_THROW(
"Known node");
43 std::map<std::string, std::string> labelMap;
44 assert(node->data() &&
"Node doesn't have data, can't render it");
45 if (isa<NeuralNetOperator>(node->data())) {
48 op->getName() +
" (" + c10::to_string((
unsigned long long)node) +
")";
49 auto* annotation = op->getAnnotation();
50 if (annotation && isa<Caffe2Annotation>(annotation)) {
51 auto device_annotation = dyn_cast<Caffe2Annotation>(annotation);
52 labelMap[
"label"] +=
"\\n[" + device_annotation->getDevice() +
"]";
53 auto hash = std::hash<std::string>{}(device_annotation->getDevice());
54 std::stringstream hex_stream;
55 hex_stream << std::hex << hash;
56 labelMap[
"color"] =
"#" + hex_stream.str().substr(0, 6);
57 labelMap[
"fontcolor"] = labelMap[
"color"];
59 labelMap[
"shape"] =
"box";
60 }
else if (isa<Data>(node->data())) {
62 labelMap[
"label"] = tensor->getName();
63 labelMap[
"label"] +=
"_" + c10::to_string(tensor->getVersion()) +
" " +
64 c10::to_string((
unsigned long long)node);
69 std::ofstream out(
"dump.dot");
70 out << nom::converters::convertToDotString(g, nnprinter);
74 struct VisitorContext {
75 VisitorContext(std::function<
bool(
const caffe2::OperatorDef&)> func)
78 std::unordered_map<NodeRef, GroupAnnotation> infos;
79 std::unordered_set<NodeRef> frontier;
80 std::vector<NodeRef> current_group;
81 std::function<bool(const caffe2::OperatorDef&)> predicate;
84 bool find_supported{
true};
87 GroupAnnotation& GetInfo(
88 std::unordered_map<NodeRef, GroupAnnotation>& infos,
90 auto it = infos.find(node);
91 CAFFE_ENFORCE(it != infos.end(),
"Node info not found for ", ShowNode(node));
95 const GroupAnnotation& GetInfo(
96 const std::unordered_map<NodeRef, GroupAnnotation>& infos,
98 auto it = infos.find(node);
100 it != infos.end(),
"Const node info not found for ", ShowNode(node));
109 const std::vector<NodeRef>& current_frontier,
110 VisitorContext* context) {
111 std::queue<NodeRef> q;
112 for (
const auto n : current_frontier) {
117 auto node = q.front();
119 auto& info = GetInfo(context->infos, node);
122 if (nn::is<NeuralNetOperator>(node)) {
123 const auto* nn_op = nn::get<NeuralNetOperator>(node);
125 dyn_cast<Caffe2Annotation>(nn_op->getAnnotation())->getOperatorDef();
126 bool wanted = context->predicate(op_def);
127 wanted = context->find_supported ? wanted : (!wanted);
129 context->frontier.emplace(node);
135 info.group = context->group;
136 info.needs_transform = context->find_supported;
137 context->current_group.push_back(node);
140 for (
const auto& out_edge : node->getOutEdges()) {
141 auto child_node = out_edge->head();
142 auto& child_info = GetInfo(context->infos, child_node);
143 if (--child_info.in_degree == 0) {
152 struct TransformSubgraph {
153 explicit TransformSubgraph(
154 std::vector<NodeRef>&& f,
155 std::vector<NodeRef>&& n,
158 : input_nodes(
std::move(f)),
163 TransformSubgraph(TransformSubgraph&& rhs) noexcept
164 : input_nodes(
std::move(rhs.input_nodes)),
165 nodes(
std::move(rhs.nodes)),
166 external_input_refs(
std::move(rhs.external_input_refs)),
167 external_output_refs(
std::move(rhs.external_output_refs)),
168 group_id(rhs.group_id),
169 needed(rhs.needed) {}
171 TransformSubgraph& operator=(TransformSubgraph&& rhs) noexcept {
172 input_nodes = std::move(rhs.input_nodes);
173 nodes = std::move(rhs.nodes);
174 external_input_refs = std::move(rhs.external_input_refs);
175 external_output_refs = std::move(rhs.external_output_refs);
176 group_id = rhs.group_id;
182 LOG(INFO) <<
"Group :" << group_id;
183 LOG(INFO) <<
" Input Nodes: ";
184 for (
const auto i : input_nodes) {
185 LOG(INFO) <<
" " << ShowNode(i);
187 LOG(INFO) <<
" Nodes: ";
188 for (
const auto i : nodes) {
189 LOG(INFO) <<
" " << ShowNode(i);
193 std::vector<NodeRef> input_nodes;
194 std::vector<NodeRef> nodes;
195 std::unordered_map<std::string, NodeRef> external_input_refs;
196 std::unordered_map<std::string, NodeRef> external_output_refs;
201 caffe2::NetDef ConvertToC2Net(
202 const TransformSubgraph& sub,
203 const std::unordered_map<NodeRef, GroupAnnotation>& infos) {
205 for (
auto node : sub.nodes) {
206 if (nn::is<NeuralNetOperator>(node)) {
207 const auto* nn_op = nn::get<NeuralNetOperator>(node);
209 isa<Caffe2Annotation>(nn_op->getAnnotation()) &&
210 "Cannot get caffe2 op from NNOp");
212 dyn_cast<Caffe2Annotation>(nn_op->getAnnotation())->getOperatorDef();
213 net.add_op()->CopyFrom(op_def);
216 for (
const auto kv : sub.external_input_refs) {
217 net.add_external_input(kv.first);
218 VLOG(2) <<
"Adding external input: " << kv.first;
220 for (
const auto& kv : sub.external_output_refs) {
221 net.add_external_output(kv.first);
222 VLOG(2) <<
"Adding external output: " << kv.first;
228 void DetectBoundaryReferences(
229 TransformSubgraph* subgraph,
230 const std::unordered_map<NodeRef, GroupAnnotation>& infos,
231 const std::unordered_set<std::string>& original_external_output) {
232 for (
auto node : subgraph->nodes) {
234 for (
auto in_edge : node->getInEdges()) {
235 auto parent_node = in_edge->tail();
236 const auto& info = GetInfo(infos, parent_node);
237 if (info.group != subgraph->group_id &&
238 nn::is<NeuralNetData>(parent_node)) {
239 const auto* nn_tensor = nn::get<const NeuralNetData>(parent_node);
240 subgraph->external_input_refs.emplace(
241 nn_tensor->getName(), parent_node);
246 if (!nn::is<NeuralNetData>(node)) {
253 auto name = nn::get<const NeuralNetData>(node)->getName();
254 if (original_external_output.count(name)) {
255 subgraph->external_output_refs.emplace(name, node);
257 for (
auto child_node : nn::getConsumers(node)) {
258 const auto& info = GetInfo(infos, child_node);
259 if (info.group != subgraph->group_id) {
260 subgraph->external_output_refs.emplace(name, node);
268 void ReplaceSubgraph(
269 const TransformSubgraph& subgraph,
270 caffe2::NetDef& net_opt,
274 for (
auto node : subgraph.nodes) {
275 if (nn::is<NeuralNetData>(node) &&
276 subgraph.external_output_refs.count(
277 nn::get<const NeuralNetData>(node)->getName())) {
278 VLOG(2) <<
"Keeping " << ShowNode(node);
281 VLOG(2) <<
"Deleting " << ShowNode(node);
286 std::unordered_map<std::string, NodeRef> tensor_map;
287 for (
const auto kv : subgraph.external_input_refs) {
288 tensor_map.emplace(kv.first, kv.second);
290 for (
const auto kv : subgraph.external_output_refs) {
291 tensor_map.emplace(kv.first, kv.second);
293 for (
auto& op : *net_opt.mutable_op()) {
295 for (
const auto& input : op.input()) {
296 if (!tensor_map.count(input)) {
298 g->
createNode(caffe2::make_unique<nom::repr::Tensor>(input));
301 auto tensor_node = tensor_map[input];
305 for (
const auto& output : op.output()) {
306 if (!tensor_map.count(output)) {
308 g->
createNode(caffe2::make_unique<nom::repr::Tensor>(output));
310 auto tensor_node = tensor_map[output];
314 op_node->resetData(convertToNeuralNetOperator(op));
318 void PruneUnrefereredNodes(
NNModule* nn) {
319 auto& g = nn->dataFlow;
320 std::vector<NodeRef> to_delete;
321 for (
auto node : g.getMutableNodes()) {
322 if (!nn::hasProducer(node) && !nn::hasConsumer(node)) {
323 to_delete.push_back(node);
326 for (
auto i : to_delete) {
327 if (nn::is<NeuralNetData>(i)) {
328 auto name = nn::get<NeuralNetData>(i)->getName();
329 auto it = nn->inputs.find(i);
330 if (it != nn->inputs.end()) {
331 VLOG(2) <<
"Removing external input " << name;
332 nn->inputs.erase(it);
334 it = nn->outputs.find(i);
335 if (it != nn->outputs.end()) {
336 VLOG(2) <<
"Removing external output " << name;
337 nn->outputs.erase(it);
346 caffe2::NetDef OptimizeForBackend(
348 std::function<
bool(
const caffe2::OperatorDef&)> supports,
349 std::function<caffe2::NetDef(
const caffe2::NetDef&)> transform_func,
352 auto& dfg = nn.dataFlow;
355 VisitorContext context(supports);
356 std::vector<NodeRef> external_inputs;
357 std::unordered_set<std::string> external_outputs;
358 for (
auto node : dfg.getMutableNodes()) {
359 context.infos.emplace(
360 std::piecewise_construct,
361 std::forward_as_tuple(node),
362 std::forward_as_tuple(node->getInEdges().size(), -1));
364 if (!nn::is<NeuralNetOperator>(node)) {
365 if (!nn::hasProducer(node)) {
366 external_inputs.push_back(node);
368 if (!nn::hasConsumer(node)) {
369 external_outputs.emplace(nn::get<const NeuralNetData>(node)->getName());
375 context.frontier.clear();
376 context.current_group.clear();
377 context.find_supported =
false;
378 std::vector<TransformSubgraph> subs;
379 for (std::vector<NodeRef> frontier(
380 external_inputs.begin(), external_inputs.end());
382 context.find_supported = !context.find_supported) {
383 Explore(frontier, &context);
384 if (context.find_supported) {
387 std::move(context.current_group),
389 context.find_supported);
392 frontier.assign(context.frontier.begin(), context.frontier.end());
393 context.frontier.clear();
394 context.current_group.clear();
399 std::vector<caffe2::NetDef> opt_subnets;
400 opt_subnets.reserve(subs.size());
401 for (
auto& g : subs) {
403 DetectBoundaryReferences(&g, context.infos, external_outputs);
405 caffe2::NetDef subnet = ConvertToC2Net(g, context.infos);
408 opt_subnets.emplace_back(transform_func(subnet));
410 ReplaceSubgraph(g, opt_subnets.back(), &dfg);
415 PruneUnrefereredNodes(&nn);
421 auto new_net = convertToCaffe2Proto(nn);
422 new_net.set_name(net.name() +
"_opt");
NodeRef createNode(T &&data)
Creates a node and retains ownership of it.
A global dictionary that holds information about what Caffe2 modules have been loaded in the current ...
void deleteNode(NodeRef n)
Deletes a node from the graph.
repr::NNModule convertToNNModule(const caffe2::NetDef &net, bool strict, std::vector< repr::NNGraph::NodeRef > *opNodeVec)
Ingest a caffe2 protobuf model and output an NNModule.
A simple graph implementation.
EdgeRef createEdge(NodeRef tail, NodeRef head, U...data)
Creates a directed edge and retains ownership of it.