Китайцы оформили вполне очевидную идею в виде статьи с формулами, графиками и всем прочим. Назвали метод SWITCH EMA (SEMA) вместо Exponential Moving Average (EMA) . Идея в том, чтобы периодически присваивать исходной модели веса EMA-модели. https://arxiv.org/pdf/2402.09240 .
Жду статью с подбором оптимального decay для EMA. От себя предложу такой алгоритм: берём основную EMA-модель и две параллельных с чуть большим и чуть меньшим decay. И периодически смотрим, какая из трёх EMA моделей даёт лучшее значение loss-а на валидации. Её далее используем как основную и из неё порождаем две параллельных. И так по кругу. Интересно было бы посмотреть, как decay будет меняться по мере обучения модели и изменения LR.
Жду статью с подбором оптимального decay для EMA. От себя предложу такой алгоритм: берём основную EMA-модель и две параллельных с чуть большим и чуть меньшим decay. И периодически смотрим, какая из трёх EMA моделей даёт лучшее значение loss-а на валидации. Её далее используем как основную и из неё порождаем две параллельных. И так по кругу. Интересно было бы посмотреть, как decay будет меняться по мере обучения модели и изменения LR.
В продолжение прошлого поста про EMA. Не совсем ясный момент, как быть с буферами модели, где, например, хранятся статистики по BarchNorm-ам.
В Stochastic Weight Averaging (SWA), который был предшественником EMA, предлагается пересчитывать батчнормы https://pytorch.org/blog/stochastic-weight-averaging-in-pytorch/ одну эпоху после усреднения весов https://pytorch.org/blog/stochastic-weight-averaging-in-pytorch/ . Вот функция для этого https://github.com/pytorch/contrib/blob/master/torchcontrib/optim/swa.py#L274 .
В
Ещё бывает, что статистики у BatchNorm-а обновляются во время тренировки, когда через EMA-модель пропускают данные.
Есть улучшенный вариант BatchNorm-а, в котором предлагается через EMA усреднять статистики у BN и тем самым сеть якобы улучшается: https://arxiv.org/pdf/2101.08482 .
Вопрос на засыпку: а как правильно?
В Stochastic Weight Averaging (SWA), который был предшественником EMA, предлагается пересчитывать батчнормы https://pytorch.org/blog/stochastic-weight-averaging-in-pytorch/ одну эпоху после усреднения весов https://pytorch.org/blog/stochastic-weight-averaging-in-pytorch/ . Вот функция для этого https://github.com/pytorch/contrib/blob/master/torchcontrib/optim/swa.py#L274 .
В
timm статистики у BatchNorm-а усредняют по дефолту, но есть возможность просто копировать их https://github.com/huggingface/pytorch-image-models/blob/main/timm/utils/model_ema.py#L133 .Ещё бывает, что статистики у BatchNorm-а обновляются во время тренировки, когда через EMA-модель пропускают данные.
Есть улучшенный вариант BatchNorm-а, в котором предлагается через EMA усреднять статистики у BN и тем самым сеть якобы улучшается: https://arxiv.org/pdf/2101.08482 .
Вопрос на засыпку: а как правильно?
👍1
Профайлир вчера свой скрипт, обучающий нейронку и обнаружил, что 30% времени тратися вот этой строчке https://github.com/pytorch/pytorch/blob/main/torch/amp/grad_scaler.py#L351 . Там с GPU на CPU в цикле тащутся данные, что естественно жутко медленно. В обычной жизни эта строчка никогда не работает, потому что все используют оптимизаторы из торча. А же взял AdamP из timm.
Как я понял, если обучать модельку в режиме Automatic Mixed Precision, который сильно ускоряет и уменьшает расход памяти, то обычно используется скалинг градиентов. И после того, как сделан обратный проход градиенты нужно сначала unscale обратно, а потом уже использовать оптимизатор. Т.е. сначала по всем градиентам проходится torch.GradScaler, а потом оптимизаитор. Чтобы всё ускорить, оптимизаторы из торча умеют сами unscale градиенты и проверять их на inf . А те опимизаторы, которые не из торча, конечно ничего подобного не умеют и потом за них эту работу делает torch.GradScaler, который, как оказалось, написан не очень эффективно. 😞
P.S.
Попробовал с AdamW из торча. Там картина точно такая же, к сожалению. Что-то мне даже кажется, что скорость обучения в режиме Automatic Mixed Precision из-за скалинга градиентов замедяется сильнее, чем если учить во float32.
Как я понял, если обучать модельку в режиме Automatic Mixed Precision, который сильно ускоряет и уменьшает расход памяти, то обычно используется скалинг градиентов. И после того, как сделан обратный проход градиенты нужно сначала unscale обратно, а потом уже использовать оптимизатор. Т.е. сначала по всем градиентам проходится torch.GradScaler, а потом оптимизаитор. Чтобы всё ускорить, оптимизаторы из торча умеют сами unscale градиенты и проверять их на inf . А те опимизаторы, которые не из торча, конечно ничего подобного не умеют и потом за них эту работу делает torch.GradScaler, который, как оказалось, написан не очень эффективно. 😞
P.S.
Попробовал с AdamW из торча. Там картина точно такая же, к сожалению. Что-то мне даже кажется, что скорость обучения в режиме Automatic Mixed Precision из-за скалинга градиентов замедяется сильнее, чем если учить во float32.
GitHub
pytorch/torch/amp/grad_scaler.py at main · pytorch/pytorch
Tensors and Dynamic neural networks in Python with strong GPU acceleration - pytorch/pytorch
gemma-2-27b-it_rus_vocab.txt
68.6 KB
Решил тут посмотреть, какие русскоязычные токены есть в довольно не плохой в русском языке на данный момент модельке google/gemma-2-27b-it .
Написал примерно такой код:
Если посмотреть то, что получилось, то можно примерно представить, на чём обучали модель и что BPE-токенизация в данном случае не очень эффективна и на практике большинство слов разбиваются на токены по 2-3 буквы. И при этом всё работает!
Интересно почитать, какой буст даёт BPE-токенизация по сравнению с посимвольной, например...
Написал примерно такой код:
from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig, Gemma2ForCausalLM
import re
huggingface_assecc_token = 'hf_xxxxxxxxxxxxxx'
model_id = "google/gemma-2-27b-it"
tokenizer = AutoTokenizer.from_pretrained(model_id, token=huggingface_assecc_token)
tokens = tokenizer.vocab.keys()
rus_tokens = list(filter(lambda t: bool(re.search('^[\n0123456789абвгдеёжзийклмнопрстуфхцчшщъыьэюяАБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ —–!",.:;?-]+$', t)), tokens))
rus_tokens.sort()
print(rus_tokens)
Если посмотреть то, что получилось, то можно примерно представить, на чём обучали модель и что BPE-токенизация в данном случае не очень эффективна и на практике большинство слов разбиваются на токены по 2-3 буквы. И при этом всё работает!
Интересно почитать, какой буст даёт BPE-токенизация по сравнению с посимвольной, например...
В выходные попытался вкатиться в NLP на своём датасете https://github.com/MichaelMonashev/TinyStories-ru/ . 😊
Сама первая проблема, с которой сталкиваешься: большие размерности входных данных: батч*длина последовательности*размер эмбединга. Пока сократил до: 64*1500*8 и с этим как-то можно уже работать.
Вторая проблема - очень зашумлённые входные данные. Нейронке сложно найти там хоть какой-то сигнал и она не может ничему научиться. Пока решил начинать учить всего на одном батче, а по достижении определённого значения лоса на валидации, добавлять в обучающую выборку данных. При этом сама валидационная выборка - это те данные, которые были недавно добавлены в обучающую выборку. Если брать за валидацию какие-то ранее не виденные нейронкой данные, то там вообще не видно никакого обобщения и просто муть какая идёт на выходе из нейронки.
Фактически сейчас я учу сеть запоминать обучающую выборку в надежде, что постепенно она научится обобщать. Пока сеть сопротивляется запоминанию через схлопывание эмбедингов примерно к одному вектору, но это я вроде победил и не даю ей этого делать.
Задача: предсказать продолжение текста, т.е. 80 эмбедингов 80-ти буковок. Токенизатор посимвольный, а не BPE.
Сетка самая примитивная:
Есть идеи, почему не обобщает? Я понимаю, что обычно предсказывают всего один следующий токен, а не сразу 80. Но хоть как-то оно ведь должно заводиться...
Сама первая проблема, с которой сталкиваешься: большие размерности входных данных: батч*длина последовательности*размер эмбединга. Пока сократил до: 64*1500*8 и с этим как-то можно уже работать.
Вторая проблема - очень зашумлённые входные данные. Нейронке сложно найти там хоть какой-то сигнал и она не может ничему научиться. Пока решил начинать учить всего на одном батче, а по достижении определённого значения лоса на валидации, добавлять в обучающую выборку данных. При этом сама валидационная выборка - это те данные, которые были недавно добавлены в обучающую выборку. Если брать за валидацию какие-то ранее не виденные нейронкой данные, то там вообще не видно никакого обобщения и просто муть какая идёт на выходе из нейронки.
Фактически сейчас я учу сеть запоминать обучающую выборку в надежде, что постепенно она научится обобщать. Пока сеть сопротивляется запоминанию через схлопывание эмбедингов примерно к одному вектору, но это я вроде победил и не даю ей этого делать.
Задача: предсказать продолжение текста, т.е. 80 эмбедингов 80-ти буковок. Токенизатор посимвольный, а не BPE.
Сетка самая примитивная:
class Net(nn.Module):
def __init__(self, vocab_size=83, embedding_size = 8, seq_len = 1500, num_of_predicted_tokens=80):
super().__init__()
self.embedding_size = embedding_size
self.embedding = nn.Embedding(vocab_size, embedding_size)
self.fc1 = nn.Linear(seq_len*embedding_size, 1024)
self.linears = nn.ModuleList([nn.Linear(1024, 1024) for i in range(10)])
self.fc2 = nn.Linear(1024, num_of_predicted_tokens*embedding_size)
def forward(self, x):
x = self.embedding(x)
x = torch.flatten(x, 1)
x = self.fc1(x)
x = F.leaky_relu(x, negative_slope=0.5)
for linear in self.linears:
x = x+ linear(x)
x = F.leaky_relu(x, negative_slope=0.5)
x = self.fc2(x)
return x
Есть идеи, почему не обобщает? Я понимаю, что обычно предсказывают всего один следующий токен, а не сразу 80. Но хоть как-то оно ведь должно заводиться...
GitHub
GitHub - MichaelMonashev/TinyStories-ru
Contribute to MichaelMonashev/TinyStories-ru development by creating an account on GitHub.
Начинаю понимать смысл трансформеров для больших моделей. На входе у них всего один токен - это вектор из 4 тысяч значений. И таких векторов тысячи, а то и сотни тысяч. И ещё размерность батча, где тоже что-то должно быть больше 1 . И выходит, что нормально работать можно только с отдельными токенами и перевзвешивать их относительно друг друга. Правда кроме трансформера, подробное модно делать и другими способами.
Grok от Маска стал бесплатным для юзеров экс-твиттера с небольшими ограничениями в использовании. Плюс Маск строит датацентры с миллионами GPU.
Если сложить вместе эти два события, то можно предположить, что сделана ставка на массовый доступный ИИ для каждого. И сейчас Маск растит клиентскую базу в условиях ещё не самой высокой конкуренции, заодно подрывая доходы самих конкурентов.
При этом сами конкуренты в очевидном ступоре и не знаю, что им делать дальше, кидаясь в разное, вместо фокусирования на чём-то одном самом важном. Например, в лучших традициях инфоциганства выпускают адвент-календари. Или берут инвестиции в виде GPU-часов...
Если сложить вместе эти два события, то можно предположить, что сделана ставка на массовый доступный ИИ для каждого. И сейчас Маск растит клиентскую базу в условиях ещё не самой высокой конкуренции, заодно подрывая доходы самих конкурентов.
При этом сами конкуренты в очевидном ступоре и не знаю, что им делать дальше, кидаясь в разное, вместо фокусирования на чём-то одном самом важном. Например, в лучших традициях инфоциганства выпускают адвент-календари. Или берут инвестиции в виде GPU-часов...
Родилась мысль, что путь мыслящего существа, вроде человека, весьма примитивен: он сначала жадно напитывается жизненным опытом, потом постепенно начинает всё сильнее и сильнее его обобщать, чтобы родить нечто новое, и потом попытаться сохранить его в какой-то форме: книге, мелодии, рисунке и т.п. И так и поколения в поколение, одно и то же, снова и снова.
Сделал слой перекодировщик: он ищет по входному вектору наиближайший к нему из списка имеющихся векторов и ставит в соответствие ему соответвующее значение эмединга:
С одной стороны мне вообще странно, что через такой слой проходят градиенты и сеть с этим слоем учится. С другой стороны он работает очень хреново, что бы я не делал...
class Lookup(nn.Module):
def __init__(self, num_embeddings, input_embedding_size, output_embedding_size):
super().__init__()
self.input_embedding = torch.nn.Embedding(num_embeddings, input_embedding_size)
self.output_embedding = torch.nn.Embedding(num_embeddings, output_embedding_size)
def forward(self, x):
distances = torch.cdist(x, self.input_embedding.weight)
pos = distances.argmin(-1)
return self.output_embedding(pos)
С одной стороны мне вообще странно, что через такой слой проходят градиенты и сеть с этим слоем учится. С другой стороны он работает очень хреново, что бы я не делал...
Должен признать, что проще узнать ответ у Gemini 2.0 Flash Thinking Experimental и потом верифицировать его, чем в специализированной телеграмм-группе спрашивать , ждать ответа и потом обнаружить, что группе нет никого, кто мог бы ответить на твой вопрос. 😞
Это весьма интересная тема, ведь человек не малую часть своего общения перекладывает на машину. Это сильно переформатирует общение между людьми. И возможно сейчас мы переживаем его расцвет и на пути к его закату.
Это весьма интересная тема, ведь человек не малую часть своего общения перекладывает на машину. Это сильно переформатирует общение между людьми. И возможно сейчас мы переживаем его расцвет и на пути к его закату.
👌2
Пришло ещё одно откровение про Трансформеры.
На входе у нас имеется довольно большой тензор (batch size, sequence length, embedding size). На практике он может быть таким (64, 8192, 4096), т.е. 2 147 483 648 значений. 2 млд. !!! Вся фишка трансформера в том, что он умудряется в каждом своём блоке учесть взаимосвязь каждого значения с каждым!
Когда GPU научатся быстро умножать матрицы размером в млд. значений, трансформер станет не нужен.
На входе у нас имеется довольно большой тензор (batch size, sequence length, embedding size). На практике он может быть таким (64, 8192, 4096), т.е. 2 147 483 648 значений. 2 млд. !!! Вся фишка трансформера в том, что он умудряется в каждом своём блоке учесть взаимосвязь каждого значения с каждым!
Когда GPU научатся быстро умножать матрицы размером в млд. значений, трансформер станет не нужен.
По Трансформерам у меня пока есть один непонятный вопрос: как его придумали. Т.е. какая была цепочка размышлений, почему он вышел именно таким, а не другим, какие ещё варианты получались, но были отметены и почему?
Чивиня (Multi-layer Parkinson)
Когда GPU научатся быстро умножать матрицы размером в млд. значений, трансформер станет не нужен.
На самом деле это возможно уже сейчас. Только такие огромные матрицы должны быть разряженными. Но даже если научиться вставлять веса в нужные места такой разряженной матрицы, то всё равно это не будет учётом ВСЕХ значений входной матрицы, что скорее всего приведёт к проигрышу относительно трансформеров.
Наконец-то получилось нормально предсказывать текст. Без трансформеров, на свёртках и полносвязанных слоях. Написал всё с нуля без использования сторонних либ.
К сожалению, заработало только тогда, когда стал предсказывать по одному токену. Думал, сразу много предсказывать, но по много не работает почему-то. 4-5 токенов ещё какие-то предсказываются, а дальше идёт паравозик из одного повторяющегося токена. Очень похоже на то, что все токены там равновероятны и сеть не может вычленить полезный сигнал при обучении почему-то.
Вот, что генерится через 15 минут обучения:
На входе сети всегда 80 токенов. На выходе тоже. Но берётся только первый. Никаких top_p, top_k и пенальти за повторения нет.
Токенизатор посимвольный.
Parameters: 230,048 Buffers: 0
Что-то похожее на слова выдаётся уже после 1000 шагов оптимизатора:
А вот, что после 12 часов обучения:
К сожалению, заработало только тогда, когда стал предсказывать по одному токену. Думал, сразу много предсказывать, но по много не работает почему-то. 4-5 токенов ещё какие-то предсказываются, а дальше идёт паравозик из одного повторяющегося токена. Очень похоже на то, что все токены там равновероятны и сеть не может вычленить полезный сигнал при обучении почему-то.
Вот, что генерится через 15 минут обучения:
Input : "Ой!"
Миша испугался и начал щипать маму за руки. Мама рассердилась. "Миша, хв
Predict: остик". Маша поняла, что смотрела на схватила посмотрела на схватила посмотрела
На входе сети всегда 80 токенов. На выходе тоже. Но берётся только первый. Никаких top_p, top_k и пенальти за повторения нет.
Токенизатор посимвольный.
Parameters: 230,048 Buffers: 0
Что-то похожее на слова выдаётся уже после 1000 шагов оптимизатора:
Input : "Ой!"
Миша испугался и начал щипать маму за руки. Мама рассердилась. "Миша, хв
Predict: оРехстила пока– Маша спа съала постел маща сала сла слся и ска 0остала и спа4ала
А вот, что после 12 часов обучения:
Input : "Ой!"
Миша испугался и начал щипать маму за руки. Мама рассердилась. "Миша, хв
Predict: ошла сова. . Маша поняла, что нужно быть осторожным. Он посмотрел на Машу и сказ
Думал, почему не выходит предсказывать сразу много токенов...
По-хорошему, должно быть что-то вроде мысли, которая потом обретает форму слов. Т.е. должна быть какая-то единая сущность в начале, из которой всё вырастает. И видимо ничего подобного не образуется, если всю последовательность токенов взять и засунуть на вход нейросети.
Тут прямо просится архитектура энкодер-декодер. А может даже энкодер-"что-то работающее с сжатым пространством"-декодер...
Или диффузионная модель, которая обуславливается входными токенами...
По-хорошему, должно быть что-то вроде мысли, которая потом обретает форму слов. Т.е. должна быть какая-то единая сущность в начале, из которой всё вырастает. И видимо ничего подобного не образуется, если всю последовательность токенов взять и засунуть на вход нейросети.
Тут прямо просится архитектура энкодер-декодер. А может даже энкодер-"что-то работающее с сжатым пространством"-декодер...
Или диффузионная модель, которая обуславливается входными токенами...
Интересно заметить, как развитие, популяризация и упрощение отдельно взятых технологий меняет медийный Олимп. Причём меняются как люди на Олимпе, так и обсуждаемые мемы. Причём отдельные "небожители" длят себя во времени и удерживаются, лишь развивая свой медийный успех.
Например, можно посмотреть, какие интервью были https://www.youtube.com/@lexfridman/videos в прошлом: 5, 10 лет назад. Узнать, куда делись все те люди, что сошли с дистанции...
Например, можно посмотреть, какие интервью были https://www.youtube.com/@lexfridman/videos в прошлом: 5, 10 лет назад. Узнать, куда делись все те люди, что сошли с дистанции...
В начале грешил на то, что torch.transpose() медленно работает, но потом попробовал так, сяк, попрофайлил и оказалось, что LayerNorm() жутко тормозная вещь. Его замена на RMSNorm() сразу даёт сильное ускорение. А если выкинуть нормализацию вообще, то вдвое быстрее становится. Но учится перестаёт: из сети nan-ы лезут. Пока не разбирался, почему...
Переписал всё на совершенно другую архитектуру. Сразу на порядок упало количество весов в сети для тех же исходных параметров:
Попробовал обучить такую сетку. Loss падает сильно медленнее. Слова начали предсказываться уже через 3 минуты. Через 15 минут сеть выдавала такое:
Думаю, что чуда не случится и примерно такая же тарабанрщина останется, если продолжить обучение. Хотя фиг знает, может будут более связанные, но менее разнообразные тексты... Или nan-ы мои любимые полезут. 😊
P.S.
Вот, что получилось. В целом стало меньше слов с ошибками и немного выросла связанность текста:
Parameters: 28,660 Buffers: 0 .Попробовал обучить такую сетку. Loss падает сильно медленнее. Слова начали предсказываться уже через 3 минуты. Через 15 минут сеть выдавала такое:
Input : который шёл ночью, кончился. Он стоял на берегу моря. Небо сияло всеми цветами
Predict: и поУЩел большой маленький Шаранила Пенок Пенький Шаранила и грусто. "Се и Холок его был очень сила так Пенок Пыбка показала на была очень грусттела на была очень грусттела на была очень на была очень был очень сине грусттел и Хонела помой. Она грусттиШа поы был очень Холок и грусттела так не было они был очень сине было грусттел и Хонела поУйа. Она была очень грусттела на была очень грусттела на
Думаю, что чуда не случится и примерно такая же тарабанрщина останется, если продолжить обучение. Хотя фиг знает, может будут более связанные, но менее разнообразные тексты... Или nan-ы мои любимые полезут. 😊
P.S.
Вот, что получилось. В целом стало меньше слов с ошибками и немного выросла связанность текста:
Input : который шёл ночью, кончился. Он стоял на берегу моря. Небо сияло всеми цветами
Predict: и расстроилась. "Маша, но не было Миша. "Маша, но он был очень сильный аккуратно подумала так был очень сильный и поЖерапаный И не было большой и сильный аккуратно шарик подумала так был очень сильный аккуратно подумала так был очень сильный и поЖерапаный И не было большой и сильный аккуратно шарик подумала так был очень сильный аккуратно подумала так был очень сильный и поЖерапаный И не было боль
Подумал, что важное отличие LLM-ки от других нейронок в том, что она больше похожа на законченный продукт, который бери и используй, не имея каких-то специальных навыков, вроде программирования.
Похоже наконец-то удалось разгадать хитрый план яндекса по созданию AGI. Они специально подсовывают обиженных на несправедливый мир таксистов, чтобы мотивировать изобрести AGI, который сможет водить автомобиль.
План, надо сказать, хреновый, ибо сильно перетягивает на себя внимание, путает мысли и вынуждает тратить время на поиск альтернатив. Благо они ещё остались.
План, надо сказать, хреновый, ибо сильно перетягивает на себя внимание, путает мысли и вынуждает тратить время на поиск альтернатив. Благо они ещё остались.
Был уверен, что размер эмбединга токена зависит исключительно от его смысловой нагрузки. Поэтому взяв посимвольный токенизатора думал обойтись эмбедингами размером в паду десятков float-ов. Но оказалось, что чем больше эмбединг, тем сеть учится сильно быстрее. И чем больше длина входной последовательности. тем больше должен быть эмбединг. Хотя, возможно это особенность моей сетки просто.