Trening detektora obiektów AI za pomocą Label Studio i MMDetection
Etykietowanie i trening wymaga pewnego stopnia łączenia
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.
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
- uzyskiwanie danych źródłowych (obrazów do detekcji obiektów i klasyfikacji)
- etykietowanie obrazów i przygotowywanie zbioru danych
- rozwijanie nowego modelu lub znajdowanie odpowiedniego istniejącego
- trenowanie modelu, czasem z dostrajaniem hiperparametrów
- 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:
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
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
- Strona Label Studio: https://labelstud.io/
- Dokumentacja MMDetection: https://mmdetection.readthedocs.io/en/latest/get_started.html
- Bash Cheat Sheet
- Wykrywanie betonowych prętów z tensorflow
- Python Cheat Sheet
- Conda Cheat Sheet
- Flux tekst do obrazu
- Ollama Cheat Sheet
- Docker Cheat Sheet
- Warstwowe Lambdy z AWS SAM i Pythonem
- Generowanie PDF w Pythonie - biblioteki i przykłady"