ModelCollapser Class Reference
Collaboration diagram for ModelCollapser:

Public Member Functions

 ModelCollapser (const CollapseModelConfig &config, Nnet *nnet)
 
void Collapse ()
 

Private Member Functions

int32 CollapseComponents (int32 component_index1, int32 component_index2)
 This function tries to collapse two successive components, where the component 'component_index1' appears as the input of 'component_index2'. More...
 
int32 SumDescriptorIsCollapsible (const SumDescriptor &sum_desc)
 
int32 DescriptorIsCollapsible (const Descriptor &desc)
 
Descriptor ReplaceNodeInDescriptor (const Descriptor &src, int32 node_to_replace, const Descriptor &expr)
 
bool OptimizeNode (int32 node_index)
 This function modifies the neural network in the case where 'node_index' is a component-input node whose component (in the node at 'node_index + 1), if a bunch of other conditions also apply. More...
 
int32 CollapseComponentsDropout (int32 component_index1, int32 component_index2)
 Tries to produce a component that's equivalent to running the component 'component_index2' with input given by 'component_index1'. More...
 
int32 CollapseComponentsBatchnorm (int32 component_index1, int32 component_index2)
 Tries to produce a component that's equivalent to running the component 'component_index2' with input given by 'component_index1'. More...
 
int32 CollapseComponentsAffine (int32 component_index1, int32 component_index2)
 Tries to produce a component that's equivalent to running the component 'component_index2' with input given by 'component_index1'. More...
 
int32 CollapseComponentsScale (int32 component_index1, int32 component_index2)
 Tries to produce a component that's equivalent to running the component 'component_index2' with input given by 'component_index1'. More...
 
int32 GetDiagonallyPreModifiedComponentIndex (const CuVectorBase< BaseFloat > &offset, const CuVectorBase< BaseFloat > &scale, const std::string &src_identifier, int32 component_index)
 This function finds, or creates, a component which is like 'component_index' but is combined with a diagonal offset-and-scale transform *before* the component. More...
 
int32 GetScaledComponentIndex (int32 component_index, BaseFloat scale)
 Given a component 'component_index', returns a component which will give the same output as the current component gives when its input is scaled by 'scale'. More...
 

Static Private Member Functions

static void PreMultiplyAffineParameters (const CuVectorBase< BaseFloat > &offset, const CuVectorBase< BaseFloat > &scale, CuVectorBase< BaseFloat > *bias_params, CuMatrixBase< BaseFloat > *linear_params)
 This helper function, used GetDiagonallyPreModifiedComponentIndex, modifies the linear and bias parameters of an affine transform to capture the effect of preceding that affine transform by a diagonal affine transform with parameters 'offset' and 'scale'. More...
 

Private Attributes

const CollapseModelConfigconfig_
 
Nnetnnet_
 

Detailed Description

Definition at line 1447 of file nnet-utils.cc.

Constructor & Destructor Documentation

◆ ModelCollapser()

ModelCollapser ( const CollapseModelConfig config,
Nnet nnet 
)
inline

Definition at line 1449 of file nnet-utils.cc.

1450  :
1451  config_(config), nnet_(nnet) { }
const CollapseModelConfig & config_
Definition: nnet-utils.cc:2095

Member Function Documentation

◆ Collapse()

void Collapse ( )
inline

Definition at line 1452 of file nnet-utils.cc.

References KALDI_ERR, KALDI_LOG, rnnlm::n, SvdApplier::nnet_, Nnet::NumComponents(), Nnet::NumNodes(), Nnet::RemoveOrphanComponents(), and Nnet::RemoveOrphanNodes().

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

1452  {
1453  bool changed = true;
1454  int32 num_nodes = nnet_->NumNodes(),
1455  num_iters = 0;
1456  int32 num_components1 = nnet_->NumComponents();
1457  for (; changed; num_iters++) {
1458  changed = false;
1459  for (int32 n = 0; n < num_nodes; n++)
1460  if (OptimizeNode(n))
1461  changed = true;
1462  // we shouldn't iterate more than a couple of times.
1463  if (num_iters >= 10)
1464  KALDI_ERR << "Something went wrong collapsing model.";
1465  }
1466  int32 num_components2 = nnet_->NumComponents();
1469  int32 num_components3 = nnet_->NumComponents();
1470  if (num_components2 != num_components1 ||
1471  num_components3 != num_components2)
1472  KALDI_LOG << "Added " << (num_components2 - num_components1)
1473  << " components, removed "
1474  << (num_components2 - num_components3);
1475  }
int32 NumNodes() const
Definition: nnet-nnet.h:126
bool OptimizeNode(int32 node_index)
This function modifies the neural network in the case where &#39;node_index&#39; is a component-input node wh...
Definition: nnet-utils.cc:1638
kaldi::int32 int32
void RemoveOrphanComponents()
Definition: nnet-nnet.cc:844
struct rnnlm::@11::@12 n
void RemoveOrphanNodes(bool remove_orphan_inputs=false)
Definition: nnet-nnet.cc:932
#define KALDI_ERR
Definition: kaldi-error.h:147
int32 NumComponents() const
Definition: nnet-nnet.h:124
#define KALDI_LOG
Definition: kaldi-error.h:153

◆ CollapseComponents()

int32 CollapseComponents ( int32  component_index1,
int32  component_index2 
)
inlineprivate

This function tries to collapse two successive components, where the component 'component_index1' appears as the input of 'component_index2'.

If the two components can be collapsed in that way, it returns the index of a combined component.

Note: in addition to the two components simply being chained together, this function supports the case where different time-offsets of the first component are appendend together as the input of the second component. So the input-dim of the second component may be a multiple of the output-dim of the first component.

The function returns the component-index of a (newly created or existing) component that combines both of these components, if it's possible to combine them; or it returns -1 if it's not possible.

Definition at line 1493 of file nnet-utils.cc.

1494  {
1495  int32 ans;
1496  if (config_.collapse_dropout &&
1497  (ans = CollapseComponentsDropout(component_index1,
1498  component_index2)) != -1)
1499  return ans;
1501  (ans = CollapseComponentsBatchnorm(component_index1,
1502  component_index2)) != -1)
1503  return ans;
1504  if (config_.collapse_affine &&
1505  (ans = CollapseComponentsAffine(component_index1,
1506  component_index2)) != -1)
1507  return ans;
1508  if (config_.collapse_scale &&
1509  (ans = CollapseComponentsScale(component_index1,
1510  component_index2)) != -1)
1511  return ans;
1512  return -1;
1513  }
int32 CollapseComponentsDropout(int32 component_index1, int32 component_index2)
Tries to produce a component that&#39;s equivalent to running the component &#39;component_index2&#39; with input...
Definition: nnet-utils.cc:1693
kaldi::int32 int32
const CollapseModelConfig & config_
Definition: nnet-utils.cc:2095
int32 CollapseComponentsBatchnorm(int32 component_index1, int32 component_index2)
Tries to produce a component that&#39;s equivalent to running the component &#39;component_index2&#39; with input...
Definition: nnet-utils.cc:1733
int32 CollapseComponentsScale(int32 component_index1, int32 component_index2)
Tries to produce a component that&#39;s equivalent to running the component &#39;component_index2&#39; with input...
Definition: nnet-utils.cc:1860
int32 CollapseComponentsAffine(int32 component_index1, int32 component_index2)
Tries to produce a component that&#39;s equivalent to running the component &#39;component_index2&#39; with input...
Definition: nnet-utils.cc:1761

