【生成模型】解读显式生成模型之完全可见置信网络FVBN
上一期為大家說明了什么是極大似然法,以及如何使用極大似然法搭建生成模型,本期將為大家介紹第一個顯式生成模型完全可見置信網絡FVBN。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 作者&編輯 | 小米粥
1 完全可見置信網絡
在完全可見置信網絡中,不存在不可觀察的潛在變量,觀察變量的概率被鏈式法則從維度上進行分解,對于 n 維觀察變量x ,其概率表達式為:
自回歸網絡是最簡單的完全可見置信網絡,其中每一個維度的觀察變量都構成概率模型的一個節點,而這些所有的節點{x1,x2,...,xn}共同構成一個完全有向圖,即圖中任意兩個節點都存在連接關系,如圖所示。
在自回歸網絡中,因為已經有了隨機變量的鏈式分解關系,那么核心問題便成為如何表達條件概率p(xi|xi-1,xx-2,...,x1) 。最簡單的模型是線性自回歸網絡,即每個條件概率均被定義為線性模型,對實數值數據使用線性回歸模型(例如定義 p(xi|xi-1,xx-2,...,x1)= w1x1+w2x2+...+wi-1xi-1?,對二值數據使用邏輯回歸,而對離散數據使用softmax回歸,其計算過程如下圖。
但線性模型容量有限,擬合函數的能力不足。在神經自回歸網絡中,使用神經網絡代替線性模型,它可以任意增加容量,理論上可以擬合任意聯合分布。神經自回歸網絡還使用了特征重用的技巧,神經網絡從觀察變量 xi 學習到的隱藏抽象特征 hi 不僅在計算p(xi+1|xi,xi-1,...,x1)時使用,也會在計算p(xi+2|xi+1,xi,...,x1)時進行重用,其計算圖如下所示,并且該模型不需要將每個條件概率的計算都分別使用不同神經網絡表示,可以將所有神經網絡整合為一個,因此只要設計成抽象特征hi只依賴于x1,x2,...,xi即可。而目前的神經自回歸密度估計器是神經自回歸網絡中最具有代表性的方案,它是在神經自回歸網絡中引入了參數共享的方案,即從觀察變量xi到任意隱藏抽象特征 hi+1,hi+2,... 的權值參數是共享的,使用了特征重用、參數共享等深度學習技巧的神經自回歸密度估計器具有非常優秀的性能。
PixelRNN和PixelCNN也屬于完全可見置信網絡,從名字可以看出,這兩個模型一般用于圖像的生成。它們將圖像x的概率p(x)按照像素分解為 n 個條件概率的乘積,其中n為圖像的像素點個數,即在每一個像素點上定義了一個條件概率用以表達像素之間的依賴關系,該條件概率分別使用RNN或者CNN進行學習。為了將輸出離散化,通常將RNN或CNN的最后一層設置為softmax層,用以表示其輸出不同像素值的概率。在PixelRNN中,一般定義從左上角開始沿著右方和下方依次生成每一個像素點,如下圖所示。這樣,對數似然的表達式便可以得到,訓練模型時只需要將其極大化即可。
PixelRNN在其感受野內可能具有無邊界的依賴范圍,因為待求位置的像素值依賴之前所有已知像素點的像素值,這將需要大量的計算代價,PixelCNN使用標準卷積層來捕獲有界的感受野,其訓練速度要快于PixelRNN。在PixelCNN中,每個位置的像素值僅與其周圍已知像素點的值有關,如下圖所示。灰色部分為已知像素,而白色部分為未知像素,計算黑色位置的像素值時,需要把方框區域內的所有灰色像素值傳遞給CNN,由CNN最后的softmax輸出層來表達表在黑色位置取不同像素值的概率,這里可以使用由0和1構成的掩模矩陣將方框區域內的白色位置像素抹掉。PixelRNN和PixelCNN此后仍有非常多改進模型,但由于它是逐個像素點地生成圖片,具有串行性,故在實際應用中效率難以保證,這也是FVBN模型的通病。
2 pixelCNN 代碼
接下來我們將提供一份完整的pixelCNN的代碼講解,其中訓練集為mnist數據集。
首先讀取相關python庫,設置訓練參數:
# 讀取相關庫?
import time?
import torch?
import torch.nn.functional as F?
from torch import nn, optim, cudafrom torch.utils?
import datafrom torchvision import datasets, transforms, utils?
# 設置訓練參數?
train_batch_size = 256?
generation_batch_size = 48?
epoch_number = 25feature_dim = 64?
# 是否使用GPU?
if torch.cuda.is_available(): ? ?
????device = torch.device('cuda:0')?
else: ? ?
????device = torch.device('cpu')
然后定義二維掩膜卷積,所謂掩膜即使卷積中心的右方和下方的權值為0,如下圖所示為3x3掩膜卷積核(A型):
定義二維掩膜卷積核,其中有A與B兩種類型,區別之處在于中心位置是否被卷積計算:
class MaskedConv2d(nn.Conv2d):
?? ?def __init__(self, mask_type, *args, **kwargs):
? ? ? ?super(MaskedConv2d, self).__init__(*args, **kwargs)
? ? ? ?assert mask_type in {'A', 'B'}
? ? ? ?self.register_buffer('mask', self.weight.data.clone())
? ? ? ?bs, o_feature_dim, kH, kW = self.weight.size()
? ? ? ?self.mask.fill_(1)
? ? ? ?self.mask[:, :, kH // 2, kW // 2 + (mask_type == 'B'):] = 0 ? ? ? ?
????????self.mask[:, :, kH // 2 + 1:] = 0
????def forward(self, x): ? ? ? ?
????????self.weight.data *= self.mask ? ? ? ?
????????return super(MaskedConv2d, self).forward(x)
我們的pixelCNN網絡為多層掩膜卷積的堆疊,即:
network = nn.Sequential(
????MaskedConv2d('A',1,feature_dim,7,1,3, bias=False),nn.BatchNorm2d(feature_dim),nn.ReLU(True),? ? ?
????MaskedConv2d('B', feature_dim, feature_dim, 7, 1, 3, bias=False), nn.BatchNorm2d(feature_dim), nn.ReLU(True),? ??
????MaskedConv2d('B', feature_dim, feature_dim, 7, 1, 3, bias=False), nn.BatchNorm2d(feature_dim), nn.ReLU(True), ? ?
????MaskedConv2d('B', feature_dim, feature_dim, 7, 1, 3, bias=False), nn.BatchNorm2d(feature_dim), nn.ReLU(True), ? ?
????MaskedConv2d('B', feature_dim, feature_dim, 7, 1, 3, bias=False), nn.BatchNorm2d(feature_dim), nn.ReLU(True), ? ?
????MaskedConv2d('B', feature_dim, feature_dim, 7, 1, 3, bias=False), nn.BatchNorm2d(feature_dim), nn.ReLU(True), ? ?
????MaskedConv2d('B', feature_dim, feature_dim, 7, 1, 3, bias=False), nn.BatchNorm2d(feature_dim), nn.ReLU(True), ? ?
????MaskedConv2d('B', feature_dim, feature_dim, 7, 1, 3, bias=False), nn.BatchNorm2d(feature_dim), nn.ReLU(True), ? ?nn.Conv2d(feature_dim, 256, 1))?
network.to(device)
接著設置dataloader和優化器:
train_data = data.DataLoader(datasets.MNIST('data', train=True, download=True, transform=transforms.ToTensor()), ? ? ? ? ? ? ? ? ? ? batch_size=train_batch_size, shuffle=True, num_workers=1, pin_memory=True)?
test_data = data.DataLoader(datasets.MNIST('data', train=False, download=True, transform=transforms.ToTensor()), ? ? ? ? ? ? ? ? ? ? batch_size=train_batch_size, shuffle=False, num_workers=1, pin_memory=True)?
?optimizer = optim.Adam(network.parameters())
開始訓練網絡,并在每一輪epoch后進行測試和生成樣本
if __name__ == "__main__":
?? ?for epoch in range(epoch_number):
?? ? ? ?# 訓練
?? ? ? ?cuda.synchronize()
?? ? ? ?network.train(True)
?? ? ? ?for input_image, _ in train_data:
?? ? ? ? ? ?time_tr = time.time()
?? ? ? ? ? ?input_image = input_image.to(device)
?? ? ? ? ? ?output_image = network(input_image)
?? ? ? ? ? ?target = (input_image.data[:, 0] * 255).long().to(device)
?? ? ? ? ? ?loss = F.cross_entropy(output_image, target)
?? ? ? ? ? ?optimizer.zero_grad()
?? ? ? ? ? ?loss.backward()
?? ? ? ? ? ?optimizer.step()
?? ? ? ? ? ?print("train: {} epoch, loss: {}, cost time: {}".format(epoch, loss.item(), time.time() - time_tr)) ? ? ? ?cuda.synchronize()
?? ? ? ?# 測試
?? ? ? ?with torch.no_grad():
?? ? ? ? ? ?cuda.synchronize()
?? ? ? ? ? ?time_te = time.time()
?? ? ? ? ? ?network.train(False)
?? ? ? ? ? ?for input_image, _ in test_data:? ? ? ? ? ? ? ? ????????????? ? ? ? ? ? ? ? ? input_image = input_image.to(device)
?? ? ? ? ? ? ? ?target = (input_image.data[:, 0] * 255).long().to(device)
?? ? ? ? ? ? ? ?loss = F.cross_entropy(network(input_image), target)
?? ? ? ? ? ?cuda.synchronize()
?? ? ? ? ? ?time_te = time.time() - time_te
?? ? ? ? ? ?print("test: {} epoch, loss: {}, cost time: {}".format(epoch, loss.item(), time_te)) ? ? ? ?
# 生成樣本
?? ? ? ?with torch.no_grad():
?? ? ? ? ? ?image = torch.Tensor(generation_batch_size, 1, 28, 28).to(device)
?? ? ? ? ? ?image.fill_(0)
?? ? ? ? ? ?network.train(False)
?? ? ? ? ? ?for i in range(28):
?? ? ? ? ? ? ? ?for j in range(28):
?? ? ? ? ? ? ? ? ? ?out = network(image)
?? ? ? ? ? ? ? ? ? ?probs = F.softmax(out[:, :, i, j]).data
?? ? ? ? ? ? ? ? ? ?image[:, :, i, j] = torch.multinomial(probs, 1).float() / 255.
?? ? ? ? ? ?utils.save_image(image, 'generation-image_{:02d}.png'.format(epoch), nrow=12, padding=0)
[1]?Oord A V D , Kalchbrenner N , Kavukcuoglu K . Pixel Recurrent Neural Networks[J]. 2016.
[2] 伊恩·古德費洛, 約書亞·本吉奧, 亞倫·庫維爾. 深度學習
總結
本期帶大家學習了第一種顯式生成模型完全可見置信網絡,并對其中的自回歸網絡和pixelRNN,pixelCNN做了講解,并講解了一份完整的pixelCNN代碼。下一期我們將對第二個顯式模型流模型進行講解。
個人知乎,歡迎關注
GAN群
有三AI建立了一個GAN群,便于有志者相互交流。感興趣的同學也可以微信搜索xiaozhouguo94,備注“加入有三-GAN群”。
更多GAN的學習
知識星球是有三AI的付費內容社區,里面包超過100種經典GAN模型的解讀,了解詳細請閱讀以下文章:
【雜談】有三AI知識星球指導手冊出爐!和公眾號相比又有哪些內容?
有三AI秋季劃GAN學習小組,可長期跟隨有三學習GAN相關的內容,并獲得及時指導,了解詳細請閱讀以下文章:
【雜談】如何讓2020年秋招CV項目能力更加硬核,可深入學習有三秋季劃4大領域32個方向
轉載文章請后臺聯系
侵權必究
往期精選
【GAN優化】GAN優化專欄上線,首談生成模型與GAN基礎
【GAN的優化】從KL和JS散度到fGAN
【GAN優化】詳解對偶與WGAN
【GAN優化】詳解SNGAN(頻譜歸一化GAN)
【GAN優化】一覽IPM框架下的各種GAN
【GAN優化】GAN優化專欄欄主小米粥自述,腳踏實地,莫問前程
【GAN優化】GAN訓練的幾個問題
【GAN優化】GAN訓練的小技巧
【GAN優化】從動力學視角看GAN是一種什么感覺?
【GAN優化】小批量判別器如何解決模式崩潰問題
【GAN優化】長文綜述解讀如何定量評價生成對抗網絡(GAN)
【技術綜述】有三說GANs(上)
【模型解讀】歷數GAN的5大基本結構
【百戰GAN】如何使用GAN拯救你的低分辨率老照片
【百戰GAN】二次元宅們,給自己做一個專屬動漫頭像可好!
【百戰GAN】羨慕別人的美妝?那就用GAN復制粘貼過來
【百戰GAN】GAN也可以拿來做圖像分割,看起來效果還不錯?
【百戰GAN】新手如何開始你的第一個生成對抗網絡(GAN)任務
【百戰GAN】自動增強圖像對比度和顏色美感,GAN如何做?
【直播回放】80分鐘剖析GAN如何從各個方向提升圖像的質量
【直播回放】60分鐘剖析GAN如何用于人臉的各種算法
總結
以上是生活随笔為你收集整理的【生成模型】解读显式生成模型之完全可见置信网络FVBN的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【生成模型】极大似然估计,你必须掌握的概
- 下一篇: 【杂谈】有三AI季划成员的4大专属权益,