zamanzadeh / CARLA

CARLA: A self-supervised contrastive learning model for time series anomaly detection. Enhances anomaly detection by learning robust representations of time series data.
MIT License
23 stars 4 forks source link

Potential bug in anomaly injection #6

Open sammy-snipes opened 2 weeks ago

sammy-snipes commented 2 weeks ago

In the SubAnomaly class from data/augment.py the __call__ method seems to be zeroing the majority of channels, and only injecting anomalies in a few channels. I though the intended behavior would be to inject anomalies in selected channels while leaving the others channels untouched, but I might be misunderstanding something here.

Steps for reproducing

from utils.config import create_config
from utils.common_config import (
    get_train_transformations,
    inject_sub_anomaly,
    get_val_transformations1,
    get_train_dataset,
)
import numpy as np
import random

np.random.seed(42)
random.seed(42)

# load config
p = create_config(
    "configs/env.yml", "configs/pretext/carla_pretext_smd.yml", "machine-1-1.txt"
)

# load train data
train_transforms = get_train_transformations(p)
sanomaly = inject_sub_anomaly(p)
val_transforms = get_val_transformations1(p)
train_data = get_train_dataset(p, train_transforms, sanomaly, to_augmented_dataset=True)

# get random item
item = train_data.__getitem__(np.random.randint(0, len(train_data)))
anchor, ss_aug = item["ts_org"], item["ts_ss_augment"]

# get unique count along channel
channel_unique_counts = lambda d: np.apply_along_axis(lambda x: len(np.unique(x)), 0, d)

print(channel_unique_counts(anchor), channel_unique_counts(ss_aug), sep=2 * "\n")

output:

[ 11 108  81  59   1  34  21   1 103  10 185  13   4 197 156 184   1   1
 197 200 189 190  54 134 137 138   1 197   1  10 196  96   3   7 179 181
   1   1]

[ 1  1 81  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1 54  1
  1  1  1  1  1  1  1  1  1  1  1  1  1  1]

showing that the ss anomaly is static in all but two channels. Given that this is being used as a negative example in contrastive learning Im not sure this behavior makes sense...

Potential fix

Im pretty sure the issue is in the __call__ of SubAnomaly, first the empty anomalies are initialized with

  window = X.copy()
  anomaly_seasonal = np.zeros_like(window)
  anomaly_trend = np.zeros_like(window)
  anomaly_global = np.zeros_like(window)
  anomaly_contextual = np.zeros_like(window)
  anomaly_shapelet = np.zeros_like(window)

Then anomalies are injected in random channels

num_dims = np.random.randint(1, int(num_features/5))
for k in range(num_dims):
    i = np.random.randint(0, num_features)
    temp_win = window[:, i].reshape((window.shape[0], 1))
    # inject anomaly based on temp_win ...

but most of the channels in anomaly_[whatever] remain zero like they were initialized. Could probably fix by initializing instead with

  anomaly_seasonal = window.copy()
  anomaly_trend = window.copy()
  anomaly_global = window.copy()
  anomaly_contextual = window.copy()
  anomaly_shapelet = window.copy()

But at the end of the day I might be misunderstanding the intended behavior. Intuitively I dont see the merit in showing the model a mostly static tensor as a negative example in a contrastive learning environment. I'd appreciate any clarification on the intended behavior and whether the current implementation is correct. Thank you!