Недавно на Хабре появилась публикация про алгоритм Хо-Кашьяпа (Ho-Kashyap procedure, он же — алгоритм НСКО, наименьшей среднеквадратичной ошибки). Мне она показалась не очень понятной и я решил разобраться в теме сам. Выяснилось, что в русскоязычном интернете тема не очень хорошо разобрана, поэтому я решил оформить статью по итогам поисков.
Несмотря на бум нейросетей в машинном обучении, алгоритмы линейной классификации остаются гораздо более простыми в использовании и интерпретации. Но при этом иногда вовсе не хочется пользоваться сколько-нибудь продвинутыми методами, вроде метода опорных векторов или логистической регрессии и возникает искушение загнать все данные в одну большую линейную МНК-регрессию, тем более её прекрасно умеет строить даже MS Excel.
Проблема такого подхода в том, что даже если входные данные линейно разделимы, то получившийся классификатор может их не разделять. Например, для набора точек
,
получим разделяющую прямую
(пример позаимствован из (1)):
![Latex](https://habrastorage.org/files/6f5/a40/b54/6f5a40b543d245898e13e6d5da843da2.png)
Встаёт вопрос — можно ли как-то избавиться от этой особенности поведения?
Для начала формализуем предмет статьи.
Дана матрица
, каждая строчка
которой соответствует признаковому описанию объекта
(включая константу
) и вектора меток классов
, где
— метка объекта
. Мы хотим построить линейный классификатор вида
.
Самый простой способ это сделать — построить МНК-регрессию для
, то есть минимизировать сумму квадратов отклонений
. Оптимальные веса можно найти по формуле
, где
— псевдообратная матрица. Таким образом получена картинка из начала статьи.
Для удобства записи мы поэлементно домножим каждую строчку неравенства
на
и назовём получившуюся в левой части матрицу
(здесь
означает построчное умножение). Тогда условие для МНК-регрессии сведётся к виду
, а задача минимизации — к минимизации
.
На этом месте можно вспомнить, что условие разделения классов это
или
, и поскольку мы хотим разделять классы, то надо решать эту задачу.
Введём вектор
, в котором
отвечает за расстояние от элемента
до разделяющей прямой (
). Поскольку мы хотим, чтобы все элементы были классифицированы правильно, мы вводим условие
. Тогда задача сведётся к
и будет решаться как
. Можно вручную подобрать такие значения
, что получившаяся плоскость будет разделять нашу выборку:
Алгоритм Хо-Кашьяпа предназначен для того, чтобы подобрать
автоматически. Схема алгоритма (
— номер шага,
обычно берут равным
):
Вектор отступов хочется вычислить каким-нибудь путём вроде
, поскольку это минимизирует функцию потерь
. К сожалению, условие
не даёт нам этого сделать, и вместо этого предлагается делать шаг по положительной части градиента функции потерь
:
, где
— шаг градиентного спуска, уменьшающийся по ходу решения задачи.
![](https://habrastorage.org/files/d83/d93/904/d83d9390457447edae015fadf98aee5d.png)
В случае линейно-разделимой выборки алгоритм всегда сходится и сходится к разделяющей плоскости (если все элементы градиента по
неотрицательны, то они все нулевые).
В случае линейно-неразделимой выборки, функция потерь может быть сколь угодно малой, поскольку достаточно домножить
и
на константу для изменения её значения и в принципе алгоритм может и не сойтись. Поиск не даёт никаких конкретных рекомендаций на эту тему.
Можно заметить, что если объект классифицирован правильно, то ошибка в поставленной оптимизационной задаче (
) ошибка на нём может быть сведена к нулю. Если же объект классифицирован неправильно, то минимальная ошибка на нём равна квадрату его отступа от разделяющей прямой (
). Тогда функцию потерь можно переписать в виде:
![\min_{w} \sum_{i} \max(0, 1 - w Y_i)^2 - 2\max(0, 1 - w Y_i)](https://tex.s2cms.ru/svg/%20%5Cmin_%7Bw%7D%20%5Csum_%7Bi%7D%20%5Cmax(0%2C%201%20-%20w%20Y_i)%5E2%20-%202%5Cmax(0%2C%201%20-%20w%20Y_i)%20%20)
В свою очередь, функция потерь линейного SVM имеет вид:
![\min_{w} \lambda ||w||^2 + \frac{1}{N} \sum_{i} \max(0, 1 - w Y_i)](https://tex.s2cms.ru/svg/%20%5Cmin_%7Bw%7D%20%5Clambda%20%7C%7Cw%7C%7C%5E2%20%2B%20%5Cfrac%7B1%7D%7BN%7D%20%5Csum_%7Bi%7D%20%5Cmax(0%2C%201%20-%20w%20Y_i)%20)
Таким образом, задача, решаемая алгоритмом Хо-Кашьяпа, представляет собой некоторый аналог SVM с квадратичной функцией потерь (она сильнее штрафует за выбросы далеко от разделяющей плоскости) и игнорирующий ширину разделяющей полосы (т.е. ищущий не плоскость, находящуюся максимально далеко от ближайших правильно классифицированных элементов, а любую разделяющую плоскость).
Можно вспомнить, что МНК-регрессия является аналогом двухклассового линейного дискриминанта Фишера (их решения совпадают с точностью до константы). Алгоритм Хо-Кашьпяпа можно применить и для случая
классов — в этом
и
становятся матрицами
и
размера
и
соотвественно, где
— размерность задачи, а
— число объектов. В этом случае в столбцах, соответствующих неверным классам, должны стоять отрицательные значения.
parpalak за удобный редактор.
rocket3 за оригинальную статью.
Несмотря на бум нейросетей в машинном обучении, алгоритмы линейной классификации остаются гораздо более простыми в использовании и интерпретации. Но при этом иногда вовсе не хочется пользоваться сколько-нибудь продвинутыми методами, вроде метода опорных векторов или логистической регрессии и возникает искушение загнать все данные в одну большую линейную МНК-регрессию, тем более её прекрасно умеет строить даже MS Excel.
Проблема такого подхода в том, что даже если входные данные линейно разделимы, то получившийся классификатор может их не разделять. Например, для набора точек
![Latex](https://habrastorage.org/files/6f5/a40/b54/6f5a40b543d245898e13e6d5da843da2.png)
Встаёт вопрос — можно ли как-то избавиться от этой особенности поведения?
Задача линейной классификации
Для начала формализуем предмет статьи.
Дана матрица
>>> import numpy as np
>>> X = np.array([[6, 9, 1], [5, 7, 1], [5, 9, 1], [0, 10, 1]])
>>> y = np.array([[1], [1], [-1], [-1]])
Самый простой способ это сделать — построить МНК-регрессию для
>>> w = np.dot(np.linalg.pinv(X), y)
>>> w
array([[ 0.15328467],
[-0.4379562 ],
[ 3.2189781 ]])
>>> np.dot(X, w)
array([[ 0.19708029],
[ 0.91970803],
[ 0.04379562],
[-1.16058394]])
Линейная разделимость
Для удобства записи мы поэлементно домножим каждую строчку неравенства
>>> Y = y * X
>>> Y
array([[ 6, 9, 1],
[ 5, 7, 1],
[ -5, -9, -1],
[ 0, -10, -1]])
На этом месте можно вспомнить, что условие разделения классов это
Введём вектор
>>> b = np.ones([4, 1])
>>> b[3] = 10
>>> w = np.dot(np.linalg.pinv(Y), b)
>>> np.dot(Y, w)
array([[ 0.8540146 ],
[ 0.98540146],
[ 0.81021898],
[ 10.02919708]])
Алгоритм Хо-Кашьяпа
Алгоритм Хо-Кашьяпа предназначен для того, чтобы подобрать
- Вычислить коэффициенты МНК-регрессии (
).
- Вычислить вектор отступов
.
- Если решение не сошлось (
), то повторить шаг 1.
Вектор отступов хочется вычислить каким-нибудь путём вроде
>>> e = -np.inf * np.ones([4, 1])
>>> b = np.ones([4, 1])
>>> while np.any(e < 0):
... w = np.dot(np.linalg.pinv(Y), b)
... e = b - np.dot(Y, w)
... b = b - e * (e < 0)
...
>>> b
array([[ 1.],
[ 1.],
[ 1.],
[ 12.]])
>>> w
array([[ 2.],
[-1.],
[-2.]])
![](https://habrastorage.org/files/d83/d93/904/d83d9390457447edae015fadf98aee5d.png)
В случае линейно-разделимой выборки алгоритм всегда сходится и сходится к разделяющей плоскости (если все элементы градиента по
В случае линейно-неразделимой выборки, функция потерь может быть сколь угодно малой, поскольку достаточно домножить
Связь алгоритма Хо-Кашьяпа и линейного SVM
Можно заметить, что если объект классифицирован правильно, то ошибка в поставленной оптимизационной задаче (
В свою очередь, функция потерь линейного SVM имеет вид:
Таким образом, задача, решаемая алгоритмом Хо-Кашьяпа, представляет собой некоторый аналог SVM с квадратичной функцией потерь (она сильнее штрафует за выбросы далеко от разделяющей плоскости) и игнорирующий ширину разделяющей полосы (т.е. ищущий не плоскость, находящуюся максимально далеко от ближайших правильно классифицированных элементов, а любую разделяющую плоскость).
Многомерный случай
Можно вспомнить, что МНК-регрессия является аналогом двухклассового линейного дискриминанта Фишера (их решения совпадают с точностью до константы). Алгоритм Хо-Кашьпяпа можно применить и для случая
Благодарности
parpalak за удобный редактор.
rocket3 за оригинальную статью.
Ссылки
(1) http://www.csd.uwo.ca/~olga/Courses/CS434a_541a/Lecture10.pdf
(2) http://research.cs.tamu.edu/prism/lectures/pr/pr_l17.pdf
(3) http://web.khu.ac.kr/~tskim/PatternClass Lec Note 07-1.pdf
(4) А.Е. Лепский, А.Г. Броневич Математические методы распознавания образов. Курс лекций
(5) Ту Дж., Гонсалес Р. Принципы распознавания образов
(6) Р.Дуда, П.Харт Распознавание образов и анализ сцен
Поделиться с друзьями
Dark_Daiver
>Таким образом, задача, решаемая алгоритмом Хо-Кашьяпа, представляет собой некоторый аналог SVM с квадратичной функцией потерь
Я бы добавил, что при наличии нескольких гиперплоскостей для линейно разделимого случая SVM выберет наиболее «среднюю» плоскость (за это отвечает терм lambda * ||w||^2). В случае алгоритма Хо-Кашьяпа этого (если я все правильно понял), не происходит. На мой взгляд это довольно важно.
Drino
Добавил примечание про ширину разделяющей полосы. Спасибо!