English 中文 Español Deutsch 日本語 Português
Глубокие нейросети (Часть IV). Создание, обучение и тестирование модели нейросети

Глубокие нейросети (Часть IV). Создание, обучение и тестирование модели нейросети

MetaTrader 5Трейдинг | 21 сентября 2017, 08:31
10 384 27
Vladimir Perervenko
Vladimir Perervenko

Оглавление

Введение

Основные направления исследования и применения

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

Подход № 1. Нейросети чрезвычайно чувствительны к способу начальной инициализации нейронов в скрытых слоях, особенно при увеличении количества скрытых слоев (выше 3). Первым решить эту проблему попытался профессор G.Hynton. Суть его предложения: инициировать веса нейронов в скрытых слоях весами, полученными при обучении без учителя автоассоциативных сетей, составленных из RBM (ограниченная машина Больцмана) или AE (автоэнкодера). Эти Stacked RBM (SRBM) и Stacked AE (SAE) обучаются на большом массиве неразмеченных данных. Цель такого обучения — выявить скрытые структуры (представления, образы) и зависимости в данных. Инициализация нейронов MLP весами, полученными при претренинге, помещает MLP в пространство решений, наиболее приближенное к оптимальному. Это дает возможность при последующей тонкой настройке (обучении) MLP уменьшить количество размеченных данных и эпох обучения. Для многих областей практического применения (особенно при обработке "больших данных") это критически важные преимущества.

Подход № 2: Другая группа ученых под руководством Иешуа Бенджио разработала специфические методы начальной инициализации скрытых нейронов, специальные функции активации, методы стабилизации и обучения. Успехи в этом направлении связаны с бурным развитием глубоких сверточных и рекуррентных нейросетей (DCNN, RNN). Такие нейросети показали максимальную эффективность в распознавании изображений, анализе и классификации текстов и переводе живой речи с одного языка на другой. Идеи и методы, разработанные для этих нейросетей, с успехом стали применяться и для MLP.

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

Пакеты в языке R, позволяющие разрабатывать и практически использовать DNN

Для создания и использования DNN с различным уровнем сложности и возможностей в языке R разработан ряд пакетов.

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

  • deepnet простой, не обремененный множеством параметров и настроек пакет. Позволяет создавать как нейросети с предобучением SAE, так и SRBM. В предыдущей статье мы рассматривали практическую реализацию экспертов с применением этого пакета. Применение RBM для претренинга дает менее стабильные результаты. Пакет предназначен для первоначального ознакомления с темой и изучения особенностей работы таких нейросетей. При правильном подходе вполне может использоваться в экспертах. RcppDL — версия этого пакета (немного урезанная ) на С++.
  • darch v.0.12 — сложный, гибкий, имеющий множество параметров, режимов и настроек пакет (рекомендованные значения по умолчанию установлены). Позволяет построить и настроить нейросеть любой сложности и конфигурации. Для претренинга используется SRBM. Пакет предназначен для подготовленных пользователей. Ниже мы детально поговорим о его возможностях.

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

  • H2O пакет для обработки "больших данных" (>1M строк и >1K колонок). Используемая в нем глубокая нейросеть имеет развитую систему регуляризации. Возможности для нашей области применения избыточны, но это не запрещает нам его применять.
  • mxnet позволяет создавать не только MLP, но и сложные рекуррентные сети, сверточные и LSTM нейросети. Пакет имеет API для нескольких языков, в том числе для R и Python. Идеология пакета сильно отличается от вышеперечисленных. Это обусловлено тем, что разработчики в основном писали пакеты для Python. Пакет mxnet для R облегчен и имеет чуть меньшую функциональность, по сравнению с пакетом для Python. Но это не умаляет его достоинств.

В среде Python тема глубоких и рекуррентных сетей широко разработана. Есть много интересных пакетов для построения таких типов нейросетей, которых в R просто нет. Вот пакеты в R, которые позволяют исполнять программы/модули, написанные на Python:

  • PythonInR и reticulate — два пакета, дающие возможность исполнять любой код Python в R. Для этого нужно иметь инсталлированный на вашем компьютере Python 2/3.
  • kerasr — R-интерфейс к популярной библиотеке глубокого обучения keras.
  • tensorflow — пакет, предоставляющий доступ к полному TensorFow API в среде R.

Недавно компания Microsoft выложила библиотеку cntk v.2.1(Computatinal Network Toolkit) GitHub, и теперь она может использоваться как backend для Keras, с которым R "дружит". Желательно протестировать ее в наших задачах.

Не отстает и Яндекс: в open source выложена его собственная библиотека CatBoost. С ее помощью можно эффективно обучать модели на разнородных данных, в том числе таких, которые трудно представить в виде чисел (например, виды облаков или категории товаров). Исходный код, документация, бенчмарки и необходимые инструменты уже опубликованы на GitHub под лицензией Apache 2.0. Несмотря на то, что это не нейросети а бустинговые деревья, тест-драйв алгоритма желательно провести, тем более, что в нем реализован API из R.


1. Краткое описание возможностей пакета

Пакет darch ver. 0.12.0 предоставляет широкий круг функций, позволяющих не просто создать и обучить модель, но буквально по кирпичикам сложить и настроить её под Ваши предпочтения. По сравнению с предыдущей версией пакета (0.10.0), которую мы рассматривали в предыдущей статье, произошли значительные изменения. Добавлены новые функции активации, инициализации и стабилизации, но главное — все сведено в одну функцию darch(), которая одновременно является и конструктором. Поддерживается использование графических карт. После обучения модели функция возвращает объект класса DArch. Структура объекта представлена на рис.1. Функции predict() и darchTest() возвращают предсказание по новым данным или метрики классификации.

StrDarch

Рис.1. Структура объекта DArch 

Все параметры имеют значения по умолчанию, как правило, неоптимальные. Всё это море параметров можно объединить в три группы: общие, для RBM, для NN. Ниже рассмотрим некоторые из них подробнее.

Функции Виды
Функции инициализации                                                                     generateWeightsUniform, generateWeightsNormal, 

                                             generateWeightsHeUniform, generateWeightsHeNormal)

Функции активации                                      exponentialLinearUnitsoftplusUnit, softmaxUnit, maxoutUnit)
Функции обучения                                         minimizeAutoencoder, minimizaClassifier)
Уровень обучения
  • bp.learnRate = 1 — уровень обучения для backpropagation. Может быть вектором, если используются различные уровни обучения в каждом слое NN
  • bp.learnRateScale = 1 — уровень обучения умножается на эту величину после каждой эпохи
Функции стабилизации
  • darch.dropout = 0 — число (0,1) или вектор с указанием  уровня отсева для каждого слоя NN
  • darch.dropout.dropConnect = F —  нужно ли применять DropConnect вместо dropout
  • darch.dropout.momentMatching = 0

  • darch.dropout.oneMaskPerEpoch = F — нужно ли генерировать новую маску для каждого batch (FALSE, default), или для каждой эпохи (TRUE)
  • darch.dither = F — нужно ли применять dither ко входным данным обучающей выборки
  • darch.nesterovMomentum = T
  • darch.weightDecay = 0
  • normalizeWeights = F
  • normalizeWeightsBound — верхняя граница для L2 norm входящего вектора весов. Используется только если  normalizeWeights = TRUE
Момент
  • darch.initialMomentum = 0.5
  • darch.finalMomentum = 0.9
  • darch.momentumRampLength = 1
 Условия остановки
  • darch.stopClassErr = 100
  • darch.stopErr = -Inf
  • darch.stopValidClassErr = 100
  • darch.stopValidErr = -Inf

Глубокая нейросеть состоит из n-го количества RBM (n = layers -1), сложенных в автоассоциативную сеть (SRBM), и собственно нейросети MLP с количеством слоев layers. Послойное предобучение RBM проводится на неразмеченных данных без учителя. Тонкое обучение нейросети проводится с учителем на размеченных данных.

Разделение этих стадий обучения с помощью параметров дает нам возможность использовать различные по объему (но не по структуре!) данные или получить несколько различных тонко обученных моделей на базе одного предобучения. Кроме того, если данные для предобучения и тонкой настройки одинаковы, возможно провести обучение одним ходом без разделения на две стадии. Можно пропустить предобучение (rbm.numEpochs = 0; darch.numEpochs = 10)) и использовать только многослойную нейросеть или, наоборот, обучить только RBM (rbm.numEpochs = 10; darch.numEpochs = 0). При этом у нас есть доступ ко всем внутренним параметрам.

Обученную нейросеть можно в последующем дообучать на новых данных неограниченное количество раз. Очень немногие модели предоставляют такую возможность. Структурная схема глубокой нейросети, инициализируемой сложенными ограниченными машинами Больцмана (DNRBM), приведена на рис.2.

DNSRBM

Рис.2. Структурная схема DNSRBM


1.1. Функции начальной инициализации нейронов

В пакете использованы две основных функции инициализации нейронов. 

  • generateWeightsUniform() использует функцию runif(n, min, max) и реализована так:
> generateWeightsUniform
function (numUnits1, numUnits2, weights.min = getParameter(".weights.min", 
    -0.1, ...), weights.max = getParameter(".weights.max", 0.1, 
    ...), ...) 
{
    matrix(runif(numUnits1 * numUnits2, weights.min, weights.max), 
        nrow = numUnits1, ncol = numUnits2)
}
<environment: namespace:darch>

Здесь numUnits1 — количество нейронов в предыдущем слое, а numUnits2 — количество нейронов в текущем слое. 

  • generateWeightsNormal() использует функцию rnorm(n, mean, sd) и реализована в пакете так:
> generateWeightsNormal
function (numUnits1, numUnits2, weights.mean = getParameter(".weights.mean", 
    0, ...), weights.sd = getParameter(".weights.sd", 0.01, ...), 
    ...) 
{
    matrix(rnorm(numUnits1 * numUnits2, weights.mean, weights.sd), 
        nrow = numUnits1, ncol = numUnits2)
}
<environment: namespace:darch>

Остальные четыре функции используют эти две, но определяют min, max, mean и sd специфическими формулами. С ними вы можете познакомиться, вызвав в терминале имя функции без скобок.


1.2. Функции активации нейронов

