一文看懂AlexNet

本文僅作爲以後複習方便查閱,其中主要分以下兩個方面介紹原論文閱讀和代碼復現,代碼復現主要借鑑大神的倉庫,現放論文和倉庫地址如下:

AlexNet論文:AlexNet原論文地址

AlexNet tensorflow實現的源碼:tensorflow實現AlexNet網絡模型

原論文作者主要有以下幾個方面的貢獻:

1.使用Relu激活函數代替tanh和sigmoid,能夠加快模型的收斂速度

2.使用雙層結構,利用GPU 並行機制,加快訓練速度

3.提出LRN,局部歸一化

4.重疊池化

5.數據增強主要爲圖像增強和Dropout

一 Relu激活函數

一般神經網絡的激活函數會選擇sigmoid和tanh,論文作者通過實驗證明,在實現相同的training error rate的過程中Relu要比tanh快的多。

Caption

圖中展示了對於一個特定的四層CNN,CIFAR-10數據集訓練中誤差率達到25%所需要的迭代次數。使用Relu做激活函數只需迭代6次,而 tanh需要迭代36次才能達到。

二 GPU並行機制

論文作者使用的GTX 580 GPU只要3GB的內存,不能實現大規模的神經網絡的訓練。因此作者使用兩塊GTX GPU採用並行的方式對AlexNet網絡進行訓練,這樣的好處是並行的網絡不要經過主機內存就可以實現相互多寫,加快了速度。作者通過證實採用並行機制能夠降低top-1和top-5分別達到1.7%和1.2%。

三 RPN局部響應歸一化

Relu本來是不需要隊輸入進行標準化,但本文發現進行局部標準化能夠提升性能。局部歸一化的公式爲:

其中a表示feature map中第i個卷積核(x, y)經過Relu激活函數之後的輸出,n表示相鄰的幾個卷積核。N表示這一層卷積核的數量。

通過RPN局部歸一化作者可以降低top-1和top-5分別爲1.4%和1.2%。

四 重疊池化

正常池化是步長s=2和窗口z=2 重疊池化步長s=2窗口z=3,採用重疊池化可以減少top-1和top-5分別爲0.4%和0.3%。重疊池化可以避免過擬合。

五 網絡結構

Alexnet 主要由五個卷積層和三個全連層組成。最後的全連接層的輸出被送到1000維的softmax函數,其產生1000個預測類。

總體描述如下:

1. Alexnet爲8層網絡結構,其中前五層爲卷積層,後面三層爲全連接層。學習參數6000萬個,神經元個數650000個。

2. AlexNet在兩個GPU上面並行。

3. ALexNet在第2,4,5層均是前一層自己GPU內連接,第3層與前面兩層全連接,全連接是2個GPU全連接。

4. RPN層是在第1,2層卷積之後。

5.pooling層是在RPN層和第5卷積層之後。

6.Relu是在每個卷積層和全連接層後。

7.每層卷積核大小如下圖所示:參考鏈接爲:https://www.learnopencv.com/understanding-alexnet/

使用caffe畫出的網絡圖:http://ethereon.github.io/netscope/#/editor

卷積層輸出計算:

使用VALID填充的:Wout = (Win - F) / S +1

使用SAME填充的:Wout =(Win + 2P - F) / S + 1

池化層計算:

使用VALID填充:Wout = (Win - F) / S + 1

使用SAME填充:Wout = (Win + 2P - F) / S +1

參數計算:

卷積核大小爲K×K,通道數爲M,卷積核數量爲N,biases數量等於卷積核的數量N

參數:K × K × M × N + N

AlexNet各層卷積結果及參數如下表:

