Zenn
🔎

Azure AI Search / Document IntelligenceでExcel / フローチャートをRAGする方法

2024/12/26に公開

初めまして、Givery AI Lab所属の楊です。
今回はAzureでRAGシステムを構築する際に、Azure AI Searchを利用して外部情報(特にExcelファイルとフローチャート)を検索エンジンに取り込む方法について調査し、検証しました。その結果について、共有させていただきます。

RAGとは

RAGは、Retrieval-Augmented Generationの略称で、大規模言語モデル(LLM)によるテキスト生成に、外部の情報の検索を組み合わせることで、モデルの回答精度を向上させる技術です。「検索拡張生成」と訳されます。

このフローチャートでは一般的なRAGシステムを示しています。特に、「検索エンジン」は、事前に取り込まれた外部情報からユーザーのプロンプトに最も関連するコンテンツを検索し、「LLM/LLMに基づくアプリ」に提供するタスクを担当します。したがって、RAGシステムにおいて重要な役割を果たします。

Azure AI Search

Azure AI Searchとは

Azure AI Searchは、ユーザーが取り込んだ異種コンテンツに対応するための検索エンジンサービスを提供します。Azure上でRAGシステムをよりスムーズに構築するためにハイブリッド検索や応用AI(Applied AI)などさまざまな追加機能を備えています。
要するに、Azure AI SearchはRAGシステムにおいて、「検索エンジン」の部分を担当します。
https://learn.microsoft.com/ja-jp/azure/search/search-what-is-azure-search

調査の対象

今回は、主にAzure AI Searchを利用して「検索エンジン」を構築する際に、Excelファイルフローチャートの外部情報をどのように検索エンジンのインデックスにインポートして活用するかという方法を調べました。

まず、対象の外部情報となるExcelファイルフローチャートを簡単にご説明いたします。

Excelファイル

Excelファイルとは、一般的にXLSX形式のファイルを指します。活用の可能性をさらに調べるために、今回の調査・検証では、XLSXファイルに加えてファイル内容のスクリーンショット(例えば.pngなどの画像ファイル)も調査範囲に含めました。

フローチャート

フローチャートとは、一般的には画像ファイルの形式(例えば.pngなど)で保存されるため、それを調査範囲に含めました。


Excelファイルとフローチャートは、どちらもビジネスでよく使用されるデータ形式で、検索エンジンのインデックスに直接取り込めばRAGシステムの構築に役立つので調査を行いました。特に両者はともに独特の構造を持っており、RAGに適用する際にはテキスト内容に加えてその構造にも注意を払う必要があるため、活用が難しくなります。

調査結果の共有

公式ドキュメントによれば、Azure AI Searchではインデックスに外部情報をインポートするために、2つの基本的な手法(プログラムでプッシュするワークフローと検査インデクサーを使用してデータをプルするワークフロー)が提供されています。

  • プッシュするワークフローはプログラムでデータをインデックスにインポートする手法です。

データソースは任意ですが、インポート形式はJSONドキュメントであるため、ExcelファイルフローチャートをJSON形式に転換する必要があります。

XLSXファイルの場合、一般的にはCSVファイルに変換してから、プログラムでそのテキストを分割してJSONドキュメントに転換しますが、実際の業務で検証したところ、このような転換が元のデータ構造(テーブルの結合セル)を損ない、RAGの精度に悪影響を与えることがわかりました。

なお、画像ファイルの場合、プログラムでJSONドキュメントに転換することが難しいです。


  • プルするワークフローは"検索Indexer"を使用して、Azureにサポートされているデータソースに接続し、データをインデックスに自動的にインポートする手法です。

データソースはAzureにサポートされているデータソースのみですが、異なるデータ形式(通常はテーブル形式)を自動的にJSONドキュメントに転換する機能を備えているため、多くのファイル形式やデータ構造に直接対応しています。例えば、CSVPDFHTMLtableviewなどが対応されています。完全なリストはAzure AI Searchの公式ドキュメントを参照してください。

