Ubuntu 실습(7)

zeusqoi·2026년 9월 9일

개인공부

목록 보기
14/42

참고 자료: https://github.com/intel/ai-reference-models/blob/pytorch-r2.2-models/docs/object_detection/tensorflow_serving/Tutorial.md

이번 실습에서는 Intel의 AI Reference Models에서 제공하는 Object Detection with TensorFlow Serving on CPU 튜토리얼을 따라가며,

  • COCO 데이터셋 준비
  • SSD-MobileNet Object Detection 모델 준비
  • TensorFlow Object Detection API 환경 구성
  • TensorFlow Serving으로 모델 배포
  • REST API를 이용한 추론
  • Jupyter Notebook을 이용한 Object Detection 결과 시각화
  • Batch Size에 따른 성능 측정

까지 진행해 보았다.

실습 환경

  • Ubuntu 22.04.5 LTS
  • CPU: Intel Core i7-14700
  • Python 3.10
  • Docker 29.7.2
  • TensorFlow Serving
  • SSD-MobileNet
  • COCO 2017 Validation Dataset
  • Jupyter Notebook

1. 학습 목표

이번 튜토리얼의 목표는 단순히 Object Detection 모델을 실행하는 것이 아니다.

TensorFlow Serving을 이용해서 Object Detection 모델을 서버에 올리고, 클라이언트에서 이미지를 전달하여 추론한 뒤 결과를 확인하는 전체 과정을 실습한다.

특히 성능 측면에서는 두 가지를 비교한다.

Online Inference

batch_size = 1

이미지를 한 장씩 서버에 전달하는 방식이다.

이 경우에는 Latency가 낮을수록 좋은 성능이라고 볼 수 있다.

Batch Inference

batch_size > 1

여러 장의 이미지를 한 번에 서버에 전달한다.

이 경우에는 Throughput이 높을수록 좋은 성능이라고 볼 수 있다.


2. COCO 2017 Validation Dataset 다운로드

먼저 Object Detection 테스트에 사용할 COCO 2017 Validation Dataset을 다운로드했다.

튜토리얼에서는 약 780MB 정도의 데이터를 사용하며, TFRecord 형식으로 변환하지 않고 원본 이미지를 그대로 사용한다.

cd ~
mkdir -p coco/val
wget http://images.cocodataset.org/zips/val2017.zip
unzip val2017.zip -d coco/val

그리고 환경 변수도 설정했다.

export COCO_VAL_DATA=$(pwd)/coco/val/val2017

확인을 위해:

ls ~/coco/val/val2017 | head

실행하면 다음과 같이 COCO 이미지들이 존재하는 것을 확인할 수 있다.

실제로 하나의 이미지도 확인해 보았다.

from PIL import Image
import numpy as np

img = np.array(
    Image.open(
        '~/coco/val/val2017/000000000139.jpg'
    )
)

print(img.shape)
print(img.dtype)

결과:

(426, 640, 3)
uint8

3. Intel Model Zoo 다운로드

다음으로 Intel Model Zoo를 다운로드했다.

cd ~
git clone https://github.com/IntelAI/models.git

그리고 Object Detection 모델로 SSD-MobileNet을 선택했다.

튜토리얼에서 제공하는 SSD-MobileNet 모델을 다운로드했다.

wget http://download.tensorflow.org/models/object_detection/ssd_mobilenet_v1_coco_2018_01_28.tar.gz

압축을 해제한다.

tar -xzvf ssd_mobilenet_v1_coco_2018_01_28.tar.gz

TensorFlow Serving에서 사용할 SavedModel을 다음 위치에 준비했다.

mkdir -p ~/obj_detection/1

그리고

cp ~/ssd_mobilenet_v1_coco_2018_01_28/saved_model/saved_model.pb \
~/obj_detection/1/

여기서 1이라는 디렉터리가 중요한데, TensorFlow Serving에서는 이 숫자를 모델 버전으로 사용하기 때문이다.

최종적으로 다음과 같은 구조가 되었다.

obj_detection/
└── 1/
    └── saved_model.pb

모델 이름도 지정했다.

export model_name=ssd-mobilenet

4. Python 가상환경 구성

튜토리얼에서는 Object Detection에 필요한 Python 패키지를 별도의 가상환경에 설치하도록 되어 있다.

현재 Ubuntu 환경의 기본 Python 버전을 확인했다.

python3 --version

그런데 현재 Python이 3.14 버전이었기 때문에, 기존 튜토리얼과의 호환성을 고려하여 Python 3.10을 사용했다.

python3.10 --version

결과:

Python 3.10.12

가상환경을 만들려고 했는데 처음에는 venv 관련 패키지가 없어서 오류가 발생했다.

그래서 다음 패키지를 설치했다.

sudo apt install -y python3.10-venv

이후 다시 가상환경을 생성했다.

python3.10 -m venv ~/od_venv

활성화는 다음과 같이 한다.

source ~/od_venv/bin/activate

터미널에 다음과 같이 표시되면 활성화된 것이다.

od_venv

5. protoc이 없는 문제

TensorFlow Object Detection API를 사용하기 위해 protobuf 파일을 Python 코드로 변환해야 한다.

튜토리얼에서는 protoc을 사용한다. 실제 튜토리얼에서도 protobuf compiler를 설치한 후 .proto 파일을 컴파일하도록 되어 있다.

먼저 확인했다.

protoc --version

그런데

zsh: command not found: protoc

가 발생했다.

처음에는 Ubuntu 패키지를 이용해서 설치하려고 했다.

sudo apt update && sudo apt install -y protobuf-compiler

하지만 현재 환경에서는

E: protobuf-compiler 패키지를 찾을 수 없습니다

가 발생했다.

apt-cache search protobuf-compiler에서도 원하는 패키지를 확인할 수 없었다.

그래서 튜토리얼에 있는 방식처럼 protoc 바이너리를 직접 사용하는 방향으로 진행했다.


6. TensorFlow Models Repository 준비

TensorFlow Object Detection API를 사용하기 위해 TensorFlow Models Repository를 clone했다.

cd ~
git clone https://github.com/tensorflow/models tensorflow-models

그리고 환경 변수를 설정했다.

export TF_MODELS_ROOT=$(pwd)/tensorflow-models

또한 Python에서 Object Detection API를 찾을 수 있도록 PYTHONPATH도 추가했다.

export PYTHONPATH=$PYTHONPATH:$(pwd):$(pwd)/slim

7. protobuf 컴파일

object_detection/protos에는 다양한 .proto 파일들이 존재한다.

확인해 보면:

ls ~/tensorflow-models/research/object_detection/protos | head

다음과 같이 여러 파일이 존재한다.

__init__.py
anchor_generator.proto
anchor_generator_pb2.py
argmax_matcher.proto
argmax_matcher_pb2.py
bipartite_matcher.proto
...

여기서 중요한 것은 .proto 파일을 Python에서 사용할 수 있도록 _pb2.py 파일로 변환하는 것이다.

튜토리얼의 기본 과정은 다음과 같다.

