Trening detektora obiektów AI za pomocą Label Studio i MMDetection

Etykietowanie i trening wymaga pewnego stopnia łączenia

Page content

Kiedyś treningowałem detektor AI obiektów – LabelImg był bardzo pomocnym narzędziem, ale eksport z Label Studio do formatu COCO nie był akceptowany przez framework MMDetection..

Wymagał to pewnych narzędzi i skryptów, aby wszystko działało.

topimage

W tym miejscu wymieniam brakujące elementy i niektóre skrypty które znalezłem w internecie, a inne napisałem sam.

Podstawowe kroki

Przygotowanie, trenowanie i wykorzystanie AI obejmuje

  1. uzyskiwanie danych źródłowych (obrazów do detekcji obiektów i klasyfikacji)
  2. etykietowanie obrazów i przygotowywanie zbioru danych
  3. rozwijanie nowego modelu lub znajdowanie odpowiedniego istniejącego
  4. trenowanie modelu, czasem z dostrajaniem hiperparametrów
  5. wykorzystywanie modelu do przewidywania etykiet dla nowych obrazów (ingerring)

Tu podaję dokładne kroki jak etykietować dane za pomocą Label Studio (krok 2) i trenować z mmdetection i torchvision (krok 4), a także dotknąć inferencji (krok 5)

Label Studio

Ma pewne korzenie w otwartym programie LabelImg ale teraz rozwijany i utrzymywany w bardzo centralizowany sposób. Ma oczywiście wersję enterprise.

Jednak nadal jest dostępny do samowysyłania, co jest bardzo przyjemne.

Konfiguracja i uruchomienie

Można zainstalować i uruchomić Label Studio na kilka sposobów, np. jako pakiet pip, lub jako grupę kontenerów docker compose. Tu używam pojedynczego kontenera docker.

Przygotuj folder źródłowy

mkdir ~/ls-data
sudo chown -R 1001:1001 ~/ls-data10

Konfiguracja lokalnego magazynu

mkdir ~/ai-local-store
mkdir ~/ai-local-labels

# ustawienie dodatkowych uprawnień

W ~/ai-local-store przechowujesz pliki obrazów, ~/ai-local-labels – zsynchronizowane etykiety.

Uruchom kontener 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 – z powodu przekierowań w interfejsie webui LS. DATA_UPLOAD_MAX_NUMBER_FILES … Django ograniczył liczbę przesyłanych plików do 100, co miało bardzo złe skutki dla Label Studio, więc trzeba było ustawić nowy limit. Wszystkie inne konfiguracje są bardzo dobrze opisane w dokumentacji Label Studio.

Import konfiguracji. W ustawieniach projektu w Cloud Storage dodaj źródło magazynu typu Local Files podobne do:

topimage

Nie zapomnij zaznaczyć “Traktuj każdy obiekt z koszyka jako plik źródłowy”, jeśli planujesz zsynchronizować obrazy (wierzę, że tak), nie jsony. Jeśli masz już pewne etykiety dla tych obrazów w oddzielnym json – po prostu skonfiguruj ten magazyn chmurowy. Nie klikaj Sync. A następnie zaimportuj json.

Skonfiguruj to samo dla magazynu chmurowego docelowego i ai-local-labels – jeśli chcesz zsynchronizować je zewnętrznie.

Zaimportowanie wcześniej etykietowanych danych do Label Studio

Lubię format JSON COCO. An także Pascal VOC. Ale nie są one bezpośrednio kompatybilne z Label Studio, więc najpierw trzeba je przekonwertować na własny format LS.

Ale zanim to zrobisz – prawdopodobnie będzie potrzebne filtracja zbioru danych. Możesz potrzebować tylko niektórych etykiet, nie wszystkich. Lubię skrypt filter.py z 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

Ok, teraz zainstaluj konwerter. Tak jak zaleca oficjalna strona konwertera:

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 . 

Konwertuj z COCO i ustaw poprawny folder

label-studio-converter import coco -i your-input-file.json -o output.json

Zaimportuj output.json klikając w przycisk Import w Label Studio.

Etykietowanie

Wiele bardzo kreatywnych prac jest wykonywanych tutaj.

Eksport

