Tensorflow(1)IT男識別玫瑰

移動安仔發表於2019-02-28

這是一個關於IT男的故事

Tensorflow(1)IT男識別玫瑰

Tensorflow(1)IT男識別玫瑰

Tensorflow(1)IT男識別玫瑰

Tensorflow(1)IT男識別玫瑰

IT男開始學習如何使用Inception程式碼庫識別玫瑰

背景知識

   在機器學習領域,卷積神經網路是大多數最先進的計算機視覺解決方案的核心,適用於各種任務。其中Tensorflow的Inception程式碼庫也是基於此網路解決了影象分類問題。Inception網路實現了2015年發表的論文《重新思考計算機視覺的初始架構》(Rethinking the Inception Architecture for Computer Vision)。
   在論文中探索了通過適當的分解卷積和積極的正則化,儘可能有效地利用增加的算力,而達到相比現有水平更好的實質性收益。 在論文中也描述了一些具有指導意義的網路設計原則,儘管這些基於大規模試驗的設計原則的實用性是推測的,將來需要更多的實驗資料來評估其準確性和有效性,但經驗證如嚴重偏離這些原則會往往導致網路質量的下降,並且按照原則執行修復偏差也會使整體架構得到改善。

原則一,網路設計早期不應過度壓縮輸入輸出資料。在設計網路的時候需要根據特徵圖的各個維度的資料粗略的提取其中的特徵,通常到達網路層的特徵圖資料的大小從輸入到輸出逐漸減小,但如果早期就過度壓縮資料,就會導致資訊的大量丟失。資訊內容不僅僅通過特徵圖的維度進行評估,也需要相關結構等重要因素。資訊丟失將會影響網路訓練的質量。
原則二,高緯度的特徵圖表示可以在本地用網路代替。在卷積神經網路中逐步增加非線性啟用響應可以獲得更多特徵,加快網路訓練的速度。
原則三,在卷積網路之前對多通道的低維特徵進行降維,不會降低特徵的資訊。 例如,在執行(例如3×3)卷積之前,可以在空間聚合之前減小輸入表示的維度但並不會產生嚴重的不利影響。基於假設其原因是相鄰單元之間的強相關性導致在降維期間資訊損失小得多。鑑於這些原理,應可以進行資料的壓縮,尺寸的減小應該可以促進更快的學習。
原則四,找到最佳的網路結構,平衡網路的寬度和深度,使網路的效能達到最優。平衡每級的濾波器數量和網路深度,可以提高網路的最佳效能。增加網路的寬度和深度可以提高網路質量。如果兩者並行增加,可以達到恆定計算量的最佳優化結果。 因此,計算預算應以平衡的方式分佈在網路的深度和寬度之間。

   雖然這些原則可能有意義,但直接應用它們來提高網路質量並非易事。Inception網路也只是在逐步的靠近這些設計原則,模模糊糊中使用它們。
   關於論文部分不再細述,請參考該論文:Rethinking the Inception Architecture for Computer Vision
   ICLR 2017年的一篇分析論文 An analysis of deep neural network models for practical applications 對實際應用中的重要技術進行了全面分析:精度,記憶體佔用,引數,操作計數,推理時間和功耗。其中在精度上,Inception網路獲得了最佳表現,當然在其他維度並沒有獲得所有的最佳表現力。精度評測結果如下:

Tensorflow(1)IT男識別玫瑰

