
튜토리얼
몇 분 만에 가공되지 않은 감시 카메라 영상에서 학습용 데이터셋 구축하기: TwelveLabs와 FiftyOne을 활용한 자동 비디오 큐레이터 제작

네이선 체
개발자는 Twelve Labs의 Marengo 3.0 임베딩과 Pegasus 1.2를 활용해 원본 감시 카메라 영상을 의미론적으로 군집화하고, 미리 정의된 카테고리 없이도 안전 분류 라벨을 자동으로 생성할 수 있습니다. 또한, 그 결과를 FiftyOne에서 시각화하고 단 한 프레임도 수동으로 검토할 필요 없이 학습 준비가 완료된 PyTorch DataLoader로 바로 내보내는 자동화된 비디오 큐레이션 파이프라인을 구축할 수 있습니다.
개발자는 Twelve Labs의 Marengo 3.0 임베딩과 Pegasus 1.2를 활용해 원본 감시 카메라 영상을 의미론적으로 군집화하고, 미리 정의된 카테고리 없이도 안전 분류 라벨을 자동으로 생성할 수 있습니다. 또한, 그 결과를 FiftyOne에서 시각화하고 단 한 프레임도 수동으로 검토할 필요 없이 학습 준비가 완료된 PyTorch DataLoader로 바로 내보내는 자동화된 비디오 큐레이션 파이프라인을 구축할 수 있습니다.

목차
No headings found on page
뉴스레터 구독하기
뉴스레터 구독하기
영상 이해 분야의 최신 기술 업데이트, 튜토리얼 및 인사이트를 받아보세요.
영상 이해 분야의 최신 기술 업데이트, 튜토리얼 및 인사이트를 받아보세요.
AI로 영상을 검색하고, 분석하고, 탐색하세요.
2026. 2. 24.
11분
링크 복사하기
소개
매년 수천 테라바이트의 보안 카메라 영상이 공장의 IP 카메라를 통해 흘러갑니다. 그 데이터의 어딘가에는 지난주 화요일 아찔한 사고를 유발할 뻔했던 지게차 위반 상황, 야간 근무 조의 개인보호장구(PPE) 미착용 패턴, 그리고 6주 동안 아무도 눈치채지 못한 채 방치된 비상구 폐쇄 상황이 기록되어 있습니다. 영상은 존재하지만, 라벨은 존재하지 않습니다.
이러한 가공되지 않은 영상 데이터 양과 실제 사용 가능한 학습 데이터 사이의 간극은 작업자 안전을 위한 컴퓨터 비전을 배포하는 데 있어 가장 큰 병목 구간입니다. AWS SageMaker Ground Truth, Scale AI 또는 전담 어노테이션 팀과 같은 기존 솔루션은 수많은 사람을 투입하여 이 문제를 해결하고자 합니다. 이 방식은 효과가 있지만, 검토에 드는 비용이 영상 시간당 25~50달러에 달하며 카메라 대수에 비례해 선형적으로 증가합니다. 카메라 수가 두 배로 늘어나면 어노테이션 예산도 두 배로 늘어납니다.

이 워크플로우를 완전히 뒤집을 수 있다면 어떨까요? 사고를 찾기 위해 영상을 일일이 돌려보는 대신, 영상이 스스로 구조화되어 유사한 이벤트를 의미별로 그룹화하고 발견한 내용을 설명해 줄 수 있다면 어떨까요?
이 튜토리얼에서 바로 이 시스템을 구축해 볼 것입니다. TwelveLabs의 비디오 이해 모델과 FiftyOne의 데이터셋 관리 플랫폼을 결합하여 다음과 같은 파이프라인을 구축해 보겠습니다.
TwelveLabs API를 사용하여 가공되지 않은 보안 카메라 영상을 수집 및 인덱싱합니다.
비디오 콘텐츠를 고밀도 벡터로 표현하는 멀티모달 임베딩(Marengo 3.0)을 추출하여 시각, 청각 및 콘텍스트 신호를 동시에 캡처합니다.
KMeans를 사용해 비디오를 의미론적으로 클러스터링하고, 사전 정의된 카테고리 없이도 유사한 안전 사고를 그룹화합니다.
생성형 비디오 추론(Pegasus 1.2)을 사용하여 각 클러스터에 라벨을 자동으로 지정하고, 추상적인 클러스터 ID를 사람이 읽을 수 있는 안전 분류명으로 변환합니다.
품질 감수를 위해 FiftyOne에서 결과를 시각화한 다음, 학습에 바로 사용 가능한 PyTorch 데이터셋으로 내보냅니다.
핵심 인사이트: TwelveLabs는 비디오를 프레임 단위로 처리하지 않습니다. Marengo는 비디오를 압축된 멀티모달 표현(TwelveLabs 팀이 "부피로서의 비디오"라고 부르는 개념)으로 인코딩하여 오디오, 텍스트, 모션 및 시각적 콘텍스트를 단일 임베딩 공간으로 통합합니다. 이것이 바로 의미론적 클러스터링이 작동하는 이유입니다. 겉보기에는 다르게 보이지만 의미가 같은 영상들(예: 서로 다른 카메라 각도에서 촬영된, 지게차가 보행자의 진로를 방해하는 두 영상)은 임베딩 공간에서 결국 서로 가까운 위치에 있게 됩니다.
아래의 가이드를 시청하여 완성된 애플리케이션의 빠른 데모를 확인해 보세요.

사전 요구 사항
이 프로젝트를 진행하려면 다음 세 가지가 필요합니다.
Python 3.8+: Python 다운로드
TwelveLabs API 키: 인증 문서 (무료 티어 이용 가능)
HuggingFace 계정 (선택 사항): 저희가 사용하는 것과 동일한 보안 카메라 데이터셋(Voxel51/Safe_and_Unsafe_Behaviours)을 사용하려는 경우에만 필요합니다.
리포지토리를 클론하고 종속성을 설치합니다.
>> git clone https://github.com/nathanchess/visual-ai-worker-safety-kit >> cd
아키텍처 개요
코드를 작성하기 전에 전체 파이프라인을 시각적으로 살펴보는 것이 도움이 됩니다. 상위 수준에서 이 프로젝트는 두 부분으로 나뉩니다. TwelveLabs가 지능형 처리(임베딩 및 라벨)를 제공하고, FiftyOne이 인프라(시각화, 필터링 및 내보내기)를 구축합니다.

