卷積神經(jīng)網(wǎng)絡(luò)

卷積神經(jīng)網(wǎng)絡(luò)(Convolutional Neural Network)是一個(gè)專門針對圖像識(shí)別問題設(shè)計(jì)的神經(jīng)網(wǎng)絡(luò)。它模仿人類識(shí)別圖像的多層過程:瞳孔攝入像素;大腦皮層某些細(xì)胞初步處理,發(fā)現(xiàn)形狀邊緣、方向;抽象判定形狀(如圓形、方形);進(jìn)一步抽象判定(如判斷物體是美女還是恐龍)。

視覺認(rèn)知

如果我們使用傳統(tǒng)神經(jīng)網(wǎng)絡(luò)方式,對一張圖片進(jìn)行分類,那么,我們把圖片的每個(gè)像素都連接到隱藏層節(jié)點(diǎn)上,那么對于一張1000x1000像素的圖片,如果我們有1M隱藏層單元,那么一共有10^12個(gè)參數(shù),這顯然是不能接受的。

卷積網(wǎng)絡(luò)通過一系列方法,成功將數(shù)據(jù)量龐大的圖像識(shí)別問題不斷降維,最終使其能夠被訓(xùn)練。

近年來,卷積神經(jīng)網(wǎng)絡(luò)在語音識(shí)別、人臉識(shí)別、通用物體識(shí)別、運(yùn)動(dòng)分析、自然語言處理甚至腦電波分析方面均有突破,是目前深度學(xué)習(xí)研究領(lǐng)域的熱點(diǎn)之一。

1. 卷積

卷積是CNN的核心,從數(shù)學(xué)表達(dá)上來看,卷積就是一種數(shù)學(xué)運(yùn)算。

這個(gè)卷積操作背后的數(shù)學(xué)知識(shí)其實(shí)非常的簡單。
要計(jì)算一個(gè)feature和其在原圖上對應(yīng)的某一小塊的結(jié)果,只需要簡單地將兩個(gè)小塊內(nèi)對應(yīng)位置的像素值進(jìn)行乘法運(yùn)算,然后將整個(gè)小塊內(nèi)乘法運(yùn)算的結(jié)果累加起來,最后再除以小塊內(nèi)像素點(diǎn)總個(gè)數(shù)即可。

卷積的目的是簡化更復(fù)雜的數(shù)據(jù)表達(dá),過濾掉復(fù)雜數(shù)據(jù)中多余的噪聲,提取出關(guān)鍵的特征。所以卷積應(yīng)用經(jīng)常被稱作濾波,而卷積核經(jīng)常被稱作過濾器(或圖像掃描器、特征掃描器、局部感受器)。

卷積操作

只需要調(diào)整卷積核的數(shù)值,就可以執(zhí)行諸如邊緣檢測、銳化、模糊等效果,這說明不同的濾波器會(huì)從圖片中探測到不同的特征,比如邊緣、曲線等。

簡而言之,卷積就是對提取特征。

傳統(tǒng)的特征工程非常難,很少有資料告訴我們特征工程應(yīng)該怎么做,這些都非常依賴經(jīng)驗(yàn)。要做好特征工程,就需要長期的經(jīng)驗(yàn)積累。例如,要識(shí)別貓和狗的圖片,就得先人工提取出貓和狗的各種特征,比如貓和狗的眼睛大小、耳朵形狀等,然后將這些提取好的特征給一個(gè)分類器(例如SVM),再進(jìn)行分類。

在卷積神經(jīng)網(wǎng)絡(luò)中,通過對卷積核賦予參數(shù),這些參數(shù)經(jīng)過訓(xùn)練,即可完成特征學(xué)習(xí)。當(dāng)我們在新的數(shù)據(jù)上訓(xùn)練時(shí),就自動(dòng)學(xué)習(xí)了新的特征。

這就是卷積神經(jīng)網(wǎng)絡(luò)如此強(qiáng)大的原因--不再需要繁重的特征工程了。