◆ CollapseComponentsAffine()

int32 CollapseComponentsAffine ( int32  component_index1,
int32  component_index2 
)
inlineprivate

Tries to produce a component that's equivalent to running the component 'component_index2' with input given by 'component_index1'.

This handles the case where 'component_index1' is of type FixedAffineComponent, AffineComponent or NaturalGradientAffineComponent, and 'component_index2' is of type AffineComponent or NaturalGradientAffineComponent.

Returns -1 if this code can't produce a combined component.

Definition at line 1761 of file nnet-utils.cc.

References Nnet::AddComponent(), CuMatrixBase< Real >::AddMatMat(), CuVectorBase< Real >::AddMatVec(), AffineComponent::BiasParams(), FixedAffineComponent::BiasParams(), Nnet::GetComponent(), Nnet::GetComponentIndex(), Nnet::GetComponentName(), rnnlm::i, AffineComponent::Init(), AffineComponent::InputDim(), FixedAffineComponent::InputDim(), KALDI_ASSERT, kaldi::kNoTrans, AffineComponent::LinearParams(), FixedAffineComponent::LinearParams(), SvdApplier::nnet_, CuMatrixBase< Real >::NumCols(), CuMatrixBase< Real >::NumRows(), AffineComponent::OutputDim(), FixedAffineComponent::OutputDim(), CuVectorBase< Real >::Range(), CuMatrixBase< Real >::Range(), and AffineComponent::SetParams().

