kaldi::nnet3::computation_graph Namespace Reference

Functions

void AddOutputToGraph (const ComputationRequest &request, const Nnet &nnet, ComputationGraph *graph)
 
void AddInputToGraph (const ComputationRequest &request, const Nnet &nnet, ComputationGraph *graph)
 
static void ComputeDependenciesSubset (const ComputationGraph &graph, const std::vector< int32 > &cindex_id_to_segment_and_epoch, std::vector< std::vector< int32 > > *dependencies_subset)
 This function outputs to dependencies_subset[c], for each cindex_id c, the subset of elements d of graph.dependencies[c] such that cindex_id_to_segment_and_epoch[d] == cindex_id_to_segment_and_epoch[c]. More...
 
static void ComputeEpochInfo (const Nnet &nnet, const ComputationGraph &graph, std::vector< int32 > *cindex_id_to_segment_and_epoch, std::vector< std::vector< std::vector< int32 > > > *epochs_per_segment, std::vector< bool > *epoch_is_trivial)
 This function computes certain information about "epochs" of cindex_ids. More...
 

Function Documentation

◆ AddInputToGraph()

void kaldi::nnet3::computation_graph::AddInputToGraph ( const ComputationRequest request,
const Nnet nnet,
ComputationGraph graph 
)

Definition at line 994 of file nnet-computation-graph.cc.

References ComputationGraph::GetCindexId(), Nnet::GetNode(), Nnet::GetNodeIndex(), rnnlm::i, ComputationRequest::inputs, rnnlm::j, KALDI_ASSERT, KALDI_ERR, kaldi::nnet3::kComponent, kaldi::nnet3::kInput, rnnlm::n, and NetworkNode::node_type.

Referenced by kaldi::nnet3::ComputeComputationGraph().

996  {
997  int32 num_added = 0;
998  for (int32 i = 0; i < request.inputs.size(); i++) {
999  int32 n = nnet.GetNodeIndex(request.inputs[i].name);
1000  if (n == -1)
1001  KALDI_ERR << "Network has no input with name "
1002  << request.inputs[i].name;
1003  NodeType t = nnet.GetNode(n).node_type;
1004  KALDI_ASSERT((t == kInput || t == kComponent) &&
1005  "Inputs to graph only allowed for Input and Component nodes.");
1006 
1007  for (int32 j = 0; j < request.inputs[i].indexes.size(); j++) {
1008  Cindex cindex(n, request.inputs[i].indexes[j]);
1009  bool is_input = true, is_new;
1010  graph->GetCindexId(cindex, is_input, &is_new); // ignore the return value.
1011  KALDI_ASSERT(is_new && "Input index seems to be listed more than once");
1012  num_added++;
1013  }
1014  }
1015  KALDI_ASSERT(num_added > 0 && "AddInputToGraph: nothing to add.");
1016 }
kaldi::int32 int32
std::pair< int32, Index > Cindex
Definition: nnet-common.h:115
struct rnnlm::@11::@12 n
#define KALDI_ERR
Definition: kaldi-error.h:147
#define KALDI_ASSERT(cond)
Definition: kaldi-error.h:185

◆ AddOutputToGraph()

void kaldi::nnet3::computation_graph::AddOutputToGraph ( const ComputationRequest request,
const Nnet nnet,
ComputationGraph graph 
)

Definition at line 971 of file nnet-computation-graph.cc.

References ComputationGraph::GetCindexId(), Nnet::GetNodeIndex(), rnnlm::i, rnnlm::j, KALDI_ASSERT, KALDI_ERR, rnnlm::n, and ComputationRequest::outputs.

Referenced by kaldi::nnet3::ComputeComputationGraph().

973  {
974  int32 num_added = 0;
975  for (int32 i = 0; i < request.outputs.size(); i++) {
976  int32 n = nnet.GetNodeIndex(request.outputs[i].name);
977  if (n == -1)
978  KALDI_ERR << "Network has no output with name "
979  << request.outputs[i].name;
980  for (int32 j = 0; j < request.outputs[i].indexes.size(); j++) {
981  Cindex cindex(n, request.outputs[i].indexes[j]);
982  bool is_input = false, is_new;
983  graph->GetCindexId(cindex, is_input, &is_new); // ignore the return value.
984  KALDI_ASSERT(is_new && "Output index seems to be listed more than once");
985  num_added++;
986  }
987  }
988  KALDI_ASSERT(num_added > 0 && "AddOutputToGraph: nothing to add.");
989 }
kaldi::int32 int32
std::pair< int32, Index > Cindex
Definition: nnet-common.h:115
struct rnnlm::@11::@12 n
#define KALDI_ERR
Definition: kaldi-error.h:147
#define KALDI_ASSERT(cond)
Definition: kaldi-error.h:185

