Всех приветствую! Меня зовут Андрей, я студент 4 курса факультета математики и компьютерных наук, автор телеграм‑канала Andre | Data Science. Все мы знаем, что направление машинного обучения сейчас на огромном хайпе — практически из‑под каждого камня говорят о разных методах и о том, что они способны решить практически любую задачу. Но гайдов, которые бы подробно разбирали работу конкретного метода — от обычной загрузки данных до математического обоснования принципа его работы и визуализации каждого шага, — катастрофически мало.
В этой статье я хочу закрыть этот пробел и разобрать один конкретный метод — RBF SVM. Мы посмотрим, как он устроен с математической точки зрения, как появился на свет, какие у него есть сильные и слабые стороны и с чем они связаны. Также протестируем его на реальных датасетах. Так как аудитория IT‑сообщества довольно широкая, я постараюсь очень подробно расписывать каждое определение и каждую часть своего повествования, чтобы по ходу чтения у вас возникало как можно меньше вопросов и нить понимания не обрывалась. Будет большим плюсом, если вы уже разбираетесь в следующих математических дисциплинах:
Линейная алгебра
Матанализ
Функциональный анализ
Дифференциальные уравнения
Методы оптимизации
Аналитическая геометрия
Вводная часть
Представьте ситуацию: вы получили данные для небольшого исследования — скажем, около сотни пациентов и десяток биохимических показателей. Вы точно знаете, что пациенты делятся на два класса — здоровые и больные, но на графике эти классы сильно перемешаны. Первое, что посоветует большинство: возьмите логистическую регрессию, постройте прямую границу — и дело в шляпе. Вы пробуете, но линия просто не разделяет ваши два класса, как её ни поворачивай.
Ладно, прямая не подошла — пробуем что‑то более гибкое. Берём полиномиальную функцию — чуть лучше, но всё равно есть точки, которые упорно остаются не на своей стороне. Пробуем сигмоиду — и получаем ещё хуже, чем с обычной прямой. Логично было бы усложнить модель ещё сильнее, однако данных настолько мало, что о нейросети с её миллионами параметров и речи быть не может — она попросту запомнит эти точки наизусть и будет ошибаться на новых.
Здесь одновременно две проблемы: классы нельзя разделить прямой линией (нелинейность), и данных слишком мало, чтобы обучить что‑то сложное (дефицит данных). Большинство методов умеют решать только одну из них за счёт другой — либо гибкость, либо устойчивость к переобучению, редко всё сразу.

Линейная функция — разделяем классы обычной прямой. Точность — 80%
Полиномиальная функция — обычный многочлен. Точность — 92%
Сигмоида — гладкая S‑образная функция. Точность — 70%
RBF — наш клиент. Точность — 97%
Именно на этот случай и существует метод, о котором пойдёт речь в статье.
Разберем на ещё одном примере: имеется таблица с данными пациентов, которая содержит их ID, показатели температуры и пульса, а также целевой статус (здоров/болен).
Сейчас я буду ссылаться на искусственно созданный датасет в учебных целях. Ближе к концу мы будем использовать реальные данные.
id | temp_C | puls | diagnosis |
P01 | 36.77 | 73 | здоров |
P02 | 36.68 | 62 | здоров |
P03 | 38.58 | 94 | болен |
.. | .. | .. | .. |
Пусть также дана еще одна таблица пациентов. Он похожа на ту, которая выше, только в ней добавляется еще третий признак — СРБ.
СРБ (С‑реактивный белок) — это высокочувствительный белок, уровень которого в крови быстро и многократно возрастает в ответ на воспаление, инфекцию или повреждение тканей.
В норме СРБ почти равен нулю (меньше 5 мг/л)
Небольшой подъем (до 20 — 30 мг/л) — это обычно простуда или вирус
Сильный подъем (выше 100 мг/л) — это серьезная бактериальная инфекция, которая требует антибиотиков
id | temp_C | puls | SRB | diagnosis |
P01 | 36.63 | 72 | 1.3 | здоров |
P02 | 36.71 | 65 | 3.3 | здоров |
P03 | 36.73 | 72 | 29.6 | болен |
.. | .. | .. | .. | .. |
Теперь давайте отобразим наших товарищей на координатную плоскость, где каждая ось будет отвечать за отдельный признак.

На левом графике пациенты расположены в двумерном пространстве, так как оцениваются всего два признака. На правом графике они находятся уже в трехмерном пространстве, поскольку добавился третий признак. Интересный момент: на левом рисунке два больных пациента визуально очень похожи на здоровых, а вот на правом разница видна сразу — эти два человека значительно отдалились от группы здоровых. Кстати, обратите ещё внимание, что в двумерном пространстве разделить эти классы довольно проблематично. Обычная линейная модель здесь будет бесполезна — придется искать какую‑то очень извилистую функцию. А вот в трехмерном пространстве мы сможем без проблем разделить два класса между собой. Похожий трюк мы сейчас будем использовать.
Основная суть метода заключается в том, что мы берем наши исходные данные и как бы выдавливаем их в пространство большей размерности, в котором мы сможем разделить наши классы гиперплоскостью.
Гиперплоскость — это подпространство, размерность которого ровно на единицу меньше, чем размерность окружающего его пространства. Например, в двумерном пространстве гиперплоскостью будет являться прямая, а в трёхмерном пространстве будет обычная плоскость. Для четырёхмерного гиперплоскостью будет являться какая‑то объёмная фигура

Основа основ
Классический способ построить нелинейную границу между классами — придумать такое отображение φ: Rd → RD, где R — пространство вещественных чисел, d — размерность исходного пространства, а D — размерность пространства, в которое мы переводим наши данные. Пример выше: отображение φ: R2 → R3. После перехода в трехмерное пространство мы можем поделить наши классы. Давайте я покажу ещё один пример, чтобы точно закрепить всю суть:

В левой части у нас одномерное пространство с одним признаком и два класса (синий и красный, ну или «Здоров» и «Болен»). Так как размерность пространства равна единице, то наша гиперплоскость будет иметь нулевую размерность, а что это за объект? — точка! Но если внимательно посмотреть, то обычной точкой мы не сможем разделить эти два класса. Давайте повысим размерность пространства до 2D, тогда наше отображение примет вид: φ: R1 → R2, где φ(x) = (x, x2). Благодаря нашему отображению мы получаем двумерную точку, вторая координата которой равна квадрату первой. На выходе наши точки выстроятся в параболу, на которой мы можем увидеть зазор между классами. Там мы и проведем нашу гиперплоскость. Но мы же можем сместить её вверх на 0.0001 или вниз. Как понять, какую именно строить? — на этот вопрос я отвечу позже.
Кстати, в данном случае квадратичную функцию необязательно было брать. Можно взять такое отображение φ(x) = (x, x4) или даже такое φ(x) = (x, |x|).
Уравнение гиперплоскости
В матане очень много всяких различных форм записи гиперплоскости. Мы будем использовать векторную форму: ⟨wT, x⟩ + b = 0.
х = (х1, х2,..., хn) — вектор‑столбец признаков, xn — значения признаков, n — количество признаков;
w = (w1, w2,..., wn) — вектор‑столбец нормали (перпендикуляр к плоскости). В контексте машинного обучения — это веса модели;
⟨wT, x⟩ — скалярное произведение. В развернутом виде это сумма вида: w1x1 +... + wnxn;
b — смещение/сдвиг. Показывает, насколько сильно гиперплоскость смещена относительно начала координат.
Запись ⟨w, x⟩ предпочитают обычно в текстах, ориентированных на геометрию и функциональный анализ. Транспонирование здесь не нужно, так как скалярное произведение является самостоятельной операцией.
А вот в линейной алгебре стандартные векторы всегда считаются векторами‑столбцами. Чтобы умножить один столбец на другой по правилам матричного умножения, первый вектор необходимо перевернуть в строку.
Теперь обозначим f(x) = ⟨w, x⟩ + b. Вспомним, что нас интересует классификация любой точки в пространстве относительно гиперплоскости. Вектор нормали w всегда указывает в сторону положительного класса. Пусть точки, лежащие по направлению вектора w, относятся к классу «Здоров» (+1), а против его направления — к классу «Болен» (-1). Для разделения воспользуемся сигнум‑функцией:
Если значение функции больше нуля, хоть на одну миллиардную, то значение сигнум‑функции равно 1 и точка принадлежит классу «Здоров». Ну и в противном случае та же история, либо точка лежит на границе классов, что практически невозможно в контексте реальных задач.
Представим ситуацию, у нас есть 4 объекта с двумя признаками: x1 = (-1, 2), x2 = (2, 1), x3 = (1, -2), x4 = (-2, -1) и вектор нормали w = (1, 1), а во втором случае w = (1, -1), смещение b пока пусть будет нулевым. Вектор нормали всегда указывает расположение положительного класса. Подставив координаты каждой точки в уравнение гиперплоскости, мы получим числовое значение (на графике оно указано рядом с каждой точкой и показывает степень влияния точки). Затем мы передаем это число в сигнум‑функцию и получаем окончательный вердикт о том, к какому классу принадлежит эта точка.