./bin/protoc object_detection/protos/*.proto --python_out=.

이 과정을 거치면서 Object Detection API에서 사용하는 protobuf Python 코드들이 생성된다.


8. protobuf 버전 문제

그런데 여기서 또 문제가 발생했다.

Jupyter Notebook에서 다음과 같은 오류가 발생했다.

TypeError: Descriptors cannot be created directly.

그리고 오류 메시지에서

your generated code is out of date

라는 내용이 나타났다.

확인해 보니 현재 설치되어 있던 protobuf 버전이 너무 최신이었다.

그래서 임시 호환을 위해 다음과 같이 protobuf를 낮췄다.

source ~/od_venv/bin/activate
pip install protobuf==3.20.3

설치 과정에서:

tensorflow 2.21.0 requires protobuf<8.0.0,>=6.31.1
but you have protobuf 3.20.3

라는 dependency conflict 경고도 발생했다.

즉, 현재 TensorFlow 2.21 환경과 오래된 Object Detection API 코드 사이에서 버전 호환 문제가 발생한 것이다.

그래도 해당 튜토리얼의 오래된 generated protobuf 코드와 호환시키기 위해 3.20.3을 사용했다.

이후에도 protobuf 관련 환경을 맞추기 위해

pip install grpcio-tools

도 설치했다.


9. Jupyter Notebook 실행

이제 튜토리얼에서 제공하는 ObjectDetection.ipynb를 실행했다.

source ~/od_venv/bin/activate
cd ~/models/docs/notebooks
jupyter notebook

브라우저에서 Jupyter Notebook을 열고

ObjectDetection.ipynb

파일을 실행했다.


10. Notebook Setup 코드

Notebook의 Setup 부분에서는 다음과 같은 값을 설정한다.

HOST = 'localhost'
MODEL = 'ssd-mobilenet'
PROTOCOL = 'rest'

IMAGES_PATH = os.environ['COCO_VAL_DATA']
TF_MODELS_PATH = os.environ['TF_MODELS_ROOT']

LABELS_PATH = os.path.join(
    TF_MODELS_PATH,
    'research',
    'object_detection',
    'data',
    'mscoco_label_map.pbtxt'
)

각 변수의 의미는 다음과 같다.

HOST

HOST = 'localhost'

TensorFlow Serving이 같은 PC에서 실행되고 있기 때문에 localhost를 사용한다.

MODEL

MODEL = 'ssd-mobilenet'

TensorFlow Serving에 등록한 모델 이름이다.

PROTOCOL

PROTOCOL = 'rest'

이번 실습에서는 REST API를 사용했다.

원래 Notebook은 REST와 gRPC를 모두 지원하도록 작성되어 있다.

IMAGES_PATH

IMAGES_PATH = os.environ['COCO_VAL_DATA']

COCO Validation 이미지가 저장된 경로다.

TF_MODELS_PATH

TF_MODELS_PATH = os.environ['TF_MODELS_ROOT']

TensorFlow Models Repository의 경로다.


11. COCO Label Map

Object Detection 결과를 단순히 숫자로 출력하는 것보다

72
62
64

같은 class ID를

giraffe
person
...

과 같은 실제 객체 이름으로 변환하는 것이 훨씬 보기 좋다.

그래서 다음 코드가 사용된다.

label_map = label_map_util.load_labelmap(LABELS_PATH)

categories = label_map_util.convert_label_map_to_categories(
    label_map,
    max_num_classes=90,
    use_display_name=True
)

CATEGORY_INDEX = label_map_util.create_category_index(categories)

여기서 CATEGORY_INDEX는

class ID → 객체 이름

으로 변환할 수 있도록 만들어 주는 dictionary라고 이해하면 된다.


12. REST API 설정

처음에는 Notebook이 기본적으로 gRPC를 사용하도록 되어 있었다.

PROTOCOL = 'grpc'

하지만 이번 실습에서는 REST를 사용하기로 했다.

따라서 다음과 같이 변경했다.

PROTOCOL = 'rest'

그리고 REST 서버 주소를 설정했다.

SERVER_URL = 'http://{}:8501/v1/models/{}:predict'.format(
    HOST,
    MODEL
)

실제로 출력하면:

rest
http://localhost:8501/v1/models/ssd-mobilenet:predict

TensorFlow Serving의 REST API를 통해 다음 주소로 요청하게 된다.

http://localhost:8501/v1/models/ssd-mobilenet:predict

13. tensorflow_serving 모듈 오류

Notebook에서 다음 오류가 발생했다.

ModuleNotFoundError: No module named 'tensorflow_serving'

그래서 처음에는

pip install tensorflow-serving-api==2.21.0

을 시도했다.

하지만:

ERROR: No matching distribution found for tensorflow-serving-api==2.21.0

가 발생했다.

즉, 현재 환경에서 튜토리얼이 기대하는 정확한 버전의 tensorflow-serving-api 패키지를 설치할 수 없었다.

그래서 이번 실습에서는 gRPC 대신 REST 방식으로 진행했다.

REST 방식에서는 Python의 requests 라이브러리를 사용하여 HTTP 요청을 보내면 되기 때문에 gRPC 전용 tensorflow_serving Python 모듈에 의존하지 않아도 된다.


14. TensorFlow Serving 실행

이제 실제 Object Detection 모델을 TensorFlow Serving에 올렸다.

처음에는 튜토리얼에 있는 Intel 최적화 이미지를 사용했다.

intel/intel-optimized-tensorflow-serving:latest

그리고 CPU의 물리 코어 수도 계산했다.

cores_per_socket=`lscpu | grep "Core(s) per socket" | cut -d':' -f2 | xargs`
num_sockets=`lscpu | grep "Socket(s)" | cut -d':' -f2 | xargs`
num_physical_cores=$((cores_per_socket * num_sockets))

echo $num_physical_cores

내 CPU에서는:

20

이 나왔다.


15. Intel Optimized TensorFlow Serving 실행 실패

그런데 Intel 최적화 TensorFlow Serving 이미지를 실행했을 때 문제가 발생했다.

컨테이너 자체는 생성되었지만 실행 직후 종료되었고 로그에서:

Illegal instruction (core dumped)

가 발생했다.

컨테이너 상태도:

Exited (132)

로 나타났다.

즉, 해당 Docker 이미지가 현재 CPU 환경에서 실행되지 않는 문제가 있었다.

결국 이 이미지를 제거하고 일반 TensorFlow Serving 이미지를 사용했다.

tensorflow/serving:latest

16. 일반 TensorFlow Serving으로 변경

다음과 같이 실행했다.

docker run \
  --name=tfserving \
  -d \
  -p 8500:8500 \
  -p 8501:8501 \
  -v "$HOME/obj_detection:/models/ssd-mobilenet" \
  -e MODEL_NAME=ssd-mobilenet \
  tensorflow/serving:latest

컨테이너가 정상적으로 실행되었고 로그에서 모델이 로드된 것을 확인할 수 있었다.

Successfully loaded servable version
{name: ssd-mobilenet version: 1}

그리고 TensorFlow Serving은

gRPC → 8500
REST → 8501

포트를 사용한다.


17. 모델 상태 확인

REST API를 통해 모델 상태를 확인했다.

curl http://127.0.0.1:8501/v1/models/ssd-mobilenet

결과에서:

version: 1
state: AVAILABLE

를 확인했다.

즉, SSD-MobileNet SavedModel이 TensorFlow Serving에 정상적으로 로드되었다.


18. 모델 Metadata 확인

다음으로 모델의 입력과 출력 구조를 확인했다.

curl http://127.0.0.1:8501/v1/models/ssd-mobilenet/metadata

입력은:

image_tensor:0
DT_UINT8
[-1, -1, -1, 3]

이었다.

즉,

batch
height
width
RGB

형태의 이미지를 입력으로 받는다.

출력은 다음과 같다.

detection_scores
detection_boxes
num_detections
detection_classes

Object Detection 모델답게 bounding box, class, confidence score 등의 결과를 반환한다.


19. 첫 번째 REST 요청

COCO 이미지 하나를 NumPy 배열로 변환했다.

img = np.array(
    Image.open(
        '$HOME/coco/val/val2017/000000000139.jpg'
    )
)

그리고 JSON 형식으로 변환했다.

{
    "instances": [
        ...
    ]
}

TensorFlow Serving REST API로 요청했다.

curl -X POST \
  http://127.0.0.1:8501/v1/models/ssd-mobilenet:predict \
  -H "Content-Type: application/json" \
  -d @$HOME/request_od.json

정상적으로 Object Detection 결과가 반환되었다.

예를 들어:

num_detections: 5

와 함께 confidence score와 class ID, bounding box가 반환되었다.

즉, TensorFlow Serving을 통해 SSD-MobileNet Object Detection 추론이 정상적으로 수행된 것이다.


20. Jupyter Notebook에서 Object Detection

Notebook의 Test Object Detection 부분에서는 COCO 데이터셋에서 랜덤 이미지를 하나 가져온다.

batch_size = 1

np_image = get_random_image(IMAGES_PATH)

get_random_image() 함수에서는 랜덤하게 이미지를 선택한 뒤 NumPy 배열로 변환한다.

image_path = os.path.join(
    image_dir,
    random.choice(os.listdir(image_dir))
)

image = Image.open(image_path)

그리고 이미지 데이터를:

np.uint8

형태의 NumPy 배열로 변환한다.


21. REST 요청 코드

REST 방식에서는 다음과 같은 형태로 요청을 만든다.

predict_request = {
    "instances": np.expand_dims(np_image, 0).tolist()
}

result = requests.post(
    SERVER_URL,
    json=predict_request
)

여기서 중요한 부분이:

np.expand_dims(np_image, 0)

이다.

원래 이미지가:

(height, width, 3)

이라면 batch 차원을 추가해서:

(1, height, width, 3)

으로 만들어야 한다.

TensorFlow Serving의 Object Detection 모델 입력이 4차원이기 때문이다.


22. 여기서 발생한 이미지 차원 오류

실습 중 이 부분에서 한 번 오류가 발생했다.

input must be 4-dimensional

처음에는 이미지 배열이:

(480, 640, 3)

형태였는데, 요청 과정에서 batch 차원이 제대로 추가되지 않은 상태였다.

Object Detection 모델은 이미지를 Batch 차원을 포함한 4차원 형태로 입력받기 때문에, np.expand_dims()를 사용해 첫 번째 위치에 batch 차원을 추가했다.

np.expand_dims(np_image, 0)

그 결과:

(480, 640, 3)
        ↓
(1, 480, 640, 3)

형태로 변환할 수 있었다.

확인을 위해:

print(np_image.ndim)
print(np_image.shape)

를 실행했고, 이후에는:

4
(1, 640, 429, 3)

와 같이 4차원 형태로 입력되는 것을 확인했다.

실제 이미지의 높이와 너비는 이미지에 따라 달라질 수 있지만, 여기서 중요한 것은 Batch 차원이 추가되어 4차원 형태가 되었다는 점이다.

이번 오류를 통해 Object Detection 모델에 이미지를 전달할 때 단순히 이미지 배열만 전달하는 것이 아니라, 모델이 요구하는 입력 Tensor의 차원까지 맞춰줘야 한다는 점을 확인할 수 있었다.


23. 두 번째 오류 KeyError: predictions

요청 자체가 실패했는데 바로:

result.json()['predictions'][0]

을 사용하면서 다음 오류도 발생했다.

KeyError: 'predictions'

처음에는 predictions가 없어서 문제가 생긴 것처럼 보였지만, 실제 원인은 그 앞의 HTTP 요청이 실패했기 때문이었다.

그래서 먼저:

print(result.status_code)
print(result.text[:1000])

를 출력해서 실제 서버 응답을 확인했다.

결과:

400
{
    "error": "input must be 4-dimensional ..."
}

이렇게 확인하면서

KeyError가 진짜 원인이 아니라, 서버가 400 Bad Request를 반환했기 때문에 predictions 필드가 존재하지 않았던 것

이라는 것을 알 수 있었다.

이런 식으로 에러가 발생했을 때 traceback의 마지막 줄만 보는 것이 아니라 HTTP status code와 response body까지 확인하는 것이 중요하다는 것을 배웠다.


24. Jupyter에서 Object Detection 결과 확인

입력 차원을 수정한 후 다시 실행하니 정상적으로 결과가 나왔다.

실제 Notebook에서는 COCO 이미지 위에 bounding box가 표시되었다.

예를 들어:

giraffe: 94%
giraffe: 87%

처럼 객체의 종류와 confidence score가 표시되었다.

다른 이미지에서는:

person: 97%

처럼 사람을 검출하는 것도 확인할 수 있었다.

즉, 단순히 TensorFlow Serving API가 응답하는 것뿐만 아니라 실제 이미지에 Detection 결과를 시각적으로 표시하는 것까지 성공했다.


25. Pillow DeprecationWarning

실습 중 다음 경고도 계속 나타났다.

DeprecationWarning:
Image.Image.getdata is deprecated
and will be removed in Pillow 14

문제가 발생한 코드는:

image.getdata()

부분이었다.

하지만 이것은 현재 실습을 중단시키는 오류가 아니라 향후 Pillow 버전에서 해당 API가 제거될 예정이라는 경고였다.

따라서 Object Detection 자체는 정상적으로 수행되었다.


26. 성능 측정

마지막으로 Notebook의 Measure Performance 부분을 실행했다.

26-1. Real-time Inference

먼저:

benchmark()

를 실행했다.

이 경우 기본적으로:

batch_size = 1

이다.

결과:

Iteration 1: 0.031 sec
Iteration 2: 0.031 sec
Iteration 3: 0.030 sec
...
Iteration 10: 0.026 sec

Average time: 0.031 sec
Batch size = 1
Latency: 30.952 ms
Throughput: 32.308 images/sec

평균 요청 시간은 약:

0.031초

즉:

약 31ms

이다.

따라서 이미지 한 장을 요청했을 때 약 31ms 정도의 latency가 측정되었다.

Throughput은:

32.308 images/sec

으로 측정되었다.

즉, 현재 환경에서 이 테스트 조건으로 약 1초에 32장의 이미지를 처리할 수 있는 수준이었다.


27. Batch Inference

다음으로:

benchmark(batch_size=128)

을 실행했다.

결과:

Iteration 1: 4.400 sec
Iteration 2: 3.866 sec
Iteration 3: 4.156 sec
Iteration 4: 3.883 sec
Iteration 5: 2.833 sec
Iteration 6: 2.816 sec
Iteration 7: 3.675 sec
Iteration 8: 4.023 sec
Iteration 9: 2.115 sec
Iteration 10: 2.500 sec

Average time: 3.250 sec
Batch size = 128
Throughput: 39.381 images/sec

여기서는 한 번에 128장의 이미지를 처리한다.

전체 요청 하나를 처리하는 데는 평균:

3.250초

가 걸렸다.

하지만 128장을 한 번에 처리했기 때문에:

128 / 3.250
≈ 39.381

이 되어 Throughput은:

39.381 images/sec

가 되었다.


28. Batch Size 1 vs 128

결과를 정리하면 다음과 같다.

항목Batch Size 1Batch Size 128
Average Time0.031 sec3.250 sec
Latency30.952 ms-
Throughput32.308 images/sec39.381 images/sec

여기서 중요한 것은 단순히

"Batch 128이 더 빠르다."

라고 해석하면 안 된다는 것이다.

Batch 128은 한 번의 요청을 처리하는 데는 약 3.25초가 걸렸다.

하지만 한 번에 128장을 처리했기 때문에 전체 처리량은 더 높아졌다.

즉,

Batch 1
→ 한 요청의 응답 시간을 줄이는 데 유리
→ 낮은 Latency

Batch 128
→ 많은 이미지를 한꺼번에 처리
→ 높은 Throughput

이라는 차이가 있다.

이번 실습에서는:

32.308 images/sec
        ↓
39.381 images/sec

으로 Throughput이 증가했다.

약 21.9% 정도 증가한 결과이다.


29. 이번 실습에서 가장 많이 헤맸던 부분

이번 실습은 이전 TensorFlow Serving 실습보다 환경적인 문제가 많았다.

protoc 명령어가 없음

zsh: command not found: protoc

→ protobuf compiler 문제 발생

protobuf 버전 호환 문제

TypeError: Descriptors cannot be created directly.

→ 오래된 Object Detection API의 generated protobuf 코드와 현재 protobuf 버전 사이의 호환성 문제

→ protobuf==3.20.3으로 변경

TensorFlow Serving API 버전 문제

No matching distribution found for tensorflow-serving-api==2.21.0

→ 튜토리얼에서 사용하는 오래된 환경과 현재 Python/TensorFlow 패키지 환경의 차이

→ gRPC 대신 REST 방식으로 진행

Intel Optimized TensorFlow Serving 실행 실패

Illegal instruction (core dumped)

→ 튜토리얼의 Intel 최적화 Docker 이미지가 현재 환경에서 정상 실행되지 않음

→ 일반:

tensorflow/serving:latest

로 변경

Docker 컨테이너 이름 충돌

이미 tfserving이라는 이름의 컨테이너가 존재해서:

Conflict. The container name "/tfserving" is already in use

오류가 발생했다.

기존 컨테이너를 확인하고 정리한 후 다시 실행했다.

SERVER_URL이 정의되지 않음

Notebook Kernel을 재시작하면서 기존 변수들이 초기화되어:

NameError: name 'SERVER_URL' is not defined

가 발생했다.

Kernel을 재시작하면 이전에 실행했던 셀의 변수와 import가 사라진다는 것을 다시 확인할 수 있었다.

그래서 Setup 셀부터 다시 실행해야 했다.

requests가 정의되지 않음

또한:

NameError: name 'requests' is not defined

가 발생했다.

REST 요청을 위해:

import requests

를 다시 실행했다.

서버가 실행되지 않은 상태

Notebook에서 REST 요청을 보냈을 때:

ConnectionRefusedError

가 발생했다.

확인해 보니 TensorFlow Serving Docker 컨테이너가 실행 중이지 않았다.

docker ps

로 컨테이너 상태를 확인한 뒤 TensorFlow Serving을 다시 실행했다.

predictions KeyError

KeyError: 'predictions'

이 발생했지만 실제 원인은 predictions가 없는 것이 아니었다.

HTTP 응답을 확인해 보니:

400

이었고 실제 서버 오류는:

input must be 4-dimensional

이었다.

그래서 입력 이미지에 batch 차원을 추가했다.

np.expand_dims(np_image, 0)

30. 튜토리얼과 실제 환경의 차이

원본 튜토리얼은 Ubuntu 18.04를 기준으로 작성된 부분이 있으며, 현재 Intel AI Reference Models 저장소 자체도 2026년 3월 4일 archive 처리되어 현재는 read-only 상태이다.

특히 원본 튜토리얼에서 사용하는 benchmark 경로:

models/benchmarks/object_detection/tensorflow_serving/

가 현재 내려받은 models 저장소에서는 존재하지 않았다.

그래서 해당 benchmark script를 억지로 찾거나 오래된 의존성을 추가로 설치하는 대신, Jupyter Notebook의 Measure Performance 코드를 이용해서 현재 TensorFlow Serving 환경에서 직접 성능을 측정하는 방식으로 실습을 진행했다.


마무리

이번 실습의 전체적인 흐름을 정리하면 다음과 같다.

COCO 2017 Dataset
        │
        ▼
SSD-MobileNet SavedModel
        │
        ▼
TensorFlow Serving
        │
        ├── REST : 8501
        │
        └── gRPC : 8500
        │
        ▼
Python / Jupyter Notebook
        │
        ├── Image 입력
        │
        ├── Object Detection 요청
        │
        ▼
Detection 결과
        │
        ├── detection_boxes
        ├── detection_classes
        ├── detection_scores
        └── num_detections
        │
        ▼
Bounding Box 시각화

그리고 성능 측면에서는:

Batch Size = 1
→ Latency 측정
→ 30.952 ms

Batch Size = 128
→ Throughput 측정
→ 39.381 images/sec

으로 확인했다.

0개의 댓글