Taste of Tech Topics

Acroquest Technology株式会社のエンジニアが書く技術ブログ

GraphRAG Toolkit を使って Amazon Bedrock で GraphRAG を構築する

こんにちは、機械学習チーム YAMALEX の駿です。
YAMALEX は Acroquest 社内で発足した、会社の未来の技術を創る、機械学習がメインテーマのデータサイエンスチームです。
(詳細はリンク先をご覧ください。)

今回は AWS が提供するオープンソースツール GraphRAG Toolkit を使って、 GraphRAG に入門しようと思います。

GraphRAG Toolkit を使うと、 Amazon NeptuneAmazon OpenSearch Serverless を使用した GraphRAG システムをローコードで実装できます。

なお、2024年12月から Bedrock Knowledge Base で使用できるようになった GraphRAG とは別物なので、混同しないようご注意ください。

1. はじめに

1.1. GraphRAG とは

従来の RAG は、キーワード検索やベクトル検索を用いて関連情報を取得するのに対し、GraphRAG はナレッジグラフを利用して、検索結果の文脈をより深く理解し、関連性の高い情報を抽出する手法です。

従来の RAG ではキーワード検索やベクトル検索でヒットした情報しか回答生成に使用することができませんが、 GraphRAG ではキーワード検索やベクトル検索で得られた結果を起点にグラフを走査することで、検索にヒットしなかったが文脈的に関連のある情報も取得し、回答生成に活用できます。

すなわち、 GraphRAG は構造化されたエンティティ間の関係を利用して、より精度の高い情報検索と文脈理解を実現します。


GraphRAGの利点

1.2. GraphRAG Toolkit とは

GraphRAG Toolkit は AWS が提供するオープンソースの GraphRAG 用ライブラリで、 GraphRAG を使ったシステムの構築を簡易化する機能が多く提供されています。

github.com

Amazon Neptune Database をグラフ DB として、 Amazon OpenSearch Serverless をベクトル DB として使用可能です。

このツールキットを使用することで、非構造化データからエンティティとそのリレーションを抽出し、ナレッジグラフとして保存、ユーザーの質問に対してこのナレッジグラフをクエリすることで、より関連性の高い情報を提供するアプリケーションを構築できます。

今回は GraphRAG を試すためにこのツールキットを使用します。

GraphRAG Toolkit におけるナレッジグラフの基本構造

ナレッジグラフは、データをエンティティ(実体)とそのリレーション(関係性)として構造化し、意味的なつながりを表現するデータモデルです。

「エンティティ」と「リレーション」のネットワークとして情報を整理することで、関係性を考慮した検索や論理的な推論が可能です。

GraphRAG Toolkit 内では下記の用語と構成でナレッジグラフを構築します。

上にあるものほど、大きな抽象的な概念で、下に行くにつれて具体的になっていきます。

