ChatGPTプログラミング【LangChain入門】(Indexes編)
はじめに
ChatGPTをはじめとするLLM界隈で話題のLangChainを勉強しています。
機能がたくさんあるので、最初公式ガイドを見るだけでは、概念がわかりにくいですよね。
読むだけでは頭に入らないので公式ガイドのサンプルを実行しながら、公式ガイドの情報をまとめてみました。
今回はLangChainの機能の1つである**「Indexes」**編です。
LangChainとは?
LangChainは、言語モデルを用いたアプリケーション開発を容易にするためのツールです。様々な言語モデルを統合し、それらを用いた複雑なタスクを実行することが可能になります。
言語モデルは、人間の言葉を理解し生成する能力を持つAIです。これらのモデルは、文章の生成、質問応答、意味の理解、文章の要約など、多岐にわたるタスクをこなすことができます。
LangChainは、このような言語モデルの能力を最大限に引き出すためのフレームワークを提供します。それぞれ特定の機能を持つモジュールから構成され、それらを組み合わせることで高度なアプリケーションを構築することが可能です。
以下にLangChainの主要なモジュールを示します:
Models: さまざまな種類のモデルとモデルの統合をサポートします。このモジュールはLangChainのバックボーンとなり、フレームワークに多様な言語モデルの能力を与えます。
Prompts: プロンプトの管理と最適化を行います。プロンプトは、言語モデルに指示を与えるためのテキストです。
Memory: 言語モデルの「記憶」を管理します。これにより、一連のタスクを通じてコンテキストを保持することが可能になります。
Indexes: 自身のテキストデータと言語モデルを組み合わせるための方法を提供します。これにより、モデルの理解力と生成能力が強化されます。
Chains: 複数の言語モデルやユーティリティを連鎖的に呼び出す機能を提供します。
Agents: 言語モデルが独自に行動を決定し、その行動を実行し、結果を観察し、プロセスを繰り返す機能を提供します。
Callbacks: チェインまたはエージェント内部の操作を監視し分析するための追加の監視レイヤーを提供します。
LangChainはまた、多数の統合機能を提供しています。これにより、さまざまな言語モデルプロバイダ、ツールプロバイダ、企業とシームレスに連携することが可能になります。
Indexesとは?
Indexesとは、 LLM がドキュメントと最適に対話できるようにドキュメントを構造化する方法を指します。
インデックスが利用される最も一般的な方法は、「検索」です。
これは、ユーザーのクエリを取得し、最も関連性の高いドキュメントを返すことを指します。この区別を行う理由は、
(1) インデックスは検索以外にも使用できること
(2) 検索では関連ドキュメントを見つけるためにインデックス以外の他のロジックを使用できること
です。
したがって、 Retrieverインターフェイスという概念があります。これは、ほとんどのチェーンが動作するインターフェイスです。
インデックスと取得について話すときは、ほとんどの場合、非構造化データ (テキスト ドキュメントなど) のインデックス作成と取得について話します。
構造化データ (SQL テーブルなど) または API と対話する場合は、関連する機能へのリンクについて、対応するユースケースのセクションを参照してください。
Indexの種類
Document Loaders: さまざまなソースからドキュメントをロードする方法。
Text Splitters:テキスト スプリッターの概要とさまざまなタイプ。
VectorStores : Vector Storeの概要とさまざまなタイプ 。
Retrievers:レトリバーの概要とさまざまなタイプ。
Getting Started
LangChain は主に、Retriever として使用することを目的としたインデックス
の構築に重点を置いています。
言い換えれば、主に検索ツールとして使うための「索引」(情報を見つけやすくするための目次のようなもの)を作るイメージと考えるとわかりやすいかもしれません。
主な種類のRetrieverは、Vectorstore Retrieverです。
Vectorstore Retriever とは何かを理解するには、Vectorstore とは何かを理解することが重要です。
それでは、それを見てみましょう。
デフォルトでは、LangChain は埋め込みのインデックス付けと検索を行うベクトルストアとしてChromaを使用します。
ChromaはOSSのベクトルストアです。
このチュートリアルを進めるには、まず Chromadbをインストールします。
Chromadbのインストール
pip install chromadb
今回の例では、文書に対する質問応答を示します。
これを開始用の例として選択したのは、さまざまな要素 (テキスト スプリッター、埋め込み、ベクトルストア) をうまく組み合わせており、それらをチェーンで使用する方法も示しているためです。
文書に対する質問応答は、次の 4 つのステップで構成されます。
- インデックスを作成する
- そのインデックスからRetrieverを作成する
- 質問応答チェーンを作成する
- 質問をする
各ステップには複数のサブステップと潜在的な構成があります。
このノートでは主に (1) に焦点を当てます。
まず簡単に1行で実現できる方法を紹介します。
その後、実際に中で何が実行されているかを詳しく説明します。
最初に、いくつかの一般的なクラスをインポートしましょう。
from langchain.chains import RetrievalQA
from langchain.llms import OpenAI
次に、使用するドキュメント ローダーを指定しましょう。
ここでは、テキストファイルをロードしますので、「TextLoader()」を利用します。
state_of_the_union.txtファイルは以下からダウンロードできます。
from langchain.document_loaders import TextLoader
loader = TextLoader('./state_of_the_union.txt', encoding='utf8')
state_of_the_union.txtファイルをダウンロードしたら、プログラムと同じディレクトリに置いておきます。
One Line Indexの作成
簡単に実行したい場合には、 VectorstoreIndexCreatorを使用できます。
VectorstoreIndexCreatorは、Embedding、ベクトルストア等、複数処理のラッパーになっています。
個別にEmbedding等を実行していってもよいのですが、シンプルにまとめて実行したい場合に「VectorstoreIndexCreator」が役立ちます。
from langchain.indexes import VectorstoreIndexCreator
index = VectorstoreIndexCreator().from_loaders([loader])
インデックスが作成されたので、それを使用してデータについて質問することができます。実際には内部でいくつかのステップも実行していることに注意してください。これについては、このガイドで後ほど説明します。
クエリの実行
query = "What did the president say about Ketanji Brown Jackson"
index.query(query)
実行結果
" The president said that Ketanji Brown Jackson is one of the nation's top legal minds, a former top litigator in private practice, a former federal public defender, and from a family of public school educators and police officers. He also said that she is a consensus builder and has received a broad range of support from the Fraternal Order of Police to former judges appointed by Democrats and Republicans."
ソース元の表示方法
query = "What did the president say about Ketanji Brown Jackson"
index.query_with_sources(query)
実行結果
{'question': 'What did the president say about Ketanji Brown Jackson',
'answer': " The president said that he nominated Circuit Court of Appeals Judge Ketanji Brown Jackson, one of the nation's top legal minds, to continue Justice Breyer's legacy of excellence, and that she has received a broad range of support from the Fraternal Order of Police to former judges appointed by Democrats and Republicans.\n",
'sources': './state_of_the_union.txt'}
VectorstoreIndexCreatorから返却されるものはVectorStoreIndexWrapperというもので、これは使いやすい検索機能と、情報源を伴う検索機能を提供します。
もしベクトルストアに直接アクセスしたいだけであれば、それも可能です。
コード全体
from langchain.schema import Document
from langchain.chains import RetrievalQA
from langchain.llms import OpenAI
from langchain.document_loaders import TextLoader
loader = TextLoader('./state_of_the_union.txt', encoding='utf8')
from langchain.indexes import VectorstoreIndexCreator
index = VectorstoreIndexCreator().from_loaders([loader])
query = "What did the president say about Ketanji Brown Jackson"
res = index.query(query)
print(res)
res = index.query_with_sources(query)
print(res)
実行結果
Using embedded DuckDB without persistence: data will be transient
The president said that Ketanji Brown Jackson is one of the nation's top legal minds, a former top litigator in private practice, a former federal public defender, and from a family of public school educators and police officers. He also said that she is a consensus builder and has received a broad range of support from the Fraternal Order of Police to former judges appointed by Democrats and Republicans.
{'question': 'What did the president say about Ketanji Brown Jackson', 'answer': " The president said that he nominated Circuit Court of Appeals Judge Ketanji Brown Jackson, one of the nation's top legal minds, to continue Justice Breyer's legacy of excellence, and that she has received a broad range of support from the Fraternal Order of Police to former judges appointed by Democrats and Republicans.\n", 'sources': './state_of_the_union.txt'}
ここまでのウォークスルー
さて、前述のプログラムは、実際に何が動いているのでしょうか?
このインデックスはどのように作成されるのでしょうか?
この中にはたくさんの魔法が隠されています。
VectorstoreIndexCreatorは何をしているのでしょうか?
ドキュメントがロードされた後は、次の 3 つの主なステップが実行されます。
- ドキュメントをチャンクに分割する
- 各ドキュメントの埋め込みの作成
- ドキュメントと埋め込みをベクトルストアに保存する
コードでこれを見てみましょう
documents = loader.load()
次に、ドキュメントをいくつかのチャンクに分割します。
from langchain.text_splitter import CharacterTextSplitter
text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=0)
texts = text_splitter.split_documents(documents)
次に、使用する埋め込みを選択します。
from langchain.embeddings import OpenAIEmbeddings
embeddings = OpenAIEmbeddings()
次に、インデックスとして使用するベクトルストアを作成します。
from langchain.vectorstores import Chroma
db = Chroma.from_documents(texts, embeddings)
これでインデックスが作成されます。次に、このインデックスを取得インターフェイスで公開します。
retriever = db.as_retriever()
次に、前と同様にチェーンを作成し、それを使用して質問に答えます。
qa = RetrievalQA.from_chain_type(llm=OpenAI(), chain_type="stuff", retriever=retriever)
query = "What did the president say about Ketanji Brown Jackson"
qa.run(query)
実行結果
" The President said that Judge Ketanji Brown Jackson is one of the nation's top legal minds, a former top litigator in private practice, a former federal public defender, and from a family of public school educators and police officers. He said she is a consensus builder and has received a broad range of support from organizations such as the Fraternal Order of Police and former judges appointed by Democrats and Republicans."
VectorstoreIndexCreatorは、このロジックすべてのラッパーにすぎません。
これは、使用するテキスト スプリッター、使用する埋め込み、および使用するベクターストアで構成できます。たとえば、次のように設定できます。
index_creator = VectorstoreIndexCreator(
vectorstore_cls=Chroma,
embedding=OpenAIEmbeddings(),
text_splitter=CharacterTextSplitter(chunk_size=1000, chunk_overlap=0)
)
「VectorstoreIndexCreater」のようにインデックスを簡単に作成する方法もも覚えておくと便利ですが、内部で何が起こっているのかを理解することも重要です。
別の情報をRetrieverしてみる
公式ガイドのサンプルだとわかりにくいので、何か別のものを読み込ませてみました。
情報元
イーロンマスクのWikiページを利用してみましょう。
上記のページを事前にダウンロードして、プログラムと同じディレクトリに保存しておきます。
以下の中では「ElonReeveMusk_Wikipedia.html」として保存しています。
Retrieverの実行
コード
from langchain.schema import Document
from langchain.chains import RetrievalQA
from langchain.llms import OpenAI
from langchain.document_loaders import BSHTMLLoader
loader = BSHTMLLoader("./ElonReeveMusk_Wikipedia.html")
from langchain.indexes import VectorstoreIndexCreator
index = VectorstoreIndexCreator().from_loaders([loader])
query = "イーロンマスクの生い立ちは? 日本語で回答してください"
res = index.query(query)
print(res)
res = index.query_with_sources(query)
print(res)
HTMLファイルを読み込むLoaderのBSHTMLLoader()というものを使って、事前にダウンロードした、「ElonReeveMusk_Wikipedia.html」ファイルを読み込みます。
そのあと、チャンクの分割、Embedding、ベクトルストアへの保存をワンライナーで実現する「VectorstoreIndexCreator」を実行しています。
そのあとに、「index.query_with_sources()」でクエリを実行します。
今回は「イーロンマスクの生い立ちは? 日本語で回答してください」というのを投げてみました。
実行結果
Using embedded DuckDB without persistence: data will be transient
Chroma collection langchain contains fewer than 4 elements.
Chroma collection langchain contains fewer than 3 elements.
Chroma collection langchain contains fewer than 2 elements.
イーロン・マスクの生い立ちは、カナダでの生活からアメリカ合衆国への移住でした。
Chroma collection langchain contains fewer than 4 elements.
Chroma collection langchain contains fewer than 3 elements.
Chroma collection langchain contains fewer than 2 elements.
{'question': 'イーロンマスクの生い立ちは?\u3000日本語で回答してください', 'answer': ' イーロン・マ スクは1971年6月28日に南アフリカ共和国のプレトリアに生まれ、カナダへの移住を経て、1995年にアメリカ合 衆国への移住を果たしました。\n', 'sources': './ElonReeveMusk_Wikipedia.html'}
実行すると、
「イーロン・マ スクは1971年6月28日に南アフリカ共和国のプレトリアに生まれ、カナダへの移住を経て、1995年にアメリカ合 衆国への移住を果たしました。」
と答えてくれました。
これだけであれば、素のChatGPTでも正確に答えてくれると思いますので、あまり効果の検証として使った情報が良くなかったかもしれませんが、「sources」を見ると、今回用意したHTMLファイルを見ている様子ですので、とりあえず期待した動作となっているようです。
「'sources': './ElonReeveMusk_Wikipedia.html'」
Discussion