All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
kaldi::nnet3::compute_computation_steps Namespace Reference

Functions

int32 AddInputSteps (const Nnet &nnet, const ComputationRequest &request, const ComputationGraph &graph, std::vector< std::vector< int32 > > *steps)
 Adds a "step" for each of the inputs in the ComputationRequest.
void AddOutputSteps (const Nnet &nnet, const ComputationRequest &request, const ComputationGraph &graph, std::vector< std::vector< int32 > > *steps)
 Adds a "step" for each of the outputs in the ComputationRequest.
static void ExtractOnlyComponentCindexes (const std::vector< int32 > &cindex_ids, const ComputationGraph &graph, const Nnet &nnet, std::vector< Cindex > *cindexes)
 Convert the cindex_ids in the vector "cindex_ids" to cindexes, but only keeping those that correspond to nodes of type kComponent.
static void AddComponentSteps (const Nnet &nnet, const ComputationGraph &graph, const std::vector< std::vector< int32 > > &phases, std::vector< std::vector< int32 > > *component_steps)
 Outputs into component_steps, steps corresponding to all Cindexes that correspond to Component nodes and that are not inputs to the network.
static void AddComponentInputSteps (const ComputationGraph &graph, std::vector< std::vector< int32 > > *component_steps, std::vector< std::vector< int32 > > *all_steps)
 You call this function after calling AddInputSteps to add steps for inputs to "all_steps", then calling AddComponentSteps to output steps for components to "component_steps".
static void CreateCindexIdToStep (const ComputationGraph &graph, const std::vector< std::vector< int32 > > &all_steps, std::vector< int32 > *cindex_id_to_step)
static void AddDimRangeSteps (const Nnet &nnet, ComputationGraph *graph, std::vector< std::vector< int32 > > *all_steps)
 This function inserts into "all_steps", which at this point should contain all but the output steps, steps corresponding to any nodes of type kDimRange.
void ReorderIndexes (const Nnet &nnet, const ComputationRequest &request, const ComputationGraph &graph, std::vector< std::vector< int32 > > *steps)
 This function would not be necessary if we had not added the ReorderIndexes function to class Component.

Function Documentation

static void kaldi::nnet3::compute_computation_steps::AddComponentInputSteps ( const ComputationGraph &  graph,
std::vector< std::vector< int32 > > *  component_steps,
std::vector< std::vector< int32 > > *  all_steps 
)
static

You call this function after calling AddInputSteps to add steps for inputs to "all_steps", then calling AddComponentSteps to output steps for components to "component_steps".

This function moves the component steps from "component_steps" to "all_steps", while preceding each component step with a corresponding step for setting up the input to that component (i.e. a step for the preceding Descriptor). The reason we do it like this is (a) to ensure that the step for the input to the Component, which comes from a Descriptor, comes immediately before it, which is convenient; and (b) because it's possible in certain rather weird setups, some Cindexes corresponding to the Descriptors at the inputs of Components will end up being listed in two separate steps; and if we added the input-descriptor steps using the same mechanism as AddComponentSteps, we wouldn't be able to correctly capture this duplication.

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

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

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

