1. 生成模型與判別模型區別
判別模型: 直接求P(Y|X)
生成模型: 通過P(X,Y) / P(X) 求 P(Y|X) ,eg:隱馬爾可夫鏈
2. 爲什麼要用生成模型?
- 對高維數據和樣本分佈問題有很好的檢測
- 模擬強化學習(RL)
- 數據缺失,半監督學習
- 多模態(multy-modal)輸出,eg:可能生出三隻眼的狗,生成結果不好
- 現實的生成任務,eg:給定一個groud truth的圖片,將目標圖片上面的馬賽克去掉。
eg:生成像素更高更清楚的圖片
eg: 圖片到圖片的翻譯,完全是另外一個維度另外一個長相,生成所需求的圖像。
3. 生成模型原理 —— 最大似然估計
通過最大似然估計,把整個數據分佈給計算出來。
問題引出:
已知經典的貝葉斯公式
可惜現實生活中,先驗概率或條件概率往往未知,所以要估計:
-
先驗概率的估計較簡單,1、每個樣本所屬的自然狀態都是已知的(有監督學習);2、依靠經驗;3、用訓練樣本中各類出現的頻率估計。
-
類條件概率的估計(非常難),原因包括:1、概率密度函數包含了一個隨機變量的全部信息;2、樣本數據可能不多;3、特徵向量x的維度可能很大等等。總之要直接估計類條件概率的密度函數很難。解決的辦法就是,把估計完全未知的概率密度轉化爲估計參數。 這裏就將概率密度估計問題轉化爲參數估計問題,極大似然估計就是一種參數估計方法。
所以得出估計的一個重要前提: 訓練樣本的分佈能代表樣本的真實分佈。 每個樣本集中的樣本都是所謂獨立同分布的隨機變量,且有充分的訓練樣本。
具體的步驟:
①獨立採樣, 可以只考慮一類樣本集D,來估計參數向量θ。
② 似然(linkehood)估計:聯合概率密度函數稱爲相對於
的θ的似然函數。
③求解極大似然函數
ML估計:求使得出現該組樣本的概率最大的θ值。
實際中爲了便於分析,定義了對數似然函數:
-
未知參數只有一個(θ爲標量)
在似然函數滿足連續、可微的正則條件下,極大似然估計量是下面微分方程的解:
-
未知參數有多個(θ爲向量)
則θ可表示爲具有S個分量的未知向量:
記梯度算子:
若似然函數滿足連續可導的條件,則最大似然估計量就是如下方程的解。
方程的解只是一個估計值,只有在樣本數趨於無限多的時候,它纔會接近於真實值。
引用:極大似然估計詳解
4.生成模型大家族
5.生成對抗網絡
- 隱性code來控制數據的隱含關係
- 數據會逐漸統一(不像變分方法)
- 沒有馬爾科夫鏈的需要
- 目前可以生成最好的樣本 (因爲目前沒有好方法去評估模型)
生成G 與 判別D
D 爲 Discriminator 判別器
G 爲 Generator 生成器
- 判別器對假數據的損失原理相同,最終達到的目標是對於所有的真實圖片,輸出爲1;對於所有的假圖片,輸出爲0;
- 生成器的目標是從噪音輸入(不帶樣本,且符合一定的分佈)生成圖片,愚弄判別器矇混過關,需要達到的目標是對於生成的圖片,輸出爲1(正好和鑑別器相反).
最終看重的是G的效果。
必要條件
z 是噪音輸入(不帶樣本,且符合一定的分佈),x是生成的樣本
- 必須可微
- 沒有可逆的需要(不需要能從x反推回z)
- 可以訓練任何size的z
- 保證z的維度比x高
訓練過程
- 同時在兩個小批量數據(判別數據、生成數據)上使用SGD相關的方法(可以選擇Adam)。
- 優化trick:可以讓生成器(one player)訓練得比判別器(the other player)學習得快一點。
三種生成損失函數
①
生成和真實差別越大,D的損失向負方向擴大,G的損失向正方向擴大。
②
- D和G的損失函數不再綁定
- G最大化使得D誤判的對數概率
- 可以先讓D訓練得不錯,再讓G加入戰局
③ KL散度 (Kullback–Leibler divergence)
衡量兩個分佈的差距
假設 p 是真實分佈,q是生成分佈
左圖,正KL(最大似然):激進派,q擬合p,目的是把所有的分佈都擬合進去,如果是雙分佈的話可能沒辦法擬合得好;
右圖,逆 KL :保守派,p擬合q,目的是能擬合好一個分佈就算完成任務了。
eg:兩個頭的狗進行生成,用最大似然可能生成兩倍大的頭,用逆KL可能是生成只有一個頭的狗,不會爲了滿足所有的分佈,而更接近於真實。
應用場景
DCGAN (Deep Convolution GAN) : 一維數組映射到圖片
大約等於 CNN 的逆過程
Keras實現代碼
-
導入庫
%matplotlib inline import os,random os.environ["KERAS_BACKEND"] = "tensorflow" import numpy as np import theano as th import theano.tensor as T from keras.utils import np_utils import keras.models as models from keras.layers import Input,merge from keras.layers.core import Reshape,Dense,Dropout,Activation,Flatten from keras.legacy.layers import MaxoutDense from keras.layers.advanced_activations import LeakyReLU from keras.activations import * from keras.layers.wrappers import TimeDistributed from keras.layers.noise import GaussianNoise from keras.layers.convolutional import Convolution2D, MaxPooling2D, ZeroPadding2D, Deconv2D, UpSampling2D from BatchNorm_GAN import BatchNormGAN from keras.layers.recurrent import LSTM from keras.regularizers import * from keras.layers.normalization import * from keras.optimizers import * from keras.datasets import mnist import matplotlib.pyplot as plt import pickle, random, sys, keras from keras.models import Model from IPython import display sys.path.append("../common") from keras.utils import np_utils from tqdm import tqdm K.set_image_dim_ordering('th')
-
準備訓練集
#28 x 28 的圖片 img_rows, img_cols = 28, 28 #把數據集洗一洗 (X_train, y_train), (X_test, y_test) = mnist.load_data() #排好 X_train = X_train.reshape(X_train.shape[0], 1, img_rows, img_cols) X_test = X_test.reshape(X_test.shape[0], 1, img_rows, img_cols) X_train = X_train.astype('float32') X_test = X_test.astype('float32') X_train /= 255 X_test /= 255 #看看我們所有數據的個數和長相 print(np.min(X_train), np.max(X_train)) print('X_train shape:', X_train.shape) print(X_train.shape[0], 'train samples') print(X_test.shape[0], 'test samples')
輸出
-
做個指示器,告訴算法,現在這個net(要麼是dis要麼是gen),能不能被繼續train
def make_trainable(net, val): net.trainable = val for l in net.layers: l.trainable = val
-
搭建網絡結構
# 看一個每一個訓練數據的長相 shp = X_train.shape[1:] print(shp) dropout_rate = 0.25 # 設置gen和dis的opt # 大家可以嘗試各種組合 opt = Adam(lr=1e-3) dopt = Adam(lr=1e-4) #opt = Adam(lr=1e-3) #opt = Adamax(lr=1e-4) #opt = Adam(lr=0.0002) #opt = Adam(lr=0.0002, beta_1=0.5, beta_2=0.999, epsilon=1e-08) nch = 200 # 造個GEN nch = 200 g_input = Input(shape=[100]) # 倒過來的CNN第一層(也就是普通CNN那個flatten那一層) H = Dense(nch*14*14, init='glorot_normal')(g_input) H = BatchNormGAN()(H) H = Activation('relu')(H) H = Reshape( [nch, 14, 14] )(H) # upscale上去2倍大。也就是從14x14 到 28x28 H = UpSampling2D(size=(2, 2))(H) # CNN濾鏡 H = Convolution2D(int(nch/2), 3, 3, border_mode='same', init='glorot_uniform')(H) H = BatchNormGAN()(H) H = Activation('relu')(H) # CNN濾鏡 H = Convolution2D(int(nch/4), 3, 3, border_mode='same', init='glorot_uniform')(H) H = BatchNormGAN()(H) H = Activation('relu')(H) # 合成一個大圖片 H = Convolution2D(1, 1, 1, border_mode='same', init='glorot_uniform')(H) g_V = Activation('sigmoid')(H) generator = Model(g_input,g_V) generator.compile(loss='binary_crossentropy', optimizer=opt) generator.summary()
輸出
# 造個DIS
# 這就是一個正常的CNN
d_input = Input(shape=shp)
# 濾鏡
H = Convolution2D(256, 5, 5, subsample=(2, 2), border_mode = 'same', activation='relu')(d_input)
H = LeakyReLU(0.2)(H)
H = Dropout(dropout_rate)(H)
# 濾鏡
H = Convolution2D(512, 5, 5, subsample=(2, 2), border_mode = 'same', activation='relu')(H)
H = LeakyReLU(0.2)(H)
H = Dropout(dropout_rate)(H)
H = Flatten()(H)
# flatten之後,接MLP
H = Dense(256)(H)
H = LeakyReLU(0.2)(H)
H = Dropout(dropout_rate)(H)
# 出一個結果,『是』或者『不是』
d_V = Dense(2,activation='softmax')(H)
discriminator = Model(d_input,d_V)
discriminator.compile(loss='categorical_crossentropy', optimizer=dopt)
discriminator.summary()
輸出
make_trainable(discriminator, False)
# 爲stacked GAN做準備
# 然後合成一個GAN的構架
gan_input = Input(shape=[100])
H = generator(gan_input)
gan_V = discriminator(H)
GAN = Model(gan_input, gan_V)
GAN.compile(loss='categorical_crossentropy', optimizer=opt)
GAN.summary()
輸出
-
兩個畫圖功能
def plot_loss(losses): display.clear_output(wait=True) display.display(plt.gcf()) plt.figure(figsize=(10,8)) plt.plot(losses["d"], label='discriminitive loss') plt.plot(losses["g"], label='generative loss') plt.legend() plt.show() def plot_gen(n_ex=16,dim=(4,4), figsize=(10,10) ): noise = np.random.uniform(0,1,size=[n_ex,100]) generated_images = generator.predict(noise) plt.figure(figsize=figsize) for i in range(generated_images.shape[0]): plt.subplot(dim[0],dim[1],i+1) img = generated_images[i,0,:,:] plt.imshow(img) plt.axis('off') plt.tight_layout() plt.show()
-
正式訓練
# 設置一下存儲D和G的格式 losses = {"d":[], "g":[]} # 訓練正經去了 def train_for_n(nb_epoch=5000, plt_frq=25,BATCH_SIZE=32): for e in tqdm(range(nb_epoch)): # 生成圖片 image_batch = X_train[np.random.randint(0,X_train.shape[0],size=BATCH_SIZE),:,:,:] noise_gen = np.random.uniform(0,1,size=[BATCH_SIZE,100]) generated_images = generator.predict(noise_gen) # 訓練DIS X = np.concatenate((image_batch, generated_images)) y = np.zeros([2*BATCH_SIZE,2]) y[0:BATCH_SIZE,1] = 1 y[BATCH_SIZE:,0] = 1 # 當然,要讓DIS可以被訓練 make_trainable(discriminator,True) d_loss = discriminator.train_on_batch(X,y) #返回loss的標量值或list losses["d"].append(d_loss) # 訓練 Generator-Discriminator stack noise_tr = np.random.uniform(0,1,size=[BATCH_SIZE,100]) y2 = np.zeros([BATCH_SIZE,2]) y2[:,1] = 1 # 這個時候,讓DIS不能被變化。保證判斷結果一致性 make_trainable(discriminator,False) g_loss = GAN.train_on_batch(noise_tr, y2 ) losses["g"].append(g_loss) # 看看新圖長什麼樣子 if e%plt_frq==plt_frq-1: plot_loss(losses) plot_gen() train_for_n(nb_epoch=250, plt_frq=25,BATCH_SIZE=128)
輸出