Научный форум dxdy

Математика, Физика, Computer Science, Machine Learning, LaTeX, Механика и Техника, Химия,
Биология и Медицина, Экономика и Финансовая Математика, Гуманитарные науки




На страницу 1, 2  След.
 You also need GatedMaxPool
Ниже приведён код каузального варианта GatedMaxPool. Он может применяться для замены трансформерных блоков в различных GPT-нейросетях.

Код:
class CausalGatedMaxPool(nn.Module):
    def __init__(
        self,
        d_model: int,
        window_size: int = 2,
    ):
        super().__init__()

        if d_model < 1:
            raise ValueError("d_model must be >= 1")
        if window_size < 2:
            raise ValueError("window_size must be >= 2")

        self.d_model = int(d_model)
        self.window_size = int(window_size)

        # Один обучаемый коэффициент на каждую feature-компоненту.
        # Инициализация около ±1 сохраняет и max-, и min-пути с самого начала.
        self.gate = nn.Parameter(torch.empty(d_model))
        nn.init.normal_(self.gate, mean=0.0, std=1.0)

        self.proj = nn.Linear(d_model, d_model)
        self.act = nn.GELU()

    def forward(self, x: torch.Tensor) -> torch.Tensor:
        """
        x:       (..., T, d_model)
        returns: (..., T, d_model)
        """
        if x.ndim < 2:
            raise ValueError("Expected x with shape (..., T, d_model)")
        if x.size(-1) != self.d_model:
            raise ValueError(
                f"Expected last dim {self.d_model}, got {x.size(-1)}"
            )

        # (..., T, D): feature-wise статический гейт.
        # Гейт применяем ПЕРЕД padding: отрицательный gate безопасен.
        x = x * self.gate

        # Реальная ширина сегмента до отбора прореженных позиций.
        left_pad = (self.window_size - 1) * self.dilation

        # Padding не должен стать максимумом ни при каких данных.
        x = F.pad(
            x,
            pad=(0, 0, left_pad, 0),
            mode="constant",
            value=float("-inf"),
        )

        # (..., T, D, left_pad + 1)
        segments = x.unfold(
            dimension=-2,
            size=left_pad + 1,
            step=1,
        )

        # (..., T, D)
        pooled = segments.amax(dim=-1)

        return self.act(self.proj(pooled))


Типовое применение:

Код:
self.cgmp1 = CausalGatedMaxPool(d_model=D_MODEL, window_size=2)
self.cgmp2 = CausalGatedMaxPool(d_model=D_MODEL, window_size=2)
self.cgmp3 = CausalGatedMaxPool(d_model=D_MODEL, window_size=2)
self.cgmp4 = CausalGatedMaxPool(d_model=D_MODEL, window_size=2)
self.cgmp5 = CausalGatedMaxPool(d_model=D_MODEL, window_size=2)
self.cgmp6 = CausalGatedMaxPool(d_model=D_MODEL, window_size=2)
self.cgmp7 = CausalGatedMaxPool(d_model=D_MODEL, window_size=16)
self.cgmp8 = CausalGatedMaxPool(d_model=D_MODEL, window_size=16)
...
def forward(self, idx, targets=None):
        #positions = torch.arange(time_steps, device=idx.device)
        x = self.token_embedding(idx) # + self.position_embedding(positions)
        #x = self.blocks(x)

        x = x + self.cgmp1(x)
        x = x + self.cgmp2(x)
        x = x + self.cgmp3(x)
        x = x + self.cgmp4(x)
        x = x + self.cgmp5(x)
        x = x + self.cgmp6(x)
        x = x + self.cgmp7(x)
        x = x + self.cgmp8(x)

...



По сути, почти обычный макспулинг в комбинации с линейными слоями и активациями обучаются как языковые модели без использования трансформеров. Просто случайное открытие.

Представленный слой сумел выучить шекспировский датасет и воспроизвести в его стиле следующее:

Цитата:
ROMEO:
I will affairs of the time modesty.

Clown:
The unhappy masters well, will not fear this true.

ROMEO:
And, and your late, nor with pardon are saw the
restite with the pardon fourth the wife,
Thou wilt to sits me from such every to the self;
And what is your words?

KING RICHARD III:
No; and down to thy beats.

ESCALUS:
And with me to make some to unship
bright it down to be prepared that should I saw mad;
The offence is famous subject my father,
Is so right for marriage of all be sitcuse noise.

