Тензоры. Базовые математические операции

Смотреть материал на YouTube | RuTube

На этом занятии рассмотрим основные математические операции, которые можно выполнять с тензорами фреймворка PyTorch. Они следующие:

Операция

Описание

+

сложение тензоров или тензора с числом

-

вычитание тензоров или тензора с числом, либо используется как унарный минус

*

умножение тензоров или тензора с числом

/

деление тензоров или тензора с числом

//

целочисленное деление тензоров или тензора с числом

**

возведение в степень (указывается или число или тензор)

%

вычисление остатка от деления (указывается или число или тензор)

Давайте посмотрим на примеры использования этих операций. Пусть у нас задан следующий одномерный тензор:

a = torch.FloatTensor([1, 2, 3])

Все указанные в таблице операции выполняются следующим образом:

-a # унарный минус
a + 2 # сложение с числом
2 + a  # так тоже можно записывать
a - 3 # вычитание с числом
a * 5 # умножение на число
a / 5  # деление на число
a // 2  # целочисленное деление
a ** 3  # возведение в степень 3
a % 2  # вычисление по модулю 2

Разумеется, приоритеты этих операций такие же, как и в языке Python. А на выходе формируется новый тензор с соответствующими значениями.

Давайте теперь добавим еще один тензор:

b = torch.IntTensor([3, 4, 5])

и посмотрим на эти же операции, но с участием двух тензоров:

a - b  # tensor([-2, -2, -2])
b + a  # tensor([4, 6, 8])
a * b  # tensor([ 3,  8, 15])
b / a  # tensor([3. , 2. , 1.66666667])
b // a # tensor([3, 2, 1])
b ** a # tensor([  3,  16, 125])
b % a  # tensor([0, 0, 2])

Везде мы видим поэлементные операции. Соответственно, чтобы они выполнялись, тензоры должны быть согласованы по длине. Например, если взять тензор:

b = torch.IntTensor([3, 4, 5, 6])

и выполнить операцию:

a + b  # ошибка: длины массивов не совпадают

то возникнет ошибка из-за несовпадения длин массивов. Но вот такая операция с двумерным тензором b сработает:

b = torch.arange(1, 7).view(2, 3)
a + b

В этом случае массив a будет применен к каждой строке массива b и на выходе увидим результат:

Такое поведение получило название транслирование тензоров.

Операции с многомерными тензорами

Все рассмотренные операции можно распространить и на многомерные тензоры, главное, чтобы они были согласованы по размерам. Я приведу два небольших примера, так как думаю, общий принцип здесь понятен. Сформируем трехмерный и двумерный тензор следующим образом:

a = torch.arange(1, 19).view(3, 3, 2)
b = torch.ones(3, 2)

С ними можно выполнять такие очевидные операции:

a - b
a * 10
a // b

И так далее. Причем, двумерный тензор b может быть применен к трехмерному тензору a благодаря операции транслирования, так как их размеры согласованы. На самом деле, умножение трехмерного тензора на число также реализуется через транслирование числа по всем элементам тензора a.

Все представленные математические операции имеют следующие расширенные аналоги:

Операция

Расшифровка

a += b

a = a + b

a -= b

a = a - b

a *= b

a = a * b

a /= b

a = a / b

a //= b

a = a // b

a **= b

a = a ** b

a %= b

a = a % b

То есть, если нам нужно произвести какие-либо математические операции с тензором и изменения сохранить в нем же, то удобно использовать такие сокращенные записи. Выполняются они очевидным образом, например, так:

a = torch.IntTensor([1, 2, 6, 8])
a += 5
b = torch.ones(4)
b *= a

И так далее. Но есть один нюанс работы этих операторов. Выполнение команды:

a += b

приведет к ошибке. С чем это связано? Дело в том, что результатом сложения вещественного числа с целочисленным, итоговое значение представляет собой вещественное число. Но тип данных массива a – целочисленный и он не может сохранять вещественные числа. Отсюда и возникает эта ошибка. Вообще, следует помнить правило:

При выполнении арифметических операций тип данных автоматически приводится к более общему.

Все описанные математические операции можно комбинировать и записывать в виде:

(a + b)*5 - 10

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

Методы математических операций

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

Метод

Расшифровка

res = x.add(y)

res = torch.add(x, y)

x.add_(y)

res = x + y

res = x + y

x += y

res = x.sub(y)

res = torch.sub(x, y)

x.sub_(y)

res = x - y

res = x - y

x -= y

res = x.mul(y)

res = torch.mul(x, y)

x.mul_(y)

res = x * y

res = x * y

x *= y

res = x.div(y)

res = torch.div(x, y)

x.div_(y)

res = x / y

res = x / y

x /= y

res = x.pow(y)

res = torch.pow(x, y)

x.pow_(y)

res = x ** y

res = x ** y

x **= y

Например:

a = torch.arange(5, dtype=torch.float32)
b = torch.ones(5)
r = a.add(b)

Тензоры a и b остаются неизменными. Но если записать:

r = a.add_(b)

то и тензор a изменится и вернет ссылку на самого себя. То есть тензор r будет ссылаться на тензор a и команда:

r[0] = 10

приведет к изменению и тензора a.

Вообще все (или почти все) inplace методы возвращают ссылку на тот же самый тензор, к которому были применены. В связи с этим нужно с осторожностью относиться к записям вида:

r = a.mul_(5)

Хотя, если записать составное выражение:

r = a.sub_(b) - 2

то тензоры r и a будут уже, конечно же, независимыми между собой.

Видео по теме