전체 화면 보기 (Lucid App): [Voxel51 x TwelveLabs] - 의미론적 데이터셋 큐레이터 툴
Marengo 3.0은 TwelveLabs의 인코더 모델로, 비디오를 보여지고 들리는 것뿐만 아니라 맥락적으로 암시되는 것까지 캡처하는 풍부한 멀티모달 임베딩으로 압축합니다. Pegasus 1.2는 추론 모델로, 이 압축된 표현을 탐색하여 이벤트를 식별하고, 행동을 분류하며, 자연어 설명문을 생성합니다. 그 다운스트림에 위치한 FiftyOne은 이 임베딩과 라벨을 수집하여 대화형 시각화, 유사성 탐색 및 PyTorch로의 직접 내보내기 기능을 제공합니다.
전체 소스 코드는 GitHub 리포지토리에서 확인할 수 있습니다. 아래의 가이드는 main.py의 구조를 따릅니다.
1단계: 지능형 비디오 수집
Hugging Face에서 보안 카메라 데이터셋을 로드하는 것으로 시작합니다. FiftyOne의 ViewField(F로 임포트됨)를 사용하면 기본 데이터를 수정하지 않고도 선언적 필터를 빌드할 수 있습니다. 여기서는 세 가지 필터가 중요합니다. 학습용 분할(train split)로 한정하고, 4초 미만의 클립(유의미한 임베딩을 만들기에는 너무 짧음)은 제외하며, 이미 인덱싱한 비디오는 건너뜁니다.
from fiftyone import ViewField as F from fiftyone.utils.huggingface import load_from_hub def load_or_create_dataset(dataset_name): # Hugging Face Hub에서 직접 로드 return load_from_hub("Voxel51/Safe_and_Unsafe_Behaviours", name=dataset_name) def ingest_videos(client, index_id, dataset, min_duration=4.0): # 뷰 생성: 오직 학습 분할(Train split)만, 길이 4초 이상, 아직 인덱싱되지 않은 데이터 base_view = ( dataset .match_tags("train") .match(F("metadata.duration") >= min_duration) .match(~F("tl_video_id").exists()) # 멱등성 확인 ) # ... 반복 로직 ...
이 ~F("tl_video_id").exists() 필터는 주의 깊게 살펴볼 가치가 있습니다. 실제 프로덕션 환경에서는 새 영상이 들어올 때마다 이 파이프라인을 매일 실행할 수 있습니다. 이 필터 덕분에 동일한 비디오를 두 번 인덱싱하는 불필요한 비용을 지불하지 않게 됩니다. 샘플에 이미 TwelveLabs 비디오 ID가 저장되어 있다면, 뷰는 이를 조용히 건너뜁니다. 별도의 추적 코드 없이 데이터 레이어에서 멱등성이 보장됩니다.
2단계: 임베딩 추출 및 FiftyOne에 데이터 입력
필터링된 뷰가 구축되면 샘플을 순회하며 각 비디오를 TwelveLabs에 업로드해 인덱싱합니다. FiftyOne의 iter_samples(autosave=True)는 데이터베이스 쓰기를 자동으로 배치 처리하므로 수천 개의 클립을 처리할 때 매우 유용합니다. 매 샘플마다 데이터베이스를 왕복하는 오버헤드를 방지해 줍니다.
# 필터링된 뷰를 순회 for sample in label_view.iter_samples(autosave=True, progress=True): try: # TwelveLabs에 업로드하고 반환된 비디오 ID를 샘플에 저장 sample["tl_video_id"] = index_video_to_twelvelabs( client, index_id, sample ) print(f" ✓ {sample.filename}") except Exception as e: print(f" ✗ {sample.filename}: {e}")
인덱싱이 완료되면 각 비디오에 대한 Marengo 3.0 임베딩을 가져옵니다. 이는 시각적으로 발생하는 일, 들리는 소리, 그리고 둘 사이의 맥락적 관계 등 각 클립의 전체 멀티모달 콘텐츠를 인코딩하는 고밀도 벡터입니다. 각 프레임을 독립적으로 처리하는 이미지 전용 모델의 임베딩과 달리, Marengo의 임베딩은 시간적 연속성을 포착합니다. "작업자가 보안경을 벗는 행동"과 "작업자가 보안경을 쓰는 행동"은 단일 프레임 상으로는 동일하게 보일 수 있지만, 의미상 확연히 다른 벡터를 생성합니다.
3단계: 비지도 의미론적 클러스터링
여기가 바로 이 파이프라인의 핵심입니다. 우리는 사전에 정의한 카테고리가 없습니다. 이 영상에 어떤 유형의 안전 위반 사항이 존재하는지, 혹은 그 모습이 구체적으로 어떤지 전혀 모릅니다. 대신, 임베딩 공간에 KMeans 클러스터링을 적용하여 데이터가 스스로 구조화되도록 유도합니다.
def cluster_and_label(client, dataset, embeddings, num_clusters=8): print(f"Clustering {len(embeddings)} videos into {num_clusters} clusters...") # 1. 임베딩 클러스터링 kmeans = KMeans(n_clusters=num_clusters, random_state=0) cluster_labels = kmeans.fit_predict(embeddings) # 2. Pegasus(생성형 AI)를 사용해 클러스터를 의미론적 라벨에 매핑 cluster_label_map = {} for cluster_idx in np.unique(cluster_labels): # 클러스터에서 대표 비디오를 선택하고 Pegasus에 라벨 지정을 요청 representative_video_id = get_video_id_for_cluster(cluster_idx) cluster_label_map[cluster_idx] = generate_label(client, representative_video_id) # 3. FiftyOne 샘플 일괄 업데이트 indexed_view = dataset.exists("tl_video_id") classifications = [Classification(label=cluster_label_map[c]) for c in cluster_labels] indexed_view.set_values("pred_cluster", classifications)
set_values()에 대한 기술적 참고 사항: 이 메서드는 단일 대량 작업으로 전체 데이터셋을 업데이트합니다. 샘플을 하나씩 순회하며 sample.save()를 호출하는 비효율적인 방식은 규모가 커질수록 처리 속도가 크게 느려집니다. 수천 개의 비디오를 다룰 때는 이러한 처리 방식의 차이가 매우 중요합니다.
왜 여기서 의미론적 클러스터링이 유의미한 그룹을 만들어낼 수 있을까요? Marengo의 임베딩은 비디오를 프레임별 특징들의 단순 집합이 아니라 압축된 멀티모달 표현으로 인코딩하기 때문입니다. 지게차가 통로를 막고 있는 모습을 담은 두 개의 클립이 서로 다른 각도, 다른 시간대, 다른 소음 환경에서 촬영되었더라도, 의미상 같은 맥락을 공유하므로 임베딩 공간에서 인접한 위치에 도달하게 됩니다. 오직 시각적 유사성에만 의존하는 기존 방식이었다면 이 두 영상은 서로 흩어졌을 것입니다.
4단계: Pegasus를 통한 제로샷 자동 라벨링
"클러스터 3"과 같은 클러스터 ID는 수학적으로는 유용하지만 실제 업무상으로는 아무런 의미가 없습니다. 우리에게는 "지게차 우선 통행 위반"과 같은 명확한 이름이 필요합니다. 여기서 Pegasus 1.2가 등장합니다.
각 클러스터에서 대표 비디오를 하나씩 선택해 구조화된 프롬프트와 함께 Pegasus에 제공합니다. Pegasus는 단일 프레임이나 대본 파일이 아닌 비디오 전체를 추론하여 정확한 안전 분류명을 매핑해 줍니다. 미세 조정(fine-tuning)은 필요 없습니다. 이것이 바로 제로샷 생성형 추론입니다. Pegasus는 이 특정 보안 카메라 영상을 이전에 본 적이 없음에도 불구하고 기존 안전 프로토콜과 일치하는 라벨을 생성해 냅니다.
CLUSTER_LABEL_PROMPT = """ Analyze this workplace safety video and classify it as exactly ONE of the following labels. UNSAFE BEHAVIORS: - Safe Walkway Violation - Unauthorized Intervention - Opened Panel Cover ... Return ONLY the exact label name. """
프롬프트는 Pegasus가 미리 설정된 안전 카테고리 체계 내에서만 판단하도록 제한합니다. 이는 의도된 설계입니다. 실제 작업 환경에서 안전 관리자는 임의의 서술형 설명이 아닌, 회사에서 기존에 사용 중인 사고 분류 시스템에 정확히 대응하는 라벨이 필요하기 때문입니다. 프롬프트를 수정하여 조직의 구체적인 안전 수칙에 맞게 이 카테고리 체계를 손쉽게 조정할 수 있습니다. 재학습도, 새로운 모델도 필요 없이 라벨 문자열 세트만 변경하면 됩니다.
5단계: UMAP을 통한 대화형 임베딩 시각화
수치와 라벨도 유용하지만, 컴퓨터 비전 작업에서는 데이터를 직접 눈으로 확인할 수 있어야 합니다. FiftyOne의 Brain 모듈은 고차원 임베딩의 2D UMAP 프로젝션을 계산하여, 각 점이 비디오를 나타내고 색상이 자동으로 생성된 라벨에 대응하는 대화형 산점도(scatterplot)를 시각화합니다.

