Стратифицированный K-кратный для обнаружения объектов нескольких классов?

#python #scikit-learn #object-detection #cross-validation

#python #scikit-learn #обнаружение объектов #перекрестная проверка

Вопрос:

Обновлено

Я загрузил фиктивный набор данных, ссылка здесь. The df.head() :

введите описание изображения здесь

Всего в нем 4 класса и df.object.value_counts() :

 human    23
car      13
cat       5
dog       3
  

Я хочу правильно выполнить K-Fold разделение проверки по набору данных обнаружения объектов нескольких классов.

Первоначальный подход

Чтобы добиться правильного разделения проверки в k раз, я принял object counts bounding box во внимание и количество. Я понимаю, K-fold стратегии разделения в основном зависят от набора данных (метаинформации). Но на данный момент с этим набором данных я попробовал что-то вроде следующего:

 skf = StratifiedKFold(n_splits=3, shuffle=True, random_state=101)
df_folds = main_df[['image_id']].copy()

df_folds.loc[:, 'bbox_count'] = 1
df_folds = df_folds.groupby('image_id').count()
df_folds.loc[:, 'object_count'] = main_df.groupby('image_id')['object'].nunique()

df_folds.loc[:, 'stratify_group'] = np.char.add(
    df_folds['object_count'].values.astype(str),
    df_folds['bbox_count'].apply(lambda x: f'_{x // 15}').values.astype(str)
)

df_folds.loc[:, 'fold'] = 0
for fold_number, (train_index, val_index) in enumerate(skf.split(X=df_folds.index, y=df_folds['stratify_group'])):
    df_folds.loc[df_folds.iloc[val_index].index, 'fold'] = fold_number

  

После разделения я проверил, работает ли он. И пока все в порядке.

введите описание изображения здесь

Все сгибы содержат стратифицированные k-fold выборки len(df_folds[df_folds['fold'] == fold_number].index) и не пересекаются друг с другом, set(A).intersection(B) где A и B — значение индекса ( image_id ) двух сгибов. Но проблема, похоже,:

 Fold 0 has total: 18   2   3 = 23 bbox
Fold 1 has total: 2   11 = 13 bbox
Fold 2 has total: 5   3 = 8 bbox
  

Проблема

Однако я не мог убедиться, подходит ли это для такого типа задач в целом. Мне нужен совет. Подходит ли приведенный выше подход? или какая-либо проблема? или есть какой-то лучший подход! Любые предложения будут оценены. Спасибо.

Ответ №1:

При создании разделения перекрестной проверки мы заботимся о создании складок, которые имеют хорошее распределение различных «случаев», встречающихся в данных.

В вашем случае вы решили основывать свои сгибы на количестве автомобилей и количестве ограничивающих прямоугольников, что является хорошим, но ограниченным выбором. Итак, если вы можете определить конкретные случаи, используя свои данные / метаданные, вы можете попытаться создать более разумные сгибы, используя их.

Наиболее очевидный выбор — сбалансировать типы объектов (классы) в ваших сгибах, но вы могли бы пойти дальше.

Вот основная идея, допустим, у вас есть изображения с автомобилями, встречающимися в основном во Франции, и другие с автомобилями, встречающимися в основном в США, ее можно использовать для создания хороших фолдов со сбалансированным количеством французских и американских автомобилей в каждом фолде. То же самое можно сделать с погодными условиями и т. Д. Таким образом, каждый fold будет содержать репрезентативные данные для изучения, чтобы ваша сеть не была предвзятой для вашей задачи. В результате ваша модель будет более устойчивой к таким потенциальным реальным изменениям в данных.

Итак, можете ли вы добавить некоторые метаданные в свою стратегию перекрестной проверки, чтобы создать лучшее резюме? Если это не так, можете ли вы получить информацию о потенциальных угловых случаях, используя столбцы x, y, w, h вашего набора данных?

Затем вы должны попытаться сбалансировать сгибы с точки зрения выборок, чтобы ваши оценки оценивались при том же размере выборки, что уменьшит дисперсию и обеспечит лучшую оценку в конце.

Комментарии:

1. Спасибо за ваш совет. Среди всех приведенных ответов этот достаточно близок к временной шкале вознаграждения. Но я бы отметил как правильный ответ, если у него тоже есть минимальная демонстрация кода.

Ответ №2:

Вы можете использовать StratifiedKFold() или StratifiedShuffleSplit() напрямую для разделения вашего набора данных с использованием стратифицированной выборки на основе некоторого категориального столбца.

Фиктивные данные:

 import pandas as pd
import numpy as np

np.random.seed(43)
df = pd.DataFrame({'ID': (1,1,2,2,3,3),
               'Object': ('bus', 'car', 'bus', 'bus', 'bus', 'car'),
               'X' : np.random.randint(0, 10, 6),
               'Y' : np.random.randn(6)

})


df
  

Использование StratifiedKFold()

 from sklearn.model_selection import StratifiedKFold

skf = StratifiedKFold(n_splits=2)

for train_index, test_index in skf.split(df, df["Object"]):
        strat_train_set_1 = df.loc[test_index]
        strat_test_set_1 = df.loc[test_index]

print('train_set :', strat_train_set_1, 'n' , 'test_set :', strat_test_set_1)
  

Аналогично, если вы решите использовать StratifiedShuffleSplit() , вы можете иметь

 from sklearn.model_selection import StratifiedShuffleSplit

sss = StratifiedShuffleSplit(n_splits=1, test_size=0.2, random_state=42)
# n_splits = Number of re-shuffling amp; splitting iterations.

for train_index, test_index in sss.split(df, df["Object"]):
 # split(X, y[, groups]) Generates indices to split data into training and test set.

        strat_train_set = df.loc[train_index]
        strat_test_set = df.loc[test_index]

print('train_set :', strat_train_set, 'n' , 'test_set :', strat_test_set)
  

Комментарии:

1. Спасибо за ваш комментарий. Однако, я думаю, вы неправильно поняли мой запрос. Меня не волнует использование StratifiedKFold() или StratifiedShuffleSplit() . Моя задача — разработать правильную стратегию проверки для обнаружения объектов нескольких классов. И для этого мой подход заключался в том, чтобы учитывать object тип и количество bbox . Однако, если вы видите в моем запросе, я уже использую StratifiedKFold() .

Ответ №3:

Я бы сделал это просто, используя KFold метод scikit-learn of python

 from numpy import array
from sklearn.model_selection import KFold
data = array([0.1, 0.2, 0.3, 0.4, 0.5, 0.6])
kfold = KFold(3, True, 1)
for train, test in kfold.split(data):
    print('train: %s, test: %s' % (data[train], data[test]))
  

и, пожалуйста, посмотрите, может ли это быть полезным

Комментарии:

1. Можете ли вы объяснить, почему вы просто выбрали «KFold», не задумываясь о последствиях?

2. @M.Innat Какие последствия?