適応フィルタとは
適応フィルタ(Adaptive Filter)は、入力信号の統計的性質が時間とともに変化する環境に対応するため、フィルタ係数を自動的に更新するフィルタです。EMAフィルタやバターワースフィルタのような固定パラメータのフィルタとは異なり、未知の環境やダイナミックに変化する信号に対して最適な性能を発揮します。
主な応用分野
- ノイズキャンセレーション: マイクで拾った環境ノイズを除去(ANCヘッドホン等)
- エコーキャンセレーション: 電話やビデオ会議のエコー除去
- システム同定: 未知のシステムの伝達関数を推定
- チャネル等化: 通信における伝送路歪みの補正
ウィーナーフィルタとの関係
適応フィルタの理論的基盤はウィーナーフィルタです。ウィーナーフィルタは、入力信号 \(\mathbf{x}(n)\) から目的信号 \(d(n)\) を推定する際の平均二乗誤差(MSE)を最小化する最適フィルタです。
フィルタ出力を \(y(n) = \mathbf{w}^T \mathbf{x}(n)\)、誤差を \(e(n) = d(n) - y(n)\) とすると、MSEは:
\[ J(\mathbf{w}) = E[e^2(n)] = E[(d(n) - \mathbf{w}^T \mathbf{x}(n))^2] \tag{1} \]これを最小化する最適解(ウィーナー・ホフ解)は:
\[ \mathbf{w}^* = \mathbf{R}^{-1} \mathbf{p} \tag{2} \]ここで、\(\mathbf{R} = E[\mathbf{x}(n)\mathbf{x}^T(n)]\) は入力の自己相関行列、\(\mathbf{p} = E[\mathbf{x}(n)d(n)]\) は相互相関ベクトルです。
実際には \(\mathbf{R}\) と \(\mathbf{p}\) が未知であるか、時間変化するため、適応アルゴリズムでウィーナー解に逐次的に近づけます。
LMS(Least Mean Squares)アルゴリズム
LMSアルゴリズムは、MSEの勾配を瞬時推定値で近似する確率的勾配降下法です。SGDと同じ原理に基づいています。
導出
MSE \(J(\mathbf{w})\) の勾配は:
\[ \nabla J(\mathbf{w}) = -2E[\mathbf{x}(n)e(n)] \tag{3} \]LMSでは期待値を瞬時値で置き換え、勾配の推定値を得ます:
\[ \hat{\nabla} J(\mathbf{w}) = -2\mathbf{x}(n)e(n) \tag{4} \]これを用いた更新則:
\[ \mathbf{w}(n+1) = \mathbf{w}(n) + \mu \mathbf{x}(n) e(n) \tag{5} \]ここで \(\mu\) はステップサイズ(学習率)です。
収束条件
LMSが収束するためには、ステップサイズ \(\mu\) が以下を満たす必要があります:
\[ 0 < \mu < \frac{2}{\lambda_{\max}} \tag{6} \]\(\lambda_{\max}\) は自己相関行列 \(\mathbf{R}\) の最大固有値です。実用的には:
\[ 0 < \mu < \frac{2}{\text{tr}(\mathbf{R})} \approx \frac{2}{M \cdot \sigma_x^2} \tag{7} \]\(M\) はフィルタ次数、\(\sigma_x^2\) は入力信号のパワーです。
NLMS(Normalized LMS)アルゴリズム
LMSの問題点は、ステップサイズが入力信号のパワーに依存することです。NLMSは入力ベクトルのノルムで正規化することでこの問題を解決します:
\[ \mathbf{w}(n+1) = \mathbf{w}(n) + \frac{\mu}{\|\mathbf{x}(n)\|^2 + \epsilon} \mathbf{x}(n) e(n) \tag{8} \]\(\epsilon\) はゼロ除算防止の小さな正の定数です。NLMSでは \(0 < \mu < 2\) で収束が保証されます。
RLS(Recursive Least Squares)アルゴリズム
RLSは、過去の全データに対する重み付き最小二乗誤差を最小化するアルゴリズムです。LMSより高速に収束しますが、計算コストが高くなります。
コスト関数
\[ J(\mathbf{w}, n) = \sum_{i=1}^{n} \lambda^{n-i} |e(i)|^2 \tag{9} \]\(\lambda\)(\(0 < \lambda \le 1\))は忘却係数で、古いデータの影響を指数的に減衰させます。
更新アルゴリズム
- ゲインベクトルの計算:
- 誤差の計算:
- フィルタ係数の更新:
- 逆相関行列の更新:
初期値は \(\mathbf{P}(0) = \delta^{-1}\mathbf{I}\)(\(\delta\) は小さな正の定数)とします。
アルゴリズムの比較
| 特性 | LMS | NLMS | RLS |
|---|---|---|---|
| 計算量(1ステップ) | \(O(M)\) | \(O(M)\) | \(O(M^2)\) |
| メモリ | \(O(M)\) | \(O(M)\) | \(O(M^2)\) |
| 収束速度 | 遅い | 中程度 | 速い |
| 定常誤差 | ステップサイズ依存 | ステップサイズ依存 | 小さい |
| 数値安定性 | 高い | 高い | やや低い |
| 調整パラメータ | \(\mu\) | \(\mu\) | \(\lambda\), \(\delta\) |
| 適用場面 | リアルタイム処理 | 入力パワー変動が大きい | 高速収束が必要 |
Python実装
LMS / NLMS
import numpy as np
def lms_filter(x, d, M, mu):
"""LMSアルゴリズムによる適応フィルタ
Parameters
----------
x : array_like
入力信号(参照信号)
d : array_like
目的信号
M : int
フィルタ次数
mu : float
ステップサイズ
Returns
-------
y : ndarray
フィルタ出力
e : ndarray
誤差信号
w_history : ndarray
フィルタ係数の履歴
"""
N = len(x)
w = np.zeros(M)
y = np.zeros(N)
e = np.zeros(N)
w_history = np.zeros((N, M))
for n in range(M, N):
x_vec = x[n:n-M:-1] if n >= M else np.pad(x[:n+1][::-1], (0, M-n-1))
y[n] = np.dot(w, x_vec)
e[n] = d[n] - y[n]
w = w + mu * e[n] * x_vec
w_history[n] = w
return y, e, w_history
def nlms_filter(x, d, M, mu=0.5, eps=1e-8):
"""NLMSアルゴリズムによる適応フィルタ"""
N = len(x)
w = np.zeros(M)
y = np.zeros(N)
e = np.zeros(N)
for n in range(M, N):
x_vec = x[n:n-M:-1] if n >= M else np.pad(x[:n+1][::-1], (0, M-n-1))
y[n] = np.dot(w, x_vec)
e[n] = d[n] - y[n]
norm = np.dot(x_vec, x_vec) + eps
w = w + (mu / norm) * e[n] * x_vec
return y, e
RLS
def rls_filter(x, d, M, lam=0.99, delta=1.0):
"""RLSアルゴリズムによる適応フィルタ
Parameters
----------
x : array_like
入力信号
d : array_like
目的信号
M : int
フィルタ次数
lam : float
忘却係数(0 < lam <= 1)
delta : float
逆相関行列の初期スケーリング
Returns
-------
y : ndarray
フィルタ出力
e : ndarray
誤差信号
"""
N = len(x)
w = np.zeros(M)
P = np.eye(M) / delta
y = np.zeros(N)
e = np.zeros(N)
for n in range(M, N):
x_vec = x[n:n-M:-1] if n >= M else np.pad(x[:n+1][::-1], (0, M-n-1))
y[n] = np.dot(w, x_vec)
e[n] = d[n] - y[n]
# ゲインベクトル
Px = P @ x_vec
k = Px / (lam + x_vec @ Px)
# フィルタ係数更新
w = w + k * e[n]
# 逆相関行列更新
P = (P - np.outer(k, x_vec @ P)) / lam
return y, e
実践例:ノイズキャンセレーション
適応フィルタの代表的な応用として、ノイズキャンセレーションを実装します。
import matplotlib.pyplot as plt
np.random.seed(42)
# 信号生成
N = 1000
t = np.arange(N)
signal = np.sin(2 * np.pi * 0.01 * t) # 目的信号
noise_ref = np.random.randn(N) # 参照ノイズ
# 未知のシステムを通ったノイズ(FIRフィルタで模擬)
h_true = np.array([0.5, -0.3, 0.2, -0.1])
noise_filtered = np.convolve(noise_ref, h_true, mode='full')[:N]
# 観測信号 = 目的信号 + フィルタされたノイズ
observed = signal + noise_filtered
# 各アルゴリズムでノイズ除去
M = 8 # フィルタ次数
y_lms, e_lms, _ = lms_filter(noise_ref, observed, M, mu=0.01)
y_nlms, e_nlms = nlms_filter(noise_ref, observed, M, mu=0.5)
y_rls, e_rls = rls_filter(noise_ref, observed, M, lam=0.99)
# プロット
fig, axes = plt.subplots(4, 1, figsize=(12, 10), sharex=True)
axes[0].plot(t, signal, 'g', label='Original signal')
axes[0].plot(t, observed, 'r', alpha=0.5, label='Observed (signal + noise)')
axes[0].set_title('Input Signals')
axes[0].legend()
axes[1].plot(t, signal, 'g', alpha=0.5, label='Original')
axes[1].plot(t, e_lms, 'b', label='LMS output')
axes[1].set_title('LMS Noise Cancellation')
axes[1].legend()
axes[2].plot(t, signal, 'g', alpha=0.5, label='Original')
axes[2].plot(t, e_nlms, 'b', label='NLMS output')
axes[2].set_title('NLMS Noise Cancellation')
axes[2].legend()
axes[3].plot(t, signal, 'g', alpha=0.5, label='Original')
axes[3].plot(t, e_rls, 'b', label='RLS output')
axes[3].set_title('RLS Noise Cancellation')
axes[3].legend()
plt.tight_layout()
plt.show()
ノイズキャンセレーションでは、誤差信号 \(e(n)\) がノイズ除去後の信号に対応します。RLSはLMSより高速に収束し、初期段階からノイズ除去性能が高いことが確認できます。
収束曲線の可視化
各アルゴリズムの収束速度を学習曲線(MSEの推移)で比較します。
# 学習曲線(MSEの移動平均)
window = 50
mse_lms = np.convolve(e_lms**2, np.ones(window)/window, mode='valid')
mse_nlms = np.convolve(e_nlms**2, np.ones(window)/window, mode='valid')
mse_rls = np.convolve(e_rls**2, np.ones(window)/window, mode='valid')
plt.figure(figsize=(10, 5))
plt.semilogy(mse_lms, label='LMS')
plt.semilogy(mse_nlms, label='NLMS')
plt.semilogy(mse_rls, label='RLS')
plt.xlabel('Sample')
plt.ylabel('MSE')
plt.title('Learning Curves')
plt.legend()
plt.grid(True)
plt.tight_layout()
plt.show()
関連記事
- 指数移動平均(EMA)フィルタの周波数特性 - 固定パラメータのフィルタの代表例。適応フィルタとの対比で理解が深まります。
- ウィーナーフィルタの理論とPython実装 - 適応フィルタ(特にLMS)の理論的基盤となる最適フィルタです。
- カルマンフィルタの理論とPython実装 - RLSと密接な関係を持つ逐次推定アルゴリズム。状態空間モデルにおける適応的推定です。
- FIRフィルタとIIRフィルタの比較 - 適応フィルタの多くはFIR構造を採用しています。FIR/IIRの特性の違いを解説しています。
- 信号処理におけるフィルタリング手法の基礎 - カルマンフィルタ、EKF、UKF、粒子フィルタの概要を体系的に解説しています。
- SGD・Adamの理論と比較 - LMSと同じ確率的勾配降下法の枠組みで、深層学習の最適化アルゴリズムを解説しています。
- 移動平均フィルタの種類と比較 - 適応フィルタと対比される固定フィルタの代表例です。
- ローパスフィルタの設計と比較 - 各種ローパスフィルタの周波数応答を比較しています。
- Unscented Kalman Filter(UKF)の理論とPython実装 - カルマンフィルタの非線形拡張で、RLSの発展形として理解できます。
参考文献
- Haykin, S. (2014). Adaptive Filter Theory (5th ed.). Pearson.
- Widrow, B., & Stearns, S. D. (1985). Adaptive Signal Processing. Prentice-Hall.
- Sayed, A. H. (2008). Adaptive Filters. Wiley-IEEE Press.