diff --git a/examples/notebooks/YOLOX-L.ipynb b/examples/notebooks/YOLOX-L.ipynb index ad184082..80dfa443 100644 --- a/examples/notebooks/YOLOX-L.ipynb +++ b/examples/notebooks/YOLOX-L.ipynb @@ -133,7 +133,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 10, "id": "ae8af1c9", "metadata": {}, "outputs": [], @@ -143,18 +143,10 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 11, "id": "3c40a9c0", "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "libfuriosa_hal.so --- v2.0, built @ 137e868\n" - ] - } - ], + "outputs": [], "source": [ "import glob\n", "from itertools import islice\n", @@ -186,7 +178,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 12, "id": "670e87fa", "metadata": {}, "outputs": [], @@ -212,7 +204,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 13, "id": "18886593", "metadata": {}, "outputs": [], @@ -254,7 +246,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 14, "id": "390a8129", "metadata": {}, "outputs": [], @@ -275,7 +267,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 15, "id": "62d5eef9", "metadata": {}, "outputs": [ @@ -283,8 +275,8 @@ "name": "stderr", "output_type": "stream", "text": [ - "Calibration: 100 samples [00:39, 2.55 samples/s]\n", - "Quantization: 100%|██████████████████████████████████████| 437/437 [00:02<00:00, 158.47 operators/s]\n" + "Calibration: 100 samples [00:37, 2.64 samples/s]\n", + "Quantization: 100%|██████████| 437/437 [00:04<00:00, 96.66 operators/s] \n" ] } ], @@ -311,7 +303,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 16, "id": "d9c6c887", "metadata": {}, "outputs": [], @@ -353,12 +345,12 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 19, "id": "f4593209", "metadata": {}, "outputs": [], "source": [ - "compiler_config = {\n", + "compile_config = {\n", " \"without_quantize\": {\n", " \"parameters\": [{\"permute\": [0, 2, 3, 1]}]\n", " },\n", @@ -386,56 +378,26 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 20, "id": "3c03f19d", "metadata": {}, "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "Saving the compilation log into /home/yong/.local/state/furiosa/logs/compile-20220507144923-4fvt9v.log\n", - "Using furiosa-compiler 0.6.3 (rev: f0e1cf5a8 built at 2022-04-30 05:36:59)\n", - "\u001b[2m2022-05-07T05:49:23.949466Z\u001b[0m \u001b[32m INFO\u001b[0m Npu (npu0pe0-1) is being initialized\n", - "\u001b[2m2022-05-07T05:49:23.951480Z\u001b[0m \u001b[32m INFO\u001b[0m NuxInner create with pes: [PeId(0)]\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "[1/6] 🔍 Compiling from onnx to dfg\n", - "Done in 0.07355098s\n", - "[2/6] 🔍 Compiling from dfg to ldfg\n", - "557372 tactics loaded\n", - "\n", - "Done in 532.0555s\n", - "[3/6] 🔍 Compiling from ldfg to cdfg\n", - "Done in 0.00401732s\n", - "[4/6] 🔍 Compiling from cdfg to gir\n", - "Done in 0.08604555s\n", - "[5/6] 🔍 Compiling from gir to lir\n", - "Done in 36.338352s\n", - "[6/6] 🔍 Compiling from lir to enf\n", - "Done in 0.17982386s\n", - "✨ Finished in 568.7407s\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\u001b[2m2022-05-07T05:58:54.477527Z\u001b[0m \u001b[32m INFO\u001b[0m [Profiler] Program binary notification has been arrived. Cleanup current profile queue data\n", - "\u001b[2m2022-05-07T05:59:11.929899Z\u001b[0m \u001b[32m INFO\u001b[0m [Profiler] Received a termination signal.\n", - "\u001b[2m2022-05-07T05:59:11.931745Z\u001b[0m \u001b[32m INFO\u001b[0m NPU (npu0pe0-1) has been destroyed\n", - "\u001b[2m2022-05-07T05:59:11.932971Z\u001b[0m \u001b[32m INFO\u001b[0m session has been destroyed\n" + "ename": "TypeError", + "evalue": "create() got an unexpected keyword argument 'compiler_config'", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[20], line 3\u001b[0m\n\u001b[1;32m 1\u001b[0m total_predictions \u001b[39m=\u001b[39m \u001b[39m0\u001b[39m\n\u001b[1;32m 2\u001b[0m elapsed_time \u001b[39m=\u001b[39m \u001b[39m0\u001b[39m\n\u001b[0;32m----> 3\u001b[0m \u001b[39mwith\u001b[39;00m furiosa\u001b[39m.\u001b[39;49mruntime\u001b[39m.\u001b[39;49msession\u001b[39m.\u001b[39;49mcreate(\u001b[39m\"\u001b[39;49m\u001b[39myolox_l_quantized.onnx\u001b[39;49m\u001b[39m\"\u001b[39;49m, compiler_config\u001b[39m=\u001b[39;49mcompile_config) \u001b[39mas\u001b[39;00m session:\n\u001b[1;32m 4\u001b[0m \u001b[39mfor\u001b[39;00m image \u001b[39min\u001b[39;00m islice(glob\u001b[39m.\u001b[39miglob(\u001b[39m\"\u001b[39m\u001b[39mcoco/test2017/*.jpg\u001b[39m\u001b[39m\"\u001b[39m), \u001b[39m1000\u001b[39m):\n\u001b[1;32m 5\u001b[0m inputs \u001b[39m=\u001b[39m [preproc(cv2\u001b[39m.\u001b[39mimread(image), (\u001b[39m640\u001b[39m, \u001b[39m640\u001b[39m))[\u001b[39m0\u001b[39m][np\u001b[39m.\u001b[39mnewaxis, \u001b[39m.\u001b[39m\u001b[39m.\u001b[39m\u001b[39m.\u001b[39m]]\n", + "\u001b[0;31mTypeError\u001b[0m: create() got an unexpected keyword argument 'compiler_config'" ] } ], "source": [ "total_predictions = 0\n", "elapsed_time = 0\n", - "with furiosa.runtime.session.create(\"yolox_l_quantized.onnx\", compiler_config=compiler_config) as session:\n", + "with furiosa.runtime.session.create(\"yolox_l_quantized.onnx\", compile_config=compile_config) as session:\n", " for image in islice(glob.iglob(\"coco/test2017/*.jpg\"), 1000):\n", " inputs = [preproc(cv2.imread(image), (640, 640))[0][np.newaxis, ...]]\n", " start = time.perf_counter_ns()\n", @@ -454,18 +416,10 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": null, "id": "4fade41d", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Average Latency: 12.714661311 ms\n" - ] - } - ], + "outputs": [], "source": [ "latency = elapsed_time / total_predictions\n", "print(f\"Average Latency: {latency / 1_000_000} ms\")" @@ -496,7 +450,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.5" + "version": "3.9.15" } }, "nbformat": 4, diff --git a/examples/notebooks/en-YOLOX-L.ipynb b/examples/notebooks/en-YOLOX-L.ipynb new file mode 100644 index 00000000..f431a984 --- /dev/null +++ b/examples/notebooks/en-YOLOX-L.ipynb @@ -0,0 +1,563 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "c3de48b7", + "metadata": {}, + "source": [ + "# YOLOX-L 모델을 최적화 옵션을 적용해 컴파일하고 실행하기\n", + "# Apply optimization options to compile and run the YOLOX-L model." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "0bf4842b", + "metadata": {}, + "source": [ + "퓨리오사 SDK는 모델을 최적으로 컴파일하고 실행하기 위한 여러 옵션을 제공합니다. 이 문서는 퓨리오사 SDK가 제공하는 옵션을 사용해서 YOLOX-L 모델을 보다 최적으로 컴파일하고 실행하는 예를 보입니다.\n", + "\n", + "The Furiosa SDK provides several options for compiling and running models optimally. This notebook provides an example of compiling and running the YOLOX-L model more optimally using the options provided by the Furiosa SDK." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "12bbb4fc", + "metadata": {}, + "source": [ + "## 1. 준비\n", + "## 1. Preparation" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "ee61ecda", + "metadata": {}, + "source": [ + "이 문서에 포함된 예제를 실행하려면, 퓨리오사 SDK 필수 리눅스 패키지 최신 버전을 설치하고 파이썬 실행 환경을 구축해야 합니다. 리눅스 패키지를 아직 설치하지 않았거나 파이썬 실행 환경을 구성하지 않았다면, 아래 두 문서를 참고해 준비할 수 있습니다:\n", + "\n", + "* [드라이버, 펌웨어, 런타임 설치 가이드](https://furiosa-ai.github.io/docs/latest/ko/software/installation.html)\n", + "* [Python 실행 환경 구성](https://furiosa-ai.github.io/docs/latest/ko/software/python-sdk.html#python)\n", + "\n", + "To run the examples included in this document, you need to install the latest version of the Furiosa SDK Linux package and set up a Python execution environment. If you have not yet installed the Linux package or configured the Python execution environment, you can prepare by referring to the following two documents:\n", + "\n", + "* [Driver, Firmware, and Runtime Installation Guide](https://furiosa-ai.github.io/docs/latest/ko/software/installation.html)\n", + " \n", + "* [Setting up the Python Execution Environment](https://furiosa-ai.github.io/docs/latest/ko/software/python-sdk.html#python)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "ddfbafa3", + "metadata": {}, + "source": [ + "필수 리눅스 패키지와 파이썬 실행 환경을 준비하고 나면, 다음 단계로 퓨리오사 SDK 파이썬 패키지와 양자화 도구 추가 패키지 최신 버전을 설치합니다.\n", + "\n", + "Once you have prepared the necessary Linux packages and Python execution environment, the next step is to install the latest version of the Furiosa SDK Python package and the additional package for quantization tools.\n", + "\n", + "```console\n", + "$ pip3 install --upgrade 'furiosa-sdk[quantizer]'\n", + "```" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "a118baf8", + "metadata": {}, + "source": [ + "마지막으로, OpenCV에 대한 파이썬 바인딩인 `opencv-python-headless` 패키지가 필요합니다. 아래에서 이미지 파일을 읽어 들이고 전처리를 하기 위해 사용합니다.\n", + "\n", + "Finally, you need the opencv-python-headless package, which is the Python binding for OpenCV. It will be used to read and preprocess image files below.\n", + "\n", + "```console\n", + "$ pip3 install opencv-python-headless\n", + "```" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "75c65682", + "metadata": {}, + "source": [ + "### 1.1 YOLOX-L 모델\n", + "### 1.1 YOLOX-L model.\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "951ad471", + "metadata": {}, + "source": [ + "YOLOX-L 모델 구현으로는 Megvii 사가 [공개](https://yolox.readthedocs.io/en/latest/demo/onnx_readme.html)한 ONNX 모델 [yolox_l.onnx](https://github.com/Megvii-BaseDetection/YOLOX/releases/download/0.1.1rc0/yolox_l.onnx)을 사용합니다. 해당 모델을 다운로드해서 현재 디렉토리에 저장합니다.\n", + "\n", + "The implementation of the YOLOX-L model uses the ONNX model [yolox_l.onnx](https://yolox.readthedocs.io/en/latest/demo/onnx_readme.html) publicly released by [Megvii](https://yolox.readthedocs.io/en/latest/demo/onnx_readme.html). Download the model and save it in the current directory.\n", + "\n", + "\n", + "```console\n", + "$ wget https://github.com/Megvii-BaseDetection/YOLOX/releases/download/0.1.1rc0/yolox_l.onnx\n", + "```" + ] + }, + { + "cell_type": "markdown", + "id": "1020d011", + "metadata": {}, + "source": [ + "### 1.2 데이터 집합" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "addd508a", + "metadata": {}, + "source": [ + "양자화 패라미터를 결정하기 위한 캘리브레이션 데이터 집합과 성능을 측정하기 위한 테스트 데이터 집합으로 [COCO 데이터 집합](https://cocodataset.org)을 사용합니다. 2017년도 [검증 데이터 집합](http://images.cocodataset.org/zips/val2017.zip) (1 GB)과 [테스트 데이터 집합](http://images.cocodataset.org/zips/test2017.zip) (6 GB)을 내려 받습니다. 아래 `tree` 명령 출력과 같은 디렉토리 구조를 가지도록, 현재 디렉토리 아래에 `coco` 디렉토리를 만들고 다시 그 `coco` 디렉토리 속으로 내려 받은 데이터 집합 압축 파일들을 풀어 줍니다.\n", + "\n", + "Use the COCO dataset as the calibration dataset to determine quantization parameters and as the test dataset to measure performance. Download the 2017 validation dataset (1 GB) and test dataset (6 GB). Create a directory named coco under the current directory, which should have the same directory structure as the output of the tree command below, and extract the downloaded dataset archives into the coco directory:\n", + "\n", + "```console\n", + "$ mkdir coco\n", + "\n", + "$ wget http://images.cocodataset.org/zips/val2017.zip\n", + "$ unzip -d coco val2017.zip\n", + "\n", + "$ wget http://images.cocodataset.org/zips/test2017.zip\n", + "$ unzip -d coco test2017.zip\n", + "\n", + "$ tree coco\n", + "coco\n", + "├── test2017\n", + "│   ├── 000000000001.jpg\n", + "│   ├── 000000000016.jpg\n", + "│   ├── 000000000019.jpg\n", + "│ ...\n", + "│   ├── 000000581911.jpg\n", + "│   └── 000000581918.jpg\n", + "└── val2017\n", + "    ├── 000000000139.jpg\n", + "    ├── 000000000285.jpg\n", + "    ├── 000000000632.jpg\n", + " ...\n", + "    ├── 000000581615.jpg\n", + "    └── 000000581781.jpg\n", + "\n", + "```" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "1d4e27fd", + "metadata": {}, + "source": [ + "### 1.3 임포트\n", + "\n", + "### 1.3 Imports" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "ae8af1c9", + "metadata": {}, + "outputs": [], + "source": [ + "import os; os.environ[\"NPU_COMPLETION_CYCLES\"] = \"0\"" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "3c40a9c0", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "libfuriosa_hal.so --- v2.0, built @ 9928508\n" + ] + } + ], + "source": [ + "import glob\n", + "from itertools import islice\n", + "import time\n", + "\n", + "import cv2\n", + "import numpy as np\n", + "import onnx\n", + "\n", + "import furiosa.runtime.session\n", + "from furiosa.quantizer.frontend.onnx import post_training_quantize" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "87d84e50", + "metadata": {}, + "source": [ + "## 2. YOLOX-L 모델\n", + "\n", + "## 2. YOLOX-L Model" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "36725719", + "metadata": {}, + "source": [ + "YOLOX-L 모델을 메모리로 읽어 들입니다.\n", + "\n", + "Load YOLOX-L model into memory." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "670e87fa", + "metadata": {}, + "outputs": [], + "source": [ + "model = onnx.load_model(\"yolox_l.onnx\")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "f4523cc2", + "metadata": {}, + "source": [ + "해당 모델은 아래 `preproc` 함수를 사용해 전처리한 이미지 데이터 집합을 사용해 훈련되어 있습니다. `preproc` 전처리 함수가 하는 일을 구체적으로 기술하면, 2번째 줄부터 13번째 줄까지는 이미지 데이터 크기를 모델 입력 크기로 변환합니다. 원본 이미지 가로세로비를 그대로 유지한 채 확대 혹은 축소하고, 여백을 값 114을 가진 픽셀들로 채웁니다. 그 다음, 15번째 줄에서 채널(C) 축을 맨 앞으로 옮깁니다. 즉, HxWxC 형태의 데이터를 CxHxW 형태의 데이터로 변환합니다. 마지막으로 16번째 줄에서 uint8 값을 float32 값으로 변환합니다. 전체적으로, 이미지를 다루는 모델 입력에 대해 자주 사용하는 전처리 유형입니다.\n", + "\n", + "The model has been trained on a preprocessed image dataset using the preproc function below. The preproc preprocessing function specifically performs the following tasks: from the second to the thirteenth line, it resizes the image data size to the model input size while maintaining the original aspect ratio of the image by zooming in or out, and fills the margins with pixels with a value of 114. Then, on the fifteenth line, it moves the channel (C) axis to the front. In other words, it converts the data from HxWxC format to CxHxW format. Finally, on the sixteenth line, it converts the uint8 value to float32 value. Overall, this is a commonly used preprocessing type for model inputs that deal with images." + ] + }, + { + "cell_type": "markdown", + "id": "4aee6de5", + "metadata": {}, + "source": [ + "https://github.com/Megvii-BaseDetection/YOLOX/blob/68408b4083f818f50aacc29881e6f97cd19fcef2/yolox/data/data_augment.py#L142-L158" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "18886593", + "metadata": {}, + "outputs": [], + "source": [ + "def preproc(img, input_size, swap=(2, 0, 1)):\n", + " if len(img.shape) == 3: # line 2\n", + " padded_img = np.ones((input_size[0], input_size[1], 3), dtype=np.uint8) * 114\n", + " else:\n", + " padded_img = np.ones(input_size, dtype=np.uint8) * 114\n", + "\n", + " r = min(input_size[0] / img.shape[0], input_size[1] / img.shape[1])\n", + " resized_img = cv2.resize(\n", + " img,\n", + " (int(img.shape[1] * r), int(img.shape[0] * r)),\n", + " interpolation=cv2.INTER_LINEAR,\n", + " ).astype(np.uint8)\n", + " padded_img[: int(img.shape[0] * r), : int(img.shape[1] * r)] = resized_img # line 13\n", + "\n", + " padded_img = padded_img.transpose(swap) # line 15\n", + " padded_img = np.ascontiguousarray(padded_img, dtype=np.float32) # line 16\n", + " return padded_img, r" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "ead12227", + "metadata": {}, + "source": [ + "## 3. 캘리브레이션과 양자화\n", + "## 3. Calibration and Quantization" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2081f744", + "metadata": {}, + "source": [ + "양자화 패라미터를 결정하기 위해 사용할 캘리브레이션 데이터 집합을 준비합니다. 이 예에서는 빠른 시연을 위해 임의로 COCO 검증 데이터 집합에 속한 100개의 이미지를 사용합니다. 참고로, MLPerf 벤치마크는 500개 이미지를 캘리브레이션 데이터 집합으로 사용합니다. 훈련할 때 사용한 전처리(`preproc`)와 동일한 전처리를 사용하는 점에 주목합니다. 2번째 줄에서 `[np.newaxis, ...]` 부분은 CxHxW 모양의 데이터를 모델 입력이 요구하는 1xCxHxW 모양의 값으로 변환합니다.\n", + "\n", + "Prepare a calibration dataset to determine quantization parameters. In this example, for a quick demonstration, 100 random images from the COCO validation dataset are used. Note that the MLPerf benchmark uses 500 images as the calibration dataset. It is important to note that the same preprocessing (preproc) used during training is also used here. The [np.newaxis, ...] part in the second line converts the data from CxHxW shape to 1xCxHxW shape, which is required by the model input." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "390a8129", + "metadata": {}, + "outputs": [], + "source": [ + "calibration_dataset = (\n", + " {\"images\": preproc(cv2.imread(image), (640, 640))[0][np.newaxis, ...]} # line 2\n", + " for image in islice(glob.iglob(\"coco/val2017/*.jpg\"), 100)\n", + ")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "21006e47", + "metadata": {}, + "source": [ + "앞에서 준비한 YOLOX-L 모델과 캘리브레이션 데이터 집합을 인자로 `post_training_quantize` 함수를 호출하여 캘리브레이션과 양자화를 수행합니다.\n", + "\n", + " Perform calibration and quantization by calling the post_training_quantize function with the YOLOX-L model and the calibration dataset prepared earlier as arguments." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "62d5eef9", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Calibration: 100 samples [00:37, 2.66 samples/s]\n", + "Quantization: 100%|██████████| 437/437 [00:04<00:00, 95.65 operators/s] \n" + ] + } + ], + "source": [ + "model_quantized = post_training_quantize(model, calibration_dataset)\n", + "onnx.save_model(model_quantized, \"yolox_l_quantized.onnx\")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "d81a28f9", + "metadata": {}, + "source": [ + "## 4. 컴파일 최적화 옵션\n", + "\n", + "## 4. Compilation Optimization Options" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "551630b0", + "metadata": {}, + "source": [ + "퓨리오사 SDK는 사용자가 컴파일 과정의 다양한 단계를 대상 모델에 맞게 미세 조정할 수 있도록 하는 여러 옵션을 제공합니다. 그러한 옵션 중의 하나로 이미지 관련 모델에서 자주 사용하는 형태의 전처리 코드의 일부를 퓨리오사 NPU 환경 하에서 보다 효율적으로 실행할 수 있는 코드로 변환하는 옵션이 있습니다. 이 옵션을 우리 예에 적용하기 위해, 우선 전처리 함수 `preproc`에서 축의 순서를 교환하는 15번째 줄과 정숫값을 소숫값으로 변환하는 16번째 줄을 주석 처리하여 전처리에서 제외합니다.\n", + "\n", + " The Furiosa SDK provides several options that allow users to fine-tune various stages of the compilation process for the target model. One such option is to convert some of the frequently used preprocessing code in image-related models into code that can be more efficiently executed in the Furiosa NPU environment. To apply this option to our example, we first exclude the 15th line that swaps the order of axes in the preproc function and the 16th line that converts integer values to floating-point values by commenting them out." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "d9c6c887", + "metadata": {}, + "outputs": [], + "source": [ + "def preproc(img, input_size, swap=(2, 0, 1)):\n", + " if len(img.shape) == 3:\n", + " padded_img = np.ones((input_size[0], input_size[1], 3), dtype=np.uint8) * 114\n", + " else:\n", + " padded_img = np.ones(input_size, dtype=np.uint8) * 114\n", + "\n", + " r = min(input_size[0] / img.shape[0], input_size[1] / img.shape[1])\n", + " resized_img = cv2.resize(\n", + " img,\n", + " (int(img.shape[1] * r), int(img.shape[0] * r)),\n", + " interpolation=cv2.INTER_LINEAR,\n", + " ).astype(np.uint8)\n", + " padded_img[: int(img.shape[0] * r), : int(img.shape[1] * r)] = resized_img\n", + "\n", + "# padded_img = padded_img.transpose(swap) # line 15\n", + "# padded_img = np.ascontiguousarray(padded_img, dtype=np.float32) # line 16\n", + " return padded_img, r" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "10a5ed82", + "metadata": {}, + "source": [ + "그런 다음, 아래 코드처럼 1xHxWxC 입력을 1xCxHxW 입력으로 보다 효율적으로 변환하도록 하는 설정 `\"permute\": [0, 2, 3, 1]`을 지정합니다. 이렇게 컴파일러에게 전처리에 대한 정보를 제공하면, 컴파일러는 위에서 주석 처리한 코드와 동일한 계산 결과를 가지면서도 전처리 다음에 오는 실제 모델 실행 코드와 더 매끄럽게 이어져 전체 실행 시간을 단축시키는 코드를 산출할 수 있습니다.\n", + "\n", + "Then, specify the setting \"permute\": [0, 2, 3, 1] to more efficiently convert 1xHxWxC input to 1xCxHxW input as shown in the code below. By providing information about preprocessing to the compiler in this way, the compiler can produce code that smoothly follows the actual model execution code following preprocessing, with the same calculation results as the commented code above, shortening the overall execution time." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9d343aaf", + "metadata": {}, + "source": [ + "퓨리오사 SDK가 제공하는 또 다른 옵션으로 컴파일러의 [프로파일 기반 최적화(profile-guided optimization)](https://en.wikipedia.org/wiki/Profile-guided_optimization) 활성화/비활성화가 있습니다. 고수준 모델 연산을 저수준 명령어 조합으로 컴파일할 때, 다양한 조합이 가능합니다. 그런 조합 중 최적의 조합을 선택하려면 각 저수준 명령어의 실행 시간을 알아야 합니다. 그러나, 저수준 명령어의 실행 시간은 입/출력의 크기와 그 명령어의 다양한 속성 및 외부적인 요인에 의해 크게 달라지고, 정적인 분석만으로는 정확한 값을 예측하기 어렵습니다. 그래서, 퓨리오사 SDK에 내장된 컴파일러는 모델을 컴파일하여 생길 수 있는 각 저수준 명령어를 실제로 실행하여 실행 시간을 측정하는 기능을 포함하고 있습니다. 그런 저수준 명령어에 대한 프로파일 정보를 바탕으로 최적의 조합을 선택할 수 있습니다. 다만, 프로파일 기반 최적화 기능은 아직 실험적인 상태로 권고한 모델 외에는 사용하지 않는 것을 권장합니다. YOLOX-L 모델의 경우에는 프로파일 기반 최적화의 효과와 안전성을 실험을 통해 검증하였습니다. 그 기능을 활성화하기 위해 아래 코드처럼 `\"use_pdb\"`를 `True`로 설정합니다. 한편, 컴파일 시간을 절약하기 위해 주요 모델에 대한 프로파일 정보는 컴파일러에 이미 포함되어 있습니다. 그 정보를 사용할지 말지를 `\"ignore_default_pdb\"` 설정으로 지정할 수 있습니다. 이 문서에서 사용하는 YOLOX-L 모델 구현에 대한 정보는 컴파일러에 포함되어 있으므로 `\"ignore_default_pdb\"`를 `False`로 설정합니다.\n", + "\n", + "Another option provided by the Furiosa SDK compiler is enabling/disabling [profile-guided optimization](https://en.wikipedia.org/wiki/Profile-guided_optimization). When compiling high-level model operations into low-level instruction sets, there are many possible combinations. To select the optimal combination, it is necessary to know the execution time of each low-level instruction. However, the execution time of low-level instructions varies greatly depending on the size of input/output and various properties and external factors of the instructions, making it difficult to predict accurate values ​​with static analysis alone. Therefore, the FUriosa SDK compiler includes a feature that compiles the model and actually executes each low-level instruction to measure its execution time. Based on profile information on these low-level instructions, the optimal combination can be selected. However, the profile-guided optimization feature is still experimental, and it is recommended not to use it for models other than those recommended. For the YOLOX-L model, the effect and safety of profile-guided optimization have been verified through experiments. To activate this feature, set `\"use_pdb\"` to `True` as shown in the code below. On the other hand, to save compilation time, profile information for major models is already included in the compiler. You can specify whether to use this information or not with the `\"ignore_default_pdb\"` setting. Since information about the YOLOX-L model implementation used in this document is included in the compiler, set `\"ignore_default_pdb\"` to `False`." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "f4593209", + "metadata": {}, + "outputs": [], + "source": [ + "compile_config = {\n", + " \"without_quantize\": {\n", + " \"parameters\": [{\"permute\": [0, 2, 3, 1]}]\n", + " },\n", + "\n", + " \"use_pdb\": True,\n", + " \"ignore_default_pdb\": False,\n", + "}" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "d91f47ff", + "metadata": {}, + "source": [ + "## 5. 추론 및 레이턴시(latency) 측정\n", + "\n", + "## 5. Inference and Latency Measurement\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bfa81ac1", + "metadata": {}, + "source": [ + "양자화시킨 모델과 위에서 설명한 컴파일 설정을 사용해 세션을 만듭니다. 생성한 세션을 사용해 테스트 데이터 집합에 대해 추론을 합니다. 캘리브레이션할 때와 마찬가지로 빠른 시연을 위해 전체 40,670개 테스트 데이터 이미지 중 임의로 1000개 이미지를 사용합니다. 그리고, 1000개 이미지를 추론하는데 걸린 총 시간을 측정합니다.\n", + "\n", + "Create a session using the quantized model and the compile settings described above. Use the created session to perform inference on the test dataset. As with calibration, for a quick demonstration, use 1,000 randomly selected images from the entire set of 40,670 test dataset images. Then measure the total time it takes to infer the 1,000 images.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "3c03f19d", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Saving the compilation log into /home/furiosa/.local/state/furiosa/logs/compile-20230227105317-pni2dh.log\n", + "Using furiosa-compiler 0.8.0 (rev: d9f0d7728 built at 2022-11-01T03:39:34Z)\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[2m2023-02-27T01:53:18.005240Z\u001b[0m \u001b[32m INFO\u001b[0m \u001b[2mnux::npu\u001b[0m\u001b[2m:\u001b[0m Npu (npu0pe0-1) is being initialized\n", + "\u001b[2m2023-02-27T01:53:18.009343Z\u001b[0m \u001b[32m INFO\u001b[0m \u001b[2mnux\u001b[0m\u001b[2m:\u001b[0m NuxInner create with pes: [PeId(0)]\n", + "\u001b[2m2023-02-27T01:53:58.788139Z\u001b[0m \u001b[32m INFO\u001b[0m \u001b[2mnux::npu\u001b[0m\u001b[2m:\u001b[0m NPU (npu0pe0-1) has been destroyed\n", + "\u001b[2m2023-02-27T01:53:58.797826Z\u001b[0m \u001b[32m INFO\u001b[0m \u001b[2mnux::capi\u001b[0m\u001b[2m:\u001b[0m session has been destroyed\n" + ] + } + ], + "source": [ + "total_predictions = 0\n", + "elapsed_time = 0\n", + "# from inspect import signature\n", + "\n", + "# print(signature(furiosa.runtime.session.create))\n", + "\n", + "with furiosa.runtime.session.create(\"yolox_l_quantized.onnx\", compile_config=compile_config) as session:\n", + " for image in islice(glob.iglob(\"coco/test2017/*.jpg\"), 1000):\n", + " inputs = [preproc(cv2.imread(image), (640, 640))[0][np.newaxis, ...]]\n", + " start = time.perf_counter_ns()\n", + " outputs = session.run(inputs)\n", + " elapsed_time += time.perf_counter_ns() - start\n", + " total_predictions += 1" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "55234d45", + "metadata": {}, + "source": [ + "1000개 이미지를 추론하는데 걸린 시간으로부터 평균 레이턴시(latency)를 계산합니다. 퓨리오사 워보이(리비전 A0)와 인텔 코어 i7-11700K 프로세서를 장착한 머신에서 12 ms 대의 평균 레이턴시가 측정되었습니다.\n", + "\n", + "Calculate the average latency from the time it took to infer 1000 images. An average latency of around 12 ms was measured on a machine equipped with Purios Warboy (revision A0) and Intel Core i7-11700K processor.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "4fade41d", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Average Latency: 22.998588863000002 ms\n" + ] + } + ], + "source": [ + "latency = elapsed_time / total_predictions\n", + "print(f\"Average Latency: {latency / 1_000_000} ms\")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "d74d2cff", + "metadata": {}, + "source": [ + "(주의: 주피터 노트북 상에서 시간을 측정하면 측정 오차가 상당히 클 수 있습니다. [`nbconvert`](https://nbconvert.readthedocs.io/)를 사용해 주피터 노트북으로부터 파이썬 스크립트를 추출하고, 그 파이썬 스크립트를 실행해 시간을 측정하면 보다 안정적인 측정값을 얻을 수 있습니다.)\n", + "\n", + "(Note: Measuring time in Jupyter Notebook can have significant measurement errors. You can get more stable measurement values by using [nbconvert](https://nbconvert.readthedocs.io/) to extract a Python script from the Jupyter Notebook and measuring time by executing that Python script.)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "yolo", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.15" + }, + "vscode": { + "interpreter": { + "hash": "27f1d0af40e0477ca66cb17053abc7cb583fa2622783b0b6487e3991c4046070" + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +}