Пришло время
познакомиться с одной из фундаментальных возможностей пакета PyTorch
–
векторно-матричными вычислениями. На одном из прошлых занятий мы с вами уже
видели, как можно поэлементно умножать один тензор на другой:
a = torch.arange(1, 10).view(3, 3)
b = torch.arange(10, 19).view(3, 3)
r1 = a * b
r2 = torch.mul(a, b)
Получим результат:
tensor([[ 10, 22, 36],
[ 52, 70, 90],
[112, 136, 162]])
Матричное умножение
Но если нам нужно
выполнить именно матричное умножение, то есть, строки одной матрицы умножать на
столбцы другой и результаты складывать:
то для этого следует
использовать специальные функции и операторы. Начнем с функций. Итак, чтобы
перемножить две матрицы a
и b по всем правилам математики, часто
используют функцию:
c = torch.matmul(a, b) # перемножение с эффектом транслирования
либо:
c = torch.mm(a, b) # перемножение без эффекта транслирования
Обе эти функции
возвращают двумерный тензор с результатом матричного умножения:
tensor([[
84, 90, 96],
[201, 216, 231],
[318, 342,
366]])
Однако функция torch.mm
выполняет перемножение без возможности транслирования, а функция torch.matmul
позволяет транслировать тензор при необходимости согласования размерностей.
Давайте я поясню это на конкретном примере. Объявим дополнительно еще один
вектор:
v = torch.LongTensor([-1, -2, -3])
Здесь используется тип long,
чтобы типы данных перемножаемых тензоров a
и v совпадали.
Тогда будет вполне допустимы команды:
c = torch.matmul(a, v) # матрица на вектор
c = torch.matmul(v, a) # вектор на матрицу
Но недопустима команда:
c = torch.mm(a, v) # ошибка
так как здесь необходимы
матрицы (тензоры) согласованных размеров. Какую именно функцию использовать на
практике, зависит от конкретной решаемой задачи.
Или же вместо этих
функций можно использовать аналогичные методы:
c = a.mm(b)
c = a.matmul(v)
В PyTorch
дополнительно
к обычному матричному умножению добавляется еще одна команда:
torch.bmm
которая перемножает
матрицы по, так называемым, батчам (пакетам). Это часто используется в
нейронных сетях. Принцип работы этой функции (и аналогичного метода) проще показать
на конкретном примере:
bx = torch.randn(7, 3, 5)
by = torch.randn(7, 5, 4)
bc = torch.bmm(bx, by)
bc.size() # тензор 7x3x4
Как видите, первая ось
определяет количество матриц в батче (пакете), которые нужно перемножить между
собой. То есть, перемножаются независимо семь матриц между собой и на выходе
формируется тоже семь матриц, но с размерностями 3x4.
Векторное умножение
Аналогичные операции
можно выполнять и с векторами. Математически, если у нас имеются два вектора:
то их умножение можно
реализовать двумя способами:
и
Первое умножение
называется скалярным и реализуется либо через функцию torch.dot (или
аналогичный метод):
a = torch.arange(1, 10, dtype=torch.float32)
b = torch.ones(9)
c = torch.dot(a, b) # tensor(45.)
Либо с помощью
оператора @:
Другой способ умножения
(внешнее умножение векторов) реализуется с помощью функции (или аналогичного
метода):
получим
результат в виде следующей матрицы:
tensor([[1.,
1., 1., 1., 1., 1., 1., 1., 1.],
[2., 2., 2., 2., 2., 2., 2., 2., 2.],
[3., 3., 3., 3., 3., 3., 3., 3., 3.],
[4., 4., 4., 4., 4., 4., 4., 4., 4.],
[5., 5., 5., 5., 5., 5., 5., 5., 5.],
[6., 6., 6., 6., 6., 6., 6., 6., 6.],
[7., 7., 7., 7., 7., 7., 7., 7., 7.],
[8., 8., 8., 8., 8., 8., 8., 8., 8.],
[9., 9.,
9., 9., 9., 9., 9., 9., 9.]])
Умножение вектора на матрицу
Наконец,
рассмотрим умножение вектора на матрицу. Это также можно записать двумя
способами:
или
Для реализации
второго способа, зададим одномерный и двумерный тензоры:
a = torch.FloatTensor([1,2,3])
b = torch.arange(4, 10, dtype=torch.float32).view(2, 3) # матрица 2x3
И, затем,
воспользуемся функцией:
r = torch.mv(b, a) # tensor([32., 50.])
или методом:
r = b.mv(a) # tensor([32., 50.])
Причем, обратите
внимание, в функции (или методе) mv тензор b должен
представлять собой матрицу, а тензор a – вектор. Если
их поменять местами:
r = torch.mv(a, b) # ошибка
то получим
ошибку.
Элементы линейной алгебры
Из высшей
математики хорошо известно, что матрицы можно использовать для решения систем
линейных уравнений. Для этого в PyTorch существует
модуль linalg. Давайте
рассмотрим некоторые из его функций.
Предположим,
имеется квадратная матрица 3x3:
a = torch.FloatTensor([(1, 2, 3), (1, 4, 9), (1, 8, 27)])
Первым делом
вычислим ранг этого тензора, чтобы быть уверенным, что он состоит из линейно
независимых строк и столбцов:
torch.linalg.matrix_rank(a) # ранг равен 3
Если ранг
матрицы совпадает с ее размерностью, значит, она способна описывать систему из
трех независимых линейных уравнений. В нашем случае, система уравнений будет
иметь вид:
Здесь -
некие числа линейного уравнения. Например, возьмем их равными:
y = torch.FloatTensor([10, 20, 30])
Тогда корни
уравнения можно вычислить с помощью функции solve:
torch.linalg.solve(a, y) # tensor([-5.0000, 10.0000, -1.6667])
Другой способ
решения этой же системы линейных уравнений возможен через вычисление обратной матрицы.
Изначально, уравнение можно записать в векторно-матричном виде:
Откуда получаем
решения :
На уровне пакета
PyTorch это делается
так:
invA = torch.linalg.inv(a) # вычисление обратной матрицы
x = torch.mv(invA, y) # вычисление корней
Получим
результат:
tensor([-5.0000,
10.0000, -1.6667])
Конечно, я здесь
представил лишь примеры использования модуля linalg. Приводить все
функции нет смысла, так как они имеют довольно специализированное назначение и
специалисты в своих областях без труда смогут ими воспользоваться. Для полноты
картины я лишь приведу список наиболее характерных функций, чтобы вы знали
возможности модуля linalg.
Функция
|
Описание
|
linalg.cholesky()
|
Разложение
Холецкого
|
linalg.qr()
|
QR-разложение
матрицы
|
linalg.svd()
|
Сингулярное
(SVD) разложение матрицы
|
linalg.norm()
|
Норма
матрицы или вектора
|
linalg.cond()
|
Число
обусловленности матрицы
|
linalg.det()
|
Определитель
(детерминант) матрицы
|
linalg.matrix_rank()
|
Вычисление
ранга матрицы по алгоритму SVD
|
torch.trace()
|
Сумма
диагональных элементов массива
|
linalg.eig()
|
Вычисление
собственных значений и правых собственных векторов
|
linalg.eigvals()
|
Вычисление
собственных значений матрицы
|
linalg.solve()
|
Решение
линейного матричного уравнения
|
linalg.tensorsolve()
|
Решение
линейного тензорного уравнения
|
linalg.lstsq()
|
Решает
задачу поиска наименьших квадратов для линейного матричного уравнения
|
linalg.inv()
|
Вычисление
обратной матрицы
|
linalg.pinv()
|
Вычисление
псевдообратной (Мура-Пенроуза) матрицы
|
linalg.tensorinv()
|
Вычисление
обратного тензора (N-мерного массива)
|
Конечно, это не
все математические функции пакета PyTorch. Полное
описание смотрите на сайте с официальной документацией:
https://pytorch.org/docs/stable/index.html