ホーム » sales-info の投稿 (ページ 3)

作者アーカイブ: sales-info

Prophet 1.0 : 季節性、休日効果とリグレッサー

Prophet 1.0 : 季節性、休日効果とリグレッサー (翻訳/解説)
翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 07/10/2021 (1.0)

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

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

 

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

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

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

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

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

 

 

Prophet 1.0 : 季節性、休日効果とリグレッサー

休日と特定のイベントをモデリング

モデル化したい休日や他の繰り返し発生する (= recurring) イベントを持つ場合、それらからデータフレームを作成しなければなりません。それは 2 つのカラム (holiday と ds) と休日の各発生のために行を持ちます。それは過去 (後方に履歴データがある限り) と未来 (予測が行なわれている限り) の両者の、休日の総ての発生を含まなければなりません、それらが未来に繰り返されない場合、Prophet はそれらをモデル化して予測ではそれらを含めません。

カラムに lower_window と upper_window を含めることができます、これらは休日を日付周りの [lower_window, upper_window] days に拡張します。例えば、クリスマスに加えてクリスマス・イブを含めることを望んだ場合、lower_window=-1,upper_window=0 を含めます。感謝祭に加えてブラックフライデーを使用したい場合には、 lower_window=0,upper_window=1 を含めます。下で説明されるように、各休日のために prior scale を個別に設定するためにカラム prior_scale を含めることもできます。

ここでは Peyton Manning のプレーオフ出場の総ての日付を含むデータフレームを作成します :

playoffs = pd.DataFrame({
  'holiday': 'playoff',
  'ds': pd.to_datetime(['2008-01-13', '2009-01-03', '2010-01-16',
                        '2010-01-24', '2010-02-07', '2011-01-08',
                        '2013-01-12', '2014-01-12', '2014-01-19',
                        '2014-02-02', '2015-01-11', '2016-01-17',
                        '2016-01-24', '2016-02-07']),
  'lower_window': 0,
  'upper_window': 1,
})
superbowls = pd.DataFrame({
  'holiday': 'superbowl',
  'ds': pd.to_datetime(['2010-02-07', '2014-02-02', '2016-02-07']),
  'lower_window': 0,
  'upper_window': 1,
})
holidays = pd.concat((playoffs, superbowls))

上ではスーパーボウル days をプレーオフ・ゲームとスーパーボウルゲームの両者として含めました。これは、スパーボウル効果はプレーオフ効果の上に追加の加算的ボーナスであることを意味します。

ひとたびテーブルが作成されれば、休日効果はそれらを holidays 引数で渡すことにより予測に含まれます。ここでは クイックスタート からの Peyton Manning データでそれを行ないます :

m = Prophet(holidays=holidays)
forecast = m.fit(df).predict(future)

休日効果は予測データフレーム内で見ることができます :

forecast[(forecast['playoff'] + forecast['superbowl']).abs() > 0][
        ['ds', 'playoff', 'superbowl']][-10:]

休日効果はまた成分プロット内にも現れます、そこではプレーオフ出場周りの日々でスパイクがあり、スーパーボウルのためには特に巨大なスパイクがあります :

fig = m.plot_components(forecast)

スーパボウル休日成分だけをプロットする plot_forecast_component(m, forecast, ‘superbowl’) のように、(Python で prophet.plot からインポートされる) plot_forecast_component 関数を使用して個々の休日をプロットできます。

 

組込みの国の休日

add_country_holidays メソッド (Python) や関数 (R) を使用して国固有の休日の組込みコレクションを使用できます。国の名前が指定されると、上で説明された holidays 引数を通して指定された任意の休日に加えて、その国のための主要な休日が含まれます。

m = Prophet(holidays=holidays)
m.add_country_holidays(country_name='US')
m.fit(df)

モデルの train_holiday_names (Python) か train.holiday.names (R) 属性を見ることによりどの休日が含まれたかを見ることができます :

m.train_holiday_names
0                         playoff
1                       superbowl
2                  New Year's Day
3      Martin Luther King Jr. Day
4           Washington's Birthday
5                    Memorial Day
6                Independence Day
7                       Labor Day
8                    Columbus Day
9                    Veterans Day
10                   Thanksgiving
11                  Christmas Day
12       Christmas Day (Observed)
13        Veterans Day (Observed)
14    Independence Day (Observed)
15      New Year's Day (Observed)
dtype: object

各国のための休日は Python の holidays パッケージにより提供されます。利用可能な国のリストと使用する国名はページ : https://github.com/dr-prodigy/python-holidays で利用可能です。それらの国に加えて、Prophet はこれらの国のための休日を含みます : ブラジル (BR), インドネシア (ID), インド (IN), マレーシア (MY), ベトナム (VN), タイ (TH), フィリッピン (PH), パキスタン (PK), バングラディシュ (BD), エジプト (EG), 中国 (CN), そしてロシア (RU), 韓国 (KR), ベラルーシ (BY), そしてアラブ首長国連邦 (AE) です。

※ 訳注 : 日本 (JP/JPN)

Python では、殆どの休日は決定論的に計算されますので任意の日付範囲で利用可能です ; 日付がその国でサポートされる範囲に入らない場合、警告が上げられます。R では、休日の日付は 1995 から 2044 年まで計算されて data-raw/generated_holidays.csv としてパッケージにストアされます。より広い日付範囲が必要であれば、このスクリプトはそのファイルを別の日付範囲と置き換えるために使用できます : https://github.com/facebook/prophet/blob/master/python/scripts/generate_holidays_file.py

上のように、国レベルの休日は成分プロットで現れます :

forecast = m.predict(future)
fig = m.plot_components(forecast)

 

季節性のフーリエ次数

季節性は部分フーリエ和を使用して推定されます。完全な詳細は 論文 を、そして部分フーリエ和がどのように任意の周期信号を近似できるかの例示のためには Wikipedia のこの図 を見てください。部分和の項の数 (次数) はどのくらい素早く周期性が変化するかを決定するパラメータです。これを説明するために、クイックスタート からの Peyton Manning データを考えます。年毎の季節性のためのデフォルトのフーリエ次数は 10 で、これはこの適合を生成します :

from prophet.plot import plot_yearly
m = Prophet().fit(df)
a = plot_yearly(m)

デフォルト値はしばしば適切ですが、季節性がより高い頻度の変化に適合する必要があるとき、それらは増やすことができます、そして一般に滑らかでなくなります。フーリエ次数はモデルをインスタンス化するとき各組込みの季節性のために指定できます、ここではそれは 20 に増やされています :

from prophet.plot import plot_yearly
m = Prophet(yearly_seasonality=20).fit(df)
a = plot_yearly(m)

フーリエ項の数を増やすことは季節性を変化するサイクルに高速に適合させることを可能にしますが、過剰適合に繋がる可能性もあります : N 個のフーリエ項はサイクルをモデリングするために使用される 2N 変数に相当します。

 

カスタム季節性を指定する

Prophet は時系列が 2 つのサイクル長を越える場合、デフォルトで weekly と yearly の季節性に適合させます。それはまた部分的な daily 時系列のために daily の季節性にも適合させます。add_seasonality メソッド (Python) or 関数 (R) を使用して他の季節性 (monthly, quarterly, hourly) を追加することができます。

この関数への入力は名前、季節性の期間 (in days)、そして季節性のためのフーリエ次数です。参考までに、デフォルトでは Prophet は weekly 季節性のために 3 のフーリエ次数をそして yearly 季節性のために 10 を使用します。add_seasonality へのオプションの入力はその季節性成分のための prior scale です – これは下で議論されます。

例として、ここでは クイックスタート から Peyton Manning データを適合させますが、weekly 季節性を monthly 季節性で置き換えます。そして monthly 季節性は成分プロットに現れます :

m = Prophet(weekly_seasonality=False)
m.add_seasonality(name='monthly', period=30.5, fourier_order=5)
forecast = m.fit(df).predict(future)
fig = m.plot_components(forecast)

 

他の要因に依存する季節性

ある場合には、夏の間は年の残りの間とは異なるような weekly 季節パターンや、週末 vs 平日で異なるような daily 季節パターンのように、季節性は他の要員に依存するかもしれません。これらのタイプの季節性は条件付き季節性を使用してモデル化できます。

クイックスタート からの Peyton Manning サンプルを考えます。デフォルトの weekly 季節性は weekly 季節性のパターンが一年を通して同じであることを仮定していますが、(毎日曜日に試合がある) オンシーズン中とオフシーズンの間では weekly 季節性のパターンが異なることを予期します。個別のオンシーズンとオフシーズン weekly 季節性を構築するために条件付き季節性を使用できます。

最初にデータフレームにブーリアン・カラムを追加します、これは各日付がオンシーズンかオフシーズンにあるかを示します :

def is_nfl_season(ds):
    date = pd.to_datetime(ds)
    return (date.month > 8 or date.month < 2)

df['on_season'] = df['ds'].apply(is_nfl_season)
df['off_season'] = ~df['ds'].apply(is_nfl_season)

そして組込みの weekly 季節性を無効にして、それを条件として指定されたこれらのカラムを持つ 2 つの weekly 季節性で置き換えます。これは、季節性は condition_name カラムが True である日付にだけ適用されることを意味します。また (そのために) 予測を行なう future データフレームにカラムを追加しなければなりません。

m = Prophet(weekly_seasonality=False)
m.add_seasonality(name='weekly_on_season', period=7, fourier_order=3, condition_name='on_season')
m.add_seasonality(name='weekly_off_season', period=7, fourier_order=3, condition_name='off_season')

future['on_season'] = future['ds'].apply(is_nfl_season)
future['off_season'] = ~future['ds'].apply(is_nfl_season)
forecast = m.fit(df).predict(future)
fig = m.plot_components(forecast)

両者の季節性が今では上の成分プロットに現れます。ゲームが毎土曜日にプレーされるオンシーズンの間、日曜日と月曜日に大規模な増加があることが見れますが、オフシーズンの間にはまったくありません。

 

休日と季節性のための Prior スケール

休日が過剰適合していることを見つける場合、パラメータ holidays_prior_scale を使用してそれらを滑らかにするために prior スケールを調整できます。デフォルトではこのパラメータは 10 で、それは正則化を殆ど提供しません。このパラメータを減少させると休日効果を減衰させます :

m = Prophet(holidays=holidays, holidays_prior_scale=0.05).fit(df)
forecast = m.predict(future)
forecast[(forecast['playoff'] + forecast['superbowl']).abs() > 0][
    ['ds', 'playoff', 'superbowl']][-10:]

休日効果の大きさは前に比べて減少しています、特にスーパーボウルについて、これは最も少ない観測を持ちました。季節性モデルがデータに適合する範囲を同様に調整するパラメータ seasonality_prior_scale があります。

prior スケールは holidays データフレーム内にカラム prior_scale を含めることにより個々の休日のために個別に設定できます。個々の季節性のための prior スケールは add_seasonality への引数として渡すことができます。例えば、weekly 季節性のためだけの prior スケールは次を使用して設定できます :

m = Prophet()
m.add_seasonality(
    name='weekly', period=7, fourier_order=3, prior_scale=0.1)

 

追加のリグレッサー

add_regressor メソッドか関数を使用して追加のリグレッサーがモデルの線形部分に追加できます。regressor 値を持つカラムは fitting と prediction データフレームの両者で存在する必要があります。例えば、NFL シーズンの間の日曜日に追加の効果を追加することができます。成分プロットでは、効果は ‘extra_regressors’ プロットに現れます :

def nfl_sunday(ds):
    date = pd.to_datetime(ds)
    if date.weekday() == 6 and (date.month > 8 or date.month < 2):
        return 1
    else:
        return 0
df['nfl_sunday'] = df['ds'].apply(nfl_sunday)

m = Prophet()
m.add_regressor('nfl_sunday')
m.fit(df)

future['nfl_sunday'] = future['ds'].apply(nfl_sunday)

forecast = m.predict(future)
fig = m.plot_components(forecast)

NFL サンデーはまた過去と未来の NFL サンデーのリストを作成して、上で説明された “holidays” インターフェイスを使用して処理されました。add_regressor 関数は追加の (= extra) 線形リグレッサーを定義するためにより一般的なインターフェイスを提供し、そして特にリグレッサーは二値インジケーターであることを必要としません。別の時系列をリグレッサーとして使用できるでしょうが、その future 値は知られなければなりません。

このノートブック は自転車の使用量の予測において追加のリグレッサーとして天気要因を使用する例を示し、そして他の時系列が追加のリグレッサーとして含まれる方法の優れた例を提供します。

add_regressor 関数は prior スケール (デフォルトでは holiday prior スケールが使用されます) とリグレッサーが標準化されているか否かを指定するためのオプション引数を持ちます - Python の help(Prophet.add_regressor) そして R の ?add_regressor で docstring を見てください。リグレッサーはモデル適合の前に追加されなければならないことに注意してください。Prophet はまたリグレッサーが履歴を通して定数である場合にはエラーを上げます、何故ならばそれから適合するものがないからです。

