Prepara un conjunto de datos personalizado
Organiza las imágenes y las etiquetas en el formato Ultralytics YOLO, convierte las cajas en píxeles a líneas de etiquetas normalizadas y detecta las etiquetas defectuosas antes de entrenar.
Una carpeta mal nombrada o una etiqueta sin normalizar no detiene el entrenamiento. Ultralytics omite las imágenes defectuosas y sigue adelante, así que entrenas con parte de tus datos, obtienes un modelo mediocre y pierdes una tarde buscando la causa en el sitio equivocado.
Esta lección trata la parte mecánica de un conjunto de datos de detección: carpetas, líneas de etiquetas, conversión y comprobaciones. Trabajarás con el conjunto de datos real de píldoras médicas de 115 imágenes que se utiliza para entrenar en el resto del curso. En Buenas prácticas de anotación se explica cómo decidir qué recopilar y cómo etiquetarlo.
Práctico
Obtén el conjunto de datos de prácticas#

La estructura siguiente también es válida como archivo ZIP en el cuadro de diálogo Nuevo conjunto de datos de Ultralytics Platform, así que puedes subir sin cambios el conjunto de datos que prepares aquí. Descarga 92 fotogramas de entrenamiento y 23 de validación de píldoras en blísteres y descomprímelos en my_dataset:
curl -L -o medical-pills.zip https://github.com/ultralytics/assets/releases/download/v0.0.0/medical-pills.zip
unzip -q medical-pills.zip -d my_datasetmy_dataset/
├── images/
│ ├── train/ Frame_0.jpg, Frame_100.jpg, ... (92 images)
│ └── val/ Frame_144.jpg, ... (23 images)
└── labels/
├── train/ Frame_0.txt, Frame_100.txt, ... (92 files)
└── val/ Frame_144.txt, ... (23 files)No hay ningún manifiesto. Ultralytics encuentra cada etiqueta sustituyendo /images/ por /labels/ en la ruta de la imagen y cambiando la extensión por .txt, así que images/val/Frame_144.jpg lee labels/val/Frame_144.txt. Esto tiene dos consecuencias:
- Las carpetas deben llamarse exactamente
imagesylabels, y estar una junto a la otra. - Una imagen con un archivo de etiquetas vacío o inexistente es una imagen de fondo: es válida y útil para enseñar al modelo qué no debe detectar, pero solo si esa era tu intención.
Interpreta una línea de etiqueta#

