end0tknr's kipple - web写経開発

太宰府天満宮の狛犬って、妙にカワイイ

LLM LangChain Retrieval (旧Indexes) オレオレ入門

LLM LangChainへ、これから入門 - end0tknr's kipple - web写経開発

更に上記entryの続き。

本日時点で、LangChain Retrieval の中では VectorstoreIndexCreator が楽な気がします。

参考url

LangChain Retrievalとは?【Document Loaders・Vector Stores・Indexing etc.】

LangChain Retrieval (旧Indexes)とは

  • 2023時点で Retrievalというmodule名ですが、以前は Indexes
  • CSVやPDF等、LLM外部にある独自dataを用いた回答生成が可能
主要機能 内容
Document Loaders PDFやCSV等のdataを読込み
Document transformers Text Splitters等、読込みdataをLLMが扱う形に変換
Text embedding models textを数値ベクトルに変換
Vector stores ベクトル化されたdataを管理
Retrievers 長いtextを複数documentの塊に分割し検索
Indexing 大量textを効率的検索の為、整理,構造化

LangChain Retrieval + OpenAI 使用例

0. pip installと api key設定

!pip install langchain
!pip install openai
import os
os.environ['OPENAI_API_KEY'] = 'sk-ないしょ'

1. Document Loaders - PDF

!pip install pypdf
!pip install tiktoken
!pip install faiss-cpu
from langchain.document_loaders  import PyPDFLoader
from langchain.vectorstores      import FAISS
from langchain.embeddings.openai import OpenAIEmbeddings

pdf_url = 'https://blog.freelance-jp.org/wp-content/uploads/2023/03/FreelanceSurvey2023.pdf'
loader = PyPDFLoader(pdf_url)
pages = loader.load_and_split()

# print(pages[0])

faiss_index = FAISS.from_documents(pages,OpenAIEmbeddings())

prompt = '「フリーランスのリモートワークの実態」について教えて、'
docs = faiss_index.similarity_search(prompt,k=2)
for doc in docs:
  print( str(doc.metadata['page']) , ':', doc.page_content )

↑こう書くと、↓以下のように質問文であるpromptに関連するページが表示されます

44 : フリーランスのリモートワーク実態  (1/3)
45(n=850)Q.リモートワークの実施およびその影響に関して、
それぞれ当てはまる選択肢を一つ選んでください。
(単一回答)
 ※リモートワークとは、情報通信技術 (ICT)を活用した、
 場所や時間にとらわれない柔軟な働き方のことを指します。
 <略>

2. Text Splitters ( Document transformers )

from langchain.text_splitter import CharacterTextSplitter
text_splitter = CharacterTextSplitter(
    separator     = "。",
    chunk_size    = 50,      # 分割する際の最大size
    chunk_overlap = 0,
)
long_txt = '''
吾輩は猫である。名前はまだない。どこで生れたか頓(とん)と見当がつかぬ。
何でも薄暗いじめじめした所でニャーニャー泣いていた事だけは記憶している。
吾輩はここで始めて人間というものを見た。
しかもあとで聞くとそれは書生という人間中で一番獰悪(どうあく)な種族であったそうだ。
この書生というのは時々我々を捕(つかま)えて煮て食うという話である。
しかしその当時は何という考(かんがえ)もなかったから別段恐しいとも思わなかった。
'''
txts = text_splitter.split_text(long_txt)
print(txts)

# documentというclassの塊に分割する場合、create_documents()
# doc_txts = text_splitter.create_documents([long_txt])
# print(doc_txts)

↑こう書くと、↓こう表示されます。

※ 「separator="。"」の指定で必ず分割される訳でなく、 「chunk_size=50」に近い長さで分割するみたい。

※「Created a chunk of size 51」のwarningの意味は調べていません

WARNING:langchain.text_splitter:Created a chunk of size 51,
which is longer than the specified 50
['吾輩は猫である。名前はまだない。どこで生れたか頓(とん)と見当がつかぬ',
 '何でも薄暗いじめじめした所でニャーニャー泣いていた事だけは記憶している',
 '吾輩はここで始めて人間というものを見た',
 'しかもあとで聞くとそれは書生という人間中で一番獰悪(どうあく)な種族であったそうだ',
 'この書生というのは時々我々を捕(つかま)えて煮て食うという話である',
 'しかしその当時は何という考(かんがえ)もなかったから別段恐しいとも思わなかった',
]

3. Vector stores

!pip install chromadb
from langchain.embeddings.openai import OpenAIEmbeddings
from langchain.text_splitter     import CharacterTextSplitter
from langchain.vectorstores      import Chroma
from langchain.indexes           import VectorstoreIndexCreator
from langchain.document_loaders  import TextLoader

long_txt = '''
吾輩は猫である。名前はまだない。どこで生れたか頓(とん)と見当がつかぬ。
何でも薄暗いじめじめした所でニャーニャー泣いていた事だけは記憶している。
吾輩はここで始めて人間というものを見た。
しかもあとで聞くとそれは書生という人間中で一番獰悪(どうあく)な種族であったそうだ。
この書生というのは時々我々を捕(つかま)えて煮て食うという話である。
しかしその当時は何という考(かんがえ)もなかったから別段恐しいとも思わなかった。
'''
text_splitter = CharacterTextSplitter(
    separator     = "。",
    chunk_size    = 50,
    chunk_overlap = 0)
index = VectorstoreIndexCreator(
    vectorstore_cls= Chroma,             # default Vectorstore class
    embedding      = OpenAIEmbeddings(), # default Embedding class
    text_splitter  = text_splitter       # text splitter class
   ).from_documents( text_splitter.create_documents([long_txt]) )
   
answer = index.query('吾輩は何ですか?')
print(answer)

↑こう書くと、↓こう表示されます。

 吾輩は猫です。

先程、「Vector stores=ベクトル化されたdataを管理」と記載しましたが、 VectorstoreIndexCreator がvectorstore_cls等を引数に受け取り、 様々さばいてくれますので、上記のように回答を得ることができます。

4. Retrievers

from langchain.embeddings.openai import OpenAIEmbeddings
from langchain.text_splitter     import CharacterTextSplitter
from langchain.vectorstores      import Chroma
from langchain.vectorstores      import FAISS

long_txt = '''
吾輩は猫である。名前はまだない。どこで生れたか頓(とん)と見当がつかぬ。
何でも薄暗いじめじめした所でニャーニャー泣いていた事だけは記憶している。
吾輩はここで始めて人間というものを見た。
しかもあとで聞くとそれは書生という人間中で一番獰悪(どうあく)な種族であったそうだ。
この書生というのは時々我々を捕(つかま)えて煮て食うという話である。
しかしその当時は何という考(かんがえ)もなかったから別段恐しいとも思わなかった。
'''

text_splitter = CharacterTextSplitter(
    separator     = "。",
    chunk_size    = 50,
    chunk_overlap = 0)
docs = text_splitter.create_documents([long_txt])
db = FAISS.from_documents(docs,OpenAIEmbeddings())

retriever = db.as_retriever() # retriever objectの作成

# 関連documentを検索
ans_docs  = retriever.get_relevant_documents('吾輩は何ですか?')

print( ans_docs[0] )

↑こう書くと、↓こう表示されます

page_content='吾輩は猫である。名前はまだない。どこで生れたか頓(とん)と見当がつかぬ'