[МУЗЫКА] В этом видео мы увидим, что проецирование — это не просто геометрическая операция, а это действие, которое позволяет получать прогнозы в простой ситуации. Поехали. Мы подключим необходимые нам функции и соответственно возьмем данные по покемонам (данные взяты из платформы kaggle), и мы соответственно их загружаем. Давайте сначала посмотрим на данные, всегда сначала надо посмотреть на данные. Мы напишем pokemon.describe и для удобства транспонируем табличку. Соответственно мы видим, что здесь у покемонов огромное количество характеристик. Давайте, чтобы не загромождать наше вычисление, сосредоточимся всего лишь на некоторых из них. Ну например, у покемона есть сила атаки. У нас 801 покемон, и вот сила атаки ранжируется от пяти до 185. Есть сила защиты, переменная defense, ранжируется от пяти до 230. И например, есть скорость, тоже от пяти до 180. На этих трех показателях мы и сосредоточимся и попробуем с помощью скорости и защиты спрогнозировать силу атаки покемона. Давайте для начала посмотрим на графике, потому что всегда полезно посмотреть, а может, там вообще никакой зависимости нет, Возьмем из пакета seaborn, построим jointplot по нашему набору, покемонам, по x мы отложим соответственно силу защиты, а по y отложим ту переменную, которую будем предсказывать, а именно силу атаки. И мы видим, что у нас есть надежда на построение прогнозов, что зависимость явно есть. Давайте посмотрим на вторую переменную, которую мы отобрали. sns.jointplot. Данные мы берем из набора данных по покемонам. По x мы откладываем скорость, а по y откладываем силу атаки. Здесь уже зависимость нам не так хорошо видна, но все-таки что-то положительное на первый взгляд прослеживается. Раз мы ознакомились с набором данных, то давайте попробуем сначала получить проекцию по нашим формулам проекции, которые мы вывели в рамках курса. Значит, для удобства мы сначала отберем y в отдельную переменную. y — это мы из нашего набора данных отбираем вектор, который отвечает за силу атаки, это вектор из 801 наблюдения. Потом мы отбираем, в матрицу иксов мы предварительно кладем вектор, который отвечает за силу защиты, и вектор, который отвечает за скорость. Отобрали. Дальше. Еще мы дополним нашу матрицу x специальным столбцом, состоящим из одних единичек — 1 1 1 1 1 1. Нам нужна, соответственно, 801 единичка, потому что если мы посмотрим на размер x, то размер x у нас 801 строка на два столбца. Соответственно нам нужен для начала еще столбик из 801 единички. Давайте мы его назовем once. Это у нас будет once по размеру, а размер мы возьмем соответственно 801 на 1. Нам нужен один столбик из единиц. И теперь мы к матрице x слева припишем еще один вектор столбика из единичек. То есть мы возьмем горизонтально, положим наш столбик из единичек и нашу старую матрицу иксов. Ну давайте посмотрим на результат. Значит, наш x, и посмотрим на несколько ее первых строчек, допустим, первые несколько строчек. Вот мы видим столбик из единичек длинный, потом столбик, который отвечает за силу защиты вектора, и потом столбик, который отвечает за скорость покемона. И соответственно берем и проецируем y на линейную оболочку этих трех векторов, то есть на линейную оболочку размерности 3. Ну давайте проецировать. На всякий случай формулы — вот они, то есть нам надо решить систему уравнений (X'X) на вектор весов v = X транспонированное y. Решаем. Давайте найдем X транспонированное X. Нам надо X, как ни странно, транспонировать и домножить матрично на X. Нам надо получить X транспонированное y, нам надо взять X транспонированное, домножить на y. После этого нам надо решить систему. Тут можно делать двумя способами. С точки зрения совсем уж чистой теории, кажется, что без разницы. Вот есть одна команда, которая просто решает систему, XtX — это левая часть системы, а Xty — это правая часть системы. Вот ровно, как здесь написано, (X'X)v = X'y. И можно посмотреть на наш вектор весов. И в принципе, есть другой вариант решения — можно обратить матрицу. И в данном случае, поскольку у нас небольшой набор данных, все достаточно устойчиво, определитель матрицы далек от нуля, то поэтому в данном случае еще сработает метод с обращением матрицы, но надо осознавать, что в реальной боевой задаче он, конечно, нестабильный и невыгодный по времени. Но в данном случае вполне сработает такой способ как можно обратить матрицу XtX и домножить ее на Xty. Но надо осознавать, что все-таки способ с решением системы более стабильный и более быстрый. Ответ здесь получится, конечно, такой же, в данном случае это несущественно. Но мы можем убедиться, что мы действительно решили задачу проекции. Если я напишу свою формулу для проекции (формула для проекции — это конечно, X помножить на вектор весов v), то я могу легко проверить, что, скажем, разница между y и проекцией, то есть то, что ортогонально иксам. Давайте проверим, что это действительно ортогонально иксам. Возьмем, например, один из наших иксов, это сила защиты, и убедимся, что у нас действительно получается ноль. −9 тут e в −10-й, это означает −9 * 10⁻¹⁰ — это действительно численно ноль. То есть действительно y − y с крышкой в данном случае оказывается ортогонально вектору защиты. Таким образом, мы нашли проекцию. Что это там позволяет делать? Ну нам позволяет прогнозировать. Допустим, у меня есть какой-то новый покемон. Родился новый покемон, и про него мы знаем, что у него соответственно сила защиты равна 42, а скорость равна 77. Вот мы тут напишем 42, 77. И мы для этого нового покемона сможем предсказать, какая у него будет сила атаки. Мы new pokemon умножим на соответственно наш вектор весов, с которыми наши столбики входят в проекцию. И получили вот прогноз силы атаки этого нового покемона. Но тут следует отметить, что значок собачки @ в Python'е означает как матричное умножение, такое и скалярное произведение. И вот здесь вот были матрицы правильно умножены, а здесь хотя вектора были с точки зрения матриц размеры не соответствовали, но поскольку эти вектора были одной длины, то поэтому это сработало как скалярное произведение. И эта задача, которую мы решили, она безумно важна. Конечно, есть более сложные способы прогнозирования, которые дают лучшие результаты, но которые при этом немножко теряют в интерпретируемости, но тем не менее этот базовый алгоритм, который называется линейной регрессией умными словами, он активно используется в боевых задачах, и давайте попробуем получить тот же самый ответ, только уже не обращая матрицы руками или не вызывая вручную решение системы, а воспользовавшись пакетом sklearn и соответствующими функциями из него. То есть сначала мы зададим наш оцениватель, это будет линейная регрессия. И после этого, мы просто скажем: подберем ее параметры. Давайте мы заново отберем x. Заново я отберу x, потому что в силу того, что вот такая техническая задача, как предписывание единичного столбика, она решается автоматически, и поэтому здесь нам не надо, когда мы пользуемся готовым продуктом, нам не надо самим вручную приписывать единичный столбик, поэтому я просто возьму вот старый код x без единичного столбика, и вот здесь вот его продублирую. И, соответственно, дальше просто получу результаты оценки линейной регрессии, подставлю туда наш x и наш y. И все. Вот это действие на самом деле считает проекции. Давайте достанем те же самые коэффициенты, с которыми в проекцию входят столбцы матрицы x. Если я возьму fit.coef, то я получу, соответственно, те же самые 48 и 38. Вот они, обратите внимание, вот они здесь. 487, 0,388 — вот это ровно они. И особую роль, поскольку автоматически добавляется единичный столбик, вот здесь 16.53 не выводится, но его тоже можно достать. Вот он fit.intercept 16.53, вот он тоже здесь 16.53. То есть задачу, которую решает линейная регрессия — это просто с алгебраической точки зрения задача проецирования. И мы точно так же можем построить прогнозы. Мы можем построить прогнозы для наших старых данных. Это прогнозы для наших старых данных. И мы можем спрогнозировать силу атаки нового покемона. Возьмем, соответственно, нашего нового покемона создадим. Мы поскольку работаем с датафреймами, то мы возьмем pandas DataFrame, и здесь создадим покемона с силой защиты 42 и со скоростью, если я правильно помню, 77. Ну сейчас заодно и проверим, правильно я помню или неправильно. Вот и мы для этого покемона тоже нового сможем предсказать, какая у него будет сила атаки. Естественно она будет 66.89, точно такая же, как у нас получилась в предыдущем результате. И более того, мы можем визуально оценить качество наших прогнозов для всей массы покемонов. Мы можем добавить в покемонов наш вектор прогнозов, это наш y-проекция, и изобразить на одном графике. По горизонтали мы отложим фактическую силу атаки покемонов, а по вертикали отложим наши прогнозы и посмотрим, насколько они совпадают. [БЕЗ_ЗВУКА] И соответственно, мы видим, что в целом неплохо. Ну например, если фактическая сила атаки была около 100, то мы тут прогнозировали где-то от 70 до 120 силу атаки. Естественно, мы иногда ошибаемся, наши прогнозы несовершенны. Где-то мы ошиблись очень сильно. Вот, например, вот здесь вот одно наблюдение висит далеко отдельностоящее, где сила атаки была около десятки на глаз, а наш прогноз был 110, но в целом мы в среднем угадываем, и поэтому мы видим, что, казалось бы, чисто геометрическая идея проецирования приносит пользу при прогнозировании. [МУЗЫКА]