打造自己的图像识别模型
實驗三 打造自己的圖像識別模型
實驗編號:BQP-A-EasyHBase
????實驗類別 ??????實驗 ?????
實驗學時 ??????3.0 ??????
一、實驗目的
通過之前對Mnist、CIFAR-10數據庫的操作使得學生初步了解了Tensorflow中的數據讀取原理以及圖像識別模型構建的基本架構。本次實驗通過對已經預訓練好的ImageNet模型進行微調(Fine-une)來使學生了解如何使用TensorFlow在自己的圖像數據上訓練深度學習模型。整個過程中能夠培養學生動手能力,使學生提高綜合能力,最終達以下到教學目標:
二、實驗原理
在自己的數據集上訓練一個新的深度學習模型時,一般采取在預訓練ImageNet上進行微調的方法。微調的原理以VGG16為例進行講解。
圖3-1 VGG16 結構示意圖
?????? 如圖3-1所示,VGG16的結構為卷積+全連接層。卷積層分為5個部分共13層,即圖中的conv?1-conv?5。還有3層是全連接層,即圖中的fc6、fc7、fc8。卷積層加上全連接層合起來一共為16層,因此它被稱為VGG16。fc8層的輸入是fc7層的特征,輸出是1000類的概率,這1000類正好對應了ImageNet模型中的1000個類別。在自己的數據中,類別數一般不是1000類,因此fc8層的結構在此時是不適用的,必須將fc8層去掉,用符合數據集類別數的全連接層,作為新的fc8。比如數據集為5類,那么新的fc8的輸出也應當是5類。
??? ???在ImageNet數據集上訓練過的VGG16中的參數已經包含了大量有用的卷積過濾器,因此在訓練時,網絡的參數的初始值并不是隨機化生成的,而是采用VGG16在ImageNet上已經訓練好的參數作為訓練的初始值。這樣做不僅可以節約大量訓練時間,而且有助于分類器性能的提高。
???? ??載入VGG16的參數后,就可以開始訓練了。此時需要指定訓練層數的范圍。一般來說,可以選擇以下幾種范圍進行訓練:
???????第一種:只訓練fc8。保持其他層的參數不動。此時VGG16當作類似于一個“特征提取器”,用fc7層提取的特征做一個Softmax模型分類。這樣做的好處是訓練速度快,但往往性能不會太好。
???????第二種:訓練所有參數。還可以對網絡中的所有參數進行訓練,這種方法的訓練速度可能比較慢,但是能取得較高的性能。
????第三種:訓練部分參數。通常是固定淺層參數不變,訓練深層參數。如固定conv1、conv2部分的參數不訓練,只訓練conv?3、conv4、conv?5、fc6、fc7、fc8的參數。
????這種訓練方法就是對神經網絡模型做微調。借助微調,可以從預訓練模型出發,將神經網絡應用到自己的數據集上。
三、實驗環境
虛擬機數量:3臺
操作系統:Ubuntu 14.04
實驗環境及版本:
Hadoop:Hadoop 2.2.0
Java:java version "1.7.0_51"
四、實驗內容及步驟
1.準備工作
(1)Tensorflow環境的搭建:
必須可以正常啟動Tensorflow環境,確保Tensorflow能夠上傳和讀寫文件。
??本次實驗的數據來源于自己的數據集,但是在導入數據之前,我們必須先將數據劃分為訓練數據集和驗證數據集。訓練集用于訓練模型,驗證集用于驗證模型的準確度。本次實驗采用的是衛星圖像數據集,該數據集一共有六個類別。具體見表3-1.
| 類別名 | 含義 | 示例圖像 |
| ? ? Wetland ? | ? ? 農田 | |
| ? ? Glacier | ? ? 冰川 | |
| ? ? Urban | ? ? 城市區域 | |
| ? ? Rock | ? ? 巖石 | |
| ? ? water | ? ? 水域 | |
| ? ? Wood | ? ? 森林 |
表3-1 衛星數據的類別及圖像實例
這些原始的圖像數據均保存在pic文件夾中。現在我們需要將這些原始數據分為train和validation兩個目錄,分別表示訓練使用的圖片和驗證使用的圖片。在每個目錄中,分別以類別名為文件夾名保存所有圖像。在每個類別文件夾下,存放的就是原始的圖像(如jpg格式的圖像文件)。之后使用預先編制好的腳本data_convert.py,將圖片轉換為為tfrecord格式,在命令行內輸入如下代碼。
| python data_convert.py -t pic/ \ ???????--train-shards 2 \ ???????--validation-shards 2 \ ???????--num-threads 2 \ ???????--dataset-name satellite |
-t?pic/:?表示轉換pic文件夾中的數據。pic文件夾中必須有一個train目錄和一個validation目錄,分別代表訓練和驗證數據集。每個目錄下按類別存放了圖像數據。
--train-shards 2 \中的2是指將訓練數據集分為兩塊,即最后的訓練數據就是兩個tfecord格式的文件。如果讀者的數據集較大,可以考慮將其分為更多的數據塊。?????
-num-threads?2中的2是指采用兩個線程產生數據。為了使得每個線程處理的數據塊數是相同的,線程數必須要能整除train-shards和validation-shards。
--dataset-name satellite給生成的數據集起一個名字。這里將數據集起名satellite,最后生成文件的開頭就是satellite_train和satellite_validation。
圖3-2 下載后產生的新文件
運行完命令后會在pic文件夾內生成5 個新的文件(如圖3-2所示),其包括訓練數據satellite_train_00000-of-00002.tfrecord,satellite_train_00001-of-00002.tfrecord,以及驗證數據satellite_validation_00000-of-00002.tfrecord ,satellite_validation _00001-of-00002 .tfrecord 。另外,還有一個文本文件label.txt ,表示圖片的內部標簽(數字)到真實類別(字符串)之間的映射順序。如圖片在tfrecord 中的標簽為0 ,那么就對應label.txt 第一行的類別,在tfrecord的標簽為1 ,就對應label.txt 中第二行的類別,依此類推。
注意:由于python版本的不同,在python3.0及以上的版本上運行以上的命令可能會報錯(如下圖3-3)。
圖3-3 版本不同而報錯的情況
解決方法是在tfrecord.py文件中做如下幾處做更改,更改后即可正常運行:
?
| //第一 //第二 image_data = f.read() //第三 //第四 |
2.使用TensorFlow Slim微調模型
TensorFlow Slim 是Google 公司公布的一個圖像分類工具包,它不僅定義了一些方便的接口,還提供了很多ImageNet數據集上常用的網絡結構和預訓練模型。首先我們應該明白如何下載Slim 的源代碼,再了解如何在Slim 中定義新的數據庫以及如何使用新的數據庫訓練、如何進行參數調整等問題。
2.1 下載TensorFlow Slim的源代碼
如果需要使用Slim 微調模型,首先要下載Slim的源代碼。Slim的源代碼保存在tensorflow/models 項目中,可以使用下面的git命令下載ensorflow/models:
| git clone?https://github.com/tensorflow/models.git |
找到models/research/ 目錄中的slim文件夾,這就是要用到的TensorFlow Slim 的源代碼。TensorFlow Slim的代碼結構見表3-2。
| 文件夾或文件名 | 用途 |
| datasets | 定義一些訓練中使用的數據集,預先設定的有mnist等數據集,如果有像訓練自己的數據集需要在datasets中進行定義 |
| nets/ | 定義了一些常見的網絡結構,如AlexNet等 |
| processing/ | 定義了一些預處理方法 |
| scripts | 包含一些實例腳本 |
| train_image_classifier.py | 訓練模型的入口代碼 |
| eval_image_classifier.py | 驗證模型性能的入口代碼 |
| download_and_convert_data.py | 下載站還數據集格式的入口代碼 |
表3-2 TensorFlow Slim的代碼結構
2.2定義新的datasets文件
首先,在datasets/目錄下新建一個文件satellite.py,并將flowers.py 文件中的內容復制到satellite.py 中。接下來,需要修改以下幾處內容:第一處是FILE_PATTERN 、SPLITS_TO SIZES 、NUM_CLASSES , 將其進行以下修改:
| FILE_PATTERN = 'satellite_%s_*.tfrecord' SPLITS_TO_SIZES = {'train':4800, 'validation':1200} _NUM_CLASSES = 6 |
FILE_PATTERN變量定義了數據的文件名的格式和訓練集、驗證集的數量。這里定義_FILE_PATTERN = ‘satellite%s_*.tfrecord’和SPLITS_TO_SIZES={‘train’:4800, ‘validation’:1200},表明數據集中,訓練集的文件格式為satellite_train_*.tfrecord共包含4800張圖片,驗證集文件名格式為satellite_validation_*.tfrecord,共包含1200張圖片。_NUM_CLASSES變量定義了數據集中圖片的類別數目。
第二處修改image/format部分,將之修改為:
| 'image/format' tf.FixedLenFeature( (), tf. string, default_value ='jpg'), |
此處定義了圖片的默認格式。收集的衛星圖片的格式為jpg圖片,因此修改為jpg 。修改完satellite.py后,還需要在同目錄的dataset_factory.py文件中注冊satellite數據庫。未修改的dataset_factory. py 中注冊數據庫的對應代碼為:
| from datasets import cifar10 from datasets import flowers from datasets import imagenet from datasets import mnist from datasets import satellite datasets_map = { 'cifar10': cifar10, 'flowers': flowers, 'imagenet': imagenet, 'mnist': mnist, 'satellite': satellite, } |
2.3 準備訓練文件夾
定義完數據集后,在slim文件夾下再新建一個satellite目錄,在這個目錄中,完成最后的幾項準備工作:
新建一個data目錄,并將轉換好格式的訓練數據復制進去。
新建一個空的train_dir 目錄,用來保存訓練過程中的日志和模型。
新建一個pretrained目錄,在slim的GitHub頁面找到Inception V3 模型的下載地址,下載并解壓后,會得到一個inception_v3 .ckpt 文件,將該文件復制到pretrained 目錄下。
2.4訓練模型
在slim 文件夾下,運行以下命令就可以開始訓練了:
| python train_image_classifier.py \ ???????--train_dir=satellite/train_dir \ ???????--dataset_name=satellite \ ???????--dataset_dir=satellite/data \ ???????--model_name=incepttion_v3 \ ???????--checkpoint_path=satellite/pretrained/inception_v3.ckpt \ ???????--checkpoint_exclude_scopes=InceptionV3/Logits,InceptionV3/AuxLogits \ ???????--trainable_scopes=InceptionV3/Logits,InceptionV3/AuxLogits \ ???????--max_number_of_strps=100000 \ ???????--batch_size=32 \ ???????--learning_rate=0.001 \ ???????--learning_rate_decay_type=fixed \ ???????--save_interval_secs=300 \ ???????--save_summaries_secs=2 \ ???????--log_every_n_steps=10 \ ???????--optimizer=rmpsprop \ ???????--weight_decay=0.00004 |
????上述命令的參數較多,下面一一做出解釋:
--trainable_scopes=InceptionV3/Logits,InceptionV3/AuxLogits:首先來解釋trainable_scope的作用,trainable_scopes規定了在模型中微調變量的范圍。這里的設定表示只對InceptionV3/Logits,InceptionV3/AuxLogits 兩個變量進行微調,其它的變量都不動。InceptionV3/Logits,InceptionV3/AuxLogits就相當于在第一節中所講的fc8,他們是Inception V3的“末端層”。如果不設定trainable_scopes,就會對模型中所有的參數進行訓練。
--train_dir=satellite/train_dir:表明會在satellite/train_dir目錄下保存日志和checkpoint。
--dataset_name=satellite、–dataset_split_name=train:指定訓練的數據集。在3.2節中定義的新的dataset就是在這里發揮用處的。
--dataset_dir=satellite/data: 指定訓練數據集保存的位置。
--model_ name=inception_v3 :使用的模型名稱。
--checkpoint_path=satellite/pretrained/inception_v3.ckpt:預訓練模型的保存位置。
--checkpoint_exclude_scopes=InceptionV3/Logits,InceptionV3/AuxLogits : 在恢復預訓練模型時,不恢復這兩層。正如之前所說,這兩層是InceptionV3模型的末端層,對應著ImageNet 數據集的1000 類,和當前的數據集不符,因此不去恢復它。
--max_number_of_steps 100000 :最大的執行步數。
--batch_size =32 :每步使用的batch 數量。
--learning rate=0.001 : 學習率。
--learning_rate_decay_type=fixed:學習率是否自動下降,此處使用固定的學習率。
--save_interval_secs=300 :每隔300s 程序會把當前模型保存到train_dir中。此處就是目錄satellite/train_dir 。
–save_summaries_secs=2 :每隔2s就會將日志寫入到train_dir中。可以用TensorBoard查看日志。此處為了方便觀察,設定的時間間隔較多,實際訓練時為了性能考慮,可以設定較長的時間間隔。
--log_every_n_steps=10: 每隔10 步,就會在屏幕上打出訓練信息。
--optimizer=rmsprop: 表示選定的優化器。
--weight_decay=0.00004 :選定的weight_decay值。即模型中所高參數的二次正則化超參數。
以上命令是只訓練末端層InceptionV3/Logits, InceptionV3 /AuxLogits, 還可以使用以下命令對所有層進行訓練:
| python train_image_classifier.py \ ???????--train_dir=satellite/train_dir \ ???????--dataset_name=satellite \ ???????--dataset_dir=satellite/data \ ???????--model_name=incepttion_v3 \ ???????--checkpoint_path=satellite/pretrained/inception_v3.ckpt \ ???????--checkpoint_exclude_scopes=InceptionV3/Logits,InceptionV3/AuxLogits \ ???????--max_number_of_strps=100000 \ ???????--batch_size=32 \ ???????--learning_rate=0.001 \ ???????--learning_rate_decay_type=fixed \ ???????--save_interval_secs=300 \ ???????--save_summaries_secs=2 \ ???????--log_every_n_steps=10 \ ???????--optimizer=rmpsprop \ ???????--weight_decay=0.00004 |
對比只訓練、末端層的命令,只再一處發生了變化,即去掉了--trainable_ scopes 參數。原先的–trainable_ scopes= Inception V3 /Logits ,InceptionV3 / AuxLogits 表示只對末端層Inception V3 /Logits 和Inception V3 / AuxLogits 進行訓練,去掉后就可以訓練模型中的所有參數了。
程序具體運行過程如下。啟動train_ image_ classifier. py 程序,如果訓練文件夾(即satellite/train_ dir )里沒有已經保存的模型,就會加載checkpoint_path中的預訓練模型,緊接著,程序會把初始模型保存到train_dir中,命名為model.cpkt-0,0表示第0步。這之后,每隔5min(參數一save interval secs=300 指定了每隔300s 保存一次,即5min)。程序還會把當前模型保存到同樣的文件夾中,命名格式和第一次保存的格式一樣。因為模型比較大,程序只會保留最新的5 個模型。
此外,**如果中斷了程序井再次運行,程序會首先檢查train dir 中有無已經保存的模型,如果有,就不會去加載checkpoint_path中的預訓練模型, 而是直接加載train dir 中已經訓練好的模型,并以此為起點進行訓練。**Slim之所以這樣設計,是為了在微調網絡的時候,可以方便地按階段手動調整學習率等參數。
2.5 驗證模型準確率
我們用eval_image classifier.py 程序進行驗證模型的準確率。執行下列命令:
| Python eval_image classifier.py \ ??????--checkpoint_path=satellite/train _ dir \ --eval_dir=satellite/eval_dir --dataset_name=satellite --dataset_split_name=validation --dataset_dir=satellite/data --model_ name=inception_ v3 |
命令中所涉及到的參數解釋如下:
--checkpoint_path=satellite/train _ dir: 這個參數既可以接收一個目錄的路徑,也可以接收一個文件的路徑。如果接收的是一個目錄的路徑,如這里的satellite/train_dir就會在這個目錄中尋找最新保存的模型文件,執行驗證。也可以指定一個模型驗證,以第300步為例,在satellite/train_ dir 文件夾下它被保存為model.clcpt-300.meta, model.ckpt-300.index ,model. ckpt-3 00.data-00000-of-00001 三個文件。此時,如果要對它執行驗證,給checkpoint_path 傳遞的參數應該為satellite/train_ dir/model.ckpt-300 。
--eval_dir=satellite/eval_dir :執行結果的日志就保存在eval_dir 中,可以通過TensorBoard查看。
--dataset_name=satellite,--dataset_split_name=validation 指定需要執行的數據集。注意此處是使用驗證集( validation )執行驗證。
--dataset_dir=satellite/data :數據集保存的位置。
--model_ name=inception_ v3 :使用的模型名稱。
運行完命令后出現出現如下運行結果:
Accuracy表示模型的分類準確率,而Recall_5 表示Top 5 的準確率,即在輸出的各類別概率中,正確的類別只要落在前5 個就算對。由于此處的類別數比較少,因此可以不執行Top 5 的準確率,民而執行Top 2 或者Top 3的準確率,只要在eval_image_classifier.py 中修改下面的部分就可以了:
| names_to_values, names_to_updates = slim.metrics.aggregate_metric_map({ ????'Accuracy': slim.metrics.streaming_accuracy(predictions, labels), ????'Recall_5': slim.metrics.streaming_recall_at_k(logits, labels, 5),}) |
?
一般我們將模型訓練完后會部署訓練好的模型并對單張圖片做識別。TensorFlow Slim提供了導出網絡結構的腳本export_inference_ graph.py 。首先在slim 文件夾下運行:
| python export_inference_ graph.py \ ? --alsologtostderr \ --model_name=inception_v3 ?\ --output_file=satallite/inception_v3_inf_graph.pb \ --dataset_name satellite |
這個命令會在satellite 文件夾中生成一個inception_v3 _inf _graph. pb 文件。注意: inception_v3 _inf _graph.pb 文件中只保存了Inception V3 的網絡結構,并不包含訓練得到的模型參數,需要將checkpoint 中的模型參數保存進來。方法是使用freeze_graph. py 腳本(在chapter_3 文件夾下運行),命令如下所示:
| python freeze-graph.py \ --input_graph slim/satellite/inception_v3_inf_graph.pb \ --input_checkpoint slim/satallite/train_dir/model.ckpt-5271 \ --input_binary true \ --output_node_names InceptionV3/Predictions/Reshape_1 \ --output_graph slim/satellite/frozen_graph.pb |
這里參數含義為:
–input_graph slim/satellite/inception_v3_inf_graph.pb。這個參數很好理解,它表示使用的網絡結構文件,即之前已經導出的inception_v3 _inf_gr aph.pb 。
–input_checkpoint slim/satallite/train_dir/model.ckpt-5271。具體將哪一個checkpoint 的參數載入到網絡結構中。這里使用的是訓練文件夾train _d讓中的第5271 步模型文件。我們需要根據訓練文件夾下checkpoint的實際步數,將5271修改成對應的數值。
input_binary true。導入的inception_v3_inf_graph.pb實際是一個protobuf文件。而protobuf 文件有兩種保存格式,一種是文本形式,一種是二進制形式。inception_v3 _ inf graph. pb 是二進制形式,所以對應的參數是–input binary true 。初學的話對此可以不用深究,若高興趣的話可以參考資料。
–output_graph slim/satellite/frozen_graph.pb。最后導出的模型保存為slim/satellite?/frozen_graph.pb 文件。
如何使用導出的frozen_graph.pb 來對單張圖片進行預測?編寫了一個classify image_inception_ v3.py 腳本來完成這件事。首先來看這個腳本的實現方式。代碼中包含一個preprocess for_ eval函數,它實際上是從slim/preprocessing/inception_preprocess ing.py里復制而來的,用途是對輸入的圖片做預處理。classify_ image_inception_v3.py 的主要邏輯在run_inference_on_ image函數中,第一步就是讀取圖片,并用preprocess_for_eval做預處理:
| with tf.Graph().as_default(): ????image_data = tf.gfile.FastGFile(image, 'rb').read() ????image_data = tf.image.decode_jpeg(image_data) ????image_data = preprocess_for_eval(image_data, 299, 299) ????image_data = tf.expand_dims(image_data, 0) ????with tf.Session() as sess: ??????image_data = sess.run(image_data) |
Inception V3 的默認輸入為299 * 299 ,所以調用preprocess_for_eval 時指定了寬和高都是299 。接著調用create_graph()將模型載入到默認的計算圖中。
| def create_graph(): ??"""Creates a graph from saved GraphDef file and returns a saver.""" ??with tf.gfile.FastGFile(FLAGS.model_path, 'rb') as f: ????graph_def = tf.GraphDef() ????graph_def.ParseFromString(f.read()) ????_ = tf.import_graph_def(graph_def, name='') |
FLAGS.model_path 就是保存的slim/satellite/frozen_graph.pb 。將之導入后先轉換為graph_def,然后用tf.import_graph_def()函數導入。導入后,就可以創建Session 并測試圖片了,對應的代碼為:
| with tf.Session() as sess: ??softmax_tensor = sess.graph.get_tensor_by_name('InceptionV3/Logits/SpatialSqueeze:0') ??predictions = sess.run(softmax_tensor,{'input:0': image_data}) ??predictions = np.squeeze(predictions) ??node_lookup = NodeLookup(FLAGS.label_path) ??top_k = predictions.argsort()[-FLAGS.num_top_predictions:][::-1] ??for node_id in top_k: human_string = node_lookup.id_to_string(node_id) ??score = predictions[node_id] ????print('%s (score = %.5f)' % (human_string, score)) |
InceptionV3/Logits/SpatialSqueeze:0是各個類別Logit值對應的節點。輸入預處理后的圖片image_data,使用sess.run()函數去除各個類別預測Logit。默認只取最有可能的FLAGS.num_top_predictions個類別輸出,這個值默認是5。可以運行腳本時用–num_top_predictions參數來改變此默認值。node_ lookup 定義了一個NodeLookup 類,它會讀取label文件,并將模型輸出的類別id轉換成實際類別名。
之后我們來看這個腳本應該如何使用:
| python classify_image_inception_v3.py \ --model_path slim/satellite/frozen_graph.pb \ --label_path data_prepare/pic/label.txt \ --image_file test_image.jpg |
--model_path就是之前導出的模型frozen_graph. pb 。模型的輸出實際是“第0 類’、“第1 類”……所以用--label_path 指定了一個label文件,label文件中按順序存儲了各個類別的名稱,這樣腳本就可以把類別的id號轉換為實際的類別名。–image _file 是需要測試的單張圖片。腳本的運行結果類似于:
這就表示模型預測圖片對應的最可能的類別是water,接著是wetland 、urban 、wood 等。score 是各個類別對應的Logit 。
?
?
?本次實驗通過使用TensorFlow Slim 微調預訓練模型,包括數據準備、定義新的datasets文件、訓練、驗證、導出模型井測試單張圖片等,讓學生掌握微調神經網絡的基本原理以及了解圖像識別模型在訓練時具體的運行過程。通過本章的學習讓學生可以修改對應的代碼來訓練自己的數據,來打造自己的圖像識別模型。這有助于激發學生的獨立思考能力和動手實踐能力。
?
?
?
?
?
?
?
?
總結
以上是生活随笔為你收集整理的打造自己的图像识别模型的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 破圈、增长、被加码,集合店能创造美妆行业
- 下一篇: R绘图-KEGG功能注释组间差异分面条形