wikillm

Paper · 2022-01-18 · cs.LG

TranAD: Deep Transformer Networks for Anomaly Detection in Multivariate Time Series Data

TranAD detects and diagnoses anomalies in multivariate time series with a transformer encoder and two decoders. A two-phase pass conditions the second reconstruction on the first one's error, and adversarial two-decoder training amplifies small deviations; MAML is added for limited data. Author-reported: up to 17% higher F1 than baselines and up to 99% lower training time.

Anomaly detectionSelf-attentionPositional encodingMeta-learninganomaly-detectionmultivariate-time-seriestransformeradversarial-trainingmeta-learning

arXivPDFCodeMarkdown25 min read

AI-compiled by claude-haiku-5-5 on 2026-10-19, not yet reviewed by a human. Every claim cites its source; check it when a detail matters.

In plain words

Many industrial systems produce many sensor readings at once, and someone needs to notice when they stop behaving normally. Labelled failures are rare, so the usual approach is to learn what normal looks like and flag anything the model cannot reproduce. This paper applies that idea with a transformer, a neural network that looks at a whole stretch of the signal at once rather than one step at a time.

The model takes a short window of recent readings and tries to rebuild it. A reading that cannot be rebuilt well is suspicious. The model runs twice. The first pass makes a rough rebuild. The second pass is given the rough rebuild's error as an extra hint, so it concentrates on the parts that looked wrong. The second pass also sees the longer history of the signal, which helps it judge whether a short spike is part of a trend.

Training uses two output heads that push against each other, so that small deviations stand out more clearly. The authors also train the model with a meta-learning method intended to help when data is limited. They report that their model trains much faster than the baselines they compare with, and that it scores higher on F1 and AUC on the public benchmarks they use. Those comparisons are the authors' own.

TL;DR

  • TranAD is a transformer-based detector and diagnoser for multivariate time series. It scores each timestamp by how badly the window around it can be reconstructed §3.5.
  • Inference runs in two phases: a first reconstruction gives an error signal (the focus score) that conditions a second reconstruction §3.4 eq. 4 eq. 5.
  • Training uses two decoders with a reconstruction loss and an adversarial term, plus MAML, with an offline and an online stage §3.4 alg. 1.
  • Author-reported: average F1 of 0.8802 on the full training data (dataset count is inconsistent in the paper, see Pitfalls), up to 17.06% higher F1 than the state-of-the-art baselines, and training time 75%–99% lower §4.3 §5.2 tab. 2 tab. 5.
  • An official BSD-3-Clause repository exists, but the released TranAD training loop omits the adversarial sign and the meta-learning step, and uses different encoder sizes and a fixed 5-epoch schedule. Read "Paper vs code" before reimplementing models.py.

Problem

Anomaly detection in multivariate time series is hard for four reasons: anomaly labels are scarce, the data is volatile, the detector must run with very low latency, and the detector must also say which dimensions caused an alarm §1. Recurrent models (LSTM-based) are slow to train and to run, and their fixed-size input windows limit how much long-term context they see §1 §2. Simple transformer encoder-decoders can miss anomalies whose deviation from normal is small §1 §3.4.

Training uses only the training series; the labels are used for evaluation on the test series §3.1 §4.1.

Method

The model is reconstruction-based. Each timestamp gets an anomaly score §3.5.

Problem definition §3.1. A multivariate series has m dimensions. Detection gives one 0/1 label per timestamp. Diagnosis gives one 0/1 label per dimension and timestamp §3.1.

Preprocessing §3.2.

  1. Normalise each dimension with the minimum and maximum of the training series, plus a small constant, so the values fall roughly in [0, 1) §3.2 eq. 1.
  2. Turn the series into sliding windows of length K ending at each timestamp, so each timestamp keeps its local context §3.2.
  3. Pad the first windows by repeating a constant vector; the text repeats x_t §3.2 (see Pitfalls).
  4. Train and test on windows, not single vectors §3.2.

Architecture §3.3. Dot-product attention scales the scores by \sqrt{m} §3.3 eq. 2. Multi-head attention uses h heads, which the paper sets equal to m §3.3 eq. 3 §4. The first encoder takes the window concatenated with the focus score, adds position encoding, and encodes the sequence up to the current timestamp §3.3 eq. 4. The window encoder applies masked self-attention to the window and then attends over the first encoder's output §3.3 eq. 5. Two identical decoders, each ending in a sigmoid, reconstruct the window §3.3 eq. 6. fig. 1 shows the architecture: the encoders, the window encoder and the two decoders.

Two-phase inference §3.4.

  • Phase 1 runs with a zero focus score. Decoder 1 gives O_1 and decoder 2 gives O_2 §3.4 alg. 1 line 5.
  • The focus score is the deviation between the input window and O_1, measured by an L2 norm. Phase 2 feeds this focus score back into the encoder and decoder 2 produces \hat{O}_2 §3.4 alg. 1 line 6. The paper calls this self-conditioning §3.4.