Inception程式碼介紹

   Google在開源Tensorflow的同時開源了一個名為 Models的程式碼庫。該程式碼庫包含了[TensorFlow](https://www.tensorflow.org)中實現的許多不同模型,結構如下:
   - official:使用TensorFlow的高階API的示例模型的集合。旨在通過最新最穩定的TensorFlow API進行開發,經過測試並保持程式碼最新。官方推薦新的TensorFlow使用者從這裡開始學習。
   - research:是研究人員在TensorFlow中實施的大量模型集合。它們沒有得到官方支援,是由個體研究人員來維護模型,但也是最活躍的程式碼目錄。
   - samples:包含程式碼片段和較小的模型,用於演示TensorFlow的功能,包括各種部落格文章中提供的程式碼。
   - tutorials:是TensorFlow教程中描述的模型集合。
   其中,本文介紹的 Inception 程式碼庫就是research的一部分,檔案結構及說明如下:

├── README.md  #非常全面的inception程式碼庫的描述
├── WORKSPACE #bazel編譯使用
├── g3doc
│   └── inception_v3_architecture.png #inception_v3網路體系結構的說明圖示
└── inception
    ├── BUILD
    ├── data #訓練資料的標註和程式碼目錄
    │   ├── build_image_data.py #使用protos將圖片資料轉換為TFRecord檔案格式
    │   ├── build_imagenet_data.py #使用protos將ImageNet圖片資料集轉換為TFRecord檔案格式
    │   ├── download_and_preprocess_flowers.sh #下載花朵資料集並將資料集轉化為TFRecord檔案格式
    │   ├── download_and_preprocess_flowers_mac.sh 
    │   ├── download_and_preprocess_imagenet.sh #下載2012ImageNet訓練和評估資料集並將資料集轉化為TFRecord檔案格式
    │   ├── download_imagenet.sh # 下載2012ImageNet訓練和評估資料集
    │   ├── imagenet_2012_validation_synset_labels.txt #訓練資料的標籤與preprocess_imagenet_validation_data.py一起對資料進行處理
    │   ├── imagenet_lsvrc_2015_synsets.txt #訓練資料的標籤
    │   ├── imagenet_metadata.txt #訓練資料的標籤和對應的語義
    │   ├── preprocess_imagenet_validation_data.py #將2012 ImageNet圖片和標籤相關聯
    │   └── process_bounding_boxes.py #將圖片資料和標籤資料相關聯的指令碼
    ├── dataset.py # 輕量級的管理訓練資料集合的庫
    ├── flowers_data.py # 使用DataSet管理的花朵訓練資料
    ├── flowers_eval.py # 對花朵分類訓練模型的評估指令碼,對inception_eval.py的封裝
    ├── flowers_train.py # 對花朵分類的訓練指令碼,對inception_train.py的封裝
    ├── image_processing.py #處理單張圖片的庫,支援多個執行緒並行和預處理圖片。
    ├── imagenet_data.py # 使用DataSet管理的ImageNet訓練資料
    ├── imagenet_distributed_train.py #支援分散式系統進行ImageNet訓練的庫,對inception_distributed_train.py的封裝
    ├── imagenet_eval.py # 對ImageNet資料集訓練模型的評估指令碼
    ├── imagenet_train.py #對ImageNet資料集的訓練指令碼
    ├── inception_distributed_train.py # 支援分散式系統進行inception的網路的訓練
    ├── inception_eval.py # inception網路訓練模型的評估驗證庫
    ├── inception_model.py # 構建inception v3網路的模型在資料集上
    ├── inception_train.py # inception網路訓練指令碼庫
    └── slim  #Tensorflow中一個輕量級的包含設計,訓練和評估模型的程式碼庫,本文不做討論。
複製程式碼

本文僅對inception_train.py(此指令碼的後續更新已經轉移到slim程式碼庫中,參考注意事項)的核心程式碼進行分析,其他檔案請參考原始碼。

首先,對inception網路訓練指令碼使用的標誌引數進行說明。

標誌引數如下:
   train_dir: 用來寫入事件日誌和檢查點(checkpoint),即訓練產生的events.out.xxx和model.ckpt-xxxx的檔案,預設值'/tmp/imagenet_train’。
   max_steps: 訓練最大的步數,預設值10000000。
   subset: 描述是訓練還是驗證。預設值是'train' 。

   用於管理執行tensorflow使用的硬體
   num_gpus: 管理用於執行tensorflow的硬體,該引數規定使用的GPU數量,預設值為1。
   log_device_placement: 設定是否記錄裝置,預設值是False。

   用於管理訓練的型別
   fine_tune: 管理訓練型別,如果設定將隨機初始化最後一層權重,以便在新任務上訓練網路。預設值是False,即可以在預訓練模型上繼續訓練。
   pretrained_model_checkpoint_path: 在一個新的訓練任務開始時指定預訓練模型存放的路徑。

   下面是關於學習速率的調整引數,這些引數的調整在很大程度上依賴於硬體架構,批大小以及對模型體系結構規範的更改。選擇一個精細調整的學習率是需要一些實驗的經驗過程的。
   initial_learning_rate: 初始學習速率。預設值是0.1。
   num_epochs_per_decay: 學習速率衰減的調整週期。
   learning_rate_decay_factor: 學習速率調整的引數因子。

其次,針對指令碼中定義的tower_loss方法做一些分析。

函式: _tower_loss
   用於計算訓練模型單個塔上的總損失。在訓練時程式會將批量圖片任務進行拆分,一個批量的圖片拆分到不同的塔上。即如果batch_size = 32,num_gpus = 2,那麼每個塔就會處理16張影象。
1,使用inception構建網路推理圖。

with tf.variable_scope(tf.get_variable_scope(), reuse=reuse_variables):
    logits = inception.inference(images, num_classes, for_training=True,
                                 restore_logits=restore_logits,
                                 scope=scope)
複製程式碼

2,構建推理圖中計算損失的部分。

split_batch_size = images.get_shape().as_list()[0]
inception.loss(logits, labels, batch_size=split_batch_size)
複製程式碼

3,僅組裝當前塔中的所有損失。

losses = tf.get_collection(slim.losses.LOSSES_COLLECTION, scope)
複製程式碼

其中slim來自inception中slim目錄中的losses.py檔案。這個方法獲取網路的所有損失值。其中,LOSSES_COLLECTION = '_losses'

4,計算當前塔的所有損失。

regularization_losses = tf.get_collection(tf.GraphKeys.REGULARIZATION_LOSSES)
total_loss = tf.add_n(losses + regularization_losses, name='total_loss')
複製程式碼

在損失函式上加上正則項是防止過擬合的一個重要方法,通過獲取tf.GraphKeys.REGULARIZATION_LOSSES集合中的所有變數獲得正則化的損失值。

5,調整引數,初始衰減速率為0.9,計算所有損失的移動平均值和總損失。

loss_averages = tf.train.ExponentialMovingAverage(0.9, name='avg')
loss_averages_op = loss_averages.apply(losses + [total_loss])
複製程式碼

通過tf.train.ExponentialMovingAverage函式建立移動平均而更新引數,控制模型的更新速度。將所有的loss儲存在一個全域性變數(GraphKeys.MOVING_AVERAGE_VARIABLES)中,再通過apply對每一個變數進行每一次的迭代移動平均計算。

最後,針對Inception的訓練函式進行一些分析。

函式:train 在資料集上進行訓練。

#建立一個變數global_step來計算train呼叫的數量。這等於處理的批次數* FLAGS.num_gpus。

global_step = tf.get_variable(
        'global_step', [],
        initializer=tf.constant_initializer(0), trainable=False)
複製程式碼

#計算獲得學習速率的調整方案。

num_batches_per_epoch = (dataset.num_examples_per_epoch() / FLAGS.batch_size)
decay_steps = int(num_batches_per_epoch * FLAGS.num_epochs_per_decay)
複製程式碼

#根據步數以指數方式衰減學習速率。

lr = tf.train.exponential_decay(FLAGS.initial_learning_rate,
                                    global_step,
                                    decay_steps,
                                    FLAGS.learning_rate_decay_factor,
                                    staircase=True)
複製程式碼

#使用RMSProp演算法建立一個執行梯度下降的優化器。

opt = tf.train.RMSPropOptimizer(lr, RMSPROP_DECAY,
                                    momentum=RMSPROP_MOMENTUM,
                                    epsilon=RMSPROP_EPSILON)
複製程式碼

#獲取ImageNet的影象和標籤,並在GPU之間拆分批處理。

split_batch_size = int(FLAGS.batch_size / FLAGS.num_gpus)
複製程式碼

#覆蓋預處理執行緒的數量,以解決塔數量增加的問題。

num_preprocess_threads = FLAGS.num_preprocess_threads * FLAGS.num_gpus
images, labels = image_processing.distorted_inputs(
        dataset,
        num_preprocess_threads=num_preprocess_threads)
複製程式碼

#Dataset標籤集中的類數加1.標籤0保留給(未使用的)後臺類。

num_classes = dataset.num_classes() + 1
複製程式碼

#拆分塔的影象和標籤批次。

images_splits = tf.split(axis=0, num_or_size_splits=FLAGS.num_gpus, value=images)
labels_splits = tf.split(axis=0, num_or_size_splits=FLAGS.num_gpus, value=labels)
複製程式碼

#計算每個模型塔的梯度。
#強制所有變數駐留在CPU上,計算ImageNet模型的一個塔的損失,此函式構造整個ImageNet模型,但在所有塔中共享變數。

with slim.arg_scope([slim.variables.variable], device='/cpu:0'):
	loss = _tower_loss(images_splits[i], labels_splits[i], num_classes,
                           scope, reuse_variables)
複製程式碼

#重用下一個塔的變數。

reuse_variables = True
複製程式碼

#保留最終塔的摘要。

summaries = tf.get_collection(tf.GraphKeys.SUMMARIES, scope)
複製程式碼

#僅保留最終塔的批量標準化更新操作。理想情況下,我們應該從所有塔中獲取更新,但這些統計資料累積得非常快,因此我們可以忽略其他塔的統計資料,而不會造成重大損失。

batchnorm_updates = tf.get_collection(slim.ops.UPDATE_OPS_COLLECTION, scope)
複製程式碼

#計算此塔上ImageNet批量資料的梯度。

grads = opt.compute_gradients(loss)
複製程式碼

#跟蹤所有塔的梯度。

tower_grads.append(grads)
複製程式碼

#計算每個梯度的平均值。請注意,這是所有塔開始同步的地方。

grads = _average_gradients(tower_grads)
複製程式碼

#新增摘要以跟蹤學習率。

summaries.append(tf.summary.scalar('learning_rate', lr))
複製程式碼

#新增漸變的直方圖。

for grad, var in grads:
      if grad is not None:
        summaries.append(
            tf.summary.histogram(var.op.name + '/gradients', grad))
複製程式碼

#應用漸變來調整共享變數。

apply_gradient_op = opt.apply_gradients(grads, global_step=global_step)
複製程式碼

#為可訓練變數新增直方圖。

for var in tf.trainable_variables():
      summaries.append(tf.summary.histogram(var.op.name, var))
複製程式碼

#跟蹤所有可訓練變數的移動平均值。維護BatchNormalization全域性統計資訊的“雙倍平均值”。這比需要的更復雜,但是使用它與之前的模型向後相容。

variable_averages = tf.train.ExponentialMovingAverage(
        inception.MOVING_AVERAGE_DECAY, global_step)
variables_to_average = (tf.trainable_variables() +  tf.moving_average_variables())
variables_averages_op = variable_averages.apply(variables_to_average)
複製程式碼

#將所有更新分組到一個訓練中。

batchnorm_updates_op = tf.group(*batchnorm_updates)
train_op = tf.group(apply_gradient_op, variables_averages_op, batchnorm_updates_op)
複製程式碼

#建立一個儲存器。

saver = tf.train.Saver(tf.global_variables())
複製程式碼

#從最後的塔摘要中構建摘要操作。

summary_op = tf.summary.merge(summaries)
複製程式碼

#構建一個初始化操作以在下面執行。

init = tf.global_variables_initializer()
複製程式碼

#開始在Graph上執行操作。必須將allow_soft_placement設定為True才能在GPU上構建塔,因為某些作業系統沒有GPU實現。

sess = tf.Session(config=tf.ConfigProto(
        allow_soft_placement=True,
        log_device_placement=FLAGS.log_device_placement))
    sess.run(init)

    if FLAGS.pretrained_model_checkpoint_path:
      …
      variables_to_restore = tf.get_collection(slim.variables.VARIABLES_TO_RESTORE)
      restorer = tf.train.Saver(variables_to_restore)
      restorer.restore(sess, FLAGS.pretrained_model_checkpoint_path)
      …
複製程式碼

#啟動佇列執行。

tf.train.start_queue_runners(sess=sess)

    # 建立寫入器
    summary_writer = tf.summary.FileWriter(FLAGS.train_dir, graph=sess.graph)
    for step in range(FLAGS.max_steps):
      …
      # 執行本步的訓練操作
      start_time = time.time()
      _, loss_value = sess.run([train_op, loss])

     …

      # 每100步更新一次摘要資訊
      if step % 100 == 0:
        summary_str = sess.run(summary_op)
        summary_writer.add_summary(summary_str, step)

      # 週期的儲存checkpoint檔案,這裡是5000步執行一次
      if step % 5000 == 0 or (step + 1) == FLAGS.max_steps:
        checkpoint_path = os.path.join(FLAGS.train_dir, 'model.ckpt')
        saver.save(sess, checkpoint_path, global_step=step)
複製程式碼

理解train方法後即可對網路進行微整。

實戰驗證

實戰參考Inception的 README 文件,本文只介紹其中的4個部分: ###如何在新任務上對預訓練模型進行微調

一,入門

   在呼叫訓練方法前,必須對圖片和標籤資料進行預處理,即將一個新的資料集轉換為分片的TFRecord格式,每個條目都是一個序列化的tf.Example proto結構。程式碼庫提供了一個指令碼,演示如何為5個標籤上分佈的幾千幅花卉影象的小資料集做到這一點。

daisy, dandelion, roses, sunflowers, tulips

   程式碼庫提供自動指令碼(download_and_preprocess_flowers.sh)下載資料集並將其轉換為TFRecord格式。與ImageNet資料集非常相似,TFRecord格式中的每條記錄都是序列化的tf.Example proto結構,其條目包括JPEG編碼的字串和整數標籤。有關詳細資訊,請參閱parse_example_proto。
   該指令碼只需幾分鐘即可執行,具體取決於您的網路連線速度以下載和處理影象。您的硬碟需要200MB的免費儲存空間(這比完整的500GB+的2012 ImageNet資料集少太多了)。在這裡我們選擇DATA_DIR = / tmp / flowers-data /作為這樣一個位置,但隨意編輯。

# 設定flower資料儲存位置 
FLOWERS_DATA_DIR=/tmp/flowers-data/

# 構建預處理指令碼
cd tensorflow-models/inception
bazel  
build //inception:download_and_preprocess_flowers

# 執行指令碼下載並處理資料 
bazel-bin/inception/download_and_preprocess_flowers "${FLOWERS_DATA_DIR}"
複製程式碼

如果指令碼成功執行,終端輸出的最後一行應如下所示:

2016-02-24 20:42:25.067551: Finished writing all 3170 images in data set.
複製程式碼

   當指令碼結束時,將在DATA_DIR中找到2個用於訓練和驗證檔案的分片。這些檔案類似train-?????-of-00002和validation-?????-of-00002,分別代表訓練資料集和驗證資料集。
注意:
   如果準備自定義影象資料集,則需要在自定義資料集上呼叫build_image_data.py。

另外,可以選擇下載程式碼庫提供的預訓練模型

# 指定下載模型要儲存的路徑
INCEPTION_MODEL_DIR=$HOME/inception-v3-model
  
mkdir -p ${INCEPTION_MODEL_DIR}

cd ${INCEPTION_MODEL_DIR}

# 通過curl命令下載模型壓縮包
curl -O http://download.tensorflow.org/models/image/imagenet/inception-v3-2016-03-01.tar.gz
tar xzf inception-v3-2016-03-01.tar.gz

# 將會在目錄中建立一個名為inception-v3的目錄,幷包含下面的檔案
> ls inception-v3

README.txt

checkpoint

model.ckpt-157585
複製程式碼

現在可以使用花朵資料集對預先訓練好的Inception v3模型進行微調。

二,如何在花卉資料上重新訓練模型

準備在花朵資料集上微調預先訓練好的Inception-v3模型,需要修改訓練指令碼的2個引數:
   - pretrained_model_checkpoint_path: 指向預先訓練的Inception-v3模型的路徑。如果指定了此標誌,它將在指令碼開始訓練之前從檢查點載入整個模型。
   - fine_tune: 一個布林值,指示最後一個分類層是應該隨機初始化還是恢復。如果您希望從檢查點繼續訓練預先訓練好的模型,則可以將此標誌設定為false。如果將此標誌設定為true,則可以從頭開始培訓新的分類層。

綜合起來,可以使用以下命令重新訓練花卉資料集上的預先訓練的Inception-v3模型。

# 通過bazel構建flowers_train網路,該網路在原始碼上封裝了對inception_train的呼叫。
cd tensorflow-models/inception

bazel build //inception:flowers_train

# 設定下載好的預訓練的模型路徑.

MODEL_PATH="${INCEPTION_MODEL_DIR}/inception-v3/model.ckpt-157585"

# 設定資料儲存的路徑,該資料已經是TFRecord檔案格式
FLOWERS_DATA_DIR=/tmp/flowers-data/

# 設定儲存訓練中輸出的事件日誌和檢查點資料
TRAIN_DIR=/tmp/flowers_train/

# 通過flowers_train網路開始使用預訓練的模型重新訓練花朵資料,fine_tune設定為True。
bazel-bin/inception/flowers_train --train_dir="${TRAIN_DIR}" 
--data_dir="${FLOWERS_DATA_DIR}" 
--pretrained_model_checkpoint_path="${MODEL_PATH}" 
--fine_tune=True 
--initial_learning_rate=0.001 
--input_queue_memory_factor=1
複製程式碼

在培訓過程中增加了一些額外選項。

  • 將模型微調為單獨的資料集要求顯著降低初始學習速率。我們將初始學習率設定為0.001。
  • 鮮花資料集非常小,所以我們縮小了示例的佇列的大小。有關更多詳細資訊,請參閱調整記憶體需求。

訓練指令碼只會報告損失。要評估微調模型的質量,您需要執行驗證指令碼flowers_eval:

# 開始構建flowers_eval模型
cd tensorflow-models/inception

bazel build //inception:flowers_eval

# 設定訓練中輸出的事件日誌和檢查點的目錄
TRAIN_DIR=/tmp/flowers_train/

# 設定訓練資料集所在目錄,該資料集是TFRecord檔案格式
FLOWERS_DATA_DIR=/tmp/flowers-data/

# 設定儲存驗證結果日誌檔案的目錄
EVAL_DIR=/tmp/flowers_eval/

# 通過flowers_eval開始驗證flowers_train訓練的結果
bazel-bin/inception/flowers_eval 
--eval_dir="${EVAL_DIR}" 
--data_dir="${FLOWERS_DATA_DIR}" 
--subset=validation 
--num_examples=500 
--checkpoint_dir="${TRAIN_DIR}" 
--input_queue_memory_factor=1 
--run_once
複製程式碼

訓練中發現在模型執行2000步後,評估的精確度達到大約93.4%。

Successfully loaded model from /tmp/flowers/model.ckpt-1999 at step=1999.
2016-03-01 16:52:51.761219: starting evaluation on (validation).
2016-03-01 16:53:05.450419: [20 batches out of 20] (36.5 examples/sec; 0.684sec/batch)
2016-03-01 16:53:05.450471: precision @ 1 = 0.9340 recall @ 5 = 0.9960 [500 examples]
複製程式碼

三,如何構建一個新的資料集進行再訓練

   使用該模型提供的現有指令碼來構建用於訓練或微調的新資料集。主要指令碼是build_image_data.py。 簡而言之,這個指令碼需要一個結構化的影象目錄,並將其轉換為可由Inception模型讀取的分片TFRecord。 具體來說,您需要建立一個指定結構的訓練影象目錄,這些影象位於TRAIN_DIR和 VALIDATION_DIR中,其排列方式如下:

$TRAIN_DIR/dog/image0.jpeg

$TRAIN_DIR/dog/image1.jpg

$TRAIN_DIR/dog/image2.png

...

$TRAIN_DIR/cat/weird-image.jpeg

$TRAIN_DIR/cat/my-image.jpeg

$TRAIN_DIR/cat/my-image.JPG

...

$VALIDATION_DIR/dog/imageA.jpeg

$VALIDATION_DIR/dog/imageB.jpg

$VALIDATION_DIR/dog/imageC.png

...

$VALIDATION_DIR/cat/weird-image.PNG

$VALIDATION_DIR/cat/that-image.jpg

$VALIDATION_DIR/cat/cat.JPG
...
複製程式碼

注意:該指令碼將附加一個索引為0的額外背景類,因此您的類標籤範圍從0到num_labels。使用上面的例子,從build_image_data.py生成的相應類標籤如下所示:

0
  
1 dog
 
2 cat
複製程式碼

TRAIN_DIR和VALIDATION_DIR中的每個子目錄對應於駐留在該子目錄內的影象的唯一標籤,影象可能是JPEG或PNG影象,目前不支援其他影象型別。 資料安排在這個目錄結構中,可以在資料上執行build_image_data.py來生成分片的TFRecord資料集。 build_image_data.py的註釋中描述了tf.Example中包含的完整資訊列表。 要執行build_image_data.py,可以執行以下命令列:

# 設定生成TFRecord檔案的目錄
OUTPUT_DIRECTORY=$HOME/my-custom-data/

# 構建指令碼build_image_data
cd tensorflow-models/inception
bazel 
build //inception:build_image_data

# 執行指令碼進行轉化
bazel-bin/inception/build_image_data 
--train_directory="${TRAIN_DIR}" 
--validation_directory="${VALIDATION_DIR}" 
--output_directory="${OUTPUT_DIRECTORY}" 
--labels_file="${LABELS_FILE}" 
--train_shards=128 
--validation_shards=24 
--num_threads=8
複製程式碼

OUTPUT_DIRECTORY是分片TFRecords的位置。
LABELS_FILE將是一個由指令碼讀取的文字檔案,該檔案提供了所有標籤的列表。例如,在鮮花資料集的情況下, LABELS_FILE包含以下資料:
daisy

dandelion

roses

sunflowers

tulips
請注意,每個標籤的每一行都與模型中最終分類器中的條目相對應。也就是說,雛菊對應於條目1的分類器;蒲公英是條目2等。我們跳過標籤0作為背景類。 執行此指令碼後,將生成如下所示的檔案:

$TRAIN_DIR/train-00000-of-00128

$TRAIN_DIR/train-00001-of-00128

...

$TRAIN_DIR/train-00127-of-00128
and
$VALIDATION_DIR/validation-00000-of-00024

$VALIDATION_DIR/validation-00001-of-00024

...

$VALIDATION_DIR/validation-00023-of-00024
複製程式碼

其中128和24分別是為每個資料集指定的分片數。目標是選擇碎片的數量,使每個碎片大約有1024個影象。一旦建立了這個資料集,就可以在這個資料集上訓練或微調一個Inception模型。 另外,如果您使用訓練指令碼,請修改flowers_data.py中的num_classes()和num_examples_per_epoch()以與新建的資料對應。

四,訓練模型的實際考慮因素

   模型架構和訓練過程嚴重依賴於用來訓練模型的硬體。如果你想在你的機器上訓練或微調這個模型,你需要調整和經驗地確定一套適合你的設定的訓練超引數。

1)尋找好的超引數

