Настройте алгоритм машинного обучения и разработайте свою первую функцию прогнозирования с помощью Java. Автомобили с автономным управлением, системы распознавания лиц и голосовые помощники — всё они разработаны на базе технологий и фреймворков машинного обучения. И это только первая волна. За следующие 10 лет новое поколение продуктов, преобразит наш мир, порождая новые подходы к разработке программ, продуктов и приложений.
Как Java-программист вы хотите поймать эту волну прямо сейчас, когда технологические компании начинают серьезно инвестировать в машинное обучение. То, что вы изучите сегодня, вы сможете использовать в течение последующих пяти лет. Только вот с чего начать? Данная статья призвана ответить на этот вопрос. Вы получите первые впечатления о принципах машинного обучения, следуя нашей небольшой инструкции по реализации и подготовке алгоритма машинного обучения. После изучения структуры алгоритма обучения и возможностей, которые вы можете использовать для его тренировки, оценки и выбора функции, обеспечивающей наилучшую точность прогнозов, вы получите представлении об использовании фреймворка для JVM (Weka) для построения решений на базе машинного обучения. Эта статья фокусируется на контролируемом машинном обучении, поскольку именно этот принцип наиболее распространен при разработке «умных» приложений.

Машинное обучение и искусственный интеллект

Машинное обучение эволюционировало из области искусственного интеллекта, цель которой — создание машин, способных подражать человеческому интеллекту. Хотя термин «машинное обучение» появился в информатике, искусственный интеллект не является новой областью науки. Тест Тьюринга, разработанный математиком Аланом Тьюрингом в начале пятидесятых годов 20-го века, является одним из первых тестов, предназначенных для определения наличия настоящего интеллекта у вычислительной машины. Согласно тесту Тьюринга, вычислительная машина доказывает наличие человеческого интеллекта тем, что выдает себя за человека, и последний не догадывается, что говорит с машиной.
Многие популярные сегодня подходы в машинном обучении базируются на идеях, которым уже десятки лет. Но последняя декада в вычислительной технике (и распределенных вычислительных платформах) привнесла достаточные мощности для применения алгоритмов машинного обучения. Большинство из них требуют огромного количества операций умножения матриц и других математических вычислений. Двадцать лет назад вычислительные технологии, позволяющие осуществить такие вычисления попросту не существовали, а сейчас они уже стали реальностью. Алгоритмы машинного обучения позволяют программам выполнять процесс улучшения качества и расширять свои возможности без участия человека. Программа, разработанная с использованием машинного обучения, способна самостоятельно обновлять или расширять свой собственный код.

Контролируемое обучение vs неконтролируемое обучение

Deep Learning, искусственный интеллект и машинное обучение для чайников: объяснение на примере
Контролируемое и неконтролируемое обучение являются двумя наиболее популярными подходами к машинному обучению. Для обоих вариантов необходимо давать машине огромное количество записей данных для построения соотношений и обучения. Такие собранные данные обычно называют «вектором признаков». Например, у нас есть некий жилой дом. В таком случае вектор признаков может содержать такие признаки, как: общая площадь дома, количество комнат, год постройки дома и так далее. При контролируемом обучении, алгоритм машинного обучения подготавливается для ответа на вопросы, связанные с векторами признаков. Для тренировки алгоритма, ему «скармливают» набор векторов признаков и связанных меток. Связанную метку предоставляет человек (учитель), и она содержит правильный «ответ» на заданный вопрос. Алгоритм обучения анализирует векторы признаков и правильные метки для поиска внутренней структуры и взаимоотношений между ними. Таким образом машина учится правильно отвечать на вопросы. В качестве примера можно рассмотреть некое интеллектуальное приложение для торговли недвижимостью. Его можно натренировать с помощью вектора признаков, включающего размер, количество комнат, и год постройки для набора домов. Человек должен присвоить каждому дому метку с правильной ценой дома, базируясь на этих факторах. Анализируя эти данные, умное приложение должно натренироваться, чтобы ответить на вопрос «Сколько денег я смогу получить за этот дом?».
Интересно читать о Java? Вступайте в группу Java Developer!
После того как процесс подготовки закончен, новые входные данные уже больше не маркируются. Машина должна быть способна правильно отвечать на вопросы, даже для не известных, не промаркированных векторов признаков. При неконтролируемом обучении, алгоритм строится таким образом чтобы предсказывать ответы без маркировки человеком (или даже без вопросов). Вместо определения маркировки или результата, алгоритмы неконтролируемого обучения используют большие массивы данных и вычислительные мощности для выявления ранее не известных взаимоотношений. Например, в маркетинге потребительских продуктов можно использовать неконтролируемое обучение для определения скрытых взаимосвязей или группирования клиентов, что в конце концов поможет усовершенствовать маркетинговую программу или создать новую. В этой статье мы сосредоточимся на контролируемом машинном обучении; в настоящее время этот подход используется чаще всего.

