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