ML & DL

FiftyOne | 데이터셋 시각화 및 분석 도구, FiftyOne 설치, FiftyOne 사용법

토오오끼 2025. 4. 6. 23:14
728x90
반응형

 

FiftyOne은 컴퓨터 비전 분야의 데이터셋을 효과적으로 관리하고 분석할 수 있도록 돕는 오픈소스 툴이다.

해당 툴을 사용하면 이미지 및 비디오 기반 데이터셋을 시각화하고 모델 예측 결과를 평가하며 잘못된 라벨을 쉽게 탐지할 수 있다.

 

1. FiftyOne 설치

  • fiftyOne 설치
apt install libcurl4 openssl
apt install libcurl4-openssl-dev

pip install fiftyone
pip install fiftyone-db-ubuntu2204

 

  • quickstart
import fiftyone as fo
import fiftyone.zoo as foz


dataset = foz.load_zoo_dataset("quickstart")
print(dataset)

sample = dataset.first()
print(sample.predictions.detections[0])

session = fo.launch_app(dataset)
session.show()

-> 주피터 노트북에서는 iframe이 뜨지 않아 port 번호를 연결해 web ui를 띄워야 한다.

-> fiftyone은 default port로 5151을 사용 중이다.

print(session.url)

-> localhost:5151 로 출력 될 것이다.

-> 해당 url을 접속하거나 컨테이너 생성 시 5151로 포트 포워딩을 해 주면 컨테이너 생성한 서버 ip:5151로 web ui가 뜨게 된다.

 

2. 주요 기능

 

1) 다양한 데이터셋 포맷 지원

COCO, YOLO, Pascal VOC, ImageDirectory, VideoDirectory 등 다양한 포맷의 데이터셋을 fo.Dataset.from_dir() 한 줄로 쉽게 로드 가능하다.

import fiftyone as fo
import fiftyone.utils.yolo as fouy

# A name for the dataset
name = "test"

# The directory containing the dataset to import
dataset_dir = "/workspace/DATA/yolo_dataset/"

dataset = fo.Dataset.from_dir(
    dataset_type=fo.types.YOLOv5Dataset,  # YOLOv8도 YOLOv5 포맷과 동일함
    dataset_dir=dataset_dir,
    split="test",  # 또는 "val", "test" 등 사용 가능
    label_field="gt",  # 라벨 필드 이름 (원하는 대로 변경 가능)
)

session = fo.launch_app(dataset)
print(session.url)

-> dataset_dir 경로 내에 dataset.yaml 파일이 무조건 있어야 한다.

 

 

2) 모델 결과 시각화 및 평가

모델 예측 결과를 시각화 하고 gt와 비교해 정답 여부를 확인할 수 있다.

import fiftyone as fo
import fiftyone.brain as fob
import fiftyone.core.labels as fol

from ultralytics import YOLO


# A name for the dataset
name = "test"

# The directory containing the dataset to import
dataset_dir = "/DATA/yolo_dataset/"

dataset = fo.Dataset.from_dir(
    dataset_type=fo.types.YOLOv5Dataset,  # YOLOv8도 YOLOv5 포맷과 동일함
    dataset_dir=dataset_dir,
    split="test",  # 또는 "val", "test" 등 사용 가능
    label_field="gt",  # 라벨 필드 이름 (원하는 대로 변경 가능)
    )

# Index images by similarity
fob.compute_similarity(
    dataset,
    model="clip-vit-base32-torch",
    brain_key="img_sim",
)

# 모델 로드
model = YOLO("/DATA_HARD/yolov8n.pt")

filepaths = dataset.values("filepath")
for sample in dataset:
    results = model(sample.filepath)[0]  # YOLOv8은 리스트 반환
    detections = []
    for box in results.boxes:
        label = results.names[int(box.cls)]
        bbox = box.xywhn[0].tolist()  # [x_center, y_center, w, h] normalized
        confidence = float(box.conf)

        detection = fol.Detection(label=label, bounding_box=bbox, confidence=confidence)
        detections.append(detection)

    # predictions 필드에 추가
    sample["predictions"] = fol.Detections(detections=detections)
    sample.save()

session = fo.launch_app(dataset)
session.wait()

 

compute_mistakenness() 함수를 사용해서 어떤 gt가 예측이 안되었고 예측 결과 중 어떤 게 잘못 되었는지 정량적으로 평가가능하다.

import fiftyone as fo
import fiftyone.brain as fob
from fiftyone import ViewField as F
from collections import Counter


# A name for the dataset
name = "test"

# The directory containing the dataset to import
dataset_dir = "/DATA/yolo_dataset/"

# 기존 데이터셋 삭제
if fo.dataset_exists(name):
    fo.delete_dataset(name)

# gt format이 yolo format일 때
dataset = fo.Dataset.from_dir(
    name=name,
    dataset_type=fo.types.YOLOv5Dataset,  # YOLOv8도 YOLOv5 포맷과 동일함
    dataset_dir=dataset_dir,
    split="test",  # 또는 "val", "test" 등 사용 가능
    label_field="gt",  # 라벨 필드 이름 (원하는 대로 변경 가능)
)

dataset_pred = fo.Dataset.from_dir(
    dataset_type=fo.types.YOLOv5Dataset,
    dataset_dir="/DATA/predict_yolo",
    split="test",
    label_field="predictions"
)

for sample, pred_sample in zip(dataset, dataset_pred):
    sample["predictions"] = pred_sample["predictions"]
    sample.save()

fob.compute_mistakenness(dataset, "predictions", label_field="gt")
mistake_view = dataset.sort_by("mistakenness", reverse=True)

session = fo.launch_app(dataset)
session.view = mistake_view
session.wait()

-> mistakenness : 예측한 결과가 오탐일 가능성이 얼마난 높은지 나타내는 최댓값이다.

    → 즉, gt가 모델에 의해 얼마나 혼동되었는지를 의미한다.

  • 각 GT 객체(바운딩 박스) 에 대해 모델이 예측한 결과와 매칭 여부 확인
  • 매칭이 안 되거나, 예측 confidence가 낮거나, IoU가 낮으면 → 이 GT는 놓칠 가능성 높다고 판단
  • 이런 위험도를 수치화해서 각 gt 마다 mistakenness score (0~1) 부여
  • 마지막으로 각 샘플(이미지) 에 대해 → gt 중 가장 높은 score를 mistakenness로 설정

→ possible_spurious(FP) : 모델은 예측 결과 중 gt와 매칭이 되지 않는 bbox 개수(라벨 누락 or 오탐지)

→ possible_missing(FN) : 모델이 예측하지 못한 gt 개수

 

 

3) 비디오 데이터셋 지원

프레임 단위 분석이 가능하며 프레임 별 예측 결과를 시각화 할 수 있다.

import fiftyone as fo
import fiftyone.core.labels as fol
import cv2
import os

from ultralytics import YOLO


# 비디오 파일 경로
video_path = "/DATA_HARD/sample.mp4"

# YOLOv8 모델 로드
model = YOLO("/DATA_HARD/yolov8n.pt")

# OpenCV로 비디오 로드
cap = cv2.VideoCapture(video_path)
frame_rate = cap.get(cv2.CAP_PROP_FPS)

frame_number = 1
frames = {}

while cap.isOpened():
    ret, frame = cap.read()
    if not ret:
        break

    # 프레임 RGB로 변환
    rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)

    # YOLO 추론
    results = model(rgb_frame)[0]

    detections = []
    for box in results.boxes:
        xc, yc, w, h = box.xywhn[0].tolist()
        bbox = [xc - w/2, yc - h/2, w, h]
        label = results.names[int(box.cls)]
        confidence = float(box.conf)

        detections.append(
            fol.Detection(label=label, bounding_box=bbox, confidence=confidence)
        )

    # 프레임 번호에 따라 저장
    frames[frame_number] = fol.Detections(detections=detections)
    frame_number += 1

cap.release()

# Dataset 생성
dataset = fo.Dataset(name="video-yolo")

# VideoSample 생성
sample = fo.Sample(filepath=video_path)

# Dataset에 먼저 추가 (프레임 액세스 위해)
dataset.add_sample(sample)

# 프레임별 prediction 할당
for frame_number, detections in frames.items():
    sample.frames[frame_number] = fo.Frame(predictions=detections)

# 저장
sample.save()

# UI 실행
session = fo.launch_app(dataset)
session.wait()

이렇게 gt 없는 비디오 하나만 web ui에서 시각화가 가능하지만 여러 개의 video가 저장 되어 있는 데이터셋도 web ui에서 시각화가 가능함.

import fiftyone as fo
import fiftyone.brain as fob
import os

# Dataset 이름
name = "videos"

# 비디오가 저장 되어 있는 디렉터리
dataset_dir = "/DATA_HARD/videos/"

# 기존 데이터셋 삭제
if fo.dataset_exists(name):
    fo.delete_dataset(name)
    
# FiftyOne Dataset 생성
dataset = fo.Dataset(name)

# .mp4 파일 전부 가져오기
video_paths = [
    os.path.join(dataset_dir, f)
    for f in os.listdir(dataset_dir)
    if f.endswith(".mp4")
]

# Sample 객체로 변환해서 Dataset에 추가
samples = [fo.Sample(filepath=vp) for vp in video_paths]
dataset.add_samples(samples)

frames_view = dataset.to_frames(sample_frames=True)  # framea_rate : 샘플링 할 간격(ex. frame_rate=1.0이면 1초당 1 frame 샘플링 한다는 것)

# 프레임별 유사도 계산
fob.compute_similarity(
    frames_view,
    model="clip-vit-base32-torch",
    brain_key="frame_sim",
)

session = fo.launch_app(dataset)         # 비디오를 시각화 하기 위함
# session = fo.launch_app(frames_view)   # 비디오의 프레임을 시각화 하기 위함
session.wait()

-> compute_similarity  해당 함수를 사용하여 프레임 간 유사도를 계산할 수 있다.

-> 유사한 프레임들만 시각화 할 수 있다.

 

 

이외에도 이미지의 고유성을 계산 해 주거나 임베딩을 시각화 하여 데이터셋을 분석할 수도 있다.

이외의 기능은 공식 문서에서 참고 가능하다.

https://docs.voxel51.com

 

FiftyOne — FiftyOne 1.4.1 documentation

Contents

docs.voxel51.com

 

 

728x90
반응형