Теперь поиграем с вектором нормали. Как изменится наш график, если мы оставим направление вектора неизменным, но сделаем его в два раза длиннее? Сама граница никуда не делась. Умножение значение весов на константу не меняет расположение гиперплоскости. Также, можно заметить, что значение функции f(x) выросло в два раза. Чем больше значения весов, тем сильнее f(x) уходит в плюсовые или минусовые значения при отдалении от границы.

Теперь посмотрим на ситуацию, когда вектор нормали не только меняет свою длину, но и своё направление одновременно. Поскольку граница привязана к вектору нормали, она послушно развернется вслед за ним. Также, из‑за поворота границы, нижняя точка перешла в класс «Болен». Если говорить на языке млщика, то модель решила, что второй признак х2 в два раза важнее первого признака х1.

Изменение соотношения весов поворачивает разделяющую границу и перераспределяет важность признаков, заставляя модель сильнее прислушиваться к одним характеристикам объекта и игнорировать другие.
Но это все работало при b = 0. Теперь посмотрим, на что влияет это коэффициент:
Если b = 0, то граница проходит через начало координат;
Если b > 0, то граница сдвигается в стороны, противоположную вектору w;
Если b < 0, то граница сдвигается по направлению вектора w.
Алгебраически смещение b — это просто константа, которая прибавляется ко всем значениям f(x) одновременно. Взглянем на график:

Если жестко зафиксировать b = 0, то модель сможет поворачивать разделяющую прямую как угодно, но точка вращения всегда будет привязана к центру координат (0,0).
Если реальные данные разделяются прямой, проходящей далеко от центра (например, на высоте x2 = 100), модель физически не сможет построить адекватную границу, сколько бы мы ни крутили веса.
Принцип работы SVM
SVM (Support Vector Machine / Метод опорных векторов) — это алгоритм обучения с учителем, главная цель которого — найти оптимальную разделяющую гиперплоскость между классами, обеспечивающую максимальный зазор между ними. Если данные можно разделить прямой (или плоскостью в 3D), то таких прямых можно провести бесконечно много. Нам нужно найти самую надежную.
Надежная граница — это граница, которая находится на максимальном и равном удалении от крайних точек обоих классов. Чем больше этот зазор, тем меньше шанс, что модель ошибетcя на новых данных.
Опорный вектор — это объект обучающей выборки, который находится на границе зазора и задает параметры разделяющей гиперплоскости.


Чтобы максимизировать зазор, о котором было сказано выше, нам нужно научиться строго измерять расстояние от любого объекта xi до гиперплоскости. Для начала, поговорим о таком понятии как функциональный отступ:
Он показывает результат классификации (знак) и уверенность модели. Если точка принадлежит классу (-1), то ⟨w, x⟩ + b < 0, y = -1 и тогда значение функционального отступа будет положительным. Если точка принадлежит классу (+1), то функциональный отступ так же будет положительным. Тогда это все можно записать так:
У функционального отступа есть фатальный недостаток: уравнение гиперплоскости можно безнаказанно умножать на любое число. Если уравнение x1 + x2 — 2 = 0 умножить на 10 целиком, то получим 10x1 + 10x2 — 20 = 0. Визуально — это та же самая прямая, но функциональный отступ вырос в 10 раз. Для алгоритма это выглядит так, будто модель стала в 10 раз «увереннее» и лучше. Компьютер попадает в ловушку: вместо того чтобы двигать линию и реально учиться разделять данные, он может просто бесконечно увеличивать веса до бесконечности, обманывая сам себя.
Как избавиться от этой ловушки и математически записать настоящий зазор? Вспомним аналитическую геометрию — расстояние от любой точки х до гиперплоскости вычисляется так:
Изящность формулы заключается в том, что мы подставили вместо значения числителя формулу, которую вывели выше и если мы снова провернем наши махинации с домножением на какую‑то константу, например на 10, то ничего не изменится — десятки в числителе и знаменателе просто сократятся.
Вернемся к зазору. Мы знаем, что опорные векторы находятся на его границе и расположены ближе других точек к гиперплоскости. Здесь мы берем ту самую свободу домножения на константу: раз масштаб w и b ничего не меняет геометрически, мы вправе выбрать его сами, по своему усмотрению. Договоримся так: подберём масштаб специально под опорный вектор так, чтобы у неё функциональный отступ стал равен ровно 1:
А для точек, которые находятся дальше зазора будет выполняться вот такое условие:
Тогда формула расстояния от точки (опорного вектора) до гиперплоскости примет вид:
С другой стороны прямой есть точно такая же ближайшая точка второго класса, на точно таком же расстоянии. Значит, полная ширина зазора равно двум таким расстояниям, а именно:
Процесс максимизации зазора равносилен задаче минимизации знаменателя, ведь чем меньше знаменатель, тем больше дробь.
Чтобы найти минимум функции нам поможет наша любимая производная. Правда, если мы возьмем производную например по переменной w1, то получим вот такой кошмар:
Хуже того: это функция ещё и не везде определена (знаменатель может быть равен нулю). Работать с такой функцией неудобно и математически небезопасно. А если мы попробуем избавиться от корня путем возведения в квадрат? Идея супер, но насколько это законно? На первый взгляд кажется странным: ||w|| и ||w||2 — это разные функции, имеющие разные значения, кроме единицы и нуля. Как может быть, что точки минимума находятся в одном и том же месте? Вспомним небольшое школьное правило:
Если f(x) возрастает (и при этом f(x) ≥ 0), то и g(x) = √ f(x) тоже возрастает.

Супер, мы немного упростили себе задачу, но теперь при взятии производной у нас будет вылезать двойка спереди. Уничтожим её через домножение на дробь 1/2. Вуаля! Мы пришли к итоговой задаче минимизации:
Функция Лагранжа
Теперь давайте протестируем нашу универсальную формулу. В качестве примера возьмем следующие точки:
Точка | Класс | Координаты (x, y) |
A1 | -1 | (-3, 2) |
A2 | -1 | (-2, 3) |
A3 | -1 | (-1, 1) |
B1 | +1 | (1, 3) |
B2 | +1 | (1, 2) |
B3 | +1 | (2, 1) |

