ホーム » Alibi Detect » Alibi Detect 0.7 : Examples : VAE 外れ値検知 on CIFAR10

Alibi Detect 0.7 : Examples : VAE 外れ値検知 on CIFAR10

Alibi Detect 0.7 : Examples : VAE 外れ値検知 on CIFAR10 (翻訳/解説)
翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 07/04/2021 (0.7.0)

* 本ページは、Alibi Detect の以下のドキュメントを翻訳した上で適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

無料 Web セミナー開催中 クラスキャット主催 人工知能 & ビジネス Web セミナー

人工知能とビジネスをテーマに WEB セミナーを定期的に開催しています。
スケジュールは弊社 公式 Web サイト でご確認頂けます。
  • お住まいの地域に関係なく Web ブラウザからご参加頂けます。事前登録 が必要ですのでご注意ください。
  • ウェビナー運用には弊社製品「ClassCat® Webinar」を利用しています。
クラスキャットは人工知能・テレワークに関する各種サービスを提供しております :

人工知能研究開発支援 人工知能研修サービス テレワーク & オンライン授業を支援
PoC(概念実証)を失敗させないための支援 (本支援はセミナーに参加しアンケートに回答した方を対象としています。)

お問合せ : 本件に関するお問い合わせ先は下記までお願いいたします。

株式会社クラスキャット セールス・マーケティング本部 セールス・インフォメーション
E-Mail:sales-info@classcat.com  ;  WebSite: https://www.classcat.com/  ;  Facebook

 

 

Alibi Detect 0.7 : Examples : VAE 外れ値検知 on CIFAR10

VAE – 概要

変分オートエンコーダ (VAE, Variational Auto-Encoder) 外れ値検知器は最初にラベル付けされていない、しかし通常 (inlier) データのバッチで訓練されます。教師なしか半教師あり訓練が望ましいです、何故ならばラベル付けされたデータはしばしば十分でないからです。VAE 検知器はそれが受け取る入力を再構築しようとします。入力データが上手く再構築されない場合、再構築エラーは高くそしてデータは外れ値としてフラグ立てできます。再構築エラーは、入力と再構築されたインスタンスの間の平均二乗誤差 (MSE, mean squared error) か、入力と再構築されたインスタンスの両者が同じプロセスで生成される確率として測定されます。アルゴリズムは表形式か画像データのために適合します。

 

データセット

CIFAR10 は 10 クラスに渡り均等に分配された 60,000 の 32 x 32 RGB 画像から成ります。

import logging
import matplotlib.pyplot as plt
import numpy as np
import tensorflow as tf
tf.keras.backend.clear_session()
from tensorflow.keras.layers import Conv2D, Conv2DTranspose, Dense, Layer, Reshape, InputLayer
from tqdm import tqdm

from alibi_detect.models.tensorflow.losses import elbo
from alibi_detect.od import OutlierVAE
from alibi_detect.utils.fetching import fetch_detector
from alibi_detect.utils.perturbation import apply_mask
from alibi_detect.utils.saving import save_detector, load_detector
from alibi_detect.utils.visualize import plot_instance_score, plot_feature_outlier_image

logger = tf.get_logger()
logger.setLevel(logging.ERROR)

 

CIFAR10 データをロードする

train, test = tf.keras.datasets.cifar10.load_data()
X_train, y_train = train
X_test, y_test = test

X_train = X_train.astype('float32') / 255
X_test = X_test.astype('float32') / 255
print(X_train.shape, y_train.shape, X_test.shape, y_test.shape)
(50000, 32, 32, 3) (50000, 1) (10000, 32, 32, 3) (10000, 1)

 

外れ値検知器をロードまたは定義する

examples ノートブックで使用される事前訓練済みの外れ値と敵対的検知器は ここ で見つかります。組込みの fetch_detector 関数を利用できます、これは事前訓練モデルをローカルディレクトリ filepath にセーブして検知器をロードします。代わりに、スクラッチから検知器を訓練することができます :

load_outlier_detector = False
filepath = 'model_vae_cifar10'  # change to directory where model is downloaded
if load_outlier_detector:  # load pretrained outlier detector
    detector_type = 'outlier'
    dataset = 'cifar10'
    detector_name = 'OutlierVAE'
    od = fetch_detector(filepath, detector_type, dataset, detector_name)
    filepath = os.path.join(filepath, detector_name)
else:  # define model, initialize, train and save outlier detector
    latent_dim = 1024

    encoder_net = tf.keras.Sequential(
      [
          InputLayer(input_shape=(32, 32, 3)),
          Conv2D(64, 4, strides=2, padding='same', activation=tf.nn.relu),
          Conv2D(128, 4, strides=2, padding='same', activation=tf.nn.relu),
          Conv2D(512, 4, strides=2, padding='same', activation=tf.nn.relu)
      ])

    decoder_net = tf.keras.Sequential(
      [
          InputLayer(input_shape=(latent_dim,)),
          Dense(4*4*128),
          Reshape(target_shape=(4, 4, 128)),
          Conv2DTranspose(256, 4, strides=2, padding='same', activation=tf.nn.relu),
          Conv2DTranspose(64, 4, strides=2, padding='same', activation=tf.nn.relu),
          Conv2DTranspose(3, 4, strides=2, padding='same', activation='sigmoid')
      ])

    # initialize outlier detector
    od = OutlierVAE(threshold=.015,  # threshold for outlier score
                    score_type='mse',  # use MSE of reconstruction error for outlier detection
                    encoder_net=encoder_net,  # can also pass VAE model instead
                    decoder_net=decoder_net,  # of separate encoder and decoder
                    latent_dim=latent_dim,
                    samples=2)
    # train
    od.fit(X_train,
           loss_fn=elbo,
           cov_elbo=dict(sim=.05),
           epochs=50,
           verbose=True)

    # save the trained outlier detector
    save_detector(od, filepath)

(訳注 : TF 2.5.0, 8 vCPUs)

782/782 [=] - 200s 254ms/step - loss: 3261.4951
782/782 [=] - 199s 254ms/step - loss: -2490.5888
782/782 [=] - 199s 254ms/step - loss: -3502.6840
782/782 [=] - 203s 259ms/step - loss: -3973.3844
782/782 [=] - 203s 259ms/step - loss: -4287.5373
782/782 [=] - 202s 258ms/step - loss: -4530.9941
782/782 [=] - 202s 257ms/step - loss: -4697.7253
782/782 [=] - 202s 257ms/step - loss: -4847.7671
782/782 [=] - 203s 260ms/step - loss: -4976.0743
782/782 [=] - 198s 253ms/step - loss: -5082.4453
782/782 [=] - 200s 254ms/step - loss: -5159.3614
782/782 [=] - 203s 259ms/step - loss: -5219.0779
782/782 [=] - 200s 255ms/step - loss: -5276.2153
782/782 [=] - 201s 256ms/step - loss: -5351.2658
782/782 [=] - 201s 257ms/step - loss: -5400.3945
782/782 [=] - 203s 258ms/step - loss: -5440.3792
(打ち切り)

(訳注 : TF 2.4.1, NVIDIA T4 GPU)

782/782 [=] - 39s 36ms/step - loss: 2861.8008
782/782 [=] - 29s 36ms/step - loss: -2660.4216
782/782 [=] - 29s 36ms/step - loss: -3656.3224
782/782 [=] - 29s 36ms/step - loss: -4132.3934
782/782 [=] - 29s 36ms/step - loss: -4442.0791
782/782 [=] - 29s 36ms/step - loss: -4647.3034
782/782 [=] - 29s 36ms/step - loss: -4835.5262
782/782 [=] - 29s 36ms/step - loss: -4972.2206
782/782 [=] - 29s 36ms/step - loss: -5072.7729
782/782 [=] - 29s 37ms/step - loss: -5150.4993
782/782 [=] - 29s 36ms/step - loss: -5216.9460
782/782 [=] - 29s 37ms/step - loss: -5288.1443
782/782 [=] - 29s 36ms/step - loss: -5347.1787
782/782 [=] - 29s 36ms/step - loss: -5415.1764
782/782 [=] - 29s 36ms/step - loss: -5458.5443
782/782 [=] - 29s 36ms/step - loss: -5507.7469
782/782 [=] - 29s 36ms/step - loss: -5530.3080
782/782 [=] - 29s 36ms/step - loss: -5575.4234
782/782 [=] - 29s 36ms/step - loss: -5614.9955
782/782 [=] - 29s 36ms/step - loss: -5635.2912
782/782 [=] - 29s 36ms/step - loss: -5672.6891
782/782 [=] - 29s 36ms/step - loss: -5689.9361
782/782 [=] - 29s 36ms/step - loss: -5719.1222
782/782 [=] - 29s 37ms/step - loss: -5734.5167
782/782 [=] - 29s 37ms/step - loss: -5761.0373
782/782 [=] - 29s 36ms/step - loss: -5770.7223
782/782 [=] - 29s 36ms/step - loss: -5797.3944
782/782 [=] - 29s 36ms/step - loss: -5813.8093
782/782 [=] - 29s 36ms/step - loss: -5825.7507
782/782 [=] - 29s 36ms/step - loss: -5842.4082
782/782 [=] - 29s 36ms/step - loss: -5852.7256
782/782 [=] - 29s 36ms/step - loss: -5870.8946
782/782 [=] - 29s 36ms/step - loss: -5877.0400
782/782 [=] - 29s 36ms/step - loss: -5887.7013
782/782 [=] - 29s 36ms/step - loss: -5898.9985
782/782 [=] - 29s 36ms/step - loss: -5910.3501
782/782 [=] - 29s 36ms/step - loss: -5918.1856
782/782 [=] - 29s 36ms/step - loss: -5930.0882
782/782 [=] - 29s 36ms/step - loss: -5940.8020
782/782 [=] - 29s 36ms/step - loss: -5944.5714
782/782 [=] - 29s 36ms/step - loss: -5952.0312
782/782 [=] - 29s 36ms/step - loss: -5963.3228
782/782 [=] - 29s 36ms/step - loss: -5966.7727
782/782 [=] - 29s 36ms/step - loss: -5970.3328
782/782 [=] - 29s 36ms/step - loss: -5976.2218
782/782 [=] - 29s 36ms/step - loss: -5977.2582
782/782 [=] - 29s 36ms/step - loss: -5990.5260
782/782 [=] - 29s 36ms/step - loss: -5997.9227
782/782 [=] - 29s 36ms/step - loss: -6001.5070
782/782 [=] - 29s 36ms/step - loss: -6005.6862

 

