10分鍾內基於gpu的目標檢測


10分鍾內基於gpu的目標檢測

Object Detection on GPUs in 10 Minutes

目標檢測仍然是自動駕駛和智能視頻分析等應用的主要驅動力。目標檢測應用程序需要使用大量數據集進行大量訓練,以實現高精度。NVIDIA gpu在訓練大型網絡以生成用於對象檢測推斷的數據集所需的並行計算性能方面表現優異。本文介紹了使用NVIDIA gpu快速高效地運行高性能目標檢測管道所需的技術。             

我們的python應用程序從實時視頻流中獲取幀,並在gpu上執行對象檢測。我們使用帶有Inception V2的預先訓練的單點檢測(SSD)模型,應用TensorRT的優化,為GPU生成一個運行時,然后對視頻feed執行推斷以獲得標簽和邊界框。然后,應用程序使用這些邊界框和類標簽注釋原始框架。生成的視頻源上覆蓋了來自我們的對象檢測網絡的邊界框預測。同樣的方法可以擴展到其他任務,如分類和分割。             

雖然不需要了解GPUs和NVIDIA軟件,但您應該熟悉對象檢測和python編程。使用的一些軟件工具包括NVIDIA GPU Cloud(NGC)的Docker容器來設置我們的環境,OpenCV來運行來自攝像機的feed,以及TensorRT來加速我們的推斷。雖然您將受益於簡單閱讀這篇文章,您需要一個CUDA功能的GPU和一個網絡攝像頭連接到您的機器來運行這個例子。您可以使用命令nvidia smi測試工作的GPU。你可能會發現這個CUDA gpu列表很有用。             

在本文結束時,您將了解設置端到端對象檢測推斷管道所需的組件,如何在gpu上應用不同的優化,以及如何在管道上執行FP16和INT8精度的推斷。在本例中,我們使用以InceptionV2為骨干的單點檢測網絡。作為參考,所有代碼(以及如何安裝所有內容的詳細自述文件)都可以在NVIDIA GitHub repo上找到。

Run the Sample!

我們使用docker容器來設置環境並將其打包以供分發。我們可以回憶許多使用容器的情況,在這些情況下,很容易從沖突和崩潰中恢復,因此在嘗試此示例之前,請確保您的計算機上有Docker和NVIDIA Docker。             

導航到“主要對象檢測”網絡攝像頭文件夾並運行以下部分以生成容器並運行應用程序:

./setup_environment.sh

python SSD_Model/detect_objects_webcam.py

這將彈出一個窗口,顯示來自您的網絡攝像頭的視頻源,並覆蓋邊框和標簽,如圖1所示。

 

Figure 1. The output on the command prompt displays the time taken for inference and the Top-1 prediction of target classes

Setup with NGC and TensorRT open source software

讓我們檢查一下安裝程序,安裝程序中提供了安裝程序的所有setup_environment.sh。有4個關鍵步驟:             

設置Docker查看網絡攝像頭的環境變量             

下載用於INT8校准的VOC數據集(我們稍后將在博客中看到)             

構建包含運行代碼所需的所有庫的Dockerfile             

啟動Dockerfile以便在正確的環境中啟動應用程序             

因為我們使用Docker容器來管理我們的環境,所以我們需要讓容器訪問主機中的所有硬件。大部分是由Docker自動處理的,除了我們手動添加的攝像頭。我們需要設置Docker訪問X11的權限,X11用於打開網絡攝像頭源的圖形用戶界面。使用環境變量並通過設置docker run命令期間傳遞到容器中的權限來執行此操作。

xhost +local:docker
XSOCK=/tmp/.X11-unix
XAUTH=/tmp/.docker.xauth
xauth nlist $DISPLAY | sed -e 's/^..../ffff/' |
xauth -f $XAUTH nmerge -

接下來,我們下載用於INT8校准的PASCAL VOC數據集,我們將在后面的章節中介紹。這個數據集包含普通家庭用品和日常用品的圖像。

