見出し画像

LlamaIndex v0.10 クイックスタートガイド - Python版

Python版の「LlamaIndex」のクイックスタートガイドをまとめました。

・llama-index 0.10.4


1. LlamaIndex

「LlamaIndex」は、プライベートやドメイン固有の知識を必要とする専門知識を必要とする質問応答チャットボットを簡単に作成できるライブラリです。

v0.10 では大規模アップデートがありました。

・llama-index-coreを導入し、Integrationを個別パッケージに分離
・ServiceContextは非推奨
・全IntegrationをLlamaHubで管理

2. LlamaIndexの5つのステージ

「LlamaIndex」には、5つのステージがあります。

2-1. Loading

データソース (テキストファイル、PDF、Webサイト、データベース、APIなど) からデータを読み込みます。
「Loading」の主要コンポーネントは、次のとおりです。

・Document : データソースのコンテナ
・Node : Documentを分割したもの。チャンクとメタデータが含まれる
・Connector : データソースからDocumentおよびNodeを取り込むモジュール

2-2. Indexing

データのクエリを可能にするデータ構造「インデックス」を作成します。
「Indexing」の主要コンポーネントは、次のとおりです。

・Index : データのクエリを可能にするデータ構造
・Embedding : 関連データを見つけるためのベクトル表現に変換するモデル

2-3. Storing

インデックスを作成した後、インデックスと他のメタデータを保存することで、再作成する必要がなくなります。

2-4. Querying

インデックスから関連データをクエリします。
「Querying」の主要コンポーネントは、次のとおりです。

・Retriever : クエリ時にIndexから関連データを効率的に取得する方法を定義
・Node Postprocessor : 取得したノードセットを受け取り、それらに変換、フィルタリング、リランキングを適用
・Response Synthesizer : ユーザークエリと取得したテキストチャンクを使用してレスポンスを生成

2-3. Evaluation

クエリに対するレスポンスがどの程度正確、忠実、迅速であるかを客観的に測定します。

3. ドキュメントの準備

今回は、マンガペディアの「ぼっち・ざ・ろっく!」のドキュメントを用意しました。

・bocchi.txt

4. 質問応答

Google Colabでの質問応答の実行手順は、次のとおりです。

(1) パッケージのインストール。

# パッケージのインストール
!pip install llama-index==0.10.4

llama-index」はスタートバンドルで、以下のパッケージをインストールしています。

・llama-index-core
・llama-index-legacy # temporarily included
・llama-index-llms-openai
・llama-index-embeddings-openai
・llama-index-program-openai
・llama-index-question-gen-openai
・llama-index-agent-openai
・llama-index-readers-file
・llama-index-multi-modal-llms-openai

(2) 環境変数の準備。
左端の鍵アイコンで「OPENAI_API_KEY」を設定してからセルを実行してください。

# 環境変数の準備 (左端の鍵アイコンでOPENAI_API_KEYを設定)
import os
from google.colab import userdata
os.environ["OPENAI_API_KEY"] = userdata.get("OPENAI_API_KEY")

(3) ログレベルの設定。

import logging
import sys

# ログレベルの設定
logging.basicConfig(stream=sys.stdout, level=logging.DEBUG, force=True)

・DEBUG : デバッグ情報の出力
・INFO : 情報の出力 (想定通りの事象の発生)
・WARNING : 警告の出力 (想定外の事象の発生)
・ERROR : エラーの出力 (実行の続行不能)

(4) Colabにdataフォルダを作成してドキュメントを配置。
左端のフォルダアイコンでファイル一覧を表示し、右クリック「新しいフォルダ」でdataフォルダを作成し、ドキュメントをドラッグ&ドロップします。

(5) ドキュメントの読み込み。

from llama_index.core import SimpleDirectoryReader

# ドキュメントの読み込み
documents = SimpleDirectoryReader("data").load_data()

(6) インデックスの作成。
「インデックス」は、データのクエリを可能にするデータ構造を保持するコンポーネントです。ドキュメントをチャンクに分割し、チャンク毎に埋め込みに変換して保持します。
チャンクは類似検索の対象となるデータ単位になります。

from llama_index.core import VectorStoreIndex

# インデックスの作成
index = VectorStoreIndex.from_documents(documents)
DEBUG:llama_index.node_parser.node_utils:> Adding chunk: 結束バンド 後藤ひとりは友達を作れない陰キャでいつも一人で過ごしていたが、中学時代にテレビのイ...
DEBUG:llama_index.node_parser.node_utils:> Adding chunk: 文化祭ライブ 夏休みに入り、後藤ひとりは知り合いも増えていたが、自分から遊びに誘うことができず...
DEBUG:llama_index.node_parser.node_utils:> Adding chunk: ひねくれ者なヨヨコは、結束バンドをライバル視しながらも、彼女たちにアドバイスを送り、ファンたち...
    :

(7) クエリエンジンの作成。
「クエリエンジン」は、ユーザー入力(クエリ)と関連する情報をインデックスから取得し、それをもとに応答を生成するモジュールです。

# クエリエンジンの作成
query_engine = index.as_query_engine()

(8) 質問応答。

# 質問応答
print(query_engine.query("ぼっちちゃんの得意な楽器は?"))
後藤ひとりの得意な楽器はギターです。


ログを確認すると、以下のコンテキストを取得していることがわかります。

[Similarity score:             0.832151] 自分には何の取り柄もないのを痛感していたため、中学の頃に暗い性格の人間がバンドをやって人気者になったインタビューを読んで、ギターを始める。毎日練習したお陰でギターの腕前はプロ級になったが、結局、...
[Similarity score:             0.829139] 変わり者で一人でいるのが好きだが、後藤ひとりと違って特にコミュ障というわけではない。音楽に関しては独自の価値観を持っており、流行(はや)りに流されるのを嫌い、バンドの個性を重視している。そのため...

OpenAI APIの通信内容も確認できます。Unicodeエスケープシーケンスを日本語に戻してます。

Context information is below.
---------------------
自分には何の取り柄もないのを痛感していたため、中学の頃に暗い性格の人間がバンドをやって人気者になったインタビューを読んで、ギターを始める。毎日練習したお陰でギターの腕前はプロ級になったが、結局、...

変わり者で一人でいるのが好きだが、後藤ひとりと違って特にコミュ障というわけではない。音楽に関しては独自の価値観を持っており、流行(はや)りに流されるのを嫌い、バンドの個性を重視している。そのため...
---------------------
Given the context information and not prior knowledge, answer the question. If the answer is not in the context, inform the user that you can't answer the question.
Question: ぼっちちゃんの得意な楽器は?
Answer: 

5. インデックスの保存

作成したインデックスは保存しておくと、次回に再作成する必要がなくなります。

(1) インデックスの保存。

# インデックスの保存
index.storage_context.persist()

(2) インデックスの読み込み。

from llama_index.core import StorageContext, load_index_from_storage

# インデックスの読み込み
storage_context = StorageContext.from_defaults(persist_dir="./storage")
index = load_index_from_storage(storage_context)

6. カスタマイズ

6-1. LLMの設定

from llama_index.core import Settings
from llama_index.llms.openai import OpenAI

# LLMの設定
Settings.llm = OpenAI(
    model="gpt-3.5-turbo", 
    temperature=0.1
)

LLMの主なパラメータは、次のとおりです。

・model : モデル名
・temperature : ランダムさ
・max_tokens : 生成するトークンの最大数

6-2. 埋め込みの設定

from llama_index.core import Settings
from llama_index.embeddings.openai import OpenAIEmbedding

# 埋め込みの設定
Settings.embed_model = OpenAIEmbedding(
    model="text-embedding-3-small", 
)

Embeddingの主なパラメータは、次のとおりです。

・model : モデル名
・embed_batch_size : バッチサイズ (速度とメモリのトレードオフ)
・max_length : 最大長

6-3. テキストスプリッターの設定

from llama_index.core import Settings
from llama_index.core.node_parser import SentenceSplitter
import tiktoken

# テキストスプリッターの設定
Settings.text_splitter = SentenceSplitter(
    separator=" ",
    chunk_size=1024,
    chunk_overlap=20,
    paragraph_separator="\n\n\n",
    secondary_chunking_regex="[^,.;。]+[,.;。]?",
    tokenizer=tiktoken.encoding_for_model("gpt-3.5-turbo").encode
)

SentenceSplitterのパラメータは、次のとおりです。

