Skip to content
Guides9 min read

Migración de Label Studio a Potato

Guía paso a paso para convertir manualmente proyectos, plantillas y anotaciones de Label Studio al formato de Potato.

Potato Team·

Migración de Label Studio a Potato

Esta guía te ayuda a migrar manualmente proyectos existentes de Label Studio a Potato. La migración implica convertir configuraciones a mano y escribir scripts de Python para transformar los formatos de datos.

Ten en cuenta que no existe una herramienta oficial de migración - este es un proceso manual que requiere comprender ambas plataformas.

¿Por Qué Migrar?

Potato ofrece ventajas para ciertos casos de uso:

  • Enfoque en investigación: Construido para estudios de anotación académica
  • Crowdsourcing: Integración nativa con Prolific y MTurk
  • Simplicidad: Configuración basada en YAML, sin base de datos requerida
  • Personalización: Fácil de extender con Python
  • Ligereza: Almacenamiento basado en archivos, fácil de desplegar

Resumen de la Migración

La migración es un proceso manual que involucra estos pasos:

  1. Convertir manualmente la plantilla XML de Label Studio a configuración YAML de Potato
  2. Escribir scripts de Python para transformar el formato de datos (JSON a JSONL)
  3. Escribir scripts para migrar anotaciones existentes (si las hay)
  4. Probar exhaustivamente y validar los datos convertidos

Conversión de Plantillas

Clasificación de Texto

XML de 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>

YAML de 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"

Clasificación Multi-Etiqueta

XML de 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>

YAML de 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"

Reconocimiento de Entidades Nombradas

XML de 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>

YAML de 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"

Nota: La anotación de span de Potato puede usar un resaltado diferente al de Label Studio. Prueba tu configuración convertida para verificar que la visualización cumple tus necesidades.

Clasificación de Imágenes

XML de 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>

YAML de 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"

Anotación de Cuadros Delimitadores

XML de 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>

YAML de 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"

Nota: El soporte de cuadros delimitadores en Potato puede diferir del de Label Studio. Consulta la documentación para las capacidades actuales.

Escalas de Calificación

XML de Label Studio:

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

YAML de 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"

Conversión de Formato de Datos

JSON de Label Studio a JSONL de Potato

Formato de Label Studio:

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

Formato JSONL de Potato:

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

Script de Conversión

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")

Migración de Anotaciones

Conversión de Anotaciones Existentes

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")

Conversión de Anotación de Span

Label Studio usa desplazamientos de caracteres; Potato también usa desplazamientos de caracteres, por lo que la conversión es directa:

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

Mapeo de Funciones

Label StudioPotato
Choices (single)radio
Choices (multiple)multiselect
Labelsspan
Ratinglikert
TextAreatext
RectangleLabelsbounding_box
PolygonLabelspolygon
Taxonomy(usar multiselect anidado)
Pairwisecomparison

Consideraciones de Control de Calidad

Al migrar desde Label Studio, necesitarás implementar medidas de control de calidad manualmente. Potato proporciona algunas capacidades básicas de QC, pero no hay un sistema integral de QC integrado.

Enfoques para el Control de Calidad

Verificaciones de atención: Puedes agregar manualmente elementos de verificación de atención a tu archivo de datos. Estos son elementos regulares con respuestas correctas conocidas que incluyes para verificar la atención del anotador:

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)

Cálculo de acuerdo: Calcula el acuerdo entre anotadores fuera de línea usando tus anotaciones recopiladas:

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

Anotación redundante: Configura múltiples anotadores por elemento asignando elementos a múltiples usuarios en tu proceso de gestión de datos.

Migración de Usuarios

Exportar Usuarios de 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()

Crear Configuración de Usuarios en 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: "..."

Pruebas de Migración

Script de Validación

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")

Lista de Verificación de Migración

  • Exportar datos de Label Studio (formato JSON)
  • Convertir manualmente la plantilla XML a YAML de Potato
  • Escribir y ejecutar scripts de Python para transformar el formato de datos (JSON a JSONL)
  • Escribir y ejecutar scripts para convertir anotaciones existentes (si las hay)
  • Configurar la estructura del proyecto de Potato
  • Probar con datos de ejemplo
  • Validar que los datos convertidos coincidan con los originales
  • Entrenar a los anotadores en la nueva interfaz
  • Ejecutar un lote piloto de anotación

Problemas Comunes

Codificación de Caracteres

Label Studio y Potato ambos usan UTF-8, pero verifica problemas de codificación en tus datos.

Rutas de Imágenes

Convierte rutas locales a URLs o actualiza las rutas para coincidir con el formato esperado de Potato.

Componentes Personalizados

Los componentes personalizados de Label Studio necesitan ser recreados como plantillas personalizadas de Potato.

Diferencias de API

Si automatizaste Label Studio, actualiza los scripts para usar la API de Potato.


¿Necesitas ayuda con la migración? Consulta la documentación completa o contacta en GitHub.