layer input num kernel_size/strides padding output parameters
conv1 227*227*3 96 11*11/4 VALID 55*55*96 11*11*3*96 + 96
norm1 55*55*96       55*55*96  
pool1 55*55*96   3*3/2 VALID 27*27*96  
conv2 27*27*96 256 5*5/1 SAME 27*27*256 5×5×96×256+256
norm2 27*27*256       27*27*256  
pool2 27*27*256   3*3/2 VALID 13*13*256  
conv3 13*13*256 384 3*3/1 SAME 13*13*384 3×3×256×384+384
conv4 13*13*384 384 3*3/1 SAME 13*13*384 3×3×384×384+384
conv5 13*13*384 256 3*3/1 SAME 13*13*256 3×3×384×256+256
pool3 13*13*384   3*3/2 VALID 6*6*256  
fc6 6*6*256       1*1*4096 6×6×6×4096+4096
fc7 1*1*4096       1*1*4096 1×1×4096×4096+4096
fc8 1*1*4096       1*1*1000 1×1×4096×1000+1000
softmax 1*1*1000       1*1*1000  

總參數共計:6200萬個

六.數據增強

論文使用的數據增強的第一種方式主要爲平移圖像和水平映射,將256×256圖片隨機截取爲227×227網絡所需要的圖片。

第二種方式爲改變圖片的RGB 通道的灰度,使用PCA抽取特徵。有效增加數據量,減少過擬合現象。

七.Dropout

Dropout是有效的模型集成學習方法,具有0.5的概率將隱藏層神經元設置爲0.運用了這種機制的神經元不會干擾前向傳遞也不影響後續操作。因此當有輸入的時候,神經網絡採樣不用的結構,但是這些結構共享一個權重。這就減少了神經元適應的複雜性。測試時,用0.5的概率隨機失活神經元。dropout減少了過擬合,也使收斂迭代次數增加一倍。失活效果圖如下:

關於dropout的原理更多可以參考如下:https://zhuanlan.zhihu.com/p/23178423

八.源碼復現

網絡層

import numpy as np
import tensorflow as tf


class AlexNet(object):
    def __init__(self, x, keep_prob, class_num, skip, model_path='bvlc_alexnet.npy'):
        self.x = x
        self.keep_pron = keep_prob
        self.class_num = class_num
        self.skip = skip
        self.model_path = model_path
        self.buildAlexNet()

    def buildAlexNet(self):
        # conv1-->relu-->norm1-->pool1 output:55*55*96
        conv1 = conv(self.x, 96, 11, 4, 'conv1', 'VALID')
        norm1 = local_response_normalization(conv1, 2, 2e-05, 0.75, 'norm1')
        pool1 = pooling(norm1, 3, 2, 'pool1', 'VALID')  # 27*27*96
        # conv2-->relu-->norm2-->pool2 output: 27*27*256
        conv2 = conv(pool1, 256, 5, 1, 'conv2', groups=2)
        norm2 = local_response_normalization(conv2, 2, 2e-5, 0.75, 'norm2')
        pool2 = pooling(norm2, 3, 2, 'pool2', 'VALID')  # 13*13*256
        # conv3-->relu output: 13*13*384
        conv3 = conv(pool2, 384, 3, 1, 'conv3')
        # conv4-->relu output: 13*13*384
        conv4 = conv(conv3, 384, 3, 1, 'conv4', groups=2)
        # conv5-->relu-->pool3 output: 13*13*256
        conv5 = conv(conv4, 256, 3, 1, 'conv5', groups=2)
        pool3 = pooling(conv5, 3, 2, 'pool3', 'VALID')  # 6*6*256
        # fc6-->relu-->drop output: 1*1*4096
        fc6 = fully_connection(tf.reshape(pool3, [-1, 256 * 6 * 6]), 256 * 6 * 6, 4096, True, 'fc6')
        drop6 = dropout(fc6, self.keep_pron, 'drop6')
        # fc7-->relu-->drop output: 1*1*4096
        fc7 = fully_connection(drop6, 4096, 4096, True, 'fc7')
        drop7 = dropout(fc7, self.keep_pron, 'drop7')
        # fc8-->relu output: 1*1*1000
        self.fc8 = fully_connection(drop7, 4096, self.class_num, False, 'fc8')

    # download weights
    def load_weights(self, sess):
        weigths_path = np.load(self.model_path, encoding='bytes').item()
        for name in weigths_path:
            if name not in self.skip:
                with tf.variable_scope(name, reuse=True):
                    for data in weigths_path[name]:
                        if len(data.shape) == 1:
                            sess.run(tf.get_variable('b', trainable=False).assign(data))
                        else:
                            sess.run(tf.get_variable('w', trainable=False).assign(data))


