이전 글에서는 "저시력자를 위한 실내 근거리 물체 탐지 및 알림 시스템" 프로젝트에서 YOLO 모델 재학습을 위한 데이터 전처리 과정 설명했습니다.
[인공지능 프로젝트] 저시력자를 위한 실내 근거리 물체 탐지 및 알림 시스템 - 2편 (YOLOv12n-seg 데
이전 글에서는 "저시력자를 위한 실내 근거리 물체 탐지 및 알림 시스템" 프로젝트에 대한 개요를 설명했습니다.https://whitecode2718.tistory.com/158 [인공지능 프로젝트] 저시력자를 위한 실내 근거리
whitecode2718.tistory.com
본 글에서는 프로젝트에서 진행한 YOLO의 학습 과정과 결과 해석에 대해 구체적으로 소개하겠습니다.
📌 깃허브 링크: https://github.com/lko9911/Artificial-Intelligence-Project
GitHub - lko9911/Artificial-Intelligence-Project: 강원대학교 인공지능 수업_프로젝트
강원대학교 인공지능 수업_프로젝트. Contribute to lko9911/Artificial-Intelligence-Project development by creating an account on GitHub.
github.com
Artificial-Intelligence-Project/Prototype/YOLO/yolo_seg(인공지능).ipynb at main · lko9911/Artificial-Intelligence-Project
강원대학교 인공지능 수업_프로젝트. Contribute to lko9911/Artificial-Intelligence-Project development by creating an account on GitHub.
github.com
1. YOLOv11n-seg 모델 재학습 (파인 튜닝)
yolov11n-seg 모델을 프로젝트에 사용하기 위해 파인튜닝을 해야하고, 이를 위해 이전 글에서 만든 데이터셋을 재학습시킵니다. 우선 데이터셋을 구글 드라이브에 업로드하였습니다.

참고) 본 글에서는 딥러닝 모델을 "재학습"했다는 표현을 썼지만, 엄밀히 말하면 재학습이 아닌 기존의 모델을 불러와 프로젝트에 맞게 다시 학습시킨 "전이학습" 및 "파인튜닝"을 했습니다. 재학습이라는 표현은 단순히 "프로젝트 데이터셋을 이용해 다시 학습했다"는 의미로 사용했기 때문에, 딥러닝 모델 논문에서 사용하는 "재학습"이라는 의미는 아닙니다.
yolo 모델의 재학습은 GPU 사용을 위해 구글 코랩에서 진행하였습니다. 만약 그래픽카드를 별도로 사용하는 컴퓨터가 있다면, 로컬 환경에서 학습시켜도 무방합니다. (팀원 중 한분이 로컬로 테스트했습니다.)
사용한 코드는 다음과 같습니다.
Artificial-Intelligence-Project/Prototype/YOLO/yolo_seg(인공지능).ipynb at main · lko9911/Artificial-Intelligence-Project
강원대학교 인공지능 수업_프로젝트. Contribute to lko9911/Artificial-Intelligence-Project development by creating an account on GitHub.
github.com
코드에 대해서 간략하게 설명하겠습니다.
# 울트라틱스사의 라이브러리 설치
!pip install ultralytics
# 구글 드라이브 마운트
from google.colab import drive
drive.mount('/content/drive')
from ultralytics import YOLO
import os
import cv2
import numpy as np
from glob import glob
# ⚙️ 데이터셋 yaml 정의 (64개 클래스로 업데이트됨)
data_yaml = """\
# ------------------------------------------------------------------
# YOLOv11 (Ultralytics) 데이터셋 구성 파일
# ------------------------------------------------------------------
path: /content/drive/MyDrive/Dataset_NYU
train: train/images
val: val/images
# ------------------------------------------------------------------
# 클래스 정의 (총 64개)
# 이 순서는 TXT 파일의 숫자 ID (0부터 63)와 정확히 일치합니다.
# ------------------------------------------------------------------
nc: 64
names:
0: airduct
1: airvent
2: bag
3: ball
4: bar
5: basket
6: book
7: bottle
8: bowl
9: box
10: cabinet
11: camera
12: ceiling
13: chair
14: clock
15: computer
16: cone
17: corkboard
18: counter
19: cup
20: desk
21: dishwasher
22: door
23: doorknob
24: faucet
25: fireextinguisher
26: floor
27: garbagebin
28: greenscreen
29: holepuncher
30: keyboard
31: ladder
32: laptop
33: light
34: magnet
35: manillaenvelope
36: mantel
37: microwave
38: monitor
39: motioncamera
40: paper
41: papertoweldispenser
42: picture
43: pipe
44: pot
45: projectorscreen
46: refridgerator
47: scissor
48: shelves
49: sink
50: speaker
51: stackedchairs
52: stand
53: stoveburner
54: styrofoamobject
55: table
56: tapedispenser
57: telephone
58: telephonecord
59: tracklight
60: unknown
61: wall
62: whiteboard
63: window
"""
yaml_path = "nyu_seg_data.yaml"
with open(yaml_path, 'w') as f:
f.write(data_yaml)
이 코드는 YOLO 모델을 학습시키기 위한 yaml 데이터를 생성합니다. yaml 파일은 단순히 yolo 모델에게 "이러한 정보를 학습시킬거야 ! " 라고 알려주는 역할을 합니다. 형식은 위에 적힌 바와 같습니다.
import os
import shutil
from glob import glob
import random
from tqdm import tqdm # tqdm 추가
DATASET_ROOT = '/content/drive/MyDrive/Dataset_NYU'
SRC_IMAGES = os.path.join(DATASET_ROOT, 'images')
SRC_LABELS = os.path.join(DATASET_ROOT, 'labels')
SPLITS = ['train', 'val', 'test']
for split in SPLITS:
os.makedirs(os.path.join(DATASET_ROOT, split, 'images'), exist_ok=True)
os.makedirs(os.path.join(DATASET_ROOT, split, 'labels'), exist_ok=True)
# 모든 이미지 확장자 포함
image_paths = sorted(glob(os.path.join(SRC_IMAGES, '*.*'))) # png, jpg, jpeg 등
random.shuffle(image_paths)
num_total = len(image_paths)
num_train = int(0.7 * num_total)
num_val = int(0.2 * num_total)
num_test = num_total - num_train - num_val
splits = {
'train': image_paths[:num_train],
'val': image_paths[num_train:num_train+num_val],
'test': image_paths[num_train+num_val:]
}
for split, paths in splits.items():
print(f"\n[{split.upper()}] 진행 중... ({len(paths)}개)")
for img_path in tqdm(paths):
img_name = os.path.basename(img_path)
name_wo_ext = os.path.splitext(img_name)[0]
# 이미지 복사
shutil.copy(img_path, os.path.join(DATASET_ROOT, split, 'images', img_name))
# 라벨 복사
label_path = os.path.join(SRC_LABELS, f"{name_wo_ext}.txt")
if os.path.exists(label_path):
shutil.copy(label_path, os.path.join(DATASET_ROOT, split, 'labels', f"{name_wo_ext}.txt"))
print("\n데이터셋 복사 완료!")
이전에 데이터셋을 images / labels로 나누었는데, 데이터셋 전체를 7:1:1 비율로 train/ test/ val 폴더에 분배합니다. (정확히는 파일을 복사합니다.) tqdm 라이브러리는 코드의 실행 과정을 시각화 하기 위한 용도라 안써도 상관 없습니다.
# ⚙️ 모델 로드 및 학습
model = YOLO('yolo11n-seg.pt') # pretrained model
model.train(
data=yaml_path, # yaml 파일 경로
epochs=100,
imgsz=512,
batch=32,
)
그 다음 yolo 모델을 불러와 하이퍼 파라미터를 설정한 후 학습시킵니다. 테스트용으로 에포크가 100 정도 설정돼 있지만, 실제로는 300으로 설정했습니다.
import os
import shutil
from google.colab import drive
# Google 드라이브 마운트
drive.mount('/content/drive')
# 원본 경로
source_path = '/content/runs'
# 목적지 경로
destination_path = '/content/drive/MyDrive/yolo_seg_project'
# 목적지 경로가 없으면 생성
if not os.path.exists(destination_path):
os.makedirs(destination_path)
# source_path 경로에 있는 모든 파일과 폴더를 이동
for filename in os.listdir(source_path):
file_path = os.path.join(source_path, filename)
if os.path.isfile(file_path) or os.path.isdir(file_path):
shutil.move(file_path, destination_path)
학습이 완료되면 runs 파일 안에 모델의 학습 내용과 모델 파일이 생성되는데, 이 내용을 구글 드라이브에 저장하는 코드입니다.
2. YOLOv11n-seg 모델 재학습 결과 해석
구글 드라이브에 저장된 runs/segment/train 파일 보면, weights 폴더 안에 last.pt, best.pt 가 있습니다. last.pt는 가장 마지막 에포크의 학습 결과의 yolo 모델 파일이고, best.pt는 학습중 가장 성능이 좋았던, 검증셋의 손실함수 값이 가장 작았던 yolo 모델 파일입니다. (프로젝트에서 왠만하면 best.pt 파일을 사용합니다.)
seg 모델의 학습 결과를 확인하기 위해서 저는 총 3개의 파일을 분석합니다. (보는 사람마다 다 달라요)
바운딩 박스의 PR 커브 해석

