Углубленное изучении генерации ошибок для линейной регрессии

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

Вам может быть непонятной одна вещь которую мы рассматривали в наших предыдущих статьях. Если мы можем в качестве входных данных взять x2, то что мешает нам взять x3, x4 или даже x5 и тем самым ещё более уменьшить погрешность?

Ведь вспомните из курса высшей математики – бесконечный ряд Тейлора может аппроксимировать любую функцию. Так до какой же степени мы можем добавлять все эти иксы?

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

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

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

Обычно мы говорим о качестве модели, имея в виду, насколько хорошо модель обобщает данные, которых ранее не было, и говорим об ошибке обобщения. Как мы можем улучшить нашу модель, моделируя ситуацию, требующую обобщённых данных?

Ну, например, использовать данные, где 80% – это учебные данные для построения модели, а остальные 20% – проверочные.

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

Итак, что происходит, когда вы настолько усложняете модель, что учебные данные показывают всё лучший и лучший результат? В некоторый момент проверка показывает, что модель начинает отклоняться от реальных значений. Это тот момент, когда вы должны сказать себе: «Стоп! Моя модель уже достаточно сложна». Лично я советую проверять свою модель на реальных данных, чтобы оценить, какие кривые получите вы. Иначе это будет слишком rate optimistic approach.

Демонстрация обобщения и переобучения в коде

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

И так просто пробежимся по коду без его написания, так как нам важнее запустить его.

Для начала создадим вводные данные – синусоиду от 0 до 6π. Если вы не знаете, как выглядит синусоида, мы изобразим её графически.

import numpy as np

import matplotlib.pyplot as plt

N = 100

X = np.linspace(0, 6*np.pi, N)

Y = np.sin(X)

plt.plot(X, Y)

plt.show()

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

def make_poly(X, deg):

n = len(X)

data = [np.ones(n)]

для d in xrange(deg):

data.append(X**(d+1))

return np.vstack(data).T

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

def fit(X, Y):

return np.linalg.solve(X.T.dot(X), X.T.dot(Y))

Следующая функция fit_and_display. В качестве аргументов она получает набор данных X и Y, а также аргументы sample и deg. Аргумент sample даёт нам номер наблюдения с X и Y для формирования  учебного набора. Мы используем эти данные, чтобы найти лучшие коэффициенты для многочлена степени, указанной в переменной deg. Затем мы изобразим графически этот многочлен в дополнение к уже построенному начальному графику и графику учебных данных. Всё это даст нам представление о том, насколько хорошо многочлен соответствует учебным данным и насколько хорошо он показывает синусоиду.

def fit_and_display (X, Y, sample, deg):

N = len(X)

train_idx = np.random.choice(N, sample)

Xtrain = X[train_idx]

Ytrain = Y[train_idx]

plt.scatter(Xtrain, Ytrain)

plt.show()

Xtrain_poly = make_poly(Xtrain, deg)

w = fit(Xtrain_poly, Ytrain)

X_poly = make_poly(X, deg)

Y_hat = X_poly.dot(w)

plt.plot(X, Y)

plt.plot(X, Y_hat)

plt.scatter(Xtrain, Ytrain)

plt.title(‘’deg = %d’’ % deg)

plt.show

Следующее – проверка функции с многочленами пятой, шестой, седьмой, восьмой и девятой степени.

for deg in (5, 6, 7, 8, 9):

fit_and_display(X, Y, 10, deg)

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

def get_mse(Y, Yhat):

d = Y – Yhat

return d.dot(d) / len(d)

Вы увидите, что некоторое время проверочный набор данных будут совпадать с учебным, но в конце концов резко с ним разойдётся, потому что не сможет адекватно описывать новые входные данные.

Следующая функция plot_train_vs_test_curves именно это и показывает. Она берёт случайный набор данных x и y, составляет из них учебный набор данных, а также составляет проверочный набор из оставшихся данных. Затем она строит многочлены со степенями от 1 до 20 и рисует кривые отклонений для учебного и проверочного наборов. Мы также построим кривую учебного набора, чтобы вы видели, как отклонение стремится к нулю и там и остаётся.

def plot_train_vs_test_curves(X, Y, sample=20, max_deg=20):

N = len(X)

train_idx = np.random.choise(N, sample)

Xtrain = X[train_idx]

Ytrain = Y[train_idx]

test_idx = [idx for idx in xrange(N) if idx not in train_idx]

Xtest = X[test_idx]

