Llama-Indexを用いてGPTのプロンプトエンジニアリングをする
大規模言語モデルを使用するプロダクトに携われることになり学習を深めようと思い、Llama-Indexを用いてGPTのプロンプトエンジニアリングをし論文やWebサイトに対してのクエリができるアプリケーションを作成しました。
構成
- Streamlit: WebアプリケーションのUI作成
- Llama-Index: 文章のエンべディング(ベクトル化)、インデックス作成
- OpenAI GPT-4: クエリに対する文章の生成
Llama-Indexについて
- 概要
Llama-IndexとはChatGPTのプロンプトエンジニアリングをするためのライブラリであり、様々なローダーを使用しファイルやWebサイト、Slackなどのアプリケーションなどから文章を取り出しその文章に対して質問することができます。
また文章の理解にはエンべディングを行っています。エンベディングは、テキストの単語をベクトルに変換します。ベクトルは、テキストの意味を数値で表し、これにより、LlamaIndexは、テキストの意味を理解し、関連するテキストを検索することができます。ChatGPTと組み合わせて使用することで、ユーザーのクエリに対するより適切で関連性の高い応答を生成することができます。
簡単にまとめると以下のフローで実行されます。- ローダーで文章を取得
- 文章をエンべディング(ベクトル化)
- クエリに対して類似度の高い文章を検索
- 検索された文章をChatGPTに入力し応答を生成
-
Roaderについて
様々な種類のRoaderを使うことで多くの種類のファイルやアプリケーションから文章を取得することができます。
LlamaHub に使用できるローダーの一覧とコードがあります。 -
インデックスの作成
Llama-Indexではクエリに対する文章を検索する際にインデックスという概念が存在します。
インデックスの構成方法としてテキストを読み込んだ後チャンクという特定の文字数で文章を分割しそれぞれをNodeと言われる構造体に格納します。その後、Nodeをエンべディングし、ベクトル化したものをインデックスとして保存します。 - インデックスの種類
インデックスにも種類がありより文章の精度を上げるためにはクエリに対して適切なインデックスを選択する必要があります。
詳細はこちら を参照してください。
- List Index
ListIndexではノードのリストを作成し、クエリに対して先頭から処理していき出力を最後に合成する。
また上位k個のノードをに対して実行する方法や、ノードに対してキーワードフィルターを適用することもできる。 - Vector Store Index
VectorStoreIndexではノードと文章のベクトルを保持し、クエリに対してベクトルの類似度を計算し、類似度の高い上位k個のものを合成し出力する。
※よく使われる - Tree Index
TreeIndexではノードをツリー構造にし、クエリに対してツリーを探索し、類似度の高い上位k個のものを合成し出力する。使用する子ノードの数を指定できる。 - Keyword Table Index
KeywordTableIndexではノードとキーワードのテーブルを作成し、クエリのキーワードを使用しノードを選択し最終的に合成して出力する。
- List Index
Llama-Indexの実装
今回はPDFとWebサイトから文章を取得し、その文章に対してクエリを行うアプリケーションを作成しました。
インデックスにはVectorStoreIndexを使用し、クエリに対して類似度の高い文章を検索しChatGPTに入力し応答を生成します。
import os
import streamlit as st
from pathlib import Path
import tempfile
from llama_index import download_loader
from llama_index import GPTVectorStoreIndex, LLMPredictor
from langchain.chat_models import ChatOpenAI
os.environ["OPENAI_API_KEY"] = os.getenv("OPENAI_API_KEY") # APIキーを環境変数から取得
llm = ChatOpenAI(model_name="gpt-4") # GPT-4を使用
llm_predictor = LLMPredictor(llm=llm)
if __name__ == "__main__":
st.title("Llama Index")
select = st.selectbox("",("URL", "PDF")) # URLかPDFを選択
if select == "URL":
URL = [st.text_input("URLを入力してください")] # URLを入力
text = st.text_input("質問を入力してください") # 質問を入力
submit = st.button("送信")
if submit and len(URL) > 0 and len(text) > 0:
SimpleWebPageReader = download_loader("SimpleWebPageReader") # SimpleWebPageReaderをダウンロード
loader = SimpleWebPageReader()
documents = loader.load_data(urls=URL) # SimpleWebPageReaderを使用してURLからデータを取得
index = GPTVectorStoreIndex.from_documents(documents=documents, text=text, llm_predictor=llm_predictor) # Llama Indexを使用今回はVectorStoreIndexを使用
query_engine = index.as_query_engine() # クエリエンジンを作成
st.write(query_engine.query(text).response) # クエリに対する回答を表示
if select == "PDF":
file = st.file_uploader("PDFをアップロードしてください", type="pdf") # PDFをアップロード
text = st.text_input("質問を入力してください") # 質問を入力
submit = st.button("送信")
if submit and file is not None and len(text) > 0:
with tempfile.NamedTemporaryFile(delete=False) as tmp_file: # PDFを一時ファイルに保存
fp = Path(tmp_file.name)
fp.write_bytes(file.read())
CJKPDFReader = download_loader("CJKPDFReader") # CJKPDFReaderをダウンロード
loader = CJKPDFReader()
documents = loader.load_data(file=tmp_file.name) # CJKPDFReaderを使用してPDFからデータを取得
index = GPTVectorStoreIndex.from_documents(documents=documents, text=text, llm_predictor=llm_predictor) # Llama Indexを使用今回はVectorStoreIndexを使用
query_engine = index.as_query_engine() # クエリエンジンを作成
st.write(query_engine.query(text).response) # クエリに対する回答を表示
streamlitを使用して簡単なUIを作成し、URLかPDFを選択し、質問を入力すると回答が表示されます。
実行結果
- URLに対する質問と回答
OpenMVというMicroPythonを使用したカメラモジュールのドキュメントに対して、RGB画像からグレースケール画像に変換する関数について質問してみました。
正しい回答が返ってきていることが確認できます。 - PDFに対する質問と回答
こちらは言語モデルのTransformerの論文を引用しTransformerについてまとめてもらいました。
まとめ
今回はLlama Indexを使用して簡単な質問応答システムを作成しました。
Llama Indexは簡単に使用できてかなり便利なツールであるためこれからもお世話になることが多そうです。今後はLangChainなども併用していき、対話型にできるようにできたらなと思います。