이 시각화에서 확인해야 할 핵심 사항: 촘촘하게 잘 분리된 클러스터는 임베딩이 깔끔하고 유의미한 그룹화를 수행했음을 나타냅니다. 즉, 모델이 "비상구 폐쇄"와 "PPE 위반"을 확실하게 구별하고 있다는 의미입니다. 반면 겹쳐져 있는 클러스터는 카테고리를 한 단계 다듬어야 할 필요가 있음을 시사합니다 (예를 들어, "잘못된 리프팅"과 "인체공학적 위반"은 서로 명확하게 나누기에는 너무 유사할 수 있습니다). 클러스터 중심에서 멀리 떨어진 아웃라이어(이상치)들은 희귀 사례 점검이나 잘못 라벨링된 데이터를 골라내기 위해 수동으로 조사해 볼 가치가 있습니다.
이 단계는 단순히 시각적 효과만을 위한 것이 아닙니다. 데이터를 저장 및 내보내기 전에 임베딩을 시각적으로 점검하면, 자칫 학습 파이프라인으로 그대로 흘러 들어갔을 데이터 품질 문제를 예방하고 바로잡을 수 있습니다. 여기서의 5분 남짓한 검토 시간이 다운스트림 개발 단계에서의 오랜 디버깅 시간을 아껴 줍니다.
6단계: PyTorch로 모델 학습 데이터 내보내기
수집된 데이터셋은 실제 인공지능 모델을 학습시킬 수 있을 때 비로소 그 가치를 발휘합니다. 문자열 라벨을 텐서(tensor)에 매핑하고 실시간으로 미리 계산된 임베딩을 검색하는 맞춤형 GetItem 클래스를 구현해, FiftyOne의 to_torch() 메서드를 사용합니다.
from fiftyone.utils.torch import GetItem import torch class WorkerSafetyGetItem(GetItem): def __call__(self, d): return { "embedding": torch.tensor(d.get("tl_embedding"), dtype=torch.float32), "label_idx": torch.tensor( self.label_to_idx.get(d.get("ground_truth").label, -1), dtype=torch.long ), } def create_dataloader(dataset, batch_size=4): indexed_view = dataset.exists("tl_video_id") # FiftyOne 뷰에서 직접 PyTorch 데이터셋 생성 torch_dataset = indexed_view.to_torch(WorkerSafetyGetItem(LABEL_TO_IDX)) return DataLoader(torch_dataset, batch_size=batch_size, shuffle=True)
결과물인 DataLoader는 미리 추출되어 연산된 임베딩과 정수형 라벨의 배치를 생성합니다. 인덱싱 과정에서 이미 Marengo 모델에 의해 임베딩이 미리 추출되어 있으므로, 실제 분류 모델 학습 시에는 무거운 비디오 피처 추출 과정을 생략할 수 있습니다. 이 가벼운 임베딩 위에서 MLP나 선형 프로브와 같은 경량화 분류기를 몇 시간 대신 몇 초 만에 쉽게 학습시킬 수 있으며, 생성된 라벨도 폴더 이름 등에서 무작위로 유추한 것이 아닌 Pegasus가 실제 비디오 내용을 추론해 얻은 것이므로 강력한 정합성을 가집니다.
샘플 데이터셋은 GitHub 리포지토리에서 찾아볼 수 있습니다.
이것이 왜 중요할까요: 데이터 관리에서 비즈니스 인텔리전스로
기술 파이프라인 구축을 완료했으니, 이 워크플로우가 기업 수준에서 왜 혁신적인 가치를 가지는지 큰 그림에서 생각해 볼 필요가 있습니다.
모델 개발 가속화. 가공되지 않은 보안 카메라 영상으로부터 모델 학습으로 이어지는 기존 과정은 수 주간의 어노테이션 작업, 반복적인 감수 프로세스, 그리고 거대한 비용 축적을 야기했습니다. 이 파이프라인은 그 복잡한 과정을 단 하나의 스크립트 실행으로 압축합니다. 내보낸 데이터셋에는 이미 계산된 고품질 임베딩과 완전한 정합성을 가진 라벨이 포함되어 있어 즉시 모델을 학습시킬 수 있습니다. 안전 분류 모델을 신속하게 도입 및 실험하는 개발 팀에서 어노테이션 병목을 마침내 해결하게 됩니다.
시스템의 구조적 패턴 파악. 안전 관리자 관점에서 이 클러스터링 시각화 뷰는 단순 데이터 점검용 화면을 넘어 실질적인 분석 대시보드가 됩니다. 예를 들어 KMeans가 유독 "비상구 폐쇄" 항목 쪽으로 비정상적으로 큰 클러스터를 분리해 냈다면, 이는 단순히 지나갈 일회성 위반이 아닙니다. 일방적인 주의 조치에 머무르는 것이 아닌 프로세스 전반의 시스템적인 대책 마련이 필요한 지점입니다. 일별, 주별 등으로 배치 작업을 정기적으로 수행하면 보안 카메라 영상을 수동적인 책임 관리 기록물에서 능동적인 현장 데이터 정보원으로 전환시킬 수 있습니다. 시간에 따라 특정 위반 사항 발생 추이가 감소하거나 늘어나는지 정밀 모니터링하고, 사내 안전 대책의 실효성을 세밀하게 평가할 수 있게 됩니다.
어노테이션 비용의 극적인 절감. 안전이 필수적인 고정밀 데이터셋을 위해 비디오 데이터를 수동 어노테이션할 때는 일반적으로 검토 영상 시간당 25~50달러의 큰 부담이 따릅니다. 본 파이프라인은 이 막대한 직접 인건비 지출 구조를 효율적인 API 연산으로 대체합니다. 특히 수 백대 규모의 카메라 인프라를 상시 가동하는 기업들에게 이 고정비의 격차는 기하급수적으로 벌어지게 됩니다. 더욱이 이 라벨들은 완벽하게 복제 및 재현 가능하여, 새로 들어오는 영상들에 동일한 파이프라인을 그대로 적용하면 수동 어노테이션 방식 특유의 검토자별 편차 문제 없이 항상 데이터 품질의 일관성을 유지할 수 있습니다.

결론
본 튜토리얼을 통해 원시 영상 수집부터 라벨링이 완료된 PyTorch DataLoader 확보까지의 통합 파이프라인을 상세히 구축해 보았습니다. TwelveLabs의 Marengo 3.0은 시각, 청각 및 맥락적 요소를 아우르는 비디오 멀티모달 벡터 임베딩 추출이라는 복잡한 고난이도 작업을 매끄럽게 수행했습니다. Pegasus 1.2 모델은 기호에 불과한 추상적 클러스터를 사람이 인식하고 활용할 수 있는 정확한 업무용 분류 카테고리로 신속하고 명확하게 변환했습니다. FiftyOne은 이 모든 정성적 프로세스를 종합적인 시각화 및 내보내기 인프라를 통해 검증 가능하며 즉시 학습 수립이 가능한 구조로 설계하도록 전방위로 도왔습니다.
전체 통합 소스 코드는 GitHub에 공개되어 있습니다. 로컬에 제품 환경을 구축하고, 실제 가동 중인 보안 카메라 영상 경로를 입력한 후 필요에 따라 Pegasus의 라벨링 프롬프트 부분을 내부 기준에 맞게 손쉽게 수정해 시작해 보세요.
지능형 영상 검색기능부터 안전 규정 규제 준수 모니터링, 하이라이트 영상 자율 추출까지 TwelveLabs의 강력한 비디오 이해 API를 통해 한 차원 더 높은 차세대 서비스를 설계해 보고 싶으시다면, 제공되는 TwelveLabs 가이드 문서를 방문하시거나 TwelveLabs 팀에 언제든 문의를 남겨 함께 비즈니스 활용 방안을 논의해 보세요.
추가 참고 자료
소개
매년 수천 테라바이트의 보안 카메라 영상이 공장의 IP 카메라를 통해 흘러갑니다. 그 데이터의 어딘가에는 지난주 화요일 아찔한 사고를 유발할 뻔했던 지게차 위반 상황, 야간 근무 조의 개인보호장구(PPE) 미착용 패턴, 그리고 6주 동안 아무도 눈치채지 못한 채 방치된 비상구 폐쇄 상황이 기록되어 있습니다. 영상은 존재하지만, 라벨은 존재하지 않습니다.
이러한 가공되지 않은 영상 데이터 양과 실제 사용 가능한 학습 데이터 사이의 간극은 작업자 안전을 위한 컴퓨터 비전을 배포하는 데 있어 가장 큰 병목 구간입니다. AWS SageMaker Ground Truth, Scale AI 또는 전담 어노테이션 팀과 같은 기존 솔루션은 수많은 사람을 투입하여 이 문제를 해결하고자 합니다. 이 방식은 효과가 있지만, 검토에 드는 비용이 영상 시간당 25~50달러에 달하며 카메라 대수에 비례해 선형적으로 증가합니다. 카메라 수가 두 배로 늘어나면 어노테이션 예산도 두 배로 늘어납니다.

