1. 程式人生 > >6、卷積神經網路CNN

6、卷積神經網路CNN

我們知道多層神經網路(DNN)有很強的特徵學習能力,但是他有一些侷限:

1、因為全連線,所以權重引數特別多,且對引數初始化很敏感

2、正是因為引數很多,所以很容易過擬合。

以上這兩點在進行影象分類時,尤為嚴重。

卷積神經網路大大緩解了DNN引數多的問題,主要有以下幾點:

1、全連線,改為區域性連線,借鑑貓眼的感受野思想。

2、同一個滑動視窗,從左上角滑至右下角的過程中,權重是共享的,固定的。(每個視窗學習影象的一個特徵,引數數量由視窗種數和視窗大小決定)

3、池化層,(max pooling | average pooling)也會減少引數。

使用場景:

1、影象識別、檢索

2、自然語言處理:看圖說話與問答、雙語翻譯。

3、一般都是通過CNN提取特徵,送給DNN,或傳統機器學習方法。

卷積神經網路層次結構:

1、輸入層:包含資料預處理等

2、卷積層:區域性關聯、視窗滑動、權值共享。 一組固定的權重和不同視窗做內積。

3、激勵層:ReLU、Sigmoid、MaxOut

4、池化層:

5、全連線層:通常在CNN尾部,倒數第二層

6、輸出層。

卷積層:

每個神經元只與區域性的輸入特徵相聯;視窗從左上角滑至右下角,權值不變;

單個隱含層的引數個數 = 神經元個數 * 每個視窗的權重數


滑動的時候有三個資料:

1、深度depth:指的是下一層有多少個神經元,如下圖,就有2個神經元。

2、步長stride:滑動的步長,下圖的demo,步長為2哦,注意哦,步長不僅僅是從左到右的步長,也是從上到下的步長哦,即demo中第二次從左到右時,下降了2格哈哈~

3、填充值zero-padding:  如下圖紫色部分是原始資料集,視窗大小3*3,步長為2,你會發現第一次從左滑到右是沒問題的,但是往下滑時,發現格數不夠3*3了!!!,只有2*3!!!,這時候我們一般都在外圍補上一圈或者兩圈零,保證可以滑。demo種的 zero-padding = 1,即一圈。


引數共享:同一個神經元的資料視窗在滑動時,其權重是不變的。

激勵層: 


Sigmoid函式面對數值很大的輸入值,其梯度約等於0. 用BP演算法時, 所以引數迭代就不會更新了,這個問題就是梯度彌散。直觀的感受就是訓練集、測試集上的loss不動了,但又不是很小。


為了讓資訊有一定程度的非線性變換,所以負的都為0。但是很脆弱:

如果輸入的資料超出一定的範圍,就會發現東西傳不出去,也傳不回來。

要做一個監控,看看輸入和輸出是否還在啟用,因為一旦掛掉了,就再也不會激活了。 


Leaky ReLU:  f(x) = max(ax,x) = max(0.01x,x)

不會“飽和”/掛掉,計算也很快。

“飽和”是掛掉的一種原因。比如梯度為0 



比較少有用ELU,因為計算量大。

Maxout: 




池化層:

做一個下采樣,一般夾在連續的卷積層中間,在資料量的層面上減小引數,之前卷積層是從權重的層面上減少引數的。

好處:

1、資料量小

2、減緩過擬合,因為資料量小了,你背不下來。


全連線層:

前面做了很多區域性連線、下采樣,有一定的資訊損失。為了能夠比較好的擬合出結果,所以引出了全連線層,增加靈活性。

個人理解,就是CNN找到的特徵餵給DNN唄。。。。

訓練演算法:

1、定義Loss Function,衡量和實際結果之間的差距。

2、找到最小化損失函式的W和b,CNN用的是SGD

3、SGD需要計算W和b的偏導

4、BP演算法就是計算偏導用的。

CNN的優缺點:

優點:

  • 共享卷積核,對高維資料處理無壓力
  • 無需手動選取特徵,訓練好權重,即得特徵
  • 分類效果好
缺點:
  • 需要調參,需要大樣本量,訓練最好要GPU
  • 黑盒,物理含義不明確

FINE-TUNING:

何謂fine-tuning:使用已用於其他目標,預訓練好模型的權重或者部分權重,作為初始值開始訓練

原因:自己從頭訓練卷積神經網路容易出現問題;fine-tuning能很快收斂到一個較理想的狀態。

做法:複用相同層的權重,新定義層取隨機權重初始值;調大新定義層的學習率,調小複用層學習率。

