Искусственная нейронная сеть (ANN) представляет собой вычислительную нелинейную модель, основанную на нейронной структуре мозга, которая может научиться выполнять такие задачи, как классификация, прогнозирование, принятие решений, визуализация и другие, просто рассматривая примеры.
Искусственная нейронная сеть состоит из искусственных нейронов или элементов обработки и организована в три взаимосвязанных слоя: вход, скрытый, который может содержать более одного уровня, и выход.
Искусственная нейронная сеть (Artificial neural network) делится на четыре вида:Введение в искуственные нейронные сети
Нейронные сети ANN Широкий класс моделей, изначально создавались для моделирования работы мозга Существует много видов нейронных сетей. Они различаются в зависимости от
- Решаемой задачи регрессия/прогнозирование, классификация, кластеризация
- архитектуры модели
- алгоритма обучения сети
Немного биологий • Дендриты – получают информацию
• Тело клетки – обрабатывает информацию
• Аксон – передает обработанную информацию другим нейронам
• Синапсы – соединяют аксон и дендриты других нейронов
• X1 X2 … Xp от других нейронов или от рецепторов
• Входы усиливаются или ослабляются умножением на веса w
• I = линейная комбинация всех входов
• Активационная функция f преобразует сумму в выходной сигнал
• Выходной сигнал поступает к другим нейронам или является выходом нейронной сети
Нейронные сети имеют различные слой, так как работа нейронов организована послойно * Входной слой - каждый нейрон имеет ровно один вход от внешней среды * Скрытый слой - входы — от каждого нейрона предыдущего слоя * Выходной слой - Выход каждого нейрона является реакцией сети
Входной слой и выходной слой обычно не считают так они являются передатчиками переменных, а где происходит вся магия так это в скрытом слое. Слоёв может быть 0, 1 ну или больше. Важно запомнить нейроны передают информацию только следующим слоям
Выбор модели ANN зависит от задачи ну и от аналитика, вот что стоит учитывать при выборе • входные нейроны • скрытые слоя • нейроны в каждом скрытом слое • выходные нейроны • активационная функция каждого нейрона • ВЕСА каждого соединения Обучение нейронной сети = определение значений весов. Остальные параметры задаются аналитиком заранее.
Давайте расмотрим как работают нейронные сети внутри “чёрного ящика”"
Метод обратного распространения (Back Propagation) задает поправки весов. Обучение нейронной сети пройсходит за счёт back propagation. Сначала мы проводим данные через нейроннурю сеть со случайными весами, затем делаем back propagation и корректируем веса, так что бы ошибка уменьшилась. Это выглядит примерно вот так: • Подадим на вход сети 1 наблюдение. Подправим веса так, чтобы ошибка 2 наблюдения уменьшилась • Продолжаем так до последнего наблюдения • Первый цикл обучения закончен. • Выполняем эти циклы, пока общая ошибка Е не станет маленькой.
Формула для изменения весов по методу обратного распространения E(W)= sum(Sigma)???[Yi – Vi(W)]^2
Обучение в нейронной сети останавливают, когда достигнут глобальный минимум на поверхности ошибок, но проблема как понять когда глобальный минимум достигнут.
Есть проблема в нейронных сетях, это когда сеть запоминает обучающую выборку - это называется Переподгонка (over fitting)
Раньше когда пользовались одним нейроном для решения задач, была задача которую долгое время не могли решить одним нейроном. Были очень спорные времена, давайте рассмотрим эту самую задачу:
Классификация посредством нейронных сетей Задача “Исключающее или”
# Данные
x1 <- c(0,0,1,1)
x2 <- c(0,1,0,1)
y <- c(1,0,0,1)
z <- cbind(x1,x2,y)
Подгрузим библиотечку neuralnet
library(neuralnet)
Давайте поставим зерно случайных чисел и постройм модель в r
# Зерно датчика случайных чисел
i <- 12341
i <- i+1
set.seed(i)
# Модель нейронных сетей
nn <- neuralnet(y ~ x1 +x2, data=z ,hidden = 2,
linear.output=F)
Делаем прогноз
res.z <- compute(nn, z[, 1:2])
y.pred <- rep(-9999, 4)
y.pred[res.z$net.result[1:4] > 0.8] <- 1
y.pred[res.z$net.result[1:4] <0.2] <- 0
sum(y.pred != y)
## [1] 4
И строим граффик нейронной сети
# график нейронной сети
plot(nn)
А тут можно посмотреть веса в нейронных сетях
nn$weights
## [[1]]
## [[1]][[1]]
## [,1] [,2]
## [1,] 0.9613744085 -1.639694324
## [2,] -0.5866447765 0.699290771
## [3,] -0.1536362394 -1.235043457
##
## [[1]][[2]]
## [,1]
## [1,] 1.347678884
## [2,] -2.041921267
## [3,] -0.298992306
Давайте рассмотрим пример нейронной сети с библиотекой “nnet”
library(nnet)
Давайте посмотрим на данные (это встроенные данные в R)
Ежемесячные суммы пассажиров международных авиакомпаний, 1949-1960 годы
data(AirPassengers)
plot(AirPassengers)
Класс таблицы не подходит для анализа, nnet не работает с объектами класса ts
class(AirPassengers)
## [1] "ts"
Меняем класс данных, заодно логарифмируем, чтобы сезонные поправки стали аддитивными, Нейронной сети так будет проще, то есть пока будем прогнозировать логарифм ряда
g.series <- log(as.numeric(AirPassengers))
# Число наблюдений ряда
n.obs <- length(g.series)
# График ряда после преобразования
plot(1:n.obs, g.series, type = "l")
Ряд сезонный, потому у нейронной сети будет 12 входов 12 первых значений в строке - входные значения, 13-ое - выходное. Будем прогнозировать на 1 наблюдение вперёд
g.2 <- matrix(rep(0, 132*13), nrow = 132, ncol = 13)
# Заполняю матрицу данных отрезками ряда
for (i in 1:132)
{
g.2[i, ] <- g.series[i:(12 + i)]
}
# зерно датчика случайных чисел
set.seed(12345)
Обучаем нейронную сеть linout = TRUE иначе выходные значения сети будут между 0 и 1 maxit = 1000, так как иногда 100 итераций не хватает
g.net <- nnet(g.2[, 1:12], g.2[, 13], size = 6,
linout = TRUE, rang=0.1, decay=0.001, maxit = 1000)
## # weights: 85
## initial value 4421.801190
## iter 10 value 21.218283
## iter 20 value 18.437123
## iter 30 value 1.706173
## iter 40 value 0.816669
## iter 50 value 0.607487
## iter 60 value 0.505726
## iter 70 value 0.476954
## iter 80 value 0.437362
## iter 90 value 0.408508
## iter 100 value 0.390039
## iter 110 value 0.369740
## iter 120 value 0.317412
## iter 130 value 0.300061
## iter 140 value 0.280406
## iter 150 value 0.270608
## iter 160 value 0.258549
## iter 170 value 0.247354
## iter 180 value 0.241215
## iter 190 value 0.239779
## iter 200 value 0.237079
## iter 210 value 0.234157
## iter 220 value 0.233405
## iter 230 value 0.232629
## iter 240 value 0.232160
## iter 250 value 0.231809
## iter 260 value 0.231582
## iter 270 value 0.231388
## iter 280 value 0.231197
## iter 290 value 0.231035
## iter 300 value 0.230793
## iter 310 value 0.230138
## iter 320 value 0.229342
## iter 330 value 0.228658
## iter 340 value 0.228195
## iter 350 value 0.227791
## iter 360 value 0.227649
## iter 370 value 0.227262
## iter 380 value 0.226685
## iter 390 value 0.226272
## iter 400 value 0.225948
## iter 410 value 0.225706
## iter 420 value 0.225550
## iter 430 value 0.225378
## iter 440 value 0.225283
## iter 450 value 0.225211
## iter 460 value 0.225174
## iter 470 value 0.225150
## iter 480 value 0.225135
## iter 490 value 0.225125
## iter 500 value 0.225119
## iter 510 value 0.225115
## iter 520 value 0.225112
## iter 530 value 0.225109
## iter 540 value 0.225027
## iter 550 value 0.225016
## iter 560 value 0.225005
## iter 570 value 0.224999
## iter 580 value 0.224997
## iter 590 value 0.224996
## iter 600 value 0.224995
## iter 610 value 0.224994
## iter 620 value 0.224994
## iter 630 value 0.224993
## iter 640 value 0.224993
## final value 0.224993
## converged
Сравниваем подгонку ряда (красный цвет) и исходный ряд (черный цвет)
plot(1:144, g.series, type = "l")
lines(13:144, g.net$fitted.values, col = "red")
Начинаем вычислять прогнозируемые значения Первый вектор входов == последние 12 наблюдений ряда
g.forecast <- g.2[nrow(g.2), -1]
# Горизонт прогноза - 12, по условию
pred.n <- 12
# Вектор спрогнозированных значений
pred.1 <- rep(-9999, pred.n)
for (i in 1:pred.n)
{
pred.1[i] <- predict(g.net, g.forecast, type = "raw")
g.forecast <- c( g.forecast[-1], pred.1[i])
}
Строим график с результатами, черным цветом исходный ряд, красным цветом подгонка, синим цветом прогноз Берем экспоненту, так как прогноз был для ряда из логарифмов
plot(1:144, exp(g.series), type = "l", xlim = c(0, 144+pred.n), ylim = c(100, 700))
lines(13:144, exp(g.net$fitted.values), col = "red")
lines((144+1):(144+pred.n), exp(pred.1), col = "blue")
Мы расмотрели на примере 2 библиотеки для реализаций Standart NN - neuralnet, nnet
Давайте рассмотрим другие библиотеки и разберём параметры этих библиотек подробнее
Подгрузим библиотечки
library(ggplot2)
library(neuralnet)
library(caret)
## Warning: package 'caret' was built under R version 3.4.4
Загрузим данные и удалим факторные значения
data("diamonds")
head(diamonds)
## # A tibble: 6 x 10
## carat cut color clarity depth table price x y z
## <dbl> <ord> <ord> <ord> <dbl> <dbl> <int> <dbl> <dbl> <dbl>
## 1 0.230 Ideal E SI2 61.5 55.0 326 3.95 3.98 2.43
## 2 0.210 Premium E SI1 59.8 61.0 326 3.89 3.84 2.31
## 3 0.230 Good E VS1 56.9 65.0 327 4.05 4.07 2.31
## 4 0.290 Premium I VS2 62.4 58.0 334 4.20 4.23 2.63
## 5 0.310 Good J SI2 63.3 58.0 335 4.34 4.35 2.75
## 6 0.240 Very Good J VVS2 62.8 57.0 336 3.94 3.96 2.48
diamonds$cut <- NULL
diamonds$color <- NULL
diamonds$clarity <- NULL
Данные слишком большие нейронная сеть будет долгоооо думать и комп зависнет. поэтому возьмем 3% данных
ubrat <- createDataPartition(diamonds$price, p = 0.03, list = F)
diamonds <- diamonds[ubrat,]
Нормализуем данные, обязательное условие нейронной сети данные должны быть от 0 до 1
samplesize = 0.70*nrow(diamonds)
set.seed(80)
index = base::sample(seq_len(nrow(diamonds)), size = samplesize )
datatrain = diamonds[ index, ]
datatest = diamonds[ -index, ]
max = apply(diamonds, 2 , max)
min = apply(diamonds, 2 , min)
scaled = as.data.frame(scale(diamonds, center = min, scale = max - min))
Создадим два датасета для нейронной сети
trainNN = scaled[index , ]
testNN = scaled[-index , ]
colnames(trainNN)
## [1] "carat" "depth" "table" "price" "x" "y" "z"
И наконец постройм нейронную сеть
NN = neuralnet(price~carat+depth+table+x+y+z, trainNN, hidden = c(5,3) , linear.output = F )
plot(NN)
Наконец долшли до предсказания
predict_testNN = compute(NN, testNN[,c(2:7)])
predict_testNN = (predict_testNN$net.result * (max(diamonds$price) - min(diamonds$price))) + min(diamonds$price)
Постройм график предсказанными значениями и фактическими и вытащим root mean squad error
plot(datatest$price, predict_testNN, col='blue', pch=16, ylab = "predicted NN", xlab = "real")
abline(0,1)
RMSE.NN = (sum((datatest$price - predict_testNN)^2) / nrow(datatest)) ^ 0.5
RMSE.NN
## [1] 2435.979904
neuralnet(formula, data, hidden = 1, threshold = 0.01, stepmax = 1e+05, rep = 1, startweights = NULL, learningrate.limit = NULL, learningrate.factor = list(minus = 0.5, plus = 1.2), learningrate=NULL, lifesign = "none", lifesign.step = 1000, algorithm = "rprop+", err.fct = "sse", act.fct = "logistic", linear.output = TRUE, exclude = NULL, constant.weights = NULL, likelihood = FALSE)
formula ~ символическое описание модели, подлежащей установке
data ~ данные, содержащий переменные, указанные в формуле
hidden ~ вектор целых чисел, определяющий количество скрытых нейронов (вершин) в каждом слое
threshold ~ числовое значение, определяющее порог для частных производных функции ошибки как критерий остановки.
stepmax ~ максимальные шаги для обучения нейронной сети. Достижение этого максимума приводит к остановке процесса обучения нейронной сети.
rep ~ количество повторений для обучения нейронной сети
startweights ~ вектор, содержащий начальные значения для весов. Весы не будут случайным образом инициализированы
learningrate.limit ~ вектор или список, содержащий самый низкий и самый высокий предел для скорости обучения. Используется только для RPROP и GRPROP
learningrate.factor ~ вектор или список, содержащий коэффициенты умножения для верхней и нижней скорости обучения. Используется только для RPROP и GRPROP
learningrate ~ числовое значение, определяющее скорость обучения, используемую традиционным обратным распространением. Используется только для традиционного backpropagation
lifesign ~ строка, определяющая, насколько функция будет печататься во время вычисления нейронной сети. ‘none’, ‘minimum’ или ‘full’
lifesign.stepr ~ целое число, определяющее шаг, чтобы напечатать минимальный порог в режиме полной жизни
algorithm ~ строка, содержащая тип алгоритма для вычисления нейронной сети. Возможны следующие типы: backprop, rprop +, rprop-, sag или slar. «backprop» относится к backpropagation, «rprop +» и «rprop-» относятся к отказоустойчивой backpropagation с возвратом backtracking и без него, тогда как «sag» и «slr» вызывают использование модифицированного глобально конвергентного алгоритма (grprop).
err.fct ~ дифференцируемой функции, которая используется для вычисления ошибки. В качестве альтернативы можно использовать строки sse ‘и’ ce ’, которые обозначают сумму квадратов ошибок и кросс-энтропии
act.fct ~ дифференцируемой функцией, которая используется для сглаживания результата поперечного произведения ковариата или нейронов и весов. Кроме того, для логистической функции и касательной гиперболичности возможны строки, «логистика» и «тань»
linear.output ~ логичной. Если act.fct не следует применять к выходным нейронам, установите линейный выход в TRUE, иначе FALSE
exclude ~ вектор или матрицу, определяющую весовые коэффициенты, которые исключаются из расчета. Если задано как вектор, то точное положение весов должно быть известно. Матрица с n-строками и тремя столбцами исключает n весов, где первый столбец обозначает слой, второй столбец для входного нейрона и третий столбец для выходного нейрона веса
constant.weights ~ вектор, определяющий значения весов, которые исключаются из учебного процесса и рассматриваются как исправление
likelihood ~ логичной. Если функция ошибки равна отрицательной функции логарифмического правдоподобия, будут вычислены информационные критерии AIC и BIC. Кроме того, использование trust.interval имеет смысл
nnet(x, y, weights, size, Wts, mask, linout = FALSE, entropy = FALSE, softmax = FALSE, censored = FALSE, skip = FALSE, rang = 0.7, decay = 0, maxit = 100, Hess = FALSE, trace = TRUE, MaxNWts = 1000, abstol = 1.0e-4, reltol = 1.0e-8, ...)
x ~ Матрица обучающей выборки
y ~ Матрица ответов для обучающей выборки (должна быть преобразована с использованием class.ind)
weights ~ Веса объектов, полагаются единицами в случае пропуска
size ~ Число нейронов скрытого слоя
formula ~ Формула вида class ~ x1 + x2 + …
data ~ Data frame – выборка
subset ~ Вектор индексов объектов, на которых следует провести обучение
na.action ~ Параметр, определяющий, что делать с NA-значениями. По умолчанию – окончание процедуры
Wts ~ Вектор начальных значений параметров. В случае пропуска задается случайно
mask ~ Логический вектор, определяющий, какие из параметров необходимо оптимизировать. По умолчанию все
linout ~ Переключение на linear output units. По умолчанию logistic output units
entropy ~ Переключатель для энтропии
softmax ~ Переключатель для softmax (log-linear model) и maximum conditional likelihood fitting. linout, entropy, softmax и censored являются взаимно исключающими
censored ~ Вариант softmax, в котором ненулевые метки означают возможность принадлежности к данному классу. Таким образом для softmax вектор (0, 1, 1) означает, что объект принадлежит обоим классам 2 и 3, но для censored он значит, что объект принадлежит или классу 2, или классу 3
skip ~ Переключение добавления связей между входом и выходом
rang ~ Случайные начальные значения весов берутся из диапазона [-rang, rang]
decay ~ Параметр для weight decay (сокращение весов). По умолчанию 0
maxit ~ Число максимального количества итераций. По умолчанию 100
Hess ~ Если значение TRUE, то гессиан меры настройки на найденном лучшем наборе возвращается через компоненту Hessian
trace ~ Включает tracing optimization. По умолчанию TRUE
MaxNWts ~ Максимально допустимое количество весов. abstol Остановка, если значение критерия при настройке опускается ниже abstol
reltol ~ Остановка, если оптимизатор не в состоянии уменьшить критерий настройки по крайней мере на 1 - reltol
Пример решения Titanic competition с помощью neuralnet и nnet
Подгрузим данные
library(neuralnet)
library(nnet)
library(ROSE)
df <- read.csv("~/data/Titanic/train.csv")
test <- read.csv("~/data/Titanic/test.csv")
subm <- read.csv("~/data/Titanic/gender_submission.csv")
# Удаляем ненужные колонки
df$PassengerId <- NULL
df$Name <- NULL
df$Ticket <- NULL
df$Cabin <- NULL
# И в тесте тоже
test$PassengerId <- NULL
test$Name <- NULL
test$Ticket <- NULL
test$Cabin <- NULL
# Заменяем пропущенные значения на медиану
df$Age <- ifelse(is.na(df$Age),median(df$Age,na.rm = TRUE),df$Age)
Делаем нормализацию данных что бы нейронная сеть хорошо читала данные :)
# делим данные на 70%
samplesize = 0.70*nrow(df)
set.seed(80) # сеем семя
# переводим в эс.нумерик
df$Embarked <- as.numeric(df$Embarked)
df$Sex <- as.numeric(df$Sex)
test$Embarked <- as.numeric(test$Embarked)
test$Sex <- as.numeric(test$Sex)
index = base::sample(seq_len(nrow(df)), size = samplesize )
datatrain = df
datatest = test
max = apply(df , 2 , max)
min = apply(df, 2 , min)
# скалируем данные
scaled = as.data.frame(scale(df,
center = min,
scale = max - min))
Подробнее про скалирование Scaling and Centering of Matrix-like Objects (Масштабирование и центрирование матричных объектов) - scale - это общая функция, метод по умолчанию которой центрирует и / или масштабирует столбцы числовой матрицы
Давайте попробуем сделать модель с помощью библиотеки neuralnet
# neuralnet
trainNN = scaled[index , ]
testNN = scaled[-index , ]
NN = neuralnet(Survived~Pclass+Sex+Age+SibSp+Parch+Fare+Embarked, trainNN, hidden = 4, linear.output = F)
plot(NN)
# Делаем predict
predict_testNN = neuralnet::compute(NN, testNN[,c(2:8)])
# чтобы заново нормализовать данные
predict_testNN = (predict_testNN$net.result * (max(df$Survived) - min(df$Survived))) + min(df$Survived)
# Смотрим RMSE (root squad mean error)
RMSE.NN = (sum((testNN$Survived - predict_testNN)^2) / nrow(testNN)) ^ 0.5
RMSE.NN
## [1] 0.3966820739
# и смотрим roc.curve
roc.curve(testNN$Survived,predict_testNN)
## Area under the curve (AUC): 0.801
Теперь давайте попробуем сделать модель с помощью библиотеки nnet
# nnet
tit.class <- class.ind(factor(trainNN[, 1]))
g.net <- nnet(trainNN, tit.class, size = 4, linout = TRUE, rang=0.1, decay=0.001, maxit = 1000)
## # weights: 46
## initial value 743.364576
## iter 10 value 100.001132
## iter 20 value 0.271174
## iter 30 value 0.207477
## iter 40 value 0.179151
## iter 50 value 0.152756
## iter 60 value 0.110144
## iter 70 value 0.090045
## iter 80 value 0.072690
## iter 90 value 0.061709
## iter 100 value 0.055012
## iter 110 value 0.043034
## iter 120 value 0.028745
## iter 130 value 0.023004
## iter 140 value 0.019471
## iter 150 value 0.017579
## iter 160 value 0.016843
## iter 170 value 0.015953
## iter 180 value 0.015302
## iter 190 value 0.015043
## iter 200 value 0.014865
## iter 210 value 0.014408
## iter 220 value 0.013971
## iter 230 value 0.013889
## iter 240 value 0.013841
## iter 250 value 0.013760
## iter 260 value 0.013628
## iter 270 value 0.013563
## iter 280 value 0.013491
## iter 290 value 0.013471
## iter 300 value 0.013209
## iter 310 value 0.012983
## iter 320 value 0.012946
## iter 330 value 0.012890
## iter 340 value 0.012865
## iter 350 value 0.012827
## iter 360 value 0.012814
## iter 370 value 0.012772
## iter 380 value 0.012760
## iter 390 value 0.012713
## iter 400 value 0.012661
## iter 410 value 0.012638
## iter 420 value 0.012621
## iter 430 value 0.012606
## iter 440 value 0.012587
## iter 450 value 0.012579
## iter 460 value 0.012567
## iter 470 value 0.012562
## iter 480 value 0.012559
## iter 490 value 0.012542
## iter 500 value 0.012539
## iter 510 value 0.012531
## iter 520 value 0.012525
## iter 530 value 0.012520
## iter 540 value 0.012518
## iter 550 value 0.012514
## iter 560 value 0.012513
## iter 570 value 0.012513
## iter 580 value 0.012511
## iter 590 value 0.012510
## iter 600 value 0.012510
## iter 610 value 0.012509
## iter 620 value 0.012508
## iter 630 value 0.012508
## iter 640 value 0.012508
## iter 650 value 0.012508
## final value 0.012508
## converged
#Готовлю вектор с результатами
a.2 <- rep(0, nrow(g.net$fitted.values))
for(i in 1:nrow(g.net$fitted.values)){
a.2[i]<- which.max(g.net$fitted.values[i, ])
}
#Таблица сопряженности, можно посмотреть сколько неправильно предсказанных значений
table(a.2, trainNN[,1])
##
## a.2 0 1
## 1 375 0
## 2 0 248
# Видим что нейронная сеть предсказала все правильно
# так как nnet работает с матрицей, нужно заново все перевести в нормальный результат
nnet_pred <- as.data.frame(predict(g.net, testNN))
nnet_pred$Survived <- ifelse(nnet_pred$`0`>0.6, 1, 0)
nnet_pred$`0` <- NULL
nnet_pred$`1` <- NULL
# и смотрим ошибки, ошибок нету очень странно
RMSE.NN = (sum((testNN$Survived - nnet_pred)^2) / nrow(testNN)) ^ 0.5
RMSE.NN
## [1] 1
roc.curve(testNN$Survived, nnet_pred$Survived)
## Area under the curve (AUC): 1.000
Тут ниже делаем туже самую нормализацию для тестовых данных и вставляем в сабмит и дальше сабмитим резульат
max = apply(datatest , 2 , max)
min = apply(datatest, 2 , min)
scaled = as.data.frame(scale(datatest,
center = min,
scale = max - min))
test_nn <- scaled
predict_testNN = neuralnet::compute(NN, test_nn)
subm <- read.csv("~/data/Titanic/gender_submission.csv")
subm$Survived <- predict_testNN$net.result
subm$Survived <- ifelse(subm$Survived>0.5, 1, 0)
write.csv(subm, "subm_nn.csv", row.names = F)