Treinando um Detetor de Objetos AI com Label Studio & MMDetection
A etiquetagem e o treinamento exigem algum colagem.
Quando eu treinei um detector de objetos AI há algum tempo - o LabelImg foi uma ferramenta muito útil, mas a exportação do Label Studio para o formato COCO não era aceita pela estrutura MMDetection..
Ela precisava de algumas ferramentas e scripts para que tudo funcionasse.
Listando aqui os bits faltantes e alguns scripts alguns dos quais encontrei na internet e outros que escrevi eu mesmo.
Passos básicos
Preparar, treinar e usar IA envolve
- obter dados fonte (imagens para detecção de objetos e classificação)
- rotular imagens e preparar o conjunto de dados
- desenvolver um novo modelo ou encontrar um existente adequado
- treinar o modelo, às vezes com ajuste de parâmetros hiper
- usar o modelo para prever rótulos para novas imagens (ingerring)
Aqui estou dando passos exatos para rotular dados com o Label Studio (etapa 2) e treinar com mmdetection e torchvision (etapa 4), e tocando na inferência (etapa 5)
Label Studio
Ele tem algumas raízes abertas no programa LabelImg mas agora é desenvolvido e mantido de forma muito centralizada. E tem uma versão empresarial, é claro.
Ainda assim, está disponível para auto-hospedagem, o que é muito bom.
Configurar e executar
É possível instalar e executar o Label Studio de várias formas, como por exemplo, o pacote pip, ou um grupo de contêineres docker compose. Aqui estou usando um único contêiner docker.
Preparar a pasta de origem
mkdir ~/ls-data
sudo chown -R 1001:1001 ~/ls-data10
Configuração de armazenamento local
mkdir ~/ai-local-store
mkdir ~/ai-local-labels
# configurar algumas permissões adicionais
Na pasta ~/ai-local-store você mantém os arquivos de imagem, ~/ai-local-labels - rótulos sincronizados.
Iniciar o contêiner 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 - devido aos redirecionamentos da interface web do LS. DATA_UPLOAD_MAX_NUMBER_FILES … Django restringiu o número de arquivos carregados para 100 e teve um efeito terrível no Label Studio, então foi necessário fornecer esse novo limite. Todas as outras configurações estão muito bem documentadas no Label Studio docs.
Importar configuração. Nos ajustes do projeto no Armazenamento em Nuvem, adicione um Armazenamento de Arquivos Locais do tipo similar a:
Não se esqueça de marcar “Tratar cada objeto do bucket como um arquivo fonte” se você planeja sincronizar imagens (acho que sim), não os jsons. Se você já tem alguns rótulos para essas imagens em um json separado - você apenas configura esse Armazenamento em Nuvem. Não clicar em Sincronizar. E então Importar o json.
Configure o mesmo para o Armazenamento em Nuvem de Destino e ai-local-labels - se quiser sincronizar para fora.
Importar dados previamente rotulados no Label Studio
Adoro o formato JSON COCO. Um VOC Pascal também. Mas eles não são diretamente compatíveis com o Label Studio, então é necessário converter para o formato proprietário do LS primeiro.
Mas antes disso, provavelmente será necessário filtrar o conjunto de dados. Você pode precisar apenas de alguns rótulos ali, não de todos. Gosto do script filter.py do 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
Okay, agora, instale o conversor. Como o site oficial do conversor recomenda:
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 .
Converter de COCO e definir a pasta correta
label-studio-converter import coco -i your-input-file.json -o output.json
Importar o output.json clicando no botão Importar no Label Studio.
Rotulagem
Muita criatividade extremamente original é feita aqui.
Exportação
Após clicar no botão Exportar no Label Studio e selecionar o formato de exportação COCO - dê uma olhada dentro desse arquivo e admire os nomes das imagens. Eles pareceriam assim se você importou rótulos antes sem substituir o caminho base da imagem
"images": [
{
"width": 800,
"height": 600,
"id": 0,
"file_name": "\/data\/local-files\/?d=\/iteration1001\/123.jpg"
},
Ou pareceriam assim se você sincronizou o armazenamento em nuvem externo.
"images": [
{
"width": 800,
"height": 600,
"id": 0,
"file_name": "http:\/\/localhost:8080\/data\/local-files\/?d=iteration1001\/123.jpg"
},
Esses não são muito agradáveis. queremos algo como
"images": [
{
"width": 800,
"height": 600,
"id": 0,
"file_name": "iteration1001/123.jpg"
},
Para corrigir os nomes dos arquivos usei scripts maravilhosos. Eles estão sobrescrevendo o arquivo result.json, então, se você precisar de um backup antes disso - cuide disso você mesmo:
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 dados e banco de dados do Label Studio
Para parar cuidadosamente seu contêiner docker do Label Studio e depois executar algo como
cp ~/ls-data ~/all-my-backups
cp ~/ai-local-store ~/all-my-backups
cp ~/ai-local-labels ~/all-my-backups
Mesclar
Às vezes é necessário mesclar vários conjuntos de dados em um, especialmente se estiver executando várias iterações.
Usei a ferramenta COCO-merger. Após instalar e executar com o 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
Sim. pode mesclar apenas dois arquivos. Então, se você tiver 10 iterações - precisa fazer um esforço extra. Ainda gosto disso.
MMDetection
Dividindo o conjunto de dados
Para dividir o conjunto de dados em treinamento e teste usei a ferramenta COCOSplit.
git clone https://github.com/akarazniewicz/cocosplit.git
cd cocosplit
pip install -r requirements
Não há muito a fazer:
$ python cocosplit.py -h
usage: cocosplit.py [-h] -s SPLIT [--having-annotations]
coco_annotations train test
Divide o arquivo de anotações COCO em conjuntos de treinamento e teste.
argumentos posicionais:
coco_annotations Caminho para o arquivo de anotações COCO.
train Onde armazenar as anotações de treinamento COCO
test Onde armazenar as anotações de teste COCO
argumentos opcionais:
-h, --help mostrar esta ajuda e sair
-s SPLIT Porcentagem de divisão; um número em (0, 1)
--having-annotations Ignorar todas as imagens sem anotações. Manter apenas aquelas com pelo menos uma anotação
--multi-class Dividir um conjunto de dados multi-classe preservando as distribuições de classe em treinamento e teste
Para executar a divisão do COCO:
python cocosplit.py --having-annotations \
--multi-class \
-s 0.8 \
source_coco_annotations.json \
train.json \
test.json
Lembre-se de adicionar a propriedade licenses no início do json do conjunto de dados, em algum lugar após o primeiro “{”. Essa ferramenta de divisão realmente quer isso.
"licenses": [],
Configuração
Sim, as configurações do modelo são complexas.
Mas o mask-rcnn é bastante rápido e tem uma taxa de detecção razoável. Veja aqui para detalhes da configuração: https://mmdetection.readthedocs.io/en/latest/user_guides/train.html#train-with-customized-datasets
# A nova config herda uma config base para destacar a modificação necessária
_base_ = '/home/someusername/mmdetection/configs/mask_rcnn/mask-rcnn_r50-caffe_fpn_ms-poly-1x_coco.py'
# Também precisamos alterar o num_classes no cabeçalho para corresponder às anotações do conjunto de dados
model = dict(
roi_head=dict(
bbox_head=dict(num_classes=3),
mask_head=dict(num_classes=3)))
# Modificar as configurações relacionadas ao conjunto de dados
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 as configurações relacionadas à métrica
val_evaluator = dict(ann_file=data_root+'test.json')
test_evaluator = val_evaluator
# Podemos usar o modelo pre-treinado Mask RCNN para obter um desempenho mais alto
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'
# se você gosta de filmes longos
# o padrão aqui, se eu me lembro corretamente, é 12
train_cfg = dict(max_epochs=24)
Algo para olhar se os masks não forem necessários: https://mmdetection.readthedocs.io/en/latest/user_guides/single_stage_as_rpn.html
Treinamento
Vamos supor que você tenha sua configuração do modelo em /home/someusername/myproject/models/configs/mask-rcnn_r50-caffe_fpn_ms-poly-1x_v1.0.py . O script de treinamento é uma chamada padrão para a ferramenta 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
Inferência
Alguns documentos estão aqui: 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')
# execute-o para um único arquivo:
# inferencer('demo/demo.jpg', out_dir='/home/someusername/myproject/test-output/1.0/', show=True)
# ou para toda a pasta
inferencer('/home/someusername/myproject/test-images/', out_dir='/home/someusername/myproject/test-output/1.0/', no_save_pred=False)
Links úteis
Espero que isso ajude você de alguma forma.
Outras leituras úteis, por favor, veja em
- Site do Label Studio: https://labelstud.io/
- Documentação do MMDetection: https://mmdetection.readthedocs.io/en/latest/get_started.html
- Folha de Dicas do Bash
- Detectando Reo Bar Caps de Concreto com tensorflow
- Folha de Dicas do Python
- Folha de Dicas do Conda
- Flux texto para imagem
- Folha de Dicas do Ollama
- Folha de Dicas do Docker
- Lambda Camadas com AWS SAM e Python
- Gerando PDF em Python - Bibliotecas e exemplos"