XLSXファイルに対応していると公式ドキュメントに記述されていますが、試したところ、ここでの自動転換もプッシュと同様に結合セルなどの構造情報を保つのが難しいようです(詳細な検証は今後実施予定です)。

また、画像ファイルには対応していません。

https://learn.microsoft.com/ja-jp/azure/search/search-indexer-overview

https://learn.microsoft.com/ja-jp/azure/search/search-data-sources-gallery


これらの基本的な手法は生のテキストファイルを元に処理を行うため、対応できるファイル形式が限られており、構造を持つドキュメントにそのまま適用すると、構造情報が失われるという問題点があります。

そのため、Azureでは構造を持つ外部情報に対応するAIサービスを提供します。これらのサービスを利用することで、Azure AI Searchと連携し、より多くのファイル形式やデータ構造に対応する検索エンジンを構築することができます。特に、Azure AI Document Intelligenceサービスではドキュメント処理に特化したツールを提供しています。Excelファイルフローチャートにも適用可能であるため、ブログ後半ではAzure AI Document Intelligenceの検証を行いました。

Azure AI Document Intelligence (AADI)

Azure AI Document Intelligenceとは

Azure AI Document Intelligence (AADI)は、Azure AI Serviceの一部として、自動的なドキュメント処理ソリューションを提供します。このサービスには、さまざまなドキュメント処理に特化した事前訓練済みのモデルが用意されており。これらのモデルを使用して、ドキュメントの構造を考慮した情報抽出が可能です。抽出された情報を基に、Azure AI Searchを利用して、より高精度な検索エンジンを構築することができます。

https://learn.microsoft.com/ja-jp/azure/ai-services/document-intelligence/?view=doc-intel-4.0.0

Layout model

今回の対象であるExcelファイルフローチャートを考慮すると、最適なのはAADIのLayout modelです。Layout modelは異なる形式のドキュメントを受け取り、構造を持つデータ表現を返します。
このモデルでは、入力としてPDFJPEG/JPGPNGBMPTIFFHEIFDOCXXLSXPPTX形式のドキュメントを受け入れ、出力としてテキスト、選択マーク、テーブル、ドキュメント構造などの情報を抽出します。
RAGの検索エンジンに使用すると考える上で、出力の中で最も注目すべきは、抽出されたテキスト(Excelファイルおよびフローチャート)とテーブル(Excelファイル)です。それらの有効性を確認するために、Excelファイルおよびフローチャートの各例において、Layout modelによる抽出を検証しました。

調査と検証結果の共有

Excelファイル(XLSXファイル)

公式ドキュメントによる説明

Layout modelの公式ドキュメントによれば、XLSXファイルがサポートされていることがわかりましたが、ただし、テーブルの抽出はサポートされていません。Excelファイルにおいて、テーブルが最も重要であるため、残念に思いますが、抽出されたテキストに注目することにしました。

検証結果

  • 入力 (ここで表示しているのはスクリーンショットですが、実際に使用したのはXLSXファイルです)

  • 出力(抽出されたテキスト)

出力
# Sheet1

カテゴリ

製品名

販売数

単価(USD)

地域

販売員

販売日時

売上金額

エレクトロニクス

TV

50

300

東京

佐藤太郎

01/10/2024

スマートフォン

100

800

大阪

鈴木花子

01/15/2024

家電

冷蔵庫

30

1200

名古屋

田中一郎

02/01/2024

洗濯機

40

500

福岡

高橋次郎

02/05/2024

家具

ソファ

20

1000

京都

中村明子

03/01/2024

ベッド

15

1200

札幌

松本幸子

03/03/2024

