Update wiki: ROCm/Intel setup, correct log path, versioned tags, object mode

2026-06-13 06:39:15 +00:00
parent 0ee3f68599
commit 82244e09ae
4 changed files with 123 additions and 29 deletions
+9 -5
@@ -10,6 +10,8 @@ No. winnow only reads from Immich (assets, people, face bounding boxes). It neve
The `auto` strategy decides this for you — it keeps selecting until adding more images would be redundant. In practice this is usually 20–60 per person. You can cap it with `MAX_AUTO_IMAGES` (default 80).
When winnow reaches the cap on subsequent runs, `QUALITY_REPLACEMENT=true` (default) lets it swap out the lowest-scoring existing image if a better candidate is found — so the training set improves over time rather than stalling. Only images winnow uploaded are ever replaced; anything added manually through Frigate's UI is never touched.
Quality and diversity matter far more than volume. 30 well-spread images outperform 200 from the same week.
---
@@ -29,7 +31,7 @@ Yes — in object mode, `FRIGATE_URL` is not used and crops are saved to the out
## How does auto-diversity mode work?
winnow computes a vector embedding for each candidate image (what the face/object actually looks like — angle, lighting, expression). It then clusters those embeddings and picks representatives that are maximally spread across the embedding space. It stops when the next-most-different image is already close to something already selected. See the README for the full pipeline.
winnow computes a vector embedding for each candidate image (what the face/object actually looks like — angle, lighting, expression). It then clusters those embeddings and picks representatives that are maximally spread across the embedding space. It stops when the next-most-different image is already close to something already selected. See the README pipeline for the full breakdown.
---
@@ -41,11 +43,13 @@ Yes. By default it processes every named person in your Immich library. Use `ONL
## What GPU is needed?
Any NVIDIA GPU with CUDA 12.x support. The models (InsightFace Buffalo_L + SigLIP) fit comfortably in 4 GB VRAM. CPU mode works but is significantly slower — typically tens of seconds per person instead of under a second on GPU.
Any NVIDIA GPU with CUDA 12.x support works with `:latest`. AMD ROCm GPUs work with `:rocm`. Intel Arc and integrated GPUs work with `:intel`. The models (InsightFace Buffalo_L + SigLIP) fit comfortably in 4 GB VRAM.
**CPU memory:** If running without a GPU, set a container memory limit of at least **2 GB** (`mem_limit: 2g`). The models use roughly 1–1.5 GB and thumbnails are processed in bounded batches, so usage stays flat even for large libraries. Without a limit a large person library can trigger an OOM kill and restart loop.
CPU mode works but is significantly slower — typically tens of seconds per person instead of under a second on GPU.
ARM builds (linux/arm64) use CPU-only — CUDA is not available on ARM.
**CPU memory:** Set a container memory limit of at least **2 GB** (`mem_limit: 2g`). The models use roughly 1–1.5 GB and thumbnails are processed in bounded batches of 32, so usage stays flat regardless of library size.
ARM builds (linux/arm64) are CPU-only — GPU variants are amd64 only.
---
@@ -62,4 +66,4 @@ docker compose pull
docker compose up -d
```
The `latest` and `cpu` tags on GHCR track the `main` branch. Pinning to a specific version is recommended for stability, but winnow doesn't publish versioned image tags — `latest` / `cpu` are the stable release tags, and `dev` / `dev-cpu` are pre-release.
The `latest`, `cpu`, `rocm`, and `intel` tags on GHCR always point to the latest release. Versioned tags (e.g. `ghcr.io/sudolulo/winnow:v0.2.13`) are also published for each release and are useful for pinning to a known-good version.
+1 -1
@@ -1,6 +1,6 @@
# winnow
winnow pulls photos from [Immich](https://immich.app), selects diverse high-quality subsets using AI embeddings, and uploads them as face recognition training data to [Frigate](https://frigate.video).
winnow pulls photos from [Immich](https://immich.app), selects diverse high-quality subsets using AI embeddings, and delivers them as training data for [Frigate](https://frigate.video). It supports face recognition training (aligned crops uploaded directly to Frigate's API) and object classification training (YOLO-detected crops saved to disk).
It runs fully headless in Docker, is configured entirely through environment variables, and can run on a schedule.
+57 -12
@@ -4,7 +4,7 @@
- [Immich](https://immich.app) v1.106+ with face recognition enabled and people tagged
- [Frigate](https://frigate.video) v0.16+ (face mode only)
- Docker with the NVIDIA container toolkit (optional but strongly recommended)
- Docker with the appropriate GPU runtime (optional but strongly recommended)
---
@@ -23,16 +23,20 @@ This is the base URL of your Frigate instance, e.g. `http://192.168.1.10:5000`.
## 3. Choose an image tag
| Tag | Arch | GPU | Notes |
| :-- | :-- | :-- | :-- |
| `:latest` | amd64 + arm64 | CUDA 13.3 (amd64) | Requires NVIDIA Container Toolkit on amd64 |
| `:cpu` | amd64 | None | ~2 GB smaller; use if you have no NVIDIA GPU |
| Tag | Arch | Acceleration |
| :-- | :-- | :-- |
| `:latest` | amd64 + arm64 | NVIDIA CUDA 13.3 (amd64) · requires [NVIDIA Container Toolkit](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/install-guide.html) |
| `:rocm` | amd64 | AMD ROCm · pass `/dev/kfd` + `/dev/dri` |
| `:intel` | amd64 | Intel Arc / iGPU via OpenVINO · pass `/dev/dri`, set `OPENVINO_DEVICE=GPU` |
| `:cpu` | amd64 + arm64 | CPU only · ~2 GB smaller · no GPU required |
Use `:cpu` if your host has no NVIDIA GPU — it skips the entire CUDA stack and runs InsightFace on CPU. **Set a container memory limit of at least 2 GB** (`mem_limit: 2g`) — the embedding models need roughly 1–1.5 GB and thumbnails are batched, but without a limit an OOM kill will restart the container mid-run.
Use `:cpu` if your host has no supported GPU — it skips the entire GPU stack. **Set a container memory limit of at least 2 GB** (`mem_limit: 2g`) — the embedding models need roughly 1–1.5 GB.
---
## 4. Deploy with Docker Compose
Copy [`compose.yml`](../compose.yml) and [`.env.example`](../.env.example) to a directory on your host:
Copy [`compose.yml`](https://github.com/sudolulo/winnow/blob/main/compose.yml) and [`.env.example`](https://github.com/sudolulo/winnow/blob/main/.env.example) to a directory on your host:
```bash
mkdir winnow && cd winnow
@@ -102,19 +106,60 @@ In scheduled mode the process (and loaded models) stays resident between runs, s
## GPU passthrough
To enable GPU acceleration, include the `deploy` block in `compose.yml` (already present in the example) and ensure the NVIDIA container toolkit is installed on your host:
### NVIDIA
Include the `deploy` block in your `compose.yml` (present in the example) and ensure the [NVIDIA Container Toolkit](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/install-guide.html) is installed on your host:
```yaml
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: all
capabilities: [gpu]
```
Verify GPU access:
```bash
# Verify GPU is accessible to Docker
docker run --rm --gpus all nvidia/cuda:13.3.0-base-ubuntu22.04 nvidia-smi
```
CPU mode works without any GPU setup — set `FORCE_CPU=true` to disable GPU explicitly, or use the `:cpu` image tag which omits the CUDA base entirely (~2 GB smaller).
### AMD (ROCm)
Use `image: ghcr.io/sudolulo/winnow:rocm` and replace the `deploy:` block with:
```yaml
devices:
- /dev/kfd
- /dev/dri
group_add:
- video
- render
```
### Intel Arc / iGPU
Use `image: ghcr.io/sudolulo/winnow:intel` and replace the `deploy:` block with:
```yaml
devices:
- /dev/dri
group_add:
- render
environment:
- OPENVINO_DEVICE=GPU # omit to run OpenVINO inference on CPU (default)
```
Set `OPENVINO_DEVICE=GPU` to target the Intel GPU. Omitting it (or setting `CPU`) runs OpenVINO on CPU — useful if you want Intel's optimised runtime without GPU passthrough.
---
## Debugging
Set `VERBOSE=true` in your `.env` to enable DEBUG-level output on the console. The log file (`/app/winnow.log`) always captures DEBUG regardless of this setting — useful when diagnosing issues without a shell into the container:
Set `VERBOSE=true` in your `.env` to enable DEBUG-level output on the console. The log file always captures DEBUG regardless of this setting:
```bash
docker exec winnow cat /app/winnow.log
# log file is written to the output volume (frigate_train mount)
docker exec winnow cat /app/frigate_train/winnow.log
```
+56 -11
@@ -23,10 +23,10 @@ Your API key has been revoked or expired. Generate a new one in Immich → **Acc
Set `VERBOSE=true` in `.env` (or pass `-e VERBOSE=true`) to enable DEBUG-level output on the console. Useful for tracing exactly which images are being fetched, filtered, and selected.
The log file at `/app/winnow.log` always captures DEBUG regardless of `VERBOSE`:
The log file always captures DEBUG regardless of `VERBOSE`. It is written to the output volume:
```bash
docker exec winnow cat /app/winnow.log
docker exec winnow cat /app/frigate_train/winnow.log
```
---
@@ -59,23 +59,23 @@ winnow downloads InsightFace and HuggingFace (SigLIP) models on first run.
## Running on CPU (no GPU)
Use the `:cpu` image tag (`ghcr.io/sudolulo/winnow:cpu`) which omits the CUDA base entirely. Or set `FORCE_CPU=true` with `:latest` to disable GPU at runtime.
Use the `:cpu` image tag (`ghcr.io/sudolulo/winnow:cpu`) which omits the GPU base entirely. Or set `FORCE_CPU=true` with any other tag to disable GPU at runtime.
Everything works on CPU but embedding computation is slower — typically tens of seconds per person instead of under a second on GPU.
**Memory:** CPU mode requires more host RAM than GPU mode. Set a minimum container memory limit of **2 GB** (`mem_limit: 2g` in compose). If you have a large library — hundreds of images per person — allow more. Thumbnails are processed in bounded batches of 32 so peak RAM stays flat regardless of library size, but the embedding models themselves use roughly 1–1.5 GB.
**Memory:** Set a minimum container memory limit of **2 GB** (`mem_limit: 2g`). If you have a large library — hundreds of images per person — allow more.
---
## GPU not being used / "No GPU execution provider found"
## GPU not being used
If you have a GPU and the container is running but winnow reports no GPU provider:
### NVIDIA
1. **Confirm the GPU device is visible** inside the container:
1. **Confirm the GPU is visible** inside the container:
```bash
docker exec winnow nvidia-smi
```
If this fails, the container doesn't have GPU access — check the `deploy.resources.reservations.devices` block in `compose.yml` and that the NVIDIA container toolkit is installed on the host.
If this fails, the container doesn't have GPU access — check the `deploy.resources.reservations.devices` block in `compose.yml` and that the NVIDIA Container Toolkit is installed on the host.
2. **Check which ONNX providers are available**:
```bash
@@ -83,14 +83,59 @@ If you have a GPU and the container is running but winnow reports no GPU provide
"import onnxruntime as ort; ort.preload_dlls(cuda=True, cudnn=True); print(ort.get_available_providers())"
```
Expected (GPU working): `['CUDAExecutionProvider', 'CPUExecutionProvider', ...]`
GPU not working: `['AzureExecutionProvider', 'CPUExecutionProvider']` — only CPU providers listed.
GPU not working: only `['CPUExecutionProvider']` listed.
3. **If only CPU providers appear even though `nvidia-smi` works**, you may be running an image older than 0.2.11. A packaging bug in 0.2.10 caused the CPU `onnxruntime` package to silently overwrite `onnxruntime-gpu`, removing GPU support without any error. Update to `:latest` to fix it:
3. **If only CPU providers appear even though `nvidia-smi` works**, you may be running an image older than 0.2.11 — a packaging bug caused `onnxruntime-gpu` to be silently overwritten. Update to fix it:
```bash
docker compose pull && docker compose up -d
```
4. **Set `VERBOSE=true`** and check startup logs — winnow logs `ONNX providers available: [...]` at DEBUG level on every start.
### AMD (ROCm)
1. **Confirm devices are passed through** — your `compose.yml` should have:
```yaml
devices:
- /dev/kfd
- /dev/dri
group_add:
- video
- render
```
2. **Verify ROCm sees the GPU**:
```bash
docker exec winnow /app/.venv/bin/python3 -c "import torch; print(torch.cuda.is_available(), torch.cuda.get_device_name(0))"
```
ROCm exposes itself as CUDA in PyTorch — `True` here means ROCm is working.
### Intel Arc / iGPU
1. **Confirm the DRI device is passed through**:
```yaml
devices:
- /dev/dri
group_add:
- render
```
2. **Ensure `OPENVINO_DEVICE=GPU` is set** — without it, OpenVINO defaults to CPU even with device passthrough.
3. **Verify OpenVINO sees the device**:
```bash
docker exec winnow /app/.venv/bin/python3 -c \
"from openvino.runtime import Core; print(Core().available_devices)"
```
Expected: `['CPU', 'GPU']`. If only `['CPU']` appears, the Level Zero / OpenCL runtime can't see the device — check group membership and device permissions.
---
## Set `VERBOSE=true` and check startup logs
winnow logs `ONNX providers available: [...]` and the selected execution provider at DEBUG level on every start. Enable verbose mode and inspect the log:
```bash
docker exec winnow cat /app/frigate_train/winnow.log | grep -i "provider\|execution\|cuda\|rocm\|openvino"
```
---