No 項目 概要
1 ドキュメント(Source) 元ドキュメントを表す属性 https://docs.aws.amazon.com/neptune/latest/userguide/intro.html
2 チャンク(Chunk) ドキュメントを小さい単位に分割したもの [](https://docs.aws.amazon.com/pdfs/neptune/latest/userguide/neptune-ug.pdf#intro) Amazon Neptune is a fast, reliable, fully managed graph database service that makes it easy to build and run applications that work with highly connected datasets. The core of Neptune is...
3 トピック(Topic) チャンクに記載されている主題 Amazon Neptune
4 文言(Statement) チャンクに書かれている文章や文言 The core of Neptune is a purpose-built, high-performance graph database engine.
5 事実(Fact) 文言から導かれる事実 Amazon Neptune HAS high-performance
6 エンティティ 事実の主語・目的語。実世界の「モノ」や「概念」にあたる Amazon Neptune

ナレッジグラフのイメージ(https://github.com/awslabs/graphrag-toolkit/blob/main/images/lexical-graph.png

1.3. Amazon Neptune とは

Amazon Neptune は、 AWS が提供するフルマネージド型のグラフデータベースサービスであり、データ間の関係性を効率的に管理・操作できます。

aws.amazon.com

Neptune は複数のグラフモデルをサポートし、柔軟なデータ操作が可能です。

ナレッジグラフの構築だけでなく、ソーシャルネットワーク分析、レコメンデーションシステムなど、多様なユースケースで活用されています。

使わない時間帯に「停止」したり、開発環境では T3 インスタンスなど比較的安価なインスタンスを使ったりして、コストを抑えられるのも、うれしいポイントです。

aws.amazon.com

2. 環境を構築する

今回構築する環境は GraphRAG Toolkit が用意している Cloudformation テンプレートで立ち上げます。

構成図

No 項目 概要
1 Amazon Neptune ナレッジグラフを保持し、検索する
2 Amazon OpenSearch Service グラフ検索と併せて使用するベクトル検索用
3 Amazon Bedrock チャンクからトピック、文言、事実、エンティティを抽出する。ベクトル DB に投入するベクトルを生成する
 抽出用モデル:Claude-3-Sonnet
 ベクトル化モデル:Titan Text Embedding V2
4 Notebook GraphRAG Toolkit を操作し、ドキュメントの投入と検索を行う

3. データを取り込む

上記 Cloudformation テンプレートを使用して環境を構築すると、サンプルノートブックがいくつか用意されています。

データの取り込みには 01-Combined-Extract-and-Build.ipynb (ドキュメントからのエンティティなどの抽出とグラフの構築をまとめて行う)、 02-Separate-Extract-and-Build.ipynb (エンティティなどの抽出とグラフの構築を別々に行う)のいずれかが使用できます。

ドキュメントの取り込みは下の手順で行われます。

  1. ドキュメントを細かい単位に分割する(チャンキング)
  2. チャンクをLLMに渡して、命題(proposition)を抽出する

    命題は、ナレッジグラフを作成するときに必要な6つの要素を抽出するための前処理として生成されるもので、 元のドキュメントに対して、複雑な文章は単純でわかりやすい文章に分割する、代名詞や略語を開く、といったクレンジングが行われています。

  3. 命題を LLM に渡して、トピック、文言、事実、エンティティを抽出する

  4. 抽出されたエンティティなどからナレッジグラフを構築し、グラフ DB に取り込む
  5. 文言を LLM に渡してエンベディングを行い、ベクトル DB に取り込む

GraphRAG Toolkit を使用すると、 extract メソッドを呼ぶだけで、上記手順をひとまとめに実施してくれるため、とても簡単に始めることができます。

3.1. 取り込まれるデータを確認する

今回は、 02-Separate-Extract-and-Build.ipynb を使用して、抽出用モデルが元のドキュメントをどのように処理してグラフ DB に取り込める形にしているのかを確認してみます。

ノートブック中の Extract フェーズを実施すると、 LLM がドキュメントを解釈し、エンティティ、属性の抽出、そしてそれぞれのリレーションを抽出します。
抽出結果は extracted/ ディレクトリに保存されます。

設定を変更することで S3 に保存することも可能なため、一度処理したものをS3に貯めておき、新しいデータが増えナレッジグラフを構築しなおす際や、別 DB に同じナレッジグラフを構築したくなった時などに、複数回抽出作業をする必要がなくなり、 LLM の制限やお財布にやさしくすることができます。

実際に extracted/ に出力された JSON の中身は下記のようになっています。(スペースの都合上、大幅に省略しています)

上で説明した、命題、トピック、文章、事実、エンティティがそれぞれ入れ子になって保存されていることが分かります。

{
  "id_": "aws:xxxx",
  "metadata": {
    "url": "https://docs.aws.amazon.com/neptune-analytics/latest/userguide/what-is-neptune-analytics.html",
    "aws::graph::propositions": [
      // 命題
      "Neptune Analytics is a memory-optimized graph database engine for analytics",
      "Neptune Analytics allows getting insights and finding trends by processing large amounts of graph data in seconds"
    ],
    "aws::graph::topics": {
      "topics": [
        {
          // トピック
          "value": "Neptune Analytics",
          "entities": [
            {
              // エンティティ
              "value": "Neptune Analytics",
              "classification": "Software"
            }
          ],
          "statements": [
            {
              // 文言
              "value": "Neptune Analytics is a memory-optimized graph database engine for analytics",
              "facts": [
                // 事実
                {
                  "subject": {
                    "value": "Neptune Analytics",
                    "classification": "Software"
                  },
                  "predicate": {
                    "value": "OPTIMIZED FOR"
                  },
                  "complement": "memory"
                }
              ]
            }
          ]
        }
      ]
    }
  }
}

なお、日本語のドキュメントに対しても、良い感じに抽出してくれました。

{
  "id_": "aws:xxxx",
  "metadata": {
    "aws::graph::propositions": [
      // 命題
      "知識表現の方法",
      "ナレッジグラフはデータをエンティティと関係性のネットワークとして表現する",
      "ナレッジグラフは情報の意味を捉えやすくする強力な手法である"
    ],
    "aws::graph::topics": {
      "topics": [
        {
          // トピック
          "value": "Knowledge Representation",
          "entities": [
            {
              // エンティティ
              "value": "ナレッジグラフ",
              "classification": "Concept"
            }
          ],
          "statements": [
            {
              // 文言
              "value": "ナレッジグラフはデータをエンティティと関係性のネットワークとして表現する",
              "facts": [
                // 事実
                {
                  "subject": {
                    "value": "ナレッジグラフ",
                    "classification": "Concept"
                  },
                  "predicate": {
                    "value": "REPRESENTS"
                  },
                  "object": {
                    "value": "データ",
                    "classification": "Concept"
                  }
                }
              ]
            }
          ]
        }
      ]
    }
  }
}

3.2. 取り込まれたデータを確認する

CloudFormation で Notebook をデプロイした際、同時にグラフエクスプローラーがデプロイされています。
このグラフエクスプローラーを開くことで、構築されたナレッジグラフを実際に確認できます。

docs.aws.amazon.com

グラフエクスプローラーの開き方

下の図はAmazon NeptuneのドキュメントをGraphRAG Toolkit を使って取り込んで作成されたナレッジグラフの一部を表示したものです。

上に書いたように、ドキュメント、チャンク、トピック、文言、事実、エンティティの階層に分かれて、それぞれのリレーションが記載されていることが分かります。


実際に構築されたナレッジグラフの一部

4. 検索する

GraphRAG では、ベクトル検索で起点となるノードを検索し、その後ナレッジグラフを走査して最終的な検索結果を返却します。

GraphRAG Toolkit は大きく2つの検索方法を提供しています。

No 検索方法 利点 欠点 用途
1 TraversalBasedRetriever ・処理が軽量
・結果の再現性が高い
・未知の単語や固有名詞に強い
・柔軟性が低い
・検索精度が低め
カテゴリごとに整理された FAQ など
2 SemanticGuidedRetriever ・質問の意図に沿った情報を探索できる
・複雑な関連性を考慮できる
・検索結果の多様性を確保できる
・計算コストが高く、処理が重い 質問の意図に合った情報、多様性のある情報など柔軟で高度な情報検索が必要な場合

4.1. TraversalBasedRetriever

TraversalBasedRetriever はグラフ構造を順番に走査することで、情報を拡張する検索方法です。

決まった順番にナレッジグラフを走査するため、処理が軽量です。

TraversalBasedRetriever にも2つの手法があり、どちらか片方、もしくは両方を組み合わせて使用することが可能です。
デフォルト設定では両方を組み合わせた結果を返却します。

  1. EntityBasedSearch

    クエリ文字列からキーワードを抽出し、マッチするエンティティをグラフから探します(Bottom up)。
    マッチするエンティティを見つけたら、そこから事実、命題、トピックと大きい概念に向かって走査することで、検索を行います。
    未知語や固有名詞であっても、事前に整理されたナレッジグラフに従って、関連した情報を集められるのが特徴です。

    エンティティから出発するため、そのエンティティに関わる情報を幅広く取得できる傾向にあります。

  2. ChunkBasedSearch

    チャンクに対してクエリとのベクトル類似度を使いて検索を行います(Top down)。
    ヒットしたチャンクから、トピック、命題、事実と小さい概念に向かって走査することで、検索を行います。

    クエリに意味的に近いところから出発するため、より関連度の高い情報に絞って取得できる傾向にあります。

4.2. SemanticGuidedRetriever

SemanticGuidedRetriever は文言(Statement)をベクトル化したものに対して検索を行います。
ベクトル検索で取得した文言からビームサーチとパス解析を用いて、近隣のノードから探索して関連度の高いものを取得します。

また、リランクで質問の意図に沿った検索結果に絞る、多様性フィルターで類似した情報ばかりではなく多様性のある回答になるようにするなど、回答生成に必要な情報を検索するための仕組みが使われています。

ベクトル検索とグラフ検索のハイブリッドを行うことで、関連度を高く保ちつつ、幅広い検索結果を返すことができますが、その分処理が重くなります。

4.3. 検索結果を確認する

GraphRAG Toolkit に含まれる 04-Querying.ipynb を使って上の2つの検索方法を試すことができます。

各Retrieverに「Neptune Database と Neptune Analytics の違いは何ですか?」と聞いた場合の LLM の回答と回答に使用したドキュメントを確認しました。

4.3.1. TraversalBaseRetriever

Neptune DatabaseとNeptune Analyticsは補完的な関係にあります。

Neptune Databaseは、管理型のグラフデータベースサービスです。
データの永続的な保存、バックアップ、レプリケーションなどの機能を提供します。
[https://docs.aws.amazon.com/neptune/latest/userguide/intro.html]

一方、Neptune Analyticsは大規模なグラフデータを高速に分析するためのインメモリエンジンです。
Neptune DatabaseやS3からグラフデータを読み込み、事前構築されたグラフアルゴリズムや
カスタムクエリを実行して分析を行います。
[https://docs.aws.amazon.com/neptune-analytics/latest/userguide/what-is-neptune-analytics.html]

つまり、Neptune Databaseはグラフデータの永続的な保存と管理を担い、
Neptune Analyticsはそのデータに対する高速な分析処理を可能にするサービスです。
データの保存と分析を分離することで、それぞれの機能に特化したパフォーマンスが得られます。
[https://docs.aws.amazon.com/neptune-analytics/latest/userguide/neptune-analytics-features.html]

■ドキュメント

[
  {
    "source": "https://docs.aws.amazon.com/neptune-analytics/latest/userguide/what-is-neptune-analytics.html",
    "topic": "Neptune Analytics",
    "statements": [
      "Neptune Analytics can load data from a Neptune Database graph",
      "Neptune Analytics complements Amazon Neptune Database, a popular managed graph database",
      "Neptune Analytics can load data from a Neptune Database snapshot",
      "The Neptune Analytics vs Neptune Database page explains when to use Neptune Analytics and when to use Neptune Database",
      "Neptune Analytics can load graph data stored in Amazon S3"
    ]
  },
  {
    "source": "https://docs.aws.amazon.com/neptune/latest/userguide/intro.html",
    "topic": "Amazon Neptune",
    "statements": [
      "Neptune Analytics is an analytics database engine.",
      "Neptune Analytics complements Neptune database.",
      "The Neptune database has continuous backup to Amazon S3",
      "Amazon Neptune automatically detects database crashes and restarts",
      "Amazon Neptune increases database performance and availability"
    ]
  }
]

4.3.1. SemanticGuidedRetriever

Neptune DatabaseとNeptune Analyticsは異なる製品で、以下のような違いがあります。

Neptune Databaseはマネージド型のグラフデータベースサービスです。
データの永続的な保存と、低レイテンシーのクエリ実行を行うことができます。
[https://docs.aws.amazon.com/neptune/latest/userguide/intro.html]

一方、Neptune Analyticsはメモリ上でグラフデータを分析するためのエンジンです。
大規模なグラフデータセットをメモリに読み込み、事前に用意された高度な分析アルゴリズムや
独自のCypherクエリを高速に実行できます。
[https://docs.aws.amazon.com/neptune-analytics/latest/userguide/what-is-neptune-analytics.html] 
Neptune Analyticsは、データ分析やデータサイエンス、探索的なワークロードに適しています。

つまり、Neptune Databaseはデータの永続的な保存と低レイテンシーのクエリ実行を、
Neptune Analyticsはメモリ上での大規模グラフデータの高速分析を主な目的としています。
Neptune Analyticsは、Neptune Databaseからデータを読み込んで分析することもできます。
[https://docs.aws.amazon.com/neptune-analytics/latest/userguide/what-is-neptune-analytics.html]

■ドキュメント

<sources>
  <source_1>
    <source_1_metadata>
      <url>
        https://docs.aws.amazon.com/neptune-analytics/latest/userguide/what-is-neptune-analytics.html</url>
    </source_1_metadata>
    <statement_1.1>Neptune Analytics is a memory-optimized graph database engine for analytics</statement_1.1>
    <statement_1.2>Neptune Analytics is a memory-optimized graph database engine for analytics</statement_1.2>
    <statement_1.3>Neptune Analytics can load data from a Neptune Database graph</statement_1.3>
    <statement_1.4>Neptune Analytics can load data from a Neptune Database snapshot</statement_1.4>
    <statement_1.5>Neptune Analytics is an ideal choice for data-science workloads that require fast
      iteration for data, analytical and algorithmic processing, or vector search on graph data</statement_1.5>
  </source_1>
  <source_2>
    <source_2_metadata>
      <url>https://docs.aws.amazon.com/neptune/latest/userguide/intro.html</url>
    </source_2_metadata>
    <statement_2.1>Neptune Analytics is an analytics database engine.</statement_2.1>
    <statement_2.2>Neptune Analytics is a solution for quickly analyzing existing graph databases.</statement_2.2>
    <statement_2.3>Neptune Analytics is a solution for quickly analyzing graph datasets stored in a
      data lake.</statement_2.3>
    <statement_2.4>Amazon Neptune is a fully managed graph database service</statement_2.4>
    <statement_2.5>Amazon Neptune is a fully managed graph database service</statement_2.5>
  </source_2>
</sources>

なんとなく、 SemanticGuidedRetriever を使った場合の方が正しい回答をしてくれているように見えますが、各検索方法での検索精度の違いなどは今後の検証課題としたいと思います。

なお、 Traversal...Semantic... でレスポンスに含まれるドキュメントの型が異なりますが、これは今後のアップデートで統一してくれるとのことです。

まとめ

GraphRAG Toolkit を使って、 GraphRAG がどのようにデータを処理しているのかを見てきました。
実際に動かしてみながら中身を確認することで、 GraphRAG の特性を理解できたと思います。

そして、 GraphRAG Toolkit を使って、とても簡単に GraphRAG システムを構築できることが分かりました。
グラフ DB に入れるまでの面倒な処理をラップしてくれているのがありがたいですね。

Acroquest Technologyでは、キャリア採用を行っています。
  • Azure OpenAI/Amazon Bedrock等を使った生成AIソリューションの開発
  • ディープラーニング等を使った自然言語/画像/音声/動画解析の研究開発
  • マイクロサービス、DevOps、最新のOSSクラウドサービスを利用する開発プロジェクト
  • 書籍・雑誌等の執筆や、社内外での技術の発信・共有によるエンジニアとしての成長
少しでも上記に興味を持たれた方は、是非以下のページをご覧ください。 www.wantedly.com

Amazon Bedrock Knowledge Bases のベクトルDBに Aurora Serverless v2 を利用する

こんにちは。大塚です。

今回は、Amazon Bedrock Knowledge Basesを使い、ベクトルDBとしてAmazon Aurora PostgreSQLの利用を試したいと思います。
昨年の12月にはBedrock Knowledge BasesのベクトルDBとしてAurora PostgreSQLをクイック作成できるようになり、セットアップ手順が大幅に簡素化されました。
このクイック作成機能も活用してセットアップをしてみます。

aws.amazon.com

1. ベクトルストアとして Aurora PostgreSQL をセットアップする

事前準備

今回はRAG用の外部データソースとしてS3を利用します。
後ほど、LLMの回答にS3に置かれた資料が参照されているかを確認します。

Knowledge Basesの作成

AWSマネジメントコンソール上で、Bedrockのページへ行き、GUI操作だけで簡単にKnowledge Basesを作成できます。

「Knowledge Base with vector store」をクリックすると、Knowledge Bases作成画面に遷移します。

「ステップ2 データソースを設定」では、事前準備で作成したS3のURIを指定します。
そして「ステップ3 埋め込みモデルを選択し、ベクトルDBを設定する」が本題です。
ベクトルDBの選択肢に「Amazon Aurora PostgresSQL Serverless」という新しい項目が増えているのでこちらを選択しましょう!
※ベクトルDBの選択肢はリージョンによって異なりますが、この記事では東京リージョンで試しています。

あとは「次へ」を押下し作成内容を確認して完了です。
GUI操作だけで済むので直感的で簡単ですね!

RDSのコンソールから作成されたデータベースを確認できます。

DBテーブルは下記のように作成されていました。

Bedrock_Knowledge_Base_Cluster=> \d bedrock_knowledge_base
        Table "bedrock_integration.bedrock_knowledge_base"
  Column   |        Type         | Collation | Nullable | Default
-----------+---------------------+-----------+----------+---------
 id        | uuid                |           | not null |
 embedding | public.vector(1536) |           |          |
 chunks    | text                |           |          |
 metadata  | jsonb               |           |          |
Indexes:
    "bedrock_knowledge_base_pkey" PRIMARY KEY, btree (id)
    "bedrock_knowledge_base_embedding_idx" hnsw (embedding public.vector_l2_ops)

Knowledge Basesのテスト

作成したKnowledge Basesを選択すると「ナレッジベースをテスト」という画面があります。
ここでLLMに質問をして想定した回答が返ってくるかテストします。
今回は「アジャイル開発のメリットはなんですか?」という質問をしてみました。
想定通りに事前準備でS3に配置しておいた資料を参照した回答が返ってきたのでうまく動いていそうです。


2. OpenSearch Severless と比較する

ベクトルDBとして利用される代表サービスとしてOpenSearch Serverlessがあります。
ここでAurora Severless v2を利用する場合との差分を整理しておきます。

機能

ベクトルDBとしてAurora PostgreSQLを利用する場合、検索タイプはセマンティック検索のみサポートされています。

一方、OpenSearch Serverlessを利用する場合は、ハイブリッド検索とセマンティック検索を選択できます。

  • セマンティック検索: 単なるキーワード一致ではなく意味的に関連する情報を検索する
  • ハイブリッド検索: キーワードによる検索とセマンティック検索を組み合わせて検索する

検索の機能性としては、OpenSearch Serverlessの方がすぐれていますね。
キーワードによる検索も組み込みたい場合はOpenSearch Severlessを選択しましょう。

コスト

OpenSearch Serverlessのコンピューティング料金は、利用していないときでも時間単位の課金が発生します。
米国東部(バージニア北部)だと1ユニットあたり0.24USD/hです。
ドキュメントによると少なくとも2ユニットについて課金されると記載されているので、0.24USD/h * 720h * 2 = 345USD/月程度かかります。

一方でAurora Severless v2では、0.12USDと安価で、0ユニットへのスケーリングにも対応しています。
そのため、利用していないときのコストを抑えることができます。

aws.amazon.com

先ほどクイック作成したAurora PostgreSQLインスタンス設定を確認すると、想定通り0ユニットへのスケーリングに対応していました。

CloudWatchでユニット使用率(ACU)を見ると、利用していないときに0ユニットへスケーリングされていることを確認できました。

3. 検索速度を確認する

最後に、ドキュメント数を増やした場合の検索速度やACUを確認してみます。
データソースには、BBC News Summary | Kaggleを使用し、約9,000件のデータをS3に配置しました。

「1. Bedrock Knowledge BasesのベクトルDBとしてAurora PostgreSQLをセットアップする」と同様にLLMへ質問してみました。
結果はドキュメント数が少なかったときと同様に数十ミリ秒で回答が返ってきました。
今回利用したデータセット程度であれば検索速度も特に問題なさそうです。

ACUを確認するとドキュメント投入時のACUが30%程度(16(最大スケール時のユニット数) * 0.3 = 5ユニット)、LLMによる回答生成時のACUが15%程度(16 * 0.15 = 2.5ユニット)でした。

ベクトルDBのReadLatencyは0.01秒に収まっていたのでストレスなく使えそうです。

4. まとめ

今回は、Bedrock Knowledge BasesでベクトルDBにAurora Serverless v2の利用を試しました。
クイック作成機能もあり、数クリックのGUI操作だけで簡単にベクトルDBを用意できました。
利用していないときのコストを抑えたいという要求も手間なく実現できそうです。

最後に

Bedrock Knowledge Basesを削除するだけでは、ベクトルDBなどのクイック作成した他のリソースは削除されません。
不要であれば手動で削除することを忘れないようにしましょう。

Acroquest Technologyでは、キャリア採用を行っています。

  • Azure OpenAI/Amazon Bedrock等を使った生成AIソリューションの開発
  • ディープラーニング等を使った自然言語/画像/音声/動画解析の研究開発
  • マイクロサービス、DevOps、最新のOSSクラウドサービスを利用する開発プロジェクト
  • 書籍・雑誌等の執筆や、社内外での技術の発信・共有によるエンジニアとしての成長

少しでも上記に興味を持たれた方は、是非以下のページをご覧ください。

www.wantedly.com

時系列基盤モデルChronos-Boltでお手軽に時系列予測を試してみた

皆さんこんにちは
Acroquestのデータサイエンスチーム「YAMALEX」のチームリーダ、@tereka114です。
YAMALEXチームでは、コンペティションへの参加や自社製品開発、技術研究などに日々取り組んでいます。
(詳細はこちらをご覧ください。)

LLMやVLMをはじめとした基盤モデルが近年、多く公開されています。
画像や自然言語処理といったものとは異なりますが、時系列モデリングでも基盤モデルが利用されようとしています。

本記事では、時系列基盤モデルの一つである「Chronos-Bolt」を紹介します。

Chronos-Boltについて

Chronosは端的に言えば、予め様々な時系列を学習させることで、適用するデータの学習をしなくとも、時系列予測を可能とする時系列基盤モデルです。
そのChronosの後継モデルがChronos-Boltになり、Chronosと比較して、精度、性能共に大きく向上しました。

参考1)Chronosの論文
arxiv.org

本記事ではAutoGluonを利用して、Chronos-Boltでの時系列予測処理を実装してみました。
AutoGluonは、AWSが中心となって開発されているOSSのAutoMLフレームワークです。AutoGluonには、いくつかの時系列予測モデルが実装されており、Chronos-Boltも含まれています。

参考2)追加学習なしの zero-shot で高精度な時系列予測 : Chronos-Bolt を AutoGluon で利用する(AWS公式ブログ)
aws.amazon.com

1. 学習不要で高精度な推論ができる。
Chronos/Chronos-Boltでは、学習なしで時系列予測を可能とするために、既存の様々な時系列データに加え、TS-MixupやKernelSynthといった合成データを作成する手法も利用し、多くの時系列データを学習しています。
それにより、ドメインに対する学習がない場合でも精度をある程度担保した予測ができます。
一般的に、予測精度を高めるためには学習が必要ですが、Chronos-Boltは学習データが存在しないケースにおいても一定の精度を見込むことができます。

2. Chronosモデルと比較して高速(最大260倍)
Chronosモデルでは一度に1つのステップの未来を予測しています。
しかし、Chronos-Boltは複数の予測(t, t+1, t+2)を同時に推論することで高速化を実現しています。公式ブログのベンチマークによれば、Chronosと同じモデルの大きさで、最大260倍の性能を発揮しています。

Chronos-Boltの推論速度(AWS公式ブログより)

3. Chronosモデルと比較して高精度
公式のベンチマークによれば、Chronosモデルと比較して高精度の結果が得られています。
この点においてもChronos-Boltを利用するメリットがあります。

Chronos-Boltを用いた精度比較(AWS公式ブログより)

Chronos-Boltを使った時系列予測

本記事ではChronos-Boltを利用した次の3つのパタンを紹介します。

パタン 説明
ゼロショット推論 学習なしで推論する
Finetune実施 学習ありで推論する
回帰モデルを利用した特徴量の外挿利用 推論する時系列以外にも補助的に利用する特徴量を使う。

準備

必要なライブラリのインストール

Chronos-Boltを利用するためには、事前にautogluonをインストールする必要があります。

pip install autogluon
データ準備

データセットはKaggleで公開されている「Store Sales - Time Series Forecasting」を利用します。
エクアドルに存在するリテール「Corporación Favorita」の商品売上数のデータを利用します。
www.kaggle.com

そのままのデータ形式では、適用が難しいので、次の実装で加工します。

import polars as pl

transaction = pl.read_csv("./data/store-sales-time-series-forecasting/transactions.csv")
holiday = pl.read_csv("./data/store-sales-time-series-forecasting/holidays_events.csv")

transaction = transaction.join(holiday, how="left", on="date")
transaction = transaction.fill_null("null")
transaction = transaction.with_columns(
    pl.col("date").str.to_date()
)
transaction = transaction.with_columns(
    pl.col("date").dt.month().alias("month"),
    pl.col("date").dt.day().alias("day"),
    pl.col("date").dt.weekday().alias("weekday")
)

df = transaction
train_df = df.filter(
    pl.col("date").dt.year() < 2017
)
train_df.write_csv("./data/train.csv")
valid_df = df.filter(
    (pl.col("date").dt.year() >= 2017) & (pl.col("date").dt.month() == 1)
)
valid_df = pl.concat([train_df, valid_df], how="diagonal_relaxed")
valid_df.write_csv("./data/valid.csv")

本解析で利用する主要なパラメータを記載します。

項目 説明
date 日付
store_nbr 店舗
transactions 取引量★今回の予測値
type 祝日ORイベント
locale 適用エリア
locale_name 地域などの名前
month dateから抽出した月
day dateから抽出した日
weekday dateから抽出した曜日

ゼロショット推論

ゼロショットで推論するのは非常に簡単です。
次の実装で対応できます。

id_columnはグルーピングするカテゴリ、timestamp_columnは時間を示す列、また、targetで予測列を指定します。
TimeSeriesPredictor.fitメソッドにて、presets="bolt_base"を指定することで、Chronos-Boltを利用できます。この際、bolt_baseをbolt_small/bolt_tinyにすることで、Chronos-Boltの別サイズのモデルの利用ができます。

import polars as pl
from autogluon.timeseries import TimeSeriesDataFrame, TimeSeriesPredictor

train_data = TimeSeriesDataFrame.from_path( 
     "data/train.csv", 
     id_column="store_nbr", 
     timestamp_column="date", 
)
predictor = TimeSeriesPredictor(prediction_length=30, freq="D", target="transactions").fit(train_data, presets="bolt_base")
predictions = predictor.predict(train_data)

推論結果を可視化します。
autogluonに可視化用のutility関数がありますので、その関数を利用します。
一時的にデータが欠損している部分がありますが、学習なしでも概ね追従できていることがわかります。

test_data = TimeSeriesDataFrame.from_path( 
     "data/valid.csv", 
     id_column="store_nbr", 
     timestamp_column="date", 
)
predictor.plot(test_data,predictions,max_history_length=365,item_ids=[25])

本プロットの青線が実績、オレンジの線がChronos-Boltによる予測結果を示しています。
また、オレンジは線の前後で網掛けになっていますが、こちらは80%信頼区間が表現されています。

推論結果(ゼロショット)

Finetune

次にFinetuneして学習した結果を利用して、時系列予測を試みます。
Finetuneは次の実装で可能です。
この実装では、ゼロショットとFinetuneしたモデルの2パタンのモデルを作成されます。
hyperparametersに含まれるChronosキーの中にリストがあり、ゼロショットとFinetuneしたモデルの2パタンを定義しています。主なポイントとして、fine_tuneをTrueにすることです。

predictor = TimeSeriesPredictor(prediction_length=31, eval_metric="MASE", freq="D", target="transactions").fit( 
     train_data, 
     hyperparameters={ 
         "Chronos": [ 
             {"model_path": "bolt_base", "ag_args": {"name_suffix": "ZeroShot"}}, 
             {"model_path": "bolt_base", "fine_tune": True, "ag_args": {"name_suffix": "FineTuned"}}, 
         ] 
     }, 
     enable_ensemble=False, 
     time_limit=600, 
)

終了後に、次の実装でゼロショットとFinetuneしたモデルの評価結果を取得できます。

predictor.leaderboard(test_data)
学習結果(外挿あり)

また、本predictorの実装を利用した結果を次の通り予測、プロットすることが可能です。

predictions = predictor.predict(train_data, model="ChronosZeroShot[bolt_base]")
predictor.plot(test_data,predictions,max_history_length=365,item_ids=symbols, max_num_item_ids=len(symbols))

ゼロショットと比較して、より実績にフィットしているように見えます。

推論結果(Finetune

特徴量の外挿利用

予測対象となる時系列データ以外にも補助的に利用できる時系列が含まれる場合があります。
例えば、店舗の売上に対して、来店に影響する降水量といったものが関係する場合があり、そのような場合に利用ができます。
ただし、外挿は当日のデータを利用することになりますので、検証するうえで、当日のデータを獲得できない場合、当日の予測をすることは難しいです。
(例:予測前日に予測当日の正確な降水量を使う場合)

外挿を利用する場合の予測値は次の通りの式になります。
回帰予測値 = Chronos-Boltの予測値 + CatBoost(外挿回帰モデル)の予測値

主な変更点として、TimeSeriesPredictorのknown_covariates_namesに外挿する列を指定します。
今回のモデルでは日付特徴(month, day, weekday)、祝日情報(type, locale, locale_name)を入力します。実装は次の通りです。

predictor = TimeSeriesPredictor(
    prediction_length=31,
    eval_metric="MASE",
    target="transactions",
    known_covariates_names=["type", "locale", "locale_name", "month", "day", "weekday"],
    freq="D"
).fit(
    train_data,
    hyperparameters={
        "Chronos": [
            {"model_path": "bolt_base", "ag_args": {"name_suffix": "ZeroShot"}},
            {
                "model_path": "bolt_base",
                "covariate_regressor": "CAT",
                "target_scaler": "standard",
                "ag_args": {"name_suffix": "WithRegressor"},
            },
        ],
    },
    time_limit=600,
    enable_ensemble=False,
)

Finetuneと同じように比較が可能です。

predictor.leaderboard(test_data)
学習結果(外挿)

今回の予測では、外挿となる特徴量を与えることが必要です。
こちらの推論は31日全てのデータが揃っているものでなければ、難しいので、全て揃っているstore_nbrが25のデータにフィルタリングして推論結果を可視化します。
実装は次の通りです。

filtered_id_df = train_data.to_data_frame().loc[25]
filtered_id_df['item_id'] = 25
filtered_id_df['timestamp'] = filtered_id_df.index
train_data = TimeSeriesDataFrame.from_data_frame(filtered_id_df)

filtered_id_df = test_data.to_data_frame().loc[25]
filtered_id_df['item_id'] = 25
filtered_id_df['timestamp'] = filtered_id_df.index
test_data = TimeSeriesDataFrame.from_data_frame(filtered_id_df)

predictions = predictor.predict(train_data, model="ChronosWithRegressor[bolt_base]", 
                                known_covariates =test_data
                               )
predictor.plot(test_data,predictions,max_history_length=365,
               item_ids=[25], max_num_item_ids=1)

更にFinetuneと比較して実績線に近くなったように見えます。

推論結果(外挿)

最後に

Auto GluonからChronos-Boltを簡単に利用し、時系列予測を試せました。
LLMのような基盤モデルの考えが、時系列モデルにも応用ができるのは非常に面白く、今後の発展も期待できそうです。

Acroquest Technologyでは、キャリア採用を行っています。

  • Azure OpenAI/Amazon Bedrock等を使った生成AIソリューションの開発
  • ディープラーニング等を使った自然言語/画像/音声/動画解析の研究開発
  • マイクロサービス、DevOps、最新のOSSクラウドサービスを利用する開発プロジェクト
  • 書籍・雑誌等の執筆や、社内外での技術の発信・共有によるエンジニアとしての成長

少しでも上記に興味を持たれた方は、是非以下のページをご覧ください。

www.wantedly.com

Amazon Bedrock Flows で対話的なフローを行ってみる

はじめに

こんにちは一史です。
冬にもかかわらず、自宅のモンステラが新芽を出しており、生命力に驚いています。

さて最近、Amazon Bedrock Flowsの新機能としてマルチターン形式の会話機能がサポートされました。
aws.amazon.com

従来は、ユーザーが1回のプロンプトで処理に必要な情報を全て入力する必要がありましたが、マルチターン形式の会話により不足している情報を適宜AIが聞き返すことが簡単に実現できます。
今回はこのマルチターン形式の会話機能により、対話的なフローを行ってみます。

概要

Amazon Bedrock Flowsとは?

Amazon Bedrock Flowsは、AWSが提供するGUIベースの生成AIフレームワークです。
これを利用すると、独自のAIエージェントやチャットボットを簡単に構築することができます。
詳細な説明については以下記事で紹介していますので、参考にしていただければと思います。
acro-engineer.hatenablog.com

マルチターンの会話機能とは?

今回新たにサポートされたマルチターンの会話機能は、不足している情報をユーザーに聞き返す、対話形式のフローを可能にするものです。

例えば、旅行プランの提案・予約フローがあった場合、単一ターンの会話と、マルチターンの会話では以下のような違いになります。

単一ターンとマルチターンの会話例の比較

従来の単一ターンの会話では、入力された文章から、必要情報を取り出して処理をする場合、対象文章に必要情報が全て含まれている必要がありました。
もし必要情報が入力に含まれていなかった場合は後続処理に移行してしまいます。
そのため、処理ができなくなるか、AIが推測して必要情報を作り出し意図しない挙動になる場合があります。

一方、マルチターンの会話機能を使うことで、不足情報があれば次の処理へは行かずに、ユーザーに聞き返すようになります。
このように、マルチターンの会話機能を使うことで、より自然な対話フローを簡単に実現できます。

Amazon Bedrock Flowsでマルチターンの会話をするフローを作成

それではAmazon Bedrock Flowsでマルチターンの会話機能を使用する方法を、紹介していきます。
今回は例として、予算・PCの用途・デスクトップかノートPCか、をもとにPCを推薦するフローを作成していきます。

フローの概要

まず今回作成するフローの全体像はこちらです。

PC推薦フロー


ユーザーからの入力はエージェントノードで受け取ります。
プロンプトノードもユーザーからの入力を、文章形式で受け取れますが、マルチターンの会話機能はエージェントのみの機能なためエージェントを用います。
そして、エージェントでは予算・PCの用途・デスクトップかノートPCかの情報を抽出し、後続のAWS Lambdaに渡してPCを推薦します。
Lambdaでの推薦は簡易的に、抽出した情報の組み合わせに対応するPC名を返すようにしています。

マルチターンの会話をするエージェントの作成

まず、ユーザー入力を受け取り、PC推薦に必要な情報を抽出するエージェントを作成します。
今回エージェントに渡した命令は以下です。

ユーザーの入力を分析し、以下の3つのパラメータを抽出してください。  

# 抽出すべき情報
1. 価格帯(price_range)  
   - ユーザーの予算が 15万円未満 の場合、"lower_than_150000yen" を返す  
   - ユーザーの予算が 15万円以上 の場合、"higher_than_150000yen" を返す  

2. 用途(use_case)  
   - ゲーム用のPCが必要な場合、"game" を返す  
   - 仕事、ドキュメント作成、メールなどのビジネス用途の場合、"business" を返す  

3. PCの種類(pc_type)  
   - デスクトップPCが必要な場合、"desktop" を返す  
   - ノートPCが必要な場合、"laptop" を返す 

# 出力フォーマット  
抽出すべき情報を取り出せたら、以下のJSON形式で結果を返してください。

```json
{
  "price_range": "lower_than_150000yen",
  "use_case": "game",
  "pc_type": "laptop"
}
```

次に、マルチターンの会話をするための設定として、「その他の設定」の「ユーザー入力」を「有効」にします。
これによってマルチターンの対話が可能になります。

エージェントのマルチターンの会話機能設定

PC推薦Lambdaの作成

次に、エージェントが作成したJSONを受け取り、推薦するPCを返すLambdaを作成します。
今回は簡易的に、関数の中にPCの一覧を定義しておきます。

# PC推薦マップ
PC_RECOMMENDATION_MAP = {
    "lower_than_150000yen.game.desktop": "ASUS ROG Strix G15",
    "lower_than_150000yen.game.laptop": "Dell G15 Ryzen Edition",
    "lower_than_150000yen.business.desktop": "HP Pavilion Desktop TP01",
    "lower_than_150000yen.business.laptop": "Lenovo ThinkPad E14",
    "higher_than_150000yen.game.desktop": "Alienware Aurora R15",
    "higher_than_150000yen.game.laptop": "Razer Blade 15 Advanced",
    "higher_than_150000yen.business.desktop": "Apple Mac Studio",
    "higher_than_150000yen.business.laptop": "Apple MacBook Pro 16-inch",
}

def lambda_handler(event, context):
    # 入力パラメータの取得
    input_str = event.get("node", {}).get("inputs", [])[0].get("value", "")

    # エージェントが出力した文字列からJSONを抽出しパースする
    match = re.search(r'\{.*?\}', input_str, re.DOTALL)
    json_str = match.group(0) if match else None
    pc_params = json.loads(json_str)

    # PCの推薦
    recommend_key = f"{pc_params['price_range']}.{pc_params['use_case']}.{pc_params['pc_type']}"
    recommended_pc = PC_RECOMMENDATION_MAP.get(recommend_key, "no_recommended_pc")

    if recommended_pc == "no_recommended_pc":
        response = "お勧めできるPCはありませんでした。"
    else:
        response = f"{recommended_pc} がお薦めです。"

    return response

マルチターンの対話を行ってみる

それでは、作成したフローと対話をしてみます。

曖昧な質問に対して、具体的な内容を聞き返すか

ユーザーの曖昧な質問に対して、具体的な内容を聞き返すかを見てみます。

まず、マルチターンの会話機能がOFFの場合の結果と、この時のエージェントが後続に渡した出力は以下です。
ここでは、「お手頃価格のPCが欲しい」と値段を曖昧に伝えてみます。

マルチターンの会話機能がOFFの場合の結果
ありがとうございます。仕事用のお手頃価格のPCをお探しですね。より適切なおすすめができるように、もう少し詳しくお聞きしたいことがあります。

デスクトップPCとノートPCのどちらをお考えでしょうか?それぞれ特徴が異なりますので、ご希望をお聞かせいただけると助かります。

これを見ると、マルチターンの会話機能がOFFでもエージェントは聞き返そうとしているようですが、その内容がユーザーにではなく後続の処理に渡ってしまいエラーしたようです。

一方、マルチターンの会話機能をONにした場合の結果は以下です。

マルチターンの会話機能をONの場合の結果


結果をみると、エージェントは次の処理へは進まず、ユーザーからの曖昧な質問に対して具体的な内容を聞き返していることがわかります。
また、Lambdaで定義した推薦するPCは以下表の通りで、期待通り「Lenovo ThinkPad E14」が推薦されています。

価格帯 用途 PCの種類 推薦PC
15万円未満 ゲーム デスクトップ ASUS ROG Strix G15
15万円未満 ゲーム ノートPC Dell G15 Ryzen Edition
15万円未満 ビジネス デスクトップ HP Pavilion Desktop TP01
15万円未満 ビジネス ノートPC Lenovo ThinkPad E14
15万円以上 ゲーム デスクトップ Alienware Aurora R15
15万円以上 ゲーム ノートPC Razer Blade 15 Advanced
15万円以上 ビジネス デスクトップ Apple Mac Studio
15万円以上 ビジネス ノートPC Apple MacBook Pro 16-inch

1度のやり取りだけではなく、複数回のやり取りを踏まえて回答できるか

では次に、会話の往復が何度も発生した場合でも、それまでの会話全てをもとに回答ができるかを見ていきます。

以下ではPCの情報を小出しにして渡してみました。

往復する会話の回答結果

これをみると情報が3回の会話に分割されてしまっていますが、最初のユーザーメッセージにあった「20万円いかないくらい」という情報も含めて、期待するPCが推薦できていることがわかります。

まとめ

Amazon Bedrock Flowsのマルチターンの会話機能により、ユーザーの入力があいまいな場合や、会話が複数回往復しても適切に必要な情報が抽出できることがわかりました。
自然な対話形式のAIサービスを簡単に作成することができるため、是非お試しください。



Acroquest Technologyでは、キャリア採用を行っています。

  • Azure OpenAI/Amazon Bedrock等を使った生成AIソリューションの開発
  • ディープラーニング等を使った自然言語/画像/音声/動画解析の研究開発
  • マイクロサービス、DevOps、最新のOSSクラウドサービスを利用する開発プロジェクト
  • 書籍・雑誌等の執筆や、社内外での技術の発信・共有によるエンジニアとしての成長

 

少しでも上記に興味を持たれた方は、是非以下のページをご覧ください。

www.wantedly.com



Amazon BedrockのMulti Agent Collaboration で高度なエージェント連携を実現

はじめに

最近OSSのLLMサービスが気になっているデータ分析エンジニアの木介です。
今回は2024年12月に発表された、Amazon Bedrockの「Multi Agent Collaboration」について実際にサンプルコードを動かしながら解説していきます。

aws.amazon.com

Multi Agent Collaborationとは

1. 概要

Multi Agent Collaborationとは、Amazon Bedrockが提供する新機能で、複数のAI Agentが協力してタスクをこなすことができる機能です。
AWS公式ブログにあったソーシャルメディアキャンペーンの例では以下のようなものが紹介されていました。

  1. ユーザーから受けたリクエストを、Supervisor Agentが受け取る。
  2. コンテンツ作成を専門とするAgentに投稿の作成を依頼する。
  3. エンゲージメント予測を専門とするAgentに投稿タイミングを依頼する。
  4. それぞれのAgentから得られた結果を統合したレスポンスをユーザーに返す。

Supervisor Agentが専門的なAgentにそれぞれタスクを割り振ることで、複数のAgentが協力した出力を生成可能となっています。


aws.amazon.com

2. 他Agentサービスとの比較

複数のAgentを扱うサービスにおいてMulti Agent Collaborationと類似するものとして、Multi Agent Orchestratorがあります。
以下の表にて、通常のAgent、Multi Agent CollaborationMulti Agent Orchestratorのそれぞれの特徴と適用範囲についてまとめました。

特徴 通常のAgent Multi Agent Collaboration Multi Agent Orchestrator
目的 1つのAgentがすべての処理を担当し、単純なタスクを完結させる。 複数のAgentが連携してタスクを分担し、複雑なタスクを効率的に処理する。 複数のAgentをまとめて管理し、最適なAgentにタスクを割り振る。
構造 単一のAgentがユーザーリクエストを受け取り、すべての処理を1つで完結させる。 管理Agentがタスクを分割し、それぞれのAgentに割り振り、結果を統合する。 管理役がリクエストを分析し、適切なAgentを選んでタスクを実行させる。
主な機能 1. 単純なタスク処理
2. 一貫した応答
1. 複数のAgentによる効率的な分業
2. 各Agentの専門性を活かした処理
1. リクエストに応じた柔軟なAgent選択
2. 動的なタスク割り振り
メリット 1. 単純で実装が容易
2. 小規模なタスクに適している
1. 複雑なタスクを効率的に処理可能
2. 各Agentの専門能力を最大限活用
1. シンプルな設計で追加や変更がしやすい
2. 幅広いタスクに柔軟に対応可能
デメリット 1. 1つのAgentですべて処理するためスケーラビリティが低い
2. 複雑なタスクには対応困難
1. Agent間の連携が必要で管理が難しい
2. 複雑なシステム設計を要する
1. Agent同士の協力が乏しく、複雑なタスクには向かない
2. 処理の多くを中央管理に依存する


github.com

Multi Agent Collaborationを用いることで通常のAgentやMulti Agent Orchestratorを用いた時と比較して、より複雑なタスクを扱うことが出来そうです。

サンプルシナリオを動かす

さて、では実際にMulti Agent Collaborationを使っていきましょう。
今回は以下のAWSの公式より、株式分析アシスタントを作成していきます。

github.com

1. 株式分析アシスタント概要

以下のような株式分析アシスタントを作成していきます。

サンプルコードを実行することで以下のような4つのAgentを作成していきます。

  1. Stock Analysis (Supervisor Agent)
    1. ユーザーからの株式情報を受け取り、株価データとニュースデータを分析。
    2. それぞれの専門Agentに処理を委任し、最終的なレポートを作成。
  2. Stock Data Researcher (株価データ研究Agent)
    1. 株価の履歴データを取得し、価格変動を分析。
    2. Lambdaを介してリアルタイムの市場データを取得。
  3. News Researcher (ニュース研究Agent)
    1. 指定されたティッカーの関連ニュースを取得し、センチメントやトレンドを分析。
    2. Lambdaを介してWeb検索APIを活用し、最新のニュースを収集。
  4. Financial Analyst (ファイナンシャルアナリスト)
    1. 株価分析とニュース分析の結果を統合し、投資判断を提供。
    2. 最終的なレコメンデーションを作成し、ユーザー向けのレポートを生成。

ユーザーから入力された株式の情報から、2~4のAgentがリクエストされた株式の情報について収集、解析を行い、それらを1のStock Analysis Agentが統合する形となっています。

2. 作成方法

Bedrock AgentでのMulti Agent Collaborationの作成は簡単で以下の設定のみとなっています。

  1. Multi Agent Collaborationで協力させたいAgentを作成する。
  2. Agentを統括するSupervisor Agentを以下の設定で作成する。
    1. Supervisor Agentを作成する際に以下の「Enable multi-agent collaboration」をオンにする。
    2. 以下の形でSupervisor Agentが統括するCollaborator Agentを設定する。

今回は以下のサンプルコードを実行することでboto3からの構築を行っています。

github.com

README通りに構築していきます。

1. 以下のコマンドを実行し、環境を構築する。

git clone https://github.com/awslabs/amazon-bedrock-agent-samples

cd amazon-bedrock-agent-samples

python3 -m venv .venv

source .venv/bin/activate

pip3 install -r src/requirements.txt

2. 以下のコマンドを実行し、サンプルのAgentを作成する。

python3 examples/multi_agent_collaboration/portfolio_assistant_agent/main.py --recreate_agents "true"

3. 以下のAgentがそれぞれ作成されます。


作成されたAgentの概要については以下の形となります。
Supervisor Agentとしてportfolio_assistant Agentを設定し、他のAgentをCollaborator Agentとして登録しています。

Stock Analysis (Supervisor Agent)
以下のコードを実行することでSupervisor Agentを作成しています。

        portfolio_assistant = SupervisorAgent.direct_create(
            "portfolio_assistant",
            role="Portfolio Assistant",
            goal="特定の潜在的な株式投資を分析し、一連の投資考慮事項を含むレポートを提供します",
            collaboration_type="SUPERVISOR",
            instructions="""
                     特定の銘柄の潜在的な株式投資を分析する熟練した専門家として行動します。
                     最近の株価の動向や株式に関する最近のニュースを理解するために調査を行います。
                     潜在的な投資家への配慮を盛り込んだ、よく書かれた、慎重に検討されたレポートを提出します。
                     アナリストの協力者を使って最終分析を行い、ニュースと株式データをアナリストに入力として渡します。
                     協力者を並行ではなく順番に使用します。
                     最終的な出力は全て日本語で回答してください。""",
            collaborator_agents=[
                {
                    "agent": "news_agent",
                    "instructions": """
                     特定の株に関するニュースを見つけるには、このコラボレーターを使用してください。""",
                },
                {
                    "agent": "stock_data_agent",
                    "instructions": """
                     特定の株式の価格履歴を検索するには、このコラボレーターを使用します。""",
                },
                {
                    "agent": "analyst_agent",
                    "instructions": """
                     このコラボレーターを使用して、生の調査を取得し、詳細なレポートと投資に関する考慮事項を作成します。""",
                },
            ],
            collaborator_objects=[news_agent, stock_data_agent, analyst_agent],
            guardrail=no_bitcoin_guardrail,
            llm="us.anthropic.claude-3-5-sonnet-20241022-v2:0",
            verbose=False,
        )

github.com

設定している項目としては以下のものになります。

  1. goal: Agent全体として期待している出力を設定する。
  2. instructions:Supervisor Agentへの指示を定義し、どのようにCollaborator Agentと協力していくかを設定する。
  3. collaborator_agents:各Collaborator Agentの設定を定義し、Supervisor AgentがCollaborator Agentを扱えるようにする。

AWSコンソール上でもportfolio_assistant AgentをSupervisor Agentとして設定し、ほかの3つのAgentをCollaborator Agentとして登録できていることがわかります。

また、プロンプトで以下の指示を与えて、他のCollaborator Agentと協力して渡された株式についての分析を行うように指示しています。
(本来は英語表記ですが、回答を日本語で行うためにプロンプトの修正を行っています。)
今回は順次実行を行うようにプロンプトで指定していますが、Collaborator Agentの並列での実行も可能となっています。

特定の銘柄の潜在的株式投資を分析する熟練した専門家として行動します。
最近の株価の動向や株式に関する最近のニュースを理解するために調査を行います。
潜在的な投資家への配慮を盛り込んだ、よく書かれた、慎重に検討されたレポートを提出します。
アナリストの協力者を使って最終分析を行い、ニュースと株式データをアナリストに入力として渡します。
協力者を並行ではなく順番に使用します。
最終的な出力は全て日本語で回答してください。

stock_data_agent (株価データ研究Agent)
以下の図のように、株式についての価格情報を取得するLambdaをアクショングループとして設定しています。
このアクショングループにより株式の価格情報を取得することが、このAgentの役割になります。

以下のコードを実行することで定義を行っています。
tool_defsで定義したパラメータをSupervisor Agentから渡されるプロンプトから抽出することで、株価情報を取得するように設定しています。

        # Define Stock Data Agent
        stock_data_agent = Agent.direct_create(
            name="stock_data_agent",
            role="財務データコレクター",
            goal="特定のティッカーの正確な株価傾向を取得します。",
            instructions="リアルタイムの財務データ抽出のスペシャリスト",
            tool_code=f"arn:aws:lambda:{region}:{account_id}:function:stock_data_lookup",
            tool_defs=[
                {  # lambda_layers: yfinance_layer.zip, numpy_layer.zip
                    "name": "stock_data_lookup",
                    "description": "指定された株価ティッカーの 1 か月の株価履歴を JSON 形式で取得します",
                    "parameters": {
                        "ticker": {
                            "description": "The ticker to retrieve price history for",
                            "type": "string",
                            "required": True,
                        }
                    },
                }
            ],
        )

github.com

news_agent (ニュース研究Agent)
以下の図のように、株式についてのニュースを検索し、取得するLambdaをアクショングループとして設定しています。
このアクショングループにより、株式関連のニュースを取得することが、このAgentの役割になります。

analyst_agent (ファイナンシャルアナリスト)
以下のプロンプトを与えて、入力された株式の価格情報とニュースから分析をおこなうように設定されています。

役割: 財務アナリスト、
目標: 株式の傾向と市場ニュースを分析して洞察を得る。
手順: 経験豊富なアナリストが戦略的な推奨事項を提供します。ニュース概要と株価概要を入力として受け取ります。
利用可能なツールがありません。自分自身の知識のみに頼ってください。

3. サンプルシナリオを動かしてみる

構築されたAgentを早速動かしていきましょう。
今回はAWS コンソール上から動かしていきます。

Supervisor Agentとして作成されたportfolio_assistant Agentから以下のリクエストでAmazonの株について聞いてみます。

リクエス

ticker:Amazon

回答

今回の場合ではおおよそ30秒程度で回答が返ってきました。
こちらの実行時間は実行するAgent依存になりますが、検索など時間のかかる処理を複数行うとそれだけ時間がかかる形となります。

また、以下の形でどのようにCollaborator Agentが処理を行っているかを確認することができます。
news_agent、stock_data_agent、analyst_agentがそれぞれ協力して質問に回答できていることがわかります。

Supervisor AgentからどのようにCollaborator Agentへ指令が行われているかを確認してみましょう。
以下のようにトレースステップから各Agentの挙動を確認することができます。

以下の形でCollaborator AgentがSupervisorAgentから渡されるプロンプトからパラメータを抽出できていることがわかります。

SupervisorAgentからCollaborator Agent(stock_data_agent)へのプロンプト

[{text=Please provide the price history for the stock with the ticker 'Amazon'.}]

Collaborator Agentが実行するアクショングループの入力

"invocationInput": [
{
"actionGroupInvocationInput": {
"actionGroupName": "actions_stock_data_agent",
"executionType": "LAMBDA",
"function": "stock_data_lookup",
"parameters": [
{
"name": "ticker",
"type": "string",
"value": "Amazon"
}
]
},
"invocationType": "ACTION_GROUP",
"traceId": "1f373c5c-12b9-4d07-8a24-1317d66f5115-0"
}
]

Supervisor Agentが与えられたリクエストに対して、Collaborator Agentにそれぞれタスクを割り振るといった流れを簡単に構築することができました。
こういった複数の専門的なAgentに分割してタスクを解決する際にMulti Agent Collaborationの力を発揮することが出来そうです。

まとめ

今回は2024年12月に発表されたAmazon BedrockのMulti Agent Collaborationについて解説を行いました。
特にBedrock Agentのみで専門的なAgentとそれを統括するSupervisor Agentまで構築できることが魅力的だと感じました。
様々なユースケースでの利用が考えられますので、今後もいろいろと試していきたいと思います。

Acroquest Technologyでは、キャリア採用を行っています。

  • Azure OpenAI/Amazon Bedrock等を使った生成AIソリューションの開発
  • ディープラーニング等を使った自然言語/画像/音声/動画解析の研究開発
  • マイクロサービス、DevOps、最新のOSSクラウドサービスを利用する開発プロジェクト
  • 書籍・雑誌等の執筆や、社内外での技術の発信・共有によるエンジニアとしての成長

少しでも上記に興味を持たれた方は、是非以下のページをご覧ください。

www.wantedly.com

Amazon Nova モデルと Bedrock Knowledge Base で動画検索を実現する

こんにちは、機械学習チーム YAMALEX の駿です。
YAMALEX は Acroquest 社内で発足した、会社の未来の技術を創る、機械学習がメインテーマのデータサイエンスチームです。
(詳細はリンク先をご覧ください。)

突然ですが、動画を検索したくなることってありますよね。

それも、動画のタイトルにはなっていないんだけど、こんなことを話してたんだよな、というざっくりとした記憶しかなく、どうしても見つけることができないこと、あると思います。

あるいは Google 検索などができれば良いのですが、自社内にある動画や、仕事で使う動画となると、そう簡単にはいきません。

そんな困りごとを解決するためのツールを Amazon Nova モデルと Amazon Bedrock Knowledge Base で作成してみました。

Bedrock Knowledge Base を使うことで、ドキュメントの取り込み・検索部分を自前で開発する必要がなく、動画を要約した結果をS3に置くだけで簡単に連携できます。

すなわち、開発コストを最小限に抑えつつ高性能なアプリを開発できます。

構成図

1. 動画検索で実現したいこと

動画のタイトルにその文言が含まれれば、タイトルで検索をかけることができますが、動画の一部で話していただけ、などの内容はなかなか文字列で検索することが困難です。

あるいは、どれか特定の動画に興味があるのではなく、同じ話題の動画を広く検索したい、ということもあるかもしれません。

今回の動画検索ではそのようなあいまいな検索を行うために、 Bedrock Knowledge Base でベクトル検索を行うことを考えます。

2. 実現方法

大まかな手順は以下の通りです。

  1. 動画をAmazon Nova モデルで要約できるようにする
  2. 要約結果を Bedrock Knowledge Base に取り込めるようにする
  3. 動画の検索処理を実現する
  4. 検索画面を作成する

それぞれ、もう少し詳しく説明します。

2.1. 動画をAmazon Nova モデルで要約できるようにする


モデルで要約する

Amazon Nova モデルはテキスト、画像に加えて、動画を入力に入れられます。

下記プロンプトとともに動画とそれを文字起こししたものを入力し、要約文を作成してもらいました。

今回は Acroquest の YouTube にアップロードされている動画の内、再生時間が1分以下のものを選択し、 S3 にアップロードしました。

S3 にアップロードするとイベントを受け取った Lambda がこれらの動画の説明文を Amazon Nova Lite に生成させ、 テキストファイルを S3 にアップロードします。

このテキストファイルが後ほど Knowledge Base で取り込む対象になります。

system_prompt = [
    {
        "text": dedent("""\
            あなたのタスクは、与えられた動画を解析し、何が映っているかを説明することです。
            動画を文字起こしした結果を「文字起こし:xxx」の部分に記載しています。説明の材料としてつかってください。
            あなたの返答は、厳密に、動画の説明文のみで構成されている必要があります。
            できるだけ詳細に説明してください。
            要約は日本語で返してください。
        """)
    }
]
use_messages = [
    {
        "role": "user",
        "content": [
            {"text": f"動画名: {filename}"},
            {
                "video": {
                    "format": "mp4",
                    "source": {"bytes": b64_content},
                },
            },
            {"text": f"文字起こし: {transcript}"},
            {"text": "この動画を日本語で説明してください。"},
        ],
    }
]
config = {"temperature": 0}
body = {
    "schemaVersion": "messages-v1",
    "system": system_prompt,
    "messages": use_messages,
    "inferenceConfig": config,
}

response = bedrock_agent.invoke_model(
    modelId=MODEL_ID,
    body=json.dumps(body),
    contentType="application/json",
)
model_response = json.loads(response["body"].read())
content = model_response["output"]["message"]["content"][0]["text"]

下のようなレスポンスが返ってきます。

動画は、リモートワーク中のITエンジニアが食べたいアンパンのランキングを紹介しています。

まず、第4位は「アンパン九十五円」で、オーソドックスなしっとり生地とこしあんが滑らかな食感で甘すぎないのが特徴です。
第3位は「たっぷりホイップワンパン百十七円」で、クリームがぎっしり詰まっていて食べた時の満足感が高いのが特徴です。
第2位は「高級通販百十四円」で、消しの実がまぶされており、お饅頭のような上品な味わいと厚みがあります。
第1位は「薄皮つ板百三十七円」で、食べやすい二口サイズで、あんこの密度が高く、手も汚れません。

リモートワーク中に食べたいアンパンのランキングを紹介し、気になるものがあったら購入を勧めています。
動画の冒頭では、キーボードの画像が映し出され、その下に「アクロクエストテクノロジー」のロゴが並んでいます。
その後、キーボードのアップショットが映し出され、キーボードの文字が日本語で表示されています。
次に、キーボードの横に、クリーニング用のスプレー缶、綿棒、そして白い布が置かれています。
そして、キーボードの上に白い布を置き、その布でキーボードを拭く動作が繰り返されます。
最後に、キーボードが映し出され、その下に「アクロクエストテクノロジー」のロゴが並んでいます。

なお、今回は短い動画のみを対象にしているため、APIに直接動画を投げる方式を取っていますが、 ファイルサイズが大きい場合は、S3にアップロードしたものを読み込ませる、細切れに区切ったものを渡し、後からマージする などの手段が必要になります。

2.2. 要約結果を Bedrock Knowledge Base に取り込めるようにする


Knowledge Base で取り込む

次に Knowledge Base で同期を行い上記説明文を取り込むのですが、そのまま取り込むだけだと、元の動画とアップロードしたテキストファイルの紐づけができません。

これを解決するために Knowledge Base の機能である、 metadata.json を使用します。

この JSON ファイルに記載したものは Knowledge Base の同期時に OpenSearch Serverless にメタデータとして保存され、検索時に取得することができます。

今回は下記のように、メタデータとして、動画のURLを指定しました。 検索後の画面表示に使用します。

{
  "metadataAttributes": {
    "original_path": "s3://bucket/path/to/video.mp4"
  }
}

動画の説明文と対応する metadata.json の配置が完了したら、 Knowledge Base を同期します。

2.3. 動画の検索処理を実現する


検索する

検索時は Knowledge Base の Retrive API を使用しました。

また、検索後にスコアの下限で閾値を設けることで、リランクをした結果関連度が低いと判断された動画が検索結果に含まれないようにしました。

const input = {
  knowledgeBaseId,
  retrievalQuery: {
    text: query.trim(),
  }
  retrievalConfiguration: {
    vectorSearchConfiguration: {
      numberOfResults: 20,
      overrideSearchType: "HYBRID",
      rerankingConfiguration: {
        bedrockRerankingConfiguration: {
          modelConfiguration: {
            modelArn: AMAZON_RERANK_MODEL,
          },
          numberOfRerankedResults: 10,
        },
        type: "BEDROCK_RERANKING_MODEL",
      },
    },
  },
};
const command = new RetrieveCommand(input);
return client.send(command);

下記のように動画の概要と動画ファイルのパスを取得できました。

{
  "retrievalResults": [{
    "content": {
      "text": "動画は、リモートワーク中のITエンジニアが食べたいアンパンのランキングを紹介しています……",
      "type": "TEXT"
    },
    "location": {
      "s3Location": {
        "uri": "s3://bucket/path/to/summary.txt"
      },
      "type": "S3"
    },
    "metadata": {
      "original_path": "s3://bucket/path/to/video.mp4"
    }
  }]
}

2.4. 検索画面を作成する

今回は上記検索を行う画面を bolt.new を使って作成しました。

私はあまりフロントが得意ではないのですが、日本語で指示するだけで、とてもいいアプリができたと思います。

作成した検索アプリ

検索結果

3. 結果

3.1. 動画に含まれる文言で検索してみる

まずは動画のタイトルにも、生成AIによる要約文にも含まれるキーワードで検索してみました。

期待する動画が第一の検索結果に表示されることを確認できます。

「あの動画をまた見たいのに、従来の検索ではヒットしない」といったときにこのアプリを使えば、見たい動画にすぐにアクセスできます。

「美味しいあんパン」

3.2. 動画には含まれない文言で検索してみる

次にタイトル、要約文には含まれないが、ベクトル検索で拾えそうな内容で検索してみました。

こちらも、2018年にサンフランシスコで行われた Elastic{ON} というカンファレンスに参加した際の様子を映した動画がヒットしています。

今回は Elastic{ON} の動画しか入っていないのですが、他の海外カンファレンスの動画を取り込んでおけば、「特定の動画が見たいわけではないが、似たような動画を広く取ってきたい」を実現できます。

「海外カンファレンスの様子」

まとめ

現在、Bedrock Knowledge Base に直接動画を入力することはできませんが、 Amazon Nova を使って説明文を生成することで、間接的に動画検索を実現することができました。

動画をエンベディングすることで同じ機能を自前で実装することは可能ですが、 Bedrock Knowledge Base を使う利点は取り込みと検索を簡単に組み込めることだと思います。

取り込みはファイルをS3に置いて「同期」を行うのみ、検索も Retrieve API を呼ぶだけです。 Retrieve APIを使えばリランクで検索結果を改善することも簡単に行えます。

Acroquest Technologyでは、キャリア採用を行っています。
  • Azure OpenAI/Amazon Bedrock等を使った生成AIソリューションの開発
  • ディープラーニング等を使った自然言語/画像/音声/動画解析の研究開発
  • マイクロサービス、DevOps、最新のOSSクラウドサービスを利用する開発プロジェクト
  • 書籍・雑誌等の執筆や、社内外での技術の発信・共有によるエンジニアとしての成長
少しでも上記に興味を持たれた方は、是非以下のページをご覧ください。 www.wantedly.com

Azure Content Understandingを使って動画から構造化データを作る

こんにちは。データサイエンスチームYAMALEXの@Ssk1029Takashiです。
最近はLLMのFine Tuningに夢中の日々です。

昨年のMicrosoft Ignite 2024にて、Azure Content Understandingというサービスが発表されました。
このサービスはOfficeドキュメントや画像、音声、動画などのファイルをもとに、RAGシステムなどで使いやすいように生成AIなどを活用してユーザーが定義した構造にデータを取り込むサービスです。
今までだと、音声はSpeech Service、画像はAI Visionなどのサービスをそれぞれ使ってデータ処理する必要があったのを、一つのサービスで構造を定義するだけで処理できるようになりました。
今回はそんなAzure Content Understandingを使って動画から検索エンジンなどに取り込みやすいJSON形式の構造化データを作成します。

Azure Content Understandingとは

記事冒頭でも説明した通り、Azure Content Understandingでは一つのサービスで複数形式のファイルから構造化データを作成できます。
構造化データを作成するにあたって、以下の図のようにファイル内の文字抽出やトランスクリプトの作成などのコンテンツ抽出が可能です。
それに加えて、生成AIを通してユーザーが定義した項目を抽出したコンテンツから生成することでファイルを構造化したデータに変換までできます。

https://learn.microsoft.com/ja-jp/azure/ai-services/content-understanding/overview

この機能を使うことで、例えばドキュメントを検索データにするときだと、普通に実装するとOCRなどで文字列抽出→LLMを使ってデータ抽出→JSONに変換のようなパイプラインが必要になるところを、定義を作成するだけで実現可能になります。
そのうえで複数のファイルを一つのサービスで対応できるようになるので便利ですね。
現在はプレビューのため、一時的に無料で使用可能になっており、価格は後日詳細が発表予定とのことです。

Azure Content Understandingの利用準備

Azure Content Understandingを使用するには、West US, Sweden Central, Australia EastのいずれかのリージョンにAzure AI Servicesリソースを作成する必要があります。
上記以外のリージョンでは利用できないため注意してください。

Azure Content Understandingを利用して動画ファイルを解釈する

それでは実際に動画ファイルから、Azure Content Understandingを使ってサマリ、カテゴリを抽出して見ましょう。
今回は以下のリンクで提供されているサンプルを利用することでPythonから実行してみます。
github.com
実施するステップとしては以下になります。

  1. データ分析定義を作成する
  2. アナライザーを作成する
  3. アナライザーを使ってファイルを分析する

データ分析定義を作成する

まず、対象のファイル形式やどのような抽出したいデータの内容を定義します。

以下のようなJSONを作成します。

{
    "description": "ビデオからファイルを抽出するサンプル",
    "scenario": "videoShot", 
    "config": {
        "returnDetails": true,
        "locales": [
            "ja-JP"
        ],
        "enableFace": false
    },
    "fieldSchema": {
        "fields": {
            "summary": {
                "type": "string",
                "method": "generate",
                "description": "内容を要約した一文"
            },
            "category": {
                "type": "string",
                "method": "classify",
                "description": "動画ファイルのカテゴリ",
                "enum": [
                    "IT",
                    "Economy",
                    "Nature"
                ]
            }
        }
    }
}

この定義をのポイントは以下になります

  1. 動画を対象にするためscenarioにはvideoShotを指定
  2. localesでは日本語のため、ja-JPのみを設定
  3. fieldSchemaでは以下の2つの抽出する項目を定義
    1. Summary: 動画の内容を要約した文章
    2. Category: IT, Economy, Natureから選択する動画のカテゴリ

fieldSchemaで設定する内容では、特にdescriptionの内容を見て生成AIがフィールドの内容を抽出するので、重要なチューニング項目になります。
ただし、今回は簡単な動作検証のため簡易的な一文で検証しています。

アナライザーを作成する

定義を作成したら、ファイルを読み込んで分析するためのアナライザーを作成します。
今のところアナライザーの作成・実行はGUIREST APIからのみ可能になっています。
ただし、上述したGithubの以下のソースコードAPIを使いやすくしたクラスが提供されているので、今回はそれを利用します。
github.com

アナライザーの作成は以下のコードで可能です。

import uuid

# アナライザーの名前を作成
ANALYZER_TEMPLATE = "video"
ANALYZER_ID = "video-sample-" + str(uuid.uuid4())

# ContentUnderstanding用のクライアントを作成
client = AzureContentUnderstandingClient(
    endpoint=AZURE_AI_ENDPOINT,
    api_version=AZURE_AI_API_VERSION,
    subscription_key=AZURE_AI_SUBSCRIPTION_KEY
)
# データ分析定義を読み込んでアナライザーを作成する
response = client.begin_create_analyzer(ANALYZER_ID, analyzer_template_path="./content_video.json")
result = client.poll_result(response)

ここまで実行することでアナライザーを作成できたので、あとは分析を実行するだけになります。

アナライザーを実行する

それではアナライザーに動画ファイルを読み込ませて実行してみましょう。
対象のファイルとしては、当社が公開している以下の動画を対象にします。
www.youtube.com
この動画ではサブネットマスクの知識について基本を解説しているため、その内容が結果として抽出できることが期待結果になります。
実行自体は簡単で以下の2行で完了します。

response = client.begin_analyze(ANALYZER_ID, file_location=analyzer_sample_file_path)
result = client.poll_result(response)

分析結果を得られるまでの時間はファイルによって変化しますが、今回の11分の動画を対象にした場合は5分程度かかりました。

結果としては以下のようなJSONが得られます。
全体だと長すぎるため一部抜粋して記載します。

{
  "id": "82eb4975-4232-44a1-a4e6-d018fa45469f",
  "status": "Succeeded",
  "result": {
    "analyzerId": "video-sample-31341919-bf0d-45af-9228-7866b252a70a",
    "apiVersion": "2024-12-01-preview",
    "createdAt": "2025-01-28T05:32:48Z",
    "warnings": [],
    "contents": [
      {
        "markdown": "# Shot 00:00.000 => 00:03.303\n## Transcript\n```\nWEBVTT\n\n```\n## Key Frames\n- 00:00.825 ![](keyFrame.825.jpg)\n- 00:01.650 ![](keyFrame.1650.jpg)\n- 00:02.475 ![](keyFrame.2475.jpg)",
        "fields": {
          "category": {
            "type": "string",
            "valueString": "IT"
          },
          "summary": {
            "type": "string",
            "valueString": "新入若手エンジニア向けのIT基礎知識について説明する動画で、今回はサブネットマスクとCIDRについて解説する。"
          }
        },
        "kind": "audioVisual",
        "startTimeMs": 0,
        "endTimeMs": 3303,
        "width": 1280,
        "height": 720,
        "KeyFrameTimesMs": [
          825,
          1650,
          2475
        ],
        "transcriptPhrases": []
      },
      {
        "markdown": "# Shot 00:03.303 => 01:02.106\n## Transcript\n```\nWEBVTT\n\n00:04.680 --> 00:28.960\n<v Speaker>では次にサブネットマスクについて説明していきます。先ほどローカルネットワークを設定して、その中でIPアドレスを設定したり、利用したりするという話をしました。で、具体的にどうローカルネットワークを定義するかなんですけれども、ここでアイピーアドレスをネットワーク部とホスト部二つに分けて考えるというやり方をとります。\n00:29.480 --> 00:59.000\n<v Speaker>具体的にどうするかというとアイピーアドレスが一つ回った時に、これらをええ、それぞれの数字をビットで表します。つまり、二進数で表記するんですね。そうするとこのような形になりますで、この時にこのある場所を境目にして、この前半部分をネットワーク部、そして後半をホスト部という風に考えるんですね。この前半部分のネットワーク部が、いわばそのネットワーク自身を表す場所。\n00:59.600 --> 01:27.680\n<v Speaker>となります。そしてその後ろのホスト部がこのネットワークに所属するそれぞれの機器の場所を表すという形になります。つまり、その前半部分のネットワーク部が一致していれば、同じネットワークだというふうにみなすわけですね。このネットワークの定義の仕方としては、クラスという考え方が今、ええ、昔から使われています。クラスは3種類あってですね。クラスABC。\n```\n## Key Frames\n- 00:06.072 ![](keyFrame.6072.jpg)\n- 00:08.844 ![](keyFrame.8844.jpg)\n- 00:11.616 ![](keyFrame.11616.jpg)\n- 00:14.388 ![](keyFrame.14388.jpg)\n- 00:17.160 ![](keyFrame.17160.jpg)\n- 00:19.932 ![](keyFrame.19932.jpg)\n- 00:22.704 ![](keyFrame.22704.jpg)\n- 00:25.476 ![](keyFrame.25476.jpg)\n- 00:28.248 ![](keyFrame.28248.jpg)\n- 00:31.020 ![](keyFrame.31020.jpg)\n- 00:33.792 ![](keyFrame.33792.jpg)\n- 00:36.564 ![](keyFrame.36564.jpg)\n- 00:39.336 ![](keyFrame.39336.jpg)\n- 00:42.108 ![](keyFrame.42108.jpg)\n- 00:44.880 ![](keyFrame.44880.jpg)\n- 00:47.652 ![](keyFrame.47652.jpg)\n- 00:50.424 ![](keyFrame.50424.jpg)\n- 00:53.196 ![](keyFrame.53196.jpg)\n- 00:55.968 ![](keyFrame.55968.jpg)\n- 00:58.740 ![](keyFrame.58740.jpg)",
        "fields": {
          "summary": {
            "type": "string",
            "valueString": "サブネットマスクの説明が行われ、IPアドレスをネットワーク部とホスト部に分ける方法が示されています。クラスABCのネットワーク定義についても触れられています。"
          },
          "category": {
            "type": "string",
            "valueString": "IT"
          }
        },
        "kind": "audioVisual",
        "startTimeMs": 3303,
        "endTimeMs": 62106,
        "width": 1280,
        "height": 720,
        "KeyFrameTimesMs": [
          6072,
          8844,
          11616,
          14388,
          17160,
          19932,
          22704,
          25476,
          28248,
          31020,
          33792,
          36564,
          39336,
          42108,
          44880,
          47652,
          50424,
          53196,
          55968,
          58740
        ],
        "transcriptPhrases": [
          {
            "speaker": "speaker",
            "startTimeMs": 4680,
            "endTimeMs": 28960,
            "text": "では次にサブネットマスクについて説明していきます。先ほどローカルネットワークを設定して、その中でIPアドレスを設定したり、利用したりするという話をしました。で、具体的にどうローカルネットワークを定義するかなんですけれども、ここでアイピーアドレスをネットワーク部とホスト部二つに分けて考えるというやり方をとります。",
            "confidence": 1,
            "words": [],
            "locale": "en-US"
          },
          {
            "speaker": "speaker",
            "startTimeMs": 29480,
            "endTimeMs": 59000,
            "text": "具体的にどうするかというとアイピーアドレスが一つ回った時に、これらをええ、それぞれの数字をビットで表します。つまり、二進数で表記するんですね。そうするとこのような形になりますで、この時にこのある場所を境目にして、この前半部分をネットワーク部、そして後半をホスト部という風に考えるんですね。この前半部分のネットワーク部が、いわばそのネットワーク自身を表す場所。",
            "confidence": 1,
            "words": [],
            "locale": "en-US"
          },
          {
            "speaker": "speaker",
            "startTimeMs": 59600,
            "endTimeMs": 87680,
            "text": "となります。そしてその後ろのホスト部がこのネットワークに所属するそれぞれの機器の場所を表すという形になります。つまり、その前半部分のネットワーク部が一致していれば、同じネットワークだというふうにみなすわけですね。このネットワークの定義の仕方としては、クラスという考え方が今、ええ、昔から使われています。クラスは3種類あってですね。クラスABC。",
            "confidence": 1,
            "words": [],
            "locale": "en-US"
          }
        ]
      },

おそらく内部的にはVideo Indexerのようなものを使っていると思われますが、ビデオを自動的にチャプター分割して、各チャプターのトランスクリプト、キーフレームの時間が取得できています。
また、カスタムで定義したsummary, categoryについてもチャプターごとに取得しており、内容としても正しいものが取れています。
簡単に定義してアナライザーを実行するだけで動画をここまで詳細に構造化できるのは便利ですね。
トランスクリプトもついているため、検索データとしても有効そうです。

まとめ

今回はAzure Content Understandingを利用して、動画データを構造化して見ました。
全体を通してかなり手間なく動画データの解析ができ、便利なサービスでした。
まだプレビュー段階ですが、RAGデータへの活用などかなり幅広い活用が出来そうなサービスのため、さらに使い方を模索していきたいところです。
それではまた。

Acroquest Technologyでは、キャリア採用を行っています。

  • Azure OpenAI/Amazon Bedrock等を使った生成AIソリューションの開発
  • ディープラーニング等を使った自然言語/画像/音声/動画解析の研究開発
  • マイクロサービス、DevOps、最新のOSSクラウドサービスを利用する開発プロジェクト
  • 書籍・雑誌等の執筆や、社内外での技術の発信・共有によるエンジニアとしての成長

少しでも上記に興味を持たれた方は、是非以下のページをご覧ください。

www.wantedly.com