追加のリグレッサーは履歴と未来の日付の両者のために知られなければなりません。従ってそれは (nfl_sunday のように) 未来の値を知っているものか、あるいは他で別に予測された何かでなければなりません。上でリンクされたノートブックで使用される天気リグレッサーは未来値のために使用できる予測を持つ追加のリグレッサーの良いサンプルです。Prophet のような時系列モデルで予測された別の時系列をリグレッサーとして使用することもできます。例えば、r(t) が y(t) のためのリグレッサーとして含まれる場合、Prophet は r(t) を予測するために使用できてそしてその予測は y(t) を予測するとき未来値としてプラグインできます。このアプローチまわりの注意点は : これは r(t) が y(t) を予測するのが幾分容易でない限りは多分有用でありません。これは r(t) の予測誤差が y(t) の予測で誤差を生成するからです。これが有用であり得る一つの設定は階層型時系列内です、そこでは高い信号対雑音 (= signal-to-noise) を持ち予測が容易なトップレベルの予測があります。その予測は各下位レベルの系列のための予測に含めることができます。

追加のリグレッサーはモデルの線形成分に配置されますので、基礎的なモデルは、加法 or 乗法要因のいずれかとして時系列が追加のリグレッサーに依存します (乗法のためには次のセクション参照)。

 

追加のリグレッサーの係数

追加のリグレッサーの beta 係数を抽出するため、適合されたモデル上でユティリティ関数 regressor_coefficients を使用します (Python では from prophet.utilities import regressor_coefficients, R では prophet::regressor_coefficients)。各リグレッサーのための推定された beta 係数はリグレッサー値の単位増加に対する予測値の増加をおおよそ表します (返される係数は常に元のデータのスケールであることに注意してください)。mcmc_samples が指定される場合、各係数の信用区間 (= credible interval) も返されます、これは各レグレッサーが「統計的に有意である」かを識別するのに役立つことができます。

 

以上



Prophet 1.0 : トレンドの変化点

Prophet 1.0 : トレンドの変化点 (翻訳/解説)
翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 07/09/2021 (1.0)

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

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

 

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

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

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

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

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

 

 

Prophet 1.0 : トレンドの変化点

ドキュメントのこれまでのサンプルで、リアルタイムな時系列はその軌跡において急激な (= abrupt) 変化を頻繁に持つことに気付いたかもしれません。デフォルトでは、Prophet はこれらの変化点を自動的に検出してトレンドが適切に適応することを可能にします。けれども、このプロセスをより細かく制御したい場合は (e.g., Prophet がレート変化を見逃す、あるいは履歴でレート変化を過剰適合させている)、使用できる幾つかの入力引数があります。

 

Prophet の自動変化点検出

Prophet は多くの潜在的な変化点 (そこではレートは変化することが許容されています) を最初に指定することにより変化点を検出します。そしてそれはレート変化の大きさにスパースな事前分布を置きます (L1 正則化と同値) – これは Prophet がレートが変化できる可能性のある多くの場所を持ちますが、出来る限りそれらの少しを使用することを本質的に意味します。クイックスタート からの Peyton Manning 予測を考えます。デフォルトでは、Prophet は 25 の潜在的な変化点を指定します、これらは時系列の最初の 80% 内に一様に配置されます。この図の垂直線は潜在的な変化点が配置されたところを示します。

レートが変化し得る多くの場所を持ちますが、スパースな事前分布ゆえに、これらの変化点の殆どは使用されません。各変化点でのレート変更の大きさをプロットすることによりこれを見ることができます :

潜在的な変化点の数は引数 n_changepoints を使用して設定できますが、正則化を調整することでより良く調整されます。意義ある変化点の位置は次で可視化できます :

from prophet.plot import add_changepoints_to_plot
fig = m.plot(forecast)
a = add_changepoints_to_plot(fig.gca(), m, forecast)

デフォルトでは変化点は時系列の最初の 80% のためにだけ推論されます、これはトレンドを前方に推定するための多くの助走路を持ちそして時系列の最後で上下動の過剰適合を回避するためです。このデフォルトは多くの状況で動作しますが総てではありません、そして changepoint_range 引数を使用して変更できます。例えば、Python で m = Prophet(changepoint_range=0.9) または R で m <- prophet(changepoint.range = 0.9) は時系列の最初の 90% 内に潜在的な変化点を配置します。

 

トレンドの柔軟性の調整

トレンド変化が過剰適合 (overfit, 柔軟性が高すぎる) か過小適合 (underfit, 柔軟性が不十分) である場合、入力引数 changepoint_prior_scale を使用してスパースな事前分布の強さを調整できます。デフォルトでは、このパラメータは 0.05 に設定されています。それを増加することはトレンドをより柔軟にします :

m = Prophet(changepoint_prior_scale=0.5)
forecast = m.fit(df).predict(future)
fig = m.plot(forecast)

それを減少することはトレンドの柔軟性を低下させます :

m = Prophet(changepoint_prior_scale=0.001)
forecast = m.fit(df).predict(future)
fig = m.plot(forecast)

予測を可視化するとき、トレンドが過剰または過小適合に見える場合、このパラメータは必要に応じて調整できます。完全に自動化された設定では、このパラメータがどのように調整されるかの推奨については、交差検証についてのドキュメントを見てください。

 

変化点の位置を指定する

お望みであれば、自動変化点検出を使用するのではなく、changepoints 引数で潜在的な変化点の位置を手動で指定できます。そして傾き (= slope) の変化は前と同じスパースな正則化によって、これらのポイントでのみ許容されます。例えば、ポイントのグリッドを自動的に成されたように作成して、そのグリッドを変化を持つ傾向にあると知られている幾つかの特定の日付で増強できます。別の例として、ここで成されるように、変化点は日付の小さいセットに全体的に制限できます :

m = Prophet(changepoints=['2014-01-01'])
forecast = m.fit(df).predict(future)
fig = m.plot(forecast)

 

以上



Prophet 1.0 : 予測の飽和

Prophet 1.0 : 予測の飽和 (翻訳/解説)
翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 07/08/2021 (1.0)

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

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

 

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

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

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

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

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

 

 

Prophet 1.0 : 予測の飽和

成長の予測

デフォルトでは、Prophet はその予測のために線形モデルを使用します。成長を予測するとき、通常はある達成可能な最大ポイントがあります : 全体的な市場規模、人口規模 etc. です。これは環境収容力 (= carrying capacity) と呼ばれ、予測はこのポイントで飽和するはずです。

Prophet は、指定された環境収容力とともに、ロジスティック成長 (= logistic growth) トレンドモデルを使用して予測を行なうことを可能にします。Wikipedia の R (プログラミング言語) のページへの訪問のログ数でこれを例示します。

df = pd.read_csv('../examples/example_wp_log_R.csv')

カラム cap で環境収容力を指定しなければなりません。ここでは特定の値を仮定しますが、これは通常は市場規模についてのデータや専門知識を使用して設定されます。

df['cap'] = 8.5

注意すべき重要なことは cap はデータフレームの総ての行のために指定されなければならず、そしてそれは定数である必要はないことです。市場規模が拡大している場合、cap は増大するシークエンスである可能性があります。

そして前のようにモデルを適合させますが、ロジスティック growth を指定するために追加の引数を渡します :

m = Prophet(growth='logistic')
m.fit(df)

前のように未来の予測のためにデータフレームを作成しますが、未来の収容力も指定しなければなりません。ここでは収容力を履歴のものと同じ値で定数として保持し、そして未来の 5 年を予測します :

future = m.make_future_dataframe(periods=1826)
future['cap'] = 8.5
fcst = m.predict(future)
fig = m.plot(fcst)

ロジスティック関数は 0 の暗黙的な最小値を持ち、収容力で飽和するのと同じように 0 で飽和します。別の飽和最小値を指定することも可能です。

 

飽和最小値

ロジスティック成長モデルはまた飽和最小値も処理できます、これは cap カラムが最大値を指定するのと同じ方法でカラム floor で指定されます :

df['y'] = 10 - df['y']
df['cap'] = 6
df['floor'] = 1.5
future['cap'] = 6
future['floor'] = 1.5
m = Prophet(growth='logistic')
m.fit(df)
fcst = m.predict(future)
fig = m.plot(fcst)

飽和最小値でロジスティック成長トレンドを使用するために、最大収容力も指定されなければなりません。

 

以上



Prophet 1.0 : クイックスタート

Prophet 1.0 : クイックスタート (翻訳/解説)
翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 07/06/2021 (1.0)

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

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

 

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

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

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

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

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

 

 

Prophet 1.0 : クイックスタート

Python API

Prophet は sklearn モデル API に従います。Prophet クラスのインスタンスを作成してからその fit と predict メソッドを呼び出します。

Prophet への入力は常に 2 つのカラム : ds と y を持つデータフレームです。ds (datestamp) カラムは Pandas により想定される形式であるべきです、理想的には日付のための YYYY-MM-DD またはタイムスタンプのための YYYY-MM-DD HH:MM:SS です。y カラムは数値でなければなりません、そして予測したい測定値を表します。

例として、 Peyton Manning のための Wikipedia ページのログの daily ページビューの時系列を見ましょう。このデータを R の Wikipediatrend パッケージを使用してかき集めました。Peyton Manning は良いサンプルを提供します、何故ならばそれは複数の季節性 (= seasonality)、成長率の変化、そして (Manning のプレーオフとスーパーボウルの出現のような) 特別な日をモデル化する機能のような Prophet の機能の幾つかを例示するからです。CSV は ここ で利用可能です。

最初にデータをインポートします :

import pandas as pd
from prophet import Prophet
df = pd.read_csv('../examples/example_wp_log_peyton_manning.csv')
df.head()

df.tail()

df.count()
ds    2905
y     2905
dtype: int64

新しい Prophet オブジェクトをインスタンス化することによりモデルを適合させます。予測手続きへの任意の設定はコンストラクタに渡されます。そしてその fit メソッドを呼び出して履歴データフレームを渡します。フィッティングには 1-5 秒かかるはずです。

m = Prophet()
m.fit(df)

それから予測は (そのために予測が成される) 日付を含むカラム ds を持つデータフレーム上で行なわれます。ヘルパーメソッド Prophet.make_future_dataframe を使用して指定された日数だけ未来に拡張する適切なデータフレームを得ることができます。デフォルトではそれは履歴からの日付も含みますので、モデルが適合していることも分かります。

future = m.make_future_dataframe(periods=365)
future.tail()

predict メソッドは future の各行に予測された値を割当てます、それは yhat と名前付けられます。履歴日付を渡す場合、それは in-sample な適合を提供します。ここで forecast オブジェクトは新しいデータフレームで、それは予測を持つカラム yhat と、成分と不確かな interval のためのカラムを含みます。

forecast = m.predict(future)
forecast[['ds', 'yhat', 'yhat_lower', 'yhat_upper']].tail()

Prophet.plot メソッドを呼び出して forecast データフレームを渡すことにより予測をプロットできます。

fig1 = m.plot(forecast)

予測成分を見ることを望む場合、Prophet.plot_components メソッドを利用できます。デフォルトでは、時系列のトレンド、yearly 周期性と weekly 周期性を見ます。休日を含める場合、ここでそれらも見ます。

fig2 = m.plot_components(forecast)

予測と成分の対話的な図は plotly で作成できます。plotly 4.0 かそれ以上を個別にインストールする必要があります、それはデフォルトでは prophet と共にインストールされないからです。notebook と ipywidgets パッケージをインストールする必要もあります。

from prophet.plot import plot_plotly, plot_components_plotly

plot_plotly(m, forecast)

plot_components_plotly(m, forecast)

各メソッドのために利用可能なオプションについての詳細は docstrings で利用可能です、例えば help(Prophet) や help(Prophet.fit) を通してです。CRAN の R リファレンスマニュアルは利用可能な関数の総ての正確なリストを提供します、それらの各々は Python の同値を持ちます。

 

以上



Alibi Detect 0.7 : Examples : 外れ値、敵対的 & ドリフト検知 on CIFAR10

Alibi Detect 0.7 : Examples : 外れ値、敵対的 & ドリフト検知 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 : 外れ値、敵対的 & ドリフト検知 on CIFAR10

0. データセット

CIFAR10 は 10 クラス : 飛行機、自動車、鳥、猫、鹿、犬、カエル、馬、船とトラック – に渡り均等に分配された 60,000 の 32 x 32 RGB 画像から成ります。

# imports and plot examples
import matplotlib.pyplot as plt
%matplotlib inline
import tensorflow as tf

(X_train, y_train), (X_test, y_test) = tf.keras.datasets.cifar10.load_data()
X_train = X_train.astype('float32') / 255
X_test = X_test.astype('float32') / 255
y_train = y_train.astype('int64').reshape(-1,)
y_test = y_test.astype('int64').reshape(-1,)
print('Train: ', X_train.shape, y_train.shape)
print('Test: ', X_test.shape, y_test.shape)

