動画のシーク作業はもう不要:キーフレーム抽出とベクトル検索によるビデオRAGシステムの構築

AI tutorial - IT technology blog
AI tutorial - IT technology blog

「シーク」という名の悪夢

先月、私はたった30秒のクリップを探すために午後を丸々潰してしまいました。私のチームは1,000時間を超えるテクニカルワークショップのライブラリを管理しているのですが、プロダクトリードがある特定の瞬間を必要としたのです。それは、エンジニアがホワイトボードに新しいデータベーススキーマを描いた正確な瞬間でした。

手動でのシーク(早送り・巻き戻し)作業は生産性の天敵です。テキストに関しては優れた検索ツールがありますが、ビデオはいまだに「ブラックボックス」のままです。ファイル名やタグで検索することはできても、標準的なツールは「物理的なホワイトボード上のマイクロサービス図」といった視覚的な概念を「見る」ことができません。

ここで、従来の検索拡張生成(RAG)は壁に突き当たります。多くの人はPDFをベクトルストアに読み込み、LLMにクエリを実行させることには慣れています。しかし、ビデオははるかに複雑です。高次元で時間軸があり、ストレージへの負荷も非常に高いのです。基本的なチャットボットを超えて、企業向けのインパクトのあるAIツールを構築したいのであれば、ビデオRAGの習得は論理的な次のステップと言えます。

なぜ標準的な検索ではビデオデータの99%が無視されるのか

核心的な問題は、ビデオをどのように表現するかという点にあります。コンピュータにとって、.mp4.mkvファイルは圧縮されたバイトのストリームに過ぎません。「タイトル」や「再生時間」といったメタデータは、フレーム内の実際のコンテンツについては何も教えてくれません。

文字起こしは助けになりますが、万能薬ではありません。Whisperのようなツールを使って音声をテキストに変換しても、キャプチャできるのは「話された内容」だけです。プレゼンターがUIのバグを実演したり、口頭で説明せずにグラフを指し示したりした場合、テキストベースのRAGではそれを見つけることができません。視覚的なコンテキストを完全に失ってしまうのです。

ここで私たちは、データの密度意味のギャップ(Semantic Gap)という2つの大きな課題に直面します。標準的な60fpsのビデオは、1分間に3,600枚の画像を生成します。すべてのフレームをAIモデルで処理するのは、コスト面でも計算リソース面でも破綻を招きます。ほとんどのフレームは直前のフレームとほぼ同一であり、ベクトルデータベース内に膨大な冗長性を生み出すことになります。

ビデオデータを扱う3つの戦略

ビデオライブラリを検索可能にするための3つの方法を評価しました。それぞれにコストと精度のトレードオフがあります。

1. 文字起こしによるショートカット

OpenAIのWhisperを使用して音声をインデックス化する方法です。高速で安価ですが、視覚的には「盲目」です。Photoshopのチュートリアルを見ていて、ナレーターが「ここをクリックしてください」と言った場合、検索エンジンは「ここ」がレイヤーパネルなのかブラシツールなのかを判断できません。

2. ブルートフォース(総当たり)方式

すべてのフレームを抽出し、CLIPのようなビジョンモデルにかける方法です。30fpsで1,000時間のビデオの場合、1億800万フレームになります。各ベクトルが512次元であれば、インデックスだけで200GBを超えてしまいます。精度は非常に高いですが、クエリのコストが法外に高くなります。

3. 「スイートスポット」:キーフレーム抽出

これが、最終的に私たちが本番環境に採用したアプローチです。アルゴリズムを使用してシーンの切り替わりや重要な動きを検出し、「キーフレーム」のみを抽出します。冗長なフレームを無視することで、意味的な内容を維持しつつ、データ量を最大98%削減できます。

ビデオRAGパイプラインの構築

本番環境に対応したパイプラインには、インテリジェントな抽出、マルチモーダル・エンベディング、ベクトル検索という3つの異なるステージが必要です。

ステップ1:インテリジェントなキーフレーム抽出

単に5秒ごとにフレームを取得するのではなく、PySceneDetectを使用して実際のコンテンツの変化を見つけます。これにより、スライドが変わった瞬間や新しいスピーカーが登壇した瞬間を正確に捉えることができます。

import cv2
from scenedetect import detect, ContentDetector

def extract_keyframes(video_path, threshold=27.0):
    # 固定間隔ではなく、内容の変化に基づいてシーンを検出
    scene_list = detect(video_path, ContentDetector(threshold=threshold))
    
    keyframes = []
    cap = cv2.VideoCapture(video_path)
    
    for i, scene in enumerate(scene_list):
        start_frame = scene[0].get_frames()
        cap.set(cv2.CAP_PROP_POS_FRAMES, start_frame)
        success, frame = cap.read()
        if success:
            filename = f"scene_{i}_at_{scene[0].get_seconds()}s.jpg"
            cv2.imwrite(filename, frame)
            keyframes.append({"file": filename, "timestamp": scene[0].get_seconds()})
            
    cap.release()
    return keyframes

ステップ2:マルチモーダル・エンベディングの生成

次に、これらのフレームを検索可能にする必要があります。私はCLIP(Contrastive Language-Image Pre-training)を使用しています。これは画像とテキストを同じ数学的空間にマッピングします。これにより、ユーザーがテキストクエリを入力して、一致する視覚的な画像を見つけることが可能になります。

from sentence_transformers import SentenceTransformer
from PIL import Image

# 速度と精度のバランスを考慮し、ViT-B-32モデルを使用
model = SentenceTransformer('clip-ViT-B-32')

def generate_embeddings(image_path):
    img = Image.open(image_path)
    return model.encode(img)

ステップ3:Qdrantによるベクトル検索

これらのエンベディングをQdrantに保存します。Qdrantは高次元ベクトルを効率的に処理し、正確なタイムスタンプなどのメタデータをベクトルに直接紐付けることができます。

from qdrant_client import QdrantClient
from qdrant_client.models import PointStruct

client = QdrantClient("localhost", port=6333)

# 検索を容易にするため、タイムスタンプとビデオIDをペイロードに含める
points = [
    PointStruct(
        id=idx, 
        vector=emb["vector"].tolist(), 
        payload={"timestamp": emb["timestamp"], "video_id": "workshop_2024_01"}
    ) 
    for idx, emb in enumerate(processed_data)
]

client.upsert(collection_name="video_intelligence", points=points)

最終的な成果

誰かが「データベースのアーキテクチャ図」と検索すると、システムはそのテキストをCLIPベクトルに変換します。その後、Qdrantをスキャンして正確なタイムスタンプを返します。私は通常、タイムスタンプの前に5秒間の「コンテキスト・バッファ」を追加しています。これにより、ユーザーがいきなり文章の途中にジャンプしてしまうのを防いでいます。

DevOpsへの最終的なアドバイス

これをスケーリングさせるのはコードだけの問題ではなく、インフラの問題でもあります。1,000時間の1080pビデオは約2.5TBの生データになります。その量に対してCLIPを実行するには、強力なGPUパワーが必要です。ビデオの処理は必ず非同期で行ってください。CeleryやRabbitMQのようなワーカーキューを使用し、サーバーが2GBのアップロードを処理している間にユーザーがローディング画面を凝視し続けなくても済むようにしましょう。

基本的なテキスト検索から視覚的なRAGへと移行することで、「死んだ」ビデオファイルを構造化された検索可能なナレッジベースへと変貌させることができます。これは、ドキュメントやトレーニングをビデオに依存しているあらゆる企業にとって、大きなアップグレードとなるはずです。

Share: