Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add version to metadata #339

Merged
merged 3 commits into from
Sep 22, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion alibi_detect/ad/tests/test_adae.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from tensorflow.keras.layers import Dense, InputLayer
from tensorflow.keras.utils import to_categorical
from alibi_detect.ad import AdversarialAE
from alibi_detect.version import __version__

threshold = [None, 5.]
w_model = [1., .5]
Expand Down Expand Up @@ -68,7 +69,8 @@ def test_adv_vae(adv_ae_params):
)

assert advae.threshold == threshold
assert advae.meta == {'name': 'AdversarialAE', 'detector_type': 'offline', 'data_type': None}
assert advae.meta == {'name': 'AdversarialAE', 'detector_type': 'offline', 'data_type': None,
'version': __version__}
for layer in advae.model.layers:
assert not layer.trainable

Expand Down
4 changes: 3 additions & 1 deletion alibi_detect/ad/tests/test_admd.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import tensorflow as tf
from tensorflow.keras.utils import to_categorical
from alibi_detect.ad import ModelDistillation
from alibi_detect.version import __version__

threshold = [None, 5.]
loss_type = ['kld', 'xent']
Expand Down Expand Up @@ -54,7 +55,8 @@ def test_adv_md(adv_md_params):
)

assert admd.threshold == threshold
assert admd.meta == {'name': 'ModelDistillation', 'detector_type': 'offline', 'data_type': None}
assert admd.meta == {'name': 'ModelDistillation', 'detector_type': 'offline', 'data_type': None,
'version': __version__}
for layer in admd.model.layers:
assert not layer.trainable

Expand Down
6 changes: 5 additions & 1 deletion alibi_detect/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@
import numpy as np
from typing import Dict

from alibi_detect.version import __version__

DEFAULT_META = {
"name": None,
"detector_type": None, # online or offline
"data_type": None # tabular, image or time-series
"data_type": None, # tabular, image or time-series
"version": None,
} # type: Dict


Expand Down Expand Up @@ -55,6 +58,7 @@ class BaseDetector(ABC):
def __init__(self):
self.meta = copy.deepcopy(DEFAULT_META)
self.meta['name'] = self.__class__.__name__
self.meta['version'] = __version__

def __repr__(self):
return self.__class__.__name__
Expand Down
3 changes: 2 additions & 1 deletion alibi_detect/od/tests/test_ae.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import tensorflow as tf
from tensorflow.keras.layers import Dense, InputLayer
from alibi_detect.od import OutlierAE
from alibi_detect.version import __version__

threshold = [None, 5.]
threshold_perc = [90.]
Expand Down Expand Up @@ -60,7 +61,7 @@ def test_ae(ae_params):
)

assert ae.threshold == threshold
assert ae.meta == {'name': 'OutlierAE', 'detector_type': 'offline', 'data_type': None}
assert ae.meta == {'name': 'OutlierAE', 'detector_type': 'offline', 'data_type': None, 'version': __version__}

# fit OutlierAE, infer threshold and compute scores
ae.fit(X, epochs=5, verbose=False)
Expand Down
3 changes: 2 additions & 1 deletion alibi_detect/od/tests/test_aegmm.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import tensorflow as tf
from tensorflow.keras.layers import Dense, InputLayer
from alibi_detect.od import OutlierAEGMM
from alibi_detect.version import __version__

threshold = [None, 5.]
n_gmm = [1, 2]
Expand Down Expand Up @@ -69,7 +70,7 @@ def test_aegmm(aegmm_params):
)

assert aegmm.threshold == threshold
assert aegmm.meta == {'name': 'OutlierAEGMM', 'detector_type': 'offline', 'data_type': None}
assert aegmm.meta == {'name': 'OutlierAEGMM', 'detector_type': 'offline', 'data_type': None, 'version': __version__}

# fit OutlierAEGMM, infer threshold and compute scores
aegmm.fit(X, w_energy=w_energy, epochs=5, batch_size=1000, verbose=False)
Expand Down
4 changes: 3 additions & 1 deletion alibi_detect/od/tests/test_iforest.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import pytest
from sklearn.datasets import load_iris
from alibi_detect.od import IForest
from alibi_detect.version import __version__

