![]() |
![]() |
![]() |
![]() |
Слияние словарей в PyTorch: зачем нужно и подводные камни |
|
МЕНЮ Главная страница Поиск Регистрация на сайте Помощь проекту Архив новостей ТЕМЫ Новости ИИ Голосовой помощник Разработка ИИГородские сумасшедшие ИИ в медицине ИИ проекты Искусственные нейросети Искусственный интеллект Слежка за людьми Угроза ИИ ИИ теория Внедрение ИИКомпьютерные науки Машинное обуч. (Ошибки) Машинное обучение Машинный перевод Нейронные сети начинающим Психология ИИ Реализация ИИ Реализация нейросетей Создание беспилотных авто Трезво про ИИ Философия ИИ Big data Работа разума и сознаниеМодель мозгаРобототехника, БПЛАТрансгуманизмОбработка текстаТеория эволюцииДополненная реальностьЖелезоКиберугрозыНаучный мирИТ индустрияРазработка ПОТеория информацииМатематикаЦифровая экономика
Генетические алгоритмы Капсульные нейросети Основы нейронных сетей Распознавание лиц Распознавание образов Распознавание речи Творчество ИИ Техническое зрение Чат-боты Авторизация |
2024-07-25 11:31 ![]() Сейчас нейросети стали настолько большими, что обучение большой сети на 1 видеокарте технически невозможно или займёт десятки и сотни лет. Кроме того, на большой обучающей выборке всплывают проблемы забывания сетью того, чему её учили вначале. Оглавление 1. Проблемы, решаемые через слияние словарей 1.1 Улучшение генерализации работы нейросети 1.2 Забывание нейросетью первых данных из датасета при последовательном обучении 1.3 Ускорение обучения нейросети за счёт распараллеливания обучения 2. Вводные для успешного слияния словарей 2.1 Исходная сеть всегда одна и та же 2.2 Успех слияния зависит от близости каждого словаря к глобальному минимуму ошибки 2.3. Слияние словарей с разными минимумами ошибки, полученных на разных кусках обучающих данных 3. Техника слияния словарей 3.1. Простейший пример слияния 3.2. Почему простейший пример нельзя использовать для слияния тысяч словарей 3.3. Способы решения проблем с потерей точности 3.4. Кручу-верчу, float64 хочу 3.5 И многопроцессность для скорости! 3.6 Полная версия скрипта для слияния словарей 4. Итоги 1. Проблемы, решаемые через слияние словарей ? 1.1. Улучшение генерализации работы нейросети В работе было показано, что усреднение весов моделей, полученных в ходе одного запуска, но сохранённых в разные моменты времени (чекпойты), повышало качество работы нейросети. В PyTorch эту стратегию можно запустить с использованием модуля SWA из torchcontrib.optim. Есть статья на сайте PyTorch об использовании SWA. Единственно напрягает, что код уже около 5 лет не обновлялся. Так что его работоспособность под сомнением. Вот ссылка на GitHub c примерами. По результатам тестов видно, что после SWA точность нейросети повысилась примерно на 1%. Нужно ли вам такое улучшение — решать только вам. Для этой стратегии не требуются хитрости и трюки из оставшейся части статьи. Хотя, возможно, предложенные ниже способы слияния словарей будут лучше. Я не проверял, потому что меня интересовало слияние словарей, полученных после обучения на разных кусках данных. ? 1.2. Забывание нейросетью первых данных из датасета при последовательном обучении Эта проблема широко известна разработчикам нейросетей. Она называется Catastrophic interference, что можно перевести как «катастрофическое забывание/размазывание/интерференция». Есть много подходов для её решения. Ещё в 1989 году McCloskey and Cohen (1989) изучали катастрофическое забывание при последовательном обучении нейросетей, и один из выводов звучал так: Interference was catastrophic in the backpropagation networks when learning was sequential but not concurrent Один из способов параллельного обучения состоит в том, чтобы разделить обучающий датасет на куски и обучать исходную сеть на каждом таком куске. По результатам такого обучения мы получим кучу комплектов весов (словарей в терминах PyTorch), которые как-то нужно слить в один словарь. ? 1.3. Ускорение обучения нейросети за счёт распараллеливания обучения С этим способом всё понятно, так как мы не можем масштабировать видеокарту/ускоритель за счёт бесконечного увеличения скорости её работы, нам приходится использовать целые фермы ускорителей, чтобы получить обученную сеть в приемлемое время. 2. Вводные для успешного слияния словарей ? 2.1. Исходная сеть всегда одна и та же Обычный подход к обучению нейросети (и так реализовано в PyTorch), что она инициализируется случайными весами. Если мы будем каждый раз обучать разную нейросеть, то ничего хорошего при слиянии не выйдет. ? 2.2. Успех слияния зависит от близости каждого словаря к глобальному минимуму ошибки При обсуждении успешности обучения нейросетей через сливание словарей на форуме PyTorch инженер Patric Black из Nvidia заметил, что слияние может быть успешным при близости к глобальному минимуму ошибки. ![]()
Совершенно неочевидно какой подход выбрать (я экспериментировал со вторым), и есть ли среди этих подходов самый правильный. Детальнее, как проконтролировать, какие словари допустить к слиянию, а какие нет (или какие из них нужно как-то перевычислить, чтобы они стали ближе к глобальному минимуму ошибки), возможно, мы обсудим в следующей статье. ? 2.3. Слияние словарей с разными минимумами ошибки, полученных на разных кусках обучающих данных При слиянии словарей полученных в результате обучения одной и той же предобученной сети на разных кусках обучающего датасета мы практически всегда вначале имеем словари с разными локальными минимумами. Станет ли от этого после слияния итоговая нейросеть полностью неточной и бесполезной? 3. Техника слияния словарей ? 3.1 Простейший пример слияния
Как мы видим, используется усреднение. Пример кода взят с форума PyTorch. Согласно научной работе это вполне хороший подход для усреднения словарей в рамках одного прогона для разных чекпойнтов под названием «стохастическое усреднение весов» (Stochastic Weight Averaging). ? 3.2 Почему простейший пример нельзя использовать для слияния тысяч словарей Представим, что нам нужно слить 1 000 весов. Формула для слияния будет выглядеть примерно так:
Понятно, что тут можно написать в виде цикла, чтобы обойтись без гигантской строки. Суть в том, что float32 (тип данных по умолчанию в PyTorch) имеет 23 бита для хранения цифр после запятой, это примерно 6-7 десятичных цифр. Проблема в том, что при усреднении большого числа словарей мы будем сильно, а иногда колоссально терять в точности. Просто потому, что число знаков в мантиссе ограничено, и значащие цифры будут из неё выпадать при добавлении маленьких чисел к большому (сумме). При увеличении числа слагаемых сумма может стать настолько большой, что добавление любого числа маленьких чисел никак на ней никак не отразится. Сложим, например, 2 числа: 1.111111 * 10^32 1.111111 * 10^-32 Чтобы их сложить, экспонента второго числа должна быть приведена к экспоненте первого числа. И второе число превратится в 0.0000000000000000000000000000000000000000000000000000000000000001111111 * 10^32 Поскольку число значащих знаков в мантиссе FLOAT32 всего 23 бита, то второе число после приведения экспоненты будет приравнено к нулю. Посмотрим код с четырьмя способами усреднения:
В этом коде мы усредняем значение 1/311 (довольно маленький вес) в 1024 ячейках 4 различными способами. И выводим ошибку в процентах для каждого способа. Результат:
Первая колонка — само число после усреднения, вторая ошибка в процентах.
Как вы и догадались, я использовал последний способ усреднения словарей. ? 3.3 Способы решения проблем с потерей точности Классический способ — это алгоритм Кэхэна, вот статья на Хабре на тему суммирования. Попарный древовидный алгоритм сложения (из предыдущего пункта статьи) показался мне более простым, чем алгоритм Кэхэна. ![]() ? 3.4 Кручу-верчу, float64 хочу И для ещё большей точности мы перед усреднением будем конвертировать словари в float64. А полученный итоговый словарь сконвертируем опять в float32. Это даст дополнительный прирост точности. И наша нейросеть будет точнее. ? 3.5 И многопроцессность для скорости! Если словарей много, то целесообразно всё делать параллельно. Для этого прекрасно подойдёт метод pool.map(). Про него немного есть в моей предыдущей статье. ? 3.6 Полная версия скрипта для слияния словарей Всё в спойлере. Первый аргумент — директория, где лежат словари для усреднения. 4. Итоги На своих нейросетях (256*16 нейронов) я увидел существенный прирост точности от попарного пирамидального усреднения (с предварительной конверсией в FP64) в сравнении с наивным усреднением в FP32. Фактически при наивном усреднении вообще ничего не работало на большом числе словарей, сеть давала рандомные результаты. Из-за того, что при многослойности ошибки от слоя к слою росли лавинообразно. После внедрения попарного суммирования на всём наборе данных я увидел рост точности работы нейросети. Источник: habr.com Комментарии: |
|