powered by TechFeed
表示モード
ハウツー

PDFの表・グラフもまとめてベクトル化 — NVIDIAのマルチモーダルRAGパイプラインをGPUなしで構築する

8月7日、Sana Hassanが「Building a Multimodal RAG Pipeline with NVIDIA NeMo Retriever, Hosted NIMs, LanceDB, Reranking, and Grounded Generation」と題した記事を公開した。この記事では、NVIDIA NeMo RetrieverとホストされたNIMエンドポイントを組み合わせ、PDFからテキスト・表・グラフを抽出してLanceDBに格納し、マルチモーダルRAGパイプラインを構築する手順について詳しく紹介されている。

8月7日、Sana Hassanが「Building a Multimodal RAG Pipeline with NVIDIA NeMo Retriever, Hosted NIMs, LanceDB, Reranking, and Grounded Generation」と題した記事を公開した。この記事では、NVIDIA NeMo RetrieverとホストされたNIMエンドポイントを組み合わせ、PDFからテキスト・表・グラフを抽出してLanceDBに格納し、マルチモーダルRAGパイプラインを構築する手順について詳しく紹介されている。


なぜこのパイプラインが実用的か

テキストだけを検索対象にした従来のRAG構成では、PDFに含まれる表やグラフ、インフォグラフィックの内容を正確に取得できないケースが多い。本チュートリアルはその課題に正面から向き合い、テキスト・表・グラフ・インフォグラフィックを統一的に処理するパイプラインを、Google Colab上で完結する形で実装している。

モデル推論はNVIDIA NIMs(NVIDIA AI Foundationが提供するホスト型推論エンドポイント)に委ねるため、ローカルGPUは不要だ。必要なのはNVIDIA APIキー(nvapi-で始まるもの)のみである。


使用するコンポーネントの背景

NVIDIA NeMo Retriever

NVIDIA NeMo Retrieverは、エンタープライズ向けRAGパイプライン構築を目的としたNVIDIAのマイクロサービス群だ。埋め込み生成・リランキング・抽出といった各処理を個別のNIMエンドポイントとして提供しており、ローカルにGPUを持たない環境でもAPIキー経由で利用できる。

LanceDB

LanceDBは、Pythonから直接利用できるサーバーレスのベクトルデータベースだ。PostgreSQLやRedisのように別プロセスを立ち上げる必要がなく、ローカルディレクトリへの書き込みだけで動作する。Chroma・Qdrant・Weaviateといった他のベクトルDBと比較したときの特徴は、Apacheのカラム型フォーマットであるLance形式を採用している点にある。これによりディスク効率と読み取り速度を両立しており、単一Notebook内での完結構成に適している。本チュートリアルではIVF_HNSW_SQインデックスを使用し、検索速度と精度のバランスを取っている。

NeMo Retriever + LanceDBの組み合わせ

NeMo RetrieverはベクトルDB自体を内包しない。埋め込み生成・リランキングの処理はNIMエンドポイントが担い、ベクトルの永続化と検索はLanceDBが担うという役割分担になっている。この構成により、推論インフラを持たない開発者でも、PDFからグラウンデッド生成までの一連のパイプラインを動かせる。


パイプラインの全体構成(6ステージ)

チュートリアルは以下の6ステージで構成されている。

Stage 1:オフラインテキスト抽出(APIキー不要)

まず、APIキーなし・GPU不要で動作することを確認するため、PDFiumを使ったCPUベースのテキスト抽出を実行する。

offline = (
    create_ingestor(run_mode="inprocess", allow_no_gpu=True)
    .files(DOCS)
    .extract(
        extract_text=True,
        extract_tables=False, extract_charts=False,
        extract_images=False, extract_infographics=False,
        use_page_elements=False,
        method="pdfium",
    )
)
df_offline = offline.ingest()

この段階では表・グラフの抽出は行わず、テキストのみを取得する。環境確認と動作検証を兼ねたステップだ。