VAE モデルの品質を確認する

idx = 8
X = X_train[idx].reshape(1, 32, 32, 3)
X_recon = od.vae(X)
plt.imshow(X.reshape(32, 32, 3))
plt.axis('off')
plt.show()

plt.imshow(X_recon.numpy().reshape(32, 32, 3))
plt.axis('off')
plt.show()

 

元の CIFAR 画像で外れ値を確認する

X = X_train[:500]
print(X.shape)
(500, 32, 32, 3)
od_preds = od.predict(X,
                      outlier_type='instance',    # use 'feature' or 'instance' level
                      return_feature_score=True,  # scores used to determine outliers
                      return_instance_score=True)
print(list(od_preds['data'].keys()))
['instance_score', 'feature_score', 'is_outlier']

 

インスタンスレベルの外れ値スコアをプロットする

target = np.zeros(X.shape[0],).astype(int)  # all normal CIFAR10 training instances
labels = ['normal', 'outlier']
plot_instance_score(od_preds, target, labels, od.threshold)

 

予測を可視化する

X_recon = od.vae(X).numpy()
plot_feature_outlier_image(od_preds,
                           X,
                           X_recon=X_recon,
                           instance_ids=[8, 60, 100, 330],  # pass a list with indices of instances to display
                           max_instances=5,  # max nb of instances to display
                           outliers_only=False)  # only show outlier predictions

(訳注 : 下は実験結果)

 

摂動された CIFAR 画像で外れ値を予測する

CIFAR 画像を画像のパッチ (マスク) にランダムノイズを追加することにより摂動させます。n_mask_sizes の各マスクサイズについて、n_masks をサンプリングしてそれらを n_imgs 画像の各々に適用します。そしてマスクされたインスタンスで外れ値を予測します :

# nb of predictions per image: n_masks * n_mask_sizes
n_mask_sizes = 10
n_masks = 20
n_imgs = 50

マスクを定義して画像を得る :

mask_sizes = [(2*n,2*n) for n in range(1,n_mask_sizes+1)]
print(mask_sizes)
img_ids = np.arange(n_imgs)
X_orig = X[img_ids].reshape(img_ids.shape[0], 32, 32, 3)
print(X_orig.shape)
[(2, 2), (4, 4), (6, 6), (8, 8), (10, 10), (12, 12), (14, 14), (16, 16), (18, 18), (20, 20)]
(50, 32, 32, 3)

インスタンスレベルの外れ値スコアを計算します :

all_img_scores = []
for i in tqdm(range(X_orig.shape[0])):
    img_scores = np.zeros((len(mask_sizes),))
    for j, mask_size in enumerate(mask_sizes):
        # create masked instances
        X_mask, mask = apply_mask(X_orig[i].reshape(1, 32, 32, 3),
                                  mask_size=mask_size,
                                  n_masks=n_masks,
                                  channels=[0,1,2],
                                  mask_type='normal',
                                  noise_distr=(0,1),
                                  clip_rng=(0,1))
        # predict outliers
        od_preds_mask = od.predict(X_mask)
        score = od_preds_mask['data']['instance_score']
        # store average score over `n_masks` for a given mask size
        img_scores[j] = np.mean(score)
    all_img_scores.append(img_scores)

 