PR 커브에 그려진 그래프 아래 면적을 AP라고 부르면 이값이 1에 가까울 수록 바운딩 박스의 정확도가 높아진거라고 생각하면 됩니다. mAP는 AP의 평균으로 총 64개의 클래스에 대한 AP 평균값이 0.135라는 결과가 나와 성능이 굉장이 낮음을 알 수 있습니다.
객체 탐지(Object Detection) 성능지표 - mAP
YOLO, Faster R-CNN 같은 객체 탐지 모델의 성능을 비교할 때 가장 많이 쓰는 지표가 바로 mAP(Mean Average Precision) 입니다.이 글에서는 mAP이 무엇이고, 왜 중요한지, 그리고 어떻게 계산되는지 아주 쉽게
whitecode2718.tistory.com
학습 진행 확인

학습 중 확인해야할 건, 오버피팅/언더피팅 여부와 학습 진행중 mAP가 얼마나 향상되고 있는지 확인하는 것입니다.
학습셋의 경우 손실함수의 값이 계속 줄어든 반면, 검증셋의 손실함수는 20~30 에포크까지 줄어들다 조금씩 올라가고 있음을 확인할 수 있습니다(오버피팅 발생).
학습 중 mAP값이 요동 치면서 특정 값에 수렴하고 있다는 것을 알수 있습니다. (불안정한 학습 과정과 early stop의 필요성 확인).
검증셋 이미지 결과 확인

