
チュートリアル
SAGEの構築:TwelveLabs埋め込みを用いたセマンティック動画比較

アーヒル・シェイク
このチュートリアルでは、SAGEの構築手順を解説します。SAGEは、Twelve LabsのMarengo 2.7埋め込み(embeddings)を使用して、2秒セグメント単位で2つの動画間の意味的なコンテンツの違いを検出するセマンティック動画比較システムです。S3ストリーミングアップロード、コサイン距離スコアリング、同期化されたサイドバイサイド再生、そしてオプションのOpenAI連携分析などの機能を備えています。
このチュートリアルでは、SAGEの構築手順を解説します。SAGEは、Twelve LabsのMarengo 2.7埋め込み(embeddings)を使用して、2秒セグメント単位で2つの動画間の意味的なコンテンツの違いを検出するセマンティック動画比較システムです。S3ストリーミングアップロード、コサイン距離スコアリング、同期化されたサイドバイサイド再生、そしてオプションのOpenAI連携分析などの機能を備えています。

この記事の内容
No headings found on page
ニュースレターに登録する
ニュースレターに登録する
ビデオ理解に関する最新の技術進歩、チュートリアル、業界の動向をお届けします
ビデオ理解に関する最新の技術進歩、チュートリアル、業界の動向をお届けします
AIを活用してビデオを検索、分析、探索します。
2026/01/06
17分
記事へのリンクをコピー
はじめに
あなたはトレーニング動画を2つのバージョンで撮影しました。内容は同じ、台本も同じですが、テイクが異なります。一方は照明が良く、もう一方は音声がよりクリアです。フレームごとのピクセル単位の違いだけでなく、コンテンツ、シーンの構成、または視覚要素の実際のセマンティック(意味的)な変化など、それらが正確にどこで異なっているかを素早く特定する必要があります。
従来の動画比較ツールには、根本的な限界があります。それは、意味ではなくピクセルを比較するという点です。これは、動画に以下のような違いがある場合に破綻します。
異なる解像度やアスペクト比
異なるエンコード設定や圧縮率
異なるカメラアングルや位置
照明やカラーグレーディングの違い
時間的なズレ(一方の動画が数秒遅れて始まるなど)
これが、私たちがSAGEを構築した理由です。SAGEは、動画に含まれるピクセルだけでなく、動画の中に何があるかを理解するシステムです。生の動画データを比較する代わりに、SAGEはTwelveLabs Marengoエンベディングを使用して動画セグメントのセマンティック表現を生成し、それらの表現を比較して意味のある違いを検出します。
重要な洞察とは何でしょうか?それは、セマンティックエンベディングは重要な要素を捉えるということです。人が歩いているカットは、同一のピクセルである必要はありません。同じアクションを表現していればよいのです。エンベディングを比較することで、技術的な理由でピクセルが異なる場合でも、動画のコンテンツが異なる場合を検出することができます。
SAGEは完全な比較ワークフローを作成します:
ストリーミングマルチパートアップロードを使用して動画をS3にアップロード(大容量ファイルを効率的に処理)
TwelveLabs Marengo-retrieval-2.7(2秒セグメント)を使用してエンベディングを生成
コサイン類似度を使用してエンベディングを比較(セマンティックな違いを検出)
同期されたタイムライン上で、サイドバイサイドの再生により違いを視覚化
オプションのAIを活用したインサイト(OpenAI連携)で違いを分析
その結果はどうなるでしょうか?単に何が違って見えるかだけでなく、何が変わったかを教えてくれるシステムが実現します。
前提条件
開始する前に、以下が準備されていることを確認してください:
Python 3.12以上がインストールされていること
Node.js 18以上またはBunがインストールされていること
APIキー:
TwelveLabs APIキー(エンベディング生成用)
OpenAI APIキー(任意、AI分析用)
S3へのアクセスが設定されたAWSアカウント(動画保存用)
リポジトリをクローンするためのGit
Python、FastAPI、Next.js、およびAWS S3に関する基本的な知識
ピクセルレベル比較の問題点
私たちが発見したのは、ピクセルレベルの比較は実世界のシナリオでは破綻するということです。以下の2つの動画を比較することを考えてみましょう:
動画 A: 1080p MP4、30fpsで撮影、H.264エンコード、自然光
動画 B: 720p MP4、24fpsで撮影、H.265エンコード、スタジオ照明
ピクセルレベルの比較では、両方の動画が同じコンテンツを表示していても、ほぼすべてのフレームが「異なる」と判定されます。根本的な問題は何でしょうか?それは、ピクセルは意味を表さないということです。
従来の方式が失敗する理由
従来の動画比較アプローチには、3つの重大な限界があります:
フォーマットの感度:解像度、コーデック、フレームレートの違いにより、誤検出(偽陽性)が発生する
時間的理解の欠如:フレームごとの比較では、時間的なコンテキストが見落とされる
セマンティック認識の欠如:「異なるピクセル」と「異なるコンテンツ」を区別できない
エンベディングによる解決策
TwelveLabs Marengoエンベディングは、動画内に何が含まれているかを表現し、どのようなピクセルが含まれているかを排除することでこれを解決します。各2秒のセグメントは、以下を捉える高次元ベクトルに変換されます:
視覚的コンテンツ(オブジェクト、シーン、アクション)
時間的パターン(動き、トランジション)
セマンティックな意味(見え方ではなく、何が起きているか)
これらのエンベディングを比較することで、単なるピクセルではなく、動画のコンテンツがいつ異なるのかを知ることができます。
デモアプリケーション
SAGEは、効率的な動画比較ワークフローを提供します:
動画のアップロード:2つの動画(一度に最大2ファイル)をアップロードすると、S3へのアップロードからエンベディング生成の完了まで、リアルタイムのステータス更新で処理プロセスを確認できます。
自動比較:両方の動画の準備が整うと、SAGEはセマンティックエンベディングを使用して自動的に比較を行い、手動でのフレームごとの確認なしにセグメントレベルで違いを特定します。
インタラクティブな分析:同期された左右並列再生、動画の違いを示す色分けされたタイムライン、類似度スコア付きの詳細なセグメントごとの内訳を通じて、違いを探索できます。
このプロセスはリアルタイムで処理されます。エンベディング生成の進行状況を観察し、類似度の割合が即座に計算されるのを確認し、正確なタイムスタンプでタイムライン上の違いを追跡します。違いのマーカーにジャンプして、動画間で具体的に何が変更されたかを正確に確認できます。
完全なデモアプリケーションを探索し、GitHubで完全なソースコードを見つけることができます。または、システムの動作を示すチュートリアル動画をご覧ください:

