Свидетели Градиента
Написал тут в комментариях так, что аж самому понравилось. Скопирую сюда: Это, конечно, движение обратно к датасетам, но зато малой кровью можно многое разведать... На мой взгляд нет. К чему все эти пляски со сложным миром, состоящим из простых подзадач…
1:13:11 Как увеличивать размер батча я долго и нудно рассказывал ещё в своем первом докладе на ODS-е, и ту ошибку в SGD with momentum в торче так и не исправили до сих пор, только предупреждение в документацию вписали. :) Но большинство то пользуется Adam-ом, где этой ошибки нет, а значит самым дешёвым способом увеличения размера батча можно пользоваться прямо из коробки, а ты его не упомянул. А самый дешёвый способ - выкрутить ему momentum, в случае Адама - это betas. по умолчанию там betas=(0.9, 0.99) но при первом же подозрении, что дисперсия градиента великовата можно ставить, например (0.99, 0.9975) - значит предположение, что проблема с дисперсией правильное и можно начинать возиться с масштабированием батчей, накоплением градиентов и тому подобным. По сути, на пратике, это часто оказывается даже быстрее и проще, чем рисовать траекторию, на которой всё это видно.
Кстати, интересное наблюдение Adam от RMSProp как раз и отличается тем, что в него прям из коробки встроен некоторый некоторый прогрев, но оказалось, что для больших батчей его недостаточно. Просто потому что betas=(0.9, 0.99), а большой батч, значит - редкие вызовы, значит второй момент, который и так идёт со сглаживанием с окном 100 очень медленно копится. Вместо долгого прогрева можно один раз посчитать пакетный градиент даже не от всей, а от части учебной выборки и положить адаму напрямую в те переменные где он накапливает моменты. Проверено - работает.
1:14:40 "Тут, к сожалению математика закончилась", что сразу закончилась то? :) Просто нужно вспомнить, что если для SDG lr это скорость обучения, то для Adam lr это не скорость, а ожидаемая дисперсия изменения веса, А дисперсия градиента с ростом размера батча увеличивается как раз как корень от количества усредняемых значений, просто из законов матстатистики. Я об этом говорил в первом докладе, и я уверен, что и студентам нужно это говорить, чтобы они лучше понимали как работает оптимизатор по умолчанию, потому что именно его они больше всего и будут пользовать.
1:15:30 LARS это конечно, прекрасно, но если не объяснять на формулах, а просто нарисовать диаграмму как градиенты распределяются в сети слой за слоем, то это станет очевидно, и что с этим делать тоже. любому человеку, который имеет сложности со своей сетью следует её нарисовать, тем более, что я осенью выложил в опенсорс инструмент, который делает это в один вызов.
1:16:30 Я, кстати, не исключаю, что в LAMB-е нормировать нужно не на градиент, а на его дисперсию, по вышеупомянутой причине. Если у тебя есть бейзлайн можешь проверить. Держим в голове, что чем более сеть обучена тем больше у неё разница между градиентогм и дисперсией, и если в начале она может быть, напрмер в 10 раз и вообще не отличаться, то к концу запросто достигает 3 порядков.
Слишком рано отправил, сейчас ещё комментариев допишу.
Кстати, интересное наблюдение Adam от RMSProp как раз и отличается тем, что в него прям из коробки встроен некоторый некоторый прогрев, но оказалось, что для больших батчей его недостаточно. Просто потому что betas=(0.9, 0.99), а большой батч, значит - редкие вызовы, значит второй момент, который и так идёт со сглаживанием с окном 100 очень медленно копится. Вместо долгого прогрева можно один раз посчитать пакетный градиент даже не от всей, а от части учебной выборки и положить адаму напрямую в те переменные где он накапливает моменты. Проверено - работает.
1:14:40 "Тут, к сожалению математика закончилась", что сразу закончилась то? :) Просто нужно вспомнить, что если для SDG lr это скорость обучения, то для Adam lr это не скорость, а ожидаемая дисперсия изменения веса, А дисперсия градиента с ростом размера батча увеличивается как раз как корень от количества усредняемых значений, просто из законов матстатистики. Я об этом говорил в первом докладе, и я уверен, что и студентам нужно это говорить, чтобы они лучше понимали как работает оптимизатор по умолчанию, потому что именно его они больше всего и будут пользовать.
1:15:30 LARS это конечно, прекрасно, но если не объяснять на формулах, а просто нарисовать диаграмму как градиенты распределяются в сети слой за слоем, то это станет очевидно, и что с этим делать тоже. любому человеку, который имеет сложности со своей сетью следует её нарисовать, тем более, что я осенью выложил в опенсорс инструмент, который делает это в один вызов.
1:16:30 Я, кстати, не исключаю, что в LAMB-е нормировать нужно не на градиент, а на его дисперсию, по вышеупомянутой причине. Если у тебя есть бейзлайн можешь проверить. Держим в голове, что чем более сеть обучена тем больше у неё разница между градиентогм и дисперсией, и если в начале она может быть, напрмер в 10 раз и вообще не отличаться, то к концу запросто достигает 3 порядков.
Слишком рано отправил, сейчас ещё комментариев допишу.
YouTube
24. Методы оптимизации в непрерывном времени. Gradient Flow. Стох. и ускоренная версии. МФТИ 2024
Лекция курса "Методы оптимизации". МФТИ 2023 - 2024 уч. год.
Сайт с материалами курса mipt23.fmin.xyz
Сайт с материалами курса mipt23.fmin.xyz
1:21:26 Тут, по-моему, очень важно упомянуть размерность хранимых данных, чтобы сформировалось интуитивное понимание сколько и каких данных хранится, и соответственно где и сколько можно съэкономить. Потому что руками многие уже ничего в этой жизни не дифференциировали и как оно под капотом работает не помнят или уже и не знают. Если у нас матрица n x n, сами веса весят n^2, их градиенты - столько же, а вот для автодифференциирования достаточно сохранить данные об активациях, пришедших из предыдущего слоя. то есть n*batch_size. И ещё два раза по n^2 хранит у себя в состоянии Adam. А данные о градиенте используются только при обратном проходе имеют размерность n*batch_size и уже не хранятся вообще, а только в ходе прохода используются. Если об этом помнить, то можно сделать несколько важных выводов:
1) При первом проходе для первых нод вообще не сохранять активации, тоесть выключать им grad_requiredдо тех пор пока с обратной стороны не подъедет градиент, экономим n*batch_size, но это возможно и так из коробки делается, я под капот конкретным реализация не заглядывал.
2) Если в много проходов строить градиент, и только потом выхзывать оптимизатор, то градиент придётся хранить для тех нод, которые уже посчитаны. Лишние расходы. Посчитали активации для одного последнего слоя, посчитали обратное распространение для одного слоя, посчитали градиенты, сразу сделали шаг оптимизатора, но не для всей модели, а для одного последнего слоя, очистили место для хранения градиентов, только теперь начингаем считать градинты следующего с конца слоя, а градиенты размером n*batch_size для одного слоя, а не всей модели, всё это время в памяти лежали. Итоговая экономия - один размер модели.
3) Если помнить, что через эмбединг туда и сюда ходит матрица размерами всего n*batch_size джля одного слоя можно разбивать модель на две карты по этому узкому месту. Делаешь первый проход на первой карте без автодифференциирования для первой половины модели, передаёшь матрицу на вторую карту, прогоняешь через вторую половину модели уже с автодифом, назад получаешь градиент, который перехватил в бакварде, передал обратно не первую карту, ещё раз прогнал прямой проход, на этот раз с автодифом, и беквард по первой половине модели. Причём если ты аккумуляируешь градиент, то можно через первую карту прогнать пол батча, а пока вторая карта разбирается с градиентами прогрнать forward для другой половины батча.
Как не трудно догадаться 4 карты по 16 гигов стоят сильно дешевле, чем одна на 64. :))))
1) При первом проходе для первых нод вообще не сохранять активации, тоесть выключать им grad_requiredдо тех пор пока с обратной стороны не подъедет градиент, экономим n*batch_size, но это возможно и так из коробки делается, я под капот конкретным реализация не заглядывал.
2) Если в много проходов строить градиент, и только потом выхзывать оптимизатор, то градиент придётся хранить для тех нод, которые уже посчитаны. Лишние расходы. Посчитали активации для одного последнего слоя, посчитали обратное распространение для одного слоя, посчитали градиенты, сразу сделали шаг оптимизатора, но не для всей модели, а для одного последнего слоя, очистили место для хранения градиентов, только теперь начингаем считать градинты следующего с конца слоя, а градиенты размером n*batch_size для одного слоя, а не всей модели, всё это время в памяти лежали. Итоговая экономия - один размер модели.
3) Если помнить, что через эмбединг туда и сюда ходит матрица размерами всего n*batch_size джля одного слоя можно разбивать модель на две карты по этому узкому месту. Делаешь первый проход на первой карте без автодифференциирования для первой половины модели, передаёшь матрицу на вторую карту, прогоняешь через вторую половину модели уже с автодифом, назад получаешь градиент, который перехватил в бакварде, передал обратно не первую карту, ещё раз прогнал прямой проход, на этот раз с автодифом, и беквард по первой половине модели. Причём если ты аккумуляируешь градиент, то можно через первую карту прогнать пол батча, а пока вторая карта разбирается с градиентами прогрнать forward для другой половины батча.
Как не трудно догадаться 4 карты по 16 гигов стоят сильно дешевле, чем одна на 64. :))))
YouTube
23. Обучение больших нейросетей. Loss surface. Grokking. double Descent. Large batch. МФТИ 2024
Лекция курса "Методы оптимизации". МФТИ 2023 - 2024 уч. год.
Сайт с материалами курса mipt23.fmin.xyz
Сайт с материалами курса mipt23.fmin.xyz
А ещё я обещал картинки. Первая картинка как раз мера генерализации. Здесь хорошо видно, как и на траектории, что гроккинг это непрерывный процесс, и loss скачком меняется только в самом его конце.
🤔1
Если ландшафт Loss-а носит характер каньона, заворачивающегося с относительно небольшой скоростью, то в проекции на прямую градиента, обозначенную на рисунке красным, он будет выглядеть как парабола, что создаёт иллюзию локального минимума, потому что эта проекция вылезает на стенки, но вдоль дня каньона это будет прямая с почти нулевой второй производной.
А как мы знаем из моего осеннего доклада, направление каньона в некоторых случаях меняется совсем медленно, всего на несколько градусов за шаг, а по мере обучения кривизна каньона стремительно растёт доходя до поворотов чуть ли не под прямым углом на каждом шаге той же скорости пока оптимизатор не начинает тупо прошивать стенки за счёт инерции попадая в соседние каньоны или приводя к частичному разрушению сети, которые на графике гроккинга выглядят как спайки вниз.
P.S. Обрати внимание, на спайках генерализация улучшается, потому что частично разрушенная сеть возвращается в плохо обученное состояние, когда изучение трейна одинаково полезно и для валидейшена.
P.P.S. Обрати внимание, после спайка сеть очень быстро восстанавливается, потому что у неё во многих слоях уже есть эффективные и несущие много информации обобщения, и задача только в том, чтобы по новой к ним присосаться. Это гвоорит о том, как вожно помнить о роли генерализации - как полезных обобщений даже если loss-ы почему-то показывают погоду в тьмутаракани.
А как мы знаем из моего осеннего доклада, направление каньона в некоторых случаях меняется совсем медленно, всего на несколько градусов за шаг, а по мере обучения кривизна каньона стремительно растёт доходя до поворотов чуть ли не под прямым углом на каждом шаге той же скорости пока оптимизатор не начинает тупо прошивать стенки за счёт инерции попадая в соседние каньоны или приводя к частичному разрушению сети, которые на графике гроккинга выглядят как спайки вниз.
P.S. Обрати внимание, на спайках генерализация улучшается, потому что частично разрушенная сеть возвращается в плохо обученное состояние, когда изучение трейна одинаково полезно и для валидейшена.
P.P.S. Обрати внимание, после спайка сеть очень быстро восстанавливается, потому что у неё во многих слоях уже есть эффективные и несущие много информации обобщения, и задача только в том, чтобы по новой к ним присосаться. Это гвоорит о том, как вожно помнить о роли генерализации - как полезных обобщений даже если loss-ы почему-то показывают погоду в тьмутаракани.
👍3🤔1
step000599_angles_by_modules.png
466.9 KB
Ну, можно констатировать, что генерализация при гроккинге очень неравномерно распределяется по слоям модели... Хотя и не так, как я думал... Интересно было бы посмотреть, как это будет менятся при прунинге, конечно, но шибко долго все эти экзотические меры считаются...
👍2🤔1
Это фиаско....
Есть две метрики, которые уменьшаются при гроккинге вообще и если weight_decay давит на сеть в частности, логически связанные друг с другом и прямо вытекающие из моей теории о том, что такое гроккинг. Одна из них меняется для обычной сети в районе 60-70, а при гроккинге падает до не больше 20. Другая, на картинке, совершенно иная по построению, зависящая от специфики сети, в отличии от прошлой, хитронормированной, при гроккинге снижается до 0.15, а без wd растёт куда-то в область 0.5 для данной конкретной сети.
Обе метрики, если хорошо подумать и много повозиться - дифференциируемы, так что можно попытаться их напрямую оптимизировать. Даже если отключить wd и оптимизировать только вторую метрику, можно увидеть как чуть-чуть снижается и первая (на третьей картинке). Но при этом попытка напрямую эти характеристики оптимизировать и близко не подводят их к значениям, характерным для гроккинга, и достигаемым при включении wd. причём оптимизация проваливается в обоих случаях из-за дестабилизации сети, которая и при гроккинге со включённым wd тоже происходит.
Спрашивается в задачнике, и чё теперь с этим всем делать. :((
P.S. Возможно процесс будет более стабилен при каких-нибудь волшебных гиперпараметрах. Но как бы хотелось,, чтобы он без всяких плясок с бубном бац и просто увеличивал в сети генерализацию... Но нет, они даже неприближают гроккинг при включённом wd, когда он и так уже приближается.
P.P.S. 1100 строк кода потрачено но фреймворк для гроккинга на текущий момент, и это ещё без прунинга. :(
Есть две метрики, которые уменьшаются при гроккинге вообще и если weight_decay давит на сеть в частности, логически связанные друг с другом и прямо вытекающие из моей теории о том, что такое гроккинг. Одна из них меняется для обычной сети в районе 60-70, а при гроккинге падает до не больше 20. Другая, на картинке, совершенно иная по построению, зависящая от специфики сети, в отличии от прошлой, хитронормированной, при гроккинге снижается до 0.15, а без wd растёт куда-то в область 0.5 для данной конкретной сети.
Обе метрики, если хорошо подумать и много повозиться - дифференциируемы, так что можно попытаться их напрямую оптимизировать. Даже если отключить wd и оптимизировать только вторую метрику, можно увидеть как чуть-чуть снижается и первая (на третьей картинке). Но при этом попытка напрямую эти характеристики оптимизировать и близко не подводят их к значениям, характерным для гроккинга, и достигаемым при включении wd. причём оптимизация проваливается в обоих случаях из-за дестабилизации сети, которая и при гроккинге со включённым wd тоже происходит.
Спрашивается в задачнике, и чё теперь с этим всем делать. :((
P.S. Возможно процесс будет более стабилен при каких-нибудь волшебных гиперпараметрах. Но как бы хотелось,, чтобы он без всяких плясок с бубном бац и просто увеличивал в сети генерализацию... Но нет, они даже неприближают гроккинг при включённом wd, когда он и так уже приближается.
P.P.S. 1100 строк кода потрачено но фреймворк для гроккинга на текущий момент, и это ещё без прунинга. :(
😭2
Есть у меня придуманная ещё в древнем 2015-ом году регуляризация, предназначенная для облегчения прунинга. Врубил её вместо обычной. Гроккинг получил в четыре раза медленнее, чем при обычной и тупой.
В процессе моя регуляризация ненавязчиво обнулила пол сети, Что, с одной стороны, явно недостаточно для обобщения через прунинг, а с другой намекает, на то что дистиляция работы в более меленькую модель, возможно, не является главной в процессе гроккинга.
+1 результат хоть и не отрицательный, но не так чтобы особо положительный.
В процессе моя регуляризация ненавязчиво обнулила пол сети, Что, с одной стороны, явно недостаточно для обобщения через прунинг, а с другой намекает, на то что дистиляция работы в более меленькую модель, возможно, не является главной в процессе гроккинга.
+1 результат хоть и не отрицательный, но не так чтобы особо положительный.
🤔2
В контексте нейросетей нужно рассматривать сходимость и прекрасность методов не на выпуклой функции, а на совершенно другой, которая когда-нибудь станет эталонной...
Смотрю очередную лекцию Дани Меркулова(@bratishk). Как обычно очень энергично и весело, хоть и понятно только в общих чертах, я столько математики за один раз с университетской скамьи не видел. Понял с какой стороны к методам с импульсом пришли в прошлый раз, и то что я заходя со стороны дисперсии градиентов, зашёл с совершенно другой стороны, и в этом есть математическая красота и прикосновение к законам мироздания, что пришли мы в итоге в одно и то же место.
И в то же время всё это вызывает у меня священную ярость. Пока ещё не на столько чтобы бросить гроккинг и садиться все свободное время рисовать слайдики, но до берсеркеранка уже не так далеко. Если в чате есть что-то, кто хочет к весеннему датафесту подготовить крутейший доклад по академии, пишите-звоните, я подробно объясню что делать.
И так, как на мой взгляд (не полный) выглядит поверхность Loss функции для достаточно параметризованной модели подходящей архитектуры, и как, соответственно, должна выглядеть эталонная функция для сравнения методов обучения:
1) У неё нет локальных минимумов. (В любой точке кроме глобального минимума есть направление снижения loss или направление где loss в окрестностях вычислительной ошибки от нуля).
2) Её рельеф в каждой точке это каньон, сравнительно узкий и поворачивающийся под углом. Если брать не двумерную задачу, то и плоскость поворота тоже постепенно меняется.
3) Радиус кривизны поворота в среднем (но неравномерно) растёт по мере приближения к глобальному минимуму, так что при любой наперёд заданной скорости найдётся место, где алгортм в поворот уже не вписывается.
4) Ширина каньона в среднем сужается. (эмпирическая вторая производная в наилучшем направлени перпендикулярном направлению каньона).
5) В каньоне возможны развилки и тупики.
6) Расстояние "по дну" до глобального мимнимума не в разы, и даже не на порядок, а на много порядков превосходит расстояние до него по прямой.
8) Выйдя из каньона на небольшое расстояние а любую сторону градиентным спуском можно найти соседний каньон, часто лучше прежнего. (Как меняется расстояние до соседнего каньона в зависимости от расстояния случайного отступа от дня текущего предстоит ещё эмпирически изучить, Также как вероятность того, что новый каньон будет как минимум не хуже прежнего.
Очевидно, что функции которые хороши в сходимости на красивом рельефе или даже тупо выпуклом совершенно не обязятельно хороши на рельефе описанном выше. В частности очевидно почему нормальный градиентный спуск на градиенте собираемом на всём учебном датасете хуже любого метода иногда двигающегося тем или иным способом не по градиенту.
Это должно быть похоже на что-то вроде того, что на картинке. Ну и ещё глобальны минимум надо из точки (0,0) сдвинуть чтобы регуляризация тянула мимо глобального минимума.
P.S. Збыл добавить множитель, который будет делать каньоны более широкими вдалеке от глобального минимума, но это и самостоятельно не сложно.
Смотрю очередную лекцию Дани Меркулова(@bratishk). Как обычно очень энергично и весело, хоть и понятно только в общих чертах, я столько математики за один раз с университетской скамьи не видел. Понял с какой стороны к методам с импульсом пришли в прошлый раз, и то что я заходя со стороны дисперсии градиентов, зашёл с совершенно другой стороны, и в этом есть математическая красота и прикосновение к законам мироздания, что пришли мы в итоге в одно и то же место.
И в то же время всё это вызывает у меня священную ярость. Пока ещё не на столько чтобы бросить гроккинг и садиться все свободное время рисовать слайдики, но до берсеркеранка уже не так далеко. Если в чате есть что-то, кто хочет к весеннему датафесту подготовить крутейший доклад по академии, пишите-звоните, я подробно объясню что делать.
И так, как на мой взгляд (не полный) выглядит поверхность Loss функции для достаточно параметризованной модели подходящей архитектуры, и как, соответственно, должна выглядеть эталонная функция для сравнения методов обучения:
1) У неё нет локальных минимумов. (В любой точке кроме глобального минимума есть направление снижения loss или направление где loss в окрестностях вычислительной ошибки от нуля).
2) Её рельеф в каждой точке это каньон, сравнительно узкий и поворачивающийся под углом. Если брать не двумерную задачу, то и плоскость поворота тоже постепенно меняется.
3) Радиус кривизны поворота в среднем (но неравномерно) растёт по мере приближения к глобальному минимуму, так что при любой наперёд заданной скорости найдётся место, где алгортм в поворот уже не вписывается.
4) Ширина каньона в среднем сужается. (эмпирическая вторая производная в наилучшем направлени перпендикулярном направлению каньона).
5) В каньоне возможны развилки и тупики.
6) Расстояние "по дну" до глобального мимнимума не в разы, и даже не на порядок, а на много порядков превосходит расстояние до него по прямой.
8) Выйдя из каньона на небольшое расстояние а любую сторону градиентным спуском можно найти соседний каньон, часто лучше прежнего. (Как меняется расстояние до соседнего каньона в зависимости от расстояния случайного отступа от дня текущего предстоит ещё эмпирически изучить, Также как вероятность того, что новый каньон будет как минимум не хуже прежнего.
Очевидно, что функции которые хороши в сходимости на красивом рельефе или даже тупо выпуклом совершенно не обязятельно хороши на рельефе описанном выше. В частности очевидно почему нормальный градиентный спуск на градиенте собираемом на всём учебном датасете хуже любого метода иногда двигающегося тем или иным способом не по градиенту.
Это должно быть похоже на что-то вроде того, что на картинке. Ну и ещё глобальны минимум надо из точки (0,0) сдвинуть чтобы регуляризация тянула мимо глобального минимума.
P.S. Збыл добавить множитель, который будет делать каньоны более широкими вдалеке от глобального минимума, но это и самостоятельно не сложно.
👍2
Ну что ж, отрицательными результатами тоже надо иногда делиться. Итак берём ту же задачку гроккинга, что и в прошлых двух докладах, Берём модельку минимального допустимого размера (ембединг 96), грокаем и начинаем жестоко прунить. Остаётся всего 3800 весов, что кажется новый рекорд, хотя в этом деле ещё есть куда расти. Видно, что работа сосредоточилась в первой половине первого селфатеншена и трёх заключительных полносвязанных слоях.
Однако тут возможны две трактовки. С одной стороны сосредоточение работы в первых слоях может быть фундаментальным свойством гроккинга, с другой, у первых и последний слоёв просто всегда выше градиент в силу чисто математических причин. Попробуем проверить может ли прунинг заставить перенести расссчёты в более поздние слои.
Однако тут возможны две трактовки. С одной стороны сосредоточение работы в первых слоях может быть фундаментальным свойством гроккинга, с другой, у первых и последний слоёв просто всегда выше градиент в силу чисто математических причин. Попробуем проверить может ли прунинг заставить перенести расссчёты в более поздние слои.
👍2
Мы знаем, что иногда прунинг может достигать результатов похожих на гроккинг. В этом примере я беру такую же сеть, только чуть побольше и при нулевом weight_decay начинаю её прунить. Результат не такой красивый, но всё-таки интересный.
👍2