Сеть обучалась последние 12 часов. Всё выглядело хорошо: градиенты стабильные, функция потерь уменьшалась. Но потом пришёл результат: все нули, один фон, ничего не распознано. «Что я сделал не так?», — спросил я у компьютера, который промолчал в ответ.
Почему нейросеть выдаёт мусор (например, среднее всех результатов или у неё реально слабая точность)? С чего начать проверку?
Сеть может не обучаться по ряду причин. По итогу многих отладочных сессий я заметил, что часто делаю одни и те же проверки. Здесь я собрал в удобный список свой опыт вместе с лучшими идеями коллег. Надеюсь, этот список будет полезен и вам.
Многое может пойти не так. Но некоторые проблемы встречаются чаще, чем другие. Я обычно начинаю с этого маленького списка как набора экстренной помощи:
Если ничего не помогло, то приступайте к чтению этого длинного списка и проверяйте каждый пункт.
![](https://habrastorage.org/r/w1560/getpro/habr/post_images/ef6/032/8ff/ef60328ff5f505334860490ca248270c.png)
Источник: http://dilbert.com/strip/2014-05-07
Проверьте, что входные данные имеют смысл. Например, я не раз смешивал в кучу высоту и ширину изображений. Иногда по ошибке отдавал в нейросеть все нули. Или использовал одну и ту же партию снова и снова. Так что напечатайте/посмотрите пару партий входных данных и плановых выходных данных — убедитесь, что всё в порядке.
Попробуйте передать случайные числа вместо реальных данных и посмотрите, останется ли та же ошибка. Если так, то это верный знак, что ваша сеть на каком-то этапе превращает данные в мусор. Попробуйте отладку слой за слоем (операция за операцией) и посмотрите, где происходит сбой.
С данными всё может быть в порядке, а ошибка в коде, который передаёт входные данные нейросети. Распечатайте и проверьте входные данные первого слоя перед началом его операций.
Проверьте, что несколько образцов входных данных снабжены правильными метками. Также проверьте, что смена местами входных образцов так же отражается на выходных метках.
Может быть, неслучайные части взаимоотношения между входом и выходом слишком малы по сравнению со случайной частью (кто-то может сказать, что таковы котировки на бирже). То есть вход недостаточно связан с выходом. Тут нет универсального метода, потому что мера случайности зависит от типа данных.
Однажды это случилось со мной, когда я стянул набор изображений продуктов питания с сайта. Там было так много плохих меток, что сеть не могла обучаться. Вручную проверьте ряд образцов входных значений и посмотрите, что все метки на месте.
Данный пункт достоин отдельного разговора, потому что эта работа показывает точность выше 50% на базе MNIST при 50% повреждённых меток.
Если ваши данные не перемешаны и располагаются в определённом порядке (отсортированы по меткам), это может отрицательно отразиться на обучении. Перемешайте набор данных: убедитесь, что перемешиваете вместе и входные данные, и метки.
Может, в наборе данных тысяча изображений класса А на одно изображение класса Б? Тогда вам может понадобиться сбалансировать функцию потерь или попробовать другие подходы устранения несбалансированности.
Если вы обучаете сеть с нуля (то есть не настраиваете её), то может понадобиться очень много данных. Например, для классификации изображений, говорят, нужна тысяча изображений на каждый класс, а то и больше.
Такое случается в отсортированном наборе данных (то есть первые 10 тыс. образцов содержат одинаковый класс). Легко исправляется перемешиванием набора данных.
Эта работа указывает, что слишком большие партии могут понизить у модели способность к обобщению.
Спасибо hengcherkeng за это:
![](https://habrastorage.org/r/w1560/getpro/habr/post_images/a37/7f1/71d/a377f171d5b6bdc0a01822ed42952033.png)
Вы откалибровали входные данные на нулевое среднее и единичную дисперсию?
Аугментация имеет регуляризующий эффект. Если она слишком сильная, то это вкупе с другими формами регуляризации (L2-регуляризация, dropout и др.) может привести к недообучению нейросети.
Если вы используете уже подготовленную модель, то убедитесь, что используются та же нормализация и предобработка, что и в модели, которую вы обучаете. Например, должен пиксель быть в диапазоне [0, 1], [-1, 1] или [0, 255]?
CS231n указал на типичную ловушку:
Также проверьте на предмет наличия различающейся предварительной обработки каждого образца и партии.
![](https://habrastorage.org/r/w1560/getpro/habr/post_images/a3c/00e/878/a3c00e878e007200b1f3501115b65066.png)
Источник: https://xkcd.com/1838/
Это поможет определить, где проблема. Например, если целевая выдача — это класс объекта и координаты, попробуйте ограничить предсказание только классом объекта.
Снова из бесподобного CS231n: Инициализируйте с небольшими параметрами, без регуляризации. Например, если у нас 10 классов, то «по вероятности» означает, что правильный класс определится в 10% случаев, а функция потерь Softmax — это обратный логарифм к вероятности правильного класса, то есть получается
После этого попробуйте увеличить силу регуляризации, что должно увеличить функцию потерь.
Если вы реализовали свою собственную, проверьте её на баги и добавьте юнит-тесты. У меня часто бывало, что слегка неправильная функция потерь тонко вредила производительности сети.
Если вы используете функцию потерь из фреймворка, то убедитесь, что передаёте ей то что нужно. Например, в PyTorch я бы смешал NLLLoss и CrossEntropyLoss, потому что первая требует входных данных softmax, а вторая — нет.
Если ваша функция потерь состоит из нескольких функций, проверьте их соотношение относительно друг друга. Для этого может понадобиться тестирование в разных вариантах соотношений.
Иногда функция потерь — не лучший предиктор того, насколько правильно обучается ваша нейросеть. Если возможно, используйте другие показатели, такие как точность.
Вы самостоятельно реализовали какие-то из слоёв сети? Дважды проверьте, что они работают как полагается.
Посмотрите, может вы неумышленно отключили обновления градиента каких-то слоёв/переменных.
Может, выразительной мощности сети недостаточно для усвоения целевой функции. Попробуйте добавить слоёв или больше скрытых юнитов в полностью соединённые слои.
Если ваши входные данные выглядят как
, то легко пропустить ошибку, связанную с неправильными измерениями. Используйте необычные числа для измерений входных данных (например, разные простые числа для каждого измерения) и посмотрите, как они распространяются по сети.
Если вы самостоятельно реализовали Gradient Descent, то с помощью Gradient Checking можно убедиться в корректной обратной связи. Дополнительная информация: 1, 2, 3.
![](https://habrastorage.org/r/w1560/getpro/habr/post_images/1f6/298/603/1f6298603efa05d69690060e56aa7178.png)
Источник: http://carlvondrick.com/ihog/
Переобучите сеть на маленьком наборе данных и убедитесь в её работе. Например, обучите её всего с 1-2 примерами и посмотрите, способна ли сеть различать объекты. Переходите к большему количеству образцов для каждого класса.
Если не уверены, используйте инициализацию Ксавьера или Хе. К тому же, ваша инициализация может вывести на плохой локальный минимум, так что испытайте другую инициализацию, может поможет.
Может вы используете плохой набор гиперпараметров. Если возможно, попробуйте grid search.
Из-за слишком сильной регуляризации сеть может конкретно недообучиться. Уменьшите регуляризацию, такую как dropout, batch norm, L2-регуляризацию weight/bias и др. В отличном курсе «Практическое глубинное обучение для программистов» Джереми Говард рекомендует в первую очередь избавиться от недообучения. То есть нужно достаточно переообучить сеть на исходных данных, и только затем бороться с переобучением.
Может сети нужно больше времени на обучение, прежде чем она начнёт делать осмысленные предсказания. Если функция потерь стабильно уменьшается, дайте ей обучиться чуть подольше.
В некоторых фреймворках слои Batch Norm, Dropout и другие ведут себя по-разному во время обучения и тестирования. Переключение в подходящий режим может помочь вашей сети начать делать правильные прогнозы.
Ваш выбор оптимизатора не должен мешать нейросети обучаться, если только вы не выбрали конкретно плохие гиперпараметры. Но правильный оптимизатор для задачи может помочь получить наилучшее обучение за кратчайшее время. Научная статья с описанием того алгоритма, который вы используете, должна упомянуть и оптимизатор. Если нет, я предпочитаю использовать Adam или простой SGD.
Прочтите отличную статью Себастьяна Рудера, чтобы узнать больше об оптимизаторах градиентного спуска.
Низкая скорость обучения приведёт к очень медленному схождению модели.
Высокая скорость обучения сначала быстро уменьшит функцию потерь, а потом вам будет трудно найти хорошее решение.
Поэкспериментируйте со скоростью обучению, ускоряя либо замедляя её в 10 раз.
Состояния NaN (Non-a-Number) гораздо чаще встречаются при обучении RNN (насколько я слышал). Некоторые способы их устранения:
Почему нейросеть выдаёт мусор (например, среднее всех результатов или у неё реально слабая точность)? С чего начать проверку?
Сеть может не обучаться по ряду причин. По итогу многих отладочных сессий я заметил, что часто делаю одни и те же проверки. Здесь я собрал в удобный список свой опыт вместе с лучшими идеями коллег. Надеюсь, этот список будет полезен и вам.
Содержание
0. Как использовать это руководство?
I. Проблемы с набором данных
II. Нормализация данных/Проблемы аугментации
III. Проблемы реализации
IV. Проблемы обучения
0. Как использовать это руководство?
Многое может пойти не так. Но некоторые проблемы встречаются чаще, чем другие. Я обычно начинаю с этого маленького списка как набора экстренной помощи:
- Начните с простой модели, которая точно правильно работает для этого типа данных (например, VGG для изображений). Используйте стандартную функцию потерь, если возможно.
- Отключите все финтифлюшки, например, регуляризацию и аугментацию данных.
- В случае тонкой настройки модели дважды проверьте препроцессинг, чтобы он соответствовал обучению первоначальной модели.
- Удостоверьтесь в правильности входных данных.
- Начните с действительно маленького набора данных (2-20 образцов). Затем расширяйте его, постепенно добавляя новые данные.
- Начните постепенно добавлять обратно все фрагменты, которые были опущены: аугментация/регуляризация, кастомные функции потерь, пробуйте более сложные модели.
Если ничего не помогло, то приступайте к чтению этого длинного списка и проверяйте каждый пункт.
I. Проблемы с набором данных
![](https://habrastorage.org/getpro/habr/post_images/ef6/032/8ff/ef60328ff5f505334860490ca248270c.png)
Источник: http://dilbert.com/strip/2014-05-07
1. Проверьте входные данные
Проверьте, что входные данные имеют смысл. Например, я не раз смешивал в кучу высоту и ширину изображений. Иногда по ошибке отдавал в нейросеть все нули. Или использовал одну и ту же партию снова и снова. Так что напечатайте/посмотрите пару партий входных данных и плановых выходных данных — убедитесь, что всё в порядке.
2. Попробуйте случайные входные значения
Попробуйте передать случайные числа вместо реальных данных и посмотрите, останется ли та же ошибка. Если так, то это верный знак, что ваша сеть на каком-то этапе превращает данные в мусор. Попробуйте отладку слой за слоем (операция за операцией) и посмотрите, где происходит сбой.
3. Проверьте загрузчик данных
С данными всё может быть в порядке, а ошибка в коде, который передаёт входные данные нейросети. Распечатайте и проверьте входные данные первого слоя перед началом его операций.
4. Убедитесь, что вход соединяется с выходом
Проверьте, что несколько образцов входных данных снабжены правильными метками. Также проверьте, что смена местами входных образцов так же отражается на выходных метках.
5. Взаимоотношение между входом и выходом слишком случайно?
Может быть, неслучайные части взаимоотношения между входом и выходом слишком малы по сравнению со случайной частью (кто-то может сказать, что таковы котировки на бирже). То есть вход недостаточно связан с выходом. Тут нет универсального метода, потому что мера случайности зависит от типа данных.
6. Слишком много шума в наборе данных?
Однажды это случилось со мной, когда я стянул набор изображений продуктов питания с сайта. Там было так много плохих меток, что сеть не могла обучаться. Вручную проверьте ряд образцов входных значений и посмотрите, что все метки на месте.
Данный пункт достоин отдельного разговора, потому что эта работа показывает точность выше 50% на базе MNIST при 50% повреждённых меток.
7. Перемешайте набор данных
Если ваши данные не перемешаны и располагаются в определённом порядке (отсортированы по меткам), это может отрицательно отразиться на обучении. Перемешайте набор данных: убедитесь, что перемешиваете вместе и входные данные, и метки.
8. Снизьте несбалансированность классов
Может, в наборе данных тысяча изображений класса А на одно изображение класса Б? Тогда вам может понадобиться сбалансировать функцию потерь или попробовать другие подходы устранения несбалансированности.
9. Достаточно ли образцов для обучения?
Если вы обучаете сеть с нуля (то есть не настраиваете её), то может понадобиться очень много данных. Например, для классификации изображений, говорят, нужна тысяча изображений на каждый класс, а то и больше.
10. Убедитесь в отсутствии партий с единственной меткой
Такое случается в отсортированном наборе данных (то есть первые 10 тыс. образцов содержат одинаковый класс). Легко исправляется перемешиванием набора данных.
11. Уменьшите размер партий
Эта работа указывает, что слишком большие партии могут понизить у модели способность к обобщению.
Дополнение 1. Используйте стандартный набор данных (например, mnist, cifar10)
Спасибо hengcherkeng за это:
При тестировании новой сетевой архитектуры или написании нового кода сначала используйте стандартные наборы данных вместо своих. Потому что для них уже есть много результатов и они гарантированно «разрешимые». Там не будет проблем с шумом в метках, разницей в распределении обучение/тестирование, слишком большой сложностью набора данных и т.д.
II. Нормализация данных/Проблемы аугментации
![](https://habrastorage.org/getpro/habr/post_images/a37/7f1/71d/a377f171d5b6bdc0a01822ed42952033.png)
12. Откалибруйте признаки
Вы откалибровали входные данные на нулевое среднее и единичную дисперсию?
13. Слишком сильная аугментация данных?
Аугментация имеет регуляризующий эффект. Если она слишком сильная, то это вкупе с другими формами регуляризации (L2-регуляризация, dropout и др.) может привести к недообучению нейросети.
14. Проверьте предобработку предварительно обученной модели
Если вы используете уже подготовленную модель, то убедитесь, что используются та же нормализация и предобработка, что и в модели, которую вы обучаете. Например, должен пиксель быть в диапазоне [0, 1], [-1, 1] или [0, 255]?
15. Проверьте предварительную обработку для набора обучение/валидация/тестирование
CS231n указал на типичную ловушку:
«… любую статистику предобработки (например, среднее данных) нужно вычислять на данных для обучения, а потом применять на данных валидации/тестирования. Например, будет ошибкой вычисление среднего и вычитание его из каждого изображения во всём наборе данных, а затем разделение данных на фрагменты для обучения/валидации/тестирования».
Также проверьте на предмет наличия различающейся предварительной обработки каждого образца и партии.
III. Проблемы реализации
![](https://habrastorage.org/getpro/habr/post_images/a3c/00e/878/a3c00e878e007200b1f3501115b65066.png)
Источник: https://xkcd.com/1838/
16. Попробуйте решить более простой вариант задачи
Это поможет определить, где проблема. Например, если целевая выдача — это класс объекта и координаты, попробуйте ограничить предсказание только классом объекта.
17. Поищите правильную функцию потерь «по вероятности»
Снова из бесподобного CS231n: Инициализируйте с небольшими параметрами, без регуляризации. Например, если у нас 10 классов, то «по вероятности» означает, что правильный класс определится в 10% случаев, а функция потерь Softmax — это обратный логарифм к вероятности правильного класса, то есть получается
После этого попробуйте увеличить силу регуляризации, что должно увеличить функцию потерь.
18. Проверьте функцию потерь
Если вы реализовали свою собственную, проверьте её на баги и добавьте юнит-тесты. У меня часто бывало, что слегка неправильная функция потерь тонко вредила производительности сети.
19. Проверьте входные данные функции потерь
Если вы используете функцию потерь из фреймворка, то убедитесь, что передаёте ей то что нужно. Например, в PyTorch я бы смешал NLLLoss и CrossEntropyLoss, потому что первая требует входных данных softmax, а вторая — нет.
20. Отрегулируйте веса функции потерь
Если ваша функция потерь состоит из нескольких функций, проверьте их соотношение относительно друг друга. Для этого может понадобиться тестирование в разных вариантах соотношений.
21. Отслеживайте другие показатели
Иногда функция потерь — не лучший предиктор того, насколько правильно обучается ваша нейросеть. Если возможно, используйте другие показатели, такие как точность.
22. Проверьте каждый кастомный слой
Вы самостоятельно реализовали какие-то из слоёв сети? Дважды проверьте, что они работают как полагается.
23. Проверьте отсутствие «зависших» слоёв или переменных
Посмотрите, может вы неумышленно отключили обновления градиента каких-то слоёв/переменных.
24. Увеличьте размер сети
Может, выразительной мощности сети недостаточно для усвоения целевой функции. Попробуйте добавить слоёв или больше скрытых юнитов в полностью соединённые слои.
25. Поищите скрытые ошибки измерений
Если ваши входные данные выглядят как
26. Исследуйте Gradient Checking
Если вы самостоятельно реализовали Gradient Descent, то с помощью Gradient Checking можно убедиться в корректной обратной связи. Дополнительная информация: 1, 2, 3.
IV. Проблемы обучения
![](https://habrastorage.org/getpro/habr/post_images/1f6/298/603/1f6298603efa05d69690060e56aa7178.png)
Источник: http://carlvondrick.com/ihog/
27. Решите задачу для действительно маленького набора данных
Переобучите сеть на маленьком наборе данных и убедитесь в её работе. Например, обучите её всего с 1-2 примерами и посмотрите, способна ли сеть различать объекты. Переходите к большему количеству образцов для каждого класса.
28. Проверьте инициализацию весов
Если не уверены, используйте инициализацию Ксавьера или Хе. К тому же, ваша инициализация может вывести на плохой локальный минимум, так что испытайте другую инициализацию, может поможет.
29. Измените гиперпараметры
Может вы используете плохой набор гиперпараметров. Если возможно, попробуйте grid search.
30. Уменьшите регуляризацию
Из-за слишком сильной регуляризации сеть может конкретно недообучиться. Уменьшите регуляризацию, такую как dropout, batch norm, L2-регуляризацию weight/bias и др. В отличном курсе «Практическое глубинное обучение для программистов» Джереми Говард рекомендует в первую очередь избавиться от недообучения. То есть нужно достаточно переообучить сеть на исходных данных, и только затем бороться с переобучением.
31. Дайте время
Может сети нужно больше времени на обучение, прежде чем она начнёт делать осмысленные предсказания. Если функция потерь стабильно уменьшается, дайте ей обучиться чуть подольше.
32. Переходите от режима обучения в режим тестирования
В некоторых фреймворках слои Batch Norm, Dropout и другие ведут себя по-разному во время обучения и тестирования. Переключение в подходящий режим может помочь вашей сети начать делать правильные прогнозы.
33. Визуализируйте обучение
- Отслеживайте активации, веса и обновления для каждого слоя. Убедитесь, что отношения их величин совпадают. Например, отношение величины обновлений к параметрам (весам и смещениям) должно равняться 1e-3.
- Рассмотрите библиотеки визуализации вроде Tensorboard и Crayon. В крайнем случае, можно просто печатать значения весов/сдвигов/активаций.
- Будьте осторожны с активациями сетей со средним намного больше нуля. Попробуйте Batch Norm или ELU.
- Deeplearning4j указал, на что смотреть в гистограммах весов и сдвигов:
«Для весов эти гистограммы должны иметь примерно гауссово (нормальное) распределение, спустя какое-то время. Гистограммы сдвигов обычно начинаются с нуля и обычно заканчиваются на уровне примерно гауссова распределения (единственное исключение — LSTM). Следите за параметрами, которые отклоняются на плюс/минус бесконечность. Следите за сдвигами, которые становятся слишком большими. Иногда такое случается в выходном слое для классификации, если распределение классов слишком несбалансировано».
- Проверяйте обновления слоёв, они должны иметь нормальное распределение.
34. Попробуйте иной оптимизатор
Ваш выбор оптимизатора не должен мешать нейросети обучаться, если только вы не выбрали конкретно плохие гиперпараметры. Но правильный оптимизатор для задачи может помочь получить наилучшее обучение за кратчайшее время. Научная статья с описанием того алгоритма, который вы используете, должна упомянуть и оптимизатор. Если нет, я предпочитаю использовать Adam или простой SGD.
Прочтите отличную статью Себастьяна Рудера, чтобы узнать больше об оптимизаторах градиентного спуска.
35. Взрыв/исчезновение градиентов
- Проверьте обновления слоя, поскольку очень большие значения могут указывать на взрывы градиентов. Может помочь клиппинг градиента.
- Проверьте активации слоя. Deeplearning4j даёт отличный совет: «Хорошее стандартное отклонение для активаций находится в районе от 0,5 до 2,0. Значительный выход за эти рамки может указывать на взрыв или исчезновение активаций».
36. Ускорьте/замедлите обучение
Низкая скорость обучения приведёт к очень медленному схождению модели.
Высокая скорость обучения сначала быстро уменьшит функцию потерь, а потом вам будет трудно найти хорошее решение.
Поэкспериментируйте со скоростью обучению, ускоряя либо замедляя её в 10 раз.
37. Устранение состояний NaN
Состояния NaN (Non-a-Number) гораздо чаще встречаются при обучении RNN (насколько я слышал). Некоторые способы их устранения:
- Уменьшите скорость обучения, особенно если NaN появляются в первые 100 итераций.
- Нечисла могут возникнуть из-за деления на ноль, взятия натурального логарифма нуля или отрицательного числа.
- Рассел Стюарт предлагает хорошие советы, что делать в случае появления NaN.
- Попробуйте оценить сеть слой за слоем и посмотреть, где появляются NaN.
Источники
cs231n.github.io/neural-networks-3
russellsstewart.com/notes/0.html
stackoverflow.com/questions/41488279/neural-network-always-predicts-the-same-class
deeplearning4j.org/visualization
www.reddit.com/r/MachineLearning/comments/46b8dz/what_does_debugging_a_deep_net_look_like
www.researchgate.net/post/why_the_prediction_or_the_output_of_neural_network_does_not_change_during_the_test_phase
book.caltech.edu/bookforum/showthread.php?t=4113
gab41.lab41.org/some-tips-for-debugging-deep-learning-3f69e56ea134
www.quora.com/How-do-I-debug-an-artificial-neural-network-algorithm
russellsstewart.com/notes/0.html
stackoverflow.com/questions/41488279/neural-network-always-predicts-the-same-class
deeplearning4j.org/visualization
www.reddit.com/r/MachineLearning/comments/46b8dz/what_does_debugging_a_deep_net_look_like
www.researchgate.net/post/why_the_prediction_or_the_output_of_neural_network_does_not_change_during_the_test_phase
book.caltech.edu/bookforum/showthread.php?t=4113
gab41.lab41.org/some-tips-for-debugging-deep-learning-3f69e56ea134
www.quora.com/How-do-I-debug-an-artificial-neural-network-algorithm