Twelve Labs 임베딩 연동

Twelve Labs 임베딩 연동 (twelvelabs)

Twelve Labs Embed API는 비디오, 텍스트, 이미지, 오디오를 하나의 통합 벡터 공간으로 표현하는 강력한 임베딩을 제공해요. 이 공간 덕분에 서로 다른 종류의 콘텐츠 간에 any-to-any 검색(어떤 콘텐츠든 어떤 콘텐츠로든)이 가능해져요.

모든 모달리티(modality)를 네이티브로 처리하다 보니 표정 표현, 음성, 맥락 같은 상호작용을 포착할 수 있어요. 그래서 감성 분석, 이상 탐지(anomaly detection), 추천 시스템 같은 고급 애플리케이션을 정밀하고 효율적으로 만들 수 있어요.

이제 Python과 Node SDK를 통해 Qdrant에서 Twelve Labs 임베딩을 다루는 방법을 볼게요.

출처: Qdrant 공식 문서 — twelvelabs

SDK 설치하기 (Installing the SDKs)

$ pip install twelvelabs qdrant-client
$ npm install twelvelabs-js @qdrant/js-client-rest

클라이언트 설정하기 (Setting up the clients)

from twelvelabs import TwelveLabs
from qdrant_client import QdrantClient

# Get your API keys from:
# https://playground.twelvelabs.io/dashboard/api-key
TL_API_KEY = "<YOUR_TWELVE_LABS_API_KEY>"
twelvelabs_client = TwelveLabs(api_key=TL_API_KEY)
qdrant_client = QdrantClient(url="http://localhost:6333/")
import { QdrantClient } from '@qdrant/js-client-rest';
import { TwelveLabs, EmbeddingsTask, SegmentEmbedding } from 'twelvelabs-js';
// Get your API keys from:
// https://playground.twelvelabs.io/dashboard/api-key
const TL_API_KEY = "<YOUR_TWELVE_LABS_API_KEY>"
const twelveLabsClient = new TwelveLabs({ apiKey: TL_API_KEY });
const qdrantClient = new QdrantClient({ url: 'http://localhost:6333' });

비디오 임베딩 (Embedding videos)

아래 예시는 "Marengo-retrieval-2.7" 모델로 비디오를 임베딩해요. 이 모델은 1024차원의 벡터 임베딩을 생성하며 코사인 유사도(cosine similarity)와 함께 동작해요.

같은 모델로 오디오, 텍스트, 이미지도 공통 벡터 공간에 임베딩할 수 있어요. 이 덕분에 **교차 모달리티 검색(cross-modality search)**이 가능해져요.

task = twelvelabs_client.embed.task.create(
    model_name="Marengo-retrieval-2.7",
    video_url="https://sample-videos.com/video321/mp4/720/big_buck_bunny_720p_2mb.mp4"
)
task.wait_for_done(sleep_interval=3)
task_result = twelvelabs_client.embed.task.retrieve(task.id)
const task = await twelveLabsClient.embed.task.create(
  "Marengo-retrieval-2.7",
  { url: "https://sample-videos.com/video321/mp4/720/big_buck_bunny_720p_2mb.mp4" }
)
await task.waitForDone(3)
const taskResult = await twelveLabsClient.embed.task.retrieve(task.id)

모델 출력을 Qdrant 포인트로 변환하기

from qdrant_client.models import PointStruct

points = [
    PointStruct(
        id=idx,
        vector=v.embeddings_float,
        payload={
            "start_offset_sec": v.start_offset_sec,
            "end_offset_sec": v.end_offset_sec,
            "embedding_scope": v.embedding_scope,
        },
    )
    for idx, v in enumerate(task_result.video_embedding.segments)
]
let points = taskResult.videoEmbedding.segments.map((data, i) => {
  return {
    id: i,
    vector: data.embeddingsFloat,
    payload: {
      startOffsetSec: data.startOffsetSec,
      endOffsetSec: data.endOffsetSec,
      embeddingScope: data.embeddingScope
    }
  }
})

각 세그먼트마다 시작/끝 오프셋(start_offset_sec, end_offset_sec)과 임베딩 범위(embedding_scope)를 payload로 함께 저장해요. 검색 결과에서 비디오의 정확한 구간을 알 수 있게 하려는 거예요.

벡터를 넣을 컬렉션 만들기

from qdrant_client.models import VectorParams, Distance

collection_name = "twelve_labs_collection"
qdrant_client.create_collection(
    collection_name,
    vectors_config=VectorParams(
        size=1024,
        distance=Distance.COSINE,
    ),
)
qdrant_client.upsert(collection_name, points)
const COLLECTION_NAME = "twelve_labs_collection"
await qdrantClient.createCollection(COLLECTION_NAME, {
  vectors: { size: 1024, distance: 'Cosine' },
});
await qdrantClient.upsert(COLLECTION_NAME, { wait: true, points })

벡터가 추가되면 서로 다른 모달리티에 걸쳐 의미 검색(semantic search)을 실행할 수 있어요. 먼저 텍스트로 시도해 볼게요.

text_segment = twelvelabs_client.embed.create(
    model_name="Marengo-retrieval-2.7",
    text="<YOUR_QUERY_TEXT>",
).text_embedding.segments[0]
qdrant_client.query_points(
    collection_name=collection_name,
    query=text_segment.embeddings_float,
)
const textSegment = (
  await twelveLabsClient.embed.create({
    modelName: "Marengo-retrieval-2.7",
    text: "<YOUR_QUERY_TEXT>"
  })
).textEmbedding.segments[0]
await qdrantClient.query(COLLECTION_NAME, {
  query: textSegment.embeddingsFloat,
});

이번엔 오디오로 시도해 볼게요.

audio_segment = twelvelabs_client.embed.create(
    model_name="Marengo-retrieval-2.7",
    audio_url="https://codeskulptor-demos.commondatastorage.googleapis.com/descent/background%20music.mp3",
).audio_embedding.segments[0]
qdrant_client.query_points(
    collection_name=collection_name,
    query=audio_segment.embeddings_float,
)
const audioSegment = (
  await twelveLabsClient.embed.create({
    modelName: "Marengo-retrieval-2.7",
    audioUrl: "https://codeskulptor-demos.commondatastorage.googleapis.com/descent/background%20music.mp3"
  })
).audioEmbedding.segments[0]
await qdrantClient.query(COLLECTION_NAME, {
  query: audioSegment.embeddingsFloat,
});

마찬가지로 이미지로도 쿼리할 수 있어요.

image_segment = twelvelabs_client.embed.create(
    model_name="Marengo-retrieval-2.7",
    image_url="https://gratisography.com/wp-content/uploads/2024/01/gratisography-cyber-kitty-1170x780.jpg",
).image_embedding.segments[0]
qdrant_client.query_points(
    collection_name=collection_name,
    query=image_segment.embeddings_float,
)
const imageSegment = (
  await twelveLabsClient.embed.create({
    modelName: "Marengo-retrieval-2.7",
    imageUrl: "https://gratisography.com/wp-content/uploads/2024/01/gratisography-cyber-kitty-1170x780.jpg"
  })
).imageEmbedding.segments[0]
await qdrantClient.query(COLLECTION_NAME, {
  query: imageSegment.embeddingsFloat,
});

텍스트·오디오·이미지 쿼리 모두 같은 패턴이에요. 쿼리가 될 콘텐츠를 같은 모델로 임베딩한 뒤 그 벡터로 query_points를 호출하면, 비디오 세그먼트 중 가장 가까운 것을 찾아줘요.

더 읽을거리 (Further Reading)

더 알아보기 (Learn more)