1. 程式人生 > >深度學習 之七 【卷積神經網路 CNN】

深度學習 之七 【卷積神經網路 CNN】

1.CNN的應用

    • 如果你能訓練人工智慧機器人唱歌,幹嘛還訓練它聊天?在 2017 年 4 月,研究人員使用 WaveNet 模型的變體生成了歌曲。原始論文和演示可以在 此處 找到。
  • 瞭解 Facebook 的 創新 CNN 方法(Facebook) ,該方法專門用於解決語言翻譯任務,準確率達到了前沿性水平,並且速度是 RNN 模型的 9 倍。

  • 利用 CNN 和強化學習玩 Atari 遊戲。你可以下載 此論文附帶的程式碼。

    • 如果你想研究一些(深度強化學習)初學者程式碼,建議你參閱 Andrej Karpathy 的帖子。
    • 閱讀 這篇文章,其中提出了一個問題:如果掌控 Go“需要人類直覺”,那麼人性受到挑戰是什麼感覺?
  • 觀看這些非常酷的視訊,其中的無人機都受到 CNN 的支援。

  • 如果你對無人駕駛汽車使用的 CNN 感興趣,請參閱:

  • 參閱視訊中沒有提到的其他應用情形。

    • 一些全球最著名的畫作被轉換成了三維形式,以便視力受損人士也能欣賞。雖然這篇文章沒有提到是怎麼做到的,我們注意到可以使用 CNN 預測單個圖片的深度
    • 一款叫做 FaceApp 的應用使用 CNN 讓你在照片中是微笑狀態或改變性別。

2.MLP和CNN的區別

MLP(多層感知器)
- 1.將輸入的矩陣轉換成向量,然後送到隱藏層,這樣會丟失一些二維資訊
- 2.對於一些小的圖片,比如:28px * 28px,想要達到好的預測效果,引數都超過60萬了

如圖:4 x 4的矩陣轉換成16維向量後,當做輸入傳遞給MLP,這是具有一個隱藏層(四個節點)的MLP,輸出層有10個節點,輸出時有一個softmax函式,返回一個十維的向量,包含圖片描述的0到9的數字的可能概率。
這裡寫圖片描述

CNN(卷積神經網路)
- 1.接受矩陣作為輸入,不需要將矩陣轉換成向量
- 2.每一個輸入,並不需要與所有的節點相關聯
- 3.通過指定過濾器

數量大小,控制卷積層的行為

卷積神經網路

特徵對映要求,輸入的圖片格式一樣大小的。

區域性連線層
- 1.包含更少的權重,
- 2.區域性相連,節點僅與上一層中的小部分節點相連
- 3.空間內共享引數

全連線層 Dense(密集層)
- 1.每個節點與前一層中的每個節點相連

卷積窗(過濾器),stride為1的移動動畫
這裡寫圖片描述

Keras的卷積層使用方法

Conv2D(filters, kernel_size, strides, padding, activation='relu', input_shape)

引數
必須傳遞以下引數:
- filters - 過濾器數量。
- kernel_size - 指定(方形)卷積視窗的高和寬的數字。

你可能還需要調整其他可選引數:
- strides - 卷積 stride。如果不指定任何值,則 strides 設為 1。
- padding - 選項包括 ‘valid’ 和 ‘same’。如果不指定任何值,則 padding 設為 ‘valid’。
- activation - 通常為 ‘relu’。如果未指定任何值,則不應用任何啟用函式。強烈建議你向網路中的每個卷積層新增一個 ReLU 啟用函式。
- input_shape - 指定輸入的高度、寬度和深度(按此順序)的元組。

注意:可以將 kernel_sizestrides 表示為數字或元組。
注意:如果卷積層不是網路的第一個層級,請勿包含 input_shape 引數。

更多詳細引數使用案例,查閱官方文件

這裡寫圖片描述

公式:卷積層中的引數數量

卷積層中的引數數量取決於 filterskernel_sizeinput_shape 的值。

  • K - 卷積層中的過濾器數量
  • F - 卷積過濾器的高度和寬度
  • D_in - 上一層級的深度