合計
  • 出力 (抽出されたテーブル)
    試してみましたが、予想通り、抽出はできませんでした。
  • 結論
    XLSX形式のファイルだと、Layout modelでは対応が難しいように思われます。テーブルの抽出は全くうまくいきませんでした。一方で、テキストの抽出においては、テーブルの主な内容がテキスト形式で抽出されましたが、計算式(e.g. =SUM()など)が抽出されなかったし、テーブルの構造も若干破壊されました。

Excelファイル(画像ファイル)

公式ドキュメントによる説明

公式ドキュメントによると、入力として画像ファイルがサポートされていますので、実際のパフォーマンスを確認します。特にXLSX形式では抽出できないテーブルの抽出に注目しています。

検証結果

- 入力 (.png形式のスクリーンショット)

  • 出力 (抽出されたテキスト)
出力
<table>
<tr>
<th>カテゴリ</th>
<th>製品名</th>
<th>販売数</th>
<th>単価(USD)</th>
<th>地域</th>
<th>販売員</th>
<th>販売日時</th>
<th>売上金額</th>
</tr>
<tr>
<td rowspan="2">エレクト ロニクス</td>
<td>TV</td>
<td>50</td>
<td>300</td>
<td>東京</td>
<td>佐藤太郎</td>
<td>2024/1/10</td>
<td>15,000</td>
</tr>
<tr>
<td>スマートフォン</td>
<td>100</td>
<td>800</td>
<td>大阪</td>
<td>鈴木花子</td>
<td>2024/1/15</td>
<td>80,000</td>
</tr>
<tr>
<td rowspan="4">家電 家具</td>
<td>冷蔵庫</td>
<td>30</td>
<td>1200</td>
<td>名古屋</td>
<td>田中一郎</td>
<td>2024/2/1</td>
<td>36,000</td>
</tr>
<tr>
<td>洗濯機</td>
<td>40</td>
<td>500</td>
<td>福岡</td>
<td>高橋次郎</td>
<td>2024/2/5</td>
<td>20,000</td>
</tr>
<tr>
<td>ソファ</td>
<td>20</td>
<td>1000</td>
<td>京都</td>
<td>中村明子</td>
<td>2024/3/1</td>
<td>20,000</td>
</tr>
<tr>
<td>ベッド</td>
<td>15</td>
<td>1200</td>
<td>札幌</td>
<td>松本幸子</td>
<td>2024/3/3</td>
<td>18,000</td>
</tr>
<tr>
<td>合計</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td>189,000</td>
</tr>
</table>
  • 出力(抽出されたテーブル)