程式碼:

MNIST資料集:

幾萬張28畫素*28畫素的手寫數字組成,這些圖片只包含灰度值。我們要講這些圖片分類,轉成0~9
訓練集55000個樣本,測試集10000,驗證集5000

#-*-coding:utf-8-*-
"""
Created on 17/3/26 下午4:01

base Info
"""
__author__ = 'sun'
__version__ = '1.0'

'''
我們依舊用MNIST資料集,簡單介紹:
幾萬張28畫素*28畫素的手寫數字組成,這些圖片只包含灰度值。我們要講這些圖片分類,轉成0~9
訓練集55000個樣本,測試集10000,驗證集5000

本次講使用兩個卷積層加一個全連線層
'''

from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets("MNIST_data/",one_hot=True)

# print(mnist.train.images.shape,mnist.train.labels.shape)
# print(mnist.test.images.shape,mnist.test.labels.shape)
# print(mnist.validation.images.shape,mnist.validation.labels.shape)

import tensorflow as tf
sess = tf.InteractiveSession()

# 有很多權重和偏置需要建立,我們先定義好初始化函式以便重複使用。
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) #避免死亡節點(dead neurons)
    return tf.Variable(initial)

# 卷積層、池化層也要重複使用
def conv2d(x,W):
    return tf.nn.conv2d(x,W,                #conv2d 是2維卷積函式
                        strides=[1,1,1,1],  #stride代表滑動步長,都為1,代表不會遺漏地劃過圖片的每一個點。
                        padding='SAME')     #邊界的處理方式,SAME代表讓卷積的輸出和輸入保持同樣的尺寸。

def max_pool_2x2(x):
    return tf.nn.max_pool(x,
                          ksize=[1,2,2,1],
                          strides=[1,2,2,1],#橫豎兩個方向以2為步長
                          padding='SAME')
# 定義輸入的placeholder
x = tf.placeholder(tf.float32,[None,784])
y_ = tf.placeholder(tf.float32,[None,10])
x_image = tf.reshape(x,[-1,28,28,1]) # CNN需要用到空間結構,所以講1*784形式轉化為28*28形式。前面的-1代表樣本不固定,後面的1代表一個顏色通道

# 定義第一個卷積層,先將W和b初始化,在放到啟用函式中,再將輸出結果進行池化操作。
W_conv1 = weight_variable([5,5,1,32]) #5*5的視窗,1個顏色通道,32個卷積核(神經元)
b_conv1 = bias_variable([32])
h_conv1 = tf.nn.relu(conv2d(x_image,W_conv1) + b_conv1)
h_pool1 = max_pool_2x2(h_conv1)

# 定義第二個卷積層,程式碼一樣,唯一不同的是卷積核變成了64個。
W_conv2 = weight_variable([5,5,32,64]) #5*5的視窗,32個輸入卷積核結果,32個卷積核(神經元)
b_conv2 = bias_variable([64])
h_conv2 = tf.nn.relu(conv2d(h_pool1,W_conv2) + b_conv2)
h_pool2 = max_pool_2x2(h_conv2)

# 因為前面經歷了兩次步長為2*2的最大池化層,所以變成已經只有1/4了。圖片由28*28變成了7*7
# 我們使用tf.reshape函式對第二個卷積層的輸出tensor進行變形,將其轉化成1D的向量,然後全連線。
w_fc1 = weight_variable([7*7*64, 1024])  # 隱含層節點1024
b_fc1 = bias_variable([1024])
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層,在訓練時,我們隨機丟棄一部分節點的資料,預測時則保留全部資料來追求最好的預測效能
keep_prob = tf.placeholder(tf.float32)
h_fc1_drop = tf.nn.dropout(h_fc1,keep_prob) # 這裡在全連線層後面才用 是不是太晚了,可以在第一個卷積層後面就用哦

# 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 交叉資訊熵,優化器選Adam
cross_entropy = tf.reduce_mean(-tf.reduce_sum(y_ * tf.log(y_conv),
                                              reduction_indices=[1]))
train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)

# 定義評測準確率
correct_prediction = tf.equal(tf.argmax(y_conv,1),tf.argmax(y_,1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction,tf.float32))

# 計算圖都被我們設計好了,現在開始訓練
tf.global_variables_initializer().run()
for i in range(20000):
    batch = mnist.train.next_batch(50)
    if i%10 == 0:
        train_accuracy = accuracy.eval(feed_dict={x:batch[0],y_:batch[1],keep_prob: 1.0})
        print("step %d, training accuracy %g"%(i,train_accuracy))

    train_step.run(feed_dict={x:batch[0],y_:batch[1],keep_prob: 0.5})