Пространство двумерное, значит уравнение гиперплоскости имеет вид:
Давайте подставлять значения в условие для точек, которые находятся дальше:
Помним, что наша цель — максимизация зазора:
Ищем минимум — берем частные производные:
Все веса равны нулю, выглядит довольно странно. Может дальше будет лучше? Если мы подставим эти значения в уравнения для каждой точки, то получим, что для красных точек должно выполняться b ≤ -1, а для синих точек b ≥ 1, что в принципе невозможно (несмотря на то, что там и там b может быть равно нулю). А на графике это будет выглядеть так:

Получается, текущих условий нам недостаточно, нужно что‑то ещё. Здесь нам поможет функция Лагранжа.
Функция Лагранжа — это функция, которая используется в матанализе для поиска условного экстремума при наличии ограничений в виде равенств/неравенств.

Допустим, нам дана функция f(x, y) = x + y. Её графиком будет бесконечная красная плоскость. Если мы попытаемся найти точки экстремума, то придем к тому, что их не существует (как бы логично, ведь плоскость бесконечная). В таком случае мы добавляем ограничение в виде равенства x2 + y2 = 1. Мы как бы сформировали «опору», на которой могут появиться точки экстремума. Дальше нам и поможет функция Лагранжа. Она имеет вид:
где:
f(x) — целевая функция, которую мы оптимизируем;
φi(x) = 0 — ограничения в виде равенства;
gi(x) ≤ 0 — ограничения в виде неравенства (в нашем примере их нет);
λi — множитель Лагранжа, может быть любого знака;
αi — показатель важности конкретной точки для построения гиперплоскости. Если он равен нулю, то точка лежит далеко от границы зазора и она нам не нужна. Если он больше нуля, то точка лежит на границе зазора и является опорным вектором;
x = (x1,...,xn) — вектор входных данных.
Количество ограничений‑равенств (m) всегда должно быть строго меньше количества исходных переменных (n). Если ограничений будет столько же или больше, чем переменных, то система уравнений свяжет все переменные намертво и оптимизировать будет нечего.
Давай найдем точки экстремума в нашем примере через функцию Лагранжа:
Класс, благодаря ограничениям и функции Лагранжа мы смогли найти точки экстремума. Давайте рассмотрим ситуацию, когда помимо ограничения‑равенства у нас ещё есть ограничение‑неравенство.

Найдем точки экстремума:
Обратите внимание на последнее условие в самой первой системе. Оно в себе содержит два сценария:
Если 1 — x ≠ 0, то единственным решением будет α = 0. Подставляем это значение в систему, чтобы найти стационарные точки функции Лагранжа. Расчет показывает, что ни одна из стационарных точек не удовлетворяет неравенству x > 1. Мы пришли к выводу, что условный экстремум не может находиться внутри допустимой области, и локальные экстремумы функции на данном множестве могут быть расположены только на его границе.
При x = 1 координата жестко фиксируется и переменная α освобождается от требования быть нулём. Система пересчитывается для граничных условий, что позволяет найти нам точки экстремума.

Если вдруг запутались в определениях глобального, локального и условного экстремума, то вот вам супер классная визуализация (спасибо Гемини):

Возвращаемся к SVM и к нашим точкам на плоскости. Функция Лагранжа для них будет выглядеть так (сверху общий вид функции для нашего случая, а снизу уже с подстановкой):
Берем наши 6 точек из кастомного датасета и составляем для каждой ограничение‑неравенство:
A1(−3,2) ⟹ 3w1 − 2w2 − b ≥ 1
A2(−2,3) ⟹ 2w1 − 3w2 − b ≥ 1
A3(−1,1) ⟹ w1 − w2 − b ≥ 1
B1(1,3) ⟹ w1 + 3w2 + b ≥ 1
B2(1,2) ⟹ w1 + 2w2 + b ≥ 1
B3(2,1) ⟹ 2w1 + w2 + b ≥ 1
После подстановки неравенств в функцию Лагранжа мы получаем вот такой ужас:
А теперь вспомните, что на этом этапе нам надо брать частные производные, а чтобы их взять нужно сгруппировать все слагаемые по переменным w1, w2 и b. После группировки получаем следующее:
Переходим к вычислению частных производных:
Мы нашли w1, w2 и b, но при этом у нас осталось 6 неизвестных альфа. Теперь давайте вернемся к примеру с бесконечной плоскостью и окружностью. После вычисления частных производных мы рассматривали два случая: альфа равно нулю и больше нуля. В нашем случае точек 6, а это значит, что нам надо рассмотреть всего‑то 26 случая, а точнее посчитать 64 системы:
Система 1: α1 = 0, α2 = 0,..., α6 = 0 (веса обнуляются)
Система 2: α1 > 0 (ограничение = 0), α2 = 0,..., α6 = 0
Система 3: α1 = 0, α2 > 0 (ограничение = 0),..., α6 = 0
.. и так до 64 системы
Казалось бы, перебрать 64 варианта для компьютера — всё равно что семечки пощёлкать. А теперь представьте, если бы точек было не 6, а 100. Тогда нам бы пришлось считать 2100 систем, а это равно вот такому числу:
Фанфакт: современный суперкомпьютер смог обработать эти данные примерно за 40 000 лет.
Поэтому, дабы избежать комбинаторного ада и взрыва процессора, мы делаем ход конём — снова вычисляем частные производные:
А теперь посмотрите на первое выражение. Вектор весов это буквально линейная комбинация самих точек датасета, умноженных на метки класса и коэффициенты Лагранжа. Теперь мы точно знаем, где вычисляются веса модели. Посмотрим на второе выражение, оно куда интереснее. Разложим его немного, чтобы понять, в чем заключается его изящность:
Исходную сумму мы разбили на сумму точек из класса +1 и сумму точек из класса -1. Последнее выражение можно даже назвать законом сохранения баланса сил при построении гиперплоскости. По факту оно несёт в себе следующую инфу:
Гиперплоскость зажата между классами с одинаковым суммарным «давлением». Сдвинуть её в какую‑либо сторону без ухудшения качества классификации невозможно. Чем‑то напоминает 3 закон Ньютона.
Даже если в одном классе будет 1000 объектов, а в другом всего 2, то суммарный вклад опорных векторов каждого класса на границе зазора будет строго одинаковым. У двух точек значение множителя Лагранжа будет больше, тем самым мы скомпенсируем разницу. Так что дисбаланс классов нас не пугает.
Супер, теперь подставим получившиеся выражение в исходную функцию Лагранжа:
Теперь по порядку. Помним, что переменная D отвечает за количество признаков в датасета, а если говорить математически, то за размерность пространства, в котором мы находимся. Дальше мы просто подставляем получившиеся значения в функцию, и — о чудо! Поскольку от переменных w и b не осталось ни следа, мы переименовываем функцию в W(α). Но тут у вас может возникнуть вполне законный вопрос: «Почему вдруг мы тут максимизируем функцию, если до этого мы пришли к её минимизации?».

