AIによる異常検知における埋め込みベクトルの距離計算アルゴリズムの比較

異常検知の精度は「距離」で決まる:ユークリッド距離とコサイン類似度をPythonで可視化比較

約10分で読めます
文字サイズ:
異常検知の精度は「距離」で決まる:ユークリッド距離とコサイン類似度をPythonで可視化比較
目次

この記事の要点

  • AI異常検知における埋め込みベクトルの活用と重要性
  • ユークリッド距離とコサイン類似度の特性と計算原理
  • 距離計算アルゴリズムが異常検知精度に与える影響

工場の現場でAI導入による稼働率向上や品質改善が検討される際、「どの最新モデルを使うか」がよく議論されます。Transformerベースか、軽量なAutoencoderかといった点です。

しかし、実務の現場において異常検知の精度が出ない根本的な理由は、モデルの複雑さではなく「距離」の定義にあります。

異常検知は「正常データから距離が遠いものを探す」作業です。距離尺度を間違えれば、高価なAIモデルでも期待する異常は見つけられず、歩留まり改善などの定量的な成果にはつながりません。

本記事では数式による解説を控え、Pythonコードでデータを可視化し、「ユークリッド距離」と「コサイン類似度」の違いを実践的に解説します。

まずは手元のデータで小さく始めて成果を可視化することが、段階的なスケールアップへの第一歩となります。Jupyter Notebookで手を動かしながら読み進めてください。

なぜ「距離」の定義が異常検知の命運を分けるのか

異常検知プロジェクトの失敗パターンの一つに、「ライブラリのデフォルト設定のまま使う」ことが挙げられます。

例えば振動センサーの異常検知では、「値の跳ね上がり」と「波形リズムの崩れ」のどちらを異常とするかで、選ぶべき距離計算方法は異なります。ここを誤ると、誤検知が多発し現場のカイゼン活動が停滞してしまいます。

ベクトル空間における「異常」の定義

扱うデータの多くは「ベクトル」で表現されます。センサーAとBの値が $(3, 4)$ なら、2次元空間上の1点となります。

ここで重要な問いです。
「$(3, 4)$ というデータと、$(6, 8)$ というデータは、似ていますか?」

  • 「似ていない」と答える視点: 値の大きさ(エネルギー)が倍も違う。3Vと6Vでは全く別物だ。
  • 「似ている」と答える視点: 比率は同じ $3:4$ だ。つまり波形の形状やバランスは変わっていない。

前者なら「ユークリッド距離」、後者なら「コサイン類似度」が適切です。ビジネス上の「異常の定義」と距離計算のロジックが食い違うと、AIは誤検知や見逃しを繰り返し、期待される生産性向上は実現できません。

直感と異なる高次元空間の罠

データが高次元(数百〜数千次元の埋め込みベクトルなど)になるとさらに厄介です。人間の直感は3次元までですが、高次元空間では距離の概念が直感とズレます(次元の呪い)。

そのため、まずは2次元平面でアルゴリズムがデータをどう「見て」いるか可視化して確認することが、データドリブンな改善の基本となります。

実験環境の準備:データを「見る」ためのセットアップ

実験を始めます。本質を理解しやすくするため、単純な2次元のダミーデータを生成します。

必要なライブラリをインポートし、基準となる「正常データ」を作成します。

import numpy as np
import matplotlib.pyplot as plt
from sklearn.metrics.pairwise import cosine_similarity
from scipy.spatial.distance import euclidean

# 表示設定
%matplotlib inline
plt.style.use('seaborn-v0_8-whitegrid')

# 1. 正常データの生成(中心付近に集まるデータ群)
# 平均(2, 2)、標準偏差0.5のガウス分布に従うデータを想定
np.random.seed(42)
normal_data = np.random.normal(loc=2.0, scale=0.5, size=(100, 2))

# データの可視化
plt.figure(figsize=(8, 8))
plt.scatter(normal_data[:, 0], normal_data[:, 1], label='Normal Data', alpha=0.6)
plt.xlim(-1, 6)
plt.ylim(-1, 6)
plt.axhline(0, color='grey', linewidth=0.8)
plt.axvline(0, color='grey', linewidth=0.8)
plt.title("Distribution of Normal Data")
plt.legend()
plt.grid(True)
plt.show()