#測試集上的準確率如何
print("test accuracy %g"%accuracy.eval(feed_dict={x:mnist.test.images,y_:mnist.test.labels,keep_prob:1.0}))


CIFAR-10資料集:

#-*-coding:utf-8-*-
"""
Created on 17/3/26 下午5:18
文件目錄: /Users/sun/tensorflow/Action/chapter/ch05/CIFAR-10/models/tutorials/image/cifar10
base Info
"""
__author__ = 'sun'
__version__ = '1.0'

"""
CIFAR-10包含60000張32*32的彩色圖片,其中訓練集50000,測試集10000.
標註了10類,每類6000張圖片,分佈為:airplane ,automobile, bird, cat, deer, dog, frog, horse, ship, truck

在這個CNN模型中,我們使用一些新的技巧:
1、對weights進行了L2正則化
2、對圖片進行了翻轉、隨機剪下等資料增強,製造了更多樣本。資料量大小恰恰是深度學習最看重的。
3、每個卷積-最大池化層後面使用了LRN層,增強了模型的泛化能力。

"""

import  cifar10,cifar10_input  #載入Tensorflow Models中自動下載、讀取CIFAR10資料的類
import tensorflow as tf
import numpy as np
import time

max_steps = 3000 #訓練輪數
batch_size = 128
data_dir = '/tmp/cifar10_data/cifar-10-batches-bin' # 下載資料的預設路徑

# 定義初始化weight的函式,不僅使用了截斷的正態分佈來初始化權重,也會給weight加一個L2的loss
def variable_with_weight_loss(shape,stddev,w1):
    var = tf.Variable(tf.truncated_normal(shape,stddev=stddev))
    if w1 is not None:
        weight_loss = tf.multiply(tf.nn.l2_loss(var),w1,name='weight_loss')
        tf.add_to_collection('losses',weight_loss)
    return var

# 下載、解壓、展開到預設位置
cifar10.maybe_download_and_extract()

# 生成訓練資料 ,這裡面封裝了資料增強的過程,並且將圖片剪下為24*24了。
images_train,labels_train = cifar10_input.distorted_inputs(data_dir=data_dir,batch_size=batch_size)

# 生成測試資料
images_test,labels_test = cifar10_input.inputs(eval_data=True,data_dir=data_dir,batch_size=batch_size)

# 建立輸入資料的placeholder,因為batch_size在之後定義網路結構用到了,所以資料尺寸的第一個值需要預先設定
image_holder = tf.placeholder(tf.float32,[batch_size,24,24,3])
label_holder = tf.placeholder(tf.int32,[batch_size])

"""
建立第一個卷積層,這裡用到了LRN層,模仿了生物神經系統的'側抑制'機制,對區域性神經元的活動建立競爭環境,使得其中響應比較大的值變得相對更大,
並抑制其他反饋較小的神經元,增強了模型的泛化能力。
LRN對ReLU這種沒有上限邊界的啟用函式會比較有用,因為它會從附近的多個卷積核的響應中挑選比較大的反饋,但不適合Sigmoid這種有固定邊界並且能抑制
過大值的啟用函式。
"""
weight1 = variable_with_weight_loss(shape=[5,5,3,64],stddev=5e-2,w1=0.0)
kernel1 = tf.nn.conv2d(image_holder,weight1,[1,1,1,1],padding='SAME')
bias1 = tf.Variable(tf.constant(0.0,shape=[64]))
conv1 = tf.nn.relu(tf.nn.bias_add(kernel1,bias1))
pool1 = tf.nn.max_pool(conv1,ksize=[1,3,3,1],strides=[1,2,2,1],padding='SAME')
norm1 = tf.nn.lrn(pool1,4,bias=1.0,alpha=0.001/9.0,beta=0.75)

"""
建立第二個卷積層,基本類似於第一個,區別如下:
輸入的通道數改為64,bias初始化0.1,調換了最大池化層和LRN層的順序。
"""
weight2 = variable_with_weight_loss(shape=[5,5,64,64],stddev=5e-2,w1=0.0)
kernel2 = tf.nn.conv2d(norm1,weight2,[1,1,1,1],padding='SAME')
bias2 = tf.Variable(tf.constant(0.1,shape=[64]))
conv2 = tf.nn.relu(tf.nn.bias_add(kernel2,bias2))
norm2 = tf.nn.lrn(conv2,4,bias=1.0,alpha=0.001/9.0,beta=0.75)
pool2 = tf.nn.max_pool(norm2,ksize=[1,3,3,1],strides=[1,2,2,1],padding='SAME')

