Обучение с глубоким градиентом

Составные модели CNN XGBoost для классификации изображений земного покрова

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

Я увлечен анализом и классификацией изображений (особенно в том, что касается анализа спутниковых изображений для получения экономических данных для стран с развивающейся экономикой). Недавно я обнаружил, что, извлекая выходные данные векторного слоя из обученной CNN и передавая эти выходные данные в модель XGBoost, можно получить лучшую производительность классификации, чем с любой отдельной моделью.

Разделы:

Обзор набора данных

Случайный лес

XGBoost

Сверточная нейронная сеть

CNN с усилением градиента

Заключительные мысли

Обзор набора данных

В этом посте я буду ссылаться на методы классификации применительно к изображениям в наборе данных Kaggle’s Deepsat (Sat-6). Набор данных состоит из изображений размером 28 x 28 пикселей (каждое имеет четыре полосы - RGB и ближний инфракрасный). Все изображения относятся к одному из шести различных классов земного покрова:

Задача состоит в том, чтобы правильно предсказать классификации немаркированных тестовых изображений. Всего набор данных содержит 324 000 обучающих изображений и 81 000 тестовых изображений.

Согласно Kaggle, данные взяты из аэрофотоснимков Калифорнии. Как и следовало ожидать, неосвоенных / сельскохозяйственных территорий намного больше, чем дорог и построек.

По мере того, как я пробую разные модели, будет особенно важно изучить матрицу путаницы каждой модели (фактическая метка против прогнозируемой метки для каждого класса), чтобы убедиться, что дисбаланс обучающего класса не оказывает негативного влияния на прогнозы.

Случайный лес

В качестве основы я попробовал базовую модель случайного леса. После небольшой настройки (полезен Scikit-Learn GridSearchCV) я обнаружил, что RandomForestClassifier(n_estimators=50, n_jobs=-1) из sklearn.ensemble достигла точности 97,5% на тестовом наборе. n_estimators указывает количество деревьев в лесу. n_jobs=-1 позволяет Scikit-Learn использовать все доступные ядра процессора. Вы можете прочитать больше о случайных лесах и ансамблях деревьев в более раннем моем посте.

Но общая точность мало что говорит нам о фактической производительности классификации по классам. Давайте посмотрим на матрицу путаницы:

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

Точность и отзывчивость - два важных показателя, которые необходимо изучить. Точность - это количество вещей, которые модель предсказывает как определенный класс, который фактически является этим классом. Напомним, из общего количества вещей, которые относятся к данному классу, сколько, по прогнозам модели, относится к этому классу.

Для каждого прямоугольника в сетке десятичные дроби представляют долю отзыва - например, 93% того, что на самом деле является пастбищем, должно было быть пастбищем.

XGBoost

Подобно случайным лесам, за исключением того, что вместо метода бэггинга с уменьшением дисперсии (несколько деревьев решений в лесу уменьшают вероятность того, что одно дерево не соответствует обучающему набору данных), деревья с градиентным усилением используют усиление подход. Как и бэггинг, бустинг использует ансамбль моделей (деревья решений) для уменьшения дисперсии, но, в отличие от бэггинга, бустинг начинается с сильного смещения и слабого ученика, а затем использует остатки от определенной целевой функции для итеративного построения деревьев, которые улучшают потеря предшествующих деревьев.

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

В отличие от случайных лесов, XGBoost будет строить модель на основе всех своих входных функций. Если каждое изображение имеет размер 28x28 пикселей, это 784 четырехмерных (синий, зеленый, красный, ближний инфракрасный) объект, умноженный на 324 000. Это много работы!

Если вы хотите обучить модель XGBoost на инстансе EC2 только с процессором (4 ядра, 64 ГБ оперативной памяти) в этом наборе данных, вы все равно будете тренироваться через 16+ часов. Не то чтобы я пробовал это ... :)

К счастью, сообщество XGBoost развивает поддержку графических процессоров. Однако запуск сборки графического процессора XGBoost был несколько хакерским и не совсем интуитивно понятным, поэтому я ненадолго остановлюсь, чтобы подробно описать шаги, которые я выполнил, чтобы запустить поддержку графического процессора XGBoost на экземпляре AWS P2 Linux.