出力
Table # 0 has 8 rows and 8 columns
Table # 0 location on page: 1 is [7, 9, 1459, 8, 1460, 1012, 8, 1013]
...Cell[0][0] has text 'カテゴリ'
...content on page 1 is within bounding polygon '[4, 2, 132, 2, 132, 34, 4, 34]'
...Cell[0][1] has text '製品名'
...content on page 1 is within bounding polygon '[132, 2, 330, 2, 330, 36, 132, 34]'
...Cell[0][2] has text '販売数'
...content on page 1 is within bounding polygon '[330, 2, 463, 2, 463, 36, 330, 36]'
...Cell[0][3] has text '単価(USD)'
...content on page 1 is within bounding polygon '[463, 2, 680, 2, 680, 36, 463, 36]'
...Cell[0][4] has text '地域'
...content on page 1 is within bounding polygon '[680, 2, 811, 2, 811, 36, 680, 36]'
...Cell[0][5] has text '販売員'
...content on page 1 is within bounding polygon '[811, 2, 943, 2, 943, 36, 811, 36]'
...Cell[0][6] has text '販売日時'
...content on page 1 is within bounding polygon '[943, 2, 1225, 2, 1225, 36, 943, 36]'
...Cell[0][7] has text '売上金額'
...content on page 1 is within bounding polygon '[1225, 2, 1460, 3, 1460, 37, 1225, 36]'
...Cell[1][0] has text 'エレクト ロニクス'
...content on page 1 is within bounding polygon '[4, 34, 132, 34, 132, 270, 4, 270]'
...Cell[1][1] has text 'TV'
...content on page 1 is within bounding polygon '[132, 34, 330, 36, 330, 118, 132, 118]'
...Cell[1][2] has text '50'
...content on page 1 is within bounding polygon '[330, 36, 463, 36, 463, 118, 330, 118]'
...Cell[1][3] has text '300'
...content on page 1 is within bounding polygon '[463, 36, 680, 36, 680, 118, 463, 118]'
...Cell[1][4] has text '東京'
...content on page 1 is within bounding polygon '[680, 36, 811, 36, 811, 118, 680, 118]'
...Cell[1][5] has text '佐藤太郎'
...content on page 1 is within bounding polygon '[811, 36, 943, 36, 942, 118, 811, 118]'
...Cell[1][6] has text '2024/1/10'
...content on page 1 is within bounding polygon '[943, 36, 1225, 36, 1225, 118, 942, 118]'
...Cell[1][7] has text '15,000'
...content on page 1 is within bounding polygon '[1225, 36, 1460, 37, 1460, 119, 1225, 118]'
...Cell[2][1] has text 'スマートフォン'
...content on page 1 is within bounding polygon '[132, 118, 330, 118, 330, 270, 132, 270]'
...Cell[2][2] has text '100'
...content on page 1 is within bounding polygon '[330, 118, 463, 118, 463, 270, 330, 270]'
...Cell[2][3] has text '800'
...content on page 1 is within bounding polygon '[463, 118, 680, 118, 678, 270, 463, 270]'
...Cell[2][4] has text '大阪'
...content on page 1 is within bounding polygon '[680, 118, 811, 118, 811, 268, 678, 270]'
...Cell[2][5] has text '鈴木花子'
...content on page 1 is within bounding polygon '[811, 118, 942, 118, 942, 268, 811, 268]'
...Cell[2][6] has text '2024/1/15'
...content on page 1 is within bounding polygon '[942, 118, 1225, 118, 1225, 270, 942, 268]'
...Cell[2][7] has text '80,000'
...content on page 1 is within bounding polygon '[1225, 118, 1460, 119, 1460, 271, 1225, 270]'
...Cell[3][0] has text '家電 家具'
...content on page 1 is within bounding polygon '[4, 270, 132, 270, 132, 915, 4, 915]'
...Cell[3][1] has text '冷蔵庫'
...content on page 1 is within bounding polygon '[132, 270, 330, 270, 330, 337, 132, 337]'
...Cell[3][2] has text '30'
...content on page 1 is within bounding polygon '[330, 270, 463, 270, 463, 337, 330, 337]'
...Cell[3][3] has text '1200'
...content on page 1 is within bounding polygon '[463, 270, 678, 270, 678, 335, 463, 337]'
...Cell[3][4] has text '名古屋'
...content on page 1 is within bounding polygon '[678, 270, 811, 268, 811, 335, 678, 335]'
...Cell[3][5] has text '田中一郎'
...content on page 1 is within bounding polygon '[811, 268, 942, 268, 942, 335, 811, 335]'
...Cell[3][6] has text '2024/2/1'
...content on page 1 is within bounding polygon '[942, 268, 1225, 270, 1225, 337, 942, 335]'
...Cell[3][7] has text '36,000'
...content on page 1 is within bounding polygon '[1225, 270, 1460, 271, 1460, 337, 1225, 337]'
...Cell[4][1] has text '洗濯機'
...content on page 1 is within bounding polygon '[132, 337, 330, 337, 330, 406, 132, 406]'
...Cell[4][2] has text '40'
...content on page 1 is within bounding polygon '[330, 337, 463, 337, 463, 405, 330, 406]'
...Cell[4][3] has text '500'
...content on page 1 is within bounding polygon '[463, 337, 678, 335, 678, 405, 463, 405]'
...Cell[4][4] has text '福岡'
...content on page 1 is within bounding polygon '[678, 335, 811, 335, 811, 405, 678, 405]'
...Cell[4][5] has text '高橋次郎'
...content on page 1 is within bounding polygon '[811, 335, 942, 335, 942, 404, 811, 405]'
...Cell[4][6] has text '2024/2/5'
...content on page 1 is within bounding polygon '[942, 335, 1225, 337, 1225, 405, 942, 404]'
...Cell[4][7] has text '20,000'
...content on page 1 is within bounding polygon '[1225, 337, 1460, 337, 1460, 405, 1225, 405]'
...Cell[5][1] has text 'ソファ'
...content on page 1 is within bounding polygon '[132, 406, 330, 406, 332, 642, 132, 640]'
...Cell[5][2] has text '20'
...content on page 1 is within bounding polygon '[330, 406, 463, 405, 463, 643, 332, 642]'
...Cell[5][3] has text '1000'
...content on page 1 is within bounding polygon '[463, 405, 678, 405, 678, 645, 463, 643]'
...Cell[5][4] has text '京都'
...content on page 1 is within bounding polygon '[678, 405, 811, 405, 811, 643, 678, 645]'
...Cell[5][5] has text '中村明子'
...content on page 1 is within bounding polygon '[811, 405, 942, 404, 942, 643, 811, 643]'
...Cell[5][6] has text '2024/3/1'
...content on page 1 is within bounding polygon '[942, 404, 1225, 405, 1225, 645, 942, 643]'
...Cell[5][7] has text '20,000'
...content on page 1 is within bounding polygon '[1225, 405, 1460, 405, 1460, 645, 1225, 645]'
...Cell[6][1] has text 'ベッド'
...content on page 1 is within bounding polygon '[132, 640, 332, 642, 332, 914, 132, 915]'
...Cell[6][2] has text '15'
...content on page 1 is within bounding polygon '[332, 642, 463, 643, 463, 914, 332, 914]'
...Cell[6][3] has text '1200'
...content on page 1 is within bounding polygon '[463, 643, 678, 645, 678, 914, 463, 914]'
...Cell[6][4] has text '札幌'
...content on page 1 is within bounding polygon '[678, 645, 811, 643, 811, 915, 678, 914]'
...Cell[6][5] has text '松本幸子'
...content on page 1 is within bounding polygon '[811, 643, 942, 643, 942, 914, 811, 915]'
...Cell[6][6] has text '2024/3/3'
...content on page 1 is within bounding polygon '[942, 643, 1225, 645, 1226, 915, 942, 914]'
...Cell[6][7] has text '18,000'
...content on page 1 is within bounding polygon '[1225, 645, 1460, 645, 1460, 914, 1226, 915]'
...Cell[7][0] has text '合計'
...content on page 1 is within bounding polygon '[4, 915, 132, 915, 132, 1017, 4, 1015]'
...Cell[7][1] has text ''
...content on page 1 is within bounding polygon '[132, 915, 332, 914, 332, 1017, 132, 1017]'
...Cell[7][2] has text ''
...content on page 1 is within bounding polygon '[332, 914, 463, 914, 463, 1017, 332, 1017]'
...Cell[7][3] has text ''
...content on page 1 is within bounding polygon '[463, 914, 678, 914, 678, 1017, 463, 1017]'
...Cell[7][4] has text ''
...content on page 1 is within bounding polygon '[678, 914, 811, 915, 811, 1017, 678, 1017]'
...Cell[7][5] has text ''
...content on page 1 is within bounding polygon '[811, 915, 942, 914, 942, 1017, 811, 1017]'
...Cell[7][6] has text ''
...content on page 1 is within bounding polygon '[942, 914, 1226, 915, 1226, 1018, 942, 1017]'
...Cell[7][7] has text '189,000'
...content on page 1 is within bounding polygon '[1226, 915, 1460, 914, 1461, 1018, 1226, 1018]'
  • 結論
    テキストの抽出では、Markdown記法を用いて元のデータ構造を保ちながらテーブルの内容を抽出しました。テーブルの抽出では、セルごとにテーブルを抽出し、各セルの位置情報と内容を保存しました。以上の結果から、両方の抽出方法ともテーブルの構造を保持したまま内容を抽出できることがわかり、Layout modelが画像形式のExcelファイルに対応可能であると結論しました。もちろん、抽出されたコンテンツをRAGシステムに適用するためには、さらに加工が必要になるかもしれませんが、有望だと考えられます。

