天天看點

學習《TensorFlow實戰Google深度學習架構》(十)Inception-v3模型

6.4.2 Inception-v3模型

Inception-v3結構是一種和LeNet-5結構完全不同的卷積神經網絡結構。在LeNet-5模型中,不同卷積層通過串聯的方式連接配接在一起,而Inception-v模型中的Inception結構是将不同的卷積層通過并聯的方式結合在一起。

Inception提出同時使用所有不同尺寸的卷積核,然後再将得到的矩陣拼接起來。

如圖所示

學習《TensorFlow實戰Google深度學習架構》(十)Inception-v3模型

Inceptiong子產品會首先使用不同尺寸的卷積核處理輸入矩陣。

圖中,最上方的矩陣為使用了邊長為1的卷積核的卷積層前向傳播結果。

不同的矩陣代表了Inception子產品中的一條計算路徑。

雖然卷積核尺寸不同,但如果每次卷積都使用全0填充且步長為1,那麼前向傳播得到的結果矩陣的長和寬都與輸入矩陣一緻。

這樣經過不同卷積核處理的結果矩陣可以拼接成一個更深的矩陣。

參考論文Rethinking the Inception Architecture for Computer Vision

下圖給出了Inception-v3的模型架構圖

學習《TensorFlow實戰Google深度學習架構》(十)Inception-v3模型

Inception-v3模型總共有46層,由11個Inception子產品組成。圖中方框标注出來的結構就是一個Inception子產品。在Inception-V3模型中有96個卷積層,如果将6.4.1的程式直接搬過來,那麼一個卷積層就需要5行代碼,于是總共需要480行代碼來實作所有的卷積層,這樣使得代碼的可讀性非常差。為了更好的實作類似Inception-v3模型這樣的複雜卷積神經網絡,在下面将先介紹TensorFlow-Slim工具來更加簡潔地實作一個卷積層。以下代碼對比了直接使用TensorFlow實作一個卷積層和使用TensorFlow-Slim實作同樣結構的神經網絡的代碼量。

# 直接使用TensorFlow原始API實作卷積層
with tf.variable_scope(scope_name):
	weights = tf.get_variable("weight",...)
	biases = tf.get_variable("bias",...)
	conv = tf.nn.conv2d(...)
	relu = tf.nn.relu(tf.nn.bias_add(conv,biases))

# 使用TensorFlow-Slim實作卷積層。slim.conv2d有三個參數是必填的。第一個參數為輸入節點矩陣,第二個參數是目前卷積核的個數,第三個參數是卷積核的尺寸。可選的參數有軍機和移動的步長,是否使用全0填充,激活函數的選擇以及變量的命名空間等。
net = slim.conv2d(input, 32, [3, 3])

TensorFlow-Slim是對TensorFlow原生API的二次封裝
           

以下代碼實作了上圖方框中的Inception子產品

# 加載slim庫
slim = tf.contrib.slim

# slim.arg_scope函數可以用于設定預設的參數取值。slim_arg_scope函數的第一個參數是一個函數清單,
# 在這個清單中的函數将使用預設的參數取值。比如通過下面的定義,調用slim.conv2d(net, 320, [1, 1])
# 函數時會自動加上stride=1和padding='SAME'的參數。通過這種方式可以進一步減少備援代碼
with slim.arg_scope([slim.conv2d, slim.max_poole2d, slim.avg_pool2d],
                    strides=1, padding='VALID'):
    # 此處省略了Inception-V3模型其他的網絡結構而直接實作最後的Inception結構。結社輸入圖檔經過之
    # 前的神經網絡前向傳播結果儲存在變量net中。
    net = 上一層輸出節點矩陣
    # 為一個Inception子產品聲明一個統一的變量命名空間
    with tf.variable_scope('Mixed_7c'):
        # 給Inception子產品中每一條路徑聲明一個命名空間
        with tf.variable_scope('Branch_0'):
            # 實作一個有320個邊長為1的卷積核的卷積層
            branch_0 = slim.conv2d(net, 320, [1, 1], scope='Conv2d_0a_1x1')

        # Inception子產品中第二條路徑。這條計算路徑上的結構本身也是一個Inception結構
        with tf.variable_scope('Branch_1'):
            branch_1 = slim.conv2d(net, 384, [1, 1],scope='Conv2d_0a_1x1')
            # tf.concat函數可以将多個矩陣拼接起來。tf.concat函數的第一個參數指定了拼接的次元,這裡給出的“3”
            # 代表了矩陣是在這個深度這個次元上進行拼接。圖1展示了在深度上拼接矩陣的方式。
            branch_1 = tf.concat(3, [
                # 如圖2所示,此處2層卷積層的輸入都是branch_1而不是net
                slim.conv2d(branch_1, 384, [1, 3], scope='Conv2d_0b_1x3'),
                slim.conv2d(branch_1, 384, [3, 1], scope='Conv2d_0c_3x1')
            ])
        # Inception子產品中第三條路徑。此計算路徑也是一個Inception結構
        with tf.variable_scope('Branch_2'):
            branch_2 = slim.conv2d(net, 448, [1, 1], scope='Conv2d_0a_1x1')
            branch_2 = slim.conv2d(branch_2, 384, [3, 3], scope='Conv2d_0b_3x3')
            branch_2 = tf.concat(3, [
                slim.conv2d(branch_2, 384, [1, 3], scope='Conv2d_0c_1x3'),
                slim.conv2d(branch_2, 384, [3, 1], scope='Conv2d_0c_3x1')
            ])
            
        # Inception子產品中第四條路徑。
        with tf.variable_scope('Branch_3'):
            branch_3 = slim.avg_pool2d(net, [3, 3], scope='AvgPool_0a_3x3')
            branch_3 = slim.conv2d(branch_3, 192, [1, 1], scope='Conv2d_0b_1x1')
            
        # 目前Inception子產品的最後輸出是由上面4個計算結果拼接得到的
        net = tf.concat(3, [branch_0, branch_1, branch_2, branch_3])
           

繼續閱讀