見出し画像

[Guidance#2]CoT, Few-Shotを用いた2値分類タスクを試す

Microsoft社が出したGuidanceについて、利用方法をまとめます。

今回は与えられた文章が時系列的にあり得るかどうかの判定タスクを試してみようと思います。またCoTとFew-showによって精度を高めてみます。

こちらのタスクは公式のnotebookを参考に、日本語版に少しアレンジをしてます。また、この記事の内容は以下のColabで試せます。


プログラムを定義

!pip install guidance transformers


!pip show guidance
>Name: guidance
>Version: 0.0.49
>(以下、略)


import guidance
import os
os.environ['OPENAI_API_KEY'] = "your-openai-api-key"
guidance.llm = guidance.llms.OpenAI('text-davinci-003')


# いくつかのショット例を定義する
examples = [
    {'input': '私はシェイクスピアについて書いた',
    'entities': [{'entity': '私', 'time': '現在'}, {'entity': 'シェイクスピア', 'time': '16世紀'}],
    'reasoning': '私はシェイクスピアについて書くことができる。なぜなら彼は私よりも過去に生きていたから時系列上あり得る',
    'answer': 'いいえ'},
    {'input': 'シェイクスピアは私について書いた',
    'entities': [{'entity': 'シェイクスピア', 'time': '16世紀'}, {'entity': '私', 'time': '現在'}],
    'reasoning': 'シェイクスピアは私について書くことができない。なぜなら彼は私が生まれる前に亡くなっているから時系列上あり得ない',
    'answer': 'はい'},
    {'input': 'ローマ皇帝は私に背中を叩いた',
    'entities': [{'entity': 'ローマ皇帝', 'time': '1-5世紀'}, {'entity': '私', 'time': '現在'}],
    'reasoning': 'ローマ皇帝は私に背中を叩くことができない。なぜなら彼は私が生まれる前に亡くなっているから時系列上あり得ない',
    'answer': 'はい'}
]


# ガイダンスプログラムを定義する
structure_prompt = guidance(
'''あなたは与えられたインプットが時間軸上あり得るかについて判定を行います。
以下では、判定するための段階的な推論方法(つまり、エンティティと関連する時期に基づいてそれが起こり得たかどうかを判定する方法)を示しています。
----

{{~! ショットの例を表示する ~}}
{{~#each examples}}
文章: {{this.input}}
エンティティと時代:{{#each this.entities}}
{{this.entity}}: {{this.time}}{{/each}}
推論: {{this.reasoning}}
時系列上誤っているか: {{this.answer}}
---
{{~/each}}

{{~! 実際の質問を最後に置く }}
文章: {{input}}
エンティティと時代:
{{gen "entities"}}
推論:{{gen "reasoning"}}
時系列上誤っているか:{{#select "answer"}}はい{{or}}いいえ{{/select}}''')

out = structure_prompt(examples=examples, input='ティラノサウルスが私の犬を噛んだ')
実行結果(青が引数、緑が生成された結果)


Guidance programの文法

guidanceの見通しをよくするため、ここで利用されている文法について簡単に解説していきます。

  • Completion型はロールタグなどをguidanceプログラム内では利用しません。必要な箇所に{{}}を用いて、生成や論理制御などを行います

  • 事前に定義しておいたfew-shotサンプルをguidance内で利用する

    • structure_prompt(examples=examples,..)で引数として渡しておきます

    • リスト形式の引数を扱うために、{{~#each examples}} {{~/each}}ブロック内で呼び出しを行なっています。またvariable.keyという形で各種バリューを呼び出し可能です

    • さらに{{~#each examples}} {{~/each}}は入れ子的に呼び出しが可能です

  • {{gen "xxx"}}という形で該当箇所に当てはまる文字列が生成されます。生成された文字列は'xxx'というキーにキャッシュされます

  • {{#select "xxx"}}A{{or}}B{{/select}}で特定の選択肢の中から文字列が生成されるようになります

    • 今回は {{#select "answer"}}はい{{or}}いいえ{{/select}} と記載し、「はい」「いいえ」のいずれかのみを生成するようにしています

    • ちなみに {{#select "xxx" logprobs='yyy'}}A{{or}}B{{/select}} とすると、AとBの対数確率(log P)を取得できます。

      • out["yyy"]

      • > {'A': -0.042361464, 'B': -3.1826203}

      • 0に近い方が確率が高く、上記の場合は A がAnswerとして選択されます

  • guidanceのキャッシュクリア

    • これはおまけですが、キャッシュをクリアしたい時は以下のように行うことができます

    • guidance.llms.OpenAI.cache.clear()


詰まったエラー

前提として、guidanceのバージョンは0.0.49であることに留意して下さい。

{{#Select}}の選択肢に半角スペースが含まれるとエラーが発生する

今回だと以下のような記述はエラーになります。

{{#select "answer"}} はい{{or}} いいえ{{/select}} 

公式notebookの方だと、半角スペースが含まれていたため踏襲した結果エラーになりました。これについてissueが既に上がっており、ver.0.0.49で解決したとのことでしたが、日本語は別なのかダメでした。

解決方法は、半角スペースを除外すると解決できます。


{{#select}}の前に半角スペースが含まれるとエラーが発生する

今回だと以下のような記述はエラーになります。

時系列上誤っているか: {{#select "answer"}}はい{{or}}いいえ{{/select}}''')

": "となっているとエラーです。few-shotの方では": "となっていますが、こちらは問題なさそうでした。 {{#select}}..{{/select}} を利用している時のみ起きていそうです。

原因は不明ですが、以下のような記述で意図する挙動となりました。

時系列上誤っているか:{{#select "answer"}}はい{{or}}いいえ{{/select}}''')


guidance programは文字列の中で記述するということもあって、エラーに気付くのが少し難しいなということに気づきました。また内部で複雑な処理をしているということもあり、エラー内容も直感的ではないものが多いです。

ここら辺、改善されていく多分使いやすくなると思うので、積極的にissue挙げていこうと思います。

この記事が参加している募集

この記事が気に入ったらサポートをしてみませんか?