wget http://host.robots.ox.ac.uk/pascal/VOC/voc2007/VOCtest_06-Nov-2007.tar
tar -xf VOCtest_06-Nov-2007.tar

然后我們建立一個包含我們整個開發環境的Dockerfile。Dockerfile安裝以下組件:             

TensorRT和必需的庫             

TensorRT開源軟件,在TensorRT安裝中替換插件和解析器             

我們應用程序的其他依賴項             

NVIDIA NGC的TensorRT容器使安裝TensorRT變得非常簡單。容器包含必需的庫,如CUDA、cuDNN和NCCL。NGC是一個預構建容器的存儲庫,每個月更新一次,並跨平台和雲服務提供商進行測試。查看發行說明中TensorRT容器中的內容。由於除了TensorRT之外,我們還需要組合其他多個庫和包,因此我們將創建一個自定義Dockerfile,並將TensorRT容器作為基本映像。             

我們在示例中使用了TensorRT插件和解析器的最新版本,因為它們是開源的。插件提供了一種在TensorRT中的模型中使用自定義層的方法,並且已經包含在TensorRT容器中。例如,SSD模型使用插件庫中的flattencat插件。嚴格地說,在本例中,我們不需要使用插件的開源版本;使用TensorRT容器中提供的版本也可以工作。這很容易知道,使您能夠擴展和自定義這些組件,以支持您的模型中的自定義層。             

為了獲得開源插件,我們克隆了TensorRT github repo,使用cmake構建組件,並用新版本替換TensorRT容器中這些組件的現有版本。TensorRT應用程序將在此路徑下搜索TensorRT核心庫、解析器和插件。             

最后,我們可以安裝應用程序所需的其他依賴項,這些依賴項主要是OpenCV及其呈現庫。OpenCV是一個計算機視覺庫,我們使用它與我們的網絡攝像頭進行交互。             

使用docker build命令生成Dockerfile中的所有組件:

docker build -t object_detection_webcam . # don’t forget the period at the end

一旦容器啟動,就可以使用detect_objects_webcam.py對象運行應用網絡攝像頭。

Optimize Model, Build Engine for Inference

探測目標的detect_objects_webcam.py應用程序如下,如圖2所示:

# Download the frozen object detection model from TensorFlow Model Zoo
# Convert the frozen model (.pb file) to Universal Framework Format (UFF) 
# Build the TensorRT engine from the UFF version of the model 
# While True: 
             # Read in a frame from the webcam 
# Run inference on that frame using our TensorRT engine 
# Overlay the bounding boxes and class labels 
# Display that frame back to the user

 

Figure 2. This post covers all the steps in this workflow, from building the TensorRT engine to plugging it into a simple application.

第一步是從TensorFlow model zoo下載凍結的SSD對象檢測模型。

This is done in prepare_ssd_model in model.py:  

221 def prepare_ssd_model(model_name="ssd_inception_v2_coco_2017_11_17", silent=False):
222    """Downloads pretrained object detection model and converts it to UFF.
223
224    The model is downloaded from Tensorflow object detection model zoo.
225    Currently only ssd_inception_v2_coco_2017_11_17 model is supported
226    due to model_to_uff() using logic specific to that network when converting.
227
228    Args:
229        model_name (str): chosen object detection model
230        silent (bool): if True, writes progress messages to stdout
231    """
232    if model_name != "ssd_inception_v2_coco_2017_11_17":
233        raise NotImplementedError(
234            "Model {} is not supported yet".format(model_name))
235    download_model(model_name, silent)
236    ssd_pb_path = PATHS.get_model_pb_path(model_name)
237    ssd_uff_path = PATHS.get_model_uff_path(model_name)
238    model_to_uff(ssd_pb_path, ssd_uff_path, silent)

下一步是優化這個模型進行推理,並生成在GPU上執行的運行時。我們使用TensorRT,一個深度學習優化器和運行時引擎。TensorRT從這個應用程序為每個NVIDIA GPU生成運行時。您需要應用程序提供盡可能低的延遲來實時執行推斷。讓我們看看如何使用TensorRT。             

