Понимание регрессии опорных векторов (SVR) - PullRequest
0 голосов
/ 02 сентября 2018

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

enter image description here

enter image description here

Тогда у нас есть это объяснение. This can be described by introducing (non-negative) slack variables , to measure the deviation of training samples outside -insensitive zone. Я понимаю эту ошибку, вне трубки, но не знаю, как мы можем использовать это в оптимизации. Может ли кто-нибудь объяснить это?

enter image description here

enter image description here


В местном источнике. Я пытаюсь достичь очень простого решения по оптимизации, без библиотек. Это то, что у меня есть для потери функции.

import numpy as np

# Kernel func, linear by default
def hypothesis(x, weight, k=None):
    k = k if k else lambda z : z
    k_x = np.vectorize(k)(x)
    return np.dot(k_x, np.transpose(weight))

.......

import math

def boundary_loss(x, y, weight, epsilon):
    prediction = hypothesis(x, weight)

    scatter = np.absolute(
        np.transpose(y) - prediction)
    bound = lambda z: z \
        if z >= epsilon else 0

    return np.sum(np.vectorize(bound)(scatter))

1 Ответ

0 голосов
/ 10 сентября 2018

Сначала давайте посмотрим на целевую функцию. Первое слагаемое 1/2 * w^2 (если бы этот сайт имел поддержку LaTeX, но этого будет достаточно) соотносится с полем SVM. Статья, на которую вы ссылались, по моему мнению, не очень хорошо объясняет это и называет этот термин описанием «сложности модели», но, возможно, это не лучший способ объяснить это. Сведение к минимуму этого термина максимизирует маржу (хотя она все еще хорошо представляет данные), что является основной целью использования регрессии SVM.

Предупреждение, Math Heavy Объяснение: Причина этого в том, что при максимизации поля вы хотите найти «самые дальние» точки выброса прямо на поле и минимизировать его расстояние. Пусть эта самая дальняя точка будет x_n. Мы хотим найти его евклидово расстояние d от плоскости f(w, x) = 0, которое я перепишу как w^T * x + b = 0 (где w^T - это просто транспонирование матрицы весов, чтобы мы могли умножить два). Чтобы найти расстояние, давайте сначала нормализуем плоскость так, чтобы |w^T * x_n + b| = epsilon, которую мы можем сделать WLOG, так как w все еще в состоянии сформировать все возможные плоскости вида w^T * x + b= 0. Затем отметим, что w перпендикулярно плоскости. Это очевидно, если вы много работали с плоскостями (особенно в векторном исчислении), но это можно доказать, выбрав две точки на плоскости x_1 и x_2, а затем заметив, что w^T * x_1 + b = 0 и w^T * x_2 + b = 0. Вычитая два уравнения, получаем w^T(x_1 - x_2) = 0. Поскольку x_1 - x_2 - это просто любой вектор строго на плоскости, а его скалярное произведение с w равно 0, то мы знаем, что w перпендикулярно плоскости. Наконец, чтобы фактически вычислить расстояние между x_n и плоскостью, мы берем вектор, образованный x_n', и некоторую точку на плоскости x' (векторы будут тогда x_n - x', и проецируя его на вектор w. Делая это, мы получаем d = |w * (x_n - x') / |w||, который мы можем переписать как d = (1 / |w|) * | w^T * x_n - w^T x'|, а затем сложить и вычесть b внутрь, чтобы получить d = (1 / |w|) * | w^T * x_n + b - w^T * x' - b|. Обратите внимание, что w^T * x_n + b равно epsilon (из нашего нормализация выше), и что w^T * x' + b равно 0, поскольку это просто точка на нашей плоскости. Таким образом, d = epsilon / |w|. Обратите внимание, что максимизация этого расстояния при условии нашего ограничения на нахождение x_n и наличия |w^T * x_n + b| = epsilon является трудная проблема оптимизации. Мы можем реструктурировать эту проблему оптимизации как минимизацию 1/2 * w^T * w с учетом первых двух ограничений на картинке, которую вы прикрепили, то есть |y_i - f(x_i, w)| <= epsilon. Вы можете подумать, что я забыл слабые переменные, и это Это правда, но когда мы сосредоточимся только на этом термине и проигнорируем второе, мы пока проигнорируем слабые переменные, я вернусь к ним позже. эти две оптимизации эквивалентны, не очевидно, но основная причина лежит в границах дискриминации, о которых вы можете прочитать больше (это намного больше математики, если честно, я не думаю, что этот ответ нуждается в большем количестве). Затем обратите внимание, что минимизация 1/2 * w^T * w - это то же самое, что минимизация 1/2 * |w|^2, что является желаемым результатом, на который мы надеялись. Конец тяжелой математики

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

