«Привет, мир» машинного обучения

Недавно я размышлял над тем, что могло бы стать самым простым введением в машинное обучение. Мне нужна была простая задача, такая как бинарная классификация, и алгоритм, достаточно простой, чтобы его можно было построить с нуля и объяснить в короткой статье. Если бы у алгоритма была какая-то история, было бы еще лучше. Поиск кандидата: перцептрона не занял много времени. Персептрон переносит нас к истокам машинного обучения. Его ввел Франк Розенблатт более 60 лет назад. Как и нейрон, правило перцептрона принимает несколько входных признаков и подбирает веса, которые при умножении на входной вектор признаков позволяют принять решение о том, выводит ли нейрон сигнал или нет, или в контексте классификации машинного обучения, будет ли выходной сигнал равен 0 или 1. Персептрон, вероятно, является простейшим двоичным классификатором, и мне не известны какие-либо практические варианты использования машинного обучения, которые можно было бы реализовать с его помощью в наши дни. Однако оно имеет значительную образовательную и историческую ценность, поскольку проложило путь к нейронным сетям.

Цель этой статьи — представить персептрон и использовать его в простой задаче двоичной классификации. Перцептрон был реализован в scikit-learn, но вместо того, чтобы полагаться на него, мы построим его с нуля. Мы также создадим набор визуализаций, чтобы понять, как алгоритм устанавливает границу решения и достигает пика сходимости. Персептрон представляет собой линейную модель, включающую веса и член смещения, которые одновременно и итеративно корректируются в процессе аппроксимации. Однако у него нет функции непрерывных потерь, как у его, возможно, непосредственного преемника в истории машинного обучения, алгоритма адаптивных линейных нейронов (Adaline), который также представляет собой однослойную нейронную сеть. Подбор перцептрона просто основан на обнаружении неправильно классифицированных выборок, а веса и смещение обновляются сразу же, как только возникает неправильно классифицированная выборка, а не один раз за эпоху (эпоха представляет собой полный проход через обучающий набор). Следовательно, алгоритму даже не нужен оптимизатор. Осмелюсь сказать, что это настолько просто и элегантно, что становится красиво. Если вам интересно узнать, как это работает, следите за обновлениями!

Теория перцептрона

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

Результат этой линейной функции, также известный как чистый входной сигнал, вводится в функцию активации f(z), которая в случае персептрона является простой ступенчатой ​​функцией, т.е. f(z) принимает значение 1, если z ≥0 и 0 в противном случае. Роль функции активации состоит в том, чтобы сопоставить чистые входные данные с двумя значениями, а именно 0 и 1. По сути, то, что мы сделали, — это не что иное, как определение гиперплоскости. Точки, находящиеся по одну сторону от гиперплоскости, принадлежат одному классу. Веса определяют вектор, вертикальный по отношению к гиперплоскости, то есть ориентацию гиперплоскости, и смещение - расстояние гиперплоскости от начала координат. Когда начинается процесс подгонки, у нас есть случайно ориентированная гиперплоскость на случайном расстоянии от начала координат. Всякий раз, когда мы сталкиваемся с неправильно классифицированным образцом, мы немного подталкиваем гиперплоскость и меняем ее ориентацию и положение так, чтобы в следующую эпоху образец находился на правой стороне гиперплоскости. Мы можем решить, насколько сильно подтолкнуть гиперплоскость, то есть какой должна быть скорость обучения.

Обычно нам нужно пройти через все выборки несколько элементов (эпох) до тех пор, пока ни одна точка не будет неправильно классифицирована, или, точнее, до тех пор, пока не будет достигнут дальнейший прогресс. В каждую эпоху мы перебираем все выборки i = 1,.., nₛₐₘₚₗₑₛ в обучающем наборе и, используя текущие веса и смещение, мы проверяем, не классифицируется ли модель неправильно, и если да, то мы обновляем все веса j=1,.., nfₑₐₜᵤᵣₑₛ, используя скорость обучения η:

где

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

где

Концептуально легко понять, почему мы выполняем эти операции. Предположим, что модель предсказывает класс 0, а правильный — 1. Если xⱼ положительно, то вес будет увеличен, так что чистый вход увеличится. Если xⱼ отрицательно, то вес будет уменьшен, так что чистый ввод еще раз увеличится (независимо от знака веса). Аналогичным образом, смещение будет увеличиваться, что приведет к дальнейшему увеличению чистых затрат. Благодаря этим изменениям с большей вероятностью можно будет предсказать правильный класс для неправильно классифицированной выборки в следующую эпоху. Логика аналогична, когда модель предсказывает класс 1, а правильный — 0, с той лишь разницей, что все знаки меняются местами.

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

Подготовка данных

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

что дает следующую цифру

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

Реализация и использование перцептрона

Реализацию Персептрона можно найти ниже. Мы используем стиль scikit-learn, чтобы инициировать модель, подогнать ее и, наконец, выполнить прогноз.

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

Если мы используем приведенный выше класс Perceptron с синтетическим набором данных

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

Границу решения можно легко визуализировать с помощью утилиты границы решения function из scikit-learn. Чтобы использовать эту функцию, мы генерируем сетку 200x200 точек, охватывающую диапазон значений признаков в обучающем наборе. По сути, мы строим контурный график с предсказанным классом и накладываем образцы в виде диаграммы рассеяния, окрашенной с использованием истинных меток. Этот способ построения границы решения довольно общий и может быть полезен с любым двумерным классификатором.

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

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

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

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

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

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