使用中提供的實用程序將凍結的TensorFlow圖轉換為通用框架格式(UFF)model.py。現在,您可以使用解析器將UFF模型導入TensorRT,應用優化並生成運行時引擎。優化是在構建過程中在幕后應用的,您不需要做任何事情來應用它們。例如,TensorRT可以將卷積、ReLU和偏壓等多個層融合到單個層中。這叫做層融合。另一種優化方法是張量融合或層聚合,在這種方法中,共享相同輸入的層融合到一個內核中,然后將它們的結果分離。             

要構建運行時引擎,需要指定4個參數:

模型的UFF文件路徑             

推理機精度(FP32、FP16或INT8)             

校准數據集(僅在運行INT8時需要)             

推斷期間使用的批大小

見engine制造規范engine.py. 生成引擎的函數稱為build_engine。              

較低精度的推理(FP16和INT8)增加了吞吐量並提供較低的延遲。使用FP16精度在張量核上提供比FP32快幾倍的性能,有效地不降低模型精度。INT8中的推理可以在模型精度下降不到1%的情況下進一步提高性能。TensorRT從FP32和您允許的任何精度中選擇內核。啟用FP16精度時,TensorRT從FP16和FP32精度中選擇內核。要使用FP16和INT8精度,請啟用兩者以獲得盡可能高的性能。             

利用定標法確定圖中張量的動態范圍,可以有效地利用INT8精度的限制范圍。稍后再談。             

最后一個參數batch size用於為推理工作負載選擇最佳內核。您可以將引擎用於比創建期間指定的更小的批處理大小。不過,表現可能並不理想。我通常為我期望的最常見的批處理大小生成一些引擎,並在它們之間切換。在本例中,我們將一次從攝像頭中抓取一幀,使批量大小為1。             

還需要注意的是,TensorRT會自動檢測GPU上的任何專用硬件。因此,如果你的GPU有張量核,它會自動檢測並在這些張量核上運行FP16內核。             

讓我們看看engine.py看看這些參數是如何工作的。

69 def build_engine(uff_model_path, trt_logger, trt_engine_datatype=trt.DataType.FLOAT, calib_dataset=None, batch_size=1, silent=False):
70    with trt.Builder(trt_logger) as builder, builder.create_network() as network, trt.UffParser() as parser:
71        builder.max_workspace_size = 2 << 30
72        builder.max_batch_size = batch_size
73        if trt_engine_datatype == trt.DataType.HALF:
74            builder.fp16_mode = True
75        elif trt_engine_datatype == trt.DataType.INT8:
76            builder.fp16_mode = True
77            builder.int8_mode = True
78            builder.int8_calibrator = calibrator.SSDEntropyCalibrator(data_dir=calib_dataset, cache_file='INT8CacheFile')
79
80        parser.register_input(ModelData.INPUT_NAME, ModelData.INPUT_SHAPE)
81        parser.register_output("MarkOutput_0")
82        parser.parse(uff_model_path, network)
83
84        if not silent:
85            print("Building TensorRT engine. This may take few minutes.")
86
87        return builder.build_cuda_engine(network)

build_engine函數為生成器、解析器和網絡創建一個對象。解析器以UFF格式導入SSD模型,並將轉換后的圖形放在network對象中。當我們使用UFF解析器導入轉換后的TensorFlow模型時,TensorRT還包括用於Caffe和ONNX的解析器。兩者都可以在TensorRT開源repo中找到。使用此模型的ONNX格式只意味着調用ONNXParser;其余代碼將是相同的。             

第71行指定TensorRT應用於應用優化的內存。這只是臨時空間,您應該提供系統允許的最大大小;我提供2 GB。條件代碼根據推理的精度設置參數。對於第一次運行,我們使用默認的FP32精度。             