Adversarial two-decoder loss §3.4. Phase 1 losses are L_1 = \|O_1 - W\| and L_2 = \|O_2 - W\| §3.4 eq. 7. The adversarial objective is a minimax: decoder 1 minimises the phase-2 error and decoder 2 maximises it §3.4 eq. 8 eq. 9. The combined losses are weighted by an evolving factor \epsilon^{-n}, where \epsilon is a training parameter close to one, so the weight on the reconstruction term changes over training §3.4 eq. 10. The paper says the reconstruction term is weighted high at first and the adversarial term is raised as training proceeds §3.4.

Meta-learning §3.4. To help with limited data, the parameters are also updated with MAML after each pass over the windows: an inner step \theta' = \theta - \alpha \nabla_\theta L and an outer step \theta \leftarrow \theta - \beta \nabla_\theta L(\theta') §3.4 eq. 11 eq. 12 alg. 1 line 11. The paper does not state the value of \alpha §4 (see Hyperparameters).

Online inference and thresholding §3.5. At test time, the model runs in the same two phases, using only data up to the current timestamp §3.5 alg. 2 lines 2–3. The anomaly score is half the L2 norm of the phase-1 error plus half the L2 norm of the phase-2 error §3.5 eq. 13. A timestamp is flagged for dimension i when the score s_i reaches a threshold chosen by Peak-Over-Threshold (POT), an extreme-value method (Siffer et al., 2017) §3.5. The timestamp is flagged for detection when any dimension is flagged §3.5 eq. 14 alg. 2 lines 5–6. The paper reports that POT gave about 7.2% higher average F1 than the annual-maximum method for TranAD §3.5.

Training schedule. The training series is split 80% for training and 20% for validation, and training stops when validation accuracy starts to fall §4. The authors call the training loop "offline" and the test loop "online" §3.4 §3.5.

Algorithm 1 (training), in Python-like form:

Pseudocode
# TranAD offline training  [alg. 1] [§3.4]
# Input:  windows (T, K, m) from the training series  [§3.2]
# Output: encoder E, decoders D1 and D2

def train(windows, encoder, decoder_1, decoder_2, epsilon, n_limit):
    # n counts processed windows in Alg. 1, not epochs  [alg. 1 line 10]
    n = 0
    while True:
        for window in windows:
            # Phase 1: reconstruct with a zero focus score  [alg. 1 line 5]
            focus = zeros_like(window)
            out_1 = decoder_1(encoder(window, focus))
            out_2 = decoder_2(encoder(window, focus))
            # Phase 2: focus = norm of phase-1 error  [alg. 1 line 6]
            focus = norm(out_1 - window)
            out_hat_2 = decoder_2(encoder(window, focus))
            # Evolving weight of the reconstruction term  [eq. 10]
            weight = epsilon ** (-n)
            # Decoder 1 minimises phase-2 error; decoder 2 maximises it  [eq. 9]
            loss_1 = weight * norm(out_1 - window)
            loss_1 += (1 - weight) * norm(out_hat_2 - window)
            loss_2 = weight * norm(out_2 - window)
            loss_2 -= (1 - weight) * norm(out_hat_2 - window)
            update(encoder, decoder_1, decoder_2, loss_1, loss_2)
            n = n + 1
        # Once per pass: MAML step on a random batch  [eq. 11-12]
        meta_learn(encoder, decoder_1, decoder_2, random_batch(windows))
        if not n < n_limit:
            break

Algorithm 2 (online scoring), in Python-like form, one timestamp at a time:

Pseudocode
# TranAD online scoring  [alg. 2] [§3.5]
# Input:  trained encoder, decoder_1, decoder_2; one test window per step
# Output: anomaly score per timestamp (and per dimension, see Pitfalls)

def score(window):
    # Phase 1 with a zero focus score  [alg. 2 line 2]
    out_1 = decoder_1(encoder(window, zeros_like(window)))
    out_2 = decoder_2(encoder(window, zeros_like(window)))
    # Phase 2, conditioned on the phase-1 deviation  [alg. 2 line 3]
    focus = norm(out_1 - window)
    out_hat_2 = decoder_2(encoder(window, focus))
    # Half phase-1 error plus half phase-2 error  [eq. 13]
    return 0.5 * norm(out_1 - window) + 0.5 * norm(out_hat_2 - window)

Threshold and labels, applied to the score of each dimension:

Pseudocode
# Labels from scores  [§3.5] [eq. 14] [alg. 2 lines 5-6]
# threshold_i is POT fitted to the anomaly scores of the training windows
def label(score_i, threshold_i):
    # Dimension i is flagged when its score reaches its threshold  [eq. 14]
    flag_i = score_i >= threshold_i
    # Timestamp is anomalous if any dimension is flagged  [eq. 14]
    return any(flag_i)

