哈喽,我是小白~
今儿和大家聊聊数据生成技术~
数据生成技术,能在数据稀缺的情况下提供足够的训练数据,提升模型的泛化能力。通过模拟真实世界的复杂情况,数据生成技术帮助模型在多样化场景下进行训练和测试。它还能用于合成复杂或特定条件下的数据,以应对偏差或过拟合等问题,提升模型的鲁棒性。
今儿涉及到的十种数据生成技术有:
数据增强 SMOTE 生成对抗网络 变分自编码器 回归模型的噪声注入 数据合成 Bootstrap 重采样 时间序列生成技术 回归树模拟 蒙特卡洛采样
文末有总结性的干货,大家记得领取~
一起来看下今天的内容!
1. 数据增强 (Data Augmentation)
数据增强主要在图像、文本和语音等领域使用,通过对现有样本进行各种变换,生成新的样本。这些操作不会改变数据的标签,但会改变数据的外观、格式或结构。
原理
图像数据增强包括旋转、缩放、剪切、平移、翻转等操作。数据增强的目标是通过小幅度变换数据来增加数据量,从而提高模型的泛化能力。
核心公式
数据增强的变换公式通常可以归结为仿射变换:
其中 是二维的仿射变换矩阵, 是偏移向量。
具体的几种操作:
旋转:
这里 是旋转角度。
缩放:
这里 和 分别是x轴和y轴的缩放系数。
剪切:
其中 和 是剪切系数。
推导
旋转变换:
图像的旋转变换是仿射变换的一种形式,假设有一个点 ,经过角度 的旋转后,得到新的点 :
缩放变换:
缩放即改变图像的尺寸。通过将点 乘以缩放因子 和 :
剪切变换:
剪切操作会将图像沿x轴或y轴进行倾斜:
这些几何变换可以组合成更复杂的增强方式。
Python案例
此案例中,展示文本数据的增强,并进行不同的可视化分析。通过对文本的增广操作,我们可以生成多个特征,例如:词频(Word Frequency)、词嵌入(Word Embedding)等。
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from sklearn.decomposition import PCA
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.preprocessing import LabelEncoder
from sklearn.manifold import TSNE
import random
# 设置随机种子
np.random.seed(42)
random.seed(42)
# 虚拟文本数据集
texts = [
"I love data science and machine learning",
"Data analysis and visualization are key skills",
"Deep learning is a subset of machine learning",
"Data science involves statistics and programming",
"Machine learning models can be complex",
"Visualization helps to communicate data insights",
]
# 标签:例如不同的文本类别(此处仅为展示效果)
labels = ['Tech', 'Tech', 'Tech', 'Tech', 'Tech', 'Tech']
# 数据增强:通过简单的替换操作来模拟
def synonym_replacement(text):
synonyms = {
'love': 'like', 'data': 'information', 'science': 'knowledge', 'machine': 'AI',
'learning': 'training', 'analysis': 'analytics', 'visualization': 'plotting',
'statistics': 'math', 'programming': 'coding', 'complex': 'complicated'
}
words = text.split()
new_words = [synonyms.get(word, word) for word in words]
return ' '.join(new_words)
augmented_texts = [synonym_replacement(text) for text in texts]
# 词频统计和向量化:
vectorizer = CountVectorizer()
X_original = vectorizer.fit_transform(texts)
X_augmented = vectorizer.transform(augmented_texts) # 使用相同的词汇表
# 将词汇转换为DataFrame以便可视化
word_freq_original = pd.DataFrame(X_original.toarray(), columns=vectorizer.get_feature_names_out())
word_freq_augmented = pd.DataFrame(X_augmented.toarray(), columns=vectorizer.get_feature_names_out())
# 数据增强前后词频对比可视化(柱状图)
def plot_word_frequencies():
word_sums_original = word_freq_original.sum(axis=0)
word_sums_augmented = word_freq_augmented.sum(axis=0)
plt.figure(figsize=(12, 6))
# 原始数据词频
plt.subplot(1, 2, 1)
word_sums_original.sort_values(ascending=False).plot(kind='bar', color='skyblue')
plt.title('Word Frequencies (Original)')
plt.xticks(rotation=45, ha='right')
# 增强后数据词频
plt.subplot(1, 2, 2)
word_sums_augmented.sort_values(ascending=False).plot(kind='bar', color='coral')
plt.title('Word Frequencies (Augmented)')
plt.xticks(rotation=45, ha='right')
# 生成文本嵌入(TF-IDF + PCA降维)
tfidf_vectorizer = TfidfVectorizer()
X_tfidf = tfidf_vectorizer.fit_transform(texts + augmented_texts)
# PCA降维
pca = PCA(n_components=2)
X_pca = pca.fit_transform(X_tfidf.toarray())
# 标签编码
label_encoder = LabelEncoder()
labels_encoded = label_encoder.fit_transform(labels * 2) # 两倍,因为包含增强数据
# 数据增强后的PCA降维可视化
def plot_pca_embeddings():
plt.figure(figsize=(8, 6))
colors = ['b', 'g', 'r', 'c', 'm', 'y', 'k']
for i in np.unique(labels_encoded):
plt.scatter(X_pca[labels_encoded == i, 0], X_pca[labels_encoded == i, 1],
color=colors[i], label=label_encoder.inverse_transform([i])[0], alpha=0.7)
plt.title('PCA of Text Embeddings')
plt.xlabel('PCA Component 1')
plt.ylabel('PCA Component 2')
plt.legend(loc='best')
# 组合绘图函数
def combined_plot():
plt.figure(figsize=(16, 10))
# 词频柱状图
plt.subplot(2, 1, 1)
plot_word_frequencies()
# PCA降维可视化
plt.subplot(2, 1, 2)
plot_pca_embeddings()
plt.tight_layout()
plt.show()
# 运行生成图形
combined_plot()
1. 数据增强:使用简单的同义词替换来模拟数据增强,这种方法虽然简单,但可以展示增强后词频的变化。
2. 词频柱状图:左图显示原始数据的词频,右图显示增强数据的词频。通过比较两个图,可以观察增强后哪些词汇出现的频率发生了变化。这有助于理解数据增强是否对文本结构产生了显著影响。
3. PCA降维可视化:使用PCA(主成分分析)对文本的词向量进行降维,展示文本在低维空间的分布。这有助于分析增强数据与原始数据在特征空间中的分布差异,是否有聚类、分离等现象。
2. SMOTE (Synthetic Minority Over-sampling Technique)
SMOTE 是一种处理不平衡数据集的过采样方法。它通过对少数类样本进行插值生成新的样本,避免了简单重复采样带来的过拟合风险。
原理
SMOTE 的核心思想是根据现有少数类样本和它的邻居进行线性插值生成新样本。假设少数类样本为 和它的最近邻 ,则新生成的样本为:
其中 是在 区间内的随机数。
核心公式
生成新样本公式:
其中:
是少数类样本 是少数类样本的一个邻居 是一个随机数
推导
SMOTE 的推导相对简单,基于线性插值。
1. 确定最近邻样本:
使用 KNN (K最近邻) 找到少数类样本 的最近邻 。
2. 生成新样本:
对于每个少数类样本 ,找到其最近邻样本 。然后根据公式:
生成新的少数类样本。这里的 是随机生成的。
这种插值生成的新数据点位于少数类样本及其邻居之间的连线上,从而引入更多的多样性。
Python案例
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import make_classification
from imblearn.over_sampling import SMOTE
from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import plot_confusion_matrix
from sklearn.model_selection import train_test_split
from matplotlib.colors import ListedColormap
# 生成虚拟数据集
X, y = make_classification(n_samples=1000, n_features=2, n_informative=2, n_redundant=0,
n_clusters_per_class=1, weights=[0.9, 0.1], flip_y=0, random_state=42)
# 可视化原始数据
def plot_data(X, y, title):
plt.scatter(X[y == 0][:, 0], X[y == 0][:, 1], color='red', label='Class 0', alpha=0.7)
plt.scatter(X[y == 1][:, 0], X[y == 1][:, 1], color='blue', label='Class 1', alpha=0.7)
plt.title(title)
plt.xlabel('Feature 1')
plt.ylabel('Feature 2')
plt.legend()
# 应用 SMOTE 技术
smote = SMOTE(sampling_strategy='auto', random_state=42)
X_res, y_res = smote.fit_resample(X, y)
# 决策边界可视化
def plot_decision_boundary(X, y, model, title):
x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1
y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1
xx, yy = np.meshgrid(np.arange(x_min, x_max, 0.02),
np.arange(y_min, y_max, 0.02))
Z = model.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)
plt.contourf(xx, yy, Z, alpha=0.3, cmap=ListedColormap(('red', 'blue')))
plot_data(X, y, title)
# 训练决策树模型
clf_before = DecisionTreeClassifier(random_state=42)
clf_after = DecisionTreeClassifier(random_state=42)
# 划分数据集并训练
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
X_res_train, X_res_test, y_res_train, y_res_test = train_test_split(X_res, y_res, test_size=0.3, random_state=42)
clf_before.fit(X_train, y_train)
clf_after.fit(X_res_train, y_res_train)
# 绘制图形
plt.figure(figsize=(15, 12))
# 原始数据
plt.subplot(2, 2, 1)
plot_data(X, y, "Original Data (Imbalanced)")
# SMOTE 后的数据
plt.subplot(2, 2, 2)
plot_data(X_res, y_res, "SMOTE Data (Balanced)")
# 原始数据的决策边界
plt.subplot(2, 2, 3)
plot_decision_boundary(X_test, y_test, clf_before, "Decision Boundary (Original Data)")
# SMOTE 后数据的决策边界
plt.subplot(2, 2, 4)
plot_decision_boundary(X_res_test, y_res_test, clf_after, "Decision Boundary (SMOTE Data)")
plt.tight_layout()
plt.show()
使用了 make_classification
生成一个不平衡的虚拟数据集,类别分布为90:10。SMOTE
用于生成新的少数类样本,以达到平衡数据的目的。
左上角是原始不平衡数据的散点图,展示数据不平衡的情况。 右上角是应用SMOTE后的数据散点图,展示了少数类样本的增加。 左下和右下分别是原始数据与应用SMOTE后的数据的决策边界可视化,可以看到SMOTE如何影响模型的学习。
3. 生成对抗网络 (GANs)
GAN 是一种生成模型,通过对抗学习生成逼真的数据。它由两个网络组成:生成器 和判别器 。生成器尝试生成逼真的数据,判别器尝试区分真实数据和生成的数据。
原理
GAN 通过对抗性训练来学习数据的分布。生成器 接收随机噪声 作为输入,输出一个生成样本 ,判别器 试图判别该样本是真实样本还是生成的假样本。通过这种博弈,生成器不断改进,直到生成的数据足够逼真。
核心公式
GAN 的目标是解决一个极大极小问题,其损失函数为:
其中:
是判别器对真实样本 的输出,表示该样本为真实数据的概率 是生成器的输出, 是从噪声分布 中采样的随机向量 表示真实数据的分布
推导
1. 判别器的目标:
判别器 的目标是最大化对真实样本的预测 ,同时最小化对生成样本 的预测,即:
其中,第一项表示判别器希望对真实样本的预测尽可能接近1,第二项表示对生成样本的预测接近0。
2. 生成器的目标:
生成器 的目标是使生成的样本 被判别器 误认为是真实样本,即最小化:
3. 对抗博弈:
判别器和生成器通过极大极小博弈进行优化,生成器试图最小化生成样本被判别器识别为假样本的概率,而判别器则试图最大化对真实样本和假样本的正确区分。最终,GAN 的目标函数变为:
最优解
理论上,当判别器和生成器达到纳什均衡时,生成器生成的样本分布和真实数据的分布相同,此时 对于所有样本 。
Python案例
生成对抗网络(GAN, Generative Adversarial Network)是一种通过两个神经网络对抗训练的技术,其中生成器(Generator)试图生成逼真的数据样本,而判别器(Discriminator)试图区分生成数据和真实数据。我们可以用虚拟数据集(例如二维数据点)来展示GAN的训练过程,并且使用数据分析图来展示其效果。
import torch
import torch.nn as nn
import torch.optim as optim
import numpy as np
import matplotlib.pyplot as plt
# 生成真实数据的函数,生成二维数据点
def real_data_distribution(num_samples):
return torch.randn(num_samples, 2) * 2 + torch.tensor([5, 5])
# 生成器类
class Generator(nn.Module):
def __init__(self):
super(Generator, self).__init__()
self.model = nn.Sequential(
nn.Linear(2, 16),
nn.ReLU(),
nn.Linear(16, 2)
)
def forward(self, x):
return self.model(x)
# 判别器类
class Discriminator(nn.Module):
def __init__(self):
super(Discriminator, self).__init__()
self.model = nn.Sequential(
nn.Linear(2, 16),
nn.ReLU(),
nn.Linear(16, 1),
nn.Sigmoid()
)
def forward(self, x):
return self.model(x)
# 超参数
num_epochs = 5000
batch_size = 128
learning_rate = 0.001
# 初始化生成器和判别器
G = Generator()
D = Discriminator()
# 优化器和损失函数
loss_fn = nn.BCELoss()
optimizer_G = optim.Adam(G.parameters(), lr=learning_rate)
optimizer_D = optim.Adam(D.parameters(), lr=learning_rate)
# 存储损失值以绘制损失曲线
losses_G = []
losses_D = []
# 训练GAN
for epoch in range(num_epochs):
# 训练判别器
real_data = real_data_distribution(batch_size)
fake_data = G(torch.randn(batch_size, 2))
# 判别器对真实数据的预测
real_preds = D(real_data)
real_labels = torch.ones(batch_size, 1)
loss_real = loss_fn(real_preds, real_labels)
# 判别器对生成数据的预测
fake_preds = D(fake_data.detach()) # 生成数据梯度不回传到生成器
fake_labels = torch.zeros(batch_size, 1)
loss_fake = loss_fn(fake_preds, fake_labels)
# 判别器总损失
loss_D = (loss_real + loss_fake) / 2
# 反向传播更新判别器
optimizer_D.zero_grad()
loss_D.backward()
optimizer_D.step()
# 训练生成器
fake_data = G(torch.randn(batch_size, 2))
preds = D(fake_data)
loss_G = loss_fn(preds, real_labels) # 生成器希望判别器认为其数据为真实
# 反向传播更新生成器
optimizer_G.zero_grad()
loss_G.backward()
optimizer_G.step()
# 记录损失
losses_G.append(loss_G.item())
losses_D.append(loss_D.item())
# 每1000次迭代打印一次损失
if epoch % 1000 == 0:
print(f'Epoch {epoch}: Generator Loss: {loss_G.item()}, Discriminator Loss: {loss_D.item()}')
# 绘制图形
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 6))
# 生成一些测试数据用于散点图
real_data_test = real_data_distribution(500).detach().numpy()
fake_data_test = G(torch.randn(500, 2)).detach().numpy()
# 图1:散点图显示真实数据和生成数据的分布
ax1.scatter(real_data_test[:, 0], real_data_test[:, 1], color='blue', label='Real Data', alpha=0.6)
ax1.scatter(fake_data_test[:, 0], fake_data_test[:, 1], color='red', label='Fake Data', alpha=0.6)
ax1.set_title('Real vs Generated Data')
ax1.legend()
ax1.grid(True)
# 图2:生成器和判别器的损失曲线
ax2.plot(losses_G, label='Generator Loss', color='green')
ax2.plot(losses_D, label='Discriminator Loss', color='orange')
ax2.set_title('Generator and Discriminator Loss During Training')
ax2.set_xlabel('Epochs')
ax2.set_ylabel('Loss')
ax2.legend()
ax2.grid(True)
plt.tight_layout()
plt.show()
真实数据分布:我们使用
real_data_distribution
函数生成一个简单的二维正态分布数据集,这样可以直观地在二维平面上展示数据点的分布。生成器(Generator)和判别器(Discriminator):生成器试图生成接近真实数据分布的二维点,判别器则尝试区分生成的数据与真实数据。两个模型的架构都使用简单的多层感知机(MLP)网络。
损失函数和优化:使用二元交叉熵(BCELoss)作为生成器和判别器的损失函数,并使用Adam优化器进行梯度更新。
图1 - 生成数据与真实数据的散点图:这个图能够清晰地展示GAN生成的虚拟数据与真实数据的对比,帮助我们直观地观察生成器的效果。随着训练的进行,生成的数据点应该逐渐接近真实数据点的分布。
图2 - 生成器和判别器的损失曲线:GAN的训练过程常常不稳定,监控生成器和判别器的损失函数有助于理解训练过程是否收敛,或者是否发生了模式崩溃(mode collapse)等问题。
4. 变分自编码器 (VAE)
VAE 是一种生成模型,它通过编码器将数据压缩到一个潜在空间,再通过解码器从潜在空间生成新的数据。
原理
VAE 假设数据 来自一个隐藏的潜在变量 ,目标是学习 的分布。VAE 使用变分推断,通过最大化变分下界 (ELBO) 来逼近后验分布。
核心公式
VAE 的目标是最大化对数似然 的变分下界:
其中:
是近似后验分布 是生成数据的条件分布 是先验分布,通常设为标准正态分布
推导
1. 边缘似然:
数据 的边缘似然可以通过积分潜在变量 得到:
但直接计算此积分非常困难。
2. 变分推断:
为了避免直接计算,我们引入一个近似后验分布 ,并将其分解为:
由于 KL 散度为正,因此可以得到变分下界 (ELBO):
3. 优化目标:
VAE 的目标是最大化 ELBO。第一项是重构误差,表示数据 和生成的样本 之间的差异;第二项是 KL 散度,用于逼近 和先验分布 的差异。
Python案例
我们使用虚拟数据集演示如何使用VAE生成数据,并通过两种不同的图表(潜在空间的2D散点图、重构后的数据对比图)来进行分析。
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader, TensorDataset
import matplotlib.pyplot as plt
import numpy as np
# 设置随机种子
torch.manual_seed(0)
np.random.seed(0)
# 生成虚拟数据集(例如,二维高斯分布)
data = np.random.randn(1000, 2)
dataset = TensorDataset(torch.tensor(data, dtype=torch.float32))
dataloader = DataLoader(dataset, batch_size=128, shuffle=True)
# 定义VAE的编码器和解码器结构
class VAE(nn.Module):
def __init__(self, input_dim=2, latent_dim=2):
super(VAE, self).__init__()
# 编码器
self.encoder = nn.Sequential(
nn.Linear(input_dim, 16),
nn.ReLU(),
nn.Linear(16, latent_dim * 2) # 输出均值和标准差
)
# 解码器
self.decoder = nn.Sequential(
nn.Linear(latent_dim, 16),
nn.ReLU(),
nn.Linear(16, input_dim)
)
def reparameterize(self, mu, logvar):
std = torch.exp(0.5 * logvar)
eps = torch.randn_like(std)
return mu + eps * std
def forward(self, x):
# 编码
x_encoded = self.encoder(x)
mu, logvar = torch.chunk(x_encoded, 2, dim=-1)
z = self.reparameterize(mu, logvar)
# 解码
x_reconstructed = self.decoder(z)
return x_reconstructed, mu, logvar, z
# 定义损失函数(重构损失 + KL散度)
def loss_function(reconstructed_x, x, mu, logvar):
recon_loss = nn.functional.mse_loss(reconstructed_x, x, reduction='sum')
kl_loss = -0.5 * torch.sum(1 + logvar - mu.pow(2) - logvar.exp())
return recon_loss + kl_loss
# 初始化模型、优化器
vae = VAE(input_dim=2, latent_dim=2)
optimizer = optim.Adam(vae.parameters(), lr=1e-3)
# 训练VAE
num_epochs = 100
vae.train()
for epoch in range(num_epochs):
total_loss = 0
for batch in dataloader:
x = batch[0]
optimizer.zero_grad()
reconstructed_x, mu, logvar, _ = vae(x)
loss = loss_function(reconstructed_x, x, mu, logvar)
loss.backward()
optimizer.step()
total_loss += loss.item()
if (epoch + 1) % 10 == 0:
print(f'Epoch {epoch+1}, Loss: {total_loss/len(dataset):.4f}')
# 获取潜在空间中的数据分布
vae.eval()
with torch.no_grad():
all_z = []
all_data = []
all_reconstructed_data = []
for batch in dataloader:
x = batch[0]
reconstructed_x, _, _, z = vae(x)
all_z.append(z)
all_data.append(x)
all_reconstructed_data.append(reconstructed_x)
all_z = torch.cat(all_z, dim=0).numpy()
all_data = torch.cat(all_data, dim=0).numpy()
all_reconstructed_data = torch.cat(all_reconstructed_data, dim=0).numpy()
# 绘制图形
plt.figure(figsize=(12, 6))
# 1. 潜在空间散点图
plt.subplot(1, 2, 1)
plt.scatter(all_z[:, 0], all_z[:, 1], c='blue', label='Latent Space', alpha=0.6)
plt.title("Latent Space Distribution")
plt.xlabel("Latent Variable 1")
plt.ylabel("Latent Variable 2")
plt.legend()
# 2. 原始数据与重构数据对比
plt.subplot(1, 2, 2)
plt.scatter(all_data[:, 0], all_data[:, 1], c='green', label='Original Data', alpha=0.5)
plt.scatter(all_reconstructed_data[:, 0], all_reconstructed_data[:, 1], c='red', label='Reconstructed Data', alpha=0.5)
plt.title("Original vs Reconstructed Data")
plt.xlabel("Feature 1")
plt.ylabel("Feature 2")
plt.legend()
# 显示图像
plt.tight_layout()
plt.show()
潜在空间的2D散点图:通过在潜在空间中绘制点,可以直观了解VAE学习的潜在变量分布,查看是否有不同数据点的聚类或模式。 原始数据与重构数据的对比:这个图展示了VAE在学习数据分布后的生成能力。通过比较原始数据与重构数据,可以评估模型的性能。
5. 回归模型的噪声注入
在回归任务中,通过在现有数据上添加随机噪声生成新样本。这是一种简单且有效的数据生成方法。
原理
我们通过将随机噪声注入到回归模型的目标变量上,从而生成新的样本。假设原始数据为 ,生成新数据 的方法是:
其中, 是服从高斯分布的噪声项 。
核心公式
公式为:
其中 是原始数据的输出, 是一个随机噪声,通常服从正态分布 。
推导
噪声注入的推导非常简单。假设我们有回归模型 ,然后在输出 上加入正态分布的噪声 ,从而生成新的数据点。
这使得模型能够在原始数据附近生成新的数据点,保持数据的多样性,同时不改变数据的分布特性。
Python案例
使用“回归模型的噪声注入”来生成一个虚拟数据集,并分析其中的数据。噪声注入的目的是为了增强模型的鲁棒性,避免过拟合。在这个案例中,我们将使用线性回归模型,并向目标变量添加高斯噪声来生成虚拟数据。
import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error
import seaborn as sns
# 设置随机种子
np.random.seed(42)
# 生成虚拟数据集
n_samples = 100
X = 2 * np.random.rand(n_samples, 1)
true_slope = 3.5
true_intercept = 1.2
noise = np.random.randn(n_samples, 1) # 高斯噪声
y_true = true_slope * X + true_intercept
y_noisy = y_true + noise # 加入噪声
# 拟合线性回归模型
lin_reg = LinearRegression()
lin_reg.fit(X, y_noisy)
y_pred = lin_reg.predict(X)
# 计算残差
residuals = y_noisy - y_pred
# 绘图
plt.figure(figsize=(16, 12))
# 1. 原始数据及拟合线
plt.subplot(2, 2, 1)
plt.scatter(X, y_noisy, color='blue', label='Noisy Data')
plt.plot(X, y_pred, color='red', label='Fitted Line', linewidth=2)
plt.title('Scatter Plot with Fitted Line', fontsize=14)
plt.xlabel('X', fontsize=12)
plt.ylabel('y', fontsize=12)
plt.legend()
# 2. 残差图
plt.subplot(2, 2, 2)
plt.scatter(X, residuals, color='green')
plt.axhline(y=0, color='red', linestyle='--')
plt.title('Residuals Plot', fontsize=14)
plt.xlabel('X', fontsize=12)
plt.ylabel('Residuals', fontsize=12)
# 3. 真实值 vs 预测值
plt.subplot(2, 2, 3)
plt.scatter(y_noisy, y_pred, color='purple')
plt.plot([min(y_noisy), max(y_noisy)], [min(y_noisy), max(y_noisy)], color='red', linestyle='--', linewidth=2)
plt.title('True vs Predicted Values', fontsize=14)
plt.xlabel('True Values', fontsize=12)
plt.ylabel('Predicted Values', fontsize=12)
# 4. 噪声分布
plt.subplot(2, 2, 4)
sns.histplot(noise, kde=True, color='orange')
plt.title('Noise Distribution', fontsize=14)
plt.xlabel('Noise', fontsize=12)
plt.ylabel('Frequency', fontsize=12)
plt.tight_layout()
plt.show()
数据生成:使用线性模型 生成虚拟数据,并注入高斯噪声。 线性回归模型:使用 sklearn
的LinearRegression
模型拟合数据,并预测目标值。
散点图展示了数据点及拟合的线性回归模型。 残差图分析了预测值与实际值的偏差,检查模型的拟合效果。 真实值 vs 预测值图用于比较模型预测的准确性。 噪声分布图分析了噪声的分布,确认噪声是否符合正态分布。
6. 数据合成 (Synthetic Data Generation)
数据合成通过学习数据的分布,尤其适合生成无法直接获取的敏感数据或具有隐私限制的数据。
原理
数据合成可以使用多种方法,例如高斯混合模型 (Gaussian Mixture Models, GMM) 或其他生成模型,从数据分布中进行采样。
核心公式
假设数据 来自多个潜在的高斯分布,那么其概率密度为:
其中:
是混合权重,满足 是第 个高斯分布的概率密度函数,均值为 ,协方差矩阵为
推导
1. 高斯混合模型的参数估计:
使用 EM (期望最大化) 算法估计 GMM 模型的参数:
E 步:计算每个样本属于各个高斯分布的概率 M 步:更新均值、协方差和混合权重
2. 生成数据:
通过 GMM 模型,从每个高斯分布中采样生成新样本。首先从混合权重 中选择一个高斯分布,然后从该分布中生成新数据。
Python案例
生成一个模拟的信用卡交易数据集,包括交易金额、交易时间、卡号、商户、交易成功与否等信息。使用多种可视化工具来进行数据分析,包括散点图、直方图和箱线图,帮助我们理解数据的分布、变化及其关系。
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
# 设置颜色风格
sns.set(style="whitegrid", palette="muted", color_codes=True)
# 生成虚拟数据集
np.random.seed(42)
n_samples = 1000
transaction_time = np.random.randint(0, 24, size=n_samples) # 交易时间:0-23小时
transaction_amount = np.random.exponential(scale=50, size=n_samples) # 交易金额:指数分布
merchant_id = np.random.choice(['Merchant_A', 'Merchant_B', 'Merchant_C', 'Merchant_D'], size=n_samples)
transaction_success = np.random.choice([0, 1], size=n_samples, p=[0.1, 0.9]) # 90% 交易成功
# 创建DataFrame
df = pd.DataFrame({
'Transaction Time': transaction_time,
'Transaction Amount': transaction_amount,
'Merchant': merchant_id,
'Success': transaction_success
})
# 创建图像
fig, axes = plt.subplots(2, 2, figsize=(14, 10), constrained_layout=True)
# 图1: 交易时间与交易金额的散点图
sns.scatterplot(x='Transaction Time', y='Transaction Amount', hue='Success', data=df, ax=axes[0, 0], palette='bright')
axes[0, 0].set_title('Scatter Plot of Transaction Time vs Amount')
# 图2: 交易金额的直方图
sns.histplot(df['Transaction Amount'], bins=30, kde=True, color='orange', ax=axes[0, 1])
axes[0, 1].set_title('Histogram of Transaction Amount')
# 图3: 每个商户的交易金额箱线图
sns.boxplot(x='Merchant', y='Transaction Amount', data=df, ax=axes[1, 0], palette='Set2')
axes[1, 0].set_title('Boxplot of Transaction Amount by Merchant')
# 图4: 成功与失败交易的金额分布
sns.violinplot(x='Success', y='Transaction Amount', data=df, ax=axes[1, 1], palette='muted')
axes[1, 1].set_title('Violin Plot of Transaction Amount by Success')
# 显示图像
plt.show()
散点图(图1):展示了交易金额和交易时间之间的关系,带有不同的颜色区分交易是否成功。这样可以帮助我们查看交易是否有时间相关性,以及失败的交易是否集中在某些金额区间。
直方图(图2):用于查看交易金额的总体分布。通过直方图,可以直观地看出交易金额的集中程度以及是否有极端值。
箱线图(图3):展示了不同商户的交易金额分布,包括中位数、四分位范围以及可能的异常值。这有助于比较不同商户之间的交易金额差异。
小提琴图(图4):结合了箱线图和核密度估计,展示了成功和失败交易的金额分布情况。这有助于深入了解交易金额的分布形态,特别是成功和失败交易之间的差异。
7. Bootstrap 重采样
Bootstrap 是一种从原始数据集中通过有放回采样生成新的数据集的方法,常用于估计模型的偏差和方差。
原理
从原始数据集中有放回采样生成多个子数据集,每个子数据集的大小与原始数据集相同。通过这种方法生成的数据集可以用于模型训练或评估。
核心公式
设数据集 ,从中有放回采样 个子集 ,其中每个子集 包含 个样本:
每次采样时,数据集中的样本可以被重复选择。
推导
Bootstrap 的推导基于有放回采样的思想。假设原始数据集中有 个样本,通过有放回采样生成新数据集,每次采样从原始数据集中随机选取一个样本,可以重复选择相同的样本。
Python案例
假设我们有一组表示某商品销量的虚拟数据,我们希望通过 Bootstrap 重采样来估计销量的均值分布,并构建 95% 置信区间。
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
# 设置随机种子以保证可重复性
np.random.seed(42)
# 生成虚拟数据集,假设为商品销量(正态分布,均值为50,标准差为10,样本量为1000)
data = np.random.normal(loc=50, scale=10, size=1000)
# Bootstrap 重采样的函数
def bootstrap(data, n_iterations=1000, sample_size=None):
if sample_size is None:
sample_size = len(data)
bootstrap_means = []
for _ in range(n_iterations):
sample = np.random.choice(data, size=sample_size, replace=True)
bootstrap_means.append(np.mean(sample))
return np.array(bootstrap_means)
# 执行 Bootstrap 重采样
n_iterations = 2000
bootstrap_means = bootstrap(data, n_iterations=n_iterations)
# 计算95%的置信区间
ci_lower = np.percentile(bootstrap_means, 2.5)
ci_upper = np.percentile(bootstrap_means, 97.5)
mean_estimate = np.mean(bootstrap_means)
# 设置图形大小和样式
plt.figure(figsize=(14, 10))
# 子图 1: 原始数据的分布
plt.subplot(2, 2, 1)
sns.histplot(data, bins=30, kde=True, color='dodgerblue', edgecolor='black', alpha=0.7)
plt.title('Original Data Distribution (Sales)', fontsize=14, fontweight='bold')
plt.xlabel('Sales')
plt.ylabel('Frequency')
# 子图 2: Bootstrap 重采样均值的分布
plt.subplot(2, 2, 2)
sns.histplot(bootstrap_means, bins=30, kde=True, color='orange', edgecolor='black', alpha=0.7)
plt.axvline(mean_estimate, color='red', linestyle='--', linewidth=2, label=f'Mean: {mean_estimate:.2f}')
plt.axvline(ci_lower, color='green', linestyle='--', linewidth=2, label=f'95% CI Lower: {ci_lower:.2f}')
plt.axvline(ci_upper, color='green', linestyle='--', linewidth=2, label=f'95% CI Upper: {ci_upper:.2f}')
plt.title('Bootstrap Sample Mean Distribution', fontsize=14, fontweight='bold')
plt.xlabel('Mean Sales')
plt.ylabel('Frequency')
plt.legend()
# 子图 3: Bootstrap 均值及其置信区间的折线图
plt.subplot(2, 1, 2)
x_vals = np.arange(1, len(bootstrap_means) + 1)
plt.plot(x_vals, sorted(bootstrap_means), color='purple', label='Bootstrap Means')
plt.fill_between(x_vals, ci_lower, ci_upper, color='lightgreen', alpha=0.5, label=f'95% CI: [{ci_lower:.2f}, {ci_upper:.2f}]')
plt.axhline(mean_estimate, color='red', linestyle='--', label=f'Mean Estimate: {mean_estimate:.2f}')
plt.title('Bootstrap Means with Confidence Interval', fontsize=14, fontweight='bold')
plt.xlabel('Iteration')
plt.ylabel('Mean Sales')
plt.legend()
# 调整子图布局
plt.tight_layout()
plt.show()
数据生成:
np.random.normal()
:我们生成一个正态分布的数据集,表示商品的销量,均值为50,标准差为10,样本量为1000。Bootstrap 重采样:
bootstrap()
:通过随机重采样多次计算数据集的均值。通过np.random.choice()
函数,每次有放回地抽取样本进行重采样。ci_lower
和ci_upper
:通过np.percentile()
计算95%置信区间的上下界。
子图1:绘制原始数据的直方图,展示销量分布。 子图2:绘制 Bootstrap 重采样后的均值分布。通过直方图和核密度估计(KDE)展示重采样均值的分布,并标记置信区间和均值。 子图3:绘制 Bootstrap 均值随迭代次数的变化,并通过阴影区域展示95%置信区间。
8. 时间序列生成技术
对于时间序列数据,常使用AR(自回归)、MA(移动平均)、ARIMA(自回归积分移动平均)、LSTM等模型生成新的时间序列数据。
原理
时间序列生成的核心是利用历史数据预测未来数据。经典方法包括自回归模型 (AR) 和移动平均模型 (MA),而 LSTM 等深度学习方法也被广泛应用于复杂时间序列生成。
核心公式
自回归模型 (AR):
其中 是当前时间点的值, 是模型参数, 是误差项。
移动平均模型 (MA):
其中 是误差项, 是模型参数。
推导
1. 自回归模型:
AR 模型假设当前时间点的值 是过去 个时间点的线性组合加上误差项。通过最小化误差的平方和,可以使用普通最小二乘法估计参数 。
2. 移动平均模型:
MA 模型假设当前值是过去误差的线性组合。通过估计历史误差项,可以构建出未来的预测值。
Python案例
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from statsmodels.graphics.tsaplots import plot_acf
# 设置随机种子,便于复现
np.random.seed(42)
# 生成虚拟时间序列数据(模拟ARIMA模型生成的时间序列)
n = 500
time = np.arange(n)
# 设置初始值和一些噪音
initial_value = 50
noise = np.random.normal(0, 1, n)
# 模拟一个简单的随机游走过程
data = np.cumsum(noise) + initial_value
# 创建一个DataFrame来存储时间和生成的数据
df = pd.DataFrame({'Time': time, 'Value': data})
# 开始绘制图形
plt.figure(figsize=(12, 10))
# 子图1:时间序列折线图
plt.subplot(3, 1, 1)
plt.plot(df['Time'], df['Value'], color='blue', label='Time Series', linewidth=2)
plt.title('Generated Time Series')
plt.xlabel('Time')
plt.ylabel('Value')
plt.grid(True)
plt.legend()
# 子图2:时间序列的自相关图
plt.subplot(3, 1, 2)
plot_acf(df['Value'], lags=40, ax=plt.gca())
plt.title('Autocorrelation Function (ACF)')
plt.grid(True)
# 子图3:时间序列的直方图
plt.subplot(3, 1, 3)
sns.histplot(df['Value'], bins=30, kde=True, color='orange')
plt.title('Histogram of Time Series Values')
plt.xlabel('Value')
plt.ylabel('Frequency')
plt.grid(True)
# 调整子图布局
plt.tight_layout()
# 显示图像
plt.show()
时间序列折线图:展示了生成的时间序列数据的趋势,可以明显看到随着时间推移的波动情况。 自相关图:通过观察滞后值的相关性,判断时间序列是否平稳,滞后效应可以帮助构建更复杂的预测模型。 直方图:帮助分析生成数据的分布是否呈现出正态分布,或有偏斜等特征,有助于更深入理解数据的统计特性。
9. 回归树模拟 (Regression Tree Simulation)
回归树模拟是一种基于决策树的生成技术,通常用于回归任务中的数据生成。它通过决策树模型学习数据的分段关系,并利用这些关系生成新的数据点。
原理
回归树模拟通过递归地划分特征空间,构建一个树形结构,每个叶节点对应于特定的预测值。回归树的目标是通过最小化误差来找到最佳的分裂点和分裂方式。
在训练完成后,回归树可以通过在各个分支中生成新的样本来模拟数据分布。新数据点可以根据训练树的分裂规则进行采样,以模拟原始数据的分布。
核心公式
回归树的目标是最小化节点中的均方误差 (MSE),定义为:
其中:
表示节点 中的数据点数量 表示第 个数据点的真实值 表示节点 中所有数据点的均值
推导
1. 分裂准则:
在每个节点,回归树通过寻找最佳分裂点来最小化误差。分裂准则可以是均方误差 (MSE),即选择能够使 MSE 最小的特征和分裂点:
其中 和 分别是左右分支的样本数, 和 分别是左右子节点的均方误差。
2. 生成新数据:
训练好的回归树可以用于生成新数据。通过随机选择特征值并根据决策树的分支规则进行采样,新的样本将遵循与原始数据相似的分布。
3. 递归生成:
树的叶节点对应于局部数据的平均值或预测值,因此新的样本可以在叶节点生成。根据节点的结构,每个新样本的特征值会依赖于其通过的树路径。
Python案例
import numpy as np
import matplotlib.pyplot as plt
from sklearn.tree import DecisionTreeRegressor, plot_tree
from sklearn.datasets import make_regression
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error
import seaborn as sns
# 生成虚拟数据集
np.random.seed(42)
X, y = make_regression(n_samples=1000, n_features=4, noise=0.1, random_state=42)
# 分割数据集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
# 创建并训练回归树模型
tree_model = DecisionTreeRegressor(max_depth=4, random_state=42)
tree_model.fit(X_train, y_train)
# 预测
y_pred = tree_model.predict(X_test)
# 计算均方误差
mse = mean_squared_error(y_test, y_pred)
print(f"Mean Squared Error: {mse:.2f}")
# 创建绘图
fig, ax = plt.subplots(2, 2, figsize=(16, 12), constrained_layout=True)
# 1. 可视化回归树结构
ax[0, 0].set_title('Regression Tree Structure', fontsize=14, color='darkblue')
plot_tree(tree_model, filled=True, feature_names=[f'Feature {i}' for i in range(1, 5)], ax=ax[0, 0], impurity=False)
# 2. 特征重要性可视化
feature_importances = tree_model.feature_importances_
ax[0, 1].set_title('Feature Importance', fontsize=14, color='darkgreen')
sns.barplot(x=[f'Feature {i}' for i in range(1, 5)], y=feature_importances, palette='bright', ax=ax[0, 1])
ax[0, 1].set_ylabel('Importance')
# 3. 真实值与预测值对比散点图
ax[1, 0].set_title('True vs Predicted Values', fontsize=14, color='darkred')
sns.scatterplot(x=y_test, y=y_pred, ax=ax[1, 0], color='orange', s=60)
ax[1, 0].set_xlabel('True Values')
ax[1, 0].set_ylabel('Predicted Values')
ax[1, 0].plot([min(y_test), max(y_test)], [min(y_test), max(y_test)], color='red', lw=2) # 参考线
# 4. 残差分析
residuals = y_test - y_pred
ax[1, 1].set_title('Residuals Plot', fontsize=14, color='purple')
sns.histplot(residuals, bins=20, kde=True, color='purple', ax=ax[1, 1])
ax[1, 1].set_xlabel('Residuals')
ax[1, 1].set_ylabel('Frequency')
# 展示图像
plt.suptitle('Regression Tree Analysis with Multiple Visualizations', fontsize=16, color='black')
plt.show()
数据生成与分割:我们使用 make_regression
函数生成了一个虚拟的回归数据集,包含200个样本和4个特征。我们将数据集分为训练集和测试集。回归树模型训练:我们使用 DecisionTreeRegressor
进行回归建模,并设置max_depth=4
限制树的深度,避免过拟合。
回归树结构图:使用 plot_tree
展示回归树的结构,直观了解如何通过决策过程对数据进行分割。特征重要性图:通过 feature_importances_
属性提取特征的重要性,并使用seaborn.barplot
绘制条形图。真实值与预测值对比图:使用 seaborn.scatterplot
绘制散点图,显示模型的拟合效果,并添加参考线。残差分析:通过 seaborn.histplot
绘制残差分布图,并使用KDE平滑显示残差分布的形态,评估模型是否存在系统性偏差。
10. 蒙特卡洛采样 (Monte Carlo Sampling)
蒙特卡洛采样是一种基于随机化的数值方法,广泛应用于数值积分、优化和生成数据等任务。通过对随机变量进行大量采样来估计目标分布,可以生成符合目标分布的新数据。
原理
蒙特卡洛方法的核心思想是通过对复杂分布的大量随机采样来逼近其期望值。假设目标分布 难以直接从解析表达式中获取,则可以通过随机生成样本来估计该分布。
蒙特卡洛采样可以用于从各种复杂的分布中生成数据,例如高维空间中的随机变量。在机器学习中,常用于从模型的预测分布中生成数据,或者用来估计期望和方差等统计量。
核心公式
蒙特卡洛估计的公式为:
其中:
是目标函数 是从分布 中随机采样的点 是采样次数
推导
1. 随机采样:
蒙特卡洛方法通过从分布 中采样得到 ,并利用这些样本来近似计算期望值。若直接从目标分布 采样较难,可以使用重要性采样等变种方法来进行采样。
2. 重要性采样:
当无法直接从目标分布 采样时,可以从一个易于采样的分布 中采样,并通过加权修正采样值:
其中, 是重要性权重,用于修正从 中采样的偏差。
3. 生成数据:
蒙特卡洛采样广泛应用于数据生成任务。通过从模型的预测分布中大量采样,可以生成符合模型假设的新数据。具体应用中,蒙特卡洛方法常用于模拟复杂系统(如物理模拟、金融模型等),并通过采样的方式生成大量数据来分析系统的行为。
4. 方差收敛:
蒙特卡洛方法的估计误差随采样次数 增加而减少,误差的期望为 ,即通过增加采样数量可以有效降低估计方差。
Python案例
假设有一个金融模型,涉及两个随机变量:资产收益率和波动率。我们使用蒙特卡洛采样生成资产收益率和波动率的虚拟数据,并分析其分布特性和相互关系。
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
# 设置随机种子
np.random.seed(42)
# 蒙特卡洛采样 - 假设收益率服从正态分布,波动率服从对数正态分布
n_samples = 10000
mean_return = 0.05
std_return = 0.1
mean_volatility = 0.2
std_volatility = 0.05
# 生成资产收益率(正态分布)和波动率(对数正态分布)的虚拟数据
returns = np.random.normal(loc=mean_return, scale=std_return, size=n_samples)
volatility = np.random.lognormal(mean=np.log(mean_volatility), sigma=std_volatility, size=n_samples)
# 创建图形
plt.figure(figsize=(12, 8))
# 1. 资产收益率的直方图
plt.subplot(2, 2, 1)
sns.histplot(returns, bins=50, color="blue", kde=True, alpha=0.7)
plt.title("Asset Returns Distribution")
plt.xlabel("Returns")
plt.ylabel("Frequency")
# 2. 波动率的直方图
plt.subplot(2, 2, 2)
sns.histplot(volatility, bins=50, color="green", kde=True, alpha=0.7)
plt.title("Volatility Distribution")
plt.xlabel("Volatility")
plt.ylabel("Frequency")
# 3. 收益率与波动率的散点图
plt.subplot(2, 2, 3)
plt.scatter(returns, volatility, color='red', alpha=0.3)
plt.title("Scatter Plot: Returns vs Volatility")
plt.xlabel("Returns")
plt.ylabel("Volatility")
# 4. 收益率与波动率的联合密度图
plt.subplot(2, 2, 4)
sns.kdeplot(x=returns, y=volatility, cmap="magma", fill=True, thresh=0.05)
plt.title("Joint Density Plot: Returns vs Volatility")
plt.xlabel("Returns")
plt.ylabel("Volatility")
# 调整布局,避免重叠
plt.tight_layout()
plt.show()
资产收益率的直方图:蓝色的直方图展示了资产收益率的频率分布,结合核密度估计线可以看到其为正态分布的形状。 波动率的直方图:绿色的直方图展示了波动率的分布,它符合对数正态分布,且右侧尾部稍长。 收益率与波动率的散点图:红色的散点图展示了两者的相互关系,可以观察到收益率与波动率之间是否有相关性(比如,波动率高时收益率较低)。 收益率与波动率的联合密度图:通过密度估计图展示了收益率和波动率的联合分布情况,颜色深浅反映了样本的密度。
最后