適応フィルタ:LMS / NLMSアルゴリズムの理論とPython実装

LMS(最小平均二乗)とNLMS(正規化LMS)アルゴリズムの導出、ステップサイズμの収束条件、誤差曲線の挙動を解説し、システム同定とノイズキャンセレーションのPython実装を示します。

はじめに

LMS(Least Mean Squares)アルゴリズムは、ウィーナーフィルタの最適解を確率的勾配降下法で逐次近似する適応フィルタ手法です。実装が極めて単純(演算量 \(O(L)\) )で安定性も高いため、エコーキャンセラ、ノイズキャンセレーション、チャネル等化、システム同定など幅広い分野で実用されています。

定常確率過程を前提としたウィーナーフィルタは、信号統計(自己相関 \(R_{yy}\) と相互相関 \(R_{sy}\) )が既知という強い仮定を置きます。LMSは統計が未知でも観測サンプルから直接フィルタ係数を更新できる点で、より実用的です。

問題設定

長さ \(L\) の FIR 適応フィルタを考えます。時刻 \(n\) における入力ベクトルとフィルタ係数ベクトルを以下のように定義します。

\[ \mathbf{x}(n) = [x(n), x(n-1), \dots, x(n-L+1)]^\top \tag{1} \] \[ \mathbf{w}(n) = [w_0(n), w_1(n), \dots, w_{L-1}(n)]^\top \tag{2} \]

フィルタ出力と誤差は

\[ y(n) = \mathbf{w}(n)^\top \mathbf{x}(n), \qquad e(n) = d(n) - y(n) \tag{3} \]

ここで \(d(n)\) は所望信号(教師信号)です。目的は、二乗誤差の期待値(コスト関数)

\[ J(\mathbf{w}) = E\bigl[e(n)^2\bigr] \tag{4} \]

を最小化する \(\mathbf{w}\) を逐次的に求めることです。

LMSアルゴリズム

勾配の確率的近似

コスト関数 \(J(\mathbf{w})\) の勾配は

\[ \nabla J = -2\, E\bigl[e(n)\, \mathbf{x}(n)\bigr] \tag{5} \]

ですが、期待値は実際には計算できません。LMSの核心は、期待値を瞬時値で置き換える確率的勾配近似です。

\[ \hat{\nabla} J(n) = -2\, e(n)\, \mathbf{x}(n) \tag{6} \]

これを最急降下法に代入して、係数更新則が得られます。

\[ \mathbf{w}(n+1) = \mathbf{w}(n) + \mu\, e(n)\, \mathbf{x}(n) \tag{7} \]

ここで \(\mu > 0\) はステップサイズ(学習率)です。係数 \(2\) は \(\mu\) に吸収しています。

収束条件

平均的な収束(係数の期待値が最適解 \(\mathbf{w}^\ast\) に収束)のためには、ステップサイズが入力相関行列 \(\mathbf{R} = E[\mathbf{x}(n)\mathbf{x}(n)^\top]\) の最大固有値 \(\lambda_{\max}\) に対して

\[ 0 < \mu < \frac{2}{\lambda_{\max}} \tag{8} \]

を満たす必要があります。実用上は、入力パワーを使ったより緩い条件

\[ 0 < \mu < \frac{2}{L \cdot P_x}, \qquad P_x = E\bigl[x(n)^2\bigr] \tag{9} \]

を採用することが多く、\(\lambda_{\max} \le \mathrm{tr}(\mathbf{R}) = L P_x\) から導かれます。

ステップサイズと挙動の関係は次のとおりです。

\(\mu\) の大きさ挙動
小さい収束が遅いが定常誤差が小さい
中程度収束速度と定常誤差のバランスが良い
大きい収束は速いが定常誤差が大きい
上限超過発散(誤差が増大し続ける)

自己相関 \(\mathbf{R}\) の固有値広がり \(\chi(\mathbf{R}) = \lambda_{\max} / \lambda_{\min}\) が大きいほど収束が遅くなります。詳細は自己相関関数を参照してください。

NLMSアルゴリズム

正規化の動機

LMSの収束はステップサイズ \(\mu\) と入力パワー \(P_x\) の積に依存します。入力レベルが大きく変動する環境(音声信号など)では、\(\mu\) を固定値にすると過小・過大の両方が起き、安定性が損なわれます。

NLMS(Normalized LMS)は、入力ベクトルのノルムでステップサイズを正規化することでこの問題を解決します。

\[ \mathbf{w}(n+1) = \mathbf{w}(n) + \frac{\tilde{\mu}}{\lVert \mathbf{x}(n) \rVert^2 + \varepsilon}\, e(n)\, \mathbf{x}(n) \tag{10} \]

\(\tilde{\mu} \in (0, 2)\) が正規化されたステップサイズで、入力パワーに依存せず収束条件を満たします。\(\varepsilon\) は分母が小さくなりすぎないようにするための小さい正の定数です。

LMSとの比較

