Collecting real data for ML often runs into compliance constraints: GDPR, HIPAA, or corporate policies prohibit transferring raw datasets to developers. In a project with bank transactions, we couldn't use real records — we had to generate synthetic data preserving distributions and correlations up to KS p-value > 0.4. Synthetic data solves this problem: expands datasets, balances classes (e.g., turn 1% fraudulent transactions into 50%), tests models without breach risk. In our work, we have implemented generation for 15+ projects in finance, healthcare, and retail. Each project requires an individual approach — no universal solution exists. The problem is especially acute with imbalanced datasets: if the target class is less than 1%, without synthetic data you cannot train a model. We use CTGAN with conditional sampling to generate examples of the required class in the right proportion. Savings on data collection — up to 40%, and on compliance audit — up to 30%.
Why is synthetic tabular data necessary for ML?
The key pain point is the shortage of high-quality labeled data. Even if data exists, it often contains personal information inaccessible to external teams. Synthetic data removes these restrictions: you get a dataset with the same statistical properties but without leakage risk. For imbalanced tasks (fraud detection, rare diseases), this is the only way to obtain a representative sample of the minority class. Savings on data collection can reach 40%, and development speed increases manyfold.
How synthetic tabular data solves the problem of imbalanced classes
CTGAN uses a conditional vector that sets the desired ratio of categories in the generated dataset. For example, for fraud detection we fix the share of fraudulent transactions at 50% — this sharply improves the model's recall. The generator and discriminator compete: the former learns to create realistic records, the latter to distinguish them from real ones. As a result, distributions and correlations are preserved with high accuracy. For financial data, typical KS p-value for numerical features is 0.2–0.6.
Choosing a generation method — synthetic tabular data
The choice depends on the data type and privacy requirements. Based on our experience:
| Method | Speed | Quality | Privacy | Suitable for |
|---|---|---|---|---|
| Gaussian Copula | Fast | Good | High | Numerical data, normal distributions |
| CTGAN | Slow | Excellent | Medium | Categorical + numerical |
| TVAE | Medium | Excellent | Medium | High dimensionality |
| REaLTabFormer | Slow | Superior | Requires DP | Complex dependencies |
Gaussian Copula works 10 times faster than CTGAN, but CTGAN better preserves complex multimodal distributions. For imbalanced classes, CTGAN guarantees exact class ratio after generation via conditional vector. We tune hyperparameters (embedding_dim, generator_dim) for each dataset — GPU utilization reaches 90% per epoch.
How to improve synthetic data quality with fine-tuning
Fine-tuning a generative model on a specific domain improves quality. For medical data, we fine-tune a pretrained CTGAN for 10 epochs with a reduced learning rate. Result: KS p-value improves from 0.05 to 0.4. However, it's important not to overfit — we use early stopping based on SDMetrics metrics. For each project, we create a model card that records hyperparameters, metrics, and generation conditions.
How to evaluate the quality of synthetic data
Validation is a key stage. We use SDMetrics and scipy to check distributions and correlations. The goal is for synthetic data to be statistically indistinguishable from real (p-value > 0.05).
from scipy.stats import ks_2samp import matplotlib.pyplot as plt def validate_synthetic_quality(real: pd.DataFrame, synthetic: pd.DataFrame) -> dict: results = {} for col in real.select_dtypes(include=np.number).columns: ks_stat, p_value = ks_2samp(real[col].dropna(), synthetic[col].dropna()) results[col] = { 'real_mean': real[col].mean(), 'synthetic_mean': synthetic[col].mean(), 'real_std': real[col].std(), 'synthetic_std': synthetic[col].std(), 'ks_stat': ks_stat, 'distribution_match': p_value > 0.05 } real_corr = real.select_dtypes(np.number).corr() synth_corr = synthetic.select_dtypes(np.number).corr() corr_diff = (real_corr - synth_corr).abs().mean().mean() results['correlation_mae'] = corr_diff return results Typical quality thresholds achievable in practice:
| Metric | Target Value | Typical Result (CTGAN) |
|---|---|---|
| KS p-value (numerical) | > 0.05 | 0.10–0.60 |
| Correlation MAE | < 0.05 | 0.02–0.04 |
| Category coverage | > 95% | 98–100% |
For most ML tasks, synthetic data generated by CTGAN with a score > 0.85 on SDMetrics allows achieving 95–98% of model quality compared to training on real data of the same volume.
Expand CTGAN code example
import pandas as pd from ctgan import CTGAN import numpy as np def train_ctgan_synthesizer( data: pd.DataFrame, discrete_columns: list, epochs: int = 300 ) -> CTGAN: synthesizer = CTGAN( embedding_dim=128, generator_dim=(256, 256), discriminator_dim=(256, 256), batch_size=500, epochs=epochs, verbose=True, pac=10, ) synthesizer.fit(data, discrete_columns=discrete_columns) return synthesizer financial_data = pd.read_parquet("transactions.parquet") discrete_cols = ['merchant_category', 'transaction_type', 'currency', 'is_fraud'] synth = train_ctgan_synthesizer(financial_data, discrete_cols) n_real = len(financial_data) synthetic = synth.sample(n_real * 5) print(f"Real fraud rate: {financial_data['is_fraud'].mean():.4f}") print(f"Synthetic fraud rate: {synthetic['is_fraud'].mean():.4f}") CTGAN: Modeling Tabular Data using Conditional GAN (Xu et al., 2019)
When validation is critical: typical mistakes
- Overfitting the generator: if the model memorized real rows, KS p-value is anomalously high (> 0.9). Check for duplicates.
- Wrong metric choice: KS test alone is insufficient — be sure to look at correlation MAE and category coverage.
- Ignoring data types: categorical features with rare values require increased batch_size and epochs.
What is included in the development of a synthetic data pipeline?
We offer a full cycle of work:
- Dataset analysis: evaluation of distributions, correlations, missing values, and outliers.
- Model selection and tuning: choosing architecture (CTGAN, TVAE, Gaussian Copula) and hyperparameters.
- Training and validation: using SDMetrics, KS tests, correlation analysis.
- Integration: creating a pipeline on Airflow or Docker, API for generation.
- Documentation: model card with metrics, operation manual, quality report.
- Team training: workshop on using the synthesizer and interpreting metrics.
- Warranty support: 2 weeks after implementation.
Timelines — from 2 to 4 weeks depending on dataset complexity. Cost is calculated individually. Get a consultation on choosing a generation method for your dataset — contact us. Order development of a pipeline for your data.







