見出し画像

「Pythonによる異常検知」を寄り道写経 ~ 第2章2.4節「高度な特徴抽出による異常検知」①k平均法

第2章「非時系列データにおける異常検知」

書籍の著者 曽我部東馬 先生、監修 曽我部完 先生


この記事は、テキスト「Pythonによる異常検知」第2章「非時系列データにおける異常検知」2.4節「高度な特徴抽出による異常検知」の通称「寄り道写経」を取り扱います。

5回連続で高度な特徴抽出技術を利用した異常検知を寄り道写経します!
今回は k 平均法による異常検知です。

ではテキストを開いて異常検知の旅に出発です🚀

旅行に行く若者のイラスト:「いらすとや」さんより

はじめに


テキスト「Pythonによる異常検知」のご紹介

このシリーズは書籍「Pythonによる異常検知」(オーム社、「テキスト」と呼びます)の寄り道写経です。

テキストは、2021年2月に発売され、機械学習等の誤差関数から異常検知を解明して、異常検知に関する実践的なPythonコードを提供する素晴らしい書籍です。
とにかく「誤差関数」と「異常度」の強い結びつきを堪能できる1冊です。

引用表記

この記事は、出典に記載の書籍に掲載された文章及びコードを引用し、適宜、掲載文章とコードを改変して書いています。
【出典】
「Pythonによる異常検知」第1版第6刷、著者 曽我部東馬、監修者 曽我部完、オーム社

記事中のイラストは、「かわいいフリー素材集いらすとや」さんのイラストをお借りしています。
ありがとうございます!


2.4 高度な特徴抽出による異常検知


主に Jupyter Notebook 形式(拡張子 .ipynb)でPythonコードを書きます。

今回の寄り道ポイントです。

  • データのクラスタを k 平均法で見つけ出し、クラスタごとにマハラノビス距離の二乗を異常値にして異常検知を行います。

  • scikit-learn の KMeans で k 平均法を実行します。

  • 学習データで異常検知モデルを作成して、未知データの異常検知を行います。

この記事で用いるライブラリをインポートします。

### インポート

# 数値計算、確率計算
import numpy as np 
import pandas as pd

# 機械学習
from sklearn.cluster import KMeans
from scipy.spatial import distance

# 描画
import matplotlib.pyplot as plt
import seaborn as sns
plt.rcParams['font.family'] = 'Meiryo'
import matplotlib.patches as patches

# ワーニング表示の抑制
import warnings
warnings.simplefilter('ignore')

k平均法

テキストの異常検知手順を要約します。

  • 学習データによる閾値の決定

    • k 平均法クラスタリング器を学習して、クラスタを識別する

    • クラスタごとに異常度を算出して、閾値を決定する

  • 未知データの異常検知の実施

    • 学習済みの k 平均法クラスタリング器でクラスタ判定する

    • 未知データの属するクラスタの異常度を算出して、当該クラスタの閾値に基づいて異常判別する

「クラスタリング→クラスタごとに異常度算出・閾値算出」のように2段階の処理を扱いますので、コード実装が複雑になっています。

準備

1.学習データの作成と可視化
2次元、300個の乱数データを作成します。
200個はグループ0、100個はグループ1を想定して、別々の2変量正規分布で乱数を作成します。

### データの作成

# 乱数生成器の設定
rng = np.random.default_rng(seed=1) # 23 or 24
# グループ0の200個のデータ作成:2変量正規分布乱数
group0 = rng.multivariate_normal(mean=[1, 1], cov=[[0.3, 0.081], [0.081, 0.3]],
                                 size=200)
# グループ1の100個のデータ作成:2変量正規分布乱数
group1 = rng.multivariate_normal(mean=[-1, -1], cov=[[0.1, 0.009], [0.009, 0.1]],
                                 size=100)
# データの統合
data = np.vstack([group0, group1])
print('data.shape:', data.shape)
print(data[:5, :], '…')

# グループラベルの作成
label_true = np.hstack([np.zeros(200), np.ones(100)]).astype(int)
print('\nグループラベル:', label_true[:10], '…')

【実行結果】

学習データの散布図を描画して、分布などを確認しましょう。

### データの散布図の描画

