1. 程式人生 > >執行 Tensorflow object_detection API

執行 Tensorflow object_detection API

在 Object Detection API 的示例程式碼中包含了一個訓練識別寵物的 Demo,包括資料集和相應的一些程式碼。雖然本課程中我們會自己準備資料和指令碼來進行訓練,但是在這之前還需要安裝一些庫、配置一下環境。在配置完成之後,執行一下這個訓練寵物的 Demo,以便檢查環境配置是否 OK,同時對訓練過程先有個整體的瞭解,然後再準備自己的資料和訓練指令碼。 

請確保已經安裝好了 Python 2.7-(我用的是Py35)。

安裝 Object Detection API

首先下載 Object Detection API 的程式碼:

git clone https://github.com/tensorflow/models.git

修改程式碼(具體看-這篇文章):

research/object_detection/utils/learning_schedules.py檔案的 第167-169行由

# # 修改167-169
rate_index = tf.reduce_max(tf.where(tf.greater_equal(global_step, boundaries),
range(num_boundaries),
[0] * num_boundaries))
# # 成
rate_index = tf.reduce_max(tf.where(tf.greater_equal(global_step, boundaries),
list(range(num_boundaries)), [0] * num_boundaries))

然後安裝 TensorFlow(我這裡使用 tensorflow-gpu  1.4.1報錯,API已經更新):

pip install tensorflow-gpu==1.8.0

接著是一些依賴庫:

pip install pillow
pip install lxml
pip install jupyter
pip install matplotlibpip install contextlib2

Object Detection API 中的模型和訓練引數是使用 protobuf 來序列化和反序列化的,所以在執行之前需要將相應的 protobuf 檔案編譯出來。

#進入 tensorflow/models/research/protoc object_detection/protos/*.proto --python_out=.

成功編譯以後可以在 object_detection/protos/ 下找到生成 .py 和 .pyc 檔案。

接下來將 Object Detection API 的庫加入到 PYTHONPATH 中:

#進入 tensorflow/models/research/export PYTHONPATH=$PYTHONPATH:`pwd`:`pwd`/slim

執行 Object Detection API 的指令碼,以及之後自己寫的指令碼都會用到這些庫,如果不想每次執行前都敲這個命令的話,可以把這條命令加入到 ~/.bashrc 中(需要將 pwd 展開為實際路徑):

# When running locally, the tensorflow/models/research/ and slim directoriesexport PYTHONPATH=$PYTHONPATH:/home/raini/pro/tf_models/research:/home/raini/pro/tf_models/research/slim

最後執行一下測試指令碼來檢測安裝是否正確:

#進入 tensorflow/models/research/python object_detection/builders/model_builder_test.py

如果看到下面的輸出,那麼 Object Detection API 的安裝就完成了。

下載資料集

資料集由圖片和相應的標註檔案組成:

wget http://www.robots.ox.ac.uk/~vgg/data/pets/data/images.tar.gz
wget http://www.robots.ox.ac.uk/~vgg/data/pets/data/annotations.tar.gz
tar -xvf annotations.tar.gz
tar -xvf images.tar.gz

完成以後目錄應該看起來是這樣的:

images:

annotations:

在 images 目錄就是一些寵物的照片,而在 annotations 資料夾裡面是對相應照片的標註,在 annotations 資料夾中的和 images 資料夾中照片檔名一致的 xml 檔案就是標註檔案,這些標註檔案為 PASCAL VOC 格式,可以開啟 Abyssinian_1.xml看一下:

標註內容主要為圖片的源資訊,如高和寬、物體的名稱及所在位置:(xmin、ymin、xmax、ymax)所標識的矩形框。

還記得需要一個物體類別的數字編號和物體類別實際名稱的對應關係的檔案嗎?可以在這裡找到:

object_detection/data/pet_label_map.pbtxt

檔案內容看起來是這樣的:

注意:所有物體類別的數字編號都是從 1 開始的,因為 0 是一個在數學計算中很特殊的值。 

生成 TFRecord 檔案

Object Detection API 的訓練框架使用 TFRecord 格式的檔案作為輸入。所以這裡需要將圖片和標註轉換為 TFRecord 格式的檔案。 

TFRecord 資料檔案是一種將影象資料和標籤統一儲存的二進位制檔案,能更好的利用記憶體,在 TensorFlow 中快速的複製、移動、讀取、儲存等。