SAGEの仕組み
SAGEは、AWS S3ストレージ、TwelveLabsエンベディング、および高度なビジュアライゼーションを組み合わせた、洗練された動画比較パイプラインを実装しています:
システムアーキテクチャ
準備手順
1. リポジトリのクローン
コードはこちらで公開されています: https://github.com/aahilshaikh-twlbs/SAGE
git clone https://github.com/aahilshaikh-twlbs/SAGE.git cd
2. バックエンドのセットアップ
cd backend python3 -m venv .venv source .venv/bin/activate # Windows: .venv\Scripts\activate pip install -r requirements.txt cp env.example .env # .envにAPIキーを追加します
3. フロントエンドのセットアップ
cd ../frontend npm install # または bun install cp .env.local.example .env.local # NEXT_PUBLIC_API_URL=http://localhost:8000 を設定します
4. AWS S3の設定
# AWS認証情報を設定します(AWS SSOまたはIAMを使用) aws configure --profile dev # または環境変数を設定します: export AWS_ACCESS_KEY_ID=your_access_key export AWS_SECRET_ACCESS_KEY=your_secret_key export AWS_REGION
5. アプリケーションの起動
# ターミナル 1: バックエンド cd backend python app.py # ターミナル 2: フロントエンド cd frontend npm run dev # または bun dev
これらの手順が完了したら、http://localhost:3000 にアクセスして SAGE に接続してください!
実装解説
SAGEの動画比較システムを支えるコアコンポーネントを順に見ていきましょう。
1. S3へのストリーミング動画アップロード
SAGEは、ストリーミングマルチパートアップロードを使用して、大容量の動画ファイルを効率的に処理します:
async def upload_to_s3_streaming(file: UploadFile) -> str: """メモリの問題を回避するため、ストリーミングを使用してファイルをS3にアップロードします。""" file_key = f"videos/{uuid.uuid4()}_{file.filename}" # 大容量ファイルにはマルチパートアップロードを使用 response = s3_client.create_multipart_upload( Bucket=S3_BUCKET_NAME, Key=file_key, ContentType=file.content_type ) upload_id = response['UploadId'] parts = [] part_number = 1 chunk_size = 10 * 1024 * 1024# 10MBのチャンク while True: chunk = await file.read(chunk_size) if not chunk: break part_response = s3_client.upload_part( Bucket=S3_BUCKET_NAME, Key=file_key, PartNumber=part_number, UploadId=upload_id, Body=chunk ) parts.append({ 'ETag': part_response['ETag'], 'PartNumber': part_number }) part_number += 1 # マルチパートアップロードを完了 s3_client.complete_multipart_upload( Bucket=S3_BUCKET_NAME, Key=file_key, UploadId=upload_id, MultipartUpload={'Parts': parts} ) return f"s3://{S3_BUCKET_NAME}/{file_key}"
主な設計上の決定:
10MBのチャンク:アップロード効率とメモリ使用量のバランスを取ります
ストリーミング:ファイルをチャンクで処理し、ファイル全体をメモリにロードしないようにします
マルチパートアップロード:5GBを超えるファイルに必須であり、100MBを超えるファイルに推奨されます
署名付きURL:TwelveLabsが動画に安全にアクセスできるようにするための一時的なURLを生成します
2. TwelveLabsによるエンベディング生成
SAGEは、TwelveLabsの Marengo-retrieval-2.7 を使用して、非同期でエンベディングを生成します:
async def generate_embeddings_async(embedding_id: str, s3_url: str, api_key: str): """S3からの動画に対して非同期でエンベディングを生成します。""" # TwelveLabsクライアントを取得 tl = get_twelve_labs_client(api_key) # TwelveLabsが動画にアクセスするための署名付きURLを生成 presigned_url = get_s3_presigned_url(s3_url) # 署名付きHTTPS URLを使用してエンベディングタスクを作成 task = tl.embed.task.create( model_name="Marengo-retrieval-2.7", video_url=presigned_url, video_clip_length=2,# 2秒セグメント video_embedding_scopes=["clip", "video"] ) # タイムアウト付きで完了を待つ task.wait_for_done(sleep_interval=5, timeout=1800)# 30分 # 完了したタスクを取得 completed_task = tl.embed.task.retrieve(task.id) # エンベディングが生成されたことを検証 if not completed_task.video_embedding or not completed_task.video_embedding.segments: raise Exception("Embedding generation failed") # エンベディングと動画の長さを保存 embedding_storage[embedding_id].update({ "status": "completed", "embeddings": completed_task.video_embedding, "duration": last_segment.end_offset_sec, "task_id": task.id })
主な特徴:
2秒セグメント:粒度と処理時間のバランスを取ります
非同期処理:ノンブロッキングで、キューを介して複数の動画を処理します
タイムアウト処理:30分のタイムアウトにより、問題のある動画での処理の中断を防ぎます
検証:エンベディングが動画の全編をカバーしていることを確認します
3. セマンティック動画比較
SAGEは、エンベディングにコサイン距離を適用して動画を比較します:
async def compare_local_videos( embedding_id1: str, embedding_id2: str, threshold: float = 0.1, distance_metric: str = "cosine" ): """エンベディングIDを使用して、2つの動画を比較します。""" # エンベディングセグメントを取得 segments1 = extract_segments(embedding_storage[embedding_id1]) segments2 = extract_segments(embedding_storage[embedding_id2]) differing_segments = [] min_segments = min(len(segments1), len(segments2)) # 対応するセグメントを比較 for i in range(min_segments): seg1 = segments1[i] seg2 = segments2[i] # コサイン距離を計算 v1 = np.array(seg1["embedding"], dtype=np.float32) v2 = np.array(seg2["embedding"], dtype=np.float32) dot = np.dot(v1, v2) norm1 = np.linalg.norm(v1) norm2 = np.linalg.norm(v2) distance = 1.0 - (dot / (norm1 * norm2)) if norm1 > 0 and norm2 > 0 else 1.0 # しきい値を超えるセグメントにフラグを立てる if distance > threshold: differing_segments.append({ "start_sec": seg1["start_offset_sec"], "end_sec": seg1["end_offset_sec"], "distance": distance }) return { "differences": differing_segments, "total_segments": min_segments, "differing_segments": len(differing_segments), "similarity_percent": ((min_segments - len(differing_segments)) / min_segments * 100) }
なぜコサイン類似度(距離)なのか?
スケール不変性:正規化されたベクトルは、大きさ(解像度など)の違いを無視します
セマンティック(意味)重視:ピクセル値ではなく、意味の類似性を測定します
解釈の容易さ:0 = 同一、1 = 直交(無関係)、2 = 反対
構成可能なしきい値:さまざまなユースケースに合わせて感度を調整できます
4. 同期されたタイムラインの視覚化
フロントエンドは、同期された再生機能を備えたインタラクティブなタイムラインを作成します:
// 同期された動画再生 const handlePlayPause = () => { if (video1Ref.current && video2Ref.current) { if (isPlaying) { video1Ref.current.pause(); video2Ref.current.pause(); } else { video1Ref.current.play(); video2Ref.current.play(); } setIsPlaying(!isPlaying); } }; // 両方の動画で特定の時間にジャンプ const seekToTime = (time: number) => { const constrainedTime = Math.min( time, Math.min(video1Data.duration, video2Data.duration) ); video1Ref.current.currentTime = constrainedTime; video2Ref.current.currentTime = constrainedTime; setCurrentTime(constrainedTime); }; // 色分けされた相違点マーカー const getSeverityColor = (distance: number) => { if (distance >= 1.5) return 'bg-red-600';// 完全に異なる if (distance >= 1.0) return 'bg-red-500';// 大きく異なる if (distance >= 0.7) return 'bg-orange-500';// かなり異なる if (distance >= 0.5) return 'bg-amber-500';// 中程度に異なる if (distance >= 0.3) return 'bg-yellow-500';// やや異なる if (distance >= 0.1) return 'bg-lime-500';// わずかに異なる return 'bg-cyan-500';// ほぼ同一 };
ビジュアライゼーション機能:
同期された再生:両方の動画が同時に再生/ポーズされます
タイムラインマーカー:色分けされたセグメントで違いの深刻度を表示します
クリックによるシーク:マーカーをクリックすると、その時間に直接ジャンプします
類似度スコア:セグメントから算出した類似度の比率(%)を示します
5. オプションのAIパワー分析
SAGEは、OpenAIを使用して人間が読みやすい分析を生成するオプションを備えています:
async def generate_openai_analysis( embedding_id1: str, embedding_id2: str, differences: List[DifferenceSegment], threshold: float, video_duration: float ): """動画の違いに関するAI駆動の分析を生成します。""" prompt = f""" 以下のデータに基づいて、2つの動画間の違いを分析してください: 视频 1: {embed_data1.get('filename', 'Unknown')} 视频 2: {embed_data2.get('filename', 'Unknown')} 総再生時間: {video_duration:.1f} 秒 類似度しきい値: {threshold} 違いの件数: {len(differences)} これらの時間セグメントで違いが検出されました: {chr(10).join([f"- {d.start_sec:.1f} 秒から {d.end_sec:.1f} 秒 (距離: {d.distance:.3f})" for d in differences[:20]])} 以下を提供してください: 1. これらの違いが何を表している可能性があるかについての簡潔な分析 2. 比較に関する主要なインサイト 3. 重大な違いが発生している注目すべき時間セグメント """ response = openai.ChatCompletion.create( model="gpt-4", messages=[ {"role": "system", "content": "あなたは専門的な動画分析アシスタントです。"}, {"role": "user", "content": prompt} ], max_tokens=500, temperature=0.7 ) return { "analysis": response.choices[0].message.content, "key_insights": extract_insights(response), "time_segments": extract_segments(response) }
主な設計上の決定
1. フレーム単位ではなくセグメント単位の比較の採用
フレーム単位の比較の代わりに2秒セグメントを選択した理由は、主に次の3点です:
時間的コンテキスト:静止フレームだけでなく、動きやアクションを捉えることができます
計算効率:比較回数を削減できます(例:1分間の動画で1800フレームに対して300セグメント)
セマンティックの正確さ:個々のフレームよりも、エンベディングの方が「何が起きているか」を良く理解できます
トレードオフ:時間の精度は低くなりますが(フレーム精度に対して2秒精度)、より意味のある相違点を特定できます。
2. メモリ内処理ではなくストリーミングアップロードの採用
大容量の動画は数ギガバイトに及ぶことがあります。ファイル全体をメモリに読み込むと、サーバーがクラッシュする原因になります:
メモリ安全性:ストリーミングにより、ファイルを10MBのチャンクで処理します
スケーラビリティ:複数の大容量アップロードがあってもサーバーの応答性が維持されます
S3統合:S3に直接アップロードし、TwelveLabs用の署名付きURLを生成します
トレードオフ:アップロードのロジックが複雑になりますが、任意のサイズの動画を処理できるようになります。
3. ユークリッド距離ではなくコサイン類似度(距離)の採用
セマンティックな比較のためにコサイン距離を使用します:
スケール不変性:異なる画質の動画間でも一貫して機能します
セマンティック重視:大きさや強度ではなく、意味の類似性を測定します
解釈が可能:明確なしきい値を設定できます(0.1 = 微細、0.5 = 中程度、1.0 = 重大)
トレードオフ:ユークリッド距離よりも直感的ではありませんが、セマンティックな比較により適しています。
4. 並行型ではなくキューベースの処理の採用
エンベディングの生成には、動画あたり5〜30分かかる場合があります。これを順次処理します:
レート制限対策:TwelveLabs APIのレート制限超過を防ぎます
リソース管理:一度に1つの動画を処理することで、リソース使用量を一定に抑えます
エラーの隔離:失敗した動画が他の処理を阻害しません
トレードオフ:総スループットは遅くなりますが、より信頼性が高く予測可能になります。
5. データベースではなくメモリ内へのエンベディング保存
エンベディングをデータベースに永続化せず、メモリ内に保存します:
パフォーマンス:比較時における高速なアクセス(データベースクエリが不要)
シンプルさ:スキーマ移行やデータベースの管理が不要
一時的な性質:エンベディングはセッション固有であり、永続化の必要性が低いです
トレードオフ:サーバー再起動時にデータが失われますが、比較ワークフローにおいては許容範囲です。
パフォーマンス・エンジニアリング:私たちが学んだこと
SAGEの構築を通じて、大規模な動画処理。を扱うための貴重な教訓を得ました:
80/20の法則の適用
私たちは最適化作業の80%を、以下の3点に費やしました:
ストリーミングアップロード:分割アップロードにより、メモリの枯渇を防ぎます。2GBのファイルをすべてロードしてクラッシュさせるか、ストリーミングしてサーバーを安定させるかの違いはここにあります。
非同期処理:ノンブロッキングのエンベディング生成により、APIの応答性を維持します。ユーザーは、それぞれの完了を待つことなく複数の動画をアップロードできます。
セグメント検証:エンベディングが動画の全範囲をカバーしていることを確認し、サイレントエラーを防ぎます。結果を受け入れる前に、セグメント数、カバレッジ、再生時間を検証します。
うまくいかなかったこと(とその理由)
いくつかの最適化を試みましたが、どれもうまくいきませんでした:
並行エンベディング生成:TwelveLabsのレート制限に当たりました。順次処理の方が信頼性が高いことがわかりました。
エンベディングのキャッシュ:動画はそれぞれユニークであり、キャッシュは役に立ちませんでした。キャッシュするよりも再生成する方が効率的です
フレームレベルの比較:細かすぎて処理が遅く、誤検出が多すぎました。セグメントレベルが最適な妥協点でした。
長時間動画の処理
10分を超える動画には、特別な配慮が必要でした:
タイムアウト管理:30分のタイムアウトにより、問題のある動画でプロセスがハングするのを防ぎます
セグメント検証:セグメントが全編をカバーしているか検証(不完全なエンベディングを捕捉)
エラーメッセージ:サイレントに失敗させるのではなく、明確なエラーを返します(
"Embedding generation incomplete")
この結果、SAGEは10秒から20分までの動画を安定して処理できるようになりました。
この件に関する詳細情報は、私たちの 長時間動画処理ガイド に掲載されています。
データ出力
SAGEは網羅的な比較結果を生成します:
比較指標

総セグメント数:比較された2秒セグメントの数
不一致セグメント数:設定した閾値を超える距離(違い)があるセグメントの数
類似度の割合(%):
(総セグメント数 - 不一致セグメント数) / 総セグメント数 * 100相違点タイムライン:不一致度スコア付きのタイムスタンプ付きセグメント情報
ビジュアライゼーション

同期された動画プレイヤー:タイムライン付きの左右に並べた動画再生
色分けされたマーカー:重大度(緑 = 類似、赤 = 異なり)のビジュアル表現
インタラクティブなタイムライン:マーカーをクリックして相違点にシーク
相違点リスト:時間セグメントによる詳細な内訳
オプションのAI分析

