Documentation Index
Fetch the complete documentation index at: https://mintlify.com/Antisource/FFD/llms.txt
Use this file to discover all available pages before exploring further.
Credit card fraud datasets are inherently imbalanced — genuine transactions outnumber fraudulent ones by a wide margin. Training classifiers directly on such skewed data causes models to favour the majority class and miss real fraud. This pipeline addresses the imbalance by training a Generative Adversarial Network (GAN) on the minority class (fraud) and using it to synthesise additional fraud samples that statistically resemble real ones.
Building the Models
The GAN consists of two neural networks that compete against each other during training.
Generator
The generator learns to map a random noise vector (dimension 29) to a synthetic fraud transaction. It is built as a fully connected Sequential model with relu activations, he_uniform kernel initialisation, and BatchNormalization layers. The output layer uses a linear activation to produce unbounded continuous values:
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, BatchNormalization
def build_generator():
model = Sequential()
model.add(Dense(32, activation='relu', input_dim=29, kernel_initializer='he_uniform'))
model.add(BatchNormalization())
model.add(Dense(64, activation='relu'))
model.add(BatchNormalization())
model.add(Dense(128, activation='relu'))
model.add(BatchNormalization())
model.add(Dense(29, activation='linear'))
model.summary()
return model
Discriminator
The discriminator is a binary classifier that distinguishes real fraud transactions from generator output. It uses relu activations and ends with a single sigmoid unit. It is compiled with Adam optimiser and binary cross-entropy loss:
def build_discriminator():
model = Sequential()
model.add(Dense(32, activation='relu', input_dim=29, kernel_initializer='he_uniform'))
model.add(Dense(64, activation='relu'))
model.add(Dense(32, activation='relu'))
model.add(Dense(32, activation='relu'))
model.add(Dense(16, activation='relu'))
model.add(Dense(1, activation='sigmoid'))
model.compile(optimizer='adam', loss='binary_crossentropy')
model.summary()
return model
Combining into a GAN
The combined GAN model chains the generator and discriminator end-to-end so that generator weights can be updated through the discriminator’s loss signal. The discriminator must be frozen (trainable = False) during this combined forward pass — otherwise both networks would receive conflicting gradient updates from the same backward pass:
from tensorflow.keras.models import Model
from tensorflow.keras.layers import Input
def build_gan(generator, discriminator):
# Freeze discriminator weights for generator training
discriminator.trainable = False
gan_input = Input(shape=(generator.input_shape[1],))
x = generator(gan_input)
gan_output = discriminator(x)
gan = Model(gan_input, gan_output)
gan.summary()
return gan
After building, compile the GAN outside the function:
generator = build_generator()
discriminator = build_discriminator()
gan = build_gan(generator, discriminator)
gan.compile(optimizer='adam', loss='binary_crossentropy')
Setting discriminator.trainable = False inside the GAN context means that when the GAN is trained, only the generator’s weights are updated — the discriminator is used purely as a differentiable loss function for the generator.
Training Loop
Training alternates between two phases every epoch:
- Discriminator update — real fraud samples are labelled
1 and generator output is labelled 0; the discriminator is trained on both batches.
- Generator update — the GAN is trained end-to-end with the discriminator frozen, using misleading labels (
1) so the generator learns to produce samples the discriminator cannot distinguish from real ones.
num_epochs = 1000
batch_size = 64
half_batch = int(batch_size / 2)
for epoch in range(num_epochs):
# ── Phase 1: Train discriminator ──────────────────────────────────
x_fake = generate_synthetic_data(generator, half_batch)
y_fake = np.zeros((half_batch, 1))
x_real = data_fraud.drop("Class", axis=1).sample(half_batch)
y_real = np.ones((half_batch, 1))
discriminator.trainable = True
discriminator.train_on_batch(x_real, y_real)
discriminator.train_on_batch(x_fake, y_fake)
# ── Phase 2: Train generator (via GAN) ───────────────────────────
noise = np.random.normal(0, 1, (batch_size, 29))
gan.train_on_batch(noise, np.ones((batch_size, 1)))
if epoch % 10 == 0:
monitor_generator(generator)
Monitoring Quality
The monitor_generator function generates a snapshot of synthetic samples every 10 epochs and overlays them on real fraud data in PCA space. If the synthetic cluster drifts away from the real cluster, training is still unstable; a tight overlap signals good convergence:
def monitor_generator(generator):
# Initialize a PCA object with 2 components
pca = PCA(n_components=2)
# Drop the 'Class' column from the fraud dataset to get real data
real_fraud_data = data_fraud.drop("Class", axis=1)
# Transform the real fraud data using PCA
transformed_data_real = pca.fit_transform(real_fraud_data.values)
df_real = pd.DataFrame(transformed_data_real)
df_real['label'] = "real"
# Generate synthetic fraud data (492 samples)
synthetic_fraud_data = generate_synthetic_data(generator, 492)
# Transform the synthetic fraud data using PCA
transformed_data_fake = pca.fit_transform(synthetic_fraud_data)
df_fake = pd.DataFrame(transformed_data_fake)
df_fake['label'] = "fake"
# Concatenate the real and fake data DataFrames
df_combined = pd.concat([df_real, df_fake])
# Create a scatterplot to visualize real vs synthetic fraud
import matplotlib.pyplot as plt
import seaborn as sns
plt.figure()
sns.scatterplot(data=df_combined, x=0, y=1, hue='label', s=10)
plt.show()
Generating Synthetic Data
Once training converges, generate_synthetic_data draws num_samples vectors from standard Gaussian noise and passes them through the trained generator:
def generate_synthetic_data(generator, num_samples):
noise = np.random.normal(0, 1, (num_samples, generator.input_shape[1]))
fake_data = generator.predict(noise)
return fake_data
# Generate 1000 synthetic fraud data points using the trained generator
synthetic_data = generate_synthetic_data(generator, 1000)
The value 492 used during monitoring matches the number of real fraud transactions in the original dataset (Class == 1). This allows a direct visual comparison of distributions between real and synthetic minority-class samples.
Monitoring GAN convergence with PCAPCA plots are a lightweight, model-agnostic way to spot GAN failure modes. Watch for two common problems:
- Mode collapse — all synthetic points collapse to a single location in PCA space, meaning the generator has found one “safe” output that always fools the discriminator.
- Distribution shift — the synthetic cluster consistently sits offset from the real cluster, indicating the generator has learned the wrong statistical moments.
If either symptom appears, try reducing the learning rate, adding more regularisation, or training for more epochs before re-generating synthetic data.