Second Servingman:
I think you'll for your brother is
Stand with then do access that we Servingman:
This is merry be gone.

GLOUCESTER:
Had, thou art to take up, and yet lead me but made
And your skill hand shame hither speech
That I come in the my soul steel we and mine either
Your of lacks the first myself above takers upon.

GLOUCESTER:
An or the way, not shall be no bed
The sing to against thou seem, 'tis ere place,
Or prisoner and her tongue a soldier.

Somerset, which I lead not with sir, with me in any curse the truly to the tent.

GLOUCESTER:
Well so he might bed myselves one most thy first George!

CATES:
And I'll that see, and which man's rest
I can his policious both, to do such kiss me about.

Shepherish and straign living surping.

KING HENRY VI:
Have helm, knife: but even is a beward?

POLIXENES:
He wast threshes, beholds where is such of precious have let our brain,
Who say the true well a favouring from the gods?

DUKE VINCENTIO:
All to hence, most They with a pardon,
That made of me by with his modestrious daughter, came and her pass'd with the orators i' the matter than thou have being with night, here come rest.
The soul and death it with virgininuries! and my father
being to prove this plarus wouldst be,
Which this full of your friends of the course.

LADY ANNE:

Prove it, sir:
Every true his supposed with and seems.

JULIET:
O, sir, proffer's perjury, so me your princes,
So man should pride the good talk of the was a shame.

Provost:
Why I dare glass ca

 Re: You also need GatedMaxPool
Аватара пользователя
Очень интересно, но не понятно (я Питона не знаю). Правильно ли я понял, что делается вот это:
$$
x^{t, \ell + 1}_{\mu} = x^{t, \ell}_{\mu} +
{\sf GELU}
\left[
\sum_{\nu = 0}^{D - 1} M^{\ell}_{\mu \nu}
{\sf MAX}\left[
\left(x^{t - 0, \ell}_{\nu} \cdot g^{0, \ell}_{\nu} \right),
\,
\left(x^{t - 1, \ell}_{\nu} \cdot g^{1, \ell}_{\nu} \right),
\,
\ldots,
\left(x^{t - (W-1), \ell}_{\nu} \cdot g^{W-1, \ell}_{\nu} \right)
\right]
\right]
$$
Может MAX заменить на Softmax? Лучше дифференцироваться будет...

 Re: You also need GatedMaxPool
В основе лежит maxpooling - это максимум в скользящем окне размером window_size=2, шагом stride 1, ещё есть параметр dilation, который обычно равен 1.

Входная последовательность макспулинга $x_1, x_2, x_3, x_4, ...$, тогда выходная последовательность $\max(-\infty, x_1), \max(x_1, x_2), \max(x_2, x_3), \max(x_3, x_4), ...$. Это для окна размером 2 и шагом 1.

-- добавлено через 4 минуты --

$g^{i, \ell}_{\nu}$ - это gating, обучаемый параметр. Если он положителен, то берутся максимумы, если отрицательный, то по сути берутся минимумы (надо только аккуратнее с левым паддингом $-\infty$). Просто с этим гейтингом результаты чуть лучше, можно его заменить на константу $1$.

 Re: You also need GatedMaxPool
А то, что цитируется - это что?
Бред на языке Шекспира?
Кто такое Cates?

 Re: You also need GatedMaxPool
Аватара пользователя
Mihaylo, сколько времени заняло обучение?

Booker48, могло быть сильно хуже.

 Re: You also need GatedMaxPool
Буквально пару минут на видеокарте. Это очень маленький датасет.

В этой генерации можно указать температуру от 0.0 до 1.0. у меня выставлена 0,8 кажется, то есть сочиняет от души, ещё topk=40, среди скольки токенов идёт отбор вроде как.

-- добавлено через 15 минут --

Мне кажется весь секрет в том, что maxpooling с окном шириной 2 "перекатывает" токены либо вверх, либо вниз на один шаг по последовательности. Более широкие окна работают хуже, особенно в начальных слоях.

-- добавлено через 6 минут --

Mihaylo в сообщении #1729404 писал(а):
В этой генерации можно указать температуру от 0.0 до 1.0. у меня выставлена 0,8 кажется, то есть сочиняет от души

Температура 0.0 соответствует более точному воспроизведению выученных текстов, температура 1.0 - это постоянная попытка менять тему типа "начал за здравие, кончил за упокой" даже в пределах одного слова, поэтому может даже придумывать новые очень правдоподобные имена типа Gabrielliet (Gabriella + Juliet)