threshold = [None, 0.]
threshold_perc = [75., 95.]
Expand All @@ -23,7 +24,8 @@ def test_isolation_forest(iforest_params):
X, y = load_iris(return_X_y=True)
iforest = IForest(threshold)
assert iforest.threshold == threshold
assert iforest.meta == {'name': 'IForest', 'detector_type': 'offline', 'data_type': 'tabular'}
assert iforest.meta == {'name': 'IForest', 'detector_type': 'offline', 'data_type': 'tabular',
'version': __version__}
iforest.fit(X)
iforest.infer_threshold(X, threshold_perc=threshold_perc)
iscore = iforest.score(X)
Expand Down
3 changes: 2 additions & 1 deletion alibi_detect/od/tests/test_llr.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import tensorflow as tf
from tensorflow.keras.layers import Dense, Input, LSTM
from alibi_detect.od import LLR
from alibi_detect.version import __version__

input_dim = 5
hidden_dim = 20
Expand Down Expand Up @@ -55,7 +56,7 @@ def test_llr(llr_params):
od = LLR(threshold=threshold, sequential=True, model=model, log_prob=likelihood_fn)

assert od.threshold == threshold
assert od.meta == {'name': 'LLR', 'detector_type': 'offline', 'data_type': None}
assert od.meta == {'name': 'LLR', 'detector_type': 'offline', 'data_type': None, 'version': __version__}

od.fit(
X_train,
Expand Down
3 changes: 2 additions & 1 deletion alibi_detect/od/tests/test_mahalanobis.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import pytest
from sklearn.datasets import load_iris
from alibi_detect.od import Mahalanobis
from alibi_detect.version import __version__

threshold = [None, 5.]
n_components = [2, 3]
Expand Down Expand Up @@ -31,7 +32,7 @@ def test_mahalanobis(mahalanobis_params):
start_clip=start_clip, max_n=max_n)
assert mh.threshold == threshold
assert mh.n == 0
assert mh.meta == {'name': 'Mahalanobis', 'detector_type': 'online', 'data_type': 'tabular'}
assert mh.meta == {'name': 'Mahalanobis', 'detector_type': 'online', 'data_type': 'tabular', 'version': __version__}
mh.infer_threshold(X, threshold_perc=threshold_perc)
assert mh.n == X.shape[0]
iscore = mh.score(X) # noqa
Expand Down
4 changes: 3 additions & 1 deletion alibi_detect/od/tests/test_prophet.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import pandas as pd
import pytest
from alibi_detect.od import OutlierProphet
from alibi_detect.version import __version__

growth = ['linear', 'logistic']
return_instance_score = [True, False]
Expand Down Expand Up @@ -36,7 +37,8 @@ def test_prophet(prophet_params):
growth, return_instance_score, return_forecast = prophet_params
od = OutlierProphet(growth=growth)
assert isinstance(od.model, fbprophet.forecaster.Prophet)
assert od.meta == {'name': 'OutlierProphet', 'detector_type': 'offline', 'data_type': 'time-series'}
assert od.meta == {'name': 'OutlierProphet', 'detector_type': 'offline', 'data_type': 'time-series',
'version': __version__}
if growth == 'logistic':
df_fit['cap'] = 10.
df_test['cap'] = 10.
Expand Down
8 changes: 5 additions & 3 deletions alibi_detect/od/tests/test_seq2seq.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import pytest
from alibi_detect.od import OutlierSeq2Seq
from alibi_detect.utils.perturbation import inject_outlier_ts
from alibi_detect.version import __version__

n_features = [1, 2]
seq_len = [20, 50]
Expand Down Expand Up @@ -35,8 +36,8 @@ def test_seq2seq(seq2seq_params):
X = np.sin(np.linspace(-50, 50, 10000)).astype(np.float32).reshape((-1, n_features))

# create outliers for threshold and detection
X_threshold = inject_outlier_ts(X, perc_outlier=100-threshold_perc, perc_window=10, n_std=10., min_std=9.).data
X_outlier = inject_outlier_ts(X, perc_outlier=100-threshold_perc, perc_window=10, n_std=10., min_std=9.).data
X_threshold = inject_outlier_ts(X, perc_outlier=100 - threshold_perc, perc_window=10, n_std=10., min_std=9.).data
X_outlier = inject_outlier_ts(X, perc_outlier=100 - threshold_perc, perc_window=10, n_std=10., min_std=9.).data

