#11. Произведение матриц и векторов, элементы линейной алгебры
Смотреть материал на видео
Пришло время познакомиться с одной из фундаментальных возможностей пакета NumPy–матричных и векторных вычислений. На одном из прошлых занятий мы с вами уже видели, как можно поэлементно умножать один вектор на другой или одну матрицу на другую:
a = np.arange(1, 10).reshape(3, 3) b = np.arange(10, 19).reshape(3, 3) a*b
В консоли увидим результат:
array([[
10, 22, 36],
[ 52, 70, 90],
[112, 136,
162]])
Матричное умножение
Но если нам нужно выполнить именно матричное умножение, то есть, строки одной матрицы умножать на столбцы другой и результаты складывать:
то для этого следует использовать специальные функции и операторы. Начнем с функций. Итак, чтобы перемножить две матрицы a иbпо всем правилам математики, запишем следующую команду:
np. dot(a, b)
Эта функция возвращает новую матрицу (двумерный массив) с результатом умножения:
array([[ 84, 90, 96],
[201,
216, 231],
[318, 342,
366]])
Тот же результат можно получить и с помощью функции:
np.matmul(a, b)
Считается, что этот вариант предпочтительнее использовать при умножении матриц.
Векторное умножение
Аналогичные операции можно выполнять и с векторами. Математически, если у нас имеются два вектора:
то их умножение можно реализовать в двух видах:
и
Первое умножение реализуется либо через функцию:
a = np.arange(1, 10) b = np.ones(9) np.dot(a, b) # значение 45
Либо, более предпочтительной функцией для внутреннего умножения векторов:
np. inner(a, b) # 45
Второй вариант умножения (внешнее умножение векторов) реализуется с помощью функции:
np.outer(a, b)
получим результат в виде следующей матрицы:
array([[1.,
1., 1., 1., 1., 1., 1., 1., 1.],
[2., 2., 2., 2., 2., 2., 2., 2., 2.],
[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.]])
Операция умножения матриц и векторов используется довольно часто, поэтому в пакете NumPy имеется весьма полезный перегруженный оператор, заменяющий функцию matmul:
a @ b # значение 45
или, с использованием матриц:
a. resize(3, 3) b.resize(3, 3) a @ b # аналог np.matmul(a, b)
Умножение вектора на матрицу
Наконец, рассмотрим умножение вектора на матрицу. Это также можно записать двумя способами:
или
Для реализации первого способа, зададим одномерный вектор и двумерную матрицу:
a = np.array([1,2,3]) b = np.arange(4,10).reshape(3,2) # матрица 3x2
И, затем, воспользуемся уже знакомой нам функцией dot:
np.dot(a, b) # array([40, 46])
При такой записи, когда одномерный массив записан первым аргументом, а матрица – вторым, получаем умножение вектора-строки на матрицу, то есть, первый способ.
Для реализации второго способа аргументы нужно поменять местами: сначала указать матрицу, а затем, вектор. Но, если мы сейчас это сделаем с нашими массивами, то получим ошибку:
np.dot(b, a) # несогласованность размеров
Дело в том, что массив a должен представлять вектор длиной два элемента, так как матрица b имеет размер в 3 строки и 2 столбца:
Определим массивa в два элемента и умножим на матрицу b:
a = np. array([1, 2]) np.dot(b, a) # array([14, 20, 26])
Получаем вектор-строку (одномерный массив) как результат умножения. Обратите внимание, по правилам математики вектор aдолжен быть вектором-столбцом, то есть, быть представленным в виде:
a.shape = -1, 1 # вектор-столбец 2x1
Но мы использовали вектор-строку. В NumPyтак тоже можно делать и это не приведет к ошибке. Результат будет именно умножение матрицы как бы на вектор-столбец. Ну а если использовать вектор-столбец, то и на выходе получим вектор-столбец:
np.dot(b, a) # вектор-столбец 3x1
Этого же результат можно достичь, используя оператор @ (перегрузка функции matmul):
a @ b # вектор-столбец 3x1
Результат будет тем же. Вот так в NumPyвыполняется умножение матриц, векторов и вектора на матрицу.
Элементы линейной алгебры
Из высшей математики хорошо известно, что матрицы можно использовать для решения систем линейных уравнений. Для этого в NumPyсуществует модуль linalg. Давайте рассмотрим некоторые из его функций.
Предположим, имеется квадратная матрица 3×3:
a = np.array([(1, 2, 3), (1, 4, 9), (1, 8, 27)])
Первым делом вычислим ранг этой матрицы, чтобы быть уверенным, что она состоит из линейно независимых строк и столбцов:
np.linalg.matrix_rank(a) # рангравен 3
Если ранг матрицы совпадает с ее размерностью, значит, она способна описывать систему из трех независимых линейных уравнений. В нашем случае, система уравнений будет иметь вид:
Здесь - некие числа линейного уравнения. Например, возьмем их равными:
y = np.array([10, 20, 30])
Тогда корни уравнения можно вычислить с помощью функции solve:
np.linalg.solve(a, y) # array([-5. , 10. , -1.66666667])
Другой способ решения этой же системы линейных уравнений возможен через вычисление обратной матрицы. Изначально, уравнение можно записать в векторно-матричном виде:
Откуда получаем решения :
На уровне пакета NumPy это делается так:
invA = np.linalg.inv(a) # вычисление обратной матрицы invA @ y # вычисление корней
Конечно, я здесь представил лишь примеры использования модуля linalg. Приводить все функции нет смысла, так как они имеют довольно специализированное назначение и специалисты в своих областях без труда смогут ими воспользоваться. Для полноты картины я лишь приведу список наиболее характерных функций, чтобы вы знали возможности расширения linalg.
Функция |
Описание |
linalg.cholesky() |
Разложение Холецкого |
linalg. |
QR-разложение матрицы |
linalg.svd() |
Сингулярное (SVD) разложение матрицы |
linalg.norm() |
Норма матрицы или вектора |
linalg.cond() |
Число обусловленности матрицы |
linalg.det() |
Определитель (детерминант) матрицы |
linalg.matrix_rank() |
Вычисление ранга матрицы по алгоритму SVD |
np.trace() |
Сумма диагональных элементов массива |
linalg. eig() |
Вычисление собственных значений и правых собственных векторов |
linalg.eigvals() |
Вычисление собственных значений матрицы |
linalg.solve() |
Решение линейного матричного уравнения |
linalg.tensorsolve() |
Решение линейного тензорного уравнения |
linalg.lstsq() |
Решает задачу поиска наименьших квадратов для линейного матричного уравнения |
linalg.inv() |
Вычисление обратной матрицы |
linalg. pinv() |
Вычисление псевдообратной (Мура-Пенроуза) матрицы |
linalg.tensorinv() |
Вычисление обратного тензора (N-мерного массива) |
Конечно, это не все математические функции пакета NumPy. Полное описание смотрите на сайте с официальной документацией:
https://numpy.org/doc/stable/
Видео по теме
#1. Пакет numpy — установка и первое знакомство | NumPy уроки
#2. Основные типы данных. Создание массивов функцией array() | NumPy уроки
#3. Функции автозаполнения, создания матриц и числовых диапазонов | NumPy уроки
#4. Свойства и представления массивов, создание их копий | NumPy уроки
#5. Изменение формы массивов, добавление и удаление осей | NumPy уроки
#6. Объединение и разделение массивов | NumPy уроки
#7. Индексация, срезы, итерирование массивов | NumPy уроки
#8. Базовые математические операции над массивами | NumPy уроки
#9. Булевы операции и функции, значения inf и nan | NumPy уроки
#10. Базовые математические функции | NumPy уроки
#11. Произведение матриц и векторов, элементы линейной алгебры | NumPy уроки
#12. Множества (unique) и операции над ними | NumPy уроки
#13. Транслирование массивов | NumPy уроки
Векторы и матрицы в инструментарии Numpy
Рубрика: 14 Инструментарий Numpy на языке PythonАвтор: Андрей Никитенко
Содержание страницы
- 1 Векторы и матрицы
- 2 Создание матриц для работы с ними
- 3 Произведение матриц
- 4 Другие операции с матрицами
- 5 Решение системы линейных уравнений
- 6 Текстовая задача
Векторы и матрицы
Здравствуйте и вновь добро пожаловать на занятия по теме «Инструментарий Numpy на языке Python».
В этой статье мы подробнее рассмотрим векторы, а также поговорим о матрицах.
Вы уже видели, насколько массивы Numpy похож на векторы: мы можем проводить над ними такие операции, как их сложение, умножение на скаляр, выполнять поэлементные операции вроде возведения в квадрат. А что с матрицами? Матрицу можно рассматривать как двухмерный массив. Согласно другому представлению, её также можно рассматривать как список списков. Действительно, можно использовать список списков, чтобы определить матрицу. Попробуем так и сделать.
Итак,первый список будет иметь элементы 1 и 2, второй список – элементы 3 и 4.Обратите внимание, что списки должны иметь одинаковую длину.
M = np.array([ [1,2], [3,4] ])
Считается,что первый индекс – это строка, второй – столбец. Для сравнения создадим такженастоящий список списков:
L = [ [1,2], [3,4] ]
Допустим,мы хотим получить элемент матрицы, скажем, единицу. В списке Python сначала индексируется строка, что даёт нам первыйсписок, содержащий 1 и 2:
L[0]
Итак,теперь у нас есть 1 и 2. Нам нужен первый элемент из этого списка, поэтому
L[0][0]
и получаем 1. Заметьте, то же самое можно сделать ис помощью массива Numpy:
M[0][0]
Этотакже даёт нам 1. Но есть и сокращённая запись, похожая на MATLAB, с использованием запятой:
M[0,0]
Иэто также даёт нам 1. Так несколько удобнее, поскольку нужно набрать на символменьше.
Обратитевнимание, что в Numpy есть типданных, который так и называется – матрица:
M2 = np.matrix([ [1,2], [3,4] ])
Получиласьматрица. Во многом матрица схожа с массивом Numpy,но есть и отличия. В большинстве случаев используются просто массивы Numpy; в действительности даже официальная документацияфактически рекомендует не пользоваться матрицами. Поэтому в данном курсе мы небудем заострять на них своё внимание. Увидев матрицу, имеет смысл преобразоватьеё в массив. Это можно сделать с помощью команды
A = np.array(M2)
Врезультате получим ту же матрицу, но в виде массива. Обратите внимание, что,хоть это и массив, мы можем пользоваться удобными матричными операциями, напримертранспонированием:
A.T
Врезультате получается транспонированная матрица A.
Резюмируем. Мы показали, что матрица на самом деле является лишь двухмерным массивом Numpy, а вектор – одномерным массивом Numpy. Таким образом, матрица на самом деле является двухмерным вектором. Обобщая, можно считать матрицу двухмерным математическим объектом, содержащим числа, а вектор – одномерным математическим объектом, также содержащим числа. Иногда встречается представление вектора в виде двухмерного объекта. Например, в учебниках по математике может указываться вектор-столбец размерности 3×1 и вектор-строка размерности 1×3. Временами мы также будем представлять их в таком виде в Numpy, но наличие двух размерностей делает такой объект более похожим на матрицу, что может сбить с толку. В таком случае просто помните, что обсуждаются лишь две вещи – одномерные и двухмерные массивы.
Создание матриц для работы с ними
Существуют некоторые различные способы генерации массивов данных.
Иногда нужны несколько массивов просто чтобы попрактиковаться – как в этом курсе. Один из способов вы уже видели, когда я воспользовался массивом Numpy для создания списка, например
np.array([1,2,3])
Заметьте,это не очень удобно, поскольку приходится вручную вставлять каждый элемент. Аесли нужен массив с сотней элементов? А если нужно, чтобы он содержал случайныечисла? Поэтому в данной лекции будет показано, как можно создавать массивы.
Преждевсего я покажу, как создать массив, состоящих из одних нулей. Это делается спомощью функции zeros с указаниемдлины:
Z = np.zeros(10)
Этодаёт вектор с 10 элементами, состоящий из нулей. Можно создать и матрицуразмерности 10×10, состоящую из одних нулей:
Z = np.zeros((10, 10))
Врезультате получаем 100 нулей в матрице размерности 10×10. Обратите внимание, что функция по-прежнему принимает лишь один аргумент – кортеж,содержащий каждую размерность. Есть эквивалентная функция, создающая массив изодних единиц. К примеру,
O = np.ones((10, 10))
Получиласьматрица размерности 10×10, состоящая изодних единиц.
Теперь предположим, что нам нужен ряд случайных числен. Для этого можно воспользоваться функцией np.random.random. Создадим, к примеру, массив случайных чисел размерности 10×10:
R = np.random.random((10,10))
Получилсянабор случайных чисел в матрице размерностью 10×10.Бросается в глаза, что все числа больше 0 и меньше 1. Это связано с тем, чтокогда речь идёт о случайных числах, имеется в виду распределение вероятностей,откуда и возникли случайные числа. Данная конкретная функция случайных чиселдаёт нам равномерно распределённые числа между 0 и 1. А если нам нужны числа сгауссовым распределением? В Numpy есть функция идля этого. Давайте попробуем. Называется она random.randn. Вновь-таки, возьмём размерность 10×10:
G = np.random.randn((10×10))
И у нас ничего не получилось – требуются целые числа. Сама функция правильна, но мы указали неправильный аргумент. Дело в том, что, как ни странно, функция randn библиотеки Numpy воспринимает каждую размерность как отдельный аргумент, в то время как все остальные упоминавшиеся функции принимают кортежи. Поэтому правильно вставлять – только для функции randn! – каждую размерность по отдельности:
G = np.random.randn(10,10)
Теперьвсё работает. Мы имеем числа с гауссовым распределением, средним значением 0 идисперсией 1. Массивы Numpy такжепредоставляют удобные функции для вычисления этих статистических величин. Так,команда
G.mean()
даёт нам среднее значение, а команда
G.var()
позволяет получить дисперсию.
Как видим, полученные числа весьма близки к истинным значениям.
Произведение матриц
Чтоинтересно в произведениях матриц при изучении линейной алгебры, – так это то, чтообычно называется умножением матриц. Умножение матриц имеет специальноетребование: внутренние размерности двух умножаемых матриц должны совпадать.Так, если у нас есть матрица A размерности 2×3и матрица Bразмерности 3×3, то мы можем умножить A на B, поскольку внутренняя размерность равна 3. Однакомы не можем умножить B на A, поскольку внутренняя размерность левого множителяравна 3, а внутренняя размерность правого равна 2.
Возникаетвопрос: для чего существует такое требование при умножении матриц? Рассмотримопределение операции умножения матриц:
Тоесть ij-йэлемент матрицы Cравен сумме произведений всех соответствующих элементов из i-й строкиматрицы Aи j-гостолбца матрицы B.Другими словами, ij-йэлемент матрицы Cравен скалярному произведению i-й строки матрицы A и j-го столбца матрицы B. В связи с этим обычно используется функция dot библиотеки Numpy:
C = A.dot(B)
Этои будет умножением матриц.
Как в математике, так и в программировании часто встречается операция поэлементного умножения. В случае векторов мы уже видели, что для этого используется звёздочка (*). Как можно догадаться, в случае двухмерных массивов звёздочка также производит поэлементное умножение. Это означает, что при использовании звёздочки в случае многомерных массивов оба массива должны иметь в точности одинаковую размерность. Это может показаться странным, ведь в других языках звёздочка действительно означает настоящее умножение матриц. Но нужно просто запомнить, что для Numpy звёздочка означает поэлементное умножение, а функция dot – умножение матриц. Также сбивать с толку может то обстоятельство, что при записи математических уравнений нет даже чётко определённого символа для операции поэлементного умножения. Так, некоторые пользуются кружком с точкой внутри, а некоторые – кружком с крестиком внутри. Похоже, в математике просто не существует стандартной записи для данной операции, хотя она часто возникает в машинному обучении в связи с необходимостью использовать градиенты.
Другие операции с матрицами
В данном подразделе, я покажу вам некоторые другие операции с матрицами, которые позволяет библиотека Numpy. При этом предполагается, что вы уже знакомы с ними из курса линейной алгебры, а потому это будет скорее демонстрация того, как их реализовать в Numpy.
Начнёмс нахождения обратной матрицы. Вначале создадим матрицу:
A = np.array([[1,2],[3,4]])
Воспользуемсяфункцией inv модуля linalg:
Ainv = np.linalg.inv(A)
Мыполучили обратную матрицу. Чтобы проверить правильность ответа, умножим матрицу,обратную A,на саму А:
Ainv.dot(A)
Врезультате имеем единичную матрицу. Можем сделать и наоборот, умножив матрицу А на обратную ей:
A.dot(Ainv)
Итоже получаем единичную матрицу.
Следующее– нахождение определителя матрицы. Это можно сделать с помощью команды
np.linalg.det(A)
Каки ожидалось, получаем ответ -2.
Иногданужна диагональ матрицы, для этого используется команда
np.diag(A)
Врезультате имеем диагональные элементы в виде вектора. В других случаях у насесть вектор чисел, представляющий диагональ матрицы, остальные элементы которойсчитаются равными нулю. Чтобы представить такой вектор в виде двухмерногомассива, можно воспользоваться этой же функцией. Например,
np.diag([1,2])
Получим1 и 2 на диагонали, а остальные элементы – нули.
Этонадо запомнить: если подставить двухмерный массив в функцию diag, то получим одномерный массив из диагональныхэлементов; если же подставить одномерный массив, то получится двухмерныймассив, в котором все внедиагональные элементы равны нулю, а первоначальныймассив располагается на главной диагонали матрицы.
Бывает, что у нас есть два вектора, а нам нужно выполнить внешнее произведение. В частности, внешнее произведение возникает, когда мы вычисляем ковариацию ряда векторов-образцов. Мы уже знакомы с поэлементным произведением, для которого используется звёздочка, и скалярным произведением, для чего используется функция dot. Напомню, что скалярное произведение также называется внутренним произведением.
Итак,создадим два вектора:
a = np.array([1,2])
b = np.array([3,4])
Внешнеепроизведение вычисляется по команде
np.outer(a, b)
Можетепроверить правильность ответа. Обратите внимание, что можно выполнить ивнутреннее произведение с помощью команды
np.inner(a, b)
Этодаст тот же ответ, что и при использовании команды
a.dot(b)
Какимиз этих способов пользоваться – сугубо на ваше усмотрение.
Другаяраспространённая операция – нахождение следа матрицы. Это сумма элементовматриц, расположенных на главной диагонали. Заметьте, что мы можем выполнитьэту операцию, используя уже имеющиеся сведения:
np. dialog(A).sum()
Получиможидаемый ответ 5. Но в Numpy для этого естьи соответствующая функция:
np.trace(A)
Имы опять-таки получаем 5.
Ипоследнее, что мы обсудим, – это собственные значения и собственные векторы.Если вы не проходили их в курсе линейной алгебры, то, вероятно, вам следуетуглубить свои знания по этому предмету. Но сейчас я просто покажу, как всё это делаетсяв коде, поэтому если вы не знаете, что такое собственные значения и собственныевекторы, просто пропустите эту часть.
Частовозникает необходимость вычислить собственные значения и собственные векторысимметричной матрицы, такой как ковариационная матрица набора данных. Создадимслучайным образом определённые данные размерности 100×3с гауссовым распределением:
X = np.random.randn(100,3)
Обратитевнимание: считается, что каждый пример – это строка, а каждый признак – этостолбец, поэтому для данного конкретного вымышленного набора данных у насимеется 100 примеров и 3 признака. Разумеется, в Numpyуже есть функция для вычисления ковариации. Опробуем её:
cov = np.cov(X)
Проверимразмерность ковариационной матрицы, чтобы убедиться, что всё правильно.
cov.shape
Получилось,что размерность равна 100×100. Этонеправильная размерность, должно быть 3×3,ведь наши данные имеют размерность 3. Попробуем ещё раз, транспонировав X:
cov = np.cov(X.T)
Теперьразмерность 3×3. Нужно помнить, что при вычислении ковариационнойматрицы данных необходимо её сначала транспонировать.
Длявычисления собственных значений и собственных векторов есть две функции – eig и eigh. Eigh предназначена только для симметричных и эрмитовыхматриц. Если вы никогда не изучали линейную алгебру комплексных чисел, небеспокойтесь о том, что такое эрмитова матрица. Симметричной называетсяматрица, которая, будучи транспонированной, остаётся равной сама себе:
Тоесть ij-йэлемент матрицы Cравен сумме произведений всех соответствующих элементов из i-й строкиматрицы Aи j-гостолбца матрицы B. Другими словами, ij-йэлемент матрицы Cравен скалярному произведению i-й строки матрицы A и j-го столбца матрицы B. В связи с этим обычно используется функция dot библиотеки Numpy:
C = A.dot(B)
Этои будет умножением матриц.
Как в математике, так и в программировании часто встречается операция поэлементного умножения. В случае векторов мы уже видели, что для этого используется звёздочка (*). Как можно догадаться, в случае двухмерных массивов звёздочка также производит поэлементное умножение. Это означает, что при использовании звёздочки в случае многомерных массивов оба массива должны иметь в точности одинаковую размерность. Это может показаться странным, ведь в других языках звёздочка действительно означает настоящее умножение матриц. Но нужно просто запомнить, что для Numpy звёздочка означает поэлементное умножение, а функция dot – умножение матриц. Также сбивать с толку может то обстоятельство, что при записи математических уравнений нет даже чётко определённого символа для операции поэлементного умножения. Так, некоторые пользуются кружком с точкой внутри, а некоторые – кружком с крестиком внутри. Похоже, в математике просто не существует стандартной записи для данной операции, хотя она часто возникает в машинному обучении в связи с необходимостью использовать градиенты.
Другие операции с матрицами
В данном подразделе, я покажу вам некоторые другие операции с матрицами, которые позволяет библиотека Numpy. При этом предполагается, что вы уже знакомы с ними из курса линейной алгебры, а потому это будет скорее демонстрация того, как их реализовать в Numpy.
Начнёмс нахождения обратной матрицы. Вначале создадим матрицу:
A = np.array([[1,2],[3,4]])
Воспользуемсяфункцией inv модуля linalg:
Ainv = np.linalg.inv(A)
Мыполучили обратную матрицу. Чтобы проверить правильность ответа, умножим матрицу,обратную A,на саму А:
Ainv.dot(A)
Врезультате имеем единичную матрицу. Можем сделать и наоборот, умножив матрицу А на обратную ей:
A.dot(Ainv)
Итоже получаем единичную матрицу.
Следующее– нахождение определителя матрицы. Это можно сделать с помощью команды
np.linalg.det(A)
Каки ожидалось, получаем ответ -2.
Иногданужна диагональ матрицы, для этого используется команда
np.diag(A)
Врезультате имеем диагональные элементы в виде вектора. В других случаях у насесть вектор чисел, представляющий диагональ матрицы, остальные элементы которойсчитаются равными нулю. Чтобы представить такой вектор в виде двухмерногомассива, можно воспользоваться этой же функцией. Например,
np.diag([1,2])
Получим1 и 2 на диагонали, а остальные элементы – нули.
Этонадо запомнить: если подставить двухмерный массив в функцию diag, то получим одномерный массив из диагональныхэлементов; если же подставить одномерный массив, то получится двухмерныймассив, в котором все внедиагональные элементы равны нулю, а первоначальныймассив располагается на главной диагонали матрицы.
Бывает, что у нас есть два вектора, а нам нужно выполнить внешнее произведение. В частности, внешнее произведение возникает, когда мы вычисляем ковариацию ряда векторов-образцов. Мы уже знакомы с поэлементным произведением, для которого используется звёздочка, и скалярным произведением, для чего используется функция dot. Напомню, что скалярное произведение также называется внутренним произведением.
Итак,создадим два вектора:
a = np.array([1,2])
b = np.array([3,4])
Внешнеепроизведение вычисляется по команде
np.outer(a, b)
Можетепроверить правильность ответа. Обратите внимание, что можно выполнить ивнутреннее произведение с помощью команды
np.inner(a, b)
Этодаст тот же ответ, что и при использовании команды
a.dot(b)
Какимиз этих способов пользоваться – сугубо на ваше усмотрение.
Другаяраспространённая операция – нахождение следа матрицы. Это сумма элементовматриц, расположенных на главной диагонали. Заметьте, что мы можем выполнитьэту операцию, используя уже имеющиеся сведения:
np.dialog(A).sum()
Получиможидаемый ответ 5. Но в Numpy для этого естьи соответствующая функция:
np.trace(A)
Имы опять-таки получаем 5.
Ипоследнее, что мы обсудим, – это собственные значения и собственные векторы.Если вы не проходили их в курсе линейной алгебры, то, вероятно, вам следуетуглубить свои знания по этому предмету. Но сейчас я просто покажу, как всё это делаетсяв коде, поэтому если вы не знаете, что такое собственные значения и собственныевекторы, просто пропустите эту часть.
Частовозникает необходимость вычислить собственные значения и собственные векторысимметричной матрицы, такой как ковариационная матрица набора данных. Создадимслучайным образом определённые данные размерности 100×3с гауссовым распределением:
X = np.random.randn(100,3)
Обратитевнимание: считается, что каждый пример – это строка, а каждый признак – этостолбец, поэтому для данного конкретного вымышленного набора данных у насимеется 100 примеров и 3 признака. Разумеется, в Numpyуже есть функция для вычисления ковариации. Опробуем её:
cov = np.cov(X)
Проверимразмерность ковариационной матрицы, чтобы убедиться, что всё правильно.
cov.shape
Получилось,что размерность равна 100×100. Этонеправильная размерность, должно быть 3×3,ведь наши данные имеют размерность 3. Попробуем ещё раз, транспонировав X:
cov = np.cov(X.T)
Теперьразмерность 3×3. Нужно помнить, что при вычислении ковариационнойматрицы данных необходимо её сначала транспонировать.
Длявычисления собственных значений и собственных векторов есть две функции – eig и eigh. Eigh предназначена только для симметричных и эрмитовыхматриц. Если вы никогда не изучали линейную алгебру комплексных чисел, небеспокойтесь о том, что такое эрмитова матрица. Симметричной называетсяматрица, которая, будучи транспонированной, остаётся равной сама себе:
Эрмитова же матрица – это матрица, которая остаётся равной сама себе, будучи сопряжённой:
Какизвестно, ковариационная матрица является симметричной, поэтому можноиспользовать функцию eigh. Её ипопробуем:
np.linalg.eigh(cov)
Врезультате получаем кортежи. Первый содержит три собственных значения, а второй– собственные векторы, представленные в столбцах. Попробуем теперь с обычнойфункцией eig:
np.linalg.eig(cov)
Получаемтот же ответ. Обратите внимание, что обычная функция eigхоть и даёт тот же ответ, но полученные собственные значения и соответствующиесобственные векторы могут размещаться в другом порядке. В данном конкретномслучае порядок тот же, но есть вероятность, что он может поменяться.
На этом лекция заканчивается. Она довольно короткая, поскольку была создана для тех, кто уже знаком с математическим аппаратом, но ещё не знает, как его реализовать в Numpy.
Решение системы линейных уравнений
В заключение мы рассмотрим последнюю из самых распространённых операций с матрицами – решение системы линейных уравнений.
Напомню, что система линейных уравнений имеет вид
где A – матрица, x – вектор-столбец искомых значений, b – вектор чисел. Решение, конечно же, состоит в умножении обоих частей уравнений на матрицу,обратную A:
Этокорректная операция, поскольку предполагается, что A является квадратной матрицей, что значит, что онаобратима. Тогда xимеет единственное решение. Другими словами, если размерность x равна D, то у нас есть D уравнений с D неизвестными.
Тутне должно возникнуть никаких сложностей, поскольку у нас уже есть весьинструментарий, необходимый для такого рода вычислений. Вы уже видели, какнаходится обратная матрица и как производить умножение матриц, а именно эти двевещи нам и нужны.
Решимпример. Aу нас будет матрицей
Этокорректная операция, поскольку предполагается, что A является квадратной матрицей, что значит, что онаобратима. Тогда xимеет единственное решение. Другими словами, если размерность x равна D, то у нас есть D уравнений с D неизвестными.
Тутне должно возникнуть никаких сложностей, поскольку у нас уже есть весьинструментарий, необходимый для такого рода вычислений. Вы уже видели, какнаходится обратная матрица и как производить умножение матриц, а именно эти двевещи нам и нужны.
Решимпример. Aу нас будет матрицей
bу нас будет вектором [1, 2]:
b = np.array([1, 2])
Решениембудет
x = np.linalg.inv(A).dot(b)
Итак,решением являются числа 0 и 0,5. Для проверки попробуйте решить этот примервручную.
Конечно же, ввиду очень частого проведения такого рода вычислений, есть способ и получше – с помощью функции с соответствующим названием solve. Поэтому можно записать
x = np.linalg.solve(A, b)
Иполучим тот же ответ.
Если вы когда-либо прежде писали код в MATLAB, то могли заметить, что при попытке использовать метод inv MATLAB выдаёт предупреждение и сообщает, что есть и более эффективный способ вычислений. В MATLAB он называется не solve, но по сути это тот же самый алгоритм, и он действительно куда более эффективен и точен. Поэтому если у вас когда-нибудь возникнет необходимость решить подобного рода уравнение, никогда не пользуетесь inv. Всегда используйте solve.
Текстовая задача
Давайте разберём несложный пример, чтобы попрактиковаться в использовании функции solve.
Итак,поставим задачу. На небольшой ярмарке входная плата составляет 1,5 доллара для ребёнкаи 4 доллара для взрослого. Однажды за день ярмарку посетило 2200 человек; приэтом было собрано 5050 долларов входной платы. Сколько детей и сколько взрослыхпосетили ярмарку в этот день?
Итак,обозначим количество детей через X1, а количествовзрослых – через X2. Мы знаем, что
Мытакже знаем, что
Мытакже знаем, что
Обратите внимание, что это линейное уравнение, где A равно:
а b равно :
а b равно :
Подставимэти значения в Numpy и найдёмрешение:
A = np.array([[1,1], [1.5,4]])
b = np. array([2200, 5050])
np.linalg.solve(A, b)
Получаемответ: 1500 детей и 700 взрослых. Попробуйте также решить это уравнениевручную, чтобы проверить ответ.
Получаемответ: 1500 детей и 700 взрослых. Попробуйте также решить это уравнениевручную, чтобы проверить ответ.
python — умножение вектора матрицы numpy
Простейшее решение
Используйте numpy.dot
или a.dot(b)
. Смотрите документацию здесь.
>>> a = np.array([[ 5, 1,3], [ 1, 1 ,1], [ 1, 2 ,1]]) >>> b = np.массив ([1, 2, 3]) >>> напечатать a.dot(b) массив([16, 6, 8])
Это происходит потому, что массивы numpy не являются матрицами, а стандартные операции *, +, -, /
работают с массивами поэлементно.
Обратите внимание, что хотя вы можете использовать numpy.matrix
(по состоянию на начало 2021 года), где *
будет рассматриваться как стандартное умножение матриц, numpy. matrix
устарело и может быть удалено в будущих выпусках. . См. примечание в его документации (воспроизведено ниже):
Больше не рекомендуется использовать этот класс, даже для линейной алгебры. Вместо этого используйте обычные массивы. В будущем класс может быть удален.
Спасибо @HopeKing.
Другие решения
Также знайте, что есть и другие варианты:
Как указано ниже, при использовании python3.5+ и numpy v1.10+ оператор
@
работает так, как вы ожидаете:>>> печать (а @ б) массив([16, 6, 8])
Если вы хотите переборщить, вы можете использовать
numpy.einsum
. Документация даст вам представление о том, как это работает, но, честно говоря, я не совсем понимал, как его использовать, пока не прочитал этот ответ и не поиграл с ним самостоятельно.>>> np.einsum('ji,i->j', а, б) массив([16, 6, 8])
По состоянию на середину 2016 года (numpy 1. 10.1) вы можете попробовать экспериментальный
numpy.matmul
, который работает какnumpy.dot
с двумя основными исключениями: нет скалярного умножения, но он работает со стеками матриц.>>> np.matmul(a, b) массив([16, 6, 8])
numpy.inner
работает так же, какnumpy.dot
для умножения матрицы на вектор, но ведет себя иначе для матрично-матричного и тензорного умножения (см. Википедию о различиях между внутренним продуктом и скалярным произведением в целом или см. этот ответ SO относительно реализаций numpy).>>> np.inner(a, b) массив([16, 6, 8]) # Остерегайтесь использования для умножения матрицы на матрицу! >>> б = а.Т >>> нп.точка (а, б) массив([[35, 9, 10], [9, 3, 4], [10, 4, 6]]) >>> np.inner(a, b) массив([[29, 12, 19], [7, 4, 5], [8, 5, 6]])
Если у вас есть несколько 2D массивов до
точка
вместе, вы можете рассмотреть функциюnp. linalg.multi_dot
, которая упрощает синтаксис многих вложенныхnp.dot
с. Обратите внимание, что это работает только с двумерными массивами (т.е. не для умножения матрицы на вектор).>>> np.dot(np.dot(a, a.T), a).dot(a.T) массив([[1406, 382, 446], [382, 106, 126], [446, 126, 152]]) >>> np.linalg.multi_dot((а, а.Т, а, а.Т)) массив([[1406, 382, 446], [382, 106, 126], [446, 126, 152]])
Более редкие варианты для крайних случаев
Если у вас есть тензоры (массивы размерности больше или равные единице), вы можете использовать
numpy.tensordot
с необязательным аргументомaxes=1
:>>> np.tensordot(a, b, оси=1) массив([16, 6, 8])
Не используйте
numpy.vdot
, если у вас есть матрица комплексных чисел, так как матрица будет сведена к одномерному массиву, тогда он попытается найти комплексно-сопряженное скалярное произведение между вашей сглаженной матрицей и вектором (что не удастся из-за несоответствия размеран*м
противн
).
python — Как умножить два вектора и получить матрицу?
спросил
Изменено 1 год, 3 месяца назад
Просмотрено 59 тысяч раз
В операции numpy у меня есть два вектора, скажем, вектор A равен 4X1, вектор B равен 1X5, если я сделаю AXB, в результате должна получиться матрица размера 4X5.
Но я пробовал много раз, выполняя множество видов изменения формы и транспонирования, все они либо вызывают ошибку, говорящую о том, что не выровнено, либо возвращают одно значение.
Как мне получить выходной продукт матрицы, которую я хочу?
- python
- numpy
- матрица
- вектор
- умножение матрицы
Нормальное умножение матриц работает до тех пор, пока векторы имеют правильную форму. Помните, что *
в Numpy — это поэлементное умножение на 9.0026 , а умножение матриц доступно с помощью numpy.dot()
(или с помощью оператора @
в Python 3.5)
>>> numpy.dot(numpy.array([[1], [2]] ), numpy.массив([[3, 4]])) массив([[3, 4], [6, 8]])
Это называется «внешний продукт». Вы можете получить его, используя простые векторы, используя numpy.outer()
:
>>> numpy.outer(numpy.array([1, 2]), numpy.array([3, 4])) массив([[3, 4], [6, 8]])
Если вы используете numpy.
Во-первых, убедитесь, что у вас есть два вектора. Например, vec1.shape = (10, )
и vec2.shape = (26, )
; в numpy вектор-строка и вектор-столбец — это одно и то же.
Во-вторых, вы делаете res_matrix = vec1.reshape(10, 1) @ vec2.reshape(1, 26) ;
.
Наконец, у вас должно быть: res_matrix.shape = (10, 26)
.
документация numpy говорит, что она устареет np.