-- добавлено через 20 минут --

Если перцептивное поле у трансформера может иметь произвольный размер, в том числе в ширину контекста, то у GatedMaxPooling перцептивное поле имеет размер $N (W - 1) + 1$, где $N$ - количество слоёв GatedMaxPool, $W$ - ширина окон в каждом слое. То есть вся модель при $W = 2$ видит только $N+1$ соседних токенов. Можно умножить на условный коэффициент 2-3 и понимаем, что соседние $(2..3)(N + 1)$ символов находятся в перцептивном поле, чего достаточно для "балякания".
Ничто не мешает в конце после нескольких легковесных слоёв GatedMaxPool добавить несколько трансформерных блоков для схватывания уже дальних зависимостей. Вместо 6-12 тяжёлых слоёв трансформеров.

10 слоёв GatedMaxPool работают в 2-4 раза быстрее, чем 6 слоёв трансформеров. И это ещё неизвестно, что происходит, когда модель гигантская.

 Re: You also need GatedMaxPool
Короче, я скрестил GatedMaxPool и Transformer - 8 слоёв + 2 слоя. Результат, который 6 слоёв Trasformer вымучивают за 6-7 минут, достигается менее чем за 1,5 минуты комбинированного обучения. Это пока результат на простых датасетах и задачах.

 Re: You also need GatedMaxPool
Пытаюсь постичь, как GatedMaxPool работает...
Для начала нужно понять, какую задачу выполняет трансформер в GPT-модели. Представьте, у GPT есть большой обучаемый эмбеддинг размером VxD, где V (vocab_size) - это размер словаря = количество уникальных токенов, которые изучает модель, D (d_model) - это размерность памяти модели о каждом токене. По сути, эмбеддинг содержит ассоциативные языковые знания, которые изучила модель.
На вход GPT подаётся набор токенов $x_1, x_2, x_3, ...x_T$, на выходе ожидается смещённый в будущее набор токенов $x_2, x_3, x_4, ..., x_{T+1}$. Из таблицы VxD извлекаются векторы, соответствующие токену в последовательности. Итого на вход первого GatedMaxPool или трансформера подаётся таблица размером TxD, где T (Time) - длина последовательности.
Модель должна быть каузальной, то есть предсказывать токен $x_i$, обрабатывая только токены от $x_1$ до $x_{i-1}$, не подглядывая в "будущее". В случае с трансформерами это обеспечивается каузальными масками [1, 1, 1, ..., 0, 0, 0], обнуляющими будущие токены.
В случае GatedMaxPool каузальные маски не требуются, так как слой сам по себе каузальный: макспулинг смотрит только в окно перед прогнозируемым токеном. Например, для предсказания токена под номером $i$ берется максимум элементов последовательности в скрытом слое $i, i-1$, те в свою очередь являются максимумами последовательности в предыдущем слое под номерами $i, i-1$ и $i-1, i-2$ и так далее.
Если опустить линейные и активационные слои в GatedMaxPool, то по сути следующие друг за другом макспулинги выбирают максимумы из входной таблицы TxD.
Линейно-активационные слои, по сути, управляют процессом отбора наилучших данных, они задают, где и какие данные сравниваются. Макспулинг с окном шириной 2, возможно, является оптимальным по той причине, что таким сравнением (отбором) легче всего управлять. В случае отбора одного из двух элементов лишь одна из веток вычислений отбрасывается при обратном распространении ошибки. Могу предположить, что большие окна могут оказаться более эффективными при увеличение объема ассоциативной памяти (d_model).

 Re: You also need GatedMaxPool
Немного о drop-слоях (терминология моя).

Drop-слой нейронной сети - это такой слой, который из входного тензора не пропускает один или несколько элементов в выходной слой, прерывая вычислительный граф на этом элементе ("дропает" элемент тензора). При обратном распространении ошибки такие элементы не получают градиент ошибки и всё вычисление от входа до дропнутого элемента не обучается. Это обучение не происходит только в текущей итерации, в следующей итерации drop-слой может дропнуть другие элементы, всё зависит от входных данных и результатов промежуточных вычислений.

Самый известный и распространённый drop-слой - это dropout - он случайным образом дропает целиком некоторые нейроны, заменяя их выходы нулями. Менее известный конкурент - drop-connect - случайно обнуляет некоторые связи между нейронами.
maxout (max, maxpool) - дропают все элементы в скользящем окне или глобально, кроме одного - максимального.