# 描画領域の設定
fig, ax = plt.subplots(figsize=(6, 4))
# 散布図の描画
sns.scatterplot(x=data[:, 0], y=data[:, 1], hue=label_true, alpha=0.7,
                palette=['tab:blue', 'tab:orange'], ax=ax)
# kmeansでクラスタ違いになるデータ点に円を描画
c_r = patches.Ellipse(xy=data[62, :], width=0.3, height=0.4, fill=False,
                      ec='tab:red', linewidth=2)
ax.add_patch(c_r)

# 凡例
handles, _ = ax.get_legend_handles_labels()
ax.legend(handles=handles, labels=['グループ0', 'グループ1'], title='正解ラベル')
# 修飾
ax.set(xlabel='変数1', ylabel='変数2', title='データの散布図')
ax.grid(lw=0.5);

【実行結果】
2つのグループに分かれていますが、グループの境界あたりは少々入り乱れている感じもします。
予告です!赤丸の点は k 平均法で別のクラスタに判別されます!

異常検知モデルの構築

テキストの STEP に沿って、学習データによる異常検知モデルの構築に取り組みます。

1.STEP 1:k 平均法によるクラスタリングの実行
scikit-learn の KMeans を使用して、学習データのクラスタリングを実行します。
クラスタ数は2です。

### STEP1 K-meansによるクラスタリングの実行

# K-meansの学習
kmeans = KMeans(n_clusters=2, random_state=1).fit(data)

# クラスタラベルの取得
cluster_label = kmeans.labels_
print('cluster_label:', cluster_label[:10], '…')

# クラスタ中心点の取得
centers = kmeans.cluster_centers_

【実行結果】
k 平均法のクラスタラベル(先頭10件)を表示しています。

あわせて、k 平均法のクラスタ中心点を centers に格納しています。

クラスタラベルでクラスタ識別した散布図を描画します。

### kmeansのクラスタラベルで層別した散布図の描画

# 描画領域の設定
fig, ax = plt.subplots(figsize=(6, 4))
# kmeansのクラスタラベルで層別した散布図の描画
sns.scatterplot(x=data[:, 0], y=data[:, 1], hue=cluster_label, 
                palette=['tab:blue', 'tab:orange'], alpha=0.7)
# kmeansのクラスタ中心点の描画
ax.scatter(centers[:, 0], centers[:, 1], marker='x', color='tab:red', s=200,
           lw=3)
# kmeansでクラスタ違いになったデータ点に円を描画
c_r = patches.Ellipse(xy=data[62, :], width=0.3, height=0.4, fill=False,
                      ec='tab:red', linewidth=2)
ax.add_patch(c_r)

# 凡例
handles, _ = ax.get_legend_handles_labels()
ax.legend(handles=handles, labels=['クラスタ0', 'クラスタ1'], title='kmeans')
# 修飾
ax.set(xlabel='変数1', ylabel='変数2', title='K-Means法によるクラスタの散布図')
ax.grid(lw=0.5);

【実行結果】
学習データのグループをうまく反映できたクラスタリングだと思います。
2つのクラスタ中心点(×印)は程よい位置にあるような感じです。
赤丸の1点だけ想定グループと異なるクラスタにラベリングされました。

2.STEP 2:クラスタごとの異常度の算出
クラスタ0、クラスタ1ごとにマハラノビス距離の二乗で異常度を算出します。
なお、テキストの異常度の計算式は「マハラノビス距離の二乗の1/2」ですので、この記事はテキストと異なる異常度を採用していることになります。

複数クラスタの繰り返し処理を一括でできるように関数化しました。

### STEP2 クラスタごとの異常度の算出 異常度はマハラノビス距離の二乗

## 異常度算出関数の定義
def calc_anomaly_score(data, cluster_label):

    # マハラノビス距離のパラメータリストの初期化
    mean_list, cov_inv_list = [], []

    # クラスタごとにマハラノビス距離のための基礎数値算出を繰り返し処理
    for cluster in np.unique(cluster_label):
        # cluster_numberのデータの取り出し
        cluster_data = data[cluster_label==cluster]
        # 変数ごとの平均値の算出
        cluster_mean = cluster_data.mean(axis=0)
        # 変数間の分散共分散行列の算出
        cluster_cov = np.cov(cluster_data.T, ddof=0)
        # 分散共分散行列の逆行列の算出
        cluster_cov_inv = np.linalg.inv(cluster_cov)
        # 平均値、分散共分散行列、逆行列をリストに追加
        mean_list.append(cluster_mean)
        cov_inv_list.append(cluster_cov_inv)

    # 異常度の算出:マハラノビス距離の二乗
    anomaly_score = np.array(
        [distance.mahalanobis(val, mean_list[cluster], cov_inv_list[cluster])**2
        for (val, cluster) in zip(data, cluster_label)])

    return anomaly_score, np.array(mean_list), np.array(cov_inv_list)

