diff --git a/api/README.md b/api/README.md index 25bb991..02ad3d5 100644 --- a/api/README.md +++ b/api/README.md @@ -92,7 +92,7 @@ api/ │ │ ├── run_log.py records every training run to runs/ │ │ └── visualize.py matplotlib helpers (lazy import) │ ├── bird_cnn/ "bird" task group: config.py, tasks.py -│ ├── yolo/ "yolo" task group: config.py, tasks.py, dataset.py, loss.py, metrics.py, wider.py +│ ├── yolo/ "yolo" task group: config.py, tasks.py, dataset.py, loss.py, metrics.py, wider.py, merge.py │ ├── r_cnn/ "rcnn" task group: config.py, tasks.py, dataset.py, crops.py, detect.py │ └── production/ "server" task group + the FastAPI app │ ├── app.py FastAPI app, CORS, startup (load models, create DB) @@ -320,6 +320,8 @@ Code: `src/yolo/` · Defaults: `src/yolo/config.py` · Model: [`Yolo_model`](#yo | `--wider-dir` | `""` | convert-wider | WIDER FACE folder with `WIDER_train/`, `WIDER_val/` and `wider_face_split/`. | | `--wider-out-dir` | `data/wider_face` | convert-wider | Output folder; gets `train/` and `test/`. | | `--wider-min-face` | `2.0` | convert-wider | Drop faces smaller than this (px, after resizing to `--image-size`). | +| `--merge-dirs` | `""` | merge-coco | COCO folders to combine, comma separated. | +| `--merge-out-dir` | `""` | merge-coco | Output folder for the combined dataset. Must not exist yet. | | `--c-hidden` | `16` | all | Base channel count (c, 2c, 4c, 8c). | | `--boxes` | `1` | all | Boxes per cell. Only 1 is supported by the dataset, loss and decoder. | | `--labels` | `1` | all | Number of classes. Must equal the number of categories in the COCO file. | @@ -461,6 +463,33 @@ python -m src.main yolo.convert-wider --wider-dir ~/Downloads/wider_face --image python -m src.main yolo.train --train-dir data/wider_face/train --val-dir data/wider_face/test ``` + +#### `yolo.merge-coco` + +Combines two or more COCO folders into one, for example to train on WIDER and +`faces_scenery` together. Run it once per split. + +```bash +python -m src.main yolo.merge-coco \ + --merge-dirs data/wider_faces_coco/train,data/faces_scenery/train --merge-out-dir data/faces_combined/train +python -m src.main yolo.merge-coco \ + --merge-dirs data/wider_faces_coco/test,data/faces_scenery/test --merge-out-dir data/faces_combined/test +``` + +- **Images:** hard-linked into the output folder, so they take no extra disk + space. If the folders are on different drives, they are copied instead. + Each file is renamed `__`, e.g. + `faces_scenery_train__0001.jpg`, so names from different sources never clash. +- **Ids:** image and annotation ids are renumbered. +- **Categories:** merged by name and numbered 1, 2, … in order of first + appearance. The same class in both sources becomes one class. The task + prints the merged list; `--labels` must equal its length. +- **Checks:** before writing anything, it stops if a source has no annotation + file, if an image listed in it is missing, or if `--merge-out-dir` already exists. + +The sources are combined as they are. Prepare each one with the same +`--image-size` and `--grid` you train with (see `yolo.convert-wider`). + --- ### `rcnn.*`: R-CNN-style experiments @@ -1244,6 +1273,7 @@ folder. Unpack it in `api/` with `7z x data.7z`. | `faces/train/` | COCO | `rcnn.create-dataset` | Face set (3,910 images). | | `faces_processed/face/`, `background/` | ImageFolder | `rcnn.train-faces` | Crops made by `rcnn.create-dataset`. | | `wider_face/train/`, `test/` | COCO | `yolo.*` | Created by `yolo.convert-wider` from WIDER FACE, category 1 = face. Not in `data.7z`. | +| any `merge-coco` output | COCO | `yolo.*` | Created by `yolo.merge-coco` from other COCO folders. Not in `data.7z`. | | `football/train/`, `valid/` | COCO | `rcnn.*-football` | Roboflow football players: 1 = ball, 2 = goalkeeper, 3 = player, 4 = referee. | | `faces_2/`, `mnist/`, WIDER Face archives | various | none | Not used by the current code. | diff --git a/api/src/models/yolo.py b/api/src/models/yolo.py index d17139b..0705614 100644 --- a/api/src/models/yolo.py +++ b/api/src/models/yolo.py @@ -51,8 +51,7 @@ class Yolo_model(nn.Module): self.size_activation = size_activation self.model = nn.Sequential( - #nn.Conv2d(c_in, c_hidden, kernel_size=3, padding=1, stride=2), - nn.Conv2d(c_in, c_hidden, kernel_size=3, padding=1), + nn.Conv2d(c_in, c_hidden, kernel_size=3, padding=1, stride=2), nn.BatchNorm2d(c_hidden), nn.LeakyReLU(inplace=True), @@ -61,8 +60,7 @@ class Yolo_model(nn.Module): SkipBlock(c_in=c_hidden, c_out=c_hidden), SkipBlock(c_in=c_hidden, c_out=c_hidden), - #nn.Conv2d(c_hidden, c_hidden * 2, kernel_size=5, padding=2, stride=4), - nn.Conv2d(c_hidden, c_hidden * 2, kernel_size=3, padding=1, stride=2), + nn.Conv2d(c_hidden, c_hidden * 2, kernel_size=5, padding=2, stride=4), nn.LeakyReLU(inplace=True), SkipBlock(c_in=c_hidden * 2, c_out=c_hidden * 2), @@ -85,7 +83,6 @@ class Yolo_model(nn.Module): nn.BatchNorm2d(c_hidden * 8), nn.LeakyReLU(inplace=True), - nn.AdaptiveAvgPool2d((grid, grid)), nn.Conv2d(c_hidden * 8, boxes * 5 + labels, kernel_size=1) ) diff --git a/api/src/yolo/config.py b/api/src/yolo/config.py index 2210ceb..df06daa 100644 --- a/api/src/yolo/config.py +++ b/api/src/yolo/config.py @@ -20,6 +20,10 @@ class Config: wider_out_dir: str = "data/wider_face" wider_min_face: float = 2.0 # drop smaller faces (px after resizing to image_size) + # merge-coco: combine COCO folders into one + merge_dirs: str = "" # comma separated, e.g. data/wider_faces_coco/train,data/faces_scenery/train + merge_out_dir: str = "" + # Model c_hidden: int = 16 boxes: int = 1 diff --git a/api/src/yolo/tasks.py b/api/src/yolo/tasks.py index 9c370ae..7ce9647 100644 --- a/api/src/yolo/tasks.py +++ b/api/src/yolo/tasks.py @@ -17,6 +17,7 @@ from src.yolo.config import Config from src.yolo.dataset import YoloDataset from src.yolo.loss import YoloLoss from src.yolo.metrics import detection_metrics +from src.yolo.merge import merge_coco from src.yolo.wider import convert_wider @@ -150,4 +151,5 @@ TASKS = { "benchmark-workers": benchmark_workers, "webcam": webcam, "convert-wider": convert_wider, + "merge-coco": merge_coco, } diff --git a/api/train_test.sh b/api/train_test.sh index f6214f0..a911f38 100644 --- a/api/train_test.sh +++ b/api/train_test.sh @@ -2,8 +2,8 @@ for ((c = 1; c <= 32; c *= 2)); do - python -m src.main yolo.train --train-dir ./data/wider_faces_coco/train/ --val-dir ./data/wider_faces_coco/test/ \ - --no-augment-val --c-hidden "$c" --batch-size 64 --epochs 200 --run-name "bn-wider-faces-c$c" \ + python -m src.main yolo.train --train-dir ./data/faces_combined/train/ --val-dir ./data/faces_combined/test/ \ + --no-augment-val --c-hidden "$c" --batch-size 64 --epochs 200 --run-name "bn-combined-faces-c$c" \ --no-save --train-workers 4 --image-size 256 --grid 8 \ --learning-rate 1e-3 --weight-decay 0.05 --warmup-epochs 3 --min-learning-rate 1e-5 done