第5回
今回は、資料を元に回答するwebアプリを作成しました。
参考URL:https://blog.streamlit.io/langchain-tutorial-4-build-an-ask-the-doc-app/
GitHub:https://github.com/s1932072/LangChain-tutorial-4
import streamlit as st
from langchain.llms import OpenAI
from langchain.text_splitter import CharacterTextSplitter
from langchain.vectorstores import Chroma
from langchain.chains import RetrievalQA
from langchain.embeddings import HuggingFaceEmbeddings
import os
os.environ["TOKENIZERS_PARALLELISM"] = "false"
class CustomHuggingFaceEmbeddings(HuggingFaceEmbeddings):
def embed_documents(self, documents):
texts = [doc.text.replace("\n", " ") if hasattr(doc, 'text') else str(doc) for doc in documents]
return super().embed_documents(texts)
def generate_embeddings(texts, llm):
return [llm.get_embedding(text) for text in texts]
def generate_response(uploaded_file, query_text):
if uploaded_file is not None:
documents = [uploaded_file.read().decode()]
text_splitter = CharacterTextSplitter()
texts = text_splitter.create_documents(documents)
embeddings = CustomHuggingFaceEmbeddings(model_name="oshizo/sbert-jsnli-luke-japanese-base-lite")
llm = OpenAI(base_url="http://localhost:1234/v1", api_key="not-needed")
db = Chroma.from_documents(texts, embeddings)
retriever = db.as_retriever(search_kwargs={"k": 1})
qa = RetrievalQA.from_chain_type(llm=llm, chain_type="stuff", retriever=retriever)
responses = []
for text in texts:
responses.append(qa.run(text))
return qa.run(query_text)
st.set_page_config(page_title='🦜🔗 Ask the Doc App')
st.title('🦜🔗 Ask the Doc App')
uploaded_file = st.file_uploader('Upload an article', type='txt')
query_text = st.text_input('Enter your question:', placeholder='Please provide a short summary.', disabled=not uploaded_file)
result = []
with st.form('myform', clear_on_submit=True):
submitted = st.form_submit_button('Submit', disabled=not (uploaded_file and query_text))
if submitted:
with st.spinner('Calculating...'):
response = generate_response(uploaded_file, query_text)
result.append(response)
if len(result):
st.info(result[0])
今回はある程度参考URLのプログラムから変更を加えました。
OpenAI APIからlocal modelへの変更。
EmbeddingにHuggingFaceEmbeddingを使用。
以下が今回使用したプログラムについてChatGPT4による説明です。
このプログラムは、Streamlitを用いて作成されたウェブアプリケーションで、「Ask the Doc App」という名前がつけられています。このアプリは、アップロードされた文書から質問に対する回答を探す機能を提供します。プログラムは複数の部分に分かれており、それぞれの部分について説明します。
1. ライブラリのインポート
import streamlit as st
from langchain.llms import OpenAI
from langchain.text_splitter import CharacterTextSplitter
from langchain.vectorstores import Chroma
from langchain.chains import RetrievalQA
from langchain.embeddings import HuggingFaceEmbeddings
import os
-
streamlit
(別名st
) は、ウェブアプリケーションを簡単に作成するためのPythonライブラリです。 -
langchain
は、自然言語処理を行うためのライブラリで、このコードではOpenAIのAPIを使用するためのクラスやテキスト分割、ベクトルストア、質問応答チェーンなどが含まれています。 -
os
ライブラリは環境変数を設定するために使用されています。
2. 環境変数の設定
os.environ["TOKENIZERS_PARALLELISM"] = "false"
- この行は、Hugging Faceのトークナイザーが並列処理を行わないようにするための設定です。これは、マルチプロセッシング環境でのデッドロックを避けるために必要です。
3. カスタム HuggingFaceEmbeddings クラス
class CustomHuggingFaceEmbeddings(HuggingFaceEmbeddings):
def embed_documents(self, documents):
texts = [doc.text.replace("\n", " ") if hasattr(doc, 'text') else str(doc) for doc in documents]
return super().embed_documents(texts)
-
CustomHuggingFaceEmbeddings
は、HuggingFaceEmbeddings
を拡張したカスタムクラスです。このクラスは、ドキュメントの埋め込みを生成する際に、改行をスペースに置き換えるカスタム動作を行います。
4. 埋め込み生成関数
def generate_embeddings(texts, llm):
return [llm.get_embedding(text) for text in texts]
- この関数は、与えられたテキストのリストに対して、指定されたローカルLLM(LangChain Large Language Model)を使用して埋め込みを生成します。
5. レスポンス生成関数
def generate_response(uploaded_file, query_text):
# ...
- この関数はアップロードされたファイルから文書を読み込み、その文書をチャンクに分割してから、それぞれのチャンクに対して質問応答(QA)処理を行います。結果は
responses
リストに追加され、最後にqa.run(query_text)
で得られた結果が返されます。
6. Streamlitウェブアプリのセットアップ
st.set_page_config(page_title='🦜🔗 Ask the Doc App')
st.title('🦜🔗 Ask the Doc App')
# ...
- Streamlitを使用してウェブアプリのUIを設定しています。ページのタイトルやアップロードフォーム、質問入力欄などが設定されています。
7. ファイルアップロードと質問入力
uploaded
_file = st.file_uploader('Upload an article', type='txt')
query_text = st.text_input('Enter your question:', placeholder='Please provide a short summary.', disabled=not uploaded_file)
- ユーザーがテキストファイルをアップロードし、質問を入力できるようにするためのUI要素です。
8. レスポンスの処理
result = []
with st.form('myform', clear_on_submit=True):
# ...
if submitted:
with st.spinner('Calculating...'):
response = generate_response(uploaded_file, query_text)
result.append(response)
if len(result):
st.info(result[0])
- ユーザーが「Submit」ボタンを押すと、選択されたファイルと入力された質問を基にレスポンスを生成し、画面上に表示します。
感想
このプログラムで、参考資料を元にした回答ができるようになりました。
しかし、1つの質問に対して時間がかかります。さらに、全く同じ質問をしても回答がブレます。これに関しては複数回出力を行わせて、よく出現する文章を最終結果とすれば良いか....
また、与えるテキストの量が多いとエラーを吐きます。これは私が使用しているLocal modelが一度に入力できるtolken数に限りがあるからです。この問題の解決にも取り組もうと思います。これができれば私のやりたいネット記事の要約もできそうです!
次回
この調子でStreamlitの記事を実装して遊んでいきます
それではまた!