Redes Neurais Profundas (Parte IV). Criação, treinamento e teste de um modelo de rede neural

Redes Neurais Profundas (Parte IV). Criação, treinamento e teste de um modelo de rede neural

Vladimir Perervenko | 20 outubro, 2017


Conteúdo

Introdução

Principais direções de estudo e aplicação

Atualmente, existem dois fluxos principais no estudo e aplicação de redes neurais profundas. Eles diferem na abordagem da inicialização dos pesos dos neurônios em camadas ocultas.

Abordagem 1. As redes neurais são muito sensíveis ao método de inicialização de neurônios em camadas ocultas, especialmente se o número de camadas ocultas aumentar (maior que 3). O professor G.Hynton foi o primeiro a tentar e resolver esse problema. A ideia por trás de sua abordagem foi iniciar os pesos dos neurônios em camadas ocultas com os pesos obtidos durante o treinamento não supervisionado das redes neurais auto-associativas constituídas por RBM (máquina de Boltzmann restrita) ou AE (autoencoder). Esses RBMs empilhados (SRBM) e AE empilhados (SAE) são treinados com uma grande variedade de dados não-rotulados. O objetivo desse treinamento é destacar estruturas ocultas (representações, imagens) e relacionamentos nos dados. A inicialização de neurônios com pesos MLP, obtidos durante o pré-treinamento, coloca o MLP ao espaço de soluções muito próximo ao ótimo. Isso permite diminuir o número de dados rotulados e as épocas durante o seguinte ajuste fino (treinamento) do MLP. Estas são vantagens extremamente importantes para muitas esferas de aplicação prática, especialmente ao processar muitos dados.

Abordagem 2: Outro grupo de cientistas liderados por Yoshua Benjio criou métodos específicos de inicialização de neurônios ocultos, funções específicas de ativação, métodos de estabilização e treinamento. O sucesso desta direção está relacionada com um extenso desenvolvimento de redes neurais convolutivas profundas e redes neuronais recorrentes (DCNN, RNN). Tais redes neurais apresentaram alta eficiência no reconhecimento de imagens, análise e classificação de textos, juntamente com a tradução do discurso falado de um idioma para outro. A ideias e métodos desenvolvidos para essas redes neurais também começaram a ser usadas ​​para a MLP.

Atualmente, ambas abordagens são usadas ativamente. Deve-se notar que com quase os mesmos resultados, as redes neurais com pré-treinamento exigem menos recursos computacionais e menos amostras para treinamento. Esta é uma vantagem importante. Eu pessoalmente sou a favor das redes neurais profundas com pré-treinamento. Eu acredito que o futuro pertence à aprendizagem não supervisionada.

Pacotes em R que permitem desenvolver e usar DNN

R possui uma série de pacotes para criar e usar uma DNN com um nível diferente de complexidade e conjunto de recursos.

Pacotes que permitem criar, treinar e testar um DNN com pré-treinamento:

  • deepnet é um pacote simples que não possui muitas configurações e parâmetros. Permite criar ambas as redes neurais SAE com pré-treinamento e SRBM. No artigo anterior nós consideramos uma implementação prática de Experts usando este pacote. O uso de RBM para pré-treinamento produz resultados menos estáveis. Este pacote é adequado para o primeiro encontro com este tema e sobre as peculiaridades de tais redes. Com a abordagem certa, ela pode ser usada em um Expert. RcppDL é uma versão deste pacote ligeiramente menor em С++.
  • darch v.0.12 é um pacote complexo e flexível que tem muitos parâmetros e configurações. As configurações recomendadas são definidas como padrão. Este pacote permite criar e configurar uma rede neural de qualquer complexidade e configuração. Ela utiliza a SRBM para pré-treinamento. Este pacote é para usuários avançados. Nós vamos discutir os seus recursos em detalhes mais tarde.

Abaixo estão os pacotes que permitem criar, treinar e testar uma DNN sem pré-treinamento:

  • H2O é um pacote para processamento de dados grandes (>1M de linhas e >1K de colunas). A rede neural profunda utilizada nela possui um sistema de regularização desenvolvido. Suas capacidades são excessivas para o nosso campo, mas isso não deve nos parar de usá-lo.
  • mxnet permite criar não só uma MLP, mas também redes recorrentes complexas, por convolução e LSTM. Este pacote tem uma API para vários idiomas, incluindo R e Python. A filosofia do pacote é diferente das listadas acima. Isso ocorre porque os desenvolvedores escreveram pacotes principalmente para Python. O pacote mxnet para R é mais leve e possuindo menos recursos do que o pacote para Python. Isso não faz este pacote pior.

O tema das redes profundas e recorrentes está bem desenvolvido no ambiente Python. Existem muitos pacotes interessantes para a construção de redes neurais desse tipo que a R não possui. Estes são pacotes R que permitem executar programas/módulos escritos em Python:

  • PythonInR e reticulate são dois pacotes que permitem execução de qualquer código Python em R. Para isso, você precisa ter o Python 2/3 instalado no seu computador.
  • kerasr é uma interface R para uma biblioteca popular de aprendizagem profunda - keras.
  • tensorflow é um pacote que fornece acesso à API TensorFow completa no ambiente R.