注意:K = filtersF = kernel_size。類似地,D_ininput_shape 元祖中的最後一個值。
因為每個過濾器有 F*F*D_in 個權重,卷積層由 K 個過濾器組成,因此卷積層中的權重總數是 K*F*F*D_in
因為每個過濾器有 1 個偏差項,卷積層有 K 個偏差。因此,卷積層中的引數數量是 K*F*F*D_in + K

公式:卷積層的形狀

卷積層的形狀取決於 kernel_sizeinput_shapepaddingstride 的值。我們定義幾個變數:

  • K - 卷積層中的過濾器數量
  • F - 卷積過濾器的高度和寬度
  • H_in - 上一層級的高度
  • W_in - 上一層級的寬度

注意:K = filtersF = kernel_size,以及S = stride。類似地,H_inW_in 分別是 input_shape 元祖的第一個和第二個值。

卷積層的深度始終為過濾器數量 K

如果 padding = ‘same’,那麼卷積層的空間維度如下:

  • height = ceil(float(H_in) / float(S))
  • width = ceil(float(W_in) / float(S))

如果 padding = ‘valid’,那麼卷積層的空間維度如下:

  • height = ceil(float(H_in - F + 1) / float(S))
  • width = ceil(float(W_in - F + 1) / float(S))

池化層

池化層總是將卷積層作為輸入。
卷積層是指特徵對映堆疊,每個過濾器對應一個特徵對映,且負責從圖片中查詢一種規律
過濾器越多,堆疊越大,維度就越高,引數也會更多,這就很可能導致過擬合,因此我們需要降低維度,這就是池化層在卷積神經網路中扮演的角色。

最大池化層 Max Pooling Layer

就是在卷積窗(過濾器)在移動過程中,每次取最大的那個數,這樣就一次一次的卷積運算,都會將維度,降低矩陣大小,如下圖

這裡寫圖片描述

每個特徵對映的寬和高都減小了,如下圖
這裡寫圖片描述

程式碼例項:

from keras.models import Sequential
from keras.layers import MaxPooling2D

model = Sequential()
model.add(MaxPooling2D(pool_size=2, strides=2, input_shape=(100, 100, 15)))
model.summary()
引數

你必須包含以下引數:
- pool_size - 指定池化視窗高度和寬度的數字。

你可能還需要調整其他可選引數:
- strides - 垂直和水平 stride。如果不指定任何值,則 strides 預設為 pool_size
- padding - 選項包括 'valid''same'。如果不指定任何值,則 padding 設為 'valid'
注意:可以將 pool_sizestrides 表示為數字或元組。

此外,建議閱讀 官方文件

舉栗子:
假設我要構建一個 CNN,並且我想通過在卷積層後面新增最大池化層,降低卷積層的維度。假設卷積層的大小是 (100, 100, 15),我希望最大池化層的大小為 (50, 50, 15)。要實現這一點,我可以在最大池化層中使用 2x2 視窗,stride 設為 2,程式碼如下:

 MaxPooling2D(pool_size=2, strides=2)

當然了,stride 也可以為 1

全域性平均池化 Global Average Pooling Layer

全域性平均池化,既不指定卷積窗(kernel_size)大小,也不指定 stride,這是一種更極端的降低維度的池化型別,過程是:

  • 1.它獲得了一堆的特徵對映
  • 2.並計算每個對映的節點均值(均值,就是先對所有的節點值求和,然後除以總節點數)
  • 3.這樣的話,每個特徵對映都縮減成了一個值

這裡寫圖片描述

最後全域性平均池化就將一個三維的陣列轉變成了一個向量

這裡寫圖片描述

瞭解不同型別的池化層,請參閱該 Keras 文件

第一個CNN的架構介紹
from keras.models import Sequential
from keras.layers import Conv2D, MaxPooling2D, Flatten, Dense

model = Sequential()
model.add(Conv2D(filters=16, kernel_size=2, padding='same', activation='relu', input_shape=(32, 32, 3)))
model.add(MaxPooling2D(pool_size=2))
model.add(Conv2D(filters=32, kernel_size=2, padding='same', activation='relu'))
model.add(MaxPooling2D(pool_size=2))
model.add(Conv2D(filters=64, kernel_size=2, padding='same', activation='relu'))
model.add(MaxPooling2D(pool_size=2))
model.add(Flatten())
model.add(Dense(500, activation='relu'))
model.add(Dense(10, activation='softmax'))

