YOLO 학습 방식 자체를 수정하는 것이기 때문에 굉장히 복잡하고 번거로움
Balance Classes During YOLO Training Using a Weighted Dataloader
< YOLOWeightedDataset.py >
from ultralytics import YOLO
from ultralytics.data.dataset import YOLODataset
import ultralytics.data.build as build
import numpy as np
import matplotlib.pyplot as plt
import cv2
class YOLOWeightedDataset(YOLODataset):
def __init__(self, *args, mode="train", **kwargs):
"""
Initialize the WeightedDataset.
Args:
class_weights (list or numpy array): A list or array of weights corresponding to each class.
"""
super(YOLOWeightedDataset, self).__init__(*args, **kwargs)
self.train_mode = "train" in self.prefix
# You can also specify weights manually instead
self.count_instances()
class_weights = np.sum(self.counts) / self.counts
# Aggregation function
self.agg_func = np.mean
self.class_weights = np.array(class_weights)
self.weights = self.calculate_weights()
self.probabilities = self.calculate_probabilities()
def count_instances(self):
"""
Count the number of instances per class
Returns:
dict: A dict containing the counts for each class.
"""
self.counts = [0 for i in range(len(self.data["names"]))]
for label in self.labels:
cls = label['cls'].reshape(-1).astype(int)
for id in cls:
self.counts[id] += 1
self.counts = np.array(self.counts)
self.counts = np.where(self.counts == 0, 1, self.counts)
def calculate_weights(self):
"""
Calculate the aggregated weight for each label based on class weights.
Returns:
list: A list of aggregated weights corresponding to each label.
"""
weights = []
for label in self.labels:
cls = label['cls'].reshape(-1).astype(int)
# Give a default weight to background class
if cls.size == 0:
weights.append(1)
continue
# Take mean of weights
# You can change this weight aggregation function to aggregate weights differently
weight = self.agg_func(self.class_weights[cls])
weights.append(weight)
return weights
def calculate_probabilities(self):
"""
Calculate and store the sampling probabilities based on the weights.
Returns:
list: A list of sampling probabilities corresponding to each label.
"""
total_weight = sum(self.weights)
probabilities = [w / total_weight for w in self.weights]
return probabilities
def __getitem__(self, index):
"""
Return transformed label information based on the sampled index.
"""
# Don't use for validation
if not self.train_mode:
return self.transforms(self.get_image_and_label(index))
else:
index = np.random.choice(len(self.labels), p=self.probabilities)
return self.transforms(self.get_image_and_label(index))
< YOLO_Class_Weighted_train.py >
from ultralytics import YOLO
from ultralytics.data.dataset import YOLODataset
import ultralytics.data.build as build
from YOLOWeightedDataset import YOLOWeightedDataset
build.YOLODataset = YOLOWeightedDataset
model = YOLO("/home/elicer/yolov8m-seg.pt")
model.train(
data="YOUR_data_yaml_PATH",
epochs=10,
imgsz=640,
batch=16,
workers=8,
save=True,
project="YOUR_RESULT_PATH",
name="class_weighted_train",
resume=False,
lr0=0.005,
#freeze=0
)

원인
- YOLO 학습 알고리즘 내부적으로 Class Weight를 적용하는 부분이 존재함
- Monkey-Patch와의 충돌로 정상작동하지 않음
소수이지만 중요도가 높은 Driving Area / Parking Area를 영역검출에 특화된 모델인 Segformer로 학습하여 YOLOv8-seg와 병합
객체검출 - YOLOv8-seg / 영역검출 - Segformer