異常度を算出します。
あわせて未知データの異常検知に用いるパラメータを取得します。

## 異常度の算出
anomaly_score, means, cov_invs = calc_anomaly_score(data, cluster_label)

## 結果表示
print('平均ベクトル クラスタ0:')
print(means[0])
print('平均ベクトル クラスタ1:')
print(means[1])
print('\n分散共分散行列の逆行列 クラスタ0:')
print(cov_invs[0])
print('分散共分散行列の逆行列 クラスタ1:')
print(cov_invs[1])
print('\n学習データの異常度:')
print(anomaly_score[:5], '…')

【実行結果】

3.STEP 3:異常度の閾値の算出
クラスタごとに分位点法による閾値を算出します。
複数クラスタの繰り返し処理を一括でできるように関数化しました。

### STEP3 異常度の閾値の算出 by 分位点法

## 分位点法による異常度の閾値算出関数の定義
def calc_thres_quantile(anomaly_score, cluster_label, quantile_point):
    
    # 各クラスタの閾値を格納するリストの初期化
    threshold_list = []
    
    # クラスタごとに閾値の算出を繰り返し処理
    for cluster in np.unique(cluster_label):
        # 該当クラスタの異常値を取り出し
        cluster_anomaly_score = anomaly_score[cluster_label==cluster]
        # 異常データの個数の算出
        num_quantile = int(round(len(cluster_anomaly_score) * quantile_point, 0))
        # 異常度の大きい順で異常データ個数目に該当する異常度を算出して閾値に設定
        anomaly_threshold = sorted(cluster_anomaly_score)[::-1][num_quantile - 1]
        threshold_list.append(anomaly_threshold)
    
    return np.array(threshold_list)

異常度の閾値をクラスタごとに算出します。
分位点は 1% です。

## 分位%点の設定
q = 0.01

## クラスタ0, 1の異常度の閾値の算出 by 分位点法
anomaly_thres = calc_thres_quantile(anomaly_score, cluster_label, q)
print('各クラスタの閾値', anomaly_thres)

【実行結果】

4.STEP 3-2:学習データで異常検知を確認
学習データの異常度に閾値を適用して異常検知を実行し、結果を可視化します。

### STEP3-2 学習データの異常検知
anomaly_label = np.array([(anomaly_score[i] >= anomaly_thres[label]) + 0
                          for i, label in enumerate(cluster_label)])
print('anomaly_label.shape:', anomaly_label.shape)
print('anomaly_label[:10] :', anomaly_label[:10], '…')
print('異常データ数:', anomaly_label.sum())

【実行結果】
学習データのうち、3点が異常判定となるモデルになりました。

可視化も関数化します。

### STEP3-2 異常度と閾値を可視化して確認

## 異常度と閾値の可視化関数の定義
def plot_anomaly1(a_score, a_threshold, a_label, data_idx, q, title):

    # 設定
    palette = ['tab:blue', 'tab:red']
    sizes = [40, 80]
    labels = ['正常', '異常']

    # 正常データがない場合
    if sum(a_label == 0) == 0:
        palette = ['tab:red']
        sizes = [80]
        labels = ['異常']

    # 描画領域の設定
    fig, ax = plt.subplots(figsize=(6, 4))
    # データの散布図の描画
    sns.scatterplot(x=data_idx, y=a_score,
                    hue=a_label, palette=palette, 
                    size=a_label, sizes=sizes)
    # 閾値の水平線の描画
    ax.axhline(a_threshold, color='tab:red', ls='--')
    handles, _ = ax.get_legend_handles_labels()
    ax.legend(handles=handles, labels=labels, bbox_to_anchor=(1, 1))
    ax.set(xlabel='データ番号', ylabel='異常度 $\\alpha$',
           title=f'{title}の異常度\n閾値 {a_threshold:.3f} (q={q:.0%})')
    ax.grid(lw=0.5)
    plt.show()

