點擊下方卡片,關注「新機器視覺」公眾號
重磅乾貨,第一時間送達
從隱圖像空間進行採樣以創建全新的圖像或編輯現有圖像是目前創作AI最受歡迎和最成功的應用方式。
圖像隱空間取樣圖像生成的關鍵思想是開發表示的低維潛在空間(自然是矢量空間),其中任何點都可以映射到逼真的圖像上。能夠實現該映射的模塊,將潛在點作為輸入並輸出圖像(像素網格),被稱為生成器(在GAN的情況下)或解碼器(在VAE的情況下)。一旦開發出這樣的潛在空間,可以有意或無意地從中採樣點,並通過將它們映射到圖像空間,生成以前從未見過的圖像。
GAN和VAE是用於學習圖像表示的潛在空間的兩種不同策略,每種都具有其自身的特徵。VAE非常適合學習結構良好的潛在空間,其中特定方向編碼數據能產生有意義的變化軸。GAN生成的圖像可能非常逼真,但它們來自潛在的空間可能沒有那麼多的結構和連續性。
給定潛在的表示空間或嵌入空間,空間中的某些方向可以編碼原始數據中有趣的變化軸。例如,在面部圖像的潛在空間中,可能存在微笑矢量s,使得如果潛在點z是某個面部的嵌入表示,則潛在點z+s是同一面部的嵌入表示,面帶微笑。一旦確定了這樣的矢量,就可以通過將圖像投影到潛在空間中來編輯圖像,以有意義的方式移動它們的表示,然後將它們解碼回圖像空間。
變分自動編碼器變分自動編碼器,是一種生成模型,特別適用於通過概念向量進行圖像編輯的任務。它們是自動編碼器的現代版本 - 一種旨在將輸入編碼到低維潛在空間然後將其解碼回來的網絡 - 將來自深度學習的想法與貝葉斯推理混合在一起.經典圖像自動編碼器通過編碼器模塊拍攝圖像,將其映射到潛在的矢量空間,然後通過解碼器模塊將其解碼回與原始圖像具有相同尺寸的輸出。然後通過使用與輸入圖像相同的圖像作為目標數據來訓練,這意味着自動編碼器學習重建原始輸入。通過對代碼(編碼器的輸出)施加各種約束,可以使自動編碼器學習或多或少有趣的數據潛在表示。最常見的是,將限制代碼為低維和稀疏(大多數為零),在這種情況下,編碼器可以將輸入數據壓縮為更少的信息位。
在實踐中,這種經典的自動編碼器不會導致特別有用或結構良好的潛在空間,也不太擅長數據壓縮。由於這些原因,他們已經基本上不再流行。然而,VAE用統計方法增強了自動編碼器,迫使他們學習連續的,高度結構化的潛在空間。它們已成為圖像生成的強大工具。
VAE不是將其輸入圖像壓縮為潛在空間中的固定代碼,而是將圖像轉換為統計分布的參數:均值和方差。從本質上講,這意味着假設輸入圖像是由統計過程生成的,並且此過程的隨機性應在編碼和解碼期間用於計算。然後,VAE使用均值和方差參數隨機採樣分布的一個元素,並將該元素解碼回原始輸入。該過程的隨機性提高了魯棒性並迫使潛在空間在任何地方編碼有意義的表示:在潛在空間中採樣的每個點被解碼為有效輸出。
數學描述,VAE工作過程:
編碼器模塊將輸入樣本input_img轉換為表示的隱空間的z_mean和z_log_variance兩個參數;
通過z=z_mean + exp(z_log_variance)*epsilon 從假定生成輸入圖像的潛在正態分布中隨機採樣點z,其中epsilon是小值的隨機張量;
解碼器模塊將隱空間中的z點映射回原始輸入圖像。
因為epsilon是隨機的,所以該過程確保接近編碼input_img(z-mean)的潛在位置的每個點都可以被解碼為類似於input_img的東西,從而迫使潛在空間持續有意義。潛在空間中的任何兩個閉合點將解碼為高度相似的圖像。連續性與潛在空間的低維度相結合,迫使潛在空間中的每個方向編碼有意義的數據變化軸,使得潛在空間非常結構化,因此非常適合通過概念向量進行操縱。
VAE的參數通過兩個損失函數進行訓練:強制解碼樣本與初始輸入匹配的重建損失函數,以及有助於學習良好的隱空間並減少過度擬合訓練數據的正則化損失函數。讓我們快速了解一下VAE的Keras實現。原理上,它看起來像這樣:
z_mean,z_log_variance = encoder(input_img)#輸入編碼成均值、方法參數z = z_mean + exp(z_log_variance)*epsilon#隱空間通過epsilon取樣reconstructed_img = decoder(z)#取樣點生成新圖片model = Model(input_img,reconstructed_img)#實例化模型:輸入圖片映射到新建圖片上,之後訓練模型定義後,使用重建損失函數和正則損失訓練模型。使用一個簡單的convnet將輸入圖片映射到隱空間的概率分布上,得到兩個向量z_mean,z_log_var。
VAE Encoder網絡
import kerasfrom keras import layersfrom keras import backend as Kfrom keras.models import Modelimport numpy as npimg_shape = (28,28,1)batch_size = 16latent_dim = 2input_img = keras.Input(shape=img_shape)x = layers.Conv2D(32,3,padding='same',activation='relu')(input_img)x = layers.Conv2D(64,3,padding='same',activation='relu',stride=(2,2))(x)x = layers.Conv2D(64,3,padding='same',activation='relu')(x)x = layers.Conv2D(64,3,padding='same',activation='relu')(x)shape_before_flattening = K.int_shape(x)x = layers.Flatten()(x)x = layers.Dense(32,activation='relu')(x)#輸入圖片最終 編碼成 兩個參數z_mean = layers.Dense(latent_dim)(x)z_log_var = layers.Dense(latent_dim)(x)之後使用輸入圖片的假設空間分布特徵z_mean和z_log_var得到隱空間取樣點z。在這裡,將一些任意代碼(構建在Keras後端基元之上)包裝到Lambda層中。在Keras中,一切都需要是一個層,因此不屬於內置層的代碼應該包裝在Lambda(或自定義層)中.
隱空間取樣函數
def sampling(args): z_mean,z_log_var = args epsilon=K.random_normal(shape=(K.shape(z_mean)[0], latent_dim),mean=0.,stddev=1.) return z_mean + K.exp(z_log_var)*epsilonz = layers.Lambda(sampling)([z_mean,z_log_var])解碼器部分實現。將向量z reshape到圖片尺寸,最後經過幾個卷積層得到最終的圖片輸出。VAE decoder網絡:隱變量空間到圖片
decoder_input = layers.Input(K.int_shape(z)[1:])#輸入z向量x = layers.Dense(np.prod(shape_before_flattening[1:]),activation='relu')(decoder_input)x = layers.Reshape(shape_before_flattening[1:])(x)x = layers.Conv2DTranspose(32,3,padding='same',activation='relu',strides=(2,2))(x)x = layers.Conv2D(1,3,padding='same',activation='sigmoid')(x)decoder = Model(decoder_input, x)#實例化模型,模型將輸入decoder_input轉換成圖片z_decoded = decoder(z)#輸入z,得到最終轉換後的輸出圖片VAE的雙重損失函數不符合傳統形式損失函數(輸入,目標)的預期。因此,將通過編寫內部使用內置add_loss圖層方法來創建任意損失的自定義圖層來設置損失函數。定義圖層計算損失函數
class CustomVariationalLayer(keras.layers.Layer): def vae_loss(self, x, z_decoded): x = K.flatten(x) z_decoded = K.flatten(z_decoded) xent_loss = keras.metrics.binary_crossentropy(x, z_decoded)#重構損失 kl_loss = -5e-4 * K.mean(1+z_log_var-K.square(z_mean)- K.exp(z_log_var), axis=-1)#encoder損失 return K.mean(xent_loss + kl_loss) def call(self,inputs): x = inputs[0] z_decoded = inputs[1] loss = self.vae_loss(x,z_decoded) self.add_loss(loss,inputs=inputs) return x y = CustomVariationalLayer()([input_img, z_decoded])最後,實例化模型並訓練。由於損失函數是在自定義層中處理的,因此不會在編譯時指定外部損失(loss=None),這反過來意味着不會在訓練期間傳遞目標數據(如所見,只能將x_train傳遞給模型在fit函數中)。VAE訓練
from keras.datasets import mnistvae = Model(input_img,y)#通過定義輸入和輸出 Model模型vae.compile(optimizer='rmsprop', loss=None)vae.summary()(x_train, _), (x_test, y_test) = mnist.load_data()x_train = x_train.astype('float32') / 255.x_train = x_train.reshape(x_train.shape + (1,))x_test = x_test.astype('float32') / 255.x_test = x_test.reshape(x_test.shape + (1,))vae.fit(x=x_train, y=None,shuffle=True,epochs=10,batch_size=batch_size, validation_data=(x_test, None))模型訓練完成後,可以使用decoder模塊將任意隱變量空間點轉換生成圖片。2D隱變量空間點取樣,生成圖片
import matplotlib.pyplot as pltfrom scipy.stats import normn = 15#15*15 225個數字圖片digit_size = 28figure = np.zeros((digit_size*n,digit_sie*n))#最終圖片grid_x = norm.ppf(np.linspace(0.05,0.95,n))#假設隱變量空間符合高斯分布grid_y = norm.ppf(np.linspace(0.05,0.95,n))#ppf隨機取樣for i,yi in enumerate(grid_x): for j, xi in enumerate(grid_y): z_sample = np.array([[xi, yi]]) #重複z_sample多次,形成一個完整的batch z_sample = np.tile(z_sample, batch_size).reshape(batch_size, 2) x_decoded = decoder.predict(z_sample, batch_size=batch_size) digit=x_decoded[0].reshape(digit_size, digit_size)#28*28*1->28*28 figure[i*digit_size:(i+1)*digit_size,j*digit_size:(j+1)*digit_size] = digitplt.figure(figsize=(10, 10))plt.imshow(figure, cmap='Greys_r')plt.show()深度學習的圖像生成是通過學習捕獲有關圖像數據集的統計信息的潛在空間來完成的。通過對潛在空間中的點進行採樣和解碼,可以生成前所未見的圖像。有兩個主要工具:VAE和GAN。
VAE導致高度結構化,連續的潛在表徵。出於這個原因,它們適用於在潛在空間中進行各種圖像編輯:面部交換,將皺眉臉變成笑臉,等等。它們也可以很好地用於基於潛在空間的動畫,例如沿着潛在空間的橫截面動畫製作動畫,顯示起始圖像以連續的方式慢慢變形為不同的圖像。
GAN可以生成逼真的單幀圖像,但可能不會引入具有堅固結構和高連續性的潛在空間。
本文僅做學術分享,如有侵權,請聯繫刪文。