Stage 2:マルチモーダル取り込み(本命)

APIキーがある場合に実行される本格的な取り込み処理がここだ。使用するNIMエンドポイントは以下の5種類:

役割 エンドポイント
ページレイアウト検出 nemotron-page-elements-v3
OCR nemotron-ocr-v1
表構造抽出 nemotron-table-structure-v1
グラフ・インフォグラフィック解析 nemotron-graphic-elements-v1
埋め込み生成 nvidia/llama-nemotron-embed-1b-v2

抽出後は512トークン単位のチャンク分割(64トークンのオーバーラップ付き)と、コンテンツハッシュ・バウンディングボックスのIoUを使った重複除去を適用する。生成されたベクトルはLanceDBのIVF_HNSW_SQインデックスに格納される。

ing = (
    create_ingestor(run_mode="inprocess", allow_no_gpu=True, error_policy="collect")
    .files(DOCS)
    .extract(
        extract_text=True, extract_tables=True,
        extract_charts=True, extract_infographics=True,
        method="pdfium", dpi=200,
        table_output_format="markdown",
        split_config={"text": {"max_tokens": 512, "overlap_tokens": 64}},
        ...
    )
    .dedup(content_hash=True, bbox_iou=True, iou_threshold=0.45)
    .embed(model_name="nvidia/llama-nemotron-embed-1b-v2", ...)
    .vdb_upload(vdb_op="lancedb", ...)
)

Stage 3〜4:検索とVLリランキング

Stage 3では、クエリを埋め込みベクトルに変換し、LanceDBのインデックスから上位チャンクを取得する密検索(dense retrieval)を実装する。単一クエリとバッチクエリの両方に対応している。

Stage 4では、nvidia/llama-nemotron-rerank-vl-1b-v2を使ったビジョン・ランゲージ(VL)リランキングを加える。候補を広めに取得(refine_factor=4)してから意味的関連度で並べ替える構成だ。

reranking = Retriever(
    rerank=True,
    rerank_kwargs={
        "model_name": "nvidia/llama-nemotron-rerank-vl-1b-v2",
        "refine_factor": 4,
        "batch_size": 16,
    },
    ...
)

Stage 5:メタデータフィルタリング

LanceDBのWHERE句を使い、特定のキーワードを含むチャンクだけを検索対象に絞ることができる。

hits = retriever.query(
    "gadget costs",
    top_k=5,
    vdb_kwargs={"where": "text LIKE '%Cost%'"},
)

Stage 6:グラウンデッド生成とRecall@k評価

最終ステージでは、取得したチャンクをコンテキストとしてnvidia/llama-3.3-nemotron-super-49b-v1.5に渡し、回答を生成する。プロンプトは「提供されたコンテキストのみを使って回答し、出典を[1][2]の形式で明記せよ」という構成になっており、ハルシネーションを抑制する設計だ。

prompt = textwrap.dedent(f"""\
    Answer the question using ONLY the numbered context below.
    Cite the sources you used as [1], [2], etc. If the context is
    insufficient, say so plainly.
    Context:
    {chr(10).join(ctx)}
    Question: {question}
    """)

最後に、3件のゴールドラベルクエリに対するRecall@5を計算し、検索品質を定量評価する。


実行環境と成果物

  • 実行環境:Python 3.12(Google Colab標準ランタイムと一致)、ローカルGPU不要
  • 生成される成果物./lancedb(ベクトルテーブル)、./extracted.md(Markdown化されたドキュメント全文)
  • 全コードGitHubで公開されている

PDFからの非構造化データ処理・ベクトルDB構築・リランキング・引用付き生成という一連のフローを、単一のNotebookで完結させている点が本チュートリアルの実用的な価値だ。

詳細はBuilding a Multimodal RAG Pipeline with NVIDIA NeMo Retriever, Hosted NIMs, LanceDB, Reranking, and Grounded Generationを参照していただきたい。