Ytest = Y[test_idx]

mse_trains = []

mse_tests = []

for deg in xrange(max_deg+1):

Xtrain_poly = make_poly(Xtrain, deg)

w = fit(Xtrain_poly, Ytrain)

Yhat_train = Xtrain_poly.dot(w)

Итак, запустим код и посмотрим, что получится.

Видим синусоиду,

далее набор случайных данных из 10 точек,

далее многочлен пятой степени.

Вы видите, что он проходит не через все точки.

Далее ещё один случайный набор из 10 точек,

и многочлен шестой степени.

Обратите внимание, что он проходит через все точки, но выглядит просто отвратительно в тех областях, где нет точек данных. Увеличив масштаб, мы увидим, что в некоторых областях результат ужасен, хотя график проходит точно через все точки.

И ещё один случайный набор, и снова у нас хорошее совпадение по точкам.

Итак, у нас есть области, где обобщение получается очень хорошим, но в тех, где точек нет, кривые очень расходятся.

Очередной набор данных и многочлен восьмой степени. Вы можете видеть, что построение очень хорошо в правой части, где много точек, но неудовлетворительно в левой части, где точек мало.

Очередные 10 точек

и многочлен девятой степени.

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

Далее мы видим среднеквадратическое отклонение для учебного и проверочного наборов.

Как мы видим, для учебного набора кривая отклонения намного ближе к нулю.

На отдельном графике учебных данных кривая, как можно легко убедиться, всегда падает до нуля.

Мы приходим к следующим интересным выводам.

Как построить  успешную модель?

Мы советуем запустить программу несколько раз для повторения эксперимента. Попробуйте запустить программу самостоятельно с разным количеством точек данных и разными степенями многочлена. Чем больше попыток вы сделаете, тем лучший результат получите. Но потом вы заметите, что большая степень многочлена вовсе не гарантирует построение успешной модели.

На самом деле всё зависит от количества учебных данных. Если выборка для обучения модели действительно репрезентативна по отношению к остальным данным, соответствие будет очень хорошим несмотря ни на что.

Применительно к нашему примеру с синусоидой, если учебные данные распределены так, что мы получаем равномерно распределённый набор точек для всех значений x, то наш многочлен хорошо опишет кривую. Это потому, что наш учебный набор показывает хорошую репрезентативность.

Если же наш учебный набор точек сгруппирован в нескольких местах, вы увидите, как многочлен резко расходится с кривой в областях, где не было точек входных данных. Это потому, что учебный набор был нерепрезентативен.

Какова же мораль?

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

Вероятностная интерпретация квадратичной ошибки

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

Напомню, она определяется как сумма квадратов разниц между фактическим значением yi и прогнозируемым значением ŷi:

E = \sum_{i=1}^{N} (y_i - \widehat y_i)^2.

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

Что мы имеем в виду, говоря о максимальной вероятности?

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

Но вот о чём вы, вероятно, не знаете, что есть более методически правильный способ получить ответ.

\mu = \frac {1} {N}\sum_{i=1}^{N} x_i.

Предположим, что мы хотим измерить истинное среднее значение параметра при гауссовом распределении. Напомню, что гауссово распределение определяется двумя параметрами: средним значением и дисперсией. Поскольку все наши xi расположены согласно распределения Гаусса, то вероятность для каждого отдельного xi можно вычислить по плотности  распределение вероятностей, описываемой функции Гаусса.

p (x_i) = \frac {1} {\sqrt {2\pi\sigma^2}} e - \frac {1(x_i - \mu)^2} {2 \sigma^2}.

Поскольку известно, что рост учащихся независим друг от друга и одинаково распределён, их общую вероятность мы можем записать как произведение всех этих величин.

p (x_i, x_2, ... , x_N) = \prod_{i=1}^{N}\frac {1} {\sqrt {2\pi\sigma^2}} e - \frac {1(x_i - \mu)^2} {2 \sigma^2}.

Мы можем записать это уравнение с использованием функции правдоподобия. Функция правдоподобия – это вероятность появления данных X, заданная параметром μ.

p (X|\mu) = p(x_1, x_2, ... , x_N) = \prod_{i=1}^{N}\frac {1} {\sqrt {2\pi\sigma^2}} e - \frac {1(x_i - \mu)^2} {2 \sigma^2}.

Не забывайте, что найти нам нужно именно μ. Если говорить точнее, нам нужно найти такое значение μ, чтобы правдоподобие было максимальным. Другими словами, нам нужно найти такое значение μ, чтобы собранные нами данные находились на распределении, заданным этим значением μ.