実行すると、$(2, 2)$ 付近に集まった青い点の集団が表示されます。これが本実験の「正常」状態です。

異常検知モデルの役割は、この集団から「どれくらい離れたら異常とするか」の境界線を引くことです。距離アルゴリズムによる境界線の違いを見ていきます。

実験1:ユークリッド距離が描く「円形」の世界

実験環境の準備:データを「見る」ためのセットアップ - Section Image

最初は、最も一般的で直感的な「ユークリッド距離」です。

ユークリッド距離の特性

ユークリッド距離は、普段定規で測る距離そのもので、ピタゴラスの定理(三平方の定理)で計算されます。

特徴は「絶対的な位置の遠さ」を重視する点です。原点からの距離やデータの大きさ(ノルム)が変化すると、距離も大きく変わります。

ユークリッド距離の可視化コード

空間全体に対し「正常データの中心 $(2, 2)$ からのユークリッド距離」を計算し、等高線(ヒートマップ)で表示します。色が濃いほど異常度が高い(距離が遠い)ことを意味します。

# 正常データの中心(平均ベクトル)
mean_vector = np.mean(normal_data, axis=0)

# グリッドポイントの作成(空間全体を網羅的に計算するため)
x = np.linspace(-1, 6, 100)
y = np.linspace(-1, 6, 100)
X, Y = np.meshgrid(x, y)

# 各グリッドポイントでのユークリッド距離を計算
Z_euclidean = np.zeros_like(X)
for i in range(X.shape[0]):
    for j in range(X.shape[1]):
        point = np.array([X[i, j], Y[i, j]])
        # 中心とのユークリッド距離
        Z_euclidean[i, j] = euclidean(mean_vector, point)

# 可視化
plt.figure(figsize=(8, 8))
plt.contourf(X, Y, Z_euclidean, levels=20, cmap='Reds', alpha=0.7)
plt.scatter(normal_data[:, 0], normal_data[:, 1], c='blue', label='Normal Data', alpha=0.6, edgecolors='white')
plt.scatter(mean_vector[0], mean_vector[1], c='black', marker='x', s=100, label='Center')
plt.colorbar(label='Anomaly Score (Euclidean Distance)')
plt.title("Anomaly Score Landscape: Euclidean Distance")
plt.xlim(-1, 6)
plt.ylim(-1, 6)
plt.legend()
plt.show()

結果の解釈:同心円の守護者

グラフを見ると、赤いグラデーションが中心 $(2, 2)$ を起点に同心円状に広がっています。

ユークリッド距離における「異常」とは、方向に関係なく「中心から離れていること」を意味します。

どのような異常が得意か:絶対的な大きさの変化

  • 製造現場での例: 電圧、圧力、温度などのセンサー値。
    • 設定値が 2.0MPa なのに 5.0MPa になったら異常。
    • 0.0MPa(停止)になっても異常。
  • ポイント: データの「強さ」や「量」そのものが重要な管理指標であり、閾値超過が直接的な品質低下や設備停止につながる場合、このユークリッド距離が適しています。

実験2:コサイン類似度が捉える「方向」の世界

実験1:ユークリッド距離が描く「円形」の世界 - Section Image

次に「コサイン類似度」を用いた異常検知です。異常度として「コサイン距離(1 - コサイン類似度)」を使用します。

コサイン類似度の特性

コサイン類似度は2つのベクトルがなす「角度」に着目します。ベクトルの長さは無視され、「どちらを向いているか」だけを評価します。

コサイン類似度の可視化コード

同じデータとグリッドに対し、コサイン距離でヒートマップを描画します。

# 各グリッドポイントでのコサイン距離を計算
Z_cosine = np.zeros_like(X)

# コサイン類似度は2次元配列を受け取るので形状を調整
mean_vec_reshaped = mean_vector.reshape(1, -1)

for i in range(X.shape[0]):
    for j in range(X.shape[1]):
        point = np.array([X[i, j], Y[i, j]]).reshape(1, -1)
        # コサイン距離 = 1 - コサイン類似度
        # 原点(0,0)付近は計算が不安定になるため回避または無視
        if np.linalg.norm(point) < 0.1:
            Z_cosine[i, j] = 1.0 # 原点は異常扱い(便宜上)
        else:
            sim = cosine_similarity(mean_vec_reshaped, point)[0][0]
            Z_cosine[i, j] = 1 - sim

