📝

Llama Index Tutorial ① (共有用)

2023/12/10に公開

Llama Index 0.9.13 memo

  • 2023/12/10 時点の公式ドキュメントの和訳・要約
  • 英語苦手な人向け
  • とりあえず概要を知りたい人向け
  • OpenAI のAPI KEY があるとスムーズ

Llama Index Tutorial Getting Started

Installation and Setup

Pipからのインストール

  • pip install llama-index コマンドを使って、LlamaIndexをインストールします。
  • 注意:LlamaIndexは、NLTKやHuggingFaceなどの様々なパッケージのローカルファイルをダウンロードして保存する場合があります。これらのファイルが保存される場所を制御するために、環境変数 LLAMA_INDEX_CACHE_DIR を使用します。

ソースからのインストールも選択できますが、その方法は後述します。

OpenAI環境のセットアップ

  • デフォルトでは、テキスト生成にはOpenAIのgpt-3.5-turboモデル、検索や埋め込みにはtext-embedding-ada-002モデルを使用します。
  • これを使用するには、環境変数として OPENAI_API_KEY を設定する必要があります。このAPIキーは、OpenAIアカウントにログインして新しいAPIキーを作成することで取得できます。

ローカルモデルのセットアップ

  • OpenAIを使用したくない場合は、サービスコンテキストでローカルのLLMおよび埋め込みモデルを設定することを検討してください。
  • LLMの使用と設定に関する完全なガイドはこちらにあります。
  • 埋め込みモデルの使用と設定に関する完全なガイドも提供されています。リンク

ソースからのインストール

  • このリポジトリをGit cloneします:
    git clone https://github.com/jerryjliu/llama_index.git
  • 次に以下のステップを実行します:
    • poetryインストールします。これはパッケージ依存関係の管理に役立ちます。
    • poetry shell コマンドで仮想環境を作成します。これにより、インストールされたパッケージがこのプロジェクトに隔離されます。
    • poetry install を実行すると、コアパッケージ要件がインストールされます。
    • オプション:poetry install --with dev,docs を実行すると、ほとんどのローカル開発に必要なすべての依存関係がインストールされます。

How to read these docs

このセクションでは、LlamaIndexのドキュメントの読み方について説明されています。これらのドキュメントは、LlamaIndexやLLM(Large Language Models)および一般的な生成AIに関する経験レベルに関係なくアプローチしやすいように設計されています。

はじめにする前に

  • LlamaIndexはPythonライブラリなので、Pythonがインストールされており、基本的なPythonの書き方を理解している必要があります。JavaScriptを好む場合は、TypeScriptパッケージを試してみることをお勧めします。
  • 多くの例はNotebooks(ジュピタースタイルのノートブック)としてフォーマットされています。ジュピターをインストールする必要はありません。Google Colabのようなホストされたサービスで、ほとんどの例を試すことができます。

ドキュメントの構造

  • これらのドキュメントは、左側のサイドバーを下に進むことで、または各ページの下部にある「次へ」リンクをクリックすることで、段階的に進めるように構成されています。

入門

  • このセクションです。LlamaIndexやLLMについて何も知らない状態から始め、ライブラリをインストールし、最初のデモを5行のコードで書き、LLMアプリケーションの高レベルな概念について学び、5行の例をカスタマイズして自分のニーズに合わせる方法を見ることができます。

ユースケース

  • LlamaIndexが特定のユースケースに適しているかどうかを検討している開発者のために、構築できるものの概要を提供しています。

LlamaIndexの理解

  • 入門セクションを完了したら、次に進むべき場所です。一連の短いチュートリアルで、LlamaIndexアプリケーションの構築の各段階を説明し、ライブラリとLLMの一般的な概念について、徐々に理解を深めるお手伝いをします。

最適化

  • すでに動作するLlamaIndexアプリケーションを持っており、さらに洗練させようとしている場合、最適化セクションでは、埋め込みモデルやチャンクサイズなど、最初に試すべきことから、より複雑で微妙なカスタマイズ、さらにはモデルの微調整までを説明します。

モジュールガイド

  • LlamaIndexの理解セクションと同じ順序でLLMアプリケーションを構築する際に、LlamaIndexの個々のコンポーネントとその使用方法に関する包括的で低レベルのガイドです。

Starter Tutorial

インストールステップの確認

  • まず、インストール手順に従っていることを確認します。

データのダウンロード

  • この例では、Paul Grahamのエッセイ「What I Worked On」のテキストを使用します。これはリポジトリのexamplesフォルダにある多くの例の一つです。
  • このエッセイをダウンロードする最も簡単な方法は、このリンクからダウンロードして、「data」というフォルダに保存することです。

OpenAI APIキーの設定

  • LlamaIndexはデフォルトでOpenAIのgpt-3.5-turboを使用します。

  • APIキーがコードから利用可能であることを確認するために、環境変数として設定します。MacOSとLinuxでは以下のコマンドを使用し、
    Windowsでは set OPENAI_API_KEY=XXXXX を使用します。

    export OPENAI_API_KEY=XXXXX
    

データの読み込みとインデックスの構築

  • 「data」フォルダを作成した同じフォルダに、「starter.py」というファイルを作成し、以下のコードを書きます。

    from llama_index import VectorStoreIndex, SimpleDirectoryReader
    
    documents = SimpleDirectoryReader("data").load_data()
    index = VectorStoreIndex.from_documents(documents)
    

    これにより、dataフォルダ内のドキュメント(この場合はエッセイのテキストですが、多くのドキュメントを含むこともできます)に対するインデックスが構築されます。

    ├── starter.py
    └── data
        └── paul_graham_essay.txt
    

データのクエリ

  • starter.pyに以下の行を追加します。

    query_engine = index.as_query_engine()
    response = query_engine.query("What did the author do growing up?")
    print(response)
    

    これにより、インデックス上でQ&Aエンジンが作成され、シンプルな質問が行われます。次のような回答が返されるはずです:「作者は小説を書き、IBM 1401でプログラミングを試みた。」

ロギングを使ってクエリとイベントを表示

  • 裏側で何が起こっているのかを見たい場合は、starter.pyの先頭に以下のロギング行を追加します。

    import logging
    import sys
    
    logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)
    logging.getLogger().addHandler(logging.StreamHandler(stream=sys.stdout))
    

    ログのレベルを DEBUG に設定すると詳細な出力が得られます。または、level=logging.INFO を使用して少ない情報で。

インデックスの保存

  • デフォルトでは、読み込んだデータは一連のベクトル埋め込みとしてメモリに保存されます。ディスクに埋め込みを保存することで、時間(およびOpenAIへのリクエスト)を節約できます。これは以下の行で行うことができます。

    index.storage_context.persist()
    

    デフォルトでは、データは「storage」というディレクトリに保存されますが、persist_dirパラメータを渡すことで変更することができます。

インデックスの永続化

  • インデックスを永続化するメリットを得るためには、データを生成して保存するだけでなく、それを読み込む必要があります。そこで、starter.pyを以下のように変更して、インデックスが存在しない場合は生成して保存し、存在する場合は読み込むようにします。

    import os.path
    from llama_index import (
        VectorStoreIndex,
        SimpleDirectoryReader,
        StorageContext,
        load_index_from_storage,
    )
    
    # ストレージが既に存在するか確認
    if not os.path.exists("./storage"):
        # ドキュメントを読み込んでインデックスを作成
        documents = SimpleDirectoryReader("data").load_data()
        index = VectorStoreIndex.from_documents(documents)
        # 後で使うために保存
        index.storage_context.persist()
    else:
        # 既存のインデックスを読み込む
        storage_context = StorageContext.from_defaults(persist_dir="./storage")
        index = load_index_from_storage(storage_context)
    
    # いずれの場合もインデックスをクエリできる
    query_engine = index.as_query_engine()
    response = query_engine.query("What did the author do growing up?")
    print(response)
    

    これで、効率的にクエリを実行できますが、これはLlamaIndexでできることの始まりに過ぎません。