要約:違いに関するハイレベルな分析
主要なインサイト:注目すべき発見事項の箇条書き表現
時間セグメント:重大な違いが発生している特定の瞬間
使用例
ケース1:2つのトレーニング動画を比較する
シナリオ:製品デモ動画の修正前・修正後バージョンを比較する
# 動画のアップロード POST /upload-and-generate-embeddings { "file": <video_file_1> } POST /upload-and-generate-embeddings { "file": <video_file_2> } # エンベディングの生成を待つ(ステータスエンドポイントをポーリング) GET /embedding-status/{embedding_id} # 動画の比較を行う POST /compare-local-videos?embedding_id1={id1}&embedding_id2={id2}&threshold=0.1
レスポンス:
{ "filename1": "demo_v1.mp4", "filename2": "demo_v2.mp4", "differences": [ { "start_sec": 12.0, "end_sec": 14.0, "distance": 0.342 }, { "start_sec": 45.0, "end_sec": 47.0, "distance": 0.521 } ], "total_segments": 180, "differing_segments": 2, "threshold_used": 0.1, "similarity_percent": 98.89 }
解釈:動画は98.89%類似しています。2つのセグメントが異なっています:
12~14秒:中程度の違い(距離:0.342)
45~47秒:中程度の違い(距離:0.521)
ケース2:かすかな違いを見つけるためにしきい値を微調整する
シナリオ:背景のわずかな変更など、ごくかすかな違いを特定する
# より高い感度のために、低いしきい値を使用します POST /compare-local-videos?embedding_id1={id1}&embedding_id2={id2}&threshold=0.05
結果:背景の微細な変化や照明の調整など、より多くの相違点を探知します。
ケース3:AI分析を生成する
シナリオ:違いに関する、人間にわかりやすい説明を生成する
POST /openai-analysis { "embedding_id1": "...", "embedding_id2": "...", "differences": [...], "threshold": 0.1, "video_duration": 360.0 }
レスポンス:
{ "analysis": "動画は全体的に類似した内容を示していますが、2つの顕著な相違点があります...", "key_insights": [ "テイク間で製品の位置変更がありました", "45秒時点で背景の照明が調整されています" ], "time_segments": [ "12-14 秒: 製品実演のアングル", "45-47 秒: 背景シーンの変更" ] }
現実世界のユースケース
SAGEを構築してテストする中で、このツールが最も価値を発揮する明確なパターンを特定しました:
コンテンツ制作
Before/Afterの比較:編集前と編集後の映像素材を比較する
バージョン管理:動画の改訂プロセスに沿って変更を追跡する
品質保証(QA):異なるパターンの動画間で一貫性を確認する
トレーニング & 教育
取扱説明用動画:更新前と更新後の動画バージョンを比較する
コースの整合性:すべてのレッスンが同じフォーマットを維持しているか確認する
コンテンツのアップデート:改訂された教材のどの部分が変更されたかを特定する
コンプライアンス(法令順守) & 検証
広告表現の検証:承認されたバージョンと実際に放送・配信されたバージョンを比較する
法的文書化:証拠動画などの変更を追跡する
ブランドガイドラインの整合性:マーケティング用動画がブランド規定に合致しているか検証する
このアプローチが効果を発揮する理由(それと機能しない場合)
SAGEはセマンティックな比較(単なるピクセルではなく、動画のコンテンツ意味の違いを検出すること)に優れています。 最もその強みを発揮するのは、以下のような場合です:
強みを発揮する場合:
✅ 動画の技術スペック(解像度、コーデック、フレームレート)が異なる場合
✅ ピクセルではなく、コンテンツ自体の違いを見つけたい場合
✅ 動画の構造が類似している場合(同じ長さ、同じようなシーン)
✅ 違いが意味のある内容である場合(シーンの変化、物体の追加など)
あまり効果的ではない場合:
❌ 動画同士が完全に異なる場合(無関係なコンテンツの比較)
❌ フレーム精度の正確なタイミングが必要な場合(SAGEは2秒セグメント単位です)
❌ 動画の長さが極端に異なる場合(比較は短い方の動画に合わせて切り詰められます)
❌ ピクセルレベルの厳密な同一性検証が必要な場合(代わりに従来の差分取得ツールをご使用ください)
スイートスポット(最適な環境)
SAGEは、同じコンテンツのバリエーション(同じ台本、同じ現場だがテイクが異なる、編集やバージョンが異なるもの)を比較するのに最適です。技術的な違いに左右されることなく、本質的に重要な違いを見つけ出します。
パフォーマンス・ベンチマーク
何百本もの動画を処理して得られた知見を以下に示します:
処理時間
1分間の動画:合計約2〜3分(アップロード + エンベディング)
5分間の動画:合計約5〜8分
10分間の動画:合計約10〜15分
15分間の動画:合計約15〜25分
内訳:アップロードは通常1分未満で完了します。エンベディング生成時間は、動画の長さにほぼ比例して増加します。
比較速度
比較ロジックの計算:動画の長さに関わらず1秒未満
タイムラインのレンダリング:一般的な動画で100ミリ秒未満
動画再生:ブラウザ本来のネイティブパフォーマンス
重要なインサイト:エンベディングが存在していれば、比較処理は一瞬で終わります。処理のボトルネックは比較そのものではなく、エンベディングの生成部分になります。
精度

セマンティック(意味的)相違点:実質的なコンテンツの変化を正確に捉えます
誤検出(偽陽性):適切なしきい値設定(デフォルトの0.1が推奨)で低く抑えられます
検出漏れ(偽陰性):極めて微細な変化は検出されない場合があります(その場合は値を下げてください)

しきい値のガイドライン:
0.05:超高感度(かすかな背景の変化なども特定)
0.1:標準的(感度のバランスに優れる)
0.2:低感度(主要な変化のみ)
0.5:最過保護(劇的なシーン変更のみ)

結論:動画比較の未来
SAGEは、「動画をピクセルではなく意味で比較するとどうなるか?」という実験から始まりました。私たちが発見したのは、セマンティックな比較が動画の「違い」に関する我々の見方を変えるということです。
ピクセルレベルのノイズに翻弄されることなく、コンテンツ自体の変更、シーン構成の違い、本質的なバリエーションといった本当に重要なことに集中できます。そして、フレームの手動比較を自動化することも可能になります。
このアプローチは示唆に富んでいます:
クリエイター向け:手動チェックなしに、以前のバージョンと何が変更されたかを即座に特定
開発者向け:単なる動画ファイルとしてではなく、動画の内容そのものを理解するインテリジェントなアプリ構築
業界全体へ:エンベディングモデルが進化すれば、自動的にこの比較精度も上がっていきます
一番エキサイティングなのは、まだこれが始まりに過ぎないという点です。動画理解モデルの進化に伴い、SAGEの比較能力もレベルアップしていきます。現在はセグメント単位ですが、将来的にはシーンごと、特定オブジェクトの検知、さらには文脈やストーリー構成の理解にまで及ぶかもしれません。
土台はすでに整っています。あとは繰り返しの開発があるのみです。
その他の情報源
GitHub リポジトリ: GitHub上のSAGEページ
TwelveLabs ドキュメント: Marengoエンベディング応用ガイド
AWS S3: マルチパートアップロードのベストプラクティス
コサイン類似度: ベクトルの類似性の理解
付録:技術的詳細
エンベディングモデル
使用モデル:Marengo-retrieval-2.7
セグメント長:2秒
ベクトル次元数:768(セグメントあたり)
スコープ選択:
["clip", "video"](クリップ単位および動画全体の双方)
類似度の数式表現
コサイン距離:
1 - (dot(v1, v2) / (norm(v1) * norm(v2)))範囲:0(完全に同一)から 2(正反対)まで
目安:0-0.1(極めて類似)、0.1-0.3(多少の違い)、0.3-0.7(中程度の相違)、0.7+(著しい相違)
S3 詳細構成
分割サイズ(チャンク):10MB
アップロード方式:常にマルチパートを使用(信頼性向上のため)
署名付きURLの有効期限:1時間(3600秒)
ホスト地域(Region):構成調整(デフォルト:us-east-2)
提供API一覧
POST /validate-key- TwelveLabsのAPI有効性をチェックPOST /upload-and-generate-embeddings- ビデオアップロードとエンベディング生成タスクの開始GET /embedding-status/{embedding_id}- エンベディング生成の現在のステータス判定POST /compare-local-videos- エンベディングIDを用いた、2つの動画の比較実行POST /openai-analysis- 違いを説明するAI分析文章の任意生成GET /serve-video/{video_id}- ブラウザ再生用の署名付きURL生成GET /health- APIサーバのヘルスチェック(動作点検)
はじめに
あなたはトレーニング動画を2つのバージョンで撮影しました。内容は同じ、台本も同じですが、テイクが異なります。一方は照明が良く、もう一方は音声がよりクリアです。フレームごとのピクセル単位の違いだけでなく、コンテンツ、シーンの構成、または視覚要素の実際のセマンティック(意味的)な変化など、それらが正確にどこで異なっているかを素早く特定する必要があります。
従来の動画比較ツールには、根本的な限界があります。それは、意味ではなくピクセルを比較するという点です。これは、動画に以下のような違いがある場合に破綻します。
異なる解像度やアスペクト比
異なるエンコード設定や圧縮率
異なるカメラアングルや位置
照明やカラーグレーディングの違い
時間的なズレ(一方の動画が数秒遅れて始まるなど)
これが、私たちがSAGEを構築した理由です。SAGEは、動画に含まれるピクセルだけでなく、動画の中に何があるかを理解するシステムです。生の動画データを比較する代わりに、SAGEはTwelveLabs Marengoエンベディングを使用して動画セグメントのセマンティック表現を生成し、それらの表現を比較して意味のある違いを検出します。
重要な洞察とは何でしょうか?それは、セマンティックエンベディングは重要な要素を捉えるということです。人が歩いているカットは、同一のピクセルである必要はありません。同じアクションを表現していればよいのです。エンベディングを比較することで、技術的な理由でピクセルが異なる場合でも、動画のコンテンツが異なる場合を検出することができます。
SAGEは完全な比較ワークフローを作成します:
ストリーミングマルチパートアップロードを使用して動画をS3にアップロード(大容量ファイルを効率的に処理)
TwelveLabs Marengo-retrieval-2.7(2秒セグメント)を使用してエンベディングを生成
コサイン類似度を使用してエンベディングを比較(セマンティックな違いを検出)
同期されたタイムライン上で、サイドバイサイドの再生により違いを視覚化
オプションのAIを活用したインサイト(OpenAI連携)で違いを分析
その結果はどうなるでしょうか?単に何が違って見えるかだけでなく、何が変わったかを教えてくれるシステムが実現します。
前提条件
開始する前に、以下が準備されていることを確認してください:
Python 3.12以上がインストールされていること
Node.js 18以上またはBunがインストールされていること
APIキー:
TwelveLabs APIキー(エンベディング生成用)
OpenAI APIキー(任意、AI分析用)
S3へのアクセスが設定されたAWSアカウント(動画保存用)
リポジトリをクローンするためのGit
Python、FastAPI、Next.js、およびAWS S3に関する基本的な知識
ピクセルレベル比較の問題点
私たちが発見したのは、ピクセルレベルの比較は実世界のシナリオでは破綻するということです。以下の2つの動画を比較することを考えてみましょう:
動画 A: 1080p MP4、30fpsで撮影、H.264エンコード、自然光
動画 B: 720p MP4、24fpsで撮影、H.265エンコード、スタジオ照明
ピクセルレベルの比較では、両方の動画が同じコンテンツを表示していても、ほぼすべてのフレームが「異なる」と判定されます。根本的な問題は何でしょうか?それは、ピクセルは意味を表さないということです。
従来の方式が失敗する理由
従来の動画比較アプローチには、3つの重大な限界があります:
フォーマットの感度:解像度、コーデック、フレームレートの違いにより、誤検出(偽陽性)が発生する
時間的理解の欠如:フレームごとの比較では、時間的なコンテキストが見落とされる
セマンティック認識の欠如:「異なるピクセル」と「異なるコンテンツ」を区別できない
エンベディングによる解決策
TwelveLabs Marengoエンベディングは、動画内に何が含まれているかを表現し、どのようなピクセルが含まれているかを排除することでこれを解決します。各2秒のセグメントは、以下を捉える高次元ベクトルに変換されます:
視覚的コンテンツ(オブジェクト、シーン、アクション)
時間的パターン(動き、トランジション)
セマンティックな意味(見え方ではなく、何が起きているか)
これらのエンベディングを比較することで、単なるピクセルではなく、動画のコンテンツがいつ異なるのかを知ることができます。
デモアプリケーション
SAGEは、効率的な動画比較ワークフローを提供します:
動画のアップロード:2つの動画(一度に最大2ファイル)をアップロードすると、S3へのアップロードからエンベディング生成の完了まで、リアルタイムのステータス更新で処理プロセスを確認できます。
自動比較:両方の動画の準備が整うと、SAGEはセマンティックエンベディングを使用して自動的に比較を行い、手動でのフレームごとの確認なしにセグメントレベルで違いを特定します。
インタラクティブな分析:同期された左右並列再生、動画の違いを示す色分けされたタイムライン、類似度スコア付きの詳細なセグメントごとの内訳を通じて、違いを探索できます。
このプロセスはリアルタイムで処理されます。エンベディング生成の進行状況を観察し、類似度の割合が即座に計算されるのを確認し、正確なタイムスタンプでタイムライン上の違いを追跡します。違いのマーカーにジャンプして、動画間で具体的に何が変更されたかを正確に確認できます。
完全なデモアプリケーションを探索し、GitHubで完全なソースコードを見つけることができます。または、システムの動作を示すチュートリアル動画をご覧ください:

SAGEの仕組み
SAGEは、AWS S3ストレージ、TwelveLabsエンベディング、および高度なビジュアライゼーションを組み合わせた、洗練された動画比較パイプラインを実装しています:
システムアーキテクチャ
準備手順
1. リポジトリのクローン
コードはこちらで公開されています: https://github.com/aahilshaikh-twlbs/SAGE
git clone https://github.com/aahilshaikh-twlbs/SAGE.git cd
2. バックエンドのセットアップ
cd backend python3 -m venv .venv source .venv/bin/activate # Windows: .venv\Scripts\activate pip install -r requirements.txt cp env.example .env # .envにAPIキーを追加します
3. フロントエンドのセットアップ
cd ../frontend npm install # または bun install cp .env.local.example .env.local # NEXT_PUBLIC_API_URL=http://localhost:8000 を設定します
4. AWS S3の設定
# AWS認証情報を設定します(AWS SSOまたはIAMを使用) aws configure --profile dev # または環境変数を設定します: export AWS_ACCESS_KEY_ID=your_access_key export AWS_SECRET_ACCESS_KEY=your_secret_key export AWS_REGION
5. アプリケーションの起動
# ターミナル 1: バックエンド cd backend python app.py # ターミナル 2: フロントエンド cd frontend npm run dev # または bun dev
これらの手順が完了したら、http://localhost:3000 にアクセスして SAGE に接続してください!
実装解説
SAGEの動画比較システムを支えるコアコンポーネントを順に見ていきましょう。
1. S3へのストリーミング動画アップロード
SAGEは、ストリーミングマルチパートアップロードを使用して、大容量の動画ファイルを効率的に処理します:
async def upload_to_s3_streaming(file: UploadFile) -> str: """メモリの問題を回避するため、ストリーミングを使用してファイルをS3にアップロードします。""" file_key = f"videos/{uuid.uuid4()}_{file.filename}" # 大容量ファイルにはマルチパートアップロードを使用 response = s3_client.create_multipart_upload( Bucket=S3_BUCKET_NAME, Key=file_key, ContentType=file.content_type ) upload_id = response['UploadId'] parts = [] part_number = 1 chunk_size = 10 * 1024 * 1024# 10MBのチャンク while True: chunk = await file.read(chunk_size) if not chunk: break part_response = s3_client.upload_part( Bucket=S3_BUCKET_NAME, Key=file_key, PartNumber=part_number, UploadId=upload_id, Body=chunk ) parts.append({ 'ETag': part_response['ETag'], 'PartNumber': part_number }) part_number += 1 # マルチパートアップロードを完了 s3_client.complete_multipart_upload( Bucket=S3_BUCKET_NAME, Key=file_key, UploadId=upload_id, MultipartUpload={'Parts': parts} ) return f"s3://{S3_BUCKET_NAME}/{file_key}"
主な設計上の決定:
10MBのチャンク:アップロード効率とメモリ使用量のバランスを取ります
ストリーミング:ファイルをチャンクで処理し、ファイル全体をメモリにロードしないようにします
マルチパートアップロード:5GBを超えるファイルに必須であり、100MBを超えるファイルに推奨されます
署名付きURL:TwelveLabsが動画に安全にアクセスできるようにするための一時的なURLを生成します
2. TwelveLabsによるエンベディング生成
SAGEは、TwelveLabsの Marengo-retrieval-2.7 を使用して、非同期でエンベディングを生成します:
async def generate_embeddings_async(embedding_id: str, s3_url: str, api_key: str): """S3からの動画に対して非同期でエンベディングを生成します。""" # TwelveLabsクライアントを取得 tl = get_twelve_labs_client(api_key) # TwelveLabsが動画にアクセスするための署名付きURLを生成 presigned_url = get_s3_presigned_url(s3_url) # 署名付きHTTPS URLを使用してエンベディングタスクを作成 task = tl.embed.task.create( model_name="Marengo-retrieval-2.7", video_url=presigned_url, video_clip_length=2,# 2秒セグメント video_embedding_scopes=["clip", "video"] ) # タイムアウト付きで完了を待つ task.wait_for_done(sleep_interval=5, timeout=1800)# 30分 # 完了したタスクを取得 completed_task = tl.embed.task.retrieve(task.id) # エンベディングが生成されたことを検証 if not completed_task.video_embedding or not completed_task.video_embedding.segments: raise Exception("Embedding generation failed") # エンベディングと動画の長さを保存 embedding_storage[embedding_id].update({ "status": "completed", "embeddings": completed_task.video_embedding, "duration": last_segment.end_offset_sec, "task_id": task.id })
主な特徴:
2秒セグメント:粒度と処理時間のバランスを取ります
非同期処理:ノンブロッキングで、キューを介して複数の動画を処理します
タイムアウト処理:30分のタイムアウトにより、問題のある動画での処理の中断を防ぎます
検証:エンベディングが動画の全編をカバーしていることを確認します
3. セマンティック動画比較
SAGEは、エンベディングにコサイン距離を適用して動画を比較します:
async def compare_local_videos( embedding_id1: str, embedding_id2: str, threshold: float = 0.1, distance_metric: str = "cosine" ): """エンベディングIDを使用して、2つの動画を比較します。""" # エンベディングセグメントを取得 segments1 = extract_segments(embedding_storage[embedding_id1]) segments2 = extract_segments(embedding_storage[embedding_id2]) differing_segments = [] min_segments = min(len(segments1), len(segments2)) # 対応するセグメントを比較 for i in range(min_segments): seg1 = segments1[i] seg2 = segments2[i] # コサイン距離を計算 v1 = np.array(seg1["embedding"], dtype=np.float32) v2 = np.array(seg2["embedding"], dtype=np.float32) dot = np.dot(v1, v2) norm1 = np.linalg.norm(v1) norm2 = np.linalg.norm(v2) distance = 1.0 - (dot / (norm1 * norm2)) if norm1 > 0 and norm2 > 0 else 1.0 # しきい値を超えるセグメントにフラグを立てる if distance > threshold: differing_segments.append({ "start_sec": seg1["start_offset_sec"], "end_sec": seg1["end_offset_sec"], "distance": distance }) return { "differences": differing_segments, "total_segments": min_segments, "differing_segments": len(differing_segments), "similarity_percent": ((min_segments - len(differing_segments)) / min_segments * 100) }
なぜコサイン類似度(距離)なのか?
スケール不変性:正規化されたベクトルは、大きさ(解像度など)の違いを無視します
セマンティック(意味)重視:ピクセル値ではなく、意味の類似性を測定します
解釈の容易さ:0 = 同一、1 = 直交(無関係)、2 = 反対
構成可能なしきい値:さまざまなユースケースに合わせて感度を調整できます
4. 同期されたタイムラインの視覚化
フロントエンドは、同期された再生機能を備えたインタラクティブなタイムラインを作成します:
// 同期された動画再生 const handlePlayPause = () => { if (video1Ref.current && video2Ref.current) { if (isPlaying) { video1Ref.current.pause(); video2Ref.current.pause(); } else { video1Ref.current.play(); video2Ref.current.play(); } setIsPlaying(!isPlaying); } }; // 両方の動画で特定の時間にジャンプ const seekToTime = (time: number) => { const constrainedTime = Math.min( time, Math.min(video1Data.duration, video2Data.duration) ); video1Ref.current.currentTime = constrainedTime; video2Ref.current.currentTime = constrainedTime; setCurrentTime(constrainedTime); }; // 色分けされた相違点マーカー const getSeverityColor = (distance: number) => { if (distance >= 1.5) return 'bg-red-600';// 完全に異なる if (distance >= 1.0) return 'bg-red-500';// 大きく異なる if (distance >= 0.7) return 'bg-orange-500';// かなり異なる if (distance >= 0.5) return 'bg-amber-500';// 中程度に異なる if (distance >= 0.3) return 'bg-yellow-500';// やや異なる if (distance >= 0.1) return 'bg-lime-500';// わずかに異なる return 'bg-cyan-500';// ほぼ同一 };
ビジュアライゼーション機能:
同期された再生:両方の動画が同時に再生/ポーズされます
タイムラインマーカー:色分けされたセグメントで違いの深刻度を表示します
クリックによるシーク:マーカーをクリックすると、その時間に直接ジャンプします
類似度スコア:セグメントから算出した類似度の比率(%)を示します
5. オプションのAIパワー分析
SAGEは、OpenAIを使用して人間が読みやすい分析を生成するオプションを備えています:
async def generate_openai_analysis( embedding_id1: str, embedding_id2: str, differences: List[DifferenceSegment], threshold: float, video_duration: float ): """動画の違いに関するAI駆動の分析を生成します。""" prompt = f""" 以下のデータに基づいて、2つの動画間の違いを分析してください: 视频 1: {embed_data1.get('filename', 'Unknown')} 视频 2: {embed_data2.get('filename', 'Unknown')} 総再生時間: {video_duration:.1f} 秒 類似度しきい値: {threshold} 違いの件数: {len(differences)} これらの時間セグメントで違いが検出されました: {chr(10).join([f"- {d.start_sec:.1f} 秒から {d.end_sec:.1f} 秒 (距離: {d.distance:.3f})" for d in differences[:20]])} 以下を提供してください: 1. これらの違いが何を表している可能性があるかについての簡潔な分析 2. 比較に関する主要なインサイト 3. 重大な違いが発生している注目すべき時間セグメント """ response = openai.ChatCompletion.create( model="gpt-4", messages=[ {"role": "system", "content": "あなたは専門的な動画分析アシスタントです。"}, {"role": "user", "content": prompt} ], max_tokens=500, temperature=0.7 ) return { "analysis": response.choices[0].message.content, "key_insights": extract_insights(response), "time_segments": extract_segments(response) }
主な設計上の決定
1. フレーム単位ではなくセグメント単位の比較の採用
フレーム単位の比較の代わりに2秒セグメントを選択した理由は、主に次の3点です:
時間的コンテキスト:静止フレームだけでなく、動きやアクションを捉えることができます
計算効率:比較回数を削減できます(例:1分間の動画で1800フレームに対して300セグメント)
セマンティックの正確さ:個々のフレームよりも、エンベディングの方が「何が起きているか」を良く理解できます
トレードオフ:時間の精度は低くなりますが(フレーム精度に対して2秒精度)、より意味のある相違点を特定できます。
2. メモリ内処理ではなくストリーミングアップロードの採用
大容量の動画は数ギガバイトに及ぶことがあります。ファイル全体をメモリに読み込むと、サーバーがクラッシュする原因になります:
メモリ安全性:ストリーミングにより、ファイルを10MBのチャンクで処理します
スケーラビリティ:複数の大容量アップロードがあってもサーバーの応答性が維持されます
S3統合:S3に直接アップロードし、TwelveLabs用の署名付きURLを生成します
トレードオフ:アップロードのロジックが複雑になりますが、任意のサイズの動画を処理できるようになります。
3. ユークリッド距離ではなくコサイン類似度(距離)の採用
セマンティックな比較のためにコサイン距離を使用します:
スケール不変性:異なる画質の動画間でも一貫して機能します
セマンティック重視:大きさや強度ではなく、意味の類似性を測定します
解釈が可能:明確なしきい値を設定できます(0.1 = 微細、0.5 = 中程度、1.0 = 重大)
トレードオフ:ユークリッド距離よりも直感的ではありませんが、セマンティックな比較により適しています。
4. 並行型ではなくキューベースの処理の採用
エンベディングの生成には、動画あたり5〜30分かかる場合があります。これを順次処理します:
レート制限対策:TwelveLabs APIのレート制限超過を防ぎます
リソース管理:一度に1つの動画を処理することで、リソース使用量を一定に抑えます
エラーの隔離:失敗した動画が他の処理を阻害しません
トレードオフ:総スループットは遅くなりますが、より信頼性が高く予測可能になります。
5. データベースではなくメモリ内へのエンベディング保存
エンベディングをデータベースに永続化せず、メモリ内に保存します:
パフォーマンス:比較時における高速なアクセス(データベースクエリが不要)
シンプルさ:スキーマ移行やデータベースの管理が不要
一時的な性質:エンベディングはセッション固有であり、永続化の必要性が低いです
トレードオフ:サーバー再起動時にデータが失われますが、比較ワークフローにおいては許容範囲です。
パフォーマンス・エンジニアリング:私たちが学んだこと
SAGEの構築を通じて、大規模な動画処理。を扱うための貴重な教訓を得ました:
80/20の法則の適用
私たちは最適化作業の80%を、以下の3点に費やしました:
ストリーミングアップロード:分割アップロードにより、メモリの枯渇を防ぎます。2GBのファイルをすべてロードしてクラッシュさせるか、ストリーミングしてサーバーを安定させるかの違いはここにあります。
非同期処理:ノンブロッキングのエンベディング生成により、APIの応答性を維持します。ユーザーは、それぞれの完了を待つことなく複数の動画をアップロードできます。
セグメント検証:エンベディングが動画の全範囲をカバーしていることを確認し、サイレントエラーを防ぎます。結果を受け入れる前に、セグメント数、カバレッジ、再生時間を検証します。
うまくいかなかったこと(とその理由)
いくつかの最適化を試みましたが、どれもうまくいきませんでした:
並行エンベディング生成:TwelveLabsのレート制限に当たりました。順次処理の方が信頼性が高いことがわかりました。
エンベディングのキャッシュ:動画はそれぞれユニークであり、キャッシュは役に立ちませんでした。キャッシュするよりも再生成する方が効率的です
フレームレベルの比較:細かすぎて処理が遅く、誤検出が多すぎました。セグメントレベルが最適な妥協点でした。
長時間動画の処理
10分を超える動画には、特別な配慮が必要でした:
タイムアウト管理:30分のタイムアウトにより、問題のある動画でプロセスがハングするのを防ぎます
セグメント検証:セグメントが全編をカバーしているか検証(不完全なエンベディングを捕捉)
エラーメッセージ:サイレントに失敗させるのではなく、明確なエラーを返します(
"Embedding generation incomplete")
この結果、SAGEは10秒から20分までの動画を安定して処理できるようになりました。
この件に関する詳細情報は、私たちの 長時間動画処理ガイド に掲載されています。
データ出力
SAGEは網羅的な比較結果を生成します:
比較指標