# define architecture
od = OutlierSeq2Seq(n_features, seq_len, threshold=threshold, latent_dim=latent_dim)
Expand All @@ -45,7 +46,8 @@ def test_seq2seq(seq2seq_params):
assert od.threshold == 0.
else:
assert od.threshold == threshold
assert od.meta == {'name': 'OutlierSeq2Seq', 'detector_type': 'offline', 'data_type': 'time-series'}
assert od.meta == {'name': 'OutlierSeq2Seq', 'detector_type': 'offline', 'data_type': 'time-series',
'version': __version__}

# fit OutlierSeq2Seq
od.fit(X, epochs=2, verbose=False)
Expand Down
4 changes: 3 additions & 1 deletion alibi_detect/od/tests/test_sr.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import numpy as np
import pytest
from alibi_detect.od import SpectralResidual
from alibi_detect.version import __version__

# create normal time series and one with perturbations
t = np.linspace(0, 0.5, 1000)
Expand Down Expand Up @@ -35,7 +36,8 @@ def test_sr(sr_params):
assert od.threshold == threshold
assert od.meta == {'name': 'SpectralResidual',
'detector_type': 'online',
'data_type': 'time-series'}
'data_type': 'time-series',
'version': __version__}
preds_in = od.predict(X, t, return_instance_score=return_instance_score)
assert preds_in['data']['is_outlier'].sum() <= 2.
if return_instance_score:
Expand Down
3 changes: 2 additions & 1 deletion alibi_detect/od/tests/test_vae.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from tensorflow.keras.layers import Dense, InputLayer
from alibi_detect.od import OutlierVAE
from alibi_detect.models.tensorflow.losses import elbo
from alibi_detect.version import __version__

threshold = [None, 5.]
score_type = ['mse']
Expand Down Expand Up @@ -68,7 +69,7 @@ def test_vae(vae_params):
)

assert vae.threshold == threshold
assert vae.meta == {'name': 'OutlierVAE', 'detector_type': 'offline', 'data_type': None}
assert vae.meta == {'name': 'OutlierVAE', 'detector_type': 'offline', 'data_type': None, 'version': __version__}

# fit OutlierVAE, infer threshold and compute scores
vae.fit(X, loss_fn=loss_fn, epochs=5, verbose=False)
Expand Down
4 changes: 3 additions & 1 deletion alibi_detect/od/tests/test_vaegmm.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import tensorflow as tf
from tensorflow.keras.layers import Dense, InputLayer
from alibi_detect.od import OutlierVAEGMM
from alibi_detect.version import __version__

threshold = [None, 5.]
n_gmm = [1, 2]
Expand Down Expand Up @@ -73,7 +74,8 @@ def test_vaegmm(vaegmm_params):
)

assert vaegmm.threshold == threshold
assert vaegmm.meta == {'name': 'OutlierVAEGMM', 'detector_type': 'offline', 'data_type': None}
assert vaegmm.meta == {'name': 'OutlierVAEGMM', 'detector_type': 'offline', 'data_type': None,
'version': __version__}

# fit OutlierAEGMM, infer threshold and compute scores
vaegmm.fit(X, w_recon=w_recon, w_energy=w_energy, epochs=5, batch_size=1000, verbose=False)
Expand Down
7 changes: 7 additions & 0 deletions alibi_detect/utils/saving.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import logging
import os
from pathlib import Path
import warnings
import tensorflow as tf
from tensorflow.keras.layers import Input, InputLayer
from tensorflow_probability.python.distributions.distribution import Distribution
Expand All @@ -24,6 +25,7 @@
OutlierSeq2Seq, OutlierVAE, OutlierVAEGMM, SpectralResidual)
from alibi_detect.od.llr import build_model
from alibi_detect.utils.tensorflow.kernels import GaussianRBF
from alibi_detect.version import __version__

# do not extend pickle dispatch table so as not to change pickle behaviour
dill.extend(use_dill=False)
Expand Down Expand Up @@ -980,6 +982,11 @@ def load_detector(filepath: Union[str, os.PathLike], **kwargs) -> Data:
# load metadata
meta_dict = dill.load(open(filepath.joinpath('meta' + suffix), 'rb'))

# check version
if meta_dict['version'] != __version__:
warnings.warn(f'Trying to load detector from version {meta_dict["version"]} when using version {__version__}. '
f'This may lead to breaking code or invalid results.')

if 'backend' in list(meta_dict.keys()) and meta_dict['backend'] == 'pytorch':
raise NotImplementedError('Detectors with PyTorch backend are not yet supported.')

Expand Down