データ取り込み

今日のRAGソリューションをより深く理解するために、RAG Cookbookの詳細をご覧ください

Watson for Customer Careリードスペースをベースとして使用して色を変更したリードスペース。
概要

取り込みとは、ソース文書から情報を解析し、後で検索できるように検索スペースに埋め込むことができるようにするプロセスです。これはプレーン・テキストにとっては単純なプロセスですが、ソース文書が非「テキスト」形式である場合、複雑さになります。例えば、Microsoft WordやPDFの場合や、およびヘッダーとフッターの繰り返し、複数の列のテキスト、表などの複雑な書式が含まれている場合です。

このセクションでは、これらの課題の克服に役立つヒント、テクニック、アクセラレーター、資産のポイントをご紹介します。

どこから始めたらいいのか。

 

技術者以外のユーザーの場合、ドキュメントは比較的シンプルで、watsonx Orchestrateを使用したノーコードのソリューションが必要です。

 

技術系ユーザーで、関連するドキュメントが比較的単純で、Watson Discoveryにアクセスできる場合は、そこから始めてください。Watson Discoveryは、コーディングの必要性が最小限で抑えられ、使いやすい優れたUIを備えています。複雑な文書の場合は、watson discoveryを使用して文書の特定の部分に注釈を付けて抽出できます。RAGバックエンド・パイプラインで使用することも、フロントエンドで文書内の段落の正確な位置を取得してテキストを強調表示することもできます。

 

