溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

TensorFlow 實戰(zhàn)之實現(xiàn)卷積神經(jīng)網(wǎng)絡的實例講解

發(fā)布時間:2020-09-20 13:49:33 來源:腳本之家 閱讀:209 作者:藍色之旅 欄目:開發(fā)技術

本文根據(jù)最近學習TensorFlow書籍網(wǎng)絡文章的情況,特將一些學習心得做了總結,詳情如下.如有不當之處,請各位大拿多多指點,在此謝過。

一、相關性概念

1、卷積神經(jīng)網(wǎng)絡(ConvolutionNeural Network,CNN)

19世紀60年代科學家最早提出感受野(ReceptiveField)。當時通過對貓視覺皮層細胞研究,科學家發(fā)現(xiàn)每一個視覺神經(jīng)元只會處理一小塊區(qū)域的視覺圖像,即感受野。20世紀80年代,日本科學家提出神經(jīng)認知機(Neocognitron)的概念,被視為卷積神經(jīng)網(wǎng)絡最初的實現(xiàn)原型。神經(jīng)認知機中包含兩類神經(jīng)元:S-cells和C-cells。S-cells用來抽取特征,對應我們現(xiàn)在主流卷積神經(jīng)網(wǎng)絡中的卷積核濾波操作;C-cells用來抗形變,對應現(xiàn)在的激活函數(shù)、最大池化(Max-Pooling)等操作。

一般情況下,卷積神經(jīng)網(wǎng)絡由多個卷積層構成,每個卷積層通常會進行如下操作:

(1) 圖像通過多個不同的卷積核的濾波,并加偏置(bias),提取出局部特征,每一個卷積核會映射出一個新的2D圖像。

(2) 將前面卷積核的濾波處理結果,進行非線性的激活函數(shù)處理。目前最常見的是使用ReLU函數(shù),之前Sigmoid函數(shù)應用較多。

(3)多激活函數(shù)處理的結果再進行池化操作(即降采樣,例如:將4*4的圖片降為1*1的圖片),一般會使用最大池化,保留最顯著特征,并提升模型畸變?nèi)萑棠芰Α?/p>

這幾個步驟就構成了最常見的卷積層,也可以再加上一個LRN(LocalResponse Normalization,局部響應歸一化層)層,現(xiàn)在非常流行的Trick還有BatchNormalization等。

2、池化層

3、卷積核尺寸

4、神經(jīng)網(wǎng)絡算法相關特性

4.1、優(yōu)點

(1)可以高效提取特征。

當我們面對一個分類任務時,傳統(tǒng)的機器學習算法,一般要首先明確feature和label,然后拿數(shù)據(jù)取“喂”訓練模型并保存,最后測試模型的準確性。這就需要我們確定好特征,當特征數(shù)目很少就無法精確進行分類而引起欠擬合;當特征數(shù)目很多,又會在分類過程中太過于看重某個特征引起分類錯誤,產(chǎn)生過擬合。而神經(jīng)網(wǎng)絡則不需要做大量的特征工程,可以直接把數(shù)據(jù)“灌”進去而讓其自身訓練,自我“修正”,即可達到預期效果。   (2)數(shù)據(jù)格式更加簡易

利用傳統(tǒng)的機器學習解決分類問題時,數(shù)據(jù)不能直接“灌”進去的,需要對數(shù)據(jù)進行一些處理,譬如量綱的歸一化,格式的轉化等等,然而在神經(jīng)網(wǎng)絡里卻不需要額外對數(shù)據(jù)做過多的處理。

(3) 參數(shù)數(shù)目的少量性

同樣在面對一個分類問題時,利用傳統(tǒng)機器學習SVM來做的話,需要涉及核函數(shù),懲罰因子,松弛變量等等參數(shù),而這些不同的參數(shù)組合會對模型效果產(chǎn)生不一樣的影響,想要迅速而又準確的得到最適合模型的參數(shù),需要對相關理論知識有深入研究,但對于一個基本的三層神經(jīng)網(wǎng)絡來說(輸入-隱含-輸出),只需要初始化時給每一個神經(jīng)元上隨機的賦予一個權重w和偏置項b,在訓練過程中,這兩個參數(shù)就會不斷的修正,調(diào)整到最優(yōu)質(zhì),使模型的誤差最小。所以從這個角度來看,我們的工作效率會更佳。 4.2、缺點