・chunk_size : 各チャンクのトークン数 (default:1024)
・chunk_overlap : 分割時の各チャンクのトークンの重なり (default:20)
・separator : 単語の区切り文字 (default:" ")
・paragraph_separator : 段落間の区切り文字 (default:"\n\n\n")
・secondary_chunking_regex : 文の区切り文字の正規表現 (default:"[^,.;。]+[,.;。]?")
・chunking_tokenizer_fn : テキストを文章に分割する関数 (default:nltk.sent_tokenize)
・callback_manager : コールバックマネージャ
・tokenizer : トークナイザー

チャンクサイズとチャンクオーバーラップは、テキストスプリッターを設定せず、個別に設定することも可能です。

# チャンクサイズとチャンクオーバーラップの設定
Settings.chunk_size = 512
Settings.chunk_overlap = 20

6-4. トークナイザーの設定

from llama_index.core import Settings
import tiktoken

# トークナイザーの設定
Settings.tokenizer = tiktoken.encoding_for_model("gpt-3.5-turbo").encode

6-5. コールバックの設定

from llama_index.core.callbacks import TokenCountingHandler, CallbackManager
from llama_index.core import Settings

# コールバックの設定
token_counter = TokenCountingHandler()
Settings.callback_manager = CallbackManager([token_counter])

6-6. プロンプトヘルパーの設定

通常、LLMの属性を使用して自動的に設定されますが、特別な場合は上書きできます。

from llama_index.core import Settings

# プロンプトヘルパーの設定
Settings.context_window = 4096
Settings.num_output = 256

6-7. クエリエンジンの設定

ベクトル検索で取得するチャンク数は、クエリエンジンのsimilarity_top_kで指定します。

# クエリエンジンの準備
query_engine = index.as_query_engine(
    similarity_top_k=10, 
)

6-8. Rerankerの設定

「Reranker」は、クエリエンジンのnode_postprocessorsで設定します。

# パッケージのインストール
!pip install llama-index-postprocessor-flag-embedding-reranker
!pip install FlagEmbedding
from llama_index.postprocessor.flag_embedding_reranker import FlagEmbeddingReranker

# Rerankerの準備
rerank = FlagEmbeddingReranker(
    model="BAAI/bge-reranker-v2-m3", 
    use_fp16=True, 
    top_n=5
)
# クエリエンジンの準備
query_engine = index.as_query_engine(
    similarity_top_k=10, 
    node_postprocessors=[rerank]
)

6-9. トランスフォーメーションの設定

from llama_index.core.node_parser import SentenceSplitter
from llama_index.core import Settings

# トランスフォーメーションの設定
Settings.transformations = [SentenceSplitter(chunk_size=1024)]

7. LlamaHub

「LLM」「埋め込み」「ローダー」「ツール」「ベクトルストア」など、「LlamaIndex」の「Integration」はすべて「LlamaHub」で管理します。


ただし「LlamaHub」への移行はまだ作業中です。 数週間以内にサイトを更新します。それまでの間は、Notionパッケージレジストリを利用します。

今回は、「llama-index-readers-web」の「SimpleWebPageReader」を使って、Webページの情報に基づいて「LlamaIndex」に質問してみます。

(1) パッケージのインストール。

# パッケージのインストール
!pip install llama-index-readers-web

(2) 「SimpleWebPageReader」でURLからドキュメントを生成。
Wikipediaのぼっち・ざ・ろっくのページを読み込みました。

from llama_index.readers.web import SimpleWebPageReader

# リーダーの準備
reader = SimpleWebPageReader(html_to_text=True)
documents = reader.load_data(urls=["https://ja.wikipedia.org/wiki/%E3%81%BC%E3%81%A3%E3%81%A1%E3%83%BB%E3%81%96%E3%83%BB%E3%82%8D%E3%81%A3%E3%81%8F!"])

(3) 質問応答。 

# 質問応答
index = VectorStoreIndex.from_documents(documents)
query_engine = index.as_query_engine()
response = query_engine.query("結束バンドとは?")
print(response)

結束バンドは、ひとりが所属しているバンドの名前です。ひとりは高校生の時に結束バンドのギタリストとして加入しました。結束バンドは、ひとりを含むメンバーたちが一緒に音楽活動を行っているバンドです。ひとりはバンド活動を通じて成長し、ライブハウスや学校の文化祭などで演奏を披露しています。



この記事が気に入ったらサポートをしてみませんか?