Skip to content
Guides6 min read

Label StudioからPotatoへの移行

Label Studioのプロジェクト、テンプレート、アノテーションをPotato形式に手動変換するためのステップバイステップガイド。

Potato Team·

Label StudioからPotatoへの移行

このガイドでは、既存のLabel StudioプロジェクトをPotatoに手動で移行する方法を説明します。移行には設定の手動変換とデータ形式を変換するPythonスクリプトの作成が含まれます。

公式の移行ツールはありません - これは両方のプラットフォームの理解を必要とする手動プロセスです。

なぜ移行するのか?

Potatoは特定のユースケースで優位性があります:

  • 研究重視:学術的なアノテーション研究のために構築
  • クラウドソーシング:ProlificとMTurkのネイティブ統合
  • シンプルさ:YAMLベースの設定、データベース不要
  • カスタマイズ性:Pythonで容易に拡張
  • 軽量:ファイルベースのストレージ、デプロイが容易

移行の概要

移行は以下のステップを含む手動プロセスです:

  1. Label StudioのXMLテンプレートをPotato YAML設定に手動変換
  2. データ形式を変換するPythonスクリプトの作成(JSONからJSONL)
  3. 既存のアノテーションを移行するスクリプトの作成(ある場合)
  4. 徹底的なテストと変換データの検証

テンプレート変換

テキスト分類

Label Studio XML:

xml
<View>
  <Text name="text" value="$text"/>
  <Choices name="sentiment" toName="text" choice="single">
    <Choice value="Positive"/>
    <Choice value="Negative"/>
    <Choice value="Neutral"/>
  </Choices>
</View>

Potato YAML:

yaml
annotation_task_name: "Sentiment Classification"
 
data_files:
  - "data/items.jsonl"
 
item_properties:
  id_key: id
  text_key: text
 
annotation_schemes:
  - annotation_type: radio
    name: sentiment
    description: "What is the sentiment?"
    labels:
      - name: positive
        tooltip: "Positive sentiment"
      - name: negative
        tooltip: "Negative sentiment"
      - name: neutral
        tooltip: "Neutral sentiment"

マルチラベル分類

Label Studio XML:

xml
<View>
  <Text name="text" value="$text"/>
  <Choices name="topics" toName="text" choice="multiple">
    <Choice value="Politics"/>
    <Choice value="Sports"/>
    <Choice value="Technology"/>
    <Choice value="Entertainment"/>
  </Choices>
</View>

Potato YAML:

yaml
annotation_schemes:
  - annotation_type: multiselect
    name: topics
    description: "Select all relevant topics"
    labels:
      - name: politics
        tooltip: "Politics content"
      - name: sports
        tooltip: "Sports content"
      - name: technology
        tooltip: "Technology content"
      - name: entertainment
        tooltip: "Entertainment content"

固有表現認識

Label Studio XML:

xml
<View>
  <Labels name="entities" toName="text">
    <Label value="PERSON" background="#FFC0CB"/>
    <Label value="ORG" background="#90EE90"/>
    <Label value="LOCATION" background="#ADD8E6"/>
  </Labels>
  <Text name="text" value="$text"/>
</View>

Potato YAML:

yaml
annotation_schemes:
  - annotation_type: span
    name: entities
    description: "Select entity spans in the text"
    labels:
      - name: PERSON
        tooltip: "Person names"
      - name: ORG
        tooltip: "Organization names"
      - name: LOCATION
        tooltip: "Location names"

注意:Potatoのスパンアノテーションは、Label Studioとは異なるハイライトを使用する場合があります。変換した設定をテストして表示がニーズに合っていることを確認してください。

画像分類

Label Studio XML:

xml
<View>
  <Image name="image" value="$image_url"/>
  <Choices name="category" toName="image">
    <Choice value="Cat"/>
    <Choice value="Dog"/>
    <Choice value="Other"/>
  </Choices>
</View>

Potato YAML:

yaml
data_files:
  - "data/images.jsonl"
 
item_properties:
  id_key: id
  text_key: image_url
 
annotation_schemes:
  - annotation_type: radio
    name: category
    description: "What animal is in the image?"
    labels:
      - name: cat
        tooltip: "Cat"
      - name: dog
        tooltip: "Dog"
      - name: other
        tooltip: "Other animal"

バウンディングボックスアノテーション

Label Studio XML:

xml
<View>
  <Image name="image" value="$image_url"/>
  <RectangleLabels name="objects" toName="image">
    <Label value="Car"/>
    <Label value="Person"/>
    <Label value="Bicycle"/>
  </RectangleLabels>