Demo 裡面包含了生成對應 TFRecord 格式檔案的指令碼,執行:

# 進入 tensorflow/models/research/
python object_detection/create_pet_tf_record.py \
    --label_map_path=object_detection/data/pet_label_map.pbtxt \
    --data_dir=DATA_DIR \
    --output_dir=DATA_DIR

這裡需要將 DATA_DIR 替換為 images 和 annotations 所在的資料夾(父資料夾),例如:

python object_detection/dataset_tools/create_pet_tf_record.py \
    --label_map_path=object_detection/data/pet_label_map.pbtxt \
    --data_dir=/home/byz/pro/tf_models/byz/pet_dataset \
    --output_dir=/home/byz/pro/tf_models/byz/pet_dataset/output_dir

得到輸出檔案大致如下:

pet_train.record 為訓練集,pet_val.record 為測試集。

準備轉移學習

還需要一個 Pre-trained 模型來進行轉移學習,儘量的縮短學習的時間,預訓練模型檔案可在這裡下載所需要的

下載以後解壓備用:

在轉移學習中要用的檔案是 model.ckpt.* 這三個檔案。

準備配置檔案

還需要一個配置檔案來對訓練的流程進行配置,如使用什麼演算法,選用什麼優化器等。在 object_detection/samples/configs/ 可以找到很多配置模板,在這裡使用 object_detection/samples/configs/ssd_mobilenet_v1_pets.config 作為起始的配置檔案,需要在這個模板上面稍作修改。

