Немного математики и немного кода, машинное обучение на рубине

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

Что ожидать?

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

Почему?

Скорее всего, раз вы читаете это, вас интересует машинное обучение. Для разработчика это похоже на зверя, совсем не похожего на то, с чем мы обычно имеем дело. По крайней мере, так было для меня. Что действительно действительно помогло мне понять некоторые вещи, так это написание моего собственного небольшого алгоритма линейной регрессии. Нет, не волнуйтесь, это не сумасшедший подвиг - на самом деле, это именно то, что мы собираемся сделать через секунду. Я надеюсь, что рассмотрение этой темы в первую очередь с помощью кода может помочь некоторым из моих коллег-разработчиков Ruby немного легче освоить машинное обучение.

Линейная регрессия, краткий обзор

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

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

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

Итак, основной поток (и терминология) следующий: у вас есть некоторые значения x, которые вы будете использовать для прогнозирования значений y. В частности, x часто называют вашими функциями, а y - вашими метками. Например, вы скайнете и посылаете своих терминаторов против этих надоедливых людей. У вас есть следующие возможности:

количество терминаторов: 10, количество людей: 1000, количество собак: 10

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

[терминаторы: 10, люди: 1000, собаки: 10] - ›человеческие потери

Гипотеза

Часть, которая что-то делает с функциями, чтобы вычислить метку, называется гипотезой.

Линейная регрессия довольно проста, что означает, что гипотеза тоже. Все, что мы делаем, - это присваиваем «вес» каждой функции. Этот вес умножается на функцию, и все эти продукты суммируются.

Результат - ценность вашей метки.

Предположим, у нас есть следующие веса: терминатор / вес: 200, люди / вес: -0,1, собаки / вес: -10

В результате человеческие потери: 1800

Хм, это не похоже на то, что наши нынешние веса делают отличную работу, в конце концов, человеческих потерь не может быть больше, чем людей! (Спасибо @mediafinger за указание на это :))

Поскольку это контролируемая проблема, у нас уже будет несколько значений функций и известных меток в начале. В нашем примере Скайнет, возможно, записал 3 сражения с людьми и собаками, а также связанные с этим человеческие потери (я надеюсь, что пример не слишком ужасный - я начал с этого примера, а теперь я должен его придерживаться. С другой стороны, нет собачьи потери!):

10 терминаторов, 1000 человек, 10 собак: 900 человеческих потерь

5 терминаторов, 800 человек, 2 собаки: 600 человеческих жертв

12 терминаторов, 2500 человек, 3 собаки: 1800 человеческих жертв

Каждая из этих строк называется экземплярами.

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

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

Результатом будет [1800, 900, 2120]. Мы отошли совсем немного на все сражения.

Наконец-то код!

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

После того, как мы получим лучшие веса, мы можем использовать их для предсказания неизвестных ярлыков (Скайнет может планировать крупномасштабную атаку на людей, отправив 3000 терминаторов против 19000 человек и 220 собак. Он хотел бы знать свои шансы заранее).

Функция стоимости

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

В линейной регрессии мы используем среднеквадратичную ошибку как функцию стоимости. Страшная математика!

Хорошо, давайте рассмотрим этот шаг за шагом:

тета: это вектор со всеми вашими весами. J (тета) означает: стоимость при использовании этих весов.

m: количество экземпляров. В нашем примере выше это 3 (строки).

ошибка: предсказанная метка - метка

в квадрате: почему? Ну, во-первых, вы избавляетесь от знака, если ошибка отрицательная. Это упрощает расчет стоимости.

Странный математический символ перед ошибкой: сумма. Суммируем все квадраты ошибок. Например, в рубине это был бы призыв к сокращению.

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

Если мы используем эту функцию стоимости для расчета стоимости для наших начальных весов [200, -0,1, -10], мы получим следующий результат: 167066,67 (округлено). Это сумма всех наших Js (функция стоимости).

Хорошо ... и что теперь? Остается только одно: нам нужно что-то сделать, чтобы снизить стоимость, чтобы максимально приблизить ее к нулю. Это означает, что нам нужно найти оптимальные веса для наших данных.

Нормальное уравнение

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

В любом случае, вернемся к обычному уравнению: я добавлю сюда формулу, а затем реализацию на Ruby, но не буду вдаваться в подробности, поскольку для правильного объяснения этого потребуется довольно много математики. Я бы рекомендовал вам углубиться в градиентный спуск и попытаться выяснить, как реализовать его в Ruby, если вы хотите пойти немного глубже. Видишь ли, у меня даже есть для тебя домашнее задание;)

(Градиентный спуск будет использовать функцию стоимости, если вам интересно, где она на самом деле используется.)

Нормальное уравнение даст нам следующие веса: [5,0, 0,675, 17,5]. Время для проверки работоспособности, вперед, используйте эти значения, чтобы предсказать метки. Думаю, вы будете хоть немного впечатлены! Теперь не ожидайте, что всегда найдете такие идеально совпадающие веса (кстати, я просто выбрал несколько случайных значений, когда выбирал функции и метки. Да, представьте себе, я не использовал данные из реальных, апокалиптических битв).

Обобщить:

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

Вот весь код вместе с некоторыми тестами, которые отражают то, что мы проверяли раньше:

Как я могу это использовать?

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

Некоторые отличные веб-сайты для поиска наборов данных:







https://www.kaggle.com/datasets

Несколько вещей, о которых я не упомянул

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

Что дальше?

По сравнению с python или R, ruby ​​может быть не лучшим языком для машинного обучения. Конечно, это связано не с самим языком, а с доступными библиотеками. Я лично рекомендую поиграть с блокнотами jupyter, возможность строить графики среди множества других интересных вещей, которые вы можете делать, делает все это намного более увлекательным.

При этом ресурсов машинного обучения предостаточно даже для Ruby:





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



И есть несколько интересных подкастов по data science / ml / ai:





крикнуть @mediafinger за обнаружение ошибки в тексте!