フローチャート(画像ファイル)

公式ドキュメントによる説明

Layout modelは画像ファイルが処理できるものの、フローチャートに対応することは公式ドキュメントに全く言及されていません。確認してみたいため、フローチャートから抽出されたテキストをチェックしました。最も注目したいのは、抽出されたテキストが元のフローチャートの構造をどれだけ保っているかという点です。

検証結果

  • 入力
  • 出力 (抽出された内容)
出力
<figure>

ユーザーが注文を入力

注文内容の確認

在庫確認

在庫あり

支払い処理

支払い方法

クレジットカード

PayPal

銀行振込

クレジットカード決済

PayPal決済

銀行振込手続き

支払い成功

在庫なし

支払い成功

配送手続き

配送準備と発送

支払い失敗

発送完了

注文完了通知

エラーメッセージ表示

在庫切れ通知

終了

</figure>
  • 結論
    RAGに適用するためには、抽出されたテキストが元のフローチャートが表すプロセス(フローチャートの構造)を保持する必要があります。しかし、抽出の結果から見ると、内容は全て抽出されましたが、構造が全く見受けられませんでした。したがって、Layout modelはフローチャートに対応できないと結論しました。

他の可能性

以上の検証から、AADIではフローチャートに対応できないことがわかりました。別の手法として、フローチャートを手作業でmermaid記法に転換し、インデックスにインポートする方法が考えられます。実際のRAGシステムでの検証により、LLMがmermaid記法で表現されたフローチャートを理解できることが確認され、この手法の有効性が証明されました。ただし、弱点は手作業で行う必要があり、労力がかかる点です。