Наряду со стандартными функциями активациями, пакет предлагает широкий набор новых. Вот некоторые из них:

x <- seq(-5, 5, 0.1)
par(mfrow = c(2,3))
plot(x, y = 1/(1 + exp(-x)), t = "l", main = "sigmoid")
abline(v = 0, col = 2)
plot(x, y = tanh(x), t = "l", main = "tanh")
abline(v = 0, h = 0, col = 2)
plot(x, y = log(1 + exp(x)), t = "l", main = "softplus");
abline(v = 0, col = 2)
plot(x, y = ifelse(x > 0, x ,exp(x) - 1), t = "l", 
     main = "ELU")
abline(h = 0, v = 0, col = 2)
plot(x, y = ifelse(x > 0, x , 0), t = "l", main = "ReLU")
abline(h = 0, v = 0, col = 2)
par(mfrow = c(1,1))

 

activFun

Рис.3. Функции активации нейронов

Рассмотрим отдельно функцию активации — maxout. Функция пришла из сверточных сетей. Скрытый слой нейросети разделяется на модули размером poolSize. Количество нейронов в скрытом слое должно быть кратным размеру пула. При обучении из пула выбирается нейрон с максимальной активацией и подается на выход. Функция активации нейронов в пуле задается отдельно. Проще говоря, это сдвоенный слой (сверточный + maxpooling) с ограниченными возможностями по шагу фильтра. По данным в публикациях показывает хорошие результаты в паре с dropout. На рис. 4. схематически показан скрытый слой с 8 нейронами и двумя размерами пула.

Maxout

Рис.4. Функция активации maxout

1.3. Методы обучения


К сожалению, в пакете представлены только два метода обучения — backpropagation и rprop базовой и улучшенной версии с обновлением весов при обратном распространении и без него.  Предусмотрена возможность изменения уровня обучения с помощью множителя bp.learnRateScale.


1.4. Методы регуляризации и стабилизации

  • dropout — отсев (обнуление веса) части нейронов скрытого слоя при обучении. Нейроны обнуляются в случайном порядке, относительное количество нейронов, подлежащих отсеву, задается параметром darch.dropout. Уровень отсева в каждом скрытом слое может быть различным. Маску отсева можно генерировать для каждой мини-выборки (batch) или для каждой эпохи.
  • dropconnect — отключение связи между частью нейронов текущего слоя с нейронами предыдущего слоя. Связи обрезаются в случайном порядке, относительное количество связей, подлежащих обрезанию, задается тем же параметром  darch.dropout (как правило, не более 0.5). По некоторым публикациям показывает результат лучше, чем dropout.
  • dither — способ предотвращения переобучения путем размыва (dithering) входных данных.
  • weightDecay — вес каждого нейрона будет умножен на (1 — weightDecay)  перед обновлением.
  • normalizeWeights — нормализовать входящий вектор весов нейронов с возможным ограничением сверху (L2 norm)

Первые три метода применяются только раздельно. 


1.5. Методы и параметры обучения RBM

Есть два способа обучения SRBM: обучать RBM по одному за rbm.numEpochs или поочередно тренировать каждый RBM по одной эпохе за раз. Выбор одного из этих способов обеспечивается параметром rbm.consecutive: TRUE или default — первый способ, FALSE — второй. На рис.5 представлена схема обучения в двух вариантах. С помощью параметра rbm.lastLayer мы можем указать, на каком слое SRBM нужно остановить предобучение. Если 0 — обучаем все слои, если (-1) — верхний слой не обучаем. Это имеет смысл, поскольку верхний слой нейросети нужно обучать отдельно и более длительно. Остальные параметры понятны без дополнительных пояснений.

SRBMtrain

Рис.5. Два метода обучения SRBM


1.6. Методы и параметры обучения DNN

DNN можно обучать двумя способами: с претренингом и без него. Эти два способа кардинально отличаются по используемым параметрам. Так, для обучения с претренингом нет никакого смысла использовать специфические способы инициализации и регуляризации. Более того, их применение может ухудшить результат. Дело в том, что после претренинга веса нейронов скрытых слоев будут помещены в область, близкую к оптимальным значениям, и им понадобится совсем небольшая тонкая настройка. И напротив, чтобы получить такой же результат, при обучении без претренинга нужно будет использовать все доступные способы инициализации и регуляризации. Как правило, обучение по этому способу занимает больше времени.

Итак, нас интересует обучение с претренингом. Как правило, оно делится на 2 этапа.

  1. Обучение SRBM на большом наборе неразмеченных данных. Параметры претренинга задаем отдельно. В результате получаем нейросеть, иницированную весами SRBM. Затем обучаем верхний слой нейросети на размеченных данных со своими параметрами обучения. В результате получаем нейросеть с обученным верхним слоем и инициированными весами в нижних слоях. Сохраним ее  как самостоятельный объект для использования в дальнейшем. 
  2. На втором этапе тонкой настройки применяем совсем немного размеченных примеров, низкий уровень обучения и небольшое количество эпох обучения для всех слоев нейросети — тонко отшлифуем сеть. Обученная нейросеть готова.

Возможность разделения этапов претренинга, тонкого тренинга и последующих дообучений дает невероятную гибкость в построении алгоритмов обучения не только одной DNN, но и, что более важно, в обучении комитетов DNN. На рис.6. представлена несколько вариантов обучения DNN и комитетов DNN. 

  • Вариант а: при тонкой настройке сохраняем DNN через каждые n эпох. Таким образом получим ряд DNN с различной степенью обучения. Мы можем в последующем использовать каждую из них в отдельности или в составе комитета. Недостаток этого варианта состоит в том, что все DNN обучаются на одних и тех же данных, ведь параметры обучения у них всех одинаковы.
  • Вариант б: инициированную DNN параллельно тонко настраиваем с различными наборами данных (скользящее окно, растущее окно и т.п.) и различными параметрами. Получим комитет DNN, который будет выдавать менее коррелированные предсказания, чем по варианту а.
  • Вариант в: инициированную DNN последовательно  тонко настраиваем с различными наборами данных и различными параметрами, сохраняя промежуточные модели. Это и есть дообучение, которое можно проводить постоянно по мере поступления достаточной порции новых данных.

DNNtrain

Рис.6. Варианты обучения DNN


2. Проверка качества работы DNN в зависимости от применяемых параметров.

2.1. Эксперименты


2.1.1. Входные данные (подготовка)

Будем использовать данные и функции из предыдущих частей статьи (1, 2, 3) . В них мы довольно подробно рассмотрели различные варианты предподготовки данных. Поэтому здесь я совсем коротко обозначу этапы предподготовки, которые мы будем выполнять. Исходные данные — OHLCV, те же, что и раньше. Входные данные — цифровые фильтры, выходные — ZigZag. Можно использовать функции и снимок рабочего пространства Cotir.RData.

Этапы подготовки данных, которые будут выполняться укрупнены в отдельные функции:

  • PrepareData() — создаем начальный набор данных dataSet, очищаем его от NA;
  • SplitData() — разделяем начальный набор dataSet на поднаборы pretrain, train, val, test;
  • CappingData() — определяем и импутируем выбросы во всех поднаборах.

Во избежание загромождения статьи листинг этих функций приводить не буду. Их можно скачать с GitHub; кроме того, они подробно разобраны в предыдущих частях. Результаты выполнения рассмотрим ниже. Также не будем рассматривать все способы трансформации при препроцессинге. Многие из них широко известны и широко используются. Остановимся на менее известном и реже применяемом — это дискретизация (с учителем и без). Во второй части статьи были рассмотрены два пакета дискретизации с учителем (discretization и smbinning). В них используются различные алгоритмы для дискретизации.

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

Что такое биннинг?

Биннинг — это термин, используемый при моделировании баллов (scoring modeling). Также в машинном обучении он известен как Discretization, процесс преобразования непрерывной переменной в конечное число интервалов (диапазонов). Это помогает лучше понять его распределение и связь с двоичной целевой переменной. Диапазоны (bins), созданные в процессе, могут стать атрибутами предсказательной характеристики для использования в моделях.

Почему биннинг?

Несмотря на сдержанное отношение к биннингу, у него есть и неоспоримые преимущества. 

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

Дискретизация без учителя

Unsupervised Discretization делит непрерывную функцию на диапазоны без учета любой другой информации. Это раздел с двумя опциями: интервалы равной длины и интервалы равной частоты.

Опция
Цель
Пример
Недостаток
Интервалы равной длины
Понять распределение переменной
Классическая гистограмма, чьи бункеры имеют равную длину, которые могут быть рассчитаны с использованием разных правил (Sturges, Rice и т.д.)
Количество записей в бункере может быть слишком малым для проведения корректного расчета
Интервалы равной частоты
Проанализировать взаимосвязь с бинарной целевой переменной с помощью таких показателей, как bad rate
Quartilies или Percentiles Выбранные точки отсечения не могут максимизировать разницу между диапазонами при сопоставлении с целевой переменной

Дискретизация с учителем

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

Сегодня с помощью различных алгоритмов, таких например как ChiMerge или Recursive Partitioning,  аналитики могут быстро найти оптимальные точки за секунды и оценить взаимосвязь с целевой переменной, используя такие показатели, как вес фактических данных (Weight of Evidence, WoE) и информационная ценность (Information Value, IV).

WoE может использоваться как инструмент для трансформации предикторов на этапе препроцессинга для алгоритмов обучения с учителем. При дискретизации предикторов мы можем заменить их на новые номинальные переменные либо на значения их WoE. Второй вариант интересен тем, что позволяет уйти от преобразования номинальных переменных (факторов) в dummy, что дает, по некоторым данным, значимый прирост в качестве классификации. 

WOE и IV играют две разные роли при анализе данных:

  • WOE описывает взаимосвязь между предсказательной переменной и бинарной целевой переменной.
  • IV измеряет силу этих отношений.

Разберем, что такое WOE и IV, в картинках и формулах. Вспомним график переменной v.fatl, разбитой на 10 равночастотных участков из второй части статьи.

vfatl_discr

Рис.7. Переменная v.fatl, разбитая на 10 равночастотных участков

Предсказательная способность данных (WOE)

Как можно видеть, в каждом диапазоне есть примеры, которые попадают как в класс "1", так и в класс "-1". Предсказательная способность диапазонов WoEi вычисляется по формуле 

WoEi = ln(Gi/Bi)*100

где:

Gi — относительная частота "хороших" (в нашем случае "хорошие" = "1") примеров в каждом диапазоне переменной;

Bi — относительная частота "плохих" (в нашем случае "плохие" = "-1") примеров в каждом диапазоне переменной.

Если WoEi = 1 , т.е. "хороших" и "плохих" примеров в этом диапазоне приблизительно равное количество, то предсказательная способность этого диапазона равна 0. Если "хороших" больше, то  WOE >0 и наоборот.

Информационное значение (IV) 

Самая распространенная мера определения значимости переменных и измерения разницы в распределении "хороших" и "плохих" примеров. Информационное значение определяется по формуле:

IV = ∑ (Gi – Bi) ln (Gi/Bi)

Информационное значение переменной равно сумме всех диапазонов переменной. По грубому правилу значения данного коэффициента трактуются следующим образом:

  • менее 0,02 — статистически незначимая переменная;
  • 0,02 – 0,1 — статистически малозначимая переменная;
  • 0,1 – 0,3 — статистически значимая переменная;
  • 0,3 и более — статистически сильная переменная.

Дальше, используя различные алгоритмы и критерии оптимизации, диапазоны объединяют/разъединяют, чтобы получить максимальную разницу этого критерия между диапазонами. Например, пакет smbinning использует Recursive Partitioning для категоризации числовых переменных и IV для определения оптимальных точек разделения, а пакет discretization решает эту задачу с помощью ChiMerge и MDL. Нужно также помнить, что точки разделения мы получаем на обучающем наборе, а валидационный и тестовый набор разделяем с их использованием. 

Существует несколько пакетов, которые позволяют тем или иным способом дискретизировать числовые переменные. Это discretization, smbinning, Information, InformationValue и woebinning. С учетом того, что нам нужно не только дискретизировать обучающий набор, но и затем, используя эту информацию, разделить валидационный и тестовый наборы, а также очень желательно иметь визуальный контроль за результатами, я остановился на пакете woebinning

Пакет автоматически разбивает числовые переменные и факторы с увязкой к бинарной целевой. Предусмотрены два подхода:

  • реализация мелкой и грубой классификации последовательно объединяет гранулярные классы и уровни;
  • древовидный подход итеративно сегментирует исходные диапазоны через двоичные расщепления.

Обе процедуры сливают разделенные диапазоны на основе аналогичных значений WOE и останавливаются на основе критериев, основанных на IV. Пакет может использоваться с одиночными переменными или целым фреймом данных. Он обеспечивает гибкие инструменты для изучения различных решений для биннинга и для их развертывания на новых данных.

Приступим к расчетам. В наше рабочее пространство уже  загружены котировки из терминала (или снимок рабочего пространства Cotir.RData из GitHub). Последовательность вычислений и результат:

  1.  PrepareData() — создаем начальный набор данных dt[7906, 14], очищаем его от NA. Набор включает временную метку Data, входные переменные(12) и целевую Class(фактор с двумя уровнями "-1" и "+1").
  2.  SplitData() — разделяем начальный набор dt[] на поднаборы pretrain, train, val, test в соотношении 2000/1000/500/500, объединив их в датафрейм DT[4, 4000, 14].
  3.  CappingData() — определяем и импутируем выбросы во всех поднаборах, получаем набор DTcap[4, 4000, 14]. Несмотря на то, что дискретизация терпимо относится к выбросам, мы их все же импутируем. Вы можете поэкспериментировать и без этого этапа. Напоминаю, параметры выбросов (pre.outl) определяем на поднаборе pretrain. Выборки train/val/test обрабатываем, используя  эти параметры.
  4.  NormData() — нормируем набор, используя метод spatialSing из пакета caret. Как и при импутации выбросов параметры нормализации (preproc) определяем на поднаборе pretrain. Выборки train/val/test обрабатываем используя  эти параметры. Получаем DTcap.n[4, 4000, 14].
  5. DiscretizeData() — определяем параметры дискретизации (preCut), качество переменных и их диапазонов в смысле WOE и IV. 
evalq({
  dt <- PrepareData(Data, Open, High, Low, Close, Volume)
  DT <- SplitData(dt, 2000, 1000, 500,500)
  pre.outl <- PreOutlier(DT$pretrain)
  DTcap <- CappingData(DT, impute = T, fill = T, dither = F, 
                       pre.outl = pre.outl)
  preproc <- PreNorm(DTcap, meth = meth)
  DTcap.n <- NormData(DTcap, preproc = preproc)
  preCut <- PreDiscret(DTcap.n)
}, env)

Сведем данные дискретизации по всем переменным в таблицу и посмотрим на них:

evalq(tabulate.binning <- woe.binning.table(preCut), env)
> env$tabulate.binning
$`WOE Table for v.fatl`
          Final.Bin Total.Count Total.Distr. 0.Count 1.Count 0.Distr. 1.Distr. 1.Rate   WOE    IV
1  <= -0.3904381926         154         7.7%     130      24    13.2%     2.4%  15.6% 171.3 0.185
2 <= -0.03713814085         769        38.5%     498     271    50.4%    26.8%  35.2%  63.2 0.149
3   <= 0.1130198981         308        15.4%     141     167    14.3%    16.5%  54.2% -14.5 0.003
4            <= Inf         769        38.5%     219     550    22.2%    54.3%  71.5% -89.7 0.289
6             Total        2000       100.0%     988    1012   100.0%   100.0%  50.6%    NA 0.626

$`WOE Table for ftlm`
          Final.Bin Total.Count Total.Distr. 0.Count 1.Count 0.Distr. 1.Distr. 1.Rate   WOE    IV
1  <= -0.2344708291         462        23.1%     333     129    33.7%    12.7%  27.9%  97.2 0.204
2 <= -0.01368798447         461        23.1%     268     193    27.1%    19.1%  41.9%  35.2 0.028
3   <= 0.1789073635         461        23.1%     210     251    21.3%    24.8%  54.4% -15.4 0.005
4            <= Inf         616        30.8%     177     439    17.9%    43.4%  71.3% -88.4 0.225
6             Total        2000       100.0%     988    1012   100.0%   100.0%  50.6%    NA 0.463

$`WOE Table for rbci`
          Final.Bin Total.Count Total.Distr. 0.Count 1.Count 0.Distr. 1.Distr. 1.Rate   WOE    IV
1  <= -0.1718377948         616        30.8%     421     195    42.6%    19.3%  31.7%  79.4 0.185
2 <= -0.09060410462         153         7.6%      86      67     8.7%     6.6%  43.8%  27.4 0.006
3   <= 0.3208178176         923        46.2%     391     532    39.6%    52.6%  57.6% -28.4 0.037
4            <= Inf         308        15.4%      90     218     9.1%    21.5%  70.8% -86.1 0.107
6             Total        2000       100.0%     988    1012   100.0%   100.0%  50.6%    NA 0.335

$`WOE Table for v.rbci`
         Final.Bin Total.Count Total.Distr. 0.Count 1.Count 0.Distr. 1.Distr. 1.Rate   WOE    IV
1 <= -0.1837437563         616        30.8%     406     210    41.1%    20.8%  34.1%  68.3 0.139
2 <= 0.03581374495         461        23.1%     253     208    25.6%    20.6%  45.1%  22.0 0.011
3  <= 0.2503922644         461        23.1%     194     267    19.6%    26.4%  57.9% -29.5 0.020
4           <= Inf         462        23.1%     135     327    13.7%    32.3%  70.8% -86.1 0.161
6            Total        2000       100.0%     988    1012   100.0%   100.0%  50.6%    NA 0.331

$`WOE Table for v.satl`
          Final.Bin Total.Count Total.Distr. 0.Count 1.Count 0.Distr. 1.Distr. 1.Rate    WOE    IV
1 <= -0.01840058612         923        46.2%     585     338    59.2%    33.4%  36.6%   57.3 0.148
2   <= 0.3247097195         769        38.5%     316     453    32.0%    44.8%  58.9%  -33.6 0.043
3   <= 0.4003869443         154         7.7%      32     122     3.2%    12.1%  79.2% -131.4 0.116
4            <= Inf         154         7.7%      55      99     5.6%     9.8%  64.3%  -56.4 0.024
6             Total        2000       100.0%     988    1012   100.0%   100.0%  50.6%     NA 0.330

$`WOE Table for v.stlm`
         Final.Bin Total.Count Total.Distr. 0.Count 1.Count 0.Distr. 1.Distr. 1.Rate   WOE    IV
1 <= -0.4030051922         154         7.7%     118      36    11.9%     3.6%  23.4% 121.1 0.102
2 <= -0.1867821117         462        23.1%     282     180    28.5%    17.8%  39.0%  47.3 0.051
3  <= 0.1141896118         615        30.8%     301     314    30.5%    31.0%  51.1%  -1.8 0.000
4           <= Inf         769        38.5%     287     482    29.0%    47.6%  62.7% -49.4 0.092
6            Total        2000       100.0%     988    1012   100.0%   100.0%  50.6%    NA 0.244

$`WOE Table for pcci`
          Final.Bin Total.Count Total.Distr. 0.Count 1.Count 0.Distr. 1.Distr. 1.Rate   WOE    IV
1  <= -0.1738420887         616        30.8%     397     219    40.2%    21.6%  35.6%  61.9 0.115
2 <= -0.03163945242         307        15.3%     165     142    16.7%    14.0%  46.3%  17.4 0.005
3   <= 0.2553612644         615        30.8%     270     345    27.3%    34.1%  56.1% -22.1 0.015
4            <= Inf         462        23.1%     156     306    15.8%    30.2%  66.2% -65.0 0.094
6             Total        2000       100.0%     988    1012   100.0%   100.0%  50.6%    NA 0.228

$`WOE Table for v.ftlm`
          Final.Bin Total.Count Total.Distr. 0.Count 1.Count 0.Distr. 1.Distr. 1.Rate   WOE    IV
1 <= -0.03697698898         923        46.2%     555     368    56.2%    36.4%  39.9%  43.5 0.086
2   <= 0.2437475615         615        30.8%     279     336    28.2%    33.2%  54.6% -16.2 0.008
3            <= Inf         462        23.1%     154     308    15.6%    30.4%  66.7% -66.9 0.099
5             Total        2000       100.0%     988    1012   100.0%   100.0%  50.6%    NA 0.194

$`WOE Table for v.rftl`
         Final.Bin Total.Count Total.Distr. 0.Count 1.Count 0.Distr. 1.Distr. 1.Rate   WOE    IV
1 <= -0.1578370554         616        30.8%     372     244    37.7%    24.1%  39.6%  44.6 0.060
2  <= 0.1880959621         768        38.4%     384     384    38.9%    37.9%  50.0%   2.4 0.000
3  <= 0.3289762494         308        15.4%     129     179    13.1%    17.7%  58.1% -30.4 0.014
4           <= Inf         308        15.4%     103     205    10.4%    20.3%  66.6% -66.4 0.065
6            Total        2000       100.0%     988    1012   100.0%   100.0%  50.6%    NA 0.140

$`WOE Table for stlm`
         Final.Bin Total.Count Total.Distr. 0.Count 1.Count 0.Distr. 1.Distr. 1.Rate   WOE    IV
1 <= -0.4586732186         154         7.7%      60      94     6.1%     9.3%  61.0% -42.5 0.014
2 <= -0.1688696056         462        23.1%     266     196    26.9%    19.4%  42.4%  32.9 0.025
3  <= 0.2631157075         922        46.1%     440     482    44.5%    47.6%  52.3%  -6.7 0.002
4  <= 0.3592235072         154         7.7%      97      57     9.8%     5.6%  37.0%  55.6 0.023
5  <= 0.4846279843         154         7.7%      81      73     8.2%     7.2%  47.4%  12.8 0.001
6           <= Inf         154         7.7%      44     110     4.5%    10.9%  71.4% -89.2 0.057
8            Total        2000       100.0%     988    1012   100.0%   100.0%  50.6%    NA 0.122

$`WOE Table for v.rstl`
          Final.Bin Total.Count Total.Distr. 0.Count 1.Count 0.Distr. 1.Distr. 1.Rate   WOE    IV
1  <= -0.4541701981         154         7.7%      94      60     9.5%     5.9%  39.0%  47.3 0.017
2  <= -0.3526306487         154         7.7%      62      92     6.3%     9.1%  59.7% -37.1 0.010
3  <= -0.2496412214         154         7.7%      53     101     5.4%    10.0%  65.6% -62.1 0.029
4 <= -0.08554320418         307        15.3%     142     165    14.4%    16.3%  53.7% -12.6 0.002
5    <= 0.360854678         923        46.2%     491     432    49.7%    42.7%  46.8%  15.2 0.011
6            <= Inf         308        15.4%     146     162    14.8%    16.0%  52.6%  -8.0 0.001
8             Total        2000       100.0%     988    1012   100.0%   100.0%  50.6%    NA 0.070

$`WOE Table for v.pcci`
          Final.Bin Total.Count Total.Distr. 0.Count 1.Count 0.Distr. 1.Distr. 1.Rate   WOE    IV
1  <= -0.4410911486         154         7.7%      92      62     9.3%     6.1%  40.3%  41.9 0.013
2 <= -0.03637567714         769        38.5%     400     369    40.5%    36.5%  48.0%  10.5 0.004
3   <= 0.1801156117         461        23.1%     206     255    20.9%    25.2%  55.3% -18.9 0.008
4   <= 0.2480148615         154         7.7%      84      70     8.5%     6.9%  45.5%  20.6 0.003
5   <= 0.3348752487         154         7.7%      67      87     6.8%     8.6%  56.5% -23.7 0.004
6   <= 0.4397404288         154         7.7%      76      78     7.7%     7.7%  50.6%  -0.2 0.000
7            <= Inf         154         7.7%      63      91     6.4%     9.0%  59.1% -34.4 0.009
9             Total        2000       100.0%     988    1012   100.0%   100.0%  50.6%    NA 0.042

В таблице для каждой переменной приведены значения: 

  • Final.Bin — границы диапазонов;
  • Total.Count  — общее количество примеров в диапазоне;
  • Total.Distr — относительное количество примеров;
  • 0.Count — количество примеров, относящихся к классу  "0";
  • 1.Count — количество примеров, относящихся к классу  "1";
  • 0.Distr — относительное количество примеров, относящихся к классу  "0";
  • 1.Distr — относительное количество примеров относящихся к классу  "1";
  • 1.Rate — процентное отношение количества примеров класса "1" к количеству примеров класса "0";
  •  WOE — предсказательная способность диапазонов;
  •  IV — статистическая значимость диапазонов (нарастающим итогом!).

Графически это будет наглядней. Выведем графики  WOE всех переменных в порядке возрастания их IV на основании этой таблицы:

> evalq(woe.binning.plot(preCut), env)

WOE 8

Рис.8. WOE первых 4 лучших переменных

WOE 10

Рис.9. WOE переменных 5-8

WOE 11

Рис.10. WOE  входных переменных 9-12 

Ну и график суммарного ранжирования переменных по их информационной значимости IV.

IV

Рис.11. Ранжирование переменных по их информационному значению IV

Две малозначимые переменные — v.rstl и v.pcci, имеющие IV < 0.1, мы в дальнейшем не будем использовать. Из графиков мы видим, что из 10 значимых переменных только две — v.satl и stlm — имеют нелинейную связь с целевой, остальные связаны с ней линейно.

Для дальнейших экспериментов нам необходимо создать три набора :

  • DTbin — набор данных, в котором непрерывные числовые предикторы преобразованы в факторы с количеством уровней, равным количеству диапазонов, на которые они разбиты;
  • DTdum — набор данных, в котором предикторы-факторы набора DTbin преобразованы в dummy-двоичные переменные;
  • DTwoe — набор данных, в котором предикторы-факторы преобразованы в числовые переменные путем замены их уровней значениями WOE этих уровней.

Первый набор DTbin нам нужен для обучения и получения метрик базовой модели. Второй и третий наборы будем использовать для обучения DNN и для сравнения эффективности этих двух методов трансформации.

В пакете woebinning есть функция woe.binning.deploy(), которая поможет нам относительно просто решить эту задачу. В функцию нужно передать:

  •  датафрейм с предикторами и целевой, причем целевая должна иметь значение 0 или 1;
  •  параметры дискретизации, полученные на предыдущем этапе (preCut);
  •  имена переменных, которые нужно категоризировать. Если категоризировать надо все переменные, то просто укажем имя датафрейма;
  •  указать, с каким минимальным IV не категоризировать переменные;
  •  указать, какие дополнительные переменные (кроме категоризированных) мы хотим получить. Два варианта — "woe" и "dum". 

Функция возвращает датафрейм, содержащий исходные переменные, категоризированные переменные и дополнительные переменные (если они были заказаны). Имена вновь созданных переменных создаются путем добавлением к имени исходной переменной соответствующего префикса или суффикса. Так, префиксы всех дополнительных переменных — "dum" или "woe", а категоризированным переменным добавляют суффикс "binned". Напишем функцию DiscretizeData(), которая, используя woe.binning.deploy(), будет трансформировать исходный набор данных.

DiscretizeData <- function(X, preCut, var){
  require(foreach)
  require(woeBinning)
  DTd <- list()
  foreach(i = 1:length(X)) %do% {
    X[[i]] %>% select(-Data) %>% targ.int() %>%
    woe.binning.deploy(preCut, min.iv.total = 0.1, 
                       add.woe.or.dum.var = var) -> res
    return(res)
  } -> DTd
  list(pretrain = DTd[[1]] , 
       train = DTd[[2]] ,
       val =   DTd[[3]] , 
       test =  DTd[[4]] ) -> DTd
  return(DTd)
}

Входные параметры функции: исходные данные (list X) со слотами pretrain/train/val/test, параметры дискретизации preCut, тип дополнительной переменной  (string var).

Функция уберет из каждого слота переменную "Data" и заменит целевую-фактор "Class" на численную целевую "Cl". После этого она подаст на вход функции woe.binning.deploy(), во входных параметрах которой мы дополнительно укажем минимальный IV = 0.1 для включения переменных в выходной набор. На выходе получим лист с теми же слотами pretrain/train/val/test,  но в каждом слоте к исходным переменным будут добавлены категоризированные и, если закажем, дополнительные. Вычислим необходимые нам наборы и добавим к ним "сырые" данные из набора DTcap.n.

evalq({
  require(dplyr)
  require(foreach)
    DTbin = DiscretizeData(DTcap.n, preCut = preCut, var = "")
    DTwoe = DiscretizeData(DTcap.n, preCut = preCut, var = "woe")
    DTdum = DiscretizeData(DTcap.n, preCut = preCut, var = "dum")
    X.woe <- list()
    X.bin <- list()
    X.dum <- list()
    foreach(i = 1:length(DTcap.n)) %do% {
      DTbin[[i]] %>% select(contains("binned")) -> X.bin[[i]]
      DTdum[[i]] %>% select(starts_with("dum")) -> X.dum[[i]]
      DTwoe[[i]] %>% select(starts_with("woe")) %>% 
        divide_by(100) -> X.woe[[i]]
      return(list(bin =  X.bin[[i]], woe = X.woe[[i]], 
                  dum = X.dum[[i]], raw = DTcap.n[[i]]))
    } -> DTcut
    list(pretrain = DTcut[[1]], 
            train = DTcut[[2]],
              val =   DTcut[[3]], 
             test =  DTcut[[4]] ) -> DTcut
    rm(DTwoe, DTdum, X.woe, X.bin, X.dum)
}, 
env)

Поскольку WOE мы получаем в процентах, то разделив на 100, мы получим значения переменных, которые можно будет подавать на входы нейросети без дополнительной нормализации. Посмотрим на структуру полученного слота, например, DTcut$val.