接下來的幾行指定解析器的輸入節點和輸出節點的名稱和形狀。這個parser.parse分析實際使用上面指定的參數在UFF文件上執行解析器。最后,builder.build_cuda_engine對網絡應用優化,並生成引擎對象。             

劇本engine.py有兩個附加的關鍵功能:保存引擎和加載引擎save_engine and load_engine。生成引擎后,可以將其保存到磁盤以備將來使用,這是一個稱為序列化的過程。序列化生成一個計划文件,隨后可以從磁盤加載該文件,通常比從頭重新構建引擎快得多。這就是這些加載和保存函數的作用。如果您確實更改了用於構建引擎、使用的模型或GPU的參數,則需要重新生成引擎,因為TensorRT將選擇不同的內核來構建引擎。             

您可以從NGC模型下載預訓練模型、參數和精度的多個組合的計划文件。如果我使用的是標准模型,我通常首先檢查NGC上是否有可直接在我的應用程序中使用的計划文件。

Run Inference With TensorRT Engine

我們現在可以使用TensorRT引擎執行目標檢測。我們的示例一次從網絡攝像機獲取一幀,並將其傳遞給TensorRT引擎推理inference.py -更具體地說,在功能推斷網絡攝像頭infer_webcam。

166    def infer_webcam(self, arr):
167        """Infers model on given image.
168
169        Args:
170            arr (numpy array): image to run object detection model on
171        """
172
173       # Load image into CPU and do any pre-processing
174        img = self._load_img_webcam(arr)
175
176        # Copy it into appropriate place into memory
177        # (self.inputs was returned earlier by allocate_buffers())
178        np.copyto(self.inputs[0].host, img.ravel())
179 
180        # When inferring on single image, we measure inference
181        # time to output it to the user
182        inference_start_time = time.time()
183
184        # Fetch output from the model
185        [detection_out, keepCount_out] = do_inference(
186            self.context, bindings=self.bindings, inputs=self.inputs,
187            outputs=self.outputs, stream=self.stream)
188
189        # Output inference time
190        print("TensorRT inference time: {} ms".format(
191            int(round((time.time() - inference_start_time) * 1000))))
192
193        # And return results
194        return detection_out, keepCount_out

此函數首先從網絡攝像機加載圖像(第174行),然后在函數load_img_webcam網絡攝像機中執行幾個預處理步驟。我們的示例將軸的順序從HWC移動到CHW,對圖像進行規格化,使所有值都在-1和+1之間,然后展平數組。您還可以在此函數中添加管道所需的任何其他預處理操作。             

計時器從第182行開始測量TensorRT引擎執行推理所需的時間。這有助於理解整個推理管道的延遲。             

我們調用do_inference來執行推理。此函數將數據發送到TensorRT引擎進行推理,並返回兩個參數:detection_out和keepCount_out。detection_out函數包含每次檢測的邊界框坐標、置信度和類標簽的所有信息。keepCount_out例程跟蹤網絡發現的檢測總數。

Putting It All Together

到目前為止,我們已經研究了如何從TensorFlow model zoo導入一個預先訓練的模型,將其轉換為UFF格式,應用優化並生成TensorRT引擎,並使用該引擎對來自網絡攝像機的單個圖像執行推斷。              

讓我們看看所有這些組件在檢測對象時是如何組合在一起detect_objects_webcam.py:

166    def infer_webcam(self, arr):
167        """Infers model on given image.
168
169        Args:
170            arr (numpy array): image to run object detection model on
171        """
172
173       # Load image into CPU and do any pre-processing
174        img = self._load_img_webcam(arr)
175
176        # Copy it into appropriate place into memory
177        # (self.inputs was returned earlier by allocate_buffers())
178        np.copyto(self.inputs[0].host, img.ravel())
179 
180        # When inferring on single image, we measure inference
181        # time to output it to the user
182        inference_start_time = time.time()
183
184        # Fetch output from the model
185        [detection_out, keepCount_out] = do_inference(
186            self.context, bindings=self.bindings, inputs=self.inputs,
187            outputs=self.outputs, stream=self.stream)
188
189        # Output inference time
190        print("TensorRT inference time: {} ms".format(
191            int(round((time.time() - inference_start_time) * 1000))))
192
193        # And return results
194        return detection_out, keepCount_out

