1、在MINST數據集中,選出一個樣本,輸入數字標簽,輸出圖像,並讓輸出的圖像與樣本圖像盡可能相似,總誤差最小化; 2、同上,只不過並不直接比較輸出和樣本相似性,而是讓一個已訓練好的手寫數字識別網絡來判斷這個偽造的圖像是幾; 3、DCGAN,同時訓練一個生成器一個判別器。每個時刻隨機采樣一個向量輸入給生成器,它輸出一張圖像,同時讀取一個數據樣本,判別器判斷樣本圖像 和生成圖像的真假。
# 導入所需要的包,請保證torchvision已經在你的環境中安裝好. # 在Windows需要單獨安裝torchvision包,在命令行運行pip install torchvision即可 import torch import torch.nn as nn from torch.autograd import Variable import torch.optim as optim import torch.nn.functional as F import torchvision.datasets as dsets import torchvision.transforms as transforms import torchvision.utils as vutil import matplotlib.pyplot as plt import numpy as np %matplotlib inline
# 定義超參數 image_size = 28 #圖像尺寸大小 input_dim = 100 #輸入給生成器的向量維度,維度越大可以增加生成器輸出樣本的多樣性 num_channels = 1# 圖像的通道數 num_features = 64 #生成器中間的卷積核數量 batch_size = 64 #批次大小 # 如果系統中存在着GPU,z我們將用GPU來完成張量的計算 use_cuda = torch.cuda.is_available() #定義一個布爾型變量,標志當前的GPU是否可用 # 如果當前GPU可用,則將優先在GPU上進行張量計算 dtype = torch.cuda.FloatTensor if use_cuda else torch.FloatTensor itype = torch.cuda.LongTensor if use_cuda else torch.LongTensor # 加載MINIST數據,如果沒有下載過,就會在當前路徑下新建/data子目錄,並把文件存放其中 # MNIST數據是屬於torchvision包自帶的數據,所以可以直接調用。 # 在調用自己的數據的時候,我們可以用torchvision.datasets.ImageFolder或者torch.utils.data.TensorDataset來加載 train_dataset = dsets.MNIST(root='./data', #文件存放路徑 train=True, #提取訓練集 transform=transforms.ToTensor(), #將圖像轉化為Tensor,在加載數據的時候,就可以對圖像做預處理 download=True) #當找不到文件的時候,自動下載 # 加載測試數據集 test_dataset = dsets.MNIST(root='./data', train=False, transform=transforms.ToTensor()) # 訓練數據集的加載器,自動將數據分割成batch,順序隨機打亂 train_loader = torch.utils.data.DataLoader(dataset=train_dataset, batch_size=batch_size, shuffle=True) '''我們希望將測試數據分成兩部分,一部分作為校驗數據,一部分作為測試數據。 校驗數據用於檢測模型是否過擬合,並調整參數,測試數據檢驗整個模型的工作''' # 首先,我們定義下標數組indices,它相當於對所有test_dataset中數據的編碼 # 然后定義下標indices_val來表示校驗集數據的那些下標,indices_test表示測試集的下標 indices = range(len(test_dataset)) indices_val = indices[:5000] print(indices_val) indices_test = indices[5000:] # 根據這些下標,構造兩個數據集的SubsetRandomSampler采樣器,它會對下標進行采樣 sampler_val = torch.utils.data.sampler.SubsetRandomSampler(indices_val) print(sampler_val) sampler_test = torch.utils.data.sampler.SubsetRandomSampler(indices_test) # 根據兩個采樣器來定義加載器,注意將sampler_val和sampler_test分別賦值給了validation_loader和test_loader validation_loader = torch.utils.data.DataLoader(dataset =test_dataset, batch_size = batch_size, sampler = sampler_val ) test_loader = torch.utils.data.DataLoader(dataset=test_dataset, batch_size=batch_size, sampler = sampler_test )
#生成器模型定義
class ModelG(nn.Module):
def __init__(self):
super(ModelG,self).__init__()
self.model=nn.Sequential() #model為一個內嵌的序列化的神經網絡模型
# 利用add_module增加一個反卷積層,輸入為input_dim維,輸出為2*num_features維,窗口大小為5,padding是0
# 輸入圖像大小為1,輸出圖像大小為W'=(W-1)S-2P+K+P'=(1-1)*2-2*0+5+0=3, 5*5
self.model.add_module('deconv1',nn.ConvTranspose2d(input_dim, num_features*2, 5, 2, 0, bias=False))
# 增加一個batchnorm層
self.model.add_module('bnorm1',nn.BatchNorm2d(num_features*2))
# 增加非線性層
self.model.add_module('relu1',nn.ReLU(True))
# 增加第二層反卷積層,輸入2*num_features維,輸出num_features維,窗口5,padding=0
# 輸入圖像大小為5,輸出圖像大小為W'=(W-1)S-2P+K+P'=(5-1)*2-2*0+5+0=13, 13*13
self.model.add_module('deconv2',nn.ConvTranspose2d(num_features*2, num_features, 5, 2, 0, bias=False))
# 增加一個batchnorm層
self.model.add_module('bnorm2',nn.BatchNorm2d(num_features))
# 增加非線性層
self.model.add_module('relu2',nn.ReLU(True))
# 增加第二層反卷積層,輸入2*num_features維,輸出num_features維,窗口4,padding=0
# 輸入圖像大小為13,輸出圖像大小為W'=(W-1)S-2P+K+P'=(13-1)*2-2*0+4+0=28, 28*28
self.model.add_module('deconv3',nn.ConvTranspose2d(num_features, num_channels, 4, 2, 0,bias=False))
#self.model.add_module('tanh',nn.Tanh())
self.model.add_module('sigmoid',nn.Sigmoid())
def forward(self,input):
output = input
#遍歷網絡的所有層,一層層輸出信息
for name, module in self.model.named_children():
output = module(output)
#輸出一張28*28的圖像
return(output)
def weight_init(m):
#模型參數初始化.
#默認的初始化參數卷積核的權重是均值大概為0,方差在10^{-2}. BatchNorm層的權重均值是大約0.5,方差在0.2左右
#使用如下初始化方式可以,可以讓方差更小,使得收斂更快
class_name=m.__class__.__name__
if class_name.find('conv')!=-1:
m.weight.data.normal_(0,0.02)
if class_name.find('norm')!=-1:
m.weight.data.normal_(1.0,0.02)
def make_show(img):
# 將張量變成可以顯示的圖像
img = img.data.expand(batch_size, 3, image_size, image_size)
return img
def imshow(inp, title=None, ax=None):
# 在屏幕上繪制圖像
"""Imshow for Tensor."""
if inp.size()[0] > 1:
inp = inp.numpy().transpose((1, 2, 0))
else:
inp = inp[0].numpy()
mvalue = np.amin(inp)
maxvalue = np.amax(inp)
if maxvalue > mvalue:
inp = (inp - mvalue)/(maxvalue - mvalue)
ax.imshow(inp)
if title is not None:
ax.set_title(title)
ModelG()
#訓練模型
print('Initialized!')
#定義生成器模型
net = ModelG()
net = net.cuda() if use_cuda else net
#目標函數采用最小均方誤差
criterion = nn.MSELoss()
#定義優化器
optimizer = optim.SGD(net.parameters(), lr=0.0001, momentum=0.9)
# 隨機選擇生成0-9的數字,用於每個周期打印查看結果用
samples = np.random.choice(10, batch_size)
samples = torch.from_numpy(samples).type(dtype).requires_grad_(False)
#開始訓練
step = 0 #計數經歷了多少時間步
num_epochs = 100 #總的訓練周期
record = []
for epoch in range(num_epochs):
train_loss = []
# 加載數據批次
for batch_idx, (data, target) in enumerate(train_loader):
# 注意數據中的data轉化為了要預測的target,數據中的target則轉化成了輸入給網絡的標簽
target, data = data.clone().detach().requires_grad_(True), target.clone().detach() #data為一批圖像,target為一批標簽
# 將數據加載到GPU中
if use_cuda:
target, data = target.cuda(), data.cuda()
#將輸入的數字標簽轉化為生成器net能夠接受的(batch_size, input_dim, 1, 1)維張量
data = data.type(dtype)
data = data.resize(data.size()[0], 1, 1, 1)
data = data.expand(data.size()[0], input_dim, 1, 1)
net.train() # 給網絡模型做標記,標志說模型正在訓練集上訓練,
#這種區分主要是為了打開關閉net的training標志
output = net(data) #神經網絡完成一次前饋的計算過程,得到預測輸出output
loss = criterion(output, target) #將output與標簽target比較,計算誤差
optimizer.zero_grad() #清空梯度
loss.backward() #反向傳播
optimizer.step() #一步隨機梯度下降算法
step += 1
# 記載損失函數值
if use_cuda:
loss = loss.cpu()
train_loss.append(loss.data.numpy())
if step % 100 == 0: #每間隔100個batch執行一次打印等操作
net.eval() # 給網絡模型做標記,標志說模型在校驗集上運行
val_loss = [] #記錄校驗數據集准確率的容器
'''開始在校驗數據集上做循環,計算校驗集上面的准確度'''
idx = 0
for (data, target) in validation_loader:
target, data = data.clone().detach().requires_grad_(True), target.clone().detach()
idx += 1
if use_cuda:
target, data = target.cuda(), data.cuda()
data = data.type(dtype)
data = data.resize(data.size()[0], 1, 1, 1)
data = data.expand(data.size()[0], input_dim, 1, 1)
output = net(data) #完成一次前饋計算過程,得到目前訓練得到的模型net在校驗數據集上的表現
loss = criterion(output, target) #將output與標簽target比較,計算誤差
if use_cuda:
loss = loss.cpu()
val_loss.append(loss.data.numpy())
#打印誤差等數值,其中正確率為本訓練周期Epoch開始后到目前撮的正確率的平均值
print('訓練周期: {} [{}/{} ({:.0f}%)]\t訓練數據Loss: {:.6f}\t校驗數據Loss: {:.6f}'.format(
epoch, batch_idx * batch_size, len(train_loader.dataset),
100. * batch_idx / len(train_loader), np.mean(train_loss), np.mean(val_loss)))
record.append([np.mean(train_loss), np.mean(val_loss)])
# 產生一組圖像保存到temp1文件夾下(需要事先建立好該文件夾),檢測生成器當前的效果
# 改變輸入數字的尺寸,適應於生成器網絡
samples.data.resize_(batch_size,1,1,1)
samples = samples.data.expand(batch_size, input_dim, 1, 1)
samples = samples.cuda() if use_cuda else samples #加載到GPU
fake_u=net(samples) #用原始網絡作為輸入,得到偽造的圖像數據
fake_u = fake_u.cpu() if use_cuda else fake_u
img = make_show(fake_u) #將張量轉化成可繪制的圖像
vutil.save_image(img,'temp1/fake%s.png'% (epoch)) #保存生成的圖像
plt.figure(figsize = (10, 7))
plt.plot([i[0] for i in record], label='Training')
plt.plot([i[1] for i in record], label='Validation')
plt.xlabel('Batchs')
plt.ylabel('Loss')
plt.legend()
# 繪制一批圖像樣本 fake_u = fake_u.cpu() if use_cuda else fake_u samples = samples.cpu() if use_cuda else samples img = fake_u.data #將張量轉化成可繪制的圖像 fig = plt.figure(figsize = (15, 15)) # Two subplots, the axes array is 1-d f, axarr = plt.subplots(8,8, sharex=True, figsize=(15,15)) for i in range(batch_size): axarr[i // 8, i % 8].axis('off') imshow(img[i], samples.data.numpy()[i][0,0,0].astype(int), axarr[i // 8, i % 8])
# 定義待遷移的網絡框架,所有的神經網絡模塊包括:Conv2d、MaxPool2d,Linear等模塊都不需要重新定義,會自動加載 # 但是網絡的forward功能沒有辦法自動實現,需要重寫。 # 一般的,加載網絡只加載網絡的屬性,不加載方法 depth = [4, 8] class ConvNet(nn.Module): def __init__(self): super(ConvNet, self).__init__() def forward(self, x): x = F.relu(self.conv1(x)) x = self.pool(x) x = F.relu(self.conv2(x)) x = self.pool(x) # 將立體的Tensor全部轉換成一維的Tensor。兩次pooling操作,所以圖像維度減少了1/4 x = x.view(-1, image_size // 4 * image_size // 4 * depth[1]) x = F.relu(self.fc1(x)) #全鏈接,激活函數 x = F.dropout(x, training=self.training) #以默認為0.5的概率對這一層進行dropout操作 x = self.fc2(x) #全鏈接,激活函數 x = F.log_softmax(x, dim = 1) #log_softmax可以理解為概率對數值 return x def retrieve_features(self, x): #該函數專門用於提取卷積神經網絡的特征圖的功能,返回feature_map1, feature_map2為前兩層卷積層的特征圖 feature_map1 = F.relu(self.conv1(x)) #完成第一層卷積 x = self.pool(feature_map1) # 完成第一層pooling feature_map2 = F.relu(self.conv2(x)) #第二層卷積,兩層特征圖都存儲到了feature_map1, feature_map2中 return (feature_map1, feature_map2) def rightness(predictions, labels): """計算預測錯誤率的函數,其中predictions是模型給出的一組預測結果,batch_size行num_classes列的矩陣,labels是數據之中的正確答案""" pred = torch.max(predictions.data, 1)[1] # 對於任意一行(一個樣本)的輸出值的第1個維度,求最大,得到每一行的最大元素的下標 rights = pred.eq(labels.data.view_as(pred)).sum() #將下標與labels中包含的類別進行比較,並累計得到比較正確的數量 return rights, len(labels) #返回正確的數量和這一次一共比較了多少元素
我們加入了一個識別器,它通過固定值的方式遷移自一個手寫體識別器 然后讓生成器生成圖像,並讓識別器進行識別,將識別的誤差作為目標函數,調整生成器,從而能給出正確的分類標簽
# 定義待遷移的網絡框架,所有的神經網絡模塊包括:Conv2d、MaxPool2d,Linear等模塊都不需要重新定義,會自動加載 # 但是網絡的forward功能沒有辦法自動實現,需要重寫。 # 一般的,加載網絡只加載網絡的屬性,不加載方法 depth = [4, 8] class ConvNet(nn.Module): def __init__(self): super(ConvNet, self).__init__() def forward(self, x): x = F.relu(self.conv1(x)) x = self.pool(x) x = F.relu(self.conv2(x)) x = self.pool(x) # 將立體的Tensor全部轉換成一維的Tensor。兩次pooling操作,所以圖像維度減少了1/4 x = x.view(-1, image_size // 4 * image_size // 4 * depth[1]) x = F.relu(self.fc1(x)) #全鏈接,激活函數 x = F.dropout(x, training=self.training) #以默認為0.5的概率對這一層進行dropout操作 x = self.fc2(x) #全鏈接,激活函數 x = F.log_softmax(x, dim = 1) #log_softmax可以理解為概率對數值 return x def retrieve_features(self, x): #該函數專門用於提取卷積神經網絡的特征圖的功能,返回feature_map1, feature_map2為前兩層卷積層的特征圖 feature_map1 = F.relu(self.conv1(x)) #完成第一層卷積 x = self.pool(feature_map1) # 完成第一層pooling feature_map2 = F.relu(self.conv2(x)) #第二層卷積,兩層特征圖都存儲到了feature_map1, feature_map2中 return (feature_map1, feature_map2) def rightness(predictions, labels): """計算預測錯誤率的函數,其中predictions是模型給出的一組預測結果,batch_size行num_classes列的矩陣,labels是數據之中的正確答案""" pred = torch.max(predictions.data, 1)[1] # 對於任意一行(一個樣本)的輸出值的第1個維度,求最大,得到每一行的最大元素的下標 rights = pred.eq(labels.data.view_as(pred)).sum() #將下標與labels中包含的類別進行比較,並累計得到比較正確的數量 return rights, len(labels) #返回正確的數量和這一次一共比較了多少元素
netR = torch.load('minst_conv_checkpoint') #讀取硬盤上的minst_conv_checkpoint文件
netR = netR.cuda() if use_cuda else netR #加載到GPU中
for para in netR.parameters():
para.requires_grad = False #將識別器的權重設置為固定值
netR
#開始訓練 print('Initialized!') netG = ModelG() #新建一個生成器 netG = netG.cuda() if use_cuda else netG #加載到GPU上 netG.apply(weight_init) #初始化參數 criterion = nn.CrossEntropyLoss() #用交叉熵作為損失函數 optimizer = optim.SGD(netG.parameters(), lr=0.0001, momentum=0.9) #定義優化器 #隨機選擇batch_size個數字,用他們來生成數字圖像 samples = np.random.choice(10, batch_size) samples = torch.from_numpy(samples).type(dtype).requires_grad_(False) num_epochs = 100 #總訓練周期 statistics = [] #數據記載器 for epoch in range(num_epochs): train_loss = [] train_rights = [] # 加載數據 for batch_idx, (data, target) in enumerate(train_loader): # 注意圖像和標簽互換了 target, data = data.clone().detach().requires_grad_(True), target.clone().detach() #data為一批標簽,target為一批圖像 if use_cuda: target, data = target.cuda(), data.cuda() # 復制標簽變量放到了label中 label = data.clone() data = data.type(dtype) # 改變張量形狀以適用於生成器網絡 data = data.resize(data.size()[0], 1, 1, 1) data = data.expand(data.size()[0], input_dim, 1, 1) netG.train() # 給網絡模型做標記,標志說模型正在訓練集上訓練, netR.train() #這種區分主要是為了打開關閉net的training標志,從而決定是否運行dropout output1 = netG(data) #神經網絡完成一次前饋的計算過程,得到預測輸出output output = netR(output1) #用識別器網絡來做分類 loss = criterion(output, label) #將output與標簽target比較,計算誤差 optimizer.zero_grad() #清空梯度 loss.backward() #反向傳播 optimizer.step() #一步隨機梯度下降算法 step += 1 if use_cuda: loss = loss.cpu() train_loss.append(loss.data.numpy()) right = rightness(output, label) #計算准確率所需數值,返回數值為(正確樣例數,總樣本數) train_rights.append(right) #將計算結果裝到列表容器train_rights中 if step % 100 == 0: #每間隔100個batch執行一次打印等操作 netG.eval() # 給網絡模型做標記,標志說模型正在校驗集上運行, netR.eval() #這種區分主要是為了打開關閉net的training標志,從而決定是否運行dropout val_loss = [] #記錄校驗數據集准確率的容器 val_rights = [] '''開始在校驗數據集上做循環,計算校驗集上面的准確度''' for (data, target) in validation_loader: # 注意target是圖像,data是標簽 target, data = data.clone().detach().requires_grad_(True), target.clone().detach() if use_cuda: target, data = target.cuda(), data.cuda() label = data.clone() data = data.type(dtype) #改變Tensor大小以適應生成網絡 data = data.resize(data.size()[0], 1, 1, 1) data = data.expand(data.size()[0], input_dim, 1, 1) output1 = netG(data) #神經網絡完成一次前饋的計算過程,得到預測輸出output output = netR(output1) #利用識別器來識別 loss = criterion(output, label) #將output與標簽target比較,計算誤差 if use_cuda: loss = loss.cpu() val_loss.append(loss.data.numpy()) right = rightness(output, label) #計算准確率所需數值,返回正確的數值為(正確樣例數,總樣本數) val_rights.append(right) # 分別計算在目前已經計算過的測試數據集,以及全部校驗集上模型的表現:分類准確率 #train_r為一個二元組,分別記錄目前已經經歷過的所有訓練集中分類正確的數量和該集合中總的樣本數, #train_r[0]/train_r[1]就是訓練集的分類准確度,同樣,val_r[0]/val_r[1]就是校驗集上的分類准確度 train_r = (sum([tup[0] for tup in train_rights]), sum([tup[1] for tup in train_rights])) #val_r為一個二元組,分別記錄校驗集中分類正確的數量和該集合中總的樣本數 val_r = (sum([tup[0] for tup in val_rights]), sum([tup[1] for tup in val_rights])) print(('訓練周期: {} [{}/{} ({:.0f}%)]\t訓練數據Loss: {:.6f},正確率: {:.2f}%\t校驗數據Loss:' + '{:.6f},正確率:{:.2f}%').format(epoch, batch_idx * batch_size, len(train_loader.dataset), 100. * batch_idx / len(train_loader), np.mean(train_loss), 100. * train_r[0].numpy() / train_r[1], np.mean(val_loss), 100. * val_r[0].numpy() / val_r[1])) #記錄中間的數據 statistics.append({'loss':np.mean(train_loss),'train': 100. * train_r[0] / train_r[1], 'valid':100. * val_r[0] / val_r[1]}) # 產生一組圖像保存到temp1文件夾下(需要事先建立好該文件夾),檢測生成器當前的效果 samples.data.resize_(batch_size,1,1,1) samples = samples.data.expand(batch_size, input_dim, 1, 1) samples = samples.cuda() if use_cuda else samples fake_u=netG(samples) fake_u = fake_u.cpu() if use_cuda else fake_u img = make_show(fake_u) vutil.save_image(img,'temp1/fake%s.png'% (epoch))
# 訓練曲線 result1 = [100 - i['train'] for i in statistics] result2 = [100 - i['valid'] for i in statistics] plt.figure(figsize = (10, 7)) plt.plot(result1, label = 'Training') plt.plot(result2, label = 'Validation') plt.xlabel('Step') plt.ylabel('Error Rate') plt.legend()
#繪制一批樣本 samples = torch.Tensor([0,1,2,3,4,5,6,7,8,9]) samples = samples.type(dtype).clone() sample_size = 10 samples.data.resize_(sample_size,1,1,1) samples = samples.data.expand(sample_size, input_dim, 1, 1) samples = samples.cuda() if use_cuda else samples fake_u = netG(samples) fake_u = fake_u.cpu() if use_cuda else fake_u samples = samples.cpu() if use_cuda else samples img = fake_u #.expand(sample_size, 3, image_size, image_size) #將張量轉化成可繪制的圖像 #fig = plt.figure(figsize = (15, 6)) f, axarr = plt.subplots(2,5, sharex=True, figsize=(15,6)) for i in range(sample_size): axarr[i // 5, i % 5].axis('off') imshow(img[i].data, samples.data.numpy()[i][0,0,0].astype(int), axarr[i // 5, i % 5])
同樣的生成器,根據輸入的一個隨機噪聲向量生成手寫數字的圖像,然后再同時訓練一個辨別器,它的任務就是負責辨別一張輸入的圖像是來源於生成器 造假還是來源於原始的數據文件。我們將兩個網絡一起訓練。值得注意的是,辨別器和生成器的目標函數是反的。
具體訓練步驟:
1、讀取一個batch的原始數據,將圖像喂給辨別器,辨別器應該輸出為真,計算誤差:D_x,
2、用隨機噪聲輸入生成器,生成器創造一個batch的假圖片,將這些圖片輸入給辨別器,辨別器應該輸出為假,計算誤差D_x2
3、將兩個誤差和起來,反向傳播訓練辨別器
4、通過生成圖像計算誤差,對生成器進行反向傳播更新梯度
# 構造判別器 class ModelD(nn.Module): def __init__(self): super(ModelD,self).__init__() self.model=nn.Sequential() #序列化模塊構造的神經網絡 self.model.add_module('conv1',nn.Conv2d(num_channels, num_features, 5, 2, 0, bias=False)) #卷積層 self.model.add_module('relu1',nn.ReLU()) #激活函數使用了ReLu #self.model.add_module('relu1',nn.LeakyReLU(0.2, inplace = True)) #激活函數使用了leakyReLu,可以防止dead ReLu的問題 #第二層卷積 self.model.add_module('conv2',nn.Conv2d(num_features, num_features * 2, 5, 2, 0, bias=False)) self.model.add_module('bnorm2',nn.BatchNorm2d(num_features * 2)) self.model.add_module('linear1', nn.Linear(num_features * 2 * 4 * 4, #全鏈接網絡層 num_features)) self.model.add_module('linear2', nn.Linear(num_features, 1)) #全鏈接網絡層 self.model.add_module('sigmoid',nn.Sigmoid()) def forward(self,input): output = input # 對網絡中的所有神經模塊進行循環,並挑選出特定的模塊linear1,將feature map展平 for name, module in self.model.named_children(): if name == 'linear1': output = output.view(-1, num_features * 2 * 4 * 4) output = module(output) return output
# 構建一個生成器模型,並加載到GPU上 netG = ModelG().cuda() if use_cuda else ModelG() # 初始化網絡的權重 netG.apply(weight_init) print(netG) # 構建一個辨別器網絡,並加載的GPU上 netD=ModelD().cuda() if use_cuda else ModelD() # 初始化權重 netD.apply(weight_init) # 要優化兩個網絡,所以需要有兩個優化器 # 使用Adam優化器,可以自動調節收斂速度 #optimizerD=optim.SGD(netD.parameters(),lr=0.0002) #optimizerG=optim.SGD(netG.parameters(),lr=0.0002) optimizerD=optim.Adam(netD.parameters(),lr=0.0002,betas=(0.5,0.999)) optimizerG=optim.Adam(netG.parameters(),lr=0.0002,betas=(0.5,0.999)) # 模型的輸入輸出 # 生成一個隨機噪聲輸入給生成器 noise=torch.tensor((batch_size, input_dim, 1, 1), dtype = torch.float, requires_grad = True) #固定噪聲是用於評估生成器結果的,它在訓練過程中始終不變 fixed_noise=torch.FloatTensor(batch_size, input_dim, 1, 1).normal_(0,1).requires_grad_(True) if use_cuda: noise = noise.cuda() fixed_noise = fixed_noise.cuda() #BCE損失函數 criterion=nn.BCELoss() error_G = None #總誤差 num_epochs = 100 #訓練周期 results = [] for epoch in range(num_epochs): for batch_idx, (data, target) in enumerate(train_loader): #訓練辨別器網絡 #清空梯度 optimizerD.zero_grad() #1、輸入真實圖片 data, target = data.clone().detach().requires_grad_(True), target.clone().detach() # 用於鑒別贗品還是真品的標簽 label = torch.ones(data.size()[0]) #正確的標簽是1(真實) label = label.cuda() if use_cuda else label if use_cuda: data, target, label = data.cuda(), target.cuda(), label.cuda() netD.train() output=netD(data) #放到辨別網絡里辨別 #計算損失函數 label.data.fill_(1) error_real=criterion(output, label) error_real.backward() #辨別器的反向誤差傳播 D_x=output.data.mean() #2、用噪聲生成一張假圖片 noise.data.resize_(data.size()[0], input_dim, 1, 1).normal_(0, 1) #噪聲是一個input_dim維度的向量 #喂給生成器生成圖像 fake_pic=netG(noise).detach() #這里的detach是為了讓生成器不參與梯度更新 output2=netD(fake_pic) #用辨別器識別假圖像 label.data.fill_(0) #正確的標簽應該是0(偽造) error_fake=criterion(output2,label) #計算損失函數 error_fake.backward() #反向傳播誤差 error_D=error_real + error_fake #計算真實圖像和機器生成圖像的總誤差 optimizerD.step() #開始優化 # 單獨訓練生成器網絡 if error_G is None or np.random.rand() < 0.5: optimizerG.zero_grad() #清空生成器梯度 '''注意生成器的目標函數是與辨別器的相反的,故而當辨別器無法辨別的時候為正確''' label.data.fill_(1) #分類標簽全部標為1,即真實圖像 noise.data.normal_(0,1) #重新隨機生成一個噪聲向量 netG.train() fake_pic=netG(noise) #生成器生成一張偽造圖像 output=netD(fake_pic) #辨別器進行分辨 error_G=criterion(output,label) #辨別器的損失函數 error_G.backward() #反向傳播 optimizerG.step() #優化網絡 if use_cuda: error_D = error_D.cpu() error_G = error_G.cpu() # 記錄數據 results.append([float(error_D.data.numpy()), float(error_G.data.numpy())]) # 打印分類器損失等指標 if batch_idx % 100 == 0: print ('第{}周期,第{}/{}撮, 分類器Loss:{:.2f}, 生成器Loss:{:.2f}'.format( epoch,batch_idx,len(train_loader), error_D.data.item(), error_G.data.item())) #生成一些隨機圖片,但因輸出到文件 netG.eval() fake_u=netG(fixed_noise) fake_u = fake_u.cpu() if use_cuda else fake_u img = make_show(fake_u) #挑選一些真實數據中的圖像圖像保存 data, _ = next(iter(train_loader)) vutil.save_image(img,'temp/fake%s.png'% (epoch)) # 保存網絡狀態到硬盤文件 torch.save(netG.state_dict(), '%s/netG_epoch_%d.pth' % ('net', epoch)) torch.save(netD.state_dict(), '%s/netD_epoch_%d.pth' % ('net', epoch)) if epoch == 0: img = make_show(data.clone().detach().requires_grad_(True)) vutil.save_image(img,'temp/real%s.png' % (epoch))
# 預測曲線 plt.figure(figsize = (10, 7)) plt.plot([i[1] for i in results], '.', label = 'Generator', alpha = 0.5) plt.plot([i[0] for i in results], '.', label = 'Discreminator', alpha = 0.5) plt.xlabel('Step') plt.ylabel('Loss') plt.legend()
# 繪制一些樣本 noise = torch.FloatTensor(batch_size, input_dim, 1, 1) noise.data.normal_(0,1) noise = noise.cuda() if use_cuda else noise sample_size = batch_size netG.eval() fake_u = netG(noise) fake_u = fake_u.cpu() if use_cuda else fake_u noise = noise.cpu() if use_cuda else samples img = fake_u #.expand(sample_size, 3, image_size, image_size) #將張量轉化成可繪制的圖像 #print(img.size()) f, axarr = plt.subplots(8,8, sharex=True, figsize=(15,15)) for i in range(batch_size): axarr[i // 8, i % 8].axis('off') imshow(img[i].data, None,axarr[i // 8, i % 8])