Recentemente, a Microsoft publicou a biblioteca cntk v.2.1 (Computational Network Toolkit) no GitHub. Ela pode ser usada como backend em comparação com Keras. Recomenda-se testá-lo em nossos problemas.

Yandex está se mantendo - a sua própria biblioteca O CatBoost está disponível em código aberto. Esta biblioteca pode ser usada para treinamento de modelos com dados de diferentes tipos. Isso inclui dados difíceis de apresentar como números, por exemplo, tipos de nuvens e tipos de mercadorias. O código-fonte, documentação, benchmarks e ferramentas necessárias já foram publicadas no GitHub com a licença Apache 2.0. Apesar de não serem redes neurais, mas árvores melhoradas, é aconselhável testar o algoritmo, especialmente porque contém a API da R.


1. Uma breve descrição dos recursos do pacote

O pacote darch ver. 0.12.0 fornece uma ampla gama de funcionalidades, permitindo que você não apenas crie e treine um modelo, mas faça o que quiser para suas necessidades e preferências. Mudanças significativas foram introduzidas na versão do pacote (0.10.0), considerado no artigo anterior. Foram adicionadas novas funções de ativação, inicialização e estabilização. A novidade mais notável é que tudo foi trazido para uma única função darch(), que é um construtor ao mesmo tempo. As placas gráficas são suportadas. Após o treinamento, a função retorna um objeto da classe DArch. A estrutura do objeto é apresentada na Fig. 1. As funções predict() e darchTest() retornam uma previsão sobre os novos dados ou métricas de classificação.

StrDarch

Fig.1. Estrutura do objeto DArch 

Todos os parâmetros têm valores padrão. Esses valores geralmente não são ótimos. Todos esses parâmetros podem ser divididos em três grupos - geral, para RBM e para NN. Nós vamos considerar alguns deles em detalhes mais tarde.

Funções Tipos
Funções de inicialização                                                                     generateWeightsUniform, generateWeightsNormal, 

                                             generateWeightsHeUniform, generateWeightsHeNormal)

Funções de ativação                                      exponentialLinearUnitsoftplusUnit, softmaxUnit, maxoutUnit)
Funções de treinamento                                         minimizeAutoencoder, minimizaClassifier)
Nível de treinamento
  • bp.learnRate = 1 é o nível de treinamento para backpropagation. Isso pode ser um vetor se diferentes níveis de treinamento forem usados ​​em cada camada de NN
  • bp.learnRateScale = 1. O nível de treinamento é multiplicado por esse valor após cada época
Funções de estabilização
  • darch.dropout = 0 é um número (0,1) ou um vetor com o nível de eliminação para cada camada de NN
  • darch.dropout.dropConnect = F indica se DropConnect deve ser usado em vez de ser abandonado
  • darch.dropout.momentMatching = 0

  • darch.dropout.oneMaskPerEpoch = F indica se deve ser gerado uma nova máscara para um novo lote (FALSE, padrão) ou para cada época (TRUE)
  • darch.dither = F mostra se dither deve ser aplicado a todos os dados de entrada do conjunto de treinamento
  • darch.nesterovMomentum = T
  • darch.weightDecay = 0
  • normalizeWeights = F
  • normalizeWeightsBound é o limite superior para a norma L2 do vetor de entrada de pesos. Isso é usado apenas se normalizeWeights = TRUE
Momentum
  • darch.initialMomentum = 0.5
  • darch.finalMomentum = 0.9
  • darch.momentumRampLength = 1
 Condições de parada
  • darch.stopClassErr = 100
  • darch.stopErr = -Inf
  • darch.stopValidClassErr = 100
  • darch.stopValidErr = -Inf

Uma rede neural profunda é composta por n RBM (n = camadas -1) conectadas em uma rede auto-associativa (SRBM) e as redes neurais reais MLP com várias camadas. O treinamento em camada da RBM é um treinamento não supervisionado em dados não-rotulados. O ajuste fino da rede neural requer supervisão e é realizado em dados rotulados.

A divisão desses estágios de treinamento com parâmetros nos dá a oportunidade de usar dados de diferentes volumes (não uma estrutura diferente!!) e obter vários modelos ajustados com base em um pré-treinamento. Se os dados para pré-treinamento e ajuste fino forem iguais, o treinamento pode ser realizado de uma só vez sem dividi-lo em duas etapas. O pré-treinamento pode ser ignorado (rbm.numEpochs = 0; darch.numEpochs = 10)). Nesse caso, você pode usar apenas uma rede neural de várias camadas ou treinar apenas a RBM (rbm.numEpochs = 10; darch.numEpochs = 0). Você ainda terá acesso a todos os parâmetros internos.

A rede neural treinada pode ser treinada em novos dados quantas vezes for necessário. Isso só é possível com um número limitado de modelos. O diagrama estrutural de uma rede neural profunda inicializada por máquinas de Boltzmann complexas e restritas (DNRBM) é exibido na Fig.2.

DNSRBM

Fig.2. Diagrama estrutural de DNSRBM


1.1. Funções de inicialização dos neurônios

Existem duas funções principais de inicialização de neurônios no pacote. 

  • generateWeightsUniform() usa a função runif(n, min, max), sendo implementada da seguinte forma:
> 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 é o número de neurônios na camada anterior e numUnits2 é o número de neurônios na camada atual. 

  • generateWeightsNormal()usa a função rnorm(n, mean, sd) sendo implementada no pacote da seguinte forma:
> 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>
         

Outras quatro funções estão usando essas duas funções, mas definem min, max, média e sd com funções específicas. Você pode estudá-las se você inserir o nome da função sem colchetes no terminal.


1.2. Funções de ativação dos neurônios

Além das funções de ativação padrão, o pacote sugere uma ampla gama de novas funções. Aqui estão algumas delas:

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

Fig.3. Funções de ativação dos neurônios

Vamos considerar a função de ativação maxout separadamente. Esta função vem de redes por convolução. A camada oculta da rede neural é dividida por módulos do tamanho da agregação (poolSize). O número de neurônios na camada oculta deve ser divisível pelo tamanho da agregação (pool). Para o treinamento, um neurônio com uma ativação máxima é selecionado da pool e enviado para a entrada. A função de ativação dos neurônios na pool é definida separadamente. Em palavras simples, esta é uma camada dupla (convolução + maxpooling) com capacidades limitadas no passo de filtração. De acordo com várias publicações, ela produz bons resultados em conjunto com dropout. Fig. 4. É exibido esquematicamente uma camada oculta com 8 neurônios e dois tamanhos da pool

Maxout

Fig.4. A função de ativação maxout

1.3. Métodos de treinamento


Infelizmente, existem apenas dois métodos de treinamento no pacote - backpropagation e rprop da versão básica e melhorada com atualização do peso durante a backpropagation e sem ela. Existe também a possibilidade de alterar o nível de treinamento com a ajuda do multiplicador bp.learnRateScale..


1.4. Métodos de regulação e estabilização

  • dropout é uma técnica de eliminação (método de regularização) de uma parte dos neurônios da camada oculta durante o treinamento. Os neurônios são zerados em uma ordem aleatória. O número relativo de neurônios a serem descartados é definido pelo parâmetro darch.dropout. O nível de regularização em cada camada oculta pode ser diferente. A máscara de regularização pode ser gerada para cada lote ou para cada época.
  • dropconnect desliga as conexões entre uma parte dos neurônios da camada atual e os neurônios da camada anterior. As conexões são cortadas em uma ordem aleatória. O número relativo de conexões a serem cortadas é definido pelo mesmo parâmetro darch.dropout (geralmente não superior a 0.5). De acordo com algumas publicações, o dropconnect exibe melhores resultados do que o método de regularização (dropout).
  • dither é uma maneira de evitar um retreinamento através da redução (dithering) dos dados de entrada.
  • weightDecay o peso de cada neurônio será multiplicado por (1 — weightDecay)antes da atualização.
  • normalizeWeights é uma maneira de normalizar um vetor de entrada de pesos de neurônios com uma possível limitação acima (norma L2)

Os três primeiros métodos são usados ​​apenas de maneira separada. 


1.5. Métodos e parâmetros de treinamento de um RBM

Há duas maneiras de treinar uma SRBM. Qualquer RBM é treinada uma a uma durante a rbm.numEpochs ou cada RBM é treinada em uma única época ao mesmo tempo. A escolha de um desses métodos é feita pelo parâmetro rbm.consecutive: TRUE ou padrão é o primeiro método e FALSE é o segundo método. Fig.5 apresenta um esquema de treinamento em duas variantes. O rbm.lastLayer pode ser usado para especificar a camada de SRBM em que o pré-treinamento deve ser interrompido. Se 0, todas as camadas devem ser treinadas e se (-1) a camada superior é para deixar sem treino. Isso faz sentido, já que a camada superior precisa ser treinada separadamente e leva muito mais tempo. Outros parâmetros não precisam de explicações adicionais.

SRBMtrain

Fig.5. Dois métodos de treinamento de uma SRBM


1.6. Métodos e parâmetros de treinamento da DNN

Uma DNN pode ser treinada de duas maneiras - com pré-treinamento e sem ele. Os parâmetros utilizados nestes métodos são totalmente diferentes. Por exemplo, não é necessário usar métodos específicos de inicialização e regularização no treinamento com pré-treinamento. De fato, usar esses métodos pode piorar o resultado. A razão por trás disso é que, após um pré-treinamento, os pesos dos neurônios nas camadas ocultas serão colocados na área próxima aos valores ótimos e eles precisarão apenas de uma pequena afinação fina. Para obter o mesmo resultado no treinamento sem pré-treinamento, todos os métodos disponíveis de inicialização e regularização terão de ser utilizados. Geralmente, o treinamento de uma rede neural dessa maneira demora mais.

Então, vamos nos concentrar no treinamento com pré-treinamento. Normalmente, ele acontece em duas etapas.

  1. Treinando a SRBM em um grande conjunto de dados não gravados. Os parâmetros de pré-treinamento são definidos separadamente. Como resultado, nós temos uma rede neural iniciada por pesos da SRBM. Em seguida, a camada superior da rede neural é treinada com dados rotulados com seus próprios parâmetros de treinamento. Desta forma, nós temos uma rede neural com uma camada superior treinada e pesos iniciados nas camadas inferiores. Salve-o como um objeto independente para uso posterior. 
  2. Na segunda etapa, nós usaremos algumas amostras rotuladas, baixo nível de treinamento e um pequeno número de épocas de treinamento para todas as camadas da rede neural. Este é um ajuste fino da rede. A rede neural é treinada.

A possibilidade de divisão de estágios de pré-treinamento, ajuste fino e treinamentos adicionais oferece uma flexibilidade incrível na criação de algoritmos de treinamento não só para uma DNN, mas para treinamento de comitês de DNN. Fig.6. representa várias variantes de treinamento de DNN e comitês de DNN. 

  • Variante а. Salve a DNN em todas as épocas durante o ajuste fino. Desta forma, nós teremos um número de DNN com um grau de treinamento diferente. Mais tarde, cada uma dessas redes neurais pode ser usada separadamente ou como parte de um comitê. A desvantagem deste cenário é que todos as DNN são treinadas nos mesmos dados, pois todos elas tinham os mesmos parâmetros de treinamento.
  • Variante b. Faça o ajuste fino da DNN iniciada em paralelo com diferentes conjuntos de dados (janela deslizante, janela em crescimento, etc.) e diferentes parâmetros. Como resultado, nós teremos uma DNN que produzirá previsões menos correlacionadas do que as variantes a.
  • Faça o ajuste fino da variante c na DNN iniciada sequencialmente com diferentes conjuntos de dados e diferentes parâmetros. Salve os modelos intermediários. Isto é o que anteriormente nós chamamos de treinamento adicional. Isso pode ser executado sempre que há dados novos suficientes.

DNNtrain

Fig.6. Variantes de treinamento da DNN


2. Testando a qualidade do trabalho de uma DNN, dependendo dos parâmetros utilizados.

2.1. Experimentos


2.1.1. Dados de entrada (preparação)

Nós usaremos os dados e funções da parte anterior do artigo (1, 2, 3). Lá foram discutidos em detalhe várias variantes da preparação preliminar de dados. Vou mencionar brevemente as etapas de preparação preliminar que nós vamos realizar. OHLCV é o dado inicial, o mesmo que antes. Os dados de entrada são filtros digitais e os dados de saída são o ZigZag. As funções e a imagem do espaço de trabalho Cotir.RData podem ser usadas.

Os estágios de preparação de dados para realizar serão reunidos em funções separadas:

  • PrepareData() — cria o DataSet inicial e remove os NA;
  • SplitData() — divide o conjunto de dados iniciais nos subconjuntos de pré-treinamento, train, val, test;
  • CappingData() — identifica e imputa os outliers em todos os subconjuntos.

Para salvar o espaço no artigo, eu não vou trazer a lista dessas funções aqui. Elas podem ser baixadas do GitHub já que elas foram consideradas em detalhes nos artigos anteriores. Nós vamos analisar os resultados mais tarde. Nós não vamos discutir todos os métodos de transformação de dados durante o processamento preliminar. Muitos deles são bem conhecidos e amplamente utilizados. Nós usaremos um método menos conhecido de discretização (supervisionado e não supervisionado). No segundo artigo nós consideramos dois pacotes de discretização supervisionada (discretization e smbinning). Eles contêm diferentes algoritmos de discretização.

Nós examinaremos os diferentes métodos de dividir variáveis contínuas em bins e as formas de usar essas variáveis ​​discretizadas em modelos.

O que é binning?

Binning é um termo usado na modelagem de pontuação. Ela é conhecida como discretização na aprendizagem por máquinas. Este é um processo de transformar uma variável contínua em um número finito de intervalos (caixas). Isso ajuda a entender sua distribuição e relacionamento com a variável objetivo binária. Os bins criados neste processo podem se tornar características da característica preditiva para uso em modelos.

Por que o binning?

Apesar de algumas resalvas sobre o binning, ele possui vantagens significativas. 

  • Ele permite incluir os dados ausentes (NA) e outros cálculos específicos (divisão por zero, por exemplo) no modelo.
  • Ele controla ou mitiga o impacto dos outliers no modelo.
  • Ele resolve o problema de diferentes escalas em preditores, tornando os coeficientes ponderados comparáveis ​​no modelo final. 

Discretização não supervisionada

A discretização não supervisionada divide uma função contínua em bins sem levar em conta outras informações. Esta divisão tem duas opções. Elas são bins de mesmo comprimento e bins de mesma frequência.

Opção
Alvo
Exemplo
Desvantagem
Bins de mesmo comprimento
Comprensão da distribuição da variável
Histograma clássico com bunkers de mesmo comprimento, que pode ser calculado usando regras diferentes (sturges, rice ets)
O número de registros no bunker pode ser muito pequeno para um cálculo correto
Bins de frequência qual
Analisa o relacionamento com a variável objetivo binária usando índices como a taxa incorreta
Quartilies ou Percentis Os pontos de corte selecionados não podem maximizar a diferença entre as caixas na verificação contra a variável objetivo

Discretização supervisionada

A discretização supervisionada divide a variável contínua em caixas projetadas na variável objetivo. A ideia-chave aqui é encontrar esses pontos de corte que maximizarão a diferença entre os grupos.

Usando algoritmos como ChiMerge ou Particionamento Recursivo, os analistas podem rapidamente encontrar pontos ótimos em segundos e avaliar sua relação com a variável objetivo, usando índices como peso de evidência (WoE) e valor de informação (IV).

WoE pode ser usado como um instrumento para transformar preditores no estágio de pré-processamento para algoritmos de aprendizagem supervisionada. Durante a discretização dos preditores, nós podemos substituí-los por suas novas variáveis ​​nominais ou pelos valores de seu WoE. A segunda variante é interessante porque permite afastar-se da transformação das variáveis ​​nominais (fatores) para as artificiais. Isso dá uma melhoria significativa na qualidade da classificação. 

WOE e IV desempenham duas funções diferentes na análise de dados:

  • WOE descreve a relação da variável preditiva e a variável objetivo binária.
  • IV mede a força dessas relações.

Vamos descobrir o qual WOE e IV estão usando diagramas e fórmulas. Lembre-se do gráfico da variável v.fatl dividido em 10 áreas equitativas na segunda parte do artigo.

vfatl_discr

Fig.7. A variável v.fatl é dividida em 10 áreas equitativas

Capacidade preditiva de dados (WOE)

Como você pode ver, cada bin possui amostras que entram na classe "1" e na classe "-1". A capacidade preditiva dos bins WoEi são calculados com a fórmula 

WoEi = ln(Gi/Bi)*100

где:

Gi — frequência relativa das amostras "boas" (no nosso caso "bom" = "1") em cada bin da variável;

Bi — frequência relativa das amostras "ruins" (no nosso caso "ruim" = "-1") em cada bin da variável.

Se WoEi = 1, o que significa que o número de amostras "boas" e "ruins" neste compartimento é aproximadamente o mesmo, então a habilidade preditiva desse bin é 0. Se as amostras "boas" superarem em número as "ruins", WOE >0 e vice-versa.

Valor de informação (IV) 

Esta é a medida mais comum de identificar a significância de variáveis ​​e medir a diferença na distribuição de amostras "boas" e "ruins". O valor da informação pode ser calculado com a fórmula:

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

O valor da informação de uma variável é igual à soma de todas bins da variável. Os valores desse coeficiente podem ser interpretados da seguinte forma:

  • abaixo de 0,02 — variável estatisticamente insignificante;
  • 0,02 - 0,1 — variável estatisticamente fraca;
  • 0,1 - 0,3 — variável estatisticamente significativa;
  • 0,3 e acima — variável estatisticamente forte.

Em seguida, os bins são unidos/divididos usando vários algoritmos e critérios de otimização para tornar a diferença entre esses bins tão grande quanto possível. Por exemplo, o pacote smbinning usa o Particionamento Recursivo para categorizar valores numéricos e o valor de informação para resolver os pontos de corte ótimos. O pacote discretization resolve esse problema com o ChiMerge e MDL. Deve-se ter em mente que os pontos de corte são obtidos no conjunto de treinamento e costuma-se dividir os conjuntos de validação e teste. 

Existem vários pacotes que permitem fazer variáveis ​​numéricas discretas de uma maneira ou de outra. Sendo elas: discretization, smbinning, Information, InformationValue e woebinning. Nós precisamos tornar o conjunto de dados de teste discreto, dividir os conjuntos de validação e teste usando essas informações. Nós também queremos ter controle visual dos resultados. Por causa desses requisitos, eu escolhi o pacote woebinning

O pacote se divide automaticamente em valores numéricos e fatores e os liga à variável objetivo binária. Aqui são contempladas duas abordagens:

  • a implementação de classificação fina e bruta uni sequencialmente classes e níveis granulados;
  • um segmento de abordagem semelhante a uma árvore através de bins iniciais de iteração através da divisão binária.

Ambos os procedimentos combinam bins divididos com base nos valores semelhantes de WOE e a parada com base nos critérios IV. O pacote pode ser usado tanto com variáveis ​​autônomas quanto com todo o quadro de dados. Isso fornece ferramentas flexíveis para estudar várias soluções para binning e para expandir novos dados.

Vamos fazer o cálculo. Nós já temos as cotações do terminal carregado em nosso ambiente de trabalho (ou imagem do ambiente de trabalho Cotir.RData do GitHub). Sequência de cálculos e resultados:

  1.  PrepareData() — cria o conjunto de dados inicial dt[7906, 14], limpo de NA. O conjunto inclui o rótulo temporário Data, variáveis de entrada (12) e a variável objetivo Class (fator com dois níveis "-1" e "+1").
  2.  SplitData() — divide o conjunto de dados inicial dt[] em subconjunto de pré-treinamento, train, val, test na proporção de 2000/1000/500/500, unindo eles em um dataframe DT[4, 4000, 14].
  3.  CappingData() — identifica e imputa os outliers em todos os subconjuntos, obtém o conjunto DTcap[4, 4000, 14]. Apesar do fato de que a discretização é toleranta aos outliers, nós os imputaremos. Você pode experimentar sem esta etapa. Como você pode lembrar, os parâmetros de outliers (pre.outl) são definidos no subconjunto de pré-treinamento. Processe os conjuntos de train/val/test usando esses parâmetros.
  4.  NormData() — normaliza o conjunto, usando o método spatialSing do pacote caret. Semelhante ao imputamento dos outliers, os parâmetros de normalização (preproc) são definidos no subconjunto pretrain. As amostras train/val/test são processadas usando esses parâmetros. Nós temos DTcap.n[4, 4000, 14] como resultado.
  5. DiscretizeData() — define os parâmetros de discretização (preCut), a qualidade das variáveis ​​e suas bins à luz de WOE e 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)
         

