Entrenar un detector de objetos AI con Label Studio y MMDetection
Etiquetar y entrenar necesita algo de pegamento
Cuando entrené un detector de objetos AI hace algún tiempo - LabelImg fue una herramienta muy útil, pero la exportación desde Label Studio a formato COCO no era aceptada por el marco MMDetection..
Necesitaba algunas herramientas y scripting para que todo funcionara.
Listando aquí los elementos faltantes y algunos scripts algunos de los cuales encontré en internet y otros los escribí yo mismo.
Pasos básicos
Preparar, entrenar y usar AI implica
- obtener datos de origen (imágenes para detección de objetos y clasificación)
- etiquetar imágenes y preparar el conjunto de datos
- desarrollar un nuevo modelo o encontrar uno existente adecuado
- entrenar el modelo, a veces con ajuste de parámetros hiper
- usar el modelo para predecir etiquetas para nuevas imágenes (ingerring)
Aquí doy pasos exactos para etiquetar datos con Label Studio (paso 2) y entrenar con mmdetection y torchvision (paso 4), y tocar la inferencia (paso 5)
Label Studio
Tiene algunas raíces de código abierto en el programa LabelImg pero ahora se desarrolla y mantiene de manera muy centralizada. Y tiene una versión empresarial, por supuesto.
Aún así, está disponible para autohospedaje, lo cual es muy útil.
Configurar y ejecutar
Puedes instalar y ejecutar Label Studio de varias maneras, como por ejemplo el paquete pip, o un grupo de contenedores docker compose. Aquí estoy usando un solo contenedor docker.
Preparar la carpeta de origen
mkdir ~/ls-data
sudo chown -R 1001:1001 ~/ls-data10
Configuración de almacenamiento local
mkdir ~/ai-local-store
mkdir ~/ai-local-labels
# configurar algunos permisos adicionales
En ~/ai-local-store guardas los archivos de imagen, ~/ai-local-labels - etiquetas sincronizadas.
Iniciar el contenedor docker
docker run -it -p 8080:8080 \
-e LABEL_STUDIO_HOST=http://your_ip_address:8080/ \
-e LABEL_STUDIO_LOCAL_FILES_SERVING_ENABLED=true \
-e LABEL_STUDIO_LOCAL_FILES_DOCUMENT_ROOT=/ai-local-store \
-e DATA_UPLOAD_MAX_NUMBER_FILES=10000000 \
-v /home/somename/ls-data:/label-studio/data \
-v /home/somename/ai-local-store:/ai-local-store \
-v /home/somename/ai-local-labels:/ai-local-labels \
heartexlabs/label-studio:latest \
label-studio \
--log-level DEBUG
LABEL_STUDIO_HOST - debido a las redirecciones del webui de LS. DATA_UPLOAD_MAX_NUMBER_FILES … Django restringió la cantidad de archivos subidos a 100 y tuvo un efecto terrible en label studio, por lo que fue necesario proporcionar este nuevo límite. Todas las demás configuraciones están muy bien documentadas en las documentaciones de Label Studio.
Importar configuración. En la configuración del proyecto en Almacenamiento en la nube, agregar Almacenamiento de archivos local de tipo similar a:
No olvides marcar “Tratar cada objeto de cubo como un archivo de origen” si planeas sincronizar imágenes (creo que sí lo estás haciendo), no los jsons. Si ya tienes algunas etiquetas para estas imágenes en un json separado - simplemente configuras este Almacenamiento en la nube. No hacer clic en Sincronizar. Y luego Importar el json.
Configura lo mismo para el Almacenamiento en la nube de destino y ai-local-labels - si deseas sincronizarlos.
Importar datos preetiquetados en Label Studio
Me encanta el formato JSON COCO. También el formato Pascal VOC. Pero no son directamente compatibles con Label Studio, por lo que necesitas convertirlos primero al formato propio de LS.
Pero antes, probablemente necesitarás filtrar el conjunto de datos. Puede que necesites solo algunas etiquetas allí, no todas. Me gusta el script filter.py de coco-manager: https://github.com/immersive-limit/coco-manager/blob/master/filter.py
python filter.py --input_json instances_train2017.json --output_json filtered.json --categories person dog cat
Bien, ahora, instala el convertidor. Como recomienda el sitio oficial del convertidor:
python -m venv env
source env/bin/activate
git clone https://github.com/heartexlabs/label-studio-converter.git
cd label-studio-converter
pip install -e .
Convertir desde COCO y establecer la carpeta correcta
label-studio-converter import coco -i your-input-file.json -o output.json
Importar el output.json haciendo clic en el botón Importar en Label Studio.
Etiquetado
Aquí se hace mucho trabajo creativo.
Exportar
Después de hacer clic en el botón Exportar en Label Studio y seleccionar el formato de exportación COCO - echa un vistazo dentro de este archivo y admira los nombres de las imágenes. Se verían así si importaste etiquetas antes sin sobrescribir la ruta base de la imagen
"images": [
{
"width": 800,
"height": 600,
"id": 0,
"file_name": "\/data\/local-files\/?d=\/iteration1001\/123.jpg"
},
O así si sincronizaste el almacenamiento en la nube externo.
"images": [
{
"width": 800,
"height": 600,
"id": 0,
"file_name": "http:\/\/localhost:8080\/data\/local-files\/?d=iteration1001\/123.jpg"
},
Estos no son muy bonitos. Queremos algo como
"images": [
{
"width": 800,
"height": 600,
"id": 0,
"file_name": "iteration1001/123.jpg"
},
Para corregir los nombres de los archivos usé scripts muy agradables. Ellos sobrescribirán el archivo result.json, así que si necesitas un respaldo antes - cuida de ello tú mismo:
sed -i -e 's/\\\/data\\\/local-files\\\/?d=\\\///g' ~/tmp/result.json
sed -i "s%http:\\\/\\\/localhost:8080\\\/data\\\/local-files\\\/?d=%%" ~/tmp/result.json
sed -i "s%http:\\\/\\\/your_ip_address:8080\\\/data\\\/local-files\\\/?d=%%" ~/tmp/result.json
Backup de datos y base de datos de Label Studio
Detén cuidadosamente tu contenedor docker de Label Studio y luego ejecuta algo como
cp ~/ls-data ~/all-my-backups
cp ~/ai-local-store ~/all-my-backups
cp ~/ai-local-labels ~/all-my-backups
Fusionar
A veces necesitas fusionar varios conjuntos de datos en uno, especialmente si estás realizando varias iteraciones.
Usé la herramienta COCO-merger. Después de instalar y ejecutar con el parámetro -h:
python tools/COCO_merger/merge.py -h
COCO Files Merge Usage
python -m COCO_merger.merge --src Json1.json Json2.json --out OUTPUT_JSON.json
Argument parser
usage: merge.py [-h] --src SRC SRC --out OUT
Merge two annotation files to one file
optional arguments:
-h, --help show this help message and exit
--src SRC SRC Path of two annotation files to merge
--out OUT Path of the output annotation file
Sí, puedes fusionar solo dos archivos. Así que si tienes 10 iteraciones - necesitas hacer un esfuerzo adicional. Aún así, me gusta.
MMDetection
Dividir el conjunto de datos
Para dividir el conjunto de datos en entrenamiento y prueba usé la herramienta COCOSplit.
git clone https://github.com/akarazniewicz/cocosplit.git
cd cocosplit
pip install -r requirements
No hay mucho en ello:
$ python cocosplit.py -h
usage: cocosplit.py [-h] -s SPLIT [--having-annotations]
coco_annotations train test
Divide el archivo de anotaciones COCO en conjuntos de entrenamiento y prueba.
argumentos posicionales:
coco_annotations Ruta al archivo de anotaciones COCO.
train Donde almacenar las anotaciones de entrenamiento COCO
test Donde almacenar las anotaciones de prueba COCO
argumentos opcionales:
-h, --help mostrar esta ayuda y salir
-s SPLIT Porcentaje de división; un número en (0, 1)
--having-annotations Ignorar todas las imágenes sin anotaciones. Solo mantener estas con al menos una anotación
--multi-class Dividir un conjunto de datos de múltiples clases manteniendo la distribución de clases en los conjuntos de entrenamiento y prueba
Para ejecutar la división de COCO:
python cocosplit.py --having-annotations \
--multi-class \
-s 0.8 \
source_coco_annotations.json \
train.json \
test.json
Recuerda agregar la propiedad licenses al principio del json del conjunto de datos, en algún lugar después del primer “{”. Esta herramienta de división realmente lo requiere.
"licenses": [],
Configuración
Sí, las configuraciones del modelo son complicadas.
Pero mask-rcnn es bastante rápido y tiene una tasa de detección razonable. Ver aquí para los detalles de la configuración: https://mmdetection.readthedocs.io/en/latest/user_guides/train.html#train-with-customized-datasets
# La nueva configuración hereda una configuración base para destacar la modificación necesaria
_base_ = '/home/someusername/mmdetection/configs/mask_rcnn/mask-rcnn_r50-caffe_fpn_ms-poly-1x_coco.py'
# También necesitamos cambiar el num_classes en la cabeza para que coincida con la anotación del conjunto de datos
model = dict(
roi_head=dict(
bbox_head=dict(num_classes=3),
mask_head=dict(num_classes=3)))
# Modificar la configuración relacionada con el conjunto de datos
data_root = '/home/someusername/'
metainfo = {
'classes': ('MyClass1', 'AnotherClass2', 'AndTheLastOne3'),
'palette': [
(220, 20, 60),
(20, 60, 220),
(60, 220, 20),
]
}
train_dataloader = dict(
batch_size=1,
dataset=dict(
data_root=data_root,
metainfo=metainfo,
ann_file='train.json',
data_prefix=dict(img='')))
val_dataloader = dict(
dataset=dict(
data_root=data_root,
metainfo=metainfo,
ann_file='test.json',
data_prefix=dict(img='')))
test_dataloader = val_dataloader
# Modificar la configuración relacionada con la métrica
val_evaluator = dict(ann_file=data_root+'test.json')
test_evaluator = val_evaluator
# Podemos usar el modelo preentrenado Mask RCNN para obtener un mejor rendimiento
load_from = 'https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_r50_caffe_fpn_mstrain-poly_3x_coco/mask_rcnn_r50_caffe_fpn_mstrain-poly_3x_coco_bbox_mAP-0.408__segm_mAP-0.37_20200504_163245-42aa3d00.pth'
# si te gustan las películas largas
# el valor predeterminado aquí, si recuerdo bien, es 12
train_cfg = dict(max_epochs=24)
Algo para ver si no se necesitan máscaras: https://mmdetection.readthedocs.io/en/latest/user_guides/single_stage_as_rpn.html
Entrenamiento
supongamos que tienes tu configuración del modelo en /home/someusername/myproject/models/configs/mask-rcnn_r50-caffe_fpn_ms-poly-1x_v1.0.py . El script de entrenamiento es una llamada estándar a la herramienta mmdetection:
cd ~/mmdetection
python tools/train.py \
/home/someusername/myproject/models/configs/mask-rcnn_r50-caffe_fpn_ms-poly-1x_v1.0.py \
--work-dir /home/someusername/myproject/work-dirs/my-object-detector-v1.0-mask-rcnn_r50-caffe_fpn_ms-poly-1x
Inferencia
Algunas documentaciones están aquí: https://mmdetection.readthedocs.io/en/latest/user_guides/inference.html
from mmdet.apis import DetInferencer
inferencer = DetInferencer(
model='/home/someusername/myproject/models/configs/mask-rcnn_r50-caffe_fpn_ms-poly-1x_v1.0.py',
weights='/home/someusername/myproject/work-dirs/my-object-detector-v1.0-mask-rcnn_r50-caffe_fpn_ms-poly-1x/epoch_12.pth')
# ejecútalo para un solo archivo:
# inferencer('demo/demo.jpg', out_dir='/home/someusername/myproject/test-output/1.0/', show=True)
# o para toda una carpeta
inferencer('/home/someusername/myproject/test-images/', out_dir='/home/someusername/myproject/test-output/1.0/', no_save_pred=False)
Enlaces útiles
Espero que esto te ayude de alguna manera.
Otras lecturas útiles por favor consulte en
- Sitio web de Label Studio: https://labelstud.io/
- Documentaciones de MMDetection: https://mmdetection.readthedocs.io/en/latest/get_started.html
- Hoja de trucos de Bash
- Detectando barras de refuerzo de concreto con tensorflow
- Hoja de trucos de Python
- Hoja de trucos de Conda
- Flux texto a imagen
- Hoja de trucos de Ollama
- Hoja de trucos de Docker
- Lambda con capas en AWS SAM y Python
- Generando PDF en Python - Librerías y ejemplos"