次のステップ

  • LlamaIndexを使って高レベルの概念をさらに学びます。
  • さまざまなカスタマイズ方法について学びます。
  • 特定のモジュールに興味がある場合は、左側のガイドをチェックしてください。

これで、LlamaIndexのスターターチュートリアルの概要が分かりました。これを基に、LlamaIndexを使って様々なアプリケーションを構築していくことができます。

High-Level Concepts

これは、大規模言語モデル(LLM)アプリケーションを構築する際によく遭遇する高度な概念についての簡単なガイドです。

  • RAG (Retrieval Augmented Generation): LLMは膨大なデータでトレーニングされていますが、個々のユーザーのデータでトレーニングされているわけではありません。RAGは、ユーザーのデータをLLMが既にアクセスできるデータに追加することで、この問題を解決します。RAGでは、ユーザーのデータを読み込んでクエリ用に準備し(インデックス化)、ユーザーのクエリがこのインデックスに作用して最も関連性の高いコンテキストを選び出し、そのコンテキストをLLMに送り、応答を得ます。

RAG内のステージ

  1. Loading: データをその保存されている場所(テキストファイル、PDF、別のウェブサイト、データベース、APIなど)からパイプラインに取り込むことを指します。LlamaHubは多くのコネクタを提供しています。

  2. Indexing: データを検索可能な構造にすることを意味します。LLMの場合、これには通常、データの意味を表す数値的表現であるベクトル埋め込みの生成が含まれます。さらに、文脈に関連するデータを簡単に正確に見つけるための多くのメタデータ戦略も使用されます。

  3. Storing: データがインデックス化されたら、そのインデックスと他のメタデータを保存することが一般的です。これにより、再度インデックスを作成する必要がなくなります。

  4. Querying: 特定のインデックス戦略には、LLMやLlamaIndexのデータ構造を利用してクエリを行う多くの方法があり、サブクエリ、マルチステップクエリ、ハイブリッド戦略などが含まれます。

  5. Evaluation: パイプラインの効果を他の戦略と比較したり、変更を加えたときの効果をチェックすることは重要なステップです。評価は、クエリへの応答の正確さ、忠実度、速さを客観的に測定します。

Important concepts within each step

各ステージ内のステップに関連する重要な概念を説明します。

Loading Stage

  • Nodes and Documents: Documentは、PDF、APIの出力、データベースからのデータなど、任意のデータソースを包含する容器です。NodeはLlamaIndex内でのデータの基本単位で、ソースドキュメントの「チャンク」(部分)を表します。ノードには、それが属するドキュメントや他のノードとの関連を示すメタデータが含まれています。
  • Connectors: データコネクタ(Readerとも呼ばれることが多い)は、さまざまなデータソースやデータ形式からドキュメントとノードにデータを取り込む役割を果たします。

Indexing Stage

  • Indexes: データを取り込んだ後、LlamaIndexは検索しやすい構造にデータをインデックス化する手助けをします。これには通常、ベクトルストアと呼ばれる特殊なデータベースに保存されるベクトル埋め込みの生成が含まれます。インデックスにはデータに関する様々なメタデータも保存できます。
  • Embeddings: LLMはデータの数値表現である埋め込みを生成します。LlamaIndexはクエリを埋め込みに変換し、ベクトルストアはクエリの埋め込みに数値的に類似したデータを見つけます。

