Label StudioからPotatoへの移行
Label Studioのプロジェクト、テンプレート、アノテーションをPotato形式に手動変換するためのステップバイステップガイド。
Label StudioからPotatoへの移行
このガイドでは、既存のLabel StudioプロジェクトをPotatoに手動で移行する方法を説明します。移行には設定の手動変換とデータ形式を変換するPythonスクリプトの作成が含まれます。
公式の移行ツールはありません - これは両方のプラットフォームの理解を必要とする手動プロセスです。
なぜ移行するのか?
Potatoは特定のユースケースで優位性があります:
- 研究重視:学術的なアノテーション研究のために構築
- クラウドソーシング:ProlificとMTurkのネイティブ統合
- シンプルさ:YAMLベースの設定、データベース不要
- カスタマイズ性:Pythonで容易に拡張
- 軽量:ファイルベースのストレージ、デプロイが容易
移行の概要
移行は以下のステップを含む手動プロセスです:
- Label StudioのXMLテンプレートをPotato YAML設定に手動変換
- データ形式を変換するPythonスクリプトの作成(JSONからJSONL)
- 既存のアノテーションを移行するスクリプトの作成(ある場合)
- 徹底的なテストと変換データの検証
テンプレート変換
テキスト分類
Label Studio 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:
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:
<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:
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:
<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:
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:
<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:
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:
<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:
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:
<View>
<Text name="text" value="$text"/>
<Rating name="quality" toName="text" maxRating="5"/>
</View>Potato 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形式:
[
{
"id": 1,
"data": {
"text": "This is great!",
"meta_info": "source1"
}
},
{
"id": 2,
"data": {
"text": "This is terrible.",
"meta_info": "source2"
}
}
]Potato JSONL形式:
{"id": "1", "text": "This is great!", "metadata": {"source": "source1"}}
{"id": "2", "text": "This is terrible.", "metadata": {"source": "source2"}}変換スクリプト
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")アノテーションの移行
既存アノテーションの変換
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も文字オフセットを使用するため、変換は簡単です:
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 Studio | Potato |
|---|---|
| Choices (single) | radio |
| Choices (multiple) | multiselect |
| Labels | span |
| Rating | likert |
| TextArea | text |
| RectangleLabels | bounding_box |
| PolygonLabels | polygon |
| Taxonomy | (ネストされたmultiselectを使用) |
| Pairwise | comparison |
品質管理の考慮事項
Label Studioから移行する際は、品質管理措置を手動で実装する必要があります。Potatoは基本的なQC機能を提供しますが、包括的な組み込みQCシステムはありません。
品質管理のアプローチ
アテンションチェック:データファイルにアテンションチェックアイテムを手動で追加できます。これらは正解がわかっている通常のアイテムで、アノテーターの注意力を確認するために含めます:
# 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)一致度計算:収集したアノテーションを使用してオフラインでアノテーター間一致度を計算:
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からユーザーをエクスポート
# 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ユーザー設定の作成
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: "..."移行のテスト
検証スクリプト
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を使用するようにスクリプトを更新してください。