這個配置檔案是一個 JSON 格式的檔案,裡面有很多配置項,先挑一些必須修改的或者重要的專案:

    train_input_reader: {
       tf_record_input_reader {
       input_path:   "PATH_OF_TRAIN_TFRECORD"  <--("byz/pet_dataset/output_dir/pet_faces_train.record"
       }
       label_map_path: "PATH_OF_LABEL_MAP"      <--("byz/pet_dataset/pet_label_map.pbtxt"
    }

需要將PATH_OF_TRAIN_TFRECORD替換為pet_train.record的絕對路徑,將PATH_OF_LABEL_MAP替換為pet_label_map.pbtxt的絕對路徑。

    eval_input_reader: {
      tf_record_input_reader {
        input_path: "PATH_OF_VAL_TFRECORD"      <--(byz/pet_dataset/output_dir/pet_faces_val.record"
      }
      label_map_path: "PATH_OF_LABEL_MAP"
    }

需要將PATH_OF_VAL_TFRECORD替換為pet_val.record的絕對路徑,將PATH_OF_LABEL_MAP替換為pet_label_map.pbtxt的絕對路徑:

    train_config: {      fine_tune_checkpoint: "CHECK_POINT_PATH"  <--("/home/byz/pro/tf_models/byz/faster_rcnn_resnet101_coco_11_06_2017/model.ckpt"
      from_detection_checkpoint: true
      num_steps: 200000
    }

如果將from_detection_checkpoint設為true的話,代表將從一個事先訓練好的模型開始繼續訓練(轉移學習),此時需要將CHECK_POINT_PATH替換為 model.ckpt 的絕對路徑(注意之前有三個檔案,model.ckpt.index、model.ckpt.meta、model.ckpt.data-xxx 在配置時不需要加model.ckpt 之後的字尾),如:fine_tune_checkpoint: "/home/byz/pro/tf_models/byz/faster_rcnn_resnet101_coco_11_06_2017/model.ckpt"

num_steps 為訓練迭代的步數,這裡暫時不修改。

將改好以後的配置檔案重新命名為 pipeline.config

開始訓練

準備好訓練資料和配置檔案以後,就可以開始進行訓練了。通常會把訓練會用到的檔案放到一起(訓練目錄),這裡建議把訓練目錄設定為這樣:

注意:需要按照這個目錄結構修改 pipeline.config 中的相應項。

然後執行訓練指令碼:

    # 進入 tensorflow/models/research/
    python object_detection/train.py \
        --logtostderr \
        --pipeline_config_path=${TRAIN_DIR}/model/pipeline.config} \
        --train_dir=${TRAIN_DIR}/model/train

TRAIN_DIR需要替換為訓練目錄的絕對路徑-checkpoint輸出。

CUDA_VISIBLE_DEVICES="" python object_detection/train.py \
        --logtostderr \
        --pipeline_config_path=/media/raini/deepin/byz/dataset/pet-record/pipeline.config \
        --train_dir=/home/raini/pro/tf_models/byz/train_checkpoint

模型開始訓練,可以在終端上看到以下輸出:


每一行輸出為:訓練迭代步數/當前損失值/每步訓練所花時間。

基本上可以看出,隨著訓練的進行,每一步的損失值是下降的,那是不是可以喝咖啡等待訓練結束了呢?

不過,每一步執行的時間大概在 10 秒左右,那麼按照我們的配置 200000 步需要 200000 X 10 秒 = 23 天左右,這顯然是不能接受的。

看來用筆記本的 CPU 進行訓練可能不是一個好主意,需要更強的計算力:GPU。

配置好了訓練環境,也把一個訓練 Demo 運行了起來,但是筆記本的 CPU 運算能力顯然不足應付這個任務,那麼接下來讓我們在 GPU 上面執行訓練。

報錯:

(如今的程式碼僅支援tf-1.5版本以上,所以需要升級版本,但是我的cuda是8.0,愁死我了)

Instructions for updating:
Please switch to tf.train.create_global_step
WARNING:tensorflow:num_readers has been reduced to 0 to match input file shards.
Traceback (most recent call last):
  File "object_detection/train.py", line 184, in <module>
    tf.app.run()
  File "/home/byz/anaconda3/envs/py35/lib/python3.5/site-packages/tensorflow/python/platform/app.py", line 48, in run
    _sys.exit(main(_sys.argv[:1] + flags_passthrough))
  File "object_detection/train.py", line 180, in main
    graph_hook_fn=graph_rewriter_fn)
  File "/home/byz/pro/tf_models/models/research/object_detection/trainer.py", line 274, in train
    train_config.prefetch_queue_capacity, data_augmentation_options)
  File "/home/byz/pro/tf_models/models/research/object_detection/trainer.py", line 59, in create_input_queue
    tensor_dict = create_tensor_dict_fn()
  File "object_detection/train.py", line 121, in get_next
    dataset_builder.build(config)).get_next()
  File "/home/byz/pro/tf_models/models/research/object_detection/builders/dataset_builder.py", line 186, in build
    process_fn, config.input_path[:], input_reader_config)
  File "/home/byz/pro/tf_models/models/research/object_detection/utils/dataset_util.py", line 137, in read_dataset
    tf.contrib.data.parallel_interleave(
AttributeError: module 'tensorflow.contrib.data' has no attribute 'parallel_interleave'

升級了一下公司伺服器的CUDA:

訓練會一直進行直到kill它。

在train_checkpoint目錄下會看到model.ckpt-***, 需要將其轉換成.pb檔案。


# 使用官方的指令碼轉換成.pb模型
# From tensorflow/models/research/
PIPELINE_CONFIG_PATH = 
TRAIN_PATH =
EXPORT_DIR =
python object_detection/export_inference_graph.py \
    --input_type image_tensor \
    --pipeline_config_path "/home/raini/pro/tf_models/byz/trian_deepfasion_all/raini/deepfasion_nas/train_checkpoint/2299/pipeline.config" \
    --trained_checkpoint_prefix "/home/raini/pro/tf_models/byz/trian_deepfasion_all/raini/deepfasion_nas/train_checkpoint/2299/model.ckpt-2299" \
    --output_directory "/home/raini/pro/tf_models/byz/trian_deepfasion_all/raini/deepfasion_nas/train_checkpoint/output_pb"

使用模型檢測圖片

import tensorflow as tf
# Imports
import time

start = time.time()
import numpy as np
import os
import six.moves.urllib as urllib
import sys
import tarfile
import zipfile
import cv2

from collections import defaultdict
# from io import StringIO
# from matplotlib import pyplot as plt
from PIL import Image

if tf.__version__ < '1.4.0':
    raise ImportError('Please upgrade your tensorflow installation to v1.4.* or later!')

os.chdir('/home/raini/pro/tf_models/models/research/object_detection')

# Env setup
# This is needed to display the images.
# %matplotlib inline
# This is needed since the notebook is stored in the object_detection folder.
sys.path.append("..")

# Object detection imports
from utils import label_map_util

from utils import visualization_utils as vis_util

# Model preparation
# What model to download.
# 這是我們剛才訓練的模型
# MODEL_NAME = 'tv_vehicle_inference_graph'
MODEL_NAME = '/home/raini/pro/tf_models/byz/trian_deepfasion_all/raini/deepfasion_nas/train_checkpoint/output_pb'
# 對應的Frozen model位置
# Path to frozen detection graph. This is the actual model that is used for the object detection.
PATH_TO_CKPT = MODEL_NAME + '/frozen_inference_graph.pb'
# List of the strings that is used to add correct label for each box.
# PATH_TO_LABELS = os.path.join('training', 'detection.pbtxt')
PATH_TO_LABELS = '/home/raini/pro/tf_models/byz/trian_deepfasion_all/raini/deepfasion_nas/test_3_label.pbtxt'
# 改成自己例子中的類別數,2
NUM_CLASSES = 3
''''' 
#Download Model 
自己的模型,不需要下載了opener = urllib.request.URLopener() 
opener.retrieve(DOWNLOAD_BASE + MODEL_FILE, MODEL_FILE) 
tar_file = tarfile.open(MODEL_FILE) 
for file in tar_file.getmembers(): 
  file_name = os.path.basename(file.name) 
  if 'frozen_inference_graph.pb' in file_name: 
    tar_file.extract(file, os.getcwd()) 
'''
# Load a (frozen) Tensorflow model into memory.
detection_graph = tf.Graph()
with detection_graph.as_default():
    od_graph_def = tf.GraphDef()
    with tf.gfile.GFile(PATH_TO_CKPT, 'rb') as fid:
        serialized_graph = fid.read()
        od_graph_def.ParseFromString(serialized_graph)
        tf.import_graph_def(od_graph_def, name='')

    # Loading label map
label_map = label_map_util.load_labelmap(PATH_TO_LABELS)
categories = label_map_util.convert_label_map_to_categories(label_map, max_num_classes=NUM_CLASSES,
use_display_name=True)
category_index = label_map_util.create_category_index(categories)


# Helper code
def load_image_into_numpy_array(image):
    (im_width, im_height) = image.size
    return np.array(image.getdata()).reshape(
        (im_height, im_width, 3)).astype(np.uint8)


# Detection
# If you want to test the code with your images, just add path to the images to the TEST_IMAGE_PATHS.
# 測試圖片位置
PATH_TO_TEST_IMAGES_DIR = "/home/raini/pro/tf_models/byz/trian_deepfasion_all/raini/deepfasion_nas/test_img"
os.chdir(PATH_TO_TEST_IMAGES_DIR)
TEST_IMAGE_PATHS = os.listdir(PATH_TO_TEST_IMAGES_DIR)

# Size, in inches, of the output images.
IMAGE_SIZE = (12, 8)

output_path = ('/home/raini/pro/tf_models/byz/trian_deepfasion_all/raini/deepfasion_nas/test_output/')

with detection_graph.as_default():
    with tf.Session(graph=detection_graph) as sess:
        # Definite input and output Tensors for detection_graph
image_tensor = detection_graph.get_tensor_by_name('image_tensor:0')
        # Each box represents a part of the image where a particular object was detected.
detection_boxes = detection_graph.get_tensor_by_name('detection_boxes:0')
        # Each score represent how level of confidence for each of the objects.
        # Score is shown on the result image, together with the class label.
detection_scores = detection_graph.get_tensor_by_name('detection_scores:0')
        detection_classes = detection_graph.get_tensor_by_name('detection_classes:0')
        num_detections = detection_graph.get_tensor_by_name('num_detections:0')
        for image_path in TEST_IMAGE_PATHS:
            image = Image.open(image_path)
            # the array based representation of the image will be used later in order to prepare the
            # result image with boxes and labels on it.
image_np = load_image_into_numpy_array(image)
            # Expand dimensions since the model expects images to have shape: [1, None, None, 3]
image_np_expanded = np.expand_dims(image_np, axis=0)
            # Actual detection.
(boxes, scores, classes, num) = sess.run(
                [detection_boxes, detection_scores, detection_classes, num_detections],
feed_dict={image_tensor: image_np_expanded})
            # Visualization of the results of a detection.
vis_util.visualize_boxes_and_labels_on_image_array(
                image_np,
np.squeeze(boxes),
np.squeeze(classes).astype(np.int32),
np.squeeze(scores),
category_index,
use_normalized_coordinates=True,
line_thickness=8)
            # 儲存檔案
cv2.imwrite(output_path + image_path.split('\\')[-1], image_np)

end = time.time()
print("Execution Time: ", end - start)

最後的預測結果(換了份資料集)