Vamos colocar os dados de discretização em todas as variáveis ​​em uma tabela e olhar para elas:

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
         

A tabela possui os seguintes valores para cada variável: 

  • Final.Bin — limites de bin;
  • Total.Count  — número total de amostras em bin;
  • Total.Distr — número relativo de amostras em bin;
  • 0.Count — número de amostras pertencentes à classe "0";
  • 1.Count — número de amostras pertencentes à classe "1";
  • 0.Distr — — número relativo de amostras pertencentes à classe "0";
  • 1.Distr — número relativo de amostras pertencentes à classe "1";
  • 1.Rate — razão percentual das amostras da classe "1" para o número de amostras da classe "0";
  •  WOE — capacidade preditiva das bins;
  •  IV — importância estatística das bins.

A representação gráfica será mais ilustrativa. Desenhe os gráficos WOE de todas as variáveis ​​na ordem crescente da sua IV com base nesta tabela:

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

WOE 8

Fig.8. WOE das 4 melhores variáveis

WOE 10

Fig.9. WOE das variáveis ​​5-8

WOE 11

Fig.10. WOE de variáveis ​​de entrada 9-12 

Gráfico da variável total das variáveis ​​por sua IV.

IV

Fig.11. Intervalo de variáveis ​​pela sua IV

Nós não vamos usar duas variáveis ​​insignificantes v.rstl e v.pcci, que têm IV < 0.1. Nós podemos ver das tabelas que de 10 variáveis significativas, apenas a v.satl e stlm têm uma relação não-linear com a variável objetivo. Outras variáveis ​​têm uma relação linear.

Para experiências adicionais, nós precisamos criar três conjuntos. Eles são:

  • DTbin é um conjunto de dados onde os preditores numéricos contínuos são transformados em fatores com o número de níveis iguais ao número de caixas nas quais eles são divididos;
  • DTdum é um conjunto de dados onde os preditores de fatores do conjunto de dados DTbin são transformados em variáveis ​​binárias artificiais;
  • DTwoe é um conjunto de dados onde os preditores de fatores são transformados em variáveis ​​numéricas, substituindo seus níveis pelos valores de WOE desses níveis.

O primeiro conjunto de DTbin é necessário para treinamento e obtenção das métricas do modelo básico. O segundo e terceiro conjuntos serão utilizados para o treinamento da DNN e para comparar a eficiência desses dois métodos de transformação.

A função woe.binning.deploy() do pacote woebinning nos permitirá resolver esse problema com bastante facilidade. Os seguintes dados devem ser passados ​​para a função:

  •  quadro de dados com preditores e a variável objetivo, onde a variável objetivo deve ter o valor de 0 ou 1;
  •  parâmetros de discretização, obtidos na fase anterior (preCut);
  •  nomes das variáveis ​​que precisam ser categorizadas. Se todas as variáveis ​​devem ser categorizadas, basta especificar o nome do quadro de dados;
  •  especificar o IV mínimo para que as variáveis ​​não sejam categorizadas;
  •  especificar quais variáveis ​​adicionais (exceto as categorizadas) que queremos obter. Existem duas variantes - "woe" e "dum". 

A função retorna um quadro de dados contendo variáveis ​​iniciais, variáveis ​​categorizadas e variáveis ​​adicionais (se elas foram especificadas). Os nomes das variáveis ​​recém-criadas são criados adicionando um prefixo ou sufixo correspondente ao nome da variável inicial. Dessa forma, os prefixos de todas as variáveis ​​adicionais são "dum" ou "woe" e as variáveis ​​categorizadas possuem o sufixo "binned". Vamos escrever uma função DiscretizeData(), o que transformará o conjunto de dados inicial usando a 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)
         }
         

Os parâmetros de entrada da função são dados iniciais (lista X) com os slots pretrain/train/val/test, parâmetros de discretização preCut e o tipo da variável adicional (string var).

A função removerá a variável "Data" de cada slot e alterará a variável objetivo - fator "Class" para a variável de objetivo numérico "Cl". Com base nisso, ela enviará woe.binning.deploy() para a entrada da função. Além disso, especificamos nos parâmetros de entrada desta função o IV mínimo = 0.1 para incluir as variáveis ​​no conjunto de saída. Na saída, receberemos uma lista com os mesmos slots pretrain/train/val/test. Em cada slot, as variáveis ​​categorizadas e, se solicitadas, as variáveis ​​adicionais serão adicionadas às variáveis ​​iniciais. Vamos calcular todos os conjuntos necessários e adicionar os dados brutos do conjunto DTcap.n para eles.

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)
         

Uma vez que a WOE é um valor percentual, nós podemos dividir o WOE por 100 e obter valores de variáveis ​​que podem ser enviadas para as entradas da rede neural sem normalização adicional. Vejamos a estrutura do slot obtido, por exemplo de 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 ...
         

Como você pode ver, o slot bin contém 10 variáveis ​​de fatores com diferentes números de níveis. Eles têm o sufixo "binned". O slot woe contém 10 variáveis ​​com níveis de fator alterados para o seu WOE (eles têm o prefixo "woe"). O slot dum tem 41 variáveis ​​numéricas artificiais com os valores (0, 1) obtidos das variáveis ​​de fatores através da codificação um para um (tem o prefixo "dum"). Existem 14 variáveis ​​no slot raw. Eles são Data — timestamp, Class — variável fator objetivo e 12 preditores numéricos.

Temos todos os dados que precisaremos para outras experiências. Os objetos listados abaixo devem estar no ambiente env até agora. Deixe-nos salvar a área de trabalho com esses objetos para o arquivo 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. Modelo básico de comparação

Nós vamos usar o modelo OneR implementado no pacote OneR como modelo base. Este modelo é simples, confiável e fácil de interpretar. Informações sobre o algoritmo podem ser encontradas na descrição do pacote. Este modelo está funcionando apenas com os dados de bin. O pacote contém funções auxiliares que podem ser variáveis numéricas discretas de diferentes maneiras. Como já transformamos os preditores em fatores, não precisamos deles.

Agora, eu vou elaborar o cálculo mostrado abaixo. Crie os conjuntos train/val/test, extraindo os slots correspondentes de DTcut e adicionando a classe de variável objetivo para eles. Vamos treinar o modelo com o conjunto 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
         
O modelo selecionou a variável v.satl.binned com a precisão básica de 63,14% como base para a criação das regras. Vejamos as informações gerais sobre este modelo:
> summary(env$model)
         Call:
         OneR(data = train, formula = NULL, ties.method = "chisq", verbose = FALSE)
         Regras:
         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
         

Representação gráfica do resultado de treinamento:

plot(env$model)

OneR

Fig.12. Distribuição das categorias da variável v.satl.binned por classes no modelo

A precisão da previsão durante o treinamento não é muito alta. Nós vamos ver a precisão que este modelo mostrará no conjunto de validação:

> 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)
         

e no conjunto de teste:

> 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)
         

Os resultados não são encorajadores. A redução da taxa de erro mostra como a precisão aumentou em relação ao nível base (0.5). O baixo valor de p (< 0.05) indica que este modelo é capaz de produzir previsões melhor do que o nível básico. A precisão do conjunto de teste é 0.6447 (323/501), que é maior do que a precisão do conjunto de validação. O conjunto de teste está mais longe do conjunto de treinamento do que o conjunto de validação. Este resultado será o ponto de referência para comparar resultados de previsão de nossos futuros modelos.


2.1.3. Estrutura de uma DNN

Nós usaremos três conjuntos de dados para treinar e testar:

  1. DTcut$$raw — 12 variáveis ​​de entrada (outliers imputados e normalizados).
  2. DTcut$$dum — 41 variáveis ​​binárias.
  3. DTcut$$woe — 10 variáveis ​​numéricas.

Nós vamos usar com todos os conjuntos de dados a variável Class = fator com dois níveis. Estrutura das redes neurais:

  • DNNraw - layers = c(12, 16, 8(2), 2), funções de ativação c(tanh, maxout(lin), softmax)
  • DNNwoe - layers = c(10, 16, 8(2), 2), funções de ativação c(tanh, maxout(lin), softmax)
  • DNNdum - layers = c(41, 50, 8(2), 2), funções de ativação c(ReLU, maxout(ReLU), softmax)

O diagrama abaixo mostra a estrutura da rede neural DNNwoe. A rede neural possui uma camada de entrada, duas camadas ocultas e uma camada de saída. Duas outras redes neurais ( DNNdum, DNNraw) têm uma estrutura semelhante. Eles apenas diferem no número de neurônios em camadas e funções de ativação.

estrutura DNN_!

Fig.13. Estrutura da rede neural DNNwoe


2.1.4. Variantes de treinamento

Com pré-treinamento

O treinamento terá duas etapas

  • pré-treinamento do SRBM com o conjunto /pretrain seguido do treinamento apenas da camada superior da rede neural, validação com o conjunto train e parâmetros — par_0;
  • ajuste fino de toda a rede com os conjuntos de train/val e parâmetros par_1.

 Nós podemos salvar os modelos intermediários de ajuste fino, mas não é obrigatório. O modelo que mostra os melhores resultados de treinamento deve ser salvo. Os parâmetros dessas duas etapas devem conter:

  • par_0 — parâmetros gerais da rede neural, parâmetros de treinamento da RBM e parâmetros de treinamento da camada superior da DNN;
  • par_1 — parâmetros de treinamento de todas as camadas da DNN.

Todos os parâmetros do DArch têm valores padrão. Se precisamos de parâmetros diferentes em uma determinada etapa do treinamento, nós podemos configurá-los por uma lista e eles substituirão os parâmetros padrão. Após o primeiro estágio de treinamento, nós obteremos a estrutura DArch com parâmetros e resultados de treinamento (erro de treinamento, erro de teste etc) e também a rede neural iniciada com os pesos do SRBM treinado. Para completar o segundo estágio de treinamento, você precisa incluir a estrutura DArch obtida na primeira etapa na lista de parâmetros para esta etapa de treinamento. Naturalmente, nós precisaremos de conjuntos de treinamento e de validação.

Consideremos os parâmetros necessários para o primeiro estágio de treinamento (pré-treinamento do SRBM e treinamento da camada superior da rede neural) e executá-lo:

##=====CODE I etap===========================
         evalq({
           require(darch)
           require(dplyr)
           require(magrittr)
           Ln <- c(0, 16, 8, 0)#     // the number of input and output neurons will be identified automatically from the data set 
           nEp_0 <- 25
           #------------------
           par_0 <- list(
             layers = Ln, #         // let us take this parameter out of the list (for simplicity)
             seed = 54321,#         // if we want to obtain identical data during initialization
             logLevel = 5, #        // what level of information output we require
                 # 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, #                       // do not train the upper layer of SRBM
             rbm.learnRate = 0.3,
             rbm.unitFunction = "tanhUnitRbm",
                 # params NN ========================
             darch.batchSize = 50,
             darch.numEpochs = nEp_0,#                  // take this parameter out of the list for simplicity
             darch.trainLayers = c(F,F,T), #обучать     //upper layer only 
             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)
         
Resultado após a conclusão do primeiro estágio de treinamento:
...........................
         
         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
         

Segunda etapa do treinamento da rede neural:

##=====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 is not to be trained!
             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)
         

Resultado da segunda etapa do treinamento:

...........................
         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
         

Gráfico da variação do erro de previsão durante o segundo estágio de treinamento:

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

DNNwoe II etap

Fig.14. Variação do erro de classificação durante o segundo estágio de treinamento

Vejamos o erro de classificação do modelo final no conjunto de teste:

#-----------
         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
         #----------------------------------------
         

A precisão neste conjunto de dados (woe) com esses parâmetros que estão longe de ser ótimos, é muito maior do que a precisão do modelo básico. Existe um potencial significativo para aumentar a precisão ao otimizar os hiperparâmetros da DNN. Se o cálculo for repetido, os dados podem não ser exatamente os mesmos que no artigo. 

Vamos levar nossos scripts para um formulário mais compacto para cálculos adicionais com outros conjuntos de dados. Vamos escrever uma função para o conjunto 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)
         }
         

Repita os cálculos para o conjunto de dados DTcut$$woe em uma forma compacta:

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)
         

Faça o cálculo para o conjunto de dados 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)
         #----------------------------
         

Abaixo está o resultado e o gráfico da variação de erro de classificação para este conjunto:

> 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

Fig.15. Variação do erro de classificação no segundo estágio

Eu não consegui treinar a rede neural com os dados DTcut$$dum. Você pode tentar fazer isso sozinho. Por exemplo, insira os dados DTcut$$bin e organize os parâmetros de treinamento para que os preditores sejam convertidos artificialmente.


Treinamento sem pré-treinamento

Vamos treinar a rede neural sem pré-treinamento com os mesmos dados (woe, raw) nos conjuntos pretrain/train/val. Vamos ver o resultado.

#-------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 is not to be trained!
             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
         

Métricas:

.......................................................
         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
         

Gráfico da variação do erro de classificação durante o treinamento:

Dnn.woe.I

Fig.16. Variação do erro de classificação sem pré-treinamento com o conjunto $woe

O mesmo para o conjunto /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 is not to be trained!
             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")
         

Métricas:

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          
         

Chart of the classification error change:

Dnn.raw.I

Fig.17. Change of the classification error without pretraining with the $raw set


2.2. Análise de resultados

Vamos colocar o resultado de nossos experimentos em uma tabela:

Tipo de treinamento Conjunto /woe Conjunto /raw
Com pré-treinamento 0.6687 (0.6255 - 0.7098) 0.6826(0.6399 - 0.7232)
Sem pré-treinamento 0.7006(0.6589 - 0.7404) 0.6786(0.6359 - 0.7194)

O erro de classificação com o pré-treinamento é quase o mesmo em ambos os conjuntos. Está na faixa de 30+/-4%. Apesar de um erro menor, fica claro a partir do gráfico de variação do erro de classificação que houve uma reconversão durante o treinamento sem pré-treinamento (o erro nos conjuntos de validação e teste foi significativamente maior que o erro de treinamento). Portanto, nós usaremos o treino com o pré-treinamento em nossos experimentos adicionais.

O resultado não é muito maior do que o resultado do modelo básico. Nós temos a possibilidade de melhorar as características ao otimizar alguns hiperparâmetros. Nós vamos fazer isso no próximo artigo. 


Conclusão

Apesar das limitações (por exemplo, apenas dois métodos básicos de treinamento), o pacote darch permite criar redes neurais diferentes em estrutura e parâmetros. Este pacote é uma boa ferramenta para o estudo profundo das redes neurais.

As características fracas da DNN são explicadas principalmente pelo uso dos parâmetros ou parâmetros padrão próximos a eles. O conjunto woe não mostrou vantagens antes do conjunto bruto. Portanto, no próximo artigo, nós iremos:

  • otimizar uma parte dos hiperparâmetros na DNN.woe criado anteriormente;
  • criaremos uma DNN, usando a biblioteca TensorFlow, testá-la e comparar os resultados com a DNN (darch);
  • criaremos um conjunto de redes neurais de diferentes tipos (empacotamento, empilhamento) e veremos como isso melhora a qualidade das previsões.

Aplicação

GitHub/PartIV contém:

  1.  FunPrepareData.R — funções usadas para a preparação de dados
  2.  RunPrepareData.R — scripts para a preparação de dados
  3.  Experiment.R — scripts para executar experimentos
  4.  Part_IV.RData — imagem da área de trabalho com todos os objetos obtidos após o estágio de preparação de dados
  5.  SessionInfo.txt — informações sobre o software usado
  6.  Darch_default.txt — lista de parâmetros da estrutura DArch com os valores padrão