AI开发:生成式对抗网络入门 模型训练和图像生成 -Python 机器学习

news2025/2/25 8:00:59

阶段1:GAN是个啥?

生成式对抗网络(Generative Adversarial Networks, GAN),名字听着就有点“对抗”的意思,没错!它其实是两个神经网络互相斗智斗勇的游戏:

  1. 生成器(Generator):负责造假,生成一些以假乱真的数据。
  2. 判别器(Discriminator):负责打假,判断数据是真还是假。

想象一下,生成器是个假币制造商,判别器是个验钞机。假币制造商不断提升造假能力,验钞机也不断升级打假技巧。最终的目标是生成的假币足以以假乱真,让验钞机无法区分。

生成式对抗网络(GAN)是一种由 Ian Goodfellow 和他的团队在2014年提出的深度学习模型。GAN 本质上是一种用于生成与真实数据分布相似的“新数据”的方法,常用于图像生成、风格转换和数据增强等任务。

一、GAN 的基本概念
1. 两个网络:生成器(Generator)和判别器(Discriminator)

GAN 的核心思想是利用两个神经网络相互对抗:

  • 生成器 (G): 学习生成接近真实数据的“假数据”。其目标是“骗过”判别器,使其认为假数据是真的。
  • 判别器 (D): 学习区分真实数据和生成器生成的假数据。其目标是提高“识别假数据的能力”。

两者形成了一种动态博弈:

  • 生成器不断改进以生成更逼真的数据。
  • 判别器不断改进以更准确地区分真假数据。

最终目标:生成器生成的数据和真实数据难以区分,判别器无法给出明确的判断。

2. 训练目标

GAN 的训练目标可以通过以下损失函数来描述:

  • 判别器的损失:最大化真实数据的得分,最小化假数据的得分。
  • 生成器的损失:最小化判别器对假数据的判断分数(即尽量骗过判别器)。

数学公式为:

这里:

  • D(x)D(x) 表示判别器给真实数据 xx 的打分。
  • G(z)G(z) 表示生成器根据随机噪声 zz 生成的假数据。
3. GAN 的对抗过程

训练过程通常分为两步:

  1. 更新判别器: 让判别器学习如何区分真实和假数据。
  2. 更新生成器: 让生成器学习生成更真实的数据,以骗过判别器。

二、直观例子:警察与造假者

你可以将 GAN 的训练过程类比为“警察(判别器)与造假者(生成器)”之间的较量:

  • 一开始,造假者技术拙劣,警察很容易识破假币。
  • 随着时间推移,造假者的造假技术逐渐提高,而警察也在不断升级检测手段。
  • 最终,假币与真币变得极为相似,警察几乎无法分辨。

阶段2:从头写个最简单的GAN

import torch
import torch.nn as nn
import torch.optim as optim

# 1. 生成器(Generator):简单的全连接网络
class Generator(nn.Module):
    def __init__(self):
        super(Generator, self).__init__()
        self.model = nn.Sequential(
            nn.Linear(100, 256),  # 输入 100 维噪声,输出中间隐藏层 256 维
            nn.ReLU(),            # 激活函数 ReLU,增加非线性
            nn.Linear(256, 784),  # 隐藏层输出 784 维数据(28x28 图像展平后)
            nn.Tanh()             # 将输出限制到 [-1, 1],方便后续训练
        )
    
    def forward(self, z):
        return self.model(z)

# 2. 判别器(Discriminator):另一个简单的全连接网络
class Discriminator(nn.Module):
    def __init__(self):
        super(Discriminator, self).__init__()
        self.model = nn.Sequential(
            nn.Linear(784, 256),       # 输入 784 维数据(展平的图像)
            nn.LeakyReLU(0.2),         # 激活函数,允许小负值(更鲁棒)
            nn.Linear(256, 1),         # 输出一个值(真 or 假)
            nn.Sigmoid()               # 输出概率,范围 [0, 1]
        )
    
    def forward(self, x):
        return self.model(x)


# 初始化网络
G = Generator()
D = Discriminator()

# 优化器
optimizer_G = optim.Adam(G.parameters(), lr=0.0002)
optimizer_D = optim.Adam(D.parameters(), lr=0.0002)