{
int32 space_for_outputs = 10; // arbitrary.
all_steps->reserve(all_steps->size() +
component_steps->size() * 2 + space_for_outputs);
for (size_t i = 0; i < component_steps->size(); i++) {
std::vector<int32> &component_step = (*component_steps)[i];
KALDI_ASSERT(!component_step.empty());
// First make a step for the descriptor at the input of this Component.
unordered_set<int32> descriptor_cindex_ids;
std::vector<int32>::iterator iter = component_step.begin(),
end = component_step.end();
for (; iter != end; ++iter) {
int32 c = *iter;
const std::vector<int32> &dependencies = graph.dependencies[c];
std::vector<int32>::const_iterator dep_iter = dependencies.begin(),
dep_end = dependencies.end();
for (; dep_iter != dep_end; ++dep_iter) {
int32 d = *dep_iter;
descriptor_cindex_ids.insert(d);
}
}
// Convert to Cindexes so we can sort them as Cindexes.
std::vector<Cindex> descriptor_cindexes;
descriptor_cindexes.reserve(descriptor_cindex_ids.size());
unordered_set<int32>::iterator set_iter = descriptor_cindex_ids.begin(),
set_end = descriptor_cindex_ids.end();
for (; set_iter != set_end; ++set_iter) {
int32 c = *set_iter;
descriptor_cindexes.push_back(graph.cindexes[c]);
}
// sort the cindexes.
std::sort(descriptor_cindexes.begin(), descriptor_cindexes.end());
// We technically allow a Component with no input, e.g. in case where for
// some reason it decides it has no dependencies, e.g. it has a constant
// output. In this case we create an empty step, to preserve the property
// that the step for the Component's input comes immediately before the step
// for the Component itself.
if (!descriptor_cindexes.empty()) {
// Make sure all these cindexes come from the same node_id, which should
// be the one immediately preceding the Component node_id of
// "component_step".
int32 node_id = descriptor_cindexes.front().first;
KALDI_ASSERT(descriptor_cindexes.back().first == node_id &&
graph.cindexes[component_step.front()].first == node_id + 1);
}
// Now that we've sorted, convert back to cindex_ids (this list will be
// the "step").
int32 size = descriptor_cindexes.size();
std::vector<int32> descriptor_step(size);
for (int32 i = 0; i < size; i++) {
descriptor_step[i] = graph.GetCindexId(descriptor_cindexes[i]);
KALDI_ASSERT(descriptor_step[i] != -1);
}
// efficiently add descriptor_step to the end of all_steps.
all_steps->push_back(std::vector<int32>());
all_steps->back().swap(descriptor_step);
// efficiently add component_step to the end of all_steps (this destroys the
// input, which we won't be needing any more).
all_steps->push_back(std::vector<int32>());
all_steps->back().swap(component_step);
}
component_steps->clear();
}
static void kaldi::nnet3::compute_computation_steps::AddComponentSteps ( const Nnet &  nnet,
const ComputationGraph &  graph,
const std::vector< std::vector< int32 > > &  phases,
std::vector< std::vector< int32 > > *  component_steps 
)
static

Outputs into component_steps, steps corresponding to all Cindexes that correspond to Component nodes and that are not inputs to the network.

(note that a Cindex for a Component node that's provided as an input to the network is not case we anticipate being common, but it's possible in the framework). Note, a step is just a list of cindex_ids that can all be computed at the same time.

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

References ExtractOnlyComponentCindexes(), ComputationGraph::GetCindexId(), rnnlm::i, and KALDI_ASSERT.

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

{
int32 num_phase_indexes = phases.size();
std::vector<Cindex> cindexes;
// We don't include phase_index = 0, because all inputs to the network
// (whether the node index is type kInput or kComponent) will be assigned to
// phase_index 0, and no non-inputs should be there (we checked this).
for (int32 phase_index = 1; phase_index < num_phase_indexes; phase_index++) {
ExtractOnlyComponentCindexes(phases[phase_index], graph, nnet, &cindexes);
// now "cindexes" contains all Cindexes that are from Component nodes (and
// we have made sure that none of these are being provided as inputs).
// Sorting this array gives us the ordering we want, where Cindexes from
// different node-ids are separated into contiguous ranges, and within each
// range, they are sorted by Index.
std::sort(cindexes.begin(), cindexes.end());
std::vector<Cindex>::iterator iter = cindexes.begin(), end = cindexes.end();
while (iter != end) {
// each pass through this while loop processes one batch of cindex_ids;
// each batch has a particular node-index.
std::vector<Cindex>::iterator cur_end = iter;
int32 this_node_id = iter->first;
while (cur_end != end && cur_end->first == this_node_id)
cur_end++;
// the range [iter, cur_end) is nonempty and contains all the same node-id.
int32 size = cur_end - iter;
component_steps->push_back(std::vector<int32>());
std::vector<int32> &this_step = component_steps->back();
this_step.resize(size);
for (int32 i = 0; i < size; i++, iter++)
this_step[i] = graph.GetCindexId(*iter);
KALDI_ASSERT(iter == cur_end);
// at this point iter will point to either the end of the "cindexes"
// vector, or the beginning of the next set of Cindexes to process.
}
}
}
static void kaldi::nnet3::compute_computation_steps::AddDimRangeSteps ( const Nnet &  nnet,
ComputationGraph *  graph,
std::vector< std::vector< int32 > > *  all_steps 
)
static

