Привет всем моим энтузиастам глубокого обучения.
Недавно я реализовал модель QANet для системы Q / A с использованием набора данных SQuAD. И столкнулся со многими проблемами при реализации того же самого.
Я не буду объяснять, как я реализовал эту модель.
Но я проведу вас через шаги, которые я выполнил для решения проблем и оптимизации модели.
Основная причина для объяснения самого процесса оптимизации заключается в приведенных ниже причинах.
1. Было доступно много материалов по тому, как реализовать QANet с помощью Pytorch. Но когда я столкнулся с небольшими проблемами при реализации модели, было очень мало материалов для их исправления, и мне потребовалось много дней исследований, чтобы решить проблему, но все это не было собрано в одном месте.
2. Как уменьшить объем памяти модели QANet.
3. Как эффективно снизить размер убытков.
Введение в модель QANet
Модель QANet - это преобразованная версия метода BIDAF (BID, направленного A ttention F low).
Как следует из названия, модели BIDAF были в основном созданы для использования механизма потока внимания не только от контекста к вопросу, но и от вопроса к контексту.
В традиционной системе ответов на вопросы поток внимания использовался только от вопроса к контексту. Но здесь все идет в обоих направлениях.
Этот базовый метод BIDAF первоначально был предложен только для сети RNN (LSTM), но для того, чтобы извлечь выгоду из вычислительных и параллельных вычислительных мощностей трансформатора, тот же метод позже был распространен на трансформаторы.
Многие модели QANet лидировали в рейтинге набора данных SQuAD на официальном сайте Стэнфорда до появления модели BERT, которая в основном обогнала все другие модели.
Я использовал код некоторых дополнительных функций из этих ссылок GITHUB. Но я в основном писал код для части фреймворка модели и других функций с нуля.
Теперь переходим к проблемам, с которыми я столкнулся при реализации модели.
Вызов 1
Большинство людей, пытающихся реализовать модель из исследовательской работы, прошли бы через это.
«С чего начать?».
Я бы посоветовал начать писать код для базовой структуры модели с более крупными компонентами, такими как кодировщик модели, многоголовое внимание и т. Д.,
Назначьте имя объекта для этих компонентов и задайте базовую структуру модели. Не беспокойтесь о коде этих отдельных компонентов или других подкомпонентов, мы позаботимся об этом позже.
После того, как основная структура Модели сформирована. Затем начните писать код для каждого из этих компонентов и его подкомпонентов. Таким образом, в этом сценарии будет эффективен базовый восходящий подход.
Вызов 2
Проблема нехватки памяти
После того, как я завершил кодирование, модель была правильно инициализирована с правильными размерами.
Я столкнулся с серьезной проблемой ошибки нехватки памяти во время выполнения модели.
Эта ошибка возникает из-за того, что общий размер выполняемой памяти превышает размер вашей оперативной памяти.
И эти модели становятся довольно большими, когда вы используете большой набор данных, такой как SQuAD 2.0.
Поверьте мне, у меня на моем компьютере было 16 ГБ ОЗУ, но все равно возникала ошибка «нехватка памяти». И даже при использовании графического процессора, предоставленного Google colab, я столкнулся с той же проблемой.
Поэтому всякий раз, когда мы сталкиваемся с этой проблемой, первым делом нужно выяснить, какая часть программы потребляет больше памяти.
В этом случае у нас есть основная идея, что из-за огромного размера набора данных возможно, что обработка набора данных потребляет больше памяти.
Самый простой способ решить эту проблему - уменьшить размер набора данных, что позволяет также уменьшить размер модели.
Но модели-трансформеры, как правило, требуют данных, и для того, чтобы они могли эффективно обучаться, нам необходимо снабдить их большим набором данных.
Поэтому я решил не изменять размер набора данных, а вместо этого хотел уменьшить размер обработки.
Но в случае, если вы не уверены, какой процесс занимает больше места в ОЗУ,
самый простой метод, которым я воспользовался, заключался в том, чтобы просто добавить оператор печати после каждого основного компонента в модели и запустить программу.
Откройте диспетчер задач (ctrl + shift + Esc) в Windows и щелкните вкладку производительности, чтобы проверить использование памяти RAM.
Из этого мы можем легко узнать, какой процесс использует больше оперативной памяти, поскольку она будет постоянно увеличиваться в этом конкретном процессе.
Решение 1
Итак, первый шаг - разделить компоненты на максимальное количество подмодулей.
Python освобождает память объектов или переменных, как только объект или память теряют свою область видимости.
Итак, преимущество наличия программы разделение на небольшие функции заключается в том, что после завершения выполнения функции вся память переменных / объектов внутри функции освобождается.
Кроме того, это улучшает читаемость и возможность повторного использования функции.
Решение 2: -
После реализации решения 1 это практически не имело никакого значения. Тем не менее я столкнулся с проблемой нехватки памяти.
Затем я начал вручную освобождать память переменной, которая будет иметь огромные данные, но не будет использоваться в программе впоследствии.
Для реализации решения я использовал следующие функции:
del и gc.collect ()
Функция del удалит переменную.
gc.collect () освободит память для этой удаленной переменной.
Внимание: -
Не используйте del и gc.collect () внутри модели (т.е. компоненты, которые будут иметь require_grad как true), делая это, это не только освободит значение тензора / переменной, но также освобождает градиент тензора,
в результате чего модель имеет неустойчивый градиентный спуск, а также значительно увеличивает время выполнения более чем на 200%. Я использовал эту функцию здесь, только на этапе обработки данных.
Если вы хотите узнать больше о сборке мусора в Python, загляните здесь
Я буквально целую неделю ломал голову над решением этого вопроса. Поверьте, использование этого избавит вас от боли.
Решение 3: -
Проверьте процессы, которые имеют много итераций, что приведет к увеличению памяти, если вы имеете дело с огромным набором данных.
В этом QANet мы комбинируем предварительно обученные вложения слов (векторы перчаток) и вложения символов.
Для встраивания символов мы использовали двумерную сверточную нейронную сеть, чтобы объединить ее со встраиванием слов. Таким образом, эта двумерная свертка будет выполняться для каждой буквы в контексте и вопросе. Таким образом, эта двумерная свертка будет иметь максимальное количество итераций и время обработки.
Замена 2D CNN на одномерную разделяемую по глубине CNN уменьшила размер модели.
Мы поговорим об этой одномерной разделяемой по глубине CNN в следующей части, так как это решение также сократило время выполнения и повысило производительность .
После применения всех этих трех решений я смог запустить программу с использованием графического процессора с размером пакета 50 без каких-либо ошибок нехватки памяти.
Вызов 3
Срок исполнения модели был высоким.
Я использовал Google colab для запуска модели с использованием графического процессора с размером пакета 50.
Но на каждую сотню пакетов, даже после использования графического процессора, требовалось почти 10 минут, а существует почти 1200 пакетов с размером каждого пакета, равным 50 для каждой эпохи.
Таким образом, на каждую эпоху уходило почти 2 часа. Google colab обеспечивает только 12 часов работы графического процессора в день.
Так что, даже сохранив эпохи до 8, я не смог запустить модель вовремя.
А также весь ваш день будет потрачен впустую, если у вас возникнут проблемы в коде
, или произойдет сбой, или вы захотите внести изменения в код.
Решение 1
Pytorch предоставляет вам функцию torch.autograd.profiler.profile (), которая помогает профилировать процессы, выполняемые в модели, и сколько времени занимает каждый процесс и сколько раз каждый процесс запускается.
Реализуя модель под этим профилировщиком, как указано выше, мы можем выяснить, какой процесс занимает много времени.
и мы можем найти альтернативный способ реализации того же процесса, эффективный с точки зрения памяти.
Например: - Как я уже упоминал в предыдущем задании, замена сверточной 2D-сети на эффективную с точки зрения памяти одномерную разделяемую по глубине сверточную сеть увеличила время обработки, а также значительно уменьшила размер памяти.
Чтобы узнать больше о разделимой по глубине сверточной сети
, ознакомьтесь с ней здесь
Вы также можете проверить реализацию torch.autograd.profiler.profile () здесь.
Решение 2
Иногда вам может потребоваться увеличить скорость и производительность с точностью модели.
поэтому в этом смысле мы можем уменьшить размеры модели, чтобы сократить общее время выполнения модели.
Поэтому выберите измерение word и char, которое может сократить время выполнения, но в то же время точность не снижается в значительной степени.
Вызов 4
Колебание значения потерь для каждой эпохи во время задней стойки.
После применения вышеуказанных решений проблемы с памятью и производительностью были решены.
Но мои значения потерь были очень большими, как показано ниже.
Даже после увеличения эпох до 12, значения потерь были очень высокими, что было необычно.
а также значение потерь не уменьшалось, так как было более беспорядочно изучать что-либо.
Решение для уменьшения колебаний стоимости убытков: -
Я изменил оптимизаторы Adadelta, которые были указаны в базовом документе BIDAF, и заменил его оптимизатором ADAM
и удалил модуль EMA (Exponential Moving Average). Колебания немного уменьшились, но разница была незначительной.
Поэтому я провел очень долгое исследование, чтобы выяснить основную причину такого поведения.
Внимательно изучая код слой за слоем, я смог определить проблему в коде.
В основном в статье QANet требуется, чтобы функция softmax находилась в конце полностью связанного слоя.
Но наличие softmax в конце и передача этих значений в функцию кросс-энтропийных потерь контрпродуктивна.
Перекрестная потеря энтропии - это комбинация softmax с потерями NLL, поэтому нам нужно просто передать необработанные логиты из полностью подключенного слоя.
После удаления функции softmax потери начинают уменьшаться после каждой эпохи без колебаний .
Уф !!!
Решение для уменьшения огромных потерь: -
После анализа и отладки кода построчно я смог выяснить основную причину такого поведения в программе.
В одной из вспомогательных функций, которую я использовал из кода модели эталонного преобразователя, был модуль, называемый модулем позиционного кодирования.
И в этом модуле он увеличивает входное значение на квадратный корень из измерения.
Это нормально, если это для слоя внедрения, так как значения для слоя внедрения будут очень низкими. А увеличение его значения только увеличит производительность.
Но когда вы используете ту же функцию в блоке Encoder, конечные размеры которого могут увеличиваться до 16 раз,
В моем случае размер слова был 100, а квадратный корень из 1600 - 40, что является огромным значением для масштабирования вверх.
Я изменил эту функцию, чтобы ограничить этот взрыв только слоем встраивания, а в слое кодировщика модели то же самое будет пропущено.
Причина упоминания здесь этой проблемы кода заключается в том, что проблема не всегда будет проблемой на стороне модели или на стороне ее реализации, но когда-то это может быть связано с некоторыми основными вещами, которые мы можем упустить. Так что всегда проверяйте и те мелочи, которые иногда могут быть причиной более серьезных проблем.
Итак, после внесения вышеуказанной модификации в код моя модель начала иметь нормальное значение потерь, как показано ниже, в течение первой эпохи, что действительно было огромным облегчением.
После реализации всех вышеперечисленных решений моя оценка EM достигла 37,534, а оценка F1 - 49,802.
Это по-прежнему средний балл F1 и EM по сравнению с базовым документом. Но в базовом документе QANet требуется значение измерения для встраивания слов, поскольку в качестве кодировщика модели использовалось 7 кодировщиков с накоплением, тогда как в кодировщике модели использовалось только 100 измерений и 4 кодировщика с накоплением.
и максимальный контекст длина была сохранена на 150 для сокращения времени обучения.
Поскольку для реализации такой огромной модели требуется графический процессор с большим объемом оперативной памяти и возможность использовать более длительное время обработки.
С моими 12 часами графического процессора, которые предоставляет Google colab, я смог реализовать только более легкую модель.
В любом случае цель этой реализации - изучить и изучить, как работают преобразователи с огромным набором данных и более громоздкой моделью.
И в этом смысле это дало мне отличные уроки, которые помогут мне в моих будущих усилиях.
Хотя это немного свело меня с ума и потребовалось несколько недель, чтобы завершить этот проект.
Конечный результат был удовлетворительным !!!!.
Надеюсь, мои ошибки и решения дали вам представление о том, чего ожидать при реализации исследовательских работ.
и как преодолевать проблемы при их реализации.
Посмотреть весь мой код на GITHUB здесь
Хлопайте, если вам это нравится, и комментируйте, если вам это нравится.
Мир !!!!
Получите доступ к экспертному обзору - Подпишитесь на DDI Intel