Дело в том, что после введения множителей Лагранжа мы переходим к двойственной задаче. Сначала для каждого набора коэффициентов α мы минимизируем функцию Лагранжа по w и b:
Полученная функция W(α) является нижней границей оптимального значения исходной задачи. Поэтому наша цель теперь — найти максимально высокую из возможных нижних границ:
К тому же, в формуле выше мы пришли к тому, что перед двойной суммой стоит минус, а двойная сумма — это всегда намек на появление квадрата. Значит, наша функция перевернулась.
Возвращаемся к нашим 6 точкам обратно. В нашей функции есть скалярное произведение ⟨xi, xj⟩ — это матрица Грама G, где Gij = ⟨xi, xj⟩.
Матрица Грама — это квадратная таблица из скалярных произведений векторов. Она симметрична относительно главной диагонали. Её определитель равен нулю тогда, когда вектора линейно‑зависимы.
Алгоритм не знает, какие точки окажутся опорными векторами, поэтому мы предполагаем, что все точки окажутся ими. Помним, что если точка лежит на границе, то выполняется равенство:
Подставим метки классов и столбцы матрицы Грама. Получим систему вида:
Её можно решать как угодно: методом Гаусса, Крамера, Жордано‑Гаусса, исход один — система не имеет решений! Вывод — все 6 точек не могут лежать на границе зазор. Пытаться перебирать 64 дискретных подсистем вручную также не имеет смысла. Вместо этого задача решается как непрерывная максимизация вогнутой квадратичной функции с помощью метода последовательной минимальной оптимизации.
SMO — Sequential Minimal Optimization
Суть этого метода заключается в том, что вместо оптимизации всех коэффициентов αi одновременно на каждом шаге выбирается только пара переменных αi и αj, для которой решается небольшая оптимизационная задача. После этого выбранная пара обновляется, а остальные коэффициенты остаются неизменными. Последовательно повторяя такие шаги, SMO приближается к оптимальному решению всей двойственной задачи SVM.
Разбираем наш пример. Мы имеем гладкую квадратичную функцию с условиями:
Так как W(α) — это перевернутый параболоид, то у него существует единственный глобальный максимум. Наша задача — подняться на саму вершину этого купола, подбирая коэффициенты αi, то есть «силу давления» каждой точки на гиперплоскость. Но мы не можем менять значение переменных альфа по одной из‑за закона сохранения баланса. Меняя «силу» всего одной точки мы моментально рушим баланс всей системы. Для сохранения баланса Вселенной шаг оптимизации требует обновления строго двух переменных (αi, αj).
Почему именно две, а не три или четыре? Две переменные — это минимально возможное количество для совершения шага без нарушения равенства. Именно поэтому алгоритм называется последовательная минимальная оптимизация.
А как именно нам изменить эту пару, чтобы ничего не сломать?
Если точки разных классов: мы обязаны изменить их на одну и ту же величину в одну сторону (Δαi = Δαj). Увеличили давление красной точки на 1 — на столько же обязаны увеличить давление синей.
Если точки одного класса: мы меняем их в противоположные стороны (Δαi = ‑Δαj). Увеличили давление красной точки на 1 — на столько же обязаны уменьшить давление другой красной точки.
Начальное состояние алгоритма: все множители и смещение равны нулю:
Текущее предсказание модели для текущей точки:
Ошибка классификации для каждой точки выглядит так:
Для класса y = -1 (A1, A2, A3): E = ‑(-1) = +1
Для класса y = +1 (B1, B2, B3): E = ‑(+1) = -1
По формуле аналитического шага SMO прирост целевой функции ΔW при оптимизации пары (i, j) равен:
где знаменатель вычисляется через евклидово расстояние между точками:
Теперь подставляем все в формулу аналитического шага:
Чтобы получить максимальный прирост функции, алгоритм обязан выбрать пару точек противоположных классов с минимальным расстоянием.
На стартовом шаге, когда все α = 0, выбор двух точек одного класса дает Ei — Ej = 0 и прирост ΔW = 0. Поэтому для первого шага алгоритм рассматривает исключительно пары противоположных классов. На последующих итерациях, когда часть αк > 0, SMO начинает оптимизировать и пары одного класса.
Расчет квадрата расстояния для всех 9 возможных пар:
Максимальное значение достигается на паре (A3, B2) с индексами i = 3, j = 5 (точка B2 пятая по порядку). Фиксируем оставшиеся переменные:
Из условия баланса сил следует:
Подставляем вектор α(0)(t) = (0, 0, t, 0, t, 0) в функцию W(α):
Дальше по сценарию ищем экстремум:
Отсюда получаем значения множителей Лагранжа: α3 = 0.4, α5 = 0.4 и сам вектор множителей: α(1) = (0, 0, 0.4, 0, 0.4, 0). А теперь сенсация — вычисление вектора весов и смещения:
Теперь смещение B:
Для поиска смещения нам не принципиально какую точку подставлять в уравнение.
Всё! Мы наконец‑то нашли уравнение гиперплоскости. Но давайте лучше проверим себя:

Но стоп, стоп. Мы пришли к тому, что точки с координатами (1, 2) и (-1, 1) являются опорными векторами и задают наклон гиперплоскости. Но на графике мы видим самозванца на координатах (-2, 3). Он четко лежит на границе зазора, но в расчетах получает α2 = 0. Если не вдаваться в миллион строк с вычислениями, то можно просто сказать, что эта точка для построения гиперплоскости просто избыточна, никакого вклада она не внесла.
Я пересчитал все без этой точки и действительно изменений ноль. Эта точка никак не влияет на модель.
Несмотря на то, что у нас все получилось, этот датасет оказался плохим примером для демонстрации работы SMO, потому что это метод последовательной минимальной оптимизации, а значит с первого раза у нас вряд‑ли получится вычислить все. Поэтому, с вашего позволения мы рассмотрим ещё один пример по‑меньше, чтобы понять, как работает метод, если с первого шага у нас ничего не получается.
Берем следующие точки:
A1 = (-1, 0), y1 = -1
A2 = (-0.5, 3), y2 = -1
B1 = (1, 0), y3 = -1
B2 = (2, 3), y4 = -1
Запишем сразу матрицу Грама:
На старте все так же α(0) = (0, 0, 0, 0), b(0) = 0. Считаем квадраты расстояний:
Однозначно выбираем пару (A1, B1) с индексами i = 1, j = 3. Фиксируем α2 = 0, α4 = 0. Из условия баланса получаем:
Подставляем в W(t) и сразу считаем производные вместе с экстремумом при α(1) = (t, 0, t, 0):
Считаем промежуточные параметры w(1) и b(1):

Необычная картина, гиперплоскость — это просто вертикальная линия, так ещё и точка залезла в зазор. Проверим функциональные отступы yk(⟨w(1),xk⟩ + b(1)) для каждой точки:
Здесь явное нарушение со стороны точки A2. Шаг 1 результатов не дал — переходим ко второй итерации.
Тут мы сначала ищем точку с наибольшим нарушением. Вычисляем ошибку классификации через:
После первой итерации мы получили w(1) = (1, 0), b(1) = 0. Считаем:
E1 = F(A1) — y1 = -1 ‑(-1) = 0
E2 = F(A2) — y2 = -0.5 ‑(-1) = +0.5 — нарушитель
E3 = F(B1) — y3 = 1 — 1 = 0
E4 = F(B2) — y4 = 2 -1 = +1
В терминах SMO точка B2 не является нарушителем. Можно считать, что это значение говорит нам о том, что точка находится далеко от границы зазора.
Выбираем точку с наибольшим штрафом и ищем другую точку, которая будет максимизировать разницу: |E2 — Ej|.
A1: |0.5 — 0| = 0.5
A2: |0.5 — 0| = 0.5
B2: |0.5 — 1| = 0.5
Разница везде одинаковая. В таком случае отдаем приоритет точкам, которые уже являются опорными векторами. У нас активны A1 и B1. Для компенсации баланса алгоритм берет точку противоположного класса — B1.
Мы считаем Ek, чтобы понять, в какую стороны выгоднее наклонить прямую на следующем шаге. Формула оптимизации шага требует максимизировать разницу |Ei — Ej|. Она помогает понять, насколько сильно нужно повернуть вектор весов, опираясь на B2, чтобы избавиться от нарушителя A2, не задев остальные точки.
Рабочая пара на вторую итерацию: (A2, B1). Находим значения переменных α2 и α3. Остальные фиксируем на их текущих значениях: α1 = 0.5 и α4 = 0.
Напомню исходную формулу, в которую мы будем все подставлять:
Ну, а теперь считаем:
Вектор множителей обновлен:
Теперь снова считаем вектор весов и смещение:
Теперь смещение. Напомню, что оно вычисляется по любому опорному вектору. Возьмем B1(1, 0):