# 可視化
plt.figure(figsize=(8, 8))
plt.contourf(X, Y, Z_cosine, levels=20, cmap='Blues', alpha=0.7)
plt.scatter(normal_data[:, 0], normal_data[:, 1], c='orange', label='Normal Data', alpha=0.6, edgecolors='black')
plt.scatter(mean_vector[0], mean_vector[1], c='black', marker='x', s=100, label='Center')
plt.plot([0, 6], [0, 6], 'k--', alpha=0.5, label='Direction Line') # 方向を示す補助線
plt.colorbar(label='Anomaly Score (Cosine Distance)')
plt.title("Anomaly Score Landscape: Cosine Distance")
plt.xlim(-1, 6)
plt.ylim(-1, 6)
plt.legend()
plt.show()

結果の解釈:扇形の監視者

グラフはユークリッド距離のような同心円ではなく、原点 $(0, 0)$ から放射状に広がる扇形のグラデーションになります。

中心 $(2, 2)$ の方向($y=x$ の直線上)であれば、原点から遠く離れても($(5, 5)$ や $(10, 10)$ など)正常に近いままです。逆に距離が同じでも、方向がズレる($(2, 5)$ など)と急激に異常度が高まります。

どのような異常が得意か:バランスや比率の変化

  • 製造現場での例: 原材料の配合比率、振動波形の周波数成分バランス。

    • 材料Aと材料Bを $1:1$ で混ぜる工程において、投入量が倍になっても比率が $1:1$ なら「正常」とみなしたい場合に最適です。絶対量ではなく「質的なバランス」を監視し、歩留まりを維持します。
  • 最新の自然言語処理(LLM/RAG)での活用: 意味ベースのベクトル検索。

    • 2026年現在、ChatGPTClaude 3といったLLM(大規模言語モデル)を活用したシステムでは、テキストをベクトル化(Embedding)し、コサイン類似度を用いて関連情報を検索するRAG(検索拡張生成)が主流となっています。
    • 例えば、製造現場のトラブル報告書を検索する際、文章の長さや具体的な単語が異なっていても、「意味の方向性(ベクトル)」が近ければ類似文書として検出可能です。
    • 公式ドキュメント等によると、最新のEmbeddingモデルでは文脈理解が高度化しており、コサイン類似度は単なるキーワード一致を超えた「意図のマッチング」における事実上の標準指標として機能しています。

比較検証:同じデータでも判定が逆転するケース

2つの距離尺度が全く異なる「正常領域」を定義することが分かりました。これが実務の現場でどのような判断につながるか、ケーススタディで確認します。

意図的に「矛盾する」2つのテストデータを用意します。

  1. Case A (Scale Anomaly): 方向は正常だが、値が異常に大きいデータ $(5.0, 5.0)$
  2. Case B (Balance Anomaly): 値の大きさは正常だが、バランスが崩れたデータ $(1.5, 3.5)$

これらを判定してみます。

# テストデータ
case_a = np.array([5.0, 5.0]) # 方向は同じ、遠い
case_b = np.array([1.5, 3.5]) # 方向が違う、距離は近い

# 距離計算
dist_euclid_a = euclidean(mean_vector, case_a)
dist_cosine_a = 1 - cosine_similarity(mean_vector.reshape(1, -1), case_a.reshape(1, -1))[0][0]

dist_euclid_b = euclidean(mean_vector, case_b)
dist_cosine_b = 1 - cosine_similarity(mean_vector.reshape(1, -1), case_b.reshape(1, -1))[0][0]

print(f"--- Case A: {case_a} ---")
print(f"ユークリッド距離(大きさ重視): {dist_euclid_a:.4f} -> 判定: {'異常' if dist_euclid_a > 1.5 else '正常'}")
print(f"コサイン距離(方向重視)    : {dist_cosine_a:.4f} -> 判定: {'異常' if dist_cosine_a > 0.1 else '正常'}")