"""
全連線層,隱含節點384個,這裡我們希望這個全連線層不要過擬合,因此設了一個非零的weight loss值。
"""
reshape = tf.reshape(pool2,[batch_size,-1])
dim = reshape.get_shape()[1].value
weight3 = variable_with_weight_loss(shape=[dim,384],stddev=0.04,w1=0.004)
bias3 = tf.Variable(tf.constant(0.1,shape=[384]))
local3 = tf.nn.relu(tf.matmul(reshape,weight3) + bias3)

"""
全連線層2,隱含節點下降了一半
"""
weight4 = variable_with_weight_loss(shape=[384,192],stddev=0.04,w1=0.004)
bias4 = tf.Variable(tf.constant(0.1,shape=[192]))
local4 = tf.nn.relu(tf.matmul(local3,weight4) + bias4)

"""
最後一層,這裡不像之前那樣使用softmax 輸出結果,因為我們把softmax的操作放在了計算loss部分,
計算softmax主要是為了算loss,這裡我們直接比較數值大小就知道應該分哪類了。
"""
weight5 = variable_with_weight_loss(shape=[192,10],stddev=1/192.0,w1=0.0)
bias5 = tf.Variable(tf.constant(0.0,shape=[10]))
logits = tf.add(tf.matmul(local4,weight5),bias5)

"""
計算CNN的loss,依然使用cross entropy,最後將cross entropy的loss 加上最後兩個全連線層的weight的loss
"""
def loss(logits,labels):
    labels = tf.cast(labels,tf.int64)
    cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=logits,
                                                                   labels=labels,
                                                                   name='cross_entropy_per_example')
    cross_entropy_mean = tf.reduce_mean(cross_entropy,name='cross_entropy')
    tf.add_to_collection('losses',cross_entropy_mean)
    return tf.add_n(tf.get_collection('losses'),name='total_loss')

loss = loss(logits,label_holder)
train_op = tf.train.AdamOptimizer(1e-3).minimize(loss)
top_k_op = tf.nn.in_top_k(logits,label_holder,1)  # 輸出結果中top k的準確率
sess = tf.InteractiveSession()
tf.global_variables_initializer().run()
tf.train.start_queue_runners() #啟動前面提到的圖片資料增強的執行緒佇列,使用了16個執行緒

"""
正式開始訓練
"""
for step in range(max_steps):
    start_time = time.time()
    image_batch,label_batch = sess.run([images_train,labels_train])
    _,loss_value = sess.run([train_op,loss],
                            feed_dict={image_holder: image_batch,label_holder:label_batch})
    duration = time.time()-start_time
    if step%10==0:
        examples_per_sec = batch_size / duration
        sec_per_batch = float(duration)
        format_str = ('step %d,loss=%.2f (%.1f examples/sec; %.3f sec/batch)')
        print(format_str % (step,loss_value,examples_per_sec,sec_per_batch))

"""
模型在測試集上的準確率
先計算一共要多少個batch才能將全部樣本評測完,同時,在每一個step中使用session的run方法獲取images_test
labels_test的batch
"""

num_examples = 10000
import math
num_iter = int(math.ceil(num_examples / batch_size))
true_count=0
total_sample_count = num_iter * batch_size
step = 0
while step < num_iter:
    image_batch,label_batch = sess.run([images_test,labels_test])
    predictions = sess.run([top_k_op],feed_dict={image_holder:image_batch,
                                                 label_holder:label_batch})
    true_count += np.sum(predictions)
    step += 1
precision = true_count / total_sample_count
print('precision  = %.3f' % precision)


深度學習其他演算法:

迴圈神經網路(RNN)的引入原因:

1、傳統神經網路輸入與輸出是獨立的,無法解決“我是中國人,我的母語是————”這樣的問題。

2、RNN引入“記憶”的概念,輸出依賴於輸入與“記憶”


Long Short Term 網路(LSTM)的引入原因:

1、RNN不能依賴很久遠的資訊。

http://www.jianshu.com/p/9dc9f41f0b29

深度學習的典型應用:

1、NBA精彩進球集錦

2、視訊中的軟性廣告

3、場景判斷、危險監控

4、雙語翻譯

5、閱讀理解、問答系統

學習資料:

1、斯坦福231N

2、斯坦福224d

3、Michael Nielsen: Neural NetWorks and Deep Learning

4、 Yobhua Bengio Deep Learning Book