- 15.03.2026
- 15.03.2026
- заметки
О чем это
Многометочные (multilabel) данные - данные, в которых у каждого примера может быть несколько меток одновременно.
Иногда нам нужно отрезать часть данных - чаще всего для тестовой выборки. Например, на 80% данных учимся, на 10% подбираем гиперпараметры, на оставшихся 10% смотрим, что получилось. Почти всегда хочется, чтобы распределение меток в разных подвыборках сохранилось.
Обычно такое желание возникает, когда мы строим классификатор - модель, которая предсказывает метки для каждой точки.
В чем проблема
Бывает, что у каждой точки в данных одна метка. На фотографии кошка или собака. Гриб съедобен или нет. Дали кредит или отказали.
В таком случае нам нужно посчитать, сколько примеров каждого класса должно быть в выборке, и соответственно поделить данные. Например, с помощью StratifiedKFold.
Многометочные данные сложнее. Например, книга может быть про любовь, Древний Рим и войну. Клиент может брать кредит, открывать вклад и брать банковскую гарантию. Магазин может торговать бакалеей и посудой одновременно.
Можно думать про многометочные данные как про несколько независимых датасетов и отдельно строить классификаторы - книга про Рим или не про Рим, про любовь или нет и так далее.
Так иногда и поступают, но когда категорий много, это неудобно. На 300 категорий нужно 300 моделей. Иногда категории связаны между собой - например, если про Рим, то и про войну наверняка тоже. Модели может быть удобно знать все метки сразу.
Если бы у нас были бесконечные патроны, можно было бы честно сформулировать разбиение на фолды как оптимизационную задачу и решить ее с помощью какого-нибудь сольвера. Но с большим датасетом это непрактично - не доживем до конца расчета.
Как быть
В реальных многометочных данных метки встречаются неравномерно. Например, у нас 26 классов, и к самому частому из них принадлежит 40% точек, а к самому редкому - 0,04%. Именно с такой ситуацией я столкнулся в 2016 году, и не нашел подходящей библиотеки. Пришлось написать свою.
Позднее выяснилось, что библиотека была, и даже была хорошая статья, а я просто не умел искать. С тех пор примерно раз в год я кому-нибудь рассказываю, как это сделать. Если вы так же плохо гуглите, как я, вот решение:
- Выбираем случайно точки, чтобы заполнить самый редкий класс.
- Заполняем самый редкий из оставшихся.
- И так далее.
Например, в датасете 1000 растений, 10 из них ядовито, 50 - лекарственные (и часть из них ядовита), 200 красиво цветут. Нужно отобрать 10%
Для начала случайно выберем одно ядовитое. Затем дополним до 5 лекарственными. Потом докинем красивыми до 20. Потом на всякий случай проверим, хорошо ли получилось.
Чем неравномернее распределены метки, тем больше шансов, что у нас все получится с первого раза.
Алгоритм легко пишется руками. Есть, правда, несколько неочевидных тонкостей. Мою первоначальную версию коллеги здорово улучшили и ускорили, но я вам ее не продам. Возьмите лучше бесплатные библиотеки:
Ссылки
- On the Stratification of Multi-label Data статья с очень похожим алгоритмом
- iterative-stratification
- scikit-multilearn-ng библиотека и статья, на которой она основана
С библиотекой scikit-multilearn-ng вышло забавно. Автор статьи опубликовал библиотеку scikit-multilearn и какое-то время поддерживал ее, потом бросил.
Сайт с документацией прокис, пулл-реквесты автор не принимал, проблемы не решались, и неравнодушный разработчик форкнул библиотеку с новым именем scikit-multilearn-ng. Прошло два года, и он тоже не принимает пулл-реквесты и не решает проблемы. Если не лень - можете тоже форкнуть, попросить Claude Code пофиксить баги и опубликовать под именем scikit-multilearn-ng2, станете опенсоурс разработчиком.
Навеяно соревнованием киберполка.