ssspy.bss.mnmf#
Algorithms#
- class ssspy.bss.mnmf.FastMNMFBase(n_basis, n_sources=None, partitioning=False, flooring_fn=functools.partial(<function max_flooring>, eps=1e-10), callbacks=None, normalization=True, record_loss=True, reference_id=0, rng=None)#
Base class of fast multichannel nonnegative matrix factorization (Fast MNMF).
- Parameters:
n_basis (int) – Number of NMF bases.
n_sources (int, optional) – Number of sources to be separated. If
None
is given,n_sources
is determined by number of channels in input spectrogram. Default:None
.partitioning (bool) – Whether to use partioning function. Default:
False
.flooring_fn (callable, optional) – A flooring function for numerical stability. This function is expected to return the same shape tensor as the input. If you explicitly set
flooring_fn=None
, the identity function (lambda x: x
) is used. Default:functools.partial(max_flooring, eps=1e-10)
.normalization (bool or str) – Normalization of diagonalizers and diagonal elements of spatial covariance matrices. Only power-based normalization is supported. Default:
True
.callbacks (callable or list[callable], optional) – Callback functions. Each function is called before separation and at each iteration. Default:
None
.record_loss (bool) – Record the loss at each iteration of the update algorithm if
record_loss=True
. Default:True
.reference_id (int) – Reference channel in multichannel Wiener filter. Default:
0
.rng (numpy.random.Generator, optioinal) – Random number generator. This is mainly used to randomly initialize PSDTF. If
None
is given,np.random.default_rng()
is used. Default:None
.
- __call__(input, n_iter=100, initial_call=True, **kwargs)#
Separate a frequency-domain multichannel signal.
- Parameters:
input (numpy.ndarray) – The mixture signal in frequency-domain. The shape is (n_channels, n_bins, n_frames).
n_iter (int) – The number of iterations of demixing filter updates. Default:
100
.initial_call (bool) – If
True
, perform callbacks (and computation of loss if necessary) before iterations.
- Return type:
ndarray
- Returns:
numpy.ndarray of the separated signal in frequency-domain. The shape is (n_channels, n_bins, n_frames).
- normalize(flooring_fn='self')#
Normalize diagonalizers and diagonal elements of spatial covariance matrices.
- Parameters:
flooring_fn (callable or str, optional) – A flooring function for numerical stability. This function is expected to return the same shape tensor as the input. If you explicitly set
flooring_fn=None
, the identity function (lambda x: x
) is used. Ifself
is given as str,self.flooring_fn
is used. Default:self
.- Return type:
None
- normalize_by_power(flooring_fn='self')#
Normalize diagonalizers and diagonal elements of spatial covariance matrices by power.
- Parameters:
flooring_fn (callable or str, optional) – A flooring function for numerical stability. This function is expected to return the same shape tensor as the input. If you explicitly set
flooring_fn=None
, the identity function (lambda x: x
) is used. Ifself
is given as str,self.flooring_fn
is used. Default:self
.- Return type:
None
Diagonalizers are normalized by
\[\boldsymbol{q}_{im} \leftarrow\frac{\boldsymbol{q}_{im}}{\psi_{im}},\]where
\[\psi_{im} = \sqrt{\frac{1}{IJ}\sum_{i,j}|\boldsymbol{q}_{im}^{\mathsf{H}} \boldsymbol{x}_{ij}|^{2}}.\]For diagonal elements of spatial covariance matrices,
\[d_{inm} \leftarrow\frac{d_{inm}}{\psi_{im}^{2}}.\]
- class ssspy.bss.mnmf.FastGaussMNMF(n_basis, n_sources=None, diagonalizer_algorithm='IP', partitioning=False, flooring_fn=functools.partial(<function max_flooring>, eps=1e-10), pair_selector=None, callbacks=None, normalization=True, record_loss=True, reference_id=0, rng=None)#
Fast multichannel nonnegative matrix factorization on Gaussian distribution (Fast Gauss-MNMF).
- Parameters:
n_basis (int) – Number of NMF bases.
n_sources (int, optional) – Number of sources to be separated. If
None
is given,n_sources
is determined by number of channels in input spectrogram. Default:None
.diagonalizer_algorithm (str) – Algorithm for diagonalizers. Choose
IP
,IP1
, orIP2
. Default:IP
.partitioning (bool) – Whether to use partioning function. Default:
False
.flooring_fn (callable, optional) – A flooring function for numerical stability. This function is expected to return the same shape tensor as the input. If you explicitly set
flooring_fn=None
, the identity function (lambda x: x
) is used. Default:functools.partial(max_flooring, eps=1e-10)
.callbacks (callable or list[callable], optional) – Callback functions. Each function is called before separation and at each iteration. Default:
None
.record_loss (bool) – Record the loss at each iteration of the update algorithm if
record_loss=True
. Default:True
.reference_id (int) – Reference channel in multichannel Wiener filter. Default:
0
.rng (numpy.random.Generator, optioinal) – Random number generator. This is mainly used to randomly initialize PSDTF. If
None
is given,np.random.default_rng()
is used. Default:None
.
- __call__(input, n_iter=100, initial_call=True, **kwargs)#
Separate a frequency-domain multichannel signal.
- Parameters:
input (numpy.ndarray) – The mixture signal in frequency-domain. The shape is (n_channels, n_bins, n_frames).
n_iter (int) – The number of iterations of demixing filter updates. Default:
100
.initial_call (bool) – If
True
, perform callbacks (and computation of loss if necessary) before iterations.
- Return type:
ndarray
- Returns:
numpy.ndarray of the separated signal in frequency-domain. The shape is (n_channels, n_bins, n_frames).
- compute_logdet(diagonalizer)#
Compute log-determinant.
- Parameters:
reconstructed – Diagonalizer with shape of (*, n_channels, n_channels).
- Return type:
ndarray
- Returns:
numpy.ndarray of computed log-determinant values. The shape is (*).
- compute_loss()#
Compute loss \(\mathcal{L}\).
\(\mathcal{L}\) is defined as follows:
\[\begin{split}\mathcal{L} &:=-\frac{1}{J}\sum_{i,j}\left\{ \mathrm{tr}\left( \boldsymbol{x}_{ij}\boldsymbol{x}_{ij}^{\mathsf{H}}\boldsymbol{R}_{ij}^{-1} \right) - \log\det\boldsymbol{R}_{ij} \right\} \\ &:=\frac{1}{J}\sum_{i,j,m}\left\{ \frac{|\boldsymbol{q}_{im}^{\mathsf{H}}\boldsymbol{x}_{ij}|^{2}} {\sum_{n}\lambda_{ijn}d_{inm}} + \log\sum_{n}\lambda_{ijn}d_{inm}\right\} - 2\sum_{i}\log|\det\boldsymbol{Q}_{i}|.\end{split}\]- Return type:
float
- Returns:
Computed loss.
- separate(input)#
Separate
input
using multichannel Wiener filter.- Parameters:
input (numpy.ndarray) – The mixture signal in frequency-domain. The shape is (n_channels, n_bins, n_frames).
- Return type:
ndarray
- Returns:
numpy.ndarray of the separated signal in frequency-domain. The shape is (n_sources, n_bins, n_frames).
- update_activation(flooring_fn='self')#
Update NMF activations by MM algorithm.
- Parameters:
flooring_fn (callable or str, optional) – A flooring function for numerical stability. This function is expected to return the same shape tensor as the input. If you explicitly set
flooring_fn=None
, the identity function (lambda x: x
) is used. Ifself
is given as str,self.flooring_fn
is used. Default:self
.- Return type:
None
Update \(v_{kjn}\) as follows:
\[v_{kjn} \leftarrow\left[ \frac{\displaystyle\sum_{i,m}\frac{|\boldsymbol{q}_{im}^{\mathsf{H}}\boldsymbol{x}_{ij}|^{2}d_{inm}t_{ikn}} {\left(\sum_{k',n'}t_{ik'n'}v_{k'jn'}d_{in'm}\right)^{2}}} {\displaystyle\sum_{i,m}\dfrac{d_{inm}t_{ikn}}{\sum_{k',n'}t_{ik'n'}v_{k'jn'}d_{in'm}}} \right]^{\frac{1}{2}}v_{kjn}.\]
- update_basis(flooring_fn='self')#
Update NMF bases by MM algorithm.
- Parameters:
flooring_fn (callable or str, optional) – A flooring function for numerical stability. This function is expected to return the same shape tensor as the input. If you explicitly set
flooring_fn=None
, the identity function (lambda x: x
) is used. Ifself
is given as str,self.flooring_fn
is used. Default:self
.- Return type:
None
Update \(t_{ikn}\) as follows:
\[t_{ikn} \leftarrow\left[ \frac{\displaystyle\sum_{j,m}\frac{|\boldsymbol{q}_{im}^{\mathsf{H}}\boldsymbol{x}_{ij}|^{2}d_{inm}v_{kjn}} {\left(\sum_{k',n'}t_{ik'n'}v_{k'jn'}d_{in'm}\right)^{2}}} {\displaystyle\sum_{j,m}\dfrac{d_{inm}v_{kjn}}{\sum_{k',n'}t_{ik'n'}v_{k'jn'}d_{in'm}}} \right]^{\frac{1}{2}}t_{ikn}.\]
- update_diagonalizer(flooring_fn='self')#
Update diagonalizer.
If
diagonalizer_algorithm
isIP
orIP1
,update_diagonalizer_model_ip1
is called.If
diagonalizer_algorithm
isIP2
,update_diagonalizer_model_ip2
is called.
- Parameters:
flooring_fn (callable or str, optional) – A flooring function for numerical stability. This function is expected to return the same shape tensor as the input. If you explicitly set
flooring_fn=None
, the identity function (lambda x: x
) is used. Ifself
is given as str,self.flooring_fn
is used. Default:self
.- Return type:
None
- update_diagonalizer_ip1(flooring_fn='self')#
Update diagonalizer once using iterative projection.
- Parameters:
flooring_fn (callable or str, optional) – A flooring function for numerical stability. This function is expected to return the same shape tensor as the input. If you explicitly set
flooring_fn=None
, the identity function (lambda x: x
) is used. Ifself
is given as str,self.flooring_fn
is used. Default:self
.- Return type:
None
Diagonalizers are updated sequentially for \(m=1,\ldots,M\) as follows:
\[\begin{split}\boldsymbol{q}_{im} &\leftarrow\left(\boldsymbol{Q}_{im}^{\mathsf{H}}\boldsymbol{U}_{im}\right)^{-1} \ \boldsymbol{e}_{m}, \\ \boldsymbol{q}_{im} &\leftarrow\frac{\boldsymbol{q}_{im}} {\sqrt{\boldsymbol{q}_{im}^{\mathsf{H}}\boldsymbol{U}_{im}\boldsymbol{q}_{im}}},\end{split}\]where
\[\boldsymbol{U}_{im} = \frac{1}{J}\sum_{j} \frac{\boldsymbol{x}_{ij}\boldsymbol{x}_{ij}^{\mathsf{H}}} {\sum_{n}\left(\sum_{k}z_{nk}t_{ik}v_{kj}\right)d_{inm}}\]if
partitioning=True
, otherwise\[\boldsymbol{U}_{im} = \frac{1}{J}\sum_{j} \frac{\boldsymbol{x}_{ij}\boldsymbol{x}_{ij}^{\mathsf{H}}} {\sum_{n}\left(\sum_{k}t_{ikn}v_{kjn}\right)d_{inm}}.\]
- update_diagonalizer_ip2(flooring_fn='self')#
Update diagonalizer once using pairwise iterative projection.
- Parameters:
flooring_fn (callable or str, optional) – A flooring function for numerical stability. This function is expected to return the same shape tensor as the input. If you explicitly set
flooring_fn=None
, the identity function (lambda x: x
) is used. Ifself
is given as str,self.flooring_fn
is used. Default:self
.- Return type:
None
For \(m_{1}\) and \(m_{2}\) (\(m_{1}\neq m_{2}\)), compute weighted covariance matrix as follows:
\[\boldsymbol{U}_{im} = \frac{1}{J}\sum_{j} \frac{\boldsymbol{x}_{ij}\boldsymbol{x}_{ij}^{\mathsf{H}}}{\sum_{n}\lambda_{ijn}d_{inm}},\]\(\lambda_{ijn}\) is computed by
\[\lambda_{ijn}=\sum_{k}z_{nk}t_{ik}v_{kj}\]if
partitioning=True
. Otherwise,\[\lambda_{ijn}=\sum_{k}t_{ikn}v_{kjn}.\]Using \(\boldsymbol{U}_{im_{1}}\) and \(\boldsymbol{U}_{im_{2}}\), we compute generalized eigenvectors.
\[\left({\boldsymbol{P}_{im_{1}}^{(m_{1},m_{2})}}^{\mathsf{H}}\boldsymbol{U}_{im_{1}} \boldsymbol{P}_{im_{1}}^{(m_{1},m_{2})}\right)\boldsymbol{h}_{i} = \mu_{i} \left({\boldsymbol{P}_{im_{2}}^{(m_{1},m_{2})}}^{\mathsf{H}}\boldsymbol{U}_{im_{2}} \boldsymbol{P}_{im_{2}}^{(m_{1},m_{2})}\right)\boldsymbol{h}_{i},\]where
\[\begin{split}\boldsymbol{P}_{im_{1}}^{(m_{1},m_{2})} &= (\boldsymbol{Q}_{i}\boldsymbol{U}_{im_{1}})^{-1} ( \begin{array}{cc} \boldsymbol{e}_{m_{1}} & \boldsymbol{e}_{m_{2}} \end{array} ), \\ \boldsymbol{P}_{im_{2}}^{(m_{1},m_{2})} &= (\boldsymbol{Q}_{i}\boldsymbol{U}_{im_{2}})^{-1} ( \begin{array}{cc} \boldsymbol{e}_{m_{1}} & \boldsymbol{e}_{m_{2}} \end{array} ).\end{split}\]After that, we standardize two eigenvectors \(\boldsymbol{h}_{im_{1}}\) and \(\boldsymbol{h}_{im_{2}}\).
\[\begin{split}\boldsymbol{h}_{im_{1}} &\leftarrow\frac{\boldsymbol{h}_{im_{1}}} {\sqrt{\boldsymbol{h}_{im_{1}}^{\mathsf{H}} \left({\boldsymbol{P}_{im_{1}}^{(m_{1},m_{2})}}^{\mathsf{H}}\boldsymbol{U}_{im_{1}} \boldsymbol{P}_{im_{1}}^{(m_{1},m_{2})}\right) \boldsymbol{h}_{im_{1}}}}, \\ \boldsymbol{h}_{im_{2}} &\leftarrow\frac{\boldsymbol{h}_{im_{2}}} {\sqrt{\boldsymbol{h}_{im_{2}}^{\mathsf{H}} \left({\boldsymbol{P}_{im_{2}}^{(m_{1},m_{2})}}^{\mathsf{H}}\boldsymbol{U}_{im_{2}} \boldsymbol{P}_{im_{2}}^{(m_{1},m_{2})}\right) \boldsymbol{h}_{im_{2}}}}.\end{split}\]Then, update \(\boldsymbol{q}_{im_{1}}\) and \(\boldsymbol{q}_{im_{2}}\) simultaneously.
\[\begin{split}\boldsymbol{q}_{im_{1}} &\leftarrow \boldsymbol{P}_{im_{1}}^{(m_{1},m_{2})}\boldsymbol{h}_{im_{1}} \\ \boldsymbol{q}_{im_{2}} &\leftarrow \boldsymbol{P}_{im_{2}}^{(m_{1},m_{2})}\boldsymbol{h}_{im_{2}}\end{split}\]At each iteration, we update pairs of \(m_{1}\) and \(m_{2}\) for \(m_{1}\neq m_{2}\).
- update_once(flooring_fn='self')#
Update MNMF parameters, diagonalizers, and diagonal elements of spatial covariance matrices once.
- Parameters:
flooring_fn (callable or str, optional) – A flooring function for numerical stability. This function is expected to return the same shape tensor as the input. If you explicitly set
flooring_fn=None
, the identity function (lambda x: x
) is used. Ifself
is given as str,self.flooring_fn
is used. Default:self
.- Return type:
None
- update_spatial()#
Update diagonal elements of spatial covariance matrix by MM algorithm.
Update \(d_{inm}\) as follows: :rtype:
None
\[d_{inm}\leftarrow\left[ \dfrac{\displaystyle\sum_{j}\frac{\lambda_{ijn}|\boldsymbol{q}_{im}^{\mathsf{H}}\boldsymbol{x}_{ij}|^{2}} {\left(\sum_{n'}\lambda_{ijn'}d_{in'm}\right)^{2}}} {\displaystyle\sum_{j}\frac{\lambda_{ijn}}{\sum_{n'}\lambda_{ijn'}d_{in'm}}} \right]^{\frac{1}{2}}d_{inm}.\]