天天看點

神經網絡中卷積層和池化層的作用探究

1.前言:

我們知道:

卷積層用于進行特征提取。

池化層用于輸入的特征圖進行壓縮,使特征圖變小,簡化網絡計算複雜度或進行特征壓縮,提取主要特征。

但在神經網絡中它們會使輸入的特征逐漸變化成什麼樣?最終得到正确的結果呢?下面我們通過一個簡單的神經網絡來探究它們的作用。

2.相關說明:

這裡将使用下圖所示的卷積神經網絡:

神經網絡中卷積層和池化層的作用探究

卷積核大小:[1,1,1,1] 分别對應 [batch,height,width,channels],步長:1,填充類型:SAME

池化視窗(濾波器)大小:[1,2,2,1] 分别對應 [batch,height,width,channels], 步長:2x2, 填充類型:SAME

實作方式:python tensorflow (現在pytorch似乎也挺火的,感興趣的也可以用其實作)

實驗資料:MNIST資料集 或 Fashion_MNIST資料集(推薦使用Fashion_MNIST資料集,效果更明顯)

Fashion_MNIST資料集下載下傳位址:https://github.com/zalandoresearch/fashion-mnist

實驗資料使用方式見:MNIST資料集使用詳解 (MNIST資料集 和 Fashion_MNIST資料集的使用方式都是相同的,相同的代碼隻需要調換資料集檔案就能完美使用)

3.參考代碼及詳解:

import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
import matplotlib.pyplot as plt
import cv2
mnist_data_folder="/Fashion_MNIST"
mnist=input_data.read_data_sets(mnist_data_folder,one_hot=True)


#建立兩個占位符,x為輸入網絡的圖像,y_為輸入網絡的圖像類别
x = tf.placeholder("float", shape=[None, 784])
y_ = tf.placeholder("float", shape=[None, 10])

#權重初始化函數
def weight_variable(shape):
    #輸出服從截尾正态分布的随機值
    initial = tf.truncated_normal(shape, stddev=0.1)
    return tf.Variable(initial)

#偏置初始化函數
def bias_variable(shape):
    initial = tf.constant(0.1, shape=shape)
    return tf.Variable(initial)

#建立卷積op
#x 是一個4維張量,shape為[batch,height,width,channels]
#卷積核移動步長為1。填充類型為SAME,可以不丢棄任何像素點
def conv2d(x, W):
    return tf.nn.conv2d(x, W, strides=[1,1,1,1], padding="SAME")

#建立池化op
#采用最大池化,也就是取視窗中的最大值作為結果
#x 是一個4維張量,shape為[batch,height,width,channels]
#ksize表示pool視窗大小為2x2,也就是高2,寬2
#strides,表示在height和width次元上的步長都為2
def max_pool_2x2(x):
    return tf.nn.max_pool(x, ksize=[1,2,2,1],
                          strides=[1,2,2,1], padding="SAME")

#第1層,卷積層
#初始化W為[5,5,1,32]的張量,表示卷積核大小為5*5,第一層網絡的輸入和輸出神經元個數分别為1和32
W_conv1 = weight_variable([5,5,1,32])
#初始化b為[32],即輸出大小
b_conv1 = bias_variable([32])

#把輸入x(二維張量,shape為[batch, 784])變成4d的x_image,x_image的shape應該是[batch,28,28,1]
#-1表示自動推測這個次元的size
x_image = tf.reshape(x, [-1,28,28,1])

#把x_image和權重進行卷積,加上偏置項,然後應用ReLU激活函數,最後進行max_pooling
#h_pool1的輸出即為第一層網絡輸出,shape為[batch,14,14,1]
h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1)
h_pool1 = max_pool_2x2(h_conv1)

#第2層,卷積層
#卷積核大小依然是5*5,這層的輸入和輸出神經元個數為32和64
W_conv2 = weight_variable([5,5,32,64])
b_conv2 = weight_variable([64])