1762  {
1763 
1764  const FixedAffineComponent *fixed_affine_component1 =
1765  dynamic_cast<const FixedAffineComponent*>(
1766  nnet_->GetComponent(component_index1));
1767  const AffineComponent *affine_component1 =
1768  dynamic_cast<const AffineComponent*>(
1769  nnet_->GetComponent(component_index1)),
1770  *affine_component2 =
1771  dynamic_cast<const AffineComponent*>(
1772  nnet_->GetComponent(component_index2));
1773  if (affine_component2 == NULL ||
1774  (fixed_affine_component1 == NULL && affine_component1 == NULL))
1775  return -1;
1776 
1777  std::ostringstream new_component_name_os;
1778  new_component_name_os << nnet_->GetComponentName(component_index1)
1779  << "." << nnet_->GetComponentName(component_index2);
1780  std::string new_component_name = new_component_name_os.str();
1781  int32 new_component_index = nnet_->GetComponentIndex(new_component_name);
1782  if (new_component_index >= 0)
1783  return new_component_index; // we previously created this.
1784 
1785  const CuMatrix<BaseFloat> *linear_params1;
1786  const CuVector<BaseFloat> *bias_params1;
1787  if (fixed_affine_component1 != NULL) {
1788  if (fixed_affine_component1->InputDim() >
1789  fixed_affine_component1->OutputDim()) {
1790  // first affine component is dimension-reducing, so combining the two
1791  // might be inefficient.
1792  return -1;
1793  }
1794  linear_params1 = &(fixed_affine_component1->LinearParams());
1795  bias_params1 = &(fixed_affine_component1->BiasParams());
1796  } else {
1797  if (affine_component1->InputDim() >
1798  affine_component1->OutputDim()) {
1799  // first affine component is dimension-reducing, so combining the two
1800  // might be inefficient.
1801  return -1;
1802  }
1803  linear_params1 = &(affine_component1->LinearParams());
1804  bias_params1 = &(affine_component1->BiasParams());
1805  }
1806 
1807  int32 input_dim1 = linear_params1->NumCols(),
1808  output_dim1 = linear_params1->NumRows(),
1809  input_dim2 = affine_component2->InputDim(),
1810  output_dim2 = affine_component2->OutputDim();
1811  KALDI_ASSERT(input_dim2 % output_dim1 == 0);
1812  // with typical configurations for TDNNs, like Append(-3, 0, 3) [in xconfigs], a.k.a.
1813  // Append(Offset(foo, -3), foo, Offset(foo, 3)), the first component's output may
1814  // be smaller than the second component's input. We construct a single
1815  // transform with a block-diagonal structure in this case.
1816  int32 multiple = input_dim2 / output_dim1;
1817  CuVector<BaseFloat> bias_params1_full(input_dim2);
1818  CuMatrix<BaseFloat> linear_params1_full(input_dim2,
1819  multiple * input_dim1);
1820  for (int32 i = 0; i < multiple; i++) {
1821  bias_params1_full.Range(i * output_dim1,
1822  output_dim1).CopyFromVec(*bias_params1);
1823  linear_params1_full.Range(i * output_dim1, output_dim1,
1824  i * input_dim1, input_dim1).CopyFromMat(
1825  *linear_params1);
1826  }
1827  const CuVector<BaseFloat> &bias_params2 = affine_component2->BiasParams();
1828  const CuMatrix<BaseFloat> &linear_params2 = affine_component2->LinearParams();
1829 
1830  int32 new_input_dim = multiple * input_dim1,
1831  new_output_dim = output_dim2;
1832  CuMatrix<BaseFloat> new_linear_params(new_output_dim,
1833  new_input_dim);
1834  CuVector<BaseFloat> new_bias_params(bias_params2);
1835  new_bias_params.AddMatVec(1.0, linear_params2, kNoTrans,
1836  bias_params1_full, 1.0);
1837  new_linear_params.AddMatMat(1.0, linear_params2, kNoTrans,
1838  linear_params1_full, kNoTrans, 0.0);
1839 
1840  AffineComponent *new_component = new AffineComponent();
1841  new_component->Init(new_input_dim, new_output_dim, 0.0, 0.0);
1842  new_component->SetParams(new_bias_params, new_linear_params);
1843  return nnet_->AddComponent(new_component_name, new_component);
1844  }
int32 AddComponent(const std::string &name, Component *component)
Adds a new component with the given name, which should not be the same as any existing component name...
Definition: nnet-nnet.cc:161
kaldi::int32 int32
int32 GetComponentIndex(const std::string &node_name) const
returns index associated with this component name, or -1 if no such index.
Definition: nnet-nnet.cc:474
const std::string & GetComponentName(int32 component_index) const
returns individual component name.
Definition: nnet-nnet.cc:689
Component * GetComponent(int32 c)
Return component indexed c. Not a copy; not owned by caller.
Definition: nnet-nnet.cc:150
#define KALDI_ASSERT(cond)
Definition: kaldi-error.h:185

◆ CollapseComponentsBatchnorm()

int32 CollapseComponentsBatchnorm ( int32  component_index1,
int32  component_index2 
)
inlineprivate

Tries to produce a component that's equivalent to running the component 'component_index2' with input given by 'component_index1'.

This handles the case where 'component_index1' is of type BatchnormComponent, and where 'component_index2' is of type AffineComponent or NaturalGradientAffineComponent.

Returns -1 if this code can't produce a combined component (normally because the components have the wrong types).

Definition at line 1733 of file nnet-utils.cc.

References Nnet::GetComponent(), Nnet::GetComponentName(), KALDI_ERR, SvdApplier::nnet_, BatchNormComponent::Offset(), and BatchNormComponent::Scale().

1734  {
1735  const BatchNormComponent *batchnorm_component =
1736  dynamic_cast<const BatchNormComponent*>(
1737  nnet_->GetComponent(component_index1));
1738  if (batchnorm_component == NULL)
1739  return -1;
1740 
1741  if (batchnorm_component->Offset().Dim() == 0) {
1742  KALDI_ERR << "Expected batch-norm components to have test-mode set.";
1743  }
1744  std::string batchnorm_component_name = nnet_->GetComponentName(
1745  component_index1);
1746  return GetDiagonallyPreModifiedComponentIndex(batchnorm_component->Offset(),
1747  batchnorm_component->Scale(),
1748  batchnorm_component_name,
1749  component_index2);
1750  }
int32 GetDiagonallyPreModifiedComponentIndex(const CuVectorBase< BaseFloat > &offset, const CuVectorBase< BaseFloat > &scale, const std::string &src_identifier, int32 component_index)
This function finds, or creates, a component which is like &#39;component_index&#39; but is combined with a d...
Definition: nnet-utils.cc:1934
#define KALDI_ERR
Definition: kaldi-error.h:147
const std::string & GetComponentName(int32 component_index) const
returns individual component name.
Definition: nnet-nnet.cc:689
Component * GetComponent(int32 c)
Return component indexed c. Not a copy; not owned by caller.
Definition: nnet-nnet.cc:150

◆ CollapseComponentsDropout()

int32 CollapseComponentsDropout ( int32  component_index1,
int32  component_index2 
)
inlineprivate

Tries to produce a component that's equivalent to running the component 'component_index2' with input given by 'component_index1'.

This handles the case where 'component_index1' is of type DropoutComponent or GeneralDropoutComponent, and where 'component_index2' is of type AffineComponent, NaturalGradientAffineComponent, LinearComponent, TdnnComponent or TimeHeightConvolutionComponent.