plt.figure(figsize=(10, 10))
n = 4
for i in range(n ** 2):
    plt.subplot(n, n, i + 1)
    plt.imshow(X_train[i])
    plt.axis('off')
plt.show();
Train:  (50000, 32, 32, 3) (50000,)
Test:  (10000, 32, 32, 3) (10000,)

 

1. 変分オートエンコーダ (VAE) による外れ値検知

メソッド

簡単に言えば :

  • 正常データ上で VAE を訓練するのでそれは inlier を上手く再構築できます。
  • VAE が incoming リクエストを上手く再構築できないのであれば?外れ値です!

VAE のより多くのリソース: 論文優れたブログ投稿

画像ソース: https://lilianweng.github.io/lil-log/2018/08/12/from-autoencoder-to-beta-vae.html

# more imports
import logging
import numpy as np
import os

from tensorflow.keras.layers import Conv2D, Conv2DTranspose, Dense
from tensorflow.keras.layers import Flatten, Layer, Reshape, InputLayer
from tensorflow.keras.regularizers import l1

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)

 

検知器をロードまたはスクラッチから訓練する

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

load_pretrained = False
%%time
filepath = os.path.join(os.getcwd(), 'outlier')

if load_pretrained:  # load pre-trained 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

    # define encoder and decoder networks
    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
        encoder_net=encoder_net,  # can also pass VAE model instead
        decoder_net=decoder_net,  # of separate encoder and decoder
        latent_dim=latent_dim
    )

    # train
    od.fit(X_train, epochs=50, verbose=True)

    # save the trained outlier detector
    save_detector(od, filepath)
782/782 [=] - 32s 36ms/step - loss: 3328.0921
782/782 [=] - 29s 36ms/step - loss: -2477.8159
782/782 [=] - 29s 36ms/step - loss: -3505.9077
782/782 [=] - 29s 36ms/step - loss: -4018.4684
782/782 [=] - 29s 36ms/step - loss: -4339.1812
782/782 [=] - 29s 36ms/step - loss: -4564.4784
782/782 [=] - 29s 36ms/step - loss: -4752.0325
782/782 [=] - 29s 36ms/step - loss: -4909.1439
782/782 [=] - 29s 36ms/step - loss: -5027.7148
782/782 [=] - 29s 36ms/step - loss: -5108.3359
782/782 [=] - 29s 36ms/step - loss: -5183.5322
782/782 [=] - 29s 36ms/step - loss: -5221.8213
782/782 [=] - 29s 36ms/step - loss: -5286.5078
782/782 [=] - 29s 36ms/step - loss: -5336.4698
782/782 [=] - 29s 36ms/step - loss: -5390.1145
782/782 [=] - 29s 36ms/step - loss: -5435.4759
782/782 [=] - 29s 36ms/step - loss: -5469.5508
782/782 [=] - 29s 36ms/step - loss: -5513.7060
782/782 [=] - 29s 36ms/step - loss: -5546.3630
782/782 [=] - 29s 36ms/step - loss: -5586.9172
782/782 [=] - 29s 36ms/step - loss: -5604.6617
782/782 [=] - 29s 36ms/step - loss: -5638.2204
782/782 [=] - 29s 36ms/step - loss: -5657.4971
782/782 [=] - 29s 36ms/step - loss: -5684.9612
782/782 [=] - 29s 36ms/step - loss: -5706.7390
782/782 [=] - 29s 36ms/step - loss: -5719.2535
782/782 [=] - 29s 36ms/step - loss: -5742.7461
782/782 [=] - 29s 36ms/step - loss: -5760.9044
782/782 [=] - 29s 36ms/step - loss: -5777.8526
782/782 [=] - 29s 36ms/step - loss: -5793.0808
782/782 [=] - 29s 36ms/step - loss: -5803.5456
782/782 [=] - 29s 36ms/step - loss: -5822.1962
782/782 [=] - 29s 36ms/step - loss: -5821.3968
782/782 [=] - 29s 36ms/step - loss: -5847.5206
782/782 [=] - 29s 36ms/step - loss: -5855.4035
782/782 [=] - 29s 36ms/step - loss: -5866.9793
782/782 [=] - 29s 36ms/step - loss: -5879.4730
782/782 [=] - 29s 36ms/step - loss: -5885.7166
782/782 [=] - 29s 36ms/step - loss: -5895.9667
782/782 [=] - 29s 36ms/step - loss: -5905.9128
782/782 [=] - 29s 36ms/step - loss: -5913.2937
782/782 [=] - 29s 36ms/step - loss: -5922.4864
782/782 [=] - 29s 36ms/step - loss: -5930.4101
782/782 [=] - 29s 36ms/step - loss: -5935.5514
782/782 [=] - 29s 36ms/step - loss: -5942.9960
782/782 [=] - 29s 36ms/step - loss: -5953.9588
782/782 [=] - 29s 36ms/step - loss: -5959.2278
782/782 [=] - 29s 36ms/step - loss: -5962.3586
782/782 [=] - 29s 36ms/step - loss: -5967.2992
782/782 [=] - 29s 36ms/step - loss: -5975.1563
Directory /home/ubuntu/ws.alibi_detect/outlier does not exist and is now created.
CPU times: user 24min 6s, sys: 22.3 s, total: 24min 28s
Wall time: 24min 8s

モデルが in-distribution 訓練データを何とか再構築できるかを確認しましょう :

# plot original and reconstructed instance
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()

 

閾値を設定する

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

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

 

外れ値を作成して検知する

元のインスタンスにランダムノイズマスクを適用することにより幾つかの外れ値を作成できます :

np.random.seed(0)

i = 1

# create masked instance
x = X_test[i].reshape(1, 32, 32, 3)
x_mask, mask = apply_mask(
    x,
    mask_size=(8,8),
    n_masks=1,
    channels=[0,1,2],
    mask_type='normal',
    noise_distr=(0,1),
    clip_rng=(0,1)
)

# predict outliers and reconstructions
sample = np.concatenate([x_mask, x])
preds = od.predict(sample)
x_recon = od.vae(sample).numpy()
# check if outlier and visualize outlier scores
labels = ['No!', 'Yes!']
print(f"Is original outlier? {labels[preds['data']['is_outlier'][1]]}")
print(f"Is perturbed outlier? {labels[preds['data']['is_outlier'][0]]}")
plot_feature_outlier_image(preds, sample, x_recon, max_instances=1)
Is original outlier? No!
Is perturbed outlier? Yes!

 

検知器を配備する

このサンプルのオープンソース配備プラットフォーム Seldon Core と (サーバレス・コンポーネントがイベントストリームに接続されることを可能にする) イベント・ベースのプロジェクト Knative を使用します。Seldon Core payload logger はモデルリクエストを含むイベントを Knative に送ります、これはこれらを外れ値、ドリフトや敵対的検知モジュールのようなサーバレス・コンポーネントに委託することができます。更に例えばアラート or ストレージ・モジュールに前方に送るためにこれらのコンポーネントにより生成されたイベントを供給するためにイベント・コンポーネントが追加できます。これらは非同期に発生します。

既に Seldon Core をインストールして DigitalOcean 上クラスタを構成しました。スクラッチから総てをセットアップする構成ステップは このサンプル・ノートブック で詳述されています。

最初に Istio Ingress Gatewaw の IP アドレスを取得します。これは Istio が LoadBalancer とともにインストールされていることを仮定しています。