# 损失函数:二分类交叉熵
criterion = nn.BCELoss()

 代码释疑:

这段代码实现了生成式对抗网络(GAN)的生成器(Generator)和判别器(Discriminator),并为它们设置了优化器和损失函数。以下是对相关内容的详细解释,帮助你理解各个部分的功能。


1. Generator 类:生成器

生成器的作用是生成假数据,用来骗过判别器。

class Generator(nn.Module):
    def __init__(self):
        super(Generator, self).__init__()
        self.model = nn.Sequential(
            nn.Linear(100, 256),  # 输入 100 维噪声,输出中间隐藏层 256 维
            nn.ReLU(),            # 激活函数 ReLU,增加非线性
            nn.Linear(256, 784),  # 隐藏层输出 784 维数据(28x28 图像展平后)
            nn.Tanh()             # 将输出限制到 [-1, 1],方便后续训练
        )
    
    def forward(self, z):
        return self.model(z)
关键点:
  • 输入:

    • 生成器的输入是一个随机噪声 z,形状为 [batch_size, 100]
    • 噪声是生成器的起点,让它从随机性中学习目标数据分布。
  • 输出:

    • 输出 784 个值,对应一张 28x28 的图像展平(如 MNIST 数据)。
    • 使用 Tanh 将输出限制在 [-1, 1] 区间,通常是为了和真实数据的归一化范围一致。

2. Discriminator 类:判别器

判别器的作用是判断输入数据是真实的还是生成的。

class Discriminator(nn.Module):
    def __init__(self):
        super(Discriminator, self).__init__()
        self.model = nn.Sequential(
            nn.Linear(784, 256),       # 输入 784 维数据(展平的图像)
            nn.LeakyReLU(0.2),         # 激活函数,允许小负值(更鲁棒)
            nn.Linear(256, 1),         # 输出一个值(真 or 假)
            nn.Sigmoid()               # 输出概率,范围 [0, 1]
        )
    
    def forward(self, x):
        return self.model(x)
关键点:
  • 输入:

    • 输入是展平的 28x28 图像(784 维),可以是真实数据或生成器的假数据。
  • 输出:

    • 输出是一个概率值,0 表示假,1 表示真。
    • 使用 Sigmoid 将值映射到 [0, 1] 区间。
  • LeakyReLU:

    • 激活函数 LeakyReLU(0.2) 在输入为负值时保留一定斜率(0.2),解决 ReLU 的“死区”问题,使训练更稳定。

3. 优化器

优化器用于更新模型的参数,使损失函数逐渐减小。

optimizer_G = optim.Adam(G.parameters(), lr=0.0002)
optimizer_D = optim.Adam(D.parameters(), lr=0.0002)
  • Adam 优化器:

    • 一种改进的梯度下降算法,适用于深度学习模型,尤其是 GAN。
    • 自动调整学习率,提高收敛速度。
  • 学习率 (lr=0.0002):

    • 学习率设置为 0.0002,是 GAN 训练中一个常见的经验值。
  • 目标:

    • optimizer_G 优化生成器的参数,使其生成更逼真的数据。
    • optimizer_D 优化判别器的参数,使其更好地区分真假数据。

4. 损失函数:BCELoss

BCELoss 是二分类交叉熵损失函数,用于计算判别器和生成器的损失。

criterion = nn.BCELoss()
什么是交叉熵?

交叉熵是一种用来衡量两个概率分布相似度的损失函数,公式如下:

  • yiy_i:真实标签(1 表示真,0 表示假)。
  • pip_i:模型预测的概率值(判别器的输出)。
在 GAN 中的作用:
  1. 判别器的损失:

    • 判别器的目标是区分真实数据和生成器生成的假数据。
    • 对于真实数据,y = 1;对于假数据,y = 0
    • 损失函数让判别器尽量输出接近真实标签的概率。
  2. 生成器的损失:

    • 生成器的目标是让判别器认为假数据是真实的。
    • 生成器通过 GAN 的损失函数间接影响判别器的输出,目标是让判别器输出 y = 1