이 워크플로우를 완전히 뒤집을 수 있다면 어떨까요? 사고를 찾기 위해 영상을 일일이 돌려보는 대신, 영상이 스스로 구조화되어 유사한 이벤트를 의미별로 그룹화하고 발견한 내용을 설명해 줄 수 있다면 어떨까요?
이 튜토리얼에서 바로 이 시스템을 구축해 볼 것입니다. TwelveLabs의 비디오 이해 모델과 FiftyOne의 데이터셋 관리 플랫폼을 결합하여 다음과 같은 파이프라인을 구축해 보겠습니다.
TwelveLabs API를 사용하여 가공되지 않은 보안 카메라 영상을 수집 및 인덱싱합니다.
비디오 콘텐츠를 고밀도 벡터로 표현하는 멀티모달 임베딩(Marengo 3.0)을 추출하여 시각, 청각 및 콘텍스트 신호를 동시에 캡처합니다.
KMeans를 사용해 비디오를 의미론적으로 클러스터링하고, 사전 정의된 카테고리 없이도 유사한 안전 사고를 그룹화합니다.
생성형 비디오 추론(Pegasus 1.2)을 사용하여 각 클러스터에 라벨을 자동으로 지정하고, 추상적인 클러스터 ID를 사람이 읽을 수 있는 안전 분류명으로 변환합니다.
품질 감수를 위해 FiftyOne에서 결과를 시각화한 다음, 학습에 바로 사용 가능한 PyTorch 데이터셋으로 내보냅니다.
핵심 인사이트: TwelveLabs는 비디오를 프레임 단위로 처리하지 않습니다. Marengo는 비디오를 압축된 멀티모달 표현(TwelveLabs 팀이 "부피로서의 비디오"라고 부르는 개념)으로 인코딩하여 오디오, 텍스트, 모션 및 시각적 콘텍스트를 단일 임베딩 공간으로 통합합니다. 이것이 바로 의미론적 클러스터링이 작동하는 이유입니다. 겉보기에는 다르게 보이지만 의미가 같은 영상들(예: 서로 다른 카메라 각도에서 촬영된, 지게차가 보행자의 진로를 방해하는 두 영상)은 임베딩 공간에서 결국 서로 가까운 위치에 있게 됩니다.
아래의 가이드를 시청하여 완성된 애플리케이션의 빠른 데모를 확인해 보세요.

사전 요구 사항
이 프로젝트를 진행하려면 다음 세 가지가 필요합니다.
Python 3.8+: Python 다운로드
TwelveLabs API 키: 인증 문서 (무료 티어 이용 가능)
HuggingFace 계정 (선택 사항): 저희가 사용하는 것과 동일한 보안 카메라 데이터셋(Voxel51/Safe_and_Unsafe_Behaviours)을 사용하려는 경우에만 필요합니다.
리포지토리를 클론하고 종속성을 설치합니다.
>> git clone https://github.com/nathanchess/visual-ai-worker-safety-kit >> cd
아키텍처 개요
코드를 작성하기 전에 전체 파이프라인을 시각적으로 살펴보는 것이 도움이 됩니다. 상위 수준에서 이 프로젝트는 두 부분으로 나뉩니다. TwelveLabs가 지능형 처리(임베딩 및 라벨)를 제공하고, FiftyOne이 인프라(시각화, 필터링 및 내보내기)를 구축합니다.

전체 화면 보기 (Lucid App): [Voxel51 x TwelveLabs] - 의미론적 데이터셋 큐레이터 툴
Marengo 3.0은 TwelveLabs의 인코더 모델로, 비디오를 보여지고 들리는 것뿐만 아니라 맥락적으로 암시되는 것까지 캡처하는 풍부한 멀티모달 임베딩으로 압축합니다. Pegasus 1.2는 추론 모델로, 이 압축된 표현을 탐색하여 이벤트를 식별하고, 행동을 분류하며, 자연어 설명문을 생성합니다. 그 다운스트림에 위치한 FiftyOne은 이 임베딩과 라벨을 수집하여 대화형 시각화, 유사성 탐색 및 PyTorch로의 직접 내보내기 기능을 제공합니다.
전체 소스 코드는 GitHub 리포지토리에서 확인할 수 있습니다. 아래의 가이드는 main.py의 구조를 따릅니다.
1단계: 지능형 비디오 수집
Hugging Face에서 보안 카메라 데이터셋을 로드하는 것으로 시작합니다. FiftyOne의 ViewField(F로 임포트됨)를 사용하면 기본 데이터를 수정하지 않고도 선언적 필터를 빌드할 수 있습니다. 여기서는 세 가지 필터가 중요합니다. 학습용 분할(train split)로 한정하고, 4초 미만의 클립(유의미한 임베딩을 만들기에는 너무 짧음)은 제외하며, 이미 인덱싱한 비디오는 건너뜁니다.
from fiftyone import ViewField as F from fiftyone.utils.huggingface import load_from_hub def load_or_create_dataset(dataset_name): # Hugging Face Hub에서 직접 로드 return load_from_hub("Voxel51/Safe_and_Unsafe_Behaviours", name=dataset_name) def ingest_videos(client, index_id, dataset, min_duration=4.0): # 뷰 생성: 오직 학습 분할(Train split)만, 길이 4초 이상, 아직 인덱싱되지 않은 데이터 base_view = ( dataset .match_tags("train") .match(F("metadata.duration") >= min_duration) .match(~F("tl_video_id").exists()) # 멱등성 확인 ) # ... 반복 로직 ...
이 ~F("tl_video_id").exists() 필터는 주의 깊게 살펴볼 가치가 있습니다. 실제 프로덕션 환경에서는 새 영상이 들어올 때마다 이 파이프라인을 매일 실행할 수 있습니다. 이 필터 덕분에 동일한 비디오를 두 번 인덱싱하는 불필요한 비용을 지불하지 않게 됩니다. 샘플에 이미 TwelveLabs 비디오 ID가 저장되어 있다면, 뷰는 이를 조용히 건너뜁니다. 별도의 추적 코드 없이 데이터 레이어에서 멱등성이 보장됩니다.
2단계: 임베딩 추출 및 FiftyOne에 데이터 입력
필터링된 뷰가 구축되면 샘플을 순회하며 각 비디오를 TwelveLabs에 업로드해 인덱싱합니다. FiftyOne의 iter_samples(autosave=True)는 데이터베이스 쓰기를 자동으로 배치 처리하므로 수천 개의 클립을 처리할 때 매우 유용합니다. 매 샘플마다 데이터베이스를 왕복하는 오버헤드를 방지해 줍니다.
# 필터링된 뷰를 순회 for sample in label_view.iter_samples(autosave=True, progress=True): try: # TwelveLabs에 업로드하고 반환된 비디오 ID를 샘플에 저장 sample["tl_video_id"] = index_video_to_twelvelabs( client, index_id, sample ) print(f" ✓ {sample.filename}") except Exception as e: print(f" ✗ {sample.filename}: {e}")
인덱싱이 완료되면 각 비디오에 대한 Marengo 3.0 임베딩을 가져옵니다. 이는 시각적으로 발생하는 일, 들리는 소리, 그리고 둘 사이의 맥락적 관계 등 각 클립의 전체 멀티모달 콘텐츠를 인코딩하는 고밀도 벡터입니다. 각 프레임을 독립적으로 처리하는 이미지 전용 모델의 임베딩과 달리, Marengo의 임베딩은 시간적 연속성을 포착합니다. "작업자가 보안경을 벗는 행동"과 "작업자가 보안경을 쓰는 행동"은 단일 프레임 상으로는 동일하게 보일 수 있지만, 의미상 확연히 다른 벡터를 생성합니다.
3단계: 비지도 의미론적 클러스터링
여기가 바로 이 파이프라인의 핵심입니다. 우리는 사전에 정의한 카테고리가 없습니다. 이 영상에 어떤 유형의 안전 위반 사항이 존재하는지, 혹은 그 모습이 구체적으로 어떤지 전혀 모릅니다. 대신, 임베딩 공간에 KMeans 클러스터링을 적용하여 데이터가 스스로 구조화되도록 유도합니다.
def cluster_and_label(client, dataset, embeddings, num_clusters=8): print(f"Clustering {len(embeddings)} videos into {num_clusters} clusters...") # 1. 임베딩 클러스터링 kmeans = KMeans(n_clusters=num_clusters, random_state=0) cluster_labels = kmeans.fit_predict(embeddings) # 2. Pegasus(생성형 AI)를 사용해 클러스터를 의미론적 라벨에 매핑 cluster_label_map = {} for cluster_idx in np.unique(cluster_labels): # 클러스터에서 대표 비디오를 선택하고 Pegasus에 라벨 지정을 요청 representative_video_id = get_video_id_for_cluster(cluster_idx) cluster_label_map[cluster_idx] = generate_label(client, representative_video_id) # 3. FiftyOne 샘플 일괄 업데이트 indexed_view = dataset.exists("tl_video_id") classifications = [Classification(label=cluster_label_map[c]) for c in cluster_labels] indexed_view.set_values("pred_cluster", classifications)
set_values()에 대한 기술적 참고 사항: 이 메서드는 단일 대량 작업으로 전체 데이터셋을 업데이트합니다. 샘플을 하나씩 순회하며 sample.save()를 호출하는 비효율적인 방식은 규모가 커질수록 처리 속도가 크게 느려집니다. 수천 개의 비디오를 다룰 때는 이러한 처리 방식의 차이가 매우 중요합니다.
왜 여기서 의미론적 클러스터링이 유의미한 그룹을 만들어낼 수 있을까요? Marengo의 임베딩은 비디오를 프레임별 특징들의 단순 집합이 아니라 압축된 멀티모달 표현으로 인코딩하기 때문입니다. 지게차가 통로를 막고 있는 모습을 담은 두 개의 클립이 서로 다른 각도, 다른 시간대, 다른 소음 환경에서 촬영되었더라도, 의미상 같은 맥락을 공유하므로 임베딩 공간에서 인접한 위치에 도달하게 됩니다. 오직 시각적 유사성에만 의존하는 기존 방식이었다면 이 두 영상은 서로 흩어졌을 것입니다.
4단계: Pegasus를 통한 제로샷 자동 라벨링
"클러스터 3"과 같은 클러스터 ID는 수학적으로는 유용하지만 실제 업무상으로는 아무런 의미가 없습니다. 우리에게는 "지게차 우선 통행 위반"과 같은 명확한 이름이 필요합니다. 여기서 Pegasus 1.2가 등장합니다.
각 클러스터에서 대표 비디오를 하나씩 선택해 구조화된 프롬프트와 함께 Pegasus에 제공합니다. Pegasus는 단일 프레임이나 대본 파일이 아닌 비디오 전체를 추론하여 정확한 안전 분류명을 매핑해 줍니다. 미세 조정(fine-tuning)은 필요 없습니다. 이것이 바로 제로샷 생성형 추론입니다. Pegasus는 이 특정 보안 카메라 영상을 이전에 본 적이 없음에도 불구하고 기존 안전 프로토콜과 일치하는 라벨을 생성해 냅니다.
CLUSTER_LABEL_PROMPT = """ Analyze this workplace safety video and classify it as exactly ONE of the following labels. UNSAFE BEHAVIORS: - Safe Walkway Violation - Unauthorized Intervention - Opened Panel Cover ... Return ONLY the exact label name. """
프롬프트는 Pegasus가 미리 설정된 안전 카테고리 체계 내에서만 판단하도록 제한합니다. 이는 의도된 설계입니다. 실제 작업 환경에서 안전 관리자는 임의의 서술형 설명이 아닌, 회사에서 기존에 사용 중인 사고 분류 시스템에 정확히 대응하는 라벨이 필요하기 때문입니다. 프롬프트를 수정하여 조직의 구체적인 안전 수칙에 맞게 이 카테고리 체계를 손쉽게 조정할 수 있습니다. 재학습도, 새로운 모델도 필요 없이 라벨 문자열 세트만 변경하면 됩니다.
5단계: UMAP을 통한 대화형 임베딩 시각화
수치와 라벨도 유용하지만, 컴퓨터 비전 작업에서는 데이터를 직접 눈으로 확인할 수 있어야 합니다. FiftyOne의 Brain 모듈은 고차원 임베딩의 2D UMAP 프로젝션을 계산하여, 각 점이 비디오를 나타내고 색상이 자동으로 생성된 라벨에 대응하는 대화형 산점도(scatterplot)를 시각화합니다.