검증셋 배치중 하나의 결과입니다. 이부분은 전반적으로 잘된 것을 확인할 수 있습니다. mAP가 낮은데 검증셋의 이미지 검출 정확도가 높은걸 봐서는 모델이 정답을 예측하는 능력은 좋지만 정답이 아닌것을 정답이라고 우기는 경향이 있다는 것을 알 수 있습니다. (Type I Error이 많다)
학습 결과 정리
- PR 커브 아래 면적인 AP는 바운딩 박스 검출 정확도를 의미하며, 본 실험에서 64개 클래스에 대한 mAP는 0.135로 전반적인 객체 탐지 성능이 낮게 나타났다.
- 학습 과정에서 학습셋 손실은 지속적으로 감소한 반면, 검증셋 손실은 20~30 에포크 이후 증가하여 오버피팅이 발생했음을 확인하였다.
- 학습 중 mAP 값이 큰 폭으로 요동치며 특정 값에 수렴하는 양상을 보여 학습 과정이 불안정함을 알 수 있다.
- 검증 이미지에서는 정답 객체를 비교적 잘 검출하지만, 오탐(False Positive)이 많아 Type I Error가 높은 경향을 보였다.
3. YOLOv11n-seg 모델 테스트
학습한 모델을 테스트 해보려면 아래 코드를 사용하면 됩니다.
from ultralytics import YOLO
# Load a pretrained YOLO11n model
model = YOLO("model/best.pt")
# Define path to the image file - 구조 확인하고 넣기
source = "SUNRGBD/kv1/NYUdata/NYU0001/image/NYU0001.jpg"
# 아래 코드를 사용하면 알아서 결과를 저장함
model.predict(source, save=True, conf=0.5, show=True)
conf는 신뢰도를 의미하고, show는 모델의 결과를 OpenCV의 imshow 형태로 결과창을 띄우는지 묻는 설정입니다.

실재 결과는 대상을 어느정도 잘 검출 했지만, 저는 만족스러운 성능 지표를 얻지 못했기 때문에 성능 향상을 위해 전략을 따로 세웠고 이는 다음 글에서 다루겠습니다.
'프로젝트' 카테고리의 다른 글
| [인공지능 프로젝트] 저시력자를 위한 실내 근거리 물체 탐지 및 알림 시스템 - 5편 (깊이 추정 모델의 학습 결과) (0) | 2025.12.26 |
|---|---|
| [인공지능 프로젝트] 저시력자를 위한 실내 근거리 물체 탐지 및 알림 시스템 - 4편 (YOLOv11n 모델 재학습 및 결과 해석) (1) | 2025.12.18 |
| [인공지능 프로젝트] 저시력자를 위한 실내 근거리 물체 탐지 및 알림 시스템 - 2편 (YOLOv12n-seg 데이터셋 전처리) (1) | 2025.12.12 |
| [인공지능 프로젝트] 저시력자를 위한 실내 근거리 물체 탐지 및 알림 시스템 - 1편 (문제 정의와 프로젝트 개요) (0) | 2025.12.08 |
| [핑 체크하는 프로그램 만들기 - 1] 핑 체크 후 결과 시각화 하는 코드 (0) | 2025.11.28 |