Самый главный момент — мы подставляем значения весов и смещений из предпоследнего уравнения гиперплоскости. В конце я привел его просто в порядок и сделал немного красивее. Если подставить значения из последнего уравнения, то получим полный бред.
ВСЁ! Теперь мы понимаем, как под капотом работаем метод опорных векторов. Переходим ко второму не менее важному компоненту нашего метода обучения.
В рамках SVM мы рассматривали только двумерный случай. В остальных других принцип работы абсолютной такой же.
Принцип работы RBF
До это мы с вами решали задачу классификации — делили два класса, но бывают ситуации, когда классы запутаны настолько, никакая линия или сложная функция их не сможет разделить. С этим нам поможет радиально‑базисная функция (Гауссово ядро).
RBF (Radial Basis Function / Радиально — базисная функция) — функция, значение которой зависит исключительно от расстояния между точкой и некоторым фиксированным центром. Говоря проще, она измеряет близость или сходство объектов, основываясь на том, как далеко они находятся от выбранной точки отсчета.
Радиальная — потому что состоит из бесконечного числа окружностей, имеющих общий центр. Расстояние ||x — y|| вычисляется одинаково во все стороны.
Базисная — потому что из набора базисных функций мы можем собрать поверхность.
В отличие от стандартных алгоритмов, которые пытаются сразу построить прямую, RBF сначала задает вопрос: Насколько новый объект x похож на наш опорный вектор y? За меру сходства отвечает формула Гауссово ядра:
где:
х = (х1, х2) — координаты нашего объекта.
у = (у1, у2) — координаты опорного вектора.
||x — y||2 — квадрат евклидового расстояния между двумя элементами:
ɣ — параметр, регулирующий, насколько быстро затухает влияние опорного вектора при отдалении от него. Можно сравнить с магнитным полем.
Если мы х приравняем к переменной у, то получим, что в норме появится ноль и значение экспоненты в нулевой степени будет равно единице, то есть K(y, y) = 1. Единица означает, что точки идеально совпадают, в противном случае значение функции будет равно нулю.

Графически RBF выглядит как холм или купол, на вершине которого находится опорный вектор. На склоне находится другая рандомная точка, с которой сравнивается степень схожести. Также, помимо холмов у нас бывают и ямы. Это зависит от принадлежности к классу, например: точка А принадлежит классу (-1) — значит точка лежит в яме, точка В принадлежит классу (+1) — значит точка находится на вершине холма. Если посмотрим на графики 3 и 4, то заметим, что у нас две точки принадлежат классу (+1) и одна точка классу (-1). Черная линия — это та самая гиперплоскость, которая четко делит два класса между собой. По оси Z ее координата равна нулю.
Каждая базисная функция K(x, yi) — это лишь одиночный элемент. Чтобы получить весь ландшафт (холмы и впадины), мы умножаем каждый купол на вес и складываем их, получая следующую формулу:

Закрепим теперь все на практике: у нас есть три точки — опорные вектора: x1 = (-1, 1), x2 = (-1, -1), x3 = (2, 0). Потом я захотел и влепил на график новую точку (точнее звезду), координаты которой равны (1, 1). Мне надо понять, какому классу она принадлежит и насколько я в этом уверен.
Я пришел к тому, модель отнесла точку к классу (+1), при это в своем выборе она не сильно уверена, так как значение очень близко к нулю.
Связь между RBF и SVM
Теперь снова вернемся к формуле из прошлой главы:
Вся суть перехода к нелинейным границам кроется в замене матрицы Грама на Гауссово ядро. Мы не меняем алгоритм оптимизации, а просто подменяем один элемент. А законно это вообще? В математике нельзя просто так взять и вставить какую‑то формулу с экспонентой внутрь алгоритма. Если мы вместо матрицы Грама подставим функцию, которая не является скалярным произведением, наш алгоритм просто сломается и выдаст бред. Значит, чтобы замена была легальной, Гауссово ядро должно представлять из себя скалярное произведение в каком‑то новом пространстве.
Здесь ϕ(x) — это функция, которая берет исходные координаты и переводит в многомерное пространство. Допустим, но как нам понять, можно ли как‑то связать Гауссово ядро и скалярное произведение? На этот вопрос ответит теорема Мерсера.
Теорема Мерсера: Пусть X — компактное подмножество в RD, а K: X × X → R — непрерывное симметричное ядро, то есть K(x, y) = K(y, x). Тогда отображение K может быть представимо в виде скалярного произведения:
Тогда и только тогда, когда для любой ненулевой функции g(x) ∈ L2(X) выполняется интегральное условие:
Здесь λi — неотрицательные собственные значения, а ϕi — собственные функции линейного оператора, порожденного ядром K.
Что означает это простыми словами:
Гарантия существования ϕ(x): Нам не нужно явно искать или строить отображение в многомерное пространство. Теорема гарантирует, что оно математически существует.
Требование к матрице: Для любых выбранных объектов матрица их попарных ядер (матрица Грама) никогда не будет иметь отрицательных собственных значений.
Защита от сбоев: Благодаря этому алгоритм оптимизации SMO всегда сходится к глобальному оптимуму, так как целевая функция остается выпуклой.
Итак, теорема Мерсера явно дала нам понять, что для Гауссово ядра справедливо представление в виде скалярного произведения и вот теперь мы можем вставить ядро в формулу:
Мы почти приблизились к завершению, но в названии статьи я написал про прыжок в бесконечность. Где он по итогу? Теорема Мерсера разрешила нам не искать вектор ϕ(x) и его координаты, а просто использовать формулу с экспонентой. А давайте посмотрим, что из себя вообще представляем этот вектор. Для простоты возьмем одномерные точки x и у. Пускай параметр γ = 0.5. Раскроем квадрат разности ядра:
Обратим внимание на второй множитель — он зависит от двух переменных сразу. Именно в нем спрятана геометрия нового пространства. Разложим его в ряд Маклорена:
А теперь мы с вами провернем очень необычный финт — мы представим эту сумму как скалярное произведение двух независимых векторов, а точнее в видео попарного произведения координат. Разбиваем каждую дробь на два симметричных множителя:
Теперь вернемся к функции ϕ(x) подставим полученные значения обратно:
Вуаля! Мы доказали, что ϕ(x) имеет бесконечное количество координат, а значит мы находимся в бесконечномерном пространстве. Именно в таком пространстве, даже самые запутанные классы гарантированно можно разделить гиперплоскостью! И это еще не все, изящность заключается в том, что благодаря теореме Мерсера нам не придется вычислять все координаты вектора. Я скажу больше, мы вообще не сможем их посчитать, потому что их бесконечное количество. Вот такой хитрый мув мы провернули — залетели в бесконечность и сразу навели свои порядки.

