LangChainとwatsonx.aiを使用したRAGチャンク化戦略の導入

このチュートリアルでは、 LangChain と、現在 watsonx.aiで利用可能な最新の IBM® Graniteモデルを使用して、いくつかのチャンク化戦略を試します。全体的な目標は、検索拡張生成 (RAG) を実行することです。

チャンク化とは

チャンク化とは、大きなテキストを小さなテキスト・セグメント、またはチャンクに分割するプロセスを指します。チャンク化の重要性を実感するために、まずRAGを理解することが役立ちます。RAGは、情報検索と大規模言語モデル(LLM)を組み合わせて、補完用データセットから関連情報を取得し、LLMの出力の品質を最適化する自然言語処理(NLP)の手法です。大量の文書を管理するためにチャンク化を使用し、文を意味のあるまとまり(チャンク)を含む小さなスニペットに分割します。こうしたテキスト・チャンクは、埋め込みモデルを使用してベクトル・データベースに埋め込んで補完することができます。こうして、RAGシステムはセマンティック検索を使用し、最も関連性の高いチャンクのみを取得することができます。コンテキスト・ウィンドウのサイズが小さいモデルが対応しやすいことから、小さなチャンクの方が大きなチャンクよりも優れたパフォーマンスを発揮する傾向があります。

チャンク化の主な要素としては以下が挙げられます。

  • チャンク化戦略:チャンク化戦略はチャンク設定の境界を設定するもので、RAGアプリケーションに合った適切なチャンク化戦略を選択することが重要です。この後のセクションでいくつかの例を紹介します。
  • チャンクのサイズ: 各チャンクに含まれるトークンの最大数。適切なチャンクのサイズを決定するには、通常、いくつかの実験が必要です。
  • チャンクのオーバーラップ: コンテキストを維持するためにチャンク間で重複しているトークンの数。これはオプションのパラメーターです。

チャンク化戦略