如果我們加深我網(wǎng)絡層,每一個網(wǎng)絡層都增加神經(jīng)元數(shù)量,則參數(shù)的個數(shù)將是M*N(m為網(wǎng)絡層數(shù),N為每層神經(jīng)元個數(shù)),這樣一來參數(shù)很多,引起模型復雜化,就更加不好調(diào)參,進而會更加容易導致過擬合。另外,從神經(jīng)網(wǎng)絡的反向傳播的過程來看,梯度在反向傳播時,不斷的迭代會導致梯度越來越小,引起梯度趨近于0(梯度消失),梯度消失就使得權值無法更新,這個神經(jīng)元的存在就毫無意義,很難導致收斂。尤其是在圖像領域,直接使用最基本的神經(jīng)網(wǎng)絡,是不合理的。

二、卷積神經(jīng)網(wǎng)絡基本原理

1、基本闡述

現(xiàn)在有一圖像,其尺寸大小是1000像素*1000像素且設定為黑白圖像,也就是只有一個顏色通道,則一張圖片就要100萬個像素點,輸入數(shù)據(jù)維度也是100萬維。如果連接的現(xiàn)在隱含層大小也是同樣大?。?00萬個隱含節(jié)點),最后將產(chǎn)生100萬*100萬即一億萬個連接。僅僅一個全連接(FullConnected Layer),就有一萬億連接的權重需要去訓練,目前看,顯然是不劃算不現(xiàn)實。

通過局部連接(LocalConnect)方法優(yōu)化解決:由于每一個感受野只接受一小塊區(qū)域的信號,且這一小塊區(qū)域內(nèi)的像素是互相關聯(lián)的,每一個神經(jīng)元不需要接收全部像素點的信息,只需要接收局部的像素點作為輸入,而后將所有這些神經(jīng)元收到的局部信息綜合起來,就可以得到全局信息。假設局部感受野大小是10*10,即每個隱含節(jié)點只與10*10個像素點相連,現(xiàn)在只需要10*100萬即1億個連接。

現(xiàn)在隱含層每一個節(jié)點都與10*10的像素相連,即每一個隱含層節(jié)點都擁有100個參數(shù)。假設我們的局部連接方式是卷積操作,即默認每一個隱含節(jié)點的參數(shù)都完全一樣,這樣參數(shù)從1億降為100。不管圖像大小是多大,一律都是這個10*10=100個參數(shù),即卷積核尺寸,顯然卷積核對縮小參數(shù)數(shù)量貢獻非常大、意義非凡。因此,此時,我們不需要再擔心有多少隱含節(jié)點或者圖片多大,參數(shù)量只跟卷積核的大小有關,即所謂的權值共享。

總結:卷積神經(jīng)網(wǎng)絡要素是局部連接(LocalConnection)、權值共享(WeightSharing)和池化層(Pooling)中的降采樣(Down-Sampling)。其中,局部連接和權值共享降低了參數(shù)量,訓練復雜度被大大下降、過擬合被減輕。同時,權值共享還賦予了卷積網(wǎng)絡對平移的容忍性,而池化層降采樣則進一步降低了輸出參數(shù)量,并賦予模型對輕度形變的容忍性,提供了模型的泛化能力。

2、LeNet5

1994年,大名鼎鼎的LeNet5誕生,作為最早的深層卷積神經(jīng)網(wǎng)絡之一,推動了深度學習的發(fā)展。自1998年開始,在多次成功迭代之后,由Yann LeCun完成的開拓性成果被命名為LeNet5。LeCun認為,可訓練參數(shù)的卷積層是一種利用少量參數(shù)在圖像的多個位置上提取相似特征的有效方式,這和直接把每個像素作為多層神經(jīng)網(wǎng)絡的輸入不一樣。像素不應該被使用在輸入層,因為圖像具有很強的空間相關性,而使用圖像中獨立的像素直接作為輸入則利用不到這些相關性。筆者認為,這些內(nèi)容比較重要。

在當時,LeNet5的特性如下:

(1)每個卷積層包含三個部分:卷積、池化和非線性激活函數(shù);

(2)使用卷積提取空間特征;

(3)降采樣(Subsample)的平均池化層(AveragePooling);

(4)雙曲正切(Tanh)或S型(Sigmoid)的激活函數(shù);

(5)MLP作為最后的分類器;

(6)層與層之間的稀疏性連接減少計算復雜度。

三、TensorFlow 實現(xiàn)簡單的卷積網(wǎng)絡

1、簡要說明

這里使用的數(shù)據(jù)集依然是MNIST,使用兩個卷積層加一個全連接層構建一個簡單但非常有代表性的卷積神經(jīng)網(wǎng)絡,預計準確率約為99.2%左右。

