Добро пожаловать! В этом блоге рассматриваются некоторые из наиболее интуитивно понятных и геометрических деталей метода наименьших квадратов для выполнения линейной регрессии. Затем мы перейдем от наших новых интуитивных представлений к обсуждению выбора функций при выполнении линейной регрессии и исследуем компромисс смещение/дисперсия в машинном обучении. Предполагается наличие некоторых базовых знаний в области исчисления, линейной алгебры и статистики.

Обзор линейной регрессии

В контексте машинного обучения мы обычно пытаемся изучить некоторую скрытую «функцию», присутствующую в реальном мире, из собранных данных, а затем предсказать новые точки данных. Возможно, самой базовой моделью машинного обучения является линейная регрессия, в которой пытаются подогнать к данным некую «линейную» функцию. В одномерном случае с одной переменной это рассматривается как соответствие прямой линии через данные, которые могут быть представлены в форме пересечения наклона из школьной алгебры. Как правило, если можно подтвердить, что набор данных следует определенному набору предположений (наиболее важно, что данные следуют линейной модели), то линейная модель, такая как линейная регрессия, предпочтительнее, поскольку они хорошо понятны и просты для оптимизации по сравнению с нелинейной моделью. модели, такие как нейронные сети.

Сначала небольшой обзор и уточнение модели, которую мы изучаем. Мы рассматриваем следующую модель наших данных:

Где 𝘆 — вектор целевых значений, X — матрица плана, 𝛉* — вектор оптимальных параметров, а ε — шум (из среднего нулевого распределения Гаусса). Мы можем думать о матрице дизайна как о каждом из наших обучающих примеров, наложенных друг на друга, как показано на рисунке (каждая строка представляет собой обучающий пример, перевернутый на бок). В этом случае каждый обучающий пример представляет собой вектор 𝒙, содержащий элементы d, где d — количество «функций» или «независимых переменных» в нашей модели.

В нашем анализе мы рассматриваем так называемый фиксированный анализ линейной регрессии, в котором каждый обучающий пример 𝒙 считается детерминированным, а не случайной величиной. Поэтому в нашей модели единственными случайными переменными являются целевые значения 𝘆 и значения шума ε.

Мы будем использовать метод обычных наименьших квадратов (OLS), чтобы найти подходящие параметры для нашей модели, и поэтому мы будем использовать квадрат ошибки в качестве функции стоимости. В качестве мотивации можно показать, что минимизация квадратичных потерь для соответствия 𝛉 эквивалентна максимизации вероятности 𝑝(𝘆|X), если мы предположим, что 𝘆 выбирается из 𝒩(X𝛉, diag(𝜎²)).[1] Стоимость записывается как сумма квадратов ошибок по всем примерам:

или в матричной записи:

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

Мы не будем вдаваться в детали этого вывода здесь (см. [1] ниже в качестве ссылки на это), но мы просто будем помнить, что мы по существу использовали исчисление для минимизации функции стоимости, чтобы найти наши подходящие параметры.

После того, как мы подогнали тета с помощью нормального уравнения, мы получили график, который выглядит так:

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

Данные как линейное преобразование

Глядя на нашу функцию потерь, мы видим, что, по сути, мы смотрим на решение приведенного ниже матричного уравнения, а это означает, что величина наших потерь зависит исключительно от того, насколько близко (X𝛉) к 𝘆.

Для тех, кто знаком с линейной алгеброй, мы можем видеть здесь, что наши данные X действуют как линейное преобразование, которое отображает некоторый вектор 𝛉 в наш целевой вектор 𝘆. В нашей матрице дизайна каждый пример составляет новую строку, а каждая функция — новый столбец. Обычно у нас будет гораздо больше обучающих примеров, чем функций, и поэтому мы получим высокую тощую матрицу, называемую «переопределенной» матрицей (что означает больше уравнений, чем у нас есть неизвестных). В этих случаях размер нашего вектора 𝛉 будет намного меньше, чем вектор в нашем выходном пространстве (например, если у нас есть 2 функции и 100 примеров, мы возьмем вектор в ℜ² и сопоставим его с вектором в ℜ¹⁰⁰) . Переопределенные матричные уравнения, подобные этим, редко имеют точные решения, что может быть интуитивно понятным, если учесть, что для существования точного решения нам потребуется уникальный вектор в ℜ² для сопоставления с каждым вектором в ¹⁰⁰, что, естественно, кажется невозможным, если у одного вектора нет возможности сопоставляться более чем с одним вектором.

