- PVSM.RU - https://www.pvsm.ru -
Недавно на Kaggle закончилось соревнование iMaterialist Challenge (Furniture), задачей в котором было классифицировать изображения на 128 видов мебели и предметов быта (так называемая fine-grained classification, где классы очень близки друг к другу).
В этой статье я опишу подход, который принес нам с m0rtido [1] третье место, но прежде, чем переходить к сути, предлагаю воспользоваться для решения этой задачи естественной нейросетью в голове и разделить стулья на фото ниже на три класса.
Вы угадали? Я — тоже нет.
Но стоп, обо всём по порядку.
В соревновании нам был дан набор данных, в котором были представлены 128 классов обычных объектов быта, таких как стулья, телевизоры, сковородки и подушки в виде аниме-персонажей.
Тренировочная часть датасета состояла из ~190 тысяч изображений (точное число назвать сложно, потому что участникам был предоставлен только набор URL для скачивания, часть из которых, разумеется, не работала), причем распределение классов было далеко от равномерного (см. кликабельное изображение ниже).
Тестовый датасет был представлен 12800 картинками, причем был идеально сбалансирован: на каждый класс приходилось по 100 изображений. Также был выдан валидационный датасет, который тоже имел сбалансированное распределение классов и был ровно вдвое меньше тестового.
Метрикой оценивания задачи был .
В первую очередь мы скачали данные и просмотрели небольшую часть глазами. Вместо многих картинок скачалось изображение размером 1х1 или плейсхолдер с ошибкой. Такие изображения мы сразу удалили скриптом.
Было очевидно, что с имеющимся количеством изображений и ограничениями по времени обучать на этом датасете нейронные сети с нуля — не очень здравая мысль. Вместо этого мы использовали подход transfer learning, идея которого заключается в следующем: веса сети, обученной на одной задаче, можно использовать для совершенно другого набора данных и получить приличное качество, а то и вовсе прирост к точности по сравнению с обучением с нуля.
За счет чего это работает? Скрытые слои в глубоких нейронных сетях выступают в качестве feature extractor'ов, извлекая признаки, которые потом используются верхними слоями непосредственно для классификации.
Этим мы и воспользовались, дообучив ряд глубоких CNN, предварительно обученных на ImageNet. Для этих целей мы использовали Keras и его зоопарк моделей, где для загрузки готовой архитектуры было достаточно примерно такого кода:
base_model = densenet.DenseNet201(weights='imagenet',
include_top=False,
input_shape=(img_width, img_height, 3),
pooling='avg')
После этого мы извлекали из сети так называемые bottleneck-признаки (фичи на выходе с последнего сверточного слоя) и обучали поверх них softmax с дропаутом [4].
Затем, обученные веса «верхушки» мы соединяли со сверточной частью сети и обучали уже всю сеть сразу.
for layer in base_model.layers:
layer.trainable = True
top_model = Sequential()
top_model.add(Dropout(0.5, name='top_dropout', input_shape=base_model.output_shape[1:]))
top_model.add(Dense(128, activation='softmax', name='top_softmax'))
top_model.load_weights('top-weights-densenet.hdf5', by_name=True)
model = Model(inputs=base_model.input, outputs=top_model(base_model.output))
initial_lrate = 0.0005
model.compile(optimizer=Adam(lr=initial_lrate),
loss='categorical_crossentropy',
metrics=['accuracy'])
При подобной тонкой настройке сетей мы успели попробовать следующие хаки:
Почему? Частично ответ на этот вопрос представлен на картинке перед катом — классы были настолько похожими, что даже при исходной разметке данных люди, проставлявшие метки классов, не могли их различить, поэтому хорошей точности выжать из этих данных всё равно бы не удалось.
train_labels = utils.to_categorical(train_generator.classes)
y_integers = np.argmax(train_labels, axis=1)
class_weights = compute_class_weight('balanced', np.unique(y_integers), y_integers)
Сети, обученные таким образом, составили 90% нашего финального ансамбля.
Дисклеймер: никогда не повторяйте приём, описанный далее, в реальной жизни.
Итак, как мы определились в предыдущем разделе, bottleneck-фичи из сетей, обученных на ImageNet, могут быть использованы для классификации на других задачах. m0rtido [1] решил пойти дальше, и предложил следующую стратегию:
Получившийся в результате чудовищный стекинг давал огромную прибавку по точности к общему ансамблю.
После всего вышеописанного мы имели около двух десятков затюненных сверточных сетей, а также два стекинга bottleneck-признаков. Стоял вопрос: как из этого всего получить единое предсказание?
По-хорошему, в лучших традициях Kaggle мы должны были сделать стекинг [8] поверх всего этого, но для того, чтобы сделать OOF stacking нас не было ни времени, ни GPU, а обучение модели верхнего уровня на валидационном холдауте приводило к очень большому оверфиту. Поэтому мы решили реализовать довольно простой алгоритм жадного создания ансамбля:
В качестве метрики был выбран . Эта формула была подобрана эмпирически таким образом, чтобы и получались примерно одних масштабов. Такая интегральная метрика хорошо коррелировала с как на валидации, так и на публичном лидерборде.
Кроме того, тот факт, что на каждой итерации мы добавляли или удаляли одну модель (т.е. веса моделей всегда оставались целыми числами), играл роль своеобразной регуляризации, не позволяя ансамблю оверфититься под валидационный набор данных.
В итоге в ансамбль вошли следующие модели:
Автор: roryorangepants
Источник [9]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/python/283721
Ссылки в тексте:
[1] m0rtido: https://habr.com/users/m0rtido/
[2] Image: https://habr.com/post/414865/#habracut
[3] Image: https://habrastorage.org/webt/s4/df/jb/s4dfjblcnusqknbhcyqpnabzv0q.png
[4] дропаутом: https://en.wikipedia.org/wiki/Dropout_(neural_networks)
[5] FancyPCA: https://deshanadesai.github.io/notes/Fancy-PCA-with-Scikit-Image
[6] Spatial Transformer Network: https://habr.com/company/newprolab/blog/339484/
[7] с разбиением на фолды: http://scikit-learn.org/stable/modules/generated/sklearn.model_selection.cross_val_predict.html
[8] стекинг: http://blog.kaggle.com/2016/12/27/a-kagglers-guide-to-model-stacking-in-practice/
[9] Источник: https://habr.com/post/414865/?utm_campaign=414865
Нажмите здесь для печати.