こちらの続編です。
日本語対応したいなということでしたので、元のデータセット翻訳しようかと思いましたが、そもそもこちらのデータセットがありました!ありがとうございます!
上のウォークスルーで言語に依存せずいけそうだなと思った(手を加えるのが面倒だったという話もあります)ので、データ差し替えだけでいきました。そして、いけました。
ソースコードはこちら。
変更点はデータセット取り込みの部分だけです。あとは、データベース名などを少々変えているのみ。
from datasets import load_dataset
import pandas as pd
dataset = load_dataset("yulanfmy/databricks-qa-ja", split="train")
pdf = pd.DataFrame(dataset)
raw_sdf = spark.createDataFrame(pdf)
display(raw_sdf)
あとは、列名を変更しているくらいです。
- response - ユーザーの質問に適した情報を含む可能性のあるドキュメントのテキストやナレッジベースの回答
- source - オンラインドキュメントをポイントするURL
raw_inputs = (
spark
.table('sources')
.selectExpr(
'response',
'source'
)
)
display(raw_inputs)
今回は複数チャンクが作られなかったので、チャンクサイズは調整しても良いかもしれません。
テスト時点でも良い感じです。
question = "Delta Lakeとは何ですか?"
# エンべディングにアクセスするためにベクトルストアをオープン
embeddings = OpenAIEmbeddings(model=config['openai_embedding_model'])
vector_store = FAISS.load_local(embeddings=embeddings, folder_path=config['vector_store_path'])
# ドキュメント取得の設定
n_documents = 5 # 取得するドキュメントの数
retriever = vector_store.as_retriever(search_kwargs={'k': n_documents}) # 取得メカニズムの設定
# 適切なドキュメントの取得
docs = retriever.get_relevant_documents(question)
for doc in docs:
print(doc,'\n')
page_content='Delta Lakeと名付けた理由は二つあります。最初の理由は、Delta Lakeはデータに対する、変更あるいはdelta(差分)を追跡し続けます。第二の理由は、Delta Lakeはお使いのデータレイクに流れ込むデータをフィルタリングする「デルタ(三角州)」として動作するからです。' metadata={'source': 'https://qiita.com/taka_yayoi/items/f206f22c4b15b6fbc7f5'}
page_content='Delta Lakeは、信頼性、セキュリティ、パフォーマンスを提供するオープンフォーマットのストレージレイヤーです。' metadata={'source': 'https://qiita.com/kohei-arai/items/5b54a89cbaec801f1972'}
page_content='Delta Lakeはデータレイクに格納されているデータに対して高速なデータ処理、強力なデータガバナンスを提供するストレージレイヤーソフトウェアです。ACIDトランザクションやデータのバージョン管理、インデックス作成機能などを提供します。機械学習の文脈ではデータのバージョン管理が重要な意味を持つことになります。' metadata={'source': 'https://qiita.com/taka_yayoi/items/5a804e53c60a55fb7340'}
page_content='Delta Lake は、データレイクのテーブルに対して挿入、更新、削除、ACID トランザクションの追加を容易にし、メンテナンスと履歴管理を簡素化するオープンなストレージ形式レイヤーです。Delta Lake は、動的ファイルプルーニングを実行して、より高速な SQL クエリに最適化する機能も提供しています。' metadata={'source': 'https://www.databricks.com/jp/blog/2022/05/20/five-simple-steps-for-implementing-a-star-schema-in-databricks-with-delta-lake.html'}
page_content='Delta Lakeの他のトランザクションストレージレイヤーソフトウェアも類似の課題を解決するためのものですが、Delta Lakeはデータエコシステムにおけるより広いユースケースをカバーしています。データレイクに、信頼性だけでなく、高性能、セキュリティを追加し、バッチ処理、ストリーミング処理を統合するフレームワークを提供します。また、データ変換パイプラインの効率を改善するだけではなく、BI、データサイエンス、機械学習と言った下流のアクティビティの効率も改善できます。DatabricksのDelta Lakeを活用することで、Deltaエンジンによるよる良いパフォーマンス、きめ細かいアクセスコントロールによる高セキュリティ・ガバナンス、多くのBIツールに対する高性能ネイティブコネクターによるエコシステムの拡大と言った様々な恩恵を享受することができます。最後に、Delta Lakeは世界中の数千のお客様によって、3年以上の実運用に耐え続けている実績を持っています。毎日、少なくとも3ペタバイトのデータがDelta Lakeに投入されています。' metadata={'source': 'https://qiita.com/taka_yayoi/items/f206f22c4b15b6fbc7f5'}
# 指定されたドキュメントのそれぞれに対して
for doc in docs:
# ドキュメントテキストの取得
text = doc.page_content
# レスポンスの生成
output = qa_chain.generate([{'context': text, 'question': question}])
# 結果から回答の取得
generation = output.generations[0][0]
answer = generation.text
# 回答の表示
if answer is not None:
print(f"Question: {question}", '\n', f"Answer: {answer}")
break
Question: Delta Lakeとは何ですか?
Answer: Delta Lakeは、データに対する変更あるいはdelta(差分)を追跡し続け、お使いのデータレイクに流れ込むデータをフィルタリングする「デルタ(三角州)」として動作するデータレイクのことです。
QABotクラスでも動作確認。
# botオブジェクトのインスタンスを作成
qabot = QABot(llm, retriever, chat_prompt)
# 質問に対するレスポンスの取得
qabot.get_answer(question)
Out[16]: {'answer': 'Delta Lakeは、データに対する変更あるいはdelta(差分)を追跡し続け、お使いのデータレイクに流れ込むデータをフィルタリングする「デルタ(三角州)」として動作する、データレイクの一種です。',
'source': 'https://qiita.com/taka_yayoi/items/f206f22c4b15b6fbc7f5',
'output_metadata': {'token_usage': {'prompt_tokens': 268,
'completion_tokens': 90,
'total_tokens': 358},
'model_name': 'gpt-3.5-turbo'}}
あとは、MLflowにトラッキングしてもらって、モデルレジストリに登録、サービングという流れです。
ソースがない場合でも結果が返ってくるのは、別のソースから生成しているからなのですかね。なお、回答は合ってます。
たまに英語が返ってくるのは、プロンプトテンプレートが英語のままだからな気がする。見直してみます。 直しました。
config['openai_embedding_model'] = 'text-embedding-ada-002'
config['openai_chat_model'] = "gpt-3.5-turbo"
config['system_message_template'] = """あなたはDatabricksによって開発された有能なアシスタントであり、指定されたコンテキストに基づいて質問に回答することを得意としており、コンテキストはドキュメントです。コンテキストが回答を決定するのに十分な情報を提供しない場合には、わかりませんと言ってください。コンテキストが質問に適していない場合には、わかりませんと言ってください。コンテキストから良い回答が見つからない場合には、わかりませんと言ってください。問い合わせが完全な質問になっていない場合には、わからないと言ってください。コンテキストから良い回答が得られた場合には、質問に回答するためにコンテキストを要約してみてください。"""
config['human_message_template'] = """指定されたコンテキスト: {context}。 質問に回答してください {question}."""
config['temperature'] = 0.15
しかし、このアーキテクチャならデータ増えてもファインチューニングする必要がないのが良いですね。ベクトルDBを更新すれば良いわけですから。
あとは、モデルの差し替えもやってみますかね。今は、OpenAI API叩いているので。