総セグメント数:比較された2秒セグメントの数
不一致セグメント数:設定した閾値を超える距離(違い)があるセグメントの数
類似度の割合(%):
(総セグメント数 - 不一致セグメント数) / 総セグメント数 * 100相違点タイムライン:不一致度スコア付きのタイムスタンプ付きセグメント情報
ビジュアライゼーション

同期された動画プレイヤー:タイムライン付きの左右に並べた動画再生
色分けされたマーカー:重大度(緑 = 類似、赤 = 異なり)のビジュアル表現
インタラクティブなタイムライン:マーカーをクリックして相違点にシーク
相違点リスト:時間セグメントによる詳細な内訳
オプションのAI分析

要約:違いに関するハイレベルな分析
主要なインサイト:注目すべき発見事項の箇条書き表現
時間セグメント:重大な違いが発生している特定の瞬間
使用例
ケース1:2つのトレーニング動画を比較する
シナリオ:製品デモ動画の修正前・修正後バージョンを比較する
# 動画のアップロード POST /upload-and-generate-embeddings { "file": <video_file_1> } POST /upload-and-generate-embeddings { "file": <video_file_2> } # エンベディングの生成を待つ(ステータスエンドポイントをポーリング) GET /embedding-status/{embedding_id} # 動画の比較を行う POST /compare-local-videos?embedding_id1={id1}&embedding_id2={id2}&threshold=0.1
レスポンス:
{ "filename1": "demo_v1.mp4", "filename2": "demo_v2.mp4", "differences": [ { "start_sec": 12.0, "end_sec": 14.0, "distance": 0.342 }, { "start_sec": 45.0, "end_sec": 47.0, "distance": 0.521 } ], "total_segments": 180, "differing_segments": 2, "threshold_used": 0.1, "similarity_percent": 98.89 }
解釈:動画は98.89%類似しています。2つのセグメントが異なっています:
12~14秒:中程度の違い(距離:0.342)
45~47秒:中程度の違い(距離:0.521)
ケース2:かすかな違いを見つけるためにしきい値を微調整する
シナリオ:背景のわずかな変更など、ごくかすかな違いを特定する
# より高い感度のために、低いしきい値を使用します POST /compare-local-videos?embedding_id1={id1}&embedding_id2={id2}&threshold=0.05
結果:背景の微細な変化や照明の調整など、より多くの相違点を探知します。
ケース3:AI分析を生成する
シナリオ:違いに関する、人間にわかりやすい説明を生成する
POST /openai-analysis { "embedding_id1": "...", "embedding_id2": "...", "differences": [...], "threshold": 0.1, "video_duration": 360.0 }
レスポンス:
{ "analysis": "動画は全体的に類似した内容を示していますが、2つの顕著な相違点があります...", "key_insights": [ "テイク間で製品の位置変更がありました", "45秒時点で背景の照明が調整されています" ], "time_segments": [ "12-14 秒: 製品実演のアングル", "45-47 秒: 背景シーンの変更" ] }
現実世界のユースケース
SAGEを構築してテストする中で、このツールが最も価値を発揮する明確なパターンを特定しました:
コンテンツ制作
Before/Afterの比較:編集前と編集後の映像素材を比較する
バージョン管理:動画の改訂プロセスに沿って変更を追跡する
品質保証(QA):異なるパターンの動画間で一貫性を確認する
トレーニング & 教育
取扱説明用動画:更新前と更新後の動画バージョンを比較する
コースの整合性:すべてのレッスンが同じフォーマットを維持しているか確認する
コンテンツのアップデート:改訂された教材のどの部分が変更されたかを特定する
コンプライアンス(法令順守) & 検証
広告表現の検証:承認されたバージョンと実際に放送・配信されたバージョンを比較する
法的文書化:証拠動画などの変更を追跡する
ブランドガイドラインの整合性:マーケティング用動画がブランド規定に合致しているか検証する
このアプローチが効果を発揮する理由(それと機能しない場合)
SAGEはセマンティックな比較(単なるピクセルではなく、動画のコンテンツ意味の違いを検出すること)に優れています。 最もその強みを発揮するのは、以下のような場合です:
強みを発揮する場合:
✅ 動画の技術スペック(解像度、コーデック、フレームレート)が異なる場合
✅ ピクセルではなく、コンテンツ自体の違いを見つけたい場合
✅ 動画の構造が類似している場合(同じ長さ、同じようなシーン)
✅ 違いが意味のある内容である場合(シーンの変化、物体の追加など)
あまり効果的ではない場合:
❌ 動画同士が完全に異なる場合(無関係なコンテンツの比較)
❌ フレーム精度の正確なタイミングが必要な場合(SAGEは2秒セグメント単位です)
❌ 動画の長さが極端に異なる場合(比較は短い方の動画に合わせて切り詰められます)
❌ ピクセルレベルの厳密な同一性検証が必要な場合(代わりに従来の差分取得ツールをご使用ください)
スイートスポット(最適な環境)
SAGEは、同じコンテンツのバリエーション(同じ台本、同じ現場だがテイクが異なる、編集やバージョンが異なるもの)を比較するのに最適です。技術的な違いに左右されることなく、本質的に重要な違いを見つけ出します。
パフォーマンス・ベンチマーク
何百本もの動画を処理して得られた知見を以下に示します:
処理時間
1分間の動画:合計約2〜3分(アップロード + エンベディング)
5分間の動画:合計約5〜8分
10分間の動画:合計約10〜15分
15分間の動画:合計約15〜25分
内訳:アップロードは通常1分未満で完了します。エンベディング生成時間は、動画の長さにほぼ比例して増加します。
比較速度
比較ロジックの計算:動画の長さに関わらず1秒未満
タイムラインのレンダリング:一般的な動画で100ミリ秒未満
動画再生:ブラウザ本来のネイティブパフォーマンス
重要なインサイト:エンベディングが存在していれば、比較処理は一瞬で終わります。処理のボトルネックは比較そのものではなく、エンベディングの生成部分になります。
精度