> env$DTcut$val %>% str()
List of 4
 $ bin:'data.frame':    501 obs. of  10 variables:
  ..$ v.fatl.binned: Factor w/ 5 levels "(-Inf,-0.3904381926]",..: 1 1 3 2 4 3 4 4 4 4 ...
  ..$ ftlm.binned  : Factor w/ 5 levels "(-Inf,-0.2344708291]",..: 2 1 1 1 2 2 3 4 4 4 ...
  ..$ rbci.binned  : Factor w/ 5 levels "(-Inf,-0.1718377948]",..: 2 1 2 1 2 3 3 3 4 4 ...
  ..$ v.rbci.binned: Factor w/ 5 levels "(-Inf,-0.1837437563]",..: 1 1 3 2 4 3 4 4 4 4 ...
  ..$ v.satl.binned: Factor w/ 5 levels "(-Inf,-0.01840058612]",..: 1 1 1 1 1 1 1 1 1 2 ...
  ..$ v.stlm.binned: Factor w/ 5 levels "(-Inf,-0.4030051922]",..: 2 2 3 2 3 2 3 3 4 4 ...
  ..$ pcci.binned  : Factor w/ 5 levels "(-Inf,-0.1738420887]",..: 1 1 4 2 4 2 4 2 2 3 ...
  ..$ v.ftlm.binned: Factor w/ 4 levels "(-Inf,-0.03697698898]",..: 1 1 3 2 3 2 3 3 2 2 ...
  ..$ v.rftl.binned: Factor w/ 5 levels "(-Inf,-0.1578370554]",..: 2 1 1 1 1 1 1 2 2 2 ...
  ..$ stlm.binned  : Factor w/ 7 levels "(-Inf,-0.4586732186]",..: 2 2 2 2 1 1 1 1 1 2 ...
 $ woe:'data.frame':    501 obs. of  10 variables:
  ..$ woe.v.fatl.binned: num [1:501] 1.713 1.713 -0.145 0.632 -0.897 ...
  ..$ woe.ftlm.binned  : num [1:501] 0.352 0.972 0.972 0.972 0.352 ...
  ..$ woe.rbci.binned  : num [1:501] 0.274 0.794 0.274 0.794 0.274 ...
  ..$ woe.v.rbci.binned: num [1:501] 0.683 0.683 -0.295 0.22 -0.861 ...
  ..$ woe.v.satl.binned: num [1:501] 0.573 0.573 0.573 0.573 0.573 ...
  ..$ woe.v.stlm.binned: num [1:501] 0.473 0.473 -0.0183 0.473 -0.0183 ...
  ..$ woe.pcci.binned  : num [1:501] 0.619 0.619 -0.65 0.174 -0.65 ...
  ..$ woe.v.ftlm.binned: num [1:501] 0.435 0.435 -0.669 -0.162 -0.669 ...
  ..$ woe.v.rftl.binned: num [1:501] 0.024 0.446 0.446 0.446 0.446 ...
  ..$ woe.stlm.binned  : num [1:501] 0.329 0.329 0.329 0.329 -0.425 ...
 $ dum:'data.frame':    501 obs. of  41 variables:
  ..$ dum.v.fatl.-Inf.-0.3904381926.binned          : num [1:501] 0 0 0 0 0 0 0 0 0 0 ...
  ..$ dum.v.fatl.-0.03713814085.0.1130198981.binned : num [1:501] 0 0 0 0 0 0 0 0 0 0 ...
  ..$ dum.v.fatl.-0.3904381926.-0.03713814085.binned: num [1:501] 0 0 0 0 0 0 0 0 0 0 ...
  ..$ dum.v.fatl.0.1130198981.Inf.binned            : num [1:501] 0 0 0 0 0 0 0 0 0 0 ...
  ..$ dum.ftlm.-0.2344708291.-0.01368798447.binned  : num [1:501] 0 0 0 0 0 0 0 0 0 0 ...
  ..$ dum.ftlm.-Inf.-0.2344708291.binned            : num [1:501] 0 0 0 0 0 0 0 0 0 0 ...
  ..$ dum.ftlm.-0.01368798447.0.1789073635.binned   : num [1:501] 0 0 0 0 0 0 0 0 0 0 ...
  ..$ dum.ftlm.0.1789073635.Inf.binned              : num [1:501] 0 0 0 0 0 0 0 0 0 0 ...
  .......................................................................................
  ..$ dum.stlm.-Inf.-0.4586732186.binned            : num [1:501] 0 0 0 0 0 0 0 0 0 0 ...
  ..$ dum.stlm.-0.1688696056.0.2631157075.binned    : num [1:501] 0 0 0 0 0 0 0 0 0 0 ...
  ..$ dum.stlm.0.2631157075.0.3592235072.binned     : num [1:501] 0 0 0 0 0 0 0 0 0 0 ...
  ..$ dum.stlm.0.3592235072.0.4846279843.binned     : num [1:501] 0 0 0 0 0 0 0 0 0 0 ...
  ..$ dum.stlm.0.4846279843.Inf.binned              : num [1:501] 0 0 0 0 0 0 0 0 0 0 ...
 $ raw:'data.frame':    501 obs. of  14 variables:
  ..$ Data  : POSIXct[1:501], format: "2017-02-23 15:30:00" "2017-02-23 15:45:00" ...
  ..$ ftlm  : num [1:501] -0.223 -0.374 -0.262 -0.31 -0.201 ...
  ..$ stlm  : num [1:501] -0.189 -0.257 -0.271 -0.389 -0.473 ...
  ..$ rbci  : num [1:501] -0.0945 -0.1925 -0.1348 -0.1801 -0.1192 ...
  ..$ pcci  : num [1:501] -0.5714 -0.2602 0.4459 -0.0478 0.2596 ...
  ..$ v.fatl: num [1:501] -0.426 -0.3977 0.0936 -0.1512 0.1178 ...
  ..$ v.satl: num [1:501] -0.35 -0.392 -0.177 -0.356 -0.316 ...
  ..$ v.rftl: num [1:501] -0.0547 -0.2065 -0.3253 -0.4185 -0.4589 ...
  ..$ v.rstl: num [1:501] 0.0153 -0.0273 -0.0636 -0.1281 -0.15 ...
  ..$ v.ftlm: num [1:501] -0.321 -0.217 0.253 0.101 0.345 ...
  ..$ v.stlm: num [1:501] -0.288 -0.3 -0.109 -0.219 -0.176 ...
  ..$ v.rbci: num [1:501] -0.2923 -0.2403 0.1909 0.0116 0.2868 ...
  ..$ v.pcci: num [1:501] -0.0298 0.3738 0.6153 -0.5643 0.2742 ...
  ..$ Class : Factor w/ 2 levels "-1","1": 1 1 1 1 2 2 2 2 2 1 ...

Как видим, в слоте bin 10 переменных-факторов с различным количеством уровней и суффиксом "binned". Слот woe содержит 10 переменных, у которых уровни факторных заменены на их WOE (имеют префикс "woe"). В слоте dum 41 числовая переменная dummy со значением (0, 1), которые получены из факторных путем кодирования один к одному (имеют префикс "dum"). В слоте raw, 14 переменных: Data — timestamp, Class — целевая-фактор и 12 числовых предикторов.

Мы получили все данные, которые понадобятся нам для дальнейших экспериментов. В окружении env к этому моменту уже должны находиться объекты, перечисленные ниже. Сохраним рабочую область с этими объектами в файл PartIV.RData.

> ls(env)
 [1] "Close"    "Data"     "dt"       "DT"       "DTbin"    "DTcap"   "DTcap.n" "DTcut"  "High"    
[10] "i"        "Low"      "Open"     "pre.outl" "preCut"   "preproc"  "Volume" 

2.1.2. Базовая модель сравнения

В качестве базовой модели будем использовать, простую, надежную и легко интерпретируемую модель OneR, реализованную в одноименном пакете OneR. Детали об алгоритме можно прочесть в описании пакета. Модель работает только с интервальными данными, но в пакете есть вспомогательные функции, которые могут дискретизировать числовые переменные различными способами. Поскольку мы уже преобразовали предикторы в факторы, они нам не понадобятся.

Теперь поясню расчет, приведенный ниже. Создаем наборы train/val/test, извлекая из DTcut соответствующие слоты и пристыковывая к ним целевую Class. Обучаем модель на наборе train.

> evalq({
+   require(OneR)
+   require(dplyr)
+   require(magrittr)
+   train <- cbind(DTcut$train$bin, Class = DTcut$train$raw$Class) %>% as.data.frame()
+   val <- cbind(DTcut$val$bin, Class = DTcut$val$raw$Class) %>% as.data.frame()
+   test <- cbind(DTcut$test$bin, Class = DTcut$test$raw$Class) %>% as.data.frame()
+   model <- OneR(data = train, formula = NULL, ties.method = "chisq", #c("first","chisq"
+                 verbose = TRUE) #FALSE, TRUE
+ }, env)
Loading required package: OneR

    Attribute     Accuracy
1 * v.satl.binned 63.14%  
2   v.fatl.binned 62.64%  
3   ftlm.binned   62.54%  
4   pcci.binned   61.44%  
5   v.rftl.binned 59.74%  
6   v.rbci.binned 58.94%  
7   rbci.binned   58.64%  
8   stlm.binned   58.04%  
9   v.stlm.binned 57.54%  
10  v.ftlm.binned 56.14%  
---
Chosen attribute due to accuracy
and ties method (if applicable): '*'

Warning message:
In OneR(data = train, formula = NULL, ties.method = "chisq", verbose = TRUE) :
  data contains unused factor levels
Модель выбрала переменную  v.satl.binned с базовой точностью 63.14% как основу для построения правил. Посмотрим общую информацию по модели:
> summary(env$model)

Call:
OneR(data = train, formula = NULL, ties.method = "chisq", verbose = FALSE)

Rules:
If v.satl.binned = (-Inf,-0.01840058612]         then Class = -1
If v.satl.binned = (-0.01840058612,0.3247097195] then Class = 1
If v.satl.binned = (0.3247097195,0.4003869443]   then Class = 1
If v.satl.binned = (0.4003869443, Inf]           then Class = 1

Accuracy:
632 of 1001 instances classified correctly (63.14%)

Contingency table:
     v.satl.binned
Class (-Inf,-0.01840058612] (-0.01840058612,0.3247097195] (0.3247097195,0.4003869443] (0.4003869443, Inf]  Sum
  -1                  * 325                           161                          28                  37  551
  1                     143                         * 229                        * 35                * 43  450
  Sum                   468                           390                          63                  80 1001
---
Maximum in each column: '*'

Pearson's Chi-squared test:
X-squared = 74.429, df = 3, p-value = 4.803e-16

Графическое представление результата обучения:

plot(env$model)

OneR

Рис.12. Распределение категорий переменной v.satl.binned по классам в модели

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

> evalq(res.val <- eval_model(predict(model, val %>% as.data.frame()), val$Class),
+       env)

Confusion matrix (absolute):
          Actual
Prediction  -1   1 Sum
       -1  106  87 193
       1   100 208 308
       Sum 206 295 501

Confusion matrix (relative):
          Actual
Prediction   -1    1  Sum
       -1  0.21 0.17 0.39
       1   0.20 0.42 0.61
       Sum 0.41 0.59 1.00

Accuracy:
0.6267 (314/501)

Error rate:
0.3733 (187/501)

Error rate reduction (vs. base rate):
0.0922 (p-value = 0.04597)

и на тестовом наборе:

> evalq(res.test <- eval_model(predict(model, test %>% as.data.frame()), test$Class),
+       env)

Confusion matrix (absolute):
          Actual
Prediction  -1   1 Sum
       -1  130 102 232
       1    76 193 269
       Sum 206 295 501

Confusion matrix (relative):
          Actual
Prediction   -1    1  Sum
       -1  0.26 0.20 0.46
       1   0.15 0.39 0.54
       Sum 0.41 0.59 1.00

Accuracy:
0.6447 (323/501)

Error rate:
0.3553 (178/501)

Error rate reduction (vs. base rate):
0.1359 (p-value = 0.005976)

Результаты получились не самые обнадеживающие. Error rate reduction показывает, как увеличилась точность по отношению к базовому (0.5) уровню. Низкое p-значение (< 0.05) свидетельствует о том, что модель действительно способна давать прогнозы лучше базового уровня. Точность тестового набора 0.6447 (323/501), который отстоит от обучающего дальше чем валидационный, выше чем у него. Этот показатель и будет ориентиром для сравнения с результатами предсказания наших будущих моделей.


2.1.3. Структура DNN

Для обучения и тестирования будем использовать три набора данных:

  1. DTcut$$raw — 12 входных переменных (выбросы импутированы и нормализованы).
  2. DTcut$$dum — 41 двоичная переменная.
  3. DTcut$$woe — 10 числовых переменных.

Со всеми наборами применяем целевую переменную Class = фактор с двумя уровнями. Структура нейросетей:

  • DNNraw - layers = c(12, 16, 8(2), 2), функции активации c(tanh, maxout(lin), softmax)
  • DNNwoe - layers = c(10, 16, 8(2), 2), функции активации c(tanh, maxout(lin), softmax)
  • DNNdum - layers = c(41, 50, 8(2), 2), функции активации c(ReLU, maxout(ReLU), softmax)

На рисунке ниже приведена структура нейросети DNNwoe. Нейросеть состоит из входного, двух скрытых и выходного слоя. Две остальных ( DNNdum, DNNraw) имеют аналогичную структуру, но отличаются количеством нейронов в слоях и функциями активации.

structura DNN_!

Рис.13. Структура нейросети DNNwoe


2.1.4. Варианты обучения

С претренингом

Обучение будем проводить в два этапа:

  • претренинг SRBM набором /pretrain и последующее обучение только верхнего слоя DNN, валидация набором train, параметры — par_0;
  • тонкое обучение всей сети наборами train/val и параметрами par_1.

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

  • par_0 — общие параметры нейросети, параметры обучения RBM и параметры обучения верхнего слоя DNN;
  • par_1 — параметры обучения всех слоев DNN.

Все параметры DArch имеют значения по умолчанию. Но если нам нужны определенные параметры на определенном этапе обучения, мы можем их задать списком, и они перезапишут параметры по умолчанию. После первого этапа обучения мы получим структуру DArch c параметрами и результатами обучения (ошибка обучения, тестирования и др.), а также нейросеть, инициированную весами обученной SRBM. Для выполнения второго этапа обучения нам нужно подать структуру DArch, полученную на первом этапе, и список параметров для этого этапа обучения. И, конечно, нам понадобятся обучающий и валидационный наборы.

Рассмотрим параметры, необходимые для первого этапа обучения (претренинг SRBM и обучение верхнего слоя нейросети) и выполним его:

##=====CODE I etap===========================
evalq({
  require(darch)
  require(dplyr)
  require(magrittr)
  Ln <- c(0, 16, 8, 0)#     // количество входных и выходных нейронов определится автоматически из набора данных 
  nEp_0 <- 25
  #------------------
  par_0 <- list(
    layers = Ln, #         // вынесем этот параметр за список (для простоты манипулирования)
    seed = 54321,#         // если хотим получать при инициализации идентичные данные
    logLevel = 5, #        // какой уровень вывода информации нам нужен
        # params RBM========================
        rbm.consecutive = F, # each RBM is trained one epoch at a time
    rbm.numEpochs = nEp_0,
    rbm.batchSize = 50,
    rbm.allData = TRUE,
    rbm.lastLayer = -1, #                       // верхний слой SRBM не обучать
    rbm.learnRate = 0.3,
    rbm.unitFunction = "tanhUnitRbm",
        # params NN ========================
    darch.batchSize = 50,
    darch.numEpochs = nEp_0,#                  // вынесем этот параметр за список для простоты манипулирования
    darch.trainLayers = c(F,F,T), #обучать     //только верхний слой 
    darch.unitFunction = c("tanhUnit","maxoutUnit", "softmaxUnit"),
    bp.learnRate = 0.5,
    bp.learnRateScale = 1,
    darch.weightDecay = 0.0002,
    darch.dither = F,
    darch.dropout = c(0.1,0.2,0.1),
    darch.fineTuneFunction = backpropagation, #rpropagation
    normalizeWeights = T,
    normalizeWeightsBound = 1,
    darch.weightUpdateFunction = c("weightDecayWeightUpdate", 
                                   "maxoutWeightUpdate",
                                   "weightDecayWeightUpdate"),
    darch.dropout.oneMaskPerEpoch = T,
    darch.maxout.poolSize = 2, 
    darch.maxout.unitFunction = "linearUnit")
#---------------------------
  
  DNN_default <- darch(darch = NULL, 
                       paramsList = par_0,
                       x = DTcut$pretrain$woe %>% as.data.frame(),
                       y = DTcut$pretrain$raw$Class %>% as.data.frame(),
                       xValid = DTcut$train$woe %>% as.data.frame(), 
                       yValid = DTcut$train$raw$Class %>% as.data.frame()
                       )
}, env)
Результат по окончанию первого этапа обучения:
...........................

INFO [2017-09-11 14:12:19] Classification error on Train set (best model): 31.95% (639/2000)
INFO [2017-09-11 14:12:19] Train set (best model) Cross Entropy error: 1.233
INFO [2017-09-11 14:12:19] Classification error on Validation set (best model): 35.86% (359/1001)
INFO [2017-09-11 14:12:19] Validation set (best model) Cross Entropy error: 1.306
INFO [2017-09-11 14:12:19] Best model was found after epoch 3
INFO [2017-09-11 14:12:19] Final 0.632 validation Cross Entropy error: 1.279
INFO [2017-09-11 14:12:19] Final 0.632 validation classification error: 34.42%
INFO [2017-09-11 14:12:19] Fine-tuning finished after 5.975 secs

Второй этап обучения всей нейросети:

##=====CODE II etap===========================
evalq({
  require(darch)
  require(dplyr)
  require(magrittr)
  nEp_1 <- 100
  bp.learnRate <- 1
  par_1 <- list(
    layers = Ln,
    seed = 54321,
    logLevel = 5,
    rbm.numEpochs = 0,# SRBM не обучаем!
    darch.batchSize = 50,
    darch.numEpochs = nEp_1,
    darch.trainLayers = c(T,T,T), #TRUE,
    darch.unitFunction = c("tanhUnit","maxoutUnit", "softmaxUnit"),
    bp.learnRate = bp.learnRate,
    bp.learnRateScale = 1,
    darch.weightDecay = 0.0002,
    darch.dither = F,
    darch.dropout = c(0.1,0.2,0.1),
    darch.fineTuneFunction = backpropagation, #rpropagation backpropagation
    normalizeWeights = T,
    normalizeWeightsBound = 1,
    darch.weightUpdateFunction = c("weightDecayWeightUpdate", 
                                   "maxoutWeightUpdate",
                                   "weightDecayWeightUpdate"),
    darch.dropout.oneMaskPerEpoch = T,
    darch.maxout.poolSize = 2, 
    darch.maxout.unitFunction = exponentialLinearUnit,
    darch.elu.alpha = 2)
        #------------------------------
        DNN_1 <- darch( darch = DNN_default, paramsList = par_1, 
                 x = DTcut$train$woe %>% as.data.frame(),
                 y = DTcut$train$raw$Class %>% as.data.frame(),
                 xValid = DTcut$val$woe %>% as.data.frame(), 
                 yValid = DTcut$val$raw$Class %>% as.data.frame()
                 )
}, env)

Результат второго этапа обучения:

...........................
INFO [2017-09-11 15:48:37] Finished epoch 100 of 100 after 0.279 secs (3666 patterns/sec)
INFO [2017-09-11 15:48:37] Classification error on Train set (best model): 31.97% (320/1001)
INFO [2017-09-11 15:48:37] Train set (best model) Cross Entropy error: 1.225
INFO [2017-09-11 15:48:37] Classification error on Validation set (best model): 31.14% (156/501)
INFO [2017-09-11 15:48:37] Validation set (best model) Cross Entropy error: 1.190
INFO [2017-09-11 15:48:37] Best model was found after epoch 96
INFO [2017-09-11 15:48:37] Final 0.632 validation Cross Entropy error: 1.203
INFO [2017-09-11 15:48:37] Final 0.632 validation classification error: 31.44%
INFO [2017-09-11 15:48:37] Fine-tuning finished after 37.22 secs

График изменения ошибки предсказания в процессе обучения на втором этапе:

plot(env$DNN_1, y = "raw")

DNNwoe II etap

Рис.14. Изменение ошибки классификации на втором этапе обучения

Посмотрим на ошибку классификации окончательной модели на тестовом наборе:

#-----------
evalq({
  xValid = DTcut$test$woe %>% as.data.frame() 
  yValid = DTcut$test$raw$Class %>% as.vector()
  Ypredict <- predict(DNN_1, newdata = xValid, type = "class")
  numIncorrect <- sum(Ypredict != yValid)
  cat(paste0("Incorrect classifications on all examples: ", numIncorrect, " (",
           round(numIncorrect/nrow(xValid)*100, 2), "%)\n"))
   caret::confusionMatrix(yValid, Ypredict)
}, env)
Incorrect classifications on all examples: 166 (33.13%)
Confusion Matrix and Statistics

          Reference
Prediction  -1   1
        -1 129  77
        1   89 206
                                          
               Accuracy : 0.6687          
                 95% CI : (0.6255, 0.7098)
    No Information Rate : 0.5649          
    P-Value [Acc > NIR] : 1.307e-06       
                                          
                  Kappa : 0.3217          
 Mcnemar's Test P-Value : 0.3932          
                                          
            Sensitivity : 0.5917          
            Specificity : 0.7279          
         Pos Pred Value : 0.6262          
         Neg Pred Value : 0.6983          
             Prevalence : 0.4351          
         Detection Rate : 0.2575          
   Detection Prevalence : 0.4112          
      Balanced Accuracy : 0.6598          
                                          
       'Positive' Class : -1
#----------------------------------------

Точность на этом наборе данных (woe) и с этими, далеко не оптимальными, параметрами немного выше базовой модели. Есть значительный потенциал к ее увеличению путем оптимизации гиперпараметров DNN. При повторении вычислений полученные данные могут не совпадать с приведенными в статье. 

Для дальнейших вычислений с другими наборами данных сведем наши скрипты в компактную форму. Напишем функцию обучения для набора woe:

#-------------------
DNN.train.woe <- function(param, X){ 
  require(darch)
  require(magrittr)
  darch( darch = NULL, paramsList = param[[1]], 
         x = X[[1]]$woe %>% as.data.frame(),
         y = X[[1]]$raw$Class %>% as.data.frame(),
         xValid = X[[2]]$woe %>% as.data.frame(), 
         yValid = X[[2]]$raw$Class %>% as.data.frame()
  ) %>%
    darch( ., paramsList = param[[2]], 
           x = X[[2]]$woe %>% as.data.frame(),
           y = X[[2]]$raw$Class %>% as.data.frame(),
           xValid = X[[3]]$woe %>% as.data.frame(), 
           yValid = X[[3]]$raw$Class %>% as.data.frame()
    ) -> Darch
  return(Darch) 
}

Повторим расчеты для набора DTcut$$woe в компактном виде:

evalq({
  require(darch)
  require(magrittr)
  Ln <- c(0, 16, 8, 0)
  nEp_0 <- 25
  nEp_1 <- 25
  rbm.learnRate = c(0.5,0.3,0.1)
  bp.learnRate <- c(0.5,0.3,0.1)
  list(par_0, par_1) %>% DNN.train.woe(DTcut) -> Dnn.woe
  xValid = DTcut$test$woe %>% as.data.frame() 
  yValid = DTcut$test$raw$Class %>% as.vector()
  Ypredict <- predict(Dnn.woe, newdata = xValid, type = "class")
  numIncorrect <- sum(Ypredict != yValid)
  cat(paste0("Incorrect classifications on all examples: ", numIncorrect, " (",
             round(numIncorrect/nrow(xValid)*100, 2), "%)\n"))
  caret::confusionMatrix(yValid, Ypredict) -> cM.woe
}, env)

Выполним расчет для набора DTcut$$raw:

#-------------------------
DNN.train.raw <- function(param, X){ 
  require(darch)
  require(magrittr)
  darch( darch = NULL, paramsList = param[[1]], 
         x = X[[1]]$raw %>% tbl_df %>% select(-c(Data, Class)),
         y = X[[1]]$raw$Class %>% as.data.frame(),
         xValid = X[[2]]$raw %>% tbl_df %>% select(-c(Data, Class)), 
         yValid = X[[2]]$raw$Class %>% as.data.frame()
  ) %>%
    darch( ., paramsList = param[[2]], 
           x = X[[2]]$raw %>% tbl_df %>% select(-c(Data, Class)),
           y = X[[2]]$raw$Class %>% as.data.frame(),
           xValid = X[[3]]$raw %>% tbl_df %>% select(-c(Data, Class)),
           yValid = X[[3]]$raw$Class %>% as.data.frame()
    ) -> Darch
  return(Darch) 
}
#-------------------------------
evalq({
  require(darch)
  require(magrittr)
  Ln <- c(0, 16, 8, 0)
  nEp_0 <- 25
  nEp_1 <- 25
  rbm.learnRate = c(0.5,0.3,0.1)
  bp.learnRate <- c(0.5,0.3,0.1)
  list(par_0, par_1) %>% DNN.train.raw(DTcut) -> Dnn.raw
  xValid = DTcut$test$raw %>% tbl_df %>% select(-c(Data, Class)) 
  yValid = DTcut$test$raw$Class %>% as.vector()
  Ypredict <- predict(Dnn.raw, newdata = xValid, type = "class")
  numIncorrect <- sum(Ypredict != yValid)
  cat(paste0("Incorrect classifications on all examples: ", numIncorrect, " (",
             round(numIncorrect/nrow(xValid)*100, 2), "%)\n"))
  caret::confusionMatrix(yValid, Ypredict) -> cM.raw
}, env)
#----------------------------

Смотрим результат и график изменения ошибки классификации для этого набора:

> env$cM.raw
Confusion Matrix and Statistics

          Reference
Prediction  -1   1
        -1 133  73
        1   86 209
                                          
               Accuracy : 0.6826          
                 95% CI : (0.6399, 0.7232)
    No Information Rate : 0.5629          
    P-Value [Acc > NIR] : 2.667e-08       
                                          
                  Kappa : 0.3508          
 Mcnemar's Test P-Value : 0.3413          
                                          
            Sensitivity : 0.6073          
            Specificity : 0.7411          
         Pos Pred Value : 0.6456          
         Neg Pred Value : 0.7085          
             Prevalence : 0.4371          
         Detection Rate : 0.2655          
   Detection Prevalence : 0.4112          
      Balanced Accuracy : 0.6742          
                                          
       'Positive' Class : -1 
#--------------------------------------
plot(env$Dnn.raw, y = "raw")

Dnn.raw  error

Рис.15. Изменение ошибки классификации на втором этапе обучения

Обучить нейросеть с данными DTcut$$dum мне не удалось. Читатель может попробовать сделать это самостоятельно: например, попробовать подать на вход данные DTcut$$bin и в параметрах обучения предусмотреть перевод предикторов в dummy.


Обучение без претренинга

Проведем эксперимент с обучением нейросети без претренинга с теми же данными (woe, raw) на выборках pretrain/train/val. Посмотрим результат.

#-------WOE----------------
evalq({
  require(darch)
  require(magrittr)
  Ln <- c(0, 16, 8, 0)
  nEp_1 <- 100 
  bp.learnRate <- c(0.5,0.7,0.1)
  #--param----------------
  par_1 <- list(
    layers = Ln,
    seed = 54321,
    logLevel = 5,
    rbm.numEpochs = 0,# SRBM не обучаем!
    darch.batchSize = 50,
    darch.numEpochs = nEp_1,
    darch.trainLayers = c(T,T,T), #TRUE,
    darch.unitFunction = c("tanhUnit","maxoutUnit", "softmaxUnit"),
    bp.learnRate = bp.learnRate,
    bp.learnRateScale = 1,
    darch.weightDecay = 0.0002,
    darch.dither = F,
    darch.dropout = c(0.0,0.2,0.1),
    darch.fineTuneFunction = backpropagation, #rpropagation backpropagation
    normalizeWeights = T,
    normalizeWeightsBound = 1,
    darch.weightUpdateFunction = c("weightDecayWeightUpdate", 
                                   "maxoutWeightUpdate",
                                   "weightDecayWeightUpdate"),
    darch.dropout.oneMaskPerEpoch = T,
    darch.maxout.poolSize = 2, 
    darch.maxout.unitFunction = exponentialLinearUnit,
    darch.elu.alpha = 2)
  #--train---------------------------
  darch( darch = NULL, paramsList = par_1, 
         x = DTcut[[1]]$woe %>% as.data.frame(),
         y = DTcut[[1]]$raw$Class %>% as.data.frame(),
         xValid = DTcut[[2]]$woe %>% as.data.frame(), 
         yValid = DTcut[[2]]$raw$Class %>% as.data.frame()
  ) -> Dnn.woe.I
  #---test--------------------------
  xValid = DTcut$val$woe %>% as.data.frame() 
  yValid = DTcut$val$raw$Class %>% as.vector()
  Ypredict <- predict(Dnn.woe.I, newdata = xValid, type = "class")
  numIncorrect <- sum(Ypredict != yValid)
  cat(paste0("Incorrect classifications on all examples: ", numIncorrect, " (",
             round(numIncorrect/nrow(xValid)*100, 2), "%)\n"))
  caret::confusionMatrix(yValid, Ypredict) -> cM.woe.I
}, env)
#---------Ris16------------------------------------
plot(env$Dnn.woe.I, type = "class")
env$cM.woe.I

Метрики:

.......................................................
INFO [2017-09-14 10:38:01] Classification error on Train set (best model): 28.7% (574/2000)
INFO [2017-09-14 10:38:01] Train set (best model) Cross Entropy error: 1.140
INFO [2017-09-14 10:38:02] Classification error on Validation set (best model): 35.86% (359/1001)
INFO [2017-09-14 10:38:02] Validation set (best model) Cross Entropy error: 1.299
INFO [2017-09-14 10:38:02] Best model was found after epoch 67
INFO [2017-09-14 10:38:02] Final 0.632 validation Cross Entropy error: 1.241
INFO [2017-09-14 10:38:02] Final 0.632 validation classification error: 33.23%
INFO [2017-09-14 10:38:02] Fine-tuning finished after 37.13 secs
Incorrect classifications on all examples: 150 (29.94%)
> env$cM.woe.I
Confusion Matrix and Statistics

          Reference
Prediction  -1   1
        -1 144  62
        1   88 207
                                          
               Accuracy : 0.7006          
                 95% CI : (0.6584, 0.7404)
    No Information Rate : 0.5369          
    P-Value [Acc > NIR] : 5.393e-14       
                                          
                  Kappa : 0.3932          
 Mcnemar's Test P-Value : 0.04123         
                                          
            Sensitivity : 0.6207          
            Specificity : 0.7695          
         Pos Pred Value : 0.6990          
         Neg Pred Value : 0.7017          
             Prevalence : 0.4631          
         Detection Rate : 0.2874          
   Detection Prevalence : 0.4112          
      Balanced Accuracy : 0.6951          
                                          
       'Positive' Class : -1    

График изменения ошибки классификации при обучении:

Dnn.woe.I

Рис.16. Изменение ошибки классификации без претренинга с набором $woe

То же для набора /raw:

evalq({
  require(darch)
  require(magrittr)
  Ln <- c(0, 16, 8, 0)
  nEp_1 <- 100
  bp.learnRate <- c(0.5,0.7,0.1)
  #--param-----------------------------
  par_1 <- list(
    layers = Ln,
    seed = 54321,
    logLevel = 5,
    rbm.numEpochs = 0,# SRBM не обучаем!
    darch.batchSize = 50,
    darch.numEpochs = nEp_1,
    darch.trainLayers = c(T,T,T), #TRUE,
    darch.unitFunction = c("tanhUnit","maxoutUnit", "softmaxUnit"),
    bp.learnRate = bp.learnRate,
    bp.learnRateScale = 1,
    darch.weightDecay = 0.0002,
    darch.dither = F,
    darch.dropout = c(0.1,0.2,0.1),
    darch.fineTuneFunction = backpropagation, #rpropagation backpropagation
    normalizeWeights = T,
    normalizeWeightsBound = 1,
    darch.weightUpdateFunction = c("weightDecayWeightUpdate", 
                                   "maxoutWeightUpdate",
                                   "weightDecayWeightUpdate"),
    darch.dropout.oneMaskPerEpoch = T,
    darch.maxout.poolSize = 2, 
    darch.maxout.unitFunction = exponentialLinearUnit,
    darch.elu.alpha = 2)
  #---train------------------------------
  darch( darch = NULL, paramsList = par_1, 
         x = DTcut[[1]]$raw %>% tbl_df %>% select(-c(Data, Class)) ,
         y = DTcut[[1]]$raw$Class %>% as.vector(),
         xValid = DTcut[[2]]$raw %>% tbl_df %>% select(-c(Data, Class)) , 
         yValid = DTcut[[2]]$raw$Class %>% as.vector()
  ) -> Dnn.raw.I
  #---test--------------------------------
  xValid = DTcut[[3]]$raw %>% tbl_df %>% select(-c(Data, Class))
  yValid = DTcut[[3]]$raw$Class %>% as.vector()
  Ypredict <- predict(Dnn.raw.I, newdata = xValid, type = "class")
  numIncorrect <- sum(Ypredict != yValid)
  cat(paste0("Incorrect classifications on all examples: ", numIncorrect, " (",
             round(numIncorrect/nrow(xValid)*100, 2), "%)\n"))
  caret::confusionMatrix(yValid, Ypredict) -> cM.raw.I
}, env)
#---------Ris17----------------------------------
env$cM.raw.I
plot(env$Dnn.raw.I, type = "class")

Метрики:

INFO [2017-09-14 11:06:13] Classification error on Train set (best model): 30.75% (615/2000)
INFO [2017-09-14 11:06:13] Train set (best model) Cross Entropy error: 1.189
INFO [2017-09-14 11:06:13] Classification error on Validation set (best model): 33.67% (337/1001)
INFO [2017-09-14 11:06:13] Validation set (best model) Cross Entropy error: 1.236
INFO [2017-09-14 11:06:13] Best model was found after epoch 45
INFO [2017-09-14 11:06:13] Final 0.632 validation Cross Entropy error: 1.219
INFO [2017-09-14 11:06:13] Final 0.632 validation classification error: 32.59%
INFO [2017-09-14 11:06:13] Fine-tuning finished after 35.47 secs
Incorrect classifications on all examples: 161 (32.14%)
> #---------Ris17----------------------------------
> env$cM.raw.I
Confusion Matrix and Statistics

          Reference
Prediction  -1   1
        -1 140  66
        1   95 200
                                          
               Accuracy : 0.6786          
                 95% CI : (0.6358, 0.7194)
    No Information Rate : 0.5309          
    P-Value [Acc > NIR] : 1.283e-11       
                                          
                  Kappa : 0.3501          
 Mcnemar's Test P-Value : 0.02733         
                                          
            Sensitivity : 0.5957          
            Specificity : 0.7519          
         Pos Pred Value : 0.6796          
         Neg Pred Value : 0.6780          
             Prevalence : 0.4691          
         Detection Rate : 0.2794          
   Detection Prevalence : 0.4112          
      Balanced Accuracy : 0.6738          
                                          
       'Positive' Class : -1          

График изменения ошибки классификации:

Dnn.raw.I

Рис.17. Изменение ошибки классификации без претренинга с набором $raw


2.2. Анализ результатов

Сведем результаты наших экспериментов в таблицу:

Вид обучения Набор  /woe Набор /raw
С претренингом 0.6687 (0.6255 - 0.7098) 0.6826(0.6399 - 0.7232)
Без претренинга 0.7006(0.6589 - 0.7404) 0.6786(0.6359 - 0.7194)

Ошибка классификации с претренингом у обоих наборов приблизительно одинакова и находится в пределах 30+/-4%. При обучении без претренинга, несмотря на более низкую ошибку, по графику ее изменения явно видно переобучение (ошибка на валидационном и тестовом наборах значительно превышает ошибку обучения). Поэтому в дальнейших экспериментах будем использовать обучение с претренингом.

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


Заключение

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

Невысокие показатели DNN объясняются главным образом тем, что мы используем параметры по умолчанию или близкие к ним. Использование набора /woe не показало преимущества перед /raw. Поэтому в следующей статье мы:

  • оптимизируем часть гиперпараметров нашей ранее созданной DNN.woe;
  • создадим DNN, используя библиотеку TensorFlow, протестируем ее и сравним результаты с DNN (darch);
  • создадим ансамбль нейросетей различного вида (bagging, stacking) и посмотрим, насколько это повышает качество предсказания.

Приложения

В GitHub/PartIV находятся:

  1.  FunPrepareData.R — функции, использующиеся при подготовке данных
  2.  RunPrepareData.R — скрипты для подготовки данных
  3.  Experiment.R — скрипты для проведения экспериментов
  4.  Part_IV.RData — снимок рабочей области со всеми объектами, полученными после этапа подготовки данных
  5.  SessionInfo.txt — информация об использованном ПО
  6.  Darch_default.txt — перечень параметров структуры DArch со значениями по умолчанию


Последние комментарии | Перейти к обсуждению на форуме трейдеров (27)
Vladimir Perervenko
Vladimir Perervenko | 18 нояб. 2017 в 06:35
elibrarius:

Еще вопрос появился )
В статье от 2014 года вы использовали для запуска эксперта на разных окнах разные порты подключения к R, через svSocket.

Сейчас это нужно тоже делать?

С последних статьях это не используется. Запускал эксперта на разных окнах (на одном символе, но с разными параметрами эксперта), он вызывает разные потоки RTerm, судя по результатам - вроде бы обращается каждый к своему. Но есть сомнение - вдруг надо все таки разделить по портам?

Добрый день.

Я в дороге, поэтому коротко.

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

Удачи

Aleksandr Petinov
Aleksandr Petinov | 18 нояб. 2017 в 06:59
Vladimir Perervenko:

Добрый день.

Я в дороге, поэтому коротко.

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

Удачи


Уважаемый добрый день, интересна статья новая ваша 4, у меня есть вопрос, реализовать советник можете по этой статье которая описана, естественно оплата будет, тех задание все дам, щас ищу именно нейронку обучения под цели, если да отпишите в ЛС там поговорим. Если Вы таким не занимаетесь то может подскажите к кому обратится для реализации советника под МТ5 нужна

JunCheng Li
JunCheng Li | 19 янв. 2018 в 10:17

非常感谢你的系列文章,我一直在跟踪学习,可是因为我知识结构的局限,理解这些文章不是件容易的事情。即使这样,还是希望能不断看到您的新文章,期待您发表新文章!谢谢!

Anton Ohmat
Anton Ohmat | 6 мая 2018 в 21:11
А результат торговый у Вас получился?
Vladimir Tkach
Vladimir Tkach | 8 мая 2018 в 07:14
Anton Ohmat:
А результат торговый у Вас получился?

Все вернулись к машкам. А это уже другая тема.

Новый подход к интерпретации классической и обратной дивергенции Новый подход к интерпретации классической и обратной дивергенции
В статье рассмотрены классический метод построения дивергенции и отличный от него способ интерпретации. Этот новый метод интерпретации положен в основу торговой стратегии, которая описана в статье.
Нечеткая логика в торговых стратегиях Нечеткая логика в торговых стратегиях
В статье рассматривается пример использования нечеткой логики для построения простой торговой системы, с использованием библиотеки Fuzzy. Предложены варианты улучшения системы путем сочетания нечеткой логики, генетических алгоритмов и нейронных сетей.
Сравнение различных типов скользящих средних в торговле Сравнение различных типов скользящих средних в торговле
Рассмотрены 7 видов скользящих средних (MA), разработана торговая стратегия по работе с ними. Выполнено тестирование и сравнение различных МА на одной торговой стратегии, дана сравнительная характеристика эффективности применения той или иной скользящей средней.
Треугольный арбитраж Треугольный арбитраж
Статья посвящена популярному методу торговли - треугольному арбитражу. Тема разобрана максимально подробно, рассмотрены положительные и отрицательные стороны стратегии, разработан готовый код эксперта.