Transfer learning что это
Transfer Learning: как быстро обучить нейросеть на своих данных
Машинное обучение становится доступнее, появляется больше возможностей применять эту технологию, используя «готовые компоненты». Например, Transfer Learning позволяет использовать накопленный при решении одной задачи опыт для решения другой, аналогичной проблемы. Нейросеть сначала обучается на большом объеме данных, затем — на целевом наборе.
В этой статье я расскажу, как использовать метод Transfer Learning на примере распознавания изображений с едой. Про другие инструменты машинного обучения я расскажу на воркшопе «Machine Learning и нейросети для разработчиков».
Если перед нами встает задача распознавания изображений, можно воспользоваться готовым сервисом. Однако, если нужно обучить модель на собственном наборе данных, то придется делать это самостоятельно.
Для таких типовых задач, как классификация изображений, можно воспользоваться готовой архитектурой (AlexNet, VGG, Inception, ResNet и т.д.) и обучить нейросеть на своих данных. Реализации таких сетей с помощью различных фреймворков уже существуют, так что на данном этапе можно использовать одну из них как черный ящик, не вникая глубоко в принцип её работы.
Однако, глубокие нейронные сети требовательны к большим объемам данных для сходимости обучения. И зачастую в нашей частной задаче недостаточно данных для того, чтобы хорошо натренировать все слои нейросети. Transfer Learning решает эту проблему.
Transfer Learning для классификации изображений
Нейронные сети, которые используются для классификации, как правило, содержат N выходных нейронов в последнем слое, где N — это количество классов. Такой выходной вектор трактуется как набор вероятностей принадлежности к классу. В нашей задаче распознавания изображений еды количество классов может отличаться от того, которое было в исходном датасете. В таком случае нам придётся полностью выкинуть этот последний слой и поставить новый, с нужным количеством выходных нейронов
Зачастую в конце классификационных сетей используется полносвязный слой. Так как мы заменили этот слой, использовать предобученные веса для него уже не получится. Придется тренировать его с нуля, инициализировав его веса случайными значениями. Веса для всех остальных слоев мы загружаем из предобученного снэпшота.
Существуют различные стратегии дообучения модели. Мы воспользуемся следующей: будем тренировать всю сеть из конца в конец (end-to-end), а предобученные веса не будем фиксировать, чтобы дать им немного скорректироваться и подстроиться под наши данные. Такой процесс называется тонкой настройкой (fine-tuning).
Структурные компоненты
Для решения задачи нам понадобятся следующие компоненты:
В нашем примере компоненты (1), (2) и (3) я буду брать из собственного репозитория, который содержит максимально легковесный код — при желании с ним можно легко разобраться. Наш пример будет реализован на популярном фреймворке TensorFlow. Предобученные веса (4), подходящие под выбранный фреймворк, можно найти, если они соответствуют одной из классических архитектур. В качестве датасета (5) для демонстрации я возьму Food-101.
Модель
В качестве модели воспользуемся классической нейросетью VGG (точнее, VGG19). Несмотря на некоторые недостатки, эта модель демонстрирует довольно высокое качество. Кроме того, она легко поддается анализу. На TensorFlow Slim описание модели выглядит достаточно компактно:
Веса для VGG19, обученные на ImageNet и совместимые с TensorFlow, скачаем с репозитория на GitHub из раздела Pre-trained Models.
Датасет
В качестве обучающей и валидационной выборки будем использовать публичный датасет Food-101, где собрано более 100 тысяч изображений еды, разбитых на 101 категорию.
Скачиваем и распаковываем датасет:
Пайплайн данных в нашем обучении устроен так, что из датасета нам понадобится распарсить следующее:
Все вспомогательные функции, ответственные за обработку данных, вынесены в отдельный файл data.py :
Обучение модели
Код обучения модели состоит из следующих шагов:
После запуска обучения можно посмотреть на его ход с помощью утилиты TensorBoard, которая поставляется в комплекте с TensorFlow и служит для визуализации различных метрик и других параметров.
В конце обучения в TensorBoard мы наблюдаем практически идеальную картину: снижение Train loss и рост Validation Accuracy
Тестирование модели
Теперь протестируем нашу модель. Для этого:
Весь код, включая ресурсы для построения и запуска Docker контейнера со всеми нужными версиями библиотек, находятся в этом репозитории — на момент прочтения статьи код в репозитории может иметь обновления.
На воркшопе «Machine Learning и нейросети для разработчиков» я разберу и другие задачи машинного обучения, а студенты к концу интенсива сами представят свои проекты.
Transfer Learning с использованием TensorFlow.JS
На практике в подавляющем большинстве вы не будете иметь дело с созданием новых моделей и обучением их с нуля на клиентской стороне. Чаще всего придется создавать модели на базе уже существующих. Эту технику называют Transfer Learning.
Кроме того, на мой взгляд Transfer Learning – это наиболее перспективная техника для использования на клиентской стороне с помощью TensorFlowJS. Большим преимуществом тут перед применением той же самой техники на сервере – это сохранение конфиденциальности клиентской информации и наличием возможности доступа к сенсорам (камера, гео-локации и др).
Принцип работы Transfer Learning прост. Вначале модель обучается на базе большого набора тренировочных данных. Во время процесса обучения, нейронная сеть извлекает большое количество полезных характеристик (признаков) конкретной решаемой задачи, которые могут быть использованы как база для новой, которая будет обучаться уже на малом числе тренировочных данных для более специфичной, но похожей задачи (рисунок 1). Таким образом, переобучение может происходить на устройствах с ограниченными ресурсами за относительно меньшее время.
Рисунок 1 – Структурная схема Transfer Learning
Для загрузки модели в TensorFlow используют специальный JSON формат, который в свою очередь может быть двух типов: graph-model или layers-model (рисунок 2).
Рисунок 2 – Классификация форматов сериализации моделей, совместимые с TensorFlowJS
Для Transfer Learning модель в формате graph-model не применима, мы можем использовать эту модель только в том виде, в котором ее загрузили, без возможности ее переобучении на новой выборке данных или же изменении топологии под свои нужды. Поэтому ниже мы будет вести речь, только про модель в формате layers-model.
С помощью специального конвертора вы можете конвертировать модели, например обученных на Python с помощью фреймворка Keras (он сейчас включен как подмодуль в TensorFlow) в формат, совместимый с TF.js. Более подробно вы можете узнать тут.
После загрузки модели, мы можем модифицировать и переобучить ее согласно требованиям задачи. Так как мы будем стремиться уменьшить время на обучение модели на базе новой выборки данных, то очевидным будет то, что необходимо “замораживать” (freeze) как можно больше слоев в исходной модели. Заморозить слой – это перевод всех нейронов слоя из категории обучаемых (trainable) в категорию необучаемых (untrainable). Во время обучения сети, оптимизатор будет модифицировать только обучаемые параметры сети, что может значительно сократить время на ее обучение.
Справедливым вопросом тогда будет – как много и какие слои мы должны замораживать. Чтобы интуитивно понимать это, давайте рассмотрим типовую структурную схему нейронной сети, которая связана с классификаций изображений между N классами. Структурная схема приведена на рисунке 3.
Рисунок 3 – Топология нейронной сети для классификации изображений между N классами
Нейронная сеть включает в себя несколько последовательно подключенных сверточных слоев (convolutional layers) и в конце подключено несколько скрытых полносвязных слоев нейронных сетей (dense layers, fully-connected layers).
Даже если мы будем использовать модель, которая знает как распознавать машины, а нам необходимо распознавать наличие человеческого лица на изображении, то несмотря на то что задачи кажутся абсолютно разными, сверточные слои, находящиеся ближе к входу нейронной сети для обоих сетей будут извлекать идентичные признаки (рисунок 4)
. Таким образом с учетом вышесказанного, замораживать слои необходимо начинать со входа нейронной сети, а количество замораживаемых слоев прямо пропорционально схожести решаемой задачи с задачей, которая решает предварительно загруженная модель.
Другим не менее важным параметром для выбора сколько слоев необходимо замораживать – это размер располагаемого набора данных, с которым мы собираемся переобучать модель.
Например, если мы располагаем ограниченным числом тренировочных данных, с которым собираемся переобучать модель, а новая задача сходна с задачей, которая решала предварительно загруженная сеть – то в данном случае имеет смысл заморозить все сверточные слои. С другой стороны – если мы имеем большее число тренировочных данных, а предварительно загруженная сеть не похожа на задачу, которую мы решаем, то тут мы можем вовсе не замораживать слои.
В последнем случае вы можете задать вопрос – так если мы не фиксируем ни одного слоя, то смысл загружать предварительно обученную модель с ее весами. Однако при обучении модели с нуля – фреймворк инициализирует веса нейронной сети произвольно, что делает процесс обучения модели дольше, чем если бы мы использовали уже предварительно настроенные веса, в которых как мы уже отмечали выше – низкоуровневые слои для разных задач будут иметь приблизительно идентичные настройки.
. Обобщим, выше сказанное – чем больше у нас обучающая выборка для обучающей модели, тем меньше слоев нам надо замораживать.
Визуализация алгоритма, описанного выше, вы можете посмотреть на рисунке 5.
Рисунок 5 – Схема настройки новой нейронной сети на базе уже обученной
Практика
Для начала давайте посмотрим на картину в целом и что мы собираемся делать и разберем каждый шаг.
Рисунок 6 – Структурная диаграмма по созданию новой модели на базе обученной модели MobileNet
1 шаг – Загрузка обученной модели MobileNet и ее анализ
MobileNet – модель, обученная на базе ImageNet выборке (коллекция, включающая более миллиона изображений, разбитую вручную между 1000 классами).
Загрузим модель с помощью специального пользовательского React-hook:
Посмотрим на топологию загруженной сети вызовом model.summary(), рисунок 7.
Рисунок 7 – Топология загруженной модели
Модель состоит из 88 слоев, входной слой которого представлен тензором размерностью [null, 224, 224, 3], принимающий изображение размерностью 224×224 пикселей с тремя цветовыми каналами. Выходной слой – это тензор размерность [null,1000].
Выходной тензор [null, 1000] – это так называемый one-hot вектор, в котором все значения равны нулю, за исключением одного, например [0, 0, 1, 0, 0] – это значит, что нейронная сеть считает с вероятностью 1, что на изображении класс с индексом 2 (индексация как обычно начинается с нуля). Когда мы будет использовать модель, то этот вектор будет представлять собой распределение вероятности для каждого из классов, например, можно получить такие значения: [0.07, 0.1, 0.03, 0.75, 0.05]. Обратите внимание, что сумма всех значений будет равна 1, а модель считает с максимальной вероятностью 0.75, что это объект класса c индексом 3.
В связи с тем, что для нашей задачи мы имеем только 3 класса, то нам необходимо модифицировать топологию модели, так как исходная загруженная модель умеет классифицировать между 1000 классами. Давайте изобразим схему того, что мы хотим сделать.
Слои модели можно разбить условно на слои, отвечающих за:
— извлечение характерных признаков изображения
— классификацию изображений между 1000 классами
Для новой модели, мы хотим использовать все слои, отвечающих за извлечение признаков изображений, но при этом исключим все слои, отвечающих за классификацию изображений между 1000 классами, и вместо этого предоставить свой классификатор, который будет производить классификацию между 3мя классами.
На рисунке 8 показана диаграмма разбивка загруженных слоев по их назначению (рисунок 8).
Рисунок 7 – Создание новой модели на базе существующей
Кстати, тут можно проанализировать нашу задачу на предмет, стоит ли замораживать слои. Во-первых, однозначно мы не будем заставлять пользователя делать большую тренировочную выборку для обучения сети, будет вполне себе достаточно по 30-50 изображений каждого класса. Также решаемая нами задача – классификация между 3 классами изображений с жестами, достаточно близка к задаче классификации изображений между 1000 классами, которая была предварительно обучена на большом наборе тренировочных изображений. Следовательно, согласно рисунку 5, мы можем заморозить все слои части модели MobileNetV2, которые мы извлекли для своих нужд:
Шаг 2 – Поиск последнего слоя в загруженной модели, отвечающего за извлечение характерных признаков изображения
Для этого будем использовать АПИ tf.LayersModel.getLayer, который позволяет найти искомый слой либо по его индексу, либо по уникальному имени. Более предпочтительным является поиск слоя по уникальному имени, чем по его индексу. Обратите внимание (рисунок 7), каждый слой загруженной модели имеет уникальное название: input_1; conv_pw_13_bn; conv_pw_13_relu и др. Как и договаривались – найдем последний сверточный слой в модели и это является слой с именем conv_pw_13_relu. Таким образом код будет выглядеть следующим образом:
Шаг 3 – Создание нового классификатора для классификации между 3 классами
— второй слой – скрытый полносвязный слой, который будет содержать 100 нейронов (параметр может быть подобран на базе экспериментов, это так называемый hyper parameter) с активационной функцией RELU. RELU – наиболее распространенная активационная функция, которая добавляет нелинейность модели;
— третий слой – выходной полносвязный слой, содержащий ровно 3 нейрона, каждый из которых определяет вероятность принадлежности изображения к одному из трех классов: камень, ножницы, бумага. Слой будет с активационной функцией SOFTMAX. SOFTMAX функция, которая всегда используется для последнего слоя для решения задач множественной классификации. Она преобразует вектор z размерности K в вектор той же размерностью σ, где каждое значение представлена вещественным числом в интервале [0, 1], а их сумма равна единице. И как уже указывалось выше,каждое значение в векторе σi трактуются как вероятность того, то объект принадлежит классу i.
Рисунок 8 – Новые слои для модели, отвечающие за классификацию между 3 классами
Код будет выглядеть следующим образом:
В шаге 2 мы нашли последний слой в графе модели, который мы хотим оставим в новой разрабатываемой модели. Давайте свяжем этот слой с классификатором, созданным на шаге 3 (см. рисунок 6). Для этого экземпляры класса модели/слоев имеют специальный метод apply, который делает соединение между слоями. Это будет выглядеть следующим образом:
Шаг 5 – Создание новой модели и ее компиляция
Для создания новой модели на базе существующей мы будет использовать tf.model, который принимает в качестве аргумента объект с двумя обязательными полями inputs и outputs. Оба параметра ожидают массив, где каждый элемент массива представляет собой Symbolic tensor (отличие от обычного тензора – это то, что по сути это описание тензора имеющая сведения о размерности и типе, но при этом отсутствуют данные, так называемый placeholder).
Каждый слой в модели имеет два атрибута – вход и выход, представленные Symbolic tensor.
Тут иногда удобно сопоставлять слой с обычной JS функцией, которая принимает входной параметр, внутри производится некоторая логика по вычислению и всегда возвращает выходной параметр. Отличие тут, что входными и выходными параметрами будут Symbolic Tensor.
Итак, нам надо определить inputs и outputs для новой модели. Input для новой модели будет совпадать с input загруженной модели и доступно через: pretrainedModel.inputs.
А выходным Symbolic Tensor – это выход после последнего слоя нового классификатора, который доступен в переменной newOutput, после применения метода apply на предыдущем шаге.
Скомпилируем модель, задав нужный оптимизатор и loss-функцию. Наиболее лучшим оптимизатором является tf.train.adam, а loss-функция для классификации задают обычно categoricalCrossentropy:
Полный код создания новой модели на базе предварительно обученной MobileNet
Осталось лишь добавить UX элементы, позволяющие пользователю задать выборки для трех типов изображений, предоставить кнопку, инициализирующей процесс обучения:
После того, как процесс обучения будет закончить, необходимо запустит процесс использования этой модели для классификации изображений:
Transfer Learning и PyTorch в Python
Трансферное обучение (Transfer Learning) в Python – это мощный метод обучения глубоких нейронных сетей, который позволяет использовать знания, полученные об одной проблеме глубокого обучения, и применять их к другой, но со схожей задачей.
Использование трансферного обучения может значительно ускорить развертывание разрабатываемого вами приложения, делая как обучение, так и внедрение вашей глубокой нейронной сети проще.
В этой статье мы рассмотрим теорию, лежащую в основе трансферного обучения, и посмотрим, как реализовать пример в сверточных нейронных сетях (CNN) в PyTorch.
Что такое PyTorch?
Pytorch – это библиотека, разработанная для Python, специализирующаяся на глубоком обучении и обработке естественного языка. PyTorch использует преимущества графических процессоров (GPU), чтобы реализовать глубокую нейронную сеть быстрее, чем обучение сети на ЦП.
PyTorch совместим с NumPy и позволяет преобразовывать массивы NumPy в тензоры и наоборот.
Определение необходимых терминов
Прежде чем мы пойдем дальше, давайте определим некоторые термины, относящиеся к трансферному обучению. Четкое понимание наших определений упростит понимание теории, лежащей в основе трансферного обучения, и реализацию экземпляра.
Что такое глубокое обучение?
Глубокое обучение – это подраздел машинного обучения, которое можно описать как просто действие, позволяющее компьютерам выполнять задачи, не будучи явно запрограммированными на это.
В системах глубокого обучения используются нейронные сети, которые представляют собой вычислительные структуры, смоделированные по образцу человеческого мозга.
Нейронные сети состоят из трех различных компонентов: входного слоя, скрытого или среднего уровня и выходного уровня.
На входном уровне просто обрабатываются данные, отправляемые в нейронную сеть, в то время как промежуточные и скрытые слои состоят из структуры, называемой узлом или нейроном.
Эти узлы представляют собой математические функции, которые каким-либо образом изменяют входную информацию и передают измененные данные на последний или выходной уровень. Простые нейронные сети могут различать простые шаблоны во входных данных путем корректировки предположений или параметров о том, как точки данных связаны друг с другом.
Глубокая нейронная сеть получила свое название от того факта, что она состоит из множества обычных нейронных сетей, соединенных вместе. Чем больше нейронных сетей связаны друг с другом, тем более сложные шаблоны может различать глубокая нейронная сеть и тем больше у нее возможностей. Существуют разные виды нейронных сетей, каждый из которых имеет свою специализацию.
Например, глубокие нейронные сети с долгосрочной краткосрочной памятью – это сети, которые очень хорошо работают при обработке чувствительных ко времени задач, где важен хронологический порядок данных, таких как текстовые или речевые данные.
Что такое сверточная нейронная сеть?
Эта статья будет посвящена сверточным нейронным сетям, типу нейронной сети, которая отлично справляется с манипулированием данными изображений.
Сверточные нейронные сети (CNN) – это особые типы нейронных сетей, способные создавать представления визуальных данных. Данные в CNN представлены в виде сетки, которая содержит значения, которые представляют, насколько ярким и какого цвета является каждый пиксель изображения.
CNN разбита на три различных компонента: сверточные слои, объединяющие слои и полносвязные слои.
Сверточный слой отвечает за создание представления изображения путем скалярного произведения двух матриц.
Первая матрица – это набор обучаемых параметров, называемых ядром. Другая матрица – это часть анализируемого изображения, которая будет иметь высоту, ширину и цветовые каналы. Сверточные слои – это то место, где в CNN происходит больше всего вычислений. Ядро перемещается по всей ширине и высоте изображения, в конечном итоге создавая представление всего изображения, которое является двухмерным, известное как карта активации.
Из-за огромного количества информации, содержащейся в сверточных слоях CNN, обучение сети может занять очень много времени. Функция позволяет уменьшить количество информации, содержащейся в сверточных слоях CNN, принимая вывод одного сверточного слоя и уменьшая его масштаб, чтобы упростить представление.
Слой объединения выполняет это, просматривая разные точки в выходных данных сети и «объединяя» соседние значения, получая одно значение, которое представляет все соседние значения. Другими словами, требуется сводная статистика значений в выбранном регионе.
Суммирование значений в области означает, что сеть может значительно уменьшить размер и сложность своего представления, сохраняя при этом релевантную информацию, которая позволит сети распознавать эту информацию и извлекать значимые шаблоны из изображения.
Существуют различные функции, которые можно использовать для суммирования значений региона, например, получение среднего значения по соседству или среднего пула. Также может быть взято средневзвешенное значение окрестности, равно как и норма L2 региона. Наиболее распространенным методом объединения является метод максимального объединения, при котором берется максимальное значение региона и используется для представления соседства.
Полностью связанный слой – это то место, где все нейроны связаны друг с другом, с подключениями между каждым предыдущим и последующим слоями в сети. Здесь анализируется информация, которая была извлечена сверточными слоями и объединена слоями, где изучаются закономерности в данных. Вычисления здесь выполняются посредством умножения матриц в сочетании с эффектом смещения.
В CNN также присутствует несколько нелинейностей. Учитывая, что изображения сами по себе являются нелинейными объектами, сеть должна иметь нелинейные компоненты, чтобы иметь возможность интерпретировать данные изображения. Нелинейные слои обычно вставляются в сеть сразу после сверточных слоев, так как это придает карте активации нелинейность.
Существует множество различных функций нелинейной активации, которые можно использовать для того, чтобы сеть могла правильно интерпретировать данные изображения. Самая популярная функция – это ReLu или выпрямленный линейный блок.
Функция ReLu превращает нелинейные входные данные в линейное представление путем сжатия реальных значений только до положительных значений выше 0. Другими словами, функция ReLu принимает любое значение выше нуля и возвращает его как есть, в то время как, если значение ниже нуля, оно возвращается как ноль.
Функция ReLu популярна из-за ее надежности и скорости, выполняя ее примерно в шесть раз быстрее, чем другие функции активации. Обратной стороной ReLu является то, что он может легко застрять при работе с большими градиентами, никогда не обновляя нейроны. Эту проблему можно решить, установив скорость обучения для функции.
Двумя другими популярными нелинейными функциями являются сигмоидальная функция и функция Tanh.
Сигмоидная функция работает, принимая реальные значения и сжимая их до диапазона от 0 до 1, хотя у нее есть проблемы с обработкой активаций, близких к крайним значениям градиента, поскольку значения становятся почти нулевыми.
Обучение и тестирование
Есть два разных этапа создания и реализации глубокой нейронной сети: обучение и тестирование.
На этапе обучения в сеть поступают данные, и она начинает изучать шаблоны, которые содержат данные, регулируя параметры сети, которые являются предположениями о том, как точки данных связаны друг с другом. Другими словами, на этапе обучения сеть «узнает» о том, какие данные были переданы.
На этапе тестирования оценивается то, что узнала сеть. Сети предоставляется новый набор данных, который она не видела раньше, а затем сеть просят применить свои предположения о паттернах, которые она усвоила, к новым данным. Оценивается точность модели, и обычно модель настраивается и переобучается, а затем повторно тестируется, пока архитектор не будет удовлетворен производительностью.
В случае трансферного обучения используемая сеть была предварительно обучена. Параметры сети уже настроены и сохранены, поэтому нет причин заново обучать всю сеть с нуля. Это означает, что сеть может быть немедленно использована для тестирования или только определенные уровни сети могут быть настроены и затем повторно обучены. Это значительно ускоряет развертывание глубокой нейронной сети.
Что такое трансферное обучение?
Идея трансферного обучения состоит в том, чтобы взять модель, обученную одной задаче, и применить ее ко второй, аналогичной задаче. Тот факт, что модель уже имеет некоторые или все данные для второй обученной задачи, означает, что она может быть реализована намного быстрее. Это позволяет быстро оценивать производительность и настраивать модель, обеспечивая более быстрое развертывание в целом.
Трансферное обучение становится все более популярным в области глубокого обучения благодаря огромному количеству вычислительных ресурсов и времени, необходимых для обучения моделей глубокого обучения в дополнение к большим и сложным наборам данных.
Основным ограничением трансферного обучения является то, что особенности модели, изученные во время первой задачи, являются общими, а не специфичными для первой. На практике это означает, что модели, обученные распознавать определенные типы изображений, могут быть повторно использованы для распознавания других изображений, если общие характеристики изображений аналогичны.
Теория трансферного обучения
Использование трансферного обучения имеет несколько важных концепций. Чтобы понять реализацию, нам нужно рассмотреть, как выглядит предварительно обученная модель и как ее можно настроить для ваших нужд.
Выбрать модель для трансферного обучения можно двумя способами. Можно создать модель с нуля для собственных нужд, сохранить параметры и структуру модели, а затем повторно использовать ее позже.
Второй способ реализовать трансферное обучение – просто взять уже существующую модель и повторно использовать ее, настраивая при этом ее параметры и гиперпараметры. В этом случае мы будем использовать предварительно обученную модель и модифицировать ее. После того, как вы решили, какой подход вы хотите использовать, выберите модель (если вы используете предварительно обученную модель).
Эти предварительно обученные модели доступны через API PyTorch, и после получения инструкций PyTorch загрузит их спецификации на ваш компьютер. Конкретная модель, которую мы собираемся использовать, – это ResNet34, часть серии Resnet.
Модель Resnet была разработана и обучена на наборе данных ImageNet, а также на наборе данных CIFAR-10. Таким образом, он оптимизирован для задач визуального распознавания и показал заметное улучшение по сравнению с серией VGG, поэтому мы будем его использовать.
Однако существуют и другие предварительно обученные модели, и вы можете поэкспериментировать с ними, чтобы увидеть, как они сравниваются.
Как объясняется в документации PyTorch по трансферному обучению, существует два основных способа использования: точная настройка или использование CNN в качестве средства извлечения фиксированных функций.
При тонкой настройке CNN вы используете параметры, которые имеет предварительно обученная сеть, вместо их случайной инициализации, а затем тренируетесь как обычно. Напротив, подход экстрактора признаков означает, что вы будете поддерживать все параметры CNN, за исключением тех, которые находятся на последних нескольких уровнях, которые будут инициализированы случайным образом и обучены как обычно.
Точная настройка модели важна, потому что, хотя модель была предварительно обучена другой (хотя, надеюсь, схожей) задаче. Плотно связанных параметров, с которыми поставляется предварительно обученная модель, вероятно, будет недостаточно, поэтому вы, вероятно, захотите переобучить последние несколько слоев сети.
Напротив, поскольку первые несколько слоев сети – это просто слои выделения признаков, и они будут работать аналогично с похожими изображениями, их можно оставить как есть. Следовательно, если набор данных небольшой и похожий, единственное обучение, которое необходимо выполнить, – это обучить нескольких последних слоев. Чем больше и сложнее будет набор данных, тем больше потребуется переобучения модели.
Помните, что трансферное обучение работает лучше всего, когда набор данных, который вы используете, меньше, чем исходная предварительно обученная модель, и похож на изображения, подаваемые в предварительно обученную модель.
Работа с моделями обучения передачи в Pytorch означает выбор слоев, которые нужно заморозить и разморозить. Замораживание модели означает указание PyTorch сохранить параметры в указанных вами слоях. Размораживание модели означает сообщение PyTorch о том, что вы хотите, чтобы указанные вами слои были доступны для обучения.
После того, как вы завершили обучение выбранных вами слоев предварительно обученной модели, вы, вероятно, захотите сохранить параметры для использования в будущем. Несмотря на то, что использование предварительно обученных моделей происходит быстрее, чем обучение модели с нуля, обучение по-прежнему требует времени, поэтому вы захотите скопировать лучшие параметры модели.
Классификация изображений
Мы готовы приступить к реализации трансферного обучения для набора данных. Мы рассмотрим как тонкую настройку ConvNet, так и использование сети в качестве средства извлечения фиксированных функций.
Предварительная обработка данных
Прежде всего, нам нужно выбрать набор данных для использования. Давайте выберем что-нибудь, на чем можно будет тренироваться с большим количеством действительно четких изображений. Набор данных Stanford Cats and Dogs – это очень часто используемый набор данных.
Обязательно разделите набор данных на два набора одинакового размера: «train» и «val».
Вы можете сделать это в любом случае, вручную переместив файлы или написав функцию для его обработки. Вы также можете ограничить набор данных меньшим размером, так как он содержит почти 12000 изображений в каждой категории, и это займет много времени для обучения. Вы можете сократить это число примерно до 5000 в каждой категории, при этом 1000 зарезервированы для проверки. Однако количество изображений, которые вы хотите использовать для обучения, зависит от вас.
Вот один из способов подготовить данные к использованию:
Загрузка данных
После того, как мы выбрали и подготовили данные, мы можем начать с импорта всех необходимых библиотек. Нам понадобятся многие пакеты Torch, такие как нейронная сеть nn, оптимизаторы и загрузчики данных. Нам также понадобится matplotlib для визуализации некоторых наших обучающих примеров.
Нам нужен numpy для обработки создания массивов данных, а также несколько других разных модулей:
Для начала нам нужно загрузить наши обучающие данные и подготовить их для использования нашей нейронной сетью. Для этой цели мы собираемся использовать преобразования Pytorch. Нам нужно убедиться, что изображения в обучающем наборе и наборе проверки имеют одинаковый размер, поэтому мы будем использовать преобразования.
Мы также будем немного увеличивать данные, пытаясь улучшить производительность нашей модели, заставляя ее узнавать об изображениях под разными углами и обрезками, поэтому мы будем произвольно обрезать и вращать изображения.
Далее мы сделаем тензоры из изображений, поскольку PyTorch работает с ними. Наконец, мы нормализуем изображения, что поможет сети работать со значениями, которые могут иметь широкий диапазон различных значений.
Затем мы составляем все выбранные нами преобразования. Обратите внимание, что преобразования проверки не имеют никакого переворачивания или вращения, поскольку они не являются частью нашего обучающего набора, поэтому сеть не узнает о них:
Теперь мы установим каталог для наших данных и будем использовать функцию PyTorch ImageFolder для создания наборов данных:
Теперь, когда мы выбрали нужные папки изображений, нам нужно использовать DataLoaders для создания повторяемых объектов, с которыми мы будем работать. Мы сообщаем ему, какие наборы данных хотим использовать, задаем размер пакета и перемешиваем данные.
Нам нужно будет сохранить некоторую информацию о нашем наборе данных, в частности размер набора данных и имена классов. Нам также необходимо указать, с каким устройством мы работаем, с процессором или графическим процессором. В следующей настройке будет использоваться графический процессор, если он доступен, в противном случае будет использоваться процессор:
Теперь давайте попробуем визуализировать некоторые из наших изображений с помощью функции. Мы возьмем ввод, создадим из него массив Numpy и транспонируем его. Затем мы нормализуем ввод, используя среднее значение и стандартное отклонение. Наконец, мы обрежем значения от 0 до 1, чтобы не было большого диапазона возможных значений массива, а затем покажем изображение:
Теперь давайте воспользуемся этой функцией и фактически визуализируем некоторые данные. Мы собираемся получить входные данные и имя классов из DataLoader и сохранить их для дальнейшего использования. Затем мы создадим сетку для отображения входов и их отображения:
Настройка предварительно обученной модели
Теперь нам нужно настроить предварительно обученную модель, которую мы хотим использовать для трансферного обучения. В этом случае мы собираемся использовать модель как есть и просто сбросить последний полностью связанный слой, снабдив его нашим количеством функций и классов.
При использовании предварительно обученных моделей PyTorch по умолчанию устанавливает модель для разморозки (с корректировкой параметров). Итак, мы будем обучать всю модель:
Если это все еще кажется неясным, может помочь визуализация композиции модели.
Вот что это вернет:
По сути, мы собираемся изменить выходы последней полностью связанной части всего на два класса и скорректировать параметры для всех остальных слоев.
Теперь нам нужно отправить нашу модель на обучающее устройство. Нам также нужно выбрать критерий потерь и оптимизатор, который мы хотим использовать с моделью. CrossEntropyLoss и оптимизатор SGD – хороший выбор, хотя есть и многие другие.
Мы также выберем планировщик скорости обучения, который снижает скорость обучения оптимизатора сверхурочно и помогает предотвратить несовпадение результатов из-за большой скорости обучения.
Теперь нам просто нужно определить функции, которые будут обучать модель и визуализировать прогнозы.
Начнем с обучающей функции. Он будет учитывать выбранную нами модель, а также оптимизатор, критерий и планировщик. Мы также укажем количество эпох обучения по умолчанию.
У каждой эпохи будет этап обучения и проверки. Для начала мы устанавливаем начальные лучшие параметры модели предварительно обученного режима, используя state_dict.
Мы также будем отслеживать точность модели на этапе обучения, и если мы перейдем к этапу проверки и точность улучшится, мы сохраним текущие параметры как лучшие:
Наши распечатки тренировок должны выглядеть примерно так:
Визуализация
Теперь мы создадим функцию, которая позволит нам увидеть прогнозы, сделанные нашей моделью.
Теперь мы можем связать все вместе. Обучим модель на наших изображениях и покажем прогнозы:
Это обучение, вероятно, займет у вас много времени, если вы используете процессор, а не графический процессор.
Экстрактор фиксированных функций
Из-за длительного времени обучения многие люди предпочитают просто использовать предварительно обученную модель в качестве экстрактора фиксированных признаков и тренировать только последний слой. Это значительно сокращает время обучения. Для этого вам нужно заменить модель, которую мы построили. Будет ссылка на репозиторий GitHub для обеих версий реализации ResNet.
Замените раздел, в котором определена предварительно обученная модель, версией, которая фиксирует параметры и не содержит наших вычислений градиента или обратного распространения.
Он выглядит очень похоже на предыдущий, за исключением того, что мы указываем, что градиенты не нуждаются в вычислении:
Что, если бы мы хотели выборочно разморозить слои и вычислить градиенты только для нескольких выбранных слоев. Это возможно? Да.
Давайте снова распечатаем дочерние элементы модели, чтобы вспомнить, какие слои и компоненты у нее есть:
Теперь, когда мы знаем, что это за слои, мы можем разморозить те, которые захотим, например, только слои 3 и 4:
Конечно, нам также необходимо обновить оптимизатор, чтобы отразить тот факт, что мы хотим оптимизировать только определенные слои.
Итак, теперь вы знаете, что можете настроить всю сеть, только последний уровень или что-то среднее.
Заключение
Мы реализовали трансферное обучение в PyTorch. Было бы неплохо сравнить реализацию настроенной сети с использованием экстрактора фиксированных функций, чтобы увидеть, как отличается производительность.
Также рекомендуется поэкспериментировать с замораживанием и размораживанием определенных слоев, поскольку это позволяет вам лучше понять, как вы можете настроить модель в соответствии со своими потребностями.