Микро-руководство: создание XGBoost с поддержкой графического процессора на AWS P2 Ubuntu

Предварительные требования:

Вам понадобится установленная последняя версия CUDA API. Хотя проще всего использовать AMI Amazon Deep Learning, мне не нравится их привкус Linux, и я решил установить CUDA на свой собственный ванильный экземпляр Ubuntu. Это несколько сложно (в зависимости от того, какие дополнения вы решите добавить - cuDNN, TensorFlow, разгон графического процессора и т. Д., Изучение и установка документации может занять 1–3 часа).

В качестве отправной точки прочтите все Руководство по установке Nvidia CUDA для Linux и Руководство Amazon , прежде чем что-либо делать. Помните: если вы понимаете общий процесс до того, как начнете менять драйверы оборудования, у вас гораздо меньше шансов совершить длительную ошибку. Потерпи.

Удалите существующий XGBoost:

Если вы уже установили conda xgboost (conda install -c conda-forge xgboost) или py-xgboost (просто xgboost с предварительно созданными привязками python - conda install -c anaconda py-xgboost), вам необходимо conda uninstall py-xgboost, прежде чем вы сможете продолжить.

Убедитесь, что удаление прошло успешно, посмотрев в каталоге /home/path_to_active_anaconda_env/anaconda3/lib/python3.6/site-packages/ на наличие xgboost/. Каталога xgboost/ не должно быть, или (в зависимости от того, как вы его установили), если он есть, он должен быть пустым. Удалите каталог (убедившись, что он пуст - в противном случае я бы исследовал все отправленные conda uninstall сообщения).

Ознакомьтесь с документацией по поддержке графического процессора XGBoost:

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

Git клонировать последнюю версию XGBoost:

Убедитесь, что вы в /home/path_to_active_anaconda_env/anaconda3/lib/python3.6/site-packages/. Затем запустите git clone --recursive https://github.com/dmlc/xgboost.

Сборка XGboost с поддержкой графического процессора:

$ mkdir build
$ cd build
$ cmake .. -DUSE_CUDA=ON
$ make -j

Установите привязки Python:

$ cd python-package
$ sudo -s python setup.py install

Обратите внимание, что вам может понадобиться distutils. Если вышеуказанная установка не работает, запустите

$ sudo apt-get install python-setuptools, а затем повторно выполните приведенные выше команды.

Обновите PATH, чтобы ядро ​​Jupyter находило все XGBoost:

Согласно официальной документации, вы можете либо установить привязки, как показано выше , либо обновить путь (в обоих случаях нет необходимости), но я обнаружил, что, хотя я мог import xgboost без ошибок в своих ноутбуках после простой установки привязки, по какой-то причине jupyter не имел доступа к полной библиотеке xgboost.

cd в свой домашний каталог и запустите vim .bashrc (vim = 👑 💪).

Внизу файла добавьте:

export PYTHONPATH=~/home/path_to_active_anaconda_env/anaconda3/lib/python3.6/site-packages/xgboost/python-package:$PYTHONPATH

Что касается установки, то все!

Обучите модель XGBoost и убедитесь, что используется графический процессор:

Чтобы использовать графический процессор, вам нужно добавить пару дополнительных аргументов при обучении модели XGBoost.

param = {'eta':0.1,
         'max_depth':6,
         'objective':'multi:softmax',
         'num_class':6
         'n_estimators':175,
         'max_bin':16,
         'tree_method':'gpu_hist'}
pure_xgb = xgboost.train(param, dtrain, num_round)

max_bin=16 предохраняет ядро ​​Jupyter от сбоев после 5 минут обучения на необработанных изображениях. Согласно документации XGBoost, «увеличение этого числа улучшает оптимальность разбиения за счет увеличения времени вычислений». Значение по умолчанию - 256.

В этой статье подробно обсуждается логическое обоснование бункера, но у меня нет мотивации копаться до этого уровня реализации XGBoost.

Наконец, tree_method=gpu_hist указывает xgboost использовать версию алгоритма построения дерева гистограммы для графических процессоров. Если вам интересно, полный набор параметров обсуждается здесь.