# convolution
def conv(input, filter, kernel_size, stride, name, padding='SAME', groups=1):
    channels = int(input.get_shape()[-1])
    convolution = lambda a, b: tf.nn.conv2d(a, b, strides=[1, stride, stride, 1], padding=padding)
    with tf.variable_scope(name) as scope:
        w = tf.get_variable('w', shape=[kernel_size, kernel_size, channels // groups, filter])
        b = tf.get_variable('b', shape=[filter])
        xNew = tf.split(value=input, num_or_size_splits=groups, axis=3)
        wNew = tf.split(value=w, num_or_size_splits=groups, axis=3)

        featureMap = [convolution(t1, t2) for t1, t2 in zip(xNew, wNew)]
        mergeFeatureMap = tf.concat(axis=3, values=featureMap)
        out = tf.nn.bias_add(mergeFeatureMap, b)
        return tf.nn.relu(tf.reshape(out, mergeFeatureMap.get_shape().as_list(), name=scope.name))


# max_pool
def pooling(input, kernel_size, stride, name, padding='SAME'):
    return tf.nn.max_pool(input, ksize=[1, kernel_size, kernel_size, 1], strides=[1, stride, stride, 1], padding=padding, name=name)


# lrn
def local_response_normalization(input, R, alpha, beta, name=None, bias=1.0):
    return tf.nn.local_response_normalization(input, depth_radius=R, beta=beta, alpha=alpha, name=name, bias=bias)


# dropout
def dropout(input, keep_prob, name=None):
    return tf.nn.dropout(input, keep_prob=keep_prob, name=name)


# fc
def fully_connection(x, input_size, output_size, relu_flag, name):
    with tf.variable_scope(name) as scope:
        w = tf.get_variable('w', shape=[input_size, output_size], dtype=tf.float32)
        b = tf.get_variable('b', shape=[output_size], dtype=tf.float32)
        output = tf.nn.xw_plus_b(x, w, b, name=scope.name)
        if relu_flag:
            return tf.nn.relu(output)
        else:
            return output

檢測

# coding: UTF-8
import os
import cv2
import time
import numpy as np
import tensorflow as tf
from alexnet import AlexNet
from caffe_classes import class_names
import matplotlib.pyplot as plt

# mean of imagenet dataset in RGB
imagenet_mean = np.array([104., 117., 124.], np.float)
# get image path
path = os.getcwd()
image_path = os.path.join(path, 'dataset')
image_files = [os.path.join(image_path, f) for f in os.listdir(image_path) if f.endswith('jpeg') or f.endswith('.jpg')]
# read images
images = []
for image in image_files:
    images.append(cv2.imread(image))
# define variable
x = tf.placeholder(tf.float32, [1, 227, 227, 3])
keep_prob = tf.placeholder(tf.float32)
# get class score
model = AlexNet(x, keep_prob, 1000, [])
score = model.fc8
softmax = tf.nn.softmax(score)
# get recognition result
fig = plt.figure(figsize=(15, 6))
with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    model.load_weights(sess)
    for i, image in enumerate(images):
        start = time.time()
        img = cv2.resize(image.astype(np.float), (227, 227))
        img -= imagenet_mean
        img = img.reshape((1, 227, 227, 3))
        probs = sess.run(softmax, feed_dict={x: img, keep_prob: 1})
        class_name = class_names[np.argmax(probs)]
        font = cv2.FONT_HERSHEY_SIMPLEX
        cv2.putText(image, class_name, (int(image.shape[0] / 3), int(image.shape[1] / 3)), font, 1, (0, 255, 0), 2)
        print("{}: {}  time: {}".format(i, class_name, time.time() - start))
        cv2.imshow("demo", image)
        cv2.imwrite('./result/{}.png'.format(i), image, [int(cv2.IMWRITE_PNG_COMPRESSION), 9])
        if cv2.waitKey(1) & 0xFF == ord('q'):
            break
        time.sleep(0.5)

檢測結果

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章