Контролируемое машинное обучение

Любое машинное обучение базируется на данных. Для проекта по контролируемому машинному обучению,нужно отметить маркерами данные таким образом, чтобы получить осмысленные ответы на задаваемый вопрос. Ниже, в Таблице-1, каждая запись информации о доме имеет метку «цена дома». Выявляя взаимосвязь между данными записей и ценой дома, алгоритм в итоге должен быть способным предсказать рыночную цену для домов не входящих в данный список. (Обратите внимание, площадь дома указана в квадратных метрах, а стоимость дома в евро).
Таблица 1. Список домов
Признак Признак Признак Метка
Площадь дома Количество комнат Возраст дома Ожидаемая цена дома
90 m2 / 295 ft 2 Комнаты 23 года 249,000 €
101 m2 / 331 ft 3 Комнаты n/a 338,000 €
1330 m2 / 4363 ft 11 комнат 12 лет 6,500,000 €
На ранних этапах вы, скорее всего, будете маркировать данные вручную, но в конце концов вы научите вашу программу делать это самостоятельно. Вы, вероятно, уже видели как работает такой подход с клиентами электронной почты, когда для перемещения письма в папку «Спам» вы отвечаете на вопрос «Является ли это письмо не желательным?». Когда вы отвечаете, вы обучаете программу распознавать письма, которые вы не хотите видеть. Спам-фильтр приложения обучается маркировать письма из того же источника, или содержащее такой же контент и управлять ими согласно соответствующим правилам. Маркированные наборы данных необходимы только для подготовки и тестирования. После того как этот этап окончен, алгоритм машинного обучения работает с немаркированными данными. К примеру, вы можете подать в алгоритм предсказания новую немаркированную запись данных о доме и он должен автоматически предсказать ожидаемую цену дома базируясь на «знаниях» полученных из подготовительных данных.

Как машина обучается предсказывать

Сложность контролируемого машинного обучения -- в поиске подходящей функции предсказания для определенного вопроса. Математически сложность состоит в том чтобы найти функцию которая получает на вход переменную х и возвращает предсказанное значение у. Эта функция гипотез (hθ) является результатом процесса подготовки. Часто функция гипотез также называется целевой функцией или функцией предсказания.
y = hθ (x)
В большинстве случаев, х представляет собой массив данных. В нашем примере -- это двухмерный массив из элементов, определяющих дом, состоящий из количества комнат и площади дома. Массив таких значений является вектором признаков. Задав конкретную целевую функцию, мы можем использовать её для предсказания каждого вектора признаков х. Чтобы предсказать цену дома, вы должны вызвать целевую функцию используя вектор признаков {101.0, 3.0}, состоящий из площади дома и количества комнат:
// целевая функция h (результат процесса обучения)
Function<Double[], Double> h = ...;

// определяем целевой вектор с площадью дома=101 и количеством комнат=3
Double[] x = new Double[] { 101.0, 3.0 };

// и предсказываем цену дома (метка)
double y = h.apply(x);
В исходном коде из примера-1, значения в массиве х представляют вектор признаков дома. Значение у, возвращаемое целевой функцией, -- это предсказанная цена дома. Целью машинного обучения является определение целевой функции, которая будет работать максимально точно при неизвестных входных параметров. В машинном обучении, целевая функция (hθ) иногда называется моделью. Эта модель является результатом процесса обучения.
Базируясь на маркированных тренировочных образцах, алгоритм обучения ищет структуры или шаблоны в тренировочных данных. Таким образом, он строит модель которая в целом хороша для этих данных. Как правило, процесс обучения носит исследовательский характер. В большинстве случае процесс повторяют многократно с использованием разных вариантов алгоритмов обучения и конфигураций. В итоге все модели оцениваются на основе метрик производительности, среди которых выбирается лучшая. И эта модель используется для вычисления предполагаемых значений для будущих не маркированных данных.

Линейная регрессия