2、實現(xiàn)過程

#載入MNIST數(shù)據(jù)集,創(chuàng)建默認的Interactive Session。
from tensorflow.examples.tutorials.mnist import input_data
import tensorflow as tf
mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)
sess = tf.InteractiveSession()
 
#定義初始化函數(shù),以便重復使用創(chuàng)建權重、偏置、卷積層、池化層。
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)
 return tf.Variable(initial)
 
def conv2d(x, W):
 return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')
 
def max_pool_2x2(x):
 return tf.nn.max_pool(x, ksize=[1, 2, 2, 1],
      strides=[1, 2, 2, 1], padding='SAME') 
 
#在設計卷積神經(jīng)網(wǎng)絡結構之前,定義輸入的placeholder,x是特征,y_是真實Label。
#由于卷積神經(jīng)網(wǎng)絡會使用到空間結構信息,所以,需要將1D的輸入向量轉為2D圖片結構,即從1*784的形式轉換為原始的28*28結構。
#因為只有一個顏色通道,所以最終尺寸為[-1,28,28,1],其中‘-1'代表樣本數(shù)量不固定,'1'代表顏色通道數(shù)量。
x = tf.placeholder(tf.float32, [None, 784])
y_ = tf.placeholder(tf.float32, [None, 10])
x_image = tf.reshape(x, [-1,28,28,1])
#定義第一個卷積層。
#先使用前面函數(shù)進行初始化,包括weights和bias。其中[5,5,1,32]代表卷積核尺寸為5**5,1個顏色通道,32個不同的卷積核。
W_conv1 = weight_variable([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)
#定義第二個卷積層。
#基本與第一個卷積層一樣,只是其中的卷積核數(shù)量變成64.
W_conv2 = weight_variable([5, 5, 32, 64])
b_conv2 = bias_variable([64])
h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2)
h_pool2 = max_pool_2x2(h_conv2)
 
W_fc1 = weight_variable([7 * 7 * 64, 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層,其用法是通過一個placeholder傳入keep_prob比率來控制。
keep_prob = tf.placeholder(tf.float32)
h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)
 
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)
#定義損失函數(shù)cross_entropy,這里選擇Adam優(yōu)化器。
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)
#繼續(xù)定義評測準確率操作。
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%100 == 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}))

3、執(zhí)行結果

Extracting MNIST_data/train-images-idx3-ubyte.gz
Extracting MNIST_data/train-labels-idx1-ubyte.gz
Extracting MNIST_data/t10k-images-idx3-ubyte.gz
Extracting MNIST_data/t10k-labels-idx1-ubyte.gz
step 0, training accuracy 0.1
step 100, training accuracy 0.82
step 200, training accuracy 0.9
step 300, training accuracy 0.9
step 400, training accuracy 0.96
step 500, training accuracy 0.9
step 600, training accuracy 0.94
step 700, training accuracy 0.96
step 800, training accuracy 0.94
step 900, training accuracy 0.98
.  .  .  .  .
 
step 19600, training accuracy 1
step 19700, training accuracy 1
step 19800, training accuracy 1
step 19900, training accuracy 1
test accuracy 0.9929

4、模型分析

CNN模型的最終準確率約為99.2%,基本上可以滿足對手寫數(shù)字識別準確率的初步要求。相比于之前的MLP2%的錯誤率,CNN下降了60%左右。這里的性能提升主要在于更佳的網(wǎng)絡設計,也就是卷積網(wǎng)絡對圖像特征的提取和抽象能力。依靠卷積核的權值共享,CNN的參數(shù)數(shù)量沒有爆炸,降低計算量的同時也減去了過擬合,所以,整個模型的性能會有較大的提升。

四、TensorFlow實現(xiàn)進階的卷積網(wǎng)絡

1、基本介紹

這里使用CIFAR-10數(shù)據(jù)集,包含60,000張32*32的彩色圖像,其中訓練集50,000張,測試10,000張,一共標注為10類,分別為airplane、automobile、bird、cat、deer、dog、frog、horse、shhip、truck,,每一類圖片6000張,其中沒有任何重疊情況發(fā)生,例如:automobile 只包括小型汽車,truck只包括卡車,不會出現(xiàn)一張圖片展現(xiàn)兩類物體的現(xiàn)象。

2、實現(xiàn)過程

#載入數(shù)據(jù)
import cifar10,cifar10_input
import tensorflow as tf
import numpy as np
import time
 