print(f"\n--- Case B: {case_b} ---")
print(f"ユークリッド距離(大きさ重視): {dist_euclid_b:.4f} -> 判定: {'異常' if dist_euclid_b > 1.5 else '正常'}")
print(f"コサイン距離(方向重視)    : {dist_cosine_b:.4f} -> 判定: {'異常' if dist_cosine_b > 0.1 else '正常'}")

(※判定の閾値 1.5 や 0.1 は、可視化結果に基づく仮置きの値です)

実行結果の考察

以下の傾向が確認できます。

  • Case A: ユークリッド距離では「異常」と判定されますが、コサイン距離では限りなく0に近く「正常」と判定されます。
  • Case B: ユークリッド距離では(中心に近いので)比較的「正常」とみなされやすいですが、コサイン距離では明確に「異常」となります。

これが「判定の逆転」です。

「センサー出力が過大になったらアラートを出したい」場合にコサイン類似度で判定すると、Case Aのような高圧状態を「バランスが良い」として見逃す可能性があります。現場の状況に合わせた現実的なアルゴリズム選定が不可欠です。

正規化(Normalization)が距離計算に与える影響

テクニカルな補足です。データ前処理の「正規化(L2正規化など)」を行うと、全ベクトルの長さが「1」に揃えられます。

長さが1に揃うと、ユークリッド距離とコサイン類似度の計算結果は実質的に同じ意味(単調関係)を持ちます。つまり、データを正規化してユークリッド距離を使うことは、正規化せずにコサイン類似度を使うこととほぼ等価です。

「とりあえず正規化する」と考えがちですが、それは「大きさの情報を捨てて方向だけの情報にする」という意思決定です。その情報を本当に捨ててよいか、現場のカイゼン目的に照らし合わせて検討が必要です。

実務への応用:失敗しないアルゴリズム選定チャート

最後に、これまでの知見を現場で使える選定ガイドとしてまとめます。

アルゴリズム選びに迷った際は、以下の表を参考にしてください。

データ特性ごとの推奨アルゴリズム対応表

データの種類 着目ポイント 推奨アルゴリズム 理由
センサー時系列
(温度, 圧力, 振動)
絶対値の逸脱
閾値超えが致命的な場合
ユークリッド距離
(またはマハラノビス距離)
物理的な限界値を超えたことを検知するため。
センサー時系列
(波形, スペクトル)
形状の変化
全体の強弱よりリズム崩れ
コサイン類似度 波形の振幅が変動しても、形状パターンが同じなら正常とみなすため。
画像データ
(埋め込みベクトル)
意味的な内容
「何が写っているか」
コサイン類似度 明るさやコントラストの影響を除外し、被写体の特徴を捉えるため。
テキストデータ
(埋め込みベクトル)
トピック・意味 コサイン類似度 文章の長さ(単語数)による影響を排除するため。
購買データ
(ユーザー特徴量)
購買規模
大口顧客か小口顧客か
ユークリッド距離 売上金額の絶対値が重要な指標となるため。

まずは簡単な可視化から始めるアプローチ

いきなり高次元データで判断せず、まずは主要な2変数(「回転数」と「温度」など)をピックアップして散布図を描いてみてください。小さく始めて成果を可視化することが重要です。

正常データが「円形」に固まっているか、「放射状」に伸びているかを見るだけで、選ぶべき距離尺度の9割は決まります。

まとめ:ツールに使われるな、データの「形」を見よ

各グリッドポイントでのコサイン距離を計算 - Section Image 3

異常検知における「距離」の重要性を、Pythonコードを用いた実験を通して解説しました。

  • ユークリッド距離は、データの「大きさ・強さ」に敏感な定規です。
  • コサイン類似度は、データの「方向・バランス」に敏感な定規です。
  • どちらが優れているかではなく、現場のビジネス課題がどちらの異常を定義しているかが全てです。

AI開発では数式の理解も大切ですが、「データが空間内でどのような形をしているか」をイメージする力が現場のトラブルシューティングや継続的な改善に役立ちます。

今回のコードは、手元のデータに入れ替えてそのまま実験に使えます。ぜひ、現場のデータが「円形」か「扇形」か確かめ、データドリブンなアプローチで生産性向上につなげてください。

異常検知の精度は「距離」で決まる:ユークリッド距離とコサイン類似度をPythonで可視化比較 - Conclusion Image

コメント

コメントは1週間で消えます
コメントを読み込み中...