<jupyter_start><jupyter_text>![Pytorch](../../../pytorch_logo_2018.svg)  Pytorch 高级篇（1）：生成对抗网络（Generative Adversarial Networks）>参考代码>>**yunjey的 [pytorch tutorial系列](https://github.com/yunjey/pytorch-tutorial/blob/master/tutorials/03-advanced/generative_adversarial_network/main.py)**  生成对抗网络 学习资料对于生成对抗网络还是很感兴趣的，可以用来生成图像, 生成的图像可以拿来当样本，hiahia。>**论文 Arxiv地址**>>[Generative Adversarial Networks](https://arxiv.org/abs/1406.2661)>>>>**推荐 相关博客和教程**>>[到底什么是生成式对抗网络GAN？By 微软亚洲研究院](https://www.msra.cn/zh-cn/news/features/gan-20170511)>>[莫烦视频：什么是生成对抗网络 (GAN)](https://morvanzhou.github.io/tutorials/machine-learning/torch/4-06-A-GAN/)>>[莫烦视频：Pytorch实现GAN](https://morvanzhou.github.io/tutorials/machine-learning/torch/4-06-GAN/)  Pytorch 实现  预处理阶段<jupyter_code># 包
import os
import torch
import torchvision
import torch.nn as nn
from torchvision import transforms
from torchvision.utils import save_image
# 设备配置
torch.cuda.set_device(1) # 这句用来设置pytorch在哪块GPU上运行
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
# 超参数设置
# Hyper-parameters
latent_size = 64
hidden_size = 256
image_size = 784
num_epochs = 200
batch_size = 100
sample_dir = 'samples'
# 如果没有文件夹就创建一个文件夹
if not os.path.exists(sample_dir):
    os.makedirs(sample_dir)
# 图像处理模块：transform设置
# Image processing：归一化
transform = transforms.Compose([
                transforms.ToTensor(),
                transforms.Normalize(mean=(0.5, 0.5, 0.5),   # 3 for RGB channels
                                     std=(0.5, 0.5, 0.5))])<jupyter_output><empty_output><jupyter_text>MINIST 数据集<jupyter_code># 加载同时做transform预处理
mnist = torchvision.datasets.MNIST(root='../../../data/minist',
                                   train=True,
                                   transform=transform,
                                   download=True)
# 数据加载器：GAN中只考虑判别模型和生成模型的对抗提高，无需设置训练集和测试集
data_loader = torch.utils.data.DataLoader(dataset=mnist,
                                          batch_size=batch_size, 
                                          shuffle=True)<jupyter_output><empty_output><jupyter_text>判别模型和生成模型的创建<jupyter_code># 创建判别模型
# Discriminator
D = nn.Sequential(
        nn.Linear(image_size, hidden_size), # 判别的输入时图像数据
        nn.LeakyReLU(0.2),
        nn.Linear(hidden_size, hidden_size),
        nn.LeakyReLU(0.2),
        nn.Linear(hidden_size, 1),
        nn.Sigmoid())
# 创建生成模型
# Generator 
G = nn.Sequential(
        nn.Linear(latent_size, hidden_size), # 生成的输入是随机数，可以自己定义
        nn.ReLU(),
        nn.Linear(hidden_size, hidden_size),
        nn.ReLU(),
        nn.Linear(hidden_size, image_size),
        nn.Tanh())
# 拷到计算设备上
# Device setting
D = D.to(device)
G = G.to(device)
# 设置损失函数和优化器
criterion = nn.BCELoss() # 二值交叉熵 Binary cross entropy loss
d_optimizer = torch.optim.Adam(D.parameters(), lr=0.0002)
g_optimizer = torch.optim.Adam(G.parameters(), lr=0.0002)
# 定义两个函数

def denorm(x):
    out = (x + 1) / 2
    return out.clamp(0, 1)

# 重置梯度
def reset_grad():
    d_optimizer.zero_grad()
    g_optimizer.zero_grad()<jupyter_output><empty_output><jupyter_text>对抗生成训练分两步：1. 固定生成模型，优化判别模型2. 固定判别模型，优化生成模型<jupyter_code>total_step = len(data_loader)
for epoch in range(num_epochs):
    for i, (images, _) in enumerate(data_loader):
        images = images.reshape(batch_size, -1).to(device)
            
        # 创建标签，随后会用于损失函数BCE loss的计算
        real_labels = torch.ones(batch_size, 1).to(device)  # true_label设为1，表示True
        fake_labels = torch.zeros(batch_size, 1).to(device) # fake_label设为0，表示False

        # ================================================================== #
        #                      训练判别模型                      #
        # ================================================================== #

        # 计算real_损失
        # 使用公式 BCE_Loss(x, y): - y * log(D(x)) - (1-y) * log(1 - D(x))，来计算realimage的判别损失
        # 其中第二项永远为零，因为real_labels == 1
        outputs = D(images)
        d_loss_real = criterion(outputs, real_labels)
        real_score = outputs
        
        
        # 计算fake损失
        # 生成模型根据随机输入生成fake_images
        z = torch.randn(batch_size, latent_size).to(device)
        fake_images = G(z) 
        # 使用公式 BCE_Loss(x, y): - y * log(D(x)) - (1-y) * log(1 - D(x))，来计算fakeImage的判别损失
        # 其中第一项永远为零，因为fake_labels == 0
        outputs = D(fake_images)
        d_loss_fake = criterion(outputs, fake_labels)
        fake_score = outputs
        
        # 反向传播和优化
        d_loss = d_loss_real + d_loss_fake
        reset_grad()
        d_loss.backward()
        d_optimizer.step()
        
        # ================================================================== #
        #                       训练生成模型                       #
        # ================================================================== #

        # 生成模型根据随机输入生成fake_images,然后判别模型进行判别
        z = torch.randn(batch_size, latent_size).to(device)
        fake_images = G(z)
        outputs = D(fake_images)
        
        # 训练生成模型，使之最大化 log(D(G(z)) ，而不是最小化 log(1-D(G(z)))
        # 具体的解释在原文第三小节最后一段有解释
        # 大致含义就是在训练初期，生成模型G还很菜，判别模型会拒绝高置信度的样本，因为这些样本与训练数据不同。
        # 这样log(1-D(G(z)))就近乎饱和，梯度计算得到的值很小，不利于反向传播和训练。
        # 换一种思路，通过计算最大化log(D(G(z))，就能够在训练初期提供较大的梯度值，利于快速收敛
        g_loss = criterion(outputs, real_labels)
        
        # 反向传播和优化
        reset_grad()
        g_loss.backward()
        g_optimizer.step()
        
        if (i+1) % 200 == 0:
            print('Epoch [{}/{}], Step [{}/{}], d_loss: {:.4f}, g_loss: {:.4f}, D(x): {:.2f}, D(G(z)): {:.2f}' 
                  .format(epoch, num_epochs, i+1, total_step, d_loss.item(), g_loss.item(), 
                          real_score.mean().item(), fake_score.mean().item()))
    
    # 在第一轮保存训练数据图像
    if (epoch+1) == 1:
        images = images.reshape(images.size(0), 1, 28, 28)
        save_image(denorm(images), os.path.join(sample_dir, 'real_images.png'))
    
    # 每一轮保存 生成的样本（即fake_images）
    fake_images = fake_images.reshape(fake_images.size(0), 1, 28, 28)
    save_image(denorm(fake_images), os.path.join(sample_dir, 'fake_images-{}.png'.format(epoch+1)))<jupyter_output>Epoch [0/200], Step [200/600], d_loss: 0.0358, g_loss: 4.3944, D(x): 0.99, D(G(z)): 0.03
Epoch [0/200], Step [400/600], d_loss: 0.1378, g_loss: 5.3265, D(x): 0.96, D(G(z)): 0.06
Epoch [0/200], Step [600/600], d_loss: 0.2501, g_loss: 3.8991, D(x): 0.88, D(G(z)): 0.09
Epoch [1/200], Step [200/600], d_loss: 0.0795, g_loss: 5.9620, D(x): 0.97, D(G(z)): 0.05
Epoch [1/200], Step [400/600], d_loss: 0.0882, g_loss: 5.2908, D(x): 0.95, D(G(z)): 0.01
Epoch [1/200], Step [600/600], d_loss: 0.4405, g_loss: 3.4986, D(x): 0.84, D(G(z)): 0.10
Epoch [2/200], Step [200/600], d_loss: 0.9144, g_loss: 3.6864, D(x): 0.86, D(G(z)): 0.39
Epoch [2/200], Step [400/600], d_loss: 0.3320, g_loss: 3.6846, D(x): 0.89, D(G(z)): 0.09
Epoch [2/200], Step [600/600], d_loss: 0.3677, g_loss: 4.0595, D(x): 0.84, D(G(z)): 0.07
Epoch [3/200], Step [200/600], d_loss: 1.1544, g_loss: 1.7915, D(x): 0.64, D(G(z)): 0.25
Epoch [3/200], Step [400/600], d_loss: 0.2243, g_loss: 5.7187, D(x): 0.93, D(G(z)): 0.03
Epoch [3/200], Step [[...]<jupyter_text>结果展示<jupyter_code>#导入包
import matplotlib.pyplot as plt # plt 用于显示图片
import matplotlib.image as mpimg # mpimg 用于读取图片
import numpy as np<jupyter_output><empty_output><jupyter_text>real image<jupyter_code>realPath = './samples/real_images.png'
realImage = mpimg.imread(realPath)
plt.imshow(realImage) # 显示图片
plt.axis('off') # 不显示坐标轴
plt.show()<jupyter_output><empty_output><jupyter_text>fake image 进化过程下图分别为第1,5,195,200轮训练生成的结果。<jupyter_code># 起始阶段
fakePath1 = './samples/fake_images-1.png'
fakeImg1 = mpimg.imread(fakePath1)

fakePath5 = './samples/fake_images-5.png'
fakeImg5 = mpimg.imread(fakePath5)

plt.figure()
plt.subplot(1,2,1 ) # 显示图片
plt.imshow(fakeImg1) # 显示图片
plt.subplot(1,2,2 ) # 显示图片
plt.imshow(fakeImg5) # 显示图片
plt.axis('off') # 不显示坐标轴
plt.show()

fakePath195 = './samples/fake_images-195.png'
fakeImg195 = mpimg.imread(fakePath195)

fakePath200 = './samples/fake_images-200.png'
fakeImg200 = mpimg.imread(fakePath200)

plt.figure()
plt.subplot(1,2,1 ) # 显示图片
plt.imshow(fakeImg195) # 显示图片
plt.subplot(1,2,2 ) # 显示图片
plt.imshow(fakeImg200) # 显示图片
plt.axis('off') # 不显示坐标轴
plt.show()<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.1832683877252801, 'fraction_numerical': 0.05674622503653191, 'mean_word_length': 3.166920345002537, 'pattern_counts': {'":': 0, '<': 28, '<?xml version=': 0, '>': 44, 'https://': 5, 'lorem ipsum': 0, 'www.': 1, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 9, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '7', 'dir': 'jupyter-structured-clean-dedup', 'id': '340878', 'license': 'MIT', 'n_tokens_mistral': 3982, 'n_tokens_neox': 3853, 'n_words': 504, 'path': 'tutorials/03-advanced/generative_adversarial_network/generative_adversarial_network.ipynb', 'repo_name': 'veraposeidon/pytorch-tutorial', 'size': '8212'}