1. 程式人生 > >生成對抗網路消除影象模糊(Keras)

生成對抗網路消除影象模糊(Keras)

2017年,烏克蘭天主教大學、布拉格捷克理工大學和解決方案提供商Eleks聯手公佈了一篇論文,文章標題為《DeblurGAN: Blind Motion Deblurring Using Conditional Adversarial Networks》。 這篇文章中,研究人員提出一種基於條件對抗式生成網路和內容損失(content loss)的端對端學習法DeblurGAN,用來去除影象上因為相機和物體相對運動而產生的模糊。

去模糊效果:

這裡看一下keras的程式碼實現。

生成器網路

keras程式碼:

def generator_model():   
    inputs = Input(shape=image_shape)


    x = ReflectionPadding2D((3, 3))(inputs)
    x = Conv2D(filters=ngf, kernel_size=(7, 7), padding='valid')(x)
    x = BatchNormalization()(x)
    x = Activation('relu')(x)

    n_downsampling = 2
    for i in range(n_downsampling):
        mult = 2**i
        x = Conv2D(filters=ngf*mult*2, kernel_size=(3, 3), strides=2, padding='same')(x)
        x = BatchNormalization()(x)
        x = Activation('relu')(x)

    mult = 2**n_downsampling
    for i in range(n_blocks_gen):
        x = res_block(x, ngf*mult, use_dropout=True)

    for i in range(n_downsampling):
        mult = 2**(n_downsampling - i)
        x = Conv2DTranspose(filters=int(ngf * mult / 2), kernel_size=(3, 3), strides=2, padding='same')(x)
        x = BatchNormalization()(x)
        x = Activation('relu')(x)

    x = ReflectionPadding2D((3, 3))(x)
    x = Conv2D(filters=output_nc, kernel_size=(7, 7), padding='valid')(x)
    x = Activation('tanh')(x)
    x = Lambda(lambda  z: z*2)(x)

    outputs = Add()([x, inputs])
    outputs = Lambda(lambda z: z/3)(outputs)

    model = Model(inputs=inputs, outputs=outputs, name='Generator')
    return model

1. 對輸入影象做一個邊界擴充套件(寬高各6個畫素) 2. 卷積核大小7×7的卷積,方式是valid,之後執行批規範化BN操作,再執行 Relu啟用函式 3. 兩次下采樣操作,每次特徵圖大小縮小為之前的二分之一,具體操作包括 same 卷積,BN和Relu啟用 4. 9個殘差模組,每個模組的操作包括邊界擴充、卷積、BN、Relu啟用、擴充、卷積、BN,其中dropout可選,接著殘差模組的是2組卷積、BN和啟用操作。 5. 最後是邊界擴充、卷積、Tanh啟用、Add輸入操作,輸出結果是一個維度大小跟輸入一致的圖片。

生成器結構圖:

判別器網路

keras程式碼:

def discriminator_model():
    """Build discriminator architecture."""
    n_layers, use_sigmoid = 3, False
    inputs = Input(shape=input_shape_discriminator)


    x = Conv2D(filters=ndf, kernel_size=(4, 4), strides=2, padding='same')(inputs)
    x = LeakyReLU(0.2)(x)

    nf_mult, nf_mult_prev = 1, 1
    for n in range(n_layers):
        nf_mult_prev, nf_mult = nf_mult, min(2**n, 8)
        x = Conv2D(filters=ndf*nf_mult, kernel_size=(4, 4), strides=2, padding='same')(x)
        x = BatchNormalization()(x)
        x = LeakyReLU(0.2)(x)

    nf_mult_prev, nf_mult = nf_mult, min(2**n_layers, 8)
    x = Conv2D(filters=ndf*nf_mult, kernel_size=(4, 4), strides=1, padding='same')(x)
    x = BatchNormalization()(x)
    x = LeakyReLU(0.2)(x)

    x = Conv2D(filters=1, kernel_size=(4, 4), strides=1, padding='same')(x)
    if use_sigmoid:
        x = Activation('sigmoid')(x)

    x = Flatten()(x)
    x = Dense(1024, activation='tanh')(x)
    x = Dense(1, activation='sigmoid')(x)

    model = Model(inputs=inputs, outputs=x, name='Discriminator')
    return model

1. 卷積+LeakyRelu操作,LeakyRelu第三象限的斜率設為0.2 2. 4組 卷積+BN+LeakyRelu 3. sigmoid啟用可選 4. Flatten展平,為全連線層做準備 5. 2個Dense全連線層,最後輸出做sigmoid,限制結果到0~1

判別器結構圖:

損失函式

感知loss(生成器) 使用的是VGG16分別提取生成圖片和真實圖片的特徵,比較的是block3_conv3層的輸出,loss是特徵差的平方再取均值

def perceptual_loss(y_true, y_pred):
    vgg = VGG16(include_top=False, weights='imagenet', input_shape=image_shape)
    loss_model = Model(inputs=vgg.input, outputs=vgg.get_layer('block3_conv3').output)
    loss_model.trainable = False
    return K.mean(K.square(loss_model(y_true) - loss_model(y_pred)))

 Wasserstein 損失 對整個模型(G+D)的輸出執行的 Wasserstein 損失,它取的是兩個影象差異的均值。這種損失函式可以改善生成對抗網路的收斂性。

def wasserstein_loss(y_true, y_pred):
    return K.mean(y_true*y_pred)

訓練過程

1. 分批次載入模糊圖片和清晰圖片資料,並隨機排序 2. 每一輪迭代中用本batch_size個訓練資料先對判別器執行5次優化,5次優化中每次又會使用清晰圖片(標籤是1)和模糊圖片(標籤是0)分別對判別器做一次優化,相當於10次優化。loss函式使用的是wasserstein距離。 3. 關閉判別器引數更新,使判別器不可訓練,以下訓練生成器,生成器的優化標準有兩個,一個是跟清晰影象的差異,一個是迷惑判別器的能力。 4. 生成器和判別器的聯合網路輸出是生成器生成的圖片+判別器的判別值(0到1),所以聯合網路d_on_g的train_on_batch訓練函式的第二個引數(該引數應該傳入訓練資料的真實標籤)含有兩個值,一個是真實清晰影象,一個是真實影象的標籤(為1);  d_on_g損失函式優化的目標是G生成的影象跟清晰影象的差異越來越小(使用VGG16提取特徵並比較),並且該生成影象經過判別器後的輸出跟清晰圖片經過判別器的輸出的差異越來越小(使用wasserstein距離)。 文中作者設定這兩個loss的比重為100:1。 5. 重複執行以上訓練過程