Конец микрогида

XGBoost (продолжение)

Теперь, когда запущено обучение gpu, обучение достойной модели XGBoost становится жизнеспособным (в разумные сроки). Настроенная модель XGBoost, построенная с поддержкой графического процессора (скорость обучения = 0,1, max_depth = 6, n_estimators = 175, num_rounds = 100), заняла около 30 минут для обучения на экземпляре AWS P2. Есть неточности в классе, но в целом неплохо.

Если мы увеличим количество раундов, с которыми алгоритм XGBoost выполняет итерацию по набору данных, до 300, мы можем получить точность 99,16%! Вполне нормально! Однако время обучения значительно больше (я бы даже не пробовал это без графического процессора).

Сверточная нейронная сеть

Несколько других ядер в наборе данных Kaggle использовали сверточные нейронные сети. И XGBoost, и случайные леса имели проблемы с отзывом определенных классов. Может ли CNN лучше работать на индивидуальных занятиях? Я дам краткий обзор CNN и конкретных CNN, которые я реализовал здесь, а затем я расскажу о производительности тестового набора.

Архитектурный и концептуальный обзор высокого уровня

Для простоты я начал с существующей высокопроизводительной модели. Сразу после этого я переключил оптимизатор с приличного стохастического градиента на Адама (адаптивная оценка момента).

Было показано, что Адам является эффективным алгоритмом оптимизации за счет включения импульса, его способности гасить колебания (аналогично среднеквадратическому распространению) и его способности регулировать скорость обучения для каждого параметра индивидуально (например, Adagrad).

В итоге архитектура моего CNN выглядела так:

Я дам краткий обзор слоев, но вот отличный учебник по CNN в целом. Кроме того, вот потрясающая интерактивная демонстрация, которая дает представление о том, как CNN обрабатывает информацию. Очень круто!

Слои:

Слои свертки отображают меньшую сетку размером высота x ширина (в данном случае 5 x 5 x 4 - я не включил '4' на диаграмме выше, потому что в 2-мерной свертке 3-я Предполагается, что размер равен глубине исходного тома) по исходному входу. Каждое место в сетке занято значением, которое фактически является параметром для обучения сети. Все значения в сетке поэлементно умножаются на соответствующие им элементы во входных данных, а затем весь результат суммируется, и получается одно значение. Затем сетка делает шаг вперед (независимо от значения шага) и повторяет процесс.

Функции активации являются ключевым компонентом во всех типах нейронных сетей, поскольку они играют решающую роль в преобразовании пространства функций. Прочтите больше о преобразовании пространства функций в моем блоге или ознакомьтесь с этим полезным визуалом.

В этой сверточной нейронной сети я использовал функцию ReLU, а для конечного выходного слоя - функцию softmax. Функция softmax выводит вектор размерности (num_classes, 1), где каждое значение - это вероятность того, что вход сети является соответствующим классом.

Максимальные слои пула также используют концепцию сетки, за исключением того, что вместо поэлементного умножения и суммирования, максимальные слои пула просто возвращают самый большой отдельный элемент во входном объеме, покрытом сеткой. Максимальное количество слоев пула не включает функцию активации. Технически сверточный слой, его функция активации и объединяющие слои являются фактически этапами одного и того же слоя (см. Диаграмму на стр. 336), но думать о них как о отдельных слоях кажется немного проще с концептуальной точки зрения. Говорящий.

Полностью связанные слои работают так же, как скрытые слои в базовой нейронной сети. Стандартное матричное умножение + функция активации. Обратите внимание, что необходимо сгладить ввод, чтобы умножение матриц в полностью связанных слоях работало.

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

Dropout присваивает каждому нейрону в слое вероятность выключиться во время прямого прохода этапа обучения. Таким образом, при каждом прямом проходе по пакету новая комбинация нейронов включается или выключается. Просто для пояснения, все прямые (и обратные) проходы по всем пакетам обучающего набора данных представляют одну эпоху.