This function inserts into "all_steps", which at this point should contain all but the output steps, steps corresponding to any nodes of type kDimRange.

"graph" is non-const as there are situations in which we might need to add cindexes for nodes of type kDimRange.

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

References ComputationGraph::cindexes, CreateCindexIdToStep(), ComputationGraph::dependencies, ComputationGraph::GetCindexId(), Nnet::GetNode(), rnnlm::i, Nnet::IsDimRangeNode(), KALDI_ASSERT, rnnlm::n, NetworkNode::node_index, Nnet::NumNodes(), and NetworkNode::u.

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

{
int32 num_nodes = nnet.NumNodes();
bool dim_range_node_exists = false;
std::vector<char> is_dim_range_node(num_nodes, '\0');
for (int32 n = 0; n < num_nodes; n++) {
if (nnet.IsDimRangeNode(n)) {
is_dim_range_node[n] = (char)1;
dim_range_node_exists = true;
}
}
if (!dim_range_node_exists)
return;
std::vector<int32> cindex_id_to_step;
CreateCindexIdToStep(*graph, *all_steps, &cindex_id_to_step);
int32 num_steps = all_steps->size();
// We are going to insert steps for nodes of type kDimRange just after the
// kInput or kComponent steps that the kDimRange nodes refer to.
// new_nodes_per_step will be a list of any nodes of type kDimRange that
// have input corresponding to something in that step.
std::vector<std::set<int32> > new_nodes_per_step(num_steps);
int32 num_cindex_ids = graph->cindexes.size();
std::vector<Cindex>::const_iterator iter = graph->cindexes.begin();
for (int32 i = 0; i < num_cindex_ids; i++,iter++) {
const Cindex &cindex = *iter;
int32 node_index = cindex.first;
if (!is_dim_range_node[node_index])
continue;
const NetworkNode &node = nnet.GetNode(node_index);
Cindex input_cindex(node.u.node_index, cindex.second);
int32 input_cindex_id = graph->GetCindexId(input_cindex);
KALDI_ASSERT(input_cindex_id != -1);
int32 input_step = cindex_id_to_step[input_cindex_id];
KALDI_ASSERT(input_step != -1);
new_nodes_per_step[input_step].insert(node_index);
}
int32 num_new_steps = 0, space_for_output = 10;
for (int32 step = 0; step < num_steps; step++)
num_new_steps += new_nodes_per_step[step].size();
// we'll later swap all_steps_out with all_steps.
std::vector<std::vector<int32> > all_steps_out;
all_steps_out.reserve(num_steps + num_new_steps + space_for_output);
for (int32 step = 0; step < num_steps; step++) {
std::vector<int32> &this_step = (*all_steps)[step];
int32 cur_out_index = all_steps_out.size();
all_steps_out.push_back(std::vector<int32>()); // make space for this step.
std::set<int32>::iterator iter = new_nodes_per_step[step].begin(),
end = new_nodes_per_step[step].end();
for (; iter != end; ++iter) {
int32 node = *iter, size = this_step.size();
std::vector<int32> new_step(size);
for (int32 i = 0; i < size; i++) {
int32 cindex_id = this_step[i];
Cindex dimrange_cindex(node, graph->cindexes[cindex_id].second);
bool input = false, is_new;
int32 dimrange_cindex_id = graph->GetCindexId(dimrange_cindex,
input, &is_new);
new_step[i] = dimrange_cindex_id;
if (is_new) { // if we newly added this cindex_id, note the dependency
// on its input.
graph->dependencies[dimrange_cindex_id].push_back(cindex_id);
}
}
all_steps_out.push_back(std::vector<int32>());
all_steps_out.back().swap(new_step);
}
all_steps_out[cur_out_index].swap(this_step);
}
all_steps->swap(all_steps_out);
}
int32 kaldi::nnet3::compute_computation_steps::AddInputSteps ( const Nnet &  nnet,
const ComputationRequest &  request,
const ComputationGraph &  graph,
std::vector< std::vector< int32 > > *  steps 
)