クラスタ別に異常検知の結果を可視化します。

## 異常度の可視化
# クラスタ0
cluster_id = 0
data_idx = np.arange(data.shape[0])[cluster_label==cluster_id]
plot_anomaly1(anomaly_score[cluster_label==cluster_id],
              anomaly_thres[cluster_id],
              anomaly_label[cluster_label==cluster_id],
              data_idx, q, 'クラスタ0')

# クラスタ1
cluster_id = 1
data_idx = np.arange(data.shape[0])[cluster_label==cluster_id]
plot_anomaly1(anomaly_score[cluster_label==cluster_id],
              anomaly_thres[cluster_id],
              anomaly_label[cluster_label==cluster_id],
              data_idx, q, 'クラスタ1')

【実行結果】
クラスタ0に判別したデータの中の異常点はそこそこ受け入れやすい感じがします。

クラスタ1判別データでは、グループ0のデータ点(インデックス62)が異常判定されました。
k 平均法のクラスタリングの悪さが影響しているかと思います。

未知データの準備

未知データを作成して、さきほど構築した異常検知モデルに適用して異常検知しましょう!

1.未知データの作成

### 未知データの作成
data_new = np.array([[-1, 2], [-1, 0.3], [0, -1.5], [0, -1], [1, -1], [2, 0]])
print('未知データ:')
print(data_new)

【実行結果】
6個のデータを作成しました。

未知データを可視化します。

### 未知データの可視化

# 描画領域の設定
fig, ax = plt.subplots(figsize=(6, 4))
# 学習データの散布図の描画
sns.scatterplot(x=data[:, 0], y=data[:, 1], hue=cluster_label, 
                palette=['tab:blue', 'tab:orange'], alpha=0.7, legend=False)
# kmeansのクラスタ中心点の描画
ax.scatter(centers[:, 0], centers[:, 1], marker='x', color='tab:red', s=200,
           lw=3)
# 未知データの散布図の描画
ax.scatter(data_new[:, 0], data_new[:, 1], color='lightpink', ec='tab:red',
           s=80, label='未知データ')
# 修飾
ax.set(xlabel='変数1', ylabel='変数2', title='未知データの散布図')
ax.legend()
ax.grid(lw=0.5);

【実行結果】
クラスタの外側に配置しました。

未知データの異常検知

いよいよ本番です!
テキストの STEP に沿って、未知データの異常検知に取り組みます。

1.STEP 4:未知データのクラスタリング
学習済みの k 平均法クラスタリング器 kmeans を用いて、未知データのクラスタリングを実行します。

### STEP4 未知データのクラスタリング
cluster_label_new = kmeans.predict(data_new)
print('未知データのクラスタ:', cluster_label_new)

【実行結果】
2点がクラスタ 0(青い点側)、4点がクラスタ 1(オレンジ点側)になりました。

2.STEP 5 未知データの異常検知
判別されたクラスタの異常度計算・閾値を適用して異常データを炙り出します!
クラスタごとに処理を繰り返すので、そっと関数化しました。

### STEP5 未知データの異常検知

## 未知データの異常度算出関数の定義
def calc_anomaly_score_for_new(data, cluster_label, means, cov_invs):    
    # 異常度の算出:マハラノビス距離の二乗
    anomaly_score = np.array(
        [distance.mahalanobis(val, means[label], cov_invs[label])**2
         for (val, label) in zip(data, cluster_label)])
    return anomaly_score

## 未知データの異常度の算出
anomaly_score_new = calc_anomaly_score_for_new(
                                data_new, cluster_label_new, means, cov_invs)
print('未知データの異常度 :', anomaly_score_new)

## 未知データの異常検知
anomaly_label_new = np.array([np.where(score >= anomaly_thres[label], 1, 0)
                              for score, label
                              in zip(anomaly_score_new, cluster_label_new)])
print('未知データの異常検知:', anomaly_label_new)

【実行結果】
未知データの1番目、2番目、5番目が異常判定されました!

未知データの異常検知結果をデータフレーム化しましょう。

### 未知データの異常検知結果のデータフレームの作成

