それでは、はじめましょう
こちらの手順に従って進めるか、事前にデータとスキーマが設定されたColabデモプロジェクトに接続することで実践できます(管理者権限なしでアクセス可能)。以前のノートブックの例と同様に、本デモではTwitter US Airline Sentimentデータセットを使用します。
セットアップ
まず、MEGAnnoをインストールし、MEGAnnoクライアントモジュールをインポートします。
!pip install "meganno_client[ui] @ git+https://github.com/megagonlabs/meganno-client.git" -q
from meganno_client import Service, Authentication, Controller, PromptTemplate
次に、MEGAnnoのアクセストークン(こちらからリクエスト)、OpenAI APIキーを設定し、ホストされているMEGAnnoサービスに接続します。
import os
token = # token shared through email, request at https://meganno.github.io/#request_form
# openai key, we don't collect or store openAI keys
os.environ['OPENAI_API_KEY']= 'sk-...'
# optional
# os.environ['OPENAI_ORGANIZATION'] = 'your_organization_key'
auth = Authentication(project='blog_post', token=token)
demo = Service(project='blog_post',auth=auth)
controller = Controller(demo, auth)
初期LLMアノテーション
LLMによるアノテーションを実行するには、エージェントを指定する必要があります。エージェントは、使用するLLMの設定とプロンプトによって定義されます。MEGAnnoはOpenAIのモデルをサポートしており、プロンプトの作成を簡単にする視覚的なインターフェースを提供しています。
モデルとプロンプトの設定
まず、基本的な組み込みプロンプトテンプレートを使用します。このテンプレートはラベルスキーマを基にシステム指示を構築し、指定されたデータエントリの内容を連結する形になっています。
ユーザーは、提供されたサンプル入力を使って連結の結果をプレビューし、必要に応じてテンプレートを修正できます。また、テンプレートにはget_template関数があり、プロンプトの内容を確認することが可能です。
schema = demo.get_schemas().value(active=True)
label_name= 'sentiment'
prompt_template = PromptTemplate(label_schema=schema[0]["schemas"]["label_schema"], label_names=[label_name])
prompt_template.preview(records=["Sample Airline tweets1", "Sample Airline tweets1"])
prompt_template.get_template()
次に、モデルを設定する必要があります。(必須項目はモデルフィールドのみです。)
model_config = {
"model": "gpt-3.5-turbo",
"temperature": 0}
サブセットに対するLLMアノテーションジョブの実行
サブセットに対してLLMアノテーションジョブを実行するには、まずエージェントを登録する必要があります。人間によるアノテーションと同様に、LLMアノテーションもサブセット単位で管理されます。LLMジョブは、エージェントをサブセット上で実行するプロセスです。エージェントを登録すると、一意の識別子が付与され、実行管理が容易になります。これにより、同じサブセットや異なるサブセットに対してエージェントを再利用することが可能になります。
既存のエージェントを一覧表示したり、詳細を確認したりするための追加APIについては、AppendixのA2を参照してください。
# register agent.
agent_gpt_3p5 = controller.create_agent(model_config, prompt_template, provider_api="openai:chat")
ここでは、「good」というキーワードで検索したサブセットを使用します。主にポジティブな例が含まれることを想定していますが、中立的な例や皮肉的なネガティブな例も含まれる可能性があります。
# selecting subset to run the job with
subset = demo.search(keyword="good")
subset.show({"view": "table"})
次に、GPT-3.5を使用した基本的なエージェントと最小限のプロンプトを用いて、run_job関数を呼び出し、サブセットのラベリングを実行します。その後の人間による検証を適切に設定するため、モデルの予測に対する信頼度の指標としてmetadata confを収集します。この値は、OpenAIのレスポンスから取得したlogprobに基づいて計算されます。
ジョブの実行が成功すると、一意の識別子job_uuidが返され、実行の参照として利用できます。既存のジョブを一覧表示したり、詳細を確認したりするための追加APIについては、AppendixのA2を参照してください。
job_uuid = controller.run_job(agent_gpt_3p5, subset, label_name, label_meta_names=["conf"])
最終的なジョブがデータベースに保存される前に、自動ラベリングの各ステップに関する統計情報も出力されます。これには、構築された有効なプロンプトの数、成功したOpenAIコールの回数、有効な結果を含むレスポンスの数とその分布などが含まれます。これらの情報を確認することで、ラベリングの品質に関する初期的な評価を行うことができます。
検証
人間による検証
次に、LLMの出力を確認し、予測を検証し、必要に応じて修正します。基本的な検証を実行するには、job_uuidを使用してジョブを参照します。
検証の際には、各予測に関連付けられたconfメタデータ(信頼度)を基にソートし、最も不確実な予測から確認を開始することができます。
verf_subset = demo.search_by_job(job_id=job_uuid)
verf_subset.show({"mode": "verifying",
"label_meta_names": ["conf"]})
この場合、皮肉的なものと無関係なものの2つの誤分類を修正し、残りの予測を確認します。保存ボタンをクリックすると、検証結果がデータおよびアノテーションとともにバックエンドに保存されます。
MEGAnno gives you control over the data entries you’d like to verify through finer-grained searches. For example, the scripts below search for all the unverified predictions from a certain job with a confidence score lower than 0.99:
args = {
"job_id": job_uuid,
"label_metadata_condition": {
"label_name": "sentiment",
"name": "conf",
"operator": "<",
"value": 0.99,
},
"verification_condition": {
"label_name": label_name,
"search_mode": "UNVERIFIED", # "ALL"|"UNVERIFIED"|"VERIFIED"
},
}
verf_subset2 = demo.search_by_job(**args)
verf_subset2.show({"mode": "verifying",
"label_meta_names": ["conf"]})
検証結果は、さまざまな条件(プリディケート)を使用して取得できます。例えば以下のスクリプトでは、人間の検証者がエージェントの予測に異議を唱え、ラベルを修正したすべてのケースを表示します。
# further filter by type of verification(CONFIRMS|CORRECTS)
# CONFIRMS: where the verification confirms the original label
# CORRECTS: where the verification is different from the original label
verf_subset.get_verification_annotations(
label_name="sentiment",
label_level="record",
annotator=job_uuid,
verified_status="CORRECTS", # CONFIRMS|CORRECTS
)
自動検証
人間による検証の代替手段として、より高性能な別のモデルを用いた「セカンドオピニオン」を導入することも可能です。ここでは、同じサブセットをGPT-4モデルで処理し、予測結果が異なるデータポイントを収集します。
model_config2 = {'model': 'gpt-4',
'temperature': 1}
agent_gpt_4 = controller.create_agent(model_config2, prompt_template, provider_api='openai:chat')
job_uuid2 = controller.run_job(agent_gpt_4,
subset,
label_name,
label_meta_names = ["conf"],
fuzzy_extraction=True)
s_conflict = demo.search(annotator_list=[job_uuid,job_uuid2],
label_condition={'name':label_name,
'operator':'conflicts'})
s_conflict.show({'mode':'reconciling'})
上記のスクリプトを使用することで、人間による検証が最も必要なデータエントリを特定することも可能になりました。
修正
初回のアノテーションと人間による検証の結果、モデルが皮肉的なツイートや、対象(航空会社)に対する中立的な感情の識別に苦戦していることが判明しました。そこで、プロンプトを修正し、明示的な指示と具体的な例を追加します。
# building new prompt
prompt_template2 = PromptTemplate(label_schema=schema[0]['schemas']['label_schema'], label_names=[label_name])
# preview templates with sample intput, switchable with the "input" drop down
prompt_template2.preview(records=[ 'Sample Airline tweets1', 'Sample Airline tweets2'])
これを行うには、修正したプロンプトを「Task Inst」フィールドに追加し、プロンプト更新ウィジェットで保存します。
# Results:
agent_gpt_3p5_revised = controller.create_agent(model_config, prompt_template2 , provider_api='openai:chat')
修正後のモデルの性能を初期評価するため、1,000件のツイートのうち、データIDでソートされた最後の30件の予測精度を比較します。これは包括的な評価ではありませんが、初期的な洞察を得るとともに、次の改善の方向性を示唆する目的で実施します。
次のセルでは、ラベル付きのサンプルテストデータを読み込み、GPT-3.5エージェントを使用して、初期プロンプトと修正後のプロンプトで実験を実行し、それぞれの予測精度を比較します。
test_subset = demo.search(skip=970, limit=30)
test_subset.show()
test_job_3p5 = controller.run_job(agent_gpt_3p5,
test_subset,
label_name,
label_meta_names = ["conf"],
fuzzy_extraction=True)
test_job_3p5_revised = controller.run_job(agent_gpt_3p5_revised,
test_subset,
label_name,
label_meta_names = ["conf"],
fuzzy_extraction=True)
def evaluate(results, label_name, job_uuid):
total, match = 0, 0
for item in results:
groud_truth = list(filter(lambda x: x['name']== 'pseudo_label', item['record_metadata']))[0]['value']
prediction = list(filter(lambda x: x['annotator']== job_uuid, item['annotation_list']))[0]
predicted_label = list(filter(lambda x: x['label_name']==label_name, prediction['labels_record']))[0]['label_value'][0]
total += 1
match += 1 if groud_truth == predicted_label else 0
return f"{match} out of {total} correct."
print("Before: ", evaluate(demo.search_by_job(job_id=test_job_3p5, limit=100).value(), label_name, test_job_3p5) )
print("After: ", evaluate(demo.search_by_job(job_id=test_job_3p5_revised, limit=100).value(), label_name, test_job_3p5_revised))
# Output
# Before: 22 out of 30 correct.
# After: 24 out of 30 correct.
まとめ
このチュートリアルを通じて、MEGAnnoの活用方法について理解を深めていただけたことを願っています。プラットフォームの利用方法や詳細な情報については、公式ドキュメントページをご参照ください。高度な機能、APIクライアントドキュメント、LLMとの統合に関する詳細情報を確認できます。
Appendix
本デモに加えて、さらなる活用のための追加情報を紹介します。
A1. ローカル環境でのプロジェクトセットアップ
事前定義されたプロジェクトを当社のインフラ上でホストする代わりに、独自のプロジェクトをデプロイする手順をこちらで確認できます。JupyterやColabなどの好みのノートブック環境に接続し、カスタマイズしたデータやタスクスキーマを使用できる完全なアクセス権を活用してください。
以下のコードブロックは、本Colabノートブックのセットアップに使用したものです。このノートブックでは、3つの候補ラベルを持つ感情分析タスクを指定し、サンプルツイートデータセットからデータをインポートしています。
demo.get_schemas().set_schemas({
'label_schema': [
{
"name": "sentiment",
"level": "record",
"options": [
{ "value": "pos", "text": "positive" },
{ "value": "neg", "text": "negative" },
{ "value": "neu", "text": "neutral" },
]
}
]
})
demo.get_schemas().value(active=True)
import from dataframe
import pandas as pd
df = pd.read_csv("tweets.csv").loc[:1000]
demo.import_data_df(df, column_mapping={
'id':'id',
'content':'content',
"metadata":'pseudo_label' # optional metadata
})
A2. エージェントとジョブの一覧表示
パイプラインの任意のタイミングで、コントローラーに対して、既存のエージェントやジョブを一覧表示するよう要求できます。この際、各エージェントやジョブの詳細な設定情報を確認できるほか、特定の条件(プリディケート)を適用してフィルタリングすることも可能です(現在、プロバイダーによるフィルタリングをサポート)。
# list agents
controller.list_my_agents()
# job_list = controller.list_jobs('agent_uuid', [agent_uuid])
# filter over agent properties and get jobs
ret = controller.list_agents(provider_filter="openai", show_job_list=True)
job_list = [val for sublist in ret for val in sublist["job_list"]]
job_list