Returns -1 if this code can't produce a combined component (normally because the components have the wrong types).

Definition at line 1693 of file nnet-utils.cc.

References DropoutComponent::DropoutProportion(), Nnet::GetComponent(), and SvdApplier::nnet_.

1694  {
1695  const DropoutComponent *dropout_component =
1696  dynamic_cast<const DropoutComponent*>(
1697  nnet_->GetComponent(component_index1));
1698  const GeneralDropoutComponent *general_dropout_component =
1699  dynamic_cast<const GeneralDropoutComponent*>(
1700  nnet_->GetComponent(component_index1));
1701 
1702  if (dropout_component == NULL && general_dropout_component == NULL)
1703  return -1;
1704  BaseFloat scale; // the scale we have to apply to correct for removing
1705  // this dropout comonent.
1706  if (dropout_component != NULL) {
1707  BaseFloat dropout_proportion = dropout_component->DropoutProportion();
1708  scale = 1.0 / (1.0 - dropout_proportion);
1709  } else {
1710  // for GeneralDropoutComponent, it's done in such a way that the expectation
1711  // is always 1. (When it's nonzero, we give it a value 1/(1-dropout_proportion).
1712  // So no scaling is needed.
1713  scale = 1.0;
1714  }
1715  // note: if the 2nd component is not of a type that we can scale, the
1716  // following function call will return -1, which is OK.
1717  return GetScaledComponentIndex(component_index2,
1718  scale);
1719  }
float BaseFloat
Definition: kaldi-types.h:29
int32 GetScaledComponentIndex(int32 component_index, BaseFloat scale)
Given a component &#39;component_index&#39;, returns a component which will give the same output as the curre...
Definition: nnet-utils.cc:2050
Component * GetComponent(int32 c)
Return component indexed c. Not a copy; not owned by caller.
Definition: nnet-nnet.cc:150

◆ CollapseComponentsScale()

int32 CollapseComponentsScale ( int32  component_index1,
int32  component_index2 
)
inlineprivate

Tries to produce a component that's equivalent to running the component 'component_index2' with input given by 'component_index1'.

This handles the case where 'component_index1' is of type AffineComponent or NaturalGradientAffineComponent, and 'component_index2' is of type FixedScaleComponent, and the output dim of the first is the same as the input dim of the second. This situation is common in output layers. Later if it's needed, we could easily enable the code to support PerElementScaleComponent.

Returns -1 if this code can't produce a combined component.

Definition at line 1860 of file nnet-utils.cc.

References Nnet::AddComponent(), AffineComponent::BiasParams(), AffineComponent::Copy(), Nnet::GetComponent(), Nnet::GetComponentIndex(), Nnet::GetComponentName(), FixedScaleComponent::InputDim(), AffineComponent::LinearParams(), SvdApplier::nnet_, AffineComponent::OutputDim(), FixedScaleComponent::Scales(), and AffineComponent::SetParams().

1861  {
1862 
1863  const AffineComponent *affine_component1 =
1864  dynamic_cast<const AffineComponent*>(
1865  nnet_->GetComponent(component_index1));
1866  const FixedScaleComponent *fixed_scale_component2 =
1867  dynamic_cast<const FixedScaleComponent*>(
1868  nnet_->GetComponent(component_index2));
1869  if (affine_component1 == NULL ||
1870  fixed_scale_component2 == NULL ||
1871  affine_component1->OutputDim() !=
1872  fixed_scale_component2->InputDim())
1873  return -1;
1874 
1875  std::ostringstream new_component_name_os;
1876  new_component_name_os << nnet_->GetComponentName(component_index1)
1877  << "." << nnet_->GetComponentName(component_index2);
1878  std::string new_component_name = new_component_name_os.str();
1879  int32 new_component_index = nnet_->GetComponentIndex(new_component_name);
1880  if (new_component_index >= 0)
1881  return new_component_index; // we previously created this.
1882 
1883  CuMatrix<BaseFloat> linear_params(affine_component1->LinearParams());
1884  CuVector<BaseFloat> bias_params(affine_component1->BiasParams());
1885  const CuVector<BaseFloat> &scales = fixed_scale_component2->Scales();
1886 
1887  bias_params.MulElements(scales);
1888  linear_params.MulRowsVec(scales);
1889 
1890  AffineComponent *new_affine_component =
1891  dynamic_cast<AffineComponent*>(affine_component1->Copy());
1892  new_affine_component->SetParams(bias_params, linear_params);
1893  return nnet_->AddComponent(new_component_name,
1894  new_affine_component);
1895  }
int32 AddComponent(const std::string &name, Component *component)
Adds a new component with the given name, which should not be the same as any existing component name...
Definition: nnet-nnet.cc:161
kaldi::int32 int32
int32 GetComponentIndex(const std::string &node_name) const
returns index associated with this component name, or -1 if no such index.
Definition: nnet-nnet.cc:474
const std::string & GetComponentName(int32 component_index) const
returns individual component name.
Definition: nnet-nnet.cc:689
Component * GetComponent(int32 c)
Return component indexed c. Not a copy; not owned by caller.
Definition: nnet-nnet.cc:150

◆ DescriptorIsCollapsible()

int32 DescriptorIsCollapsible ( const Descriptor desc)
inlineprivate

Definition at line 1553 of file nnet-utils.cc.

References rnnlm::i, Descriptor::NumParts(), and Descriptor::Part().

