3. Autoencoders and anomaly detection¶
This section explores the potential usage of autoencoders in the context of credit card fraud detection.
3.1. Definition and usage¶
An autoencoder is a special type of deep learning architecture used to learn representations of data based solely on descriptive features. The representation, which is a transformation of the raw data, is learned with the objective to reconstruct the original data the most accurately. This representation learning strategy can be used for dimensionality reduction, denoising, or even generative applications.
An autoencoder can be divided into two parts:
The encoder part that maps the input into the representation, also referred to as the “code” or the “bottleneck”.
The decoder that maps the code to a reconstruction of the input.
The encoder and decoder can have complex architectures like recurrent neural networks when dealing with sequential data or convolutional neural networks when dealing with images. But in their simplest form, they are multi-layer feed-forward neural networks. The dimension of the code, which is also the input of the decoder, can be fixed arbitrarily. This dimension is generally chosen to be lower than the original input dimension to reduce the dimensionality and to learn underlying meta variables. The dimension of the output of the decoder is the same as the input of the encoder because its purpose is to reconstruct the input.
The architecture is generally trained end-to-end by optimizing the input reconstruction, i.e. by minimizing a loss that measures a difference between the model’s output and the input. It can be trained with any unlabeled data. Note that when the autoencoder is “deep”, i.e. there are intermediate layers \(h_2\) and \(h_2'\) respectively between the input \(x\) and the bottleneck \(h\) and between the bottleneck and the output \(y\) (like in the figure above), one can train the layers successively instead of simultaneously. More precisely, one can first consider a submodel with only \(x\), \(h_2\) and \(y\) and train it to reconstruct the input from the intermediate code \(h_2\). Then, consider a second submodel with only \(h_2\), \(h\) and \(h_2'\) and train it to reconstruct the intermediate code from the code \(h\). Finally, fine-tune the whole model with \(x\), \(h_2\), \(h\), \(h_2'\) and \(y\) to reconstruct the input.
Autoencoders can be used as techniques for unsupervised or semi-supervised anomaly detection, which led them to be used multiple times for credit card fraud detection [AC15, ZP17].
3.1.1. Anomaly detection¶
Although not detailed before, fraud detection can be performed with both supervised and unsupervised techniques [CLBC+19, VAK+16], as it is a special instance of a broader problem referred to as anomaly detection or outlier detection. The latter generally includes techniques to identify items that are rare or differ significantly from the “normal” behavior, observable in the majority of the data.
And one can easily see how a credit card fraud can be defined as an anomaly in transactions. These anomalies can be rare events or unexpected bursts in the activity of a single cardholder behavior, or specific patterns, not necessarily rare, in the global consumers’ behavior. Rare events or outliers can be detected with unsupervised techniques that learn the normality and which are able to estimate discrepancy to this normality. But detection of other types of anomaly can require supervised techniques with proper training.
Therefore, one can think of three types of anomaly detection techniques:
Supervised techniques that were widely explored in previous sections and chapters. These techniques require annotations on data that consist of two classes, “normal” (or “genuine”) and “abnormal” (or “fraud”), and they learn to discriminate between those classes.
Unsupervised techniques that aim at detecting anomalies by modeling the majority behavior and considering it as “normal”. Then they detect the “abnormal” or fraudulent behavior by searching for examples that do not fit well to the normal behavior.
Semi-supervised techniques that are in between the two above cases and that can learn from both unlabeled and labeled data to detect fraudulent transactions.
An autoencoder can be used to model the normal behavior of data and detect outliers using the reconstruction error as an indicator. In particular, one way to do so is to train it to globally reconstruct transactions in a dataset. The normal trend that is observed in the majority of transactions will be better approximated than rare events. Therefore, the reconstruction error of “normal” data will be lower than the reconstruction error of outliers.
An autoencoder can therefore be considered as an unsupervised technique for fraud detection. In this section, we will implement and test it for both semi-supervised and unsupervised fraud detection.
3.1.2. Representation learning¶
Other than unsupervised anomaly detection, an autoencoder can simply be used as a general representation learning method for credit card transaction data. In a more complex manner than PCA, an autoencoder will learn a transformation from the original feature space to a representation space with new variables that encodes all the useful information to reconstruct the original data.
If the dimension of the code is chosen to be 2 or 3, one can visualize the transaction in the novel 2D/3D space. Otherwise, the code can also be used for other purposes, like:
Clustering: Clustering can be performed on the code instead of the original features. Groups learned from the clustering can be useful to characterize the types of behaviors of consumers or fraudsters.
Additional or replacement variables: The code can be used as replacement variables, or additional variables, to train any supervised learning model for credit card fraud detection.
3.1.3. Content of the section¶
The following puts into practice the use of autoencoders for credit card fraud detection. It starts by defining the data structures for unlabeled transactions. It then implements and evaluates an autoencoder for unsupervised fraud detection. The autoencoder is then used to compute transaction representation for visualization and clustering. Finally, we explore a semi-supervised strategy for fraud detection.
Let us dive into it by making all necessary imports.
# Initialization: Load shared functions and simulated data
# Load shared functions
!curl -O https://raw.githubusercontent.com/Fraud-Detection-Handbook/fraud-detection-handbook/main/Chapter_References/shared_functions.py
%run shared_functions.py
# Get simulated data from Github repository
if not os.path.exists("simulated-data-transformed"):
!git clone https://github.com/Fraud-Detection-Handbook/simulated-data-transformed
This section reuses some “deep learning” specific material that was implemented in the previous section. It includes the evaluation function, the preparation of generators, the early-stopping strategy, the training loop, and so on. This material has been added to the shared functions
.
3.2. Data loading¶
The same experimental setup as the previous section is used for our exploration, i.e. a fixed training and validation period, and the same features from the transformed simulated data (simulated-data-transformed/data/
).
DIR_INPUT='simulated-data-transformed/data/'
BEGIN_DATE = "2018-06-11"
END_DATE = "2018-09-14"
print("Load files")
%time transactions_df=read_from_files(DIR_INPUT, BEGIN_DATE, END_DATE)
print("{0} transactions loaded, containing {1} fraudulent transactions".format(len(transactions_df),transactions_df.TX_FRAUD.sum()))
output_feature="TX_FRAUD"
input_features=['TX_AMOUNT','TX_DURING_WEEKEND', 'TX_DURING_NIGHT', 'CUSTOMER_ID_NB_TX_1DAY_WINDOW',
'CUSTOMER_ID_AVG_AMOUNT_1DAY_WINDOW', 'CUSTOMER_ID_NB_TX_7DAY_WINDOW',
'CUSTOMER_ID_AVG_AMOUNT_7DAY_WINDOW', 'CUSTOMER_ID_NB_TX_30DAY_WINDOW',
'CUSTOMER_ID_AVG_AMOUNT_30DAY_WINDOW', 'TERMINAL_ID_NB_TX_1DAY_WINDOW',
'TERMINAL_ID_RISK_1DAY_WINDOW', 'TERMINAL_ID_NB_TX_7DAY_WINDOW',
'TERMINAL_ID_RISK_7DAY_WINDOW', 'TERMINAL_ID_NB_TX_30DAY_WINDOW',
'TERMINAL_ID_RISK_30DAY_WINDOW']
Load files
CPU times: user 387 ms, sys: 282 ms, total: 669 ms
Wall time: 760 ms
919767 transactions loaded, containing 8195 fraudulent transactions
# Set the starting day for the training period, and the deltas
start_date_training = datetime.datetime.strptime("2018-07-25", "%Y-%m-%d")
delta_train=7
delta_delay=7
delta_test=7
delta_valid = delta_test
start_date_training_with_valid = start_date_training+datetime.timedelta(days=-(delta_delay+delta_valid))
(train_df, valid_df)=get_train_test_set(transactions_df,start_date_training_with_valid,
delta_train=delta_train,delta_delay=delta_delay,delta_test=delta_test)
# By default, scales input data
(train_df, valid_df)=scaleData(train_df, valid_df,input_features)
3.3. Autoencoder implementation¶
For the sake of consistency, the implementation of the autoencoders will be done with the PyTorch
library. As usual, a seed will be used as follows to ensure reproducibility:
SEED = 42
if torch.cuda.is_available():
DEVICE = "cuda"
else:
DEVICE = "cpu"
print("Selected device is",DEVICE)
seed_everything(SEED)
Selected device is cuda
Let us also convert our features and labels into torch tensors.
x_train = torch.FloatTensor(train_df[input_features].values)
x_valid = torch.FloatTensor(valid_df[input_features].values)
y_train = torch.FloatTensor(train_df[output_feature].values)
y_valid = torch.FloatTensor(valid_df[output_feature].values)
The autoencoder has the same input as the baseline feed-forward neural network but a different output. Instead of the fraud/genuine label, its target will be the same as the input. Therefore, the experiments here will not rely on the FraudDataset
defined before but on a new Dataset: FraudDatasetUnsupervised
, which only receives the descriptive features of the transaction x
and returns it as both input and output.
class FraudDatasetUnsupervised(torch.utils.data.Dataset):
def __init__(self, x,output=True):
'Initialization'
self.x = x
self.output = output
def __len__(self):
'Returns the total number of samples'
return len(self.x)
def __getitem__(self, index):
'Generates one sample of data'
# Select sample index
item = self.x[index].to(DEVICE)
if self.output:
return item, item
else:
return item
training_set = FraudDatasetUnsupervised(x_train)
valid_set = FraudDatasetUnsupervised(x_valid)
This Dataset can also be turned into DataLoaders
with the function prepare_generators
from the shared functions.
training_generator,valid_generator = prepare_generators(training_set, valid_set, batch_size = 64)
The second and main element in our deep learning pipeline is the model/module. Since our data are tabular and each sample is a vector, we will resort to a regular feed-forward autoencoder. Its definition is very similar to our supervised feed-forward network for fraud detection, except that the output has as many neurons as the input, with linear activations, instead of a single neuron with sigmoid activation. An intermediate layer, before the representation layer, will also be considered such that the overall succession of layers with their dimensions (input_dim
, output_dim
) are the following:
A first input layer with ReLu activation (
input_size
,intermediate_size
)A second layer with ReLu activation (
intermediate_size
,code_size
)A third layer with ReLu activation (
code_size
,intermediate_size
)An output layer with linear activation (
intermediate_size
,input_size
)
class SimpleAutoencoder(torch.nn.Module):
def __init__(self, input_size, intermediate_size, code_size):
super(SimpleAutoencoder, self).__init__()
# parameters
self.input_size = input_size
self.intermediate_size = intermediate_size
self.code_size = code_size
self.relu = torch.nn.ReLU()
#encoder
self.fc1 = torch.nn.Linear(self.input_size, self.intermediate_size)
self.fc2 = torch.nn.Linear(self.intermediate_size, self.code_size)
#decoder
self.fc3 = torch.nn.Linear(self.code_size, self.intermediate_size)
self.fc4 = torch.nn.Linear(self.intermediate_size, self.input_size)
def forward(self, x):
hidden = self.fc1(x)
hidden = self.relu(hidden)
code = self.fc2(hidden)
code = self.relu(code)
hidden = self.fc3(code)
hidden = self.relu(hidden)
output = self.fc4(hidden)
#linear activation in final layer)
return output
The third element of our pipeline is the optimization problem. The underlying machine learning problem is a regression here, where the predicted and expected outputs are real-valued variables. Therefore, the most adapted loss function is the mean squared error torch.nn.MSELoss
.
criterion = torch.nn.MSELoss().to(DEVICE)
3.4. Using the autoencoder for unsupervised fraud detection¶
As explained in the introduction, the autoencoder’s goal is to predict the input from the input. Therefore, one cannot directly use its prediction for fraud detection. Instead, the idea is to use its reconstruction error, i.e. the mean squared error (MSE) between the input and the output, as an indicator for fraud likelihood. The higher the error, the higher the risk score. Therefore, the reconstruction error can be considered as a predicted fraud risk score, and its relevance can be directly measured with any threshold-free metric.
For that purpose, let us define a function per_sample_mse
that will compute the MSE of a model
for each sample provided by a generator
:
def per_sample_mse(model, generator):
model.eval()
criterion = torch.nn.MSELoss(reduction="none")
batch_losses = []
for x_batch, y_batch in generator:
# Forward pass
y_pred = model(x_batch)
# Compute Loss
loss = criterion(y_pred.squeeze(), y_batch)
loss_app = list(torch.mean(loss,axis=1).detach().cpu().numpy())
batch_losses.extend(loss_app)
return batch_losses
Here is what happens when trying it on the validation samples with an untrained autoencoder. Let us use 100 neurons in the intermediate layer and 20 neurons in the representation layer:
seed_everything(SEED)
model = SimpleAutoencoder(x_train.shape[1], 100, 20).to(DEVICE)
losses = per_sample_mse(model, valid_generator)
Before training it, here are the loss values for the first five samples, and the overall average loss.
print(losses[0:5])
print(np.mean(losses))
[0.6754841, 0.7914626, 1.1697073, 0.807015, 1.258897]
0.9325166
With random weights in its layers, the untrained autoencoder is rather bad at reconstruction. It has a squared error of 0.93 on average for the standardized transaction variables.
Let us now train it and see how this evolves. Like in the previous section, the process is the following:
Prepare the generators.
Define the criterion.
Instantiate the model.
Perform several optimization loops (with an optimization technique like gradient descent with Adam) on the training data.
Stop optimization with early stopping using validation data.
All of these steps are implemented in the shared function training_loop
defined in the previous section.
seed_everything(SEED)
training_generator,valid_generator = prepare_generators(training_set, valid_set, batch_size = 64)
criterion = torch.nn.MSELoss().to(DEVICE)
model = SimpleAutoencoder(len(input_features), 100,20).to(DEVICE)
optimizer = torch.optim.Adam(model.parameters(), lr = 0.0001)
model,training_execution_time,train_losses,valid_losses = training_loop(model,
training_generator,
valid_generator,
optimizer,
criterion,
max_epochs=500,
verbose=True)
Epoch 0: train loss: 0.44459555436274745
valid loss: 0.11862650322295278
New best score: 0.11862650322295278
Epoch 1: train loss: 0.08513386782087799
valid loss: 0.043951269408148495
New best score: 0.043951269408148495
Epoch 2: train loss: 0.033392506879513964
valid loss: 0.023117523054119016
New best score: 0.023117523054119016
Epoch 3: train loss: 0.020011974392676046
valid loss: 0.014828811516690124
New best score: 0.014828811516690124
Epoch 4: train loss: 0.012006818789661101
valid loss: 0.007833917340253545
New best score: 0.007833917340253545
Epoch 5: train loss: 0.006807905782315075
valid loss: 0.0051493146614543074
New best score: 0.0051493146614543074
Epoch 6: train loss: 0.005084841840688574
valid loss: 0.003938508272181199
New best score: 0.003938508272181199
Epoch 7: train loss: 0.0038803787977678655
valid loss: 0.003023105256559704
New best score: 0.003023105256559704
Epoch 8: train loss: 0.002949208616150853
valid loss: 0.002528339391192574
New best score: 0.002528339391192574
Epoch 9: train loss: 0.0023992182049200465
valid loss: 0.002028128087057483
New best score: 0.002028128087057483
Epoch 10: train loss: 0.002048360905142583
valid loss: 0.0017903703262983654
New best score: 0.0017903703262983654
Epoch 11: train loss: 0.0017800394437421847
valid loss: 0.0015466983321306036
New best score: 0.0015466983321306036
Epoch 12: train loss: 0.0015514137394313148
valid loss: 0.0013498795192683614
New best score: 0.0013498795192683614
Epoch 13: train loss: 0.0013534181380584786
valid loss: 0.0012023842363628493
New best score: 0.0012023842363628493
Epoch 14: train loss: 0.0011936210340287815
valid loss: 0.0010217127795751038
New best score: 0.0010217127795751038
Epoch 15: train loss: 0.0010376906175015898
valid loss: 0.0009490489228145102
New best score: 0.0009490489228145102
Epoch 16: train loss: 0.0009196382938585036
valid loss: 0.0008569012993063696
New best score: 0.0008569012993063696
Epoch 17: train loss: 0.000808632700802201
valid loss: 0.0006878849415393556
New best score: 0.0006878849415393556
Epoch 18: train loss: 0.000717277750494199
valid loss: 0.0006156707037484548
New best score: 0.0006156707037484548
Epoch 19: train loss: 0.0006314616377501486
valid loss: 0.0005276680644569776
New best score: 0.0005276680644569776
Epoch 20: train loss: 0.0005619078842173135
valid loss: 0.0004729103698405371
New best score: 0.0004729103698405371
Epoch 21: train loss: 0.0005059312825463133
valid loss: 0.0004051279672771397
New best score: 0.0004051279672771397
Epoch 22: train loss: 0.00045364069106100914
valid loss: 0.0005014552684854513
1 iterations since best score.
Epoch 23: train loss: 0.00041752959810872697
valid loss: 0.00033180076396569885
New best score: 0.00033180076396569885
Epoch 24: train loss: 0.0003782341861741236
valid loss: 0.0002830096538852305
New best score: 0.0002830096538852305
Epoch 25: train loss: 0.0003408297134593822
valid loss: 0.00027195419235914546
New best score: 0.00027195419235914546
Epoch 26: train loss: 0.00031857445692523546
valid loss: 0.0002608046046877407
New best score: 0.0002608046046877407
Epoch 27: train loss: 0.0002881577817039132
valid loss: 0.00033317482079216405
1 iterations since best score.
Epoch 28: train loss: 0.00026948186539978955
valid loss: 0.000231769638331009
New best score: 0.000231769638331009
Epoch 29: train loss: 0.00024825533775938363
valid loss: 0.0002077069559342564
New best score: 0.0002077069559342564
Epoch 30: train loss: 0.0002292815091932147
valid loss: 0.00021443175747300387
1 iterations since best score.
Epoch 31: train loss: 0.00021850082260071492
valid loss: 0.00018189250032100675
New best score: 0.00018189250032100675
Epoch 32: train loss: 0.0002003885048218608
valid loss: 0.00023674504868648031
1 iterations since best score.
Epoch 33: train loss: 0.0001864828156532936
valid loss: 0.0001377424587060817
New best score: 0.0001377424587060817
Epoch 34: train loss: 0.0001759348686819397
valid loss: 0.00014076000892988513
1 iterations since best score.
Epoch 35: train loss: 0.00016855719783386223
valid loss: 0.00013386969116393633
New best score: 0.00013386969116393633
Epoch 36: train loss: 0.00015410608822975889
valid loss: 0.00015747381228402695
1 iterations since best score.
Epoch 37: train loss: 0.00014448216809085328
valid loss: 0.00013747311631313658
2 iterations since best score.
Epoch 38: train loss: 0.0001393559023808135
valid loss: 0.00011850201456089978
New best score: 0.00011850201456089978
Epoch 39: train loss: 0.00012889235032522076
valid loss: 0.00010649465980524161
New best score: 0.00010649465980524161
Epoch 40: train loss: 0.0001246698037786603
valid loss: 9.618921698735202e-05
New best score: 9.618921698735202e-05
Epoch 41: train loss: 0.00011618241758029194
valid loss: 0.00015093085798657566
1 iterations since best score.
Epoch 42: train loss: 0.00011435067549593118
valid loss: 0.00016568731693926273
2 iterations since best score.
Epoch 43: train loss: 0.00010542401560435254
valid loss: 8.921389312316699e-05
New best score: 8.921389312316699e-05
Epoch 44: train loss: 0.00010427492318011766
valid loss: 7.638492299589872e-05
New best score: 7.638492299589872e-05
Epoch 45: train loss: 9.43076783905485e-05
valid loss: 8.106221539293457e-05
1 iterations since best score.
Epoch 46: train loss: 9.408740700618431e-05
valid loss: 9.908837212114137e-05
2 iterations since best score.
Epoch 47: train loss: 8.668002232277154e-05
valid loss: 8.026456355074004e-05
3 iterations since best score.
Early stopping
losses = per_sample_mse(model, valid_generator)
print(losses[0:5])
print(np.mean(losses))
[2.0756474e-05, 2.1485403e-05, 3.70714e-05, 3.204957e-05, 4.3641372e-05]
8.030665e-05
print(x_train[0])
print(model(x_train[0].to(DEVICE)))
tensor([-0.1323, -0.6306, 2.1808, -0.3003, 0.1241, -1.6917, 0.5035, -1.6630,
-0.0482, -0.9810, -0.0816, -1.9895, -0.1231, -0.9719, -0.1436])
tensor([-0.1373, -0.6222, 2.1850, -0.3158, 0.1244, -1.6941, 0.5060, -1.6674,
-0.0407, -0.9684, -0.0796, -1.9885, -0.1316, -0.9716, -0.1380],
device='cuda:0', grad_fn=<AddBackward0>)
When trained, the autoencoder is much better at encoding/decoding a transaction. It now obtains a very low squared error (0.00008) on average for our standardized transaction variables. Moreover, the example above (with x_train[0]
) illustrates how well the reconstructed transaction is similar to the input transaction.
Now the remaining question is the following: are frauds less well reconstructed than genuine transactions such that reconstruction error can be used as an indicator of fraud risk?
To answer, one can compute the average squared error for fraudulent and genuine transactions separately.
genuine_losses = np.array(losses)[y_valid.cpu().numpy() == 0]
fraud_losses = np.array(losses)[y_valid.cpu().numpy() == 1]
print("Average fraud reconstruction error:", np.mean(fraud_losses))
print("Average genuine reconstruction error:", np.mean(genuine_losses))
Average fraud reconstruction error: 0.0018339771
Average genuine reconstruction error: 6.9023976e-05
It appears that frauds are indeed less well reconstructed than genuine transactions, which is very encouraging. Let us now compute the AUC ROC, the average precision, and card precision@100 on the validation set by considering the reconstruction error as a predicted fraud score.
predictions_df=valid_df
predictions_df['predictions']=losses
performance_assessment(predictions_df, top_k_list=[100])
AUC ROC | Average precision | Card Precision@100 | |
---|---|---|---|
0 | 0.836 | 0.18 | 0.217 |
Although less accurate than the supervised techniques covered before, this unsupervised method leads to encouraging results and is much more accurate than the random classifier.
3.5. Comparison with another unsupervised baseline: Isolation Forest¶
The autoencoder has a very high AUC ROC without making any use of the labels during training. To contrast this result and as a sanity check, it is interesting to implement and test another popular unsupervised baseline.
Isolation Forest is a state-of-the-art anomaly detection technique that relies on tree-based models. It computes, for each sample of data, an anomaly score that reflects how atypical the sample is. In order to calculate this score, the algorithm tries to isolate the sample from the rest of the dataset recursively: it chooses a random cutoff (pair feature-threshold), and evaluates if it allows the sample at hand to be isolated. If so, the algorithm stops. Otherwise, it adds another cutoff, and repeats the process until the sample is isolated from the rest. This recursive data partitioning can be represented as a decision tree and the number of cutoffs necessary to isolate a sample can be considered as the anomaly score. The lower the number of cutoffs (i.e. the easier it is to isolate the data point), the more likely the sample is to be an outlier.
This algorithm is implemented in sklearn
under the class sklearn.ensemble.IsolationForest
. Let us train it on the training data and evaluate the anomaly score of the validation data. On the latter, the anomaly score of a sample is computed from the average depth of the leaves reached by it.
from sklearn.ensemble import IsolationForest
anomalyclassifier = IsolationForest(random_state=SEED, n_estimators=10)
anomalyclassifier.fit(train_df[input_features])
IsolationForest(n_estimators=10, random_state=42)
predictions_df = valid_df
predictions_df['predictions'] = -anomalyclassifier.score_samples(valid_df[input_features])
performance_assessment(predictions_df, top_k_list=[100])
AUC ROC | Average precision | Card Precision@100 | |
---|---|---|---|
0 | 0.808 | 0.164 | 0.19 |
We can see that this state-of-the-art unsupervised baseline provides performances that are close (slightly lower) to those of the autoencoder.
3.6. Transactions representation, visualization and clustering¶
Additionally to its ability to detect anomalies, the autoencoder has other advantages, as was mentioned in the introduction. In particular, after training, one can use the encoder part alone to obtain representations of the transactions for visualization or clustering purposes. For that, let us train an autoencoder with a code dimension of 2.
seed_everything(SEED)
training_generator,valid_generator = prepare_generators(training_set, valid_set, batch_size = 64)
criterion = torch.nn.MSELoss().to(DEVICE)
small_model = SimpleAutoencoder(len(input_features), 100,2).to(DEVICE)
optimizer = torch.optim.Adam(small_model.parameters(), lr = 0.0001)
small_model,training_execution_time,train_losses,valid_losses = training_loop(small_model,
training_generator,
valid_generator,
optimizer,
criterion,
max_epochs=500,
verbose=True)
Epoch 0: train loss: 0.7684247642185674
valid loss: 0.5768663428194536
New best score: 0.5768663428194536
Epoch 1: train loss: 0.5679652139003145
valid loss: 0.493461870886589
New best score: 0.493461870886589
Epoch 2: train loss: 0.5022419279095776
valid loss: 0.473104252678449
New best score: 0.473104252678449
Epoch 3: train loss: 0.4897903477769509
valid loss: 0.4661512801393134
New best score: 0.4661512801393134
Epoch 4: train loss: 0.48440276228788937
valid loss: 0.4619032563733273
New best score: 0.4619032563733273
Epoch 5: train loss: 0.4809599176305658
valid loss: 0.45911747171579165
New best score: 0.45911747171579165
Epoch 6: train loss: 0.4785268415000653
valid loss: 0.45734123809090077
New best score: 0.45734123809090077
Epoch 7: train loss: 0.47650860255694527
valid loss: 0.45569751822883314
New best score: 0.45569751822883314
Epoch 8: train loss: 0.47459852245296164
valid loss: 0.4537866956223556
New best score: 0.4537866956223556
Epoch 9: train loss: 0.47209983668067484
valid loss: 0.45195874930079516
New best score: 0.45195874930079516
Epoch 10: train loss: 0.4688010113758977
valid loss: 0.4489863704625375
New best score: 0.4489863704625375
Epoch 11: train loss: 0.46363054532165965
valid loss: 0.4430646009621073
New best score: 0.4430646009621073
Epoch 12: train loss: 0.45564043236964064
valid loss: 0.436130166786616
New best score: 0.436130166786616
Epoch 13: train loss: 0.4461127134114789
valid loss: 0.4285837764134173
New best score: 0.4285837764134173
Epoch 14: train loss: 0.4380761761909233
valid loss: 0.42339507507496194
New best score: 0.42339507507496194
Epoch 15: train loss: 0.43373974593601994
valid loss: 0.4212275084413466
New best score: 0.4212275084413466
Epoch 16: train loss: 0.43143339252836616
valid loss: 0.41868471999963125
New best score: 0.41868471999963125
Epoch 17: train loss: 0.4295925242153226
valid loss: 0.4175699397025864
New best score: 0.4175699397025864
Epoch 18: train loss: 0.42802723729359715
valid loss: 0.41582820369897644
New best score: 0.41582820369897644
Epoch 19: train loss: 0.42654467802886525
valid loss: 0.4146303626194678
New best score: 0.4146303626194678
Epoch 20: train loss: 0.4251323835229783
valid loss: 0.41341693038171756
New best score: 0.41341693038171756
Epoch 21: train loss: 0.42361518552608746
valid loss: 0.4119065884032536
New best score: 0.4119065884032536
Epoch 22: train loss: 0.4221145006655279
valid loss: 0.41042865231388903
New best score: 0.41042865231388903
Epoch 23: train loss: 0.420692662379026
valid loss: 0.4093369360504255
New best score: 0.4093369360504255
Epoch 24: train loss: 0.41928241582833103
valid loss: 0.4075301027851678
New best score: 0.4075301027851678
Epoch 25: train loss: 0.41791854382814914
valid loss: 0.40682729972841963
New best score: 0.40682729972841963
Epoch 26: train loss: 0.4165563806179149
valid loss: 0.4052450547615687
New best score: 0.4052450547615687
Epoch 27: train loss: 0.4153376409986963
valid loss: 0.4041597635042472
New best score: 0.4041597635042472
Epoch 28: train loss: 0.4141319388983127
valid loss: 0.402859646179637
New best score: 0.402859646179637
Epoch 29: train loss: 0.41285213913561963
valid loss: 0.40170063721677646
New best score: 0.40170063721677646
Epoch 30: train loss: 0.4116742888440593
valid loss: 0.40088034272193906
New best score: 0.40088034272193906
Epoch 31: train loss: 0.41035284606266204
valid loss: 0.3995635224821789
New best score: 0.3995635224821789
Epoch 32: train loss: 0.4091937180686863
valid loss: 0.39790562842387317
New best score: 0.39790562842387317
Epoch 33: train loss: 0.40791810011886503
valid loss: 0.39734370631924093
New best score: 0.39734370631924093
Epoch 34: train loss: 0.4066621295922573
valid loss: 0.39603803266918725
New best score: 0.39603803266918725
Epoch 35: train loss: 0.4054151028394699
valid loss: 0.39471466424035245
New best score: 0.39471466424035245
Epoch 36: train loss: 0.4042683201583804
valid loss: 0.39363554948991764
New best score: 0.39363554948991764
Epoch 37: train loss: 0.40304669034070767
valid loss: 0.3928234031617316
New best score: 0.3928234031617316
Epoch 38: train loss: 0.4019878175301944
valid loss: 0.39189601801132246
New best score: 0.39189601801132246
Epoch 39: train loss: 0.4009062413047878
valid loss: 0.3904342273219687
New best score: 0.3904342273219687
Epoch 40: train loss: 0.39968154577976434
valid loss: 0.389421878518954
New best score: 0.389421878518954
Epoch 41: train loss: 0.3985906217111912
valid loss: 0.3884314530534171
New best score: 0.3884314530534171
Epoch 42: train loss: 0.3975262547884115
valid loss: 0.38768000658092605
New best score: 0.38768000658092605
Epoch 43: train loss: 0.3963781732845033
valid loss: 0.3869185962312208
New best score: 0.3869185962312208
Epoch 44: train loss: 0.3953533396837132
valid loss: 0.3854033345244621
New best score: 0.3854033345244621
Epoch 45: train loss: 0.39430287879238163
valid loss: 0.38500170057914296
New best score: 0.38500170057914296
Epoch 46: train loss: 0.393358859659153
valid loss: 0.3836113703381168
New best score: 0.3836113703381168
Epoch 47: train loss: 0.3923147459786891
valid loss: 0.3828077902233666
New best score: 0.3828077902233666
Epoch 48: train loss: 0.3912491195231505
valid loss: 0.3819570972456958
New best score: 0.3819570972456958
Epoch 49: train loss: 0.39028021909766625
valid loss: 0.3809337183100278
New best score: 0.3809337183100278
Epoch 50: train loss: 0.38943510518247265
valid loss: 0.38022821946222274
New best score: 0.38022821946222274
Epoch 51: train loss: 0.3884981773352076
valid loss: 0.3795201025373949
New best score: 0.3795201025373949
Epoch 52: train loss: 0.3876228652766284
valid loss: 0.37822110652923585
New best score: 0.37822110652923585
Epoch 53: train loss: 0.38679932696071684
valid loss: 0.37764905341010274
New best score: 0.37764905341010274
Epoch 54: train loss: 0.38596470047137477
valid loss: 0.3768933874824659
New best score: 0.3768933874824659
Epoch 55: train loss: 0.38524927426820504
valid loss: 0.3762990965543549
New best score: 0.3762990965543549
Epoch 56: train loss: 0.38443353883281034
valid loss: 0.37535335162623984
New best score: 0.37535335162623984
Epoch 57: train loss: 0.3837052441520855
valid loss: 0.3749958235220831
New best score: 0.3749958235220831
Epoch 58: train loss: 0.38290596316011183
valid loss: 0.3742534691518773
New best score: 0.3742534691518773
Epoch 59: train loss: 0.3822552053752866
valid loss: 0.37333267306695217
New best score: 0.37333267306695217
Epoch 60: train loss: 0.3815679804031981
valid loss: 0.3731067414980769
New best score: 0.3731067414980769
Epoch 61: train loss: 0.38101293354599697
valid loss: 0.372067863840223
New best score: 0.372067863840223
Epoch 62: train loss: 0.3802933061840885
valid loss: 0.3714882905365991
New best score: 0.3714882905365991
Epoch 63: train loss: 0.3796952187901017
valid loss: 0.3709707405397801
New best score: 0.3709707405397801
Epoch 64: train loss: 0.3791553959905759
valid loss: 0.3706924600516512
New best score: 0.3706924600516512
Epoch 65: train loss: 0.37854866244710883
valid loss: 0.3700746950067458
New best score: 0.3700746950067458
Epoch 66: train loss: 0.37798203368373856
valid loss: 0.3694668825369715
New best score: 0.3694668825369715
Epoch 67: train loss: 0.3774965272977749
valid loss: 0.3690996943438639
New best score: 0.3690996943438639
Epoch 68: train loss: 0.3769960572968022
valid loss: 0.3686452254734404
New best score: 0.3686452254734404
Epoch 69: train loss: 0.37646223467918927
valid loss: 0.3680194248430064
New best score: 0.3680194248430064
Epoch 70: train loss: 0.37601271881428094
valid loss: 0.36788663092206736
New best score: 0.36788663092206736
Epoch 71: train loss: 0.3755262392086919
valid loss: 0.3672549909744106
New best score: 0.3672549909744106
Epoch 72: train loss: 0.3749947243051365
valid loss: 0.3667607526473009
New best score: 0.3667607526473009
Epoch 73: train loss: 0.3745634045815149
valid loss: 0.36628531810364434
New best score: 0.36628531810364434
Epoch 74: train loss: 0.37411142332945907
valid loss: 0.36622899013464566
New best score: 0.36622899013464566
Epoch 75: train loss: 0.37372889710202045
valid loss: 0.36578597017650394
New best score: 0.36578597017650394
Epoch 76: train loss: 0.3733352996537152
valid loss: 0.36549377529347526
New best score: 0.36549377529347526
Epoch 77: train loss: 0.37285123603065884
valid loss: 0.36517465775781643
New best score: 0.36517465775781643
Epoch 78: train loss: 0.37245363994264696
valid loss: 0.3646077791524064
New best score: 0.3646077791524064
Epoch 79: train loss: 0.37203873899083295
valid loss: 0.3642017390395774
New best score: 0.3642017390395774
Epoch 80: train loss: 0.37175256421301606
valid loss: 0.3638398566044094
New best score: 0.3638398566044094
Epoch 81: train loss: 0.37134953726432063
valid loss: 0.3634167999192014
New best score: 0.3634167999192014
Epoch 82: train loss: 0.3709514387536003
valid loss: 0.36295943159223254
New best score: 0.36295943159223254
Epoch 83: train loss: 0.3705764208879343
valid loss: 0.36283708996460085
New best score: 0.36283708996460085
Epoch 84: train loss: 0.37014006422992873
valid loss: 0.36238899123473245
New best score: 0.36238899123473245
Epoch 85: train loss: 0.36990586698169237
valid loss: 0.3623995320393088
1 iterations since best score.
Epoch 86: train loss: 0.3695502413105554
valid loss: 0.3618717197674871
New best score: 0.3618717197674871
Epoch 87: train loss: 0.36921520776206174
valid loss: 0.3613973431085628
New best score: 0.3613973431085628
Epoch 88: train loss: 0.3689540654819736
valid loss: 0.36140266564048706
1 iterations since best score.
Epoch 89: train loss: 0.36862950323302707
valid loss: 0.3608671872342219
New best score: 0.3608671872342219
Epoch 90: train loss: 0.36836055966795744
valid loss: 0.3606637365179635
New best score: 0.3606637365179635
Epoch 91: train loss: 0.3680474031039907
valid loss: 0.3603602769270621
New best score: 0.3603602769270621
Epoch 92: train loss: 0.3676915493282716
valid loss: 0.3602089701454496
New best score: 0.3602089701454496
Epoch 93: train loss: 0.36756731669487963
valid loss: 0.35985519713391373
New best score: 0.35985519713391373
Epoch 94: train loss: 0.36728893448358285
valid loss: 0.3597481974161388
New best score: 0.3597481974161388
Epoch 95: train loss: 0.36700058066024377
valid loss: 0.3596342912951454
New best score: 0.3596342912951454
Epoch 96: train loss: 0.36675196672374843
valid loss: 0.3593450021222641
New best score: 0.3593450021222641
Epoch 97: train loss: 0.3664314620923358
valid loss: 0.3591256910985936
New best score: 0.3591256910985936
Epoch 98: train loss: 0.3662416543775945
valid loss: 0.3590364758922754
New best score: 0.3590364758922754
Epoch 99: train loss: 0.3659238473122252
valid loss: 0.3586511149595344
New best score: 0.3586511149595344
Epoch 100: train loss: 0.36580749696572923
valid loss: 0.35853858080392326
New best score: 0.35853858080392326
Epoch 101: train loss: 0.36555497986522734
valid loss: 0.3583731103138845
New best score: 0.3583731103138845
Epoch 102: train loss: 0.3652697747170811
valid loss: 0.35791740759474333
New best score: 0.35791740759474333
Epoch 103: train loss: 0.36510542584992733
valid loss: 0.357564813598909
New best score: 0.357564813598909
Epoch 104: train loss: 0.36488343900635867
valid loss: 0.3574086686952518
New best score: 0.3574086686952518
Epoch 105: train loss: 0.3645800205934116
valid loss: 0.3588905628587379
1 iterations since best score.
Epoch 106: train loss: 0.3644165340561712
valid loss: 0.35713441704140336
New best score: 0.35713441704140336
Epoch 107: train loss: 0.3641442490125477
valid loss: 0.3570101023371754
New best score: 0.3570101023371754
Epoch 108: train loss: 0.36390998686020964
valid loss: 0.3570574991038588
1 iterations since best score.
Epoch 109: train loss: 0.36367379285637325
valid loss: 0.3565490502640198
New best score: 0.3565490502640198
Epoch 110: train loss: 0.3634785708346285
valid loss: 0.3563823215948428
New best score: 0.3563823215948428
Epoch 111: train loss: 0.36325681463140375
valid loss: 0.3560527459031246
New best score: 0.3560527459031246
Epoch 112: train loss: 0.36312379888891033
valid loss: 0.35614496834291137
1 iterations since best score.
Epoch 113: train loss: 0.3629203543893238
valid loss: 0.35565979868336456
New best score: 0.35565979868336456
Epoch 114: train loss: 0.3627093475076938
valid loss: 0.3556141742917358
New best score: 0.3556141742917358
Epoch 115: train loss: 0.36245119580233304
valid loss: 0.3555297355834252
New best score: 0.3555297355834252
Epoch 116: train loss: 0.36233158876859434
valid loss: 0.35528882447161964
New best score: 0.35528882447161964
Epoch 117: train loss: 0.3620295404362177
valid loss: 0.3550981194432316
New best score: 0.3550981194432316
Epoch 118: train loss: 0.3619585951815144
valid loss: 0.3548639331196175
New best score: 0.3548639331196175
Epoch 119: train loss: 0.3617405699954206
valid loss: 0.3550969279528967
1 iterations since best score.
Epoch 120: train loss: 0.3615904108516117
valid loss: 0.3545777801444622
New best score: 0.3545777801444622
Epoch 121: train loss: 0.3614249893287856
valid loss: 0.35460314793013487
1 iterations since best score.
Epoch 122: train loss: 0.361224607915313
valid loss: 0.35458590122845657
2 iterations since best score.
Epoch 123: train loss: 0.36105165147644613
valid loss: 0.3542227226528314
New best score: 0.3542227226528314
Epoch 124: train loss: 0.36089479367660066
valid loss: 0.3539815934466534
New best score: 0.3539815934466534
Epoch 125: train loss: 0.3607355942860616
valid loss: 0.35401103999771055
1 iterations since best score.
Epoch 126: train loss: 0.360593699246474
valid loss: 0.35378003074823183
New best score: 0.35378003074823183
Epoch 127: train loss: 0.3604586847824531
valid loss: 0.35374672082278247
New best score: 0.35374672082278247
Epoch 128: train loss: 0.36026581016370945
valid loss: 0.35388875315423873
1 iterations since best score.
Epoch 129: train loss: 0.36007167088370023
valid loss: 0.35342369677264834
New best score: 0.35342369677264834
Epoch 130: train loss: 0.35987311892254176
valid loss: 0.3533464732717295
New best score: 0.3533464732717295
Epoch 131: train loss: 0.3598369659132985
valid loss: 0.3530454443614991
New best score: 0.3530454443614991
Epoch 132: train loss: 0.3596282069416393
valid loss: 0.35290176399418566
New best score: 0.35290176399418566
Epoch 133: train loss: 0.35945622198440375
valid loss: 0.3533968297184491
1 iterations since best score.
Epoch 134: train loss: 0.35930171876746886
valid loss: 0.352829405486258
New best score: 0.352829405486258
Epoch 135: train loss: 0.3591677751071594
valid loss: 0.3526372389715226
New best score: 0.3526372389715226
Epoch 136: train loss: 0.359064665425348
valid loss: 0.3525837041641194
New best score: 0.3525837041641194
Epoch 137: train loss: 0.35886832957741865
valid loss: 0.3522841656305751
New best score: 0.3522841656305751
Epoch 138: train loss: 0.35870904294528194
valid loss: 0.35225988093946803
New best score: 0.35225988093946803
Epoch 139: train loss: 0.3585920189979199
valid loss: 0.35207713433627874
New best score: 0.35207713433627874
Epoch 140: train loss: 0.35844638561883346
valid loss: 0.3520351732852029
New best score: 0.3520351732852029
Epoch 141: train loss: 0.3581896108320521
valid loss: 0.3525604487116871
1 iterations since best score.
Epoch 142: train loss: 0.3581589228735371
valid loss: 0.35182255013067215
New best score: 0.35182255013067215
Epoch 143: train loss: 0.3579721470180023
valid loss: 0.3518180005211648
New best score: 0.3518180005211648
Epoch 144: train loss: 0.3577889849932198
valid loss: 0.35163149548684314
New best score: 0.35163149548684314
Epoch 145: train loss: 0.3576514876196535
valid loss: 0.3514987884649162
New best score: 0.3514987884649162
Epoch 146: train loss: 0.3574727186167445
valid loss: 0.35132194807946354
New best score: 0.35132194807946354
Epoch 147: train loss: 0.35737212919710243
valid loss: 0.3518874969150199
1 iterations since best score.
Epoch 148: train loss: 0.3571610676683842
valid loss: 0.3510494019815831
New best score: 0.3510494019815831
Epoch 149: train loss: 0.35705440558393414
valid loss: 0.35092813877460083
New best score: 0.35092813877460083
Epoch 150: train loss: 0.3569017581171561
valid loss: 0.35103738451264593
1 iterations since best score.
Epoch 151: train loss: 0.35687340736161227
valid loss: 0.35078356940238203
New best score: 0.35078356940238203
Epoch 152: train loss: 0.3566489354431287
valid loss: 0.351060890369728
1 iterations since best score.
Epoch 153: train loss: 0.35646914444279715
valid loss: 0.35075386410853904
New best score: 0.35075386410853904
Epoch 154: train loss: 0.35635819458482826
valid loss: 0.35059281462202957
New best score: 0.35059281462202957
Epoch 155: train loss: 0.3562699848105757
valid loss: 0.3503138803556317
New best score: 0.3503138803556317
Epoch 156: train loss: 0.35617095665083787
valid loss: 0.3504037611797208
1 iterations since best score.
Epoch 157: train loss: 0.3560048634075982
valid loss: 0.3503424207532341
2 iterations since best score.
Epoch 158: train loss: 0.3559037697132869
valid loss: 0.35010807908297886
New best score: 0.35010807908297886
Epoch 159: train loss: 0.3558188352598515
valid loss: 0.3501646858584034
1 iterations since best score.
Epoch 160: train loss: 0.35570161739795203
valid loss: 0.34974523794129897
New best score: 0.34974523794129897
Epoch 161: train loss: 0.35554915552613386
valid loss: 0.34962316972961843
New best score: 0.34962316972961843
Epoch 162: train loss: 0.3553889135671619
valid loss: 0.349606013542316
New best score: 0.349606013542316
Epoch 163: train loss: 0.3552543936906308
valid loss: 0.3494946970314276
New best score: 0.3494946970314276
Epoch 164: train loss: 0.3551593191537073
valid loss: 0.34953100366996287
1 iterations since best score.
Epoch 165: train loss: 0.3550806263208845
valid loss: 0.34943127057265716
New best score: 0.34943127057265716
Epoch 166: train loss: 0.35500883899386937
valid loss: 0.3493845041983766
New best score: 0.3493845041983766
Epoch 167: train loss: 0.3548054924202695
valid loss: 0.3494515491802184
1 iterations since best score.
Epoch 168: train loss: 0.3548040342860878
valid loss: 0.3489428101830144
New best score: 0.3489428101830144
Epoch 169: train loss: 0.35459009003798775
valid loss: 0.34909528777247567
1 iterations since best score.
Epoch 170: train loss: 0.3545222410052502
valid loss: 0.3487735234974512
New best score: 0.3487735234974512
Epoch 171: train loss: 0.35444276211480563
valid loss: 0.3489290329113684
1 iterations since best score.
Epoch 172: train loss: 0.35433073894585526
valid loss: 0.34865225776622855
New best score: 0.34865225776622855
Epoch 173: train loss: 0.354227108492678
valid loss: 0.3486745737940887
1 iterations since best score.
Epoch 174: train loss: 0.3540804327446006
valid loss: 0.34847301876610093
New best score: 0.34847301876610093
Epoch 175: train loss: 0.353996581773685
valid loss: 0.34889905400614923
1 iterations since best score.
Epoch 176: train loss: 0.3539140959257833
valid loss: 0.3482982144166863
New best score: 0.3482982144166863
Epoch 177: train loss: 0.3537893352841328
valid loss: 0.34839311771379794
1 iterations since best score.
Epoch 178: train loss: 0.3536900930092394
valid loss: 0.3482122821560323
New best score: 0.3482122821560323
Epoch 179: train loss: 0.35362554246107436
valid loss: 0.34852553967569694
1 iterations since best score.
Epoch 180: train loss: 0.35350341792886164
valid loss: 0.34817046757604253
New best score: 0.34817046757604253
Epoch 181: train loss: 0.3533807491196273
valid loss: 0.3478579554075752
New best score: 0.3478579554075752
Epoch 182: train loss: 0.3533511972963012
valid loss: 0.3475855511068646
New best score: 0.3475855511068646
Epoch 183: train loss: 0.3531828399598029
valid loss: 0.3475934645517276
1 iterations since best score.
Epoch 184: train loss: 0.3531163542266102
valid loss: 0.34748886225979186
New best score: 0.34748886225979186
Epoch 185: train loss: 0.353066695209443
valid loss: 0.3475219822320782
1 iterations since best score.
Epoch 186: train loss: 0.3529354565800034
valid loss: 0.34738321175666453
New best score: 0.34738321175666453
Epoch 187: train loss: 0.3527984747353293
valid loss: 0.3474910751392281
1 iterations since best score.
Epoch 188: train loss: 0.35269127361633124
valid loss: 0.3472975330437467
New best score: 0.3472975330437467
Epoch 189: train loss: 0.35258841543079106
valid loss: 0.34747938613096874
1 iterations since best score.
Epoch 190: train loss: 0.35257175886722186
valid loss: 0.34768702619062747
2 iterations since best score.
Epoch 191: train loss: 0.35244498092745274
valid loss: 0.3471462391113323
New best score: 0.3471462391113323
Epoch 192: train loss: 0.35240889008710763
valid loss: 0.34711034876075597
New best score: 0.34711034876075597
Epoch 193: train loss: 0.35235085171677644
valid loss: 0.3468500987916696
New best score: 0.3468500987916696
Epoch 194: train loss: 0.3522142497804835
valid loss: 0.34691527915782616
1 iterations since best score.
Epoch 195: train loss: 0.3521673695876995
valid loss: 0.3467520169710201
New best score: 0.3467520169710201
Epoch 196: train loss: 0.3520123922688099
valid loss: 0.3468928875167513
1 iterations since best score.
Epoch 197: train loss: 0.3519577576883892
valid loss: 0.3469653481017045
2 iterations since best score.
Epoch 198: train loss: 0.3519127744447661
valid loss: 0.346654592495147
New best score: 0.346654592495147
Epoch 199: train loss: 0.3518416799790089
valid loss: 0.3466361478052504
New best score: 0.3466361478052504
Epoch 200: train loss: 0.35172814831679
valid loss: 0.34668891447815087
1 iterations since best score.
Epoch 201: train loss: 0.3516605096667948
valid loss: 0.3464359102516226
New best score: 0.3464359102516226
Epoch 202: train loss: 0.35161338928894603
valid loss: 0.34623810518634773
New best score: 0.34623810518634773
Epoch 203: train loss: 0.35146604811712157
valid loss: 0.34637316817142927
1 iterations since best score.
Epoch 204: train loss: 0.3514169772826919
valid loss: 0.34636639934745644
2 iterations since best score.
Epoch 205: train loss: 0.3513855440096919
valid loss: 0.34600382748523045
New best score: 0.34600382748523045
Epoch 206: train loss: 0.35120018587285656
valid loss: 0.3462549442476262
1 iterations since best score.
Epoch 207: train loss: 0.35112138801961285
valid loss: 0.3459852658008617
New best score: 0.3459852658008617
Epoch 208: train loss: 0.35110988725781667
valid loss: 0.3461580883609793
1 iterations since best score.
Epoch 209: train loss: 0.3510144509617275
valid loss: 0.34613404322843083
2 iterations since best score.
Epoch 210: train loss: 0.3509096265352023
valid loss: 0.34592601948422813
New best score: 0.34592601948422813
Epoch 211: train loss: 0.35084550259332126
valid loss: 0.34570493792575563
New best score: 0.34570493792575563
Epoch 212: train loss: 0.3507964052046234
valid loss: 0.3456118444275986
New best score: 0.3456118444275986
Epoch 213: train loss: 0.3506889792860808
valid loss: 0.3456960986220772
1 iterations since best score.
Epoch 214: train loss: 0.3506408885947605
valid loss: 0.3456522176825935
2 iterations since best score.
Epoch 215: train loss: 0.3505368314498241
valid loss: 0.34537068623336936
New best score: 0.34537068623336936
Epoch 216: train loss: 0.350509597882486
valid loss: 0.3454063627563539
1 iterations since best score.
Epoch 217: train loss: 0.35038468848564885
valid loss: 0.3460760150939389
2 iterations since best score.
Epoch 218: train loss: 0.3503669098333009
valid loss: 0.34533744100990194
New best score: 0.34533744100990194
Epoch 219: train loss: 0.3502007991531383
valid loss: 0.3452964130320836
New best score: 0.3452964130320836
Epoch 220: train loss: 0.35017206960038977
valid loss: 0.3452372838402055
New best score: 0.3452372838402055
Epoch 221: train loss: 0.3501182178010448
valid loss: 0.34491898435386803
New best score: 0.34491898435386803
Epoch 222: train loss: 0.35007528399418236
valid loss: 0.34484236478154123
New best score: 0.34484236478154123
Epoch 223: train loss: 0.3499769602633343
valid loss: 0.3452564421735826
1 iterations since best score.
Epoch 224: train loss: 0.34994534848980874
valid loss: 0.3448767109646823
2 iterations since best score.
Epoch 225: train loss: 0.3498022082772811
valid loss: 0.344659458516074
New best score: 0.344659458516074
Epoch 226: train loss: 0.3497091270484614
valid loss: 0.344617711602013
New best score: 0.344617711602013
Epoch 227: train loss: 0.3496640003206169
valid loss: 0.3447480450888149
1 iterations since best score.
Epoch 228: train loss: 0.34956770907396567
valid loss: 0.34473640628851177
2 iterations since best score.
Epoch 229: train loss: 0.3495605050035918
valid loss: 0.34430122165406335
New best score: 0.34430122165406335
Epoch 230: train loss: 0.34945341993130413
valid loss: 0.344738097887873
1 iterations since best score.
Epoch 231: train loss: 0.34943006878601207
valid loss: 0.3444186775085053
2 iterations since best score.
Epoch 232: train loss: 0.34933042286914806
valid loss: 0.3448007179902551
3 iterations since best score.
Early stopping
Once trained, to obtain the transactions 2D-representation from the encoder part alone, the idea is to simply apply the first two layers of the Autoencoder.
def compute_representation(x,model):
x_representation = model.fc1(x)
x_representation = model.relu(x_representation)
x_representation = model.fc2(x_representation)
x_representation = model.relu(x_representation)
return x_representation
x_train_representation = []
for x_batch, y_batch in training_generator:
x_train_representation.append(compute_representation(x_batch, small_model).detach().cpu().numpy())
x_train_representation = np.vstack(x_train_representation)
After this process, the obtained representations of the training data are in 2D:
print(x_train.shape)
print(x_train_representation.shape)
torch.Size([66928, 15])
(66928, 2)
Transactions can be now visualized on a plane (e.g. with different colors for frauds and genuine)
plt.scatter(x_train_representation[:, 0], x_train_representation[:, 1], c=y_train.numpy(), s=50, cmap='viridis')
<matplotlib.collections.PathCollection at 0x7f9f243358e0>
It is also possible to apply a K-means clustering and vizualize the clusters:
from sklearn.cluster import KMeans
kmeans = KMeans(n_clusters=10, random_state=SEED)
kmeans.fit(x_train_representation)
y_kmeans = kmeans.predict(x_train_representation)
plt.scatter(x_train_representation[:, 0], x_train_representation[:, 1], c=y_kmeans, s=50, cmap='viridis')
centers = kmeans.cluster_centers_
plt.scatter(centers[:, 0], centers[:, 1], c='black', s=200, alpha=0.5)
<matplotlib.collections.PathCollection at 0x7f9f25e1c460>
3.7. Semi-supervised fraud detection¶
Finally, the autoencoder can be used in a semi-supervised credit card fraud detection system [CLBC+19]. There are two main ways to do this:
W1
: The most natural one is to keep the autoencoder as is and train it on all available labeled and unlabeled data. Then, to combine it with a supervised neural network trained only on labeled data. The combination can be done by aggregating the predicted score from the supervised model and the predicted score from the unsupervised model (W1A
), or more elegantly by providing the unsupervised risk score from the autoencoder (reconstruction error) as an additional variable to the supervised model (W1B
) as in [AHJ+20].W2
: Another possibility is to change the architecture of the autoencoder into a hybrid semi-supervised model. More precisely, one can add, to the autoencoder, output neurons similar to those of the supervised neural network from the previous section, and additionally predict them from the code neurons. Therefore, the learned representation (code neurons) will be shared between the decoder network that aims at reconstructing the input and the prediction network that aims at predicting fraud. The first is trained on all samples and the latter is only trained on labeled samples. The intuition with this approach is similar to pre-training in natural language processing: learning a representation that embeds the underlying structure in the input data can help with solving supervised tasks.
The following explores the W1B
semi-supervised approach. But first, let us reevaluate here the baseline supervised model without the reconstruction error feature. FraudDataset
and SimpleFraudMLPWithDropout
are available in the shared functions and can be directly used here.
seed_everything(SEED)
training_set_supervised = FraudDataset(x_train.to(DEVICE), y_train.to(DEVICE))
valid_set_supervised = FraudDataset(x_valid.to(DEVICE), y_valid.to(DEVICE))
training_generator_supervised,valid_generator_supervised = prepare_generators(training_set_supervised,
valid_set_supervised,
batch_size=64)
model_supervised = SimpleFraudMLPWithDropout(len(input_features), 1000, 0.2).to(DEVICE)
optimizer = torch.optim.Adam(model_supervised.parameters(), lr = 0.0001)
criterion = torch.nn.BCELoss().to(DEVICE)
model_supervised,training_execution_time,train_losses_dropout,valid_losses_dropout =\
training_loop(model_supervised,
training_generator_supervised,
valid_generator_supervised,
optimizer,
criterion,
verbose=True)
Epoch 0: train loss: 0.10161001316804161
valid loss: 0.03556653917487202
New best score: 0.03556653917487202
Epoch 1: train loss: 0.03833597035852419
valid loss: 0.026109349807022047
New best score: 0.026109349807022047
Epoch 2: train loss: 0.031094471842315882
valid loss: 0.02396169698420566
New best score: 0.02396169698420566
Epoch 3: train loss: 0.028757975434966342
valid loss: 0.023352081602938026
New best score: 0.023352081602938026
Epoch 4: train loss: 0.02775647486163834
valid loss: 0.022305768956761052
New best score: 0.022305768956761052
Epoch 5: train loss: 0.026740792337858456
valid loss: 0.021572637600223713
New best score: 0.021572637600223713
Epoch 6: train loss: 0.02606928332959859
valid loss: 0.021479292579320935
New best score: 0.021479292579320935
Epoch 7: train loss: 0.025758702903594215
valid loss: 0.02105785213192019
New best score: 0.02105785213192019
Epoch 8: train loss: 0.02527676529854707
valid loss: 0.020694473186187202
New best score: 0.020694473186187202
Epoch 9: train loss: 0.024878843686695656
valid loss: 0.02042211337890374
New best score: 0.02042211337890374
Epoch 10: train loss: 0.02438850605296751
valid loss: 0.020538174796116644
1 iterations since best score.
Epoch 11: train loss: 0.024034218137004692
valid loss: 0.02071835831255535
2 iterations since best score.
Epoch 12: train loss: 0.023670860127248512
valid loss: 0.01998928964819983
New best score: 0.01998928964819983
Epoch 13: train loss: 0.023701557650517523
valid loss: 0.01986558492743293
New best score: 0.01986558492743293
Epoch 14: train loss: 0.02331948666341414
valid loss: 0.01986974315244521
1 iterations since best score.
Epoch 15: train loss: 0.023050426844735388
valid loss: 0.01970138840245012
New best score: 0.01970138840245012
Epoch 16: train loss: 0.022976456373844503
valid loss: 0.01949228905777503
New best score: 0.01949228905777503
Epoch 17: train loss: 0.022786046317435738
valid loss: 0.01982322550668824
1 iterations since best score.
Epoch 18: train loss: 0.022582473735731044
valid loss: 0.01974369095577324
2 iterations since best score.
Epoch 19: train loss: 0.02253310805256523
valid loss: 0.019359889266824176
New best score: 0.019359889266824176
Epoch 20: train loss: 0.022279470434334897
valid loss: 0.01928481163261102
New best score: 0.01928481163261102
Epoch 21: train loss: 0.02220114628115694
valid loss: 0.018997078554964335
New best score: 0.018997078554964335
Epoch 22: train loss: 0.022127013866091592
valid loss: 0.01905235069170289
1 iterations since best score.
Epoch 23: train loss: 0.022129482317989224
valid loss: 0.018972578141940095
New best score: 0.018972578141940095
Epoch 24: train loss: 0.02179307807468405
valid loss: 0.01934589187568817
1 iterations since best score.
Epoch 25: train loss: 0.021895541540831398
valid loss: 0.018781205812657426
New best score: 0.018781205812657426
Epoch 26: train loss: 0.02153568957303695
valid loss: 0.018873276155635388
1 iterations since best score.
Epoch 27: train loss: 0.021719574671969652
valid loss: 0.01871486084059878
New best score: 0.01871486084059878
Epoch 28: train loss: 0.021623164488258195
valid loss: 0.01849896589324611
New best score: 0.01849896589324611
Epoch 29: train loss: 0.021195488194819277
valid loss: 0.018997153041558596
1 iterations since best score.
Epoch 30: train loss: 0.021170051902644763
valid loss: 0.01968524302457729
2 iterations since best score.
Epoch 31: train loss: 0.02115491109416708
valid loss: 0.01879670942823092
3 iterations since best score.
Early stopping
predictions = []
for x_batch, y_batch in valid_generator_supervised:
predictions.append(model_supervised(x_batch.to(DEVICE)).detach().cpu().numpy())
predictions_df=valid_df
predictions_df['predictions']=np.vstack(predictions)
performance_assessment(predictions_df, top_k_list=[100])
AUC ROC | Average precision | Card Precision@100 | |
---|---|---|---|
0 | 0.859 | 0.646 | 0.28 |
Now, for the W1B
semi-supervised approach, let us compute the reconstruction error of all transactions with our first autoencoder (stored in model
) and add it as a new variable in train_df
and valid_df
.
loader_params = {'batch_size': 64,
'num_workers': 0}
training_generator = torch.utils.data.DataLoader(training_set, **loader_params)
valid_generator = torch.utils.data.DataLoader(valid_set, **loader_params)
train_reconstruction = per_sample_mse(model, training_generator)
valid_reconstruction = per_sample_mse(model, valid_generator)
train_df["reconstruction_error"] = train_reconstruction
valid_df["reconstruction_error"] = valid_reconstruction
Then, we can reevaluate the supervised model with this extra variable.
seed_everything(SEED)
input_features_new = input_features + ["reconstruction_error"]
# Rescale the reconstruction error
(train_df, valid_df)=scaleData(train_df, valid_df, ["reconstruction_error"])
x_train_new = torch.FloatTensor(train_df[input_features_new].values)
x_valid_new = torch.FloatTensor(valid_df[input_features_new].values)
training_set_supervised_new = FraudDataset(x_train_new.to(DEVICE), y_train.to(DEVICE))
valid_set_supervised_new = FraudDataset(x_valid_new.to(DEVICE), y_valid.to(DEVICE))
training_generator_supervised,valid_generator_supervised = prepare_generators(training_set_supervised_new,
valid_set_supervised_new,
batch_size=64)
model_supervised = SimpleFraudMLPWithDropout(len(input_features_new), 100, 0.2).to(DEVICE)
optimizer = torch.optim.Adam(model_supervised.parameters(), lr = 0.0001)
criterion = torch.nn.BCELoss().to(DEVICE)
model_supervised,training_execution_time,train_losses_dropout,valid_losses_dropout = \
training_loop(model_supervised,
training_generator_supervised,
valid_generator_supervised,
optimizer,
criterion,
verbose=True)
predictions = []
for x_batch, y_batch in valid_generator_supervised:
predictions.append(model_supervised(x_batch).detach().cpu().numpy())
Epoch 0: train loss: 0.32470558333009425
valid loss: 0.11669875736770734
New best score: 0.11669875736770734
Epoch 1: train loss: 0.0857971908175609
valid loss: 0.050325598816076914
New best score: 0.050325598816076914
Epoch 2: train loss: 0.055031552282206526
valid loss: 0.03683824896140665
New best score: 0.03683824896140665
Epoch 3: train loss: 0.045474566717366785
valid loss: 0.031574366707863705
New best score: 0.031574366707863705
Epoch 4: train loss: 0.03988730701053405
valid loss: 0.028506962655753386
New best score: 0.028506962655753386
Epoch 5: train loss: 0.03588366161686427
valid loss: 0.0264507318495727
New best score: 0.0264507318495727
Epoch 6: train loss: 0.03320539464736019
valid loss: 0.025167095382784395
New best score: 0.025167095382784395
Epoch 7: train loss: 0.03146339780281531
valid loss: 0.024275742485722313
New best score: 0.024275742485722313
Epoch 8: train loss: 0.03083236553407066
valid loss: 0.02372574291110568
New best score: 0.02372574291110568
Epoch 9: train loss: 0.029903344949351242
valid loss: 0.023402261280561568
New best score: 0.023402261280561568
Epoch 10: train loss: 0.029085144788573495
valid loss: 0.02301961904330576
New best score: 0.02301961904330576
Epoch 11: train loss: 0.02864163257867226
valid loss: 0.022839933189008732
New best score: 0.022839933189008732
Epoch 12: train loss: 0.028412688680235332
valid loss: 0.022551339802133745
New best score: 0.022551339802133745
Epoch 13: train loss: 0.02834755417122461
valid loss: 0.022378979152821697
New best score: 0.022378979152821697
Epoch 14: train loss: 0.028343526197305343
valid loss: 0.022222696035805213
New best score: 0.022222696035805213
Epoch 15: train loss: 0.027864306476447966
valid loss: 0.022193545799402144
New best score: 0.022193545799402144
Epoch 16: train loss: 0.027414113502855098
valid loss: 0.0219534132029108
New best score: 0.0219534132029108
Epoch 17: train loss: 0.027424028700433072
valid loss: 0.02181996635156251
New best score: 0.02181996635156251
Epoch 18: train loss: 0.02662210316247223
valid loss: 0.021762580478747115
New best score: 0.021762580478747115
Epoch 19: train loss: 0.026635378260048814
valid loss: 0.02163417828167519
New best score: 0.02163417828167519
Epoch 20: train loss: 0.026724288395666526
valid loss: 0.02153022467486988
New best score: 0.02153022467486988
Epoch 21: train loss: 0.0266051148182408
valid loss: 0.021428093735047213
New best score: 0.021428093735047213
Epoch 22: train loss: 0.02637773595953335
valid loss: 0.021339995185513803
New best score: 0.021339995185513803
Epoch 23: train loss: 0.026257544278254445
valid loss: 0.021252412970885228
New best score: 0.021252412970885228
Epoch 24: train loss: 0.026126052476430087
valid loss: 0.021180965348218103
New best score: 0.021180965348218103
Epoch 25: train loss: 0.02579458606234408
valid loss: 0.021089692087589554
New best score: 0.021089692087589554
Epoch 26: train loss: 0.025870972849209625
valid loss: 0.02096897741255498
New best score: 0.02096897741255498
Epoch 27: train loss: 0.02589873712250329
valid loss: 0.02103838359115167
1 iterations since best score.
Epoch 28: train loss: 0.025474642118506745
valid loss: 0.02083245238971189
New best score: 0.02083245238971189
Epoch 29: train loss: 0.025491673881757868
valid loss: 0.020914757669053444
1 iterations since best score.
Epoch 30: train loss: 0.0256901814166704
valid loss: 0.02079701650928441
New best score: 0.02079701650928441
Epoch 31: train loss: 0.025314893825050894
valid loss: 0.02063066685404323
New best score: 0.02063066685404323
Epoch 32: train loss: 0.025559447194898346
valid loss: 0.02062068326803321
New best score: 0.02062068326803321
Epoch 33: train loss: 0.024862300820050344
valid loss: 0.020600306137866987
New best score: 0.020600306137866987
Epoch 34: train loss: 0.025080274871548718
valid loss: 0.020477301164280846
New best score: 0.020477301164280846
Epoch 35: train loss: 0.024728723903396744
valid loss: 0.020394465477394114
New best score: 0.020394465477394114
Epoch 36: train loss: 0.02477897367222915
valid loss: 0.020449772248645134
1 iterations since best score.
Epoch 37: train loss: 0.024465490879030653
valid loss: 0.020325674156434426
New best score: 0.020325674156434426
Epoch 38: train loss: 0.024305174552148163
valid loss: 0.020269202150511326
New best score: 0.020269202150511326
Epoch 39: train loss: 0.024521953303208416
valid loss: 0.020168634172646034
New best score: 0.020168634172646034
Epoch 40: train loss: 0.024144744264793513
valid loss: 0.020071419583928714
New best score: 0.020071419583928714
Epoch 41: train loss: 0.024092775663578203
valid loss: 0.020062985963854797
New best score: 0.020062985963854797
Epoch 42: train loss: 0.02412362778172018
valid loss: 0.020077789380454302
1 iterations since best score.
Epoch 43: train loss: 0.023811501132976087
valid loss: 0.01995381725687391
New best score: 0.01995381725687391
Epoch 44: train loss: 0.02424977031820427
valid loss: 0.019959632811630195
1 iterations since best score.
Epoch 45: train loss: 0.02389407654877852
valid loss: 0.0198583653335373
New best score: 0.0198583653335373
Epoch 46: train loss: 0.023958714262081085
valid loss: 0.01977933693201347
New best score: 0.01977933693201347
Epoch 47: train loss: 0.023485491898673876
valid loss: 0.019806338451762016
1 iterations since best score.
Epoch 48: train loss: 0.023657646779875568
valid loss: 0.019662209351603045
New best score: 0.019662209351603045
Epoch 49: train loss: 0.023470603387796607
valid loss: 0.01968898415804683
1 iterations since best score.
Epoch 50: train loss: 0.02365986756685708
valid loss: 0.019617491648048976
New best score: 0.019617491648048976
Epoch 51: train loss: 0.02301738877784297
valid loss: 0.01950973152880216
New best score: 0.01950973152880216
Epoch 52: train loss: 0.023529816942643837
valid loss: 0.019513023327883395
1 iterations since best score.
Epoch 53: train loss: 0.023395457049479162
valid loss: 0.01949332637045675
New best score: 0.01949332637045675
Epoch 54: train loss: 0.02303883458951125
valid loss: 0.01939637301988452
New best score: 0.01939637301988452
Epoch 55: train loss: 0.022967305407017097
valid loss: 0.01937846771538095
New best score: 0.01937846771538095
Epoch 56: train loss: 0.023021493925660472
valid loss: 0.01932469207037729
New best score: 0.01932469207037729
Epoch 57: train loss: 0.02324649085298298
valid loss: 0.019319333965365924
New best score: 0.019319333965365924
Epoch 58: train loss: 0.02314090387805331
valid loss: 0.01927059471538504
New best score: 0.01927059471538504
Epoch 59: train loss: 0.022600235849646513
valid loss: 0.019282025675024694
1 iterations since best score.
Epoch 60: train loss: 0.022965760435352554
valid loss: 0.019262573485965002
New best score: 0.019262573485965002
Epoch 61: train loss: 0.023083728506856634
valid loss: 0.019118402978720885
New best score: 0.019118402978720885
Epoch 62: train loss: 0.022604351701886245
valid loss: 0.019093919451643657
New best score: 0.019093919451643657
Epoch 63: train loss: 0.02272556185217959
valid loss: 0.01905925034906695
New best score: 0.01905925034906695
Epoch 64: train loss: 0.0222361646619328
valid loss: 0.01899794363760252
New best score: 0.01899794363760252
Epoch 65: train loss: 0.022313779764221074
valid loss: 0.018973210823080944
New best score: 0.018973210823080944
Epoch 66: train loss: 0.022522902168711046
valid loss: 0.018901191251937687
New best score: 0.018901191251937687
Epoch 67: train loss: 0.02248352280253434
valid loss: 0.01889178021709165
New best score: 0.01889178021709165
Epoch 68: train loss: 0.022433675548692106
valid loss: 0.018912809526501987
1 iterations since best score.
Epoch 69: train loss: 0.02257502525739895
valid loss: 0.018815524044034422
New best score: 0.018815524044034422
Epoch 70: train loss: 0.0224518446423368
valid loss: 0.018792384044302862
New best score: 0.018792384044302862
Epoch 71: train loss: 0.02226468713430556
valid loss: 0.018732086603804567
New best score: 0.018732086603804567
Epoch 72: train loss: 0.022387760058183395
valid loss: 0.01883075698622365
1 iterations since best score.
Epoch 73: train loss: 0.022133301173819384
valid loss: 0.01867632728383006
New best score: 0.01867632728383006
Epoch 74: train loss: 0.022178997785715745
valid loss: 0.018700476872729636
1 iterations since best score.
Epoch 75: train loss: 0.022028937389144593
valid loss: 0.018650301263280863
New best score: 0.018650301263280863
Epoch 76: train loss: 0.02214035027425373
valid loss: 0.018606594060496758
New best score: 0.018606594060496758
Epoch 77: train loss: 0.021738268898102858
valid loss: 0.018632269502983842
1 iterations since best score.
Epoch 78: train loss: 0.02227861989753848
valid loss: 0.01854319557435607
New best score: 0.01854319557435607
Epoch 79: train loss: 0.021809139837872374
valid loss: 0.018610031961443035
1 iterations since best score.
Epoch 80: train loss: 0.022240891229284725
valid loss: 0.018547307454874037
2 iterations since best score.
Epoch 81: train loss: 0.022066849491217958
valid loss: 0.01848699482949196
New best score: 0.01848699482949196
Epoch 82: train loss: 0.021692594942564668
valid loss: 0.018455498772571525
New best score: 0.018455498772571525
Epoch 83: train loss: 0.02205857191164488
valid loss: 0.01839941353727987
New best score: 0.01839941353727987
Epoch 84: train loss: 0.021860865075639797
valid loss: 0.0183856724618929
New best score: 0.0183856724618929
Epoch 85: train loss: 0.021753365356460868
valid loss: 0.018383686461923593
New best score: 0.018383686461923593
Epoch 86: train loss: 0.021993174024765874
valid loss: 0.018376381002758412
New best score: 0.018376381002758412
Epoch 87: train loss: 0.021596283111213768
valid loss: 0.018388316551951153
1 iterations since best score.
Epoch 88: train loss: 0.021463176303113105
valid loss: 0.018383121337420993
2 iterations since best score.
Epoch 89: train loss: 0.02156794395123699
valid loss: 0.0182802003695085
New best score: 0.0182802003695085
Epoch 90: train loss: 0.021446433153811905
valid loss: 0.018424906315602608
1 iterations since best score.
Epoch 91: train loss: 0.02153544440147882
valid loss: 0.01828304048184855
2 iterations since best score.
Epoch 92: train loss: 0.02158625805340921
valid loss: 0.01830057208871601
3 iterations since best score.
Early stopping
predictions_df=valid_df
predictions_df['predictions']=np.vstack(predictions)
performance_assessment(predictions_df, top_k_list=[100])
AUC ROC | Average precision | Card Precision@100 | |
---|---|---|---|
0 | 0.861 | 0.651 | 0.276 |
The three metrics are very close, with or without the additional feature. They respectively went from 0.859, 0.646 and 0.28 to 0.861, 0.651 and 0.276. The conclusion is therefore mitigated and does not show a significant benefit from this semi-supervised modeling. Nevertheless, keep in mind that, in practice in a different setting, there can be a benefit, especially if the quantity of available unlabeled data is much larger than the quantity of labeled data.
Also, note that there are several directions for improvement. For example, this semi-supervised technique can be pushed further by training two separate autoencoders, for each class, and by using both reconstruction errors as additional variables.
3.8. Conclusion¶
Autoencoders are part of the large deep learning models family. Their goal is to learn representations to reconstruct descriptive variables, so they have been widely used for unsupervised learning problems. Anomaly detection, and in particular fraud detection, can be tackled with unsupervised or semi-supervised techniques.
In this section, we used the autoencoder, and in particular its reconstruction error, as an indicator for fraud risk. Used solely (unsupervised method), it detects data points that are away from the rest of the distribution, which allows detecting many frauds but also introduces a lot of false alerts (e.g. genuine transactions that have rare characteristics). Therefore, it obtains a decent AUC ROC but low precision-based metrics. Used as an extra variable in a supervised method (semi-supervised usage), it can allow boosting the performance in specific settings.