大約5-10個超引數控制網路訓練的速度。除了--batch_size和–num_gpus之外,在inception_train.py中定義了幾個常量,它們規定了學習任務。

RMSPROP_DECAY = 0.9 # RMSProp演算法的衰減項.

MOMENTUM = 0.9 # RMSProp演算法的Momentum值.

RMSPROP_EPSILON = 1.0 # RMSProp演算法的Epsilon項.

INITIAL_LEARNING_RATE = 0.1 # 學習速錄初始化時的值.

NUM_EPOCHS_PER_DECAY = 30.0 # 學習速率開始衰減的時期Epochs.

LEARNING_RATE_DECAY_FACTOR = 0.16 # 學習速率衰減因子.
複製程式碼

在訓練中下面引數的調整對結果有較大的影響。

  • INITIAL_LEARNING_RATE:較高的學習率可以加快訓練速度。但速率太高會導致不穩定,使模型引數發散到無窮大或NaN。
  • batch_size:較大的批量大小可以提高梯度的質量估算,並可對較高學習率的模型進行訓練。
  • num_gpus:通常GPU記憶體是阻止採用更大批量的瓶頸,使用更多的GPU允許使用更大的批量。
2)調整記憶體需求

   訓練這個模型在CPU和GPU方面有很大的記憶體需求,與CPU記憶體相比,GPU記憶體相對較小。兩個專案決定了GPU記憶體使用量——模型架構和批量大小(batch_size)。假設您保持模型架構不變,訓練對GPU需求的唯一控制引數就是批量大小。