이 시각화에서 확인해야 할 핵심 사항: 촘촘하게 잘 분리된 클러스터는 임베딩이 깔끔하고 유의미한 그룹화를 수행했음을 나타냅니다. 즉, 모델이 "비상구 폐쇄"와 "PPE 위반"을 확실하게 구별하고 있다는 의미입니다. 반면 겹쳐져 있는 클러스터는 카테고리를 한 단계 다듬어야 할 필요가 있음을 시사합니다 (예를 들어, "잘못된 리프팅"과 "인체공학적 위반"은 서로 명확하게 나누기에는 너무 유사할 수 있습니다). 클러스터 중심에서 멀리 떨어진 아웃라이어(이상치)들은 희귀 사례 점검이나 잘못 라벨링된 데이터를 골라내기 위해 수동으로 조사해 볼 가치가 있습니다.
이 단계는 단순히 시각적 효과만을 위한 것이 아닙니다. 데이터를 저장 및 내보내기 전에 임베딩을 시각적으로 점검하면, 자칫 학습 파이프라인으로 그대로 흘러 들어갔을 데이터 품질 문제를 예방하고 바로잡을 수 있습니다. 여기서의 5분 남짓한 검토 시간이 다운스트림 개발 단계에서의 오랜 디버깅 시간을 아껴 줍니다.
6단계: PyTorch로 모델 학습 데이터 내보내기
수집된 데이터셋은 실제 인공지능 모델을 학습시킬 수 있을 때 비로소 그 가치를 발휘합니다. 문자열 라벨을 텐서(tensor)에 매핑하고 실시간으로 미리 계산된 임베딩을 검색하는 맞춤형 GetItem 클래스를 구현해, FiftyOne의 to_torch() 메서드를 사용합니다.
from fiftyone.utils.torch import GetItem import torch class WorkerSafetyGetItem(GetItem): def __call__(self, d): return { "embedding": torch.tensor(d.get("tl_embedding"), dtype=torch.float32), "label_idx": torch.tensor( self.label_to_idx.get(d.get("ground_truth").label, -1), dtype=torch.long ), } def create_dataloader(dataset, batch_size=4): indexed_view = dataset.exists("tl_video_id") # FiftyOne 뷰에서 직접 PyTorch 데이터셋 생성 torch_dataset = indexed_view.to_torch(WorkerSafetyGetItem(LABEL_TO_IDX)) return DataLoader(torch_dataset, batch_size=batch_size, shuffle=True)
결과물인 DataLoader는 미리 추출되어 연산된 임베딩과 정수형 라벨의 배치를 생성합니다. 인덱싱 과정에서 이미 Marengo 모델에 의해 임베딩이 미리 추출되어 있으므로, 실제 분류 모델 학습 시에는 무거운 비디오 피처 추출 과정을 생략할 수 있습니다. 이 가벼운 임베딩 위에서 MLP나 선형 프로브와 같은 경량화 분류기를 몇 시간 대신 몇 초 만에 쉽게 학습시킬 수 있으며, 생성된 라벨도 폴더 이름 등에서 무작위로 유추한 것이 아닌 Pegasus가 실제 비디오 내용을 추론해 얻은 것이므로 강력한 정합성을 가집니다.
샘플 데이터셋은 GitHub 리포지토리에서 찾아볼 수 있습니다.
이것이 왜 중요할까요: 데이터 관리에서 비즈니스 인텔리전스로
기술 파이프라인 구축을 완료했으니, 이 워크플로우가 기업 수준에서 왜 혁신적인 가치를 가지는지 큰 그림에서 생각해 볼 필요가 있습니다.
모델 개발 가속화. 가공되지 않은 보안 카메라 영상으로부터 모델 학습으로 이어지는 기존 과정은 수 주간의 어노테이션 작업, 반복적인 감수 프로세스, 그리고 거대한 비용 축적을 야기했습니다. 이 파이프라인은 그 복잡한 과정을 단 하나의 스크립트 실행으로 압축합니다. 내보낸 데이터셋에는 이미 계산된 고품질 임베딩과 완전한 정합성을 가진 라벨이 포함되어 있어 즉시 모델을 학습시킬 수 있습니다. 안전 분류 모델을 신속하게 도입 및 실험하는 개발 팀에서 어노테이션 병목을 마침내 해결하게 됩니다.
시스템의 구조적 패턴 파악. 안전 관리자 관점에서 이 클러스터링 시각화 뷰는 단순 데이터 점검용 화면을 넘어 실질적인 분석 대시보드가 됩니다. 예를 들어 KMeans가 유독 "비상구 폐쇄" 항목 쪽으로 비정상적으로 큰 클러스터를 분리해 냈다면, 이는 단순히 지나갈 일회성 위반이 아닙니다. 일방적인 주의 조치에 머무르는 것이 아닌 프로세스 전반의 시스템적인 대책 마련이 필요한 지점입니다. 일별, 주별 등으로 배치 작업을 정기적으로 수행하면 보안 카메라 영상을 수동적인 책임 관리 기록물에서 능동적인 현장 데이터 정보원으로 전환시킬 수 있습니다. 시간에 따라 특정 위반 사항 발생 추이가 감소하거나 늘어나는지 정밀 모니터링하고, 사내 안전 대책의 실효성을 세밀하게 평가할 수 있게 됩니다.
어노테이션 비용의 극적인 절감. 안전이 필수적인 고정밀 데이터셋을 위해 비디오 데이터를 수동 어노테이션할 때는 일반적으로 검토 영상 시간당 25~50달러의 큰 부담이 따릅니다. 본 파이프라인은 이 막대한 직접 인건비 지출 구조를 효율적인 API 연산으로 대체합니다. 특히 수 백대 규모의 카메라 인프라를 상시 가동하는 기업들에게 이 고정비의 격차는 기하급수적으로 벌어지게 됩니다. 더욱이 이 라벨들은 완벽하게 복제 및 재현 가능하여, 새로 들어오는 영상들에 동일한 파이프라인을 그대로 적용하면 수동 어노테이션 방식 특유의 검토자별 편차 문제 없이 항상 데이터 품질의 일관성을 유지할 수 있습니다.

