В тіні випадкового лісу

1. Вступ
Це невеликий розповідь про практичних питаннях використання машинного навчання для масштабних статистичних досліджень різних даних в Інтернет. Також буде порушена тема застосування базових методів математичної статистики для аналізу даних.

2. Класифікація асесором
Асесор дуже часто стикається з нормально розподіленими наборами даних. Існує правило, згідно з яким при нормальному розподілі (його називають розподілом Гауса) випадкова величина вкрай рідко відхиляється більше ніж на три сігми (грецькою буквою «сигма» позначають квадратний корінь з дисперсії, тобто середньоквадратичне відхилення) від свого математичного очікування. Іншими словами: при нормальному розподілі більшість об'єктів будуть мало відрізнятися від середнього. Отже, гістограма буде мати наступний вигляд:

Така випадкова величина може корелювати з іншою величиною і навіть утворювати групи (кластери) за деякими ознаками схожості. Розглянемо невеликий приклад. Припустимо, що ми володіємо інформацією про масу і висоті об'єкта, а також знаємо, що в генеральній сукупності об'єкти бувають тільки двох класів (велосипед, вантажівка). Очевидно, що висота і маса вантажівки явно більше, ніж у велосипеда, отже, обидва показника надзвичайно цінні для класифікації.
Якщо ми відобразимо в двовимірному просторі хмара точок, координати яких задані досліджуваної парою множин, то буде очевидно, що точки не розподілені випадковим чином, а збираються в групи (кластери). Отже, простір можна розділити линів (гіперплощиною), яка дозволить відрізнити вантажні машини від велосипедів (наприклад, якщо маса понад 1000 кг, то це явно вантажівка).
Більш того, можна зафіксувати лінійну залежність між цими метриками (при збільшенні позиції по ординате відбувається явне збільшення абсциссе). Таким чином, точки шикуються в ряд від лівого нижнього кута до правого верхнього кута, немов навколо натягнутої нитки. Для більшої наочності додамо (методом найменших квадратів) пряму лінію регресійного аналізу, зазначимо центродиды кластерів, а також відобразимо графіки цих змінних:

Використовуючи наявні дані, обчислимо ступінь взаємозв'язку. Для подібних наборів даних математичної статистики застосовують лінійний коефіцієнт кореляції Карла Пірсона. Це досить зручно, так як він знаходиться в інтервалі від -1 до 1. Щоб обчислити його необхідно розділити ковариацию на твір середньоквадратичних відхилень множин (квадратний корінь з дисперсії). До речі, коваріація не зміниться, якщо поміняти місцями порівнювані множини, а якщо вони будуть однакові, то ми отримаємо дисперсію випадкової величини. Важливо відзначити, що залежність повинна бути строго лінійною.

Проте, існує ще ряд інших незалежних змінних (предиктори), які впливають на залежну (критеріальну) змінну. Немає функціональної залежності, яка дозволила б за формулою обчислити одну змінну, знаючи іншу. Наприклад, це як залежність росту людини від ваги (залежність існує, але немає формули, яка дозволить знаючи тільки вага точно обчислити зростання). А як діяти, якщо дуже багато змінних, і необхідно оцінити важливість кожної з них?
Одним з підходів до вирішення цієї задачі можна назвати застосування алгоритмів машинного навчання. Так, наприклад, алгоритм CART (був розроблений ще на початку 80-х років XX століття вченими Лео Брейманом, Джеромом Фрідманом, Чарлзом Стоуном і Річардом Олшеном) дозволить досить добре відобразити дерева рішень. Природно, він припускає, що всі вектори ознак вже розмічені асесором. Однак, для більш складних задач частіше застосовують Random forest або Gradient Boosting. Використовуючи згадані методи (а також логістичну регресію) візуально відобразимо ступінь важливості кожної змінної:

певна річ, вибір методів залежить від поставленого завдання і специфіки набору даних. Але в переважній більшості завдань автоматична класифікація починається з попереднього аналізу даних. Цілком ймовірно, що асесори і експерти зможуть побачити важливі закономірності просто вивчаючи візуальне подання даних і показники описової статистики (кореляція, мінімальне значення, максимальне значення, середнє значення, медіана, квартилі, дисперсія).
3. Автоматична класифікація
По суті, замість конкретного об'єкта (сайт, людина, машина, чи літак) ми стикаємося з набором його характеристик (маса, швидкість, наявність USB). Це означає, що відбувається процес перетворення сутності вектор ознак. Так як же бути з дійсно складними наборами даних?
Єдиного підходу не може існувати, так як є дуже багато специфічних вимог. Для автоматичного розпізнавання зображення потрібні одні алгоритми (наприклад, штучні нейронні мережі), а для складного лінгвістичного аналізу тексту потрібні зовсім інші.
У переважній більшості випадків машинне навчання починається з попередньої очистки даних. На цьому етапі може відбуватися видалення явно невідповідних об'єктів і більш прості способи класифікації. В наступному прикладі відбувається пошук центроїдів двох кластерів. В якості конкретної реалізації буде виступати досить популярне рішення (нещодавно опублікована друга версія Apache Spark).
import org.apache.spark.mllib.clustering.{KMeans, KMeansModel}
import org.apache.spark.mllib.linalg.Vectors

val data = sc.textFile(clusterDataFile).map(s => Vectors.dense(s.split(',').map(_.toDouble))).cache()

KMeans.train(data, 2, 100).clusterCenters.map(h => h(0) + "," + h(1)).mkString("\n")

Із збільшенням складності завдання зростає і складність систем машинного навчання. Так, наприклад, деякі пошукові системи використовують Gradient Boosting Machine для автоматичного аналізу якості сайтів. Такий алгоритм будує велику кількість decision tree, які використовують boosting (поступове поліпшення допомогою підвищення якості прогнозу попередніх decision tree). Перейдемо до прикладу. Ось кілька рядків з файлу вхідних даних:
0 1:86 2:33 3:79 4:77 5:74 6:81 7:90 8:63 9:8 10:27 11:33 12:29 13:99 14:14 15:92
1 1:100 2:82 3:18 4:8 5:73 6:27 7:18 8:11 9:25 10:46 11:79 12:88 13:62 14:12 15:20
0 1:98 2:53 3:79 4:74 5:26 6:60 7:64 8:89 9:67 10:91 11:22 12:95 13:90 14:35 15:87
0 1:89 2:17 3:4 4:96 5:89 6:31 7:13 8:99 9:55 10:59 11:78 12:43 13:20 14:90 15:62
0 1:20 2:88 3:14 4:99 5:62 6:40 7:58 8:26 9:28 10:25 11:16 12:49 13:20 14:5 15:84
0 1:6 2:94 3:100 4:10 5:89 6:88 7:40 8:2 9:87 10:95 11:61 12:65 13:37 14:81 15:55
1 1:99 2:100 3:42 4:12 5:98 6:4 7:52 8:56 9:29 10:79 11:80 12:44 13:28 14:99 15:49
0 1:5 2:42 3:11 4:14 5:31 6:98 7:54 8:33 9:85 10:48 11:93 12:49 13:85 14:73 15:3

Припустимо, що це деяка статистика по найважливішим поведінкових факторів. Перший символ рядка — це клас (строго номінативна мінлива: 0 або 1), а все інше — це характеристики об'єкта (індекс: значення). Цей набір даних необхідний для машинного навчання. Після цього модель зможе самостійно виявляти клас по вектору ознак. Існує другий такий же файл, але з іншими спостереженнями. Він необхідний для перевірки правильності роботи автоматичної класифікації.
Для навчання і перевірки необхідно безліч спостережень, де вже точно відомі класи (як варіант, можна удатися до поділу вибірки на навчальну і перевірочну частини). Ми порівняємо фактичний (вже відомий) клас з результатом роботи алгоритмів машинного навчання. В ідеалі, ROC-крива точності роботи бінарного класифікатора повинна мати площу під кривою, яка близька до одиниці. Але, якщо вона близька до половини простору, то це буде схоже на процес випадкового вгадування, що, природно, категорично неприйнятно.
Припустимо, що за умовою задачі необхідно використовувати дихотомічну (бінарну) класифікацію. В даному прикладі кількість дерев буде 180 (з обмеженням висоти до 4), а кількість класів строго дорівнює двом. У моделі є метод predict, який виконує класифікацію (повертає мітку класу), вказаному в якості аргументу вектора:
import org.apache.spark.mllib.tree.GradientBoostedTrees
import org.apache.spark.mllib.tree.configuration.BoostingStrategy
import org.apache.spark.mllib.tree.model.GradientBoostedTreesModel
import org.apache.spark.mllib.util.MLUtils