“一個很好的經驗法則是嘗試使用盡可能大的批量大小以適應GPU。”

   如果用完GPU記憶體,請降低--batch_size或在工作機上使用更多GPU。該模型在GPU之間執行批量分割,因此N個GPU可以處理N倍於1個GPU的批量。 該模型也需要大量的CPU記憶體。Inception調整了這個模型來使用大約20GB的CPU記憶體。因此,獲得大約40GB的CPU記憶體將是理想的。
   如果這不可行,可以通過降低——input_queue_memory_factor來調低模型的記憶體需求。相對於跨–num_preprocess_threads執行緒的主要訓練對影象進行非同步預處理。
   預處理的影象儲存在佇列中,其中每個GPU執行出隊操作以接收批量影象。 為了保證資料的良好序列,我們維護一個1024 x input_queue_memory_factor影象的大型佇列。對於當前的模型架構,這相當於大約4GB的CPU記憶體。 可以通過降低input_queue_memory_factor以減少記憶體佔用量。但大幅降低此值可能會導致模型在從頭開始訓練時預測精度略低。
   有關更多詳細資訊,請參閱image_processing.py中的註釋。

通過上面的學習和實戰驗證,IT男使用訓練好的模型終於可以大概率的識別出什麼事玫瑰。

Tensorflow(1)IT男識別玫瑰

引用及注意事項

引用:
1.Inception
2.Rethinking the Inception Architecture for Computer Vision
3.An analysis of deep neural network models for practical applications
4.ImageNet 是機器學習中用於訓練影象識別系統的常用學術資料集。
5.Inception的模型架構圖如下:

Tensorflow(1)IT男識別玫瑰
6.RMSProp演算法是一種梯度下降法,全稱是Root Mean Square Prop。 7.tower: 這個概念在tensorflow中並沒有明確的定義,我的理解tower是tensorflow神經網路模型的一部分,是在梯度漸變計算中抽象出來可獨立計算的函式。可以在單GPU上進行計算,也支援在多GPUs上進行獨立計算。

注意:
   對於Research中開源的Inception程式碼庫,谷歌的開發者們將大部分的後續改進工作轉移到了 slim 程式碼庫中。即可以在 slim 中找到此程式碼庫的最新版本,持續學習可以參考。

PS. 本人作為機器學習的新鳥一隻,在學習過程中對很多概念和演算法都不明覺厲,如有錯誤請批評指正。

掃描二維碼,關注公眾號。 獲得最新的移動開發技術乾貨。

Tensorflow(1)IT男識別玫瑰

相關文章