data_new_df = pd.DataFrame(data_new, columns=['変数1', '変数2'])
data_new_df['クラスタ'] = cluster_label_new
data_new_df['異常度'] = anomaly_score_new
data_new_df['閾値'] = data_new_df['クラスタ'].apply(lambda x: anomaly_thres[x])
data_new_df['異常検知'] = anomaly_label_new
display(data_new_df)

【実行結果】

3.異常検知結果を可視化
未知データの異常検知結果をさまざまな可視化手法で堪能しましょう。

① 異常度の散布図

### 異常度の可視化
# クラスタ1
cluster_id = 0
data_idx = np.arange(data_new.shape[0])[cluster_label_new==cluster_id]
plot_anomaly1(anomaly_score_new[cluster_label_new==cluster_id],
              anomaly_thres[cluster_id],
              anomaly_label_new[cluster_label_new==cluster_id],
              data_idx, q, 'クラスタ0')
# クラスタ2
cluster_id = 1
data_idx = np.arange(data_new.shape[0])[cluster_label_new==cluster_id]
plot_anomaly1(anomaly_score_new[cluster_label_new==cluster_id],
              anomaly_thres[cluster_id],
              anomaly_label_new[cluster_label_new==cluster_id],
              data_idx, q, 'クラスタ1')

【実行結果】
クラスタ0 のデータ点 0 はたしかに閾値からかなり乖離しており、異常であることがよく分かります。

クラスタ 1 のデータ点 4 もたしかに閾値からかなり乖離しており、異常であることがよく分かります。
データ点 1 は微妙・・・?

② 2変数の散布図に正常・異常をマップ
学習データを含む2変数の散布図に未知データを追加してみましょう。
未知データは正常・異常を識別してプロットします。

### 未知データのクラスタ&異常検知の可視化

# 描画領域の設定
fig, ax = plt.subplots(figsize=(6, 4))
# 学習データの散布図の描画
sns.scatterplot(x=data[:, 0], y=data[:, 1], hue=cluster_label, 
                palette=['tab:blue', 'tab:orange'], alpha=0.5, legend=False,
                ax=ax)
# kmeansのクラスタ中心点の描画
ax.scatter(centers[:, 0], centers[:, 1], marker='x', color='tab:red', s=200,
           lw=3)
# 未知データの散布図の描画
sns.scatterplot(x=data_new[:, 0], y=data_new[:, 1],
                hue=cluster_label_new, palette=['tab:blue', 'tab:orange'],
                alpha=0.7, ec='tab:red', 
                style=anomaly_label_new, markers=['o', 'X'],
                size=anomaly_label_new, sizes=[200, 250], legend=False,
                ax=ax)
for i in range(len(data_new)):
    ax.annotate(text=i, xy=data_new[i, :]+0.12, fontsize=14)
# 凡例
ax.scatter([], [], marker='o', c='tab:blue', alpha=0.7, s=70, label='クラスタ0')
ax.scatter([], [], marker='o', c='tab:orange', alpha=0.7, s=70, label='クラスタ1')
ax.scatter([], [], marker='o', c='white', ec='tab:red', s=70, label='正常')
ax.scatter([], [], marker='X', c='white', ec='tab:red', s=100, label='異常')
ax.legend(bbox_to_anchor=(1, 1))
# 修飾
ax.set(xlabel='変数1', ylabel='変数2', title='未知データの異常検知')
ax.grid(lw=0.5);

【実行結果】
青色がクラスタ 0、オレンジ色がクラスタ 1 です。
形状が◯印のデータは正常、X印のデータは異常です。

データ点 4 がクラスタ 1 になるとはびっくりです。
クラスタ違いになると異常判定される可能性が高まるような印象です。
一方、データ点 0 はクラスタ 0 になっています。
確かにどちらのクラスタになってもおかしくは無さそうです。

クラスタ 0 とクラスタ 1 の判別の基準や境界はどうなっているのでしょう???
描いてみましょう!!!

③ 決定境界付き散布図
scikit-learn の DecisionBoundaryDisplay を使えば簡単に決定境界を描画できます!

### 決定境界の描画

# 追加インポート
from sklearn.inspection import DecisionBoundaryDisplay
from matplotlib.colors import ListedColormap

# 色の設定
cmap_bound = ListedColormap(['lightblue', 'bisque'])