Cada línea corresponde a un objeto: un índice de clase entero, seguido del centro y el tamaño de la caja delimitadora como fracciones de la anchura y la altura de la imagen. Una línea real de labels/train/Frame_0.txt:
0 0.330729 0.816204 0.103125 0.1212960: índice de clase 0, que el archivo YAML denominarápill.0.330729 0.816204: el centro de la caja está al 33 % de la anchura y al 82 % de la altura.0.103125 0.121296: la caja ocupa aproximadamente el 10 % de la anchura y el 12 % de la altura de la imagen.
Ejemplo práctico: convierte una caja en píxeles#
Las herramientas de etiquetado suelen exportar las esquinas en píxeles (x1, y1, x2, y2). Divide entre el tamaño de la imagen. La persona situada más a la izquierda en bus.jpg, de 810×1080, de la lección anterior ocupa desde (49, 398) hasta (240, 902):
| Campo | Fórmula | Valor |
|---|---|---|
x_center | (x1 + x2) / 2 / width | (49 + 240) / 2 / 810 = 0.178395 |
y_center | (y1 + y2) / 2 / height | (398 + 902) / 2 / 1080 = 0.601852 |
width | (x2 − x1) / width | 191 / 810 = 0.235802 |
height | (y2 − y1) / height | 504 / 1080 = 0.466667 |
La mayoría de las herramientas de etiquetado, y herramientas como Roboflow, pueden exportar directamente al formato YOLO. Para COCO JSON, sigue la guía de entrenamiento con COCO JSON; el repositorio JSON2YOLO cubre otros formatos. Para archivos XML de VOC de Pascal, puedes aplicar la fórmula anterior con un script breve:
import xml.etree.ElementTree as ET
from pathlib import Path
CLASSES = ["person", "bus"] # index = class id; must match names in data.yaml
def voc_to_yolo(xml_file: Path, out_dir: Path) -> None:
root = ET.parse(xml_file).getroot()
w, h = int(root.find("size/width").text), int(root.find("size/height").text)
lines = []
for obj in root.iter("object"):
name = obj.find("name").text
if name not in CLASSES:
continue
box = obj.find("bndbox")
x1, y1, x2, y2 = (float(box.find(k).text) for k in ("xmin", "ymin", "xmax", "ymax"))
xc, yc, bw, bh = (x1 + x2) / 2 / w, (y1 + y2) / 2 / h, (x2 - x1) / w, (y2 - y1) / h
lines.append(f"{CLASSES.index(name)} {xc:.6f} {yc:.6f} {bw:.6f} {bh:.6f}")
out_dir.mkdir(parents=True, exist_ok=True)
(out_dir / f"{xml_file.stem}.txt").write_text("\n".join(lines) + "\n")
voc_to_yolo(Path("bus.xml"), Path("labels"))
print(Path("labels/bus.txt").read_text())Si lo ejecutas en un bus.xml que contenga las cajas de la persona y el autobús de la lección anterior, imprimirá el ejemplo práctico y el autobús:
0 0.178395 0.601852 0.235802 0.466667
1 0.495679 0.454167 0.991358 0.482407Comprueba todo antes de entrenar#
Dibuja algunos archivos de etiquetas sobre sus imágenes. Una clase desplazada, las coordenadas x e y intercambiadas o cajas alrededor de sombras se ven a simple vista, pero no en las métricas:
from ultralytics.data.utils import visualize_image_annotations
visualize_image_annotations(
"my_dataset/images/train/Frame_0.jpg",
"my_dataset/labels/train/Frame_0.txt",
{0: "pill"},
)Después, deja que Ultralytics compruebe todos los archivos. Analiza las etiquetas la primera vez que lee un conjunto de datos y decide, línea por línea, si cada imagen se puede usar:
La línea del análisis indica el resultado: 23 images, 0 backgrounds, 0 corrupt es el aspecto que tiene un conjunto de datos correcto. Cualquier otro resultado corresponde a uno de los errores siguientes.
Evita estos errores#
Síntoma: ignoring corrupt image/label: non-normalized or out of bounds coordinates [634. 881. 198. 163.] y un recuento de corrupt distinto de cero. Causa: se han usado valores de píxel en lugar de fracciones. Solución: divide entre la anchura y la altura de la imagen. Si algún valor de etiqueta supera 1, significa que el cuadro nunca se normalizó.
Síntoma: yolo val analiza 0 images, 23 backgrounds y muestra la advertencia No labels found; el entrenamiento se detiene con ValueError: train: No labels found, en este caso porque la carpeta de etiquetas se llamaba annotations. Solución: asigna exactamente los nombres images y labels a las carpetas, y asegúrate de que los nombres de archivo coincidan.
Síntoma: Label class 1 exceeds dataset class count 1. Possible class labels are 0-0. Causa: la herramienta de exportación numeró las clases empezando por 1. Solución: las clases de YOLO empiezan en 0; resta 1 en el script de conversión o corrige el ajuste de exportación.
Si los cuadros aparecen desplazados media caja hacia abajo y a la derecha, has escrito x1 y1 w h en lugar de x_center y_center w h. Dibujar un archivo permite detectarlo en segundos; ninguna comprobación de análisis puede hacerlo.
Para realizar comprobaciones sistemáticas en todo un conjunto de datos, consulta Control de calidad de los conjuntos de datos. Los conjuntos integrados, como coco8, coco128 y african-wildlife, son buenos ejemplos de estructuras que puedes copiar; en la descripción general de conjuntos de datos se enumeran todos. El entrenador aplica aumento de datos a tus etiquetas, así que una limpieza rápida de los datos aquí beneficia cada época.
Mostrar solución
for s in train val; do
echo "$s: $(ls my_dataset/images/$s | wc -l) images, $(ls my_dataset/labels/$s | wc -l) labels"
done
# train: 92 images, 92 labels
# val: 23 images, 23 labelsfrom pathlib import Path
for txt in Path("my_dataset/labels").rglob("*.txt"):
for line in txt.read_text().splitlines():
values = [float(v) for v in line.split()[1:]]
if any(v < 0 or v > 1 for v in values):
print(txt, line)Si el script no genera resultados, todas las coordenadas están normalizadas. Si las cantidades de cada partición coinciden, todas las imágenes tienen un archivo de etiquetas; la línea del análisis de la próxima lección confirmará que no hay archivos dañados.