2024年12月24日火曜日

dspy で二値判定と分類器を試してみる

dspy で二値判定と分類器を試してみる

概要

前回 dspy の基本となる Programs 機能を一通り試してみました
今回は Metrics の機能として判定系を試しました

環境

  • 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-api-endpoint/ai/chat-ai/gpt4",
)
dspy.configure(lm=lm)


class NegativePositiveJudge(dspy.Signature):
    """Judge if the answer is factually correct based on the context."""

    context = dspy.InputField(desc="Context for the prediction")
    question = dspy.InputField(desc="Question to be answered")
    answer = dspy.InputField(desc="Answer for the question")
    factually_correct = dspy.OutputField(
        desc="Is the answer factually correct based on the context?",
        prefix="Factual[positive/negative]:",
    )


judge = dspy.ChainOfThought(NegativePositiveJudge)
factual = judge(
    context="今日はとても気分がいいです",
    question="contextで与えた文章がネガティブかポジティブか判定してください",
    answer="positive",
)
# ポジティブな文章を与えて期待する答えもポジティブなので結果はyesになる
print(factual)

factual = judge(
    context="今日はとても気分が悪いです、最悪の日です",
    question="contextで与えた文章がネガティブかポジティブか判定してください",
    answer="positive",
)
# ネガティブな文章を与えて期待する答えがポジティブなので結果はnoになる
print(factual)


class Classifier(dspy.Signature):

    context = dspy.InputField(desc="Context for the prediction")
    question = dspy.InputField(desc="Question to be answered")
    factually_correct = dspy.OutputField(
        desc="Classify a word or sentence passed in context. There are three categories: 'Fruits', 'Toys' and 'Others.'",
    )


classifier = dspy.ChainOfThought(Classifier)
factual = classifier(
    context="みかん",
    question="contextで与えた文章を分類してください。",
)
print(factual)

factual = classifier(
    context="餃子",
    question="contextで与えた文章を分類してください。",
)
print(factual)

factual = classifier(
    context="hawksnowlog",
    question="contextで与えた文章を分類してください。",
)
print(factual)

factual = classifier(
    context="ちいかわのぬいぐるみはかわいいです。",
    question="contextで与えた文章を分類してください。",
)
print(factual)

factual = classifier(
    context="冬はみかんがおいしいですねー。",
    question="contextで与えた文章を分類してください。",
)
print(factual)

二値判定

今回はネガポジ判定をしています
それ以外にも Signature の条件をいろいろ変更することでいろいろな判定をすることが可能です

class NegativePositiveJudge(dspy.Signature):
    """Judge if the answer is factually correct based on the context."""

    context = dspy.InputField(desc="Context for the prediction")
    question = dspy.InputField(desc="Question to be answered")
    answer = dspy.InputField(desc="Answer for the question")
    factually_correct = dspy.OutputField(
        desc="Is the answer factually correct based on the context?",
        prefix="Factual[positive/negative]:",
    )


judge = dspy.ChainOfThought(NegativePositiveJudge)
factual = judge(
    context="今日はとても気分がいいです",
    question="contextで与えた文章がネガティブかポジティブか判定してください",
    answer="positive",
)
# ポジティブな文章を与えて期待する答えもポジティブなので結果はyesになる
print(factual)

factual = judge(
    context="今日はとても気分が悪いです、最悪の日です",
    question="contextで与えた文章がネガティブかポジティブか判定してください",
    answer="positive",
)
# ネガティブな文章を与えて期待する答えがポジティブなので結果はnoになる
print(factual)

分類器

今回は3つのタグに分類してもらっています
分類の場合は期待する answer を設定する必要がないです

class Classifier(dspy.Signature):

    context = dspy.InputField(desc="Context for the prediction")
    question = dspy.InputField(desc="Question to be answered")
    factually_correct = dspy.OutputField(
        desc="Classify a word or sentence passed in context. There are three categories: 'Fruits', 'Toys' and 'Others.'",
    )


classifier = dspy.ChainOfThought(Classifier)
factual = classifier(
    context="みかん",
    question="contextで与えた文章を分類してください。",
)
print(factual)

factual = classifier(
    context="餃子",
    question="contextで与えた文章を分類してください。",
)
print(factual)

factual = classifier(
    context="hawksnowlog",
    question="contextで与えた文章を分類してください。",
)
print(factual)

factual = classifier(
    context="ちいかわのぬいぐるみはかわいいです。",
    question="contextで与えた文章を分類してください。",
)
print(factual)

factual = classifier(
    context="冬はみかんがおいしいですねー。",
    question="contextで与えた文章を分類してください。",
)
print(factual)

結果は以下の通りです、ちゃんと分類できていることがわかります

Prediction(
    reasoning='The word "みかん" refers to a type of citrus fruit commonly known as a mandarin or tangerine in English. It is a type of fruit.',
    factually_correct='Fruits'
)
Prediction(
    reasoning='The word "餃子" refers to a type of food, specifically dumplings, which are not related to fruits or toys. Therefore, it does not fit into the categories of \'Fruits\' or \'Toys\'.',
    factually_correct='Others'
)
Prediction(
    reasoning='The word "hawksnowlog" does not fit into the categories of \'Fruits\' or \'Toys\'. It appears to be a unique or specific term that does not relate to common fruits or toys.',
    factually_correct='Others'
)
Prediction(
    reasoning='The context mentions "ちいかわのぬいぐるみ" which translates to "Chiikawa plush toy" in English. This indicates that the subject is a type of toy.',
    factually_correct='Toys'
)
Prediction(
    reasoning='The context mentions "みかん" which is a type of fruit. The sentence is discussing the taste of "みかん" in winter, which is related to the category of \'Fruits\'.',
    factually_correct='Fruits'
)

最後に

dspy で Metrics 機能を使っていろいろな判定機能を実装してみました
LLM を使った判定/分類器は基本的にファインチューニングを使って再学習するのが定番ですが dspy を使えばそれをする必要がなくかつ簡単に判定することができます

更に dspy には評価を行う機能もありデータセットを与えて期待した答えるになっているかの精度を出す仕組みもあるのでそれを使えば Signature で与えているコンテキスト(InputField)や回答方針(OutputField)が正確かどうかの評価もできます

次回はそのあたりの機能を試したいので dspy の最大の機能である Optimizer を試します

参考サイト

0 件のコメント:

コメントを投稿