特性LMSNLMS
計算量\(O(L)\)\(O(L)\)
ステップサイズ\(\mu < 2/(L P_x)\) (入力依存)\(\tilde{\mu} < 2\) (入力非依存)
入力変動への頑健性弱い強い
実装の手間最小LMS に正規化項を追加

Python実装

システム同定の例

未知のFIRシステム \(\mathbf{w}^\ast\) を、白色雑音 \(x(n)\) を入力として LMS / NLMS で同定します。誤差 \(e(n)\) の二乗を学習曲線としてプロットします。

import numpy as np
import matplotlib.pyplot as plt

np.random.seed(0)

# --- 真のシステム(同定対象) ---
L = 16
w_true = np.array([0.5, -0.3, 0.2, 0.1, -0.05, 0.02,
                   0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
                   0.0, 0.0, 0.0, 0.0])

# --- 入力・観測信号 ---
N = 5000
x = np.random.randn(N)
v = 0.05 * np.random.randn(N)        # 観測ノイズ
d = np.convolve(x, w_true, mode='full')[:N] + v


def lms(x, d, L, mu):
    """標準LMSアルゴリズム"""
    N = len(x)
    w = np.zeros(L)
    e = np.zeros(N)
    for n in range(L, N):
        x_vec = x[n:n - L:-1] if n - L >= 0 else x[n::-1]
        y = w @ x_vec
        e[n] = d[n] - y
        w = w + mu * e[n] * x_vec
    return w, e


def nlms(x, d, L, mu_tilde, eps=1e-6):
    """正規化LMSアルゴリズム"""
    N = len(x)
    w = np.zeros(L)
    e = np.zeros(N)
    for n in range(L, N):
        x_vec = x[n:n - L:-1]
        y = w @ x_vec
        e[n] = d[n] - y
        norm = x_vec @ x_vec + eps
        w = w + (mu_tilde / norm) * e[n] * x_vec
    return w, e


# --- 実行 ---
mu_lms = 0.01
mu_nlms = 0.5
w_lms, e_lms = lms(x, d, L, mu_lms)
w_nlms, e_nlms = nlms(x, d, L, mu_nlms)


# --- 学習曲線(瞬時誤差の二乗を移動平均) ---
def smooth(v, win=50):
    kernel = np.ones(win) / win
    return np.convolve(v ** 2, kernel, mode='same')


plt.figure(figsize=(10, 5))
plt.semilogy(smooth(e_lms), label=f'LMS (μ={mu_lms})')
plt.semilogy(smooth(e_nlms), label=f'NLMS (μ̃={mu_nlms})')
plt.xlabel('Iteration n')
plt.ylabel('Mean Squared Error')
plt.title('Learning Curves: LMS vs NLMS')
plt.legend()
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()

print(f"LMS   final coeff error: {np.linalg.norm(w_lms - w_true):.4f}")
print(f"NLMS  final coeff error: {np.linalg.norm(w_nlms - w_true):.4f}")

学習曲線(縦軸が対数)を見ると、最初は誤差が指数的に減少し、やがて定常誤差レベルで水平になります。\(\mu\) (または \(\tilde{\mu}\) )を大きくすると初期収束は速くなりますが、定常誤差レベルも上昇する典型的なトレードオフが観察できます。

ステップサイズの感度解析

plt.figure(figsize=(10, 5))
for mu in [0.005, 0.02, 0.05, 0.1]:
    _, e = lms(x, d, L, mu)
    plt.semilogy(smooth(e), label=f'μ={mu}', alpha=0.8)
plt.xlabel('Iteration n')
plt.ylabel('Mean Squared Error')
plt.title('LMS: Sensitivity to Step Size μ')
plt.legend()
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()

\(\mu\) が小さすぎると収束が極めて遅く、\(\mu\) が上限近くでは初期から振動的になります。実用上は (8) 式の半分以下を目安に選ぶと安定です。

ウィーナーフィルタとの関係

LMSは、ウィーナー解 \(\mathbf{w}^\ast = \mathbf{R}^{-1}\mathbf{p}\) (\(\mathbf{p} = E[d(n)\mathbf{x}(n)]\) )に逐次的に収束します。

\[ E\bigl[\mathbf{w}(n)\bigr] \xrightarrow{n \to \infty} \mathbf{w}^\ast \tag{11} \]

ただし、瞬時勾配を使うため有限の過剰平均二乗誤差(excess MSE)が残ります。RLS(再帰的最小二乗)はこれを高速化する代替手法です。

用途別ガイド

用途推奨理由
入力レベルが安定LMS実装最小、計算量も最少
音声・通信信号など変動大NLMS入力パワー変動に頑健
速い収束が必須RLS二次収束、ただし \(O(L^2)\)
組み込み・低リソースLMS / NLMS\(O(L)\) で十分

関連記事

参考文献

  • Haykin, S. (2014). Adaptive Filter Theory (5th ed.). Pearson. Chapters 5-6.
  • Widrow, B., & Stearns, S. D. (1985). Adaptive Signal Processing. Prentice Hall.
  • Sayed, A. H. (2008). Adaptive Filters. Wiley-IEEE Press.