Оказывается, размерность пространства решений (X𝛉) (имеется в виду набор всех возможных значений при всех возможных значениях 𝛉) будет определяться рангом матрица дизайна, которая будет максимально использовать количество функций в модели. В частности, пространство решений будет таким же, как пространство столбцов нашей матрицы дизайна X. пространство-столбец можно рассматривать как все возможные векторы, которые могут быть достигнуты линейными комбинациями векторов-столбцов нашей матрицы дизайна. Поэтому, если наша матрица дизайна имеет только два линейно независимых вектора, она сможет достичь векторов только в некоторой двумерной плоскости. Таким образом, для следующей матрицы дизайна игрушек X мы видим, что столбец-пространство на самом деле просто формирует обычную плоскость x-y в трехмерном пространстве. На следующем изображении мы обозначаем это столбцовое пространство элемента X как W и показываем, как наш 𝘆, компонент которого находится в z -направление, лежит вне этого пространства.

Здесь мы получаем некоторую интуицию для нашего исходного многомерного примера, показывающего, как наше пространство решений W будет занимать лишь небольшую часть большего многомерного пространства 𝘆. Наша новая цель состоит в том, чтобы найти ближайшее приближение (то есть «ближайшую» точку) в нашем пространстве решений к вектору решения 𝘆, который мы будем обозначать ŷ.

Двойственные подходы к методу наименьших квадратов

Чтобы найти это ближайшее приближение, мы будем использовать теорему о наилучшем приближении [2] из линейной алгебры, которая гласит (вкратце):

Допустим, W — некоторое подпространство в ℜ^(n), а y — вектор в ℜ^(n). Пусть ŷ — ортогональная проекция y на W. Тогда ŷ является ближайшей точкой в ​​W к y.

Давайте теперь посмотрим, как мы можем применить эту теорему, чтобы найти наилучшее соответствие для 𝛉.

Для обозначения давайте снова обозначим столбцовое пространство элемента X как W. Напоминаем, что размер W будет таким же, как ранг X. Мы можем думать о W как о подпространстве в нижнем измерении многомерного пространства 𝘆. Теперь, используя теорему, мы можем сказать, что ближайшей точкой в ​​нашем пространстве решений Wк 𝘆 является ортогональная проекцияот 𝘆 на W. Это просто визуализируется в двухмерном пространстве как линия, проведенная от 𝘆 к W, которая ортогональна или перпендикулярна W (см. Image2 для примера.) Здесь мы отмечаем, что, как правило, в линейной алгебре все ортогональные векторы всегда будут иметь внутренний продукт, равный 0.

Хотя невозможно визуализировать, как эта проекция выглядит для нашего гораздо большего пространства ℜ¹⁰⁰, мы знаем, используя линейную алгебру, что правила ортогональности все еще применяются. В частности, кратчайший остаточныйвектор (𝘆 ˗ X𝛉) ортогонален любым векторам в пространстве столбцовиз X, из чего следует, что скалярное произведение этого вектора невязки наилучшего приближения и каждого вектора-столбца X равно 0! Зная это, мы можем написать:

Первый шаг связан с тем, что мы знаем, что каждый столбец X ортогонален остатку, из чего мы можем сказать, что внутренний продукт транспонирования X с остатком приведет к вектору 0. Оттуда мы выполняем некоторые манипуляции с линейной алгеброй для нашего окончательного уравнения. Как мы видим, мы получаем то же нормальное уравнение, что и в нашем исходном выводе!

Какая здесь связь с нашим первоначальным выводом с использованием исчисления? Мы можем увидеть это, если попробуем наш предыдущий вывод немного другим способом. Вместо того, чтобы напрямую использовать теорему о наилучшем приближении, давайте снова рассмотрим, как мы можем найти «ближайшее» приближение к 𝘆 из нашего пространства решений (W). Теперь мы можем записать это как задачу оптимизации:

Эта левосторонняя задача оптимизации пытается найти точку в W, которая минимизирует квадрат расстояния (здесь записанный как квадрат нормы L2) от цели y. Заменив wнашу исходную линейную функцию, мы быстро увидим, что это та же проблема оптимизации, которую мы решили, когда минимизировали исходную функцию стоимости! Таким образом, получается, что мы, по сути, минимизируем то же «расстояние», что и изначально, но мы просто пришли к этому с точки зрения линейной алгебры. Конечно, взяв производную от этого и найдя 𝛉, мы вернемся к нормальному уравнению.

Мы замечаем, что можем решить эту же проблему с помощью геометрии (теорема о наилучшем приближении) или с помощью исчисления (через задачу оптимизации). Это круто, поскольку позволяет нам чувствовать себя более уверенно в нашем результате и усиливает ценность рассмотрения проблем с разных точек зрения, поскольку никогда не знаешь, когда один метод окажется более интуитивным или более эффективным, чем другой.

Интуиция подгонки параметров

Давайте сделаем небольшое отступление, чтобы получить немного больше интуиции о подгоночных параметрах. Скажем, мы подгоняем приведенную выше линейную регрессию с двумя функциями (и пропускаем точку пересечения), у нас есть подходящие параметры 𝛉_1 и 𝛉_2. Мы можем интерпретировать абсолютное значение 𝛉_i как относительную важность функции i в этой линейной модели. Мы можем подкрепить эту интуицию, заметив, что влияние любого признака i полностью исчезает, когда 𝛉_i приближается к 0.

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

Однако можно заметить, что если подобрать модель с двумя функциями, а затем снова подобрать модель с добавленной функцией, значения 𝛉_i для исходных двух функций могут измениться! Учитывая, что мы интерпретируем каждое значение 𝛉_i как наилучшеевозможное влияние функции i на эту модель, почему новая независимая функция должна изменить наши исходные два 𝛉ценности?

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

Если мы решим это с помощью метода наименьших квадратов, мы получим 𝛉_1 ~= .4. Из этого уравнения видно, что наше столбцовое пространство нашей матрицы дизайна имеет размерность 1 и будет линией на плоскости x-y. Мы могли бы визуализировать это примерно так. Здесь ŷ — наше наилучшее приближение, а 𝘆 со стрелкой вверху — целевой вектор.

Как мы видим, наше целевое значение — это некоторая точка в трехмерном пространстве, которую не может достичь наша ограниченная линия в плоскости x-y. Таким образом, мы используем теорему о наилучшем приближении, чтобы найти наше решение методом наименьших квадратов, ŷ которое ортогонально пространству решений, как мы описали ранее. Назовем это решение ŷ_1. Теперь подумайте, добавим ли мы функцию. Давайте рассмотрим следующие два возможных новых матричных уравнения:

Если мы решим уравнение (2) с помощью метода наименьших квадратов, мы получим 𝛉_1 ~= -0,3 и 𝛉_2 ~= 0,7, но для уравнения (3) мы получим 𝛉_1 ~= 0,4 и 𝛉_2 ~= 0,7. Помните, что наше исходное значение для уравнения (1) было 𝛉_1 ~= 0,4. Здесь мы видим, что наше значение 𝛉_1 остается неизменным при решении уравнения (3), но это не так для уравнения (2)… Почему это так?

Получается, что значение признаков не меняется при решении уравнения (3), потому что новый столбец признаков ортогонален исходному признаку. Чтобы понять почему, давайте попробуем визуализировать пространство решений. В этом конкретном случае мы получим что-то вроде этого для обоих уравнений:

Рассмотрим здесь предыдущую линию в плоскости xy, растянутую в трехмерное пространство, так что мы получаем это двумерное плоское пространство решений. Мы можем интуитивно увидеть, как наше целевое значение 𝘆 ближе к ближайшей точке на этой плоскости, чем к ближайшей точке на линии в плоскости x-y из предыдущего изображения. Мы также можем видеть здесь, как, если новый объект ортогонален исходному вектору, создавшему линию на плоскости xy, новое решение ŷ будет достигнуто простым под прямым углом от исходного решения ŷ_1 с ортогональным вектором признаков! Однако, если новая функция не является ортогональной, необходимо внести некоторые коррективы в ŷ_1, чтобы достичь нашего нового решения ŷ. (в этом случае ŷ_1должно быть меньше)

Теперь можно спросить, подразумевает ли эта ортогональность наших данных что-либо о корреляции наших различных характеристик. Оказывается, о статистическом распределении данных говорить не приходится. В этом случае ортогональность — это просто геометрическое свойство, а не подразумевающее что-либо относительно отношений независимости объектов. Поэтому, когда мы замечаем какое-либо 𝛉_iсмещение при добавлении функции, мы не обязательно предполагаем, что это связано с корреляцией данных (хотя это может быть), но, скорее всего, просто из-за естественных ограничений Линейная алгебра.

Анализ нашей подгонки

Теперь, когда мы увидели, как мы можем вывести нормальное уравнение, и получили некоторое представление о наших подходящих параметрах, давайте попробуем использовать его в наборе данных и посмотрим, что наша новая интуиция может сказать нам о результатах. Давайте рассмотрим пример, реализованный на Python, и визуализируем наше соответствие с одной и двумя функциями.

Мы будем использовать следующие библиотеки:

import numpy as np 
import pandas as pd 
import copy
import matplotlib.pyplot as plt 
import seaborn as sns
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error

Мы будем запускать линейные регрессии для набора данных «Реклама» из Kaggle. [3] В этом наборе данных мы пытаемся спрогнозировать объемы продаж из разных рекламных бюджетов в разных средах. Данные дают нам доступ к трем различным наборам рекламных бюджетов, которые мы можем использовать в качестве функций («ТВ», «Радио» и «Газета»). Удивительно, изображение 1 (вверху этого поста) было получено, когда мы использовали только «ТВ» в качестве функции и выполнили регрессию. Ради интереса, давайте начнем с переменной «Радио» и посмотрим, что получится. Мы также будем реализовывать нормальное уравнение с нуля вместо того, чтобы использовать библиотеку sklearn только для практики. Для нашей модели мы будем использовать тестовое разделение поездов 70/30.

#read in the training set
df = pd.read_csv("~/Desktop/least_squares/data_ad/Advertising.csv")
df.to_csv('orig_data.csv')
#set the train/test split size
train_size = .7
test_size = 1-train_size
#pick the feature(s)
#feature_labels = ['TV', 'Radio', 'Newspaper']
feature_labels = ['Radio']
target_label = ['Sales']
features = df[feature_labels]
target = df[target_label]
features_np = features.to_numpy()
target_np = target.to_numpy()
#now split into train/test
total_samples = target_np.shape[0]
train_samples = int(train_size*total_samples)
test_samples = total_samples - train_samples
features_train = features_np[:train_samples]
target_train = target_np[:train_samples]
features_test = features_np[train_samples:]
target_test = target_np[train_samples:]
#run the alg w/ the normal equations
X = features_train
y = target_train
theta = (np.linalg.inv((X.T)@X))@((X.T)@y)
X_test = features_test
y_test = target_test
y_preds_train = (X@theta)
y_preds_test = (X_test@theta)
train_error = mean_squared_error(y, y_preds_train)
test_error = mean_squared_error(y_test, y_preds_test)
#plot this
plt.figure(1)
plt.scatter(X[:, 0], y[:, 0], color='blue')
plt.plot(X[:, 0], y_preds_train[:, 0], color='red', label='LR Prediction')
plt.legend()
plt.xlabel('Radio Budget')
plt.ylabel('Sales')
plt.show()

Выполнение этого дает нам следующий график:

Ура работает! Печать нашей потери или MSE (среднеквадратичной ошибки) здесь дает нам значение ~42,14 для обучающих данных и ~45,74 для тестовых данных. Если мы теперь повторно запустим код, используя все 3 доступные функции, мы увидим, что мы получаем MSE ~4,23 для наших обучающих данных и ~3,67для наших тестовых данных ! Поэтому очевидно, что добавление функций имеет большое значение. Можем ли мы использовать нашу новую интуицию, чтобы подумать о том, как сделать эти цифры еще лучше? В следующих нескольких разделах мы рассмотрим эти вопросы.

Почему бы не добавить больше функций? (Компромисс смещения/дисперсии)

Эти игрушечные примеры могут заставить нас задуматься. «Если я продолжу добавлять функции, могу ли я постоянно гарантировать, что мои потери уменьшатся?» Или по-другому: «Если мы найдем способ повысить ранг нашей матрицы дизайна, разве это не уменьшит наши потери при обучении и не даст нам лучшую модель?» Хотя этот вопрос может показаться наивным для тех из вас, кто знаком с машинным обучением, позвольте мне рассмотреть его немного подробнее, чтобы укрепить некоторые из наших интуитивных представлений.

Давайте снова рассмотрим наш пример с рекламным набором данных выше. Набор данных предоставляет нам три функции («ТВ», «Радио» и «Газета»), которые представляют относительный бюджет для каждого рекламного носителя. Предположим пока, что эти признаки независимы. Давайте теперь рассмотрим пару экспериментов:

Случайные функции

Сначала мы рассмотрим идею добавления случайных функций в нашу матрицу дизайна в надежде повысить ранг нашей матрицы и уменьшить потери при обучении. Чтобы увидеть, как это влияет на нашу ошибку обучения и теста, давайте попробуем подметать количество случайных признаков от 1 до 100, чтобы увидеть, как это влияет на наши результаты. Чтобы добавить случайную функцию, мы просто создадим нормально распределенный случайный вектор и добавим его в нашу матрицу дизайна. Хотя мы не делаем этого здесь, мы также можем убедиться, что ранг матрицы дизайна увеличивается по мере добавления случайных функций. Для этого мы используем следующий код, используя библиотеку sklearn LinearRegression:

num_random_features = 100
features_rand = copy.deepcopy(features_np)
train_errors = np.zeros((num_random_features))
test_errors = np.zeros((num_random_features))
for i in range(num_random_features):
 rand = np.random.normal(0, 1, total_samples).reshape(total_samples, 1)
 features_rand = np.concatenate((features_rand, rand), axis=1)
features_train = features_rand[:train_samples]
 features_test = features_rand[train_samples:]
lr = LinearRegression(fit_intercept=False)
 lr.fit(features_train, target_train)
target_train_preds = lr.predict(features_train)
 target_test_preds = lr.predict(features_test)
train_error = mean_squared_error(target_train, target_train_preds)
 test_error = mean_squared_error(target_test, target_test_preds)
train_errors[i] = train_error
 test_errors[i] = test_error
#plot this
x = np.linspace(0, num_random_features, num=num_random_features)
plt.figure(2)
plt.plot(x, train_errors, color='red', label='train error')
plt.plot(x, test_errors, color='blue', label='test error')
plt.legend()
plt.xlabel('# Random Features')
plt.ylabel('Mean Squared Error')
plt.show()

Вывод дает нам этот график:

Здесь мы видим, что действительно наша ошибка поезда в основном монотонно уменьшается по мере нашего увеличения! Но, как и следовало ожидать, наша тестовая ошибка зашкаливает. Здесь мы быстро сталкиваемся с проблемой высокой дисперсии или переобучения, что связано с наличием большого количества функций. Мы также заметили, что случайные функции в основном не помогают нашей ошибке теста, а только помогают нашей ошибке обучения из-за увеличения ранга матрицы дизайна. Делая это, мы увеличиваем выразительность нашей модели или уменьшаем предвзятость, но из нашей высокой ошибки теста мы видим, что эти случайные функции не добавили никаких дополнительных базовых понимания в нашей модели. Это быстро подтверждает ключевой момент, что наша цель состоит не в том, чтобы минимизировать ошибку поезда, а в том, чтобы минимизировать ошибку теста.

Из этого следует, что добавление случайных функций — не лучшая идея. Но что, если нам нужно будет разработать новые функции на основе уже существующих?