Итак, мы вводим второй член. Чтобы снизить допустимый размер поля до разумного размера, вводятся переменные слабины (я буду называть их p и p*, потому что я не хочу каждый раз печатать "psi"). Эти слабые переменные будут игнорировать все на полях, то есть те точки, которые не наносят ущерба цели, и те, которые являются «правильными» с точки зрения их статуса регрессии. Однако точки вне поля являются выбросами, они плохо отражаются на регрессии, поэтому мы наказываем их просто за существующие. Функция слабой ошибки, которая дана там, относительно проста для понимания, она просто складывает ошибку слабины каждой точки (p_i + p*_i) для i = 1,...,N, а затем умножается на постоянную модуляции C, которая определяет относительную важность два условия. Низкое значение C означает, что у нас все в порядке с выбросами, поэтому запас будет уменьшаться и будет создаваться больше выбросов. Высокое значение C указывает на то, что мы очень заботимся о том, чтобы не провисать, поэтому запас будет увеличен, чтобы учесть эти выбросы за счет менее полного представления общих данных.

Несколько замечаний по поводу p и p*. Во-первых, обратите внимание, что они оба всегда >= 0. Ограничение на вашей картинке показывает это, но оно также интуитивно понятно, поскольку провисание всегда должно приводить к ошибке, поэтому оно является положительным. Во-вторых, обратите внимание, что если p > 0, то p* = 0 и наоборот как выброс могут быть только на одной стороне поля. Наконец, все точки внутри поля будут иметь p и p* равными 0, так как они хороши там, где они есть, и, следовательно, не способствуют потере.

Обратите внимание, что с введением слабых переменных, если у вас есть какие-либо выбросы, вам не нужно условие от первого слагаемого, то есть |w^T * x_n + b| = epsilon, так как x_n будет этим выбросом, и ваше целое модель будет облажаться. Таким образом, мы разрешаем изменить ограничение на |w^T * x_n + b| = epsilon + (p + p*). При переводе на новое ограничение оптимизации мы получаем полное ограничение из картинки, которую вы прикрепили, то есть |y_i - f(x_i, w)| <= epsilon + p + p*. (Здесь я объединил два уравнения в одно, но вы можете переписать их так, как показано на рисунке, и это будет то же самое).

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


Если я правильно понимаю вопрос, вы также хотите, чтобы код вычислял эту функцию цели / потери, что, я думаю, не так уж плохо. Я не проверял это (пока), но я думаю, что это должно быть то, что вы хотите.

# Function for calculating the error/loss for a SVM. I assume that:
#  - 'x' is 2d array representing the vectors of the data points
#  - 'y' is an array representing the values each vector actually gives
#  - 'weights' is an array of weights that we tune for the regression
#  - 'epsilon' is a scalar representing the breadth of our margin.
def optimization_objective(x, y, weights, epsilon):
    # Calculates first term of objective (note that norm^2 = dot product)
    margin_term = np.dot(weight, weight) / 2

    # Now calculate second term of objective. First get the sum of slacks.
    slack_sum = 0
    for i in range(len(x)): # For each observation
        # First find the absolute distance between expected and observed.
        diff = abs(hypothesis(x[i]) - y[i])
        # Now subtract epsilon
        diff -= epsilon
        # If diff is still more than 0, then it is an 'outlier' and will have slack.
        slack = max(0, diff)
        # Add it to the slack sum
        slack_sum += slack

    # Now we have the slack_sum, so then multiply by C (I picked this as 1 aribtrarily)
    C = 1
    slack_term = C * slack_sum

    # Now, simply return the sum of the two terms, and we are done.
    return margin_term + slack_term

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

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

Наконец, я хотел бы отметить, что большую часть этой информации я получил из заметок, которые я сделал в моем классе ML, который я взял в прошлом году, и профессор (д-р Абу-Мостафа) оказал мне большую помощь в изучении материала. , Лекции для этого класса онлайн (от того же профессора), и соответствующие темы для этой темы здесь и здесь (хотя, по моему очень предвзятому мнению, вы должны смотреть все лекции они очень помогли). Оставьте комментарий / вопрос, если вам нужно что-то прояснить или если вы думаете, что я где-то допустил ошибку. Если вы все еще не понимаете, я могу попробовать отредактировать свой ответ, чтобы сделать его более понятным. Надеюсь, это поможет!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...