Статьи

Начало работы с машинным обучением

«Машинное обучение» — мистический термин. Большинство разработчиков вообще не нуждаются в этом в своей повседневной работе, и единственные подробности, о которых мы знаем, взяты из какого-то университетского курса 5 лет назад (который уже забыт). Я не специалист по машинному обучению, но мне довелось работать в компании, которая занимается этим, поэтому мне пришлось изучать основы. Я никогда не программировал реальные задачи машинного обучения, но получил хороший обзор.

Но что такое машинное обучение? Он инструктирует компьютер распознавать большие объемы данных (#bigdata hashtag — check). В каких случаях?

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

Как? Со многими различными алгоритмами и структурами данных . К счастью, которые уже написаны учеными-программистами, разработчики могут просто использовать их (конечно, с достаточным пониманием).

Но если алгоритмы уже написаны, то должно быть легко использовать машинное обучение? По иронии судьбы, самая сложная часть машинного обучения — это та часть, где человек говорит машине, что важно в данных. Этот процесс называется выбором функции . Что это за особенности, которые описывают данные таким образом, что компьютер может использовать их для идентификации значимых шаблонов. Я не эксперт по машинному обучению, но, как я понимаю, этот шаг — то, что большинство инженеров машинного обучения (или ученых-данных) делают ежедневно. Они не изобретают новые алгоритмы; они пытаются выяснить, какие комбинации функций для данных дают наилучшие результаты. И это процесс со многими «эвристиками», с которыми у меня нет опыта. (Это, конечно, упрощение, поскольку мои коллеги действительно занимались исследованиями и предлагали усовершенствования алгоритмов, но это научный аспект вещей)

Теперь я ограничусь только проблемами классификации, а остальное оставлю. И когда я говорю «лучшие результаты», как это измеряется? Существуют метрики «точность» и «отзыв» (их легче всего использовать для классификации на две группы, но есть способы применить их для классификации нескольких классов или нескольких меток). Если вам нужно классифицировать письмо как спам, а не как спам, ваша точность — это процент писем, помеченных как спам, от всех писем, помеченных как спам. И отзыв — это процент писем, помеченных как спам, от общего количества писем, помеченных как спам. Таким образом, если у вас есть 200 электронных писем, 100 из которых являются спамом, и ваша программа помечает 80 из них как спам правильно и 20 неправильно, вы получите 80% точности (80/80 + 20) и 80% отзыва (80/100 фактический спам). электронная почта). Хорошие результаты достигаются, когда вы набираете больше баллов по этим двум показателям. Т.е. ваш спам-фильтр хорош, если он правильно обнаруживает большинство спам-писем, а также не помечает не спам-письма как спам.

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

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

Как уже упоминалось выше, я должен был начать с выбора правильных функций. После нескольких итераций я в конечном итоге использовал: количество примеров в задаче, среднюю длину примера, количество назначений, количество лингвистических компонентов, которые необходимо обнаружить как часть решения, и данные о проблеме. взбитый или нет. Сложность (легкая, средняя, ​​сложная) определяется фактическими результатами участников олимпиады (средний балл: 0–8 баллов = сложный, 8–12 — средний,> 12 легких). Я не уверен, связаны ли эти функции со сложностью проблемы, поэтому я экспериментировал с добавлением и удалением некоторых. Я помещаю данные объектов в файл Weka arff , который выглядит следующим образом (attribute = features):

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@RELATION problem-complexity
 
@ATTRIBUTE examples NUMERIC
@ATTRIBUTE avgExampleSize NUMERIC
@ATTRIBUTE components NUMERIC
@ATTRIBUTE assignments NUMERIC
@ATTRIBUTE scrambled {true,false}
@ATTRIBUTE complexity {easy,medium,hard}
 
@DATA
34,6,11,8,false,medium
12,21,7,17,false,medium
14,11,11,17,true,hard
13,16,9,14,false,hard
16,35,7,17,false,hard
20,9,7,10,false,hard
24,5,8,6,false,medium
9,14,13,4,false,easy
18,7,17,7,true,hard
18,7,12,10,false,easy
10,16,9,11,false,hard
11,3,17,13,true,easy
...

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

Weka был рекомендован как хороший инструмент (по крайней мере, для запуска), и в нем много алгоритмов, которые можно просто использовать повторно.

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public static void main(String[] args) throws Exception {
    ArffLoader loader = new ArffLoader();
    loader.setFile(new File("problem_complexity_train_3.arff"));
    Instances trainingSet = loader.getDataSet();
    // this is the complexity, here we specify what are our classes,
    // into which we want to classify the data
    int classIdx = 5;
        
    ArffLoader loader2 = new ArffLoader();
    loader2.setFile(new File("problem_complexity_test_3.arff"));
    Instances testSet = loader2.getDataSet();
        
    trainingSet.setClassIndex(classIdx);
    testSet.setClassIndex(classIdx);
        
     // using the LMT classification algorithm. Many more are available  
     Classifier classifier = new LMT();
     classifier.buildClassifier(trainingSet);
        
     Evaluation eval = new Evaluation(trainingSet);
     eval.evaluateModel(classifier, testSet); 
 
     System.out.println(eval.toSummaryString());
  
     // Get the confusion matrix
     double[][] confusionMatrix = eval.confusionMatrix();
     ....
}

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

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

1
2
3
4
...
@DATA
34,6,11,8,false,?
12,21,7,17,false,?

Затем вы можете запустить классификатор:

01
02
03
04
05
06
07
08
09
10
11
12
ArffLoader loader = new ArffLoader();
loader.setFile(new File("unclassified.arff"));
Instances dataSet = loader.getDataSet();
 
DecimalFormat df = new DecimalFormat("#.##");
for (Enumeration<Instance> en = dataSet.enumerateInstances(); en.hasMoreElements();) {
    double[] results = classifier.distributionForInstance(en.nextElement());
    for (double result : results) {
        System.out.print(df.format(result) + " ");
     }
     System.out.println();
};

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

Как масштабируется этот подход, однако. Могу ли я использовать приведенный выше код для крупносерийной производственной системы? В Интернете вы обычно не запускаете задачи машинного обучения в режиме реального времени (вместо этого вы запускаете их как запланированные задачи), поэтому, вероятно, ответ «да».

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

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