</View>

Potato YAML:

yaml
annotation_schemes:
  - annotation_type: bounding_box
    name: objects
    description: "Draw boxes around objects"
    labels:
      - name: car
        tooltip: "Car"
      - name: person
        tooltip: "Person"
      - name: bicycle
        tooltip: "Bicycle"

注意:Potatoのバウンディングボックスサポートは、Label Studioと異なる場合があります。現在の機能についてはドキュメントを確認してください。

評価スケール

Label Studio XML:

xml
<View>
  <Text name="text" value="$text"/>
  <Rating name="quality" toName="text" maxRating="5"/>
</View>

Potato YAML:

yaml
annotation_schemes:
  - annotation_type: likert
    name: quality
    description: "Rate the quality"
    size: 5
    labels:
      - name: "1"
        tooltip: "Poor"
      - name: "2"
        tooltip: "Below average"
      - name: "3"
        tooltip: "Average"
      - name: "4"
        tooltip: "Good"
      - name: "5"
        tooltip: "Excellent"

データ形式変換

Label Studio JSONからPotato JSONL

Label Studio形式:

json
[
  {
    "id": 1,
    "data": {
      "text": "This is great!",
      "meta_info": "source1"
    }
  },
  {
    "id": 2,
    "data": {
      "text": "This is terrible.",
      "meta_info": "source2"
    }
  }
]

Potato JSONL形式:

json
{"id": "1", "text": "This is great!", "metadata": {"source": "source1"}}
{"id": "2", "text": "This is terrible.", "metadata": {"source": "source2"}}

変換スクリプト

python
import json
 
def convert_label_studio_to_potato(ls_file, potato_file):
    """Convert Label Studio JSON to Potato JSONL"""
 
    with open(ls_file, 'r') as f:
        ls_data = json.load(f)
 
    with open(potato_file, 'w') as f:
        for item in ls_data:
            potato_item = {
                "id": str(item["id"]),
                "text": item["data"].get("text", ""),
            }
 
            # Convert nested data fields
            if "data" in item:
                for key, value in item["data"].items():
                    if key != "text":
                        if "metadata" not in potato_item:
                            potato_item["metadata"] = {}
                        potato_item["metadata"][key] = value
 
            # Handle image URLs
            if "image" in item.get("data", {}):
                potato_item["image_url"] = item["data"]["image"]
 
            f.write(json.dumps(potato_item) + "\n")
 
    print(f"Converted {len(ls_data)} items")
 
# Usage
convert_label_studio_to_potato("label_studio_export.json", "data/items.jsonl")

アノテーションの移行

既存アノテーションの変換

python
def convert_annotations(ls_export, potato_output):
    """Convert Label Studio annotations to Potato format"""
 
    with open(ls_export, 'r') as f:
        ls_data = json.load(f)
 
    with open(potato_output, 'w') as f:
        for item in ls_data:
            if "annotations" not in item or not item["annotations"]:
                continue
 
            for annotation in item["annotations"]:
                potato_ann = {
                    "id": str(item["id"]),
                    "text": item["data"].get("text", ""),
                    "annotations": {},
                    "annotator": annotation.get("completed_by", {}).get("email", "unknown"),
                    "timestamp": annotation.get("created_at", "")
                }
 
                # Convert results
                for result in annotation.get("result", []):
                    scheme_name = result.get("from_name", "unknown")
 
                    if result["type"] == "choices":
                        # Classification
                        potato_ann["annotations"][scheme_name] = result["value"]["choices"][0]
 
                    elif result["type"] == "labels":
                        # NER spans
                        if scheme_name not in potato_ann["annotations"]:
                            potato_ann["annotations"][scheme_name] = []
 
                        potato_ann["annotations"][scheme_name].append({
                            "start": result["value"]["start"],
                            "end": result["value"]["end"],
                            "label": result["value"]["labels"][0],
                            "text": result["value"]["text"]
                        })
 
                    elif result["type"] == "rating":
                        potato_ann["annotations"][scheme_name] = result["value"]["rating"]
 
                f.write(json.dumps(potato_ann) + "\n")
 
# Usage
convert_annotations("ls_annotated_export.json", "annotations/migrated.jsonl")

スパンアノテーションの変換

Label Studioは文字オフセットを使用し、Potatoも文字オフセットを使用するため、変換は簡単です:

python
def convert_spans(ls_spans):
    """Convert Label Studio span format to Potato format"""
    potato_spans = []
 
    for span in ls_spans:
        potato_spans.append({
            "start": span["value"]["start"],
            "end": span["value"]["end"],
            "label": span["value"]["labels"][0],
            "text": span["value"]["text"]
        })
 
    return potato_spans

機能マッピング

Label StudioPotato
Choices (single)radio
Choices (multiple)multiselect
Labelsspan
Ratinglikert
TextAreatext
RectangleLabelsbounding_box
PolygonLabelspolygon
Taxonomy(ネストされたmultiselectを使用)
Pairwisecomparison

品質管理の考慮事項

Label Studioから移行する際は、品質管理措置を手動で実装する必要があります。Potatoは基本的なQC機能を提供しますが、包括的な組み込みQCシステムはありません。

品質管理のアプローチ

アテンションチェック:データファイルにアテンションチェックアイテムを手動で追加できます。これらは正解がわかっている通常のアイテムで、アノテーターの注意力を確認するために含めます:

python
# Add attention check items to your data
attention_items = [
    {"id": "attn_1", "text": "ATTENTION CHECK: Please select 'Positive'", "is_attention": True},
    {"id": "attn_2", "text": "ATTENTION CHECK: Please select 'Negative'", "is_attention": True},
]
 
# Intersperse with regular items
import random
all_items = regular_items + attention_items
random.shuffle(all_items)

一致度計算:収集したアノテーションを使用してオフラインでアノテーター間一致度を計算:

python
from sklearn.metrics import cohen_kappa_score
import numpy as np
 
def compute_agreement(annotations_file):
    """Compute agreement from collected annotations"""
    # Load annotations and compute metrics externally
    # Potato does not have built-in agreement calculation
    pass

冗長アノテーション:データ管理プロセスで複数のユーザーにアイテムを割り当てて、アイテムごとに複数のアノテーターを設定。

ユーザー移行

Label Studioからユーザーをエクスポート

python
# Label Studio API call to get users
import requests
 
def export_ls_users(ls_url, api_key):
    response = requests.get(
        f"{ls_url}/api/users",
        headers={"Authorization": f"Token {api_key}"}
    )
    return response.json()

Potatoユーザー設定の作成

yaml
user_config:
  # Simple auth for migrated users
  auth_type: password
 
  users:
    - username: user1@example.com
      password_hash: "..."  # Generate new passwords
 
    - username: user2@example.com
      password_hash: "..."

移行のテスト

検証スクリプト

python
def validate_migration(original_ls, converted_potato):
    """Validate converted data matches original"""
 
    with open(original_ls) as f:
        ls_data = json.load(f)
 
    with open(converted_potato) as f:
        potato_data = [json.loads(line) for line in f]
 
    # Check item count
    assert len(ls_data) == len(potato_data), "Item count mismatch"
 
    # Check IDs preserved
    ls_ids = {str(item["id"]) for item in ls_data}
    potato_ids = {item["id"] for item in potato_data}
    assert ls_ids == potato_ids, "ID mismatch"
 
    # Check text content
    for ls_item, potato_item in zip(
        sorted(ls_data, key=lambda x: x["id"]),
        sorted(potato_data, key=lambda x: x["id"])
    ):
        assert ls_item["data"]["text"] == potato_item["text"], \
            f"Text mismatch for item {ls_item['id']}"
 
    print("Validation passed!")
 
validate_migration("label_studio_export.json", "data/items.jsonl")

移行チェックリスト

  • Label Studioからデータをエクスポート(JSON形式)
  • テンプレートXMLをPotato YAMLに手動変換
  • データ形式を変換するPythonスクリプトの作成と実行(JSONからJSONL)
  • 既存アノテーションを変換するスクリプトの作成と実行(ある場合)
  • Potatoプロジェクト構造のセットアップ
  • サンプルデータでテスト
  • 変換データがオリジナルと一致することを検証
  • 新しいインターフェースでアノテーターをトレーニング
  • パイロットアノテーションバッチの実行

よくある問題

文字エンコーディング

Label StudioとPotatoは共にUTF-8を使用しますが、データのエンコーディング問題をチェックしてください。

画像パス

ローカルパスをURLに変換するか、Potatoの期待する形式に合わせてパスを更新してください。

カスタムコンポーネント

Label Studioのカスタムコンポーネントは、Potatoのカスタムテンプレートとして再作成する必要があります。

APIの違い

Label Studioを自動化していた場合、PotatoのAPIを使用するようにスクリプトを更新してください。


移行にヘルプが必要ですか?完全なドキュメントを確認するか、GitHubでお問い合わせください。