> ## Documentation Index
> Fetch the complete documentation index at: https://wb-21fd5541-docs-1917.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Weave でコンピュータビジョンパイプラインをトレースして Evaluate する

> W&B Weave を使って、コンピュータビジョンパイプラインをトレースして Evaluate する方法を学びます

<Note>
  これはインタラクティブなノートブックです。ローカルで実行することも、以下のリンクを使用することもできます。

  * [Google Colab で開く](https://colab.research.google.com/github/wandb/docs/blob/main/weave/cookbooks/source/ocr-pipeline.ipynb)
  * [GitHub でソースを表示](https://github.com/wandb/docs/blob/main/weave/cookbooks/source/ocr-pipeline.ipynb)
</Note>

<div id="prerequisites">
  ## 前提条件
</div>

始める前に、必要なライブラリをインストールして `import` し、W\&B APIキーを取得し、Weaveプロジェクトを初期化してください。

```python lines theme={null}
# 必要な依存関係をインストールする
!pip install openai weave -q
python
import json
import os

from google.colab import userdata
from openai import OpenAI

import weave
python
# APIキーを取得する
os.environ["OPENAI_API_KEY"] = userdata.get(
    "OPENAI_API_KEY"
)  # 左側のメニューからColabの環境シークレットとしてキーを設定してください
os.environ["WANDB_API_KEY"] = userdata.get("WANDB_API_KEY")

# プロジェクト名を設定する
# PROJECT の値を自分のプロジェクト名に置き換えてください
PROJECT = "vlm-handwritten-ner"

# Weave プロジェクトを初期化する
weave.init(PROJECT)
```

<div id="1-create-and-iterate-on-prompts-with-weave">
  ## 1. Weave でプロンプトを作成し、改善を重ねる
</div>

適切なプロンプトエンジニアリングは、モデルがEntitiesを正しく抽出できるようにするうえで重要です。まず、画像データから何を抽出するかと、その出力形式をモデルに指示する基本的なプロンプトを作成します。次に、そのプロンプトを Weave に保存して、トラッキングと改善に活用します。

````python lines theme={null}
# Weave でプロンプトオブジェクトを作成する
prompt = """
Extract all readable text from this image. Format the extracted entities as a valid JSON.
Do not return any extra text, just the JSON. Do not include ```json```
Use the following format:
{"Patient Name": "James James","Date": "4/22/2025","Patient ID": "ZZZZZZZ123","Group Number": "3452542525"}
"""
system_prompt = weave.StringPrompt(prompt)
# プロンプトを Weave に公開する
weave.publish(system_prompt, name="NER-prompt")
````

次に、出力の誤りを減らすため、指示と検証ルールをさらに追加してプロンプトを改善します。

````python lines theme={null}
better_prompt = """
You are a precision OCR assistant. Given an image of patient information, extract exactly these fields into a single JSON object—and nothing else:

- Patient Name
- Date (MM/DD/YYYY)
- Patient ID
- Group Number

Validation rules:
1. Date must match MM/DD/YY; if not, set Date to "".
2. Patient ID must be alphanumeric; if unreadable, set to "".
3. Always zero-pad months and days (e.g. "04/07/25").
4. Omit any markup, commentary, or code fences.
5. Return strictly valid JSON with only those four keys.

Do not return any extra text, just the JSON. Do not include ```json```
Example output:
{"Patient Name":"James James","Date":"04/22/25","Patient ID":"ZZZZZZZ123","Group Number":"3452542525"}
"""
# プロンプトを編集する
system_prompt = weave.StringPrompt(better_prompt)
# 編集したプロンプトをWeaveに公開する
weave.publish(system_prompt, name="NER-prompt")
````

<div id="2-get-the-dataset">
  ## 2. データセットを取得する
</div>

次に、OCR パイプラインの入力として使用する、手書きメモのデータセットを取得します。

データセット内の画像はすでに `base64` でエンコードされているため、前処理なしでそのまま LLM で使用できます。

```python lines theme={null}
# 以下のWeaveプロジェクトからデータセットを取得する
dataset = weave.ref(
    "weave://wandb-smle/vlm-handwritten-ner/object/NER-eval-dataset:G8MEkqWBtvIxPYAY23sXLvqp8JKZ37Cj0PgcG19dGjw"
).get()

# データセット内の特定のサンプルにアクセスする
example_image = dataset.rows[3]["image_base64"]

# example_imageを表示する
from IPython.display import HTML, display

html = f'<img src="{example_image}" style="max-width: 100%; height: auto;">'
display(HTML(html))
```

<div id="3-build-the-ner-pipeline">
  ## 3. NER パイプラインを構築する
</div>

次に、NER パイプラインを構築します。このパイプラインは、次の 2 つの関数で構成されます。

1. データセット内の PIL 画像を受け取り、VLM に渡せる画像の `base64` エンコード済み文字列を返す `encode_image` 関数
2. 画像と system prompt を受け取り、system prompt に記述されたとおりにその画像から抽出した Entities を返す `extract_named_entities_from_image` 関数

```python lines theme={null}
# GPT-4-Visionを使用したトレース可能な関数
def extract_named_entities_from_image(image_base64) -> dict:
    # LLMクライアントの初期化
    client = OpenAI()

    # 指示プロンプトの設定
    # Weaveに保存されたプロンプトをweave.ref("weave://wandb-smle/vlm-handwritten-ner/object/NER-prompt:FmCv4xS3RFU21wmNHsIYUFal3cxjtAkegz2ylM25iB8").get().content.strip()で使用することも可能です
    prompt = better_prompt

    response = client.responses.create(
        model="gpt-4.1",
        input=[
            {
                "role": "user",
                "content": [
                    {"type": "input_text", "text": prompt},
                    {
                        "type": "input_image",
                        "image_url": image_base64,
                    },
                ],
            }
        ],
    )

    return response.output_text
```

次に、`named_entity_recognation` という関数を作成します。これには次の処理を実装します。

* 画像データを NER パイプラインに渡す
* 結果を正しい形式の JSON で返す

W\&B UI で関数の実行を自動的にトラッキングおよびトレースするには、[`@weave.op()` decorator](/ja/weave/reference/python-sdk/trace/op) デコレータを使用します。

`named_entity_recognation` を実行するたびに、完全なトレース結果が Weave UI に表示されます。トレースを表示するには、Weave プロジェクトの **トレース** タブにアクセスします。

```python lines theme={null}
# 評価用NER関数
@weave.op()
def named_entity_recognation(image_base64, id):
    result = {}
    try:
        # 1) vision opを呼び出し、JSON文字列を取得する
        output_text = extract_named_entities_from_image(image_base64)

        # 2) JSONを一度だけパースする
        result = json.loads(output_text)

        print(f"Processed: {str(id)}")
    except Exception as e:
        print(f"Failed to process {str(id)}: {e}")
    return result
```

最後に、データセットに対してパイプラインを実行し、結果を確認します。

次のコードはデータセットの各項目を順に処理し、その結果をローカルファイル `processing_results.json` に保存します。結果は Weave UI でも確認できます。

```python lines theme={null}
# 結果を出力
results = []

# データセット内のすべての画像をループ処理
for row in dataset.rows:
    result = named_entity_recognation(row["image_base64"], str(row["id"]))
    result["image_id"] = str(row["id"])
    results.append(result)

# すべての結果をJSONファイルに保存
output_file = "processing_results.json"
with open(output_file, "w") as f:
    json.dump(results, f, indent=2)

print(f"Results saved to: {output_file}")
```

Weave UI の**トレース**表に、以下のような内容が表示されます。

<Frame>
  <img src="https://mintcdn.com/wb-21fd5541-docs-1917/PReu-nQCaYehtM7U/media/ocr-pipeline/screenshot-2025-05-02-at-120300-pm.png?fit=max&auto=format&n=PReu-nQCaYehtM7U&q=85&s=0ec3570dcaf4b5cd06fa825222024eab" alt="Screenshot 2025-05-02 at 12.03.00 PM.png" width="2389" height="1145" data-path="media/ocr-pipeline/screenshot-2025-05-02-at-120300-pm.png" />
</Frame>

<div id="4-evaluate-the-pipeline-using-weave">
  ## 4. Weave を使ってパイプラインを評価する
</div>

VLM を使用して NER を実行するパイプラインを作成したので、次は Weave を使ってこれを体系的に評価し、どの程度うまく機能するかを確認できます。Weave の評価について詳しくは、[Evaluations Overview](/ja/weave/guides/core-types/evaluations) を参照してください。

Weave の評価を構成する基本要素の 1 つが [Scorers](/ja/weave/guides/evaluation/scorers) です。Scorers は AI の出力を評価し、評価メトリクスを返すために使用されます。AI の出力を受け取り、それを分析して、結果を dict として返します。Scorers は必要に応じて入力データを参照として使用できるほか、評価に関する説明や推論などの追加情報を出力することもできます。

このセクションでは、パイプラインを評価するために 2 つの Scorers を作成します。

1. プログラムによる Scorer
2. LLM-as-a-judge Scorer

<div id="programatic-scorer">
  ### プログラムベースのスコアラー
</div>

プログラムベースのスコアラー `check_for_missing_fields_programatically` は、モデルの出力 (`named_entity_recognition` 関数の出力) を受け取り、結果内で欠けている、または空の `keys` を特定します。

このチェックは、モデルがいずれかのフィールドを取りこぼしたサンプルを特定するのに非常に役立ちます。

```python lines theme={null}
# スコアラーの実行をトラッキングするために weave.op() を追加する
@weave.op()
def check_for_missing_fields_programatically(model_output):
    # すべてのエントリに必須のキー
    required_fields = {"Patient Name", "Date", "Patient ID", "Group Number"}

    for key in required_fields:
        if (
            key not in model_output
            or model_output[key] is None
            or str(model_output[key]).strip() == ""
        ):
            return False  # このエントリにはフィールドの欠損または空白がある

    return True  # すべての必須フィールドが存在し、かつ空でない
```

<div id="llm-as-a-judge-scorer">
  ### LLM-as-a-judge scorer
</div>

評価の次のstepでは、評価結果が実際のNERパフォーマンスを反映するように、画像データとモデルの出力の両方が提供されます。参照されるのはモデルの出力だけでなく、画像の内容そのものです。

このstepで使用されるScorer `check_for_missing_fields_with_llm` は、LLM (具体的にはOpenAIの`gpt-4o`) を使用してスコアリングを行います。`eval_prompt`の内容で指定されているとおり、`check_for_missing_fields_with_llm`は`Boolean`値を出力します。すべてのフィールドが画像内の情報と一致し、フォーマットが正しければ、Scorerは`true`を返します。いずれかのフィールドが欠落している、空である、誤っている、または一致していない場合、結果は`false`となり、scorerは問題を説明するメッセージも返します。

```python lines theme={null}
# LLM-as-a-judge の system prompt

eval_prompt = """
You are an OCR validation system. Your role is to assess whether the structured text extracted from an image accurately reflects the information in that image.
Only validate the structured text and use the image as your source of truth.

Expected input text format:
{"Patient Name": "First Last", "Date": "04/23/25", "Patient ID": "131313JJH", "Group Number": "35453453"}

Evaluation criteria:
- All four fields must be present.
- No field should be empty or contain placeholder/malformed values.
- The "Date" should be in MM/DD/YY format (e.g., "04/07/25") (zero padding the date is allowed)

Scoring:
- Return: {"Correct": true, "Reason": ""} if **all fields** match the information in the image and formatting is correct.
- Return: {"Correct": false, "Reason": "EXPLANATION"} if **any** field is missing, empty, incorrect, or mismatched.

Output requirements:
- Respond with a valid JSON object only.
- "Correct" must be a JSON boolean: true or false (not a string or number).
- "Reason" must be a short, specific string indicating all the problem — e.g., "Patient Name mismatch", "Date not zero-padded", or "Missing Group Number".
- Do not return any additional explanation or formatting.

Your response must be exactly one of the following:
{"Correct": true, "Reason": null}
OR
{"Correct": false, "Reason": "EXPLANATION_HERE"}
"""

# weave.op() を追加して Scorer の実行をトラッキングする
@weave.op()
def check_for_missing_fields_with_llm(model_output, image_base64):
    client = OpenAI()
    response = client.chat.completions.create(
        model="gpt-4o",
        messages=[
            {"role": "developer", "content": [{"text": eval_prompt, "type": "text"}]},
            {
                "role": "user",
                "content": [
                    {
                        "type": "image_url",
                        "image_url": {
                            "url": image_base64,
                        },
                    },
                    {"type": "text", "text": str(model_output)},
                ],
            },
        ],
        response_format={"type": "json_object"},
    )
    response = json.loads(response.choices[0].message.content)
    return response
```

<div id="5-run-the-evaluation">
  ## 5. 評価を実行する
</div>

最後に、渡された `dataset` を自動的に反復処理し、結果をまとめて Weave UI にログする評価 call を定義します。

次のコードは評価を開始し、NER パイプラインの各出力に 2 つの Scorers を適用します。結果は Weave UI の **Evals** タブで確認できます。

```python lines theme={null}
evaluation = weave.Evaluation(
    dataset=dataset,
    scorers=[
        check_for_missing_fields_with_llm,
        check_for_missing_fields_programatically,
    ],
    name="Evaluate_4.1_NER",
)

print(await evaluation.evaluate(named_entity_recognation))
```

上記のコードを実行すると、Weave UI の評価表へのリンクが生成されます。リンクを開いて結果を確認し、任意のモデル、プロンプト、データセット間でパイプラインの異なる反復を比較してください。Weave UI は、以下に示すような可視化をチーム用に自動的に作成します。

<Frame>
  <img src="https://mintcdn.com/wb-21fd5541-docs-1917/PReu-nQCaYehtM7U/media/ocr-pipeline/screenshot-2025-05-02-at-122615-pm.png?fit=max&auto=format&n=PReu-nQCaYehtM7U&q=85&s=803c4a4380682a2640a5bed7851a334f" alt="Screenshot 2025-05-02 at 12.26.15 PM.png" width="2383" height="851" data-path="media/ocr-pipeline/screenshot-2025-05-02-at-122615-pm.png" />
</Frame>