Одним из побочных эффектов выпадения является то, что во время фазы прогнозирования последующий уровень в нейронной сети ожидает ввода, эквивалентного ожидаемой активации выпадающего слоя. С этим можно справиться, масштабируя вход следующего слоя с той же вероятностью выпадения, или, что чаще (делается за кулисами в TensorFlow), используя перевернутый выпадение (прокрутите вниз до подраздела выпадения) на этапе обучения. .

Унизительно, насколько сложными могут быть даже концептуально простые концепции!

Напомним, что у моделей XGBoost и случайного леса были проблемы с отзывом по классам 😉. Как работает настроенный CNN?

Сейчас мы говорим! По сравнению с исходной моделью случайного леса, мы перешли с 7% дорог, обозначенных как здания, до 0,3%!

Идея:

Мы знаем, что и CNN, и XGBoost хорошо работают с этим набором данных. Что бы произошло, если бы мы использовали обученную CNN для определения функций, а затем добавили эти функции в модель XGBoost?

CNN с градиентным усилением

Общая концепция состоит в том, чтобы взять выходные данные векторного слоя, созданные обученной CNN, и использовать эти выходные данные для обучения модели XGBoost.

Если у вас есть обученная CNN, код для извлечения векторного слоя (мне нравится tf.contrib.keras (такой же, как tf.keras 😄) довольно прост.

def get_feature_layer(model, data):
    
    if model==None:
        # load trained cnn
        model = load_cnn("baseline_model_atom_5.5e-5.h5")
    
    total_layers = len(model.layers)
    
    fl_index = total_layers-1
    
    feature_layer_model = tf.keras.Model(
                     inputs=model.input,
                     outputs=model.get_layer(index=fl_index).output)
    
    feature_layer_output = feature_layer_model.predict(data)
    
    return feature_layer_output

Шаг обучения несколько сложнее. Несмотря на то, что я потратил утомительное количество времени, пытаясь заставить XGBoost обучаться с использованием графического процессора на выходе функционального уровня из модели CNN, мне не удалось сохранить ядро ​​jupyter в живых. Так что в этом случае мы будем обучать XGBoost, используя только CPU 😢.

def train_xgb_cnn(x_train_cnn, y_train, cnn=None, num_round=100):
    param = {'eta':0.1,
             'max_depth':6,
             'objective':'multi:softmax',
             'n_estimators':175,
             'silent':1,
             'num_class':6}
    feat_out = get_feature_layer(cnn_model, x_train_cnn)
    dtrain = xgboost.DMatrix(feat_out,
                             label=y_train.idxmax(axis=1).values)
    xgb_feature_layer = xgboost.train(param, dtrain, num_round)
    return xgb_feature_layer

Из-за медленного времени обучения я просто скопировал идентичные параметры модели XGBoost, которые использовал ранее. Как было выступление?

Лучшее пока! И если мы удвоим количество итераций XGBoost…

Это отличная производительность, но, чтобы внести ясность, мы не сравниваем яблоки с яблоками. Время обучения этих окончательных моделей эквивалентно времени обучения CNN + несколько часов для обучения моделей процессора XGBoost. Возможно, что простое добавление большего количества слоев к CNN может дать еще лучшие результаты при гораздо более коротком времени обучения.

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

Вот и мой пост! Спасибо за прочтение :)

Кроме того, подход, который я использовал с композитной моделью, был вдохновлен прекрасной статьей Глубокий гауссовский процесс для прогнозирования урожайности на основе данных дистанционного зондирования исследователей из Стэнфорда Цзясюон Ю, Сяочэн Ли, Мелвин Лоу. », Дэвид Лобелл и Стефано Эрмон , я надеюсь сделать гауссовские составные модели процессов темой будущего сообщения в блоге. Код их статьи можно увидеть здесь. Стоя на плечах гигантов.

Обновление от 24.06.18: После публикации этого поста я узнал о, казалось бы, идентичном подходе к классификационному слою CNN + XGBoost, принятому исследователями из Шанхайского университета Цзяо Тонг. Хотя я еще не читал эту статью, если вы хотите узнать больше об этих типах составных моделей (и жаждете более тщательного академического анализа), я предлагаю вам проверить Новый метод классификации изображений с моделью CNN-XGBoost .