セマンティック(意味的)相違点:実質的なコンテンツの変化を正確に捉えます
誤検出(偽陽性):適切なしきい値設定(デフォルトの0.1が推奨)で低く抑えられます
検出漏れ(偽陰性):極めて微細な変化は検出されない場合があります(その場合は値を下げてください)

しきい値のガイドライン:
0.05:超高感度(かすかな背景の変化なども特定)
0.1:標準的(感度のバランスに優れる)
0.2:低感度(主要な変化のみ)
0.5:最過保護(劇的なシーン変更のみ)

結論:動画比較の未来
SAGEは、「動画をピクセルではなく意味で比較するとどうなるか?」という実験から始まりました。私たちが発見したのは、セマンティックな比較が動画の「違い」に関する我々の見方を変えるということです。
ピクセルレベルのノイズに翻弄されることなく、コンテンツ自体の変更、シーン構成の違い、本質的なバリエーションといった本当に重要なことに集中できます。そして、フレームの手動比較を自動化することも可能になります。
このアプローチは示唆に富んでいます:
クリエイター向け:手動チェックなしに、以前のバージョンと何が変更されたかを即座に特定
開発者向け:単なる動画ファイルとしてではなく、動画の内容そのものを理解するインテリジェントなアプリ構築
業界全体へ:エンベディングモデルが進化すれば、自動的にこの比較精度も上がっていきます
一番エキサイティングなのは、まだこれが始まりに過ぎないという点です。動画理解モデルの進化に伴い、SAGEの比較能力もレベルアップしていきます。現在はセグメント単位ですが、将来的にはシーンごと、特定オブジェクトの検知、さらには文脈やストーリー構成の理解にまで及ぶかもしれません。
土台はすでに整っています。あとは繰り返しの開発があるのみです。
その他の情報源
GitHub リポジトリ: GitHub上のSAGEページ
TwelveLabs ドキュメント: Marengoエンベディング応用ガイド
AWS S3: マルチパートアップロードのベストプラクティス
コサイン類似度: ベクトルの類似性の理解
付録:技術的詳細
エンベディングモデル
使用モデル:Marengo-retrieval-2.7
セグメント長:2秒
ベクトル次元数:768(セグメントあたり)
スコープ選択:
["clip", "video"](クリップ単位および動画全体の双方)
類似度の数式表現
コサイン距離:
1 - (dot(v1, v2) / (norm(v1) * norm(v2)))範囲:0(完全に同一)から 2(正反対)まで
目安:0-0.1(極めて類似)、0.1-0.3(多少の違い)、0.3-0.7(中程度の相違)、0.7+(著しい相違)
S3 詳細構成
分割サイズ(チャンク):10MB
アップロード方式:常にマルチパートを使用(信頼性向上のため)
署名付きURLの有効期限:1時間(3600秒)
ホスト地域(Region):構成調整(デフォルト:us-east-2)
提供API一覧
POST /validate-key- TwelveLabsのAPI有効性をチェックPOST /upload-and-generate-embeddings- ビデオアップロードとエンベディング生成タスクの開始GET /embedding-status/{embedding_id}- エンベディング生成の現在のステータス判定POST /compare-local-videos- エンベディングIDを用いた、2つの動画の比較実行POST /openai-analysis- 違いを説明するAI分析文章の任意生成GET /serve-video/{video_id}- ブラウザ再生用の署名付きURL生成GET /health- APIサーバのヘルスチェック(動作点検)
はじめに
あなたはトレーニング動画を2つのバージョンで撮影しました。内容は同じ、台本も同じですが、テイクが異なります。一方は照明が良く、もう一方は音声がよりクリアです。フレームごとのピクセル単位の違いだけでなく、コンテンツ、シーンの構成、または視覚要素の実際のセマンティック(意味的)な変化など、それらが正確にどこで異なっているかを素早く特定する必要があります。
従来の動画比較ツールには、根本的な限界があります。それは、意味ではなくピクセルを比較するという点です。これは、動画に以下のような違いがある場合に破綻します。
異なる解像度やアスペクト比
異なるエンコード設定や圧縮率
異なるカメラアングルや位置
照明やカラーグレーディングの違い
時間的なズレ(一方の動画が数秒遅れて始まるなど)
これが、私たちがSAGEを構築した理由です。SAGEは、動画に含まれるピクセルだけでなく、動画の中に何があるかを理解するシステムです。生の動画データを比較する代わりに、SAGEはTwelveLabs Marengoエンベディングを使用して動画セグメントのセマンティック表現を生成し、それらの表現を比較して意味のある違いを検出します。
重要な洞察とは何でしょうか?それは、セマンティックエンベディングは重要な要素を捉えるということです。人が歩いているカットは、同一のピクセルである必要はありません。同じアクションを表現していればよいのです。エンベディングを比較することで、技術的な理由でピクセルが異なる場合でも、動画のコンテンツが異なる場合を検出することができます。
SAGEは完全な比較ワークフローを作成します:
ストリーミングマルチパートアップロードを使用して動画をS3にアップロード(大容量ファイルを効率的に処理)
TwelveLabs Marengo-retrieval-2.7(2秒セグメント)を使用してエンベディングを生成
コサイン類似度を使用してエンベディングを比較(セマンティックな違いを検出)
同期されたタイムライン上で、サイドバイサイドの再生により違いを視覚化
オプションのAIを活用したインサイト(OpenAI連携)で違いを分析
その結果はどうなるでしょうか?単に何が違って見えるかだけでなく、何が変わったかを教えてくれるシステムが実現します。
前提条件
開始する前に、以下が準備されていることを確認してください:
Python 3.12以上がインストールされていること
Node.js 18以上またはBunがインストールされていること
APIキー:
TwelveLabs APIキー(エンベディング生成用)
OpenAI APIキー(任意、AI分析用)
S3へのアクセスが設定されたAWSアカウント(動画保存用)
リポジトリをクローンするためのGit
Python、FastAPI、Next.js、およびAWS S3に関する基本的な知識
ピクセルレベル比較の問題点
私たちが発見したのは、ピクセルレベルの比較は実世界のシナリオでは破綻するということです。以下の2つの動画を比較することを考えてみましょう:
動画 A: 1080p MP4、30fpsで撮影、H.264エンコード、自然光
動画 B: 720p MP4、24fpsで撮影、H.265エンコード、スタジオ照明
ピクセルレベルの比較では、両方の動画が同じコンテンツを表示していても、ほぼすべてのフレームが「異なる」と判定されます。根本的な問題は何でしょうか?それは、ピクセルは意味を表さないということです。
従来の方式が失敗する理由
従来の動画比較アプローチには、3つの重大な限界があります:
フォーマットの感度:解像度、コーデック、フレームレートの違いにより、誤検出(偽陽性)が発生する
時間的理解の欠如:フレームごとの比較では、時間的なコンテキストが見落とされる
セマンティック認識の欠如:「異なるピクセル」と「異なるコンテンツ」を区別できない
エンベディングによる解決策
TwelveLabs Marengoエンベディングは、動画内に何が含まれているかを表現し、どのようなピクセルが含まれているかを排除することでこれを解決します。各2秒のセグメントは、以下を捉える高次元ベクトルに変換されます:
視覚的コンテンツ(オブジェクト、シーン、アクション)
時間的パターン(動き、トランジション)
セマンティックな意味(見え方ではなく、何が起きているか)
これらのエンベディングを比較することで、単なるピクセルではなく、動画のコンテンツがいつ異なるのかを知ることができます。
デモアプリケーション
SAGEは、効率的な動画比較ワークフローを提供します:
動画のアップロード:2つの動画(一度に最大2ファイル)をアップロードすると、S3へのアップロードからエンベディング生成の完了まで、リアルタイムのステータス更新で処理プロセスを確認できます。
自動比較:両方の動画の準備が整うと、SAGEはセマンティックエンベディングを使用して自動的に比較を行い、手動でのフレームごとの確認なしにセグメントレベルで違いを特定します。
インタラクティブな分析:同期された左右並列再生、動画の違いを示す色分けされたタイムライン、類似度スコア付きの詳細なセグメントごとの内訳を通じて、違いを探索できます。
このプロセスはリアルタイムで処理されます。エンベディング生成の進行状況を観察し、類似度の割合が即座に計算されるのを確認し、正確なタイムスタンプでタイムライン上の違いを追跡します。違いのマーカーにジャンプして、動画間で具体的に何が変更されたかを正確に確認できます。
完全なデモアプリケーションを探索し、GitHubで完全なソースコードを見つけることができます。または、システムの動作を示すチュートリアル動画をご覧ください:

SAGEの仕組み
SAGEは、AWS S3ストレージ、TwelveLabsエンベディング、および高度なビジュアライゼーションを組み合わせた、洗練された動画比較パイプラインを実装しています:
システムアーキテクチャ
準備手順
1. リポジトリのクローン
コードはこちらで公開されています: https://github.com/aahilshaikh-twlbs/SAGE
git clone https://github.com/aahilshaikh-twlbs/SAGE.git cd
2. バックエンドのセットアップ
cd backend python3 -m venv .venv source .venv/bin/activate # Windows: .venv\Scripts\activate pip install -r requirements.txt cp env.example .env # .envにAPIキーを追加します
3. フロントエンドのセットアップ
cd ../frontend npm install # または bun install cp .env.local.example .env.local # NEXT_PUBLIC_API_URL=http://localhost:8000 を設定します
4. AWS S3の設定
# AWS認証情報を設定します(AWS SSOまたはIAMを使用) aws configure --profile dev # または環境変数を設定します: export AWS_ACCESS_KEY_ID=your_access_key export AWS_SECRET_ACCESS_KEY=your_secret_key export AWS_REGION
5. アプリケーションの起動
# ターミナル 1: バックエンド cd backend python app.py # ターミナル 2: フロントエンド cd frontend npm run dev # または bun dev
これらの手順が完了したら、http://localhost:3000 にアクセスして SAGE に接続してください!
実装解説
SAGEの動画比較システムを支えるコアコンポーネントを順に見ていきましょう。
1. S3へのストリーミング動画アップロード
SAGEは、ストリーミングマルチパートアップロードを使用して、大容量の動画ファイルを効率的に処理します:
async def upload_to_s3_streaming(file: UploadFile) -> str: """メモリの問題を回避するため、ストリーミングを使用してファイルをS3にアップロードします。""" file_key = f"videos/{uuid.uuid4()}_{file.filename}" # 大容量ファイルにはマルチパートアップロードを使用 response = s3_client.create_multipart_upload( Bucket=S3_BUCKET_NAME, Key=file_key, ContentType=file.content_type ) upload_id = response['UploadId'] parts = [] part_number = 1 chunk_size = 10 * 1024 * 1024# 10MBのチャンク while True: chunk = await file.read(chunk_size) if not chunk: break part_response = s3_client.upload_part( Bucket=S3_BUCKET_NAME, Key=file_key, PartNumber=part_number, UploadId=upload_id, Body=chunk ) parts.append({ 'ETag': part_response['ETag'], 'PartNumber': part_number }) part_number += 1 # マルチパートアップロードを完了 s3_client.complete_multipart_upload( Bucket=S3_BUCKET_NAME, Key=file_key, UploadId=upload_id, MultipartUpload={'Parts': parts} ) return f"s3://{S3_BUCKET_NAME}/{file_key}"
主な設計上の決定:
10MBのチャンク:アップロード効率とメモリ使用量のバランスを取ります
ストリーミング:ファイルをチャンクで処理し、ファイル全体をメモリにロードしないようにします
マルチパートアップロード:5GBを超えるファイルに必須であり、100MBを超えるファイルに推奨されます
署名付きURL:TwelveLabsが動画に安全にアクセスできるようにするための一時的なURLを生成します
2. TwelveLabsによるエンベディング生成
SAGEは、TwelveLabsの Marengo-retrieval-2.7 を使用して、非同期でエンベディングを生成します:
async def generate_embeddings_async(embedding_id: str, s3_url: str, api_key: str): """S3からの動画に対して非同期でエンベディングを生成します。""" # TwelveLabsクライアントを取得 tl = get_twelve_labs_client(api_key) # TwelveLabsが動画にアクセスするための署名付きURLを生成 presigned_url = get_s3_presigned_url(s3_url) # 署名付きHTTPS URLを使用してエンベディングタスクを作成 task = tl.embed.task.create( model_name="Marengo-retrieval-2.7", video_url=presigned_url, video_clip_length=2,# 2秒セグメント video_embedding_scopes=["clip", "video"] ) # タイムアウト付きで完了を待つ task.wait_for_done(sleep_interval=5, timeout=1800)# 30分 # 完了したタスクを取得 completed_task = tl.embed.task.retrieve(task.id) # エンベディングが生成されたことを検証 if not completed_task.video_embedding or not completed_task.video_embedding.segments: raise Exception("Embedding generation failed") # エンベディングと動画の長さを保存 embedding_storage[embedding_id].update({ "status": "completed", "embeddings": completed_task.video_embedding, "duration": last_segment.end_offset_sec, "task_id": task.id })
主な特徴:
2秒セグメント:粒度と処理時間のバランスを取ります
非同期処理:ノンブロッキングで、キューを介して複数の動画を処理します
タイムアウト処理:30分のタイムアウトにより、問題のある動画での処理の中断を防ぎます
検証:エンベディングが動画の全編をカバーしていることを確認します
3. セマンティック動画比較
SAGEは、エンベディングにコサイン距離を適用して動画を比較します:
async def compare_local_videos( embedding_id1: str, embedding_id2: str, threshold: float = 0.1, distance_metric: str = "cosine" ): """エンベディングIDを使用して、2つの動画を比較します。""" # エンベディングセグメントを取得 segments1 = extract_segments(embedding_storage[embedding_id1]) segments2 = extract_segments(embedding_storage[embedding_id2]) differing_segments = [] min_segments = min(len(segments1), len(segments2)) # 対応するセグメントを比較 for i in range(min_segments): seg1 = segments1[i] seg2 = segments2[i] # コサイン距離を計算 v1 = np.array(seg1["embedding"], dtype=np.float32) v2 = np.array(seg2["embedding"], dtype=np.float32) dot = np.dot(v1, v2) norm1 = np.linalg.norm(v1) norm2 = np.linalg.norm(v2) distance = 1.0 - (dot / (norm1 * norm2)) if norm1 > 0 and norm2 > 0 else 1.0 # しきい値を超えるセグメントにフラグを立てる if distance > threshold: differing_segments.append({ "start_sec": seg1["start_offset_sec"], "end_sec": seg1["end_offset_sec"], "distance": distance }) return { "differences": differing_segments, "total_segments": min_segments, "differing_segments": len(differing_segments), "similarity_percent": ((min_segments - len(differing_segments)) / min_segments * 100) }
なぜコサイン類似度(距離)なのか?
スケール不変性:正規化されたベクトルは、大きさ(解像度など)の違いを無視します
セマンティック(意味)重視:ピクセル値ではなく、意味の類似性を測定します
解釈の容易さ:0 = 同一、1 = 直交(無関係)、2 = 反対
構成可能なしきい値:さまざまなユースケースに合わせて感度を調整できます
4. 同期されたタイムラインの視覚化
フロントエンドは、同期された再生機能を備えたインタラクティブなタイムラインを作成します:
// 同期された動画再生 const handlePlayPause = () => { if (video1Ref.current && video2Ref.current) { if (isPlaying) { video1Ref.current.pause(); video2Ref.current.pause(); } else { video1Ref.current.play(); video2Ref.current.play(); } setIsPlaying(!isPlaying); } }; // 両方の動画で特定の時間にジャンプ const seekToTime = (time: number) => { const constrainedTime = Math.min( time, Math.min(video1Data.duration, video2Data.duration) ); video1Ref.current.currentTime = constrainedTime; video2Ref.current.currentTime = constrainedTime; setCurrentTime(constrainedTime); }; // 色分けされた相違点マーカー const getSeverityColor = (distance: number) => { if (distance >= 1.5) return 'bg-red-600';// 完全に異なる if (distance >= 1.0) return 'bg-red-500';// 大きく異なる if (distance >= 0.7) return 'bg-orange-500';// かなり異なる if (distance >= 0.5) return 'bg-amber-500';// 中程度に異なる if (distance >= 0.3) return 'bg-yellow-500';// やや異なる if (distance >= 0.1) return 'bg-lime-500';// わずかに異なる return 'bg-cyan-500';// ほぼ同一 };
ビジュアライゼーション機能:
同期された再生:両方の動画が同時に再生/ポーズされます
タイムラインマーカー:色分けされたセグメントで違いの深刻度を表示します
クリックによるシーク:マーカーをクリックすると、その時間に直接ジャンプします
類似度スコア:セグメントから算出した類似度の比率(%)を示します
5. オプションのAIパワー分析
SAGEは、OpenAIを使用して人間が読みやすい分析を生成するオプションを備えています:
async def generate_openai_analysis( embedding_id1: str, embedding_id2: str, differences: List[DifferenceSegment], threshold: float, video_duration: float ): """動画の違いに関するAI駆動の分析を生成します。""" prompt = f""" 以下のデータに基づいて、2つの動画間の違いを分析してください: 视频 1: {embed_data1.get('filename', 'Unknown')} 视频 2: {embed_data2.get('filename', 'Unknown')} 総再生時間: {video_duration:.1f} 秒 類似度しきい値: {threshold} 違いの件数: {len(differences)} これらの時間セグメントで違いが検出されました: {chr(10).join([f"- {d.start_sec:.1f} 秒から {d.end_sec:.1f} 秒 (距離: {d.distance:.3f})" for d in differences[:20]])} 以下を提供してください: 1. これらの違いが何を表している可能性があるかについての簡潔な分析 2. 比較に関する主要なインサイト 3. 重大な違いが発生している注目すべき時間セグメント """ response = openai.ChatCompletion.create( model="gpt-4", messages=[ {"role": "system", "content": "あなたは専門的な動画分析アシスタントです。"}, {"role": "user", "content": prompt} ], max_tokens=500, temperature=0.7 ) return { "analysis": response.choices[0].message.content, "key_insights": extract_insights(response), "time_segments": extract_segments(response) }
主な設計上の決定
1. フレーム単位ではなくセグメント単位の比較の採用
フレーム単位の比較の代わりに2秒セグメントを選択した理由は、主に次の3点です:
時間的コンテキスト:静止フレームだけでなく、動きやアクションを捉えることができます
計算効率:比較回数を削減できます(例:1分間の動画で1800フレームに対して300セグメント)
セマンティックの正確さ:個々のフレームよりも、エンベディングの方が「何が起きているか」を良く理解できます
トレードオフ:時間の精度は低くなりますが(フレーム精度に対して2秒精度)、より意味のある相違点を特定できます。
2. メモリ内処理ではなくストリーミングアップロードの採用
大容量の動画は数ギガバイトに及ぶことがあります。ファイル全体をメモリに読み込むと、サーバーがクラッシュする原因になります:
メモリ安全性:ストリーミングにより、ファイルを10MBのチャンクで処理します
スケーラビリティ:複数の大容量アップロードがあってもサーバーの応答性が維持されます
S3統合:S3に直接アップロードし、TwelveLabs用の署名付きURLを生成します
トレードオフ:アップロードのロジックが複雑になりますが、任意のサイズの動画を処理できるようになります。
3. ユークリッド距離ではなくコサイン類似度(距離)の採用
セマンティックな比較のためにコサイン距離を使用します:
スケール不変性:異なる画質の動画間でも一貫して機能します
セマンティック重視:大きさや強度ではなく、意味の類似性を測定します
解釈が可能:明確なしきい値を設定できます(0.1 = 微細、0.5 = 中程度、1.0 = 重大)
トレードオフ:ユークリッド距離よりも直感的ではありませんが、セマンティックな比較により適しています。
4. 並行型ではなくキューベースの処理の採用
エンベディングの生成には、動画あたり5〜30分かかる場合があります。これを順次処理します:
レート制限対策:TwelveLabs APIのレート制限超過を防ぎます
リソース管理:一度に1つの動画を処理することで、リソース使用量を一定に抑えます
エラーの隔離:失敗した動画が他の処理を阻害しません
トレードオフ:総スループットは遅くなりますが、より信頼性が高く予測可能になります。
5. データベースではなくメモリ内へのエンベディング保存
エンベディングをデータベースに永続化せず、メモリ内に保存します:
パフォーマンス:比較時における高速なアクセス(データベースクエリが不要)
シンプルさ:スキーマ移行やデータベースの管理が不要
一時的な性質:エンベディングはセッション固有であり、永続化の必要性が低いです
トレードオフ:サーバー再起動時にデータが失われますが、比較ワークフローにおいては許容範囲です。
パフォーマンス・エンジニアリング:私たちが学んだこと
SAGEの構築を通じて、大規模な動画処理。を扱うための貴重な教訓を得ました:
80/20の法則の適用
私たちは最適化作業の80%を、以下の3点に費やしました:
ストリーミングアップロード:分割アップロードにより、メモリの枯渇を防ぎます。2GBのファイルをすべてロードしてクラッシュさせるか、ストリーミングしてサーバーを安定させるかの違いはここにあります。
非同期処理:ノンブロッキングのエンベディング生成により、APIの応答性を維持します。ユーザーは、それぞれの完了を待つことなく複数の動画をアップロードできます。
セグメント検証:エンベディングが動画の全範囲をカバーしていることを確認し、サイレントエラーを防ぎます。結果を受け入れる前に、セグメント数、カバレッジ、再生時間を検証します。
うまくいかなかったこと(とその理由)
いくつかの最適化を試みましたが、どれもうまくいきませんでした:
並行エンベディング生成:TwelveLabsのレート制限に当たりました。順次処理の方が信頼性が高いことがわかりました。
エンベディングのキャッシュ:動画はそれぞれユニークであり、キャッシュは役に立ちませんでした。キャッシュするよりも再生成する方が効率的です
フレームレベルの比較:細かすぎて処理が遅く、誤検出が多すぎました。セグメントレベルが最適な妥協点でした。
長時間動画の処理
10分を超える動画には、特別な配慮が必要でした:
タイムアウト管理:30分のタイムアウトにより、問題のある動画でプロセスがハングするのを防ぎます
セグメント検証:セグメントが全編をカバーしているか検証(不完全なエンベディングを捕捉)
エラーメッセージ:サイレントに失敗させるのではなく、明確なエラーを返します(
"Embedding generation incomplete")
この結果、SAGEは10秒から20分までの動画を安定して処理できるようになりました。
この件に関する詳細情報は、私たちの 長時間動画処理ガイド に掲載されています。
データ出力
SAGEは網羅的な比較結果を生成します:
比較指標

