Модульное тестирование - твой друг
Среди писателей есть поговорка: «Все письма переписывают», то есть большая часть написания пересматривается. Для программистов (или, по крайней мере, исследователей данных) выражение можно перефразировать как «Все кодирование отлаживается».
Каждый раз, когда вы пишете код, вы должны убедиться, что он работает как задумано. Лучший метод проверки правильности, который я когда-либо нашел, - разбить ваш код на маленькие сегменты и убедиться, что каждый сегмент работает. Это можно сделать, сравнив вывод сегмента с тем, что, как вы знаете, является правильным ответом. Это называется модульным тестированием . Написание хороших модульных тестов является ключевым моментом для того, чтобы стать хорошим статистиком / специалистом по данным / специалистом по машинному обучению / практиком нейронных сетей. Там просто нет замены.
Вы должны проверить, что ваш код не содержит ошибок, прежде чем вы сможете настроить производительность сети! В противном случае, вы также можете переставить шезлонги на RMS Titanic .
Есть две особенности нейронных сетей, которые делают проверку еще более важной, чем для других типов машинного обучения или статистических моделей.
Нейронные сети не являются «готовыми» алгоритмами, как случайный лес или логистическая регрессия. Даже для простых сетей с прямой связью пользователь в основном принимает решения о том, как сеть будет настроена, подключена, инициализирована и оптимизирована. Это означает написание кода, а написание кода означает отладку.
Даже когда код нейронной сети выполняется без выдачи исключения, в сети все еще могут быть ошибки! Эти ошибки могут даже быть коварным видом, для которого будет обучаться сеть, но застрянут в неоптимальном решении, или полученная сеть не будет иметь желаемой архитектуры. ( Это пример различия между синтаксической и семантической ошибкой .)
В этой публикации среднего уровня « Как проводить модульное тестирование кода машинного обучения » Чейз Робертс более подробно рассматривается модульное тестирование для моделей машинного обучения. Я взял этот пример с ошибочным кодом из статьи:
def make_convnet(input_image):
net = slim.conv2d(input_image, 32, [11, 11], scope="conv1_11x11")
net = slim.conv2d(input_image, 64, [5, 5], scope="conv2_5x5")
net = slim.max_pool2d(net, [4, 4], stride=4, scope='pool1')
net = slim.conv2d(input_image, 64, [5, 5], scope="conv3_5x5")
net = slim.conv2d(input_image, 128, [3, 3], scope="conv4_3x3")
net = slim.max_pool2d(net, [2, 2], scope='pool2')
net = slim.conv2d(input_image, 128, [3, 3], scope="conv5_3x3")
net = slim.max_pool2d(net, [2, 2], scope='pool3')
net = slim.conv2d(input_image, 32, [1, 1], scope="conv6_1x1")
return net
Вы видите ошибку? Многие из различных операций фактически не используются, потому что предыдущие результаты перезаписываются новыми переменными. Использование этого блока кода в сети все еще будет тренироваться, а весы будут обновляться, и потери могут даже уменьшиться - но код определенно не выполняет то, что предполагалось. (Автор также не согласен с использованием одинарных или двойных кавычек, но это чисто стилистически.)
Наиболее распространенные ошибки программирования, относящиеся к нейронным сетям:
- Переменные создаются, но никогда не используются (обычно из-за ошибок копирования-вставки);
- Выражения для градиентных обновлений неверны;
- Обновления веса не применяются;
- Функции потерь не измеряются по правильной шкале (например, потеря кросс-энтропии может быть выражена через вероятность или логиты)
- Потеря не подходит для задачи (например, использование категориальной кросс-энтропийной потери для регрессионной задачи).
Ползти, прежде чем идти; Иди, прежде чем бежать
Широкие и глубокие нейронные сети и нейронные сети с экзотической проводкой - вот что сейчас важно для машинного обучения. Но эти сети не возникли полностью сформированными; их дизайнеры создавали их из небольших подразделений. Сначала создайте небольшую сеть с одним скрытым слоем и убедитесь, что она работает правильно. Затем постепенно добавляйте дополнительную сложность модели и проверяйте, что каждая из них также работает.
Слишком мало нейронов в слое может ограничить представление, которое изучает сеть, вызывая недопоставку. Слишком много нейронов может вызвать перенапряжение, потому что сеть «запомнит» данные обучения.
Даже если вы можете доказать, что математически существует только небольшое количество нейронов, необходимых для моделирования проблемы, часто бывает так, что наличие «еще нескольких» нейронов облегчает оптимизатору поиск «хорошей» конфигурации. (Но я не думаю, что кто-то полностью понимает, почему это так.) Я привожу пример этого в контексте проблемы XOR: не нужны ли мои итерации для обучения NN для XOR с MSE <0,001 слишком высоким? ,
Выбор количества скрытых слоев позволяет сети изучать абстракцию из необработанных данных. Глубокое обучение в моде в наши дни, и сети с большим количеством уровней показали впечатляющие результаты. Но добавление слишком большого количества скрытых слоев может привести к перерасходу или усложнить оптимизацию сети.
Выбор умной сетевой проводки может сделать для вас большую работу. Подходит ли ваш источник данных для специализированных сетевых архитектур? Сверточные нейронные сети могут достичь впечатляющих результатов на «структурированных» источниках данных, изображениях или аудиоданных. Периодические нейронные сети могут успешно работать с последовательными типами данных, такими как данные на естественном языке или временные ряды. Остаточные соединения могут улучшить сети с прямой связью.
Обучение нейронной сети похоже на взлом замков
Чтобы достичь современных или даже просто хороших результатов, вам необходимо настроить все части, настроенные для совместной работы . Настройка конфигурации нейронной сети, которая на самом деле запоминается, очень похожа на взлом блокировки: все части должны быть выстроены правильно. Точно так же, как недостаточно располагать один тумблер в нужном месте, так и недостаточно правильно настроить только архитектуру или только оптимизатор.
Настроить выбор конфигурации на самом деле не так просто, как сказать, что один тип выбора конфигурации (например, скорость обучения) более или менее важен, чем другой (например, количество единиц), поскольку все эти варианты взаимодействуют со всеми другими вариантами, поэтому выбор может быть успешным в сочетании с другим выбором, сделанным в другом месте .
Это не полный список параметров конфигурации, которые не являются также параметрами регуляризации или числовой оптимизации.
Все эти темы являются активными областями исследований.
Инициализация сети часто игнорируется как источник ошибок нейронной сети. Инициализация через слишком большой интервал может сделать начальные веса слишком большими, а это означает, что отдельные нейроны оказывают огромное влияние на поведение сети.
Ключевое различие между нейронной сетью и регрессионной моделью заключается в том, что нейронная сеть представляет собой совокупность многих нелинейных функций, называемых функциями активации . (См .: Какова существенная разница между нейронной сетью и линейной регрессией )
Результаты классической нейронной сети сфокусированы на сигмоидальных активационных функциях (логистических или функциях). Недавний результат показал, что модули ReLU (или аналогичные) имеют тенденцию работать лучше, потому что имеют более крутые градиенты, поэтому обновления могут быть применены быстро. (См .: Почему мы используем ReLU в нейронных сетях и как мы его используем? ) Одно из предостережений в отношении ReLU - это феномен «мертвого нейрона», который может препятствовать обучению; Дефектный и другие подобные варианты позволяют избежать этой проблемы. ВидетьTANH
Есть ряд других вариантов. Смотрите: Полный список функций активации в нейронных сетях с плюсами / минусами
Остаточные соединения - это аккуратная разработка, которая может упростить обучение нейронных сетей. «Глубокое остаточное обучение для распознавания изображений»
Кайминг Хе, Сянъюй Чжан, Шаоцин Рен, Цзянь Сань В: CVPR. (2016). Кроме того, изменение порядка операций в остаточном блоке может дополнительно улучшить результирующую сеть. « Отображение идентичности в глубоких остаточных сетях » Каймин Хе, Сяньюй Чжан, Шаоцин Рен и Цзянь Сунь.
Невыпуклая оптимизация трудна
Целевая функция нейронной сети является выпуклой только тогда, когда нет скрытых единиц, все активации являются линейными, а матрица проектирования является полноценной - потому что эта конфигурация идентична обычной задаче регрессии.
Во всех остальных случаях задача оптимизации невыпуклая, а невыпуклая оптимизация трудна. Проблемы обучения нейронных сетей общеизвестны (см. Почему трудно обучать глубокие нейронные сети? ). Кроме того, нейронные сети имеют очень большое количество параметров, что ограничивает нас только методами первого порядка (см .: Почему метод Ньютона не широко используется в машинном обучении? ). Это очень активная область исследований.
Установка слишком высокой скорости обучения приведет к расхождению оптимизации, потому что вы перепрыгнете с одной стороны «каньона» на другую. Слишком малое значение этого параметра не позволит вам добиться реального прогресса и, возможно, позволит шуму, присущему SGD, превзойти ваши оценки градиента.
Ограничение градиента изменяет масштаб градиента, если оно превышает некоторый порог. Раньше я думал, что это был параметр «установил и забыл», обычно на уровне 1,0, но я обнаружил, что могу значительно улучшить языковую модель LSTM, установив ее на 0,25. Я не знаю, почему это так.
Планирование скорости обучения может снизить скорость обучения в течение обучения. По моему опыту, попытка использовать планирование очень похожа на регулярное выражение : она заменяет одну проблему («Как я могу продолжить обучение после определенной эпохи?») На две проблемы («Как я могу продолжить обучение после определенной эпохи» ? »и« Как выбрать хороший график? »). Другие люди настаивают на том, что планирование важно. Я позволю тебе решить.
Выбор хорошего размера мини-партии может косвенно влиять на процесс обучения, поскольку большая мини-партия будет иметь тенденцию иметь меньшую дисперсию ( закон больших чисел ), чем меньшая мини-партия. Вы хотите, чтобы мини-пакет был достаточно большим, чтобы иметь представление о направлении градиента, но достаточно маленьким, чтобы SGD мог регулировать вашу сеть.
Существует несколько вариантов стохастического градиентного спуска, в которых используется импульс, адаптивные скорости обучения, обновления Нестерова и т. Д. Для улучшения ванильной SGD. Разработка лучшего оптимизатора - очень активная область исследований. Некоторые примеры:
Когда он впервые появился, оптимизатор Adam вызвал большой интерес. Но некоторые недавние исследования показали, что SGD с импульсом может превзойти методы адаптивного градиента для нейронных сетей. « Предельная ценность адаптивных градиентных методов в машинном обучении » Ашиа Уилсон, Ребекка Рулофс, Митчелл Стерн, Натан Сребро, Бенджамин Рехт
Но с другой стороны, в этой самой недавней статье предлагается новый адаптивный оптимизатор скорости обучения, который предположительно закрывает разрыв между методами адаптивной скорости и SGD с импульсом. « Устранение пробела в обобщении адаптивных градиентных методов в обучении глубоких нейронных сетей », Цзинхуэй Чен, Цюаньцюань Гу
Обнаружено, что методы адаптивного градиента, которые принимают историческую информацию о градиенте для автоматической настройки скорости обучения, обобщают хуже, чем стохастический градиентный спуск (SGD) с импульсом в обучении глубоких нейронных сетей. Это оставляет, как закрыть разрыв обобщения адаптивных градиентных методов открытой проблемой. В этой работе мы покажем, что методы адаптивного градиента, такие как Адам, Амсград, иногда «чрезмерно адаптированы». Мы разрабатываем новый алгоритм, называемый частично адаптивным методом оценки импульса (Padam), который объединяет Адама / Амстердама с SGD для достижения лучшего из обоих миров. Эксперименты на стандартных тестах показывают, что Padam может поддерживать высокую скорость сходимости как Adam / Amsgrad, в то же время обобщая, а также SGD при обучении глубоких нейронных сетей.
нормализация
Масштаб данных может иметь большое значение при обучении.
До представления данных в нейронную сеть стандартизация данных, чтобы они имели среднее значение 0 и единичную дисперсию или находились в небольшом интервале, таком как может улучшить обучение. Это равносильно предварительной обработке и устраняет влияние выбора единиц измерения на вес сети. Например, длина в миллиметрах и длина в километрах представляют одну и ту же концепцию, но в разных масштабах. Точные детали того, как стандартизировать данные, зависят от того, как выглядят ваши данные.[ - 0,5 , 0,5 ]
Нормализация уровня может улучшить обучение сети, сохраняя среднее значение и стандартное отклонение для активаций нейронов. Непонятно, почему это помогает обучению и остается активной областью исследований.
- « Понимание нормализации партии » Йохана Бьорка, Карлы Гомес, Барта Сельмана
- Джонат Колер, Хади Данешманд, Аурелиен Луччи, Мин Чжоу, Клаус Неймейр, Томас Хофманн « На пути к теоретическому пониманию периодической нормализации »
- « Как нормализация партии помогает оптимизировать? (Нет, речь не идет о внутреннем смещении ковариат) » Шибани Сантуркар, Димитрис Ципрас, Эндрю Ильяс, Александр Мадри
регуляризация
Выбор и настройка регуляризации сети является ключевой частью построения модели, которая хорошо обобщает (то есть модель, которая не соответствует учебным данным). Однако в то время, когда ваша сеть пытается снизить потери обучающих данных - когда сеть не обучается - регуляризация может скрыть проблему.
Когда моя сеть не учится, я отключаю всю регуляризацию и проверяю, что нерегулярная сеть работает правильно. Затем я добавляю обратно каждый элемент регуляризации и проверяю, что каждый из них работает по пути.
Эта тактика может точно определить, где какая-то регуляризация может быть плохо установлена. Некоторые примеры
L2Регуляризация (также известная как уменьшение веса) или регуляризация установлена слишком большой, поэтому веса не могут двигаться.L1
Две части регуляризации находятся в конфликте. Например, широко отмечается, что нормализация и выпадение слоя трудно использовать вместе. Поскольку любое из них само по себе очень полезно, понимание того, как использовать оба, является активной областью исследований.
Вести журнал экспериментов
Когда я настраиваю нейронную сеть, я не жестко программирую настройки параметров. Вместо этого я делаю это в файле конфигурации (например, JSON), который читается и используется для заполнения деталей конфигурации сети во время выполнения. Я храню все эти файлы конфигурации. Если я делаю какие-либо изменения параметров, я делаю новый файл конфигурации. Наконец, я добавляю в качестве комментариев все потери за эпоху для обучения и проверки.
Причина, по которой я так одержим сохранением старых результатов, заключается в том, что это позволяет очень легко вернуться и просмотреть предыдущие эксперименты. Это также препятствует ошибочному повторению того же тупикового эксперимента. Психологически это также позволяет вам оглянуться назад и наблюдать: «Ну, проект может быть не там, где я хочу, чтобы он был сегодня, но я делаю успехи по сравнению с тем, где я был недель назад».k
В качестве примера я хотел узнать о языковых моделях LSTM, поэтому я решил создать бота в Twitter, который будет писать новые твиты в ответ другим пользователям Twitter. Я работал над этим в свое свободное время, между аспирантурой и моей работой. Это заняло около года, и я перебрал около 150 различных моделей, прежде чем перейти к модели, которая сделала то, что я хотел: создать новый англоязычный текст, который (вроде бы) имеет смысл. (Одним из ключевых препятствий и одной из причин того, что потребовалось так много попыток, было то, что было недостаточно просто получить низкую потерю выборки, так как ранним моделям с малыми потерями удалось запомнить тренировочные данные, поэтому он просто воспроизводил германские текстовые блоки в ответ на подсказки - потребовалось некоторое изменение, чтобы сделать модель более спонтанной и при этом иметь низкие потери.)