さまざまなチャンク化戦略の選択肢が存在します。LLMアプリケーションの特定のユースケースに応じて、最も効果的なチャンク化の手法を選択することが重要です。一般的に使用されるチャンク化のプロセスには次のものがあります。

  • サイズ固定チャンク化:チャンクサイズと、オプションであるチャンクのオーバーラップを指定してテキストを分割します。このアプローチは最も一般的で簡単な方法です。
  • 再帰的チャンク化:好ましいチャンスサイズになるまで、デフォルトのセパレーターを繰り返します。デフォルトのセパレーターには、[" \n\n", " \n", " , ""] が含まれます。このチャンク化では、階層型セパレーターを使用して、段落、文、単語を可能な限りまとめられるようにします。
  • 意味論的チャンク化:テキスト分割の際、埋め込みの意味的類似性に基づいて文をグループ化します。意味的類似性の高い埋め込みは、意味的類似性の低い埋め込みよりも近くます。これにより、コンテキストに応じたチャンクを得ることができます。
  • 文書ベースのチャンク化:文書の構造に基づく分割を実行します。文書構造を判断する方法としては、マークダウン・テキスト、画像、テーブル、Pythonコード・クラスや関数を使用することができます。この手法では大規模な文書をチャンク化し、LLMで処理することができます。
  • エージェントチャンク化エージェント型AIを活用し、LLMが意味だけでなく、段落の種類、セクションの見出し、手順ごとの説明などのコンテンツ構造に基づいて適切なドキュメント分割を決定できるようにします。このチャンク化手法は実験的なもので、長い文書を処理する際の人間の推論をシミュレートする試みです。

手順

ステップ1. 環境を設定する

いくつかあるツールの中から選択することもできますが、このチュートリアルでは、Jupyter Notebookを使用するためにIBMアカウントを設定する方法について説明します。

  1. IBM Cloudアカウントを使用して、watsonx.aiにログインします。

  2. watsonx.aiプロジェクトを作成します。

    プロジェクトIDはプロジェクト内から取得できます。[管理]タブをクリックし、[一般]ページの[詳細]セクションからプロジェクトIDをコピーしてください。このチュートリアルではこのIDが必要になります。

  3. Jupyter Notebookを作成します。

このステップでは、このチュートリアルからコードをコピーできるノートブック環境が開きます。あるいは、このノートブックをローカル・システムにダウンロードし、watsonx.aiプロジェクトにアセットとしてアップロードすることもできます。さらにGraniteのチュートリアルを表示するには、 IBM Graniteコミュニティをご覧ください。このJupyter Notebookと使用されるデータセットは GitHub にあります。

ステップ2. watsonx.ai RuntimeのインスタンスとAPIキーを設定する

  1. watsonx.aiランタイム・サービス・インスタンスを作成します(適切なリージョンを選択し、無料インスタンスであるLiteプランを選択)。

  2. APIキーを生成します。

  3. watsonx.aiランタイム・サービス・インスタンスを、watsonx.aiで作成したプロジェクトに関連付けます。

ステップ3. 関連ライブラリーをインストールしてインポートし、認証情報を設定する

#installations
!pip install -q langchain langchain-ibm langchain_experimental langchain-text-splitters langchain_chroma transformers bs4 langchain_huggingface sentence-transformers
# imports
import getpass
from langchain_ibm import WatsonxLLM
from langchain_chroma import Chroma
from langchain_community.document_loaders import WebBaseLoader
from ibm_watsonx_ai.metanames import GenTextParamsMetaNames as GenParams
from transformers import AutoTokenizer

資格情報を設定するには、ステップ1で生成したWATSONX_APIKEYおよびWATSONX_PROJECT_IDが必要です。また、APIのエンドポイントとして機能するURLも設定します。

WATSONX_APIKEY = getpass.getpass("Please enter your watsonx.ai Runtime API key (hit enter): ")
WATSONX_PROJECT_ID = getpass.getpass("Please enter your project ID (hit enter): ")
URL = "https://us-south.ml.cloud.ibm.com"

ステップ 4. LLMを初期化する

このチュートリアルでは、LLMとしてGranite 3.1を使用します。LLMを初期化するには、モデル・パラメーターを設定する必要があります。最小・最大トークン制限などのモデル・パラメーターの詳細については、ドキュメンテーションを参照してください。

llm = WatsonxLLM(
        model_id= "ibm/granite-3-8b-instruct",
        url=URL,
        apikey=WATSONX_APIKEY,
        project_id=WATSONX_PROJECT_ID,
        params={
            GenParams.DECODING_METHOD: "greedy",
            GenParams.TEMPERATURE: 0,
            GenParams.MIN_NEW_TOKENS: 5,
            GenParams.MAX_NEW_TOKENS: 2000,
            GenParams.REPETITION_PENALTY:1.2
        }
)

ステップ5. ドキュメントを読み込む

RAGパイプラインに使用しているコンテキストは、Granite 3.1のリリースに関するIBMの公式発表です。LangChainのWebBaseLoaderを使用すると、ウェブページからドキュメントにブログを直接ロードできます。

url = "https://www.ibm.com/jp-ja/new/announcements/ibm-granite-3-1-powerful-performance-long-context-and-more"
doc = WebBaseLoader(url).load()

ステップ 6.テキスト分割の実行

LangChainから入手できるこのチュートリアルで前述した各ストラテジーを実装するためのサンプルコードを見せましょう。

サイズ固定チャンク化

サイズ固定チャンク化を実装するには、LangChainの CapacityTextSplitter を使用し、chunk_sizeとchunk_overlapを設定できます。chunk_sizeは文字数で測定されます。さまざまな値を自由に試してください。また、段落を区別できるように、セパレーターを改行文字に設定します。トークン化には、granite-3.1-8b-instructトークナイザーを使用することができます。トークン化はテキストをLLMで処理できるトークンに分解します。

from langchain_text_splitters import CharacterTextSplitter
tokenizer = AutoTokenizer.from_pretrained(“ibm-granite/granite-3.1-8b-instruct”)
text_splitter = CharacterTextSplitter.from_huggingface_tokenizer(
                    tokenizer,
                    separator=”\n”, #default: “\n\n”
                    chunk_size=1200, chunk_overlap=200)
fixed_size_chunks = text_splitter.create_documents([doc[0].page_content])

チャンクの1つをプリントして、構造をより深く理解することができます。

fixed_size_chunks[1]

出力: (省略)

Document(metadata={}, page_content=’As always, IBM’s historical commitment to open source is reflected in the permissive and standard open source licensing for every offering discussed in this article.\n\r\n Granite 3.1 8B Instruct: raising the bar for lightweight enterprise models\r\n \nIBM’s efforts in the ongoing optimization the Granite series are most evident in the growth of its flagship 8B dense model. IBM Granite 3.1 8B Instruct now bests most open models in its weight class in average scores on the academic benchmarks evaluations included in the Hugging Face OpenLLM Leaderboard...’)

また、Verifyを使用してプロセスを検証したり、各チャンクに存在するトークンの数を確認したりすることもできます。このステップは任意であり、デモンストレーションを目的としています。

for idx, val in enumerate(fixed_size_chunks):
    token_count = len(tokenizer.encode(val.page_content))
    print(f”The chunk at index {idx} contains {token_count} tokens.”)

アウトプット

The chunk at index 0 contains 1106 tokens.
The chunk at index 1 contains 1102 tokens.
The chunk at index 2 contains 1183 tokens.
The chunk at index 3 contains 1010 tokens.

成功です。チャンクサイズが適切に実装されているようです。

再帰的チャンク化

再帰的チャンク化には、LangChainのRecursive TextSplitterを使用できます。固定サイズのチャンク化の例と同様に、さまざまなサイズのチャンクやオーバーラップ・サイズを試すことができます。

from langchain_text_splitters import RecursiveCharacterTextSplitter

text_splitter = RecursiveCharacterTextSplitter(chunk_size=100, chunk_overlap=0)
recursive_chunks = text_splitter.create_documents([doc[0].page_content])
recursive_chunks[:5]

アウトプット

[Document(metadata={}, page_content=’IBM Granite 3.1: powerful performance, longer context and more’),
Document(metadata={}, page_content=’IBM Granite 3.1: powerful performance, longer context, new embedding models and more’),
Document(metadata={}, page_content=’Artificial Intelligence’),
Document(metadata={}, page_content=’Compute and servers’),
Document(metadata={}, page_content=’IT automation’)]

スプリッターは、デフォルトのセパレーター [“ \n \n”, 「\n」、「 “, “」] を使用してテキストを正常にチャンク化しました。

意味論的チャンク化

意味論的チャンク化には、埋め込みモデルまたはエンコーダーモデルが必要です。埋め込みモデルとして、granite-embedding-30m-englishモデルを使用できます。また、構造をより深く理解するために、チャンクの1つをプリントすることもできます。

from langchain_huggingface import HuggingFaceEmbeddings
from langchain_experimental.text_splitter import SemanticChunker

embeddings_model = HuggingFaceEmbeddings(model_name=”ibm-granite/granite-embedding-30m-english”)
text_splitter = SemanticChunker(embeddings_model)
semantic_chunks = text_splitter.create_documents([doc[0].page_content])
semantic_chunks[1]

出力: (省略)

Document(metadata={}, page_content=’Our latest dense models (Granite 3.1 8B, Granite 3.1 2B), MoE models (Granite 3.1 3B-A800M, Granite 3.1 1B-A400M) and guardrail models (Granite Guardian 3.1 8B, Granite Guardian 3.1 2B) all feature a 128K token context length.We’re releasing a family of all-new embedding models. The new retrieval-optimized Granite Embedding models are offered in four sizes, ranging from 30M–278M parameters. Like their generative counterparts, they offer multilingual support across 12 different languages: English, German, Spanish, French, Japanese, Portuguese, Arabic, Czech, Italian, Korean, Dutch and Chinese. Granite Guardian 3.1 8B and 2B feature a new function calling hallucination detection capability, allowing increased control over and observability for agents making tool calls...’)

文書ベースのチャンク化

LangChainの文書ベースのテキスト・スプリッターは、さまざまなファイルタイプの文書と互換性があります。このチュートリアルでは、マークダウン・ファイルを使用します。再帰的JSON分割、コード分割、HTML分割の例については、LangChainのドキュメンテーションを参照してください

ロードできるマークダウン・ファイルの例としては、IBMの GitHubにあるGranite 3.1のREADMEファイルがあります。

url = “https://raw.githubusercontent.com/ibm-granite/granite-3.1-language-models/refs/heads/main/README.md”
markdown_doc = WebBaseLoader(url).load()
markdown_doc

アウトプット

[Document(metadata={‘source’: ‘https://raw.githubusercontent.com/ibm-granite/granite-3.1-language-models/refs/heads/main/README.md’}, page_content=’\n\n\n\n :books: Paper (comming soon)\xa0 | :hugs: HuggingFace Collection\xa0 | \n :speech_balloon: Discussions Page\xa0 | 📘 IBM Granite Docs\n\n\n---\n## Introduction to Granite 3.1 Language Models\nGranite 3.1 language models are lightweight, state-of-the-art, open foundation models that natively support multilinguality, coding, reasoning, and tool usage, including the potential to be run on constrained compute resources. All the models are publicly released under an Apache 2.0 license for both research and commercial use. The models\’ data curation and training procedure were designed for enterprise usage and customization, with a process that evaluates datasets for governance, risk and compliance (GRC) criteria, in addition to IBM\’s standard data clearance process and document quality checks...’)]

ここで、LangChainのMarkdownHeaderTextSplitterを使用して、Headings_to_split_onリストに設定したヘッダー・タイプごとにファイルを分割できるようになりました。また、例としてチャンクの 1 つを出力します。

#document based chunking
from langchain_text_splitters import MarkdownHeaderTextSplitter
headers_to_split_on = [
    (“#”, “Header 1”),
    (“##”, “Header 2”),
    (“###”, “Header 3”),
]
markdown_splitter = MarkdownHeaderTextSplitter(headers_to_split_on)
document_based_chunks = markdown_splitter.split_text(markdown_doc[0].page_content)
document_based_chunks[3]

アウトプット

Document(metadata={‘Header 2’: ‘How to Use our Models?’, ‘Header 3’: ‘Inference’}, page_content=’This is a simple example of how to use Granite-3.1-1B-A400M-Instruct model. \n```python\nimport torch\nfrom transformers import AutoModelForCausalLM, AutoTokenizer\n\ndevice = “auto”\nmodel_path = “ibm-granite/granite-3.1-1b-a400m-instruct”\ntokenizer = AutoTokenizer.from_pretrained(model_path)\n# drop device_map if running on CPU\nmodel = AutoModelForCausalLM.from_pretrained(model_path, device_map=device)\nmodel.eval()\n# change input text as desired\nchat = [\n{ “role”: “user”, “content”: “Please list one IBM Research laboratory located in the United States. You should only output its name and location.” },\n]\nchat = tokenizer.apply_chat_template(chat, tokenize=False, add_generation_prompt=True)\n# tokenize the text\ninput_tokens = tokenizer(chat, return_tensors=”pt”).to(device)\n# generate output tokens\noutput = model.generate(**input_tokens,\nmax_new_tokens=100)\n# decode output tokens into text\noutput = tokenizer.batch_decode(output)\n# print output\nprint(output)\n```’)

アウトプットからわかるように、チャンク化によりテキストがヘッダー・タイプごとに正常に分割されています。

ステップ7. ベクトル・ストアを作成する

さまざまなチャンク化戦略の実験ができました。RAGの実装に進みましょう。このチュートリアルでは、意味論的分割で作成したチャンクを使用し、ベクトル埋め込みに変換します。使用できるオープンソースのベクトル・ストアはChroma DBです。langchain_cromaパッケージを通じてChroma機能に簡単にアクセスできます。

Chromaベクトル・データベースを初期化し、埋め込みモデルを指定して、意味論的チャンク化で生成されたドキュメントを追加してみましょう。

vector_db = Chroma(
    collection_name=”example_collection”,
    embedding_function=embeddings_model,
    persist_directory=”./chroma_langchain_db”, # Where to save data locally
)
Vector_db.add_documents(semantic_chunks)

アウトプット

[‘84fcc1f6-45bb-4031-b12e-031139450cf8’,
‘433da718-0fce-4ae8-a04a-e62f9aa0590d’,
‘4bd97cd3-526a-4f70-abe3-b95b8b47661e’,
‘342c7609-b1df-45f3-ae25-9d9833829105’,
‘46a452f6-2f02-4120-a408-9382c240a26e’]

ステップ8. プロンプト・テンプレートを構造化する

次に、LLM用のプロンプト・テンプレートの作成に進みます。このプロンプト・テンプレートを使用すると、最初のプロンプト構造を変更することなく、複数の質問をすることができます。レトリーバーとしてベクター・ストアを提供することもできます。このステップで、RAG構造が完成します。

from langchain.chains import create_retrieval_chain
from langchain.prompts import PromptTemplate
from langchain.chains.combine_documents import create_stuff_documents_chain

prompt_template = """ < |start_of_role| > user < |end_of_role| > 最後の質問に答えるには、次のコンテキストを使用してください。答えが分からない場合は、分からないと正直に言ってください。答えを作ろうとしてはいけません。
{context}
質問:{input} <|end_of_text|>
<|start_of_role|>assistant<|end_of_role|>"""

qa_chain_prompt = PromptTemplate.from_template(prompt_template)
combine_docs_chain = create_stuff_documents_chain(llm, qa_chain_prompt)
rag_chain = create_retrieval_chain(vector_db.as_retriever(), combine_docs_chain)combine_docs_chain)

ステップ9: RAGチェーンのプロンプト

完成したRAGワークフローを使用して、ユーザー・クエリーを実行してみましょう。1つ目は、構築したベクトル・ストアから追加のコンテキストを必要とせずにモデルを戦略的にプロンプトすることができ、モデルが組み込みの知識を使用しているか、それとも純粋なRAGコンテキストを使用しているのかをテストすることができます。Granite 3.1の発表ブログではDocling について言及しています。これはさまざまなドキュメント・タイプを解析し、マークダウン形式またはJSONに変換するためのIBMのツールです。LLMにDoctlingについて質問してみましょう。

output = llm.invoke(“What is Docling?”)
output

アウトプット

‘?\n\n”Docling” does not appear to be a standard term in English. It might be a typo or a slang term specific to certain contexts. If you meant “documenting,” it refers to the process of creating and maintaining records, reports, or other written materials that provide information about an activity, event, or situation. Please check your spelling or context for clarification.’

明らかに、モデルはDoclingに関する情報に基づいてトレーニングされておらず、外部ツールや情報がなければ、この情報を提供することはできません。構築したRAGチェーンに同じクエリーを実行してみましょう

rag_output = rag_chain.invoke({“input”: “What is Docling?”})
rag_output[‘answer’]

アウトプット

‘Docling is a powerful tool developed by IBM Deep Search for parsing documents in various formats such as PDF, DOCX, images, PPTX, XLSX, HTML, and AsciiDoc, and converting them into model-friendly formats like Markdown or JSON. This enables easier access to the information within these documents for models like Granite for tasks such as RAG and other workflows. Docling is designed to integrate seamlessly with agentic frameworks like LlamaIndex, LangChain, and Bee, providing developers with the flexibility to incorporate its assistance into their preferred ecosystem. It surpasses basic optical character recognition (OCR) and text extraction methods by employing advanced contextual and element-based preprocessing techniques. Currently, Docling is open-sourced under the permissive MIT License, and the team continues to develop additional features, including equation and code extraction, as well as metadata extraction.’

成功です。Graniteモデルは、RAGコンテキストを正しく使用して、セマンティックの一貫性を維持しながら、Doclingに関する正しい情報を教えてくれました。同じ成果は、RAGを使用しなければ不可能であることがわかりました。

まとめ

このチュートリアルでは、RAGパイプラインを作成し、システムの検索精度を向上させるためにいくつかのチャンク化ストラテジーを実験しました。Granite 3.1モデルを使用して、コンテキストとして提供された文書に関連するユーザーのクエリに対する適切なモデル応答を生成することに成功しました。このRAGの実装に使用したテキストは、Granite 3.1のリリースを発表したibm.comのブログから読み込まれました。このモデルは、モデルの初期の知識ベースの一部ではなかったため、提供されたコンテキストを通じてのみアクセスできる情報を提供しました。

さらに詳しく知りたい方は、HTML構造のチャンク化と watsonx チャンク化を使用して、LLMの性能を比較するプロジェクトの成果をご覧ください。

関連ソリューション
IBM watsonx.ai

AI開発者向けの次世代エンタープライズ・スタジオであるIBM watsonx.aiを使用して、生成AI、基盤モデル、機械学習機能をトレーニング、検証、チューニング、導入しましょう。わずかなデータとわずかな時間でAIアプリケーションを構築できます。

watsonx.aiをご覧ください。
人工知能ソリューション

業界をリードするIBMのAI専門知識とソリューション製品群を使用すれば、ビジネスにAIを活用できます。

AIソリューションはこちら
AIサービス

AIの導入で重要なワークフローと業務を再構築し、エクスペリエンス、リアルタイムの意思決定とビジネス価値を最大化します。

AIサービスはこちら
次のステップ

AI開発ライフサイクル全体にわたる機能にワンストップでアクセスできます。使いやすいインターフェース、ワークフロー、業界標準のAPIやSDKを利用して、強力なAIソリューションを構築できます。

watsonx.aiの詳細はこちら デモを予約