解析命令行參數后,prepare_ssd_model使用model.py將凍結的TensorFlow圖轉換為UFF格式。然后在第153行初始化一個TensorRT推理對象,該對象在engine.py如上所述,實際構建TensorRT引擎。如果沒有引擎文件保存在args.trt_引擎路徑然后我們需要從頭開始。UFF版本的模型也是如此。我們將在默認的FP32精度下運行,這樣就不需要提供校准數據集。最后,由於我們只在一個網絡攝像頭feed上運行實時推斷,因此我們將保持批大小為1。             

現在讓我們將其集成到操作網絡攝像頭的應用程序中。如果打開相機標志(默認),應用程序將使用OpenCV(第164行)啟動視頻流,並在第167行中輸入主循環。如第169行所示,該循環不斷從網絡攝像機中拉入新幀,然后如第172行所示對該幀執行推斷。             

最后,我們將邊界框結果覆蓋到原始幀(第176-180行)上,並使用imshow將其顯示給用戶。             

這就是我們的整個管道!

Inference in INT8 Precision With TensorRT

與框架內推理相比,應用程序在gpu上使用TensorRT執行推理的速度快了幾倍。但是,你可以使它快幾倍。到目前為止,我們使用單精度(FP32)進行推理,其中每個數字都使用32位表示。在FP32中,激活值可以在±3.4×1038的范圍內,並且需要32位來存儲每個數字。數量越大,執行時需要的存儲空間就越大,這也會導致性能降低。當切換到使用精度較低的FP16時,大多數型號的精度幾乎相同。使用NVIDIA提供的模型和技術,可以使用INT8 precision進行推理,從而獲得盡可能高的性能。但是,請注意表1中可以用INT8精度表示的明顯較低的動態范圍。

 

Table 1. The dynamic range of values that can be represented at in FP32, FP16, and INT8 precision

使用INT8精度獲得類似於FP32推斷的精度意味着執行稱為校准的附加步驟。在校准期間,您可以對與最終數據集類似的訓練數據運行推斷,並收集激活值的范圍。然后,TensorRT計算一個比例因子,將INT8值的范圍分布在每個節點的激活值范圍內。圖3顯示,如果一個節點的激活范圍在-6和+6之間,那么您希望可以用INT8表示的256個值只覆蓋這個范圍。

 

Figure 3. Calibration and quantization are critical steps for converting to INT8 precision.

使用下面的命令重新構建TensorRT引擎,以便在應用程序中使用INT8來提高精度,執行校准並運行推斷。整個過程可能需要幾分鍾:

python detect_objects_webcam -p 8

您應該會看到與先前使用FP32精度獲得的結果相比,性能更高的相同結果。             

讓我們看看如何在內置引擎中執行此操作engine.py. 條件塊基於為推理啟用的精度啟用不同的生成器模式。默認情況下,TensorRT始終選擇FP32內核。啟用FP16模式意味着它還會嘗試以FP16精度運行的內核;INT8也是如此。             

然而,僅僅因為允許較低精度的內核並不意味着它們在性能上總是優於較高精度的內核。例如,即使我們將precision模式設置為INT8,一些FP16或FP32內核可能仍然存在,最終會運行得更快。TensorRT自動選擇最佳優化。             

TensorRT檢測專用硬件(如Tensor內核)的存在,並將在其上使用FP16內核以獲得盡可能高的性能。TensorRT自動選擇最佳內核的能力稱為內核自動調整。這使得在提供高性能的同時跨多種應用程序使用TensorRT成為可能。