2. 局部感知野

在圖像處理中,往往把圖像表示為像素的向量,比如一個(gè)1000×1000的圖像,可以表示為一個(gè)1000000的向量。在上一節(jié)中提到的神經(jīng)網(wǎng)絡(luò)中,如果隱含層數(shù)目與輸入層一樣,即也是1000000時(shí),那么輸入層到隱含層的參數(shù)數(shù)據(jù)為1000000×1000000=10^12,這樣就太多了,基本沒法訓(xùn)練。所以圖像處理要想練成神經(jīng)網(wǎng)絡(luò)大法,必先減少參數(shù)加快速度。

卷積神經(jīng)網(wǎng)絡(luò)有兩種神器可以降低參數(shù)數(shù)目,第一種神器叫做局部感知野。

一般認(rèn)為人對外界的認(rèn)知是從局部到全局的,而圖像的空間聯(lián)系也是局部的像素聯(lián)系較為緊密,而距離較遠(yuǎn)的像素相關(guān)性則較弱。因而,每個(gè)神經(jīng)元其實(shí)沒有必要對全局圖像進(jìn)行感知,只需要對局部進(jìn)行感知,然后在更高層將局部的信息綜合起來就得到了全局的信息。網(wǎng)絡(luò)部分連通的思想,也是受啟發(fā)于生物學(xué)里面的視覺系統(tǒng)結(jié)構(gòu)。視覺皮層的神經(jīng)元就是局部接受信息的(即這些神經(jīng)元只響應(yīng)某些特定區(qū)域的刺激)。如下圖所示:左圖為全連接,右圖為局部連接。

局部感知

在上右圖中,假如每個(gè)神經(jīng)元只和10×10個(gè)像素值相連,那么權(quán)值數(shù)據(jù)為1000000×100個(gè)參數(shù),減少為原來的千分之一。而那10×10個(gè)像素值對應(yīng)的10×10個(gè)參數(shù),其實(shí)就相當(dāng)于卷積操作。

3. 參數(shù)共享

但其實(shí)這樣的話參數(shù)仍然過多,那么就啟動(dòng)第二級(jí)神器,即權(quán)值共享(也就是參數(shù)共享)。

在上面的局部連接中,每個(gè)神經(jīng)元都對應(yīng)100個(gè)參數(shù),一共1000000個(gè)神經(jīng)元,如果這1000000個(gè)神經(jīng)元的100個(gè)參數(shù)都是相等的,那么參數(shù)數(shù)目就變?yōu)?00了。

怎么理解權(quán)值共享呢?我們可以這100個(gè)參數(shù)(也就是卷積操作)看成是提取特征的方式,該方式與位置無關(guān)。這其中隱含的原理則是:圖像的一部分的統(tǒng)計(jì)特性與其他部分是一樣的。這也意味著我們在這一部分學(xué)習(xí)的特征也能用在另一部分上,所以對于這個(gè)圖像上的所有位置,我們都能使用同樣的學(xué)習(xí)特征。

更直觀一些,當(dāng)從一個(gè)大尺寸圖像中隨機(jī)選取一小塊,比如說 8×8 作為樣本,并且從這個(gè)小塊樣本中學(xué)習(xí)到了一些特征,這時(shí)我們可以把從這個(gè) 8×8 樣本中學(xué)習(xí)到的特征作為探測器,應(yīng)用到這個(gè)圖像的任意地方中去。特別是,我們可以用從 8×8 樣本中所學(xué)習(xí)到的特征跟原本的大尺寸圖像作卷積,從而對這個(gè)大尺寸圖像上的任一位置獲得一個(gè)不同特征的激活值。

4. 多卷積核

上面所述只有100個(gè)參數(shù)時(shí),表明只有1個(gè)100*100的卷積核,顯然,特征提取是不充分的,我們可以添加多個(gè)卷積核,比如32個(gè)卷積核,可以學(xué)習(xí)32種特征。在有多個(gè)卷積核時(shí),如下圖所示:

多卷積核

每個(gè)卷積核都會(huì)將圖像生成為另一幅圖像。比如兩個(gè)卷積核就可以將生成兩幅圖像,這兩幅圖像可以看做是一張圖像的不同的通道:比如,一張圖像是經(jīng)過邊緣檢測的結(jié)果,一張圖像是銳化的結(jié)果。

5. 池化

在通過卷積獲得了特征 (features) 之后,下一步我們希望利用這些特征去做分類。理論上講,我們可以用所有提取得到的特征去訓(xùn)練分類器,例如 softmax 分類器,但這樣做將面臨計(jì)算量的挑戰(zhàn)。

例如:對于一個(gè) 96X96 像素的圖像,假設(shè)我們已經(jīng)學(xué)習(xí)得到了400個(gè)定義在8X8輸入上的特征,每一個(gè)特征和圖像卷積都會(huì)得到一個(gè) (96 ? 8 + 1) × (96 ? 8 + 1) = 7921 維的卷積特征,由于有 400 個(gè)特征,所以每個(gè)樣例 (example) 都會(huì)得到一個(gè) 892 × 400 = 3,168,400 維的卷積特征向量。學(xué)習(xí)一個(gè)擁有超過 3 百萬特征輸入的分類器十分不便,并且容易出現(xiàn)過擬合 (over-fitting)。

為了解決這個(gè)問題,首先回憶一下,我們之所以決定使用卷積后的特征是因?yàn)閳D像具有一種“靜態(tài)性”的屬性,這也就意味著在一個(gè)圖像區(qū)域有用的特征極有可能在另一個(gè)區(qū)域同樣適用。因此,為了描述大的圖像,一個(gè)很自然的想法就是對不同位置的特征進(jìn)行聚合統(tǒng)計(jì),例如,人們可以計(jì)算圖像一個(gè)區(qū)域上的某個(gè)特定特征的平均值 (或最大值)。這些概要統(tǒng)計(jì)特征不僅具有低得多的維度 (相比使用所有提取得到的特征),同時(shí)還會(huì)改善結(jié)果(不容易過擬合)。這種聚合的操作就叫做池化 (pooling),有時(shí)也稱為平均池化或者最大池化 (取決于計(jì)算池化的方法)。

平均池化:池化區(qū)域內(nèi)所有值的平均值作為池化結(jié)果;
最大池化:池化區(qū)域內(nèi)所有值的最大值作為池化結(jié)果。

池化

舉個(gè)例子:如有1000萬人參加選舉,每個(gè)家庭10個(gè)人,如果一個(gè)人一個(gè)人的去統(tǒng)計(jì)這1000萬人的選票,太麻煩了,反正每個(gè)家庭投票意見基本一致,那么干脆就每個(gè)家庭一張選票,這樣要統(tǒng)計(jì)的投票數(shù)量就縮小到了100萬。

人民代表大會(huì)制度,是不是也是一種池化?

6. 多層卷積

在實(shí)際應(yīng)用中,往往使用多層卷積,然后再使用全連接層進(jìn)行訓(xùn)練,多層卷積的目的是一層卷積學(xué)到的特征往往是局部的,層數(shù)越高,學(xué)到的特征就越全局化。

那么,卷積神經(jīng)網(wǎng)絡(luò)為什么要設(shè)計(jì)多層的結(jié)構(gòu)呢?

