EMBED
To add the widget to iGoogle, click here. On the next page click the «Add» button. You will then see the widget on your iGoogle account.
To embed this widget in a post on your WordPress blog, copy and paste the shortcode below into the HTML source:
For self-hosted WordPress blogs
To embed this widget in a post, install the Wolfram|Alpha Widget Shortcode Plugin and copy and paste the shortcode above into the HTML source.
To embed a widget in your blog’s sidebar, install the Wolfram|Alpha Widget Sidebar Plugin, and copy and paste the Widget ID below into the «id» field:
To add a widget to a MediaWiki site, the wiki must have the Widgets Extension installed, as well as the code for the Wolfram|Alpha widget.
To include the widget in a wiki page, paste the code below into the page source.
WolframAlpha для всех
Матрица, полученная из данной матрицы заменой каждой ее строки на столбец с тем же номером, называется транспонированной к данной.
В Wolfram|Alpha для получения транспонированной матрицы служит запрос transpose.
Если вместо буквенных обозначений использовать числа, то Wolfram|Alpha, по запросу transpose кроме транспонированной матрицы выдает еще и другую сопутствующую информацию.
Wolfram|Alpha по запросу transpose выдает транспонированную матрицу для матрицы любого размера:
Транспонированная матрица имеет следующее свойство: transpose (transpose A) = A.
Глава 8 Работа с символьным процессором
Системы компьютерной алгебры снабжаются специальным процессором для выполнения аналитических (символьных) вычислений. Его основой является ядро, хранящее всю совокупность формул и формульных преобразований, с помощью которых производятся аналитические вычисления. Чем больше этих формул в ядре, тем надежней работа символьного процессора и тем вероятнее, что поставленная задача будет решена, разумеется, если такое решение существует в принципе (что бывает далеко не всегда).
Ядро символьного процессора системы MathCAD — несколько упрощенный вариант ядра известной системы символьной математики Maple V фирмы Waterloo Maple Software [12, 18], у которой MathSoft (разработчик MathCAD) приобрела лицензию на его применение, благодаря чему MathCAD стала (начиная с версии 3.0) системой символьной математики.
Прямой доступ к большинству операций ядра (возможный в системе Maple V R3/R4), для пользователя MathCAD, к сожалению, закрыт. К примеру, библиотечный модуль Maple V содержит около 100 функций линейной алгебры, тогда как в модуле MathCAD 3.0 есть только три важнейшие функции из этого раздела. Многие функции и правила преобразования в ядре используются лишь для получения промежуточных преобразований.
Тем не менее это обстоятельство нельзя считать уж слишком большим недостатком системы MathCAD. Во-первых, потому, что ее назначение иное — прежде всего решение задач в численном виде, а во-вторых, потому, что система Maple V явно избыточна (в ее ядре около 2800 функций и правил преобразования) и ориентирована на пользователей с весьма далекими от средних потребностями в решении задач компьютерной алгебры (символьной математики) [12].
Введение в систему MathCAD символьных вычислений придает ей качественно новые возможности, которые отсутствовали у прежних версий системы [5—7]. Теоретические сведения об алгоритмах компьютерной алгебры можно найти в книге [19]. Куда важней, что символьные вычисления выполняются в конечном счете столь же просто (для пользователя), как, скажем, вычисление квадрата х.
Операции, относящиеся к работе символьного процессора, содержатся в подменю позиции Symbolic (Символика) главного меню (см. рис. 8.1).
Чтобы символьные операции выполнялись, процессору необходимо указать, над каким выражением эти операции должны производиться, т е надо выделить выражение (правила выделения неоднократно описывались выше) Для ряда операций следует не только указать выражение, к которому они относятся, но и наметить переменную, относительно которой выполняется та или иная символьная операция. Само выражение в таком случае не выделяется. ведь и так ясно, что если маркер ввода выделяет переменную какого-либо выражения, то это выражение уже отмечено наличием в нем выделяемой переменной
Рис. 8.1 Вид экрана системы с подменю позиции Symbolic главного меню
Символьные операции разбиты на пять характерных разделов Первыми идут наиболее часто используемые операции Они могут выполняться с выра жениями, содержащими комплексные числа или имеющими решения в комплексном виде
Операции с выделенными выражениями
К операциям с выделенными выражениями относятся следующие Evaluate (Вычислить) — преобразовать выражение с выбором вида преоб разований из подменю,
Simplify (Упростить) — упростить выделенное выражение с выполнением таких операций, как сокращение подобных слагае мых, приведение к общему знаменателю, использова ние основных тригонометрических тождеств и т д, Expand (Разложить — раскрыть выражение [например, для по степеням) (Х+ Y) (Х- Y) получаем X2- Y2>, Factor (Разложить — разложить число или выражение на множи-на множители) тели [например X2-Y2 даст (X+Y) (X-Y)], Collect (Разложить — собрать слагаемые, подобные выделенному по подвыражению) выражению, которое может быть отдельной переменной или функцией со своим аргументом (ре зультатом будет выражение, полиномиальное от носительно выбранного выражения),
Polynomial Coefficients — найти коэффициенты полинома по заданной
(Полиномиальные переменной, приближающего выражение,
коэффициенты) в котором эта переменная использована
Операции с выделенными переменными
К числу операций с выделенными переменными относятся
Solve (Решить — найти значения выделенной переменной,
относительно переменной) при которых содержащее ее выражение становится равным нулю (решить уравнение или неравенство относительно выделенной переменной);
Substitute (Заменить — заменить указанную переменную содержи-
переменную) мым буфера обмена;
Differentiate — дифференцировать все выражение, содержа-(Дифференцировать щее выделенную переменную, по отношению по переменной) к этой переменной (остальные переменные рассматриваются как константы);
Integrate (Интегрировать — интегрировать все выражение, содержащее
по переменной) выделенную переменную, по этой переменной;
Expand to Series. — найти несколько членов разложения выра-(Разложить в ряд) жения в ряд Тейлора относительно выделен ной переменной;
Convert to Partial Fraction — разложить на элементарные дроби выраже-(Разложить на элементарные ние, которое рассматривается как рацио-дроби) нальная дробь относительно выделенной пе ременной.
Операции с выделенными матрицами
Операции с выделенными матрицами представлены позицией подменю Matrix (Матричные операции), которая имеет свое подменю со следующими операциями:
Transpose (Транспонировать) — получить транспонированную матрицу;
Invert (Обратить) — создать обратную матрицу;
Determinant (Определитель) — вычислить детерминант (определитель) матрицы.
Операции преобразования
В MathCAD 7.0 PRO в позиции Symbol содержится раздел операций преобразования, создающий подменю со следующими возможностями:
Fourier Transform — выполнить прямое преобразование Фурье
(Преобразование Фурье) относительно выделенной переменной;
Inverse Fourier Transform — выполнить обратное преобразование
(Обратное преобразование Фурье относительно выделенной
Laplace Transform — выполнить прямое преобразование (Преобразование Лапласа) Лапласа относительно выделенной пере менной (результат — функция от пере менной s);
Inverse Laplace Transform — выполнить обратное преобразование (Обратное преобразование Лапласа относительно выделенной Лапласа) переменной (результат — функция от переменной t);
Z Transform (Z-преобразование) — выполнить прямое Z-преобразование вы ражения относительно выделенной пере менной (результат — функция от пере менной z);
Inverse Z Transform — выполнить обратное Z-преобразование (Обратное Z-преобразование) относительно выделенной переменной (результат — функция от переменной n)
Стиль эволюции
К стилю эволюции относится одна операция-
Evaluation Style. — задать вывод результата символьной операции под
(Стиль эволюции) основным выражением, рядом с ним или вместо него
В последующих разделах этой главы каждая из упомянутых выше операций будет рассмотрена более подробно с необходимыми примерами.
8.2. Выделение объектов символьных операций
Для проведения символьных операций нужно прежде всего выделить объект, над которым эти операции будут выполняться. Если объект отсутствует, доступа к соответствующим операциям в позиции Symbolic главного меню нет, а сами операции выделены затененным шрифтом. Объектом операции может быть самостоятельное математическое выражение, часть такого выражения или заданной пользователем функции, результат предшествующей операции и т д.
Напомним, что есть два вида выделения: пунктирными линиями и сплошными. Чтобы отметить объект пунктирной линией, достаточно установить на него курсор-крестик, нажать клавишу Ctrl или Shift и воспользоваться правой клавишей мыши.
Можно установить курсор около выбранного объекта и, нажав правую клавишу мыши, перемещать ее по столу При этом появляется пунктирный прямоугольник, который перемещением мыши можно расширять в различных направлениях. Как только этот прямоугольник захватывает один или несколько объектов, они оказываются также выделенными — обведенными пунктирной линией, отмечающей область, занимаемую объектом в окне.
Выделение пунктирной линией используется для перемещения объектов по окну. Для этого достаточно внутрь отмеченного объекта (выражения) поместить курсор мыши, нажать правую клавишу и, удерживая ее нажатой, перемещать мышь. При этом объект (или сразу несколько объектов) будет перемещаться по экрану и его можно оставить (отпустив клавишу мыши) на новом месте Напоминаем, что нажатие клавиши F3 ведет к переносу выражений в буфер обмена и стиранию их в окне. Нажатие клавиши F4 переносит выражения из буфера обмена на место, указанное курсором. Курсор можно перемещать как мышью, так и обычными клавишами управления им.
Для выполнения операций с символьным процессором нужно выделить объект (целое выражение или его часть) сплошными линиями, синими на экране цветного дисплея. Для выделения некоторой переменной в объекте нужно подвести к ее концу курсор мыши и нажать левую клавишу. Переменная будет отмечена жирной чертой (синей на экране цветного дисплея), расположенной сразу после переменной Перемещая курсор по полю объекта и нажимая левую клавишу повторно, можно выделить отдельные части выражения или выражение целиком.
Часть символьных операций производится указанием на объект как на выражение или его часть. Например, расширение или упрощение выражений требуют такого указания на объект Другие операции, такие, как вычисление производной или интеграла, требуют указания переменной, относительно которой производится операция, допустим, дифференцирования или интегрирования.
Если заданная операция невыполнима, система выводит в дополнительном окне сообщение об ошибке или просто повторяет выделенное выражение. Последнее означает, что операция задана корректно, но результат не может быть получен, например, если делается попытка разложить на множители объект, уже разложенный или не содержащий такого разложения в принципе.
При выполнении символьных операций иногда приходится сталкиваться с неприятной и трудной проблемой — «разбуханием» результатов, как промежуточных, так и конечных. Решения, которые хорошо известны профессиональному математику (и даже студенту), система обычно выдает с блеском — в виде, который описан в справочнике или учебнике. Примером может служить решение квадратного уравнения или вычисление простого неопределенного интеграла
Но даже незначительное усложнение задачи порою может породить очень сложное и громоздкое решение. Пример тому — решение кубического уравнения. В этом случае решение (по запросу системы) можно поместить в буфер обмена и использовать его для оценки пользователем (но не для дальнейших преобразований системой в автоматическом режиме).
Система MathCAD содержит пять типов символьных операций, выполняемых над объектами — выделенными математическими выражениями. При этом под математическим выражением подразумевается как полная математическая формула, так и функционально полная часть какой-либо формулы. Ниже представлено описание символьных операций над выражениями
8.3. Выполнение символьных вычислений (Evaluate Symbolically)
Символьная операция Evaluate Symbolically [Shift+F9] (Вычислить) обеспечивает работу с математическими выражениями, содержащими встроенные в систему функции и представленными в различном виде: полиномиальном, дробно-рациональном, в виде сумм и произведений, производных и интегралов и т. д. Операция стремится произвести все возможные численные вычисления и представить выражение в наиболее простом виде. Она возможна над матрицами с символьными элементами. Производные и определенные интегралы, символьные значения которых вычисляются, должны быть представлены в своей естественной форме.
Особо следует отметить возможность выполнения численных вычислений с повышенной точностью — 20 знаков после запятой. Для перехода в такой режим вычислений нужно числовые константы в вычисляемых объектах задавать с обязательным указанием десятичной точки, например 10.0 или 3.0, а не 10 или 3. Этот признак является указанием на проведение вычислений такого типа.
На рис. 8.2 показаны типовые примеры действия операции Evaluate Symbolically.
Здесь слева показаны исходные выражения, подвергаемые символьным преобразованиям, а справа — результат этих преобразований. Так представлены и другие примеры, приведенные в этой главе.
Рис. 8.2 Действие операции Evaluate Symbolically
Операция Evaluate Symbolically одна из самых мощных. Как видно на рис. 8.2, она позволяет в символьном виде вычислять суммы (и произведения) рядов, производные и неопределенные интегралы, выполнять символьные и численные операции с матрицами.
Эта операция содержит подменю со следующими командами:
Evaluate Symbolically [Shift+F9] — выполнить символьное вычисление (Вычислить в символах) выражения;
Floating Point Evaluation. — выполнить арифметические (С плавающей точкой) операции в выражении с результатом в форме числа с плавающей точкой;
Complex Evaluation — выполнить преобразование с пред-(В комплексном виде) ставлением в комплексном виде.
Команда Evaluate Symbolically тут наиболее важная. Назначение других команд очевидно: они нужны, если результат требуется получить в форме комплексного или действительного числа. К примеру, если вы хотите вместо числа л получить 3.141. используйте команду Floating Point Evaluation. В режиме символьных вычислений результат может превосходить машинную бесконечность системы — см. пример на вычисление ехр( 1000.0) на рис. 7.2. При этом число точных значащих цифр результата практически не ограничено (или, точнее говоря, зависит от емкости ОЗУ).
8.4. Упрощение выражений (Simplify)
Символьная операция Simplify (Упростить) — одна из самых важных. Эта операция позволяет упрощать математические выражения, содержащие алгебраические и тригонометрические функции, а также выражения со степенными многочленами (полиномами).
Упрощение означает замену более сложных фрагментов выражений на более простые. Приоритет тут отдается простоте функций. К примеру, функция tan(x) считается более сложной, чем функции sin(x) и cos(.x). Поэтому tan(x) упрощается так, что получает представление через соотношение этих функций, что несколько неожиданно, так как в некоторых пакетах символьной математики, например Derive, ситуация иная: они заменяют отношение sin(x)/cos(x) функцией tan(x).
Эта команда открывает широкие возможности для упрощения сложных и плохо упорядоченных алгебраических выражений. На рис. 8.3 даны примеры применения операции Simplify.
Рис. 8.3 Действие операции Simplify
Два последних примера на приведенном рисунке показывают, как с помощью операции Simplify можно выполнять символьные вычисления производных и определенных интегралов. Результатом вычислений могут быть специальные математические функции (см. последний пример, в котором символьное значение интеграла дает результат, выраженный через интегральный синус). Вполне возможно вычисление производных высшего порядка. На рис. 8.4 показано последовательное применение операции Simplify для вычисления производных алгебраического выражения, от первой до пятой включительно.
Система MathCAD содержит встроенную функцию для вычисления значений определенных интегралов приближенным численным методом. Ею целесообразно пользоваться, когда нужно просто получить значение определенного интеграла в виде числа. Однако команда Simplify применительно к вычислениям определенных интегралов делает гораздо больше — она ищет аналитическое выражение для интеграла. Более того, она способна делать это и при вычислении кратных интегралов, пределы которых — функции. Наглядный пример этому дает рис. 8.5.
На рис. 8.6 показано применение операции Simplify для вычисления сумм и произведений символьных последовательностей. Результат операции, как и следовало ожидать, получается в символьной форме (если она существует).
Приведенные примеры могут создать впечатление, что MathCAD лихо справляется со всеми производными, интегралами, суммами и произведения-
Рис. 8.4 Вычисление производных алгебраического выражения с порядком от 1 до 5
Рис. 8.5 Вычисление двойных и тройных определенных интегралов
ми с помощью операции Simplify. К сожалению, это далеко не так. Нередко система не справляется с кажущимися простыми справочными примерами. Надо помнить, что символьный процессор системы MathCAD обладает заметно урезанной библиотекой функций и преобразований (в сравнении с библиотекой системы Maple V). Поэтому часто система не находит решение в замкнутом виде, хотя оно и приводится в справочнике. Тогда система повторяет введенное выражение или сообщает об ошибке.
Следует также отметить, что при выполнении командами меню Symbol символьных вычислений выражения необходимо указывать явно. Например, недопустимо вводить некоторую функцию пользователя F(x) и пытаться найти ее производные или интеграл. Это существенное ограничение, и котором надо всегда помнить. Однако оно преодолимо при выполнении вычислений с помощью функций системы SmartMath, которая описывается в дальнейшем;
Рис. 8.6 Вычисление сумм и произведений символьных последовательностей
главное в том, что для вывода символьных вычислений в этом случае используется оператор —>.
В результате преобразований могут появляться специальные функции — как встроенные в систему (функции Бесселя, гамма-функция, интеграл вероятности и др.), так и ряд функций, дополнительно определенных при загрузке символьного процессора (интегральные синус и косинус, интегралы Френеля, эллиптические интегралы и др.) Последние нельзя использовать при создании математических выражений.
8.5. Расширение выражений (Expand)
Действие операции Expand (Разложить по степеням) в известном смысле противоположно действию операции Simplify. Подвергаемое преобразованию выражение расширяется с использованием известных (и введенных в символьное ядро) соотношений, например алгебраических разложений многочленов, произведений углов и т. д Разумеется, расширение происходит только в том случае, когда его результат однозначно возможен Иначе нельзя считать, что действие этой операции противоположно действию операции Simplify. К примеру, операция Simplify преобразует сумму квадратов синуса и косинуса в 1, тогда как обратное преобразование многозначно и потому в общем виде невыполнимо
При преобразовании выражений операция Expand Expression старается более простые функции представить через более сложные, свести алгебраические выражения, представленные в сжатом виде, к выражениям в развернутом виде и т д Примеры действия операции Expand Expression даны на рис. 8 7
Последний пример на этом рисунке показывает, что результатом операции может быть специальная математическая функция, которая считаегся более сложным выражением, чем порождающее ее выражение С виду, однако, выражения со специальными математическими функциями обычно выглядят гораздо проще, чем исходные выражения
Рис. 8.7 Действие операции Expand Expression
8.6. Разложение выражений (Factor)
Операция Factor Expression (Разложить на множители) используется для факторизации — разложения выражений или чисел на простые множители Она способствует выявлению математической сущности выражений, к примеру, наглядно выявляет представление полинома через его действительные корни, а в том случае, когда разложение части полинома содержит комплекс-
Рис. 8.8 Действие операции Factor Expression
но-сопряженные корни, порождающее их выражение представляется квадратичным трехчленом. Примеры действия этой операции даны на рис. 8.8.
В большинстве случаев (но не всегда) операция факторизации ведет к упрощению выражений. Термин факторизация не является общепризнанным в отечественной математической литературе, но мы его оставляем в связи с созвучностью с англоязычным именем этой операции.
8.7. Комплектование по выражениям (Collect)
Операция Collect (Разложить по подвыражению) обеспечивает замену указанного выражения выражением, скомплектованным по базису указанной переменной, если такое представление возможно. В противном случае появляется окно с сообщением о невозможности комплектования по указанному базису. Эта команда особенно удобна, когда заданное выражение есть функция ряда переменных и нужно представить его в виде функции заданной переменной, имеющей вид степенного многочлена. При этом другие переменные входят в сомножители указанной переменной, представленной в порядке уменьшения ее степени. На рис. 8.9 показаны примеры действия этой операции.
Рис. 8.9 Действие операции Collect
В том случае, когда комплектование по базису указанной переменной невозможно, система выдает сообщение об этом. Оно выводится в отдельном небольшом информационном окошке.
8.8. Вычисление коэффициентов полиномов (Polynomial Coefficients)
Операция Polynomial Coefficients (Полиномиальные коэффициенты), в ранних версиях MathCAD отсутствующая, служит для вычисления коэффициентов полинома. Операция применяется, если заданное выражение — полином
Рис. 8.10 Примеры применения операции Polynomial Coefficients
(степенной многочлен) или может быть представлено таковым относительно выделенной переменной На рис 810 показаны примеры применения этой операции
Результатом операции является вектор с коэффициентами полинома Операция полезна при решении задач полиномиальной аппроксимации и регрессии
8.9. Дифференцирование по заданной переменной (Differentiate)
Следующая группа символьных операций выполняется с выражениями, требующими указания переменной, по отношению к которой выполняется операция Для этого достаточно установить на переменной курсор ввода Само выражение при этом не указывается отдельно, поскольку указание в нем на переменную является одновременно и указанием на само выражение. Если выражение содержит другие переменные, то они рассматриваются как константы Изучим операции этой группы, начиная с операции дифференцирования
Нахождение символьного значения производной — одна из самых рас пространенных задач в аналитических вычислениях. Операция Differentiate (Дифференцировать по переменной) возвращает символьное значение производной выражения по той переменной, которая указана курсором Для вычисления производных высшего порядка (свыше 1) нужно повторить вычисление необходимое число раз На рис 811 показано применение операции дифференцирования
В трех последних примерах на рис 811 показано, что в выражениях, производная которых вычисляется, могут стоять и встроенные в систему специальные математические функции Они могут появляться и в результатах вычислений
Рис. 8.11 Примеры символьного дифференцирования
Особое внимание стоит обратить на последний пример. Попытка вычислить производную от функции Бесселя здесь ведет к тому, что результат направляется в буфер обмена, поскольку представлен в виде функций или действий, содержащихся в ядре символьных операций системы, но недоступных символьному процессору. Поэтому результат дифференцирования вызывается из буфера обмена командой Paste (Вставить). Такой результат нельзя использовать в других операциях прямо, но он вполне подходит для творческого осмысления пользователем. Тот и решает, полезен результат или нет
8.10. Интегрирование по заданной переменной (Integrate)
Другая не менее важная операция при символьных вычислениях — вычисление интегралов (или нахождение первообразных) для аналитически заданной функции. Для этого используется операция Integrate (Интегрировать по переменной). Она возвращает символьное значение неопределенного интеграла по указанной курсором ввода переменной. Выражение, в состав которого входит переменная, является подынтегральной функцией.
На рис. 8.12 показаны примеры символьного интегрирования по переменной х. Визуализация таких вычислений (как и описанных выше), прямо скажем, не велика если бы не поясняющие текстовые комментарии, то было бы совсем неясно, откуда берутся выражения в документе и резульгаты чего они представляют
Как и для операции дифференцирования, в состав исходных выражений и результатов символьного интегрирования могут входить встроенные в систему специальные математические функции. На это указывают два последних примера из приведенных на рис. 8.12.
Рис. 8.12 Примеры символьного интегрирования
8.11. Решение уравнения относительно заданной переменной (Solve)
Если задано некоторое выражение F(x) и отмечена переменная х, то операция Solve (Решить) возвращает символьные значения указанной переменной х, при которых F(x)==0. Это очень удобно для решения алгебраических уравнений, например квадратных и кубических, или для вычисления корней полинома. Рис. 8.13 содержит примеры решения квадратного уравнения и нахождения комплексных корней полинома четвертой степени.
Рис.8.13 Примеры решения уравнений
Ранее отмечалось, что усложнение уравнения, например переход от квадратного уравнения к кубическому, может вызвать и существенное усложнение результата. Тогда система представляет решение в более компактном виде (но без общепринятой математической символики) и предлагает занести его в буфер обмена.
С помощью операции Paste (Вставить) в позиции Edit (Правка) главного меню можно перенести решение в основное окно системы, но оно имеет уже тип текстового комментария, а не математического выражения, пригодного для дальнейших преобразований. Впрочем, часть его можно (опять-таки с помощью буфера обмена) ввести в формульные блоки для последующих преобразований и вычислений.
Более того, форма представления результата в таком случае отличается от принятой в системе MathCAD (например, в качестве знака деления используется косая черта, для возведения в степень — составной знак** и т. д.). Это сделано ради компактности представления результатов вычислений. Рис. 8.14 показывает результат решения кубического уравнения в символьном виде.
Рис. 8.14 Решение кубического уравнения в символьном виде
Последний пример наглядно иллюстрирует проблему «разбухания» результатов. Если при решении квадратного уравнения получены простые выражения, известные даже школьникам (рис. 8.13 — первый пример), то при увеличении порядка уравнения всего на единицу результат оказался представленным весьма громоздкими и малопригодными для анализа формулами. Хорошо еще, что существующими!
В случаях, подобных приведенному, пользователю надо реально оценить свои силы в упрощении решения. Это придется сделать вручную. При технических расчетах специалист нередко знает, какие из параметров решения несущественны и может отбросить их. Однако для строгих математических расчетов это не всегда возможно, поэтому даже громоздкий результат может быть весьма полезным с познавательной точки зрения.
8.12. Подстановка для заданной переменной (Substitute)
Операция Substitute (Подстановка) возвращает новое выражение, полученное путем подстановки на место указанной переменной некоторого другого выражения. Последнее должно быть подготовлено и скопировано (операциями Cut или Copy) в буфер обмена. Наряду с получением результата в символьном виде эта команда позволяет найти и числовые значения функции некоторой переменной путем замены ее на числовое значение. На рис. 8.15 представлены примеры операций с подстановкой.
Рис. 8.15 Примеры операций с подстановкой
Подстановки и замены переменных довольно часто встречаются в математических расчетах, что делает эту операцию весьма полезной. Кроме того, она дает возможность перейти от символьного представления результата к числовому.
8.13. Разложение в ряд Тейлора по заданной переменной (Expand to Series. )
Операция Expand to Series. (Разложить в ряд) возвращает разложение в ряд Тейлора выражения относительно выделенной переменной с заданным по запросу числом членов ряда n (число определяется по степеням ряда). По умолчанию задано п=6. Разложение возможно для функции заданной переменной. В разложении указывается остаточная погрешность разложения. На рис. 8.16 представлено применение этой операции для разложения функции sin(x)/x. Минимальная погрешность получается при малых х (см. графическое представление функции и ее ряда).
Символьные операции нередко можно комбинировать для решения сложных задач. Рис. 8.17 показываег интересное решение одной из таких задач — вычисление определенного интеграла, который не берется в замкнутой форме.
Рис. 8.16 Пример на разложение функции в ряд Тейлора
Рис. 8.17 Взятие определенного интеграла в символьной форме с заменой подынтегральной функции ее разложением
Если пользователя (например, инженера) интересует просто числовое значение интеграла, надо лишь поставить после интеграла знак вывода = и значение интеграла будет вычислено адаптивным численным методом Симп-сона. Однако вычислить такой интеграл с помощью операции Simplify (Упростить) не удастся после долгих попыток система сообщит, что интеграл в замкнутой форме не берется.
Прием, который иллюстрирует рис. 8.17, заключается в замене подынтегральной функции ее разложением в ряд Тейлора. Вначале получим такое разложение с избытком — для 10 членов ряда (однако учтенных нечетных членов тут нет, такова специфика функции). Далее, выделив четыре первых реальных члена и используя операции Copy (Копировать) и Paste (Вставить) в позиции Edit (Правка) главного меню, поместим это разложение на место шаблона подынтегральной функции. Теперь проблем с вычислением интеграла операцией Simplify не будет
Интеграл получен в форме числа е=ехр(1), помноженного на дробный множитель, представленный в рациональной форме (отношения целых чисел) Это обстоятельство, возможно, бесполезное для рядового пользователя, наверняка будет весьма положительно воспринято математиком, поскольку здесь напрашиваются определенные аналитические выводы (которых нельзя сделать при вычислении интеграла численными методами)
8.14. Разложение на правильные дроби (Convert to Partial Fraction)
Операция Convert to Partial Fraction (Разложить на элементарные дроби) возвращает символьное разложение выражения, представленное относительно заданной переменной в виде суммы правильных целых дробей. На рис 8 18 даны примеры такого разложения
Рис. 8.18 Примеры разложения на дроби
Как видно из представленных примеров, применение этой операции в большинстве случаев делает результат более длинным, чем исходное выражение Однако он более нагляден и содействует выявлению математической сущности исходного выражения
8.15. Транспонирование матрицы (Transpose)
Символьный процессор системы MathCAD обеспечивает проведение в символьном виде трех наиболее распространенных матричных операций’ транспонирование и обращение матриц, а также вычисление их детерминанта.
Эти операции в подменю Matrix обозначены так: Transpose (Транспонировать), Invert (Обратить) и Determinant (Найти определитель). Если элементы матрицы — числа, то выполняются соответствующие операции в числовой форме.
Транспонирование матрицы означает перестановку строк и столбцов. Оно реализуется операцией Transpose (Транспонировать). Подлежащая транспонированию матрица должна быть выделена.
8.1 б. Обращение матриц (Invert)
Обращение матриц означает создание такой матрицы А-1, для которой произведение ее на исходную матрицу А дает единичную матрицу, т. е. матрицу с диагональными элементами, равными 1, и остальными — нулевыми. Обращение допустимо для квадратных матриц с размером NxN, где N> 1 — число строк и столбцов матрицы. Такую же размерность имеет и обращенная матрица.
Обращение матриц — широко распространенная математическая задача. Существует множество программ на разных языках программирования, решающих эту задачу с той или иной степенью успеха. Для MathCAD это рутинная задача. На рис. 8.19 приведены примеры выполнения типовых матричных операций. Последняя из них — обращение матрицы в символьной форме с помощью операции Invert.
Рис. 8.19 Примеры матричных операций в символьной форме
При выполнении матричных операций в символьной форме проблема «разбухания» результатов становится весьма серьезной. Если, к примеру, для обратной матрицы с размером 2х2 или 3х3 еще можно получить ответ, размещающийся в окне документа, то для матриц большего размера это становится невозможным. Впрочем, большинство аналитических задач очень редко оперирует такими матрицами.
8.17. Функции преобразований Фурье, Лапласа и Z-преобразований
Для выполнения широко распространенных в технических и научных приложениях преобразований Фурье (Fourie и Inverse Fourie), Лапласа (Laplace и Inverse Laplace) и Z-преобразований (Z и Inverse Z) служат соответствующие операции в подменю позиции Symbolic главного меню
Для применения этих операций следует записать исходное выражение и отметить в нем переменную, относительно которой будет производиться преобразование Тогда указанные выше операции становятся доступными и выделяются четкими надписями
Не вдаваясь в суть перечисленных достаточно известных преобразований, приведем простейшие примеры их применения, они показаны на рис 8 20 Здесь даны примеры как прямого, так и обратного (Inverse) преобразования каждого типа
Рис. 8.20 Примеры применения функций преобразования Фурье, Лапласа и Z-преобразований
Не следует полагать, что для всех случаев результаты преобразования будут в точности совпадать со справочными и что результат двойного преобразования (вначале прямого, а затем обратного) приведет к первоначальной функции Указанные преобразования довольно сложны, и грамотное применение их требует соответствующих математических познаний Не случайно в ранние версии MathCAD (например, 3 0) они не были включены в виде команд
8.18. Установка стиля эволюции символьных выражений (Evolution Style. )
Последняя позиция подменю Symbolic — Evalution Style. — служит для установки стиля эволюции выражений, над которыми выполняются символьные операции Напомним, что под эволюцией математических выражений в данном случае подразумеваеюя изменение их вида в результате символьных преобразований.
Данная операция выводит окно с установками стиля эволюции, показанное на рис. 8.21.
Рис. 8.21 Окно установки стиля эволюции
В этом окне можно установить три тина вывода результата символьных преобразований:
Vertically, inserting lines — расположение результата под основным
(Вертикально, включая линии) выражением с включением пустых линий,
Vertically, without inserting lines — расположение результата прямо под
(Вертикально, без линий) основным выражением;
Gorizontally (По горизонтали) — расположение результата рядом (по го ризонтали) с основным выражением.
Кроме того, установкой знака «птички» в прямоугольниках можно ввести еще две опции:
Show Comments — наблюдать комментарии;
Evaluate in Place (Замещать) — заместить исходное выражение резуль татом его символьного преобразования.
Все варианты стиля вывода результатов символьных операций представлены на рис. 8.21. В ряде случаев предпочтительно применение символьного оператора вывода —>, который делает символьные преобразования более наглядными.
8.19. Интерпретация результатов символьных операций в буфере обмена
Необходимо отметить, что не всегда результат символьных операций выводится в окно редактирования. Иногда он оказывается настолько громоздким, что MathCAD использует специальную компактную форму его представления и помещает его в буфер обмена. Уже оттуда его можно вызвать в текстовом формате в окно редактирования, нажав клавишу F4 или клавиши Shift+ Ins. To же самое можно сделать с помощью команды Copy (Копировать) в позиции Edit (Правка) главного меню.
Записи математических выражений в буфере обмена напоминают их записи на языке Фортран:
• справедливы операторы арифметических операций+, -,* и /;
• возведение в степень обозначается как**;
• первая производная функции f(x) записывается в виде diff(f(x),x), а п-я производная в виде diff(f(x),x$n);
• частная производная обозначается как D, п-го порядка (D,n) и по п-му аргументу как (D[n]);
• интеграл с подынтегральной функцией/^) записывается как int(f(x),x);
• операторы суммы и произведения обозначаются как sum() и product();
• композиция функций указывается символом @ [например, (exp@cos) (x) означает exp(sin(x))\,
• кратная композиция указывается символами @@ [например, (f(@@3) (x) означает f(f(f(x)))>;
• замещение любого корня уравнения указывается записью RootOf (уравнение) [например, оба корня i и -г уравнения Z**2+1=0 представляются записью RootOf(Z**2+l)].
С помощью команды Save As. (Сохранить как) в позиции File папки обмена можно сохранить последнее содержимое буфера обмена в виде текстового файла. Это может быть полезным для осмысления и анализа полученного результата. В Windows 95 доступ к папке обмена обеспечивает приложение «Просмотр папки обмена», которое находится в папке «Стандартные» меню программ.
8.20. Применение преобразований Лапласа для
аналитического решения дифференциальных уравнений
Итак, если результаты символьных вычислений включают функции, не содержащиеся во входном языке системы, они помещаются в буфер обмена по запросу системы и могут быть вызваны оттуда командой Paste (Вставить). Тогда результаты имеют статус текстовых комментариев, т. е. в явном виде с ними дальнейшие действия проводить невозможно.
Однако это совсем не означает бесполезности таких результатов. Напротив, пользователь, владеющий приемами аналитических вычислений, может успешно использовать такие результаты для решения серьезных математических задач. Здесь мы остановимся на задаче получения аналитического решения для линейных дифференциальных уравнений. Сразу отметим, что системы компьютерной алгебры Mathematica 2.2.2 или Maple V R3/R4 легко решают подобные задачи встроенными средствами. Рассмотрим, как это можно сделать в системе MathCAD 6.0 PRO, таких средств не имеющей.
Для получения решения можно воспользоваться преобразованиями Лапласа. Это иллюстрирует рис. 8.22, на котором подробно показан процесс получения результата. Приходится вручную запускать прямое преобразование Лапласа, по его результатам составлять алгебраическое уравнение и после решения запускать обратное преобразование Лапласа — оно дает решение в виде временной зависимости
Рис. 8.22 Пример решения дифференциального уравнения второго порядка с применением преобразований Лапласа
На рис 8.23 приведено решение другого дифференциального уравнения Используется тот же метод решения, что и в предыдущем примере.
Оба примера наглядно показывают, что помещаемый в буфер обмена результат символьных операций может быть очень полезным и порой предоставлять возможности, которые нельзя получить прямым образом. Это расширяет области применения системы MathCAD
Рис. 8.23 Пример решения другого дифференциального уравнения
Прикладная линейная алгебра для исследователей данных [1 ed.] 9786017989453
![Прикладная линейная алгебра для исследователей данных [1 ed.] 9786017989453](https://dokumen.pub/img/200x200/1nbsped-9786017989453.jpg)
Table of contents :
От издательства
Об авторе
Колофон
Предисловие
Глава 1. Введение
Что такое линейная алгебра и зачем ее изучать?
Об этой книге
Предварительные требования
Математика
Отношение
Программирование
Математические доказательства в противовес интуитивному пониманию на основе программирования
Рабочий код в книге и предназначенный для скачивания онлайн
Упражнения по программированию
Как пользоваться этой книгой (для учителей и самообучающихся)
Глава 2. Векторы. Часть 1
Создание и визуализация векторов в NumPy
Геометрия векторов
Операции на векторах
Сложение двух векторов
Вычитание двух векторов
Геометрия сложения и вычитания векторов
Умножение вектора на скаляр
Сложение скаляра с вектором
Геометрия умножения вектора на скаляр
Транспонирование
Транслирование векторов в Python
Модуль вектора и единичные векторы
Точечное произведение векторов
Точечное произведение является дистрибутивным
Геометрия точечного произведения
Другие умножения векторов
Адамарово умножение
Внешнее произведение
Перекрестное и тройное произведения
Ортогональное разложение векторов
Резюме
Упражнения по программированию
Глава 3. Векторы. Часть 2
Множества векторов
Линейно-взвешенная комбинация
Линейная независимость
Математика линейной независимости
Независимость и вектор нулей
Подпространство и охват
Базис
Определение базиса
Резюме
Упражнения по программированию
Глава 4. Применения векторов
Корреляция и косинусное сходство
Фильтрация временных рядов и обнаружение признаков
Кластеризация методом k-средних
Упражнения по программированию
Упражнения по корреляции
Упражнения по фильтрации и обнаружению признаков
Упражнения по алгоритму k-средних
Глава 5. Матрицы. Часть 1
Создание и визуализация матриц в NumPy
Визуализация, индексация и нарезка матриц
Специальные матрицы
Матричная математика: сложение, умножение на скаляр, адамарово умножение
Сложение и вычитание
«Сдвиг» матрицы
Умножение на скаляр и адамарово умножение
Стандартное умножение матриц
Правила допустимости умножения матриц
Умножение матриц
Умножение матрицы на вектор
Линейно-взвешенные комбинации
Результаты геометрических преобразований
Матричные операции: транспонирование
Обозначение точечного и внешнего произведений
Матричные операции: LIVE EVIL (порядок следования операций)
Симметричные матрицы
Создание симметричных матриц из несимметричных
Резюме
Упражнения по программированию
Глава 6. Матрицы. Часть 2
Нормы матриц
След матрицы и норма Фробениуса
Пространства матрицы (столбцовое, строчное, нуль-пространство)
Столбцовое пространство
Строчное пространство
Нуль-пространства
Ранг
Ранги специальных матриц
Ранг сложенных и умноженных матриц
Ранг сдвинутых матриц
Теория и практика
Применения ранга
В столбцовом пространстве
Линейная независимость множества векторов
Определитель
Вычисление определителя
Определитель с линейными зависимостями
Характеристический многочлен
Резюме
Упражнения по программированию
Глава 7. Применения матриц
Матрицы ковариаций многопеременных данных
Геометрические преобразования посредством умножения матриц на векторы
Обнаружение признаков изображения
Резюме
Упражнения по программированию
Упражнения по матрицам ковариаций и корреляций
Упражнения по геометрическим преобразованиям
Упражнения по обнаружению признаков изображения
Глава 8. Обратные матрицы
Обратная матрица
Типы обратных матриц и условия обратимости
Вычисление обратной матрицы
Обратная матрица матрицы 2×2
Обратная матрица диагональной матрицы
Инвертирование любой квадратной полноранговой матрицы
Односторонние обратные матрицы
Уникальность обратной матрицы
Псевдообратная матрица Мура–Пенроуза
Численная стабильность обратной матрицы
Геометрическая интерпретация обратной матрицы
Резюме
Упражнения по программированию
Глава 9. Ортогональные матрицы и QR-разложение
Ортогональные матрицы
Процедура Грама–Шмидта
QR-разложение
Размеры матриц Q и R
Почему матрица R является верхнетреугольной
QR и обратные матрицы
Резюме
Упражнения по программированию
Глава 10. Приведение строк и LU-разложение
Системы уравнений
Конвертирование уравнений в матрицы
Работа с матричными уравнениями
Приведение строк
Метод устранения по Гауссу
Метод устранения по Гауссу–Жордану
Обратная матрица посредством метода устранения по Гауссу–Жордану
LU-разложение
Взаимообмен строками посредством матриц перестановок
Резюме
Упражнения по программированию
Глава 11. Общие линейные модели и наименьшие квадраты
Общие линейные модели
Терминология
Настройка общей линейной модели
Решение общих линейных моделей
Является ли решение точным?
Геометрическая перспектива наименьших квадратов
В чем причина работы метода наименьших квадратов?
Общая линейная модель на простом примере
Наименьшие квадраты посредством QR-разложения
Резюме
Упражнения по программированию
Глава 12. Применения метода наименьших квадратов
Предсказывание количеств велопрокатов на основе погоды
Регрессионная таблица с использованием библиотеки statsmodels
Мультиколлинеарность
Регуляризация
Полиномиальная регрессия
Поиск в параметрической решетке для отыскания модельных параметров
Резюме
Упражнения по программированию
Упражнения по аренде велосипедов
Упражнения по мультиколлинеарности
Упражнения по регуляризации
Упражнение по полиномиальной регрессии
Упражнения по поиску в параметрической решетке
Глава 13. Собственное разложение
Интерпретации собственных чисел и собственных векторов
Геометрия
Статистика (анализ главных компонент)
Подавление шума
Уменьшение размерности (сжатие данных)
Отыскание собственных чисел
Отыскание собственных векторов
Неопределенность собственных векторов по знаку и шкале
Диагонализация квадратной матрицы
Особая удивительность симметричных матриц
Ортогональные собственные векторы
Действительно-значные собственные числа
Собственное разложение сингулярных матриц
Квадратичная форма, определенность и собственные числа
Квадратичная форма матрицы
Определенность
ATA является положительной (полу)определенной
Обобщенное собственное разложение
Резюме
Упражнения по программированию
Глава 14. Сингулярное разложение
Общая картина сингулярного разложения
Сингулярные числа и ранг матрицы
Сингулярное разложение на Python
Сингулярное разложение и одноранговые «слои» матрицы
Сингулярное разложение из собственного разложения
Сингулярное разложение матрицы АТА
Конвертация сингулярных чисел в дисперсию: объяснение
Кондиционное число
Сингулярное разложение и псевдообратная матрица Mура–Пенроуза
Резюме
Упражнения по программированию
Глава 15. Применения собственного и сингулярного разложений
Анализ главных компонент с использованием собственного и сингулярного разложений
Математика анализа главных компонент
Шаги выполнения PCA
PCA посредством сингулярного разложения
Линейный дискриминантный анализ
Низкоранговая аппроксимация посредством сингулярного разложения
Сингулярное разложение для шумоподавления
Резюме
Упражнения
Анализ главных компонент (PCA)
Линейный дискриминантный анализ (LDA)
Сингулярное разложение для низкоранговых аппроксимаций
Сингулярное разложение для шумоподавления в изображениях
Глава 16. Краткое руководство по языку Python
Почему Python и какие есть альтернативы?
Интерактивные среды разработки
Использование Python локально и онлайн
Работа с файлами исходного кода в Google Colab
Переменные
Типы данных
Индексация
Функции
Методы в качестве функций
Написание своих собственных функций
Библиотеки
NumPy
Индексация и нарезка в NumPy
Визуализация
Переложение формул в исходный код
Форматирование печати и F-строки
Поток управления
Компараторы
Инструкции if
Инструкции elif и else
Несколько условий
Циклы for
Вложенные инструкции управления
Измерение времени вычислений
Получение помощи и приобретение новых знаний
Что делать, когда дела идут наперекосяк
Резюме
Дополнение А. Теорема о ранге и нульности
Тематический указатель
Citation preview
Прикладная линейная алгебра для исследователей данных
Practical Linear Algebra for Data Science
From Core Concepts to Applications Using Python
Beijing · Boston · Farnham · Sebastopol · Tokyo
Прикладная линейная алгебра для исследователей данных От ключевых концепций до приложений с использованием Python
УДК 512.64 ББК 22.143 К76
Коэн M. И. К76 Прикладная линейная алгебра для исследователей данных / пер. с англ. А. В. Логунова. – М.: ДМК Пресс, 2023. – 328 с.: ил. ISBN 978-6-01798-945-3 В этой книге рассказывается о ключевых концепциях линейной алгебры, реализованных на Python, и о том, как их использовать в науке о данных, машинном и глубоком обучении и вычислительном моделировании. Рассматриваются интерпретации и приложения векторов и матриц, матричная арифметика, важные разложения, используемые в прикладной линейной алгебре, и пр. Прочитав книгу, вы научитесь внедрять и адаптировать под свои задачи целый ряд современных методов анализа и алгоритмов. Издание адресовано специалистам по обработке данных, а также будет полезно студентам и широкому кругу разработчиков ПО.
УДК 512.64 ББК 22.143
Authorized Russian translation of the English edition of Practical Linear Algebra for Data Science ISBN 9781098120610 © 2022 Syncxpress BV. This translation is published and sold by permission of O’Reilly Media, Inc., which owns or controls all rights to publish and sell the same. Все права защищены. Любая часть этой книги не может быть воспроизведена в какой бы то ни было форме и какими бы то ни было средствами без письменного разрешения владельцев авторских прав.
ISBN 978-1-098-12061-0 (англ.) ISBN 978-6-01798-945-3 (каз.)
© 2022 Syncxpress BV © Перевод, оформление, издание, Books.kz, 2023
Содержание От издательства. . 12 Об авторе. . 13 Колофон. 14 Предисловие. 15 Глава 1. Введение . . 17 Что такое линейная алгебра и зачем ее изучать. 17 Об этой книге. . 18 Предварительные требования. 19 Математика. 19 Отношение. . 19 Программирование. 20 Математические доказательства в противовес интуитивному пониманию на основе программирования. 20 Рабочий код в книге и предназначенный для скачивания онлайн. 22 Упражнения по программированию. 22 Как пользоваться этой книгой (для учителей и самообучающихся). 23
Глава 2. Векторы. Часть 1. 24 Создание и визуализация векторов в NumPy. 24 Геометрия векторов. . 27 Операции на векторах. . 28 Сложение двух векторов. 28 Вычитание двух векторов. . 29 Геометрия сложения и вычитания векторов. 30 Умножение вектора на скаляр. 31 Сложение скаляра с вектором. . 32 Геометрия умножения вектора на скаляр. . 32
6 Содержание Транспонирование. 33 Транслирование векторов в Python. 34 Модуль вектора и единичные векторы. . 35 Точечное произведение векторов. 36 Точечное произведение является дистрибутивным. 38 Геометрия точечного произведения. . 39 Другие умножения векторов. 40 Адамарово умножение. . 40 Внешнее произведение. . 41 Перекрестное и тройное произведения. 42 Ортогональное разложение векторов. . 42 Резюме. 46 Упражнения по программированию. 46
Глава 3. Векторы. Часть 2. 49 Множества векторов. 49 Линейно-взвешенная комбинация. 50 Линейная независимость. 51 Математика линейной независимости. 53 Независимость и вектор нулей. 54 Подпространство и охват. . 54 Базис. 57 Определение базиса. 60 Резюме. 61 Упражнения по программированию. 62
Глава 4. Применения векторов. . 64 Корреляция и косинусное сходство. 64 Фильтрация временных рядов и обнаружение признаков. 67 Кластеризация методом k-средних. 68 Упражнения по программированию. 71 Упражнения по корреляции. . 71 Упражнения по фильтрации и обнаружению признаков. 73 Упражнения по алгоритму k-средних. 75
Глава 5. Матрицы. Часть 1. 76 Создание и визуализация матриц в NumPy. . 76 Визуализация, индексация и нарезка матриц. 76 Специальные матрицы. 78 Матричная математика: сложение, умножение на скаляр, адамарово умножение. 80 Сложение и вычитание. 80 «Сдвиг» матрицы. 81
Умножение на скаляр и адамарово умножение. . 82 Стандартное умножение матриц. . 82 Правила допустимости умножения матриц. 83 Умножение матриц. . 84 Умножение матрицы на вектор. . 85 Линейно-взвешенные комбинации. 86 Результаты геометрических преобразований. 86 Матричные операции: транспонирование. 88 Обозначение точечного и внешнего произведений. 88 Матричные операции: LIVE EVIL (порядок следования операций). . 89 Симметричные матрицы. 89 Создание симметричных матриц из несимметричных. 90 Резюме. 91 Упражнения по программированию. 92
Глава 6. Матрицы. Часть 2. 97 Нормы матриц. . 97 След матрицы и норма Фробениуса. 99 Пространства матрицы (столбцовое, строчное, нуль-пространство). 100 Столбцовое пространство. . 100 Строчное пространство. 104 Нуль-пространства. 104 Ранг. 108 Ранги специальных матриц. 110 Ранг сложенных и умноженных матриц. 112 Ранг сдвинутых матриц. 113 Теория и практика. 113 Применения ранга. 114 В столбцовом пространстве. 115 Линейная независимость множества векторов. 116 Определитель. 117 Вычисление определителя. 117 Определитель с линейными зависимостями. 119 Характеристический многочлен. 119 Резюме. 121 Упражнения по программированию. 123
Глава 7. Применения матриц. 128 Матрицы ковариаций многопеременных данных. 128 Геометрические преобразования посредством умножения матриц на векторы. . 131 Обнаружение признаков изображения. 135 Резюме. 138 Упражнения по программированию. 138
8 Содержание Упражнения по матрицам ковариаций и корреляций. 138 Упражнения по геометрическим преобразованиям. 140 Упражнения по обнаружению признаков изображения. 142
Глава 8. Обратные матрицы. 144 Обратная матрица. 144 Типы обратных матриц и условия обратимости. 145 Вычисление обратной матрицы. 146 Обратная матрица матрицы 2×2. 146 Обратная матрица диагональной матрицы. 148 Инвертирование любой квадратной полноранговой матрицы. 149 Односторонние обратные матрицы. 151 Уникальность обратной матрицы. 153 Псевдообратная матрица Мура–Пенроуза. 154 Численная стабильность обратной матрицы. 155 Геометрическая интерпретация обратной матрицы. 156 Резюме. 158 Упражнения по программированию. 158
Глава 9. Ортогональные матрицы и QR-разложение. 162 Ортогональные матрицы. 162 Процедура Грама–Шмидта. 164 QR-разложение. 165 Размеры матриц Q и R. 166 Почему матрица R является верхнетреугольной. . 168 QR и обратные матрицы. 169 Резюме. 169 Упражнения по программированию. 170
Глава 10. Приведение строк и LU-разложение. 174 Системы уравнений. . 174 Конвертирование уравнений в матрицы. 175 Работа с матричными уравнениями. . 176 Приведение строк. 178 Метод устранения по Гауссу. . 180 Метод устранения по Гауссу–Жордану. . 181 Обратная матрица посредством метода устранения по Гауссу–Жордану. 182 LU-разложение. 183 Взаимообмен строками посредством матриц перестановок. 185 Резюме. 186 Упражнения по программированию. 186
Глава 11. Общие линейные модели и наименьшие квадраты. 189 Общие линейные модели. . 190 Терминология. . 190 Настройка общей линейной модели. . 190 Решение общих линейных моделей. . 192 Является ли решение точным. 193 Геометрическая перспектива наименьших квадратов. 194 В чем причина работы метода наименьших квадратов. 195 Общая линейная модель на простом примере. . 197 Наименьшие квадраты посредством QR-разложения. 201 Резюме. 202 Упражнения по программированию. 203
Глава 12. Применения метода наименьших квадратов. 207 Предсказывание количеств велопрокатов на основе погоды. 207 Регрессионная таблица с использованием библиотеки statsmodels. 212 Мультиколлинеарность. . 213 Регуляризация. 213 Полиномиальная регрессия. . 215 Поиск в параметрической решетке для отыскания модельных параметров. 218 Резюме. 220 Упражнения по программированию. 221 Упражнения по аренде велосипедов. 221 Упражнения по мультиколлинеарности. 222 Упражнения по регуляризации. 223 Упражнение по полиномиальной регрессии. . 224 Упражнения по поиску в параметрической решетке. 225
Глава 13. Собственное разложение. . 227 Интерпретации собственных чисел и собственных векторов. 228 Геометрия. . 228 Статистика (анализ главных компонент). 229 Подавление шума. 230 Уменьшение размерности (сжатие данных). . 231 Отыскание собственных чисел. 231 Отыскание собственных векторов. 234 Неопределенность собственных векторов по знаку и шкале. 235 Диагонализация квадратной матрицы. 236 Особая удивительность симметричных матриц. 238 Ортогональные собственные векторы. . 238 Действительно-значные собственные числа. 240
10 Содержание Собственное разложение сингулярных матриц. 241 Квадратичная форма, определенность и собственные числа. 243 Квадратичная форма матрицы. . 243 Определенность . 245 ATA является положительной (полу)определенной. 245 Обобщенное собственное разложение. 246 Резюме. 248 Упражнения по программированию. 249
Глава 14. Сингулярное разложение. 254 Общая картина сингулярного разложения. 254 Сингулярные числа и ранг матрицы. . 256 Сингулярное разложение на Python. 256 Сингулярное разложение и одноранговые «слои» матрицы. . 257 Сингулярное разложение из собственного разложения. 259 Сингулярное разложение матрицы АТА. 260 Конвертация сингулярных чисел в дисперсию: объяснение. 260 Кондиционное число. . 261 Сингулярное разложение и псевдообратная матрица Mура–Пенроуза. 262 Резюме. 263 Упражнения по программированию. 264
Глава 15. Применения собственного и сингулярного разложений. 268 Анализ главных компонент с использованием собственного и сингулярногоразложений. 268 Математика анализа главных компонент. 269 Шаги выполнения PCA. 271 PCA посредством сингулярного разложения. . 272 Линейный дискриминантный анализ. 273 Низкоранговая аппроксимация посредством сингулярного разложения. 275 Сингулярное разложение для шумоподавления. . 276 Резюме. 276 Упражнения. 277 Анализ главных компонент (PCA). 277 Линейный дискриминантный анализ (LDA). 281 Сингулярное разложение для низкоранговых аппроксимаций. . 285 Сингулярное разложение для шумоподавления в изображениях. 287
Глава 16. Краткое руководство по языку Python. 291 Почему Python и какие есть альтернативы. 291 Интерактивные среды разработки. . 292 Использование Python локально и онлайн. 292
Работа с файлами исходного кода в Google Colab. . 293 Переменные. 294 Типы данных. 296 Индексация. 297 Функции. 297 Методы в качестве функций. 299 Написание своих собственных функций. 299 Библиотеки. 301 NumPy. 301 Индексация и нарезка в NumPy. 302 Визуализация. . 303 Переложение формул в исходный код. 305 Форматирование печати и F-строки. . 308 Поток управления. 309 Компараторы. 309 Инструкции if. 310 Инструкции elif и else. 310 Несколько условий. 311 Циклы for. . 312 Вложенные инструкции управления. 312 Измерение времени вычислений. 313 Получение помощи и приобретение новых знаний. 313 Что делать, когда дела идут наперекосяк. . 314 Резюме. 314
Дополнение А. Теорема о ранге и нульности. . 315 Тематический указатель. 317
От издательства Отзывы и пожелания Мы всегда рады отзывам наших читателей. Расскажите нам, что вы думаете об этой книге – что понравилось или, может быть, не понравилось. Отзывы важны для нас, чтобы выпускать книги, которые будут для вас максимально полезны. Вы можете написать отзыв на нашем сайте www.dmkpress.com, зайдя на страницу книги и оставив комментарий в разделе «Отзывы и рецензии». Также можно послать письмо главному редактору по адресу dmkpress@gmail. com; при этом укажите название книги в теме письма. Если вы являетесь экспертом в какой-либо области и заинтересованы в написании новой книги, заполните форму на нашем сайте по адресу http:// dmkpress.com/authors/publish_book/ или напишите в издательство по адресу [email protected].
Список опечаток Хотя мы приняли все возможные меры для того, чтобы обеспечить высокое качество наших текстов, ошибки все равно случаются. Если вы найдете ошибку в одной из наших книг, мы будем очень благодарны, если вы сообщите о ней главному редактору по адресу [email protected]. Сделав это, вы избавите других читателей от недопонимания и поможете нам улучшить последующие издания этой книги.
Нарушение авторских прав Пиратство в интернете по-прежнему остается насущной проблемой. Издательство «ДМК Пресс» очень серьезно относится к вопросам защиты авторских прав и лицензирования. Если вы столкнетесь в интернете с незаконной публикацией какой-либо из наших книг, пожалуйста, пришлите нам ссылку на интернет-ресурс, чтобы мы могли применить санкции. Ссылку на подозрительные материалы можно прислать по адресу элект ронной почты [email protected]. Мы высоко ценим любую помощь по защите наших авторов, благодаря которой мы можем предоставлять вам качественные материалы.
Об авторе Майк Икс Коэн – адъюнкт-профессор неврологии1 в Институте Дондерса (Медицинский центр Университета Радбуда) в Нидерландах. Имеет более чем 20-летний опыт преподавания научного программирования, анализа данных, статистики и смежных тем, а также является автором нескольких онлайновых курсов2 и учебников. У него подозрительно сухое чувство юмора, и ему нравится все фиолетовое.
См. https://oreil.ly/Ee23F. См. https://oreil.ly/BurUH.
Колофон Животное на обложке книги «Прикладная линейная алгебра для исследователей данных» – это антилопа ньяла, также именуемая низменной ньялой либо просто ньялой (Tragelaphus angasii). Самки и молодые ньялы обычно имеют светло-красновато-коричневый окрас шерсти, в то время как взрослые самцы имеют темно-коричневую или даже сероватую шерсть. Как у самцов, так и у самок есть белые полосы вдоль тела и белые пятна на боках. У самцов спиралевидные рога, которые вырастают до 33 дюймов в длину, а их шерсть намного более лохматая, с длинной бахромой, свисающей от горла до задних конечностей, и гривой густых черных волос вдоль позвоночника. Самки весят около 130 фунтов, тогда как самцы могут весить до 275 фунтов. Ньялы обитают в лесах Юго-Восточной Африки, ареал которых включает Малави, Мозамбик, Южную Африку, Эсватини, Замбию и Зимбабве. Они пуг ливы и предпочитают пастись ранним утром, ближе к вечеру либо ночью, проводя бóльшую часть жаркой части дня, отдыхая в укрытии. Ньялы образуют свободные стада численностью до десяти особей, хотя самцы постарше ведут одиночный образ жизни. Они не являются территориальными животными, хотя самцы будут бороться за доминирование во время спаривания. Ньялы считаются видом, вызывающим наименьшее беспокойство, хотя выпас скота, сельское хозяйство и потеря среды обитания представляют для них угрозу. Многие животные на обложках издательства O’Reilly находятся под угрозой исчезновения, и все они важны для нашего мира. Иллюстрация на обложке выполнена Карен Монтгомери по мотивам старинной линейной гравюры из Histoire Naturelle.
Предисловие Условные обозначения в книге В книге используются следующие типографические условные обозначения: курсивный шрифт обозначает новые термины, URL-адреса, адреса электронной почты, имена файлов и расширения файлов. моноширинный шрифт используется для листингов программ, а также внутри абзацев для ссылки на элементы программ, такие как переменные или имена функций, базы данных, типы данных, переменные среды, инструкции и ключевые слова. Данный элемент обозначает общее замечание. Данный элемент обозначает предупреждение или предостережение.
Использование примеров исходного кода Дополнительные материалы (примеры исходного кода, упражнения и т. д.) доступны для скачивания по адресу https://github.com/mikexcohen/LinAlg4DataScience. Если у вас есть технический вопрос или проблема с использованием примеров исходного кода, то, пожалуйста, отправьте электронное письмо по адресу [email protected].
Благодарности Должен признаться, я действительно не люблю писать разделы с признания ми. И это не потому, что мне не хватает благодарности или я считаю, что мне некого благодарить, – совсем наоборот: у меня слишком много людей, которых нужно поблагодарить, и я не знаю, с чего начать, кого перечислить по имени, а кого пропустить. Должен ли я поблагодарить своих родителей за их роль в формировании меня таким человеком, который написал эту книгу? Возможно, их родителей за то, что они сформировали моих родителей? Помню, как моя учительница в четвертом классе говорила мне, что я, должно быть, стану писателем, когда вырасту. (Я не помню ее имени и не уверен, когда я вырасту, но, возможно, она оказала некоторое влияние на эту книгу.) Я написал бóльшую часть этой книги во время поездок на Канарские острова с работой на удалении; возможно, мне следует поблагодарить пилотов, кото-
16 Предисловие рые доставили меня туда? Или электриков, которые устанавливали проводку в коворкингах? Вероятно, я должен быть благодарен Оздемиру Паше за его роль в популяризации кофе, который одновременно облегчал и отвлекал меня от писательства. И давайте не будем забывать фермеров, которые выращивали вкусную еду, которая меня поддерживала и делала счастливым. Видите, к чему это ведет: мои пальцы печатали, но потребовалась вся история человеческой цивилизации, чтобы создать меня и среду, которая позволила мне написать эту книгу – и которая позволила вам прочитать эту книгу. Таким образом, спасибо человечеству! Ну да ладно, я также могу посвятить один абзац более традиционному разделу благодарностей. Самое главное, я благодарен всем моим студентам на моих курсах в университете и летней школе с живым преподаванием, а также на моих онлайновых курсах Udemy за то, что они доверили мне преподавание у них и мотивировали меня продолжать совершенствовать свои объяснения прикладной математики и других технических тем. Я также благодарен Джесс Хаберману, редактору отдела закупок в O’Reilly, которая установила «первый контакт», чтобы спросить, не буду ли я заинтересован в написании этой книги. Шира Эванс (редактор разработки), Джонатан Оуэн (производственный редактор), Элизабет Оливер (выпускающий редактор), Кристен Браун (менеджер по контентным услугам) и два эксперта – технических рецензента сыграли непосредственную роль в трансформации моих нажатий клавиш в книгу, которую вы сейчас читаете. Уверен, что этот список неполон, потому что другие люди, которые помогли опубликовать эту книгу, мне неизвестны либо потому, что я забыл их из-за потери памяти в моем преклонном возрасте1. Благодарности всем читающим этот текст, кто чувствует, что внес хотя бы ничтожный вклад в эту книгу.
ЛОЛ, когда я написал эту книгу, мне было 42 года.
Что такое линейная алгебра и зачем ее изучать? Линейная алгебра имеет интересную историю в математике, восходящую к XVII веку на Западе и гораздо раньше в Китае. Матрицы – развернутые таблицы чисел, лежащие в основе линейной алгебры, – использовались для обеспечения компактной системы счисления с целью хранения наборов чисел, таких как геометрические координаты (это было первоначальное применение матриц Декартом), и систем уравнений (впервые введенных Гауссом). В XX веке матрицы и векторы использовались для многопеременной математики, включая математическое исчисление, дифференциальные уравнения, физику и экономику. Но до недавнего времени большинству людей не нужно было заботиться о матрицах. Однако, как оказалось, компьютеры чрезвычайно эффективны в работе с матрицами. И поэтому современные вычисления породили современную линейную алгебру. Современная линейная алгебра является вычислительной, в то время как традиционная линейная алгебра является абстрактной. Современную линейную алгебру лучше всего изучать на исходном коде и приложениях в области графики, статистики, науки о данных, искусственного интеллекта и численного моделирования, в то время как традиционная линейная алгебра изучается на доказательствах и размышлениях о бесконечномерных пространствах векторов. Современная линейная алгебра предоставляет структурные элементы, которые поддерживают почти каждый алгоритм, реализованный на компьютерах, в то время как традиционная линейная алгебра зачастую является интеллектуальной пищей для продвинутых студентов математических университетов. Добро пожаловать в современную линейную алгебру. Стоит ли вам изучать линейную алгебру? Это зависит от того, хотите ли вы понимать алгоритмы и процедуры или же просто применять методы, разработанные другими. Я не хочу принижать последнее – в сущности, нет ничего
18 Введение плохого в использовании инструментов, которые вы не понимаете (я пишу это на ноутбуке, который я могу использовать, но не смог создать его с нуля). Но, учитывая, что вы читаете книгу с таким названием в коллекции книг O’Reilly, я предполагаю, что вы (1) хотите узнать принцип работы алгоритмов либо (2) хотите разрабатывать или адаптировать вычислительные методы. Так что да, вы должны изучать линейную алгебру, и вы должны изучить ее современную версию.
Об этой книге Предназначение этой книги – научить вас современной линейной алгебре. Но речь идет не о том, чтобы запомнить несколько ключевых уравнений и пройтись по абстрактным доказательствам; цель в том, чтобы научить вас думать о матрицах, векторах и операциях на них. Вы разовьете геометрическое понимание на уровне интуиции относительно того, почему линейная алгебра такова, какая она есть. И вы поймете, как реализовывать концепции линейной алгебры в рабочем коде Python, уделяя особое внимание приложениям в области машинного обучения и науки о данных. Во многих традиционных учебниках по линейной алгебре избегаются числовые примеры в интересах обобщений, ожидается, что вы самостоятельно получите сложные доказательства, и преподается множество концепций, которые имеют мало отношения либо вообще не имеют отношения к применению или реализации на компьютерах. Я пишу эти строки не как критику – абстрактная линейная алгебра прекрасна и элегантна. Но если ваша цель – использовать линейную алгебру (и математику в целом) в качестве инструмента для понимания данных, статистики, глубокого обучения, обработки изображений и т. д., то традиционные учебники по линейной алгеб ре, возможно, покажутся досадной тратой времени, которая поставит вас в замешательство и обеспокоит, взяв под сомнение ваши потенциальные возможности в технической области. Эта книга написана с учетом того, что учащиеся занимаются самообуче нием. Возможно, у вас есть степень в области математики, инженерии или физики, но вам нужно научиться реализовывать линейные алгоритмы в рабочем коде. Или же, вполне вероятно, вы не изучали математику в университете и теперь осознаете важность линейной алгебры для вашей учебы или работы. В любом случае, эта книга является самостоятельным ресурсом; она не представляет собой исключительно дополнение к лекционному курсу (хотя ее можно было бы использовать для этой цели). Если, читая последние три абзаца, вы кивнули головой в знак согласия, то эта книга определенно для вас. Если вы хотите погрузиться в линейную алгебру поглубже, с большим количеством доказательств и разведывательной работы, то существует несколько отличных учебников, о прочтении которых вы можете подумать,
Предварительные требования 19
включая мой учебник «Линейная алгебра: теория, интуиция, исходный код (Sincxpress BV)1.
Предварительные требования Я пытался написать эту книгу для увлеченных учеников с минимальной формальной подготовкой. И тем не менее ничего никогда не изучается понастоящему с нуля.
Математика Вы должны чувствовать себя комфортно в математике уровня средней школы. Просто основы алгебры и геометрии, и ничего особенного. Для этой книги требуется абсолютно нулевой уровень в исчислении (хотя дифференциальное исчисление имеет большую важность для приложений, в которых линейная алгебра используется часто, таких как глубокое обучение и оптимизация). Но самое главное – вам нужно чувствовать себя комфортно, думая о математике, рассматривая уравнения и графики и не боясь противостоять интеллектуальным трудностям, которые возникают при изучении математики.
Отношение Линейная алгебра – это раздел математики, и, следовательно, данная книга посвящена математике. Изучение математики, в особенности во взрослом возрасте, требует определенного терпения, целеустремленности и напорис тости. Выпейте чашку кофе, сделайте глубокий вдох, уберите свой телефон в другую комнату и погрузитесь в работу. В глубине вашей головы будет звучать голос, говорящий о том, что вы слишком стары или слишком глупы, чтобы изучать продвинутую математику. Иногда этот голос будет звучать громче, а иногда мягче, но он всегда будет присутствовать. И это не только у вас – он есть у всех. Этот голос невозможно подавить или уничтожить; даже не пытайтесь. Просто примите, что некоторая неуверенность в себе – это часть человеческого бытия. Всякий раз, когда этот голос заговаривает, вам приходится доказывать, что он неправ.
Приношу извинения за бесстыдную саморекламу; обещаю, что в данной книге это единственный случай, когда я позволяю себе подобное послабление.
Программирование Данная книга посвящена линейно-алгебраическим приложениям в рабочем коде. Я написал эту книгу, ориентируясь на Python, потому что Python в настоящее время является наиболее широко используемым языком в науке о данных, машинном обучении и смежных областях. Если вы предпочитаете другие языки, такие как MATLAB, R, C или Julia, то я надеюсь, что вам будет легко переложить рабочий код Python на эти языки. Я постарался сделать код Python как можно проще, оставляя его при этом релевантным для приложений. Глава 16 содержит базовое введение в программирование на Python. Стоит ли вам просматривать эту главу? Все зависит от вашего уровня владения языком Python: Средний/продвинутый уровень (опыт программирования > 1 года) Полностью пропустите главу 16 либо, по возможности, пролистайте ее, чтобы получить представление о типе рабочего кода, который появится в остальной части книги. Некоторые знания (опыт > array([[14, 15, 16], [24, 25, 26], [34, 35, 36]])
Возможно, результат покажется запутанным и несовместимым с приведенным ранее определением сложения векторов. На самом деле в Python реализована операция, именуемая транслированием. Вы узнаете о транслировании больше чуть позже в этой главе, но я призываю вас потратить немного времени на осмысление результата и подумать о том, как он возник в результате сложения вектора-строки с вектором-столбцом. Несмотря на это, данный пример показывает, что ориентация действительно важна: два вектора можно сложить, только если они имеют одинаковую размерность и одинаковую ориентацию.
Геометрия сложения и вычитания векторов Для того чтобы сложить два вектора геометрически, надо расположить векторы так, чтобы хвост одного вектора находился в голове другого вектора. Суммируемый вектор проходит от хвоста первого вектора к голове второго (график А на рис. 2.2). Эту процедуру можно расширить, чтобы суммировать любое число векторов: надо просто уложить все векторы от хвоста к голове, и тогда сумма будет равна отрезку, идущему от первого хвоста к итоговой голове. А)
Рис. 2.2 Сумма и разность двух векторов
Геометрическое вычитание векторов немного отличается, но является одинаково элементарным: надо выровнять два вектора так, чтобы их хвос ты находились в одной и той же координате (это легко достигается, если оба вектора находятся в стандартном положении); вектор разности – это отрезок,
Операции на векторах 31
который идет от головы «отрицательного» вектора к голове «положительного» вектора (график В на рис. 2.2). Не стоит недооценивать важность геометрии вычитания векторов: она лежит в основе ортогонального разложения векторов, которое, в свою очередь, лежит в основе метода линейных наименьших квадратов, который является одним из наиболее важных приложений линейной алгебры в науке и технике.
Умножение вектора на скаляр Скаляр в линейной алгебре – это число в чистом виде, не вложенное ни в вектор, ни в матрицу. Скаляры обычно обозначаются строчными греческими буквами, такими как α или λ. Поэтому умножение вектора на скаляр обозначается, например, как βu. Умножение вектора на скаляр выполняется очень просто: надо умножить каждый элемент вектора на скаляр. Для понимания будет достаточно одного численного примера (уравнение 2.5): Уравнение 2.5. Умножение вектора на скаляр (также умножение скаляра на вектор)
Вектор нулей Вектор, состоящий из одних нулей, также именуемый вектором нулей, или нуль-вектором, обозначается жирным шрифтом, 0, и в линейной алгебре является специальным вектором. Нередко использование вектора нулей для решения задачи фактически принято называть тривиальным решением и исключать. Линейная алгебра полна утверждений типа «найти ненулевой вектор, который может решить . » или «найти нетривиальное решение для . ».
Ранее я писал, что тип данных хранящей вектор переменной иногда важен, а иногда и не важен. Умножение вектора на скаляр является примером случая, когда тип данных имеет значение: s = 2 a = [3, 4, 5] # в виде списка b = np.array(a) # в виде np-массива print(a*s) print(b*s) >> [ 3, 4, 5, 3, 4, 5 ] >> [ 6 8 10 ]
32 Векторы. Часть 1 Приведенный выше исходный код создает скаляр (переменную s) и вектор в виде списка (переменную a), затем конвертирует их в массив NumPy (переменную b). В Python звездочка перегружена, то есть ее поведение зависит от типа переменной: умножение списка на скаляр повторяет список s раз (в данном случае два раза), что определенно не является линейно-алгебраической операцией умножения скаляра на вектор. Однако когда вектор хранится в виде массива NumPy, звездочка интерпретируется как поэлементное умножение. (Вот для вас небольшое упражнение: что произойдет, если задать s = 2.0, и почему1?) Обе эти операции (повторение списка и умножение вектора на скаляр) используются в реальном программировании, поэтому об указанном различии следует помнить.
Сложение скаляра с вектором Сложение скаляра с вектором формально в линейной алгебре не определено: это два отдельных вида математических объектов, которые невозможно объединить. Однако программы числовой обработки, такие как Python, позволяют складывать скаляры с векторами, и указанная операция сравнима с умножением скаляра на вектор: скаляр прибавляется к каждому элементу вектора. Следующий ниже исходный код иллюстрирует эту идею: s = 2 v = np.array([3, 6]) s+v >> [5 8]
Геометрия умножения вектора на скаляр Почему скаляры называются «скалярами»? Это вытекает из геометрической интерпретации. Скаляры шкалируют векторы, не меняя их направления. Существует четыре эффекта умножения вектора на скаляр, которые зависят от того, является ли скаляр больше 1, между 0 и 1, в точности 0 либо отрицательным. Рисунок 2.3 иллюстрирует эту идею. Ранее я писал, что скаляры не меняют направление вектора. Но на рисунке показано, что направление вектора меняется, когда скаляр отрицательный (то есть его угол поворачивается на 180°). Возможно, это покажется противоречием, но существует интерпретация векторов, в которой они указывают вдоль бесконечно длинной линии, проходящей через начало координат и уходящей в бесконечность в обоих направлениях (в следующей главе я буду называть такую интерпретацию «одномерным подпространством»). В этом смысле «повернутый» вектор по-прежнему указывает вдоль той же самой 1
Выражение a*s выдаст ошибку, потому что повторять список можно только с использованием целых чисел; невозможно повторить список 2.72 раза!
Операции на векторах 33
бесконечной линии, и, следовательно, отрицательный скаляр не меняет направления. Указанная интерпретация важна для пространств матриц, собственных векторов и сингулярных векторов, все из которых представлены в последующих главах.
Рис. 2.3 Один и тот же вектор (черная стрелка), умноженный на разные скаляры σ (серый отрезок; для наглядности слегка сдвинут)
Умножение вектора на скаляр в сочетании со сложением векторов приводит непосредственно к усреднению векторов. Усреднение векторов – это то же самое, что усреднение чисел: просуммировать и поделить на количество чисел. Таким образом, для того чтобы усреднить два вектора, надо их сложить, а затем скалярно умножить на .5. В общем случае, для того чтобы усреднить N векторов, надо их просуммировать и скалярно умножить результат на 1/N.
Транспонирование Вы уже узнали об операции транспонирования: она конвертирует векторы-столбцы в векторы-строки и наоборот. Тут стоит дать несколько более формальное определение, которое будет обобщено на транспонирование матриц (тема в главе 5). Матрица состоит из строк и столбцов; следовательно, каждый элемент матрицы имеет индекс в формате (строка, столбец). Операция транспонирования просто меняет местами эти индексы. Она формализуется в уравнении 2.6: Уравнение 2.6. Операция транспонирования mTi, j = mj,i. Векторы имеют либо одну строку, либо один столбец, в зависимости от их ориентации. Например, шестимерный вектор-строка имеет i = 1 и индексы j от 1 до 6, тогда как шестимерный вектор-столбец имеет индексы i от 1 до 6 и j = 1. Таким образом, перемена мест индексов i, j меняет местами строки и столбцы. Вот важное правило: двойное транспонирование возвращает вектор в изначальную ориентацию. Другими словами, vTT = v. Возможно, указанное правило покажется очевидным и тривиальным, но оно является краеугольным камнем нескольких важных доказательств в науке о данных и машин-
34 Векторы. Часть 1 ном обучении, включая создание симметричных матриц ковариаций при умножении матрицы данных на ее транспонированную версию (что, в свою очередь, является причиной того, что анализ главных компонент есть ортогональный поворот пространства данных. не волнуйтесь, это предложение обретет смысл в данной книге чуть позже!).
Транслирование векторов в Python Транслирование – это операция, которая существует только в современной компьютерной линейной алгебре; это не та процедура, которую вы бы нашли в традиционном учебнике по линейной алгебре. Транслирование, по существу, означает многократное повторение операции между одним вектором и каждым элементом другого вектора. Рассмот рим следующую ниже серию уравнений1: [1 1] + [10 20]; [2 2] + [10 20]; [3 3] + [10 20]. Обратите внимание на закономерности в векторах. Приведенный выше набор уравнений можно компактно реализовать, сжав указанные закономерности в векторы [1 2 3] и [10 20], а затем оттранслировав сложение. Вот как это выглядит на Python: v = np.array([[1, 2, 3]]).T # вектор-столбец w = np.array([[10, 20]]) # вектор-строка v + w # сложение при помощи транслирования >> array([[11, 21], [12, 22], [13, 23]])
Здесь вы снова заметите важность ориентации в линейно-алгебраических операциях: попробуйте выполнить приведенный выше исходный код, изменив v на вектор-строку и w – на вектор-столбец2. Поскольку транслирование позволяет проводить эффективные и компактные вычисления, оно встречается в числовом программировании очень час то. В данной книге вы увидите несколько примеров транслирования, в том числе в разделе, посвященном кластеризации методом k-средних (глава 4). 1
Для ясности вот как данный термин трактуется в документации NumPy: термин «транслирование» (англ. broadcasting) относится к тому, как NumPy обрабатывает массивы с разным размером во время арифметических операций. С учетом определенных ограничений меньший массив «заполняется» по большему массиву, чтобы они имели совместимые очертания. – Прим. перев. 2 Python по-прежнему выполнит транслирование, но в результате вместо матрицы 2×3 получится матрица 3×2.
Модуль вектора и единичные векторы 35
Модуль вектора и единичные векторы Модуль вектора, также именуемый геометрической длиной, или нормой1, представляет собой расстояние от хвоста до головы вектора и вычисляется с использованием стандартной формулы евклидова расстояния: квадратный корень из суммы квадратов элементов вектора (см. уравнение 2.7). Модуль вектора указывается с помощью двойных вертикальных полос вокруг вектора: ||v||. Уравнение 2.7. Норма вектора
В некоторых приложениях используются квадраты модулей векторов (записываемые как ||v||2), и в этом случае член квадратного корня в правой части выпадает. Прежде чем показать исходный код Python, следует объяснить еще несколько терминологических разночтений между линейной алгеброй «на меловой доске» и линейной алгеброй на Python. В математике размерность вектора – это число элементов в этом векторе, тогда как длина – это геометрическое расстояние; на языке Python функция len() (где len – это сокращение от англ. length, длина) возвращает размерность массива, тогда как функция np.norm() возвращает геометрическую длину (модуль вектора). Во избежание путаницы в данной книге вместо термина длина я буду использовать термин модуль вектора (либо геометрическая длина): v = np.array([1, 2, 3, 7, 8, 9]) v_dim = len(v) # математическая размерность v_mag = np.linalg.norm(v) # матем. модуль, длина или норма вектора
Существуют приложения, в которых нужен вектор, геометрическая длина которого равна единице, и такой вектор называется единичным вектором. Примеры приложений включают ортогональные матрицы, матрицы поворота, собственные векторы и сингулярные векторы. Единичный вектор определяется как ||v|| = 1. Излишне говорить, что многие векторы не являются единичными векторами. (У меня возникает соблазн написать «большинство векторов не являются единичными векторами», но существует бесконечное число единичных векторов и неединичных векторов, хотя множество бесконечных неединичных векторов больше, чем множество бесконечных единичных векторов.) К счастью, любой неединичный вектор имеет ассоциированный с ним единичный вектор. Это означает, что мы можем создать единичный вектор в том же направлении, что и неединичный вектор. Создать ассоциированный единичный вектор несложно; надо просто умножить на скаляр модуля вектора, взаимообратный его норме (уравнение 2.8): 1
Англ. magnitude; син. величина вектора. – Прим. перев.
36 Векторы. Часть 1 Уравнение 2.8. Создание единичного вектора
На рис. 2.4 показано общепринятое правило обозначения единичных векторов (vˆ) в том же направлении, что и их родительский вектор v. Данный рисунок иллюстрирует эти случаи.
Рис. 2.4 Единичный вектор (серая стрелка) можно создать из неединичного вектора (черная стрелка); оба вектора имеют одинаковый угол, но разные модули
На самом деле утверждение о том, что «любой неединичный вектор имеет ассоциированный единичный вектор», не совсем верно. Существует вектор, который имеет неединичную длину и в то же время не имеет ассоциированного единичного вектора. Сможете ли вы угадать, какой это вектор1? Здесь я не показываю исходный код Python создания единичных векторов, потому что это будет одним из упражнений в конце данной главы.
Точечное произведение векторов Точечное произведение (также иногда именуемое внутренним произведением) является одной из наиболее важных операций во всей линейной алгебре. Это базовый вычислительный строительный блок, на основе которого строятся многие операции и алгоритмы, включая свертку, корреляцию, результаты преобразования Фурье, матричное умножение, извлечение линейных признаков, фильтрацию сигналов и т. д. Точечное произведение между двумя векторами можно указать несколькими способами. Я буду использовать в основном общепринятую нотацию aTb по причинам, которые станут ясны после изучения матричного умножения. В других контекстах вы могли бы увидеть a · b или �a, b�. 1
Вектор нулей имеет длину 0, но не ассоциирован с единичным вектором, поскольку у него нет направления и поскольку невозможно прошкалировать вектор нулей так, чтобы он имел ненулевую длину.
Точечное произведение векторов 37
Точечное произведение – это одно число, которое предоставляет информацию о взаимосвязи между двумя векторами. Давайте сначала сосредоточимся на алгоритме вычисления точечного произведения, а затем я расскажу, как его интерпретировать. Для вычисления точечного произведения надо перемножить соответствующие элементы двух векторов, а затем просуммировать все отдельные произведения. Другими словами, это поэлементное умножение и сумма. В уравнении 2.9 a и b являются векторами, а ai указывает на i-й элемент вектора a. Уравнение 2.9. Формула точечного произведения
По формуле можно заключить, что точечное произведение допустимо только между двумя векторами одинаковой размерности. Уравнение 2.10 показывает численный пример: Уравнение 2.10. Пример вычисления точечного произведения [1 2 3 4] · [5 6 7 8] = 1×5 + 2×6 + 3×7 + 4×8 = 5 + 12 + 21 + 32 = 70.
Расстройства от индексации Стандартная математическая система счисления и некоторые математикоориентированные программы обработки чисел, такие как MATLAB и Julia, начинают индексацию с 1 и останавливаются на N, тогда как некоторые языки программирования, такие как Python и Java, начинают индексацию с 0 и останавливаются на N – 1. Нам незачем обсуждать достоинства и ограничения каждого соглашения, хотя иногда я все-таки задумываюсь обо всех тех ошибках, которое данное несоответствие внесло в человеческую цивилизацию, но об этом различии важно помнить при переложении формул в исходный код Python.
Есть несколько способов реализации точечного произведения на Python; самый элементарный состоит в использовании функции np.dot(): v = np.array([1, 2, 3, 4]) w = np.array([5, 6, 7, 8]) np.dot(v,w)
Примечание к функции np.dot() На самом деле точечное произведение векторов в функции np.dot() не реализовано; в ней реализовано умножение матриц, которое представляет собой набор точечных произведений. Это будет иметь больше смысла после ознакомления с правилами и механизмами умножения матриц (глава 5). Если вы хотите обследовать их сейчас, то можете изменить приведенный выше исходный код, чтобы придать обоим векторам ориентацию (вдоль строки в противовес ориентации вдоль столбца). И вы обнаружите, что результат будет точечным произведением только тогда, когда первый аргумент на входе в функцию представляет собой вектор-строку, а второй аргумент – вектор-столбец.
38 Векторы. Часть 1 Вот интересное свойство точечного произведения: умножение одного вектора на скаляр шкалирует точечное произведение на то же число. Указанное свойство можно обследовать, расширив приведенный ранее исходный код: s = 10 np.dot(s*v, w)
Точечное произведение векторов v и w равно 70, а точечное произведение с использованием s*v (которое в математических обозначениях было бы записано как σvTw) равно 700. Теперь попробуйте этот пример с отрицательным скаляром, например s = –1. И вы увидите, что величина точечного произведения сохраняется, но знак меняется на противоположный. Разумеется, при s = 0 точечное произведение равно нулю. Теперь вы знаете, как вычислять точечное произведение. Каков смысл точечного произведения и как его интерпретировать? Точечное произведение можно интерпретировать как меру сходства, или соотнесенности, между двумя векторами. Представьте, что вы собрали данные о росте и весе 20 человек и сохранили эти данные в двух векторах. Вы, конечно же, ожидаете, что эти переменные будут друг с другом связаны (более высокие люди, как правило, весят больше), и поэтому можно ожидать, что точечное произведение между этими двумя векторами будет большим. С другой стороны, величина точечного произведения зависит от шкалы данных, и, стало быть, точечное произведение между данными, измеренными в граммах и сантиметрах, будет больше, чем точечное произведение между данными, измеренными в фунтах и футах. Однако указанное произвольное шкалирование можно устранить с помощью коэффициента нормализации. Нормализованное точечное произведение между двумя переменными на самом деле называется коэффициентом корреляции Пирсона, и оно является одним из наиболее важных методов анализа в науке о данных. Подробнее об этом будет в главе 4!
Точечное произведение является дистрибутивным Дистрибутивное свойство математики выглядит так: a(b + c) = ab + ac. В переложении на векторы и точечное произведение векторов это означает, что: aT(b + c) = aTb + aTc. На словах вы бы сказали, что точечное произведение суммы векторов равно сумме точечных произведений векторов. Следующий ниже исходный код Python иллюстрирует свойство дистрибутивности: a = np.array([ 0, 1, 2 ]) b = np.array([ 3, 5, 8 ])
Точечное произведение векторов 39 c = np.array([ 13,21,34 ]) # точечное произведение дистрибутивно res1 = np.dot( a, b+c ) res2 = np.dot( a,b ) + np.dot( a,c )
Два результата res1 и res2 одинаковы (с этими векторами ответ равен 110), иллюстрируя дистрибутивность точечного произведения. Обратите внимание, как математическая формула переводится в исходный код на языке Python; в математикоориентированном программировании переложение формул в исходный код является важным навыком.
Геометрия точечного произведения Существует также геометрическое определение точечного произведения, которое представляет собой произведение модулей двух векторов, шкалированных на косинус угла между ними (уравнение 2.11). Уравнение 2.11. Геометрическое определение точечного произведения векторов α = cos(θv,w)||v|| ||w||.
Уравнение 2.9 и уравнение 2.11 математически эквивалентны, но выражены в разных формах. Доказательство их эквивалентности является интересным упражнением в математическом анализе, но займет около страницы текста и основывается сперва на доказательстве других принципов, включая закон косинусов. Указанное доказательство не имеет отношения к данной книге и поэтому опущено. Обратите внимание, что модули векторов являются строго положительными числами (за исключением вектора нулей, который имеет ||0|| = 0), тогда как косинус угла может находиться в диапазоне от –1 до +1. Это означает, что знак точечного произведения полностью определяется геометрической взаимосвязью между двумя векторами. На рис. 2.5 показаны пять случаев знака точечного произведения в зависимости от угла между двумя векторами (в 2D-визуализации, но данный принцип соблюдается для более высоких измерений). Острый
Ортогональный Коллинеарный Коллинеарный
Рис. 2.5 Знак точечного произведения между двумя векторами показывает геометрическую взаимосвязь между этими векторами
40 Векторы. Часть 1 Запомните: ортогональные векторы имеют нулевое точечное произведение Некоторые учителя математики настаивают на том, что вы не должны запоминать формулы и члены, а вместо этого должны понимать процедуры и доказательства. Но давайте будем честны: запоминание – важная и неизбежная часть изучения математики. К счастью, линейная алгебра не требует чрезмерного запоминания, но есть несколько вещей, которые вам просто нужно запомнить. Вот одна из них: ортогональные векторы имеют точечное произведение, равное нулю (это утверждение соблюдается в обоих направлениях – когда точечное произведение равно нулю, то два вектора ортогональны). Таким образом, следующие утверждения являются эквивалентными: два вектора ортогональны; два вектора имеют точечное произведение, равное нулю; два вектора пересекаются под углом 90°. Повторяйте эту эквивалентность до тех пор, пока она не отпечатается в вашем мозгу навечно.
Другие умножения векторов Точечное произведение, пожалуй, является наиболее важным и наиболее часто используемым способом умножения векторов. Но есть и несколько других способов умножения векторов.
Адамарово умножение Это просто причудливый термин для поэлементного умножения. Для реализации адамарова умножения надо перемножить каждый соответствующий элемент в двух векторах. Произведение представляет собой вектор той же размерности, что и у двух сомножителей. Например:
В Python звездочка указывает на поэлементное умножение двух векторов или матриц: a = np.array([5, 4, 8, 2]) b = np.array([1, 0,.5]) a*b
Попробуйте выполнить приведенный выше исходный код Python и. о-хохо! Python выдаст сообщение об ошибке. Отыщите и исправьте ошибку. Что вы узнали об адамаровом умножении из этой ошибки? Обратитесь к сноске с ответом1. 1
Ошибка в том, что два вектора имеют разные размерности, указывая на то, что адамарово умножение определено только для двух векторов одинаковой размерности. Проблема устраняется за счет удаления одного числа из a либо добавления одного числа в b.
Другие умножения векторов 41
Адамарово умножение является удобным способом организовывать многочисленные умножения на скаляр. Например, представьте, что у вас есть данные о числе виджетов, продаваемых в разных магазинах, и цене за виджет в каждом магазине. Вы могли бы представить каждую переменную в виде вектора, а затем перемножать эти векторы по Адамару, чтобы вычислять доход виджета по каждому магазину (он отличается от суммарного дохода по всем магазинам, который будет вычислен как точечное произведение).
Внешнее произведение Внешнее произведение – это способ создания матрицы из вектора-столбца и вектора-строки. Каждая строка в матрице внешнего произведения представляет собой скаляр вектора-строки, умноженный на соответствующий элемент в векторе-столбце. Можно было бы также сказать, что каждый столбец в матрице произведения является скаляром вектора-столбца, умноженным на соответствующий элемент в векторе-строке. В главе 6 я назову эту матрицу «матрицей ранга 1», а пока об этом термине не беспокойтесь и вместо этого сосредоточьтесь на закономерности, проиллюстрированной в следующем ниже примере:
Использование букв в линейной алгебре На уроках алгебры в средней школе вы узнали, что использование букв в качестве абст рактных местозаполнителей вместо чисел позволяет понимать математику на более глубоком уровне, чем арифметика. Та же концепция используется в линейной алгебре: иногда, когда это облегчает понимание, внутри матриц учителя используют буквы вместо чисел. Буквы можно рассматривать как переменные.
Внешнее произведение сильно отличается от точечного произведения: вместо скаляра оно производит матрицу, а два вектора во внешнем произведении могут иметь разные размерности, тогда как два вектора в точечном произведении должны иметь одинаковую размерность. Внешнее произведение обозначается как vwT (вспомните, что мы исходим из того, что векторы ориентированы вдоль столбца; следовательно, внешнее произведение предусматривает умножение столбца на строку). Обратите внимание на тонкое, но важное различие между обозначениями точечного произведения (vTw) и внешнего произведения (vwT). Возможно, сейчас это покажется странным и сбивающим с толку, но обещаю, это станет совершенно понятным после того, как вы узнаете о матричном произведении в главе 5. Внешнее произведение похоже на транслирование, но это не одно и то же: транслирование – это общая операция программирования, которая исполь-
42 Векторы. Часть 1 зуется для расширения векторов в арифметических операциях, таких как сложение, умножение и деление; внешнее произведение – это специфическая математическая процедура умножения двух векторов. NumPy может вычислять внешнее произведение с помощью функции np.outer() либо функции np.dot(), при условии что два входных вектора ориентированы соответственно вдоль столбца и строки.
Перекрестное и тройное произведения Существует несколько других способов умножения векторов, таких как перекрестное произведение и тройное произведение. Эти методы используются в геометрии и физике, но не так часто встречаются в приложениях, связанных с технологиями, чтобы тратить на них время в этой книге. Я упоминаю их здесь только для того, чтобы вы были мимолетно знакомы с названиями.
Ортогональное разложение векторов «Разложить» вектор или матрицу означает разбить эту матрицу на несколько более элементарных частей. Разложение используется для выявления информации, которая «скрыта» в матрице, с целью облегчения работы с матрицей либо с целью сжатия данных. Не будет преуменьшением написать, что бóльшая часть линейной алгебры (абстрактной и практической) предусмат ривает матричные разложения. Матричные разложения – серьезная штука. Ниже я представлю концепцию разложения, используя два простых примера со скалярами: число 42.01 можно разложить на две части: 42 и .01. Возможно, .01 – это шум, который следует игнорировать, либо, вероятно, цель состоит в том, чтобы сжать данные (целое число 42 требует меньше памяти, чем 42.01 с плавающей точкой1). Независимо от мотивации, разложение предусматривает представление одного математического объекта как суммы более элементарных объектов (42 = 42 + .01); число 42 можно разложить на произведение простых чисел 2, 3 и 7. Такое разложение называется разложением на простые множители и имеет много применений в числовой обработке и криптографии. В данном примере вместо сумм предусматривается использование произведений, но суть – та же самая: разложить один математический объект на более мелкие и более элементарные части. В этом разделе мы начнем обследовать элементарное, но важное разложение, которое заключается в разбиении вектора на два отдельных вектора, 1
В переводе оставлена форма записи чисел, принятая в оригинале книги, где для отделения дробной части принято использовать точку, а для отделения групп по три числа многозначного числа принято использовать запятую. – Прим. перев.
Ортогональное разложение векторов 43
один из которых ортогонален опорному вектору, а другой параллелен этому опорному вектору. Ортогональное разложение векторов непосредственно приводит к процедуре Грама–Шмидта и QR-разложению, которое часто используется в решении обратных задач в статистике. Давайте начнем с рисунка, чтобы представить цель разложения визуально. Рисунок 2.6 иллюстрирует ситуацию: даны два вектора a и b в стандартном положении, и наша цель – найти точку на векторе a, которая находится как можно ближе к голове вектора b. Указанную задачу также можно было бы выразить как задачу оптимизации: спроецировать вектор b на вектор a таким образом, чтобы проекционное расстояние было минимизировано. Разумеется, эта точка на a будет шкалированной версией вектора a; другими словами, βa. Таким образом, теперь наша цель состоит в том, чтобы найти скаляр β. (Связь с ортогональным разложением векторов вскоре станет ясна.)
Рис. 2.6 Для того чтобы спроецировать точку в голове вектора b на вектор a с минимальным расстоянием, нужна формула вычисления β таким образом, чтобы длина проекционного вектора (b – βa) была минимальной
Важно отметить, что для определения отрезка от b до βa можно использовать вычитание векторов. Мы могли бы дать этому отрезку отдельную букву, например вектор c, но для отыскания решения необходимо вычитание. Ключевой для понимания момент, который приводит к решению этой задачи, заключается в том, что точка на a, ближайшая к голове b, отыскивается путем проведения отрезка от b, которая пересекается с a под прямым углом. Интуитивное понимание здесь достигается, если вообразить треугольник, образованный началом координат, вершиной b и βa; длина отрезка от b до βa становится больше по мере того, как угол ∡βa становится меньше 90° или больше 90°. Собрав все это вместе, мы пришли к выводу, что (b – βa) является ортогональным βa, а это то же самое, что сказать, что данные векторы перпендикулярны. И стало быть, точечное произведение между ними должно быть равно нулю. Давайте трансформируем приведенные выше слова в уравнение: aT(b – βa) = 0.
44 Векторы. Часть 1 Отсюда можно применить немного алгебры, чтобы отыскать β (обратите внимание на применение дистрибутивного свойства точечных произведений), которое показано в уравнении 2.12: Уравнение 2.12. Решение задачи ортогональной проекции aTb – βaTa = 0;
Получилось довольно красиво: мы начали с простой геометрической картинки, обследовали последствия геометрии, выразили эти последствия в виде формулы, а затем применили немного алгебры. И в результате обнаружили формулу проецирования точки на отрезок с минимальным расстоянием. Указанная проекция называется ортогональной проекцией и является основой для многих приложений в статистике и машинном обучении, включая знаменитую формулу наименьших квадратов для решения линейных моделей (вы увидите ортогональные проекции в главах 9, 10 и 11). Могу себе представить, что вы весьма заинтригованы посмотреть, как будет выглядеть исходный код Python с реализацией этой формулы. Но вам придется написать этот код самостоятельно в упражнении 2.8 в конце данной главы. Если вы не хотите ждать конца главы, то смело решайте это упражнение сейчас, а затем продолжайте изучать ортогональное разложение. Возможно, вам интересно, как это связано с ортогональным разложением векторов, то есть с названием данного раздела. Проекция с минимальным расстоянием была необходимой подготовительной работой, и теперь вы готовы усвоить разложение. Как обычно, мы начнем с постановки и цели анализа. Вначале даны два вектора, которые я назову «целевым вектором» и «опорным вектором». Цель состоит в том, чтобы разложить целевой вектор на два других вектора таким образом, чтобы: 1) эти два вектора в сумме составляли целевой вектор и 2) один вектор был ортогонален опорному вектору, в то время как другой был параллелен опорному вектору. Указанная ситуация проиллюстрирована на рис. 2.7. Прежде чем приступать к математике, давайте проясним наши термины: я обозначу целевой вектор через t и опорный вектор – через r. Далее два вектора, сформированные из целевого вектора, будут называться перпендикулярной компонентой, обозначенной через t�r, и параллельной компонентой, обозначенной через t||r. Мы начнем с определения параллельной компоненты. Каким будет вектор, параллельный вектору r? Дело в том, что любая шкалированная версия вектора r очевидным образом является параллельной вектору r. И поэтому мы находим t||r, просто применяя формулу ортогональной проекции, которую мы только что обнаружили (уравнение 2.13):
Ортогональное разложение векторов 45
Рис. 2.7 Иллюстрация ортогонального разложения векторов: разложить вектор t на сумму двух других векторов, которые ортогональны и параллельны вектору r
Уравнение 2.13. Вычисление параллельной компоненты вектора t относительно вектора r
Обратите внимание на тонкое отличие от уравнения 2.12: там мы вычисляли только скаляр β; здесь же мы хотим вычислить шкалированный вектор βr. Это и есть параллельная компонента. Как найти перпендикулярную компоненту? Ее найти проще, потому что мы уже знаем, что две компоненты вектора должны в сумме составлять изначальный целевой вектор. Таким образом: t = t�r + t||r;
Другими словами, мы вычитаем параллельную компоненту из изначального вектора, и остатком будет наша перпендикулярная компонента. Но действительно ли эта перпендикулярная компонента ортогональна опорному вектору? Да, это так! В целях доказательства вы показываете, что точечное произведение между перпендикулярной компонентой и опорным вектором равно нулю: (t�r)Tr = 0;
Работа с алгеброй указанного доказательства прямолинейна, но утомительна, поэтому я ее опустил. Вместо этого вы будете работать над укреплением интуитивного понимания в упражнениях, используя исходный код Python. Надеюсь, вам понравилось изучать ортогональное разложение векторов. Еще раз обратите внимание на общий принцип: мы разбиваем один математический объект на комбинацию других объектов. Детали разложения зависят от наших ограничений (в данном случае ортогонального и параллельного
46 Векторы. Часть 1 опорному вектору), означая, что разные ограничения (то есть разные цели анализа) могут приводить к разным разложениям одного и того же вектора.
Резюме Прелесть линейной алгебры заключается в том, что даже самые сложные и вычислительно емкие операции на матрицах состоят из элементарных операций, большинство из которых можно понять на уровне интуиции с помощью геометрии. Не стоит недооценивать важность изучения элементарных операций на векторах, потому что то, что вы узнали в этой главе, ляжет в основу остальной части книги – и остальной части вашей карьеры прикладного линейного алгебраиста (кем вы на самом деле и являетесь, если занимае тесь наукой о данных, машинным обучением, искусственным интеллектом, глубоким обучением, обработкой изображений, компьютерным зрением, статистикой и всем таким прочим). Вот наиболее важные выводы, которые следует вынести из этой главы. Вектор – это упорядоченный список чисел, который помещается в столбец либо строку. Число элементов в векторе называется его размерностью, и в геометрическом пространстве вектор можно представить в виде отрезка с числом осей, равным размерности. Несколько арифметических операций (сложение, вычитание и адамарово умножение) на векторах выполняется поэлементно. Точечное произведение – это одно число, в котором кодируется взаимосвязь между двумя векторами одинаковой размерности и которое вычисляется как поэлементное умножение и сумма. Точечное произведение равно нулю для ортогональных векторов, что геометрически означает, что векторы пересекаются под прямым углом. Ортогональное разложение векторов предусматривает разбиение вектора на сумму двух других векторов, которые ортогональны и параллельны опорному вектору. Формулу данного разложения можно снова извлечь из геометрии, но вы должны помнить фразу «отображение над модулем» как концепцию, выражаемую этой формулой.
Упражнения по программированию Надеюсь, вы не воспринимаете эти упражнения как утомительную работу, которую вам нужно проделать. Как раз наоборот, данные упражнения дают возможность отточить ваши математические навыки и навыки программирования, а также убедиться, что вы действительно понимаете материал этой главы. Хотелось бы также, чтобы вы рассматривали эти упражнения как трамплин, позволяющий вам продолжить обследовать линейную алгебру с использованием Python. Вносите изменения в исходный код, используя разные числа, разные размерности, разные ориентации и т. д. Пишите свой исход-
Упражнения по программированию 47
ный код тестирования других упомянутых в этой главе концепций. Самое главное: получайте удовольствие и учитесь использовать учебный опыт. В качестве напоминания: решения ко всем упражнениям можно просмот реть либо скачать по ссылке https://github.com/mikexcohen/LA4DataScience.
Упражнение 2.1 В онлайновом репозитории исходного кода «отсутствует» код создания рис. 2.2. (На самом деле он не отсутствует – я перенес его в решение данного упражнения.) Итак, здесь ваша цель – написать свой исходный код для создания рис. 2.2.
Упражнение 2.2 Напишите алгоритм, который вычисляет норму вектора путем переложения уравнения 2.7 в исходный код. Подтвердите, используя случайные векторы с разными размерностями и ориентациями, что вы получаете тот же результат, что и np.linalg.norm(). Данное упражнение предназначено для того, чтобы дать вам больше опыта в индексации массивов NumPy и переложении формул в исходный код; на практике нередко проще использовать np.linalg.norm().
Упражнение 2.3 Создайте функцию Python, которая на входе будет принимать вектор, а на выходе – выдавать единичный вектор в том же направлении. Что происходит при вводе вектора нулей?
Упражнение 2.4 Вы знаете, как создавать единичные векторы; что, если вы хотите создавать вектор любого произвольного модуля? Напишите функцию Python, которая на входе будет принимать вектор и желаемый модуль вектора и возвращать вектор в том же направлении, но с модулем, соответствующим второму входному аргументу.
Упражнение 2.5 Напишите цикл for для транспонирования вектора-строки в вектор-столбец без использования встроенной функции или метода, такого как np.transpose() или v.T. Данное упражнение поможет вам создавать и индексировать векторы с наделенной ориентацией.
Упражнение 2.6 Вот интересный факт: квадратная норма вектора может вычисляться как точечное произведение этого вектора на себя. Вернитесь к уравнению 2.8, чтобы убедиться в данной эквивалентности. Затем подтвердите ее с помощью Python.
Упражнение 2.7 Напишите исходный код, чтобы продемонстрировать, что точечное произведение является коммутативным. Коммутативное свойство означает a × b = b × a, что для точечного произведения векторов означает aTb = bTa. Проде-
48 Векторы. Часть 1 монстрировав это в исходном коде, примените уравнение 2.9, чтобы понять, почему точечное произведение является коммутативным.
Упражнение 2.8 Напишите исходный код создания рис. 2.6. (Обратите внимание, что если присутствуют ключевые элементы, то ваше решение не обязательно должно выглядеть в точности так, как показано на рисунке.)
Упражнение 2.9 Реализуйте ортогональное разложение векторов. Начните с двух векторов случайных чисел t и r и воспроизведите рис. 2.8 (обратите внимание, что ваш график будет выглядеть несколько иначе из-за случайных чисел). Затем подтвердите, что сумма двух компонент равна t и что t�r и t||r ортогональны.
Рис. 2.8 Упражнение 9
Упражнение 2.10 Важным навыком программирования является отыскание ошибок. Допус тим, в вашем исходном коде есть дефект, из-за которого знаменатель в проекционном скаляре уравнения 2.13 равен tTt вместо rTr (исходя из личного опыта написания этой главы, легко допустить ошибку!). Внедрите эту ошибку, чтобы проверить, действительно ли она уводит в сторону от точного исходного кода. Что можно сделать, чтобы проверить, является ли результат правильным либо неправильным? (В программировании подтверждение исходного кода известными результатами называется проверкой на исправность.)
3 Векторы. Часть 2
Предыдущая глава заложила основу для понимания векторов и базовых операций на векторах. Теперь вы расширите горизонты своих знаний в области линейной алгебры, познакомившись с набором взаимосвязанных понятий, включая линейную независимость, подпространства и базисы. Каждая из этих тем имеет решающее значение для понимания операций на матрицах. Возможно, некоторые из представленных здесь тем покажутся абстрактными и оторванными от приложений, но между ними существует очень короткий путь, например векторные подпространства и подгонка статистических моделей к данным. Приложения в области науки о данных появятся позже, поэтому, пожалуйста, продолжайте концентрировать свое внимание на основах, чтобы продвинутые темы было легче понять.
Множества векторов Мы начнем главу с чего-нибудь простого: коллекция векторов называется множеством. Вообразите, как вы кладете себе в рюкзак пучок векторов, чтобы носить его с собой. Векторные множества обозначаются курсивом и заглавными буквами, такими как S или V. Математические множества описываются следующим образом: V = . Представьте, например, набор данных о числе случаев заражения Ковидом-19, госпитализациях и смертях из ста стран; эти данные из каждой страны можно было бы сохранить в трехэлементном векторе и создать множество векторов, содержащее сто векторов. Множество векторов может содержать конечное или бесконечное число векторов. Возможно, множества векторов с бесконечным числом векторов будет звучать как бесполезно глупая абстракция, но векторные подпространства – это бесконечные множества векторов, и они имеют большое значение для подгонки статистических моделей к данным.
50 Векторы. Часть 2 Множества векторов также могут быть пустыми и обозначаются как V = <>. Вы столкнетесь с пустыми множествами векторов, когда познакомитесь с пространствами матриц.
Линейно-взвешенная комбинация Линейно-взвешенная комбинация – это способ смешивания информации из нескольких переменных, при этом некоторые переменные вносят больший вклад, чем другие. Указанную фундаментальную операцию также иногда называют линейной смесью, или взвешенной комбинацией (часть со словом линейная подразумевается). Иногда вместо слова вес используется термин «коэффициент». Линейно-взвешенная комбинация просто означает умножение векторов на скаляр и сложение: взять некоторое множество векторов, умножить каждый вектор на скаляр и сложить их, чтобы получить один вектор (уравнение 3.1). Уравнение 3.1. Линейно-взвешенная комбинация w = λ1v 1 + λ2v 2 + … + λnv n.
Подразумевается, что все векторы vi имеют одинаковую размерность; в противном случае сложение недопустимо. Скаляры λ могут быть любым действительно-значным числом, включая ноль. Технически уравнение 3.1 можно было бы переписать для вычитания векторов, но поскольку можно вычитать, задавая отрицательное значение числа λi, проще обсуждать линейно-взвешенные комбинации в терминах суммирования. Уравнение 3.2 показывает пример, помогающий сделать все это конкретнее: Уравнение 3.2. Линейно-взвешенная комбинация
Линейно-взвешенные комбинации реализуются в исходном коде очень просто, как показано ниже. В Python важен тип данных; проверьте, что произойдет, когда векторы будут списками, а не массивами NumPy1: 1
Как показано в главах 2 и 16, умножение списка на целое число повторяет список заданное число раз вместо его умножения на скаляр.
Линейная независимость 51 l1 = 1 l2 = 2 l3 = -3 v1 = np.array([ 4, 5, 1]) v2 = np.array([-4, 0,-4]) v3 = np.array([ 1, 3, 2]) l1*v1 + l2*v2 + l3*v3
Организовывать хранение каждого вектора и каждого коэффициента в виде отдельных переменных утомительно, и такой подход не масштабируется под решение более крупных задач. Поэтому на практике линейновзвешенные комбинации реализуются с помощью компактного и масштабируемого метода умножения матриц на векторы, о котором вы узнаете в главе 5; пока же основное внимание будет сконцентрировано на концепции и реализации в исходном коде. Линейно-взвешенные комбинации имеют несколько применений. Три из них таковы: предсказанные данные на выходе из статистической модели создаются путем применения линейно-взвешенной комбинации регрессоров (предсказательных переменных) и коэффициентов (скаляров), которые вычисляются с помощью алгоритма наименьших квадратов, о котором вы узнаете в главах 11 и 12; в процедурах уменьшения размерности, таких как анализ главных компонент, каждая компонента (иногда именуемая фактором, или модой) извлекается как линейно-взвешенная комбинация каналов данных, при этом веса (коэффициенты) отбираются таким образом, чтобы максимизировать дисперсию компоненты (наряду с некоторыми другими ограничениями, о которых вы узнаете в главе 15); искусственные нейронные сети (архитектура и алгоритм, приводящие в действие технологию глубокого обучения) предусматривают две операции: линейно-взвешенную комбинацию входных данных с последующим нелинейным преобразованием. Веса усваиваются алгоритмом путем минимизации стоимостной функции, которая обычно представляет собой разницу между модельным предсказанием и реальной целевой переменной. Концепция линейно-взвешенной комбинации является механизмом создания подпространств векторов и пространств матриц и занимает центральное место в линейной независимости. И действительно, линейно-взвешенная комбинация и точечное произведение являются двумя наиболее важными элементарными строительными блоками, из которых строятся многие продвинутые линейно-алгебраические вычисления.
Линейная независимость Множество векторов является линейно зависимым, если по меньшей мере один вектор в множестве можно выразить как линейно-взвешенную ком-
52 Векторы. Часть 2 бинацию других векторов в этом множестве. И следовательно, множество векторов является линейно независимым, если ни один вектор невозможно выразить как линейно-взвешенную комбинацию других векторов в этом множестве. Ниже приведены два множества векторов. Прежде чем читать текст, попробуйте определить, является ли каждое множество зависимым либо независимым. (Когда слово линейная подразумевается, термин линейная независимость иногда сокращается до одного слова независимость.)
Векторное множество V является линейно независимым: невозможно выразить один вектор в множестве как линейное кратное другого вектора в множестве. То есть если обозначить векторы в множестве через v1 и v2, то не существует возможного скаляра λ, для которого v1 = λv2. Что скажете насчет множества S? Оно является зависимым, потому что мы можем применить линейно-взвешенные комбинации некоторых векторов в множестве и получить другие векторы в множестве. Существует бесконечное число таких комбинаций, две из которых – s1 = .5*s*2 и s2 = 2*s*1. Давайте попробуем еще один пример. Опять же, вопрос заключается в том, является ли множество T линейно независимым либо линейно зависимым:
А вот тут разобраться намного сложнее, чем в двух предыдущих примерах. Оказывается, что это линейно зависимое множество (например, сумма первых трех векторов равна удвоенному четвертому вектору). Но я и не жду, что вы сможете понять это в результате визуального осмотра. Так как же определять линейную независимость на практике? Линейная независимость определяется путем создания матрицы из множества векторов, вычисления ранга матрицы и сравнения ранга с наименьшим числом из числа строк или столбцов. Возможно, это предложение сейчас не будет иметь для вас смысла, потому что вы еще не познакомились с рангом мат рицы. Поэтому сейчас сосредоточьте свое внимание на концепции, что множество векторов является линейно зависимым, если по меньшей мере один вектор в указанном множестве можно выразить как линейно-взвешенную комбинацию других векторов в данном множестве, и множество векторов является линейно независимым, если ни один вектор невозможно выразить как комбинацию других векторов.
Линейная независимость 53
Независимые множества Независимость – это свойство множества векторов. То есть множество векторов может быть линейно независимым либо линейно зависимым; независимость не является свойством отдельного вектора внутри множества.
Математика линейной независимости Теперь, когда вы понимаете эту концепцию, я хочу убедиться, что вы также понимаете формальное математическое определение линейной зависимости, которое выражено в уравнении 3.3. Уравнение 3.3. Линейная зависимость1 0 = λ1v1 + λ2v2 + … + λnvn, λ Î ℝ.
Это уравнение говорит, что линейная зависимость означает существование возможности определить некоторую линейно-взвешенную комбинацию векторов в множестве, такую чтобы получить вектор нулей. Если есть возможность найти несколько скаляров λ, которые делают уравнение истинным, то множество векторов является линейно зависимым. И наоборот, если нет возможного способа линейно скомбинировать векторы так, чтобы получить векторы нулей, то множество является линейно независимым. Вероятно, поначалу это покажется не поддающимся интуитивному пониманию. Почему нас волнует вектор нулей, когда рассматривается вопрос о возможности выразить хотя бы один вектор в множестве через взвешенную комбинацию других векторов в этом множестве? Вероятно, вы бы предпочли переписать определение линейной зависимости следующим образом: λ1v1 = λ2v2 + … + λnvn, λ Î ℝ.
Почему бы не начать именно с этого уравнения, а не помещать вектор нулей в левую часть? Приравнивание уравнения к нулю помогает укрепить принцип, согласно которому все множество целиком является зависимым либо независимым; ни один отдельный вектор не имеет привилегированного положения в качестве «зависимого вектора» (см. «Независимые множества» на данной странице). Другими словами, когда речь заходит о независимости, множества векторов становятся исключительно эгалитарными. Но погодите. Тщательный анализ уравнения 3.3 показывает тривиальное решение: приравнять все скаляры λ к нулю, и уравнение будет выглядеть как 0 = 0, независимо от векторов в множестве. Однако, как я написал в главе 2, в линейной алгебре содержащие нули тривиальные решения нередко игнорируются. Таким образом, мы добавляем ограничение, что по меньшей мере один λ ≠ 0. 1
Это уравнение представляет собой применение линейно-взвешенной комбинации!
54 Векторы. Часть 2 Это ограничение можно встроить в уравнение путем деления на один из скаляров; имейте в виду, что v1 и λ1 могут относиться к любой векторно-скалярной паре в множестве: λ Î ℝ, λ1 ≠ 0.
Независимость и вектор нулей Говоря по-простому, любое множество векторов, включающее вектор нулей, автоматически является линейно зависимым множеством. И вот почему: любое скалярное кратное вектору нулей по-прежнему остается вектором нулей, поэтому определение линейной зависимости всегда соблюдается. Это можно увидеть в следующем ниже уравнении: λ00 = 0v1 + 0v2 + 0vn.
До тех пор, пока λ0 ≠ 0, мы имеем нетривиальное решение, и множество соответствует определению линейной зависимости. Как насчет нелинейной независимости? – Но, Майк, – я представляю, как вы протестуете, – разве жизнь, Вселенная и все остальное не нелинейны? Полагаю, было бы интересным упражнением подсчитать суммарное число линейных взаимодействий во Вселенной относительно числа нелинейных и посмотреть, какая сумма больше. Но линейная алгебра всецело касается, ну, вы поняли, линейных операций. Если один вектор можно выразить как нелинейную (а не линейную) комбинацию других векторов, то эти векторы все равно будут формировать линейно независимое множество. Причина ограничения линейности заключается в том, что мы хотим выражать преобразования как умножение матриц, которое является линейной операцией. Это говорится не для того, чтобы бросить тень на нелинейные операции – в моей воображаемой беседе вы красноречиво заявили, что чисто линейная Вселенная была бы довольно скучной и предсказуемой. Но нам вовсе не нужно объяснять всю Вселенную с помощью линейной алгебры; линейная алгебра нам нужна только для линейных частей. (Тут также стоит упомянуть, что многие нелинейные системы хорошо аппроксимируются с помощью линейных функций.)
Подпространство и охват Когда я вводил понятие линейно-взвешенных комбинаций, я приводил примеры с конкретными числовыми значениями весов (например, λ1 = 1, λ3 = –3). Подпространство – это та же идея, но с использованием бесконечности возможных способов линейного комбинирования векторов в множестве. То есть для некоторого (конечного) множества векторов бесконечное число способов их линейного комбинирования – с использованием одних и тех
Подпространство и охват 55
же векторов, но разных числовых значений весов – создает подпространство векторов. А механизм комбинирования всех возможных линейно-взвешенных комбинаций называется охватом множества векторов1. Давайте разберем несколько примеров. Мы начнем с элементарного примера множества векторов, содержащего один вектор:
Охватом данного множества векторов является бесконечность векторов, которые могут быть созданы как линейные комбинации векторов в множестве. Для множества с одним вектором это просто означает все возможные шкалированные версии данного вектора. На рис. 3.1 показаны вектор и подпространство, которое он охватывает. Учтите, что любой вектор в серой пунктирной линии можно сформировать как некую шкалированную версию вектора.
Рис. 3.1 Вектор (черным цветом) и подпространство, которое он охватывает (серым цветом)
Нашим следующим примером является множество из двух векторов в ℝ3:
Векторы находятся в ℝ3, поэтому они графически представлены на трехмерной оси. Но подпространство, которое они охватывают, представляет собой двумерную плоскость в этом трехмерном пространстве (рис. 3.2). Указанная плоскость проходит через начало координат, потому что шкалирование обоих векторов на ноль дает вектор нулей.
Также называется линейной оболочкой (англ. linear hull), однако используемый в книге термин охват (англ. span) является более релевантным по причинам, которые будут ясны далее в этой главе. – Прим. перев.
56 Векторы. Часть 2
Рис. 3.2 Два вектора (черным цветом) и подпространство, которое они охватывают (серым цветом)
В первом примере был один вектор, и его охватом было одномерное подпространство, а во втором примере было два вектора, и их охватом было двумерное подпространство. Кажется, вырисовывается некая закономерность, но внешность бывает обманчивой. Рассмотрим следующий пример:
Два вектора в ℝ3, но подпространство, которое они охватывают, попрежнему является всего лишь одномерным подпространством – отрезком (рис. 3.3). Почему так? А все потому, что один вектор в множестве уже находится в зоне охвата другим вектором. И поэтому, с точки зрения охвата, один из двух векторов является избыточным.
Рис. 3.3 Одномерное подпространство (серым цветом), охватываемое двумя векторами (черным цветом)
Так какова же взаимосвязь между размерностью охватываемого подпространства и числом векторов в множестве? Вероятно, вы уже догадались, что это как-то связано с линейной независимостью.
Размерность подпространства, охватываемого множеством векторов, – это наименьшее число векторов, образующих линейно независимое множество. Если множество векторов является линейно независимым, то размерность подпространства, охватываемого векторами в этом множестве, равна числу векторов в этом множестве. Если множество является зависимым, то размерность подпространства, охватываемого этими векторами, с необходимостью меньше числа векторов в этом множестве. Насколько именно меньше, это другой вопрос – для того чтобы знать взаимосвязь между числом векторов в множестве и размерностью охватываемого ими подпространства, вам нужно понимать ранг матрицы, о котором вы узнаете в главе 6. Формальное определение подпространства векторов таково: это подмножество, которое замкнуто при сложении и умножении на скаляр и включает начало пространства1. Это означает, что любая линейно-взвешенная комбинация векторов в подпространстве также должна находиться в одном и том же подпространстве, включая установку всех весов равными нулю, чтобы произвести вектор нулей в начале пространства. Пожалуйста, не теряйте сон, размышляя о том, что значит быть «замкнутым при сложении и умножении на скаляр»; просто запомните, что подпространство векторов создается из всех возможных линейных комбинаций множества векторов.
В чем разница между подпространством и охватом? Многих студентов смущает разница между охватом и подпространством. И это понятно, потому что данные понятия тесно связаны и нередко относятся к одному и тому же. Я объясню разницу между ними, но не придавайте значения тонкостям – охват и подпространство так часто относятся к идентичным математическим объектам, что использовать эти термины взаимозаменяемо, как правило, совершенно правильно. Я нахожу, что термин охват лучше использовать в глагольной форме, а термин подпространство – разумеется, как существительное, и это помогает понять их различие: мно жество векторов охватывает, и результатом охвата является подпространство. Теперь учтите, что подпространство может быть меньшей частью большего пространства, как вы увидели на рис. 3.3. Подытоживая все сказанное: охват – это механизм создания подпространства. (С другой стороны, при использовании охвата в качестве существительного охват и подпространство относятся к одному и тому же бесконечному множеству векторов.)
Базис Как далеко друг от друга находятся Амстердам и Тенерифе? Примерно 2000. Что означает «2000»? Это число имеет смысл только в том случае, если добавить базисную единицу. Базис подобен линейке для измерения пространства. В приведенном выше примере единицей измерения является миля. Таким образом, наше базисное измерение расстояния между Голландией и Испани1
Син. начало координат пространства. – Прим. перев.
58 Векторы. Часть 2 ей составляет 1 милю. Конечно же, мы могли бы использовать и другие единицы измерения, такие как нанометры или световые годы, но думаю, можно согласиться с тем, что миля является удобным базисом для определения расстояния по этой шкале. Как насчет длины, которую ваш ноготь отрастает за один день, – должны ли мы по-прежнему использовать мили? В техническом плане это возможно, но, думаю, можно согласиться с тем, что миллиметр будет более удобной базисной единицей. Для ясности: величина, на которую вырос ваш ноготь за последние 24 часа, будет одинаковой, независимо от того, в чем вы ее измеряете: в нанометрах, милях или световых годах. Но разные единицы измерения более или менее удобны для разных задач. Вернемся к линейной алгебре: базис – это множество линеек, которые вы используете для описания информации в матрице (например, матрице данных). Как и в приведенных выше примерах, одни и те же данные можно описывать, используя разные линейки, но некоторые линейки более удобны для решения определенных задач, чем другие. Наиболее распространенным базисным множеством является декартова ось: знакомая плоскость XY, которую вы используете с начальной школы. Базисные множества для двумерного и трехмерного декартовых графиков можно записать следующим образом:
Обратите внимание, что декартовы базисные множества содержат векторы, которые являются взаимно ортогональными и имеют единичную длину. Это замечательные свойства, и именно по этой причине декартовы базисные множества столь распространены (и действительно, они называются стандартным базисным множеством). Но это не единственные базисные множества. Следующее ниже множество является другим базисным множеством для ℝ2:
Базисные множества S2 и T оба охватывают одно и то же подпространство (все из ℝ2). Почему вы бы предпочли T, а не S? Представим, что мы хотим описать точки данных p и q на рис. 3.4. Указанные точки данных можно описать как их отношение к началу координат – то есть их координаты, – используя базис S либо базис T. В базисе S эти две координаты равны p = (3, 1) и q = (–6, 2). В линейной алгебре мы говорим, что точки выражаются как линейные комбинации базисных векторов. В данном случае эта комбинация равна 3s1 + 1s2 для точки p и –6s1 + 2s2 для точки q. Теперь давайте опишем эти точки в базисе T. В качестве координат мы имеем p = (1, 0) и q = (0, 2). И в терминах базисных векторов мы имеем 1t1 + 0t2 для точки p и 0t1 + 2t2 для точки q (другими словами, p = t1 и q = 2t2). Опять
же, точки данных p и q одинаковы независимо от базисного множества, но T обеспечил компактное и ортогональное описание. Базисы имеют чрезвычайную важность в науке о данных и машинном обучении. Собственно говоря, многие задачи прикладной линейной алгебры концептуализируются как отыскание наилучшего множества базисных векторов с целью описания некоторого подпространства. Вы, вероятно, слышали о следующих терминах: уменьшение размерности, извлечение признаков, анализ главных компонент, анализ независимых компонент, факторный анализ, сингулярное разложение, линейный дискриминантный анализ, аппроксимация изображений, сжатие данных. Хотите верьте, хотите нет, но все эти методы анализа, по сути, являются способами определения оптимальных базисных векторов для конкретной задачи.
Рис. 3.4 Те же самые точки (p и q) могут быть описаны базисным множеством S (черные сплошные линии) либо T (черные пунктирные линии)
Рассмотрим рис. 3.5: это набор данных, состоящий из двух переменных (каждая точка представляет точку данных). На рисунке фактически показаны три четко различимых базиса: «стандартное базисное множество», соответствующее отрезкам x = 0 и y = 0, и базисные множества, определенные с помощью анализа главных компонент (PCA1; левый график) и с помощью анализа независимых компонент (ICA2; правый график). Какое из этих базисных множеств обеспечивает «наилучший» способ описания данных? У вас, возможно, возникнет соблазн сказать, что базисные векторы, вычисленные 1 2
Англ. Principal Components Analysis. – Прим. перев. Англ. Independent Components Analysis. – Прим. перев.
60 Векторы. Часть 2 с помощью ICA, будут наилучшими. Истина же будет посложнее (как это обычно и бывает): ни одно базисное множество не является, по сути, лучше или хуже; для конкретных задач разные базисные множества могут быть более или менее полезными в зависимости от целей анализа, особенностей данных, налагаемых анализом ограничений и т. д. Базисные векторы PCA
Базисные векторы ICA
Рис. 3.5 Двумерный набор данных с использованием разных базисных векторов (черные линии)
Определение базиса Разобравшись со смыслом понятий базиса и базисного множества, формальное определение станет простым. В сущности, базис – это просто комбинация охвата и независимости: множество векторов может быть базисом для некоторого подпространства, если оно 1) охватывает это подпространство и 2) является независимым множеством векторов. Базис должен охватывать подпространство, чтобы его использовать в качест ве базиса для этого подпространства, потому что невозможно описать то, что невозможно измерить1. На рис. 3.6 показан пример точки за пределами одномерного подпространства. Базисный вектор для этого подпространства не может измерить точку r. Черный вектор по-прежнему является допустимым базисным вектором для подпространства, которое он охватывает, но он не формирует базиса для любого подпространства за пределами того, что он охватывает. Таким образом, базис должен охватывать пространство, для которого он используется. Это понятно. Но почему базисное множество требует линейной независимости? Причина в том, что любой данный вектор в подпространстве должен иметь уникальную координату, используя этот базис. Давайте пред1
В науке это общепризнанная азбучная истина.
ставим, что мы описываем точку p из рис. 3.4, используя следующее ниже множество векторов:
Рис. 3.6 Базисное множество может измерять только то, что содержится внутри его охвата
U – это совершенно допустимое множество векторов, но оно определенно не является базисным множеством. Почему1? Какая линейно-взвешенная комбинация описывает точку p в множест ве U? Дело в том, что коэффициентами линейно-взвешенной комбинации трех векторов в U могут быть (3, 0, 1) либо (0, 1,5, 1), либо . триллионы других возможностей. Такая ситуация сбивает с толку, и поэтому математики решили, что в пределах базисного множества вектор должен иметь уникальные координаты. А линейная независимость гарантирует такую уникальность. Для ясности, точку p (или любую другую точку) можно описать с использованием бесконечного числа базисных множеств. Таким образом, результат измерения не будет уникальным с точки зрения громадного числа возможных базисных множеств. Но в пределах базисного множества точка определяется ровно одной линейно-взвешенной комбинацией. То же самое и с моей аналогией по поводу расстояния в начале этого раздела: расстояние от Амстердама до Тенерифе можно измерить, используя много разных единиц измерения, но это расстояние имеет только одно значение в расчете на одну единицу измерения. Расстояние не составляет одновременно 3200 миль и 2000 миль, но оно составляет одновременно 3200 километров и 2000 миль. (Примечание для ботаников: я здесь аппроксимирую, хорошо?)
Резюме Поздравляю с завершением еще одной главы! (Ну, почти завершением: еще нужно решить несколько упражнений по программированию.) Смысл этой 1
Потому что оно является линейно зависимым множеством.
62 Векторы. Часть 2 главы состоял в том, чтобы вывести ваши фундаментальные знания о векторах на новый уровень. Ниже приведен список ключевых выводов, но, пожалуйста, помните, что в основе всех этих выводов лежит очень мало элементарных принципов, в первую очередь линейно-взвешенных комбинаций векторов. Множество векторов – это коллекция векторов. В множестве может быть конечное либо бесконечное число векторов. Линейно-взвешенная комбинация означает умножение на скаляр и сложение векторов в множестве. Линейно-взвешенная комбинация является одним из наиболее важных понятий в линейной алгебре. Множество векторов является линейно зависимым, если вектор в множестве можно выразить как линейно-взвешенную комбинацию других векторов в множестве. И множество является линейно независимым, если такой линейно-взвешенной комбинации не существует. Подпространство – это бесконечное множество всех возможных линейно-взвешенных комбинаций множества векторов. Базис – это линейка для измерения пространства. Множество векторов может быть базисом для подпространства, если оно 1) охватывает это подпространство и 2) является линейно независимым. Одной из главнейших целей в науке о данных является отыскание наилучшего базисного множества, чтобы описывать наборы данных или решать задачи.
Упражнения по программированию Упражнение 3.1 Перепишите исходный код линейно-взвешенной комбинации, но поместите скаляры в список, а векторы – в качестве элементов в списке (таким образом, у вас будет два списка, один из скаляров и один из массивов NumPy). Затем примените цикл for, чтобы реализовать операцию линейно-взвешенной комбинации. Инициализируйте выходной вектор функцией np.zeros(). Подтвердите, что вы получаете тот же результат, что и в приведенном ранее исходном коде. Упражнение 3.2 Хотя метод прокручивания списков из предыдущего упражнения в цикле не так эффективен, как умножение матриц на векторы, он более масштабируем, чем без цикла for. Этот факт можно обследовать, добавив в качестве элементов списков дополнительные скаляры и векторы. Что произойдет, если новый добавленный вектор находится в ℝ4, а не в ℝ3? А что произойдет, если у вас будет больше скаляров, чем векторов? Упражнение 3.3 В этом упражнении вы будете извлекать случайные точки в подпространствах. Благодаря этому упражнению вы укрепите идею о том, что подпространства содержат любую линейно-взвешенную комбинацию охватываю-
Упражнения по программированию 63
щих векторов. Определите множество векторов, содержащее один вектор [1, 3]. Затем создайте 100 чисел, выбранных случайным образом из равномерного распределения между –4 и +4. Это ваши случайные скаляры. Умножьте случайные скаляры на базисный вектор, чтобы создать 100 случайных точек в подпространстве. Нанесите эти точки на график. Затем повторите процедуру, но используя два вектора в ℝ3: [3, 5, 1] и [0, 2, 2]. Обратите внимание, что для 100 точек и двух векторов вам нужно 100×2 случайных скаляров. Результирующие случайные точки будут находиться на плоскости. На рис. 3.7 показано, как будут выглядеть результаты (по рисунку не ясно, что точки лежат на плоскости, но вы увидите это, когда будете перетаскивать график на экране). Рекомендую для рисования точек использовать библиотеку plotly, чтобы иметь возможность кликать и перетаскивать трехмерную ось по всему графику. Вот подсказка, как сделать так, чтобы все работало1: import plotly.graph_objects as go fig = go.Figure( data=[go.Scatter3d( x=points[:,0], y=points[:,1], z=points[:,2], mode=’markers’ )]) fig.show()
Наконец, повторите случай ℝ3, но задайте второй вектор как 1/2 от первого. А)
Рис. 3.7 Упражнение 3.3
Указанный выше исходный код реализован в рамках среды Colab. При работе в блокноте Jupyter локально в самом начале следует добавить две следующие строки кода: import plotly.io as pio pio.renderers.default = ‘iframe’ – Прим. перев.
4 Применения векторов
Работая с предыдущими двумя главами, вы, возможно, ощущали, что часть материала была эзотерической и абстрактной. Вероятно, вы чувствовали, что задача изучения линейной алгебры не окупится пониманием приложений, реально существующих в области науки о данных и машинного обучения. Надеюсь, что данная глава эти сомнения у вас развеет. В этой главе вы узнаете, как векторы и векторные операции используются в методах анализа в рамках науки о данных. И вы сможете расширить эти знания, выполнив упражнения.
Корреляция и косинусное сходство Корреляция представляет собой один из наиболее фундаментальных и важных методов анализа в статистике и машинном обучении. Коэффициент корреляции – это одно число, которое количественно описывает линейную взаимосвязь между двумя переменными. Коэффициенты корреляции варьи руются от –1 до +1, причем –1 указывает на идеальную отрицательную взаи мосвязь, +1 – на идеальную положительную взаимосвязь, а 0 указывает на отсутствие линейной взаимосвязи. На рис. 4.1 показано несколько примеров пар переменных и их коэффициентов корреляции. В главе 2 я упоминал, что точечное произведение участвует в коэффициенте корреляции и что величина точечного произведения связана с величиной числовых значений в данных (вспомните обсуждение темы использования граммов вместо фунтов для измерения веса). Следовательно, коэффициент корреляции требует некоторой нормализации, чтобы он находился в ожидаемом диапазоне от –1 до +1. Эти две нормализации таковы: Центрировать каждую переменную по среднему значению Центрирование по среднему значению означает вычитание среднего значения из каждого значения данных. Разделить точечное произведение на произведение векторных норм Это делящая нормализация, которая отменяет единицы измерения и шкалирует максимально возможную величину корреляции в |1|.
Корреляция и косинусное сходство 65
Рис. 4.1 Примеры данных, демонстрирующих положительную корреляцию, отрицательную корреляцию и нулевую корреляцию. Нижняя правая панель иллюстрирует, что корреляция является линейной мерой; нелинейные взаимосвязи между переменными могут существовать, даже если их корреляция равна нулю
Уравнение 4.1 показывает полную формулу коэффициента корреляции Пирсона. Уравнение 4.1. Формула коэффициента корреляции Пирсона
Возможно, тут не так очевидно, что корреляция представляет собой не что иное, как три точечных произведения. Уравнение 4.2 показывает эту же формулу, переписанную с использованием обозначения линейно-алгебраического точечного произведения. В этом уравнении — x является среднецент рированной версией x (то есть переменной x с примененной нормализацией № 1).
66 Применения векторов Уравнение 4.2. Корреляция Пирсона, выраженная на языке линейной алгебры
Так что вот так: знаменитый и широко используемый коэффициент корреляции Пирсона – это просто точечное произведение между двумя переменными, нормированное векторными модулями переменных. (Кстати, по этой формуле также можно заметить, что если переменные единично нормированы таким образом, что ||x|| = ||y|| = 1, то их корреляция равна их точечному произведению. ) (Вспомним из упражнения 2.6, что Корреляция – это не единственный способ оценивать сходство между двумя переменными. Еще один метод называется косинусным сходством. Формула косинусного сходства представляет собой просто геометрическую формулу точечного произведения (уравнение 2.11), решаемую для косинусного члена:
где α – это точечное произведение между x и y. Может показаться, что корреляция и косинусное сходство – это совершенно одна и та же формула. Однако следует запомнить, что уравнение 4.1 является полной формулой, тогда как уравнение 4.2 является упрощением в рамках допущения, что переменные уже были центрированы по среднему значению. Отсюда косинусное сходство не содержит первый фактор нормализации.
Корреляция по сравнению с косинусным сходством Что означает «связанность» двух переменных между собой? Корреляция Пирсона и косинусное сходство могут давать разные результаты для одних и тех же данных, поскольку они исходят из разных допущений. По мнению Пирсона, переменные [0, 1, 2, 3] и [100, 101, 102, 103] идеально коррелируют (ρ = 1), поскольку изменения в одной переменной точно отражаются в другой переменной, и не имеет значения, что одна переменная имеет более крупные числовые значения. Однако косинусное сходство между этими переменными равно .808 – они не находятся на одной числовой шкале и, следовательно, связаны не идеально. Ни одна мера не является ни неправильной, ни наилучшей, чем другая; просто разные статистические методы принимают разные допущения о данных, и эти допущения влияют на результаты – и на правильную интерпретацию. У вас будет возможность обследовать этот момент в упражнении 4.2.
Из данного раздела можно понять, почему корреляция Пирсона и косинусное сходство отражают линейную зависимость между двумя переменными: они основаны на точечном произведении, а точечное произведение является линейной операцией.
Фильтрация временных рядов и обнаружение признаков 67
С этим разделом связаны четыре упражнения по программированию, которые приведены в конце главы. Вы можете выбрать, когда их решать: перед чтением следующего раздела либо продолжать чтение остальной части главы, а затем проработать упражнения. (Моя личная рекомендация относится к первому, но вы являетесь хозяином своей судьбы в линейной алгебре!)
Фильтрация временных рядов и обнаружение признаков Точечное произведение также используется в фильтрации временных рядов. Фильтрация – это, по сути, метод обнаружения признаков, при котором шаблон – именуемый на языке фильтрации вычислительным ядром – сопоставляется с частями сигнала временного ряда, и результатом фильтрации является еще один временной ряд, который показывает, насколько характеристики сигнала соответствуют характеристикам ядра. Ядра тщательно конструируются, чтобы оптимизировать те или иные критерии, такие как плавные колебания, острые пики, определенные контуры волновых форм и т. д. Механизм фильтрации заключается в вычислении точечного произведения между ядром и сигналом временного ряда. Но фильтрация обычно требует локального обнаружения признаков, и ядро обычно намного короче, чем весь временной ряд. Поэтому мы вычисляем точечное произведение между ядром и коротким фрагментом данных той же длины, что и ядро. Такая процедура создает одну временную точку в отфильтрованном сигнале (рис. 4.2),
Рис. 4.2 Иллюстрация фильтрации временного ряда
68 Применения векторов а затем ядро перемещается на один временной шаг вправо, чтобы вычислить точечное произведение с другим (накладывающимся) сегментом сигнала. Формально эта процедура называется сверткой и предусматривает несколько дополнительных шагов, которые я опускаю, чтобы сосредоточиться на применении точечного произведения в обработке сигналов. Темпоральная фильтрация является важной темой в науке и технике. И действительно, без темпоральной фильтрации не было бы музыки, радио, телекоммуникаций, спутников и т. д. И все же математическим сердцем, которое заставляет вашу музыку пульсировать, является точечное произведение векторов. В упражнениях в конце главы вы узнаете, как точечные произведения используются для обнаружения признаков (резких изменений) и сглаживания данных временных рядов.
Кластеризация методом k-средних Кластеризация методом k-средних – это неконтролируемый метод классифицирования многопеременных данных на относительно малое число групп, или категорий, основываясь на минимизации расстояния до центра группы. Кластеризация методом k-средних является важным методом анализа в машинном обучении, и существуют самые изощренные варианты кластеризации методом -средних. Здесь мы реализуем простую версию алгоритма -средних с целью увидеть, как понятия о векторах (в частности, векторах, векторных нормах и транслировании) используются в алгоритме k-средних. Вот краткое описание алгоритма, который мы напишем. 1. Инициализировать k центроидов как случайные точки в пространстве данных. Каждый центроид является классом или категорией, и на следующих шагах каждое наблюдение данных будет относиться к каждому классу. (Центроид – это центр, обобщенный на любое число измерений1.) 2. Вычислить евклидово расстояние между каждым наблюдением данных и каждым центроидом2. 3. Отнести каждое наблюдение данных к группе с ближайшим цент роидом. 4. Обновить каждый центроид как среднее значение всех наблюдений данных, назначенных этому центроиду. 5. Повторять шаги 2–4 до тех пор, пока не будет удовлетворен критерий схождения либо в течение N итераций. Если вам удобно программировать на Python и вы хотели бы реализовать этот алгоритм, то рекомендую это сделать сейчас, прежде чем продолжать. 1 2
Центроид также иногда именуется центром тяжести. – Прим. перев. Напоминание: евклидово расстояние – это квадратный корень из суммы квадратов расстояний от точки наблюдения данных до центроида.
Кластеризация методом k-средних 69
Далее мы проработаем математику и исходный код по каждому указанному шагу, уделяя особое внимание использованию векторов и транслированию в NumPy. Мы также протестируем указанный алгоритм, используя случайно сгенерированные двумерные данные, чтобы подтвердить правильность исходного кода. Давайте начнем с шага 1: инициализировать центроиды k случайных кластеров. k – это параметр кластеризации методом k-средних; в реальных данных определить оптимальный параметр k довольно трудно, но здесь мы зададим k = 3. Инициализировать центроиды случайных кластеров можно несколькими способами; в целях упрощения задачи в качестве центроидов я случайно возьму k выборок данных. Данные содержатся в переменной data (эта переменная имеет размер 150×2, что соответствует 150 наблюдениям по 2 признака) и визуализируются на верхней левой панели рис. 4.3 (онлайновый исходный код показывает, как эти данные генерируются): k=3 ridx = np.random.choice(range(len(data)),k,replace=False) centroids = data[ridx,:] # матрица данных содержит образцы по признакам
Теперь перейдем к шагу 2: вычислить расстояние между каждым наблюдением данных и центроидом каждого кластера. Здесь мы используем линейно-алгебраические концепции, которые вы усвоили в предыдущих главах. Для одного наблюдения данных и центроида евклидово расстояние вычисляется следующим образом:
где δi,j указывает на расстояние от наблюдения данных i до центроида j, dix – это признак x-го наблюдения данных, а cjx – координата по оси x для центроида j. Возможно, вы думаете, что этот шаг должен быть реализован с использованием двойного цикла for: один цикл по k центроидам и второй цикл по N наблюдениям данных (вероятно, вы даже подумали о третьем цикле for по признакам данных). Однако тут можно применить векторы и транслирование, сделав эту операцию компактной и эффективной. Это пример того, как линейная алгебра нередко выглядит иначе в уравнениях по сравнению с исходным кодом: dists = np.zeros((data.shape[0], k)) for ci in range(k): dists[:,ci] = np.sum((data-centroids[ci,:])**2, axis=1)
Давайте подумаем о размерах этих переменных: data имеет размер 150×2 (наблюдения по числу признаков), а размер centroids[ci,:] равен 1×2 (клас тер ci по числу признаков). В формальном плане вычесть эти два вектора невозможно. Однако в Python реализована операция транслирования, которая будет работать путем повтора центроидов кластеров 150 раз и, следова-
70 Применения векторов тельно, вычитая центроид из каждого наблюдения данных. Операция возведения в степень ** применяется поэлементно, и входной аргумент axis=1 говорит Python о том, что нужно суммировать по столбцам (отдельно по каждой строке). Таким образом, результатом функции np.sum() будет массив размером 150×1, в котором кодируется евклидово расстояние каждой точки до центроида ci. Найдите минутку, чтобы сравнить этот исходный код с формулой расстоя ния. Действительно ли они одинаковые? На самом деле это не так: квад ратный корень из евклидова расстояния в коде отсутствует. И что, значит, исходный код неправильный? Сделайте небольшую паузу и подумайте об этом; я дам подробный ответ чуть позже. Шаг 3 состоит в отнесении каждого наблюдения данных к группе с минимальным расстоянием. Этот шаг в Python довольно компактен и реализуется одной функцией: groupidx = np.argmin(dists, axis=1)
Обратите внимание на разницу между функцией np.min, которая возвращает минимальное значение, и функцией np.argmin, которая возвращает индекс, при котором происходит минимум. Теперь можно вернуться к несоответствию между формулой расстояния и ее реализацией в исходном коде. В данном алгоритме k-средних используется расстояние, чтобы относить каждую точку данных к ближайшему к ней центроиду. Расстояние и квадрат расстояния монотонно связаны, поэтому обе метрики дают один и тот же ответ. Добавление операции квадратного корня увеличивает сложность исходного кода и время вычислений без влия ния на результаты, поэтому ее можно просто опустить. Шаг 4 заключается в перевычислении центроидов как среднего значения всех точек данных внутри класса. Здесь можно прокрутить k кластеров в цикле и использовать индексацию Python, чтобы найти все точки данных, отнесенные к каждому кластеру: for ki in range(k): centroids[ki,:] = [ np.mean(data[groupidx==ki, 0]), np.mean(data[groupidx==ki, 1]) ]
Наконец, шаг 5 заключается в размещении приведенных выше шагов в цикле, который повторяется до тех пор, пока не будет получено хорошее решение. В алгоритмах k-средних производственного уровня итерации продолжаются до тех пор, пока не будет достигнут критерий останова, например когда центроиды кластеров больше не перемещаются. Для простоты здесь мы выполним три итерации (данное число выбрано произвольно, чтобы сделать график визуально сбалансированным). Четыре панели на рис. 4.3 показывают центроиды изначально случайных кластеров (итерация 0) и их обновленные местоположения после каждой из трех итераций. Если вы изучаете алгоритмы кластеризации, то вы узнаете изощренные методы инициализации центроидов и критерии останова, а также количест венные методы отбора подходящего параметра k. Тем не менее все методы
Упражнения по программированию 71
k-средних, по существу, являются расширениями вышеупомянутого алгоритма, и в основе их реализаций лежит линейная алгебра.
Рис. 4.3 Результат работы метода k-средних
Упражнения по программированию Упражнения по корреляции Упражнение 4.1 Напишите функцию Python, которая на входе принимает два вектора и на выходе выдает два числа: коэффициент корреляции Пирсона и значение косинусного сходства. Напишите исходный код, который следует формулам, представленным в данной главе; не используйте вызовы встроенной в NumPy функции np.corrcoef и встроенной в SciPy функции spatial.distance. cosine. Убедитесь, что два значения на выходе идентичны, когда переменные уже центрированы по среднему значению, и различны, когда переменные не центрированы по среднему значению. Упражнение 4.2 Давайте продолжим обследовать разницу между корреляцией и косинусным сходством. Создайте переменную, содержащую целые числа от 0 до 3, и вторую переменную, равную первой переменной плюс некоторое смеще-
72 Применения векторов ние. Затем создайте симуляцию, в которой вы систематически варьируете это смещение между –50 и +50 (то есть на первой итерации симуляции вторая переменная будет равна [–50, –49, –48, –47]). В цикле for вычислите корреляцию и косинусное сходство между двумя переменными и сохраните эти результаты. Затем постройте линейный график, показывающий, как среднее смещение влияет на корреляцию и косинусное сходство. Вы должны суметь воспроизвести рис. 4.4.
Рис. 4.4 Результаты упражнения 4.2
Упражнение 4.3 В Python есть несколько функций, которые вычисляют коэффициент корреляции Пирсона. Одна из них называется pearsonr и находится в модуле stats библиотеки SciPy. Откройте исходный код этого файла (подсказка: ??functionname) и убедитесь, что вы понимаете, как реализация на Python соотносится с формулами, представленными в данной главе. Упражнение 4.4 Зачем вообще нужно программировать свои конкретно-прикладные функции, если они уже существуют в Python? Отчасти причина состоит в том, что написание своих конкретно-прикладных функций имеет огромную образовательную ценность, потому что вы видите, что (в данном случае) корреляция – это простое вычисление, а не какой-то невероятно сложный черно-ящичный алгоритм, который под силу понять только кандидату в области вычислительной науки. Но еще одна причина заключается в том, что встроенные функции иногда работают медленнее из-за громадного числа проверок входных данных, работы с дополнительными входными парамет рами, преобразованиями типов данных и т. п. Все это повышает удобство использования, но за счет времени вычислений.
Упражнения по программированию 73
В данном упражнении ваша цель – посмотреть, будет ли ваша сокращенная функция корреляции работать быстрее, чем функция NumPy corrcoef. Модифицируйте функцию из упражнения 4.2, чтобы вычислить только коэффициент корреляции. Затем, в цикле for по 1000 итерациям, сгенерируйте две переменные из 500 случайных чисел и вычислите корреляцию между ними. Засеките время исполнения цикла for. Затем повторите, но используя функцию np.corrcoef. В моих тестах конкретно-прикладная функция была примерно на 33 % быстрее, чем np.corrcoef. В этих игрушечных примерах различия измеряются в миллисекундах, но если вы выполняете миллиарды корреляций с крупными наборами данных, то эти миллисекунды складываются, давая действительно большой прирост в производительности! (Обратите внимание, что написание своих конкретно-прикладных функций без проверок входных данных сопряжено с риском ошибок на входе, которые были бы обнаружены функцией np.corrcoef.) (Также обратите внимание, что преимущество в скорости уменьшается с более крупными векторами. Попробуйте сами!)
Упражнения по фильтрации и обнаружению признаков Упражнение 4.5 Давайте построим детектор резких изменений1. Ядро детектора резких изменений будет очень простым: [–1 +1]. Точечное произведение этого ядра на фрагмент сигнала временного ряда с постоянным значением (например, [10 10]) равно 0. Но это точечное произведение будет крупным, когда сигнал имеет резкое изменение (например, [1 10] даст точечное произведение, равное 9). Мы будем работать с сигналом, который будет представлен функцией плато. Графики A и B на рис. 4.5 показывают ядро и сигнал. На первом шаге в данном упражнении пишется исходный код, который создает эти два временных ряда. Далее напишите цикл for для временных точек в сигнале. В каждый момент времени вычисляйте точечное произведение между ядром и сегментом данных временного ряда, который имеет ту же длину, что и ядро. Вы должны создать график, который выглядит как график C на рис. 4.5. (Сосредоточьтесь больше на результате, чем на эстетике.) Обратите внимание, что наш детектор резких изменений вернул 0, когда сигнал был ровным, +1, когда сигнал подскочил вверх, и –1, когда сигнал прыгнул вниз. Смело продолжайте обследовать этот исходный код. Например, изменится ли что-нибудь, если дополнить ядро нулями ([0 –1 1 0])? А что, если перевернуть ядро так, чтобы оно было [1 –1]? Как насчет того, если ядро будет асимметричным ([–1 2])? 1
В обработке временных рядов обнаружение резких изменений также называется обнаружением скачков, всплесков или отклонений от среднего уровня временного ряда или сигнала. – Прим. перев.
74 Применения векторов А)
Рис. 4.5 Результаты упражнения 4.5
Упражнение 4.6 Теперь мы повторим ту же процедуру, но с другим сигналом и ядром. Цель будет состоять в том, чтобы сгладить неровный временной ряд. Временной ряд будет состоять из 100 случайных чисел, сгенерированных из гауссова распределения (также именуемого нормальным распределением). Ядро будет представлять собой функцию в форме колокола, которая аппроксимирует гауссову функцию, определенную как числа [0, .1, .3, .8, 1, .8, .3, .1, 0], но шкалированную так, чтобы сумма по ядру составляла 1. Ваше ядро должно соответствовать графику A на рис. 4.6, хотя из-за случайных чисел ваш сигнал не будет выглядеть точно так же, как график B. Скопируйте и адаптируйте исходный код из предыдущего упражнения под вычисление скользящего временного ряда точечных произведений – сигнала, отфильтрованного гауссовым ядром. Предупреждение: будьте внимательны к индексации в цикле for. График C на рис. 4.6 показывает примерный результат. Хорошо видно, что отфильтрованный сигнал является сглаженной версией изначального сигнала. Такая процедура также называется низкочастотной фильтрацией. Упражнение 4.7 Замените 1 в центре ядра на –1 и усредните центр ядра. Затем выполните исходный код фильтрации и построения графика повторно. Каким будет результат? Он действительно подчеркивает резкие признаки! По сути дела,
Упражнения по программированию 75
данное ядро теперь является высокочастотным фильтром, а значит, оно приглушает плавные (низкочастотные) признаки и выделяет быстро меняющие ся (высокочастотные) признаки. A)
Рис. 4.6 Результаты упражнения 4.6
Упражнения по алгоритму k-средних Упражнение 4.8 Оптимальное значение k можно определить путем повторения кластеризации несколько раз (всякий раз с использованием случайно инициализированных центроидов кластеров) и оценивания того, является ли итоговая кластеризация одинаковой либо другой. Не генерируя новых данных, повторите исходный код алгоритма -средних несколько раз, используя k = 3, чтобы увидеть похожесть/непохожесть результирующих кластеров (это качест венная оценка, основанная на визуальном осмотре). Выглядят ли итоговые отнесения к кластерам в целом похожими, даже если центроиды выбраны случайным образом? Упражнение 4.9 Повторите кластеризации несколько раз, используя k = 2 и k = 4. Что вы думаете об этих результатах?
5 Матрицы. Часть 1
Матрица – это вектор, перенесенный на следующий уровень. Матрицы как математические объекты очень разноплановы. В них могут храниться наборы уравнений, геометрические преобразования, положения частиц во времени, финансовые отчеты и громадное число других вещей. В науке о данных матрицы иногда называют таблицами данных, в которых строки соответствуют наблюдениям (например, клиентам), а столбцы – признакам (например, покупкам). Данная и следующие две главы выведут ваши знания о линейной алгебре на новый уровень. Выпейте чашечку кофе и наденьте свою мыслительную тюбетейку. К концу главы ваш мозг станет больше.
Создание и визуализация матриц в NumPy В зависимости от контекста матрицы концептуализируются в уме как множество векторов-столбцов, расположенных бок о бок (например, как таблицы в формате «наблюдения по признакам»), как множество уложенных стопкой векторов-строк (например, в виде мультисенсорных данных, в которых каждая строка – это временной ряд из другого канала) или как упорядоченный набор отдельных матричных элементов (например, в виде изображения, в каждом матричном элементе которого закодировано значение интенсивности пиксела).
Визуализация, индексация и нарезка матриц Малые матрицы можно легко распечатывать полностью, как в следующих ниже примерах:
Создание и визуализация матриц в NumPy 77
Но это не масштабируется, и матрицы, с которыми вы работаете на практике, могут быть большими, возможно, содержащими миллиарды элементов. Поэтому более крупные матрицы можно визуализировать в виде изображений. Числовое значение каждого элемента матричной карты соотносится с цветом изображения. В большинстве случаев такие карты1 псевдоцветны, поскольку соотнесенность числового значения с цветом является произвольной. На рис. 5.1 показано несколько примеров матриц, визуализированных в виде изображений с использованием библиотеки Python matplotlib.
Рис. 5.1 Три матрицы, визуализированные в виде изображений
Матрицы обозначаются заглавными буквами жирным шрифтом, например матрица A или M. Размер матрицы указывается традиционным образом в формате (строка, столбец). Например, следующая ниже матрица имеет размер 3×5, поскольку в ней три строки и пять столбцов:
На конкретные элементы матрицы можно ссылаться, обращаясь по индексу строки и столбца: элемент в 3-й строке и 4-м столбце матрицы A обозначается как a3.4 (в предыдущем примере матрицы a3.4 = 8). Важное напоминание: в математике используется индексация с отсчетом от единицы, в то время как в Python используется индексация с отсчетом от нуля. Таким образом, в Python элемент a3.4 индексируется как A[2,3]. Подмножество строк или столбцов матрицы извлекается с помощью операции нарезки. Если вы в Python новичок, то обратитесь к главе 16, чтобы ознакомиться с нарезкой списков и массивов NumPy. Для того чтобы извлечь срез из матрицы, надо указать начальные и конечные строки и столбцы, а также шаг нарезки, равный 1. Онлайновый исходный код проведет вас по всей процедуре, а следующий ниже исходный код показывает пример извлечения подматрицы из строк 2–4 и столбцов 1–5 более крупной матрицы: A = np.arange(60).reshape(6, 10) sub = A[1:4:1,0:5:1] 1
Син. растровые изображения. – Прим. перев.
78 Матрицы. Часть 1 Ниже приведены полная матрица и подматрица: Изначальная матрица: [[ 0 1 2 3 4 5 6 7 8 9] [10 11 12 13 14 15 16 17 [20 21 22 23 24 25 26 27 [30 31 32 33 34 35 36 37 [40 41 42 43 44 45 46 47 [50 51 52 53 54 55 56 57
Подматрица: [[10 11 12 13 14] [20 21 22 23 24] [30 31 32 33 34]]
Специальные матрицы Существует бесконечное число матриц, потому что существует бесконечное число способов организации чисел в матрицу. Но матрицы можно описывать с использованием относительно малого числа характеристик, в результате создавая «семейства», или категории матриц. Указанные категории важно знать, потому что они появляются в определенных операциях либо обладают определенными полезными свойствами. Некоторые категории матриц используются так часто, что для их создания есть специальные функции NumPy. Ниже приведен список нескольких распространенных специальных матриц и исходный код Python для их создания1; вы их увидите на рис. 5.2. Матрица случайных чисел Это матрица, которая содержит числа, берущиеся случайно из некоторого распределения, обычно гауссова (также именуемого нормальным). Матрицы случайных чисел отлично подходят для обследования линейной алгеб ры в исходном коде, потому что они быстро и легко создаются с любым размером и рангом (понятие ранга матрицы вы узнаете в главе 16). В NumPy имеется несколько способов создания случайных матриц в зависимости от того, из какого распределения вы хотите извлекать числа. В этой книге мы будем использовать числа, в основном распределенные по Гауссу: Mrows = 4 # очертание 0 Ncols = 6 # очертание 1 A = np.random.randn(Mrows, Ncols)
Квадратная и неквадратная Квадратная матрица имеет такое же число строк, что и столбцов; другими словами, матрица имеет размер ℝN×N. Неквадратная матрица, также 1
Существуют и другие специальные матрицы, о которых вы узнаете в книге позже, но этого списка для начала будет достаточно.
Создание и визуализация матриц в NumPy 79
иногда именуемая прямоугольной матрицей, имеет отличающееся число строк и столбцов. Квадратные и прямоугольные матрицы можно создавать из случайных чисел, настроив параметры очертания в приведенном выше исходном коде. Прямоугольные матрицы называются высокими, если в них больше строк, чем столбцов, и широкими, если в них больше столбцов, чем строк. Диагональная Диагональ матрицы – это элементы, начинающиеся в верхнем левом углу и спускающиеся в нижний правый. Диагональная матрица имеет нули во всех внедиагональных элементах; диагональные элементы тоже могут содержать нули, но они являются единственными элементами, которые могут содержать ненулевые значения. Функция NumPy np.diag() имеет два вида поведения в зависимости от входных данных: при вводе матрицы функция np.diag возвращает диагональные элементы в виде вектора; при вводе вектора функция np.diag возвращает матрицу с этими векторными элементами на диагонали. (Примечание: извлечение диагональных элементов матрицы не называется «диагонализацией матрицы»; это отдельная операция, представленная в главе 13.) Треугольная Треугольная матрица содержит одни нули выше либо ниже главной диагонали. Матрица называется верхней треугольной, если ненулевые элементы находятся выше диагонали, и нижней треугольной, если ненулевые элементы находятся ниже диагонали. В NumPy имеются специальные функции для извлечения верхнего (np. triu()) либо нижнего (np.tril()) треугольника матрицы. Единичная Единичная матрица1 является одной из наиболее важных специальных матриц. Она эквивалентна числу 1 в том смысле, что любая матрица или вектор, умноженные на единичную матрицу, будут той же самой матрицей или вектором. Единичная матрица – это квадратная диагональная матри ца, все диагональные элементы которой имеют значение 1. Она обозначается буквой I. Иногда вместе с буквой можно увидеть индекс, указывающий на ее размер (например, I5 – это единичная матрица размером 5×5); если индекса нет, то ее размер можно определить из контекста (например, чтобы уравнение было совместимым). В Python единичная матрица создается функцией np.eye(). Нулей Матрица нулей сравнима с вектором нулей: это матрица, состоящая из одних нулей. Как и вектор нулей, она обозначается отмеченным жирным шрифтом нулем: 0. Возможно, то, что один и тот же символ обозначает как вектор, так и матрицу, будет немного сбивать с толку, но в математи1
Англ. identity matrix; син. матрица тождественного преобразования. – Прим. перев.
80 Матрицы. Часть 1 ческой и естественно-научной нотации такого рода перегрузка довольно распространена. Матрица нулей создается функцией np.zeros().
Рис. 5.2 Несколько специальных матриц. Числа и значения в оттенках серого указывают значение каждого элемента матрицы
Матричная математика: сложение, умножение на скаляр, адамарово умножение Математические операции на матрицах делятся на две категории: понятные и непонятные на интуитивном уровне. В общем и целом интуитивно понятные операции можно выражать в виде пошаговых процедур, тогда как объяснение интуитивно непонятных операций требует больше времени, а их понимание – немного практики. Давайте начнем с интуитивно понятных операций.
Сложение и вычитание Две матрицы складываются путем сложения соответствующих элементов матриц. Вот пример:
Матричная математика: сложение, умножение на скаляр, адамарово умножение 81
Как можно было догадаться из примера, сложение матриц определяется только между двумя матрицами одинакового размера.
«Сдвиг» матрицы Как и в случае с векторами, прибавить скаляр к матрице, как в λ + A, невозможно формально. Python же такую операцию допускает (например, 3+np. eye(2)), которая предусматривает транслирование скаляра в каждый элемент матрицы. Это удобное вычисление, но формально оно не является линейноалгебраической операцией. Однако существует линейно-алгебраический способ прибавления скаляра к квадратной матрице, и он называется сдвигом матрицы и работает путем прибавления постоянного значения к диагонали, то есть реализуется посредством прибавления умноженной на скаляр единичной матрицы: A + λI.
Вот численный пример:
На языке Python сдиг выполняется просто: A S A A
np.array([ [4,5,1], [0,1,11], [4,9,7] ]) 6 s # НЕ сдвигается! s*np.eye(len(A)) # сдвигается
Обратите внимание, что меняются только диагональные элементы; остальная часть матрицы сдвигом не искажается. На практике сдвигают на относительно малое число, чтобы в матрице сохранить как можно больше информации, при этом извлекая выгоду из эффектов сдвига, включая повышение численной стабильности матрицы (позже в книге вы узнаете, почему происходит нестабильность). На сколько именно нужно сдвигать, зависит от текущих исследований во многих областях машинного обучения, статистики, глубокого обучения, разработки систем управления и т. д. Например, сдвиг на λ = 6 – это мало или много? Как насчет λ = .001? Очевидно, что эти числа являются «большими» либо «малыми» по отношению к числовым значениям в матрице. Поэтому на практике лямбда λ обычно задается как некоторая доля определенного матрицей элемента, такого как норма или среднее значение собственных чисел. Вы сможете обследовать эту тему в последующих главах. «Сдвиг» матрицы имеет два первичных (чрезвычайно важных!) применения: это механизм отыскания собственных чисел матрицы и механизм регуляризации матриц при подгонке моделей к данным.
82 Матрицы. Часть 1
Умножение на скаляр и адамарово умножение Указанные два типа умножения работают для матриц так же, как и для векторов, то есть поэлементно. Умножение матрицы на скаляр означает умножение каждого элемента матрицы на один и тот же скаляр. Вот пример использования матрицы, содержащей буквы вместо чисел:
Адамарово умножение аналогичным образом предусматривает поэлементное умножение двух матриц (отсюда и альтернативная терминология поэлементное умножение). Вот пример:
В NumPy адамарово умножение можно реализовать с помощью функции np.multiply(). Но нередко синтаксически его проще реализовать, используя звездочку между двумя матр иц ами: A*B. Возможно, это вызовет некоторую путаницу, поскольку стандартное умножение матр иц (следующий раздел) обозначается символом @. И здесь есть тонкое, но важное различие! (Данный факт будет особенно сбивать с толку тех читателей, кто переходит на Python из MATLAB, в котором умножение матр иц обозначается символом *.) A = np.random.randn(3, 4) B = np.random.randn(3, 4) A*B # адамарово умножение np.multiply(A, B) # тоже адамарово A@B # НЕ адамарово!
Адамарово умножение действительно имеет несколько применений в линейной алгебре, например при вычислении обратной матрицы. Однако чаще всего оно применяется в приложениях как удобный способ хранения большого числа отдельных умножений. Это похоже на то, как нередко применяется адамарово умножение векторов, как обсуждалось в главе 2.
Стандартное умножение матриц Теперь мы переходим к интуитивно непонятному способу умножения мат риц. Для ясности стандартное умножение матриц не особенно сложное; оно просто отличается от того, что можно было бы ожидать. Вместо того чтобы оперировать поэлементно, стандартное умножение матриц оперирует по-
Стандартное умножение матриц 83
строчно/постолбцово. Собственно говоря, стандартное умножение матриц сводится к систематической коллекции точечных произведений между строками одной матрицы и столбцами другой матрицы. (Эта форма умножения формально называется просто умножением матриц; я добавил прилагательное стандартное, чтобы помочь устранить неоднозначность в отношении адамарова умножения и умножения на скаляр.) Но прежде чем перейти к деталям процедуры умножения двух матриц, сначала объясню, как определять, можно умножать две матрицы или нет. Как вы узнаете, две матрицы можно умножить только в том случае, если их размеры согласуются.
Правила допустимости умножения матриц Вы знаете, что размеры матрицы записываются как M×N – строки по столбцам. Две умножающие друг друга матрицы могут иметь разные размеры, поэтому давайте обозначим размер второй матрицы как N×K. При записи двух матриц-сомножителей с их размерами внизу можно ссылаться на «внут ренние» мерности N и «внешние» мерности M и K. Вот важный момент: умножать матрицы допустимо только тогда, когда «внутренние» мерности совпадают, а размер матрицы произведения определяется «внешними» мерностями. Смотрите рис. 5.3.
Рис. 5.3 Визуализация допустимости умножения матриц. Запомните эту картинку
Выражаясь формальнее, умножение матриц допустимо, когда число столбцов в левой матрице равно числу строк в правой матрице, а размер мат рицы произведения определяется числом строк в левой матрице и числом столбцов в правой матрице. Я нахожу, что правило «внутренние/внешние» запоминается легче. Уже можно видеть, что умножение матриц не подчиняется коммутативному закону: AB может быть допустимым, тогда как BA – недопустимым. Даже если оба умножения верны (например, если обе матрицы – квадратные), они могут давать разные результаты. То есть если C = AB и D = BA, тогда в общем случае C ≠ D (в некоторых особых случаях они равны, но в целом допускать равенство невозможно).
84 Матрицы. Часть 1 Обратите внимание на обозначения: адамарово умножение обозначается кругом с точкой (A ⊙ B), тогда как умножение матриц обозначается как две матрицы бок о бок без какого-либо символа между ними (AB). Теперь самое время узнать о механике и интерпретации умножения мат риц.
Умножение матриц Причина, по которой умножение матриц допустимо только в том случае, если число столбцов в левой матрице соответствует числу строк в правой матри це, заключается в том, что (i, j)-й элемент в матрице произведения является точечным произведением между i-й строкой левой матрицы и j-м столбцом в правой матрице. Уравнение 5.1 показывает пример умножения матриц с использованием тех же двух матриц, которые мы использовали для адамарова умножения. Убедитесь, что вы понимаете, как каждый элемент в матрице произведения вычисляется в виде точечных произведений соответствующих строк и столбцов матриц левой части. Уравнение 5.1. Пример умножения матриц. Круглые скобки добавлены, чтобы облегчить визуальное группирование
Если вы прилагаете усилия, чтобы запомнить принцип работы умножения матриц, но у вас едва получается, то на рис. 5.4 показан мнемонический трюк с выведением умножения при помощи пальцев.
Рис. 5.4 Движения пальцев для умножения матриц
Как интерпретировать умножение матриц? Вспомните, что точечное произведение – это число, в котором кодируется взаимосвязь между двумя векторами. И таким образом, результатом умножения матриц является матрица, в которой хранятся все попарные линейные взаимосвязи между строками левой матрицы и столбцами правой матрицы. Это прекрасная вещь, и она лежит в основе вычисления матриц ковариаций и корреляций, общей ли-
Стандартное умножение матриц 85
нейной модели (используемой в статистическом анализе, включая модели ANOVA и регрессии), сингулярного разложения и бесчисленного количества других применений.
Умножение матрицы на вектор В чисто механическом смысле умножение матрицы на вектор не представляет собой ничего особенного и не заслуживает отдельного подраздела: взаимное умножение матрицы и вектора – это просто умножение матрицы, в котором одна «матрица» является вектором. Однако умножение матрицы и вектора между собой имеет много применений в науке о данных, машинном обучении и компьютерной графике, поэтому на него стоит потратить немного времени. Давайте начнем с основ. Матрицу можно умножить на вектор-столбец, расположенный справа, но не на вектор-строку, и ее можно умножить на вектор-строку, расположенную слева, но не на вектор-столбец. Другими словами, Av и vTA допустимы, но AvT и vA не допустимы. Это ясно из осмотра размеров матрицы: матрицу M×N можно предпозиционно умножить на матрицу 1×M (также именуемую векторомстрокой) либо постпозиционно умножить на матрицу N×1 (также именуемую вектором-столбцом). Результатом умножения матрицы на вектор всегда является вектор, и ориентация этого вектора зависит от ориентации вектора-сомножителя: предпозиционное умножение матрицы на вектор-строку создает еще один вектор-строку, тогда как постпозиционное умножение матри цы на вектор-столбец производит еще один вектор-столбец. Опять же, это очевидно, если думать о размерах матрицы, но на это стоит указать. Умножение матриц на векторы имеет несколько применений. В статистике предсказываемые моделью значения данных получаются путем умножения расчетной матрицы на коэффициенты регрессии, что записывается как Bβ. В анализе главных компонент выявляется вектор весововых коэффициентов «важностей признаков», в котором максимизирована дисперсия в наборе данных Y, и записывается как (YTY)v (вектор важностей признаков v называется собственным вектором). В многопеременной обработке сигналов размерноредуцированная компонента получается путем применения пространственного фильтра к данным многоканального временного ряда S и записывается как wTS. В геометрии и компьютерной графике множество координат изображения можно преобразовывать с использованием матрицы математического преобразования, и преобразование записывается как Tp, где T – это матрица преобразования, а p – множество геометрических координат. В прикладной линейной алгебре существует еще очень много примеров применения умножения матрицы на вектор, и позже в данной книге вы увидите несколько таких примеров. Умножение матрицы на вектор также является основой для пространств матриц; с этой важной темой вы познакомитесь позже в следующей главе.
86 Матрицы. Часть 1 А пока мне хотелось бы сосредоточиться на двух конкретных интерпретациях умножения матрицы на вектор: как средства реализации линейно-взвешенных комбинаций векторов и как механизма реализации геометрических преобразований.
Линейно-взвешенные комбинации В предыдущей главе мы рассчитывали линейно-взвешенные комбинации, используя отдельные скаляры и векторы, а затем перемножали их по отдельности. Но теперь вы стали умнее, чем когда начинали предыдущую главу, и поэтому готовы усвоить более оптимальный, более компактный и масштабируемый метод вычисления линейно-взвешенных комбинаций: помещать отдельные векторы в матрицу, а веса – в соответствующие элементы вектора. И затем умножать. Вот численный пример:
Пожалуйста, найдите минутку, чтобы проработать умножение, и убедитесь, что понимаете принцип реализации линейно-взвешенной комбинации двух векторов в виде умножения матрицы на вектор. Ключевой момент заключается в том, что каждый элемент в векторе умножает соответствующий столбец в матрице на скаляр, а затем взвешенные векторы-столбцы суммируются, чтобы получить произведение. Данный пример предусматривает линейно-взвешенные комбинации векторов-столбцов; что бы вы изменили для вычисления линейно-взвешенных комбинаций векторов-строк1?
Результаты геометрических преобразований Когда мы думаем о векторе как о геометрическом отрезке, то умножение матрицы на вектор становится способом поворота и шкалирования этого вектора (вспомните, что умножение скаляра на вектор может шкалировать, но не поворачивать). Ради удобства визуализации давайте начнем с двумерного примера. Вот наша матрица и векторы: M = np.array([ [2,3], [2,1] ]) x = np.array([ [1, 1.5] ]).T Mx = M@x
Обратите внимание, что я создал x как вектор-строку, а затем транспонировал его в вектор-столбец; за счет этого сократилось число квадратных скобок при наборе исходного кода. 1
Поместить коэффициенты в вектор-строку и предпозиционно умножить на этот вектор.
Стандартное умножение матриц 87
График А на рис. 5.5 создает визуализацию этих двух векторов. Хорошо видно, что матрица M одновременно повернула и растянула изначальный вектор. Давайте попробуем другой вектор с той же матрицей. На самом деле, просто ради развлечения, давайте использовать те же векторные элементы, но с переставленными позициями (то есть вектор v = [1.5,1]). Теперь на графике B (рис. 5.5) происходит странная вещь: произведение матрицы и вектора больше не поворачивается в другом направлении. Матрица по-прежнему прошкалировала вектор, но его направление сохранилось. Другими словами, умножение матрицы на вектор действовало так, как если бы это было умножение вектора на скаляр. И это не случайное событие: на самом деле вектор v является собственным вектором матрицы M, а число, на которое M растянула v, является собственным числом данной матрицы1. Это настолько невероятно важное явление, что оно заслуживает отдельной главы (глава 13), но я просто не мог удержаться, чтобы не познакомить вас с этой концепцией сейчас. А)
Рис. 5.5 Примеры умножения матриц на векторы
Переходя к более сложным темам, основной смысл этих демонстраций – в том, что одна из функций умножения матрицы на вектор заключается в том, что матрица содержит преобразование, которое при применении к вектору может поворачивать и растягивать этот вектор.
Нередко используются синонимичные термины: характеристический вектор и характеристическое число. В английском языке во всех подобных терминах используется заимствованный из немецкого языка корень eigen, со значением «собственный», «характеристический». – Прим. перев.
88 Матрицы. Часть 1
Матричные операции: транспонирование Вы узнали об операции транспонирования векторов в главе 2. Данный принцип соблюдается и с матрицами: поменять местами строки и столбцы. И точно так же, как с векторами, транспонирование обозначается надстрочной буквой T (таким образом, CT – это транспонированная версия матрицы C). А двойное транспонирование матрицы возвращает изначальную матрицу (CTT = C). Формальное математическое определение операции транспонирования приведено в уравнении 5.2 (и, по существу, повторяется из предыдущей главы), но, полагаю, одинаково легко запомнить, что транспонирование меняет местами строки и столбцы. Уравнение 5.2. Определение операции транспонирования aTi, j = aj,i. Вот пример:
В Python существует несколько способов транспонирования матриц с использованием функций и методов, работающих на массивах NumPy: A = np.array([ [3,4,5], [1,2,3] ]) A_T1 = A.T # в качестве метода A_T2 = np.transpose(A) # в качестве функции
В данном примере в матрице используется двумерный массив NumPy; что, по вашему мнению, произойдет, если применить метод транспонирования к вектору, запрограммированному в виде одномерного массива? Попробуйте – и узнаете1!
Обозначение точечного и внешнего произведений Теперь, когда вы знакомы с операцией транспонирования и правилами допустимости умножения матриц, можно вернуться к обозначению точечного произведения векторов. Для двух векторов-столбцов M×1 транспонирование первого вектора, а не второго, дает две «матрицы» размером 1×M и M×1. «Внутренние» мерности совпадают, а «внешние» мерности говорят о том, что 1
Ничего. NumPy вернет тот же одномерный массив, не изменяя его и не выдавая предупреждения либо ошибки.
Симметричные матрицы 89
произведением будет 1×1, то есть скаляр. Это и есть та причина, по которой точечное произведение указывается в виде aTb. То же самое рассуждение будет и для внешнего произведения: умножение вектора-столбца на вектор-строку имеет размеры M×1 и 1×N. «Внутренние» мерности совпадают, и размером результата будет M×N.
Матричные операции: LIVE EVIL (порядок следования операций) LIVE EVIL – это палиндром1 и симпатичная мнемоника для запоминания того, как транспонирование влияет на порядок действий при умножении матриц. В сущности, правило заключается в том, что результат транспонирования умноженных матриц будет одинаков, что и транспонирование и умножение отдельных матриц, но в обратном порядке. В уравнении 5.3 L, I, V и E являются матрицами, и для того чтобы сделать умножение допустимым, исходят из того, что их размеры совпадают. Уравнение 5.3. Пример правила LIVE EVIL (LIVE)T = ETVTITLT. Излишне говорить, что данное правило применимо для умножения любого числа матриц, а не только четырех, и не только с этими «случайно выбранными» буквами. Указанное правило действительно кажется странным, но это единственный способ сделать так, чтобы транспонирование умноженных матриц работало. У вас будет возможность протестировать его самостоятельно в упражнении 5.7 в конце данной главы. Если хотите, то, перед тем как двигаться дальше, можете сразу перейти к этому упражнению.
Симметричные матрицы Симметричные матрицы имеют целый ряд особых свойств, которые делают их удобными для работы. Кроме того, они, как правило, обладают численной стабильностью и, следовательно, удобны для вычислительных алгоритмов. Работая с данной книгой, вы узнаете об особых свойствах симметричных матриц; здесь я сосредоточусь на вопросах о том, что такое симметричные матрицы и как их создавать из несимметричных матриц. Что значит для матрицы быть симметричной? Это означает, что соответствующие строки и столбцы равны. И это означает, что при изменении строк 1
Палиндромом называется слово или фраза, которые пишутся одинаково в прямом и обратном порядке.
90 Матрицы. Часть 1 и столбцов местами с матрицей ничего не происходит. А это, в свою очередь, означает, что симметричная матрица равна ее транспонированной версии. В математических терминах матрица A является симметричной, если AT = A. Посмотрите на симметричную матрицу в уравнении 5.4. Уравнение 5.4. Симметричная матрица; обратите внимание, что каждая строка равна соответствующему ей столбцу
Может ли неквадратная матрица быть симметричной? Нет! Причина в том, что если матрица имеет размер M×N, то ее транспонированная версия имеет размер N×M. Эти две матрицы не могут быть равными, за исключением случая, когда M = N, а это означает, что матрица является квадратной.
Создание симметричных матриц из несимметричных Возможно, поначалу это покажется удивительным, но умножение любой мат рицы – даже неквадратной и несимметричной – на ее транспонированную версию будет приводить к получению квадратной симметричной матрицы. Другими словами, ATA является квадратной симметричной, как и AAT. (Если вам не хватает времени, терпения либо навыков работы с клавиатурой, чтобы форматировать надстрочную T, то можете писать AtA и AAt либо A¢A и AA¢.) Прежде чем рассматривать пример, давайте строго докажем это утверждение. С одной стороны, на самом деле не требуется доказывать отдельно, что ATA является квадратной и симметричной, потому что из последнего вытекает первое. Но доказательство прямоугольности является простым и хорошим упражнением в линейно-алгебраических доказательствах (которые, как правило, короче и проще, чем, например, доказательства в дифференциальном исчислении). Доказательство достигается просто путем рассмотрения размеров мат рицы: если A имеет размер M×N, то ATA имеет размеры (N×M)(M×N) и, следовательно, матрица произведения имеет размер N×N. Ту же логику можно использовать и для AAT. Теперь переходим к доказательству симметрии. Вспомните определение симметричной матрицы – это матрица, равная своей транспонированной версии. Итак, давайте транспонируем ATA, задействуем немного алгебры и посмотрим, что получится. Внимательно проследите каждый приведенный ниже шаг; доказательство основано на правиле LIVE EVIL: (ATA)T = ATATT = ATA.
Беря первый и последний члены, мы получаем (ATA)T = (ATA). Матрица равна ее транспонированной версии, следовательно, она является симмет ричной. Теперь повторите доказательство самостоятельно, используя AAT. Внимание, спойлер! Вы придете к тому же выводу. Но написание доказательства поможет вам усвоить концепцию до мозга костей. Таким образом, обе матрицы, AAT и ATA, являются квадратно-симметричными. Но это не одна и та же матрица! На самом деле если A не является квадратной, то два матричных произведения даже не имеют одинакового размера. ATA называется мультипликативным методом создания симметричных матриц. Существует также аддитивный метод, который допустим, когда мат рица является квадратной, но несимметричной. Этот метод обладает некоторыми интересными свойствами, но не имеет большой прикладной ценности, поэтому я не буду на нем заострять внимания. Упражнение 5.9 познакомит вас с алгоритмом; если вы готовы к испытанию, то, прежде чем приступать к выполнению упражнения, можете попробовать разработать этот алгоритм самостоятельно.
Резюме Данная глава является первой в серии из трех глав, посвященных матрицам. Здесь вы ознакомились с основой, на которой базируются все матричные операции. Вкратце: Матрицы – это развернутые таблицы чисел. В различных приложениях их удобно концептуализировать в уме в виде множества векторовстолбцов, множества векторов-строк либо некоей упорядоченности отдельных значений. Как бы то ни было, визуализация матриц в виде изображений нередко бывает информативной либо, по меньшей мере, приятной на вид. Существует несколько категорий специальных матриц. Знакомство со свойствами типов матриц поможет вам разобраться в матричных уравнениях и продвинутых приложениях. Некоторые арифметические операции выполняются поэлементно, такие как сложение, умножение на скаляр и адамарово умножение. «Сдвиг» матрицы означает добавление константы к диагональным элементам (без изменения внедиагональных элементов). Сдвиг имеет несколько применений в машинном обучении, в первую очередь для отыс кания собственных чисел и регуляризации статистических моделей. Умножение матриц предусматривает точечные произведения между строками левой матрицы и столбцами правой матрицы. Матрица произведения представляет собой организованный набор соотнесений между парами строк и столбцов. Запомните правило проверки правильности умножения матриц: (M×N)(N×K) = (M×K).
92 Матрицы. Часть 1 LIVE EVIL1,2: транспонирование умноженных матриц равно транспонированным и умноженным отдельным матрицам с обратным порядком следования. Симметричные матрицы зеркально отражаются по диагонали, то есть каждая строка равна соответствующим ей столбцам, и определяются как A = AT. Симметричные матрицы обладают многими интересными и полезными свойствами, которые делают их удобными для работы в приложениях. Симметричная матрица создается из любой матрицы, умножая эту матрицу на ее транспонированную версию. Результирующая матрица ATA занимает центральное место в статистических моделях и сингулярном разложении.
Упражнения по программированию Упражнение 5.1 Это упражнение поможет вам ознакомиться с индексацией элементов матрицы. Создайте матрицу 3×4, используя np.arange(12).reshape(3,4). Затем напишите исходный код на Python, чтобы извлечь элемент во второй строке, четвертом столбце. Используйте мягкое программирование, чтобы выбирать разные индексы строк/столбцов. Распечатайте сообщение, подобное следующему ниже: Элемент матрицы по индексу (2,4) равен 7.
Упражнение 5.2 Это и следующее упражнения сосредоточены на нарезке матриц с целью получения подматриц. Начните с создания матрицы C на рис. 5.6 и примените существующую в Python операцию нарезки, чтобы извлечь подмат рицу, состоящую из первых пяти строк и пяти столбцов. Давайте назовем эту матрицу C1. Попробуйте воспроизвести рис. 5.6, но если у вас возникли проблемы с программированием визуализации на Python, то просто сосредоточьтесь на правильном извлечении подматрицы. Упражнение 5.3 Расширьте этот исходный код, чтобы извлечь остальные четыре блока размером 5×5. Затем создайте новую матрицу с этими блоками, которые переставлены местами в соответствии с рис. 5.7.
LIVE EVIL – это милая мнемотехника, а не рекомендация о том, как вести себя в обществе! 2 Для справки: LIVE EVIL (Зло в натуре, Зло живьем) – это концертный альбом хевиметал группы Black Sabbath, выпущенный в 1982 году. – Прим. перев.
Упражнения по программированию 93
Рис. 5.6 Визуализация упражнения 5.2
Рис. 5.7 Визуализация упражнения 5.3
Упражнение 5.4 Реализуйте поэлементное сложение матриц, используя два цикла for по строкам и столбцам. Что происходит, когда вы пытаетесь сложить две матри цы с несовпадающими размерами? Это упражнение поможет вам подумать о разбиении матрицы на строки, столбцы и отдельные элементы. Упражнение 5.5 Сложение матриц и умножение матрицы на скаляр подчиняются математическим законам коммутативности и дистрибутивности. Это означает, что
94 Матрицы. Часть 1 следующие ниже уравнения дают одинаковые результаты (допустим, что матрицы A и B имеют одинаковый размер и что σ является неким скаляром): σ(A + B) = σA + σB = Aσ + Bσ.
Вместо того чтобы доказывать это уравнение математически, вы продемонстрируете это с помощью программирования. Создайте на Python две матрицы случайных чисел размером 3×4 и случайный скаляр. Затем реализуйте три выражения в приведенном выше уравнении. Вам нужно будет найти способ подтвердить эквивалентность трех результатов. Имейте в виду, что крошечные ошибки вычислительной прецизионности1 в диапазоне 10–15 следует игнорировать. Упражнение 5.6 Запрограммируйте умножение матриц с использованием циклов for. Подтвердите свои результаты с помощью оператора @ библиотеки NumPy. Это упражнение поможет вам укрепить ваше понимание умножения матриц, но на практике всегда лучше использовать @ вместо написания двойного цикла for. Упражнение 5.7 Подтвердите правило LIVE EVIL, выполнив следующие ниже пять шагов. 1. Создайте четыре матрицы случайных чисел, установив размеры L Î ℝ2×6, I Î ℝ6×3, V Î ℝ3×5 и E Î ℝ5×2. 2. Умножьте четыре матрицы и транспонируйте произведение. 3. Транспонируйте каждую матрицу по отдельности и умножьте их, не меняя их порядок следования. 4. Транспонируйте каждую матрицу по отдельности и умножьте их в обратном порядке в соответствии с правилом LIVE EVIL. Проверьте совпадение результата шага 2 с результатами шага 3 и шага 4. 5. Повторите приведенные выше шаги, но используя только квадратные матрицы. Упражнение 5.8 В этом упражнении вы напишете функцию Python, которая выполняет проверку матрицы на симметричность. На входе она должна принимать мат рицу и на выходе выводить булево значение True, если матрица является симметричной, либо False, если матрица является несимметричной. Имейте в виду, что малые ошибки вычислительного округления/прецизионности могут создавать впечатление, что «равные» матрицы являются неравными. Следовательно, вам нужно будет выполнять проверку на равенство с некоторой разумной терпимостью. Протестируйте функцию на симметричных и несимметричных матрицах. 1
Для справки: метрика точности (accuracy) измеряет степень отклонения от целевого показателя, по сути являясь метрикой ширины отклонения, а метрика прецизионности (precision) измеряет степень глубины (резкости) измеряемой величины. – Прим. перев.
Упражнения по программированию 95
Упражнение 5.9 Я упоминал, что существует аддитивный метод создания симметричной матрицы из несимметричной квадратной матрицы. Указанный метод довольно прост: усреднить матрицу посредством ее транспонированной версии. Реализуйте этот алгоритм на Python и подтвердите, что результат действительно является симметричным. (Подсказка: используйте функцию, которую вы написали в предыдущем упражнении!) Упражнение 5.10 Повторите вторую часть упражнения 3.3 (два вектора в ℝ3), но используйте умножение матрицы на вектор вместо умножения вектора на скаляр. То есть вычислите As вместо σ1v1 + σ2v2. Упражнение 5.11 Диагональные матрицы обладают многими интересными свойствами, которые делают их полезными для работы. В данном упражнении вы узнаете о двух из этих свойств: предпозиционное умножение диагональной матрицы на матрицусомножитель шкалирует строки правой матрицы на соответствующие диагональные элементы; постпозиционное умножение диагональной матрицы на матрицусомножитель шкалирует столбцы левой матрицы на соответствующие диагональные элементы. Этот факт используется в нескольких приложениях, включая вычисление матриц корреляций (глава 7) и диагонализацию матриц (главы 13 и 14). Давайте обследуем последствия этих свойств. Начнем с создания трех мат риц 4×4: матрицы из одних единиц (подсказка: np.ones()); диагональной матрицы, в которой диагональные элементы равны 1, 4, 9 и 16; и диагональной матрицы, равной квадратному корню из предыдущей диагональной матрицы. Далее распечатайте матрицу единиц, пред- и постпозиционно умноженную на первую диагональную матрицу-сомножитель. Вы получите следующие ниже результаты: # Предпозиционно умножить на диагональную матрицу: [[ 1. 1. 1. 1.] [ 4. 4. 4. 4.] [ 9. 9. 9. 9.] [16. 16. 16. 16.]] # Постпозиционно умножить на диагональную матрицу: [[ 1. 4. 9. 16.] [ 1. 4. 9. 16.] [ 1. 4. 9. 16.] [ 1. 4. 9. 16.]]
Наконец, предпозиционно и постпозиционно умножьте матрицу единиц на матрицу квадратных корней из диагональной матрицы. Вы получите следующее ниже:
96 Матрицы. Часть 1 # Пред- и постпозиционно умножить # на квадратично-диагональную матрицу [[ 1. 2. 3. 4.] [ 2. 4. 6. 8.] [ 3. 6. 9. 12.] [ 4. 8. 12. 16.]]
Обратите внимание, что строки и столбцы прошкалированы таким образом, что (i, j)-й элемент в матрице умножается на произведение i-го и j-го диагональных элементов. (На самом деле мы создали таблицу умножения!) Упражнение 5.12 Еще один забавный факт: умножение матриц – это то же самое, что и адамарово умножение двух диагональных матриц. Подумайте, почему так, используя бумагу и карандаш с двумя диагональными матрицами 3×3, а затем проиллюстрируйте это в исходном коде на Python.
6 Матрицы. Часть 2
Умножение матриц представляет собой один из самых замечательных даров, которыми наделили нас математики. Но для того чтобы перейти от элементарной линейной алгебры к продвинутой – а затем понимать и разрабатывать алгоритмы науки о данных, – нужно делать больше, чем просто умножать матрицы. Мы начинаем эту главу с изложения норм матриц и пространств матриц. Нормы матриц, по сути, являются расширением норм векторов, а пространства матриц, по сути, являются расширением подпространств векторов (подпространства векторов, в свою очередь, являются не чем иным, как линейновзвешенными комбинациями). Так что у вас уже есть необходимые базовые знания для этой главы. Такие понятия, как линейная независимость, ранг и определитель, позволят перейти от понимания элементарных понятий, таких как транспонирование и умножение, к пониманию сложных тем, таких как обратная матрица, собственные числа и сингулярные числа. И эти продвинутые темы раскрывают возможности линейной алгебры для применений в науке о данных. Таким образом, эта глава является отправной точкой в вашей трансформации из новичка в линейной алгебре в ее знатока. Внешне матрицы выглядят очень простыми – просто развернутой таблицей чисел. Но в предыдущих главах вы уже увидели, что в матрицах кроется нечто большее, чем кажется на первый взгляд. Итак, сделайте глубокий и успокаивающий вдох и погружайтесь в тему.
Нормы матриц Вы познакомились с нормами векторов в главе 2: норма вектора – это его евклидова геометрическая длина, которая вычисляется как квадратный корень из суммы квадратов элементов вектора. Нормы матриц немного сложнее. Прежде всего не существует «единственной в своем роде нормы матрицы»; из матрицы можно вычислить многочисленные отличимые нормы. Нормы матриц в чем-то похожи на нормы векторов – в том смысле, что каждая норма содержит одно характеризующее
98 Матрицы. Часть 2 матрицу число и что норма обозначается двойными вертикальными линиями, как в норме матрицы A, которая обозначается как ||A||. Но разные нормы матриц обладают разными смыслами. Громадное число норм матриц в общих чертах можно разделить на два семейства: поэлементные (также иногда именуемые матричными нормами «по входам»1) и индуцированные. Поэлементные нормы вычисляются на основе отдельных элементов матрицы, и, следовательно, эти нормы интерпретируются как отражение ими величин элементов в матрице. Индуцированные нормы интерпретируются следующим образом: одной из функций матрицы является кодирование преобразования вектора; индуцированная норма матрицы является мерой того, насколько это преобразование масштабирует (растягивает или сжимает) этот вектор. Данная интерпретация будет иметь больше смысла в главе 7, когда вы узнаете о применении матриц для геометрических преобразований, и в главе 14, когда вы узнаете о сингулярном разложении. В данной главе я познакомлю вас с поэлементными нормами, а начну с евклидовой нормы, которая на самом деле является прямым расширением векторной нормы на матрицы. Евклидова норма также называется нормой Фробениуса и вычисляется как квадратный корень из суммы всех элементов матрицы в квадрате (уравнение 6.1). Уравнение 6.1. Норма Фробениуса
Индексы i и j соответствуют M строкам и N столбцам. Также обратите внимание на подстрочную букву F, указывающую на норму Фробениуса. Норма Фробениуса еще называется нормой ℓ2 (ℓ – это причудливо выглядящая буква L). А норма ℓ2 получила свое название от общей формулы поэлементных -норм (обратите внимание, что норму Фробениуса можно получить при p = 2):
Нормы матр иц имеют несколько применений в машинном обучении и статистическом анализе. Одним из важных применений является регуляризация, целью которой является улучшение подгонки моделей и повышение обобщаемости моделей на данные, которые в модель ранее не подавались (позже в книге вы увидите соответствющие примеры). Базовая идея регуляризации состоит в добавлении матричной нормы в качестве стоимостной функции в алгоритм минимизации. Эта норма не дает модельным параметрам становиться слишком большими (регуляризация ℓ2, также именуемая гребневой регрессией) либо не поощряет разреженные ре1
Англ. entry-wise. – Прим. перев.
Нормы матриц 99
шения (регуляризация ℓ1, также именуемая лассо-регрессией). По сути дела, современные архитектуры глубокого обучения достигают впечатляющей результативности в решении задач компьютерного зрения, опираясь на матричные нормы. Еще одним применением нормы Фробениуса является вычисление меры «матричного расстояния». Расстояние между матрицей и самой собой равно 0, а расстояние между двумя разными матрицами увеличивается по мере того, как числовые значения в этих матрицах становятся все более непохожими. Матричное расстояние Фробениуса вычисляется просто путем замены матрицы A на матрицу C = A – B в уравнении 6.1. Указанное расстояние можно использовать в качестве критерия оптимизации в алгоритмах машинного обучения, например чтобы уменьшать размер хранилища изображений, минимизируя расстояние Фробениуса между уменьшенной и изначальной матрицами. Упражнение 6.2 проведет вас по простому примеру минимизации.
След матрицы и норма Фробениуса След матрицы – это сумма ее диагональных элементов, обозначаемая как tr(A), и существует он только для квадратных матриц. Обе следующие ниже матрицы имеют одинаковый след (14):
След обладает несколькими интересными свойствами. Например, след матрицы равен сумме собственных чисел1 матрицы и, следовательно, является мерой «объема» собственного пространства2. Многие свойства следа менее подходят для применения в науке о данных, но вот одно интересное исключение:
Другими словами, норму Фробениуса можно вычислять как квадратный корень из следа матрицы, умноженной на ее транспонированную версию. Причина, по которой это работает, состоит в том, что каждый диагональный элемент матрицы ATA определяется точечным произведением каждой строки на саму себя. Упражнение 6.3 поможет вам обследовать следовой метод вычисления нормы Фробениуса.
Англ. eigenvalues. – Прим. перев. Син. характеристическое пространство. – Прим. перев.
100 Матрицы. Часть 2
Пространства матрицы (столбцовое, строчное, нуль-пространство) Концепция пространств матрицы занимает центральное место во многих темах абстрактной и прикладной линейной алгебры. К счастью, пространства матрицы обладают концептуальной простотой и, по сути, представляют собой просто линейно-взвешенные комбинации разных признаков матрицы.
Столбцовое пространство Напомню, что линейно-взвешенная комбинация векторов предусматривает умножение на скаляр и суммирование множества векторов. Две модификации этой концепции будут расширять линейно-взвешенную комбинацию до столбцового пространства матрицы. Во-первых, мы концептуализируем матрицу в виде множества векторов-столбцов. Во-вторых, вместо того чтобы работать с определенным множеством скаляров, мы рассматриваем бесконечность действительно-значных скаляров. Бесконечное число скаляров дает бесконечное число способов комбинирования множества векторов. Это результирующее бесконечное множество векторов называется столбцовым пространством матрицы. Давайте конкретизируем все это с помощью нескольких числовых примеров. Мы начнем с простого – с матрицы, которая имеет только один столбец (что на самом деле то же самое, что и вектор-столбец). Ее столбцовое пространство – все возможные линейно-взвешенные комбинации этого столбца – можно выразить следующим образом: λ Î ℝ.
C(A) обозначает столбцовое пространство матрицы A, а символ Î обозначает принадлежность к множеству. В данном контексте это означает, что λ может быть любым возможным действительно-значным числом. Что означает указанное математическое выражение? Оно означает, что столбцовое пространство является множеством всех возможных шкалированных версий вектора-столбца [1 3]. Давайте рассмотрим несколько конкретных случаев. Находится ли вектор [1 3] в столцовом пространстве? Да, потому что этот вектор можно выразить как матрицу, умноженную на λ = 1. Как быть с [2 –6]? Тоже да, потому что этот вектор можно выразить как мат рицу, умноженную на λ = –2. А что насчет [1 4]? Здесь ответ – нет: вектор [1 4] не находится в столбцовом пространстве матрицы, потому что просто нет скаляра, который мог бы умножить матрицу, чтобы произвести этот вектор. Как выглядит столбцовое пространство? Для матрицы с одним столбцом столбцовое пространство представляет собой линию, которая проходит через начало координат в направлении вектора-столбца и простирается до
Пространства матрицы (столбцовое, строчное, нуль-пространство) 101
бесконечности в обоих направлениях. (В техническом плане линия не растягивается до буквальной бесконечности, потому что бесконечность не является вещественным числом. Но эта линия имеет сколь угодно большую длину – намного длиннее, чем наш ограниченный человеческий разум может представить, – поэтому во всех отношениях и с любой точки зрения об этой линии можно говорить как о бесконечно длинной.) На рис. 6.1 показано изображение столбцового пространства этой матрицы.
Рис. 6.1 Визуализация столбцового пространства матрицы с одним столбцом. Данное столбцовое пространство является одномерным подпространством
Теперь давайте рассмотрим матрицу с бóльшим числом столбцов. Мы оставим размерность столбца равной двум, чтобы иметь возможность визуа лизировать его на двумерном графике. Вот наша матрица и ее столбцовое пространство: λ Î ℝ.
У нас два столбца, поэтому допускаем два отдельных λ (оба являются действительно-значными числами, но могут отличаться друг от друга). Теперь вопрос состоит в том, каким будет множество всех векторов, которое может быть достигнуто некоторой линейной комбинацией этих двух векторовстолбцов. Ответ таков: все векторы в ℝ2. Например, вектор [–4 3] можно получить путем шкалирования двух столбцов соответственно на 11 и –15. Как я додумался до этих скалярных значений? Я использовал проекцию методом наименьших квадратов, о которой вы узнаете в главе 11. А пока вы можете
102 Матрицы. Часть 2 сосредоточиться на концепции, согласно которой эти два столбца могут быть надлежаще взвешенны, чтобы достичь любой точки в ℝ2. График А на рис. 6.2 показывает два столбца матрицы. Я не нарисовал столбцовое пространство матрицы, потому что оно будет всей осью целиком.
Рис. 6.2 Дополнительные примеры столбцовых пространств
Еще один пример в ℝ2. Вот новая матрица, которую мы рассмотрим: λ Î ℝ.
Какова размерность ее столбцового пространства? Можно ли достичь любой точки в ℝ2 с помощью некоторой линейно-взвешенной комбинации двух столбцов? Ответ на второй вопрос – нет. И если вы мне не верите, то попробуйте найти линейно-взвешенную комбинацию двух столбцов, которая создает вектор [3 5]. Это просто невозможно. Два столбца фактически являются коллинеарными (график В на рис. 6.2), потому что один уже является шкалированной версией другого. Это означает, что столбцовое пространство этой матр иц ы 2×2 по-прежнему остается просто линией – одномерным подпространством. Из этого можно вынести один вывод: наличие N столбцов в матрице не гарантирует, что столбцовое пространство будет -мерным. Размерность столбцового пространства равна числу столбцов только в том случае, если столбцы образуют линейно независимое множество. (Вспомните главу 3, в которой говорилось, что линейная независимость означает множество векторов, в котором ни один вектор невозможно выразить как линейно-взвешенную комбинацию других векторов в этом множестве.)
Пространства матрицы (столбцовое, строчное, нуль-пространство) 103
Заключительный пример столбцовых пространств служит для того, чтобы увидеть, что происходит, когда мы переходим в три измерения. Вот наша матрица и ее столбцовое пространство:
λ Î ℝ. Теперь в ℝ3 два столбца. Эти два столбца линейно независимы, и, значит, выразить один как шкалированную версию другого невозможно. Таким образом, столбцовое пространство этой матрицы является двумерным, но оно является двумерной плоскостью, которая вложена в ℝ3 (рис. 6.3). Столбцовое пространство этой матрицы представляет собой бесконечную двумерную плоскость, но указанная плоскость является всего лишь бесконечно малым срезом трех измерений. Это можно представить как бесконечно тонкий лист бумаги, который охватывает Вселенную.
Рис. 6.3 Двумерное столбцовое пространство матрицы, встроенной в три измерения. Две толстые линии изображают два столбца матрицы
На этой плоскости есть много векторов (т. е. много векторов, которые можно получить как линейную комбинацию двух векторов-столбцов), но еще больше векторов, которые не находятся на плоскости. Другими словами, есть векторы в столбцовом пространстве матрицы, и есть векторы вне столбцового пространства матрицы. Как узнать, что вектор находится в столбцовом пространстве матрицы? Это вовсе не тривиальный вопрос – на самом деле данный вопрос лежит
104 Матрицы. Часть 2 в основе линейного метода наименьших квадратов, и просто невозможно передать словами важность наименьших квадратов в прикладной математике и инженерии. Итак, как определить, что вектор находится в пространстве столбцов? В приводимых до сих пор примерах мы просто использовали некоторые догадки, арифметику и визуализацию. Смысл этого подхода состоял в том, чтобы привить интуитивное понимание, но эти методы, очевидно, не масштабирутся на более высокие измерения и на более сложные задачи. Количественные методы определения присутствия вектора в столбцовом пространстве матрицы основаны на концепции ранга матрицы, о котором вы узнаете позже в этой главе. А пока сосредоточьтесь на интуитивном понимании, что столбцы матрицы образуют векторное подпространство, которое может включать в себя все M-мерное пространство либо может быть некоторым подпространством меньшей мерности, и что важный вопрос состоит в том, не находится ли какой-либо другой вектор внутри этого подпространства (это означает, что вектор можно выразить как линейно-взвешенную комбинацию столбцов матрицы).
Строчное пространство После того как вы разберетесь в столбцовом пространстве матрицы, понять строчное пространство будет реально легко. Строчное пространство мат рицы – это, по сути, точно такая же концепция, однако вместо столбцов мы рассматриваем все возможные взвешенные комбинации строк. Строчное пространство обозначается как R(A). И поскольку операция транспонирования меняет местами строки и столбцы, можно также записать, что строчное пространство матрицы является столбцовым пространством транспонированной матрицы, другими словами, R(A) = C(AT). Между строчным и столбцовым пространствами матрицы есть несколько различий; например, строчное пространство (но не столбцовое пространство) инвариантно к операциям приведения строк. Но это выходит за рамки данной главы. Поскольку строчное пространство равно столбцовому пространству транспонированой матрицы, эти два матричных пространства идентичны для симметричных матриц.
Нуль-пространства Нуль-пространство незначительно, но важно отличается от столбцового пространства. Столбцовое пространство можно кратко свести к следующему ниже уравнению: Ax = b. Оно переводится на естественный так: «можно ли найти некоторый набор коэффициентов в x такой, что взвешенная комбинация столбцов в A произ-
Пространства матрицы (столбцовое, строчное, нуль-пространство) 105
водила бы вектор b?» Если ответ – да, то b Î C(A), и вектор x говорит о том, как взвешивать столбцы A, чтобы добраться до b. Нуль-пространство, напротив, можно кратко свести к следующему ниже уравнению: Ay = 0. Оно переводится на естественный так: «можно ли найти некоторый набор коэффициентов в y такой, что взвешенная комбинация столбцов в A давала бы вектор нулей 0?» Мгновенный осмотр покажет ответ, который действует для любой возможной матрицы A: установить y = 0! Очевидно, что умножение всех столбцов на нули приведет к получению вектора нулей. Но это решение будет тривиальным, и мы его исключаем. Следовательно, возникает вопрос: «можно ли найти множество весов – не все из которых равны 0, – которое производит вектор нулей?» Любой вектор y, который может удовлетворять этому уравнению, находится в нуль-пространстве A, которое мы записываем как N(A). Давайте начнем с простого примера. Прежде чем читать следующий далее текст, посмотрим, сможете ли вы найти такой вектор y:
Вы придумали вектор? Мой будет таким: [7.34, 7.34]. Готов держать пари размером с Лас-Вегас, что вы не придумали тот же вектор. Возможно, вы придумали [1, 1] или [–1, –1]. А может быть, [2, 2]? Думаю, вы видите, к чему это ведет – существует бесконечное число векторов y, которые удовлетворяют Ay = 0 для этой конкретной матрицы A. И все эти векторы можно выразить в виде некоторой шкалированной версии любого из этих вариантов. Таким образом, нуль-пространство данной матрицы можно выразить как λ Î ℝ.
Вот еще один пример матрицы. Опять же, попробуйте найти набор коэффициентов такой, что взвешенная сумма столбцов давала бы вектор нулей (то есть найдите y в Ay = 0):
Готов сделать еще более высокую ставку на то, что вы не сможете найти такой вектор. Но это не потому, что я не верю в ваши способности (я очень высокого мнения о своих читателях!), а потому, что в этой матрице нет нульпространства. Формально мы говорим, что нуль-пространство этой матрицы является пустым множеством: N(A) = <>.
106 Матрицы. Часть 2 Взгляните на два примера матриц в этом подразделе еще раз. Вы заметите, что первая матрица содержит столбцы, которые могут быть сформированы как шкалированные версии других столбцов, тогда как вторая матрица содержит столбцы, которые образуют независимое множество. Это вовсе не совпадение: между размерностью нуль-пространства и линейной независимостью столбцов в матрице существует тесная взаимосвязь. Точная природа этой взаимосвязи задается теоремой о ранге и нульности1, о которой вы узнаете в дополнении А к книге. Но их ключевой момент заключается в следующем: нуль-пространство является пустым, когда столбцы матрицы образуют линейно независимое множество. Рискуя показаться избыточным, повторю вот этот важный момент: полноранговые матрицы и матрицы с полным столбцовым рангом имеют пустые нуль-пространства, тогда как рангово-пониженные матрицы имеют непус тые (нетривиальные) нуль-пространства. Библиотека SciPy в Python содержит функцию, которая вычисляет нульпространство матрицы. Давайте подтвердим наши результаты с помощью исходного кода: A = np.array([ [1,-1], [-2,2] ]) B = np.array([ [1,-1], [-2,3] ]) print( scipy.linalg.null_space(A) ) print( scipy.linalg.null_space(B) ) Вот результат: [[0.70710678] [0.70710678]] []
Второй результат ([]) – это пустое множество. Почему Python выбрал для нуль-пространства A числовые значения 0.70710678? Разве не было бы легче читать, если бы Python выбрал 1? Учитывая бесконечность возможных векторов, Python вернул единичный вектор2 (норму этого вектора можно вычислить в уме, зная, что » .7071). Единичные векторы удобны в работе и обладают несколькими приятными свойствами, включая численную стабильность. Поэтому компьютерные алгоритмы часто возвращают единичные векторы в качестве базисов подпространств. Вы увидите это снова с собственными векторами и сингулярными векторами. Как выглядит нуль-пространство? На рис. 6.4 показаны векторы-строки и нуль-пространство матрицы A. Почему я вывел на график векторы-строки вместо векторов-столбцов? Оказывается, что строчное пространство расположено ортогонально нуль1
Англ. rank-nullity theorem. См. https://en.wikipedia.org/wiki/Rank-nullity_theorem. – Прим. перев. 2 То есть с векторной величиной, равной единице. – Прим. перев.
Пространства матрицы (столбцовое, строчное, нуль-пространство) 107
пространству. Это не по какой-то причудливой эзотерической причине; как раз наоборот, это зашито прямо в определение нуль-пространства как Ay = 0. Переписывание этого уравнения для каждой строки матрицы (ai) приводит к выражению ai y = 0; другими словами, точечное произведение между каждой строкой и нуль-пространственным вектором равно 0.
Рис. 6.4 Визуализация нуль-пространства матрицы
К чему весь этот сыр-бор по поводу нуль-пространств? Излишнее внимание векторам, которые могут умножать матрицу, чтобы получать вектор нулей, вполне может показаться странным. Однако, как вы узнаете в главе 13, нуль-пространство является краеугольным камнем в отыскании собственных и сингулярных векторов. Заключительная мысль данного раздела такова: каждая матрица имеет четыре ассоциированных подпространства; вы узнали о трех (столбцовом, строчном, нуль-пространстве). Четвертое подпространство называется правым нуль-пространством1 и представляет собой строчное нуль-пространство. Оно нередко записывается как нуль-пространство транспонированной мат рицы: N(AT). Традиционная учебная программа по математике теперь потратила бы несколько недель на обследование тонкостей и взаимосвязей между четырьмя подпространствами. Матричные подпространства заслуживают изучения из-за их завораживающей красоты и совершенства, но на такой уровень глубины мы погружаться не будем. 1
Существует два нуль-пространства: левое и правое. Когда пишут просто «нульпространство», то имеют в виду левое нуль-пространство. – Прим. перев.
108 Матрицы. Часть 2
Ранг Ранг – это число, ассоциированное с матрицей. Оно связано с размерностями матричных подпространств и имеет важные последствия для матричных операций, включая инвертирование матриц и определение числа решений системы уравнений. Как и в случае с другими темами в этой книге, существуют богатые и подробные теории ранга матрицы, но здесь я сосредоточусь на том, что вам нужно знать для науки о данных и связанных с ней применений. Начну с перечисления нескольких свойств ранга. Без какого-либо определенного порядка важности: ранг представляет собой неотрицательное целое число, поэтому мат рица может иметь ранг 0, 1, 2, . но не −2 или 3.14; каждая матрица имеет один уникальный ранг; матрица не может одновременно иметь несколько отдельных рангов (Это также означает, что ранг является характеристикой матрицы, а не строк или столбцов.); ранг матрицы указывается с использованием r(A) или rank(A). Также уместно говорить, что «A является -ранговой матрицей»; максимально возможный ранг матрицы – это меньшее из числа ее строк либо столбцов. Другими словами, максимально возможный ранг равен min; матрица с максимально возможным рангом называется «полноранговой». Матрица с рангом r tol)
M – это рассматриваемая матрица, S – вектор сингулярных чисел, а tol – порог допуска. И значит, NumPy на самом деле не подсчитывает ненулевые сингулярные числа; он подсчитывает сингулярные числа, которые превышают некоторый порог. Точное пороговое значение зависит от числовых значений в матрице, но обычно оно примерно на 10–12 порядков меньше, чем элементы матрицы. Это означает, что NumPy принимает решение о том, какие числа достаточно малы, чтобы считаться «фактически нулевыми». Я, конечно же, не критикую NumPy – это правильный поступок! (Другие программы числовой обработки, такие как MATLAB и Julia, вычисляют ранг таким же образом.) Но зачем это делать? Почему бы просто не подсчитывать ненулевые сингулярные числа? Ответ в том, что допуск поглощает малые числовые неточности, которые могут возникать из-за ошибки вычислительного округления. Наличие допуска также позволяет игнорировать незначительные шумы, которыми могут, например, загрязняться датчики сбора данных. Эта идея используется для очистки данных, сжатия и уменьшения размерности. Рисунок 6.6 иллюстрирует эту концепцию.
Рис. 6.6 Ранг представляющей двумерную плоскость матрицы 3×3 может считаться равным 3 при наличии малого количества шума. Крайняя правая диаграмма показывает перспективу взгляда непосредственно через поверхность плоскости
Применения ранга Существует целый ряд применений ранга матрицы. В данном разделе я представлю два из них.
Применения ранга 115
В столбцовом пространстве В главе 4 вы узнали о столбцовом пространстве матрицы и о том, что важным вопросом в линейной алгебре является принадлежность/непринадлежность вектора к столбцовому пространству матрицы (который математически можно записать как v Î C(A)?). Я также написал, что строгий и масштабируемый ответ на данный вопрос зависит от понимания ранга матрицы. Прежде чем рассказать об алгоритме определения v Î C(A), мне нужно кратко представить процедуру, именуемую расширением1 матрицы. Расширить матрицу означает добавить дополнительные столбцы в правую часть матрицы. Вы начинаете с «базовой» матрицы M×N и «дополнительной» матрицы M×K. Расширенная матрица имеет размер M×(N + K). Расширение двух матриц допустимо при условии, что они имеют одинаковое число строк (у них может быть разное число столбцов). Вы увидите расширенные матри цы здесь, в этом разделе, и снова в главе 10 при решении систем уравнений. Ниже приведен пример, иллюстрирующий данную процедуру:
С учетом этого обязательного отклонения от темы вот алгоритм определения принадлежности/непринадлежности вектора к столбцовому пространству матрицы: 1. Раширить матрицу вектором. Обозначим изначальную матрицу через A и раширенную матрицу – через Ã. 2. Вычислить ранги двух матриц. 3. Сравнить два ранга. Будет один из двух возможных исходов: a) rank(A) = rank(Ã) Вектор v находится в столбцовом пространстве матрицы A. b) rank(A) min, и почему это происходит?
Рис. 6.8 Решение к упражнению 6.4
Упражнение 6.6 Продемонстрируйте правило сложения ранга матриц (r(A + B) £ r(A) + r(B)), создав три пары одноранговых матриц, просуммированных с 1) нуль-ранговой, 2) одноранговой и 3) двуранговой. Затем повторите это упражнение, используя умножение матриц вместо их сложения. Упражнение 6.7 Поместите исходный код из упражнения 6.5 в функцию Python, которая на входе принимает параметры M и r и на выходе выдает случайную матрицу M×M ранга r. В двойном цикле for создайте пары матриц 20×20 с отдельными рангами, варьирующимися от 2 до 15. Складывайте и умножайте эти матри цы и сохраняйте ранги этих результирующих матриц. Указанные ранги должны быть организованы в матрицу и визуализированы как функция рангов отдельных матриц (рис. 6.9). Упражнение 6.8 Интересно, что все матрицы A, AT, ATA и AAT имеют одинаковый ранг. Напишите исходный код, чтобы это продемонстрировать, используя случайные матрицы различных размеров, очертаний (квадратные, высокие, широкие) и рангов. Упражнение 6.9 Цель этого упражнения – ответить на вопрос: v Î C(A)? Создайте мат рицу A Î ℝ4×3 ранга 3 и вектор v Î ℝ4 с использованием чисел, взятых из нормального распределения в случайном порядке. Следуйте описанному ранее алгоритму определения принадлежности/непринадлежности вектора столбцовому пространству матрицы. Повторите исходный код несколько раз,
126 Матрицы. Часть 2 чтобы попробовать найти неуклонную закономерность. Затем используйте матрицу A Î ℝ4×4 ранга 4. Готов поспорить на один миллион биткойнов1, что вы всегда будете находить, что v Î C(A), когда A – это случайная матрица 4×4 (при условии отсутствия ошибок программирования). Что именно вселяет в меня уверенность в вашем ответе2?
Рис. 6.9 Результаты упражнения 6.7
В целях дополнительной сложности поместите этот исходный код в функцию, которая возвращает True либо False в зависимости от результата проверки и которая вызывает исключение (то есть полезное сообщение об ошибке), если размер вектора не соответствует расширению матрицы. Упражнение 6.10 Напомню, что определитель рангово-пониженной матрицы – теоретически – равен нулю. В данном упражнении вы проверите эту теорию на практике. Выполните следующие ниже шаги. 1. Создайте квадратную случайную матрицу. 2. Понизьте ранг матрицы. Ранее вы делали это путем умножения прямо угольных матриц; здесь задайте один столбец кратным другому столбцу. 3. Вычислите определитель и сохраните его абсолютное значение. Выполните эти три шага в двойном цикле for: один цикл для матриц размером от 3×3 до 30×30 и второй цикл, который повторяет три шага сто раз (повторение эксперимента широко применяется при симулировании шумовых данных). Наконец, выведите определитель, усредненный по ста повторам, на линейный график как функцию от числа элементов в матрице. Теория линейной алгебры предсказывает, что эта линия (то есть определители всех 1 2
Nota bene. У меня не так много биткойнов, ¯\_(ツ)_/¯ Потому что столбцовое пространство матрицы полного ранга охватывает все ℝM, и, следовательно, все векторы в ℝM обязательно находятся в пространстве столбцов.
Упражнения по программированию 127
рангово-пониженных матриц) равна нулю независимо от размера матрицы. На рис. 6.10 показано иное, отражая вычислительные трудности, связанные с точным вычислением определителя. Я подверг данные логарифмическому преобразованию с целью повышения наглядности; вы должны проинспектировать график, используя логарифмическую и линейную шкалы.
Рис. 6.10 Результаты упражнения 6.10
7 Применения матриц
Надеюсь, что теперь, после насыщенных теорией двух последних глав, вы чувствуете себя так, словно только что закончили интенсивную тренировку в тренажерном зале: измотанные, но полные энергии. Данная глава должна быть похожа на велосипедную прогулку по холмам в сельской местности: временами она будет требовать усилий, но при этом предлагать свежую и вдохновляющую перспективу. Описанные в данной главе приложения в общих чертах основаны на приложениях, описанных в главе 4. Я сделал это, чтобы иметь несколько общих нитей изложения, которые связывают главы о векторах и о матрицах. И потому, что хочу, чтобы вы увидели, что, несмотря на все более усложняющие ся концепции и приложения, по мере продвижения по линейной алгебре фундамент по-прежнему строится на тех же простых принципах, таких как линейно-взвешенные комбинации и точечное произведение.
Матрицы ковариаций многопеременных данных В главе 4 вы узнали, как вычислять коэффициент корреляции Пирсона в виде векторного точечного произведения двух переменных, деленного на произведение норм векторов. Эта формула была рассчитана на две переменные (например, рост и вес); а что, если у вас несколько переменных (например, рост, вес, возраст, еженедельные упражнения. )? Вы могли бы представить, что пишете двойной цикл for для всех переменных и применяете формулу двухпеременной корреляции ко всем парам переменных. Но это будет громоздко и неэлегантно, а следовательно, противоречит духу линейной алгебры. Цель этого раздела – показать, как вычислять матрицы ковариаций и корреляций из наборов многопеременных1 данных. 1
Англ. multivariate; син. многопараметрический; относится к нескольким независимым переменным в статистической модели или модели МО. – Прим. перев.
Матрицы ковариаций многопеременных данных 129
Давайте начнем с ковариации. Ковариация – это просто числитель уравнения корреляции, другими словами, точечное произведение двух переменных, центрированных по среднему значению. Ковариация интерпретируется так же, как корреляция (положительная, когда переменные движутся вместе; отрицательная, когда переменные движутся порознь; нулевая, когда переменные не имеют линейной зависимости), за исключением того, что в ковариации сохраняется шкала данных и, следовательно, не ограничена ±1. Ковариация также имеет коэффициент нормализации n – 1, где n – это число точек данных. Указанная нормализация не дает ковариации увеличиваться по мере суммирования большего числа значений данных (аналогично делению на N, чтобы преобразовать сумму в среднее значение). Вот уравнение ковариации:
Как и в главе 4, если через x обозначить переменную, центрированную по среднему значению переменной x, то ковариация будет равна xTy/(n – 1). Ключевым моментом в реализации указанной формулы для нескольких переменных является то, что матричное умножение представляет собой организованный набор точечных произведений между строками левой матри цы и столбцами правой матрицы. Итак, вот что мы делаем: создаем матрицу, в которой каждый столбец соответствует каждой переменной (переменная – это признак данных). Обозначим эту матрицу через X. Теперь умножение XX не имеет смысла (и, вероятно, даже недопустимо, потому что матрицы данных, как правило, являются высокими, следовательно, M > N). Но если бы мы транспонировали первую матрицу, то строки матрицы XT соответствовали бы столбцам матрицы X. Следовательно, в произведении матриц XTX кодируются все попарные ковариации (при условии что столбцы центрированы по среднему значению и затем делятся на n – 1). Другими словами, (i, j)-й элемент в мат рице ковариаций является точечным произведением между признаками данных i и j. Матричное уравнение для матр иц ы ковариаций выглядит элегантно и компактно:
Матрица C симметрична. Это вытекает из доказательства в главе 5, что любая матрица, умноженная на ее транспонированную версию, является квадратно-симметричной, но это также имеет смысл статистически: ковариация и корреляция симметричны, и, стало быть, например, корреляция между высотой и весом такая же, как и корреляция между весом и ростом. Каковы диагональные элементы C? Они содержат ковариации каждой переменной с самой собой, которые в статистике называются дисперсией1, 1
Англ. variance. – Прим. перев.
130 Применения матриц и количественно определяют разбросанность вокруг среднего значения (дисперсия – это возведенное в квадрат стандартное отклонение).
Зачем транспонировать левую матрицу? В транспонировании левой матрицы нет ничего особенного. Если ваша матрица данных организована как признаки по наблюдениям, тогда ее матрица ковариаций равна XXT. Если вы когда-либо будете сомневаться в том, какую матрицу следует транспонировать: левую либо правую, – подумайте о том, как применить правила умножения матриц, чтобы получить матрицу в формате «признаки по признакам». Матрицы ковариаций всегда организованы как «признаки по признакам».
Пример в онлайновом исходном коде создает матрицу ковариаций из общедоступного набора данных статистики преступности. Набор данных содержит более ста признаков социальной, экономической, образовательной и жилищной информации в различных сообществах по всей территории США1. Цель набора данных – использовать указанные признаки для предсказывания уровня преступности, но здесь мы будем использовать его для инспектирования матриц ковариации и корреляции. После импорта и небольшой обработки данных (описанной в онлайновом коде) у нас будет матрица данных под названием dataMat. Следующий ниже исходный код показывает, как вычислять матрицу ковариаций: datamean = np.mean(dataMat,axis=0) # вектор средних значений признаков dataMatM = dataMat — datamean # среднецентрировать, # используя транслирование covMat = dataMatM.T @ dataMatM # матрица данных, умноженная на # ее транспонированную версию covMat /= (dataMatM.shape[0]-1) # разделить на N–1
На рис. 7.1 показано изображение матрицы ковариаций. Прежде всего она выглядит просто замечательно, не правда ли? На своей «повседневной работе» я работаю с наборами многопеременных данных в качестве профессора нейробиологии, и разглядывание матриц ковариаций никогда не переставало вызывать улыбку на моем лице. В этой матрице светлые цвета указывают на переменные, которые ковариируют положительно (например, процент разведенных мужчин по сравнению с числом людей, живущих в бедности), темные цвета указывают на переменные, которые ковариируют отрицательно (например, процент разведенных мужчин по сравнению со средним доходом), а серые цвета указывают на переменные, которые друг с другом не связаны. Как вы узнали в главе 4, вычисление корреляции на основе ковариации просто предусматривает шкалирование на нормы векторов. Его можно пере1
Редмонд М. А. и Бавея А. Программный инструмент, управляемый данными, для обеспечения совместного обмена информацией между полицейскими департаментами (M. A. Redmond and A. Baveja, “A Data-Driven Software Tool for Enabling Cooperative Information Sharing Among Police Departments,” European Journal of Operational Research 141 (2002): 660–678).
Геометрические преобразования посредством умножения матриц на векторы 131
ложить в матричное уравнение, которое позволит вычислять матрицу корреляций данных без циклов for. Упражнение 7.1 и упражнение 7.2 познакомят с процедурой. Как я писал в главе 4, прежде чем переходить к следующему разделу, призываю вас проработать эти упражнения. Матрица ковариаций данных
Рис. 7.1 Матрица ковариаций данных
Заключительное примечание: в NumPy есть функции вычисления мат риц ковариаций и корреляций (соответственно, np.cov() и np.corrcoef()). На практике использовать эти функции удобнее, чем писать исходный код самостоятельно. Но – как всегда в этой книге – я хотел бы, чтобы вы поняли математику и механизмы, которые реализованы в этих функциях. Следовательно, в этих упражнениях вы должны реализовать ковариацию как прямое переложение формул вместо вызова функций NumPy.
Геометрические преобразования посредством умножения матриц на векторы В главе 5 упоминалось, что одной из целей умножения матрицы на вектор является применение геометрического преобразования к множеству координат. В данном разделе вы это увидите на двумерных статичных изображениях и в анимации. Попутно вы узнаете о матрицах чистого поворота и о создании анимации данных на Python.
132 Применения матриц «Матрица чистого поворота» поворачивает вектор, сохраняя при этом его длину. Подумайте о стрелках аналоговых часов: с течением времени стрелки вращаются, но не меняются по длине. Двумерная матрица поворота выражается как
Матрица чистого поворота является примером ортогональной матрицы. Я опишу ортогональные матрицы подробнее в главе 9, но хотел бы указать, что столбцы матрицы T ортогональны (их точечное произведение равно cos(θ)sin(θ) – sin(θ)cos(θ)) и являются единичными векторами (вспомним тригонометрическое тождество, что cos2(θ) + sin2(θ) = 1). Для того чтобы применить эту трансформационную матрицу, надо установить θ равной некоторому углу поворота по часовой стрелке, а затем умножить матрицу T на матрицу геометрических точек 2×Т, где каждый столбец в этой матрице содержит координаты (X,Y) по каждой из Т точек данных. Например, θ = 0 не изменит местоположения точек (потому что θ = 0 означает T = I); θ = π/2 повернет точки на 90° вокруг начала координат. В качестве простого примера возьмем множество точек, выровненных по вертикальной линии, и эффект умножения этих координат на T. На рис. 7.2 я задал θ = π/5. Поворот на 36°
Рис. 7.2 Поворот точек вокруг начала координат с помощью матрицы чистого поворота
Геометрические преобразования посредством умножения матриц на векторы 133
Прежде чем продолжить работу с этим разделом, пожалуйста, ознакомьтесь с онлайновым исходным кодом, которым генерируется этот рисунок. Убедитесь, что понимаете, как написанная выше математика переводится в исходный код, и найдите минутку, чтобы поэкспериментировать с разными углами поворота. Вы также можете попробовать выяснить, как сделать поворот против часовой стрелки вместо поворота по часовой стрелке; ответ находится в сноске1. Давайте сделаем обследование поворотов более захватывающим, применив «нечистые» повороты (то есть растягивание и поворот, а не только поворот) и анимировав преобразования. В частности, мы будем плавно корректировать трансформационную матрицу в каждом кадре фильма. Анимации на Python создаются несколькими способами; применяемый здесь метод предусматривает определение функции Python, которая создает содержимое рисунка для каждого кадра фильма, затем вызывает процедуру библиотеки matplotlib, чтобы выполнять эту функцию на каждой итерации фильма. Я называю этот фильм «Шаткий круг». Окружности определяются мно жеством точек cos(θ) и sin(θ) для вектора углов θ, которые варьируются от 0 до 2π. Трансформационная матрица задана в следующем виде:
Почему я выбрал именно эти значения и как интерпретировать трансформационную матрицу? В общем случае диагональные элементы шкалируют координаты по осям x и y, тогда как внедиагональные элементы растягивают обе оси. Точные значения в приведенной выше матрице были выбраны путем подбора чисел до тех пор, пока я не нашел что-то, что, по моему мнению, выглядело замечательно. Позже, в упражнениях, у вас будет возможность обследовать эффекты изменения трансформационной матрицы. По ходу фильма значение φ будет плавно переходить от 1 к 0 и обратно к 1, подчиняясь формуле φ = x2, –1 £ x £ 1. Обратите внимание, что T = I при φ = 1. Исходный код анимации данных на Python можно подразделить на три части. Первая часть состоит в настройке рисунка: theta = np.linspace(0,2*np.pi,100) points = np.vstack((np.sin(theta),np.cos(theta))) fig,ax = plt.subplots(1,figsize=(12,6)) plth, = ax.plot(np.cos(x),np.sin(x),’ko’)
Результатом работы метода ax.plot является переменная plth, представляющая собой дескриптор, или указатель на объект графика. Этот дескриптор позволяет обновлять место расположения точек, не перерисовывая рисунок с нуля в каждом кадре. Вторая часть состоит в определении функции, которая обновляет оси в каждом кадре: 1
Поменяйте местами знаки минус в функциях синуса.
134 Применения матриц def
aframe(ph): # создать и применить трансформационную матрицу T = np.array([ [1,1-ph],[0,1] ]) P = T@points
# обновить местоположение точек plth.set_xdata(P[0,:]) plth.set_ydata(P[1,:]) return plth
Наконец, определяем параметр преобразования φ и вызываем функцию matplotlib, которая создает анимацию: phi = np.linspace(-1,1-1/40,40)**2 animation.FuncAnimation(fig, aframe, phi, interval=100, repeat=True)
На рис. 7.3 показан один кадр фильма, и вы можете просмотреть все видео, выполнив исходный код. По общему признанию, этот фильм вряд ли получит какие-либо награды, но он показывает, как умножение матриц применяется в анимации. Графика в CGI-фильмах и видеоиграх немного сложнее, потому что в них используются математические объекты, именуемые кватернионами, то есть векторы в ℝ4, допускающие повороты и трансляции в 3D. Но принцип – умножение матрицы геометрических координат на трансформационную матрицу – в точности тот же.
Рис. 7.3 Кадр из фильма «Шаткий круг»
Обнаружение признаков изображения 135
Прежде чем приступать к выполнению упражнений данного раздела, рекомендую потратить немного времени на то, чтобы поиграть с исходным кодом этого раздела. В частности, измените трансформационную матрицу, установив одному из диагональных элементов значение .5 или 2, измените нижний левый внедиагональный элемент вместо верхнего правого внедиагонального элемента (либо в дополнение к нему), параметризуйте один из диагональных элементов вместо внедиагонального элемента и т. д. И вот вопрос: сможете ли вы выяснить, как заставить круг качаться влево, а не вправо? Ответ содержится в сноске1.
Обнаружение признаков изображения В данном разделе я познакомлю вас с фильтрацией изображений, то есть механизмом обнаружения признаков изображения. Фильтрация изображений на самом деле является расширением фильтра временного ряда, так что ознакомление с главой 4 пойдет вам здесь на пользу. Напомню, что для фильтрации или обнаружения признаков в сигнале временного ряда разрабатывается ядро, а затем создается временной ряд точечных произведений между ядром и накладывающимися друг на друга сегментами сигнала. Фильтрация изображений работает таким же образом, только в 2D, а не в 1D. Мы конструируем двумерное ядро, а затем создаем новое изображение, содержащее «точечные произведения» между ядром и накладывающимися окнами изображения. Я написал «точечные произведения» в кавычках, как бы извиняясь, потому что здесь операция формально не совпадает с точечным произведением векторов. Вычисление одно и то же – поэлементное умножение и сумма, однако операция выполняется между двумя матрицами, поэтому реализация представляет собой адамарово умножение и суммирование по всем элементам матрицы. График А на рис. 7.4 иллюстрирует указанную процедуру. Сущест вуют дополнительные детали свертки, – например, дополнение изображения таким образом, чтобы результат был того же размера, – о которых вы узнаете из книги по обработке изображений. Здесь же я хотел бы, чтобы вы сосредоточились на аспектах линейной алгебры, в частности на идее о том, что точечное произведение количественно определяет взаимосвязь между двумя векторами (или матрицами) и его можно применять для обнаружения признаков и фильтрации. Прежде чем перейти к анализу, кратко объясню, как создается двумерное гауссово ядро. Двумерный гауссиан задается следующим ниже уравнением: G = exp(–(X 2 + Y 2)/σ). Несколько замечаний об этом уравнении: exp обозначает натуральную экспоненту (константа e = 2,71828…), а exp(x) используется вместо ex, когда 1
Установите значение нижнего правого элемента равным –1.
136 Применения матриц экспоненциальный член – длинный. X и Y – это двумерные решетки с координатами x, y, на которых вычисляется функция. Наконец, σ – это параметр функции, который часто называют «коэффициентом масштаба», или «шириной»: меньшие его значения делают гауссиан уже, тогда как бóльшие значения делают ее шире. В настоящий момент я зафиксирую данный параметр на определенных значениях, и вы сможете обследовать последствия изменения указанного параметра в упражнении 7.6.
Результаты Изображение Ядро
Рис. 7.4 Механизм свертки изображения
Вот как эта формула переводится в исходный код: Y, X = np.meshgrid(np.linspace(-3,3,21), np.linspace(-3,3,21)) kernel = np.exp( -(X**2+Y**2) / 20 ) kernel = kernel / np.sum(kernel) # нормализовать
Решетки X и Y изменяются от –3 до +3 с шагом 21. Параметр ширины жестко задан равным 20. Третья строка нормализует значения в ядре таким
Обнаружение признаков изображения 137
образом, чтобы сумма по всему ядру была равна 1. Это позволяет поддерживать изначальную шкалу данных. При надлежащей нормализации каждый шаг свертки – и, следовательно, каждый пиксел в отфильтрованном изображении – становится средневзвешенным значением окружающих пикселов, при этом веса определяются гауссианом. Вернемся к текущей задаче: мы сгладим матрицу случайных чисел аналогично тому, как мы сглаживали временной ряд случайных чисел в главе 4. Матрица случайных чисел, гауссово ядро и результат свертки показаны на рис. 7.4. Следующий ниже исходный код Python показывает реализацию свертки изображения. И вновь вспомните свертку временного ряда в главе 4, чтобы понять, что идея та же самая, но с дополнительным геометрическим измерением, требующим дополнительного цикла for: for rowi in range(halfKr,imgN-halfKr): # прокрутить строки for coli in range(halfKr,imgN-halfKr): # прокрутить столбцы # вырезать кусок изображения pieceOfImg = imagePad[ rowi-halfKr:rowi+halfKr+1:1, coli-halfKr:coli+halfKr+1:1 ] # точечное произведение: # адамарово умножение и суммирование dotprod = np.sum( pieceOfImg*kernel ) # сохранить результат для этого пиксела convoutput[rowi,coli] = dotprod
Реализация свертки в виде двойного цикла for на самом деле неэффективна с вычислительной точки зрения. Оказывается, что свертку можно реализовать быстрее и с меньшим объемом исходного кода в частотном диапазоне. Это происходит благодаря теореме о свертке, которая гласит, что свертка во временном (или пространственном) диапазоне равна умножению в частотном диапазоне. Полное изложение теоремы о свертке выходит за рамки этой книги; я упоминаю о ней здесь для того, чтобы обосновать рекомендацию использовать функцию SciPy convolve2d вместо двойного цикла for, в особенности в случае больших изображений. Давайте попробуем сгладить реальный фотоснимок. Мы будем использовать фотографию музея Стеделийк в Амстердаме, которую я с любовью называю «ванна из космоса». Это изображение является трехмерной матрицей, поскольку в нем есть строки, столбцы и глубина – глубина содержит значения интенсивности пикселов из красного, зеленого и синего цветовых каналов. Указанное изображение хранится в виде матрицы размером ℝ1675×3000×3. Формально она называется тензором, потому что является кубом, а не развернутой таблицей чисел. Пока что мы сведем ее к двумерной матрице путем преобразования в оттенки серого. Это упрощает вычисления, хотя в этом нет необходимости. В упражнении 7.5 вы узнаете, как сглаживать трехмерное изображение. На рис. 7.5 показано изображение в оттенках серого до и после применения гауссова ядра сглаживания.
138 Применения матриц
Рис. 7.5 Фотография музея-ванны до и после сносного сглаживания
В обоих этих примерах использовалось гауссово ядро. Сколько существует других ядер? Бесконечное число! В упражнении 7.7 вы протестируете два других ядра, которые применяются для выявления вертикальных и горизонтальных линий. Эти детекторы признаков широко применяются в обработке изображений (и используются нейронами мозга для обнаружения краев в узорах света, попадающих на сетчатку). Ядра свертки изображений являются главнейшей темой в компьютерном зрении. Собственно говоря, невероятная результативность сверточных нейронных сетей (архитектуры глубокого обучения, оптимизированной под компьютерное зрение) полностью обусловлена способностью сети искусно создавать оптимальные фильтерные ядра посредством автоматического обучения.
Резюме Буду говорить просто: вся суть в том, – повторю это еще раз – что невероятно важные и изощренные методы в науке о данных и машинном обучении построены на простых линейно-алгебраических принципах.
Упражнения по программированию Упражнения по матрицам ковариаций и корреляций Упражнение 7.1 В данном упражнении вы преобразуете матрицу ковариаций в матрицу корреляций. Процедура предусматривает деление каждого элемента матри цы (то есть ковариации между каждой парой переменных) на произведение дисперсий этих двух переменных.
Упражнения по программированию 139
Указанная процедура реализуется путем пред- и постпозиционного умно жения матрицы ковариаций на диагональную матрицу, содержащую инвертированные стандартные отклонения каждой переменной (стандартное отклонение – это квадратный корень из дисперсии). Стандартные отклонения инвертируются, потому что нам нужно делить на отклонения, хотя мы будем умножать матрицы. Причиной пред- и постпозиционного умножения на стандартные отклонения является особое свойство пред- и постпозиционного умножения на диагональную матрицу, которое было объяснено в упражнении 5.11. Уравнение 7.1 показывает формулу. Упражнение 7.1. Корреляция из ковариации R = SCS. C – это матрица ковариаций, а S – диагональная матрица взаимообратных стандартных отклонений по каждой переменной (то есть -я диагональ равна 1/σi, где σi – это стандартное отклонение переменной i). В данном упражнении ваша цель – вычислить матрицу корреляций из мат рицы ковариацй, переложив уравнение 7.1 в исходный код Python. И тогда вы сможете воспроизвести рис. 7.6. Матрица ковариаций данных
Матрица корреляций данных
Рис. 7.6 Решение упражнения 7.1
Упражнение 7.2 В библиотеке NumPy есть функция np.corrcoef(), которая возвращает мат рицу корреляций с учетом входной матрицы данных. Примените эту функцию, чтобы воспроизвести матрицу корреляций, созданную вами в предыдущем упражнении. Покажите обе матрицы и их разницу на рисунке, подобном рис. 7.7, чтобы подтвердить их одинаковость. Далее проинспектируйте исходный код функции np.corrcoef(), выполнив ??np.corrcoef(). В NumPy используется несколько иная реализация транслируемого деления на стандартные отклонения вместо пред- и постпозицион-
140 Применения матриц ного умножения на диагональную матрицу, состоящую из инвертированных стандартных отклонений, но вы должны суметь понять, как их реализация в исходном коде соответствует математике и исходному коду Python, который вы написали в предыдущем упражнении. Моя матрица корреляций
Матрица корреляций NumPy
Рис. 7.7 Решение упражнения 7.2. Обратите внимание на разницу в шкалировании цвета
Упражнения по геометрическим преобразованиям Упражнение 7.3 Цель этого упражнения – показать точки в круге до и после применения преобразования, аналогично тому, как я показал линию до и после поворота на рис. 7.2. Примените следующую ниже матрицу преобразования, а затем создайте график, который выглядит как на рис. 7.8:
Упражнение 7.4 Теперь перейдем к еще одному фильму. Я называю его «Сворачивающаяся ДНК». На рис. 7.9 показан один кадр фильма. Процедура та же, что и для «Шаткого круга», – настроить рисунок, создать функцию Python, которая применяет трансформационную матрицу к матрице координат, сообщить библиотеке matplotlib, что нужно создать анимацию с использованием этой функции. Используйте следующую ниже трансформационную матрицу:
Упражнения по программированию 141 Изначальный Преобразованный
Рис. 7.8 Решение к упражнению 7.3
Рис. 7.9 Решение к упражнению 7.4
142 Применения матриц
Упражнения по обнаружению признаков изображения Упражнение 7.5 Сгладьте трехмерное изображение музея-ванны (если вам нужна подсказка, то обратитесь к сноске1). Результат работы функции convolve2d имеет тип данных float64 (вы можете убедиться в этом сами, введя variableName.dtype). Однако plt.imshow выдаст предупреждение об отсечении числовых значений, и изображение не будет отображаться надлежащим образом. Следовательно, вам нужно будет преобразовать результат свертки в uint8. Упражнение 7.6 Для каждого цветового канала вовсе не нужно использовать одно и то же ядро. Измените параметр ширины гауссианы по каждому каналу в соответствии со значениями, показанными на рис. 7.10. Эффект на изображении незаметен, но разные размытия разных значений цвета придают ему немного трехмерный вид, как будто вы смотрите на красно-синий анаглиф без очков. Ширина 0.5 (канал R)
Ширина 5 (канал G)
Ширина 50 (канал B)
Рис. 7.10 Ядра для каждого цветового канала, используемые в упражнении 7.6
Упражнение 7.7 В техническом плане сглаживание изображения – это извлечение признаков, поскольку оно предусматривает извлечение сглаженных признаков сигнала при одновременном ослаблении резких признаков. Здесь мы изменим фильтерные ядра, чтобы решить другие задачи обнаружения признаков изображения: выявление горизонтальных и вертикальных линий. Два ядра показаны на рис. 7.11, как и их влияние на изображение. Два ядра можно создать вручную, основываясь на их внешнем виде; они имеют размер 3×3 и содержат только числа –1, 0 и +1. Примените свертку с этими 1
Подсказка: сглаживайте каждый цветовой канал по отдельности.
Упражнения по программированию 143
ядрами к двумерному фотоснимку в оттенках серого, чтобы создать карты признаков, показанные на рис. 7.11. Вертикальное ядро
Рис. 7.11 Результаты упражнения 7.7
8 Обратные матрицы
Мы движемся к решению матричных уравнений. Матричные уравнения похожи на обычные уравнения (например, найти решение для x в 4x = 8), но. в них есть матрицы. К этому моменту книги вы хорошо понимаете, что когда в дело вступают матрицы, все становится сложнее. Тем не менее мы должны принять эту сложность как само собой разумеющееся, потому что решение матричных уравнений – огромная часть науки о данных. Обратная матрица играет центральную роль в решении матричных уравнений в практических приложениях, включая подгонку статистических моделей к данным (подумайте об общих линейных моделях и регрессии). К концу этой главы вы поймете, что такое обратная матрица, когда ее можно и нельзя вычислить, как ее вычислять и как ее интерпретировать.
Обратная матрица Матрица, обратная матрице A, – это еще одна матрица A–1 (произносится как «обратная матрица, или инверсная матрица, матрицы A»), которая умножает матрицу A, производя единичную матрицу. Другими словами, A–1A = I. Именно так матрица «отменяется». Еще одна концептуализация заключается в намерении преобразовать матрицу линейно в единичную матрицу; обратная матрица содержит это линейное преобразование, а матричное умножение является механизмом применения этого преобразования к матрице. Но зачем вообще инвертировать матрицы? Нам приходится «отменять» матрицу, чтобы решать задачи, которые можно выразить в форме Ax = b, где A и b – это известные величины, и мы хотим найти решение для x. Решение имеет следующую ниже общую форму: Ax = b; A–1Ax = A–1b; Ix = A–1b; x = A–1b.
Типы обратных матриц и условия обратимости 145
Она выглядит очень простой, но, как вы скоро узнаете, вычислять обратную матрицу обманчиво трудно.
Типы обратных матриц и условия обратимости «Инвертировать матрицу» звучит так, как будто это должно работать всегда. Кому не хотелось бы инвертировать матрицу, если это удобно? К сожалению, жизнь не всегда так проста: не все матрицы можно инвертировать. Есть три разных вида обратных матриц с разными условиями обратимости. Они представлены ниже; их подробности находятся в следующих далее разделах: Полная обратная матрица Она означает, что A–1A = AA–1 = I. Наличие у матрицы полной обратной матрицы обусловливается двумя свойствами: (1) она должна быть квад ратной и (2) полноранговой. Каждая квадратная полноранговая матрица имеет обратную матрицу, и каждая матрица, имеющая полную обратную матрицу, является квадратной и полноранговой. Между прочим, здесь я использую термин «полная обратная матрица», чтобы отличить ее от следующих ниже двух возможностей; полная обратная матрица обычно называется просто обратной матрицей. Односторонняя обратная матрица Односторонняя обратная матрица может преобразовывать прямоугольную матрицу в единичную матрицу, но она работает только для одного порядка умножения. В частности, высокая матрица T может иметь левообратную матрицу, то есть LT = I, но TL ≠ I. А широкая матрица W может иметь правообратную матрицу, то есть WR = I, но RW ≠ I. Неквадратная матрица имеет одностороннюю обратную матрицу только в том случае, если она имеет максимально возможный ранг. То есть высокая матрица имеет левообратную матрицу, если она имеет ранг N (полный столбцовый ранг), тогда как широкая матрица имеет правообратную мат рицу, если она имеет ранг M (полный строчный ранг). Псевдообратная матрица Каждая матр иц а имеет псевдообратную матр иц у, независимо от ее формы и ранга. Если матр иц а является квадратной полноранговой, то псевдообратная матр иц а равна полной обратной матр иц е. Точно так же если матр иц а является неквадратной и имеет максимально возможный ранг, то псевдообратная марица равна левообратной матр иц е (для высокой матр иц ы) или правообратной матр иц е (для широкой матр иц ы). Но рангово-пониженная матр иц а по-прежнему имеет псевдообратную матрицу, и в этом случае псевдообратная матрица преобразовывает сингулярную матр иц у в еще одну матр иц у, близкую, но не равную единичной матр иц е.
146 Обратные матрицы Матрицы, не имеющие ни полной, ни односторонней обратной матрицы, называются сингулярными, или необратимыми. Это то же самое, что пометить матрицу как рангово-пониженную или рангово-дефицитную.
Вычисление обратной матрицы Обратная матрица звучит великолепно! Как ее вычислять? Давайте начнем, подумав о том, как вычислить обратное значение скаляра: надо просто инвертировать его число (взять его взаимообратную величину). Например, число, обратное числу 3, равно 1/3, что то же самое, что 3–1. Тогда 3×3–1 = 1. Основываясь на этом рассуждении, можно догадаться, что взятие обратной матрицы работает точно так же: инвертировать каждый элемент матри цы. Давай попробуем:
К сожалению, это не произведет желаемого результата, что легко продемонстрировать, умножив изначальную матрицу на матрицу отдельно инвертированных элементов:
Это допустимое умножение, но оно не дает единичной матрицы, и, стало быть, матрица с отдельно инвертированными элементами не является обратной матрицей. Существует алгоритм вычисления матрицы для любой обратимой матри цы. Он долгий и утомительный (вот почему у нас есть компьютеры, которые делают всю числодробительную работу за нас!), но имеется несколько сокращенных версий для специальных матриц.
Обратная матрица матрицы 2×2 Для того чтобы инвертировать матрицу 2×2, надо поменять местами диагональные элементы, умножить внедиагональные элементы на −1 и разделить на определитель. Этот алгоритм создаст матрицу, которая преобразовывает изначальную матрицу в единичную. Проследите за следующей ниже последовательностью уравнений:
Вычисление обратной матрицы 147
Давайте проработаем числовой пример:
Сработало замечательно. Вычислить обратую матрицу на Python очень просто: A = np.array([ [1,4],[2,7] ]) Ainv = np.linalg.inv(A) A@Ainv
Вы можете подтвердить, что A@Ainv дает единичную матрицу, как и Ainv@A. Конечно же, A*Ainv не дает единичной матрицы, потому что операция * озна чает адамарово (поэлементное) умножение. Матрица, обратная ей матрица и их произведение представлены на рис. 8.1. Матрица
Рис. 8.1 Обратная матрица матрицы 2×2
Давайте попробуем еще один пример:
148 Обратные матрицы В этом примере есть несколько проблем. Умножение матриц вместо I дает 0. Но тут есть более крупная проблема – определитель равен нулю! Математики веками предупреждали о том, что на ноль делить нельзя. Так давайте же не будем начинать это делать сейчас. Чем отличается второй пример? Это рангово-пониженная матрица (ранг = 1). В нем показан числовой пример того, что рангово-пониженные матрицы необратимы. Что делает Python в этом случае? Давайте выясним: A = np.array([ [1,4],[2,8] ]) Ainv = np.linalg.inv(A) A@Ainv
Python даже не будет пытаться вычислить результат, как это сделал я. Вместо этого он выдаст ошибку со следующим ниже сообщением1: LinAlgError: Singular matrix
Рангово-пониженные матрицы не имеют обратной матрицы, и такие программы, как Python, даже не будут пытаться ее вычислить. Однако эта матри ца имеет псевдообратную матрицу. Я вернусь к ней через несколько разделов.
Обратная матрица диагональной матрицы Существует также сокращенная версия вычисления обратной матрицы квад ратной диагональной матрицы. К этому сокращению ведет понимание, что произведение двух диагональных матриц – это просто умножение диагональных элементов на скаляр (обнаруженное в упражнении 5.12). Рассмот рим приведенный ниже пример; прежде чем продолжить чтение текста, попробуйте найти сокращенное вычисление обратной матрицы для диагональной матрицы:
Вы поняли, как вычислять обратную матрицу диагональной матрицы? Хитрость в том, что надо просто инвертировать каждый диагональный элемент, игнорируя внедиагональные нули. Это ясно из предыдущего примера, если задать b = 1/2, c = 1/3 и d = 1/4. Что произойдет, если у вас диагональная матрица с нулем по диагонали? Инвертировать этот элемент не получится, потому что у вас будет 1/0. Таким образом, диагональная матрица хотя бы с одним нулем по диагонали не имеет обратной матрицы. (Также впомните из главы 6, что диагональная матрица является полноранговой, только если все диагональные элементы отличны от нуля.) 1
Линейно-алгебраическая ошибка: сингулярная матрица. – Прим. перев.
Вычисление обратной матрицы 149
Обратная матрица диагональной важна тем, что она напрямую приводит к формуле вычисления псевдообратной матрицы. Подробнее об этом позже.
Инвертирование любой квадратной полноранговой матрицы Честно говоря, я сомневался, стоит ли включать этот раздел в главу. Полный алгоритм инвертирования обратимой матрицы – длинный и утомительный, и вам никогда не понадобится его использовать в приложениях (вместо этого вы будете использовать функцию np.linalg.inv либо другие функции, вызывающие inv). С другой стороны, реализация алгоритма на Python – это для вас отличная возможность попрактиковаться в переложении в исходный код Python алгоритма, описанного в уравнениях и на естественном языке. Поэтому здесь я объясню принцип работы алгоритма, не показывая никакого исходного кода. Я призываю вас реализовать алгоритм программно на языке Python, по ходу чения этого раздела, и потом вы сможете сравнить свое решение с моим в упражнении 8.2 онлайнового исходного кода. Алгоритм вычисления обратной матрицы предусматривает четыре промежуточные матрицы, именуемые матрицей миноров, матрицей-решеткой, матрицей кофакторов и матрицей адъюгатов. Матрица миноров Эта матрица содержит определители подматриц. Каждый элемент mi,j мат рицы миноров является определителем подматрицы, созданной путем исключения i-й строки и j-го столбца. На рис. 8.2 показан общий вид процедуры для матрицы 3×3.
Рис. 8.2 Вычисление матрицы миноров (области, окрашенные серым цветом, удаляются, чтобы создать каждую подматрицу)
150 Обратные матрицы Матрица-решетка Матрица-решетка – это шахматная доска с чередующимися значениями +1 и –1. Она рассчитывается по следующей ниже формуле: gi, j = –1i+j. При реализации этой формулы на Python будьте осмотрительны с индексацией и возведением в степень. Вы должны тщательно проверить матри цу, чтобы убедиться, что это шахматная доска с чередующимися знаками, с +1 в верхнем левом элементе. Матрица кофакторов Матрица кофакторов – это адамарово умножение матрицы миноров на матрицу-решетку. Матрица адъюгатов1 Это транспонированная версия матрицы кофакторов, скалярно умноженной на взаимообратное значение определителя изначальной матрицы (матрицы, матричную инверсию которой вы вычисляете, а не матрицы кофакторов). Матрица адъюгатов является инверсией изначальной матрицы. На рис. 8.3 показаны четыре промежуточные матрицы, а также обратная матрица, возвращенная функцией np.linalg.inv, и единичная матрица, полученная в результате умножения изначальной матрицы на обратную ей матрицу, вычисленную в соответствии с ранее описанной процедурой. Изначальная матрица была матрицей случайных чисел. Матрица миноров
Матрица адъюгатов (инверсия)
Рис. 8.3 Визуализация матриц, продуцирующих инверсию для матрицы случайных чисел 1
Англ. adjugate; син. примыканий, присоединений. – Прим. перев.
Вычисление обратной матрицы 151
Односторонние обратные матрицы Высокая матрица не имеет полной обратной матрицы. То есть для матрицы T размера M > N не существует высокой матрицы T–1 такой, что TT–1 = T–1T = I. Но существует матрица L такая, что LT–1. Сейчас наша цель – найти эту матрицу. Начнем с того, что сделаем матрицу T квадратной. Как превратить неквадратную матрицу в квадратную? Разумеется, вы знаете ответ – ее надо умножить на ее транспонированную версию. Возникает следующий вопрос: что вычислять – TTT или TTT? Обе являются квадратными… но TTT – полноранговая, если T имеет полный столбцовый ранг. И почему это важно? Как вы уже догадались, все квадратные полноранговые матрицы имеют обратную матрицу. Прежде чем представить математический вывод левообратной матрицы, давайте продемонстрируем на Python, что высокая матрица, умноженная на ее транспонированную версию, имеет полную обратную матрицу: T = np.random.randint(-10, 11, size=(40,4)) TtT = T.T @ T TtT_inv = np.linalg.inv(TtT) TtT_inv @ TtT
В приведенном выше исходном коде можно подтвердить, что последняя строка создает единичную матрицу (в пределах машинно зависимой ошибки прецизионности). Давайте я переложу этот исходный код Python в математическое уравнение: (TTT)–1(TTT) = I. Из исходного кода и формулы видно, что поскольку TTT – это не та же мат рица, что и T, (TTT)–1 не является обратной матрицей матрицы T. Но – и вот ключевой момент – мы ищем матрицу, которая умножает T с левой стороны, чтобы получить единичную матрицу; нас на самом деле не волнует, какие другие матрицы нужно умножить, чтобы получить эту матри цу. Тогда давайте разобьем и перегруппируем умножения матриц: L = (TTT)–1TT = I; LT = I. Эта матрица L является левообратной матрицей матрицы T. Теперь можно завершить исходный код Python вычисления левообратной матрицы и подтвердить, что он соответствует нашей спецификации. Умножьте изначальную высокую матрицу с левой стороны, чтобы получить единичную матрицу: L = TtT_inv @ T.T # левообратная матрица L@T # производит единичную матрицу
Вы также можете подтвердить на Python, что TL (то есть умножение на левообратную матрицу справа) не дает единичную матрицу. Вот почему левообратная матрица является односторонней обратной матрицей.
152 Обратные матрицы На рис. 8.4 показаны высокая матрица, ее левообратная матрица и два способа умножения левообратной матрицы на матрицу. Обратите внимание, что левообратная матрица не является квадратной и что постпозиционное умножение на левообратную матрицу дает результат, который определенно не является единичной матрицей. Высокая матрица
Рис. 8.4 Визуализация левообратной матрицы
Левообратная матрица имеет чрезвычайную важность. На самом деле, после того как вы узнаете о подгонке статистических моделей к данным и методе наименьших квадратов, вы повсюду будете встречать левообратную мат рицу. Не будет преувеличением сказать, что левообратная матрица – один из самых важных вкладов линейной алгебры в современную человеческую цивилизацию.
Уникальность обратной матрицы 153
Заключительное замечание о левообратной матрице, которое присутствовало в этом изложении неявно: левообратная матрица определена только для высоких матриц с полным столцовым рангом. Матрица размера M > N ранга r N) мы создаем матрицу Q с N столбцами или же с M столбцами? Первый вариант
называется экономным, или сокращенным1, и дает высокую матрицу Q, а последний вариант называется полным2 и дает квадратную матрицу Q.
Квадратная полноранговая Квадратная сингулярная
Рис. 9.2 Размеры матриц Q и R в зависимости от размера матрицы A. Символ «?» указывает на то, что матричные элементы зависят от значений в матрице A, т. е. это не единичная матрица
Возможно, покажется удивительным, что матрица Q может быть квадратной, когда матрица A – высокая (другими словами, Q может иметь больше столбцов, чем A): откуда берутся дополнительные столбцы? На самом деле ортогональные векторы можно создавать «из воздуха». Рассмотрим следующий ниже пример на Python: A = np.array([ [1,-1] ]).T Q,R = np.linalg.qr(A,’complete’) Q*np.sqrt(2) # шкалируется с помощью sqrt(2), # чтобы получить целые числа >> array([[-1., 1.], [ 1., 1.]])
Обратите внимание на опциональный второй аргумент ‘complete’, который производит полное QR-разложение. Задание этого аргумента равным ‘reduced’, который используется по умолчанию, дает экономное QRразложение, в котором матрица Q имеет тот же размер, что и матрица A. Поскольку из матрицы с N столбцами можно создать более M > N ортогональных векторов, ранг матрицы Q всегда является максимально возможным 1 2
Англ. economy, reduced. – Прим. перев. Англ. full, complete. – Прим. перев.
168 Ортогональные матрицы и QR-разложение рангом, который равен M для всех квадратных матриц Q и N для экономной матрицы Q. Ранг матрицы R совпадает с рангом матрицы A. Разница в ранге между матрицами Q и A в результате ортогонализации означает, что Q охватывает все ℝM, даже если столбцовое пространство мат рицы A является лишь низкоразмерным подпространством ℝM. Этот факт является ключевым в том, почему сингулярное разложение выступает таким удобным для выявления свойств матрицы, включая ее ранг и нульпространство. Еще одна причина с нетерпением ждать информации об SVD в главе 14! Примечание об уникальности: QR-разложение не является уникальным для всех размеров и рангов матриц. Это означает, что можно получить A = Q1R1 и A = Q2R2, гдеQ1 ¹ Q2. Однако все результаты QR-разложения обладают одинаковыми свойствами, описанными в этом разделе. QR-разложение можно сделать уникальным при наличии дополнительных ограничений (например, положительных значений на диагоналях матр иц ы R), хотя в большинстве случаев это необязательно и не реализовано ни в Python, ни в MATLAB. Вы увидите эту неуникальность в упражнении 9.2 при сравнении GS с QR.
Почему матрица R является верхнетреугольной Надеюсь, вы серьезно задумались над данным вопросом. В QR-разложении это сложный момент, поэтому если вы не смогли разобраться в нем самостоятельно, то, пожалуйста, прочитайте следующие несколько абзацев, а затем оторвите взгляд от книги/экрана и заново выведите аргумент математически. Начну с того, что напомню три факта: матрица R получается из формулы QTA = R; нижний треугольник матрицы произведения содержит точечные произведения между последующими строками левой матрицы и предшест вующими столбцами правой матрицы; строки матрицы QT являются столбцами матрицы Q. Соединяя все вместе: поскольку ортогонализация проходит по столбцам слева направо, последующие столбцы в матрице Q ортогонализируются к предшествующим столбцам матрицы A. Следовательно, нижний треугольник матрицы R получается из пар ортогонализированных векторов. Напротив, предшествующие столбцы в Q не ортогонализируются к последующим столбцам матрицы A, поэтому их точечные произведения ожидаемо не будут равны нулю. Заключительный комментарий: если бы столбцы i и j матрицы A уже были ортогональны, то соответствующий (i, j)-й элемент в R был бы равен нулю. На самом деле если вычислить QR-разложение ортогональной матрицы, то R будет диагональной матрицей, в которой диагональные элементы являются нормами каждого столбца в матрице A. Это означает, что если A = Q, то R = I, что очевидно из уравнения, вычисленного для R. Вы обследуете этот вопрос в упражнении 9.3.
QR и обратные матрицы QR-разложение обеспечивает численно более стабильный способ вычисления обратной матрицы. Давайте начнем с написания формулы QR-разложения и инвертирования обеих частей уравнения (обратите внимание на применение правила LIVE EVIL): A = QR; A
A–1 = R–1Q–1; A–1 = R–1QT. Таким образом, обратную матрицу матрицы A можно получить как инверсию матрицы R, умноженную на транспонированную версию матрицы Q. Матрица Q численно устойчива из-за алгоритма отражения Хаусхолдера, а матрица R численно устойчива, потому что она является просто результатом матричного умножения. Теперь нам все еще нужно инвертировать R в явной форме, но инвертирование треугольных матриц численно является очень стабильным, если выполняется с помощью процедуры, именуемой обратной подстановкой. Вы узнаете о ней подробнее в следующей главе, но ключевой момент заключается в следующем: важным применением QR-разложения является предоставление численно более стабильного способа инвертирования матриц по сравнению с алгоритмом, который был представлен в предыдущей главе. С другой стороны, имейте в виду, что матрицы, которые теоретически являются обратимыми, но близкими к сингулярным, по-прежнему очень трудно инвертировать; QR-разложение может быть численно более стабильным, чем представленный в предыдущей главе традиционный алгоритм, но это не гарантирует высококачественную обратную матрицу. Если окунуть гнилое яблоко в мед, то оно по-прежнему останется гнилым.
Резюме QR-разложение – великолепная вещь. Оно определенно входит в мой список пяти лучших матричных разложений в линейной алгебре. Вот ключевые выводы, которые следует вынести из этой главы. Ортогональная матрица имеет столбцы, которые попарно ортогональны и имеют норму, равную 1. Ортогональные матрицы являются ключом к нескольким матричным разложениям, включая QR-разложение, собственное разложение и сингулярное разложение. Ортогональные матрицы также важны в геометрии и компьютерной графике (например, матрицы чистого поворота).
170 Ортогональные матрицы и QR-разложение Неортогональная матрица преобразовывается в ортогональную матри цу посредством процедуры Грама–Шмидта, которая предусматривает применение ортогонального разложения векторов, чтобы изолировать компоненту каждого столбца, который ортогонален всем предыдущим столбцам («предыдущий» означает слева направо). QR-разложение является результатом процедуры Грама–Шмидта (технически оно реализовано более стабильным алгоритмом, но GS попрежнему является правильным ключом к его пониманию).
Упражнения по программированию Упражнение 9.1 Квадратная матрица Q имеет следующие ниже тождества: QTQ = QQT = Q–1Q = QQ–1 = I. Продемонстрируйте это в исходном коде, вычислив Q из матрицы случайных чисел, потом вычислив QT и Q–1. Затем покажите, что все четыре выражения дают единичную матрицу. Упражнение 9.2 Реализуйте процедуру Грама–Шмидта, как описано ранее1. Используйте матрицу случайных чисел 4×4. Сравните свой ответ с матрицей Q из функции np.linalg.qr. Важно: в преобразованиях типа отражения Хаусхолдера присутствует фундаментальная неопределенность знака. Это означает, что векторы могут «переворачиваться» (умножаться на −1) в зависимости от незначительных различий в алгоритме и реализации. Эта особенность существует во многих матричных разложениях, включая собственные векторы. В главе 13 имеется более глубокое и подробное изложение причины, по которой это происходит, и что это означает. А пока результат таков: надо вычесть свою матрицу Q из Python’овской матрицы Q и сложить свою матрицу Q и Python’овскую мат рицу Q. Ненулевые столбцы в одной будут нулями в другой. Упражнение 9.3 В этом упражнении вы узнаете, что происходит при применении QRразложения к матрице, которая почти, но не совсем ортогональна. Во-первых, создайте ортогональную матрицу под названием U из QR-разложения мат рицы случайных чисел 6×6. Вычислите QR-разложение матрицы U и подтвердите, что R = I (и убедитесь, что вы понимаете причину!). Во-вторых, видоизмените нормы каждого столбца матрицы U. Установите нормы столбцов 1–6 равными 10–15 (то есть первый столбец матрицы U должен иметь норму 10, второй столбец должен иметь норму 11 и т. д.). Про1
Не торопитесь с этим упражнением; оно довольно сложное.
Упражнения по программированию 171
гоните эту модулированную матрицу U через QR- разложение и подтвердите, что ее R является диагональной матрицей с диагональными элементами, равными 10–15. Какой будет QTQ для этой матрицы? В-третьих, нарушьте ортогональность матрицы U, установив элемент u1.4 = 0. Что произойдет с R и почему? Упражнение 9.4 Цель этого упражнения – сравнить численные ошибки, полученные при использовании «традиционного» инверсного метода, с которым вы познакомились в предыдущей главе, с инверсным методом на основе QR. Мы будем использовать матрицы случайных чисел, помня о том, что они, как правило, численно стабильны и, следовательно, имеют точные обратные матрицы. Вот что нужно сделать: скопируйте исходный код из упражнения 8.2 в функцию Python, которая на входе принимает матрицу и на выходе предоставляет обратную ей матр иц у. (Вы также можете включить проверку того, что входная матрица является квадратной и является полноранговой.) Я назвал эту функцию oldSchoolInv. Затем создайте матрицу случайных чисел 5×5. Вычислите обратную ей матрицу, используя традиционный метод и представленный в этой главе метод на основе QR-разложения (для вычисления R–1 можете использовать свой «традиционный метод»). Рассчитайте ошибку вычисления обратной матрицы как евклидово расстояние от матри цы, умноженной на обратную ей матрицу, до истинной единичной матрицы из функции np.eye. Постройте гистограмму результатов, показав два метода на оси x и ошибку (евклидово расстояние до I) на оси y, как представлено на рис. 9.3.
Евклидово расстояние до единичной матрицы
Ошибка обратной матрицы (матрица 5×5)
Рис. 9.3 Результаты упражнения 9.4
172 Ортогональные матрицы и QR-разложение Выполняйте исходный код несколько раз и инспектируйте гистограмму. Вы обнаружите, что иногда традиционный метод работает лучше, а иногда лучше работает метод на основе QR (чем меньше числа, тем лучше; теоретически столбцы должны иметь нулевую высоту). Попробуйте еще раз, используя матрицу 30×30. Будут ли результаты более последовательными? На самом деле от прогона к прогону будет большой разброс. Это означает, что необходимо провести эксперимент, в котором сравнение повторяется много раз. Это следующее упражнение. Упражнение 9.5 Поместите исходный код из предыдущего упражнения в цикл for с сотней итераций, в которых вы повторяете эксперимент, всякий раз используя другую матрицу случайных чисел. Сохраняйте ошибку (евклидово расстояние) по каждой итерации и постройте график, подобный рис. 9.4, который показывает среднее значение по всем прогонам эксперимента (серый столбик) и все отдельные ошибки (черные точки). Проведите эксперимент для матриц 5×5 и 30×30. Вы также можете попробовать использовать функцию np.linalg.inv, чтобы вместо традиционного метода инвертировать R и увидеть, будет ли это иметь эффект. Ошибка обратной матрицы (матрица 5×5) Евклидово расстояние до единичной матрицы
Евклидово расстояние до единичной матрицы
Ошибка обратной матрицы (матрица 5×5)
Рис. 9.4 Результаты упражнения 9.5. Обратите внимание на разницу в шкалировании оси y между левой и правой панелями
Упражнение 9.6 Интересное свойство квадратных ортогональных матриц состоит в том, что все их сингулярные числа (и их собственные числа) равны 1. Это означает, что они имеют индуцированную 2-норму, равную 1 (индуцированная норма является наибольшим сингулярным числом), и они имеют фробениусову норму M. Последний результат объясняется тем, что фробениусова норма
Упражнения по программированию 173
равна квадратному корню из суммы квадратов сингулярных чисел. В этом упражнении вы подтвердите эти свойства. Создайте ортогональную матрицу M×M как QR-разложение случайной матр иц ы. Вычислите ее индуцированную 2-норму, используя функцию np.linalg.norm, и вычислите ее фробениусову норму, используя уравнение, которое вы узнали в главе 6, деленное на квадратный корень из M. Подтвердите, что обе величины равны 1 (в разумных пределах допуска ошибки округления). Проверьте, используя несколько разных значений M. Далее обследуйте значение индуцированной нормы, используя умножение матрицы на вектор. Создайте случайный -элементный вектор-столбец v. Затем вычислите нормы векторов v и Qv. Эти нормы должны быть равны друг другу (хотя не следует ожидать, что они будут равны 1). Наконец, возьмите лист бумаги и разработайте доказательство этой эмпирической демонстрации. Указанное доказательство напечатано в следующем абзаце, так что не смотрите вниз! Но вы можете обратиться к сноске, если вам нужна подсказка1. Искренне надеюсь, что вы это читаете, чтобы проверить свои рассуждения, а не потому, что жульничаете! Так или иначе, доказательство состоит в том, что векторную норму ||v|| можно вычислить как vTv; поэтому векторная норма ||Qv|| вычисляется как (Qv)TQv = vTQTQv. QTQ отменяется, давая единичную матрицу, при этом оставляя точечное произведение вектора с самим собой. Вывод состоит в том, что ортогональные матрицы могут поворачивать вектор, но не шкалировать его. Упражнение 9.7 В этом упражнении будет освещена одна особенность матрицы R, которая имеет отношение к пониманию того, как использовать QR для реализации метода наименьших квадратов (глава 12): когда матрица A является высокой и имеет полный столбцовый ранг, первые N строк матрицы R являются верхнетреугольными, тогда как строки N + 1 вплоть до M – нулевыми. Подтвердите это на Python, используя случайную матрицу 10×4. Проследите, чтобы использовалось полное QR-разложение, а не экономное (компактное) разложение. Конечно же, матрица R необратима, потому что она не квадратная. Но 1) подматрица, состоящая из первых N строк, является квадратной и полноранговой (когда матрица A имеет полный столбцовый ранг), следовательно, имеет полную обратную матрицу, и 2) высокая матрица R имеет псевдообратную матрицу. Вычислите обе обратные матрицы и подтвердите, что полная обратная матрица первых N строк матрицы R равна первым N столбцам псевдообратной матрицы высокой матрицы R.
Подсказка: распишите формулу точечного произведения для векторной нормы.
10 Приведение строк и LU-разложение
Теперь переходим к LU-разложению. LU, как и QR, является одним из вычислительных стержней, лежащих в основе алгоритмов обработки данных, включая подгонку модели методом наименьших квадратов и обратную мат рицу. Таким образом, эта глава имеет ключевое значение для вашего образования в области линейной алгебры. Особенность LU-разложения состоит в том, что его невозможно просто выучить за один присест. Вместо этого сначала придется познакомиться с системами уравнений, приведением строк и гауссовым устранением. И в ходе изучения этих тем вы также узнаете о ступенчатых матрицах и матрицах перестановок. О да, дорогой читатель, это будет захватывающая и насыщенная событиями глава.
Системы уравнений В целях понимания LU-разложения и его применений нужно понять приведение строк и устранение по Гауссу. А в целях понимания этих тем нужно понять, как манипулировать уравнениями, преобразовывать их в матричное уравнение и решать это матричное уравнение, используя приведение строк. Начнем с «системы» из одного уравнения: 2x = 8. Уверен, что, научившись в школе, вы способны выполнять различные математические манипуляции с уравнением – при условии что вы делаете одно и то же с обеими частями уравнения. Это означает, что следующее уравнение не совпадает с предыдущим, но они связаны между собой простыми манипуляциями. Что еще важнее, любое решение одного уравнения является решением другого: 5(2x – 3) = 5(8 – 3).
Системы уравнений 175
Теперь перейдем к системе из двух уравнений: x = 4 – y; y = x/2 + 2. В этой системе уравнений найти уникальные значения x и y только из одного из этих уравнений невозможно. Вместо этого, чтобы получить решение, нужно рассматривать оба уравнения одновременно. Если вы попытаетесь решить эту систему сейчас, то, вероятно, воспользуетесь стратегией подстановки y в первом уравнении правой частью второго уравнения. После отыскания решения для x в первом уравнении вы подставите это значение во второе уравнение, чтобы найти y. Эта стратегия похожа (хотя и не так эффективна) на обратную подстановку, определение которой я дам позже. Важной особенностью системы уравнений является то, что отдельные уравнения можно складывать друг с другом либо вычитать. В следующих ниже уравнениях я два раза добавил второе уравнение к первому и вычел первое изначальное уравнение из второго (скобки добавлены для ясности): x + (2y) = 4 – y + (x + 4); y – (x) = x/2 + 2 – (4 – y). Даю вам возможность поработать над арифметикой, но итогом является то, что x выпадает из первого уравнения, а y выпадает из второго уравнения. Это значительно упрощает вычисление решения (x = 4/3, y = 8/3). И вот важный момент: умножение уравнения на скаляр и добавление их к другим уравнениям облегчили отыскание решения системы. Опять же, модулированная и изначальная системы – это не одни и те же уравнения, но их решения одинаковы, поскольку две системы связаны серией линейных операций. Это базовые знания, необходимые для того, чтобы научиться решать системы уравнений с помощью линейной алгебры. Но прежде чем научиться этому подходу, вам нужно научиться представлять систему уравнений мат рицами и векторами.
Конвертирование уравнений в матрицы Конвертирование систем уравнений в матрично-векторное уравнение применяется для решения систем уравнений и используется для настройки формулы общей линейной модели в статистике. К счастью, переложение уравнений в матрицы осуществляется концептуально просто и выполняется в два шага. Во-первых, надо организовать уравнения так, чтобы константы находились в правой части уравнений. Константы – это числа, которые не привязаны к переменным (иногда именуемые пересечениями, или сдвигами). Переменные и умножающие их коэффициенты находятся в левой части уравнения в том же порядке (например, все уравнения должны иметь сначала
176 Приведение строк и LU-разложение член x, затем член y и т. д.). Следующие ниже уравнения образуют систему уравнений, с которой мы работали ранее, но в правильной организации: x + y = 4; –x/2 + y = 2. Во-вторых, надо выделить коэффициенты (числа, на которые умножаются переменные; отсутствующие в уравнении переменные имеют коэффициент, равный нулю) в матрицу с одной строкой в расчете на одно уравнение. Переменные помещаются в вектор-столбец, который умножает матрицу коэффициентов справа. А константы помещаются в вектор-столбец в правой части уравнения. Наша примерная система имеет матричное уравнение, которое выглядит следующим образом:
И вуаля! Вы конвертировали систему уравнений в одно матричное уравнение. К этому уравнению можно обращаться как к Ax = b, где A – это матрица коэффициентов, x – вектор неизвестных переменных, для которых нужно найти решение (в данном случае x – это вектор, содержащий [x y]), а b – вектор констант. Пожалуйста, найдите минутку, чтобы убедиться, что вы понимаете, как матричное уравнение отображается в систему уравнений. В частности, проработайте умножение матрицы на вектор, чтобы продемонстрировать, что оно эквивалентно изначальной системе уравнений.
Работа с матричными уравнениями Матричными уравнениями можно манипулировать так же, как и обычными уравнениями, включая сложение, умножение, транспонирование и т. д., при условии что манипуляции допустимы (например, в случае сложения размеры матриц совпадают) и все манипуляции влияют на обе части уравнения. Например, допустима следующая ниже прогрессия уравнений: Ax = b; v + Ax = v + b; (v + Ax)T = (v + b)T. Главное различие между работой с матричными уравнениями и скалярными уравнениями заключается в том, что, поскольку умножение матриц зависит от стороны, матрицы необходимо умножать одинаково в обеих час тях уравнения. Например, допустима следующая ниже прогрессия уравнений: AX = B; CAX = CB.
Системы уравнений 177
Обратите внимание, что C предпозиционно умножает обе части уравнения. Напротив, следующая ниже прогрессия недопустима: AX = B; AXC = CB. Проблема здесь в том, что C постпозиционно умножает в левой части, но предпозиционно умножает в правой части. Несомненно, есть несколько исключительных случаев, когда это уравнение будет допустимым (например, если C является единичной матрицей или матрицей нулей), но в общем случае эта прогрессия недопустима. Давайте обратимся к примеру на Python. Мы найдем неизвестную матрицу X в уравнении AX = B. Следующий ниже исходный код генерирует матрицы A и B из случайных чисел. Вы уже знаете, что X можно найти, используя A–1. Вопрос в том, имеет ли значение порядок умножения1. A = np.random.randn(4,4) B = np.random.randn(4,4) # найти X X1 = np.linalg.inv(A) @ B X2 = B @ np.linalg.inv(A) # остаток (должен быть матрицей нулей) res1 = A@X1 — B res2 = A@X2 — B
Если бы умножение матриц было коммутативным (означая, что порядок не имеет значения), то res1 и res2 должны были быть равны матрице нулей. Давайте посмотрим: res1: [[-0. [-0. [ 0. [ 0.
res2: [[-0.47851507 [ 2.47389146 [-2.12244448 [-3.61085707
6.24882633 2.56857366 -0.20807188 -3.80132548
4.39977191 1.80312482] 1.58116135 -0.52646367] 0.2824044 -0.91822892] -3.47900644 -2.372463 ]]
Теперь вы знаете, как выражать систему уравнений одним матричным уравнением. Я собираюсь вернуться к этому вопросу через несколько разделов; сперва мне нужно научить вас приведению строк и ступенчатой форме матрицы. 1
Конечно же, вы знаете, что порядок имеет значение, но эмпирические демонстрации помогают развивать интуицию. И я хочу, чтобы вы привыкли использовать Python как инструмент для эмпирического подтверждения математических принципов.
178 Приведение строк и LU-разложение
Приведение строк В традиционной линейной алгебре теме приведения строк уделяется много внимания, потому что это проверенный временем способ ручного решения систем уравнений. Я серьезно сомневаюсь, что в своей карьере исследователя данных вы будете решать какие-либо системы уравнений вручную. Но о приведении строк полезно знать, и оно ведет напрямую к LU-разложению, которое применяется на практике в прикладной линейной алгебре. Итак, начнем. Приведение строк1 означает итеративное применение двух операций – скалярного умножения и сложения – к строкам матрицы. Приведение строк основано на том же принципе, что и добавление уравнений к другим уравнениям в системе. Запомните вот это утверждение: цель приведения строк – преобразовать плотную матрицу в верхнетреугольную матрицу. Начнем с простого примера. В следующей ниже плотной матрице мы добавляем первую строку ко второй строке, тем самым выбивая −2. И таким образом мы преобразовали плотную матрицу в верхнетреугольную матрицу:
Полученная в результате приведения строк верхнетреугольная матрица называется ступенчатой формой матрицы. Формально матрица имеет ступенчатую форму, если 1) самое левое ненулевое число в строке (именуемое опорным элементом2) находится справа от опорного элемента вышележащих строк и 2) любые строки из одних нулей находятся ниже строк, содержащих ненулевые числа. Подобно манипулированию уравнениями в системе, матрица после приведения строк отличается от матрицы до приведения строк. Но две матрицы связаны линейным преобразованием. А поскольку линейные преобразования можно представлять матрицами, то для выражения приведения строк можно использовать матричное умножение:
Я буду называть эту матрицу L–1 по причинам, которые станут ясны, когда я введу понятие LU-разложения3. Таким образом, в выражении (L–1A = U) матрица L–1 – это линейное преобразование, отслеживающее манипуляции, 1
Англ. Row Reduction; син. сокращение строк. – Прим. перев. Англ. Pivot. – Прим. перев. 3 Внимание, спойлер: LU-разложение предусматривает представление матрицы как произведения нижнетреугольной и верхнетреугольной матриц. 2
Приведение строк 179
которые мы реализовали посредством приведения строк. Пока что не нужно сосредоточиваться на матрице L–1 – на самом деле при гауссовом отсеивании ее часто игнорируют. Но ключевой момент (слегка расширенный по сравнению с более ранним утверждением) заключается в следующем: приведение строк предусматривает преобразовывание матрицы в верхнетреугольную матрицу посредством манипуляций со строками, реализуемых как препозиционное умножение на трансформационную матрицу. Вот еще один пример матрицы 3×3. Для преобразования этой матрицы в ступенчатую форму требуется два шага:
Процедура приведения строк утомительна (см. врезку «Всегда ли процедура приведения строк так проста?»). Наверняка должна существовать функция Python, которая делает это за нас! Все дело в том, что она есть и ее нет. Нет функции Python, которая возвращает ступенчатую форму, подобную той, что я создал в двух предыдущих примерах. Причина в том, что ступенчатая форма матрицы не уникальна. Например, в приведенной выше матрице 3×3 вы могли умножить вторую строку на 2, чтобы получить вектор-строку [0 10 4]. Это создает совершенно допустимую, но другую ступенчатую форму той же изначальной матрицы. И действительно, с этой матрицей связано бесконечное число ступенчатых матриц. С учетом сказанного, двухступенчатые формы матрицы предпочтительнее бесконечно возможных ступенчатых форм. Эти две формы уникальны с учетом некоторых ограничений и называются строчно приведенной ступенчатой формой и матрицей U из LU-разложения. Я представлю обе позже; сначала пора научиться использовать приведение строк для решения систем уравнений.
Всегда ли процедура приведения строк так проста? Овладение навыком приведения строк матрицы к ее ступенчатой форме требует усердной практики и усвоения нескольких приемов с крутыми названиями, такими как взаимообмен строк и взаимообмен строк и столбцов1. Приведение строк раскрывает несколько интересных свойств строчного и столбцового пространств матрицы. И хотя успешное приведение строк матрицы 5×6 с помощью ручного взаимообмена строк приносит чувство выполненного долга и удовлетворения, я полагаю, что ваше время лучше потратить на линейно-алгебраические концепции, которые имеют более прямое применение в науке о данных. Мы движемся к пониманию LU-разложения, и этого краткого введения в приведение строк будет для указанной цели вполне достаточно.
Англ. partial pivoting; данный прием используется во избежание ошибок округления, которые могут возникать при делении каждого значения строки на опорный элемент. – Прим. перев.
180 Приведение строк и LU-разложение
Метод устранения по Гауссу К этому моменту книги вы знаете, как решать матричные уравнения, используя обратную матрицу. Что, если бы я вам сказал, что вы можете решить матричное уравнение, не инвертируя никаких матриц1? Этот метод называется устранением по Гауссу, или гауссовым устранением2. Несмотря на свое название, этот алгоритм на самом деле был разработан китайскими математиками почти за две тысячи лет до Гаусса, а затем открыт заново Ньютоном за сотни лет до Гаусса. Но Гаусс внес в этот метод важный вклад, в том числе технические приемы, реализованные в современных компьютерах. Метод гауссова устранения работает просто: надо расширить матрицу коэффициентов вектором констант, привести строки к ступенчатой форме, а затем применить обратную подстановку, чтобы найти решение для каждой переменной по очереди. Начнем с системы из двух уравнений, которую мы решили ранее: x = 4 – y; y = x/2 + 2. Первым шагом является конвертирование этой системы уравнений в мат ричное уравнение. Данный шаг уже нами пройден; это уравнение напечатано здесь как напоминание:
Далее расширим матрицу коэффициентов вектором констант:
Затем выполним приведение строк в этой расширенной матрице. Обратите внимание, что при приведении строк вектор-столбец констант изменится:
Получив матрицу в ступенчатой форме, переводим расширенную матрицу обратно в систему уравнений. Она выглядит так: x + y = 4; 3/2y = 4. 1
Пожалуйста, вообразите в уме мем Матрицы, в которой Морфеус предлагает красную и синюю таблетки, соответствующие новым знаниям в противоположность приверженности тому, что вы уже знаете. 2 Англ. Gaussian Elimination; син. исключение по Гауссу. – Прим. перев.
Приведение строк 181
Метод гауссова устранения посредством приведения строк удалил член x во втором уравнении, а это означает, что отыскание y предусматривает всего лишь несколько арифметических действий. Решив y = 8/3, подставьте это значение в y в первом уравнении и найдите x. Эта процедура называется обратной подстановкой. В предыдущем разделе я писал, что в Python нет функции для вычисления ступенчатой формы матрицы, поскольку она не уникальна. А затем написал, что существует одна уникальная ступенчатая матрица, именуемая строчно приведенной ступенчатой формой (нередко сокращенно RREF1), которую Python вычислит. Продолжайте читать, чтобы узнать подробности…
Метод устранения по Гауссу–Жордану Давайте продолжим приведение строк нашей образцовой матр иц ы с целью превратить все опорные элементы – крайние левые ненулевые числа в каждой строке – в единицы. При наличии ступенчатой матр иц ы каждая строка просто делится на ее опорный элемент. В этом примере первая строка уже имеет 1 в крайнем левом положении, поэтому нам просто нужно скорректировать вторую строку. В результате получаем следующую ниже матр иц у:
А теперь подходим к хитрости: мы продолжаем приведение строк вверх, чтобы отсеить все элементы над каждым опорным элементом. Другими словами, нам нужна ступенчатая матрица, в которой каждый опорный элемент равен 1, и это единственное ненулевое число в ее столбце.
Это и есть строчно приведенная ступенчатая форма (RREF) нашей изначальной матрицы. Вы увидите единичную матрицу слева – RREF-форма всегда будет создавать единичную матрицу как подматрицу в левом верхнем углу изначальной матрицы. Это результат установки всех опорных элементов равными 1 и использования восходящего сведения строк, чтобы устранять все элементы над каждым опорным элементом. Теперь мы продолжим гауссово устранение путем переложения матрицы обратно в систему уравнений: x = 4/3; y = 8/3. 1
От англ. Reduced Row Echelon Form. – Прим. перев.
182 Приведение строк и LU-разложение Нам больше не нужна ни обратная подстановка, ни даже базовая арифметика: модифицированный метод гауссова устранения, который называется методом устранения по Гауссу–Жордану, разъединил переплетенные переменные в системе уравнений и обнажил решения по каждой переменной. Метод устранения по Гауссу–Жордану применялся людьми для решения систем уравнений вручную более чем за столетие до того, как появились компьютеры, чтобы помогать нам в числодробительной работе. На самом деле в компьютерах реализован все тот же метод, лишь с малыми модификациями в целях обеспечения численной стабильности. RREF-форма уникальна, и, стало быть, матрица имеет только одну ассоциированную RREF-форму. В NumPy нет функции для вычисления RREF-формы матрицы, но в библиотеке sympy она есть (sympy – это библиотека для символьных математических вычислений на Python и мощный двигатель для математики «классной доски»): import sympy as sym # матрица, конвертированная в sympy M = np.array([ [1,1,4],[-1/2,1,2] ]) symMat = sym.Matrix(M) # RREF-форма symMat.rref()[0] >> [[1, 0, 1.33333333333333], [0, 1, 2.66666666666667]]
Обратная матрица посредством метода устранения по Гауссу–Жордану Ключевой момент метода устранения по Гауссу–Жордану заключается в том, что приведение строк производит последовательность манипуляций со строками, которая решает систему уравнений. Эти манипуляции со строками являются линейными преобразованиями. Любопытно, что описание метода устранения по Гауссу–Жордану согласуется с описанием обратной матрицы, то есть линейного преобразования, которое решает систему уравнений. Но подождите, какую «систему уравнений» решает обратная матрица? Свежий взгляд на обратную матрицу даст несколько новых идей. Рассмотрим вот эту систему уравнений: ax1 + by1 = 1; cx1 + dy1 = 0. Переведя в матричное уравнение, получим:
Посмотрите на вектор констант – это первый столбец единичной матрицы 2×2! Это означает, что применение RREF-формы к полноранговой квадратной матрице, расширенной первым столбцом единичной матрицы, покажет линейное преобразование, которое переводит матрицу в первый столбец единичной матрицы. А это, в свою очередь, означает, что вектор [x1 y1]T является первым столбцом обратной матрицы. Затем мы повторяем процедуру, но находя решение для второго столбца обратной матрицы: ax2 + by2 = 0; cx2 + dy2 = 1. RREF-форма в этой системе дает вектор [x2 y2]T, который является вторым столбцом обратной матрицы. Я отделил столбцы единичной матрицы, чтобы связать их с перспективой решения систем уравнений. Но мы можем расширить всю единичную мат рицу целиком и найти обратную матрицу с помощью одной RREF-формы. Вот вид с высоты птичьего полета на получение обратной матрицы посредством метода устранения по Гауссу–Жордану (квадратные скобки обозначают расширенные матрицы, при этом вертикальная линия отделяет две составляющие матрицы): rref([A | I]) ⇒ [I | A–1].
Это интересно, поскольку обеспечивает механизм вычисления обратной матрицы без вычисления определителей. С другой стороны, приведение строк предусматривает большое число делений, увеличивая риск ошибок численной прецизионности. Например, представьте, что даны два числа, которые по существу равны нулю плюс ошибка округления. Если мы в конечном итоге придем к тому, что поделим эти числа во время получения RREF, то мы получим дробь 10−15/10−16, которая на самом деле равна 0/0, но ответ будет 10. Вывод здесь аналогичен тому, который я изложил в предыдущей главе об использовании QR-разложения для вычисления обратной матрицы: применение метода устранения по Гауссу–Жордану для вычисления обратной матрицы, вероятно, будет более численно стабильным, чем полный алгоритм вычисления обратной матрицы, но инвертировать матрицу, близкую к сингулярной или имеющую высокое кондиционное число, будет трудно, независимо от используемого алгоритма.
LU-разложение Буквы «LU» в LU-разложении означают «нижний верхний»1, как в нижнем треугольном, верхнем треугольнике. Идея состоит в том, чтобы разложить матрицу на произведение двух треугольных матриц: 1
Англ. lower upper. – Прим. перев.
184 Приведение строк и LU-разложение A = LU. Вот числовой пример:
А вот соответствующий исходный код Python (обратите внимание, что функция для LU-разложения находится в библиотеке SciPy): import scipy.linalg # LU в библиотеке scipy A = np.array([ [2,2,4], [1,0,3], [2,1,2] ]) _, L, U = scipy.linalg.lu(A) # распечатать их print(‘L: ‘), print(L) print(‘U: ‘), print(U) L: [[1. 0. 0. ] [0.5 1. 0. ] [1. 1. 1. ]] U: [[ 2. 2. 4.] [ 0. -1. 1.] [ 0. 0. -3.]]
Откуда взялись эти две матрицы? На самом деле вы уже знаете ответ: приведение строк может быть выражено как (L–1A = U), где L–1 содержит набор манипуляций со строками, который преобразовывает плотную матрицу A в верхнетреугольную (ступенчатую) матрицу U. Поскольку ступенчатая форма не уникальна, LU-разложение не является обязательно уникальным. То есть существует бесконечная пара нижнеи верхнетреугольных матриц, которые можно перемножать, чтобы получать матрицу A. Однако добавление ограничения, состоящего в том, что диагонали матрицы L равны 1, обеспечивает, чтобы LU-разложение было уникальным для квадратной полноранговой матрицы A (это видно в предыдущем примере). Уникальность LU-разложений рангово-пониженных матриц и неквадратных матриц потребует более продолжительного изложения, которое я здесь не буду рассматривать; однако алгоритм LU-разложения библиотеки SciPy является детерминированным, а это означает, что повторяющиеся LUразложения заданной матрицы будут идентичными.
Взаимообмен строками посредством матриц перестановок Некоторые матрицы очень легко преобразовываются в верхнетреугольную форму. Рассмотрим следующую ниже матрицу:
Она не находится в ступенчатой форме, но была бы в ней, если бы мы поменяли местами вторую и третью строки. Взаимообмен строками – это один из приемов приведения строк, который реализуется посредством матрицы перестановок:
Матрицы перестановок часто обозначаются как P. Таким образом, полное LU-разложение на самом деле принимает следующий вид: PA = LU; A = PTLU. Примечательно, что матрицы перестановок ортогональны, поэтому P–1 = P . Вкратце: причина состоит в том, что все элементы матрицы перестановок равны либо 0, либо 1, а поскольку строки обмениваются местами только один раз, каждый столбец имеет ровно один ненулевой элемент (и действительно, все матрицы перестановок являются единичными матрицами со взаимообменом строк). Следовательно, точечное произведение любых двух столбцов равно 0, а точечное произведение столбца на самого себя равно 1, и, стало быть, PTP = I. Важно: приведенные выше формулы дают математическое описание LUразложения. Библиотека Scipy на самом деле возвращает A = PLU, что также можно было бы записать как PTA = LU. Упражнение 10.4 дает возможность обследовать этот дезориентирующий момент. На рис. 10.1 показан пример LU-разложения, примененного к случайной матрице. T
186 Приведение строк и LU-разложение
Рис. 10.1 Визуализация LU-разложения
LU-разложение используется в нескольких приложениях, включая вычисление определителя и обратной матрицы. В следующей главе вы увидите, как LU-разложение применяется в вычислении методом наименьших квадратов.
Резюме Я открыл эту главу, пообещав увлекательное образовательное приключение. Надеюсь, вы испытали несколько всплесков адреналина, изучая новые взгляды на алгебраические уравнения, разложение матриц и обратную матрицу. Вот ключевые выводы, которые следует вынести из этой главы. Системы уравнений можно преобразовывать в матричное уравнение. Помимо обеспечения компактного представления, это обеспечивает возможность находить самые изощренные линейно-алгебраические решения систем уравнений. При работе с матричными уравнениями следует помнить, что манипуляции должны применяться к обеим частям уравнения и что умножение матриц некоммутативно. Приведение строк – это процедура, в которой строки матрицы A скалярно умножаются и складываются до тех пор, пока матрица не будет линейно преобразована в верхнетреугольную матрицу U. Набор линейных преобразований можно хранить в еще одной матрице L–1, которая умножает матрицу A слева, чтобы произвести выражение L–1A = U. Приведение строк веками использовалось для ручного решения систем уравнений, включая обратную матрицу. Мы по-прежнему используем процедуру приведения строк, хотя компьютеры берут на себя всю арифметику. Приведение строк также используется для реализации LU-разложения. LU-разложение является уникальным в условиях некоторых ограничений, которые встроены в функцию lu() библиотеки SciPy.
Упражнения по программированию Упражнение 10.1 LU-разложение бывает вычислительно емким, хотя оно и эффективнее других разложений, таких как QR. Интересно, что LU-разложение часто ис-
Упражнения по программированию 187
пользуется в качестве эталона для сравнения времени вычислений между операционными системами, аппаратными процессорами, компьютерными языками (например, C, Python и MATLAB) или реализуемыми алгоритмами. Из любопытства я проверил время, которое потребовалось Python и MATLAB на выполнение LU-разложения на тысяче матриц размера 100×100. На моем ноутбуке MATLAB заняло около 300 мс, а Python – около 410 мс. Исходный код Python в Google Colab занял около 1000 мс. Проверьте, сколько времени он займет на вашем компьютере. Упражнение 10.2 Примените метод матричного умножения, чтобы создать матрицу 6×8 ранга 3. Возьмите его LU-разложение и покажите три матрицы с их рангами в заголовке, как на рис. 10.2. Обратите внимание на ранги трех матриц и на все единицы на диагонали матрицы L. Можете свободно обследовать ранги матриц с другими размерами и рангами.
Рис. 10.2 Результаты упражнения 10.2
Упражнение 10.3 Одним из примерений LU-разложения является вычисление определителя1. Вот два свойства определителя: определитель треугольной матрицы равен произведению диагоналей, а определитель матрицы произведения равен произведению определителей (то есть det(AB) = det(A)det(B)). Объединив эти два факта вместе, вы сможете вычислить определитель матрицы как произведение диагоналей матрицы L, умноженное на произведение диагоналей матрицы U. С другой стороны, поскольку все диагонали матрицы L равны 1 (при реализации на Python в целях обеспечения уникальности разложения), определитель матрицы A – это просто произведение диагоналей матрицы U. Попробуйте это на Python – и, перед тем как читать следующий далее абзац, сравните с результатом вызова функции np.linalg.det(A) – несколько раз с разными случайными матрицами. 1
Это один из бесчисленных аспектов линейной алгебры, которые вы могли бы изучить в традиционном учебнике по линейной алгебре; они интересны сами по себе, но имеют меньшее отношение к науке о данных.
188 Приведение строк и LU-разложение Получили ли вы тот же результат, что и Python? Допускаю, что вы обнаружили совпадение детерминантов по величине, но знаки, по-видимому, будут различаться случайным образом. Почему это произошло? Это произошло потому, что в инструкциях я опустил матрицу перестановок. Определитель матрицы перестановок равен +1 при четном числе взаимообменов строк и −1 при нечетном числе взаимообменов строк. Теперь вернитесь к своему исходному коду и вставьте определитель P в свои вычисления. Упражнение 10.4 Следуя формуле из раздела «LU-разложение» на стр. 183, обратная матри ца выражается как A = PTLU; A–1 = (PTLU)–1; A–1 = U–1L–1P. Реализуйте третье уравнение напрямую, используя данные на выходе из функции scipy.linalg.lu на матрице случайных чисел 4×4. Будет ли AA–1 единичной матрицей? Иногда да, а иногда нет, в зависимости от P. Это расхождение возникает по причине описанных мной данных на выходе из функции scipy.linalg.lu. Скорректируйте исходный код так, чтобы он соответствовал соглашению, принятому в библиотеке SciPy, а не математическому соглашению. Вот вывод, который следует вынести из этого упражнения: отсутствие сообщений об ошибках не обязательно означает, что ваш исходный код является правильным. Пожалуйста, проверяйте исправность своего математического исходного кода как можно тщательнее. Упражнение 10.5 Для матрицы A = PLU (с использованием упорядочения матрицы перестановок на Python) ATA может вычисляться как UTLTLU – без матриц перестановок. Почему можно отбросить матрицу перестановок? Ответьте на вопрос, а затем, используя случайные матрицы, подтвердите на Python, что ATA = UTLTLU, даже при P ¹ I.
Общие линейные модели и наименьшие квадраты Вселенная – действительно большое и реально замысловатое место. Все животные на Земле обладают естественным любопытством, побуждающим их обследовать и пытаться понять окружающую среду, но мы, люди, наделены интеллектом, позволяющим разрабатывать научные и статистические инструменты, чтобы выводить наше любопытство на новый уровень. Вот почему у нас есть самолеты, аппараты МРТ, марсоходы, вакцины и, конечно же, книги, подобные этой. Как мы познаем вселенную? Разрабатывая математически обоснованные теории и собирая данные, чтобы тестировать и совершенствовать эти теории. И это подводит нас к статистическим моделям. Статистическая модель – это упрощенное математическое представление какого-то отдельного аспекта мира. Некоторые статистические модели характерны своей простотой (например, предсказание роста фондового рынка в течение десятилетий); другие гораздо более изощренны, например проект Blue Brain, в котором симулируется деятельность мозга с такой точностью, что одна секунда симулируемой активности требует 40 минут вычислительного времени. Ключевое отличие статистических моделей (в отличие от других математических моделей) заключается в том, что они содержат свободные параметры, которые подгоняются к данным. Например, я знаю, что фондовый рынок со временем вырастет, но не знаю, насколько. Поэтому я допускаю, что изменение цены на фондовом рынке с течением времени (то есть наклон1) является свободным параметром, числовое значение которого определяется данными. Разработка статистической модели сопряжена с различными трудностями и требует творческого подхода, опыта и знаний. Но отыскание свободных параметров, основываясь на подгонке модели к данным, является элементарным вопросом линейной алгебры – на самом деле вы уже знаете всю математику, необходимую для этой главы; это просто вопрос соединения частей воедино и усвоения статистической терминологии. 1
Син. угол наклона, угловой коэффициент. – Прим. перев.
190 Общие линейные модели и наименьшие квадраты
Общие линейные модели Статистическая модель представляет собой систему уравнений, связывающих предсказатели (именуемые независимыми переменными) с наблюдениями (именуемыми зависимой переменной). В модели фондового рынка независимой переменной является время, а зависимой переменной – цена на фондовом рынке (например, количественно определяемая как индекс S&P 500). В этой книге я сосредоточусь на общих линейных моделях, сокращенно обозначаемых как GLM1. Например, регрессия является одним из типов общей линейной модели.
Терминология Статистики используют несколько иную терминологию, чем линейные алгеб раисты. В табл. 11.1 показаны ключевые буквы и описания векторов и мат риц, используемых в общей линейной модели. Таблица 11.1. Таблица членов общей линейной модели Линейная алгебра Статистика Ax = b Xβ = y A X x b
Описание Общая линейная модель (GLM) Расчетная матрица (столбцы = независимые переменные, предсказатели, регрессоры) Коэффициенты регрессии или бета-параметры Зависимая переменная, исход, мера результата, данные
Настройка общей линейной модели Настройка общей линейной модели предусматривает: 1) определение уравнения, которое связывает предсказательные переменные с зависимой переменной; 2) отображение наблюдаемых данных в уравнения; 3) преобразование серии уравнений в матричное уравнение; 4) решение этого уравнения. Я буду использовать простой пример, чтобы конкретизировать указанную процедуру. У меня есть модель, которая предсказывает рост взрослого человека на основе веса и роста родителей. Уравнение выглядит следующим образом:
y = β0 + β1w + β2h + ϵ,
Англ. General Linear Model. – Прим. перев.
Общие линейные модели 191
где y – это рост человека, w – его вес, h – рост его родителей (средние показатели матери и отца). ϵ – ошибка (также именуемая остатком), потому что невозможно разумно ожидать, что вес и рост родителей полностью определяют рост человека; наша модель не учитывает громадное число факторов, и дисперсия, не связанная с весом и ростом родителей, будет поглощена остатком. Моя гипотеза состоит в том, что вес и рост родителей имеют важность для роста человека, но я не знаю степень важности каждой переменной. Теперь введем члены β: это коэффициенты, или веса, которые говорят мне о том, как комбинировать вес и рост родителей, чтобы предсказывать рост человека. Другими словами, это линейно-взвешенная комбинация, где β – это веса. Член β 0 называется пересечением1 (иногда его называют константой). Член пересечения – это вектор, состоящий из одних единиц. Без указанного члена линия наилучшей подгонки будет вынужденно проходить через начало координат. Я объясню причину и покажу демонстрацию ближе к концу главы. Теперь у нас есть уравнение, модель Вселенной (ну, хотя бы одна крошечная ее часть). Далее необходимо отобразить наблюдаемые данные в уравнения. Для простоты я выдумаю немного данных и сведу их в табл. 11.2 (как вы, наверное, догадались, y и h измеряются в сантиметрах, а w – в килограммах). y 175 181 159 165
h 177 190 180 172
Таблица 11.2. Выдуманные данные для статистической модели роста
Отображение наблюдаемых данных в нашу статистическую модель предусматривает повторение уравнения четыре раза (что соответствует четырем наблюдениям в наборе данных), всякий раз заменяя переменные y, w и h измеренными данными: 175 = β0 + 70β1 + 177β2 + ϵ;
181 = β0 + 86β1 + 190β2 + ϵ; 159 = β0 + 63β1 + 180β2 + ϵ;
165 = β0 + 62β1 + 172β2 + ϵ.
Пока что я опускаю член ϵ; об остатках я расскажу позже. Теперь нам нужно переложить эту систему уравнений в матричное уравнение. Я знаю, что вы знаете, как это делается, поэтому распечатаю здесь уравнение только для того, чтобы вы могли подтвердить то, что уже знаете из главы 10: 1
Син. точка пересечения, коэффициент сдвига. – Прим. перев.
192 Общие линейные модели и наименьшие квадраты
И конечно же, это уравнение можно выразить кратко как Xβ = y.
Решение общих линейных моделей Уверен, что вы уже знаете главную идею этого раздела: для того чтобы найти вектор неизвестных коэффициентов β, надо просто умножить обе части уравнения слева на левообратную матрицу X, то есть расчетную матрицу. Решение выглядит так: Xβ = y; (XTX)–1XTXβ = (XTX)–1XTy; β = (XTX)–1XTy. Пожалуйста, всмотритесь в заключительное уравнение и удерживайте свой взор на нем до тех пор, пока оно не отпечатается в вашем мозгу навсегда. Оно называется решением методом наименьших квадратов и является одним из наиболее важных математических уравнений в прикладной линейной алгебре. Вы будете встречать его в научных публикациях, учебниках, блогах, лекциях, документационных литералах по функциям Python, на рекламных щитах в Таджикистане1 и во многих других местах. При этом вы будете видеть другие буквы или, возможно, некоторые дополнения, например следующие ниже: b = (HTWH + λLTL)–1HTx.
Смысл этого уравнения и интерпретация дополнительных матриц не важны (это различные способы регуляризации подгонки модели); важно то, что вы способны увидеть формулу наименьших квадратов, встроенную в это замысловато выглядящее уравнение (например, представьте, что W = I и λ = 0). Решение методом наименьших квадратов посредством левообратной мат рицы можно переложить напрямую в исходный код Python (переменная X – это расчетная матрица, а переменная y – вектор данных): X_leftinv = np.linalg.inv(X.T@X) @ X.T # найти коэффициенты beta = X_leftinv @ y 1
Признаться, никогда не видел это уравнение на таджикском рекламном щите, но суть в том, чтобы быть открытым ко всему новому.
Решение общих линейных моделей 193
Позже в этой главе я покажу результаты этих моделей – и то, как их интерпретировать; сейчас же я бы хотел, чтобы вы сосредоточились на том, как математические формулы переводятся в исходный код Python.
Левообратная матрица в спосоставлении с решателем методом наименьших квадратов NumPy Исходный код в этой главе является прямым переложением математики в исходный код Python. Вычисление левообратной матрицы в явной форме – не самый численно стабильный способ решения общей линейной модели (хотя и точен для простых задач этой главы), но я хочу, чтобы вы увидели, что кажущаяся абстрактной линейная алгебра работает реально. Существуют более численно стабильные способы решения общей линейной модели, включая QR-разложение (который вы увидите позже в этой главе) и более численно стабильные методы Python (с которыми вы познакомитесь в следующей главе).
Является ли решение точным? Уравнение Xβ = y точно разрешимо, когда y находится в столбцовом пространстве расчетной матрицы X. Поэтому вопрос заключается в том, гарантированно ли вектор данных находится в столбцовом пространстве статис тической модели. Ответ – нет, такой гарантии нет. На самом деле вектор данных y почти никогда не находится в столбцовом пространстве матрицы X. В целях понимания причины, по которой такой гарантии нет, давайте представим опрос студентов университета, в ходе которого исследователи пытаются предсказать средний балл GPA (средний балл успеваемости) на основе поведения, связанного с употреблением алкоголя. Опрос может содержать данные, полученные от двух тысяч студентов, но имеет только три вопроса (например, сколько алкоголя вы употребляете; как часто вы теряете сознание; какой у вас средний балл). Данные содержатся в таблице 2000×3. Столбцовое пространство расчетной матрицы является 2D-подпространством внутри этой объемлющей размерности 2000D, а вектор данных является 1D-подпространством внутри той же объемлющей размерности. Если данные находятся в столбцовом пространстве расчетной матрицы, то это означает, что модель учитывает 100 % дисперсии данных. Но этого почти никогда не происходит: реальные данные содержат шум и изменчивость отбора экземпляров, а модели представляют собой упрощения, не учитывающие всей изменчивости (например, средний балл GPA определяется громадным числом факторов, которые наша модель игнорирует). Решение этой головоломки заключается в видоизменении уравнения общей линейной модели так, чтобы учесть расхождение между модельно-предсказанными и наблюдаемыми данными. Его можно выразить несколькими эквивалентными способами (с точностью до знака): Xβ = y + ϵ; Xβ + ϵ = y; ϵ = Xβ – y.
194 Общие линейные модели и наименьшие квадраты Первое уравнение интерпретируется так: ϵ – это остаток, или член ошибки, который добавляется в вектор данных, чтобы он помещался внутри столбцового пространства расчетной матрицы. Второе уравнение интерпретируется следующим образом: остаточный член – это корректировка расчетной мат рицы таким образом, чтобы она идеально вписывалась в данные. Наконец, интерпретация третьего уравнения такова: остаток определяется как разница между модельно-предсказанными и наблюдаемыми данными. Существует еще одна, очень проницательная интерпретация, которая подходит к общим линейным моделям и методу наименьших квадратов с геометрической точки зрения. Я вернусь к ней в следующем разделе. Суть же этого раздела в том, что наблюдаемые данные почти никогда не находятся внутри подпространства, охватываемого регрессорами. По этой причине также нередко можно увидеть, как общая линейная модель выражается как X = βyˆ, где yˆ = y + ϵ. Следовательно, цель общей линейной модели состоит в том, чтобы найти линейную комбинацию регрессоров, максимально приближенную к наблюдаемым данным. Подробнее об этом позже; теперь же я хочу познакомить вас с геометрической перспективой наименьших квадратов.
Геометрическая перспектива наименьших квадратов До сих пор я представлял решение общей линейной модели с алгебраической точки зрения решения матричного уравнения. Существует также геометрическая перспектива общей линейной модели, которая обеспечивает альтернативную перспективу и помогает раскрыть несколько важных особенностей решения задачи о наименьших квадратах. Предположим, что столбцовое пространство расчетной матрицы C(X) является подпространством ℝM. В типичной ситуации это очень низкоразмерное подпространство (то есть N |t| [0.025 0.975] =========================================================================== const 530.4946 9.313 56.963 0.000 512.239 548.750 Rainfall(mm) -80.5237 5.818 -13.841 0.000 -91.928 -69.120 Seasons 369.1267 13.127 28.121 0.000 343.395 394.858 1
Англ. Ordinary Least Squares. – Прим. перев.
Предсказывание количеств велопрокатов на основе погоды 213 =========================================================================== Omnibus: 1497.901 Durbin-Watson: 0.240 Prob(Omnibus): 0.000 Jarque-Bera (JB): 2435.082 Skew: 1.168 Prob(JB): 0.00 Kurtosis: 4.104 Cond. No. 2.80 ===========================================================================
Мультиколлинеарность Если вы посещали курсы статистики, то, возможно, слышали о термине мультиколлинеарность. Его определение в Википедии гласит: «в модели множест венной регрессии одна предсказательная переменная может с существенной степенью точности линейно предсказываться по другим переменным»1. Это означает, что в расчетной матрице существуют линейные зависимости. На языке линейной алгебры мультиколлинеарность – это просто причудливый термин, который обозначает линейную зависимость, что равносильно утверждению, что расчетная матрица имеет пониженный ранг либо что она сингулярна. Рангово-пониженная расчетная матрица не имеет левообратной матри цы, а значит, задача о наименьших квадратах не решаема аналитически. Вы увидите последствия мультиколлинеарности в упражнении 12.3.
Решение общей линейной модели с мультиколлинеарностью На самом деле существует возможность аналитического вывода решения для общих линейных моделей, которые имеют рангово-пониженную расчетную матрицу. Это делается с помощью модификации процедуры QR-разложения из предыдущей главы и с использованием псевдообратной матрицы Mура–Пенроуза. В случае рангово-пониженной расчетной матрицы единственного решения нет, но можно выбрать решение с минимальной ошибкой. Это называется решением с минимальной нормой, или просто решением min-norm, и часто используется, например, в биомедицинской визуализации. Несмотря на специальные применения, расчетная матрица с линейными зависимостями обычно указывает на проблему со статистической моделью и должна быть всячески обследована (распространенными источниками мультиколлинеарности являются ошибки ввода данных и ошибки программирования).
Регуляризация Регуляризация – это зонтичный термин, который относится к различным способам модификации статистической модели с целью улучшения численной стабильности, преобразования сингулярных или плохо кондиционных мат риц в полноранговые (и, следовательно, обратимые) или улучшения обобщаемости за счет уменьшения переподгонки. В зависимости от характера 1
Википедия, поиск по «multicollinearity», https://en.wikipedia.org/wiki/Multicollinearity.
214 Применения метода наименьших квадратов задачи и цели регуляризации применяется несколько форм регуляризации; некоторые конкретные методы, о которых вы, возможно, слышали, включают гребневую (она же L2), лассо (она же L1), тихоновскую и усадочную1. Разные методы регуляризации работают по-разному, но многие регуляризаторы «сдвигают» расчетную матрицу на некоторую величину. Из главы 5 вы помните, что сдвиг матрицы означает добавление некоторой константы к диагонали в виде A + λI, а из главы 6 – что сдвиг матрицы может преобразовывать рангово-пониженную матрицу в полноранговую. В этой главе мы выполним регуляризацию расчетной матрицы, сдвинув ее в соответствии с некоторой долей ее фробениусовой нормы. За счет этого видоизменится решение уравнения 12.1 наименьших квадратов. Уравнение 12.1. Регуляризация β = (XTX + γ||X||2F I)–1XTy.
Ключевым параметром является γ (греческая буква гамма), который определяет степень регуляризации (обратите внимание, что γ = 0 соответствует отсутствию регуляризации). Выбор подходящего параметра γ нетривиален и нередко осуществляется с помощью статистических методов, таких как перекрестная валидация. Наиболее очевидный эффект регуляризации заключается в том, что если расчетная матрица имеет пониженный ранг, то регуляризованная квадратная расчетная матрица имеет полный ранг. Регуляризация также уменьшает кондиционное число, которое измеряет «разброс» информации в матрице (это отношение наибольшего сингулярного числа к наименьшему; вы узнаете о нем в главе 14). Благодаря ей увеличивается численная стабильность матрицы. В статистическом плане последствием регуляризации является «сглаживание» решения за счет снижения чувствительности модели к отдельным точкам данных, которые могут быть выбросами либо нерепрезентативными, и, следовательно, вероятность наблюдать их в новых наборах данных весьма мала. Почему я шкалирую на квадрат нормы Фробениуса? Учтите, что указанное значение γ, скажем γ = 0.01, может иметь огромное или незначительное влияние на расчетную матрицу в зависимости от диапазона числовых значений в матрице. Поэтому мы шкалируем в числовой диапазон матрицы, то есть мы интерпретируем параметр γ как долю регуляризации. Причина возведения в квадрат нормы Фробениуса заключается в том, что ||X||2F = ||XTX||F . Другими словами, квадрат нормы расчетной матрицы равен норме расчетной матри цы, умноженной на ее транспонированную версию. На самом деле вместо нормы Фробениуса чаще используется среднее значение собственных чисел расчетной матрицы. Познакомившись с собственными числами в главе 13, вы сможете сравнить два метода регуляризации. Реализации регуляризации в исходном коде посвящено упражнение 12.4. 1
Англ. shrinkage. – Прим. перев.
Полиномиальная регрессия 215
Полиномиальная регрессия Полиномиальная регрессия похожа на обычную регрессию, но независимыми переменными являются значения оси x, возводимые в более высокие степени. То есть каждый столбец i расчетной матрицы определяется как xi, где x – это обычно время или пространство, но может быть и другими переменными, такими как дозировка лекарства или численность населения. Математическая модель выглядит так: y = β0 x 0 + β 1x 1 + … + β n x n.
Обратите внимание на x0 = 1, которое дает пересечение модели. В противном случае это по-прежнему будет обычной регрессией – цель состоит в том, чтобы найти значения β, которые минимизируют квадраты разниц между предсказанными и наблюдаемыми данными. Степень многочлена равна наибольшей степени i. Например, полиномиальная регрессия четвертой степени имеет члены до x4 (если нет члена x3, то это по-прежнему модель четвертой степени с β3 = 0). На рис. 12.5 показан пример отдельных регрессоров и расчетные матри цы многочлена третьей степени (имейте в виду, что многочлен n-й степени имеет n + 1 регрессоров, включая пересечение). Полиномиальные функции являются базисными векторами, служащими для моделирования наблюдаемых данных. Отдельные регрессоры
Рис. 12.5 Расчетная матрица полиномиальной регрессии
216 Применения метода наименьших квадратов За исключением специальной расчетной матрицы, полиномиальная регрессия выполняется точно так же, как и любая другая регрессия: применить левообратную матрицу (либо более вычислительно стабильные альтернативы), чтобы получить набор коэффициентов, такой что взвешенная комбинация регрессоров (то есть предсказанные данные) совпадает с наблюдаемыми данными наилучшим образом. Полиномиальные регрессии используются для подгонки кривых и аппроксимации нелинейных функций. Приложения включают моделирование временных рядов, динамику населения, функции доза–реакция в медицинских исследованиях и физические нагрузки на опорные балки. Полиномы также могут выражаться в 2D, которые используются для моделирования пространственной структуры, такой как распространение землетрясений и активность мозга. Но довольно фоновой информации. Давайте поработаем на примере. Я выбрал набор данных, который основан на модели удвоения населения. Вопрос заключается в следующем: «Сколько времени потребуется для того, чтобы население человечества удвоилось (например, с пятисот миллионов до одного миллиарда)?» Если скорость прироста населения сама по себе увеличивается (поскольку у большего числа людей рождается больше детей, а у тех, кто вырастает, рождается еще больше детей), то время удвоения будет уменьшаться с каждым удвоением. С другой стороны, если рост населения замедляется (люди рожают меньше детей), то время удвоения будет увеличиваться по сравнению с последующими удвоениями. Я нашел подходящий набор данных в интернете1. Это небольшой набор данных, поэтому все цифры доступны в онлайновом исходном коде и показаны на рис. 12.6. Указанный набор данных включает в себя как фактические измеренные данные, так и проекции до 2100 года. Эти проекции в будущее основаны на ряде допущений, и никто толком не знает, как сложится будущее (поэтому вам следует находить баланс между подготовкой к будущему и наслаждением моментом). Тем не менее данные на сей момент показывают, что за последние пятьсот лет (по меньшей мере) население человечества удваивалось с возрастающей частотой, и авторы набора данных предсказывают, что скорость удвоения немного увеличится в следующем столетии. Для подгонки к данным я выбрал многочлен третьей степени, а затем создал модель и выполнил ее подгонку, используя следующий ниже исходный код (переменная year содержит координаты по оси x, а переменная doubleTime содержит зависимую переменную): # расчетная матрица X = np.zeros((N, 4)) for i in range(4): X[:, i] = np.array(year)**i # выполнить подгонку модели и # вычислить предсказанные данные 1
Розер Макс, Ричи Ханна и Ортис-Оспина Эстебан. Прирост населения мира, OurWorldInData.org, 2013 г., https://ourworldindata.org/world-population-growth.
Полиномиальная регрессия 217
Время удвоения (годы)
beta = np.linalg.lstsq(X, doubleTime, rcond=None) yHat = X@beta[0]
Год Рис. 12.6 График данных
Время удвоения (годы)
На рис. 12.7 показаны предсказанные данные с использованием полиномиальной регрессии, созданной этим исходным кодом.
Год Рис. 12.7 График данных
Модель улавливает как нисходящий тренд, так и спроецированный виток вверх в данных. Без дальнейшего статистического анализа невозможно сказать, что это наилучшая модель или что указанная модель статистически значимо хорошо вписывается в данные. Но ясно одно, что полиномиальные
218 Применения метода наименьших квадратов регрессии хорошо подходят для подгонки кривых. В упражнении 12.5 вы продолжите обследовать эту модель и данные, но рекомендую вам поэкс периментировать с исходным кодом, создающим рис. 12.7, пробуя разные параметры степени. Полиномиальные регрессии находят широкое применение, и в библиотеке NumPy имеются специальные функции для создания и подгонки таких моделей: beta = np.polyfit(year, doubleTime, 3) # 3-я степень yHat = np.polyval(beta, year)
Поиск в параметрической решетке для отыскания модельных параметров Метод наименьших квадратов посредством левообратной матрицы – отличный способ подгонки моделей к данным. Метод наименьших квадратов является точным, быстрым и детерминированным (это означает, что при каждом повторном прогоне исходного кода вы будете получать один и тот же результат). Но он работает только для подгонки линейных моделей, и не все модели можно подгонять с помощью линейных методов. В этом разделе я познакомлю вас с еще одним методом оптимизации, используемым для выявления модельных параметров, именуемым поиском в параметрической решетке1. Поиск в параметрической решетке работает путем отбора значений параметра в параметрическом пространстве, вычисления подгонки модели к данным с каждым значением параметра, а затем отбора значения, дающего наилучшую подгонку модели. В качестве простого примера возьмем функцию y = x2. Нам нужно найти минимум этой функции. Разумеется, мы уже знаем, что минимум находится в x = 0; это поможет понять и оценить результаты поиска в решетке параметров. При выполнении поиска в параметрической решетке мы начинаем с предопределенного набора тестируемых значений x. Давайте воспользуемся набором (−2, −1, 0, 1, 2). Это наша «решетка». Затем по каждому значению в решетке мы вычисляем функцию и получаем y = (4, 1, 0, 1, 4). И находим, что минимум y происходит при x = 0. В этом случае решение на основе решетки совпадает с истинным решением. Но поиск в параметрической решетке не гарантирует оптимального решения. Например, представьте, что наша решетка была бы (−2, −0.5, 1, 2.5); значения функции были бы y = (4, 0.25, 1, 6.25), и мы бы заключили, что x = –0.5 является значением параметра, которое минимизирует функцию y = x2. Этот 1
Англ. Grid Search; идея поиска в параметрической решетке состоит в создании «решетки» параметров и просто опробывании всех возможных их комбинаций. – Прим. перев.
Поиск в параметрической решетке для отыскания модельных параметров 219
вывод будет «отчасти правильным», потому что в указанной решетке это лучшее решение. Ошибки при поиске в параметрической решетке также могут возникать из-за неправильно выбранного диапазона значений. Представьте, например, что наша решетка была бы (−1000, −990, −980, −970); мы пришли бы к выводу, что y = x2 минимизируется при x = –970. Дело в том, что и диапазон, и разрешающая способность (расстояние между точками решетки) имеют большую важность, потому что они определяют получаемое вами решение, которое может быть наилучшим, довольно хорошим либо ужасным. В приведенном выше игрушечном примере подходящий диапазон и разрешающая способность определяются легко. В многосложных, многопеременных, нелинейных моделях для поиска в параметрической решетке могут потребоваться дополнительная работа и обследование подходящих параметров. Я выполнил поиск в параметрической решетке на данных о «счастливых студентах» из предыдущей главы (напомню: это были поддельные данные из поддельного опроса, показывающего, что люди, записавшиеся на большее количество моих курсов, были более удовлетворены жизнью). Модель этих данных имеет два параметра – пересечение и наклон, поэтому мы вычисляем эту функцию в каждой точке двумерной решетки возможных пар парамет ров. Результаты показаны на рис. 12.8. Что означает этот график и как его интерпретировать? Две оси соответствуют значениям параметров, поэтому каждая координата на этом графике создает модель с соответствующими значениями параметров. Затем вычисляется подгонка каждой из этих моделей к данным, сохраняется и визуализируется в виде изображения. Координата с наилучшей полгонкой к данным (наименьшая сумма квадратов ошибок) является оптимальным набором параметров. На рис. 12.8 также показано аналитическое решение с использованием метода наименьших квадратов. Они близки, но не накладываются друг на друга точно. В упражнении 12.6 у вас будет возможность реализовать этот поиск по параметрической решетке, а также обследовать вопрос важности разрешающей способности решетки для точности результатов. Зачем вообще кому-то использовать поиск в параметрической решетке, когда метод наименьших квадратов работает лучше и быстрее? Все верно, не следует использовать метод поиска в параметрической решетке, когда жизнеспособным решением является метод наименьших квадратов. Вместе с тем поиск в параметрической решетке является полезным методом отыскания параметров в нелинейных моделях и нередко используется, например, для определения гиперпараметров в моделях глубокого обучения (гиперпараметры – это конструктивные особенности модельной архитектуры, которые отбираются исследователем, а не извлекаются из данных). В случае больших моделей поиск в параметрической решетке бывает времязатратным, но его параллелизация может делать его выполнение более приемлемым. Вывод состоит в том, что поиск в параметрической решетке является нелинейным методом подгонки моделей к данным в ситуациях, когда линейные методы применить невозможно. По ходу своего путешествия по науке о данных и машинному обучению вы также узнаете о дополнительных не-
220 Применения метода наименьших квадратов линейных методах, включая симплекс и знаменитый алгоритм градиентного спуска, который приводит в действие глубокое обучение. SSE (подгонка модели к данным)
Минимум по параметрической решетке Аналитическое решение
Рис. 12.8 Результаты поиска в параметрической решетке на наборе данных «Счастливый студент». Интенсивность показывает сумму квадратов ошибок, подогнанных к данным
Резюме Надеюсь, вам понравилось читать о применении метода наименьших квад ратов и сравнении с другими подходами к подгонке моделей. Следующие ниже упражнения являются наиболее важной частью этой главы, поэтому я не хочу отнимать у вас время подведением итогов. Вот важные моменты главы. Визуальное обследование данных имеет большую важность для отбора правильных статистических моделей и правильной интерпретации статистических результатов. Линейная алгебра используется для количественного оценивания наборов данных, включая матрицы корреляций. Методы визуализации матриц, о которых вы узнали в главе 5, полезны для инспектирования расчетных матриц. В разных областях математические понятия иногда получают разные названия. В этой главе понятие мультиколлинеарности является одним из таких примеров; оно означает линейные зависимости в расчетной матрице.
Упражнения по программированию 221
Регуляризация предусматривает «сдвиг» расчетной матрицы на некоторую малую величину, в целях повышения численной стабильности и вероятности обобщения на новых данных. Глубокое понимание линейной алгебры поможет вам отбирать наиболее подходящий вид статистического анализа, интерпретировать результаты и предвидеть возможные проблемы. Полиномиальная регрессия аналогична «обычной» регрессии, но столбцы в расчетной матрице определяются как значения по оси x, возводимые в возрастающую степень. Полиномиальные регрессии используются для подгонки кривых. Поиск в параметрической решетке – это нелинейный метод подгонки моделей. Линейный метод наименьших квадратов является оптимальным подходом, когда модель является линейной.
Упражнения по программированию Упражнения по аренде велосипедов Упражнение 12.1 Возможно, часть проблемы с отрицательными велопрокатами на рис. 12.4 можно решить, исключив дни без дождя. Повторите анализ и график этого анализа, но выберите только те строки данных, в которых имеется нулевое количество осадков. Улучшатся ли результаты с точки зрения более высокого R2 и положительного предсказанного количества велопрокатов? Упражнение 12.2 Поскольку времена года – это категориальная переменная, дисперсионный анализ действительно был бы более подходящей статистической мод елью, чем регрессия. Возможно, бинаризованным временам года не хватает чувствительности для предсказания количеств велопрокатов (например, осенью могут быть теплые солнечные дни, а весной – холодные дождливые дни), и поэтому температура может быть самым лучшим предсказателем. Замените времена года в расчетной матрице температурой и выполните повторный прогон регрессии (можно использовать все дни, а не только дни без осадков из предыдущего упражнения) и воспроизведите рис. 12.9. Остается проблема предсказания отрицательных велопрокатов (это связано с линейностью модели), но R2 получается выше, и предсказание выглядит качественно лучше.
222 Применения метода наименьших квадратов
Предсказанные количества велопрокатов
Подгонка модели (R2): 0.313
Наблюдаемые количества велопрокатов
Рис. 12.9 Результаты упражнения 12.2
Упражнения по мультиколлинеарности Упражнение 12.3 Это упражнение продолжает работу с моделью из упражнения 12.21. Указанная модель содержит три регрессора, включая пересечение. Создайте новую расчетную матрицу, содержащую четвертый регрессор, определенный как некоторая линейно-взвешенная комбинация температуры и количества осадков. Дайте этой расчетной матрице другое имя переменной, потому что она понадобится вам в следующем упражнении. Подтвердите, что расчетная матрица имеет четыре столбца, но имеет ранг 3, и вычислите матрицу корреляций расчетной матрицы. Обратите внимание, что в зависимости от перевесовки двух переменных корреляция ожидаемо не будет равна 1, даже при линейных зависимостях; здесь вы также не ожидаете воспроизвести точные корреляции: Размер расчетной матрицы: (8760, 4) Ранг расчетной матрицы: 3 Матрица корреляций расчетной матрицы: [[1. 0.05028 nan 0.7057 ] [0.05028 1. nan 0.74309] [ nan nan nan nan] [0.7057 0.74309 nan 1. ]] 1
Если вы столкнулись с ошибками Python, то попробуйте выполнить повторный прогон предыдущего исходного кода, а затем повторно создать переменные расчетной матрицы.
Упражнения по программированию 223
Выполните подгонку модели, используя три разных программных подхода: 1) прямая реализация с левообратной матрицей, как вы узнали из предыдущей главы; 2) с использованием функции NumPy lstsqr; 3) с использованием статистических моделей. Для всех трех методов вычислите R2 и коэффициенты регрессии. Распечатайте результаты, как показано ниже. Отчетливо видна численная нестабильность функции np.linalg.inv на рангово-пониженной расчетной матрице. ПОДГОНКА МОДЕЛИ К ДАННЫМ: Левообратная матрица: 0.0615 np lstsqr : 0.3126 statsmodels : 0.3126 БЕТА-КОЭФФИЦИЕНТЫ: Левообратная матрица: [[-1528.071 11.277 337.483 5.537 ]] np lstsqr : [[ -87.632 7.506 337.483 5.234 ]] statsmodels : [ -87.632 7.506 337.483 5.234 ]
Попутное замечание: никаких сообщений об ошибках, ни предупреждений не поступало; Python просто выдал результаты, хотя с расчетной матрицей явно что-то не так. Можно обсуждать достоинства такого поведения, но этот пример еще раз подчеркивает важность понимания линейной алгебры в науке о данных и что правильная наука о данных – это больше, чем просто знание математики.
Упражнения по регуляризации Упражнение 12.4 Здесь вы займетесь обследованием эффектов регуляризации на ранговопониженную расчетную матрицу, которую вы создали в предыдущем упражнении. Начните с реализации (XTX + γ||X||2F I)–1, используя γ = 0 и γ = 0.01. Распечатайте размер и ранг двух матриц. Вот мои результаты (интересно отметить, что расчетная матрица ранга 3 является настолько численно нестабильной, что ее «обратная матрица» фактически имеет ранг 2): размер inv(X’X + 0.0*I): (4, 4) ранг inv(X’X + 0.0*I) : 2 размер inv(X’X + 0.01*I): (4, 4) ранг inv(X’X + 0.01*I) : 4
Теперь об эксперименте. Здесь цель состоит в том, чтобы обследовать эффекты регуляризации на подгонку модели к данным. Напишите исходный код, который будет вычислять подгонку к данным как R2, используя метод наименьших квадратов с регуляризацией на расчетных матрицах с мультиколлинеарностью и без нее. Поместите этот исходный код в цикл for, реали-
224 Применения метода наименьших квадратов зующий диапазон значений γ от 0 до 0.2. Затем покажите результаты в виде рисунка, подобного рис. 12.10. Между прочим, тривиальным для полноранговых расчетных матриц случаем является уменьшение подгонки модели с увеличением регуляризации – и действительно, регуляризация предназначена для того, чтобы делать модель менее чувствительной к данным. Важный вопрос заключается в том, улучшает ли регуляризация подгонку к тестовому набору данных или валидационному блоку, который был исключен при подгонке модели. Если регуляризация выгодна, то можно ожидать увеличение обобщаемости регуляризованной модели до некоторого γ, а затем снова ее уменьшение. Это тот уровень детализации, о котором вы узнаете из специальной книги по статистике или машинному обучению, хотя в главе 15 вы все же доберетесь до перекрестной валидации исходного кода.
R 2 подгонки к данным
Рис. 12.10 Результаты упражнения 12.4
Упражнение по полиномиальной регрессии Упражнение 12.5 Цель этого упражнения – выполнить подгонку полиномиальной регрессии, используя диапазон степеней от нуля до девяти. В цикле for перевычисляйте регрессию и предсказанные значения данных. Покажите результаты, как на рис. 12.11. В этом упражнении высвечиваются проблемы недоподгонки и переподгонки. Модель со слишком малым числом параметров плохо справляется с предсказыванием данных. С другой стороны, модель с большим числом параметров слишком хорошо прилегает к данным и рискует оказаться чрезмерно чувствительной к шуму и быть не способной обобщать на новых данных. Стратегии отыскания баланса между недоподгонкой и переподгонкой
Упражнения по программированию 225
включают перекрестную валидацию и байесов информационный критерий; с этими темами вы познакомитесь в книге по машинному обучению или статистике. Степень = 0
Рис. 12.11 Результаты упражнения 12.5
Упражнения по поиску в параметрической решетке Упражнение 12.6 Здесь ваша цель будет простой: воспроизвести рис. 12.8, следуя инструкциям, представленным в тексте вокруг этого рисунка. Распечатайте коэффициенты регрессии для сравнения. Например, следующие ниже результаты были получены с использованием параметра разрешающей способности парамет рической решетки, заданной равной 50: Аналитический результат: Пересечение: 23.13, наклон: 3.70 Эмпирический результат: Пересечение: 22.86, наклон: 3.67
Когда у вас будет рабочий исходный код, попробуйте несколько разных параметров разрешающей способности. Я сделал рис. 12.8, используя разрешающую способность 100; вам также следует попробовать другие значения, например 20 или 500. Еще обратите внимание на время вычисления при более высоких значениях разрешающей способности – и это только двухпараметрическая модель! Исчерпывающий поиск в параметрической решетке с высокой разрешающей способностью для 10-параметрической модели потребует чрезвычайно больших вычислительных ресурсов. Упражнение 12.7 Вы видели два разных метода оценивания подгонки модели к данным: сумма квадратов ошибок и R2. В предыдущем упражнении для оценивания подгонки модели к данным вы использовали сумму квадратов ошибок;
226 Применения метода наименьших квадратов в этом упражнении вы определите, является ли вариант с R2 столь же жизнеспособным. Часть исходного кода в этом упражнении будет простой: надо видоизменить исходный код из предыдущего упражнения, вычислив R2 вместо SSE (проследите, чтобы видоизменения коснулись копии исходного кода, без перезаписи предыдущего упражнения). Теперь самое сложное: вы обнаружите, что R2 выглядит ужасно! Этот показатель дает совершенно неправильный ответ. Ваша задача – выяснить причину, по которой это так (онлайновое решение содержит изложение данного вопроса). Совет: сохраняйте предсказанные данные по каждой паре парамет ров, чтобы иметь возможность инспектировать предсказанные значения, а затем сравнивать их с наблюдаемыми данными.
13 Собственное разложение
Собственное разложение1 – жемчужина линейной алгебры. Что такое жемчужина? Позвольте мне процитировать прямо из книги «20 000 лье под водой»: Для поэта жемчужина – слеза моря, для восточных народов – окаменевшая капля росы; для женщин – драгоценный овальной формы камень с перламутровым блеском, который они носят, как украшение, на руках, на шее, в ушах; для химика – соединение фосфорнокислых солей с углекислым кальцием; и, наконец, для натуралиста – просто болезненный нарост, представляющий собою шаровидные наплывы перламутра внутри мягкой ткани мантии у некоторых представителей двустворчатых моллюсков. – Жюль Верн
Дело в том, что один и тот же объект видится по-разному в зависимости от его использования. То же самое и с собственным разложением: собственное разложение имеет геометрическую интерпретацию (оси вращательной инвариантности), статистическую интерпретацию (направления максимальной ковариации), системно-динамическую интерпретацию (стабильные состоя ния системы), теоретико-графовую интерпретацию (влияние узла на его сеть), финансово-рыночную интерпретацию (выявление ковариирующих акций) и многие другие. Собственное разложение (и SVD, которое, как вы узнаете в следующей главе, тесно связано с собственным разложением) – один из самых важных вкладов линейной алгебры в науку о данных. Цель этой главы – дать вам интуитивное понимание собственных чисел и собственных векторов – результатов собственного разложения матрицы. Попутно вы узнаете о диагонализации и других специальных свойствах симметричных матриц. В главе 14 – после того, как собственное разложение будет расширено на SVD, в главе 15 вы увидите несколько его приложений. 1
Англ. Eigendecomposition; син. спектральное разложение. – Прим. перев.
228 Собственное разложение
Только для квадратов Собственное разложение определено только для квадратных матриц. Матрицу размера M×N невозможно разложить, если только M не равно N. Неквадратные матрицы можно раскладывать с помощью SVD. Каждая квадратная матрица размера M×M имеет M собственных чисел (скаляров) и M соответствующих собственных векторов. Собственное разложение предназначено для выявления этих M векторно-скалярных пар.
Интерпретации собственных чисел и собственных векторов В следующих разделах я опишу несколько способов интерпретации собственных чисел/векторов. Конечно же, математика в любом случае остается одинаковой, но наличие нескольких перспектив может облегчить интуитивное понимание, которое, в свою очередь, поможет разобраться в том, как и почему собственное разложение имеет такую важность в науке о данных.
Геометрия На самом деле в главе 5 я уже познакомил вас с геометрической концепцией собственных векторов. На рис. 5.5 мы обнаружили, что существует особая комбинация матрицы и вектора, при которой матрица растягивает – но не поворачивает – этот вектор. Этот вектор является собственным вектором матрицы, а величина растяжения – собственным числом. На рис. 13.1 показаны векторы до и после умножения на матрицу 2×2. Два вектора на левом графике (v1 и v2) являются собственными векторами, а два вектора на правом графике – нет. Собственные векторы указывают в одном направлении до и после постпозиционного умножения матр и цы. В собственных числах кодируется степень растяжения; попытайтесь угадать собственные числа на основе визуального осмотра графика. Ответы – в сноске1. Вот геометрическая картина: собственный вектор означает, что умножение матрицы на вектор действует как умножение вектора на скаляр. Давайте посмотрим, сможем ли мы записать это в виде уравнения (мы можем, и это напечатано в уравнении 13.1). Уравнение 13.1. Уравнение собственного числа Av = λv. 1
Приблизительно 0.6 и 1.6.
Интерпретации собственных чисел и собственных векторов 229
Будьте осторожны с интерпретацией этого уравнения: оно не говорит, что матрица равна скаляру; в нем говорится, что влияние матрицы на вектор такое же, как влияние скаляра на тот же вектор.
Рис. 13.1 Геометрия собственных векторов
Эта формула называется уравнением собственного числа, и это еще одна ключевая формула линейной алгебры, которую стоит запомнить. Вы будете сталкиваться с ней на протяжении всей этой главы, вы увидите ее небольшую вариацию в следующей главе, и вы будете сталкиваться с ней много раз, изучая многопеременную статистику, обработку сигналов, оптимизацию, теорию графов и бесчисленное число других приложений, в которых среди нескольких одновременно регистрируемых признаков выявляются закономерности.
Статистика (анализ главных компонент) Одной из причин, по которой люди применяют статистику, является выявление и количественное оценивание взаимосвязей между переменными. Например, повышение глобальной температуры коррелирует с сокращением числа пиратов1, но насколько сильна эта взаимосвязь? Разумеется, когда у вас есть только две переменные, будет достаточно простой корреляции (подобной той, о которой вы узнали в главе 4). Но в многопеременном наборе данных, включающем десятки или сотни переменных, двухпеременные корреляции не способны выявлять глобальные закономерности. Давайте конкретизируем это на примере. Криптовалюты – это цифровые хранилища стоимости, закодированные в блочной цепи, так называемом блокчейне, который представляет собой систему отслеживания транзакций. Вы, наверное, слышали о биткойне и эфириуме; существуют десятки тысяч других криптомонет, которые имеют различное предназначение. Можно за1
«Открытое письмо школьному совету Канзаса», Церковь летающего макаронного монстра, spaghettimonster.org/about/open-letter.
230 Собственное разложение
дасться вопросом, работает ли все криптопространство как единая система (имеется в виду, что стоимость всех монет движется вверх и вниз вместе) или же в этом пространстве есть независимые подкатегории (имеется в виду, что стоимость некоторых монет или групп монет меняется независимо от стоимости других монет). Эту гипотезу можно проверить, выполнив анализ главных компонент на наборе данных, содержащем цены различных криптовалют с течением времени. Если бы весь крипторынок работал как единое целое, то график крутого склона1 (график собственных чисел матрицы ковариаций в наборе данных) показал бы, что одна компонента отвечает за бóльшую часть дисперсии системы, а все остальные компоненты – за очень малую дисперсию (график А на рис. 13.2). Напротив, если бы рынок криптовалют имел, скажем, три главенствующие подкатегории с независимыми движениями цен, то мы бы ожидали увидеть три больших собственных числа (график B на рис. 13.2).
Рис. 13.2 Симулированные графики крутого склона на многопеременных наборах данных (данные просимулированы, чтобы проиллюстрировать возможные результаты)
Подавление шума Большинство наборов данных содержат шум. Шум относится к вариациям в наборе данных, которые либо необъяснимы (например, случайные вариации), либо нежелательны (например, артефакты шума электрических линий в радиосигналах). Существует целый ряд способов ослабления или устранения шума, и оптимальная стратегия подавления шума зависит от характера и происхождения шума, а также от характеристик сигнала. Одним из методов подавления случайного шума является идентификация собственных чисел и собственных векторов системы, а также «редукция» направлений в пространстве данных, связанных с малыми собственными числами. При этом принято исходить из допущения, что случайный шум вносит относительно малый вклад в общую дисперсию. «Редуцирование» размер1
Англ. scree plot. – Прим. перев.
Отыскание собственных чисел 231
ности данных означает реконструкцию набора данных после приравнивания к нулю некоторых собственных чисел, которые ниже некоторого порога. В главе 15 вы увидите пример использования собственного разложения для подавления шума.
Уменьшение размерности (сжатие данных) Информационные коммуникационные технологии, такие как телефоны, интернет и телевидение, создают и передают огромный объем данных, таких как изображения и видео. Передача данных может занимать много времени и средств, и выгодно сжимать данные перед их передачей. Сжатие означает уменьшение размера данных (в байтах) при минимальном влиянии на качество данных. Например, файл изображения в формате TIFF может иметь размер 10 Мб, тогда как преобразованная в JPG версия может иметь размер 0.1 Мб, сохраняя при этом достаточно хорошее качество. Один из способов размерно уменьшить набор данных состоит в том, чтобы взять его собственное разложение, отбросить собственные числа и собственные векторы, связанные с малыми направлениями в пространстве данных, а затем передать только относительно большие пары собственный вектор/ число. На самом деле для сжатия данных чаще используется SVD (в главе 15 вы увидите пример), хотя принцип остается тем же. Современные алгоритмы сжатия данных на самом деле работают быстрее и эффективнее, чем ранее описанный метод, но идея остается той же: разложить набор данных на набор базисных векторов, которые охватывают наиболее важные признаки данных, а затем восстановить высококачественную версию изначальных данных.
Отыскание собственных чисел Для того чтобы выполнить собственное разложение квадратной матрицы, сначала нужно найти собственные числа, а затем использовать каждое собственное число для отыскания соответствующего собственного вектора. Собственные числа подобны ключам, которые вставляются в матрицу, чтобы получить доступ к мистическому собственному вектору. Отыскание собственных чисел матрицы на Python выполняется очень легко: matrix = np.array([ [1,2], [3,4] ]) # получить собственные числа evals = np.linalg.eig(matrix)[0]
232 Собственное разложение Два собственных числа (округленные до сотых) равны −0.37 и 5.37. Однако важный вопрос не в том, какая функция возвращает собственные числа, а в том, как собственные числа матрицы идентифицируются. Для того чтобы найти собственные числа матрицы, мы начинаем с уравнения собственного числа, показанного в уравнении 13.1, и выполняем несколько простых арифметических действий, как показано в уравнении 13.2. Уравнение 13.2. Реорганизованное уравнение собственного числа Av = λv;
Первое уравнение является точным повторением уравнения собственного числа. Во втором уравнении мы просто вычли правую часть, чтобы приравнять уравнение к вектору нулей. Переход от второго к третьему уравнению требует некоторого пояснения. В левой части второго уравнения есть два векторных члена, оба из которых содержат v. И поэтому мы выносим этот вектор за скобки. Но в результате мы остаемся с операцией вычитания матрицы и скаляра (A – λ), которая в линейной алгебре не определена1. Вместо этого мы сдвигаем матрицу на λ. Это подводит нас к третьему уравнению. (Попутное замечание: выражение λI иногда называется скалярной матрицей.) Что означает это третье уравнение? Оно означает, что собственный вектор находится в нуль-пространстве матрицы, сдвинутой на ее собственное число. Если это поможет вам понять концепцию собственного вектора как нульпространственного вектора сдвинутой матрицы, то можете подумать о добавлении двух дополнительных уравнений: Ã = A – λI; Ãv = 0.
Почему это утверждение столь проницательно? Вспомните, что в линейной алгебре тривиальные решения игнорируются, поэтому мы не считаем v = 0 собственным вектором. А это означает, что сдвинутая на собственное число матрица является сингулярной, потому что только сингулярные мат рицы имеют нетривиальное нуль-пространство. А что еще мы знаем о сингулярных матрицах? Мы знаем, что их определитель равен нулю. Следовательно: |A – λI| = 0.
Хотите верьте, хотите нет, но это ключ к отысканию собственных чисел: надо сдвинуть матрицу на неизвестное собственное число λ, приравнять его
Как я писал в главе 5, Python вернет результат, но это будет транслированное вычитание скаляра, которое не является линейно-алгебраической операцией.
Отыскание собственных чисел 233
определитель к нулю и найти λ. Давайте посмотрим, как это выглядит для матрицы 2×2:
(a – λ)(d – λ) – bc = 0;
λ2 – (a + d)λ + (ad – bc) = 0.
Для того чтобы найти решение для двух значений λ, можно применить квадратичную формулу. Но сам ответ не важен; важно увидеть логическую последовательность математических понятий, сформулированных ранее в этой книге: умножение матрицы на вектор действует как умножение вектора на скаляр (уравнение собственного числа); мы приравниваем уравнение собственного числа к вектору нулей и выносим за скобки общие члены; этим показывается, что собственный вектор находится в нульпространстве матрицы, сдвинутой на собственное число. Мы не считаем вектор нулей собственным вектором, а значит, сдвинутая матрица является сингулярной; поэтому мы приравниваем определитель сдвинутой матрицы к нулю и находим неизвестное собственное число. Приравненный к нулю определитель сдвинутой собственным числом мат рицы называется характеристическим многочленом матрицы. Обратите внимание, что в предыдущем примере мы начали с матрицы 2×2 и получили член λ2, и, значит, это полиномиальное уравнение второй степени. Возможно, из школьного курса алгебры вы помните, что многочлен n-й степени имеет n решений, некоторые из которых могут быть комплекс нозначными (это называется фундаментальной теоремой алгебры). И поэтому будет два удовлетворяющих уравнению числа λ. Совпадающие двойки не случайны: характеристический многочлен мат рицы M×M будет иметь член λM. По этой причине матрица M×M будет иметь M собственных чисел. Утомительные практические задачи На этом этапе в традиционном учебнике по линейной алгебре вам поручили бы найти вручную собственные числа десятков матриц 2×2 и 3×3. У меня по поводу такого рода упражнений смешанные чувства: с одной стороны, решение задач вручную действительно помогает усваивать механизм отыскания собственных чисел; но, с другой стороны, в этой книге я хочу сосредоточить внимание на концепциях, исходном коде и приложениях, не увязая в утомительной арифметике. Если вы чувствуете вдохновение решать задачи на собственные числа вручную, тогда дерзайте! Вы можете найти массу таких задач в традиционных учебниках или в интернете. Но я принял смелое
234 Собственное разложение (и, возможно, спорное) решение избегать в этой книге задач, решаемых вручную, и вместо этого использовать упражнения, посвященные программированию и глубокому пониманию.
Отыскание собственных векторов Как и в случае с собственными числами, отыскание собственных векторов на Python выполняется очень легко: evals,evecs = np.linalg.eig(matrix) print(evals), print(evecs) [-0.37228132 5.37228132] [[-0.82456484 -0.41597356] [ 0.56576746 -0.90937671]]
Собственные векторы находятся в столбцах матрицы evecs и в том же порядке, что и собственные числа (то есть собственный вектор в первом столбце матрицы evecs парно связан с первым собственным числом в векторе evals). Мне нравится использовать имена переменных evals и evecs, потому что они короткие и осмысленные. Вы также, возможно, заметите, что нередко используются имена переменных L и V или D и V. L – это Λ (заглавная буква λ), а V – это V, матрица, в которой каждый столбец i является собственным вектором vi. D означает диагональ, потому что собственные числа часто хранятся в диагональной матрице по причинам, которые я объясню позже в этой главе.
Собственные векторы в столбцах, а не в строках! Самое важное, что нужно помнить о собственных векторах при программировании, – это то, что они хранятся в столбцах матрицы, а не в строках! Такие размерно-индексационные ошибки легко возникают при работе с квадратными матрицами (потому что Python не будет реагировать сообщением об ошибке), но случайное использование строк вместо столбцов матрицы собственных векторов может иметь катастрофические последствия в приложениях. Если вы сомневаетесь, то вспомните изложение в главе 2 о том, что в линейной алгебре общепринято исходить из допущения, что векторы имеют ориентацию вдоль столбца.
Хорошо, но опять же, приведенный выше исходный код показывает, как получать собственные векторы матрицы из функции NumPy. Это можно узнать из документационного литерала docstring по функции np.linalg.eig. Важный вопрос: а откуда берутся собственные векторы и как их отыскивать? На самом деле я уже писал, как отыскивать собственные векторы: надо найти вектор v, который находится в нуль-пространстве матрицы, сдвинутой на λ. Другими словами: vi Î N(A – λiI).
Отыскание собственных векторов 235
Давайте посмотрим на числовой пример. Ниже приведены матрица и ее собственные числа:
Сосредоточимся на первом собственном числе. Для того чтобы выявить его собственный вектор, мы сдвигаем матрицу на 3 и находим вектор в его нуль-пространстве:
Это означает, что [1 1] является собственным вектором матрицы, ассоциированным с собственным числом 3. Я нашел этот нуль-пространственный вектор, просто взглянув на матрицу. Как выявляют нуль-пространственные векторы (то есть собственные векторы матрицы) на практике? Нуль-пространственные векторы можно найти методом Гаусса–Джордана, чтобы решить систему уравнений, где матрица коэффициентов – это λ-сдвинутая матрица, а вектор констант – вектор нулей. Это хороший способ концептуализировать решение. В реализации применяются более стабильные численные методы отыскания собственных чисел и собственных векторов, в том числе QR-разложение и процедура, именуемая степенным методом.
Неопределенность собственных векторов по знаку и шкале Давайте я вернусь к числовому примеру из предыдущего раздела. Я написал, что [1 1] – это собственный вектор матрицы, потому что этот вектор является базисом нуль-пространства матрицы, сдвинутой на ее собственное число 3. Посмотрите на сдвинутую матрицу и спросите себя, является ли [1 1] единственным возможным базисным вектором нуль-пространства? Даже близко ничего похожего! Можно также использовать [4 4] или [−5.4 −5.4], или… Я думаю, вы понимаете, к чему это идет: любая шкалированная версия вектора [1 1] является базисом этого нуль-пространства. Другими словами, если v – это собственный вектор матрицы, то таким же является и αv для любого действительно-значного α, кроме нуля. В этой связи следует отметить, что важность собственных векторов обу словливается их направлением, а не их модулем. Бесконечность возможных нуль-пространственных базисных векторов приводит к двум вопросам: существует ли один «самый лучший» базисный вектор? «Самого лучшего» базисного вектора как такового не существует, но удобно иметь
236 Собственное разложение единично-нормализованные собственные векторы (евклидова норма 1). Это особенно полезно для симметричных матриц по причинам, которые будут объяснены позже в этой главе1; каков «правильный» знак собственного вектора? Никакой. На самом деле при использовании разных версий NumPy, а также разных программ, таких как MATLAB, Julia или Mathematica, вы можете получать разные знаки собственных векторов из одной и той же матрицы. Неопределенность знака собственного вектора – это просто особенность жизни в нашей Вселенной. В таких применениях, как PCA, существуют принципиальные способы придания им знака, но это просто общепринятое соглашение, облегчающее интерпретацию.
Диагонализация квадратной матрицы Уравнение собственного числа, с которым вы теперь знакомы, содержит одно собственное число и один собственный вектор. Это означает, что матрица M×M имеет M уравнений собственных чисел: Av1 = λ1v1;
В этой серии уравнений нет ничего неправильного, но она довольно уродлива, а уродство нарушает один из принципов линейной алгебры: делать уравнения компактными и элегантными. Поэтому мы преобразовываем эту серию уравнений в одно матричное уравнение. Ключевым моментом при написании уравнения матричного собственного числа является то, что каждый столбец матрицы собственных векторов шкалируется ровно одним собственным числом. Это реализуется с помощью постпозиционного умножения на диагональную матрицу (как вы узнали из главы 6). Таким образом, вместо хранения собственных чисел в векторе мы храним собственные числа в диагонали матрицы. Следующее ниже уравнение показывает форму диагонализации для матрицы 3×3 (с использованием @ вместо числовых значений матрицы). В матрице собственных векторов первое подстрочное число соответствует собственному вектору, а второе подстрочное число – элементу собственного вектора. Например, v12 – это второй элемент первого собственного вектора: 1
Для того чтобы снять неопределенность, следует отметить, что за счет этого мат рица собственных векторов делается ортогональной матрицей.
Диагонализация квадратной матрицы 237
= Пожалуйста, найдите время, чтобы убедиться, что каждое собственное число шкалирует все элементы соответствующего собственного вектора, а не какие-либо другие собственные векторы. В более общем случае матричное уравнение собственных чисел – также именуемое диагонализацией квадратной матрицы – таково: AV = VΛ. Функция eig библиотеки NumPy возвращает собственные векторы в мат рице и собственные числа в векторе. Это означает, что для диагонализации матрицы в NumPy требуется немного дополнительного исходного кода: evals,evecs = np.linalg.eig(matrix) D = np.diag(evals)
Между прочим, в математике часто интересно и познавательно переставлять уравнения, находя решения для разных переменных. Рассмотрим следующий ниже список эквивалентных объявлений: AV = VΛ; A = VΛV–1; Λ = V–1AV. Второе уравнение показывает, что матрица A становится диагональной внутри пространства V (то есть V перемещает нас в «диагональное пространство», а затем V–1 возвращает нас назад из диагонального пространства). Это можно интерпретировать в контексте базисных векторов: матрица A является плотной в стандартном базисе, но затем мы применяем набор преобразований (V), чтобы повернуть матрицу в новый набор базисных векторов (собственных векторов), в которых информация разрежена и представлена диагональной матрицей. (В конце уравнения нам нужно вернуться назад в стандартное базисное пространство, отсюда и V–1.)
238 Собственное разложение
Особая удивительность симметричных матриц Из предыдущих глав вы уже знаете, что симметричные матрицы обладают особыми свойствами, благодаря которым с ними очень удобно работать. Теперь вы готовы познакомиться с еще двумя специальными свойствами, относящимися к собственному разложению.
Ортогональные собственные векторы Симметричные матрицы имеют ортогональные собственные векторы. Это означает, что все собственные векторы симметричной матрицы попарно ортогональны. Давайте я начну с примера, затем разберу последствия ортогональности собственных векторов и, наконец, покажу доказательство: # всего лишь случайная симметричная матрица A = np.random.randint(-3, 4, (3,3)) A = A.T@A # ее собственное разложение L,V = np.linalg.eig(A) # все попарные точечные произведения print( np.dot(V[:,0], V[:,1]) ) print( np.dot(V[:,0], V[:,2]) ) print( np.dot(V[:,1], V[:,2]) )
Все три точечных произведения равны нулю (в пределах вычислительных ошибок округления порядка 10−16. (Обратите внимание, что я создал симмет ричные матрицы как случайную матрицу, умноженную на ее транспонированную версию.) Свойство ортогональности собственного вектора означает, что точечное произведение между любой парой собственных векторов равно нулю, тогда как точечное произведение собственного вектора с самим собой не равно нулю (поскольку мы не считаем вектор нулей собственным вектором). Это можно записать как VTV = D, где D – это диагональная матрица с диагоналями, содержащими нормы собственных векторов. Но можно сделать еще лучше, чем просто диагональную матрицу: вспомните, что важность собственных векторов обусловлена их направлением, а не их модулем. И поэтому собственный вектор может иметь любой желаемый модуль (кроме, очевидно, нулевого модуля). Давайте прошкалируем все собственные векторы так, чтобы они имели единичную длину. Вопрос к вам: если все собственные векторы ортогональны и имеют единичную длину, что произойдет, если умножить матрицу собственных векторов на ее транспонированную версию? Вы, конечно же, знаете ответ: VTV = I.
Ортогональные собственные векторы 239
Другими словами, матрица собственных векторов симметричной матри цы является ортогональной матрицей! Это имеет целый ряд последствий для науки о данных, в том числе то, что собственные векторы очень легко инвертировать (потому что их надо просто транспонировать). Существуют и другие последствия использования ортогональных собственных векторов для таких применений, как анализ главных компонент, о которых я расскажу в главе 15. В главе 1 я писал, что в этой книге сравнительно мало доказательств. Но ортогональные собственные векторы симметричных матриц – это настолько важная концепция, что вам действительно нужно убедиться, что данное утверждение доказано. Цель этого доказательства – показать, что точечное произведение любой пары собственных векторов равно нулю. Мы исходим из двух допущений: 1) матрица A – это симметричная матрица и 2) λ1 и λ2 – это отдельные собственные числа матрицы A (отдельные в смысле, что они не могут быть равны друг другу) с v1 и v2 в качестве соответствующих им собственных векторов. Попробуйте проследить за каждым шагом равенства слева направо в уравнении 13.3. Уравнение 13.3. Доказательство ортогональности собственных векторов для симметричных матриц λ1v1Tv2 = (Av1)Tv2 = v1TATv2 = v1Tλ2v2 = λ2v1Tv2.
Члены в середине – это просто преобразования; обратите внимание на первый и последний члены. Они переписываются в уравнении 13.4, а затем вычитаются для обнуления. Уравнение 13.4. Продолжение доказательства ортогональности собственных векторов… λ1v1Tv2 = λ2v1Tv2;
λ1v1Tv2 = λ2v1Tv2 = 0.
Оба члена содержат точечное произведение v1Tv2, которое можно исключить. Это подводит нас к заключительной части доказательства, показанной в уравнении 13.5. Уравнение 13.5. Доказательство ортогональности собственных векторов, часть 3 (λ1 – λ2)v1Tv2 = 0.
Приведенное выше последнее уравнение говорит о том, что два числа умножаются, чтобы получить 0, а это означает, что одно или оба этих числа должны быть равны нулю. Разность λ1 – λ2 не может быть равна нулю, поскольку мы исходили из допущения, что они различаются. Следовательно,
240 Собственное разложение v1Tv2 должно быть равно нулю, а это означает, что два собственных вектора ортогональны. Вернитесь к уравнениям, чтобы убедиться, что это доказательство не работает для несимметричных матриц, когда AT ¹ A. Таким образом, собственные векторы несимметричной матрицы не ограничены быть ортогональными (они будут линейно независимыми для всех различающихся собственных чисел, но я опущу это обсуждение и доказательство).
Действительно-значные собственные числа Второе особое свойство симметричных матриц состоит в том, что они имеют действительно-значные собственные числа (и, следовательно, действительно-значные собственные векторы). Давайте я начну с демонстрации того, что матрицы – даже со всеми действительно-значными элементами – могут иметь комплексно-значные собственные числа: A = np.array([[-3, -3, 0], [ 3, -2, 3], [ 0, 1, 2]]) # ее собственное разложение L,V = np.linalg.eig(A) L.reshape(-1,1) # напечатать в виде вектора-столбца >> array([[-2.744739 +2.85172624j], [-2.744739 -2.85172624j], [ 2.489478 +0.j ]])
(Будьте осторожны с интерпретацией этого массива NumPy; это не матри ца 3×2; это вектор-столбец 3×1, содержащий комплексные числа. Обратите внимание на j и отсутствие запятой между числами.) 3×3-матрица A имеет два комплексно-значных собственных числа и одно действительно-значное собственное число. Сопряженные с комплекснозначными собственными числами собственные векторы сами будут комп лексно-значными. В этой конкретной матрице нет ничего особенного; я буквально сгенерировал ее из случайных целых чисел от −3 до +3. Интересно, что комплексно-значные решения выводятся сопряженными парами. Это означает, что если существует λi = a + ib, то существует и λk = a – ib. Соответствующие им собственные векторы также являются комплексными сопряженными парами. Не буду вдаваться в подробности комплексно-значных решений, кроме как покажу, что комплексные решения задачи собственного разложения элементарны1. Симметричные матрицы гарантированно имеют действительно-значные собственные числа, следовательно, также действительно-значные собствен1
Под «элементарными» я подразумеваю математически ожидаемые; интерпретация сложных решений в собственном разложении далеко не элементарна.
Собственное разложение сингулярных матриц 241
ные векторы. Давайте я начну с того, что видоизменю приведенный выше пример, чтобы сделать матрицу симметричной: A = np.array([[-3, -3, 0], [-3, -2, 1], [ 0, 1, 2]]) # ее собственное разложение L,V = np.linalg.eig(A) L.reshape(-1,1) # напечатать в виде вектора-столбца >> array([[-5.59707146], [ 0.22606174], [ 2.37100972]])
Это только один конкретный пример; может, нам тут повезло? Рекомендую сделать паузу и обследовать этот вопрос самостоятельно в онлайновом исходном коде; вы можете создать случайную симметричную матрицу (путем создания случайной матрицы и собственного разложения ATA) любого размера, чтобы подтвердить, что собственные числа являются действительно-значными. Получение гарантированных действительно-значных собственных чисел из симметричных матриц – это настоящая удача, потому что с комплексными числами зачастую сложно работать. Многие используемые в науке о данных матрицы являются симметричными, поэтому если в своих приложениях в этой области вы видите комплексные собственные числа, то, возможно, проблема связана с исходным кодом или данными. Эффективное применение симметрии Если вы знаете, что работаете с симметричной матрицей, то вместо функции np.linalg. eig можете использовать функцию np.linalg.eigh (или вместо функции SciPy eig – функцию eigh). Символ h означает «эрмитову матрицу»1, то есть комплексную версию симметричной матрицы. Функция eigh в целом работает быстрее и является более численно стабильной, чем функция eig, но оперирует только симметричными матрицами.
Собственное разложение сингулярных матриц Я вставил этот раздел сюда, потому что обнаружил, что у студентов часто возникает идея, что сингулярные матрицы невозможно разложить процедурой собственного разложения или что собственные векторы сингулярной матрицы должны быть какими-то необычными. Эта идея совершенно неверна. Применять собственное разложение к сингулярным матрицам – это совершенно нормально. Вот краткий пример: # сингулярная матрица A = np.array([[1,4,7], 1
Англ. Hermitian. – Прим. перев.
242 Собственное разложение [2,5,8], [3,6,9]]) # ее собственное разложение L,V = np.linalg.eig(A)
Распечатка ранга, собственных чисел и собственных векторов этой мат рицы приведена ниже: print( f’Ранг = \n’ ) print(‘Собственные числа: ‘), print(L.round(2)), print(‘ ‘) print(‘Собственные векторы:’), print(V.round(2)) >> Ранг = 2 Собственные числа: [16.12 -1.12 -0. ] Собственные векторы: [[-0.46 -0.88 0.41] [-0.57 -0.24 -0.82] [-0.68 0.4 0.41]]
Эта матрица ранга 2 имеет одно нуль-значное собственное число с ненулевым собственным вектором. Вы можете использовать онлайновый исходный код, чтобы обследовать собственное разложение других рангово-пониженных случайных матриц. Существует одно особое свойство собственного разложения сингулярных матриц, при котором по крайней мере одно собственное число гарантированно равно нулю. Это не означает, что количество ненулевых собственных чисел равно рангу матрицы – это верно для сингулярных чисел (скаляров из SVD), но не для собственных чисел. Но если матрица – сингулярная, то хотя бы одно собственное число равно нулю. Верно и обратное: каждая полноранговая матриц а имеет ноль нульзначных собственных чисел. Одно из объяснений причины, по которой это происходит, заключается в том, что сингулярная матрица уже имеет нетривиальное нуль-пространство, а это означает, что λ = 0 обеспечивает нетривиальное решение уравнения (A – λI)v = 0. Это видно в приведенном выше примере матрицы: ассоциированный с λ = 0 собственный вектор – это нормализованный вектор [1 −2 1], то есть линейно-взвешенная комбинация столбцов (или строк), которая дает вектор нулей. Основные выводы, которые следует вынести из этого раздела, таковы: 1) собственное разложение допустимо для рангово-пониженных мат риц и 2) наличие по меньшей мере одного нуль-значного собственного числа указывает на рангово-пониженную матрицу.
Квадратичная форма, определенность и собственные числа 243
Квадратичная форма, определенность и собственные числа Следует признать, что термины квадратичная форма и определенность выглядят пугающе. Но не беспокойтесь – оба этих термина просты и открывают доступ к продвинутой линейной алгебре и таким ее применениям, как анализ главных компонент и симуляции методом Монте-Карло. И что еще лучше: интеграция исходного кода Python в ваше обучение даст вам огромное преимущество в изучении этих понятий по сравнению с традиционными учебниками по линейной алгебре.
Квадратичная форма матрицы Рассмотрим следующее ниже выражение: vTAw = α.
Другими словами, мы пред- и постпозиционно умножаем квадратную мат рицу на один и тот же вектор w и получаем скаляр. (Обратите внимание, что это умножение допустимо только для квадратных матриц.) Это называется квадратичной формой матрицы A. Какую матрицу и какой вектор мы используем? Идея квадратичной формы заключается в использовании одной конкретной матрицы и набора всех возможных векторов (соответствующего размера). Важный вопрос касается знаков α всех возможных векторов. Давайте посмотрим на пример:
Для этой конкретной матрицы не существует возможной комбинации x и y, которая может давать отрицательный ответ, потому что квадраты членов (2×2 и 3y2) всегда будут перевешивать перекрестный член (4xy), даже если x или y – отрицательные. Кроме того, α может быть неположительным только при x = y = 0. Это нетривиальный результат квадратичной формы. Например, следующая ниже матрица может иметь положительное или отрицательное α в зависимости от значений x и y:
Вы можете подтвердить, что задание [x y] равным [−1 1] дает отрицательный результат квадратичной формы, а [−1 −1] дает положительный результат.
244 Собственное разложение Как вообще можно узнать, какой скаляр (положительный, отрицательный либо нулевой) будет произведен квадратичной формой для всех возможных векторов? Ключ происходит из учитывания того, что полноранговая матрица собственных векторов охватывает все ℝM, и, следовательно, каждый вектор из ℝM может быть выражен как некоторая взвешенная линейная комбинация собственных векторов1. Затем мы начинаем с уравнения собственного числа и умножаем его слева на собственный вектор, чтобы вернуться к квадратичной форме: Av = λv;
Последнее уравнение является ключевым. Обратите внимание, что ||vTv||2 – строго положительный (модули векторов не могут быть отрицательными, и мы игнорируем вектор нулей), а это означает, что знак правой части уравнения полностью определяется собственным числом λ. В этом уравнении используется только одно собственное число и его собственный вектор, но нам нужно знать о любом возможном векторе. Ключевой для понимания момент состоит в том, чтобы учесть, что если уравнение допустимо для каждой пары «собственный вектор – собственное число», то оно допустимо для любой комбинации пар «собственный вектор – собственное число». Например: v1TAv1 = λ1||v1||2; v2TAv2 = λ2||v2||2;
(v1 + v2)TA(v1 + v2) = (λ1 + λ2)||v1 + v2||2; uTAu = ζ||u||2.
Другими словами, мы можем задать любой вектор u как некоторую линейную комбинацию собственных векторов и некоторый скаляр ζ как ту же самую линейную комбинацию собственных чисел. Во всяком случае, это не меняет того принципа, что знак правой части – а значит, и знак квадратичной формы – определяется знаком собственных чисел. Теперь давайте подумаем об этих уравнениях в условиях разных допущений о знаках собственных чисел λ. Все собственные числа положительны Правая часть уравнения – всегда положительная, а это означает, что vTAv всегда положителен для любого вектора v. Собственные числа положительны либо равны нулю vTAv неотрицателен и будет равен нулю при λ = 0 (что происходит, когда матрица является сингулярной). 1
Ради краткости я здесь опускаю некоторые тонкости относительно редких случаев, когда матрица собственных векторов не охватывает всемерное подпространство.
Квадратичная форма, определенность и собственные числа 245
Собственные числа отрицательны либо равны нулю Результат квадратичной формы будет нулевым или отрицательным. Собственные числа – отрицательные Результат квадратичной формы будет отрицательным для всех векторов.
Определенность Определенность – это характеристика квадратной матрицы и определяется знаками собственных чисел матрицы, которые равносильны знакам результатов квадратичной формы. Определенность влияет на обратимость матри цы, а также на продвинутые методы анализа данных, такие как обобщенное собственное разложение (используемое в многопеременных линейных классификаторах и обработке сигналов). Как показано в табл. 13.1, существует пять категорий определенности; знаки + и − указывают знаки собственных чисел. Таблица 13.1. Категории определенности Категория Положительно определенная Положительно полуопределенная Неопределенная Отрицательно полуопределенная Отрицательно определенная
Квадратичная форма Положительная Неотрицательная Положительная и отрицательная Неположительная Отрицательная
Собственные числа + + и 0 + и −
«Зависит» в таблице означает, что матрица может быть обратимой или сингулярной в зависимости от чисел матрицы, а не от категории определенности.
ATA является положительной (полу)определенной Любая матрица, которая может быть выражена как произведение матрицы и ее транспонированной версии (то есть S = ATA), гарантированно будет положительно определенной либо положительно полуопределенной. Комбинация этих двух категорий часто записывается как «положительно (полу) определенная». Все матрицы ковариаций данных являются положительно (полу)определенными, поскольку они определяются как произведение матрицы данных на ее транспонированую версию. Это означает, что все матрицы ковариаций имеют неотрицательные собственные числа. Все собственные числа будут положительными, когда матрица данных является полноранговой (имеет полный столбцовый ранг, если данные хранятся в виде наблюдений по при-
246 Собственное разложение знакам), и будет иметься по меньшей мере одно нуль-значное собственное число, если матрица данных является рангово-пониженной. Доказательство того, что S положительно (полу)определена, основано на выписывании его квадратичной формы и применении нескольких алгебраи ческих манипуляций. (Обратите внимание, что для перехода от первого уравнения ко второму требуется просто переставить скобки; в линейной алгебре такое «доказательство с помощью скобок» является общепринятым.) wTSw = wT(ATA)w = (wTAT)Aw = (Aw)T(Aw) = ||Aw||2. Дело в том, что квадратичная форма ATA равна квадрату модуля матри цы, умноженной на вектор. Модули не могут быть отрицательными и могут быть равны нулю только тогда, когда вектор равен нулю. А если Aw = 0 для нетривиального w, то A является сингулярной. Следует иметь в виду, что хотя все матрицы ATA симметричны, не все симметричные матрицы могут быть выражены как ATA. Другими словами, матричная симметрия сама по себе не гарантирует положительной (полу) определенности, потому что не все симметричные матрицы могут быть выражены как произведение матрицы и ее транспонированной версии.
Кого вообще волнуют квадратичная форма и определенность? Положительная определенность востребована в науке о данных, потому что некоторые линейно-алгебраические операции применимы только к таким «хорошо обеспеченным» матрицам, включая разложение Холецкого, используемое для создания коррелированных наборов данных в симуляциях Монте-Карло. Положительно определенные матрицы также важны для задач оптимизации (например, градиентного спуска), потому что можно гарантированно находить минимум. В своем бесконечном стремлении улучшить свое мастерство в науке о данных вы можете столкнуться с техническими документами, в которых используется аббревиатура SPD1: симметричная положительно определенная.
Обобщенное собственное разложение Обратите внимание, что следующее ниже уравнение совпадает с фундаментальным уравнением собственного числа: Av = λIv. 1
Англ. Symmetric Positive Definite. – Прим. перев.
Обобщенное собственное разложение 247
Это очевидно, поскольку Iv = v. Обобщенное собственное разложение предусматривает замену единичной матрицы еще одной матрицей (не единичной либо матрицей нулей): Av = λBv.
Обобщенное собственное разложение также называется одновременной диагонализацией двух матриц. Результирующая пара (λ, v) не является собственным числом/вектором только матрицы A или только матрицы B. Вместо этого две матрицы имеют общие пары «собственное число/вектор». В концептуальном плане обобщенное собственное разложение может трактоваться как «обычное» собственное разложение матрицы произведения: C = AB–1; Cv = λv.
Это только концептуально; на практике же обобщенное собственное разложение не требует от матрицы B быть обратимой. Здесь не тот случай, когда любые две матрицы могут быть диагонализированы одновременно. Но эта диагонализация возможна, если B положительно (полу)определена. В библиотеке NumPy обобщенное собственное разложение не вычисляется, но в SciPy такая возможность есть. Если вы знаете, что две матрицы симметричны, то можете применить функцию eigh, которая является численно более стабильной: # A A B B
создать коррелированные матрицы = np.random.randn(4,4) = [email protected] = np.random.randn(4,4) = [email protected] + A/10
# обобщенное собственное разложение from scipy.linalg import eigh evals,evecs = eigh(A,B)
Помните о порядке входимых аргументов: второй аргумент является концептуально инвертированным. В науке о данных обобщенное собственное разложение используется в анализе классификации. В частности, линейный дискриминантный анализ Фишера основан на обобщенном собственном разложении двух матриц ковариации данных. В главе 15 вы увидите соответствующий пример.
Несметное число тонкостей собственного разложения О свойствах собственного разложения можно было бы сказать гораздо больше. Вот несколько примеров: сумма собственных чисел равна следу матрицы, а произведение собственных чисел равно определителю; не все квадратные матрицы можно диагонализовать; некоторые матрицы имеют повторяющиеся собственные числа, что влияет на их
248 Собственное разложение собственные векторы; комплексные собственные числа действительно-значных матриц находятся внутри круга на комплексной плоскости. Математические познания в области собственных чисел имеют глубокие корни, но эта глава дает все базовые знания, необходимые для работы с собственным разложением в приложениях.
Резюме Вот это была глава так глава! Напомню ее ключевые моменты. Собственное разложение идентифицирует M пар скаляр/вектор матри цы M×M. Эти пары «собственное число / собственный вектор» отражают особые направления в матрице и имеют громадное число применений в науке о данных (обычно это анализ главных компонент), а также в геометрии, физике, вычислительной биологии и массе других технических дисциплин. Собственные числа отыскиваются путем принятия допущения о том, что матрица, сдвинутая на неизвестный скаляр λ, является сингулярной, посредством приравнивания ее определителя к нулю (именуемого характеристическим многочленом) и вычисления чисел λ. Собственные векторы отыскиваются путем отыскания базисного вектора для нуль-пространства λ-сдвинутой матрицы. Диагонализация матрицы означает представление матрицы в виде VΛV –1, где V – это матрица с собственными векторами в столбцах, а Λ – диагональная матрица с собственными числами в диагональных элементах. Симметричные матрицы обладают рядом особых свойств в собственном разложении; наиболее важным для науки о данных является то, что все собственные векторы попарно ортогональны. Это означает, что матрица собственных векторов является ортогональной матрицей (когда собственные векторы единично-нормализованы), что, в свою очередь, означает, что обратная матрица матрицы собственных векторов является ее транспонированной версией. Определенность матрицы относится к знакам ее собственных чисел. В науке о данных наиболее релевантными категориями являются положительно (полу)определенные, что означает, что все собственные числа либо неотрицательны, либо положительны. Матрица, умноженная на ее транспонированную версию, всегда положительно (полу)определена, что означает, что все матрицы ковариа ций имеют неотрицательные собственные числа. Учение о собственном разложении носит насыщенный и подробный характер, и в этой области исследований было открыто много захватывающих тонкостей, частных случаев и применений. Надеюсь, что обзор этой темы в данной главе обеспечит прочную основу для ваших потребностей как исследователя данных и, возможно, вдохновит вас узнатьеще больше о фантастической красоте собственного разложения.
Упражнения по программированию 249
Упражнения по программированию Упражнение 13.1 Интересно, что собственные векторы A–1 сопадают с собственными векторами A, а собственные числа равны λ–1. Докажите, что это так, выписав собственное разложение A и A–1. Затем проиллюстрируйте это, используя случайную полноранговую симметричную матрицу 5×5. Упражнение 13.2 Воссоздайте левую панель рис. 13.1, но вместо столбцов используйте строки матрицы V. Конечно же, вы знаете, что это ошибка программирования, но результаты будут поучительными: тест на геометрию не будет пройден, поскольку матрица, умноженная на ее собственный вектор, только растягивается. Упражнение 13.3 Цель этого упражнения – продемонстрировать, что собственные числа неразрывно сопряжены со своими собственными векторами. Диагонализируйте симметричную матрицу случайных целых чисел1, созданную с помощью аддитивного метода (см. упражнение 5.9), но переупорядочите собственные числа в случайном порядке (назовем эту матрицу Λ ) без переупорядочивания собственных векторов. Сначала продемонстрируйте, что вы можете реконструировать изначальную матрицу как VΛV–1. Точность реконструкции можно вычислить как расстояние Фробениуса между изначальной и реконструированной матрица ми. Далее, попытайтесь реконструировать матрицу, используя Λ. Насколько близка реконструированная матрица к изначальной? Что произойдет, если вместо случайного переупорядочивания взаимно поменять местами только два самых больших собственных числа? Как насчет двух наименьших собственных чисел? Наконец, создайте гистограмму, показывающую расстояния Фробениуса до изначальной матрицы для разных вариантов взаимообмена (рис. 13.3). (Разумеется, из-за случайных матриц – и, следовательно, случайных собственных чисел – ваш график не будет выглядеть точно так же, как мой.) Упражнение 13.4 Одно интересное свойство случайных матриц состоит в том, что их комплексно-значные собственные числа распределены по кругу с радиусом, пропорциональным размеру матрицы. В целях демонстрации этого свойства вычислите 123 случайные матрицы 42×42, извлеките их собственные числа, разделите на квадратный корень из размера матрицы (42) и нанесите собственные числа на комплексную плоскость, как показано на рис. 13.4.
В упражнениях я очень часто использую симметричные матрицы; это связано с тем, что они имеют действительно-значные собственные числа, но это не меняет ни принципа, ни математики, а просто облегчает визуальный осмотр решений.
250 Собственное разложение
Расстояние Фробениуса до изначальной матрицы
Два самых больших
Два самых малых
Тип взаимообмена собственных чисел
Рис. 13.3 Результаты упражнения 13.3
Рис. 13.4 Результаты упражнения 13.4
Упражнение 13.5 Это упражнение поможет вам получше разобраться в том, что собственный вектор является базисом нуль-пространства сдвинутой собственным числом матрицы, а также выявит риски числовых ошибок прецизионности. Примените процедуру собственного разложения к случайной симметричной мат
Упражнения по программированию 251
рице 3×3. Затем по каждому собственному числу примените функцию scipy. linalg.null_space(), чтобы найти базисный вектор нуль-пространства каждой сдвинутой матрицы. Совпадают ли эти векторы с собственными векторами? Обратите внимание, что вам, возможно, понадобится принять во внимание нормы и знаковые неопределенности собственных векторов. При выполнении исходного кода несколько раз на разных случайных мат рицах вы, скорее всего, будете получать ошибки Python. Ошибка возникает из-за пустого нуль-пространства λ-сдвинутой матрицы. Указанная ошибка при обследовании возникает из-за того, что сдвинутая матрица имеет полный ранг. (Не верьте мне на слово; подтвердите это сами!) Этого не должно происходить, что еще раз подчеркивает, что 1) конечно-прецизионная математика на компьютерах не всегда соответствует математике на «классной доске» и 2) вам следует использовать конкретно ориентированные и более численно стабильные функции, вместо того чтобы пытаться делать прямое переложение формул в исходный код. Упражнение 13.6 В данном упражнении я собираюсь научить вас третьему методу создания случайных симметричных матриц1. Начните с создания диагональной мат рицы 4×4, по диагонали которой расположены положительные числа (например, это могут быть числа 1, 2, 3, 4). Затем создайте 4×4-матрицу Q из QRразложения матрицы случайных чисел. Используйте эти матрицы в качестве собственных чисел и собственных векторов и умножьте их соответствующим образом, чтобы собрать матрицу. Убедитесь, что собранная матрица симметрична и что ее собственные числа равны собственным числам, которые указали вы. Упражнение 13.7 Вернемся к упражнению 12.4. Повторите это упражнение, но вместо квад рата фробениусовой нормы расчетной матрицы используйте среднее значение собственных чисел (это называется усадочной регуляризацией). Как полученное число соотносится с числом из главы 12? Упражнение 13.8 Это и следующее упражнение тесно связаны между собой. Мы создадим суррогатные данные с заданной матрицей корреляций (в этом упражнении), а затем удалим корреляцию (в следующем упражнении). Формула для создания данных с заданной структурой корреляции такова: Y = VΛ1/2X, где V и Λ – это собственные векторы и собственные числа матрицы корреляций, а X – N×T-матрица некоррелированных случайных чисел (N каналов и T временных точек). 1
Первые два были мультипликативным и аддитивным методами.
252 Собственное разложение Примените эту формулу, чтобы создать 3×10 000-матрицу Y данных со следующей ниже структурой корреляции:
Затем вычислите эмпирическую корреляционную матрицу матрицы X. Она не будет точно равна R, потому что мы отбираем образцы в случайном порядке из конечного набора данных. Но она должна быть достаточно близкой (например, в пределах 0.01). Упражнение 13.9 Теперь давайте удалим эти навязанные корреляции с помощью отбеливания. Термин отбеливание используется в обработке сигналов и изображений и обозначает устранение корреляций. Многопеременный временной ряд можно отбелить, реализовав следующую ниже формулу: = YTVΛ–1/2. Y Примените эту формулу к матрице данных из предыдущего упражнения и убедитесь, что матрица корреляций является единичной матрицей (опять же, с некоторым допуском на случайный отбор данных). Упражнение 13.10 В обобщенном собственном разложении собственные векторы не ортогональны, даже когда обе матрицы симметричны. Подтвердите на Python, что V–1 ¹ VT. Это происходит потому, что хотя и A, и B симметричны, C = AB не симметрична1. Однако собственные векторы ортогональны относительно B, и, стало быть, VTBV = I. Подтвердите эти свойства, выполнив обобщенное собственное разложение двух симметричных матриц и получив рис. 13.5. Упражнение 13.11 Давайте обследуем шкалирование собственных векторов. Начните с создания 4×4-матрицы случайных целых чисел, извлеченных в интервале между −14 и +14. Диагонализируйте матрицу и эмпирически подтвердите, что A = VΛV–1. Подтвердите, что евклидова норма каждого собственного вектора равна 1. Обратите внимание, что квадрат комплексного числа вычисляется как это число, умноженное на его комплексно-сопряженное число (подсказка: используйте функцию np.conj()). Затем умножьте матрицу собственных векторов на любой ненулевой скаляр. Я использовал π без особой веской причины, кроме того что его было интересно набирать на клавиатуре. Влияет ли этот скаляр на точность ре1
Причина, по которой произведение двух симметричных матриц не является симметричным, совпадает с причиной, по которой матрица R из QR-разложения имеет нули на нижней диагонали.
Упражнения по программированию 253
конструированной матрицы и/или норм собственных векторов? Почему да или почему нет? Наконец, повторите эту процедуру, но используя симметричную матрицу, и замените V–1на VT. Изменят ли эти изменения умозаключение?
Рис. 13.5 Результаты упражнения 13.10
14 Сингулярное разложение
Предыдущая глава была по-настоящему насыщенной! Я изо всех сил старался сделать ее понятной и строгой, не слишком увязая в деталях, которые имеют меньшую релевантность для науки о данных. К счастью, бóльшая часть того, что вы узнали о собственном разложении, применима и к сингулярному разложению (SVD)1. Это означает, что данная глава будет проще и короче. Сингулярное разложение предназначено для разложения матрицы на произведение трех матриц, именуемых левыми сингулярными векторами (U), сингулярными числами (Σ) и правыми сингулярными векторами (V): A = UΣVT. Это разложение, должно быть, выглядит похоже на собственное разложение. На самом деле сингулярное разложение можно трактовать как обобщение собственного разложения на неквадратные матрицы – либо трактовать собственное разложение как частный случай собственного разложения для квадратных матриц2. Сингулярные числа сравнимы с собственными числами, а матрицы сингулярных векторов сравнимы с собственными векторами (эти два набора величин совпадают при некоторых обстоятельствах, которые я объясню позже).
Общая картина сингулярного разложения Сначала я познакомлю вас с идеей и интерпретацией матриц, а позже в этой главе объясню, как вычислять сингулярное разложение. 1 2
Англ. Singular Value Decomposition. – Прим. перев. Сингулярное разложение – это не то же самое, что собственное разложение для всех квадратных матриц; подробнее об этом позже.
Общая картина сингулярного разложения 255
На рис. 14.1 показан общий вид сингулярного разложения.
Рис. 14.1 Общая картина сингулярного разложения
На этой диаграмме видны многие важные признаки сингулярного разложения; эти признаки будут рассмотрены подробнее в данной главе, но если их сгруппировать, то получится вот такой список: и U, и V – это квадратные матрицы, даже если A не является квад ратной; матрицы сингулярных векторов U и V ортогональны, а значит, UTU = I и VTV = I. Напомним, что это означает, что каждый столбец ортогонален другому столбцу и любое подмножество столбцов ортогонально любому другому (непересекающемуся) подмножеству столбцов; первые r столбцов матрицы U обеспечивают ортогональные базисные векторы для столбцового пространства матрицы A, тогда как остальные столбцы предоставляют ортогональные базисные векторы для левого нуль-пространства (если только r не равно M, в коем случае матрица имеет полный столбцовый ранг, а левое нуль-пространство является пустым); первые r строк матрицы VT (которые являются столбцами матрицы V) предоставляют ортогональные базисные векторы для строчного пространства, тогда как остальные строки предоставляют ортогональные базисные векторы для нуль-пространства; матрица сингулярных чисел – это диагональная матрица того же размера, что и A. Сингулярные числа всегда сортируются от наибольшего (вверху слева) к наименьшему (внизу справа); все сингулярные числа неотрицательны и действительно-значные. Они не могут быть ни комплексными, ни отрицательными, даже если матрица содержит комплексно-значные числа; количество ненулевых сингулярных чисел равно рангу матрицы. Возможно, самое удивительное в сингулярном разложении то, что оно показывает все четыре подпространства матр иц ы: столбцовое пространство и левое пустое пространство охватываются первыми r и последними от M – r до M столбцами матрицы U, тогда как строчное пространство и пус тое пространство охватываются первыми r и последними N – r до N строками матр иц ы V T. В случае прямоугольной матр иц ы если r = M, то левое
256 Сингулярное разложение нуль-пространство является пустым, а если r = N, то нуль-пространство является пустым.
Сингулярные числа и ранг матрицы Ранг матрицы определяется как количество ненулевых сингулярных чисел. Причина пристекает из предыдущего изложения, что столцовое пространство и строчное пространство матрицы определяются как левый и правый сингулярные векторы, которые шкалируются соответствующими им сингулярными числами, чтобы иметь некий «объем» в матричном пространстве, тогда как левое и правое нуль-пространства определяются как левый и правый сингулярные векторы, шкалированные в нули. Таким образом, размерность столбцового и строчного пространств определяется количеством ненулевых сингулярных чисел. Собственно говоря, можно заглянуть в функцию NumPy np.linalg.matrix_ rank, чтобы увидеть, как Python вычисляет ранг матрицы (я немного отредактировал исходный код, чтобы сосредоточиться на ключевых понятиях): S = svd(M, compute_uv=False) # вернуть только сингулярные числа tol = S.max() * max(M.shape[-2:]) * finfo(S.dtype).eps return count_nonzero(S > tol)
Возвращаемое значение – это количество сингулярных чисел, которые превышают пороговое значение tol. Что такое tol? Это уровень допуска, учитывающий возможные ошибки округления. Он определяется как машинная прецизионность для этого типа данных (eps), шкалированная по наибольшему сингулярному числу и размеру матрицы. Таким образом, мы еще раз видим разницу между «математикой на классной доске» и прецизионной математикой, реализованной на компьютерах: ранг матрицы на самом деле вычисляется не как количество ненулевых сингулярных чисел, а как количество сингулярных чисел, которые больше некоторого малого числа. Существует риск того, что малые, но по-настоящему ненулевые сингулярные числа будут проигнорированы, но это перевешивает риск неправильного завышения ранга матрицы, когда по-настоящему нуль-значные сингулярные числа оказываются ненулевыми из-за ошибок прецизионности.
Сингулярное разложение на Python На языке Python сингулярное разложение вычисляется довольно просто: U, s, Vt = np.linalg.svd(A)
Следует помнить о двух особенностях функции svd в библиотеке NumPy. Во-первых, сингулярные числа возвращаются в виде вектора, а не в виде мат
Сингулярное разложение и одноранговые «слои» матрицы 257
рицы того же размера, что и A. Это означает, что вам нужен дополнительный исходный код получения матрицы Σ: S = np.zeros(np.shape(A)) np.fill_diagonal(S, s)
Сначала вы, возможно, подумаете об использовании np.diag(s), но она создает правильную матрицу сингулярных чисел только для квадратной мат рицы A. Поэтому я сначала создаю матрицу нулей правильного размера, а затем заполняю диагональ сингулярными числами. Вторая особенность заключается в том, что NumPy возвращает матри цу VT, а не V. Возможно, это будет дезориентировать читателей, знакомых с MATLAB, потому что функция MATLAB svd возвращает матрицу V. Подсказка находится в документационном литерале, описывающем матрицу vh, где буква h обозначает эрмитов, то есть имя симметричной комплексно-значной матрицы. На рис. 14.2 показаны результаты на выходе из функции svd (в которых сингулярные числа конвертированы в матрицу).
U (левые сингулярные векторы)
V (правые сингулярные векторы)
Рис. 14.2 Общая картина сингулярного разложения образцовой матрицы
Сингулярное разложение и одноранговые «слои» матрицы Первое уравнение, которое я показал в предыдущей главе, было векторноскалярной версией уравнения собственного числа (Av = λv). Я открыл эту главу с матричного уравнения сингулярного разложения (A = UΣVT); как это уравнение выглядит для одного вектора? Его можно написать двумя разными способами, которые подчеркивают разные признаки сингулярного разложения: Av = uσ; uTA = σvT.
258 Сингулярное разложение Эти уравнения чем-то похожи на уравнение собственного числа, за исключением того, что вместо одного вектора используются два. И следовательно, интерпретации немного более тонкие: в общем случае эти уравнения говорят о том, что влияние матрицы на один вектор такое же, как влияние скаляра на другой вектор. Обратите внимание, что первое уравнение означает, что u находится в столбцовом пространстве матрицы A, а v обеспечивает веса для комбинирования столбцов. То же самое относится и ко второму уравнению, но v находится в строчном пространстве матрицы A, а u обеспечивает веса. Но это не то, на чем я хотел бы сосредоточиться в данном разделе; я хочу рассмотреть, что происходит при умножении одного левого сингулярного вектора на один правый сингулярный вектор. Поскольку сингулярные векторы парно соединены с одним и тем же сингулярным числом, нам нужно умножить i-й левый сингулярный вектор на i-е сингулярное число на i-й правый сингулярный вектор. Обратите внимание на ориентацию в этом умножении вектора на вектор: столбец слева, строка справа (рис. 14.3). Это означает, что результатом будет внешнее произведение того же размера, что и изначальная матрица. Кроме того, это внешнее произведение представляет собой одноранговую матрицу, норма которой определяется сингулярным числом (поскольку сингулярные векторы имеют единичную длину): u1σ1v1T = A1.
Рис. 14.3 Внешнее произведение сингулярных векторов создает матричный «слой»
Подстрочная 1 в уравнении указывает на использование первых сингулярных векторов и первого (наибольшего) сингулярного числа. Я обозначил результат как A1, потому что это не изначальная матрица A, а одноранговая мат рица того же размера, что и A. И не просто любая одноранговая матрица – это самый важный «слой» матрицы. Он является самым важным, потому что имеет наибольшее сингулярное число (подробнее об этом – в следующем разделе). С учетом этого изначальная матрица реконструируется путем суммирования всех «слоев» сингулярного разложения, ассоциированных с σ > 01:
Суммировать нуль-значные сингулярные числа смысла нет, потому что это просто сложение матриц нулей.
Сингулярное разложение из собственного разложения 259
Суть этого суммирования в том, что не обязательно использовать все r слоев; вместо этого можно реконструировать какую-то другую матрицу, назовем ее Ã, содержащую первые k > 30.4 Результаты Знак >>, который вы видите в блоках исходного кода, является результатом исполнения ячейки исходного кода. Последующий текст – это то, что вы видите на экране, когда вычисляете исходный код в ячейке.
Теперь попробуйте следующее: var1 + var3
А-а-а, вы только что получили свою первую ошибку Python! Добро пожаловать в клуб 🙂 Не волнуйтесь, ошибки программирования очень распространены. На самом деле разница между хорошим программистом и плохим состоит в том, что хорошие программисты учатся на своих ошибках, тогда как плохие программисты думают, что хорошие программисты никогда не ошибаются. Ошибки в Python бывает трудно понять. Ниже приведено сообщение об ошибке на моем экране: TypeError Traceback (most recent call last) in () 3 var3 = ‘hello, my name is Mike’ 4 —-> 5 var1 + var3 TypeError: unsupported operand type(s) for +: ‘int’ and ‘str’
Python указывает стрелкой ошибочную строку. Сообщение об ошибке, которое, как мы надеемся, поможет нам понять, что именно пошло не так и как это исправить, напечатано внизу. В этом случае сообщением об ошибке является TypeError (Ошибка типа). Что это значит и что такое «типы»?
296 Краткое руководство по языку Python
Типы данных Оказывается, у переменных есть типы, описывающие тип данных, которые эти переменные хранят. Наличие разных типов делает вычисления эффективнее, поскольку операции работают по-разному с разными типами данных. В Python существует много типов данных. Здесь я представлю четыре из них, и по мере чтения книги вы узнаете больше о других типах данных: Целочисленный Они называются int и представляют собой целые числа, такие как −3, 0, 10 и 1234. Вещественный Они называются float, но это всего лишь причудливый термин для чисел с (плавающей) десятичной точкой, таких как –3.1, 0.12345 и 12.34. Имейте в виду, что числа с плавающей точкой и целые могут выглядеть одинаково для нас, людей, но функции Python обрабатывают их по-разному. Например, 3 – это целое число, а 3.0 – это число с плавающей точкой. Строковый Они называются str и являются текстом. Здесь также помните о различии между 5 (строковым значением, соответствующим символу 5) и 5 (int, соответствующим числу 5). Списковый Список – это коллекция элементов, каждый из которых может иметь разный тип данных. Списки очень удобны и повсеместно используются в программировании на Python. Следующий ниже фрагмент исходного кода иллюстрирует три важные особенности списков: (1) они обозначаются квадратными скобками [ ], (2) запятые отделяют элементы списка и (3) отдельные элементы списка могут иметь разные типы данных: list1 = [ 1,2,3 ] list2 = [ ‘hello’, 123.2, [3, ‘qwerty’] ]
Второй список показывает, что списки могут содержать другие списки. Иными словами, третий элемент списка list2 сам по себе является списком. Что делать, если вы хотите обратиться только ко второму элементу списка list2? Отдельные элементы списка извлекаются с помощью индексации, о которой я расскажу в следующем разделе. Тип данных определяется с помощью функции type. Например, вычислите следующее в новой ячейке: type(var1)
Эй, подождите, а что такое «функция»? Вы, возможно, с нетерпением хотите узнать о применении и создании функций, но сначала я хотел бы обратиться к индексации.
Функции 297 Как называть свои переменные? Есть несколько жестких правил именования переменных. Имена переменных нельзя начинать с цифр (хотя они могут содержать цифры), а также нельзя включать пробелы или не буквенно-цифровые символы, такие как !@#$%^&*(). Символы подчеркивания _ разрешены. Существуют также рекомендации по именованию переменных. Самое важное правило – делать имена переменных осмысленными и интерпретируемыми. Например, имя rawDataMatrix гораздо лучше, чем q. В своем исходном коде можно создавать десятки переменных, и совсем не помешает возможность понимать данные, на которые переменная ссылается, по ее имени.
Индексация Индексация означает доступ к определенному элементу в списке (и связанным типам данных, включая векторы и матрицы). Вот как извлекается второй элемент списка: aList = [ 19,3,4 ] aList[1] >> 3
Обратите внимание, что индексация выполняется с использованием квад ратных скобок после имени переменной, а затем числа, которое вы хотите индексировать. Но постойте: я написал, что нам нужен второй элемент; почему исходный код обращается к элементу 1? Это не опечатка! В языке Python индексация начинается с 0, то есть индекс 0 – это первый элемент (в данном случае число 19), индекс 1 – второй элемент и т. д. Если вы новичок в языках программирования, в которых применяется отсчет от 0, то, возможно, это покажется странным и запутанным. Полностью сочувствую. Хотел бы написать, что это станет второй натурой после некоторой практики, но правда в том, что индексация с отсчетом от 0 всегда будет источником путаницы и ошибок. Это просто то, о чем нужно всегда помнить. Как обратиться к числу 4 в списке aList? Его можно индексировать напрямую как aList[2]. Но индексация Python имеет удобную функцию, благодаря которой можно индексировать элементы списка в обратном порядке. Для того чтобы обратиться к последнему элементу списка, наберите aList[-1]. -1 можно трактовать как отсчет по кругу от конца списка. Аналогичным образом предпоследним элементом списка будет aList[-2] и т. д.
Функции Функция – это фрагмент исходного кода, который можно выполнять без необходимости снова и снова набирать все отдельные его элементы. Некото-
298 Краткое руководство по языку Python рые функции – короткие и состоят всего из нескольких строк кода, тогда как другие состоят из сотен или тысяч строк кода. Функции обозначаются в Python с помощью круглых скобок сразу после имени функции. Вот несколько общих функций: type() # возвращает тип данных print() # печатает текстовую информацию в блокнот sum() # складывает числа
Комментарии Комментарии – это куски кода, которые Python игнорирует. Комментарии помогают вам и другим интерпретировать и понимать исходный код. Комментарии в Python обозначаются символом #. Любой текст после # игнорируется. Комментарии могут находиться в отдельных строках либо справа от фрагмента исходного кода.
Функции могут принимать входные данные и могут предоставлять выходные данные. Общая анатомия функции Python выглядит так: output1, output2 = functionname(input1, input2, input3)
Возвращаясь к предыдущим функциям: dtype = type(var1) print(var1+var2) total = sum([1,3,5,4]) >> 30.4
print() – очень полезная функция. Python выводит результат только последней строки в ячейке и только тогда, когда эта строка не содержит присваивание значения переменной. Например, напишите следующий ниже фрагмент исходного кода: var1+var2 total = var1+var2 print(var1+var2) newvar = 10 >> 30.4
Приведенный выше исходный код состоит из четырех строк, поэтому можно было ожидать, что Python выдаст четыре результата. Но выдается только один результат, который соответствует функции print(). Первые две строки не выводят свой результат, потому что они не являются последней строкой, а последняя строка – потому что это присваивание значения переменной.
Методы в качестве функций Метод – это функция, которая вызывается непосредственно на переменной. Различные типы данных имеют разные методы, а это означает, что метод, работающий на списках, не будет работать на строковых литералах. Например, списковый тип данных имеет метод append, который добавляет дополнительный элемент в существующий список. Вот пример: aSmallList = [ ‘one’,’more’ ] print(aSmallList) aSmallList.append( ‘time’ ) print(aSmallList) >> [‘one’,’more’] [‘one’,’more’,’time’]
Обратите внимание на форматирование синтаксиса: методы похожи на функции тем, что они имеют круглые скобки, и (у некоторых методов) есть входные аргументы. Но методы присоединяются к имени переменной точкой и могут изменять переменную напрямую без выдачи явного результата. Потратьте немного времени, чтобы изменить исходный код, используя другой тип данных, например строковое значение вместо списка. Повторное выполнение исходного кода приведет к следующему ниже сообщению об ошибке: AttributeError: ‘str’ object has no attribute ‘append’
Это сообщение об ошибке означает, что строковый тип данных распознает функцию append (атрибут – это свойство переменной, а метод – один из таких атрибутов). Методы являются стержневой частью объектно-ориентированного программирования и классов. Этим аспектам Python можно было бы посвятить отдельную книгу по Python, но не беспокойтесь – для того чтобы изучать линейную алгебру с помощью этой книги, полное понимание объектно-ориентированного программирования вам не потребуется.
Написание своих собственных функций В Python имеется много функций. Слишком много, чтобы сосчитать. Но никогда не найдется той идеальной функции, которая делает именно то, что вам нужно. И в итоге вам придется писать свои собственные функции. Создавать собственные функции легко и удобно; для определения функции используется встроенное ключевое слово def (ключевое слово – это зарезервированное имя, которое нельзя переопределять как переменную или
300 Краткое руководство по языку Python функцию), затем указывается имя функции и любые возможные входные данные, и строка заканчивается двоеточием. Любые строки кода после этого включаются в функцию, если они отделены двумя пробелами1. Python очень требователен к отступам в начале строки (но не к отступам в других частях строки). Любые результаты обозначаются ключевым словом return. Начнем с простого примера: def add2numbers(n1, n2): total = n1+n2 print(total) return total
Эта функция принимает два входных аргумента и вычисляет, печатает и выводит их сумму. Теперь пришло время вызвать функцию: s = add2numbers(4,5) print(s) >> 9 9
Почему число 9 появилось дважды? Оно было напечатано один раз, потому что функция print() была вызвана внутри функции, а затем оно было напечатано во второй раз, когда я вызвал print(s) после функции. Для того чтобы в этом убедиться, попробуйте поменять строку после вызова функции на print(s+1). (Видоизменение исходного кода, чтобы увидеть результат на выходе, – отличный способ изучить Python; только не забывайте отменять свои изменения.) Обратите внимание, что имя переменной, назначенное для вывода внутри функции (total), может отличаться от имени переменной, которое я использовал при вызове функции (s). Написание конкретно-прикладных функций обеспечивает бóльшую гибкость, например установка дополнительных входных данных и используемых по умолчанию параметров, проверка входных данных на тип данных и согласованность и т. д. Но в этой книге базового понимания функций будет достаточно.
Когда писать функции? Если у вас есть строки исходного кода, которые нужно выполнять десятки, сотни или, может быть, миллиарды раз, то написание функции – это, безусловно, правильный путь. Некоторым людям действительно нравится писать функции, и они будут писать специальные функции, даже если они вызываются только один раз. Я предпочитаю создавать функцию только тогда, когда она будет вызываться несколько раз в разных контекстах или частях исходного кода. Но вы являетесь хозяином своего собственного исходного кода, и по мере накопления опыта программирования вы сможете выбирать ситуации, когда помещать исходный код в функции. 1
В некоторых IDE допускается два или четыре пробела; в других принимается только четыре пробела. Я считаю, что два пробела выглядят чище.
Библиотеки Python сконструирован таким образом, чтобы его можно было легко и быстро устанавливать и выполнять. Но недостатком является то, что базовый пакет Python поставляется с малым числом встроенных функций. Поэтому разработчики создают коллекции функций, ориентированных на определенную тему, и такие коллекции называются библиотеками. После того как вы импортируете библиотеку в Python, вы можете обращаться ко всем функциям, типам переменных и методам, доступным в этой библиотеке. Согласно результатам поиска в Google, существует более 130 000 библио тек Python. Не волнуйтесь, вам не нужно запоминать их все! В этой книге мы будем использовать лишь несколько библиотек, предназначенных для числовой обработки и визуализации данных. Самая важная библиотека для линейной алгебры называется NumPy; ее название состоит из комбинации слов numerical Python (численный Python). Библиотеки Python не входят в базовую установку Python, а это значит, что вам нужно скачать их из интернета, а затем импортировать в Python. За счет этого обеспечивается их доступность для использования внутри Python. Их нужно скачивать только один раз, но импортировать их придется повторно в каждом сеансе Python1.
NumPy Для того чтобы импортировать библиотеку NumPy в Python, наберите: import numpy as np
Обратите внимание на общую формулировку импорта библиотек: import libraryname as abbreviation (импортировать имя библиотеки как аббревиатуру). Аббревиатура является удобным сокращением. Для получения доступа к функциям в библиотеке пишется сокращенное имя библиотеки, точка и имя функции. Например: average = np.mean([1,2,3]) sorted1 = np.sort([2,1,3]) theRank = np.linalg.matrix_rank([[1,2],[1,3]])
Третья строка исходного кода показывает, что библиотеки могут иметь вложенные в них подбиблиотеки или модули. В данном случае у NumPy есть много функций, а внутри NumPy есть библиотека под названием linalg, которая содержит свои функции, специально связанные с линейной алгеброй. NumPy имеет собственный тип данных под названием массив NumPy. Массивы NumPy изначально кажутся похожими на списки в том смысле, что 1
Если вы установили Python через Anaconda или используете среду Google Colab, то скачивать какие-либо библиотеки для этой книги не нужно, но вам нужно будет их импортировать.
302 Краткое руководство по языку Python в них обоих хранятся коллекции информации. Но в массивах NumPy хранятся только числа, и у них есть атрибуты, удобные для математического программирования. В следующем ниже фрагменте исходного кода показано, как создавать массив NumPy: vector = np.array([ 9,8,1,2 ])
Индексация и нарезка в NumPy Хотел бы вернуться к теме доступа к одному элементу внутри переменной. К одному элементу массива NumPy можно обращаться с помощью индексации точно так же, как индексируется список. В следующем ниже блоке кода я использую функцию np.arange, чтобы создать массив целых чисел от −4 до 4. Это не опечатка в коде – второй аргумент равен +5, но возвращаемые значения заканчиваются на 4. В Python часто используются эксклительные верхние границы; это означает, что Python считает до указанного вами конечного числа, но не включает его: ary = np.arange(-4,5) print(ary) print(ary[5]) >> [-4 -3 -2 -1 0 1 2 3 4] 1
Это все хорошо, но что, если вы хотите получить доступ, например, к первым трем элементам? Или каждому второму элементу? Тут самое время перейти от индексации к нарезке. Нарезка работает просто: надо указать начальный и конечный индексы с двоеточием между ними. Просто помните, что диапазоны Python имеют эксклительные верхние границы. Таким образом, для того чтобы получить первые три элемента массива, мы нарезаем до индекса 3 + 1 = 4, но тогда нам нужно учитывать индексацию на основе 0, то есть первые три элемента имеют индексы 0, 1 и 2, и мы нарезаем их, используя 0:3: ary[0:3] >> array([-4, -3, -2])
Каждый второй элемент проиндексируется с помощью оператора пропуска: ary[0:5:2] >> array([-4, -2, 0])
Формулировка индексации с пропуском такова: [начало:конец:пропуск]. Весь массив можно просматривать в обратном порядке, указывая пропуск, равный −1, например вот так: ary[::-1].
Знаю, это немного сбивает с толку, но обещаю: на практике все будет легче.
Визуализация Многие концепции линейной алгебры и большинства других областей математики лучше всего понять, увидев их на экране компьютера. Большинство визуализаций данных на Python обрабатываются библиотекой matplotlib. Некоторые аспекты графических отображений зависят от интерактивной среды разработки. Однако весь исходный код в этой книге работает как есть в любой среде Jupyter (посредством Google Colab, другого облачного сервера или локально). Если вы используете другую интерактивную среду разработки, то вам может потребоваться внести несколько незначительных изменений. Вводить matplotlib.pyplot становится очень утомительно, поэтому название этой библиотеки часто сокращают до plt. Вы увидите это в следующем ниже блоке кода. Начнем с рисования точек и линий. Посмотрите, сможете ли вы понять, как следующий ниже фрагмент исходного кода отображается на рис. 16.2: import matplotlib.pyplot as plt import numpy as np plt.plot(1,2,’ko’) plt.plot([0,2],[0,4],’r—‘) plt.xlim([-4,4]) plt.ylim([-4,4]) plt.title(‘Имя графика’)
начерить черный кружок начерить линию задать пределы оси x задать пределы оси y указать имя графика Имя графика
Рис. 16.2 Визуализация данных, часть 1
Удалось расшифровать исходный код? Строка кода № 1 говорит, что нужно нарисовать черный кружок (ko – k означает черный, а o – кружок) в местопо-
304 Краткое руководство по языку Python ложении xy 1,2. Строка кода № 2 предоставляет списки чисел вместо отдельных чисел. В ней определяется линия, которая начинается в координате xy (0, 0) и заканчивается в координате (2, 4). r— обозначает красную пунктирную линию. Строки кода № 3 и № 4 устанавливают ограничения по осям x и y, и, конечно же, строка № 5 создает заголовок. Прежде чем двигаться дальше, уделите немного времени обследованию этого исходного кода. Нарисуйте дополнительные точки и линии, попробуйте разные маркеры (подсказка: попробуйте буквы о, с и р) и разные цвета (попробуйте r, k, b, y, g и m). В следующем ниже блоке кода представлены графики и изображения. Подграфик – это способ разделения графической области (именуемой figure, то есть рисунком) на решетку отдельных осей, по которым можно рисовать разные визуализации. Как и в случае с предыдущим блоком кода, прежде чем читать мое описание, проверьте свое понимание процедуры создания рис. 16.3 приведенным ниже фрагментом исходного кода: _,axs = plt.subplots(1,2,figsize=(8,5)) # 1) создать подграфики axs[0].plot(np.random.randn(10,5)) # 2) линейный график слева axs[1].imshow(np.random.randn(10,5)) # 3) изображение справа
Рис. 16.3 Визуализация данных, часть 2
Строка кода № 1 создает подграфики. Первые два аргумента функции plt. subplots определяют геометрию решетки – в данном случае это матрица подграфиков 1×2, то есть одна строка и два столбца, что означает два графика бок о бок. Первый аргумент задает общий размер рисунка, причем два элемента в этом кортеже соответствуют ширине, а затем высоте в дюймах. (Размеры всегда указываются как ширина, высота. Мнемоника для запоминания порядка – WH как в «White House».) Функция plt.subplots предоставляет два результата. Первый – это дескриптор всего рисунка, который нам не нужен, поэтому вместо имени переменной я использовал подчеркивание. Второй
Переложение формул в исходный код 305
результат – это массив NumPy, содержащий дескрипторы каждой оси. Дескриптор – это особый тип переменной, которая указывает на объект на рисунке. Теперь о строке кода № 2. Это должно быть знакомо по предыдущему блоку кода; два новых понятия – построение графика на определенной оси вместо всего рисунка (с использованием plt.). И передача на вход матрицы вместо отдельных чисел. Python создает отдельную строку для каждого столбца мат рицы, поэтому на рис. 16.3 вы видите пять линий. Наконец, строка кода № 3 показывает, как создавать изображение. Как вы узнали из главы 5, матрицы нередко визуализируются как изображения. Цвет каждого маленького блока в изображении соотносится с числовым значением в матрице. Что ж, о создании графики на Python можно было бы сказать гораздо больше. Но я надеюсь, что этого введения будет достаточно, чтобы начать работу.
Переложение формул в исходный код Переложение математических уравнений в исходный код Python иногда не представляет никаких трудностей, а иногда сопряжено со сложностями. Но это важный навык, и вы улучшите его с практикой. Начнем с простого примера в уравнении 16.1. Уравнение 16.1. Уравнение y = x 2. Возможно, вы подумаете, что следующий ниже фрагмент исходного кода будет работать: y = x**2
Но вы получите сообщение об ошибке (NameError: name x is not defined). Проблема в том, что мы пытаемся использовать переменную x до ее определения. Так как же определять x? На самом деле, когда вы смотрите на математическое уравнение, вы определяете x, даже не задумываясь об этом: x начинается в отрицательной бесконечности и уходит в положительную бесконечность. Но вы не очерчиваете функцию так далеко – для построения этой функции вы, вероятно, выбрали бы ограниченный диапазон, возможно от −4 до +4. И этот диапазон – именно то, что необходимо указать на Python: x = np.arange(-4,5) y = x**2
На рис. 16.4 показан график функции, созданный с использованием plt. plot(x,y,’s-‘).
306 Краткое руководство по языку Python
Рис. 16.4 Визуализация данных, часть 3
Выглядит неплохо, но я думаю, что он получился слишком прерывистым; хотелось бы, чтобы линия выглядела глаже. Этого можно добиться, увеличив разрешающую способность, то есть увеличив число точек в диапазоне от −4 до +4. Я буду использовать функцию np.linspace(), которая принимает три аргумента: начальное значение, конечное значение и число промежуточных точек: x = np.linspace(-4,4,42) y = x**2 plt.plot(x,y,’s-‘)
Теперь у нас 42 точки, расположенные линейно (равномерно) между −4 и +4. В результате график делается более гладким (рис. 16.5). Обратите внимание, что функция np.linspace выводит вектор, оканчивающийся на +4. Эта функция имеет инклюзивные границы. Немного дезориентирует, что приходится запоминать, какие функции имеют инклюзивные, а какие – эксклюзивные границы. Не волнуйтесь, вы с этим справитесь.
Рис. 16.5 Визуализация данных, часть 4
Переложение формул в исходный код 307
Давайте попробуем еще один перевод функции в исходный код. Я также воспользуюсь этой возможностью, чтобы познакомить вас с концепцией, именуемой мягким программированием, которая означает создание переменных для параметров, которые вы, возможно, захотите изменить позже. Пожалуйста, перед тем как смотреть на мой следующий ниже фрагмент исходного кода, переведите следующую математическую функцию в исходный код и создайте график:
Эта функция называется сигмоидой и часто используется в прикладной математике, например как нелинейная активационная функция в моделях глубокого обучения. α и β – это параметры уравнения. Здесь я установил для них определенные значения. Но после того, как ваш исходный код заработает, вы сможете обследовать влияние изменения этих параметров на результирующий график. На самом деле использование исходного кода для понимания математики – это, ИМХО1, самый лучший способ изучения математики. Эту функцию можно запрограммировать двумя способами. Один из них заключается в помещении числовых значений α и β непосредственно в функцию. Это пример жесткого программирования, поскольку значения парамет ров реализуются в функции напрямую. Альтернативой является установка переменных Python равными этим двум параметрам, а затем использование этих параметров при создании математической функции. Это и есть мягкое программирование, и оно упрощает чтение, видоизменение и отладку исходного кода: x = np.linspace(-4,4,42) alpha = 1.4 beta = 2 num = alpha # числитель den = 1 + np.exp(-beta*x) # знаменатель fx = num / den plt.plot(x,fx,’s-‘)
Обратите внимание, что я разделил создание функции на три строки кода, которые определяют числитель и знаменатель, а затем их отношение. Это делает исходный код чище, и его легче читать. Всегда стремитесь к тому, чтобы исходный код легко читался, потому что это (1) снижает риск ошибок и (2) облегчает отладку. 1
Мне сказали, что ИМХО – это тысячелетний жаргонизм от англ. in my humble opinion (по моему скромному мнению).
308 Краткое руководство по языку Python На рис. 16.6 показана результирующая сигмоида. Потратьте несколько минут, чтобы поэкспериментировать с исходным кодом: измените пределы и разрешающую способность переменной x, измените значения параметров alpha и beta, возможно, даже измените саму функцию. Математика прекрасна, Python – это ваш холст, а исходный код – ваша кисть!
Рис. 16.6 Визуализация данных, часть 5
Форматирование печати и F-строки Вы уже знаете, как распечатывать переменные с помощью функции print(). Но эта фукция применяется только для вывода одной переменной без другого текста. F-строки дают вам больше контроля над выходным форматом. Например: var1 = 10.54 print(f’Значение переменной равно , и я от этого счастлив.’) >> Значение переменной равно 10.54, и я от этого счастлив.
Обратите внимание на две ключевые особенности f-строки: начальную букву f перед первой кавычкой и фигурные скобки <>, заключающие имена переменных, которые заменяются значениями переменных. Следующий ниже блок кода еще больше подчеркивает гибкость f-строк: theList = [‘Майк’, 7] print(f’ ест гр. шоколада каждый день.’) >> Майк ест 700 гр. шоколада каждый день.
Из этого примера следует усвоить два ключевых момента: (1) не волнуйтесь, на самом деле я не ем столько шоколада (ну, не каждый день хотя бы),
Поток управления 309
и (2) вы можете использовать индексацию и исходный код внутри фигурных скобок, и Python распечатает результат вычисления. И последняя особенность форматирования f-строк: pi = 22/7 print(f’, ‘) >> 3.142857142857143, 3.143
Ключевым дополнением в приведенном выше фрагменте исходного кода является форматное выражение :.3f, которое управляет форматированием результата. Этот фрагмент кода сообщает Python, что надо напечатать три числа после запятой. Посмотрите, что происходит, если заменить 3 на другое целое число, и что происходит, если вставить целое число перед двоеточием. Существует много других вариантов форматирования и иных способов гибкого вывода текста, но в этой книге вам требуется лишь базовое понимание принципа работы f-строк.
Поток управления Сила и гибкость программирования исходят из того, что вы наделяете свой исходный код способностью адаптировать свое поведение в зависимости от состояния определенных переменных или вводимых пользователем данных. Динамичность исходного кода обеспечивается инструкциями управления потоком.
Компараторы Компараторы – это специальные символы, которые позволяют сравнивать разные значения. Результатом компаратора является тип данных, именуемый булевым типом, который принимает одно из двух значений: True либо False. Вот несколько примеров: print( 45 ) # 2 print( 4==5 ) # 3
Результаты этих строк таковы: True в #1 и False в #2 и #3. Третье выражение содержит двойной знак равенства. Он сильно отличается от одиночного знака равенства, который, как вы уже знаете, используется для присваивания значений переменной. Еще два компаратора таковы: = (больше или равно).
310 Краткое руководство по языку Python
Инструкции if Инструкции if интуитивно понятны, потому что они применяются все время: если (if ) я устану, то я отдохну. Базовая инструкция if состоит из трех частей: ключевого слова if, условного выражения и содержимого исходного кода. Условное выражение – это фрагмент исходного кода, который вычисляется как истина либо ложь, за которым следует двоеточие (:). Если условие истинно, то исполняется весь исходный код, расположенный ниже и с отступом; если условие ложно, то ни одна строка исходного кода с отступом не исполняется, и Python продолжит исполнение исходного кода без отступа. Вот пример: var = 4 if var==4: print(f’ равно 4!’) print(«Я -снаружи цикла +for+.») >> 4 равно 4! Я — снаружи цикла +for+.
А вот еще пример: var = 4 if var==5: print(f’ равно 5!’) print(«Я -снаружи цикла +for+.») >> Я — снаружи цикла +for+.
Первое сообщение пропускается, так как 4 не равно 5; следовательно, условное выражение ложно, и поэтому Python игнорирует весь исходный код с отступом.
Инструкции elif и else Эти два примера показывают базовую форму инструкции if. Инструкции if могут содержать дополнительные условные конструкции для повышения сложности потока информации. Прежде чем читать мое объяснение следующего ниже фрагмента исходного кода и прежде чем набирать его на Python на своем компьютере, попытайтесь понять исходный код и предсказать, какие сообщения будут напечатаны: var = 4 if var==5: print(‘var is 5’) # фрагмент 1 elif var>5:
Поток управления 311 print(‘var > 5’) # фрагмент 2 else: print(‘var
Contact information
Address: 1918 St.Regis, Dorval, Quebec, H9P 1H6, Canada.
