Treine modelos em volumes, nuvens de pontos e vídeo
Ajuste uma U-Net 3D com máscaras de TC pintadas, execute o seu próprio detector OpenPCDet em cuboides de lidar e treine um rastreador de vídeo, cada um pontuado em itens reservados.
Novo na v2.14.0
Este tutorial treina três modelos em dados 3D e em movimento a partir das anotações de um estudo do Potato. volume-unet treina uma U-Net 3D que segmenta volumes de TC e RM, external-3d executa um detector de lidar que você mesmo compila e executa, e video-track treina um rastreador de objetos em vídeo. A página é para equipes que já anotam volumes, nuvens de pontos ou vídeo no Potato e querem um modelo que pré-rotule o restante. Para cada treinador, a página cobre o esquema que produz os dados de treinamento, um config que passa em potato validate, o que uma execução faz, a pontuação em itens reservados e como as predições chegam aos anotadores. O tutorial do primeiro modelo percorre a página Training com um classificador de texto que dispensa GPU, e este tutorial pressupõe essa explicação.
Resumo dos treinadores
Os três treinadores ajustam um esquema por execução, pontuam o modelo nos itens val e test reservados da execução e gravam de volta as predições como pré-rótulos que os anotadores editam. volume-unet e os detectores por trás de external-3d precisam de uma GPU na prática, então os configs desses dois treinadores enviam as execuções a um servidor com GPU por ssh, como o tutorial de treinamento remoto configura.
| Treinador | Esquema | Itens | Instalação na máquina de treinamento | Pontuação principal |
|---|---|---|---|---|
volume-unet | volume_annotation (máscaras pintadas) | volumes NIfTI, séries DICOM | pip install "potato-annotation[train-vision,volumes]"; uma GPU na prática | mean_dice |
external-3d | spatial_annotation (cuboides) | nuvens de pontos | o seu próprio detector (OpenPCDet, MMDetection3D) | map_3d_50 |
video-track | video_annotation no modo tracking ou combined | vídeo | pip install "potato-annotation[train-vision]" opencv-python | idf1 |
Todo treinador lê a mídia de arquivos em disco. O caminho de uma mídia deve apontar para dentro do diretório da tarefa ou de media_directory, e os itens cuja mídia é uma URL http(s) ficam fora do treinamento e da predição.
Segmente volumes de TC e RM com volume-unet
volume-unet aprende as máscaras que os anotadores pintam em um esquema volume_annotation e pinta máscaras nos volumes que ninguém segmentou ainda. O esquema mostra um arquivo NIfTI ou uma série DICOM em três vistas ligadas (axial, coronal e sagital), e os anotadores pintam cada rótulo com o pincel ou fechando um polígono. O campo do item que source_field nomeia contém um arquivo .nii ou .nii.gz, ou um diretório com uma série DICOM, relativo a media_directory. Uma caixa desenhada em um corte não tem máscara, então uma resposta só com caixas não é um alvo de treinamento. O Potato é um software de pesquisa sem autorização clínica ou regulatória, e um modelo treinado aqui propõe máscaras para os anotadores corrigirem.
O config abaixo segmenta o baço e o fígado, treina em um servidor com GPU e pré-rotula todo volume fora da divisão train:
annotation_task_name: "Spleen segmentation"
task_dir: .
media_directory: media
data_files:
- data/scans.jsonl
item_properties:
id_key: id
text_key: volume
output_annotation_dir: annotation_output
user_config:
allow_all_users: true
annotation_schemes:
- annotation_type: volume_annotation
name: organs
description: "Paint the spleen and the liver on every slice where each organ appears."
source_field: volume
labels:
- {name: Spleen, color: "#e6553a", key_value: "1"}
- {name: Liver, color: "#3a8ee6", key_value: "2"}
tools: [brush, eraser, polygon]
default_window: Soft tissue
model_training:
enabled: true
backend: gpu-server
backends:
gpu-server:
type: ssh
host: gpu.example.edu
python: /opt/conda/bin/python3
gpus: auto
min_free_gpu_mb: 8000
trainer_params:
volume-unet:
patch: 96
writeback:
schemas: [organs]
min_confidence: 0.6
include_unlabeled: trueCada linha de dados nomeia um volume:
{"id": "case_001", "volume": "case_001.nii.gz"}
{"id": "case_002", "volume": "case_002_ct"}O valor de patch define o lado do bloco cúbico em que a rede treina e deve ser um múltiplo de 4 nos três níveis padrão (2 elevado a levels menos 1). Um bloco de 96 voxels dá à rede mais contexto em torno de cada voxel que o padrão de 64 e precisa de mais memória de GPU. Com min_free_gpu_mb: 8000, o Potato inicia uma execução apenas em uma GPU com pelo menos 8.000 MB livres. O Potato toma a probabilidade média do modelo dentro da máscara de cada rótulo pintado e informa a menor dessas médias como a confiança do volume. Com min_confidence: 0.6, um volume em que algum órgão foi pintado com probabilidade média abaixo de 0,6 não recebe pré-rótulo.
Etapas de treinamento em uma execução de volume-unet
Uma execução lê cada volume anotado na ordem de voxels em que o widget pinta e transforma as máscaras em um rótulo por voxel. Onde dois rótulos se sobrepõem, vence o rótulo posterior na ordem de rótulos do pacote, como na exportação NIfTI. As intensidades são cortadas nos percentis 0,5 e 99,5 de cada volume e padronizadas. A rede é uma U-Net 3D em torch puro (três níveis, normalização de instância), treinada com entropia cruzada mais Dice suave em 100 blocos aleatórios por época. Metade dos blocos é centrada em um voxel pintado, de modo que estruturas pequenas aparecem no treinamento. Após cada época, a execução pontua o Dice médio nos volumes val e mantém a melhor época, e a execução para após 10 épocas sem uma pontuação melhor. A predição desliza o bloco por todo o volume com sobreposição de meio bloco e faz a média das probabilidades.
A divisão train precisa de pelo menos três volumes anotados (min_instances). Um volume cuja forma pintada difere da forma do arquivo fica de fora com um aviso no log da execução, e o mesmo vale para um volume cujo arquivo não existe. Com vários leitores em um volume, a execução treina com as máscaras do único leitor para o qual aggregation resolve, porque os agregadores comparam respostas inteiras. Segmente volumes de TC com vários leitores e STAPLE trata da concordância entre leitores e de uma máscara de consenso construída fora do Potato.
Pontuação em itens reservados para volumes
O Potato calcula o coeficiente de Dice para cada rótulo em cada volume reservado (dice_<label>) e informa a média sobre todos os volumes e rótulos como mean_dice. Um rótulo que o modelo omitiu em um volume que tem o rótulo recebe 0. O treinador escolhe a melhor época em val, então a tabela Runs toma a pontuação principal de test (test_mean_dice). A lista completa de pontuações por rótulo está na página da execução.
Pré-rótulos de máscara nas vistas de corte
As máscaras previstas voltam no formato próprio do widget, por rótulo e por corte axial, marcadas com prefilled_from: model, e aparecem nas vistas de corte quando um anotador abre o volume. O anotador corrige as máscaras com o pincel e a borracha, e as máscaras corrigidas são salvas como a resposta desse anotador. Itens da divisão train da execução nunca recebem pré-rótulo. Sem include_unlabeled, uma execução prevê apenas nos volumes val e test da execução.
Detecte cuboides 3D com external-3d
external-3d entrega as varreduras anotadas a um detector 3D que você instala no host de treinamento, executa os seus comandos de treinamento e de predição e lê as caixas previstas de volta como cuboides. O Potato não inclui um detector de nuvem de pontos. Os dois detectores para os quais o treinador foi feito, OpenPCDet e MMDetection3D, precisam de operadores CUDA compilados no host com GPU. Do lado do Potato, o treinador precisa apenas de numpy.
Os dados de treinamento vêm de um esquema spatial_annotation com a ferramenta cuboid_3d. Os anotadores posicionam caixas orientadas em varreduras PCD, PLY, LAS ou KITTI .bin, armazenadas em metros no referencial próprio da varredura (veja Nuvens de pontos 3D e cuboides). Os cuboides são os únicos alvos, então pontos, polilinhas e segmentos de pontos no mesmo esquema são ignorados. Cada item é uma varredura, e uma sequência de quadros com ids de trilha não é um alvo de treinamento.
Pasta de conjunto de dados gravada para o detector
Antes de o comando de treinamento executar, o Potato grava as varreduras em {dataset}, uma pasta chamada dataset dentro da pasta da execução, no layout de conjunto de dados personalizado do OpenPCDet:
points/<id>.npycontém uma varredura como linhas float32 dex y z intensity, com intensidade 0 quando o arquivo não tem nenhuma.labels/<id>.txtcontém uma linha por cuboide para as varredurastraineval, comox y z dx dy dz heading label. O heading é a rotação em torno de +z em radianos, e inclinação e rolagem não são mantidas. Um espaço em um nome de rótulo vira um sublinhado.ImageSets/train.txt,val.txtepredict.txtlistam os ids das varreduras.predict.txtlista as varredurasvaletest, mais as varreduras não rotuladas quandowriteback.include_unlabeledestá ativado.
Cada <id> são os primeiros 16 caracteres hexadecimais do SHA-1 do id do item, porque os ids dos itens podem conter caracteres que um nome de arquivo não aceita. Os rótulos das varreduras test nunca são gravados na pasta. O Potato grava uma varredura georreferenciada relativa à origem da varredura, porque coordenadas georreferenciadas são grandes demais para float32, e desloca as caixas previstas de volta pelo mesmo deslocamento.
Arquivos de predição lidos de volta do detector
O comando de predição grava um arquivo por varredura em {predictions}/<id>.txt, uma caixa por linha, como x y z dx dy dz heading label score. A pontuação é opcional. O Potato deixa de fora uma linha que não é analisada, contém nan ou inf, nomeia um rótulo que o esquema não tem ou descreve uma caixa sem tamanho, e o log da execução conta as linhas deixadas de fora. As caixas restantes voltam como objetos cuboid_3d marcados com source: model, e a pontuação de cada caixa se torna a confiança da caixa. O Potato define a confiança de uma varredura como a menor pontuação de caixa da varredura, e min_confidence se aplica a esse valor.
Comandos de treinamento e de predição
Os dois comandos são definidos como parâmetros do treinador. O Potato divide cada comando em palavras como um shell faria, substitui {dataset}, {workdir} (a pasta da execução) e {predictions} pelos caminhos correspondentes e executa o comando na pasta da execução sem um shell. Outras chaves em um comando ficam como escritas. Pipes, && e $VARIABLES chegam ao programa como palavras literais, então trabalho de várias etapas pertence a um script. Adicione este bloco ao config do estudo de nuvens de pontos:
model_training:
enabled: true
backend: gpu-server
backends:
gpu-server:
type: ssh
host: gpu.example.edu
python: /opt/conda/envs/pcdet/bin/python
gpus: auto
trainer_params:
external-3d:
train_command: "/opt/pcdet-jobs/train.sh {dataset} {workdir}"
predict_command: "/opt/conda/envs/pcdet/bin/python /opt/pcdet-jobs/predict.py {dataset} {workdir} {predictions}"
timeout_s: 43200
writeback:
schemas: [objects]
min_confidence: 0.3
include_unlabeled: trueOs caminhos estão no host de treinamento, onde a pasta da execução também fica. timeout_s limita cada comando, e 12 horas aqui substituem o padrão de 24. Um comando de treinamento que sai com código diferente de zero faz a execução falhar, e o erro da execução mostra as últimas linhas da saída do comando.
train.sh é o seu próprio script. Para o OpenPCDet, o script aponta o config de conjunto de dados personalizado do OpenPCDet para {dataset}, constrói os arquivos de informação com python -m pcdet.datasets.custom.custom_dataset create_custom_infos <dataset config>, executa o script de treinamento do OpenPCDet e salva o checkpoint em {workdir}. Os nomes de classe no config do detector devem coincidir com os nomes de rótulo como escritos em labels/. O guia de conjunto de dados personalizado do MMDetection3D lê nuvens de pontos apenas como arquivos .bin, então, para o MMDetection3D, o script primeiro grava cada points/<id>.npy como um arquivo .bin. Deixe train_command vazio para pular o treinamento, e predict_command passa a executar um modelo que você treinou em outro lugar.
predict.py lê os ids em predict.txt, executa o detector em cada varredura e grava as caixas. As funções load_model e detect são o seu próprio código em torno do detector que você treinou:
"""predict.py DATASET WORKDIR PREDICTIONS: one line per box, one file per scan."""
import os
import sys
import numpy as np
from my_detector import load_model, detect # your code around OpenPCDet or MMDetection3D
dataset, workdir, out = sys.argv[1:4]
model = load_model(os.path.join(workdir, "output")) # where train.sh saved the checkpoint
os.makedirs(out, exist_ok=True)
with open(os.path.join(dataset, "ImageSets", "predict.txt")) as fh:
scan_ids = fh.read().split()
for scan_id in scan_ids:
points = np.load(os.path.join(dataset, "points", scan_id + ".npy")) # float32, (N, 4)
with open(os.path.join(out, scan_id + ".txt"), "w") as fh:
for x, y, z, dx, dy, dz, heading, label, score in detect(model, points):
fh.write(f"{x} {y} {z} {dx} {dy} {dz} {heading} {label} {score}\n")Sem um train_command, o comando de predição é a execução inteira, e um comando de predição que sai com código diferente de zero faz a execução falhar. Um comando de predição que sai com código 0 mas não grava nenhum <id>.txt para qualquer scan, ou grava apenas linhas que o Potato deixa de fora, também conta como falha, porque uma execução pontuada com essa saída pareceria um modelo que não encontrou nada. Um arquivo vazio é um scan sem objetos. Depois de um comando de treinamento, um comando de predição que falha não faz a execução falhar. A execução termina com um aviso, "Prediction pass failed after 0 prediction(s)", e as últimas linhas do comando, e a execução não tem predições nem pontuação em itens reservados. Verifique o log da execução por esse aviso quando uma execução concluída mostrar "not scored".
Pontuação em itens reservados para cuboides
O Potato calcula a precisão média 3D por rótulo com um IoU volumétrico de 0,5 (ap_3d_50_<label>). O IoU é exato para cuboides orientados em qualquer rotação. O Potato também informa a média sobre os rótulos com IoU 0,25 e 0,5, e precisão, revocação e F1 em 0,5. A pontuação principal é map_3d_50. Com um train_command, a tabela Runs toma a pontuação principal primeiro de test, porque o comando de treinamento recebe os rótulos val em {dataset} e pode escolher um checkpoint com esses rótulos. Sem um train_command, o modelo nunca viu os itens val do estudo, e a pontuação principal vem de val.
Pré-rótulos de cuboide no visualizador de nuvem de pontos
Os cuboides previstos aparecem no visualizador de nuvem de pontos como caixas editáveis, e uma caixa editada é salva como a resposta do anotador. O Potato verifica cada objeto previsto antes de o visualizador desenhá-lo e deixa de fora um objeto cuja geometria não é analisada, um cuboide sem tamanho e um rótulo que o esquema não tem. Como anotar nuvens de pontos 3D trata do lado da anotação e da concordância em caixas 3D.
Rastreie objetos em vídeo com video-track
video-track aprende com as trilhas que os anotadores desenham em um esquema video_annotation no modo tracking ou combined e prevê trilhas nos vídeos que ninguém rastreou. Uma trilha é um rótulo e uma caixa em cada quadro-chave, e o widget interpola a caixa entre os quadros-chave. Um esquema video_annotation em outro modo armazena segmentos em vez de trilhas, e video-track não é oferecido para esse esquema.
annotation_task_name: "Pedestrian tracking"
task_dir: .
media_directory: media
data_files:
- data/clips.jsonl
item_properties:
id_key: id
text_key: video
output_annotation_dir: annotation_output
user_config:
allow_all_users: true
annotation_schemes:
- annotation_type: video_annotation
name: tracks
description: "Track every pedestrian and cyclist with a box at each keyframe."
source_field: video
mode: tracking
video_fps: 25
labels:
- {name: pedestrian, color: "#FF6B6B"}
- {name: cyclist, color: "#45B7D1"}
tracking_options:
interpolation: linear
auto_advance_frames: 5
frame_stepping: true
model_training:
enabled: true
trainer_params:
video-track:
frame_fps: 2
predict_fps: 5
max_age: 4
writeback:
schemas: [tracks]
include_unlabeled: trueCada linha de dados nomeia um clipe em media_directory:
{"id": "clip_001", "video": "/media/clip_001.mp4"}
{"id": "clip_002", "video": "/media/clip_002.mp4"}Defina video_fps como a taxa de quadros real dos clipes. Os quadros-chave são numerados por quadro, e o treinador numera os quadros-chave previstos no video_fps do esquema (30 quando não definido). O treinador lê a caixa em um quadro amostrado pela configuração interpolation de cada trilha (linear, Catmull-Rom cúbica ou constante). As caixas com que o modelo aprende, portanto, seguem o caminho que os anotadores viram. Como no widget, uma trilha com um só quadro-chave é mantida em todos os quadros, e uma trilha a que o widget deu um startFrame e um endFrame corre entre esses dois quadros. Um quadro-chave fora desses dois quadros ainda aparece no próprio quadro. predict_fps: 5 detecta em cinco quadros por segundo em vez de dois. Cinco quadros por segundo dão a um objeto que se move rápido mais quadros-chave e custam mais passagens do detector. Uma trilha termina após mais de max_age quadros amostrados sem uma detecção correspondente, então max_age: 4 a 5 quadros por segundo permite que uma trilha sobreviva a 0,8 segundo de detecções perdidas. Um pedestre que passa atrás de um poste mantém uma trilha quando o detector o encontra de novo em até 0,8 segundo. O config treina na máquina que executa o Potato (o backend local), e o backend gpu-server do config de volumes também funciona para video-track.
Treinamento e ligação de trilhas em uma execução de video-track
Uma execução amostra cada vídeo anotado a frame_fps quadros por segundo, do início ao último quadro-chave, e lê cada quadro amostrado com OpenCV. Os pixels do canvas dependem da tela do anotador, então o treinador lê a caixa de cada trilha nesse quadro como uma fração da imagem. Um quadro amostrado sem nenhuma trilha permanece no treinamento como exemplo de uma cena vazia. A execução ajusta um Faster R-CNN do torchvision nesses quadros, com os parâmetros e os padrões de torchvision-detect. O detector parte dos pesos COCO do torchvision. Após cada época, a execução pontua o mAP de caixas com IoU 0,5 nos quadros val (frame_map_50) e mantém a melhor época.
A predição amostra cada vídeo a predict_fps, detecta em cada quadro amostrado e liga as detecções em trilhas. As detecções de cada quadro são casadas um a um com as trilhas ativas do mesmo rótulo, pela sobreposição com a última caixa de cada trilha, e um casamento precisa de um IoU de pelo menos match_iou (0,3). Uma detecção sem casamento com pontuação de pelo menos new_track_score (0,5) inicia uma nova trilha. O treinador descarta uma trilha prevista com menos de min_track_keyframes detecções (2 por padrão) antes de gravá-la e pontuá-la. Sem isso, o widget manteria em todos os quadros uma trilha de uma só detecção. Toda detecção se torna um quadro-chave, então o widget interpola entre os quadros-chave do modelo como faz entre os de um anotador.
A divisão train precisa de pelo menos dois vídeos anotados (min_instances). Um vídeo que o OpenCV não consegue decodificar fica de fora com um aviso que nomeia o item, e a execução continua com o resto.
Pontuação em itens reservados para trilhas
O Potato compara as trilhas previstas com as trilhas anotadas em quadros amostrados a 2 por segundo em cada vídeo reservado, mais todo quadro-chave anotado. As pontuações são MOTA, IDF1, trocas de identidade, omissões e falsos positivos, e uma caixa conta como encontrada com IoU 0,5. O MOTA debita omissões, falsos positivos e trocas de identidade contra o número de caixas anotadas. O IDF1 é a pontuação F1 das caixas casadas com a identidade correta ao longo de todo o vídeo, e o IDF1 é a pontuação principal (idf1). O treinador escolhe a melhor época em val, então a tabela Runs toma a pontuação principal de test.
Pré-rótulos de trilha no widget de vídeo
As trilhas previstas preenchem o widget de vídeo como trilhas com quadros-chave marcados como saída de IA e com a pontuação do detector. O widget converte as caixas para os pixels do próprio canvas ao desenhar as trilhas, e o anotador corrige as trilhas como qualquer outra trilha. A confiança de um vídeo é a pontuação média dos quadros-chave do modelo. No modo combined, um pré-rótulo contém apenas trilhas, e o anotador adiciona os segmentos. Como anotar rastreamento de objetos em vídeo trata do lado da anotação, da oclusão e da concordância temporal.
Inicie uma execução e verifique o resultado
Reinicie o servidor depois de adicionar model_training, abra /admin/training, escolha o esquema e o treinador e clique em Train now. A página oferece apenas os treinadores que se ajustam a cada esquema e lista um treinador que não está instalado como "not installed", com o comando de instalação. Para verificar um servidor com GPU antes da primeira execução, execute:
potato training check gpu-server config.yamlA verificação informa a versão do Python remoto, cada GPU com a memória livre da GPU, a GPU que uma execução receberia e os pacotes de treinamento que faltam no interpretador remoto. Uma execução também pode começar pela API, com parâmetros por execução que substituem trainer_params:
curl -X POST -H "X-API-Key: $KEY" -H "Content-Type: application/json" \
http://localhost:8000/admin/training/api/runs \
-d '{"trainer": "video-track", "schemas": ["tracks"], "params": {"predict_fps": 10}}'Quando a execução termina, a tabela Runs lista a pontuação em itens reservados, com a divisão e a métrica em uma dica. Os pré-rótulos ancoram os anotadores, então ative include_unlabeled em um estudo novo quando a pontuação em itens reservados justificar mostrar as respostas do modelo. O tutorial do modelo no ciclo trata de servir primeiro os pré-rótulos de menor confiança e de retreinar com as correções, e Configuração e rotas do treinamento de modelos lista todas as chaves, os backends e as verificações de gravação de volta.
Leitura complementar
- Treinamento de modelos com as suas próprias anotações
- Treinadores de modelos no Potato, agrupados por tarefa
- O tutorial de modelos de visão, para detecção e segmentação 2D
- Rastreamento de vídeo e propagação de máscaras
- Anotação de vídeo
- Anotação de imagens médicas
- Consenso de máscaras com STAPLE
- Referência upstream para
volume-unet,external-3devideo-track
Referências
- Bernardin, K., and Stiefelhagen, R. (2008). Evaluating Multiple Object Tracking Performance: The CLEAR MOT Metrics. EURASIP Journal on Image and Video Processing. https://doi.org/10.1155/2008/246309
- Çiçek, Ö., Abdulkadir, A., Lienkamp, S. S., Brox, T., and Ronneberger, O. (2016). 3D U-Net: Learning Dense Volumetric Segmentation from Sparse Annotation. MICCAI 2016. https://arxiv.org/abs/1606.06650
- Ristani, E., Solera, F., Zou, R. S., Cucchiara, R., and Tomasi, C. (2016). Performance Measures and a Data Set for Multi-Target, Multi-Camera Tracking. ECCV 2016 Workshop on Benchmarking Multi-Target Tracking. https://arxiv.org/abs/1609.01775