注意事項

  • 始終向 CNN 中的 Conv2D 層新增 ReLU 啟用函式。但是網路的最後層級除外,密集層也應該具有 ReLU 啟用函式。
  • 在構建分類網路時,網路中的最後層級應該是具有 softmax 啟用函式的 密集層。最後層級的節點數量應該等於資料集中的類別總數
  • 建議參閱 Andrej Karpathy 的 tumblr(來自外網,可能打不開),其中包含了使用者提交的損失函式,對應的是本身有問題的模型。損失函式在訓練期間應該是減小的,但是這些圖表顯示的卻是非常不同的行為 :)。

增強圖片的訓練

為什麼要增強圖片的訓練?這是因為圖片資料集中的預測目標物件不可能都是在正中間,有的圖片傾斜著,有的圖片歪倒著,這就需要我們來生成一些位置不一樣的同類圖片,加入到訓練集中。

  • Rotation Invariance 旋轉不變性
  • Translation Invariance 平移不變性

這裡寫圖片描述

這就需要用到 ImageDataGenerator

from keras.preprocessing.image import ImageDataGenerator

# create and configure augmented image generator
datagen_train = ImageDataGenerator(
    width_shift_range=0.1,  # randomly shift images horizontally (10% of total width)
    height_shift_range=0.1,  # randomly shift images vertically (10% of total height)
    horizontal_flip=True) # randomly flip images horizontally

# fit augmented image generator on data
datagen_train.fit(x_train)

訓練時,將 fit() 函式換成了 fit_generator()

from keras.callbacks import ModelCheckpoint   

batch_size = 32
epochs = 100

# train the model
checkpointer = ModelCheckpoint(filepath='aug_model.weights.best.hdf5', verbose=1, 
                               save_best_only=True)