Итак, как же нам найти μ?

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

Почему именно так? Если брать производную непосредственно из функции правдоподобия, то получившееся выражение будет очень сложно для решения. Это работает потому, что функция логарифма является монотонно растущей. Иными словами, если A>B, то и log A > log B. Если вы нам не верите, можете проверить это с помощью калькулятора.

Итак, первым делом найдём логарифм функции правдоподобия (обозначим его l).

l = log \prod_{i=1}^{N}\frac {1} {\sqrt {2\pi\sigma^2}} e - \frac {1(x_i - \mu)^2} {2 \sigma^2}.

Мы знаем, что логарифм произведения равен сумме логарифмов. Это позволит нам упростить выражение.

l = \sum_{i=1}^{N} [ - \frac {1} {2} log (2\pi\sigma^2) - \frac {1}{2}\frac {(x_i - \mu)^2} {\sigma^2}].

Далее берём производную и приравниваем её к нулю.

\frac {dl} {d\mu} = -\sum_{i=1}^{N} \frac {(x_i - \mu)} {\sigma^2} = 0.

Решив это уравнение и найдя μ, мы обнаружим нечто знакомое – сумму наших xi, делённую на количество наблюдений N.

 \mu = \frac {1}{N} \sum_{i=1}^{N} x_i.

Ключевым моментом во всём этом упражнении является вид логарифма функции правдоподобия.

l = \sum_{i=1}^{N} [-\frac {1} {2} log(2\pi\sigma^2)-\frac {1} {2}\frac {(x_i - \mu)^2} {\sigma^2}]

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

Если мы упростим выражение, убрав все не относящиеся к делу части, мы получим просто задачу нахождения максимума отрицательной суммы квадратов x_i и разницы \mu.

equivalent \;l = \sum_{i=1}^{N} (x_i - \mu)^2

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

l = - \sum_{i=1} ^ {N} (x_i - \mu)^2.

 E = \sum_{i=1}^{N} (y_i - \widehat y_i)^2.

Разные знаки в формулах объясняются тем, что ранее мы пытались найти минимум функции, а теперь – максимум.

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

y\sim N(w^Tx, \sigma^2).

Это можно также переписать в виде

y=w^Tx+\varepsilon, \varepsilon\sim N(0, \sigma^2),

где ε – гауссов распределённый шум с нулевым средним значением и произвольной дисперсией

Ловушка фиктивной переменной

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

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

Правда, поскольку одно из значений переменной закодировано нулями, мы не можем напрямую видеть её влиянияе на конечный результат.

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

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

Мы не будем углубляться в линейную алгебру, чтобы детально это объяснить, но факт остаётся фактом: взять величину, обратную

XTX оказывается невозможным. Иногда такую ситуацию и называют ловушкой фиктивной переменной.

Есть несколько способов обойти эту ловушку.

  • Простейший из них – это, конечно, использовать К-1-кодирование вместо прямого, но это неинтересно.
  • Второй способ – удалить столбец из единиц, чтобы избежать смещения, а линейная комбинация столбцов не оказывалась равной другому столбцу.

Также можно использовать уже обсуждавшуюся L2-регуляризацию. Как можно видеть из решения для нахождения весовых коэффициентов, в этом случае нам нет необходимости находить величину, обратную XTX

  • потому что мы добавляем к ней ещё один член, а именно λI. Таким образом, мы избавляемся от необходимости работать с единичной матрицей.
  • И последний метод, называемый градиентным спуском, будет обсуждаться далее в связи с L1-регуляризацией. Это прекраснейший метод, но и остальные обсуждаемые нами вещи также имеют важное значение. Градиентный спуск применяется во всех областях глубокого обучения, а так как линейная регрессия является в значительной степени единственным способом для нахождения весовых коэффициентов, то полезно упомянуть градиентный спуск прямо сейчас. Тем более, что это наиболее общий метод, который может использоваться и для всех последующих моделей.

Отметим, что инвертирование единичной матрицы в матричном счислении является операцией, эквивалентной делению на нуль. Добавление в матрицу λI эквивалентно добавлению небольшого числа λ в знаменатель, чтобы он перестал быть равным нулю.

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

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

Андрей Никитенко
Андрей Никитенко
Задать вопрос эксперту
Понравилась статья? Поделить с друзьями:
Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!:

Share via
Copy link