https://mermaid.js.org/

一方で、AADI以外にも、LlamaIndexが提供するLlamaParseというドキュメント処理ツールがあります。まだ使用したことがなく、有効性については不明ですが、公式サイトにはフローチャートに対応できると記載されています。

The best genAI-native parsing platform built specifically to transform complex documents (with tables, charts, images, flow diagrams etc) into clean data for LLM applications

https://www.llamaindex.ai/llamaparse

終わりに

今回の記事では、RAGシステムのためにAzure AI Searchを使って検索エンジンを構築する際に、Excelファイルフローチャートの外部情報を活用する手法を調査・検証しました。

Azure AI Searchが提供する基本的な手法では難しいという背景のもと、Azure AI Document IntelligenceのLayout modelが利用可能かどうかを検証しました。Excelに関して、XLSXファイルの場合、Layout modelによる抽出では元のテーブル構造が破壊されたため対応できない一方、画像ファイルの場合は対応可能でした。フローチャートに関して、画像ファイルのみを検証しましたが、構造が全く保たれず、対応できませんでした。

注意すべき点は、ここでの調査と検証はRAGシステムの検索エンジンの構築にとどまったため、さらにRAGシステムでどのように利用するのか、またはRAGの精度に具体的にどんな影響を与えるのかはさらに深掘りする必要があります。筆者も今後、この方向に引き続き取り組んでいきます。今回の記事は皆さんのお役に立てれば幸いです。

生成AIを活用したPoCや支援にご興味があれば、以下リンクよりお問い合わせください。
https://givery.co.jp/lp/ai-lab/members/

Givery AI Lab

Discussion

ログインするとコメントできます