深度学习基础 - 生成对抗网络(GAN)通俗详解
·
-
🧠 什么是生成对抗网络?用生活案例来理解
基本概念比喻
想象一下艺术品伪造者 vs 鉴定专家的故事:
伪造者(Generator):学习制作逼真的假画
-
目标:制作让鉴定专家分不清真假的赝品
🎯 GAN就像"猫鼠游戏"
-
循环往复,两者共同进步!
对抗过程:
伪造者技术提升 → 赝品更逼真
鉴定专家经验增长 → 识别能力更强
鉴定专家(Discriminator):学习识别真假艺术品
目标:准确判断画作是真迹还是赝品
# 极简版GAN思考过程
def simple_gan_analogy():
# 初始状态
forger_skill = 1 # 伪造者技能
expert_skill = 1 # 专家技能
for round in range(10):
print(f"\n第{round+1}轮:")
# 伪造者制作赝品
fake_quality = forger_skill * 0.8
print(f"伪造者制作了质量 {fake_quality:.2f} 的赝品")
# 专家鉴定
if expert_skill > fake_quality:
print("专家识破了赝品!")
# 伪造者从失败中学习
forger_skill += 0.2
else:
print("专家被赝品骗过了!")
# 专家从失败中学习
expert_skill += 0.2
print(f"\n最终结果: 伪造者技能 {forger_skill:.2f}, 专家技能 {expert_skill:.2f}")
simple_gan_analogy()
🔍 GAN核心概念详解
1. 生成器(Generator)- 像"天才伪造者"
import numpy as np
import matplotlib.pyplot as plt
class SimpleGenerator:
"""极简生成器演示"""
def __init__(self):
self.skills = {
'线条技巧': 0.5,
'色彩感觉': 0.3,
'风格模仿': 0.4
}
def generate_image(self, noise):
"""从噪声生成图像"""
# 噪声就像随机的灵感
quality = np.mean(list(self.skills.values())) + noise * 0.1
return np.clip(quality, 0, 1)
def learn_from_feedback(self, was_detected):
"""根据反馈学习"""
if was_detected:
# 如果被识破,提升技能
for skill in self.skills:
self.skills[skill] += 0.1
print("生成器: 被识破了,我要改进技术!")
else:
print("生成器: 成功骗过判别器!")
class SimpleDiscriminator:
"""极简判别器演示"""
def __init__(self):
self.experience = {
'细节观察': 0.6,
'风格分析': 0.4,
'材质识别': 0.5
}
def discriminate(self, image_quality, is_real):
"""判断图像真伪"""
skill_level = np.mean(list(self.experience.values()))
if is_real:
# 真实图像应该被识别为真
correct = skill_level > 0.5
else:
# 生成图像应该被识别为假
correct = skill_level > image_quality
return correct
def learn_from_mistake(self, made_mistake):
"""从错误中学习"""
if made_mistake:
for skill in self.experience:
self.skill[skill] += 0.1
print("判别器: 判断错误了,我要积累经验!")
# 演示GAN的基本对抗过程
def demonstrate_gan_dynamics():
generator = SimpleGenerator()
discriminator = SimpleDiscriminator()
real_image_quality = 0.9 # 真实图像质量
print("GAN训练过程演示:")
print("=" * 40)
for epoch in range(5):
print(f"\n--- 第{epoch+1}轮训练 ---")
# 生成器生成图像
noise = np.random.normal(0, 0.1)
fake_image_quality = generator.generate_image(noise)
# 判别器判断
is_real = False
discriminator_decision = discriminator.discriminate(fake_image_quality, is_real)
# 学习过程
if discriminator_decision:
print(f"判别器识破了生成器! (生成质量: {fake_image_quality:.3f})")
generator.learn_from_feedback(True)
else:
print(f"生成器骗过了判别器! (生成质量: {fake_image_quality:.3f})")
discriminator.learn_from_mistake(True)
demonstrate_gan_dynamics()
2. 对抗训练 - 像"博弈进化"
def visualize_gan_training():
"""可视化GAN的训练动态"""
epochs = 20
g_losses = []
d_losses = []
g_skills = []
d_skills = []
# 模拟训练过程
g_skill = 0.3
d_skill = 0.4
for epoch in range(epochs):
# 生成器进步
g_improvement = 0.1 if d_skill > g_skill else 0.05
g_skill = min(g_skill + g_improvement, 0.95)
# 判别器进步
d_improvement = 0.08 if g_skill > d_skill else 0.03
d_skill = min(d_skill + d_improvement, 0.95)
# 计算损失(简化版)
g_loss = max(0, d_skill - g_skill)
d_loss = max(0, g_skill - d_skill)
g_losses.append(g_loss)
d_losses.append(d_loss)
g_skills.append(g_skill)
d_skills.append(d_skill)
# 绘制训练过程
plt.figure(figsize=(15, 10))
# 技能进步图
plt.subplot(2, 2, 1)
plt.plot(g_skills, label='生成器技能', linewidth=2, marker='o')
plt.plot(d_skills, label='判别器技能', linewidth=2, marker='s')
plt.xlabel('训练轮次')
plt.ylabel('技能水平')
plt.title('生成器和判别器的技能进步')
plt.legend()
plt.grid(True, alpha=0.3)
# 损失变化图
plt.subplot(2, 2, 2)
plt.plot(g_losses, label='生成器损失', linewidth=2, marker='o')
plt.plot(d_losses, label='判别器损失', linewidth=2, marker='s')
plt.xlabel('训练轮次')
plt.ylabel('损失值')
plt.title('生成器和判别器的损失变化')
plt.legend()
plt.grid(True, alpha=0.3)
# 对抗平衡图
plt.subplot(2, 2, 3)
balance = np.array(g_skills) - np.array(d_skills)
plt.plot(balance, linewidth=2, color='purple')
plt.axhline(y=0, color='red', linestyle='--', alpha=0.5)
plt.xlabel('训练轮次')
plt.ylabel('技能差距 (生成器 - 判别器)')
plt.title('对抗平衡过程')
plt.grid(True, alpha=0.3)
# 生成质量模拟
plt.subplot(2, 2, 4)
quality_scores = [min(g_skill * 1.2, 1.0) for g_skill in g_skills]
plt.plot(quality_scores, linewidth=2, color='green', marker='o')
plt.xlabel('训练轮次')
plt.ylabel('生成质量')
plt.title('生成图像质量提升')
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
visualize_gan_training()
🚀 完整的可运行实例:手写数字生成
import tensorflow as tf
from tensorflow.keras import layers, models
import numpy as np
import matplotlib.pyplot as plt
import time
import os
print("🚀 开始生成对抗网络实战:手写数字生成")
print("=" * 50)
# 设置随机种子
np.random.seed(42)
tf.random.set_seed(42)
# 创建保存生成图像的文件夹
os.makedirs('gan_images', exist_ok=True)
# 1. 加载和预处理数据
print("\n1. 📊 加载MNIST手写数字数据集...")
(train_images, train_labels), (_, _) = tf.keras.datasets.mnist.load_data()
# 数据预处理
def preprocess_data(images):
"""预处理图像数据"""
# 归一化到 [-1, 1] 范围,这对GAN训练更好
images = images.reshape(-1, 28, 28, 1).astype('float32')
images = (images - 127.5) / 127.5 # 从[0,255]归一化到[-1,1]
return images
train_images = preprocess_data(train_images)
print(f"训练图像形状: {train_images.shape}")
print(f"像素值范围: [{np.min(train_images):.3f}, {np.max(train_images):.3f}]")
# 2. 可视化原始数据
print("\n2. 👀 可视化原始手写数字...")
plt.figure(figsize=(10, 5))
for i in range(10):
plt.subplot(2, 5, i + 1)
plt.imshow(train_images[i].reshape(28, 28) * 0.5 + 0.5, cmap='gray') # 反归一化显示
plt.title(f'真实数字')
plt.axis('off')
plt.suptitle('MNIST真实手写数字样本', fontsize=16)
plt.tight_layout()
plt.show()
# 3. 构建生成器(Generator)
print("\n3. 🎨 构建生成器模型...")
def build_generator(latent_dim=100):
"""构建生成器模型"""
model = models.Sequential([
# 第一层:将随机噪声转换为更丰富的特征
layers.Dense(7 * 7 * 256, use_bias=False, input_shape=(latent_dim,)),
layers.BatchNormalization(),
layers.LeakyReLU(alpha=0.2),
# 重塑为 7x7x256 的特征图
layers.Reshape((7, 7, 256)),
# 第一次上采样:7x7 -> 14x14
layers.Conv2DTranspose(128, (5, 5), strides=(1, 1), padding='same', use_bias=False),
layers.BatchNormalization(),
layers.LeakyReLU(alpha=0.2),
# 第二次上采样:14x14 -> 28x28
layers.Conv2DTranspose(64, (5, 5), strides=(2, 2), padding='same', use_bias=False),
layers.BatchNormalization(),
layers.LeakyReLU(alpha=0.2),
# 最终输出层:生成28x28x1的图像
layers.Conv2DTranspose(1, (5, 5), strides=(2, 2), padding='same',
use_bias=False, activation='tanh')
])
return model
# 创建生成器
generator = build_generator()
print("生成器模型结构:")
generator.summary()
# 测试生成器(未经训练)
print("\n测试未经训练的生成器...")
test_noise = tf.random.normal([1, 100])
test_generated_image = generator(test_noise, training=False)
plt.imshow(test_generated_image[0, :, :, 0] * 0.5 + 0.5, cmap='gray')
plt.title('未经训练的生成器输出')
plt.axis('off')
plt.show()
# 4. 构建判别器(Discriminator)
print("\n4. 🔍 构建判别器模型...")
def build_discriminator():
"""构建判别器模型"""
model = models.Sequential([
# 第一层:输入28x28x1的图像
layers.Conv2D(64, (5, 5), strides=(2, 2), padding='same',
input_shape=(28, 28, 1)),
layers.LeakyReLU(alpha=0.2),
layers.Dropout(0.3),
# 第二层:下采样
layers.Conv2D(128, (5, 5), strides=(2, 2), padding='same'),
layers.LeakyReLU(alpha=0.2),
layers.Dropout(0.3),
# 第三层:进一步提取特征
layers.Conv2D(256, (5, 5), strides=(1, 1), padding='same'),
layers.LeakyReLU(alpha=0.2),
layers.Dropout(0.3),
# 展平并输出判断结果
layers.Flatten(),
layers.Dense(1, activation='sigmoid') # 输出0-1的概率值
])
return model
# 创建判别器
discriminator = build_discriminator()
print("判别器模型结构:")
discriminator.summary()
# 5. 定义损失函数和优化器
print("\n5. ⚙️ 定义损失函数和优化器...")
# 交叉熵损失函数
cross_entropy = tf.keras.losses.BinaryCrossentropy()
def discriminator_loss(real_output, fake_output):
"""判别器损失函数"""
# 真实图像应该被判断为1,生成图像应该被判断为0
real_loss = cross_entropy(tf.ones_like(real_output), real_output)
fake_loss = cross_entropy(tf.zeros_like(fake_output), fake_output)
total_loss = real_loss + fake_loss
return total_loss
def generator_loss(fake_output):
"""生成器损失函数"""
# 生成器希望生成的图像被判断为真实(1)
return cross_entropy(tf.ones_like(fake_output), fake_output)
# 优化器
generator_optimizer = tf.keras.optimizers.Adam(1e-4)
discriminator_optimizer = tf.keras.optimizers.Adam(1e-4)
# 6. 定义训练步骤
print("\n6. 🔄 定义训练过程...")
@tf.function
def train_step(images, batch_size, latent_dim):
"""单个训练步骤"""
# 生成随机噪声
noise = tf.random.normal([batch_size, latent_dim])
with tf.GradientTape() as gen_tape, tf.GradientTape() as disc_tape:
# 生成器生成图像
generated_images = generator(noise, training=True)
# 判别器判断真实图像和生成图像
real_output = discriminator(images, training=True)
fake_output = discriminator(generated_images, training=True)
# 计算损失
gen_loss = generator_loss(fake_output)
disc_loss = discriminator_loss(real_output, fake_output)
# 计算梯度并更新生成器
gradients_of_generator = gen_tape.gradient(gen_loss, generator.trainable_variables)
generator_optimizer.apply_gradients(zip(gradients_of_generator, generator.trainable_variables))
# 计算梯度并更新判别器
gradients_of_discriminator = disc_tape.gradient(disc_loss, discriminator.trainable_variables)
discriminator_optimizer.apply_gradients(zip(gradients_of_discriminator, discriminator.trainable_variables))
return gen_loss, disc_loss
# 7. 定义监控和可视化函数
print("\n7. 📊 定义训练监控函数...")
def generate_and_save_images(model, epoch, test_input, save_dir='gan_images'):
"""生成并保存图像"""
# 生成图像
predictions = model(test_input, training=False)
# 绘制图像
fig = plt.figure(figsize=(10, 10))
for i in range(predictions.shape[0]):
plt.subplot(4, 4, i + 1)
plt.imshow(predictions[i, :, :, 0] * 0.5 + 0.5, cmap='gray')
plt.axis('off')
plt.suptitle(f'训练轮次: {epoch + 1}', fontsize=16)
plt.tight_layout()
# 保存图像
plt.savefig(f'{save_dir}/image_at_epoch_{epoch + 1:04d}.png')
plt.close()
def plot_training_history(g_losses, d_losses):
"""绘制训练历史"""
plt.figure(figsize=(12, 4))
plt.subplot(1, 2, 1)
plt.plot(g_losses, label='生成器损失', alpha=0.7)
plt.plot(d_losses, label='判别器损失', alpha=0.7)
plt.xlabel('训练批次')
plt.ylabel('损失值')
plt.title('生成器和判别器损失')
plt.legend()
plt.grid(True, alpha=0.3)
plt.subplot(1, 2, 2)
# 计算移动平均以平滑曲线
window = min(50, len(g_losses) // 10)
if window > 1:
g_smooth = np.convolve(g_losses, np.ones(window)/window, mode='valid')
d_smooth = np.convolve(d_losses, np.ones(window)/window, mode='valid')
plt.plot(g_smooth, label='生成器损失(平滑)', linewidth=2)
plt.plot(d_smooth, label='判别器损失(平滑)', linewidth=2)
plt.xlabel('训练批次')
plt.ylabel('损失值')
plt.title('损失趋势(平滑后)')
plt.legend()
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
# 8. 训练GAN模型
print("\n8. 🏃 开始训练GAN模型...")
# 训练参数
EPOCHS = 50
BATCH_SIZE = 256
LATENT_DIM = 100
NUM_EXAMPLES_TO_GENERATE = 16
# 准备数据集
train_dataset = tf.data.Dataset.from_tensor_slices(train_images).shuffle(60000).batch(BATCH_SIZE)
# 固定噪声用于生成示例图像
seed = tf.random.normal([NUM_EXAMPLES_TO_GENERATE, LATENT_DIM])
# 训练记录
generator_losses = []
discriminator_losses = []
print(f"开始训练,总共 {EPOCHS} 轮,每轮 {len(train_images) // BATCH_SIZE} 批次")
# 训练循环
for epoch in range(EPOCHS):
start_time = time.time()
epoch_gen_loss = []
epoch_disc_loss = []
# 遍历每个批次
for image_batch in train_dataset:
gen_loss, disc_loss = train_step(image_batch, BATCH_SIZE, LATENT_DIM)
epoch_gen_loss.append(gen_loss)
epoch_disc_loss.append(disc_loss)
# 记录损失
avg_gen_loss = np.mean(epoch_gen_loss)
avg_disc_loss = np.mean(epoch_disc_loss)
generator_losses.append(avg_gen_loss)
discriminator_losses.append(avg_disc_loss)
# 每5轮生成一次示例图像
if (epoch + 1) % 5 == 0:
generate_and_save_images(generator, epoch, seed)
# 打印进度
if (epoch + 1) % 10 == 0 or epoch == 0:
elapsed_time = time.time() - start_time
print(f'轮次 {epoch + 1}/{EPOCHS}, '
f'生成器损失: {avg_gen_loss:.4f}, '
f'判别器损失: {avg_disc_loss:.4f}, '
f'时间: {elapsed_time:.2f}秒')
# 显示当前生成的图像
generate_and_save_images(generator, epoch, seed)
# 显示图像
predictions = generator(seed, training=False)
plt.figure(figsize=(10, 10))
for i in range(min(9, predictions.shape[0])):
plt.subplot(3, 3, i + 1)
plt.imshow(predictions[i, :, :, 0] * 0.5 + 0.5, cmap='gray')
plt.axis('off')
plt.suptitle(f'训练轮次: {epoch + 1}', fontsize=16)
plt.tight_layout()
plt.show()
print("训练完成!")
# 9. 可视化训练过程
print("\n9. 📈 分析训练结果...")
# 绘制训练历史
plot_training_history(generator_losses, discriminator_losses)
# 10. 生成最终结果
print("\n10. 🎨 生成最终手写数字...")
# 生成新的数字
final_noise = tf.random.normal([25, LATENT_DIM])
generated_images = generator(final_noise, training=False)
# 显示生成的数字
plt.figure(figsize=(10, 10))
for i in range(25):
plt.subplot(5, 5, i + 1)
plt.imshow(generated_images[i, :, :, 0] * 0.5 + 0.5, cmap='gray')
plt.axis('off')
plt.suptitle('GAN生成的手写数字', fontsize=16)
plt.tight_layout()
plt.show()
# 11. 对比真实图像和生成图像
print("\n11. 🔄 对比真实图像和生成图像...")
# 选择一些真实图像
real_sample_indices = np.random.choice(len(train_images), 16, replace=False)
real_samples = train_images[real_sample_indices]
# 生成对应数量的假图像
fake_samples = generator(tf.random.normal([16, LATENT_DIM]), training=False)
# 对比显示
fig, axes = plt.subplots(4, 8, figsize=(16, 8))
for i in range(8):
# 显示真实图像
axes[0, i].imshow(real_samples[i].reshape(28, 28) * 0.5 + 0.5, cmap='gray')
axes[0, i].set_title('真实图像')
axes[0, i].axis('off')
# 显示生成图像
axes[1, i].imshow(fake_samples[i].numpy().reshape(28, 28) * 0.5 + 0.5, cmap='gray')
axes[1, i].set_title('生成图像')
axes[1, i].axis('off')
# 显示更多真实图像
axes[2, i].imshow(real_samples[i+8].reshape(28, 28) * 0.5 + 0.5, cmap='gray')
axes[2, i].set_title('真实图像')
axes[2, i].axis('off')
# 显示更多生成图像
axes[3, i].imshow(fake_samples[i+8].numpy().reshape(28, 28) * 0.5 + 0.5, cmap='gray')
axes[3, i].set_title('生成图像')
axes[3, i].axis('off')
plt.tight_layout()
plt.show()
# 12. 创建交互式生成器
print("\n12. 🎯 交互式数字生成演示...")
def interactive_generation():
"""交互式生成数字"""
print("\n" + "="*40)
print("🎲 交互式数字生成")
print("="*40)
print("输入 'r' 随机生成数字")
print("输入 'q' 退出")
while True:
user_input = input("\n请输入指令: ").strip().lower()
if user_input == 'q':
break
elif user_input == 'r':
# 生成随机数字
noise = tf.random.normal([1, LATENT_DIM])
generated_image = generator(noise, training=False)
# 显示结果
plt.figure(figsize=(6, 6))
plt.imshow(generated_image[0, :, :, 0] * 0.5 + 0.5, cmap='gray')
plt.title('GAN生成的手写数字', fontsize=14)
plt.axis('off')
plt.show()
# 同时显示多个变体
print("生成多个变体...")
multiple_noise = tf.random.normal([9, LATENT_DIM])
multiple_images = generator(multiple_noise, training=False)
plt.figure(figsize=(12, 4))
for i in range(9):
plt.subplot(3, 3, i + 1)
plt.imshow(multiple_images[i, :, :, 0] * 0.5 + 0.5, cmap='gray')
plt.axis('off')
plt.suptitle('同一模型生成的不同数字变体', fontsize=14)
plt.tight_layout()
plt.show()
else:
print("未知指令,请输入 'r' 或 'q'")
# 运行交互式生成
interactive_generation()
# 13. 保存模型
print("\n13. 💾 保存训练好的模型...")
# 保存生成器
generator.save('gan_generator.h5')
print("生成器模型已保存为 'gan_generator.h5'")
# 保存判别器
discriminator.save('gan_discriminator.h5')
print("判别器模型已保存为 'gan_discriminator.h5'")
print("\n" + "="*50)
print("🎉 恭喜!你已经完成了生成对抗网络的完整实战!")
print("="*50)
print("\n📚 下一步学习建议:")
print(" • 尝试训练更多轮次(100+)获得更好的生成质量")
print(" • 修改网络结构,如增加层数或神经元数量")
print(" • 尝试不同的GAN变体(DCGAN, WGAN, StyleGAN等)")
print(" • 在其他数据集上训练,如人脸生成(CelebA)")
📊 GAN训练过程详解
# 补充:详细解释GAN训练的关键概念
def explain_gan_concepts():
"""详细解释GAN的关键概念"""
concepts = {
'潜在空间 (Latent Space)': {
'描述': '生成器输入的随机噪声空间',
'比喻': '就像艺术家的灵感源泉',
'作用': '通过改变噪声向量可以控制生成图像的特征'
},
'对抗训练 (Adversarial Training)': {
'描述': '生成器和判别器相互对抗、共同进步的过程',
'比喻': '就像学生和老师互相促进',
'作用': '确保生成器和判别器平衡发展'
},
'模式崩溃 (Mode Collapse)': {
'描述': '生成器只学会生成少数几种样本',
'比喻': '就像艺术家只会画一种风格的画',
'解决': '使用不同的损失函数或改进训练技巧'
},
'梯度消失 (Gradient Vanishing)': {
'描述': '在训练早期,判别器太强导致生成器学不到东西',
'比喻': '就像学生觉得老师太厉害而放弃学习',
'解决': '使用Wasserstein GAN或其他改进方法'
},
'批量归一化 (Batch Normalization)': {
'描述': '对每批数据进行归一化处理',
'比喻': '就像标准化学习材料',
'作用': '稳定训练过程,加速收敛'
}
}
print("\n🔍 GAN关键概念详解:")
print("=" * 50)
for concept, info in concepts.items():
print(f"\n📖 {concept}:")
print(f" 描述: {info['描述']}")
print(f" 生活比喻: {info['比喻']}")
print(f" 作用/解决: {info['作用']}")
explain_gan_concepts()
# 可视化潜在空间插值
def demonstrate_latent_space_interpolation():
"""演示潜在空间插值"""
print("\n🎨 潜在空间插值演示...")
# 生成两个随机噪声向量
z1 = tf.random.normal([1, 100])
z2 = tf.random.normal([1, 100])
# 在两个向量之间进行插值
num_interpolations = 8
interpolated_images = []
for alpha in np.linspace(0, 1, num_interpolations):
# 线性插值
z_interpolated = z1 * (1 - alpha) + z2 * alpha
generated_image = generator(z_interpolated, training=False)
interpolated_images.append(generated_image)
# 显示插值结果
plt.figure(figsize=(12, 3))
for i, img in enumerate(interpolated_images):
plt.subplot(1, num_interpolations, i + 1)
plt.imshow(img[0, :, :, 0] * 0.5 + 0.5, cmap='gray')
plt.title(f'{i+1}')
plt.axis('off')
plt.suptitle('潜在空间插值:在两个随机向量之间平滑过渡', fontsize=14)
plt.tight_layout()
plt.show()
# 运行插值演示
demonstrate_latent_space_interpolation()
💡 GAN核心概念总结
| 概念 | 生活比喻 | 在代码中的体现 | 作用 |
|---|---|---|---|
| 生成器 | 艺术伪造者 | generator模型 | 从噪声生成逼真数据 |
| 判别器 | 鉴定专家 | discriminator模型 | 区分真实和生成数据 |
| 对抗训练 | 猫鼠游戏 | 交替训练两个网络 | 相互促进,共同进步 |
| 潜在空间 | 灵感源泉 | 随机噪声输入 | 控制生成数据的特征 |
| 损失函数 | 成绩单 | generator_loss, discriminator_loss | 指导模型改进方向 |
| 批量归一化 | 标准化 | BatchNormalization层 | 稳定训练过程 |
🎯 学习建议
-
运行完整代码:观察GAN从噪声生成逼真图像的完整过程
-
调整超参数:尝试不同的学习率、批量大小、网络结构
-
监控训练过程:关注损失值的变化,确保两个网络平衡发展
-
可视化理解:重点关注潜在空间插值和生成质量的提升过程
-
尝试改进:研究DCGAN、WGAN等改进版本解决训练不稳定问题
这个实例展示了GAN从基础概念到完整实现的全部过程,帮助你直观理解生成对抗网络的工作原理和训练技巧!
更多信息请关注微信公众号:AI弟
更多推荐
所有评论(0)