Я тут выделяю особенно характеристику drop-уровень - доля дропнутых связей среди всех потенциальных связей на входе drop-слоя. (Когда дропается целиком элемент, то это значит дропаются все его связи.)

У dropout этот уровень обозначается буквой $p$ и привычно задаётся в диапазоне 0,05-0,30.
У maxout (max) дропается доля $\frac{n-1}{n}$.
У maxpooling доля дропа равна $p = 1 - \frac{1}{w}$, где $w$ - ширина окна. Минимальное значение drop-уровня составляет 0,5 при $w = 2$. Мне кажется это является определяющим, при $w = 3$ drop-уровень 0,66 уже неприлично высокий.
Именно это обстоятельство заставило меня искать решение в виде замены max на top-k - это понижает drop-уровень. Правда скорость вычислений очень неприлично падает...

 Re: You also need GatedMaxPool
Mihaylo в сообщении #1729404 писал(а):
То есть вся модель при $W = 2$ видит только $N+1$ соседних токенов. Можно умножить на условный коэффициент 2-3 и понимаем, что соседние $(2..3)(N + 1)$ символов находятся в перцептивном поле,

Ниже схема перцептивного поля для лучшего понимания:

Изображение

 Re: You also need GatedMaxPool
Аватара пользователя
Я не специалист, но мне кажется, что, наверное, ключевым всё же является не gated maxpooling сам по себе, а несколько слоёв "простой CNN" с окном 2 и страйдом 1 в начале.

То есть если перед вызовом полноценного трансформерного внимания сначала сделать несколько слоёв какой угодно "простой CNN" (не обязательно основанной на gated maxpooling) с окном 2 и страйдом 1, то уже будет польза, так как трансформерное внимание будет применено к более крупным семантическим единицам чем просто токены. Квадратичная сложность внимания будет ослаблена тем, что семантических едниц к которым оно применяется стало меньше.

 Re: You also need GatedMaxPool
Аватара пользователя
А не пробовали постепенно увеличивать окно? Сначала 2, поверх него 3, поверх него 4...? При наличии "простой CNN" на нижнем уровне можно эмбеддинговые векторы сопоставить буквам, а не словам. Несколько слоёв "простой CNN" с окнами 2, 3, 4, 5 "быстро" соберёт буквы в слоги, слоги в слова, слова во фразы. А далеее уже к фразам применять трансформерное внимание.

 Re: You also need GatedMaxPool
SergeyGubanov в сообщении #1729762 писал(а):
не gated maxpooling сам по себе, а несколько слоёв "простой CNN" с окном 2 и страйдом 1 в начале.

Это можно и нужно попробовать...

SergeyGubanov в сообщении #1729770 писал(а):
А не пробовали постепенно увеличивать окно? Сначала 2, поверх него 3, поверх него 4...?

Я пробовал. Но двойка остаётся самой оптимальной на первых слоях довольно далеко вглубь.

Я это обосновываю drop-коэффициентом макспулинга, он самый низкий у window_size=2 и сам по себе высоковат для любой модели.

В моей простейшей gpt-модели в последних двух слоях выставил window_size=16 и ничего не сломал и даже улучшил лосс на какие-то там 0,001. Предполагаю, это исключительно благодаря резидуальным связям:

$h^{k+1}_i = h^k_i + GatedMaxPool(h^k_i, w=16)$,
где $GatedMaxPool(h^k_i, w=16) \approx 0$

То есть последние слои вносят незначительные коррекции.

 Re: You also need GatedMaxPool
Я бы не торопился с выводами, мне кажется предложенный алгоритм может не сработать также эффективно в более сложных задачах. Вряд ли такое не пробовали до меня.

Этой проверкой я должен заняться, чуть позже отпишусь про свёрточные слои вместо трансформерных.

 Re: You also need GatedMaxPool
Аватара пользователя
Мне подсказали почему может быть плохой идеей сделать прослойку из нескольких "простых CNN" между векторами эмбеддингов и полноценным трансформерным блоком внимания. Такая LLM скорее всего будет испытывать трудности со следующим промптом: "Запомни и повтори следующую случайную последовательность hjgKEsdvc97612E34eighFv28Mqwe7d65vi123e". Скорее всего прослойка из нескольких "простых CNN" помешает запомнить и повторить случайную последовательность символов.

 [ Сообщений: 18 ]  На страницу 1, 2  След.


Соглашение о конфиденциальности | Общие правила

Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group