A newer version of the Gradio SDK is available: 6.15.2
Auto-Mode ๋ค์ค GPU ๋ณ๋ ฌ ์ฒ๋ฆฌ ๊ตฌํ ๊ณํ
์์ฑ์ผ: 2026-04-25 ๋์: HuggingFace Spaces ์ dedicated multi-GPU ํ๋์จ์ด (์: 4ร A100-80GB) ์์ Auto-Mode ํ์ ์ ๋ก๋ํ N ๊ฐ์ ์์์ ๊ฐ GPU 1๋๋น 1์์์ฉ ๋์์ ๋ถํ (segment) ํ๋๋ก ๊ตฌ์กฐ ๋ณ๊ฒฝ.
0. ๋ฐฐ๊ฒฝ / ํ์ฌ ๊ตฌ์กฐ ํ๋์ ๋ณด๊ธฐ
| ํญ๋ชฉ | ํ์ฌ (sequential) |
|---|---|
| Auto-Mode ์ง์ ์ | app.py:5033 _auto_mode_process(file_list, text_prompt) |
| ๋จ์ผ ์์ ๋ถํ | app.py:4896 @spaces.GPU(duration=119) def segment_video(...) |
| ์์ปค ํ๋ก์ธ์ค | mp.get_context("spawn").Process(target=_segment_video_worker_entry, ...) (app.py:4854) |
| ์ฝ์ด ๋ก์ง | app.py:4239 _segment_video_core(...) (chunk-wise SAM3 ์ถ๋ก ) |
| ๋ชจ๋ธ ์ธ์คํด์คํ | app.py:4453 predictor_cls = _get_sam3_predictor_cls(); predictor = predictor_cls(...) |
| ๊ฒฐ๊ณผ ์ ์ฅ ๋๋ ํ ๋ฆฌ | build/downloads/ (_persist_for_download ํธ์ถ) |
| ์งํ ํต์ | mp.Queue ๋ก progress / status / result / error ๋ฉ์์ง ์คํธ๋ฆฌ๋ฐ |
| ์์ ๊ฐ ์ฒ๋ฆฌ | for path in paths: ์ง๋ ฌ ๋ฃจํ, GPU cleanup (_cleanup_cuda_cache()) ํ ๋ค์ ์์ ์ฒ๋ฆฌ |
HuggingFace Spaces ํ๋์จ์ด / spaces.GPU ๋์
@spaces.GPU๋ฐ์ฝ๋ ์ดํฐ๋Config.zero_gpu(=SPACES_ZERO_GPU=true) ์ธ ๊ฒฝ์ฐ์๋ง ZeroGPU ์ฌ๋ผ์ด์ค ํ ๋น ๋ก์ง์ด ๋ถ๋๋ค (spaces/zero/decorator.py:83). dedicated GPU Space (4รA100 ๋ฑ) ์์๋ ๋ฐ์ฝ๋ ์ดํฐ๊ฐ no-op ์ด๋ฉฐ, ์ผ๋ฐ Python ํ๋ก์ธ์ค๊ฐ CUDA 4 ์ฅ ๋ชจ๋๋ฅผ ์ง์ ๋ณธ๋ค (torch.cuda.device_count() == 4).- ZeroGPU(MIG slice) ๋ชจ๋๋ ํ ๋ฒ์ ํ GPU ์ฌ๋ผ์ด์ค๋ง ํ ๋น๋๋ฏ๋ก ์ด ๊ณํ์ dedicated multi-GPU ํ๋์จ์ด ์ ์ ์ด๋ค. ZeroGPU ํ๊ฒฝ์์ ์๋์ผ๋ก ๊ธฐ์กด ์ง๋ ฌ ๊ฒฝ๋ก๋ก fallback ํ๋ค.
๊ฒฉ๋ฆฌ(isolation) ์๊ตฌ์ฌํญ ์ ๋ฆฌ
| ์์ | ์ถฉ๋ ๊ฐ๋ฅ์ฑ | ํด๊ฒฐ ๋ฐฉ๋ฒ |
|---|---|---|
| GPU ๋ฉ๋ชจ๋ฆฌ / ์ปจํ ์คํธ | ๊ฐ์ device ์์์ 4 ์์์ด ๋ชจ๋ธ์ ๋์ ์ ์ฌ โ OOM, ์ปจํ ์คํธ ๊ฐ์ญ | ์์ปค๋ง๋ค CUDA_VISIBLE_DEVICES=N ํ๊ฒฝ๋ณ์๋ก 1์ฅ๋ง ๋ณด์ด๊ฒ ๊ณ ์ |
sam3.* ๋ชจ๋ in-process ์บ์ (_SAM3_PREDICTOR_CLS, _LAST_SEG_CACHE, cached_frame_outputs ๋ฑ) |
๊ฐ์ ์ธํฐํ๋ฆฌํฐ ๋ด 4-way concurrent ํธ์ถ์ ์ํ๊ฐ ์ฝํ | spawn ๋ฐฉ์์ ๋ณ๋ ํ๋ก์ธ์ค โ ๋ชจ๋ ์ํ ์์ฒด๊ฐ ๋ถ๋ฆฌ๋จ |
tempfile.mkdtemp() (chunk ์
๋ ฅ dir, ํธ๋ฆฌ๋ฐ๋ mp4) |
mkdtemp ๋ ์๋์ผ๋ก ์ถฉ๋ ์๋ ์ด๋ฆ ์์ฑ โ ์์ |
์ถ๊ฐ ์กฐ์น ๋ถํ์ |
build/downloads/ ์ฐ์ถ๋ฌผ ํ์ผ๋ช
|
๋์ ์์ ์์์ด ๋์ผ timestamp โ auto_mode_results_YYYYMMDD_HHMMSS.zip / *_overlay.mp4 ์ถฉ๋ |
ํ์ผ๋ช
์ short uuid (uuid.uuid4().hex[:8]) + ์์ ์ธ๋ฑ์ค ์ถ๊ฐ |
| ๋ชจ๋ธ ์ฒดํฌํฌ์ธํธ ๋ค์ด๋ก๋ / BPE vocab | 4 ์์ปค๊ฐ ๋์์ ๊ฐ์ ํ์ผ์ download/write โ race | ๋ถ๋ชจ(๋ฉ์ธ process)์์ ์ฌ์ 1ํ ๋ณด์ฅ ํ ์์ปค๋ read-only |
.zerogpu/tensors ๋ฑ ์บ์ |
dedicated ๋ชจ๋์์ ZeroGPU ์บ์๋ ์ฌ์ฉ ์ ํจ | ์ํฅ ์์ |
sam3/ ๋๋ ํ ๋ฆฌ ์์ฒด |
Python import ๋ ํ๋ก์ธ์ค๋ง๋ค ๋ ๋ฆฝ โ ๋๋ ํ ๋ฆฌ ์ฌ๋ณธ ๋ถํ์ | ์ฌ๋ณธ ์์ฑ X |
๊ฒฐ๋ก
sam3ํด๋ ๋ณต์ ๋ ํ์ ์๋ค. ๊ฒฉ๋ฆฌ ๋จ์๋ โํ๋ก์ธ์คโ ํ ๋จ๊ณ๋ก ์ถฉ๋ถํ๋ค.- ๊ฐ ์์์ด 1 ๊ฐ์ spawn child process ์์ ์คํ๋๋ฉฐ, child ์ง์
์งํ (torch import ์ )
CUDA_VISIBLE_DEVICES๋ฅผ 1 ์ฅ์ผ๋ก ์ขํ๋ค โ child ์ ์ฅ์์ ํญ์cuda:0ํ ๊ฐ๋ง ์กด์ฌ โ ๋ชจ๋ธ/SAM3 ์ฝ๋์cuda/cuda:0ํ๋์ฝ๋ฉ ์ด๋๋ ์์ . - ๋ถ๋ชจ ํ๋ก์ธ์ค๋ GPU ์ฌ์ฉ X. ๋จ์ํ 4-์ฌ๋กฏ ํ์ ์ด์ํ๋ ๋์คํจ์ฒ ์ญํ ๋ง ์ํ. ๋ชจ๋ ๋ฌด๊ฑฐ์ด import ๋ ์์ปค ์์์.
1. ๋์์ธ ๊ฐ์
1.1 ์์ปค ํ ๊ตฌ์กฐ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Gradio main process (no torch CUDA usage) โ
โ โโ _auto_mode_process() generator โ
โ โโ ParallelSegmentDispatcher โ
โ โ โโ pool of N workers (N = min(num_gpus, num_videos)) โ
โ โ โโ submit queue (video_path โ free worker) โ
โ โ โโ event queue (progress / status / result / err) โ
โ โ โโ per-video state: gpu_idx, started_at, last_pctโฆ โ
โ โโ yields UI updates (status / per-video progress / files)โ
โโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ spawn child ร N
โผ
โโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโ
โ Worker GPU 0 โ โ Worker GPU 1 โ โ Worker GPU N-1 โ
โ CUDA_VISIBLE=0 โ โ CUDA_VISIBLE=1 โ โ CUDA_VISIBLE=N-1โ
โ runs โ โ runs โ โ runs โ
โ _segment_video โ โ _segment_video โ โ _segment_video โ
โ _core(...) โ โ _core(...) โ โ _core(...) โ
โโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโ
- ๊ฐ ์์ปค๋ ์์ ์ด์์๋ โpersistent workerโ๋ก ์ด์ํด ๋ชจ๋ธ ๊ฐ์ค์น ๋ก๋ฉ ๋น์ฉ์ ์ฒซ ์์ 1ํ๋ง ๋ถ๋ดํ๋ค (์ ํ ์ต์ ํ: ยง6.2). 1์ฐจ ๊ตฌํ์ ๋จ์ํจ์ ์ํด ์์๋ง๋ค ์์ปค ์๋ก spawn ํ๋ โfresh-per-videoโ ๊ตฌ์กฐ๋ก ์์ โ ์์ ํ ํ ์ฌ์ฌ์ฉํ์ผ๋ก ์ ํ.
- N ๊ฐ์ ์์์ด 4 GPU ๋ณด๋ค ๋ง์ผ๋ฉด, ํ + ํ ๊ตฌ์กฐ๋ผ ์๋์ผ๋ก ์ง๋ ฌํ๋๋ค (ํ GPU ๊ฐ ํ๋ ๋๋ด๋ฉด ๋ค์ ์์์ ๋ฐ์).
1.2 ์์ปค entry ๋ชจ๋ ๋ถ๋ฆฌ โ parallel_segment_worker.py
์ ๋ณ๋ ํ์ผ์ด ํ์ํ๊ฐ:
- ํ์ฌ worker target (
_segment_video_worker_entry) ์app.py๋ด๋ถ ํจ์๋ค. - spawn ์์ ํ๋ก์ธ์ค๊ฐ ์ด target ์ unpickle ํ๋ ค๋ฉด
app.py๋ฅผ import ํด์ผ ํ๊ณ ,app.py:4์์import torch๊ฐ ์ฆ์ ์คํ๋๋ค. - ๊ทธ ์์ ์ ์์์ด ์์ง
os.environ["CUDA_VISIBLE_DEVICES"]๋ฅผ ์ขํ๊ธฐ ์ ์ด๋ฏ๋ก, torch ๊ฐ 4 ์ฅ ๋ชจ๋ ๋ณด์ด๋ ์ํ๋ก cuda runtime ์ ์ด๊ธฐํํ๋ค โ ์ฐ๋ฆฌ๊ฐcuda:0๋ง ์ฐ๋ ค ํด๋ ๋ค๋ฅธ ์ฅ์น ์ปจํ ์คํธ๊ฐ ๋ฐ๋ผ์จ๋ค. - ํด๊ฒฐ: ์์ปค entry ๋ฅผ torch ๋ฅผ top-level ์์ import ํ์ง ์๋ ์ ํ์ผ๋ก ๋ถ๋ฆฌ. ์์์ด ๊ทธ ํ์ผ๋ง import ํ ๋ค, ํจ์ ๋ณธ๋ฌธ ์ฒซ ์ค์์
os.environ["CUDA_VISIBLE_DEVICES"]์ค์ ํ๊ณ , ๊ทธ ๋ค์ torch / app ์ import.
# parallel_segment_worker.py (intentionally minimal top-level imports)
import os
import sys
import traceback
def worker_main(gpu_index, args, progress_queue):
os.environ["CUDA_VISIBLE_DEVICES"] = str(gpu_index)
os.environ["SAM3_WORKER_MODE"] = "1" # skip Gradio launch in app.py
os.environ.setdefault("SAM3_CACHE_FRAME_OUTPUTS", "0")
os.environ.setdefault("SAM3_OFFLOAD_TRACKER_STATE_TO_CPU", "1")
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
# NOW it is safe to import torch / app
import torch
if torch.cuda.is_available():
torch.cuda.set_device(0) # only one device visible: cuda:0
from app import _segment_video_core # imports torch but env is already set
(video_path, text_prompt, duration_limit, id_corrections_text,
id_drop_text, id_override_start_sec, show_trails, view_mode) = args
def _progress_cb(val, desc):
progress_queue.put({"type": "progress", "value": val, "desc": desc,
"gpu_index": gpu_index})
def _status_cb(msg):
progress_queue.put({"type": "status", "message": msg,
"gpu_index": gpu_index})
try:
progress_queue.put({"type": "progress", "value": 0.0,
"desc": f"GPU {gpu_index}: starting...",
"gpu_index": gpu_index})
out_path, status, loc_path = _segment_video_core(
video_path, text_prompt, duration_limit,
id_corrections_text=id_corrections_text,
id_drop_text=id_drop_text,
id_override_start_sec=id_override_start_sec,
show_trails=show_trails,
view_mode=view_mode,
progress_callback=_progress_cb,
status_callback=_status_cb,
)
progress_queue.put({"type": "result",
"data": (out_path, status, loc_path),
"gpu_index": gpu_index})
except Exception as exc: # noqa: BLE001
progress_queue.put({"type": "error",
"message": str(exc),
"traceback": traceback.format_exc(),
"gpu_index": gpu_index})
finally:
try:
import torch
if torch.cuda.is_available():
torch.cuda.empty_cache()
torch.cuda.ipc_collect()
except Exception:
pass
1.3 ๋์คํจ์ฒ ํด๋์ค โ app.py ๋ด๋ถ ์ถ๊ฐ
class ParallelSegmentDispatcher:
"""Distribute one video per GPU concurrently and stream events back."""
def __init__(self, num_gpus: int):
self.num_gpus = num_gpus
self.ctx = mp.get_context("spawn")
self.event_queue = self.ctx.Queue()
self.workers: dict[int, mp.Process] = {} # gpu_index -> Process
self.gpu_assignments: dict[int, dict] = {} # gpu_index -> task meta
def submit(self, gpu_index, video_meta, args):
from parallel_segment_worker import worker_main
p = self.ctx.Process(
target=worker_main,
args=(gpu_index, args, self.event_queue),
daemon=False,
)
p.start()
self.workers[gpu_index] = p
self.gpu_assignments[gpu_index] = video_meta
def free_gpu(self, gpu_index):
proc = self.workers.pop(gpu_index, None)
meta = self.gpu_assignments.pop(gpu_index, None)
if proc is not None:
proc.join(timeout=5)
if proc.is_alive():
proc.terminate()
proc.join(timeout=5)
return meta
def shutdown(self):
for gi in list(self.workers.keys()):
self.free_gpu(gi)
1.4 _auto_mode_process ์ ๋ณ๋ ฌ ๋ณํ โ _auto_mode_process_parallel
๋๋ต์ ์๊ณ ๋ฆฌ์ฆ:
๊ฐ์ฉ GPU ์ G = torch.cuda.device_count()
์์ ์ N = len(paths)
slot_count = min(G, N)
dispatcher = ParallelSegmentDispatcher(slot_count)
# 1) ์ด๊ธฐ N ๊ฐ ์ค ์ฒซ slot_count ๊ฐ๋ฅผ ๊ฐ GPU ์ ๋ฐฐ์
free_gpus = list(range(slot_count))
queue_index = 0
in_flight = 0
while queue_index < N and free_gpus:
gi = free_gpus.pop(0)
dispatcher.submit(gi, meta_for(queue_index), args_for(queue_index))
queue_index += 1
in_flight += 1
yield UI status
# 2) ์ด๋ฒคํธ ๋ฃจํ
while in_flight > 0:
msg = dispatcher.event_queue.get(timeout=...)
gi = msg["gpu_index"]
if msg["type"] == "progress":
update per-GPU progress bar text; aggregate overall progress
yield UI status
elif msg["type"] == "status":
append status for that GPU
yield UI status
elif msg["type"] == "result":
out_path, status, loc_path = msg["data"]
finalize: rename/persist with disambiguating suffix
append (mp4, csv) to all_results
yield UI status (with newly visible result)
dispatcher.free_gpu(gi)
in_flight -= 1
if queue_index < N:
dispatcher.submit(gi, meta_for(queue_index), args_for(queue_index))
queue_index += 1
in_flight += 1
yield UI status
elif msg["type"] == "error":
record failure for that video
yield UI status
dispatcher.free_gpu(gi)
in_flight -= 1
# same re-fill logic as result
# 3) ์ข
๋ฃ ์ ๋ฆฌ
dispatcher.shutdown()
yield final summary
2. UI ๋ณ๊ฒฝ
2.1 ์ถ๊ฐ ์ปดํฌ๋ํธ โ Auto-Mode (Batch Queue) accordion ์
| ์ปดํฌ๋ํธ | ์ฉ๋ |
|---|---|
auto_mode_parallel_status (Markdown) |
GPU ์ / ํ์ฑ ์์ปค ์ / ํ์ ๋จ์ ์์ ์ / ๋ถ๋ฅ๋ณ ์งํ๋ฅ (์: GPU0: video_a.mp4 73%, GPU1: video_b.mp4 41% โฆ) |
์์๋ณ ๊ฒฐ๊ณผ ๋์ ์ ๊ธฐ์กด auto_results_files_state / auto_results_list ์ฌ์ฌ์ฉ |
๋ณ๊ฒฝ ์์ |
2.2 ๋จ์ผ ์์ ๋ฏธ๋ฆฌ๋ณด๊ธฐ / overlay ์ปดํฌ๋ํธ
๋ณ๋ ฌ ๋ชจ๋์์ โํ์ฌ ์ฒ๋ฆฌ์คโ ๋จ์ผ ์์์ด ์์ผ๋ฏ๋ก:
video_input/video_output๋ฑ ๋จ์ผ ์ฌ๋กฏ ์์ ฏ์ ๋ง์ง๋ง์ผ๋ก ์๋ฃ๋ ์์ ๊ฒฐ๊ณผ๋ก ๊ฐฑ์ (UX ์น์ ).- ์ฃผ๋ ์งํ ํ์๋ multiline
auto_mode_parallel_status๊ฐ ๋ด๋น.
2.3 fallback
torch.cuda.device_count() <= 1โ_auto_mode_process(ํ์ฌ ์ง๋ ฌ ๋์) ๊ทธ๋๋ก ์ ์ง.>1โ_auto_mode_process_parallel๋ถ๊ธฐ.- ํ ๊ธ: ํ๊ฒฝ๋ณ์
SAM3_PARALLEL_AUTO_MODE(๊ธฐ๋ณธauto,0์ผ๋ก ๋นํ์ฑ,1๋ก ๊ฐ์ ) ๋ก ์ต์ ํ.
3. ํ์ผ๋ช / ์ถ๋ ฅ ์ถฉ๋ ๋ฐฉ์ง
build/downloads/ ๋๋ ํ ๋ฆฌ ์์ 4 ๊ฐ ์์์ด ๊ฑฐ์ ๋์์ ๊ฒฐ๊ณผ๋ฅผ ๋จ์ด๋จ๋ฆด ๋:
| ํจ์ | ๋ณ๊ฒฝ |
|---|---|
_rename_with_rule |
๊ฒฐ๊ณผ ํ์ผ๋ช
์ ์งง์ ์์-์ธ์คํด์ค ID ๋ฅผ ๋ผ์๋ฃ๋๋ก ๋ณด๊ฐ. ์: {stem}_{video_id8}_seg_{dur}_{elapsed}s.mp4 |
_persist_for_download |
์ค๋ณต basename ์ธ ๊ฒฝ์ฐ _{n} ์ ๋ฏธ์ฌ ๋ถ์ฌ (์ด๋ฏธ ์ด๋ ์ ๋ ์ฒ๋ฆฌ๋์ง๋ง race-safe ํ๊ฒ os.rename ํ ์ฌํ์ธ) |
_build_zip_from_paths |
์ด๋ฏธ basename ์ค๋ณต disambiguation ๋ก์ง ์์ (seen_names) โ ๊ทธ๋๋ก ์ฌ์ฉ |
video_id8 ๋ ๋์คํจ์ฒ๊ฐ ์์ ํ์ ๋ฃ์ ๋ uuid.uuid4().hex[:8] ๋ก ํ ๋ฒ ์์ฑํ์ฌ meta ์ ์ ์ฅ.
4. ์์ ์ฅ์น / ์ฃ์ง ์ผ์ด์ค
- GPU ๋ฉ๋ชจ๋ฆฌ ์ฌ์ ์ฒดํฌ: ์์ปค ์ง์
์งํ
_check_gpu_memory_safe()๊ฐ true ์ธ์ง ํ์ธ (๋ชจ๋ธ ์ต์ด ์ ์ฌ ์ ). false ๋ฉดerror๋ฉ์์ง๋ก ๋์คํจ์ฒ์ ๋ณด๊ณ ํ๊ณ ์ข ๋ฃ. - ์์ปค ๋น์ ์ ์ข
๋ฃ: ๋์คํจ์ฒ๋
event_queue.get(timeout=heartbeat)์ผ๋ก ํด๋งํ๋ฉฐ, heartbeat ์๊ฐ ๋ด ๋ฉ์์ง๊ฐ ์๊ณ ํด๋น ์์ปค๊ฐis_alive() == False๋ฉดerror์ฒ๋ฆฌ +free_gpu. - ๋ถ๋ชจ ํ๋ก์ธ์ค์ daemon ์ฒดํฌ: ๊ธฐ์กด
segment_video๊ฐmp.current_process().daemon๋ฉด in-process ๋ก ํด๋ฐฑํ๋ ๋ถ๊ธฐ (app.py:4918) ์ ๋์ผํ ์ ์ ์ผ๋ก, ๋์คํจ์ฒ๋ daemon ๋ถ๋ชจ์์ ๋นํ์ฑํ โ ์์ฐจ ํด๋ฐฑ. - ์ทจ์(์คํฑ ๋ฒํผ): 1์ฐจ ๊ตฌํ์ ๋ฏธํฌํจ (ํ์ฌ ์ง๋ ฌ ๋ชจ๋์๋ stop ์์). ํ์ ์์ .
- ๋ก๊ทธ prefix: ์์ปค๊ฐ ๋ณด๋ด๋ progress/status ๋ฉ์์ง ์์
[GPU{n}]์ ๋๋ฅผ ๋ถ์ฌ์ UI ์ stdout ๊ตฌ๋ถ. - ๊ฒฐ์ ์ ๋๋ฐ์ด์ค ๋ถ๋ฐฐ: ์์ i ๊ฐ ๋ชจ๋ ๊ฐ์ GPU ๋ก ๊ฐ์ง ์๋๋ก ๋์คํจ์ฒ๊ฐ round-robin (์ฌ์ค์ โ๋จผ์ ๋๋ GPU ์ ๋ค์ ์์โ).
5. ํ ์คํธ / ๊ฒ์ฆ
5.1 ๋ก์ปฌ (๋จ์ผ GPU)
_parallel_dispatcher๊ฐdevice_count == 1์ผ ๋ ์๋์ผ๋ก ์ง๋ ฌ ๊ฒฝ๋ก๋ก ํด๋ฐฑ๋๋์ง ํ์ธ.- ํ๊ฒฝ๋ณ์
SAM3_PARALLEL_AUTO_MODE=1+CUDA_VISIBLE_DEVICES=0โ ๋์คํจ์ฒ๊ฐ 1-์ฌ๋กฏ ๋ชจ๋๋ก ๋์ (์์ปค 1๊ฐ) โ ๊ฒฐ๊ณผ๊ฐ ๊ธฐ์กด_auto_mode_process์ ๋์ผํด์ผ ํจ.
5.2 ๋ก์ปฌ (๊ฐ์ง ๋ฉํฐ GPU ์๋ฎฌ๋ ์ด์ )
SAM3_PARALLEL_AUTO_MODE=1+SAM3_FAKE_GPU_COUNT=4๋ก ๋์คํจ์ฒ ์ฝ๋๊ฐ 4-์ฌ๋กฏ ํ์ ๋ง๋ค์ง๋ง ์ค์ ๋ก ๋ชจ๋ ๋์ผํ device 0 ์ ๊ณต์ (ํ ์คํธ์ฉ; ๋จ์ dispatcher ๋ก์ง ๊ฒ์ฆ).
5.3 HF Space (4รA100)
- 4 ๊ฐ ์์ ์ ๋ก๋ โ ๊ฐ ์์ ์ฒ๋ฆฌ ์๊ฐ์ด single-GPU ๋๋น 3.5~4ร ๋นจ๋ผ์ง๋์ง ํ์ธ.
nvidia-smi๋ก 4 ์ฅ ๋ชจ๋ utilization ์ฌ๋ผ๊ฐ๋์ง ํ์ธ (๋๋ฒ๊ทธ ๋ก๊ทธ์GPU memory util:์ถ๋ ฅ).
5.4 ํ๊ท
- ๋จ์ผ ์์ โRun Segmentationโ ๋ฒํผ์ ๋ณ๊ฒฝ ์์ โ ํ๊ท ์ํ ๋ฎ์.
- ๊ฒฐ๊ณผ mp4 / csv ์ ๋ถํ ์ ํ๋๋ ๋จ์ผ/๋ณ๋ ฌ ๋ชจ๋์์ bit-identical (๊ฐ์ ์๋๋ผ๋ฉด) โ ๋จ์ผ vs ๋ณ๋ ฌ ๊ฒฐ๊ณผ mp4 ์ frame-by-frame mask IoU ๋ก sanity check.
6. ๋จ๊ณ๋ณ ๊ตฌํ ์ฒดํฌ๋ฆฌ์คํธ (์คํ ์์)
์ด ๋ฌธ์์ ์ ํ ์์๋๋ก ์ฝ๋ ์์ .
Step 1 โ ์ ํ์ผ parallel_segment_worker.py ์์ฑ
- top-level imports:
os, sys, traceback๋ง. worker_main(gpu_index, args, progress_queue)ํจ์ 1.2 ์ ์ฝ๋๋๋ก ์์ฑ.
Step 2 โ app.py ์ ๋์คํจ์ฒ ํด๋์ค ์ถ๊ฐ
class ParallelSegmentDispatcher:์ ์ (1.3 ์ ).import uuid๊ฐ ์ด๋ฏธ ์๋์ง ํ์ธ (app.py:30) โ โ ์์.
Step 3 โ app.py ์ _auto_mode_process_parallel(...) ์ ๋๋ ์ดํฐ ์ถ๊ฐ
- ์๊ณ ๋ฆฌ์ฆ์ 1.4 ์ . ์ถ๋ ฅ ํํ ํํ๋ ๊ธฐ์กด
_auto_mode_process์_pkg(...)์ ๋์ผํ๊ฒ 19-tuple ์ ์ง (UI ์์ด์ด๋ง ๋ณ๊ฒฝ ์ ํจ). auto_mode_status๋ฉ์์ง๋ฅผ multiline ์ผ๋ก ๊ตฌ์ฑํด GPU ๋ณ ์งํ๋ฅ ๋ ธ์ถ.- ๊ฒฐ๊ณผ ํ์ผ๋ช
disambiguation: video meta ์
vid8 = uuid.uuid4().hex[:8],_rename_with_ruleํธ์ถ ํ_persist_for_download์ ๋จ๊ณ์์ stem ์_{vid8}์ฝ์ .
Step 4 โ app.py ์ _auto_mode_process ์ง์
๋ถ์ ๋ผ์ฐํฐ ์ถ๊ฐ
- ํจ์ ์ฒซ ๋ถ๋ถ์์:
num_gpus = torch.cuda.device_count() if torch.cuda.is_available() else 0 parallel_env = os.getenv("SAM3_PARALLEL_AUTO_MODE", "auto").lower() use_parallel = ( (parallel_env == "1") or (parallel_env == "auto" and num_gpus > 1) ) and not bool(os.getenv("SPACES_ZERO_GPU")) if use_parallel: yield from _auto_mode_process_parallel(file_list, text_prompt, num_gpus, progress) return - ZeroGPU ๋ชจ๋์์ ๋นํ์ฑ (๊ฐ ํธ์ถ์ด ์ฌ๋ผ์ด์ค ๋จ์๋ก ๋ง GPU ํ ๋น๋ฐ์ โ ๋์์ฑ ๋ฌด์๋ฏธ).
Step 5 โ _segment_video_worker_entry ์์ ์ฝ๋ ์ค๋ณต ์ ๋ฆฌ
- ๊ธฐ์กด single-video ๊ฒฝ๋ก (
segment_videoโ_segment_video_worker_entry) ๋ ์ ์ง์ ์ผ๋กparallel_segment_worker.worker_main์ ์ฌ์ฉํด ํ ๊ณณ์์ ๊ด๋ฆฌํ๋๋ก ํตํฉ (์ ํ). 1์ฐจ ๊ตฌํ์์ ๊ฑด๋๋ฆฌ์ง ์๋๋ค (ํ๊ท ์ํ ์ต์ํ).
Step 6 โ ์ถ๋ ฅ ํ์ผ๋ช disambiguation ํจ์น
_rename_with_rule์๊ทธ๋์ฒ์extra_tag: str = ""์ต์ ์ถ๊ฐ (๊ธฐ๋ณธ ๋น ๋ฌธ์์ด๋ก ํ๋ฐฉํธํ).- ๋ณ๋ ฌ ๊ฒฝ๋ก์์๋ง
extra_tag=vid8์ ๋ฌ.
Step 7 โ UI ํ ์คํธ ๋ณด๊ฐ
auto_mode_statusMarkdown ์ multi-line ์ถ๋ ฅ (GPU ๋ณ 1์ค). ๋๋ฌด ๊ธธ๋ฉด ์ ๊ธฐ ๊ฐ๋ฅํ ์ฝ๋๋ธ๋ญ์ผ๋ก.gr.Progress๋ ๋จ์ผ ๋ง๋์ด๋ฏ๋ก, ๋ณ๋ ฌ ๋ชจ๋์ โ์ ์ฒด ํ๊ท ์งํ๋ฅ โ ๋ง ๊ฑฐ๊ธฐ์ ๋ณด๋ด๊ณ GPU ๋ณ ์ธ๋ถ๋ ํ ์คํธ๋ก.
Step 8 โ ์ค๋ชจํฌ ํ ์คํธ
- ๋ก์ปฌ์์
python app.py๋ก ๋์ฐ๊ณ :- ์์ 2๊ฐ ์ ๋ก๋ โ ๋จ์ผ GPU ํ๊ฒฝ์์ ์ง๋ ฌ ๋ชจ๋๋ก ๋์ (GPU 1 ์ฅ๋ง ๋ณด์).
SAM3_PARALLEL_AUTO_MODE=1 CUDA_VISIBLE_DEVICES=0 python app.pyโ 1-์ฌ๋กฏ ํ๋ก ๋์.
- ๊ฒฐ๊ณผ mp4 / csv ๊ฐ ์ฌ๋ฐ๋ฅด๊ฒ ์์ฑ๋๋์ง, status UI ๊ฐ ๊ฐฑ์ ๋๋์ง ํ์ธ.
Step 9 โ ํธ์
requirements.txt๋ณ๊ฒฝ ์์ (multiprocessing / uuid ํ์ค ๋ผ์ด๋ธ๋ฌ๋ฆฌ).- HF Space ์์ ํ๋์จ์ด ํญ์์
4xA100-large(๋๋ ๋ฑ๊ฐ) ๋ก ์ ๊ทธ๋ ์ด๋ํ ํ ๋์ผ ์ฝ๋๋ฅผ ํธ์ํ๋ฉด ์๋์ผ๋ก ๋ณ๋ ฌ ๋ชจ๋ ์ง์ .
7. ํฅํ ํ์ฅ (์ด๋ฒ PR ๋ฒ์ ์ธ)
- ์์ปค ์ฌ์ฌ์ฉ (persistent): ๋งค ์์๋ง๋ค spawn ๋์
Connection/Pipe๊ธฐ๋ฐ RPC ๋ก ๋ช ๋ น์ ์์ปค์ ๋ณด๋ด ๋ชจ๋ธ 1ํ๋ง ์ ์ฌ. SAM3 ๊ฐ์ค์น ๋ก๋ฉ ๋น์ฉ์ด ์์๋น 1~3 ๋ถ์ด๋ผ๋ฉด ์๋ ์ด๋ ํผ. - ์ทจ์ / ์ผ์์ ์ง: stop ๋ฒํผ โ ๋์คํจ์ฒ๊ฐ ๋ชจ๋ ์์ปค์ SIGTERM ๋ณด๋ด๊ณ partial ๊ฒฐ๊ณผ ๋ฐํ.
- GPU ๋ณ ๋ฉ๋ชจ๋ฆฌ ๋ค๋ฅธ ๊ฒฝ์ฐ: ํฐ ์์์ 80 GB GPU ๋ก, ์์ ์์์ ์์ GPU ๋ก ๋ผ์ฐํ ํ๋ ์ฐ์ ์์ ํ.
- ๋ถ์ฐ (multi-node): ๋์ผ ์ธํฐํ์ด์ค๋ก worker ๋ฅผ SSH ๋๋จธ ๋ ธ๋๋ก ๋์ธ ์ ์๊ฒ ์ถ์ํ.