GoogLeNet Inception 系列
文章目錄
- 論文信息
- Inception 架構動機
- Inception 架構細節
- GoogLeNet 網絡結構 (Inception-v1)
- GoogLeNet 與 VGG 對比
- Inception-v2
- Inception-v3
- Inception-v3 微調
論文信息
[v1] Going Deeper withConvolutions, 6.67% test error,2014.9
論文地址:http://arxiv.org/abs/1409.4842
[v2] Batch Normalization:Accelerating Deep Network Training by Reducing Internal Covariate Shift, 4.8% test error,2015.2
論文地址:http://arxiv.org/abs/1502.03167
[v3] Rethinking theInception Architecture for Computer Vision, 3.5%test error,2015.12
論文地址:http://arxiv.org/abs/1512.00567
[v4] Inception-v4,Inception-ResNet and the Impact of Residual Connections on Learning, 3.08% test error,2016.2
論文地址:http://arxiv.org/abs/1602.07261
Inception 架構動機
提高深度神經網絡性能,最直接的方式是增加它們的尺寸:
-
不僅包括增加深度:網絡層次的數目
-
也包括增加它的寬度:每一層的單元數目。
但是這個簡單方案有兩個主要的缺點:
-
更大的尺寸通常意味著更多的參數,這會使增大的網絡更容易過擬合,尤其是在訓練集的標注樣本有限的情況下。
-
另一個缺點是計算資源使用的顯著增加。例如,在一個深度視覺網絡中,如果兩個卷積層相連,它們的濾波器數目的任何均勻增加都會引起計算量平方式的增加。如果增加的能力使用時效率低下(例如,如果大多數權重結束時接近于0),那么會浪費大量的計算能力。
解決這兩個問題的一個基本的方式就是引入稀疏性并將全連接層替換為稀疏的全連接層,甚至是卷積層。遺憾的是,切換到稀疏矩陣并不可行。當碰到在非均勻的稀疏數據結構上進行數值計算時,現在的計算架構效率非常低下。即使算法運算的數量減少100倍,查詢和緩存丟失上的開銷仍占主導地位。
Inception架構,嘗試利用濾波器水平的稀疏性,通過密集矩陣計算來提高硬件計算效率。
Inception 架構細節
Inception 架構,使用不同尺寸的卷積核,在同一層提取不同的特征:
-
一個 block 就包含1×11\times11×1卷積,3×33\times33×3卷積,5×55\times55×5卷積,3×33\times33×3 池化。 使用這樣的尺寸不是必需的,可以根據需要進行調整。
-
這樣,網絡中每一層都能學習到“稀疏”(3×33\times33×3、5×55\times55×5)或“不稀疏”(1×11\times11×1)的特征,既增加了網絡的寬度,也增加了網絡對尺度的適應性。
-
通過deep concat 合成 block 的特征集合,獲得非線性屬性。
在具有大量濾波器的卷積層之上,即使適量的 5×55×55×5 卷積也可能是非常昂貴的。池化層輸出和卷積層輸出的合并,會導致輸出數量不可避免的增加。雖然這種架構可能會覆蓋最優稀疏結構,但它會非常低效,導致在幾個階段內計算量爆炸。
這導致了Inception架構的第二個想法:在昂貴的 3×33×33×3 和 5×55×55×5 卷積之前,用 1×11×11×1 卷積,在降維的同時,增加了ReLU(線性修正)單元。改進后的結構圖如下:
通常,Inception網絡是一個由上述類型的模塊互相堆疊組成的網絡,偶爾會有步長為2的最大池化層將網絡分辨率減半。
GoogLeNet 網絡結構 (Inception-v1)
GoogLeNet 網絡層次結構:查看
-
所有的卷積都使用了ReLU(修正線性激活),包括Inception模塊內部的卷積,網絡有22個參數層。
-
作者將全連接層變為平均池化,提高了 top-1 大約 0.6%0.6\%0.6% 的準確率。即使移除了全連接層,DropOutDropOutDropOut 的使用仍是必須的。
-
作者通過在中間層中添加輔助分類器,在提供正則化的同時克服梯度消失問題。在訓練期間,它們的損失以 0.30.30.3 的權重比例,加到網絡的整個損失上。在預測時,不使用輔助分類器。作者后面的控制實驗表明,輔助網絡的影響相對較小(約0.50.50.5),只需要其中一個就能取得同樣的效果。
包括輔助分類器在內的附加網絡的具體結構如下:
-
一個濾波器大小為 5×55×55×5,步長為 333 的平均池化層。
-
具有128個濾波器的 1×11×11×1 卷積,用于降維和修正線性激活。
-
一個全連接層,具有 102410241024 個單元和修正線性激活。
-
丟棄70%輸出的丟棄層。
-
使用帶有 softmax 損失的線性層作為分類器(作為主分類器預測同樣的1000類,但在預測時移除)。
GoogLeNet 與 VGG 對比
VGG 繼承了 LeNet 以及 AlexNet 的一些框架結構,而 GoogLeNet 則做了更加大膽的網絡結構嘗試,雖然深度只有 222222 層,但大小卻比 AlexNet 和 VGG 小很多:
-
GoogLeNet 參數為 500500500 萬個
-
AlexNet 參數個數是 GoogLeNet 的 121212 倍
-
VGG 參數又是 AlexNet 的 333 倍
因此在內存或計算資源有限時,GoogLeNet 是比較好的選擇;從模型結果來看,GoogLeNet 的性能卻更加優越。
但 GoogLeNet 也有自身的問題:
-
GoogLeNet 為了防止梯度消失,在前面的層增加了兩個損失函數,softmax0 和 softmax1,正是這兩個損失函數導致了 GoogLeNet 的可拓展性沒有 VGG 那么強
-
Inception 架構的復雜性使得更難以對網絡進行更改。如果單純地放大架構,大部分的計算收益可能會立即丟失。
但是即便如此,GoogLeNet 在分類領域還是非常好用的。
Inception-v2
Inception-v2 的結構,在 v1 的基礎之上主要做了以下改進:
-
輸入從 224×224224×224224×224 變為 229×229229×229229×229。
-
使用 BN 層 (Batch-normalized),將每一層的輸出都規范化到一個N(0,1)N(0,1)N(0,1) 的正態分布,這將有助于訓練,因為下一層不必學習輸入數據中的偏移,可以專注與如何更好地組合特征。
BN 層在 Inception-v2 中的優化效果,使其幾乎成為深度網絡的必備
-
使用 222 個 3×33×33×3 的卷積代替 5×55×55×5 的卷積,這樣既可以獲得相同的視野,在減少參數量的同時,還間接增加了網絡的深度。網絡結構如下:
-
在理論上,可以通過非對稱卷積( 1×n1×n1×n 卷積,后面接一個 n×1n×1n×1 卷積)來替換任何 n×nn×nn×n 的對稱卷積。
-
在特征圖大小為12~20的中間層,通過使用 1×71×71×7 卷積,后接 7×17×17×1 卷積可以獲得非常好的結果。網絡結構如下:
- 在特征圖大小為 8×88×88×8 的高維特征上,可以增加多維濾波器輸出,以此來產生高維的稀疏特征:
在 Imagenet 數據集上,Inception-v2 與 Inception-v1 相比,分類錯誤率由29%降為23.4%。
Inception-v3
Inception 模塊之間特征圖的縮小,主要有下面兩種方式:
-
Inception-v2 采用左圖的方式,即在不同的 Inception 塊之間(35/17/8的特征圖大小)先用Pooling進行下采樣,再進行 Inception 操作。
這種操作會造成表達瓶頸問題,也就是說特征圖的大小不應該出現急劇的衰減(只經過一層就驟降)。如果出現急劇縮減,將會丟失大量的信息,對模型的訓練造成困難。
-
右圖是先進行 Inception 操作,再進行 Pooling 下采樣,但是這樣參數量明顯多于左圖。
-
因此,Inception-v3 采用一種并行的降維結構。35/17 之間的特征圖尺寸減小方式:
- 17/8 之間的特征圖尺寸縮小方式:
Inception-v3 微調
Inception-v3 權重文件下載: 地址
Inception-v3 微調:
import glob import os.path import random import numpy as np import tensorflow as tf from tensorflow.python.platform import gfile# 數據參數 MODEL_DIR = 'inception_dec_2015/' # inception-v3模型的文件夾 MODEL_FILE = 'tensorflow_inception_graph.pb' # inception-v3模型文件名 CACHE_DIR = 'tmp/bottleneck' # 圖像的特征向量保存地址 INPUT_DATA = './citySpace/outData/train' # 圖片數據文件夾 VALIDATION_PERCENTAGE = 10 # 驗證數據的百分比 TEST_PERCENTAGE = 10 # 測試數據的百分比# inception-v3模型參數 BOTTLENECK_TENSOR_SIZE = 2048 # inception-v3模型瓶頸層的節點個數 BOTTLENECK_TENSOR_NAME = 'pool_3/_reshape:0' # inception-v3模型中代表瓶頸層結果的張量名稱 JPEG_DATA_TENSOR_NAME = 'DecodeJpeg/contents:0' # 圖像輸入張量對應的名稱# 神經網絡的訓練參數 LEARNING_RATE = 0.01 STEPS = 1000 BATCH = 100 CHECKPOINT_EVERY = 100 NUM_CHECKPOINTS = 5# 從數據文件夾中讀取所有的圖片列表并按訓練、驗證、測試分開 def create_image_lists(validation_percentage, test_percentage):result = {} # 保存所有圖像。key為類別名稱。value也是字典,存儲了所有的圖片名稱sub_dirs = [x[0] for x in os.walk(INPUT_DATA)] # 獲取所有子目錄is_root_dir = True # 第一個目錄為當前目錄,需要忽略# 分別對每個子目錄進行操作for sub_dir in sub_dirs:if is_root_dir:is_root_dir = Falsecontinue# 獲取當前目錄下的所有有效圖片extensions = {'PNG', 'png'}file_list = [] # 存儲所有圖像dir_name = os.path.basename(sub_dir) # 獲取路徑的最后一個目錄名字for extension in extensions:file_glob = os.path.join(INPUT_DATA, dir_name, '*.' + extension)file_list.extend(glob.glob(file_glob))if not file_list:continue# 將當前類別的圖片隨機分為訓練數據集、測試數據集、驗證數據集label_name = dir_name.lower() # 通過目錄名獲取類別的名稱training_images = []testing_images = []validation_images = []for file_name in file_list:base_name = os.path.basename(file_name) # 獲取該圖片的名稱chance = np.random.randint(100) # 隨機產生100個數代表百分比if chance < validation_percentage:validation_images.append(base_name)elif chance < (validation_percentage + test_percentage):testing_images.append(base_name)else:training_images.append(base_name)# 將當前類別的數據集放入結果字典result[label_name] = {'dir': dir_name,'training': training_images,'testing': testing_images,'validation': validation_images}# 返回整理好的所有數據return result# 通過類別名稱、所屬數據集、圖片編號獲取一張圖片的地址 def get_image_path(image_lists, image_dir, label_name, index, category):label_lists = image_lists[label_name] # 獲取給定類別中的所有圖片category_list = label_lists[category] # 根據所屬數據集的名稱獲取該集合中的全部圖片mod_index = index % len(category_list) # 規范圖片的索引base_name = category_list[mod_index] # 獲取圖片的文件名sub_dir = label_lists['dir'] # 獲取當前類別的目錄名full_path = os.path.join(image_dir, sub_dir, base_name) # 圖片的絕對路徑return full_path# 通過類別名稱、所屬數據集、圖片編號獲取特征向量值的地址 def get_bottleneck_path(image_lists, label_name, index, category):return get_image_path(image_lists, CACHE_DIR, label_name, index,category) + '.txt'# 使用inception-v3處理圖片獲取特征向量 def run_bottleneck_on_image(sess, image_data, image_data_tensor,bottleneck_tensor):bottleneck_values = sess.run(bottleneck_tensor,{image_data_tensor: image_data})bottleneck_values = np.squeeze(bottleneck_values) # 將四維數組壓縮成一維數組return bottleneck_values# 獲取一張圖片經過inception-v3模型處理后的特征向量 def get_or_create_bottleneck(sess, image_lists, label_name, index, category,jpeg_data_tensor, bottleneck_tensor):# 獲取一張圖片對應的特征向量文件的路徑label_lists = image_lists[label_name]sub_dir = label_lists['dir']sub_dir_path = os.path.join(CACHE_DIR, sub_dir)if not os.path.exists(sub_dir_path):os.makedirs(sub_dir_path)bottleneck_path = get_bottleneck_path(image_lists, label_name, index,category)# 如果該特征向量文件不存在,則通過inception-v3模型計算并保存if not os.path.exists(bottleneck_path):image_path = get_image_path(image_lists, INPUT_DATA, label_name, index,category) # 獲取圖片原始路徑image_data = gfile.FastGFile(image_path, 'rb').read() # 獲取圖片內容bottleneck_values = run_bottleneck_on_image(sess, image_data, jpeg_data_tensor,bottleneck_tensor) # 通過inception-v3計算特征向量# 將特征向量存入文件bottleneck_string = ','.join(str(x) for x in bottleneck_values)with open(bottleneck_path, 'w') as bottleneck_file:bottleneck_file.write(bottleneck_string)else:# 否則直接從文件中獲取圖片的特征向量with open(bottleneck_path, 'r') as bottleneck_file:bottleneck_string = bottleneck_file.read()bottleneck_values = [float(x) for x in bottleneck_string.split(',')]# 返回得到的特征向量return bottleneck_values# 隨機獲取一個batch圖片作為訓練數據 def get_random_cached_bottlenecks(sess, n_classes, image_lists, how_many,category, jpeg_data_tensor,bottleneck_tensor):bottlenecks = []ground_truths = []for _ in range(how_many):# 隨機一個類別和圖片編號加入當前的訓練數據label_index = random.randrange(n_classes)label_name = list(image_lists.keys())[label_index]image_index = random.randrange(65535)bottleneck = get_or_create_bottleneck(sess, image_lists, label_name, image_index, category,jpeg_data_tensor, bottleneck_tensor)ground_truth = np.zeros(n_classes, dtype=np.float32)ground_truth[label_index] = 1.0bottlenecks.append(bottleneck)ground_truths.append(ground_truth)return bottlenecks, ground_truths# 獲取全部的測試數據 def get_test_bottlenecks(sess, image_lists, n_classes, jpeg_data_tensor,bottleneck_tensor):bottlenecks = []ground_truths = []label_name_list = list(image_lists.keys())# 枚舉所有的類別和每個類別中的測試圖片for label_index, label_name in enumerate(label_name_list):category = 'testing'for index, unused_base_name in enumerate(image_lists[label_name][category]):bottleneck = get_or_create_bottleneck(sess, image_lists, label_name, index, category,jpeg_data_tensor, bottleneck_tensor)ground_truth = np.zeros(n_classes, dtype=np.float32)ground_truth[label_index] = 1.0bottlenecks.append(bottleneck)ground_truths.append(ground_truth)return bottlenecks, ground_truthsdef main(_):# 讀取所有的圖片image_lists = create_image_lists(VALIDATION_PERCENTAGE, TEST_PERCENTAGE)n_classes = len(image_lists.keys())with tf.Graph().as_default() as graph:# 讀取訓練好的inception-v3模型with gfile.FastGFile(os.path.join(MODEL_DIR, MODEL_FILE), 'rb') as f:graph_def = tf.GraphDef()graph_def.ParseFromString(f.read())# 加載inception-v3模型,并返回數據輸入張量和瓶頸層輸出張量bottleneck_tensor, jpeg_data_tensor = tf.import_graph_def(graph_def,return_elements=[BOTTLENECK_TENSOR_NAME, JPEG_DATA_TENSOR_NAME])# 定義新的神經網絡輸入bottleneck_input = tf.placeholder(tf.float32, [None, BOTTLENECK_TENSOR_SIZE],name='BottleneckInputPlaceholder')# 定義新的標準答案輸入ground_truth_input = tf.placeholder(tf.float32, [None, n_classes], name='GroundTruthInput')# 定義一層全連接層解決新的圖片分類問題with tf.name_scope('final_training_ops'):weights = tf.Variable(tf.truncated_normal([BOTTLENECK_TENSOR_SIZE, n_classes], stddev=0.1))biases = tf.Variable(tf.zeros([n_classes]))logits = tf.matmul(bottleneck_input, weights) + biasesfinal_tensor = tf.nn.softmax(logits)# 定義交叉熵損失函數cross_entropy = tf.nn.softmax_cross_entropy_with_logits(logits=logits, labels=ground_truth_input)cross_entropy_mean = tf.reduce_mean(cross_entropy)train_step = tf.train.GradientDescentOptimizer(LEARNING_RATE).minimize(cross_entropy_mean)# 計算正確率with tf.name_scope('evaluation'):correct_prediction = tf.equal(tf.argmax(final_tensor, 1), tf.argmax(ground_truth_input, 1))evaluation_step = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))# 訓練過程with tf.Session(graph=graph) as sess:init = tf.global_variables_initializer().run()# 模型和摘要的保存目錄import timetimestamp = str(int(time.time()))out_dir = os.path.abspath(os.path.join(os.path.curdir, 'runs', timestamp))print('\nWriting to {}\n'.format(out_dir))# 損失值和正確率的摘要loss_summary = tf.summary.scalar('loss', cross_entropy_mean)acc_summary = tf.summary.scalar('accuracy', evaluation_step)# 訓練摘要train_summary_op = tf.summary.merge([loss_summary, acc_summary])train_summary_dir = os.path.join(out_dir, 'summaries', 'train')train_summary_writer = tf.summary.FileWriter(train_summary_dir,sess.graph)# 開發摘要dev_summary_op = tf.summary.merge([loss_summary, acc_summary])dev_summary_dir = os.path.join(out_dir, 'summaries', 'dev')dev_summary_writer = tf.summary.FileWriter(dev_summary_dir, sess.graph)# 保存檢查點checkpoint_dir = os.path.abspath(os.path.join(out_dir, 'checkpoints'))checkpoint_prefix = os.path.join(checkpoint_dir, 'model')if not os.path.exists(checkpoint_dir):os.makedirs(checkpoint_dir)saver = tf.train.Saver(tf.global_variables(), max_to_keep=NUM_CHECKPOINTS)for i in range(STEPS):# 每次獲取一個batch的訓練數據train_bottlenecks, train_ground_truth = get_random_cached_bottlenecks(sess, n_classes, image_lists, BATCH, 'training',jpeg_data_tensor, bottleneck_tensor)_, train_summaries = sess.run([train_step, train_summary_op],feed_dict={bottleneck_input: train_bottlenecks,ground_truth_input: train_ground_truth})# 保存每步的摘要train_summary_writer.add_summary(train_summaries, i)# 在驗證集上測試正確率if i % 100 == 0 or i + 1 == STEPS:validation_bottlenecks, validation_ground_truth = get_random_cached_bottlenecks(sess, n_classes, image_lists, BATCH, 'validation',jpeg_data_tensor, bottleneck_tensor)validation_accuracy, dev_summaries = sess.run([evaluation_step, dev_summary_op],feed_dict={bottleneck_input: validation_bottlenecks,ground_truth_input: validation_ground_truth})print('Step %d : Validation accuracy on random sampled %d examples = %.1f%%'% (i, BATCH, validation_accuracy * 100))# 每隔checkpoint_every保存一次模型和測試摘要if i % CHECKPOINT_EVERY == 0:dev_summary_writer.add_summary(dev_summaries, i)path = saver.save(sess, checkpoint_prefix, global_step=i)print('Saved model checkpoint to {}\n'.format(path))# 最后在測試集上測試正確率test_bottlenecks, test_ground_truth = get_test_bottlenecks(sess, image_lists, n_classes, jpeg_data_tensor, bottleneck_tensor)test_accuracy = sess.run(evaluation_step,feed_dict={bottleneck_input: test_bottlenecks,ground_truth_input: test_ground_truth})print('Final test accuracy = %.1f%%' % (test_accuracy * 100))# 保存標簽output_labels = os.path.join(out_dir, 'labels.txt')with tf.gfile.FastGFile(output_labels, 'w') as f:keys = list(image_lists.keys())for i in range(len(keys)):keys[i] = '%2d -> %s' % (i, keys[i])f.write('\n'.join(keys) + '\n')if __name__ == '__main__':tf.app.run()Inception-v3 預測:
import tensorflow as tf import numpy as np# 模型目錄 CHECKPOINT_DIR = './runs/1537964662/checkpoints' INCEPTION_MODEL_FILE = 'inception_dec_2015/tensorflow_inception_graph.pb'# inception-v3模型參數 BOTTLENECK_TENSOR_NAME = 'pool_3/_reshape:0' # inception-v3模型中代表瓶頸層結果的張量名稱 JPEG_DATA_TENSOR_NAME = 'DecodeJpeg/contents:0' # 圖像輸入張量對應的名稱# 測試數據 file_path = './data/test.jpg' y_test = [4]# 讀取數據 image_data = tf.gfile.FastGFile(file_path, 'rb').read()# 評估 checkpoint_file = tf.train.latest_checkpoint(CHECKPOINT_DIR) with tf.Graph().as_default() as graph:with tf.Session().as_default() as sess:# 讀取訓練好的inception-v3模型with tf.gfile.FastGFile(INCEPTION_MODEL_FILE, 'rb') as f:graph_def = tf.GraphDef()graph_def.ParseFromString(f.read())# 加載inception-v3模型,并返回數據輸入張量和瓶頸層輸出張量bottleneck_tensor, jpeg_data_tensor = tf.import_graph_def(graph_def,return_elements=[BOTTLENECK_TENSOR_NAME, JPEG_DATA_TENSOR_NAME])# 使用inception-v3處理圖片獲取特征向量bottleneck_values = sess.run(bottleneck_tensor,{jpeg_data_tensor: image_data})# 將四維數組壓縮成一維數組,由于全連接層輸入時有batch的維度,所以用列表作為輸入bottleneck_values = [np.squeeze(bottleneck_values)]# 加載元圖和變量saver = tf.train.import_meta_graph('{}.meta'.format(checkpoint_file))saver.restore(sess, checkpoint_file)# 通過名字從圖中獲取輸入占位符input_x = graph.get_operation_by_name('BottleneckInputPlaceholder').outputs[0]# 我們想要評估的tensorspredictions = graph.get_operation_by_name('evaluation/ArgMax').outputs[0]# 收集預測值all_predictions = []all_predictions = sess.run(predictions, {input_x: bottleneck_values})# 如果提供了標簽則打印正確率 if y_test is not None:correct_predictions = float(sum(all_predictions == y_test))print('\nTotal number of test examples: {}'.format(len(y_test)))print('Accuracy: {:g}'.format(correct_predictions / float(len(y_test))))總結
以上是生活随笔為你收集整理的GoogLeNet Inception 系列的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 激活函数 activation func
- 下一篇: Xception