MT法(マハラノビス・タグチ法, Mahalanobis-Taguchi method)は、マハラノビス距離を基盤とした異常検知手法です。田口玄一によって提唱された品質工学(タグチメソッド)の一部であり、正常データの分布からの逸脱度を測ることで異常を検知します。
MT法はシンプルでありながら実用性が高く、製造業の品質管理やロケットのヘルスモニタリングなど、幅広い分野で活用されています。IHIが開発するイプシロンロケットでもMT法に基づく異常検知が利用されています。
本記事の内容
- MT法の理論的背景
- マハラノビス距離の正規化
- SN比による特徴量選択
- Pythonでのスクラッチ実装
前提知識
この記事を読む前に、以下の記事を読んでおくと理解が深まります。
MT法の概要
MT法は以下の3つのステップで構成されます。
- 単位空間の構築: 正常データ群(基準データ)から平均ベクトルと共分散行列を計算
- マハラノビス距離の計算: 評価対象データの正常データからの距離を算出
- 特徴量選択: SN比を用いて有効な特徴量を選択(MTS法)
マハラノビス距離の定義
$p$ 次元のデータ $\bm{x}$ に対するマハラノビス距離は、正常データの平均 $\bm{\mu}$ と共分散行列 $\bm{\Sigma}$ を用いて以下のように定義されます。
$$ D^2(\bm{x}) = (\bm{x} – \bm{\mu})^T \bm{\Sigma}^{-1} (\bm{x} – \bm{\mu}) $$
MT法では、これを次元数 $p$ で正規化したマハラノビス距離(MD)を用います。
$$ MD(\bm{x}) = \frac{1}{p} (\bm{x} – \bm{\mu})^T \bm{\Sigma}^{-1} (\bm{x} – \bm{\mu}) $$
正規化することで、正常データにおける $MD$ の期待値がおよそ1になります。これにより、変数の数によらず統一的な基準で異常度を評価できます。
導出: なぜ $p$ で正規化するのか
正常データが $p$ 次元の多変量正規分布 $\mathcal{N}(\bm{\mu}, \bm{\Sigma})$ に従うとき、マハラノビス距離の二乗はカイ二乗分布に従います。
$$ D^2(\bm{x}) = (\bm{x} – \bm{\mu})^T \bm{\Sigma}^{-1} (\bm{x} – \bm{\mu}) \sim \chi^2(p) $$
カイ二乗分布 $\chi^2(p)$ の期待値は $p$ であるため、
$$ E[D^2] = p $$
したがって、$MD = D^2 / p$ とすると $E[MD] = 1$ となり、正常データのMDは1付近の値を取ります。MDが1から大きく離れたデータを異常と判定します。
SN比による特徴量選択(MTS法)
MT法の発展形であるMTS法(Mahalanobis-Taguchi System)では、SN比(Signal-to-Noise ratio)を用いて有効な特徴量を選択します。
全特徴量を使ったMDと、一部の特徴量を除外したMDを比較し、異常データと正常データの分離度が向上する特徴量の組み合わせを探索します。
SN比は以下の「望大特性」で評価します。
$$ \eta = -10 \log_{10}\left(\frac{1}{n_a}\sum_{i=1}^{n_a} \frac{1}{MD_i}\right) $$
ここで $n_a$ は異常データの数、$MD_i$ は異常データのマハラノビス距離です。SN比が大きいほど、正常と異常の分離が良いことを意味します。
Pythonでの実装
import numpy as np
import matplotlib.pyplot as plt
from scipy import stats
np.random.seed(42)
class MTMethod:
"""MT法(マハラノビス・タグチ法)による異常検知"""
def __init__(self):
self.mu = None
self.sigma_inv = None
self.p = None
def fit(self, X_normal):
"""正常データから単位空間を構築"""
self.p = X_normal.shape[1]
self.mu = X_normal.mean(axis=0)
sigma = np.cov(X_normal.T)
self.sigma_inv = np.linalg.inv(sigma)
return self
def mahalanobis_distance(self, X):
"""正規化マハラノビス距離(MD)を計算"""
diff = X - self.mu
md = np.array([d @ self.sigma_inv @ d for d in diff]) / self.p
return md
def predict(self, X, threshold=None):
"""異常判定(threshold=Noneの場合はMD>4を閾値とする)"""
md = self.mahalanobis_distance(X)
if threshold is None:
threshold = 4.0 # 一般的な経験的閾値
return (md > threshold).astype(int), md
# --- データ生成 ---
n_normal = 200
n_anomaly = 15
p = 3
# 正常データ
mean_normal = np.array([5.0, 3.0, 7.0])
cov_normal = np.array([
[1.0, 0.5, 0.3],
[0.5, 1.5, 0.2],
[0.3, 0.2, 2.0]
])
X_normal = np.random.multivariate_normal(mean_normal, cov_normal, n_normal)
# 異常データ(平均からずれたデータ)
X_anomaly = np.random.multivariate_normal(
mean_normal + np.array([3.0, 2.0, 4.0]),
cov_normal * 0.5,
n_anomaly
)
# --- MT法の適用 ---
mt = MTMethod()
mt.fit(X_normal)
# 正常データのMD
md_normal = mt.mahalanobis_distance(X_normal)
# 異常データのMD
md_anomaly = mt.mahalanobis_distance(X_anomaly)
# 全データの異常判定
X_all = np.vstack([X_normal, X_anomaly])
labels_true = np.array([0] * n_normal + [1] * n_anomaly)
predictions, md_all = mt.predict(X_all)
print(f"=== MT法による異常検知結果 ===")
print(f"正常データのMD: 平均={md_normal.mean():.3f}, 標準偏差={md_normal.std():.3f}")
print(f"異常データのMD: 平均={md_anomaly.mean():.3f}, 標準偏差={md_anomaly.std():.3f}")
print(f"検出された異常数: {predictions.sum()} / 実際の異常数: {labels_true.sum()}")
# --- 可視化 ---
fig, axes = plt.subplots(1, 3, figsize=(18, 5))
# MDのヒストグラム
ax1 = axes[0]
ax1.hist(md_normal, bins=30, alpha=0.6, label='Normal', color='blue', density=True)
ax1.hist(md_anomaly, bins=15, alpha=0.6, label='Anomaly', color='red', density=True)
ax1.axvline(x=4.0, color='green', linestyle='--', linewidth=2, label='Threshold=4')
ax1.set_xlabel('Mahalanobis Distance (MD)')
ax1.set_ylabel('Density')
ax1.set_title('Distribution of MD')
ax1.legend()
ax1.grid(True, alpha=0.3)
# MDの時系列的なプロット
ax2 = axes[1]
ax2.scatter(range(n_normal), md_normal, c='blue', s=10, alpha=0.5, label='Normal')
ax2.scatter(range(n_normal, n_normal + n_anomaly), md_anomaly,
c='red', s=30, alpha=0.8, label='Anomaly')
ax2.axhline(y=4.0, color='green', linestyle='--', linewidth=2, label='Threshold')
ax2.set_xlabel('Sample Index')
ax2.set_ylabel('MD')
ax2.set_title('MD for Each Sample')
ax2.legend()
ax2.grid(True, alpha=0.3)
# 正常データのMDとカイ二乗分布の比較
ax3 = axes[2]
md_sorted = np.sort(md_normal * p) # p倍してカイ二乗分布と比較
x_chi2 = np.linspace(0, md_sorted.max(), 200)
ax3.hist(md_sorted, bins=30, density=True, alpha=0.6, color='blue', label='Data')
ax3.plot(x_chi2, stats.chi2.pdf(x_chi2, df=p), 'r-', linewidth=2,
label=f'$\\chi^2({p})$')
ax3.set_xlabel('$D^2$')
ax3.set_ylabel('Density')
ax3.set_title('$D^2$ vs $\\chi^2$ Distribution')
ax3.legend()
ax3.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
正常データのMDがおよそ1付近に集中し、異常データのMDが大きな値を取ることが確認できます。また、正規化前のマハラノビス距離の二乗 $D^2$ がカイ二乗分布に従うことも可視化で確認しています。
まとめ
本記事では、MT法(マハラノビス・タグチ法)による異常検知について解説しました。
- MT法は正常データの平均と共分散から単位空間を構築し、マハラノビス距離で異常度を評価する手法
- 次元数 $p$ で正規化することで、正常データのMDの期待値が1になる
- $D^2 \sim \chi^2(p)$ の性質に基づき、統計的な閾値設定が可能
- MTS法ではSN比を用いた特徴量選択によってさらに判別精度を向上できる
次のステップとして、以下の記事も参考にしてください。