2024年12月23日月曜日

dspy.ai の Programs を一通り使ってみた

dspy.ai の Programs を一通り使ってみた

概要

dspy の Programs (ChainOfThought や ReAct など) を使ってみました
dspy で日本語化する方法も紹介します

環境

  • Python 3.11.3
  • dspy 2.5.43

コード全体

まずはコード全体を紹介します
以下で機能部分ごとにコードを紹介しています

import dspy

lm = dspy.LM(
    "azure/gpt-4-32k",
    api_key="xxx",
    api_version="",
    api_base="https://your-azure-endpoint/ai/chat-ai/gpt4",
)
dspy.configure(lm=lm)


# 日本語化
class JapaneseQA(dspy.Signature):
    question = dspy.InputField()
    answer = dspy.OutputField(desc="The answer should be in Japanese")


generate_jp_answer = dspy.ChainOfThought(JapaneseQA)
result = generate_jp_answer(question="こんにちわ!あなたは誰ですか?")
print(result)

# ヒントあり質問
result = generate_jp_answer(
    question="hawksnowlogがよく書くブログのカテゴリは何ですか?",
    hint="hawksnowlogのぶログはこれです、https://hawksnowlog.blogspot.com/",
)
print(result)

# 問題特化な質問はProgramOfThoughtを使ったほうが良さそう
pot = dspy.ProgramOfThought(JapaneseQA)

question = "サラはリンゴを5個持っています。彼女は店からさらに 7 個のリンゴを購入します。サラは今リンゴを何個持っていますか?"
result = pot(question=question)
print(result)


# 質問に対して更に質問する場合にReActを使ってスレッドっぽく質問できる
# ReAct は複数の InputField を定義することができ履歴を管理する history フィールドを追加する
# tools はCallableなdspy.Toolクラスのインスタンスを指定することができるらしくその実行結果を回答に反映することができるらしい
class JapaneseQA4ReAct(dspy.Signature):
    question = dspy.InputField()
    history = dspy.InputField(desc="Chat history")
    answer = dspy.OutputField(desc="The answer should be in Japanese")


react_module = dspy.ReAct(JapaneseQA4ReAct, tools=[])

question = "サラはリンゴを5個持っています。彼女は店からさらに 7 個のリンゴを購入します。サラは今リンゴを何個持っていますか?"
result = react_module(question=question, history="")
print(result)
history = f"USER: {question}\nAI: {result.answer}"

question2 = "更に30個購入しました。サラは今リンゴを何個持っていますか?"
result = react_module(question=question2, history=history)
print(result)


# Retrive 特定のツールを使った検索結果や、APIの結果を考慮して回答させることができる
# いわゆる RAG (Retrieval Augmented Generation)
# 以下は ColBERTv2 を使ってサーバからのレスポンスを考慮して回答するようになっている
# 他にも FAISS や AzureAISearch などが使える https://github.com/stanfordnlp/dspy/blob/main/docs/docs/deep-dive/retrieval_models_clients/FaissRM.md
colbertv2_wiki17_abstracts = dspy.ColBERTv2(
    url="http://20.102.90.50:2017/wiki17_abstracts"
)
dspy.settings.configure(rm=colbertv2_wiki17_abstracts)

retriever = dspy.Retrieve(k=3)

query = "When was the first FIFA World Cup held?"

topK_passages = retriever(query).passages  # type: ignore

for idx, passage in enumerate(topK_passages):
    print(f"{idx+1}]", passage, "\n")

日本語化の仕方

dspy を使って LLM から返答を得る場合基本的には英語で返答してきます
なので日本語で返答するように指示します

# 日本語化
class JapaneseQA(dspy.Signature):
    question = dspy.InputField()
    answer = dspy.OutputField(desc="The answer should be in Japanese")


generate_jp_answer = dspy.ChainOfThought(JapaneseQA)
result = generate_jp_answer(question="こんにちわ!あなたは誰ですか?")
print(result)

ヒントありで質問

ヒントありで質問するとそのヒントをもとに回答を作成することができます

# ヒントあり質問
result = generate_jp_answer(
    question="hawksnowlogがよく書くブログのカテゴリは何ですか?",
    hint="hawksnowlogのぶログはこれです、https://hawksnowlog.blogspot.com/",
)
print(result)

問題形式の質問

ChainOfThought と正直変わらないと思うのですが問題形式の場合は ProgramOfThought を使ったほうがいいようです

pot = dspy.ProgramOfThought(JapaneseQA)

question = "サラはリンゴを5個持っています。彼女は店からさらに 7 個のリンゴを購入します。サラは今リンゴを何個持っていますか?"
result = pot(question=question)
print(result)

反復的に質問する

ReAct は前の質問の結果を次の質問に継続することができます
スレッド的な使い方ができます
回答の履歴を管理するフィールドを新たに追加するのがポイントです

class JapaneseQA4ReAct(dspy.Signature):
    question = dspy.InputField()
    history = dspy.InputField(desc="Chat history")
    answer = dspy.OutputField(desc="The answer should be in Japanese")


react_module = dspy.ReAct(JapaneseQA4ReAct, tools=[])

question = "サラはリンゴを5個持っています。彼女は店からさらに 7 個のリンゴを購入します。サラは今リンゴを何個持っていますか?"
result = react_module(question=question, history="")
print(result)
history = f"USER: {question}\nAI: {result.answer}"

question2 = "更に30個購入しました。サラは今リンゴを何個持っていますか?"
result = react_module(question=question2, history=history)
print(result)

RAG

ColBERTv2 という Retriever を使っていますが FAISS なども使えます
少し凝ったことをしたい場合には RAG を使うことになるかなと思います

colbertv2_wiki17_abstracts = dspy.ColBERTv2(
    url="http://20.102.90.50:2017/wiki17_abstracts"
)
dspy.settings.configure(rm=colbertv2_wiki17_abstracts)

retriever = dspy.Retrieve(k=3)

query = "When was the first FIFA World Cup held?"

topK_passages = retriever(query).passages  # type: ignore

for idx, passage in enumerate(topK_passages):
    print(f"{idx+1}]", passage, "\n")

最後に

dspy の Programs を試してみました
どれも LLM を使って質問に一工夫加えてほしい回答を得るという仕組みになっています

Programs の他にも Metrics や Evaluation という機能があるので興味があれば使ってみるといいかなと思います

参考サイト

0 件のコメント:

コメントを投稿