#h_pool2即為第二層網絡輸出,shape為[batch,7,7,1]
h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2)
h_pool2 = max_pool_2x2(h_conv2)

#第3層, 全連接配接層
#這層是擁有1024個神經元的全連接配接層
#W的第1維size為7*7*64,7*7是h_pool2輸出的size,64是第2層輸出神經元個數
W_fc1 = weight_variable([7*7*64, 1024])
b_fc1 = bias_variable([1024])

#計算前需要把第2層的輸出reshape成[batch, 7*7*64]的張量
h_pool2_flat = tf.reshape(h_pool2, [-1, 7*7*64])
h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1)

#Dropout層
#為了減少過拟合,在輸出層前加入dropout
keep_prob = tf.placeholder("float")
h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)

#輸出層
#最後,添加一個softmax層
#可以了解為另一個全連接配接層,隻不過輸出時使用softmax将網絡輸出值轉換成了機率
W_fc2 = weight_variable([1024, 10])
b_fc2 = bias_variable([10])

y_conv = tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)

#預測值和真實值之間的交叉墒
cross_entropy = -tf.reduce_sum(y_ * tf.log(y_conv))

#train op, 使用ADAM優化器來做梯度下降。學習率為0.0001
train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)

#評估模型,tf.argmax能給出某個tensor對象在某一維上資料最大值的索引。
#因為标簽是由0,1組成了one-hot vector,傳回的索引就是數值為1的位置
correct_predict = tf.equal(tf.argmax(y_conv, 1), tf.argmax(y_, 1))

#計算正确預測項的比例,因為tf.equal傳回的是布爾值,
#使用tf.cast把布爾值轉換成浮點數,然後用tf.reduce_mean求平均值
accuracy = tf.reduce_mean(tf.cast(correct_predict, "float"))

#也可以批量導入訓練,注意使用mnist.train.next_batch(batch_size),得到的批次資料每次都會自動随機抽取這個批次大小的資料
with tf.Session() as sess:
    sess.run(tf.global_variables_initializer()) #初始化變量
    for i in range(2000):  #開始訓練模型,循環2000次,每次傳入一個批次的圖像
        batch_size = 1  # 每次批量訓練1幅圖像
        batch_xs, batch_ys = mnist.train.next_batch(batch_size)  # 随機抓取訓練資料中的1個批處理資料點
        #print(batch_xs)    #列印該圖像清單
        #print(batch_ys)    #列印該圖像标簽
        sess.run(train_step,feed_dict={x:batch_xs, y_:batch_ys, keep_prob:0.5})

    batch =1  # 每次批量測試1幅圖像,可以設定多一點,算正确率
    test_xs, test_ys = mnist.test.next_batch(batch)
    print(test_xs.shape)

    plt.figure()
    im = test_xs.reshape(28, 28)  # 從訓練資料集中随機取的一張圖,将其轉化為28x28格式
    # im=batch_xs[i].reshape(28,28)	#該批次的第i張圖
    plt.imshow(im,cmap='gray')
    plt.pause(0.1)  # 暫停時間

    result=(sess.run([h_conv1,h_pool1,h_conv2,h_pool2,y_conv,], feed_dict={x: test_xs, y_: test_ys, keep_prob: 1.0}))

    print(result[0].shape)
    plt.matshow(result[0][0,:,:,0], cmap=plt.get_cmap('gray'))
    #a = result[0][0].reshape((32, 28, 28))
    #plt.imshow(a[0],cmap='gray')
    plt.pause(0.1)  # 暫停時間

    print(result[1].shape)
    #b = result[1][0].reshape((32, 14, 14))
    plt.matshow(result[1][0, :, :, 0], cmap=plt.get_cmap('gray'))
    plt.savefig("1.jpg")
    #plt.imshow(b[0], cmap='gray')
    plt.pause(0.1)  # 暫停時間

    print(result[2].shape)
    plt.matshow(result[2][0, :, :, 0], cmap=plt.get_cmap('gray'))
    #plt.imshow(c[0], cmap='gray')
    plt.pause(0.1)  # 暫停時間

    print(result[3].shape)
    plt.matshow(result[3][0, :, :, 0], cmap=plt.get_cmap('gray'))
    #plt.imshow(d[0], cmap='gray')
    plt.pause(0.1)  # 暫停時間

    print(result[4])
    plt.show()


           