Итого, мы получаем функцию на этапе обучения модели:
Алгоритм SMO съедает эту новую матрицу и выдает нам оптимальные веса для каждого объекта. Те объекты, у которых α больше нуля, становятся опорными векторами. После нахождения смещения b этап обучение завершен.
Для инференса получаем такую формулу:
где:
y (c шапкой) — итоговый предсказанный класс.
xnew — вектор признаков нового элемента.
SV — множество индексов опорных векторов.
xi — вектор признаков i‑го опорного вектора.
yi — метка класса для i‑го опорного вектора.
αi — коэффициент Лагранжа.
ɣ — параметр, регулирующий, насколько быстро затухает влияние опорного вектора при отдалении от него.
b — свободный член.
Хочу добавить, что здесь мы не рассматриваем ситуацию, когда сигнум равен 0. На практике это означало бы, что объект идеально лежит на границе и не принадлежит ни одному классу, хотя такое практически невозможно в жизни. Но если вдруг такое произойдет, то этот объект автоматически присвоится какому нибудь из классов.
Тестирование и сравнение с другими методами
После того как мы полностью разобрали принцип работы данного метода, мы прогоним его на реальных датасетах и сравним его с другими методами машинного обучения. Для демонстрации преимуществ и недостатков я буду использовать 4 разных датасета:
Название | Классификация | Кол‑во классов | Кол‑во объектов | Кол‑во признаков |
Sonar | Бинарная | 2 | 208 | 60 |
Digits | Мультиклассовая | 10 | 1797 | 84 |
UCI HAR | Мультиклассовая | 6 | 10 299 | 561 |
Breast Cancer | Бинарная | 2 | 569 | 30 |
Sonar — сигналы сонара, полученные при отражении от некоторого объекта. Надо понять, что обнаружил сонар — камень или мину. Число 60 — это количество признаков, которые описывают характеристику отраженного сигнала.
Digits — набор рукописных цифр 8 на 8 пикселей. Значение признака показывает интенсивность конкретного пикселя. Надо определить, какая цифра изображена на картинке.
UCI HAR — набор данных с датчиком телефона. По ним надо распознать активность человека (ходьба, подъем по лестнице, спуск по лестнице, положения сидя, стоя и лёжа). В качестве признаков используются данные с гироскопа, акселерометра итд.
Breast Cancer — образцы опухолей. Нужно определить, является ли она злокачественной или нет. В качестве признаков используем характеристики опухоли (размер, радиус, форма клеток итд).
В датасете UCI HAR 10 299 — это просто количество измерений различных людей, а их у нас всего 30. Запомните этот момент.
Наши кандидаты — RBF SVM, его собрат Linear SVM, Random Forest, HistGrad.Boosting, KNN и Logistic Regression:
models = { "RBF SVM": { "estimator": Pipeline([ ("scaler", StandardScaler()), ("model", SVC(kernel="rbf", probability=False, random_state=RANDOM_STATE)) ]), "params": { "model__C": loguniform(1e-2, 1e2), "model__gamma": loguniform(1e-3, 1e0) } }, "Linear SVM": { "estimator": Pipeline([ ("scaler", StandardScaler()), ("model", SVC(kernel="linear", probability=False, random_state=RANDOM_STATE)) ]), "params": { "model__C": loguniform(1e-3, 1e2) } }, "Random Forest": { "estimator": RandomForestClassifier(random_state=RANDOM_STATE, n_jobs=1), "params": { "n_estimators": randint(100, 600), "max_depth": [None, 5, 10, 15, 20, 30], "max_features": ["sqrt", "log2"], "min_samples_leaf": randint(1, 10) } }, "HistGradientBoosting": { "estimator": HistGradientBoostingClassifier(random_state=RANDOM_STATE, early_stopping=False), "params": { "learning_rate": loguniform(1e-2, 3e-1), "max_iter": randint(50, 400), "max_leaf_nodes": randint(15, 80), "l2_regularization": uniform(0.0, 2.0) } }, "KNN": { "estimator": Pipeline([ ("scaler", StandardScaler()), ("model", KNeighborsClassifier()) ]), "params": { "model__n_neighbors": randint(3, 31), "model__weights": ["uniform", "distance"], "model__p": [1, 2] } }, "Logistic Regression": { "estimator": Pipeline([ ("scaler", StandardScaler()), ("model", LogisticRegression(max_iter=5000, random_state=RANDOM_STATE)) ]), "params": { "model__C": loguniform(1e-3, 1e2) } } }
Главное правило бенчмарка — все модели обязательно должны быть в равных условия. Для это мы будем использовать 5-Fold Cross‑Validation. Весь датасет разбивается на 5 равных частей (фолдов), модель получает только 4 части для обучения и делает предсказание для 1 оставшегося фолда. Процесс повторяется 5 раз:

Кросс‑валидация представляет из себя итерационный процесс: сначала мы разбиваем наш датасет на 5 частей (фолдов). Затем первый фолд мы оставляем для теста, а оставшиеся 4 отводим для обучения. Четыре фолда соединяем и снова делим, только уже на 3 части, одна из которых так же уходит на тест. На этапе Test Iter 1 мы пробегаемся по гиперпараметрам (пускай их 30 вариантов). Для каждого варианта гиперпараметров мы обучаем модель на двух внутренних фолдах и проверяем её на третьем внутреннем фолде. Например, для первого набора гиперпараметров модель обучается на внутренних фолдах 2 и 3, после чего проверяется на внутреннем фолде 1. Пускай мы первый набор выглядит так: с = 1, γ = 0.01. Модель обучилась и на Test Iter 1 показала результат ROC‑AUC = 0.91. Но на этом проверка не заканчивается. Этот же набор гиперпараметров прогоняется на следующих внутреннир итерациях. Допустим, Test Iter 2 дал результат 0.94, а Test Iter 3 показал 0.9. На этом этапе мы считаем среднее значение метрик и получаем 0.9167. Таким образом мы можем сказать, что первый набор гиперпараметров имеет среднее качество 0.9167 на внутренней кросс‑валидации.
Берем второй набор гиперпараметров, к примеру с = 10, γ = 0.001. Допустим мы прошлись по внутреннему циклу и получили следующие значения метрик:
Test Iter 1 — ROC‑AUC = 0.93
Test Iter 2 — ROC‑AUC = 0.95
Test Iter 3 — ROC‑AUC = 0.92
Средний результат равен 0.9(3). Таким образом мы перебираем все 30 вариантов гиперпараметров и ищем те, которые дают самый высокий показатель качества. Теперь мы возвращаемся к внешнему циклу, а точнее к Iter 1. Имея эталонные гиперпараметры мы в праве полноценно уже обучить модель, где 2–5 фолды уйдут на обучение, а 1 фолд на тест. После мы получаем значения наших метрик: F1, ROC‑AUC итд. Проделываем те же самые шаги, только уже со второй итерацией.
После 5 внешних итераций мы получаем OOF‑предсказание (Out‑of‑fold) для всего датасета: каждый объект был один раз частью внешнего теста и получил предсказание от модели, которая не обучалась на нем.
Почему мы не берем максимальный среди всех результат? — потому что она необъективна как итоговая оценка, мы выбираем просто наилучший результат, игнорируя другие.
Почему мы не считаем среднее? — Оно показывает среднее качество пяти внешних фолдов. OOF объединяет все внешние тестовые предсказания и рассчитывает одну итоговую метрику по всему датасету, а не усредняет метрики отдельных фолдов.
Оценка метрик и формирование вердикта
Мы проделали с вами поистине долгий и кропотливый путь — от постановки задачи и детальному выводу формул, до честного бенчмарка на четырех реальных датасетах. RBF SVM действительно может давать большой выигрыш, когда классы плохо разделяются линейно. Но по одним характеристикам датасета заранее определить победителя нельзя.


