Skip to content

feat: yolov8 #380

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 21 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions label_studio_ml/examples/yolov8/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
FROM python:3.11-slim

RUN apt-get update && \
apt-get upgrade -y && \
apt-get install -y git wget && \
apt-get -y install ffmpeg libsm6 libxext6 libffi-dev python3-dev gcc

ENV PYTHONUNBUFFERED=True \
PORT=9090

WORKDIR /tmp
COPY requirements.txt .

RUN pip install --no-cache-dir -r requirements.txt

RUN wget https://github.com/ultralytics/assets/releases/download/v0.0.0/yolov8n-oiv7.pt
RUN wget https://github.com/ultralytics/assets/releases/download/v0.0.0/yolov8n.pt

COPY uwsgi.ini /etc/uwsgi/
COPY supervisord.conf /etc/supervisor/conf.d/

WORKDIR /app

RUN mkdir -p datasets/temp/images
RUN mkdir -p datasets/temp/labels

COPY * /app/

EXPOSE 9090

CMD ["/usr/local/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"]
71 changes: 71 additions & 0 deletions label_studio_ml/examples/yolov8/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
This project integrates the YOLOv8 model with Label Studio.



https://github.com/HumanSignal/label-studio-ml-backend/assets/106922533/82f539f1-dbee-47bf-b129-f7b5df83af43



## How The Project Works

This project helps you detect objects in Label Studio by doing two things.

1 - Uses a pretrained YOLOv8 model on Google's Open Images V7 (OIV7) to provide a pretrained model on 600 classes!

2 - Use a custom model for classes in cases that don't fit under the 600 classes in the OIV7 dataset

While annotating in label studio, you predefine which one of your labels overlap with the first pretrained model and custom labels that don't fit under the 600 classes are automatically used in the second custom model for predictions that is trained as you submit annotations in Label Studio.

Predictions are then gathered using the OIV7 pretrained model and the custom model in Label Studio in milliseconds, where you can adjust annotations and fine tune your custom model for even more precise predictions.


## Setup

1. Defining Classes for Pretrained and Custom Models

Edit your labeling config to something like the following

```xml
<View>
<Image name="image" value="$image"/>
<RectangleLabels name="label" toName="image">
<Label value="cats" background="red"/>
<Label value="cars" background="purple"/>
<Label value="taxi" background="orange"/>
<Label value="lights" background="green"/>
</RectangleLabels>
</View>
```