# 描画領域の設定
fig, ax = plt.subplots(figsize=(6, 4))
# 決定境界の描画
disp = DecisionBoundaryDisplay.from_estimator(
    kmeans, data, response_method='predict', cmap=cmap_bound,
    xlabel='Magnesium', ylabel='Color intensity', alpha=0.3, ax=ax)
# 学習データの散布図の描画
sns.scatterplot(x=data[:, 0], y=data[:, 1], hue=cluster_label, 
                palette=['tab:blue', 'tab:orange'], alpha=0.5, legend=False,
                ax=ax)
# kmeansのクラスタ中心点の描画
ax.scatter(centers[:, 0], centers[:, 1], marker='x', color='tab:red', s=200,
           lw=3)
# 未知データの散布図の描画
sns.scatterplot(x=data_new[:, 0], y=data_new[:, 1],
                hue=cluster_label_new, palette=['tab:blue', 'tab:orange'],
                alpha=0.7, ec='tab:red', 
                style=anomaly_label_new, markers=['o', 'X'],
                size=anomaly_label_new, sizes=[200, 250], legend=False,
                ax=ax)
# 凡例表示
ax.scatter([], [], marker='o', c='tab:blue', alpha=0.7, s=70, label='クラスタ0')
ax.scatter([], [], marker='o', c='tab:orange', alpha=0.7, s=70, label='クラスタ1')
ax.scatter([], [], marker='o', c='white', ec='tab:red', s=70, label='正常')
ax.scatter([], [], marker='X', c='white', ec='tab:red', s=100, label='異常')
ax.legend(bbox_to_anchor=(1.3, 1))
# 修飾
ax.set(xlabel='変数1', ylabel='変数2', title='未知データの異常検知と決定境界')
ax.grid(lw=0.5);

【実行結果】
クラスタを判別する「決定境界」が明らかになりました!
直線的な境界になっています。

この図はあくまでクラスタ判別基準だけを示しています。
正常・異常の境界はどうなっているのでしょう???
描いてみましょう!!!

④ 決定境界付き閾値付き散布図
等高線で閾値を描画します!

### 閾値の描画

## 等高線に用いるマハラノビス距離の算出
# scipy.distance利用 引数: (データ1, データ2), 平均, 分散共分散行列の逆数
pos_3d0 = np.array([[(i, j, distance.mahalanobis((i, j), means[0], cov_invs[0]))
                      for i in np.arange(-3, 4, 0.05)]
                      for j in np.arange(-3, 4, 0.05)])
pos_3d1 = np.array([[(i, j, distance.mahalanobis((i, j), means[1], cov_invs[1]))
                      for i in np.arange(-3, 4, 0.05)]
                      for j in np.arange(-3, 4, 0.05)])

## 描画処理
# 描画領域の設定
fig, ax = plt.subplots(figsize=(6, 4))
# 決定境界の描画
disp = DecisionBoundaryDisplay.from_estimator(
    kmeans, data, response_method='predict', cmap=cmap_bound,
    xlabel='変数1', ylabel='変数2', alpha=0.3, ax=ax)
# 学習データの散布図の描画
sns.scatterplot(x=data[:, 0], y=data[:, 1], hue=cluster_label, 
                palette=['tab:blue', 'tab:orange'], alpha=0.5, legend=False,
                ax=ax)
# 未知データの散布図の描画
sns.scatterplot(x=data_new[:, 0], y=data_new[:, 1],
                hue=cluster_label_new, palette=['tab:blue', 'tab:orange'],
                alpha=0.7, ec='tab:red', 
                style=anomaly_label_new, markers=['o', 'X'],
                size=anomaly_label_new, sizes=[200, 250], legend=False,
                ax=ax)
# kmeansのクラスタ中心点の描画
ax.scatter(centers[:, 0], centers[:, 1], marker='x', color='tab:red', s=200,
           lw=3)
# 等高線図の描画:クラスタ1とクラスタ2
ax.contour(pos_3d0.T[0], pos_3d0.T[1], pos_3d0.T[2], levels=20, cmap='Blues_r',
           vmin=0, vmax=10, linewidths=0.2)
ax.contour(pos_3d1.T[0], pos_3d1.T[1], pos_3d1.T[2], levels=20, cmap='Reds_r',
           vmin=0, vmax=10, linewidths=0.2)