Adds a "step" for each of the inputs in the ComputationRequest.

Does this in the same order in which they were declared in the request (this order won't matter at all). returns the total number of cindex_ids that correspond to inputs.

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

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

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

{
KALDI_ASSERT(steps->empty());
steps->reserve(50); // will minimize unnecessary copies of vectors.
unordered_set<int32> all_nodes; // to make sure nothing is listed twice.
int32 num_cindex_ids = 0;
for (int32 i = 0; i < request.inputs.size(); i++) {
int32 n = nnet.GetNodeIndex(request.inputs[i].name);
if (n == -1)
KALDI_ERR << "Network has no output with name "
<< request.inputs[i].name;
// ensure no input node is listed twice.
KALDI_ASSERT(all_nodes.count(n) == 0 && "Invalid computation request: "
"double listing of node.");
all_nodes.insert(n);
KALDI_ASSERT(!request.inputs[i].indexes.empty() &&
"Computation request had no indexes for input ");
steps->push_back(std::vector<int32>());
std::vector<int32> &this_step = steps->back();
this_step.resize(request.inputs[i].indexes.size());
for (int32 j = 0; j < request.inputs[i].indexes.size(); j++) {
Cindex cindex(n, request.inputs[i].indexes[j]);
int32 cindex_id = graph.GetCindexId(cindex);
KALDI_ASSERT(cindex_id != -1); // would be code error.
this_step[j] = cindex_id;
}
num_cindex_ids += request.inputs[i].indexes.size();
}
return num_cindex_ids;
}
void kaldi::nnet3::compute_computation_steps::AddOutputSteps ( const Nnet &  nnet,
const ComputationRequest &  request,
const ComputationGraph &  graph,
std::vector< std::vector< int32 > > *  steps 
)

Adds a "step" for each of the outputs in the ComputationRequest.

This will be done after adding steps for all the inputs and then all the non(input/output)s. Does this in the same order in which they were declared in the request (this won't matter at all).

Definition at line 1414 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::ComputeComputationSteps().

{
std::set<int32> all_nodes; // to make sure nothing listed twice.
for (int32 i = 0; i < request.outputs.size(); i++) {
int32 n = nnet.GetNodeIndex(request.outputs[i].name);
if (n == -1)
KALDI_ERR << "Network has no output with name "
<< request.outputs[i].name;
// ensure no output node is listed twice.
KALDI_ASSERT(all_nodes.count(n) == 0 && "Invalid computation request: "
"double listing of node.");
all_nodes.insert(n);
KALDI_ASSERT(!request.outputs[i].indexes.empty() &&
"Computation request had no indexes for output ");
steps->push_back(std::vector<int32>());
std::vector<int32> &this_step = steps->back();
this_step.resize(request.outputs[i].indexes.size());
for (int32 j = 0; j < request.outputs[i].indexes.size(); j++) {
Cindex cindex(n, request.outputs[i].indexes[j]);
int32 cindex_id = graph.GetCindexId(cindex);
KALDI_ASSERT(cindex_id != -1); // would be code error.
this_step[j] = cindex_id;
}
}
}
static void kaldi::nnet3::compute_computation_steps::CreateCindexIdToStep ( const ComputationGraph &  graph,
const std::vector< std::vector< int32 > > &  all_steps,
std::vector< int32 > *  cindex_id_to_step 
)
static

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

References ComputationGraph::cindexes.

Referenced by AddDimRangeSteps().

{
int32 num_cindex_ids = graph.cindexes.size();
cindex_id_to_step->clear();
cindex_id_to_step->resize(num_cindex_ids, -1);
int32 num_steps = all_steps.size();
for (int32 step = 0; step < num_steps; step++) {
std::vector<int32>::const_iterator iter = all_steps[step].begin(),
end = all_steps[step].end();
for (; iter != end; ++iter) {
int32 cindex_id = *iter;
(*cindex_id_to_step)[cindex_id] = step;
}
}
}
static void kaldi::nnet3::compute_computation_steps::ExtractOnlyComponentCindexes ( const std::vector< int32 > &  cindex_ids,
const ComputationGraph &  graph,
const Nnet &  nnet,
std::vector< Cindex > *  cindexes 
)
static

Convert the cindex_ids in the vector "cindex_ids" to cindexes, but only keeping those that correspond to nodes of type kComponent.

Asserts that none of these cindexes have the "is_input" set to true. [this is possible because we call this only for phases >1, and inputs should not be there.]

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

References ComputationGraph::cindexes, ComputationGraph::is_input, Nnet::IsComponentNode(), and KALDI_ASSERT.

Referenced by AddComponentSteps().

{
cindexes->clear();
cindexes->reserve(cindex_ids.size());
std::vector<int32>::const_iterator iter = cindex_ids.begin(),
end = cindex_ids.end();
for (; iter != end; ++iter) {
int32 cindex_id = *iter;
const Cindex &cindex = graph.cindexes[cindex_id];
if (nnet.IsComponentNode(cindex.first)) {
KALDI_ASSERT(!graph.is_input[cindex_id]);
cindexes->push_back(cindex);
}
}
}
void kaldi::nnet3::compute_computation_steps::ReorderIndexes ( const Nnet &  nnet,
const ComputationRequest &  request,
const ComputationGraph &  graph,
std::vector< std::vector< int32 > > *  steps 
)

This function would not be necessary if we had not added the ReorderIndexes function to class Component.

It is responsible for possibly modifying the order of the inputs and outputs of non-simple Components, and also possibly removing some inputs if the Component has decided it doesn't need them. It may be a while before this is ever used for something. An example use is that maybe in convolutional nets or simple models, some components may want, efficiency or convenience, a certain ordering of the input that differs from the normal order.

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

References ComputationGraph::cindexes, NetworkNode::component_index, ComputationGraph::GetCindexId(), Nnet::GetComponent(), Nnet::GetNode(), rnnlm::i, ComputationGraph::is_input, KALDI_ASSERT, kaldi::nnet3::kComponent, kaldi::nnet3::kReordersIndexes, NetworkNode::node_type, Component::Properties(), Component::ReorderIndexes(), and NetworkNode::u.

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

{
for (int32 step = 0; step < steps->size(); step++) {
std::vector<int32> &cindex_ids = (*steps)[step];
if (cindex_ids.empty()) continue;
int32 cindex_id = cindex_ids.front();
int32 node_index = graph.cindexes[cindex_id].first;
const NetworkNode &node = nnet.GetNode(node_index);
if (node.node_type != kComponent ||
graph.is_input[cindex_id])
continue; // nothing to do if an input, or if not a Component.
int32 c = node.u.component_index;
const Component *component = nnet.GetComponent(c);
if (!(component->Properties() & kReordersIndexes))
continue; // nothing to do if it doesn't modify indexes.
KALDI_ASSERT(step > 0); // or should have continued already.
// preceding step will be Cindexes from the input Descriptor.
std::vector<int32> &input_cindex_ids = (*steps)[step - 1];
int32 size = cindex_ids.size(), input_size = input_cindex_ids.size();
std::vector<Index> indexes(size), input_indexes(input_size);
for (int32 i = 0; i < size; i++)
indexes[i] = graph.cindexes[cindex_ids[i]].second;
for (int32 i = 0; i < input_size; i++)
input_indexes[i] = graph.cindexes[input_cindex_ids[i]].second;
component->ReorderIndexes(&input_indexes, &indexes);
// size should not change.
KALDI_ASSERT(input_indexes.size() == input_size && indexes.size() == size);
if (size > 0) {
int32 node_index = graph.cindexes[cindex_ids.front()].first;
for (int32 i = 0; i < size; i++) {
Cindex cindex(node_index, indexes[i]);
cindex_ids[i] = graph.GetCindexId(cindex);
}
}
if (input_size > 0) {
int32 input_node_index = graph.cindexes[input_cindex_ids.front()].first;
for (int32 i = 0; i < input_size; i++) {
Cindex cindex(input_node_index, input_indexes[i]);
input_cindex_ids[i] = graph.GetCindexId(cindex);
}
}
// note: cindex_ids and input_cindex_ids are references, so we have
// changed *steps by writing to them in the above two loops.
}
}