Чтобы научить машину «думать», сначала нужно выбрать алгоритм обучения, который вы будете использовать. Например, линейную регрессию. Это – один из простейших и самых популярных алгоритмов контролируемого машинного обучения. Алгоритм предполагает что отношение между входными признаками и маркерами результата – линейно. Общая функция линейной регрессии, приведенная ниже, возвращает предсказанное значение путем суммирования всех элементов вектора признаков умноженных на параметр θ (тета). Этот параметр используется в процессе обучения для адаптации или «подстройки» функции регрессии на основе тренировочных данных.
hθ (x) = θ0 * 1 + θ1 * x1 + ... θn * xn
В функции линейной регрессии параметр тета и параметры признаков пронумерованы подстрочными индексами. Подстрочный индекс определяет позицию параметра (θ) и параметра признака (х) в векторе. Обратите внимание, что признак x0 является постоянным элементом сдвига и имеет значение 1 для вычислительных целях. В результате индекс значимых параметров таких, как площадь дома, начинается с x1. Так, если x1 присвоено первое значение вектора признаков (площадь дома), то x2 будет принимать следующее значение (количество комнат) и так далее. Пример-2 демонстрирует Java-реализацию функции линейной регрессии, математически обозначаемой как hθ(x). Для простоты, вычисления выполняются с использованием типа данных double. В методе apply(), предусмотрено, что первый элемент массива будет равен 1.0 и будет установлен за пределами этой функции. Пример 2. Линейная регрессия на Java
public class LinearRegressionFunction implements Function<Double[], Double> {
 private final double[] thetaVector;

 LinearRegressionFunction(double[] thetaVector) {
 this.thetaVector = Arrays.copyOf(thetaVector, thetaVector.length);
 }

 public Double apply(Double[] featureVector) {
 // с целью упрощения вычислений первый элемент должен быть равен 1.0
 assert featureVector[0] == 1.0;

 // простое последовательное сложение
 double prediction = 0;
 for (int j = 0; j < thetaVector.length; j++) {
 prediction += thetaVector[j] * featureVector[j];
 }
 return prediction;
 }

 public double[] getThetas() {
 return Arrays.copyOf(thetaVector, thetaVector.length);
 }
}
Чтобы создать новый экземпляр LinearRegressionFunction, нужно задать параметр θ. Этот параметр или вектор используется для адаптации общей функции линейной регрессии к лежащим в основе тренировочных данных. Параметр θ, используемый в программе, будет настроен в процессе обучения, базируясь на тренировочных примерах. Качество обученной целевой функции будет зависеть от качества подготовленных для обучения данных. В примере ниже мы используем LinearRegressionFunction для иллюстрации предсказания цены, базируясь на размере дома. Принимая во внимание то, что x0 должен быть константой со значением 1.0, целевая функция инициализируется, используя два параметра θ, при этом они являются результатом процесса обучения. После создания нового примера, цена дома площадью 1330 квадратных метров будет предсказываться как показано ниже:
// тета вектор, используемый в этом примере, является результатом процесса обучения
double[] thetaVector = new double[] { 1.004579, 5.286822 };
LinearRegressionFunction targetFunction = new LinearRegressionFunction(thetaVector);

// создание вектора признаков со значениями x0=1 (для удобства вычислений) и //x1=площадь дома
Double[] featureVector = new Double[] { 1.0, 1330.0 };

// выполняем расчеты
double predictedPrice = targetFunction.apply(featureVector);
На рисунке ниже вы можете видеть график целевой функции предсказаний (синяя линия). Он получен путем вычисления целевой функции для всех значений площади дома. График также содержит пары цена-площадь, используемые для обучения.
Сейчас график предсказаний выглядит достаточно хорошо. Координаты графика (положение и наклон) определяются вектором θ { 1.004579, 5.286822 }. Но как определить, что именно этот θ-вектор лучше всего подходит для вашего применения? Будет ли функция соответствовать лучше, если вы измените первый или может быть второй параметр? Для определения наиболее подходящего тета вектора вам нужна функция полезности, которая будет оценивать насколько хорошо целевая функция справляется с этой задачей. ПРОДОЛЖЕНИЕ СЛЕДУЕТ Перевод с английского. Автор — Грегор Рот (Gregor Roth), Software Architect, JavaWorld.
Что еще почитать:

Топ 5 библиотек машинного обучения для Java

5 технологических тенденций для построения успешной карьеры в 2018

Не переоцениваем ли мы машинное обучение?