CLUSTER_IPS=!(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
CLUSTER_IP=CLUSTER_IPS[0]
print(CLUSTER_IP)
188.166.139.197
SERVICE_HOSTNAMES=!(kubectl get ksvc vae-outlier -o jsonpath='{.status.url}' | cut -d "/" -f 3)
SERVICE_HOSTNAME_VAEOD=SERVICE_HOSTNAMES[0]
print(SERVICE_HOSTNAME_VAEOD)
vae-outlier.default.example.com

配備されたモデルの予測のために幾つかのユティリティ関数を定義します。

import json
import requests
from typing import Union

classes = ('plane', 'car', 'bird', 'cat', 'deer',
           'dog', 'frog', 'horse', 'ship', 'truck')

def predict(x: np.ndarray) -> Union[str, list]:
    """ Model prediction. """
    formData = {
    'instances': x.tolist()
    }
    headers = {}
    res = requests.post(
        'http://'+CLUSTER_IP+'/seldon/default/tfserving-cifar10/v1/models/resnet32/:predict',
        json=formData,
        headers=headers
    )
    if res.status_code == 200:
        return classes[np.array(res.json()["predictions"])[0].argmax()]
    else:
        print("Failed with ",res.status_code)
        return []


def outlier(x: np.ndarray) -> Union[dict, list]:
    """ Outlier prediction. """
    formData = {
    'instances': x.tolist()
    }
    headers = {
        "Alibi-Detect-Return-Feature-Score": "true",
        "Alibi-Detect-Return-Instance-Score": "true"
    }
    headers["Host"] = SERVICE_HOSTNAME_VAEOD
    res = requests.post('http://'+CLUSTER_IP+'/', json=formData, headers=headers)
    if res.status_code == 200:
        od = res.json()
        od["data"]["feature_score"] = np.array(od["data"]["feature_score"])
        od["data"]["instance_score"] = np.array(od["data"]["instance_score"])
        return od
    else:
        print("Failed with ",res.status_code)
        return []


def show(x: np.ndarray) -> None:
    plt.imshow(x.reshape(32, 32, 3))
    plt.axis('off')
    plt.show()

元のインスタンス上で予測を行ないましょう :

show(x)
predict(x)

'ship'

外れ値検知器の出力のためにメッセージ dumper を確認しましょう :

res=!kubectl logs $(kubectl get pod -l serving.knative.dev/configuration=message-dumper -o jsonpath='{.items[0].metadata.name}') user-container
data = []
for i in range(0,len(res)):
    if res[i] == 'Data,':
        data.append(res[i+1])
j = json.loads(json.loads(data[0]))
print("Outlier?",labels[j["data"]["is_outlier"]==[1]])
Outlier? No!

そして摂動されたインスタンスで予測を行ないます :

show(x_mask)
predict(x_mask)

'ship'

予測は依然として正しいですが、インスタンスは明らかに外れ値です :

res=!kubectl logs $(kubectl get pod -l serving.knative.dev/configuration=message-dumper -o jsonpath='{.items[0].metadata.name}') user-container
data= []
for i in range(0,len(res)):
    if res[i] == 'Data,':
        data.append(res[i+1])
j = json.loads(json.loads(data[1]))
print("Outlier?",labels[j["data"]["is_outlier"]==[1]])
Outlier? Yes!
preds = outlier(x_mask)
plot_feature_outlier_image(preds, x_mask, X_recon=None)

 

2. 予測確率のマッチングによる敵対的検知

メソッド

敵対的検知器は Adversarial Detection and Correction by Matching Prediction Distributions に基づいています。通常、オートエンコーダは平均二乗再構築誤差のような $x$ と $x’$ の間の類似性を捕捉するのに適する損失関数を用いて、入力インスタンス $x$ を出来る限り正確に再構築するような変換 $T$ を見つけるために訓練されます。敵対的オートエンコーダ (AE) 検知器の新奇性は、オートエンコーダネットワークを訓練するために、モデルの出力空間の距離尺度に基づいた分類モデル依存な損失関数の使用に依存していることです。分類モデル $M$ が与えられたとき、オートエンコーダの重みは $x$ と $x’$ のモデル予測間の KL-ダイバージェンス を最小化するように最適化されます。再構築損失項 $x’$ の存在がない場合には、$x$ と $x’$ の近接性を気に掛けることなく、単純に予測確率 $M(x’)$ と $M(x)$ が一致することを確かなものにしようとします。その結果、$x’$ はモデル $M$ に関する異なる決定境界 shape によって $x$ とは異なる入力特徴空間の異なる領域に存在することが可能になります。$x$ 周りで効果的な注意深く作成された敵対的摂動は特徴空間の $x’$ の新しい位置には転送されませんので、従って攻撃は無力化されます。オートエンコーダの訓練は教師なしです、何故ならばモデル予測確率と通常の訓練インスタンスへのアクセスだけを必要とするからです。基礎となる敵対的攻撃についての知識を必要としません、そして分類器の重みは訓練の間凍結されます。

検知器は以下のように利用できます :

  • 敵対的スコア $S$ が計算されます。$S$ は $x$ と $x’$ のモデル予測間の K-L ダイバージェンスに等しいです。
  • $S$ が (明示的に定義されたか訓練データから推論された) 閾値を越える場合、インスタンスは敵対的とフラグ立てされます。
  • 敵対的インスタンスについては、モデル $M$ は予測を行なうために再構築されたインスタンス $x’$ を使用します。敵対的スコアが閾値の下であれば、モデルは元のインスタンス $x$ 上で予測を行ないます。

この手順は下の図で図示されます :

この方法は非常に柔軟で、モデル性能にネガティブな影響を与える一般的なデータ破損と摂動を検出するためにも使用できます。

# more imports
from sklearn.metrics import roc_curve, auc
from alibi_detect.ad import AdversarialAE
from alibi_detect.datasets import fetch_attack
from alibi_detect.utils.fetching import fetch_tf_model
from alibi_detect.utils.prediction import predict_batch

 

ユティリティ関数

# instance scaling and plotting utility functions
def scale_by_instance(X: np.ndarray) -> np.ndarray:
    mean_ = X.mean(axis=(1, 2, 3)).reshape(-1, 1, 1, 1)
    std_ = X.std(axis=(1, 2, 3)).reshape(-1, 1, 1, 1)
    return (X - mean_) / std_, mean_, std_


def accuracy(y_true: np.ndarray, y_pred: np.ndarray) -> float:
    return (y_true == y_pred).astype(int).sum() / y_true.shape[0]


def plot_adversarial(idx: list,
                     X: np.ndarray,
                     y: np.ndarray,
                     X_adv: np.ndarray,
                     y_adv: np.ndarray,
                     mean: np.ndarray,
                     std: np.ndarray,
                     score_x: np.ndarray = None,
                     score_x_adv: np.ndarray = None,
                     X_recon: np.ndarray = None,
                     y_recon: np.ndarray = None,
                     figsize: tuple = (10, 5)) -> None:

    # category map from class numbers to names
    cifar10_map = {0: 'airplane', 1: 'automobile', 2: 'bird', 3: 'cat', 4: 'deer', 5: 'dog',
                   6: 'frog', 7: 'horse', 8: 'ship', 9: 'truck'}

    nrows = len(idx)
    ncols = 3 if isinstance(X_recon, np.ndarray) else 2
    fig, ax = plt.subplots(nrows=nrows, ncols=ncols, figsize=figsize)

    n_subplot = 1
    for i in idx:

        # rescale images in [0, 1]
        X_adj = (X[i] * std[i] + mean[i]) / 255
        X_adv_adj = (X_adv[i] * std[i] + mean[i]) / 255
        if isinstance(X_recon, np.ndarray):
            X_recon_adj = (X_recon[i] * std[i] + mean[i]) / 255

        # original image
        plt.subplot(nrows, ncols, n_subplot)
        plt.axis('off')
        if i == idx[0]:
            if isinstance(score_x, np.ndarray):
                plt.title('CIFAR-10 Image \n{}: {:.3f}'.format(cifar10_map[y[i]], score_x[i]))
            else:
                plt.title('CIFAR-10 Image \n{}'.format(cifar10_map[y[i]]))
        else:
            if isinstance(score_x, np.ndarray):
                plt.title('{}: {:.3f}'.format(cifar10_map[y[i]], score_x[i]))
            else:
                plt.title('{}'.format(cifar10_map[y[i]]))
        plt.imshow(X_adj)
        n_subplot += 1

        # adversarial image
        plt.subplot(nrows, ncols, n_subplot)
        plt.axis('off')
        if i == idx[0]:
            if isinstance(score_x_adv, np.ndarray):
                plt.title('Adversarial \n{}: {:.3f}'.format(cifar10_map[y_adv[i]], score_x_adv[i]))
            else:
                plt.title('Adversarial \n{}'.format(cifar10_map[y_adv[i]]))
        else:
            if isinstance(score_x_adv, np.ndarray):
                plt.title('{}: {:.3f}'.format(cifar10_map[y_adv[i]], score_x_adv[i]))
            else:
                plt.title('{}'.format(cifar10_map[y_adv[i]]))
        plt.imshow(X_adv_adj)
        n_subplot += 1

        # reconstructed image
        if isinstance(X_recon, np.ndarray):
            plt.subplot(nrows, ncols, n_subplot)
            plt.axis('off')
            if i == idx[0]:
                plt.title('AE Reconstruction \n{}'.format(cifar10_map[y_recon[i]]))
            else:
                plt.title('{}'.format(cifar10_map[y_recon[i]]))
            plt.imshow(X_recon_adj)
            n_subplot += 1

    plt.show()


def plot_roc(roc_data: dict, figsize: tuple = (10,5)):
    plot_labels = []
    scores_attacks = []
    labels_attacks = []
    for k, v in roc_data.items():
        if 'original' in k:
            continue
        score_x = roc_data[v['normal']]['scores']
        y_pred = roc_data[v['normal']]['predictions']
        score_v = v['scores']
        y_pred_v = v['predictions']
        labels_v = np.ones(score_x.shape[0])
        idx_remove = np.where(y_pred == y_pred_v)[0]
        labels_v = np.delete(labels_v, idx_remove)
        score_v = np.delete(score_v, idx_remove)
        scores = np.concatenate([score_x, score_v])
        labels = np.concatenate([np.zeros(y_pred.shape[0]), labels_v]).astype(int)
        scores_attacks.append(scores)
        labels_attacks.append(labels)
        plot_labels.append(k)

    for sc_att, la_att, plt_la in zip(scores_attacks, labels_attacks, plot_labels):
        fpr, tpr, thresholds = roc_curve(la_att, sc_att)
        roc_auc = auc(fpr, tpr)
        label = str('{}: AUC = {:.2f}'.format(plt_la, roc_auc))
        plt.plot(fpr, tpr, lw=1, label='{}: AUC={:.4f}'.format(plt_la, roc_auc))

    plt.plot([0, 1], [0, 1], color='black', lw=1, linestyle='--')
    plt.xlim([0.0, 1.0])
    plt.ylim([0.0, 1.05])
    plt.xlabel('False Positive Rate')
    plt.ylabel('True Positive Rate')
    plt.title('{}'.format('ROC curve'))
    plt.legend(loc="lower right", ncol=1)
    plt.grid()
    plt.show()

 

データをリスケールする

ResNet 分類モデルはインスタンスにより標準化されたデータ上で訓練されます :

# rescale data
X_train, mean_train, std_train = scale_by_instance(X_train * 255.)
X_test, mean_test, std_test = scale_by_instance(X_test * 255.)
scale = (mean_train, std_train), (mean_test, std_test)

 

事前訓練済みの分類器をロードする

dataset = 'cifar10'
model = 'resnet56'
clf = fetch_tf_model(dataset, model)

テスト上の予測を確認します :

y_pred = predict_batch(clf, X_test, batch_size=32, return_class=True)
acc_y_pred = accuracy(y_test, y_pred)
print('Accuracy: {:.4f}'.format(acc_y_pred))
Accuracy: 0.9315

 

敵対的攻撃

Carlini-Wagner (C&W)SLIDE 攻撃の両者を調査します。以前に見つかった敵対的なインスタンスを事前訓練済みの ResNet-56 モデル上に単純にロードできます。攻撃は Foolbox を使用して生成されます :

# C&W attack
data_cw = fetch_attack(dataset, model, 'cw')
X_train_cw, X_test_cw = data_cw['data_train'], data_cw['data_test']
meta_cw = data_cw['meta'] # metadata with hyperparameters of the attack
# SLIDE attack
data_slide = fetch_attack(dataset, model, 'slide')
X_train_slide, X_test_slide = data_slide['data_train'], data_slide['data_test']
meta_slide = data_slide['meta']

分類器の精度が殆ど 0% に低下することを検証できます :

y_pred_cw = predict_batch(clf, X_test_cw, batch_size=32, return_class=True)
y_pred_slide = predict_batch(clf, X_test_slide, batch_size=32, return_class=True)
acc_y_pred_cw = accuracy(y_test, y_pred_cw)
acc_y_pred_slide = accuracy(y_test, y_pred_slide)
print('Accuracy: cw {:.4f} -- SLIDE {:.4f}'.format(acc_y_pred_cw, acc_y_pred_slide))
Accuracy: cw 0.0000 -- SLIDE 0.0001

幾つかの敵対的インスタンスを可視化しましょう :

# plot attacked instances
idx = [3, 4]
print('C&W attack...')
plot_adversarial(idx, X_test, y_pred, X_test_cw, y_pred_cw,
                 mean_test, std_test, figsize=(10, 10))
print('SLIDE attack...')
plot_adversarial(idx, X_test, y_pred, X_test_slide, y_pred_slide,
                 mean_test, std_test, figsize=(10, 10))

C&W 攻撃 …

SLIDE 攻撃 …

 

敵対的検知器をロードまたは訓練して評価する

Google Cloud Bucket から事前訓練済みの検知器を再度取得するかスクラッチから訓練できます :

load_pretrained = False
filepath = os.path.join(os.getcwd(), 'adversarial')

if load_pretrained:
    detector_type = 'adversarial'
    detector_name = 'base'
    ad = fetch_detector(filepath, detector_type, dataset, detector_name, model=model)
    filepath = os.path.join(filepath, detector_name)
else:  # train detector from scratch

    # define encoder and decoder networks
    encoder_net = tf.keras.Sequential(
            [
                InputLayer(input_shape=(32, 32, 3)),
                Conv2D(32, 4, strides=2, padding='same',
                       activation=tf.nn.relu, kernel_regularizer=l1(1e-5)),
                Conv2D(64, 4, strides=2, padding='same',
                       activation=tf.nn.relu, kernel_regularizer=l1(1e-5)),
                Conv2D(256, 4, strides=2, padding='same',
                       activation=tf.nn.relu, kernel_regularizer=l1(1e-5)),
                Flatten(),
                Dense(40)
            ]
        )

    decoder_net = tf.keras.Sequential(
        [
                InputLayer(input_shape=(40,)),
                Dense(4 * 4 * 128, activation=tf.nn.relu),
                Reshape(target_shape=(4, 4, 128)),
                Conv2DTranspose(256, 4, strides=2, padding='same',
                                activation=tf.nn.relu, kernel_regularizer=l1(1e-5)),
                Conv2DTranspose(64, 4, strides=2, padding='same',
                                activation=tf.nn.relu, kernel_regularizer=l1(1e-5)),
                Conv2DTranspose(3, 4, strides=2, padding='same',
                                activation=None, kernel_regularizer=l1(1e-5))
            ]
        )

    # initialise and train detector
    ad = AdversarialAE(
        encoder_net=encoder_net,
        decoder_net=decoder_net,
        model=clf
    )
    ad.fit(X_train, epochs=40, batch_size=64, verbose=True)

    # save the trained adversarial detector
    save_detector(ad, filepath)

検知器は最初に敵対的であり得る入力インスタンスを再構築します。そして再構築された入力は敵対的スコアを計算するために分類器に供給されます。スコアが閾値より上の場合、インスタンスは敵対的と分類されて検知器は攻撃を正そうとします。攻撃されたインスタンスを再構築してその上で予測を行なうとき何が起きるかを調べましょう :

X_recon_cw = predict_batch(ad.ae, X_test_cw, batch_size=32)
X_recon_slide = predict_batch(ad.ae, X_test_slide, batch_size=32)
y_recon_cw = predict_batch(clf, X_recon_cw, batch_size=32, return_class=True)
y_recon_slide = predict_batch(clf, X_recon_slide, batch_size=32, return_class=True)

攻撃された (インスタンス) vs. 再構築されたインスタンスの精度 :

acc_y_recon_cw = accuracy(y_test, y_recon_cw)
acc_y_recon_slide = accuracy(y_test, y_recon_slide)
print('Accuracy after C&W attack {:.4f} -- reconstruction {:.4f}'.format(acc_y_pred_cw, acc_y_recon_cw))
print('Accuracy after SLIDE attack {:.4f} -- reconstruction {:.4f}'.format(acc_y_pred_slide, acc_y_recon_slide))
Accuracy after C&W attack 0.0000 -- reconstruction 0.8048
Accuracy after SLIDE attack 0.0001 -- reconstruction 0.8159

検知器は攻撃後の精度を殆ど 0 % から 80% を上手く越えるまでに復旧します!攻撃的スコアを計算して再構築されたインスタンスの幾つかを調査できます :

score_x = ad.score(X_test, batch_size=32)
score_cw = ad.score(X_test_cw, batch_size=32)
score_slide = ad.score(X_test_slide, batch_size=32)
# visualize original, attacked and reconstructed instances with adversarial scores
print('C&W attack...')
idx = [10, 13, 14, 16, 17]
plot_adversarial(idx, X_test, y_pred, X_test_cw, y_pred_cw, mean_test, std_test,
                 score_x=score_x, score_x_adv=score_cw, X_recon=X_recon_cw,
                 y_recon=y_recon_cw, figsize=(10, 15))
print('SLIDE attack...')
idx = [23, 25, 27, 29, 34]
plot_adversarial(idx, X_test, y_pred, X_test_slide, y_pred_slide, mean_test, std_test,
                 score_x=score_x, score_x_adv=score_slide, X_recon=X_recon_slide,
                 y_recon=y_recon_slide, figsize=(10, 15))

C&W 攻撃 …

SLIDE 攻撃 …

ROC カーブと AUC 値は敵対的インスタンスを検知するための敵対的スコアの有効性を示します :

# plot roc curve
roc_data = {
    'original': {'scores': score_x, 'predictions': y_pred},
    'C&W': {'scores': score_cw, 'predictions': y_pred_cw, 'normal': 'original'},
    'SLIDE': {'scores': score_slide, 'predictions': y_pred_slide, 'normal': 'original'}
}

plot_roc(roc_data)

敵対的スコアのための閾値は infer_threshold を通して設定できます。インスタンスのバッチ X を渡してそれらの何パーセントを正常であると考えるかを threshold_perc を通して指定する必要があります。Assume we have only normal instances some of which the model has misclassified leading to a higher score if the reconstruction picked up features from the correct class or some might look adversarial in the first place. その結果、閾値を 95% に設定します :

ad.infer_threshold(X_test, threshold_perc=95, margin=0., batch_size=32)
print('Adversarial threshold: {:.4f}'.format(ad.threshold))
Adversarial threshold: 2.6722

検知器の正しい方法は Figure 1 の図を実行します。最初に敵対的スコアが計算されます。スコアが閾値を越えるインスタンスについては、再構築されたインスタンスの分類器予測が返されます。そうでなければ元の予測が保持されます。このメソッドは検知器のメタデータ、(バッチのインスタンスが敵対的であろうとなかろうと、) 訂正メカニズムを使用した分類器予測、そして元の予測と再構築された予測の両者を含む辞書を返します。幾つかの敵対的 (インスタンス) そして元のテストセットのインスタンスを含むバッチでこれを示しましょう :

n_test = X_test.shape[0]
np.random.seed(0)
idx_normal = np.random.choice(n_test, size=1600, replace=False)
idx_cw = np.random.choice(n_test, size=400, replace=False)

X_mix = np.concatenate([X_test[idx_normal], X_test_cw[idx_cw]])
y_mix = np.concatenate([y_test[idx_normal], y_test[idx_cw]])
print(X_mix.shape, y_mix.shape)
(2000, 32, 32, 3) (2000,)

モデル性能を確認しましょう :

y_pred_mix = predict_batch(clf, X_mix, batch_size=32, return_class=True)
acc_y_pred_mix = accuracy(y_mix, y_pred_mix)
print('Accuracy {:.4f}'.format(acc_y_pred_mix))
Accuracy 0.7380

これは訂正メカニズムで改良できます :

preds = ad.correct(X_mix, batch_size=32)
acc_y_corr_mix = accuracy(y_mix, preds['data']['corrected'])
print('Accuracy {:.4f}'.format(acc_y_corr_mix))
Accuracy 0.8205

論文 でハイライトされていて (temperature スケーリング隠れ層 K-L ダイバージェンス) Alibi Detect で実装されている幾つかの他のトリックがあります、これは敵対的検知器の性能を更にブーストできます。より詳細については この example ノートブック を確認してください。

 

以上



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

Alibi Detect 0.7 : Examples : AE 外れ値検知 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 : AE 外れ値検知 on CIFAR10

オートエンコーダ – 概要

オートエンコーダ (AE, Auto-Encoder) 外れ値検知器はラベル付けされていない、しかし通常 (inlier) データのバッチで最初に訓練されます。教師なし訓練が望ましいです、何故ならばラベル付けされたデータはしばしば十分でないからです。AE 検知器はそれが受け取る入力を再構築しようとします。入力データが上手く再構築されない場合、再構築エラーは高くそしてデータは外れ値としてフラグ立てできます。再構築エラーは入力と再構築されたインスタンスの間の平均二乗誤差 (MSE, mean squared error) として測定されます。

 

データセット

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

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

from alibi_detect.od import OutlierAE
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_ae_cifar10'  # change to (absolute) directory where model is downloaded
if load_outlier_detector:  # load pretrained outlier detector
    detector_type = 'outlier'
    dataset = 'cifar10'
    detector_name = 'OutlierAE'
    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
    encoding_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),
          Flatten(),
          Dense(encoding_dim,)
      ])

    decoder_net = tf.keras.Sequential(
      [
          InputLayer(input_shape=(encoding_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 = OutlierAE(threshold=.015,  # threshold for outlier score
                    encoder_net=encoder_net,  # can also pass AE model instead
                    decoder_net=decoder_net,  # of separate encoder and decoder
                    )
    # train
    od.fit(X_train,
           epochs=50,
           verbose=True)

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

 

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

idx = 8
X = X_train[idx].reshape(1, 32, 32, 3)
X_recon = od.ae(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()

 

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

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.ae(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.ae(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.0017021061840932791
 

以上



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
 

以上



Alibi Detect 0.7 : Examples : AEGMM と VAEGMM 外れ値検知 on TCP dump

Alibi Detect 0.7 : Examples : AEGMM と VAEGMM 外れ値検知 on KDD Cup ‘99 データセット (翻訳/解説)
翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 07/03/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 :AEGMM と VAEGMM 外れ値検知 on KDD Cup ‘99 データセット

AEGMM (オートエンコーディング・ガウス混合モデル) – 概要

オートエンコーディング・ガウス混合モデル (AEGMM) 外れ値検知器は 教師なし異常検知のための深層オートエンコーディング・ガウス混合モデル (Deep Autoencoding Gaussian Mixture Model for Unsupervised Anomaly Detection) 論文に従っています。エンコーダがデータを圧縮する一方で、デコーダにより生成された再構築されたインスタンスは入力と再構築の間の再構築誤差に基づいた追加の特徴を作成するために使用されます。これらの特徴はエンコーディングと結合されてガウス混合モデル (GMM) に供給されます。AEGMM 外れ値検知器はラベル付けされていない、しかし通常 (inlier) データのバッチ上で最初に訓練されます。教師なしか半教師あり訓練が望ましいです、何故ならばラベル付けされたデータはしばしば十分ではないからです。そして GMM のサンプルのエネルギーはインスタンスが外れ値 (高サンプル・エネルギー) か否か (低サンプル・エネルギー) を決定するために使用できます。このアルゴリズムは表形式か画像データのために適合します。

 

VAEGMM (変分オートエンコーディング・ガウス混合モデル) – 概要

変分オートエンコーディング・ガウス混合モデル (VAEGMM) 外れ値検知器は 教師なし異常検知のための深層オートエンコーディング・ガウス混合モデル (Deep Autoencoding Gaussian Mixture Model for Unsupervised Anomaly Detection) 論文に従っていますが、通常のオートエンコーダの代わりに VAE を使用します。エンコーダがデータを圧縮する一方で、デコーダにより生成された再構築されたインスタンスは入力と再構築の間の再構築誤差に基づいた追加の特徴を作成するために使用されます。これらの特徴はエンコーディングと結合されてガウス混合モデル (GMM) に供給されます。VAEGMM 外れ値検知器はラベル付けされていない、しかし通常 (inlier) データのバッチ上で最初に訓練されます。教師なしか半教師あり訓練が望ましいです、何故ならばラベル付けされたデータはしばしば十分ではないからです。そして GMM のサンプルのエネルギーはインスタンスが外れ値 (高サンプル・エネルギー) か否か (低サンプル・エネルギー) を決定するために使用できます。このアルゴリズムは表形式か画像データのために適合します。

 

データセット

典型的な U.S. 空軍 LAN をシミュレートした LAN の TCP dump データを使用して、外れ値検知器はコンピュータ・ネットワーク侵入を検知する必要があります。コネクションは明確に定義された時間で開始して終了する TCP パケットのシークエンスで、その間にデータは明確に定義されたプロトコルのもとにソース IP とターゲット IP アドレス間で流れます。各コネクションは正常、また攻撃としてラベル付けされます。

データセットには 4 タイプの攻撃があります :

  • DOS: denial-of-service, e.g. syn flood;
  • R2L: 遠隔マシンからの権限のないアクセス、e.g. パスワードの推測 ;
  • U2R: ローカルのスーパーユーザ (root) 特権への権限のないアクセス ;
  • probing : 偵察と他の厳密な調査、e.g., ポートスキャン。

データセットは約 500 万のコネクション・レコードを含みます。

3 つのタイプの特徴があります :

  • 個々のコネクションの基本的な特徴, e.g. 接続時間 (duration of connection)
  • コネクション内のコンテンツ特徴, e.g. 失敗したログイン試行の数
  • 2 秒 window 内の traffic 特徴, e.g. 現在の接続と同じホストへのコネクションの数
import logging
import matplotlib.pyplot as plt
%matplotlib inline
import numpy as np
import pandas as pd
import seaborn as sns
from sklearn.metrics import confusion_matrix, f1_score
import tensorflow as tf
tf.keras.backend.clear_session()
from tensorflow.keras.layers import Dense, InputLayer

from alibi_detect.datasets import fetch_kdd
from alibi_detect.models.tensorflow.autoencoder import eucl_cosim_features
from alibi_detect.od import OutlierAEGMM, OutlierVAEGMM
from alibi_detect.utils.data import create_outlier_batch
from alibi_detect.utils.fetching import fetch_detector
from alibi_detect.utils.saving import save_detector, load_detector
from alibi_detect.utils.visualize import plot_instance_score, plot_feature_outlier_tabular, plot_roc

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

 

データセットをロードする

幾つかの continuous (連続) な特徴 (41 の内から 18) だけを保持します。

kddcup = fetch_kdd(percent10=True)  # only load 10% of the dataset
print(kddcup.data.shape, kddcup.target.shape)
Downloading https://ndownloader.figshare.com/files/5976042
(494021, 18) (494021,)
kddcup.data[0]
array([8, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 9, 9, 1.0, 0.0, 0.11, 0.0,
       0.0, 0.0, 0.0, 0.0], dtype=object)

モデルはデータセットの (外れ値ではなく) 正常インスタンス上で訓練されて標準化 (= standardization) が適用されていると仮定します :

np.random.seed(0)
normal_batch = create_outlier_batch(kddcup.data, kddcup.target, n_samples=400000, perc_outlier=0)
X_train, y_train = normal_batch.data.astype('float32'), normal_batch.target
print(X_train.shape, y_train.shape)
print('{}% outliers'.format(100 * y_train.mean()))
(400000, 18) (400000,)
0.0% outliers
mean, stdev = X_train.mean(axis=0), X_train.std(axis=0)
print(mean)
print(stdev)
[1.09550075e+01 1.55620000e-03 1.74745000e-03 5.54626500e-02
 5.57733250e-02 9.85440925e-01 1.82663500e-02 1.33057100e-01
 1.48641492e+02 2.02133175e+02 8.44858050e-01 5.64032750e-02
 1.33479675e-01 2.40508250e-02 2.11637500e-03 1.05915000e-03
 5.73124750e-02 5.52324500e-02]
[2.17181039e+01 2.78305002e-02 2.61481724e-02 2.28073133e-01
 2.26952689e-01 9.25608777e-02 1.16637691e-01 2.77172101e-01
 1.03333220e+02 8.68577798e+01 3.05254458e-01 1.79868747e-01
 2.80221411e-01 4.92476707e-02 2.95181081e-02 1.59275611e-02
 2.24229381e-01 2.17798555e-01]

標準化を適用します :

X_train = (X_train - mean) / stdev

 

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

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

load_outlier_detector = False
filepath = 'model_aegmm' # 'my_path'  # change to directory (absolute path) where model is downloaded
if load_outlier_detector:  # load pretrained outlier detector
    detector_type = 'outlier'
    dataset = 'kddcup'
    detector_name = 'OutlierAEGMM'
    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
    # the model defined here is similar to the one defined in the original paper
    n_features = X_train.shape[1]
    latent_dim = 1
    n_gmm = 2  # nb of components in GMM

    encoder_net = tf.keras.Sequential(
    [
        InputLayer(input_shape=(n_features,)),
        Dense(60, activation=tf.nn.tanh),
        Dense(30, activation=tf.nn.tanh),
        Dense(10, activation=tf.nn.tanh),
        Dense(latent_dim, activation=None)
    ])

    decoder_net = tf.keras.Sequential(
    [
        InputLayer(input_shape=(latent_dim,)),
        Dense(10, activation=tf.nn.tanh),
        Dense(30, activation=tf.nn.tanh),
        Dense(60, activation=tf.nn.tanh),
        Dense(n_features, activation=None)
    ])

    gmm_density_net = tf.keras.Sequential(
    [
        InputLayer(input_shape=(latent_dim + 2,)),
        Dense(10, activation=tf.nn.tanh),
        Dense(n_gmm, activation=tf.nn.softmax)
    ])

    # initialize outlier detector
    od = OutlierAEGMM(threshold=None,  # threshold for outlier score
                      encoder_net=encoder_net,         # can also pass AEGMM model instead
                      decoder_net=decoder_net,         # of separate encoder, decoder
                      gmm_density_net=gmm_density_net, # and gmm density net
                      n_gmm=n_gmm,
                      recon_features=eucl_cosim_features)  # fn used to derive features
                                                           # from the reconstructed
                                                           # instances based on cosine
                                                           # similarity and Eucl distance

    # train
    od.fit(X_train,
           epochs=50,
           batch_size=1024,
           #save_path=filepath,
           verbose=True)

    # save the trained outlier detector
    save_detector(od, filepath)
391/391 [=] - 10s 25ms/step - loss: 1.7450
391/391 [=] - 10s 26ms/step - loss: 1.6758
391/391 [=] - 10s 26ms/step - loss: 1.5309
391/391 [=] - 10s 25ms/step - loss: 1.4671
391/391 [=] - 10s 25ms/step - loss: 1.4172
391/391 [=] - 10s 25ms/step - loss: 1.3793
391/391 [=] - 10s 25ms/step - loss: 1.3396
391/391 [=] - 10s 26ms/step - loss: 1.2962
391/391 [=] - 10s 26ms/step - loss: 1.2483
391/391 [=] - 10s 25ms/step - loss: 1.2015
391/391 [=] - 10s 25ms/step - loss: 1.1678
391/391 [=] - 10s 25ms/step - loss: 1.1356
391/391 [=] - 10s 25ms/step - loss: 1.0989
391/391 [=] - 10s 26ms/step - loss: 1.0617
391/391 [=] - 10s 26ms/step - loss: 1.0288
391/391 [=] - 10s 25ms/step - loss: 0.9975
391/391 [=] - 10s 25ms/step - loss: 0.9630
391/391 [=] - 10s 25ms/step - loss: 0.9298
391/391 [=] - 10s 25ms/step - loss: 0.9013
391/391 [=] - 10s 26ms/step - loss: 0.8748
391/391 [=] - 11s 27ms/step - loss: 0.8481
391/391 [=] - 10s 25ms/step - loss: 0.8259
391/391 [=] - 10s 25ms/step - loss: 0.8081
391/391 [=] - 10s 25ms/step - loss: 0.7944
391/391 [=] - 10s 25ms/step - loss: 0.7825
391/391 [=] - 10s 26ms/step - loss: 0.7726
391/391 [=] - 10s 26ms/step - loss: 0.7628
391/391 [=] - 10s 25ms/step - loss: 0.7543
391/391 [=] - 10s 25ms/step - loss: 0.7460
391/391 [=] - 10s 25ms/step - loss: 0.7385
391/391 [=] - 10s 26ms/step - loss: 0.7314
391/391 [=] - 10s 26ms/step - loss: 0.7242
391/391 [=] - 10s 25ms/step - loss: 0.7185
391/391 [=] - 10s 25ms/step - loss: 0.7120
391/391 [=] - 10s 25ms/step - loss: 0.7064
391/391 [=] - 10s 25ms/step - loss: 0.7005
391/391 [=] - 10s 25ms/step - loss: 0.6947
391/391 [=] - 10s 26ms/step - loss: 0.6891
391/391 [=] - 11s 27ms/step - loss: 0.6844
391/391 [=] - 10s 25ms/step - loss: 0.6787
391/391 [=] - 10s 25ms/step - loss: 0.6738
391/391 [=] - 10s 25ms/step - loss: 0.6693
391/391 [=] - 10s 25ms/step - loss: 0.6648
391/391 [=] - 10s 26ms/step - loss: 0.6604
391/391 [=] - 10s 26ms/step - loss: 0.6561
391/391 [=] - 10s 25ms/step - loss: 0.6521
391/391 [=] - 10s 25ms/step - loss: 0.6476
391/391 [=] - 10s 25ms/step - loss: 0.6446
391/391 [=] - 10s 25ms/step - loss: 0.6409
391/391 [=] - 10s 26ms/step - loss: 0.6378
!ls model_aegmm -l
!ls model_aegmm/model -l
total 12
-rw-rw-r-- 1 ubuntu ubuntu  760  7月  3 07:33 OutlierAEGMM.pickle
-rw-rw-r-- 1 ubuntu ubuntu   89  7月  3 07:33 meta.pickle
drwxrwxr-x 2 ubuntu ubuntu 4096  7月  3 07:33 model
total 120
-rw-rw-r-- 1 ubuntu ubuntu 29329  7月  3 07:33 aegmm.ckpt.data-00000-of-00001
-rw-rw-r-- 1 ubuntu ubuntu  1398  7月  3 07:33 aegmm.ckpt.index
-rw-rw-r-- 1 ubuntu ubuntu    77  7月  3 07:33 checkpoint
-rw-rw-r-- 1 ubuntu ubuntu 31576  7月  3 07:33 decoder_net.h5
-rw-rw-r-- 1 ubuntu ubuntu 31608  7月  3 07:33 encoder_net.h5
-rw-rw-r-- 1 ubuntu ubuntu 14488  7月  3 07:33 gmm_density_net.h5

警告は outlier threshold (外れ値閾値) を依然として設定する必要があることを教えます。これは infer_threshold メソッドで成されます。インスタンスのバッチを渡してそれらの何パーセントを正常であると考えるかを threshold_perc を通して指定する必要があります。およそ 5% の外れ値を含むことを知るあるデータを持つと仮定しましょう。外れ値のパーセンテージは create_outlier_batch 関数で perc_outlier で設定できます。

np.random.seed(0)
perc_outlier = 5
threshold_batch = create_outlier_batch(kddcup.data, kddcup.target, n_samples=1000, perc_outlier=perc_outlier)
X_threshold, y_threshold = threshold_batch.data.astype('float32'), threshold_batch.target
X_threshold = (X_threshold - mean) / stdev
print('{}% outliers'.format(100 * y_threshold.mean()))
5.0% outliers
od.infer_threshold(X_threshold, threshold_perc=100-perc_outlier)
print('New threshold: {}'.format(od.threshold))
New threshold: 5.188828468322754

更新された閾値で外れ値検知器をセーブしましょう :

save_detector(od, filepath)

 

外れ値を検出する

今は 10% の外れ値を持つデータのバッチを生成しそしてバッチ内の外れ値を検出します。

np.random.seed(1)
outlier_batch = create_outlier_batch(kddcup.data, kddcup.target, n_samples=1000, perc_outlier=10)
X_outlier, y_outlier = outlier_batch.data.astype('float32'), outlier_batch.target
X_outlier = (X_outlier - mean) / stdev
print(X_outlier.shape, y_outlier.shape)
print('{}% outliers'.format(100 * y_outlier.mean()))
(1000, 18) (1000,)
10.0% outliers

外れ値を予測します :

od_preds = od.predict(X_outlier, return_instance_score=True)

 

結果を表示する

F1 スコアと混同行列 :

labels = outlier_batch.target_names
y_pred = od_preds['data']['is_outlier']
f1 = f1_score(y_outlier, y_pred)
print('F1 score: {:.4f}'.format(f1))
cm = confusion_matrix(y_outlier, y_pred)
df_cm = pd.DataFrame(cm, index=labels, columns=labels)
sns.heatmap(df_cm, annot=True, cbar=True, linewidths=.5)
plt.show()
F1 score: 0.3352

インスタンスレベルの外れ値スコア vs 外れ値閾値をプロットします :

plot_instance_score(od_preds, y_outlier, labels, od.threshold, ylim=(None, None))

検出器の外れ値スコアのための ROC カーブをプロットすることもできます :

roc_data = {'AEGMM': {'scores': od_preds['data']['instance_score'], 'labels': y_outlier}}
plot_roc(roc_data)

 

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

潜在空間のインスタンスのエンコーディングとデコーダにより再構築されたインスタンスに由来する特徴を可視化できます。そしてエンコーディングと特徴は GMM 密度ネットワークに供給されます。

enc = od.aegmm.encoder(X_outlier)  # encoding
X_recon = od.aegmm.decoder(enc)  # reconstructed instances
recon_features = od.aegmm.recon_features(X_outlier, X_recon)  # reconstructed features
df = pd.DataFrame(dict(enc=enc[:, 0].numpy(),
                       cos=recon_features[:, 0].numpy(),
                       eucl=recon_features[:, 1].numpy(),
                       label=y_outlier))

groups = df.groupby('label')
fig, ax = plt.subplots()
for name, group in groups:
    ax.plot(group.enc, group.cos, marker='o',
            linestyle='', ms=6, label=labels[name])
plt.title('Encoding vs. Cosine Similarity')
plt.xlabel('Encoding')
plt.ylabel('Cosine Similarity')
ax.legend()
plt.show()

fig, ax = plt.subplots()
for name, group in groups:
    ax.plot(group.enc, group.eucl, marker='o',
            linestyle='', ms=6, label=labels[name])
plt.title('Encoding vs. Relative Euclidean Distance')
plt.xlabel('Encoding')
plt.ylabel('Relative Euclidean Distance')
ax.legend()
plt.show()

多くの外れ値が潜在空間で既に上手く分離されています。

 

VAEGMM 外れ値検知器を使用する

Google Cloud Bucket から 事前訓練済みの VAEGMM 検出器を再度インスタンス化できます。組込みの fetch_detector 関数を利用できます、これは事前訓練モデルをローカルディレクトリ filepath にセーブして検知器をロードします。代わりに、スクラッチから検知器を訓練することができます :

load_outlier_detector = False
filepath = 'model_vaegmm'  # change to directory (absolute path) where model is downloaded
if load_outlier_detector:  # load pretrained outlier detector
    detector_type = 'outlier'
    dataset = 'kddcup'
    detector_name = 'OutlierVAEGMM'
    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
    # the model defined here is similar to the one defined in
    # the OutlierVAE notebook
    n_features = X_train.shape[1]
    latent_dim = 2
    n_gmm = 2

    encoder_net = tf.keras.Sequential(
    [
        InputLayer(input_shape=(n_features,)),
        Dense(20, activation=tf.nn.relu),
        Dense(15, activation=tf.nn.relu),
        Dense(7, activation=tf.nn.relu)
    ])

    decoder_net = tf.keras.Sequential(
    [
        InputLayer(input_shape=(latent_dim,)),
        Dense(7, activation=tf.nn.relu),
        Dense(15, activation=tf.nn.relu),
        Dense(20, activation=tf.nn.relu),
        Dense(n_features, activation=None)
    ])

    gmm_density_net = tf.keras.Sequential(
    [
        InputLayer(input_shape=(latent_dim + 2,)),
        Dense(10, activation=tf.nn.relu),
        Dense(n_gmm, activation=tf.nn.softmax)
    ])


    # initialize outlier detector
    od = OutlierVAEGMM(threshold=None,
                       encoder_net=encoder_net,
                       decoder_net=decoder_net,
                       gmm_density_net=gmm_density_net,
                       n_gmm=n_gmm,
                       latent_dim=latent_dim,
                       samples=10,
                       recon_features=eucl_cosim_features)

    # train
    od.fit(X_train,
           epochs=50,
           batch_size=1024,
           cov_elbo=dict(sim=.0025),  # standard deviation assumption
           verbose=True)           # for elbo training

    # save the trained outlier detector
    save_detector(od, filepath)
391/391 [=] - 16s 40ms/step - loss: 0.7435
391/391 [=] - 15s 39ms/step - loss: 0.6727
391/391 [=] - 15s 38ms/step - loss: 0.6538
391/391 [=] - 15s 38ms/step - loss: 0.6449
391/391 [=] - 16s 40ms/step - loss: 0.6384
391/391 [=] - 15s 38ms/step - loss: 0.6383
391/391 [=] - 15s 38ms/step - loss: 0.6410
391/391 [=] - 16s 40ms/step - loss: 0.6557
391/391 [=] - 15s 39ms/step - loss: 0.7462
391/391 [=] - 15s 38ms/step - loss: 0.9006
391/391 [=] - 15s 38ms/step - loss: 0.8419
391/391 [=] - 15s 39ms/step - loss: 0.8301
391/391 [=] - 16s 41ms/step - loss: 0.8225
391/391 [=] - 15s 38ms/step - loss: 0.8158
391/391 [=] - 15s 39ms/step - loss: 0.8086
391/391 [=] - 15s 39ms/step - loss: 0.8014
391/391 [=] - 16s 40ms/step - loss: 0.7956
391/391 [=] - 15s 39ms/step - loss: 0.7901
391/391 [=] - 15s 39ms/step - loss: 0.7835
391/391 [=] - 15s 39ms/step - loss: 0.7772
391/391 [=] - 15s 39ms/step - loss: 0.7687
391/391 [=] - 15s 39ms/step - loss: 0.7593
391/391 [=] - 15s 39ms/step - loss: 0.7490
391/391 [=] - 15s 39ms/step - loss: 0.7400
391/391 [=] - 16s 41ms/step - loss: 0.7326
391/391 [=] - 15s 39ms/step - loss: 0.7261
391/391 [=] - 15s 39ms/step - loss: 0.7212
391/391 [=] - 16s 40ms/step - loss: 0.7167
391/391 [=] - 16s 40ms/step - loss: 0.7125
391/391 [=] - 15s 39ms/step - loss: 0.7068
391/391 [=] - 15s 39ms/step - loss: 0.7023
391/391 [=] - 16s 40ms/step - loss: 0.6979
391/391 [=] - 15s 39ms/step - loss: 0.6941
391/391 [=] - 15s 39ms/step - loss: 0.6903
391/391 [=] - 15s 39ms/step - loss: 0.6883
391/391 [=] - 16s 40ms/step - loss: 0.6861
391/391 [=] - 16s 40ms/step - loss: 0.6843
391/391 [=] - 15s 39ms/step - loss: 0.6829
391/391 [=] - 15s 39ms/step - loss: 0.6814
391/391 [=] - 16s 41ms/step - loss: 0.6802
391/391 [=] - 15s 39ms/step - loss: 0.6791
391/391 [=] - 15s 39ms/step - loss: 0.6786
391/391 [=] - 15s 39ms/step - loss: 0.6774
391/391 [=] - 16s 40ms/step - loss: 0.6767
391/391 [=] - 15s 39ms/step - loss: 0.6759
391/391 [=] - 15s 39ms/step - loss: 0.6756
391/391 [=] - 15s 40ms/step - loss: 0.6753
391/391 [=] - 16s 41ms/step - loss: 0.6741
391/391 [=] - 15s 39ms/step - loss: 0.6729
391/391 [=] - 15s 39ms/step - loss: 0.6733
!ls model_vaegmm -l
!ls model_vaegmm/model -l
total 12
-rw-rw-r-- 1 ubuntu ubuntu  935  7月  3 07:56 OutlierVAEGMM.pickle
-rw-rw-r-- 1 ubuntu ubuntu   90  7月  3 07:56 meta.pickle
drwxrwxr-x 2 ubuntu ubuntu 4096  7月  3 07:56 model
total 80
-rw-rw-r-- 1 ubuntu ubuntu    79  7月  3 07:56 checkpoint
-rw-rw-r-- 1 ubuntu ubuntu 22104  7月  3 07:56 decoder_net.h5
-rw-rw-r-- 1 ubuntu ubuntu 19304  7月  3 07:56 encoder_net.h5
-rw-rw-r-- 1 ubuntu ubuntu 14488  7月  3 07:56 gmm_density_net.h5
-rw-rw-r-- 1 ubuntu ubuntu 10033  7月  3 07:56 vaegmm.ckpt.data-00000-of-00001
-rw-rw-r-- 1 ubuntu ubuntu  1503  7月  3 07:56 vaegmm.ckpt.index

再度閾値を推論する必要があります :

od.infer_threshold(X_threshold, threshold_perc=100-perc_outlier)
print('New threshold: {}'.format(od.threshold))
New threshold: 9.472237873077368

更新された閾値で外れ値検知器をセーブします :

save_detector(od, filepath)

 

外れ値を検出して結果を表示する

予測します :

od_preds = od.predict(X_outlier, return_instance_score=True)

F1 スコアと混同行列 :

labels = outlier_batch.target_names
y_pred = od_preds['data']['is_outlier']
f1 = f1_score(y_outlier, y_pred)
print('F1 score: {:.4f}'.format(f1))
cm = confusion_matrix(y_outlier, y_pred)
df_cm = pd.DataFrame(cm, index=labels, columns=labels)
sns.heatmap(df_cm, annot=True, cbar=True, linewidths=.5)
plt.show()
F1 score: 0.9515

インスタンスレベルの外れ値スコア vs 外れ値閾値をプロットします :

plot_instance_score(od_preds, y_outlier, labels, od.threshold, ylim=(None, None))

ylim の min と max 値を調整することによりズームインできます。VAEGMM ROC カーブを AEGMM と比較することもできます :

roc_data['VAEGMM'] = {'scores': od_preds['data']['instance_score'], 'labels': y_outlier}
plot_roc(roc_data)

 

以上



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

Alibi Detect 0.7 : Examples : VAE 外れ値検知 on KDD Cup ‘99 データセット (翻訳/解説)
翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 07/02/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 KDD Cup ‘99 データセット

VAE – 概要

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

 

データセット

典型的な U.S. 空軍 LAN をシミュレートした LAN の TCP dump データを使用して、外れ値検知器はコンピュータ・ネットワーク侵入を検知する必要があります。コネクションは明確に定義された時間で開始して終了する TCP パケットのシークエンスで、その間にデータは明確に定義されたプロトコルのもとにソース IP とターゲット IP アドレス間で流れます。各コネクションは正常、また攻撃としてラベル付けされます。

データセットには 4 タイプの攻撃があります :

  • DOS: denial-of-service, e.g. syn flood;
  • R2L: 遠隔マシンからの権限のないアクセス、e.g. パスワードの推測 ;
  • U2R: ローカルのスーパーユーザ (root) 特権への権限のないアクセス ;
  • probing : 偵察と他の厳密な調査、e.g., ポートスキャン。

データセットは約 500 万のコネクション・レコードを含みます。

3 つのタイプの特徴があります :

  • 個々のコネクションの基本的な特徴, e.g. 接続時間 (duration of connection)
  • コネクション内のコンテンツ特徴, e.g. 失敗したログイン試行の数
  • 2 秒 window 内の traffic 特徴, e.g. 現在の接続と同じホストへのコネクションの数
import logging
import matplotlib.pyplot as plt
%matplotlib inline
import numpy as np
import pandas as pd
import seaborn as sns
from sklearn.metrics import confusion_matrix, f1_score
import tensorflow as tf
tf.keras.backend.clear_session()
from tensorflow.keras.layers import Dense, InputLayer

from alibi_detect.datasets import fetch_kdd
from alibi_detect.models.tensorflow.losses import elbo
from alibi_detect.od import OutlierVAE
from alibi_detect.utils.data import create_outlier_batch
from alibi_detect.utils.fetching import fetch_detector
from alibi_detect.utils.saving import save_detector, load_detector
from alibi_detect.utils.visualize import plot_instance_score, plot_feature_outlier_tabular, plot_roc

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

 

データセットをロードする

幾つかの continuous (連続) な特徴 (41 の内から 18) だけを保持します。

kddcup = fetch_kdd(percent10=True)  # only load 10% of the dataset
print(kddcup.data.shape, kddcup.target.shape)
Downloading https://ndownloader.figshare.com/files/5976042
(494021, 18) (494021,)
kddcup.data[0]
array([8, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 9, 9, 1.0, 0.0, 0.11, 0.0,
       0.0, 0.0, 0.0, 0.0], dtype=object)

機械学習モデルはデータセットの (外れ値ではなく) 正常インスタンス上で訓練されて標準化 (= standardization) が適用されていると仮定します :

np.random.seed(0)
normal_batch = create_outlier_batch(kddcup.data, kddcup.target, n_samples=400000, perc_outlier=0)
X_train, y_train = normal_batch.data.astype('float'), normal_batch.target
print(X_train.shape, y_train.shape)
print('{}% outliers'.format(100 * y_train.mean()))
(400000, 18) (400000,)
0.0% outliers
mean, stdev = X_train.mean(axis=0), X_train.std(axis=0)
print(mean)
print(stdev)
[1.09550075e+01 1.55620000e-03 1.74745000e-03 5.54626500e-02
 5.57733250e-02 9.85440925e-01 1.82663500e-02 1.33057100e-01
 1.48641492e+02 2.02133175e+02 8.44858050e-01 5.64032750e-02
 1.33479675e-01 2.40508250e-02 2.11637500e-03 1.05915000e-03
 5.73124750e-02 5.52324500e-02]
[2.17181039e+01 2.78305002e-02 2.61481724e-02 2.28073133e-01
 2.26952689e-01 9.25608777e-02 1.16637691e-01 2.77172101e-01
 1.03333220e+02 8.68577798e+01 3.05254458e-01 1.79868747e-01
 2.80221411e-01 4.92476707e-02 2.95181081e-02 1.59275611e-02
 2.24229381e-01 2.17798555e-01]

標準化を適用します :

X_train = (X_train - mean) / stdev

 

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

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

load_outlier_detector = False
filepath = 'my_dir'  # change to directory (absolute path) where model is downloaded
if load_outlier_detector:  # load pretrained outlier detector
    detector_type = 'outlier'
    dataset = 'kddcup'
    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
    n_features = X_train.shape[1]
    latent_dim = 2

    encoder_net = tf.keras.Sequential(
      [
          InputLayer(input_shape=(n_features,)),
          Dense(20, activation=tf.nn.relu),
          Dense(15, activation=tf.nn.relu),
          Dense(7, activation=tf.nn.relu)
      ])

    decoder_net = tf.keras.Sequential(
      [
          InputLayer(input_shape=(latent_dim,)),
          Dense(7, activation=tf.nn.relu),
          Dense(15, activation=tf.nn.relu),
          Dense(20, activation=tf.nn.relu),
          Dense(n_features, activation=None)
      ])

    # initialize outlier detector
    od = OutlierVAE(threshold=None,  # 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=5)
    # train
    od.fit(X_train,
           loss_fn=elbo,
           cov_elbo=dict(sim=.01),
           epochs=30,
           verbose=True)

    # save the trained outlier detector
    save_detector(od, filepath)
6250/6250 [=] - 148s 24ms/step - loss: 28695.7242
6250/6250 [=] - 148s 24ms/step - loss: 12437.6854
6250/6250 [=] - 146s 23ms/step - loss: 10256.9219
6250/6250 [=] - 155s 25ms/step - loss: 9557.9839
6250/6250 [=] - 147s 23ms/step - loss: 8414.8731
6250/6250 [=] - 148s 24ms/step - loss: 7847.7130
6250/6250 [=] - 151s 24ms/step - loss: 7549.5546
6250/6250 [=] - 150s 24ms/step - loss: 7412.6179
6250/6250 [=] - 147s 23ms/step - loss: 7358.9181
6250/6250 [=] - 148s 24ms/step - loss: 7064.3351
6250/6250 [=] - 145s 23ms/step - loss: 7047.5733
6250/6250 [=] - 150s 24ms/step - loss: 6862.8724
6250/6250 [=] - 146s 23ms/step - loss: 6859.6950
6250/6250 [=] - 149s 24ms/step - loss: 6733.0950
6250/6250 [=] - 146s 23ms/step - loss: 6395.8519
6250/6250 [=] - 152s 24ms/step - loss: 6239.8235
6250/6250 [=] - 147s 23ms/step - loss: 6141.0218
6250/6250 [=] - 149s 24ms/step - loss: 6080.4994
6250/6250 [=] - 150s 24ms/step - loss: 6030.9626
6250/6250 [=] - 152s 24ms/step - loss: 5986.5710
6250/6250 [=] - 152s 24ms/step - loss: 5968.1871
6250/6250 [=] - 150s 24ms/step - loss: 6026.3471
6250/6250 [=] - 152s 24ms/step - loss: 5828.1424
6250/6250 [=] - 149s 24ms/step - loss: 5841.5844
6250/6250 [=] - 147s 24ms/step - loss: 5794.9508
6250/6250 [=] - 154s 25ms/step - loss: 5792.5575
6250/6250 [=] - 147s 24ms/step - loss: 5767.5592
6250/6250 [=] - 150s 24ms/step - loss: 5643.3078
6250/6250 [=] - 147s 23ms/step - loss: 5571.4557
6250/6250 [=] - 148s 24ms/step - loss: 5541.4037
!ls model_vae -l
!ls model_vae/model -l
total 12
-rw-rw-r-- 1 ubuntu ubuntu  107  7月  2 13:26 OutlierVAE.pickle
-rw-rw-r-- 1 ubuntu ubuntu   87  7月  2 13:26 meta.pickle
drwxrwxr-x 2 ubuntu ubuntu 4096  7月  2 13:26 model
total 64
-rw-rw-r-- 1 ubuntu ubuntu    73  7月  2 13:26 checkpoint
-rw-rw-r-- 1 ubuntu ubuntu 22104  7月  2 13:26 decoder_net.h5
-rw-rw-r-- 1 ubuntu ubuntu 19304  7月  2 13:26 encoder_net.h5
-rw-rw-r-- 1 ubuntu ubuntu  9269  7月  2 13:26 vae.ckpt.data-00000-of-00001
-rw-rw-r-- 1 ubuntu ubuntu  1259  7月  2 13:26 vae.ckpt.index

警告は outlier threshold (外れ値閾値) を依然として設定する必要があることを教えます。これは infer_threshold メソッドで成されます。インスタンスのバッチを渡してそれらの何パーセントを正常であると考えるかを threshold_perc を通して指定する必要があります。およそ 5% の外れ値を含むことを知るあるデータを持つと仮定しましょう。外れ値のパーセンテージは create_outlier_batch 関数で perc_outlier で設定できます。

np.random.seed(0)
perc_outlier = 5
threshold_batch = create_outlier_batch(kddcup.data, kddcup.target, n_samples=1000, perc_outlier=perc_outlier)
X_threshold, y_threshold = threshold_batch.data.astype('float'), threshold_batch.target
X_threshold = (X_threshold - mean) / stdev
print('{}% outliers'.format(100 * y_threshold.mean()))
5.0% outliers
od.infer_threshold(X_threshold, threshold_perc=100-perc_outlier)
print('New threshold: {}'.format(od.threshold))
New threshold: 1.5222603200798008

threshold_perc を例えば 99 に設定して推論された閾値に少しのマージンを加えることにより通常の訓練データから閾値をすいろんすることもできました。更新された閾値で外れ値検知器をセーブしましょう :

save_detector(od, filepath)
!ls model_vae -l
!ls model_vae/model -l
total 12
-rw-rw-r-- 1 ubuntu ubuntu  218  7月  2 13:35 OutlierVAE.pickle
-rw-rw-r-- 1 ubuntu ubuntu   87  7月  2 13:35 meta.pickle
drwxrwxr-x 2 ubuntu ubuntu 4096  7月  2 13:35 model
total 64
-rw-rw-r-- 1 ubuntu ubuntu    73  7月  2 13:35 checkpoint
-rw-rw-r-- 1 ubuntu ubuntu 22104  7月  2 13:35 decoder_net.h5
-rw-rw-r-- 1 ubuntu ubuntu 19304  7月  2 13:35 encoder_net.h5
-rw-rw-r-- 1 ubuntu ubuntu  9269  7月  2 13:35 vae.ckpt.data-00000-of-00001
-rw-rw-r-- 1 ubuntu ubuntu  1259  7月  2 13:35 vae.ckpt.index

 

外れ値を検出する

今は 10% の外れ値を持つデータのバッチを生成しそしてバッチ内の外れ値を検出します。

np.random.seed(1)
outlier_batch = create_outlier_batch(kddcup.data, kddcup.target, n_samples=1000, perc_outlier=10)
X_outlier, y_outlier = outlier_batch.data.astype('float'), outlier_batch.target
X_outlier = (X_outlier - mean) / stdev
print(X_outlier.shape, y_outlier.shape)
print('{}% outliers'.format(100 * y_outlier.mean()))
(1000, 18) (1000,)
10.0% outliers

外れ値を予測します :

od_preds = od.predict(X_outlier,
                      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']
od_preds['data']['is_outlier'][:10]
array([0, 0, 0, 1, 0, 1, 1, 0, 0, 0])
od_preds['data']['instance_score'][:10]
array([5.74442137e-04, 6.24835379e-04, 6.83877481e-04, 1.52228703e+00,
       2.47923307e-02, 4.29582862e+01, 1.52229905e+00, 4.45219175e-03,
       8.09561496e-02, 2.37765956e-02])
od_preds['data']['feature_score']
array([[4.77347707e-05, 6.23364775e-05, 4.80892692e-05, ...,
        3.21192958e-04, 6.79559419e-05, 5.60611257e-05],
       [1.67435697e-04, 7.68863402e-05, 1.12371712e-04, ...,
        3.19287128e-04, 9.01695907e-05, 8.10840695e-05],
       [9.42410698e-04, 1.08231414e-06, 3.40019366e-05, ...,
        5.65485925e-04, 1.88107799e-05, 1.04572967e-05],
       ...,
       [2.08158688e-03, 4.93670543e-06, 3.57241685e-05, ...,
        6.89739720e-04, 9.91289453e-05, 3.80882650e-06],
       [2.27466705e-03, 4.25181944e-03, 6.06307292e-05, ...,
        3.40673684e-05, 9.27018184e-06, 6.57232246e-03],
       [1.50122506e-03, 4.51257037e-03, 1.09080541e-05, ...,
        4.76422446e-04, 4.67411636e-03, 4.86378648e-02]])

 

結果を表示する

F1 スコアと混同行列 :

labels = outlier_batch.target_names
y_pred = od_preds['data']['is_outlier']
f1 = f1_score(y_outlier, y_pred)
print('F1 score: {:.4f}'.format(f1))
cm = confusion_matrix(y_outlier, y_pred)
df_cm = pd.DataFrame(cm, index=labels, columns=labels)
sns.heatmap(df_cm, annot=True, cbar=True, linewidths=.5)
plt.show()
F1 score: 0.9754

インスタンスレベルの外れ値スコア vs 外れ値閾値をプロットします :

plot_instance_score(od_preds, y_outlier, labels, od.threshold)

幾つかの外れ値が検出するのに非常に簡単である一方で、他は通常データに近い外れ値スコアを持つことが明瞭に分かります。検出器の外れ値スコアのための ROC カーブをプロットすることもできます :

roc_data = {'VAE': {'scores': od_preds['data']['instance_score'], 'labels': y_outlier}}
plot_roc(roc_data)

 

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

今は X_outlier 上の個々の予測の幾つかを詳しく見ることができます。

X_recon = od.vae(X_outlier).numpy()  # reconstructed instances by the VAE
plot_feature_outlier_tabular(od_preds,
                             X_outlier,
                             X_recon=X_recon,
                             threshold=od.threshold,
                             instance_ids=None,  # pass a list with indices of instances to display
                             max_instances=5,  # max nb of instances to display
                             top_n=5,  # only show top_n features ordered by outlier score
                             outliers_only=False,  # only show outlier predictions
                             feature_names=kddcup.feature_names,  # add feature names
                             figsize=(20, 30))

(訳注 : 下は実験結果)

srv_count 特徴は多くの表示される外れ値の責任を負います。

 

以上



Alibi Detect 0.7 : Examples : Isolation Forest 外れ値検知 on TCP dump

Alibi Detect 0.7 : Examples : Isolation Forest 外れ値検知 on KDD Cup ‘99 データセット (翻訳/解説)
翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 07/02/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 : Isolation Forest 外れ値検知 on KDD Cup ‘99 データセット

Isolation Forest – 概要

Isolation forests (IF) は特に外れ値検知のために使用される tree ベースのモデルです。IF は特徴をランダムに選択してから選択された特徴の最大値と最小値の間の分割値をランダムに選択することにより観測を isolate (分離、区分け) します。サンプルを区分けするために必要な分割の数はルートノードから終端 (= terminating) ノードへのパスの長さに等しいです。ランダム tree の森に渡り平均された、パスの長さは正常の尺度で異常スコアを定義するために使用されます。外れ値は典型的にはより迅速に分離され、より短いパスに繋がります。アルゴリズムは低次元から medium 次元の表形式データのために適します。

 

データセット

典型的な U.S. 空軍 LAN をシミュレートした LAN の TCP dump データを使用して、外れ値検知器はコンピュータ・ネットワーク侵入を検知する必要があります。コネクションは明確に定義された時間で開始して終了する TCP パケットのシークエンスで、その間にデータは明確に定義されたプロトコルのもとにソース IP とターゲット IP アドレス間で流れます。各コネクションは正常、また攻撃としてラベル付けされます。

データセットには 4 タイプの攻撃があります :

  • DOS: denial-of-service, e.g. syn flood;
  • R2L: 遠隔マシンからの権限のないアクセス、e.g. パスワードの推測 ;
  • U2R: ローカルのスーパーユーザ (root) 特権への権限のないアクセス ;
  • probing : 偵察と他の厳密な調査、e.g., ポートスキャン。

データセットは約 500 万のコネクション・レコードを含みます。

3 つのタイプの特徴があります :

  • 個々のコネクションの基本的な特徴, e.g. 接続時間 (duration of connection)
  • コネクション内のコンテンツ特徴, e.g. 失敗したログイン試行の数
  • 2 秒 window 内の traffic 特徴, e.g. 現在の接続と同じホストへのコネクションの数
import matplotlib
%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np
import os
import pandas as pd
import seaborn as sns
from sklearn.metrics import confusion_matrix, f1_score

from alibi_detect.od import IForest
from alibi_detect.datasets import fetch_kdd
from alibi_detect.utils.data import create_outlier_batch
from alibi_detect.utils.fetching import fetch_detector
from alibi_detect.utils.saving import save_detector, load_detector
from alibi_detect.utils.visualize import plot_instance_score, plot_roc

 

データセットをロードする

幾つかの continuous (連続) な特徴 (41 の内から 18) だけを保持します。

kddcup = fetch_kdd(percent10=True)  # only load 10% of the dataset
print(kddcup.data.shape, kddcup.target.shape)
Downloading https://ndownloader.figshare.com/files/5976042
(494021, 18) (494021,)
kddcup.data[0]
array([8, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 9, 9, 1.0, 0.0, 0.11, 0.0,
       0.0, 0.0, 0.0, 0.0], dtype=object)

機械学習モデルはデータセットの (外れ値ではなく) 正常インスタンス上で訓練されて標準化 (= standardization) が適用されていると仮定します :

np.random.seed(0)
normal_batch = create_outlier_batch(kddcup.data, kddcup.target, n_samples=400000, perc_outlier=0)
X_train, y_train = normal_batch.data.astype('float'), normal_batch.target
print(X_train.shape, y_train.shape)
print('{}% outliers'.format(100 * y_train.mean()))
(400000, 18) (400000,)
0.0% outliers
mean, stdev = X_train.mean(axis=0), X_train.std(axis=0)
print(mean)
print(stdev)
[1.09550075e+01 1.55620000e-03 1.74745000e-03 5.54626500e-02
 5.57733250e-02 9.85440925e-01 1.82663500e-02 1.33057100e-01
 1.48641492e+02 2.02133175e+02 8.44858050e-01 5.64032750e-02
 1.33479675e-01 2.40508250e-02 2.11637500e-03 1.05915000e-03
 5.73124750e-02 5.52324500e-02]
[2.17181039e+01 2.78305002e-02 2.61481724e-02 2.28073133e-01
 2.26952689e-01 9.25608777e-02 1.16637691e-01 2.77172101e-01
 1.03333220e+02 8.68577798e+01 3.05254458e-01 1.79868747e-01
 2.80221411e-01 4.92476707e-02 2.95181081e-02 1.59275611e-02
 2.24229381e-01 2.17798555e-01]

標準化を適用します :

X_train = (X_train - mean) / stdev

 

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

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

load_outlier_detector = False
filepath = 'my_path'  # change to directory where model is downloaded
if load_outlier_detector:  # load pretrained outlier detector
    detector_type = 'outlier'
    dataset = 'kddcup'
    detector_name = 'IForest'
    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

    # initialize outlier detector
    od = IForest(threshold=None,  # threshold for outlier score
                 n_estimators=100)

    # train
    od.fit(X_train)

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

load_outlier_detector が False に等しい場合、警告が outlier threshold (外れ値閾値) を依然として設定する必要があることを教えます。これは infer_threshold メソッドで成されます。インスタンスのバッチを渡してそれらの何パーセントを正常であると考えるかを threshold_perc を通して指定する必要があります。およそ 5% の外れ値を含むことを知るあるデータを持つと仮定しましょう。外れ値のパーセンテージは create_outlier_batch 関数で perc_outlier で設定できます。

np.random.seed(0)
perc_outlier = 5
threshold_batch = create_outlier_batch(kddcup.data, kddcup.target, n_samples=1000, perc_outlier=perc_outlier)
X_threshold, y_threshold = threshold_batch.data.astype('float'), threshold_batch.target
X_threshold = (X_threshold - mean) / stdev
print('{}% outliers'.format(100 * y_threshold.mean()))
5.0% outliers
od.infer_threshold(X_threshold, threshold_perc=100-perc_outlier)
print('New threshold: {}'.format(od.threshold))
New threshold: 0.0797010793476482

更新された閾値で外れ値検知器をセーブしましょう :

save_detector(od, filepath)

 

外れ値を検出する

今は 10% の外れ値を持つデータのバッチを生成しそしてバッチ内の外れ値を検出します。

np.random.seed(1)
outlier_batch = create_outlier_batch(kddcup.data, kddcup.target, n_samples=1000, perc_outlier=10)
X_outlier, y_outlier = outlier_batch.data.astype('float'), outlier_batch.target
X_outlier = (X_outlier - mean) / stdev
print(X_outlier.shape, y_outlier.shape)
print('{}% outliers'.format(100 * y_outlier.mean()))
(1000, 18) (1000,)
10.0% outliers

外れ値を予測します :

od_preds = od.predict(X_outlier, return_instance_score=True)

 

結果を表示する

F1 スコアと混同行列 :

labels = outlier_batch.target_names
y_pred = od_preds['data']['is_outlier']
f1 = f1_score(y_outlier, y_pred)
print('F1 score: {:.4f}'.format(f1))
cm = confusion_matrix(y_outlier, y_pred)
df_cm = pd.DataFrame(cm, index=labels, columns=labels)
sns.heatmap(df_cm, annot=True, cbar=True, linewidths=.5)
plt.show()
F1 score: 0.3279

インスタンスレベルの外れ値スコア vs 外れ値閾値をプロットします :

plot_instance_score(od_preds, y_outlier, labels, od.threshold)

isolation forest が外れ値スコアが 0 あたりの外れ値の 1 タイプを検出するのに良い仕事をしていないことが分かります。これは外れ値についての明示的な知識なしに良い閾値を推論することを困難にしています。閾値を 0 のすぐ下に設定することはデータセットの外れ値のために実質的により良い検出性能に繋がるでしょう。これはまた ROC カーブによっても反映されます :

roc_data = {'IF': {'scores': od_preds['data']['instance_score'], 'labels': y_outlier}}
plot_roc(roc_data)

 

以上



ClassCat® Chatbot

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