Математика глубокого обучения

Broadcasting

Правило выравнивания форм — и почему оно молча создаёт тензоры не того размера

Шаг 75 из 117 · ~24 мин

Правило целиком

Broadcasting — это одно правило, и его стоит выучить точно, потому что все ошибки с ним происходят от приблизительного знания.

оси сопоставляются с конца;совместимы, если равны или одна из них=1\htmlData{k=align}{\text{оси сопоставляются с конца}}; \quad \text{совместимы, если равны или одна из них} = 1

Пошагово:

  1. более короткую форму дополнить единицами слева;
  2. пройти по осям и проверить: равны или одна равна 11;
  3. результат по каждой оси — максимум из двух.

Ключевое слово — . Именно это ломает интуицию:

форма Aформа Bрезультат
(3,4)(3, 4)(4)(4)(3,4)(3, 4) — работает
(3,4)(3, 4)(3)(3)ошибка

Второй случай выглядит естественнее первого — «прибавить по одному числу к каждой строке» — и именно он не работает. Вектор (3)(3) выравнивается по последней оси, то есть сопоставляется с 44, и не подходит. Чтобы получить желаемое, нужно сказать это явно: (3,1)(3, 1).

Оси-единицы — это разрешение

Ось длины 11 — не пустая формальность, а способ сказать «здесь можно растянуть». Отсюда классический приём для внешнего произведения:

(3,1) и (1,4)  (3,4)(3, 1) \ \text{и} \ (1, 4) \ \longrightarrow \ (3, 4)

Обе оси растягиваются, каждая в своём направлении. Именно так пишут попарные расстояния, матрицы масок и позиционные разности — без единого цикла.

Полезно понимать, что растягивание не копирует данные: реализация ставит страйд 00 по растянутой оси, и один и тот же элемент читается многократно. Поэтому broadcasting дешёв по памяти на входе — но результат-то материализуется полностью, и вот здесь и подстерегает главная опасность.

Как это молча создаёт гигантские тензоры

Самая дорогая ошибка с broadcasting — не падение, а успех.

(8,1,512) и (100,512)  (8,100,512)(8, 1, 512) \ \text{и} \ (100, 512) \ \longrightarrow \ (8, 100, 512)

Это 409600409\,600 элементов вместо 40964096 и 5120051\,200 на входе. Формы совместимы, ошибки нет, и если вы ожидали поэлементной операции — получили попарную. При батче 256256 и длине последовательности 20482048 такая опечатка мгновенно даёт out-of-memory, и трассировка указывает на строку, где память кончилась, а не на ту, где потерялась ось.

Признак, по которому это узнаётся: тензор вырос, а вы этого не просили. Поэтому в рабочем коде полезно проверять формы утверждениями, а не глазами:

assert scores.shape == (batch, heads, seq, seq), scores.shape

Что делать вместо

Broadcasting неявен, и это его единственный настоящий недостаток. Явные альтернативы:

вместолучше
x[:, None] * y[None, :]einsum('i,j->ij', x, y)
x.unsqueeze(1)rearrange(x, 'b d -> b 1 d')
надежды на выравниваниеassert на форму

Первая строка — тема следующего урока: einsum делает то же самое, но каждая ось названа, поэтому пропущенную ось видно в спецификации, а не в счётчике памяти.

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

i, j ij

индексразмерроль
i
3
остаётся в выходе
j
4
остаётся в выходе
форма результата
[3, 4]
элементов
12
умножений-сложений
12
операций на элемент
1
Внешнее произведение — то же, что broadcasting с осями-единицами, только оси названы. Суммируемых индексов нет, поэтому операций ровно столько, сколько элементов на выходе: отношение равно 1. Это чистое перемножение без свёртки.

Сравните две последние спецификации: они различаются одной буквой в выходе, а результат — в 512512 раз по объёму. В einsum это видно; в записи через None-индексы — нет.

Что стоит унести

  • выравнивание идёт справа, и (3,4)(3,4) с (3)(3) не совместимы, хотя выглядят так;
  • ось длины 11 — это разрешение растянуть, и её отсутствие — тоже утверждение;
  • растягивание не копирует вход, но результат материализуется целиком;
  • самая дорогая ошибка не падает: формы сходятся, а тензор оказывается больше нужного в сотни раз.

Источники

Проверки

0 из 2
  1. Правило broadcasting

    Отметьте все верные утверждения о broadcasting.

  2. Вывести форму результата

    Реализуйте broadcast_shape(a, b) — примените правило broadcasting к двум формам.

    Верните [1.0, ...result], где result — форма результата, если формы совместимы, и [0.0], если нет. То есть первый элемент — флаг успеха, а остальные — сама форма.

    Правило:

    1. более короткую форму мысленно дополнить единицами слева;
    2. по каждой оси: совместимы, если размеры равны или один из них равен 11;
    3. размер результата по оси — максимум из двух.

    Пустой список означает скаляр и совместим с чем угодно.

    Главное — направление выравнивания. Формы [3,4] и [4] совместимы, а [3,4] и [3] — нет, потому что оси сопоставляются с конца.

    функция broadcast_shape

    Загрузка редактора…

    Ctrl/⌘ + Enter