결론
본 튜토리얼을 통해 원시 영상 수집부터 라벨링이 완료된 PyTorch DataLoader 확보까지의 통합 파이프라인을 상세히 구축해 보았습니다. TwelveLabs의 Marengo 3.0은 시각, 청각 및 맥락적 요소를 아우르는 비디오 멀티모달 벡터 임베딩 추출이라는 복잡한 고난이도 작업을 매끄럽게 수행했습니다. Pegasus 1.2 모델은 기호에 불과한 추상적 클러스터를 사람이 인식하고 활용할 수 있는 정확한 업무용 분류 카테고리로 신속하고 명확하게 변환했습니다. FiftyOne은 이 모든 정성적 프로세스를 종합적인 시각화 및 내보내기 인프라를 통해 검증 가능하며 즉시 학습 수립이 가능한 구조로 설계하도록 전방위로 도왔습니다.
전체 통합 소스 코드는 GitHub에 공개되어 있습니다. 로컬에 제품 환경을 구축하고, 실제 가동 중인 보안 카메라 영상 경로를 입력한 후 필요에 따라 Pegasus의 라벨링 프롬프트 부분을 내부 기준에 맞게 손쉽게 수정해 시작해 보세요.
지능형 영상 검색기능부터 안전 규정 규제 준수 모니터링, 하이라이트 영상 자율 추출까지 TwelveLabs의 강력한 비디오 이해 API를 통해 한 차원 더 높은 차세대 서비스를 설계해 보고 싶으시다면, 제공되는 TwelveLabs 가이드 문서를 방문하시거나 TwelveLabs 팀에 언제든 문의를 남겨 함께 비즈니스 활용 방안을 논의해 보세요.
추가 참고 자료
소개
매년 수천 테라바이트의 보안 카메라 영상이 공장의 IP 카메라를 통해 흘러갑니다. 그 데이터의 어딘가에는 지난주 화요일 아찔한 사고를 유발할 뻔했던 지게차 위반 상황, 야간 근무 조의 개인보호장구(PPE) 미착용 패턴, 그리고 6주 동안 아무도 눈치채지 못한 채 방치된 비상구 폐쇄 상황이 기록되어 있습니다. 영상은 존재하지만, 라벨은 존재하지 않습니다.
이러한 가공되지 않은 영상 데이터 양과 실제 사용 가능한 학습 데이터 사이의 간극은 작업자 안전을 위한 컴퓨터 비전을 배포하는 데 있어 가장 큰 병목 구간입니다. AWS SageMaker Ground Truth, Scale AI 또는 전담 어노테이션 팀과 같은 기존 솔루션은 수많은 사람을 투입하여 이 문제를 해결하고자 합니다. 이 방식은 효과가 있지만, 검토에 드는 비용이 영상 시간당 25~50달러에 달하며 카메라 대수에 비례해 선형적으로 증가합니다. 카메라 수가 두 배로 늘어나면 어노테이션 예산도 두 배로 늘어납니다.

이 워크플로우를 완전히 뒤집을 수 있다면 어떨까요? 사고를 찾기 위해 영상을 일일이 돌려보는 대신, 영상이 스스로 구조화되어 유사한 이벤트를 의미별로 그룹화하고 발견한 내용을 설명해 줄 수 있다면 어떨까요?
이 튜토리얼에서 바로 이 시스템을 구축해 볼 것입니다. TwelveLabs의 비디오 이해 모델과 FiftyOne의 데이터셋 관리 플랫폼을 결합하여 다음과 같은 파이프라인을 구축해 보겠습니다.
TwelveLabs API를 사용하여 가공되지 않은 보안 카메라 영상을 수집 및 인덱싱합니다.
비디오 콘텐츠를 고밀도 벡터로 표현하는 멀티모달 임베딩(Marengo 3.0)을 추출하여 시각, 청각 및 콘텍스트 신호를 동시에 캡처합니다.
KMeans를 사용해 비디오를 의미론적으로 클러스터링하고, 사전 정의된 카테고리 없이도 유사한 안전 사고를 그룹화합니다.
생성형 비디오 추론(Pegasus 1.2)을 사용하여 각 클러스터에 라벨을 자동으로 지정하고, 추상적인 클러스터 ID를 사람이 읽을 수 있는 안전 분류명으로 변환합니다.
품질 감수를 위해 FiftyOne에서 결과를 시각화한 다음, 학습에 바로 사용 가능한 PyTorch 데이터셋으로 내보냅니다.
핵심 인사이트: TwelveLabs는 비디오를 프레임 단위로 처리하지 않습니다. Marengo는 비디오를 압축된 멀티모달 표현(TwelveLabs 팀이 "부피로서의 비디오"라고 부르는 개념)으로 인코딩하여 오디오, 텍스트, 모션 및 시각적 콘텍스트를 단일 임베딩 공간으로 통합합니다. 이것이 바로 의미론적 클러스터링이 작동하는 이유입니다. 겉보기에는 다르게 보이지만 의미가 같은 영상들(예: 서로 다른 카메라 각도에서 촬영된, 지게차가 보행자의 진로를 방해하는 두 영상)은 임베딩 공간에서 결국 서로 가까운 위치에 있게 됩니다.
아래의 가이드를 시청하여 완성된 애플리케이션의 빠른 데모를 확인해 보세요.

사전 요구 사항
이 프로젝트를 진행하려면 다음 세 가지가 필요합니다.
Python 3.8+: Python 다운로드
TwelveLabs API 키: 인증 문서 (무료 티어 이용 가능)
HuggingFace 계정 (선택 사항): 저희가 사용하는 것과 동일한 보안 카메라 데이터셋(Voxel51/Safe_and_Unsafe_Behaviours)을 사용하려는 경우에만 필요합니다.
리포지토리를 클론하고 종속성을 설치합니다.
>> git clone https://github.com/nathanchess/visual-ai-worker-safety-kit >> cd
아키텍처 개요
코드를 작성하기 전에 전체 파이프라인을 시각적으로 살펴보는 것이 도움이 됩니다. 상위 수준에서 이 프로젝트는 두 부분으로 나뉩니다. TwelveLabs가 지능형 처리(임베딩 및 라벨)를 제공하고, FiftyOne이 인프라(시각화, 필터링 및 내보내기)를 구축합니다.