model.fit_generator(datagen_train.flow(x_train, y_train, batch_size=batch_size),
                    steps_per_epoch=x_train.shape[0] // batch_size,
                    epochs=epochs, verbose=2, callbacks=[checkpointer],
                    validation_data=(x_valid, y_valid),
                    validation_steps=x_valid.shape[0] // batch_size)

在 Keras 中,封裝了一些著名的 CNN 預訓練模型
- Xception
- VGG16
- VGG19
- ResNet50
- InceptionV3
- InceptionResNetV2
- MobileNet
- DenseNet
- NASNet

視覺化CNN

這是另一個 CNN 視覺化工具的 演示。如果你想詳細瞭解這些視覺化圖表是如何製作的,請觀看此視訊

這是另一個可與 Keras 和 Tensorflow 中的 CNN 無縫合作的視覺化工具

閱讀這篇視覺化 CNN 如何看待這個世界的 Keras 博文。在此博文中,你會找到 Deep Dreams 的簡單介紹,以及在 Keras 中自己編寫 Deep Dreams 的程式碼。閱讀了這篇博文後:

再觀看這個利用 Deep Dreams 的 音樂視訊(注意 3:15-3:40 部分)!
使用這個網站建立自己的 Deep Dreams(不用編寫任何程式碼!)。

如果你想詳細瞭解 CNN 的解釋
這篇文章詳細講解了在現實生活中使用深度學習模型(暫時無法解釋)的一些危險性。
這一領域有很多熱點研究。這些作者最近朝著正確的方向邁出了一步。

假如有一個三層的Conv2d的網路模型,那麼它發現規律的流程是

  • 第一層級將檢測圖片中的邊緣
  • 第二層級將檢測形狀
  • 第三個卷積層將檢測更高階的特徵
著名卷積網路
  • AlexNet

TensorFlow 的CNN

上述程式碼用了 tf.nn.conv2d() 函式來計算卷積,weights 作為濾波器,[1, 2, 2, 1] 作為 strides。TensorFlow 對每一個 input 維度使用一個單獨的 stride 引數,[batch, input_height, input_width, input_channels]。我們通常把 batchinput_channels (strides 序列中的第一個第四個)的 stride 設為 1

你可以專注於修改 input_heightinput_widthbatchinput_channels 都設定成 1input_heightinput_width strides 表示濾波器在input 上移動的步長。上述例子中,在 input 之後,設定了一個 5x5stride2 的濾波器。

# Output depth
k_output = 64

# Image Properties
image_width = 10
image_height = 10
color_channels = 3

# Convolution filter
filter_size_width = 5
filter_size_height = 5

# Input/Image
input = tf.placeholder(
    tf.float32,
    shape=[None, image_height, image_width, color_channels])

# Weight and bias
weight = tf.Variable(tf.truncated_normal(
    [filter_size_height, filter_size_width, color_channels, k_output]))
bias = tf.Variable(tf.zeros(k_output))

conv_layer = tf.nn.conv2d(input, weight, strides=[1, 2, 2, 1], padding='SAME')
conv_layer = tf.nn.bias_add(conv_layer, bias)
conv_layer = tf.nn.relu(conv_layer)

ksizestrides 引數也被構建為四個元素的列表,每個元素對應 input tensor 的一個維度 ([batch, height, width, channels]),對 ksizestrides 來說,batchchannel 通常都設定成 1

conv_layer = tf.nn.max_pool(
    conv_layer,
    ksize=[1, 2, 2, 1],
    strides=[1, 2, 2, 1],
    padding='SAME')

池化層總的來說,目的是:減小輸入大小,降低過擬合,相應的引數也減少了很多。
但是,近期,池化層並不是很受青睞,部分原因是:

  • 現在的資料集又大又複雜,我們更關心欠擬合問題
  • dropout是一種更好的正則化方法
  • 池化導致資訊丟失,想象最大池化的例子,n個數字當中,我們只保留最大的,把剩餘的n-1完全捨棄了。

小練習

最大池化層練習 Max Pooling Layer

假如輸入資料是

[[[0, 1, 0.5, 10],
   [2, 2.5, 1, -8],
   [4, 0, 5, 6],
   [15, 1, 2, 3]]]

filter_size=(2, 2), strides=(2, 2), 輸出維度: 2x2x1,那麼經過最大池化層後的結果是:

[2.5,10,15,6]

計算方式

max(0, 1, 2, 2.5) = 2.5
max(0.5, 10, 1, -8) = 10
max(4, 0, 15, 1) = 15
max(5, 6, 2, 3) = 6
全域性平均池化 Global Average Layer

假如輸入資料是:

[[[0, 1, 0.5, 10],
   [2, 2.5, 1, -8],
   [4, 0, 5, 6],
   [15, 1, 2, 3]]]

filter_size=(2, 2), strides=(2, 2), 輸出維度: 2x2x1,那麼經過全域性平均池化層後的結果是:

1.375,0.875,5.0,4.0

計算方式是:

mean(0, 1, 2, 2.5) = 1.375
mean(0.5, 10, 1, -8) = 0.875
mean(4, 0, 15, 1) = 5
mean(5, 6, 2, 3) = 4

1x1卷積

  • 1.1x1卷積會增加引數數量
  • 2.它關注的是一個畫素,而不是一塊影象
  • 3.相較於池化,它更高效

Inception 模組

通過1x1的卷積輸出給 1x1、3x3、5x5的卷積後,再將他們組合起來輸出,看起來有點複雜,但是要比簡單的卷積好的多
類似如下圖
這裡寫圖片描述

測試專案 【使用TensorFlow的CNN來訓練資料】

from tensorflow.examples.tutorials.mnist import input_data
import tensorflow as tf
import math

# 1.匯入MNIST資料集
mnist = input_data.read_data_sets(".", one_hot=True, reshape=False)

# 2.設定一些引數
learning_rate = 0.00001
epochs = 10
batch_size = 128

test_valid_size = 256

n_classes = 10
dropout = 0.75

# weights = [filter_size_height, filter_size_width, color_channels, k_output]
weights = {
    'wc1': tf.Variable(tf.random_normal([28, 28, 1, 32])),
    'wc2': tf.Variable(tf.random_normal([14, 14, 32, 64])),
    'wd1': tf.Variable(tf.random_normal([7*7*64, 1024])),
    'out': tf.Variable(tf.random_normal([1024, n_classes]))}

biases = {
    'bc1': tf.Variable(tf.random_normal([32])),
    'bc2': tf.Variable(tf.random_normal([64])),
    'bd1': tf.Variable(tf.random_normal([1024])),
    'out': tf.Variable(tf.random_normal([n_classes]))
}

# 3.建立卷積函式
def conv2d(x, W, b, strides=1):
    # strides = [batch, input_height, input_width, input_channels]
    x = tf.nn.conv2d(x, W, strides=[1, strides, strides, 1], padding="SAME")
    x = tf.nn.bias_add(x, b)
    return tf.nn.relu(x)

# 建立最大池化層函式
def maxpool2d(x, k=2):
    return tf.nn.max_pool(x, ksize=[1, k, k, 1], strides=[1, k, k, 1], padding="SAME")

# 建立卷積網路函式
def conv_net(x, weights, biases, dropout):
    # Layer 1 - 28*28*1 to 14*14*32
    conv1 = conv2d(x, weights['wc1'], biases['bc1'])  # 28*28*1 
    conv1 = maxpool2d(conv1, k=2) # 14*14*32

    # Layer 2 - 14*14*32 to 7*7*64
    conv2 = conv2d(conv1, weights['wc2'], biases['bc2']) # 14*14*32
    conv2 = maxpool2d(conv2, k=2) # 7*7*64

    fc1 = tf.reshape(conv2, [-1, weights['wd1'].get_shape().as_list()[0]])
    fc1 = tf.add(tf.matmul(fc1, weights['wd1']), biases['bd1'])
    fc1 = tf.nn.relu(fc1)
    fc1 = tf.nn.dropout(fc1, dropout)

    out = tf.add(tf.matmul(fc1, weights['out']), biases['out'])
    return out


# inputs = [bach_size, image_height, image_width, color_channels]
x = tf.placeholder(tf.float32, [None, 28, 28, 1])
y = tf.placeholder(tf.float32, [None, n_classes])

# 留存率 訓練時=0.5,驗證和測試時=1.0
keep_prob = tf.placeholder(tf.float32) 

# 計算輸出層的線性啟用函式 logit score
logits = conv_net(x, weights, biases, keep_prob)

# 建立誤差項
cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=logits, labels=y))