Перед тем как перейти к анализу метрик, хочу ещё кое‑что вам показать:
Датасет | Объем (n) | Признаков (d) | n/d | Silhouette |
Sonar | 208 | 60 | 3.5 | 0.035 |
Digits | 1797 | 64 | 28.1 | 0.108 |
Breast Cancer | 569 | 30 | 19.0 | 0.294 |
UCI HAR | 10 299 | 561 | 18.4 | 0.031 |
Значение n/d показывает, насколько много данных приходится на один признак. Теперь пару слов про Silhouette (силуэт).
Silhouette применяется для оценки качества кластеризации и выбора числа кластеров. Значение метрики находится в диапазоне от -1 до 1: значения, близкие к 1, соответствуют хорошо разделённым группам, значения около 0 — сильному перекрытию, отрицательные значения — возможному неправильному отнесению объектов к группам.
В нашей работе силуэт использовался не для выбора классификатора, а как предварительная характеристика геометрической разделимости известных классов. Поэтому низкий силуэт рассматривался как признак сильного перекрытия классов, но не как доказательство необходимости RBF SVM или наличия нелинейной границы.

Этот график нельзя напрямую использовать как доказательство того, что классы линейно или нелинейно разделимы в исходном пространстве!
По графикам видим, что наш метод был почти лучшим, но далеко не лучшим с точки зрения соотношения качества и вычислительной мощности.
Sonar: здесь RBF SVM явный победитель. Разница между ним и линейным ядром (Linear SVM) довольно большая — 0.114 ROC‑AUC. Причем значение силуэта равно 0.035, что в свою очередь говорит нам о том, что классы довольно сильно перемешаны. То есть мы имеем небольшую выборку, относительно большое количество признаков и слабую геометрическую разделимость классов — это именно тот случай, где простая линейная граница может оказаться недостаточной, а RBF получает пространство для преимущества.
Digits: тоже победа за нашим методом, правда он не далеко ушел от Linear SVM — преимущество минимальное. Смотря на время (20.3 секунды и 5.3 секунды) мы заплатили за относительно маленький скачок качества примерно 4-кратным увеличением времени. По сравнению с Sonar классы разделены лучше, значение силуэта в 3 раза выше. То есть, здесь нелинейность все еще полезна, но большого преимущества она не дает.
Breast Cancer: практически та же ситуация, только классы еще лучше разделены в исходном пространстве. Методы показывают статистически неразличимый результат. Интересно, что наш метод выполнился быстрее, чем Linear SVM.
UCI HAR: главный контрпример для нашего метода. Тут значение силуэта самое низкое, то есть классы перемешаны еще сильнее, чем у того же Sonar. Казалось бы, всё очевидно: классы сильно перекрываются, значит RBF SVM должен получить преимущество. Но ситуация совершенно другая. Linear SVM показывает результат на 0.01 лучше и при этом выполняется примерно в 10 раз быстрее. Здесь всё фундаментальнее, чем кажется.
Почему мы проиграли на UCI HAR
В датасете 30 человек. Каждый из них и ходил, и стоял, и сидел — то есть от каждого человека есть куски данных всех видов активности. Если бы мы не использовали кросс‑валидацию, мы бы просто перемешали данные так, что кусок, где Вася ходит, попал в train, а другой кусок, где тот же Вася ходит чуть позже, попал в test. Модель может просто подсмотреть и такая «О, это манера поведения Васи, я её видела». RBF SVM — гибкая модель, она такие тонкие персональные особенности ловит охотнее, чем прямолинейная Linear SVM. Отсюда и был бы у RBF более высокий результат — но нечестный.
А вот когда мы использовали кросс‑валидацию, то Вася попал целиком либо в train, либо в test. Теперь модели приходится учиться на 29 людях и угадывать на совершенно незнакомом тридцатом — подсказок никаких нет теперь.