◆ ComputeDependenciesSubset()

static void kaldi::nnet3::computation_graph::ComputeDependenciesSubset ( const ComputationGraph graph,
const std::vector< int32 > &  cindex_id_to_segment_and_epoch,
std::vector< std::vector< int32 > > *  dependencies_subset 
)
static

This function outputs to dependencies_subset[c], for each cindex_id c, the subset of elements d of graph.dependencies[c] such that cindex_id_to_segment_and_epoch[d] == cindex_id_to_segment_and_epoch[c].

That is, it's the dependency graph of the entire computation, but removing links that go from one segment/epoch to another segment/epoch. Topologically, 'dependencies_subset' would therefore consist of a bunch of disconnected graphs.

Definition at line 1028 of file nnet-computation-graph.cc.

References ComputationGraph::cindexes, rnnlm::d, ComputationGraph::dependencies, rnnlm::i, and KALDI_ASSERT.

Referenced by kaldi::nnet3::ComputeComputationPhases().

1031  {
1032  int32 num_cindex_ids = graph.cindexes.size();
1033  KALDI_ASSERT(cindex_id_to_segment_and_epoch.size() == num_cindex_ids);
1034  dependencies_subset->resize(num_cindex_ids);
1035  for (int32 cindex_id = 0; cindex_id < num_cindex_ids; cindex_id++) {
1036  int32 phase_index = cindex_id_to_segment_and_epoch[cindex_id];
1037  const std::vector<int32> &dependencies = graph.dependencies[cindex_id];
1038  std::vector<int32> &dep_subset = (*dependencies_subset)[cindex_id];
1039  int32 num_dep = dependencies.size();
1040  for (int32 i = 0; i < num_dep; i++) {
1041  int32 d = dependencies[i];
1042  if (cindex_id_to_segment_and_epoch[d] == phase_index)
1043  dep_subset.push_back(d);
1044  }
1045  }
1046 }
kaldi::int32 int32
#define KALDI_ASSERT(cond)
Definition: kaldi-error.h:185

◆ ComputeEpochInfo()

static void kaldi::nnet3::computation_graph::ComputeEpochInfo ( const Nnet nnet,
const ComputationGraph graph,
std::vector< int32 > *  cindex_id_to_segment_and_epoch,
std::vector< std::vector< std::vector< int32 > > > *  epochs_per_segment,
std::vector< bool > *  epoch_is_trivial 
)
static

This function computes certain information about "epochs" of cindex_ids.

The function ComputeNnetComputationEpochs() from nnet-graph.h gives us a map from the NetworkNode index to an index we call the "epoch" index: basically, nodes that are computed first have a lower epoch index, and all nodes that are part of strongly connected components have the same epoch index. In an acyclic nnet graph each component will usually have its own epoch index, but in things like LSTMs, each LSTM layer (with multiple components) will have its own epoch index.