# 建立優化器
optimizer = tf.train.GradientDescentOptimizer(learning_rate=learning_rate).minimize(cost)

# 預測是否正確
correct_pred = tf.equal(tf.argmax(logits, 1), tf.argmax(y, 1))
# 獲得精確度
accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32))


# 4.訓練網路
with tf.Session() as session:

    # 初始化tensorflow的所有變數
    session.run(tf.global_variables_initializer())

    # 迭代 epoch 次
    for epoch in range(epochs):

        # 計算需要多少個 batch
        batches = int(math.ceil(mnist.train.num_examples // batch_size))

        # 每次 epoch,需要訓練分類器 batches 次
        for batch in range(batches):

            # 獲取下一個批次的訓練資料
            batch_x, batch_y = mnist.train.next_batch(batch_size)

            # 訓練資料
            session.run(optimizer, feed_dict={x: batch_x, y: batch_y, keep_prob: dropout})

            # 計算訓練誤差值
            loss = session.run(cost, feed_dict={x: batch_x, y: batch_y, keep_prob: 1.})

            # 計算訓練精準度
            vali_acc = session.run(accuracy, feed_dict={x: mnist.validation.images[:test_valid_size], y: mnist.validation.labels[:test_valid_size], keep_prob: 1.})

            print("Epoch={:>2} Batch={:>3} Loss={:.4f} Validation Accuracy={:.6f}"
                  .format(epoch+1, batch+1, loss, vali_acc))

    # 最後訓練完後,根據測試資料集得出測試精準度
    test_acc = session.run(accuracy, feed_dict={x: mnist.test.images[:test_valid_size], y: mnist.test.labels[:test_valid_size], keep_prob: 1.})
    print("Test Accuracy: {}".format(test_acc))

test accuracy是:

Test Accuracy: 0.9609375

課外閱讀
Andrej Karpathy’s CS231n Stanford course on Convolutional Neural Networks.
Michael Nielsen’s free book on Deep Learning.
Goodfellow, Bengio, and Courville’s more advanced free book on Deep Learning.