69 def build_engine(uff_model_path, trt_logger, trt_engine_datatype=trt.DataType.FLOAT, calib_dataset=None, batch_size=1, silent=False):
70    with trt.Builder(trt_logger) as builder, builder.create_network() as network, trt.UffParser() as parser:
71        builder.max_workspace_size = 2 << 30
72        builder.max_batch_size = batch_size
73        if trt_engine_datatype == trt.DataType.HALF:
74            builder.fp16_mode = True
75        elif trt_engine_datatype == trt.DataType.INT8:
76            builder.fp16_mode = True
77            builder.int8_mode = True
78            builder.int8_calibrator = calibrator.SSDEntropyCalibrator(data_dir=calib_dataset, cache_file='INT8CacheFile')
注意,INT8條件塊使用函數SSDEntropyCalibrator。這個類在批量校准期間通過模型運行校准數據。因此,只需實現名為get_batch in的函數calibrator.py從校准數據集中獲取下一批數據。請參閱中的SSDEntropyCalibrator代碼calibrator.py下面。
14 class SSDEntropyCalibrator(trt.IInt8EntropyCalibrator2):
15    def __init__(self, data_dir, cache_file):
16        # Whenever you specify a custom constructor for a TensorRT class,
17        # you MUST call the constructor of the parent explicitly.
18        trt.IInt8EntropyCalibrator2.__init__(self)
19
20        self.num_calib_imgs = 100 # the number of images from the dataset to use for calibration
21        self.batch_size = 10
22        self.batch_shape = (self.batch_size, IMG_CH, IMG_H, IMG_W)
23        self.cache_file = cache_file
24
25        calib_imgs = [os.path.join(data_dir, f) for f in os.listdir(data_dir)]
26        self.calib_imgs = np.random.choice(calib_imgs, self.num_calib_imgs)
27        self.counter = 0 # for keeping track of how many files we have read
28
29        self.device_input = cuda.mem_alloc(trt.volume(self.batch_shape) * trt.float32.itemsize)

此函數將圖像目錄作為要校准的輸入,並將其作為存儲緩存文件的位置。此緩存文件包含網絡激活所需的所有縮放因子。如果保存激活值,則只需對特定配置運行一次校准,並且只需為任何后續運行加載此緩存表。              

這就是用TensorRT進行INT8校准所需要做的一切!

Next Steps

現在您已經基本了解了如何在GPU上快速設置和運行對象檢測應用程序。我們已經覆蓋了很多領域,包括設置、INT8 precision部署、使用TensorRT中最新的開源插件和解析器、連接到網絡攝像頭以及疊加結果。如果您在使用此應用程序時遇到問題,請確保檢查此示例的GitHub repo中的問題以了解類似的問題和解決方案。             

如果你想繼續使用gpu進行目標檢測和其他與AI相關的任務,請查看開發者博客中有關為gpu創建目標檢測管道以及如何使用TensorRT加速推理的相關文章。我們還提供了一個關於如何為公共應用程序執行推理的網絡研討會,它使用了本文中介紹的相同的代碼庫。您還可以在TensorRT開源repo和TensorRT示例頁面上找到TensorRT的其他資源,其中包括剛剛介紹的SSD示例。NVIDIA TensorRT開發者論壇提供了一個TensorRT用戶社區,他們就最佳實踐交換信息。             

最后,如果你想加入免費的NVIDIA開發計划,以獲得額外的技術資源和文件錯誤報告的能力,在我們的開發程序頁注冊。您將加入NVIDIA開發人員的龐大和不斷增長的社區,為gpu創建新的和新穎的應用程序。

References

[Liu et al. 2016] Liu, Wei, et al. “SSD: Single shot multibox detector.” European Conference on Computer Vision. Springer, Cham, 2016.

[Szegedy et al. 2016] Szegedy, Christian, et al. “Rethinking the inception architecture for computer vision.” Proceedings of the IEEE conference on computer vision and pattern recognition. 2016.

[Lin et al. 2014] Lin, Tsung-Yi, et al. “Microsoft COCO: Common objects in context.” European conference on computer vision. Springer, Cham, 2014.

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM