CTGAN 完整教學
Repository: https://github.com/sdv-dev/CTGAN Stars: 1,560 | Forks: 331 | Language: Python | License: Business Source License (BSL) Tags:
synthetic-data,generative-adversarial-network,tabular-data,data-generation最後更新: 2026-06-08 | 目前版本: v0.12.1 論文: Modeling Tabular data using Conditional GAN — NeurIPS 2019
2. 核心架構 (Core Architecture)
2.1 整體資料流
flowchart TB
subgraph INPUT["輸入層"]
RAW["原始表格資料
pandas DataFrame"]
DC["離散欄位清單
discrete_columns"]
end
subgraph TRANSFORM["資料轉換層 DataTransformer"]
direction TB
CONT["連續欄位
ClusterBasedNormalizer
(Bayesian GMM)"]
DISC["離散欄位
OneHotEncoder"]
CONT --> |"scalar [-1,1] + mode softmax"| ENCODED["轉換後矩陣
float32 ndarray"]
DISC --> |"one-hot vector"| ENCODED
end
subgraph SAMPLER["條件取樣層 DataSampler"]
CONDVEC["Conditional Vector
條件向量"]
LOGFREQ["Log-Frequency
對數頻率取樣"]
end
subgraph GAN["GAN 訓練核心"]
direction LR
subgraph GEN["Generator 生成器"]
NOISE["z ~ N(0,1)
噪聲向量"]
RESIDUAL["Residual Layers
殘差層 × N"]
ACTIVATE["Activation
tanh + Gumbel-Softmax"]
end
subgraph DIS["Discriminator 鑑別器"]
PAC["PacGAN (pac=10)
多樣本打包"]
WGANGP["WGAN-GP Loss
梯度懲罰"]
end
GEN -->|"fake data"| DIS
DIS -->|"gradient"| GEN
end
subgraph OUTPUT["輸出層"]
INVERSE["逆轉換
inverse_transform"]
SYNTH["合成表格資料
pandas DataFrame"]
end
RAW --> TRANSFORM
DC --> TRANSFORM
ENCODED --> SAMPLER
SAMPLER --> GAN
ENCODED -->|"real data"| DIS
CONDVEC -->|"concat with z"| NOISE
GAN -->|"fakeact"| INVERSE
INVERSE --> SYNTH
style INPUT fill:#e8f4f8,stroke:#2196F3
style TRANSFORM fill:#fff3e0,stroke:#FF9800
style SAMPLER fill:#f3e5f5,stroke:#9C27B0
style GAN fill:#fce4ec,stroke:#E91E63
style OUTPUT fill:#e8f5e9,stroke:#4CAF50
2.2 模組拆解
CTGAN 的程式碼結構十分精簡,核心僅 6 個檔案:
1ctgan/
2├── __init__.py # 入口:匯出 CTGAN, TVAE, load_demo
3├── data_transformer.py # DataTransformer:Bayesian GMM + OneHot
4├── data_sampler.py # DataSampler:條件向量取樣邏輯
5├── synthesizers/
6│ ├── base.py # BaseSynthesizer:random state 管理、序列化
7│ ├── ctgan.py # CTGAN:Generator + Discriminator + 訓練迴圈
8│ └── tvae.py # TVAE:Encoder + Decoder + ELBO loss
9├── data.py # CLI 資料讀取(CSV/TSV)
10├── demo.py # 內建 demo 資料集(Adult Census)
11└── errors.py # 自訂例外
2.3 關鍵技術細節
Mode-specific Normalization
連續欄位的處理是 CTGAN 最核心的創新。傳統做法是直接 min-max 正規化,但這會破壞多峰分佈。CTGAN 的做法:
- 對每個連續欄位 fit 一個
ClusterBasedNormalizer(底層是 Bayesian GMM,最多 10 個 cluster) - 轉換後的輸出為
[scalar, mode_indicator]:scalar:經 tanh 壓縮到 [-1, 1] 的正規化值mode_indicator:softmax 向量,表示該值屬於哪個高斯分量
- 反轉換時根據 mode_indicator 選擇對應的均值和標準差來還原原始值
Conditional Vector & Training-by-Sampling
訓練時的條件取樣機制:
- 隨機選擇一個離散欄位
- 根據 log-frequency 機率選擇該欄位的一個類別值
- 構造 one-hot conditional vector
c並與噪聲z拼接 - 從真實資料中取出符合同類別的樣本作為 real data
- 計算 cross-entropy conditional loss,確保生成器忠實於條件
PacGAN (Packing)
Discriminator 不是逐筆判斷,而是將 pac=10 筆資料打包成一組一起判斷。這個技巧(來自 PacGAN 論文)有助於防止 mode collapse (模式崩塌),讓生成器必須產生多樣化的輸出。
WGAN-GP Loss
使用 Wasserstein distance 搭配 Gradient Penalty (梯度懲罰) 取代傳統 GAN 的 BCE loss,訓練更穩定:
1loss_d = -(torch.mean(y_real) - torch.mean(y_fake)) # Wasserstein distance
2pen = discriminator.calc_gradient_penalty(...) # λ=10 gradient penalty
2.4 Generator 與 Discriminator 結構
Generator 使用 Residual Layer (殘差層),每層輸出與輸入 concatenate(而非相加),使得維度逐層累加:
1Input: z (128) + cond (C)
2→ Residual(128+C, 256) → dim = 128+C+256
3→ Residual(128+C+256, 256) → dim = 128+C+256+256
4→ Linear(dim, data_dim) → output
Discriminator 使用標準前饋網路搭配 LeakyReLU + Dropout:
1Input: data_dim × pac (10)
2→ Linear → LeakyReLU(0.2) → Dropout(0.5)
3→ Linear → LeakyReLU(0.2) → Dropout(0.5)
4→ Linear → 1 (real/fake score)
3. 安裝與設定 (Installation & Setup)
3.1 標準安裝
透過 pip(獨立使用):
1pip install ctgan
透過 conda:
1conda install -c pytorch -c conda-forge ctgan
透過 SDV 生態系(推薦):
1pip install sdv
SDV 封裝了 CTGAN 並提供額外的前處理、constraint (約束條件) 功能與 metadata 管理,適合生產環境使用。
3.2 開發環境安裝
1git clone https://github.com/sdv-dev/CTGAN.git
2cd CTGAN
3
4# 使用 uv(推薦)
5uv venv .venv && source .venv/bin/activate
6uv pip install -e ".[dev]"
7
8# 或使用 pip
9pip install -e ".[dev]"
3.3 相依套件
| 套件 | 用途 |
|---|---|
torch | 深度學習框架,Generator/Discriminator 實作 |
numpy, pandas | 資料處理 |
rdt (Reversible Data Transforms) | DataTransformer 底層的 ClusterBasedNormalizer 和 OneHotEncoder |
tqdm | 訓練進度顯示 |
joblib | 平行化 data transform |
3.4 GPU 支援
CTGAN 預設會嘗試使用 GPU(v0.11.1 起也支援 macOS MPS):
1# 強制使用 CPU
2ctgan = CTGAN(enable_gpu=False)
3
4# 使用 CUDA GPU(預設行為)
5ctgan = CTGAN(enable_gpu=True)
3.5 系統需求
- Python 3.9 ~ 3.14(v0.12.0 起支援 3.14)
- PyTorch(相容版本由 pip resolver 處理)
- 記憶體需求:資料集大小的 ~3-5 倍(Bayesian GMM fitting + training)
- GPU VRAM:小型資料集(<10K rows)約需 1-2 GB;大型資料集可能需要調整 batch_size
4. 使用方式與程式碼範例 (Usage & Code Examples)
4.1 基本使用:Adult Census 資料集
這是最基礎的使用方式,載入內建 demo 資料集並生成合成資料:
1from ctgan import CTGAN, load_demo
2
3# 載入 Adult Census 資料集(32,561 筆、15 欄位)
4real_data = load_demo()
5
6# 定義離散欄位名稱(CTGAN 必須明確知道哪些欄位是離散的)
7discrete_columns = [
8 'workclass',
9 'education',
10 'marital-status',
11 'occupation',
12 'relationship',
13 'race',
14 'sex',
15 'native-country',
16 'income',
17]
18
19# 初始化並訓練 CTGAN
20ctgan = CTGAN(
21 embedding_dim=128, # 噪聲向量維度
22 generator_dim=(256, 256), # Generator 殘差層大小
23 discriminator_dim=(256, 256), # Discriminator 層大小
24 batch_size=500,
25 epochs=50, # 生產環境建議 300+
26 verbose=True, # 顯示訓練進度
27)
28
29ctgan.fit(real_data, discrete_columns)
30
31# 生成 1000 筆合成資料
32synthetic_data = ctgan.sample(1000)
33
34# 驗證基本統計特性
35print("=== 原始資料 ===")
36print(real_data.describe())
37print("\n=== 合成資料 ===")
38print(synthetic_data.describe())
39
40# 檢查離散欄位分佈是否保留
41for col in discrete_columns:
42 print(f"\n--- {col} ---")
43 print("原始:", real_data[col].value_counts(normalize=True).head(3).to_dict())
44 print("合成:", synthetic_data[col].value_counts(normalize=True).head(3).to_dict())
4.2 進階使用:條件式生成 (Conditional Sampling) 應用於臨床資料
這個範例展示如何利用 CTGAN 的條件式生成功能,針對特定子群(如罕見疾病患者)生成更多合成樣本:
1import pandas as pd
2import numpy as np
3from ctgan import CTGAN
4
5# 模擬一個藥物反應資料集(類似臨床試驗 CRF 資料)
6np.random.seed(42)
7n_patients = 500
8
9clinical_data = pd.DataFrame({
10 'age': np.random.normal(55, 12, n_patients).clip(18, 85).astype(int),
11 'weight_kg': np.random.normal(70, 15, n_patients).clip(40, 150),
12 'bmi': np.random.normal(25, 5, n_patients).clip(15, 45),
13 'biomarker_level': np.random.lognormal(2, 0.8, n_patients),
14 'treatment_arm': np.random.choice(
15 ['Drug_A', 'Drug_B', 'Placebo'],
16 n_patients,
17 p=[0.4, 0.4, 0.2]
18 ),
19 'response': np.random.choice(
20 ['CR', 'PR', 'SD', 'PD'], # Complete/Partial Response, Stable/Progressive Disease
21 n_patients,
22 p=[0.05, 0.15, 0.50, 0.30] # CR 是少數類別
23 ),
24 'adverse_event': np.random.choice(
25 ['None', 'Mild', 'Moderate', 'Severe'],
26 n_patients,
27 p=[0.5, 0.3, 0.15, 0.05]
28 ),
29 'egfr': np.random.normal(80, 20, n_patients).clip(15, 120),
30})
31
32discrete_columns = ['treatment_arm', 'response', 'adverse_event']
33
34# 訓練 CTGAN
35ctgan = CTGAN(
36 epochs=100,
37 batch_size=100, # 較小資料集用較小 batch_size
38 generator_dim=(128, 128),
39 discriminator_dim=(128, 128),
40 pac=10,
41 verbose=True,
42)
43ctgan.fit(clinical_data, discrete_columns)
44
45# === 條件式生成:補足少數類別 ===
46# 生成更多 Complete Response (CR) 的患者資料
47synthetic_cr = ctgan.sample(
48 200,
49 condition_column='response',
50 condition_value='CR'
51)
52
53print(f"原始 CR 樣本數: {(clinical_data['response'] == 'CR').sum()}")
54print(f"合成 CR 樣本數: {(synthetic_cr['response'] == 'CR').sum()}")
55print(f"合成 CR 比例: {(synthetic_cr['response'] == 'CR').mean():.2%}")
56
57# === 儲存與載入模型 ===
58import pickle
59
60with open('ctgan_clinical_model.pkl', 'wb') as f:
61 pickle.dump(ctgan, f)
62
63# 後續載入
64with open('ctgan_clinical_model.pkl', 'rb') as f:
65 loaded_ctgan = pickle.load(f)
66 new_synthetic = loaded_ctgan.sample(500)
4.3 TVAE 對比與評估:結合 SDMetrics 的完整工作流
1import pandas as pd
2import numpy as np
3from ctgan import CTGAN, load_demo
4
5# 載入資料
6real_data = load_demo()
7discrete_columns = [
8 'workclass', 'education', 'marital-status', 'occupation',
9 'relationship', 'race', 'sex', 'native-country', 'income',
10]
11
12# --- 方法 A:CTGAN ---
13ctgan = CTGAN(epochs=50, verbose=True)
14ctgan.fit(real_data, discrete_columns)
15synthetic_ctgan = ctgan.sample(len(real_data))
16
17# 查看訓練 loss 曲線
18loss_df = ctgan.loss_values
19print("CTGAN 最終 Generator Loss:", loss_df['Generator Loss'].iloc[-1])
20print("CTGAN 最終 Discriminator Loss:", loss_df['Discriminator Loss'].iloc[-1])
21
22# --- 方法 B:TVAE ---
23from ctgan import TVAE
24
25tvae = TVAE(
26 embedding_dim=128,
27 compress_dims=(128, 128),
28 decompress_dims=(128, 128),
29 epochs=50,
30 batch_size=500,
31 loss_factor=2,
32 verbose=True,
33)
34tvae.fit(real_data, discrete_columns)
35synthetic_tvae = tvae.sample(len(real_data))
36
37# --- 手動評估(不依賴 SDMetrics 也可做基本比較) ---
38continuous_cols = [c for c in real_data.columns if c not in discrete_columns]
39
40print("\n=== 連續欄位統計比較 ===")
41for col in continuous_cols:
42 real_mean = real_data[col].mean()
43 ctgan_mean = synthetic_ctgan[col].mean()
44 tvae_mean = synthetic_tvae[col].mean()
45 print(f"{col:>20s}: 真實={real_mean:.2f} | CTGAN={ctgan_mean:.2f} | TVAE={tvae_mean:.2f}")
46
47print("\n=== 離散欄位 Jensen-Shannon Divergence ===")
48from scipy.spatial.distance import jensenshannon
49
50for col in discrete_columns[:3]: # 取前三個欄位示範
51 real_dist = real_data[col].value_counts(normalize=True).sort_index()
52 ctgan_dist = synthetic_ctgan[col].value_counts(normalize=True).reindex(real_dist.index, fill_value=0)
53 tvae_dist = synthetic_tvae[col].value_counts(normalize=True).reindex(real_dist.index, fill_value=0)
54
55 jsd_ctgan = jensenshannon(real_dist.values, ctgan_dist.values)
56 jsd_tvae = jensenshannon(real_dist.values, tvae_dist.values)
57 print(f"{col:>20s}: CTGAN JSD={jsd_ctgan:.4f} | TVAE JSD={jsd_tvae:.4f}")
58
59# --- 使用 SDMetrics 做完整品質報告(如已安裝) ---
60try:
61 from sdmetrics.reports.single_table import QualityReport
62
63 metadata = {
64 'columns': {col: {'sdtype': 'categorical'} if col in discrete_columns
65 else {'sdtype': 'numerical'}
66 for col in real_data.columns}
67 }
68
69 report = QualityReport()
70 report.generate(real_data, synthetic_ctgan, metadata)
71 print(f"\nSDMetrics 品質分數: {report.get_score():.4f}")
72except ImportError:
73 print("\n(未安裝 sdmetrics,跳過完整品質報告)")
5. 在 SDG 生態系中的定位 (Position in the SDG Ecosystem)
5.1 技術世代定位
在合成資料生成 (SDG) 的技術演進中,CTGAN 處於第一世代 GAN-based 方法的代表位置:
1第一世代:GAN-based(2019-2022 主流)
2├── CTGAN ← 你在這裡
3├── dp_cgans(CTGAN + Differential Privacy)
4├── PATE-GAN
5└── TableGAN
6
7第二世代:Diffusion-based(2022-2025 興起)
8├── TabDDPM(Denoising Diffusion)
9├── TabDiff(Score-based Diffusion)
10├── ForestDiffusion(XGBoost + Diffusion)
11└── TabCSDI(Conditional Score-based Diffusion)
12
13第三世代:LLM-based(2024- 新興)
14├── GReaT(GPT-2 fine-tuned on tabular)
15├── distilabel(LLM-as-judge 合成)
16└── REaLTabFormer(Transformer-based)
5.2 與 SDV 生態系的關係
CTGAN 是 SDV (Synthetic Data Vault) 生態系的底層引擎之一:
| SDV 層級 | 工具 | 角色 |
|---|---|---|
| 用戶介面層 | sdv | 完整 API、metadata 管理、constraint |
| 模型層 | ctgan (CTGAN/TVAE) | GAN/VAE 合成引擎 |
| 模型層 | copulas | 統計模型合成引擎 (GaussianCopula) |
| 轉換層 | rdt | Reversible Data Transforms |
| 評估層 | sdmetrics | 品質、隱私、診斷指標 |
在生產環境中,推薦透過 SDV 使用 CTGAN,因為 SDV 提供:
- 自動 metadata 推斷
- Constraint 支援(例如「column A 必須大於 column B」)
- 多表 (multi-table) 關聯生成
- 時間序列 (time-series) 生成
5.3 在 Bio-SDG 領域的角色
在生物醫學合成資料的應用鏈中,CTGAN 定位為通用表格生成基礎設施:
- scRNA-seq 資料:CTGAN 可以處理經 PCA 降維後的表格化表達量資料,但不適合直接處理高維度 raw count matrix(數萬個基因欄位會讓 Bayesian GMM 和 one-hot encoding 崩潰)
- 臨床試驗 CRF 資料:適用,混合型態欄位 + 類別不平衡是其強項
- ADMET 性質預測:可為化合物描述符 (descriptor) 表格生成合成樣本
- Patient record:可搭配 DP (Differential Privacy; 差分隱私) wrapper 如
dp_cgans生成隱私安全的合成病歷
6. 與其他工具的整合 (Integration with Other SDG Tools)
6.1 與 SDMetrics 整合(品質評估)
1# CTGAN 生成 → SDMetrics 評估 → 迭代改善
2from sdmetrics.single_table import KSComplement, TVComplement
3
4# 連續欄位品質
5ks_score = KSComplement.compute(real_data, synthetic_data,
6 column_name='age')
7
8# 離散欄位品質
9tv_score = TVComplement.compute(real_data, synthetic_data,
10 column_name='sex')
6.2 與 dp_cgans 整合(差分隱私)
dp_cgans 是 CTGAN 的隱私強化版,在原始 CTGAN 訓練迴圈中加入 DP-SGD (Differentially Private Stochastic Gradient Descent):
1from dpctgan import DPCTGAN
2
3dp_model = DPCTGAN(
4 epochs=100,
5 epsilon=1.0, # 隱私預算(越小越嚴格)
6 delta=1e-5, # 隱私鬆弛參數
7)
8dp_model.fit(clinical_data, discrete_columns)
9private_synthetic = dp_model.sample(1000)
6.3 與 Diffusion-based 方法的比較切換
在評估是否從 CTGAN 遷移到 TabDDPM 等 diffusion-based 方法時,建議的實驗框架:
1# 統一評估介面
2results = {}
3
4# CTGAN baseline
5from ctgan import CTGAN
6ctgan = CTGAN(epochs=300)
7ctgan.fit(train_data, discrete_columns)
8results['CTGAN'] = ctgan.sample(len(train_data))
9
10# TabDDPM(如已安裝)
11# from tab_ddpm import TabDDPM
12# ddpm = TabDDPM(...)
13# ddpm.fit(train_data)
14# results['TabDDPM'] = ddpm.sample(len(train_data))
15
16# 統一用 SDMetrics 比較
17from sdmetrics.reports.single_table import QualityReport
18for name, syn in results.items():
19 report = QualityReport()
20 report.generate(train_data, syn, metadata)
21 print(f"{name}: Quality Score = {report.get_score():.4f}")
6.4 與 AIKT Pipeline 的整合點
在 AI Knowledge Template (AIKT) 管線中,CTGAN 可整合至以下工作階段:
| AIKT 階段 | 整合方式 |
|---|---|
| WP2 資料前處理 | 使用 CTGAN 擴增小樣本資料集 |
| WP3 特徵工程 | 對 descriptor table 做 augmentation |
| WP5 模型訓練 | 生成合成 training data 改善 class imbalance |
| WP6 驗證 | 使用 SDMetrics 評估合成資料品質 |
7. 優缺點分析 (Strengths & Limitations)
7.1 優勢
| 優勢 | 說明 |
|---|---|
| 學術基礎穩固 | NeurIPS 2019 論文,引用數超過 2,000,經過大量實驗驗證 |
| 生態系完整 | SDV 全家桶(rdt + sdmetrics + sdv)提供從轉換到評估的完整鏈路 |
| API 極簡 | fit() + sample() 兩行搞定,入門門檻極低 |
| 條件式生成 | 原生支援依特定離散欄位值生成,解決 class imbalance |
| 混合型態處理 | Bayesian GMM + OneHot 組合能正確處理連續+離散混合欄位 |
| 模型可序列化 | 支援 pickle 儲存/載入,便於部署 |
| GPU 加速 | 支援 CUDA 和 macOS MPS |
| 活躍維護 | 最新版 v0.12.1(2026-02),持續支援新 Python 版本 |
7.2 限制
| 限制 | 說明 | 影響 |
|---|---|---|
| 訓練不穩定 | GAN 的固有問題——mode collapse、訓練震盪 | 需要多次調參和重訓 |
| 不支援缺失值 | 連續欄位不能有 null,需要先自行填補 | 增加前處理負擔 |
| 高維度瓶頸 | 欄位數超過 ~100 時效能和品質急劇下降 | 不適合 raw omics 資料 |
| 無隱私保證 | 原生不含 DP 機制,需外掛 dp_cgans | 醫療場景需額外處理 |
| 無多表支援 | 單表模型,多表關聯需透過 SDV | 獨立使用時功能受限 |
| 無時序支援 | 無法捕捉時間相依性 | 不適合 longitudinal data |
| 品質上限 | Diffusion-based 方法在多數 benchmark 已超越 CTGAN | 新專案建議同時評估 TabDDPM |
| BSL 授權 | Business Source License,非傳統開源授權 | 商業使用需確認條款 |
7.3 何時選擇 CTGAN,何時選擇替代方案
| 場景 | 推薦工具 | 原因 |
|---|---|---|
| 快速 prototype、概念驗證 | CTGAN | API 最簡潔,生態系最完整 |
| 最高品質要求 | TabDDPM / ForestDiffusion | Diffusion 在多數 benchmark 勝出 |
| 醫療隱私合規 | dp_cgans 或 synthcity (DPGAN) | 需要形式化隱私保證 |
| 高維度 omics 資料 | scDesign3 / scVI | 專為 single-cell 設計 |
| 多表關聯資料 | SDV (HMA) | SDV 的多表合成引擎 |
| 時間序列臨床資料 | SDV (PAR) / TabCSDI | 原生支援時序依賴 |
| LLM 驅動的語義增強 | GReaT / distilabel | 利用語言模型理解欄位語義 |
7.4 效能基準參考
根據公開 benchmark(TabDDPM 論文、synthcity 論文等)的大致排名:
1品質排名(由高到低,視資料集而異):
2TabDDPM / ForestDiffusion ≈ TabDiff
3 > TVAE ≈ CTGAN
4 > GaussianCopula
5 > 簡單統計取樣
但 CTGAN 在訓練速度和易用性上仍有優勢,且 SDV 生態系的完整度是其他工具難以匹敵的。
參考資料
- Xu, L., Skoularidou, M., Cuesta-Infante, A., & Veeramachaneni, K. (2019). Modeling Tabular data using Conditional GAN. NeurIPS 2019. https://arxiv.org/abs/1907.00503
- SDV 官方文件:https://sdv.dev
- CTGAN GitHub:https://github.com/sdv-dev/CTGAN
- DataCebo Forum:https://forum.datacebo.com
Comments