Querying Stage

  • Retrievers: リトリーバーは、あなたが入力したクエリ(質問や検索キーワード)に基づいて、データベースから関連する情報を見つけ出す役割を果たします。どの情報が関連しているか、そしてそれをどれだけ効率的に見つけ出せるかは、リトリーバーの戦略に依存します。
  • Routers: ルーターは、どのリトリーバーを使って知識ベースから情報を探すかを決めます。RouterRetrieverクラスは、複数あるリトリーバーの中から最適なものを選択するために、セレクター(選択ツール)を使用します。
  • Node Postprocessors: Node Postprocessorsは、リトリーバーが見つけた情報(ノードと呼ばれる)に対して、加工やフィルタリング、再ランキング(優先順位付け)などの処理を行います。これにより、より役立つ情報を提供できます。
  • Response Synthesizers: レスポンスシンセサイザーは、ユーザーの質問とリトリーバーが見つけたテキストを元に、言語学習モデル(LLM)を使って答えを作り出します。

Putting it all together

データを基にしたLLM(大規模言語モデル)アプリケーションは多岐にわたりますが、主に以下の3つのカテゴリーに分けられます。

  • Query Engines
    クエリエンジンは、データに関する質問をするための一連のプロセス(パイプライン)です。自然言語での質問を受け取り、それに対する回答と、LLMに渡される参照コンテキスト(関連情報)を返します。この仕組みにより、ユーザーはデータベースや文書集合に対して直接質問ができ、その質問に対する適切な回答を得ることができます。

  • Chat Engines
    チャットエンジンは、データとの対話を実現するための一連のプロセスです。単一の質問と回答ではなく、複数回のやり取り(バックアンドフォース)が可能です。これにより、ユーザーはデータとのより自然な会話形式のインタラクションを行うことができ、より深い洞察や情報を引き出すことが可能になります。

  • Agents
    エージェントは、LLMによって駆動される自動的な意思決定者で、一連のツールを通じて世界と相互作用します。エージェントは、与えられたタスクを完了するために任意の数のステップを踏むことができ、事前に定められたステップに従うのではなく、動的に最適な行動コースを決定します。これにより、より複雑なタスクに対応する柔軟性が向上します。

Customization Tutorial

このチュートリアルでは、LlamaIndexを使ったアプリケーションのカスタマイズ方法について説明します。まず、基本的なコードから始め、一般的なカスタマイズ例を見ていきます。

初期のコード例

from llama_index import VectorStoreIndex, SimpleDirectoryReader

documents = SimpleDirectoryReader("data").load_data()
index = VectorStoreIndex.from_documents(documents)
query_engine = index.as_query_engine()
response = query_engine.query("What did the author do growing up?")
print(response)

このコードは、データを読み込み、それをインデックス化し、クエリエンジンを使用して質問に答える基本的な流れを示しています。

ドキュメントを小さなチャンクに分割する

この部分では、大きなドキュメントを小さな部分(チャンク)に分割する方法を示しています。これは、大量のデータを扱いやすくするために行います。ServiceContextを使って、ドキュメントの分割サイズを設定し、その設定を使用してインデックスを作成しています。

from llama_index import ServiceContext

service_context = ServiceContext.from_defaults(chunk_size=1000)

ServiceContextは、LlamaIndexパイプライン全体で使用されるサービスと設定のバンドルです。ここでは、ドキュメントを小さなチャンク(部分)に分割する方法を示しています。

from llama_index import VectorStoreIndex, SimpleDirectoryReader

