行列の幾何平均計算の実装
正定値Hermite行列に対する幾何平均をNumPyで実装
ブラインド音源分離手法の多チャネル非負値行列因子分解(MNMF)や独立半正定値テンソル分析(IPSDTA)には,半正定値Hermite行列に対する幾何平均の計算が登場します.Pythonの数値計算ライブラリのNumPyでこれらの手法を実装してみました.その際の工夫について共有します.
正定値Hermite行列に対する幾何平均の定義
2つの正定値Hermite行列
この式の解は,次のように定義される行列の幾何平均で与えられます.
式(1)の両辺の逆行列を計算し整理すると,次のように変形も可能です.
つまり,
NumPyで正定値Hermite行列の幾何平均を実装する際の問題点
式(2)〜(4)からわかるように,行列の幾何平均を計算する際には,行列の平方根の実装が必要になります.
NumPyのnumpy.ndarray
で行列の平方根を計算する方法の一つは,scipy.linalg.sqrtm
を利用することです.この関数を使うと,平方根の計算の実装が不要で便利な一方,並列処理ができない欠点があります[1].応用先のMNMFやIPSDTAでは,周波数ごとに幾何平均を計算するため,scipy.linalg.sqrtm
をforループで回す必要があり,計算時間のボトルネックとなります.また,この関数は一般の行列の平方根を計算するために用意されており,正定値Hermite行列の性質を使って計算していません.そこで,並列処理が可能で,正定値Hermite行列の性質を使った効率的な幾何平均の実装をNumPyでできないか考えてみました.
正定値Hermite行列に対する平方根の実装
幾何平均の具体的な実装に入る前に,式(2)に登場する正定値Hermite行列に対する平方根の計算とその実装を確認します.
正定値Hermite行列
で定義されます.ただし,
正定値Hermite行列に対する固有値と固有ベクトルは,numpy.linalg.eigh
で計算可能です.この関数は,並列処理が可能な利点があります.また,一般の正方行列に対する固有値と固有ベクトルを計算するnumpy.linalg.eig
とは異なり,正定値Hermite行列の性質を利用した効率的な計算が可能です.
まとめると,正定値Hermite行列に対する平方根は,次のように実装できます.
import numpy as np
def make_matrix(K: int, N: int) -> np.ndarray:
"""Make Hermitian positive definite matrix.
Args:
K (int): Number of matrices.
N (int): Size of matrix.
Returns:
np.ndarray: Hermitian positive definite matrix of shape (K, N, N).
"""
vec = np.random.randn(K, 1, N, 2 * N) + 1j * np.random.randn(K, 1, N, 2 * N)
cov = vec * vec.transpose(0, 2, 1, 3).conj()
return np.mean(cov, axis=-1)
def sqrt_hermite(X: np.ndarray) -> np.ndarray:
"""Square root of Hermitian positive definite matrix.
Args:
X (np.ndarray): Hermitian positive definite matrix of shape (K, N, N).
Returns:
np.ndarray: Square root of X of shape (K, N, N).
"""
lamb, Z = np.linalg.eigh(X)
Z_hermite = Z.swapaxes(-2, -1)
Z_hermite = Z_hermite.conj()
Lamb = np.sqrt(lamb)[..., np.newaxis] * np.eye(lamb.shape[-1])
return Z @ Lamb @ Z_hermite
正定値Hermite行列に対する幾何平均の実装(その1)
正定値Hermite行列に対する平方根の実装を踏まえると,式(2)の定義で幾何平均を計算する場合は,numpy.linalg.eigh
を使用して次のように実装できます.
def geo_mean_hermite_type1(A: np.ndarray, B: np.ndarray) -> np.ndarray:
"""Geometric mean of Hermitian positive definite matrices.
Args:
A (np.ndarray): Hermitian positive definite matrix of shape (K, N, N).
B (np.ndarray): Hermitian positive definite matrix of shape (K, N, N).
Returns:
np.ndarray: Geometric mean of A and B of shape (K, N, N).
"""
A_sqrt = sqrt_hermite(A)
A_sqrt_inv = np.linalg.inv(A_sqrt)
ABA = sqrt_hermite(A_sqrt_inv @ B @ A_sqrt_inv)
return A_sqrt @ ABA @ A_sqrt
一般化固有値分解
式(3)と(4)の定義で幾何平均を計算する場合,numpy.linalg.eig
を用いて,次の固有値分解を解く必要があります.
この固有値問題が計算できれば,次のように
この方法で実装する場合,numpy.linalg.eig
を用いた並列処理が可能ですが,正定値Hermite行列の性質を利用できていません.そこで,式(5)の両辺に
行列
半正定値Hermite行列のための一般化固有値分解
式(6)を解くために,
NumPyではnumpy.linalg.cholesky
で
ただし,numpy.linalg.eigh
を用いて固有値と固有ベクトルを計算できます.一般化固有値問題(6)に対する固有値と固有ベクトルは,求めた
で得られます.
以上を踏まえると,正定値Hermite行列のための一般化固有値分解は,次のように実装できます.
def gen_eig_hermite(A: np.ndarray, B: np.ndarray) -> tuple[np.ndarray, np.ndarray]:
"""Generalized eigenvalue decomposition for Hermitian positive definite matrices.
Args:
A (np.ndarray): Eigenvalues of Hermitian positive definite matrix of shape (K, N, N).
B (np.ndarray): Eigenvectors of Hermitian positive definite matrix of shape (K, N, N).
Returns:
np.ndarray: Geometric mean of A and B of shape (K, N, N).
"""
L = np.linalg.cholesky(A)
L_inv = np.linalg.inv(L)
L_inv_hermite = np.swapaxes(L_inv, -2, -1)
L_inv_hermite = L_inv_hermite.conj()
C = L_inv @ B @ L_inv_hermite
lamb, Y = np.linalg.eigh(C)
Z = L_inv_hermite @ Y
return lamb, Z
正定値Hermite行列に対する幾何平均の実装(その2)
これまでの説明をまとめると式(3)に基づく行列の幾何平均は
と整理できます.
def geo_mean_hermite_type2(A: np.ndarray, B: np.ndarray) -> np.ndarray:
"""Geometric mean of Hermitian positive definite matrices.
Args:
A (np.ndarray): Hermitian positive definite matrix of shape (K, N, N).
B (np.ndarray): Hermitian positive definite matrix of shape (K, N, N).
Returns:
np.ndarray: Geometric mean of A and B of shape (K, N, N).
"""
lamb, Z = gen_eig_hermite(A, B)
lamb = np.sqrt(lamb)
Lamb = lamb[..., np.newaxis] * np.eye(Z.shape[-1])
AB = Z @ Lamb @ np.linalg.inv(Z)
return A @ AB
まとめ
MNMFやIPSDTAのための正定値Hermite行列の計算の実装方法を説明しました.今回紹介した方法は,並列処理が可能であり,正定値Hermite行列の性質を利用した効率的な実装を利用しています.
-
各行列を1次元に変形して,
numpy.apply_along_axis
を利用すれば,擬似的に並列計算は可能です. ↩︎
Discussion