5. 上述代码小结

  • 生成器 (G): 学习生成逼真的假数据。
  • 判别器 (D): 学习区分真实数据和假数据。
  • 损失函数 (BCELoss): 衡量模型输出概率和目标标签之间的差异。
  • 优化器 (Adam): 调整模型参数,使损失函数最小化。

在训练过程中:

  • 生成器试图最小化生成器的损失。
  • 判别器试图最大化判别器的准确率。

这段代码是 GAN 的基础骨架,你可以在此基础上进行实验,比如用它来生成 MNIST 图像!

题外话:

PyTorch(简称 torch)是一个流行的开源深度学习框架,它提供了许多用于构建和训练神经网络的功能。它特别以易用性、灵活性和性能而著称,是机器学习和深度学习领域的常用工具之一。下面,我们来了解一下 PyTorch 的作用,以及在这段 GAN 代码中它是如何发挥作用的。

1. PyTorch 的基本功能

PyTorch 提供了以下几个关键功能:

  • 张量(Tensor):

    • PyTorch 中的核心数据结构是张量(torch.Tensor),类似于 NumPy 的数组,但是张量支持 GPU 加速。
    • 张量是神经网络中的数据载体,存储输入数据、权重、偏置等。
  • 自动求导(Autograd):

    • PyTorch 提供自动求导功能,能够计算神经网络中每一层的梯度,简化了反向传播算法的实现。
    • 当你定义模型并传入数据后,PyTorch 会自动计算损失函数的梯度,并更新模型的参数。
  • 构建和训练神经网络:

    • 使用 torch.nn 提供的模块,可以方便地构建神经网络的各层(如全连接层、卷积层、激活函数等)。
    • torch.optim 提供了优化算法(如 SGD、Adam)来训练模型。
  • GPU 加速:

    • PyTorch 可以利用 GPU(如 CUDA)来加速计算。你可以将张量和模型移动到 GPU 上,这样就能提高训练速度。

阶段3:它们怎么斗起来?

核心是两步:

  1. 训练判别器:真图片标为1,假图片标为0,看看它能不能区分真伪。
  2. 训练生成器:假图片骗过判别器,努力让判别器给它打1分。
from torchvision import datasets, transforms
from torch.utils.data import DataLoader

# 数据加载(MNIST 数据集)
transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize([0.5], [0.5])])
mnist = datasets.MNIST(root='./data', train=True, download=True, transform=transform)
dataloader = DataLoader(mnist, batch_size=64, shuffle=True)

# 训练循环
epochs = 10
for epoch in range(epochs):
    for i, (real_imgs, _) in enumerate(dataloader):
        # ========== 1. 训练判别器 ==========
        # 真数据
        real_imgs = real_imgs.view(real_imgs.size(0), -1)  # 展平图片
        real_labels = torch.ones(real_imgs.size(0), 1)  # 真图片标签为1
        
        # 假数据
        z = torch.randn(real_imgs.size(0), 100)  # 随机噪声
        fake_imgs = G(z)
        fake_labels = torch.zeros(real_imgs.size(0), 1)  # 假图片标签为0
        
        # 判别器的预测和损失
        real_preds = D(real_imgs)
        fake_preds = D(fake_imgs.detach())  # 假图片不更新生成器
        loss_real = criterion(real_preds, real_labels)
        loss_fake = criterion(fake_preds, fake_labels)
        loss_D = loss_real + loss_fake
        
        # 优化判别器
        optimizer_D.zero_grad()
        loss_D.backward()
        optimizer_D.step()
        
        # ========== 2. 训练生成器 ==========
        z = torch.randn(real_imgs.size(0), 100)
        fake_imgs = G(z)
        fake_preds = D(fake_imgs)
        loss_G = criterion(fake_preds, real_labels)  # 欺骗判别器的损失
        
        # 优化生成器
        optimizer_G.zero_grad()
        loss_G.backward()
        optimizer_G.step()

        # 打印进度
        if i % 200 == 0:
            print(f"Epoch [{epoch+1}/{epochs}], Step [{i}/{len(dataloader)}], "
                  f"D Loss: {loss_D.item():.4f}, G Loss: {loss_G.item():.4f}")