documents = SimpleDirectoryReader("data").load_data()
index = VectorStoreIndex.from_documents(
    documents, service_context=service_context
)
query_engine = index.as_query_engine()
response = query_engine.query("What did the author do growing up?")
print(response)
補足:
  • ServiceContext: ServiceContextは、LlamaIndexパイプラインにおいて、さまざまなサービスや設定を一つにまとめたものです。これを利用することで、LlamaIndexがデータをどのように処理するかを細かく設定することができます。ここでの「チャンクに分割する」というのは、大きなドキュメントやデータを小さな部分に分けることを指します。

  • チャンク(Chunk): これは、大きなテキストやデータのセットをより小さい単位に分割することを意味します。たとえば、非常に長い文書をより扱いやすい小さなセクションに分けることができます。これにより、データの処理が効率的になり、特定のクエリやタスクに必要なデータの部分だけに焦点を当てることができます。

  • サービスと設定のバンドル: ServiceContextは、データの処理方法、データのチャンクサイズ、インデックス作成の方法など、様々な設定を包含します。これにより、アプリケーション全体で一貫したデータ処理ルールを設定することが可能になります。

  • ドキュメントのチャンク化: 例えば、ある長い文書があるとします。ServiceContextを使用して、この文書を1000単語ごとのチャンクに分割し、各チャンクを別々にインデックス化して処理することができます。これにより、大きな文書全体を一度に扱うのではなく、必要な部分だけを高速に検索・分析することができるようになります。このように、ServiceContextを使用することで、LlamaIndexにおけるデータ処理のカスタマイズが可能になり、より効率的で柔軟なアプリケーションの構築が実現します。

異なるベクトルストアを使用する

ここでは、デフォルトのベクトルストアではなく、ChromaVectorStoreという別のベクトルストアを使用しています。これにより、データの保存や取得の方法をカスタマイズできます。StorageContextを通じて、この新しいベクトルストアをインデックス作成に適用しています。

import chromadb
from llama_index.vector_stores import ChromaVectorStore
from llama_index import StorageContext

chroma_client = chromadb.PersistentClient()
chroma_collection = chroma_client.create_collection("quickstart")
vector_store = ChromaVectorStore(chroma_collection=chroma_collection)
storage_context = StorageContext.from_defaults(vector_store=vector_store)

StorageContextは、ドキュメント、埋め込み、インデックスが保存されるストレージバックエンドを定義します。ここでは、異なるベクトルストア(ここではChromaVectorStore)を使用する例を示しています。

from llama_index import VectorStoreIndex, SimpleDirectoryReader

documents = SimpleDirectoryReader("data").load_data()
index = VectorStoreIndex.from_documents(
    documents, storage_context=storage_context
)
query_engine = index.as_query_engine()
response = query_engine.query("What did the author do growing up?")
print(response)

より多くのコンテキストを取得する

このコードは、クエリの際に、デフォルトの上位2つのドキュメントではなく、類似度が高い上位5つのドキュメントを取得するように設定しています。これにより、クエリの応答に関連するコンテキストをより多く得ることができます。

from llama_index import VectorStoreIndex, SimpleDirectoryReader

documents = SimpleDirectoryReader("data").load_data()
index = VectorStoreIndex.from_documents(documents)
query_engine = index.as_query_engine(similarity_top_k=5)
response = query_engine.query("What did the author do growing up?")
print(response)

異なるLLMを使用する

ここでは、標準のLLMではなく、PaLMという別のLLMを使用しています。ServiceContextを通じて、このLLMをクエリエンジンに適用しています。

from llama_index import ServiceContext
from llama_index.llms import PaLM

service_context = ServiceContext.from_defaults(llm=PaLM())

LLMのカスタマイズはこちら

from llama_index import VectorStoreIndex, SimpleDirectoryReader

documents = SimpleDirectoryReader("data").load_data()
index = VectorStoreIndex.from_documents(documents)
query_engine = index.as_query_engine(service_context=service_context)
response = query_engine.query("What did the author do growing up?")
print(response)

異なる応答モードを使用する

このセクションでは、通常の応答モードの代わりに「tree_summarize」というモードを使用しています。これにより、応答の形式や内容を変更することができます。

from llama_index import VectorStoreIndex, SimpleDirectoryReader

documents = SimpleDirectoryReader("data").load_data()
index = VectorStoreIndex.from_documents(documents)
query_engine = index.as_query_engine(response_mode="tree_summarize")
response = query_engine.query("What did the author do growing up?")
print(response)
Response Modes examples: 詳細はこちら

Refine: 各テキストチャンクを順番に使用して回答を洗練させます。チャンクが大きすぎる場合は分割して、それぞれを別々に処理。詳細な回答が必要な場合。