In the `class_matching.yml` edit the `labels_to_coco` dictionary to where the keys are the exact names of your rectangular labels in label studio and the values are the exact names of the same classes in [open-images-v7.yaml](https://github.com/ultralytics/ultralytics/blob/main/ultralytics/cfg/datasets/open-images-v7.yaml).

Any classes in your labeling config that you do not add to the `labels_to_coco` dictionary in `class_matching.yml` will be trained using the second, custom model.

In the `all_classes` dictionary add all of the classes in your Label Studio labeling config that are under the rectangular labels.

Note: if you leave the `labels_to_coco` dictionary empty with no keys and values, only the custom model will be trained and then used for predictions. In such a case, the model trained on 600 classes will not be used at all.

2. Editing `docker-compose.yml`

Set `LABEL_STUDIO_HOST` to your private IP address (which starts with 192 so ex. 192.168.1.1) with the port that label studio is running on. For example, your docker compose may look like `LABEL_STUDIO_HOST=192.168.1.1:8080`

Set `LABEL_STUDIO_ACCESS_TOKEN` by going to your Label Studio Accounts & Settings, and then copying the Access Token. Paste it into the docker file. Ex. `LABEL_STUDIO_ACCESS_TOKEN=cjneskn2keoqpejleed8d8frje9992jdjdasvbfnwe2jsx`

3. Running the backend

Run `docker compose up` to start the backend. Under the `Machine Learning` settings in your project in Label Studio enter the following URL while adding the model: `http://{your_private_ip}:9090`. Note: if you changed the port before running the backend, you will have to change it here as well.

## Training With ML Backend

In the machine learning tab for label studio, make sure the first toggle for training the model when annotations are submitted is turned on. This will allow training the custom model for custom classes that you defined in the previous steps when you submit annotations.

If you would like to train multiple images at once, which is preferred, run label studio from docker using the [`feature/batch-train`](https://github.com/HumanSignal/label-studio/tree/feature/batch-train) branch. Under the app and inside the environment variables in the `docker-compose.yml` add `EXPERIMENTAL_FEATURES=True`. Then, run the instance.

In the task menu, select all the tasks you would like to train your ML backend custom model on and under the toggle menu in the top left hand corner, select `Batch Train` and select `Ok` in the next popup menu.


## Notes

If you would like to save your model inside of your docker container or move it into your local machine, you will need to access the terminal of your docker container. See how to do this [here](https://stackoverflow.com/a/30173220).

If you want to train a new custom model, move the `yolov8n(custom).pt` out of your container's directory. It will automatically realize there is no custom model, and will create a new one from scratch to use when training custom models.
114 changes: 114 additions & 0 deletions label_studio_ml/examples/yolov8/_wsgi.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import os
import argparse
import json
import logging
import logging.config

logging.config.dictConfig({
"version": 1,
"formatters": {
"standard": {
"format": "[%(asctime)s] [%(levelname)s] [%(name)s::%(funcName)s::%(lineno)d] %(message)s"
}
},
"handlers": {
"console": {
"class": "logging.StreamHandler",
"level": os.getenv('LOG_LEVEL'),
"stream": "ext://sys.stdout",
"formatter": "standard"
}
},
"root": {
"level": os.getenv('LOG_LEVEL'),
"handlers": [
"console"
],
"propagate": True
}
})

from label_studio_ml.api import init_app
from model import YOLO_LS


_DEFAULT_CONFIG_PATH = os.path.join(os.path.dirname(__file__), 'config.json')


def get_kwargs_from_config(config_path=_DEFAULT_CONFIG_PATH):
if not os.path.exists(config_path):
return dict()
with open(config_path) as f:
config = json.load(f)
assert isinstance(config, dict)
return config


if __name__ == "__main__":
parser = argparse.ArgumentParser(description='Label studio')
parser.add_argument(
'-p', '--port', dest='port', type=int, default=9090,
help='Server port')
parser.add_argument(
'--host', dest='host', type=str, default='0.0.0.0',
help='Server host')
parser.add_argument(
'--kwargs', '--with', dest='kwargs', metavar='KEY=VAL', nargs='+', type=lambda kv: kv.split('='),
help='Additional LabelStudioMLBase model initialization kwargs')
parser.add_argument(
'-d', '--debug', dest='debug', action='store_true',
help='Switch debug mode')
parser.add_argument(
'--log-level', dest='log_level', choices=['DEBUG', 'INFO', 'WARNING', 'ERROR'], default=None,
help='Logging level')
parser.add_argument(
'--model-dir', dest='model_dir', default=os.path.dirname(__file__),
help='Directory where models are stored (relative to the project directory)')
parser.add_argument(
'--check', dest='check', action='store_true',
help='Validate model instance before launching server')

args = parser.parse_args()

# setup logging level
if args.log_level:
logging.root.setLevel(args.log_level)

def isfloat(value):
try:
float(value)
return True
except ValueError:
return False

def parse_kwargs():
param = dict()
for k, v in args.kwargs:
if v.isdigit():
param[k] = int(v)
elif v == 'True' or v == 'true':
param[k] = True
elif v == 'False' or v == 'false':
param[k] = False
elif isfloat(v):
param[k] = float(v)
else:
param[k] = v
return param

kwargs = get_kwargs_from_config()

if args.kwargs:
kwargs.update(parse_kwargs())

if args.check:
print('Check "' + YOLO_LS.__name__ + '" instance creation..')
model = YOLO_LS(**kwargs)

app = init_app(model_class=YOLO_LS)

app.run(host=args.host, port=args.port, debug=args.debug)

else:
# for uWSGI use
app = init_app(model_class=YOLO_LS)
13 changes: 13 additions & 0 deletions label_studio_ml/examples/yolov8/class_matching.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Note: check here to match class names
# https://github.com/ultralytics/ultralytics/blob/main/ultralytics/cfg/datasets/open-images-v7.yaml
# the key should be what you've named your label, the value is what class it matches from the link above
labels_to_coco:
cats: Cat
lights: Traffic light
cars: Car
all_classes:
- "cats"
- "cars"
- "taxi"
- "lights"
- "others"
7 changes: 7 additions & 0 deletions label_studio_ml/examples/yolov8/custom_config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
names:
0: taxi
1: other
path: ./temp
test: null
train: images
val: images
32 changes: 32 additions & 0 deletions label_studio_ml/examples/yolov8/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
version: "3.8"

services:
redis:
image: redis:alpine
container_name: redis
hostname: redis
volumes:
- "./data/redis:/data"
expose:
- 6379
server:
container_name: ml-backend
build: .
environment:
- MODEL_DIR=/data/models
- LOG_LEVEL=DEBUG
- LABEL_STUDIO_HOST=
- LABEL_STUDIO_ACCESS_TOKEN=
- RQ_QUEUE_NAME=default
- REDIS_HOST=redis
- REDIS_PORT=6379
- LABEL_STUDIO_USE_REDIS=true
ports:
- "9090:9090"
depends_on:
- redis
links:
- redis
volumes:
- "./data/server:/data"
- "./logs:/tmp"
Loading