外れ値スコア vs. マスクサイズ

x_plt = [mask[0] for mask in mask_sizes]
for ais in all_img_scores:
    plt.plot(x_plt, ais)
    plt.xticks(x_plt)
plt.title('Outlier Score All Images for Increasing Mask Size')
plt.xlabel('Mask size')
plt.ylabel('Outlier Score')
plt.show()

ais_np = np.zeros((len(all_img_scores), all_img_scores[0].shape[0]))
for i, ais in enumerate(all_img_scores):
    ais_np[i, :] = ais
ais_mean = np.mean(ais_np, axis=0)
plt.title('Mean Outlier Score All Images for Increasing Mask Size')
plt.xlabel('Mask size')
plt.ylabel('Outlier score')
plt.plot(x_plt, ais_mean)
plt.xticks(x_plt)
plt.show()

 

インスタンスレベルの外れ値を調査する

i = 8  # index of instance to look at
plt.plot(x_plt, all_img_scores[i])
plt.xticks(x_plt)
plt.title('Outlier Scores Image {} for Increasing Mask Size'.format(i))
plt.xlabel('Mask size')
plt.ylabel('Outlier score')
plt.show()

マスクされた画像の再構築とチャネル毎の外れ値スコア :

all_X_mask = []
X_i = X_orig[i].reshape(1, 32, 32, 3)
all_X_mask.append(X_i)
# apply masks
for j, mask_size in enumerate(mask_sizes):
    # create masked instances
    X_mask, mask = apply_mask(X_i,
                              mask_size=mask_size,
                              n_masks=1,  # just 1 for visualization purposes
                              channels=[0,1,2],
                              mask_type='normal',
                              noise_distr=(0,1),
                              clip_rng=(0,1))
    all_X_mask.append(X_mask)
all_X_mask = np.concatenate(all_X_mask, axis=0)
all_X_recon = od.vae(all_X_mask).numpy()
od_preds = od.predict(all_X_mask)

可視化します :

plot_feature_outlier_image(od_preds,
                           all_X_mask,
                           X_recon=all_X_recon,
                           max_instances=all_X_mask.shape[0],
                           n_channels=3)

(訳注 : 下は実験結果)

 

特徴のサブセットで外れ値を予測する

外れ値検知器の sensitivity (感度) は閾値を通してだけでなくインスタンスレベルの外れ値スコア計算のために使用される特徴のパーセンテージを選択することによっても制御できます。例えば、特徴の 40% が閾値を越える平均外れ値スコアを持つ場合外れ値であるとフラグ立てすることを望むかもしれません。これは predict 関数の outlier_perc 引数を通して可能です。それは降順の外れ値スコア順序でソートされた、外れ値検知のために使用される特徴のパーセンテージを指定します。

perc_list = [20, 40, 60, 80, 100]

all_perc_scores = []
for perc in perc_list:
    od_preds_perc = od.predict(all_X_mask, outlier_perc=perc)
    iscore = od_preds_perc['data']['instance_score']
    all_perc_scores.append(iscore)

外れ値スコア vs. マスクサイズと使用された特徴サイズのパーセンテージを可視化します :

x_plt = [0] + x_plt
for aps in all_perc_scores:
    plt.plot(x_plt, aps)
    plt.xticks(x_plt)
plt.legend(perc_list)
plt.title('Outlier Score for Increasing Mask Size and Different Feature Subsets')
plt.xlabel('Mask Size')
plt.ylabel('Outlier Score')
plt.show()

 

外れ値の閾値を推論する

良い閾値を見つけることは技巧的であり得ます、何故ならばそれらは典型的には解釈することが容易でないからです。infer_threshold メソッドは sensible な値を見つけるのに役立ちます。インスタンスのバッチ X を渡してそれらの何パーセントを正常であると考えるかを threshold_perc を通して指定する必要があります。

print('Current threshold: {}'.format(od.threshold))
od.infer_threshold(X, threshold_perc=99)  # assume 1% of the training data are outliers
print('New threshold: {}'.format(od.threshold))
Current threshold: 0.015
New threshold: 0.010383214280009267

(訳注 : 実験結果)

Current threshold: 0.015
New threshold: 0.0018019021837972088
 

以上



ClassCat® Chatbot

人工知能開発支援
クラスキャットは 人工知能研究開発支援 サービスを提供しています :
  • テクニカルコンサルティングサービス
  • 実証実験 (プロトタイプ構築)
  • アプリケーションへの実装
  • 人工知能研修サービス
◆ お問合せ先 ◆
クラスキャット
セールス・インフォメーション
E-Mail:sales-info@classcat.com