天天看點

TensorFlow2實作條件批歸一化(Conditional Batch Normalization)條件批歸一化(Conditional Batch Normalization)TensorFlow實作條件批歸一化在殘差塊中應用條件批歸一化

條件批歸一化(Conditional Batch Normalization)

批歸一化 (Batch Normalization, BN) 是深度學習中常用的網絡訓練技巧,它不僅可以加快了模型的收斂速度,而且更重要的是在一定程度緩解了深層網絡中“梯度彌散”的問題,進而使得訓練深層網絡模型更加容易和穩定,是以目前 BN 已經成為幾乎所有卷積神經網絡的标配技巧了,簡單回顧下 BN 的方程式:  

TensorFlow2實作條件批歸一化(Conditional Batch Normalization)條件批歸一化(Conditional Batch Normalization)TensorFlow實作條件批歸一化在殘差塊中應用條件批歸一化

其中,均值 µ 和标準差 σ 是在 (N, H, W) 次元上進行計算的,每個規範化層隻有一個仿射變換參數對 γ 和 β,它們是在訓練時網絡自己學習得到的。

但是在生成對抗網絡 (Generative Adversarial Networks, GAN) 中使用 BN 會導緻生成圖檔在一定程度上出現同質化的缺點。例如,在 CIFAR10 資料集中,有10類圖檔:6種是動物(分别為:鳥,貓,鹿,狗,青蛙和馬),4種是交通工具(分别是:飛機,汽車,輪船和卡車)。顯然,不同類别的圖檔在外觀上看起來截然不同——交通往往具有堅硬而筆直的邊緣,而動物傾向于具有彎曲的邊緣和較柔和的紋理。

風格遷移

中我們已經了解了,激活的統計資料決定了圖像樣式。是以,混合批統計資訊可以建立看上去有點像動物同時也有點像交通工具(例如,汽車形狀的貓)的圖像。這是因為批歸一化在由不同類别圖檔組成的整個批次中僅使用一個 γ 和一個 β。如果每種類别都有一個 γ 和一個 β,則該問題得以解決,而這正是條件批規範化的意義所在。每個類别有一個 γ 和一個 β,是以CIFAR10中的10個類别每層有10個 γ 和10個 β。

TensorFlow實作條件批歸一化

現在,我們可以構造條件批處理規範化所需的變量,如下所示:

1. 形狀為 (10, C) 的 β 和 γ,其中 C 是激活通道數。

2. (1, 1, 1, C) 形狀的遊動均值和方差。在訓練中,均值和方差是從小批次計算得出的。在推論過程中,我們使用訓練中累積的移動均值。它們的形狀使算術運算可以廣播到 N,H 和 W 次元。

利用自定義層實作條件批歸一化,首先建立所需變量:

class ConditionBatchNorm(Layer):
    def build(self, input_shape):
        self.input_size = input_shape
        n, h, w, c = input_shape
        self.gamma = self.add_weight(shape=[self.n_class, c],  
            initializer='zeros', trainable=True, name='gamma')
        self.moving_mean = self.add_weight(shape=[1, 1, 1, c],
            initializer='zeros', trainable=False, name='moving_mean')
        self.moving_var = self.add_weight(shape=[1, 1, 1, c],  
            initializer='zeros', trainable=False, name='moving_var')      

當運作條件批歸一化時,為标簽檢索正确的 β 和 γ。這是使用 tf.gather(self.beta, labels) 完成的,它在概念上等效于 beta = self.beta[labels],如下所示:

def call(self, x, labels, trainable=False):
        beta = tf.gather(self.beta, labels)
        beta = tf.expand_dims(beta, 1)
        gamma = tf.gather(self.gamma, labels)
        gamma = tf.expand_dims(gamma, 1)
  if training:
    mean, var = tf.nn.moments(x, axes=(0,1,2), keepdims=True)
    self.moving_mean.assign(self.decay * self.moving_mean + (1-self.decay)*mean)
    self.moving_var.assign(self.decay * self.moving_var + (1-self.decay)*var)
    output = tf.nn.batch_normalization(x, mean, var, beta, gamma, self.eps)
  else:
    output = tf.nn.batch_normalization(x, self.moving_mean, self.moving_var, beta, gamma, self.eps)
  return output      

在殘差塊中應用條件批歸一化

條件批歸一化的使用方式與批歸一化相同,作為示例,現在我們将條件批歸一化添加到殘差塊中:

class ResBlock(Layer):
    def build(self, input_shape):
        input_filter = input_shape[-1]
        self.conv_1 = Conv2D(self.filters, 3, padding='same', name='conv2d_1')
        self.conv_2 = Conv2D(self.filters, 3, padding='same', name='conv2d_2')
        self.cbn_1 = ConditionBatchNorm(self.n_class)
        self.cbn_2 = ConditionBatchNorm(self.n_class)
        self.learned_skip = False
        if self.filters != input_filter:
            self.learned_skip = True
            self.conv_3 = Conv2D(self.filters, 1, padding='same', name='conv2d_3')
            self.cbn_3 = ConditionBatchNorm(self.n_class)      

以下是使用條件批歸一化殘差塊的前向計算代碼:

def call(self, input_tensor, labels):
        x = self.conv_1(input_tensor)
        x = self.cbn_1(x, labels)
        x = tf.nn.leaky_relu(x, 0.2)
        x = self.conv_2(x)
        x = tf.cbn_2(x, labels)
        x = tf.nn.leaky_relu(x, 0.2)
        if self.learned_skip:
            skip = self.conv_3(input_tensor)
            skip = self.cbn_3(skip, labels)
            skip = tf.nn.leaky_relu(skip, 0.2)
        else:
            skip = input_tensor
        output = skip + x
        return output      

繼續閱讀