# 閾値の描画:クラスタ1とクラスタ2
ax.contour(pos_3d0.T[0], pos_3d0.T[1], pos_3d0.T[2],
           levels=[np.sqrt(anomaly_thres[0])],
           colors=['tab:red'], linestyles=['--'])
ax.contour(pos_3d1.T[0], pos_3d1.T[1], pos_3d1.T[2],
           levels=[np.sqrt(anomaly_thres[1])],
           colors=['tab:red'], linestyles=['--'])
# 凡例表示
ax.scatter([], [], marker='o', c='tab:blue', alpha=0.7, s=70, label='クラスタ0')
ax.scatter([], [], marker='o', c='tab:orange', alpha=0.7, s=70, label='クラスタ1')
ax.scatter([], [], marker='o', c='white', ec='tab:red', s=70, label='正常')
ax.scatter([], [], marker='X', c='white', ec='tab:red', s=100, label='異常')
ax.plot([], [], c='tab:red', ls='--', label='閾値')
ax.legend(bbox_to_anchor=(1.3, 1))
# 修飾
ax.set(title='未知データの異常検知\nクラスタの決定境界と異常値の閾値',
       xlim=(-2.7, 3.5), ylim=(-2.7, 4.1))
ax.grid(lw=0.5);

【実行結果】
等高線図に赤い点線の異常度の閾値をプロットしました。
今回の k 平均法による2次元データの異常検知マップ(地図)が明確になりました。

今回の寄り道写経は以上です。


シリーズの記事

次の記事

前の記事

目次

ブログの紹介


note で7つのシリーズ記事を書いています。
ぜひ覗いていってくださいね!

1.のんびり統計

統計検定2級の問題集を手がかりにして、確率・統計をざっくり掘り下げるブログです。
雑談感覚で大丈夫です。ぜひ覗いていってくださいね。
統計検定2級公式問題集CBT対応版に対応しています。
Python、EXCELのサンプルコードの配布もあります。

2.実験!たのしいベイズモデリング1&2をPyMC Ver.5で

書籍「たのしいベイズモデリング」・「たのしいベイズモデリング2」の心理学研究に用いられたベイズモデルを PyMC Ver.5で描いて分析します。
この書籍をはじめ、多くのベイズモデルは R言語+Stanで書かれています。
PyMCの可能性を探り出し、手軽にベイズモデリングを実践できるように努めます。
身近なテーマ、イメージしやすいテーマですので、ぜひぜひPyMCで動かして、一緒に楽しみましょう!

3.実験!岩波データサイエンス1のベイズモデリングをPyMC Ver.5で

書籍「実験!岩波データサイエンスvol.1」の4人のベイジアンによるベイズモデルを PyMC Ver.5で描いて分析します。
この書籍はベイズプログラミングのイロハをざっくりと学ぶことができる良書です。
楽しくPyMCモデルを動かして、ベイズと仲良しになれた気がします。
みなさんもぜひぜひPyMCで動かして、一緒に遊んで学びましょう!

4.楽しい写経 ベイズ・Python等

ベイズ、Python、その他の「書籍の写経活動」の成果をブログにします。
主にPythonへの翻訳に取り組んでいます。
写経に取り組むお仲間さんのサンプルコードになれば幸いです🍀

5.RとStanではじめる心理学のための時系列分析入門 を PythonとPyMC Ver.5 で

書籍「RとStanではじめる心理学のための時系列分析入門」の時系列分析をPythonとPyMC Ver.5 で実践します。
この書籍には時系列分析のテーマが盛りだくさん!
時系列分析の懐の深さを実感いたしました。
大好きなPythonで楽しく時系列分析を学びます。

6.データサイエンスっぽいことを綴る

統計、データ分析、AI、機械学習、Pythonのコラムを不定期に綴っています。
統計・データサイエンス書籍にまつわる記事が多いです。
「統計」「Python」「数学とPython」「R」のシリーズが生まれています。

7.Python機械学習プログラミング実践記

書籍「Python機械学習プログラミング PyTorch & scikit-learn編」を学んだときのさまざまな思いを記事にしました。
この書籍は、scikit-learnとPyTorchの教科書です。
よかったらぜひ、お試しくださいませ。

最後までお読みいただきまして、ありがとうございました。

この記事が気に入ったらサポートをしてみませんか?