Compact: すべてのチャンクを事前に連結し、一度に処理。RefineよりLLMの呼び出しが少ない。効率的な処理を優先する場合。

Tree Summarize: チャンクを連結して要約し、必要に応じて再帰的に処理。最終的に1つの回答を生成。要約が求められる場合。

応答をストリーミングで返す

ここでは、応答を一度に返すのではなく、ストリーミング形式で段階的に返す方法を示しています。これは、大量のデータや長い応答を扱う際に有効です。

from llama_index import VectorStoreIndex, SimpleDirectoryReader

documents = SimpleDirectoryReader("data").load_data()
index = VectorStoreIndex.from_documents(documents)
query_engine = index.as_query_engine(streaming=True)
response = query_engine.query("What did the author do growing up?")
response.print_response_stream()

Q&Aではなくチャットボットを作る

最後の例では、単一の質問と回答の形式ではなく、チャット形式のインタラクションを行うためのチャットエンジンを使用しています。これにより、ユーザーはデータとの対話をより自然な形で行うことができます。詳細

from llama_index import VectorStoreIndex, SimpleDirectoryReader

documents = SimpleDirectoryReader("data").load_data()
index = VectorStoreIndex.from_documents(documents)
query_engine = index.as_chat_engine()
response = query_engine.chat("What did the author do growing up?")
print(response)

response = query_engine.chat("Oh interesting, tell me more.")
print(response)

Next Steps

  • LlamaIndexのほぼすべてを網羅する詳細なウォークスルーを希望する場合は、「Understanding LlamaIndex」を参照してください。
  • 特定のモジュールについてより深く理解したい場合は、左側のナビゲーションにあるモジュールガイドをチェックしてください。

これらの例は、LlamaIndexを用いたLLMアプリケーションのカスタマイズのための基本的なガイドラインを提供します。ユーザーの特定の要件に応じて、これらの設定を調整することができます。

Discover LlamaIndex Video Series

https://docs.llamaindex.ai/en/stable/getting_started/discover_llamaindex.html

「Discover LlamaIndex」シリーズは、ビデオを通してLlamaIndexの使い方を学ぶためのものです。このシリーズでは、LlamaIndexの基本的な概念や使い方に焦点を当てています。
https://docs.llamaindex.ai/en/stable/understanding/understanding.html

Bottoms-Up Development (Llama Docs Bot)

このシリーズでは、ドキュメントチャットボットを一から構築する方法を紹介します。LLMやデータオブジェクトを独立したモジュールとして使用し、徐々に高度な概念(インデックス化、高度なリトリーバーやリランカー)を追加していく「ボトムアップ」方式での開発を示しています。

  • Full Repo
    https://github.com/run-llama/llama_docs_bot
  • [Part 1] LLMs and Prompts: LLMとプロンプトを使った基本的な構築方法。
  • [Part 2] Documents and Metadata: ドキュメントとメタデータの管理。
  • [Part 3] Evaluation: システムの評価方法。
  • [Part 4] Embeddings: データの埋め込み方法。
  • [Part 5] Retrievers and Postprocessors: リトリーバーとポストプロセッサーの使用方法。

SubQuestionQueryEngine + 10K Analysis

金融文書に適用されるSubQuestionQueryEngineについて解説します。複雑なクエリを複数のサブクエリに分解する方法を示します。

Discord Document Management

Discordなど、常に更新されるソースからのドキュメント管理方法をカバーします。ドキュメントの重複を避け、埋め込みトークンを節約する方法について説明します。

SQLと意味検索を組み合わせた統一されたクエリインターフェースの構築方法を解説します。

これらのビデオシリーズは、LlamaIndexの使い方を学ぶための実践的なリソースです。ビデオとノートブックを組み合わせることで、理論と実践の両方の側面からLlamaIndexを理解し、活用することができます。

Discussion