The overall computation order that we compute, will respect this ordering into epochs (except that outputs of nodes of type kComponent that are actually provided as inputs to the network, won't be subject to these limitations but will come first in the order)... we will just ignore the output of this function as it concerns cindex-ids that are provided as input to the network.

Parameters
nnet[in] The neural net
graph[in] The computation graph
cindex_id_to_segment_and_epoch[out] A vector that maps cindex_id to a number that is the same if two cindex_ids are in the same segment and same epoch, and different otherwise. This number combines the segment index and the epoch index; the details are not important to the calling code.
epochs_per_segment[out] This is a listing of all the cindex_ids in the computation graph, divided up first by segment and then by epoch.
epoch_is_trivial[out] A vector of bool, indexed by the epoch index which is the same as the second index of 'epochs_per_segment', that's true if this epoch index corresponds to just a single NetworkNode (and also true for epoch indexes corresponding to inputs to the network, which will be the first epoch of each segment). This depends on the neural network structure only.

Definition at line 1082 of file nnet-computation-graph.cc.

References ComputationGraph::cindexes, kaldi::nnet3::ComputeNnetComputationEpochs(), rnnlm::i, ComputationGraph::is_input, KALDI_ASSERT, KALDI_VLOG, rnnlm::n, Nnet::NumNodes(), kaldi::nnet3::PrintIntegerVector(), and ComputationGraph::segment_ends.

Referenced by kaldi::nnet3::ComputeComputationPhases().

1087  {
1088 
1089  // node_to_epoch maps each nnet node to an index >= 0 that tells us coarsely
1090  // what order to compute them in... but we may need to compute a finer
1091  // ordering at the cindex_id level in cases like RNNs.
1092  std::vector<int32> node_to_epoch;
1093  ComputeNnetComputationEpochs(nnet, &node_to_epoch);
1094  {
1095  std::ostringstream os;
1096  PrintIntegerVector(os, node_to_epoch);
1097  KALDI_VLOG(6) << "node_to_epoch: " << os.str();
1098  }
1099 
1100  // Add one to the epoch numbering because we will be reserving
1101  // zero for inputs to the network, and we don't want to have to
1102  // prove that epoch number 0 would correspond only to inputs.
1103  for (int32 i = 0; i < node_to_epoch.size(); i++)
1104  node_to_epoch[i]++;
1105  int32 num_nodes = nnet.NumNodes(),
1106  num_cindex_ids = graph.cindexes.size(),
1107  num_segments = graph.segment_ends.size(),
1108  num_epoch_indexes = 1 + *std::max_element(node_to_epoch.begin(),
1109  node_to_epoch.end());
1110  KALDI_ASSERT(node_to_epoch.size() == num_nodes);
1111 
1112  epochs_per_segment->clear();
1113  epochs_per_segment->resize(num_segments);
1114 
1115  // epoch_to_num_nodes is only used so we know whether each epoch
1116  // index corresponds to multiple nodes; if it's just one node then we know
1117  // the computation is very simple and we can do an optimization.
1118  std::vector<int32> epoch_to_num_nodes(num_epoch_indexes, 0);
1119  for (int32 n = 0; n < num_nodes; n++)
1120  epoch_to_num_nodes[node_to_epoch[n]]++;
1121 
1122  epoch_is_trivial->resize(num_epoch_indexes);
1123  for (int32 o = 0; o < num_epoch_indexes; o++) {
1124  KALDI_ASSERT(o == 0 || epoch_to_num_nodes[o] > 0);
1125  (*epoch_is_trivial)[o] = (epoch_to_num_nodes[o] <= 1);
1126  }
1127  cindex_id_to_segment_and_epoch->resize(num_cindex_ids);
1128  KALDI_ASSERT(graph.segment_ends.back() == num_cindex_ids);
1129  int32 cur_segment_start = 0, cur_segment_end;
1130  for (int32 segment = 0; segment < num_segments; segment++) {
1131  cur_segment_end = graph.segment_ends[segment];
1132  std::vector<std::vector<int32> > &epochs = (*epochs_per_segment)[segment];
1133  epochs.resize(num_epoch_indexes);
1134 
1135  for (int32 cindex_id = cur_segment_start;
1136  cindex_id < cur_segment_end; cindex_id++) {
1137  int32 node_index = graph.cindexes[cindex_id].first,
1138  epoch_index = (graph.is_input[cindex_id] ? 0 :
1139  node_to_epoch[node_index]);
1140  (*cindex_id_to_segment_and_epoch)[cindex_id] =
1141  epoch_index + segment * num_epoch_indexes;
1142  epochs[epoch_index].push_back(cindex_id);
1143  }
1144  cur_segment_start = cur_segment_end;
1145  }
1146 }
kaldi::int32 int32
struct rnnlm::@11::@12 n
void ComputeNnetComputationEpochs(const Nnet &nnet, std::vector< int32 > *node_to_epoch)
This function computes the order in which we need to compute each node in the graph, where each node-index n maps to an epoch-index t = 0, 1, ...
Definition: nnet-graph.cc:265
void PrintIntegerVector(std::ostream &os, const std::vector< int32 > &ints)
Definition: nnet-common.cc:525
#define KALDI_ASSERT(cond)
Definition: kaldi-error.h:185
#define KALDI_VLOG(v)
Definition: kaldi-error.h:156