Специализированные функции

Здесь мы рассматриваем создание некоторых простых функций второго и третьего порядка путем перемножения комбинаций наших исходных функций. Интуитивно мы могли бы подумать, что «если корреляция между этими функциями сама коррелирует с целью, это могло бы улучшить нашу модель». Мы просматриваем 14 новых инженерных функций с помощью следующего кода. В тандеме мы также переобучаем 14 случайных функций:

#add engineered feature
features_rand = copy.deepcopy(features_np)
features_eng = copy.deepcopy(features_np)
num_eng_features = 14
eng_noise = .001
eng_features = np.zeros((total_samples, num_eng_features))
eng_features[:, 0] = features_np[:, 0]*features_np[:, 0] 
eng_features[:, 1] = features_np[:, 1]*features_np[:, 1] 
eng_features[:, 2] = features_np[:, 2]*features_np[:, 2]
eng_features[:, 3] = features_np[:, 0]*features_np[:, 1] 
eng_features[:, 4] = features_np[:, 0]*features_np[:, 2] 
eng_features[:, 5] = features_np[:, 1]*features_np[:, 2]
eng_features[:, 6] = (features_np[:, 0]*features_np[:, 1]*features_np[:, 2])
eng_features[:, 7] = features_np[:, 0]*features_np[:, 0]*features_np[:, 0]
eng_features[:, 8] = features_np[:, 1]*features_np[:, 1]*features_np[:, 1]
eng_features[:, 9] = features_np[:, 2]*features_np[:, 2]*features_np[:, 2]
eng_features[:, 10] = features_np[:, 0]*features_np[:, 1]*features_np[:, 1]
eng_features[:, 11] = features_np[:, 0]*features_np[:, 2]*features_np[:, 2]
eng_features[:, 12] = features_np[:, 1]*features_np[:, 2]*features_np[:, 2]
eng_features[:, 13] = (features_np[:, 0]*features_np[:, 1]*features_np[:, 2])
train_rand_errors = np.zeros((num_eng_features))
test_rand_errors = np.zeros((num_eng_features))
train_eng_errors = np.zeros((num_eng_features))
test_eng_errors = np.zeros((num_eng_features))
for i in range(num_eng_features):
 rand = np.random.normal(0, 1, total_samples).reshape(total_samples, 1)
 features_rand = np.concatenate((features_rand, rand), axis=1)
eng = eng_features[:, i].reshape(total_samples, 1)
 features_eng = np.concatenate((features_eng, eng), axis=1)
features_rand_train = features_rand[:train_samples]
 features_rand_test = features_rand[train_samples:]
features_eng_train = features_eng[:train_samples]
 features_eng_test = features_eng[train_samples:]
#fit and test rand
 lr = LinearRegression(fit_intercept=False)
 lr.fit(features_rand_train, target_train)
target_train_rand_preds = lr.predict(features_rand_train)
 target_test_rand_preds = lr.predict(features_rand_test)
train_error = mean_squared_error(target_train, target_train_rand_preds)
 test_error = mean_squared_error(target_test, target_test_rand_preds)
train_rand_errors[i] = train_error
 test_rand_errors[i] = test_error
#fit and test eng
 lr = LinearRegression(fit_intercept=False)
 lr.fit(features_eng_train, target_train)
target_train_eng_preds = lr.predict(features_eng_train)
 target_test_eng_preds = lr.predict(features_eng_test)
train_error = mean_squared_error(target_train, target_train_eng_preds)
 test_error = mean_squared_error(target_test, target_test_eng_preds)
train_eng_errors[i] = train_error
 test_eng_errors[i] = test_error
#plot this
x = np.linspace(0, num_eng_features, num=num_eng_features)
plt.figure(3)
plt.plot(x, train_rand_errors, color='red', label='rand train error')
plt.plot(x, test_rand_errors, color='blue', label='rand test error')
plt.plot(x, train_eng_errors, color='cyan', label='eng train error')
plt.plot(x, test_eng_errors, color='green', label='eng test error')
plt.legend()
plt.xlabel('# Features')
plt.ylabel('Mean Squared Error')
plt.show()

