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 的做法:

  1. 對每個連續欄位 fit 一個 ClusterBasedNormalizer(底層是 Bayesian GMM,最多 10 個 cluster)
  2. 轉換後的輸出為 [scalar, mode_indicator]
    • scalar:經 tanh 壓縮到 [-1, 1] 的正規化值
    • mode_indicator:softmax 向量,表示該值屬於哪個高斯分量
  3. 反轉換時根據 mode_indicator 選擇對應的均值和標準差來還原原始值

Conditional Vector & Training-by-Sampling

訓練時的條件取樣機制:

  1. 隨機選擇一個離散欄位
  2. 根據 log-frequency 機率選擇該欄位的一個類別值
  3. 構造 one-hot conditional vector c 並與噪聲 z 拼接
  4. 從真實資料中取出符合同類別的樣本作為 real data
  5. 計算 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 底層的 ClusterBasedNormalizerOneHotEncoder
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)
轉換層rdtReversible 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、概念驗證CTGANAPI 最簡潔,生態系最完整
最高品質要求TabDDPM / ForestDiffusionDiffusion 在多數 benchmark 勝出
醫療隱私合規dp_cganssynthcity (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 生態系的完整度是其他工具難以匹敵的。


參考資料

  1. Xu, L., Skoularidou, M., Cuesta-Infante, A., & Veeramachaneni, K. (2019). Modeling Tabular data using Conditional GAN. NeurIPS 2019. https://arxiv.org/abs/1907.00503
  2. SDV 官方文件:https://sdv.dev
  3. CTGAN GitHub:https://github.com/sdv-dev/CTGAN
  4. DataCebo Forum:https://forum.datacebo.com