max_steps = 3000 #訓練輪數(shù)
batch_size = 128
data_dir = '/tmp/cifar10_data/cifar-10-batches-bin'#下載數(shù)據(jù)默認路徑。
 
 
def variable_with_weight_loss(shape, stddev, wl):
 var = tf.Variable(tf.truncated_normal(shape, stddev=stddev))
 if wl is not None:
  weight_loss = tf.multiply(tf.nn.l2_loss(var), wl, name='weight_loss')
  tf.add_to_collection('losses', weight_loss)
 return var
 
#計算CNN的損失。
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') 
 
cifar10.maybe_download_and_extract()
 
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)            
 
image_holder = tf.placeholder(tf.float32, [batch_size, 24, 24, 3])
label_holder = tf.placeholder(tf.int32, [batch_size])
 
#創(chuàng)建第一個卷積層。
weight1 = variable_with_weight_loss(shape=[5, 5, 3, 64], stddev=5e-2, wl=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)
 
#創(chuàng)建第二個卷積層。
weight2 = variable_with_weight_loss(shape=[5, 5, 64, 64], stddev=5e-2, wl=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')
#使用一個全連接層,先把第二個卷積層的輸出結果flatten,將每個樣本都變成一維向量。
reshape = tf.reshape(pool2, [batch_size, -1])
dim = reshape.get_shape()[1].value
weight3 = variable_with_weight_loss(shape=[dim, 384], stddev=0.04, wl=0.004)
bias3 = tf.Variable(tf.constant(0.1, shape=[384]))
local3 = tf.nn.relu(tf.matmul(reshape, weight3) + bias3)
#下面這個使用的全連接層,其隱含節(jié)點數(shù)下降了一半。
weight4 = variable_with_weight_loss(shape=[384, 192], stddev=0.04, wl=0.004)
bias4 = tf.Variable(tf.constant(0.1, shape=[192]))          
local4 = tf.nn.relu(tf.matmul(local3, weight4) + bias4)
 
weight5 = variable_with_weight_loss(shape=[192, 10], stddev=1/192.0, wl=0.0)
bias5 = tf.Variable(tf.constant(0.0, shape=[10]))
logits = tf.add(tf.matmul(local4, weight5), bias5)
loss = loss(logits, label_holder)
train_op = tf.train.AdamOptimizer(1e-3).minimize(loss) #0.72
top_k_op = tf.nn.in_top_k(logits, label_holder, 1)
sess = tf.InteractiveSession()
tf.global_variables_initializer().run()
tf.train.start_queue_runners()
#正式開始訓練。
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))
  
#評測模型在測試集上的準確率。
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 @ 1 = %.3f' % precision)

3、執(zhí)行結果

由于筆者試了幾次CIFAR-10模塊,最后一步失敗,所以沒能正確顯示,后續(xù)找機會再試試,但以筆者的初步判斷,在CIFAR-10安裝成功的情況下,執(zhí)行結果應該是沒問題的。感興趣的朋友可以再看看,這里就不貼出相關結果了,望各位網(wǎng)友理解。

4、模型分析

在CIFAR-10數(shù)據(jù)集上,通過一個短時間小迭代次數(shù)的訓練,可以達到約73%的準確率,后續(xù)若增加max_steps,期望準確率會逐漸增加。若max_steps比較大的化,建議使用學習速率衰減(decay)的SGD來進行訓練,其訓練過程中準確率的峰值會較高,約86%,因為這其中的L2正則和LRN層的使用均提升了模型準確率和模型的泛化性能。

五、小結

卷積網(wǎng)絡最后的幾個全連接層的作用是輸出分類結果,前面的卷積層主要做特征提取工作,直到最后的全連接層才開始對特征進行組合分配,并進行分類。

卷積層一般需要和一個池化層連接,二者組合是做圖像識別時的一個標準組件。卷積層的訓練相對于全連接層更復雜,訓練全連接層基本是進行一個些矩陣乘法運算,而目前卷積層的訓練基本上依賴于cuDNN實現(xiàn),其中的算法也相對復雜,甚至會涉及到傅里葉變換。

參考資料 主要參考資料《TensorFlow實戰(zhàn)》(黃文堅 唐源 著)(電子工業(yè)出版社)。

以上這篇TensorFlow 實戰(zhàn)之實現(xiàn)卷積神經(jīng)網(wǎng)絡的實例講解就是小編分享給大家的全部內(nèi)容了,希望能給大家一個參考,也希望大家多多支持億速云。

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內(nèi)容。

AI