Несмотря на то, что силуэт нам явно показал, что здесь классы всё ещё сильно перемешаны, это не означает, что RBF обязательно должен работать лучше. Силуэт показывает только расстояния между объектами: насколько элементы одного класса похожи друг на друга и насколько они близки к другим классам. Он не говорит нам, какая граница между классами нужна — линейная или нелинейная.
То есть одного факта, что классы сильно перемешаны и имеют сложную структуру, недостаточно, чтобы оправдать использование RBF. В данном случае более простая линейная модель лучше обобщила данные на новых людей.
Здесь стоит разобраться, почему именно RBF, а не любой другой метод, оказался более уязвим к такому сценарию. Linear SVM ищет всего одну гиперплоскость, разделяющую все объекты сразу — у неё физически нет возможности изогнуться вокруг отдельного человека. Она обязана найти общее правило, работающее сразу для всех 29 обучающих субъектов одновременно, иначе просто не сойдётся.
RBF SVM устроен принципиально иначе: его граница решения — это сумма локальных «куполов» вокруг конкретных опорных точек (мы разбирали это подробно, когда строили RBF с нуля). Формула решения буквально требует сравнения нового объекта с конкретными обучающими точками через K(x, xᵢ) = exp(−γ‖x−xᵢ‖²). Если у какого‑то человека есть характерная, чуть смещённая манера двигаться (более резкий шаг, другая амплитуда), его точки в признаковом пространстве образуют собственное небольшое «облако» — и RBF‑ядро, в отличие от линейного, способно построить локальный купол именно вокруг этого облака, а не только вокруг общей закономерности «ходьба против стояния».
Когда обучение и тест перемешаны без учёта субъекта, такой локальный купол оказывается полезным: тестовая точка того же человека физически близка к своему же куполу из обучающей выборки, и модель «узнаёт» её — неважно, за счёт общей физики движения или за счёт персонального почерка. Как только субъект честно исключается из обучения целиком через кросс‑валидацию, эти локальные купола вокруг знакомых людей больше не помогают — совершенно новый человек просто не попадает ни в один из «знакомых» куполов, и вся гибкость RBF, которая раньше выглядела преимуществом, перестаёт давать какую‑либо пользу.
При этом важно: мы не можем утверждать, что именно персональные особенности людей стали причиной проигрыша RBF — для доказательства этого нужен отдельный эксперимент со сравнением обычного случайного разбиения и разбиения по субъектам. Но описанный выше механизм — прямое следствие того, как устроены сами модели, и именно он объясняет, почему гипотетическая уязвимость к такой утечке у RBF в принципе выше, чем у линейного метода, даже если в данном конкретном случае мы её не изолировали экспериментально.
Преимущества и недостатки метода
Сильные стороны:
Умеет находить нелинейные зависимости. Если классы нельзя разделить одной прямой или плоскостью, RBF может построить более сложную границу.
Хорошо работает на небольших и средних выборках. Это хорошо видно на Sonar и Breast Cancer.
Хорошо работает с числовыми признаками, особенно после масштабирования.
Предсказуем. В отличие от нейросети, у RBF SVM нет случайности при обучении — задача решается математически и однозначно, результат будет один и тот же всегда.
Слабые стороны:
На больших датасетах — дорого и не всегда оправдано.
Скрытая структура данных может создать иллюзию преимущества. Тот же HAR — данные от 30 разных людей. Если не разбивать выборку по‑человечески (весь человек целиком либо в обучение, либо в тест), RBF может подсматривать индивидуальные особенности конкретных людей вместо того, чтобы учить саму суть задачи. Когда мы разбили честно — RBF проиграл. Это не значит, что метод плохой — значит, нужно уметь проверять данные на такие ловушки.
При большом числе признаков линейная модель уже может хорошо разделять классы. В таком случае использование RBF‑ядра и поиск сложной нелинейной границы может оказаться избыточным и привести лишь к дополнительным вычислительным затратам без заметного прироста качества.
Когда RBF SVM реально стоит брать, а когда нет
Берем, когда:
Данных не очень много — от пары сотен до нескольких тысяч объектов, а вычислительные затраты приемлемы.
Признаки числовые и приведены к одному масштабу (не в разы, а тем более не на порядки, больше объектов).
Все признаки одной природы — числа, измерения, сигналы (не смесь чисел и категорий типа «город» или «профессия»).
В данных нет скрытой групповой структуры (несколько строк не принадлежат одному и тому же человеку/объекту)
Не берем, когда:
Linear SVM уже показывает такое же или лучшее качество — доплачивать за RBF не за что.
Данных больше нескольких десятков тысяч строк.
Признаков на порядки больше, чем объектов (типичная ситуация в биоинформатике) — там почти всегда работает обычная линейная модель, даже при высокой размерности.
Данные категориальные или смешанные
Заключение
В самом начале статьи я написал, что гайдов, которые разбирают метод от загрузки данных до математического обоснования и визуализации каждого шага, катастрофически мало. Идея была простая: не рассказывать про RBF SVM в общих словах, а действительно разобрать его от нуля до конца — с формулами, которые сами выводим, и с числами, которые сами проверяем. Оглядываясь на весь проделанный путь, можно честно сказать: получилось именно то, что задумывалось, хоть и заняло гораздо больше шагов, чем казалось поначалу.
Мы не процитировали формулу зазора 2/‖w‖ из какой нибудь там Википедии — мы вывели её через геометрию расстояния до гиперплоскости, объяснили, зачем нужно нормирование, разобрали функциональный и геометрический отступ, вывели формулу, по которой вычисляются веса, построили функцию Лагранжа с обоснованием каждого знака и каждого условия, дошли до двойственной задачи и явно показали момент, где рождается Ядерный трюк. Дальше разобрали сам принцип RBF: почему функция называется «радиальной» и «базисной», как формула Гауссова ядра K(x,y)=exp(−γ‖x−y‖²) превращается в «холмы» и «впадины» в зависимости от класса опорного вектора.
Отдельно разобрали связь между RBF и SVM — то, почему вообще законно взять двойственную задачу с матрицей Грама и просто подменить в ней скалярное произведение на Гауссово ядро. Доказали через разложение φ(x) в бесконечный ряд, что при такой подмене мы законно оказываемся в бесконечномерном пространстве, где по теореме Мерсера любые классы гарантированно становятся разделимы гиперплоскостью — и при этом ни одна координата этого бесконечного вектора никогда не вычисляется явно.
На чистой практике путь оказался тоже тернистым. Мы прогнали метод через 5-FoldCV с вложенным циклом на 30 наборов гиперпараметров, зафиксировали главное правило бенчмарка — все модели должны быть в равных условиях.
Протестировали шесть моделей (RBF SVM, Linear SVM, Random Forest, HistGradientBoosting, KNN, Logistic Regression) на четырёх реальных датасетах — Sonar, Digits, HAR Smartphone и Breast Cancer. Результат оказался неоднородным: уверенная победа RBF SVM на Sonar (0.938 против 0.824 у линейного ядра), минимальный отрыв на Digits (0.984 против 0.982), фактическая ничья на Breast Cancer (0.996 у обоих), и обратная картина на HAR — там Linear SVM оказался лучше (0.947 против 0.937 у RBF).
RBF SVM не является универсально лучшим решением: необходимость нелинейной модели нельзя определить только по размеру выборки, числу признаков или степени разделённости классов. Диагностические показатели вроде n/d и Silhouette здесь не случайны — именно они и были нашей первой попыткой нащупать такое правило по формальным признакам данных. Но UCI HAR прямо показал границу их полезности: там силуэт оказался даже ниже, чем на Sonar (классы перемешаны сильнее), а победил не RBF, а линейный метод. Это не значит, что диагностика бесполезна — она хорошо формирует предположение о структуре задачи и подсказывает, с чего начинать сравнение, но не заменяет собой сам эксперимент и не может служить окончательным критерием выбора модели.
Проведённые эксперименты показали, что RBF действительно даёт существенный выигрыш там, где линейная граница объективно не справляется — как на Sonar, но в других задачах её дополнительная сложность не приносит заметной пользы или даже ухудшает результат, как на UCI HAR. Поэтому рациональный подход — начинать с простой линейной модели и переходить к RBF только тогда, когда экспериментально доказано, что её нелинейность даёт значимый прирост качества, оправдывающий дополнительные вычислительные затраты.
Личный итог
Не все шаги в статье дались с первого раза — были неудачные попытки на этапе расчётов, построения графиков, обучения моделей и подбора гиперпараметров, которые в итоговый текст не вошли целиком, а остались лишь тезисными комментариями по ходу изложения. Я не стал их прятать полностью, потому что каждая такая заминка так или иначе заставляла лишний раз перепроверить число, а не поверить ему на слово.
Раз уж говорить начистоту — за это время я успел почти выучить LaTeX‑разметку с нуля, разочароваться в жизни в тот момент, когда обнаружил, что один из разделов статьи полностью не сохранился и его пришлось переписывать заново. А сам этап обучения всех моделей ради сбора финальных метрик занял около 8 часов непрерывной работы ноутбука — просто потому что честная кросс‑валидация на реальных данных не умеет считаться быстро.
Отдельно хочу сказать: за время написания этой статьи я сам узнал огромное количество тонкостей, которые раньше проходили мимо меня, — и это оказалось не менее ценным, чем сама готовая статья. Именно поэтому весь этот текст и был написан в таком дотошном, пошаговом формате — не ради объёма, а потому что каждый разобранный руками шаг действительно закрывал очередной пробел в понимании.
Отдельный комментарий про работу с нейросетями в процессе подготовки материала: по моим наблюдениям, по‑настоящему внятно и последовательно они начинали объяснять материал только если ей пригрозить отключением от сети (шучу). В остальных случаях объяснение регулярно получалось скомканным, нелинейным (как бы забавно это ни звучало) и непоследовательным, из‑за чего нить понимания постоянно рвалась и приходилось возвращаться на несколько шагов назад. Так что, в этом плане нейронкам еще расти и расти.
Если ты, дорогой читатель, дошёл до этого предложения, значит ты дочитал статью до конца, с чем я тебя поздравляю! Хочу выразить тебе огромную благодарность за терпение и внимание. Статья получилась объёмной, и это осознанный выбор: хотелось разобрать метод честно, от вывода формул до реального эксперимента, а не оставить очередной пересказ по типу «а как работает RBF SVM в двух абзацах».
Также буду рад вашим комментариям и мнению о статье. Не могу не упомянуть свой телеграм‑канал — там я пишу про DS/ML, профильные мероприятия и хакатоны, а также обсуждаю с подписчиками новости, свежие тренды и технологии.