Key equations, copied in form from the paper (numbers as they appear in the compiled paper):

x_t \leftarrow \frac{x_t - \min(\mathcal{T})}{\max(\mathcal{T}) - \min(\mathcal{T}) + \epsilon'} eq. 1

\mathrm{Attention}(Q, K, V) = \mathrm{softmax}\left(\frac{QK^T}{\sqrt{m}}\right)V eq. 2

s = \tfrac{1}{2} \lVert O_1 - \hat{W} \rVert_2 + \tfrac{1}{2} \lVert \hat{O}_2 - \hat{W} \rVert_2 eq. 13

y_i = \mathbf{1}(s_i \geq \mathrm{POT}(s_i)), \quad y = \bigvee_i y_i eq. 14

Hyperparameters

Values stated in the paper, with the official code's value where the code sets one. "not stated" means the paper gives no value.

NameValueWhereNotes
Window size K10§4 §5.3 fig. 7The paper picks 10 as a balance between F1 and training time §5.3. Code: n_window = 10 models.py
Transformer encoder layers1§4Code: TransformerEncoder(..., 1) models.py
Feed-forward layers in encoder2§4Code has two linear layers in each encoder and decoder block dlutils.py
Hidden units in encoder layers64§4Code: dim_feedforward=16 models.py (see Paper vs code)
Dropout in encoders0.1§4Code: dropout=0.1 models.py
Attention heads hequal to the number of dimensions m§4Code: nhead=feats models.py. Only dataset-specific hyperparameter, per the paper.
OptimizerAdamW§4Code: AdamW(..., weight_decay=1e-5), weight decay not stated in paper main.py
Initial learning rate0.01§4Code uses a per-dataset learning rate, from 0.0001 to 0.009 constants.py (see Paper vs code)
Meta learning rate \beta0.02§4Not in code (see Paper vs code)
Learning-rate schedulestep scheduler, "step size of 0.5"§4Unit and decay factor not stated. Code: StepLR(optimizer, 5, 0.9), i.e. every 5 epochs, factor 0.9 main.py
Train / validation split80% / 20%§4Not in code: the full training series is used main.py
Stopping rulestop when validation accuracy starts to fall§4Accuracy is not defined for reconstruction. Code runs a fixed 5 epochs main.py
Epochs / iteration limit Nnot statedalg. 1Code: 5 epochs
Evolving-weight constant \epsilon"close to one", value not stated§3.4 eq. 10Code uses weights 1/n and 1 - 1/n with n the epoch number models.py
Normalisation constant \epsilon'"small constant", value not stated§3.2 eq. 1Code adds 10^{-4} for SMAP, MSL, MBA and MSDS preprocess.py
Meta inner step \alphalearning rate, value not stated§3.4 eq. 11Not in code
Batch sizenot statedCode: 128 models.py
POT coefficient10^{-4}§4Code: q = 10^{-5} pot.py
POT low quantile0.07 SMAP, 0.01 MSL, 0.001 others§4Not used in code. Code sets a per-dataset initial level and a multiplier constants.py pot.py
Grid searchused for all hyperparameters except K§4Search ranges and the split used to pick them are not stated
HardwareIntel i7-10700K CPU, 64 GB RAM, NVIDIA RTX 3080, Windows 11; PyTorch 1.7.1§4 (footnote)Training time is measured on this machine tab. 5
Baseline hyperparameters"as presented in their respective papers"§4Several baselines were re-implemented by the authors (footnote)

Dataset statistics, copied from the paper's table with its numbers tab. 1. Values in parentheses are the number of sequences in each dataset; results are averaged over sequences §4.1:

DatasetTrainTestDimensions (sequences)Anomalies (%)
NAB403340331 (6)0.92
UCR160059001 (4)1.88
MBA1000001000002 (8)0.14
SMAP13518342761725 (55)13.13
MSL583177372955 (3)10.72
SWaT49680044991951 (1)11.98
WADI1048571172801123 (1)5.99
SMD70840570842038 (4)4.16
MSDS14643014643010 (1)5.37

Results

Author-reported. Setup: the nine datasets in tab. 1, the baselines listed in §4, and one NVIDIA RTX 3080 machine §4. The paper excludes the Yahoo benchmark and keeps only the non-trivial sequences of MSL and SMD §4.1.

Headline numbers §4.3. Average F1 across datasets is 0.8802 with full training data and 0.8012 with 20% of training data (F1*) §4.3. Against the state-of-the-art baselines, the paper reports gains of up to 17.06% in F1, 14.64% in F1*, 11.69% in AUC and 11.06% in AUC* §4.3. Training time is 75%–99% lower than the baselines §5.2 tab. 5. Anomaly diagnosis on SMD and MSDS identifies between 46.3% and 75.3% of the root-cause dimensions §4.3 tab. 4.

Full detection results (F1 and AUC on full training data) tab. 2. Bold is the paper's bold.

NAB, UCR and MBA (P = precision, R = recall, AUC = ROC AUC):

MethodNAB PNAB RNAB AUCNAB F1UCR PUCR RUCR AUCUCR F1MBA PMBA RMBA AUCMBA F1
MERLIN0.80130.72620.84140.76190.75420.80180.89840.77730.98460.49130.78280.6555
LSTM-NDT0.64000.66670.83220.65310.52310.82940.97810.64160.92070.97180.97800.9456
DAGMM0.76220.72920.85720.74530.53370.97180.99160.68900.94750.99000.98580.9683
OmniAnomaly0.84210.66670.83300.74420.83460.99990.99810.90980.85611.00000.95700.9225
MSCRED0.85220.67000.84010.75020.54410.97180.99200.69760.92721.00000.97990.9623
MAD-GAN0.86660.70120.84780.77520.85380.98910.99840.91650.93961.00000.98360.9689
USAD0.84210.66670.83300.74420.89521.00000.99890.94470.89530.99890.97010.9443
MTAD-GAT0.84210.72720.82210.78040.78120.99720.99780.87610.90181.00000.97210.9484
CAE-M0.79180.80190.80190.79680.69811.00000.99570.82220.84420.99970.96610.9154
GDN0.81290.78720.85420.79980.68940.99880.99590.81580.88320.98920.95280.9332
TranAD0.88890.98920.95410.93640.94071.00000.99940.96940.95691.00000.98850.9780

SMAP, MSL and SWaT:

MethodSMAP PSMAP RSMAP AUCSMAP F1MSL PMSL RMSL AUCMSL F1SWaT PSWaT RSWaT AUCSWaT F1
MERLIN0.15770.99990.74260.27250.26130.46450.62810.33450.65600.25470.61750.3669
LSTM-NDT0.85230.73260.86020.78790.62881.00000.95320.77210.77780.51090.71400.6167
DAGMM0.80690.98910.98850.88880.73631.00000.97160.84820.99330.68790.84360.8128
OmniAnomaly0.81300.94190.98890.87280.78480.99240.97820.87650.97820.69570.84670.8131
MSCRED0.81750.92160.98210.86640.89120.98620.98070.93630.99920.67700.84330.8072
MAD-GAN0.81570.92160.98910.86540.85160.99300.98620.91690.95930.69570.84630.8065
USAD0.74800.96270.98900.84190.79490.99120.97950.88220.99770.68790.84600.8143
MTAD-GAT0.79910.99910.98440.88800.79170.98240.98990.87680.97180.69570.84640.8109
CAE-M0.81930.95670.99010.88270.77511.00000.99030.87330.96970.69570.84640.8101
GDN0.74800.98910.98640.85180.93080.98920.98140.95910.96970.69570.84620.8101
TranAD0.80430.99990.99210.89150.90380.99990.99160.94940.97600.69970.84910.8151

WADI, SMD and MSDS:

MethodWADI PWADI RWADI AUCWADI F1SMD PSMD RSMD AUCSMD F1MSDS PMSDS RMSDS AUCMSDS F1
MERLIN0.06360.76690.59120.11740.28710.58040.71580.38420.72540.31100.50220.4353
LSTM-NDT0.01380.78230.67210.02710.97360.84400.96710.90420.99990.80120.80130.8896
DAGMM0.07600.99810.85630.14120.91030.99140.99540.94910.98910.80260.90130.8861
OmniAnomaly0.31580.65410.81980.42600.88810.99850.99460.94011.00000.79640.89820.8867
MSCRED0.25130.73190.84120.37410.72760.99740.99210.84141.00000.79830.89430.8878
MAD-GAN0.22330.91240.80260.35880.99910.84400.99330.91500.99820.61070.80540.7578
USAD0.18730.82960.87230.30560.90600.99740.99330.94950.99120.79590.89790.8829
MTAD-GAT0.28180.80120.88210.41690.82100.92150.99210.86830.99190.79640.89820.8835
CAE-M0.27820.79180.87280.41170.90820.96710.97830.93670.99080.84390.90130.9115
GDN0.29120.79310.87770.42600.71700.99740.99240.83420.99890.80260.91050.8900
TranAD0.35290.82960.89680.49510.92620.99740.99740.96050.99990.86260.90130.9262

Results with 20% of the training data (AUC*, F1*) tab. 3. The paper uses a centred 20% of the training series (see Paper vs code). Bold is the paper's bold.

NAB, UCR and MBA:

MethodNAB AUC*NAB F1*UCR AUC*UCR F1*MBA AUC*MBA F1*
MERLIN0.80290.76190.89840.77730.78280.6555
LSTM-NDT0.80130.62120.89130.51980.96170.9282
DAGMM0.78270.61250.98120.57180.96710.9396
OmniAnomaly0.81290.67130.97280.79180.94070.9217
MSCRED0.82990.70130.96370.49290.94990.9108
MAD-GAN0.81940.71090.99590.82160.95500.9192
USAD0.72670.67810.99670.85380.96970.9425
MTAD-GAT0.69560.70130.99740.86710.96880.9425
CAE-M0.73120.71260.99260.75250.96160.9002
GDN0.82990.70130.99370.80290.96710.9316
TranAD0.92170.84210.99890.93990.97180.9617

SMAP, MSL and SWaT:

MethodSMAP AUC*SMAP F1*MSL AUC*MSL F1*SWaT AUC*SWaT F1*
MERLIN0.74260.27250.62810.33450.61750.3669
LSTM-NDT0.70070.54180.95200.76080.66900.4145
DAGMM0.98810.83690.96060.80100.84210.8001
OmniAnomaly0.98790.81310.97030.84240.83190.7433
MSCRED0.98110.80500.97970.82320.83850.7922
MAD-GAN0.98770.84680.96490.81900.84560.8012
USAD0.98830.83790.96490.81900.84380.8087
MTAD-GAT0.98140.82250.97820.80240.84590.8079
CAE-M0.98920.83120.98360.73030.84580.7841
GDN0.98870.84110.94140.89590.83900.8072
TranAD0.98850.88890.98570.91720.84380.8094

WADI, SMD and MSDS:

MethodWADI AUC*WADI F1*SMD AUC*SMD F1*MSDS AUC*MSDS F1*
MERLIN0.59120.11740.71580.38420.50220.4353
LSTM-NDT0.66370.00000.95630.67540.78130.7912
DAGMM0.64970.06300.98450.89860.77630.8389
OmniAnomaly0.79130.10170.98590.93520.56130.8389
MSCRED0.60290.04130.97680.80040.77160.8283
MAD-GAN0.53830.09370.86350.93180.50020.7390
USAD0.70110.07330.98540.92130.76130.8389
MTAD-GAT0.62670.05200.97980.66610.61220.8248
CAE-M0.61090.07810.95690.93180.60010.8389
GDN0.61210.04120.98110.71070.68190.8389
TranAD0.76880.06490.98690.94780.81130.8391

Anomaly diagnosis on SMD and MSDS tab. 4. H = HitRate@P%, N = NDCG@P%, with P = 100 or 150 §4.2. Bold is the paper's bold.

MethodSMD H@100%SMD H@150%SMD N@100%SMD N@150%MSDS H@100%MSDS H@150%MSDS N@100%MSDS N@150%
MERLIN0.59070.61770.41500.49120.38160.56260.30100.3947
LSTM-NDT0.38080.52250.36030.44510.15040.29590.11240.1993
DAGMM0.49270.60910.51690.58450.26170.43330.31530.4154
OmniAnomaly0.45670.56520.45450.51250.28390.43650.33380.4231
MSCRED0.42720.51800.46090.51640.23220.34690.22970.2962
MAD-GAN0.46300.57850.46810.55220.38560.55890.42770.5292
USAD0.49250.60550.51790.57810.30950.47690.35340.4515
MTAD-GAT0.34930.47770.37590.45300.58120.58850.59260.6522
CAE-M0.47070.58780.54740.61780.25300.41710.20470.3010
GDN0.31430.43860.29800.37240.22760.33820.29210.3570
TranAD0.49810.64010.49410.61780.46300.75330.59810.6963

Training time per epoch, in seconds tab. 5. The paper says MERLIN's entry is discord discovery time on the test data rather than training time §5.2. Bold is the paper's bold.

MethodNABUCRMBASMAPMSLSWaTWADISMDMSDS
MERLIN3.284.0920.196.895.1210.12132.6972.3242.22
LSTM-NDT10.648.7127.8027.6226.2426.43297.12373.14361.12
DAGMM25.3820.7874.6219.0516.4118.51178.17204.36187.54
OmniAnomaly38.2727.96109.8627.0521.3128.39212.99276.97277.10
MSCRED258.86262.45592.1316.1333.47183.671349.05237.66109.63
MAD-GAN39.8025.71160.2929.4926.2727.79293.60314.82285.25
USAD31.2121.10120.8623.6321.2222.72242.86250.97232.82
MTAD-GAT145.0097.12233.081015.031287.42103.929812.136564.111304.09
CAE-M22.4819.4267.44187.35575.9641.255525.623102.12552.83
GDN83.8458.78159.0162.3396.7159.404063.05809.94585.34
TranAD1.250.844.083.555.270.87115.9143.5617.15

Ablations, F1 and F1 tab. 6 §5.1.* Bold is the paper's bold.

NAB, UCR and MBA:

VariantNAB F1NAB F1*UCR F1UCR F1*MBA F1MBA F1*
TranAD0.93640.84210.96940.93990.97800.9617
w/o transformer0.88500.80190.84660.54950.97490.9584
w/o self-condition0.88870.81020.91910.90280.97700.9617
w/o adversarial training0.90120.81020.96340.92890.97520.9592
w/o MAML0.90680.82100.96890.93040.97560.9617

SMAP, MSL and SWaT:

VariantSMAP F1SMAP F1*MSL F1MSL F1*SWaT F1SWaT F1*
TranAD0.89150.88890.94940.91720.81510.8094
w/o transformer0.86430.81470.91370.90370.81430.6360
w/o self-condition0.88940.81530.91750.89130.79530.8094
w/o adversarial training0.89060.84760.94550.91720.80280.7832
w/o MAML0.89150.88990.94660.67320.81430.8079

WADI, SMD and MSDS:

VariantWADI F1WADI F1*SMD F1SMD F1*MSDS F1MSDS F1*
TranAD0.49510.06490.96050.94780.92620.8391
w/o transformer0.21810.00370.90710.90320.88670.8389
w/o self-condition0.36200.06310.95020.88470.87480.8214
w/o adversarial training0.38200.06210.91770.86670.91810.8389
w/o MAML0.48150.05530.94330.81640.88700.8389

Baseline re-implementation check tab. 7 app. A. Appendix A compares the MERLIN baseline in the original MATLAB code with the authors' Python port, on the same benchmarks. The paper reports that the Python version gives scores close to the original, with training-time gains of up to 96%. The full table has precision, recall, AUC, F1 and time for each dataset; the paper gives the MinL and MaxL values it used per dataset app. A.

Figures, in words.

  • fig. 1 shows the TranAD architecture: the window encoder feeding two decoders §3.3.
  • fig. 2 plots the anomaly predictions against the ground truth for a sample series §3.5.
  • fig. 3 shows, on SMD, that focus scores rise at peaks and sudden changes and that attention weights concentrate on dimensions with larger deviations §3.5.
  • fig. 4 gives critical-difference diagrams for F1 and AUC, based on a Friedman test followed by Wilcoxon signed-rank tests at \alpha = 0.05. TranAD ranks first §4.3.
  • fig. 5 plots predicted and ground-truth labels on the MSDS test set §4.3.
  • fig. 6 varies the fraction of training data from 20% to 100%, and shows F1, AUC and training time, with 90% confidence bounds. TranAD has the highest F1 and the lowest training time at every fraction §5.3.
  • fig. 7 varies the window size and shows the same three metrics §5.3.

Paper vs code

Official repository: imperial-qore/TranAD, commit 7ffb98d0c18189cc3d9ab732b4cb0278200a0af0 (cloned at depth 1), BSD-3-Clause, copyright Shreshth Tuli LICENSE. The paper links it in its Software Availability section. The repository is marked "refactored" and says it is not the code used for the baselines README.md.

Each item cites the paper and the code.

  1. Adversarial sign is not implemented. The paper gives decoder 2 a negative loss on the phase-2 error §3.4 eq. 9. The code's TranAD branch takes one loss, the weighted sum of both decoders' errors with positive signs: loss = mean(l1) where l1 = (1/n) * l(z[0]) + (1 - 1/n) * l(z[1]) models.py. Both decoders are updated by the same gradient, so the minimax game in eq. 8 is absent.
  2. Weight schedule differs. The paper uses \epsilon^{-n} with n counted per window §3.4 alg. 1 line 10. The code uses 1/n and 1 - 1/n, with n equal to the epoch index plus one models.py main.py.
  3. Meta-learning is absent. The paper has a MAML step once per pass §3.4 alg. 1 line 11 eq. 11 eq. 12. The released code has no inner or outer MAML update. A search for meta and maml finds only class-name comments models.py models.py models.py models.py.
  4. Focus score is squared, not a norm, and not detached. The paper defines the focus score as the L2 deviation of O_1 from W §3.4 alg. 1 line 6. The code uses elementwise squared error c = (x1 - src) ** 2 and does not detach it, so gradients flow through the focus into the phase-1 decoder models.py.
  5. Decoder input is the last timestep only. The paper's window encoder attends over the full window with a causal mask §3.3 eq. 5. The code's decoder takes tgt = elem, the last row of the window, repeated twice, as its query, and passes no mask models.py models.py. With one query step, the causal mask is not needed.
  6. Architecture count. The paper says the model has two transformer encoders and two decoders §3.3. The code has one encoder and two decoders models.py.
  7. Feed-forward size. The paper gives 64 hidden units per encoder layer §4. The code uses dim_feedforward=16 for the encoder and both decoders, with d_{\text{model}} = 2m models.py.
  8. Encoder block has no LayerNorm and uses LeakyReLU. The paper's equations 4 and 5 use LayerNorm after each residual §3.3 eq. 4 eq. 5. The custom encoder and decoder layers have residuals but no normalisation, and use LeakyReLU as the activation dlutils.py dlutils.py.
  9. Attention scale. The paper scales by \sqrt{m} §3.3 eq. 2. The code uses PyTorch's multi-head attention, which scales by the square root of the head dimension. With d_{\text{model}} = 2m and h = m, the head dimension is 2 dlutils.py models.py.
  10. Input scaling is not in the paper. The code multiplies the concatenated input by \sqrt{m} before position encoding models.py.
  11. Positional encoding differs from the cited form. The paper cites the standard sinusoidal encoding §3.3. The code adds both sine and cosine of the same angle to every dimension dlutils.py.
  12. Anomaly score. The paper scores with half the L2 norm of each phase's error §3.5 eq. 13. The code scores with the squared error of the phase-2 output only, per dimension models.py. Phase 1 is not used at test time.
  13. Detection rule. The paper labels a timestamp when any dimension passes its own POT threshold §3.5 eq. 14. The code's reported detection result runs one POT fit on the mean score over dimensions, labelled anomalous when any dimension is labelled main.py. Per-dimension POT is used only for diagnosis main.py.
  14. POT parameters. The paper gives coefficient 10^{-4} and low quantiles §4. The code's pot_eval uses q = 10^{-5} pot.py, starts from a per-dataset level constants.py, and multiplies the mean threshold by a per-dataset factor pot.py. The paper's quantiles do not appear in the code.
  15. Training duration and split. The paper uses an 80/20 split with early stopping §4. The code trains on the full training series for 5 epochs main.py.
  16. Optimiser and scheduler. The paper gives one initial learning rate, 0.01, and a meta rate, 0.02 §4. The code uses per-dataset rates constants.py, AdamW with weight decay 10^{-5} main.py, and StepLR(optimizer, 5, 0.9) main.py. Batch size is 128 models.py.
  17. Window padding. The text repeats x_t §3.2. The code pads with the first point of the series, data[0] main.py.
  18. Window alignment. The window for index i is data[i-w:i], which ends at x_{i-1}, not x_i main.py. Its last row is the one scored models.py. The labels are not shifted in the scoring loop. The code shifts the ground truth by one step only for plotting main.py. This reading comes from the code and was not verified by running it.
  19. Only one trace per dataset is evaluated. The paper reports averages over all sequences §4.1. load_dataset loads a single hard-coded trace, such as P-1_ for SMAP or 136_ for UCR main.py. The README's sample SMAP output has precision 0.8043, recall 0.9999 and F1 0.8915 README.md. These match the paper's SMAP row in tab. 2. Whether the table row is one trace or an average is unresolved.
  20. Data preprocessing differs from the paper's description. The paper normalises with training-series minima and maxima §3.2 eq. 1. The code:
    • for NAB, uses the same series as both train and test preprocess.py;
    • for WADI, subsamples every tenth row and normalises train and test each with their own minimum and maximum preprocess.py;
    • for MSDS, subsamples every fifth row and takes the minimum and maximum over train and test together preprocess.py;
    • for SMD, writes the raw values with no normalisation preprocess.py;
    • for UCR, normalises the whole series with its own minimum and maximum before splitting preprocess.py;
    • for SWaT, keeps rows 3000–6000 for training and 7000–12000 for test preprocess.py, which does not match the sizes in tab. 1. The repository's data/SWaT/series.json is empty, so this cannot be checked.
  21. 20% of the training data is a centred slice. The paper's sensitivity study uses randomly sampled subsequences §5.3. The code keeps a central contiguous 20% of the series utils.py main.py.
  22. Diagnosis rounding. The paper defines the top-candidate count as P% of the ground-truth dimensions, with 100% and 150% as examples §4.2. The code computes round(p * len / 100), which in Python rounds half to even diagnosis.py diagnosis.py. Cases that land on .5 may differ from a round-half-up reading.
  23. POT initialisation uses training scores. The code fits POT on the training windows' scores main.py pot.py. The paper does not state this in the algorithm, though it describes a threshold computed from past scores §3.5.
  24. Baselines. The README says the baseline implementations in the repository are not the ones used in the paper README.md. The paper says some baselines were re-implemented and some use the original code §4 (footnote).

Pitfalls

  • Focus score is ambiguous. The paper writes F = L_1 §3.4, a scalar norm, but also calls it a deviation matrix with the window's shape §3.3. The code uses an elementwise squared error (see item 4 in Paper vs code). Pick one and state it.
  • Evolving weight direction depends on \epsilon. For \epsilon < 1, \epsilon^{-n} grows without bound, so 1 - \epsilon^{-n} becomes large and negative. That reverses the schedule the text describes §3.4 eq. 10. The text wants the reconstruction weight high first and falling. For that, \epsilon > 1 is needed. The value of \epsilon is not stated.
  • Counter n counts windows. Algorithm 1 increments n inside the per-window loop alg. 1 line 10, so \epsilon^{-n} changes per window. The text calls n the training epoch §3.4. The code uses the epoch count (see item 2 in Paper vs code).
  • Which decoder produces \hat{O}_2 at test time. Algorithm 2 assigns \hat{O}_2 from both D_1 and D_2 on one line alg. 2 line 3, while training uses only D_2 alg. 1 line 6. The score in eq. 13 needs one \hat{O}_2. The text refers to "the pair (O_1, \hat{O}_2)" §3.5.
  • Per-dimension scores. eq. 13 uses vector norms, giving one number per window. The text then talks about per-dimension scores s_i §3.5 eq. 14. Use a per-dimension error if you want diagnosis; the paper does not write this out.
  • Normalisation can push test values outside [0, 1). Eq. 1 uses training minima and maxima §3.2. Test values outside the training range fall outside the sigmoid's output range (0, 1), so the decoder cannot match them and the error grows.
  • Padding. Repeating x_1 (code) and repeating x_t (text) differ for early windows. Test windows are affected too.
  • Early stopping on "accuracy". Reconstruction has no accuracy. The paper's stopping rule §4 cannot be implemented as written. The code runs five epochs.
  • Learning-rate scale. Learning rates differ by 90× across datasets in the code (0.0001 to 0.009) constants.py. None of the code's per-dataset values equals the paper's single 0.01 §4 constants.py.
  • Meta-learning is a claimed component with no code. The ablation without MAML shows about 1% F1 change but about 12% F1* change on average §5.1. Without the released code path, the MAML contribution cannot be checked.
  • Diagnosis and detection can disagree. Detection in the code is a single threshold on the mean score (item 13). Diagnosis thresholds each dimension. A timestamp can be detected but have no dimension flagged, or the reverse.
  • WADI text and table disagree on the best F1*. The text says OmniAnomaly has the highest WADI F1* at 0.1017 §4.3. The table bolds MERLIN at 0.1174 tab. 3. Check before quoting.
  • Diagnosis percentages at small counts. The code's rounding (item 22) can change the top-candidate count, and if one NDCG call raises an exception, the function returns an empty result for all NDCG keys diagnosis.py. Either can change the reported averages.
  • The training-time comparison is one machine. The paper reports per-epoch time on one RTX 3080 workstation §4 tab. 5. The code's epoch count (5) and the paper's early-stopping schedule are different, so per-epoch time is the only measure they share.
  • Relative gains are per dataset. "Up to 17%" §1 §4.3 is the largest relative increase over the state-of-the-art baselines on any one dataset, not an average.
  • POT parameters are hard to reproduce. The paper's quantiles and coefficient §4 and the code's level and multiplier constants.py pot.py give different thresholds. Report which one you use.
  • The Python MERLIN port is not the original. The authors grid-searched MinL and MaxL per dataset to maximise F1 app. A. The paper does not say which split the search used, so it may have used the evaluation labels. Treat the MERLIN row with that in mind.
  • Gains are on public benchmarks with known quality concerns. The paper drops NAB's nyc-taxi trace and the Yahoo benchmark because of label problems §4.1, and says it shares concerns about benchmark quality in general §4.1. Comparisons inherit those concerns.
  • Dataset count is inconsistent. The abstract says six public datasets [abstract]. Section 4.1 says seven §4.1. Table 1 and Table 2 list nine tab. 1 tab. 2. Check which set an average covers before comparing it with another paper.
  • Test-time cost. Phase 2 repeats the encoder and decoders for each timestamp alg. 2, so each score costs about two forward passes. The paper does not report memory use.

Lineage

  • Builds on: the transformer encoder-decoder with scaled dot-product attention and positional encoding §3.3 (Vaswani et al., 2017); MAML for meta-learning §3.4 (Finn et al., 2017); the adversarial two-decoder idea from USAD §2 (Audibert et al., 2020); and POT thresholding from OmniAnomaly §3.5 (Su et al., 2019) §2.
  • Compared against: GDN, CAE-M, MTAD-GAT, USAD, MAD-GAN, MSCRED, OmniAnomaly, DAGMM, LSTM-NDT and MERLIN §4 tab. 2.
  • Superseded by: not known from content already in this wiki.

Source and license

Adapted from “TranAD: Deep Transformer Networks for Anomaly Detection in Multivariate Time Series Data” by Shreshth Tuli, Giuliano Casale, Nicholas R. Jennings (arXiv:2201.07284), used under CC BY 4.0. Changes: condensed and restructured for implementation, with tables, equations and algorithms taken from the paper and its code, each cited to its source. Not affiliated with or endorsed by the authors. Code citations refer to https://github.com/imperial-qore/TranAD at commit 7ffb98d0c181 (BSD-3-Clause). Any quoted code is shown under that license.