这段代码实现了一个基本的 生成式对抗网络(GAN) 训练过程,使用 MNIST 数据集 生成与真实手写数字类似的图像。执行这段代码会产生以下几个结果:

1. 数据加载(MNIST 数据集)

首先,代码通过 torchvision 中的 datasets.MNIST 加载了 MNIST 数据集。这个数据集包含了 60,000 张手写数字的训练图像和 10,000 张测试图像(这里只使用了训练集)。数据被转换为 PyTorch 张量并做了标准化处理,使每个像素值在 [-1, 1] 之间。然后,DataLoader 将数据划分为批次(batch),每次加载 64 张图像。

2. 训练循环

接下来,代码进入训练循环,在每个 epoch 中,它会进行以下操作:

(1)训练判别器(Discriminator)
  • 真数据:

    • 从 MNIST 数据集中提取实际的手写数字图像,将图像展平为 784 维(28x28 的像素展平)。
    • 创建真实标签,所有真实图像的标签为 1
  • 假数据:

    • 从随机噪声 z(100 维的向量)中生成假图像。
    • 创建假的标签,所有生成的假图像标签为 0
  • 判别器损失:

    • 判别器会分别计算它对真实数据和假数据的预测,使用二元交叉熵损失 BCELoss 计算真实数据和假数据的损失。
    • loss_real 是判别器对真实图像的损失,loss_fake 是对假图像的损失,最终判别器的总损失是两者之和 loss_D
  • 优化判别器:

    • 使用 optimizer_D.zero_grad() 清除先前的梯度,进行反向传播并更新判别器的参数。
(2)训练生成器(Generator)
  • 生成假图像:
    • 使用随机噪声 z 通过生成器生成一批假图像。
  • 生成器损失:
    • 生成器的目标是欺骗判别器,让它认为生成的假图像是真实的。因此,生成器的损失是判别器对这些假图像的预测(希望是 1)的损失,即 loss_G
  • 优化生成器:
    • 使用 optimizer_G.zero_grad() 清除先前的梯度,进行反向传播并更新生成器的参数。

3. 打印进度

每训练 200 个批次,代码会打印出当前 epoch 和 step 的进度,并显示判别器和生成器的损失:

Epoch [1/10], Step [0/938], D Loss: 0.6881, G Loss: 0.7014
Epoch [1/10], Step [200/938], D Loss: 0.6834, G Loss: 0.7102
...

实际运行效果:

执行结果

  1. 训练输出:

    • 在训练过程中,随着生成器和判别器的不断优化,你会看到输出的 D Loss(判别器损失)和 G Loss(生成器损失)。初始时,这两个损失通常较大,因为模型还没有学会如何生成和判断图像。
    • 随着训练的进行,损失会逐渐减小,表示生成器和判别器在相互博弈中逐渐变得更强。
  2. 图像生成:

    • 由于 GAN 的训练是一个对抗过程,因此每个 epoch 训练后,生成器的输出图像会逐渐接近真实图像的分布。
    • 生成器在训练中会变得越来越善于生成逼真的手写数字图像,直到它能够生成看起来很像 MNIST 数据集中的真实数字。

小结

  • 判别器:学习区分真实和假图像,给出图像是“真”还是“假”的概率。
  • 生成器:学习生成越来越像真实手写数字的图像,目的是“欺骗”判别器,使判别器认为生成的假图像是真实的。

执行完这段代码后,生成器(G)会经过 10 个 epoch 的训练,逐步学会生成类似 MNIST 手写数字的图像。你可以根据损失值的变化和生成的图像的质量,观察训练过程的进展。


阶段4:GAN生成的图像是啥样?

每训练一段时间,我们让生成器画个画,看看它有没有长进:

import matplotlib.pyplot as plt

def show_images(generator, num_images=16):
    z = torch.randn(num_images, 100)  # 随机噪声
    fake_imgs = generator(z).view(num_images, 1, 28, 28)  # 恢复图片形状
    fake_imgs = (fake_imgs + 1) / 2.0  # 把值范围从 [-1, 1] 变到 [0, 1]
    grid = torch.cat([fake_imgs[i] for i in range(num_images)], dim=2).squeeze(0)
    plt.imshow(grid.detach().numpy(), cmap='gray')
    plt.axis('off')  # 不显示坐标轴
    plt.savefig("generated_images.png", bbox_inches='tight')  # 保存图像到文件
    plt.close()  # 关闭图形窗口

 这是最终生成地图像:

局部放大

是不是可以联想到:生成式对抗网络的应用场景相当广泛,比如半导体晶圆缺陷检测领域,医学影像疾病识别领域等等。


阶段5:GAN训练的问题

GAN不是一帆风顺的,训练GAN像哄熊孩子:生成器和判别器常常互相欺负对方导致训练不稳定。
怎么办?我们可以尝试改进:

  1. 改网络结构:比如用更强大的卷积网络。
  2. 改损失函数:比如使用Wasserstein GAN。
  3. 调参:改动学习率、优化器等等。

这就是生成式对抗网络的基础啦,希望它的斗智斗勇能让你觉得有趣!你也可以试试用它生成其他类型的数据,比如音乐、画作或者文字!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2253330.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

040集——CAD中放烟花(CAD—C#二次开发入门)

效果如下: 单一颜色的烟花: 渐变色的火花: namespace AcTools {public class HH{public static TransientManager tm TransientManager.CurrentTransientManager;public static Random rand new Random();public static Vector3D G new V…

JavaScript实现tab栏切换

JavaScript实现tab栏切换 代码功能概述 这段代码实现了一个简单的选项卡(Tab)切换功能。它通过操作 HTML 元素的类名(class)来控制哪些选项卡(Tab)和对应的内容板块显示,哪些隐藏。基本思路是先…

【天地图】HTML页面实现车辆轨迹、起始点标记和轨迹打点的完整功能

目录 一、功能演示 二、完整代码 三、参考文档 一、功能演示 运行以后完整的效果如下: 点击开始,小车会沿着轨迹进行移动,点击轨迹点会显示经纬度和时间: 二、完整代码 废话不多说,直接给完整代码,替换…

HCIA笔记6--路由基础与静态路由:浮动路由、缺省路由、迭代查找

文章目录 0. 概念1.路由器工作原理2. 跨网访问流程3. 静态路由配置4. 静态路由的应用场景4.1 路由备份4.2 浮动路由4.3 缺省路由 5. 迭代路由6 问题6.1 为什么路由表中有的下一跳的地址有接口?6.2 个人电脑的网关本质是什么? 0. 概念 自治系统&#xff…

20241129解决在Ubuntu20.04下编译中科创达的CM6125的Android10出现找不到库文件libncurses.so.5的问题

20241129解决在Ubuntu20.04下编译中科创达的CM6125的Android10出现找不到库文件libncurses.so.5的问题 2024/11/29 21:11 缘起:中科创达的高通CM6125开发板的Android10的编译环境需要。 vendor/qcom/proprietary/commonsys/securemsm/seccamera/service/jni/jni_if.…

Matlab搜索路径添加不上

发现无论是右键文件夹添加到路径,还是在“设置路径”中专门添加,我的路径始终添加不上,导致代码运行始终报错,后来将路径中的“”加号去掉后,就添加成功了,经过测试,路径中含有中文也可以添加成…

自由学习记录(28)

C# 中的流(Stream) 流(Stream)是用于读取和写入数据的抽象基类。 流表示从数据源读取或向数据源写入数据的矢量过程。 C# 中的流类是从 System.IO.Stream 基类派生的,提供了多种具体实现,每种实现都针对…

Redis3——线程模型与数据结构

Redis3——线程模型与数据结构 本文讲述了redis的单线程模型和IO多线程工作原理,以及几个主要数据结构的实现。 1. Redis的单线程模型 redis6.0之前,一个redis进程只有一个io线程,通过reactor模式可以连接大量客户端;redis6.0为了…

使用playwright自动化测试时,npx playwright test --ui打开图形化界面时报错

使用playwright自动化测试时,npx playwright test --ui打开图形化界面时报错 1、错误描述:2、解决办法3、注意符号的转义 1、错误描述: 在运行playwright的自动化测试项目时,使用npm run test无头模式运行正常,但使用…

深度学习模型:门控循环单元(GRU)详解

本文深入探讨了门控循环单元(GRU),它是一种简化版的长短期记忆网络(LSTM),在处理序列数据方面表现出色。文章详细介绍了 GRU 的基本原理、与 LSTM 的对比、在不同领域的应用以及相关的代码实现,…

用html+jq实现元素的拖动效果——js基础积累

用htmljq实现元素的拖动效果 效果图如下&#xff1a; 将【item10】拖动到【item1】前面 直接上代码&#xff1a; html部分 <ul id"sortableList"><li id"item1" class"w1" draggable"true">Item 1</li><li …

单片机学习笔记 12. 定时/计数器_定时

更多单片机学习笔记&#xff1a;单片机学习笔记 1. 点亮一个LED灯单片机学习笔记 2. LED灯闪烁单片机学习笔记 3. LED灯流水灯单片机学习笔记 4. 蜂鸣器滴~滴~滴~单片机学习笔记 5. 数码管静态显示单片机学习笔记 6. 数码管动态显示单片机学习笔记 7. 独立键盘单片机学习笔记 8…

【乐企文件生成工程】搭建docker环境,使用docker部署工程

1、自行下载docker 2、自行下载docker-compose 3、编写Dockerfile文件 # 使用官方的 OpenJDK 8 镜像 FROM openjdk:8-jdk-alpine# 设置工作目录 WORKDIR ./app# 复制 JAR 文件到容器 COPY ../lq-invoice/target/lq-invoice.jar app.jar # 暴露应用程序监听的端口 EXPOSE 1001…

React基础知识三 router路由全指南

现在最新版本是Router6和Router5有比较大的变化&#xff0c;Router5和Router4变化不大&#xff0c;本文以Router6的写法为主&#xff0c;也会对比和Router5的不同。比较全面。 安装路由 npm i react-router-dom基本使用 有两种Router&#xff0c;BrowserRouter和HashRouter&…

【C#】书籍信息的添加、修改、查询、删除

文章目录 一、简介二、程序功能2.1 Book类属性&#xff1a;方法&#xff1a; 2.2 Program 类 三、方法&#xff1a;四、用户界面流程&#xff1a;五、程序代码六、运行效果 一、简介 简单的C#控制台应用程序&#xff0c;用于管理书籍信息。这个程序将允许用户添加、编辑、查看…

打造去中心化交易平台:公链交易所开发全解析

公链交易所&#xff08;Public Blockchain Exchange&#xff09;是指基于公有链&#xff08;如以太坊、波场、币安智能链等&#xff09;建立的去中心化交易平台。与传统的中心化交易所&#xff08;CEX&#xff09;不同&#xff0c;公链交易所基于区块链技术实现资产交换的去中心…

CLIP模型也能处理点云信息

✨✨ 欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭❤&#xff5e;✨✨ &#x1f31f;&#x1f31f; 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢&#xff0c;在这里我会分享我的知识和经验。&am…

关于NXP开源的MCU_boot的项目心得

MCU的启动流程细查 注意MCU上电第一个函数运行的就是Reset_Handler函数&#xff0c;下图是表示了这个函数做了啥事情&#xff0c;注意加强一下对RAM空间的段的印象&#xff0c;从上到下是栈&#xff0c;堆&#xff0c;.bss段&#xff0c;.data段。 bootloader的难点 固件完…

MySQL5.6升级MySQL5.7

升级方式介绍 08 数据库服务版本升级方法 5.6 – 5.7 – 8.0 数据库版本升级方法&#xff1a; Inplace-本地升级 步骤一&#xff1a;在同一台服务器中&#xff0c;需要部署高版本数据库服务实例步骤二&#xff1a;低版本数据库中的数据进行备份迁移&#xff0c;迁移到高版本…

怎么理解BeamSearch?

在大模型推理中&#xff0c;常会用到BeamSearch&#xff0c;本文就BeamSearch原理与应用理解展开讲解。 一、BeamSearch原理 Beam Search 是一种启发式搜索算法&#xff0c;常用于自然语言处理&#xff08;NLP&#xff09;和其他需要生成序列的任务中&#xff0c;比如机器翻译…