전체 화면 보기 (Lucid App): [Voxel51 x TwelveLabs] - 의미론적 데이터셋 큐레이터 툴
Marengo 3.0은 TwelveLabs의 인코더 모델로, 비디오를 보여지고 들리는 것뿐만 아니라 맥락적으로 암시되는 것까지 캡처하는 풍부한 멀티모달 임베딩으로 압축합니다. Pegasus 1.2는 추론 모델로, 이 압축된 표현을 탐색하여 이벤트를 식별하고, 행동을 분류하며, 자연어 설명문을 생성합니다. 그 다운스트림에 위치한 FiftyOne은 이 임베딩과 라벨을 수집하여 대화형 시각화, 유사성 탐색 및 PyTorch로의 직접 내보내기 기능을 제공합니다.
전체 소스 코드는 GitHub 리포지토리에서 확인할 수 있습니다. 아래의 가이드는 main.py의 구조를 따릅니다.
1단계: 지능형 비디오 수집
Hugging Face에서 보안 카메라 데이터셋을 로드하는 것으로 시작합니다. FiftyOne의 ViewField(F로 임포트됨)를 사용하면 기본 데이터를 수정하지 않고도 선언적 필터를 빌드할 수 있습니다. 여기서는 세 가지 필터가 중요합니다. 학습용 분할(train split)로 한정하고, 4초 미만의 클립(유의미한 임베딩을 만들기에는 너무 짧음)은 제외하며, 이미 인덱싱한 비디오는 건너뜁니다.
from fiftyone import ViewField as F from fiftyone.utils.huggingface import load_from_hub def load_or_create_dataset(dataset_name): # Hugging Face Hub에서 직접 로드 return load_from_hub("Voxel51/Safe_and_Unsafe_Behaviours", name=dataset_name) def ingest_videos(client, index_id, dataset, min_duration=4.0): # 뷰 생성: 오직 학습 분할(Train split)만, 길이 4초 이상, 아직 인덱싱되지 않은 데이터 base_view = ( dataset .match_tags("train") .match(F("metadata.duration") >= min_duration) .match(~F("tl_video_id").exists()) # 멱등성 확인 ) # ... 반복 로직 ...
이 ~F("tl_video_id").exists() 필터는 주의 깊게 살펴볼 가치가 있습니다. 실제 프로덕션 환경에서는 새 영상이 들어올 때마다 이 파이프라인을 매일 실행할 수 있습니다. 이 필터 덕분에 동일한 비디오를 두 번 인덱싱하는 불필요한 비용을 지불하지 않게 됩니다. 샘플에 이미 TwelveLabs 비디오 ID가 저장되어 있다면, 뷰는 이를 조용히 건너뜁니다. 별도의 추적 코드 없이 데이터 레이어에서 멱등성이 보장됩니다.
2단계: 임베딩 추출 및 FiftyOne에 데이터 입력
필터링된 뷰가 구축되면 샘플을 순회하며 각 비디오를 TwelveLabs에 업로드해 인덱싱합니다. FiftyOne의 iter_samples(autosave=True)는 데이터베이스 쓰기를 자동으로 배치 처리하므로 수천 개의 클립을 처리할 때 매우 유용합니다. 매 샘플마다 데이터베이스를 왕복하는 오버헤드를 방지해 줍니다.
# 필터링된 뷰를 순회 for sample in label_view.iter_samples(autosave=True, progress=True): try: # TwelveLabs에 업로드하고 반환된 비디오 ID를 샘플에 저장 sample["tl_video_id"] = index_video_to_twelvelabs( client, index_id, sample ) print(f" ✓ {sample.filename}") except Exception as e: print(f" ✗ {sample.filename}: {e}")
인덱싱이 완료되면 각 비디오에 대한 Marengo 3.0 임베딩을 가져옵니다. 이는 시각적으로 발생하는 일, 들리는 소리, 그리고 둘 사이의 맥락적 관계 등 각 클립의 전체 멀티모달 콘텐츠를 인코딩하는 고밀도 벡터입니다. 각 프레임을 독립적으로 처리하는 이미지 전용 모델의 임베딩과 달리, Marengo의 임베딩은 시간적 연속성을 포착합니다. "작업자가 보안경을 벗는 행동"과 "작업자가 보안경을 쓰는 행동"은 단일 프레임 상으로는 동일하게 보일 수 있지만, 의미상 확연히 다른 벡터를 생성합니다.
3단계: 비지도 의미론적 클러스터링
여기가 바로 이 파이프라인의 핵심입니다. 우리는 사전에 정의한 카테고리가 없습니다. 이 영상에 어떤 유형의 안전 위반 사항이 존재하는지, 혹은 그 모습이 구체적으로 어떤지 전혀 모릅니다. 대신, 임베딩 공간에 KMeans 클러스터링을 적용하여 데이터가 스스로 구조화되도록 유도합니다.
def cluster_and_label(client, dataset, embeddings, num_clusters=8): print(f"Clustering {len(embeddings)} videos into {num_clusters} clusters...") # 1. 임베딩 클러스터링 kmeans = KMeans(n_clusters=num_clusters, random_state=0) cluster_labels = kmeans.fit_predict(embeddings) # 2. Pegasus(생성형 AI)를 사용해 클러스터를 의미론적 라벨에 매핑 cluster_label_map = {} for cluster_idx in np.unique(cluster_labels): # 클러스터에서 대표 비디오를 선택하고 Pegasus에 라벨 지정을 요청 representative_video_id = get_video_id_for_cluster(cluster_idx) cluster_label_map[cluster_idx] = generate_label(client, representative_video_id) # 3. FiftyOne 샘플 일괄 업데이트 indexed_view = dataset.exists("tl_video_id") classifications = [Classification(label=cluster_label_map[c]) for c in cluster_labels] indexed_view.set_values("pred_cluster", classifications)
set_values()에 대한 기술적 참고 사항: 이 메서드는 단일 대량 작업으로 전체 데이터셋을 업데이트합니다. 샘플을 하나씩 순회하며 sample.save()를 호출하는 비효율적인 방식은 규모가 커질수록 처리 속도가 크게 느려집니다. 수천 개의 비디오를 다룰 때는 이러한 처리 방식의 차이가 매우 중요합니다.
왜 여기서 의미론적 클러스터링이 유의미한 그룹을 만들어낼 수 있을까요? Marengo의 임베딩은 비디오를 프레임별 특징들의 단순 집합이 아니라 압축된 멀티모달 표현으로 인코딩하기 때문입니다. 지게차가 통로를 막고 있는 모습을 담은 두 개의 클립이 서로 다른 각도, 다른 시간대, 다른 소음 환경에서 촬영되었더라도, 의미상 같은 맥락을 공유하므로 임베딩 공간에서 인접한 위치에 도달하게 됩니다. 오직 시각적 유사성에만 의존하는 기존 방식이었다면 이 두 영상은 서로 흩어졌을 것입니다.
4단계: Pegasus를 통한 제로샷 자동 라벨링
"클러스터 3"과 같은 클러스터 ID는 수학적으로는 유용하지만 실제 업무상으로는 아무런 의미가 없습니다. 우리에게는 "지게차 우선 통행 위반"과 같은 명확한 이름이 필요합니다. 여기서 Pegasus 1.2가 등장합니다.
각 클러스터에서 대표 비디오를 하나씩 선택해 구조화된 프롬프트와 함께 Pegasus에 제공합니다. Pegasus는 단일 프레임이나 대본 파일이 아닌 비디오 전체를 추론하여 정확한 안전 분류명을 매핑해 줍니다. 미세 조정(fine-tuning)은 필요 없습니다. 이것이 바로 제로샷 생성형 추론입니다. Pegasus는 이 특정 보안 카메라 영상을 이전에 본 적이 없음에도 불구하고 기존 안전 프로토콜과 일치하는 라벨을 생성해 냅니다.
CLUSTER_LABEL_PROMPT = """ Analyze this workplace safety video and classify it as exactly ONE of the following labels. UNSAFE BEHAVIORS: - Safe Walkway Violation - Unauthorized Intervention - Opened Panel Cover ... Return ONLY the exact label name. """
프롬프트는 Pegasus가 미리 설정된 안전 카테고리 체계 내에서만 판단하도록 제한합니다. 이는 의도된 설계입니다. 실제 작업 환경에서 안전 관리자는 임의의 서술형 설명이 아닌, 회사에서 기존에 사용 중인 사고 분류 시스템에 정확히 대응하는 라벨이 필요하기 때문입니다. 프롬프트를 수정하여 조직의 구체적인 안전 수칙에 맞게 이 카테고리 체계를 손쉽게 조정할 수 있습니다. 재학습도, 새로운 모델도 필요 없이 라벨 문자열 세트만 변경하면 됩니다.
5단계: UMAP을 통한 대화형 임베딩 시각화
수치와 라벨도 유용하지만, 컴퓨터 비전 작업에서는 데이터를 직접 눈으로 확인할 수 있어야 합니다. FiftyOne의 Brain 모듈은 고차원 임베딩의 2D UMAP 프로젝션을 계산하여, 각 점이 비디오를 나타내고 색상이 자동으로 생성된 라벨에 대응하는 대화형 산점도(scatterplot)를 시각화합니다.