総セグメント数:比較された2秒セグメントの数
不一致セグメント数:設定した閾値を超える距離(違い)があるセグメントの数
類似度の割合(%):
(総セグメント数 - 不一致セグメント数) / 総セグメント数 * 100相違点タイムライン:不一致度スコア付きのタイムスタンプ付きセグメント情報
ビジュアライゼーション

同期された動画プレイヤー:タイムライン付きの左右に並べた動画再生
色分けされたマーカー:重大度(緑 = 類似、赤 = 異なり)のビジュアル表現
インタラクティブなタイムライン:マーカーをクリックして相違点にシーク
相違点リスト:時間セグメントによる詳細な内訳
オプションのAI分析

要約:違いに関するハイレベルな分析
主要なインサイト:注目すべき発見事項の箇条書き表現
時間セグメント:重大な違いが発生している特定の瞬間
使用例
ケース1:2つのトレーニング動画を比較する
シナリオ:製品デモ動画の修正前・修正後バージョンを比較する
# 動画のアップロード POST /upload-and-generate-embeddings { "file": <video_file_1> } POST /upload-and-generate-embeddings { "file": <video_file_2> } # エンベディングの生成を待つ(ステータスエンドポイントをポーリング) GET /embedding-status/{embedding_id} # 動画の比較を行う POST /compare-local-videos?embedding_id1={id1}&embedding_id2={id2}&threshold=0.1
レスポンス:
{ "filename1": "demo_v1.mp4", "filename2": "demo_v2.mp4", "differences": [ { "start_sec": 12.0, "end_sec": 14.0, "distance": 0.342 }, { "start_sec": 45.0, "end_sec": 47.0, "distance": 0.521 } ], "total_segments": 180, "differing_segments": 2, "threshold_used": 0.1, "similarity_percent": 98.89 }
解釈:動画は98.89%類似しています。2つのセグメントが異なっています:
12~14秒:中程度の違い(距離:0.342)
45~47秒:中程度の違い(距離:0.521)
ケース2:かすかな違いを見つけるためにしきい値を微調整する
シナリオ:背景のわずかな変更など、ごくかすかな違いを特定する
# より高い感度のために、低いしきい値を使用します POST /compare-local-videos?embedding_id1={id1}&embedding_id2={id2}&threshold=0.05
結果:背景の微細な変化や照明の調整など、より多くの相違点を探知します。
ケース3:AI分析を生成する
シナリオ:違いに関する、人間にわかりやすい説明を生成する
POST /openai-analysis { "embedding_id1": "...", "embedding_id2": "...", "differences": [...], "threshold": 0.1, "video_duration": 360.0 }
レスポンス:
{ "analysis": "動画は全体的に類似した内容を示していますが、2つの顕著な相違点があります...", "key_insights": [ "テイク間で製品の位置変更がありました", "45秒時点で背景の照明が調整されています" ], "time_segments": [ "12-14 秒: 製品実演のアングル", "45-47 秒: 背景シーンの変更" ] }
現実世界のユースケース
SAGEを構築してテストする中で、このツールが最も価値を発揮する明確なパターンを特定しました:
コンテンツ制作
Before/Afterの比較:編集前と編集後の映像素材を比較する
バージョン管理:動画の改訂プロセスに沿って変更を追跡する
品質保証(QA):異なるパターンの動画間で一貫性を確認する
トレーニング & 教育
取扱説明用動画:更新前と更新後の動画バージョンを比較する
コースの整合性:すべてのレッスンが同じフォーマットを維持しているか確認する
コンテンツのアップデート:改訂された教材のどの部分が変更されたかを特定する
コンプライアンス(法令順守) & 検証
広告表現の検証:承認されたバージョンと実際に放送・配信されたバージョンを比較する
法的文書化:証拠動画などの変更を追跡する
ブランドガイドラインの整合性:マーケティング用動画がブランド規定に合致しているか検証する
このアプローチが効果を発揮する理由(それと機能しない場合)
SAGEはセマンティックな比較(単なるピクセルではなく、動画のコンテンツ意味の違いを検出すること)に優れています。 最もその強みを発揮するのは、以下のような場合です:
強みを発揮する場合:
✅ 動画の技術スペック(解像度、コーデック、フレームレート)が異なる場合
✅ ピクセルではなく、コンテンツ自体の違いを見つけたい場合
✅ 動画の構造が類似している場合(同じ長さ、同じようなシーン)
✅ 違いが意味のある内容である場合(シーンの変化、物体の追加など)
あまり効果的ではない場合:
❌ 動画同士が完全に異なる場合(無関係なコンテンツの比較)
❌ フレーム精度の正確なタイミングが必要な場合(SAGEは2秒セグメント単位です)
❌ 動画の長さが極端に異なる場合(比較は短い方の動画に合わせて切り詰められます)
❌ ピクセルレベルの厳密な同一性検証が必要な場合(代わりに従来の差分取得ツールをご使用ください)
スイートスポット(最適な環境)
SAGEは、同じコンテンツのバリエーション(同じ台本、同じ現場だがテイクが異なる、編集やバージョンが異なるもの)を比較するのに最適です。技術的な違いに左右されることなく、本質的に重要な違いを見つけ出します。
パフォーマンス・ベンチマーク
何百本もの動画を処理して得られた知見を以下に示します:
処理時間
1分間の動画:合計約2〜3分(アップロード + エンベディング)
5分間の動画:合計約5〜8分
10分間の動画:合計約10〜15分
15分間の動画:合計約15〜25分
内訳:アップロードは通常1分未満で完了します。エンベディング生成時間は、動画の長さにほぼ比例して増加します。
比較速度
比較ロジックの計算:動画の長さに関わらず1秒未満
タイムラインのレンダリング:一般的な動画で100ミリ秒未満
動画再生:ブラウザ本来のネイティブパフォーマンス
重要なインサイト:エンベディングが存在していれば、比較処理は一瞬で終わります。処理のボトルネックは比較そのものではなく、エンベディングの生成部分になります。
精度

セマンティック(意味的)相違点:実質的なコンテンツの変化を正確に捉えます
誤検出(偽陽性):適切なしきい値設定(デフォルトの0.1が推奨)で低く抑えられます
検出漏れ(偽陰性):極めて微細な変化は検出されない場合があります(その場合は値を下げてください)

しきい値のガイドライン:
0.05:超高感度(かすかな背景の変化なども特定)
0.1:標準的(感度のバランスに優れる)
0.2:低感度(主要な変化のみ)
0.5:最過保護(劇的なシーン変更のみ)

結論:動画比較の未来
SAGEは、「動画をピクセルではなく意味で比較するとどうなるか?」という実験から始まりました。私たちが発見したのは、セマンティックな比較が動画の「違い」に関する我々の見方を変えるということです。
ピクセルレベルのノイズに翻弄されることなく、コンテンツ自体の変更、シーン構成の違い、本質的なバリエーションといった本当に重要なことに集中できます。そして、フレームの手動比較を自動化することも可能になります。
このアプローチは示唆に富んでいます:
クリエイター向け:手動チェックなしに、以前のバージョンと何が変更されたかを即座に特定
開発者向け:単なる動画ファイルとしてではなく、動画の内容そのものを理解するインテリジェントなアプリ構築
業界全体へ:エンベディングモデルが進化すれば、自動的にこの比較精度も上がっていきます
一番エキサイティングなのは、まだこれが始まりに過ぎないという点です。動画理解モデルの進化に伴い、SAGEの比較能力もレベルアップしていきます。現在はセグメント単位ですが、将来的にはシーンごと、特定オブジェクトの検知、さらには文脈やストーリー構成の理解にまで及ぶかもしれません。
土台はすでに整っています。あとは繰り返しの開発があるのみです。
その他の情報源
GitHub リポジトリ: GitHub上のSAGEページ
TwelveLabs ドキュメント: Marengoエンベディング応用ガイド
AWS S3: マルチパートアップロードのベストプラクティス
コサイン類似度: ベクトルの類似性の理解
付録:技術的詳細
エンベディングモデル
使用モデル:Marengo-retrieval-2.7
セグメント長:2秒
ベクトル次元数:768(セグメントあたり)
スコープ選択:
["clip", "video"](クリップ単位および動画全体の双方)
類似度の数式表現
コサイン距離:
1 - (dot(v1, v2) / (norm(v1) * norm(v2)))範囲:0(完全に同一)から 2(正反対)まで
目安:0-0.1(極めて類似)、0.1-0.3(多少の違い)、0.3-0.7(中程度の相違)、0.7+(著しい相違)
S3 詳細構成
分割サイズ(チャンク):10MB
アップロード方式:常にマルチパートを使用(信頼性向上のため)
署名付きURLの有効期限:1時間(3600秒)
ホスト地域(Region):構成調整(デフォルト:us-east-2)
提供API一覧
POST /validate-key- TwelveLabsのAPI有効性をチェックPOST /upload-and-generate-embeddings- ビデオアップロードとエンベディング生成タスクの開始GET /embedding-status/{embedding_id}- エンベディング生成の現在のステータス判定POST /compare-local-videos- エンベディングIDを用いた、2つの動画の比較実行POST /openai-analysis- 違いを説明するAI分析文章の任意生成GET /serve-video/{video_id}- ブラウザ再生用の署名付きURL生成GET /health- APIサーバのヘルスチェック(動作点検)