舉個(gè)例子:我們要做一個(gè)區(qū)分貓和狗的圖像應(yīng)用,貓頭和狗頭是一個(gè)特征,但是對于全部都是由像素點(diǎn)組成的圖像來說,用幾個(gè)卷積核直接判斷一個(gè)貓頭實(shí)在是太困難了。那么,把貓頭也作為一個(gè)識(shí)別目標(biāo),比如貓頭應(yīng)該具有更底層的一些特征,如貓眼睛、貓耳朵、貓鼻子等,但是這些特征還是太高級(jí),必須繼續(xù)往下尋找低級(jí)特征,一直找到最低級(jí)的像素點(diǎn),這樣就構(gòu)成了多層神經(jīng)網(wǎng)絡(luò)。第一層卷積層也許是從原始像素點(diǎn)檢測到一些邊緣線條,然后根據(jù)邊緣線條在第二層檢測出一些簡單的形狀,后面的層根據(jù)這些形狀檢測出更高級(jí)的特征,比如臉部輪廓、眼睛輪廓、耳朵輪廓、鼻子輪廓等。最后一層是利用這些高級(jí)特征的一個(gè)分類器,也可以采用神經(jīng)網(wǎng)絡(luò)之外的分類器。

多層卷積神經(jīng)網(wǎng)絡(luò)也符合我們本身對圖像的識(shí)別過程:圖像本身是由像素組成,由像素構(gòu)成線條,由線條構(gòu)成基本形狀,由基本形狀構(gòu)成物體,然后再由物體構(gòu)成我們的概念。

是不是有點(diǎn)兒像畫家在繪制一幅畫?

卷積神經(jīng)網(wǎng)絡(luò)的Hello World-手寫數(shù)字識(shí)別:

import tensorflow as tf
import numpy as np
import os

os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
from tensorflow.examples.tutorials.mnist import input_data

mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)
trX, trY, teX, teY = mnist.train.images, mnist.train.labels, mnist.test.images, mnist.test.labels
# 把上述trX和teX的形狀變?yōu)閇-1,28,28,1],-1表示不考慮輸入圖片的數(shù)量,28×28是圖片的長和寬的像素?cái)?shù),
# 1是通道(channel)數(shù)量,因?yàn)镸NIST的圖片是黑白的,所以通道是1,如果是RGB彩色圖像,通道是3。
trX = trX.reshape(-1, 28, 28, 1)  # 28x28x1 input img
teX = teX.reshape(-1, 28, 28, 1)  # 28x28x1 input img

X = tf.placeholder("float", [None, 28, 28, 1])
Y = tf.placeholder("float", [None, 10])


# 初始化權(quán)重與定義網(wǎng)絡(luò)結(jié)構(gòu)。
# 這里,我們將要構(gòu)建一個(gè)擁有3個(gè)卷積層和3個(gè)池化層,隨后接1個(gè)全連接層和1個(gè)輸出層的卷積神經(jīng)網(wǎng)絡(luò)
def init_weights(shape):
    return tf.Variable(tf.random_normal(shape, stddev=0.01))


w = init_weights([3, 3, 1, 32])  # patch大小為3×3,輸入維度為1,輸出維度為32
w2 = init_weights([3, 3, 32, 64])  # patch大小為3×3,輸入維度為32,輸出維度為64
w3 = init_weights([3, 3, 64, 128])  # patch大小為3×3,輸入維度為64,輸出維度為128
w4 = init_weights([128 * 4 * 4, 625])  # 全連接層,輸入維度為 128 × 4 × 4,是上一層的輸出數(shù)據(jù)又三維的轉(zhuǎn)變成一維, 輸出維度為625
w_o = init_weights([625, 10])  # 輸出層,輸入維度為 625, 輸出維度為10,代表10類(labels)


# 神經(jīng)網(wǎng)絡(luò)模型的構(gòu)建函數(shù),傳入以下參數(shù)
# X:輸入數(shù)據(jù)
# w:每一層的權(quán)重
# p_keep_conv,p_keep_hidden:dropout要保留的神經(jīng)元比例

def model(X, w, w2, w3, w4, w_o, p_keep_conv, p_keep_hidden):
    # 第一組卷積層及池化層,最后dropout一些神經(jīng)元
    l1a = tf.nn.relu(tf.nn.conv2d(X, w, strides=[1, 1, 1, 1], padding='SAME'))
    # l1a shape=(?, 28, 28, 32)
    l1 = tf.nn.max_pool(l1a, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
    # l1 shape=(?, 14, 14, 32)
    l1 = tf.nn.dropout(l1, p_keep_conv)

    # 第二組卷積層及池化層,最后dropout一些神經(jīng)元
    l2a = tf.nn.relu(tf.nn.conv2d(l1, w2, strides=[1, 1, 1, 1], padding='SAME'))
    # l2a shape=(?, 14, 14, 64)
    l2 = tf.nn.max_pool(l2a, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
    # l2 shape=(?, 7, 7, 64)
    l2 = tf.nn.dropout(l2, p_keep_conv)
    # 第三組卷積層及池化層,最后dropout一些神經(jīng)元
    l3a = tf.nn.relu(tf.nn.conv2d(l2, w3, strides=[1, 1, 1, 1], padding='SAME'))
    # l3a shape=(?, 7, 7, 128)
    l3 = tf.nn.max_pool(l3a, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
    # l3 shape=(?, 4, 4, 128)
    l3 = tf.reshape(l3, [-1, w4.get_shape().as_list()[0]])  # reshape to (?, 2048)
    l3 = tf.nn.dropout(l3, p_keep_conv)
    # 全連接層,最后dropout一些神經(jīng)元
    l4 = tf.nn.relu(tf.matmul(l3, w4))
    l4 = tf.nn.dropout(l4, p_keep_hidden)
    # 輸出層
    pyx = tf.matmul(l4, w_o)
    return pyx  # 返回預(yù)測值


# 我們定義dropout的占位符——keep_conv,它表示在一層中有多少比例的神經(jīng)元被保留下來。生成網(wǎng)絡(luò)模型,得到預(yù)測值
p_keep_conv = tf.placeholder("float")
p_keep_hidden = tf.placeholder("float")
py_x = model(X, w, w2, w3, w4, w_o, p_keep_conv, p_keep_hidden)  # 得到預(yù)測值
# 定義損失函數(shù),這里我們?nèi)匀徊捎胻f.nn.softmax_cross_entropy_with_logits來比較預(yù)測值和真實(shí)值的差異,并做均值處理;
# 定義訓(xùn)練的操作(train_op),采用實(shí)現(xiàn)RMSProp算法的優(yōu)化器tf.train.RMSPropOptimizer,學(xué)習(xí)率為0.001,衰減值為0.9,使損失最?。?# 定義預(yù)測的操作(predict_op)
cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=py_x, labels=Y))
train_op = tf.train.RMSPropOptimizer(0.001, 0.9).minimize(cost)
predict_op = tf.argmax(py_x, 1)
# 定義訓(xùn)練時(shí)的批次大小和評(píng)估時(shí)的批次大小
batch_size = 128
test_size = 256
# 在一個(gè)會(huì)話中啟動(dòng)圖,開始訓(xùn)練和評(píng)估
# Launch the graph in a session
with tf.Session() as sess:
    # you need to initialize all variables
    tf.global_variables_initializer().run()
    for i in range(100):
        training_batch = zip(range(0, len(trX), batch_size),
                             range(batch_size, len(trX) + 1, batch_size))
        for start, end in training_batch:
            sess.run(train_op, feed_dict={X: trX[start:end], Y: trY[start:end],
                                          p_keep_conv: 0.8, p_keep_hidden: 0.5})

        test_indices = np.arange(len(teX))  # Get A Test Batch
        np.random.shuffle(test_indices)
        test_indices = test_indices[0:test_size]

        print(i, np.mean(np.argmax(teY[test_indices], axis=1) ==
                         sess.run(predict_op, feed_dict={X: teX[test_indices],
                                                         p_keep_conv: 1.0,
                                                         p_keep_hidden: 1.0})))

Kevin,2018年8月4日,成都。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容