이 시각화에서 확인해야 할 핵심 사항: 촘촘하게 잘 분리된 클러스터는 임베딩이 깔끔하고 유의미한 그룹화를 수행했음을 나타냅니다. 즉, 모델이 "비상구 폐쇄"와 "PPE 위반"을 확실하게 구별하고 있다는 의미입니다. 반면 겹쳐져 있는 클러스터는 카테고리를 한 단계 다듬어야 할 필요가 있음을 시사합니다 (예를 들어, "잘못된 리프팅"과 "인체공학적 위반"은 서로 명확하게 나누기에는 너무 유사할 수 있습니다). 클러스터 중심에서 멀리 떨어진 아웃라이어(이상치)들은 희귀 사례 점검이나 잘못 라벨링된 데이터를 골라내기 위해 수동으로 조사해 볼 가치가 있습니다.
이 단계는 단순히 시각적 효과만을 위한 것이 아닙니다. 데이터를 저장 및 내보내기 전에 임베딩을 시각적으로 점검하면, 자칫 학습 파이프라인으로 그대로 흘러 들어갔을 데이터 품질 문제를 예방하고 바로잡을 수 있습니다. 여기서의 5분 남짓한 검토 시간이 다운스트림 개발 단계에서의 오랜 디버깅 시간을 아껴 줍니다.
6단계: PyTorch로 모델 학습 데이터 내보내기
수집된 데이터셋은 실제 인공지능 모델을 학습시킬 수 있을 때 비로소 그 가치를 발휘합니다. 문자열 라벨을 텐서(tensor)에 매핑하고 실시간으로 미리 계산된 임베딩을 검색하는 맞춤형 GetItem 클래스를 구현해, FiftyOne의 to_torch() 메서드를 사용합니다.
from fiftyone.utils.torch import GetItem import torch class WorkerSafetyGetItem(GetItem): def __call__(self, d): return { "embedding": torch.tensor(d.get("tl_embedding"), dtype=torch.float32), "label_idx": torch.tensor( self.label_to_idx.get(d.get("ground_truth").label, -1), dtype=torch.long ), } def create_dataloader(dataset, batch_size=4): indexed_view = dataset.exists("tl_video_id") # FiftyOne 뷰에서 직접 PyTorch 데이터셋 생성 torch_dataset = indexed_view.to_torch(WorkerSafetyGetItem(LABEL_TO_IDX)) return DataLoader(torch_dataset, batch_size=batch_size, shuffle=True)
결과물인 DataLoader는 미리 추출되어 연산된 임베딩과 정수형 라벨의 배치를 생성합니다. 인덱싱 과정에서 이미 Marengo 모델에 의해 임베딩이 미리 추출되어 있으므로, 실제 분류 모델 학습 시에는 무거운 비디오 피처 추출 과정을 생략할 수 있습니다. 이 가벼운 임베딩 위에서 MLP나 선형 프로브와 같은 경량화 분류기를 몇 시간 대신 몇 초 만에 쉽게 학습시킬 수 있으며, 생성된 라벨도 폴더 이름 등에서 무작위로 유추한 것이 아닌 Pegasus가 실제 비디오 내용을 추론해 얻은 것이므로 강력한 정합성을 가집니다.
샘플 데이터셋은 GitHub 리포지토리에서 찾아볼 수 있습니다.
이것이 왜 중요할까요: 데이터 관리에서 비즈니스 인텔리전스로
기술 파이프라인 구축을 완료했으니, 이 워크플로우가 기업 수준에서 왜 혁신적인 가치를 가지는지 큰 그림에서 생각해 볼 필요가 있습니다.
모델 개발 가속화. 가공되지 않은 보안 카메라 영상으로부터 모델 학습으로 이어지는 기존 과정은 수 주간의 어노테이션 작업, 반복적인 감수 프로세스, 그리고 거대한 비용 축적을 야기했습니다. 이 파이프라인은 그 복잡한 과정을 단 하나의 스크립트 실행으로 압축합니다. 내보낸 데이터셋에는 이미 계산된 고품질 임베딩과 완전한 정합성을 가진 라벨이 포함되어 있어 즉시 모델을 학습시킬 수 있습니다. 안전 분류 모델을 신속하게 도입 및 실험하는 개발 팀에서 어노테이션 병목을 마침내 해결하게 됩니다.
시스템의 구조적 패턴 파악. 안전 관리자 관점에서 이 클러스터링 시각화 뷰는 단순 데이터 점검용 화면을 넘어 실질적인 분석 대시보드가 됩니다. 예를 들어 KMeans가 유독 "비상구 폐쇄" 항목 쪽으로 비정상적으로 큰 클러스터를 분리해 냈다면, 이는 단순히 지나갈 일회성 위반이 아닙니다. 일방적인 주의 조치에 머무르는 것이 아닌 프로세스 전반의 시스템적인 대책 마련이 필요한 지점입니다. 일별, 주별 등으로 배치 작업을 정기적으로 수행하면 보안 카메라 영상을 수동적인 책임 관리 기록물에서 능동적인 현장 데이터 정보원으로 전환시킬 수 있습니다. 시간에 따라 특정 위반 사항 발생 추이가 감소하거나 늘어나는지 정밀 모니터링하고, 사내 안전 대책의 실효성을 세밀하게 평가할 수 있게 됩니다.
어노테이션 비용의 극적인 절감. 안전이 필수적인 고정밀 데이터셋을 위해 비디오 데이터를 수동 어노테이션할 때는 일반적으로 검토 영상 시간당 25~50달러의 큰 부담이 따릅니다. 본 파이프라인은 이 막대한 직접 인건비 지출 구조를 효율적인 API 연산으로 대체합니다. 특히 수 백대 규모의 카메라 인프라를 상시 가동하는 기업들에게 이 고정비의 격차는 기하급수적으로 벌어지게 됩니다. 더욱이 이 라벨들은 완벽하게 복제 및 재현 가능하여, 새로 들어오는 영상들에 동일한 파이프라인을 그대로 적용하면 수동 어노테이션 방식 특유의 검토자별 편차 문제 없이 항상 데이터 품질의 일관성을 유지할 수 있습니다.

결론
본 튜토리얼을 통해 원시 영상 수집부터 라벨링이 완료된 PyTorch DataLoader 확보까지의 통합 파이프라인을 상세히 구축해 보았습니다. TwelveLabs의 Marengo 3.0은 시각, 청각 및 맥락적 요소를 아우르는 비디오 멀티모달 벡터 임베딩 추출이라는 복잡한 고난이도 작업을 매끄럽게 수행했습니다. Pegasus 1.2 모델은 기호에 불과한 추상적 클러스터를 사람이 인식하고 활용할 수 있는 정확한 업무용 분류 카테고리로 신속하고 명확하게 변환했습니다. FiftyOne은 이 모든 정성적 프로세스를 종합적인 시각화 및 내보내기 인프라를 통해 검증 가능하며 즉시 학습 수립이 가능한 구조로 설계하도록 전방위로 도왔습니다.
전체 통합 소스 코드는 GitHub에 공개되어 있습니다. 로컬에 제품 환경을 구축하고, 실제 가동 중인 보안 카메라 영상 경로를 입력한 후 필요에 따라 Pegasus의 라벨링 프롬프트 부분을 내부 기준에 맞게 손쉽게 수정해 시작해 보세요.
지능형 영상 검색기능부터 안전 규정 규제 준수 모니터링, 하이라이트 영상 자율 추출까지 TwelveLabs의 강력한 비디오 이해 API를 통해 한 차원 더 높은 차세대 서비스를 설계해 보고 싶으시다면, 제공되는 TwelveLabs 가이드 문서를 방문하시거나 TwelveLabs 팀에 언제든 문의를 남겨 함께 비즈니스 활용 방안을 논의해 보세요.