Po kliknięciu przycisku Eksport w Label Studio i wybraniu formatu eksportu COCO – zajrzyj do tego pliku i podziwiaj nazwy obrazów. Wyglądają one tak, jeśli wcześniej zaimportowałeś etykiety bez nadpisywania podstawowego adresu obrazu

  "images": [
    {
      "width": 800,
      "height": 600,
      "id": 0,
      "file_name": "\/data\/local-files\/?d=\/iteration1001\/123.jpg"
    },

Lub tak, jeśli zsynchronizowałeś zewnętrzny magazyn chmurowy.

  "images": [
    {
      "width": 800,
      "height": 600,
      "id": 0,
      "file_name": "http:\/\/localhost:8080\/data\/local-files\/?d=iteration1001\/123.jpg"
    },

To nie są bardzo przyjemne. chcemy coś takiego jak

  "images": [
    {
      "width": 800,
      "height": 600,
      "id": 0,
      "file_name": "iteration1001/123.jpg"
    },

Aby naprawić nazwy plików użyłem wspaniałych skryptów. Zastępują one plik result.json, więc jeśli potrzebujesz kopii zapasowej wcześniej – zadbaj o to sam:

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

Kopie zapasowe danych Label Studio i bazy danych

Delikatnie zatrzymaj swój kontener Label Studio docker a następnie uruchom coś takiego jak

cp ~/ls-data ~/all-my-backups
cp ~/ai-local-store ~/all-my-backups
cp ~/ai-local-labels ~/all-my-backups

Scalanie

Czasami trzeba scalić kilka zbiorów danych w jeden, zwłaszcza jeśli przeprowadzasz kilka iteracji.

Użyłem narzędzia COCO-merger. Po zainstalowaniu i uruchomieniu z parametrem -h:

python tools/COCO_merger/merge.py -h

Użycie COCO Files Merge

python -m COCO_merger.merge --src Json1.json Json2.json --out OUTPUT_JSON.json

Parser argumentów

usage: merge.py [-h] --src SRC SRC --out OUT

Scal dwa pliki z anotacjami w jeden plik

argumenty opcjonalne:
  -h, --help     Pokaż tę pomoc i wyjdź
  --src SRC SRC  Ścieżka do dwóch plików z anotacjami do scalenia
  --out OUT      Ścieżka do wyjściowego pliku z anotacjami

Tak. można scalić tylko dwa pliki. Więc jeśli masz 10 iteracji – trzeba wykonać dodatkowe wysiłki. Mimo to lubię to.

MMDetection

topimage

Dzielenie zbioru danych

Aby podzielić zbiór danych na trening i test użyłem narzędzia COCOSplit.

git clone https://github.com/akarazniewicz/cocosplit.git
cd cocosplit
pip install -r requirements

Nie ma za dużo do tego:

$ python cocosplit.py -h
usage: cocosplit.py [-h] -s SPLIT [--having-annotations]
                    coco_annotations train test

Dzieli plik anotacji COCO na zbiory treningowe i testowe.

argumenty pozycyjne:
  coco_annotations      Ścieżka do pliku anotacji COCO.
  train                 Gdzie zapisać anotacje treningowe COCO
  test                  Gdzie zapisać anotacje testowe COCO

argumenty opcjonalne:
  -h, --help            Pokaż tę pomoc i wyjdź
  -s SPLIT              Procentowy podział; liczba w przedziale (0, 1)
  --having-annotations  Ignoruj wszystkie obrazy bez anotacji. Zachowaj tylko te
                        z co najmniej jedną anotacją
  --multi-class         Podziel zbiór danych wieloklasowy, zachowując dystrybucję klas w zbiorach treningowych i testowych

Aby uruchomić podział COCO:

python cocosplit.py --having-annotations \
  --multi-class \
  -s 0.8 \
  source_coco_annotations.json \
  train.json \
  test.json

Pamiętaj, aby dodać właściwość licenses na początku pliku JSON zbioru danych, gdzieś po pierwszym “{”. To narzędzie podziału naprawdę tego potrzebuje.

  "licenses": [],

Konfiguracja

Tak, konfiguracje modeli są trudne.

Ale mask-rcnn jest dość szybki i ma rozsądną stopę detekcji. Zobacz tutaj szczegóły konfiguracji: https://mmdetection.readthedocs.io/en/latest/user_guides/train.html#train-with-customized-datasets

# Nowa konfiguracja dziedziczy bazową konfigurację, aby podkreślić konieczne modyfikacje
_base_ = '/home/someusername/mmdetection/configs/mask_rcnn/mask-rcnn_r50-caffe_fpn_ms-poly-1x_coco.py'

# Musimy również zmienić num_classes w nagłówku, aby dopasować do anotacji zbioru danych
model = dict(
    roi_head=dict(
        bbox_head=dict(num_classes=3),
        mask_head=dict(num_classes=3)))

# Zmodyfikuj ustawienia związane z zbiorem danych
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

# Zmodyfikuj ustawienia związane z metryką
val_evaluator = dict(ann_file=data_root+'test.json')
test_evaluator = val_evaluator

# Możemy użyć wstępnie wytrenowanego modelu Mask RCNN, aby uzyskać lepszą wydajność
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'

# jeśli lubisz długie filmy
# domyślnie tutaj, jeśli dobrze pamiętam, to 12
train_cfg = dict(max_epochs=24) 

Coś do obejrzenia, jeśli maski nie są potrzebne: https://mmdetection.readthedocs.io/en/latest/user_guides/single_stage_as_rpn.html

Trenowanie

Załóżmy, że masz swoją konfigurację modelu w /home/someusername/myproject/models/configs/mask-rcnn_r50-caffe_fpn_ms-poly-1x_v1.0.py . Skrypt trenowania to standardowy wywołanie narzędzia 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

Inferencja

Niektóre dokumenty są tutaj: 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')

# uruchom dla pojedynczego pliku:
# inferencer('demo/demo.jpg', out_dir='/home/someusername/myproject/test-output/1.0/', show=True)

# lub dla całego folderu
inferencer('/home/someusername/myproject/test-images/', out_dir='/home/someusername/myproject/test-output/1.0/', no_save_pred=False)

Przydatne linki

Mam nadzieję, że w jakiś sposób pomogłem.

Inne przydatne czytania znajdziesz na