/**
* Використовуємо класифікатор з вказаними параметрами
*/
val boostingStrategy = BoostingStrategy.defaultParams("Classification")
boostingStrategy.numIterations = 180
boostingStrategy.treeStrategy.numClasses = 2
boostingStrategy.treeStrategy.maxDepth = 4
boostingStrategy.treeStrategy.categoricalFeaturesInfo = Map[Int, Int]()

/**
* Завантаження даних для навчання і для перевірки
*/
val data = MLUtils.loadLibSVMFile(sc, trainingDataFile)
val newData = MLUtils.loadLibSVMFile(sc, testDataFile)

/**
* Навчання моделі
*/
val model = GradientBoostedTrees.train(data, boostingStrategy)

/**
* Виявимо точність моделі
*/
val cardinality = newData.count
val errors = newData.map{p => (model.predict(p.features), p.label)}.filter(r => r._1 != r._2).count.toDouble

println(" Всього спостережень = " + cardinality)
println(" Помилок = " + errors)
println(" Точність = " + errors / cardinality)

Такі алгоритми можна використовувати і в цілях підвищення конверсії сайтів. Подібний дихотомический (бінарний) класифікатор дуже зручний у практичному застосуванні, так як вимагає тільки вектор ознак, а повертає його клас. Можна в автоматичному режимі розмічати величезні обсяги інформації. Найважливішим недоліком можна назвати необхідність наявності великого набору вже розмічених векторів ознак. Повторюся, що дуже багато чого буде залежати від точності підбору ознак і від первинного аналізу даних.
Машинне навчання і методи математичної статистики застосовуються в самих різних галузях науки і виробництва. Дуже складно провести межу між завданнями оцінки ефективності поведінкових факторів на порталах і завданнями машинного навчання. Статистика застосовується у всіх сферах життя людини. Більше того, застосування згаданих алгоритмів машинного навчання дозволяє виконувати такі завдання, які ще кілька років тому були неможливими без участі людини.

5. Додаток

Код на R для візуалізації показаних прикладів.
#
# Завантаження даних
#
dataset <- read.csv("dataset.csv") # заздалегідь зроблений setwd(...)

#
# Лінійна регресія та кластерний аналіз
#
plot(dataset, ylim=c(1, 25), xlim=c(1, 25), pch=20)
abline(lm(dataset$b ~ dataset$a), col = "blue")

clusters <- kmeans(dataset, 2, nstart = 50)
dimnames(clusters$centers) <- list(c("Альфа", "Beta"), c("X", "Y"))
points(clusters$centers, col="red", cex=1, pch=8)
text(clusters$centers, row.names(clusters$centers), cex=1.3, pos=4, col="red")

plot(hclust(dist(dataset, method="евклідовому"), method="average"), col="blue")

#
# Відображення на графіку і лінійний коефіцієнт кореляції
#
title <- paste("Pearson's product-moment correlation: ", cor(dataset$a, dataset$b))
plot(dataset$a, type="o", main=title, lty=2, ylim=c(0, 25), xlim=c(0, 10), col = "red")
lines(dataset$b, type="o", pch=22, lty=2, col="blue")

#
# Бібліотеки і завантаження даних
#
library(party)
library(randomForest)
library(gbm)

rd <- read.csv("rd.csv")
rd$class <- as.factor(rd$class)

#
# Дерево рішень
#
plot(ctree(class ~ ., data = rd))

#
# Random Forest
#
varImpPlot(randomForest(class ~ ., data = rd, importance=TRUE,
keep.forest=FALSE, ntree=15), main="Random Forest")

#
# GBM
#
summary(gbm(class ~ alpha + beta + gamma + delta + epsilon + zeta,
data=rd, shrinkage=0.01, n.trees=100, cv.fold=10, interaction.depth=4,
distribution="gaussian"))

#
# Логістична регресія
#
summary(glm(formula = class ~ ., data = rd, family = біном))

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.