1553  {
1554  int32 ans = SumDescriptorIsCollapsible(desc.Part(0));
1555  for (int32 i = 1; i < desc.NumParts(); i++) {
1556  if (ans != -1) {
1557  int32 node_index = SumDescriptorIsCollapsible(desc.Part(i));
1558  if (node_index != ans)
1559  ans = -1;
1560  }
1561  }
1562  // note: ans is only >= 0 if the answers from all parts of
1563  // the SumDescriptors were >=0 and identical to each other.
1564  // Otherwise it will be -1.
1565  return ans;
1566  }
kaldi::int32 int32
int32 SumDescriptorIsCollapsible(const SumDescriptor &sum_desc)
Definition: nnet-utils.cc:1525

◆ GetDiagonallyPreModifiedComponentIndex()

int32 GetDiagonallyPreModifiedComponentIndex ( const CuVectorBase< BaseFloat > &  offset,
const CuVectorBase< BaseFloat > &  scale,
const std::string &  src_identifier,
int32  component_index 
)
inlineprivate

This function finds, or creates, a component which is like 'component_index' but is combined with a diagonal offset-and-scale transform *before* the component.

(We may later create a function called GetDiagonallyPostModifiedComponentIndex if we need to apply the transform *after* the component.

This function doesn't work for convolutional components, because due to zero-padding, it's not possible to represent an offset/scale on the input filters via changes in the convolutional parameters. [the scale, yes; but we don't bother doing that.]

This may require modifying its linear and bias parameters.

Parameters
[in]offsetThe offset term 'b' in the diagnonal transform y = a x + b.
[in]scaleThe scale term 'a' in the diagnonal transform y = a x + b. Must have the same dimension as 'offset'.
[in]src_identifierA string that uniquely identifies 'offset' and 'scale'. In practice it will be the component-index from where 'offset' and 'scale' were taken.
[in]component_indexThe component to be modified (not in-place, but as a copy). The component described in 'component_index' must be AffineComponent, NaturalGradientAffineComponent, LinearComponent or TdnnComponent, and the dimension of 'offset'/'scale' should divide the component input dimension, otherwise it's an error.
Returns
Returns the component-index of a suitably modified component. If one like this already exists, the existing one will be returned. If the component in 'component_index' was not of a type that can be modified in this way, returns -1.

Definition at line 1934 of file nnet-utils.cc.

References Nnet::AddComponent(), AffineComponent::BiasParams(), TdnnComponent::BiasParams(), SvdApplier::ModifiedComponentInfo::component_index, Component::Copy(), TdnnComponent::Copy(), CuVectorBase< Real >::Dim(), Nnet::GetComponent(), Nnet::GetComponentIndex(), Nnet::GetComponentName(), KALDI_ASSERT, UpdatableComponent::LearningRate(), AffineComponent::LinearParams(), TdnnComponent::LinearParams(), CuVectorBase< Real >::Max(), CuVectorBase< Real >::Min(), SvdApplier::nnet_, TdnnComponent::OutputDim(), LinearComponent::OutputDim(), and LinearComponent::Params().

1938  {
1939  KALDI_ASSERT(offset.Dim() > 0 && offset.Dim() == scale.Dim());
1940  if (offset.Max() == 0.0 && offset.Min() == 0.0 &&
1941  scale.Max() == 1.0 && scale.Min() == 1.0)
1942  return component_index; // identity transform.
1943  std::ostringstream new_component_name_os;
1944  new_component_name_os << src_identifier
1945  << "."
1946  << nnet_->GetComponentName(component_index);
1947  std::string new_component_name = new_component_name_os.str();
1948  int32 new_component_index = nnet_->GetComponentIndex(new_component_name);
1949  if (new_component_index >= 0)
1950  return new_component_index; // we previously created this.
1951 
1952  const Component *component = nnet_->GetComponent(component_index);
1953  const AffineComponent *affine_component =
1954  dynamic_cast<const AffineComponent*>(component);
1955  const LinearComponent *linear_component =
1956  dynamic_cast<const LinearComponent*>(component);
1957  const TdnnComponent *tdnn_component =
1958  dynamic_cast<const TdnnComponent*>(component);
1959 
1960  Component *new_component = NULL;
1961  if (affine_component != NULL) {
1962  new_component = component->Copy();
1963  AffineComponent *new_affine_component =
1964  dynamic_cast<AffineComponent*>(new_component);
1965  PreMultiplyAffineParameters(offset, scale,
1966  &(new_affine_component->BiasParams()),
1967  &(new_affine_component->LinearParams()));
1968  } else if (linear_component != NULL) {
1969  CuVector<BaseFloat> bias_params(linear_component->OutputDim());
1970  AffineComponent *new_affine_component =
1971  new AffineComponent(linear_component->Params(),
1972  bias_params,
1973  linear_component->LearningRate());
1974  PreMultiplyAffineParameters(offset, scale,
1975  &(new_affine_component->BiasParams()),
1976  &(new_affine_component->LinearParams()));
1977  new_component = new_affine_component;
1978  } else if (tdnn_component != NULL) {
1979  new_component = tdnn_component->Copy();
1980  TdnnComponent *new_tdnn_component =
1981  dynamic_cast<TdnnComponent*>(new_component);
1982  if (new_tdnn_component->BiasParams().Dim() == 0) {
1983  // make sure it has a bias even if it had none before.
1984  new_tdnn_component->BiasParams().Resize(
1985  new_tdnn_component->OutputDim());
1986  }
1987  PreMultiplyAffineParameters(offset, scale,
1988  &(new_tdnn_component->BiasParams()),
1989  &(new_tdnn_component->LinearParams()));
1990 
1991  } else {
1992  return -1; // we can't do this: this component isn't of the right type.
1993  }
1994  return nnet_->AddComponent(new_component_name, new_component);
1995  }
int32 AddComponent(const std::string &name, Component *component)
Adds a new component with the given name, which should not be the same as any existing component name...
Definition: nnet-nnet.cc:161
virtual Component * Copy() const =0
Copies component (deep copy).
kaldi::int32 int32
static void PreMultiplyAffineParameters(const CuVectorBase< BaseFloat > &offset, const CuVectorBase< BaseFloat > &scale, CuVectorBase< BaseFloat > *bias_params, CuMatrixBase< BaseFloat > *linear_params)
This helper function, used GetDiagonallyPreModifiedComponentIndex, modifies the linear and bias param...
Definition: nnet-utils.cc:2006
int32 GetComponentIndex(const std::string &node_name) const
returns index associated with this component name, or -1 if no such index.
Definition: nnet-nnet.cc:474
const std::string & GetComponentName(int32 component_index) const
returns individual component name.
Definition: nnet-nnet.cc:689
Component * GetComponent(int32 c)
Return component indexed c. Not a copy; not owned by caller.
Definition: nnet-nnet.cc:150
#define KALDI_ASSERT(cond)
Definition: kaldi-error.h:185

◆ GetScaledComponentIndex()

int32 GetScaledComponentIndex ( int32  component_index,
BaseFloat  scale 
)
inlineprivate

Given a component 'component_index', returns a component which will give the same output as the current component gives when its input is scaled by 'scale'.

This will generally mean applying the scale to the linear parameters in the component, if it is an affine, linear or convolutional component.

If the component referred to in 'component_index' is not an affine or convolutional component, and therefore cannot be scaled (by this code), then this function returns -1.

Definition at line 2050 of file nnet-utils.cc.

References Nnet::AddComponent(), SvdApplier::ModifiedComponentInfo::component_index, Component::Copy(), Nnet::GetComponent(), Nnet::GetComponentIndex(), Nnet::GetComponentName(), KALDI_ASSERT, SvdApplier::nnet_, AffineComponent::Scale(), TdnnComponent::Scale(), and LinearComponent::Scale().

2051  {
2052  if (scale == 1.0)
2053  return component_index;
2054  std::ostringstream os;
2055  os << nnet_->GetComponentName(component_index)
2056  << ".scale" << std::setprecision(3) << scale;
2057  std::string new_component_name = os.str(); // e.g. foo.s2.0
2058  int32 ans = nnet_->GetComponentIndex(new_component_name);
2059  if (ans >= 0)
2060  return ans; // one already exists, no need to create it.
2061  const Component *current_component = nnet_->GetComponent(component_index);
2062  const AffineComponent *affine_component =
2063  dynamic_cast<const AffineComponent*>(current_component);
2064  const TimeHeightConvolutionComponent *conv_component =
2065  dynamic_cast<const TimeHeightConvolutionComponent*>(current_component);
2066  const LinearComponent *linear_component =
2067  dynamic_cast<const LinearComponent*>(current_component);
2068  const TdnnComponent *tdnn_component =
2069  dynamic_cast<const TdnnComponent*>(current_component);
2070 
2071  if (affine_component == NULL && conv_component == NULL &&
2072  linear_component == NULL && tdnn_component == NULL) {
2073  // We can't scale this component (at least, not using this code).
2074  return -1;
2075  }
2076 
2077  Component *new_component = current_component->Copy();
2078 
2079  if (affine_component != NULL) {
2080  // AffineComponent or NaturalGradientAffineComponent.
2081  dynamic_cast<AffineComponent*>(new_component)->
2082  LinearParams().Scale(scale);
2083  } else if (conv_component != NULL) {
2084  dynamic_cast<TimeHeightConvolutionComponent*>(new_component)->
2085  ScaleLinearParams(scale);
2086  } else if (linear_component != NULL) {
2087  dynamic_cast<LinearComponent*>(new_component)->Params().Scale(scale);
2088  } else {
2089  KALDI_ASSERT(tdnn_component != NULL);
2090  dynamic_cast<TdnnComponent*>(new_component)->LinearParams().Scale(scale);
2091  }
2092  return nnet_->AddComponent(new_component_name, new_component);
2093  }
int32 AddComponent(const std::string &name, Component *component)
Adds a new component with the given name, which should not be the same as any existing component name...
Definition: nnet-nnet.cc:161
virtual Component * Copy() const =0
Copies component (deep copy).
kaldi::int32 int32
virtual void Scale(BaseFloat scale)
This virtual function when called on – an UpdatableComponent scales the parameters by "scale" when c...
int32 GetComponentIndex(const std::string &node_name) const
returns index associated with this component name, or -1 if no such index.
Definition: nnet-nnet.cc:474
const std::string & GetComponentName(int32 component_index) const
returns individual component name.
Definition: nnet-nnet.cc:689
Component * GetComponent(int32 c)
Return component indexed c. Not a copy; not owned by caller.
Definition: nnet-nnet.cc:150
#define KALDI_ASSERT(cond)
Definition: kaldi-error.h:185

◆ OptimizeNode()

bool OptimizeNode ( int32  node_index)
inlineprivate

This function modifies the neural network in the case where 'node_index' is a component-input node whose component (in the node at 'node_index + 1), if a bunch of other conditions also apply.

First, he descriptor in the node at 'node_index' has to have a certain limited structure, e.g.:

  • the input-descriptor is a component-node name like 'foo' or:
  • the input-descriptor is a combination of Append and/or and Offset expressions, like: 'Append(Offset(foo, -3), foo, Offset(foo, 3))', referring to only a single node 'foo'.

ALSO the components need to be collapsible by the function CollapseComponents(), which will only be possible for certain pairs of component types (like, say, a dropout node preceding an affine or convolutional node); see that function for details.

This function will (if it does anything), modify the node to replace the component at 'node_index + 1' with a newly created component that combines the two components involved. It will also modify the node at 'node_index' by replacing its Descriptor with a modified input descriptor, so that if the input-descriptor of node 'foo' was 'bar', the descriptor for our node would now look like: 'Append(Offset(bar, -3), bar, Offset(bar, 3))'... and note that 'bar' itself doesn't have to be just a node-name, it can be a more general expression. This function returns true if it changed something in the neural net, and false otherwise.

Definition at line 1638 of file nnet-utils.cc.

References NetworkNode::component_index, SvdApplier::ModifiedComponentInfo::component_index, NetworkNode::descriptor, Nnet::GetNode(), kaldi::nnet3::kComponent, kaldi::nnet3::kDescriptor, SvdApplier::nnet_, NetworkNode::node_type, Nnet::NumNodes(), and NetworkNode::u.

1638  {
1639  NetworkNode &descriptor_node = nnet_->GetNode(node_index);
1640  if (descriptor_node.node_type != kDescriptor ||
1641  node_index + 1 >= nnet_->NumNodes())
1642  return false;
1643  NetworkNode &component_node = nnet_->GetNode(node_index + 1);
1644  if (component_node.node_type != kComponent)
1645  return false;
1646  Descriptor &descriptor = descriptor_node.descriptor;
1647  int32 component_index = component_node.u.component_index;
1648 
1649  int32 input_node_index = DescriptorIsCollapsible(descriptor);
1650  if (input_node_index == -1)
1651  return false; // do nothing, the expression in the Descriptor is too
1652  // general for this code to handle.
1653  const NetworkNode &input_node = nnet_->GetNode(input_node_index);
1654  if (input_node.node_type != kComponent)
1655  return false;
1656  int32 input_component_index = input_node.u.component_index;
1657  int32 combined_component_index = CollapseComponents(input_component_index,
1658  component_index);
1659  if (combined_component_index == -1)
1660  return false; // these components were not of types that can be
1661  // collapsed.
1662  component_node.u.component_index = combined_component_index;
1663 
1664  // 'input_descriptor_node' is the input descriptor of the component
1665  // that's the input to the node in "node_index". (e.g. the component for
1666  // the node "foo" in our example above).
1667  const NetworkNode &input_descriptor_node = nnet_->GetNode(input_node_index - 1);
1668  const Descriptor &input_descriptor = input_descriptor_node.descriptor;
1669 
1670  // The next statement replaces the descriptor in the network node with one
1671  // in which the component 'input_component_index' has been replaced with its
1672  // input, thus bypassing the component in 'input_component_index'.
1673  // We'll later remove that component and its node from the network, if
1674  // needed by RemoveOrphanNodes() and RemoveOrphanComponents().
1675  descriptor = ReplaceNodeInDescriptor(descriptor,
1676  input_node_index,
1677  input_descriptor);
1678  return true;
1679  }
int32 NumNodes() const
Definition: nnet-nnet.h:126
int32 CollapseComponents(int32 component_index1, int32 component_index2)
This function tries to collapse two successive components, where the component &#39;component_index1&#39; app...
Definition: nnet-utils.cc:1493
int32 DescriptorIsCollapsible(const Descriptor &desc)
Definition: nnet-utils.cc:1553
kaldi::int32 int32
const NetworkNode & GetNode(int32 node) const
returns const reference to a particular numbered network node.
Definition: nnet-nnet.h:146
Descriptor ReplaceNodeInDescriptor(const Descriptor &src, int32 node_to_replace, const Descriptor &expr)
Definition: nnet-utils.cc:1572

◆ PreMultiplyAffineParameters()

static void PreMultiplyAffineParameters ( const CuVectorBase< BaseFloat > &  offset,
const CuVectorBase< BaseFloat > &  scale,
CuVectorBase< BaseFloat > *  bias_params,
CuMatrixBase< BaseFloat > *  linear_params 
)
inlinestaticprivate

This helper function, used GetDiagonallyPreModifiedComponentIndex, modifies the linear and bias parameters of an affine transform to capture the effect of preceding that affine transform by a diagonal affine transform with parameters 'offset' and 'scale'.

The dimension of 'offset' and 'scale' must be the same and must divide the input dim of the affine transform, i.e. must divide linear_params->NumCols().

Definition at line 2006 of file nnet-utils.cc.

References CuVectorBase< Real >::AddMatVec(), rnnlm::d, CuVectorBase< Real >::Dim(), KALDI_ASSERT, kaldi::kNoTrans, CuMatrixBase< Real >::MulColsVec(), CuMatrixBase< Real >::NumCols(), and CuMatrixBase< Real >::NumRows().

2010  {
2011  int32 input_dim = linear_params->NumCols(),
2012  transform_dim = offset.Dim();
2013  KALDI_ASSERT(bias_params->Dim() == linear_params->NumRows() &&
2014  offset.Dim() == scale.Dim() &&
2015  input_dim % transform_dim == 0);
2016  // we may have to repeat 'offset' and scale' several times.
2017  // 'full_offset' and 'full_scale' may be repeated versions of
2018  // 'offset' and 'scale' in case input_dim > transform_dim.
2019  CuVector<BaseFloat> full_offset(input_dim),
2020  full_scale(input_dim);
2021  for (int32 d = 0; d < input_dim; d += transform_dim) {
2022  full_offset.Range(d, transform_dim).CopyFromVec(offset);
2023  full_scale.Range(d, transform_dim).CopyFromVec(scale);
2024  }
2025 
2026  // Image the affine component does y = a x + b, and by applying
2027  // the pre-transform we are replacing x with s x + o
2028  // s for scale and o for offset), so we have:
2029  // y = a s x + (b + a o).
2030  // do: b += a o.
2031  bias_params->AddMatVec(1.0, *linear_params, kNoTrans, full_offset, 1.0);
2032  // do: a = a * s.
2033  linear_params->MulColsVec(full_scale);
2034 
2035 
2036  }
kaldi::int32 int32
#define KALDI_ASSERT(cond)
Definition: kaldi-error.h:185

◆ ReplaceNodeInDescriptor()

Descriptor ReplaceNodeInDescriptor ( const Descriptor src,
int32  node_to_replace,
const Descriptor expr 
)
inlineprivate

Definition at line 1572 of file nnet-utils.cc.

References kaldi::nnet3::DescriptorTokenize(), Nnet::GetNodeNames(), KALDI_ASSERT, SvdApplier::nnet_, Descriptor::Parse(), and Descriptor::WriteConfig().

1574  {
1575  // The way we replace it is at the textual level: we create a "fake" vector
1576  // of node-names where the printed form of 'expr' appears as the
1577  // node name in node_names[node_to_replace]; we print the descriptor
1578  // in 'src' using that faked node-names vector; and we parse it again
1579  // using the real node-names vector.
1580  std::vector<std::string> node_names = nnet_->GetNodeNames();
1581  std::ostringstream expr_os;
1582  expr.WriteConfig(expr_os, node_names);
1583  node_names[node_to_replace] = expr_os.str();
1584  std::ostringstream src_replaced_os;
1585  src.WriteConfig(src_replaced_os, node_names);
1586  std::vector<std::string> tokens;
1587  // now, in the example, src_replaced_os.str() would equal
1588  // Append(Offset(Offset(bar, -1), -1), Offset(Offset(bar, -1), 1)).
1589  bool b = DescriptorTokenize(src_replaced_os.str(),
1590  &tokens);
1591  KALDI_ASSERT(b);
1592  // 'tokens' might now contain something like [ "Append", "(", "Offset", ..., ")" ].
1593  tokens.push_back("end of input");
1594  const std::string *next_token = &(tokens[0]);
1595  Descriptor ans;
1596  // parse using the un-modified node names.
1597  ans.Parse(nnet_->GetNodeNames(), &next_token);
1598  KALDI_ASSERT(*next_token == "end of input");
1599  // Note: normalization of expressions in Descriptors, such as conversion of
1600  // Offset(Offset(bar, -1), -1) to Offset(bar, -2), takes place inside the
1601  // Descriptor parsing code.
1602  return ans;
1603  }
bool DescriptorTokenize(const std::string &input, std::vector< std::string > *tokens)
This function tokenizes input when parsing Descriptor configuration values.
Definition: nnet-parse.cc:30
#define KALDI_ASSERT(cond)
Definition: kaldi-error.h:185
const std::vector< std::string > & GetNodeNames() const
returns vector of node names (needed by some parsing code, for instance).
Definition: nnet-nnet.cc:63

◆ SumDescriptorIsCollapsible()

int32 SumDescriptorIsCollapsible ( const SumDescriptor sum_desc)
inlineprivate

Definition at line 1525 of file nnet-utils.cc.

References SimpleForwardingDescriptor::GetNodeDependencies(), OffsetForwardingDescriptor::Src(), and SimpleSumDescriptor::Src().

1525  {
1526  // I don't much like having to use dynamic_cast here.
1527  const SimpleSumDescriptor *ss = dynamic_cast<const SimpleSumDescriptor*>(
1528  &sum_desc);
1529  if (ss == NULL) return -1;
1530  const ForwardingDescriptor *fd = &(ss->Src());
1531  const OffsetForwardingDescriptor *od =
1532  dynamic_cast<const OffsetForwardingDescriptor*>(fd);
1533  if (od != NULL)
1534  fd = &(od->Src());
1535  const SimpleForwardingDescriptor *sd =
1536  dynamic_cast<const SimpleForwardingDescriptor*>(fd);
1537  if (sd == NULL) return -1;
1538  else {
1539  // the following is a rather roundabout way to get the node-index from a
1540  // SimpleForwardingDescriptor, but it works (it avoids adding other stuff
1541  // to the interface).
1542  std::vector<int32> v;
1543  sd->GetNodeDependencies(&v);
1544  int32 node_index = v[0];
1545  return node_index;
1546  }
1547  }
kaldi::int32 int32

Member Data Documentation

◆ config_

const CollapseModelConfig& config_
private

Definition at line 2095 of file nnet-utils.cc.

◆ nnet_

Nnet* nnet_
private

Definition at line 2096 of file nnet-utils.cc.


The documentation for this class was generated from the following file: