1 基础信息
1.1 案例背景
这是一个用于增强结构化客户交易数据的生成式算法。这种类型的数据增强在金融、电子商务等领域非常有用,可以帮助解决数据不平衡问题或在小数据集上提高模型性能。
1.2 问题定义
给定原始交易数据集D={x₁, x₂, ..., xₙ},其中每条记录包含:
交易金额
交易时间
客户年龄
客户收入水平
交易类别
地理位置
是否为欺诈交易(标签)
目标:生成与原始数据分布相似但多样化的新样本,同时保持字段间的合理关系。
2 实现步骤
2.1 数据预处理
import pandas as pd
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.compose import ColumnTransformer
# 假设原始数据已加载为df
# 分离数值型和类别型特征
numeric_features = ['amount', 'age', 'income']
categorical_features = ['time_of_day', 'category', 'location']
# 构建预处理管道
preprocessor = ColumnTransformer(
transformers=[
('num', StandardScaler(), numeric_features),
('cat', OneHotEncoder(handle_unknown='ignore'), categorical_features)
])
# 对特征进行预处理
X_processed = preprocessor.fit_transform(df.drop('is_fraud', axis=1))
y = df['is_fraud'].values
2.2 构建CVAE模型
import tensorflow as tf
from tensorflow.keras.layers import Input, Dense, Lambda, Concatenate
from tensorflow.keras.models import Model
from tensorflow.keras import backend as K
# 定义编码器
def build_encoder(input_dim, latent_dim, label_dim):
inputs = Input(shape=(input_dim,))
labels = Input(shape=(label_dim,))
x = Concatenate()([inputs, labels])
h = Dense(256, activation='relu')(x)
h = Dense(128, activation='relu')(h)
z_mean = Dense(latent_dim)(h)
z_log_var = Dense(latent_dim)(h)
return Model([inputs, labels], [z_mean, z_log_var], name='encoder')
# 定义解码器
def build_decoder(latent_dim, output_dim, label_dim):
latent_inputs = Input(shape=(latent_dim,))
labels = Input(shape=(label_dim,))
x = Concatenate()([latent_inputs, labels])
h = Dense(128, activation='relu')(x)
h = Dense(256, activation='relu')(h)
outputs = Dense(output_dim, activation='linear')(h)
return Model([latent_inputs, labels], outputs, name='decoder')
# 采样函数
def sampling(args):
z_mean, z_log_var = args
batch = K.shape(z_mean)[0]
dim = K.int_shape(z_mean)[1]
epsilon = K.random_normal(shape=(batch, dim))
return z_mean + K.exp(0.5 * z_log_var) * epsilon
# 构建完整CVAE
input_dim = X_processed.shape[1]
latent_dim = 16
label_dim = 1 # 二分类
encoder = build_encoder(input_dim, latent_dim, label_dim)
decoder = build_decoder(latent_dim, input_dim, label_dim)
# 定义输入
inputs = Input(shape=(input_dim,))
labels = Input(shape=(label_dim,))
# 编码
z_mean, z_log_var = encoder([inputs, labels])
z = Lambda(sampling)([z_mean, z_log_var])
# 解码
outputs = decoder([z, labels])
# 定义CVAE模型
cvae = Model([inputs, labels], outputs, name='cvae')
# 添加KL散度损失
reconstruction_loss = tf.keras.losses.mse(inputs, outputs)
reconstruction_loss *= input_dim
kl_loss = 1 + z_log_var - K.square(z_mean) - K.exp(z_log_var)
kl_loss = K.sum(kl_loss, axis=-1)
kl_loss *= -0.5
cvae_loss = K.mean(reconstruction_loss + kl_loss)
cvae.add_loss(cvae_loss)
cvae.compile(optimizer='adam')
2.3 训练模型
# 准备标签数据
y_reshaped = y.reshape(-1, 1)
# 训练模型
history = cvae.fit(
[X_processed.toarray(), y_reshaped],
epochs=100,
batch_size=64,
validation_split=0.2,
verbose=1
)
2.4 生成新样本
def generate_samples(num_samples, label_value):
# 从潜在空间随机采样
z_samples = np.random.normal(size=(num_samples, latent_dim))
# 创建标签数组
labels = np.full((num_samples, 1), label_value)
# 解码生成样本
generated_data = decoder.predict([z_samples, labels])
# 逆变换回原始特征空间
generated_df = pd.DataFrame(
data=generated_data,
columns=preprocessor.get_feature_names_out()
)
# 对数值特征进行逆标准化
num_cols = [f'num__{col}' for col in numeric_features]
generated_df[num_cols] = preprocessor.named_transformers_['num'].inverse_transform(
generated_df[num_cols]
)
# 对类别特征进行逆变换(取最大概率的类别)
cat_cols = [col for col in generated_df.columns if col.startswith('cat__')]
for orig_col in categorical_features:
# 获取该原始列的所有one-hot列
cols = [c for c in cat_cols if c.startswith(f'cat__{orig_col}_')]
if cols:
# 取概率最大的类别
max_idx = generated_df[cols].values.argmax(axis=1)
generated_df[orig_col] = [cols[i].split('_')[-1] for i in max_idx]
# 只保留原始列名
final_cols = numeric_features + categorical_features
generated_df = generated_df[final_cols]
generated_df['is_fraud'] = label_value
return generated_df
# 生成欺诈和非欺诈样本各1000个
generated_fraud = generate_samples(1000, 1)
generated_non_fraud = generate_samples(1000, 0)
# 合并生成的数据
augmented_data = pd.concat([df, generated_fraud, generated_non_fraud], ignore_index=True)
3 关键技术与优化
3 .1 条件控制
通过将标签作为条件输入,可以控制生成特定类别的样本,解决类别不平衡问题。
3.2 特征关系保持
数值字段的统计特性通过标准化/逆标准化保持
类别字段的关系通过one-hot编码和argmax保持
3.3 数据验证机制
def validate_generated_data(original, generated):
# 比较统计特性
for col in numeric_features:
print(f"\n{col} 统计比较:")
print("原始数据:", original[col].describe())
print("生成数据:", generated[col].describe())
# 比较类别分布
for col in categorical_features:
print(f"\n{col} 分布比较:")
print("原始数据:\n", original[col].value_counts(normalize=True))
print("生成数据:\n", generated[col].value_counts(normalize=True))
validate_generated_data(df, augmented_data)
3.4 边界控制:在生成后可添加业务规则验证
def apply_business_rules(df):
# 例如:年龄不能为负
df['age'] = df['age'].clip(lower=18, upper=100)
# 交易金额不能超过收入的一定比例
df['amount'] = np.where(df['amount'] > df['income']*0.5,
df['income']*0.5,
df['amount'])
return df
4 评估指标
统计相似性:比较原始数据和生成数据的统计特性
机器学习效用:使用原始数据、增强数据分别训练模型,比较性能
多样性指标:计算生成样本之间的平均距离
Novelty检测:确保生成样本不是简单复制原始样本
5 扩展应用
这种结构化数据增强方法可以应用于:
金融风控中的欺诈检测
医疗诊断中的罕见病例数据增强
制造业中的异常检测
客户流失预测中的小类别增强