ただし、Watson Discoveryには制限があります。文書が50MBを超えると、Watson Discoveryにロードすることができません。文書が複雑すぎる場合(例:ネストされたテーブル形式や不規則なテーブル形式が含まれる場合、Watson Discoveryは文書構造全体を把握できない場合があります。このような場合は、オープンソース・ライブラリーを使用してカスタム・データ取り込みパイプラインを実装することをお勧めします。

 

オープンソース・ライブラリーに関しては、LangChainとLlamaIndexはデータ取り込み機能の点で非常に似ています。しかし、ドキュメンテーションおよびベクトル・データベースやその他のアプリケーションとの統合の点で、LangChainの方がLlamaIndexよりも使いやすいでしょう。LangChainとLlamaIndexの両方には、Documentオブジェクトをある状態から別の状態に変換できるヘルパー関数があるため、LangChainとLlamaIndexの切り替えは、パイプラインのどの時点でも行うことができます。

複雑なテーブルを含むドキュメントの取り込み

以下のメモには、複雑なテーブルと、あるエンゲージメント中にテストされたさまざまなアプローチを含む、ドキュメントの取り込みについて得られたいくつかの教訓が含まれています。一部の観察はエンゲージメントごとに異なる場合がありますが、ネストされたテーブルや複雑なテーブルを含むドキュメントを含むエンゲージメントでは、結論のほとんどが類似するはずです。

Watson Discovery

Watson Discoveryは50MBを超える文書をサポートしていません。元の文書がWord形式であることが原因でサイズが大きくなっている場合、Watson Discoveryを使用するために元のWord文書をPDFに変換する必要があります。ただし、このアプローチを使用すると、特に複雑な表やネストされた表の場合に、Watson Discoveryは表形式を適切にキャプチャできないことがあります。

Watson Discoveryを使用した取り込みでは、事前トレーニングされたモデルと、DiscoveryのSmart Document Understandingを通じて手動で数ページに注釈を付けることによってトレーニングされたユーザー・モデルとの2つの異なるアプローチをテストできます。ここでは、複雑なネストされたテーブルを含むエンゲージメントの1つに対する、これら2つのアプローチから得られた教訓について説明します。

  • 事前トレーニング済みモデル
    • 長所:
      • DiscoveryのHTMLアウトプットは、多くの場合、ユーザーで学習されたモデル(SDU)よりもうまく表の構造を捉えています。
    • 短所:
      • Discoveryでは、ネストされたテーブルを検出するのが依然として困難でした。場合によっては、最も内側のテーブルだけがテーブルとして認識され、外側のテーブルがテキストとしてキャプチャされ、データ構造が失われます。
    • Watson Discoveryを使用する場合、生のテキストを使用する方が、HTMLアウトプットを使用するよりも良い成果が得られる場合があります。

  • ユーザー定義モデル
    • 長所:
      • 注釈が非常に簡単で、後で特定のセクションをフィルタリングしたい単純なドキュメントに適しています。
    • 短所:
      • テーブル内のすべてのテーブルに注釈を付けたとしても、ネストされたテーブル構造を検出できませんでした。
      • 単純なテーブルであっても、新しいドキュメント内のテーブルを検出するのに問題がありました。7箇所以上の文書から20ページに手作業で注釈を付けた後でも、同様です。

Watson Discoveryの結果は、複雑で入れ子になった表については後述する他のカスタム・ライブラリーほど正確ではありませんでしたが、その結果にはページ番号やテキストの座標などの多くのメタデータが含まれており、UIの強調表示に役立つメタデータが多数含まれていました。このメタデータは、検索中に追加のフィルターを追加したり、特定のHTMLセクションに基づいてドキュメントを分割したりする場合に非常に役立ちます。

PyPDFLoader vs PyMuPDF

PyMuPDFライブラリーははるかに高速で、特に大規模なデータに対してより正確な結果をもたらします。50.6 MBのデータセットに次の2つの関数を使用すると、LangChainのPypdfLoaderでは0.366秒かかるのに対し、PyMuPDFでは0.131秒でした。したがって、大きなファイルの場合や、ドキュメントの各セクションを個別のセクションとして抽出したくない場合は、PyMuPdfライブラリーを使用します。

def pdf_to_text(path: str,
                        start_page: int = 1,
                        end_page: Optional[int | None] = None) -> (list[str], list[str], list[dict]):
    """
    Converts PDF to plain text.

    Params:
        path (str): Path to the PDF file.
        start_page (int): Page to start getting text from.
        end_page (int): Last page to get text from.
    """
    logger.debug("Processing PDF %s".format(path))

    from langchain_community.document_loaders import PyPDFLoader

    loader = PyPDFLoader(path)
    pages = loader.load()
    total_pages = len(pages)
    logger.debug(f'Total pages: {total_pages}')
    _ids = []
    _metadata = []
    _file_name = Path(path).name

    if end_page is None:
        end_page = len(pages)

    _text_list = []
    for index in range(start_page - 1, end_page):
        text = pages[index].page_content
        text = text.replace('\n', ' ')
        text = re.sub(r'\s+', ' ', text)
        _text_list.append(text)
        _ids.append(f'page_{index + 1}')
        _metadata.append({
            "id": index + 1,
            "file_name": _file_name,
            "page": f'page {index + 1}'
        })

    return _text_list, _ids, _metadata

def large_pdf_to_text(path: str,
                        start_page: int = 1,
                        end_page: Optional[int | None] = None,
                        remove_string_list=None) -> (list[str], list[str], list[dict]):
    """
        Converts PDF to plain text using PyMuPDF, impressive execution speed, making it suitable for
        large-scale PDF processing. While it does not offer dedicated table extraction features, it can
        still be used to analyze the PDF structure and extract tabular data with additional processing
        steps if needed.

        50.6MB -> 0.131 seconds (PyMuPDF) vs. 0.366 seconds (pypdf)

        Params:
            file_stream (stream): Stream to the PDF file.
            path (str): Path to the PDF
            start_page (int): Page to start getting text from.
            end_page (int): Last page to get text from.
            remove_string_list (list[str]): Remove string list.
        """
    logger.debug("Processing PDF %s".format(path))
    if remove_string_list is None:
        remove_string_list = []

    import fitz

    # pdf_reader = fitz.open(stream = file_stream, filetype = "pdf")
    pdf_reader = fitz.open(path)
    if end_page is None:
        end_page = pdf_reader.page_count

    _text_list = []
    _ids = []
    _metadata = []
    _file_name = Path(path).name
    for index in range(start_page - 1, end_page):
        text = pdf_reader[index].get_text("text")
        text = text.replace('\n', ' ')
        text = re.sub(r'\s+', ' ', text)
        for remove_string in remove_string_list:
            re_compile = re.compile(remove_string)
            text = re_compile.sub('', text)
            _metadata.append({
                "id": index + 1,
                "file_name": _file_name,
                "page": f'page {index + 1}'
            })
        _ids.append(f'page_{index + 1}')
        _text_list.append(text)

    return _text_list, _ids, _metadata

LangChainのUnstructuredPDFLoader用のhtml取り込み用の非構造化PDFローダー

  • 長所:
    • Watson Discoveryと類似したテキストのページ番号やx、y座標など、多数のメタデータが含まれており、その後のハイライトを示すのに役立つ可能性があります。
  • 短所:
    • 多数のネストされたテーブルや複雑なテーブルを適切にキャプチャできません。また、このアプローチでは、元のdocx形式をそれぞれPDFに変換する必要があります。

LangChainからのHTML取り込みUnstructuredWordDocumentLoader用の非構造化Wordローダー

  • 長所:
    • ドキュメントを使用すると、テーブルをより適切にキャプチャできるようになり、LLMが回答を生成するためのより適切なコンテキストをサポートできました。
  • 短所:
    • PDFローダーと比較してページ区切りを追加した後でも、ページ番号などのメタデータが適切にキャプチャされなかった。

HTMLテーブルの要約

テーブル構造を保つため、抽出されたHTML(非構造化ライブラリーを使用)からテーブル要素をLLM(長いテーブル用のMixtralはコンテキストサイズのため良い選択肢である)にインプットし、プロンプトを作成してテーブルを要約するか、フュー・ショット学習を使用して、テーブルを大規模言語モデルにより理解しやすくなると考える形式にテーブルを変換します。

  • 長所:
    • アウトプットテキストのチャンク化と取り込みが容易になり、表の構造が維持されます。
  • 短所:
    • 大規模な複数ページの表の場合、プロンプトでは一部の情報が失われる可能性があるため、表を要約するのではなく変換するフュー・ショット学習を行うことで、より良い成果が得られる可能性があります。

camelotやtabulaなどの他のテーブル・リーダー・ライブラリーもテストされましたが、これらのライブラリーは複雑なテーブルに対しては非構造化ほど正確ではなく、また文書内のテーブルを検知するため、大規模言語に渡す意味のあるチャンクを作成するには、より多くの前処理が必要になる可能性があります。

結論として、Watson Discoveryは、ドキュメントを取り込み、UIの構築(例えば、文章を強調表示するため)に役立つ多くの追加メタデータを取得するための簡単かつ迅速な方法ですが、「非構造化」ライブラリー(LlamaIndexとLangChainの両方にラッパーがある)は、Discoveryと比較して、ドキュメント内の複雑なテーブルをより正確にキャプチャできました。したがって、両方のアプローチを組み合わせると、ドキュメント内に複雑なテーブルを含むプロジェクトには役立ちます。

IBMツール

watsonx Orchestrateによる取り込み

グラフィカル・ユーザー・インターフェースなしの取り込みは、技術的なエクスペリエンスと知識が必要であるため、ビジネス・ユーザーにとっては難しい場合があります。watsonx Orchestrateは、ドラッグ・アンド・ドロップ式のユーザー・インターフェース(UI)を備えており、ビジネス・ユーザーのフラストレーションを軽減します。青いアップロード・ボタンをクリックすると、ドキュメントをwatsonx Discovery(別名Elasticsearch)に直接アップロード、保管する機能を開始します。

watsonx Orchestrateのドラッグ・アンド・ドロップ・ポータルのスクリーンショット

これにより、ユーザーはドキュメントをElasticsearchに直接アップロードして、独自のカスタム・ナレッジ・ベースを作成できます。

watsonx Orchestrateのドキュメントアップロード画面のスクリーンショット

ドキュメントをwatsonx Discoveryに直接取り込む代わりに、watsonx Orchestrateを通じてwatson Discoveryに接続することもできます。Watson Discoveryを介してデータを取り込む方法の詳細については、以下をご覧ください。

Watson Discovery

取り込みに使用できるその他の内部ツールのうちの1つが、Watson Discoveryです。Watson Discoveryは単なる取り込みツールではなく、非構造化データを取り込み、正規化、強化、検索できます。以下の画像は、Watson Discoveryのコア・コンポーネントと機能を示しています。

Watson Discoveryの主要機能の図

上記のとおり、Watson Discoveryの中核機能の1つは、JSON、HTML、PDF、Wordなどのさまざまな形式で構造化および非構造化データを取り込み、Smart Document Understandingを実行することです。

環境内にWatson Discoveryがプロビジョニングされている場合は、最初にプロジェクトを作成し、次にローカル・ストレージまたはオブジェクト・ストレージ・バケットなどのクラウドの場所からデータをアップロードできるコレクションを作成すると、ドキュメントを簡単に取り込むことができます。

コレクションを作成したら、ハンバーガー・メニューの「コレクションの管理」タブに移動し、「アイデンティティー・フィールド」で、ドキュメントの処理に使用する方法を選択できます。

Watson Discoveryの文書処理画面の画面

選択肢は3つあります。

  • テキスト抽出のみ:ドキュメントからテキストのみを抽出します。
  • ユーザー・トレーニング・モデル:ドキュメントのさまざまな部分にカスタム・タグを使用して注釈を付け、ドキュメント内の繰り返しの視覚パターンに基づいて独自のモデルをトレーニングできます。
  • 事前トレーニング済みモデル:事前トレーニング済みのIBMモデルを使用してテキストを抽出し、表、リスト、セクションを識別できます。

すべての文書が処理されるまでには時間がかかる場合があります。その後、Watson Discovery APIを使用して、コード内でデータを読み取り、使用できます。

以下は、APIを使用してコレクションからデータを読み取る方法を示すサンプル・コードです。

from langchain.docstore.document import Document
import os
import logging
import time
from ibm_watson import DiscoveryV2
from ibm_cloud_sdk_core.authenticators import IAMAuthenticator

WD_PAGE_SIZE = 200
MAX_RETRIES = 2
def get_documents_from_wd(collection_ids=[WD_COLLECTION_ID]):
    
    print("Fetching documents from Watson Discovery")
    documents = []
    
    print("Configuring the WD client")
    authenticator = IAMAuthenticator(WD_API_KEY)
    wd_client = DiscoveryV2(
        version="2023-03-31",
        authenticator=authenticator
    )
    
    print("Setting the WD service URL")
    wd_client.set_service_url(WD_SERVICE_URL)
    
    print("Fetching documents from WD")
    page_id = 0
    retries = 0
    while True:
        try:
            print("Fetching page: " + str(page_id))
            response = wd_client.query(
                project_id=WD_PROJECT_ID,
                collection_ids=collection_ids,
                return_=["text"],
                count=WD_PAGE_SIZE,
                offset=page_id*WD_PAGE_SIZE
            ).get_result()
            if response is None or not isinstance(response, dict):
                print("No query result")
                raise ValueError("No query result")
            if "results" not in response or response["results"] is None or not isinstance(response["results"], list):
                print("No query result 2")
                raise ValueError("No query result")
            results = response["results"]
            if len(results) == 0:
                print("No more results")
                break
            print("Fetched " + str(len(results)) + " documents")
            documents.extend(list(map(lambda result: Document(page_content=result["text"][0],  
                                                              metadata= {"collection_id": WD_COLLECTION_ID, "document_id" : result['document_id']}), results)))
            page_id += 1
        except Exception as error:
            logging.error("Failed to fetch documents from WD", str(error))
            retries += 1
            time.sleep(5)
            if retries > MAX_RETRIES:
                break
            print("Retrying...")
    print("Fetched " + str(len(documents)) + " documents")
    return documents

 

Watson DiscoveryのWeb Crawl

Watson Discoveryでプロジェクトを作成する場合、Webクロールをスケジュールして、指定されたURLから情報を取得できます。必要なのは、データ・ソースを選択する際にWeb Crawlを選択し、URLとCrawlスケジュールを指定することだけです。

Watson Discovery Webクローラーの構成画面。
Watson DiscoveryのWebクローラー・スケジューラーの画面

その後、前述の手順を実行してコレクションを管理し、APIを使用してコレクションからデータを読み取ることができます。Watson DiscoveryでWeb Crawlを設定する方法の詳細については、こちらの動画をご覧ください。

詳細と機能については、Watson Discoveryの開発者向けページを参照してください。

Deep Search

Deep Searchは、IBM Researchの取り込み用オープンソースのツールキットです。Deep Searchは、最先端のAI手法を活用して、大量の文書コレクションを継続的に収集、変換、強化、リンクします。公開PDFドキュメントと専有PDFドキュメントの両方に使用できます。

Deep Searchは、非構造化PDFドキュメントを構造化されたJSONファイルに、正確かつ簡単に変換します。知識抽出を自動化できるだけでなく、独自の基本モデルや大規模言語モデルを微調整することもできます。

Deep Searchの詳細についてはこちらでご覧いただけます。

Deep Searchは、非構造化ライブラリーやその他のオープンソース・ライブラリーの代わりに複雑なドキュメントを読み取るための優れた内部ツールです。Pythonノートブックとしてさまざまな種類の例を含む、リポジトリーには非常に優れたドキュメンテーションもあります。

Deep Searchのサンプル・ノートブック

PDFからのテーブル取り込み

Deep Searchは、JSON形式のPDFから表を抽出する際に、適切な成果を提供します。

テストでは、CIPPの部分的なPDFドキュメントを使用しました。これには3つのページが含まれており、それぞれに表データが表示されています。この場合、RAGの最高のパフォーマンスは、Deep Searchを使用して表をJSON形式に抽出し、PDFを直接使用するのではなく、Watson Discovery / watsonx Discoveryで使用するためにHTMLに変換することで、Deep SearchなしでPDFをHTMLに変換するか、Watson Discovery / watsonx DiscoveryでDeep SearchのJSONのみを使用することで達成します。Watson Discoveryは、表の間違ったセルから間違った答えを選択する場合もありますが、それよりも速いことがわかり、また、同じケースで、Watson Discoveryは表のグリッド内の正確な答えを特定できることがわかりました。

IBM Datacap

IBM Datacapは、文書とデータ・キャプチャーのための包括的なソリューションで、データとドキュメント画像の高速かつ正確でコスト効率の高いスキャン、分類、認識、検証、エクスポートを提供します。ドキュメントをキャプチャし、関連データを抽出し、それを下流のビジネスプロセスに統合します。

Datacapは、紙文書をスキャナー、多機能プリンター、またはモバイルデバイスから取得し、電子文書をファイルシステム、ファックス、またはEメールサーバーからインポートします。行やにじみ、境界線の傾き補正や削除などの画像処理機能でデータ抽出を強化します。

Datacapは、光学式文字認識(OCR)、手書きのインテリジェント文字認識(ICR)、マークの光学式マーク認識(OMR)、バーコード読み取りを使用して、データを効率的に抽出します。

インストールと使用方法の詳細については、IBM Datacapのドキュメンテーションを参照してください。

オープンソースツール

LangChainデータローダー

LangChainは、LangChainのDocumentオブジェクトとしてソースからデータをロードするさまざまなタイプのドキュメントローダーをサポートしています。Documentオブジェクトは、パイプラインの後でチャンク化や取得に簡単に使用できるテキストとそれに関連付けられたメタデータです。

現在、LangChainはディレクトリー全体、または次のようなさまざまな種類のファイルをロードするローダーをサポートしています。

  • CSV
  • HTML
  • JSON
  • マークダウン
  • Microsoft Office
  • PDF

これらのローダーは、Pythonの数行で使用できます。例えば、PDFを読み込むには、次のコードを使用できます。

from langchain_community.document_loaders import PyPDFLoader

loader = PyPDFLoader("example_data/layout-parser-paper.pdf")
pages = loader.load_and_split()

これらのローダーの使用方法については、LangChainのドキュメンテーションを参照してください。

上記のリンクに記載されているローダー以外にも、Eメール、Github、Googleドライブなどの特定のアプリケーションから直接データを読み込むことができる追加のローダーがあります。さまざまなローダーのリストはこちらで確認できます。

複雑な構造、形式、テーブルを持つドキュメントがある場合は、LangChainの非構造化ローダーを使用できます。これらのローダーは内部で、複雑なドキュメント(PDF、MSWord、Power Pointなどさまざまなフォーマット)や、ドキュメントをさまざまなコンポーネントに分解するのに最適なライブラリーの一つである非構造化ライブラリーを使用しています:LangChain UnstructuredLoader

LlamaIndexデータローダー

LangChainと同様に、LlamaIndexはさまざまなタイプのローダーもサポートしています。LlamaIndexを使用してデータをロードする簡単な方法の1つは、SimpleDirectoryReaderを使用することです。SimpleDirectoryReaderは次のタイプをサポートしています。

  • .csv -カンマで区切られた値
  • .docx - Microsoft Word
  • .epub - EPUB電子書籍フォーマット
  • .hwp - Hangul Word Processor
  • .ipynb - Jupyter Notebook
  • .jpeg、.jpg- JPEG画像
  • .mbox - MBOXメールアーカイブ
  • .md - マークダウン
  • .mp3、.mp4- 音声、動画
  • .pdf - ポータブル・ドキュメント・フォーマット
  • .png - ポータブル・ネットワーク・グラフィックス
  • .ppt、.pptm、.pptx - Microsoft PowerPoint

次のコード・スニペットを使用して、SimpleDirectoryLoaderを使用できます。

from llama_index.core import SimpleDirectoryReader

reader = SimpleDirectoryReader(input_dir="path/to/directory")
documents = reader.load_data()

また、特定の拡張機能を追加して、ディレクトリーからそれらの拡張機能のみを読み取ることもできます。

SimpleDirectoryReader(
   input_dir="path/to/directory", required_exts=[".pdf", ".docx"]
)

LlamaIndex Loaderの使用方法の詳細については、ドキュメンテーションを参照してください。

また、Huggingfaceと同様に、独自のカスタムローダーを作成したり、llamahub.aiで他の人のカスタムローダーを使用したりすることもできます。

ただし、LlamaIndexの欠点の1つは、LangChainと比較して、ドキュメンテーションとコミュニティー・サポートがLangChainほど優れていないため、コードのデバッグが困難になる可能性があることです。

非構造化

複雑なドキュメントを読み込む場合には、非構造化ライブラリーを直接使用することもできます。非構造化ライブラリーは、テーブルの読み取りと解析に非常に優れています。

非構造化ライブラリーはLangChainとLlamaIndexの両方のラッパーでもサポートされており、Documentオブジェクトを自動的に作成して返すため、将来のRAGパイプラインでは使いやすい可能性があります。ただし、プロジェクトでさらなるカスタマイズが必要で、代わりに非構造化ライブラリーを直接使用したい場合は、次のドキュメントを参照してください:Unstructured

詳細はこちら

IBMから最新のテクノロジー・パターン、ソリューション・アーキテクチャー、およびアーキテクチャーに関する出版物を入手してください。

  1. IBM Architecture Centerに移動