運作結果說明:

神經網絡中卷積層和池化層的作用探究

我們可以看到預測的機率為:

[[3.0097897e-05 3.6264613e-05 1.6801963e-04 7.7600002e-05 3.7330945e-05

3.6119664e-04 1.0586195e-03 5.9116323e-05 6.3654697e-05 9.9810815e-01]]

其分别對應官方給出的标簽圖(翻譯是Google的錯,嗯):

神經網絡中卷積層和池化層的作用探究

很明顯,結果中最大值所表明的标簽值是9,神經網絡的預測結果是正确的,接下來,我們在來看看圖檔特征經過每個卷積和池化層都發生了什麼。

原圖:

神經網絡中卷積層和池化層的作用探究

這裡我們可以看出它是什麼,還有它的大小是:28x28

特征圖1:

神經網絡中卷積層和池化層的作用探究

我們可以看出它的大小對于原圖是沒有發生變化的,這對應了卷積核的填充方式“SAME”,和步長1,其不會改變輸出圖檔大小。

第一層卷積層似乎提取了其邊緣的特征。(這隻是該神經網絡認為的特征,有時其實我們并不能明确看出它把什麼作為了特征)

特征圖2:

神經網絡中卷積層和池化層的作用探究

我們可以看出它的大小是14x14,這對應了2x2的池化核和2x2的步長,另外需要說明的是池化層總是會使圖檔縮小(或大小不變,但這是沒有意義的,相當于池化層沒有做任何事)。

第二層池化層似乎将特征圖進行壓縮,使特征圖變小,使邊緣特征更加明顯化。

特征圖3:

神經網絡中卷積層和池化層的作用探究

我們可以看出卷積層沒有改變由池化層得到的特征大小。

第三層卷積層認為其特征邊緣為,頂部的那一橫,中間的一豎,左下角的一個弧形,以及右下角的一個點。

特征圖4:

神經網絡中卷積層和池化層的作用探究

我們可以看到池化層使圖檔縮小,進行特征壓縮,得到了如上特征。

而在後面全連接配接層中,會将該特征歸類。

或許這樣的解釋很模糊,但是我們可以将不同類别所得到的特征圖進行對比,活血可以看出提取了什麼不同的特征(下面隻展示相應特征圖四,并且隻針對與正确的結果)。

原圖:

神經網絡中卷積層和池化層的作用探究

體恤的特征圖4:

神經網絡中卷積層和池化層的作用探究

涼鞋的特征圖4:

神經網絡中卷積層和池化層的作用探究

褲子的特征圖4:

神經網絡中卷積層和池化層的作用探究

好了,不弄了,對于有的特征圖,我們可以猜測出它提取了什麼特征(如上例中,明顯提取了部分邊緣特征),但有些特征圖我們并看不出任何東西,但神經網絡卻得到了正确的分類,至于神經網絡為什麼能完成這樣的操作,至今還沒有較為合理的科學解釋,但它就是那種,很神秘很酷的那種(還很令人頭秃)。

其它說明:

1.由各層得到的矩陣(也就是特征圖直接列印出來的話,你什麼也看不出來,那是沒有經過排序的),請注意代碼中是如何列印出各個特征圖!

2.請注意該資料集的使用許可:

神經網絡中卷積層和池化層的作用探究

3.有時候得到的特征圖可能看不出什麼東東,這是正常的(這些圖檔太小了,而且有的差别不是很大,或者你訓練的圖檔不夠多),你可以多嘗試幾次。

4.如果你有其它建議或想法,歡迎在評論區讨論。

結論:

神經網絡中卷積層和池化層的作用探究

繼續閱讀