Отсюда получаем такой сравнительный график:

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

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

Конечно, такие слова, как понимание и сигнал, несколько расплывчаты, поэтому давайте углубимся в наш мыслительный процесс при выборе функций.

Зависимые и независимые функции

Ранее мы видели, как наши потери при обучении и потери при тестировании реагируют по-разному, поскольку мы продолжаем добавлять больше функций, и в целом, как мы можем продолжать уменьшать нашу ошибку обучения, но рискуем получить более высокую ошибку тестирования, если мы переобучаем. Однако в предыдущем разделе мы рассмотрели только то, что можно было бы назвать «искусственными» функциями. Что, если бы мы добавили только функции, «естественно» взятые из реального мира, которые, как мы ожидаем, будут иметь низкую корреляцию друг с другом? Должны ли мы чувствовать себя лучше, постоянно добавляя их по сравнению с инженерной функцией?

Ответ: вероятно. Хотя мы можем обратиться к деталям более глубокого статистического анализа линейной регрессии для ответа здесь (что мы пока не будем делать), мы можем вместо этого проработать его, рассмотрев некоторые общие принципы. Давайте рассмотрим некоторые части, на которые влияет добавление других функций:

  • Степени свободы: еще один способ указать «больше параметров». Когда мы добавили наши случайные функции в предыдущем разделе, мы в первую очередь изменили эту часть. Как правило, эта часть уменьшит предвзятость, но увеличит дисперсию.
  • Коллинеарность функций: описывает корреляцию между функциями. Если мы посмотрим и сравним ковариационные матрицы трех моделей, то увидим, что случайные признаки наименее коррелированы, а сконструированные — больше всего. Мы хотим ограничить степень коллинеарности признаков, так как это увеличивает дисперсию наших предикторов, что может привести к переобучению.
  • Корреляция с целевой переменной: «сигнал» системы. В идеале мы хотим найти несколько переменных, которые независимо коррелируют с целевой переменной. Мы можем предположить, что наши случайные признаки практически не связаны с целью. Неясно, будет ли инженерная функция в меньшей или большей степени коррелировать с целевой переменной, чем новая функция, но в лучшем случае она, скорее всего, будет давать менее исходный сигнал.

В целом, мы можем начать понимать, что добавление «новой» функции, скорее всего, даст нам максимальную отдачу от затраченных средств. Он имеет ту же стоимость степени свободы, что и два других варианта, с меньшей вероятностью будет коллинеарным с другим признаком и имеет больше возможностей для новой способности прогнозировать целевую переменную. Тем не менее, он по-прежнему подвержен тому же компромиссу между ошибками и различиями, который мы видели с другими функциями, и поэтому он по-прежнему подвергается тщательному анализу в процессе проектирования.

Всегда ли больше данных лучше?

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

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

Вывод

Ничего себе, так что мы прошли довольно много. Давайте вспомним некоторые основные моменты:

  • Метод наименьших квадратов можно решить аналитически с помощью нормального уравнения. Нормальное уравнение может быть получено либо с помощью оптимизации, либо с помощью геометрического угла.
  • В целом мы предполагаем, что при использовании линейной регрессии признаки практически не коррелируют друг с другом. При добавлении функций мы можем увидеть, как наши ранее подобранные параметры перемещаются, но это может быть связано с базовой геометрией линейной алгебры, а не со скрытыми корреляциями между функциями.
  • Мы можем постоянно улучшать наши потери при обучении, добавляя функции, хотя мы быстро начинаем переобуваться. Функции нужно добавлять осторожно, чтобы найти оптимальное соотношение смещение/дисперсия.

Надеюсь, это было полезно! Пожалуйста, оставьте комментарий, если у вас есть какие-либо предложения или исправления. Спасибо!

Связанный репозиторий кода:

https://github.com/surajmenon72/least_squares

Использованная литература:

[1] Примечания к линейным моделям Stanford CS229

[2] UConn Math 3511 Orthogonality and Least Squares Notes

[3] Рекламный набор данных Kaggle

[4] Stanford EE263 Метод наименьших квадратов. Примечания