TensorFlow:實戰Google深度學習框架(五)影象識別與卷積神經網路
第6章 影象識別與卷積神經網路
本章通過利用CNN實現影象識別的應用來說明如何使用TensorFlow實現卷積神經網路
6.1 影象識別問題簡介及經典資料集
1. Cifar
Cifar-10:10種不同種類的60000張影象,畫素大小為32*32的彩色影象
Cifar-100:20 個大類,大類又細分為 100 個小類別,每類包含 600 張影象。
- Cifar相比MNIST的最大區別:彩色,且每張圖包含一個種類的實體分類難度更高。
- 無論Cifar還是MNIST,相比真實環境的影象識別,還有兩個主要的問題:
- 現實生活的圖片解析度遠遠高於32*32,且解析度不固定;
- 現實生活中物體類別很多,且每張影象中不會僅僅出現一種物體;
2. ImageNet
- 由斯坦福教授李飛飛(Feifei Li)帶頭整理的資料庫,更加貼近真實生活環境。
- Imagenet資料集有1400多萬幅圖片,涵蓋2萬多個類別;其中有超過百萬的圖片有明確的類別標註和影象中物體位置的標註,
相關資訊:
1)Total number of non-empty synsets: 218412)Total number of images: 14,197,122
3)Number of images with bounding box annotations: 1,034,908
4)Number of synsets with SIFT features: 1000
5)Number of images with SIFT features: 1.2 million
6.2 卷積神經網路簡介
前面所介紹的神經網路都為全連線神經網路,本節的卷積神經網路是非全連線的神經網路,全連線與卷積神經網路的結構對比如下:
兩者之間最主要的區別就在於相鄰兩層之間的連線方式
1. 為什麼全連線不能很好的處理影象資料
最大的問題在於全連線層引數太多,使得計算速度減慢,且容易導致過擬合問題。
2. 卷積神經網路的優勢
卷積神經網路的前幾層都被組織成一個三維矩陣,可以看出前幾層的每個節點都是和上層中的部分節點相連,卷積神經網路由以下五部分構成:
1. 輸入層
輸入層是整個神經網路的輸入,在影象處理中,輸入一般代表一張圖片的畫素矩陣。在上圖中,最左側的三維矩陣就代表一張圖片。三維矩陣的長和寬代表影象的大小,深度代表了影象的色彩通道。從輸入層開始,卷積神經網路通過不同的神經網路結構將上一層的三維矩陣轉化為下一層的三維矩陣,知道最後的全連線層。
2. 卷積層
卷積層是卷積神經網路中最重要的部分。卷積層中每一個節點的輸入只是上一層神經網路的一小塊,這個小塊常用的大小有3∗3或5∗5,卷積層試圖將神經網路中的每個小塊進行更加深入的分析從而抽象得到更高層次的特徵,一般來說經過卷積層處理過的節點矩陣會變得更深。
3. 池化層
池化層神經網路不會改變三維矩陣的深度,但是它可以縮小矩陣的大小。池化操作可以認為是將一張解析度較高的圖片轉化為解析度較低的圖片。池化層可以進一步縮小最後全連線層中節點的個數,從而達到減少整個神經網路中引數的目的。
4. 全連線層
經過多輪卷積層和池化層的處理之後,卷積神經網路的最後一般會是由1-2個全連線層來給出最後的分類結果。經過幾輪卷積層和池化層處理之後,可以認為影象中的資訊已經被抽象成了資訊含量更高的特徵。 可以將卷積和池化層看成特徵提取的過程,提取完成之後,仍然需要使用全連線層來完成分類任務。
5. Softmax層(pooling層)
Softmax層主要用於分類問題,通過softmax可以得到當前樣例屬於不同種類的概率分佈情況。
6.3 卷積神經網路常用結構
6.3.1 卷積層
TensorFlow文件中將下圖的部分被稱為“過濾器”或“核心”,過濾器可以將當前層神經網路上的一個子節點矩陣轉化為下一層神經網路上的一個單位節點矩陣(長和寬都為1,但深度不限的節點矩陣)。
過濾器:
- 常用的過濾器的大小為 3*3 或 5*5 的,過濾器處理的矩陣深度和當前車網路節點矩陣的深度一致。
- 尺寸:過濾器輸入節點矩陣的大小
- 深度:輸出節點矩陣的深度
- 上圖中,左側小矩陣的尺寸為過濾器的尺寸,右側單位矩陣的深度為過濾器的深度。
- 前向傳播過程:通過左側小矩陣的節點計算出右側單位矩陣中節點的過程
過濾器的前向傳播
卷積層結構的前向傳播過程就是通過將一個過濾器從神經網路當前層的左上角移動到右下角,並且在移動過程中計算每一個對應的單位矩陣得到的。
傳播過程:左上角右上角左下角右下角全零填充:為了避免尺寸變化,可以使用“全零填充”,可以使得前向傳播後兩矩陣大小不變。
設定不同的步長:也可以調整卷積後矩陣的大小
引數共享:每一個卷積層中使用的濾波器中的引數都是一樣的(很重要的性質)
使得影象上的內容不受位置的影響,因為一幅圖上的濾波器是相同的,無論“1”出現在圖中的哪個位置,濾波的結果都是一樣的。
很大程度上減少神經網路的引數
示例:全零填充、步長為2的卷積層前向傳播過程
左上角的計算方式:
- TensorFlow實現卷積神經網路
1.建立濾波器的權值和偏置
filter_weight=tf.get_variable('weights',[5,5,3,16],
initializer=tf.truncated_initializer(stddev=0.1))
# t通過tf.get_variable的方式建立濾波器的權值和偏置
# 宣告一個4維矩陣,前面兩個是濾波器尺寸、第三個表示當前層深度、第四個表示過濾器的深度(也就是卷積核個數)
biases=tf.get_variable('biases',[16],initializer=tf.constant_initializer(0.1))
# 當前層矩陣上不同位置的偏置項也是共享的,所以偏置項個數=下一層深度,本例為16
2.實現卷積層的前向傳播
conv=tf.nn.conv2d(input,filter_weight,strides=[1,1,1,1],padding='SAME')
# tf.nn.conv2提供了一個非常方便的函式來實現卷積層的前向傳播
# 第一個輸入:當前層節點矩陣
# (比如輸入層,input[0,:,:,:]表示輸入第一張影象,input[1,:,:,:]表示輸入第二張影象
# 第二個引數:卷積層的權重
# 第三個引數不同維度上的步長(第一維和最後一維要求一定是1,因為步長只對矩陣的長和寬有效)
# 第四個引數:填充的方法,可選'SAME'(全0填充)/'VALID'(不填充)
3.加上偏置項
bias=tf.nn.bias_add(conv,biases)
# tf.nn.bias_add提供了一個方便的函式給每個節點加上偏置項
# 不直接使用加法:因為矩陣上不同位置上的節點都需要加上相同的偏置項
4.啟用
actived_conv=tf.nn.relu(bias)
# 將計算結果通過ReLU函式啟用
6.3.2 池化層
作用:
- 減少引數
- 防止過擬合
- 獲得平移不變性
常用池化型別:
- 最大池化
- 平均池化
池化層的作用範圍:
- 隻影響一個深度上的節點
- 在長、寬、深這三個維度都要進行移動
- TensorFlow實現最大池化層的前向傳播
pool = tf.nn.max_pool(actived_conv,ksize[1,3,3,1],strides=[1,2,2,1],padding='SAME')
# 第一個引數:當前層節點矩陣
# 第二個引數:過濾器尺寸
# 給出的是一個長度為4的一位陣列,但陣列的首位和末位必須為1
# 意味著池化層的過濾器是不可以跨過不同樣例或節點矩陣深度的
# 第三個引數:步長,第一維和最後一維必須為1,即池化層不能減少節點矩陣的深度或者輸入樣例的個數
# 第四個引數:填充方法,'SAME'表示全0填充,'VALID'表示不填充
- TensorFlow實現平均池化層的前向傳播
pool = tf.nn.avg_pool(actived_conv,ksize[1,3,3,1],strides=[1,2,2,1],padding='SAME')
# 第一個引數:當前層節點矩陣
# 第二個引數:過濾器尺寸
# 給出的是一個長度為4的一維陣列,但陣列的首位和末位必須為1
# 意味著池化層的過濾器是不可以跨過不同樣例或節點矩陣深度的
# 第三個引數:步長,第一維和最後一維必須為1,即池化層不能減少節點矩陣的深度或者輸入樣例的個數
# 第四個引數:填充方法,'SAME'表示全0填充,'VALID'表示不填充
卷積層、池化層樣例
# 《TensorFlow實戰Google深度學習框架》06 影象識別與卷積神經網路
# win10 Tensorflow1.0.1 python3.5.3
# CUDA v8.0 cudnn-8.0-windows10-x64-v5.1
# filename:ts06.01.py # 卷積層、池化層樣例
import tensorflow as tf
import numpy as np
# 1. 輸入矩陣
M = np.array([
[[1],[-1],[0]],
[[-1],[2],[1]],
[[0],[2],[-2]]
])
print("Matrix shape is: ",M.shape)
# Matrix shape is: (3, 3, 1)
# 2. 定義卷積過濾器, 深度為1
filter_weight = tf.get_variable('weights', [2, 2, 1, 1], initializer = tf.constant_initializer([[1, -1],[0, 2]]))
biases = tf.get_variable('biases', [1], initializer = tf.constant_initializer(1))
# 3. 調整輸入的格式符合TensorFlow的要求
M = np.asarray(M, dtype='float32')
M = M.reshape(1, 3, 3, 1)
# 4. 計算矩陣通過卷積層過濾器和池化層過濾器計算後的結果
x = tf.placeholder('float32', [1, None, None, 1])
conv = tf.nn.conv2d(x, filter_weight, strides=[1, 2, 2, 1], padding='SAME')
bias = tf.nn.bias_add(conv, biases)
pool = tf.nn.avg_pool(x, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
with tf.Session() as sess:
tf.global_variables_initializer().run()
convoluted_M = sess.run(bias, feed_dict={x: M})
pooled_M = sess.run(pool, feed_dict={x: M})
print("convoluted_M: \n", convoluted_M)
print("pooled_M: \n", pooled_M)
輸出:
Matrix shape is: (3, 3, 1)
convoluted_M:
[[[[ 7.],[ 1.]]
[[-1.],[-1.]]]]
pooled_M:
[[[[ 0.25],[ 0.5 ]]
[[ 1. ],[-2. ]]]]
6.4 經典卷積神經網路模型
6.4.1 LeNet-5模型
Yann LeCun 教授於1998年提出,是第一個成功用於數字識別的卷積神經網路,在mnist資料集上,可以達到99.2%的效果,共有7層,如下圖所示。
輸入原始影象的大小是32×32
1. 卷積層
輸入:原始影象的畫素(32*32*1)
過濾器:尺寸為5*5,深度為6,不使用全0填充,步長為1
輸出:尺寸為 32-5+1=28,深度為6
引數個數:5*5*1*6+6=156,
下一層節點矩陣的節點:28*28*6=4704,每個節點和5*5=25 個當前層節點相連
本層卷積層共有連線個數:4704*(25+1)=122304
2. 池化層
輸入:第一層的輸出,是一個28*28*6的節點矩陣
過濾器:大小為2*2,長、寬、步長都為2
輸出:14*14*6
3. 卷積層
輸入:14*14*6
過濾器:大小為5*5,深度為16,不使用0填充,步長為1
輸出:10*10*16,按標準的卷積層,本層應該有5*5*6*16+16=2416個引數
共有:10*10*16*(25+1)=41600個連線
4. 池化層
輸入:10*10*16
過濾器:大小為2*2,步長為2
輸出:矩陣大小為5*5*16
5. 全連線層
輸入:5*5*16,本來論文中稱本層為卷積層,但是因為濾波器大小為5*5,所以和全連線層沒有區別,之後就將其看成全連線層。如果將矩陣5*5*16拉成一個向量,則和第四章的無區別
輸出:節點個數為120
總共引數:5*5*16*120+120個引數。
6. 全連線層
輸入:節點個數為120個
輸出:節點個數為84個
總共引數:120*84+84=10164個
7. 全連線層
輸入:84個節點
輸出:10個節點
總共引數:84*10+10=850個
程式碼示例:
- LeNet_inference.py
# 《TensorFlow實戰Google深度學習框架》06 影象識別與卷積神經網路
# win10 Tensorflow1.0.1 python3.5.3
# CUDA v8.0 cudnn-8.0-windows10-x64-v5.1
# filename:LeNet5_infernece.py # LeNet5前向傳播
import tensorflow as tf
# 1. 設定神經網路的引數
INPUT_NODE = 784
OUTPUT_NODE = 10
IMAGE_SIZE = 28
NUM_CHANNELS = 1
NUM_LABELS = 10
CONV1_DEEP = 32
CONV1_SIZE = 5
CONV2_DEEP = 64
CONV2_SIZE = 5
FC_SIZE = 512
# 2. 定義前向傳播的過程
def inference(input_tensor, train, regularizer):
with tf.variable_scope('layer1-conv1'):
conv1_weights = tf.get_variable(
"weight", [CONV1_SIZE, CONV1_SIZE, NUM_CHANNELS, CONV1_DEEP],
initializer=tf.truncated_normal_initializer(stddev=0.1))
conv1_biases = tf.get_variable("bias", [CONV1_DEEP],
initializer=tf.constant_initializer(0.0))
conv1 = tf.nn.conv2d(input_tensor, conv1_weights, strides=[1, 1, 1, 1],
padding='SAME')
relu1 = tf.nn.relu(tf.nn.bias_add(conv1, conv1_biases))
with tf.name_scope("layer2-pool1"):
pool1 = tf.nn.max_pool(relu1, ksize = [1,2,2,1],strides=[1,2,2,1],padding="SAME")
with tf.variable_scope("layer3-conv2"):
conv2_weights = tf.get_variable(
"weight", [CONV2_SIZE, CONV2_SIZE, CONV1_DEEP, CONV2_DEEP],
initializer=tf.truncated_normal_initializer(stddev=0.1))
conv2_biases = tf.get_variable("bias", [CONV2_DEEP],
initializer=tf.constant_initializer(0.0))
conv2 = tf.nn.conv2d(pool1, conv2_weights, strides=[1, 1, 1, 1], padding='SAME')
relu2 = tf.nn.relu(tf.nn.bias_add(conv2, conv2_biases))
with tf.name_scope("layer4-pool2"):
pool2 = tf.nn.max_pool(relu2, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1],
padding='SAME')
pool_shape = pool2.get_shape().as_list()
nodes = pool_shape[1] * pool_shape[2] * pool_shape[3]
reshaped = tf.reshape(pool2, [pool_shape[0], nodes])
with tf.variable_scope('layer5-fc1'):
fc1_weights = tf.get_variable("weight", [nodes, FC_SIZE],
initializer=tf.truncated_normal_initializer(stddev=0.1))
if regularizer != None: tf.add_to_collection('losses', regularizer(fc1_weights))
fc1_biases = tf.get_variable("bias", [FC_SIZE], initializer=tf.constant_initializer(0.1))
fc1 = tf.nn.relu(tf.matmul(reshaped, fc1_weights) + fc1_biases)
if train: fc1 = tf.nn.dropout(fc1, 0.5)
with tf.variable_scope('layer6-fc2'):
fc2_weights = tf.get_variable("weight", [FC_SIZE, NUM_LABELS],
initializer=tf.truncated_normal_initializer(stddev=0.1))
if regularizer != None: tf.add_to_collection('losses', regularizer(fc2_weights))
fc2_biases = tf.get_variable("bias", [NUM_LABELS],
initializer=tf.constant_initializer(0.1))
logit = tf.matmul(fc1, fc2_weights) + fc2_biases
return logit
- LeNet_train.py
# 《TensorFlow實戰Google深度學習框架》06 影象識別與卷積神經網路
# win10 Tensorflow1.0.1 python3.5.3
# CUDA v8.0 cudnn-8.0-windows10-x64-v5.1
# filename:LeNet5_train.py # LeNet5訓練
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
import LeNet_inference
import os
import numpy as np
# 1. 定義神經網路相關的引數
BATCH_SIZE = 100
LEARNING_RATE_BASE = 0.01
LEARNING_RATE_DECAY = 0.99
REGULARIZATION_RATE = 0.0001
TRAINING_STEPS = 55000
MOVING_AVERAGE_DECAY = 0.99
MODEL_SAVE_PATH = "LeNet5_model/" # 在當前目錄下存在LeNet5_model子資料夾
MODEL_NAME = "LeNet5_model"
# 2. 定義訓練過程
def train(mnist):
# 定義輸出為4維矩陣的placeholder
x = tf.placeholder(tf.float32, [
BATCH_SIZE,
LeNet_inference.IMAGE_SIZE,
LeNet_inference.IMAGE_SIZE,
LeNet_inference.NUM_CHANNELS],
name='x-input')
y_ = tf.placeholder(tf.float32, [None, LeNet_inference.OUTPUT_NODE], name='y-input')
regularizer = tf.contrib.layers.l2_regularizer(REGULARIZATION_RATE)
y = LeNet_inference.inference(x, True, regularizer)
global_step = tf.Variable(0, trainable=False)
# 定義損失函式、學習率、滑動平均操作以及訓練過程。
variable_averages = tf.train.ExponentialMovingAverage(MOVING_AVERAGE_DECAY, global_step)
variables_averages_op = variable_averages.apply(tf.trainable_variables())
cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=y, labels=tf.argmax(y_, 1))
cross_entropy_mean = tf.reduce_mean(cross_entropy)
loss = cross_entropy_mean + tf.add_n(tf.get_collection('losses'))
learning_rate = tf.train.exponential_decay(
LEARNING_RATE_BASE,
global_step,
mnist.train.num_examples / BATCH_SIZE, LEARNING_RATE_DECAY,
staircase=True)
train_step = tf.train.GradientDescentOptimizer(learning_rate).minimize(loss, global_step=global_step)
with tf.control_dependencies([train_step, variables_averages_op]):
train_op = tf.no_op(name='train')
# 初始化TensorFlow持久化類。
saver = tf.train.Saver()
with tf.Session() as sess:
tf.global_variables_initializer().run()
for i in range(TRAINING_STEPS):
xs, ys = mnist.train.next_batch(BATCH_SIZE)
reshaped_xs = np.reshape(xs, (
BATCH_SIZE,
LeNet_inference.IMAGE_SIZE,
LeNet_inference.IMAGE_SIZE,
LeNet_inference.NUM_CHANNELS))
_, loss_value, step = sess.run([train_op, loss, global_step], feed_dict={x: reshaped_xs, y_: ys})
if i % 1000 == 0:
print("After %d training step(s), loss on training batch is %g." % (step, loss_value))
saver.save(sess, os.path.join(MODEL_SAVE_PATH, MODEL_NAME), global_step=global_step)
# 3. 主程式入口
def main(argv=None):
mnist = input_data.read_data_sets("MNIST_data", one_hot=True)
train(mnist)
if __name__ == '__main__':
tf.app.run()
輸出:
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
After 1 training step(s), loss on training batch is 6.10953.
After 1001 training step(s), loss on training batch is 0.801586.
After 2001 training step(s), loss on training batch is 0.829287.
After 3001 training step(s), loss on training batch is 0.655056.
After 4001 training step(s), loss on training batch is 0.698159.
After 5001 training step(s), loss on training batch is 0.744295.
After 6001 training step(s), loss on training batch is 0.657604.
After 7001 training step(s), loss on training batch is 0.697003.
After 8001 training step(s), loss on training batch is 0.685206.
After 9001 training step(s), loss on training batch is 0.651352.
After 10001 training step(s), loss on training batch is 0.729663.
After 11001 training step(s), loss on training batch is 0.666927.
After 12001 training step(s), loss on training batch is 0.65114.
After 13001 training step(s), loss on training batch is 0.648548.
...
- LeNet_eval.py(測試該網路在mnist的正確率,達到99.4%,巨幅高於第五章的98.4%)
# 《TensorFlow實戰Google深度學習框架》06 影象識別與卷積神經網路
# win10 Tensorflow1.0.1 python3.5.3
# CUDA v8.0 cudnn-8.0-windows10-x64-v5.1
# filename:LeNet5_eval.py # 測試
import time
import math
import tensorflow as tf
import numpy as np
from tensorflow.examples.tutorials.mnist import input_data
import LeNet5_infernece
import LeNet5_train
def evaluate(mnist):
with tf.Graph().as_default() as g:
# 定義輸出為4維矩陣的placeholder
x = tf.placeholder(tf.float32, [
mnist.test.num_examples,
#LeNet5_train.BATCH_SIZE,
LeNet5_infernece.IMAGE_SIZE,
LeNet5_infernece.IMAGE_SIZE,
LeNet5_infernece.NUM_CHANNELS],
name='x-input')
y_ = tf.placeholder(tf.float32, [None, LeNet5_infernece.OUTPUT_NODE], name='y-input')
validate_feed = {x: mnist.test.images, y_: mnist.test.labels}
global_step = tf.Variable(0, trainable=False)
regularizer = tf.contrib.layers.l2_regularizer(LeNet5_train.REGULARIZATION_RATE)
y = LeNet5_infernece.inference(x, False, regularizer)
correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
variable_averages = tf.train.ExponentialMovingAverage(LeNet5_train.MOVING_AVERAGE_DECAY)
variables_to_restore = variable_averages.variables_to_restore()
saver = tf.train.Saver(variables_to_restore)
#n = math.ceil(mnist.test.num_examples / LeNet5_train.BATCH_SIZE)
n = math.ceil(mnist.test.num_examples / mnist.test.num_examples)
for i in range(n):
with tf.Session() as sess:
ckpt = tf.train.get_checkpoint_state(LeNet5_train.MODEL_SAVE_PATH)
if ckpt and ckpt.model_checkpoint_path:
saver.restore(sess, ckpt.model_checkpoint_path)
global_step = ckpt.model_checkpoint_path.split('/')[-1].split('-')[-1]
xs, ys = mnist.test.next_batch(mnist.test.num_examples)
#xs, ys = mnist.test.next_batch(LeNet5_train.BATCH_SIZE)
reshaped_xs = np.reshape(xs, (
mnist.test.num_examples,
#LeNet5_train.BATCH_SIZE,
LeNet5_infernece.IMAGE_SIZE,
LeNet5_infernece.IMAGE_SIZE,
LeNet5_infernece.NUM_CHANNELS))
accuracy_score = sess.run(accuracy, feed_dict={x:reshaped_xs, y_:ys})
print("After %s training step(s), test accuracy = %g" % (global_step, accuracy_score))
else:
print('No checkpoint file found')
return
# 主程式
def main(argv=None):
mnist = input_data.read_data_sets("../../../datasets/MNIST_data", one_hot=True)
evaluate(mnist)
if __name__ == '__main__':
tf.app.run()
LeNet-5模型的缺點
不能很好的解決所有問題,比如類似ImageNet的複雜資料集
如何設計卷積神經網路架構
該正則表達公式總結了經典的用於影象分類問題的卷積神經網路架構
其中:
- +表示一層或多層
- “池化層?”表示沒有或一層池化層,因為有的網路是通過直接調整卷積層步長來完成的引數減少
- 多倫卷積核池化之後,輸出層之前會有1~2個全連線層
LeNet-5的正則表達:
從VGGNet觀察正則表示式的特點:
convX-Y:濾波器的邊長為X,深度為Y
VGGNet中的濾波器的邊長基本都為3或1
LeNet-5中使用了大小為5*5的濾波器,一般都不會超過5,但也有的設定為7*7,甚至11*11的
在濾波器的深度選擇上,大部分都採用逐層遞增的方式,VGG中,每經過一個池化層,濾波器的深度*2,
卷積層的步長一般為2,但也有例外(使用2或3)
池化層配置相對簡單,用的最多的是最大池化層,邊長一般為2或3,步長一般為2或3
6.4.2 Inception模型
已知濾波器的大小可選,但是沒有先驗知識幫助我們去選擇其大小,所以Inception模型將不同尺寸的濾波器的濾波結果通過並聯的方式結合在一起,即將得到的矩陣拼接起來。
Inception模型會使用不同尺寸的濾波器處理矩陣,使用全0填充和步長為1的方法獲得的特徵圖譜大小是相同的,不會影響矩陣的拼接。
Inception-v3模型共有46層,由11個Inception模組構成,上圖中方框標註的就是一個模組,Inception-v3模型有96個卷積層,直接使用上文的TensorFlow模型會非常冗長,所以此處介紹TensorFlow-Slim工具來更加簡潔的實現同樣結構的神經網路程式碼量。
1.直接使用TensorFlow原始API實現卷積層
with tf.variable_scope(scope_name):
weights=tf.get_variable("weight",...)
biases=tf.get_variable("bias",...)
conv=tf.nn.conv2d(...)
relu=tf.nn.relu(tf.nn.bias_add(conv,biases))
2.使用TensorFlow-Slim實現卷積層
tf.contrib.slim.conv2d (inputs,
num_outputs,#[卷積核個數]
kernel_size,#[高度,寬度]
stride=1,#步長
padding='SAME',#VALID
存在於tensorflow.contrib
的庫中,匯入方法:import tensorflow.contrib.slim as slim
net=slim.conv2d(input,32,[3,3])
# 可以在一行中實現一個卷積層的前向傳播演算法
# 第一個引數:輸入節點矩陣
# 第二個引數:當前卷積層過濾器的深度
# 第三個引數:過濾器的尺寸
# 可選步長、填充、啟用函式、變數名稱空間等
6.5 卷積神經網路實現遷移學習
6.5.1 遷移學習的介紹
從上表可以看出,隨著模型層數及複雜度的增加,模型在ImageNet上的錯誤率也隨之降低,然而訓練神經網路需要非常多的標註資料,ImageNet資料集中有120萬標註影象,所以才能使得152層的ResNet模型達到分類的96.5%的正確率,實際中很難收集到如此多的標記資料,即使收集也會花費很多的人力物力,並且海量的訓練資料使得複雜的卷積神經網路的訓練需要幾天甚至幾周,為了解決標註資料和訓練時間的問題,提出了遷移學習。
遷移學習:將一個問題上訓練好的模型通過簡單的調整使其適用於一個新的問題。比如可以保留訓練好的Inception-v3模型中所有卷積層的引數,只是替換最後一層全連線層,可以解決一個新的影象分類問題。
瓶頸層:在最後這一層全連線層之前的網路層稱之為瓶頸層。
- 將新的影象通過訓練好的卷積神經網路直到瓶頸層的過程可以看成是對影象進行特徵提取的過程。
- 可以認為瓶頸層的輸出節點向量可以被作為任何影象的一個更加精簡且表達能力更強的特徵向量,於是可以用該訓練好的神經網路在新的資料集上完成對影象特徵的提取。
- 再將提取到的